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