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