Componentes.Terceros.jvcl/official/3.32/run/JvListView.pas

2931 lines
84 KiB
ObjectPascal

{-----------------------------------------------------------------------------
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/MPL-1.1.html
Software distributed under the License is distributed on an "AS IS" basis,
WITHOUT WARRANTY OF ANY KIND, either expressed or implied. See the License for
the specific language governing rights and limitations under the License.
The Original Code is: JvListView.PAS, released on 2001-02-28.
The Initial Developer of the Original Code is Sébastien Buysse [sbuysse att buypin dott com]
Portions created by Sébastien Buysse are Copyright (C) 2001 Sébastien Buysse.
All Rights Reserved.
Contributor(s): Michael Beck [mbeck att bigfoot dott com].
dejoy
Olivier Sannier [obones att altern dott org]
You may retrieve the latest version of this file at the Project JEDI's JVCL home page,
located at http://jvcl.sourceforge.net
Known Issues:
Mantis 3932: In the OnCustomDrawItem, if you change the canvas font directly,
then your changes will be ignored and the items be drawn bold if
the item brush is not used for the given list view style
(report for instance). As a workaround, always change the item's
properties, never the canvas' directly.
-----------------------------------------------------------------------------}
// $Id: JvListView.pas 11120 2007-01-04 09:54:33Z obones $
unit JvListView;
{$I jvcl.inc}
{$I vclonly.inc}
interface
uses
{$IFDEF UNITVERSIONING}
JclUnitVersioning,
{$ENDIF UNITVERSIONING}
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
ComCtrls, CommCtrl, Menus, ImgList, Clipbrd,
JvJCLUtils, JvJVCLUtils, JvTypes, JvExComCtrls, JvAppStorage, JvVCL5Utils;
type
TJvViewStyle = (vsIcon, vsSmallIcon, vsList, vsReport, vsTile);
TJvHeaderImagePosition = (hipLeft, hipRight);
const
WM_AUTOSELECT = WM_USER + 1;
ALL_VIEW_STYLES = [vsIcon, vsSmallIcon, vsList, vsReport, vsTile];
type
TJvListView = class;
TJvListViewGroup = class;
EJvListViewError = EJVCLException;
// Mantis 980: new type for Groups
TLVGROUP = record
cbSize: UINT;
mask: UINT;
pszHeader: LPWSTR;
cchHeader: Integer;
pszFooter: LPWSTR;
cchFooter: Integer;
iGroupId: Integer;
stateMask: UINT;
state: UINT;
uAlign: UINT;
end;
// TJvSortMethod = (smAutomatic, smAlphabetic, smNonCaseSensitive, smNumeric, smDate, smTime, smDateTime, smCurrency);
TJvOnProgress = procedure(Sender: TObject; Progression, Total: Integer) of object;
TJvListViewCompareGroupEvent = procedure(Sender: TObject; Group1, Group2: TJvListViewGroup; var Compare: Integer) of object;
TJvListItems = class(TListItems, IJvAppStorageHandler, IJvAppStoragePublishedProps)
private
FOwnerInterface: IInterface;
protected
{ IInterface }
function _AddRef: Integer; stdcall;
function _Release: Integer; stdcall;
{ IJvAppStorageHandler }
procedure ReadFromAppStorage(AppStorage: TJvCustomAppStorage; const BasePath: string);
procedure WriteToAppStorage(AppStorage: TJvCustomAppStorage; const BasePath: string);
{ List item reader used in the call to ReadList. }
procedure ReadListItem(Sender: TJvCustomAppStorage; const Path: string;
const List: TObject; const Index: Integer; const ItemName: string);
{ List item writer used in the call to WriteList. }
procedure WriteListItem(Sender: TJvCustomAppStorage; const Path: string;
const List: TObject; const Index: Integer; const ItemName: string);
{ List item deleter used in the call to WriteList. }
procedure DeleteListItem(Sender: TJvCustomAppStorage; const Path: string;
const List: TObject; const First, Last: Integer; const ItemName: string);
public
function QueryInterface(const IID: TGUID; out Obj): HRESULT; virtual; stdcall;
procedure AfterConstruction; override;
end;
TJvListItem = class(TListItem)
private
FPopupMenu: TPopupMenu;
FBold: Boolean;
FFont: TFont;
FBrush: TBrush;
FGroupId: Integer;
FTileColumns: TIntegerList;
procedure SetBrush(const Value: TBrush);
procedure SetGroupId(const Value: Integer);
procedure SetTileColumns(const Value: TIntegerList);
procedure ReadTileColumns(Reader: TReader);
procedure WriteTileColumns(Writer: TWriter);
procedure TileColumnsChange(Sender: TObject; Item: Integer; Action: TListNotification);
protected
procedure SetPopupMenu(const Value: TPopupMenu);
procedure SetFont(const Value: TFont);
procedure UpdateTileColumns;
public
constructor CreateEnh(AOwner: TListItems; const Popup: TPopupMenu);
destructor Destroy; override;
procedure DefineProperties(Filer: TFiler); override;
property PopupMenu: TPopupMenu read FPopupMenu write SetPopupMenu;
property TileColumns: TIntegerList read FTileColumns write SetTileColumns;
published
property Font: TFont read FFont write SetFont;
property Brush: TBrush read FBrush write SetBrush;
property GroupId: Integer read FGroupId write SetGroupId default -1;
// Published now for the usage of AppStorage.Read/WritePersistent
property Caption;
property Checked;
property Selected;
property SubItems;
end;
TJvListExtendedColumn = class(TCollectionItem)
private
FSortMethod: TJvSortMethod;
FUseParentSortMethod: Boolean;
FHeaderImagePosition: TJvHeaderImagePosition;
FUseParentHeaderImagePosition: Boolean;
function GetSortMethod: TJvSortMethod;
procedure SetSortMethod(const Value: TJvSortMethod);
function GetHeaderImagePosition: TJvHeaderImagePosition;
procedure SetHeaderImagePosition(const Value: TJvHeaderImagePosition);
procedure SetUseParentHeaderImagePosition(const Value: Boolean);
public
constructor Create(Collection: Classes.TCollection); override;
procedure Assign(AValue: TPersistent); override;
published
property SortMethod: TJvSortMethod read GetSortMethod write SetSortMethod default smAutomatic;
property UseParentSortMethod : Boolean read FUseParentSortMethod write FUseParentSortMethod default True;
property HeaderImagePosition: TJvHeaderImagePosition read GetHeaderImagePosition write SetHeaderImagePosition default hipLeft;
property UseParentHeaderImagePosition : Boolean read FUseParentHeaderImagePosition write SetUseParentHeaderImagePosition default True;
end;
TJvListExtendedColumns = class(TOwnedCollection)
private
function GetItem(Index: Integer): TJvListExtendedColumn;
procedure SetItem(Index: Integer; const Value: TJvListExtendedColumn);
function Owner : TPersistent;
public
constructor Create(AOwner: TPersistent);
property Items[Index: Integer] : TJvListExtendedColumn read GetItem write SetItem; default;
end;
TJvListViewGroup = class(TCollectionItem)
private
FHeader: WideString;
FGroupId: Integer;
FHeaderAlignment: TAlignment;
procedure SetHeader(const Value: WideString);
procedure SetHeaderAlignment(const Value: TAlignment);
procedure SetGroupId(const Value: Integer);
procedure UpdateGroupProperties(const NewGroupId: Integer = -1);
public
constructor Create(Collection: Classes.TCollection); override;
destructor Destroy; override;
procedure Assign(AValue: TPersistent); override;
procedure SetLVGROUP(var GroupInfo: TLVGROUP);
published
property GroupId: Integer read FGroupId write SetGroupId default -1;
property Header: WideString read FHeader write SetHeader;
property HeaderAlignment: TAlignment read FHeaderAlignment write SetHeaderAlignment default taLeftJustify;
end;
TJvListViewGroups = class(TOwnedCollection)
private
FSorted: Boolean;
function GetItem(Index: Integer): TJvListViewGroup;
procedure SetItem(Index: Integer; const Value: TJvListViewGroup);
function ParentList: TJvListView;
procedure InsertGroupIntoList(group: TJvListViewGroup);
procedure RemoveGroupFromList(group: TJvListViewGroup);
function Compare(Id1, Id2: Integer): Integer;
function GetItemById(GroupId: Integer): TJvListViewGroup;
procedure SetSorted(const Value: Boolean);
protected
procedure Notify(Item: TCollectionItem; Action: TCollectionNotification); override;
public
constructor Create(AOwner: TPersistent);
procedure Sort;
property Items[Index: Integer] : TJvListViewGroup read GetItem write SetItem; default;
property ItemsById[GroupId: Integer]: TJvListViewGroup read GetItemById;
published
property Sorted: Boolean read FSorted write SetSorted;
end;
TJvGroupsPropertiesBorderRect = class(TJvRect)
public
constructor Create;
published
property Top default 12;
end;
TJvGroupsPropertiesBorderColors = class(TPersistent)
private
FRight: TColor;
FBottom: TColor;
FTop: TColor;
FLeft: TColor;
FOnChange: TNotifyEvent;
procedure SetBottom(const Value: TColor);
procedure SetLeft(const Value: TColor);
procedure SetRight(const Value: TColor);
procedure SetTop(const Value: TColor);
protected
procedure DoChange;
public
constructor Create;
procedure Assign(Source: TPersistent); override;
property OnChange: TNotifyEvent read FOnChange write FOnChange;
published
property Top: TColor read FTop write SetTop default $C8D0D4;
property Left: TColor read FLeft write SetLeft default clWhite;
property Bottom: TColor read FBottom write SetBottom default clWhite;
property Right: TColor read FRight write SetRight default clWhite;
end;
TJvGroupsProperties = class(TPersistent)
private
FBorderSize: TJvGroupsPropertiesBorderRect;
FBorderColor: TJvGroupsPropertiesBorderColors;
FHeaderColor: TColor;
FOnChange: TNotifyEvent;
FLoading: Boolean;
procedure SetBorderSize(const Value: TJvGroupsPropertiesBorderRect);
procedure SetBorderColor(const Value: TJvGroupsPropertiesBorderColors);
procedure BorderSizeChange(Sender: TObject);
procedure BorderColorChange(Sender: TObject);
procedure SetHeaderColor(const Value: TColor);
protected
procedure DoChange;
public
constructor Create;
destructor Destroy; override;
procedure LoadFromList(List: TCustomListView);
property OnChange: TNotifyEvent read FOnChange write FOnChange;
published
property BorderSize: TJvGroupsPropertiesBorderRect read FBorderSize write SetBorderSize;
property HeaderColor: TColor read FHeaderColor write SetHeaderColor default clBlack;
// Note that BorderColor is currently ignored by the Win32 API
property BorderColor: TJvGroupsPropertiesBorderColors read FBorderColor write SetBorderColor;
end;
TJvViewStyles = set of TJvViewStyle;
TJvTileSizeKind = (tskAutoSize, tskFixedWidth, tskFixedHeight, tskFixedSize);
TJvTileViewProperties = class(TPersistent)
private
FLabelMargin: TJvRect;
FTileSize: TJvSize;
FSubLinesCount: Integer;
FTileSizeKind: TJvTileSizeKind;
FOnChange: TNotifyEvent;
FLoading: Boolean;
procedure SetLabelMargin(const Value: TJvRect);
procedure SetSubLinesCount(const Value: Integer);
procedure SetTileSize(const Value: TJvSize);
procedure SetTileSizeKind(const Value: TJvTileSizeKind);
procedure LabelMarginChange(Sender: TObject);
procedure TileSizeChange(Sender: TObject);
protected
procedure DoChange;
public
constructor Create;
destructor Destroy; override;
procedure LoadFromList(List: TCustomListView);
property OnChange: TNotifyEvent read FOnChange write FOnChange;
published
property TileSizeKind: TJvTileSizeKind read FTileSizeKind write SetTileSizeKind default tskAutoSize;
property TileSize: TJvSize read FTileSize write SetTileSize;
property SubLinesCount: Integer read FSubLinesCount write SetSubLinesCount default 1;
property LabelMargin: TJvRect read FLabelMargin write SetLabelMargin;
end;
TJvInsertMarkPosition = (impBefore, impAfter);
TJvListView = class(TJvExListView)
private
FAutoClipboardCopy: Boolean;
FSortOnClick: Boolean;
FLast: Integer;
FOnSaveProgress: TJvOnProgress;
FOnLoadProgress: TJvOnProgress;
FOnAutoSort: TJvListViewColumnSortEvent;
FSortMethod: TJvSortMethod;
FOnHorizontalScroll: TNotifyEvent;
FOnVerticalScroll: TNotifyEvent;
FImageChangeLink: TChangeLink;
FHeaderImagePosition: TJvHeaderImagePosition;
FHeaderImages: TCustomImageList;
FAutoSelect: Boolean;
FPicture: TPicture;
FExtendedColumns: TJvListExtendedColumns;
FSavedExtendedColumns: TJvListExtendedColumns;
FViewStylesItemBrush: TJvViewStyles; // use for Create/DestroyWnd process
FGroupView: Boolean;
FGroups: TJvListViewGroups;
FGroupsProperties: TJvGroupsProperties;
FOnCompareGroups: TJvListViewCompareGroupEvent;
FViewStyle: TJvViewStyle;
FTileViewProperties: TJvTileViewProperties;
FInsertMarkColor: TColor;
FSettingJvViewStyle: Boolean;
FSettingHeaderImagePosition: Boolean;
procedure DoPictureChange(Sender: TObject);
procedure SetPicture(const Value: TPicture);
procedure SetGroupView(const Value: Boolean);
procedure SetGroups(const Value: TJvListViewGroups);
procedure SetGroupsProperties(const Value: TJvGroupsProperties);
procedure SetTileViewProperties(const Value: TJvTileViewProperties);
procedure SetInsertMarkColor(const Value: TColor);
procedure SetHeaderImagePosition(const Value: TJvHeaderImagePosition);
procedure SetHeaderImages(const Value: TCustomImageList);
procedure UpdateHeaderImages(HeaderHandle: Integer);
procedure WMAutoSelect(var Msg: TMessage); message WM_AUTOSELECT;
procedure SetExtendedColumns(const Value: TJvListExtendedColumns);
{$IFDEF COMPILER5}
function GetItemIndex: Integer;
procedure SetItemIndex(const Value: Integer);
{$ENDIF COMPILER5}
procedure SetViewStylesItemBrush(const Value: TJvViewStyles);
function DoCompareGroups(Group1, Group2: TJvListViewGroup): Integer;
procedure TileViewPropertiesChange(Sender: TObject);
procedure GroupsPropertiesChange(Sender: TObject);
procedure LoadTileViewProperties;
procedure LoadGroupsProperties;
protected
function CreateListItem: TListItem; override;
function CreateListItems: TListItems; {$IFDEF COMPILER6_UP} override; {$ENDIF}
procedure WMHScroll(var Msg: TWMHScroll); message WM_HSCROLL;
procedure WMVScroll(var Msg: TWMVScroll); message WM_VSCROLL;
procedure KeyUp(var Key: Word; Shift: TShiftState); override;
procedure Notification(AComponent: TComponent; Operation: TOperation); override;
function GetColumnsOrder: string;
procedure SetColumnsOrder(const Order: string);
procedure SetItemPopup(Node: TListItem; Value: TPopupMenu);
function GetItemPopup(Node: TListItem): TPopupMenu;
procedure DoHeaderImagesChange(Sender: TObject);
procedure Loaded; override;
{$IFDEF COMPILER6_UP}
procedure SetViewStyle(Value: TViewStyle); override;
{$ENDIF COMPILER6_UP}
procedure SetJvViewStyle(Value: TJvViewStyle); virtual;
procedure CreateWnd; override;
procedure DestroyWnd; override;
procedure WMNCCalcSize(var Msg: TWMNCCalcSize); message WM_NCCALCSIZE;
procedure LVMDeleteColumn(var Msg: TMessage); message LVM_DELETECOLUMN;
procedure LVMInsertColumn(var Msg: TMessage); message LVM_INSERTCOLUMN;
procedure LVMSetColumn(var Msg: TMessage); message LVM_SETCOLUMN;
procedure CNNotify(var Message: TWMNotify); message CN_NOTIFY;
procedure InsertItem(Item: TListItem); override;
function IsCustomDrawn(Target: TCustomDrawTarget; Stage: TCustomDrawStage): Boolean; {$IFDEF COMPILER6_UP} override; {$ENDIF}
function CustomDraw(const ARect: TRect; Stage: TCustomDrawStage): Boolean; override;
function CustomDrawItem(Item: TListItem; State: TCustomDrawState;
Stage: TCustomDrawStage): Boolean; override;
function CustomDrawSubItem(Item: TListItem; SubItem: Integer;
State: TCustomDrawState; Stage: TCustomDrawStage): Boolean; override;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
procedure ColClick(Column: TListColumn); override;
procedure SaveToStrings(Strings: TStrings; Separator: Char);
procedure LoadFromStrings(Strings: TStrings; Separator: Char);
procedure SaveToFile(FileName: string; ForceOldStyle: Boolean = False);
procedure LoadFromFile(FileName: string);
procedure SaveToStream(Stream: TStream; ForceOldStyle: Boolean = False);
procedure LoadFromStream(Stream: TStream);
procedure SaveToCSV(FileName: string; Separator: Char = ';');
procedure LoadFromCSV(FileName: string; Separator: Char = ';');
procedure SetSmallImages(const Value: TCustomImageList);
{$IFDEF COMPILER5}
procedure SelectAll;
procedure DeleteSelected;
{$ENDIF COMPILER5}
procedure UnselectAll;
procedure InvertSelection;
function MoveUp(Index: Integer; Focus: Boolean = True): Integer;
function MoveDown(Index: Integer; Focus: Boolean = True): Integer;
function SelectNextItem(Focus: Boolean = True): Integer;
function SelectPrevItem(Focus: Boolean = True): Integer;
function ShowInsertMark(ItemIndex: Integer; Position: TJvInsertMarkPosition): Boolean;
function HideInsertMark: Boolean;
function GetInsertMarkPosition(const X, Y: Integer; var ItemIndex: Integer; var Position: TJvInsertMarkPosition): Boolean;
property ItemPopup[Item: TListItem]: TPopupMenu read GetItemPopup write SetItemPopup;
procedure SetBounds(ALeft: Integer; ATop: Integer; AWidth: Integer;
AHeight: Integer); override;
procedure SetFocus; override;
{$IFDEF COMPILER5}
property ItemIndex: Integer read GetItemIndex write SetItemIndex;
{$ENDIF COMPILER5}
published
property AutoSelect: Boolean read FAutoSelect write FAutoSelect default True;
property ColumnsOrder: string read GetColumnsOrder write SetColumnsOrder;
property HintColor;
property Picture: TPicture read FPicture write SetPicture;
property HeaderImagePosition: TJvHeaderImagePosition read FHeaderImagePosition write SetHeaderImagePosition default hipLeft;
property HeaderImages: TCustomImageList read FHeaderImages write SetHeaderImages;
property SortMethod: TJvSortMethod read FSortMethod write FSortMethod default smAutomatic;
property SortOnClick: Boolean read FSortOnClick write FSortOnClick default True;
property SmallImages write SetSmallImages;
property AutoClipboardCopy: Boolean read FAutoClipboardCopy write FAutoClipboardCopy default True;
property GroupView: Boolean read FGroupView write SetGroupView default False;
property Groups: TJvListViewGroups read FGroups write SetGroups;
property GroupsProperties: TJvGroupsProperties read FGroupsProperties write SetGroupsProperties;
property TileViewProperties: TJvTileViewProperties read FTileViewProperties write SetTileViewProperties;
property InsertMarkColor: TColor read FInsertMarkColor write SetInsertMarkColor default clBlack;
property ViewStylesItemBrush : TJvViewStyles read FViewStylesItemBrush write SetViewStylesItemBrush default ALL_VIEW_STYLES;
property ViewStyle: TJvViewStyle read FViewStyle write SetJvViewStyle default vsIcon;
property OnAutoSort: TJvListViewColumnSortEvent read FOnAutoSort write FOnAutoSort;
property OnHorizontalScroll: TNotifyEvent read FOnHorizontalScroll write FOnHorizontalScroll;
property OnLoadProgress: TJvOnProgress read FOnLoadProgress write FOnLoadProgress;
property OnSaveProgress: TJvOnProgress read FOnSaveProgress write FOnSaveProgress;
property OnVerticalScroll: TNotifyEvent read FOnVerticalScroll write FOnVerticalScroll;
property OnCompareGroups: TJvListViewCompareGroupEvent read FOnCompareGroups write FOnCompareGroups;
property OnMouseEnter;
property OnMouseLeave;
property OnParentColorChange;
// This property contains a collection that allows to specify additional
// properties for each columns (sort method for instance). It can not be
// included in the Columns collection as the VCL does not offer a way
// to specify which class to use for the items of the Columns collection.
// Note that this one (ExtendedColumns) is populated automatically when
// a column is added or deleted. But because the VCL code for add starts
// by deleting all columns to reinsert them after, you should not change
// the properties for any item of ExtendedColumns in a loop that contains
// a call to the Add method of the Columns property.
property ExtendedColumns : TJvListExtendedColumns read FExtendedColumns write SetExtendedColumns;
end;
{$IFDEF UNITVERSIONING}
const
UnitVersioning: TUnitVersionInfo = (
RCSfile: '$URL: https://jvcl.svn.sourceforge.net/svnroot/jvcl/tags/JVCL3_32/run/JvListView.pas $';
Revision: '$Revision: 11120 $';
Date: '$Date: 2007-01-04 10:54:33 +0100 (jeu., 04 janv. 2007) $';
LogPath: 'JVCL\run'
);
{$ENDIF UNITVERSIONING}
implementation
uses
Math, Contnrs,
{$IFDEF HAS_UNIT_VARIANTS}
VarUtils, Variants,
{$ELSE}
ActiveX,
{$ENDIF HAS_UNIT_VARIANTS}
JclWideStrings,
JvConsts, JvResources;
type
// Mantis 980: New types for group/tile/insert mark handling
tagLVITEMA = packed record
mask: UINT;
iItem: Integer;
iSubItem: Integer;
state: UINT;
stateMask: UINT;
pszText: PAnsiChar;
cchTextMax: Integer;
iImage: Integer;
lParam: lParam;
iIndent: Integer;
iGroupId: Integer;
cColumns: UINT;
puColumns: PUINT;
end;
TLVITEMA = tagLVITEMA;
TFNLVGROUPCOMPARE = function (Group1_ID: Integer; Group2_ID: Integer; pvData: Pointer): Integer; stdcall;
PFNLVGROUPCOMPARE = ^TFNLVGROUPCOMPARE;
tagLVINSERTGROUPSORTED = packed record
pfnGroupCompare: PFNLVGROUPCOMPARE;
pvData: Pointer;
lvGroup: TLVGROUP;
end;
TLVINSERTGROUPSORTED = tagLVINSERTGROUPSORTED;
PLVINSERTGROUPSORTED = ^TLVINSERTGROUPSORTED;
tagLVTILEVIEWINFO = packed record
cbSize: UINT;
dwMask: DWORD;
dwFlags: DWORD;
sizeTile: TSize;
cLines: Integer;
rcLabelMargin: TRect;
end;
TLVTILEVIEWINFO = tagLVTILEVIEWINFO;
PLVTILEVIEWINFO = ^TLVTILEVIEWINFO;
tagLVTILEINFO = packed record
cbSize: UINT;
iItem: Integer;
cColumns: UINT;
puColumns: PUINT;
end;
TLVTILEINFO = tagLVTILEINFO;
PLVTILEINFO = ^TLVTILEINFO;
tagLVINSERTMARK = packed record
cbSize: UINT;
dwFlags: DWORD;
iItem: Integer;
dwReserved: DWORD;
end;
TLVINSERTMARK = tagLVINSERTMARK;
PLVINSERTMARK = ^TLVINSERTMARK;
tagLVGROUPMETRICS = packed record
cbSize: UINT;
mask: UINT;
Left: UINT;
Top: UINT;
Right: UINT;
Bottom: UINT;
crLeft: COLORREF;
crTop: COLORREF;
crRight: COLORREF;
crBottom: COLORREF;
crHeader: COLORREF;
crFooter: COLORREF;
end;
TLVGROUPMETRICS = tagLVGROUPMETRICS;
PLVGROUPMETRICS = ^TLVGROUPMETRICS;
const
// Mantis 980: New constants for group/tile/insert mark handling
LVM_SETTILEWIDTH = LVM_FIRST + 141;
LVM_SETVIEW = LVM_FIRST + 142;
LVM_INSERTGROUP = LVM_FIRST + 145;
LVM_SETGROUPINFO = LVM_FIRST + 147;
LVM_REMOVEGROUP = LVM_FIRST + 150;
LVM_MOVEITEMTOGROUP = LVM_FIRST + 154;
LVM_SETGROUPMETRICS = LVM_FIRST + 155;
LVM_GETGROUPMETRICS = LVM_FIRST + 156;
LVM_ENABLEGROUPVIEW = LVM_FIRST + 157;
LVM_SORTGROUPS = LVM_FIRST + 158;
LVM_INSERTGROUPSORTED = LVM_FIRST + 159;
LVM_REMOVEALLGROUPS = LVM_FIRST + 160;
LVM_SETTILEVIEWINFO = LVM_FIRST + 162;
LVM_GETTILEVIEWINFO = LVM_FIRST + 163;
LVM_SETTILEINFO = LVM_FIRST + 164;
LVM_GETTILEINFO = LVM_FIRST + 165;
LVM_SETINSERTMARK = LVM_FIRST + 166;
LVM_INSERTMARKHITTEST = LVM_FIRST + 168;
LVM_GETINSERTMARKRECT = LVM_FIRST + 169;
LVM_SETINSERTMARKCOLOR = LVM_FIRST + 170;
LVM_GETINSERTMARKCOLOR = LVM_FIRST + 171;
// ListViewItemFlag
LVIF_GROUPID = $0100;
// ListViewGroupFlag
LVGF_HEADER = $00000001;
LVGF_ALIGN = $00000008;
LVGF_GROUPID = $00000010;
// group alignment
LVGA_HEADER_LEFT = $00000001;
LVGA_HEADER_CENTER = $00000002;
LVGA_HEADER_RIGHT = $00000004;
// view styles
LV_VIEW_ICON = $00;
LV_VIEW_DETAILS = $01;
LV_VIEW_SMALLICON = $02;
LV_VIEW_LIST = $03;
LV_VIEW_TILE = $04;
// LVTVIF (ListViewTileViewInfoFlag Constants)
LVTVIF_AUTOSIZE = 0;
LVTVIF_FIXEDWIDTH = 1;
LVTVIF_FIXEDHEIGHT = 2;
LVTVIF_FIXEDSIZE = 3;
// LVTVIM (ListViewTileViewInfoMask Constants)
LVTVIM_TILESIZE = 1;
LVTVIM_COLUMNS = 2;
LVTVIM_LABELMARGIN = 4;
// LVIM (ListViewInsertMark Constants)
LVIM_AFTER = 1;
// LVGMF (ListViewGroupMetricsFlag Constants)
LVGMF_NONE = $00000000;
LVGMF_BORDERSIZE = $00000001;
LVGMF_BORDERCOLOR = $00000002;
LVGMF_TEXTCOLOR = $00000004;
AlignmentToLVGA: array[TAlignment] of Integer = (LVGA_HEADER_LEFT, LVGA_HEADER_RIGHT, LVGA_HEADER_CENTER);
TileSizeKindToLVTVIF: array[TJvTileSizeKind] of Integer = (LVTVIF_AUTOSIZE, LVTVIF_FIXEDWIDTH, LVTVIF_FIXEDHEIGHT, LVTVIF_FIXEDSIZE);
InsertMarkPositionToLVIM: array[TJvInsertMarkPosition] of Integer = (0, LVIM_AFTER);
// (rom) increased from 100
cColumnsHandled = 1024;
//=== { TJvListItem } ========================================================
constructor TJvListItem.CreateEnh(AOwner: TListItems; const Popup: TPopupMenu);
begin
inherited Create(AOwner);
FBold := False;
FPopupMenu := Popup; // (Salvatore) Get it from the JvListView
FFont := TFont.Create;
FBrush := TBrush.Create;
FGroupId := -1;
FTileColumns := TIntegerList.Create;
FTileColumns.OnChange := TileColumnsChange;
if AOwner.Owner is TJvListView then
FFont.Assign((AOwner.Owner as TJvListView).Font);
end;
procedure TJvListItem.DefineProperties(Filer: TFiler);
begin
inherited DefineProperties(Filer);
// Because a TList is not saved natively by Delphi, we do it ourselves.
Filer.DefineProperty('TileColumns', ReadTileColumns, WriteTileColumns, True);
end;
destructor TJvListItem.Destroy;
begin
FTileColumns.Free;
FFont.Free;
FBrush.Free;
inherited Destroy;
end;
procedure TJvListItem.ReadTileColumns(Reader: TReader);
begin
FTileColumns.ReadData(Reader);
UpdateTileColumns;
end;
procedure TJvListItem.SetBrush(const Value: TBrush);
begin
FBrush.Assign(Value);
end;
procedure TJvListItem.SetFont(const Value: TFont);
begin
FFont.Assign(Value);
end;
procedure TJvListItem.SetGroupId(const Value: Integer);
var
Infos: JvListView.TLVITEMA;
List: TCustomListView;
begin
if FGroupId <> Value then
begin
FGroupId := Value;
List := Owner.Owner;
if Assigned(List) then
begin
ZeroMemory(@Infos, sizeof(Infos));
Infos.mask := LVIF_GROUPID;
Infos.iItem := Index;
Infos.iGroupId := FGroupId;
SendMessage(List.Handle, LVM_SETITEM, 0, LPARAM(@Infos));
end;
end;
end;
procedure TJvListItem.SetPopupMenu(const Value: TPopupMenu);
begin
FPopupMenu := Value;
end;
procedure TJvListItem.SetTileColumns(const Value: TIntegerList);
begin
FTileColumns.Assign(Value);
end;
procedure TJvListItem.TileColumnsChange(Sender: TObject; Item: Integer;
Action: TListNotification);
begin
if not TileColumns.Loading then
UpdateTileColumns;
end;
procedure TJvListItem.UpdateTileColumns;
type
TCardinalArray = array [0..0] of Cardinal;
var
List: TCustomListView;
TileInfos: TLVTILEINFO;
Cols: ^TCardinalArray;
I: Integer;
begin
List := Owner.Owner;
if Assigned(List) then
begin
GetMem(Cols, FTileColumns.Count);
try
for I := 0 to FTileColumns.Count - 1 do
begin
Cols[I] := FTileColumns[I];
end;
ZeroMemory(@TileInfos, SizeOf(TileInfos));
TileInfos.cbSize := SizeOf(TileInfos);
TileInfos.iItem := Index;
TileInfos.cColumns := FTileColumns.Count;
TileInfos.puColumns := PUINT(Cols);
SendMessage(List.Handle, LVM_SETTILEINFO, 0, LPARAM(@TileInfos));
finally
FreeMem(Cols);
end;
end;
end;
procedure TJvListItem.WriteTileColumns(Writer: TWriter);
begin
FTileColumns.WriteData(Writer);
end;
//=== { TJvListItems } =======================================================
procedure TJvListItems.AfterConstruction;
begin
inherited AfterConstruction;
if GetOwner <> nil then
GetOwner.GetInterface(IInterface, FOwnerInterface);
end;
function TJvListItems._AddRef: Integer;
begin
if FOwnerInterface <> nil then
Result := FOwnerInterface._AddRef
else
Result := -1;
end;
function TJvListItems._Release: Integer;
begin
if FOwnerInterface <> nil then
Result := FOwnerInterface._Release
else
Result := -1;
end;
function TJvListItems.QueryInterface(const IID: TGUID; out Obj): HRESULT;
const
E_NOINTERFACE = HRESULT($80004002);
begin
if GetInterface(IID, Obj) then
Result := 0
else
Result := E_NOINTERFACE;
end;
procedure TJvListItems.ReadFromAppStorage(AppStorage: TJvCustomAppStorage; const BasePath: string);
begin
BeginUpdate;
try
Clear;
AppStorage.ReadList(BasePath, Self, ReadListItem, cItem);
finally
EndUpdate;
end;
end;
procedure TJvListItems.WriteToAppStorage(AppStorage: TJvCustomAppStorage; const BasePath: string);
begin
AppStorage.WriteList(BasePath, Self, Count, WriteListItem, DeleteListItem, cItem);
end;
procedure TJvListItems.ReadListItem(Sender: TJvCustomAppStorage;
const Path: string; const List: TObject; const Index: Integer; const ItemName: string);
var
NewItem: TPersistent;
NewPath: string;
begin
if List is TJvListItems then
try
NewPath := Sender.ConcatPaths([Path, ItemName + IntToStr(Index)]);
NewItem := TJvListItems(List).Add;
Sender.ReadPersistent(NewPath, NewItem);
except
end;
end;
procedure TJvListItems.WriteListItem(Sender: TJvCustomAppStorage;
const Path: string; const List: TObject; const Index: Integer; const ItemName: string);
begin
if List is TJvListItems then
if Assigned(TJvListItems(List)[Index]) then
Sender.WritePersistent(Sender.ConcatPaths([Path, ItemName + IntToStr(Index)]), TPersistent(TJvListItems(List)[Index]));
end;
procedure TJvListItems.DeleteListItem(Sender: TJvCustomAppStorage;
const Path: string; const List: TObject; const First, Last: Integer; const ItemName: string);
var
I: Integer;
begin
if List is TJvListItems then
for I := First to Last do
Sender.DeleteValue(Sender.ConcatPaths([Path, ItemName + IntToStr(I)]));
end;
{ TJvListExtendedColumn }
procedure TJvListExtendedColumn.Assign(AValue: TPersistent);
begin
if AValue is TJvListExtendedColumn then
begin
FSortMethod := TJvListExtendedColumn(AValue). SortMethod;
FUseParentSortMethod := TJvListExtendedColumn(AValue).UseParentSortMethod;
FHeaderImagePosition := TJvListExtendedColumn(AValue).HeaderImagePosition;
FUseParentHeaderImagePosition := TJvListExtendedColumn(AValue).UseParentHeaderImagePosition;
end
else
inherited Assign(AValue);
end;
constructor TJvListExtendedColumn.Create(Collection: Classes.TCollection);
begin
inherited Create(Collection);
FSortMethod := smAutomatic;
FUseParentSortMethod := True;
FHeaderImagePosition := hipLeft;
FUseParentHeaderImagePosition := True;
end;
function TJvListExtendedColumn.GetHeaderImagePosition: TJvHeaderImagePosition;
begin
if (TJvListExtendedColumns(Collection).Owner is TJvListView) and UseParentHeaderImagePosition then
Result := TJvListView(TJvListExtendedColumns(Collection).Owner).HeaderImagePosition
else
Result := FHeaderImagePosition;
end;
function TJvListExtendedColumn.GetSortMethod: TJvSortMethod;
begin
if (TJvListExtendedColumns(Collection).Owner is TJvListView) and UseParentSortMethod then
Result := TJvListView(TJvListExtendedColumns(Collection).Owner).SortMethod
else
Result := FSortMethod;
end;
procedure TJvListExtendedColumn.SetHeaderImagePosition(
const Value: TJvHeaderImagePosition);
begin
FHeaderImagePosition := Value;
UseParentHeaderImagePosition := False;
if (TJvListExtendedColumns(Collection).Owner is TJvListView) then
begin
TJvListView(TJvListExtendedColumns(Collection).Owner).DoHeaderImagesChange(Self);
end;
end;
procedure TJvListExtendedColumn.SetSortMethod(
const Value: TJvSortMethod);
begin
FSortMethod := Value;
UseParentSortMethod := False;
end;
procedure TJvListExtendedColumn.SetUseParentHeaderImagePosition(
const Value: Boolean);
begin
if FUseParentHeaderImagePosition <> Value then
begin
FUseParentHeaderImagePosition := Value;
if (TJvListExtendedColumns(Collection).Owner is TJvListView) then
begin
TJvListView(TJvListExtendedColumns(Collection).Owner).DoHeaderImagesChange(Self);
end;
end;
end;
{ TJvListExtendedColumns }
constructor TJvListExtendedColumns.Create(AOwner: TPersistent);
begin
inherited Create(AOwner, TJvListExtendedColumn);
end;
function TJvListExtendedColumns.GetItem(
Index: Integer): TJvListExtendedColumn;
begin
Result := TJvListExtendedColumn(inherited Items[Index]);
end;
function TJvListExtendedColumns.Owner: TPersistent;
begin
Result := GetOwner;
end;
procedure TJvListExtendedColumns.SetItem(Index: Integer;
const Value: TJvListExtendedColumn);
begin
inherited Items[Index] := Value;
end;
//=== { TJvListView } ========================================================
const
cLISTVIEW01 = 'LISTVIEW01';
constructor TJvListView.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
FSortOnClick := True;
FSortMethod := smAutomatic;
FLast := -1;
FInsertMarkColor := clBlack;
FAutoClipboardCopy := True;
FHeaderImagePosition := hipLeft;
FImageChangeLink := TChangeLink.Create;
FImageChangeLink.OnChange := DoHeaderImagesChange;
FAutoSelect := True;
FPicture := TPicture.Create;
FPicture.OnChange := DoPictureChange;
FViewStylesItemBrush := ALL_VIEW_STYLES;
FExtendedColumns := TJvListExtendedColumns.Create(Self);
FSavedExtendedColumns := TJvListExtendedColumns.Create(Self);
FGroups := TJvListViewGroups.Create(Self);
FGroupsProperties := TJvGroupsProperties.Create;
FTileViewProperties := TJvTileViewProperties.Create;
FTileViewProperties.OnChange := TileViewPropertiesChange;
FGroupsProperties.OnChange := GroupsPropertiesChange;
end;
destructor TJvListView.Destroy;
begin
FGroupsProperties.Free;
FTileViewProperties.Free;
FGroups.Free;
FExtendedColumns.Free;
FSavedExtendedColumns.Free;
FImageChangeLink.Free;
FPicture.Free;
inherited Destroy;
end;
procedure TJvListView.WMHScroll(var Msg: TWMHScroll);
begin
inherited;
UpdateHeaderImages(ListView_GetHeader(Handle));
if Assigned(FOnHorizontalScroll) then
FOnHorizontalScroll(Self);
end;
procedure TJvListView.WMVScroll(var Msg: TWMVScroll);
begin
inherited;
UpdateHeaderImages(ListView_GetHeader(Handle));
if Assigned(FOnVerticalScroll) then
FOnVerticalScroll(Self);
end;
procedure TJvListView.ColClick(Column: TListColumn);
type
TParamSort = record
Index: Integer;
Sender: TObject;
end;
var
Parm: TParamSort;
function CustomCompare1(Item1, Item2, ParamSort: Integer): Integer stdcall;
var
Parm: TParamSort;
i1, i2: TListItem;
S1, S2: string;
I: Integer;
SortKind: TJvSortMethod;
function IsBigger(First, Second: string; SortType: TJvSortMethod): Boolean;
var
I, J: Double;
d, e: TDateTime;
a, b: Currency;
l, m: Int64;
st, st2: string;
int1, int2: Integer;
function FirstNonAlpha(Value: string): Integer;
var
Len: Integer;
I, J: Integer;
Comma: Boolean;
begin
Len := Length(Value);
I := 1;
J := 0;
Comma := False;
while I <= Len do
begin
case Value[I] of
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
J := I;
',', '.':
if not Comma then
Comma := True
else
begin
J := I - 1;
I := Len;
end;
else
begin
J := I - 1;
I := Len;
end;
end;
Inc(I);
end;
Result := J;
end;
begin
Result := False;
if Trim(First) = '' then
Result := False
else
if Trim(Second) = '' then
Result := True
else
begin
case SortType of
smAlphabetic:
Result := First > Second;
smNonCaseSensitive:
Result := UpperCase(First) > UpperCase(Second);
smNumeric:
begin
try
VarR8FromStr(First, LOCALE_USER_DEFAULT, 0, I);
VarR8FromStr(Second, LOCALE_USER_DEFAULT, 0, J);
Result := I > J;
except
try
l := StrToInt64(First);
except
l := 0;
end;
try
m := StrToInt64(Second);
except
m := 0;
end;
Result := l > m;
end;
end;
smDate:
begin
d := StrToDate(First);
e := StrToDate(Second);
Result := d > e;
end;
smTime:
begin
d := StrToTime(First);
e := StrToTime(Second);
Result := d > e;
end;
smDateTime:
begin
d := StrToDateTime(First);
e := StrToDateTime(Second);
Result := d > e;
end;
smCurrency:
begin
VarCyFromStr(First, LOCALE_USER_DEFAULT, 0, a);
VarCyFromStr(Second, LOCALE_USER_DEFAULT, 0, b);
Result := a > b;
end;
smAutomatic:
begin
int1 := FirstNonAlpha(First);
int2 := FirstNonAlpha(Second);
if (int1 <> 0) and (int2 <> 0) then
begin
st := Copy(First, 1, int1);
st2 := Copy(Second, 1, int2);
try
Result := StrToFloat(st) > StrToFloat(st2);
except
Result := First > Second;
end;
end
else
Result := First > Second;
end;
end;
end;
end;
begin
Parm := TParamSort(Pointer(ParamSort)^);
i1 := TListItem(Item1);
i2 := TListItem(Item2);
I := Parm.Index;
// (Salvatore)
if Parm.Index < TJvListView(Parm.Sender).ExtendedColumns.Count then
SortKind := TJvListView(Parm.Sender).ExtendedColumns[Parm.Index].SortMethod
else
SortKind := TJvListView(Parm.Sender).SortMethod;
if Assigned(TJvListView(Parm.Sender).OnAutoSort) then
TJvListView(Parm.Sender).OnAutoSort(Parm.Sender, Parm.Index, SortKind);
case I of
{sort by caption}
0:
begin
S1 := i1.Caption;
S2 := i2.Caption;
if IsBigger(S1, S2, SortKind) then
Result := 1
else
if IsBigger(S2, S1, SortKind) then
Result := -1
else
Result := 0;
end;
else
{sort by Column}
begin
if I > i1.SubItems.Count then
begin
if I > i2.SubItems.Count then
Result := 0
else
Result := -1;
end
else
if I > i2.SubItems.Count then
Result := 1
else
begin
S1 := i1.SubItems[I - 1];
S2 := i2.SubItems[I - 1];
if IsBigger(S1, S2, SortKind) then
Result := 1
else
if IsBigger(S2, S1, SortKind) then
Result := -1
else
Result := 0;
end;
end;
end;
end;
function CustomCompare2(Item1, Item2, ParamSort: Integer): Integer; stdcall;
begin
Result := -CustomCompare1(Item1, Item2, ParamSort);
end;
begin
inherited ColClick(Column);
if FSortOnClick then
begin
Parm.Index := Column.Index;
Parm.Sender := Self;
if FLast = Column.Index then
begin
FLast := -1;
CustomSort(TLVCompare(@CustomCompare2), Integer(@Parm));
end
else
begin
FLast := Column.Index;
CustomSort(TLVCompare(@CustomCompare1), Integer(@Parm));
end;
end;
end;
function TJvListView.CreateListItem: TListItem;
begin
Result := TJvListItem.CreateEnh(Items, Self.PopupMenu);
end;
function TJvListView.CreateListItems: TListItems;
begin
Result := TJvListItems.Create(Self);
end;
function TJvListView.GetItemPopup(Node: TListItem): TPopupMenu;
begin
Result := TJvListItem(Node).PopupMenu;
end;
procedure TJvListView.SetItemPopup(Node: TListItem; Value: TPopupMenu);
begin
TJvListItem(Node).PopupMenu := Value;
end;
procedure TJvListView.LoadFromFile(FileName: string);
var
Stream: TFileStream;
begin
Stream := TFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite);
try
LoadFromStream(Stream);
finally
Stream.Free;
end;
end;
// (rom) a 100 char buffer is silly
procedure TJvListView.LoadFromStream(Stream: TStream);
var
Buf: array [0..100] of Char;
Start: Integer;
procedure LoadOldStyle(Stream: TStream);
var
I, J, K: Integer;
Buf: array [0..100] of Byte;
st: string;
ch1, checks: Boolean;
t: TListItem;
begin
I := Stream.Position;
t := nil;
st := '';
Items.Clear;
if Assigned(FOnLoadProgress) then
FOnLoadProgress(Self, 0, Stream.Size - Start);
checks := False;
ch1 := CheckBoxes;
while I < Stream.Size do
begin
J := Stream.Read(Buf, 100);
if Assigned(FOnLoadProgress) then
FOnLoadProgress(Self, J, Stream.Size - Start);
I := I + J;
K := 0;
while K < J do
begin
while (K < J) and (Buf[K] <> 0) and (Buf[K] <> 1) do
begin
st := st + Char(Buf[K]);
Inc(K);
end;
if K < J then
begin
if t <> nil then
t.SubItems.Add(st)
else
begin
t := Items.Add;
checks := checks or (st[1] = 'T');
t.Checked := st[1] = 'T';
st := Copy(st, 2, Length(st));
t.Caption := st;
end;
if Buf[K] = 1 then
t := nil;
st := '';
end;
Inc(K);
end;
end;
if (not ch1) and (not checks) then
CheckBoxes := False;
end;
procedure LoadNewStyle(Stream: TStream);
const
LV_HASCHECKBOXES = $80;
// hs- LV_CHECKED = $8000;
var
Count, I, J: SmallInt;
Options: Byte;
st: string;
t: TListItem;
Buf: array [0..2048] of Char;
begin
try
Self.Items.BeginUpdate;
Self.Items.Clear;
Self.Items.EndUpdate;
Stream.Read(Options, SizeOf(Options));
CheckBoxes := (Options and LV_HASCHECKBOXES) = LV_HASCHECKBOXES;
//Read all lines
while Stream.Position < Stream.Size do
begin
Stream.Read(Count, SizeOf(Count));
//statistics
if Assigned(FOnLoadProgress) then
FOnLoadProgress(Self, Stream.Position, Stream.Size - Start);
//Read all columns
t := Self.Items.Add;
for I := 1 to Count do
begin
// hs-
if I = 1 then
begin
Stream.Read(Options, SizeOf(Options));
if CheckBoxes then
t.Checked := Boolean(Options and Ord(True));
end;
// -hs
(* hs-
Stream.Read(J, SizeOf(I));
-hs *)
Stream.Read(J, SizeOf(J));
//Read the string
FillChar(Buf, SizeOf(Buf), #0);
Stream.Read(Buf, J);
st := Buf;
if I = 1 then
begin
t.Caption := st;
(* hs-
if CheckBoxes then
t.Checked := (I and LV_CHECKED) = LV_CHECKED;
-hs *)
end
else
t.SubItems.Add(st);
end;
end;
except
end;
end;
begin
Start := Stream.Position;
Stream.Read(Buf, 10);
Buf[10] := #0;
if Buf <> cLISTVIEW01 then
begin
Stream.Position := Start;
LoadOldStyle(Stream);
end
else
LoadNewStyle(Stream);
end;
procedure TJvListView.SaveToFile(FileName: string; ForceOldStyle: Boolean);
var
Stream: TFileStream;
begin
Stream := TFileStream.Create(FileName, fmCreate or fmShareExclusive);
try
SaveToStream(Stream, ForceOldStyle);
finally
Stream.Free;
end;
end;
procedure TJvListView.SaveToStream(Stream: TStream; ForceOldStyle: Boolean);
procedure SaveOldStyle(Stream: TStream);
var
I, J, K: Integer;
b, c, d, e: Byte;
st: string;
Buf: array [0..1000] of Byte;
begin
b := 0;
c := 1;
d := Ord('T'); //checked
e := Ord('F'); //not checked
if Assigned(FOnSaveProgress) then
FOnSaveProgress(Self, 0, Self.Items.Count);
for I := 0 to Self.Items.Count - 1 do
begin
if Assigned(FOnSaveProgress) then
FOnSaveProgress(Self, I + 1, Self.Items.Count);
st := Self.Items[I].Caption;
for K := 1 to Length(st) do
Buf[K - 1] := Byte(st[K]);
K := Length(st);
//write checked,not
if Self.Items[I].Checked then
Stream.Write(d, 1)
else
Stream.Write(e, 1);
Stream.Write(Buf, K);
if Self.Items[I].SubItems.Count = 0 then
Stream.Write(c, 1)
else
begin
Stream.Write(b, 1);
for J := 0 to Self.Items[I].SubItems.Count - 2 do
begin
st := Self.Items[I].SubItems[J];
for K := 1 to Length(st) do
Buf[K - 1] := Byte(st[K]);
K := Length(st);
Stream.Write(Buf, K);
Stream.Write(b, 1);
end;
J := Self.Items[I].SubItems.Count - 1;
st := Self.Items[I].SubItems[J];
for K := 1 to Length(st) do
Buf[K - 1] := Byte(st[K]);
K := Length(st);
Stream.Write(Buf, K);
Stream.Write(c, 1);
end;
end;
end;
procedure SaveNewStyle(Stream: TStream);
const
LV_HASCHECKBOXES = $80;
// hs- LV_CHECKED = $8000;
var
Buf: array [0..100] of Char;
// hs- I, J: Word;
I: Integer;
J: SmallInt;
// hs Options : Byte;
Options, IsChecked: Byte;
procedure WriteString(const Txt: string);
var
I: Word;
begin
I := Length(Txt);
Stream.Write(I, SizeOf(I));
if I > 0 then
Stream.Write(Txt[1], I);
end;
begin
Buf := cLISTVIEW01;
Stream.Write(Buf, 10);
if CheckBoxes then
Options := LV_HASCHECKBOXES
else
Options := 0;
Stream.Write(Options, SizeOf(Options));
for I := 0 to Items.Count - 1 do
with Items[I] do
begin
J := SubItems.Count + 1;
Stream.Write(J, SizeOf(J));
// hs-
IsChecked := Options or (Byte(Ord(Checked)));
Stream.Write(IsChecked, SizeOf(IsChecked));
// -hs
WriteString(Items[I].Caption);
for J := 0 to Items[I].SubItems.Count - 1 do
WriteString(SubItems[J]);
end;
end;
begin
if ForceOldStyle then
SaveOldStyle(Stream)
else
SaveNewStyle(Stream);
end;
procedure TJvListView.SaveToStrings(Strings: TStrings; Separator: Char);
var
I, J: Integer;
TmpStr: string;
begin
if Assigned(FOnSaveProgress) then
FOnSaveProgress(Self, 0, Items.Count);
for I := 0 to Items.Count - 1 do
begin
if Assigned(FOnSaveProgress) then
FOnSaveProgress(Self, I + 1, Items.Count);
TmpStr := AnsiQuotedStr(Items[I].Caption, '"');
for J := 0 to Items[I].SubItems.Count - 1 do
TmpStr := TmpStr + Separator + AnsiQuotedStr(Items[I].SubItems[J], '"');
Strings.Add(TmpStr);
end;
end;
procedure TJvListView.LoadFromStrings(Strings: TStrings; Separator: Char);
var
I: Integer;
Start, Stop, TmpStart: PChar;
TmpStr: string;
Li: TListItem;
begin
for I := 0 to Strings.Count - 1 do
begin
Li := nil;
Start := PChar(Strings[I]);
Stop := Start + Length(Strings[I]);
if (Start <> Stop) and (Start <> nil) and (Start^ <> #0) then
begin
if Start^ = '"' then
begin
Li := Items.Add;
TmpStr := AnsiExtractQuotedStr(Start, '"'); // this moves the PChar pointer
Li.Caption := TmpStr;
end
else
begin
TmpStart := Start;
while Start^ <> Separator do
begin
if Start = Stop then
Break;
Inc(Start);
end;
SetString(TmpStr, TmpStart, Start - TmpStart);
Li := Items.Add;
Li.Caption := TmpStr;
end;
end;
if Li <> nil then
begin
while (Start <> Stop) and (Start <> nil) and (Start^ <> #0) do
begin
while Start^ = Separator do
Inc(Start);
if Start^ = '"' then
begin
TmpStr := AnsiExtractQuotedStr(Start, '"'); // this moves the PChar pointer
Li.SubItems.Add(TmpStr);
end
else
begin
TmpStart := Start;
while Start^ <> Separator do
begin
if Start = Stop then
Break;
Inc(Start);
end;
SetString(TmpStr, TmpStart, Start - TmpStart);
Li.SubItems.Add(TmpStr);
end;
end;
end;
end;
end;
procedure TJvListView.LoadFromCSV(FileName: string; Separator: Char);
var
S: TStringList;
begin
S := TStringList.Create;
Items.BeginUpdate;
try
Items.Clear;
S.LoadFromFile(FileName);
LoadFromStrings(S, Separator);
finally
Items.EndUpdate;
S.Free;
end;
end;
procedure TJvListView.SaveToCSV(FileName: string; Separator: Char);
var
S: TStringList;
begin
S := TStringList.Create;
Items.BeginUpdate;
try
SaveToStrings(S, Separator);
S.SaveToFile(FileName);
finally
Items.EndUpdate;
S.Free;
end;
end;
procedure TJvListView.InvertSelection;
var
I: Integer;
begin
Items.BeginUpdate;
for I := 0 to Items.Count - 1 do
Items[I].Selected := not Items[I].Selected;
Items.EndUpdate;
end;
{$IFDEF COMPILER5}
procedure TJvListView.SelectAll;
var
I: Integer;
begin
Items.BeginUpdate;
for I := 0 to Items.Count - 1 do
Items[I].Selected := True;
Items.EndUpdate;
end;
{$ENDIF COMPILER5}
procedure TJvListView.UnselectAll;
var
I: Integer;
begin
Items.BeginUpdate;
for I := 0 to Items.Count - 1 do
Items[I].Selected := False;
Items.EndUpdate;
end;
procedure TJvListView.KeyUp(var Key: Word; Shift: TShiftState);
var
st: string;
I, J: Integer;
begin
inherited KeyUp(Key, Shift);
if AutoClipboardCopy then
if (Key in [Ord('c'), Ord('C')]) and (ssCtrl in Shift) then
begin
for I := 0 to Columns.Count - 1 do
st := st + Columns[I].Caption + Tab;
if st <> '' then
st := st + sLineBreak;
for I := 0 to Items.Count - 1 do
if (SelCount = 0) or Items[I].Selected then
begin
st := st + Items[I].Caption;
for J := 0 to Items[I].SubItems.Count - 1 do
st := st + Tab + Items[I].SubItems[J];
st := st + sLineBreak;
end;
Clipboard.SetTextBuf(PChar(st));
end;
end;
{$IFDEF COMPILER5}
procedure TJvListView.DeleteSelected;
var
I: Integer;
begin
Items.BeginUpdate;
if SelCount = 1 then
begin
I := Selected.Index - 1;
Selected.Delete;
if I = -1 then
I := 0;
if Items.Count > 0 then
Selected := Items[I];
end
else
for I := Items.Count - 1 downto 0 do
if Items[I].Selected then
Items[I].Delete;
Items.EndUpdate;
end;
{$ENDIF COMPILER5}
function TJvListView.GetColumnsOrder: string;
var
Res: array [0..cColumnsHandled - 1] of Integer;
I: Integer;
begin
ListView_GetColumnOrderArray(Columns.Owner.Handle, Columns.Count, @Res[0]);
Result := '';
if Columns.Count > cColumnsHandled then
raise EJvListViewError.CreateRes(@RsETooManyColumns);
for I := 0 to Columns.Count - 1 do
begin
if Result <> '' then
Result := Result + ',';
Result := Result + IntToStr(Res[I]) + '=' + IntToStr(Columns[I].Width);
end;
end;
procedure TJvListView.SetColumnsOrder(const Order: string);
var
Res: array [0..cColumnsHandled - 1] of Integer;
I, J: Integer;
st: string;
begin
FillChar(Res, SizeOf(Res), #0);
with TStringList.Create do
try
CommaText := Order;
I := 0;
while Count > 0 do
begin
st := Strings[0];
J := Pos('=', st);
if (J <> 0) and (I < Columns.Count) then
begin
Columns[I].Width := StrToIntDef(Copy(st, J + 1, Length(st)), Columns[I].Width);
st := Copy(st, 1, J - 1);
end;
Res[I] := StrToIntDef(st, 0);
Delete(0);
Inc(I);
end;
ListView_SetColumnOrderArray(Columns.Owner.Handle, Columns.Count, @Res[0]);
finally
Free;
end;
end;
procedure TJvListView.SetHeaderImages(const Value: TCustomImageList);
begin
if FHeaderImages <> Value then
begin
if FHeaderImages <> nil then
FHeaderImages.UnRegisterChanges(FImageChangeLink);
FHeaderImages := Value;
if Assigned(FHeaderImages) then
begin
FHeaderImages.RegisterChanges(FImageChangeLink);
FHeaderImages.FreeNotification(Self);
end;
UpdateHeaderImages(ListView_GetHeader(Handle));
end;
end;
procedure TJvListView.SetExtendedColumns(
const Value: TJvListExtendedColumns);
begin
FExtendedColumns.Assign(Value);
end;
procedure TJvListView.Notification(AComponent: TComponent;
Operation: TOperation);
var
I: Integer;
begin
inherited Notification(AComponent, Operation);
if Operation = opRemove then
if AComponent = HeaderImages then
HeaderImages := nil
else
if not (csDestroying in ComponentState) and (AComponent is TPopupMenu) then
for I := 0 to Items.Count - 1 do
if TJvListItem(Items[I]).PopupMenu = AComponent then
TJvListItem(Items[I]).PopupMenu := nil;
end;
procedure TJvListView.CreateWnd;
begin
inherited CreateWnd;
UpdateHeaderImages(ListView_GetHeader(Handle));
if FSavedExtendedColumns.Count > 0 then
FExtendedColumns.Assign(FSavedExtendedColumns);
// Get the values from the newly created list view
LoadTileViewProperties;
LoadGroupsProperties;
FInsertMarkColor := SendMessage(Handle, LVM_GETINSERTMARKCOLOR, 0, 0);
// Force a change from True to False so that InsertMarks work correctly.
SendMessage(Handle, LVM_ENABLEGROUPVIEW, Integer(not FGroupView), 0);
SendMessage(Handle, LVM_ENABLEGROUPVIEW, Integer(FGroupView), 0);
end;
procedure TJvListView.UpdateHeaderImages(HeaderHandle: Integer);
//var
// WP: TWindowPlacement;
begin
if (HeaderHandle <> 0) and (ViewStyle = vsReport) and ShowColumnHeaders then
begin
// WP.length := SizeOf(WP);
// GetWindowPlacement(HeaderHandle, @WP);
if HeaderImages <> nil then
begin
Header_SetImageList(HeaderHandle, HeaderImages.Handle);
// WP.rcNormalPosition.Bottom := WP.rcNormalPosition.Top + HeaderImages.Height + 3;
end
else
if ComponentState * [csLoading, csDestroying] = [] then
begin
Header_SetImageList(HeaderHandle, 0);
// WP.rcNormalPosition.Bottom := WP.rcNormalPosition.Top + 17;
end;
// the problem with resizing the header is that there doesn't seem to be an easy way of telling the listview about it...
// SetWindowPlacement(HeaderHandle, @WP);
UpdateColumns;
InvalidateRect(HeaderHandle, nil, True)
end;
end;
procedure TJvListView.DoHeaderImagesChange(Sender: TObject);
begin
UpdateHeaderImages(ListView_GetHeader(Handle));
end;
procedure TJvListView.SetSmallImages(const Value: TCustomImageList);
begin
inherited SmallImages := Value;
UpdateHeaderImages(ListView_GetHeader(Handle));
end;
procedure TJvListView.Loaded;
begin
inherited Loaded;
UpdateHeaderImages(ListView_GetHeader(Handle));
TileViewProperties.DoChange;
end;
procedure TJvListView.WMNCCalcSize(var Msg: TWMNCCalcSize);
//var
// R: TRect;
begin
inherited;
// if Msg.CalcValidRects and Assigned(HeaderImages) and (ViewStyle = vsReport) and ShowColumnHeaders then
// with Msg.CalcSize_Params^.rgrc[0] do
// Top := Top + HeaderImages.Height + 3;
end;
procedure TJvListView.SetBounds(ALeft, ATop, AWidth, AHeight: Integer);
begin
inherited SetBounds(ALeft, ATop, AWidth, AHeight);
if HandleAllocated then
UpdateHeaderImages(ListView_GetHeader(Handle));
end;
procedure TJvListView.InsertItem(Item: TListItem);
begin
inherited InsertItem(Item);
if AutoSelect and (Selected = nil) and (Items.Count < 2) then
PostMessage(Handle, WM_AUTOSELECT, Integer(Item), 1);
end;
procedure TJvListView.WMAutoSelect(var Msg: TMessage);
var
lv: TListItem;
begin
with Msg do
begin
lv := TListItem(WParam);
if Assigned(lv) and (Items.IndexOf(lv) >= 0) and (LParam = 1) then
begin
lv.Selected := True;
lv.Focused := True;
end;
end;
end;
function TJvListView.MoveDown(Index: Integer; Focus: Boolean = True): Integer;
var
lv, lv2: TListItem;
FOnInsert, FOnDeletion: TLVDeletedEvent;
FOnCompare: TLVCompareEvent;
begin
Result := Index;
if (Index >= 0) and (Index < Items.Count) then
begin
lv2 := Items[Index];
FOnInsert := OnInsert;
FOnDeletion := OnDeletion;
FOnCompare := OnCompare;
try
OnInsert := nil;
OnDeletion := nil;
OnCompare := nil;
lv := Items.Insert(Index + 2);
lv.Assign(lv2);
lv2.Delete;
finally
OnInsert := FOnInsert;
OnDeletion := FOnDeletion;
OnCompare := FOnCompare;
end;
if Focus then
begin
lv.Selected := True;
lv.Focused := True;
end;
Result := lv.Index;
end;
end;
function TJvListView.MoveUp(Index: Integer; Focus: Boolean = True): Integer;
var
lv, lv2: TListItem;
FOnInsert, FOnDeletion: TLVDeletedEvent;
FOnCompare: TLVCompareEvent;
begin
Result := Index;
if (Index > 0) and (Index < Items.Count) then
begin
lv2 := Items[Index];
FOnInsert := OnInsert;
FOnDeletion := OnDeletion;
FOnCompare := OnCompare;
try
OnInsert := nil;
OnDeletion := nil;
OnCompare := nil;
lv := Items.Insert(Index - 1);
lv.Assign(lv2);
lv2.Delete;
finally
OnInsert := FOnInsert;
OnDeletion := FOnDeletion;
OnCompare := FOnCompare;
end;
if Focus then
begin
lv.Selected := True;
lv.Focused := True;
end;
Result := lv.Index;
end;
end;
function TJvListView.SelectNextItem(Focus: Boolean = True): Integer;
begin
Result := ItemIndex + 1;
if Result < Items.Count then
ItemIndex := Result;
Result := ItemIndex;
if Focus and (Result >= 0) and (Result < Items.Count) then
begin
Items[Result].Selected := True;
Items[Result].Focused := True;
end;
end;
function TJvListView.SelectPrevItem(Focus: Boolean = True): Integer;
begin
Result := ItemIndex - 1;
if Result >= 0 then
ItemIndex := Result;
Result := ItemIndex;
if Focus and (Result >= 0) and (Result < Items.Count) then
begin
Items[Result].Selected := True;
Items[Result].Focused := True;
end;
end;
procedure TJvListView.SetFocus;
var
Index: Integer;
begin
inherited SetFocus;
Index := 0;
if Assigned(ItemFocused) then
Index := ItemIndex;
if AutoSelect and (Selected = nil) and (Items.Count > 0) then
PostMessage(Handle, WM_AUTOSELECT, Integer(Items[Index]), 1);
end;
function TJvListView.ShowInsertMark(ItemIndex: Integer; Position: TJvInsertMarkPosition): Boolean;
var
Infos: TLVINSERTMARK;
begin
ZeroMemory(@Infos, SizeOf(Infos));
Infos.cbSize := SizeOf(Infos);
Infos.dwFlags := InsertMarkPositionToLVIM[Position];
Infos.iItem := ItemIndex;
Result := Bool(SendMessage(Handle, LVM_SETINSERTMARK, 0, LPARAM(@Infos)));
end;
function TJvListView.HideInsertMark: Boolean;
begin
Result := ShowInsertMark(-1, impBefore);
end;
function TJvListView.GetInsertMarkPosition(const X, Y: Integer;
var ItemIndex: Integer; var Position: TJvInsertMarkPosition): Boolean;
var
Infos: TLVINSERTMARK;
Point: TPoint;
begin
Point.X := X;
Point.Y := Y;
ZeroMemory(@Infos, SizeOf(Infos));
Infos.cbSize := SizeOf(Infos);
Result := Bool(SendMessage(Handle, LVM_INSERTMARKHITTEST, WPARAM(@Point), LPARAM(@Infos)));
if Result then
begin
ItemIndex := Infos.iItem;
if (Infos.dwFlags and LVIM_AFTER) = LVIM_AFTER then
Position := impAfter
else
Position := impBefore;
end;
end;
function TJvListView.IsCustomDrawn(Target: TCustomDrawTarget; Stage: TCustomDrawStage): Boolean;
begin
{ We must custom draw both cdPrePaint and cdPostPaint because without the
cdPostPaint the TListView creates GDI fonts without releasing them. }
Result := inherited IsCustomDrawn(Target, Stage) or
((Stage in [cdPrePaint, cdPostPaint]) and (Picture.Graphic <> nil) and not Picture.Graphic.Empty) or
((Stage in [cdPrePaint, cdPostPaint]) and ((Target = dtItem) or (Target = dtSubItem)));
end;
function TJvListView.CustomDraw(const ARect: TRect; Stage: TCustomDrawStage): Boolean;
var
BmpXPos, BmpYPos: Integer; // X and Y position for bitmap
ItemRect: TRect; // List item bounds rectangle
TopOffset: Integer; // Y pos where bmp drawing starts
Bmp: TBitmap;
function GetHeaderHeight: Integer;
var
Header: HWND; // header window handle
Pl: TWindowPlacement; // header window placement
begin
// Get header window
Header := SendMessage(Handle, LVM_GETHEADER, 0, 0);
// Get header window placement
FillChar(Pl, SizeOf(Pl), 0);
Pl.length := SizeOf(Pl);
GetWindowPlacement(Header, @Pl);
// Calculate header window height
Result := Pl.rcNormalPosition.Bottom - Pl.rcNormalPosition.Top;
end;
begin
Result := inherited CustomDraw(ARect, Stage);
if Result and (Stage = cdPrePaint) and (FPicture <> nil) and (FPicture.Graphic <> nil) and not
FPicture.Graphic.Empty and (FPicture.Graphic.Width > 0) and (FPicture.Graphic.Height > 0) then
begin
Bmp := TBitmap.Create;
try
Bmp.Width := ClientWidth;
Bmp.Height := ClientHeight;
Bmp.Canvas.Brush.Color := Self.Color;
Bmp.Canvas.FillRect(ClientRect);
// Get top offset where drawing starts
if Items.Count > 0 then
begin
ListView_GetItemRect(Handle, 0, ItemRect, LVIR_BOUNDS);
TopOffset := ListView_GetTopIndex(Handle) * (ItemRect.Bottom - ItemRect.Top);
end
else
TopOffset := 0;
if ViewStyle = vsReport then
BmpYPos := ARect.Top - TopOffset + GetHeaderHeight
else
BmpYPos := 0;
// Draw the image
while BmpYPos < ARect.Bottom do
begin
// draw image across width of display
BmpXPos := ARect.Left;
while BmpXPos < ARect.Right do
begin
// DrawIconEx draws alpha-blended icons better (on XP) but gives problems with selecting in the listview
// if Picture.Graphic is TIcon then
// DrawIconEx(Canvas.Handle, BmpXPos, BmpYPos, Picture.Icon.Handle, 0, 0, 0, 0, DI_NORMAL)
// else
Bmp.Canvas.Draw(BmpXPos, BmpYPos, Picture.Graphic);
Inc(BmpXPos, Picture.Graphic.Width);
end;
// move to next row
Inc(BmpYPos, Picture.Graphic.Height);
end;
BitBlt(Canvas, 0, 0, ClientWidth, ClientHeight, Bmp.Canvas, 0, 0, SRCCOPY);
// Ensure that the items are drawn transparently
SetBkMode(Canvas.Handle, TRANSPARENT);
ListView_SetTextBkColor(Handle, CLR_NONE);
ListView_SetBKColor(Handle, CLR_NONE);
finally
Bmp.Free;
end;
end;
end;
function TJvListView.CustomDrawItem(Item: TListItem;
State: TCustomDrawState; Stage: TCustomDrawStage): Boolean;
begin
if (Stage = cdPrePaint) and Assigned(Item) then
begin
Canvas.Font := TJvListItem(Item).Font;
if ViewStyle in ViewStylesItemBrush then
Canvas.Brush := TJvListItem(Item).Brush;
Canvas.Handle;
end;
Result := inherited CustomDrawItem(Item, State, Stage);
end;
procedure TJvListView.CNNotify(var Message: TWMNotify);
begin
with Message do
begin
if NMHdr^.code = NM_CUSTOMDRAW then
begin
with PNMCustomDraw(NMHdr)^ do
begin
if (dwDrawStage and CDDS_SUBITEM <> 0) and
(PNMLVCustomDraw(NMHdr)^.iSubItem = 0) then
begin
// Mantis 3908: For some reason, the inherited handler will not call
// the CustomDrawSubItem if iSubItem is equal to zero. But not calling
// it has the consequence to trigger wrong rendering if the order of
// columns is modified and the list item has a non standard font.
// Calling it ourselves here is not enough as the inherited handler
// does some very specific management with the canvas. So we must
// trick it by changing the value to a recognizable value used
// in our CustomDrawSubItem handler.
PNMLVCustomDraw(NMHdr)^.iSubItem := -1;
inherited;
PNMLVCustomDraw(NMHdr)^.iSubItem := 0;
Exit;
end;
end;
end;
end;
inherited;
end;
function TJvListView.CustomDrawSubItem(Item: TListItem; SubItem: Integer;
State: TCustomDrawState; Stage: TCustomDrawStage): Boolean;
begin
if SubItem = -1 then // See above
SubItem := 0;
if (*(Stage = cdPrePaint) and*) Assigned(Item) then
begin
Canvas.Font := TJvListItem(Item).Font;
if ViewStyle in ViewStylesItemBrush then
Canvas.Brush := TJvListItem(Item).Brush;
end;
Result := inherited CustomDrawSubItem(Item, SubItem, State, Stage);
end;
procedure TJvListView.SetPicture(const Value: TPicture);
begin
FPicture.Assign(Value);
end;
procedure TJvListView.SetGroupView(const Value: Boolean);
begin
if FGroupView <> Value then
begin
FGroupView := Value;
SendMessage(Handle, LVM_ENABLEGROUPVIEW, Integer(FGroupView), 0);
end;
end;
procedure TJvListView.SetGroups(const Value: TJvListViewGroups);
begin
FGroups.Assign(Value);
end;
procedure TJvListView.SetGroupsProperties(const Value: TJvGroupsProperties);
begin
FGroupsProperties.Assign(Value);
end;
procedure TJvListView.SetTileViewProperties(const Value: TJvTileViewProperties);
begin
FTileViewProperties.Assign(Value);
end;
procedure TJvListView.SetInsertMarkColor(const Value: TColor);
begin
if FInsertMarkColor <> Value then
begin
FInsertMarkColor := Value;
SendMessage(Handle, LVM_SETINSERTMARKCOLOR, 0, ColorToRGB(FInsertMarkColor));
end;
end;
procedure TJvListView.SetHeaderImagePosition(const Value: TJvHeaderImagePosition);
begin
if FHeaderImagePosition <> Value then
begin
FHeaderImagePosition := Value;
UpdateHeaderImages(ListView_GetHeader(Handle));
end;
end;
procedure TJvListView.DoPictureChange(Sender: TObject);
begin
// if (Picture.Graphic <> nil) and not Picture.Graphic.Empty then
// Picture.Graphic.Transparent := true;
Invalidate;
end;
procedure TJvListView.LVMDeleteColumn(var Msg: TMessage);
begin
inherited;
// This may happen at design time, especially when migrating
// a project that uses an old version of TJvListView that did
// not have the ExtendedColumns
if Msg.WParam < FExtendedColumns.Count then
FExtendedColumns.Delete(Msg.WParam);
end;
procedure TJvListView.LVMInsertColumn(var Msg: TMessage);
begin
inherited;
FExtendedColumns.Insert(Msg.WParam);
end;
procedure TJvListView.LVMSetColumn(var Msg: TMessage);
var
i: Integer;
Column: tagLVCOLUMNA;
begin
inherited;
if not FSettingHeaderImagePosition then
begin
for i := 0 to ExtendedColumns.Count - 1 do
begin
if ExtendedColumns[i].GetHeaderImagePosition = hipRight then
begin
Column.mask := LVCF_FMT;
ListView_GetColumn(Handle, i, Column);
if Column.fmt and LVCFMT_IMAGE <> 0 then
begin
Column.fmt := Column.fmt or LVCFMT_BITMAP_ON_RIGHT;
FSettingHeaderImagePosition := True;
try
ListView_SetColumn(Handle, i, Column);
finally
FSettingHeaderImagePosition := False;
end;
end;
end;
end;
end;
end;
procedure TJvListView.DestroyWnd;
begin
FSavedExtendedColumns.Assign(FExtendedColumns);
inherited DestroyWnd;
end;
{$IFDEF COMPILER5}
function TJvListView.GetItemIndex: Integer;
begin
if Selected <> nil then
Result := Selected.Index
else
Result := -1;
end;
procedure TJvListView.SetItemIndex(const Value: Integer);
begin
if (Value >= 0) and (Value < Items.Count) then
Items[Value].Selected := True;
end;
{$ENDIF COMPILER5}
procedure TJvListView.SetViewStylesItemBrush(const Value: TJvViewStyles);
begin
FViewStylesItemBrush := Value;
Invalidate;
end;
{$IFDEF COMPILER6_UP}
procedure TJvListView.SetViewStyle(Value: TViewStyle);
begin
// If someone is setting the view style via an ancestor class reference,
// we force it to be set through our setter. But if it's set via our setter
// then we inform the ancestor class' code so that display is updated.
if not FSettingJvViewStyle then
SetJvViewStyle(TJvViewStyle(Value))
else
inherited SetViewStyle(Value);
end;
{$ENDIF COMPILER6_UP}
procedure TJvListView.SetJvViewStyle(Value: TJvViewStyle);
begin
if Value <> FViewStyle then
begin
FSettingJvViewStyle := True;
try
FViewStyle := Value;
if Value = vsTile then
begin
if HandleAllocated then
begin
SendMessage(Handle, LVM_SETVIEW, LV_VIEW_TILE, 0);
end;
end
else
begin
inherited ViewStyle := TViewStyle(Value);
end;
finally
FSettingJvViewStyle := False;
end;
end;
end;
function TJvListView.DoCompareGroups(Group1, Group2: TJvListViewGroup): Integer;
begin
if Assigned(OnCompareGroups) then
OnCompareGroups(Self, Group1, Group2, Result)
else
Result := Group2.GroupId - Group1.GroupId;
end;
procedure TJvListView.TileViewPropertiesChange(Sender: TObject);
var
Infos: TLVTILEVIEWINFO;
begin
if not (csLoading in ComponentState) then
begin
Infos.cbSize := SizeOf(Infos);
Infos.dwMask := LVTVIM_TILESIZE or LVTVIM_COLUMNS or LVTVIM_LABELMARGIN;
Infos.dwFlags := TileSizeKindToLVTVIF[TileViewProperties.TileSizeKind];
TileViewProperties.TileSize.CopyToSize(Infos.sizeTile);
infos.cLines := TileViewProperties.SubLinesCount;
TileViewProperties.LabelMargin.CopyToRect(infos.rcLabelMargin);
SendMessage(Handle, LVM_SETTILEVIEWINFO, 0, LPARAM(@Infos));
end;
end;
procedure TJvListView.GroupsPropertiesChange(Sender: TObject);
var
Infos: TLVGROUPMETRICS;
begin
if not (csLoading in ComponentState) then
begin
ZeroMemory(@Infos, SizeOf(Infos));
Infos.cbSize := SizeOf(Infos);
Infos.mask := LVGMF_BORDERSIZE or LVGMF_BORDERCOLOR or LVGMF_TEXTCOLOR;
Infos.Top := GroupsProperties.BorderSize.Top;
Infos.Left := GroupsProperties.BorderSize.Left;
Infos.Bottom := GroupsProperties.BorderSize.Bottom;
Infos.Right := GroupsProperties.BorderSize.Right;
Infos.crTop := GroupsProperties.BorderColor.Top;
Infos.crLeft := GroupsProperties.BorderColor.Left;
Infos.crBottom := GroupsProperties.BorderColor.Bottom;
Infos.crRight := GroupsProperties.BorderColor.Right;
Infos.crHeader := GroupsProperties.HeaderColor;
SendMessage(Handle, LVM_SETGROUPMETRICS, 0, LPARAM(@Infos));
end;
end;
procedure TJvListView.LoadTileViewProperties;
begin
TileViewProperties.LoadFromList(Self);
end;
procedure TJvListView.LoadGroupsProperties;
begin
GroupsProperties.LoadFromList(Self);
end;
{ TJvListViewGroup }
procedure TJvListViewGroup.Assign(AValue: TPersistent);
var
Source: TJvListViewGroup;
begin
if AValue is TJvListViewGroup then
begin
Source := AValue as TJvListViewGroup;
FHeader := Source.Header;
FHeaderAlignment := Source.HeaderAlignment;
FGroupId := Source.GroupId;
UpdateGroupProperties;
end;
end;
constructor TJvListViewGroup.Create(Collection: Classes.TCollection);
begin
// Before inherited for Notify to acces it
FGroupId := -1;
FHeaderAlignment := taLeftJustify;
FHeader := 'Group';
inherited Create(Collection);
end;
destructor TJvListViewGroup.Destroy;
begin
inherited Destroy;
end;
procedure TJvListViewGroup.SetHeader(const Value: WideString);
var
SavedGroupId: Integer;
begin
if FHeader <> Value then
begin
FHeader := Value;
// Due to a undocumented bug/feature in the list view, one has to change
// the GroupId as well when changing the caption or the modification is
// not taken into account.
SavedGroupId := GroupId;
UpdateGroupProperties(MaxInt);
FGroupId := MaxInt;
UpdateGroupProperties(SavedGroupId);
FGroupId := SavedGroupId;
end;
end;
procedure TJvListViewGroup.SetHeaderAlignment(const Value: TAlignment);
begin
if FHeaderAlignment <> Value then
begin
FHeaderAlignment := Value;
UpdateGroupProperties;
end;
end;
procedure TJvListViewGroup.SetLVGROUP(var GroupInfo: TLVGROUP);
begin
ZeroMemory(@GroupInfo, sizeof(GroupInfo));
GroupInfo.cbSize := sizeof(GroupInfo);
GroupInfo.mask := LVGF_HEADER or LVGF_ALIGN or LVGF_GROUPID;
GroupInfo.iGroupId := FGroupId;
GroupInfo.pszHeader := PWideChar(FHeader);
GroupInfo.cchHeader := Length(FHeader);
GroupInfo.uAlign := AlignmentToLVGA[HeaderAlignment];
end;
procedure TJvListViewGroup.SetGroupId(const Value: Integer);
begin
if FGroupId <> Value then
begin
UpdateGroupProperties(Value);
FGroupId := Value;
end;
end;
procedure TJvListViewGroup.UpdateGroupProperties(const NewGroupId: Integer = -1);
var
GroupInfo: TLVGROUP;
List: TJvListView;
begin
List := (Collection as TJvListViewGroups).ParentList;
if Assigned(List) then
begin
SetLVGROUP(GroupInfo);
if NewGroupId <> -1 then
GroupInfo.iGroupId := NewGroupId;
SendMessage(List.Handle, LVM_SETGROUPINFO, FGroupId, LPARAM(@GroupInfo));
List.Invalidate;
end;
end;
{ TJvListViewGroups }
function TJvListViewGroups.Compare(Id1, Id2: Integer): Integer;
var
List: TJvListView;
begin
Result := Id2 - Id1;
List := ParentList;
if Assigned(List) then
begin
Result := List.DoCompareGroups(ItemsById[Id1], ItemsById[Id2]);
end;
end;
constructor TJvListViewGroups.Create(AOwner: TPersistent);
begin
inherited Create(AOwner, TJvListViewGroup);
end;
function TJvListViewGroups.GetItem(Index: Integer): TJvListViewGroup;
begin
Result := inherited Items[Index] as TJvListViewGroup;
end;
function TJvListViewGroups.GetItemById(GroupId: Integer): TJvListViewGroup;
var
I: Integer;
begin
Result := nil;
I := 0;
while (I < Count) and not Assigned(Result) do
begin
if Items[I].GroupId = GroupId then
Result := Items[I];
Inc(I);
end;
end;
function LVGroupCompare(Group1_ID: Integer; Group2_ID: Integer; pvData: Pointer): Integer; stdcall;
begin
Result := TJvListViewGroups(pvData).Compare(Group1_ID, Group2_ID);
end;
procedure TJvListViewGroups.InsertGroupIntoList(group: TJvListViewGroup);
var
List: TJvListView;
GroupInfo: TLVGROUP;
GroupSortedInfo: TLVINSERTGROUPSORTED;
begin
List := ParentList;
if Assigned(List) then
begin
if group.GroupId = -1 then
group.FGroupId := Count;
if Sorted then
begin
GroupSortedInfo.pfnGroupCompare := @LVGroupCompare;
GroupSortedInfo.pvData := Self;
group.SetLVGROUP(GroupSortedInfo.lvGroup);
SendMessage(List.Handle, LVM_INSERTGROUPSORTED, WPARAM(@GroupSortedInfo), 0);
end
else
begin
group.SetLVGROUP(GroupInfo);
SendMessage(List.Handle, LVM_INSERTGROUP, group.Index, LPARAM(@GroupInfo));
end;
end;
end;
procedure TJvListViewGroups.Notify(Item: TCollectionItem;
Action: TCollectionNotification);
begin
case Action of
cnAdded:
InsertGroupIntoList(Item as TJvListViewGroup);
cnDeleting:
RemoveGroupFromList(Item as TJvListViewGroup);
end;
end;
function TJvListViewGroups.ParentList: TJvListView;
var
Owner: TPersistent;
begin
Result := nil;
Owner := GetOwner;
if Owner is TJvListView then
Result := Owner as TJvListView;
end;
procedure TJvListViewGroups.RemoveGroupFromList(group: TJvListViewGroup);
var
List: TJvListView;
begin
List := ParentList;
if Assigned(List) then
begin
SendMessage(List.Handle, LVM_REMOVEGROUP, group.GroupId, 0);
end;
end;
procedure TJvListViewGroups.SetItem(Index: Integer;
const Value: TJvListViewGroup);
begin
inherited Items[Index] := Value;
end;
procedure TJvListViewGroups.SetSorted(const Value: Boolean);
begin
if FSorted <> Value then
begin
FSorted := Value;
if FSorted then
Sort;
end;
end;
procedure TJvListViewGroups.Sort;
var
List: TJvListView;
begin
List := ParentList;
if Assigned(List) then
begin
SendMessage(List.Handle, LVM_SORTGROUPS, WPARAM(@LVGroupCompare), LPARAM(Self));
end;
end;
{ TJvTileViewProperties }
constructor TJvTileViewProperties.Create;
begin
inherited Create;
FLabelMargin := TJvRect.Create;
FTileSize := TJvSize.Create;
FLabelMargin.OnChange := LabelMarginChange;
FTileSize.OnChange := TileSizeChange;
FSubLinesCount := 1;
FTileSizeKind := tskAutoSize;
end;
destructor TJvTileViewProperties.Destroy;
begin
FTileSize.Free;
FLabelMargin.Free;
inherited Destroy;
end;
procedure TJvTileViewProperties.DoChange;
begin
if not FLoading and Assigned(OnChange) then
OnChange(Self);
end;
procedure TJvTileViewProperties.LabelMarginChange(Sender: TObject);
begin
DoChange;
end;
procedure TJvTileViewProperties.LoadFromList(List: TCustomListView);
var
Infos: TLVTILEVIEWINFO;
begin
if not (csDesigning in List.ComponentState) then
begin
Infos.cbSize := SizeOf(Infos);
Infos.dwMask := LVTVIM_TILESIZE or LVTVIM_COLUMNS or LVTVIM_LABELMARGIN;
SendMessage(List.Handle, LVM_GETTILEVIEWINFO, 0, LPARAM(@Infos));
FLoading := True;
try
case Infos.dwFlags of
LVTVIF_FIXEDHEIGHT:
FTileSizeKind := tskFixedHeight;
LVTVIF_FIXEDWIDTH:
FTileSizeKind := tskFixedWidth;
LVTVIF_FIXEDSIZE:
FTileSizeKind := tskFixedSize;
else
FTileSizeKind := tskAutoSize;
end;
TileSize.Assign(Infos.sizeTile);
FSubLinesCount := infos.cLines;
LabelMargin.Assign(infos.rcLabelMargin);
finally
FLoading := False;
end;
end;
end;
procedure TJvTileViewProperties.SetLabelMargin(const Value: TJvRect);
begin
FLabelMargin.Assign(Value);
end;
procedure TJvTileViewProperties.SetSubLinesCount(const Value: Integer);
begin
FSubLinesCount := Value;
DoChange;
end;
procedure TJvTileViewProperties.SetTileSize(const Value: TJvSize);
begin
FTileSize.Assign(Value);
end;
procedure TJvTileViewProperties.SetTileSizeKind(const Value: TJvTileSizeKind);
begin
FTileSizeKind := Value;
DoChange;
end;
procedure TJvTileViewProperties.TileSizeChange(Sender: TObject);
begin
DoChange;
end;
{ TJvGroupProperties }
procedure TJvGroupsProperties.BorderColorChange(Sender: TObject);
begin
DoChange;
end;
procedure TJvGroupsProperties.BorderSizeChange(Sender: TObject);
begin
DoChange;
end;
constructor TJvGroupsProperties.Create;
begin
inherited Create;
FBorderSize := TJvGroupsPropertiesBorderRect.Create;
FBorderColor := TJvGroupsPropertiesBorderColors.Create;
FBorderSize.OnChange := BorderSizeChange;
FBorderColor.OnChange := BorderColorChange;
end;
destructor TJvGroupsProperties.Destroy;
begin
FBorderSize.Free;
FBorderColor.Free;
inherited Destroy;
end;
procedure TJvGroupsProperties.DoChange;
begin
if not FLoading and Assigned(OnChange) then
OnChange(Self);
end;
procedure TJvGroupsProperties.LoadFromList(List: TCustomListView);
var
Infos: TLVGROUPMETRICS;
begin
if not (csDesigning in List.ComponentState) then
begin
ZeroMemory(@Infos, SizeOf(Infos));
Infos.cbSize := SizeOf(Infos);
Infos.mask := LVGMF_BORDERSIZE or LVGMF_BORDERCOLOR or LVGMF_TEXTCOLOR;
SendMessage(List.Handle, LVM_GETGROUPMETRICS, 0, LPARAM(@Infos));
FLoading := True;
try
BorderSize.Top := Infos.Top;
BorderSize.Left := Infos.Left;
BorderSize.Bottom := Infos.Bottom;
BorderSize.Right := Infos.Right;
BorderColor.Top := Infos.crTop and $00FFFFFF;
BorderColor.Left := Infos.crLeft and $00FFFFFF;
BorderColor.Bottom := Infos.crBottom and $00FFFFFF;
BorderColor.Right := Infos.crRight and $00FFFFFF;
HeaderColor := Infos.crHeader and $00FFFFFF;
finally
FLoading := False;
end;
end;
end;
procedure TJvGroupsProperties.SetBorderColor(const Value: TJvGroupsPropertiesBorderColors);
begin
FBorderColor.Assign(Value);
end;
procedure TJvGroupsProperties.SetBorderSize(const Value: TJvGroupsPropertiesBorderRect);
begin
FBorderSize.Assign(Value);
end;
procedure TJvGroupsProperties.SetHeaderColor(const Value: TColor);
begin
if FHeaderColor <> Value then
begin
FHeaderColor := Value;
DoChange;
end;
end;
{ TJvGroupsPropertiesBorderRect }
constructor TJvGroupsPropertiesBorderRect.Create;
begin
inherited Create;
Top := 12;
end;
{ TJvGroupsPropertiesBorderColors }
procedure TJvGroupsPropertiesBorderColors.Assign(Source: TPersistent);
var
SourceColors: TJvGroupsPropertiesBorderColors;
begin
if Source is TJvGroupsPropertiesBorderColors then
begin
SourceColors := Source as TJvGroupsPropertiesBorderColors;
FTop := SourceColors.Top;
FLeft := SourceColors.Left;
FBottom := SourceColors.Bottom;
FRight := SourceColors.Right;
DoChange;
end
else
begin
inherited Assign(Source);
end;
end;
constructor TJvGroupsPropertiesBorderColors.Create;
begin
inherited Create;
Top := $C8D0D4;
Left := clWhite;
Bottom := clWhite;
Right := clWhite;
end;
procedure TJvGroupsPropertiesBorderColors.DoChange;
begin
if Assigned(OnChange) then
OnChange(Self);
end;
procedure TJvGroupsPropertiesBorderColors.SetBottom(const Value: TColor);
begin
if FBottom <> Value then
begin
FBottom := Value;
DoChange;
end;
end;
procedure TJvGroupsPropertiesBorderColors.SetLeft(const Value: TColor);
begin
if FLeft <> Value then
begin
FLeft := Value;
DoChange;
end;
end;
procedure TJvGroupsPropertiesBorderColors.SetRight(const Value: TColor);
begin
if FRight <> Value then
begin
FRight := Value;
DoChange;
end;
end;
procedure TJvGroupsPropertiesBorderColors.SetTop(const Value: TColor);
begin
if FTop <> Value then
begin
FTop := Value;
DoChange;
end;
end;
{$IFDEF UNITVERSIONING}
initialization
RegisterUnitVersion(HInstance, UnitVersioning);
finalization
UnregisterUnitVersion(HInstance);
{$ENDIF UNITVERSIONING}
end.