unit ObjectListView;
interface

uses
	Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
	ComCtrls, StdCtrls, CommCtrl;

type
	TItemEventType = (ieOpen, ieClose, ieClear, ieUpdate, ieDestroying, ieAdd, ieEdit);
	TItemEvent = procedure(aSender: TObject; anItem: TListItem;
		anEvent: TItemEventType) of object;
	TObjectListView = class(TCustomListView)
	private
		FPreventEvents: Boolean;
		FAddButton,
		FEditButton,
		FDeleteButton: TButton;
		FOpenedItem: TListItem;
		FEventItem: TListItem;
		FOnItemEvent: TItemEvent;

		procedure SetActionButton(var aButton: TButton; aValue: TButton; aClick: TNotifyEvent);
		procedure SetAddButton(aValue: TButton);
		procedure SetEditButton(aValue: TButton);
		procedure SetDeleteButton(aValue: TButton);

		procedure AddButtonClick(aSender: TObject);
		procedure EditButtonClick(aSender: TObject);
		procedure DeleteButtonClick(aSender: TObject);
		procedure SetOpenedItem(anItem: TListItem);
	protected
		procedure Delete(anItem: TListItem); override;
		procedure Change(anItem: TListItem; aChange: Integer); override;
		procedure ItemEvent(anItem: TListItem; anEvent: TItemEventType); dynamic;
		procedure DoItemEvent(anItem: TListItem; anEvent: TItemEventType);
	public
		property OpenedItem: TListItem read FOpenedItem;
		function AddObject(anObject: TObject; aSelect: Boolean): TListItem;
		procedure SetObjectList(aList: TList);
		procedure UpdateItem(anItem: TListItem);
		procedure SelectItem(anItem: TListItem);
	published
		property AddButton: TButton read FAddButton write SetAddButton;
		property EditButton: TButton read FEditButton write SetEditButton;
		property DeleteButton: TButton read FDeleteButton write SetDeleteButton;
		property OnItemEvent: TItemEvent read FOnItemEvent write FOnItemEvent;

    property Align;
    property BorderStyle;
    property Color;
    property ColumnClick;
    property OnClick;
    property OnDblClick;
		property Columns;
		property Ctl3D;
		property DragMode;
		property ReadOnly default False;
		property Enabled;
		property Font;
		property GridLines;
		property HideSelection;
		property HotTrack;
		property IconOptions;
		property Items;
		property AllocBy;
		property MultiSelect;
		property RowSelect;
		property OnChange;
		property OnChanging;
		property OnColumnClick;
		property OnCompare;
		property OnDeletion;
		property OnEdited;
		property OnEditing;
		property OnEnter;
		property OnExit;
		property OnInsert;
		property OnDragDrop;
		property OnDragOver;
		property DragCursor;
		property OnStartDrag;
		property OnEndDrag;
		property OnMouseDown;
		property OnMouseMove;
		property OnMouseUp;
		property ParentColor default False;
		property ParentFont;
		property ParentShowHint;
		property ShowHint;
		property PopupMenu;
		property ShowColumnHeaders;
		property SortType;
		property TabOrder;
		property TabStop default True;
		property ViewStyle;
		property Visible;
		property OnKeyDown;
		property OnKeyPress;
		property OnKeyUp;
		property LargeImages;
		property SmallImages;
		property StateImages;
	end;


implementation

procedure TObjectListView.AddButtonClick(aSender: TObject);
begin
	DoItemEvent(FOpenedItem, ieAdd);
end;

procedure TObjectListView.EditButtonClick(aSender: TObject);
begin
	DoItemEvent(FOpenedItem, ieEdit);
end;

procedure TObjectListView.DeleteButtonClick(aSender: TObject);
var
	aSaveIndex: Integer;
begin
	if FOpenedItem <> nil then begin
		aSaveIndex := FOpenedItem.Index;
		Delete(FOpenedItem);
		if aSaveIndex > Items.Count - 1 then
			aSaveIndex := Items.Count - 1;
		if aSaveIndex >= 0 then
			SelectItem(Items[aSaveIndex]);
	end;
