source: trunk/Packages/Common/UListViewSort.pas

Last change on this file was 38, checked in by chronos, 7 years ago
  • Modified: Updated Common package.
File size: 15.4 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 GridDoOnKeyUp(Sender: TObject; var Key: Word; Shift: TShiftState);
84 procedure GridDoOnResize(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.GridDoOnKeyUp(Sender: TObject; var Key: Word;
113 Shift: TShiftState);
114begin
115 if Assigned(FOnChange) then
116 FOnChange(Self);
117end;
118
119procedure TListViewFilter.GridDoOnResize(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 := GridDoOnKeyUp;
138 FStringGrid1.OnResize := GridDoOnResize;
139end;
140
141procedure TListViewFilter.UpdateFromListView(ListView: TListView);
142var
143 I: Integer;
144 R: TRect;
145begin
146 with FStringGrid1 do begin
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 if Selection.Left = I then begin
152 R := CellRect(I, 0);
153 Editor.Left := R.Left + 2;
154 Editor.Width := R.Width - 4;
155 end;
156 end;
157 end;
158end;
159
160function TListViewFilter.TextEntered: Boolean;
161begin
162 Result := TextEnteredCount > 0;
163end;
164
165function TListViewFilter.TextEnteredCount: Integer;
166var
167 I: Integer;
168begin
169 Result := 0;
170 for I := 0 to FStringGrid1.ColCount - 1 do begin
171 if FStringGrid1.Cells[I, 0] <> '' then begin
172 Inc(Result);
173 end;
174 end;
175end;
176
177function TListViewFilter.TextEnteredColumn(Index: Integer): Boolean;
178begin
179 Result := FStringGrid1.Cells[Index, 0] <> '';
180end;
181
182function TListViewFilter.GetColValue(Index: Integer): string;
183begin
184 if (Index >= 0) and (Index < StringGrid.Columns.Count) then
185 Result := StringGrid.Cells[Index, 0]
186 else Result := '';
187end;
188
189{ TListViewSort }
190
191{$IFDEF WINDOWS}
192procedure TListViewSort.NewListViewWindowProc(var AMsg: TMessage);
193var
194 vColWidth: Integer;
195 vMsgNotify: TLMNotify absolute AMsg;
196 Code: Integer;
197begin
198 // call the old WindowProc of ListView
199 FOldListViewWindowProc(AMsg);
200
201 // Currently we care only with WM_NOTIFY message
202 if AMsg.Msg = WM_NOTIFY then
203 begin
204 Code := NMHDR(PHDNotify(vMsgNotify.NMHdr)^.Hdr).Code;
205 case Code of
206 HDN_ENDTRACKA, HDN_ENDTRACKW:
207 DoColumnResized(PHDNotify(vMsgNotify.NMHdr)^.Item);
208
209 HDN_BEGINTRACKA, HDN_BEGINTRACKW:
210 DoColumnBeginResize(PHDNotify(vMsgNotify.NMHdr)^.Item);
211
212 HDN_TRACKA, HDN_TRACKW:
213 begin
214 vColWidth := -1;
215 if (PHDNotify(vMsgNotify.NMHdr)^.PItem<>nil)
216 and (PHDNotify(vMsgNotify.NMHdr)^.PItem^.Mask and HDI_WIDTH <> 0)
217 then
218 vColWidth := PHDNotify(vMsgNotify.NMHdr)^.PItem^.cxy;
219
220 DoColumnResizing(PHDNotify(vMsgNotify.NMHdr)^.Item, vColWidth);
221 end;
222 end;
223 end;
224end;
225{$ENDIF}
226
227procedure TListViewSort.DoColumnBeginResize(const AColIndex: Integer);
228begin
229end;
230
231procedure TListViewSort.DoColumnResizing(const AColIndex, AWidth: Integer);
232begin
233end;
234
235procedure TListViewSort.DoColumnResized(const AColIndex: Integer);
236begin
237 if Assigned(FOnColumnWidthChanged) then
238 FOnColumnWidthChanged(Self);
239end;
240
241procedure TListViewSort.ColumnClick(Sender: TObject; Column: TListColumn);
242begin
243 if Column.Index = Self.Column then begin
244 if FOrder = soUp then FOrder := soDown
245 else if FOrder = soDown then FOrder := soUp
246 else FOrder := soUp;
247 end else Self.Column := Column.Index;
248 Refresh;
249 UpdateColumns;
250end;
251
252procedure TListViewSort.SetOrder(const Value: TSortOrder);
253begin
254 FOrder := Value;
255 UpdateColumns;
256end;
257
258procedure TListViewSort.SetColumn(const Value: Integer);
259begin
260 FColumn := Value;
261 UpdateColumns;
262end;
263
264procedure TListViewSort.SetListView(const Value: TListView);
265begin
266 if FListView = Value then Exit;
267 if Assigned(FListView) then
268 ListView.WindowProc := FOldListViewWindowProc;
269 FListView := Value;
270 FListView.OnColumnClick := ColumnClick;
271 FListView.OnCustomDrawItem := ListViewCustomDrawItem;
272 FListView.OnClick := ListViewClick;
273 FOldListViewWindowProc := FListView.WindowProc;
274 {$IFDEF WINDOWS}
275 FListView.WindowProc := NewListViewWindowProc;
276 {$ENDIF}
277end;
278
279procedure TListViewSort.Sort(Compare: TCompareEvent);
280begin
281 if (List.Count > 0) then
282 List.Sort(Compare);
283end;
284
285procedure TListViewSort.Refresh;
286begin
287 if Assigned(FOnFilter) then FOnFilter(Self)
288 else if Assigned(Source) then
289 List.Assign(Source) else
290 List.Clear;
291 if ListView.Items.Count <> List.Count then
292 ListView.Items.Count := List.Count;
293 if Assigned(FOnCompareItem) and (Order <> soNone) then Sort(FOnCompareItem);
294 //ListView.Items[-1]; // Workaround for not show first row if selected
295 ListView.Refresh;
296 // Workaround for not working item selection on first row
297 //if not Assigned(ListView.Selected) then begin
298 // ListView.Items.Count := 0;
299 // ListView.Items.Count := List.Count;
300 //end;
301 //if ListView.Items.Count > 0 then
302 // ListView.Items[0].Selected := True;
303 //ListView.Selected := nil;
304 UpdateColumns;
305end;
306
307const
308 //W_64: Integer = 64; {Width of thumbnail in ICON view mode}
309 H_64: Integer = 64; {Height of thumbnail size}
310 CheckWidth: Integer = 14; {Width of check mark box}
311 CheckHeight: Integer = 14; {Height of checkmark}
312 CheckBiasTop: Integer = 2; {This aligns the checkbox to be in centered}
313 CheckBiasLeft: Integer = 3; {In the row of the list item display}
314
315function TListViewSort.CompareBoolean(Value1, Value2: Boolean): Integer;
316begin
317 if Value1 > Value2 then Result := 1
318 else if Value1 < Value2 then Result := -1
319 else Result := 0;
320end;
321
322function TListViewSort.CompareInteger(Value1, Value2: Integer): Integer;
323begin
324 if Value1 > Value2 then Result := 1
325 else if Value1 < Value2 then Result := -1
326 else Result := 0;
327end;
328
329function TListViewSort.CompareString(Value1, Value2: string): Integer;
330begin
331 Result := AnsiCompareStr(Value1, Value2);
332// if Value1 > Value2 then Result := -1
333// else if Value1 < Value2 then Result := 1
334// else Result := 0;
335end;
336
337function TListViewSort.CompareTime(Time1, Time2: TDateTime): Integer;
338begin
339 Result := DateUtils.CompareDateTime(Time1, Time2);
340end;
341
342constructor TListViewSort.Create(AOwner: TComponent);
343begin
344 inherited;
345 List := TListObject.Create;
346 List.OwnsObjects := False;
347end;
348
349destructor TListViewSort.Destroy;
350begin
351 List.Free;
352 inherited;
353end;
354
355procedure TListViewSort.DrawCheckMark(Item: TListItem; Checked:
356 Boolean);
357var
358 TP1: TPoint;
359 XBias, YBias: Integer;
360 PenColor: TColor;
361 BrushColor: TColor;
362 BiasTop, BiasLeft: Integer;
363 Rect1: TRect;
364 lRect: TRect;
365 ItemLeft: Integer;
366begin
367 XBias := 0;
368 YBias := 0;
369 BiasTop := 0;
370 BiasLeft := 0;
371 Item.Left := 0;
372 GetCheckBias(XBias, YBias, BiasTop, BiasLeft, ListView);
373 PenColor := ListView.Canvas.Pen.Color;
374 BrushColor := ListView.Canvas.Brush.Color;
375 //TP1 := Item.GetPosition;
376 lRect := Item.DisplayRect(drBounds); // Windows 7 workaround
377 TP1.X := lRect.Left;
378 TP1.Y := lRect.Top;
379 //ShowMessage(IntToStr(Item.Index) + ', ' + IntToStr(GetScrollPos(Item.ListView.Handle, SB_VERT)) + ' ' +
380 // IntToHex(Integer(Item), 8) + ', ' + IntToStr(TP1.X) + ', ' + IntToStr(TP1.Y));
381
382// if Checked then
383 ListView.Canvas.Brush.Color := clWhite;
384 ItemLeft := Item.Left;
385 ItemLeft := 23; // Windows 7 workaround
386
387 Rect1.Left := ItemLeft - CheckWidth - BiasLeft + 1 + XBias;
388 //ShowMessage(IntToStr(Tp1.Y) + ', ' + IntToStr(BiasTop) + ', ' + IntToStr(XBias));
389 Rect1.Top := Tp1.Y + BiasTop + 1 + YBias;
390 Rect1.Right := ItemLeft - BiasLeft - 1 + XBias;
391 Rect1.Bottom := Tp1.Y + BiasTop + CheckHeight - 1 + YBias;
392 //ShowMessage(IntToStr(Rect1.Left) + ', ' + IntToStr(Rect1.Top) + ', ' + IntToStr(Rect1.Right) + ', ' + IntToStr(Rect1.Bottom));
393
394 ListView.Canvas.FillRect(Rect1);
395 //if Checked then ListView.Canvas.Brush.Color := clBlack
396 ListView.Canvas.Brush.Color := clBlack;
397 ListView.Canvas.FrameRect(Rect1);
398 ListView.Canvas.FrameRect(Rect(Rect1.Left - 1, Rect1.Top - 1,
399 Rect1.Right + 1, Rect1.Bottom + 1));
400 if Checked then begin
401 ListView.Canvas.Pen.Color := clBlack;
402 ListView.Canvas.MoveTo(ItemLeft - BiasLeft - 2 + XBias - 2,
403 Tp1.Y + BiasTop + 3 + YBias);
404 ListView.Canvas.LineTo(ItemLeft - BiasLeft - (CheckWidth div 2) + XBias,
405 Tp1.Y + BiasTop + (CheckHeight - 4) + YBias);
406 ListView.Canvas.LineTo(ItemLeft - BiasLeft - (CheckWidth - 3) + XBias,
407 Tp1.Y + BiasTop + (CheckHeight div 2) + YBias - 1);
408
409 ListView.Canvas.MoveTo(ItemLeft - BiasLeft - 2 - 1 + XBias - 2,
410 Tp1.Y + BiasTop + 3 + YBias);
411 ListView.Canvas.LineTo(ItemLeft - BiasLeft - (CheckWidth div 2) - 1 + XBias,
412 Tp1.Y + BiasTop + (CheckHeight - 4) + YBias);
413 ListView.Canvas.LineTo(ItemLeft - BiasLeft - (CheckWidth - 3) - 1 + XBias,
414 Tp1.Y + BiasTop + (CheckHeight div 2) + YBias - 1);
415 end;
416 //ListView.Canvas.Brush.Color := ListView.Color;
417 ListView.Canvas.Brush.Color := BrushColor;
418 ListView.Canvas.Pen.Color := PenColor;
419end;
420
421procedure TListViewSort.GetCheckBias(var XBias, YBias, BiasTop, BiasLeft: Integer;
422 const ListView: TListView);
423begin
424 XBias := 0;
425 YBias := 0;
426 if ListView.ViewStyle = vsICON then
427 begin
428 YBias := H_64 - CheckHeight;
429 XBias := 0;
430 end;
431 BiasTop := CheckBiasTop;
432 BiasLeft := CheckBiasLeft;
433 if ListView.ViewStyle <> vsReport then
434 begin
435 BiasTop := 0;
436 BiasLeft := 0;
437 end;
438end;
439
440procedure TListViewSort.ListViewCustomDrawItem(Sender: TCustomListView;
441 Item: TListItem; State: TCustomDrawState; var DefaultDraw: Boolean);
442begin
443 if Assigned(Item) then begin
444 if ListView.Checkboxes then
445 DrawCheckMark(Item, Item.Checked);
446 if Assigned(FOnCustomDraw) then
447 FOnCustomDraw(Sender, Item, State, DefaultDraw);
448 end;
449end;
450
451procedure TListViewSort.ListViewClick(Sender: TObject);
452var
453 Item: TListItem;
454 Pos: TPoint;
455 DefaultDraw: Boolean;
456begin
457 Pos := ListView.ScreenToClient(Mouse.CursorPos);
458 Item := ListView.GetItemAt(Pos.X, Pos.Y);
459 //ShowMessage(IntToStr(Item.Index) + ', ' + IntToStr(Pos.X) + ', ' + IntToStr(Pos.Y));
460 if Assigned(Item) and (Pos.X < 20) then begin
461
462 Item.Checked := not Item.Checked;
463 //ShowMessage(IntToStr(Item.Index) + ', ' +BoolToStr(Item.Checked));
464 if Assigned(ListView.OnChange) then
465 ListView.OnChange(Self, Item, ctState);
466 DefaultDraw := False;
467 ListViewCustomDrawItem(ListView, Item, [], DefaultDraw);
468 //ListView.UpdateItems(Item.Index, Item.Index);
469 end;
470end;
471
472procedure TListViewSort.UpdateColumns;
473{$IFDEF Windows}
474const
475 HDF_SORTUP = $0400;
476 HDF_SORTDOWN = $0200;
477 SortOrder: array[TSortOrder] of Word = (0, HDF_SORTUP, HDF_SORTDOWN);
478var
479 Item: THDItem;
480 I: Integer;
481begin
482 if Assigned(FListView) then begin
483 FHeaderHandle := ListView_GetHeader(FListView.Handle);
484 for I := 0 to FListView.Columns.Count - 1 do begin
485 {$push}{$warn 5057 off}
486 FillChar(Item, SizeOf(THDItem), 0);
487 {$pop}
488 Item.Mask := HDI_FORMAT;
489 Header_GetItem(FHeaderHandle, I, Item);
490 Item.fmt := Item.fmt and not (HDF_SORTDOWN or HDF_SORTUP);
491 if (Column <> -1) and (I = Column) then
492 Item.fmt := Item.fmt or SortOrder[FOrder];
493 Header_SetItem(FHeaderHandle, I, Item);
494 end;
495 end;
496end;
497{$ELSE}
498begin
499end;
500{$ENDIF}
501
502end.
Note: See TracBrowser for help on using the repository browser.