Files
HeidiSQL/components/mORMot/SQLite3/SQLite3UI.pas

1933 lines
70 KiB
ObjectPascal
Raw Blame History

/// Grid to display Database content
// - this unit is a part of the freeware Synopse SQLite3 database framework,
// licensed under a MPL/GPL/LGPL tri-license; version 1.17
unit SQLite3UI;
(*
This file is part of Synopse mORMot framework.
Synopse mORMot framework. Copyright (C) 2012 Arnaud Bouchez
Synopse Informatique - http://synopse.info
*** BEGIN LICENSE BLOCK *****
Version: MPL 1.1/GPL 2.0/LGPL 2.1
The contents of this file are subject to the Mozilla Public License Version
1.1 (the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.mozilla.org/MPL
Software distributed under the License is distributed on an "AS IS" basis,
WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
for the specific language governing rights and limitations under the License.
The Original Code is Synopse SQLite3 database framework.
The Initial Developer of the Original Code is Arnaud Bouchez.
Portions created by the Initial Developer are Copyright (C) 2012
the Initial Developer. All Rights Reserved.
Contributor(s):
Alternatively, the contents of this file may be used under the terms of
either the GNU General Public License Version 2 or later (the "GPL"), or
the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
in which case the provisions of the GPL or the LGPL are applicable instead
of those above. If you wish to allow use of your version of this file only
under the terms of either the GPL or the LGPL, and not to allow others to
use your version of this file under the terms of the MPL, indicate your
decision by deleting the provisions above and replace them with the notice
and other provisions required by the GPL or the LGPL. If you do not delete
the provisions above, a recipient may use your version of this file under
the terms of any one of the MPL, the GPL or the LGPL.
***** END LICENSE BLOCK *****
Fill a TDrawGrid with data from our database engine
*****************************************************
- associate TSQLTable content to a TDrawGrid
- display UTF-8 values with true unicode characters
- column size is calculated from data size
- field sort by left/right arrow keys or clicking on first row
- incremental key lookup for direct search inside displayed values
- Ctrl + click on a cell to display its full unicode content
- ID column can be hidden on demand, but IDs still remaining in memory
Initial version: 2008 March, by Arnaud Bouchez
Version 1.4 - February 8, 2010
- whole Synopse SQLite3 database framework released under the GNU Lesser
General Public License version 3, instead of generic "Public Domain"
Version 1.5 - February 19, 2010
- User Interface Query action implementation
Version 1.8
- added TSynIntegerLabeledEdit component (e.g. used in SQLite3UIOptions)
Version 1.9
- improved Delphi 2009/2010 UnicodeString compatibility
- fix some issues, and complete implementation of marking from time elapsed
Version 1.9.2
- new ForceRefresh parameter to the TSQLTableToGrid.Refresh method
Version 1.13
- now compiles with Delphi 6
- new TSQLTableToGrid.SelectedRecordCreate method
- added generic TSynLabeledEdit to handle Integer, Int64, Currency and
double kind of values
Version 1.14
- fixed issue with MaxValue if RangeChecking not enabled
Version 1.15
- compatibility with Delphi XE2
- new TSQLTableToGrid.SetFieldFixedWidth method
- new TSQLTableToGrid.FieldTitleTruncatedNotShownAsHint property
- fixed issue on TDrawGrid events when TSQLTableToGrid is destroyed
Version 1.16
- new FillStringGrid() function, ready to fill a regular TStringGrid
- includes new TSynAnsiConvert classes for handling Ansi charsets
Version 1.17
- global functions AddApplicationToFirewall() and AddPortToFirewall()
are now compatible with Windows XP, Vista and Seven - renamed on purpose
*)
interface
{$I Synopse.inc}
uses
Windows,
SynCommons, SQLite3Commons, SQLite3i18n,
SysUtils, Classes, Messages, Variants,
{$ifdef WITHUXTHEME}
Themes,
{$endif}
Graphics, StdCtrls, Controls, Grids, Buttons, ExtCtrls, Forms;
type
/// a THintWindow descendant, with an internal delay to auto-hide
// - this component can be used directly with the hint text to be displayed
// (companion to the controls Hint properties and Application.ShowHint)
// - you can specify a time interval for the popup window to be hidden
// - this component expects UTF-8 encoded text, and displays it as Unicode
THintWindowDelayed = class(THintWindow)
private
fRow: integer;
fCol: integer;
protected
fTimerEnabled: boolean;
fFontColor: TColor;
fUTF8Text: RawUTF8;
/// called after a Hide call
procedure VisibleChanging; override;
/// used to hide the popup hint after a delay
procedure WMTimer(var Msg: TWMTimer); message WM_TIMER;
/// overriden method, Unicode ready
procedure Paint; override;
public
/// initializes the component
constructor Create(aOwner: TComponent); override;
/// releases component resources and memory
destructor Destroy; override;
/// displays the appropriate Hint Text at a specified screen position
// - Text is decoded from UTF-8 to Unicode before display
// - Time is the maximum text display delay, in milliseconds
procedure ShowDelayedUTF8(const Text: RawUTF8; X,Y,Time: integer;
FontColor: TColor; AlignLeft: boolean=false); overload;
/// displays the appropriate Hint Text at a position relative to a control
// - Text is decoded from UTF-8 to Unicode before display
// - Time is the maximum text display delay, in milliseconds
procedure ShowDelayedUTF8(const Text: RawUTF8; Origin: TControl;
X,Y,Time: integer; FontColor: TColor; AlignLeft: boolean=false); overload;
/// displays the appropriate Hint Text at a specified screen position
// - if string is AnsiString (i.e. for Delphi 2 to 2007), Text is decoded into
// Unicode (using the current i18n code page) before display
// - Time is the maximum text display delay, in milliseconds
procedure ShowDelayedString(const Text: string; X,Y,Time: integer;
FontColor: TColor; AlignLeft: boolean=false); overload;
{$ifdef HASINLINE}inline;{$endif}
/// displays the appropriate Hint Text at a position relative to a control
// - Text is decoded from Ansi to Unicode (using the current i18n code page) before display
// - Time is the maximum text display delay, in milliseconds
procedure ShowDelayedString(const Text: string; Origin: TControl;
X,Y,Time: integer; FontColor: TColor; AlignLeft: boolean=false); overload;
{$ifdef HASINLINE}inline;{$endif}
/// overriden method, Unicode ready
function CalcHintRect(MaxWidth: Integer; const AHint: RawUTF8; AData: Pointer): TRect; reintroduce;
/// the column number when the hint is displayed
property Col: integer read fCol;
/// the row number when the hint is displayed
property Row: integer read fRow;
end;
/// kind of event used to change some text on the fly for grid display
// - expect generic string Text, i.e. UnicodeString for Delphi 2009/2010,
// ready to be used with the VCL for all Delphi compiler versions
// - if the cell at FiieldIndex/RowIndex is to have a custom content,
// shall set the Text variable content and return TRUE
// - if returns FALSE, the default content will be displayed
TValueTextEvent = function(Sender: TSQLTable; FieldIndex, RowIndex: Integer; var Text: string): boolean of object;
/// kind of event used to change some text on the fly for popup hint
// - expect generic string Text, i.e. UnicodeString for Delphi 2009/2010,
// ready to be used with the VCL for all Delphi compiler versions
THintTextEvent = function(Sender: TSQLTable; FieldIndex, RowIndex: Integer; var Text: string): boolean of object;
/// kind of event used to display a menu on a cell right click
TRightClickCellEvent = procedure(Sender: TSQLTable; ACol, ARow, MouseX, MouseY: Integer) of object;
/// a hidden component, used for displaying a TSQLTable in a TDrawGrid
// - just call TSQLTableToGrid.Create(Grid,Table) to initiate the association
// - the Table will be released when no longer necessary
// - any former association by TSQLTableToGrid.Create() will be overriden
// - handle unicode, column size, field sort, incremental key lookup, hide ID
// - Ctrl + click on a cell to display its full unicode content
TSQLTableToGrid = class(TComponent)
private
fOnSelectCell: TSelectCellEvent;
fOnRightClickCell: TRightClickCellEvent;
fClient: TSQLRestClientURI;
fOnDrawCellBackground: TDrawCellEvent;
fMarked: array of byte;
fMarkAllowed: boolean;
fMouseDownMarkedValue: (markNone,markOn,markOff);
fTruncAsHint: Boolean;
fOnSelectCellProcessing: boolean;
fFieldIndexTimeLogForMark: integer;
function GetMarked(RowIndex: integer): boolean;
procedure SetMarked(RowIndex: integer; const Value: boolean);
function GetMarkAvailable: boolean;
function GetDrawGrid: TDrawGrid;
function GetMarkedIsOnlyCurrrent: boolean;
function GetMarkedTotalCount: integer;
// function because field information may be set manually after Create
function GetFieldIndexTimeLogForMark: integer;
protected
fOnValueText: TValueTextEvent;
fOnHintText: THintTextEvent;
/// associated TSQLTable result to be displayed
fTable: TSQLTable;
/// true if the specific field is in Ascending order
fFieldOrder: array of boolean;
/// current field number used for field
fCurrentFieldOrder: integer;
/// avoid resizing columns on height change only
fLastWidth: cardinal;
/// contain the key to be searched
fIncrementalSearch: RawUTF8;
/// true if row is changed by incremental key lookup
fIncrementalSearchMove: boolean;
/// used to display some hint text
fHint: THintWindowDelayed;
/// text of this field must be centered
fCentered: Int64;
/// text of this column/field name has been truncated
fFieldNameTruncated: Int64;
/// used by OnTableUpdate() event
fOnTableUpdateID: TIntegerDynArray;
/// return true if a GPF may occur
function NotDefined: boolean;
public
/// fill a TDrawGrid with the results contained in a TSQLTable
constructor Create(aOwner: TDrawGrid; aTable: TSQLTable; aClient: TSQLRestClientURI); reintroduce;
/// release the hidden object
// - will be called by the parent Grid when it is destroyed
// - will be called by any future TSQLTableToGrid.Create() association
// - free the associated TSQLTable and its memory content
// - will reset the Grid overriden events to avoid GPF
destructor Destroy; override;
/// called by the owner TDrawGrid to draw a Cell from the TSQLTable data
// - the cell is drawn using direct Win32 Unicode API
// - the first row (fixed) is drawn as field name (centered bold text with
// sorting order displayed with a triangular arrow)
procedure DrawCell(Sender: TObject; ACol, ARow: Longint; Rect: TRect; State: TGridDrawState);
/// called by the owner TDrawGrid when a Cell is selected
procedure DrawGridSelectCell(Sender: TObject; ACol, ARow: Integer; var CanSelect: Boolean);
/// called by the owner TDrawGrid when a Cell is clicked by the mouse
// - check if the first (fixed) row is clicked: then change sort order
// - Ctrl + click to display its full unicode content (see HintText to customize it)
procedure DrawGridMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
/// called by the owner TDrawGrid when the mouse is unclicked over a Cell
procedure DrawGridMouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
/// called by the owner TDrawGrid when the mouse is over a Cell
procedure DrawGridMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
/// called by the owner TDrawGrid when the user presses a key
// - used for incremental key lookup
procedure DrawGridKeyPress(Sender: TObject; var Key: Char);
/// called by the owner TDrawGrid when the user presses a key
// - used for LEFT/RIGHT ARROW column order change
procedure DrawGridKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
/// call this procedure to automaticaly resize the TDrawString columns
// - can be used as TSQLTableToGrid.From(DrawGrid).Resize();
procedure Resize(Sender: TObject);
/// display a popup Hint window at a specified Cell position
// - expect generic string Text, i.e. UnicodeString for Delphi 2009/2010,
// ready to be used with the VCL for all Delphi compiler versions
procedure ShowHintString(const Text: string; ACol, ARow, Time: integer;
FontColor: TColor=clBlack);
/// if the ID column is available, hides it from the grid
procedure IDColumnHide;
/// toggle the sort order of a specified column
procedure SortChange(ACol: integer);
/// set a specified column for sorting
// - if ACol=-1, then the Marked[] rows are shown first, in current sort
procedure SortForce(ACol: integer; Ascending: boolean; ARow: integer=-1);
/// get the ID of the first selected row, 0 on error (no ID field e.g.)
// - usefull even if ID column was hidden with IDColumnHide
function SelectedID: integer;
/// retrieve the record content of the first selected row, nil on error
// - record type is retrieved via Table.QueryTables[0] (if defined)
// - warning: it's up to the caller to Free the created instance after use
// (you should e.g. embedd the process in a try...finally block):
// ! Rec := Grid.SelectedRecordCreate;
// ! if Rec<>nil then
// ! try
// ! DoSomethingWith(Rec);
// ! finally
// ! Rec.Free;
// ! end;
// - usefull even if ID column was hidden with IDColumnHide
function SelectedRecordCreate: TSQLRecord;
/// set columns number which must be centered
procedure SetCentered(const Cols: array of cardinal); overload;
/// set a column number which must be centered
procedure SetCentered(aCol: cardinal); overload;
/// force the mean of characters length for every field
// - supply a string with every character value is proportionate to
// the corresponding column width
// - if the character is lowercase, the column is set as centered
// - if aMarkAllowed is set, a first checkbox column is added, for
// reflecting and updating the Marked[] field values e.g.
// - if Lengths='', will set some uniform width, left aligned
procedure SetFieldLengthMean(const Lengths: RawUTF8; aMarkAllowed: boolean);
/// force all columns to have a specified width, in pixels
procedure SetFieldFixedWidth(aColumnWidth: integer);
/// force refresh paint of Grid from Table data
// - return true if Table data has been successfully retrieved from Client
// and if data was refreshed because changed since last time
// - if ForceRefresh is TRUE, the Client is not used to retrieve the data,
// which must be already refreshed before this call
function Refresh(ForceRefresh: Boolean=false): boolean;
/// call this procedure after a refresh of the data
// - current Row will be set back to aID
// - called internal by Refresh function above
procedure AfterRefresh(aID: integer);
/// you can call this method when the list is no more on the screen
// - it will hide any pending popup Hint windows, for example
procedure PageChanged;
/// perform the corresponding Mark/Unmark[All] Action
procedure SetMark(aAction: TSQLAction);
/// retrieve the Marked[] bits array
function GetMarkedBits: pointer;
{{ read-only access to a particular row values, as VCL text
- Model is one TSQLModel instance (used to display TRecordReference)
- returns the text as generic string, ready to be displayed via the VCL
after translation, for sftEnumerate, sftTimeLog, sftRecord and all other
properties
- uses OnValueText property Event if defined by caller }
function ExpandRowAsString(Row: integer; Client: TObject): string;
/// retrieve the associated TSQLTableToGrid from a specific TDrawGrid
class function From(Grid: TDrawGrid): TSQLTableToGrid;
/// used by TSQLRestClientURI.UpdateFromServer() to let the client
// perform the rows update (for Marked[])
procedure OnTableUpdate(State: TOnTableUpdateState);
/// associated TDrawGrid
// - just typecast the Owner as TDrawGrid
property DrawGrid: TDrawGrid read GetDrawGrid;
/// associated TSQLTable to be displayed
property Table: TSQLTable read fTable;
/// associated Client used to retrieved the Table data
property Client: TSQLRestClientURI read fClient;
/// used to display some hint text
property Hint: THintWindowDelayed read fHint;
/// assign an event here to customize the background drawing of a cell
property OnDrawCellBackground: TDrawCellEvent read fOnDrawCellBackground
write fOnDrawCellBackground;
/// individual bits of this field is set to display a column data as centered
property Centered: Int64 read fCentered;
/// true if Marked[] is available (add checkboxes at the left side of every row)
property MarkAllowed: boolean read fMarkAllowed;
/// true if any Marked[] is checked
property MarkAvailable: boolean read GetMarkAvailable;
/// true if only one entry is in Marked[], and it is the current one
property MarkedIsOnlyCurrrent: boolean read GetMarkedIsOnlyCurrrent;
/// returns the number of item marked or selected
// - if no item is marked, it return 0 even if a row is currently selected
property MarkedTotalCount: integer read GetMarkedTotalCount;
/// retrieves if a row was previously marked
// - first data row index is 1
property Marked[RowIndex: integer]: boolean read GetMarked write SetMarked;
/// retrieves the index of the sftTimeLog first field
// - i.e. the field index which can be used for Marked actions
// - equals -1 if not such field exists
property FieldIndexTimeLogForMark: integer read GetFieldIndexTimeLogForMark;
/// current field number used for current table sorting
property CurrentFieldOrder: integer read fCurrentFieldOrder;
/// set to FALSE to display the column title as hint when truncated on screen
property FieldTitleTruncatedNotShownAsHint: boolean read fTruncAsHint write fTruncAsHint;
/// override this event to customize the text display in the table
property OnValueText: TValueTextEvent read fOnValueText write fOnValueText;
/// override this event to customize the Ctrl+Mouse click popup text
property OnHintText: THintTextEvent read fOnHintText write fOnHintText;
/// override this event to customize the Mouse click on a data cell
property OnSelectCell: TSelectCellEvent read fOnSelectCell write fOnSelectCell;
/// override this event to customize the Mouse right click on a data cell
property OnRightClickCell: TRightClickCellEvent read fOnRightClickCell write fOnRightClickCell;
end;
type
/// exception class raised by TSynIntegerLabeledEdit
ESynLabeledEdit = class(Exception);
/// diverse kind of values which may be edited by a TSynLabeledEdit
TSynLabeledEditKind = (sleInteger, sleInt64, sleCurrency, sleDouble);
/// TLabeledEdit with optional boundaries check of a Variant value
TSynLabeledEdit = class(TLabeledEdit)
protected
FMaxValue: Variant;
FMinValue: Variant;
FAdditionalHint: string;
FKind: TSynLabeledEditKind;
FRangeChecking: boolean;
function IsValid(const Txt: string; var ToValue: Variant): Boolean;
procedure SetValue(const Value: Variant);
function GetValue: Variant;
procedure KeyPress(var Key: char); override;
procedure MouseMove(Shift: TShiftState; X, Y: integer); override;
public
/// if true, GetValue() will raise an ESynVariantLabeledEdit exception on
// any Variant value range error, when the Value property is read
RaiseExceptionOnError: boolean;
/// convert the entered Variant value into a textual representation
function ToString(NumberOfDigits: integer): string; reintroduce;
/// return TRUE if the entered value is inside the boundaries
function ValidateValue: boolean;
/// create the component instance
constructor Create(AOwner: TComponent); override;
published
/// the kind of value which is currently edited by this TSynLabeledEdit
property Kind: TSynLabeledEditKind read fKind write fKind default sleInteger;
/// the entered value
// - getting this property will check for in range according to the
// current MinValue/MaxValue boundaries, if RangeChecking is set
// - if RangeChecking is not set, could return a NULL variant for no data
// - it will sound a beep in case of any out of range
// - it will also raise a ESynVariantLabeledEdit exception if
// RaiseExceptionOnError is set to TRUE (equals FALSE by default)
property Value: Variant read GetValue write SetValue;
/// set to TRUE if MinValue/MaxValue properties must be checked when
// reading Value property
property RangeChecking: boolean read fRangeChecking write fRangeChecking;
/// lowest allowed Variant value
property MinValue: Variant read FMinValue write FMinValue;
/// highest allowed Variant value
property MaxValue: Variant read FMaxValue write FMaxValue;
/// some additional popup hint to be displayed
// - by default, the allowed range is displayed: 'Min. Value: #, Max. Value: #'
// - you can specify here some additional text to be displayed when the mouse
// is hover the component
property AdditionalHint: string read FAdditionalHint write FAdditionalHint;
end;
/// register the TSynIntegerLabeledEdit component in the IDE toolbar
// - not necessary for the SQLite3 framework to run: since all User Interface
// is created from code, and not from the Delphi IDE, you don't have to register
// anything
procedure Register;
resourcestring
SErrorFieldNotValid = 'Field "%s"'#13'does not contain a valid %s value';
SErrorFieldTooSmall = 'Field "%s"'#13'is too small, value must be >= %s';
SErrorFieldTooLarge = 'Field "%s"'#13'is too large, value must be <= %s';
SMinMaxValue = 'Min. Value: %s, Max. Value: %s';
{$define VISTAFORM}
{$ifdef VISTAFORM}
{$R SQLite3UI.RES}
type
/// Vista-enabled TForm descendant
// - this form will have a button in the TaskBar
// - this form will hide the default Delphi application virtual form
// - this form can be with no caption bar using SetNoCaption method
TVistaForm = class(TForm)
protected
fNoCaption: TPanel;
fNoCaptionLabel: TLabel;
fMinimizeBtn: TSpeedButton;
fMaximizeBtn: TSpeedButton;
fCloseBtn: TSpeedButton;
procedure NoCaptionMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
procedure BtnClick(Sender: TObject);
procedure CreateParams(var Params: TCreateParams); override;
procedure WMSyscommand(var M: TMessage); message WM_SYSCOMMAND;
public
/// call this method to hide the Caption bar and replace it with a TPanel
procedure SetNoCaption(aTopMostPanel: TPanel; aLabelLeft: integer);
/// the TPanel instance replacing the Caption bar
property NoCaptionPanel: TPanel read fNoCaption;
/// the TLabel instance created on NoCaptionPanel to replace the Caption bar
property NoCaptionLabel: TLabel read fNoCaptionLabel;
end;
/// low level VCL routine in order to hide the application from Windows task bar
// - don't use it directly: it's called by TVistaForm.CreateParams()
procedure HideAppFormTaskBarButton;
{$endif}
/// draw a CheckBox in the Canvas Handle of the Wwindow hWnd, in the middle
// of the Rect coordinates
// - use theming under XP, Vista and Seven
procedure DrawCheckBox(hWnd: THandle; Handle: HDC; const Rect: TRect; Checked: boolean);
/// test if the ClearType is enabled for font display
// - ClearType is a software technology that improves the readability of text
// on liquid crystal display (LCD) monitors
function IsClearTypeEnabled: boolean;
/// enable the ClearType font display
// - under Windows 2000, standard font smoothing is forced, since Clear Type
// was introduced with XP
procedure ClearTypeEnable;
/// create an Icon
// - return the .lnk file name (i.e. Name+'.lnk')
function CreateAnIcon (const Name, Description, Path, Parameters,
WorkingDir, IconFilename: TFileName; const IconIndex: Integer;
const RunMinimized: Boolean = false): TFileName;
/// get the corresponding windows folder, from its ID
function GetShellFolderPath(const FolderID: Integer): string;
const
CSIDL_PROGRAMS = $0002;
CSIDL_DOCUMENTS = $0005;
CSIDL_COMMON_PROGRAMS = $0017;
CSIDL_COMMON_STARTMENU = $0016;
CSIDL_COMMON_DESKTOPDIRECTORY = $0019;
/// allow an application to access the network through the Windows firewall
// - works on Windows WP, Vista and Seven
// - caller process must have the administrator rights (this is the case
// for a setup program)
procedure AddApplicationToFirewall(const EntryName, ApplicationPathAndExe: string);
/// open a firewall port on the current computer
// - works on Windows XP, Vista and Seven
// - caller process must have the administrator rights (this is the case
// for a setup program)
procedure AddPortToFirewall(const EntryName: string; PortNumber: cardinal);
/// fill TStringGrid.Cells[] with the supplied data
// - will be slower than the TSQLTableToGrid method, but will work on
// a non standard TDrawGrid component
// - it will display date & time and enumerates as plain text, and handle
// the header properly (using the current SQLite3i18n language settings, if any)
// - the Client optional parameter will be used to display any RecordRef column
// - all data will be stored within the TStringGrid: you can safely release the
// Source data after having called this procedure
procedure FillStringGrid(Source: TSQLTable; Dest: TStringGrid; Client: TSQLRest=nil);
implementation
uses
ShellApi, ComObj, Activex, Shlobj, VarUtils;
procedure CreateShellLink (const Filename, Description, ShortcutTo, Parameters,
WorkingDir, IconFilename: String; const IconIndex: Integer;
const RunMinimized: Boolean);
{ Creates a lnk file named Filename, with a description of Description, which
points to ShortcutTo. }
var
Obj: IUnknown;
SL: IShellLink;
PF: IPersistFile;
WideFilename: WideString;
begin
Obj := CreateComObject(CLSID_ShellLink);
SL := Obj as IShellLink;
SL.SetPath(pointer(ShortcutTo));
SL.SetArguments(pointer(Parameters));
if WorkingDir <> '' then
SL.SetWorkingDirectory(pointer(WorkingDir));
if IconFilename <> '' then
SL.SetIconLocation(pointer(IconFilename), IconIndex);
SL.SetDescription(pointer(Description));
PF := Obj as IPersistFile;
WideFilename := Filename;
PF.Save(PWideChar(WideFilename), True);
{ Delphi 3+ automatically releases COM objects when they go out of scope }
end;
function GetShellFolderPath(const FolderID: Integer): string;
var pidl: PItemIDList;
Buffer: array[0..MAX_PATH-1] of Char;
Malloc: IMalloc;
begin
Result := '';
if Win32MajorVersion<4 then Exit;
if SUCCEEDED(SHGetSpecialFolderLocation(0, FolderID, pidl)) then begin
if SHGetPathFromIDList(pidl, Buffer) then begin
Result := Buffer;
if Result[length(Result)]<>'\' then
Result := Result+'\';
end;
if not FAILED(SHGetMalloc(Malloc)) then
Malloc.Free(pidl);
end;
end;
function CreateAnIcon (const Name, Description, Path, Parameters,
WorkingDir, IconFilename: TFileName; const IconIndex: Integer;
const RunMinimized: Boolean = false): TFileName;
var Dir: TFileName;
begin
result := Name + '.lnk';
Dir := ExtractFilePath(result);
if Dir='' then begin
if Win32Platform=VER_PLATFORM_WIN32_NT then
Dir := GetShellFolderPath(CSIDL_COMMON_DESKTOPDIRECTORY) else
Dir := GetShellFolderPath(CSIDL_DESKTOPDIRECTORY);
if Dir[length(Dir)]='\' then
result := Dir+result else
result := Dir+'\'+result;
end;
if not DirectoryExists(Dir) then begin
Dir := ExpandFilename(Dir);
if not CreateDirectory(pointer(Dir), nil) then exit;
SHChangeNotify(SHCNE_MKDIR, SHCNF_PATH, pointer(Dir), nil);
SHChangeNotify(SHCNE_UPDATEDIR, SHCNF_PATH or SHCNF_FLUSH,
pointer(ExtractFilePath(Dir)), nil);
end;
CreateShellLink(result, Description, Path, Parameters, WorkingDir,
IconFilename, IconIndex, RunMinimized);
SHChangeNotify(SHCNE_CREATE, SHCNF_PATH, pointer(result), nil);
SHChangeNotify(SHCNE_UPDATEDIR, SHCNF_PATH or SHCNF_FLUSH,
pointer(ExtractFilePath(result)), nil);
end;
const
NET_FW_PROFILE_DOMAIN = 0;
NET_FW_PROFILE_STANDARD = 1;
NET_FW_PROFILE2_PRIVATE = 2;
NET_FW_PROFILE2_PUBLIC = 4;
NET_FW_IP_VERSION_ANY = 2;
NET_FW_IP_PROTOCOL_UDP = 17;
NET_FW_IP_PROTOCOL_TCP = 6;
NET_FW_SCOPE_ALL = 0;
NET_FW_SCOPE_LOCAL_SUBNET = 1;
NET_FW_ACTION_ALLOW = 1;
function GetXPFirewall(var fwMgr, profile: OleVariant): boolean;
begin
result := (Win32Platform=VER_PLATFORM_WIN32_NT) and
(Win32MajorVersion>5) or ((Win32MajorVersion=5) and (Win32MinorVersion>0));
if result then // need Windows XP at least
try
fwMgr := CreateOleObject('HNetCfg.FwMgr');
profile := fwMgr.LocalPolicy.CurrentProfile;
except
on E: Exception do
result := false;
end;
end;
function GetVistaSevenFirewall(var fwMgr, rule: OleVariant; const Description: string): boolean;
begin
result := (Win32Platform=VER_PLATFORM_WIN32_NT) and (Win32MajorVersion>5);
if result then // need Windows Vista at least
try
fwMgr := CreateOleObject('HNetCfg.FwPolicy2');
rule := CreateOleObject('HNetCfg.FWRule');
rule.Name := Description;
rule.Description := Description;
rule.Protocol := NET_FW_IP_PROTOCOL_TCP;
rule.Enabled := true;
rule.Profiles := NET_FW_PROFILE2_PRIVATE OR NET_FW_PROFILE2_PUBLIC;
rule.Action := NET_FW_ACTION_ALLOW;
except
on E: Exception do
result := false;
end;
end;
procedure AddApplicationToFirewall(const EntryName, ApplicationPathAndExe: string);
var fwMgr, profile, app, rule: OleVariant;
begin
if Win32MajorVersion<6 then begin
if GetXPFirewall(fwMgr,profile) and profile.FirewallEnabled then begin
app := CreateOLEObject('HNetCfg.FwAuthorizedApplication');
app.ProcessImageFileName := ApplicationPathAndExe;
app.Name := EntryName;
app.Scope := NET_FW_SCOPE_ALL;
app.IpVersion := NET_FW_IP_VERSION_ANY;
app.Enabled := true;
profile.AuthorizedApplications.Add(app);
end;
end else
if GetVistaSevenFirewall(fwMgr,rule,EntryName) then begin
rule.ApplicationName := ApplicationPathAndExe;
fwMgr.Rules.Add(rule);
end;
end;
procedure AddPortToFirewall(const EntryName: string; PortNumber: cardinal);
var fwMgr, profile, port, rule: OleVariant;
begin
if Win32MajorVersion<6 then begin
if GetXPFirewall(fwMgr,profile) and profile.FirewallEnabled then begin
port := CreateOLEObject('HNetCfg.FWOpenPort');
port.Name := EntryName;
port.Protocol := NET_FW_IP_PROTOCOL_TCP;
port.Port := PortNumber;
port.Scope := NET_FW_SCOPE_ALL;
port.Enabled := true;
profile.GloballyOpenPorts.Add(port);
end;
end else
if GetVistaSevenFirewall(fwMgr,rule,EntryName) then begin
rule.LocalPorts := PortNumber;
fwMgr.Rules.Add(rule);
end;
end;
const // for Delphi 6 compilation
SPI_GETFONTSMOOTHINGTYPE = $200A;
SPI_SETFONTSMOOTHINGTYPE = $200B;
FE_FONTSMOOTHINGSTANDARD = $0001;
FE_FONTSMOOTHINGCLEARTYPE = $0002;
function IsClearTypeEnabled: boolean;
// see http://blogs.msdn.com/michkap/archive/2008/03/01/7971061.aspx
var MType, SmoothFonts: DWORD;
begin
SmoothFonts := 0;
SystemParametersInfo(SPI_GETFONTSMOOTHING, 1, @SmoothFonts, 0);
SystemParametersInfo(SPI_GETFONTSMOOTHINGTYPE, 0, @MType, 0);
result := boolean(SmoothFonts) and (MType=FE_FONTSMOOTHINGCLEARTYPE);
end;
procedure ClearTypeEnable;
var MType, SmoothFonts: DWORD;
begin
if (Win32MajorVersion<5) or IsClearTypeEnabled then
exit; // no font smoothing before Win2K
SystemParametersInfo(SPI_GETFONTSMOOTHING, 1, @SmoothFonts, 0);
if not boolean(SmoothFonts) then
SystemParametersInfo(SPI_SETFONTSMOOTHING, 1, nil, SPIF_UPDATEINIFILE or
SPIF_SENDCHANGE);
if (Win32MajorVersion=5) and (Win32MinorVersion=0) then
MType := FE_FONTSMOOTHINGSTANDARD else // no Clear Type on Win2K
MType := FE_FONTSMOOTHINGCLEARTYPE;
SystemParametersInfo(SPI_SETFONTSMOOTHINGTYPE, 0, Pointer(MType),
SPIF_UPDATEINIFILE or SPIF_SENDCHANGE);
end;
/// attempt to change the scroll bar size -> need Grids.pas rewrite -> buggy
procedure SetScrollVPage(Handle: HWND; aPage, aMax: Integer);
var ScrollInfo: TScrollInfo;
begin
if Handle=0 then
exit;
with ScrollInfo do begin
cbSize := SizeOf(ScrollInfo);
fMask := SIF_ALL;
GetScrollInfo(Handle, SB_VERT, ScrollInfo);
if nMin>=nMax then exit;
if nMax<>127 then exit;
fMask := SIF_DISABLENOSCROLL or SIF_PAGE or SIF_RANGE;
if aMax<=0 then
nPage := 1 else begin
nPage := 30;
inc(nMax,nPage);
end;
end;
SetScrollInfo(Handle, SB_VERT, ScrollInfo, true);
end;
{ THintWindowDelayed }
constructor THintWindowDelayed.Create(aOwner: TComponent);
begin
inherited Create(aOwner);
Color := $C0FFFF;
fFontColor := clBlack;
{ if aOwner.InheritsFrom(TSQLTableToGrid) and // done by ParentFont := true
not TSQLTableToGrid(aOwner).NotDefined then
Canvas.Font := TDrawGrid(TSQLTableToGrid(aOwner).Owner).Canvas.Font; }
end;
destructor THintWindowDelayed.Destroy;
begin
Hide;
inherited;
end;
procedure THintWindowDelayed.ShowDelayedUTF8(const Text: RawUTF8; X, Y, Time: integer;
FontColor: TColor; AlignLeft: boolean=false);
var R: TRect;
begin
if self=nil then
exit;
Hide;
if Text='' then
exit; // no text to show
R := CalcHintRect(512, Text, nil);
if not AlignLeft then
dec(X,R.Right-R.Left); // align right
inc(R.Left,X);
inc(R.Right,X);
inc(R.Top,Y);
inc(R.Bottom,Y);
ActivateHint(R,U2S(Text)); // perform Caption := Text
fUTF8Text := Text; // so it will work with Delphi 2009/2010
if Time<>0 then begin
if fTimerEnabled then
KillTimer(Handle,1) else
fTimerEnabled := true;
SetTimer(Handle,1,Time,nil);
end;
fFontColor := FontColor;
Show;
end;
procedure THintWindowDelayed.ShowDelayedUTF8(const Text: RawUTF8; Origin: TControl;
X, Y, Time: integer; FontColor: TColor; AlignLeft: boolean=false);
begin
with Origin.ClientToScreen(Point(X,Y)) do
ShowDelayedUTF8(Text,X,Y,Time,FontColor,AlignLeft);
end;
function THintWindowDelayed.CalcHintRect(MaxWidth: Integer;
const AHint: RawUTF8; AData: Pointer): TRect;
var U: RawUnicode; // faster than a WideString
begin // unicode version
Result := Rect(0, 0, MaxWidth, 0);
U := Utf8DecodeToRawUnicode(AHint);
DrawTextW(Canvas.Handle, pointer(U), length(U) shr 1, Result, DT_CALCRECT or DT_LEFT or
DT_WORDBREAK or DT_NOPREFIX or DrawTextBiDiModeFlagsReadingOnly);
Inc(Result.Right, 6);
Inc(Result.Bottom, 2);
end;
procedure THintWindowDelayed.Paint;
var R: TRect;
U: RawUnicode; // faster than a WideString
begin // unicode version
R := ClientRect;
Inc(R.Left, 2);
Inc(R.Top, 2);
Canvas.Font.Color := fFontColor;
U := Utf8DecodeToRawUnicodeUI(fUTF8Text);
DrawTextW(Canvas.Handle, pointer(U), -1, R, DT_LEFT or DT_NOPREFIX or
DT_WORDBREAK or DrawTextBiDiModeFlagsReadingOnly);
end;
procedure THintWindowDelayed.VisibleChanging;
begin
try
if fTimerEnabled and Visible then begin // are we in a Hide process?
KillTimer(Handle,1);
fTimerEnabled := false;
fRow := -1;
if HandleAllocated then
ReleaseHandle;
end;
finally
inherited VisibleChanging;
end;
end;
procedure THintWindowDelayed.WMTimer(var Msg: TWMTimer);
begin
Hide;
fRow := -1;
end;
procedure THintWindowDelayed.ShowDelayedString(const Text: string; X, Y,
Time: integer; FontColor: TColor; AlignLeft: boolean=false);
begin
ShowDelayedUTF8(S2U(Text),X,Y,Time,FontColor,AlignLeft);
end;
procedure THintWindowDelayed.ShowDelayedString(const Text: string;
Origin: TControl; X, Y, Time: integer; FontColor: TColor; AlignLeft: boolean=false);
begin
with Origin.ClientToScreen(Point(X,Y)) do
ShowDelayedUTF8(S2U(Text),X,Y,Time,FontColor,AlignLeft);
end;
{ TSQLTableToGrid }
resourcestring
sErrorTSQLTableToGridNoData = '%s didn''t receive any data for %s';
constructor TSQLTableToGrid.Create(aOwner: TDrawGrid; aTable: TSQLTable;
aClient: TSQLRestClientURI);
begin
if aTable=nil then
raise Exception.CreateFmt(sErrorTSQLTableToGridNoData,[ClassName,aOwner.Name]);
From(aOwner).Free; // any old association will be overriden by this instance
inherited Create(aOwner);
fTable := aTable;
fClient := aClient;
fHint := THintWindowDelayed.Create(self);
aOwner.RowCount := 2; // first reset row count, to avoid flicking
aOwner.FixedRows := 1;
with aOwner.Canvas.TextExtent('jQH<51>;') do
aOwner.DefaultRowHeight := cy+4;
aOwner.Options := [goFixedHorzLine,goFixedVertLine,goVertLine,
goHorzLine,goColSizing,goRowSelect,goThumbTracking]; // no goRangeSelect
aOwner.FixedCols := 0;
aOwner.ColCount := Table.FieldCount;
SetLength(fFieldOrder,Table.FieldCount); // auto filled to false
fCurrentFieldOrder := Table.FieldIndex(
pointer(SQLGetOrder(Table.QuerySQL))); // get 'ORDER BY' field index
if fCurrentFieldOrder>=0 then
fFieldOrder[fCurrentFieldOrder] := true; // mark 'ORDER BY' field ascending
fFieldIndexTimeLogForMark := -2; // so GetFieldIndexTimeLogForMark will get it
aOwner.OnDrawCell := DrawCell;
aOwner.OnMouseMove := DrawGridMouseMove;
aOwner.OnMouseDown := DrawGridMouseDown;
aOwner.OnMouseUp := DrawGridMouseUp;
aOwner.OnSelectCell := DrawGridSelectCell;
aOwner.OnKeyPress := DrawGridKeyPress;
aOwner.OnKeyDown := DrawGridKeyDown;
if Table.RowCount>0 then
aOwner.RowCount := Table.RowCount+1;
end;
destructor TSQLTableToGrid.Destroy;
begin
if (Owner<>nil) and Owner.InheritsFrom(TDrawGrid) then
with TDrawGrid(Owner) do begin // reset the Grid overriden events to avoid GPF
OnDrawCell := nil;
OnMouseMove := nil;
OnMouseDown := nil;
OnMouseUp := nil;
OnSelectCell := nil;
OnKeyPress := nil;
OnKeyDown := nil;
end;
FreeAndNil(fTable);
inherited;
end;
class function TSQLTableToGrid.From(Grid: TDrawGrid): TSQLTableToGrid;
var i: integer;
begin
if Grid<>nil then
for i := 0 to Grid.ComponentCount-1 do begin
result := pointer(Grid.Components[i]);
if result.InheritsFrom(TSQLTableToGrid) and (result.Owner=Grid) then
exit;
end;
result := nil;
end;
const
CheckBoxWidth = 13;
{$WARN SYMBOL_DEPRECATED OFF} // for ThemeServices
procedure DrawCheckBox(hWnd: THandle; Handle: HDC; const Rect: TRect; Checked: boolean);
const
{$ifdef WITHUXTHEME}
XPState: array[boolean] of TThemedButton = (
tbCheckBoxUncheckedNormal, tbCheckBoxCheckedNormal);
{$endif}
Win32State: array[boolean] of cardinal = (
DFCS_BUTTONCHECK, DFCS_BUTTONCHECK or DFCS_CHECKED);
var DrawRect: TRect;
begin
DrawRect.Left := Rect.Left+(Rect.Right-Rect.Left-CheckBoxWidth)shr 1;
DrawRect.Top:= Rect.Top+2;
DrawRect.Right := DrawRect.Left + CheckBoxWidth;
DrawRect.Bottom := DrawRect.Top + CheckBoxWidth;
{$ifdef WITHUXTHEME}
if ThemeServices.ThemesEnabled then begin // Windows XP and later: use theming
ThemeServices.DrawElement(handle,
ThemeServices.GetElementDetails(XPState[Checked]), DrawRect);
end else
{$endif}
DrawFrameControl(Handle,DrawRect,DFC_BUTTON,Win32State[Checked]);
end;
procedure TSQLTableToGrid.DrawCell(Sender: TObject; ACol, ARow: Integer;
Rect: TRect; State: TGridDrawState);
var Options, x,y, L, i, XInc: integer;
StringValue: string; // generic string type, VCL ready
Points: array[0..2] of TPoint;
Centered, WithMark: boolean;
tmp: array[0..255] of WideChar; // 255 chars is wide enough inside a cell
begin
// default cell draw
if NotDefined then
exit;
if Assigned(OnDrawCellBackground) then
OnDrawCellBackground(Owner,ACol,ARow,Rect,State);
if (cardinal(ARow)>cardinal(Table.RowCount)) or
(cardinal(ACol)>=cardinal(Table.FieldCount)) then // avoid any possible GPF
exit;
with TDrawGrid(Owner).Canvas do begin
Options := ETO_CLIPPED or TextFlags;
if Brush.Style <> bsClear then
Options := Options or ETO_OPAQUE;
WithMark := fMarkAllowed and (ACol=0);
if ARow=0 then begin
// 1. 1st row = field name: bold + centered translated text, with sort indicator
if not Assigned(OnValueText) or
not OnValueText(Table,ACol,0,StringValue) then
StringValue := Table.GetCaption(0,ACol); // auto translated
if not Assigned(OnDrawCellBackground) then
Font.Style := [fsBold];
L := Rect.Right-Rect.Left;
if WithMark then
dec(L,CheckBoxWidth+4);
Centered := TextWidth(StringValue)<L;
if Centered then begin
UnSetBit64(fFieldNameTruncated,ACol);
XInc := L shr 1;
SetTextAlign(Handle,TA_CENTER);
end else begin
SetBit64(fFieldNameTruncated,ACol);
XInc := 2;
end;
if WithMark then
inc(XInc,CheckBoxWidth+4);
ExtTextOut(Handle, Rect.Left+XInc,Rect.Top+2, Options, @Rect, pointer(StringValue),
length(StringValue), nil); // direct translated text centered draw
if Centered then
SetTextAlign(Handle,TA_LEFT);
Font.Style := [];
if fCurrentFieldOrder=ACol then begin
// sorted field: draw sort indicator
x := Rect.Right-8;
if fFieldOrder[ACol] then begin
Points[0].X := x+5; // ascending order arrow
Points[1].X := x;
Points[2].X := x-5;
end else begin
Points[0].X := x-5; // descending order arrow
Points[1].X := x+5;
Points[2].X := x;
end;
y := Rect.Bottom-9;
if fFieldOrder[ACol] then begin
Points[0].Y := y-5; // ascending order arrow
Points[1].Y := y+5;
Points[2].Y := y-5
end else begin
Points[0].Y := y+5; // descending order arrow
Points[1].Y := y+5;
Points[2].Y := y-5
end;
Brush.Color := clWhite; // fill the arrow content
Polygon(Points);
Pen.Color := clLtGray; // draw the arrow border
MoveTo(Points[0].X,Points[0].Y);
LineTo(Points[1].X,Points[1].Y);
Pen.Color := clGray;
LineTo(Points[2].X,Points[2].Y);
LineTo(Points[0].X,Points[0].Y);
end;
end else begin
// 2. field value rows
Centered := GetBit64(fCentered,ACol);
if Centered then begin
SetTextAlign(Handle,TA_CENTER);
L := Rect.Right-Rect.Left;
if WithMark then
dec(L,CheckBoxWidth+4);
XInc := L shr 1;
end else
XInc := 4;
if WithMark then
inc(XInc,CheckBoxWidth+4);
if Assigned(OnValueText) and OnValueText(Table,ACol,ARow,StringValue) then
ExtTextOut(Handle, Rect.Left+XInc, Rect.Top+2, Options, @Rect, pointer(StringValue),
length(StringValue), nil) else // translated text
case Table.ExpandAsString(ARow,ACol,Client,StringValue) of
// very fast response (calculated once)
sftBoolean:
// display boolean as checkbox
DrawCheckBox(TDrawGrid(Owner).Handle, Handle, Rect,
PWord(Table.Get(ARow,ACol))^<>ord('0')); // fast StrComp(,'0')
sftEnumerate, sftTimeLog, sftRecord, sftDateTime:
ExtTextOut(Handle, Rect.Left+XInc, Rect.Top+2, Options, @Rect, pointer(StringValue),
length(StringValue), nil); // translated text
//sftID: { TODO : display ID as TSQLRecord content? better calculate it in SELECT }
else begin
// normal field value: unicode text (even with Delphi 2-2007 VCL), left aligned
L := Table.GetWP(ARow,ACol,tmp,high(tmp));
for i := 0 to L-1 do // replace #13,#10 chars in the grid with spaces
if tmp[i]<' ' then
tmp[i] := ' ';
// direct unicode text draw
ExtTextOutW(Handle, Rect.Left+XInc, Rect.Top+2, Options, @Rect, tmp, L, nil);
end;
end;
if Centered then
SetTextAlign(Handle,TA_LEFT);
end;
if WithMark then begin // draw left side checkbox with Marked[] value
inc(Rect.Left,2);
Rect.Right := Rect.Left+CheckBoxWidth;
DrawCheckBox(TDrawGrid(Owner).Handle, Handle,Rect,Marked[ARow]);
end;
end;
end;
procedure TSQLTableToGrid.SortForce(ACol: integer; Ascending: boolean;
ARow: integer=-1);
var
MIDs: TIntegerDynArray;
begin
if NotDefined or (ACol>=Table.FieldCount) then // we allow ACol<0 (see below)
exit;
if ARow<0 then
ARow := TDrawGrid(Owner).Row; // keep current row selected if none specified
if ACol<0 then begin
// if ACol=-1, then the Marked[] rows are shown first, in current sort
if fMarked=nil then
exit; // no Marked[] rows to put in first place
ARow := Table.IDColumnHiddenValue(ARow); // current row
Table.SortBitsFirst(fMarked[0]);
TDrawGrid(Owner).Row := Table.RowFromID(ARow);
// no PageChanged here
end else begin
if fMarked<>nil then
Table.IDArrayFromBits(fMarked[0],MIDs); // save current marked entries
fFieldOrder[ACol] := Ascending;
fCurrentFieldOrder := ACol;
Table.SortFields(ACol,fFieldOrder[ACol],@ARow);
if MIDs<>nil then
Table.IDArrayToBits(fMarked[0],MIDs); // restore marked entries
TDrawGrid(Owner).Row := ARow; // reselect row after sort (+ Invalidate)
PageChanged; // hide any pending popup Hint e.g.
end;
end;
procedure TSQLTableToGrid.DrawGridMouseDown(Sender: TObject;
Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
var ACol, ARow: integer;
Hint: string; // generic string type, for VCL
begin
if NotDefined then // avoid any possible GPF
exit;
fMouseDownMarkedValue := markNone;
TDrawGrid(Owner).MouseToCell(X, Y, ACol, ARow);
if cardinal(ACol)<cardinal(Table.FieldCount) then
if ARow=0 then
if (ssCtrl in Shift) or (Button<>mbLeft) then begin
// Ctrl or right button pressed -> display first row as hint
ShowHintString(U2S(Table.Get(ARow,ACol)),ACol,ARow,4000);
end else begin
// first row -> sort fields
if fMarkAllowed and (X<CheckBoxWidth+4) then
// sort Marked[] first
SortForce(-1,true) else
if fCurrentFieldOrder=ACol then
// same column -> toggle sorting order
SortForce(ACol,not fFieldOrder[ACol]) else
// column changed -> sort ascending first
SortForce(ACol,true);
end else
// not first row: data
if (Button=mbRight) and (ssRight in Shift) and Assigned(OnRightClickCell) then
OnRightClickCell(Table,ACol,ARow,X,Y) else
if (ssCtrl in Shift) or (Button<>mbLeft) then begin
if not Assigned(OnHintText) or
not OnHintText(Table,ACol,ARow,Hint) then
Table.ExpandAsString(ARow,ACol,Client,Hint);
// Hint := IntToStr(SelectedID);
ShowHintString(Hint,ACol,ARow,4000);
end else
if (Button=mbLeft) and (ACol=0) and fMarkAllowed and (X<CheckBoxWidth+4) then begin
if Marked[ARow] then // on click: invert current Marked[] checkbox state
fMouseDownMarkedValue := markOff else
fMouseDownMarkedValue := markOn;
Marked[ARow] := (fMouseDownMarkedValue=markOn);
end;
TDrawGrid(Owner).Invalidate;
end;
resourcestring
sPutMarkedRowFirst = 'Sort marked rows first';
procedure TSQLTableToGrid.DrawGridMouseMove(Sender: TObject;
Shift: TShiftState; X, Y: Integer);
var ACol, ARow: integer;
begin
if NotDefined then // avoid any possible GPF
exit;
TDrawGrid(Owner).MouseToCell(X, Y, ACol, ARow);
if cardinal(ACol)>=cardinal(Table.FieldCount) then
exit;
if ARow=0 then begin
// over the checkbox left of the first row: show appropriate hint
if (ACol=0) and fMarkAllowed and (fMarked<>nil) and (X<CheckBoxWidth+4) and
((Hint=nil) or (Hint.Col<>-1) or (Hint.Row<>0)) then begin
ShowHintString(sPutMarkedRowFirst,0,0,1000);
Hint.fCol := -1; // column = -1 for checkbox
end else
// over the first row, i.e. column name: show hint if name was truncated
if (not FieldTitleTruncatedNotShownAsHint) and GetBit64(fFieldNameTruncated,ACol) and
((Hint=nil) or (Hint.Col<>ACol) or (Hint.Row<>0)) then
ShowHintString(Table.GetCaption(0,ACol),ACol,0,1000);
end else
// select/unselect checkbox left of data rows
if (ACol=0) and fMarkAllowed and (fMouseDownMarkedValue<>markNone) and
(X<=CheckBoxWidth+4) then
Marked[ARow] := (fMouseDownMarkedValue=markOn);
end;
procedure TSQLTableToGrid.DrawGridMouseUp(Sender: TObject;
Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
fMouseDownMarkedValue := markNone; // reset Marked[] checkbox state
end;
procedure TSQLTableToGrid.DrawGridSelectCell(Sender: TObject;
ACol, ARow: Integer; var CanSelect: Boolean);
begin
if NotDefined then // avoid any possible GPF
exit;
if not fIncrementalSearchMove then
fIncrementalSearch := ''; // reset incremental key lookup
if Assigned(OnSelectCell) and not fOnSelectCellProcessing then
try
fOnSelectCellProcessing := true; // avoid endless loop or GPF
OnSelectCell(Sender,ACol,ARow,CanSelect);
finally
fOnSelectCellProcessing := false;
end;
end;
procedure TSQLTableToGrid.Resize(Sender: TObject);
var i: integer;
width,tot: cardinal;
begin
if NotDefined then // avoid any possible GPF
exit;
width := TDrawGrid(Owner).ClientWidth-GetSystemMetrics(SM_CXBORDER)*4;
if (width=fLastWidth) then
exit; // draw if necessary
fLastWidth := width;
tot := Table.FieldLengthMeanSum;
for i := 0 to Table.FieldCount-1 do
TDrawGrid(Owner).ColWidths[i] := (width*Table.FieldLengthMean(i))div tot;
// with TDrawGrid(Owner) do SetScrollVPage(Handle,ClientHeight div DefaultRowHeight,RowCount-FixedRows);
end;
function TSQLTableToGrid.NotDefined: boolean;
begin
result := (self=nil) or (Owner=nil) or (Table=nil) or
not Owner.InheritsFrom(TDrawGrid);
end;
procedure TSQLTableToGrid.DrawGridKeyPress(Sender: TObject; var Key: Char);
var F,R: integer;
begin // incremental key lookup
if NotDefined then // avoid any possible GPF
exit;
if Key=#27 then // ESC key reset the lookup string
fIncrementalSearch := '' else
if Key=#8 then begin // BACKDEL key delete last lookup char
if fIncrementalSearch<>'' then
SetLength(fIncrementalSearch,length(fIncrementalSearch)-1);
end else
if Key>=' ' then
if (Key=' ') and (fIncrementalSearch='') then begin
// space with no lookup key -> allow mark/unmark current one
R := TDrawGrid(Owner).Row;
if fMarkAllowed and (R>0) then begin
Marked[R] := not Marked[R];
inc(R);
if R<=Table.RowCount then
TDrawGrid(Owner).Row := R else // and go to next row
TDrawGrid(Owner).Invalidate;
end;
exit;
end else
fIncrementalSearch := fIncrementalSearch+RawUTF8(NormToUpper[AnsiChar(Key)]);
if fIncrementalSearch='' then begin
if fHint<>nil then
fHint.Hide;
exit; // nothing to search
end;
// search from the next row
F := fCurrentFieldOrder;
R := Table.SearchValue(fIncrementalSearch,TDrawGrid(Owner).Row+1,fCurrentFieldOrder,Client);
if R=0 then begin // not found: search from the beginning
R := Table.SearchValue(fIncrementalSearch,1,fCurrentFieldOrder,Client);
if R=0 then begin // not found in this field: search in all fields
R := Table.SearchValue(fIncrementalSearch,TDrawGrid(Owner).Row+1,@F,Client);
if R=0 then // not found: search from the beginning
R := Table.SearchValue(fIncrementalSearch,1,@F,Client);
end;
end;
if R>0 then begin
fIncrementalSearchMove := true; // DrawGridSelectCell() won't reset fIncremental
TDrawGrid(Owner).Row := R;
fIncrementalSearchMove := false;
ShowHintString(U2S(fIncrementalSearch),F,R,2000,clNavy);
end else // not found: display searched string in red
ShowHintString(U2S(fIncrementalSearch)+'?',fCurrentFieldOrder,
TDrawGrid(Owner).Row,2000,clRed);
end;
procedure TSQLTableToGrid.ShowHintString(const Text: string; ACol, ARow, Time: integer;
FontColor: TColor=clBlack);
begin
if NotDefined then // avoid any possible GPF
exit;
if Text='' then begin
if fHint<>nil then
fHint.Hide;
exit;
end;
fHint.fCol := ACol;
fHint.fRow := ARow;
with TDrawGrid(Owner).CellRect(ACol,ARow) do
fHint.ShowDelayedString(Text,TDrawGrid(Owner),Right,Top+2,Time,FontColor);
end;
procedure TSQLTableToGrid.DrawGridKeyDown(Sender: TObject; var Key: Word;
Shift: TShiftState);
var F: integer;
begin
if NotDefined or (Shift<>[]) then // avoid any possible GPF
exit;
case Key of
VK_LEFT: // LEFT ARROW key sort previous column
if fCurrentFieldOrder>0 then
F := fCurrentFieldOrder-1 else
F := Table.FieldCount-1;
VK_RIGHT: // RIGHT ARROW key sort next column
if fCurrentFieldOrder>=Table.FieldCount-1 then
F := 0 else
F := fCurrentFieldOrder+1;
else exit;
end;
SortChange(F);
Key := 0; // we proceed this key -> caller will ignore it
end;
procedure TSQLTableToGrid.SortChange(ACol: integer);
begin
if not NotDefined and (cardinal(ACol)<cardinal(Table.FieldCount)) then
if fCurrentFieldOrder=ACol then
// same column -> toggle sorting order
SortForce(ACol,not fFieldOrder[ACol]) else
// column changed -> sort ascending first
SortForce(ACol,true);
end;
procedure TSQLTableToGrid.IDColumnHide;
begin
if NotDefined or not Table.IDColumnHide then
exit;
TDrawGrid(Owner).ColCount := Table.FieldCount; // we loose one column
fCurrentFieldOrder := -1; // force no previous column -> always ascending order
SortChange(0);
end;
function TSQLTableToGrid.SelectedID: integer;
begin
if NotDefined then
result := 0 else
result := Table.IDColumnHiddenValue(TDrawGrid(Owner).Row);
end;
function TSQLTableToGrid.SelectedRecordCreate: TSQLRecord;
var aID: integer;
RecordType: TSQLRecordClass;
begin
aID := SelectedID;
if (aID<=0) or (fClient=nil) then
result := nil else begin
RecordType := TSQLRecordClass(Table.QueryRecordType);
if (RecordType=nil) or not RecordType.InheritsFrom(TSQLRecord) then
result := nil else
result := RecordType.Create(fClient,aID);
end;
end;
procedure TSQLTableToGrid.SetCentered(const Cols: array of cardinal);
var i: integer;
begin
fCentered := 0;
if Table<>nil then
for i := 0 to high(Cols) do
if Cols[i]<cardinal(Table.FieldCount) then
SetBit64(fCentered,Cols[i]-1);
end;
procedure TSQLTableToGrid.SetCentered(aCol: cardinal);
begin
if self<>nil then
if aCol<cardinal(Table.FieldCount) then
SetBit64(fCentered,aCol);
end;
procedure TSQLTableToGrid.PageChanged;
begin
if (Self<>nil) and (Hint<>nil) then
Hint.Hide;
end;
function TSQLTableToGrid.Refresh(ForceRefresh: Boolean=false): boolean;
var Refreshed: boolean;
aID: integer;
begin
if self=nil then
result := false else begin
aID := Table.IDColumnHiddenValue(TDrawGrid(Owner).Row);
if ForceRefresh then
result := true else
result := Client.UpdateFromServer([Table],Refreshed) and Refreshed;
if result then
AfterRefresh(aID);
end;
end;
procedure TSQLTableToGrid.AfterRefresh(aID: integer);
var CurrentRow: integer;
Bulk: boolean;
begin
with TDrawGrid(Owner) do begin
if Table.RowCount=0 then
RowCount := 2 else
RowCount := Table.RowCount+1;
if Table.FieldCount<>ColCount then begin
// we get results from a void table for the first time
ColCount := Table.FieldCount;
SetLength(fFieldOrder,Table.FieldCount);
end;
CurrentRow := Table.RowFromID(aID);
if CurrentRow=0 then
CurrentRow := 1;
Row := CurrentRow;
TopRow := 1;
Invalidate;
end;
Resize(nil); // auto resize columns
if Assigned(OnSelectCell) then
OnSelectCell(Owner,0,CurrentRow,Bulk); // refresh details
end;
procedure TSQLTableToGrid.SetFieldLengthMean(const Lengths: RawUTF8; aMarkAllowed: boolean);
var L, i: integer;
c: AnsiChar;
Means: array of cardinal;
begin
if self=nil then Exit;
fMarkAllowed := aMarkAllowed;
L := length(Lengths);
if L=0 then begin
SetLength(Means,Table.FieldCount);
for i := 0 to Table.FieldCount-1 do
Means[i] := 10; // some fixed width
end else
if Table.FieldCount=L then begin
SetLength(Means,L);
for i := 0 to L-1 do begin
c := Lengths[i+1];
if c in ['a'..'z'] then begin
SetCentered(i);
dec(c,32);
end;
Means[i] := ord(c)+(-ord('A')+1);
end;
Table.SetFieldLengthMean(Means);
end;
if aMarkAllowed then
Table.FieldLengthMeanIncrease(0,2); // space for Marked[] checkbox e.g.
end;
procedure TSQLTableToGrid.SetFieldFixedWidth(aColumnWidth: integer);
var i: integer;
begin
with TDrawGrid(Owner) do
for i := 0 to ColCount-1 do
ColWidths[i] := aColumnWidth;
end;
function TSQLTableToGrid.GetMarked(RowIndex: integer): boolean;
begin
dec(RowIndex);
if (self=nil) or (fMarked=nil) or
(cardinal(RowIndex)>=cardinal(length(fMarked)shl 3)) then
result := false else
result := GetBit(fMarked[0],RowIndex);
end;
procedure TSQLTableToGrid.SetMarked(RowIndex: integer;
const Value: boolean);
var n: integer;
begin
dec(RowIndex);
if (self=nil) or (cardinal(RowIndex)>=cardinal(Table.RowCount)) then
exit;
n := (Table.RowCount shr 3)+1;
if length(fMarked)<n then // need to allocate/expand fMarked[] array?
SetLength(fMarked,n); // initializes all expanded bytes to 0
if Value then
SetBit(fMarked[0],RowIndex) else
UnSetBit(fMarked[0],RowIndex)
end;
procedure TSQLTableToGrid.SetMark(aAction: TSQLAction);
var i: integer;
V, Time: Int64;
const
DIFFTIME: array[actMarkOlderThanOneDay..actMarkOlderThanOneYear] of double =
(1,7,31,183,365); // 183 = more or less half a year
begin
if NotDefined then
exit;
with TDrawGrid(Owner) do
case aAction of
actMarkAllEntries:
for i := 1 to RowCount do
Marked[i] := true;
actUnMarkAll:
if fMarked<>nil then
Finalize(fMarked);
actmarkInverse:
for i := 1 to RowCount do
Marked[i] := not Marked[i];
actMarkOlderThanOneDay..actMarkOlderThanOneYear:
if FieldIndexTimeLogForMark>=0 then begin
// use TDateTime calculation because TTimeLog is not duration compatible
Iso8601(Time).From(Now-DIFFTIME[aAction],true);
for i := 1 to RowCount do begin
SetInt64(Table.Get(i,fFieldIndexTimeLogForMark),V);
if (V>0) and (V<=Time) then
Marked[i] := true;
end;
end;
else exit;
end;
TDrawGrid(Owner).Invalidate; // refresh screen
end;
function TSQLTableToGrid.GetMarkAvailable: boolean;
var i: integer;
begin
result := fMarkAllowed and (fMarked<>nil);
if not result then
exit;
for i := 0 to Table.RowCount-1 do // very any bit is realy set
if GetBit(fMarked[0],i) then
exit;
result := false;
end;
function TSQLTableToGrid.GetMarkedIsOnlyCurrrent: boolean;
begin
with TDrawGrid(Owner) do
result := fMarkAllowed and (fMarked<>nil) and
(GetBitsCount(fMarked[0],RowCount)=1) and Marked[Row];
end;
function TSQLTableToGrid.GetMarkedTotalCount: integer;
begin
with TDrawGrid(Owner) do
if not fMarkAllowed or (fMarked=nil) then
result := 0 else
result := GetBitsCount(fMarked[0],RowCount);
end;
function TSQLTableToGrid.ExpandRowAsString(Row: integer; Client: TObject): string;
var F, i: integer;
Text: string; // generic VCL-ready string
begin
result := '';
if (self=nil) or (cardinal(Row)>cardinal(Table.RowCount)) or (Table.FieldCount<=0) then
exit;
for F := 0 to Table.FieldCount-1 do begin
if (not Assigned(OnValueText)) or
(not OnValueText(Table,F,Row,text)) then
Table.ExpandAsString(Row,F,Client,Text);
i := pos(#13,Text); // trim multi-line text to first line
if i>0 then
SetLength(Text,i-1);
if (F>0) and (text<>'') then
text := ' '+text;
result := result+text;
end;
end;
procedure TSQLTableToGrid.OnTableUpdate(State: TOnTableUpdateState);
begin
if (self=nil) or (fMarked=nil) then
exit; // wrong parameters
case State of
tusPrepare:
// save current marked entries
if fMarked<>nil then begin
Table.IDArrayFromBits(fMarked[0],fOnTableUpdateID);
exit; // don't Finalize(fOnTableUpdateID)
end;
tusChanged:
// restore marked entries
if fOnTableUpdateID<>nil then
Table.IDArrayToBits(fMarked[0],fOnTableUpdateID);
end;
// tusNoChange or tusChanged: release IDs memory
if fOnTableUpdateID<>nil then
Finalize(fOnTableUpdateID);
end;
function TSQLTableToGrid.GetMarkedBits: pointer;
begin
result := fMarked;
end;
function TSQLTableToGrid.GetDrawGrid: TDrawGrid;
begin
if self=nil then
result := nil else
result := TDrawGrid(Owner);
end;
function TSQLTableToGrid.GetFieldIndexTimeLogForMark: integer;
var F: integer;
begin
if fFieldIndexTimeLogForMark=-2 then begin
fFieldIndexTimeLogForMark := -1;
for F := 0 to Table.FieldCount-1 do
if Table.FieldType(F,nil)=sftTimeLog then begin
fFieldIndexTimeLogForMark := F;
break;
end;
end;
result := fFieldIndexTimeLogForMark;
end;
{ TVistaForm }
{$ifdef VISTAFORM}
var
AppFormTaskBarButtonHidden: boolean = false;
procedure HideAppFormTaskBarButton;
{$ifdef ISDELPHI2007ANDUP}
begin
end;
{$else}
var ExtendedStyle: Integer;
begin
if AppFormTaskBarButtonHidden then
exit;
AppFormTaskBarButtonHidden := true;
ShowWindow(Application.Handle, SW_HIDE);
ExtendedStyle := GetWindowLong(Application.Handle, GWL_EXSTYLE);
SetWindowLong(Application.Handle, GWL_EXSTYLE,
ExtendedStyle and not WS_EX_APPWINDOW or WS_EX_TOOLWINDOW);
ShowWindow(Application.Handle, SW_SHOW);
Application.DialogHandle := Application.Handle;
end;
{$endif}
procedure TVistaForm.BtnClick(Sender: TObject);
begin
if Sender=fCloseBtn then
Close else
if Sender=fMinimizeBtn then
WindowState := wsMinimized else
if (Sender=fMaximizeBtn) or Sender.InheritsFrom(TLabel) then
if WindowState=wsMaximized then
WindowState := wsNormal else
WindowState := wsMaximized;
end;
procedure TVistaForm.CreateParams(var Params: TCreateParams);
begin
HideAppFormTaskBarButton; // check if not already hidden
inherited;
{$ifndef ISDELPHI2007ANDUP}
Params.ExStyle := Params.ExStyle and not WS_EX_TOOLWINDOW or
WS_EX_APPWINDOW; // this form will appear in the TaskBar
{$endif}
if fNoCaption<>nil then begin
Params.ExStyle := Params.ExStyle or WS_EX_STATICEDGE;
Params.Style := Params.Style or WS_SIZEBOX;
end;
end;
procedure TVistaForm.NoCaptionMouseDown(Sender: TObject;
Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
const
SC_DRAGMOVE = $F012;
begin
if Button = mbLeft then
begin
ReleaseCapture;
Perform(WM_SYSCOMMAND, SC_DRAGMOVE, 0);
end;
end;
procedure TVistaForm.SetNoCaption(aTopMostPanel: TPanel; aLabelLeft: integer);
const BUT_SIZE = 16;
procedure SetEvent(Event: TMouseEvent);
var i: integer;
begin
if fNoCaption<>nil then begin
fNoCaption.OnMouseDown := Event;
for i := 0 to fNoCaption.ComponentCount-1 do
if fNoCaption.Components[i].InheritsFrom(TLabel) then
with TLabel(fNoCaption.Components[i]) do
if not Assigned(OnClick) then
OnMouseDown := Event;
end;
end;
var R: integer;
function Btn(const ResName: string): TSpeedButton;
begin
dec(R,BUT_SIZE+1);
result := TSpeedButton.Create(aTopMostPanel);
result.Parent := aTopMostPanel;
result.SetBounds(R,2,BUT_SIZE+1,BUT_SIZE);
result.Anchors := [akRight, akTop];
result.Glyph.LoadFromResourceName(HInstance,ResName);
result.OnClick := BtnClick;
result.Flat := true;
end;
begin
if aTopMostPanel=fNoCaption then
exit;
SetEvent(nil);
fNoCaption := aTopMostPanel;
if aTopMostPanel<>nil then begin
fNoCaptionLabel := TLabel.Create(aTopMostPanel);
R := aTopMostPanel.ClientWidth-4;
fCloseBtn := Btn('ButClose');
fMaximizeBtn := Btn('ButMax');
fMinimizeBtn := Btn('ButMin');
with fNoCaptionLabel do begin
Parent := aTopMostPanel;
Transparent := true;
AutoSize := false;
SetBounds(aLabelLeft,2,R-aLabelLeft,20);
Anchors := [akLeft, akRight, akBottom, akTop];
Alignment := Classes.taCenter;
OnDblClick := BtnClick;
end;
SetEvent(NoCaptionMouseDown);
BorderStyle := bsNone;
{$ifdef ISDELPHI2007ANDUP}
RecreateWnd;
{$endif}
end else
BorderStyle := bsSizeable;
end;
procedure TVistaForm.WMSyscommand(var M: TMessage);
begin
{$ifdef ISDELPHI2007ANDUP}
inherited;
{$else}
case (M.WParam and $FFF0) of
SC_MINIMIZE, SC_RESTORE, SC_MAXIMIZE: begin
M.Result := DefWindowProc(Handle, M.Msg, M.WParam, M.LParam);
ShowWindow(Application.Handle, SW_HIDE);
end;
else
inherited;
end;
{$endif}
end;
{$endif}
{ TSynLabeledEdit }
constructor TSynLabeledEdit.Create(AOwner: TComponent);
begin
inherited;
ShowHint := True;
MaxValue := 100;
MinValue := 1;
Text := '';
end;
function TSynLabeledEdit.GetValue: Variant;
var Txt: string;
begin
Txt := trim(Text);
if Txt='' then
if RangeChecking then begin
result := MinValue;
Text := MinValue;
end else
VarClear(result) else begin
if not IsValid(Txt,result) then begin
if RaiseExceptionOnError then
raise ESynLabeledEdit.CreateFmt(SErrorFieldNotValid, [EditLabel.Caption,
GetEnumCaption(TypeInfo(TSynLabeledEditKind),Kind)]);
if RangeChecking then
result := MinValue else
VarClear(result);
end;
end;
if RangeChecking and (Result<MinValue) then begin
Text := MinValue;
if RaiseExceptionOnError then
raise ESynLabeledEdit.CreateFmt(SErrorFieldTooSmall,
[EditLabel.Caption,string(MinValue)]);
end;
if RangeChecking and (Result>MaxValue) then begin
Text := MaxValue;
if RaiseExceptionOnError then
raise ESynLabeledEdit.CreateFmt(SErrorFieldTooLarge,
[EditLabel.Caption,string(MaxValue)]);
end;
end;
function TSynLabeledEdit.IsValid(const Txt: string; var ToValue: Variant): Boolean;
var err: integer;
resInt32: integer;
resInt64: Int64;
resDouble: Double;
begin
result := false;
case Kind of
sleInteger: begin
val(Txt,resInt32,err);
if err<>0 then exit;
ToValue := resInt32;
end;
sleInt64: begin
val(Txt,resInt64,err);
if err<>0 then exit;
ToValue := resInt64;
end;
sleCurrency: begin
val(Txt,resDouble,err);
if err<>0 then exit;
ToValue := Currency(resDouble);
end;
sleDouble: begin
val(Txt,resDouble,err);
if err<>0 then exit;
ToValue := resDouble;
end;
end;
result := true;
end;
procedure TSynLabeledEdit.KeyPress(var Key: char);
Var Temp: Variant;
TempString: string;
begin
inherited;
if Key=#8 then exit;
if Key=',' then
Key := '.';
if (Kind in [sleInteger,sleInt64]) and (Key='.') then
Key := #0;
if ((Key<'0') or (Key>'9')) and (Key<>'.') then begin
Key := #0;
Beep;
exit;
end;
TempString := Text;
if (TempString=#0) or (Self.SelText=TempString) then
exit;
Insert(Key, TempString, Self.SelStart + 1);
if IsValid(TempString,Temp) and
RangeChecking and (Temp>MaxValue) then begin
Key := #0;
Beep;
end;
end;
procedure TSynLabeledEdit.MouseMove(Shift: TShiftState; X,Y: integer);
var H: string;
begin
inherited;
if RangeChecking then
H := format(SMinMaxValue,[string(FMinValue),string(FMaxValue)]);
if FAdditionalHint <> '' then
H := trim(FAdditionalHint + #13#10 + H);
Hint := H;
end;
procedure TSynLabeledEdit.SetValue(const Value: Variant);
begin
Text := Value;
end;
function TSynLabeledEdit.ToString(NumberOfDigits: integer): string;
var numberOfMissingDigits: integer;
begin
Result := Value;
numberOfMissingDigits := NumberOfDigits - Length(Result);
if numberOfMissingDigits > 0 then
Result := StringOfChar('0', numberOfMissingDigits) + Result;
end;
function TSynLabeledEdit.ValidateValue: boolean;
var V: Variant;
begin
result := IsValid(Text,V);
if RangeChecking and result then
result := (V>=MinValue) and (V<=MaxValue);
end;
procedure FillStringGrid(Source: TSQLTable; Dest: TStringGrid; Client: TSQLRest);
var C,R: integer;
s: string;
begin
if (Source=nil) or (Dest=nil) then
exit; // avoid GPF
Dest.ColCount := Source.FieldCount;
Dest.RowCount := Source.RowCount+1;
for R := 0 to Source.RowCount+1 do
for C := 0 to Source.FieldCount-1 do begin
Source.ExpandAsString(R,C,Client,s); // will do all the magic
Dest.Cells[C,R] := s;
end;
end;
procedure Register;
begin
RegisterComponents('Synopse',[TSynLabeledEdit]);
end;
end.