end;

procedure TObjectListView.SetActionButton(var aButton: TButton; aValue: TButton; aClick: TNotifyEvent);
begin
	if aButton <> aValue then 
		if csDesigning in ComponentState then
			aButton := aValue
		else begin
			if Assigned(aButton) then
				aButton.OnClick := nil;
			aButton := aValue;
			if Assigned(aValue) then
				aValue.OnClick := aClick;
		end;
end;

procedure TObjectListView.SetAddButton(aValue: TButton);
begin
	SetActionButton(FAddButton, aValue, AddButtonClick);
end;

procedure TObjectListView.SetEditButton(aValue: TButton);
begin
	SetActionButton(FEditButton, aValue, EditButtonClick);
end;

procedure TObjectListView.SetDeleteButton(aValue: TButton);
begin
	SetActionButton(FDeleteButton, aValue, DeleteButtonClick);
end;

procedure TObjectListView.ItemEvent(anItem: TListItem; anEvent: TItemEventType);
begin
	if Assigned(FOnItemEvent) then
		FOnItemEvent(Self, anItem, anEvent);
end;

procedure TObjectListView.DoItemEvent(anItem: TListItem; anEvent: TItemEventType);
var
	aSaveItem: TListItem;
begin
	if FPreventEvents or (csDestroying in ComponentState) then
		Exit;
	aSaveItem := FEventItem;
	try
		FEventItem := anItem;
		ItemEvent(anItem, anEvent);
		if (FEventItem <> nil) and (anEvent in [ieClose, ieEdit]) then
			DoItemEvent(anItem, ieUpdate);
	finally
		FEventItem := aSaveItem
	end;
end;

procedure TObjectListView.SetOpenedItem(anItem: TListItem);
begin
	if anItem <> FOpenedItem then begin
		if Assigned(FOpenedItem) and Assigned(anItem) then
			DoItemEvent(FOpenedItem, ieClose);
		FOpenedItem := anItem;
		if anItem <> nil then
			DoItemEvent(anItem, ieOpen)
		else
			DoItemEvent(nil, ieClear);
	end;
end;

procedure TObjectListView.SelectItem(anItem: TListItem);
begin
	if anItem <> nil then begin
		ItemFocused := anItem;
		anItem.Selected := True;
	end;
end;

procedure TObjectListView.Delete(anItem: TListItem);
begin
	if anItem.Data <> nil then begin
		if anItem = FEventItem then
			FEventItem := nil;
		if anItem = FOpenedItem then
			FOpenedItem := nil;
		DoItemEvent(anItem, ieDestroying);
		anItem.Data := nil;
		if Items.Count = 1 then
			DoItemEvent(nil, ieClear);
	end;
	inherited Delete(anItem);
end;

procedure TObjectListView.Change(anItem: TListItem; aChange: Integer);
begin
	if (aChange = LVIF_STATE) and (anItem.Focused and anItem.Selected) then
		SetOpenedItem(anItem);
end;

function TObjectListView.AddObject(anObject: TObject; aSelect: Boolean): TListItem;
begin
	Result := Items.Add;
	Result.Data := anObject;
	DoItemEvent(Result, ieUpdate);
	if aSelect then
		SelectItem(Result);
end;

procedure TObjectListView.SetObjectList(aList: TList);
var
	i: Integer;
	anItem: TListItem;
begin
	FOpenedItem := nil;
	Items.BeginUpdate;
	try
		FPreventEvents := True;
		try
			Items.Clear;
		finally
			FPreventEvents := False;
		end;
		for i := 0 to aList.Count - 1 do begin
			anItem := Items.Add;
			anItem.Data := aList[i];
			DoItemEvent(anItem, ieUpdate);
		end;
	finally
		Items.EndUpdate
	end;
end;

procedure TObjectListView.UpdateItem(anItem: TListItem);
var
	i: Integer;
begin
	if Assigned(anItem) then
		DoItemEvent(anItem, ieUpdate)
	else
		for i := 0 to Items.Count - 1 do
			DoItemEvent(Items[i], ieUpdate);
end;

end.
