1 // Written by Christopher E. Miller 2 // See the included license.txt for copyright and license details. 3 4 5 6 module dfl.listbox; 7 8 private import dfl.internal.dlib; 9 10 private import dfl.internal.winapi, dfl.control, dfl.base, dfl.application; 11 private import dfl.drawing, dfl.event, dfl.collections; 12 13 private import std.algorithm; 14 15 private extern(C) void* memmove(void*, void*, size_t len); 16 17 private extern(Windows) void _initListbox(); 18 19 20 alias StringObject ListString; 21 22 23 24 abstract class ListControl: ControlSuperClass { // docmain 25 26 final Dstring getItemText(Object item) { 27 return getObjectString(item); 28 } 29 30 31 //EventHandler selectedValueChanged; 32 Event!(ListControl, EventArgs) selectedValueChanged; /// 33 34 35 36 abstract @property void selectedIndex(int idx); // setter 37 /// ditto 38 abstract @property int selectedIndex(); // getter 39 40 41 abstract @property void selectedValue(Object val); // setter 42 /// ditto 43 44 45 abstract @property void selectedValue(Dstring str); // setter 46 /// ditto 47 abstract @property Object selectedValue(); // getter 48 49 50 static @property Color defaultBackColor() { // getter 51 return SystemColors.window; 52 } 53 54 55 override @property Color backColor() { // getter 56 if(Color.empty == backc) { 57 return defaultBackColor; 58 } 59 return backc; 60 } 61 62 alias Control.backColor backColor; // Overload. 63 64 65 static @property Color defaultForeColor() { //getter 66 return SystemColors.windowText; 67 } 68 69 70 override @property Color foreColor() { // getter 71 if(Color.empty == forec) { 72 return defaultForeColor; 73 } 74 return forec; 75 } 76 77 alias Control.foreColor foreColor; // Overload. 78 79 80 this() { 81 } 82 83 84 protected: 85 86 87 void onSelectedValueChanged(EventArgs ea) { 88 selectedValueChanged(this, ea); 89 } 90 91 92 93 // Index change causes the value to be changed. 94 void onSelectedIndexChanged(EventArgs ea) { 95 onSelectedValueChanged(ea); // This appears to be correct. 96 } 97 } 98 99 100 101 enum SelectionMode: ubyte { 102 ONE, /// 103 NONE, /// ditto 104 MULTI_SIMPLE, /// ditto 105 MULTI_EXTENDED, /// ditto 106 } 107 108 109 110 class ListBox: ListControl { // docmain 111 112 static class SelectedIndexCollection { 113 deprecated alias length count; 114 115 @property int length() { // getter 116 if(!lbox.isHandleCreated) { 117 return 0; 118 } 119 120 if(lbox.isMultSel()) { 121 return lbox.prevwproc(LB_GETSELCOUNT, 0, 0); 122 } else { 123 return (lbox.selectedIndex == -1) ? 0 : 1; 124 } 125 } 126 127 128 int opIndex(int idx) { 129 foreach(int onidx; this) { 130 if(!idx) { 131 return onidx; 132 } 133 idx--; 134 } 135 136 // If it's not found it's out of bounds and bad things happen. 137 assert(0); 138 } 139 140 141 bool contains(int idx) { 142 return indexOf(idx) != -1; 143 } 144 145 146 int indexOf(int idx) { 147 int i = 0; 148 foreach(int onidx; this) { 149 if(onidx == idx) { 150 return i; 151 } 152 i++; 153 } 154 return -1; 155 } 156 157 158 int opApply(int delegate(ref int) dg) { 159 int result = 0; 160 161 if(lbox.isMultSel()) { 162 int[] items; 163 items = new int[length]; 164 if(items.length != lbox.prevwproc(LB_GETSELITEMS, items.length, cast(LPARAM)cast(int*)items)) { 165 throw new DflException("Unable to enumerate selected list items"); 166 } 167 foreach(int _idx; items) { 168 int idx = _idx; // Prevent ref. 169 result = dg(idx); 170 if(result) { 171 break; 172 } 173 } 174 } else { 175 int idx; 176 idx = lbox.selectedIndex; 177 if(-1 != idx) { 178 result = dg(idx); 179 } 180 } 181 return result; 182 } 183 184 mixin OpApplyAddIndex!(opApply, int); 185 186 187 protected this(ListBox lb) { 188 lbox = lb; 189 } 190 191 192 package: 193 ListBox lbox; 194 } 195 196 197 198 static class SelectedObjectCollection { 199 deprecated alias length count; 200 201 @property int length() { // getter 202 if(!lbox.isHandleCreated) { 203 return 0; 204 } 205 206 if(lbox.isMultSel()) { 207 return lbox.prevwproc(LB_GETSELCOUNT, 0, 0); 208 } else { 209 return (lbox.selectedIndex == -1) ? 0 : 1; 210 } 211 } 212 213 214 Object opIndex(int idx) { 215 foreach(Object obj; this) { 216 if(!idx) { 217 return obj; 218 } 219 idx--; 220 } 221 222 // If it's not found it's out of bounds and bad things happen. 223 assert(0); 224 } 225 226 227 bool contains(Object obj) { 228 return indexOf(obj) != -1; 229 } 230 231 232 bool contains(Dstring str) { 233 return indexOf(str) != -1; 234 } 235 236 237 int indexOf(Object obj) { 238 int idx = 0; 239 foreach(Object onobj; this) { 240 if(onobj == obj) { // Not using is. 241 return idx; 242 } 243 idx++; 244 } 245 return -1; 246 } 247 248 249 int indexOf(Dstring str) { 250 int idx = 0; 251 foreach(Object onobj; this) { 252 //if(getObjectString(onobj) is str && getObjectString(onobj).length == str.length) 253 if(getObjectString(onobj) == str) { 254 return idx; 255 } 256 idx++; 257 } 258 return -1; 259 } 260 261 262 // Used internally. 263 int _opApply(int delegate(ref Object) dg) { // package 264 int result = 0; 265 266 if(lbox.isMultSel()) { 267 int[] items; 268 items = new int[length]; 269 if(items.length != lbox.prevwproc(LB_GETSELITEMS, items.length, cast(LPARAM)cast(int*)items)) { 270 throw new DflException("Unable to enumerate selected list items"); 271 } 272 foreach(int idx; items) { 273 Object obj; 274 obj = lbox.items[idx]; 275 result = dg(obj); 276 if(result) { 277 break; 278 } 279 } 280 } else { 281 Object obj; 282 obj = lbox.selectedItem; 283 if(obj) { 284 result = dg(obj); 285 } 286 } 287 return result; 288 } 289 290 291 // Used internally. 292 int _opApply(int delegate(ref Dstring) dg) { // package 293 int result = 0; 294 295 if(lbox.isMultSel()) { 296 int[] items; 297 items = new int[length]; 298 if(items.length != lbox.prevwproc(LB_GETSELITEMS, items.length, cast(LPARAM)cast(int*)items)) { 299 throw new DflException("Unable to enumerate selected list items"); 300 } 301 foreach(int idx; items) { 302 Dstring str; 303 str = getObjectString(lbox.items[idx]); 304 result = dg(str); 305 if(result) { 306 break; 307 } 308 } 309 } else { 310 Object obj; 311 Dstring str; 312 obj = lbox.selectedItem; 313 if(obj) { 314 str = getObjectString(obj); 315 result = dg(str); 316 } 317 } 318 return result; 319 } 320 321 mixin OpApplyAddIndex!(_opApply, Dstring); 322 323 mixin OpApplyAddIndex!(_opApply, Object); 324 325 // Had to do it this way because: DMD 1.028: -H is broken for mixin identifiers 326 // Note that this way probably prevents opApply from being overridden. 327 alias _opApply opApply; 328 329 330 protected this(ListBox lb) { 331 lbox = lb; 332 } 333 334 335 package: 336 ListBox lbox; 337 } 338 339 340 341 enum int DEFAULT_ITEM_HEIGHT = 13; 342 343 enum int NO_MATCHES = LB_ERR; 344 345 346 protected override @property Size defaultSize() { // getter 347 return Size(120, 95); 348 } 349 350 351 352 @property void borderStyle(BorderStyle bs) { // setter 353 final switch(bs) { 354 case BorderStyle.FIXED_3D: 355 _style(_style() & ~WS_BORDER); 356 _exStyle(_exStyle() | WS_EX_CLIENTEDGE); 357 break; 358 359 case BorderStyle.FIXED_SINGLE: 360 _exStyle(_exStyle() & ~WS_EX_CLIENTEDGE); 361 _style(_style() | WS_BORDER); 362 break; 363 364 case BorderStyle.NONE: 365 _style(_style() & ~WS_BORDER); 366 _exStyle(_exStyle() & ~WS_EX_CLIENTEDGE); 367 break; 368 } 369 370 if(isHandleCreated) { 371 redrawEntire(); 372 } 373 } 374 375 /// ditto 376 @property BorderStyle borderStyle() { // getter 377 if(_exStyle() & WS_EX_CLIENTEDGE) { 378 return BorderStyle.FIXED_3D; 379 } else if(_style() & WS_BORDER) { 380 return BorderStyle.FIXED_SINGLE; 381 } 382 return BorderStyle.NONE; 383 } 384 385 386 387 @property void drawMode(DrawMode dm) { // setter 388 LONG wl = _style() & ~(LBS_OWNERDRAWVARIABLE | LBS_OWNERDRAWFIXED); 389 390 final switch(dm) { 391 case DrawMode.OWNER_DRAW_VARIABLE: 392 wl |= LBS_OWNERDRAWVARIABLE; 393 break; 394 395 case DrawMode.OWNER_DRAW_FIXED: 396 wl |= LBS_OWNERDRAWFIXED; 397 break; 398 399 case DrawMode.NORMAL: 400 break; 401 } 402 403 _style(wl); 404 405 _crecreate(); 406 } 407 408 /// ditto 409 @property DrawMode drawMode() { // getter 410 LONG wl = _style(); 411 412 if(wl & LBS_OWNERDRAWVARIABLE) { 413 return DrawMode.OWNER_DRAW_VARIABLE; 414 } 415 if(wl & LBS_OWNERDRAWFIXED) { 416 return DrawMode.OWNER_DRAW_FIXED; 417 } 418 return DrawMode.NORMAL; 419 } 420 421 422 423 final @property void horizontalExtent(int he) { // setter 424 if(isHandleCreated) { 425 prevwproc(LB_SETHORIZONTALEXTENT, he, 0); 426 } 427 428 hextent = he; 429 } 430 431 /// ditto 432 final @property int horizontalExtent() { // getter 433 if(isHandleCreated) { 434 hextent = cast(int)prevwproc(LB_GETHORIZONTALEXTENT, 0, 0); 435 } 436 return hextent; 437 } 438 439 440 441 final @property void horizontalScrollbar(bool byes) { // setter 442 if(byes) { 443 _style(_style() | WS_HSCROLL); 444 } else { 445 _style(_style() & ~WS_HSCROLL); 446 } 447 448 _crecreate(); 449 } 450 451 /// ditto 452 final @property bool horizontalScrollbar() { // getter 453 return (_style() & WS_HSCROLL) != 0; 454 } 455 456 457 458 final @property void integralHeight(bool byes) { //setter 459 if(byes) { 460 _style(_style() & ~LBS_NOINTEGRALHEIGHT); 461 } else { 462 _style(_style() | LBS_NOINTEGRALHEIGHT); 463 } 464 465 _crecreate(); 466 } 467 468 /// ditto 469 final @property bool integralHeight() { // getter 470 return (_style() & LBS_NOINTEGRALHEIGHT) == 0; 471 } 472 473 474 475 // This function has no effect if the drawMode is OWNER_DRAW_VARIABLE. 476 final @property void itemHeight(int h) { // setter 477 if(drawMode == DrawMode.OWNER_DRAW_VARIABLE) { 478 return; 479 } 480 481 iheight = h; 482 483 if(isHandleCreated) { 484 prevwproc(LB_SETITEMHEIGHT, 0, MAKELPARAM(h, 0)); 485 } 486 } 487 488 /// ditto 489 // Return value is meaningless when drawMode is OWNER_DRAW_VARIABLE. 490 final @property int itemHeight() { // getter 491 // Requesting it like this when owner draw variable doesn't work. 492 /+ 493 if(!isHandleCreated) { 494 return iheight; 495 } 496 497 int result = prevwproc(LB_GETITEMHEIGHT, 0, 0); 498 if(result == LB_ERR) { 499 result = iheight; // ? 500 } else { 501 iheight = result; 502 } 503 504 return result; 505 +/ 506 507 return iheight; 508 } 509 510 511 512 final @property ObjectCollection items() { // getter 513 return icollection; 514 } 515 516 517 518 final @property void multiColumn(bool byes) { // setter 519 // TODO: is this the correct implementation? 520 521 if(byes) { 522 _style(_style() | LBS_MULTICOLUMN | WS_HSCROLL); 523 } else { 524 _style(_style() & ~(LBS_MULTICOLUMN | WS_HSCROLL)); 525 } 526 527 _crecreate(); 528 } 529 530 /// ditto 531 final @property bool multiColumn() { // getter 532 return (_style() & LBS_MULTICOLUMN) != 0; 533 } 534 535 536 537 final @property void scrollAlwaysVisible(bool byes) { // setter 538 if(byes) { 539 _style(_style() | LBS_DISABLENOSCROLL); 540 } else { 541 _style(_style() & ~LBS_DISABLENOSCROLL); 542 } 543 544 _crecreate(); 545 } 546 547 /// ditto 548 final @property bool scrollAlwaysVisible() { // getter 549 return (_style() & LBS_DISABLENOSCROLL) != 0; 550 } 551 552 553 override @property void selectedIndex(int idx) { // setter 554 if(isHandleCreated) { 555 if(isMultSel()) { 556 if(idx == -1) { 557 // Remove all selection. 558 559 // Not working right. 560 //prevwproc(LB_SELITEMRANGE, false, MAKELPARAM(0, ushort.max)); 561 562 // Get the indices directly because deselecting them during 563 // selidxcollection.foreach could screw it up. 564 565 int[] items; 566 567 items = new int[selidxcollection.length]; 568 if(items.length != prevwproc(LB_GETSELITEMS, items.length, cast(LPARAM)cast(int*)items)) { 569 throw new DflException("Unable to clear selected list items"); 570 } 571 572 foreach(int _idx; items) { 573 prevwproc(LB_SETSEL, false, _idx); 574 } 575 } else { 576 // ? 577 prevwproc(LB_SETSEL, true, idx); 578 } 579 } else { 580 prevwproc(LB_SETCURSEL, idx, 0); 581 } 582 } 583 } 584 585 override @property int selectedIndex() { // getter 586 if(isHandleCreated) { 587 if(isMultSel()) { 588 if(selidxcollection.length) { 589 return selidxcollection[0]; 590 } 591 } else { 592 LRESULT result; 593 result = prevwproc(LB_GETCURSEL, 0, 0); 594 if(LB_ERR != result) { // Redundant. 595 return cast(int)result; 596 } 597 } 598 } 599 return -1; 600 } 601 602 603 604 final @property void selectedItem(Object o) { // setter 605 int i; 606 i = items.indexOf(o); 607 if(i != -1) { 608 selectedIndex = i; 609 } 610 } 611 612 /// ditto 613 final @property void selectedItem(Dstring str) { // setter 614 int i; 615 i = items.indexOf(str); 616 if(i != -1) { 617 selectedIndex = i; 618 } 619 } 620 621 622 final @property Object selectedItem() { // getter 623 int idx; 624 idx = selectedIndex; 625 if(idx == -1) { 626 return null; 627 } 628 return items[idx]; 629 } 630 631 632 override @property void selectedValue(Object val) { // setter 633 selectedItem = val; 634 } 635 636 override @property void selectedValue(Dstring str) { // setter 637 selectedItem = str; 638 } 639 640 override @property Object selectedValue() { // getter 641 return selectedItem; 642 } 643 644 645 646 final @property SelectedIndexCollection selectedIndices() { // getter 647 return selidxcollection; 648 } 649 650 651 652 final @property SelectedObjectCollection selectedItems() { // getter 653 return selobjcollection; 654 } 655 656 657 658 @property void selectionMode(SelectionMode selmode) { // setter 659 LONG wl = _style() & ~(LBS_NOSEL | LBS_EXTENDEDSEL | LBS_MULTIPLESEL); 660 661 final switch(selmode) { 662 case SelectionMode.ONE: 663 break; 664 665 case SelectionMode.MULTI_SIMPLE: 666 wl |= LBS_MULTIPLESEL; 667 break; 668 669 case SelectionMode.MULTI_EXTENDED: 670 wl |= LBS_EXTENDEDSEL; 671 break; 672 673 case SelectionMode.NONE: 674 wl |= LBS_NOSEL; 675 break; 676 } 677 678 _style(wl); 679 680 _crecreate(); 681 } 682 683 /// ditto 684 @property SelectionMode selectionMode() { // getter 685 LONG wl = _style(); 686 687 if(wl & LBS_NOSEL) { 688 return SelectionMode.NONE; 689 } 690 if(wl & LBS_EXTENDEDSEL) { 691 return SelectionMode.MULTI_EXTENDED; 692 } 693 if(wl & LBS_MULTIPLESEL) { 694 return SelectionMode.MULTI_SIMPLE; 695 } 696 return SelectionMode.ONE; 697 } 698 699 700 701 final @property void sorted(bool byes) { // setter 702 /+ 703 if(byes) { 704 _style(_style() | LBS_SORT); 705 } else { 706 _style(_style() & ~LBS_SORT); 707 } 708 +/ 709 _sorting = byes; 710 } 711 712 /// ditto 713 final @property bool sorted() { // getter 714 //return (_style() & LBS_SORT) != 0; 715 return _sorting; 716 } 717 718 719 720 final @property void topIndex(int idx) { // setter 721 if(isHandleCreated) { 722 prevwproc(LB_SETTOPINDEX, idx, 0); 723 } 724 } 725 726 /// ditto 727 final @property int topIndex() { // getter 728 if(isHandleCreated) { 729 return prevwproc(LB_GETTOPINDEX, 0, 0); 730 } 731 return 0; 732 } 733 734 735 736 final @property void useTabStops(bool byes) { // setter 737 if(byes) { 738 _style(_style() | LBS_USETABSTOPS); 739 } else { 740 _style(_style() & ~LBS_USETABSTOPS); 741 } 742 743 _crecreate(); 744 } 745 746 /// ditto 747 final @property bool useTabStops() { // getter 748 return (_style() & LBS_USETABSTOPS) != 0; 749 } 750 751 752 753 final void beginUpdate() { 754 prevwproc(WM_SETREDRAW, false, 0); 755 } 756 757 /// ditto 758 final void endUpdate() { 759 prevwproc(WM_SETREDRAW, true, 0); 760 invalidate(true); // Show updates. 761 } 762 763 764 package final bool isMultSel() { 765 return (_style() & (LBS_EXTENDEDSEL | LBS_MULTIPLESEL)) != 0; 766 } 767 768 769 770 final void clearSelected() { 771 if(created) { 772 selectedIndex = -1; 773 } 774 } 775 776 777 778 final int findString(Dstring str, int startIndex) { 779 // TODO: find string if control not created ? 780 781 int result = NO_MATCHES; 782 783 if(created) { 784 if(dfl.internal.utf.useUnicode) { 785 result = prevwproc(LB_FINDSTRING, startIndex, cast(LPARAM)dfl.internal.utf.toUnicodez(str)); 786 } else { 787 result = prevwproc(LB_FINDSTRING, startIndex, cast(LPARAM)dfl.internal.utf.unsafeAnsiz(str)); 788 } 789 if(result == LB_ERR) { // Redundant. 790 result = NO_MATCHES; 791 } 792 } 793 794 return result; 795 } 796 797 /// ditto 798 final int findString(Dstring str) { 799 return findString(str, -1); // Start at beginning. 800 } 801 802 803 804 final int findStringExact(Dstring str, int startIndex) { 805 // TODO: find string if control not created ? 806 807 int result = NO_MATCHES; 808 809 if(created) { 810 if(dfl.internal.utf.useUnicode) { 811 result = prevwproc(LB_FINDSTRINGEXACT, startIndex, cast(LPARAM)dfl.internal.utf.toUnicodez(str)); 812 } else { 813 result = prevwproc(LB_FINDSTRINGEXACT, startIndex, cast(LPARAM)dfl.internal.utf.unsafeAnsiz(str)); 814 } 815 if(result == LB_ERR) { // Redundant. 816 result = NO_MATCHES; 817 } 818 } 819 820 return result; 821 } 822 823 /// ditto 824 final int findStringExact(Dstring str) { 825 return findStringExact(str, -1); // Start at beginning. 826 } 827 828 829 830 final int getItemHeight(int idx) { 831 int result = prevwproc(LB_GETITEMHEIGHT, idx, 0); 832 if(LB_ERR == result) { 833 throw new DflException("Unable to obtain item height"); 834 } 835 return result; 836 } 837 838 839 840 final Rect getItemRectangle(int idx) { 841 RECT rect; 842 if(LB_ERR == prevwproc(LB_GETITEMRECT, idx, cast(LPARAM)&rect)) { 843 //if(idx >= 0 && idx < items.length) 844 return Rect(0, 0, 0, 0); // ? 845 //throw new DflException("Unable to obtain item rectangle"); 846 } 847 return Rect(&rect); 848 } 849 850 851 852 final bool getSelected(int idx) { 853 return prevwproc(LB_GETSEL, idx, 0) > 0; 854 } 855 856 857 858 final int indexFromPoint(int x, int y) { 859 // LB_ITEMFROMPOINT is "nearest", so also check with the item rectangle to 860 // see if the point is directly in the item. 861 862 // Maybe use LBItemFromPt() from common controls. 863 864 int result = NO_MATCHES; 865 866 if(created) { 867 result = prevwproc(LB_ITEMFROMPOINT, 0, MAKELPARAM(x, y)); 868 if(!HIWORD(result)) { // In client area 869 //result = LOWORD(result); // High word already 0. 870 if(result < 0 || !getItemRectangle(result).contains(x, y)) { 871 result = NO_MATCHES; 872 } 873 } else { // Outside client area. 874 result = NO_MATCHES; 875 } 876 } 877 878 return result; 879 } 880 881 /// ditto 882 final int indexFromPoint(Point pt) { 883 return indexFromPoint(pt.x, pt.y); 884 } 885 886 887 888 final void setSelected(int idx, bool byes) { 889 if(created) { 890 prevwproc(LB_SETSEL, byes, idx); 891 } 892 } 893 894 895 896 protected ObjectCollection createItemCollection() { 897 return new ObjectCollection(this); 898 } 899 900 901 902 void sort() { 903 if(icollection._items.length) { 904 Object[] itemscopy; 905 itemscopy = icollection._items.dup; 906 std.algorithm.sort(itemscopy); 907 908 items.clear(); 909 910 beginUpdate(); 911 scope(exit) 912 endUpdate(); 913 914 foreach(int i, Object o; itemscopy) { 915 items.insert(i, o); 916 } 917 } 918 } 919 920 921 922 static class ObjectCollection { 923 protected this(ListBox lbox) { 924 this.lbox = lbox; 925 } 926 927 928 protected this(ListBox lbox, Object[] range) { 929 this.lbox = lbox; 930 addRange(range); 931 } 932 933 934 protected this(ListBox lbox, Dstring[] range) { 935 this.lbox = lbox; 936 addRange(range); 937 } 938 939 940 /+ 941 protected this(ListBox lbox, ObjectCollection range) { 942 this.lbox = lbox; 943 addRange(range); 944 } 945 +/ 946 947 948 void add(Object value) { 949 add2(value); 950 } 951 952 953 void add(Dstring value) { 954 add(new ListString(value)); 955 } 956 957 958 void addRange(Object[] range) { 959 if(lbox.sorted) { 960 foreach(Object value; range) { 961 add(value); 962 } 963 } else { 964 _wraparray.addRange(range); 965 } 966 } 967 968 969 void addRange(Dstring[] range) { 970 foreach(Dstring value; range) { 971 add(value); 972 } 973 } 974 975 976 private: 977 978 ListBox lbox; 979 Object[] _items; 980 981 982 LRESULT insert2(WPARAM idx, Dstring val) { 983 insert(idx, val); 984 return idx; 985 } 986 987 988 LRESULT add2(Object val) { 989 int i; 990 if(lbox.sorted) { 991 for(i = 0; i != _items.length; i++) { 992 if(val < _items[i]) { 993 break; 994 } 995 } 996 } else { 997 i = _items.length; 998 } 999 1000 insert(i, val); 1001 1002 return i; 1003 } 1004 1005 1006 LRESULT add2(Dstring val) { 1007 return add2(new ListString(val)); 1008 } 1009 1010 1011 void _added(size_t idx, Object val) { 1012 if(lbox.created) { 1013 if(dfl.internal.utf.useUnicode) { 1014 lbox.prevwproc(LB_INSERTSTRING, idx, cast(LPARAM)dfl.internal.utf.toUnicodez(getObjectString(val))); 1015 } else { 1016 lbox.prevwproc(LB_INSERTSTRING, idx, cast(LPARAM)dfl.internal.utf.toAnsiz(getObjectString(val))); // Can this be unsafeAnsiz()? 1017 } 1018 } 1019 } 1020 1021 1022 void _removed(size_t idx, Object val) { 1023 if(size_t.max == idx) { // Clear all. 1024 if(lbox.created) { 1025 lbox.prevwproc(LB_RESETCONTENT, 0, 0); 1026 } 1027 } else { 1028 if(lbox.created) { 1029 lbox.prevwproc(LB_DELETESTRING, cast(WPARAM)idx, 0); 1030 } 1031 } 1032 } 1033 1034 1035 public: 1036 1037 mixin ListWrapArray!(Object, _items, 1038 _blankListCallback!(Object), _added, 1039 _blankListCallback!(Object), _removed, 1040 true, false, false) _wraparray; 1041 } 1042 1043 1044 this() { 1045 _initListbox(); 1046 1047 // Default useTabStops and vertical scrolling. 1048 wstyle |= WS_TABSTOP | LBS_USETABSTOPS | LBS_HASSTRINGS | WS_VSCROLL | LBS_NOTIFY; 1049 wexstyle |= WS_EX_CLIENTEDGE; 1050 ctrlStyle |= ControlStyles.SELECTABLE; 1051 wclassStyle = listboxClassStyle; 1052 1053 icollection = createItemCollection(); 1054 selidxcollection = new SelectedIndexCollection(this); 1055 selobjcollection = new SelectedObjectCollection(this); 1056 } 1057 1058 1059 protected override void onHandleCreated(EventArgs ea) { 1060 super.onHandleCreated(ea); 1061 1062 // Set the Ctrl ID to the HWND so that it is unique 1063 // and WM_MEASUREITEM will work properly. 1064 SetWindowLongA(hwnd, GWL_ID, cast(LONG)hwnd); 1065 1066 if(hextent != 0) { 1067 prevwproc(LB_SETHORIZONTALEXTENT, hextent, 0); 1068 } 1069 1070 if(iheight != DEFAULT_ITEM_HEIGHT) { 1071 prevwproc(LB_SETITEMHEIGHT, 0, MAKELPARAM(iheight, 0)); 1072 } 1073 1074 Message m; 1075 m.hWnd = handle; 1076 m.msg = LB_INSERTSTRING; 1077 // Note: duplicate code. 1078 if(dfl.internal.utf.useUnicode) { 1079 foreach(int i, Object obj; icollection._items) { 1080 m.wParam = i; 1081 m.lParam = cast(LPARAM)dfl.internal.utf.toUnicodez(getObjectString(obj)); // <-- 1082 1083 prevWndProc(m); 1084 //if(LB_ERR == m.result || LB_ERRSPACE == m.result) 1085 if(m.result < 0) { 1086 throw new DflException("Unable to add list item"); 1087 } 1088 1089 //prevwproc(LB_SETITEMDATA, m.result, cast(LPARAM)cast(void*)obj); 1090 } 1091 } else { 1092 foreach(int i, Object obj; icollection._items) { 1093 m.wParam = i; 1094 m.lParam = cast(LPARAM)dfl.internal.utf.toAnsiz(getObjectString(obj)); // Can this be unsafeAnsiz? // <-- 1095 1096 prevWndProc(m); 1097 //if(LB_ERR == m.result || LB_ERRSPACE == m.result) 1098 if(m.result < 0) { 1099 throw new DflException("Unable to add list item"); 1100 } 1101 1102 //prevwproc(LB_SETITEMDATA, m.result, cast(LPARAM)cast(void*)obj); 1103 } 1104 } 1105 1106 //redrawEntire(); 1107 } 1108 1109 1110 /+ 1111 override void createHandle() { 1112 if(isHandleCreated) { 1113 return; 1114 } 1115 1116 createClassHandle(LISTBOX_CLASSNAME); 1117 1118 onHandleCreated(EventArgs.empty); 1119 } 1120 +/ 1121 1122 1123 protected override void createParams(ref CreateParams cp) { 1124 super.createParams(cp); 1125 1126 cp.className = LISTBOX_CLASSNAME; 1127 } 1128 1129 1130 //DrawItemEventHandler drawItem; 1131 Event!(ListBox, DrawItemEventArgs) drawItem; /// 1132 //MeasureItemEventHandler measureItem; 1133 Event!(ListBox, MeasureItemEventArgs) measureItem; /// 1134 1135 1136 protected: 1137 1138 1139 void onDrawItem(DrawItemEventArgs dieh) { 1140 drawItem(this, dieh); 1141 } 1142 1143 1144 1145 void onMeasureItem(MeasureItemEventArgs miea) { 1146 measureItem(this, miea); 1147 } 1148 1149 1150 package final void _WmDrawItem(DRAWITEMSTRUCT* dis) 1151 in { 1152 assert(dis.hwndItem == handle); 1153 assert(dis.CtlType == ODT_LISTBOX); 1154 } 1155 body { 1156 DrawItemState state; 1157 state = cast(DrawItemState)dis.itemState; 1158 1159 if(dis.itemID == -1) { 1160 FillRect(dis.hDC, &dis.rcItem, hbrBg); 1161 if(state & DrawItemState.FOCUS) { 1162 DrawFocusRect(dis.hDC, &dis.rcItem); 1163 } 1164 } else 1165 { 1166 DrawItemEventArgs diea; 1167 Color bc, fc; 1168 1169 if(state & DrawItemState.SELECTED) { 1170 bc = Color.systemColor(COLOR_HIGHLIGHT); 1171 fc = Color.systemColor(COLOR_HIGHLIGHTTEXT); 1172 } else 1173 { 1174 bc = backColor; 1175 fc = foreColor; 1176 } 1177 1178 prepareDc(dis.hDC); 1179 diea = new DrawItemEventArgs(new Graphics(dis.hDC, false), wfont, 1180 Rect(&dis.rcItem), dis.itemID, state, fc, bc); 1181 1182 onDrawItem(diea); 1183 } 1184 } 1185 1186 1187 package final void _WmMeasureItem(MEASUREITEMSTRUCT* mis) 1188 in { 1189 assert(mis.CtlType == ODT_LISTBOX); 1190 } 1191 body { 1192 MeasureItemEventArgs miea; 1193 scope Graphics gpx = new CommonGraphics(handle(), GetDC(handle)); 1194 miea = new MeasureItemEventArgs(gpx, mis.itemID, /+ mis.itemHeight +/ iheight); 1195 miea.itemWidth = mis.itemWidth; 1196 1197 onMeasureItem(miea); 1198 1199 mis.itemHeight = miea.itemHeight; 1200 mis.itemWidth = miea.itemWidth; 1201 } 1202 1203 1204 override void prevWndProc(ref Message msg) { 1205 //msg.result = CallWindowProcA(listboxPrevWndProc, msg.hWnd, msg.msg, msg.wParam, msg.lParam); 1206 msg.result = dfl.internal.utf.callWindowProc(listboxPrevWndProc, msg.hWnd, msg.msg, msg.wParam, msg.lParam); 1207 } 1208 1209 1210 protected override void onReflectedMessage(ref Message m) { 1211 super.onReflectedMessage(m); 1212 1213 switch(m.msg) { 1214 case WM_DRAWITEM: 1215 _WmDrawItem(cast(DRAWITEMSTRUCT*)m.lParam); 1216 m.result = 1; 1217 break; 1218 1219 case WM_MEASUREITEM: 1220 _WmMeasureItem(cast(MEASUREITEMSTRUCT*)m.lParam); 1221 m.result = 1; 1222 break; 1223 1224 case WM_COMMAND: 1225 assert(cast(HWND)m.lParam == handle); 1226 switch(HIWORD(m.wParam)) { 1227 case LBN_SELCHANGE: 1228 onSelectedIndexChanged(EventArgs.empty); 1229 break; 1230 1231 case LBN_SELCANCEL: 1232 onSelectedIndexChanged(EventArgs.empty); 1233 break; 1234 1235 default: 1236 } 1237 break; 1238 1239 default: 1240 } 1241 } 1242 1243 1244 override void wndProc(ref Message msg) { 1245 switch(msg.msg) { 1246 case LB_ADDSTRING: 1247 //msg.result = icollection.add2(stringFromStringz(cast(char*)msg.lParam).dup); // TODO: fix. 1248 //msg.result = icollection.add2(stringFromStringz(cast(char*)msg.lParam).idup); // TODO: fix. // Needed in D2. Doesn't work in D1. 1249 msg.result = icollection.add2(cast(Dstring)stringFromStringz(cast(char*)msg.lParam).dup); // TODO: fix. // Needed in D2. 1250 return; 1251 1252 case LB_INSERTSTRING: 1253 //msg.result = icollection.insert2(msg.wParam, stringFromStringz(cast(char*)msg.lParam).dup); // TODO: fix. 1254 //msg.result = icollection.insert2(msg.wParam, stringFromStringz(cast(char*)msg.lParam).idup); // TODO: fix. // Needed in D2. Doesn't work in D1. 1255 msg.result = icollection.insert2(msg.wParam, cast(Dstring)stringFromStringz(cast(char*)msg.lParam).dup); // TODO: fix. // Needed in D2. 1256 return; 1257 1258 case LB_DELETESTRING: 1259 icollection.removeAt(msg.wParam); 1260 msg.result = icollection.length; 1261 return; 1262 1263 case LB_RESETCONTENT: 1264 icollection.clear(); 1265 return; 1266 1267 case LB_SETITEMDATA: 1268 // Cannot set item data from outside DFL. 1269 msg.result = LB_ERR; 1270 return; 1271 1272 case LB_ADDFILE: 1273 msg.result = LB_ERR; 1274 return; 1275 1276 case LB_DIR: 1277 msg.result = LB_ERR; 1278 return; 1279 1280 default: 1281 } 1282 super.wndProc(msg); 1283 } 1284 1285 1286 private: 1287 int hextent = 0; 1288 int iheight = DEFAULT_ITEM_HEIGHT; 1289 ObjectCollection icollection; 1290 SelectedIndexCollection selidxcollection; 1291 SelectedObjectCollection selobjcollection; 1292 bool _sorting = false; 1293 1294 1295 package: 1296 final: 1297 LRESULT prevwproc(UINT msg, WPARAM wparam, LPARAM lparam) { 1298 //return CallWindowProcA(listviewPrevWndProc, hwnd, msg, wparam, lparam); 1299 return dfl.internal.utf.callWindowProc(listboxPrevWndProc, hwnd, msg, wparam, lparam); 1300 } 1301 } 1302