source: trunk/Packages/Common/UListViewSort.pas

Last change on this file was 19, checked in by chronos, 7 years ago
  • Fixed: Build under Lazarus 1.8.0.
  • Modified: Updated Common package.
File size: 15.1 KB
Line 
1unit UListViewSort;
2
3// Date: 2010-11-03
4
5{$mode delphi}
6
7interface
8
9uses
10 {$IFDEF Windows}Windows, CommCtrl, {$ENDIF}Classes, Graphics, ComCtrls, SysUtils,
11 Controls, DateUtils, Dialogs, SpecializedList, Forms, Grids, StdCtrls, ExtCtrls,
12 LclIntf, LMessages, LclType, LResources;
13
14type
15 TSortOrder = (soNone, soUp, soDown);
16
17 TListViewSort = class;
18
19 TCompareEvent = function (Item1, Item2: TObject): Integer of object;
20 TListFilterEvent = procedure (ListViewSort: TListViewSort) of object;
21
22 { TListViewSort }
23
24 TListViewSort = class(TComponent)
25 private
26 FListView: TListView;
27 FOnCompareItem: TCompareEvent;
28 FOnFilter: TListFilterEvent;
29 FOnCustomDraw: TLVCustomDrawItemEvent;
30 {$IFDEF Windows}FHeaderHandle: HWND;{$ENDIF}
31 FColumn: Integer;
32 FOrder: TSortOrder;
33 FOldListViewWindowProc: TWndMethod;
34 FOnColumnWidthChanged: TNotifyEvent;
35 procedure DoColumnBeginResize(const AColIndex: Integer);
36 procedure DoColumnResized(const AColIndex: Integer);
37 procedure DoColumnResizing(const AColIndex, AWidth: Integer);
38 procedure SetListView(const Value: TListView);
39 procedure ColumnClick(Sender: TObject; Column: TListColumn);
40 procedure Sort(Compare: TCompareEvent);
41 procedure DrawCheckMark(Item: TListItem; Checked: Boolean);
42 procedure GetCheckBias(var XBias, YBias, BiasTop, BiasLeft: Integer;
43 const ListView: TListView);
44 procedure ListViewCustomDrawItem(Sender: TCustomListView;
45 Item: TListItem; State: TCustomDrawState; var DefaultDraw: Boolean);
46 procedure ListViewClick(Sender: TObject);
47 procedure UpdateColumns;
48 procedure SetColumn(const Value: Integer);
49 procedure SetOrder(const Value: TSortOrder);
50 {$IFDEF WINDOWS}
51 procedure NewListViewWindowProc(var AMsg: TMessage);
52 {$ENDIF}
53 public
54 List: TListObject;
55 Source: TListObject;
56 constructor Create(AOwner: TComponent); override;
57 destructor Destroy; override;
58 function CompareTime(Time1, Time2: TDateTime): Integer;
59 function CompareInteger(Value1, Value2: Integer): Integer;
60 function CompareString(Value1, Value2: string): Integer;
61 function CompareBoolean(Value1, Value2: Boolean): Integer;
62 procedure Refresh;
63 published
64 property ListView: TListView read FListView write SetListView;
65 property OnCompareItem: TCompareEvent read FOnCompareItem
66 write FOnCompareItem;
67 property OnFilter: TListFilterEvent read FOnFilter
68 write FOnFilter;
69 property OnCustomDraw: TLVCustomDrawItemEvent read FOnCustomDraw
70 write FOnCustomDraw;
71 property OnColumnWidthChanged: TNotifyEvent read FOnColumnWidthChanged
72 write FOnColumnWidthChanged;
73 property Column: Integer read FColumn write SetColumn;
74 property Order: TSortOrder read FOrder write SetOrder;
75 end;
76
77 { TListViewFilter }
78
79 TListViewFilter = class(TWinControl)
80 private
81 FOnChange: TNotifyEvent;
82 FStringGrid1: TStringGrid;
83 procedure DoOnKeyUp(Sender: TObject; var Key: Word; Shift: TShiftState);
84 procedure DoOnResize(Sender: TObject);
85 public
86 constructor Create(AOwner: TComponent); override;
87 procedure UpdateFromListView(ListView: TListView);
88 function TextEntered: Boolean;
89 function TextEnteredCount: Integer;
90 function TextEnteredColumn(Index: Integer): Boolean;
91 function GetColValue(Index: Integer): string;
92 property StringGrid: TStringGrid read FStringGrid1 write FStringGrid1;
93 published
94 property OnChange: TNotifyEvent read FOnChange write FOnChange;
95 property Align;
96 property Anchors;
97 property BorderSpacing;
98 end;
99
100procedure Register;
101
102
103implementation
104
105procedure Register;
106begin
107 RegisterComponents('Common', [TListViewSort, TListViewFilter]);
108end;
109
110{ TListViewFilter }
111
112procedure TListViewFilter.DoOnKeyUp(Sender: TObject; var Key: Word;
113 Shift: TShiftState);
114begin
115 if Assigned(FOnChange) then
116 FOnChange(Self);
117end;
118
119procedure TListViewFilter.DoOnResize(Sender: TObject);
120begin
121 FStringGrid1.DefaultRowHeight := FStringGrid1.Height;
122end;
123
124constructor TListViewFilter.Create(AOwner: TComponent);
125begin
126 inherited Create(AOwner);
127 FStringGrid1 := TStringGrid.Create(Self);
128 FStringGrid1.Align := alClient;
129 FStringGrid1.Parent := Self;
130 FStringGrid1.Visible := True;
131 FStringGrid1.ScrollBars := ssNone;
132 FStringGrid1.FixedCols := 0;
133 FStringGrid1.FixedRows := 0;
134 FStringGrid1.RowCount := 1;
135 FStringGrid1.Options := [goFixedHorzLine, goFixedVertLine, goVertLine,
136 goHorzLine, goRangeSelect, goEditing, goAlwaysShowEditor, goSmoothScroll];
137 FStringGrid1.OnKeyUp := DoOnKeyUp;
138 FStringGrid1.OnResize := DoOnResize;
139end;
140
141procedure TListViewFilter.UpdateFromListView(ListView: TListView);
142var
143 I: Integer;
144begin
145 with FStringGrid1 do begin
146 //Columns.Clear;
147 while Columns.Count > ListView.Columns.Count do Columns.Delete(Columns.Count - 1);
148 while Columns.Count < ListView.Columns.Count do Columns.Add;
149 for I := 0 to ListView.Columns.Count - 1 do begin
150 Columns[I].Width := ListView.Columns[I].Width;
151 end;
152 end;
153end;
154
155function TListViewFilter.TextEntered: Boolean;
156begin
157 Result := TextEnteredCount > 0;
158end;
159
160function TListViewFilter.TextEnteredCount: Integer;
161var
162 I: Integer;
163begin
164 Result := 0;
165 for I := 0 to FStringGrid1.ColCount - 1 do begin
166 if FStringGrid1.Cells[I, 0] <> '' then begin
167 Inc(Result);
168 end;
169 end;
170end;
171
172function TListViewFilter.TextEnteredColumn(Index: Integer): Boolean;
173begin
174 Result := FStringGrid1.Cells[Index, 0] <> '';
175end;
176
177function TListViewFilter.GetColValue(Index: Integer): string;
178begin
179 if (Index >= 0) and (Index < StringGrid.Columns.Count) then
180 Result := StringGrid.Cells[Index, 0]
181 else Result := '';
182end;
183
184{ TListViewSort }
185
186{$IFDEF WINDOWS}
187procedure TListViewSort.NewListViewWindowProc(var AMsg: TMessage);
188var
189 vColWidth: Integer;
190 vMsgNotify: TLMNotify absolute AMsg;
191 Code: Integer;
192begin
193 // call the old WindowProc of ListView
194 FOldListViewWindowProc(AMsg);
195
196 // Currently we care only with WM_NOTIFY message
197 if AMsg.Msg = WM_NOTIFY then
198 begin
199 Code := PHDNotify(vMsgNotify.NMHdr)^.Hdr.Code;
200 case Code of
201 HDN_ENDTRACKA, HDN_ENDTRACKW:
202 DoColumnResized(PHDNotify(vMsgNotify.NMHdr)^.Item);
203
204 HDN_BEGINTRACKA, HDN_BEGINTRACKW:
205 DoColumnBeginResize(PHDNotify(vMsgNotify.NMHdr)^.Item);
206
207 HDN_TRACKA, HDN_TRACKW:
208 begin
209 vColWidth := -1;
210 if (PHDNotify(vMsgNotify.NMHdr)^.PItem<>nil)
211 and (PHDNotify(vMsgNotify.NMHdr)^.PItem^.Mask and HDI_WIDTH <> 0)
212 then
213 vColWidth := PHDNotify(vMsgNotify.NMHdr)^.PItem^.cxy;
214
215 DoColumnResizing(PHDNotify(vMsgNotify.NMHdr)^.Item, vColWidth);
216 end;
217 end;
218 end;
219end;
220{$ENDIF}
221
222procedure TListViewSort.DoColumnBeginResize(const AColIndex: Integer);
223begin
224end;
225
226procedure TListViewSort.DoColumnResizing(const AColIndex, AWidth: Integer);
227begin
228end;
229
230procedure TListViewSort.DoColumnResized(const AColIndex: Integer);
231begin
232 if Assigned(FOnColumnWidthChanged) then
233 FOnColumnWidthChanged(Self);
234end;
235
236procedure TListViewSort.ColumnClick(Sender: TObject; Column: TListColumn);
237begin
238 if Column.Index = Self.Column then begin
239 if FOrder = soUp then FOrder := soDown
240 else if FOrder = soDown then FOrder := soUp
241 else FOrder := soUp;
242 end else Self.Column := Column.Index;
243 Refresh;
244 UpdateColumns;
245end;
246
247procedure TListViewSort.SetOrder(const Value: TSortOrder);
248begin
249 FOrder := Value;
250 UpdateColumns;
251end;
252
253procedure TListViewSort.SetColumn(const Value: Integer);
254begin
255 FColumn := Value;
256 UpdateColumns;
257end;
258
259procedure TListViewSort.SetListView(const Value: TListView);
260begin
261 if FListView = Value then Exit;
262 if Assigned(FListView) then
263 ListView.WindowProc := FOldListViewWindowProc;
264 FListView := Value;
265 FListView.OnColumnClick := ColumnClick;
266 FListView.OnCustomDrawItem := ListViewCustomDrawItem;
267 FListView.OnClick := ListViewClick;
268 FOldListViewWindowProc := FListView.WindowProc;
269 {$IFDEF WINDOWS}
270 FListView.WindowProc := NewListViewWindowProc;
271 {$ENDIF}
272end;
273
274procedure TListViewSort.Sort(Compare: TCompareEvent);
275begin
276 if (List.Count > 0) then
277 List.Sort(Compare);
278end;
279
280procedure TListViewSort.Refresh;
281begin
282 if Assigned(FOnFilter) then FOnFilter(Self)
283 else if Assigned(Source) then
284 List.Assign(Source) else
285 List.Clear;
286 if ListView.Items.Count <> List.Count then
287 ListView.Items.Count := List.Count;
288 if Assigned(FOnCompareItem) and (Order <> soNone) then Sort(FOnCompareItem);
289 //ListView.Items[-1]; // Workaround for not show first row if selected
290 ListView.Refresh;
291 // Workaround for not working item selection on first row
292 //if not Assigned(ListView.Selected) then begin
293 // ListView.Items.Count := 0;
294 // ListView.Items.Count := List.Count;
295 //end;
296 //if ListView.Items.Count > 0 then
297 // ListView.Items[0].Selected := True;
298 //ListView.Selected := nil;
299 UpdateColumns;
300end;
301
302const
303 //W_64: Integer = 64; {Width of thumbnail in ICON view mode}
304 H_64: Integer = 64; {Height of thumbnail size}
305 CheckWidth: Integer = 14; {Width of check mark box}
306 CheckHeight: Integer = 14; {Height of checkmark}
307 CheckBiasTop: Integer = 2; {This aligns the checkbox to be in centered}
308 CheckBiasLeft: Integer = 3; {In the row of the list item display}
309
310function TListViewSort.CompareBoolean(Value1, Value2: Boolean): Integer;
311begin
312 if Value1 > Value2 then Result := 1
313 else if Value1 < Value2 then Result := -1
314 else Result := 0;
315end;
316
317function TListViewSort.CompareInteger(Value1, Value2: Integer): Integer;
318begin
319 if Value1 > Value2 then Result := 1
320 else if Value1 < Value2 then Result := -1
321 else Result := 0;
322end;
323
324function TListViewSort.CompareString(Value1, Value2: string): Integer;
325begin
326 Result := AnsiCompareStr(Value1, Value2);
327// if Value1 > Value2 then Result := -1
328// else if Value1 < Value2 then Result := 1
329// else Result := 0;
330end;
331
332function TListViewSort.CompareTime(Time1, Time2: TDateTime): Integer;
333begin
334 Result := DateUtils.CompareDateTime(Time1, Time2);
335end;
336
337constructor TListViewSort.Create(AOwner: TComponent);
338begin
339 inherited;
340 List := TListObject.Create;
341 List.OwnsObjects := False;
342end;
343
344destructor TListViewSort.Destroy;
345begin
346 List.Free;
347 inherited;
348end;
349
350procedure TListViewSort.DrawCheckMark(Item: TListItem; Checked:
351 Boolean);
352var
353 TP1: TPoint;
354 XBias, YBias: Integer;
355 OldColor: TColor;
356 BiasTop, BiasLeft: Integer;
357 Rect1: TRect;
358 lRect: TRect;
359 ItemLeft: Integer;
360begin
361 XBias := 0;
362 YBias := 0;
363 BiasTop := 0;
364 BiasLeft := 0;
365 Item.Left := 0;
366 GetCheckBias(XBias, YBias, BiasTop, BiasLeft, ListView);
367 OldColor := ListView.Canvas.Pen.Color;
368 //TP1 := Item.GetPosition;
369 lRect := Item.DisplayRect(drBounds); // Windows 7 workaround
370 TP1.X := lRect.Left;
371 TP1.Y := lRect.Top;
372 //ShowMessage(IntToStr(Item.Index) + ', ' + IntToStr(GetScrollPos(Item.ListView.Handle, SB_VERT)) + ' ' +
373 // IntToHex(Integer(Item), 8) + ', ' + IntToStr(TP1.X) + ', ' + IntToStr(TP1.Y));
374
375// if Checked then
376 ListView.Canvas.Brush.Color := clWhite;
377 ItemLeft := Item.Left;
378 ItemLeft := 23; // Windows 7 workaround
379
380 Rect1.Left := ItemLeft - CheckWidth - BiasLeft + 1 + XBias;
381 //ShowMessage(IntToStr(Tp1.Y) + ', ' + IntToStr(BiasTop) + ', ' + IntToStr(XBias));
382 Rect1.Top := Tp1.Y + BiasTop + 1 + YBias;
383 Rect1.Right := ItemLeft - BiasLeft - 1 + XBias;
384 Rect1.Bottom := Tp1.Y + BiasTop + CheckHeight - 1 + YBias;
385 //ShowMessage(IntToStr(Rect1.Left) + ', ' + IntToStr(Rect1.Top) + ', ' + IntToStr(Rect1.Right) + ', ' + IntToStr(Rect1.Bottom));
386
387 ListView.Canvas.FillRect(Rect1);
388 //if Checked then ListView.Canvas.Brush.Color := clBlack
389 ListView.Canvas.Brush.Color := clBlack;
390 ListView.Canvas.FrameRect(Rect1);
391 ListView.Canvas.FrameRect(Rect(Rect1.Left - 1, Rect1.Top - 1,
392 Rect1.Right + 1, Rect1.Bottom + 1));
393 if Checked then begin
394 ListView.Canvas.Pen.Color := clBlack;
395 ListView.Canvas.MoveTo(ItemLeft - BiasLeft - 2 + XBias - 2,
396 Tp1.Y + BiasTop + 3 + YBias);
397 ListView.Canvas.LineTo(ItemLeft - BiasLeft - (CheckWidth div 2) + XBias,
398 Tp1.Y + BiasTop + (CheckHeight - 4) + YBias);
399 ListView.Canvas.LineTo(ItemLeft - BiasLeft - (CheckWidth - 3) + XBias,
400 Tp1.Y + BiasTop + (CheckHeight div 2) + YBias - 1);
401
402 ListView.Canvas.MoveTo(ItemLeft - BiasLeft - 2 - 1 + XBias - 2,
403 Tp1.Y + BiasTop + 3 + YBias);
404 ListView.Canvas.LineTo(ItemLeft - BiasLeft - (CheckWidth div 2) - 1 + XBias,
405 Tp1.Y + BiasTop + (CheckHeight - 4) + YBias);
406 ListView.Canvas.LineTo(ItemLeft - BiasLeft - (CheckWidth - 3) - 1 + XBias,
407 Tp1.Y + BiasTop + (CheckHeight div 2) + YBias - 1);
408 end;
409 //ListView.Canvas.Brush.Color := ListView.Color;
410 ListView.Canvas.Brush.Color := clWindow;
411 ListView.Canvas.Pen.Color := OldColor;
412end;
413
414procedure TListViewSort.GetCheckBias(var XBias, YBias, BiasTop, BiasLeft: Integer;
415 const ListView: TListView);
416begin
417 XBias := 0;
418 YBias := 0;
419 if ListView.ViewStyle = vsICON then
420 begin
421 YBias := H_64 - CheckHeight;
422 XBias := 0;
423 end;
424 BiasTop := CheckBiasTop;
425 BiasLeft := CheckBiasLeft;
426 if ListView.ViewStyle <> vsReport then
427 begin
428 BiasTop := 0;
429 BiasLeft := 0;
430 end;
431end;
432
433procedure TListViewSort.ListViewCustomDrawItem(Sender: TCustomListView;
434 Item: TListItem; State: TCustomDrawState; var DefaultDraw: Boolean);
435begin
436 if Assigned(Item) then begin
437 if ListView.Checkboxes then
438 DrawCheckMark(Item, Item.Checked);
439 if Assigned(FOnCustomDraw) then
440 FOnCustomDraw(Sender, Item, State, DefaultDraw);
441 end;
442end;
443
444procedure TListViewSort.ListViewClick(Sender: TObject);
445var
446 Item: TListItem;
447 Pos: TPoint;
448 DefaultDraw: Boolean;
449begin
450 Pos := ListView.ScreenToClient(Mouse.CursorPos);
451 Item := ListView.GetItemAt(Pos.X, Pos.Y);
452 //ShowMessage(IntToStr(Item.Index) + ', ' + IntToStr(Pos.X) + ', ' + IntToStr(Pos.Y));
453 if Assigned(Item) and (Pos.X < 20) then begin
454
455 Item.Checked := not Item.Checked;
456 //ShowMessage(IntToStr(Item.Index) + ', ' +BoolToStr(Item.Checked));
457 if Assigned(ListView.OnChange) then
458 ListView.OnChange(Self, Item, ctState);
459 DefaultDraw := False;
460 ListViewCustomDrawItem(ListView, Item, [], DefaultDraw);
461 //ListView.UpdateItems(Item.Index, Item.Index);
462 end;
463end;
464
465procedure TListViewSort.UpdateColumns;
466{$IFDEF Windows}
467const
468 HDF_SORTUP = $0400;
469 HDF_SORTDOWN = $0200;
470 SortOrder: array[TSortOrder] of Word = (0, HDF_SORTUP, HDF_SORTDOWN);
471var
472 Item: THDItem;
473 I: Integer;
474begin
475 if Assigned(FListView) then begin
476 FHeaderHandle := ListView_GetHeader(FListView.Handle);
477 for I := 0 to FListView.Columns.Count - 1 do begin
478 FillChar(Item, SizeOf(THDItem), 0);
479 Item.Mask := HDI_FORMAT;
480 Header_GetItem(FHeaderHandle, I, Item);
481 Item.fmt := Item.fmt and not (HDF_SORTDOWN or HDF_SORTUP);
482 if (Column <> -1) and (I = Column) then
483 Item.fmt := Item.fmt or SortOrder[FOrder];
484 Header_SetItem(FHeaderHandle, I, Item);
485 end;
486 end;
487end;
488{$ELSE}
489begin
490end;
491{$ENDIF}
492
493end.
Note: See TracBrowser for help on using the repository browser.