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