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