1{%MainUnit ../comctrls.pp} 2{ 3 ***************************************************************************** 4 This file is part of the Lazarus Component Library (LCL) 5 6 See the file COPYING.modifiedLGPL.txt, included in this distribution, 7 for details about the license. 8 ***************************************************************************** 9} 10 11{ TIconOptions } 12 13procedure TIconOptions.SetArrangement(Value: TIconArrangement); 14begin 15 if FArrangement <> Value then 16 begin 17 FArrangement := Value; 18 if FListView.HandleAllocated then 19 TWSCustomListViewClass(FListView.WidgetSetClass).SetIconArrangement(FListView, Arrangement); 20 end; 21end; 22 23function TIconOptions.GetAutoArrange: Boolean; 24begin 25 Result := FListView.GetProperty(Ord(lvpAutoArrange)); 26end; 27 28function TIconOptions.GetWrapText: Boolean; 29begin 30 Result := FListView.GetProperty(Ord(lvpWrapText)); 31end; 32 33procedure TIconOptions.SetAutoArrange(Value: Boolean); 34begin 35 FListView.SetProperty(Ord(lvpAutoArrange), Value); 36end; 37 38procedure TIconOptions.SetWrapText(Value: Boolean); 39begin 40 FListView.SetProperty(Ord(lvpWrapText), Value); 41end; 42 43procedure TIconOptions.AssignTo(Dest: TPersistent); 44var 45 DestOptions: TIconOptions absolute Dest; 46begin 47 if Dest is TIconOptions then 48 begin 49 DestOptions.Arrangement := Arrangement; 50 DestOptions.AutoArrange := AutoArrange; 51 DestOptions.WrapText := WrapText; 52 end 53 else 54 inherited AssignTo(Dest); 55end; 56 57function TIconOptions.GetOwner: TPersistent; 58begin 59 Result := FListView; 60end; 61 62constructor TIconOptions.Create(AOwner: TCustomListView); 63begin 64 inherited Create; 65 FListView := AOwner; 66 FArrangement := iaTop; 67end; 68 69{ TCustomListViewEditor } 70 71procedure TCustomListViewEditor.ListViewEditorKeyDown(Sender: TObject; 72 var Key: Word; Shift: TShiftState); 73begin 74 if (Shift = []) and Visible then 75 begin 76 if Key = VK_ESCAPE then 77 begin 78 Key := 0; 79 FItem := nil; 80 Visible := False; 81 Parent.SetFocus; 82 end else 83 if Key = VK_RETURN then 84 begin 85 Key := 0; 86 Parent.SetFocus; 87 end; 88 end; 89end; 90 91procedure TCustomListViewEditor.DoExit; 92begin 93 TCustomListView(Parent).HideEditor; 94 inherited DoExit; 95end; 96 97constructor TCustomListViewEditor.Create(AOwner: TComponent); 98begin 99 inherited Create(AOwner); 100 FItem := nil; 101 OnKeyDown := @ListViewEditorKeyDown; 102end; 103 104{------------------------------------------------------------------------------ 105 TCustomListView Constructor 106------------------------------------------------------------------------------} 107constructor TCustomListView.Create(AOwner: TComponent); 108var 109 lvil: TListViewImageList; 110begin 111 inherited Create(AOwner); 112 ControlStyle := ControlStyle - [csCaptureMouse]; 113 FAutoSort := True; 114 FAutoWidthLastColumn := False; 115 FSortDirection := sdAscending; 116 FIconOptions := TIconOptions.Create(Self); 117 FColumns := TListColumns.Create(Self); 118 FListItems := CreateListItems; 119 BorderStyle := bsSingle; 120 FScrollBars := ssBoth; 121 FCompStyle := csListView; 122 FViewStyle := vsList; 123 FSortType := stNone; 124 FSortColumn := -1; 125 126 for lvil := Low(TListViewImageList) to High(TListViewImageList) do 127 begin 128 FImageChangeLinks[lvil] := TChangeLink.Create; 129 FImageChangeLinks[lvil].OnChange := @ImageChanged; 130 FImageChangeLinks[lvil].OnDestroyResolutionHandle := @ImageResolutionHandleDestroyed; 131 end; 132 FHoverTime := -1; 133 TabStop := true; 134 with GetControlClassDefaultSize do 135 SetInitialBounds(0, 0, CX, CY); 136 ParentColor := False; 137 Color := {$ifdef UseCLDefault}clDefault{$else}clWindow{$endif}; 138 FCanvas := TControlCanvas.Create; 139 TControlCanvas(FCanvas).Control := Self; 140 FProperties := [lvpColumnClick, lvpHideSelection, lvpShowColumnHeaders, lvpToolTips, lvpWrapText]; 141 142 FOwnerDataItem := TOwnerDataListItem.Create(FListItems); 143 FEditor := TCustomListViewEditor.Create(Self); 144 FEditor.ControlStyle := FEditor.ControlStyle + [csNoDesignVisible, csNoDesignSelectable]; 145 FEditor.AutoSize := False; 146 FEditor.Visible := False; 147 FEditor.Parent := Self; 148end; 149 150{------------------------------------------------------------------------------ 151 TCustomListView CustomDraw 152------------------------------------------------------------------------------} 153function TCustomListView.CustomDraw(const ARect: TRect; AStage: TCustomDrawStage): Boolean; 154begin 155 Result := True; 156 if Assigned(FOnCustomDraw) and (AStage = cdPrePaint) 157 then FOnCustomDraw(Self, ARect, Result); 158 159 if Assigned(FOnAdvancedCustomDraw) 160 then FOnAdvancedCustomDraw(Self, ARect, AStage, Result) 161end; 162 163{------------------------------------------------------------------------------} 164{ TCustomListView CustomDrawItem } 165{------------------------------------------------------------------------------} 166function TCustomListView.CustomDrawItem(AItem: TListItem; AState: TCustomDrawState; AStage: TCustomDrawStage): Boolean; 167begin 168 Result := True; 169 if Assigned(FOnCustomDrawItem) and (AStage = cdPrePaint) 170 then FOnCustomDrawItem(Self, AItem, AState, Result); 171 172 if Assigned(FOnAdvancedCustomDrawItem) 173 then FOnAdvancedCustomDrawItem(Self, AItem, AState, AStage, Result); 174end; 175 176{------------------------------------------------------------------------------} 177{ TCustomListView CustomDrawSubItem } 178{------------------------------------------------------------------------------} 179function TCustomListView.CustomDrawSubItem(AItem: TListItem; ASubItem: Integer; AState: TCustomDrawState; AStage: TCustomDrawStage): Boolean; 180begin 181 Result := True; 182 if Assigned(FOnCustomDrawSubItem) and (AStage = cdPrePaint) 183 then FOnCustomDrawSubItem(Self, AItem, ASubItem, AState, Result); 184 185 if Assigned(FOnAdvancedCustomDrawSubItem) 186 then FOnAdvancedCustomDrawSubItem(Self, AItem, ASubItem, AState, AStage, Result); 187 188end; 189 190{------------------------------------------------------------------------------} 191{ TCustomListView Change } 192{------------------------------------------------------------------------------} 193procedure TCustomListView.Change(AItem: TListItem; AChange: Integer); 194var 195 ItemChange: TItemChange; 196begin 197 case AChange of 198 LVIF_TEXT: ItemChange := ctText; 199 LVIF_IMAGE: ItemChange := ctImage; 200 LVIF_STATE: ItemChange := ctState; 201 else 202 Exit; 203 end; 204 if Assigned(FOnChange) 205 then FOnChange(Self, AItem, ItemChange); 206end; 207 208{------------------------------------------------------------------------------} 209{ TCustomListView ColClick } 210{------------------------------------------------------------------------------} 211procedure TCustomListView.ColClick(AColumn: TListColumn); 212const 213 DirToIndicator : array [TSortDirection] of TSortIndicator = (siAscending, siDescending); 214var 215 i: Integer; 216begin 217 if IsEditing then 218 begin 219 if FEditor.Focused then 220 begin 221 SetFocus; 222 HideEditor; 223 end; 224 end; 225 if Assigned(FOnColumnClick) and ColumnClick then FOnColumnClick(Self, AColumn); 226 // we set autosort after FOnColumnClick, maybe programmer want to 227 // stop autosorting after some special column is clicked. 228 if FAutoSort then 229 begin 230 if SortType <> stNone then 231 begin 232 if AColumn.Index <> SortColumn then begin 233 if FAutoSortIndicator then 234 for i:=0 to Columns.Count-1 do 235 if (i <> AColumn.Index) and (Columns[i].SortIndicator <> siNone) then 236 Columns[i].SortIndicator := siNone; 237 238 SortColumn := AColumn.Index; 239 SortDirection := sdAscending; 240 if FAutoSortIndicator then AColumn.SortIndicator := siAscending; 241 end 242 else 243 begin 244 // with same column we are changing only direction 245 if SortDirection = sdAscending then 246 SortDirection := sdDescending 247 else 248 SortDirection := sdAscending; 249 if FAutoSortIndicator then AColumn.SortIndicator := DirToIndicator[SortDirection]; 250 end; 251 end; 252 end; 253end; 254 255{------------------------------------------------------------------------------} 256{ TCustomListView CNNotify } 257{------------------------------------------------------------------------------} 258procedure TCustomListView.CNNotify(var AMessage: TLMNotify); 259var 260 nm: PNMListView; 261 Item: TListItem; 262 n: Integer; 263begin 264 nm := PNMListView(AMessage.NMHdr); 265 // ignore any notifications while initializing items 266 if (nm^.iItem >= Items.Count) or 267 not (OwnerData or (lisfWSItemsCreated in FListItems.Flags)) then Exit; 268 //remark: NMHdr^.code is normally unhanged by the win32 interface, so the others 269 // maps there codes to the of win32 270 case AMessage.NMHdr^.code of 271// HDN_TRACK: 272// NM_CUSTOMDRAW: 273 // Custom Drawing is handled direct from the interfaces by IntfCustomDraw 274// LVN_BEGINDRAG: 275 LVN_DELETEITEM: begin 276 Item := FListItems[nm^.iItem]; 277 if FSelected = Item then 278 InvalidateSelected; 279 if Item = nil then Exit; //? nm^.iItem > Items.Count ? 280 Exclude(Item.FFlags, lifCreated); 281 if not (lifDestroying in Item.FFlags) 282 then Item.Delete; 283 end; 284 LVN_DELETEALLITEMS: begin 285 InvalidateSelected; 286 for n := FListItems.Count - 1 downto 0 do 287 begin 288 Item := FListItems[n]; 289 Exclude(Item.FFlags, lifCreated); 290 if not (lifDestroying in Item.FFlags) 291 then Item.Delete; 292 end; 293 end; 294// LVN_GETDISPINFO: 295// LVN_ODCACHEHINT: 296// LVN_ODFINDITEM: 297// LVN_ODSTATECHANGED: 298// LVN_BEGINLABELEDIT: implemented via TCustomListViewEditor 299// LVN_ENDLABELEDIT: implemented via TCustomListViewEditor 300 LVN_COLUMNCLICK: 301 begin 302 ColClick(Columns[nm^.iSubItem]); 303 end; 304 LVN_INSERTITEM: begin 305 // don't call insert yet, 306 // there is no solution available when we have inserted the item first 307 // see delete 308 // besides... who's inserting items 309 end; 310 LVN_ITEMCHANGING: begin 311 //Check 312 end; 313 LVN_ITEMCHANGED: 314 begin 315 if nm^.iItem < 0 then 316 Item := nil 317 else 318 Item := Items[nm^.iItem]; 319 //DebugLn('TCustomListView.CNNotify Count=',dbgs(Items.Count),' nm^.iItem=',dbgs(nm^.iItem),' destroying=',dbgs(lifDestroying in Item.FFlags)); 320 if (Item <> nil) and (not OwnerData) and (lifDestroying in Item.FFlags) then 321 begin 322 if Item=FFocused then 323 FFocused:=nil; 324 if Item=FSelected then 325 InvalidateSelected; 326 end else 327 begin 328 if (nm^.uChanged = LVIF_STATE) then 329 begin 330 // checkbox 331 if Checkboxes then 332 DoItemChecked(Item); 333 334 // focus 335 if (nm^.uOldState and LVIS_FOCUSED) <> (nm^.uNewState and LVIS_FOCUSED) then 336 begin 337 // focus state changed 338 if (nm^.uNewState and LVIS_FOCUSED) = 0 then 339 begin 340 if FFocused = Item then 341 FFocused := nil; 342 end 343 else 344 FFocused := Item; 345 end; 346 // select 347 if (((nm^.uOldState and LVIS_SELECTED) <> (nm^.uNewState and LVIS_SELECTED))) 348 or (not (lffSelectedValid in FFlags) and (nm^.uNewState and LVIS_SELECTED <> 0)) then 349 begin 350 // select state changed 351 if (nm^.uNewState and LVIS_SELECTED) = 0 then 352 begin 353 if not OwnerData then 354 begin 355 if FSelected = Item then 356 InvalidateSelected 357 end else 358 if (nm^.iItem < 0) or (nm^.iItem = FSelectedIdx) then 359 InvalidateSelected; 360 end else 361 begin 362 FSelected := Item; 363 Include(FFlags,lffSelectedValid); 364 if OwnerData then 365 FSelectedIdx:=nm^.iItem; 366 //DebugLn('TCustomListView.CNNotify FSelected=',dbgs(FSelected)); 367 end; 368 Change(Item, nm^.uChanged); 369 DoSelectItem(Item, (nm^.uNewState and LVIS_SELECTED) <> 0); 370 end; 371 end 372 else 373 Change(Item, nm^.uChanged); 374 end; 375 end; 376// LVN_GETINFOTIP: 377// NM_CLICK: 378// NM_RCLICK: 379 end; 380end; 381 382procedure TCustomListView.DrawItem(AItem: TListItem; ARect: TRect; 383 AState: TOwnerDrawState); 384begin 385 if Assigned(FOnDrawItem) then FOnDrawItem(Self, AItem, ARect, AState) 386 else 387 begin 388 FCanvas.FillRect(ARect); 389 FCanvas.TextOut(ARect.Left + 2, ARect.Top, AItem.Caption); 390 end; 391end; 392 393procedure TCustomListView.CNDrawItem(var Message: TLMDrawListItem); 394var 395 State: TOwnerDrawState; 396 SaveIndex: Integer; 397begin 398 if Assigned(FCanvas) then 399 begin 400 with Message.DrawListItemStruct^ do 401 begin 402 State := ItemState; 403 SaveIndex := SaveDC(DC); 404 FCanvas.Lock; 405 try 406 FCanvas.Handle := DC; 407 FCanvas.Font := Font; 408 FCanvas.Brush := Brush; 409 if itemID = DWORD(-1) then 410 FCanvas.FillRect(Area) 411 else 412 DrawItem(Items[itemID], Area, State); 413 finally 414 FCanvas.Handle := 0; 415 FCanvas.Unlock; 416 RestoreDC(DC, SaveIndex); 417 end; 418 end; 419 Message.Result := 1; 420 end; 421end; 422 423procedure TCustomListView.InvalidateSelected; 424begin 425 FSelected:=nil; 426 FSelectedIdx := -1; 427 Exclude(FFlags,lffSelectedValid); 428end; 429 430procedure TCustomListView.HideEditor; 431var 432 S: String; 433begin 434 S := FEditor.Text; 435 if FEditor.Item <> nil then 436 DoEndEdit(FEditor.Item, S); 437 FEditor.Item := nil; 438 FEditor.Visible := False; 439 FEditor.SetBounds(0, 0, 0, 0); 440end; 441 442procedure TCustomListView.ShowEditor; 443var 444 Item: TListItem; 445 R: TRect; 446 TempHeight: Integer; 447 S: String; 448begin 449 if (ItemIndex >= 0) and (ItemIndex < Items.Count) then 450 Item := Items[ItemIndex] 451 else 452 Item := nil; 453 HideEditor; 454 if Item = nil then 455 exit; 456 if not CanEdit(Item) then 457 exit; 458 459 R := Item.DisplayRect(drLabel); 460 if LCLIntf.IsRectEmpty(R) then 461 exit; 462 S := Item.Caption; 463 if S = '' then 464 S := 'H'; 465 TempHeight := Canvas.TextHeight(S); 466 if TempHeight >= R.Bottom - R.Top then 467 TempHeight := TempHeight - (R.Bottom - R.Top) + 4 {border above and below text} 468 else 469 TempHeight := 0; 470 with R do 471 FEditor.SetBounds(Left, Top, Right - Left, (Bottom - Top) + TempHeight); 472 FEditor.Item := Item; 473 FEditor.Text := Item.Caption; 474 FEditor.Visible := True; 475 FEditor.SetFocus; 476end; 477 478procedure TCustomListView.WMHScroll(var message: TLMHScroll); 479begin 480 if IsEditing then 481 begin 482 if FEditor.Focused then 483 SetFocus 484 else 485 HideEditor; 486 end; 487end; 488 489procedure TCustomListView.WMVScroll(var message: TLMVScroll); 490begin 491 if IsEditing then 492 begin 493 if FEditor.Focused then 494 SetFocus 495 else 496 HideEditor; 497 end; 498end; 499 500{------------------------------------------------------------------------------} 501{ TCustomListView IsCustomDrawn } 502{------------------------------------------------------------------------------} 503function TCustomListView.IsCustomDrawn(ATarget: TCustomDrawTarget; AStage: TCustomDrawStage): Boolean; 504begin 505 case ATarget of 506 dtControl: Result := Assigned(FOnAdvancedCustomDraw) 507 or Assigned(FOnAdvancedCustomDrawItem) 508 or Assigned(FOnAdvancedCustomDrawSubItem); 509 dtItem: Result := Assigned(FOnAdvancedCustomDrawItem) 510 or Assigned(FOnAdvancedCustomDrawSubItem); 511 dtSubItem: Result := Assigned(FOnAdvancedCustomDrawSubItem); 512 end; 513 514 if Result then exit; 515 516 // check the normal events only in the prepaint stage 517 if AStage <> cdPrePaint then Exit; 518 519 case ATarget of 520 dtControl: Result := Assigned(FOnCustomDraw) 521 or Assigned(FOnCustomDrawItem) 522 or Assigned(FOnCustomDrawSubItem); 523 dtItem: Result := Assigned(FOnCustomDrawItem) 524 or Assigned(FOnCustomDrawSubItem); 525 dtSubItem: Result := Assigned(FOnCustomDrawSubItem); 526 end; 527end; 528 529{------------------------------------------------------------------------------} 530{ TCustomListView InitializeWnd } 531{------------------------------------------------------------------------------} 532procedure TCustomListView.InitializeWnd; 533var 534 LVC: TWSCustomListViewClass; 535 lvil: TListViewImageList; 536begin 537 inherited InitializeWnd; 538 539 LVC := TWSCustomListViewClass(WidgetSetClass); 540 541 // set the style first 542 LVC.SetViewStyle(Self, FViewStyle); 543 544 // add columns 545 FColumns.WSCreateColumns; 546 547 // set imagelists and item depending properties 548 for lvil := Low(TListViewImageList) to High(TListViewImageList) do 549 begin 550 if FImages[lvil] <> nil 551 then LVC.SetImageList(Self, lvil, FImages[lvil].ResolutionForPPI[FImagesWidth[lvil], Font.PixelsPerInch, 1].Resolution); // to-do: support scaling factor 552 end; 553 LVC.SetScrollBars(Self, FScrollBars); 554 LVC.SetViewOrigin(Self, FViewOriginCache) ; 555 LVC.SetProperties(Self, FProperties); 556 LVC.SetSort(Self, FSortType, FSortColumn, FSortDirection); 557 558 // add items 559 if not OwnerData then 560 begin 561 FListItems.WSCreateItems; 562 // set other properties 563 LVC.SetAllocBy(Self, FAllocBy); 564 end 565 else 566 begin 567 LVC.SetOwnerData(Self, True); 568 LVC.SetItemsCount(Self, FListItems.Count); 569 end; 570 571 LVC.SetDefaultItemHeight(Self, FDefaultItemHeight); 572 LVC.SetHotTrackStyles(Self, FHotTrackStyles); 573 LVC.SetHoverTime(Self, FHoverTime); 574 575 if FSelected <> nil 576 then LVC.ItemSetState(Self, FSelected.Index, FSelected, lisSelected, True); 577 if FFocused <> nil 578 then LVC.ItemSetState(Self, FFocused.Index, FFocused, lisFocused, True); 579end; 580 581{------------------------------------------------------------------------------} 582{ TCustomListView DoDeletion } 583{------------------------------------------------------------------------------} 584procedure TCustomListView.DoDeletion(AItem: TListItem); 585begin 586 if not (TMethod(@Self.Delete).Code = Pointer(@TCustomListView.Delete)) then 587 //There is an override for Delete, so use that for Delphi compatibility. Issue #0038263 588 Delete(AItem) 589 else 590 //if you change the code below, make sure to change it in TCustomListView.Delete as well 591 if not (csDestroying in ComponentState) and Assigned(FOnDeletion) then 592 FOnDeletion(Self, AItem); 593end; 594 595{------------------------------------------------------------------------------} 596{ TCustomListView Delete } 597{------------------------------------------------------------------------------} 598procedure TCustomListView.Delete(AItem : TListItem); 599begin 600 { 601 In Delphi Delete is called in reaction to Items.Delete, but 602 if you call it directly it will also do the actual deletion and then call the OnDeletion handler 603 In that case we simply call Items.Delete and this will then call Delete again and in the second run 604 we call the OnDeletion handler. 605 Not 100% Delphi compatible, but more compatible then it was before. (BB) 606 } 607 //debugln(['TCustomListView.Delete: (lifDestroying in AItem.FFlags)=',(lifDestroying in AItem.FFlags)]); 608 if not (lifDestroying in AItem.FFlags) then 609 begin 610 AItem.Delete; 611 Exit; 612 end; 613 if not (csDestroying in ComponentState) and Assigned(FOnDeletion) then 614 FOnDeletion(Self, AItem); 615end; 616 617{------------------------------------------------------------------------------} 618{ TCustomListView DoInsert } 619{------------------------------------------------------------------------------} 620procedure TCustomListView.DoInsert(AItem: TListItem); 621begin 622 if not (TMethod(@Self.InsertItem).Code = Pointer(@TCUstomListView.InsertItem)) then 623 //There is an override for InsertItem, so use that for Delphi compatibility. Issue #0038263 624 InsertItem(AItem) 625 else 626 //if you change the code below, make sure to change it in TCustomListView.Delete as well 627 if Assigned(FOnInsert) then FOnInsert(Self, AItem); 628end; 629 630{------------------------------------------------------------------------------} 631{ TCustomListView InsertItem } 632{------------------------------------------------------------------------------} 633procedure TCustomListView.InsertItem(AItem : TListItem); 634begin 635 if Assigned(FOnInsert) then FOnInsert(Self, AItem); 636end; 637 638{------------------------------------------------------------------------------} 639{ TCustomListView DoItemChecked } 640{------------------------------------------------------------------------------} 641procedure TCustomListView.DoItemChecked(AItem: TListItem); 642var 643 B: Boolean; 644begin 645 if (not HandleAllocated) or (csLoading in ComponentState) then exit; 646 B := TWSCustomListViewClass(WidgetSetClass).ItemGetChecked(Self, 647 AItem.Index, AItem); 648 if B <> AItem.GetCheckedInternal then 649 begin 650 AItem.Checked := B; 651 if Assigned(FOnItemChecked) then 652 FOnItemChecked(Self, AItem); 653 end; 654end; 655 656{------------------------------------------------------------------------------} 657{ TCustomListView DoSelectItem } 658{------------------------------------------------------------------------------} 659procedure TCustomListView.DoSelectItem(AItem: TListItem; ASelected: Boolean); 660begin 661 if Assigned(AItem) then 662 AItem.Selected:=ASelected; 663 if Assigned(FOnSelectItem) and 664 ([lffItemsMoving, lffItemsSorting] * FFlags = []) then 665 FOnSelectItem(Self, AItem, ASelected); 666end; 667 668procedure TCustomListView.DoAutoAdjustLayout( 669 const AMode: TLayoutAdjustmentPolicy; const AXProportion, AYProportion: Double 670 ); 671var 672 i: Integer; 673 C: TListColumn; 674 L: TListViewImageList; 675begin 676 inherited; 677 678 if AMode in [lapAutoAdjustWithoutHorizontalScrolling, lapAutoAdjustForDPI] then 679 begin 680 for i := ColumnCount - 1 downto 0 do 681 begin 682 C := Column[i]; 683 C.MaxWidth := Round(C.MaxWidth * AXProportion); 684 C.MinWidth := Round(C.MinWidth * AXProportion); 685 C.Width := Round(C.Width * AXProportion); 686 end; 687 688 for L in TListViewImageList do 689 SetImageListWS(L); 690 end; 691end; 692 693procedure TCustomListView.DoSetBounds(ALeft, ATop, AWidth, AHeight: integer); 694begin 695 inherited DoSetBounds(ALeft, ATop, AWidth, AHeight); 696 if AutoWidthLastColumn then 697 ResizeLastColumn; 698end; 699 700procedure TCustomListView.DoEndEdit(AItem: TListItem; const AValue: String); 701var 702 S: string; 703begin 704 if not (TMethod(@Self.Edit).Code = Pointer(@TCustomListView.Edit)) then 705 //There is an override for Edit, so use that for Delphi compatibility. Issue #0038263 706 Edit(AItem) 707 else 708 begin 709 //if you change the code below, make sure to change it in TCustomListView.Edit as well 710 S := AValue; 711 if Assigned(FOnEdited) then 712 FOnEdited(Self, AItem, S); 713 if AItem <> nil then 714 AItem.Caption := S; 715 end; 716end; 717 718procedure TCustomListView.Edit(AItem: TListItem); 719var 720 S: String; 721begin 722 //debugln(['TCustomListView.Edit: FEditor.Text=',FEditor.Text]); 723 S := FEditor.Text; 724 if Assigned(FOnEdited) then 725 FOnEdited(Self, AItem, S); 726 if AItem <> nil then 727 AItem.Caption := S; 728end; 729 730{------------------------------------------------------------------------------} 731{ TCustomListView ItemDeleted } 732{------------------------------------------------------------------------------} 733procedure TCustomListView.ItemDeleted(const AItem: TListItem); //called by TListItems 734begin 735 //DebugLn('TCustomListView.ItemDeleted ',dbgs(AItem),' FSelected=',dbgs(FSelected)); 736 if FSelected = AItem then InvalidateSelected; 737 if FFocused = AItem then FFocused := nil; 738 DoDeletion(AItem); 739end; 740 741{------------------------------------------------------------------------------} 742{ TCustomListView ItemInserted } 743{------------------------------------------------------------------------------} 744procedure TCustomListView.ItemInserted(const AItem: TListItem); 745begin 746 if csDestroying in Componentstate then Exit; 747 DoInsert(AItem); 748end; 749 750class procedure TCustomListView.WSRegisterClass; 751begin 752 RegisterPropertyToSkip(Self, 'ItemIndex', 'Property streamed in older Lazarus revision', ''); 753 RegisterPropertyToSkip(Self, 'BevelKind', 'VCL compatibility property', ''); 754 RegisterPropertyToSkip(TListItem, 'OverlayIndex', 'VCL compatibility property', ''); 755 inherited WSRegisterClass; 756 RegisterCustomListView; 757end; 758 759class function TCustomListView.GetControlClassDefaultSize: TSize; 760begin 761 Result.CX := 250; 762 Result.CY := 150; 763end; 764 765{------------------------------------------------------------------------------} 766{ TCustomListView SetItems } 767{------------------------------------------------------------------------------} 768procedure TCustomListView.SetItems(const AValue : TListItems); 769begin 770end; 771 772{------------------------------------------------------------------------------} 773{ TCustomListView SetItemVisible } 774{------------------------------------------------------------------------------} 775procedure TCustomListView.SetItemVisible(const AValue : TListItem; 776 const APartialOK: Boolean); 777begin 778 if (not HandleAllocated) or (csLoading in ComponentState) then exit; 779 TWSCustomListViewClass(WidgetSetClass).ItemShow( 780 Self, AValue.Index, AValue, APartialOK); 781end; 782 783 784 785function TCustomListView.IntfCustomDraw(ATarget: TCustomDrawTarget; AStage: TCustomDrawStage; AItem, ASubItem: Integer; AState: TCustomDrawState; const ARect: PRect): TCustomDrawResult; 786begin 787 Result := []; 788 789 // in the prepaint stage, return the notifications we want 790 if AStage = cdPrePaint 791 then begin 792 case ATarget of 793 dtControl: begin 794 if IsCustomDrawn(dtItem, cdPrePaint) or IsCustomDrawn(dtItem, cdPreErase) 795 then Include(Result, cdrNotifyItemDraw); 796 797 if IsCustomDrawn(dtItem, cdPostPaint) 798 then Include(Result, cdrNotifyPostPaint); 799 800 if IsCustomDrawn(dtItem, cdPostErase) 801 then Include(Result, cdrNotifyPostErase); 802 803 if IsCustomDrawn(dtSubItem, cdPrePaint) 804 then Include(Result, cdrNotifySubitemDraw); 805 end; 806 dtItem: begin 807 if IsCustomDrawn(dtItem, cdPostPaint) 808 then Include(Result, cdrNotifyPostPaint); 809 810 if IsCustomDrawn(dtItem, cdPostErase) 811 then Include(Result, cdrNotifyPostErase); 812 813 if IsCustomDrawn(dtSubItem, cdPrePaint) 814 then Include(Result, cdrNotifySubitemDraw); 815 end; 816 dtSubItem: begin 817 if IsCustomDrawn(dtSubItem, cdPostPaint) 818 then Include(Result, cdrNotifyPostPaint); 819 820 if IsCustomDrawn(dtSubItem, cdPostErase) 821 then Include(Result, cdrNotifyPostErase); 822 end; 823 end; 824 end; 825 826 if not IsCustomDrawn(ATarget, AStage) then Exit; 827 828 829 case ATarget of 830 dtControl: if CustomDraw(ARect^, AStage) then Exit; 831 dtItem: if CustomDrawItem(Items[AItem], AState, AStage) then Exit; 832 dtSubItem: if CustomDrawSubItem(Items[AItem], ASubItem, AState, AStage) then Exit; 833 end; 834 835 // if we are here, a custom step returned false, so no default drawing 836 if AStage = cdPrePaint 837 then Result := [cdrSkipDefault]; 838end; 839 840function TCustomListView.GetUpdateCount: Integer; 841begin 842 Result := FUpdateCount; 843end; 844 845procedure TCustomListView.DoGetOwnerData(Item: TListItem); 846begin 847 if Assigned(OnData) then OnData(Self, Item); 848end; 849 850function TCustomListView.DoOwnerDataHint(AStartIndex, AEndIndex: Integer 851 ): Boolean; 852begin 853 Result := Assigned(FOnDataHint); 854 if Result then 855 FOnDataHint(Self, AStartIndex, AEndIndex); 856end; 857 858function TCustomListView.DoOwnerDataStateChange(AStartIndex, 859 AEndIndex: Integer; AOldState, ANewState: TListItemStates): Boolean; 860begin 861 Result := Assigned(FOnDataStateChange); 862 if Result then 863 FOnDataStateChange(Self, AStartIndex, AEndIndex, AOldState, ANewState); 864end; 865 866procedure TCustomListView.QueuedShowEditor(Data: PtrInt); 867begin 868 if fShowEditorQueued and IsVisible then 869 begin 870 fShowEditorQueued:=false; 871 ShowEditor; 872 end; 873end; 874 875procedure TCustomListView.DblClick; 876begin 877 inherited DblClick; 878 if not ReadOnly and Assigned(FEditor) then 879 ShowEditorQueued:=true; 880end; 881 882procedure TCustomListView.KeyDown(var Key: Word; Shift: TShiftState); 883begin 884 if not ReadOnly and (Key = VK_F2) and (Shift = []) then 885 begin 886 ShowEditor; 887 Key := 0; 888 end else 889 inherited KeyDown(Key, Shift); 890end; 891 892{------------------------------------------------------------------------------} 893{ TCustomListView SetColumns } 894{------------------------------------------------------------------------------} 895procedure TCustomListView.SetColumns(const AValue: TListColumns); 896begin 897 if AValue=FColumns then exit; 898 BeginUpdate; 899 FColumns.Assign(AValue); 900 EndUpdate; 901 if ([csDesigning,csLoading,csReading]*ComponentState=[csDesigning]) then 902 OwnerFormDesignerModified(Self); 903end; 904 905 906{------------------------------------------------------------------------------} 907{ TCustomListView SetViewOrigin } 908{------------------------------------------------------------------------------} 909procedure TCustomListView.SetViewOrigin(AValue: TPoint); 910begin 911 if AValue.X < 0 then AValue.X := 0; 912 if AValue.Y < 0 then AValue.Y := 0; 913 if HandleAllocated 914 then begin 915 TWSCustomListViewClass(WidgetSetClass).SetViewOrigin(Self, AValue); 916 end 917 else begin 918 FViewOriginCache := AValue; 919 end; 920end; 921 922{------------------------------------------------------------------------------} 923{ TCustomListView SetViewStyle } 924{------------------------------------------------------------------------------} 925procedure TCustomListView.SetViewStyle(const Avalue: TViewStyle); 926begin 927 if FViewStyle = AValue then Exit; 928 FViewStyle := AValue; 929 if not HandleAllocated then Exit; 930 TWSCustomListViewClass(WidgetSetClass).SetViewStyle(Self, AValue); 931end; 932 933{------------------------------------------------------------------------------} 934{ TCustomListView SetSortType } 935{------------------------------------------------------------------------------} 936procedure TCustomListView.SetSortType(const AValue: TSortType); 937begin 938 if FSortType = AValue then Exit; 939 FSortType := AValue; 940 Sort; 941end; 942 943{------------------------------------------------------------------------------} 944{ TCustomListView SetSortColumn } 945{------------------------------------------------------------------------------} 946procedure TCustomListView.SetSortColumn(const AValue : Integer); 947begin 948 if FSortColumn = AValue then Exit; 949 FSortColumn := AValue; 950 Sort; 951end; 952 953procedure TCustomListView.SetSortDirection(const AValue: TSortDirection); 954begin 955 if FSortDirection=AValue then exit; 956 FSortDirection:=AValue; 957 Sort; 958end; 959 960function _CompareListViewItems_CustomSort(Item1, Item2: Pointer): Integer; 961var 962 ListView: TCustomListView; 963begin 964 Result := 0; 965 ListView := TListItem(Item1).Owner.Owner; 966 967 if Assigned(ListView.FCustomSort_Func) then 968 Result := ListView.FCustomSort_Func(TListItem(Item1), TListItem(Item2), ListView.FCustomSort_Param) 969 else 970 if Assigned(ListView.FOnCompare) then 971 ListView.FOnCompare(ListView, TListItem(Item1), TListItem(Item2), 0, Result); 972end; 973 974function _CompareListViewItems(Item1, Item2: Pointer): Integer; 975var 976 Str1: String; 977 Str2: String; 978 ListView: TCustomListView; 979begin 980 Result := 0; 981 ListView := TListItem(Item1).Owner.Owner; 982 if Assigned(ListView.FOnCompare) then 983 ListView.FOnCompare(ListView, TListItem(Item1), TListItem(Item2),0 ,Result) 984 else 985 begin 986 if ListView.SortType in [stData] then 987 Result := CompareValue(PtrUInt(TListItem(Item1).Data), PtrUInt(TListItem(Item2).Data)) 988 else 989 begin 990 if ListView.FSortColumn = 0 then 991 begin 992 Str1 := TListItem(Item1).Caption; 993 Str2 := TListItem(Item2).Caption; 994 end else 995 begin 996 if ListView.FSortColumn <= TListItem(Item1).SubItems.Count then 997 Str1 := TListItem(Item1).SubItems.Strings[ListView.FSortColumn-1] 998 else 999 Str1 := ''; 1000 if ListView.FSortColumn <= TListItem(Item2).SubItems.Count then 1001 Str2 := TListItem(Item2).SubItems.Strings[ListView.FSortColumn-1] 1002 else 1003 Str2 := ''; 1004 end; 1005 Result := AnsiCompareText(Str1, Str2); 1006 end; 1007 if ListView.SortDirection = sdDescending then 1008 Result := -Result; 1009 end; 1010end; 1011 1012{------------------------------------------------------------------------------} 1013{ TCustomListView Sort } 1014{------------------------------------------------------------------------------} 1015procedure TCustomListView.Sort; 1016begin 1017 if FSortType = stNone then exit; 1018 if (FSortColumn < 0) or (FSortColumn >= ColumnCount) then exit; 1019 SortWithParams(@_CompareListViewItems); 1020end; 1021 1022function TCustomListView.CustomSort(ASortProc: TLVCompare; AOptionalParam: PtrInt): Boolean; 1023begin 1024 Result := Assigned(ASortProc) or Assigned(FOnCompare); 1025 if Result then 1026 begin 1027 FCustomSort_Func := ASortProc; 1028 FCustomSort_Param := AOptionalParam; 1029 SortWithParams(@_CompareListViewItems_CustomSort); 1030 end; 1031end; 1032 1033procedure TCustomListView.SortWithParams(ACompareFunc: TListSortCompare); 1034var 1035 FSavedSelection: TFPList; 1036 FSavedFocused: TListItem; 1037 FSavedChecked: TFPList; 1038 FSavedCheckItem: TListItem; 1039 i: Integer; 1040 AItemIndex: Integer; 1041begin 1042 if FListItems.Count < 2 then exit; 1043 if lffPreparingSorting in FFlags then exit; 1044 1045 if HandleAllocated then 1046 begin 1047 Include(FFlags, lffItemsSorting); 1048 FSavedSelection := TFPList.Create; 1049 FSavedChecked := nil; 1050 try 1051 if (ItemIndex >= 0) then 1052 FSavedFocused := Items[ItemIndex] 1053 else 1054 FSavedFocused := nil; 1055 if Assigned(Selected) then 1056 begin 1057 FSavedSelection.Add(Selected); 1058 if MultiSelect then 1059 for i := 0 to Items.Count-1 do 1060 if Items[i].Selected and (Items[i] <> Selected) then 1061 FSavedSelection.Add(Items[i]); 1062 end; 1063 if (TWSCustomListViewClass(WidgetSetClass).RestoreItemCheckedAfterSort(Self)) 1064 and Items.WSUpdateAllowed 1065 and not OwnerData then 1066 begin 1067 FSavedChecked := TFPList.Create; 1068 for i := 0 to Items.Count - 1 do 1069 if Items[i].Checked then 1070 FSavedChecked.Add(Items[i]); 1071 end; 1072 1073 Items.FCacheIndex := -1; 1074 Items.FCacheItem := nil; 1075 1076 FListItems.FItems.Sort(ACompareFunc); 1077 TWSCustomListViewClass(WidgetSetClass).SetSort(Self, FSortType, 1078 FSortColumn, FSortDirection); 1079 1080 if (FSavedSelection.Count > 0) or Assigned(FSavedFocused) then 1081 begin 1082 Selected := nil; // unselect all 1083 1084 if FSavedFocused <> nil then 1085 TWSCustomListViewClass(WidgetSetClass).ItemSetState(Self, 1086 FSavedFocused.Index, FSavedFocused, lisFocused, True); 1087 1088 for i := FSavedSelection.Count - 1 downto 0 do 1089 begin 1090 AItemIndex := Items.IndexOf(TListItem(FSavedSelection.Items[i])); 1091 if AItemIndex <> -1 then 1092 TWSCustomListViewClass(WidgetSetClass).ItemSetState(Self, 1093 AItemIndex, Items[AItemIndex], lisSelected, True); 1094 end; 1095 1096 if FSavedChecked <> nil then 1097 begin 1098 for i := 0 to FSavedChecked.Count - 1 do 1099 begin 1100 FSavedCheckItem := TListItem( FSavedChecked[i] ); 1101 // todo: this is inefficient, because FSavedCheckItem.Index must be called again 1102 TWSCustomListViewClass(WidgetSetClass).ItemSetChecked(Self, 1103 FSavedCheckItem.Index, FSavedCheckItem, True); 1104 end; 1105 FSavedChecked.Free; 1106 end; 1107 1108 end; 1109 finally 1110 FreeThenNil(FSavedSelection); 1111 Exclude(FFlags, lffItemsSorting); 1112 end; 1113 end else 1114 FListItems.FItems.Sort(ACompareFunc); 1115end; 1116 1117{------------------------------------------------------------------------------} 1118{ TCustomListView Destructor } 1119{------------------------------------------------------------------------------} 1120destructor TCustomListView.Destroy; 1121var 1122 lvil: TListViewImageList; 1123begin 1124 Application.RemoveAsyncCalls(Self); 1125 // Better destroy the wincontrol (=widget) first. So wo don't have to delete 1126 // all items/columns and we won't get notifications for each. 1127 FreeAndNil(FCanvas); 1128 inherited Destroy; 1129 FreeAndNil(FColumns); 1130 for lvil := Low(TListViewImageList) to High(TListViewImageList) do 1131 FreeAndNil(FImageChangeLinks[lvil]); 1132 FreeAndNil(FOwnerDataItem); 1133 FreeAndNil(FListItems); 1134 FreeAndNil(FIconOptions); 1135end; 1136 1137procedure TCustomListView.AddItem(Item: string; AObject: TObject); 1138var 1139 AItem: TListItem; 1140begin 1141 AItem := Items.Add; 1142 AItem.Caption := Item; 1143 AItem.Data := AObject; 1144end; 1145 1146function TCustomListView.AlphaSort: Boolean; 1147begin 1148 Result := False; 1149 Include(FFlags, lffPreparingSorting); 1150 // always reset direction, so sort triggers later ! 1151 SortDirection := sdDescending; 1152 SortType := stText; 1153 SortColumn := 0; 1154 Exclude(FFlags, lffPreparingSorting); 1155 // now trigger sort when all rules are applied 1156 SortDirection := sdAscending; 1157 Result := True; 1158end; 1159 1160{------------------------------------------------------------------------------ 1161 TCustomListView DestroyWnd 1162 Params: None 1163 Result: none 1164 1165 Frees the canvas 1166 ------------------------------------------------------------------------------} 1167procedure TCustomListView.DestroyWnd; 1168begin 1169 if FCanvas<>nil then 1170 TControlCanvas(FCanvas).FreeHandle; 1171 inherited DestroyWnd; 1172end; 1173 1174procedure TCustomListView.BeginAutoDrag; 1175begin 1176 BeginDrag(False); 1177end; 1178 1179function TCustomListView.CreateListItem: TListItem; 1180var 1181 AItemClass: TListItemClass; 1182begin 1183 AItemClass := TListItem; 1184 if Assigned(OnCreateItemClass) then 1185 OnCreateItemClass(Self, AItemClass); 1186 Result := AItemClass.Create(Items); 1187end; 1188 1189function TCustomListView.CreateListItems: TListItems; 1190begin 1191 Result := TListItems.Create(Self); 1192end; 1193 1194{------------------------------------------------------------------------------ 1195 TCustomListView BeginUpdate 1196 Params: None 1197 Result: none 1198 1199 Increases the update count. Use this procedure before any big change, so that 1200 the interface will not show any single step. 1201 ------------------------------------------------------------------------------} 1202procedure TCustomListView.BeginUpdate; 1203begin 1204 Inc(FUpdateCount); 1205 Include(FFlags, lffPreparingSorting); 1206 if (FUpdateCount = 1) and HandleAllocated 1207 then TWSCustomListViewClass(WidgetSetClass).BeginUpdate(Self); 1208end; 1209 1210function TCustomListView.CanEdit(Item: TListItem): Boolean; 1211begin 1212 Result := True; 1213 if Assigned(FOnEditing) then 1214 FOnEditing(Self, Item, Result); 1215end; 1216 1217procedure TCustomListView.Clear; 1218begin 1219 FListItems.Clear; 1220end; 1221 1222{------------------------------------------------------------------------------} 1223{ TCustomListView EndUpdate } 1224{------------------------------------------------------------------------------} 1225procedure TCustomListView.EndUpdate; 1226begin 1227 if FUpdateCount <= 0 1228 then RaiseGDBException('TCustomListView.EndUpdate FUpdateCount=0'); 1229 1230 Dec(FUpdateCount); 1231 if FUpdateCount=0 then Exclude(FFlags, lffPreparingSorting); 1232 if (FUpdateCount = 0) and HandleAllocated 1233 then TWSCustomListViewClass(WidgetSetClass).EndUpdate(Self); 1234end; 1235 1236procedure TCustomListView.Repaint; 1237begin 1238 if OwnerData then 1239 // the last cached item might be left updated, because OnData isn't called! 1240 FOwnerDataItem.SetDataIndex(-1); 1241 inherited Repaint; 1242end; 1243 1244procedure TCustomListView.FinalizeWnd; 1245begin 1246 FShowEditorQueued:=false; 1247 // store origin 1248 FViewOriginCache := TWSCustomListViewClass(WidgetSetClass).GetViewOrigin(Self); 1249 if not OwnerData then 1250 FListItems.DoFinalizeWnd; 1251 Columns.DoFinalizeWnd; 1252 inherited FinalizeWnd; 1253end; 1254 1255function TCustomListView.FindCaption(StartIndex: Integer; Value: string; 1256 Partial, Inclusive, Wrap: Boolean; PartStart: Boolean = True): TListItem; 1257begin 1258 Result := FListItems.FindCaption(StartIndex, Value, Partial, Inclusive, Wrap, PartStart); 1259end; 1260 1261function TCustomListView.FindData(StartIndex: Integer; Value: Pointer; 1262 Inclusive, Wrap: Boolean): TListItem; 1263begin 1264 Result := FListItems.FindData(StartIndex, Value, Inclusive, Wrap); 1265end; 1266 1267function TCustomListView.GetBoundingRect: TRect; 1268begin 1269 if not HandleAllocated 1270 then Result := Rect(0,0,0,0) 1271 else Result := TWSCustomListViewClass(WidgetSetClass).GetBoundingRect(Self); 1272end; 1273 1274function TCustomListView.GetColumnCount: Integer; 1275begin 1276 Result := FColumns.Count; 1277end; 1278 1279function TCustomListView.GetColumnFromIndex(AIndex: Integer): TListColumn; 1280begin 1281 Result := FColumns[AIndex]; 1282end; 1283 1284function TCustomListView.GetDropTarget: TListItem; 1285var 1286 idx: Integer; 1287begin 1288 if not HandleAllocated 1289 then idx := -1 1290 else idx := TWSCustomListViewClass(WidgetSetClass).GetDropTarget(Self); 1291 if idx = -1 1292 then Result := nil 1293 else Result := FListItems[idx]; 1294end; 1295 1296function TCustomListView.GetFocused: TListItem; 1297begin 1298 Result := FFocused; 1299end; 1300 1301function TCustomListView.GetImageList(const ALvilOrd: Integer): TCustomImageList; 1302begin 1303 Result := FImages[TListViewImageList(ALvilOrd)]; 1304end; 1305 1306function TCustomListView.GetImageListWidth(const ALvilOrd: Integer): Integer; 1307begin 1308 Result := FImagesWidth[TListViewImageList(ALvilOrd)]; 1309end; 1310 1311function TCustomListView.GetHoverTime: Integer; 1312begin 1313 if HandleAllocated 1314 then Result := TWSCustomListViewClass(WidgetSetClass).GetHoverTime(Self) 1315 else Result := FHoverTime; 1316end; 1317 1318function TCustomListView.GetItemIndex: Integer; 1319begin 1320 Result := -1; 1321 if not OwnerData then begin 1322 if Selected = nil then Exit; 1323 Result := Selected.Index 1324 end else 1325 Result := FSelectedIdx; 1326end; 1327 1328function TCustomListView.GetHitTestInfoAt(X, Y: Integer): THitTests; 1329begin 1330 Result := []; 1331 if HandleAllocated then 1332 Result := TWSCustomListViewClass(WidgetSetClass).GetHitTestInfoAt( Self, X, Y ); 1333end; 1334 1335function TCustomListView.GetItemAt(x, y: integer): TListItem; 1336var 1337 Item: Integer; 1338begin 1339 Result := nil; 1340 if HandleAllocated 1341 then begin 1342 Item := TWSCustomListViewClass(WidgetSetClass).GetItemAt(Self,x,y); 1343 if Item <> -1 1344 then Result := Items[Item]; 1345 end; 1346end; 1347 1348function TCustomListView.GetNearestItem(APoint: TPoint; 1349 Direction: TSearchDirection): TListItem; 1350var 1351 AItem: TListItem; 1352 AIndex: Integer; 1353begin 1354 Result := nil; 1355 AItem := GetItemAt(APoint.x, APoint.y); 1356 if Assigned(AItem) then 1357 begin 1358 AIndex := AItem.Index; 1359 case Direction of 1360 sdAbove: if AIndex - 1 >= 0 then Result := Items[AIndex - 1]; 1361 sdBelow: if AIndex - 1 < Items.Count then 1362 Result := Items[AIndex + 1]; 1363 end; 1364 end; 1365end; 1366 1367function TCustomListView.GetNextItem(StartItem: TListItem; 1368 Direction: TSearchDirection; States: TListItemStates): TListItem; 1369begin 1370 Result := nil; 1371 if HandleAllocated then 1372 Result := TWSCustomListViewClass(WidgetSetClass).GetNextItem(Self, StartItem, Direction, States); 1373end; 1374 1375procedure TCustomListView.ClearSelection; 1376begin 1377 Self.BeginUpdate; 1378 if MultiSelect then 1379 begin 1380 if HandleAllocated then 1381 TWSCustomListViewClass(WidgetSetClass).SelectAll(Self, False); 1382 Items.ClearSelection; 1383 end else 1384 if (ItemIndex >= 0) and (ItemIndex < Items.Count) then 1385 Items.Item[ItemIndex].Selected := False; 1386 Self.EndUpdate; 1387end; 1388 1389procedure TCustomListView.SelectAll; 1390begin 1391 if not MultiSelect then 1392 exit; 1393 Self.BeginUpdate; 1394 if HandleAllocated then 1395 TWSCustomListViewClass(WidgetSetClass).SelectAll(Self, True); 1396 Items.SelectAll; 1397 Self.EndUpdate; 1398end; 1399 1400function TCustomListView.IsEditing: Boolean; 1401begin 1402 Result := Assigned(Self.FEditor) and FEditor.Visible; 1403end; 1404 1405function TCustomListView.GetProperty(const ALvpOrd: Integer): Boolean; 1406begin 1407 Result := (TListViewProperty(ALvpOrd) in FProperties); 1408end; 1409 1410function TCustomListView.GetSelCount: Integer; 1411var 1412 i: integer; 1413begin 1414 if HandleAllocated 1415 then Result := TWSCustomListViewClass(WidgetSetClass).GetSelCount(Self) 1416 else 1417 begin 1418 Result := 0; 1419 for i := 0 to Items.Count - 1 do 1420 if Items[i].Selected then 1421 inc(Result); 1422 end; 1423end; 1424 1425{------------------------------------------------------------------------------ 1426 TCustomListView GetSelection 1427------------------------------------------------------------------------------} 1428function TCustomListView.GetSelection: TListItem; 1429var 1430 i: Integer; 1431begin 1432 if OwnerData and not MultiSelect then 1433 begin 1434 if FSelectedIdx>=0 then begin 1435 FOwnerDataItem.SetDataIndex(FSelectedIdx); 1436 Result:=FOwnerDataItem; 1437 end else 1438 Result:=nil; 1439 end 1440 else begin 1441 { according to Delphi docs we always must return first selected item, 1442 not the last selected one see issue #16773 } 1443 if not (lffSelectedValid in FFlags) or MultiSelect then 1444 begin 1445 FSelected := nil; 1446 for i := 0 to Items.Count - 1 do 1447 begin 1448 if Items[i].Selected then 1449 begin 1450 FSelected := Items[i]; 1451 break; 1452 end; 1453 end; 1454 Include(FFlags, lffSelectedValid); 1455 end; 1456 Result := FSelected; 1457 end; 1458end; 1459 1460function TCustomListView.GetTopItem: TListItem; 1461var 1462 idx: Integer; 1463begin 1464 if ViewStyle in [vsSmallIcon, vsIcon] 1465 then idx := -1 1466 else idx := TWSCustomListViewClass(WidgetSetClass).GetTopItem(Self); 1467 if idx = -1 1468 then Result := nil 1469 else Result := FListItems[idx]; 1470end; 1471 1472{------------------------------------------------------------------------------} 1473{ TCustomListView GetViewOrigin } 1474{------------------------------------------------------------------------------} 1475function TCustomListView.GetViewOrigin: TPoint; 1476begin 1477 if HandleAllocated 1478 then begin 1479 Result := TWSCustomListViewClass(WidgetSetClass).GetViewOrigin(Self); 1480 end 1481 else begin 1482 Result := FViewOriginCache; 1483 end; 1484end; 1485 1486 1487function TCustomListView.GetVisibleRowCount: Integer; 1488begin 1489 if ViewStyle in [vsReport, vsList] 1490 then Result := TWSCustomListViewClass(WidgetSetClass).GetVisibleRowCount(Self) 1491 else Result := 0; 1492end; 1493 1494procedure TCustomListView.SetAllocBy(const AValue: Integer); 1495begin 1496 if FAllocBy = AValue then Exit; 1497 FAllocBy := AValue; 1498 if not HandleAllocated then Exit; 1499 TWSCustomListViewClass(WidgetSetClass).SetAllocBy(Self, AValue); 1500end; 1501 1502procedure TCustomListView.ResizeLastColumn; 1503var 1504 i: Integer; 1505 LastVisibleColumn: Integer; 1506 Accu: Integer; 1507 W: Integer; 1508 NewWidth: Integer; 1509begin 1510 if not (ViewStyle in [vsList, vsReport]) or (ColumnCount = 0) then 1511 exit; 1512 LastVisibleColumn := -1; 1513 1514 // find last visible column 1515 for i := ColumnCount - 1 downto 0 do 1516 begin 1517 if Column[i].Visible then 1518 begin 1519 LastVisibleColumn := i; 1520 break; 1521 end; 1522 end; 1523 1524 // calculate size and apply it only if it's > 0 1525 if LastVisibleColumn >= 0 then 1526 begin 1527 //TODO: gtk2 doesnt return correct ClientWidth. win32 and qt works ok. 1528 W := ClientWidth - (BorderWidth * 2); 1529 Accu := 0; 1530 for i := 0 to LastVisibleColumn - 1 do 1531 begin 1532 if Column[i].Visible then 1533 Accu := Accu + Column[i].Width; 1534 end; 1535 NewWidth := W - Accu; 1536 if NewWidth > 0 then 1537 begin 1538 // now set AutoSize and MinWidth/MaxWidth to 0 1539 Column[LastVisibleColumn].AutoSize := False; 1540 Column[LastVisibleColumn].MinWidth := 0; 1541 Column[LastVisibleColumn].MaxWidth := 0; 1542 Column[LastVisibleColumn].Width := NewWidth; 1543 end; 1544 end; 1545end; 1546 1547procedure TCustomListView.SetAutoWidthLastColumn(AValue: Boolean); 1548begin 1549 if FAutoWidthLastColumn=AValue then Exit; 1550 FAutoWidthLastColumn:=AValue; 1551 if FAutoWidthLastColumn then 1552 ResizeLastColumn; 1553end; 1554 1555procedure TCustomListView.SetDefaultItemHeight(AValue: Integer); 1556begin 1557 if AValue <=0 then AValue := 20; 1558 if AValue = FDefaultItemHeight then Exit; 1559 FDefaultItemHeight := AValue; 1560 if not HandleAllocated then Exit; 1561 TWSCustomListViewClass(WidgetSetClass).SetDefaultItemHeight(Self, AValue); 1562end; 1563 1564procedure TCustomListView.SetDropTarget(const AValue: TListItem); 1565begin 1566 if not HandleAllocated then Exit; 1567 TWSCustomListViewClass(WidgetSetClass).ItemSetState(Self, AValue.Index, AValue, lisDropTarget, True); 1568end; 1569 1570procedure TCustomListView.SetFocused(const AValue: TListItem); 1571begin 1572 if FFocused = AValue then exit; 1573 FFocused := AValue; 1574 if not HandleAllocated then 1575 Exit; 1576 if FFocused <> nil then 1577 TWSCustomListViewClass(WidgetSetClass).ItemSetState(Self, FFocused.Index, FFocused, lisFocused, True); 1578end; 1579 1580procedure TCustomListView.SetHotTrackStyles(const AValue: TListHotTrackStyles); 1581begin 1582 if FHotTrackStyles = AValue then Exit; 1583 FHotTrackStyles := AValue; 1584 if not HandleAllocated then Exit; 1585 TWSCustomListViewClass(WidgetSetClass).SetHotTrackStyles(Self, AValue); 1586end; 1587 1588procedure TCustomListView.SetHoverTime(const AValue: Integer); 1589begin 1590 if FHoverTime = AValue then Exit; 1591 FHoverTime := AValue; 1592 if not HandleAllocated then Exit; 1593 TWSCustomListViewClass(WidgetSetClass).SetHoverTime(Self, FHoverTime); 1594end; 1595 1596procedure TCustomListView.SetIconOptions(const AValue: TIconOptions); 1597begin 1598 FIconOptions.Assign(AValue); 1599end; 1600 1601procedure TCustomListView.SetImageList(const ALvilOrd: Integer; const AValue: TCustomImageList); 1602var 1603 lvil: TListViewImageList; 1604begin 1605 lvil := TListViewImageList(ALvilOrd); 1606 1607 if FImages[lvil] = AValue then Exit; 1608 1609 if FImages[lvil] <> nil 1610 then FImages[lvil].UnregisterChanges(FImageChangeLinks[lvil]); 1611 1612 FImages[lvil] := AValue; 1613 1614 if FImages[lvil] <> nil 1615 then begin 1616 FImages[lvil].RegisterChanges(FImageChangeLinks[lvil]); 1617 FImages[lvil].FreeNotification(self); 1618 end; 1619 1620 SetImageListWS(lvil); 1621end; 1622 1623procedure TCustomListView.SetImageListAsync(Data: PtrInt); 1624begin 1625 SetImageListWS(TListViewImageList(Data)); 1626end; 1627 1628procedure TCustomListView.SetImageListWidth(const ALvilOrd: Integer; 1629 const AValue: Integer); 1630var 1631 lvil: TListViewImageList; 1632begin 1633 lvil := TListViewImageList(ALvilOrd); 1634 1635 if FImagesWidth[lvil] = AValue then Exit; 1636 1637 FImagesWidth[lvil] := AValue; 1638 1639 SetImageListWS(lvil); 1640end; 1641 1642procedure TCustomListView.SetImageListWS(const ALvil: TListViewImageList); 1643var 1644 R: TCustomImageListResolution; 1645begin 1646 if not HandleAllocated then 1647 Exit; 1648 if FImages[ALvil]<>nil then 1649 R := FImages[ALvil].ResolutionForPPI[FImagesWidth[ALvil], Font.PixelsPerInch, 1].Resolution // to-do: support scaling factor 1650 else 1651 R := nil; 1652 TWSCustomListViewClass(WidgetSetClass).SetImageList(Self, ALvil, R) 1653end; 1654 1655procedure TCustomListView.SetItemIndex(const AValue: Integer); 1656begin 1657 if (AValue < -1) or (AValue >= Items.Count) then 1658 raise Exception.CreateFmt(rsListIndexExceedsBounds,[AValue]); 1659 1660 if AValue = -1 then 1661 Selected := nil 1662 else 1663 begin 1664 // trigger ws selection update, it'll update Selected too 1665 if OwnerData then 1666 begin 1667 // clean selection when itemindex is changed. issue #19825 1668 if MultiSelect then 1669 Selected := nil; 1670 FSelectedIdx := AValue; 1671 Items.Item[AValue].Selected := True; 1672 end else 1673 Selected := Items.Item[AValue]; 1674 end; 1675end; 1676 1677{------------------------------------------------------------------------------ 1678 TCustomListView SetSelection 1679------------------------------------------------------------------------------} 1680procedure TCustomListView.SetSelection(const AValue: TListItem); 1681var 1682 i: integer; 1683begin 1684 if (AValue<>nil) and (AValue.ListView<>Self) then 1685 raise Exception.Create('Item does not belong to this listview'); 1686 if (not FOwnerData) and (FSelected = AValue) then Exit; 1687 //DebugLn('TCustomListView.SetSelection FSelected=',dbgs(FSelected)); 1688 if AValue = nil then 1689 begin 1690 if MultiSelect then 1691 begin 1692 BeginUpdate; 1693 try 1694 for i:=0 to Items.Count-1 do 1695 with Items[i] do 1696 if Selected then 1697 Selected:=False; 1698 finally 1699 EndUpdate; 1700 end; 1701 end else 1702 if FSelected <> nil then 1703 FSelected.Selected := False; 1704 FSelected := nil; 1705 Include(FFlags,lffSelectedValid); 1706 end else 1707 begin 1708 FSelected := AValue; 1709 if HandleAllocated then 1710 TWSCustomListViewClass(WidgetSetClass).ItemSetState(Self, FSelected.Index, 1711 FSelected, lisSelected, True); 1712 end; 1713end; 1714 1715procedure TCustomListView.SetShowEditorQueued(AValue: boolean); 1716begin 1717 if FShowEditorQueued=AValue then Exit; 1718 FShowEditorQueued:=AValue; 1719 if FShowEditorQueued then 1720 Application.QueueAsyncCall(@QueuedShowEditor,0); 1721end; 1722 1723procedure TCustomListView.SetOwnerData(const AValue: Boolean); 1724begin 1725 if FOwnerData=AValue then exit; 1726 FOwnerData:=AValue; 1727 FOwnerDataItem.SetOwner(nil); 1728 Items.Free; 1729 if AValue then 1730 begin 1731 FSelectedIdx := -1; 1732 FListItems := TOwnerDataListItems.Create(Self); 1733 end 1734 else 1735 FListItems := CreateListItems; 1736 1737 if HandleAllocated then 1738 TWSCustomListViewClass(WidgetSetClass).SetOwnerData(Self, AValue); 1739 FOwnerDataItem.SetOwner(FListItems); 1740end; 1741 1742procedure TCustomListView.SetProperty(const ALvpOrd: Integer; 1743 const AIsSet: Boolean); 1744var 1745 AProp: TListViewProperty; 1746begin 1747 AProp := TListViewProperty(ALvpOrd); 1748 if (AProp in FProperties) = AIsSet then Exit; 1749 1750 if AIsSet 1751 then Include(FProperties, AProp) 1752 else Exclude(FProperties, AProp); 1753 1754 if not HandleAllocated then Exit; 1755 TWSCustomListViewClass(WidgetSetClass).SetProperty(Self, AProp, AIsSet); 1756end; 1757 1758procedure TCustomListView.ImageChanged(Sender : TObject); 1759begin 1760 if csDestroying in ComponentState Then Exit; 1761// TODO: move Imagelist to interface, image changes can be update there 1762// if FUpdateCount>0 then 1763// Include(FStates,lvUpdateNeeded) 1764// else begin 1765// //image changed so redraw it all.... 1766// UpdateProperties; 1767// end; 1768end; 1769 1770procedure TCustomListView.ImageResolutionHandleDestroyed( 1771 Sender: TCustomImageList; AWidth: Integer; AReferenceHandle: TLCLHandle); 1772var 1773 lvil: TListViewImageList; 1774begin 1775 if not HandleAllocated then 1776 Exit; 1777 1778 for lvil in TListViewImageList do 1779 if Sender = FImages[lvil] then 1780 begin 1781 TWSCustomListViewClass(WidgetSetClass).SetImageList(Self, lvil, nil); 1782 Application.QueueAsyncCall(@SetImageListAsync, Ord(lvil)); 1783 end; 1784end; 1785 1786procedure TCustomListView.Loaded; 1787begin 1788 // create interface columns if needed 1789 if HandleAllocated then 1790 FColumns.WSCreateColumns; 1791 inherited Loaded; 1792end; 1793 1794procedure TCustomListView.Notification(AComponent: TComponent; Operation: TOperation); 1795begin 1796 inherited Notification(AComponent, Operation); 1797 if Operation = opRemove then begin 1798 if AComponent = LargeImages then LargeImages := nil; 1799 if AComponent = SmallImages then SmallImages := nil; 1800 if AComponent = StateImages then StateImages := nil; 1801 end; 1802end; 1803 1804procedure TCustomListView.SetScrollBars(const AValue: TScrollStyle); 1805begin 1806 if (FScrollBars = AValue) then exit; 1807 FScrollBars := AValue; 1808 if not HandleAllocated then Exit; 1809 TWSCustomListViewClass(WidgetSetClass).SetScrollBars(Self, AValue); 1810 UpdateScrollBars; 1811end; 1812 1813procedure TCustomListView.UpdateScrollbars; 1814begin 1815 // this needs to be done in the widgetset 1816 DebugLn('TODO: TCustomListView.UpdateScrollbars'); 1817 exit; 1818 1819 if not HandleAllocated then exit; 1820end; 1821 1822