lazarus-ccr/components/kcontrols/source/kcontrols.pas
sekelsenmat eb3ecee187 Initial commit of kcontrols
git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@1732 8e941d3f-bd1b-0410-a28a-d453659cc2b4
2011-07-04 06:24:41 +00:00

2826 lines
88 KiB
ObjectPascal
Executable File

{ @abstract(This unit contains the base class for all visible controls.)
@author(Tomas Krysl (tk@tkweb.eu))
@created(18 Sep 2009)
@lastmod(20 Jun 2010)
This unit implements the base class TKCustomControl for all visible controls
from the KControls Development Suite.
Copyright © 2009 Tomas Krysl (tk@@tkweb.eu)<BR><BR>
<B>License:</B><BR>
This code is distributed as a freeware. You are free to use it as part
of your application for any purpose including freeware, commercial and
shareware applications. The origin of this source code must not be
misrepresented; you must not claim your authorship. You may modify this code
solely for your own purpose. Please feel free to contact the author if you
think your changes might be useful for other users. You may distribute only
the original package. The author accepts no liability for any damage
that may result from using this code. }
unit KControls;
{$include kcontrols.inc}
{$WEAKPACKAGEUNIT ON}
interface
uses
{$IFDEF FPC}
LCLType, LCLIntf, LMessages, LCLProc, LResources,
{$ELSE}
Windows, Messages,
{$ENDIF}
SysUtils, Classes, Graphics, Controls, Forms, KFunctions
{$IFDEF USE_THEMES}
, Themes
{$IFNDEF FPC}
, UxTheme
{$ENDIF}
{$ENDIF}
;
type
{ This array serves as storage place for all colors. }
TKColorArray = array of TColor;
{ Declares possible indexes e.g. for the @link(TKPreviewColors.Color) property. }
TKPreviewColorIndex = Integer;
{ Declares print options - possible values for the @link(TKPrintPageSetup.Options) property. }
TKPrintOption = (
{ If there are more printed copies these will be collated. }
poCollate,
{ The printed shape will be scaled to fit on page. }
poFitToPage,
{ Every even page will be printed with mirrored (swapped) margins. }
poMirrorMargins,
{ Page numbers will be added to the bottom of each printed page. }
poPageNumbers,
{ Paints the selection in control's specific manner. }
poPaintSelection,
{ Title will be printed to the top of each printed page. }
poTitle,
{ Color page will be printed instead of B/W page. }
poUseColor
);
{ Print options can be arbitrary combined. }
TKPrintOptions = set of TKPrintOption;
{ Declares possible values for the @link(TKPrintPageSetup.Range) property. }
TKPrintRange = (
{ All pages will be printed. }
prAll,
{ Only selected block will be printed. }
prSelectedOnly,
{ Only given range of pages will be printed. }
prRange
);
{ Declares measurement units for KControls printing system. }
TKPrintUnits = (
{ Corresponding value is given in millimeters. }
puMM,
{ Corresponding value is given in centimeters. }
puCM,
{ Corresponding value is given in inches. }
puInch,
{ Corresponding value is given in hundredths of inches. }
puHundredthInch
);
const
{ Default value for the @link(TKCustomControl.BorderStyle) property. }
cBorderStyleDef = bsSingle;
{ Minimum for the @link(TKPrintPageSetup.Copies) property }
cCopiesMin = 1;
{ Maximum for the @link(TKPrintPageSetup.Copies) property }
cCopiesMax = 1000;
{ Default value for the @link(TKPrintPageSetup.Copies) property }
cCopiesDef = 1;
{ Default value for the @link(TKPrintPageSetup.MarginBottom) property }
cMarginBottomDef = 2.0;
{ Default value for the @link(TKPrintPageSetup.MarginLeft) property }
cMarginLeftDef = 1.5;
{ Default value for the @link(TKPrintPageSetup.MarginRight) property }
cMarginRightDef = 1.5;
{ Default value for the @link(TKPrintPageSetup.MarginTop) property }
cMarginTopDef = 1.8;
{ Default value for the @link(TKPrintPageSetup.Options) property. }
cOptionsDef = [poFitToPage, poPageNumbers, poUseColor];
{ Default value for the @link(TKPrintPageSetup.Options) property. }
cRangeDef = prAll;
{ Minimum for the @link(TKPrintPageSetup.Scale) property }
cScaleDef = 100;
{ Maximum for the @link(TKPrintPageSetup.Scale) property }
cScaleMin = 10;
{ Default value for the @link(TKPrintPageSetup.Scale) property }
cScaleMax = 500;
{ Default value for the @link(TKPrintPageSetup.Units) property. }
cUnitsDef = puCM;
{ Default value for the @link(TKPreviewColors.Paper) color property. }
cPaperDef = clWhite;
{ Default value for the @link(TKPreviewColors.BkGnd) color property. }
cBkGndDef = clAppWorkSpace;
{ Default value for the @link(TKPreviewColors.Border) color property. }
cBorderDef = clBlack;
{ Default value for the @link(TKPreviewColors.SelectedBorder) color property. }
cSelectedBorderDef = clNavy;
{ Index for the @link(TKPreviewColors.Paper) property. }
ciPaper = TKPreviewColorIndex(0);
{ Index for the @link(TKPreviewColors.BkGnd) property. }
ciBkGnd = TKPreviewColorIndex(1);
{ Index for the @link(TKPreviewColors.Border) property. }
ciBorder = TKPreviewColorIndex(2);
{ Index for the @link(TKPreviewColors.SelectedBorder) property. }
ciSelectedBorder = TKPreviewColorIndex(3);
{ Maximum color array index }
ciPreviewColorsMax = ciSelectedBorder;
{ Constant for control scrollbars. It means: Leave that scrollbar untouched. }
cScrollNoAction = -1;
{ Constant for control scrollbars. It means: Use given Delta to update scrollbar. }
cScrollDelta = -2;
{ Internal flag for TKPrintPreview. }
cPF_Dragging = $00000001;
{ Internal flag for TKPrintPreview. }
cPF_UpdateRange = $00000002;
type
{ Declares possible values for the @link(ScaleMode) property }
TKPreviewScaleMode = (
{ Apply scale defined by the @link(Scale) property }
smScale,
{ Scale the page so that it horizontally fits to the window client area }
smPageWidth,
{ Scale the page so that it fits to the window client area }
smWholePage);
{ @abstract(Declares @link(TKPrintPreview.OnChanged) event handler)
<UL>
<LH>Parameters:</LH>
<LI><I>Sender</I> - identifies the event caller</LI>
</UL>
}
TKPreviewChangedEvent = procedure(Sender: TObject) of object;
{ @abstract(Declares the information structure for the @link(TKCustomControl.MeasurePages) method)
<UL>
<LH>Members:</LH>
<LI><I>OutlineWidth</I> - printed outline width (maximum of all pages) in desktop pixels</LI>
<LI><I>OutlineHeight</I> - printed outline height (maximum of all pages) in desktop pixels</LI>
<LI><I>HorzPageCount</I> - number of pages to split a wide shape into</LI>
<LI><I>VertPageCount</I> - number of pages to split a tall shape into</LI>
<LI><I>PageCount</I> - total number of pages for 1 copy. Might be HorzPageCount * VertPageCount
but does not necessarilly have to be. </LI>
</UL>
}
TKPrintMeasureInfo = record
OutlineWidth: Integer;
OutlineHeight: Integer;
HorzPageCount: Integer;
VertPageCount: Integer;
PageCount: Integer;
end;
{ Declares possible values for the Status parameter in the @link(TKPrintNotifyEvent) event }
TKPrintStatus = (
{ This event occurs at the beginning of the print job - you may show an Abort dialog here }
epsBegin,
{ This event occurs after each page has been printed - you may update the Page/Copy information
in the Abort dialog }
epsNewPage,
{ This event occurs at the end of the print job - you may hide the Abort dialog here }
epsEnd
);
{ @abstract(Declares @link(TKCustomControl.OnPrintNotify) event handler)
<UL>
<LH>Parameters:</LH>
<LI><I>Sender</I> - identifies the event caller</LI>
<LI><I>Status</I> - specifies the event type</LI>
<LI><I>Abort</I> - set to True to abort the print job</LI>
</UL>
Remark: At certain time slots, the print spooler allows the message queue
to be processed for the thread where the print job is running. This e.g. allows
the user to press a button on the Abort dialog. Because this message loop can be invoked
e.g. during a Printer.Canvas.TextRect function and any painting messages may hover in
the message queue, any functions used both to print a job and to process particular
messages should be reentrant to avoid conflicts. Perhaps should print jobs be run
in seperate threads?
}
TKPrintNotifyEvent = procedure(Sender: TObject; Status: TKPrintStatus;
var Abort: Boolean) of object;
{ @abstract(Declares @link(TKCustomControl.OnPrintPaint) event handler)
<UL>
<LH>Parameters:</LH>
<LI><I>Sender</I> - identifies the event caller</LI>
</UL>
}
TKPrintPaintEvent = procedure(Sender: TObject) of object;
TKPrintPageSetup = class;
TKPrintPreview = class;
{ Base class for all visible controls in KControls. }
TKCustomControl = class(TCustomControl)
private
{$IFNDEF FPC}
FBorderStyle: TBorderStyle;
{$ENDIF}
{$IFNDEF COMPILER10_UP}
FMouseInClient: Boolean;
{$ENDIF}
FMemoryCanvas: TCanvas;
FMemoryCanvasRect: TRect;
FPageSetup: TKPrintPageSetup;
FUpdateLock: Integer;
FOnPrintNotify: TKPrintNotifyEvent;
FOnPrintPaint: TKPrintPaintEvent;
{$IFNDEF FPC}
procedure CMCancelMode(var Msg: TMessage); message CM_CANCELMODE;
procedure CMCtl3DChanged(var Msg: TMessage); message CM_CTL3DCHANGED;
{$ENDIF}
procedure CMMouseLeave(var Msg: TLMessage); message CM_MOUSELEAVE;
function GetCanPrint: Boolean;
function GetPageSetup: TKPrintPageSetup;
function GetPageSetupAllocated: Boolean;
procedure KMLateUpdate(var Msg: TLMessage); message KM_LATEUPDATE;
{$IFNDEF FPC}
procedure SetBorderStyle(Value: TBorderStyle);
{$ENDIF}
procedure SetPageSetup(Value: TKPrintPageSetup);
{$IFNDEF FPC}
procedure WMCancelMode(var Msg: TWMCancelMode); message WM_CANCELMODE;
{$ENDIF}
{$IFNDEF COMPILER10_UP}
procedure WMMouseLeave(var Msg: TLMessage); message KM_MOUSELEAVE;
{$ENDIF}
{$IFNDEF FPC}
procedure WMNCPaint(var Msg: TWMNCPaint); message WM_NCPAINT;
procedure WMSetCursor(var Msg: TWMSetCursor); message WM_SETCURSOR;
{$ENDIF}
procedure WMSize(var Msg: TLMSize); message LM_SIZE;
{$IFNDEF FPC}
{$IFDEF USE_THEMES}
procedure WMThemeChanged(var Msg: TMessage); message WM_THEMECHANGED;
{$ENDIF}
{$ENDIF}
protected
{ Holds the mutually inexclusive state as cXF... flags. }
FFlags: Cardinal;
{ Defines the message queue for late update. }
FMessages: array of TLMessage;
{ Gains access to the list of associated previews. }
FPreviewList: TList;
{ Adds a preview control to the internal list of associated previews. }
procedure AddPreview(APreview: TKPrintPreview);
{ Gives the descendant the possibility to adjust the associated TKPrintPageSetup
instance just before printing. }
procedure AdjustPageSetup; virtual;
{ Cancels any dragging or resizing operations performed by mouse. }
procedure CancelMode; virtual;
{ Defines additional styles. }
procedure CreateParams(var Params: TCreateParams); override;
{$IFDEF FPC}
{ Overriden method. Calls @link(TKCustomControl.UpdateSize). }
procedure CreateWnd; override;
{ Overriden method. Calls @link(TKCustomControl.UpdateSize). }
procedure DoOnChangeBounds; override;
{$ENDIF}
{ If Value is True, includes the flag specified by AFLag to @link(FFlags).
If Value is False, excludes the flag specified by AFLag from @link(FFlags). }
procedure FlagAssign(AFlag: Cardinal; Value: Boolean);
{ Excludes the flag specified by AFLag from @link(FFlags). }
procedure FlagClear(AFlag: Cardinal);
{ Includes the flag specified by AFLag to @link(FFlags). }
procedure FlagSet(AFlag: Cardinal);
{ If the flag specified by AFLag is included in @link(FFlags), FlagToggle
excludes it and vice versa. }
procedure FlagToggle(AFlag: Cardinal);
{ Invalidates the page setup settings. If page setup is required again,
it's UpdateSettings method is called. }
procedure InvalidatePageSetup;
{ Invalidates a rectangular part of the client area if control updating is not locked
by @link(TKCustomControl.LockUpdate). }
procedure InvalidateRectArea(const R: TRect); virtual;
{ Returns True if the control has a selection. }
function InternalGetSelAvail: Boolean; virtual;
{ Called in UnlockUpdate. Allows the changes to be reflected. }
procedure InternalUnlockUpdate; virtual;
{ Determines if control can be painted with OS themes. }
function IsThemed: Boolean; virtual;
{ Called from KM_LATEUPDATE. Performs late update. Override to adapt. }
procedure LateUpdate(var Msg: TLMessage); virtual;
{ Updates information about printed shape. }
procedure MeasurePages(var Info: TKPrintMeasureInfo); virtual;
{ Retrieves a message from message queue if there is one. Used for late update.}
function MessagePeek(out Msg: TLMessage): Boolean;
{ Puts a new message into the message queue. Used for late update.}
procedure MessagePoke(const Msg: TLMessage);
{ Searches the message queue for given message code. }
function MessageSearch(MsgCode: Cardinal): Boolean;
{ Responds to WM_MOUSELEAVE message. }
procedure MouseFormLeave; virtual;
{ Overriden method - see Delphi help. }
procedure MouseMove(Shift: TShiftState; X, Y: Integer); override;
{ Notifies all associated previews about a change in the associated page setup. }
procedure NotifyPreviews;
{ Overriden method - see Delphi help. Paints the entire control client area. }
procedure Paint; override;
{ Paints a page to a printer/preview canvas. }
procedure PaintPage; virtual;
{ Paints the control to the specified canvas. Must always be overriden. }
procedure PaintToCanvas(ACanvas: TCanvas); virtual; abstract;
{ Adds a message to message queue for late update. Set IfNotExists to True to
add that message only if the specified message code does not exist in the
message queue at this moment. }
procedure PostLateUpdate(const Msg: TLMessage; IfNotExists: Boolean = False);
{ Calls the @link(TKCustomControl.OnPrintNotify) event }
procedure PrintNotify(Status: TKPrintStatus; var Abort: Boolean); virtual;
{ Calls the @link(TKCustomControl.OnPrintPaint) event }
procedure PrintPaint; virtual;
{ Removse a preview control to the internal list of associated previews. }
procedure RemovePreview(APreview: TKPrintPreview);
{ Updates mouse cursor according to the state determined from current mouse
position. Returns True if cursor has been changed. }
function SetMouseCursor(X, Y: Integer): Boolean; virtual;
{ Updates the control size. Responds to WM_SIZE under Delphi and similar
notifications under Lazarus. }
procedure UpdateSize; virtual;
public
{ Creates the instance. Assigns default values to properties, allocates
default column, row and cell data. }
constructor Create(AOwner: TComponent); override;
{ Destroys the instance along with all allocated column, row and cell data.
See TObject.Destroy in Delphi help. }
destructor Destroy; override;
{ Determines whether a flag specified by AFlag is included in @link(FFlags). }
function Flag(AFlag: Cardinal): Boolean;
{ Invalidates the entire control if control updating is not locked
by @link(TKCustomControl.LockUpdate). }
procedure Invalidate; override;
{ Locks control updating so that all possibly slow operations such as all Invalidate...
methods will not be performed. This is useful e.g. when assigning many
properties at one time. Every LockUpdate call must have
a corresponding @link(TKCustomControl.UnlockUpdate) call, please use a
try-finally section. }
procedure LockUpdate;
{ Prints the control. }
procedure PrintOut;
{ Unlocks back to normal control updating and calls InternalUnlockUpdate
to reflect (possible) multiple changes made. Each @link(LockUpdate) call must
be always followed by the UnlockUpdate call. }
procedure UnlockUpdate;
{ Returns True if control updating is not locked, i.e. there is no open
LockUpdate and UnlockUpdate pair. }
function UpdateUnlocked: Boolean;
{ Determines whether a single line border is drawn around the control.
Set BorderStyle to bsSingle to add a single line border around the control.
Set BorderStyle to bsNone to omit the border. }
{$IFDEF FPC}
property BorderStyle default cBorderStyleDef;
{$ELSE}
property BorderStyle: TBorderStyle read FBorderStyle write SetBorderStyle default cBorderStyleDef;
{$ENDIF}
{ Returns True if the control has anything to print and a printer is installed. }
property CanPrint: Boolean read GetCanPrint;
{$IFNDEF COMPILER10_UP}
{ This property has the same meaning as the MouseInClient property introduced
into TWinControl in BDS 2006. }
property MouseInClient: Boolean read FMouseInClient;
{$ENDIF}
{ Setting this property causes the control to be painted to MemoryCanvas in it's
Paint method. This approach replaces PaintTo as it does not work good for all
LCL widget sets. The control is painted normally on it's Canvas and then
copied only once to MemoryCanvas. MemoryCanvas is then set to nil (not freed)
to indicate the copying is complete. }
property MemoryCanvas: TCanvas read FMemoryCanvas write FMemoryCanvas;
{ Specifies what rectangular part of the control should be copied on MemoryCanvas. }
property MemoryCanvasRect: TRect read FMemoryCanvasRect write FMemoryCanvasRect;
{ This event is called at certain phases of the actually running print job. }
property OnPrintNotify: TKPrintNotifyEvent read FOnPrintNotify write FOnPrintNotify;
{ This event is called after the shape was drawn onto the printer canvas. }
property OnPrintPaint: TKPrintPaintEvent read FOnPrintPaint write FOnPrintPaint;
{ Specifies the page setup component used for this control. }
property PageSetup: TKPrintPageSetup read GetPageSetup write SetPageSetup;
{Returns True if page setup component is allocated for this control. }
property PageSetupAllocated: Boolean read GetPageSetupAllocated;
end;
{ @abstract(Class to specify the print job parameters) }
TKPrintPageSetup = class(TPersistent)
private
FActive: Boolean;
FCanvas: TCanvas;
FControl: TKCustomControl;
FCopies: Integer;
FCurrentCopy: Integer;
FCurrentPage: Integer;
FCurrentScale: Double;
FDesktopPixelsPerInchX: Integer;
FDesktopPixelsPerInchY: Integer;
FEndPage: Integer;
FFooterSpace: Double;
FHeaderSpace: Double;
FHorzPageCount: Integer;
FIsValid: Boolean;
FMarginBottom: Double;
FMarginLeft: Double;
FMarginRight: Double;
FMarginTop: Double;
FOptions: TKPrintOptions;
FOutlineHeight: Integer;
FOutlineWidth: Integer;
FPageCount: Integer;
FPageHeight: Integer;
FPageWidth: Integer;
FPaintAreaHeight: Integer;
FPaintAreaWidth: Integer;
FPreviewing: Boolean;
FPrinterFooterSpace: Integer;
FPrinterHeaderSpace: Integer;
FPrinterMarginBottom: Integer;
FPrinterMarginLeft: Integer;
FPrinterMarginLeftMirrored: Integer;
FPrinterMarginRight: Integer;
FPrinterMarginRightMirrored: Integer;
FPrinterMarginTop: Integer;
FPrinterName: string;
FPrinterPixelsPerInchX: Integer;
FPrinterPixelsPerInchY: Integer;
FPrintingMapped: Boolean;
FRange: TKPrintRange;
FStartPage: Integer;
FScale: Integer;
FTitle: string;
FUnits: TKPrintUnits;
FUpdateLock: Integer;
FValidating: Boolean;
FVertPageCount: Integer;
function GetCanPrint: Boolean;
procedure SetCopies(Value: Integer);
procedure SetEndPage(Value: Integer);
procedure SetFooterSpace(Value: Double);
procedure SetHeaderSpace(Value: Double);
procedure SetMarginBottom(Value: Double);
procedure SetMarginLeft(Value: Double);
procedure SetMarginRight(Value: Double);
procedure SetMarginTop(Value: Double);
procedure SetOptions(Value: TKPrintOptions);
procedure SetPrinterName(const Value: string);
procedure SetPrintingMapped(Value: Boolean);
procedure SetRange(Value: TKPrintRange);
procedure SetScale(Value: Integer);
procedure SetStartPage(Value: Integer);
procedure SetUnits(Value: TKPrintUnits);
function GetSelAvail: Boolean;
protected
{ Called before new Units are set. Converts the margins to inches by default. }
procedure AfterUnitsChange; virtual;
{ Called after new Units are set. Converts the margins from inches by default. }
procedure BeforeUnitsChange; virtual;
{ Paints a page to APreview.Canvas. }
procedure PaintPageToPreview(APreview: TKPrintPreview); virtual;
{ Prints the page number at the bottom of the page, horizontally centered. }
procedure PrintPageNumber(Value: Integer); virtual;
{ Prints the title at the top of the page. }
procedure PrintTitle; virtual;
{ Updates entire printing information. }
procedure UpdateSettings; virtual;
public
{ Creates the instance. Assigns default values to properties. }
constructor Create(AControl: TKCustomControl);
{ Copies shareable properties of another TKPrintPageSetup instance
to this instance. }
procedure Assign(Source: TPersistent); override;
{ Returns a value mapped from desktop horizontal units to printer horizontal units. }
function HMap(Value: Integer): Integer;
{ Invalidates the settings. }
procedure Invalidate;
{ Prints the associated control. }
procedure PrintOut;
{ Locks page setup updating. Use this if you assign many properties at the
same time. Every LockUpdate call must have a corresponding
@link(TKPrintPageSetup.UnlockUpdate) call, please use a try-finally section. }
procedure LockUpdate; virtual;
{ Unlocks page setup updating and updates the page settings.
Each @link(TKPrintPageSetup.LockUpdate) call must be always followed
by the UnlockUpdate call. }
procedure UnlockUpdate; virtual;
{ Returns True if updating is not locked, i.e. there is no open
LockUpdate and UnlockUpdate pair. }
function UpdateUnlocked: Boolean; virtual;
{ Validates the settings. }
procedure Validate;
{ Returns a value mapped from desktop vertical units to printer vertical units. }
function VMap(Value: Integer): Integer;
{ Returns True if printing or previewing is active. }
property Active: Boolean read FActive;
{ Returns True if the control is associated and has anything to print. }
property CanPrint: Boolean read GetCanPrint;
{ Returns the Printer.Canvas or TkPrintPreview.Canvas. Do not access outside
print job. }
property Canvas: TCanvas read FCanvas;
{ Returns the control to which this TKPrintPageSetup instance is assigned. }
property Control: TKCustomControl read FControl;
{ Specifies the number of copies to print. }
property Copies: Integer read FCopies write SetCopies;
{ Returns the currently printed copy. }
property CurrentCopy: Integer read FCurrentCopy;
{ Returns the currently printed page. }
property CurrentPage: Integer read FCurrentPage;
{ Returns the horizontal scale for the printed shape, without dimension. }
property CurrentScale: Double read FCurrentScale;
{ Returns the amount of pixels per inch for the desktop device context's horizontal axis }
property DesktopPixelsPerInchX: Integer read FDesktopPixelsPerInchX;
{ Returns the amount of pixels per inch for the desktop device context's vertical axis }
property DesktopPixelsPerInchY: Integer read FDesktopPixelsPerInchY;
{ Specifies last page printed if Range is eprRange. }
property EndPage: Integer read FEndPage write SetEndPage;
{ Specifies the vertical space that should stay free for application
specific footer. Value is given in Units. }
property FooterSpace: Double read FFooterSpace write SetFooterSpace;
{ Specifies the vertical space that should stay free for application
specific header. Value is given in Units. }
property HeaderSpace: Double read FHeaderSpace write SetHeaderSpace;
{ Returns the maximum amount of pages for horizontal axis of the control. }
property HorzPageCount: Integer read FHorzPageCount;
{ Specifies the bottom margin. Value is given in Units. }
property MarginBottom: Double read FMarginBottom write SetMarginBottom;
{ Specifies the left margin. Value is given in Units. }
property MarginLeft: Double read FMarginLeft write SetMarginLeft;
{ Specifies the right margin. Value is given in Units. }
property MarginRight: Double read FMarginRight write SetMarginRight;
{ Specifies the top margin. Value is given in Units. }
property MarginTop: Double read FMarginTop write SetMarginTop;
{ Specifies the printing options. }
property Options: TKPrintOptions read FOptions write SetOptions;
{ Returns the printed shape height (maximum of all pages)
in units depending on PrintingMapped.. }
property OutlineHeight: Integer read FOutlineHeight;
{ Returns the printed shape width (maximum of all pages)
in units depending on PrintingMapped.. }
property OutlineWidth: Integer read FOutlineWidth;
{ Returns the amount of all pages. }
property PageCount: Integer read FPageCount;
{ Returns the page height in printer device context's pixels. }
property PageHeight: Integer read FPageHeight;
{ Returns the page width in printer device context's pixels. }
property PageWidth: Integer read FPageWidth;
{ Returns the top paint area width on canvas in units depending on PrintingMapped. }
property PaintAreaHeight: Integer read FPaintAreaHeight;
{ Returns the top paint area width on canvas in units depending on PrintingMapped. }
property PaintAreaWidth: Integer read FPaintAreaWidth;
{ Returns True if painting to a TKPrintPreview.Canvas is active. }
property Previewing: Boolean read FPreviewing;
{ Returns the footer space in printer device context's units. }
property PrinterFooterSpace: Integer read FPrinterFooterSpace;
{ Returns the header space in printer device context's units. }
property PrinterHeaderSpace: Integer read FPrinterHeaderSpace;
{ Returns the bottom margin in printer device context's units. }
property PrinterMarginBottom: Integer read FPrinterMarginBottom;
{ Returns the left margin in printer device context's units. }
property PrinterMarginLeft: Integer read FPrinterMarginLeft;
{ Returns the left margin in printer device context's units with respect to current page. }
property PrinterMarginLeftMirrored: Integer read FPrinterMarginLeftMirrored;
{ Returns the right margin in printer device context's units. }
property PrinterMarginRight: Integer read FPrinterMarginRight;
{ Returns the left margin in printer device context's units with respect to current page. }
property PrinterMarginRightMirrored: Integer read FPrinterMarginRightMirrored;
{ Returns the top margin in printer device context's units. }
property PrinterMarginTop: Integer read FPrinterMarginTop;
{ Specifies the printer name. }
property PrinterName: string read FPrinterName write SetPrinterName;
{ Returns the amount of pixels per inch for the printer device context's horizontal axis }
property PrinterPixelsPerInchX: Integer read FPrinterPixelsPerInchX;
{ Returns the amount of pixels per inch for the printer device context's vertical axis }
property PrinterPixelsPerInchY: Integer read FPrinterPixelsPerInchY;
{ Specifies the units for printing the control's shape and OutlineX properties.
If True, those extents are given in printer device context's pixels,
otherwise in desktop device context's pixels. It can be adjusted by the descendant
in the AdjustPageSetup method. }
property PrintingMapped: Boolean read FPrintingMapped write SetPrintingMapped;
{ Specifies the printing range. }
property Range: TKPrintRange read FRange write SetRange;
{ Returns True if the associated control has a selection. }
property SelAvail: Boolean read GetSelAvail;
{ Specifies first page printed if Range is eprRange. }
property StartPage: Integer read FStartPage write SetStartPage;
{ Specifies the requested scale for the printed shape, in percent.
If epoFitToPage is specified in Options, this parameter is ignored. }
property Scale: Integer read FScale write SetScale;
{ Specifies the document title as it appears in printer manager. }
property Title: string read FTitle write FTitle;
{ Specifies the units for print margins. }
property Units: TKPrintUnits read FUnits write SetUnits;
{ Returns the maximum amount of pages for vertical axis of the control. }
property VertPageCount: Integer read FVertPageCount;
end;
{ @abstract(Container for all colors used by @link(TKPrintPreview) class)
This container allows to group many colors into one item in object inspector.
Colors are accessible via published properties or several public Color*
properties. }
TKPreviewColors = class(TPersistent)
private
FPreview: TKPrintPreview;
function GetColor(Index: TKPreviewColorIndex): TColor;
function GetColorEx(Index: TKPreviewColorIndex): TColor;
procedure SetColor(Index: TKPreviewColorIndex; Value: TColor);
procedure SetColorEx(Index: TKPreviewColorIndex; Value: TColor);
procedure SetColors(const Value: TKColorArray);
protected
FColors: TKColorArray;
{ Initializes the color array. }
procedure Initialize; virtual;
{ Returns the specific color according to ColorScheme. }
function InternalGetColor(Index: TKPreviewColorIndex): TColor; virtual;
{ Replaces the specific color. }
procedure InternalSetColor(Index: TKPreviewColorIndex; Value: TColor); virtual;
public
{ Creates the instance. You can create a custom instance and pass it
e.g. to a @link(TKPrintPreview.Colors) property. The APreview parameter has no meaning
in this case and you may set it to nil. }
constructor Create(APreview: TKPrintPreview);
{ Copies the properties of another instance that inherits from
TPersistent into this TKPreviewColors instance. }
procedure Assign(Source: TPersistent); override;
{ Returns color for given index. }
property Color[Index: TKPreviewColorIndex]: TColor read GetColorEx write SetColorEx;
{ Returns array of colors. }
property Colors: TKColorArray read FColors write SetColors;
published
{ Specifies the paper background color. }
property Paper: TColor index ciPaper read GetColor write SetColor default cPaperDef;
{ Specifies the color of the background around paper. }
property BkGnd: TColor index ciBkGnd read GetColor write SetColor default cBkGndDef;
{ Specifies the color of the paper border. }
property Border: TColor index ciBorder read GetColor write SetColor default cBorderDef;
{ Specifies the color of the paper border when the control has input focus. }
property SelectedBorder: TColor index ciSelectedBorder read GetColor write SetColor default cSelectedBorderDef;
end;
{ @abstract(Print preview control for the TKCustomControl component) }
TKPrintPreview = class(TKCustomControl)
private
FColors: TKPreviewColors;
FControl: TKCustomControl;
FMouseWheelAccumulator: Integer;
FPage: Integer;
FPageOld: Integer;
FPageSize: TPoint;
FExtent: TPoint;
FPageOffset: TPoint;
FScale: Integer;
FScaleMode: TKPreviewScaleMode;
FScrollExtent: TPoint;
FScrollPos: TPoint;
FScrollPosOld: TPoint;
FX: Integer;
FY: Integer;
FOnChanged: TKPreviewChangedEvent;
function GetCurrentScale: Integer;
function GetEndPage: Integer;
function GetStartPage: Integer;
procedure SetControl(Value: TKCustomControl);
procedure SetPage(Value: Integer);
procedure SetScale(Value: Integer);
procedure SetScaleMode(Value: TKPreviewScaleMode);
procedure WMEraseBkgnd(var Msg: TLMessage); message LM_ERASEBKGND;
procedure WMGetDlgCode(var Msg: TLMNoParams); message LM_GETDLGCODE;
procedure WMHScroll(var Msg: TLMHScroll); message LM_HSCROLL;
procedure WMKillFocus(var Msg: TLMKillFocus); message LM_KILLFOCUS;
procedure WMSetFocus(var Msg: TLMSetFocus); message LM_SETFOCUS;
procedure WMVScroll(var Msg: TLMVScroll); message LM_VSCROLL;
procedure SetColors(const Value: TKPreviewColors);
protected
{ Initializes a scroll message handling. }
procedure BeginScrollWindow;
{ Defines additional styles. }
procedure CreateParams(var Params: TCreateParams); override;
{ Overriden method - handles mouse wheel messages. }
function DoMouseWheel(Shift: TShiftState; WheelDelta: Integer;
MousePos: TPoint): Boolean; override;
{ Calls the ScrollWindowEx function to complete a scroll message. }
procedure EndScrollWindow;
{ Returns current page rectangle inside of the window client area. }
function GetPageRect: TRect;
{ Processes virtual key strokes. }
procedure KeyDown(var Key: Word; Shift: TShiftState); override;
{ Processes scrollbar messages.
<UL>
<LH>Parameters:</LH>
<LI><I>ScrollBar</I> - scrollbar type from OS</LI>
<LI><I>ScrollCode</I> - scrollbar action from OS</LI>
<LI><I>Delta</I> - scrollbar position change</LI>
</UL> }
procedure ModifyScrollBar(ScrollBar, ScrollCode, Delta: Integer);
{ Initializes drag&scroll functionality. }
procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); override;
{ Performs drag&scroll functionality. }
procedure MouseMove(Shift: TShiftState; X, Y: Integer); override;
{ Finalizes drag&scroll functionality. }
procedure MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); override;
{ Notifies about associated TKCustomControl control removal. }
procedure Notification(AComponent: TComponent; Operation: TOperation); override;
{ Paints paper and control shape. }
procedure Paint; override;
{ Calls the @link(OnChanged) event. }
procedure Changed;
{ Grants the input focus to the control when possible and the control has had none before. }
procedure SafeSetFocus;
{ Updates mouse cursor. }
function SetMouseCursor(X, Y: Integer): Boolean; override;
{ Updates page sizes and scrollbar ranges. }
procedure UpdateScrollRange;
{ Updates the control size. }
procedure UpdateSize; override;
public
{ Performs necessary initializations - default values to properties. }
constructor Create(AOwner: TComponent); override;
{ Destroy instance... }
destructor Destroy; override;
{ Shows first page for the given range. }
procedure FirstPage;
{ Shows last page for the given range. }
procedure LastPage;
{ Shows next page. }
procedure NextPage;
{ Shows previous page. }
procedure PreviousPage;
{ Updates the preview. }
procedure UpdatePreview;
{ Returns the page scaling with regard to the @link(ScaleMode) property. }
property CurrentScale: Integer read GetCurrentScale;
{ Returns the current page area rectangle in desktop pixels. }
property PageRect: TRect read GetPageRect;
{ Returns the last page for the given range. }
property EndPage: Integer read GetEndPage;
{ Returns the first page for the given range. }
property StartPage: Integer read GetStartPage;
published
{ Inherited property - see Delphi help. }
property Align;
{ Inherited property - see Delphi help. }
property Anchors;
{ See TKCustomControl.@link(TKCustomControl.BorderStyle) for details. }
property BorderStyle;
{ Inherited property - see Delphi help. }
property BorderWidth;
{ Specifies all colors used by TKPrintPreview's default painting. }
property Colors: TKPreviewColors read FColors write SetColors;
{ Inherited property - see Delphi help. }
property Constraints;
{ Specifies the associated control. }
property Control: TKCustomControl read FControl write SetControl;
{ Inherited property - see Delphi help. }
property DragCursor;
{ Inherited property - see Delphi help. }
property DragKind;
{ Inherited property - see Delphi help. }
property DragMode;
{ Specifies the currently displayed page. }
property Page: Integer read FPage write SetPage default 1;
{ Inherited property - see Delphi help. }
property ParentShowHint;
{ Inherited property - see Delphi help. }
property PopupMenu;
{ Specifies the user defined page scale - i.e. when ScaleMode = smScale. }
property Scale: Integer read FScale write SetScale default 100;
{ Specifies the scale mode to display and scroll previewed pages. }
property ScaleMode: TKPreviewScaleMode read FScaleMode write SetScaleMode default smPageWidth;
{ Inherited property - see Delphi help. }
property ShowHint;
{ Inherited property - see Delphi help. }
property TabStop;
{ Inherited property - see Delphi help. }
property TabOrder;
{ Inherited property - see Delphi help. }
property Visible;
{ Called whenever print preview is updated. }
property OnChanged: TKPreviewChangedEvent read FOnChanged write FOnChanged;
{ Inherited property - see Delphi help. }
property OnClick;
{ Inherited property - see Delphi help. }
property OnContextPopup;
{ Inherited property - see Delphi help. }
property OnDblClick;
{ Inherited property - see Delphi help. }
property OnDockDrop;
{ Inherited property - see Delphi help. }
property OnDockOver;
{ Inherited property - see Delphi help. }
property OnDragDrop;
{ Inherited property - see Delphi help. }
property OnDragOver;
{ Inherited property - see Delphi help. }
property OnEndDock;
{ Inherited property - see Delphi help. }
property OnEndDrag;
{ Inherited property - see Delphi help. }
property OnEnter;
{ Inherited property - see Delphi help. }
property OnExit;
{ Inherited property - see Delphi help. }
property OnGetSiteInfo;
{ Inherited property - see Delphi help. }
property OnKeyDown;
{ Inherited property - see Delphi help. }
property OnKeyPress;
{ Inherited property - see Delphi help. }
property OnKeyUp;
{ Inherited property - see Delphi help. }
property OnMouseDown;
{ Inherited property - see Delphi help. }
property OnMouseMove;
{ Inherited property - see Delphi help. }
property OnMouseUp;
{ Inherited property - see Delphi help. }
property OnMouseWheel;
{ Inherited property - see Delphi help. }
property OnMouseWheelDown;
{ Inherited property - see Delphi help. }
property OnMouseWheelUp;
{ Inherited property - see Delphi help. }
property OnResize;
{ Inherited property - see Delphi help. }
property OnStartDock;
{ Inherited property - see Delphi help. }
property OnStartDrag;
{ Inherited property - see Delphi help. }
property OnUnDock;
end;
{ Converts a value given in inches into a value given in specified units.
<UL>
<LH>Parameters:</LH>
<LI><I>Units</I> - measurement units for the output value</LI>
<LI><I>Value</I> - input value to convert</LI>
</UL> }
function InchesToValue(Units: TKPrintUnits; Value: Double): Double;
{ Converts value given in specified units into a value given in inches.
<UL>
<LH>Parameters:</LH>
<LI><I>Units</I> - measurement units for the input value</LI>
<LI><I>Value</I> - input value to convert</LI>
</UL> }
function ValueToInches(Units: TKPrintUnits; Value: Double): Double;
implementation
uses
Math, Printers, KGraphics;
const
cPreviewHorzBorder = 30;
cPreviewVertBorder = 30;
cPreviewShadowSize = 3;
function InchesToValue(Units: TKPrintUnits; Value: Double): Double;
begin
case Units of
puMM: Result := Value * 25.4;
puCM: Result := Value * 2.54;
puHundredthInch: Result := Value * 100;
else
Result := Value;
end;
end;
function ValueToInches(Units: TKPrintUnits; Value: Double): Double;
begin
case Units of
puMM: Result := Value / 25.4;
puCM: Result := Value / 2.54;
puHundredthInch: Result := Value / 100;
else
Result := Value;
end;
end;
{ TKCustomControl }
constructor TKCustomControl.Create(AOwner: TComponent);
begin
inherited;
BorderStyle := cBorderStyleDef;
FFlags := 0;
FMemoryCanvas := nil;
FMessages := nil;
{$IFNDEF COMPILER10_UP}
FMouseInClient := False;
{$ENDIF}
FPageSetup := nil;
FPreviewList := TList.Create;
FUpdateLock := 0;
FOnPrintNotify := nil;
FOnPrintPaint := nil;
end;
destructor TKCustomControl.Destroy;
begin
inherited;
FMessages := nil;
FreeAndNil(FPreviewList);
FreeAndNil(FPageSetup);
end;
procedure TKCustomControl.AddPreview(APreview: TKPrintPreview);
begin
if Assigned(APreview) then
FPreviewList.Add(APreview);
end;
procedure TKCustomControl.AdjustPageSetup;
begin
end;
procedure TKCustomControl.CancelMode;
begin
end;
{$IFNDEF FPC}
procedure TKCustomControl.CMCancelMode(var Msg: TLMessage);
begin
inherited;
CancelMode;
end;
procedure TKCustomControl.CMCtl3DChanged(var Msg: TLMessage);
begin
inherited;
RecreateWnd;
end;
{$ENDIF}
procedure TKCustomControl.CMMouseLeave(var Msg: TLMessage);
begin
inherited;
try
MouseFormLeave;
except
end;
end;
procedure TKCustomControl.CreateParams(var Params: TCreateParams);
begin
inherited;
{$IFNDEF FPC}
with Params do
begin
WindowClass.style := CS_DBLCLKS;
if BorderStyle = bsSingle then
if NewStyleControls and Ctl3D then
begin
Style := Style and not WS_BORDER;
ExStyle := ExStyle or WS_EX_CLIENTEDGE;
end
else
Style := Style or WS_BORDER;
end;
{$ENDIF}
end;
{$IFDEF FPC}
procedure TKCustomControl.CreateWnd;
begin
inherited;
UpdateSize;
end;
procedure TKCustomControl.DoOnChangeBounds;
begin
inherited;
UpdateSize;
end;
{$ENDIF}
function TKCustomControl.Flag(AFlag: Cardinal): Boolean;
begin
Result := FFlags and AFlag <> 0;
end;
procedure TKCustomControl.FlagAssign(AFlag: Cardinal; Value: Boolean);
begin
if Value then
FlagSet(AFlag)
else
FlagClear(AFlag);
end;
procedure TKCustomControl.FlagClear(AFlag: Cardinal);
begin
FFlags := FFlags and not AFlag;
end;
procedure TKCustomControl.FlagSet(AFlag: Cardinal);
begin
FFlags := FFlags or AFlag;
end;
procedure TKCustomControl.FlagToggle(AFlag: Cardinal);
begin
FFlags := FFlags xor AFlag;
end;
function TKCustomControl.GetCanPrint: Boolean;
begin
Result := PageSetup.CanPrint;
end;
function TKCustomControl.GetPageSetup: TKPrintPageSetup;
begin
if not Assigned(FPageSetup) and not (csDestroying in ComponentState) then
begin
FPageSetup := TKPrintPageSetup.Create(Self);
AdjustPageSetup;
end;
if Assigned(FPageSetup) then
FPageSetup.Validate;
Result := FPageSetup;
end;
function TKCustomControl.GetPageSetupAllocated: Boolean;
begin
Result := Assigned(FPageSetup);
end;
function TKCustomControl.InternalGetSelAvail: Boolean;
begin
Result := False;
end;
procedure TKCustomControl.InternalUnlockUpdate;
begin
end;
procedure TKCustomControl.Invalidate;
begin
if UpdateUnlocked and HandleAllocated then
inherited;
end;
procedure TKCustomControl.InvalidatePageSetup;
begin
if Assigned(FPageSetup) then
FPageSetup.Invalidate;
end;
procedure TKCustomControl.InvalidateRectArea(const R: TRect);
begin
if UpdateUnlocked and HandleAllocated then
InvalidateRect(Handle, @R, False);
end;
function TKCustomControl.IsThemed: Boolean;
begin
Result := True;
end;
procedure TKCustomControl.KMLateUpdate(var Msg: TLMessage);
var
M: TLMessage;
begin
if MessagePeek(M) then
LateUpdate(M);
end;
procedure TKCustomControl.LateUpdate(var Msg: TLMessage);
begin
case Msg.Msg of
LM_SIZE: UpdateSize;
end;
end;
procedure TKCustomControl.LockUpdate;
begin
Inc(FUpdateLock);
end;
procedure TKCustomControl.MeasurePages(var Info: TKPrintMeasureInfo);
begin
end;
function TKCustomControl.MessagePeek(out Msg: TLMessage): Boolean;
var
ALen: Integer;
begin
ALen := Length(FMessages);
if ALen > 0 then
begin
Dec(ALen);
Msg := FMessages[ALen];
SetLength(FMessages, ALen);
Result := True;
end else
Result := False;
end;
procedure TKCustomControl.MessagePoke(const Msg: TLMessage);
var
ALen: Integer;
begin
ALen := Length(FMessages);
SetLength(FMessages, ALen + 1);
FMessages[ALen] := Msg;
end;
function TKCustomControl.MessageSearch(MsgCode: Cardinal): Boolean;
var
I: Integer;
begin
Result := False;
for I := 0 to Length(FMessages) - 1 do
if FMessages[I].Msg = MsgCode then
begin
Result := True;
Exit;
end;
end;
procedure TKCustomControl.MouseFormLeave;
begin
end;
procedure TKCustomControl.MouseMove(Shift: TShiftState; X, Y: Integer);
begin
inherited;
{$IFNDEF COMPILER10_UP}
CallTrackMouseEvent(Self, FMouseInClient);
{$ENDIF}
{$IFDEF FPC}
if not MouseCapture then
SetMouseCursor(X, Y);
{$ENDIF}
end;
procedure TKCustomControl.NotifyPreviews;
var
I: Integer;
begin
for I := 0 to FPreviewList.Count - 1 do
TKPrintPreview(FPreviewList[I]).UpdatePreview;
end;
procedure TKCustomControl.Paint;
begin
PaintToCanvas(Canvas);
if Assigned(FMemoryCanvas) then
begin
{$IFDEF USE_WINAPI}
// this is the best method but does not work both on QT and GTK!
MoveWindowOrg(FMemoryCanvas.Handle, -FMemoryCanvasRect.Left, -FMemoryCanvasRect.Top);
try
PaintToCanvas(FMemoryCanvas);
finally
MoveWindowOrg(FMemoryCanvas.Handle, FMemoryCanvasRect.Left, FMemoryCanvasRect.Top);
end;
{$ELSE}
FMemoryCanvas.CopyRect(Rect(0, 0, FMemoryCanvasRect.Right - FMemoryCanvasRect.Left,
FMemoryCanvasRect.Bottom - FMemoryCanvasRect.Top), Canvas, FMemoryCanvasRect);
{$ENDIF}
FMemoryCanvas := nil;
end;
end;
procedure TKCustomControl.PostLateUpdate(const Msg: TLMessage;
IfNotExists: Boolean);
begin
if HandleAllocated then
begin
if not IfNotExists or not MessageSearch(Msg.Msg) then
MessagePoke(Msg);
PostMessage(Handle, KM_LATEUPDATE, 0, 0);
end;
end;
procedure TKCustomControl.PrintNotify(Status: TKPrintStatus; var Abort: Boolean);
begin
if Assigned(FOnPrintNotify) then
FOnPrintNotify(Self, Status, Abort);
end;
procedure TKCustomControl.PrintPaint;
begin
if Assigned(FOnPrintPaint) then
FOnPrintPaint(Self);
end;
procedure TKCustomControl.PrintOut;
begin
GetPageSetup.PrintOut;
end;
procedure TKCustomControl.PaintPage;
begin
end;
procedure TKCustomControl.RemovePreview(APreview: TKPrintPreview);
begin
if Assigned(FPreviewList) and (FPreviewList.IndexOf(APreview) >= 0) then
FPreviewList.Remove(APreview);
end;
{$IFNDEF FPC}
procedure TKCustomControl.SetBorderStyle(Value: TBorderStyle);
begin
if FBorderStyle <> Value then
begin
FBorderStyle := Value;
RecreateWnd;
end;
end;
{$ENDIF}
function TKCustomControl.SetMouseCursor(X, Y: Integer): Boolean;
begin
Result := False;
end;
procedure TKCustomControl.SetPageSetup(Value: TKPrintPageSetup);
begin
if Value <> FPageSetup then
GetPageSetup.Assign(Value);
end;
procedure TKCustomControl.UnlockUpdate;
begin
if FUpdateLock > 0 then
begin
Dec(FUpdateLock);
if FUpdateLock = 0 then
InternalUnlockUpdate;
end;
end;
procedure TKCustomControl.UpdateSize;
begin
end;
function TKCustomControl.UpdateUnlocked: Boolean;
begin
Result := FUpdateLock = 0;
end;
{$IFNDEF FPC}
procedure TKCustomControl.WMCancelMode(var Msg: TWMCancelMode);
begin
inherited;
CancelMode;
end;
{$ENDIF}
{$IFNDEF COMPILER10_UP}
procedure TKCustomControl.WMMouseLeave(var Msg: TLMessage);
begin
{ this is because of CM_MOUSELEAVE is not sent if mouse has left client area
and entered any of the standard control scrollbars. This behavior has been
fixed via TrackMouseEvent in BDS 2006. }
inherited;
FMouseInClient := False;
Perform(CM_MOUSELEAVE, 0, 0);
end;
{$ENDIF}
{$IFNDEF FPC}
procedure TKCustomControl.WMNCPaint(var Msg: TWMNCPaint);
{$IFDEF USE_THEMES}
var
R: TRect;
ExStyle: Integer;
TempRgn: HRGN;
BorderWidth,
BorderHeight: Integer;
{$ENDIF}
begin
{$IFDEF USE_THEMES}
with ThemeServices do if IsThemed and ThemesEnabled then
begin
// If OS themes are enabled and the client edge border is set for the window then prevent the default window proc
// from painting the old border to avoid flickering.
ExStyle := GetWindowLong(Handle, GWL_EXSTYLE);
if (ExStyle and WS_EX_CLIENTEDGE) <> 0 then
begin
GetWindowRect(Handle, R);
// Determine width of the client edge.
BorderWidth := GetSystemMetrics(SM_CXEDGE);
BorderHeight := GetSystemMetrics(SM_CYEDGE);
InflateRect(R, -BorderWidth, -BorderHeight);
TempRgn := CreateRectRgnIndirect(R);
// Exclude the border from the message region if there is one. Otherwise just use the inflated
// window area region.
if Msg.Rgn <> 1 then
CombineRgn(TempRgn, Msg.Rgn, TempRgn, RGN_AND);
DefWindowProc(Handle, Msg.Msg, Integer(TempRgn), 0);
DeleteObject(TempRgn);
PaintBorder(Self, True);
end else
inherited;
end else
{$ENDIF}
inherited;
end;
procedure TKCustomControl.WMSetCursor(var Msg: TWMSetCursor);
var
MousePt: TPoint;
begin
if (Msg.HitTest = HTCLIENT) and (Msg.CursorWnd = Handle) then
begin
MousePt := ScreenToClient(Mouse.CursorPos);
if SetMouseCursor(MousePt.X, MousePt.Y) then
Msg.Result := 1
else
inherited
end else
inherited;
end;
{$ENDIF}
procedure TKCustomControl.WMSize(var Msg: TLMSize);
begin
inherited;
PostLateUpdate(FillMessage(LM_SIZE, 0, 0), True);
end;
{$IFNDEF FPC}
{$IFDEF USE_THEMES}
procedure TKCustomControl.WMThemeChanged(var Msg: TLMessage);
begin
if IsThemed then
begin
inherited;
ThemeServices.UpdateThemes;
RedrawWindow(Handle, nil, 0, RDW_INVALIDATE or RDW_VALIDATE or RDW_FRAME);
end;
end;
{$ENDIF}
{$ENDIF}
{ TKPrintPageSetup }
constructor TKPrintPageSetup.Create(AControl: TKCustomControl);
begin
inherited Create;
FActive := False;
FCanvas := nil;
FControl := AControl;
FCopies := cCopiesDef;
FCurrentCopy := 0;
FCurrentPage := 0;
FCurrentScale := 0;
FDesktopPixelsPerInchX := 0;
FDesktopPixelsPerInchY := 0;
FEndPage := 0;
FFooterSpace := 0;
FHeaderSpace := 0;
FHorzPageCount := 0;
FIsValid := False;
FMarginBottom := cMarginBottomDef;
FMarginLeft := cMarginLeftDef;
FMarginRight := cMarginRightDef;
FMarginTop := cMarginTopDef;
FOptions := cOptionsDef;
FOutlineHeight := 0;
FOutlineWidth := 0;
FPageCount := 0;
FPageHeight := 0;
FPageWidth := 0;
FPaintAreaHeight := 0;
FPaintAreaWidth := 0;
FPreviewing := False;
FPrinterFooterSpace := 0;
FPrinterHeaderSpace := 0;
FPrinterMarginBottom := 0;
FPrinterMarginLeft := 0;
FPrinterMarginLeftMirrored := 0;
FPrinterMarginRight := 0;
FPrinterMarginRightMirrored := 0;
FPrinterMarginTop := 0;
FPrinterName := '';
FPrinterPixelsPerInchX := 0;
FPrinterPixelsPerInchY := 0;
FPrintingMapped := True;
FRange := cRangeDef;
FStartPage := 0;
FScale := cScaleDef;
FTitle := '';
FUnits := cUnitsDef;
FUpdateLock := 0;
FValidating := False;
FVertPageCount := 0;
end;
function TKPrintPageSetup.GetCanPrint: Boolean;
begin
Result := Assigned(FControl) and (FPageCount > 0) and (Printer.Printers.Count > 0);
end;
function TKPrintPageSetup.GetSelAvail: Boolean;
begin
if Assigned(FControl) then
Result := FControl.InternalGetSelAvail
else
Result := False;
end;
procedure TKPrintPageSetup.AfterUnitsChange;
begin
FFooterSpace := InchesToValue(FUnits, FFooterSpace);
FHeaderSpace := InchesToValue(FUnits, FHeaderSpace);
FMarginBottom := InchesToValue(FUnits, FMarginBottom);
FMarginLeft := InchesToValue(FUnits, FMarginLeft);
FMarginRight := InchesToValue(FUnits, FMarginRight);
FMarginTop := InchesToValue(FUnits, FMarginTop);
end;
procedure TKPrintPageSetup.Assign(Source: TPersistent);
begin
if Source is TKPrintPageSetup then
begin
LockUpdate;
try
Copies := TKPrintPageSetup(Source).Copies;
EndPage := TKPrintPageSetup(Source).EndPage;
FooterSpace := TKPrintPageSetup(Source).FooterSpace;
HeaderSpace := TKPrintPageSetup(Source).HeaderSpace;
MarginBottom := TKPrintPageSetup(Source).MarginBottom;
MarginLeft := TKPrintPageSetup(Source).MarginLeft;
MarginRight := TKPrintPageSetup(Source).MarginRight;
MarginTop := TKPrintPageSetup(Source).MarginTop;
Options := TKPrintPageSetup(Source).Options;
PrinterName := TKPrintPageSetup(Source).PrinterName;
Range := TKPrintPageSetup(Source).Range;
StartPage := TKPrintPageSetup(Source).StartPage;
Scale := TKPrintPageSetup(Source).Scale;
Title := TKPrintPageSetup(Source).Title;
Units := TKPrintPageSetup(Source).Units;
finally
UnlockUpdate;
end;
end;
end;
procedure TKPrintPageSetup.BeforeUnitsChange;
begin
FFooterSpace := ValueToInches(FUnits, FFooterSpace);
FHeaderSpace := ValueToInches(FUnits, FHeaderSpace);
FMarginBottom := ValueToInches(FUnits, FMarginBottom);
FMarginLeft := ValueToInches(FUnits, FMarginLeft);
FMarginRight := ValueToInches(FUnits, FMarginRight);
FMarginTop := ValueToInches(FUnits, FMarginTop);
end;
function TKPrintPageSetup.HMap(Value: Integer): Integer;
begin
Result := MulDiv(Value, FPrinterPixelsPerInchX, FDesktopPixelsPerInchX);
end;
procedure TKPrintPageSetup.Invalidate;
begin
FIsValid := False;
end;
procedure TKPrintPageSetup.LockUpdate;
begin
Inc(FUpdateLock);
end;
procedure TKPrintPageSetup.PaintPageToPreview;
var
PaperWidth, PaperHeight, SaveIndex: Integer;
R, PageRect: TRect;
begin
if UpdateUnlocked and Assigned(FControl) then
begin
FCanvas := APreview.Canvas;
FActive := True;
FPreviewing := True;
try
FCurrentCopy := 1;
FCurrentPage := APreview.Page;
if (poMirrorMargins in FOptions) and (FCurrentPage and 1 <> 0) then
begin
FPrinterMarginLeftMirrored := FPrinterMarginRight;
FPrinterMarginRightMirrored := FPrinterMarginLeft;
end else
begin
FPrinterMarginLeftMirrored := FPrinterMarginLeft;
FPrinterMarginRightMirrored := FPrinterMarginRight;
end;
R := APreview.PageRect;
PaperWidth := R.Right - R.Left;
PaperHeight := R.Bottom - R.Top;
SaveIndex := SaveDC(FCanvas.Handle);
try
// change the canvas mapping mode to scale the page outline
CanvasSetOffset(FCanvas,
R.Left + MulDiv(FPrinterMarginLeftMirrored, PaperWidth, FPageWidth),
R.Top + MulDiv(FPrinterMarginTop + FPrinterHeaderSpace, PaperHeight, FPageHeight));
if FPrintingMapped then
CanvasSetScale(FCanvas, Round(PaperWidth * FCurrentScale), Round(PaperHeight * FCurrentScale),
MulDiv(FPageWidth, FDesktopPixelsPerInchX, FPrinterPixelsPerInchX),
MulDiv(FPageHeight, FDesktopPixelsPerInchY, FPrinterPixelsPerInchY))
else
CanvasSetScale(FCanvas, PaperWidth, PaperHeight, FPageWidth, FPageHeight);
FControl.PaintPage;
finally
RestoreDC(FCanvas.Handle, SaveIndex);
end;
SaveIndex := SaveDC(FCanvas.Handle);
try
CanvasSetOffset(FCanvas, R.Left, R.Top);
CanvasSetScale(FCanvas, PaperWidth, PaperHeight, FPageWidth, FPageHeight);
PageRect := Rect(0, 0, FPageWidth, FPageHeight);
TranslateRectToDevice(FCanvas.Handle, PageRect);
SelectClipRect(FCanvas.Handle, PageRect);
FControl.PrintPaint;
finally
RestoreDC(FCanvas.Handle, SaveIndex);
end;
SaveIndex := SaveDC(FCanvas.Handle);
try
CanvasSetOffset(FCanvas, R.Left, R.Top);
CanvasSetScale(FCanvas, PaperWidth, PaperHeight, FPageWidth, FPageHeight);
PageRect := Rect(0, 0, FPageWidth, FPageHeight);
TranslateRectToDevice(FCanvas.Handle, PageRect);
SelectClipRect(FCanvas.Handle, PageRect);
PrintTitle;
PrintPageNumber(FCurrentPage);
finally
RestoreDC(FCanvas.Handle, SaveIndex);
end;
finally
FActive := False;
FPreviewing := False;
FCanvas := nil;
end;
end;
end;
procedure TKPrintPageSetup.PrintPageNumber(Value: Integer);
var
S: string;
begin
if poPageNumbers in FOptions then
begin
FCanvas.Brush.Style := bsClear;
FCanvas.Font.Color := clBlack;
FCanvas.Font.Height := 1;
FCanvas.Font.Height := VMap(16);
FCanvas.Font.Name := 'Arial';
FCanvas.Font.Pitch := fpDefault;
FCanvas.Font.Style := [fsBold];
S := Format('- %d -', [Value]);
FCanvas.TextOut(FPrinterMarginLeftMirrored + (FPageWidth - FPrinterMarginLeft - FPrinterMarginRight - FCanvas.TextWidth(S)) div 2,
FPageHeight - FPrinterMarginBottom + VMap(5), S);
end;
end;
procedure TKPrintPageSetup.PrintTitle;
begin
if poTitle in FOptions then
begin
FCanvas.Brush.Style := bsClear;
FCanvas.Font.Color := clBlack;
FCanvas.Font.Height := 1;
FCanvas.Font.Height := VMap(16);
FCanvas.Font.Name := 'Arial';
FCanvas.Font.Pitch := fpDefault;
FCanvas.Font.Style := [fsBold];
FCanvas.TextOut(FPrinterMarginLeftMirrored, FPrinterMarginTop - VMap(36), Title);
FCanvas.Brush.Style := bsSolid;
FCanvas.Brush.Color := clBlack;
FCanvas.FillRect(Rect(FPrinterMarginLeftMirrored, FPrinterMarginTop - VMap(14), FPageWidth - FPrinterMarginRight, FPrinterMarginTop - VMap(12)));
end;
end;
procedure TKPrintPageSetup.PrintOut;
function DoPrint: Boolean;
var
SaveIndex: Integer;
PageRect: TRect;
begin
Result := False;
if (poMirrorMargins in FOptions) and (FCurrentPage and 1 <> 0) then
begin
FPrinterMarginLeftMirrored := FPrinterMarginRight;
FPrinterMarginRightMirrored := FPrinterMarginLeft;
end else
begin
FPrinterMarginLeftMirrored := FPrinterMarginLeft;
FPrinterMarginRightMirrored := FPrinterMarginRight;
end;
SaveIndex := SaveDC(FCanvas.Handle);
try
CanvasSetOffset(FCanvas, FPrinterMarginLeftMirrored, FPrinterMarginTop + FPrinterHeaderSpace);
if FPrintingMapped then
begin
// change the canvas mapping mode to scale the page outline
CanvasSetScale(FCanvas, Round(FPageWidth * FCurrentScale), Round(FPageHeight * FCurrentScale),
MulDiv(FPageWidth, FDesktopPixelsPerInchX, FPrinterPixelsPerInchX),
MulDiv(FPageHeight, FDesktopPixelsPerInchY, FPrinterPixelsPerInchY));
end else
CanvasResetScale(FCanvas);
FControl.PaintPage;
finally
RestoreDC(FCanvas.Handle, SaveIndex);
end;
SaveIndex := SaveDC(FCanvas.Handle);
try
CanvasResetScale(FCanvas);
PageRect := Rect(0, 0, FPageWidth, FPageHeight);
TranslateRectToDevice(FCanvas.Handle, PageRect);
SelectClipRect(FCanvas.Handle, PageRect);
FControl.PrintPaint;
finally
RestoreDC(FCanvas.Handle, SaveIndex);
end;
SaveIndex := SaveDC(FCanvas.Handle);
try
CanvasResetScale(FCanvas);
PageRect := Rect(0, 0, FPageWidth, FPageHeight);
TranslateRectToDevice(FCanvas.Handle, PageRect);
SelectClipRect(FCanvas.Handle, PageRect);
PrintTitle;
PrintPageNumber(FCurrentPage);
finally
RestoreDC(FCanvas.Handle, SaveIndex);
end;
FControl.PrintNotify(epsNewPage, Result);
if ((FCurrentPage < FEndPage) or (FCurrentCopy < FCopies)) and not Result then
Printer.NewPage;
end;
var
I, J: Integer;
AbortPrint: Boolean;
{ Orientation: TPrinterOrientation;
PaperSize: TPaperSize;
APageWidth, ApageHeight, APaperWidth, APaperHeight: Integer;
PrinterType: TPrinterType;
APaperRect: TPaperRect;}
begin
if UpdateUnlocked and Assigned(FControl) then
begin
UpdateSettings;
if FPageCount > 0 then
begin
AbortPrint := False;
FCanvas := Printer.Canvas;
Printer.Title := FTitle;
Printer.Copies := 1;
{ PrinterType := Printer.PrinterType;
APageWidth := Printer.PageWidth;
APageHeight := Printer.PageHeight;
APaperRect := Printer.PaperSize.PaperRect;
Orientation := Printer.Orientation;}
Printer.BeginDoc;
FActive := True;
try
FControl.PrintNotify(epsBegin, AbortPrint);
{ Printer.Canvas.Font.Name := 'Arial';
Printer.Canvas.Font.color := clBlack;
Printer.Canvas.Font.height := 100;
Printer.Canvas.TextOut(200, 200, 'hello!');}
if not AbortPrint then
begin
if poCollate in FOptions then
for I := 1 to FCopies do
begin
FCurrentCopy := I;
for J := FStartPage to FEndPage do
begin
FCurrentPage := J;
AbortPrint := DoPrint;
if AbortPrint then Break;
end;
if AbortPrint then Break;
end
else
for J := FStartPage to FEndPage do
begin
FCurrentPage := J;
for I := 1 to FCopies do
begin
FCurrentCopy := I;
AbortPrint := DoPrint;
if AbortPrint then Break;
end;
if AbortPrint then Break;
end
end;
FCurrentPage := 0;
FCurrentCopy := 0;
FControl.PrintNotify(epsEnd, AbortPrint);
finally
FActive := False;
Printer.EndDoc;
FCanvas := nil;
end;
end;
end;
end;
procedure TKPrintPageSetup.SetCopies(Value: Integer);
begin
if FActive then Exit;
if Value <> FCopies then
begin
FCopies := Value;
UpdateSettings;
end;
end;
procedure TKPrintPageSetup.SetEndPage(Value: Integer);
begin
if FActive then Exit;
if Value <> FEndPage then
begin
FEndPage := Value;
UpdateSettings;
end;
end;
procedure TKPrintPageSetup.SetFooterSpace(Value: Double);
begin
if FActive then Exit;
if Value <> FFooterSpace then
begin
FFooterSpace := Value;
UpdateSettings;
end;
end;
procedure TKPrintPageSetup.SetHeaderSpace(Value: Double);
begin
if FActive then Exit;
if Value <> FHeaderSpace then
begin
FHeaderSpace := Value;
UpdateSettings;
end;
end;
procedure TKPrintPageSetup.SetMarginBottom(Value: Double);
begin
if FActive then Exit;
if Value <> FMarginBottom then
begin
FMarginBottom := Value;
UpdateSettings;
end;
end;
procedure TKPrintPageSetup.SetMarginLeft(Value: Double);
begin
if FActive then Exit;
if Value <> FMarginLeft then
begin
FMarginLeft := Value;
UpdateSettings;
end;
end;
procedure TKPrintPageSetup.SetMarginRight(Value: Double);
begin
if FActive then Exit;
if Value <> FMarginRight then
begin
FMarginRight := Value;
UpdateSettings;
end;
end;
procedure TKPrintPageSetup.SetMarginTop(Value: Double);
begin
if FActive then Exit;
if Value <> FMarginTop then
begin
FMarginTop := Value;
UpdateSettings;
end;
end;
procedure TKPrintPageSetup.SetOptions(Value: TKPrintOptions);
begin
if FActive then Exit;
if Value <> FOptions then
begin
FOptions := Value;
UpdateSettings;
end;
end;
procedure TKPrintPageSetup.SetPrinterName(const Value: string);
begin
if FActive then Exit;
if Value <> FPrinterName then
begin
FPrinterName := Value;
UpdateSettings;
end;
end;
procedure TKPrintPageSetup.SetPrintingMapped(Value: Boolean);
begin
if FActive then Exit;
if Value <> FPrintingMapped then
begin
FPrintingMapped := Value;
UpdateSettings;
end;
end;
procedure TKPrintPageSetup.SetRange(Value: TKPrintRange);
begin
if FActive then Exit;
if Value <> FRange then
begin
FRange := Value;
UpdateSettings;
end;
end;
procedure TKPrintPageSetup.SetScale(Value: Integer);
begin
if FActive then Exit;
if Value <> FScale then
begin
FScale := Value;
UpdateSettings;
end;
end;
procedure TKPrintPageSetup.SetStartPage(Value: Integer);
begin
if FActive then Exit;
if Value <> FStartPage then
begin
FStartPage := Value;
UpdateSettings;
end;
end;
procedure TKPrintPageSetup.SetUnits(Value: TKPrintUnits);
begin
if FActive then Exit;
if Value <> FUnits then
begin
BeforeUnitsChange;
FUnits := Value;
AfterUnitsChange;
end;
end;
procedure TKPrintPageSetup.UnlockUpdate;
begin
if FUpdateLock > 0 then
begin
Dec(FUpdateLock);
UpdateSettings;
end;
end;
procedure TKPrintPageSetup.UpdateSettings;
var
I, PixelsPerInchX, PixelsPerInchY: Integer;
D: Double;
DC: HDC;
Info: TKPrintMeasureInfo;
begin
if UpdateUnlocked and not FActive and not FValidating then
begin
FValidating := True;
try
Printer.Refresh;
I := Printer.Printers.IndexOf(FPrinterName);
if I >= 0 then
Printer.PrinterIndex := I;
// limit copies and Scale
FCopies := MinMax(FCopies, cCopiesMin, cCopiesMax);
FScale := MinMax(FScale, cScaleMin, cScaleMax);
// get metrics for the desktop
DC := GetDC(0);
try
FDesktopPixelsPerInchX := GetDeviceCaps(DC, LOGPIXELSX);
FDesktopPixelsPerInchY := GetDeviceCaps(DC, LOGPIXELSY);
finally
ReleaseDC(0, DC);
end;
// get metrics for the printer
if Printer.Printers.Count > 0 then
begin
FPageWidth := Printer.PageWidth;
FPageHeight := Printer.PageHeight;
{$IFDEF FPC}
FPrinterPixelsPerInchX := Printer.XDPI;
FPrinterPixelsPerInchY := Printer.YDPI;
{$ELSE}
FPrinterPixelsPerInchX := GetDeviceCaps(Printer.Handle, LOGPIXELSX);
FPrinterPixelsPerInchY := GetDeviceCaps(Printer.Handle, LOGPIXELSY);
{$ENDIF}
end else
begin
// fake printer metrics if no printer is installed
FPageWidth := 2360;
FPageHeight := 3400;
FPrinterPixelsPerInchX := 300;
FPrinterPixelsPerInchY := 300;
end;
// decide how to outline extent
if FPrintingMapped then
begin
PixelsPerInchX := FDesktopPixelsPerInchX;
PixelsPerInchY := FDesktopPixelsPerInchY;
end else
begin
PixelsPerInchX := FPrinterPixelsPerInchX;
PixelsPerInchY := FPrinterPixelsPerInchY;
end;
// limit and convert margins
D := FPageWidth * 0.4; // 40% of the page
FPrinterMarginLeft := Round(MinMax(ValueToInches(FUnits, FMarginLeft) * FPrinterPixelsPerInchX, 0, D));
FPrinterMarginLeftMirrored := FPrinterMarginLeft;
FMarginLeft := InchesToValue(FUnits, FPrinterMarginLeft / FPrinterPixelsPerInchX);
FPrinterMarginRight := Round(MinMax(ValueToInches(FUnits, FMarginRight) * FPrinterPixelsPerInchX, 0, D));
FPrinterMarginRightMirrored := FPrinterMarginRight;
FMarginRight := InchesToValue(FUnits, FPrinterMarginRight / FPrinterPixelsPerInchX);
D := FPageHeight * 0.4; // 40% of the page
FPrinterMarginTop := Round(MinMax(ValueToInches(FUnits, FMarginTop) * FPrinterPixelsPerInchY, 0, D));
FMarginTop := InchesToValue(FUnits, FPrinterMarginTop / FPrinterPixelsPerInchY);
FPrinterMarginBottom := Round(MinMax(ValueToInches(FUnits, FMarginBottom) * FPrinterPixelsPerInchY, 0, D));
FMarginBottom := InchesToValue(FUnits, FPrinterMarginBottom / FPrinterPixelsPerInchY);
// limit and convert header and footer space
FPrinterHeaderSpace := Round(MinMax(ValueToInches(FUnits, Max(FHeaderSpace, 0)) * FPrinterPixelsPerInchY, 0, D - FPrinterMarginTop));
FHeaderSpace := InchesToValue(FUnits, FPrinterHeaderSpace / FPrinterPixelsPerInchY);
FPrinterFooterSpace := Round(MinMax(ValueToInches(FUnits, Max(FFooterSpace, 0)) * FPrinterPixelsPerInchY, 0, D - FPrinterMarginBottom));
FFooterSpace := InchesToValue(FUnits, FPrinterFooterSpace / FPrinterPixelsPerInchY);
// paint area extent
FPaintAreaHeight := MulDiv(FPageHeight - FPrinterMarginTop - FPrinterMarginBottom - FPrinterHeaderSpace - FPrinterFooterSpace, PixelsPerInchY, FPrinterPixelsPerInchY);
FPaintAreaWidth := MulDiv(FPageWidth - FPrinterMarginLeft - FPrinterMarginRight, PixelsPerInchX, FPrinterPixelsPerInchX);
// default horizontal scaling
FCurrentScale := FScale / 100;
// default page/copy info
FCurrentCopy := 0;
FCurrentPage := 0;
// measured data
if Assigned(FControl) then
begin
FillChar(Info, SizeOf(TKPrintMeasureInfo), 0);
FControl.MeasurePages(Info);
FOutlineWidth := Info.OutlineWidth;
FOutlineHeight := Info.OutlineHeight;
FHorzPageCount := Info.HorzPageCount;
FVertPageCount := Info.VertPageCount;
FPageCount := Info.PageCount;
if FPageCount > 0 then
begin
// update horizontal scaling
if (poFitToPage in FOptions) and (FOutlineWidth > 0) then
FCurrentScale := FPaintAreaWidth / FOutlineWidth;
// limit start and end page
case FRange of
prAll, prSelectedOnly:
begin
FStartPage := 1;
FEndPage := FPageCount;
end;
prRange:
begin
FEndPage := MinMax(FEndPage, 1, FPageCount);
FStartPage := MinMax(FStartPage, 1, FEndPage);
end;
end;
end;
// notify all previews/ force their repainting
FControl.NotifyPreviews;
end else
begin
FOutlineWidth := 0;
FOutlineHeight := 0;
FHorzPageCount := 0;
FVertPageCount := 0;
FPageCount := 0;
FEndPage := 0;
FStartPage := 0;
end;
FIsValid := True;
finally
FValidating := False;
end;
end;
end;
function TKPrintPageSetup.UpdateUnlocked: Boolean;
begin
Result := FUpdateLock = 0;
end;
procedure TKPrintPageSetup.Validate;
begin
if not FIsValid and not FValidating then
UpdateSettings;
end;
function TKPrintPageSetup.VMap(Value: Integer): Integer;
begin
Result := MulDiv(Value, FPrinterPixelsPerInchY, FDesktopPixelsPerInchY);
end;
{ TKPreviewColors }
constructor TKPreviewColors.Create(APreview: TKPrintPreview);
begin
inherited Create;
FPreview := APreview;
Initialize;
end;
procedure TKPreviewColors.Assign(Source: TPersistent);
begin
inherited;
if Source is TKPreviewColors then
begin
Colors := TKPreviewColors(Source).Colors;
FPreview.Invalidate;
end
end;
function TKPreviewColors.GetColor(Index: TKPreviewColorIndex): TColor;
begin
Result := InternalGetColor(Index);
end;
function TKPreviewColors.GetColorEx(Index: TKPreviewColorIndex): TColor;
begin
Result := FColors[Index];
end;
procedure TKPreviewColors.Initialize;
begin
SetLength(FColors, ciPreviewColorsMax + 1);
FColors[ciPaper] := cPaperDef;
FColors[ciBkGnd] := cBkGndDef;
FColors[ciBorder] := cBorderDef;
FColors[ciSelectedBorder] := cSelectedBorderDef;
end;
function TKPreviewColors.InternalGetColor(Index: TKPreviewColorIndex): TColor;
begin
Result := FColors[Index];
end;
procedure TKPreviewColors.InternalSetColor(Index: TKPreviewColorIndex; Value: TColor);
begin
if FColors[Index] <> Value then
begin
FColors[Index] := Value;
if not (csLoading in FPreview.ComponentState) then
FPreview.Invalidate;
end;
end;
procedure TKPreviewColors.SetColor(Index: TKPreviewColorIndex; Value: TColor);
begin
InternalSetColor(Index, Value);
end;
procedure TKPreviewColors.SetColorEx(Index: TKPreviewColorIndex; Value: TColor);
begin
FColors[Index] := Value;
end;
procedure TKPreviewColors.SetColors(const Value: TKColorArray);
var
I: Integer;
begin
for I := 0 to Min(Length(FColors), Length(Value)) - 1 do
FColors[I] := Value[I];
end;
{ TKPrintPreview }
constructor TKPrintPreview.Create(AOwner: TComponent);
begin
inherited;
FColors := TKPreviewColors.Create(Self);
FControl := nil;
FMouseWheelAccumulator := 0;
FPage := 1;
FPageSize := Point(0, 0);
FScale := 100;
FScaleMode := smPageWidth;
FOnChanged := nil;
LoadCustomCursor(crDragHandFree, 'KPREVIEW_CURSOR_HAND_FREE');
LoadCustomCursor(crDragHandGrip, 'KPREVIEW_CURSOR_HAND_GRIP');
Width := 300;
Height := 200;
end;
destructor TKPrintPreview.Destroy;
begin
if Assigned(FControl) then
FControl.RemovePreview(Self);
inherited;
FColors.Free;
end;
procedure TKPrintPreview.BeginScrollWindow;
begin
FPageOld := FPage;
FScrollPosOld := FScrollPos;
end;
procedure TKPrintPreview.CreateParams(var Params: TCreateParams);
begin
inherited;
with Params do
Style := Style or WS_HSCROLL or WS_VSCROLL;
end;
function TKPrintPreview.DoMouseWheel(Shift: TShiftState; WheelDelta: Integer;
MousePos: TPoint): Boolean;
const
cWheelDivisor = 120;
var
Delta, WheelClicks: Integer;
begin
Result := inherited DoMouseWheel(Shift, WheelDelta, MousePos);
if not Result then
begin
if ssCtrl in Shift then
begin
if FScaleMode = smWholePage then Delta := 10 else Delta := ClientHeight;
end else
if FScaleMode = smWholePage then Delta := 1 else Delta := ClientHeight div 10;
Inc(FMouseWheelAccumulator, WheelDelta);
WheelClicks := FMouseWheelAccumulator div cWheelDivisor;
FMouseWheelAccumulator := FMouseWheelAccumulator mod cWheelDivisor;
BeginScrollWindow;
ModifyScrollBar(SB_VERT, -1, -WheelClicks * Delta);
EndScrollWindow;
Result := True;
end;
end;
procedure TKPrintPreview.EndScrollWindow;
begin
if (FPage <> FPageOld) then
Invalidate
else if (FScrollPos.X <> FScrollPosOld.X) or (FScrollPos.Y <> FScrollPosOld.Y) then
begin
ScrollWindowEx(Handle, FScrollPosOld.X - FScrollPos.X, FScrollPosOld.Y - FScrollPos.Y,
nil, nil, 0, nil, SW_INVALIDATE);
end;
end;
procedure TKPrintPreview.FirstPage;
begin
Page := StartPage;
end;
function TKPrintPreview.GetCurrentScale: Integer;
begin
if Assigned(FControl) then
Result := MulDiv(FPageSize.X, 100, MulDiv(FControl.PageSetup.PageWidth, 300, FControl.PageSetup.PrinterPixelsPerInchX))
else
Result := FScale;
end;
function TKPrintPreview.GetEndPage: Integer;
begin
if Assigned(FControl) then
begin
Result := FControl.PageSetup.EndPage;
if Result = 0 then
begin
FControl.PageSetup.UpdateSettings;
Result := FControl.PageSetup.EndPage
end;
end else
Result := 0;
end;
function TKPrintPreview.GetPageRect: TRect;
begin
with Result do
begin
Left := FPageOffset.X - FScrollPos.X;
if FScaleMode = smWholePage then
Top := FPageOffset.Y
else
Top := FPageOffset.Y - FScrollPos.Y;
Right := Left + FPageSize.X;
Bottom := Top + FPageSize.Y;
end;
end;
function TKPrintPreview.GetStartPage: Integer;
begin
if Assigned(FControl) then
begin
Result := FControl.PageSetup.StartPage;
if Result = 0 then
begin
FControl.PageSetup.UpdateSettings;
Result := FControl.PageSetup.StartPage
end;
end else
Result := 0;
end;
procedure TKPrintPreview.KeyDown(var Key: Word; Shift: TShiftState);
var
DeltaX, DeltaY, LineX, PageY: Integer;
NoAlt, NoAltCtrl: Boolean;
begin
NoAlt := Shift * [ssAlt] = [];
NoAltCtrl := Shift * [ssAlt, ssCtrl] = [];
DeltaX := 0;
DeltaY := 0;
LineX := ClientWidth div 10;
PageY := ClientHeight;
case Key of
VK_UP:
if NoAltCtrl then
begin
if FScaleMode = smWholePage then
PreviousPage
else
DeltaY := -PageY div 10;
end;
VK_DOWN:
if NoAltCtrl then
begin
if FScaleMode = smWholePage then
NextPage
else
DeltaY := PageY div 10;
end;
VK_PRIOR:
if NoAltCtrl then
begin
if FScaleMode = smWholePage then
PreviousPage
else
DeltaY := -PageY;
end;
VK_NEXT:
if NoAltCtrl then
begin
if FScaleMode = smWholePage then
NextPage
else
DeltaY := PageY;
end;
VK_LEFT: if NoAltCtrl then DeltaX := -LineX;
VK_RIGHT: if NoAltCtrl then DeltaX := LineX;
VK_HOME:
if NoAlt then
begin
if ssCtrl in Shift then
FirstPage
else
DeltaX := -FScrollPos.X;
end;
VK_END:
if NoAlt then
begin
if ssCtrl in Shift then
LastPage
else
DeltaX := FScrollExtent.X - FScrollPos.X;
end;
end;
if (DeltaX <> 0) or (DeltaY <> 0) then
begin
BeginScrollWindow;
if DeltaX <> 0 then
ModifyScrollBar(SB_HORZ, -1, DeltaX);
if DeltaY <> 0 then
ModifyScrollBar(SB_VERT, -1, DeltaY);
EndScrollWindow;
end;
end;
procedure TKPrintPreview.LastPage;
begin
Page := EndPage;
end;
procedure TKPrintPreview.ModifyScrollBar(ScrollBar, ScrollCode, Delta: Integer);
var
I, AEndPage: Integer;
Divisor: Cardinal;
PPos, PExtent: PInteger;
SI: TScrollInfo;
begin
Divisor := 10;
if ScrollBar = SB_HORZ then
begin
PPos := @FScrollPos.X;
PExtent := @FScrollExtent.X;
end else
begin
if FScaleMode = smWholePage then
begin
PPos := @FPage;
AEndPage := EndPage;
PExtent := @AEndPage;
Divisor := 1;
end else
begin
PPos := @FScrollPos.Y;
PExtent := @FScrollExtent.Y;
end;
end;
if PExtent^ > 0 then
begin
SI.cbSize := SizeOf(TScrollInfo);
SI.fMask := SIF_RANGE or SIF_PAGE or SIF_TRACKPOS;
GetScrollInfo(Handle, ScrollBar, SI);
{$IF DEFINED(LCLGTK2)}
{.$WARNING "scrollbar arrows still not working properly on GTK2 in some cases!"}
SI.nTrackPos := Delta;
{$IFEND}
I := PPos^;
case ScrollCode of
SB_TOP: I := SI.nMin;
SB_BOTTOM: I := SI.nMax; // will be trimmed below
SB_LINEUP: Dec(I, SI.nPage div Divisor);
SB_LINEDOWN: Inc(I, SI.nPage div Divisor);
SB_PAGEUP: Dec(I, SI.nPage);
SB_PAGEDOWN: Inc(I, SI.nPage);
SB_THUMBTRACK, SB_THUMBPOSITION: I := SI.nTrackPos;
else
Inc(I, Delta)
end;
if FScaleMode = smWholePage then
I := MinMax(I, 1, PExtent^)
else
I := MinMax(I, 0, PExtent^);
PPos^ := I;
SI.nPos := I;
SI.fMask := SIF_POS;
SetScrollInfo(Handle, ScrollBar, SI, True);
end;
end;
procedure TKPrintPreview.MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
inherited;
if ssLeft in Shift then
begin
SafeSetFocus;
if (FScaleMode <> smWholePage) and PtInRect(GetPageRect, Point(X, Y)) then
begin
FlagSet(cPF_Dragging);
FX := X;
FY := Y;
SetMouseCursor(X, Y);
end;
end;
end;
procedure TKPrintPreview.MouseMove(Shift: TShiftState; X, Y: Integer);
begin
inherited;
if Flag(cPF_Dragging) and MouseCapture then
begin
BeginScrollWindow;
if (X > FX) and (FScrollPos.X > 0) or (X < FX) and (FScrollPos.X < FScrollExtent.X) then
begin
ModifyScrollBar(SB_HORZ, -1, FX - X);
FX := X;
end;
if (Y > FY) and (FScrollPos.Y > 0) or (Y < FY) and (FScrollPos.Y < FScrollExtent.Y) then
begin
ModifyScrollBar(SB_VERT, -1, FY - Y);
FY := Y;
end;
EndScrollWindow;
end;
end;
procedure TKPrintPreview.MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
inherited;
FlagClear(cPF_Dragging);
SetMouseCursor(X, Y);
end;
procedure TKPrintPreview.NextPage;
begin
Page := Page + 1;
end;
procedure TKPrintPreview.Notification(AComponent: TComponent;
Operation: TOperation);
begin
inherited;
if (Operation = opRemove) and (AComponent = FControl) then
begin
FControl := nil;
UpdatePreview;
end;
end;
procedure TKPrintPreview.Paint;
procedure DoPaint(IsBuffer: Boolean);
var
C: TColor;
R, RPaper, RPage: TRect;
RgnPaper: HRGN;
begin
Canvas.Brush.Style := bsSolid;
Canvas.Pen.Mode := pmCopy;
Canvas.Pen.Style := psSolid;
Canvas.Pen.Width := 1;
RPage := GetPageRect;
RPaper := RPage;
with RPaper do
begin
Inc(Right, cPreviewShadowSize);
Inc(Bottom, cPreviewShadowSize);
end;
if not IsBuffer then
RgnPaper := CreateRectRgnIndirect(RPaper)
else
RgnPaper := 0;
try
// paint background around paper, we don't want at least this to flicker
if IsBuffer or (ExtSelectClipRgn(Canvas.Handle, RgnPaper, RGN_DIFF) <> NULLREGION) then
begin
Canvas.Brush.Color := FColors.BkGnd;
Canvas.FillRect(ClientRect);
end;
if not IsBuffer then
SelectClipRgn(Canvas.Handle, RgnPaper);
finally
if not IsBuffer then
DeleteObject(rgnPaper);
end;
// paint paper outline
if Focused then
C := FColors.SelectedBorder
else
C := FColors.Border;
Canvas.Pen.Color := C;
Canvas.Brush.Color := FColors.Paper;
Canvas.Rectangle(RPage);
Canvas.Brush.Color := FColors.BkGnd;
R := Rect(RPage.Left, RPage.Bottom, RPage.Left + cPreviewShadowSize, RPage.Bottom + cPreviewShadowSize);
Canvas.FillRect(R);
R := Rect(RPage.Right, RPage.Top, RPage.Right + cPreviewShadowSize, RPage.Top + cPreviewShadowSize);
Canvas.FillRect(R);
Canvas.Brush.Color := C;
R := Rect(RPage.Left + cPreviewShadowSize, RPage.Bottom, RPaper.Right, RPaper.Bottom);
Canvas.FillRect(R);
R := Rect(RPage.Right, RPage.Top + cPreviewShadowSize, RPaper.Right, RPaper.Bottom);
Canvas.FillRect(R);
// paint page outline
InflateRect(RPage, -1, -1);
FControl.PageSetup.PaintPageToPreview(Self);
end;
var
SaveIndex: Integer;
RClient: TRect;
{$IFDEF USE_WINAPI}
Org: TPoint;
MemBitmap, OldBitmap: HBITMAP;
DC: HDC;
{$ENDIF}
begin
RClient := ClientRect;
if Assigned(FControl) then
begin
SaveIndex := SaveDC(Canvas.Handle);
try
{$IFDEF USE_WINAPI}
if DoubleBuffered then
begin
// we must paint always the entire client because of canvas scaling
MemBitmap := CreateCompatibleBitmap(Canvas.Handle, RClient.Right - RClient.Left, RClient.Bottom - RClient.Top);
try
OldBitmap := SelectObject(Canvas.Handle, MemBitmap);
try
SetWindowOrgEx(Canvas.Handle, 0, 0, @Org);
SelectClipRect(Canvas.Handle, Rect(0, 0, RClient.Right - RClient.Left, RClient.Bottom - RClient.Top));
DoPaint(True);
finally
SelectObject(Canvas.Handle, OldBitmap);
SetWindowOrgEx(Canvas.Handle, Org.X, Org.Y, nil);
end;
// copy MemBitmap to original canvas
DC := CreateCompatibleDC(Canvas.Handle);
try
OldBitmap := SelectObject(DC, MemBitmap);
try
CopyBitmap(Canvas.Handle, RClient, DC, 0, 0);
finally
SelectObject(DC, OldBitmap);
end;
finally
DeleteDC(DC);
end;
finally
DeleteObject(MemBitmap);
end;
end else
{$ENDIF}
DoPaint(False);
finally
RestoreDC(Canvas.Handle, SaveIndex);
end;
end else
begin
Canvas.Brush.Color := FColors.BkGnd;
Canvas.FillRect(RClient);
end;
end;
procedure TKPrintPreview.Changed;
begin
if Assigned(FOnChanged) then
FOnChanged(Self);
end;
procedure TKPrintPreview.PreviousPage;
begin
Page := Page - 1;
end;
procedure TKPrintPreview.SafeSetFocus;
var
Form: TCustomForm;
begin
Form := GetParentForm(Self);
if (Form <> nil) and Form.Visible and Form.Enabled and Visible and Enabled then
Form.ActiveControl := Self;
end;
procedure TKPrintPreview.SetColors(const Value: TKPreviewColors);
begin
FColors.Assign(Value);
end;
procedure TKPrintPreview.SetControl(Value: TKCustomControl);
begin
if (Value <> FControl) and (Value <> Self) and not (Value is TKPrintPreview) then
begin
if Assigned(FControl) then
FControl.RemovePreview(Self);
FControl := Value;
if Assigned(FControl) then
FControl.AddPreview(Self);
UpdatePreview;
end;
end;
procedure TKPrintPreview.SetPage(Value: Integer);
begin
Value := MinMax(Value, StartPage, EndPage);
if Value <> FPage then
begin
BeginScrollWindow;
if FScaleMode = smWholePage then
ModifyScrollBar(SB_VERT, -1, Value - FPage)
else
FPage := Value;
EndScrollWindow;
Changed;
end;
end;
procedure TKPrintPreview.SetScale(Value: Integer);
begin
Value := MinMax(Value, cScaleMin, cScaleMax);
if Value <> FScale then
begin
FScale := Value;
UpdatePreview;
end;
end;
procedure TKPrintPreview.SetScaleMode(Value: TKPreviewScaleMode);
begin
if Value <> FScaleMode then
begin
FScaleMode := Value;
UpdatePreview;
end;
end;
function TKPrintPreview.SetMouseCursor(X, Y: Integer): Boolean;
var
ACursor: TCursor;
begin
if PtInRect(GetPageRect, Point(X, Y)) and (FScaleMode <> smWholePage) then
begin
if MouseCapture then
ACursor := crDragHandGrip
else
ACursor := crDragHandFree;
end else
ACursor := crDefault;
{$IFDEF FPC}
FCursor := ACursor;
SetTempCursor(ACursor);
{$ELSE}
Windows.SetCursor(Screen.Cursors[ACursor]);
{$ENDIF}
Result := True;
end;
procedure TKPrintPreview.UpdatePreview;
begin
Page := FPage;
UpdateScrollRange;
Changed;
end;
procedure TKPrintPreview.UpdateScrollRange;
var
I: Integer;
PageWidth100Percent, PageHeight100Percent: Integer;
SI: TScrollInfo;
begin
if HandleAllocated and not Flag(cPF_UpdateRange) then
begin
FlagSet(cPF_UpdateRange);
try
if Assigned(FControl) then
begin
// get isotropic page size in 300 dpi
PageWidth100Percent := MulDiv(FControl.PageSetup.PageWidth, 300, FControl.PageSetup.PrinterPixelsPerInchX);
PageHeight100Percent := MulDiv(FControl.PageSetup.PageHeight, 300, FControl.PageSetup.PrinterPixelsPerInchY);
case FScaleMode of
smScale:
begin
FPageSize.X := MulDiv(PageWidth100Percent, FScale, 100);
FPageSize.Y := MulDiv(PageHeight100Percent, FScale, 100);
end;
smPageWidth:
begin
FPageSize.X := Max(ClientWidth - 2 * cPreviewHorzBorder - cPreviewShadowSize, 40);
FPageSize.Y := MulDiv(FPageSize.X, PageHeight100Percent, PageWidth100Percent);
end;
smWholePage:
begin
FPageSize.X := Max(ClientWidth - 2 * cPreviewHorzBorder - cPreviewShadowSize, 40);
FPageSize.Y := Max(ClientHeight - 2 * cPreviewVertBorder - cPreviewShadowSize, 40);
I := MulDiv(FPageSize.Y, PageWidth100Percent, PageHeight100Percent);
if I < FPageSize.X then
FPageSize.X := I
else
FPageSize.Y := MulDiv(FPageSize.X, PageHeight100Percent, PageWidth100Percent);
end;
end;
FExtent.X := FPageSize.X + 2 * cPreviewHorzBorder + cPreviewShadowSize;
FExtent.Y := FPageSize.Y + 2 * cPreviewVertBorder + cPreviewShadowSize;
FPageOffset.X := cPreviewHorzBorder;
if (FExtent.X < ClientWidth) then
Inc(FPageOffset.X, (ClientWidth - FExtent.X) div 2);
FPageOffset.Y := cPreviewVertBorder;
if (FExtent.Y < ClientHeight) then
Inc(FPageOffset.Y, (ClientHeight - FExtent.Y) div 2);
// adjust horizontal scroll position
I := FScrollPos.X + ClientWidth - FExtent.X - 1;
if I > 0 then
Dec(FScrollPos.X, I);
FScrollPos.X := Max(FScrollPos.X, 0);
// adjust vertical scroll position
I := FScrollPos.Y + ClientHeight - FExtent.Y - 1;
if I > 0 then
Dec(FScrollPos.Y, I);
FScrollPos.Y := Max(FScrollPos.Y, 0);
// update scroll range
FScrollExtent.X := 0;
FScrollExtent.Y := 0;
FillChar(SI, SizeOf(TScrollInfo), 0);
SI.cbSize := SizeOf(TScrollInfo);
SI.fMask := SIF_RANGE or SIF_PAGE or SIF_POS or SIF_DISABLENOSCROLL {$IFDEF UNIX}or SIF_UPDATEPOLICY{$ENDIF};
SI.nMin := 0;
{$IFDEF UNIX}
SI.ntrackPos := SB_POLICY_CONTINUOUS;
{$ENDIF}
case FScaleMode of
smScale:
begin
ShowScrollbar(Handle, SB_HORZ, True);
ShowScrollbar(Handle, SB_VERT, True);
SI.nMax := FExtent.X{$IFDEF FPC}+ 1{$ENDIF};
SI.nPage := ClientWidth;
SI.nPos := FScrollPos.X;
FScrollExtent.X := SI.nMax - Integer(SI.nPage);
SetScrollInfo(Handle, SB_HORZ, SI, True);
SI.nMax := FExtent.Y{$IFDEF FPC}+ 1{$ENDIF};
SI.nPage := ClientHeight;
SI.nPos := FScrollPos.Y;
FScrollExtent.Y := SI.nMax - Integer(SI.nPage);
SetScrollInfo(Handle, SB_VERT, SI, True);
end;
smPageWidth:
begin
ShowScrollbar(Handle, SB_HORZ, False);
ShowScrollbar(Handle, SB_VERT, True);
SI.nMax := FExtent.Y{$IFDEF FPC}+ 1{$ENDIF};
SI.nPage := ClientHeight;
SI.nPos := FScrollPos.Y;
FScrollExtent.Y := SI.nMax - Integer(SI.nPage);
SetScrollInfo(Handle, SB_VERT, SI, True);
end;
smWholePage:
begin
// another mode for vertical scrollbar - page selection
ShowScrollbar(Handle, SB_HORZ, False);
ShowScrollbar(Handle, SB_VERT, True);
SI.nMin := StartPage;
SI.nMax := EndPage{$IFDEF FPC}+ 1{$ENDIF};
SI.nPage := 1;
SI.nPos := FPage;
SetScrollInfo(Handle, SB_VERT, SI, True);
end;
end;
end else
begin
ShowScrollbar(Handle, SB_HORZ, False);
ShowScrollbar(Handle, SB_VERT, False);
end;
Invalidate;
finally
FlagClear(cPF_UpdateRange);
end;
end;
end;
procedure TKPrintPreview.UpdateSize;
begin
inherited;
UpdatePreview;
end;
procedure TKPrintPreview.WMEraseBkgnd(var Msg: TLMessage);
begin
Msg.Result := 1;
end;
procedure TKPrintPreview.WMGetDlgCode(var Msg: TLMNoParams);
begin
Msg.Result := DLGC_WANTARROWS;
end;
procedure TKPrintPreview.WMHScroll(var Msg: TLMHScroll);
begin
SafeSetFocus;
BeginScrollWindow;
ModifyScrollBar(SB_HORZ, Msg.ScrollCode, Msg.Pos);
EndScrollWindow;
end;
procedure TKPrintPreview.WMKillFocus(var Msg: TLMKillFocus);
begin
inherited;
Invalidate;
end;
procedure TKPrintPreview.WMSetFocus(var Msg: TLMSetFocus);
begin
inherited;
Invalidate;
end;
procedure TKPrintPreview.WMVScroll(var Msg: TLMVScroll);
begin
SafeSetFocus;
BeginScrollWindow;
ModifyScrollBar(SB_VERT, Msg.ScrollCode, Msg.Pos);
EndScrollWindow;
end;
{$IFDEF FPC}
initialization
{$i kcontrols.lrs}
{$ELSE}
{$R kcontrols.res}
{$ENDIF}
end.