home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Chip 2001 September
/
Chip_2001-09_cd1.bin
/
zkuste
/
delphi
/
kolekce
/
d123456
/
DFS.ZIP
/
EnhListView.pas
< prev
next >
Wrap
Pascal/Delphi Source File
|
2001-06-28
|
81KB
|
2,573 lines
{$I DFS.INC} { Standard defines for all Delphi Free Stuff components }
// Delphi 2 and C++B 1 have incorrectly declared InsertItem as private.
{$IFDEF DFS_COMPILER_3_UP}
{$DEFINE DFS_FIXED_LIST_VIEW}
{$ENDIF}
{.$DEFINE DFS_DEBUG}
{------------------------------------------------------------------------------}
{ TdfsEnhListView v3.72 }
{------------------------------------------------------------------------------}
{ A list view control that provides enhanced functionality beyond the }
{ standard list view. For example, automatic sorting of simple data types, }
{ owner draw event for vsReport mode, and more. This does NOT require any }
{ special version of COMCTL32.DLL. }
{ }
{ Copyright 1998-2001, Brad Stowers. All Rights Reserved. }
{ }
{ Copyright: }
{ All Delphi Free Stuff (hereafter "DFS") source code is copyrighted by }
{ Bradley D. Stowers (hereafter "author"), and shall remain the exclusive }
{ property of the author. }
{ }
{ Distribution Rights: }
{ You are granted a non-exlusive, royalty-free right to produce and distribute }
{ compiled binary files (executables, DLLs, etc.) that are built with any of }
{ the DFS source code unless specifically stated otherwise. }
{ You are further granted permission to redistribute any of the DFS source }
{ code in source code form, provided that the original archive as found on the }
{ DFS web site (http://www.delphifreestuff.com) is distributed unmodified. For }
{ example, if you create a descendant of TDFSColorButton, you must include in }
{ the distribution package the colorbtn.zip file in the exact form that you }
{ downloaded it from http://www.delphifreestuff.com/mine/files/colorbtn.zip. }
{ }
{ Restrictions: }
{ Without the express written consent of the author, you may not: }
{ * Distribute modified versions of any DFS source code by itself. You must }
{ include the original archive as you found it at the DFS site. }
{ * Sell or lease any portion of DFS source code. You are, of course, free }
{ to sell any of your own original code that works with, enhances, etc. }
{ DFS source code. }
{ * Distribute DFS source code for profit. }
{ }
{ Warranty: }
{ There is absolutely no warranty of any kind whatsoever with any of the DFS }
{ source code (hereafter "software"). The software is provided to you "AS-IS", }
{ and all risks and losses associated with it's use are assumed by you. In no }
{ event shall the author of the softare, Bradley D. Stowers, be held }
{ accountable for any damages or losses that may occur from use or misuse of }
{ the software. }
{ }
{ Support: }
{ Support is provided via the DFS Support Forum, which is a web-based message }
{ system. You can find it at http://www.delphifreestuff.com/discus/ }
{ All DFS source code is provided free of charge. As such, I can not guarantee }
{ any support whatsoever. While I do try to answer all questions that I }
{ receive, and address all problems that are reported to me, you must }
{ understand that I simply can not guarantee that this will always be so. }
{ }
{ Clarifications: }
{ If you need any further information, please feel free to contact me directly.}
{ This agreement can be found online at my site in the "Miscellaneous" section.}
{------------------------------------------------------------------------------}
{ The lateset version of my components are always available on the web at: }
{ http://www.delphifreestuff.com/ }
{ See ELV.txt for notes, known issues, and revision history. }
{------------------------------------------------------------------------------}
{ Date last modified: June 28, 2001 }
{------------------------------------------------------------------------------}
unit EnhListView;
interface
{$IFNDEF DFS_WIN32}
ERROR! This unit only available for Delphi 2.0 or higher!!!
{$ENDIF}
uses
Forms, Windows, Messages, Classes, Controls, ComCtrls, CommCtrl, SysUtils,
{$IFDEF DFS_COMPILER_4_UP} ImgList, {$ENDIF} Graphics, StdCtrls, Menus;
const
{ This shuts up C++Builder 3 about the redefiniton being different. There
seems to be no equivalent in C1. Sorry. }
{$IFDEF DFS_CPPB_3_UP}
{$EXTERNALSYM DFS_COMPONENT_VERSION}
{$ENDIF}
DFS_COMPONENT_VERSION = 'TdfsEnhListView v3.72';
DRAWTEXTEX_FLAGS = DT_NOPREFIX or DT_SINGLELINE or DT_VCENTER or
DT_END_ELLIPSIS;
DRAWTEXTEX_ALIGNMENT: array[TAlignment] of UINT = (DT_LEFT, DT_RIGHT,
DT_CENTER);
WM_OWNERDRAWCOLUMNS = WM_USER + 143;
type
TIntArray = array[0..(MaxInt div SizeOf(Integer)-1)] of Integer;
PIntArray = ^TIntArray;
TResizeMethod = (rmFitText, rmFitHeader);
TAutoColumnSort = (acsNoSort, acsSort, acsSortToggle);
TAutoSortStyle = (assSmart, assDefault);
TSortAs = (saNone, saString, saNumeric, saDateTime);
TLVStyle = (lvStandard, lvOwnerDrawFixed);
TLVHDrawItemEvent = procedure(Control: TWinControl; var ACanvas: TCanvas;
Index: Integer; var ARect: TRect; Selected: boolean;
var DefaultDrawing: boolean) of object;
TLVMeasureItemEvent = procedure(Control: TWinControl;
var AHeight: UINT) of object;
TLVDrawItemEvent = procedure(Control: TWinControl; var ACanvas: TCanvas;
Index: Integer; ARect: TRect; State: TOwnerDrawState;
var DefaultDrawing, FullRowSelect: boolean) of object;
TLVDrawSubItemEvent = procedure(Control: TWinControl; var ACanvas: TCanvas;
Index, SubItem: Integer; ARect: TRect; State: TOwnerDrawState;
var DefaultDrawing: boolean) of object;
TLVAfterDrawItemEvent = procedure(Control: TWinControl; var ACanvas: TCanvas;
Index: Integer; ARect: TRect; State: TOwnerDrawState) of object;
TLVSortItemsEvent = procedure(Sender: TObject; Item1, Item2: TListItem;
SortColumn: integer; var SortAs: TSortAs; var CompResult: integer) of object;
TLVSortStatusEvent = procedure(Sender: TObject; SortColumn: integer;
Ascending: boolean) of object;
TLVEditCanceled = procedure(Sender: TObject; Item: TListItem) of object;
{$IFNDEF DFS_COMPILER_4_UP}
TLVNotifyEvent = procedure(Sender: TObject; Item: TListItem) of object;
{$ENDIF}
// Class for saved settings
TdfsEnhLVSaveSettings = class(TPersistent)
private
FAutoSave: boolean;
FRegistryKey: string;
FSaveColumnSizes: boolean;
FSaveCurrentSort: boolean;
FSaveViewStyle: boolean;
public
constructor Create; virtual;
procedure StoreColumnSizes(ColCount: integer;
const IntArray: array of integer);
procedure ReadColumnSizes(ColCount: integer;
var IntArray: array of integer);
procedure StoreCurrentSort(Ascending: boolean; SortCol: integer);
procedure ReadCurrentSort(var Ascending: boolean; var SortCol: integer);
procedure StoreViewStyle(Style: TViewStyle);
function ReadViewStyle(Default: TViewStyle): TViewStyle;
published
property AutoSave: boolean read FAutoSave write FAutoSave default FALSE;
property RegistryKey: string read FRegistryKey write FRegistryKey;
property SaveColumnSizes: boolean
read FSaveColumnSizes
write FSaveColumnSizes
default TRUE;
property SaveCurrentSort: boolean
read FSaveCurrentSort
write FSaveCurrentSort
default TRUE;
property SaveViewStyle: boolean
read FSaveViewStyle
write FSaveViewStyle
default TRUE;
end;
{ The new class }
TCustomEnhListView = class(TCustomListView)
private
FSortDirty: boolean;
FUpdateCount: integer;
FStyle: TLVStyle;
FAutoColumnSort: TAutoColumnSort;
FAutoSortStyle: TAutoSortStyle;
FAutoResort: boolean;
FAutoSortAscending: boolean;
FTmpAutoSortAscending: boolean;
FLastColumnClicked: Integer;
FSaveSettings: TdfsEnhLVSaveSettings;
FShowSortArrows: boolean;
FReverseSortArrows: boolean;
FSortUpBmp,
FSortDownBmp: TBitmap;
FCreatingWindowHandle: boolean;
{$IFDEF BACKGROUND_FIXED}
FBackgroundImage: TBitmap;
{$ENDIF}
FNoColumnResize: boolean;
FOldHeaderWndProc: pointer;
FHeaderInstance: pointer;
FSearchStr: string;
FSearchTickCount: Double;
FColumnSearch: boolean;
FOnSortBegin: TLVSortStatusEvent;
FOnSortFinished: TLVSortStatusEvent;
FOnMeasureItem: TLVMeasureItemEvent;
FOnDrawItem: TLVDrawItemEvent;
FOnDrawSubItem: TLVDrawSubItemEvent;
FOnAfterDefaultDrawItem: TLVAfterDrawItemEvent;
FOnDrawHeader: TLVHDrawItemEvent;
FOnSortItems: TLVSortItemsEvent;
FOnEditCanceled: TLVEditCanceled;
{$IFNDEF DFS_COMPILER_4_UP}
FOnGetImageIndex: TLVNotifyEvent;
{$ENDIF}
procedure HeaderWndProc(var Message: TMessage);
{ Message handlers }
procedure CMSysColorChange(var Message: TWMSysColorChange);
message CM_SYSCOLORCHANGE;
procedure CMFontChanged(var Messsage: TMessage); message CM_FONTCHANGED;
procedure CNMeasureItem(var Message: TWMMeasureItem); message CN_MEASUREITEM;
procedure CNDrawItem(var Message: TWMDrawItem); message CN_DRAWITEM;
procedure CNNotify(var Message: TWMNotify); message CN_NOTIFY;
procedure WMDestroy(var Message: TWMDestroy); message WM_DESTROY;
procedure WMDrawHeader(var Message: TWMDrawItem); message WM_DRAWITEM;
procedure WMNotify(var Message: TWMNotify); message WM_NOTIFY;
procedure WMOwnerDrawColumns(var Message: TMessage);
message WM_OWNERDRAWCOLUMNS;
procedure WMParentNotify(var Message: TWMParentNotify); message WM_PARENTNOTIFY;
protected
{ USE WITH CARE. This can be NIL }
FCanvas: TCanvas;
FHeaderHandle: HWND;
procedure KeyUp(var Key: Word; Shift: TShiftState); override;
procedure Notification(AComponent: TComponent; Operation: TOperation); override;
{$IFNDEF DFS_COMPILER_4_UP}
function GetItem(Value: TLVItem): TListItem;
{$ENDIF}
procedure ResetOwnerDrawHeight;
procedure InvalidateColumnHeader(Index: integer); virtual;
procedure DoSort(ColumnIndex:integer; Descending: boolean); virtual;
procedure SortBegin(ColumnIndex: integer; Ascending: boolean); virtual;
procedure SortFinished(ColumnIndex: integer; Ascending: boolean); virtual;
procedure SortItems(const Item1, Item2: TListItem; SortColumn: integer;
var CompResult: integer); virtual;
procedure MeasureItem(var Height: UINT); virtual;
procedure DefaultDrawItem(Index: Integer; Rect: TRect;
State: TOwnerDrawState; FullRowSelect: boolean); virtual;
procedure DefaultDrawSubItem(Index, SubItem: integer; Rect: TRect;
State: TOwnerDrawState); virtual;
procedure ProcessDrawItemMsg(Index: Integer;
Rect: TRect; State: TOwnerDrawState; var DefaultDrawing,
FullRowSelect: boolean); virtual;
function ActualColumnIndex(Index: integer): integer; virtual;
function GetActualColumn(Index: integer): TListColumn; virtual;
function GetSubItemText(Index, SubItem: integer): string; virtual;
procedure DrawSubItem(Index, SubItem: Integer; Rect: TRect;
State: TOwnerDrawState; var DefaultDrawing: boolean); virtual;
procedure DrawItem(var Canvas: TCanvas; Index: Integer; Rect: TRect;
State: TOwnerDrawState; var DefaultDrawing,
FullRowSelect: boolean);
{$IFDEF DFS_COMPILER_4_UP} reintroduce; overload; {$ENDIF} virtual;
procedure AfterDrawItem(var Canvas: TCanvas; Index: Integer;
Rect: TRect; State: TOwnerDrawState); virtual;
procedure Edit(const Item: TLVItem); override;
procedure EditCanceled(const Item: TLVItem); virtual;
{ Overriden ancestor methods }
procedure ColClick(Column: TListColumn); override;
{$IFDEF DFS_FIXED_LIST_VIEW}
procedure InsertItem(Item: TListItem); override;
{$ENDIF}
procedure KeyDown(var Key: Word; Shift: TShiftState); override;
procedure CreateWnd; override;
procedure DestroyWnd; override;
procedure ProcessDrawHeaderMsg(Index: Integer; Rect: TRect;
State: TOwnerDrawState; var DefaultDrawing: boolean); virtual;
procedure DrawHeader(var Canvas: TCanvas; Index: Integer; var Rect: TRect;
Selected: boolean; var DefaultDrawing: boolean); virtual;
procedure DefaultDrawHeader(var Canvas: TCanvas; Index: Integer;
var Rect: TRect; Selected: boolean); virtual;
procedure SetOnDrawHeader(Value: TLVHDrawItemEvent); virtual;
procedure SetColumnsOwnerDrawFlag(OwnerDrawn: boolean); virtual;
procedure CreateSortBmps(var UpBmp, DownBmp: TBitmap); virtual;
{$IFNDEF DFS_COMPILER_4_UP}
procedure GetImageIndex(Item: TListItem); virtual;
{$ENDIF}
{$IFDEF BACKGROUND_FIXED}
procedure BackgroundImageChanged(Sender: TObject); virtual;
{$ENDIF}
{ Property methods }
procedure SetAutoColumnSort(Value: TAutoColumnSort);
procedure SetAutoSortStyle(Value: TAutoSortStyle);
procedure SetCurrentSortAscending(Value: boolean);
procedure SetAutoSortAscending(Value: boolean);
procedure SetStyle(Value: TLVStyle);
procedure SetShowSortArrows(Value: boolean);
procedure SetReverseSortArrows(Value: boolean);
procedure SetLastColumnClicked(Value: integer);
procedure SetAutoResort(Value: boolean);
{$IFDEF BACKGROUND_FIXED}
procedure SetBackgroundImage(const Value: TBitmap);
{$ENDIF}
function GetSmallImages: {$IFDEF DFS_COMPILER_4_UP} TCustomImageList {$ELSE}
TImageList {$ENDIF};
procedure SetSmallImages(Val: {$IFDEF DFS_COMPILER_4_UP} TCustomImageList
{$ELSE} TImageList {$ENDIF});
function GetVersion: string; virtual;
procedure SetVersion(const Val: string);
function GetCurrentColumnWidth(Index: integer): integer;
procedure CreateParams(var Params: TCreateParams); override;
procedure Loaded; override;
{ Should probably remain protected }
property SortUpBmp: TBitmap
read FSortUpBmp;
property SortDownBmp: TBitmap
read FSortDownBmp;
{ Should be made public by descendants as needed }
property LastColumnClicked: Integer
read FLastColumnClicked
write SetLastColumnClicked;
{ Should be published by descendants as needed }
property HeaderHandle: HWnd
read FHeaderHandle;
property AutoColumnSort: TAutoColumnSort
read FAutoColumnSort
write SetAutoColumnSort
default acsNoSort;
property AutoSortStyle: TAutoSortStyle
read FAutoSortStyle
write SetAutoSortStyle
default assSmart;
property AutoResort: boolean
read FAutoResort
write SetAutoResort
default TRUE;
property AutoSortAscending: boolean
read FAutoSortAscending
write SetAutoSortAscending
default TRUE;
property ColumnSearch: boolean
read FColumnSearch
write FColumnSearch
default FALSE;
property ShowSortArrows: boolean
read FShowSortArrows
write SetShowSortArrows
default FALSE;
property ReverseSortArrows: boolean
read FReverseSortArrows
write SetReverseSortArrows
default FALSE;
property CurrentSortAscending: boolean
read FTmpAutoSortAscending
write SetCurrentSortAscending;
property SaveSettings: TdfsEnhLVSaveSettings
read FSaveSettings
write FSaveSettings;
property Style: TLVStyle
read FStyle
write SetStyle
default lvStandard;
property CurrentColumnWidth[Index: integer]: integer
read GetCurrentColumnWidth;
{$IFDEF BACKGROUND_FIXED}
property BackgroundImage: TBitmap
read FBackgroundImage
write SetBackgroundImage;
{$ENDIF}
property NoColumnResize: boolean
read FNoColumnResize
write FNoColumnResize;
// We have to redeclare this so we can hook into the read/write methods.
property SmallImages:
{$IFDEF DFS_COMPILER_4_UP} TCustomImageList {$ELSE} TImageList {$ENDIF}
read GetSmallImages
write SetSmallImages;
{ Events }
property OnDrawHeader: TLVHDrawItemEvent
read FOnDrawHeader
write SetOnDrawHeader;
property OnMeasureItem: TLVMeasureItemEvent
read FOnMeasureItem
write FOnMeasureItem;
property OnDrawItem: TLVDrawItemEvent
read FOnDrawItem
write FOnDrawItem;
property OnDrawSubItem: TLVDrawSubItemEvent
read FOnDrawSubItem
write FOnDrawSubItem;
property OnAfterDefaultDrawItem: TLVAfterDrawItemEvent
read FOnAfterDefaultDrawItem
write FOnAfterDefaultDrawItem;
property OnSortItems: TLVSortItemsEvent
read FOnSortItems
write FOnSortItems;
property OnSortBegin: TLVSortStatusEvent
read FOnSortBegin
write FOnSortBegin;
property OnSortFinished: TLVSortStatusEvent
read FOnSortFinished
write FOnSortFinished;
property OnEditCanceled: TLVEditCanceled
read FOnEditCanceled
write FOnEditCanceled;
{$IFNDEF DFS_COMPILER_4_UP}
property OnGetImageIndex: TLVNotifyEvent
read FOnGetImageIndex
write FOnGetImageIndex;
{$ENDIF}
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
function StoreSettings: boolean; virtual;
function WriteSettings: boolean; virtual;
function LoadSettings: boolean; virtual;
function ReadSettings: boolean; virtual;
procedure DefaultSort(ColumnIndex:integer; Descending: boolean); virtual;
procedure Resort; virtual;
// Use these as replacements for Items.BeginUpdate and EndUpdate. They
// call those methods, but they also inhibit autosorting until after the
// last EndUpdate.
procedure BeginUpdate; virtual;
procedure EndUpdate; virtual;
// Resize all columns.
procedure ResizeColumns(ResizeMethod: TResizeMethod); virtual;
// Move list item to new position.
procedure MoveItem(OriginalIndex, NewIndex: Integer); virtual;
function StringSelect(FindStr: string; ColumnIndex: Integer): boolean; virtual;
function SubStringSelect(FindStr: string; ColumnIndex: Integer): boolean; virtual;
// Accounts for re-ordered columns
property ActualColumn[Index: integer]: TListColumn
read GetActualColumn;
published
property Version: string
read GetVersion
write SetVersion
stored FALSE;
end;
TdfsEnhListView = class(TCustomEnhListView)
public
property HeaderHandle;
property CurrentSortAscending;
property LastColumnClicked;
property CurrentColumnWidth;
published
property AutoColumnSort;
property AutoSortStyle;
property AutoResort;
property AutoSortAscending;
{$IFDEF BACKGROUND_FIXED}
property BackgroundImage;
{$ENDIF}
property ColumnSearch;
property NoColumnResize;
property ReverseSortArrows;
property ShowSortArrows;
property SaveSettings;
property Style;
property OnMeasureItem;
property OnDrawItem;
property OnDrawSubItem;
property OnAfterDefaultDrawItem;
property OnDrawHeader;
property OnSortItems;
property OnSortBegin;
property OnSortFinished;
property OnEditCanceled;
{ Publish TCustomListView inherited protected properties }
property Align;
{$IFDEF DFS_COMPILER_4_UP}
property Anchors;
property BiDiMode;
{$ENDIF}
property BorderStyle;
{$IFDEF DFS_COMPILER_4_UP}
property BorderWidth;
{$ENDIF}
property Color;
property ColumnClick;
property OnClick;
property OnDblClick;
property Columns;
{$IFDEF DFS_COMPILER_4_UP}
property Constraints;
{$ENDIF}
property Ctl3D;
{$IFDEF DFS_COMPILER_4_UP}
property DragKind;
{$ENDIF}
property DragMode;
property ReadOnly
default False;
property Enabled;
property Font;
property HideSelection;
property IconOptions;
property Items;
property AllocBy;
property MultiSelect;
property OnChange;
property OnChanging;
property OnColumnClick;
property OnDeletion;
property OnEdited;
property OnEditing;
{$IFDEF DFS_COMPILER_4_UP}
property OnEndDock;
{$ENDIF}
property OnEnter;
property OnExit;
property OnInsert;
property OnDragDrop;
property OnDragOver;
property DragCursor;
property OnStartDrag;
property OnEndDrag;
property OnGetImageIndex;
property OnMouseDown;
property OnMouseMove;
property OnMouseUp;
{$IFDEF DFS_COMPILER_4_UP}
property OnResize;
property OnSelectItem;
property OnStartDock;
{$ENDIF}
property ParentColor
default False;
property ParentFont;
property ParentShowHint;
{$IFDEF DFS_COMPILER_4_UP}
property ParentBiDiMode;
{$ENDIF}
property ShowHint;
property PopupMenu;
property ShowColumnHeaders;
property TabOrder;
property TabStop
default True;
property ViewStyle;
property Visible;
property OnKeyDown;
property OnKeyPress;
property OnKeyUp;
property LargeImages;
property SmallImages;
property StateImages;
end;
var
{ Default drawing variables }
DefDraw_TextOffset: integer; // Offset for the text -- 5
DefDraw_ImageOffset: integer; // Offset for image -- 2
implementation
uses
Registry, ExtListView;
var
FDirection,
FSortColNum: integer;
{$IFNDEF DFS_COMPILER_4_UP}
type
TReplaceFlags = set of (rfReplaceAll, rfIgnoreCase);
function StringReplace(const S, OldPattern, NewPattern: string;
Flags: TReplaceFlags): string;
var
SearchStr, Patt, NewStr: string;
Offset: Integer;
begin
if rfIgnoreCase in Flags then
begin
SearchStr := AnsiUpperCase(S);
Patt := AnsiUpperCase(OldPattern);
end else
begin
SearchStr := S;
Patt := OldPattern;
end;
NewStr := S;
Result := '';
while SearchStr <> '' do
begin
Offset := {$IFDEF DFS_COMPILER_2} Pos( {$ELSE} AnsiPos( {$ENDIF} Patt, SearchStr);
if Offset = 0 then
begin
Result := Result + NewStr;
Break;
end;
Result := Result + Copy(NewStr, 1, Offset - 1) + NewPattern;
NewStr := Copy(NewStr, Offset + Length(OldPattern), MaxInt);
if not (rfReplaceAll in Flags) then
begin
Result := Result + NewStr;
Break;
end;
SearchStr := Copy(SearchStr, Offset + Length(Patt), MaxInt);
end;
end;
{$ENDIF}
function IsValidNumber(S: string; var V: extended): boolean;
var
NumCode: integer;
FirstSpace: integer;
begin
FirstSpace := Pos(' ', S);
if FirstSpace > 0 then
S := Copy(S, 1, FirstSpace - 1);
Val(S, V, NumCode);
Result := (NumCode = 0);
if not Result then
begin
// Remove all thousands seperators
S := StringReplace(S, ThousandSeparator, '', [rfReplaceAll]);
// change DecimalSeperator to '.' because Val only recognizes that, not
// the locale specific decimal char. Stupid Val.
S := StringReplace(S, DecimalSeparator, '.', [rfReplaceAll]);
// and try again
Val(S, V, NumCode);
Result := (NumCode = 0);
End;
end;
// date conversion will fail if using long format, e.g. '1 January 1994'
function IsValidDateTime(const S: string; var D: TDateTime): boolean;
var
i: integer;
HasDate: boolean;
HasTime: boolean;
begin
// Check for two date seperators. This is because some regions use a "-"
// to seperate dates, so if we just checked for one we would flag negative
// numbers as being dates.
i := Pos(DateSeparator, S);
HasDate := i > 0;
if HasDate and (i <> Length(S)) then
HasDate := Pos(DateSeparator, Copy(S, i+1, Length(S)-i)) > 0;
HasTime := Pos(TimeSeparator, S) > 0;
Result := HasDate or HasTime;
if Result then
begin
try
if HasDate and HasTime then
D := StrToDateTime(S)
else if HasDate then
D := StrToDate(S)
else if HasTime then
D := StrToTime(S);
except
// Something failed to convert...
D := 0;
Result := FALSE;
end;
end;
end; { IsValidDateTime }
function __CustomSortProc1__(Item1, Item2: TListItem; Data: integer): integer;
stdcall;
var
Str1, Str2: string;
Val1, Val2: extended;
Date1, Date2: TDateTime;
Diff: TDateTime;
begin
if (Item1 = NIL) or (Item2 = NIL) then
begin
// something bad happening, I'm outta here
Result := 0;
exit;
end;
try
if FSortColNum = -1 then
begin
Str1 := Item1.Caption;
Str2 := Item2.Caption;
end else begin
if FSortColNum < Item1.SubItems.Count then
Str1 := Item1.SubItems[FSortColNum]
else
Str1 := '';
if FSortColNum < Item2.SubItems.Count then
Str2 := Item2.SubItems[FSortColNum]
else
Str2 := '';
end;
if TCustomEnhListView(Data).AutoSortStyle = assSmart then
begin
if IsValidDateTime(Str1, Date1) and IsValidDateTime(Str2, Date2) then
begin
Diff := Date1 - Date2;
if Diff < 0.0 then Result := -1
else if Diff > 0.0 then Result := 1
else Result := 0
end else if IsValidNumber(Str1, Val1) and IsValidNumber(Str2, Val2) then
begin
if Val1 < Val2 then Result := -1
else if Val1 > Val2 then Result := 1
else Result := 0
end else
Result := AnsiCompareStr(Str1, Str2);
end else
Result := AnsiCompareStr(Str1, Str2);
Result := FDirection * Result; // Set direction flag.
except
Result := 0; // Something went bad in the comparison. Say they are equal.
end;
end;
function __CustomSortProc2__(Item1, Item2: TListItem; Data: integer): integer;
stdcall;
var
EvRes: integer;
begin
EvRes := 0;
TCustomEnhListView(Data).SortItems(Item1, Item2, FSortColNum, EvRes);
Result := EvRes * FDirection;
end;
{ TdfsEnhLVSaveSettings }
constructor TdfsEnhLVSaveSettings.Create;
begin
inherited Create;
FAutoSave := FALSE;
FRegistryKey := '';
FSaveViewStyle := TRUE;
FSaveColumnSizes := TRUE;
SaveCurrentSort := TRUE;
end;
procedure TdfsEnhLVSaveSettings.StoreColumnSizes(ColCount: integer;
const IntArray: array of integer);
var
Reg: TRegIniFile;
x: integer;
s: string;
begin
if ColCount < 1 then exit;
s := '';
for x := 0 to ColCount-1 do
s := s + IntToStr(IntArray[x]) + ',';
SetLength(s, Length(s)-1);
Reg := TRegIniFile.Create(FRegistryKey);
try
Reg.WriteString('Columns', 'Sizes', s);
finally
Reg.Free;
end;
end;
procedure TdfsEnhLVSaveSettings.ReadColumnSizes(ColCount: integer;
var IntArray: array of integer);
var
Reg: TRegIniFile;
x,y: integer;
s: string;
begin
if ColCount < 1 then exit;
s := '';
Reg := TRegIniFile.Create(FRegistryKey);
try
s := Reg.ReadString('Columns', 'Sizes', '');
finally
Reg.Free;
end;
if s = '' then
begin
IntArray[0] := -1;
exit;
end;
y := 0;
for x := 0 to ColCount-1 do
begin
try
y := Pos(',', s);
if y = 0 then
y := Length(s)+1;
IntArray[x] := StrToInt(Copy(s, 1, y-1));
except
{ Nothing, just eat the exception };
end;
s := copy(s, y+1, length(s));
if s = '' then break;
end;
end;
procedure TdfsEnhLVSaveSettings.StoreCurrentSort(Ascending: boolean;
SortCol: integer);
var
Reg: TRegIniFile;
begin
Reg := TRegIniFile.Create(FRegistryKey);
try
Reg.WriteBool('Sort', 'Ascending', Ascending);
Reg.WriteInteger('Sort', 'SortCol', SortCol);
finally
Reg.Free;
end;
end;
procedure TdfsEnhLVSaveSettings.ReadCurrentSort(var Ascending: boolean;
var SortCol: integer);
var
Reg: TRegIniFile;
begin
Reg := TRegIniFile.Create(FRegistryKey);
try
Ascending := Reg.ReadBool('Sort', 'Ascending', TRUE);
SortCol := Reg.ReadInteger('Sort', 'SortCol', 0);
finally
Reg.Free;
end;
end;
procedure TdfsEnhLVSaveSettings.StoreViewStyle(Style: TViewStyle);
const
STYLE_VAL: array[TViewStyle] of integer = (0, 1, 2, 3);
var
Reg: TRegIniFile;
begin
Reg := TRegIniFile.Create(FRegistryKey);
try
Reg.WriteInteger('ViewStyle', 'ViewStyle', STYLE_VAL[Style]);
finally
Reg.Free;
end;
end;
function TdfsEnhLVSaveSettings.ReadViewStyle(Default: TViewStyle): TViewStyle;
const
STYLES: array[0..3] of TViewStyle = (vsIcon, vsSmallIcon, vsList, vsReport);
var
Reg: TRegIniFile;
i: integer;
begin
Reg := TRegIniFile.Create(FRegistryKey);
try
i := Reg.ReadInteger('ViewStyle', 'ViewStyle', -1);
if (i >= Low(STYLES)) and (i <= High(STYLES)) then
Result := STYLES[i]
else
Result := Default;
finally
Reg.Free;
end;
end;
// Override constructor to "zero out" our internal variable.
constructor TCustomEnhListView.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
FSearchStr := '';
FSearchTickCount := 0;
FHeaderHandle := 0;
FSortDirty := FALSE;
FUpdateCount := 1; // inhibit sorting until finished creating.
FSaveSettings := TdfsEnhLVSaveSettings.Create;
FAutoColumnSort := acsNoSort;
FAutoResort := TRUE;
FAutoSortStyle := assSmart;
FAutoSortAscending := TRUE;
FTmpAutoSortAscending := FAutoSortAscending;
FLastColumnClicked := -1;
FCanvas := NIL;
FStyle := lvStandard;
FSortUpBmp := NIL;
FSortDownBmp := NIL;
FShowSortArrows := FALSE;
FReverseSortArrows := FALSE;
{$IFDEF BACKGROUND_FIXED}
FBackgroundImage := TBitmap.Create;
{$ENDIF}
FHeaderInstance := MakeObjectInstance(HeaderWndProc);
end;
destructor TCustomEnhListView.Destroy;
begin
{$IFDEF BACKGROUND_FIXED}
FBackgroundImage.Free;
{$ENDIF}
FSortUpBmp.Free;
FSortDownBmp.Free;
FCanvas.Free;
if FHeaderHandle <> 0 then
SetWindowLong(FHeaderHandle, GWL_WNDPROC, LongInt(FOldHeaderWndProc));
FreeObjectInstance(FHeaderInstance);
inherited Destroy;
FSaveSettings.Free;
end;
procedure TCustomEnhListView.CreateParams(var Params: TCreateParams);
begin
inherited CreateParams(Params);
if (FStyle = lvOwnerDrawFixed) then
begin
Params.Style := Params.Style or LVS_OWNERDRAWFIXED;
if FCanvas = NIL then
FCanvas := TCanvas.Create;
end else begin
if (not assigned(FOnDrawHeader)) and (not FShowSortArrows) then
begin
FCanvas.Free;
FCanvas := NIL;
end;
end;
end;
procedure TCustomEnhListView.CreateWnd;
begin
// if FCreatingWindowHandle then exit;
FCreatingWindowHandle := TRUE;
try
inherited CreateWnd;
// If we are loading object from stream (form file), we have to wait until
// everything is loaded before populating the list. If we are not loading,
// i.e. the component was created dynamically or was just dropped on a form,
// we need to reset the flag now.
if not (csLoading in ComponentState) then
FUpdateCount := 0;
// Something very bizarre happens in either TCustomListView or in the
// list view code itself in COMCTL32.DLL: The first WM_MEASUREITEM value
// is not honored if the listview has small images assigned to it. Instead
// the value is ignored and the height of the images are used. I found that
// by forcing Windows to ask for the item height a second time, it would
// honor the value then.
if Style = lvOwnerDrawFixed then
ResetOwnerDrawHeight;
finally
FCreatingWindowHandle := FALSE;
end;
end;
procedure TCustomEnhListView.Loaded;
begin
inherited Loaded;
{$IFDEF BACKGROUND_FIXED}
BackgroundImageChanged(Self);
{$ENDIF}
if not FCreatingWindowHandle then
HandleNeeded;
FUpdateCount := 0;
if (not LoadSettings) or (not SaveSettings.SaveCurrentSort) then
begin
if Columns.Count > 0 then
FLastColumnClicked := 0;
Resort;
end;
// Something flaky going on. Hard to explain, but this clears it up.
PostMessage(Handle, WM_OWNERDRAWCOLUMNS, 0, 0);
end;
procedure TCustomEnhListView.WMDestroy(var Message: TWMDestroy);
begin
StoreSettings;
inherited;
end;
function TCustomEnhListView.StoreSettings: boolean;
begin
if FSaveSettings.AutoSave and
(([csDesigning, csLoading, csReading] * ComponentState) = []) then
Result := WriteSettings
else
Result := FALSE;
end;
function TCustomEnhListView.WriteSettings: boolean;
var
ColCount: integer;
ColArray: PIntArray;
x: integer;
begin
Result := TRUE;
ColCount := Columns.Count;
if ColCount > 0 then
begin
GetMem(ColArray, SizeOf(Integer)*ColCount);
try
if FSaveSettings.SaveColumnSizes then
begin
for x := 0 to ColCount-1 do
ColArray[x] := ActualColumn[x].Width;
FSaveSettings.StoreColumnSizes(ColCount, ColArray^);
end;
if FSaveSettings.SaveCurrentSort then
FSaveSettings.StoreCurrentSort(CurrentSortAscending, LastColumnClicked);
if FSaveSettings.SaveViewStyle then
FSaveSettings.StoreViewStyle(ViewStyle);
finally
FreeMem(ColArray);
end;
end;
end;
function TCustomEnhListView.LoadSettings: boolean;
begin
if FSaveSettings.AutoSave and (not(csDesigning in ComponentState)) then
Result := ReadSettings
else
Result := FALSE;
end;
function TCustomEnhListView.ReadSettings: boolean;
var
ColCount: integer;
ColArray: PIntArray;
x: integer;
SortCol: integer;
SortAscending: boolean;
begin
Result := TRUE;
ColCount := Columns.Count;
if ColCount > 0 then
begin
GetMem(ColArray, SizeOf(Integer)*ColCount);
try
if FSaveSettings.SaveColumnSizes then
begin
for x := 0 to ColCount-1 do
ColArray[x] := ActualColumn[x].Width;
FSaveSettings.ReadColumnSizes(ColCount, ColArray^);
if ColArray[0] <> -1 then
for x := 0 to ColCount-1 do
ActualColumn[x].Width := ColArray[x];
end;
finally
FreeMem(ColArray);
end;
end;
if FSaveSettings.SaveCurrentSort then
begin
FSaveSettings.ReadCurrentSort(SortAscending, SortCol);
if SortCol >= Columns.Count then
SortCol := Columns.Count-1;
if SortCol < 0 then
SortCol := 0;
BeginUpdate;
try
CurrentSortAscending := SortAscending;
LastColumnClicked := SortCol;
Resort;
finally
EndUpdate;
end;
end;
if FSaveSettings.SaveViewStyle then
ViewStyle := FSaveSettings.ReadViewStyle(ViewStyle);
end;
procedure TCustomEnhListView.DoSort(ColumnIndex:integer; Descending: boolean);
begin
FSortDirty := FALSE;
LastColumnClicked := ColumnIndex;
SortBegin(ColumnIndex, not Descending);
if Descending then
FDirection := 1
else
FDirection := -1;
FSortColNum := ColumnIndex - 1;
if assigned(FOnSortItems) then
CustomSort(@__CustomSortProc2__, integer(Self))
else
CustomSort(@__CustomSortProc1__, integer(Self));
SortFinished(ColumnIndex, not Descending);
end;
procedure TCustomEnhListView.DefaultSort(ColumnIndex: integer;
Descending: boolean);
begin
// Check if the sort order should be toggled
if FAutoColumnSort = acsSortToggle then
if LastColumnClicked = ColumnIndex then
FTmpAutoSortAscending := not Descending
else
FTmpAutoSortAscending := Descending;
InvalidateColumnHeader(ColumnIndex);
DoSort(ColumnIndex, Descending);
end;
procedure TCustomEnhListView.SortItems(const Item1, Item2: TListItem;
SortColumn: integer; var CompResult: integer);
var
SortAs: TSortAs;
Str1, Str2: string;
F1, F2: extended;
Date1, Date2, Diff: TDateTime;
begin
// The only way to get in here is if FOnSortItems is assigned, so don't bother
// checking for NIL
SortAs := saNone;
FonSortItems(Self, Item1, Item2, SortColumn, SortAs, CompResult);
// Do they want us to sort it?
if SortAs <> saNone then
begin
if SortColumn = -1 then
begin
Str1 := Item1.Caption;
Str2 := Item2.Caption;
end else begin
if SortColumn < Item1.SubItems.Count then
Str1 := Item1.SubItems[SortColumn]
else
Str1 := '';
if SortColumn < Item2.SubItems.Count then
Str2 := Item2.SubItems[SortColumn]
else
Str2 := '';
end;
case SortAs of
saString: CompResult := AnsiCompareStr(Str1, Str2);
saNumeric:
begin
if not IsValidNumber(Str1, F1) then
F1 := 0;
if not IsValidNumber(Str2, F2) then
F2 := 0;
if F1 < F2 then CompResult := -1
else if F1 > F2 then CompResult := 1
else CompResult := 0;
end;
saDateTime:
begin
if not IsValidDateTime(Str1, Date1) then
Date1 := 0;
if not IsValidDateTime(Str2, Date2) then
Date1 := 0;
Diff := Date1 - Date2;
if Diff < 0.0 then CompResult := -1
else if Diff > 0.0 then CompResult := 1
else CompResult := 0
end;
end;
end;
end;
procedure TCustomEnhListView.SortBegin(ColumnIndex: integer;
Ascending: boolean);
begin
if assigned(FOnSortBegin) then
FOnSortBegin(Self, ColumnIndex, Ascending);
end;
procedure TCustomEnhListView.SortFinished(ColumnIndex: integer;
Ascending: boolean);
begin
if assigned(FOnSortFinished) then
FOnSortFinished(Self, ColumnIndex, Ascending);
end;
procedure TCustomEnhListView.ColClick(Column: TListColumn);
begin
// Check if the sort order should be toggled
if FAutoColumnSort = acsSortToggle then
if LastColumnClicked = Column.Index then
FTmpAutoSortAscending := not FTmpAutoSortAscending
else
FTmpAutoSortAscending := FAutoSortAscending;
inherited ColClick(Column);
if (FAutoColumnSort <> acsNoSort) and (Column.Index < Columns.Count) then
DoSort(Column.Index, FTmpAutoSortAscending);
LastColumnClicked := Column.Index;
end;
{$IFDEF DFS_FIXED_LIST_VIEW}
procedure TCustomEnhListView.InsertItem(Item: TListItem);
begin
inherited InsertItem(Item);
if FAutoResort then
Resort;
end;
{$ENDIF}
procedure TCustomEnhListView.Edit(const Item: TLVItem);
begin
inherited Edit(Item);
if FAutoResort then
Resort;
end;
type
THackListItems = class(TListItems)
end;
procedure TCustomEnhListView.EditCanceled(const Item: TLVItem);
begin
if assigned(FOnEditCanceled) then
with Item do
FOnEditCanceled(Self, THackListItems(Items).GetItem(iItem));
end;
{$IFNDEF DFS_COMPILER_4_UP}
function TCustomEnhListView.GetItem(Value: TLVItem): TListItem;
begin
with Value do
if (mask and LVIF_PARAM) <> 0 then Result := TListItem(lParam)
else Result := Items[IItem];
end;
{$ENDIF}
{$IFNDEF DFS_COMPILER_4_UP}
type
THackdfsExtListView = class(TdfsExtListView);
{$ENDIF}
procedure TCustomEnhListView.CNNotify(var Message: TWMNotify);
{$IFNDEF DFS_COMPILER_4_UP}
var
Item: TListItem;
{$ENDIF}
begin
inherited;
with Message.NMHdr^ do
case code of
{$IFNDEF DFS_FIXED_LIST_VIEW}
LVN_INSERTITEM:
if FAutoResort then
Resort;
{$ENDIF}
LVN_ENDLABELEDIT:
with PLVDispInfo(Pointer(Message.NMHdr))^ do
if (item.pszText = NIL) and (item.IItem <> -1) then
EditCanceled(item);
{$IFNDEF DFS_COMPILER_4_UP}
LVN_GETDISPINFO:
begin
Item := GetItem(PLVDispInfo(Message.NMHdr)^.item);
if Item <> NIL then
with PLVDispInfo(Message.NMHdr)^.item do
begin
if (mask and LVIF_IMAGE) <> 0 then
begin
if iSubItem = 0 then
begin
GetImageIndex(Item);
iImage := Item.ImageIndex;
if Assigned(StateImages) then
begin
state := IndexToStateImageMask(Item.StateIndex + 1);
stateMask := $F000;
mask := mask or LVIF_STATE;
end;
end;
end;
end;
end;
{$ENDIF}
end;
end;
procedure TCustomEnhListView.SetAutoColumnSort(Value: TAutoColumnSort);
begin
if FAutoColumnSort <> Value then
begin
FAutoColumnSort := Value;
if FAutoColumnSort <> acsNoSort then
Resort;
end;
end;
procedure TCustomEnhListView.SetAutoSortStyle(Value: TAutoSortStyle);
begin
if FAutoSortStyle <> Value then
begin
FAutoSortStyle := Value;
Resort;
end;
end;
procedure TCustomEnhListView.SetAutoResort(Value: boolean);
begin
if FAutoResort <> Value then
FAutoResort := Value;
end;
procedure TCustomEnhListView.SetCurrentSortAscending(Value: boolean);
begin
if FTmpAutoSortAscending <> Value then
begin
FTmpAutoSortAscending := Value;
InvalidateColumnHeader(FLastColumnClicked);
end;
end;
procedure TCustomEnhListView.SetAutoSortAscending(Value: Boolean);
begin
if FAutoSortAscending <> Value then
begin
FAutoSortAscending := Value;
FTmpAutoSortAscending := Value;
end;
end;
procedure TCustomEnhListView.Resort;
begin
FSortDirty := TRUE;
if ((FAutoColumnSort <> acsNoSort) and (LastColumnClicked >= 0) and
(LastColumnClicked < Columns.Count)) or (assigned(FOnSortItems)) then
begin
if FUpdateCount < 1 then
DoSort(LastColumnClicked, FTmpAutoSortAscending);
end;
end;
procedure TCustomEnhListView.BeginUpdate;
begin
Items.BeginUpdate;
inc(FUpdateCount);
end;
procedure TCustomEnhListView.EndUpdate;
begin
dec(FUpdateCount);
if FUpdateCount < 0 then
FUpdateCount := 0; // In case someone gets overly happy with EndUpdate calls
if FUpdateCount = 0 then
begin
// Need to resort?
if FSortDirty then
Resort;
end;
// Call this last so resort happens before screen redraw is re-enabled.
Items.EndUpdate;
end;
procedure TCustomEnhListView.DrawItem(var Canvas: TCanvas; Index: Integer;
Rect: TRect; State: TOwnerDrawState; var DefaultDrawing,
FullRowSelect: boolean);
begin
DefaultDrawing := not assigned(FOnDrawItem);
if assigned(FOnDrawItem) then
FOnDrawItem(Self, Canvas, Index, Rect, State, DefaultDrawing,FullRowSelect);
end;
procedure TCustomEnhListView.AfterDrawItem(var Canvas: TCanvas; Index: Integer;
Rect: TRect; State: TOwnerDrawState);
begin
if assigned(FOnAfterDefaultDrawItem) then
FOnAfterDefaultDrawItem(Self, Canvas, Index, Rect, State);
end;
procedure TCustomEnhListView.CMSysColorChange(var Message: TWMSysColorChange);
begin
// Need to recreate the sort arrow bmps to use the new system colors
if ShowSortArrows then
CreateSortBmps(FSortUpBmp, FSortDownBmp);
inherited;
end;
procedure TCustomEnhListView.CMFontChanged(var Messsage: TMessage);
begin
if HandleAllocated and (Style = lvOwnerDrawFixed) then
RecreateWnd
else
inherited;
end;
procedure TCustomEnhListView.CNMeasureItem(var Message: TWMMeasureItem);
var
DC: HDC;
OldFont: HFONT;
Size: TSize;
begin
inherited;
DC := CreateCompatibleDC(0);
OldFont := SelectObject(DC, Font.Handle);
try
GetTextExtentPoint32(DC, 'Wy', 2, Size);
// Owner drawing only happens in vsReport mode, so no need to check anything
// besides that.
// I'm checking SmallImages.Height here, but I don't think it'll do any
// good. From what I can tell, if you have SmallImages assigned, this
// handler will get called but the value you give it is ignored and the
// list uses it's normal item height. Strange....
if assigned(SmallImages) and (SmallImages.Height > Size.cy) then
Message.MeasureItemStruct.itemHeight := SmallImages.Height
else
Message.MeasureItemStruct.itemHeight := Size.cy + 1;
finally
SelectObject(DC, OldFont);
DeleteDC(DC);
end;
MeasureItem(Message.MeasureItemStruct.itemHeight);
Message.Result := 1;
end;
procedure TCustomEnhListView.MeasureItem(var Height: UINT);
begin
if assigned(FOnMeasureItem) then
FOnMeasureItem(Self, Height);
end;
procedure TCustomEnhListView.CNDrawItem(var Message: TWMDrawItem);
var
State: TOwnerDrawState;
DoDefaultDrawing: boolean;
FullRowSelect: boolean;
SavedDC: integer;
begin { CNDrawItem }
if FCanvas = NIL then exit;
with Message.DrawItemStruct^ do
begin
{$IFDEF DFS_COMPILER_5_UP}
State := TOwnerDrawState(LongRec(itemState).Lo);
{$ELSE}
State := TOwnerDrawState(WordRec(LongRec(itemState).Lo).Lo);
{$ENDIF}
SavedDC := SaveDC(hDC);
FCanvas.Handle := hDC;
try
FCanvas.Font := Font;
FCanvas.Brush := Brush;
DoDefaultDrawing := FALSE;
FullRowSelect := FALSE;
ProcessDrawItemMsg(itemID, rcItem, State, DoDefaultDrawing, FullRowSelect);
finally
FCanvas.Handle := 0;
RestoreDC(hDC, SavedDC);
end;
end;
Message.Result := 1;
end;
function TCustomEnhListView.GetActualColumn(Index: integer): TListColumn;
begin
// Delphi 2 and C++B 1 have a bug in TListColumn.GetWidth. It returns zero
// for the width if the handle hasn't been allocated yet instead of returning
// the value of the internal storage variable like Delphi 3 does. I've also
// had some problems similar under Delphi 3, so I'm just always requiring the
// handle to be valid.
HandleNeeded;
if Index >= Columns.Count then
Result := NIL
else
Result := Columns[Index];
end;
function TCustomEnhListView.GetSubItemText(Index, SubItem: integer): string;
begin
if SubItem < 0 then
Result := Items[Index].Caption
else
Result := Items[Index].SubItems[SubItem];
end;
// SubItem is -1 for Caption item
procedure TCustomEnhListView.DrawSubItem(Index, SubItem: Integer; Rect: TRect;
State: TOwnerDrawState; var DefaultDrawing: boolean);
begin
DefaultDrawing := not assigned(FOnDrawSubItem);
if assigned(FOnDrawSubItem) then
FOnDrawSubItem(Self, FCanvas, Index, SubItem, Rect, State, DefaultDrawing);
end;
procedure TCustomEnhListView.DefaultDrawSubItem(Index, SubItem: Integer;
Rect: TRect; State: TOwnerDrawState);
var
DoDefaultDrawing: boolean;
SavedDC: integer;
begin
DoDefaultDrawing := csDesigning in ComponentState;
SavedDC := SaveDC(FCanvas.Handle);
try
if not (csDesigning in ComponentState) then
DrawSubItem(Index, SubItem, Rect, State, DoDefaultDrawing);
if DoDefaultDrawing then
begin
if SubItem >= 0 then
InflateRect(Rect, -4, 0);
if ActualColumn[SubItem+1].Alignment = taLeftJustify then
Inc(Rect.Left, DefDraw_TextOffset);
DrawTextEx(FCanvas.Handle, PChar(GetSubItemText(Index, SubItem)), -1, Rect,
DRAWTEXTEX_FLAGS or
DRAWTEXTEX_ALIGNMENT[ActualColumn[SubItem+1].Alignment], NIL);
end;
finally
RestoreDC(FCanvas.Handle, SavedDC);
end;
end;
{$IFDEF DFS_COMPILER_4_UP}
type
THackImageList = class(TCustomImageList);
{$ENDIF}
procedure TCustomEnhListView.DefaultDrawItem(Index: Integer; Rect: TRect;
State: TOwnerDrawState; FullRowSelect: boolean);
{$IFDEF DFS_COMPILER_4_UP}
const
DrawingStyles: array[TDrawingStyle] of Longint = (ILD_FOCUS, ILD_SELECTED,
ILD_NORMAL, ILD_TRANSPARENT);
Images: array[TImageType] of Longint = (0, ILD_MASK);
{$ENDIF}
var
{$IFDEF DFS_COMPILER_4_UP}
DS: TDrawingStyle;
x: integer;
{$ELSE}
OldStyle: TDrawingStyle;
{$ENDIF}
OldBlend: TColor;
Count: Integer;
SubRect: TRect;
ImgTop: integer;
begin
if Items[Index] = NIL then
// something bad happening, I'm outta here
exit;
if Columns.Count > 0 then
begin
if (odSelected in State) then
begin
if Focused then
begin
FCanvas.Brush.Color := clHighlight;
FCanvas.Font.Color := clHighlightText;
end else begin
if not HideSelection then
begin
FCanvas.Brush.Color := clBtnFace;
FCanvas.Font.Color := clBtnText;
end;
end;
end;
SubRect := Rect;
SubRect.Right := Rect.Left + CurrentColumnWidth[0]{ - 2};
if assigned(StateImages) then
begin
StateImages.Draw(FCanvas, SubRect.Left + DefDraw_ImageOffSet,
SubRect.Top + (SubRect.Bottom - SubRect.Top - StateImages.Height) div 2,
Items[Index].StateIndex);
Inc(SubRect.Left, StateImages.Width);
end;
if assigned(SmallImages) then
begin
OldBlend := SmallImages.BlendColor;
SmallImages.BlendColor := clHighlight;
ImgTop := SubRect.Top + (SubRect.Bottom - SubRect.Top -
SmallImages.Height) div 2;
{$IFDEF DFS_COMPILER_4_UP}
{ Changing DrawStyle causes an invalidate, which is very nasty since we
are in the process of repainting here. Continuous flickering.... }
if Focused and ((odSelected in State) or Items[Index].Focused) then
DS := dsSelected
else
DS := dsTransparent;
// Draw OverlayImage
if (Items[Index].OverlayIndex >= 0) and
(Items[Index].OverlayIndex <= 3) then // vadid overlay index?
begin
x := IndexToOverlayMask(Items[Index].OverlayIndex+1);
THackImageList(SmallImages).DoDraw(Items[Index].ImageIndex, FCanvas,
SubRect.Left + DefDraw_ImageOffSet, ImgTop, DrawingStyles[DS] or
Images[SmallImages.ImageType] or ILD_OVERLAYMASK and x, Enabled);
end else
THackImageList(SmallImages).DoDraw(Items[Index].ImageIndex, FCanvas,
SubRect.Left + DefDraw_ImageOffSet, ImgTop,
DrawingStyles[DS] or Images[SmallImages.ImageType], Enabled);
{$ELSE}
OldStyle := SmallImages.DrawingStyle;
if Focused and ((odSelected in State) or Items[Index].Focused) then
SmallImages.DrawingStyle := dsSelected
else
SmallImages.DrawingStyle := dsTransparent;
SmallImages.Draw(FCanvas, SubRect.Left + DefDraw_ImageOffSet, ImgTop,
Items[Index].ImageIndex);
// Draw OverlayImage
if (Items[Index].OverlayIndex >= 0) and
(Items[Index].OverlayIndex <= 3) then // vadid overlay index?
SmallImages.DrawOverlay(FCanvas, SubRect.Left + DefDraw_ImageOffSet,
ImgTop, Items[Index].ImageIndex, Items[Index].OverlayIndex);
SmallImages.DrawingStyle := OldStyle;
{$ENDIF}
SmallImages.BlendColor := OldBlend;
if ActualColumn[0].Alignment = taLeftJustify then
Inc(SubRect.Left, {DefDraw_TextOffset + }SmallImages.Width);
{ end else begin
if ActualColumn[0].Alignment = taLeftJustify then
Inc(SubRect.Left, DefDraw_TextOffset);}
end;
DefaultDrawSubItem(Index, -1, SubRect, State);
// Already done column 0, start at 1.
for Count := 1 to Columns.Count-1 do
begin
{ Restore this through each iteration since they may screw with it in
the OnDrawSubItem event. }
if not FullRowSelect then
begin
FCanvas.Brush.Color := clWindow;
FCanvas.Font.Color := clWindowText;
end;
if Count > Items[Index].SubItems.Count then
continue; // Hidden item
if ActualColumn[Count].Alignment = taLeftJustify then
begin
SubRect.Left := SubRect.Right;
SubRect.Right := SubRect.Left + CurrentColumnWidth[Count];
// Inc(SubRect.Left, DefDraw_TextOffset)
end else begin
SubRect.Left := SubRect.Right;// + DefDraw_TextOffset;
SubRect.Right := SubRect.Left + CurrentColumnWidth[Count];
// Dec(SubRect.Right, DefDraw_TextOffset);
end;
DefaultDrawSubItem(Index, Count-1, SubRect, State);
end;
end;
end;
procedure TCustomEnhListView.ProcessDrawItemMsg(Index: Integer; Rect: TRect;
State: TOwnerDrawState; var DefaultDrawing, FullRowSelect: boolean);
var
SubRect: TRect;
begin
DefaultDrawing := csDesigning in ComponentState;
if not (csDesigning in ComponentState) then
DrawItem(FCanvas, Index, Rect, State, DefaultDrawing, FullRowSelect);
if DefaultDrawing then
begin
FCanvas.FillRect(Rect);
if (Index >= 0) then
begin
if (odSelected in State) then
begin
if (not HideSelection) or Focused then
begin
if Focused then
FCanvas.Brush.Color := clHighlight
else
FCanvas.Brush.Color := clBtnFace;
SubRect := Rect;
// Inc(SubRect.Left, DefDraw_TextOffset - 2);
// Dec(SubRect.Left, 2);
if (not FullRowSelect) then
begin
if assigned(Items[Index]) then
SubRect.Right := SubRect.Left +
FCanvas.TextWidth(Items[Index].Caption) + 8;
if assigned(StateImages) then
OffsetRect(SubRect, StateImages.Width, 0);
if assigned(SmallImages) then
OffsetRect(SubRect, SmallImages.Width, 0);
// Don't let it go past first column width
if (Columns.Count > 0) and
(CurrentColumnWidth[0] < SubRect.Right) then
SubRect.Right := CurrentColumnWidth[0];
end else begin
if assigned(StateImages) then
Inc(SubRect.Left, StateImages.Width);
if assigned(SmallImages) then
Inc(SubRect.Left, SmallImages.Width);
end;
FCanvas.FillRect(SubRect);
end;
end;
DefaultDrawItem(Index, Rect, State, FullRowSelect);
if (odFocused in State) and Focused then
begin
SubRect := Rect;
// Inc(SubRect.Left, DefDraw_TextOffset - 2);
// Dec(SubRect.Left, 2);
if (not FullRowSelect) then
begin
if assigned(Items[Index]) then
SubRect.Right := SubRect.Left +
FCanvas.TextWidth(Items[Index].Caption) + 8;
if assigned(SmallImages) then
OffsetRect(SubRect, SmallImages.Width, 0);
if assigned(StateImages) then
OffsetRect(SubRect, StateImages.Width, 0);
// Don't let it go past first column width
if (Columns.Count > 0) and
(CurrentColumnWidth[0] < SubRect.Right) then
SubRect.Right := CurrentColumnWidth[0];
end else begin
if assigned(StateImages) then
Inc(SubRect.Left, StateImages.Width);
if assigned(SmallImages) then
Inc(SubRect.Left, SmallImages.Width);
end;
FCanvas.DrawFocusRect(SubRect);
end;
end else
FCanvas.FillRect(Rect);
if (not (csDesigning in ComponentState)) then
AfterDrawItem(FCanvas, Index, Rect, State);
end;
end;
procedure TCustomEnhListView.SetStyle(Value: TLVStyle);
begin
if FStyle <> Value then
begin
FStyle := Value;
if HandleAllocated then
RecreateWnd;
end;
end;
procedure TCustomEnhListView.SetReverseSortArrows(Value: boolean);
begin
if Value <> FReverseSortArrows then
begin
FReverseSortArrows := Value;
if ShowSortArrows then
begin
CreateSortBmps(FSortUpBmp, FSortDownBmp);
InvalidateColumnHeader(FLastColumnClicked);
end;
end;
end;
procedure TCustomEnhListView.SetShowSortArrows(Value: boolean);
begin
if Value <> FShowSortArrows then
FShowSortArrows := Value;
FSortUpBmp.Free;
FSortDownBmp.Free;
if FShowSortArrows then
begin
FSortUpBmp := TBitmap.Create;
FSortDownBmp := TBitmap.Create;
CreateSortBmps(FSortUpBmp, FSortDownBmp);
if not (csReading in ComponentState) then
SetColumnsOwnerDrawFlag(TRUE);
end else begin
FSortUpBmp := NIL;
FSortDownBmp := NIL;
if not (csReading in ComponentState) then
SetColumnsOwnerDrawFlag(assigned(FOnDrawHeader))
end;
if HandleAllocated then
Invalidate;
end;
procedure TCustomEnhListView.CreateSortBmps(var UpBmp, DownBmp: TBitmap);
var
HeaderHeight: integer;
MidPoint: integer;
Bmp: TBitmap;
begin
if UpBmp = NIL then
UpBmp := TBitmap.Create;
if DownBmp = NIL then
DownBmp := TBitmap.Create;
UpBmp.Canvas.Font.Assign(Font);
HeaderHeight := UpBmp.Canvas.TextHeight('Wy') - 6;
if HeaderHeight > 0 then
begin
if Odd(HeaderHeight) then
Inc(HeaderHeight);
UpBmp.Width := HeaderHeight;
UpBmp.Height := HeaderHeight;
DownBmp.Width := HeaderHeight;
DownBmp.Height := HeaderHeight;
MidPoint := HeaderHeight div 2;
{ Don't ask about the drawing. I just fooled around until I got
something I liked. }
if FReverseSortArrows then
Bmp := UpBmp
else
Bmp := DownBmp;
with Bmp.Canvas do
begin
Brush.Color := clBtnFace;
FillRect(Rect(0, 0, HeaderHeight, HeaderHeight));
Pen.Color := clBtnShadow;
MoveTo(MidPoint, HeaderHeight-2);
LineTo(HeaderHeight-1, 0);
Pixels[HeaderHeight-1, 0] := Pen.Color;
Pen.Color := clBtnHighlight;
MoveTo(HeaderHeight-2, 0);
LineTo(0, 0);
LineTo(MidPoint-1, HeaderHeight-2);
Pixels[MidPoint-1, HeaderHeight-2] := Pen.Color;
end;
if FReverseSortArrows then
Bmp := DownBmp
else
Bmp := UpBmp;
with Bmp.Canvas do
begin
Brush.Color := clBtnFace;
FillRect(Rect(0, 0, HeaderHeight, HeaderHeight));
Pen.Color := clBtnHighlight;
MoveTo(0, HeaderHeight-1);
LineTo(MidPoint-1, 0);
Pen.Color := clBtnShadow;
MoveTo(MidPoint, 0);
LineTo(HeaderHeight-1, HeaderHeight-1);
LineTo(-1, HeaderHeight-1);
Pixels[MidPoint, 0] := clBtnFace;
end;
end;
end;
procedure TCustomEnhListView.DestroyWnd;
begin
if not FCreatingWindowHandle then
begin
inherited DestroyWnd;
FHeaderHandle := 0;
end;
end;
procedure TCustomEnhListView.DrawHeader(var Canvas: TCanvas; Index: Integer;
var Rect: TRect; Selected: boolean; var DefaultDrawing: boolean);
begin
DefaultDrawing := not assigned(FOnDrawHeader);
if assigned(FOnDrawHeader) then
FOnDrawHeader(Self, Canvas, Index, Rect, Selected, DefaultDrawing);
end;
procedure TCustomEnhListView.WMNotify(var Message: TWMNotify);
const
RECURSE_FLAG: boolean = FALSE;
begin
if NoColumnResize then
case Message.NMHdr.code of
HDN_BEGINTRACK, HDN_TRACK, HDN_BEGINTRACKW, HDN_TRACKW:
begin
Message.Result := 1;
exit;
end;
end;
inherited;
// Note the recursion flag. This is needed since the SetColumnsOwnerDrawFlag
// call below will cause some HDN_xxx notification messages.
if RECURSE_FLAG then
exit;
// For some reason, the SECOND time you drag a header width, it toasts the
// column index in the draw item message. Also seems to reset owner draw
// info at times, too. Anyway, the best fix I could come up with was to
// always reset the owner draw flag.
case Message.NMHdr.code of
HDN_BEGINTRACK, HDN_ITEMCHANGED, HDN_BEGINTRACKW, HDN_ITEMCHANGEDW:
begin
if Message.NMHdr.code <> HDN_TRACK then
begin
RECURSE_FLAG := TRUE;
try
SetColumnsOwnerDrawFlag(assigned(FOnDrawHeader) or FShowSortArrows);
finally
RECURSE_FLAG := FALSE;
end;
end;
end;
HDN_DIVIDERDBLCLICK, HDN_DIVIDERDBLCLICKW:
{ D4 (and others probably) don't update column width when this happens. }
begin
with PHDNotify(Pointer(Message.NMHdr))^ do
if Item < Columns.Count then
{$IFDEF DFS_COMPILER_4_UP}
Column[Item].Width :=
{$ELSE}
ActualColumn[Item].Width :=
{$ENDIF}
ListView_GetColumnWidth(Handle, Item);
end;
end;
(* old way. had some performance problems when used in conjunction with
TToolbar97 component. No idea why that would cause it, though.
// For some reason, the SECOND time you drag a header width, it toasts the
// column index in the draw item message. Also seems to reset owner draw
// info at times, too. Anyway, the best fix I could come up with was to
// always watch for a change in the header handle, and always reset the owner
// draw flag. Note the recursion flag. This is needed since the
// SetColumnsOwnerDrawFlag will cause some HDN_xxx notification messages.
// Best way that I can find to snag the real header handle. Kludgy at best,
// but what else are you gonna do?
case Message.NMHdr.code of
HDN_LAST..HDN_FIRST:
begin
if Message.NMHdr.hwndFrom <> FHeaderHandle then
FHeaderHandle := Message.NMHdr^.hwndFrom;
if RECURSE_FLAG or (FUpdateCount > 0) then exit;
RECURSE_FLAG := TRUE;
try
SetColumnsOwnerDrawFlag(assigned(FOnDrawHeader) or FShowSortArrows);
finally
RECURSE_FLAG := FALSE;
end;
end;
end;
*)
end;
procedure TCustomEnhListView.WMDrawHeader(var Message: TWMDrawItem);
var
State: TOwnerDrawState;
DoDefaultDrawing: boolean;
SavedDC: integer;
begin { CNDrawItem }
if FCanvas = NIL then exit;
with Message.DrawItemStruct^ do
begin
Message.Result := 1;
{$IFDEF DFS_COMPILER_5_UP}
State := TOwnerDrawState(LongRec(itemState).Lo);
{$ELSE}
State := TOwnerDrawState(WordRec(LongRec(itemState).Lo).Lo);
{$ENDIF}
SavedDC := SaveDC(hDC);
FCanvas.Handle := hDC;
try
FCanvas.Font := Font;
FCanvas.Brush := Brush;
DoDefaultDrawing := FALSE;
ProcessDrawHeaderMsg(itemID, rcItem, State, DoDefaultDrawing);
finally
FCanvas.Handle := 0;
RestoreDC(hDC, SavedDC);
end;
end;
end;
procedure TCustomEnhListView.ProcessDrawHeaderMsg(Index: Integer; Rect: TRect;
State: TOwnerDrawState; var DefaultDrawing: boolean);
begin
FCanvas.Font.Assign(Font);
FCanvas.Brush.Assign(Brush);
FCanvas.Brush.Style := bsClear;
FCanvas.Brush.Color := clBtnFace;
DefaultDrawing := csDesigning in ComponentState;
if not (csDesigning in ComponentState) then
DrawHeader(FCanvas, Index, Rect, odSelected in State, DefaultDrawing);
if DefaultDrawing then
DefaultDrawHeader(FCanvas, Index, Rect, odSelected in State);
end;
procedure TCustomEnhListView.DefaultDrawHeader(var Canvas: TCanvas;
Index: Integer; var Rect: TRect; Selected: boolean);
var
TheColumn: TListColumn;
Offset: integer;
R, CR: TRect;
Bmp: TBitmap;
begin
(******************************************************************************)
(* NOTE: This method is overriden and replaced in TExtListView. That means *)
(* that if changes are made here, they will also need to be made in *)
(* ExtListView.pas' DefaultDrawHeader method. *)
(******************************************************************************)
if not Selected then
InflateRect(Rect, -2, -2);
Canvas.FillRect(Rect);
if Selected then
InflateRect(Rect, -2, -2);
if (Index >= 0) and (Index < Columns.Count) then
begin
// Don't use ActualColumn[] here! That's for SubItem foolery, not header.
TheColumn := Columns[Index];
if Selected then
begin
inc(Rect.Top);
inc(Rect.Left);
end;
R := Rect;
case TheColumn.Alignment of
taRightJustify:
Dec(R.Right, 4);
taLeftJustify:
Inc(R.Left, 4);
// taCenter needs no modification
end;
if FShowSortArrows and (LastColumnClicked = Index) and
(AutoColumnSort <> acsNoSort) then
begin
if CurrentSortAscending then
Bmp := FSortUpBmp
else
Bmp := FSortDownBmp;
if TheColumn.Alignment = taRightJustify then
Inc(R.Left, Bmp.Width + 8)
else
Dec(R.Right, Bmp.Width + 8);
{ How big of a rectangle do we have to work with for the text? }
CR := R;
DrawTextEx(FCanvas.Handle, PChar(TheColumn.Caption), -1, CR,
DRAWTEXTEX_FLAGS or DT_CALCRECT or
DRAWTEXTEX_ALIGNMENT[TheColumn.Alignment], NIL);
{ Note that DT_CALCRECT does not adjust for alignment. We must do that }
case TheColumn.Alignment of
taRightJustify:
R.Left := R.Right - (CR.Right - CR.Left);
taCenter:
begin
R.Left := R.Left + (((R.Right - R.Left) - (CR.Right - CR.Left)) div
2);
R.Right := R.Left + (CR.Right - CR.Left);
end;
else // taLeftJustify: doesn't matter, that is what DT_CALCRECT returns
R := CR;
end;
if R.Left < Rect.Left then
R.Left := Rect.Left;
if R.Right > Rect.Right then
R.Right := Rect.Right;
if Selected then
OffsetRect(R, 1, 1);
// Draw the caption in the rect available
DrawTextEx(FCanvas.Handle, PChar(TheColumn.Caption), -1, R,
DRAWTEXTEX_FLAGS or DRAWTEXTEX_ALIGNMENT[TheColumn.Alignment], NIL);
// Draw the sort arrow bitmap
Offset := (Rect.Bottom - Rect.Top - Bmp.Height) div 2;
case TheColumn.Alignment of
taRightJustify:
// Only draw if we have enough room
if (R.Left - Bmp.Width - 8) >= Rect.Left then
Canvas.Draw(R.Left - Bmp.Width - 8, R.Top + Offset, Bmp);
else // taLeftJustify, taCenter
// Only draw if we have enough room
if (R.Right + Bmp.Width + 8) <= Rect.Right then
Canvas.Draw(R.Right + 8, R.Top + Offset, Bmp);
end;
end else begin
if Selected then
OffsetRect(R, 1, 1);
DrawTextEx(FCanvas.Handle, PChar(TheColumn.Caption), -1, R,
DRAWTEXTEX_FLAGS or DRAWTEXTEX_ALIGNMENT[TheColumn.Alignment], NIL);
end;
end;
end;
procedure TCustomEnhListView.SetOnDrawHeader(Value: TLVHDrawItemEvent);
begin
FOnDrawHeader := Value;
SetColumnsOwnerDrawFlag(assigned(FOnDrawHeader) or FShowSortArrows);
end;
procedure TCustomEnhListView.SetColumnsOwnerDrawFlag(OwnerDrawn: boolean);
var
Item: THDItem;
x: integer;
begin
if not HandleAllocated then exit;
for x := 0 to Columns.Count-1 do
begin
Item.Mask := HDI_FORMAT;
if Header_GetItem(HeaderHandle, x, Item) then
begin
if OwnerDrawn then
Item.Fmt := Item.Fmt or HDF_OWNERDRAW
else
Item.Fmt := Item.Fmt and not HDF_OWNERDRAW;
Header_SetItem(HeaderHandle, x, Item);
end;
end;
if OwnerDrawn then
begin
if (FCanvas = NIL) then
FCanvas := TCanvas.Create;
end else begin
if (Style = lvStandard) and (FCanvas <> NIL) then
begin
FCanvas.Free;
FCanvas := NIL;
end;
end;
end;
procedure TCustomEnhListView.SetLastColumnClicked(Value: integer);
var
OldValue: integer;
begin
if Value <> FLastColumnClicked then
begin
OldValue := FLastColumnClicked;
FLastColumnClicked := Value;
// If showing arrows and clicked column changes, we have to get rid of the
// old sorting arrow by causing the header to be repainted.
if FShowSortArrows then
// Can't do this above because FLastColumnClicked is used to paint the
// arrow
InvalidateColumnHeader(OldValue);
end;
end;
function TCustomEnhListView.ActualColumnIndex(Index: integer): integer;
begin
Result := Index;
end;
procedure TCustomEnhListView.InvalidateColumnHeader(Index: integer);
function RealColWidth(i: integer): integer;
{$IFDEF DFS_COMPILER_4_UP}
var
Column: TLVColumn;
{$ENDIF}
begin
{$IFDEF DFS_COMPILER_4_UP}
Column.mask := LVCF_WIDTH;
ListView_GetColumn(Handle, i, Column);
Result := Column.cx;
{$ELSE}
Result := Columns[i].Width;
{$ENDIF}
end;
var
R: TRect;
x: integer;
w: integer;
begin
if (Index < 0) or (Index >= Columns.Count) or (HeaderHandle = 0) then
exit;
w := RealColWidth(Index);
// We have to turn this into the actual column index if drag-drop headers have
// re-arranged stuff in the TExtListView descendant component.
Index := ActualColumnIndex(Index);
Windows.GetClientRect(HeaderHandle, R);
for x := 0 to Columns.Count - 1 do
if ActualColumnIndex(x) < Index then
inc(R.Left, RealColWidth(x));
R.Right := R.Left + w;
// Adjust for shadow
InflateRect(R, -2, -2);
InvalidateRect(HeaderHandle, @R, FALSE);
end;
procedure TCustomEnhListView.WMOwnerDrawColumns(var Message: TMessage);
begin
SetColumnsOwnerDrawFlag(assigned(FOnDrawHeader) or FShowSortArrows);
Update;
end;
function TCustomEnhListView.GetVersion: string;
begin
Result := DFS_COMPONENT_VERSION;
end;
procedure TCustomEnhListView.SetVersion(const Val: string);
begin
{ empty write method, just needed to get it to show up in Object Inspector }
end;
procedure TCustomEnhListView.ResizeColumns(ResizeMethod: TResizeMethod);
var
i: integer;
begin
BeginUpdate;
Columns.BeginUpdate;
try
for i := 0 to Columns.Count - 1 do
if ResizeMethod = rmFitText then
Columns[i].Width := -1
else
Columns[i].Width := -2;
finally
EndUpdate;
Columns.EndUpdate;
end;
end;
function TCustomEnhListView.GetCurrentColumnWidth(Index: integer): integer;
{$IFDEF DFS_COMPILER_4_UP}
var
Column: TLVColumn;
{$ENDIF}
begin
{$IFDEF DFS_COMPILER_4_UP}
if HandleAllocated then
begin
Column.mask := LVCF_WIDTH;
ListView_GetColumn(Handle, ActualColumnIndex(Index), Column);
Result := Column.cx;
end else
Result := ActualColumn[Index].Width;
{$ELSE}
Result := ActualColumn[Index].Width;
{$ENDIF}
end;
{$IFDEF BACKGROUND_FIXED}
procedure TCustomEnhListView.SetBackgroundImage(
const Value: TBitmap);
begin
FBackgroundImage.Assign(Value);
BackgroundImageChanged(Self);
end;
{$ENDIF}
{$IFDEF BACKGROUND_FIXED}
procedure TCustomEnhListView.BackgroundImageChanged(Sender: TObject);
begin
Brush.Bitmap := NIL;
if (FBackgroundImage <> NIL) and (not FBackgroundImage.Empty) then
begin
// Transparent text
ListView_SetTextBkColor(Handle, $FFFFFFFF);
Brush.Bitmap := FBackgroundImage;
end else begin
ListView_SetTextBkColor(Handle, ColorToRGB(Color));
Brush.Color := Color;
end;
Invalidate;
end;
{$ENDIF}
function TCustomEnhListView.GetSmallImages:
{$IFDEF DFS_COMPILER_4_UP} TCustomImageList {$ELSE} TImageList {$ENDIF};
begin
Result := inherited SmallImages;
end;
procedure TCustomEnhListView.SetSmallImages(Val:
{$IFDEF DFS_COMPILER_4_UP} TCustomImageList {$ELSE} TImageList {$ENDIF});
begin
inherited SmallImages := Val;
// If owner drawn, we have to recreate the window so that the WM_MEASUREITEM
// will get sent to us again, and we can handle it to account for image list
// size.
if HandleAllocated and (Style = lvOwnerDrawFixed) and (not (csLoading in
ComponentState)) then
ResetOwnerDrawHeight;
end;
procedure TCustomEnhListView.HeaderWndProc(var Message: TMessage);
function DisallowColumnResize: boolean;
var
HTI: THDHitTestInfo;
pt: TPoint;
begin
Result := NoColumnResize;
if (not Result) and (Self is TCustomExtListView) then
begin
// get cursor position
GetCursorPos(pt);
// convert to coordinates on header control of the listview
Windows.ScreentoClient(HeaderHandle, pt);
// fill in hittest structure
HTI.flags := HHT_ONHEADER Or HHT_ONDIVIDER;
HTI.point.x := pt.x;
HTI.point.y := pt.y;
// get the header's hit-test info
SendMessage(HeaderHandle, HDM_HITTEST, LongInt(0),LongInt(@HTI));
if (HTI.Item >=0) and (HTI.Item <
TdfsExtListView(Self).ColumnsFormat.Count) then
Result := not TdfsExtListView(Self).ColumnsFormat[HTI.Item].AllowResize;
end;
end;
var
HTI: THDHitTestInfo;
Icon: HICON;
begin
try
with Message do
begin
case Msg of
WM_SETCURSOR:
begin
if DisallowColumnResize then
// if NoColumnResize then
begin
Icon := GetClassLong(FHeaderHandle, GCL_HICON);
if Icon = 0 then
Icon := LoadCursor(0, IDC_ARROW);
SetCursor(Icon);
exit;
end;
end;
WM_NCHITTEST:
begin
with TWMNCHitTest(Message) do
if csDesigning in ComponentState then
begin
Result := Windows.HTTRANSPARENT;
exit;
end
else if DisallowColumnResize then
begin
HTI.Point := Point(LoWord(Message.LParam), HiWord(Message.LParam));
Windows.ScreenToClient(FHeaderHandle, HTI.Point);
SendMessage(FHeaderHandle, HDM_HITTEST, 0, integer(@HTI));
if ((HTI.Flags and HHT_ONHeader) = 0) then
begin
Result := Windows.HTNOWHERE;
exit;
end;
end;
end;
WM_NCDESTROY:
begin
Result := CallWindowProc(FOldHeaderWndProc, FHeaderHandle, Msg, WParam, LParam);
FHeaderHandle := 0;
FOldHeaderWndProc := nil;
Exit;
end;
end;
Result := CallWindowProc(FOldHeaderWndProc, FHeaderHandle, Msg, WParam, LParam);
end;
except
Application.HandleException(Self);
end;
end;
procedure TCustomEnhListView.WMParentNotify(var Message: TWMParentNotify);
begin
with Message do
if (Event = WM_CREATE) and (FHeaderHandle = 0) then
begin
FHeaderHandle := ChildWnd;
FOldHeaderWndProc := Pointer(GetWindowLong(FHeaderHandle, GWL_WNDPROC));
SetWindowLong(FHeaderHandle, GWL_WNDPROC, LongInt(FHeaderInstance));
end;
inherited;
end;
procedure TCustomEnhListView.KeyDown(var Key: Word; Shift: TShiftState);
begin
inherited;
// Ctrl + causes all columns to change size as if their dividers had been
// double-clicked. Can't have that.
if NoColumnResize and (Key = VK_ADD) and (Shift = [ssCtrl]) then
Key := VK_SUBTRACT;
end;
procedure TCustomEnhListView.ResetOwnerDrawHeight;
var
r: TRect;
wp: TWindowPos;
begin
// Found this code on www.codeguru.com in an article talking about how to get
// an owner draw listview to ask for the item height (WM_MEASUREITEM) again.
GetWindowRect(Handle, r);
wp.hwnd := Handle;
wp.cx := Width;
wp.cy := Height;
wp.flags := SWP_NOACTIVATE or SWP_NOMOVE or SWP_NOOWNERZORDER or SWP_NOZORDER;
SendMessage(Handle, WM_WINDOWPOSCHANGED, 0, LPARAM(@wp));
end;
procedure TCustomEnhListView.MoveItem(OriginalIndex, NewIndex: Integer);
var
Selected, Focused: boolean;
ListItem: TListItem;
begin
if ((OriginalIndex < 0) or (OriginalIndex > Items.Count)) or
((NewIndex < 0) or (NewIndex > Items.Count)) then
Exit;
BeginUpdate;
try
Selected := Items[OriginalIndex].Selected;
Focused := Items[OriginalIndex].Focused;
if NewIndex < OriginalIndex then
inc(OriginalIndex);
if (NewIndex > OriginalIndex) then
ListItem := Items.Insert(NewIndex + 1)
else
ListItem := Items.Insert(NewIndex);
ListItem.Assign(Items[OriginalIndex]);
Items.Delete(OriginalIndex);
ListItem.Selected := Selected;
ListItem.Focused := Focused;
finally
EndUpdate;
end;
end;
procedure TCustomEnhListView.KeyUp(var Key: Word; Shift: TShiftState);
var
PrevSearch: string;
Ascii: array[0..1] of char;
KBState: TKeyboardState;
begin
inherited;
if ColumnSearch then
begin
GetKeyboardState(KBState);
if (ToAscii(Key, 0, KBState, Ascii, 0) = 1) and (Ascii[0] in [#32..#127]) then
begin
PrevSearch := FSearchStr; // remember searchstring
if GetTickCount > FSearchTickCount + 1000 then // last search over one second ago?
PrevSearch := ''; // reset searchstring
FSearchStr := PrevSearch + Ascii[0]; // Append searchstring
FSearchTickCount := GetTickCount; // remember last search time
Key := 0; // prevent automatic search on first column
if not StringSelect(FSearchStr, LastColumnClicked) then
begin
MessageBeep(MB_ICONSTOP);
FSearchStr := PrevSearch;
end;
end;
end;
end;
function TCustomEnhListView.StringSelect(FindStr: string; ColumnIndex: Integer): boolean;
var
SearchLen,
SearchIndex,
SearchStart: Integer;
begin
Result := FALSE;
SearchLen := Length(FindStr);
if Assigned(Selected) then // determine starting item
SearchStart := Selected.Index + 1
else
SearchStart := 1;
// Searches from currently selected item to last item
// and from first item to currently selected item until result(found)
SearchIndex := 0;
while (SearchIndex < Items.Count) and not Result do
begin
if ColumnIndex = 0 then // find main or subitem?
Result := AnsiCompareText(Copy(Items[(SearchStart + SearchIndex) mod
Items.Count].Caption, 0, SearchLen), FindStr) = 0
else
Result := AnsiCompareText(Copy(Items[(SearchStart + SearchIndex) mod
Items.Count].SubItems[ColumnIndex - 1], 0, SearchLen), FindStr) = 0;
Inc(SearchIndex);
end;
if Result then
begin
Selected := Items[(SearchStart + SearchIndex - 1) mod Items.Count];
ItemFocused := Selected;
end;
end;
function TCustomEnhListView.SubStringSelect(FindStr: string;
ColumnIndex: Integer): boolean;
var
SearchIndex,
SearchStart: Integer;
begin
Result := FALSE;
if Assigned(Selected) then // determine starting item
SearchStart := Selected.Index + 1
else
SearchStart := 1;
// Searches from currently selected item to last item
// and from first item to currently selected item until result(found)
SearchIndex := 0;
while (SearchIndex < Items.Count) and not Result do
begin
if ColumnIndex = 0 then // find main or subitem?
Result := Pos(FindStr, Items[(SearchStart + SearchIndex) mod
Items.Count].Caption) > 0
else
Result := Pos(FindStr, Items[(SearchStart + SearchIndex) mod
Items.Count].SubItems[ColumnIndex - 1]) > 0;
Inc(SearchIndex);
end;
if Result then
begin
Selected := Items[(SearchStart + SearchIndex - 1) mod Items.Count];
ItemFocused := Selected;
end;
end;
{$IFNDEF DFS_COMPILER_4_UP}
procedure TCustomEnhListView.GetImageIndex(Item: TListItem);
begin
if Assigned(FOnGetImageIndex) then
FOnGetImageIndex(Self, Item);
end;
{$ENDIF}
procedure TCustomEnhListView.Notification(AComponent: TComponent; Operation: TOperation);
begin
if (Operation = opRemove) and (AComponent = SmallImages) then
SmallImages := NIL;
inherited Notification(AComponent, Operation);
end;
initialization
DefDraw_TextOffset := 4;
DefDraw_ImageOffset := 2;
end.