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