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