1 // Written by Christopher E. Miller 2 // See the included license.txt for copyright and license details. 3 4 5 6 module dfl.treeview; 7 8 private import dfl.internal.dlib; 9 10 private import dfl.control, dfl.application, dfl.base, dfl.internal.winapi; 11 private import dfl.event, dfl.drawing, dfl.collections, dfl.internal.utf; 12 13 version(DFL_NO_IMAGELIST) { 14 } 15 else { 16 private import dfl.imagelist; 17 } 18 19 20 private extern(Windows) void _initTreeview(); 21 22 23 24 enum TreeViewAction : ubyte { 25 UNKNOWN, 26 COLLAPSE, /// ditto 27 EXPAND, /// ditto 28 BY_KEYBOARD, /// ditto 29 BY_MOUSE, /// ditto 30 } 31 32 33 class TreeViewCancelEventArgs: CancelEventArgs { 34 this(TreeNode node, bool cancel, TreeViewAction action) { 35 super(cancel); 36 37 _node = node; 38 _action = action; 39 } 40 41 42 final @property TreeViewAction action() { // getter 43 return _action; 44 } 45 46 47 final @property TreeNode node() { // getter 48 return _node; 49 } 50 51 52 private: 53 TreeNode _node; 54 TreeViewAction _action; 55 } 56 57 58 class TreeViewEventArgs: EventArgs { 59 this(TreeNode node, TreeViewAction action) { 60 _node = node; 61 _action = action; 62 } 63 64 65 this(TreeNode node) { 66 _node = node; 67 //_action = TreeViewAction.UNKNOWN; 68 } 69 70 71 final @property TreeViewAction action() { // getter 72 return _action; 73 } 74 75 76 final @property TreeNode node() { // getter 77 return _node; 78 } 79 80 81 private: 82 TreeNode _node; 83 TreeViewAction _action = TreeViewAction.UNKNOWN; 84 } 85 86 87 class NodeLabelEditEventArgs: EventArgs { 88 this(TreeNode node, Dstring label) { 89 _node = node; 90 _label = label; 91 } 92 93 94 this(TreeNode node) { 95 _node = node; 96 } 97 98 99 final @property TreeNode node() { // getter 100 return _node; 101 } 102 103 104 final @property Dstring label() { // getter 105 return _label; 106 } 107 108 109 final @property void cancelEdit(bool byes) { // setter 110 _cancel = byes; 111 } 112 113 114 final @property bool cancelEdit() { // getter 115 return _cancel; 116 } 117 118 119 private: 120 TreeNode _node; 121 Dstring _label; 122 bool _cancel = false; 123 } 124 125 126 class TreeNode: DObject { 127 this(Dstring labelText) { 128 this(); 129 130 ttext = labelText; 131 } 132 133 134 this(Dstring labelText, TreeNode[] children) { 135 this(); 136 137 ttext = labelText; 138 tchildren.addRange(children); 139 } 140 141 142 this() { 143 Application.ppin(cast(void*)this); 144 145 /* 146 bcolor = Color.empty; 147 fcolor = Color.empty; 148 */ 149 150 tchildren = new TreeNodeCollection(tview, this); 151 } 152 153 this(Object val) { // package 154 this(getObjectString(val)); 155 } 156 157 //final @property void backColor(Color c) { 158 //if(created) { 159 //COLORREF cref; 160 //if(Color.empty == c) { 161 //cref = CLR_NONE; 162 //} else { 163 //cref = c.toRgb(); 164 //} 165 ////SendMessageA(tview.hwnd, TVM_SETBKCOLOR, 0, cast(LPARAM)cref); 166 //prevwproc(LVM_SETBKCOLOR, 0, cast(LPARAM)cref); 167 //prevwproc(LVM_SETTEXTBKCOLOR, 0, cast(LPARAM)cref); 168 //} 169 //super.backColor = c; 170 //} 171 172 //final @property Color backColor() { 173 //if(Color.empty == backc) 174 //return defaultBackColor; 175 //return backc; 176 //} 177 178 /* 179 final @property void backColor(Color c) // setter 180 { 181 bcolor = c; 182 } 183 184 final @property Color backColor() // getter 185 { 186 return bcolor; 187 } 188 */ 189 190 191 final @property Rect bounds() { // getter 192 Rect result; 193 194 if (created) { 195 RECT rect; 196 *(cast(HTREEITEM*)&rect) = hnode; 197 if (SendMessageA(tview.handle, TVM_GETITEMRECT, FALSE, cast(LPARAM)&rect)) { 198 result = Rect(&rect); 199 } 200 } 201 202 return result; 203 } 204 205 206 final @property TreeNode firstNode() { // getter 207 if (tchildren.length) { 208 return tchildren._nodes[0]; 209 } 210 return null; 211 } 212 213 214 /* 215 final @property void foreColor(Color c) // setter 216 { 217 fcolor = c; 218 } 219 220 final @property Color foreColor() // getter 221 { 222 return fcolor; 223 } 224 */ 225 226 227 // Path from the root to this node. 228 final @property Dstring fullPath() { // getter 229 if (!tparent) { 230 return ttext; 231 } 232 233 // Might want to manually loop through parents and preallocate the whole buffer. 234 assert(tview !is null); 235 dchar sep; 236 sep = tview.pathSeparator; 237 //return std.string.format("%s%s%s", tparent.fullPath, sep, ttext); 238 char[4] ssep; 239 int sseplen = 0; 240 foreach(char ch; (&sep)[0 .. 1]) { 241 ssep[sseplen++] = ch; 242 } 243 //return tparent.fullPath ~ ssep[0 .. sseplen] ~ ttext; 244 return tparent.fullPath ~ cast(Dstring)ssep[0 .. sseplen] ~ ttext; // Needed in D2. 245 } 246 247 248 final @property HTREEITEM handle() { // getter 249 return hnode; 250 } 251 252 253 // Index of this node in the parent node. 254 final @property int index() { // getter 255 int result = -1; 256 if (tparent) { 257 result = tparent.tchildren.indexOf(this); 258 assert(result != -1); 259 } 260 return result; 261 } 262 263 264 /* 265 final @property bool isEditing() // getter 266 { 267 } 268 */ 269 270 271 272 final @property bool isExpanded() { // getter 273 return isState(TVIS_EXPANDED); 274 } 275 276 final @property bool isSelected() { // getter 277 return isState(TVIS_SELECTED); 278 } 279 280 /* 281 final @property bool isVisible() // getter 282 { 283 } 284 */ 285 286 final @property TreeNode lastNode() { // getter 287 if (tchildren.length) { 288 return tchildren._nodes[tchildren.length - 1]; 289 } 290 return null; 291 } 292 293 // Next sibling node. 294 final @property TreeNode nextNode() { // getter 295 if (tparent) { 296 int i; 297 i = tparent.tchildren.indexOf(this); 298 assert(i != -1); 299 300 i++; 301 if (i != tparent.tchildren.length) { 302 return tparent.tchildren._nodes[i]; 303 } 304 } 305 return null; 306 } 307 308 /* 309 310 final @property void nodeFont(Font f) // setter 311 { 312 tfont = f; 313 } 314 315 316 final @property Font nodeFont() // getter 317 { 318 return tfont; 319 } 320 */ 321 322 final @property TreeNodeCollection nodes() { // getter 323 return tchildren; 324 } 325 326 final @property TreeNode parent() { // getter 327 return tparent; 328 } 329 330 // Previous sibling node. 331 final @property TreeNode prevNode() { // getter 332 if (tparent) { 333 int i; 334 i = tparent.tchildren.indexOf(this); 335 assert(i != -1); 336 337 if (i) { 338 i--; 339 return tparent.tchildren._nodes[i]; 340 } 341 } 342 return null; 343 } 344 345 final @property void tag(Object o) { // setter 346 ttag = o; 347 } 348 349 350 final @property Object tag() { // getter 351 return ttag; 352 } 353 354 355 final @property void text(Dstring newText) { // setter 356 ttext = newText; 357 358 if (created) { 359 TV_ITEMA item; 360 Message m; 361 362 item.mask = TVIF_HANDLE | TVIF_TEXT; 363 item.hItem = hnode; 364 /* 365 item.pszText = stringToStringz(ttext); 366 //item.cchTextMax = ttext.length; // ? 367 m = Message(tview.handle, TVM_SETITEMA, 0, cast(LPARAM)&item); 368 */ 369 if (dfl.internal.utf.useUnicode) { 370 item.pszText = cast(typeof(item.pszText))dfl.internal.utf.toUnicodez(ttext); 371 m = Message(tview.handle, TVM_SETITEMW, 0, cast(LPARAM)&item); 372 } else { 373 item.pszText = cast(typeof(item.pszText))dfl.internal.utf.unsafeAnsiz(ttext); 374 m = Message(tview.handle, TVM_SETITEMA, 0, cast(LPARAM)&item); 375 } 376 tview.prevWndProc(m); 377 } 378 } 379 380 final @property Dstring text() { // getter 381 return ttext; 382 } 383 384 // Get the TreeView control this node belongs to. 385 final @property TreeView treeView() { // getter 386 return tview; 387 } 388 389 final void beginEdit() { 390 if (created) { 391 SetFocus(tview.hwnd); // Needs to have focus. 392 HWND hwEdit; 393 hwEdit = cast(HWND)SendMessageA(tview.hwnd, TVM_EDITLABELA, 0, cast(LPARAM)hnode); 394 if (!hwEdit) { 395 goto err_edit; 396 } 397 } else { 398 err_edit: 399 throw new DflException("Unable to edit TreeNode"); 400 } 401 } 402 403 404 /* 405 406 final void endEdit(bool cancel) 407 { 408 // ? 409 } 410 */ 411 412 final void ensureVisible() { 413 if (created) { 414 SendMessageA(tview.hwnd, TVM_ENSUREVISIBLE, 0, cast(LPARAM)hnode); 415 } 416 } 417 418 final void collapse() { 419 if (created) { 420 SendMessageA(tview.hwnd, TVM_EXPAND, TVE_COLLAPSE, cast(LPARAM)hnode); 421 } 422 } 423 424 final void expand() { 425 if (created) { 426 SendMessageA(tview.hwnd, TVM_EXPAND, TVE_EXPAND, cast(LPARAM)hnode); 427 } 428 } 429 430 final void expandAll() { 431 if (created) { 432 SendMessageA(tview.hwnd, TVM_EXPAND, TVE_EXPAND, cast(LPARAM)hnode); 433 434 foreach(TreeNode node; tchildren._nodes) { 435 node.expandAll(); 436 } 437 } 438 } 439 440 static TreeNode fromHandle(TreeView tree, HTREEITEM handle) { 441 return tree.treeNodeFromHandle(handle); 442 } 443 444 final void remove() { 445 if (tparent) { 446 tparent.tchildren.remove(this); 447 } else if (tview) { // It's a top level node. 448 tview.tchildren.remove(this); 449 } 450 } 451 452 final void toggle() { 453 if (created) { 454 SendMessageA(tview.hwnd, TVM_EXPAND, TVE_TOGGLE, cast(LPARAM)hnode); 455 } 456 } 457 458 version(DFL_NO_IMAGELIST) { 459 } else { 460 final @property void imageIndex(int index) { // setter 461 this._imgidx = index; 462 463 if (created) { 464 TV_ITEMA item; 465 Message m; 466 m = Message(tview.handle, TVM_SETITEMA, 0, cast(LPARAM)&item); 467 468 item.mask = TVIF_HANDLE | TVIF_IMAGE; 469 item.hItem = hnode; 470 item.iImage = _imgidx; 471 if (tview._selimgidx < 0) { 472 item.mask |= TVIF_SELECTEDIMAGE; 473 item.iSelectedImage = _imgidx; 474 } 475 tview.prevWndProc(m); 476 } 477 } 478 479 final @property int imageIndex() { // getter 480 return _imgidx; 481 } 482 } 483 484 override Dstring toString() { 485 return ttext; 486 } 487 488 override Dequ opEquals(Object o) { 489 return 0 == stringICmp(ttext, getObjectString(o)); // ? 490 } 491 492 Dequ opEquals(TreeNode node) { 493 return 0 == stringICmp(ttext, node.ttext); 494 } 495 496 Dequ opEquals(Dstring val) { 497 return 0 == stringICmp(ttext, val); 498 } 499 500 override int opCmp(Object o) { 501 return stringICmp(ttext, getObjectString(o)); // ? 502 } 503 504 int opCmp(TreeNode node) { 505 return stringICmp(ttext, node.ttext); 506 } 507 508 int opCmp(Dstring val) { 509 return stringICmp(text, val); 510 } 511 512 513 private: 514 Dstring ttext; 515 TreeNode tparent; 516 TreeNodeCollection tchildren; 517 Object ttag; 518 HTREEITEM hnode; 519 TreeView tview; 520 version(DFL_NO_IMAGELIST) { 521 } else { 522 int _imgidx = -1; 523 } 524 /* 525 Color bcolor, fcolor; 526 Font tfont; 527 */ 528 529 package final @property bool created() { // getter 530 if (tview && tview.created()) { 531 assert(hnode); 532 return true; 533 } 534 return false; 535 } 536 537 bool isState(UINT state) { 538 if (created) { 539 TV_ITEMA ti; 540 ti.mask = TVIF_HANDLE | TVIF_STATE; 541 ti.hItem = hnode; 542 ti.stateMask = state; 543 if (SendMessageA(tview.handle, TVM_GETITEMA, 0, cast(LPARAM)&ti)) { 544 if (ti.state & state) { 545 return true; 546 } 547 } 548 } 549 return false; 550 } 551 552 void _reset() { 553 hnode = null; 554 tview = null; 555 tparent = null; 556 } 557 } 558 559 560 561 class TreeNodeCollection { 562 void add(TreeNode node) { 563 //cprintf("Adding node %p '%.*s'\n", cast(void*)node, getObjectString(node)); 564 565 int i; 566 567 if (tview && tview.sorted()) { 568 // Insertion sort. 569 570 for (i = 0; i != _nodes.length; i++) { 571 if (node < _nodes[i]) { 572 break; 573 } 574 } 575 } else { 576 i = _nodes.length; 577 } 578 579 insert(i, node); 580 } 581 582 void add(Dstring text) { 583 return add(new TreeNode(text)); 584 } 585 586 void add(Object val) { 587 return add(new TreeNode(getObjectString(val))); // ? 588 } 589 590 591 void addRange(Object[] range) { 592 foreach(Object o; range) { 593 add(o); 594 } 595 } 596 597 void addRange(TreeNode[] range) { 598 foreach(TreeNode node; range) { 599 add(node); 600 } 601 } 602 603 void addRange(Dstring[] range) { 604 foreach(Dstring s; range) { 605 add(s); 606 } 607 } 608 609 610 // Like clear but doesn't bother removing stuff from the lists. 611 // Used when a parent is being removed and the children only 612 // need to be reset. 613 private void _reset() { 614 foreach(TreeNode node; _nodes) { 615 node._reset(); 616 } 617 } 618 619 620 // Clear node handles when the TreeView window is destroyed so 621 // that it can be reconstructed. 622 private void _resetHandles() { 623 foreach(TreeNode node; _nodes) { 624 node.tchildren._resetHandles(); 625 node.hnode = null; 626 } 627 } 628 629 630 private: 631 632 TreeView tview; // null if not assigned to a TreeView yet. 633 TreeNode tparent; // null if root. The parent of -_nodes-. 634 TreeNode[] _nodes; 635 636 637 void verifyNoParent(TreeNode node) { 638 if (node.tparent) { 639 throw new DflException("TreeNode already belongs to a TreeView"); 640 } 641 } 642 643 644 package this(TreeView treeView, TreeNode parentNode) { 645 tview = treeView; 646 tparent = parentNode; 647 } 648 649 650 package final void setTreeView(TreeView treeView) { 651 tview = treeView; 652 foreach(TreeNode node; _nodes) { 653 node.tchildren.setTreeView(treeView); 654 } 655 } 656 657 658 package final @property bool created() { // getter 659 return tview && tview.created(); 660 } 661 662 663 package void populateInsertChildNode(ref Message m, ref TV_ITEMA dest, TreeNode node) { 664 with(dest) { 665 mask = /* TVIF_CHILDREN | */ TVIF_PARAM | TVIF_TEXT; 666 version(DFL_NO_IMAGELIST) { 667 } 668 else { 669 mask |= TVIF_IMAGE | TVIF_SELECTEDIMAGE; 670 iImage = node._imgidx; 671 if (tview._selimgidx < 0) { 672 iSelectedImage = node._imgidx; 673 } else { 674 iSelectedImage = tview._selimgidx; 675 } 676 } 677 /* cChildren = I_CHILDRENCALLBACK; */ 678 lParam = cast(LPARAM)cast(void*)node; 679 /* 680 pszText = stringToStringz(node.text); 681 //cchTextMax = node.text.length; // ? 682 */ 683 if (dfl.internal.utf.useUnicode) { 684 pszText = cast(typeof(pszText))dfl.internal.utf.toUnicodez(node.text); 685 m.hWnd = tview.handle; 686 m.msg = TVM_INSERTITEMW; 687 } else { 688 pszText = cast(typeof(pszText))dfl.internal.utf.unsafeAnsiz(node.text); 689 m.hWnd = tview.handle; 690 m.msg = TVM_INSERTITEMA; 691 } 692 } 693 } 694 695 696 void doNodes() 697 in { 698 assert(created); 699 } 700 body { 701 TV_INSERTSTRUCTA tis; 702 Message m; 703 704 tis.hInsertAfter = TVI_LAST; 705 706 m.hWnd = tview.handle; 707 m.wParam = 0; 708 709 foreach(TreeNode node; _nodes) { 710 assert(!node.handle); 711 712 tis.hParent = tparent ? tparent.handle : TVI_ROOT; 713 populateInsertChildNode(m, tis.item, node); 714 715 m.lParam = cast(LPARAM)&tis; 716 tview.prevWndProc(m); 717 assert(m.result); 718 node.hnode = cast(HTREEITEM)m.result; 719 720 node.tchildren.doNodes(); 721 } 722 } 723 724 725 void _added(size_t idx, TreeNode val) { 726 verifyNoParent(val); 727 728 val.tparent = tparent; 729 val.tview = tview; 730 val.tchildren.setTreeView(tview); 731 732 if (created) { 733 TV_INSERTSTRUCTA tis; 734 735 if (idx <= 0) { 736 tis.hInsertAfter = TVI_FIRST; 737 } else if (idx >= cast(int)_nodes.length) { 738 tis.hInsertAfter = TVI_LAST; 739 } else { 740 tis.hInsertAfter = _nodes[idx - 1].handle; 741 } 742 743 tis.hParent = tparent ? tparent.handle : TVI_ROOT; 744 assert(tis.hInsertAfter); 745 746 Message m; 747 m.wParam = 0; 748 749 populateInsertChildNode(m, tis.item, val); 750 751 m.lParam = cast(LPARAM)&tis; 752 tview.prevWndProc(m); 753 assert(m.result); 754 val.hnode = cast(HTREEITEM)m.result; 755 756 val.tchildren.doNodes(); 757 758 if (tparent) { 759 tview.invalidate(tparent.bounds); 760 } 761 } 762 } 763 764 765 void _removing(size_t idx, TreeNode val) { 766 if (size_t.max == idx) { // Clearing all... 767 TreeNode[] nodes = _nodes; 768 _nodes = _nodes[0 .. 0]; // Not nice to dfl.collections, but OK. 769 if (created) { 770 Message m; 771 m.hWnd = tview.handle; 772 m.msg = TVM_DELETEITEM; 773 m.wParam = 0; 774 if (tparent) { 775 foreach(TreeNode node; nodes) { 776 assert(node.handle !is null); 777 m.lParam = cast(LPARAM)node.handle; 778 tview.prevWndProc(m); 779 780 node._reset(); 781 } 782 } else { 783 m.lParam = cast(LPARAM)TVI_ROOT; 784 tview.prevWndProc(m); 785 foreach(TreeNode node; nodes) { 786 node._reset(); 787 } 788 } 789 } 790 } else { 791 } 792 } 793 794 795 void _removed(size_t idx, TreeNode val) { 796 if (size_t.max == idx) { // Clear all. 797 } else { 798 if (created) { 799 assert(val.hnode); 800 Message m; 801 m = Message(tview.handle, TVM_DELETEITEM, 0, cast(LPARAM)val.hnode); 802 tview.prevWndProc(m); 803 } 804 805 // Clear children. 806 val._reset(); 807 } 808 } 809 810 811 public: 812 813 mixin ListWrapArray!(TreeNode, _nodes, 814 _blankListCallback!(TreeNode), _added, 815 _removing, _removed, 816 true, /*true*/ false, false) _wraparray; 817 } 818 819 820 821 class TreeView: ControlSuperClass { // docmain 822 this() { 823 _initTreeview(); 824 825 wstyle |= WS_TABSTOP | TVS_HASBUTTONS | TVS_LINESATROOT | TVS_HASLINES; 826 wexstyle |= WS_EX_CLIENTEDGE; 827 ctrlStyle |= ControlStyles.SELECTABLE; 828 wclassStyle = treeviewClassStyle; 829 830 tchildren = new TreeNodeCollection(this, null); 831 } 832 833 834 /* 835 ~this() 836 { 837 if(tchildren) 838 tchildren._dtorReset(); 839 } 840 */ 841 842 843 static @property Color defaultBackColor() { // getter 844 return SystemColors.window; 845 } 846 847 848 override @property Color backColor() { // getter 849 if (Color.empty == backc) { 850 return defaultBackColor; 851 } 852 return backc; 853 } 854 855 856 override @property void backColor(Color b) { // setter 857 super.backColor = b; 858 859 if (created) { 860 // For some reason the left edge isn't showing the new color. 861 // This causes the entire control to be redrawn with the new color. 862 // Sets the same font. 863 prevwproc(WM_SETFONT, this.font ? cast(WPARAM)this.font.handle : 0, MAKELPARAM(TRUE, 0)); 864 } 865 } 866 867 868 static @property Color defaultForeColor() { //getter 869 return SystemColors.windowText; 870 } 871 872 873 override @property Color foreColor() { // getter 874 if (Color.empty == forec) { 875 return defaultForeColor; 876 } 877 return forec; 878 } 879 880 alias Control.foreColor foreColor; // Overload. 881 882 883 final @property void borderStyle(BorderStyle bs) { // setter 884 final switch (bs) { 885 case BorderStyle.FIXED_3D: 886 _style(_style() & ~WS_BORDER); 887 _exStyle(_exStyle() | WS_EX_CLIENTEDGE); 888 break; 889 890 case BorderStyle.FIXED_SINGLE: 891 _exStyle(_exStyle() & ~WS_EX_CLIENTEDGE); 892 _style(_style() | WS_BORDER); 893 break; 894 895 case BorderStyle.NONE: 896 _style(_style() & ~WS_BORDER); 897 _exStyle(_exStyle() & ~WS_EX_CLIENTEDGE); 898 break; 899 } 900 901 if (created) { 902 redrawEntire(); 903 } 904 } 905 906 907 final @property BorderStyle borderStyle() { // getter 908 if (_exStyle() & WS_EX_CLIENTEDGE) { 909 return BorderStyle.FIXED_3D; 910 } else if (_style() & WS_BORDER) { 911 return BorderStyle.FIXED_SINGLE; 912 } 913 return BorderStyle.NONE; 914 } 915 916 917 /* 918 919 final @property void checkBoxes(bool byes) // setter 920 { 921 if(byes) 922 _style(_style() | TVS_CHECKBOXES); 923 else 924 _style(_style() & ~TVS_CHECKBOXES); 925 926 _crecreate(); 927 } 928 929 930 final @property bool checkBoxes() // getter 931 { 932 return (_style() & TVS_CHECKBOXES) != 0; 933 } 934 */ 935 936 937 938 final @property void fullRowSelect(bool byes) { // setter 939 if (byes) { 940 _style(_style() | TVS_FULLROWSELECT); 941 } else { 942 _style(_style() & ~TVS_FULLROWSELECT); 943 } 944 945 _crecreate(); // ? 946 } 947 948 949 final @property bool fullRowSelect() { // getter 950 return (_style() & TVS_FULLROWSELECT) != 0; 951 } 952 953 954 955 final @property void hideSelection(bool byes) { // setter 956 if (byes) { 957 _style(_style() & ~TVS_SHOWSELALWAYS); 958 } else { 959 _style(_style() | TVS_SHOWSELALWAYS); 960 } 961 } 962 963 964 final @property bool hideSelection() { // getter 965 return (_style() & TVS_SHOWSELALWAYS) == 0; 966 } 967 968 969 deprecated alias hoverSelection hotTracking; 970 971 972 final @property void hoverSelection(bool byes) { // setter 973 if (byes) { 974 _style(_style() | TVS_TRACKSELECT); 975 } else { 976 _style(_style() & ~TVS_TRACKSELECT); 977 } 978 } 979 980 981 final @property bool hoverSelection() { // getter 982 return (_style() & TVS_TRACKSELECT) != 0; 983 } 984 985 986 987 final @property void indent(int newIndent) { // setter 988 if (newIndent < 0) { 989 newIndent = 0; 990 } else if (newIndent > 32_000) { 991 newIndent = 32_000; 992 } 993 994 ind = newIndent; 995 996 if (created) { 997 SendMessageA(hwnd, TVM_SETINDENT, ind, 0); 998 } 999 } 1000 1001 1002 final @property int indent() { // getter 1003 if (created) { 1004 ind = cast(int)SendMessageA(hwnd, TVM_GETINDENT, 0, 0); 1005 } 1006 return ind; 1007 } 1008 1009 1010 1011 final @property void itemHeight(int h) { // setter 1012 if (h < 0) { 1013 h = 0; 1014 } 1015 1016 iheight = h; 1017 1018 if (created) { 1019 SendMessageA(hwnd, TVM_SETITEMHEIGHT, iheight, 0); 1020 } 1021 } 1022 1023 1024 final @property int itemHeight() { // getter 1025 if (created) { 1026 iheight = cast(int)SendMessageA(hwnd, TVM_GETITEMHEIGHT, 0, 0); 1027 } 1028 return iheight; 1029 } 1030 1031 1032 1033 final @property void labelEdit(bool byes) { // setter 1034 if (byes) { 1035 _style(_style() | TVS_EDITLABELS); 1036 } else { 1037 _style(_style() & ~TVS_EDITLABELS); 1038 } 1039 } 1040 1041 1042 final @property bool labelEdit() { // getter 1043 return (_style() & TVS_EDITLABELS) != 0; 1044 } 1045 1046 1047 1048 final @property TreeNodeCollection nodes() { // getter 1049 return tchildren; 1050 } 1051 1052 1053 1054 final @property void pathSeparator(dchar sep) { // setter 1055 pathsep = sep; 1056 } 1057 1058 final @property dchar pathSeparator() { // getter 1059 return pathsep; 1060 } 1061 1062 1063 1064 final @property void scrollable(bool byes) { // setter 1065 if (byes) { 1066 _style(_style() & ~TVS_NOSCROLL); 1067 } else { 1068 _style(_style() | TVS_NOSCROLL); 1069 } 1070 1071 if (created) { 1072 redrawEntire(); 1073 } 1074 } 1075 1076 final @property bool scrollable() { // getter 1077 return (_style & TVS_NOSCROLL) == 0; 1078 } 1079 1080 1081 1082 final @property void selectedNode(TreeNode node) { // setter 1083 if (created) { 1084 if (node) { 1085 SendMessageA(hwnd, TVM_SELECTITEM, TVGN_CARET, cast(LPARAM)node.handle); 1086 } else { 1087 // Should the selection be cleared if -node- is null? 1088 //SendMessageA(hwnd, TVM_SELECTITEM, TVGN_CARET, cast(LPARAM)null); 1089 } 1090 } 1091 } 1092 1093 1094 final @property TreeNode selectedNode() { // getter 1095 if (created) { 1096 HTREEITEM hnode; 1097 hnode = cast(HTREEITEM)SendMessageA(hwnd, TVM_GETNEXTITEM, TVGN_CARET, cast(LPARAM)0); 1098 if (hnode) { 1099 return treeNodeFromHandle(hnode); 1100 } 1101 } 1102 return null; 1103 } 1104 1105 1106 1107 final @property void showLines(bool byes) { // setter 1108 if (byes) { 1109 _style(_style() | TVS_HASLINES); 1110 } else { 1111 _style(_style() & ~TVS_HASLINES); 1112 } 1113 1114 _crecreate(); // ? 1115 } 1116 1117 1118 final @property bool showLines() { // getter 1119 return (_style() & TVS_HASLINES) != 0; 1120 } 1121 1122 1123 1124 final @property void showPlusMinus(bool byes) { // setter 1125 if (byes) { 1126 _style(_style() | TVS_HASBUTTONS); 1127 } else { 1128 _style(_style() & ~TVS_HASBUTTONS); 1129 } 1130 1131 _crecreate(); // ? 1132 } 1133 1134 1135 final @property bool showPlusMinus() { // getter 1136 return (_style() & TVS_HASBUTTONS) != 0; 1137 } 1138 1139 1140 1141 // -showPlusMinus- should be false. 1142 final @property void singleExpand(bool byes) { // setter 1143 if (byes) { 1144 _style(_style() | TVS_SINGLEEXPAND); 1145 } else { 1146 _style(_style() & ~TVS_SINGLEEXPAND); 1147 } 1148 1149 _crecreate(); // ? 1150 } 1151 1152 1153 final @property bool singleExpand() { // getter 1154 return (_style & TVS_SINGLEEXPAND) != 0; 1155 } 1156 1157 1158 1159 final @property void showRootLines(bool byes) { // setter 1160 if (byes) { 1161 _style(_style() | TVS_LINESATROOT); 1162 } else { 1163 _style(_style() & ~TVS_LINESATROOT); 1164 } 1165 1166 _crecreate(); // ? 1167 } 1168 1169 1170 final @property bool showRootLines() { // getter 1171 return (_style() & TVS_LINESATROOT) != 0; 1172 } 1173 1174 1175 1176 final @property void sorted(bool byes) { // setter 1177 _sort = byes; 1178 } 1179 1180 1181 final @property bool sorted() { // getter 1182 return _sort; 1183 } 1184 1185 1186 1187 // First visible node, based on the scrolled position. 1188 final @property TreeNode topNode() { // getter 1189 if (created) { 1190 HTREEITEM hnode; 1191 hnode = cast(HTREEITEM)SendMessageA(hwnd, TVM_GETNEXTITEM, 1192 TVGN_FIRSTVISIBLE, cast(LPARAM)0); 1193 if (hnode) { 1194 return treeNodeFromHandle(hnode); 1195 } 1196 } 1197 return null; 1198 } 1199 1200 1201 1202 // Number of visible nodes, including partially visible. 1203 final @property int visibleCount() { // getter 1204 if (!created) { 1205 return 0; 1206 } 1207 return cast(int)SendMessageA(hwnd, TVM_GETVISIBLECOUNT, 0, 0); 1208 } 1209 1210 1211 1212 final void beginUpdate() { 1213 SendMessageA(handle, WM_SETREDRAW, false, 0); 1214 } 1215 1216 1217 final void endUpdate() { 1218 SendMessageA(handle, WM_SETREDRAW, true, 0); 1219 invalidate(true); // Show updates. 1220 } 1221 1222 1223 1224 final void collapseAll() { 1225 if (created) { 1226 void collapsing(TreeNodeCollection tchildren) { 1227 foreach(TreeNode node; tchildren._nodes) { 1228 SendMessageA(hwnd, TVM_EXPAND, TVE_COLLAPSE, cast(LPARAM)node.hnode); 1229 collapsing(node.tchildren); 1230 } 1231 } 1232 1233 1234 collapsing(tchildren); 1235 } 1236 } 1237 1238 1239 1240 final void expandAll() { 1241 if (created) { 1242 void expanding(TreeNodeCollection tchildren) { 1243 foreach(TreeNode node; tchildren._nodes) { 1244 SendMessageA(hwnd, TVM_EXPAND, TVE_EXPAND, cast(LPARAM)node.hnode); 1245 expanding(node.tchildren); 1246 } 1247 } 1248 1249 1250 expanding(tchildren); 1251 } 1252 } 1253 1254 1255 1256 final TreeNode getNodeAt(int x, int y) { 1257 if (created) { 1258 TVHITTESTINFO thi; 1259 HTREEITEM hti; 1260 thi.pt.x = x; 1261 thi.pt.y = y; 1262 hti = cast(HTREEITEM)SendMessageA(hwnd, TVM_HITTEST, 0, cast(LPARAM)&thi); 1263 if (hti) { 1264 TreeNode result; 1265 result = treeNodeFromHandle(hti); 1266 if (result) { 1267 assert(result.tview is this); 1268 return result; 1269 } 1270 } 1271 } 1272 return null; 1273 } 1274 1275 1276 final TreeNode getNodeAt(Point pt) { 1277 return getNodeAt(pt.x, pt.y); 1278 } 1279 1280 1281 /* 1282 1283 // TODO: finish. 1284 final int getNodeCount(bool includeSubNodes) 1285 { 1286 int result; 1287 result = tchildren.length(); 1288 1289 if(includeSubNodes) 1290 { 1291 // ... 1292 } 1293 1294 return result; 1295 } 1296 */ 1297 1298 1299 version(DFL_NO_IMAGELIST) { 1300 } 1301 else { 1302 1303 final @property void imageList(ImageList imglist) { // setter 1304 if (isHandleCreated) { 1305 prevwproc(TVM_SETIMAGELIST, TVSIL_NORMAL, 1306 cast(LPARAM)(imglist ? imglist.handle : cast(HIMAGELIST)null)); 1307 } 1308 1309 _imglist = imglist; 1310 } 1311 1312 1313 final @property ImageList imageList() { // getter 1314 return _imglist; 1315 } 1316 1317 1318 /* 1319 1320 // Default image index (if -1 use this). 1321 final @property void imageIndex(int index) // setter 1322 { 1323 _defimgidx = index; 1324 } 1325 1326 1327 final @property int imageIndex() // getter 1328 { 1329 return _defimgidx; 1330 } 1331 */ 1332 1333 1334 1335 final @property void selectedImageIndex(int index) { // setter 1336 //assert(index >= 0); 1337 assert(index >= -1); 1338 _selimgidx = index; 1339 1340 if (isHandleCreated) { 1341 TreeNode curnode = selectedNode; 1342 _crecreate(); 1343 if (curnode) { 1344 curnode.ensureVisible(); 1345 } 1346 } 1347 } 1348 1349 1350 final @property int selectedImageIndex() { // getter 1351 return _selimgidx; 1352 } 1353 } 1354 1355 1356 protected override @property Size defaultSize() { // getter 1357 return Size(120, 100); 1358 } 1359 1360 1361 /* 1362 override void createHandle() 1363 { 1364 if(isHandleCreated) 1365 return; 1366 1367 createClassHandle(TREEVIEW_CLASSNAME); 1368 1369 onHandleCreated(EventArgs.empty); 1370 } 1371 */ 1372 1373 1374 protected override void createParams(ref CreateParams cp) { 1375 super.createParams(cp); 1376 1377 cp.className = TREEVIEW_CLASSNAME; 1378 } 1379 1380 1381 protected override void onHandleCreated(EventArgs ea) { 1382 super.onHandleCreated(ea); 1383 1384 prevwproc(CCM_SETVERSION, 5, 0); // Fixes font size issue. 1385 1386 prevwproc(TVM_SETINDENT, ind, 0); 1387 1388 prevwproc(TVM_SETITEMHEIGHT, iheight, 0); 1389 1390 version(DFL_NO_IMAGELIST) { 1391 } 1392 else { 1393 if (_imglist) { 1394 prevwproc(TVM_SETIMAGELIST, TVSIL_NORMAL, cast(LPARAM)_imglist.handle); 1395 } 1396 } 1397 1398 tchildren.doNodes(); 1399 } 1400 1401 1402 protected override void onHandleDestroyed(EventArgs ea) { 1403 tchildren._resetHandles(); 1404 1405 super.onHandleDestroyed(ea); 1406 } 1407 1408 1409 protected override void wndProc(ref Message m) { 1410 // TODO: support these messages. 1411 switch (m.msg) { 1412 case TVM_INSERTITEMA: 1413 case TVM_INSERTITEMW: 1414 m.result = cast(LRESULT)0; 1415 return; 1416 1417 case TVM_SETITEMA: 1418 case TVM_SETITEMW: 1419 m.result = cast(LRESULT) - 1; 1420 return; 1421 1422 case TVM_DELETEITEM: 1423 m.result = FALSE; 1424 return; 1425 1426 case TVM_SETIMAGELIST: 1427 m.result = cast(LRESULT)0; 1428 return; 1429 1430 default: 1431 } 1432 1433 super.wndProc(m); 1434 } 1435 1436 1437 protected override void prevWndProc(ref Message msg) { 1438 //msg.result = CallWindowProcA(treeviewPrevWndProc, msg.hWnd, msg.msg, msg.wParam, msg.lParam); 1439 msg.result = dfl.internal.utf.callWindowProc(treeviewPrevWndProc, msg.hWnd, msg.msg, msg.wParam, msg.lParam); 1440 } 1441 1442 1443 //TreeViewEventHandler afterCollapse; 1444 Event!(TreeView, TreeViewEventArgs) afterCollapse; 1445 //TreeViewEventHandler afterExpand; 1446 Event!(TreeView, TreeViewEventArgs) afterExpand; 1447 //TreeViewEventHandler afterSelect; 1448 Event!(TreeView, TreeViewEventArgs) afterSelect; 1449 //NodeLabelEditEventHandler afterLabelEdit; 1450 Event!(TreeView, NodeLabelEditEventArgs) afterLabelEdit; 1451 //TreeViewCancelEventHandler beforeCollapse; 1452 Event!(TreeView, TreeViewCancelEventArgs) beforeCollapse; 1453 //TreeViewCancelEventHandler beforeExpand; 1454 Event!(TreeView, TreeViewCancelEventArgs) beforeExpand; 1455 //TreeViewCancelEventHandler beforeSelect; 1456 Event!(TreeView, TreeViewCancelEventArgs) beforeSelect; 1457 //NodeLabelEditEventHandler beforeLabelEdit; 1458 Event!(TreeView, NodeLabelEditEventArgs) beforeLabelEdit; 1459 1460 1461 1462 protected void onAfterCollapse(TreeViewEventArgs ea) { 1463 afterCollapse(this, ea); 1464 } 1465 1466 1467 1468 protected void onAfterExpand(TreeViewEventArgs ea) { 1469 afterExpand(this, ea); 1470 } 1471 1472 1473 1474 protected void onAfterSelect(TreeViewEventArgs ea) { 1475 afterSelect(this, ea); 1476 } 1477 1478 1479 1480 protected void onAfterLabelEdit(NodeLabelEditEventArgs ea) { 1481 afterLabelEdit(this, ea); 1482 } 1483 1484 1485 1486 protected void onBeforeCollapse(TreeViewCancelEventArgs ea) { 1487 beforeCollapse(this, ea); 1488 } 1489 1490 1491 1492 protected void onBeforeExpand(TreeViewCancelEventArgs ea) { 1493 beforeExpand(this, ea); 1494 } 1495 1496 1497 1498 protected void onBeforeSelect(TreeViewCancelEventArgs ea) { 1499 beforeSelect(this, ea); 1500 } 1501 1502 1503 1504 protected void onBeforeLabelEdit(NodeLabelEditEventArgs ea) { 1505 beforeLabelEdit(this, ea); 1506 } 1507 1508 1509 protected override void onReflectedMessage(ref Message m) { // package 1510 super.onReflectedMessage(m); 1511 1512 switch (m.msg) { 1513 case WM_NOTIFY: { 1514 NMHDR* nmh; 1515 NM_TREEVIEW* nmtv; 1516 TreeViewCancelEventArgs cea; 1517 1518 nmh = cast(NMHDR*)m.lParam; 1519 assert(nmh.hwndFrom == hwnd); 1520 1521 switch (nmh.code) { 1522 case NM_CUSTOMDRAW: { 1523 NMTVCUSTOMDRAW* tvcd; 1524 tvcd = cast(NMTVCUSTOMDRAW*)nmh; 1525 //if(tvcd.nmcd.dwDrawStage & CDDS_ITEM) 1526 { 1527 //if(tvcd.nmcd.uItemState & CDIS_SELECTED) 1528 if ((tvcd.nmcd.dwDrawStage & CDDS_ITEM) 1529 && (tvcd.nmcd.uItemState & CDIS_SELECTED)) { 1530 // Note: might not look good with custom colors. 1531 tvcd.clrText = SystemColors.highlightText.toRgb(); 1532 tvcd.clrTextBk = SystemColors.highlight.toRgb(); 1533 } else { 1534 //tvcd.clrText = foreColor.toRgb(); 1535 tvcd.clrText = foreColor.solidColor(backColor).toRgb(); 1536 tvcd.clrTextBk = backColor.toRgb(); 1537 } 1538 } 1539 m.result |= CDRF_NOTIFYITEMDRAW; // | CDRF_NOTIFYITEMERASE; 1540 1541 // This doesn't seem to be doing anything. 1542 Font fon; 1543 fon = this.font; 1544 if (fon) { 1545 SelectObject(tvcd.nmcd.hdc, fon.handle); 1546 m.result |= CDRF_NEWFONT; 1547 } 1548 } 1549 break; 1550 1551 /* 1552 case TVN_GETDISPINFOA: 1553 1554 break; 1555 */ 1556 1557 case TVN_SELCHANGINGW: 1558 goto sel_changing; 1559 1560 case TVN_SELCHANGINGA: 1561 if (dfl.internal.utf.useUnicode) { 1562 break; 1563 } 1564 sel_changing: 1565 1566 nmtv = cast(NM_TREEVIEW*)nmh; 1567 switch (nmtv.action) { 1568 case TVC_BYMOUSE: 1569 cea = new TreeViewCancelEventArgs(cast(TreeNode)cast(void*)nmtv.itemNew.lParam, 1570 false, TreeViewAction.BY_MOUSE); 1571 onBeforeSelect(cea); 1572 m.result = cea.cancel; 1573 break; 1574 1575 case TVC_BYKEYBOARD: 1576 cea = new TreeViewCancelEventArgs(cast(TreeNode)cast(void*)nmtv.itemNew.lParam, 1577 false, TreeViewAction.BY_KEYBOARD); 1578 onBeforeSelect(cea); 1579 m.result = cea.cancel; 1580 break; 1581 1582 //case TVC_UNKNOWN: 1583 default: 1584 cea = new TreeViewCancelEventArgs(cast(TreeNode)cast(void*)nmtv.itemNew.lParam, 1585 false, TreeViewAction.UNKNOWN); 1586 onBeforeSelect(cea); 1587 m.result = cea.cancel; 1588 } 1589 break; 1590 1591 case TVN_SELCHANGEDW: 1592 goto sel_changed; 1593 1594 case TVN_SELCHANGEDA: 1595 if (dfl.internal.utf.useUnicode) { 1596 break; 1597 } 1598 sel_changed: 1599 1600 nmtv = cast(NM_TREEVIEW*)nmh; 1601 switch (nmtv.action) { 1602 case TVC_BYMOUSE: 1603 onAfterSelect(new TreeViewEventArgs(cast(TreeNode)cast(void*)nmtv.itemNew.lParam, 1604 TreeViewAction.BY_MOUSE)); 1605 break; 1606 1607 case TVC_BYKEYBOARD: 1608 onAfterSelect(new TreeViewEventArgs(cast(TreeNode)cast(void*)nmtv.itemNew.lParam, 1609 TreeViewAction.BY_KEYBOARD)); 1610 break; 1611 1612 //case TVC_UNKNOWN: 1613 default: 1614 onAfterSelect(new TreeViewEventArgs(cast(TreeNode)cast(void*)nmtv.itemNew.lParam, 1615 TreeViewAction.UNKNOWN)); 1616 } 1617 break; 1618 1619 case TVN_ITEMEXPANDINGW: 1620 goto item_expanding; 1621 1622 case TVN_ITEMEXPANDINGA: 1623 if (dfl.internal.utf.useUnicode) { 1624 break; 1625 } 1626 item_expanding: 1627 1628 nmtv = cast(NM_TREEVIEW*)nmh; 1629 switch (nmtv.action) { 1630 case TVE_COLLAPSE: 1631 cea = new TreeViewCancelEventArgs(cast(TreeNode)cast(void*)nmtv.itemNew.lParam, 1632 false, TreeViewAction.COLLAPSE); 1633 onBeforeCollapse(cea); 1634 m.result = cea.cancel; 1635 break; 1636 1637 case TVE_EXPAND: 1638 cea = new TreeViewCancelEventArgs(cast(TreeNode)cast(void*)nmtv.itemNew.lParam, 1639 false, TreeViewAction.EXPAND); 1640 onBeforeExpand(cea); 1641 m.result = cea.cancel; 1642 break; 1643 1644 default: 1645 } 1646 break; 1647 1648 case TVN_ITEMEXPANDEDW: 1649 goto item_expanded; 1650 1651 case TVN_ITEMEXPANDEDA: 1652 if (dfl.internal.utf.useUnicode) { 1653 break; 1654 } 1655 item_expanded: 1656 1657 nmtv = cast(NM_TREEVIEW*)nmh; 1658 switch (nmtv.action) { 1659 case TVE_COLLAPSE: { 1660 scope TreeViewEventArgs tvea = new TreeViewEventArgs( 1661 cast(TreeNode)cast(void*)nmtv.itemNew.lParam, 1662 TreeViewAction.COLLAPSE); 1663 onAfterCollapse(tvea); 1664 } 1665 break; 1666 1667 case TVE_EXPAND: { 1668 scope TreeViewEventArgs tvea = new TreeViewEventArgs( 1669 cast(TreeNode)cast(void*)nmtv.itemNew.lParam, 1670 TreeViewAction.EXPAND); 1671 onAfterExpand(tvea); 1672 } 1673 break; 1674 1675 default: 1676 } 1677 break; 1678 1679 case TVN_BEGINLABELEDITW: 1680 goto begin_label_edit; 1681 1682 case TVN_BEGINLABELEDITA: 1683 if (dfl.internal.utf.useUnicode) { 1684 break; 1685 } 1686 begin_label_edit: 1687 1688 { 1689 TV_DISPINFOA* nmdi; 1690 nmdi = cast(TV_DISPINFOA*)nmh; 1691 TreeNode node; 1692 node = cast(TreeNode)cast(void*)nmdi.item.lParam; 1693 scope NodeLabelEditEventArgs nleea = new NodeLabelEditEventArgs(node); 1694 onBeforeLabelEdit(nleea); 1695 m.result = nleea.cancelEdit; 1696 } 1697 break; 1698 1699 case TVN_ENDLABELEDITW: { 1700 Dstring label; 1701 TV_DISPINFOW* nmdi; 1702 nmdi = cast(TV_DISPINFOW*)nmh; 1703 if (nmdi.item.pszText) { 1704 TreeNode node; 1705 node = cast(TreeNode)cast(void*)nmdi.item.lParam; 1706 label = fromUnicodez(nmdi.item.pszText); 1707 scope NodeLabelEditEventArgs nleea = new NodeLabelEditEventArgs(node, label); 1708 onAfterLabelEdit(nleea); 1709 if (nleea.cancelEdit) { 1710 m.result = FALSE; 1711 } else { 1712 // TODO: check if correct implementation. 1713 // Update the node's cached text.. 1714 node.ttext = label; 1715 1716 m.result = TRUE; 1717 } 1718 } 1719 } 1720 break; 1721 1722 case TVN_ENDLABELEDITA: 1723 if (dfl.internal.utf.useUnicode) { 1724 break; 1725 } else { 1726 Dstring label; 1727 TV_DISPINFOA* nmdi; 1728 nmdi = cast(TV_DISPINFOA*)nmh; 1729 if (nmdi.item.pszText) { 1730 TreeNode node; 1731 node = cast(TreeNode)cast(void*)nmdi.item.lParam; 1732 label = fromAnsiz(nmdi.item.pszText); 1733 scope NodeLabelEditEventArgs nleea = new NodeLabelEditEventArgs(node, label); 1734 onAfterLabelEdit(nleea); 1735 if (nleea.cancelEdit) { 1736 m.result = FALSE; 1737 } else { 1738 // TODO: check if correct implementation. 1739 // Update the node's cached text.. 1740 node.ttext = label; 1741 1742 m.result = TRUE; 1743 } 1744 } 1745 break; 1746 } 1747 1748 default: 1749 } 1750 } 1751 break; 1752 1753 default: 1754 } 1755 } 1756 1757 1758 private: 1759 TreeNodeCollection tchildren; 1760 int ind = 19; // Indent. 1761 dchar pathsep = '\\'; 1762 bool _sort = false; 1763 int iheight = 16; 1764 version(DFL_NO_IMAGELIST) { 1765 } 1766 else { 1767 ImageList _imglist; 1768 int _selimgidx = -1; //0; 1769 } 1770 1771 1772 TreeNode treeNodeFromHandle(HTREEITEM hnode) { 1773 TV_ITEMA ti; 1774 ti.mask = TVIF_HANDLE | TVIF_PARAM; 1775 ti.hItem = hnode; 1776 if (SendMessageA(hwnd, TVM_GETITEMA, 0, cast(LPARAM)&ti)) { 1777 return cast(TreeNode)cast(void*)ti.lParam; 1778 } 1779 return null; 1780 } 1781 1782 package: 1783 final: 1784 LRESULT prevwproc(UINT msg, WPARAM wparam, LPARAM lparam) { 1785 //return CallWindowProcA(treeviewPrevWndProc, hwnd, msg, wparam, lparam); 1786 return dfl.internal.utf.callWindowProc(treeviewPrevWndProc, hwnd, msg, wparam, lparam); 1787 } 1788 } 1789