1 // =================================
   2 // Copyright (c) 2024 Seppo Laakko
   3 // Distributed under the MIT license
   4 // =================================
   5 
   6 using System;
   7 using System.Collections;
   8 
   9 namespace System.Windows
  10 {
  11     public class ListViewItemEventArgs
  12     {
  13         public ListViewItemEventArgs(ListView* view_ListViewItem* item_) : view(view_)item(item_)location()control(false)errorId(0)
  14         {
  15         }
  16         public ListView* view;
  17         public ListViewItem* item;
  18         public Point location;
  19         public bool control;
  20         public int errorId;
  21     }
  22 
  23     public class ListViewColumnEventArgs
  24     {
  25         public ListViewColumnEventArgs(ListView* view_ListViewColumn* column_) : view(view_)column(column_)
  26         {
  27         }
  28         public ListView* view;
  29         public ListViewColumn* column;
  30     }
  31 
  32     public class delegate void ListViewItemEventHandler(ListViewItemEventArgs& args);
  33     public class delegate void ListViewColumnEventHandler(ListViewColumnEventArgs& args);
  34 
  35     public Color DefaultListViewBackgroundColor()
  36     {
  37         return Color.White();
  38     }
  39 
  40     public string DefaultListViewFontFamilyName()
  41     {
  42         return "Segoe UI";
  43     }
  44 
  45     public float DefaultListViewFontSize()
  46     {
  47         return 9.000000f;
  48     }
  49 
  50     public Color DefaultListViewColumnTextColor()
  51     {
  52         return Color(76u96u122u);
  53     }
  54 
  55     public Color DefaultListViewItemTextColor()
  56     {
  57         return Color.Black();
  58     }
  59 
  60     public Color DefaultListViewDisabledItemTextColor()
  61     {
  62         return Color(201u201u201u);
  63     }
  64 
  65     public Color DefaultListViewColumnDividerColor()
  66     {
  67         return Color(229u229u229u);
  68     }
  69 
  70     public Color DefaultListViewItemSelectedColor()
  71     {
  72         return Color(204u232u255u);
  73     }
  74 
  75     public Padding DefaultListViewColumnHeaderPadding()
  76     {
  77         return Padding(4044);
  78     }
  79 
  80     public Padding DefaultListViewItemPadding()
  81     {
  82         return Padding(4040);
  83     }
  84 
  85     public Padding DefaultListViewItemColumnPadding()
  86     {
  87         return Padding(4040);
  88     }
  89 
  90     public Padding DefaultListViewColumnDividerPadding()
  91     {
  92         return Padding(1010);
  93     }
  94 
  95     public Padding DefaultListViewImagePadding()
  96     {
  97         return Padding(2222);
  98     }
  99 
 100     public ControlCreateParams& ListViewControlCreateParams(ControlCreateParams& controlCreateParams)
 101     {
 102         return controlCreateParams.SetWindowClassName("System.Windows.ListView").SetWindowClassStyle(DoubleClickWindowClassStyle()).
 103             SetWindowClassBackgroundColor(SystemColor.COLOR_WINDOW).SetBackgroundColor(DefaultListViewBackgroundColor());
 104     }
 105 
 106     public class ListViewCreateParams
 107     {
 108         public ListViewCreateParams(ControlCreateParams& controlCreateParams_) : 
 109             controlCreateParams(controlCreateParams_)
 110             allowMultiselect(false)
 111             fontFamilyName(DefaultListViewFontFamilyName())
 112             fontSize(DefaultListViewFontSize())
 113             columnTextColor(DefaultListViewColumnTextColor())
 114             itemTextColor(DefaultListViewItemTextColor())
 115             disabledItemTextColor(DefaultListViewDisabledItemTextColor())
 116             columnDividerColor(DefaultListViewColumnDividerColor())
 117             itemSelectedColor(DefaultListViewItemSelectedColor())
 118             columnHeaderPadding(DefaultListViewColumnHeaderPadding())
 119             itemPadding(DefaultListViewItemPadding())
 120             itemColumnPadding(DefaultListViewItemColumnPadding())
 121             columnDividerPadding(DefaultListViewColumnDividerPadding())
 122             imagePadding(DefaultListViewImagePadding())
 123         {
 124         }
 125         public ListViewCreateParams& Defaults()
 126         {
 127             return *this;
 128         }
 129         public ListViewCreateParams& SetAllowMultiselect(bool allowMultiselect_)
 130         {
 131             allowMultiselect = allowMultiselect_;
 132             return *this;
 133         }
 134         public ListViewCreateParams& SetFontFamilyName(const string& fontFamilyName_)
 135         {
 136             fontFamilyName = fontFamilyName_;
 137             return *this;
 138         }
 139         public ListViewCreateParams& SetFontSize(float fontSize_)
 140         {
 141             fontSize = fontSize_;
 142             return *this;
 143         }
 144         public ListViewCreateParams& SetColumnTextColor(const Color& columnTextColor_)
 145         {
 146             columnTextColor = columnTextColor_;
 147             return *this;
 148         }
 149         public ListViewCreateParams& SetItemTextColor(const Color& itemTextColor_)
 150         {
 151             itemTextColor = itemTextColor_;
 152             return *this;
 153         }
 154         public ListViewCreateParams& SetDisabledItemTextColor(const Color& disabledItemTextColor_)
 155         {
 156             disabledItemTextColor = disabledItemTextColor_;
 157             return *this;
 158         }
 159         public ListViewCreateParams& SetColumnDividerColor(const Color& columnDividerColor_)
 160         {
 161             columnDividerColor = columnDividerColor_;
 162             return *this;
 163         }
 164         public ListViewCreateParams& SetItemSelectedColor(const Color& itemSelectedColor_)
 165         {
 166             itemSelectedColor = itemSelectedColor_;
 167             return *this;
 168         }
 169         public ListViewCreateParams& SetColumnHeaderPadding(const Padding& columnHeaderPadding_)
 170         {
 171             columnHeaderPadding = columnHeaderPadding_;
 172             return *this;
 173         }
 174         public ListViewCreateParams& SetItemPadding(const Padding& itemPadding_)
 175         {
 176             itemPadding = itemPadding_;
 177             return *this;
 178         }
 179         public ListViewCreateParams& SetItemColumnPadding(const Padding& itemColumnPadding_)
 180         {
 181             itemColumnPadding = itemColumnPadding_;
 182             return *this;
 183         }
 184         public ListViewCreateParams& SetColumnDividerPadding(const Padding& columnDividerPadding_)
 185         {
 186             columnDividerPadding = columnDividerPadding_;
 187             return *this;
 188         }
 189         public ListViewCreateParams& SetImagePadding(const Padding& imagePadding_)
 190         {
 191             imagePadding = imagePadding_;
 192             return *this;
 193         }
 194         public ControlCreateParams& controlCreateParams;
 195         public bool allowMultiselect;
 196         public string fontFamilyName;
 197         public float fontSize;
 198         public Color columnTextColor;
 199         public Color itemTextColor;
 200         public Color disabledItemTextColor;
 201         public Color columnDividerColor;
 202         public Color itemSelectedColor;
 203         public Padding columnHeaderPadding;
 204         public Padding itemPadding;
 205         public Padding itemColumnPadding;
 206         public Padding columnDividerPadding;
 207         public Padding imagePadding;
 208     }
 209 
 210     public class ListView : Control
 211     {
 212         public enum Flags : int
 213         {
 214             none = 0measured = 1 << 0allowMultiselect = 1 << 1
 215         }
 216         public ListView(ListViewCreateParams& createParams) : 
 217             base(createParams.controlCreateParams)
 218             flags(Flags.none)imageList(null)
 219             columnHeaderTextBrush(createParams.columnTextColor)
 220             itemTextBrush(createParams.itemTextColor)
 221             disabledItemTextBrush(createParams.disabledItemTextColor)
 222             itemSelectedBrush(createParams.itemSelectedColor)
 223             columnDividerPen(createParams.columnDividerColor)
 224             stringFormat()
 225             columnHeaderPadding(createParams.columnHeaderPadding)
 226             itemPadding(createParams.itemPadding)
 227             itemColumnPadding(createParams.itemColumnPadding)
 228             columnDividerPadding(createParams.columnDividerPadding)
 229             imagePadding(createParams.imagePadding)
 230             charWidth(0)
 231             charHeight(0)
 232             columnDividerWidth(1)
 233             ellipsisWidth(0)
 234             mouseDownItem(null)
 235             mouseEnterItem(null)
 236             selectedItem(null)
 237             mouseDownColumnDivider(null)
 238             data(null)
 239             arrowCursor()
 240             columnSizeCursor(null)
 241         {
 242             auto cursorResult = LoadStandardCursor(StandardCursorId.IDC_ARROW);
 243             if (cursorResult.Error())
 244             {
 245                 SetErrorId(cursorResult.GetErrorId());
 246                 return;
 247             }
 248             arrowCursor = Rvalue(cursorResult.Value());
 249             auto columnSizeCursorResult = Application.GetResourceManager().GetCursor("column.size.system.windows.cursor");
 250             if (columnSizeCursorResult.Error())
 251             {
 252                 SetErrorId(columnSizeCursorResult.GetErrorId());
 253                 return;
 254             }
 255             columnSizeCursor = Rvalue(columnSizeCursorResult.Value());
 256             if (createParams.allowMultiselect)
 257             {
 258                 SetFlag(Flags.allowMultiselect);
 259             }
 260             SetFont(Font(FontFamily(createParams.fontFamilyName)createParams.fontSize));
 261         }
 262         public inline ImageList* GetImageList() const
 263         {
 264             return imageList;
 265         }
 266         public void SetImageList(ImageList* imageList_)
 267         {
 268             imageList = imageList_;
 269         }
 270         public const StringFormat& GetStringFormat() const
 271         {
 272             return stringFormat;
 273         }
 274         public const Brush& ColumnHeaderTextBrush() const
 275         {
 276             return columnHeaderTextBrush;
 277         }
 278         public const Brush* ItemTextBrush() const
 279         {
 280             return &itemTextBrush;
 281         }
 282         public const Brush* DisabledItemTextBrush() const
 283         {
 284             return &disabledItemTextBrush;
 285         }
 286         public const Brush& ItemSelectedBrush() const
 287         {
 288             return itemSelectedBrush;
 289         }
 290         public const Pen& ColumnDividerPen() const
 291         {
 292             return columnDividerPen;
 293         }
 294         public const Padding& ColumnHeaderPadding() const
 295         {
 296             return columnHeaderPadding;
 297         }
 298         public const Padding& ItemPadding() const
 299         {
 300             return itemPadding;
 301         }
 302         public const Padding& ItemColumnPadding() const
 303         {
 304             return itemColumnPadding;
 305         }
 306         public const Padding& ColumnDividerPadding() const
 307         {
 308             return columnDividerPadding;
 309         }
 310         public const Padding& ImagePadding() const
 311         {
 312             return imagePadding;
 313         }
 314         public inline bool GetFlag(Flags flag) const
 315         {
 316             return (flags & flag) != Flags.none;
 317         }
 318         public void SetFlag(Flags flag)
 319         {
 320             flags = cast<Flags>(flags | flag);
 321         }
 322         public void ResetFlag(Flags flag)
 323         {
 324             flags = cast<Flags>(flags & ~flag);
 325         }
 326         public int ColumnCount() const
 327         {
 328             return cast<int>(columns.Count());
 329         }
 330         public void AddColumn(const string& nameint width)
 331         {
 332             ListViewColumn* column = new ListViewColumn(thisnamewidth);
 333             columns.Add(UniquePtr<ListViewColumn>(column));
 334             columnDividers.Add(UniquePtr<ListViewColumnDivider>(new ListViewColumnDivider(thiscolumn)));
 335         }
 336         [nodiscard]
 337         public Result<ListViewColumn*> GetColumn(int columnIndex) const
 338         {
 339             if (columnIndex < 0 || columnIndex >= ColumnCount())
 340             {
 341                 int errorId = AllocateError("invalid column index");
 342                 return Result<ListViewColumn*>(ErrorId(errorId));
 343             }
 344             return Result<ListViewColumn*>(columns[columnIndex].Get());
 345         }
 346         public int ItemCount() const
 347         {
 348             return cast<int>(items.Count());
 349         }
 350         public ListViewItem* AddItem()
 351         {
 352             ListViewItem* item = new ListViewItem(this);
 353             items.Add(UniquePtr<ListViewItem>(item));
 354             return item;
 355         }
 356         [nodiscard]
 357         public Result<ListViewItem*> GetItem(int itemIndex) const
 358         {
 359             if (itemIndex < 0 || itemIndex >= ItemCount())
 360             {
 361                 int errorId = AllocateError("invalid item index");
 362                 return Result<ListViewItem*>(ErrorId(errorId));
 363             }
 364             return Result<ListViewItem*>(items[itemIndex].Get());
 365         }
 366         public inline ListViewItem* SelectedItem() const
 367         {
 368             return selectedItem;
 369         }
 370         [nodiscard]
 371         public Result<bool> SetSelectedItem(ListViewItem* selectedItem_)
 372         {
 373             if (GetFlag(Flags.allowMultiselect))
 374             {
 375                 auto result = ResetSelectedItems();
 376                 if (result.Error()) return result;
 377             }
 378             if (selectedItem != selectedItem_)
 379             {
 380                 if (selectedItem != null)
 381                 {
 382                     auto result = selectedItem->ResetSelected();
 383                     if (result.Error()) return result;
 384                 }
 385                 selectedItem = selectedItem_;
 386                 if (selectedItem != null)
 387                 {
 388                     auto result = selectedItem->SetSelected();
 389                     if (result.Error()) return result;
 390                 }
 391             }
 392             return Result<bool>(true);
 393         }
 394         public List<ListViewItem*> GetSelectedItems() const
 395         {
 396             List<ListViewItem*> result;
 397             for (const UniquePtr<ListViewItem>& item : items)
 398             {
 399                 if (item->IsSelected())
 400                 {
 401                     result.Add(item.Get());
 402                 }
 403             }
 404             return result;
 405         }
 406         [nodiscard]
 407         public Result<bool> ResetSelectedItems()
 408         {
 409             for (const UniquePtr<ListViewItem>& item : items)
 410             {
 411                 if (item.Get() != SelectedItem())
 412                 {
 413                     if (item->IsSelected())
 414                     {
 415                         auto result = item->ResetSelected();
 416                         if (result.Error()) return result;
 417                     }
 418                 }
 419             }
 420             return Result<bool>(true);
 421         }
 422         public ListViewItem* ItemAt(const Point& location) const
 423         {
 424             for (const UniquePtr<ListViewItem>& item : items)
 425             {
 426                 Rect rect(item->Location()item->GetSize());
 427                 if (rect.Contains(location))
 428                 {
 429                     return item.Get();
 430                 }
 431             }
 432             return null;
 433         }
 434         public ListViewColumnDivider* ColumnDividerAt(const Point& location) const
 435         {
 436             for (const UniquePtr<ListViewColumnDivider>& columnDivider : columnDividers)
 437             {
 438                 Rect rect(columnDivider->Location()columnDivider->GetSize());
 439                 if (rect.Contains(location))
 440                 {
 441                     return columnDivider.Get();
 442                 }
 443             }
 444             return null;
 445         }
 446         public inline float TextHeight() const
 447         {
 448             return charHeight;
 449         }
 450         public inline float ColumnDividerWidth() const
 451         {
 452             return columnDividerWidth;
 453         }
 454         public float EllipsisWidth() const
 455         {
 456             return ellipsisWidth;
 457         }
 458         public void FireColumnWidthChanged(ListViewColumn* column)
 459         {
 460             ListViewColumnEventArgs args(thiscolumn);
 461             OnColumnWidthChanged(args);
 462         }
 463         public void SetData(void* data_)
 464         {
 465             data = data_;
 466         }
 467         public inline void* Data() const
 468         {
 469             return data;
 470         }
 471         public Event<ListViewItemEventHandlerListViewItemEventArgs>& ItemClickEvent()
 472         {
 473             return itemClickEvent;
 474         }
 475         public Event<ListViewItemEventHandlerListViewItemEventArgs>& ItemRightClickEvent()
 476         {
 477             return itemRightClickEvent;
 478         }
 479         public Event<ListViewItemEventHandlerListViewItemEventArgs>& ItemDoubleClickEvent()
 480         {
 481             return itemDoubleClickEvent;
 482         }
 483         public Event<ListViewItemEventHandlerListViewItemEventArgs>& ItemEnterEvent()
 484         {
 485             return itemEnterEvent;
 486         }
 487         public Event<ListViewItemEventHandlerListViewItemEventArgs>& ItemLeaveEvent()
 488         {
 489             return itemLeaveEvent;
 490         }
 491         public Event<ListViewColumnEventHandlerListViewColumnEventArgs>& ColumnWidthChangedEvent()
 492         {
 493             return columnWidthChangedEvent;
 494         }
 495         [nodiscard]
 496         protected override Result<bool> OnSizeChanged(SizeChangedEventArgs& args)
 497         {
 498             auto result = base->OnSizeChanged(args);
 499             if (result.Error()) return result;
 500             SetContentLocation(Point(00));
 501             return Result<bool>(true);
 502         }
 503         [nodiscard]
 504         protected override Result<bool> OnPaint(PaintEventArgs& args)
 505         {
 506             if (!GetFlag(Flags.measured))
 507             {
 508                 SetFlag(Flags.measured);
 509                 auto result = Measure(args.graphics);
 510                 if (result.Error())
 511                 {
 512                     return Result<bool>(ErrorId(result.GetErrorId()));
 513                 }
 514             }
 515             Size contentSize(00);
 516             auto result = args.graphics.Clear(BackgroundColor());
 517             if (result.Error()) return result;
 518             PointF origin;
 519             auto measureResult = MeasureItems(args.graphicscontentSize);
 520             if (measureResult.Error())
 521             {
 522                 return Result<bool>(ErrorId(measureResult.GetErrorId()));
 523             }
 524             result = DrawColumnHeader(args.graphicsorigin);
 525             if (result.Error()) return result;
 526             result = DrawItems(args.graphicsorigin);
 527             if (result.Error()) return result;
 528             result = SetContentSize(contentSize);
 529             if (result.Error()) return result;
 530             return base->OnPaint(args);
 531         }
 532         [nodiscard]
 533         protected override Result<bool> OnMouseDown(MouseEventArgs& args)
 534         {
 535             mouseDownItem = null;
 536             ListViewItem* item = ItemAt(args.location);
 537             if (item != null)
 538             {
 539                 mouseDownItem = item;
 540             }
 541             else
 542             {
 543                 auto result = SetSelectedItem(null);
 544                 if (result.Error()) return result;
 545             }
 546             if (args.buttons == MouseButtons.lbutton)
 547             {
 548                 ListViewColumnDivider* columnDivider = ColumnDividerAt(args.location);
 549                 if (columnDivider != null)
 550                 {
 551                     columnDivider->OnLButtonDown(args.location);
 552                     mouseDownColumnDivider = columnDivider;
 553                 }
 554             }
 555             return Result<bool>(true);
 556         }
 557         [nodiscard]
 558         protected override Result<bool> OnMouseUp(MouseEventArgs& args)
 559         {
 560             ListViewItem* item = ItemAt(args.location);
 561             if (item == mouseDownItem)
 562             {
 563                 ListViewItemEventArgs itemArgs(thisitem);
 564                 if ((args.buttons & MouseButtons.lbutton) != MouseButtons.none)
 565                 {
 566                     if (GetFlag(Flags.allowMultiselect))
 567                     {
 568                         if ((args.buttons & MouseButtons.control) != MouseButtons.none)
 569                         {
 570                             itemArgs.control = true;
 571                         }
 572                     }
 573                     itemClickEvent.Fire(itemArgs);
 574                 }
 575                 else if (args.buttons == MouseButtons.rbutton)
 576                 {
 577                     itemArgs.location = args.location;
 578                     itemRightClickEvent.Fire(itemArgs);
 579                 }
 580                 if (itemArgs.errorId != 0)
 581                 {
 582                     return Result<bool>(ErrorId(itemArgs.errorId));
 583                 }
 584             }
 585             mouseDownItem = null;
 586             if (args.buttons == MouseButtons.lbutton)
 587             {
 588                 if (mouseDownColumnDivider != null)
 589                 {
 590                     mouseDownColumnDivider->OnLButtonUp();
 591                     mouseDownColumnDivider = null;
 592                 }
 593             }
 594             return Result<bool>(true);
 595         }
 596         [nodiscard]
 597         protected override Result<bool> OnMouseDoubleClick(MouseEventArgs& args)
 598         {
 599             ListViewItem* item = ItemAt(args.location);
 600             if (item != null)
 601             {
 602                 ListViewItemEventArgs args(thisitem);
 603                 itemDoubleClickEvent.Fire(args);
 604             }
 605             mouseDownItem = null;
 606             return Result<bool>(true);
 607         }
 608         [nodiscard]
 609         protected override Result<bool> OnMouseEnter(EnterLeaveEventArgs& args)
 610         {
 611             mouseEnterItem = null;
 612             return Result<bool>(true);
 613         }
 614         protected override Result<bool> OnMouseLeave(EnterLeaveEventArgs& args)
 615         {
 616             if (mouseEnterItem != null)
 617             {
 618                 ListViewItemEventArgs leaveItemArgs(thismouseEnterItem);
 619                 itemLeaveEvent.Fire(leaveItemArgs);
 620                 mouseEnterItem = null;
 621             }
 622             return Result<bool>(true);
 623         }
 624         [nodiscard]
 625         protected override Result<bool> OnMouseMove(MouseEventArgs& args)
 626         {
 627             if (mouseEnterItem == null)
 628             {
 629                 mouseEnterItem = ItemAt(args.location);
 630                 if (mouseEnterItem != null)
 631                 {
 632                     ListViewItemEventArgs itemArgs(thismouseEnterItem);
 633                     itemEnterEvent.Fire(itemArgs);
 634                 }
 635             }
 636             else
 637             {
 638                 ListViewItem* item = ItemAt(args.location);
 639                 if (item != mouseEnterItem)
 640                 {
 641                     ListViewItemEventArgs leaveItemArgs(thismouseEnterItem);
 642                     itemLeaveEvent.Fire(leaveItemArgs);
 643                     mouseEnterItem = item;
 644                     if (mouseEnterItem != null)
 645                     {
 646                         ListViewItemEventArgs enterItemArgs(thismouseEnterItem);
 647                         itemEnterEvent.Fire(enterItemArgs);
 648                     }
 649                 }
 650             }
 651             if (args.buttons == MouseButtons.lbutton)
 652             {
 653                 if (mouseDownColumnDivider != null)
 654                 {
 655                     if (mouseDownColumnDivider->HasMouseCapture())
 656                     {
 657                         auto result = mouseDownColumnDivider->OnMouseMove(args.location);
 658                         if (result.Error()) return result;
 659                     }
 660                 }
 661             }
 662             return Result<bool>(true);
 663         }
 664         protected virtual void OnColumnWidthChanged(ListViewColumnEventArgs& args)
 665         {
 666             columnWidthChangedEvent.Fire(args);
 667         }
 668         protected override Result<bool> SetCursor()
 669         {
 670             Point pt;
 671             auto cpResult = GetCursorPos(pt.xpt.y);
 672             if (cpResult.Error()) return cpResult;
 673             auto result = ScreenToClient(pt);
 674             if (result.Error())
 675             {
 676                 return Result<bool>(ErrorId(result.GetErrorId()));
 677             }
 678             Point cursorPos = result.Value();
 679             bool cursorSet = false;
 680             for (const UniquePtr<ListViewColumnDivider>& columnDivider : columnDividers)
 681             {
 682                 Rect r(columnDivider->Location()columnDivider->GetSize());
 683                 if (r.Contains(cursorPos))
 684                 {
 685                     System.Windows.SetCursor(columnSizeCursor);
 686                     cursorSet = true;
 687                     break;
 688                 }
 689             }
 690             if (!cursorSet)
 691             {
 692                 System.Windows.SetCursor(&arrowCursor);
 693             }
 694             return Result<bool>(true);
 695         }
 696         private Result<bool> Measure(Graphics& graphics)
 697         {
 698             auto measureResult = graphics.MeasureStringRectF("This is test string"GetFont()PointF(00)stringFormat);
 699             if (measureResult.Error())
 700             {
 701                 return Result<bool>(ErrorId(measureResult.GetErrorId()));
 702             }
 703             RectF charRect = measureResult.Value();
 704             charHeight = charRect.size.h;
 705             charWidth = charRect.size.w;
 706             SetScrollUnits(cast<int>(charHeight + 0.500000f)cast<int>(2 * (charWidth + 0.500000f)));
 707             measureResult = graphics.MeasureStringRectF("..."GetFont()PointF(00)stringFormat);
 708             if (measureResult.Error())
 709             {
 710                 return Result<bool>(ErrorId(measureResult.GetErrorId()));
 711             }
 712             RectF ellipsisRect = measureResult.Value();
 713             ellipsisWidth = ellipsisRect.size.w;
 714             return Result<bool>(true);
 715         }
 716         private Result<bool> MeasureItems(Graphics& graphicsSize& contentSize)
 717         {
 718             int maxWidth = 0;
 719             Point loc(itemPadding.leftcast<int>(charHeight + 0.500000f + columnHeaderPadding.Vertical()));
 720             for (const UniquePtr<ListViewItem>& item : items)
 721             {
 722                 item->SetLocation(loc);
 723                 auto measureResult = item->Measure(graphics);
 724                 if (measureResult.Error())
 725                 {
 726                     return Result<bool>(ErrorId(measureResult.GetErrorId()));
 727                 }
 728                 Size itemSize = item->GetSize();
 729                 loc.y = loc.y + itemSize.h;
 730                 maxWidth = Max(maxWidthitemSize.w);
 731             }
 732             contentSize.w = Max(contentSize.wmaxWidth);
 733             contentSize.h = Max(contentSize.hloc.y);
 734             return Result<bool>(true);
 735         }
 736         private Result<bool> DrawColumnHeader(Graphics& graphicsPointF& origin)
 737         {
 738             PointF headerOrigin = origin;
 739             headerOrigin.x = headerOrigin.x + columnHeaderPadding.left;
 740             headerOrigin.y = headerOrigin.y + columnHeaderPadding.top;
 741             long n = columns.Count();
 742             for (long i = 0; i < n; ++i;)
 743             {
 744                 ListViewColumn* column = columns[i].Get();
 745                 auto drawResult = column->Draw(graphicsheaderOrigin);
 746                 if (drawResult.Error())
 747                 {
 748                     return Result<bool>(ErrorId(drawResult.GetErrorId()));
 749                 }
 750                 headerOrigin.x = headerOrigin.x + column->Width() + columnHeaderPadding.Horizontal();
 751                 ListViewColumnDivider* divider = columnDividers[i].Get();
 752                 headerOrigin.x = headerOrigin.x + columnDividerPadding.left;
 753                 drawResult = divider->Draw(graphicsheaderOrigin);
 754                 if (drawResult.Error())
 755                 {
 756                     return Result<bool>(ErrorId(drawResult.GetErrorId()));
 757                 }
 758                 headerOrigin.x = headerOrigin.x + columnDividerWidth + columnDividerPadding.right;
 759             }
 760             origin.y = origin.y + charHeight + columnHeaderPadding.Vertical();
 761             return Result<bool>(true);
 762         }
 763         private Result<bool> DrawItems(Graphics& graphicsPointF& origin)
 764         {
 765             for (const UniquePtr<ListViewItem>& item : items)
 766             {
 767                 PointF itemOrigin = origin;
 768                 itemOrigin.y = itemOrigin.y + itemPadding.top;
 769                 itemOrigin.x = itemOrigin.x + itemPadding.left;
 770                 auto drawResult = item->Draw(graphicsitemOrigin);
 771                 if (drawResult.Error())
 772                 {
 773                     return Result<bool>(ErrorId(drawResult.GetErrorId()));
 774                 }
 775                 Size sz = item->GetSize();
 776                 origin.y = origin.y + sz.h;
 777             }
 778             return Result<bool>(true);
 779         }
 780         private Flags flags;
 781         private ImageList* imageList;
 782         private SolidBrush columnHeaderTextBrush;
 783         private SolidBrush itemTextBrush;
 784         private SolidBrush disabledItemTextBrush;
 785         private SolidBrush itemSelectedBrush;
 786         private Pen columnDividerPen;
 787         private StringFormat stringFormat;
 788         private Padding columnHeaderPadding;
 789         private Padding itemPadding;
 790         private Padding itemColumnPadding;
 791         private Padding columnDividerPadding;
 792         private Padding imagePadding;
 793         private float charWidth;
 794         private float charHeight;
 795         private float columnDividerWidth;
 796         private float ellipsisWidth;
 797         private ListViewItem* mouseDownItem;
 798         private ListViewItem* mouseEnterItem;
 799         private ListViewItem* selectedItem;
 800         private ListViewColumnDivider* mouseDownColumnDivider;
 801         private void* data;
 802         private Cursor arrowCursor;
 803         private Cursor* columnSizeCursor;
 804         private List<UniquePtr<ListViewColumn>> columns;
 805         private List<UniquePtr<ListViewColumnDivider>> columnDividers;
 806         private List<UniquePtr<ListViewItem>> items;
 807         private Event<ListViewItemEventHandlerListViewItemEventArgs> itemClickEvent;
 808         private Event<ListViewItemEventHandlerListViewItemEventArgs> itemRightClickEvent;
 809         private Event<ListViewItemEventHandlerListViewItemEventArgs> itemDoubleClickEvent;
 810         private Event<ListViewItemEventHandlerListViewItemEventArgs> itemEnterEvent;
 811         private Event<ListViewItemEventHandlerListViewItemEventArgs> itemLeaveEvent;
 812         private Event<ListViewColumnEventHandlerListViewColumnEventArgs> columnWidthChangedEvent;
 813     }
 814 
 815     public class ListViewColumn
 816     {
 817         public ListViewColumn(ListView* view_const string& name_int width_) : 
 818             view(view_)name(name_)width(width_)minWidth(0)
 819         {
 820         }
 821         public inline const string& Name() const
 822         {
 823             return name;
 824         }
 825         public inline int Width() const
 826         {
 827             return width;
 828         }
 829         [nodiscard]
 830         public Result<bool> SetWidth(int width_)
 831         {
 832             if (width != width_)
 833             {
 834                 width = width_;
 835                 view->FireColumnWidthChanged(this);
 836                 auto result = view->Invalidate();
 837                 if (result.Error()) return result;
 838             }
 839             return Result<bool>(true);
 840         }
 841         public inline int MinWidth() const
 842         {
 843             return minWidth;
 844         }
 845         public void SetMinWidth(int minWidth_)
 846         {
 847             minWidth = minWidth_;
 848         }
 849         [nodiscard]
 850         public Result<bool> Draw(Graphics& graphicsconst PointF& origin)
 851         {
 852             auto drawResult = graphics.DrawString(nameview->GetFont()originview->ColumnHeaderTextBrush());
 853             return drawResult;
 854         }
 855         private ListView* view;
 856         private string name;
 857         private int width;
 858         private int minWidth;
 859     }
 860 
 861     public class ListViewColumnDivider
 862     {
 863         public ListViewColumnDivider(ListView* view_ListViewColumn* column_) : 
 864             view(view_)column(column_)location()startCapturePos()startColumnWidth(0)hasMouseCapture(false)
 865         {
 866         }
 867         public inline const Point& Location() const
 868         {
 869             return location;
 870         }
 871         public Size GetSize() const
 872         {
 873             return Size(cast<int>(view->ColumnDividerWidth() + 4 * view->ColumnDividerPadding().Horizontal() + 0.500000f)cast<int>(view->TextHeight() + 0.500000f));
 874         }
 875         public inline bool HasMouseCapture() const
 876         {
 877             return hasMouseCapture;
 878         }
 879         public void OnLButtonDown(const Point& pos)
 880         {
 881             WinSetCapture(view->Handle());
 882             startCapturePos = pos;
 883             startColumnWidth = column->Width();
 884             hasMouseCapture = true;
 885         }
 886         [nodiscard]
 887         public Result<bool> OnMouseMove(const Point& pos)
 888         {
 889             auto result = column->SetWidth(Max(column->MinWidth()startColumnWidth + pos.x - startCapturePos.x));
 890             if (result.Error()) return result;
 891             return Result<bool>(true);
 892         }
 893         public void OnLButtonUp()
 894         {
 895             WinReleaseCapture();
 896             hasMouseCapture = false;
 897         }
 898         [nodiscard]
 899         public Result<bool> Draw(Graphics& graphicsconst PointF& origin)
 900         {
 901             const Pen& pen = view->ColumnDividerPen();
 902             Size sz = GetSize();
 903             location = Point(cast<int>(origin.x - sz.w / 2)cast<int>(origin.y));
 904             PointF end = origin;
 905             end.y = end.y + view->TextHeight();
 906             auto drawResult = graphics.DrawLine(penoriginend);
 907             return drawResult;
 908         }
 909         private ListView* view;
 910         private ListViewColumn* column;
 911         private Point location;
 912         private Point startCapturePos;
 913         private int startColumnWidth;
 914         private bool hasMouseCapture;
 915     }
 916 
 917     public class ListViewItem
 918     {
 919         public enum Flags : int
 920         {
 921             none = 0disabled = 1 << 0selected = 1 << 1
 922         }
 923         public enum State : int
 924         {
 925             enableddisabled
 926         }
 927         public explicit ListViewItem(ListView* view_) : view(view_)flags(Flags.none)imageIndex(-1)disabledImageIndex(-1)data(null)
 928         {
 929         }
 930         [nodiscard]
 931         public Result<bool> SetColumnValue(int columnIndexconst string& columnValue)
 932         {
 933             if (columnIndex < 0)
 934             {
 935                 int errorId = AllocateError("invalid column index");
 936                 return Result<bool>(ErrorId(errorId));
 937             }
 938             while (columnIndex >= columnValues.Count())
 939             {
 940                 columnValues.Add(string());
 941             }
 942             columnValues[columnIndex] = columnValue;
 943             return Result<bool>(true);
 944         }
 945         [nodiscard]
 946         public Result<string> GetColumnValue(int columnIndex) const
 947         {
 948             if (columnIndex < 0)
 949             {
 950                 int errorId = AllocateError("invalid column index");
 951                 return Result<string>(ErrorId(errorId));
 952             }
 953             if (columnIndex >= columnValues.Count())
 954             {
 955                 return string();
 956             }
 957             return Result<string>(columnValues[columnIndex]);
 958         }
 959         [nodiscard]
 960         public Result<bool> Draw(Graphics& graphicsPointF& origin)
 961         {
 962             if (IsSelected())
 963             {
 964                 const Brush& selectedBrush = view->ItemSelectedBrush();
 965                 Rect rect(locationsize);
 966                 auto result = graphics.FillRectangle(selectedBrushrect);
 967                 if (result.Error())
 968                 {
 969                     return Result<bool>(ErrorId(result.GetErrorId()));
 970                 }
 971             }
 972             PointF itemOrigin = origin;
 973             int imageSpace = 0;
 974             auto result = DrawImage(graphicsitemOriginimageSpace);
 975             if (result.Error())
 976             {
 977                 return Result<bool>(ErrorId(result.GetErrorId()));
 978             }
 979             for (int index = 0; index < view->ColumnCount(); ++index;)
 980             {
 981                 int imgSpc = 0;
 982                 if (index == 0)
 983                 {
 984                     imgSpc = imageSpace;
 985                 }
 986                 imgSpc = imageSpace;
 987                 bool clipped = false;
 988                 Region prevClip;
 989                 if (index < textWidths.Count())
 990                 {
 991                     auto getColumnResult = view->GetColumn(index);
 992                     if (getColumnResult.Error())
 993                     {
 994                         return Result<bool>(ErrorId(getColumnResult.GetErrorId()));
 995                     }
 996                     ListViewColumn* column = getColumnResult.Value();
 997                     if (textWidths[index] > column->Width())
 998                     {
 999                         auto clipResult = graphics.GetClip();
1000                         if (clipResult.Error())
1001                         {
1002                             return Result<bool>(ErrorId(clipResult.GetErrorId()));
1003                         }
1004                         prevClip = clipResult.Value();
1005                         clipped = true;
1006                         auto columnResult = view->GetColumn(index);
1007                         if (columnResult.Error())
1008                         {
1009                             return Result<bool>(ErrorId(columnResult.GetErrorId()));
1010                         }
1011                         ListViewColumn* column = columnResult.Value();
1012                         Size itemSize(Size(cast<int>(column->Width() - view->EllipsisWidth() - imgSpc)cast<int>(view->TextHeight())));
1013                         Rect clip(Point(cast<int>(itemOrigin.x)cast<int>(itemOrigin.y))itemSize);
1014                         auto setClipResult = graphics.SetClip(clip);
1015                         if (setClipResult.Error())
1016                         {
1017                             return Result<bool>(ErrorId(setClipResult.GetErrorId()));
1018                         }
1019                     }
1020                 }
1021                 const Brush* brush = view->ItemTextBrush();
1022                 if (GetState() == State.disabled)
1023                 {
1024                     brush = view->DisabledItemTextBrush();
1025                 }
1026                 auto columnValueResult = GetColumnValue(index);
1027                 if (columnValueResult.Error())
1028                 {
1029                     return Result<bool>(ErrorId(columnValueResult.GetErrorId()));
1030                 }
1031                 string columnValue = Rvalue(columnValueResult.Value());
1032                 auto result = graphics.DrawString(columnValueview->GetFont()itemOrigin*brush);
1033                 if (result.Error())
1034                 {
1035                     return Result<bool>(ErrorId(result.GetErrorId()));
1036                 }
1037                 auto columnResult = view->GetColumn(index);
1038                 if (columnResult.Error())
1039                 {
1040                     return Result<bool>(ErrorId(columnResult.GetErrorId()));
1041                 }
1042                 ListViewColumn* column = columnResult.Value();
1043                 itemOrigin.x = itemOrigin.x + column->Width();
1044                 if (clipped)
1045                 {
1046                     auto setClipResult = graphics.SetClip(prevClip);
1047                     if (setClipResult.Error())
1048                     {
1049                         return Result<bool>(ErrorId(setClipResult.GetErrorId()));
1050                     }
1051                     PointF ellipsisOrigin(itemOrigin.x - view->EllipsisWidth() - imgSpcitemOrigin.y);
1052                     auto result = graphics.DrawString("..."view->GetFont()ellipsisOrigin*brush);
1053                     if (result.Error())
1054                     {
1055                         return Result<bool>(ErrorId(result.GetErrorId()));
1056                     }
1057                 }
1058                 itemOrigin.x = itemOrigin.x + view->ColumnDividerPadding().Horizontal() + view->ColumnDividerWidth() - imgSpc;
1059                 itemOrigin.x = itemOrigin.x + view->ItemColumnPadding().Horizontal();
1060             }
1061             return Result<bool>(true);
1062         }
1063         [nodiscard]
1064         public Result<bool> Measure(Graphics& graphics)
1065         {
1066             float width = 0;
1067             float height = 0;
1068             Bitmap* image = null;
1069             ImageList* imageList = view->GetImageList();
1070             if (imageList != null)
1071             {
1072                 if (GetState() == State.enabled)
1073                 {
1074                     image = imageList->GetImage(imageIndex);
1075                 }
1076                 else
1077                 {
1078                     image = imageList->GetImage(disabledImageIndex);
1079                 }
1080             }
1081             if (image != null)
1082             {
1083                 int imageWidth = cast<int>(image->GetWidth());
1084                 int imageHeight = cast<int>(image->GetHeight());
1085                 Padding padding = view->ImagePadding();
1086                 if (view->ColumnCount() > 0)
1087                 {
1088                     auto columnResult = view->GetColumn(0);
1089                     if (columnResult.Error())
1090                     {
1091                         return Result<bool>(ErrorId(columnResult.GetErrorId()));
1092                     }
1093                     ListViewColumn* firstColumn = columnResult.Value();
1094                     firstColumn->SetMinWidth(Max(firstColumn->MinWidth()cast<int>(imageWidth + padding.Horizontal() + view->EllipsisWidth() + 0.500000f)));
1095                 }
1096                 height = imageHeight + padding.Vertical();
1097             }
1098             for (int index = 0; index < view->ColumnCount(); ++index;)
1099             {
1100                 auto columnResult = view->GetColumn(index);
1101                 if (columnResult.Error())
1102                 {
1103                     return Result<bool>(ErrorId(columnResult.GetErrorId()));
1104                 }
1105                 ListViewColumn* column = columnResult.Value();
1106                 width = width + column->Width();
1107                 width = width + view->ColumnDividerWidth() + view->ColumnDividerPadding().Horizontal();
1108                 height = Max(heightview->TextHeight());
1109                 column->SetMinWidth(Max(column->MinWidth()cast<int>(view->EllipsisWidth() + 0.500000f)));
1110             }
1111             width = width + view->ItemPadding().Horizontal();
1112             size = Size(cast<int>(width + 0.500000f)cast<int>(height + 0.500000f));
1113             textWidths.Clear();
1114             for (const string& columnValue : columnValues)
1115             {
1116                 auto measureResult = graphics.MeasureStringRectF(columnValueview->GetFont()PointF(00)view->GetStringFormat());
1117                 if (measureResult.Error())
1118                 {
1119                     return Result<bool>(ErrorId(measureResult.GetErrorId()));
1120                 }
1121                 RectF r = measureResult.Value();
1122                 textWidths.Add(r.size.w);
1123             }
1124             return Result<bool>(true);
1125         }
1126         public inline const Point& Location() const
1127         {
1128             return location;
1129         }
1130         public void SetLocation(const Point& location_)
1131         {
1132             location = location_;
1133         }
1134         public inline const Size& GetSize() const
1135         {
1136             return size;
1137         }
1138         public inline bool GetFlag(Flags flag) const
1139         {
1140             return (flags & flag) != Flags.none;
1141         }
1142         public inline void SetFlag(Flags flag)
1143         {
1144             flags = cast<Flags>(flags | flag);
1145         }
1146         public inline void ResetFlag(Flags flag)
1147         {
1148             flags = cast<Flags>(flags & ~flag);
1149         }
1150         public inline bool IsSelected() const
1151         {
1152             return GetFlag(Flags.selected);
1153         }
1154         [nodiscard]
1155         public Result<bool> SetSelected()
1156         {
1157             if (!IsSelected())
1158             {
1159                 SetFlag(Flags.selected);
1160                 auto result = view->Invalidate();
1161                 if (result.Error()) return result;
1162             }
1163             return Result<bool>(true);
1164         }
1165         [nodiscard]
1166         public Result<bool> ResetSelected()
1167         {
1168             if (IsSelected())
1169             {
1170                 ResetFlag(Flags.selected);
1171                 auto result = view->Invalidate();
1172                 if (result.Error()) return result;
1173             }
1174             return Result<bool>(true);
1175         }
1176         public State GetState() const
1177         {
1178             if (GetFlag(Flags.disabled))
1179             {
1180                 return State.disabled;
1181             }
1182             else
1183             {
1184                 return State.enabled;
1185             }
1186         }
1187         [nodiscard]
1188         public Result<bool> SetState(State state)
1189         {
1190             if (state != GetState())
1191             {
1192                 if (state == State.disabled)
1193                 {
1194                     SetFlag(Flags.disabled);
1195                 }
1196                 else
1197                 {
1198                     ResetFlag(Flags.disabled);
1199                 }
1200                 auto result = view->Invalidate();
1201                 if (result.Error()) return result;
1202             }
1203             return Result<bool>(true);
1204         }
1205         public inline ListView* View() const
1206         {
1207             return view;
1208         }
1209         public inline void* Data() const
1210         {
1211             return data;
1212         }
1213         public void SetData(void* data_)
1214         {
1215             data = data_;
1216         }
1217         public inline int ImageIndex() const
1218         {
1219             return imageIndex;
1220         }
1221         public void SetImageIndex(int imageIndex_)
1222         {
1223             imageIndex = imageIndex_;
1224         }
1225         public inline int DisabledImageIndex() const
1226         {
1227             return disabledImageIndex;
1228         }
1229         public void SetDisabledImageIndex(int disabledImageIndex_)
1230         {
1231             disabledImageIndex = disabledImageIndex_;
1232         }
1233         private Result<bool> DrawImage(Graphics& graphicsPointF& originint& imageSpace)
1234         {
1235             imageSpace = 0;
1236             Bitmap* image = null;
1237             ImageList* imageList = view->GetImageList();
1238             if (imageList != null)
1239             {
1240                 if (GetState() == State.enabled)
1241                 {
1242                     image = imageList->GetImage(imageIndex);
1243                 }
1244                 else
1245                 {
1246                     image = imageList->GetImage(disabledImageIndex);
1247                 }
1248             }
1249             if (image != null)
1250             {
1251                 int imageWidth = cast<int>(image->GetWidth());
1252                 int imageHeight = cast<int>(image->GetHeight());
1253                 Padding padding = view->ImagePadding();
1254                 Point imageLoc = Point(cast<int>(origin.x)cast<int>(origin.y));
1255                 imageLoc.x = imageLoc.x + padding.left;
1256                 imageLoc.y = imageLoc.y + padding.top;
1257                 Rect r(imageLocSize(imageWidth + padding.Horizontal()imageHeight + padding.Vertical()));
1258                 ImageAttributes attributes;
1259                 if (attributes.Error())
1260                 {
1261                     return Result<bool>(ErrorId(attributes.GetErrorId()));
1262                 }
1263                 auto setResult = attributes.SetColorKey(Color.DefaultBitmapTransparent()Color.DefaultBitmapTransparent()ColorAdjustType.default_);
1264                 if (setResult.Error())
1265                 {
1266                     return Result<bool>(ErrorId(setResult.GetErrorId()));
1267                 }
1268                 auto drawResult = graphics.DrawImage(*imager00imageWidth + padding.Horizontal()imageHeight + padding.Vertical()Unit.pixelattributes);
1269                 if (drawResult.Error())
1270                 {
1271                     return Result<bool>(ErrorId(drawResult.GetErrorId()));
1272                 }
1273                 imageSpace = imageWidth + padding.Horizontal();
1274                 origin.x = origin.x + imageSpace;
1275             }
1276             return Result<bool>(true);
1277         }
1278         private Flags flags;
1279         private Point location;
1280         private Size size;
1281         private ListView* view;
1282         private int imageIndex;
1283         private int disabledImageIndex;
1284         private void* data;
1285         private List<string> columnValues;
1286         private List<float> textWidths;
1287     }