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