1 // Written by Christopher E. Miller 2 // See the included license.txt for copyright and license details. 3 4 5 /// 6 module dfl.textbox; 7 8 private import dfl.internal.dlib; 9 10 private import dfl.control, dfl.base, dfl.internal.winapi, dfl.application; 11 private import dfl.drawing, dfl.event, dfl.internal.utf; 12 13 version(DFL_NO_MENUS) 14 { 15 } 16 else 17 { 18 private import dfl.menu; 19 } 20 21 22 private extern(Windows) void _initTextBox(); 23 24 25 // Note: ControlStyles.CACHE_TEXT might not work correctly with a text box. 26 // It's not actually a bug, but a limitation of this control. 27 28 /// 29 abstract class TextBoxBase: ControlSuperClass // docmain 30 { 31 /// 32 final @property void acceptsTab(bool byes) // setter 33 { 34 atab = byes; 35 setStyle(ControlStyles.WANT_TAB_KEY, atab); 36 } 37 38 /// ditto 39 final @property bool acceptsTab() // getter 40 { 41 return atab; 42 } 43 44 45 /// 46 @property void borderStyle(BorderStyle bs) // setter 47 { 48 final switch(bs) 49 { 50 case BorderStyle.FIXED_3D: 51 _style(_style() & ~WS_BORDER); 52 _exStyle(_exStyle() | WS_EX_CLIENTEDGE); 53 break; 54 55 case BorderStyle.FIXED_SINGLE: 56 _exStyle(_exStyle() & ~WS_EX_CLIENTEDGE); 57 _style(_style() | WS_BORDER); 58 break; 59 60 case BorderStyle.NONE: 61 _style(_style() & ~WS_BORDER); 62 _exStyle(_exStyle() & ~WS_EX_CLIENTEDGE); 63 break; 64 } 65 66 if(created) 67 { 68 redrawEntire(); 69 } 70 } 71 72 /// ditto 73 @property BorderStyle borderStyle() // getter 74 { 75 if(_exStyle() & WS_EX_CLIENTEDGE) 76 return BorderStyle.FIXED_3D; 77 else if(_style() & WS_BORDER) 78 return BorderStyle.FIXED_SINGLE; 79 return BorderStyle.NONE; 80 } 81 82 83 /// 84 final @property bool canUndo() // getter 85 { 86 if(!created) 87 return false; 88 return SendMessageA(handle, EM_CANUNDO, 0, 0) != 0; 89 } 90 91 92 /// 93 final @property void hideSelection(bool byes) // setter 94 { 95 if(byes) 96 _style(_style() & ~ES_NOHIDESEL); 97 else 98 _style(_style() | ES_NOHIDESEL); 99 } 100 101 /// ditto 102 final @property bool hideSelection() // getter 103 { 104 return (_style() & ES_NOHIDESEL) == 0; 105 } 106 107 108 /// 109 final @property void lines(Dstring[] lns) // setter 110 { 111 Dstring result; 112 foreach(Dstring s; lns) 113 { 114 result ~= s ~ "\r\n"; 115 } 116 if(result.length) // Remove last \r\n. 117 result = result[0 .. result.length - 2]; 118 text = result; 119 } 120 121 /// ditto 122 final @property Dstring[] lines() // getter 123 { 124 return stringSplitLines(text); 125 } 126 127 128 /// 129 @property void maxLength(uint len) // setter 130 { 131 if(!len) 132 { 133 if(multiline) 134 lim = 0xFFFFFFFF; 135 else 136 lim = 0x7FFFFFFE; 137 } 138 else 139 { 140 lim = len; 141 } 142 143 if(created) 144 { 145 Message m; 146 m = Message(handle, EM_SETLIMITTEXT, cast(WPARAM)lim, 0); 147 prevWndProc(m); 148 } 149 } 150 151 /// ditto 152 @property uint maxLength() // getter 153 { 154 if(created) 155 lim = cast(uint)SendMessageA(handle, EM_GETLIMITTEXT, 0, 0); 156 return lim; 157 } 158 159 160 /// 161 final uint getLineCount() 162 { 163 if(!multiline) 164 return 1; 165 166 if(created) 167 { 168 return cast(uint)SendMessageA(handle, EM_GETLINECOUNT, 0, 0); 169 } 170 171 Dstring s; 172 size_t iw = 0; 173 uint count = 1; 174 s = text; 175 for(; iw != s.length; iw++) 176 { 177 if('\r' == s[iw]) 178 { 179 if(iw + 1 == s.length) 180 break; 181 if('\n' == s[iw + 1]) 182 { 183 iw++; 184 count++; 185 } 186 } 187 } 188 return count; 189 } 190 191 192 /// 193 final @property void modified(bool byes) // setter 194 { 195 if(created) 196 SendMessageA(handle, EM_SETMODIFY, byes, 0); 197 } 198 199 /// ditto 200 final @property bool modified() // getter 201 { 202 if(!created) 203 return false; 204 return SendMessageA(handle, EM_GETMODIFY, 0, 0) != 0; 205 } 206 207 208 /// 209 @property void multiline(bool byes) // setter 210 { 211 /+ 212 if(byes) 213 _style(_style() & ~ES_AUTOHSCROLL | ES_MULTILINE); 214 else 215 _style(_style() & ~ES_MULTILINE | ES_AUTOHSCROLL); 216 +/ 217 218 // TODO: check if correct implementation. 219 220 LONG st; 221 222 if(byes) 223 { 224 st = _style() | ES_MULTILINE | ES_AUTOVSCROLL; 225 226 if(_wrap) 227 st &= ~ES_AUTOHSCROLL; 228 else 229 st |= ES_AUTOHSCROLL; 230 } 231 else 232 { 233 st = _style() & ~(ES_MULTILINE | ES_AUTOVSCROLL); 234 235 // Always H-scroll when single line. 236 st |= ES_AUTOHSCROLL; 237 } 238 239 _style(st); 240 241 _crecreate(); 242 } 243 244 /// ditto 245 @property bool multiline() // getter 246 { 247 return (_style() & ES_MULTILINE) != 0; 248 } 249 250 251 /// 252 final @property void readOnly(bool byes) // setter 253 { 254 if(created) 255 { 256 SendMessageA(handle, EM_SETREADONLY, byes, 0); // Should trigger WM_STYLECHANGED. 257 invalidate(); // ? 258 } 259 else 260 { 261 if(byes) 262 _style(_style() | ES_READONLY); 263 else 264 _style(_style() & ~ES_READONLY); 265 } 266 } 267 268 /// ditto 269 final @property bool readOnly() // getter 270 { 271 return (_style() & ES_READONLY) != 0; 272 } 273 274 275 /// 276 @property void selectedText(Dstring sel) // setter 277 { 278 /+ 279 if(created) 280 SendMessageA(handle, EM_REPLACESEL, FALSE, cast(LPARAM)unsafeStringz(sel)); 281 +/ 282 283 if(created) 284 { 285 //dfl.internal.utf.sendMessage(handle, EM_REPLACESEL, FALSE, sel); 286 dfl.internal.utf.sendMessageUnsafe(handle, EM_REPLACESEL, FALSE, sel); 287 } 288 } 289 290 /// ditto 291 @property Dstring selectedText() // getter 292 { 293 /+ 294 if(created) 295 { 296 uint v1, v2; 297 SendMessageA(handle, EM_GETSEL, cast(WPARAM)&v1, cast(LPARAM)&v2); 298 if(v1 == v2) 299 return null; 300 assert(v2 > v1); 301 Dstring result = new char[v2 - v1 + 1]; 302 result[result.length - 1] = 0; 303 result = result[0 .. result.length - 1]; 304 result[] = text[v1 .. v2]; 305 return result; 306 } 307 return null; 308 +/ 309 310 if(created) 311 return dfl.internal.utf.getSelectedText(handle); 312 return null; 313 } 314 315 316 /// 317 @property void selectionLength(uint len) // setter 318 { 319 if(created) 320 { 321 uint v1, v2; 322 SendMessageA(handle, EM_GETSEL, cast(WPARAM)&v1, cast(LPARAM)&v2); 323 v2 = v1 + len; 324 SendMessageA(handle, EM_SETSEL, v1, v2); 325 } 326 } 327 328 /// ditto 329 // Current selection length, in characters. 330 // This does not necessarily correspond to the length of chars; some characters use multiple chars. 331 // An end of line (\r\n) takes up 2 characters. 332 @property uint selectionLength() // getter 333 { 334 if(created) 335 { 336 uint v1, v2; 337 SendMessageA(handle, EM_GETSEL, cast(WPARAM)&v1, cast(LPARAM)&v2); 338 assert(v2 >= v1); 339 return v2 - v1; 340 } 341 return 0; 342 } 343 344 345 /// 346 @property void selectionStart(uint pos) // setter 347 { 348 if(created) 349 { 350 uint v1, v2; 351 SendMessageA(handle, EM_GETSEL, cast(WPARAM)&v1, cast(LPARAM)&v2); 352 assert(v2 >= v1); 353 v2 = pos + (v2 - v1); 354 SendMessageA(handle, EM_SETSEL, pos, v2); 355 } 356 } 357 358 /// ditto 359 // Current selection starting index, in characters. 360 // This does not necessarily correspond to the index of chars; some characters use multiple chars. 361 // An end of line (\r\n) takes up 2 characters. 362 @property uint selectionStart() // getter 363 { 364 if(created) 365 { 366 uint v1, v2; 367 SendMessageA(handle, EM_GETSEL, cast(WPARAM)&v1, cast(LPARAM)&v2); 368 return v1; 369 } 370 return 0; 371 } 372 373 374 /// 375 // Number of characters in the textbox. 376 // This does not necessarily correspond to the number of chars; some characters use multiple chars. 377 // An end of line (\r\n) takes up 2 characters. 378 // Return may be larger than the amount of characters. 379 // This is a lot faster than retrieving the text, but retrieving the text is completely accurate. 380 @property uint textLength() // getter 381 { 382 if(!(ctrlStyle & ControlStyles.CACHE_TEXT) && created()) 383 //return cast(uint)SendMessageA(handle, WM_GETTEXTLENGTH, 0, 0); 384 return cast(uint)dfl.internal.utf.sendMessage(handle, WM_GETTEXTLENGTH, 0, 0); 385 return wtext.length; 386 } 387 388 389 /// 390 @property final void wordWrap(bool byes) // setter 391 { 392 /+ 393 if(byes) 394 _style(_style() | ES_AUTOVSCROLL); 395 else 396 _style(_style() & ~ES_AUTOVSCROLL); 397 +/ 398 399 // TODO: check if correct implementation. 400 401 if(_wrap == byes) 402 return; 403 404 _wrap = byes; 405 406 // Always H-scroll when single line. 407 if(multiline) 408 { 409 if(byes) 410 { 411 _style(_style() & ~(ES_AUTOHSCROLL | WS_HSCROLL)); 412 } 413 else 414 { 415 LONG st; 416 st = _style(); 417 418 st |= ES_AUTOHSCROLL; 419 420 if(_hscroll) 421 st |= WS_HSCROLL; 422 423 _style(st); 424 } 425 } 426 427 _crecreate(); 428 } 429 430 /// ditto 431 final @property bool wordWrap() // getter 432 { 433 //return (_style() & ES_AUTOVSCROLL) != 0; 434 435 return _wrap; 436 } 437 438 439 /// 440 final void appendText(Dstring txt) 441 { 442 if(created) 443 { 444 selectionStart = textLength; 445 selectedText = txt; 446 } 447 else 448 { 449 text = text ~ txt; 450 } 451 } 452 453 454 /// 455 final void clear() 456 { 457 /+ 458 // WM_CLEAR only clears the selection ? 459 if(created) 460 SendMessageA(handle, WM_CLEAR, 0, 0); 461 else 462 wtext = null; 463 +/ 464 465 text = null; 466 } 467 468 469 /// 470 final void clearUndo() 471 { 472 if(created) 473 SendMessageA(handle, EM_EMPTYUNDOBUFFER, 0, 0); 474 } 475 476 477 /// 478 final void copy() 479 { 480 if(created) 481 { 482 SendMessageA(handle, WM_COPY, 0, 0); 483 } 484 else 485 { 486 // There's never a selection if the window isn't created; so just empty the clipboard. 487 488 if(!OpenClipboard(null)) 489 { 490 debug(APP_PRINT) 491 cprintf("Unable to OpenClipboard().\n"); 492 //throw new DflException("Unable to set clipboard data."); 493 return; 494 } 495 EmptyClipboard(); 496 CloseClipboard(); 497 } 498 } 499 500 501 /// 502 final void cut() 503 { 504 if(created) 505 { 506 SendMessageA(handle, WM_CUT, 0, 0); 507 } 508 else 509 { 510 // There's never a selection if the window isn't created; so just empty the clipboard. 511 512 if(!OpenClipboard(null)) 513 { 514 debug(APP_PRINT) 515 cprintf("Unable to OpenClipboard().\n"); 516 //throw new DflException("Unable to set clipboard data."); 517 return; 518 } 519 EmptyClipboard(); 520 CloseClipboard(); 521 } 522 } 523 524 525 /// 526 final void paste() 527 { 528 if(created) 529 { 530 SendMessageA(handle, WM_PASTE, 0, 0); 531 } 532 else 533 { 534 // Can't do anything because there's no selection ? 535 } 536 } 537 538 539 /// 540 final void scrollToCaret() 541 { 542 if(created) 543 SendMessageA(handle, EM_SCROLLCARET, 0, 0); 544 } 545 546 547 /// 548 final void select(uint start, uint length) 549 { 550 if(created) 551 SendMessageA(handle, EM_SETSEL, start, start + length); 552 } 553 554 alias Control.select select; // Overload. 555 556 557 /// 558 final void selectAll() 559 { 560 if(created) 561 SendMessageA(handle, EM_SETSEL, 0, -1); 562 } 563 564 565 override Dstring toString() 566 { 567 return text; // ? 568 } 569 570 571 /// 572 final void undo() 573 { 574 if(created) 575 SendMessageA(handle, EM_UNDO, 0, 0); 576 } 577 578 579 /+ 580 override void createHandle() 581 { 582 if(isHandleCreated) 583 return; 584 585 createClassHandle(TEXTBOX_CLASSNAME); 586 587 onHandleCreated(EventArgs.empty); 588 } 589 +/ 590 591 592 override void createHandle() 593 { 594 if(!isHandleCreated) 595 { 596 Dstring txt; 597 txt = wtext; 598 599 super.createHandle(); 600 601 //dfl.internal.utf.setWindowText(hwnd, txt); 602 text = txt; // So that it can be overridden. 603 } 604 } 605 606 607 protected override void createParams(ref CreateParams cp) 608 { 609 super.createParams(cp); 610 611 cp.className = TEXTBOX_CLASSNAME; 612 cp.caption = null; // Set in createHandle() to allow larger buffers. 613 } 614 615 616 protected override void onHandleCreated(EventArgs ea) 617 { 618 super.onHandleCreated(ea); 619 620 //SendMessageA(hwnd, EM_SETLIMITTEXT, cast(WPARAM)lim, 0); 621 maxLength = lim; // Call virtual function. 622 } 623 624 625 private 626 { 627 version(DFL_NO_MENUS) 628 { 629 } 630 else 631 { 632 void menuUndo(Object sender, EventArgs ea) 633 { 634 undo(); 635 } 636 637 638 void menuCut(Object sender, EventArgs ea) 639 { 640 cut(); 641 } 642 643 644 void menuCopy(Object sender, EventArgs ea) 645 { 646 copy(); 647 } 648 649 650 void menuPaste(Object sender, EventArgs ea) 651 { 652 paste(); 653 } 654 655 656 void menuDelete(Object sender, EventArgs ea) 657 { 658 // Only clear selection. 659 SendMessageA(handle, WM_CLEAR, 0, 0); 660 } 661 662 663 void menuSelectAll(Object sender, EventArgs ea) 664 { 665 selectAll(); 666 } 667 668 669 bool isClipboardText() 670 { 671 if(!OpenClipboard(handle)) 672 return false; 673 674 bool result; 675 result = GetClipboardData(CF_TEXT) != null; 676 677 CloseClipboard(); 678 679 return result; 680 } 681 682 683 void menuPopup(Object sender, EventArgs ea) 684 { 685 int slen, tlen; 686 bool issel; 687 688 slen = selectionLength; 689 tlen = textLength; 690 issel = slen != 0; 691 692 miundo.enabled = canUndo; 693 micut.enabled = !readOnly() && issel; 694 micopy.enabled = issel; 695 mipaste.enabled = !readOnly() && isClipboardText(); 696 midel.enabled = !readOnly() && issel; 697 misel.enabled = tlen != 0 && tlen != slen; 698 } 699 700 701 MenuItem miundo, micut, micopy, mipaste, midel, misel; 702 } 703 } 704 705 706 this() 707 { 708 _initTextBox(); 709 710 wstyle |= WS_TABSTOP | ES_AUTOHSCROLL; 711 wexstyle |= WS_EX_CLIENTEDGE; 712 ctrlStyle |= ControlStyles.SELECTABLE; 713 wclassStyle = textBoxClassStyle; 714 715 version(DFL_NO_MENUS) 716 { 717 } 718 else 719 { 720 MenuItem mi; 721 722 cmenu = new ContextMenu; 723 cmenu.popup ~= &menuPopup; 724 725 miundo = new MenuItem; 726 miundo.text = "&Undo"; 727 miundo.click ~= &menuUndo; 728 miundo.index = 0; 729 cmenu.menuItems.add(miundo); 730 731 mi = new MenuItem; 732 mi.text = "-"; 733 mi.index = 1; 734 cmenu.menuItems.add(mi); 735 736 micut = new MenuItem; 737 micut.text = "Cu&t"; 738 micut.click ~= &menuCut; 739 micut.index = 2; 740 cmenu.menuItems.add(micut); 741 742 micopy = new MenuItem; 743 micopy.text = "&Copy"; 744 micopy.click ~= &menuCopy; 745 micopy.index = 3; 746 cmenu.menuItems.add(micopy); 747 748 mipaste = new MenuItem; 749 mipaste.text = "&Paste"; 750 mipaste.click ~= &menuPaste; 751 mipaste.index = 4; 752 cmenu.menuItems.add(mipaste); 753 754 midel = new MenuItem; 755 midel.text = "&Delete"; 756 midel.click ~= &menuDelete; 757 midel.index = 5; 758 cmenu.menuItems.add(midel); 759 760 mi = new MenuItem; 761 mi.text = "-"; 762 mi.index = 6; 763 cmenu.menuItems.add(mi); 764 765 misel = new MenuItem; 766 misel.text = "Select &All"; 767 misel.click ~= &menuSelectAll; 768 misel.index = 7; 769 cmenu.menuItems.add(misel); 770 } 771 } 772 773 774 override @property Color backColor() // getter 775 { 776 if(Color.empty == backc) 777 return defaultBackColor; 778 return backc; 779 } 780 781 alias Control.backColor backColor; // Overload. 782 783 784 static @property Color defaultBackColor() // getter 785 { 786 return Color.systemColor(COLOR_WINDOW); 787 } 788 789 790 override @property Color foreColor() // getter 791 { 792 if(Color.empty == forec) 793 return defaultForeColor; 794 return forec; 795 } 796 797 alias Control.foreColor foreColor; // Overload. 798 799 800 static @property Color defaultForeColor() //getter 801 { 802 return Color.systemColor(COLOR_WINDOWTEXT); 803 } 804 805 806 override @property Cursor cursor() // getter 807 { 808 if(!wcurs) 809 return _defaultCursor; 810 return wcurs; 811 } 812 813 alias Control.cursor cursor; // Overload. 814 815 816 /// 817 int getFirstCharIndexFromLine(int line) 818 { 819 if(!isHandleCreated) 820 return -1; // ... 821 if(line < 0) 822 return -1; 823 return SendMessageA(hwnd, EM_LINEINDEX, line, 0); 824 } 825 826 /// ditto 827 int getFirstCharIndexOfCurrentLine() 828 { 829 if(!isHandleCreated) 830 return -1; // ... 831 return SendMessageA(hwnd, EM_LINEINDEX, -1, 0); 832 } 833 834 835 /// 836 int getLineFromCharIndex(int charIndex) 837 { 838 if(!isHandleCreated) 839 return -1; // ... 840 if(charIndex < 0) 841 return -1; 842 return SendMessageA(hwnd, EM_LINEFROMCHAR, charIndex, 0); 843 } 844 845 846 /// 847 Point getPositionFromCharIndex(int charIndex) 848 { 849 if(!isHandleCreated) 850 return Point(0, 0); // ... 851 if(charIndex < 0) 852 return Point(0, 0); 853 POINT point; 854 SendMessageA(hwnd, EM_POSFROMCHAR, cast(WPARAM)&point, charIndex); 855 return Point(point.x, point.y); 856 } 857 858 /// ditto 859 int getCharIndexFromPosition(Point pt) 860 { 861 if(!isHandleCreated) 862 return -1; // ... 863 if(!multiline) 864 return 0; 865 auto lresult = SendMessageA(hwnd, EM_CHARFROMPOS, 0, MAKELPARAM(pt.x, pt.y)); 866 if(-1 == lresult) 867 return -1; 868 return cast(int)cast(short)(lresult & 0xFFFF); 869 } 870 871 872 package static @property Cursor _defaultCursor() // getter 873 { 874 static Cursor def = null; 875 876 if(!def) 877 { 878 synchronized 879 { 880 if(!def) 881 def = new SafeCursor(LoadCursorA(null, IDC_IBEAM)); 882 } 883 } 884 885 return def; 886 } 887 888 889 protected: 890 protected override void onReflectedMessage(ref Message m) 891 { 892 super.onReflectedMessage(m); 893 894 switch(m.msg) 895 { 896 case WM_COMMAND: 897 switch(HIWORD(m.wParam)) 898 { 899 case EN_CHANGE: 900 onTextChanged(EventArgs.empty); 901 break; 902 903 default: 904 } 905 break; 906 907 /+ 908 case WM_CTLCOLORSTATIC: 909 case WM_CTLCOLOREDIT: 910 /+ 911 //SetBkColor(cast(HDC)m.wParam, backColor.toRgb()); // ? 912 SetBkMode(cast(HDC)m.wParam, OPAQUE); // ? 913 +/ 914 break; 915 +/ 916 917 default: 918 } 919 } 920 921 922 override void prevWndProc(ref Message msg) 923 { 924 version(DFL_NO_MENUS) 925 { 926 // Don't prevent WM_CONTEXTMENU so at least it'll have a default menu. 927 } 928 else 929 { 930 if(msg.msg == WM_CONTEXTMENU) // Ignore the default context menu. 931 return; 932 } 933 934 //msg.result = CallWindowProcA(textBoxPrevWndProc, msg.hWnd, msg.msg, msg.wParam, msg.lParam); 935 msg.result = dfl.internal.utf.callWindowProc(textBoxPrevWndProc, msg.hWnd, msg.msg, msg.wParam, msg.lParam); 936 } 937 938 939 protected override bool processKeyEventArgs(ref Message msg) // package 940 { 941 switch(msg.msg) 942 { 943 case WM_KEYDOWN: 944 case WM_KEYUP: 945 case WM_CHAR: 946 if('\t' == msg.wParam) 947 { 948 // TODO: fix this. This case shouldn't be needed. 949 if(atab) 950 { 951 if(super.processKeyEventArgs(msg)) 952 return true; // Handled. 953 if(WM_KEYDOWN == msg.msg) 954 { 955 if(multiline) // Only multiline textboxes can have real tabs.. 956 { 957 //selectedText = "\t"; 958 //SendMessageA(handle, EM_REPLACESEL, TRUE, cast(LPARAM)"\t".ptr); // Allow undo. // Crashes DMD 0.161. 959 auto str = "\t".ptr; 960 SendMessageA(handle, EM_REPLACESEL, TRUE, cast(LPARAM)str); // Allow undo. 961 } 962 } 963 return true; // Handled. 964 } 965 } 966 break; 967 968 default: 969 } 970 return super.processKeyEventArgs(msg); 971 } 972 973 974 override void wndProc(ref Message msg) 975 { 976 switch(msg.msg) 977 { 978 case WM_GETDLGCODE: 979 super.wndProc(msg); 980 if(atab) 981 { 982 //if(GetKeyState(Keys.TAB) & 0x8000) 983 { 984 //msg.result |= DLGC_WANTALLKEYS; 985 msg.result |= DLGC_WANTTAB; 986 } 987 } 988 else 989 { 990 msg.result &= ~DLGC_WANTTAB; 991 } 992 return; 993 994 default: 995 super.wndProc(msg); 996 } 997 } 998 999 1000 override @property Size defaultSize() // getter 1001 { 1002 return Size(120, 23); // ? 1003 } 1004 1005 1006 private: 1007 package uint lim = 30_000; // Documented as default. 1008 bool _wrap = true; 1009 bool _hscroll; 1010 1011 bool atab = false; 1012 1013 /+ 1014 @property bool atab() // getter 1015 { 1016 if(_style() & X) 1017 return true; 1018 return false; 1019 } 1020 1021 @property void atab(bool byes) // setter 1022 { 1023 if(byes) 1024 _style(_style() | X); 1025 else 1026 _style(_style() & ~X); 1027 } 1028 +/ 1029 1030 1031 @property void hscroll(bool byes) // setter 1032 { 1033 _hscroll = byes; 1034 1035 if(byes && (!_wrap || !multiline)) 1036 _style(_style() | WS_HSCROLL | ES_AUTOHSCROLL); 1037 } 1038 1039 1040 @property bool hscroll() // getter 1041 { 1042 return _hscroll; 1043 } 1044 } 1045 1046 1047 /// 1048 class TextBox: TextBoxBase // docmain 1049 { 1050 /// 1051 final @property void acceptsReturn(bool byes) // setter 1052 { 1053 if(byes) 1054 _style(_style() | ES_WANTRETURN); 1055 else 1056 _style(_style() & ~ES_WANTRETURN); 1057 } 1058 1059 /// ditto 1060 final @property bool acceptsReturn() // getter 1061 { 1062 return (_style() & ES_WANTRETURN) != 0; 1063 } 1064 1065 1066 /// 1067 final @property void characterCasing(CharacterCasing cc) // setter 1068 { 1069 LONG wl = _style() & ~(ES_UPPERCASE | ES_LOWERCASE); 1070 1071 final switch(cc) 1072 { 1073 case CharacterCasing.UPPER: 1074 wl |= ES_UPPERCASE; 1075 break; 1076 1077 case CharacterCasing.LOWER: 1078 wl |= ES_LOWERCASE; 1079 break; 1080 1081 case CharacterCasing.NORMAL: 1082 break; 1083 } 1084 1085 _style(wl); 1086 } 1087 1088 /// ditto 1089 final @property CharacterCasing characterCasing() // getter 1090 { 1091 LONG wl = _style(); 1092 if(wl & ES_UPPERCASE) 1093 return CharacterCasing.UPPER; 1094 else if(wl & ES_LOWERCASE) 1095 return CharacterCasing.LOWER; 1096 return CharacterCasing.NORMAL; 1097 } 1098 1099 1100 /// 1101 // Set to 0 (NUL) to remove. 1102 final @property void passwordChar(dchar pwc) // setter 1103 { 1104 if(pwc) 1105 { 1106 // When the EM_SETPASSWORDCHAR message is received by an edit control, 1107 // the edit control redraws all visible characters by using the 1108 // character specified by the ch parameter. 1109 1110 if(created) 1111 //SendMessageA(handle, EM_SETPASSWORDCHAR, pwc, 0); 1112 dfl.internal.utf.emSetPasswordChar(handle, pwc); 1113 else 1114 _style(_style() | ES_PASSWORD); 1115 } 1116 else 1117 { 1118 // The style ES_PASSWORD is removed if an EM_SETPASSWORDCHAR message 1119 // is sent with the ch parameter set to zero. 1120 1121 if(created) 1122 //SendMessageA(handle, EM_SETPASSWORDCHAR, 0, 0); 1123 dfl.internal.utf.emSetPasswordChar(handle, 0); 1124 else 1125 _style(_style() & ~ES_PASSWORD); 1126 } 1127 1128 passchar = pwc; 1129 } 1130 1131 /// ditto 1132 final @property dchar passwordChar() // getter 1133 { 1134 if(created) 1135 //passchar = cast(dchar)SendMessageA(handle, EM_GETPASSWORDCHAR, 0, 0); 1136 passchar = dfl.internal.utf.emGetPasswordChar(handle); 1137 return passchar; 1138 } 1139 1140 1141 /// 1142 final @property void scrollBars(ScrollBars sb) // setter 1143 { 1144 /+ 1145 switch(sb) 1146 { 1147 case ScrollBars.BOTH: 1148 _style(_style() | WS_HSCROLL | WS_VSCROLL); 1149 break; 1150 1151 case ScrollBars.HORIZONTAL: 1152 _style(_style() & ~WS_VSCROLL | WS_HSCROLL); 1153 break; 1154 1155 case ScrollBars.VERTICAL: 1156 _style(_style() & ~WS_HSCROLL | WS_VSCROLL); 1157 break; 1158 1159 case ScrollBars.NONE: 1160 _style(_style() & ~(WS_HSCROLL | WS_VSCROLL)); 1161 break; 1162 } 1163 +/ 1164 final switch(sb) 1165 { 1166 case ScrollBars.BOTH: 1167 _style(_style() | WS_VSCROLL); 1168 hscroll = true; 1169 break; 1170 1171 case ScrollBars.HORIZONTAL: 1172 _style(_style() & ~WS_VSCROLL); 1173 hscroll = true; 1174 break; 1175 1176 case ScrollBars.VERTICAL: 1177 _style(_style() | WS_VSCROLL); 1178 hscroll = false; 1179 break; 1180 1181 case ScrollBars.NONE: 1182 _style(_style() & ~WS_VSCROLL); 1183 hscroll = false; 1184 break; 1185 } 1186 1187 if(created) 1188 redrawEntire(); 1189 } 1190 1191 /// ditto 1192 final @property ScrollBars scrollBars() // getter 1193 { 1194 LONG wl = _style(); 1195 1196 //if(wl & WS_HSCROLL) 1197 if(hscroll) 1198 { 1199 if(wl & WS_VSCROLL) 1200 return ScrollBars.BOTH; 1201 return ScrollBars.HORIZONTAL; 1202 } 1203 if(wl & WS_VSCROLL) 1204 return ScrollBars.VERTICAL; 1205 return ScrollBars.NONE; 1206 } 1207 1208 1209 /// 1210 final @property void textAlign(HorizontalAlignment ha) // setter 1211 { 1212 LONG wl = _style() & ~(ES_RIGHT | ES_CENTER | ES_LEFT); 1213 1214 final switch(ha) 1215 { 1216 case HorizontalAlignment.RIGHT: 1217 wl |= ES_RIGHT; 1218 break; 1219 1220 case HorizontalAlignment.CENTER: 1221 wl |= ES_CENTER; 1222 break; 1223 1224 case HorizontalAlignment.LEFT: 1225 wl |= ES_LEFT; 1226 break; 1227 } 1228 1229 _style(wl); 1230 1231 _crecreate(); 1232 } 1233 1234 /// ditto 1235 final @property HorizontalAlignment textAlign() // getter 1236 { 1237 LONG wl = _style(); 1238 1239 if(wl & ES_RIGHT) 1240 return HorizontalAlignment.RIGHT; 1241 if(wl & ES_CENTER) 1242 return HorizontalAlignment.CENTER; 1243 return HorizontalAlignment.LEFT; 1244 } 1245 1246 1247 this() 1248 { 1249 wstyle |= ES_LEFT; 1250 } 1251 1252 1253 protected override @property void onHandleCreated(EventArgs ea) 1254 { 1255 super.onHandleCreated(ea); 1256 1257 if(passchar) 1258 { 1259 SendMessageA(hwnd, EM_SETPASSWORDCHAR, passchar, 0); 1260 } 1261 } 1262 1263 1264 /+ 1265 override @property void wndProc(ref Message msg) 1266 { 1267 switch(msg.msg) 1268 { 1269 /+ 1270 case WM_GETDLGCODE: 1271 if(!acceptsReturn && (GetKeyState(Keys.RETURN) & 0x8000)) 1272 { 1273 // Hack. 1274 msg.result = DLGC_HASSETSEL | DLGC_WANTCHARS | DLGC_WANTARROWS; 1275 return; 1276 } 1277 break; 1278 +/ 1279 1280 default: 1281 } 1282 1283 super.wndProc(msg); 1284 } 1285 +/ 1286 1287 1288 private: 1289 dchar passchar = 0; 1290 } 1291