1 // Written by Christopher E. Miller 2 // See the included license.txt for copyright and license details. 3 4 module dfl.listview; 5 import core.sys.windows.windows; 6 import core.sys.windows.commctrl; 7 8 import dfl.exception; 9 import dfl.application; 10 import dfl.base; 11 import dfl.collections; 12 import dfl.control; 13 import dfl.drawing; 14 import dfl.event; 15 import dfl.internal.clib; 16 import dfl.internal.dlib; 17 import dfl.internal.utf; 18 19 version (DFL_NO_IMAGELIST) { 20 } else { 21 import dfl.imagelist; 22 } 23 24 private extern (Windows) void _initListview(); 25 26 enum ListViewAlignment : ubyte { 27 TOP, 28 DEFAULT, 29 LEFT, 30 SNAP_TO_GRID, 31 } 32 33 private union CallText { 34 Dstringz ansi; 35 Dwstringz unicode; 36 } 37 38 private CallText getCallText(Dstring text) { 39 CallText result; 40 if (text is null) { 41 if (useUnicode) { 42 result.unicode = null; 43 } else { 44 result.ansi = null; 45 } 46 } else { 47 if (useUnicode) { 48 result.unicode = toUnicodez(text); 49 } else { 50 result.ansi = toAnsiz(text); 51 } 52 } 53 return result; 54 } 55 56 package union LvColumn { 57 LV_COLUMNW lvcw; 58 LV_COLUMNA lvca; 59 struct { 60 UINT mask; 61 int fmt; 62 int cx; 63 private void* pszText; 64 int cchTextMax; 65 int iSubItem; 66 } 67 } 68 69 class ListViewSubItem : DObject { 70 71 this() { 72 Application.ppin(cast(void*) this); 73 } 74 75 this(Dstring thisSubItemText) { 76 this(); 77 78 settextin(thisSubItemText); 79 } 80 81 this(ListViewItem owner, Dstring thisSubItemText) { 82 this(); 83 84 settextin(thisSubItemText); 85 if (owner) { 86 this._item = owner; 87 owner.subItems.add(this); 88 } 89 } 90 91 /+ 92 this(Object obj) { // package 93 this(getObjectString(obj)); 94 } 95 +/ 96 97 package final void settextin(Dstring newText) { 98 calltxt = getCallText(newText); 99 _txt = newText; 100 } 101 102 override Dstring toString() { 103 return text; 104 } 105 106 override Dequ opEquals(Object o) { 107 return text == getObjectString(o); 108 } 109 110 Dequ opEquals(Dstring val) { 111 return text == val; 112 } 113 114 override int opCmp(Object o) { 115 return stringICmp(text, getObjectString(o)); 116 } 117 118 int opCmp(Dstring val) { 119 return stringICmp(text, val); 120 } 121 122 final @property void text(Dstring newText) { 123 settextin(newText); 124 125 if (_item && _item.lview && _item.lview.created) { 126 int ii, subi; 127 ii = _item.lview.items.indexOf(_item); 128 assert(-1 != ii); 129 subi = _item.subItems.indexOf(this); 130 assert(-1 != subi); 131 _item.lview.updateItemText(ii, newText, subi + 1); // Sub items really start at 1 in the list view. 132 } 133 } 134 135 final @property Dstring text() { 136 return _txt; 137 } 138 139 private: 140 package ListViewItem _item; 141 Dstring _txt; 142 package CallText calltxt; 143 } 144 145 class ListViewItem : DObject { 146 147 static class ListViewSubItemCollection { 148 protected this(ListViewItem owner) 149 in { 150 assert(!owner.isubs); 151 } 152 body { 153 _item = owner; 154 } 155 156 private: 157 158 ListViewItem _item; 159 package ListViewSubItem[] _subs; 160 161 void _adding(size_t idx, ListViewSubItem val) { 162 if (val._item) { 163 throw new DflException("ListViewSubItem already belongs to a ListViewItem"); 164 } 165 } 166 167 public: 168 169 mixin ListWrapArray!(ListViewSubItem, _subs, _adding, 170 _blankListCallback!(ListViewSubItem), 171 _blankListCallback!(ListViewSubItem), 172 _blankListCallback!(ListViewSubItem), true, false, false); 173 } 174 175 this() { 176 Application.ppin(cast(void*) this); 177 178 isubs = new ListViewSubItemCollection(this); 179 } 180 181 this(Dstring text) { 182 this(); 183 184 settextin(text); 185 } 186 187 private final void _setcheckstate(int thisindex, bool bchecked) { 188 if (lview && lview.created) { 189 LV_ITEMA li; 190 li.stateMask = LVIS_STATEIMAGEMASK; 191 li.state = cast(LPARAM)(bchecked ? 2 : 1) << 12; 192 lview.prevwproc(LVM_SETITEMSTATE, cast(WPARAM) thisindex, cast(LPARAM)&li); 193 } 194 } 195 196 private final bool _getcheckstate(int thisindex) { 197 if (lview && lview.created) { 198 if ((lview.prevwproc(LVM_GETITEMSTATE, cast(WPARAM) thisindex, 199 LVIS_STATEIMAGEMASK) >> 12) - 1) { 200 return true; 201 } 202 } 203 return false; 204 } 205 206 final @property void checked(bool byes) { 207 return _setcheckstate(index, byes); 208 } 209 210 final @property bool checked() { 211 return _getcheckstate(index); 212 } 213 214 package final void settextin(Dstring newText) { 215 calltxt = getCallText(newText); 216 _txt = newText; 217 } 218 219 override Dstring toString() { 220 return text; 221 } 222 223 override Dequ opEquals(Object o) { 224 return text == getObjectString(o); 225 } 226 227 Dequ opEquals(Dstring val) { 228 return text == val; 229 } 230 231 override int opCmp(Object o) { 232 return stringICmp(text, getObjectString(o)); 233 } 234 235 int opCmp(Dstring val) { 236 return stringICmp(text, val); 237 } 238 239 final @property Rect bounds() { 240 if (lview) { 241 int i = index; 242 assert(-1 != i); 243 return lview.getItemRect(i); 244 } 245 return Rect(0, 0, 0, 0); 246 } 247 248 final @property int index() { 249 if (lview) { 250 return lview.litems.indexOf(this); 251 } 252 return -1; 253 } 254 255 final @property void text(Dstring newText) { 256 settextin(newText); 257 258 if (lview && lview.created) { 259 lview.updateItemText(this, newText); 260 } 261 } 262 263 final @property Dstring text() { 264 return _txt; 265 } 266 267 final @property void selected(bool byes) { 268 if (lview && lview.created) { 269 LV_ITEMA li; 270 li.stateMask = LVIS_SELECTED; 271 if (byes) { 272 li.state = LVIS_SELECTED; 273 } 274 lview.prevwproc(LVM_SETITEMSTATE, cast(WPARAM) index, cast(LPARAM)&li); 275 } 276 } 277 278 final @property bool selected() { 279 if (lview && lview.created) { 280 if (lview.prevwproc(LVM_GETITEMSTATE, cast(WPARAM) index, LVIS_SELECTED)) { 281 return true; 282 } 283 } 284 return false; 285 } 286 287 final @property ListView listView() { 288 return lview; 289 } 290 291 final @property void tag(Object obj) { 292 _tag = obj; 293 } 294 295 final @property Object tag() { 296 return _tag; 297 } 298 299 final void beginEdit() { 300 if (lview && lview.created) { 301 if (dfl.internal.utf.useUnicode) { 302 lview.prevwproc(LVM_EDITLABELW, index, 0); 303 } else { 304 lview.prevwproc(LVM_EDITLABELA, index, 0); 305 } 306 } 307 } 308 309 final @property ListViewSubItemCollection subItems() { 310 return isubs; 311 } 312 313 version (DFL_NO_IMAGELIST) { 314 } else { 315 316 final @property void imageIndex(int index) { 317 this._imgidx = index; 318 319 if (lview && lview.created) { 320 lview.updateItem(this); 321 } 322 } 323 324 final @property int imageIndex() { 325 return _imgidx; 326 } 327 } 328 329 private: 330 package ListView lview = null; 331 Object _tag = null; 332 package ListViewSubItemCollection isubs = null; 333 version (DFL_NO_IMAGELIST) { 334 } else { 335 int _imgidx = -1; 336 } 337 Dstring _txt; 338 package CallText calltxt; 339 } 340 341 class ColumnHeader : DObject { 342 343 this(Dstring text) { 344 this(); 345 346 this._txt = text; 347 } 348 349 this() { 350 Application.ppin(cast(void*) this); 351 } 352 353 final @property ListView listView() { 354 return lview; 355 } 356 357 final @property void text(Dstring newText) { 358 _txt = newText; 359 360 if (lview && lview.created) { 361 lview.updateColumnText(this, newText); 362 } 363 } 364 365 final @property Dstring text() { 366 return _txt; 367 } 368 369 override Dstring toString() { 370 return text; 371 } 372 373 override Dequ opEquals(Object o) { 374 return text == getObjectString(o); 375 } 376 377 Dequ opEquals(Dstring val) { 378 return text == val; 379 } 380 381 override int opCmp(Object o) { 382 return stringICmp(text, getObjectString(o)); 383 } 384 385 int opCmp(Dstring val) { 386 return stringICmp(text, val); 387 } 388 389 final @property int index() { 390 if (lview) { 391 lview.cols.indexOf(this); 392 } 393 return -1; 394 } 395 396 final @property void textAlign(HorizontalAlignment halign) { 397 _align = halign; 398 399 if (lview && lview.created) { 400 lview.updateColumnAlign(this, halign); 401 } 402 } 403 404 final @property HorizontalAlignment textAlign() { 405 return _align; 406 } 407 408 final @property void width(int w) { 409 _width = w; 410 411 if (lview && lview.created) { 412 lview.updateColumnWidth(this, w); 413 } 414 } 415 416 final @property int width() { 417 if (lview && lview.created) { 418 int xx; 419 xx = lview.getColumnWidth(this); 420 if (-1 != xx) { 421 _width = xx; 422 } 423 } 424 return _width; 425 } 426 427 private: 428 package ListView lview; 429 Dstring _txt; 430 int _width; 431 HorizontalAlignment _align; 432 } 433 434 class LabelEditEventArgs : EventArgs { 435 436 this(ListViewItem item, Dstring label) { 437 _item = item; 438 _label = label; 439 } 440 441 this(ListViewItem node) { 442 _item = item; 443 } 444 445 final @property ListViewItem item() { 446 return _item; 447 } 448 449 final @property Dstring label() { 450 return _label; 451 } 452 453 final @property void cancelEdit(bool byes) { 454 _cancel = byes; 455 } 456 457 final @property bool cancelEdit() { 458 return _cancel; 459 } 460 461 private: 462 ListViewItem _item; 463 Dstring _label; 464 bool _cancel = false; 465 } 466 467 /+ 468 class ItemCheckEventArgs: EventArgs { 469 this(int index, CheckState newCheckState, CheckState oldCheckState) { 470 this._idx = index; 471 this._ncs = newCheckState; 472 this._ocs = oldCheckState; 473 } 474 475 476 final @property CheckState currentValue() { 477 return _ocs; 478 } 479 480 481 /+ 482 final @property void newValue(CheckState cs) { 483 _ncs = cs; 484 } 485 +/ 486 487 488 final @property CheckState newValue() { 489 return _ncs; 490 } 491 492 493 private: 494 int _idx; 495 CheckState _ncs, _ocs; 496 } 497 +/ 498 499 class ItemCheckedEventArgs : EventArgs { 500 this(ListViewItem item) { 501 this._item = item; 502 } 503 504 final @property ListViewItem item() { 505 return this._item; 506 } 507 508 private: 509 ListViewItem _item; 510 } 511 512 class ListView : ControlSuperClass { 513 514 static class ListViewItemCollection { 515 protected this(ListView lv) 516 in { 517 assert(lv.litems is null); 518 } 519 body { 520 this.lv = lv; 521 } 522 523 void add(ListViewItem item) { 524 int ii = -1; // Insert index. 525 526 switch (lv.sorting) { 527 case SortOrder.NONE: // Add to end. 528 ii = _items.length; 529 break; 530 531 case SortOrder.ASCENDING: // Insertion sort. 532 for (ii = 0; ii != _items.length; ii++) { 533 assert(lv._sortproc); 534 //if(item < _items[ii]) 535 if (lv._sortproc(item, _items[ii]) < 0) { 536 break; 537 } 538 } 539 break; 540 541 case SortOrder.DESCENDING: // Insertion sort. 542 for (ii = 0; ii != _items.length; ii++) { 543 assert(lv._sortproc); 544 //if(item >= _items[ii]) 545 if (lv._sortproc(item, _items[ii]) >= 0) { 546 break; 547 } 548 } 549 break; 550 551 default: 552 assert(0); 553 } 554 555 assert(-1 != ii); 556 insert(ii, item); 557 } 558 559 void add(Dstring text) { 560 return add(new ListViewItem(text)); 561 } 562 563 // addRange must have special case in case of sorting. 564 565 void addRange(ListViewItem[] range) { 566 foreach (ListViewItem item; range) { 567 add(item); 568 } 569 } 570 571 /+ 572 void addRange(Object[] range) { 573 foreach(Object o; range) { 574 add(o); 575 } 576 } 577 +/ 578 579 void addRange(Dstring[] range) { 580 foreach (Dstring s; range) { 581 add(s); 582 } 583 } 584 585 private: 586 587 ListView lv; 588 package ListViewItem[] _items; 589 590 package final @property bool created() { 591 return lv && lv.created(); 592 } 593 594 package final void doListItems() // DMD 0.125: this member is not accessible when private. 595 596 in { 597 assert(created); 598 } 599 body { 600 int ii; 601 foreach (int i, ListViewItem item; _items) { 602 ii = lv._ins(i, item); 603 //assert(-1 != ii); 604 assert(i == ii); 605 606 /+ 607 // Add sub items. 608 foreach(int subi, ListViewSubItem subItem; item.isubs._subs) { 609 lv._ins(i, subItem, subi + 1); // Sub items really start at 1 in the list view. 610 } 611 +/ 612 } 613 } 614 615 void verifyNoParent(ListViewItem item) { 616 if (item.lview) { 617 throw new DflException("ListViewItem already belongs to a ListView"); 618 } 619 } 620 621 void _adding(size_t idx, ListViewItem val) { 622 verifyNoParent(val); 623 } 624 625 void _added(size_t idx, ListViewItem val) { 626 val.lview = lv; 627 628 int i; 629 if (created) { 630 i = lv._ins(idx, val); 631 assert(-1 != i); 632 } 633 } 634 635 void _removed(size_t idx, ListViewItem val) { 636 if (size_t.max == idx) { // Clear all. 637 if (created) { 638 lv.prevwproc(LVM_DELETEALLITEMS, 0, 0); 639 } 640 } else { 641 if (created) { 642 lv.prevwproc(LVM_DELETEITEM, cast(WPARAM) idx, 0); 643 } 644 } 645 } 646 647 public: 648 649 mixin ListWrapArray!(ListViewItem, _items, _adding, _added, 650 _blankListCallback!(ListViewItem), _removed, true, false, false); 651 } 652 653 static class ColumnHeaderCollection { 654 protected this(ListView owner) 655 in { 656 assert(!owner.cols); 657 } 658 body { 659 lv = owner; 660 } 661 662 private: 663 ListView lv; 664 ColumnHeader[] _headers; 665 666 package final @property bool created() { 667 return lv && lv.created(); 668 } 669 670 void verifyNoParent(ColumnHeader header) { 671 if (header.lview) { 672 throw new DflException("ColumnHeader already belongs to a ListView"); 673 } 674 } 675 676 package final void doListHeaders() // DMD 0.125: this member is not accessible when private. 677 678 in { 679 assert(created); 680 } 681 body { 682 int ii; 683 foreach (int i, ColumnHeader header; _headers) { 684 ii = lv._ins(i, header); 685 assert(-1 != ii); 686 //assert(i == ii); 687 } 688 } 689 690 void _adding(size_t idx, ColumnHeader val) { 691 verifyNoParent(val); 692 } 693 694 void _added(size_t idx, ColumnHeader val) { 695 val.lview = lv; 696 697 int i; 698 if (created) { 699 i = lv._ins(idx, val); 700 assert(-1 != i); 701 } 702 } 703 704 void _removed(size_t idx, ColumnHeader val) { 705 if (size_t.max == idx) { // Clear all. 706 } else { 707 if (created) { 708 lv.prevwproc(LVM_DELETECOLUMN, cast(WPARAM) idx, 0); 709 } 710 } 711 } 712 713 public: 714 715 mixin ListWrapArray!(ColumnHeader, _headers, _adding, _added, 716 _blankListCallback!(ColumnHeader), _removed, true, false, false, true); // CLEAR_EACH 717 } 718 719 static class SelectedIndexCollection { 720 deprecated alias count = length; 721 722 @property int length() { 723 if (!lview.created) { 724 return 0; 725 } 726 727 int result = 0; 728 foreach (int onidx; this) { 729 result++; 730 } 731 return result; 732 } 733 734 int opIndex(int idx) { 735 foreach (int onidx; this) { 736 if (!idx) { 737 return onidx; 738 } 739 idx--; 740 } 741 742 // If it's not found it's out of bounds and bad things happen. 743 assert(0); 744 } 745 746 bool contains(int idx) { 747 return indexOf(idx) != -1; 748 } 749 750 int indexOf(int idx) { 751 int i = 0; 752 foreach (int onidx; this) { 753 if (onidx == idx) { 754 return i; 755 } 756 i++; 757 } 758 return -1; 759 } 760 761 int opApply(int delegate(ref int) dg) { 762 if (!lview.created) { 763 return 0; 764 } 765 766 int result = 0; 767 int idx = -1; 768 for (;;) { 769 idx = cast(int) lview.prevwproc(LVM_GETNEXTITEM, cast(WPARAM) idx, 770 MAKELPARAM(cast(UINT) LVNI_SELECTED, 0)); 771 if (-1 == idx) { // Done. 772 break; 773 } 774 int dgidx = idx; // Prevent ref. 775 result = dg(dgidx); 776 if (result) { 777 break; 778 } 779 } 780 return result; 781 } 782 783 mixin OpApplyAddIndex!(opApply, int); 784 785 protected this(ListView lv) { 786 lview = lv; 787 } 788 789 package: 790 ListView lview; 791 } 792 793 deprecated alias SelectedListViewItemCollection = SelectedItemCollection; 794 795 static class SelectedItemCollection { 796 deprecated alias count = length; 797 798 @property int length() { 799 if (!lview.created) { 800 return 0; 801 } 802 803 int result = 0; 804 foreach (ListViewItem onitem; this) { 805 result++; 806 } 807 return result; 808 } 809 810 ListViewItem opIndex(int idx) { 811 foreach (ListViewItem onitem; this) { 812 if (!idx) { 813 return onitem; 814 } 815 idx--; 816 } 817 818 // If it's not found it's out of bounds and bad things happen. 819 assert(0); 820 } 821 822 bool contains(ListViewItem item) { 823 return indexOf(item) != -1; 824 } 825 826 int indexOf(ListViewItem item) { 827 int i = 0; 828 foreach (ListViewItem onitem; this) { 829 if (onitem == item) { // Not using is. 830 return i; 831 } 832 i++; 833 } 834 return -1; 835 } 836 837 int opApply(int delegate(ref ListViewItem) dg) { 838 if (!lview.created) { 839 return 0; 840 } 841 842 int result = 0; 843 int idx = -1; 844 for (;;) { 845 idx = cast(int) lview.prevwproc(LVM_GETNEXTITEM, cast(WPARAM) idx, 846 MAKELPARAM(cast(UINT) LVNI_SELECTED, 0)); 847 if (-1 == idx) { // Done. 848 break; 849 } 850 ListViewItem litem = lview.litems._items[idx]; // Prevent ref. 851 result = dg(litem); 852 if (result) { 853 break; 854 } 855 } 856 return result; 857 } 858 859 mixin OpApplyAddIndex!(opApply, ListViewItem); 860 861 protected this(ListView lv) { 862 lview = lv; 863 } 864 865 package: 866 ListView lview; 867 } 868 869 static class CheckedIndexCollection { 870 deprecated alias count = length; 871 872 @property int length() { 873 if (!lview.created) { 874 return 0; 875 } 876 877 int result = 0; 878 foreach (int onidx; this) { 879 result++; 880 } 881 return result; 882 } 883 884 int opIndex(int idx) { 885 foreach (int onidx; this) { 886 if (!idx) { 887 return onidx; 888 } 889 idx--; 890 } 891 892 // If it's not found it's out of bounds and bad things happen. 893 assert(0); 894 } 895 896 bool contains(int idx) { 897 return indexOf(idx) != -1; 898 } 899 900 int indexOf(int idx) { 901 int i = 0; 902 foreach (int onidx; this) { 903 if (onidx == idx) { 904 return i; 905 } 906 i++; 907 } 908 return -1; 909 } 910 911 int opApply(int delegate(ref int) dg) { 912 if (!lview.created) { 913 return 0; 914 } 915 916 int result = 0; 917 foreach (ref size_t i, ref ListViewItem lvitem; lview.items) { 918 if (lvitem._getcheckstate(i)) { 919 int dgidx = i; // Prevent ref. 920 result = dg(dgidx); 921 if (result) { 922 break; 923 } 924 } 925 } 926 return result; 927 } 928 929 mixin OpApplyAddIndex!(opApply, int); 930 931 protected this(ListView lv) { 932 lview = lv; 933 } 934 935 package: 936 ListView lview; 937 } 938 939 this() { 940 _initListview(); 941 942 litems = new ListViewItemCollection(this); 943 cols = new ColumnHeaderCollection(this); 944 selidxcollection = new SelectedIndexCollection(this); 945 selobjcollection = new SelectedItemCollection(this); 946 checkedis = new CheckedIndexCollection(this); 947 948 wstyle |= WS_TABSTOP | LVS_ALIGNTOP | LVS_AUTOARRANGE | LVS_SHAREIMAGELISTS; 949 wexstyle |= WS_EX_CLIENTEDGE; 950 ctrlStyle |= ControlStyles.SELECTABLE; 951 wclassStyle = listviewClassStyle; 952 } 953 954 final @property void activation(ItemActivation ia) { 955 switch (ia) { 956 case ItemActivation.STANDARD: 957 _lvexstyle(LVS_EX_ONECLICKACTIVATE | LVS_EX_TWOCLICKACTIVATE, 0); 958 break; 959 960 case ItemActivation.ONE_CLICK: 961 _lvexstyle( 962 LVS_EX_ONECLICKACTIVATE | LVS_EX_TWOCLICKACTIVATE, LVS_EX_ONECLICKACTIVATE); 963 break; 964 965 case ItemActivation.TWO_CLICK: 966 _lvexstyle( 967 LVS_EX_ONECLICKACTIVATE | LVS_EX_TWOCLICKACTIVATE, LVS_EX_TWOCLICKACTIVATE); 968 break; 969 970 default: 971 assert(0); 972 } 973 } 974 975 final @property ItemActivation activation() { 976 DWORD lvex; 977 lvex = _lvexstyle(); 978 if (lvex & LVS_EX_ONECLICKACTIVATE) { 979 return ItemActivation.ONE_CLICK; 980 } 981 if (lvex & LVS_EX_TWOCLICKACTIVATE) { 982 return ItemActivation.TWO_CLICK; 983 } 984 return ItemActivation.STANDARD; 985 } 986 987 /+ 988 989 final void alignment(ListViewAlignment lva) { 990 // TODO 991 992 switch(lva) { 993 case ListViewAlignment.TOP: 994 _style((_style() & ~(LVS_ALIGNLEFT | foo)) | LVS_ALIGNTOP); 995 break; 996 997 default: 998 assert(0); 999 } 1000 } 1001 1002 1003 final @property ListViewAlignment alignment() { 1004 // TODO 1005 } 1006 +/ 1007 1008 final @property void allowColumnReorder(bool byes) { 1009 _lvexstyle(LVS_EX_HEADERDRAGDROP, byes ? LVS_EX_HEADERDRAGDROP : 0); 1010 } 1011 1012 final @property bool allowColumnReorder() { 1013 return (_lvexstyle() & LVS_EX_HEADERDRAGDROP) == LVS_EX_HEADERDRAGDROP; 1014 } 1015 1016 final @property void autoArrange(bool byes) { 1017 if (byes) { 1018 _style(_style() | LVS_AUTOARRANGE); 1019 } else { 1020 _style(_style() & ~LVS_AUTOARRANGE); 1021 } 1022 1023 //_crecreate(); // ? 1024 } 1025 1026 final @property bool autoArrange() { 1027 return (_style() & LVS_AUTOARRANGE) == LVS_AUTOARRANGE; 1028 } 1029 1030 override @property void backColor(Color c) { 1031 if (created) { 1032 COLORREF cref; 1033 if (Color.empty == c) { 1034 cref = CLR_NONE; 1035 } else { 1036 cref = c.toRgb(); 1037 } 1038 prevwproc(LVM_SETBKCOLOR, 0, cast(LPARAM) cref); 1039 prevwproc(LVM_SETTEXTBKCOLOR, 0, cast(LPARAM) cref); 1040 } 1041 1042 super.backColor = c; 1043 } 1044 1045 override @property Color backColor() { 1046 if (Color.empty == backc) { 1047 return defaultBackColor; 1048 } 1049 return backc; 1050 } 1051 1052 final @property void borderStyle(BorderStyle bs) { 1053 final switch (bs) { 1054 case BorderStyle.FIXED_3D: 1055 _style(_style() & ~WS_BORDER); 1056 _exStyle(_exStyle() | WS_EX_CLIENTEDGE); 1057 break; 1058 1059 case BorderStyle.FIXED_SINGLE: 1060 _exStyle(_exStyle() & ~WS_EX_CLIENTEDGE); 1061 _style(_style() | WS_BORDER); 1062 break; 1063 1064 case BorderStyle.NONE: 1065 _style(_style() & ~WS_BORDER); 1066 _exStyle(_exStyle() & ~WS_EX_CLIENTEDGE); 1067 break; 1068 } 1069 1070 if (created) { 1071 redrawEntire(); 1072 } 1073 } 1074 1075 final @property BorderStyle borderStyle() { 1076 if (_exStyle() & WS_EX_CLIENTEDGE) { 1077 return BorderStyle.FIXED_3D; 1078 } else if (_style() & WS_BORDER) { 1079 return BorderStyle.FIXED_SINGLE; 1080 } 1081 return BorderStyle.NONE; 1082 } 1083 1084 final @property void checkBoxes(bool byes) { 1085 _lvexstyle(LVS_EX_CHECKBOXES, byes ? LVS_EX_CHECKBOXES : 0); 1086 } 1087 1088 final @property bool checkBoxes() { 1089 return (_lvexstyle() & LVS_EX_CHECKBOXES) == LVS_EX_CHECKBOXES; 1090 } 1091 1092 // ListView.CheckedIndexCollection 1093 final @property CheckedIndexCollection checkedIndices() { 1094 return checkedis; 1095 } 1096 1097 /+ 1098 1099 // ListView.CheckedListViewItemCollection 1100 final @property CheckedListViewItemCollection checkedItems() { 1101 // TODO 1102 } 1103 +/ 1104 1105 final @property ColumnHeaderCollection columns() { 1106 return cols; 1107 } 1108 1109 // Extra. 1110 final @property int focusedIndex() { 1111 if (!created) { 1112 return -1; 1113 } 1114 return cast(int) prevwproc(LVM_GETNEXTITEM, cast(WPARAM)-1, 1115 MAKELPARAM(cast(UINT) LVNI_FOCUSED, 0)); 1116 } 1117 1118 final @property ListViewItem focusedItem() { 1119 int i; 1120 i = focusedIndex; 1121 if (-1 == i) { 1122 return null; 1123 } 1124 return litems._items[i]; 1125 } 1126 1127 override @property void foreColor(Color c) { 1128 if (created) { 1129 prevwproc(LVM_SETTEXTCOLOR, 0, cast(LPARAM) c.toRgb()); 1130 } 1131 1132 super.foreColor = c; 1133 } 1134 1135 override @property Color foreColor() { 1136 if (Color.empty == forec) { 1137 return defaultForeColor; 1138 } 1139 return forec; 1140 } 1141 1142 final @property void fullRowSelect(bool byes) { 1143 _lvexstyle(LVS_EX_FULLROWSELECT, byes ? LVS_EX_FULLROWSELECT : 0); 1144 } 1145 1146 final @property bool fullRowSelect() { 1147 return (_lvexstyle() & LVS_EX_FULLROWSELECT) == LVS_EX_FULLROWSELECT; 1148 } 1149 1150 final @property void gridLines(bool byes) { 1151 _lvexstyle(LVS_EX_GRIDLINES, byes ? LVS_EX_GRIDLINES : 0); 1152 } 1153 1154 final @property bool gridLines() { 1155 return (_lvexstyle() & LVS_EX_GRIDLINES) == LVS_EX_GRIDLINES; 1156 } 1157 1158 /+ 1159 1160 final @property void headerStyle(ColumnHeaderStyle chs) { 1161 // TODO: LVS_NOCOLUMNHEADER ... default is clickable. 1162 } 1163 1164 1165 final @property ColumnHeaderStyle headerStyle() { 1166 // TODO 1167 } 1168 +/ 1169 1170 final @property void hideSelection(bool byes) { 1171 if (byes) { 1172 _style(_style() & ~LVS_SHOWSELALWAYS); 1173 } else { 1174 _style(_style() | LVS_SHOWSELALWAYS); 1175 } 1176 } 1177 1178 final @property bool hideSelection() { 1179 return (_style() & LVS_SHOWSELALWAYS) != LVS_SHOWSELALWAYS; 1180 } 1181 1182 final @property void hoverSelection(bool byes) { 1183 _lvexstyle(LVS_EX_TRACKSELECT, byes ? LVS_EX_TRACKSELECT : 0); 1184 } 1185 1186 final @property bool hoverSelection() { 1187 return (_lvexstyle() & LVS_EX_TRACKSELECT) == LVS_EX_TRACKSELECT; 1188 } 1189 1190 final @property ListViewItemCollection items() { 1191 return litems; 1192 } 1193 1194 // Simple as addRow("item", "sub item1", "sub item2", "etc"); 1195 // rowstrings[0] is the item and rowstrings[1 .. rowstrings.length] are its sub items. 1196 //final void addRow(Dstring[] rowstrings ...) 1197 final ListViewItem addRow(Dstring[] rowstrings...) { 1198 if (rowstrings.length) { 1199 ListViewItem item; 1200 item = new ListViewItem(rowstrings[0]); 1201 if (rowstrings.length > 1) { 1202 item.subItems.addRange(rowstrings[1 .. rowstrings.length]); 1203 } 1204 items.add(item); 1205 return item; 1206 } 1207 assert(0); 1208 } 1209 1210 final @property void labelEdit(bool byes) { 1211 if (byes) { 1212 _style(_style() | LVS_EDITLABELS); 1213 } else { 1214 _style(_style() & ~LVS_EDITLABELS); 1215 } 1216 } 1217 1218 final @property bool labelEdit() { 1219 return (_style() & LVS_EDITLABELS) == LVS_EDITLABELS; 1220 } 1221 1222 final @property void labelWrap(bool byes) { 1223 if (byes) { 1224 _style(_style() & ~LVS_NOLABELWRAP); 1225 } else { 1226 _style(_style() | LVS_NOLABELWRAP); 1227 } 1228 } 1229 1230 final @property bool labelWrap() { 1231 return (_style() & LVS_NOLABELWRAP) != LVS_NOLABELWRAP; 1232 } 1233 1234 final @property void multiSelect(bool byes) { 1235 if (byes) { 1236 _style(_style() & ~LVS_SINGLESEL); 1237 } else { 1238 _style(_style() | LVS_SINGLESEL); 1239 1240 if (selectedItems.length > 1) { 1241 selectedItems[0].selected = true; // Clear all but first selected. 1242 } 1243 } 1244 } 1245 1246 final @property bool multiSelect() { 1247 return (_style() & LVS_SINGLESEL) != LVS_SINGLESEL; 1248 } 1249 1250 // Note: scrollable=false is not compatible with the list or details(report) styles(views). 1251 // See Knowledge Base Article Q137520. 1252 final @property void scrollable(bool byes) { 1253 if (byes) { 1254 _style(_style() & ~LVS_NOSCROLL); 1255 } else { 1256 _style(_style() | LVS_NOSCROLL); 1257 } 1258 1259 _crecreate(); 1260 } 1261 1262 final @property bool scrollable() { 1263 return (_style() & LVS_NOSCROLL) != LVS_NOSCROLL; 1264 } 1265 1266 final @property SelectedIndexCollection selectedIndices() { 1267 return selidxcollection; 1268 } 1269 1270 final @property SelectedItemCollection selectedItems() { 1271 return selobjcollection; 1272 } 1273 1274 final @property void view(View v) { 1275 switch (v) { 1276 case View.LARGE_ICON: 1277 _style(_style() & ~(LVS_SMALLICON | LVS_LIST | LVS_REPORT)); 1278 break; 1279 1280 case View.SMALL_ICON: 1281 _style((_style() & ~(LVS_LIST | LVS_REPORT)) | LVS_SMALLICON); 1282 break; 1283 1284 case View.LIST: 1285 _style((_style() & ~(LVS_SMALLICON | LVS_REPORT)) | LVS_LIST); 1286 break; 1287 1288 case View.DETAILS: 1289 _style((_style() & ~(LVS_SMALLICON | LVS_LIST)) | LVS_REPORT); 1290 break; 1291 1292 default: 1293 assert(0); 1294 } 1295 1296 if (created) { 1297 redrawEntire(); 1298 } 1299 } 1300 1301 final @property View view() { 1302 LONG st; 1303 st = _style(); 1304 if (st & LVS_SMALLICON) { 1305 return View.SMALL_ICON; 1306 } 1307 if (st & LVS_LIST) { 1308 return View.LIST; 1309 } 1310 if (st & LVS_REPORT) { 1311 return View.DETAILS; 1312 } 1313 return View.LARGE_ICON; 1314 } 1315 1316 final @property void sorting(SortOrder so) { 1317 if (so == _sortorder) { 1318 return; 1319 } 1320 1321 switch (so) { 1322 case SortOrder.NONE: 1323 _sortproc = null; 1324 break; 1325 1326 case SortOrder.ASCENDING: 1327 case SortOrder.DESCENDING: 1328 if (!_sortproc) { 1329 _sortproc = &_defsortproc; 1330 } 1331 break; 1332 1333 default: 1334 assert(0); 1335 } 1336 1337 _sortorder = so; 1338 1339 sort(); 1340 } 1341 1342 final @property SortOrder sorting() { 1343 return _sortorder; 1344 } 1345 1346 final void sort() { 1347 if (SortOrder.NONE != _sortorder) { 1348 assert(_sortproc); 1349 ListViewItem[] sitems = items._items; 1350 if (sitems.length > 1) { 1351 sitems = sitems.dup; // So exception won't damage anything. 1352 // Stupid bubble sort. At least it's a "stable sort". 1353 bool swp; 1354 auto sortmax = sitems.length - 1; 1355 size_t iw; 1356 do { 1357 swp = false; 1358 for (iw = 0; iw != sortmax; iw++) { 1359 //if(sitems[iw] > sitems[iw + 1]) 1360 if (_sortproc(sitems[iw], sitems[iw + 1]) > 0) { 1361 swp = true; 1362 ListViewItem lvis = sitems[iw]; 1363 sitems[iw] = sitems[iw + 1]; 1364 sitems[iw + 1] = lvis; 1365 } 1366 } 1367 } 1368 while (swp); 1369 1370 if (created) { 1371 beginUpdate(); 1372 SendMessageA(handle, LVM_DELETEALLITEMS, 0, 0); // Note: this sends LVN_DELETEALLITEMS. 1373 foreach (idx, lvi; sitems) { 1374 _ins(idx, lvi); 1375 } 1376 endUpdate(); 1377 } 1378 1379 items._items = sitems; 1380 } 1381 } 1382 } 1383 1384 final @property void sorter(int delegate(ListViewItem, ListViewItem) sortproc) { 1385 if (sortproc == this._sortproc) { 1386 return; 1387 } 1388 1389 if (!sortproc) { 1390 this._sortproc = null; 1391 sorting = SortOrder.NONE; 1392 return; 1393 } 1394 1395 this._sortproc = sortproc; 1396 1397 if (SortOrder.NONE == sorting) { 1398 sorting = SortOrder.ASCENDING; 1399 } 1400 sort(); 1401 } 1402 1403 final int delegate(ListViewItem, ListViewItem) sorter() @property { 1404 return _sortproc; 1405 } 1406 1407 /+ 1408 1409 // Gets the first visible item. 1410 final @property ListViewItem topItem() { 1411 if(!created) { 1412 return null; 1413 } 1414 // TODO: LVM_GETTOPINDEX 1415 } 1416 +/ 1417 1418 final @property void arrangeIcons() { 1419 if (created) // SendMessageA(hwnd, LVM_ARRANGE, LVA_DEFAULT, 0); 1420 { 1421 prevwproc(LVM_ARRANGE, LVA_DEFAULT, 0); 1422 } 1423 } 1424 1425 final void arrangeIcons(ListViewAlignment a) { 1426 if (created) { 1427 switch (a) { 1428 case ListViewAlignment.TOP: 1429 //SendMessageA(hwnd, LVM_ARRANGE, LVA_ALIGNTOP, 0); 1430 prevwproc(LVM_ARRANGE, LVA_ALIGNTOP, 0); 1431 break; 1432 1433 case ListViewAlignment.DEFAULT: 1434 //SendMessageA(hwnd, LVM_ARRANGE, LVA_DEFAULT, 0); 1435 prevwproc(LVM_ARRANGE, LVA_DEFAULT, 0); 1436 break; 1437 1438 case ListViewAlignment.LEFT: 1439 //SendMessageA(hwnd, LVM_ARRANGE, LVA_ALIGNLEFT, 0); 1440 prevwproc(LVM_ARRANGE, LVA_ALIGNLEFT, 0); 1441 break; 1442 1443 case ListViewAlignment.SNAP_TO_GRID: 1444 //SendMessageA(hwnd, LVM_ARRANGE, LVA_SNAPTOGRID, 0); 1445 prevwproc(LVM_ARRANGE, LVA_SNAPTOGRID, 0); 1446 break; 1447 1448 default: 1449 assert(0); 1450 } 1451 } 1452 } 1453 1454 final void beginUpdate() { 1455 SendMessageA(handle, WM_SETREDRAW, false, 0); 1456 } 1457 1458 final void endUpdate() { 1459 SendMessageA(handle, WM_SETREDRAW, true, 0); 1460 invalidate(true); // Show updates. 1461 } 1462 1463 final void clear() { 1464 litems.clear(); 1465 } 1466 1467 final void ensureVisible(int index) { 1468 // Can only be visible if it's created. Check if correct implementation. 1469 createControl(); 1470 1471 //if(created) 1472 // SendMessageA(hwnd, LVM_ENSUREVISIBLE, cast(WPARAM)index, FALSE); 1473 prevwproc(LVM_ENSUREVISIBLE, cast(WPARAM) index, FALSE); 1474 } 1475 1476 /+ 1477 1478 // Returns null if no item is at this location. 1479 final ListViewItem getItemAt(int x, int y) { 1480 // LVM_FINDITEM LVFI_NEARESTXY ? since it's nearest, need to see if it's really at that location. 1481 // TODO 1482 } 1483 +/ 1484 1485 final Rect getItemRect(int index) { 1486 if (created) { 1487 RECT rect; 1488 rect.left = LVIR_BOUNDS; 1489 if (prevwproc(LVM_GETITEMRECT, cast(WPARAM) index, cast(LPARAM)&rect)) { 1490 return Rect(&rect); 1491 } 1492 } 1493 return Rect(0, 0, 0, 0); 1494 } 1495 1496 final Rect getItemRect(int index, ItemBoundsPortion ibp) { 1497 if (created) { 1498 RECT rect; 1499 switch (ibp) { 1500 case ItemBoundsPortion.ENTIRE: 1501 rect.left = LVIR_BOUNDS; 1502 break; 1503 1504 case ItemBoundsPortion.ICON: 1505 rect.left = LVIR_ICON; 1506 break; 1507 1508 case ItemBoundsPortion.ITEM_ONLY: 1509 rect.left = LVIR_SELECTBOUNDS; // ? 1510 break; 1511 1512 case ItemBoundsPortion.LABEL: 1513 rect.left = LVIR_LABEL; 1514 break; 1515 1516 default: 1517 assert(0); 1518 } 1519 if (prevwproc(LVM_GETITEMRECT, cast(WPARAM) index, cast(LPARAM)&rect)) { 1520 return Rect(&rect); 1521 } 1522 } 1523 return Rect(0, 0, 0, 0); 1524 } 1525 1526 version (DFL_NO_IMAGELIST) { 1527 } else { 1528 1529 final @property void largeImageList(ImageList imglist) { 1530 if (isHandleCreated) { 1531 prevwproc(LVM_SETIMAGELIST, LVSIL_NORMAL, 1532 cast(LPARAM)(imglist ? imglist.handle : cast(HIMAGELIST) null)); 1533 } 1534 1535 _lgimglist = imglist; 1536 } 1537 1538 final @property ImageList largeImageList() { 1539 return _lgimglist; 1540 } 1541 1542 final @property void smallImageList(ImageList imglist) { 1543 if (isHandleCreated) { 1544 prevwproc(LVM_SETIMAGELIST, LVSIL_SMALL, 1545 cast(LPARAM)(imglist ? imglist.handle : cast(HIMAGELIST) null)); 1546 } 1547 1548 _smimglist = imglist; 1549 } 1550 1551 final @property ImageList smallImageList() { 1552 return _smimglist; 1553 } 1554 1555 /+ 1556 1557 final @property void stateImageList(ImageList imglist) { 1558 if(isHandleCreated) { 1559 prevwproc(LVM_SETIMAGELIST, LVSIL_STATE, 1560 cast(LPARAM)(imglist ? imglist.handle : cast(HIMAGELIST)null)); 1561 } 1562 1563 _stimglist = imglist; 1564 } 1565 1566 1567 final @property ImageList stateImageList() { 1568 return _stimglist; 1569 } 1570 +/ 1571 } 1572 1573 // TODO: 1574 // itemActivate, itemDrag 1575 //CancelEventHandler selectedIndexChanging; // ? 1576 1577 Event!(ListView, ColumnClickEventArgs) columnClick; 1578 Event!(ListView, LabelEditEventArgs) afterLabelEdit; 1579 Event!(ListView, LabelEditEventArgs) beforeLabelEdit; 1580 //Event!(ListView, ItemCheckEventArgs) itemCheck; 1581 Event!(ListView, ItemCheckedEventArgs) itemChecked; 1582 Event!(ListView, EventArgs) selectedIndexChanged; 1583 1584 protected void onColumnClick(ColumnClickEventArgs ea) { 1585 columnClick(this, ea); 1586 } 1587 1588 protected void onAfterLabelEdit(LabelEditEventArgs ea) { 1589 afterLabelEdit(this, ea); 1590 } 1591 1592 protected void onBeforeLabelEdit(LabelEditEventArgs ea) { 1593 beforeLabelEdit(this, ea); 1594 } 1595 1596 /+ 1597 protected void onItemCheck(ItemCheckEventArgs ea) { 1598 itemCheck(this, ea); 1599 } 1600 +/ 1601 1602 protected void onItemChecked(ItemCheckedEventArgs ea) { 1603 itemChecked(this, ea); 1604 } 1605 1606 protected void onSelectedIndexChanged(EventArgs ea) { 1607 selectedIndexChanged(this, ea); 1608 } 1609 1610 protected override @property Size defaultSize() { 1611 return Size(120, 95); 1612 } 1613 1614 static @property Color defaultBackColor() { 1615 return SystemColors.window; 1616 } 1617 1618 static @property Color defaultForeColor() { 1619 return SystemColors.windowText; 1620 } 1621 1622 protected override void createParams(ref CreateParams cp) { 1623 super.createParams(cp); 1624 1625 cp.className = LISTVIEW_CLASSNAME; 1626 } 1627 1628 protected override void prevWndProc(ref Message msg) { 1629 switch (msg.msg) { 1630 case WM_MOUSEHOVER: 1631 if (!hoverSelection) { 1632 return; 1633 } 1634 break; 1635 1636 default: 1637 } 1638 1639 //msg.result = CallWindowProcA(listviewPrevWndProc, msg.hWnd, msg.msg, msg.wParam, msg.lParam); 1640 msg.result = dfl.internal.utf.callWindowProc(listviewPrevWndProc, 1641 msg.hWnd, msg.msg, msg.wParam, msg.lParam); 1642 } 1643 1644 protected override void wndProc(ref Message m) { 1645 // TODO: support the listview messages. 1646 1647 switch (m.msg) { 1648 /+ 1649 case WM_PAINT: 1650 // This seems to be the only way to display columns correctly. 1651 prevWndProc(m); 1652 return; 1653 +/ 1654 1655 case LVM_ARRANGE: 1656 m.result = FALSE; 1657 return; 1658 1659 case LVM_DELETEALLITEMS: 1660 litems.clear(); 1661 m.result = TRUE; 1662 return; 1663 1664 case LVM_DELETECOLUMN: 1665 cols.removeAt(cast(int) m.wParam); 1666 m.result = TRUE; 1667 return; 1668 1669 case LVM_DELETEITEM: 1670 litems.removeAt(cast(int) m.wParam); 1671 m.result = TRUE; 1672 return; 1673 1674 case LVM_INSERTCOLUMNA: 1675 case LVM_INSERTCOLUMNW: 1676 m.result = -1; 1677 return; 1678 1679 case LVM_INSERTITEMA: 1680 case LVM_INSERTITEMW: 1681 m.result = -1; 1682 return; 1683 1684 case LVM_SETBKCOLOR: 1685 backColor = Color.fromRgb(cast(COLORREF) m.lParam); 1686 m.result = TRUE; 1687 return; 1688 1689 case LVM_SETCALLBACKMASK: 1690 m.result = FALSE; 1691 return; 1692 1693 case LVM_SETCOLUMNA: 1694 case LVM_SETCOLUMNW: 1695 m.result = FALSE; 1696 return; 1697 1698 case LVM_SETCOLUMNWIDTH: 1699 return; 1700 1701 case LVM_SETIMAGELIST: 1702 m.result = cast(LRESULT) 0; 1703 return; 1704 1705 case LVM_SETITEMA: 1706 m.result = FALSE; 1707 return; 1708 1709 case LVM_SETITEMSTATE: 1710 m.result = FALSE; 1711 return; 1712 1713 case LVM_SETITEMTEXTA: 1714 case LVM_SETITEMTEXTW: 1715 m.result = FALSE; 1716 return; 1717 1718 //case LVM_SETTEXTBKCOLOR: 1719 1720 case LVM_SETTEXTCOLOR: 1721 foreColor = Color.fromRgb(cast(COLORREF) m.lParam); 1722 m.result = TRUE; 1723 return; 1724 1725 case LVM_SORTITEMS: 1726 m.result = FALSE; 1727 return; 1728 1729 default: 1730 } 1731 super.wndProc(m); 1732 } 1733 1734 protected override void onHandleCreated(EventArgs ea) { 1735 super.onHandleCreated(ea); 1736 1737 //SendMessageA(hwnd, LVM_SETEXTENDEDLISTVIEWSTYLE, wlvexstyle, wlvexstyle); 1738 prevwproc(LVM_SETEXTENDEDLISTVIEWSTYLE, 0, wlvexstyle); // wparam=0 sets all. 1739 1740 Color color; 1741 COLORREF cref; 1742 1743 color = backColor; 1744 if (Color.empty == color) { 1745 cref = CLR_NONE; 1746 } else { 1747 cref = color.toRgb(); 1748 } 1749 prevwproc(LVM_SETBKCOLOR, 0, cast(LPARAM) cref); 1750 prevwproc(LVM_SETTEXTBKCOLOR, 0, cast(LPARAM) cref); 1751 1752 //prevwproc(LVM_SETTEXTCOLOR, 0, foreColor.toRgb()); // DMD 0.125: cast(Control )(this).foreColor() is not an lvalue 1753 color = foreColor; 1754 prevwproc(LVM_SETTEXTCOLOR, 0, cast(LPARAM) color.toRgb()); 1755 1756 version (DFL_NO_IMAGELIST) { 1757 } else { 1758 if (_lgimglist) { 1759 prevwproc(LVM_SETIMAGELIST, LVSIL_NORMAL, cast(LPARAM) _lgimglist.handle); 1760 } 1761 if (_smimglist) { 1762 prevwproc(LVM_SETIMAGELIST, LVSIL_SMALL, cast(LPARAM) _smimglist.handle); 1763 } 1764 //if(_stimglist) 1765 // prevwproc(LVM_SETIMAGELIST, LVSIL_STATE, cast(LPARAM)_stimglist.handle); 1766 } 1767 1768 cols.doListHeaders(); 1769 litems.doListItems(); 1770 1771 recalcEntire(); // Fix frame. 1772 } 1773 1774 protected override void onReflectedMessage(ref Message m) { 1775 super.onReflectedMessage(m); 1776 1777 switch (m.msg) { 1778 case WM_NOTIFY: { 1779 NMHDR* nmh; 1780 nmh = cast(NMHDR*) m.lParam; 1781 switch (nmh.code) { 1782 case LVN_GETDISPINFOA: 1783 if (dfl.internal.utf.useUnicode) { 1784 break; 1785 } else { 1786 LV_DISPINFOA* lvdi; 1787 lvdi = cast(LV_DISPINFOA*) nmh; 1788 1789 // Note: might want to verify it's a valid ListViewItem. 1790 1791 ListViewItem item; 1792 item = cast(ListViewItem) cast(void*) lvdi.item.lParam; 1793 1794 if (!lvdi.item.iSubItem) { // Item. 1795 version (DFL_NO_IMAGELIST) { 1796 } else { 1797 if (lvdi.item.mask & LVIF_IMAGE) { 1798 lvdi.item.iImage = item._imgidx; 1799 } 1800 } 1801 1802 if (lvdi.item.mask & LVIF_TEXT) { 1803 lvdi.item.pszText = cast(typeof(lvdi.item.pszText)) item.calltxt.ansi; 1804 } 1805 } else { // Sub item. 1806 if (lvdi.item.mask & LVIF_TEXT) { 1807 if (lvdi.item.iSubItem <= item.subItems.length) { 1808 lvdi.item.pszText = cast( 1809 typeof(lvdi.item.pszText)) item.subItems[lvdi.item.iSubItem 1810 - 1].calltxt.ansi; 1811 } 1812 } 1813 } 1814 break; 1815 } 1816 1817 case LVN_GETDISPINFOW: { 1818 Dstring text; 1819 LV_DISPINFOW* lvdi; 1820 lvdi = cast(LV_DISPINFOW*) nmh; 1821 1822 // Note: might want to verify it's a valid ListViewItem. 1823 1824 ListViewItem item; 1825 item = cast(ListViewItem) cast(void*) lvdi.item.lParam; 1826 1827 if (!lvdi.item.iSubItem) { // Item. 1828 version (DFL_NO_IMAGELIST) { 1829 } else { 1830 if (lvdi.item.mask & LVIF_IMAGE) { 1831 lvdi.item.iImage = item._imgidx; 1832 } 1833 } 1834 1835 if (lvdi.item.mask & LVIF_TEXT) { 1836 lvdi.item.pszText = cast(typeof(lvdi.item.pszText)) item.calltxt.unicode; 1837 } 1838 } else { // Sub item. 1839 if (lvdi.item.mask & LVIF_TEXT) { 1840 if (lvdi.item.iSubItem <= item.subItems.length) { 1841 lvdi.item.pszText = cast( 1842 typeof(lvdi.item.pszText)) item.subItems[lvdi.item.iSubItem 1843 - 1].calltxt.unicode; 1844 } 1845 } 1846 } 1847 } 1848 break; 1849 1850 /+ 1851 case LVN_ITEMCHANGING: { 1852 auto nmlv = cast(NM_LISTVIEW*)nmh; 1853 if(-1 != nmlv.iItem) { 1854 UINT stchg = nmlv.uNewState ^ nmlv.uOldState; 1855 if(stchg & (3 << 12)) { 1856 // Note: not tested. 1857 scope ItemCheckEventArgs ea = new ItemCheckEventArgs(nmlv.iItem, 1858 (((nmlv.uNewState >> 12) & 3) - 1) ? CheckState.CHECKED : CheckState.UNCHECKED, 1859 (((nmlv.uOldState >> 12) & 3) - 1) ? CheckState.CHECKED : CheckState.UNCHECKED); 1860 onItemCheck(ea); 1861 } 1862 } 1863 } 1864 break; 1865 +/ 1866 1867 case LVN_ITEMCHANGED: { 1868 auto nmlv = cast(NM_LISTVIEW*) nmh; 1869 if (-1 != nmlv.iItem) { 1870 if (nmlv.uChanged & LVIF_STATE) { 1871 UINT stchg = nmlv.uNewState ^ nmlv.uOldState; 1872 1873 //if(stchg & LVIS_SELECTED) 1874 { 1875 // Only fire for the selected one; don't fire twice for old/new. 1876 if (nmlv.uNewState & LVIS_SELECTED) { 1877 onSelectedIndexChanged(EventArgs.empty); 1878 } 1879 } 1880 1881 if (stchg & (3 << 12)) { 1882 scope ItemCheckedEventArgs ea = new ItemCheckedEventArgs( 1883 items[nmlv.iItem]); 1884 onItemChecked(ea); 1885 } 1886 } 1887 } 1888 } 1889 break; 1890 1891 case LVN_COLUMNCLICK: { 1892 auto nmlv = cast(NM_LISTVIEW*) nmh; 1893 scope ccea = new ColumnClickEventArgs(nmlv.iSubItem); 1894 onColumnClick(ccea); 1895 } 1896 break; 1897 1898 case LVN_BEGINLABELEDITW: 1899 goto begin_label_edit; 1900 1901 case LVN_BEGINLABELEDITA: 1902 if (dfl.internal.utf.useUnicode) { 1903 break; 1904 } 1905 begin_label_edit: { 1906 LV_DISPINFOA* nmdi; 1907 nmdi = cast(LV_DISPINFOA*) nmh; 1908 if (nmdi.item.iSubItem) { 1909 m.result = TRUE; 1910 break; 1911 } 1912 ListViewItem lvitem; 1913 lvitem = cast(ListViewItem) cast(void*) nmdi.item.lParam; 1914 scope LabelEditEventArgs leea = new LabelEditEventArgs(lvitem); 1915 onBeforeLabelEdit(leea); 1916 m.result = leea.cancelEdit; 1917 } 1918 break; 1919 1920 case LVN_ENDLABELEDITW: { 1921 Dstring label; 1922 LV_DISPINFOW* nmdi; 1923 nmdi = cast(LV_DISPINFOW*) nmh; 1924 if (nmdi.item.pszText) { 1925 ListViewItem lvitem; 1926 lvitem = cast(ListViewItem) cast(void*) nmdi.item.lParam; 1927 if (nmdi.item.iSubItem) { 1928 m.result = FALSE; 1929 break; 1930 } 1931 label = fromUnicodez(nmdi.item.pszText); 1932 scope LabelEditEventArgs nleea = new LabelEditEventArgs(lvitem, 1933 label); 1934 onAfterLabelEdit(nleea); 1935 if (nleea.cancelEdit) { 1936 m.result = FALSE; 1937 } else { 1938 // TODO: check if correct implementation. 1939 // Update the lvitem's cached text.. 1940 lvitem.settextin(label); 1941 1942 m.result = TRUE; 1943 } 1944 } 1945 } 1946 break; 1947 1948 case LVN_ENDLABELEDITA: 1949 if (dfl.internal.utf.useUnicode) { 1950 break; 1951 } else { 1952 Dstring label; 1953 LV_DISPINFOA* nmdi; 1954 nmdi = cast(LV_DISPINFOA*) nmh; 1955 if (nmdi.item.pszText) { 1956 ListViewItem lvitem; 1957 lvitem = cast(ListViewItem) cast(void*) nmdi.item.lParam; 1958 if (nmdi.item.iSubItem) { 1959 m.result = FALSE; 1960 break; 1961 } 1962 label = fromAnsiz(nmdi.item.pszText); 1963 scope LabelEditEventArgs nleea = new LabelEditEventArgs(lvitem, 1964 label); 1965 onAfterLabelEdit(nleea); 1966 if (nleea.cancelEdit) { 1967 m.result = FALSE; 1968 } else { 1969 // TODO: check if correct implementation. 1970 // Update the lvitem's cached text.. 1971 lvitem.settextin(label); 1972 1973 m.result = TRUE; 1974 } 1975 } 1976 break; 1977 } 1978 1979 default: 1980 } 1981 } 1982 break; 1983 1984 default: 1985 } 1986 } 1987 1988 private: 1989 DWORD wlvexstyle = 0; 1990 ListViewItemCollection litems; 1991 ColumnHeaderCollection cols; 1992 SelectedIndexCollection selidxcollection; 1993 SelectedItemCollection selobjcollection; 1994 SortOrder _sortorder = SortOrder.NONE; 1995 CheckedIndexCollection checkedis; 1996 int delegate(ListViewItem, ListViewItem) _sortproc; 1997 version (DFL_NO_IMAGELIST) { 1998 } else { 1999 ImageList _lgimglist, _smimglist; 2000 //ImageList _stimglist; 2001 } 2002 2003 int _defsortproc(ListViewItem a, ListViewItem b) { 2004 return a.opCmp(b); 2005 } 2006 2007 DWORD _lvexstyle() { 2008 //if(created) 2009 // wlvexstyle = cast(DWORD)SendMessageA(hwnd, LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0); 2010 // wlvexstyle = cast(DWORD)prevwproc(LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0); 2011 return wlvexstyle; 2012 } 2013 2014 void _lvexstyle(DWORD flags) { 2015 DWORD _b4; 2016 _b4 = wlvexstyle; 2017 2018 wlvexstyle = flags; 2019 if (created) { 2020 // hwnd, msg, mask, flags 2021 //SendMessageA(hwnd, LVM_SETEXTENDEDLISTVIEWSTYLE, flags ^ _b4, wlvexstyle); 2022 prevwproc(LVM_SETEXTENDEDLISTVIEWSTYLE, flags ^ _b4, wlvexstyle); 2023 //redrawEntire(); // Need to recalc the frame ? 2024 } 2025 } 2026 2027 void _lvexstyle(DWORD mask, DWORD flags) 2028 in { 2029 assert(mask); 2030 } 2031 body { 2032 wlvexstyle = (wlvexstyle & ~mask) | (flags & mask); 2033 if (created) { 2034 // hwnd, msg, mask, flags 2035 //SendMessageA(hwnd, LVM_SETEXTENDEDLISTVIEWSTYLE, mask, flags); 2036 prevwproc(LVM_SETEXTENDEDLISTVIEWSTYLE, mask, flags); 2037 //redrawEntire(); // Need to recalc the frame ? 2038 } 2039 } 2040 2041 // If -subItemIndex- is 0 it's an item not a sub item. 2042 // Returns the insertion index or -1 on failure. 2043 package final LRESULT _ins(int index, LPARAM lparam, Dstring itemText, 2044 int subItemIndex, int imageIndex = -1) 2045 in { 2046 assert(created); 2047 } 2048 body { 2049 /+ 2050 cprintf("^ Insert item: index=%d, lparam=0x%X, text='%.*s', subItemIndex=%d\n", 2051 index, lparam, itemText.length > 20 ? 20 : itemText.length, cast(char*)itemText, subItemIndex); 2052 +/ 2053 2054 LV_ITEMA lvi; 2055 lvi.mask = LVIF_TEXT | LVIF_PARAM; 2056 version (DFL_NO_IMAGELIST) { 2057 } else { 2058 //if(-1 != imageIndex) 2059 if (!subItemIndex) { 2060 lvi.mask |= LVIF_IMAGE; 2061 } 2062 //lvi.iImage = imageIndex; 2063 lvi.iImage = I_IMAGECALLBACK; 2064 } 2065 lvi.iItem = index; 2066 lvi.iSubItem = subItemIndex; 2067 //lvi.pszText = toStringz(itemText); 2068 lvi.pszText = LPSTR_TEXTCALLBACKA; 2069 lvi.lParam = lparam; 2070 return prevwproc(LVM_INSERTITEMA, 0, cast(LPARAM)&lvi); 2071 } 2072 2073 package final LRESULT _ins(int index, ListViewItem item) { 2074 //return _ins(index, cast(LPARAM)cast(void*)item, item.text, 0); 2075 version (DFL_NO_IMAGELIST) { 2076 return _ins(index, cast(LPARAM) cast(void*) item, item.text, 0, -1); 2077 } else { 2078 return _ins(index, cast(LPARAM) cast(void*) item, item.text, 0, item._imgidx); 2079 } 2080 } 2081 2082 package final LRESULT _ins(int index, ListViewSubItem subItem, int subItemIndex) 2083 in { 2084 assert(subItemIndex > 0); 2085 } 2086 body { 2087 return _ins(index, cast(LPARAM) cast(void*) subItem, subItem.text, subItemIndex); 2088 } 2089 2090 package final LRESULT _ins(int index, ColumnHeader header) { 2091 // TODO: column inserted at index 0 can only be left aligned, so will need to 2092 // insert a dummy column to change the alignment, then delete the dummy column. 2093 2094 //LV_COLUMNA lvc; 2095 LvColumn lvc; 2096 lvc.mask = LVCF_FMT | LVCF_SUBITEM | LVCF_TEXT | LVCF_WIDTH; 2097 switch (header.textAlign) { 2098 case HorizontalAlignment.RIGHT: 2099 lvc.fmt = LVCFMT_RIGHT; 2100 break; 2101 2102 case HorizontalAlignment.CENTER: 2103 lvc.fmt = LVCFMT_CENTER; 2104 break; 2105 2106 default: 2107 lvc.fmt = LVCFMT_LEFT; 2108 } 2109 lvc.cx = header.width; 2110 lvc.iSubItem = index; // iSubItem is probably only used when retrieving column info. 2111 if (dfl.internal.utf.useUnicode) { 2112 lvc.lvcw.pszText = cast(typeof(lvc.lvcw.pszText)) dfl.internal.utf.toUnicodez(header.text); 2113 return prevwproc(LVM_INSERTCOLUMNW, cast(WPARAM) index, cast(LPARAM)&lvc.lvcw); 2114 } else { 2115 lvc.lvca.pszText = cast(typeof(lvc.lvca.pszText)) dfl.internal.utf.toAnsiz(header.text); 2116 return prevwproc(LVM_INSERTCOLUMNA, cast(WPARAM) index, cast(LPARAM)&lvc.lvca); 2117 } 2118 } 2119 2120 // If -subItemIndex- is 0 it's an item not a sub item. 2121 // Returns FALSE on failure. 2122 LRESULT updateItem(int index) 2123 in { 2124 assert(created); 2125 } 2126 body { 2127 return prevwproc(LVM_REDRAWITEMS, cast(WPARAM) index, cast(LPARAM) index); 2128 } 2129 2130 LRESULT updateItem(ListViewItem item) { 2131 int index; 2132 index = item.index; 2133 assert(-1 != index); 2134 return updateItem(index); 2135 } 2136 2137 LRESULT updateItemText(int index, Dstring newText, int subItemIndex = 0) { 2138 return updateItem(index); 2139 } 2140 2141 LRESULT updateItemText(ListViewItem item, Dstring newText, int subItemIndex = 0) { 2142 return updateItem(item); 2143 } 2144 2145 LRESULT updateColumnText(int colIndex, Dstring newText) { 2146 //LV_COLUMNA lvc; 2147 LvColumn lvc; 2148 2149 lvc.mask = LVCF_TEXT; 2150 if (dfl.internal.utf.useUnicode) { 2151 lvc.lvcw.pszText = cast(typeof(lvc.lvcw.pszText)) dfl.internal.utf.toUnicodez(newText); 2152 return prevwproc(LVM_SETCOLUMNW, cast(WPARAM) colIndex, cast(LPARAM)&lvc.lvcw); 2153 } else { 2154 lvc.lvca.pszText = cast(typeof(lvc.lvca.pszText)) dfl.internal.utf.toAnsiz(newText); 2155 return prevwproc(LVM_SETCOLUMNA, cast(WPARAM) colIndex, cast(LPARAM)&lvc.lvca); 2156 } 2157 } 2158 2159 LRESULT updateColumnText(ColumnHeader col, Dstring newText) { 2160 int colIndex; 2161 colIndex = columns.indexOf(col); 2162 assert(-1 != colIndex); 2163 return updateColumnText(colIndex, newText); 2164 } 2165 2166 LRESULT updateColumnAlign(int colIndex, HorizontalAlignment halign) { 2167 LV_COLUMNA lvc; 2168 lvc.mask = LVCF_FMT; 2169 switch (halign) { 2170 case HorizontalAlignment.RIGHT: 2171 lvc.fmt = LVCFMT_RIGHT; 2172 break; 2173 2174 case HorizontalAlignment.CENTER: 2175 lvc.fmt = LVCFMT_CENTER; 2176 break; 2177 2178 default: 2179 lvc.fmt = LVCFMT_LEFT; 2180 } 2181 return prevwproc(LVM_SETCOLUMNA, cast(WPARAM) colIndex, cast(LPARAM)&lvc); 2182 } 2183 2184 LRESULT updateColumnAlign(ColumnHeader col, HorizontalAlignment halign) { 2185 int colIndex; 2186 colIndex = columns.indexOf(col); 2187 assert(-1 != colIndex); 2188 return updateColumnAlign(colIndex, halign); 2189 } 2190 2191 LRESULT updateColumnWidth(int colIndex, int w) { 2192 LV_COLUMNA lvc; 2193 lvc.mask = LVCF_WIDTH; 2194 lvc.cx = w; 2195 return prevwproc(LVM_SETCOLUMNA, cast(WPARAM) colIndex, cast(LPARAM)&lvc); 2196 } 2197 2198 LRESULT updateColumnWidth(ColumnHeader col, int w) { 2199 int colIndex; 2200 colIndex = columns.indexOf(col); 2201 assert(-1 != colIndex); 2202 return updateColumnWidth(colIndex, w); 2203 } 2204 2205 int getColumnWidth(int colIndex) { 2206 LV_COLUMNA lvc; 2207 lvc.mask = LVCF_WIDTH; 2208 lvc.cx = -1; 2209 prevwproc(LVM_GETCOLUMNA, cast(WPARAM) colIndex, cast(LPARAM)&lvc); 2210 return lvc.cx; 2211 } 2212 2213 int getColumnWidth(ColumnHeader col) { 2214 int colIndex; 2215 colIndex = columns.indexOf(col); 2216 assert(-1 != colIndex); 2217 return getColumnWidth(colIndex); 2218 } 2219 2220 package: 2221 final: 2222 LRESULT prevwproc(UINT msg, WPARAM wparam, LPARAM lparam) { 2223 //return CallWindowProcA(listviewPrevWndProc, hwnd, msg, wparam, lparam); 2224 return dfl.internal.utf.callWindowProc(listviewPrevWndProc, hwnd, msg, wparam, 2225 lparam); 2226 } 2227 }