summaryrefslogtreecommitdiff
path: root/src/gui/fpg_listview.pas
diff options
context:
space:
mode:
Diffstat (limited to 'src/gui/fpg_listview.pas')
-rw-r--r--src/gui/fpg_listview.pas1753
1 files changed, 1753 insertions, 0 deletions
diff --git a/src/gui/fpg_listview.pas b/src/gui/fpg_listview.pas
new file mode 100644
index 00000000..acb4e337
--- /dev/null
+++ b/src/gui/fpg_listview.pas
@@ -0,0 +1,1753 @@
+{
+ fpGUI - Free Pascal GUI Toolkit
+
+ Copyright (C) 2006 - 2008 See the file AUTHORS.txt, included in this
+ distribution, for details of the copyright.
+
+ See the file COPYING.modifiedLGPL, included in this distribution,
+ for details about redistributing fpGUI.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+ Description:
+ Defines a Listview control.
+}
+
+unit fpg_listview;
+
+{$mode objfpc}{$H+}
+
+interface
+
+uses
+ Classes,
+ SysUtils,
+ fpg_base,
+ fpg_main,
+ fpg_widget,
+ fpg_scrollbar;
+
+type
+ TfpgListView = class;
+ TfpgLVItem = class;
+ TfpgLVColumns = class;
+ TfpgLVColumn = class;
+
+ TfpgLVColumnClickEvent = procedure(Listview: TfpgListView; Column: TfpgLVColumn; Button: Integer) of object;
+
+
+ TfpgLVColumn = class(TComponent)
+ private
+ FAlignment: TAlignment;
+ FCaptionAlignment: TAlignment;
+ FDown: Boolean;
+ FAutoSize: Boolean;
+ FCaption: String;
+ FClickable: Boolean;
+ FColumnIndex: Integer;
+ FColumns: TfpgLVColumns;
+ FHeight: Integer;
+ FResizable: Boolean;
+ FVisible: Boolean;
+ FWidth: Integer;
+ procedure SetAlignment(const AValue: TAlignment);
+ procedure SetAutoSize(const AValue: Boolean);
+ procedure SetCaption(const AValue: String);
+ procedure SetCaptionAlignment(const AValue: TAlignment);
+ procedure SetColumnIndex(const AValue: Integer);
+ procedure SetHeight(const AValue: Integer);
+ procedure SetResizable(const AValue: Boolean);
+ procedure SetVisible(const AValue: Boolean);
+ procedure SetWidth(const AValue: Integer);
+ public
+ constructor Create(AColumns: TfpgLVColumns); reintroduce;
+ destructor Destroy; override;
+ property Caption: String read FCaption write SetCaption;
+ property CaptionAlignment: TAlignment read FCaptionAlignment write SetCaptionAlignment;
+ property Alignment: TAlignment read FAlignment write SetAlignment;
+ property AutoSize: Boolean read FAutoSize write SetAutoSize;
+ property Width: Integer read FWidth write SetWidth;
+ property Height: Integer read FHeight write SetHeight;
+ property Visible: Boolean read FVisible write SetVisible;
+ property ColumnIndex: Integer read FColumnIndex write SetColumnIndex;
+ property Clickable: Boolean read FClickable write FClickable;
+ property Resizable: Boolean read FResizable write SetResizable;
+ end;
+
+
+ TfpgLVColumns = class(TPersistent)
+ private
+ FListView: TfpgListView;
+ FColumns: TList;
+ function GetColumn(AIndex: Integer): TfpgLVColumn;
+ procedure SetColumn(AIndex: Integer; const AValue: TfpgLVColumn);
+ public
+ constructor Create(AListView: TfpgListView);
+ destructor Destroy; override;
+ function Add(AColumn: TfpgLVColumn): Integer;
+ procedure Clear;
+ procedure Delete(AIndex: Integer);
+ procedure Insert(AColumn: TfpgLVColumn; AIndex: Integer);
+ function Count: Integer;
+ property Column[AIndex: Integer]: TfpgLVColumn read GetColumn write SetColumn;
+ end;
+
+
+ TfpgLVItemState = set of (lisFocused, lisSelected, lisHotTrack);
+
+ TfpgLVItemPaintPart = set of (lvppBackground, lvppIcon, lvppText, lvppFocused);
+
+ TfpgLVPaintColumnEvent = procedure(ListView: TfpgListView; Canvas: TfpgCanvas; Column: TfpgLVColumn;
+ ColumnIndex: Integer; Area: TfpgRect; var PaintPart: TfpgLVItemPaintPart) of object;
+ TfpgLVPaintItemEvent = procedure(ListView: TfpgListView; Canvas: TfpgCanvas; Item: TfpgLVItem;
+ ItemIndex: Integer; Area:TfpgRect; var PaintPart: TfpgLVItemPaintPart) of object;
+ TfpgLVItemSelectEvent = procedure(ListView: TfpgListView; Item: TfpgLVItem;
+ ItemIndex: Integer; Selected: Boolean) of object;
+
+
+ IfpgLVItemViewer = interface
+ procedure ItemDeleted(AIndex: Integer);
+ procedure ItemAdded(AIndex: Integer);
+ procedure ItemChanged(AIndex: Integer);
+ procedure ItemsUpdated;
+ end;
+
+
+ TfpgLVItems = class(TObject)
+ private
+ FUpdateCount: Integer;
+ FColumns: TfpgLVColumns;
+ FCurrentIndexOf: Integer;
+ FViewers: TList;
+ FItems: TList;
+ function GetCapacity: Integer;
+ function GetItem(AIndex: Integer): TfpgLVItem;
+ procedure SetCapacity(const AValue: Integer);
+ procedure SetItem(AIndex: Integer; const AValue: TfpgLVItem);
+ procedure AddViewer(AValue: IfpgLVItemViewer);
+ procedure DeleteViewer(AValue: IfpgLVItemViewer);
+ // interface method triggers
+ procedure DoChange(AItem: TfpgLVItem);
+ procedure DoAdd(AItem: TfpgLVItem);
+ procedure DoDelete(AItem: TfpgLVItem);
+ procedure DoEndUpdate;
+ public
+ constructor Create(AViewer: IfpgLVItemViewer);
+ destructor Destroy; override;
+ function Add(AItem: TfpgLVItem): Integer;
+ function Count: Integer;
+ procedure Clear;
+ procedure Delete(AIndex: Integer);
+ function IndexOf(AItem: TfpgLVItem): Integer;
+ procedure InsertItem(AItem: TfpgLVItem; AIndex: Integer);
+ procedure BeginUpdate;
+ procedure EndUpdate;
+ property Capacity: Integer read GetCapacity write SetCapacity;
+ property Columns: TfpgLVColumns read FColumns;
+ property Item[AIndex: Integer]: TfpgLVItem read GetItem write SetItem;
+ end;
+
+
+ TfpgLVItem = class(TObject)
+ private
+ FCaption: String;
+ FItems: TfpgLVItems;
+ FSubItems: TStrings;
+ FUserData: Pointer;
+ function GetSelected(ListView: TfpgListView): Boolean;
+ procedure SetCaption(const AValue: String);
+ procedure SetSelected(ListView: TfpgListView; const AValue: Boolean);
+ procedure SubItemsChanged(Sender: TObject);
+ public
+ constructor Create(Items: TfpgLVItems); virtual;
+ destructor Destroy; override;
+ property Caption: String read FCaption write SetCaption;
+ property UserData: Pointer read FUserData write FUserData;
+ property SubItems: TStrings read FSubItems;
+ property Selected[ListView: TfpgListView]: Boolean read GetSelected write SetSelected;
+ end;
+
+
+ TfpgListView = class(TfpgWidget, IfpgLVItemViewer)
+ private
+ FItemIndex: Integer;
+ FMultiSelect: Boolean;
+ FOnPaintColumn: TfpgLVPaintColumnEvent;
+ FOnSelectionChanged: TfpgLVItemSelectEvent;
+ FShiftCount: Integer;
+ FSelectionFollowsFocus: Boolean;
+ FSelectionShiftStart: Integer;
+ FOnColumnClick: TfpgLVColumnClickEvent;
+ FSelected: TList;
+ FOldSelected: TList;
+ FUpdateCount: Integer;
+ FVScrollBar: TfpgScrollBar;
+ FHScrollBar: TfpgScrollBar;
+ FColumns: TfpgLVColumns;
+ FItems: TfpgLVItems;
+ FOnPaintItem: TfpgLVPaintItemEvent;
+ FShowHeaders: Boolean;
+ FResizingColumn: TfpgLVColumn;
+ FMouseDownPoint: TPoint;
+ FScrollBarNeedsUpdate: Boolean;
+ function GetItemHeight: Integer;
+ procedure SetItemIndex(const AValue: Integer);
+ procedure SetItems(const AValue: TfpgLVItems);
+ procedure SetMultiSelect(const AValue: Boolean);
+ procedure SetOnColumnClick(const AValue: TfpgLVColumnClickEvent);
+ procedure SetShowHeaders(const AValue: Boolean);
+ procedure VScrollChange(Sender: TObject; Position: Integer);
+ procedure HScrollChange(Sender: TObject; Position: Integer);
+ // interface methods
+ procedure ItemDeleted(AIndex: Integer);
+ procedure ItemAdded(AIndex: Integer);
+ procedure ItemChanged(AIndex: Integer);
+ procedure ItemsUpdated;
+ //
+ function GetVisibleColumnsWidth: Integer;
+ function GetItemAreaHeight: Integer;
+ procedure StartShiftSelection;
+ procedure EndShiftSelection;
+ procedure SelectionSetRangeEnabled(AStart, AEnd: Integer; AValue: Boolean);
+ procedure SelectionToggleRange(AStart, AEnd: Integer; const ShiftState: TShiftState; IgnoreStartIndex: Boolean);
+ procedure SelectionClear;
+ function ItemGetSelected(const AItem: TfpgLVItem): Boolean;
+ procedure ItemSetSelected(const AItem: TfpgLVItem; const AValue: Boolean);
+ function ItemGetFromPoint(const X, Y: Integer): TfpgLVItem;
+ function ItemGetRect(AIndex: Integer): TfpgRect;
+ function ItemIndexFromY(Y: Integer): Integer;
+ function HeaderHeight: Integer;
+ procedure DoRepaint;
+ procedure DoColumnClick(Column: TfpgLVColumn; Button: Integer);
+ procedure HandleHeaderMouseMove(x, y: Integer; btnstate: word; Shiftstate: TShiftState);
+ protected
+ procedure MsgPaint(var msg: TfpgMessageRec); message FPGM_PAINT;
+ procedure HandleMouseScroll(x, y: integer; shiftstate: TShiftState; delta: smallint); override;
+ procedure HandleLMouseDown(x, y: integer; shiftstate: TShiftState); override;
+ procedure HandleRMouseDown(x, y: integer; shiftstate: TShiftState); override;
+ procedure HandleLMouseUp(x, y: integer; shiftstate: TShiftState); override;
+ procedure HandleRMouseUp(x, y: integer; shiftstate: TShiftState); override;
+ procedure HandleMouseMove(x, y: integer; btnstate: word; shiftstate: TShiftState); override;
+ procedure HandleKeyPress(var keycode: word; var shiftstate: TShiftState; var consumed: boolean); override;
+ procedure HandleKeyRelease(var keycode: word; var shiftstate: TShiftState; var consumed: boolean); override;
+ procedure HandlePaint; override;
+ procedure HandleResize(awidth, aheight: TfpgCoord); override;
+ procedure PaintHeaders; virtual;
+ procedure PaintItems; virtual;
+ procedure UpdateScrollBarPositions; virtual;
+ public
+ constructor Create(AOwner: TComponent); override;
+ destructor Destroy; override;
+ function GetClientRect: TfpgRect; override;
+ procedure BeginUpdate;
+ procedure EndUpdate;
+ procedure MakeItemVisible(AIndex: Integer; PartialOK: Boolean = False);
+ function ItemAdd: TfpgLVItem;
+ published
+ property Columns: TfpgLVColumns read FColumns;
+ property HScrollBar: TfpgScrollBar read FHScrollBar;
+ property ItemHeight: Integer read GetItemHeight;
+ property ItemIndex: Integer read FItemIndex write SetItemIndex;
+ property Items: TfpgLVItems read FItems write SetItems;
+ property MultiSelect: Boolean read FMultiSelect write SetMultiSelect;
+ property ParentShowHint;
+ property SelectionFollowsFocus: Boolean read FSelectionFollowsFocus write FSelectionFollowsFocus;
+ property ShowHeaders: Boolean read FShowHeaders write SetShowHeaders;
+ property ShowHint;
+ property TabOrder;
+ property VScrollBar: TfpgScrollBar read FVScrollBar;
+ property OnColumnClick: TfpgLVColumnClickEvent read FOnColumnClick write SetOnColumnClick;
+ property OnPaintColumn: TfpgLVPaintColumnEvent read FOnPaintColumn write FOnPaintColumn;
+ property OnPaintItem: TfpgLVPaintItemEvent read FOnPaintItem write FOnPaintItem;
+ property OnSelectionChanged: TfpgLVItemSelectEvent read FOnSelectionChanged write FOnSelectionChanged;
+ end;
+
+
+implementation
+
+uses
+ fpg_constants;
+
+
+type
+ // used to access protected methods
+ TfpgScrollbarFriend = class(TfpgScrollbar)
+ end;
+
+{ TfpgLVItems }
+
+function Min(AInt, BInt: Integer): Integer;
+begin
+ if AInt < Bint then
+ Result := AInt
+ else Result := BInt;
+end;
+
+function Max(AInt, BInt: Integer): INteger;
+begin
+ if AInt > Bint then
+ Result := AInt
+ else Result := BInt;
+end;
+
+function TfpgLVItems.GetItem(AIndex: Integer): TfpgLVItem;
+begin
+ Result := TfpgLVItem(FItems.Items[AIndex]);
+end;
+
+function TfpgLVItems.GetCapacity: Integer;
+begin
+ Result := FItems.Capacity;
+end;
+
+procedure TfpgLVItems.SetCapacity(const AValue: Integer);
+begin
+ FItems.Capacity := AValue;
+end;
+
+procedure TfpgLVItems.SetItem(AIndex: Integer; const AValue: TfpgLVItem);
+begin
+ FItems.Items[AIndex] := AValue;
+end;
+
+procedure TfpgLVItems.AddViewer(AValue: IfpgLVItemViewer);
+begin
+ if AValue <> nil then
+ FViewers.Add(AValue);
+end;
+
+procedure TfpgLVItems.DeleteViewer(AValue: IfpgLVItemViewer);
+var
+ AIndex: Integer;
+begin
+ AIndex := FViewers.IndexOf(AValue);
+ if AIndex > -1 then
+ begin
+ FViewers.Delete(AIndex);
+ end;
+ if FViewers.Count = 0 then
+ Free;
+end;
+
+procedure TfpgLVItems.DoChange(AItem: TfpgLVItem);
+var
+ I: Integer;
+ AIndex: Integer;
+begin
+ if FUpdateCount > 0 then
+ Exit;
+ AIndex := IndexOf(AItem);
+ for I := 0 to FViewers.Count -1 do
+ begin
+ IfpgLVItemViewer(FViewers.Items[I]).ItemChanged(AIndex);
+ end;
+end;
+
+procedure TfpgLVItems.DoAdd(AItem: TfpgLVItem);
+var
+ I: Integer;
+ AIndex: Integer;
+begin
+ if FUpdateCount > 0 then
+ Exit;
+ AIndex := IndexOf(AItem);
+ for I := 0 to FViewers.Count -1 do
+ begin
+ IfpgLVItemViewer(FViewers.Items[I]).ItemAdded(AIndex);
+ end;
+end;
+
+procedure TfpgLVItems.DoDelete(AItem: TfpgLVItem);
+var
+ I: Integer;
+ AIndex: Integer;
+begin
+ if FUpdateCount > 0 then
+ Exit;
+ AIndex := IndexOf(AItem);
+ for I := 0 to FViewers.Count -1 do
+ begin
+ IfpgLVItemViewer(FViewers.Items[I]).ItemDeleted(AIndex);
+ end;
+end;
+
+procedure TfpgLVItems.DoEndUpdate;
+var
+ I: Integer;
+begin
+ if FUpdateCount > 0 then
+ Exit;
+ for I := 0 to FViewers.Count -1 do
+ begin
+ IfpgLVItemViewer(FViewers.Items[I]).ItemsUpdated;
+ end;
+end;
+
+constructor TfpgLVItems.Create(AViewer: IfpgLVItemViewer);
+begin
+ FItems := TList.Create;
+ FViewers := TList.Create;
+ AddViewer(AViewer);
+end;
+
+destructor TfpgLVItems.Destroy;
+begin
+ FItems.Free;
+ FViewers.Free;
+ inherited Destroy;
+end;
+
+function TfpgLVItems.Add(AItem: TfpgLVItem): Integer;
+begin
+ Result := Count;
+ InsertItem(AItem, Count);
+ DoAdd(AItem);
+end;
+
+function TfpgLVItems.Count: Integer;
+begin
+ Result := FItems.Count;
+end;
+
+procedure TfpgLVItems.Clear;
+var
+ i: integer;
+begin
+ for i := FItems.Count-1 downto 0 do
+ Delete(i);
+ FItems.Clear;
+end;
+
+procedure TfpgLVItems.Delete(AIndex: Integer);
+begin
+ DoDelete(GetItem(AIndex));
+ FItems.Delete(AIndex);
+end;
+
+function TfpgLVItems.IndexOf(AItem: TfpgLVItem): Integer;
+begin
+ Result := -1;
+ // this checks for a index close to the old one whic can speed up
+ // search significantly when we are using indexof in a for loop
+ if (FCurrentIndexOf > 100) and (FCurrentIndexOf < Count-2) then
+ begin
+ if FItems.Items[FCurrentIndexOf] = Pointer(AItem) then
+ Result := FCurrentIndexOf
+ else if FItems.Items[FCurrentIndexOf+1] = Pointer(AItem) then
+ Result := FCurrentIndexOf+1
+ else if FItems.Items[FCurrentIndexOf-1] = Pointer(AItem) then
+ Result := FCurrentIndexOf-1
+ end;
+ if Result = -1 then
+ Result := FItems.IndexOf(AItem);
+ FCurrentIndexOf := Result;
+end;
+
+procedure TfpgLVItems.InsertItem(AItem: TfpgLVItem; AIndex: Integer);
+begin
+ if AItem.InheritsFrom(TfpgLVItem) then
+ FItems.Insert(AIndex, AItem)
+ else
+ raise Exception.CreateFmt(rsErrItemOfWrongType, ['TfpgLVItem']);
+end;
+
+procedure TfpgLVItems.BeginUpdate;
+begin
+ Inc(FUpdateCount);
+end;
+
+procedure TfpgLVItems.EndUpdate;
+begin
+ Dec(FUpdateCount);
+ if FUpdateCount < 0 then
+ FUpdateCount := 0;
+ if FUpdateCount = 0 then
+ DoEndUpdate;
+end;
+
+{ TfpgLVItem }
+
+procedure TfpgLVItem.SetCaption(const AValue: String);
+begin
+ if FCaption=AValue then
+ Exit;
+ FCaption:=AValue;
+ if Assigned(FItems) then
+ FItems.DoChange(Self);
+end;
+
+function TfpgLVItem.GetSelected(ListView: TfpgListView): Boolean;
+begin
+ Result := ListView.ItemGetSelected(Self);
+end;
+
+procedure TfpgLVItem.SetSelected(ListView: TfpgListView; const AValue: Boolean);
+begin
+ ListView.ItemSetSelected(Self, AValue);
+end;
+
+procedure TfpgLVItem.SubItemsChanged(Sender: TObject);
+begin
+ if Assigned(FItems) then
+ FItems.DoChange(Self);
+end;
+
+constructor TfpgLVItem.Create(Items: TfpgLVItems);
+begin
+ FItems := Items;
+ FSubItems := TStringList.Create;
+ TStringList(FSubItems).OnChange := @SubItemsChanged;
+end;
+
+destructor TfpgLVItem.Destroy;
+begin
+ FSubItems.Free;
+ inherited Destroy;
+end;
+
+{ TfpgListView }
+
+
+procedure TfpgListView.SetShowHeaders(const AValue: Boolean);
+begin
+ if FShowHeaders=AValue then
+ Exit;
+ FShowHeaders:=AValue;
+ DoRePaint;
+end;
+
+
+procedure TfpgListView.VScrollChange(Sender: TObject; Position: Integer);
+begin
+ DoRepaint;
+end;
+
+procedure TfpgListView.HScrollChange(Sender: TObject; Position: Integer);
+begin
+ DoRepaint;
+end;
+
+procedure TfpgListView.SetItems(const AValue: TfpgLVItems);
+begin
+ if AValue = FItems then
+ Exit;
+ AValue.AddViewer(Self);
+ FItems.DeleteViewer(Self);
+ Fitems := AValue;
+end;
+
+procedure TfpgListView.SetMultiSelect(const AValue: Boolean);
+begin
+ if FMultiSelect=AValue then
+ Exit;
+ FMultiSelect:=AValue;
+end;
+
+procedure TfpgListView.SetOnColumnClick(const AValue: TfpgLVColumnClickEvent);
+begin
+ if FOnColumnClick=AValue then
+ Exit;
+ FOnColumnClick:=AValue;
+end;
+
+function TfpgListView.GetItemHeight: Integer;
+begin
+ Result := Canvas.Font.Height + 4;
+end;
+
+procedure TfpgListView.SetItemIndex(const AValue: Integer);
+begin
+ if FItemIndex=AValue then
+ Exit;
+ if (AValue >= -1) and (AValue < FItems.Count) then
+ FItemIndex:=AValue;
+end;
+
+procedure TfpgListView.ItemDeleted(AIndex: Integer);
+begin
+ if FUpdateCount = 0 then
+ DoRePaint;
+end;
+
+procedure TfpgListView.ItemAdded(AIndex: Integer);
+begin
+ if FUpdateCount = 0 then
+ DoRePaint;
+end;
+
+procedure TfpgListView.ItemChanged(AIndex: Integer);
+begin
+ if FUpdateCount = 0 then
+ DoRePaint;
+end;
+
+procedure TfpgListView.ItemsUpdated;
+begin
+ DoRepaint;
+end;
+
+function TfpgListView.GetClientRect: TfpgRect;
+begin
+ Result.Top := 2;
+ Result.Left := 2;
+ Result.SetRight(Width - 2);
+ Result.SetBottom(Height - 2);
+end;
+
+function TfpgListView.GetVisibleColumnsWidth: Integer;
+var
+ I: Integer;
+begin
+ Result := 0;
+ for I := 0 to FColumns.Count-1 do
+ if FColumns.Column[I].Visible then
+ Inc(Result, FColumns.Column[I].Width);
+end;
+
+function TfpgListView.GetItemAreaHeight: Integer;
+begin
+ Result := Height - 4;
+ if ShowHeaders then
+ Dec(Result, HeaderHeight);
+ if FHScrollBar.Visible then
+ Dec(Result,FHScrollBar.Height);
+end;
+
+procedure TfpgListView.StartShiftSelection;
+var
+ I: Integer;
+begin
+ Inc(FShiftCount);
+ if FItems.Count = 0 then
+ Exit;
+ if FShiftCount> 1 then
+ Exit;
+ FSelectionShiftStart := FItemIndex;
+ if FSelectionShiftStart = -1 then
+ Inc(FSelectionShiftStart);
+ FOldSelected.Clear;
+ FOldSelected.Capacity := FSelected.Capacity;
+ for I := 0 to FSelected.Count-1 do
+ begin
+ FOldSelected.Add(FSelected.Items[I]);
+ end;
+end;
+
+procedure TfpgListView.EndShiftSelection;
+begin
+ Dec(FShiftCount);
+ if FShiftCount > 0 then
+ Exit;
+ FSelectionShiftStart := -1;
+ FOldSelected.Clear;
+end;
+
+procedure TfpgListView.SelectionSetRangeEnabled(AStart, AEnd: Integer; AValue: Boolean);
+var
+ TmpI: LongInt;
+ I: LongInt;
+ ShouldShow: Boolean;
+begin
+ if AStart > AEnd then
+ begin
+ TmpI := AStart;
+ AStart := AEnd;
+ AEnd := TmpI;
+ end;
+ FSelected.Clear;
+ FSelected.Capacity := FOldSelected.Capacity;
+ for I := 0 to FOldSelected.Count-1 do
+ begin
+ FSelected.Add(FOldSelected.Items[I]);
+ end;
+ if (AStart < 0) or (AEnd > FItems.Count-1) then
+ Exit;
+ for I := AStart to AEnd do
+ begin
+ ShouldShow := AValue;
+ if FOldSelected.IndexOf(FItems.Item[I]) > -1 then
+ ShouldShow := not AValue;
+
+ if I <> FSelectionShiftStart then
+ ItemSetSelected(FItems.Item[I], ShouldShow);
+ end;
+end;
+
+procedure TfpgListView.SelectionToggleRange(AStart, AEnd: Integer;
+ const ShiftState: TShiftState; IgnoreStartIndex: Boolean);
+var
+ TmpI: Integer;
+ I: LongInt;
+begin
+ TmpI := AStart;
+ if AStart > AEnd then
+ begin
+ AStart := AEnd;
+ AEnd := TmpI;
+ end;
+ if not FMultiSelect then
+ begin
+ SelectionClear;
+ ItemSetSelected(FItems.Item[TmpI], True);
+ Exit;
+ end;
+ if ssShift in ShiftState then
+ for I := AStart to AEnd do
+ begin
+ if not(IgnoreStartIndex and (I = TmpI))
+ then ItemSetSelected(FItems.Item[I], not ItemGetSelected(FItems.Item[I]));
+ end;
+end;
+
+procedure TfpgListView.SelectionClear;
+var
+ Item: TfpgLVItem;
+ I: Integer;
+begin
+ for I := FSelected.Count-1 downto 0 do
+ begin
+ Item := TfpgLVItem(FSelected.Items[I]);
+ FSelected.Delete(I);
+ if Assigned(FOnSelectionChanged) then
+ FOnSelectionChanged(Self, Item, Items.IndexOf(Item), False);
+ end;
+
+end;
+
+
+function TfpgListView.ItemGetSelected(const AItem: TfpgLVItem): Boolean;
+begin
+ Result := FSelected.IndexOf(AItem) > -1;
+end;
+
+procedure TfpgListView.ItemSetSelected(const AItem: TfpgLVItem; const AValue: Boolean);
+var
+ Index: Integer;
+begin
+ Index := FSelected.IndexOf(AItem);
+
+ if AValue and (Index = -1) then
+ FSelected.Add(AItem);
+ if (AValue = False) and (Index <> -1) then
+ FSelected.Delete(Index);
+ if Assigned(FOnSelectionChanged) then
+ FOnSelectionChanged(Self, AItem, Items.IndexOf(AItem), AValue);
+end;
+
+function TfpgListView.ItemGetFromPoint(const X, Y: Integer): TfpgLVItem;
+var
+ Index: Integer;
+ ItemTop: Integer;
+begin
+ Result := nil;
+ ItemTop := (FVScrollBar.Position + Y) -2;
+ if ShowHeaders then
+ Dec(ItemTop, HeaderHeight);
+ Index := ItemTop div ItemHeight;
+ if Index < 0 then
+ Exit;
+ if Index >= FItems.Count then
+ Exit;
+ if FHScrollBar.Position - 2 + X > GetVisibleColumnsWidth then
+ Exit;
+
+ Result := FItems.Item[Index];
+end;
+
+function TfpgListView.ItemGetRect(AIndex: Integer): TfpgRect;
+begin
+ Result.Top := 2 + (AIndex * ItemHeight) - FVScrollBar.Position;
+ if ShowHeaders then
+ Inc(Result.Top, HeaderHeight);
+ Result.Height := ItemHeight;
+ Result.Left := 2 - FHScrollBar.Position;
+ Result.Width := GetVisibleColumnsWidth;
+end;
+
+function TfpgListView.ItemIndexFromY(Y: Integer): Integer;
+var
+ TopPos: Integer;
+begin
+ if ShowHeaders and (Y < HeaderHeight) then
+ Exit(-1);
+
+ TopPos := (FVScrollBar.Position + Y) - 2;
+ if ShowHeaders then
+ Dec(TopPos, HeaderHeight);
+ Result := TopPos div ItemHeight;
+ if Result > Fitems.Count-1 then
+ Result := -1;
+end;
+
+function TfpgListView.HeaderHeight: Integer;
+begin
+ Result := Canvas.Font.Height + 10;
+end;
+
+procedure TfpgListView.DoRepaint;
+begin
+ if FUpdateCount = 0 then
+ RePaint;
+end;
+
+procedure TfpgListView.DoColumnClick(Column: TfpgLVColumn; Button: Integer);
+begin
+ if not Column.Clickable then
+ Exit;
+ if Assigned(FOnColumnClick) then
+ FOnColumnClick(Self, Column, Button);
+
+ Column.FDown := True;
+
+ if FUpdateCount = 0 then
+ begin
+ Canvas.BeginDraw(False);
+ PaintHeaders;
+ Canvas.EndDraw;//(2,2, width-4, Height-4);
+ end;
+end;
+
+procedure TfpgListView.HandleHeaderMouseMove(x, y: Integer; btnstate: word;
+ Shiftstate: TShiftState);
+var
+ I: Integer;
+ curLeft: Integer;
+ curRight: Integer;
+ Column: TfpgLVColumn;
+ LastColumn: TfpgLVColumn;
+ HeaderX: Integer; // this is X from the headers point of view
+ NewMouseCursor: TMouseCursor;
+begin
+ curLeft := 0;
+
+ HeaderX := FHScrollBar.Position - 2 + X;
+ NewMouseCursor := MouseCursor;
+ LastColumn := nil;
+ for I := 0 to FColumns.Count-1 do
+ begin
+ Column := FColumns.Column[I];
+ if not Column.Visible then
+ Continue;
+ curRight := curLeft + Column.Width-1;
+ if Column.Resizable or (Assigned(LastColumn) and LastColumn.Resizable) then
+ begin
+ if (FResizingColumn <> nil) and (FResizingColumn = Column) then
+ begin
+ FResizingColumn.Width := (x + FHScrollBar.Position)- curLeft;
+ DoRepaint;
+ Break;
+ end
+ else begin
+ if (HeaderX >= curLeft) and (HeaderX <= curRight) then // we are within this columns space
+ begin
+ if ((LastColumn <> nil) and (LastColumn.Resizable) and (HeaderX - curLeft < 5))
+ or (Column.Resizable) and (curRight - HeaderX < 5)
+ then
+ begin
+ NewMouseCursor := mcSizeEW;
+ Break;
+ end;
+ end
+ else
+ NewMouseCursor := mcDefault;
+ end;
+ end;
+ LastColumn := Column;
+ Inc(curLeft, Column.Width);
+ end;
+ if not Assigned(FResizingColumn) and Assigned(LastColumn) and LastColumn.Resizable then
+ if (HeaderX - curLeft < 5) and (HeaderX - curLeft >= 0) then
+ NewMouseCursor := mcSizeEW;
+
+ if FResizingColumn <> nil then
+ NewMouseCursor := mcSizeEW;
+
+ if NewMouseCursor <> MouseCursor then
+ MouseCursor := NewMouseCursor;
+
+end;
+
+procedure TfpgListView.MsgPaint(var msg: TfpgMessageRec);
+begin
+ // Optimises painting and prevents Begin[End]Draw and OnPaint event firing
+ // in not needed.
+ if FUpdateCount = 0 then
+ inherited MsgPaint(msg);
+end;
+
+procedure TfpgListView.HandleMouseScroll(x, y: integer;
+ shiftstate: TShiftState; delta: smallint);
+var
+ cRect: TfpgRect;
+begin
+ cRect := GetClientRect;
+ if FShowHeaders then
+ Inc(cRect.Top, HeaderHeight);
+ if FHScrollBar.Visible then
+ Dec(cRect.Height, FHScrollBar.Height);
+ if FVScrollBar.Visible then
+ Dec(cRect.Width, FVScrollBar.Width);
+
+
+ if not PtInRect(cRect, Point(X,Y)) then
+ Exit;
+
+ TfpgScrollbarFriend(FVScrollBar).HandleMouseScroll(x, y, shiftstate, delta);
+end;
+
+procedure TfpgListView.HandleLMouseDown(x, y: integer; shiftstate: TShiftState);
+var
+ Item: TfpgLVItem;
+ cRect: TfpgRect;
+ curLeft, curRight: Integer;
+ I: Integer;
+ Column: TfpgLVColumn;
+ LastColumn: TfpgLVColumn;
+ HeaderX: Integer;
+begin
+ inherited HandleLMouseDown(x, y, shiftstate);
+
+ cRect := GetClientRect;
+
+ FMouseDownPoint := Point(X,Y);
+
+ if not PtInRect(cRect, Point(X,Y)) then
+ Exit;
+
+ if FShowHeaders then
+ begin
+ if (Y < HeaderHeight + cRect.Top) then
+ begin
+ LastColumn := nil;
+ HeaderX := FHScrollBar.Position - 2 + x;
+
+ curLeft := 0;
+ for I := 0 to FColumns.Count-1 do
+ begin
+ Column := FColumns.Column[I];
+ if Column.Visible then
+ begin
+ curRight := curLeft + Column.Width-1;
+ if (HeaderX <= curRight) and (HeaderX >= curLeft) then
+ begin
+ if (MouseCursor = mcSizeEW) then
+ begin
+ if Column.Resizable and (curRight - HeaderX < 5) then
+ FResizingColumn := Column
+ else
+ if Assigned(LastColumn) and LastColumn.Resizable and (HeaderX - curLeft < 5) then
+ FResizingColumn := LastColumn
+ end
+ else // only perform a mouse click if we aren't resizing
+ DoColumnClick(Column, 1);
+ end;
+ Inc(curLeft, Column.Width);
+ end;
+ LastColumn := Column;
+ end;
+ if not Assigned(FResizingColumn) and Assigned(LastColumn) and LastColumn.Resizable then
+ if (HeaderX - curLeft < 5) and (HeaderX - curLeft >= 0) then
+ FResizingColumn := LastColumn;
+ end;
+
+ Inc(cRect.Top, HeaderHeight);
+ end;
+
+ if FHScrollBar.Visible then
+ Dec(cRect.Height, FHScrollBar.Height);
+ if FVScrollBar.Visible then
+ Dec(cRect.Width, FVScrollBar.Width);
+
+ if not PtInRect(cRect, Point(X,Y)) then
+ Exit;
+
+ Item := ItemGetFromPoint(X, Y);
+ if not FMultiSelect then
+ SelectionClear;
+ if Item <> nil then
+ begin
+ FItemIndex := ItemIndexFromY(Y);
+ MakeItemVisible(FItemIndex);
+ if FMultiSelect then
+ begin
+ if not ((ssCtrl in shiftstate) or (ssShift in shiftstate)) then
+ begin
+ SelectionClear;
+ ItemSetSelected(Item, True);
+ end
+ else begin
+ if ssCtrl in shiftstate then
+ ItemSetSelected(Item, not ItemGetSelected(Item));
+ if ssShift in shiftstate then
+ SelectionSetRangeEnabled(FSelectionShiftStart, FItemIndex, True);
+ end
+ end
+ else ItemSetSelected(Item, True);
+ end;
+ DoRepaint;
+end;
+
+procedure TfpgListView.HandleRMouseDown(x, y: integer; shiftstate: TShiftState);
+var
+ I: Integer;
+ cLeft, cRight: Integer;
+ cRect: TfpgRect;
+ Column: TfpgLVColumn;
+begin
+ inherited HandleRMouseDown(x, y, shiftstate);
+
+ cRect := GetClientRect;
+
+ if not PtInRect(cRect, Point(X,Y)) then
+ Exit;
+
+ if FShowHeaders then
+ begin
+ if (Y < HeaderHeight + cRect.Top) then
+ begin
+ cLeft := cRect.Left - FHScrollBar.Position;
+ for I := 0 to FColumns.Count-1 do
+ begin
+ Column := FColumns.Column[I];
+ if Column.Visible then
+ begin
+ cRight := cLeft + Column.Width-1;
+ if (X <= cRight) and (X >= cLeft) then
+ DoColumnClick(Column, 3);
+ Inc(cLeft, Column.Width);
+ end;
+ end;
+ end;
+ Inc(cRect.Top, HeaderHeight);
+ end;
+
+ if FVScrollBar.Visible then
+ Dec(cRect.Width, FVScrollBar.Width);
+ if FHScrollBar.Visible then
+ Dec(cRect.Height, FHScrollBar.Height);
+end;
+
+procedure TfpgListView.HandleLMouseUp(x, y: integer; shiftstate: TShiftState);
+var
+ I: Integer;
+begin
+ inherited HandleLMouseUp(x, y, shiftstate);
+ for I := 0 to FColumns.Count-1 do
+ begin
+ FColumns.Column[I].FDown := False;
+ end;
+
+ FResizingColumn := nil;
+ DoRepaint;
+end;
+
+procedure TfpgListView.HandleRMouseUp(x, y: integer; shiftstate: TShiftState);
+var
+ I: Integer;
+begin
+ inherited HandleRMouseUp(x, y, shiftstate);
+ for I := 0 to FColumns.Count-1 do
+ begin
+ FColumns.Column[I].FDown := False;
+ end;
+ DoRepaint;
+end;
+
+procedure TfpgListView.HandleMouseMove(x, y: integer; btnstate: word;
+ shiftstate: TShiftState);
+var
+ cRect: TfpgRect;
+begin
+ inherited HandleMouseMove(x, y, btnstate, shiftstate);
+
+ cRect := GetClientRect;
+
+ if not PtInRect(cRect, Point(X,Y)) and (FResizingColumn = nil) then
+ Exit;
+
+ if ((Y < (cRect.Top + HeaderHeight)) and ShowHeaders) or (FResizingColumn <> nil) then
+ begin
+ HandleHeaderMouseMove(x, y, btnstate, shiftstate);
+ end
+ else
+ if (MouseCursor <> mcDefault) and (FResizingColumn = nil) then
+ MouseCursor := mcDefault;
+
+ //if FVScrollBar.Visible then Dec(cRect.Width, FVScrollBar.Width);
+ //if FHScrollBar.Visible then Dec(cRect.Height, FHScrollBar.Height);
+end;
+
+procedure TfpgListView.HandleKeyPress(var keycode: word;
+ var shiftstate: TShiftState; var consumed: boolean);
+var
+ iIndex: Integer;
+ OldIndex: Integer;
+ procedure CheckMultiSelect;
+ begin
+ if FMultiSelect then begin
+ if (ssShift in shiftstate) or (FSelectionShiftStart > -1) then
+ begin
+ SelectionSetRangeEnabled(FSelectionShiftStart, FItemIndex, True);
+ end
+ else if ssCtrl in shiftstate then
+ begin
+ SelectionToggleRange(FItemIndex, FItemIndex, shiftstate, False);
+ end;
+ end;
+ end;
+ procedure CheckSelectionFocus;
+ begin
+ if ((ssShift in shiftstate) or (ssCtrl in shiftstate)) then
+ Exit;
+ SelectionClear;
+ if FSelectionFollowsFocus and (FItemIndex > -1) then
+ ItemSetSelected(FItems.Item[FItemIndex], True);
+ end;
+begin
+ consumed := True;
+ OldIndex := FItemIndex;
+ //WriteLn('Got key: ',IntToHex(keycode, 4));
+ case keycode of
+ keyShift, keyShiftR:
+ begin
+ if FMultiSelect then
+ StartShiftSelection;
+ end;
+ keyUp:
+ begin
+ if ItemIndex > 0 then
+ ItemIndex := ItemIndex-1;
+ MakeItemVisible(ItemIndex);
+ if OldIndex <> ItemIndex then
+ CheckSelectionFocus;
+ CheckMultiSelect;
+ end;
+ keyDown:
+ begin
+ ItemIndex := ItemIndex+1;
+ MakeItemVisible(ItemIndex);
+ if OldIndex <> ItemIndex then
+ CheckSelectionFocus;
+ CheckMultiSelect;
+ end;
+ keyLeft:
+ begin
+ FHScrollBar.Position := FHScrollBar.Position - FHScrollBar.ScrollStep;
+ end;
+ keyRight:
+ begin
+ FHScrollBar.Position := FHScrollBar.Position + FHScrollBar.ScrollStep;
+ end;
+ keyHome:
+ begin
+ ItemIndex := 0;
+ MakeItemVisible(ItemIndex);
+ if OldIndex <> ItemIndex then
+ CheckSelectionFocus;
+ CheckMultiSelect;
+ end;
+ keyEnd:
+ begin
+ ItemIndex := FItems.Count-1;
+ MakeItemVisible(ItemIndex);
+ if OldIndex <> ItemIndex then
+ CheckSelectionFocus;
+ CheckMultiSelect;
+ end;
+ keyPageUp:
+ begin
+ iIndex := ItemIndex - (GetItemAreaHeight div ItemHeight);
+ if iIndex < 0 then
+ iIndex := 0;
+ ItemIndex := iIndex;
+ MakeItemVisible(ItemIndex);
+ if OldIndex <> ItemIndex then
+ CheckSelectionFocus;
+ CheckMultiSelect;
+ end;
+ keyPageDown:
+ begin
+ iIndex := ItemIndex + (GetItemAreaHeight div ItemHeight);
+ if iIndex > FItems.Count-1 then
+ iIndex := FItems.Count-1;
+ ItemIndex := iIndex;
+ MakeItemVisible(ItemIndex);
+ if OldIndex <> ItemIndex then
+ CheckSelectionFocus;
+ CheckMultiSelect
+ end;
+ else
+ consumed := False;
+ inherited HandleKeyPress(keycode, shiftstate, consumed);
+ Exit;
+ end;
+ DoRepaint;
+
+end;
+
+procedure TfpgListView.HandleKeyRelease(var keycode: word;
+ var shiftstate: TShiftState; var consumed: boolean);
+begin
+ consumed := True;
+ case keycode of
+ keyShift, keyShiftR:
+ begin
+ EndShiftSelection;
+ end;
+ else
+ consumed := False;
+ inherited HandleKeyRelease(keycode, shiftstate, consumed);
+ end;
+
+end;
+
+procedure TfpgListView.HandlePaint;
+var
+ ClipRect: TfpgRect;
+begin
+ //if FScrollBarNeedsUpdate then
+ UpdateScrollBarPositions;
+ fpgStyle.DrawControlFrame(Canvas, 0, 0, Width, Height);
+
+ ClipRect.Top := 2;
+ ClipRect.Left := 2;
+ ClipRect.Width := Width -4;
+ ClipRect.Height := Height -4;
+
+ if ShowHeaders then
+ begin
+ PaintHeaders;
+ Inc(ClipRect.Top, HeaderHeight);
+ Dec(ClipRect.Height, HeaderHeight);
+ end;
+
+ Canvas.SetClipRect(ClipRect);
+
+ // this paints the small square remaining below the vscrollbar and to the right of the hscrollbar
+ if FVScrollBar.Visible and FHScrollBar.Visible then
+ begin
+ Canvas.Color := clButtonFace;
+ Canvas.FillRectangle(Width - 2 - FVScrollBar.Width,
+ Height - 2 - FHScrollBar.Height,
+ Width - 2,
+ Height - 2);
+ end;
+
+ if FVScrollBar.Visible then
+ Dec(ClipRect.Width, FVScrollBar.Width);
+ if FHScrollBar.Visible then
+ Dec(ClipRect.Height, FhScrollBar.Height);
+
+ Canvas.SetClipRect(ClipRect);
+ PaintItems;
+end;
+
+procedure TfpgListView.HandleResize(awidth, aheight: TfpgCoord);
+begin
+ inherited HandleResize(awidth, aheight);
+ FScrollBarNeedsUpdate := FScrollBarNeedsUpdate or FSizeIsDirty;
+end;
+
+procedure TfpgListView.PaintHeaders;
+var
+ I: Integer;
+ cLeft,
+ cTop: Integer;
+ Column: TfpgLVColumn;
+ Flags: TFButtonFlags;
+ ClipRect: TfpgRect;
+ cRect: TfpgRect;
+ PaintPart: TfpgLVItemPaintPart;
+ tWidth,
+ tLeft: Integer;
+begin
+ cLeft := 2;
+ ClipRect.Top := 2;
+ ClipRect.Left := 2;
+ ClipRect.Height := HeaderHeight;
+ ClipRect.Width := Width -4;
+ Canvas.SetClipRect(ClipRect);
+
+ if FHScrollBar.Visible then Dec(cLeft, FHScrollBar.Position);
+ cTop := 2;
+ for I := 0 to Columns.Count-1 do
+ begin
+ Column := Columns.Column[I];
+ if Column.Visible then
+ begin
+ Flags := [btfIsEmbedded];
+ if Column.FDown then Flags := Flags + [btfIsPressed];
+ cRect.Top := cTop;
+ cRect.Left := cLeft;
+ cRect.Width := Column.Width;
+ cRect.Height := HeaderHeight;
+ fpgStyle.DrawButtonFace(Canvas,cLeft, cRect.Top, cRect.Width, cRect.Height, Flags);
+ PaintPart := [lvppText];
+
+ if Assigned(FOnPaintColumn) then
+ FOnPaintColumn(Self, Canvas, Column, I, cRect, PaintPart);
+
+ if lvppText in PaintPart then
+ begin
+ tLeft := cLeft;
+ tWidth := Canvas.Font.TextWidth(Column.Caption);
+ case Column.CaptionAlignment of
+ taRightJustify: Inc(tLeft, Column.Width - tWidth - 5);
+ taCenter: Inc(tLeft, (Column.Width - tWidth - 5) div 2);
+ taLeftJustify: Inc(tLeft, 5);
+ end;
+ fpgStyle.DrawString(Canvas, tLeft, cTop+5, Column.Caption, Enabled);
+ end;
+ Inc(cLeft, Column.Width);
+ end;
+ end;
+ if cLeft < FWidth-2 then
+ begin
+ Canvas.SetColor(clButtonFace);
+ Canvas.FillRectangle(cLeft, cTop, cLeft+(Width-3-cLeft), Canvas.Font.Height+10);
+ end;
+end;
+
+procedure TfpgListView.PaintItems;
+var
+ FirstIndex,
+ LastIndex: Integer;
+ I, J : Integer;
+ PaintPart: TfpgLVItemPaintPart;
+ ItemRect: TfpgRect;
+ ItemState: TfpgLVItemState;
+ Item: TfpgLVItem;
+ TheText: String;
+ TheTextColor: TfpgColor;
+ oClipRect: TfpgRect;
+ iColumnClipRect: TfpgRect;
+ ColumnIndex: Integer;
+ cBottom: Integer;
+ vBottom: Integer;
+ tLeft,
+ tWidth: Integer;
+begin
+ FirstIndex := (FVScrollBar.Position) div ItemHeight;
+ LastIndex := (FVScrollBar.Position+GetItemAreaHeight) div ItemHeight;
+
+ if LastIndex > FItems.Count-1 then
+ LastIndex := FItems.Count-1;
+
+ cBottom := 2 + ((LastIndex+1 - FirstIndex) * ItemHeight);
+
+ if ShowHeaders then
+ Inc(cBottom, HeaderHeight);
+
+ oClipRect := Canvas.GetClipRect;
+
+ for I := FirstIndex to LastIndex do
+ begin
+ ItemState := [];
+ PaintPart := [lvppBackground, lvppIcon, lvppText];
+ ItemRect := ItemGetRect(I);
+
+ if (I = FirstIndex)
+ and (ShowHeaders)
+ and (ItemRect.Top < 2 + HeaderHeight) then
+ Dec(cBottom, (2 + HeaderHeight) - ItemRect.Top);
+
+ Item := FItems.Item[I];
+ if Item.Selected[Self] then
+ Include(ItemState, lisSelected);
+ if FItemIndex = I then
+ begin
+ Include(ItemState, lisFocused);
+ Include(PaintPart, lvppFocused);
+ end;
+
+ if lisSelected in (ItemState) then
+ begin
+ if Focused then
+ Canvas.Color := clSelection
+ else
+ Canvas.Color := clInactiveSel;
+ end
+ else Canvas.Color := clListBox;
+
+ Canvas.FillRectangle(ItemRect);
+ Exclude(PaintPart, lvppBackground);
+ TheTextColor := Canvas.TextColor;
+ if Assigned(FOnPaintItem) then
+ FOnPaintItem(Self, Canvas, Item, I, ItemRect, PaintPart);
+
+ if lvppIcon in PaintPart then
+ begin
+ { TODO: paint icon }
+ end;
+
+ if lvppFocused in PaintPart then
+ begin
+ Canvas.Color := clBlack;
+ Canvas.SetLineStyle(1, lsDot);
+ Canvas.DrawRectangle(ItemRect);
+ end;
+
+ if lvppText in PaintPart then
+ begin
+ if lisSelected in ItemState then
+ Canvas.TextColor := clSelectionText;
+ for J := 0 to FColumns.Count-1 do
+ begin
+ if FColumns.Column[J].Visible then
+ begin
+ iColumnClipRect.Left := Max(ItemRect.Left, oClipRect.Left);
+ iColumnClipRect.Top := Max(ItemRect.Top, oClipRect.Top);
+ iColumnClipRect.SetRight(Min(ItemRect.Left+FColumns.Column[J].Width, oClipRect.Right));
+ iColumnClipRect.SetBottom(Min(ItemRect.Bottom, oClipRect.Bottom));
+ Canvas.SetClipRect(iColumnClipRect);
+ if FColumns.Column[J].ColumnIndex <> -1 then
+ ColumnIndex := FColumns.Column[J].ColumnIndex
+ else
+ ColumnIndex := J;
+ if ColumnIndex = 0 then
+ TheText := Item.Caption
+ else if Item.SubItems.Count >= ColumnIndex then
+ TheText := Item.SubItems.Strings[ColumnIndex-1]
+ else
+ TheText := '';
+
+ tLeft := ItemRect.Left;
+ tWidth := Canvas.Font.TextWidth(TheText);
+ case FColumns.Column[J].Alignment of
+ taRightJustify: Inc(tLeft, FColumns.Column[J].Width - tWidth - 5);
+ taCenter: Inc(tLeft, (FColumns.Column[J].Width - tWidth - 5) div 2);
+ taLeftJustify: Inc(tLeft, 5);
+ end;
+
+ fpgStyle.DrawString(Canvas, tLeft, ItemRect.Top+2, TheText, Enabled);
+ Inc(ItemRect.Left, FColumns.Column[J].Width);
+ //WriteLn(ItemRect.Left,' ', ItemRect.Top, ' ', ItemRect.Right, ' ', ItemRect.Bottom);
+ end;
+ end;
+ end;
+
+ Canvas.SetClipRect(oClipRect);
+
+ Canvas.TextColor := TheTextColor;
+ end;
+
+ vBottom := Height - 2;
+ if FHScrollBar.Visible then
+ Dec(vBottom, FHScrollBar.Height);
+
+ // the painted items haven't fully covered the visible area
+ if vBottom > cBottom then begin
+ ItemRect.Left := 2;
+ ItemRect.Top := cBottom;
+ ItemRect.SetBottom(vBottom);
+ ItemRect.Width := Width - 4;
+ Canvas.SetColor(clListBox);
+ Canvas.FillRectangle(ItemRect);
+ end;
+ if GetVisibleColumnsWidth < oClipRect.Width then
+ begin
+ ItemRect.Left := GetVisibleColumnsWidth+2;
+ ItemRect.SetRight(oClipRect.Right);
+ ItemRect.Top := oClipRect.Top;
+ ItemRect.Height := oClipRect.Height;
+ Canvas.SetColor(clListBox);
+ Canvas.FillRectangle(ItemRect);
+ end;
+end;
+
+procedure TfpgListView.UpdateScrollBarPositions;
+var
+ BevelSize: Integer;
+ I: Integer;
+ MaxH,
+ MaxV: Integer;
+begin
+ MaxH := 0;
+ MaxV := 0;
+ BevelSize := 2;
+
+ for I := 0 to Columns.Count -1 do
+ begin
+ if Columns.Column[I].Visible then
+ Inc(MaxH, Columns.Column[I].Width);
+ end;
+
+ MaxV := (FItems.Count+2) * ItemHeight - (Height);
+ if ShowHeaders then
+ Inc(MaxV, HeaderHeight);
+ if FVScrollBar.Visible then
+ Inc(MaxH, FVScrollBar.Width);
+
+ FHScrollBar.Top := Height - FHScrollBar.Height - (BevelSize );
+ FHScrollBar.Left := BevelSize;
+ FHScrollBar.Width := Width - (BevelSize * 2);
+
+
+ FVScrollBar.Top := BevelSize;
+ FVScrollBar.Left := Width - FVScrollBar.Width - (BevelSize );
+ FVScrollBar.Height := Height - FVScrollBar.Top - BevelSize;
+
+ if FVScrollBar.Visible and FHScrollBar.Visible then
+ begin
+ FHScrollBar.Width := FHScrollBar.Width - FVScrollBar.Width;
+ FVScrollBar.Height := FVScrollBar.Height - FHScrollBar.Height;
+ end;
+
+ FHScrollBar.Max := MaxH-(Width-(BevelSize * 2));
+ FVScrollBar.Max := MaxV;
+
+ if FVScrollBar.Max = 0 then
+ FVScrollBar.SliderSize := 1
+ else
+ begin
+ if (FVScrollBar.Max + FVScrollBar.Height) > 0 then
+ FVScrollBar.SliderSize := FVScrollBar.Height / (FVScrollBar.Max + FVScrollBar.Height)
+ else
+ FVScrollBar.SliderSize := 0.5;
+ end;
+ FVScrollBar.RepaintSlider;
+
+ if FHScrollBar.Max = 0 then
+ FHScrollBar.SliderSize := 1
+ else
+ begin
+ if (FHScrollBar.Max + FHScrollBar.Width) > 0 then
+ FHScrollBar.SliderSize := FHScrollBar.Width / (FHScrollBar.Max + FHScrollBar.Width)
+ else
+ FHScrollBar.SliderSize := 0.5;
+ end;
+ FHScrollBar.RepaintSlider;
+
+
+ if FHScrollBar.Visible then
+ FHScrollBar.UpdateWindowPosition;
+ if FVScrollBar.Visible then
+ FVScrollBar.UpdateWindowPosition;
+
+ FScrollBarNeedsUpdate := False;
+end;
+
+constructor TfpgListView.Create(AOwner: TComponent);
+begin
+ inherited Create(AOwner);
+ FWidth := 120;
+ FHeight := 80;
+ Focusable := True;
+ FShowHeaders := True;
+
+ FVScrollBar := TfpgScrollBar.Create(Self);
+ FVScrollBar.Orientation := orVertical;
+ FVScrollBar.OnScroll := @VScrollChange;
+ FVScrollBar.ScrollStep := 18;
+ FVScrollBar.Position := 0;
+
+ FHScrollBar := TfpgScrollBar.Create(Self);
+ FHScrollBar.Orientation := orHorizontal;
+ FHScrollBar.OnScroll := @HScrollChange;
+ FHScrollBar.ScrollStep := 18;
+ FHScrollBar.Position := 0;
+
+ FColumns := TfpgLVColumns.Create(Self);
+
+ FItems := TfpgLVItems.Create(Self);
+ FSelected := TList.Create;
+ FOldSelected := TList.Create;;
+ FSelectionShiftStart := -1;
+ FSelectionFollowsFocus := True;
+ FItemIndex := -1;
+ FScrollBarNeedsUpdate := True;
+end;
+
+destructor TfpgListView.Destroy;
+begin
+ FItems.DeleteViewer(Self);
+ FSelected.Free;
+ FOldSelected.Free;
+ inherited Destroy;
+end;
+
+procedure TfpgListView.BeginUpdate;
+begin
+ Inc(FUpdateCount);
+ FItems.BeginUpdate;
+end;
+
+procedure TfpgListView.EndUpdate;
+begin
+ FItems.EndUpdate;
+ Dec(FUpdateCount);
+ if FUpdateCount < 0 then
+ FUpdateCount := 0;
+ if FUpdateCount = 0 then
+ DoRePaint;
+end;
+
+procedure TfpgListView.MakeItemVisible(AIndex: Integer; PartialOK: Boolean);
+var
+ iTop,
+ iBottom: integer;
+ tVisible, bVisible: Boolean;
+begin
+ if AIndex = -1 then
+ Exit;
+ iTop := AIndex * ItemHeight;
+ iBottom := iTop + ItemHeight;
+
+ tVisible := (iTop >= FVScrollBar.Position) and (iTop < FVScrollBar.Position + GetItemAreaHeight);
+ bVisible := (iBottom >= FVScrollBar.Position) and (iBottom < FVScrollBar.Position + GetItemAreaHeight);
+
+ if PartialOK and (bVisible or tVisible) then
+ Exit;
+
+ if bVisible and tVisible then
+ Exit;
+
+ if (iBottom >= FVScrollBar.Position + GetItemAreaHeight) then
+ FVScrollBar.Position := iBottom - GetItemAreaHeight
+ else
+ FVScrollBar.Position := iTop;
+end;
+
+function TfpgListView.ItemAdd: TfpgLVItem;
+begin
+ Result := TfpgLVItem.Create(FItems);
+ FItems.Add(Result);
+end;
+
+{ TfpgLVColumns }
+
+function TfpgLVColumns.GetColumn(AIndex: Integer): TfpgLVColumn;
+begin
+ Result := TfpgLVColumn(FColumns.Items[AIndex]);
+end;
+
+procedure TfpgLVColumns.SetColumn(AIndex: Integer; const AValue: TfpgLVColumn);
+begin
+ FColumns.Items[AIndex] := AValue;
+end;
+
+constructor TfpgLVColumns.Create(AListView: TfpgListView);
+begin
+ FListView := AListView;
+ FColumns := TList.Create;
+end;
+
+destructor TfpgLVColumns.Destroy;
+begin
+ FColumns.Free;
+ inherited Destroy;
+end;
+
+function TfpgLVColumns.Add(AColumn: TfpgLVColumn): Integer;
+begin
+ Result := Count;
+ Insert(AColumn, Count);
+end;
+
+procedure TfpgLVColumns.Clear;
+var
+ i: integer;
+begin
+ for i := FColumns.Count-1 downto 0 do
+ Delete(i);
+ FColumns.Clear;
+end;
+
+procedure TfpgLVColumns.Delete(AIndex: Integer);
+begin
+ FColumns.Delete(AIndex);
+end;
+
+procedure TfpgLVColumns.Insert(AColumn: TfpgLVColumn; AIndex: Integer);
+begin
+ FColumns.Insert(AIndex, AColumn);
+end;
+
+function TfpgLVColumns.Count: Integer;
+begin
+ Result := FColumns.Count;
+end;
+
+{ TfpgLVColumn }
+
+procedure TfpgLVColumn.SetCaption(const AValue: String);
+begin
+ if FCaption=AValue then
+ Exit;
+ FCaption:=AValue;
+end;
+
+procedure TfpgLVColumn.SetCaptionAlignment(const AValue: TAlignment);
+begin
+ if FCaptionAlignment=AValue then exit;
+ FCaptionAlignment:=AValue;
+ if Assigned(FColumns) and Assigned(FColumns.FListView) then
+ FColumns.FListView.DoRepaint;
+
+end;
+
+procedure TfpgLVColumn.SetColumnIndex(const AValue: Integer);
+begin
+ if FColumnIndex=AValue then
+ Exit;
+ FColumnIndex:=AValue;
+end;
+
+procedure TfpgLVColumn.SetHeight(const AValue: Integer);
+begin
+ if FHeight=AValue then Exit;
+ FHeight:=AValue;
+end;
+
+procedure TfpgLVColumn.SetResizable(const AValue: Boolean);
+begin
+ if FResizable=AValue then exit;
+ FResizable:=AValue;
+end;
+
+procedure TfpgLVColumn.SetVisible(const AValue: Boolean);
+begin
+ if FVisible=AValue then exit;
+ FVisible:=AValue;
+end;
+
+procedure TfpgLVColumn.SetAutoSize(const AValue: Boolean);
+begin
+ if FAutoSize=AValue then exit;
+ FAutoSize:=AValue;
+end;
+
+procedure TfpgLVColumn.SetAlignment(const AValue: TAlignment);
+begin
+ if FAlignment=AValue then exit;
+ FAlignment:=AValue;
+ if Assigned(FColumns)and Assigned(FColumns.FListView) then
+ FColumns.FListView.DoRepaint;
+end;
+
+procedure TfpgLVColumn.SetWidth(const AValue: Integer);
+begin
+ if FWidth=AValue then exit;
+ FWidth:=AValue;
+ if FWidth < 1 then
+ FWidth := 1;
+end;
+
+constructor TfpgLVColumn.Create(AColumns: TfpgLVColumns);
+begin
+ FVisible := True;
+ FColumnIndex := -1;
+ FColumns := AColumns;
+ FClickable := True;
+ FAlignment := taLeftJustify;
+ FCaptionAlignment := taLeftJustify;
+end;
+
+destructor TfpgLVColumn.Destroy;
+begin
+ inherited Destroy;
+end;
+
+end.