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