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