1{%MainUnit ../comboex.pas} 2 3{***************************************************************************** 4 TCustomComboBoxEx, TCustomCheckComboBox 5 ***************************************************************************** 6 7 ***************************************************************************** 8 See the file COPYING.modifiedLGPL.txt, included in this distribution, 9 for details about the license. 10 ***************************************************************************** 11} 12 13{ TListControlItem } 14 15constructor TListControlItem.Create(ACollection: TCollection); 16begin 17 inherited Create(ACollection); 18 FImageIndex:=-1; 19end; 20 21{ TListControlItem.Setters } 22 23procedure TListControlItem.SetCaption(const AValue: TTranslateString); 24begin 25 if FCaption=AValue then exit; 26 FCaption:=AValue; 27 Changed(False); 28end; 29 30procedure TListControlItem.SetImageIndex(AValue: TImageIndex); 31begin 32 if FImageIndex=AValue then exit; 33 FImageIndex:=AValue; 34 Changed(False); 35end; 36 37{ TComboExItem } 38 39constructor TComboExItem.Create(ACollection: TCollection); 40begin 41 inherited Create(ACollection); 42 FIndent:=-1; 43 FOverlayImageIndex:=-1; 44 FSelectedImageIndex:=-1; 45end; 46 47destructor TComboExItem.Destroy; 48begin 49 { normally, Items.Count should be already MenuItems.Count-1 ATM } 50 { this solves case when item is not deleted via Collection.Delete(Index) } 51 { but directly via Item.Free (exactly what Collection Editor of IDE does) } 52 { therefore Notify must be called from here, so count of Items and MenuItems remains same } 53 if assigned(Collection) and assigned(Collection.Owner) and 54 not (csDestroying in (Collection.Owner as TCustomComboBoxEx).ComponentState) 55 and (Collection.Count <= (Collection.Owner as TCustomComboBoxEx).Items.Count) 56 then TComboExItems(Collection).Notify(self, cnDeleting); 57 inherited Destroy; 58end; 59 60{ TComboExItem.Setters } 61 62procedure TComboExItem.SetIndent(AValue: Integer); 63begin 64 if FIndent=AValue then exit; 65 FIndent:=AValue; 66 Changed(False); 67end; 68 69procedure TComboExItem.SetOverlayImageIndex(AValue: TImageIndex); 70begin 71 if FOverlayImageIndex=AValue then exit; 72 FOverlayImageIndex:=AValue; 73 { Changed(False); } 74end; 75 76procedure TComboExItem.SetSelectedImageIndex(AValue: TImageIndex); 77begin 78 if FSelectedImageIndex=AValue then exit; 79 FSelectedImageIndex:=AValue; 80 Changed(False); 81end; 82 83{ TListControlItems } 84 85function TListControlItems.Add: TListControlItem; 86begin 87 Result:=TListControlItem.Create(self); 88end; 89 90function TListControlItems.CompareItems(AItem1, AItem2: TListControlItem): Integer; 91begin 92 if CaseSensitive 93 then Result:=CompareStr((AItem1 as TListControlItem).Caption, 94 (AItem2 as TListControlItem).Caption) 95 else Result:=CompareStr(lowercase((AItem1 as TListControlItem).Caption), 96 lowercase((AItem2 as TListControlItem).Caption)); 97end; 98 99procedure TListControlItems.CustomSort(ACompare: TListItemsCompare); 100begin 101 if assigned(ACompare) then 102 begin 103 FCompare:=ACompare; 104 Sort; 105 FCompare:=nil; 106 end; 107end; 108 109function TListControlItems.DoCustomSort(AItem1, AItem2: TListControlItem): Integer; 110begin 111 Result:=FCompare(self, AItem1.Index, AItem2.Index); 112end; 113 114function TListControlItems.DoOnCompare(AItem1, AItem2: TListControlItem): Integer; 115begin 116 Result:=OnCompare(self, AItem1, AItem2); 117end; 118 119procedure TListControlItems.Sort; 120var pCompareItems: function(AItem1, AItem2: TListControlItem): Integer of object; 121 122 procedure QuickSort(aTop, aBottom: Integer); 123 var i, j, aPivot: Integer; 124 begin 125 repeat 126 i:=aTop; 127 j:=aBottom; 128 aPivot:=(aTop+aBottom) div 2; 129 repeat 130 while pCompareItems(Items[aPivot], Items[i])>0 do 131 inc(i); 132 while pCompareItems(Items[aPivot], Items[j])<0 do 133 dec(j); 134 if i<=j then 135 begin 136 if i<>j then 137 if pCompareItems(Items[i], Items[j])<>0 then Exchange(i, j); 138 if aPivot=i 139 then aPivot:=j 140 else if aPivot=j then aPivot:=i; 141 inc(i); 142 dec(j); 143 end; 144 until i>j; 145 if aTop<j then QuickSort(aTop, j); 146 aTop:=i; 147 until i>=aBottom; 148 end; 149 150var aID: Integer; 151begin 152 pCompareItems:=nil; 153 if assigned(FCompare) 154 then pCompareItems:=@DoCustomSort 155 else 156 case SortType of 157 stData: if assigned(OnCompare) then pCompareItems:=@DoOnCompare; 158 stText: pCompareItems:=@CompareItems; 159 stBoth: if assigned(OnCompare) 160 then pCompareItems:=@DoOnCompare 161 else pCompareItems:=@CompareItems; 162 end; 163 aID:=Items[(Owner as TCustomComboBoxEx).ItemIndex].ID; 164 BeginUpdate; 165 if assigned(pCompareItems) then QuickSort(0, Count-1); 166 (Owner as TCustomComboBoxEx).ItemIndex:=FindItemID(aID).Index; 167 EndUpdate; 168end; 169 170procedure TListControlItems.Update(AItem: TCollectionItem); 171begin 172 inherited Update(AItem); 173end; 174 175{ TListControlItems.Getters and Setters } 176 177function TListControlItems.GetItems(AIndex: Integer): TListControlItem; 178begin 179 Result:=GetItem(AIndex) as TListControlItem; 180end; 181 182procedure TListControlItems.SetCaseSensitive(AValue: Boolean); 183begin 184 if FCaseSensitive=AValue then exit; 185 FCaseSensitive:=AValue; 186end; 187 188procedure TListControlItems.SetSortType(AValue: TListItemsSortType); 189begin 190 if FSortType=AValue then exit; 191 FSortType:=AValue; 192 Sort; 193end; 194 195{ TComboExItems } 196 197function TComboExItems.Add: TComboExItem; 198begin 199 Result:=TComboExItem.Create(self); 200end; 201 202function TComboExItems.AddItem(const ACaption: string; AImageIndex: SmallInt; 203 AOverlayImageIndex: SmallInt; ASelectedImageIndex: SmallInt; AIndent: SmallInt; AData: TCustomData 204 ): TComboExItem; 205begin 206 Result:=Add(); 207 with Result do 208 begin 209 Caption:=ACaption; 210 Indent:=AIndent; 211 ImageIndex:=AImageIndex; 212 OverlayImageIndex:=AOverlayImageIndex; 213 SelectedImageIndex:=ASelectedImageIndex; 214 Data:=AData; 215 end; 216end; 217 218function TComboExItems.Insert(AIndex: Integer): TComboExItem; 219begin 220 Result := TComboExItem(inherited Insert(AIndex)); 221end; 222 223procedure TComboExItems.Notify(Item: TCollectionItem; Action: TCollectionNotification); 224var i: Integer; 225begin 226 inherited Notify(Item, Action); 227 case Action of 228 cnAdded: 229 begin 230 FAddingOrDeletingItem:=True; 231 with Owner as TCustomComboBoxEx do 232 begin 233 Items.Add(''); 234 if not (csLoading in ComponentState) then 235 TComboExItem(Item).FCaption:=TComboExItem.cDefCaption+inttostr(Item.ID); 236 end; 237 end; 238 cnDeleting: 239 begin 240 FAddingOrDeletingItem:=True; 241 with Owner as TCustomComboBoxEx do 242 begin 243 i:=ItemIndex; 244 Items.Delete(Item.Index); 245 if i<Count then ItemIndex:=i 246 else if i>0 then ItemIndex:=i-1; 247 end; 248 end; 249 end; 250end; 251 252procedure TComboExItems.Update(Item: TCollectionItem); 253var aItemIndex: Integer; 254begin 255 inherited Update(Item); 256 aItemIndex:=(Owner as TCustomComboBoxEx).ItemIndex; 257 if not assigned(Item) or ((aItemIndex>=0) and 258 (Item=(Owner as TCustomComboBoxEx).ItemsEx[aItemIndex])) 259 then (Owner as TCustomComboBoxEx).Invalidate; 260 FAddingOrDeletingItem:=False; 261end; 262 263{ TComboExItems.Getters and Setters } 264 265function TComboExItems.GetComboItems(AIndex: Integer): TComboExItem; 266begin 267 Result:=Items[AIndex] as TComboExItem; 268end; 269 270{ TCustomComboBoxEx } 271 272constructor TCustomComboBoxEx.Create(TheOwner: TComponent); 273begin 274 inherited Create(TheOwner); 275 FAutoCompleteOptions:=cDefAutoCompOpts; 276 FItemsEx:=TComboExItems.Create(self, TComboExItem); 277 FNeedMeasure:=True; 278 inherited Style:=csOwnerDrawFixed; 279 FStyle:=cDefStyle; 280 FStyleEx:=[]; 281end; 282 283destructor TCustomComboBoxEx.Destroy; 284begin 285 FreeAndNil(FItemsEx); 286 inherited Destroy; 287end; 288 289procedure TCustomComboBoxEx.Add(const ACaption: string; AIndent: Integer; 290 AImgIdx: TImageIndex; AOverlayImgIdx: TImageIndex; ASelectedImgIdx: TImageIndex); 291begin 292 Insert(ItemsEx.Count, ACaption, AIndent, AImgIdx, AOverlayImgIdx, ASelectedImgIdx); 293end; 294 295function TCustomComboBoxEx.Add: Integer; 296begin 297 Result:=ItemsEx.Count; 298 Insert(Result, TComboExItem.cDefCaption); 299end; 300 301procedure TCustomComboBoxEx.AddItem(const Item: String; AnObject: TObject); 302begin 303 Insert(ItemsEx.Count, Item); 304 ItemsEx[ItemsEx.Count].Data:=AnObject; 305end; 306 307procedure TCustomComboBoxEx.AssignItemsEx(AItemsEx: TComboExItems); 308begin 309 ItemsEx.Assign(AItemsEx); 310end; 311 312procedure TCustomComboBoxEx.AssignItemsEx(AItems: TStrings); 313var i: Integer; 314begin 315 FItemsEx.BeginUpdate; 316 FItemsEx.Clear; 317 for i:=0 to AItems.Count-1 do 318 ItemsEx.AddItem(AItems[i]); 319 FItemsEx.EndUpdate; 320end; 321 322procedure TCustomComboBoxEx.Clear; 323begin 324 FItemsEx.Clear; 325end; 326 327procedure TCustomComboBoxEx.CMBiDiModeChanged(var Message: TLMessage); 328begin 329 inherited CMBiDiModeChanged(Message); 330 FRightToLeft:=IsRightToLeft; 331 Invalidate; 332end; 333 334procedure TCustomComboBoxEx.Delete(AIndex: Integer); 335begin 336 ItemsEx.Delete(AIndex); 337end; 338 339procedure TCustomComboBoxEx.DeleteSelected; 340begin 341 if ItemIndex>=0 then Delete(ItemIndex); 342end; 343 344procedure TCustomComboBoxEx.DrawItem(Index: Integer; ARect: TRect; State: TOwnerDrawState); 345const caThemes: array [Boolean] of TThemedButton = (tbPushButtonDisabled, tbPushButtonNormal); 346 cItemIndent: SmallInt = 2; 347var aDetail: TThemedElementDetails; 348 aDropped: Boolean; 349 aEnabled: Boolean; 350 aFlags: Cardinal; 351 aFocusedEditableMainItemNoDD: Boolean; { combo has edit-like line edit in csDropDownList (Win) and is closed (not DroppedDown } 352 aImgPoint: TPoint; 353 aIndent: SmallInt; 354 aItemIndex: SmallInt; 355 aMainItem: Boolean; 356 anyRect: TRect; 357 ImagesSize: TSize; 358begin { do not call inherited ! } 359 aDropped:=DroppedDown; 360 aEnabled:=IsEnabled; 361 aMainItem:= (ARect.Left>0); 362 {$IF DEFINED(LCLWin32) or DEFINED(LCLWin64)} 363 aFocusedEditableMainItemNoDD := (Focused and aMainItem and not aDropped); 364 {$ELSE} 365 aFocusedEditableMainItemNoDD := False; 366 {$ENDIF} 367 if aDropped and not aMainItem or aFocusedEditableMainItemNoDD then 368 begin 369 if not (odSelected in State) then Canvas.Brush.Color:=clWindow; 370 Canvas.Brush.Style:=bsSolid; 371 Canvas.FillRect(ARect); 372 end; 373 aDetail:=ThemeServices.GetElementDetails(caThemes[aEnabled]); 374 if FNeedMeasure then 375 begin 376 FTextHeight:=Canvas.TextHeight('ŠjÁÇ'); 377 FNeedMeasure := False; 378 end; 379 if not aMainItem 380 then aIndent:=TComboExItem(ItemsEx[Index]).Indent 381 else aIndent:=-1; 382 if aIndent<0 then aIndent:=0; 383 inc(aIndent, cItemIndent); 384 if assigned(Images) then 385 begin 386 aItemIndex:=-1; 387 ImagesSize := Images.SizeForPPI[ImagesWidth, Font.PixelsPerInch]; 388 if (aMainItem or (odSelected in State)) and 389 ((ItemsEx[Index].SelectedImageIndex>=0) and (ItemsEx[Index].SelectedImageIndex<Images.Count)) 390 then aItemIndex:=ItemsEx[Index].SelectedImageIndex; 391 if aItemIndex<0 then aItemIndex:=ItemsEx[Index].ImageIndex; 392 if aItemIndex>=0 then 393 begin 394 if not FRightToLeft 395 then aImgPoint.X:=ARect.Left+aIndent 396 else aImgPoint.X:=ARect.Right-aIndent-ImagesSize.cx; 397 aImgPoint.Y:=(ARect.Bottom+ARect.Top-ImagesSize.cy) div 2; 398 ThemeServices.DrawIcon(Canvas, aDetail, aImgPoint, Images, aItemIndex); 399 end; 400 inc(aIndent, ImagesSize.cx+2*cItemIndent); 401 end; 402 Canvas.Brush.Style:=bsClear; 403 if (not (odSelected in State) or not aDropped) and not aFocusedEditableMainItemNoDD 404 then Canvas.Font.Color:=clWindowText 405 else Canvas.Font.Color:=clHighlightText; 406 if aFocusedEditableMainItemNoDD then 407 begin 408 LCLIntf.SetBkColor(Canvas.Handle, ColorToRGB(clBtnFace)); 409 LCLIntf.DrawFocusRect(Canvas.Handle, aRect); 410 end; 411 aFlags:=DT_END_ELLIPSIS+DT_VCENTER+DT_SINGLELINE+DT_NOPREFIX; 412 if not FRightToLeft then 413 begin 414 anyRect.Left:=ARect.Left+aIndent; 415 anyRect.Right:=ARect.Right; 416 end else 417 begin 418 anyRect.Right:=ARect.Right-aIndent; 419 anyRect.Left:=ARect.Left; 420 aFlags:=aFlags or DT_RIGHT or DT_RTLREADING; 421 end; 422 anyRect.Top:=(ARect.Top+ARect.Bottom-FTextHeight) div 2; 423 anyRect.Bottom:=anyRect.Top+FTextHeight; 424 DrawText(Canvas.Handle, PChar(ItemsEx[Index].Caption), Length(ItemsEx[Index].Caption), anyRect, aFlags); 425end; 426 427procedure TCustomComboBoxEx.FontChanged(Sender: TObject); 428begin 429 FNeedMeasure:=True; 430 inherited FontChanged(Sender); 431end; 432 433procedure TCustomComboBoxEx.InitializeWnd; 434begin 435 inherited InitializeWnd; 436 FRightToLeft:=IsRightToLeft; 437end; 438 439procedure TCustomComboBoxEx.Insert(AIndex: Integer; const ACaption: string; AIndent: Integer = -1; 440 AImgIdx: TImageIndex = -1; AOverlayImgIdx: TImageIndex = -1; ASelectedImgIdx: TImageIndex = -1); 441var aItem: TCollectionItem; 442begin 443 aItem:=ItemsEx.Insert(AIndex); 444 with aItem as TComboExItem do 445 begin 446 Caption:=ACaption; 447 Indent:=AIndent; 448 ImageIndex:=AImgIdx; 449 OverlayImageIndex:=AOverlayImgIdx; 450 SelectedImageIndex:=ASelectedImgIdx; 451 end; 452end; 453 454procedure TCustomComboBoxEx.SetItemHeight(const AValue: Integer); 455begin 456 inherited SetItemHeight(AValue); 457 FNeedMeasure:=True; 458end; 459 460{ TCustomComboBoxEx.Setters } 461 462procedure TCustomComboBoxEx.SetImages(AValue: TCustomImageList); 463begin 464 if FImages=AValue then exit; 465 FImages:=AValue; 466 Invalidate; 467end; 468 469procedure TCustomComboBoxEx.SetImagesWidth(const aImagesWidth: Integer); 470begin 471 if FImagesWidth = aImagesWidth then Exit; 472 FImagesWidth := aImagesWidth; 473 Invalidate; 474end; 475 476procedure TCustomComboBoxEx.SetStyle(AValue: TComboBoxExStyle); 477begin 478 if FStyle=AValue then exit; 479 FStyle:=AValue; 480end; 481 482procedure TCustomComboBoxEx.SetStyleEx(AValue: TComboBoxExStyles); 483begin 484 if FStyleEx=AValue then exit; 485 FStyleEx:=AValue; 486end; 487 488{ TCustomCheckCombo } 489 490constructor TCustomCheckCombo.Create(AOwner: TComponent); 491begin 492 inherited Create(AOwner); 493 TStringList(Items).Duplicates:=dupIgnore; 494 Style:=csOwnerDrawFixed; 495 FNeedMeasure:=True; 496 FRejectToggleOnSelect:=True; 497end; 498 499destructor TCustomCheckCombo.Destroy; 500begin 501 ClearItemStates; 502 inherited Destroy; 503end; 504 505procedure TCustomCheckCombo.AddItem(const AItem: string; AState: TCheckBoxState; AEnabled: Boolean); 506var pItemState: TCheckComboItemState; 507begin 508 pItemState:=TCheckComboItemState.Create; 509 pItemState.State:=aState; 510 pItemState.Enabled:=AEnabled; 511 pItemState.Data:=nil; 512 inherited AddItem(AItem, pItemState); 513end; 514 515procedure TCustomCheckCombo.AssignItems(AItems: TStrings); 516begin 517 ClearItemStates; 518 Items.Assign(AItems); 519 InitItemStates; 520end; 521 522procedure TCustomCheckCombo.CheckAll(AState: TCheckBoxState; AAllowGrayed: Boolean; 523 AAllowDisabled: Boolean); 524var i: Integer; 525begin 526 for i:=0 to Items.Count-1 do 527 begin 528 if (AAllowGrayed or (State[i]<>cbGrayed)) and (AAllowDisabled or ItemEnabled[i]) 529 then State[i]:=AState; 530 end; 531end; 532 533procedure TCustomCheckCombo.Clear; 534begin 535 ClearItemStates; 536 inherited Clear; 537end; 538 539procedure TCustomCheckCombo.ClearItemStates; 540var i: Integer; 541begin 542 for i:=0 to Items.Count-1 do 543 begin 544 Items.Objects[i].Free; 545 Items.Objects[i]:=nil; 546 end; 547end; 548 549procedure TCustomCheckCombo.CloseUp; 550begin 551 FDropped:=False; 552 if FRejectDropDown then 553 begin 554 FRejectDropDown:=False; 555 Update; 556 end else 557 inherited CloseUp; 558end; 559 560procedure TCustomCheckCombo.CMBiDiModeChanged(var Message: TLMessage); 561begin 562 inherited CMBiDiModeChanged(Message); 563 FRightToLeft:=IsRightToLeft; 564 FNeedMeasure:=True; 565 Invalidate; 566end; 567 568procedure TCustomCheckCombo.DeleteItem(AIndex: Integer); 569begin 570 if (AIndex>=0) and (AIndex<Items.Count) then 571 begin 572 Items.Objects[AIndex].Free; 573 Items.Delete(AIndex); 574 end; 575end; 576 577procedure TCustomCheckCombo.DrawItem(Index: Integer; ARect: TRect; State: TOwnerDrawState); 578 { Enabled, State, Highlighted } 579const caCheckThemes: array [Boolean, TCheckBoxState, Boolean] of TThemedButton = 580 { normal, highlighted } 581 (((tbCheckBoxUncheckedDisabled, tbCheckBoxUncheckedDisabled), { disabled, unchecked } 582 (tbCheckBoxCheckedDisabled, tbCheckBoxCheckedDisabled), { disabled, checked } 583 (tbCheckBoxMixedDisabled, tbCheckBoxMixedDisabled)), { disabled, greyed } 584 ((tbCheckBoxUncheckedNormal, tbCheckBoxUncheckedHot), { enabled, unchecked } 585 (tbCheckBoxCheckedNormal, tbCheckBoxCheckedHot), { enabled, checked } 586 (tbCheckBoxMixedNormal, tbCheckBoxMixedHot))); { enabled, greyed } 587 cCheckIndent: SmallInt = 2; 588 cTextIndent: SmallInt = 5; 589var aDetail: TThemedElementDetails; 590 aDropped: Boolean; 591 aEnabled: Boolean; 592 aFlags: Cardinal; 593 aFocusedEditableMainItemNoDD: Boolean; { combo has edit-like line edit in csDropDownList (Win) and is closed (not DroppedDown } 594 aGray: Byte; 595 anyRect: TRect; 596 aState: TCheckBoxState; 597 ItemState: TCheckComboItemState; 598begin { do not call inherited ! } 599 ItemState:=TCheckComboItemState(Items.Objects[Index]); 600 if not (ItemState is TCheckComboItemState) then 601 QueueCheckItemStates; 602 aDropped:=DroppedDown; 603 if aDropped and FRejectDropDown then 604 begin 605 DroppedDown:=False; 606 exit; { Exit! } 607 end; 608 aEnabled:=IsEnabled; 609 if not (csDesigning in ComponentState) then 610 aEnabled:= (aEnabled and ItemState.Enabled); 611 {$IF DEFINED(LCLWin32) or DEFINED(LCLWin64)} 612 aFocusedEditableMainItemNoDD := (Focused and (ARect.Left>0) and not aDropped); 613 {$ELSE} 614 aFocusedEditableMainItemNoDD := False; 615 {$ENDIF} 616 if (ARect.Left=0) or aFocusedEditableMainItemNoDD then 617 begin 618 if odSelected in State then 619 begin 620 if not aEnabled then 621 begin 622 aGray:=ColorToGray(Canvas.Brush.Color); 623 Canvas.Brush.Color:=RGBToColor(aGray, aGray, aGray); 624 end; 625 end else 626 Canvas.Brush.Color:=clWindow; 627 Canvas.Brush.Style:=bsSolid; 628 Canvas.FillRect(ARect); 629 end; 630 if not (csDesigning in ComponentState) 631 then aState:=ItemState.State 632 else aState:=cbUnchecked; 633 aDetail:=ThemeServices.GetElementDetails(caCheckThemes 634 [aEnabled, aState, not aDropped and FCheckHighlight]); 635 if FNeedMeasure then 636 begin 637 FCheckSize:=ThemeServices.GetDetailSize(aDetail); 638 FTextHeight:=Canvas.TextHeight('ŠjÁÇ'); 639 if not aDropped then 640 begin 641 if not FRightToLeft then 642 begin 643 FHiLiteLeft:=-1; 644 FHiLiteRight:=ARect.Right; 645 end else 646 begin 647 FHiLiteLeft:=ARect.Left; 648 FHiLiteRight:=ARect.Right; 649 end; 650 FNeedMeasure := False; 651 end; 652 end; 653 if not FRightToLeft 654 then anyRect.Left:=ARect.Left+cCheckIndent 655 else anyRect.Left:=ARect.Right-cCheckIndent-FCheckSize.cx; 656 anyRect.Right:=anyRect.Left+FCheckSize.cx; 657 anyRect.Top:=(ARect.Bottom+ARect.Top-FCheckSize.cy) div 2; 658 anyRect.Bottom:=anyRect.Top+FCheckSize.cy; 659 ThemeServices.DrawElement(Canvas.Handle, aDetail, anyRect); 660 Canvas.Brush.Style:=bsClear; 661 if (not (odSelected in State) or not aDropped) and not aFocusedEditableMainItemNoDD 662 then Canvas.Font.Color:=clWindowText 663 else begin 664 Canvas.Font.Color:=clHighlightText; 665 FHilightedIndex:=Index; 666 end; 667 if aFocusedEditableMainItemNoDD then 668 begin 669 LCLIntf.SetBkColor(Canvas.Handle, ColorToRGB(clBtnFace)); 670 LCLIntf.DrawFocusRect(Canvas.Handle, aRect); 671 end; 672 aFlags:=DT_END_ELLIPSIS+DT_VCENTER+DT_SINGLELINE+DT_NOPREFIX; 673 if not FRightToLeft then 674 begin 675 anyRect.Left:=ARect.Left+cCheckIndent+FCheckSize.cx+cTextIndent; 676 anyRect.Right:=ARect.Right; 677 end else 678 begin 679 anyRect.Right:=anyRect.Left-cTextIndent; 680 anyRect.Left:=ARect.Left; 681 aFlags:=aFlags or DT_RIGHT or DT_RTLREADING; 682 end; 683 anyRect.Top:=(ARect.Top+ARect.Bottom-FTextHeight) div 2; 684 anyRect.Bottom:=anyRect.Top+FTextHeight; 685 DrawText(Canvas.Handle, PChar(Items[Index]), Length(Items[Index]), anyRect, aFlags); 686end; 687 688procedure TCustomCheckCombo.DropDown; 689{$IF DEFINED(LCLWin32) or DEFINED(LCLWin64)} 690{$ELSE} 691var aCursorPos: TPoint; 692 aRect: TRect; 693{$ENDIF} 694begin 695 {$IF DEFINED(LCLWin32) or DEFINED(LCLWin64)} 696 FRejectDropDown:=False; 697 {$ELSE} 698 aCursorPos:=ScreenToControl(Mouse.CursorPos); 699 aRect:=Rect(FHiLiteLeft, 0, FHiLiteRight, Height); 700 FRejectDropDown:=PtInRect(aRect, aCursorPos); 701 {$ENDIF} 702 FDropped:=True; 703 if not FRejectDropDown then 704 begin 705 inherited DropDown; 706 FRejectToggleOnSelect:=False; 707 end else 708 if (ItemIndex>=0) and ItemEnabled[ItemIndex] then Toggle(ItemIndex); 709end; 710 711procedure TCustomCheckCombo.FontChanged(Sender: TObject); 712begin 713 FNeedMeasure:=True; 714 inherited FontChanged(Sender); 715end; 716 717procedure TCustomCheckCombo.InitializeWnd; 718begin 719 InitItemStates; 720 inherited InitializeWnd; 721 CheckItemStates; 722 FRightToLeft:=IsRightToLeft; 723end; 724 725procedure TCustomCheckCombo.InitItemStates; 726var i: Integer; 727 pItemState: TCheckComboItemState; 728begin 729 for i:=0 to Items.Count-1 do 730 if Items.Objects[i]=nil then begin 731 pItemState:=TCheckComboItemState.Create; 732 pItemState.Enabled:=True; 733 pItemState.State:=cbUnchecked; 734 pItemState.Data:=nil; 735 Items.Objects[i]:=pItemState; 736 end else if not (Items.Objects[i] is TCheckComboItemState) then 737 raise Exception.Create(DbgSName(Self)+': Item '+IntToStr(i)+' is not a TCheckComboItemState'); 738end; 739 740procedure TCustomCheckCombo.CheckItemStates; 741var 742 i: Integer; 743begin 744 for i:=0 to Items.Count-1 do 745 if not (Items.Objects[i] is TCheckComboItemState) then 746 raise Exception.Create(DbgSName(Self)+': Item '+IntToStr(i)+' is not a TCheckComboItemState'); 747end; 748 749procedure TCustomCheckCombo.QueueCheckItemStates; 750begin 751 Application.QueueAsyncCall(@AsyncCheckItemStates,0); 752end; 753 754procedure TCustomCheckCombo.KeyDown(var Key: Word; Shift: TShiftState); 755begin 756 case Key of 757 VK_RETURN: 758 if FDropped then 759 if (ItemIndex=FHilightedIndex) and ItemEnabled[ItemIndex] then Toggle(ItemIndex); 760 VK_SPACE: 761 if DroppedDown then 762 if (ItemIndex>=0) and ItemEnabled[ItemIndex] then 763 begin 764 if ItemIndex<>FHilightedIndex then 765 begin 766 ItemIndex:=FHilightedIndex; 767 inherited Select; 768 end; 769 Toggle(ItemIndex); 770 DroppedDown:=False; 771 end; 772 end; 773 inherited KeyDown(Key, Shift); 774end; 775 776procedure TCustomCheckCombo.Loaded; 777begin 778 inherited Loaded; 779 InitItemStates; 780end; 781 782procedure TCustomCheckCombo.MouseLeave; 783begin 784 FCheckHighlight:=False; 785 inherited MouseLeave; 786end; 787 788procedure TCustomCheckCombo.MouseMove(Shift: TShiftState; X, Y: Integer); 789var aHighlight: Boolean; 790begin 791 inherited MouseMove(Shift, X, Y); 792 aHighlight:=((X>FHiLiteLeft) and (X<FHiLiteRight)); 793 if aHighlight<>FCheckHighlight then 794 begin 795 FCheckHighlight:=aHighlight; 796 Invalidate; 797 end; 798end; 799 800procedure TCustomCheckCombo.Select; 801begin 802 inherited Select; 803 {$IF DEFINED(LCLWin32) or DEFINED(LCLWin64)} 804 if DroppedDown then FRejectToggleOnSelect:=True; 805 {$ENDIF} 806 if not FRejectToggleOnSelect then 807 begin 808 if (ItemIndex >= 0) and ItemEnabled[ItemIndex] then Toggle(ItemIndex); 809 FRejectToggleOnSelect:=True; 810 end; 811 FDropped:=False; 812end; 813 814procedure TCustomCheckCombo.SetItemHeight(const AValue: Integer); 815begin 816 inherited SetItemHeight(AValue); 817 FNeedMeasure:=True; 818end; 819 820procedure TCustomCheckCombo.SetItems(const Value: TStrings); 821begin 822 ClearItemStates; 823 inherited SetItems(Value); 824 InitItemStates; 825end; 826 827procedure TCustomCheckCombo.Toggle(AIndex: Integer); 828const caNewStateMap: array [TCheckBoxState, Boolean] of TCheckBoxState = 829 { False (AllowGrayed) True } 830 ((cbChecked, cbGrayed), { cbUnchecked } 831 (cbUnChecked, cbUnChecked), { cbChecked } 832 (cbChecked, cbChecked)); { cbGrayed } 833begin 834 State[AIndex]:=caNewStateMap[State[AIndex], AllowGrayed]; 835end; 836 837{ TCustomCheckCombo.Getters and Setters } 838 839function TCustomCheckCombo.GetChecked(AIndex: Integer): Boolean; 840begin 841 Result:=(TCheckComboItemState(Items.Objects[AIndex]).State=cbChecked); 842end; 843 844procedure TCustomCheckCombo.AsyncCheckItemStates(Data: PtrInt); 845begin 846 CheckItemStates; 847end; 848 849function TCustomCheckCombo.GetCount: Integer; 850begin 851 Result:=Items.Count; 852end; 853 854function TCustomCheckCombo.GetItemEnabled(AIndex: Integer): Boolean; 855begin 856 Result:=TCheckComboItemState(Items.Objects[AIndex]).Enabled; 857end; 858 859function TCustomCheckCombo.GetObject(AIndex: Integer): TObject; 860begin 861 Result:=TCheckComboItemState(Items.Objects[AIndex]).Data; 862end; 863 864function TCustomCheckCombo.GetState(AIndex: Integer): TCheckBoxState; 865begin 866 Result:=TCheckComboItemState(Items.Objects[AIndex]).State; 867end; 868 869procedure TCustomCheckCombo.SetChecked(AIndex: Integer; AValue: Boolean); 870begin 871 if AValue=(TCheckComboItemState(Items.Objects[AIndex]).State=cbChecked) then exit; 872 if AValue 873 then TCheckComboItemState(Items.Objects[AIndex]).State:=cbChecked 874 else TCheckComboItemState(Items.Objects[AIndex]).State:=cbUnchecked; 875 if Assigned(FOnItemChange) then 876 FOnItemChange(Self, AIndex); 877 if AIndex=ItemIndex then 878 Invalidate; 879end; 880 881procedure TCustomCheckCombo.SetItemEnabled(AIndex: Integer; AValue: Boolean); 882begin 883 if TCheckComboItemState(Items.Objects[AIndex]).Enabled=AValue then exit; 884 TCheckComboItemState(Items.Objects[AIndex]).Enabled:=AValue; 885 if AIndex=ItemIndex then 886 Invalidate; 887end; 888 889procedure TCustomCheckCombo.SetObject(AIndex: Integer; AValue: TObject); 890begin 891 TCheckComboItemState(Items.Objects[AIndex]).Data:=AValue; 892end; 893 894procedure TCustomCheckCombo.SetState(AIndex: Integer; AValue: TCheckBoxState); 895begin 896 if TCheckComboItemState(Items.Objects[AIndex]).State=AValue then exit; 897 TCheckComboItemState(Items.Objects[AIndex]).State:=AValue; 898 if Assigned(FOnItemChange) then 899 FOnItemChange(self, AIndex); 900 if AIndex=ItemIndex then 901 Invalidate; 902end; 903 904 905