1{%MainUnit win32wscomctrls.pp} 2{ $Id$ 3 4 ***************************************************************************** 5 This file is part of the Lazarus Component Library (LCL) 6 7 See the file COPYING.modifiedLGPL.txt, included in this distribution, 8 for details about the license. 9 ***************************************************************************** 10} 11 12{ TWin32WSCustomListView } 13 14type 15 TLVStyleType = (lsStyle, lsInvert, lsExStyle); 16 17const 18 LV_STYLES: array[TListViewProperty] of record 19 StyleType: TLVStyleType; 20 Style: Integer; 21 end = ( 22 (StyleType: lsStyle; Style: LVS_AUTOARRANGE), // lvpAutoArrange 23 (StyleType: lsExStyle; Style: LVS_EX_CHECKBOXES), // lvpCheckboxes 24 (StyleType: lsInvert; Style: LVS_NOSORTHEADER), // lvpColumnClick 25 (StyleType: lsExStyle; Style: LVS_EX_FLATSB), // lvpFlatScrollBars 26 (StyleType: lsExStyle; Style: LVS_EX_HEADERDRAGDROP), // lvpFullDrag 27 (StyleType: lsExStyle; Style: LVS_EX_GRIDLINES), // lvpGridLines 28 (StyleType: lsInvert; Style: LVS_SHOWSELALWAYS), // lvpHideSelection 29 (StyleType: lsExStyle; Style: LVS_EX_TRACKSELECT), // lvpHotTrack 30 (StyleType: lsInvert; Style: LVS_SINGLESEL), // lvpMultiSelect 31 (StyleType: lsStyle; Style: LVS_OWNERDRAWFIXED), // lvpOwnerDraw 32 (StyleType: lsInvert; Style: LVS_EDITLABELS), // lvpReadOnly, 33 (StyleType: lsExStyle; Style: LVS_EX_FULLROWSELECT), // lvpRowSelect 34 (StyleType: lsInvert; Style: LVS_NOCOLUMNHEADER), // lvpShowColumnHeaders 35 (StyleType: lsExStyle; Style: LVS_EX_MULTIWORKAREAS), // lvpShowWorkAreas 36 (StyleType: lsInvert; Style: LVS_NOLABELWRAP), // lvpWrapText 37 (StyleType: lsExStyle; Style: LVS_EX_LABELTIP) // lvpToolTips 38 ); 39 40 41type 42 // TODO: add iImage and iOrder to exiting TLvColumn 43 // this is a hack !!! 44 TLvColumn_v4_7 = record 45 lvc: TLvColumn; 46 iImage: Integer; 47 iOrder: Integer; 48 end; 49 50 51type 52 TCustomListViewAccess = class(TCustomListView); 53 TListColumnAccess = class(TListColumn); 54 55//////////////////////////////////////////////////////////////////////////////// 56// Msg handlers 57//////////////////////////////////////////////////////////////////////////////// 58 59var 60 ListViewWindProcInfo: record 61 ActiveListView: TCustomListView; 62 NoMouseUp: Boolean; 63 end; 64 65function ListViewParentMsgHandler(const AWinControl: TWinControl; Window: HWnd; 66 Msg: UInt; WParam: Windows.WParam; LParam: Windows.LParam; 67 var MsgResult: Windows.LResult; var WinProcess: Boolean): Boolean; 68 69type 70 PNMLVOwnerData = PLVDISPINFO; 71var 72 NMHdr: PNMHdr absolute LParam; // used by WM_NOTIFY 73 74 // Gets the cursor position relative to a given window 75 function GetClientCursorPos(ClientWindow: HWND) : TSmallPoint; 76 var 77 P: TPoint; 78 begin 79 Windows.GetCursorPos(P); 80 //if the mouse is not over the window is better to set to 0 to avoid weird behaviors 81 if Windows.WindowFromPoint(P) = ClientWindow then 82 Windows.ScreenToClient(ClientWindow, P) 83 else 84 begin 85 P.X:=0; 86 P.Y:=0; 87 end; 88 Result := Windows.PointToSmallPoint(P); 89 end; 90 91 procedure HandleListViewOwnerData(ALV: TCustomListViewAccess); 92 var 93 DataInfo: PNMLVOwnerData; // absolute NMHdr; 94 txt: String; 95 LVInfo: PWin32WindowInfo; 96 idx: Integer; 97 listitem: TListItem; 98 begin 99 LVInfo:= GetWin32WindowInfo(ALV.Handle); 100 DataInfo := PNMLVOwnerData(NMHdr); 101 if not Assigned(DataInfo) or (not ALV.OwnerData) then 102 Exit; 103 listitem := ALV.Items[DataInfo^.item.iItem]; 104 if not Assigned(listitem) then 105 Exit; 106 if (DataInfo^.item.mask and LVIF_TEXT) <> 0 then 107 begin 108 if DataInfo^.item.iSubItem = 0 then 109 txt := listitem.Caption 110 else 111 begin 112 idx := DataInfo^.item.iSubItem - 1; 113 if idx < listitem.SubItems.Count then 114 txt := listitem.SubItems[idx] 115 else 116 txt := ''; 117 end; 118 if DataInfo^.hdr.code = UInt(LVN_GETDISPINFOA) then 119 begin 120 LVInfo^.DispInfoTextA[LVInfo^.DispInfoIndex]:=Utf8ToAnsi(txt+#0); 121 DataInfo^.item.pszText := @(LVInfo^.DispInfoTextA[LVInfo^.DispInfoIndex][1]); 122 end 123 else 124 begin 125 LVInfo^.DispInfoTextW[LVInfo^.DispInfoIndex]:=UTF8Decode(txt+#0); 126 DataInfo^.item.pszText := PChar(LVInfo^.DispInfoTextW[LVInfo^.DispInfoIndex]); 127 end; 128 inc(LVInfo^.DispInfoIndex); 129 if LVInfo^.DispInfoIndex=LV_DISP_INFO_COUNT then LVInfo^.DispInfoIndex:=0; 130 end; 131 if (DataInfo^.item.mask and LVIF_IMAGE) <> 0 then 132 begin 133 if DataInfo^.item.iSubItem = 0 then 134 DataInfo^.item.iImage := listitem.ImageIndex 135 else 136 begin 137 idx := DataInfo^.item.iSubItem - 1; 138 if idx < listitem.SubItems.Count then 139 DataInfo^.item.iImage := listitem.SubItemImages[idx] 140 else 141 DataInfo^.item.iImage := -1; 142 end; 143 if Assigned(ALV.StateImages) then 144 begin 145 DataInfo^.item.state := IndexToStateImageMask(listitem.StateIndex + 1); 146 DataInfo^.item.stateMask := $F000; // States start from 12 bit 147 DataInfo^.item.mask := DataInfo^.item.mask or LVIF_STATE; 148 end; 149 end; 150 end; 151 152 procedure HandleListViewCustomDraw(ALV: TCustomListViewAccess); 153 function ConvState(const State: uint): TCustomDrawState; 154 begin 155 Result := []; 156 if state and CDIS_CHECKED <> 0 then Include(Result, cdsChecked); 157 if state and CDIS_DEFAULT <> 0 then Include(Result, cdsDefault); 158 if state and CDIS_DISABLED <> 0 then Include(Result, cdsDisabled); 159 if state and CDIS_FOCUS <> 0 then Include(Result, cdsFocused); 160 if state and CDIS_GRAYED <> 0 then Include(Result, cdsGrayed); 161 if state and CDIS_HOT <> 0 then Include(Result, cdsHot); 162 if state and CDIS_INDETERMINATE <> 0 then Include(Result, cdsIndeterminate); 163 if state and CDIS_MARKED <> 0 then Include(Result, cdsMarked); 164 if state and CDIS_SELECTED <> 0 then Include(Result, cdsSelected); 165 end; 166 167 const 168 CDRFRESULT: array[TCustomDrawResultFlag] of Integer = ( 169 CDRF_SKIPDEFAULT, 170 CDRF_NOTIFYPOSTPAINT, 171 CDRF_NOTIFYITEMDRAW, 172 CDRF_NOTIFYSUBITEMDRAW, 173 CDRF_NOTIFYPOSTERASE, 174 CDRF_NOTIFYITEMERASE 175 ); 176 var 177 DrawInfo: PNMLVCustomDraw absolute NMHdr; 178 Stage: TCustomDrawStage; 179 DrawResult: TCustomDrawResult; 180 ResultFlag: TCustomDrawResultFlag; 181 OldDC: HDC; 182 begin 183 MsgResult := CDRF_DODEFAULT; 184 WinProcess := False; 185 if not ALV.IsCustomDrawn(dtControl, cdPrePaint) then 186 exit; 187 188 case DrawInfo^.nmcd.dwDrawStage and $7 of //Get drawing state 189 CDDS_PREPAINT: Stage := cdPrePaint; 190 CDDS_POSTPAINT: Stage := cdPostPaint; 191 CDDS_PREERASE: Stage := cdPreErase; 192 CDDS_POSTERASE: Stage := cdPostErase; 193 else 194 Exit; 195 end; 196 197 OldDC := ALV.Canvas.Handle; 198 ALV.Canvas.Handle := DrawInfo^.nmcd.hdc; 199 ALV.Canvas.Font.Assign(ALV.Font); 200 ALV.Canvas.Brush.Assign(ALV.Brush); 201 202 if DrawInfo^.nmcd.dwDrawStage and CDDS_SUBITEM <> 0 then 203 begin 204 // subitem 0 is handled by dtItem 205 if DrawInfo^.iSubItem = 0 then Exit; 206 DrawResult := ALV.IntfCustomDraw(dtSubItem, Stage, 207 DrawInfo^.nmcd.dwItemSpec, DrawInfo^.iSubItem, 208 ConvState(DrawInfo^.nmcd.uItemState), nil); 209 end 210 else 211 if DrawInfo^.nmcd.dwDrawStage and CDDS_ITEM <> 0 then 212 DrawResult := ALV.IntfCustomDraw(dtItem, Stage, DrawInfo^.nmcd.dwItemSpec, 213 -1, ConvState(DrawInfo^.nmcd.uItemState), nil) 214 else 215 DrawResult := ALV.IntfCustomDraw(dtControl, Stage, -1, -1, [], @DrawInfo^.nmcd.rc); //Whole control 216 217 if DrawResult <> [] then 218 MsgResult := 0; 219 220 if not (cdrSkipDefault in DrawResult) and 221 (DrawInfo^.nmcd.dwDrawStage and CDDS_ITEMPREPAINT = CDDS_ITEMPREPAINT) then 222 begin 223 DrawInfo^.clrText := ColorToRGB(ALV.Canvas.Font.Color); 224 DrawInfo^.clrTextBk := ColorToRGB(ALV.Canvas.Brush.Color); 225 end; 226 ALV.Canvas.Handle := OldDC; 227 228 for ResultFlag := Low(ResultFlag) to High(ResultFlag) do 229 begin 230 if ResultFlag in DrawResult then 231 MsgResult := MsgResult or CDRFRESULT[ResultFlag]; 232 end; 233 end; 234 235begin 236 Result := False; 237 case Msg of 238 WM_NOTIFY: 239 begin 240 case PNMHdr(LParam)^.code of 241 LVN_GETDISPINFOA, LVN_GETDISPINFOW: 242 HandleListViewOwnerData(TCustomListViewAccess(AWinControl)); 243 NM_CUSTOMDRAW: 244 HandleListViewCustomDraw(TCustomListViewAccess(AWinControl)); 245 LVN_BEGINDRAG, LVN_BEGINRDRAG: begin 246 if ListViewWindProcInfo.ActiveListView = AWinControl then 247 ListViewWindProcInfo.NoMouseUp := True; 248 end; 249 end; 250 end; 251 end; 252end; 253//////////////////////////////////////////////////////////////////////////////// 254// Event code 255//////////////////////////////////////////////////////////////////////////////// 256 257 258//////////////////////////////////////////////////////////////////////////////// 259// Column code 260//////////////////////////////////////////////////////////////////////////////// 261 262class procedure TWin32WSCustomListView.ColumnDoAutosize(const ALV: TCustomListView; const AIndex: Integer); 263var 264 CaptionSize: TSize; 265begin 266 if (ALV.Items.Count > 0) then 267 ListView_SetColumnWidth(ALV.Handle, AIndex, LVSCW_AUTOSIZE) 268 else 269 begin 270 // normally, we have to use ListView_GetStringWidth, but it doesn't work with 271 // Unicode, so we take a universal function to get the width of the caption 272 if GetTextExtentPoint32W(ALV.Canvas.Handle, 273 PWideChar(UTF8ToUTF16(ALV.Column[AIndex].Caption)), 274 UTF8Length(ALV.Column[AIndex].Caption), 275 CaptionSize) then 276 begin 277 // to retrieve the column width that can contain the string without 278 // truncating it, you must add padding to the returned string width 279 // see msdn: ListView_GetStringWidth 280 // there is no way to get the needed padding size for a list view caption 281 // so we take the height of the current caption text, to be DPI aware 282 ListView_SetColumnWidth(ALV.Handle, AIndex, CaptionSize.cx + CaptionSize.cy); 283 end 284 else 285 ListView_SetColumnWidth(ALV.Handle, AIndex, TListColumnAccess(ALV.Column[AIndex]).GetStoredWidth); 286 end; 287end; 288 289class procedure TWin32WSCustomListView.ColumnDelete(const ALV: TCustomListView; const AIndex: Integer); 290var 291 hHdr, hLV: THandle; 292 Count: Integer; 293begin 294 if not WSCheckHandleAllocated(ALV, 'ColumnDelete') 295 then Exit; 296 297 hLV := ALV.Handle; 298 hHdr := GetHeader(hLV); 299 if hHdr = 0 then Exit; //??? 300 301 Count := Header_GetItemCount(hHdr); 302 if Count <= Aindex then Exit; 303 304 // Move column to the last, otherwise our items get shuffeled 305 if AIndex <> Count - 1 then 306 ColumnMove(ALV, AIndex, Count - 1, nil); 307 ListView_DeleteColumn(hLV, Count - 1); 308end; 309 310class function TWin32WSCustomListView.ColumnGetWidth(const ALV: TCustomListView; const AIndex: Integer; const AColumn: TListColumn): Integer; 311var 312 lvc: TLvColumn; 313begin 314 Result := -1; 315 // this implementation uses columnwidht = 0 for invisible 316 // so fallback to default (= AColumn.FWidth) 317 // Don't return AColumn.Width, this will cause a loop 318 if not AColumn.Visible then Exit; 319 320 if not WSCheckHandleAllocated(ALV, 'ColumnGetWidth') 321 then Exit; 322 323 // do not use ListView_GetColumnWidth since we can not detect errors 324 lvc.Mask := LVCF_WIDTH; 325 if ListView_GetColumn(ALV.Handle, AIndex, lvc) <> 0 326 then Result := lvc.cx; 327end; 328 329class procedure TWin32WSCustomListView.ColumnInsert(const ALV: TCustomListView; const AIndex: Integer; const AColumn: TListColumn); 330var 331 lvc: TLvColumn; 332begin 333 if not WSCheckHandleAllocated(ALV, 'ColumnInsert') 334 then Exit; 335 336 lvc.Mask := LVCF_TEXT; 337 338 lvc.pszText := PChar(PWideChar(UTF8ToUTF16(AColumn.Caption))); 339 SendMessage(ALV.Handle, LVM_INSERTCOLUMNW, WPARAM(AIndex), LPARAM(@lvc)); 340end; 341 342class procedure TWin32WSCustomListView.ColumnMove(const ALV: TCustomListView; const AOldIndex, ANewIndex: Integer; const AColumn: TListColumn); 343var 344 lvc, oldlvc: TLvColumn_v4_7; 345 buf, oldbuf: array[0..1024] of Char; 346 Count, idx: Integer; 347 348begin 349 if not WSCheckHandleAllocated(ALV, 'ColumnMove') 350 then Exit; 351 352 Count := AOldIndex - ANewIndex; 353 354 // Fetch old column values 355 oldlvc.lvc.Mask := LVCF_FMT or LVCF_IMAGE or LVCF_TEXT or LVCF_WIDTH; 356 oldlvc.lvc.pszText := @oldbuf[0]; 357 oldlvc.lvc.cchTextMax := SizeOF(oldbuf); 358 ListView_GetColumn(ALV.Handle, AOldIndex, oldlvc.lvc); 359 360 idx := AOldIndex; 361 while Count <> 0 do 362 begin 363 // get next index 364 if Count < 0 365 then Inc(idx) 366 else Dec(idx); 367 // and data 368 lvc.lvc.Mask := LVCF_FMT or LVCF_IMAGE or LVCF_TEXT or LVCF_WIDTH; 369 lvc.lvc.pszText := @buf[0]; 370 lvc.lvc.cchTextMax := SizeOF(buf); 371 ListView_GetColumn(ALV.Handle, idx, lvc.lvc); 372 // set data 373 ListView_SetColumn(ALV.Handle, ANewIndex + Count, lvc.lvc); 374 375 if Count < 0 376 then Inc(Count) 377 else Dec(Count); 378 end; 379 // finally copy original data to new column 380 ListView_SetColumn(ALV.Handle, ANewIndex, oldlvc.lvc); 381end; 382 383class procedure TWin32WSCustomListView.ColumnSetAlignment(const ALV: TCustomListView; const AIndex: Integer; const AColumn: TListColumn; const AAlignment: TAlignment); 384const 385 JUSTIFICATION: array[TAlignment] of Integer = ( 386 LVCFMT_LEFT, 387 LVCFMT_RIGHT, 388 LVCFMT_CENTER 389 ); 390var 391 lvc: TLvColumn; 392begin 393 if not WSCheckHandleAllocated(ALV, 'ColumnSetAlignment') 394 then Exit; 395 396 lvc.Mask := LVCF_FMT; 397 ListView_GetColumn(ALV.Handle, AIndex, lvc); 398 lvc.fmt := (lvc.fmt and not LVCFMT_JUSTIFYMASK) or JUSTIFICATION[AAlignment]; 399 ListView_SetColumn(ALV.Handle, AIndex, lvc); 400end; 401 402class procedure TWin32WSCustomListView.ColumnSetAutoSize(const ALV: TCustomListView; const AIndex: Integer; const AColumn: TListColumn; const AAutoSize: Boolean); 403begin 404 if not WSCheckHandleAllocated(ALV, 'ColumnSetAutoSize') 405 then Exit; 406 407 if AAutoSize 408 then ColumnDoAutosize(ALV, AIndex) 409 else ListView_SetColumnWidth(ALV.Handle, AIndex, TListColumnAccess(AColumn).GetStoredWidth); 410end; 411 412class procedure TWin32WSCustomListView.ColumnSetCaption(const ALV: TCustomListView; const AIndex: Integer; const AColumn: TListColumn; const ACaption: String); 413var 414 lvc: TLvColumn; 415begin 416 if not WSCheckHandleAllocated(ALV, 'ColumnSetCaption') 417 then Exit; 418 419 lvc.Mask := LVCF_TEXT; 420 421 lvc.pszText := PChar(PWideChar(UTF8ToUTF16(AColumn.Caption))); 422 SendMessage(ALV.Handle, LVM_SETCOLUMNW, WPARAM(AIndex), LPARAM(@lvc)); 423end; 424 425class procedure TWin32WSCustomListView.ColumnSetImage(const ALV: TCustomListView; const AIndex: Integer; const AColumn: TListColumn; const AImageIndex: Integer); 426var 427 lvc: TLvColumn_v4_7; 428begin 429 if not WSCheckHandleAllocated(ALV, 'ColumnSetImage') 430 then Exit; 431 432 // forst get the old lvc, since we have to tell the bloody thing that this 433 // column has an image otherwise we will have a crash on XP using comctl 6 434 435 lvc.lvc.Mask := LVCF_FMT; 436 ListView_GetColumn(ALV.Handle, AIndex, lvc.lvc); 437 438 if AImageIndex = -1 439 then begin 440 lvc.lvc.Mask := LVCF_FMT; 441 lvc.lvc.fmt := lvc.lvc.fmt and not (LVCFMT_IMAGE or LVCFMT_COL_HAS_IMAGES); 442 end 443 else begin 444 lvc.lvc.Mask := LVCF_IMAGE or LVCF_FMT; 445 lvc.lvc.fmt := lvc.lvc.fmt or LVCFMT_IMAGE or LVCFMT_COL_HAS_IMAGES; 446 lvc.iImage := AImageIndex; 447 end; 448 449 ListView_SetColumn(ALV.Handle, AIndex, lvc.lvc); 450end; 451 452class procedure TWin32WSCustomListView.ColumnSetMaxWidth(const ALV: TCustomListView; const AIndex: Integer; const AColumn: TListColumn; const AMaxWidth: Integer); 453begin 454 if not WSCheckHandleAllocated(ALV, 'ColumnSetMaxWidth') 455 then Exit; 456 457 // TODO: in messageHandler 458end; 459 460class procedure TWin32WSCustomListView.ColumnSetMinWidth(const ALV: TCustomListView; const AIndex: Integer; const AColumn: TListColumn; const AMinWidth: integer); 461begin 462 if not WSCheckHandleAllocated(ALV, 'ColumnSetMinWidth') 463 then Exit; 464 465 // TODO: in messageHandler 466end; 467 468class procedure TWin32WSCustomListView.ColumnSetWidth(const ALV: TCustomListView; const AIndex: Integer; const AColumn: TListColumn; const AWidth: Integer); 469begin 470 if not WSCheckHandleAllocated(ALV, 'ColumnSetWidth') 471 then Exit; 472 473 if AColumn.AutoSize 474 then ColumnDoAutosize(ALV, AIndex) 475 else ListView_SetColumnWidth(ALV.Handle, AIndex, AWidth) 476end; 477 478class procedure TWin32WSCustomListView.ColumnSetVisible(const ALV: TCustomListView; const AIndex: Integer; const AColumn: TListColumn; const AVisible: Boolean); 479begin 480 if not WSCheckHandleAllocated(ALV, 'ColumnSetVisible') 481 then Exit; 482 483 // TODO: implement with LV_COLUMN.subitem (associate different columns and insert/delete last. 484 485 if AVisible 486 then if AColumn.AutoSize 487 then ColumnDoAutosize(ALV, AIndex) 488 else ListView_SetColumnWidth(ALV.Handle, AIndex, TListColumnAccess(AColumn).GetStoredWidth) 489 else ListView_SetColumnWidth(ALV.Handle, AIndex, 0); 490end; 491 492class procedure TWin32WSCustomListView.ColumnSetSortIndicator( 493 const ALV: TCustomListView; const AIndex: Integer; 494 const AColumn: TListColumn; const AAndicator: TSortIndicator); 495var 496 Hdr: HWND; 497 Itm: THDITEM; 498begin 499 if not WSCheckHandleAllocated(ALV, 'ColumnSetSortIndicator') 500 then Exit; 501 502 Hdr := ListView_GetHeader(ALV.Handle); 503 FillChar(itm, sizeof(itm),0); 504 itm.mask := HDI_FORMAT; 505 Header_GetItem(Hdr, AIndex, Itm); 506 case AAndicator of 507 siNone: itm.fmt := itm.fmt and (not (HDF_SORTDOWN or HDF_SORTUP)); 508 siAscending: itm.fmt := (itm.fmt or HDF_SORTUP) and (not HDF_SORTDOWN); 509 siDescending: itm.fmt := (itm.fmt or HDF_SORTDOWN) and (not HDF_SORTUP); 510 end; 511 Header_SetItem(Hdr, AIndex, Itm); 512end; 513 514//////////////////////////////////////////////////////////////////////////////// 515// Item code 516//////////////////////////////////////////////////////////////////////////////// 517 518class procedure TWin32WSCustomListView.ItemDelete(const ALV: TCustomListView; const AIndex: Integer); 519begin 520 if not WSCheckHandleAllocated(ALV, 'ItemDelete') 521 then Exit; 522 523 ListView_DeleteItem(ALV.Handle, AIndex); 524end; 525 526class function TWin32WSCustomListView.ItemDisplayRect(const ALV: TCustomListView; const AIndex, ASubItem: Integer; ACode: TDisplayCode):TRect; 527const 528 DISPLAYCODES: array[TDisplayCode] of DWORD=(LVIR_BOUNDS, LVIR_ICON, LVIR_LABEL, LVIR_SELECTBOUNDS); 529var 530 mes: uint; 531begin 532 Result := Rect(0,0,0,0); 533 if not WSCheckHandleAllocated(ALV, 'ItemDisplayRect') 534 then Exit; 535 536 if ASubItem = 0 537 then mes:=LVM_GETITEMRECT 538 else begin 539 mes:=LVM_GETSUBITEMRECT; 540 if ACode = drSelectBounds 541 then ACode := drBounds; 542 end; 543 Result.top := ASubItem; 544 Result.left := DISPLAYCODES[ACode]; 545 SendMessage(ALV.Handle, mes, AIndex, lparam(@Result)); 546end; 547 548class procedure TWin32WSCustomListView.LVItemAssign(const ALV: TCustomListView; 549 AItem: TListItem; const AIndex: Integer); 550var 551 i: Integer; 552 B: Boolean; 553begin 554 if ALV.CheckBoxes then 555 B := AItem.Checked 556 else 557 B := False; 558 559 // apply texts 560 ItemSetText(ALV, AIndex, AItem, 0, AItem.Caption); 561 for i := 0 to AItem.SubItems.Count - 1 do 562 ItemSetText(ALV, AIndex, AItem, i + 1, AItem.SubItems[i]); 563 // make sure no texts are left over 564 for i := AItem.SubItems.Count to ALV.ColumnCount-1 do 565 ItemSetText(ALV, AIndex, AItem, i + 1, ''); 566 567 // set state image 568 ItemSetStateImage(ALV, AIndex,AItem,0, AItem.StateIndex); 569 570 // set image 571 ItemSetImage(ALV, AIndex, AItem, 0, AItem.ImageIndex); 572 573 // apply checkbox state 574 ItemSetChecked(ALV, AIndex, AItem, B); 575end; 576 577class procedure TWin32WSCustomListView.ItemExchange(const ALV: TCustomListView; 578 AItem: TListItem; const AIndex1, AIndex2: Integer); 579var 580 AItem1, AItem2: TListItem; 581begin 582 if not WSCheckHandleAllocated(ALV, 'ItemExchange') then 583 exit; 584 585 //We have to reassign TLvItem to AIndex1 and AIndex2 586 //or use RecreateWnd() which is very expensive 587 588 AItem1 := ALV.Items[AIndex2]; 589 AItem2 := ALV.Items[AIndex1]; 590 591 LVItemAssign(ALV, AItem1, AIndex2); 592 LVItemAssign(ALV, AItem2, AIndex1); 593end; 594 595class procedure TWin32WSCustomListView.ItemMove(const ALV: TCustomListView; 596 AItem: TListItem; const AFromIndex, AToIndex: Integer); 597var 598 i: Integer; 599begin 600 if not WSCheckHandleAllocated(ALV, 'ItemMove') then 601 exit; 602 if AFromIndex = AToIndex then 603 exit; 604 if AFromIndex > AToIndex then 605 begin 606 for i := AToIndex to AFromIndex do 607 LVItemAssign(ALV, ALV.Items[i], i); 608 end else 609 for i := AFromIndex to AToIndex do 610 LVItemAssign(ALV, ALV.Items[i], i); 611end; 612 613class function TWin32WSCustomListView.ItemGetChecked(const ALV: TCustomListView; const AIndex: Integer; const AItem: TListItem): Boolean; 614begin 615 Result := False; 616 if not WSCheckHandleAllocated(ALV, 'ItemGetChecked') 617 then Exit; 618 // shr 12 will give teh stateimage index, however a value of 619 // 0 means no image and 1 means unchecked. All other 14 are checked (?) 620 // so shifting 13 will always result in something <> 0 when checked. 621 Result := SendMessage(ALV.Handle, LVM_GETITEMSTATE, AIndex, LVIS_STATEIMAGEMASK) shr 13 <> 0; 622end; 623 624class function TWin32WSCustomListView.ItemGetPosition( 625 const ALV: TCustomListView; const AIndex: Integer): TPoint; 626begin 627 Result := Point(0, 0); 628 if WSCheckHandleAllocated(ALV, 'ItemGetPosition') then 629 SendMessage(ALV.Handle, LVM_GETITEMPOSITION, AIndex, LPARAM(@Result)); 630end; 631 632class function TWin32WSCustomListView.ItemGetState(const ALV: TCustomListView; 633 const AIndex: Integer; const AItem: TListItem; const AState: TListItemState; 634 out AIsSet: Boolean): Boolean; 635const 636 // lisCut, lisDropTarget, lisFocused, lisSelected 637 FLAGS: array[TListItemState] of Integer = (LVIS_CUT, LVIS_DROPHILITED, LVIS_FOCUSED, LVIS_SELECTED); 638begin 639 Result := False; 640 641 if not WSCheckHandleAllocated(ALV, 'ItemGetState') 642 then Exit; 643 644 AIsSet := 0 <> ListView_GetItemState(ALV.Handle, AIndex, FLAGS[AState]); 645 Result := True; 646end; 647 648class function TWin32WSCustomListView.ItemGetStates(const ALV: TCustomListView; const AIndex: Integer; out AStates: TListItemStates): Boolean; 649const 650 MASK = LVIS_CUT or LVIS_DROPHILITED or LVIS_FOCUSED or LVIS_SELECTED; 651var 652 Flags: Integer; 653begin 654 Result := False; 655 656 if not WSCheckHandleAllocated(ALV, 'ItemGetStates') 657 then Exit; 658 659 AStates := []; 660 Flags := ListView_GetItemState(ALV.Handle, AIndex, MASK); 661 if (Flags and LVIS_CUT) <> 0 then 662 Include(AStates, lisCut); 663 if (Flags and LVIS_DROPHILITED) <> 0 then 664 Include(AStates, lisDropTarget); 665 if (Flags and LVIS_FOCUSED) <> 0 then 666 Include(AStates, lisFocused); 667 if (Flags and LVIS_SELECTED) <> 0 then 668 Include(AStates, lisSelected); 669 670 Result := True; 671end; 672 673class procedure TWin32WSCustomListView.ItemInsert(const ALV: TCustomListView; const AIndex: Integer; const AItem: TListItem); 674var 675 lvi: TLvItem; 676begin 677 if not WSCheckHandleAllocated(ALV, 'ItemInsert') 678 then Exit; 679 680 lvi.Mask := LVIF_TEXT or LVIF_PARAM; 681 lvi.iItem := AIndex; 682 lvi.iSubItem := 0; 683 lvi.lParam := LPARAM(AItem); 684 685 lvi.pszText := PChar(PWideChar(UTF8ToUTF16(AItem.Caption))); 686 SendMessage(ALV.Handle, LVM_INSERTITEMW, 0, LPARAM(@lvi)); 687end; 688 689class procedure TWin32WSCustomListView.ItemSetChecked(const ALV: TCustomListView; const AIndex: Integer; const AItem: TListItem; const AChecked: Boolean); 690begin 691 if not WSCheckHandleAllocated(ALV, 'ItemSetChecked') 692 then Exit; 693 694 if AChecked then 695 ListView_SetItemState(ALV.Handle, AIndex, IndexToStateImageMask(2), LVIS_STATEIMAGEMASK) 696 else 697 ListView_SetItemState(ALV.Handle, AIndex, IndexToStateImageMask(1), LVIS_STATEIMAGEMASK); 698end; 699 700class procedure TWin32WSCustomListView.ItemSetImage(const ALV: TCustomListView; const AIndex: Integer; const AItem: TListItem; const ASubIndex, AImageIndex: Integer); 701var 702 lvi: TLvItem; 703begin 704 if not WSCheckHandleAllocated(ALV, 'ItemSetImage') 705 then Exit; 706 707 lvi.Mask := LVIF_IMAGE; 708 lvi.iItem := AIndex; 709 lvi.iSubItem := ASubIndex; 710 lvi.iImage := AImageIndex; 711 712 ListView_SetItem(ALV.Handle, lvi); 713end; 714 715class function TWin32WSCustomListView.ItemSetPosition(const ALV: TCustomListView; const AIndex: Integer; const ANewPosition: TPoint): Boolean; 716begin 717 if not WSCheckHandleAllocated(ALV, 'ItemSetPosition') then 718 Result := False 719 else 720 Result := SendMessage(ALV.Handle, LVM_SETITEMPOSITION, 721 AIndex, MAKELPARAM(ANewPosition.X, ANewPosition.Y)) <> 0; 722end; 723 724class procedure TWin32WSCustomListView.ItemSetStateImage(const ALV: TCustomListView; const AIndex: Integer; const AItem: TListItem; const ASubIndex, AStateImageIndex: Integer); 725begin 726 if not WSCheckHandleAllocated(ALV, 'ItemSetStateImage') 727 then Exit; 728 729 ListView_SetItemState(ALV.Handle, AIndex, IndexToStateImageMask(AStateImageIndex + 1), LVIS_STATEIMAGEMASK); 730end; 731 732class procedure TWin32WSCustomListView.ItemSetState(const ALV: TCustomListView; const AIndex: Integer; const AItem: TListItem; const AState: TListItemState; const AIsSet: Boolean); 733const 734 // lisCut, lisDropTarget, lisFocused, lisSelected 735 FLAGS: array[TListItemState] of Integer = (LVIS_CUT, LVIS_DROPHILITED, LVIS_FOCUSED, LVIS_SELECTED); 736begin 737 if not WSCheckHandleAllocated(ALV, 'ItemSetState') 738 then Exit; 739 {Don't change the state if it already has needed value} 740 if ((ListView_GetItemState(ALV.Handle, AIndex, FLAGS[AState]) and FLAGS[AState]) = FLAGS[AState]) = AIsSet then exit; 741 742 if AIsSet 743 then ListView_SetItemState(ALV.Handle, AIndex, FLAGS[AState], FLAGS[AState]) 744 else ListView_SetItemState(ALV.Handle, AIndex, 0, FLAGS[AState]); 745end; 746 747class procedure TWin32WSCustomListView.ItemSetText(const ALV: TCustomListView; const AIndex: Integer; const AItem: TListItem; const ASubIndex: Integer; const AText: String); 748var 749 _gnu_lvi : LV_ITEM; 750begin 751 if not WSCheckHandleAllocated(ALV, 'ItemSetText') 752 then Exit; 753 754 _gnu_lvi.iSubItem := ASubIndex; 755 _gnu_lvi.pszText := PChar(PWideChar(UTF8ToUTF16(AText))); 756 757 SendMessage(ALV.Handle, LVM_SETITEMTEXTW, WPARAM(AIndex), LPARAM(@_gnu_lvi)); 758 // autosize is an *extreme* performance bottleneck, even if WM_SETREDRAW 759 // was set to false it will ignore this and still redraw all columns. 760 // We will therefore postpone all autosizing until EndUpdate where we do 761 // it only once per column. 762 763 if (ASubIndex >= 0) and (ASubIndex < ALV.ColumnCount) and ALV.Column[ASubIndex].AutoSize and (TCustomListViewAccess(ALV).GetUpdateCount = 0) then 764 ColumnDoAutosize(ALV, ASubIndex); 765end; 766 767class procedure TWin32WSCustomListView.ItemShow(const ALV: TCustomListView; const AIndex: Integer; const AItem: TListItem; const PartialOK: Boolean); 768begin 769 if not WSCheckHandleAllocated(ALV, 'ItemShow') 770 then Exit; 771 772 ListView_EnsureVisible(ALV.Handle, AIndex, Ord(PartialOK)); 773end; 774 775//////////////////////////////////////////////////////////////////////////////// 776// LV code 777//////////////////////////////////////////////////////////////////////////////// 778 779procedure ListViewDrawItem(const AWinControl: TWinControl; Window: HWnd; 780 Msg: UInt; WParam: Windows.WParam; const DrawIS: TDrawItemStruct; 781 var ItemMsg: Integer; var DrawListItem: Boolean); 782begin 783 DrawListItem := (AWinControl is TListView) and 784 (TListView(AWinControl).ViewStyle = vsReport) and 785 (DrawIS.ctlType = ODT_LISTVIEW) and 786 (TListView(AWinControl).OwnerDraw); 787 ItemMsg := CN_DRAWITEM; 788end; 789 790function ListViewProc(Window: HWnd; Msg: UInt; WParam: Windows.WParam; 791 LParam: Windows.LParam): LResult; stdcall; 792 793 function GetClientCursorPos(ClientWindow: HWND) : TPoint; 794 begin 795 Windows.GetCursorPos(Result); 796 Windows.ScreenToClient(ClientWindow, Result); 797 end; 798 799var 800 WindowInfo: PWin32WindowInfo; 801 ListItem: TListItem; 802 ListView: TCustomListView; 803 AMsg: UINT; 804 MPos: TPoint; 805begin 806 case Msg of 807 WM_CONTEXTMENU: 808 begin 809 if ListViewWindProcInfo.ActiveListView <> nil then begin 810 Result := 1; 811 exit; 812 end; 813 end; 814 WM_LBUTTONDOWN, WM_RBUTTONDOWN: 815 begin 816 (* 817 A ListView doesn't get a WM_LBUTTONUP, WM_RBUTTONUP message, 818 because it keeps the message in its own event loop, 819 see msdn article about "Default List-View Message Processing" 820 therefore we take this notification and create a 821 LM_LBUTTONUP, LM_RBUTTONUP message out of it. 822 823 - When the ListView starts dragging (LVN_BEGIN(R)DRAG), it will get the 824 mouse up 825 - The WindProg for the WM_LBUTTONUP, WM_RBUTTONUP does not always 826 return immediately (multi select). It will return on either mouse up 827 or BeginDrag. 828 The LCL mouse down event is executed with the same delay. 829 - Also see issue #33330 830 *) 831 832 WindowInfo := GetWin32WindowInfo(Window); 833 ListView := TListView(WindowInfo^.WinControl); 834 ListItem := ListView.GetItemAt(GET_X_LPARAM(LParam), GET_Y_LPARAM(LParam)); 835 836 if Msg = WM_LBUTTONDOWN 837 then AMsg := LM_LBUTTONUP 838 else AMsg := LM_RBUTTONUP; 839 840 ListViewWindProcInfo.ActiveListView := ListView; 841 ListViewWindProcInfo.NoMouseUp := False; 842 try 843 Result := WindowProc(Window, Msg, WParam, LParam); 844 finally 845 ListViewWindProcInfo.ActiveListView:= nil; 846 end; 847 848 if (not ListViewWindProcInfo.NoMouseUp) and 849 (Assigned(ListItem) or ListView.MultiSelect) 850 then 851 begin 852 MPos := GetClientCursorPos(Window); 853 PostMessage(Window, AMsg, 0, Windows.MakeLParam(MPos.X, MPos.Y)); 854 end; 855 exit; 856 end; 857 end; 858 Result := WindowProc(Window, Msg, WParam, LParam); 859end; 860 861class function TWin32WSCustomListView.CreateHandle(const AWinControl: TWinControl; 862 const AParams: TCreateParams): HWND; 863const 864 LISTVIEWSTYLES: array[TViewStyle] of DWORD = (LVS_ICON, LVS_SMALLICON, LVS_LIST, LVS_REPORT); 865 Arrangement: array[TIconArrangement] of DWord = (LVS_ALIGNTOP, LVS_ALIGNLEFT); 866var 867 Params: TCreateWindowExParams; 868begin 869 // general initialization of Params 870 PrepareCreateWindow(AWinControl, AParams, Params); 871 // customization of Params 872 with Params do 873 begin 874 pClassName := WC_LISTVIEW; 875 SubClassWndProc := @ListViewProc; 876 WindowTitle := StrCaption; 877 Flags := Flags or LISTVIEWSTYLES[TListView(AWinControl).ViewStyle] or 878 LVS_SINGLESEL or LVS_SHAREIMAGELISTS or 879 Arrangement[TListView(AWinControl).IconOptions.Arrangement]; 880 if TCustomListView(AWinControl).OwnerData then 881 Flags := Flags or LVS_OWNERDATA; 882 if TListView(AWinControl).OwnerDraw then 883 Flags := Flags or LVS_OWNERDRAWFIXED; 884 if TCustomListView(AWinControl).BorderStyle = bsSingle then 885 FlagsEx := FlagsEx or WS_EX_CLIENTEDGE; 886 end; 887 // create window 888 FinishCreateWindow(AWinControl, Params, false); 889 Params.WindowInfo^.ParentMsgHandler := @ListViewParentMsgHandler; 890 Params.WindowInfo^.needParentPaint := false; 891 Params.WindowInfo^.DrawItemHandler := @ListViewDrawItem; 892 Result := Params.Window; 893 if TCustomListView(AWinControl).checkboxes 894 then UpdateExStyle(result,lvs_ex_SubitemImages or lvs_Ex_Checkboxes,lvs_ex_SubitemImages or lvs_Ex_Checkboxes) else 895 UpdateExStyle(Result, LVS_EX_SUBITEMIMAGES, LVS_EX_SUBITEMIMAGES); 896end; 897 898class procedure TWin32WSCustomListView.BeginUpdate(const ALV: TCustomListView); 899begin 900 if not WSCheckHandleAllocated(ALV, 'BeginUpdate') 901 then Exit; 902 903 SendMessage(ALV.Handle,WM_SETREDRAW,WPARAM(False),0); 904end; 905 906class procedure TWin32WSCustomListView.EndUpdate(const ALV: TCustomListView); 907var 908 ColIndex : Integer; 909begin 910 if not WSCheckHandleAllocated(ALV, 'EndUpdate') 911 then Exit; 912 913 // we have skipped all column resizing in ItemSetText() 914 // for performance reasons, so now we need to do it here. 915 // 916 // A further significant perfomance boost and reduced flickering 917 // can be achieved by setting the widget to invisible during the 918 // following operation (it ignores the state of WM_SETREDRAW for 919 // column resizing, but this way we we can really enforce it). 920 // ShowWindow() itself does not force an immediate redraw, 921 // so it won't flicker at all. 922 ShowWindow(ALV.Handle, SW_HIDE); 923 for ColIndex := 0 to TCustomListViewAccess(ALV).Columns.Count - 1 do 924 if ALV.Column[ColIndex].AutoSize 925 then ColumnDoAutosize(ALV, ColIndex); 926 927 SendMessage(ALV.Handle,WM_SETREDRAW,WPARAM(True),0); 928 if ALV.Visible then 929 ShowWindow(ALV.Handle, SW_SHOW); 930end; 931 932class function TWin32WSCustomListView.GetBoundingRect(const ALV: TCustomListView): TRect; 933begin 934 Result := Rect(0,0,0,0); 935 if not WSCheckHandleAllocated(ALV, 'GetBoundingRect') 936 then Exit; 937 938 ListView_GetViewRect(ALV.Handle, Result); 939end; 940 941class function TWin32WSCustomListView.GetDropTarget(const ALV: TCustomListView): Integer; 942begin 943 Result := -1; 944 if not WSCheckHandleAllocated(ALV, 'GetDropTarget') 945 then Exit; 946 947 Result := ListView_GetNextItem(ALV.Handle, -1, LVNI_ALL or LVNI_DROPHILITED); 948end; 949 950class function TWin32WSCustomListView.GetFocused(const ALV: TCustomListView): Integer; 951begin 952 Result := -1; 953 if not WSCheckHandleAllocated(ALV, 'GetFocused') 954 then Exit; 955 956 Result := ListView_GetNextItem(ALV.Handle, -1, LVNI_ALL or LVNI_FOCUSED); 957end; 958 959class function TWin32WSCustomListView.GetHitTestInfoAt( const ALV: TCustomListView; X, Y: Integer ) : THitTests; 960var 961 HitInfo: LV_HITTESTINFO; 962 963begin 964 Result := []; 965 if not WSCheckHandleAllocated(ALV, 'GetHitTestInfoAt') 966 then Exit; 967 968 with HitInfo do 969 begin 970 pt.X := X; 971 pt.Y := Y; 972 ListView_HitTest( ALV.Handle, HitInfo ); 973 974 if ((flags and LVHT_ABOVE) <> 0) and (Y < 0) then 975 Include(Result, htAbove); 976 977 if (flags and LVHT_BELOW) <> 0 then 978 Include(Result, htBelow); 979 980 if (flags and LVHT_NOWHERE) <> 0 then 981 Include(Result, ComCtrls.htNowhere); 982 983 if (flags and LVHT_ONITEM) = LVHT_ONITEM then 984 Include(Result, htOnItem) 985 986 else 987 begin 988 if (flags and LVHT_ONITEMICON) <> 0 then 989 Include(Result, htOnIcon); 990 991 if (flags and LVHT_ONITEMLABEL) <> 0 then 992 Include(Result, htOnLabel); 993 994 if (flags and LVHT_ONITEMSTATEICON) <> 0 then 995 Include(Result, htOnStateIcon); 996 997 end; 998 999 if (flags and LVHT_TOLEFT) <> 0 then 1000 Include(Result, htToLeft); 1001 1002 if (flags and LVHT_TORIGHT) <> 0 then 1003 Include(Result, htToRight); 1004 1005 end; 1006end; 1007 1008class function TWin32WSCustomListView.GetHoverTime(const ALV: TCustomListView): Integer; 1009begin 1010 Result := -1; 1011 if not WSCheckHandleAllocated(ALV, 'GetHoverTime') 1012 then Exit; 1013 1014 Result := SendMessage(ALV.Handle, LVM_GETHOVERTIME, 0, 0); 1015end; 1016 1017class function TWin32WSCustomListView.GetItemAt(const ALV: TCustomListView; x, 1018 y: Integer): Integer; 1019var 1020 HitInfo: LV_HITTESTINFO; 1021begin 1022 Result := -1; 1023 if not WSCheckHandleAllocated(ALV, 'GetItemAt') 1024 then Exit; 1025 1026 HitInfo.pt.x:=x; 1027 HitInfo.pt.y:=y; 1028 ListView_HitTest(alv.Handle,HitInfo); 1029 if HitInfo.flags <> LVHT_NOWHERE 1030 then Result:=HitInfo.iItem; 1031end; 1032 1033class function TWin32WSCustomListView.GetSelCount(const ALV: TCustomListView): Integer; 1034begin 1035 Result := 0; 1036 if not WSCheckHandleAllocated(ALV, 'GetSelCount') 1037 then Exit; 1038 1039 Result := ListView_GetSelectedCount(ALV.Handle); 1040end; 1041 1042class function TWin32WSCustomListView.GetSelection(const ALV: TCustomListView): Integer; 1043begin 1044 Result := -1; 1045 if not WSCheckHandleAllocated(ALV, 'GetSelection') 1046 then Exit; 1047 1048 Result := ListView_GetNextItem(ALV.Handle, -1, LVNI_ALL or LVNI_SELECTED); 1049end; 1050 1051class function TWin32WSCustomListView.GetTopItem(const ALV: TCustomListView): Integer; 1052begin 1053 Result := -1; 1054 if not WSCheckHandleAllocated(ALV, 'GetTopItem') 1055 then Exit; 1056 1057 case GetWindowLong(ALV.Handle, GWL_STYLE) and LVS_TYPEMASK of 1058 LVS_LIST, 1059 LVS_REPORT: Result := ListView_GetTopIndex(ALV.Handle); 1060 else 1061 Result := -1; 1062 end; 1063end; 1064 1065class function TWin32WSCustomListView.GetViewOrigin(const ALV: TCustomListView): TPoint; 1066begin 1067 if not WSCheckHandleAllocated(ALV, 'GetViewOrigin') 1068 then begin 1069 Result := Point(0, 0); 1070 Exit; 1071 end; 1072 1073 ListView_GetOrigin(ALV.Handle, Result); 1074end; 1075 1076class function TWin32WSCustomListView.GetVisibleRowCount(const ALV: TCustomListView): Integer; 1077begin 1078 Result := 0; 1079 if not WSCheckHandleAllocated(ALV, 'GetVisibleRowCount') 1080 then Exit; 1081 1082 case GetWindowLong(ALV.Handle, GWL_STYLE) and LVS_TYPEMASK of 1083 LVS_LIST, 1084 LVS_REPORT: Result := ListView_GetCountPerPage(ALV.Handle); 1085 else 1086 Result := -1; 1087 end; 1088end; 1089 1090class procedure TWin32WSCustomListView.SelectAll(const ALV: TCustomListView; 1091 const AIsSet: Boolean); 1092begin 1093 if not WSCheckHandleAllocated(ALV, 'SelectAll') then 1094 exit; 1095 // Index param -1 means select all. 1096 if AIsSet then 1097 ListView_SetItemState(ALV.Handle, -1, LVIS_SELECTED, LVIS_SELECTED) 1098 else 1099 ListView_SetItemState(ALV.Handle, -1, 0, LVIS_SELECTED); 1100end; 1101 1102class function TWin32WSCustomListView.GetHeader(const AHandle: THandle): THandle; 1103begin 1104 Result := THandle(SendMessage(AHandle, LVM_GETHEADER, 0, 0)); 1105 if Result <> 0 then Exit; 1106 1107 // probably old version, try the first child 1108 Result := GetWindow(AHandle, GW_CHILD); 1109end; 1110 1111// MWE: original from MS knowledgebase KB137520 1112(******************************************************************** 1113 PositionHeader 1114 1115 Call this function when the ListView is created, resized, the 1116 view is changed, or a WM_SYSPARAMETERCHANGE message is received. 1117 1118 ********************************************************************) 1119class procedure TWin32WSCustomListView.PositionHeader(const AHandle: THandle); 1120var 1121 hwndHeader: HWND; 1122 dwStyle: PtrInt; 1123 rc: TRect; 1124 hdLayout: THDLAYOUT; 1125 wpos: Windows.TWINDOWPOS; 1126begin 1127 dwStyle := GetWindowLong(AHandle, GWL_STYLE); 1128 1129 if dwStyle and LVS_NOSCROLL = 0 then Exit; // nothing to do 1130 if dwStyle and LVS_REPORT = 0 then Exit; // nothing to do 1131 1132 hwndHeader := GetHeader(AHandle); 1133 if hwndHeader = 0 then Exit; // nothing to do 1134 1135 Windows.GetClientRect(AHandle, rc); 1136 FillChar(hdLayout, SizeOf(hdLayout), 0); 1137 hdLayout.prc := @rc; 1138 hdLayout.pwpos := @wpos; 1139 Header_Layout(hwndHeader, hdLayout); 1140 1141 Windows.SetWindowPos(hwndHeader, 1142 wpos.hwndInsertAfter, 1143 wpos.x, 1144 wpos.y, 1145 wpos.cx, 1146 wpos.cy, 1147 wpos.flags or SWP_SHOWWINDOW); 1148 1149 ListView_EnsureVisible(AHandle, 0, 0); 1150end; 1151 1152class procedure TWin32WSCustomListView.SetAllocBy(const ALV: TCustomListView; const AValue: Integer); 1153begin 1154 if not WSCheckHandleAllocated(ALV, 'SetAllocBy') 1155 then Exit; 1156 1157 ListView_SetItemCount(ALV.Handle, AValue); 1158end; 1159 1160class procedure TWin32WSCustomListView.SetBorderStyle(const AWinControl: TWinControl; const ABorderStyle: TBorderStyle); 1161begin 1162 if not WSCheckHandleAllocated(AWinControl, 'TWin32WSCustomListView.SetBorder') then 1163 Exit; 1164 // changing border style by changing EXSTYLE here does not work correctly 1165 RecreateWnd(AWinControl); 1166end; 1167 1168class procedure TWin32WSCustomListView.SetColor(const AWinControl: TWinControl); 1169var 1170 Color: TColor; 1171begin 1172 if not WSCheckHandleAllocated(AWinControl, 'TWin32WSCustomListView.SetColor') then 1173 Exit; 1174 Color := AWinControl.Color; 1175 if Color = clDefault then 1176 Color := AWinControl.GetDefaultColor(dctBrush); 1177 Windows.SendMessage(AWinControl.Handle, LVM_SETBKCOLOR, 0, ColorToRGB(Color)); 1178 Windows.SendMessage(AWinControl.Handle, LVM_SETTEXTBKCOLOR, 0, ColorToRGB(Color)); 1179end; 1180 1181class procedure TWin32WSCustomListView.SetDefaultItemHeight(const ALV: TCustomListView; const AValue: Integer); 1182begin 1183 if not WSCheckHandleAllocated(ALV, 'SetDefaultItemHeight') 1184 then Exit; 1185 1186 // TODO ??? 1187end; 1188 1189class procedure TWin32WSCustomListView.SetFont(const AWinControl: TWinControl; const AFont: TFont); 1190var 1191 Color: TColor; 1192begin 1193 // call inherited SetFont; need to do it this way, 1194 // because the compile time ancestor class is TWSCustomListView 1195 TWSWinControlClass(ClassParent).SetFont(AWinControl, AFont); 1196 Color := AFont.Color; 1197 if Color = clDefault then 1198 Color := AWinControl.GetDefaultColor(dctFont); 1199 Windows.SendMessage(AWinControl.Handle, LVM_SETTEXTCOLOR, 0, ColorToRGB(Color)); 1200end; 1201 1202class procedure TWin32WSCustomListView.SetHotTrackStyles(const ALV: TCustomListView; const AValue: TListHotTrackStyles); 1203const 1204 MASK = LVS_EX_ONECLICKACTIVATE or LVS_EX_TWOCLICKACTIVATE or LVS_EX_UNDERLINEHOT or LVS_EX_UNDERLINECOLD; 1205var 1206 Style: Integer; 1207begin 1208 if not WSCheckHandleAllocated(ALV, 'SetHotTrackStyles') 1209 then Exit; 1210 1211 if htHandPoint in AValue 1212 then Style := LVS_EX_ONECLICKACTIVATE 1213 else if [htUnderlineHot, htUnderlineCold] * AValue <> [] 1214 then Style := LVS_EX_TWOCLICKACTIVATE 1215 else Style := 0; 1216 1217 if htUnderlineHot in AValue 1218 then Style := Style or LVS_EX_UNDERLINEHOT; 1219 1220 if htUnderlineCold in AValue 1221 then Style := Style or LVS_EX_UNDERLINECOLD; 1222 1223 UpdateExStyle(ALV.Handle, MASK, Style); 1224end; 1225 1226class procedure TWin32WSCustomListView.SetHoverTime(const ALV: TCustomListView; const AValue: Integer); 1227begin 1228 if not WSCheckHandleAllocated(ALV, 'SetHoverTime') 1229 then Exit; 1230 1231 SendMessage(ALV.Handle, LVM_SETHOVERTIME, 0, AValue); 1232end; 1233 1234class procedure TWin32WSCustomListView.SetIconArrangement( 1235 const ALV: TCustomListView; const AValue: TIconArrangement); 1236const 1237 ArrangementMap: array[TIconArrangement] of DWord = ( 1238 { iaTop } LVS_ALIGNTOP, 1239 { iaLeft } LVS_ALIGNLEFT 1240 ); 1241begin 1242 if not WSCheckHandleAllocated(ALV, 'SetIconArrangement') 1243 then Exit; 1244 1245 // LVM_ALIGN styles are not implemented in windows (according to w7 sdk) => change style 1246 UpdateStyle(ALV.Handle, LVS_ALIGNMASK, ArrangementMap[AValue]); 1247end; 1248 1249class procedure TWin32WSCustomListView.SetImageList(const ALV: TCustomListView; 1250 const AList: TListViewImageList; const AValue: TCustomImageListResolution); 1251const 1252 LIST_MAP: array[TListViewImageList] of WPARAM = ( 1253 {lvilSmall} LVSIL_SMALL, 1254 {lvilLarge} LVSIL_NORMAL, 1255 {lvilState} LVSIL_STATE 1256 ); 1257begin 1258 if not WSCheckHandleAllocated(ALV, 'SetImageList') 1259 then Exit; 1260 1261 if AValue <> nil then 1262 SendMessage(ALV.Handle, LVM_SETIMAGELIST, LIST_MAP[AList], AValue.Reference._Handle) 1263 else 1264 SendMessage(ALV.Handle, LVM_SETIMAGELIST, LIST_MAP[AList], 0); 1265end; 1266 1267class procedure TWin32WSCustomListView.SetItemsCount(const ALV: TCustomListView; const AValue: Integer); 1268begin 1269 if not WSCheckHandleAllocated(ALV, 'SetItemsCount') 1270 then Exit; 1271 SendMessage(ALV.Handle, LVM_SETITEMCOUNT, AValue, 0); 1272end; 1273 1274class procedure TWin32WSCustomListView.SetOwnerData(const ALV: TCustomListView; const AValue: Boolean); 1275begin 1276 if not WSCheckHandleAllocated(ALV, 'SetOwnerData') 1277 then Exit; 1278 RecreateWnd(ALV); 1279end; 1280 1281class procedure TWin32WSCustomListView.SetProperty(const ALV: TCustomListView; const AProp: TListViewProperty; const AIsSet: Boolean); 1282begin 1283 if not WSCheckHandleAllocated(ALV, 'SetProperty') 1284 then Exit; 1285 1286 case LV_STYLES[AProp].StyleType of 1287 lsStyle: begin 1288 if AIsSet 1289 then UpdateStyle(ALV.Handle, LV_STYLES[AProp].Style, LV_STYLES[AProp].Style) 1290 else UpdateStyle(ALV.Handle, LV_STYLES[AProp].Style, 0); 1291 end; 1292 lsInvert: 1293 begin 1294 // we are always using readonly on ws since ListView_GetEditControl 1295 // requires minimum windows 2000. Editing is implemented in LCL atm, 1296 // can be changed later to use ws for item editing. 1297 // http://msdn.microsoft.com/en-us/library/windows/desktop/bb761260%28v=vs.85%29.aspx 1298 if (AProp = lvpReadOnly) then 1299 UpdateStyle(ALV.Handle, LV_STYLES[AProp].Style, 0) 1300 else 1301 if AIsSet then 1302 UpdateStyle(ALV.Handle, LV_STYLES[AProp].Style, 0) 1303 else 1304 UpdateStyle(ALV.Handle, LV_STYLES[AProp].Style, LV_STYLES[AProp].Style); 1305 end; 1306 lsExStyle: begin 1307 if AIsSet 1308 then UpdateExStyle(ALV.Handle, LV_STYLES[AProp].Style, LV_STYLES[AProp].Style) 1309 else UpdateExStyle(ALV.Handle, LV_STYLES[AProp].Style, 0); 1310 end; 1311 end; 1312 if(aProp=lvpCheckboxes)and aIsSet 1313 then ReCreateWnd(ALV) 1314end; 1315 1316class procedure TWin32WSCustomListView.SetProperties(const ALV: TCustomListView; const AProps: TListViewProperties); 1317var 1318 Prop: TListViewProperty; 1319 Style, ExStyle, Mask, ExMask: Integer; 1320begin 1321 if not WSCheckHandleAllocated(ALV, 'SetProperties') 1322 then Exit; 1323 1324 Style := 0; 1325 ExStyle := 0; 1326 Mask := 0; 1327 ExMask := 0; 1328 1329 for Prop := Low(Prop) to High(Prop) do 1330 begin 1331 case LV_STYLES[Prop].StyleType of 1332 lsStyle, 1333 lsInvert: 1334 begin 1335 // we are always using readonly on ws since ListView_GetEditControl 1336 // requires minimum windows 2000. Editing is implemented in LCL atm, 1337 // can be changed later to use ws for item editing. 1338 // http://msdn.microsoft.com/en-us/library/windows/desktop/bb761260%28v=vs.85%29.aspx 1339 if Prop = lvpReadOnly then 1340 UpdateStyle(ALV.Handle, LVS_EDITLABELS, 0) 1341 else 1342 begin 1343 Mask := Mask or LV_STYLES[Prop].Style; 1344 if (LV_STYLES[Prop].StyleType = lsStyle) = (Prop in AProps) 1345 then Style := Style or LV_STYLES[Prop].Style 1346 else Style := Style and not LV_STYLES[Prop].Style; 1347 end; 1348 end; 1349 lsExStyle: begin 1350 ExMask := ExMask or LV_STYLES[Prop].Style; 1351 1352 if Prop in AProps 1353 then ExStyle := ExStyle or LV_STYLES[Prop].Style 1354 else ExStyle := ExStyle and not LV_STYLES[Prop].Style; 1355 end; 1356 end; 1357 end; 1358 1359 if Mask <> 0 1360 then UpdateStyle(ALV.Handle, Mask, Style); 1361 if ExMask <> 0 1362 then UpdateExStyle(ALV.Handle, ExMask, ExStyle); 1363end; 1364 1365class procedure TWin32WSCustomListView.SetScrollBars(const ALV: TCustomListView; const AValue: TScrollStyle); 1366begin 1367 if not WSCheckHandleAllocated(ALV, 'SetScrollBars') 1368 then Exit; 1369 1370 // we only can hide all scrollbars. 1371 if AValue = ssNone 1372 then UpdateStyle(ALV.Handle, LVS_NOSCROLL, LVS_NOSCROLL) 1373 else UpdateStyle(ALV.Handle, LVS_NOSCROLL, 0); 1374end; 1375 1376function ListCompare(lParam1, lParam2: LParam; lParamSort: LParam): Integer; stdcall; 1377var 1378 Item1: TListItem absolute lParam1; 1379 Item2: TListItem absolute lParam2; 1380begin 1381 Result := CompareValue(Item1.Index, Item2.Index); 1382end; 1383 1384class procedure TWin32WSCustomListView.SetSort(const ALV: TCustomListView; 1385 const AType: TSortType; const AColumn: Integer; const ASortDirection: TSortDirection); 1386begin 1387 if not WSCheckHandleAllocated(ALV, 'SetSort') 1388 then Exit; 1389 ListView_SortItems(ALV.Handle, @ListCompare, Windows.MAKELPARAM(Ord(AType), AColumn)); 1390end; 1391 1392class procedure TWin32WSCustomListView.SetViewOrigin(const ALV: TCustomListView; const AValue: TPoint); 1393var 1394 dx, dy: Integer; 1395 Origin: TPoint; 1396begin 1397 if not WSCheckHandleAllocated(ALV, 'SetViewOrigin') 1398 then Exit; 1399 1400 ListView_GetOrigin(ALV.Handle, Origin); 1401 1402 dx := AValue.X - Origin.X; 1403 dy := AValue.Y - Origin.Y; 1404 if (dx <> 0) or (dy <> 0) 1405 then ListView_Scroll(ALV.Handle, dx, dy); 1406end; 1407 1408class procedure TWin32WSCustomListView.SetViewStyle(const ALV: TCustomListView; const Avalue: TViewStyle); 1409const 1410 //vsIcon, vsSmallIcon, vsList, vsReport 1411 STYLES: array[TViewStyle] of DWORD = (LVS_ICON, LVS_SMALLICON, LVS_LIST, LVS_REPORT); 1412begin 1413 if not WSCheckHandleAllocated(ALV, 'SetViewStyle') 1414 then Exit; 1415 1416 UpdateStyle(ALV.Handle, LVS_TYPEMASK, STYLES[AValue]); 1417end; 1418 1419class procedure TWin32WSCustomListView.UpdateStyle(const AHandle: THandle; const AMask, AStyle: Integer); 1420var 1421 OldStyle, NewStyle: PtrInt; 1422begin 1423 OldStyle := GetWindowLong(AHandle, GWL_STYLE); 1424 1425 NewStyle := (OldStyle and not AMask) or AStyle; 1426 1427 if OldStyle = NewStyle then Exit; 1428 1429 SetWindowLong(AHandle, GWL_STYLE, NewStyle); 1430 1431 // fix header if needed 1432 if (NewStyle and LVS_NOSCROLL)<> 0 then begin 1433 if (OldStyle and LVS_NOSCROLL = 0) 1434 or (NewStyle and LVS_REPORT <> 0) 1435 then PositionHeader(AHandle); 1436 end; 1437 1438 //Invalidate Listview, so that changes are made visible 1439 Windows.InvalidateRect(AHandle, nil, true); 1440end; 1441 1442class procedure TWin32WSCustomListView.UpdateExStyle(const AHandle: THandle; const AMask, AStyle: Integer); 1443var 1444 OldStyle, NewStyle: Integer; 1445begin 1446 OldStyle := SendMessage(AHandle, LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0); 1447 1448 NewStyle := (OldStyle and not AMask) or AStyle; 1449 1450 if OldStyle = NewStyle then Exit; 1451 1452 SendMessage(AHandle, LVM_SETEXTENDEDLISTVIEWSTYLE, 0, NewStyle); 1453 1454 //Invalidate Listview, so that changes are made visible 1455 Windows.InvalidateRect(AHandle, nil, true); 1456end; 1457 1458class function TWin32WSCustomListView.GetNextItem(const ALV: TCustomListView; const StartItem: TListItem; 1459 const Direction: TSearchDirection; const States: TListItemStates): TListItem; 1460var 1461 Flags, Index: Integer; 1462begin 1463 Result := nil; 1464 1465 if not WSCheckHandleAllocated(ALV, 'GetNextItem') 1466 then Exit; 1467 1468 Flags := 0; 1469 case Direction of 1470 sdAbove: Flags := LVNI_ABOVE; 1471 sdBelow: Flags := LVNI_BELOW; 1472 sdLeft: Flags := LVNI_TOLEFT; 1473 sdRight: Flags := LVNI_TORIGHT; 1474 sdAll: Flags := LVNI_ALL; 1475 end; 1476 1477 if StartItem <> nil then Index := StartItem.Index else Index := -1; 1478 1479 if lisCut in States then Flags := Flags or LVNI_CUT; 1480 if lisDropTarget in States then Flags := Flags or LVNI_DROPHILITED; 1481 if lisFocused in States then Flags := Flags or LVNI_FOCUSED; 1482 if lisSelected in States then Flags := Flags or LVNI_SELECTED; 1483 1484 Index := ListView_GetNextItem(ALV.Handle, Index, Flags); 1485 if Index <> -1 then Result := ALV.Items[Index]; 1486end; 1487