1 /* 2 * Edit control 3 * 4 * Copyright David W. Metcalfe, 1994 5 * Copyright William Magro, 1995, 1996 6 * Copyright Frans van Dorsselaer, 1996, 1997 7 * Copyright Frank Richter, 2005 8 * 9 * 10 * This library is free software; you can redistribute it and/or 11 * modify it under the terms of the GNU Lesser General Public 12 * License as published by the Free Software Foundation; either 13 * version 2.1 of the License, or (at your option) any later version. 14 * 15 * This library is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 18 * Lesser General Public License for more details. 19 * 20 * You should have received a copy of the GNU Lesser General Public 21 * License along with this library; if not, write to the Free Software 22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 23 * 24 * TODO: 25 * - EDITBALLOONTIP structure 26 * - EM_GETCUEBANNER/Edit_GetCueBannerText 27 * - EM_HIDEBALLOONTIP/Edit_HideBalloonTip 28 * - EM_SETCUEBANNER/Edit_SetCueBannerText 29 * - EM_SHOWBALLOONTIP/Edit_ShowBalloonTip 30 * - EM_GETIMESTATUS, EM_SETIMESTATUS 31 * - EN_ALIGN_LTR_EC 32 * - EN_ALIGN_RTL_EC 33 * - ES_OEMCONVERT 34 * 35 */ 36 37 #include "config.h" 38 39 #include <stdarg.h> 40 #include <string.h> 41 #include <stdlib.h> 42 43 #include "windef.h" 44 #include "winbase.h" 45 #include "wingdi.h" 46 #include "winuser.h" 47 #include "imm.h" 48 #ifdef __REACTOS__ 49 #include <immdev.h> 50 #endif 51 #include "usp10.h" 52 #include "commctrl.h" 53 #include "uxtheme.h" 54 #include "vsstyle.h" 55 #include "wine/unicode.h" 56 #include "wine/debug.h" 57 #include "wine/heap.h" 58 59 WINE_DEFAULT_DEBUG_CHANNEL(edit); 60 61 #define BUFLIMIT_INITIAL 30000 /* initial buffer size */ 62 #define GROWLENGTH 32 /* buffers granularity in bytes: must be power of 2 */ 63 #define ROUND_TO_GROW(size) (((size) + (GROWLENGTH - 1)) & ~(GROWLENGTH - 1)) 64 #define HSCROLL_FRACTION 3 /* scroll window by 1/3 width */ 65 66 /* 67 * extra flags for EDITSTATE.flags field 68 */ 69 #define EF_MODIFIED 0x0001 /* text has been modified */ 70 #define EF_FOCUSED 0x0002 /* we have input focus */ 71 #define EF_UPDATE 0x0004 /* notify parent of changed state */ 72 #define EF_VSCROLL_TRACK 0x0008 /* don't SetScrollPos() since we are tracking the thumb */ 73 #define EF_HSCROLL_TRACK 0x0010 /* don't SetScrollPos() since we are tracking the thumb */ 74 #define EF_AFTER_WRAP 0x0080 /* the caret is displayed after the last character of a 75 wrapped line, instead of in front of the next character */ 76 #define EF_USE_SOFTBRK 0x0100 /* Enable soft breaks in text. */ 77 #define EF_DIALOGMODE 0x0200 /* Indicates that we are inside a dialog window */ 78 79 #define ID_CB_LISTBOX 1000 80 81 typedef enum 82 { 83 END_0 = 0, /* line ends with terminating '\0' character */ 84 END_WRAP, /* line is wrapped */ 85 END_HARD, /* line ends with a hard return '\r\n' */ 86 END_SOFT, /* line ends with a soft return '\r\r\n' */ 87 END_RICH /* line ends with a single '\n' */ 88 } LINE_END; 89 90 typedef struct tagLINEDEF { 91 INT length; /* bruto length of a line in bytes */ 92 INT net_length; /* netto length of a line in visible characters */ 93 LINE_END ending; 94 INT width; /* width of the line in pixels */ 95 INT index; /* line index into the buffer */ 96 SCRIPT_STRING_ANALYSIS ssa; /* Uniscribe Data */ 97 struct tagLINEDEF *next; 98 } LINEDEF; 99 100 typedef struct 101 { 102 LPWSTR text; /* the actual contents of the control */ 103 UINT text_length; /* cached length of text buffer (in WCHARs) - use get_text_length() to retrieve */ 104 UINT buffer_size; /* the size of the buffer in characters */ 105 UINT buffer_limit; /* the maximum size to which the buffer may grow in characters */ 106 HFONT font; /* NULL means standard system font */ 107 INT x_offset; /* scroll offset for multi lines this is in pixels 108 for single lines it's in characters */ 109 #ifdef __REACTOS__ 110 DWORD dwCaretWidth; 111 #endif 112 INT line_height; /* height of a screen line in pixels */ 113 INT char_width; /* average character width in pixels */ 114 DWORD style; /* sane version of wnd->dwStyle */ 115 WORD flags; /* flags that are not in es->style or wnd->flags (EF_XXX) */ 116 INT undo_insert_count; /* number of characters inserted in sequence */ 117 UINT undo_position; /* character index of the insertion and deletion */ 118 LPWSTR undo_text; /* deleted text */ 119 UINT undo_buffer_size; /* size of the deleted text buffer */ 120 INT selection_start; /* == selection_end if no selection */ 121 INT selection_end; /* == current caret position */ 122 WCHAR password_char; /* == 0 if no password char, and for multi line controls */ 123 INT left_margin; /* in pixels */ 124 INT right_margin; /* in pixels */ 125 RECT format_rect; 126 INT text_width; /* width of the widest line in pixels for multi line controls 127 and just line width for single line controls */ 128 INT region_posx; /* Position of cursor relative to region: */ 129 INT region_posy; /* -1: to left, 0: within, 1: to right */ 130 EDITWORDBREAKPROCW word_break_proc; 131 INT line_count; /* number of lines */ 132 INT y_offset; /* scroll offset in number of lines */ 133 BOOL bCaptureState; /* flag indicating whether mouse was captured */ 134 BOOL bEnableState; /* flag keeping the enable state */ 135 HWND hwndSelf; /* the our window handle */ 136 HWND hwndParent; /* Handle of parent for sending EN_* messages. 137 Even if parent will change, EN_* messages 138 should be sent to the first parent. */ 139 HWND hwndListBox; /* handle of ComboBox's listbox or NULL */ 140 INT wheelDeltaRemainder; /* scroll wheel delta left over after scrolling whole lines */ 141 /* 142 * only for multi line controls 143 */ 144 INT lock_count; /* amount of re-entries in the EditWndProc */ 145 INT tabs_count; 146 LPINT tabs; 147 LINEDEF *first_line_def; /* linked list of (soft) linebreaks */ 148 HLOCAL hloc32W; /* our unicode local memory block */ 149 HLOCAL hlocapp; /* The text buffer handle belongs to the app */ 150 /* 151 * IME Data 152 */ 153 #ifndef __REACTOS__ /* Rely on the composition window */ 154 UINT composition_len; /* length of composition, 0 == no composition */ 155 int composition_start; /* the character position for the composition */ 156 #endif 157 /* 158 * Uniscribe Data 159 */ 160 SCRIPT_LOGATTR *logAttr; 161 SCRIPT_STRING_ANALYSIS ssa; /* Uniscribe Data for single line controls */ 162 } EDITSTATE; 163 164 165 #define SWAP_UINT32(x,y) do { UINT temp = (UINT)(x); (x) = (UINT)(y); (y) = temp; } while(0) 166 #define ORDER_UINT(x,y) do { if ((UINT)(y) < (UINT)(x)) SWAP_UINT32((x),(y)); } while(0) 167 168 static inline BOOL notify_parent(const EDITSTATE *es, INT code) 169 { 170 HWND hwnd = es->hwndSelf; 171 TRACE("notification %d sent to %p.\n", code, es->hwndParent); 172 SendMessageW(es->hwndParent, WM_COMMAND, MAKEWPARAM(GetWindowLongPtrW(es->hwndSelf, GWLP_ID), code), (LPARAM)es->hwndSelf); 173 return IsWindow(hwnd); 174 } 175 176 static LRESULT EDIT_EM_PosFromChar(EDITSTATE *es, INT index, BOOL after_wrap); 177 178 /********************************************************************* 179 * 180 * EM_CANUNDO 181 * 182 */ 183 static inline BOOL EDIT_EM_CanUndo(const EDITSTATE *es) 184 { 185 return (es->undo_insert_count || strlenW(es->undo_text)); 186 } 187 188 189 /********************************************************************* 190 * 191 * EM_EMPTYUNDOBUFFER 192 * 193 */ 194 static inline void EDIT_EM_EmptyUndoBuffer(EDITSTATE *es) 195 { 196 es->undo_insert_count = 0; 197 *es->undo_text = '\0'; 198 } 199 200 static HBRUSH EDIT_NotifyCtlColor(EDITSTATE *es, HDC hdc) 201 { 202 HBRUSH hbrush; 203 UINT msg; 204 205 if ((!es->bEnableState || (es->style & ES_READONLY))) 206 msg = WM_CTLCOLORSTATIC; 207 else 208 msg = WM_CTLCOLOREDIT; 209 210 /* Why do we notify to es->hwndParent, and we send this one to GetParent()? */ 211 hbrush = (HBRUSH)SendMessageW(GetParent(es->hwndSelf), msg, (WPARAM)hdc, (LPARAM)es->hwndSelf); 212 if (!hbrush) 213 hbrush = (HBRUSH)DefWindowProcW(GetParent(es->hwndSelf), msg, (WPARAM)hdc, (LPARAM)es->hwndSelf); 214 return hbrush; 215 } 216 217 218 static inline UINT get_text_length(EDITSTATE *es) 219 { 220 if(es->text_length == (UINT)-1) 221 es->text_length = strlenW(es->text); 222 return es->text_length; 223 } 224 225 226 /********************************************************************* 227 * 228 * EDIT_WordBreakProc 229 * 230 * Find the beginning of words. 231 * Note: unlike the specs for a WordBreakProc, this function can 232 * only be called without linebreaks between s[0] up to 233 * s[count - 1]. Remember it is only called 234 * internally, so we can decide this for ourselves. 235 * Additionally we will always be breaking the full string. 236 * 237 */ 238 static INT EDIT_WordBreakProc(EDITSTATE *es, LPWSTR s, INT index, INT count, INT action) 239 { 240 INT ret = 0; 241 242 TRACE("s=%p, index=%d, count=%d, action=%d\n", s, index, count, action); 243 244 if(!s) return 0; 245 246 if (!es->logAttr) 247 { 248 SCRIPT_ANALYSIS psa; 249 250 memset(&psa,0,sizeof(SCRIPT_ANALYSIS)); 251 psa.eScript = SCRIPT_UNDEFINED; 252 253 es->logAttr = heap_alloc(sizeof(SCRIPT_LOGATTR) * get_text_length(es)); 254 ScriptBreak(es->text, get_text_length(es), &psa, es->logAttr); 255 } 256 257 switch (action) { 258 case WB_LEFT: 259 if (index) 260 index--; 261 while (index && !es->logAttr[index].fSoftBreak) 262 index--; 263 ret = index; 264 break; 265 case WB_RIGHT: 266 if (!count) 267 break; 268 while (index < count && s[index] && !es->logAttr[index].fSoftBreak) 269 index++; 270 ret = index; 271 break; 272 case WB_ISDELIMITER: 273 ret = es->logAttr[index].fWhiteSpace; 274 break; 275 default: 276 ERR("unknown action code, please report !\n"); 277 break; 278 } 279 return ret; 280 } 281 282 283 /********************************************************************* 284 * 285 * EDIT_CallWordBreakProc 286 * 287 * Call appropriate WordBreakProc (internal or external). 288 * 289 * Note: The "start" argument should always be an index referring 290 * to es->text. The actual wordbreak proc might be 291 * 16 bit, so we can't always pass any 32 bit LPSTR. 292 * Hence we assume that es->text is the buffer that holds 293 * the string under examination (we can decide this for ourselves). 294 * 295 */ 296 static INT EDIT_CallWordBreakProc(EDITSTATE *es, INT start, INT index, INT count, INT action) 297 { 298 INT ret; 299 300 if (es->word_break_proc) 301 ret = es->word_break_proc(es->text + start, index, count, action); 302 else 303 ret = EDIT_WordBreakProc(es, es->text, index + start, count + start, action) - start; 304 305 return ret; 306 } 307 308 static inline void EDIT_InvalidateUniscribeData_linedef(LINEDEF *line_def) 309 { 310 if (line_def->ssa) 311 { 312 ScriptStringFree(&line_def->ssa); 313 line_def->ssa = NULL; 314 } 315 } 316 317 static inline void EDIT_InvalidateUniscribeData(EDITSTATE *es) 318 { 319 LINEDEF *line_def = es->first_line_def; 320 while (line_def) 321 { 322 EDIT_InvalidateUniscribeData_linedef(line_def); 323 line_def = line_def->next; 324 } 325 if (es->ssa) 326 { 327 ScriptStringFree(&es->ssa); 328 es->ssa = NULL; 329 } 330 } 331 332 static SCRIPT_STRING_ANALYSIS EDIT_UpdateUniscribeData_linedef(EDITSTATE *es, HDC dc, LINEDEF *line_def) 333 { 334 if (!line_def) 335 return NULL; 336 337 if (line_def->net_length && !line_def->ssa) 338 { 339 int index = line_def->index; 340 HFONT old_font = NULL; 341 HDC udc = dc; 342 SCRIPT_TABDEF tabdef; 343 HRESULT hr; 344 345 if (!udc) 346 udc = GetDC(es->hwndSelf); 347 if (es->font) 348 old_font = SelectObject(udc, es->font); 349 350 tabdef.cTabStops = es->tabs_count; 351 tabdef.iScale = GdiGetCharDimensions(udc, NULL, NULL); 352 tabdef.pTabStops = es->tabs; 353 tabdef.iTabOrigin = 0; 354 355 hr = ScriptStringAnalyse(udc, &es->text[index], line_def->net_length, 356 (1.5*line_def->net_length+16), -1, 357 SSA_LINK|SSA_FALLBACK|SSA_GLYPHS|SSA_TAB, -1, 358 NULL, NULL, NULL, &tabdef, NULL, &line_def->ssa); 359 if (FAILED(hr)) 360 { 361 WARN("ScriptStringAnalyse failed (%x)\n",hr); 362 line_def->ssa = NULL; 363 } 364 365 if (es->font) 366 SelectObject(udc, old_font); 367 if (udc != dc) 368 ReleaseDC(es->hwndSelf, udc); 369 } 370 371 return line_def->ssa; 372 } 373 374 static SCRIPT_STRING_ANALYSIS EDIT_UpdateUniscribeData(EDITSTATE *es, HDC dc, INT line) 375 { 376 LINEDEF *line_def; 377 378 if (!(es->style & ES_MULTILINE)) 379 { 380 if (!es->ssa) 381 { 382 INT length = get_text_length(es); 383 HFONT old_font = NULL; 384 HDC udc = dc; 385 386 if (!udc) 387 udc = GetDC(es->hwndSelf); 388 if (es->font) 389 old_font = SelectObject(udc, es->font); 390 391 if (es->style & ES_PASSWORD) 392 ScriptStringAnalyse(udc, &es->password_char, length, (1.5*length+16), -1, SSA_LINK|SSA_FALLBACK|SSA_GLYPHS|SSA_PASSWORD, -1, NULL, NULL, NULL, NULL, NULL, &es->ssa); 393 else 394 ScriptStringAnalyse(udc, es->text, length, (1.5*length+16), -1, SSA_LINK|SSA_FALLBACK|SSA_GLYPHS, -1, NULL, NULL, NULL, NULL, NULL, &es->ssa); 395 396 if (es->font) 397 SelectObject(udc, old_font); 398 if (udc != dc) 399 ReleaseDC(es->hwndSelf, udc); 400 } 401 return es->ssa; 402 } 403 else 404 { 405 line_def = es->first_line_def; 406 while (line_def && line) 407 { 408 line_def = line_def->next; 409 line--; 410 } 411 412 return EDIT_UpdateUniscribeData_linedef(es,dc,line_def); 413 } 414 } 415 416 static inline INT get_vertical_line_count(EDITSTATE *es) 417 { 418 INT vlc = (es->format_rect.bottom - es->format_rect.top) / es->line_height; 419 return max(1,vlc); 420 } 421 422 /********************************************************************* 423 * 424 * EDIT_BuildLineDefs_ML 425 * 426 * Build linked list of text lines. 427 * Lines can end with '\0' (last line), a character (if it is wrapped), 428 * a soft return '\r\r\n' or a hard return '\r\n' 429 * 430 */ 431 static void EDIT_BuildLineDefs_ML(EDITSTATE *es, INT istart, INT iend, INT delta, HRGN hrgn) 432 { 433 LPWSTR current_position, cp; 434 INT fw; 435 LINEDEF *current_line; 436 LINEDEF *previous_line; 437 LINEDEF *start_line; 438 INT line_index = 0, nstart_line, nstart_index; 439 INT line_count = es->line_count; 440 INT orig_net_length; 441 RECT rc; 442 INT vlc; 443 444 if (istart == iend && delta == 0) 445 return; 446 447 previous_line = NULL; 448 current_line = es->first_line_def; 449 450 /* Find starting line. istart must lie inside an existing line or 451 * at the end of buffer */ 452 do { 453 if (istart < current_line->index + current_line->length || 454 current_line->ending == END_0) 455 break; 456 457 previous_line = current_line; 458 current_line = current_line->next; 459 line_index++; 460 } while (current_line); 461 462 if (!current_line) /* Error occurred start is not inside previous buffer */ 463 { 464 FIXME(" modification occurred outside buffer\n"); 465 return; 466 } 467 468 /* Remember start of modifications in order to calculate update region */ 469 nstart_line = line_index; 470 nstart_index = current_line->index; 471 472 /* We must start to reformat from the previous line since the modifications 473 * may have caused the line to wrap upwards. */ 474 if (!(es->style & ES_AUTOHSCROLL) && line_index > 0) 475 { 476 line_index--; 477 current_line = previous_line; 478 } 479 start_line = current_line; 480 481 fw = es->format_rect.right - es->format_rect.left; 482 current_position = es->text + current_line->index; 483 vlc = get_vertical_line_count(es); 484 do { 485 if (current_line != start_line) 486 { 487 if (!current_line || current_line->index + delta > current_position - es->text) 488 { 489 /* The buffer has been expanded, create a new line and 490 insert it into the link list */ 491 LINEDEF *new_line = heap_alloc_zero(sizeof(*new_line)); 492 new_line->next = previous_line->next; 493 previous_line->next = new_line; 494 current_line = new_line; 495 es->line_count++; 496 } 497 else if (current_line->index + delta < current_position - es->text) 498 { 499 /* The previous line merged with this line so we delete this extra entry */ 500 previous_line->next = current_line->next; 501 heap_free(current_line); 502 current_line = previous_line->next; 503 es->line_count--; 504 continue; 505 } 506 else /* current_line->index + delta == current_position */ 507 { 508 if (current_position - es->text > iend) 509 break; /* We reached end of line modifications */ 510 /* else recalculate this line */ 511 } 512 } 513 514 current_line->index = current_position - es->text; 515 orig_net_length = current_line->net_length; 516 517 /* Find end of line */ 518 cp = current_position; 519 while (*cp) { 520 if (*cp == '\n') break; 521 if ((*cp == '\r') && (*(cp + 1) == '\n')) 522 break; 523 cp++; 524 } 525 526 /* Mark type of line termination */ 527 if (!(*cp)) { 528 current_line->ending = END_0; 529 current_line->net_length = strlenW(current_position); 530 } else if ((cp > current_position) && (*(cp - 1) == '\r')) { 531 current_line->ending = END_SOFT; 532 current_line->net_length = cp - current_position - 1; 533 } else if (*cp == '\n') { 534 current_line->ending = END_RICH; 535 current_line->net_length = cp - current_position; 536 } else { 537 current_line->ending = END_HARD; 538 current_line->net_length = cp - current_position; 539 } 540 541 if (current_line->net_length) 542 { 543 const SIZE *sz; 544 EDIT_InvalidateUniscribeData_linedef(current_line); 545 EDIT_UpdateUniscribeData_linedef(es, NULL, current_line); 546 if (current_line->ssa) 547 { 548 sz = ScriptString_pSize(current_line->ssa); 549 /* Calculate line width */ 550 current_line->width = sz->cx; 551 } 552 else current_line->width = es->char_width * current_line->net_length; 553 } 554 else current_line->width = 0; 555 556 /* FIXME: check here for lines that are too wide even in AUTOHSCROLL (> 32767 ???) */ 557 558 /* Line breaks just look back from the end and find the next break and try that. */ 559 560 if (!(es->style & ES_AUTOHSCROLL)) { 561 if (current_line->width > fw && fw > es->char_width) { 562 563 INT prev, next; 564 int w; 565 const SIZE *sz; 566 float d; 567 568 prev = current_line->net_length - 1; 569 w = current_line->net_length; 570 d = (float)current_line->width/(float)fw; 571 if (d > 1.2f) d -= 0.2f; 572 next = prev/d; 573 if (next >= prev) next = prev-1; 574 do { 575 prev = EDIT_CallWordBreakProc(es, current_position - es->text, 576 next, current_line->net_length, WB_LEFT); 577 current_line->net_length = prev; 578 EDIT_InvalidateUniscribeData_linedef(current_line); 579 EDIT_UpdateUniscribeData_linedef(es, NULL, current_line); 580 if (current_line->ssa) 581 sz = ScriptString_pSize(current_line->ssa); 582 else sz = 0; 583 if (sz) 584 current_line->width = sz->cx; 585 else 586 prev = 0; 587 next = prev - 1; 588 } while (prev && current_line->width > fw); 589 current_line->net_length = w; 590 591 if (prev == 0) { /* Didn't find a line break so force a break */ 592 INT *piDx; 593 const INT *count; 594 595 EDIT_InvalidateUniscribeData_linedef(current_line); 596 EDIT_UpdateUniscribeData_linedef(es, NULL, current_line); 597 598 if (current_line->ssa) 599 { 600 count = ScriptString_pcOutChars(current_line->ssa); 601 piDx = heap_alloc(sizeof(INT) * (*count)); 602 ScriptStringGetLogicalWidths(current_line->ssa,piDx); 603 604 prev = current_line->net_length-1; 605 do { 606 current_line->width -= piDx[prev]; 607 prev--; 608 } while ( prev > 0 && current_line->width > fw); 609 if (prev<=0) 610 prev = 1; 611 heap_free(piDx); 612 } 613 else 614 prev = (fw / es->char_width); 615 } 616 617 /* If the first line we are calculating, wrapped before istart, we must 618 * adjust istart in order for this to be reflected in the update region. */ 619 if (current_line->index == nstart_index && istart > current_line->index + prev) 620 istart = current_line->index + prev; 621 /* else if we are updating the previous line before the first line we 622 * are re-calculating and it expanded */ 623 else if (current_line == start_line && 624 current_line->index != nstart_index && orig_net_length < prev) 625 { 626 /* Line expanded due to an upwards line wrap so we must partially include 627 * previous line in update region */ 628 nstart_line = line_index; 629 nstart_index = current_line->index; 630 istart = current_line->index + orig_net_length; 631 } 632 633 current_line->net_length = prev; 634 current_line->ending = END_WRAP; 635 636 if (current_line->net_length > 0) 637 { 638 EDIT_UpdateUniscribeData_linedef(es, NULL, current_line); 639 if (current_line->ssa) 640 { 641 sz = ScriptString_pSize(current_line->ssa); 642 current_line->width = sz->cx; 643 } 644 else 645 current_line->width = 0; 646 } 647 else current_line->width = 0; 648 } 649 else if (current_line == start_line && 650 current_line->index != nstart_index && 651 orig_net_length < current_line->net_length) { 652 /* The previous line expanded but it's still not as wide as the client rect */ 653 /* The expansion is due to an upwards line wrap so we must partially include 654 it in the update region */ 655 nstart_line = line_index; 656 nstart_index = current_line->index; 657 istart = current_line->index + orig_net_length; 658 } 659 } 660 661 662 /* Adjust length to include line termination */ 663 switch (current_line->ending) { 664 case END_SOFT: 665 current_line->length = current_line->net_length + 3; 666 break; 667 case END_RICH: 668 current_line->length = current_line->net_length + 1; 669 break; 670 case END_HARD: 671 current_line->length = current_line->net_length + 2; 672 break; 673 case END_WRAP: 674 case END_0: 675 current_line->length = current_line->net_length; 676 break; 677 } 678 es->text_width = max(es->text_width, current_line->width); 679 current_position += current_line->length; 680 previous_line = current_line; 681 682 /* Discard data for non-visible lines. It will be calculated as needed */ 683 if ((line_index < es->y_offset) || (line_index > es->y_offset + vlc)) 684 EDIT_InvalidateUniscribeData_linedef(current_line); 685 686 current_line = current_line->next; 687 line_index++; 688 } while (previous_line->ending != END_0); 689 690 /* Finish adjusting line indexes by delta or remove hanging lines */ 691 if (previous_line->ending == END_0) 692 { 693 LINEDEF *pnext = NULL; 694 695 previous_line->next = NULL; 696 while (current_line) 697 { 698 pnext = current_line->next; 699 EDIT_InvalidateUniscribeData_linedef(current_line); 700 heap_free(current_line); 701 current_line = pnext; 702 es->line_count--; 703 } 704 } 705 else if (delta != 0) 706 { 707 while (current_line) 708 { 709 current_line->index += delta; 710 current_line = current_line->next; 711 } 712 } 713 714 /* Calculate rest of modification rectangle */ 715 if (hrgn) 716 { 717 HRGN tmphrgn; 718 /* 719 * We calculate two rectangles. One for the first line which may have 720 * an indent with respect to the format rect. The other is a format-width 721 * rectangle that spans the rest of the lines that changed or moved. 722 */ 723 rc.top = es->format_rect.top + nstart_line * es->line_height - 724 (es->y_offset * es->line_height); /* Adjust for vertical scrollbar */ 725 rc.bottom = rc.top + es->line_height; 726 if ((es->style & ES_CENTER) || (es->style & ES_RIGHT)) 727 rc.left = es->format_rect.left; 728 else 729 #ifdef __REACTOS__ /* CORE-11475 */ 730 rc.left = (short)LOWORD(EDIT_EM_PosFromChar(es, nstart_index, FALSE)); 731 #else 732 rc.left = LOWORD(EDIT_EM_PosFromChar(es, nstart_index, FALSE)); 733 #endif 734 rc.right = es->format_rect.right; 735 SetRectRgn(hrgn, rc.left, rc.top, rc.right, rc.bottom); 736 737 rc.top = rc.bottom; 738 rc.left = es->format_rect.left; 739 rc.right = es->format_rect.right; 740 /* 741 * If lines were added or removed we must re-paint the remainder of the 742 * lines since the remaining lines were either shifted up or down. 743 */ 744 if (line_count < es->line_count) /* We added lines */ 745 rc.bottom = es->line_count * es->line_height; 746 else if (line_count > es->line_count) /* We removed lines */ 747 rc.bottom = line_count * es->line_height; 748 else 749 rc.bottom = line_index * es->line_height; 750 rc.bottom += es->format_rect.top; 751 rc.bottom -= (es->y_offset * es->line_height); /* Adjust for vertical scrollbar */ 752 tmphrgn = CreateRectRgn(rc.left, rc.top, rc.right, rc.bottom); 753 CombineRgn(hrgn, hrgn, tmphrgn, RGN_OR); 754 DeleteObject(tmphrgn); 755 } 756 } 757 758 /********************************************************************* 759 * 760 * EDIT_CalcLineWidth_SL 761 * 762 */ 763 static void EDIT_CalcLineWidth_SL(EDITSTATE *es) 764 { 765 EDIT_UpdateUniscribeData(es, NULL, 0); 766 if (es->ssa) 767 { 768 const SIZE *size; 769 size = ScriptString_pSize(es->ssa); 770 es->text_width = size->cx; 771 } 772 else 773 es->text_width = 0; 774 } 775 776 /********************************************************************* 777 * 778 * EDIT_CharFromPos 779 * 780 * Beware: This is not the function called on EM_CHARFROMPOS 781 * The position _can_ be outside the formatting / client 782 * rectangle 783 * The return value is only the character index 784 * 785 */ 786 static INT EDIT_CharFromPos(EDITSTATE *es, INT x, INT y, LPBOOL after_wrap) 787 { 788 INT index; 789 790 if (es->style & ES_MULTILINE) { 791 int trailing; 792 INT line = (y - es->format_rect.top) / es->line_height + es->y_offset; 793 INT line_index = 0; 794 LINEDEF *line_def = es->first_line_def; 795 EDIT_UpdateUniscribeData(es, NULL, line); 796 while ((line > 0) && line_def->next) { 797 line_index += line_def->length; 798 line_def = line_def->next; 799 line--; 800 } 801 802 x += es->x_offset - es->format_rect.left; 803 if (es->style & ES_RIGHT) 804 x -= (es->format_rect.right - es->format_rect.left) - line_def->width; 805 else if (es->style & ES_CENTER) 806 x -= ((es->format_rect.right - es->format_rect.left) - line_def->width) / 2; 807 if (x >= line_def->width) { 808 if (after_wrap) 809 *after_wrap = (line_def->ending == END_WRAP); 810 return line_index + line_def->net_length; 811 } 812 if (x <= 0 || !line_def->ssa) { 813 if (after_wrap) 814 *after_wrap = FALSE; 815 return line_index; 816 } 817 818 ScriptStringXtoCP(line_def->ssa, x , &index, &trailing); 819 if (trailing) index++; 820 index += line_index; 821 if (after_wrap) 822 *after_wrap = ((index == line_index + line_def->net_length) && 823 (line_def->ending == END_WRAP)); 824 } else { 825 INT xoff = 0; 826 INT trailing; 827 if (after_wrap) 828 *after_wrap = FALSE; 829 x -= es->format_rect.left; 830 if (!x) 831 return es->x_offset; 832 833 if (!es->x_offset) 834 { 835 INT indent = (es->format_rect.right - es->format_rect.left) - es->text_width; 836 if (es->style & ES_RIGHT) 837 x -= indent; 838 else if (es->style & ES_CENTER) 839 x -= indent / 2; 840 } 841 842 EDIT_UpdateUniscribeData(es, NULL, 0); 843 if (es->x_offset) 844 { 845 if (es->ssa) 846 { 847 if (es->x_offset>= get_text_length(es)) 848 { 849 const SIZE *size; 850 size = ScriptString_pSize(es->ssa); 851 xoff = size->cx; 852 } 853 ScriptStringCPtoX(es->ssa, es->x_offset, FALSE, &xoff); 854 } 855 else 856 xoff = 0; 857 } 858 if (x < 0) 859 { 860 if (x + xoff > 0 || !es->ssa) 861 { 862 ScriptStringXtoCP(es->ssa, x+xoff, &index, &trailing); 863 if (trailing) index++; 864 } 865 else 866 index = 0; 867 } 868 else 869 { 870 if (x) 871 { 872 const SIZE *size = NULL; 873 if (es->ssa) 874 size = ScriptString_pSize(es->ssa); 875 if (!size) 876 index = 0; 877 else if (x > size->cx) 878 index = get_text_length(es); 879 else if (es->ssa) 880 { 881 ScriptStringXtoCP(es->ssa, x+xoff, &index, &trailing); 882 if (trailing) index++; 883 } 884 else 885 index = 0; 886 } 887 else 888 index = es->x_offset; 889 } 890 } 891 return index; 892 } 893 894 895 /********************************************************************* 896 * 897 * EDIT_ConfinePoint 898 * 899 * adjusts the point to be within the formatting rectangle 900 * (so CharFromPos returns the nearest _visible_ character) 901 * 902 */ 903 static void EDIT_ConfinePoint(const EDITSTATE *es, LPINT x, LPINT y) 904 { 905 *x = min(max(*x, es->format_rect.left), es->format_rect.right - 1); 906 *y = min(max(*y, es->format_rect.top), es->format_rect.bottom - 1); 907 } 908 909 910 /********************************************************************* 911 * 912 * EM_LINEFROMCHAR 913 * 914 */ 915 static INT EDIT_EM_LineFromChar(EDITSTATE *es, INT index) 916 { 917 INT line; 918 LINEDEF *line_def; 919 920 if (!(es->style & ES_MULTILINE)) 921 return 0; 922 if (index > (INT)get_text_length(es)) 923 return es->line_count - 1; 924 if (index == -1) 925 index = min(es->selection_start, es->selection_end); 926 927 line = 0; 928 line_def = es->first_line_def; 929 index -= line_def->length; 930 while ((index >= 0) && line_def->next) { 931 line++; 932 line_def = line_def->next; 933 index -= line_def->length; 934 } 935 return line; 936 } 937 938 939 /********************************************************************* 940 * 941 * EM_LINEINDEX 942 * 943 */ 944 static INT EDIT_EM_LineIndex(const EDITSTATE *es, INT line) 945 { 946 INT line_index; 947 const LINEDEF *line_def; 948 949 if (!(es->style & ES_MULTILINE)) 950 return 0; 951 if (line >= es->line_count) 952 return -1; 953 954 line_index = 0; 955 line_def = es->first_line_def; 956 if (line == -1) { 957 INT index = es->selection_end - line_def->length; 958 while ((index >= 0) && line_def->next) { 959 line_index += line_def->length; 960 line_def = line_def->next; 961 index -= line_def->length; 962 } 963 } else { 964 while (line > 0) { 965 line_index += line_def->length; 966 line_def = line_def->next; 967 line--; 968 } 969 } 970 return line_index; 971 } 972 973 974 /********************************************************************* 975 * 976 * EM_LINELENGTH 977 * 978 */ 979 static INT EDIT_EM_LineLength(EDITSTATE *es, INT index) 980 { 981 LINEDEF *line_def; 982 983 if (!(es->style & ES_MULTILINE)) 984 return get_text_length(es); 985 986 if (index == -1) { 987 /* get the number of remaining non-selected chars of selected lines */ 988 INT32 l; /* line number */ 989 INT32 li; /* index of first char in line */ 990 INT32 count; 991 l = EDIT_EM_LineFromChar(es, es->selection_start); 992 /* # chars before start of selection area */ 993 count = es->selection_start - EDIT_EM_LineIndex(es, l); 994 l = EDIT_EM_LineFromChar(es, es->selection_end); 995 /* # chars after end of selection */ 996 li = EDIT_EM_LineIndex(es, l); 997 count += li + EDIT_EM_LineLength(es, li) - es->selection_end; 998 return count; 999 } 1000 line_def = es->first_line_def; 1001 index -= line_def->length; 1002 while ((index >= 0) && line_def->next) { 1003 line_def = line_def->next; 1004 index -= line_def->length; 1005 } 1006 return line_def->net_length; 1007 } 1008 1009 1010 /********************************************************************* 1011 * 1012 * EM_POSFROMCHAR 1013 * 1014 */ 1015 static LRESULT EDIT_EM_PosFromChar(EDITSTATE *es, INT index, BOOL after_wrap) 1016 { 1017 INT len = get_text_length(es); 1018 INT l; 1019 INT li; 1020 INT x = 0; 1021 INT y = 0; 1022 INT w; 1023 INT lw; 1024 LINEDEF *line_def; 1025 1026 index = min(index, len); 1027 if (es->style & ES_MULTILINE) { 1028 l = EDIT_EM_LineFromChar(es, index); 1029 EDIT_UpdateUniscribeData(es, NULL, l); 1030 1031 y = (l - es->y_offset) * es->line_height; 1032 li = EDIT_EM_LineIndex(es, l); 1033 if (after_wrap && (li == index) && l) { 1034 INT l2 = l - 1; 1035 line_def = es->first_line_def; 1036 while (l2) { 1037 line_def = line_def->next; 1038 l2--; 1039 } 1040 if (line_def->ending == END_WRAP) { 1041 l--; 1042 y -= es->line_height; 1043 li = EDIT_EM_LineIndex(es, l); 1044 } 1045 } 1046 1047 line_def = es->first_line_def; 1048 while (line_def->index != li) 1049 line_def = line_def->next; 1050 1051 lw = line_def->width; 1052 w = es->format_rect.right - es->format_rect.left; 1053 if (line_def->ssa) 1054 { 1055 ScriptStringCPtoX(line_def->ssa, (index - 1) - li, TRUE, &x); 1056 x -= es->x_offset; 1057 } 1058 else 1059 #ifdef __REACTOS__ /* CORE-15780 */ 1060 x = (lw > 0 ? es->x_offset : x - es->x_offset); 1061 #else 1062 x = es->x_offset; 1063 #endif 1064 1065 if (es->style & ES_RIGHT) 1066 x = w - (lw - x); 1067 else if (es->style & ES_CENTER) 1068 x += (w - lw) / 2; 1069 } else { 1070 INT xoff = 0; 1071 INT xi = 0; 1072 EDIT_UpdateUniscribeData(es, NULL, 0); 1073 if (es->x_offset) 1074 { 1075 if (es->ssa) 1076 { 1077 if (es->x_offset >= get_text_length(es)) 1078 { 1079 int leftover = es->x_offset - get_text_length(es); 1080 if (es->ssa) 1081 { 1082 const SIZE *size; 1083 size = ScriptString_pSize(es->ssa); 1084 xoff = size->cx; 1085 } 1086 else 1087 xoff = 0; 1088 xoff += es->char_width * leftover; 1089 } 1090 else 1091 ScriptStringCPtoX(es->ssa, es->x_offset, FALSE, &xoff); 1092 } 1093 else 1094 xoff = 0; 1095 } 1096 if (index) 1097 { 1098 if (index >= get_text_length(es)) 1099 { 1100 if (es->ssa) 1101 { 1102 const SIZE *size; 1103 size = ScriptString_pSize(es->ssa); 1104 xi = size->cx; 1105 } 1106 else 1107 xi = 0; 1108 } 1109 else if (es->ssa) 1110 ScriptStringCPtoX(es->ssa, index, FALSE, &xi); 1111 else 1112 xi = 0; 1113 } 1114 x = xi - xoff; 1115 1116 if (index >= es->x_offset) { 1117 if (!es->x_offset && (es->style & (ES_RIGHT | ES_CENTER))) 1118 { 1119 w = es->format_rect.right - es->format_rect.left; 1120 if (w > es->text_width) 1121 { 1122 if (es->style & ES_RIGHT) 1123 x += w - es->text_width; 1124 else if (es->style & ES_CENTER) 1125 x += (w - es->text_width) / 2; 1126 } 1127 } 1128 } 1129 y = 0; 1130 } 1131 x += es->format_rect.left; 1132 y += es->format_rect.top; 1133 return MAKELONG((INT16)x, (INT16)y); 1134 } 1135 1136 1137 /********************************************************************* 1138 * 1139 * EDIT_GetLineRect 1140 * 1141 * Calculates the bounding rectangle for a line from a starting 1142 * column to an ending column. 1143 * 1144 */ 1145 static void EDIT_GetLineRect(EDITSTATE *es, INT line, INT scol, INT ecol, LPRECT rc) 1146 { 1147 SCRIPT_STRING_ANALYSIS ssa; 1148 INT line_index = 0; 1149 INT pt1, pt2, pt3; 1150 1151 if (es->style & ES_MULTILINE) 1152 { 1153 const LINEDEF *line_def = NULL; 1154 rc->top = es->format_rect.top + (line - es->y_offset) * es->line_height; 1155 if (line >= es->line_count) 1156 return; 1157 1158 line_def = es->first_line_def; 1159 if (line == -1) { 1160 INT index = es->selection_end - line_def->length; 1161 while ((index >= 0) && line_def->next) { 1162 line_index += line_def->length; 1163 line_def = line_def->next; 1164 index -= line_def->length; 1165 } 1166 } else { 1167 while (line > 0) { 1168 line_index += line_def->length; 1169 line_def = line_def->next; 1170 line--; 1171 } 1172 } 1173 ssa = line_def->ssa; 1174 } 1175 else 1176 { 1177 line_index = 0; 1178 rc->top = es->format_rect.top; 1179 ssa = es->ssa; 1180 } 1181 1182 rc->bottom = rc->top + es->line_height; 1183 pt1 = (scol == 0) ? es->format_rect.left : (short)LOWORD(EDIT_EM_PosFromChar(es, line_index + scol, TRUE)); 1184 pt2 = (ecol == -1) ? es->format_rect.right : (short)LOWORD(EDIT_EM_PosFromChar(es, line_index + ecol, TRUE)); 1185 if (ssa) 1186 { 1187 ScriptStringCPtoX(ssa, scol, FALSE, &pt3); 1188 pt3+=es->format_rect.left; 1189 } 1190 else pt3 = pt1; 1191 rc->right = max(max(pt1 , pt2),pt3); 1192 rc->left = min(min(pt1, pt2),pt3); 1193 } 1194 1195 1196 static inline void text_buffer_changed(EDITSTATE *es) 1197 { 1198 es->text_length = (UINT)-1; 1199 1200 heap_free( es->logAttr ); 1201 es->logAttr = NULL; 1202 EDIT_InvalidateUniscribeData(es); 1203 } 1204 1205 /********************************************************************* 1206 * EDIT_LockBuffer 1207 * 1208 */ 1209 static void EDIT_LockBuffer(EDITSTATE *es) 1210 { 1211 if (!es->text) 1212 { 1213 if (!es->hloc32W) 1214 return; 1215 1216 es->text = LocalLock(es->hloc32W); 1217 } 1218 1219 es->lock_count++; 1220 } 1221 1222 1223 /********************************************************************* 1224 * 1225 * EDIT_UnlockBuffer 1226 * 1227 */ 1228 static void EDIT_UnlockBuffer(EDITSTATE *es, BOOL force) 1229 { 1230 /* Edit window might be already destroyed */ 1231 if (!IsWindow(es->hwndSelf)) 1232 { 1233 WARN("edit hwnd %p already destroyed\n", es->hwndSelf); 1234 return; 1235 } 1236 1237 if (!es->lock_count) 1238 { 1239 ERR("lock_count == 0 ... please report\n"); 1240 return; 1241 } 1242 1243 if (!es->text) 1244 { 1245 ERR("es->text == 0 ... please report\n"); 1246 return; 1247 } 1248 1249 if (force || (es->lock_count == 1)) 1250 { 1251 if (es->hloc32W) 1252 { 1253 LocalUnlock(es->hloc32W); 1254 es->text = NULL; 1255 } 1256 else 1257 { 1258 ERR("no buffer ... please report\n"); 1259 return; 1260 } 1261 1262 } 1263 1264 es->lock_count--; 1265 } 1266 1267 1268 /********************************************************************* 1269 * 1270 * EDIT_MakeFit 1271 * 1272 * Try to fit size + 1 characters in the buffer. 1273 */ 1274 static BOOL EDIT_MakeFit(EDITSTATE *es, UINT size) 1275 { 1276 HLOCAL hNew32W; 1277 1278 if (size <= es->buffer_size) 1279 return TRUE; 1280 1281 TRACE("trying to ReAlloc to %d+1 characters\n", size); 1282 1283 /* Force edit to unlock its buffer. es->text now NULL */ 1284 EDIT_UnlockBuffer(es, TRUE); 1285 1286 if (es->hloc32W) { 1287 UINT alloc_size = ROUND_TO_GROW((size + 1) * sizeof(WCHAR)); 1288 if ((hNew32W = LocalReAlloc(es->hloc32W, alloc_size, LMEM_MOVEABLE | LMEM_ZEROINIT))) { 1289 TRACE("Old 32 bit handle %p, new handle %p\n", es->hloc32W, hNew32W); 1290 es->hloc32W = hNew32W; 1291 es->buffer_size = LocalSize(hNew32W)/sizeof(WCHAR) - 1; 1292 } 1293 } 1294 1295 EDIT_LockBuffer(es); 1296 1297 if (es->buffer_size < size) { 1298 WARN("FAILED ! We now have %d+1\n", es->buffer_size); 1299 notify_parent(es, EN_ERRSPACE); 1300 return FALSE; 1301 } else { 1302 TRACE("We now have %d+1\n", es->buffer_size); 1303 return TRUE; 1304 } 1305 } 1306 1307 1308 /********************************************************************* 1309 * 1310 * EDIT_MakeUndoFit 1311 * 1312 * Try to fit size + 1 bytes in the undo buffer. 1313 * 1314 */ 1315 static BOOL EDIT_MakeUndoFit(EDITSTATE *es, UINT size) 1316 { 1317 UINT alloc_size; 1318 1319 if (size <= es->undo_buffer_size) 1320 return TRUE; 1321 1322 TRACE("trying to ReAlloc to %d+1\n", size); 1323 1324 alloc_size = ROUND_TO_GROW((size + 1) * sizeof(WCHAR)); 1325 if ((es->undo_text = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, es->undo_text, alloc_size))) { 1326 es->undo_buffer_size = alloc_size/sizeof(WCHAR) - 1; 1327 return TRUE; 1328 } 1329 else 1330 { 1331 WARN("FAILED ! We now have %d+1\n", es->undo_buffer_size); 1332 return FALSE; 1333 } 1334 } 1335 1336 1337 /********************************************************************* 1338 * 1339 * EDIT_UpdateTextRegion 1340 * 1341 */ 1342 static void EDIT_UpdateTextRegion(EDITSTATE *es, HRGN hrgn, BOOL bErase) 1343 { 1344 if (es->flags & EF_UPDATE) { 1345 es->flags &= ~EF_UPDATE; 1346 if (!notify_parent(es, EN_UPDATE)) return; 1347 } 1348 InvalidateRgn(es->hwndSelf, hrgn, bErase); 1349 } 1350 1351 1352 /********************************************************************* 1353 * 1354 * EDIT_UpdateText 1355 * 1356 */ 1357 static void EDIT_UpdateText(EDITSTATE *es, const RECT *rc, BOOL bErase) 1358 { 1359 if (es->flags & EF_UPDATE) { 1360 es->flags &= ~EF_UPDATE; 1361 if (!notify_parent(es, EN_UPDATE)) return; 1362 } 1363 InvalidateRect(es->hwndSelf, rc, bErase); 1364 } 1365 1366 /********************************************************************* 1367 * 1368 * EDIT_SL_InvalidateText 1369 * 1370 * Called from EDIT_InvalidateText(). 1371 * Does the job for single-line controls only. 1372 * 1373 */ 1374 static void EDIT_SL_InvalidateText(EDITSTATE *es, INT start, INT end) 1375 { 1376 RECT line_rect; 1377 RECT rc; 1378 1379 EDIT_GetLineRect(es, 0, start, end, &line_rect); 1380 if (IntersectRect(&rc, &line_rect, &es->format_rect)) 1381 EDIT_UpdateText(es, &rc, TRUE); 1382 } 1383 1384 /********************************************************************* 1385 * 1386 * EDIT_ML_InvalidateText 1387 * 1388 * Called from EDIT_InvalidateText(). 1389 * Does the job for multi-line controls only. 1390 * 1391 */ 1392 static void EDIT_ML_InvalidateText(EDITSTATE *es, INT start, INT end) 1393 { 1394 INT vlc = get_vertical_line_count(es); 1395 INT sl = EDIT_EM_LineFromChar(es, start); 1396 INT el = EDIT_EM_LineFromChar(es, end); 1397 INT sc; 1398 INT ec; 1399 RECT rc1; 1400 RECT rcWnd; 1401 RECT rcLine; 1402 RECT rcUpdate; 1403 INT l; 1404 1405 if ((el < es->y_offset) || (sl > es->y_offset + vlc)) 1406 return; 1407 1408 sc = start - EDIT_EM_LineIndex(es, sl); 1409 ec = end - EDIT_EM_LineIndex(es, el); 1410 if (sl < es->y_offset) { 1411 sl = es->y_offset; 1412 sc = 0; 1413 } 1414 if (el > es->y_offset + vlc) { 1415 el = es->y_offset + vlc; 1416 ec = EDIT_EM_LineLength(es, EDIT_EM_LineIndex(es, el)); 1417 } 1418 GetClientRect(es->hwndSelf, &rc1); 1419 IntersectRect(&rcWnd, &rc1, &es->format_rect); 1420 if (sl == el) { 1421 EDIT_GetLineRect(es, sl, sc, ec, &rcLine); 1422 if (IntersectRect(&rcUpdate, &rcWnd, &rcLine)) 1423 EDIT_UpdateText(es, &rcUpdate, TRUE); 1424 } else { 1425 EDIT_GetLineRect(es, sl, sc, 1426 EDIT_EM_LineLength(es, 1427 EDIT_EM_LineIndex(es, sl)), 1428 &rcLine); 1429 if (IntersectRect(&rcUpdate, &rcWnd, &rcLine)) 1430 EDIT_UpdateText(es, &rcUpdate, TRUE); 1431 for (l = sl + 1 ; l < el ; l++) { 1432 EDIT_GetLineRect(es, l, 0, 1433 EDIT_EM_LineLength(es, 1434 EDIT_EM_LineIndex(es, l)), 1435 &rcLine); 1436 if (IntersectRect(&rcUpdate, &rcWnd, &rcLine)) 1437 EDIT_UpdateText(es, &rcUpdate, TRUE); 1438 } 1439 EDIT_GetLineRect(es, el, 0, ec, &rcLine); 1440 if (IntersectRect(&rcUpdate, &rcWnd, &rcLine)) 1441 EDIT_UpdateText(es, &rcUpdate, TRUE); 1442 } 1443 } 1444 1445 1446 /********************************************************************* 1447 * 1448 * EDIT_InvalidateText 1449 * 1450 * Invalidate the text from offset start up to, but not including, 1451 * offset end. Useful for (re)painting the selection. 1452 * Regions outside the linewidth are not invalidated. 1453 * end == -1 means end == TextLength. 1454 * start and end need not be ordered. 1455 * 1456 */ 1457 static void EDIT_InvalidateText(EDITSTATE *es, INT start, INT end) 1458 { 1459 if (end == start) 1460 return; 1461 1462 if (end == -1) 1463 end = get_text_length(es); 1464 1465 if (end < start) { 1466 INT tmp = start; 1467 start = end; 1468 end = tmp; 1469 } 1470 1471 if (es->style & ES_MULTILINE) 1472 EDIT_ML_InvalidateText(es, start, end); 1473 else 1474 EDIT_SL_InvalidateText(es, start, end); 1475 } 1476 1477 1478 /********************************************************************* 1479 * 1480 * EDIT_EM_SetSel 1481 * 1482 * note: unlike the specs say: the order of start and end 1483 * _is_ preserved in Windows. (i.e. start can be > end) 1484 * In other words: this handler is OK 1485 * 1486 */ 1487 static BOOL EDIT_EM_SetSel(EDITSTATE *es, UINT start, UINT end, BOOL after_wrap) 1488 { 1489 UINT old_start = es->selection_start; 1490 UINT old_end = es->selection_end; 1491 UINT len = get_text_length(es); 1492 1493 if (start == old_start && end == old_end) 1494 return FALSE; 1495 1496 if (start == (UINT)-1) { 1497 start = es->selection_end; 1498 end = es->selection_end; 1499 } else { 1500 start = min(start, len); 1501 end = min(end, len); 1502 } 1503 es->selection_start = start; 1504 es->selection_end = end; 1505 if (after_wrap) 1506 es->flags |= EF_AFTER_WRAP; 1507 else 1508 es->flags &= ~EF_AFTER_WRAP; 1509 /* Compute the necessary invalidation region. */ 1510 /* Note that we don't need to invalidate regions which have 1511 * "never" been selected, or those which are "still" selected. 1512 * In fact, every time we hit a selection boundary, we can 1513 * *toggle* whether we need to invalidate. Thus we can optimize by 1514 * *sorting* the interval endpoints. Let's assume that we sort them 1515 * in this order: 1516 * start <= end <= old_start <= old_end 1517 * Knuth 5.3.1 (p 183) assures us that this can be done optimally 1518 * in 5 comparisons; i.e. it is impossible to do better than the 1519 * following: */ 1520 ORDER_UINT(end, old_end); 1521 ORDER_UINT(start, old_start); 1522 ORDER_UINT(old_start, old_end); 1523 ORDER_UINT(start, end); 1524 /* Note that at this point 'end' and 'old_start' are not in order, but 1525 * start is definitely the min. and old_end is definitely the max. */ 1526 if (end != old_start) 1527 { 1528 /* 1529 * One can also do 1530 * ORDER_UINT32(end, old_start); 1531 * EDIT_InvalidateText(es, start, end); 1532 * EDIT_InvalidateText(es, old_start, old_end); 1533 * in place of the following if statement. 1534 * (That would complete the optimal five-comparison four-element sort.) 1535 */ 1536 if (old_start > end ) 1537 { 1538 EDIT_InvalidateText(es, start, end); 1539 EDIT_InvalidateText(es, old_start, old_end); 1540 } 1541 else 1542 { 1543 EDIT_InvalidateText(es, start, old_start); 1544 EDIT_InvalidateText(es, end, old_end); 1545 } 1546 } 1547 else EDIT_InvalidateText(es, start, old_end); 1548 1549 return TRUE; 1550 } 1551 1552 1553 /********************************************************************* 1554 * 1555 * EDIT_UpdateScrollInfo 1556 * 1557 */ 1558 static void EDIT_UpdateScrollInfo(EDITSTATE *es) 1559 { 1560 if ((es->style & WS_VSCROLL) && !(es->flags & EF_VSCROLL_TRACK)) 1561 { 1562 SCROLLINFO si; 1563 si.cbSize = sizeof(SCROLLINFO); 1564 si.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_DISABLENOSCROLL; 1565 si.nMin = 0; 1566 si.nMax = es->line_count - 1; 1567 si.nPage = (es->format_rect.bottom - es->format_rect.top) / es->line_height; 1568 si.nPos = es->y_offset; 1569 TRACE("SB_VERT, nMin=%d, nMax=%d, nPage=%d, nPos=%d\n", 1570 si.nMin, si.nMax, si.nPage, si.nPos); 1571 SetScrollInfo(es->hwndSelf, SB_VERT, &si, TRUE); 1572 } 1573 1574 if ((es->style & WS_HSCROLL) && !(es->flags & EF_HSCROLL_TRACK)) 1575 { 1576 SCROLLINFO si; 1577 si.cbSize = sizeof(SCROLLINFO); 1578 si.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_DISABLENOSCROLL; 1579 si.nMin = 0; 1580 si.nMax = es->text_width - 1; 1581 si.nPage = es->format_rect.right - es->format_rect.left; 1582 si.nPos = es->x_offset; 1583 TRACE("SB_HORZ, nMin=%d, nMax=%d, nPage=%d, nPos=%d\n", 1584 si.nMin, si.nMax, si.nPage, si.nPos); 1585 SetScrollInfo(es->hwndSelf, SB_HORZ, &si, TRUE); 1586 } 1587 } 1588 1589 1590 /********************************************************************* 1591 * 1592 * EDIT_EM_LineScroll_internal 1593 * 1594 * Version of EDIT_EM_LineScroll for internal use. 1595 * It doesn't refuse if ES_MULTILINE is set and assumes that 1596 * dx is in pixels, dy - in lines. 1597 * 1598 */ 1599 static BOOL EDIT_EM_LineScroll_internal(EDITSTATE *es, INT dx, INT dy) 1600 { 1601 INT nyoff; 1602 INT x_offset_in_pixels; 1603 INT lines_per_page = (es->format_rect.bottom - es->format_rect.top) / 1604 es->line_height; 1605 1606 if (es->style & ES_MULTILINE) 1607 { 1608 x_offset_in_pixels = es->x_offset; 1609 } 1610 else 1611 { 1612 dy = 0; 1613 x_offset_in_pixels = (short)LOWORD(EDIT_EM_PosFromChar(es, es->x_offset, FALSE)); 1614 } 1615 1616 if (-dx > x_offset_in_pixels) 1617 dx = -x_offset_in_pixels; 1618 if (dx > es->text_width - x_offset_in_pixels) 1619 dx = es->text_width - x_offset_in_pixels; 1620 nyoff = max(0, es->y_offset + dy); 1621 if (nyoff >= es->line_count - lines_per_page) 1622 nyoff = max(0, es->line_count - lines_per_page); 1623 dy = (es->y_offset - nyoff) * es->line_height; 1624 if (dx || dy) { 1625 RECT rc1; 1626 RECT rc; 1627 1628 es->y_offset = nyoff; 1629 if(es->style & ES_MULTILINE) 1630 es->x_offset += dx; 1631 else 1632 es->x_offset += dx / es->char_width; 1633 1634 GetClientRect(es->hwndSelf, &rc1); 1635 IntersectRect(&rc, &rc1, &es->format_rect); 1636 ScrollWindowEx(es->hwndSelf, -dx, dy, 1637 NULL, &rc, NULL, NULL, SW_INVALIDATE); 1638 /* force scroll info update */ 1639 EDIT_UpdateScrollInfo(es); 1640 } 1641 if (dx && !(es->flags & EF_HSCROLL_TRACK)) 1642 notify_parent(es, EN_HSCROLL); 1643 if (dy && !(es->flags & EF_VSCROLL_TRACK)) 1644 notify_parent(es, EN_VSCROLL); 1645 return TRUE; 1646 } 1647 1648 /********************************************************************* 1649 * 1650 * EM_LINESCROLL 1651 * 1652 * NOTE: dx is in average character widths, dy - in lines; 1653 * 1654 */ 1655 static BOOL EDIT_EM_LineScroll(EDITSTATE *es, INT dx, INT dy) 1656 { 1657 if (!(es->style & ES_MULTILINE)) 1658 return FALSE; 1659 1660 dx *= es->char_width; 1661 return EDIT_EM_LineScroll_internal(es, dx, dy); 1662 } 1663 1664 1665 /********************************************************************* 1666 * 1667 * EM_SCROLL 1668 * 1669 */ 1670 static LRESULT EDIT_EM_Scroll(EDITSTATE *es, INT action) 1671 { 1672 INT dy; 1673 1674 if (!(es->style & ES_MULTILINE)) 1675 return (LRESULT)FALSE; 1676 1677 dy = 0; 1678 1679 switch (action) { 1680 case SB_LINEUP: 1681 if (es->y_offset) 1682 dy = -1; 1683 break; 1684 case SB_LINEDOWN: 1685 if (es->y_offset < es->line_count - 1) 1686 dy = 1; 1687 break; 1688 case SB_PAGEUP: 1689 if (es->y_offset) 1690 dy = -(es->format_rect.bottom - es->format_rect.top) / es->line_height; 1691 break; 1692 case SB_PAGEDOWN: 1693 if (es->y_offset < es->line_count - 1) 1694 dy = (es->format_rect.bottom - es->format_rect.top) / es->line_height; 1695 break; 1696 default: 1697 return (LRESULT)FALSE; 1698 } 1699 if (dy) { 1700 INT vlc = get_vertical_line_count(es); 1701 /* check if we are going to move too far */ 1702 if(es->y_offset + dy > es->line_count - vlc) 1703 dy = max(es->line_count - vlc, 0) - es->y_offset; 1704 1705 /* Notification is done in EDIT_EM_LineScroll */ 1706 if(dy) { 1707 EDIT_EM_LineScroll(es, 0, dy); 1708 return MAKELONG(dy, TRUE); 1709 } 1710 1711 } 1712 return (LRESULT)FALSE; 1713 } 1714 1715 1716 #ifdef __REACTOS__ 1717 static void EDIT_ImmSetCompositionWindow(EDITSTATE *es, POINT pt) 1718 { 1719 COMPOSITIONFORM CompForm; 1720 HIMC hIMC = ImmGetContext(es->hwndSelf); 1721 if (!hIMC) 1722 { 1723 ERR("!hIMC\n"); 1724 return; 1725 } 1726 1727 CompForm.ptCurrentPos = pt; 1728 if (es->style & ES_MULTILINE) 1729 { 1730 CompForm.dwStyle = CFS_RECT; 1731 CompForm.rcArea = es->format_rect; 1732 } 1733 else 1734 { 1735 CompForm.dwStyle = CFS_POINT; 1736 SetRectEmpty(&CompForm.rcArea); 1737 } 1738 1739 ImmSetCompositionWindow(hIMC, &CompForm); 1740 ImmReleaseContext(es->hwndSelf, hIMC); 1741 } 1742 #endif 1743 /********************************************************************* 1744 * 1745 * EDIT_SetCaretPos 1746 * 1747 */ 1748 static void EDIT_SetCaretPos(EDITSTATE *es, INT pos, 1749 BOOL after_wrap) 1750 { 1751 LRESULT res = EDIT_EM_PosFromChar(es, pos, after_wrap); 1752 #ifdef __REACTOS__ 1753 HKL hKL = GetKeyboardLayout(0); 1754 POINT pt = { (short)LOWORD(res), (short)HIWORD(res) }; 1755 SetCaretPos(pt.x, pt.y); 1756 1757 if (!ImmIsIME(hKL)) 1758 return; 1759 1760 EDIT_ImmSetCompositionWindow(es, pt); 1761 #else 1762 TRACE("%d - %dx%d\n", pos, (short)LOWORD(res), (short)HIWORD(res)); 1763 SetCaretPos((short)LOWORD(res), (short)HIWORD(res)); 1764 #endif 1765 } 1766 1767 1768 /********************************************************************* 1769 * 1770 * EM_SCROLLCARET 1771 * 1772 */ 1773 static void EDIT_EM_ScrollCaret(EDITSTATE *es) 1774 { 1775 if (es->style & ES_MULTILINE) { 1776 INT l; 1777 INT vlc; 1778 INT ww; 1779 INT cw = es->char_width; 1780 INT x; 1781 INT dy = 0; 1782 INT dx = 0; 1783 1784 l = EDIT_EM_LineFromChar(es, es->selection_end); 1785 x = (short)LOWORD(EDIT_EM_PosFromChar(es, es->selection_end, es->flags & EF_AFTER_WRAP)); 1786 vlc = get_vertical_line_count(es); 1787 if (l >= es->y_offset + vlc) 1788 dy = l - vlc + 1 - es->y_offset; 1789 if (l < es->y_offset) 1790 dy = l - es->y_offset; 1791 ww = es->format_rect.right - es->format_rect.left; 1792 if (x < es->format_rect.left) 1793 dx = x - es->format_rect.left - ww / HSCROLL_FRACTION / cw * cw; 1794 if (x > es->format_rect.right) 1795 dx = x - es->format_rect.left - (HSCROLL_FRACTION - 1) * ww / HSCROLL_FRACTION / cw * cw; 1796 if (dy || dx || (es->y_offset && (es->line_count - es->y_offset < vlc))) 1797 { 1798 /* check if we are going to move too far */ 1799 if(es->x_offset + dx + ww > es->text_width) 1800 dx = es->text_width - ww - es->x_offset; 1801 if(dx || dy || (es->y_offset && (es->line_count - es->y_offset < vlc))) 1802 EDIT_EM_LineScroll_internal(es, dx, dy); 1803 } 1804 } else { 1805 INT x; 1806 INT goal; 1807 INT format_width; 1808 1809 x = (short)LOWORD(EDIT_EM_PosFromChar(es, es->selection_end, FALSE)); 1810 format_width = es->format_rect.right - es->format_rect.left; 1811 if (x < es->format_rect.left) { 1812 goal = es->format_rect.left + format_width / HSCROLL_FRACTION; 1813 do { 1814 es->x_offset--; 1815 x = (short)LOWORD(EDIT_EM_PosFromChar(es, es->selection_end, FALSE)); 1816 } while ((x < goal) && es->x_offset); 1817 /* FIXME: use ScrollWindow() somehow to improve performance */ 1818 EDIT_UpdateText(es, NULL, TRUE); 1819 } else if (x > es->format_rect.right) { 1820 INT x_last; 1821 INT len = get_text_length(es); 1822 goal = es->format_rect.right - format_width / HSCROLL_FRACTION; 1823 do { 1824 es->x_offset++; 1825 x = (short)LOWORD(EDIT_EM_PosFromChar(es, es->selection_end, FALSE)); 1826 x_last = (short)LOWORD(EDIT_EM_PosFromChar(es, len, FALSE)); 1827 } while ((x > goal) && (x_last > es->format_rect.right)); 1828 /* FIXME: use ScrollWindow() somehow to improve performance */ 1829 EDIT_UpdateText(es, NULL, TRUE); 1830 } 1831 } 1832 1833 if(es->flags & EF_FOCUSED) 1834 EDIT_SetCaretPos(es, es->selection_end, es->flags & EF_AFTER_WRAP); 1835 } 1836 1837 1838 /********************************************************************* 1839 * 1840 * EDIT_MoveBackward 1841 * 1842 */ 1843 static void EDIT_MoveBackward(EDITSTATE *es, BOOL extend) 1844 { 1845 INT e = es->selection_end; 1846 1847 if (e) { 1848 e--; 1849 if ((es->style & ES_MULTILINE) && e && 1850 (es->text[e - 1] == '\r') && (es->text[e] == '\n')) { 1851 e--; 1852 if (e && (es->text[e - 1] == '\r')) 1853 e--; 1854 } 1855 } 1856 EDIT_EM_SetSel(es, extend ? es->selection_start : e, e, FALSE); 1857 EDIT_EM_ScrollCaret(es); 1858 } 1859 1860 1861 /********************************************************************* 1862 * 1863 * EDIT_MoveDown_ML 1864 * 1865 * Only for multi line controls 1866 * Move the caret one line down, on a column with the nearest 1867 * x coordinate on the screen (might be a different column). 1868 * 1869 */ 1870 static void EDIT_MoveDown_ML(EDITSTATE *es, BOOL extend) 1871 { 1872 INT s = es->selection_start; 1873 INT e = es->selection_end; 1874 BOOL after_wrap = (es->flags & EF_AFTER_WRAP); 1875 LRESULT pos = EDIT_EM_PosFromChar(es, e, after_wrap); 1876 INT x = (short)LOWORD(pos); 1877 INT y = (short)HIWORD(pos); 1878 1879 e = EDIT_CharFromPos(es, x, y + es->line_height, &after_wrap); 1880 if (!extend) 1881 s = e; 1882 EDIT_EM_SetSel(es, s, e, after_wrap); 1883 EDIT_EM_ScrollCaret(es); 1884 } 1885 1886 1887 /********************************************************************* 1888 * 1889 * EDIT_MoveEnd 1890 * 1891 */ 1892 static void EDIT_MoveEnd(EDITSTATE *es, BOOL extend, BOOL ctrl) 1893 { 1894 BOOL after_wrap = FALSE; 1895 INT e; 1896 1897 /* Pass a high value in x to make sure of receiving the end of the line */ 1898 if (!ctrl && (es->style & ES_MULTILINE)) 1899 e = EDIT_CharFromPos(es, 0x3fffffff, 1900 HIWORD(EDIT_EM_PosFromChar(es, es->selection_end, es->flags & EF_AFTER_WRAP)), &after_wrap); 1901 else 1902 e = get_text_length(es); 1903 EDIT_EM_SetSel(es, extend ? es->selection_start : e, e, after_wrap); 1904 EDIT_EM_ScrollCaret(es); 1905 } 1906 1907 1908 /********************************************************************* 1909 * 1910 * EDIT_MoveForward 1911 * 1912 */ 1913 static void EDIT_MoveForward(EDITSTATE *es, BOOL extend) 1914 { 1915 INT e = es->selection_end; 1916 1917 if (es->text[e]) { 1918 e++; 1919 if ((es->style & ES_MULTILINE) && (es->text[e - 1] == '\r')) { 1920 if (es->text[e] == '\n') 1921 e++; 1922 else if ((es->text[e] == '\r') && (es->text[e + 1] == '\n')) 1923 e += 2; 1924 } 1925 } 1926 EDIT_EM_SetSel(es, extend ? es->selection_start : e, e, FALSE); 1927 EDIT_EM_ScrollCaret(es); 1928 } 1929 1930 1931 /********************************************************************* 1932 * 1933 * EDIT_MoveHome 1934 * 1935 * Home key: move to beginning of line. 1936 * 1937 */ 1938 static void EDIT_MoveHome(EDITSTATE *es, BOOL extend, BOOL ctrl) 1939 { 1940 INT e; 1941 1942 /* Pass the x_offset in x to make sure of receiving the first position of the line */ 1943 if (!ctrl && (es->style & ES_MULTILINE)) 1944 e = EDIT_CharFromPos(es, -es->x_offset, 1945 HIWORD(EDIT_EM_PosFromChar(es, es->selection_end, es->flags & EF_AFTER_WRAP)), NULL); 1946 else 1947 e = 0; 1948 EDIT_EM_SetSel(es, extend ? es->selection_start : e, e, FALSE); 1949 EDIT_EM_ScrollCaret(es); 1950 } 1951 1952 1953 /********************************************************************* 1954 * 1955 * EDIT_MovePageDown_ML 1956 * 1957 * Only for multi line controls 1958 * Move the caret one page down, on a column with the nearest 1959 * x coordinate on the screen (might be a different column). 1960 * 1961 */ 1962 static void EDIT_MovePageDown_ML(EDITSTATE *es, BOOL extend) 1963 { 1964 INT s = es->selection_start; 1965 INT e = es->selection_end; 1966 BOOL after_wrap = (es->flags & EF_AFTER_WRAP); 1967 LRESULT pos = EDIT_EM_PosFromChar(es, e, after_wrap); 1968 INT x = (short)LOWORD(pos); 1969 INT y = (short)HIWORD(pos); 1970 1971 e = EDIT_CharFromPos(es, x, 1972 y + (es->format_rect.bottom - es->format_rect.top), 1973 &after_wrap); 1974 if (!extend) 1975 s = e; 1976 EDIT_EM_SetSel(es, s, e, after_wrap); 1977 EDIT_EM_ScrollCaret(es); 1978 } 1979 1980 1981 /********************************************************************* 1982 * 1983 * EDIT_MovePageUp_ML 1984 * 1985 * Only for multi line controls 1986 * Move the caret one page up, on a column with the nearest 1987 * x coordinate on the screen (might be a different column). 1988 * 1989 */ 1990 static void EDIT_MovePageUp_ML(EDITSTATE *es, BOOL extend) 1991 { 1992 INT s = es->selection_start; 1993 INT e = es->selection_end; 1994 BOOL after_wrap = (es->flags & EF_AFTER_WRAP); 1995 LRESULT pos = EDIT_EM_PosFromChar(es, e, after_wrap); 1996 INT x = (short)LOWORD(pos); 1997 INT y = (short)HIWORD(pos); 1998 1999 e = EDIT_CharFromPos(es, x, 2000 y - (es->format_rect.bottom - es->format_rect.top), 2001 &after_wrap); 2002 if (!extend) 2003 s = e; 2004 EDIT_EM_SetSel(es, s, e, after_wrap); 2005 EDIT_EM_ScrollCaret(es); 2006 } 2007 2008 2009 /********************************************************************* 2010 * 2011 * EDIT_MoveUp_ML 2012 * 2013 * Only for multi line controls 2014 * Move the caret one line up, on a column with the nearest 2015 * x coordinate on the screen (might be a different column). 2016 * 2017 */ 2018 static void EDIT_MoveUp_ML(EDITSTATE *es, BOOL extend) 2019 { 2020 INT s = es->selection_start; 2021 INT e = es->selection_end; 2022 BOOL after_wrap = (es->flags & EF_AFTER_WRAP); 2023 LRESULT pos = EDIT_EM_PosFromChar(es, e, after_wrap); 2024 INT x = (short)LOWORD(pos); 2025 INT y = (short)HIWORD(pos); 2026 2027 e = EDIT_CharFromPos(es, x, y - es->line_height, &after_wrap); 2028 if (!extend) 2029 s = e; 2030 EDIT_EM_SetSel(es, s, e, after_wrap); 2031 EDIT_EM_ScrollCaret(es); 2032 } 2033 2034 2035 /********************************************************************* 2036 * 2037 * EDIT_MoveWordBackward 2038 * 2039 */ 2040 static void EDIT_MoveWordBackward(EDITSTATE *es, BOOL extend) 2041 { 2042 INT s = es->selection_start; 2043 INT e = es->selection_end; 2044 INT l; 2045 INT ll; 2046 INT li; 2047 2048 l = EDIT_EM_LineFromChar(es, e); 2049 ll = EDIT_EM_LineLength(es, e); 2050 li = EDIT_EM_LineIndex(es, l); 2051 if (e - li == 0) { 2052 if (l) { 2053 li = EDIT_EM_LineIndex(es, l - 1); 2054 e = li + EDIT_EM_LineLength(es, li); 2055 } 2056 } else { 2057 e = li + EDIT_CallWordBreakProc(es, li, e - li, ll, WB_LEFT); 2058 } 2059 if (!extend) 2060 s = e; 2061 EDIT_EM_SetSel(es, s, e, FALSE); 2062 EDIT_EM_ScrollCaret(es); 2063 } 2064 2065 2066 /********************************************************************* 2067 * 2068 * EDIT_MoveWordForward 2069 * 2070 */ 2071 static void EDIT_MoveWordForward(EDITSTATE *es, BOOL extend) 2072 { 2073 INT s = es->selection_start; 2074 INT e = es->selection_end; 2075 INT l; 2076 INT ll; 2077 INT li; 2078 2079 l = EDIT_EM_LineFromChar(es, e); 2080 ll = EDIT_EM_LineLength(es, e); 2081 li = EDIT_EM_LineIndex(es, l); 2082 if (e - li == ll) { 2083 if ((es->style & ES_MULTILINE) && (l != es->line_count - 1)) 2084 e = EDIT_EM_LineIndex(es, l + 1); 2085 } else { 2086 e = li + EDIT_CallWordBreakProc(es, 2087 li, e - li + 1, ll, WB_RIGHT); 2088 } 2089 if (!extend) 2090 s = e; 2091 EDIT_EM_SetSel(es, s, e, FALSE); 2092 EDIT_EM_ScrollCaret(es); 2093 } 2094 2095 2096 /********************************************************************* 2097 * 2098 * EDIT_PaintText 2099 * 2100 */ 2101 static INT EDIT_PaintText(EDITSTATE *es, HDC dc, INT x, INT y, INT line, INT col, INT count, BOOL rev) 2102 { 2103 COLORREF BkColor; 2104 COLORREF TextColor; 2105 LOGFONTW underline_font; 2106 HFONT hUnderline = 0; 2107 HFONT old_font = 0; 2108 INT ret; 2109 INT li; 2110 INT BkMode; 2111 SIZE size; 2112 2113 if (!count) 2114 return 0; 2115 BkMode = GetBkMode(dc); 2116 BkColor = GetBkColor(dc); 2117 TextColor = GetTextColor(dc); 2118 if (rev) { 2119 #ifdef __REACTOS__ 2120 if (TRUE) 2121 #else 2122 if (es->composition_len == 0) 2123 #endif 2124 { 2125 SetBkColor(dc, GetSysColor(COLOR_HIGHLIGHT)); 2126 SetTextColor(dc, GetSysColor(COLOR_HIGHLIGHTTEXT)); 2127 SetBkMode( dc, OPAQUE); 2128 } 2129 else 2130 { 2131 HFONT current = GetCurrentObject(dc,OBJ_FONT); 2132 GetObjectW(current,sizeof(LOGFONTW),&underline_font); 2133 underline_font.lfUnderline = TRUE; 2134 hUnderline = CreateFontIndirectW(&underline_font); 2135 old_font = SelectObject(dc,hUnderline); 2136 } 2137 } 2138 li = EDIT_EM_LineIndex(es, line); 2139 if (es->style & ES_MULTILINE) { 2140 ret = (INT)LOWORD(TabbedTextOutW(dc, x, y, es->text + li + col, count, 2141 es->tabs_count, es->tabs, es->format_rect.left - es->x_offset)); 2142 } else { 2143 TextOutW(dc, x, y, es->text + li + col, count); 2144 GetTextExtentPoint32W(dc, es->text + li + col, count, &size); 2145 ret = size.cx; 2146 } 2147 if (rev) { 2148 #ifdef __REACTOS__ 2149 if (TRUE) 2150 #else 2151 if (es->composition_len == 0) 2152 #endif 2153 { 2154 SetBkColor(dc, BkColor); 2155 SetTextColor(dc, TextColor); 2156 SetBkMode( dc, BkMode); 2157 } 2158 else 2159 { 2160 if (old_font) 2161 SelectObject(dc,old_font); 2162 if (hUnderline) 2163 DeleteObject(hUnderline); 2164 } 2165 } 2166 return ret; 2167 } 2168 2169 2170 /********************************************************************* 2171 * 2172 * EDIT_PaintLine 2173 * 2174 */ 2175 static void EDIT_PaintLine(EDITSTATE *es, HDC dc, INT line, BOOL rev) 2176 { 2177 INT s = 0; 2178 INT e = 0; 2179 INT li = 0; 2180 INT ll = 0; 2181 INT x; 2182 INT y; 2183 LRESULT pos; 2184 SCRIPT_STRING_ANALYSIS ssa; 2185 2186 if (es->style & ES_MULTILINE) { 2187 INT vlc = get_vertical_line_count(es); 2188 2189 if ((line < es->y_offset) || (line > es->y_offset + vlc) || (line >= es->line_count)) 2190 return; 2191 } else if (line) 2192 return; 2193 2194 TRACE("line=%d\n", line); 2195 2196 ssa = EDIT_UpdateUniscribeData(es, dc, line); 2197 pos = EDIT_EM_PosFromChar(es, EDIT_EM_LineIndex(es, line), FALSE); 2198 x = (short)LOWORD(pos); 2199 y = (short)HIWORD(pos); 2200 2201 if (es->style & ES_MULTILINE) 2202 { 2203 int line_idx = line; 2204 x = -es->x_offset; 2205 if (es->style & ES_RIGHT || es->style & ES_CENTER) 2206 { 2207 LINEDEF *line_def = es->first_line_def; 2208 int w, lw; 2209 2210 while (line_def && line_idx) 2211 { 2212 line_def = line_def->next; 2213 line_idx--; 2214 } 2215 w = es->format_rect.right - es->format_rect.left; 2216 lw = line_def->width; 2217 2218 if (es->style & ES_RIGHT) 2219 x = w - (lw - x); 2220 else if (es->style & ES_CENTER) 2221 x += (w - lw) / 2; 2222 } 2223 x += es->format_rect.left; 2224 } 2225 2226 if (rev) 2227 { 2228 li = EDIT_EM_LineIndex(es, line); 2229 ll = EDIT_EM_LineLength(es, li); 2230 s = min(es->selection_start, es->selection_end); 2231 e = max(es->selection_start, es->selection_end); 2232 s = min(li + ll, max(li, s)); 2233 e = min(li + ll, max(li, e)); 2234 } 2235 2236 if (ssa) 2237 ScriptStringOut(ssa, x, y, 0, &es->format_rect, s - li, e - li, FALSE); 2238 else if (rev && (s != e) && 2239 ((es->flags & EF_FOCUSED) || (es->style & ES_NOHIDESEL))) { 2240 x += EDIT_PaintText(es, dc, x, y, line, 0, s - li, FALSE); 2241 x += EDIT_PaintText(es, dc, x, y, line, s - li, e - s, TRUE); 2242 x += EDIT_PaintText(es, dc, x, y, line, e - li, li + ll - e, FALSE); 2243 } else 2244 x += EDIT_PaintText(es, dc, x, y, line, 0, ll, FALSE); 2245 } 2246 2247 2248 /********************************************************************* 2249 * 2250 * EDIT_AdjustFormatRect 2251 * 2252 * Adjusts the format rectangle for the current font and the 2253 * current client rectangle. 2254 * 2255 */ 2256 static void EDIT_AdjustFormatRect(EDITSTATE *es) 2257 { 2258 RECT ClientRect; 2259 2260 es->format_rect.right = max(es->format_rect.right, es->format_rect.left + es->char_width); 2261 if (es->style & ES_MULTILINE) 2262 { 2263 INT fw, vlc, max_x_offset, max_y_offset; 2264 2265 vlc = get_vertical_line_count(es); 2266 es->format_rect.bottom = es->format_rect.top + vlc * es->line_height; 2267 2268 /* correct es->x_offset */ 2269 fw = es->format_rect.right - es->format_rect.left; 2270 max_x_offset = es->text_width - fw; 2271 if(max_x_offset < 0) max_x_offset = 0; 2272 if(es->x_offset > max_x_offset) 2273 es->x_offset = max_x_offset; 2274 2275 /* correct es->y_offset */ 2276 max_y_offset = es->line_count - vlc; 2277 if(max_y_offset < 0) max_y_offset = 0; 2278 if(es->y_offset > max_y_offset) 2279 es->y_offset = max_y_offset; 2280 2281 /* force scroll info update */ 2282 EDIT_UpdateScrollInfo(es); 2283 } 2284 else 2285 /* Windows doesn't care to fix text placement for SL controls */ 2286 es->format_rect.bottom = es->format_rect.top + es->line_height; 2287 2288 /* Always stay within the client area */ 2289 GetClientRect(es->hwndSelf, &ClientRect); 2290 es->format_rect.bottom = min(es->format_rect.bottom, ClientRect.bottom); 2291 2292 if ((es->style & ES_MULTILINE) && !(es->style & ES_AUTOHSCROLL)) 2293 EDIT_BuildLineDefs_ML(es, 0, get_text_length(es), 0, NULL); 2294 2295 EDIT_SetCaretPos(es, es->selection_end, es->flags & EF_AFTER_WRAP); 2296 } 2297 2298 2299 /********************************************************************* 2300 * 2301 * EDIT_SetRectNP 2302 * 2303 * note: this is not (exactly) the handler called on EM_SETRECTNP 2304 * it is also used to set the rect of a single line control 2305 * 2306 */ 2307 static void EDIT_SetRectNP(EDITSTATE *es, const RECT *rc) 2308 { 2309 LONG_PTR ExStyle; 2310 INT bw, bh; 2311 ExStyle = GetWindowLongPtrW(es->hwndSelf, GWL_EXSTYLE); 2312 2313 CopyRect(&es->format_rect, rc); 2314 2315 if (ExStyle & WS_EX_CLIENTEDGE) { 2316 es->format_rect.left++; 2317 es->format_rect.right--; 2318 2319 if (es->format_rect.bottom - es->format_rect.top 2320 >= es->line_height + 2) 2321 { 2322 es->format_rect.top++; 2323 es->format_rect.bottom--; 2324 } 2325 } 2326 else if (es->style & WS_BORDER) { 2327 bw = GetSystemMetrics(SM_CXBORDER) + 1; 2328 bh = GetSystemMetrics(SM_CYBORDER) + 1; 2329 InflateRect(&es->format_rect, -bw, 0); 2330 if (es->format_rect.bottom - es->format_rect.top >= es->line_height + 2 * bh) 2331 InflateRect(&es->format_rect, 0, -bh); 2332 } 2333 2334 es->format_rect.left += es->left_margin; 2335 es->format_rect.right -= es->right_margin; 2336 EDIT_AdjustFormatRect(es); 2337 } 2338 2339 2340 /********************************************************************* 2341 * 2342 * EM_CHARFROMPOS 2343 * 2344 * returns line number (not index) in high-order word of result. 2345 * NB : Q137805 is unclear about this. POINT * pointer in lParam apply 2346 * to Richedit, not to the edit control. Original documentation is valid. 2347 * FIXME: do the specs mean to return -1 if outside client area or 2348 * if outside formatting rectangle ??? 2349 * 2350 */ 2351 static LRESULT EDIT_EM_CharFromPos(EDITSTATE *es, INT x, INT y) 2352 { 2353 POINT pt; 2354 RECT rc; 2355 INT index; 2356 2357 pt.x = x; 2358 pt.y = y; 2359 GetClientRect(es->hwndSelf, &rc); 2360 if (!PtInRect(&rc, pt)) 2361 return -1; 2362 2363 index = EDIT_CharFromPos(es, x, y, NULL); 2364 return MAKELONG(index, EDIT_EM_LineFromChar(es, index)); 2365 } 2366 2367 2368 /********************************************************************* 2369 * 2370 * EM_FMTLINES 2371 * 2372 * Enable or disable soft breaks. 2373 * 2374 * This means: insert or remove the soft linebreak character (\r\r\n). 2375 * Take care to check if the text still fits the buffer after insertion. 2376 * If not, notify with EN_ERRSPACE. 2377 * 2378 */ 2379 static BOOL EDIT_EM_FmtLines(EDITSTATE *es, BOOL add_eol) 2380 { 2381 es->flags &= ~EF_USE_SOFTBRK; 2382 if (add_eol) { 2383 es->flags |= EF_USE_SOFTBRK; 2384 FIXME("soft break enabled, not implemented\n"); 2385 } 2386 return add_eol; 2387 } 2388 2389 2390 /********************************************************************* 2391 * 2392 * EM_GETHANDLE 2393 * 2394 * Hopefully this won't fire back at us. 2395 * We always start with a fixed buffer in the local heap. 2396 * Despite of the documentation says that the local heap is used 2397 * only if DS_LOCALEDIT flag is set, NT and 2000 always allocate 2398 * buffer on the local heap. 2399 * 2400 */ 2401 static HLOCAL EDIT_EM_GetHandle(EDITSTATE *es) 2402 { 2403 if (!(es->style & ES_MULTILINE)) 2404 return 0; 2405 2406 EDIT_UnlockBuffer(es, TRUE); 2407 2408 /* The text buffer handle belongs to the app */ 2409 es->hlocapp = es->hloc32W; 2410 2411 TRACE("Returning %p, LocalSize() = %ld\n", es->hlocapp, LocalSize(es->hlocapp)); 2412 return es->hlocapp; 2413 } 2414 2415 2416 /********************************************************************* 2417 * 2418 * EM_GETLINE 2419 * 2420 */ 2421 static INT EDIT_EM_GetLine(EDITSTATE *es, INT line, LPWSTR dst) 2422 { 2423 INT line_len, dst_len; 2424 LPWSTR src; 2425 INT i; 2426 2427 if (es->style & ES_MULTILINE) 2428 { 2429 if (line >= es->line_count) 2430 return 0; 2431 } 2432 else 2433 line = 0; 2434 2435 i = EDIT_EM_LineIndex(es, line); 2436 src = es->text + i; 2437 line_len = EDIT_EM_LineLength(es, i); 2438 dst_len = *(WORD *)dst; 2439 2440 if (dst_len <= line_len) 2441 { 2442 memcpy(dst, src, dst_len * sizeof(WCHAR)); 2443 return dst_len; 2444 } 2445 else /* Append 0 if enough space */ 2446 { 2447 memcpy(dst, src, line_len * sizeof(WCHAR)); 2448 dst[line_len] = 0; 2449 return line_len; 2450 } 2451 } 2452 2453 2454 /********************************************************************* 2455 * 2456 * EM_GETSEL 2457 * 2458 */ 2459 static LRESULT EDIT_EM_GetSel(const EDITSTATE *es, PUINT start, PUINT end) 2460 { 2461 UINT s = es->selection_start; 2462 UINT e = es->selection_end; 2463 2464 ORDER_UINT(s, e); 2465 if (start) 2466 *start = s; 2467 if (end) 2468 *end = e; 2469 return MAKELONG(s, e); 2470 } 2471 2472 2473 /********************************************************************* 2474 * 2475 * EM_REPLACESEL 2476 * 2477 * FIXME: handle ES_NUMBER and ES_OEMCONVERT here 2478 * 2479 */ 2480 static void EDIT_EM_ReplaceSel(EDITSTATE *es, BOOL can_undo, const WCHAR *lpsz_replace, UINT strl, 2481 BOOL send_update, BOOL honor_limit) 2482 { 2483 UINT tl = get_text_length(es); 2484 UINT utl; 2485 UINT s; 2486 UINT e; 2487 UINT i; 2488 UINT size; 2489 LPWSTR p; 2490 HRGN hrgn = 0; 2491 LPWSTR buf = NULL; 2492 UINT bufl; 2493 2494 TRACE("%s, can_undo %d, send_update %d\n", 2495 debugstr_wn(lpsz_replace, strl), can_undo, send_update); 2496 2497 s = es->selection_start; 2498 e = es->selection_end; 2499 2500 EDIT_InvalidateUniscribeData(es); 2501 if ((s == e) && !strl) 2502 return; 2503 2504 ORDER_UINT(s, e); 2505 2506 size = tl - (e - s) + strl; 2507 if (!size) 2508 es->text_width = 0; 2509 2510 /* Issue the EN_MAXTEXT notification and continue with replacing text 2511 * so that buffer limit is honored. */ 2512 if ((honor_limit) && (size > es->buffer_limit)) 2513 { 2514 if (!notify_parent(es, EN_MAXTEXT)) return; 2515 /* Buffer limit can be smaller than the actual length of text in combobox */ 2516 if (es->buffer_limit < (tl - (e-s))) 2517 strl = 0; 2518 else 2519 strl = min(strl, es->buffer_limit - (tl - (e-s))); 2520 } 2521 2522 if (!EDIT_MakeFit(es, tl - (e - s) + strl)) 2523 return; 2524 2525 if (e != s) { 2526 /* there is something to be deleted */ 2527 TRACE("deleting stuff.\n"); 2528 bufl = e - s; 2529 buf = heap_alloc((bufl + 1) * sizeof(WCHAR)); 2530 if (!buf) return; 2531 memcpy(buf, es->text + s, bufl * sizeof(WCHAR)); 2532 buf[bufl] = 0; /* ensure 0 termination */ 2533 /* now delete */ 2534 strcpyW(es->text + s, es->text + e); 2535 text_buffer_changed(es); 2536 } 2537 if (strl) { 2538 /* there is an insertion */ 2539 tl = get_text_length(es); 2540 TRACE("inserting stuff (tl %d, strl %d, selstart %d (%s), text %s)\n", tl, strl, s, debugstr_w(es->text + s), debugstr_w(es->text)); 2541 for (p = es->text + tl ; p >= es->text + s ; p--) 2542 p[strl] = p[0]; 2543 for (i = 0 , p = es->text + s ; i < strl ; i++) 2544 p[i] = lpsz_replace[i]; 2545 if(es->style & ES_UPPERCASE) 2546 CharUpperBuffW(p, strl); 2547 else if(es->style & ES_LOWERCASE) 2548 CharLowerBuffW(p, strl); 2549 text_buffer_changed(es); 2550 } 2551 if (es->style & ES_MULTILINE) 2552 { 2553 INT st = min(es->selection_start, es->selection_end); 2554 INT vlc = get_vertical_line_count(es); 2555 2556 hrgn = CreateRectRgn(0, 0, 0, 0); 2557 EDIT_BuildLineDefs_ML(es, st, st + strl, 2558 strl - abs(es->selection_end - es->selection_start), hrgn); 2559 /* if text is too long undo all changes */ 2560 if (honor_limit && !(es->style & ES_AUTOVSCROLL) && (es->line_count > vlc)) { 2561 if (strl) 2562 strcpyW(es->text + e, es->text + e + strl); 2563 if (e != s) 2564 for (i = 0 , p = es->text ; i < e - s ; i++) 2565 p[i + s] = buf[i]; 2566 text_buffer_changed(es); 2567 EDIT_BuildLineDefs_ML(es, s, e, 2568 abs(es->selection_end - es->selection_start) - strl, hrgn); 2569 strl = 0; 2570 e = s; 2571 hrgn = CreateRectRgn(0, 0, 0, 0); 2572 if (!notify_parent(es, EN_MAXTEXT)) return; 2573 } 2574 } 2575 else { 2576 INT fw = es->format_rect.right - es->format_rect.left; 2577 EDIT_InvalidateUniscribeData(es); 2578 EDIT_CalcLineWidth_SL(es); 2579 /* remove chars that don't fit */ 2580 if (honor_limit && !(es->style & ES_AUTOHSCROLL) && (es->text_width > fw)) { 2581 while ((es->text_width > fw) && s + strl >= s) { 2582 strcpyW(es->text + s + strl - 1, es->text + s + strl); 2583 strl--; 2584 es->text_length = -1; 2585 EDIT_InvalidateUniscribeData(es); 2586 EDIT_CalcLineWidth_SL(es); 2587 } 2588 text_buffer_changed(es); 2589 if (!notify_parent(es, EN_MAXTEXT)) return; 2590 } 2591 } 2592 2593 if (e != s) { 2594 if (can_undo) { 2595 utl = strlenW(es->undo_text); 2596 if (!es->undo_insert_count && (*es->undo_text && (s == es->undo_position))) { 2597 /* undo-buffer is extended to the right */ 2598 EDIT_MakeUndoFit(es, utl + e - s); 2599 memcpy(es->undo_text + utl, buf, (e - s)*sizeof(WCHAR)); 2600 (es->undo_text + utl)[e - s] = 0; /* ensure 0 termination */ 2601 } else if (!es->undo_insert_count && (*es->undo_text && (e == es->undo_position))) { 2602 /* undo-buffer is extended to the left */ 2603 EDIT_MakeUndoFit(es, utl + e - s); 2604 for (p = es->undo_text + utl ; p >= es->undo_text ; p--) 2605 p[e - s] = p[0]; 2606 for (i = 0 , p = es->undo_text ; i < e - s ; i++) 2607 p[i] = buf[i]; 2608 es->undo_position = s; 2609 } else { 2610 /* new undo-buffer */ 2611 EDIT_MakeUndoFit(es, e - s); 2612 memcpy(es->undo_text, buf, (e - s)*sizeof(WCHAR)); 2613 es->undo_text[e - s] = 0; /* ensure 0 termination */ 2614 es->undo_position = s; 2615 } 2616 /* any deletion makes the old insertion-undo invalid */ 2617 es->undo_insert_count = 0; 2618 } else 2619 EDIT_EM_EmptyUndoBuffer(es); 2620 } 2621 if (strl) { 2622 if (can_undo) { 2623 if ((s == es->undo_position) || 2624 ((es->undo_insert_count) && 2625 (s == es->undo_position + es->undo_insert_count))) 2626 /* 2627 * insertion is new and at delete position or 2628 * an extension to either left or right 2629 */ 2630 es->undo_insert_count += strl; 2631 else { 2632 /* new insertion undo */ 2633 es->undo_position = s; 2634 es->undo_insert_count = strl; 2635 /* new insertion makes old delete-buffer invalid */ 2636 *es->undo_text = '\0'; 2637 } 2638 } else 2639 EDIT_EM_EmptyUndoBuffer(es); 2640 } 2641 2642 heap_free(buf); 2643 2644 s += strl; 2645 2646 /* If text has been deleted and we're right or center aligned then scroll rightward */ 2647 if (es->style & (ES_RIGHT | ES_CENTER)) 2648 { 2649 INT delta = strl - abs(es->selection_end - es->selection_start); 2650 2651 if (delta < 0 && es->x_offset) 2652 { 2653 if (abs(delta) > es->x_offset) 2654 es->x_offset = 0; 2655 else 2656 es->x_offset += delta; 2657 } 2658 } 2659 2660 EDIT_EM_SetSel(es, s, s, FALSE); 2661 es->flags |= EF_MODIFIED; 2662 if (send_update) es->flags |= EF_UPDATE; 2663 if (hrgn) 2664 { 2665 EDIT_UpdateTextRegion(es, hrgn, TRUE); 2666 DeleteObject(hrgn); 2667 } 2668 else 2669 EDIT_UpdateText(es, NULL, TRUE); 2670 2671 EDIT_EM_ScrollCaret(es); 2672 2673 /* force scroll info update */ 2674 EDIT_UpdateScrollInfo(es); 2675 2676 2677 if(send_update || (es->flags & EF_UPDATE)) 2678 { 2679 es->flags &= ~EF_UPDATE; 2680 if (!notify_parent(es, EN_CHANGE)) return; 2681 } 2682 EDIT_InvalidateUniscribeData(es); 2683 } 2684 2685 2686 /********************************************************************* 2687 * 2688 * EM_SETHANDLE 2689 * 2690 * FIXME: ES_LOWERCASE, ES_UPPERCASE, ES_OEMCONVERT, ES_NUMBER ??? 2691 * 2692 */ 2693 static void EDIT_EM_SetHandle(EDITSTATE *es, HLOCAL hloc) 2694 { 2695 if (!(es->style & ES_MULTILINE)) 2696 return; 2697 2698 if (!hloc) 2699 return; 2700 2701 EDIT_UnlockBuffer(es, TRUE); 2702 2703 es->hloc32W = hloc; 2704 es->buffer_size = LocalSize(es->hloc32W)/sizeof(WCHAR) - 1; 2705 2706 /* The text buffer handle belongs to the control */ 2707 es->hlocapp = NULL; 2708 2709 EDIT_LockBuffer(es); 2710 text_buffer_changed(es); 2711 2712 es->x_offset = es->y_offset = 0; 2713 es->selection_start = es->selection_end = 0; 2714 EDIT_EM_EmptyUndoBuffer(es); 2715 es->flags &= ~EF_MODIFIED; 2716 es->flags &= ~EF_UPDATE; 2717 EDIT_BuildLineDefs_ML(es, 0, get_text_length(es), 0, NULL); 2718 EDIT_UpdateText(es, NULL, TRUE); 2719 EDIT_EM_ScrollCaret(es); 2720 /* force scroll info update */ 2721 EDIT_UpdateScrollInfo(es); 2722 } 2723 2724 2725 /********************************************************************* 2726 * 2727 * EM_SETLIMITTEXT 2728 * 2729 * NOTE: this version currently implements WinNT limits 2730 * 2731 */ 2732 static void EDIT_EM_SetLimitText(EDITSTATE *es, UINT limit) 2733 { 2734 if (!limit) limit = ~0u; 2735 if (!(es->style & ES_MULTILINE)) limit = min(limit, 0x7ffffffe); 2736 es->buffer_limit = limit; 2737 } 2738 2739 2740 /********************************************************************* 2741 * 2742 * EM_SETMARGINS 2743 * 2744 * EC_USEFONTINFO is used as a left or right value i.e. lParam and not as an 2745 * action wParam despite what the docs say. EC_USEFONTINFO calculates the 2746 * margin according to the textmetrics of the current font. 2747 * 2748 * When EC_USEFONTINFO is used in the non_cjk case the margins only 2749 * change if the edit control is equal to or larger than a certain 2750 * size. Though there is an exception for the empty client rect case 2751 * with small font sizes. 2752 */ 2753 static BOOL is_cjk(UINT charset) 2754 { 2755 switch(charset) 2756 { 2757 case SHIFTJIS_CHARSET: 2758 case HANGUL_CHARSET: 2759 case GB2312_CHARSET: 2760 case CHINESEBIG5_CHARSET: 2761 return TRUE; 2762 } 2763 /* HANGUL_CHARSET is strange, though treated as CJK by Win 8, it is 2764 * not by other versions including Win 10. */ 2765 return FALSE; 2766 } 2767 2768 static void EDIT_EM_SetMargins(EDITSTATE *es, INT action, 2769 WORD left, WORD right, BOOL repaint) 2770 { 2771 TEXTMETRICW tm; 2772 INT default_left_margin = 0; /* in pixels */ 2773 INT default_right_margin = 0; /* in pixels */ 2774 2775 /* Set the default margins depending on the font */ 2776 if (es->font && (left == EC_USEFONTINFO || right == EC_USEFONTINFO)) { 2777 HDC dc = GetDC(es->hwndSelf); 2778 HFONT old_font = SelectObject(dc, es->font); 2779 LONG width = GdiGetCharDimensions(dc, &tm, NULL); 2780 RECT rc; 2781 2782 /* The default margins are only non zero for TrueType or Vector fonts */ 2783 if (tm.tmPitchAndFamily & ( TMPF_VECTOR | TMPF_TRUETYPE )) { 2784 if (!is_cjk(tm.tmCharSet)) { 2785 default_left_margin = width / 2; 2786 default_right_margin = width / 2; 2787 2788 GetClientRect(es->hwndSelf, &rc); 2789 if (rc.right - rc.left < (width / 2 + width) * 2 && 2790 (width >= 28 || !IsRectEmpty(&rc)) ) { 2791 default_left_margin = es->left_margin; 2792 default_right_margin = es->right_margin; 2793 } 2794 } else { 2795 /* FIXME: figure out the CJK values. They are not affected by the client rect. */ 2796 default_left_margin = width / 2; 2797 default_right_margin = width / 2; 2798 } 2799 } 2800 SelectObject(dc, old_font); 2801 ReleaseDC(es->hwndSelf, dc); 2802 } 2803 2804 if (action & EC_LEFTMARGIN) { 2805 es->format_rect.left -= es->left_margin; 2806 if (left != EC_USEFONTINFO) 2807 es->left_margin = left; 2808 else 2809 es->left_margin = default_left_margin; 2810 es->format_rect.left += es->left_margin; 2811 } 2812 2813 if (action & EC_RIGHTMARGIN) { 2814 es->format_rect.right += es->right_margin; 2815 if (right != EC_USEFONTINFO) 2816 es->right_margin = right; 2817 else 2818 es->right_margin = default_right_margin; 2819 es->format_rect.right -= es->right_margin; 2820 } 2821 2822 if (action & (EC_LEFTMARGIN | EC_RIGHTMARGIN)) { 2823 EDIT_AdjustFormatRect(es); 2824 if (repaint) EDIT_UpdateText(es, NULL, TRUE); 2825 } 2826 2827 TRACE("left=%d, right=%d\n", es->left_margin, es->right_margin); 2828 } 2829 2830 2831 /********************************************************************* 2832 * 2833 * EM_SETPASSWORDCHAR 2834 * 2835 */ 2836 static void EDIT_EM_SetPasswordChar(EDITSTATE *es, WCHAR c) 2837 { 2838 LONG style; 2839 2840 if (es->style & ES_MULTILINE) 2841 return; 2842 2843 if (es->password_char == c) 2844 return; 2845 2846 style = GetWindowLongW( es->hwndSelf, GWL_STYLE ); 2847 es->password_char = c; 2848 if (c) { 2849 SetWindowLongW( es->hwndSelf, GWL_STYLE, style | ES_PASSWORD ); 2850 es->style |= ES_PASSWORD; 2851 } else { 2852 SetWindowLongW( es->hwndSelf, GWL_STYLE, style & ~ES_PASSWORD ); 2853 es->style &= ~ES_PASSWORD; 2854 } 2855 EDIT_InvalidateUniscribeData(es); 2856 EDIT_UpdateText(es, NULL, TRUE); 2857 } 2858 2859 2860 /********************************************************************* 2861 * 2862 * EM_SETTABSTOPS 2863 * 2864 */ 2865 static BOOL EDIT_EM_SetTabStops(EDITSTATE *es, INT count, const INT *tabs) 2866 { 2867 if (!(es->style & ES_MULTILINE)) 2868 return FALSE; 2869 heap_free(es->tabs); 2870 es->tabs_count = count; 2871 if (!count) 2872 es->tabs = NULL; 2873 else { 2874 es->tabs = heap_alloc(count * sizeof(INT)); 2875 memcpy(es->tabs, tabs, count * sizeof(INT)); 2876 } 2877 EDIT_InvalidateUniscribeData(es); 2878 return TRUE; 2879 } 2880 2881 2882 /********************************************************************* 2883 * 2884 * EM_SETWORDBREAKPROC 2885 * 2886 */ 2887 static void EDIT_EM_SetWordBreakProc(EDITSTATE *es, EDITWORDBREAKPROCW wbp) 2888 { 2889 if (es->word_break_proc == wbp) 2890 return; 2891 2892 es->word_break_proc = wbp; 2893 2894 if ((es->style & ES_MULTILINE) && !(es->style & ES_AUTOHSCROLL)) { 2895 EDIT_BuildLineDefs_ML(es, 0, get_text_length(es), 0, NULL); 2896 EDIT_UpdateText(es, NULL, TRUE); 2897 } 2898 } 2899 2900 2901 /********************************************************************* 2902 * 2903 * EM_UNDO / WM_UNDO 2904 * 2905 */ 2906 static BOOL EDIT_EM_Undo(EDITSTATE *es) 2907 { 2908 INT ulength; 2909 LPWSTR utext; 2910 2911 /* As per MSDN spec, for a single-line edit control, 2912 the return value is always TRUE */ 2913 if( es->style & ES_READONLY ) 2914 return !(es->style & ES_MULTILINE); 2915 2916 ulength = strlenW(es->undo_text); 2917 2918 utext = heap_alloc((ulength + 1) * sizeof(WCHAR)); 2919 2920 strcpyW(utext, es->undo_text); 2921 2922 TRACE("before UNDO:insertion length = %d, deletion buffer = %s\n", 2923 es->undo_insert_count, debugstr_w(utext)); 2924 2925 EDIT_EM_SetSel(es, es->undo_position, es->undo_position + es->undo_insert_count, FALSE); 2926 EDIT_EM_EmptyUndoBuffer(es); 2927 EDIT_EM_ReplaceSel(es, TRUE, utext, ulength, TRUE, TRUE); 2928 EDIT_EM_SetSel(es, es->undo_position, es->undo_position + es->undo_insert_count, FALSE); 2929 /* send the notification after the selection start and end are set */ 2930 if (!notify_parent(es, EN_CHANGE)) return TRUE; 2931 EDIT_EM_ScrollCaret(es); 2932 heap_free(utext); 2933 2934 TRACE("after UNDO:insertion length = %d, deletion buffer = %s\n", 2935 es->undo_insert_count, debugstr_w(es->undo_text)); 2936 return TRUE; 2937 } 2938 2939 2940 /* Helper function for WM_CHAR 2941 * 2942 * According to an MSDN blog article titled "Just because you're a control 2943 * doesn't mean that you're necessarily inside a dialog box," multiline edit 2944 * controls without ES_WANTRETURN would attempt to detect whether it is inside 2945 * a dialog box or not. 2946 */ 2947 static inline BOOL EDIT_IsInsideDialog(EDITSTATE *es) 2948 { 2949 return (es->flags & EF_DIALOGMODE); 2950 } 2951 2952 2953 /********************************************************************* 2954 * 2955 * WM_PASTE 2956 * 2957 */ 2958 static void EDIT_WM_Paste(EDITSTATE *es) 2959 { 2960 HGLOBAL hsrc; 2961 LPWSTR src, ptr; 2962 int len; 2963 2964 /* Protect read-only edit control from modification */ 2965 if(es->style & ES_READONLY) 2966 return; 2967 2968 OpenClipboard(es->hwndSelf); 2969 if ((hsrc = GetClipboardData(CF_UNICODETEXT))) { 2970 src = GlobalLock(hsrc); 2971 len = strlenW(src); 2972 /* Protect single-line edit against pasting new line character */ 2973 if (!(es->style & ES_MULTILINE) && ((ptr = strchrW(src, '\n')))) { 2974 len = ptr - src; 2975 if (len && src[len - 1] == '\r') 2976 --len; 2977 } 2978 EDIT_EM_ReplaceSel(es, TRUE, src, len, TRUE, TRUE); 2979 GlobalUnlock(hsrc); 2980 } 2981 else if (es->style & ES_PASSWORD) { 2982 /* clear selected text in password edit box even with empty clipboard */ 2983 EDIT_EM_ReplaceSel(es, TRUE, NULL, 0, TRUE, TRUE); 2984 } 2985 CloseClipboard(); 2986 } 2987 2988 2989 /********************************************************************* 2990 * 2991 * WM_COPY 2992 * 2993 */ 2994 static void EDIT_WM_Copy(EDITSTATE *es) 2995 { 2996 INT s = min(es->selection_start, es->selection_end); 2997 INT e = max(es->selection_start, es->selection_end); 2998 HGLOBAL hdst; 2999 LPWSTR dst; 3000 DWORD len; 3001 3002 if (e == s) return; 3003 3004 len = e - s; 3005 hdst = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, (len + 1) * sizeof(WCHAR)); 3006 dst = GlobalLock(hdst); 3007 memcpy(dst, es->text + s, len * sizeof(WCHAR)); 3008 dst[len] = 0; /* ensure 0 termination */ 3009 TRACE("%s\n", debugstr_w(dst)); 3010 GlobalUnlock(hdst); 3011 OpenClipboard(es->hwndSelf); 3012 EmptyClipboard(); 3013 SetClipboardData(CF_UNICODETEXT, hdst); 3014 CloseClipboard(); 3015 } 3016 3017 3018 /********************************************************************* 3019 * 3020 * WM_CLEAR 3021 * 3022 */ 3023 static inline void EDIT_WM_Clear(EDITSTATE *es) 3024 { 3025 /* Protect read-only edit control from modification */ 3026 if(es->style & ES_READONLY) 3027 return; 3028 3029 EDIT_EM_ReplaceSel(es, TRUE, NULL, 0, TRUE, TRUE); 3030 } 3031 3032 3033 /********************************************************************* 3034 * 3035 * WM_CUT 3036 * 3037 */ 3038 static inline void EDIT_WM_Cut(EDITSTATE *es) 3039 { 3040 EDIT_WM_Copy(es); 3041 EDIT_WM_Clear(es); 3042 } 3043 3044 3045 /********************************************************************* 3046 * 3047 * WM_CHAR 3048 * 3049 */ 3050 static LRESULT EDIT_WM_Char(EDITSTATE *es, WCHAR c) 3051 { 3052 BOOL control; 3053 3054 control = GetKeyState(VK_CONTROL) & 0x8000; 3055 3056 switch (c) { 3057 case '\r': 3058 /* If it's not a multiline edit box, it would be ignored below. 3059 * For multiline edit without ES_WANTRETURN, we have to make a 3060 * special case. 3061 */ 3062 if ((es->style & ES_MULTILINE) && !(es->style & ES_WANTRETURN)) 3063 if (EDIT_IsInsideDialog(es)) 3064 break; 3065 case '\n': 3066 if (es->style & ES_MULTILINE) { 3067 if (es->style & ES_READONLY) { 3068 EDIT_MoveHome(es, FALSE, FALSE); 3069 EDIT_MoveDown_ML(es, FALSE); 3070 } else { 3071 static const WCHAR cr_lfW[] = {'\r','\n'}; 3072 EDIT_EM_ReplaceSel(es, TRUE, cr_lfW, 2, TRUE, TRUE); 3073 } 3074 } 3075 break; 3076 case '\t': 3077 if ((es->style & ES_MULTILINE) && !(es->style & ES_READONLY)) 3078 { 3079 static const WCHAR tabW[] = {'\t'}; 3080 if (EDIT_IsInsideDialog(es)) 3081 break; 3082 EDIT_EM_ReplaceSel(es, TRUE, tabW, 1, TRUE, TRUE); 3083 } 3084 break; 3085 case VK_BACK: 3086 if (!(es->style & ES_READONLY) && !control) { 3087 if (es->selection_start != es->selection_end) 3088 EDIT_WM_Clear(es); 3089 else { 3090 /* delete character left of caret */ 3091 EDIT_EM_SetSel(es, (UINT)-1, 0, FALSE); 3092 EDIT_MoveBackward(es, TRUE); 3093 EDIT_WM_Clear(es); 3094 } 3095 } 3096 break; 3097 case 0x03: /* ^C */ 3098 if (!(es->style & ES_PASSWORD)) 3099 SendMessageW(es->hwndSelf, WM_COPY, 0, 0); 3100 break; 3101 case 0x16: /* ^V */ 3102 if (!(es->style & ES_READONLY)) 3103 SendMessageW(es->hwndSelf, WM_PASTE, 0, 0); 3104 break; 3105 case 0x18: /* ^X */ 3106 if (!((es->style & ES_READONLY) || (es->style & ES_PASSWORD))) 3107 SendMessageW(es->hwndSelf, WM_CUT, 0, 0); 3108 break; 3109 case 0x1A: /* ^Z */ 3110 if (!(es->style & ES_READONLY)) 3111 SendMessageW(es->hwndSelf, WM_UNDO, 0, 0); 3112 break; 3113 3114 default: 3115 /*If Edit control style is ES_NUMBER allow users to key in only numeric values*/ 3116 if( (es->style & ES_NUMBER) && !( c >= '0' && c <= '9') ) 3117 break; 3118 3119 if (!(es->style & ES_READONLY) && (c >= ' ') && (c != 127)) 3120 EDIT_EM_ReplaceSel(es, TRUE, &c, 1, TRUE, TRUE); 3121 break; 3122 } 3123 return 1; 3124 } 3125 3126 3127 /********************************************************************* 3128 * 3129 * EDIT_ContextMenuCommand 3130 * 3131 */ 3132 static void EDIT_ContextMenuCommand(EDITSTATE *es, UINT id) 3133 { 3134 switch (id) { 3135 case EM_UNDO: 3136 SendMessageW(es->hwndSelf, WM_UNDO, 0, 0); 3137 break; 3138 case WM_CUT: 3139 SendMessageW(es->hwndSelf, WM_CUT, 0, 0); 3140 break; 3141 case WM_COPY: 3142 SendMessageW(es->hwndSelf, WM_COPY, 0, 0); 3143 break; 3144 case WM_PASTE: 3145 SendMessageW(es->hwndSelf, WM_PASTE, 0, 0); 3146 break; 3147 case WM_CLEAR: 3148 SendMessageW(es->hwndSelf, WM_CLEAR, 0, 0); 3149 break; 3150 case EM_SETSEL: 3151 SendMessageW(es->hwndSelf, EM_SETSEL, 0, -1); 3152 break; 3153 default: 3154 ERR("unknown menu item, please report\n"); 3155 break; 3156 } 3157 } 3158 3159 3160 /********************************************************************* 3161 * 3162 * WM_CONTEXTMENU 3163 * 3164 * Note: the resource files resource/sysres_??.rc cannot define a 3165 * single popup menu. Hence we use a (dummy) menubar 3166 * containing the single popup menu as its first item. 3167 * 3168 * FIXME: the message identifiers have been chosen arbitrarily, 3169 * hence we use MF_BYPOSITION. 3170 * We might as well use the "real" values (anybody knows ?) 3171 * The menu definition is in resources/sysres_??.rc. 3172 * Once these are OK, we better use MF_BYCOMMAND here 3173 * (as we do in EDIT_WM_Command()). 3174 * 3175 */ 3176 static void EDIT_WM_ContextMenu(EDITSTATE *es, INT x, INT y) 3177 { 3178 HMENU menu = LoadMenuA(GetModuleHandleA("user32.dll"), "EDITMENU"); 3179 HMENU popup = GetSubMenu(menu, 0); 3180 UINT start = es->selection_start; 3181 UINT end = es->selection_end; 3182 UINT cmd; 3183 POINT pt; 3184 3185 ORDER_UINT(start, end); 3186 3187 /* undo */ 3188 EnableMenuItem(popup, 0, MF_BYPOSITION | (EDIT_EM_CanUndo(es) && !(es->style & ES_READONLY) ? MF_ENABLED : MF_GRAYED)); 3189 /* cut */ 3190 EnableMenuItem(popup, 2, MF_BYPOSITION | ((end - start) && !(es->style & ES_PASSWORD) && !(es->style & ES_READONLY) ? MF_ENABLED : MF_GRAYED)); 3191 /* copy */ 3192 EnableMenuItem(popup, 3, MF_BYPOSITION | ((end - start) && !(es->style & ES_PASSWORD) ? MF_ENABLED : MF_GRAYED)); 3193 /* paste */ 3194 EnableMenuItem(popup, 4, MF_BYPOSITION | (IsClipboardFormatAvailable(CF_UNICODETEXT) && !(es->style & ES_READONLY) ? MF_ENABLED : MF_GRAYED)); 3195 /* delete */ 3196 EnableMenuItem(popup, 5, MF_BYPOSITION | ((end - start) && !(es->style & ES_READONLY) ? MF_ENABLED : MF_GRAYED)); 3197 /* select all */ 3198 EnableMenuItem(popup, 7, MF_BYPOSITION | (start || (end != get_text_length(es)) ? MF_ENABLED : MF_GRAYED)); 3199 3200 pt.x = x; 3201 pt.y = y; 3202 3203 if (pt.x == -1 && pt.y == -1) /* passed via VK_APPS press/release */ 3204 { 3205 RECT rc; 3206 3207 /* Windows places the menu at the edit's center in this case */ 3208 GetClientRect(es->hwndSelf, &rc); 3209 pt.x = rc.left + (rc.right - rc.left) / 2; 3210 pt.y = rc.top + (rc.bottom - rc.top) / 2; 3211 ClientToScreen(es->hwndSelf, &pt); 3212 } 3213 3214 if (!(es->flags & EF_FOCUSED)) 3215 SetFocus(es->hwndSelf); 3216 3217 cmd = TrackPopupMenu(popup, TPM_LEFTALIGN | TPM_RIGHTBUTTON | TPM_RETURNCMD | TPM_NONOTIFY, 3218 pt.x, pt.y, 0, es->hwndSelf, NULL); 3219 3220 if (cmd) 3221 EDIT_ContextMenuCommand(es, cmd); 3222 3223 DestroyMenu(menu); 3224 } 3225 3226 3227 /********************************************************************* 3228 * 3229 * WM_GETTEXT 3230 * 3231 */ 3232 static INT EDIT_WM_GetText(const EDITSTATE *es, INT count, LPWSTR dst) 3233 { 3234 if (!count) 3235 return 0; 3236 3237 lstrcpynW(dst, es->text, count); 3238 return strlenW(dst); 3239 } 3240 3241 /********************************************************************* 3242 * 3243 * EDIT_CheckCombo 3244 * 3245 */ 3246 static BOOL EDIT_CheckCombo(EDITSTATE *es, UINT msg, INT key) 3247 { 3248 HWND hLBox = es->hwndListBox; 3249 HWND hCombo; 3250 BOOL bDropped; 3251 int nEUI; 3252 3253 if (!hLBox) 3254 return FALSE; 3255 3256 hCombo = GetParent(es->hwndSelf); 3257 bDropped = TRUE; 3258 nEUI = 0; 3259 3260 TRACE("[%p]: handling msg %x (%x)\n", es->hwndSelf, msg, key); 3261 3262 if (key == VK_UP || key == VK_DOWN) 3263 { 3264 if (SendMessageW(hCombo, CB_GETEXTENDEDUI, 0, 0)) 3265 nEUI = 1; 3266 3267 if (msg == WM_KEYDOWN || nEUI) 3268 bDropped = (BOOL)SendMessageW(hCombo, CB_GETDROPPEDSTATE, 0, 0); 3269 } 3270 3271 switch (msg) 3272 { 3273 case WM_KEYDOWN: 3274 if (!bDropped && nEUI && (key == VK_UP || key == VK_DOWN)) 3275 { 3276 /* make sure ComboLBox pops up */ 3277 SendMessageW(hCombo, CB_SETEXTENDEDUI, FALSE, 0); 3278 key = VK_F4; 3279 nEUI = 2; 3280 } 3281 3282 SendMessageW(hLBox, WM_KEYDOWN, key, 0); 3283 break; 3284 3285 case WM_SYSKEYDOWN: /* Handle Alt+up/down arrows */ 3286 if (nEUI) 3287 SendMessageW(hCombo, CB_SHOWDROPDOWN, !bDropped, 0); 3288 else 3289 SendMessageW(hLBox, WM_KEYDOWN, VK_F4, 0); 3290 break; 3291 } 3292 3293 if (nEUI == 2) 3294 SendMessageW(hCombo, CB_SETEXTENDEDUI, TRUE, 0); 3295 3296 return TRUE; 3297 } 3298 3299 3300 /********************************************************************* 3301 * 3302 * WM_KEYDOWN 3303 * 3304 * Handling of special keys that don't produce a WM_CHAR 3305 * (i.e. non-printable keys) & Backspace & Delete 3306 * 3307 */ 3308 static LRESULT EDIT_WM_KeyDown(EDITSTATE *es, INT key) 3309 { 3310 BOOL shift; 3311 BOOL control; 3312 3313 if (GetKeyState(VK_MENU) & 0x8000) 3314 return 0; 3315 3316 shift = GetKeyState(VK_SHIFT) & 0x8000; 3317 control = GetKeyState(VK_CONTROL) & 0x8000; 3318 3319 switch (key) { 3320 case VK_F4: 3321 case VK_UP: 3322 if (EDIT_CheckCombo(es, WM_KEYDOWN, key) || key == VK_F4) 3323 break; 3324 3325 /* fall through */ 3326 case VK_LEFT: 3327 if ((es->style & ES_MULTILINE) && (key == VK_UP)) 3328 EDIT_MoveUp_ML(es, shift); 3329 else 3330 if (control) 3331 EDIT_MoveWordBackward(es, shift); 3332 else 3333 EDIT_MoveBackward(es, shift); 3334 break; 3335 case VK_DOWN: 3336 if (EDIT_CheckCombo(es, WM_KEYDOWN, key)) 3337 break; 3338 /* fall through */ 3339 case VK_RIGHT: 3340 if ((es->style & ES_MULTILINE) && (key == VK_DOWN)) 3341 EDIT_MoveDown_ML(es, shift); 3342 else if (control) 3343 EDIT_MoveWordForward(es, shift); 3344 else 3345 EDIT_MoveForward(es, shift); 3346 break; 3347 case VK_HOME: 3348 EDIT_MoveHome(es, shift, control); 3349 break; 3350 case VK_END: 3351 EDIT_MoveEnd(es, shift, control); 3352 break; 3353 case VK_PRIOR: 3354 if (es->style & ES_MULTILINE) 3355 EDIT_MovePageUp_ML(es, shift); 3356 else 3357 EDIT_CheckCombo(es, WM_KEYDOWN, key); 3358 break; 3359 case VK_NEXT: 3360 if (es->style & ES_MULTILINE) 3361 EDIT_MovePageDown_ML(es, shift); 3362 else 3363 EDIT_CheckCombo(es, WM_KEYDOWN, key); 3364 break; 3365 case VK_DELETE: 3366 if (!(es->style & ES_READONLY) && !(shift && control)) { 3367 if (es->selection_start != es->selection_end) { 3368 if (shift) 3369 EDIT_WM_Cut(es); 3370 else 3371 EDIT_WM_Clear(es); 3372 } else { 3373 if (shift) { 3374 /* delete character left of caret */ 3375 EDIT_EM_SetSel(es, (UINT)-1, 0, FALSE); 3376 EDIT_MoveBackward(es, TRUE); 3377 EDIT_WM_Clear(es); 3378 } else if (control) { 3379 /* delete to end of line */ 3380 EDIT_EM_SetSel(es, (UINT)-1, 0, FALSE); 3381 EDIT_MoveEnd(es, TRUE, FALSE); 3382 EDIT_WM_Clear(es); 3383 } else { 3384 /* delete character right of caret */ 3385 EDIT_EM_SetSel(es, (UINT)-1, 0, FALSE); 3386 EDIT_MoveForward(es, TRUE); 3387 EDIT_WM_Clear(es); 3388 } 3389 } 3390 } 3391 break; 3392 case VK_INSERT: 3393 if (shift) { 3394 if (!(es->style & ES_READONLY)) 3395 EDIT_WM_Paste(es); 3396 } else if (control) 3397 EDIT_WM_Copy(es); 3398 break; 3399 case VK_RETURN: 3400 /* If the edit doesn't want the return send a message to the default object */ 3401 if(!(es->style & ES_MULTILINE) || !(es->style & ES_WANTRETURN)) 3402 { 3403 DWORD dw; 3404 3405 if (!EDIT_IsInsideDialog(es)) break; 3406 if (control) break; 3407 dw = SendMessageW(es->hwndParent, DM_GETDEFID, 0, 0); 3408 if (HIWORD(dw) == DC_HASDEFID) 3409 { 3410 HWND hwDefCtrl = GetDlgItem(es->hwndParent, LOWORD(dw)); 3411 if (hwDefCtrl) 3412 { 3413 SendMessageW(es->hwndParent, WM_NEXTDLGCTL, (WPARAM)hwDefCtrl, TRUE); 3414 PostMessageW(hwDefCtrl, WM_KEYDOWN, VK_RETURN, 0); 3415 } 3416 } 3417 } 3418 break; 3419 case VK_ESCAPE: 3420 if ((es->style & ES_MULTILINE) && EDIT_IsInsideDialog(es)) 3421 PostMessageW(es->hwndParent, WM_CLOSE, 0, 0); 3422 break; 3423 case VK_TAB: 3424 if ((es->style & ES_MULTILINE) && EDIT_IsInsideDialog(es)) 3425 SendMessageW(es->hwndParent, WM_NEXTDLGCTL, shift, 0); 3426 break; 3427 case 'A': 3428 if (control) 3429 { 3430 if (EDIT_EM_SetSel(es, 0, get_text_length(es), FALSE)) 3431 { 3432 if (!notify_parent(es, EN_UPDATE)) break; 3433 notify_parent(es, EN_CHANGE); 3434 } 3435 } 3436 break; 3437 } 3438 return TRUE; 3439 } 3440 3441 3442 /********************************************************************* 3443 * 3444 * WM_KILLFOCUS 3445 * 3446 */ 3447 static LRESULT EDIT_WM_KillFocus(HTHEME theme, EDITSTATE *es) 3448 { 3449 UINT flags = RDW_INVALIDATE | RDW_UPDATENOW; 3450 HWND hwndSelf = es->hwndSelf; 3451 3452 es->flags &= ~EF_FOCUSED; 3453 DestroyCaret(); 3454 if (!(es->style & ES_NOHIDESEL)) 3455 EDIT_InvalidateText(es, es->selection_start, es->selection_end); 3456 if (!notify_parent(es, EN_KILLFOCUS)) return 0; 3457 /* Throw away left over scroll when we lose focus */ 3458 es->wheelDeltaRemainder = 0; 3459 3460 if (theme) 3461 flags |= RDW_FRAME; 3462 3463 RedrawWindow(hwndSelf, NULL, NULL, flags); 3464 return 0; 3465 } 3466 3467 3468 /********************************************************************* 3469 * 3470 * WM_LBUTTONDBLCLK 3471 * 3472 * The caret position has been set on the WM_LBUTTONDOWN message 3473 * 3474 */ 3475 static LRESULT EDIT_WM_LButtonDblClk(EDITSTATE *es) 3476 { 3477 INT s; 3478 INT e = es->selection_end; 3479 INT l; 3480 INT li; 3481 INT ll; 3482 3483 es->bCaptureState = TRUE; 3484 SetCapture(es->hwndSelf); 3485 3486 l = EDIT_EM_LineFromChar(es, e); 3487 li = EDIT_EM_LineIndex(es, l); 3488 ll = EDIT_EM_LineLength(es, e); 3489 s = li + EDIT_CallWordBreakProc(es, li, e - li, ll, WB_LEFT); 3490 e = li + EDIT_CallWordBreakProc(es, li, e - li, ll, WB_RIGHT); 3491 EDIT_EM_SetSel(es, s, e, FALSE); 3492 EDIT_EM_ScrollCaret(es); 3493 es->region_posx = es->region_posy = 0; 3494 SetTimer(es->hwndSelf, 0, 100, NULL); 3495 return 0; 3496 } 3497 3498 3499 /********************************************************************* 3500 * 3501 * WM_LBUTTONDOWN 3502 * 3503 */ 3504 static LRESULT EDIT_WM_LButtonDown(EDITSTATE *es, DWORD keys, INT x, INT y) 3505 { 3506 INT e; 3507 BOOL after_wrap; 3508 3509 es->bCaptureState = TRUE; 3510 SetCapture(es->hwndSelf); 3511 EDIT_ConfinePoint(es, &x, &y); 3512 e = EDIT_CharFromPos(es, x, y, &after_wrap); 3513 EDIT_EM_SetSel(es, (keys & MK_SHIFT) ? es->selection_start : e, e, after_wrap); 3514 EDIT_EM_ScrollCaret(es); 3515 es->region_posx = es->region_posy = 0; 3516 SetTimer(es->hwndSelf, 0, 100, NULL); 3517 3518 if (!(es->flags & EF_FOCUSED)) 3519 SetFocus(es->hwndSelf); 3520 3521 return 0; 3522 } 3523 3524 3525 /********************************************************************* 3526 * 3527 * WM_LBUTTONUP 3528 * 3529 */ 3530 static LRESULT EDIT_WM_LButtonUp(EDITSTATE *es) 3531 { 3532 if (es->bCaptureState) { 3533 KillTimer(es->hwndSelf, 0); 3534 if (GetCapture() == es->hwndSelf) ReleaseCapture(); 3535 } 3536 es->bCaptureState = FALSE; 3537 return 0; 3538 } 3539 3540 3541 /********************************************************************* 3542 * 3543 * WM_MBUTTONDOWN 3544 * 3545 */ 3546 static LRESULT EDIT_WM_MButtonDown(EDITSTATE *es) 3547 { 3548 SendMessageW(es->hwndSelf, WM_PASTE, 0, 0); 3549 return 0; 3550 } 3551 3552 3553 /********************************************************************* 3554 * 3555 * WM_MOUSEMOVE 3556 * 3557 */ 3558 static LRESULT EDIT_WM_MouseMove(EDITSTATE *es, INT x, INT y) 3559 { 3560 INT e; 3561 BOOL after_wrap; 3562 INT prex, prey; 3563 3564 /* If the mouse has been captured by process other than the edit control itself, 3565 * the windows edit controls will not select the strings with mouse move. 3566 */ 3567 if (!es->bCaptureState || GetCapture() != es->hwndSelf) 3568 return 0; 3569 3570 /* 3571 * FIXME: gotta do some scrolling if outside client 3572 * area. Maybe reset the timer ? 3573 */ 3574 prex = x; prey = y; 3575 EDIT_ConfinePoint(es, &x, &y); 3576 es->region_posx = (prex < x) ? -1 : ((prex > x) ? 1 : 0); 3577 es->region_posy = (prey < y) ? -1 : ((prey > y) ? 1 : 0); 3578 e = EDIT_CharFromPos(es, x, y, &after_wrap); 3579 EDIT_EM_SetSel(es, es->selection_start, e, after_wrap); 3580 EDIT_SetCaretPos(es,es->selection_end,es->flags & EF_AFTER_WRAP); 3581 return 0; 3582 } 3583 3584 3585 /********************************************************************* 3586 * 3587 * WM_PAINT 3588 * 3589 */ 3590 static void EDIT_WM_Paint(EDITSTATE *es, HDC hdc) 3591 { 3592 PAINTSTRUCT ps; 3593 INT i; 3594 HDC dc; 3595 HFONT old_font = 0; 3596 RECT rc; 3597 RECT rcClient; 3598 RECT rcLine; 3599 RECT rcRgn; 3600 HBRUSH brush; 3601 HBRUSH old_brush; 3602 INT bw, bh; 3603 BOOL rev = es->bEnableState && 3604 ((es->flags & EF_FOCUSED) || 3605 (es->style & ES_NOHIDESEL)); 3606 dc = hdc ? hdc : BeginPaint(es->hwndSelf, &ps); 3607 3608 /* The dc we use for calculating may not be the one we paint into. 3609 This is the safest action. */ 3610 EDIT_InvalidateUniscribeData(es); 3611 GetClientRect(es->hwndSelf, &rcClient); 3612 3613 /* get the background brush */ 3614 brush = EDIT_NotifyCtlColor(es, dc); 3615 3616 /* paint the border and the background */ 3617 IntersectClipRect(dc, rcClient.left, rcClient.top, rcClient.right, rcClient.bottom); 3618 3619 if(es->style & WS_BORDER) { 3620 bw = GetSystemMetrics(SM_CXBORDER); 3621 bh = GetSystemMetrics(SM_CYBORDER); 3622 rc = rcClient; 3623 if(es->style & ES_MULTILINE) { 3624 if(es->style & WS_HSCROLL) rc.bottom+=bh; 3625 if(es->style & WS_VSCROLL) rc.right+=bw; 3626 } 3627 3628 /* Draw the frame. Same code as in nonclient.c */ 3629 old_brush = SelectObject(dc, GetSysColorBrush(COLOR_WINDOWFRAME)); 3630 PatBlt(dc, rc.left, rc.top, rc.right - rc.left, bh, PATCOPY); 3631 PatBlt(dc, rc.left, rc.top, bw, rc.bottom - rc.top, PATCOPY); 3632 PatBlt(dc, rc.left, rc.bottom - 1, rc.right - rc.left, -bw, PATCOPY); 3633 PatBlt(dc, rc.right - 1, rc.top, -bw, rc.bottom - rc.top, PATCOPY); 3634 SelectObject(dc, old_brush); 3635 3636 /* Keep the border clean */ 3637 IntersectClipRect(dc, rc.left+bw, rc.top+bh, 3638 max(rc.right-bw, rc.left+bw), max(rc.bottom-bh, rc.top+bh)); 3639 } 3640 3641 GetClipBox(dc, &rc); 3642 FillRect(dc, &rc, brush); 3643 3644 IntersectClipRect(dc, es->format_rect.left, 3645 es->format_rect.top, 3646 es->format_rect.right, 3647 es->format_rect.bottom); 3648 if (es->style & ES_MULTILINE) { 3649 rc = rcClient; 3650 IntersectClipRect(dc, rc.left, rc.top, rc.right, rc.bottom); 3651 } 3652 if (es->font) 3653 old_font = SelectObject(dc, es->font); 3654 3655 if (!es->bEnableState) 3656 SetTextColor(dc, GetSysColor(COLOR_GRAYTEXT)); 3657 GetClipBox(dc, &rcRgn); 3658 if (es->style & ES_MULTILINE) { 3659 INT vlc = get_vertical_line_count(es); 3660 for (i = es->y_offset ; i <= min(es->y_offset + vlc, es->y_offset + es->line_count - 1) ; i++) { 3661 EDIT_UpdateUniscribeData(es, dc, i); 3662 EDIT_GetLineRect(es, i, 0, -1, &rcLine); 3663 if (IntersectRect(&rc, &rcRgn, &rcLine)) 3664 EDIT_PaintLine(es, dc, i, rev); 3665 } 3666 } else { 3667 EDIT_UpdateUniscribeData(es, dc, 0); 3668 EDIT_GetLineRect(es, 0, 0, -1, &rcLine); 3669 if (IntersectRect(&rc, &rcRgn, &rcLine)) 3670 EDIT_PaintLine(es, dc, 0, rev); 3671 } 3672 if (es->font) 3673 SelectObject(dc, old_font); 3674 3675 if (!hdc) 3676 EndPaint(es->hwndSelf, &ps); 3677 } 3678 3679 static void EDIT_WM_NCPaint(HWND hwnd, HRGN region) 3680 { 3681 DWORD exStyle = GetWindowLongW(hwnd, GWL_EXSTYLE); 3682 HTHEME theme = GetWindowTheme(hwnd); 3683 HRGN cliprgn = region; 3684 3685 if (theme && exStyle & WS_EX_CLIENTEDGE) 3686 { 3687 HDC dc; 3688 RECT r; 3689 int cxEdge = GetSystemMetrics(SM_CXEDGE), 3690 cyEdge = GetSystemMetrics(SM_CYEDGE); 3691 const int part = EP_EDITTEXT; 3692 int state = ETS_NORMAL; 3693 DWORD dwStyle = GetWindowLongW(hwnd, GWL_STYLE); 3694 3695 if (!IsWindowEnabled(hwnd)) 3696 state = ETS_DISABLED; 3697 else if (dwStyle & ES_READONLY) 3698 state = ETS_READONLY; 3699 else if (GetFocus() == hwnd) 3700 state = ETS_FOCUSED; 3701 3702 GetWindowRect(hwnd, &r); 3703 3704 /* New clipping region passed to default proc to exclude border */ 3705 cliprgn = CreateRectRgn(r.left + cxEdge, r.top + cyEdge, 3706 r.right - cxEdge, r.bottom - cyEdge); 3707 if (region != (HRGN)1) 3708 CombineRgn(cliprgn, cliprgn, region, RGN_AND); 3709 OffsetRect(&r, -r.left, -r.top); 3710 3711 #ifdef __REACTOS__ /* r73789 */ 3712 dc = GetWindowDC(hwnd); 3713 /* Exclude client part */ 3714 ExcludeClipRect(dc, 3715 r.left + cxEdge, 3716 r.top + cyEdge, 3717 r.right - cxEdge, 3718 r.bottom -cyEdge); 3719 #else 3720 dc = GetDCEx(hwnd, region, DCX_WINDOW|DCX_INTERSECTRGN); 3721 OffsetRect(&r, -r.left, -r.top); 3722 #endif 3723 3724 if (IsThemeBackgroundPartiallyTransparent(theme, part, state)) 3725 DrawThemeParentBackground(hwnd, dc, &r); 3726 DrawThemeBackground(theme, dc, part, state, &r, 0); 3727 ReleaseDC(hwnd, dc); 3728 } 3729 3730 /* Call default proc to get the scrollbars etc. also painted */ 3731 DefWindowProcW (hwnd, WM_NCPAINT, (WPARAM)cliprgn, 0); 3732 } 3733 3734 /********************************************************************* 3735 * 3736 * WM_SETFOCUS 3737 * 3738 */ 3739 static void EDIT_WM_SetFocus(HTHEME theme, EDITSTATE *es) 3740 { 3741 UINT flags = RDW_INVALIDATE | RDW_UPDATENOW; 3742 3743 es->flags |= EF_FOCUSED; 3744 3745 if (!(es->style & ES_NOHIDESEL)) 3746 EDIT_InvalidateText(es, es->selection_start, es->selection_end); 3747 3748 #ifdef __REACTOS__ 3749 SystemParametersInfo(SPI_GETCARETWIDTH, 0, &es->dwCaretWidth, 0); 3750 CreateCaret(es->hwndSelf, NULL, es->dwCaretWidth, es->line_height); 3751 #else 3752 CreateCaret(es->hwndSelf, 0, 1, es->line_height); 3753 #endif 3754 EDIT_SetCaretPos(es, es->selection_end, es->flags & EF_AFTER_WRAP); 3755 ShowCaret(es->hwndSelf); 3756 if (!notify_parent(es, EN_SETFOCUS)) return; 3757 3758 if (theme) 3759 flags |= RDW_FRAME | RDW_ERASE; 3760 3761 RedrawWindow(es->hwndSelf, NULL, NULL, flags); 3762 } 3763 3764 3765 /********************************************************************* 3766 * 3767 * WM_SETFONT 3768 * 3769 * With Win95 look the margins are set to default font value unless 3770 * the system font (font == 0) is being set, in which case they are left 3771 * unchanged. 3772 * 3773 */ 3774 static void EDIT_WM_SetFont(EDITSTATE *es, HFONT font, BOOL redraw) 3775 { 3776 TEXTMETRICW tm; 3777 HDC dc; 3778 HFONT old_font = 0; 3779 RECT clientRect; 3780 3781 es->font = font; 3782 EDIT_InvalidateUniscribeData(es); 3783 dc = GetDC(es->hwndSelf); 3784 if (font) 3785 old_font = SelectObject(dc, font); 3786 GetTextMetricsW(dc, &tm); 3787 es->line_height = tm.tmHeight; 3788 es->char_width = tm.tmAveCharWidth; 3789 if (font) 3790 SelectObject(dc, old_font); 3791 ReleaseDC(es->hwndSelf, dc); 3792 3793 /* Reset the format rect and the margins */ 3794 GetClientRect(es->hwndSelf, &clientRect); 3795 EDIT_SetRectNP(es, &clientRect); 3796 EDIT_EM_SetMargins(es, EC_LEFTMARGIN | EC_RIGHTMARGIN, 3797 EC_USEFONTINFO, EC_USEFONTINFO, FALSE); 3798 3799 if (es->style & ES_MULTILINE) 3800 EDIT_BuildLineDefs_ML(es, 0, get_text_length(es), 0, NULL); 3801 else 3802 EDIT_CalcLineWidth_SL(es); 3803 3804 if (redraw) 3805 EDIT_UpdateText(es, NULL, TRUE); 3806 if (es->flags & EF_FOCUSED) { 3807 DestroyCaret(); 3808 #ifdef __REACTOS__ 3809 CreateCaret(es->hwndSelf, NULL, es->dwCaretWidth, es->line_height); 3810 #else 3811 CreateCaret(es->hwndSelf, 0, 1, es->line_height); 3812 #endif 3813 EDIT_SetCaretPos(es, es->selection_end, 3814 es->flags & EF_AFTER_WRAP); 3815 ShowCaret(es->hwndSelf); 3816 } 3817 #ifdef __REACTOS__ 3818 if (ImmIsIME(GetKeyboardLayout(0))) 3819 { 3820 LOGFONTW lf; 3821 HIMC hIMC = ImmGetContext(es->hwndSelf); 3822 if (font == NULL) 3823 font = (HFONT)GetStockObject(DEFAULT_GUI_FONT); 3824 GetObjectW(font, sizeof(lf), &lf); 3825 ImmSetCompositionFontW(hIMC, &lf); 3826 ImmReleaseContext(es->hwndSelf, hIMC); 3827 } 3828 #endif 3829 } 3830 3831 3832 /********************************************************************* 3833 * 3834 * WM_SETTEXT 3835 * 3836 * NOTES 3837 * For multiline controls (ES_MULTILINE), reception of WM_SETTEXT triggers: 3838 * The modified flag is reset. No notifications are sent. 3839 * 3840 * For single-line controls, reception of WM_SETTEXT triggers: 3841 * The modified flag is reset. EN_UPDATE and EN_CHANGE notifications are sent. 3842 * 3843 */ 3844 static void EDIT_WM_SetText(EDITSTATE *es, LPCWSTR text) 3845 { 3846 if (es->flags & EF_UPDATE) 3847 /* fixed this bug once; complain if we see it about to happen again. */ 3848 ERR("SetSel may generate UPDATE message whose handler may reset " 3849 "selection.\n"); 3850 3851 EDIT_EM_SetSel(es, 0, (UINT)-1, FALSE); 3852 if (text) 3853 { 3854 TRACE("%s\n", debugstr_w(text)); 3855 EDIT_EM_ReplaceSel(es, FALSE, text, strlenW(text), FALSE, FALSE); 3856 } 3857 else 3858 { 3859 TRACE("<NULL>\n"); 3860 EDIT_EM_ReplaceSel(es, FALSE, NULL, 0, FALSE, FALSE); 3861 } 3862 es->x_offset = 0; 3863 es->flags &= ~EF_MODIFIED; 3864 EDIT_EM_SetSel(es, 0, 0, FALSE); 3865 3866 /* Send the notification after the selection start and end have been set 3867 * edit control doesn't send notification on WM_SETTEXT 3868 * if it is multiline, or it is part of combobox 3869 */ 3870 if( !((es->style & ES_MULTILINE) || es->hwndListBox)) 3871 { 3872 if (!notify_parent(es, EN_UPDATE)) return; 3873 if (!notify_parent(es, EN_CHANGE)) return; 3874 } 3875 EDIT_EM_ScrollCaret(es); 3876 EDIT_UpdateScrollInfo(es); 3877 EDIT_InvalidateUniscribeData(es); 3878 } 3879 3880 3881 /********************************************************************* 3882 * 3883 * WM_SIZE 3884 * 3885 */ 3886 static void EDIT_WM_Size(EDITSTATE *es, UINT action) 3887 { 3888 if ((action == SIZE_MAXIMIZED) || (action == SIZE_RESTORED)) { 3889 RECT rc; 3890 GetClientRect(es->hwndSelf, &rc); 3891 EDIT_SetRectNP(es, &rc); 3892 EDIT_UpdateText(es, NULL, TRUE); 3893 } 3894 } 3895 3896 3897 /********************************************************************* 3898 * 3899 * WM_STYLECHANGED 3900 * 3901 * This message is sent by SetWindowLong on having changed either the Style 3902 * or the extended style. 3903 * 3904 * We ensure that the window's version of the styles and the EDITSTATE's agree. 3905 * 3906 * See also EDIT_WM_NCCreate 3907 * 3908 * It appears that the Windows version of the edit control allows the style 3909 * (as retrieved by GetWindowLong) to be any value and maintains an internal 3910 * style variable which will generally be different. In this function we 3911 * update the internal style based on what changed in the externally visible 3912 * style. 3913 * 3914 * Much of this content as based upon the MSDN, especially: 3915 * Platform SDK Documentation -> User Interface Services -> 3916 * Windows User Interface -> Edit Controls -> Edit Control Reference -> 3917 * Edit Control Styles 3918 */ 3919 static LRESULT EDIT_WM_StyleChanged ( EDITSTATE *es, WPARAM which, const STYLESTRUCT *style) 3920 { 3921 if (GWL_STYLE == which) { 3922 DWORD style_change_mask; 3923 DWORD new_style; 3924 /* Only a subset of changes can be applied after the control 3925 * has been created. 3926 */ 3927 style_change_mask = ES_UPPERCASE | ES_LOWERCASE | 3928 ES_NUMBER; 3929 if (es->style & ES_MULTILINE) 3930 style_change_mask |= ES_WANTRETURN; 3931 3932 new_style = style->styleNew & style_change_mask; 3933 3934 /* Number overrides lowercase overrides uppercase (at least it 3935 * does in Win95). However I'll bet that ES_NUMBER would be 3936 * invalid under Win 3.1. 3937 */ 3938 if (new_style & ES_NUMBER) { 3939 ; /* do not override the ES_NUMBER */ 3940 } else if (new_style & ES_LOWERCASE) { 3941 new_style &= ~ES_UPPERCASE; 3942 } 3943 3944 es->style = (es->style & ~style_change_mask) | new_style; 3945 } else if (GWL_EXSTYLE == which) { 3946 ; /* FIXME - what is needed here */ 3947 } else { 3948 WARN ("Invalid style change %ld\n",which); 3949 } 3950 3951 return 0; 3952 } 3953 3954 /********************************************************************* 3955 * 3956 * WM_SYSKEYDOWN 3957 * 3958 */ 3959 static LRESULT EDIT_WM_SysKeyDown(EDITSTATE *es, INT key, DWORD key_data) 3960 { 3961 if ((key == VK_BACK) && (key_data & 0x2000)) { 3962 if (EDIT_EM_CanUndo(es)) 3963 EDIT_EM_Undo(es); 3964 return 0; 3965 } else if (key == VK_UP || key == VK_DOWN) { 3966 if (EDIT_CheckCombo(es, WM_SYSKEYDOWN, key)) 3967 return 0; 3968 } 3969 return DefWindowProcW(es->hwndSelf, WM_SYSKEYDOWN, key, key_data); 3970 } 3971 3972 3973 /********************************************************************* 3974 * 3975 * WM_TIMER 3976 * 3977 */ 3978 static void EDIT_WM_Timer(EDITSTATE *es) 3979 { 3980 if (es->region_posx < 0) { 3981 EDIT_MoveBackward(es, TRUE); 3982 } else if (es->region_posx > 0) { 3983 EDIT_MoveForward(es, TRUE); 3984 } 3985 /* 3986 * FIXME: gotta do some vertical scrolling here, like 3987 * EDIT_EM_LineScroll(hwnd, 0, 1); 3988 */ 3989 } 3990 3991 /********************************************************************* 3992 * 3993 * WM_HSCROLL 3994 * 3995 */ 3996 static LRESULT EDIT_WM_HScroll(EDITSTATE *es, INT action, INT pos) 3997 { 3998 INT dx; 3999 INT fw; 4000 4001 if (!(es->style & ES_MULTILINE)) 4002 return 0; 4003 4004 if (!(es->style & ES_AUTOHSCROLL)) 4005 return 0; 4006 4007 dx = 0; 4008 fw = es->format_rect.right - es->format_rect.left; 4009 switch (action) { 4010 case SB_LINELEFT: 4011 TRACE("SB_LINELEFT\n"); 4012 if (es->x_offset) 4013 dx = -es->char_width; 4014 break; 4015 case SB_LINERIGHT: 4016 TRACE("SB_LINERIGHT\n"); 4017 if (es->x_offset < es->text_width) 4018 dx = es->char_width; 4019 break; 4020 case SB_PAGELEFT: 4021 TRACE("SB_PAGELEFT\n"); 4022 if (es->x_offset) 4023 dx = -fw / HSCROLL_FRACTION / es->char_width * es->char_width; 4024 break; 4025 case SB_PAGERIGHT: 4026 TRACE("SB_PAGERIGHT\n"); 4027 if (es->x_offset < es->text_width) 4028 dx = fw / HSCROLL_FRACTION / es->char_width * es->char_width; 4029 break; 4030 case SB_LEFT: 4031 TRACE("SB_LEFT\n"); 4032 if (es->x_offset) 4033 dx = -es->x_offset; 4034 break; 4035 case SB_RIGHT: 4036 TRACE("SB_RIGHT\n"); 4037 if (es->x_offset < es->text_width) 4038 dx = es->text_width - es->x_offset; 4039 break; 4040 case SB_THUMBTRACK: 4041 TRACE("SB_THUMBTRACK %d\n", pos); 4042 es->flags |= EF_HSCROLL_TRACK; 4043 if(es->style & WS_HSCROLL) 4044 dx = pos - es->x_offset; 4045 else 4046 { 4047 INT fw, new_x; 4048 /* Sanity check */ 4049 if(pos < 0 || pos > 100) return 0; 4050 /* Assume default scroll range 0-100 */ 4051 fw = es->format_rect.right - es->format_rect.left; 4052 new_x = pos * (es->text_width - fw) / 100; 4053 dx = es->text_width ? (new_x - es->x_offset) : 0; 4054 } 4055 break; 4056 case SB_THUMBPOSITION: 4057 TRACE("SB_THUMBPOSITION %d\n", pos); 4058 es->flags &= ~EF_HSCROLL_TRACK; 4059 if(GetWindowLongW( es->hwndSelf, GWL_STYLE ) & WS_HSCROLL) 4060 dx = pos - es->x_offset; 4061 else 4062 { 4063 INT fw, new_x; 4064 /* Sanity check */ 4065 if(pos < 0 || pos > 100) return 0; 4066 /* Assume default scroll range 0-100 */ 4067 fw = es->format_rect.right - es->format_rect.left; 4068 new_x = pos * (es->text_width - fw) / 100; 4069 dx = es->text_width ? (new_x - es->x_offset) : 0; 4070 } 4071 if (!dx) { 4072 /* force scroll info update */ 4073 EDIT_UpdateScrollInfo(es); 4074 notify_parent(es, EN_HSCROLL); 4075 } 4076 break; 4077 case SB_ENDSCROLL: 4078 TRACE("SB_ENDSCROLL\n"); 4079 break; 4080 /* 4081 * FIXME : the next two are undocumented ! 4082 * Are we doing the right thing ? 4083 * At least Win 3.1 Notepad makes use of EM_GETTHUMB this way, 4084 * although it's also a regular control message. 4085 */ 4086 case EM_GETTHUMB: /* this one is used by NT notepad */ 4087 { 4088 LRESULT ret; 4089 if(GetWindowLongW( es->hwndSelf, GWL_STYLE ) & WS_HSCROLL) 4090 ret = GetScrollPos(es->hwndSelf, SB_HORZ); 4091 else 4092 { 4093 /* Assume default scroll range 0-100 */ 4094 INT fw = es->format_rect.right - es->format_rect.left; 4095 ret = es->text_width ? es->x_offset * 100 / (es->text_width - fw) : 0; 4096 } 4097 TRACE("EM_GETTHUMB: returning %ld\n", ret); 4098 return ret; 4099 } 4100 case EM_LINESCROLL: 4101 TRACE("EM_LINESCROLL16\n"); 4102 dx = pos; 4103 break; 4104 4105 default: 4106 ERR("undocumented WM_HSCROLL action %d (0x%04x), please report\n", 4107 action, action); 4108 return 0; 4109 } 4110 if (dx) 4111 { 4112 INT fw = es->format_rect.right - es->format_rect.left; 4113 /* check if we are going to move too far */ 4114 if(es->x_offset + dx + fw > es->text_width) 4115 dx = es->text_width - fw - es->x_offset; 4116 if(dx) 4117 EDIT_EM_LineScroll_internal(es, dx, 0); 4118 } 4119 return 0; 4120 } 4121 4122 4123 /********************************************************************* 4124 * 4125 * WM_VSCROLL 4126 * 4127 */ 4128 static LRESULT EDIT_WM_VScroll(EDITSTATE *es, INT action, INT pos) 4129 { 4130 INT dy; 4131 4132 if (!(es->style & ES_MULTILINE)) 4133 return 0; 4134 4135 if (!(es->style & ES_AUTOVSCROLL)) 4136 return 0; 4137 4138 dy = 0; 4139 switch (action) { 4140 case SB_LINEUP: 4141 case SB_LINEDOWN: 4142 case SB_PAGEUP: 4143 case SB_PAGEDOWN: 4144 TRACE("action %d (%s)\n", action, (action == SB_LINEUP ? "SB_LINEUP" : 4145 (action == SB_LINEDOWN ? "SB_LINEDOWN" : 4146 (action == SB_PAGEUP ? "SB_PAGEUP" : 4147 "SB_PAGEDOWN")))); 4148 EDIT_EM_Scroll(es, action); 4149 return 0; 4150 case SB_TOP: 4151 TRACE("SB_TOP\n"); 4152 dy = -es->y_offset; 4153 break; 4154 case SB_BOTTOM: 4155 TRACE("SB_BOTTOM\n"); 4156 dy = es->line_count - 1 - es->y_offset; 4157 break; 4158 case SB_THUMBTRACK: 4159 TRACE("SB_THUMBTRACK %d\n", pos); 4160 es->flags |= EF_VSCROLL_TRACK; 4161 if(es->style & WS_VSCROLL) 4162 dy = pos - es->y_offset; 4163 else 4164 { 4165 /* Assume default scroll range 0-100 */ 4166 INT vlc, new_y; 4167 /* Sanity check */ 4168 if(pos < 0 || pos > 100) return 0; 4169 vlc = get_vertical_line_count(es); 4170 new_y = pos * (es->line_count - vlc) / 100; 4171 dy = es->line_count ? (new_y - es->y_offset) : 0; 4172 TRACE("line_count=%d, y_offset=%d, pos=%d, dy = %d\n", 4173 es->line_count, es->y_offset, pos, dy); 4174 } 4175 break; 4176 case SB_THUMBPOSITION: 4177 TRACE("SB_THUMBPOSITION %d\n", pos); 4178 es->flags &= ~EF_VSCROLL_TRACK; 4179 if(es->style & WS_VSCROLL) 4180 dy = pos - es->y_offset; 4181 else 4182 { 4183 /* Assume default scroll range 0-100 */ 4184 INT vlc, new_y; 4185 /* Sanity check */ 4186 if(pos < 0 || pos > 100) return 0; 4187 vlc = get_vertical_line_count(es); 4188 new_y = pos * (es->line_count - vlc) / 100; 4189 dy = es->line_count ? (new_y - es->y_offset) : 0; 4190 TRACE("line_count=%d, y_offset=%d, pos=%d, dy = %d\n", 4191 es->line_count, es->y_offset, pos, dy); 4192 } 4193 if (!dy) 4194 { 4195 /* force scroll info update */ 4196 EDIT_UpdateScrollInfo(es); 4197 notify_parent(es, EN_VSCROLL); 4198 } 4199 break; 4200 case SB_ENDSCROLL: 4201 TRACE("SB_ENDSCROLL\n"); 4202 break; 4203 /* 4204 * FIXME : the next two are undocumented ! 4205 * Are we doing the right thing ? 4206 * At least Win 3.1 Notepad makes use of EM_GETTHUMB this way, 4207 * although it's also a regular control message. 4208 */ 4209 case EM_GETTHUMB: /* this one is used by NT notepad */ 4210 { 4211 LRESULT ret; 4212 if(GetWindowLongW( es->hwndSelf, GWL_STYLE ) & WS_VSCROLL) 4213 ret = GetScrollPos(es->hwndSelf, SB_VERT); 4214 else 4215 { 4216 /* Assume default scroll range 0-100 */ 4217 INT vlc = get_vertical_line_count(es); 4218 ret = es->line_count ? es->y_offset * 100 / (es->line_count - vlc) : 0; 4219 } 4220 TRACE("EM_GETTHUMB: returning %ld\n", ret); 4221 return ret; 4222 } 4223 case EM_LINESCROLL: 4224 TRACE("EM_LINESCROLL %d\n", pos); 4225 dy = pos; 4226 break; 4227 4228 default: 4229 ERR("undocumented WM_VSCROLL action %d (0x%04x), please report\n", 4230 action, action); 4231 return 0; 4232 } 4233 if (dy) 4234 EDIT_EM_LineScroll(es, 0, dy); 4235 return 0; 4236 } 4237 4238 /********************************************************************* 4239 * 4240 * EM_GETTHUMB 4241 * 4242 * FIXME: is this right ? (or should it be only VSCROLL) 4243 * (and maybe only for edit controls that really have their 4244 * own scrollbars) (and maybe only for multiline controls ?) 4245 * All in all: very poorly documented 4246 * 4247 */ 4248 static LRESULT EDIT_EM_GetThumb(EDITSTATE *es) 4249 { 4250 return MAKELONG(EDIT_WM_VScroll(es, EM_GETTHUMB, 0), 4251 EDIT_WM_HScroll(es, EM_GETTHUMB, 0)); 4252 } 4253 4254 4255 /******************************************************************** 4256 * 4257 * The Following code is to handle inline editing from IMEs 4258 */ 4259 4260 static void EDIT_GetCompositionStr(HIMC hIMC, LPARAM CompFlag, EDITSTATE *es) 4261 { 4262 LONG buflen; 4263 LPWSTR lpCompStr; 4264 LPSTR lpCompStrAttr = NULL; 4265 DWORD dwBufLenAttr; 4266 4267 buflen = ImmGetCompositionStringW(hIMC, GCS_COMPSTR, NULL, 0); 4268 4269 if (buflen < 0) 4270 { 4271 return; 4272 } 4273 4274 lpCompStr = heap_alloc(buflen); 4275 if (!lpCompStr) 4276 { 4277 ERR("Unable to allocate IME CompositionString\n"); 4278 return; 4279 } 4280 4281 if (buflen) 4282 ImmGetCompositionStringW(hIMC, GCS_COMPSTR, lpCompStr, buflen); 4283 4284 if (CompFlag & GCS_COMPATTR) 4285 { 4286 /* 4287 * We do not use the attributes yet. it would tell us what characters 4288 * are in transition and which are converted or decided upon 4289 */ 4290 dwBufLenAttr = ImmGetCompositionStringW(hIMC, GCS_COMPATTR, NULL, 0); 4291 if (dwBufLenAttr) 4292 { 4293 dwBufLenAttr ++; 4294 lpCompStrAttr = heap_alloc(dwBufLenAttr + 1); 4295 if (!lpCompStrAttr) 4296 { 4297 ERR("Unable to allocate IME Attribute String\n"); 4298 heap_free(lpCompStr); 4299 return; 4300 } 4301 ImmGetCompositionStringW(hIMC,GCS_COMPATTR, lpCompStrAttr, 4302 dwBufLenAttr); 4303 lpCompStrAttr[dwBufLenAttr] = 0; 4304 } 4305 } 4306 4307 #ifndef __REACTOS__ /* We don't use internal composition string. Rely on the composition window */ 4308 /* check for change in composition start */ 4309 if (es->selection_end < es->composition_start) 4310 es->composition_start = es->selection_end; 4311 4312 /* replace existing selection string */ 4313 es->selection_start = es->composition_start; 4314 4315 if (es->composition_len > 0) 4316 es->selection_end = es->composition_start + es->composition_len; 4317 else 4318 es->selection_end = es->selection_start; 4319 4320 EDIT_EM_ReplaceSel(es, FALSE, lpCompStr, buflen / sizeof(WCHAR), TRUE, TRUE); 4321 es->composition_len = abs(es->composition_start - es->selection_end); 4322 4323 es->selection_start = es->composition_start; 4324 es->selection_end = es->selection_start + es->composition_len; 4325 #endif 4326 4327 heap_free(lpCompStrAttr); 4328 heap_free(lpCompStr); 4329 } 4330 4331 static void EDIT_GetResultStr(HIMC hIMC, EDITSTATE *es) 4332 { 4333 LONG buflen; 4334 LPWSTR lpResultStr; 4335 4336 buflen = ImmGetCompositionStringW(hIMC, GCS_RESULTSTR, NULL, 0); 4337 if (buflen <= 0) 4338 { 4339 return; 4340 } 4341 4342 lpResultStr = heap_alloc(buflen); 4343 if (!lpResultStr) 4344 { 4345 ERR("Unable to alloc buffer for IME string\n"); 4346 return; 4347 } 4348 4349 ImmGetCompositionStringW(hIMC, GCS_RESULTSTR, lpResultStr, buflen); 4350 4351 #ifndef __REACTOS__ 4352 /* check for change in composition start */ 4353 if (es->selection_end < es->composition_start) 4354 es->composition_start = es->selection_end; 4355 4356 es->selection_start = es->composition_start; 4357 es->selection_end = es->composition_start + es->composition_len; 4358 EDIT_EM_ReplaceSel(es, TRUE, lpResultStr, buflen / sizeof(WCHAR), TRUE, TRUE); 4359 es->composition_start = es->selection_end; 4360 es->composition_len = 0; 4361 #endif 4362 4363 heap_free(lpResultStr); 4364 } 4365 4366 static void EDIT_ImeComposition(HWND hwnd, LPARAM CompFlag, EDITSTATE *es) 4367 { 4368 HIMC hIMC; 4369 int cursor; 4370 4371 #ifdef __REACTOS__ 4372 if (es->selection_start != es->selection_end) 4373 EDIT_EM_ReplaceSel(es, TRUE, NULL, 0, TRUE, TRUE); 4374 #else 4375 if (es->composition_len == 0 && es->selection_start != es->selection_end) 4376 { 4377 EDIT_EM_ReplaceSel(es, TRUE, NULL, 0, TRUE, TRUE); 4378 es->composition_start = es->selection_end; 4379 } 4380 #endif 4381 4382 hIMC = ImmGetContext(hwnd); 4383 if (!hIMC) 4384 return; 4385 4386 if (CompFlag & GCS_RESULTSTR) 4387 { 4388 EDIT_GetResultStr(hIMC, es); 4389 cursor = 0; 4390 } 4391 else 4392 { 4393 if (CompFlag & GCS_COMPSTR) 4394 EDIT_GetCompositionStr(hIMC, CompFlag, es); 4395 #ifdef __REACTOS__ 4396 cursor = 0; 4397 #else 4398 cursor = ImmGetCompositionStringW(hIMC, GCS_CURSORPOS, 0, 0); 4399 #endif 4400 } 4401 ImmReleaseContext(hwnd, hIMC); 4402 EDIT_SetCaretPos(es, es->selection_start + cursor, es->flags & EF_AFTER_WRAP); 4403 } 4404 4405 4406 /********************************************************************* 4407 * 4408 * WM_NCCREATE 4409 * 4410 * See also EDIT_WM_StyleChanged 4411 */ 4412 static LRESULT EDIT_WM_NCCreate(HWND hwnd, LPCREATESTRUCTW lpcs) 4413 { 4414 EDITSTATE *es; 4415 UINT alloc_size; 4416 4417 TRACE("Creating edit control, style = %08x\n", lpcs->style); 4418 4419 if (!(es = heap_alloc_zero(sizeof(*es)))) 4420 return FALSE; 4421 SetWindowLongPtrW( hwnd, 0, (LONG_PTR)es ); 4422 4423 /* 4424 * Note: since the EDITSTATE has not been fully initialized yet, 4425 * we can't use any API calls that may send 4426 * WM_XXX messages before WM_NCCREATE is completed. 4427 */ 4428 4429 es->style = lpcs->style; 4430 4431 es->bEnableState = !(es->style & WS_DISABLED); 4432 4433 es->hwndSelf = hwnd; 4434 /* Save parent, which will be notified by EN_* messages */ 4435 es->hwndParent = lpcs->hwndParent; 4436 4437 if (es->style & ES_COMBO) 4438 es->hwndListBox = GetDlgItem(es->hwndParent, ID_CB_LISTBOX); 4439 4440 /* FIXME: should we handle changes to WS_EX_RIGHT style after creation? */ 4441 if (lpcs->dwExStyle & WS_EX_RIGHT) es->style |= ES_RIGHT; 4442 4443 /* Number overrides lowercase overrides uppercase (at least it 4444 * does in Win95). However I'll bet that ES_NUMBER would be 4445 * invalid under Win 3.1. 4446 */ 4447 if (es->style & ES_NUMBER) { 4448 ; /* do not override the ES_NUMBER */ 4449 } else if (es->style & ES_LOWERCASE) { 4450 es->style &= ~ES_UPPERCASE; 4451 } 4452 if (es->style & ES_MULTILINE) { 4453 es->buffer_limit = BUFLIMIT_INITIAL; 4454 if (es->style & WS_VSCROLL) 4455 es->style |= ES_AUTOVSCROLL; 4456 if (es->style & WS_HSCROLL) 4457 es->style |= ES_AUTOHSCROLL; 4458 es->style &= ~ES_PASSWORD; 4459 if ((es->style & ES_CENTER) || (es->style & ES_RIGHT)) { 4460 /* Confirmed - RIGHT overrides CENTER */ 4461 if (es->style & ES_RIGHT) 4462 es->style &= ~ES_CENTER; 4463 es->style &= ~WS_HSCROLL; 4464 es->style &= ~ES_AUTOHSCROLL; 4465 } 4466 } else { 4467 es->buffer_limit = BUFLIMIT_INITIAL; 4468 if ((es->style & ES_RIGHT) && (es->style & ES_CENTER)) 4469 es->style &= ~ES_CENTER; 4470 es->style &= ~WS_HSCROLL; 4471 es->style &= ~WS_VSCROLL; 4472 if (es->style & ES_PASSWORD) 4473 es->password_char = '*'; 4474 } 4475 4476 alloc_size = ROUND_TO_GROW((es->buffer_size + 1) * sizeof(WCHAR)); 4477 if(!(es->hloc32W = LocalAlloc(LMEM_MOVEABLE | LMEM_ZEROINIT, alloc_size))) 4478 goto cleanup; 4479 es->buffer_size = LocalSize(es->hloc32W)/sizeof(WCHAR) - 1; 4480 4481 if (!(es->undo_text = heap_alloc_zero((es->buffer_size + 1) * sizeof(WCHAR)))) 4482 goto cleanup; 4483 es->undo_buffer_size = es->buffer_size; 4484 4485 if (es->style & ES_MULTILINE) 4486 if (!(es->first_line_def = heap_alloc_zero(sizeof(LINEDEF)))) 4487 goto cleanup; 4488 es->line_count = 1; 4489 4490 /* 4491 * In Win95 look and feel, the WS_BORDER style is replaced by the 4492 * WS_EX_CLIENTEDGE style for the edit control. This gives the edit 4493 * control a nonclient area so we don't need to draw the border. 4494 * If WS_BORDER without WS_EX_CLIENTEDGE is specified we shouldn't have 4495 * a nonclient area and we should handle painting the border ourselves. 4496 * 4497 * When making modifications please ensure that the code still works 4498 * for edit controls created directly with style 0x50800000, exStyle 0 4499 * (which should have a single pixel border) 4500 */ 4501 if (lpcs->dwExStyle & WS_EX_CLIENTEDGE) 4502 es->style &= ~WS_BORDER; 4503 else if (es->style & WS_BORDER) 4504 SetWindowLongW(hwnd, GWL_STYLE, es->style & ~WS_BORDER); 4505 4506 return TRUE; 4507 4508 cleanup: 4509 SetWindowLongPtrW(es->hwndSelf, 0, 0); 4510 EDIT_InvalidateUniscribeData(es); 4511 heap_free(es->first_line_def); 4512 heap_free(es->undo_text); 4513 if (es->hloc32W) LocalFree(es->hloc32W); 4514 heap_free(es->logAttr); 4515 heap_free(es); 4516 return FALSE; 4517 } 4518 4519 4520 /********************************************************************* 4521 * 4522 * WM_CREATE 4523 * 4524 */ 4525 static LRESULT EDIT_WM_Create(EDITSTATE *es, const WCHAR *name) 4526 { 4527 RECT clientRect; 4528 4529 TRACE("%s\n", debugstr_w(name)); 4530 4531 /* 4532 * To initialize some final structure members, we call some helper 4533 * functions. However, since the EDITSTATE is not consistent (i.e. 4534 * not fully initialized), we should be very careful which 4535 * functions can be called, and in what order. 4536 */ 4537 EDIT_WM_SetFont(es, 0, FALSE); 4538 EDIT_EM_EmptyUndoBuffer(es); 4539 4540 /* We need to calculate the format rect 4541 (applications may send EM_SETMARGINS before the control gets visible) */ 4542 GetClientRect(es->hwndSelf, &clientRect); 4543 EDIT_SetRectNP(es, &clientRect); 4544 4545 if (name && *name) 4546 { 4547 EDIT_EM_ReplaceSel(es, FALSE, name, strlenW(name), FALSE, FALSE); 4548 /* if we insert text to the editline, the text scrolls out 4549 * of the window, as the caret is placed after the insert 4550 * pos normally; thus we reset es->selection... to 0 and 4551 * update caret 4552 */ 4553 es->selection_start = es->selection_end = 0; 4554 /* Adobe Photoshop does NOT like this. and MSDN says that EN_CHANGE 4555 * Messages are only to be sent when the USER does something to 4556 * change the contents. So I am removing this EN_CHANGE 4557 * 4558 * EDIT_NOTIFY_PARENT(es, EN_CHANGE); 4559 */ 4560 EDIT_EM_ScrollCaret(es); 4561 } 4562 4563 /* force scroll info update */ 4564 EDIT_UpdateScrollInfo(es); 4565 OpenThemeData(es->hwndSelf, WC_EDITW); 4566 4567 /* The rule seems to return 1 here for success */ 4568 /* Power Builder masked edit controls will crash */ 4569 /* if not. */ 4570 /* FIXME: is that in all cases so ? */ 4571 return 1; 4572 } 4573 4574 4575 /********************************************************************* 4576 * 4577 * WM_NCDESTROY 4578 * 4579 */ 4580 static LRESULT EDIT_WM_NCDestroy(EDITSTATE *es) 4581 { 4582 LINEDEF *pc, *pp; 4583 HTHEME theme; 4584 4585 theme = GetWindowTheme(es->hwndSelf); 4586 CloseThemeData(theme); 4587 4588 /* The app can own the text buffer handle */ 4589 if (es->hloc32W && (es->hloc32W != es->hlocapp)) 4590 LocalFree(es->hloc32W); 4591 4592 EDIT_InvalidateUniscribeData(es); 4593 4594 pc = es->first_line_def; 4595 while (pc) 4596 { 4597 pp = pc->next; 4598 heap_free(pc); 4599 pc = pp; 4600 } 4601 4602 SetWindowLongPtrW( es->hwndSelf, 0, 0 ); 4603 heap_free(es->undo_text); 4604 heap_free(es); 4605 4606 return 0; 4607 } 4608 4609 static LRESULT CALLBACK EDIT_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) 4610 { 4611 EDITSTATE *es = (EDITSTATE *)GetWindowLongPtrW(hwnd, 0); 4612 HTHEME theme = GetWindowTheme(hwnd); 4613 LRESULT result = 0; 4614 RECT *rect; 4615 4616 TRACE("hwnd=%p msg=%#x wparam=%lx lparam=%lx\n", hwnd, msg, wParam, lParam); 4617 4618 if (!es && msg != WM_NCCREATE) 4619 return DefWindowProcW(hwnd, msg, wParam, lParam); 4620 4621 if (es && (msg != WM_NCDESTROY)) 4622 EDIT_LockBuffer(es); 4623 4624 switch (msg) 4625 { 4626 case EM_GETSEL: 4627 result = EDIT_EM_GetSel(es, (UINT *)wParam, (UINT *)lParam); 4628 break; 4629 4630 case EM_SETSEL: 4631 EDIT_EM_SetSel(es, wParam, lParam, FALSE); 4632 EDIT_EM_ScrollCaret(es); 4633 result = 1; 4634 break; 4635 4636 case EM_GETRECT: 4637 rect = (RECT *)lParam; 4638 if (rect) 4639 *rect = es->format_rect; 4640 break; 4641 4642 case EM_SETRECT: 4643 if ((es->style & ES_MULTILINE) && lParam) 4644 { 4645 EDIT_SetRectNP(es, (RECT *)lParam); 4646 EDIT_UpdateText(es, NULL, TRUE); 4647 } 4648 break; 4649 4650 case EM_SETRECTNP: 4651 if ((es->style & ES_MULTILINE) && lParam) 4652 EDIT_SetRectNP(es, (LPRECT)lParam); 4653 break; 4654 4655 case EM_SCROLL: 4656 result = EDIT_EM_Scroll(es, (INT)wParam); 4657 break; 4658 4659 case EM_LINESCROLL: 4660 result = (LRESULT)EDIT_EM_LineScroll(es, (INT)wParam, (INT)lParam); 4661 break; 4662 4663 case EM_SCROLLCARET: 4664 EDIT_EM_ScrollCaret(es); 4665 result = 1; 4666 break; 4667 4668 case EM_GETMODIFY: 4669 result = ((es->flags & EF_MODIFIED) != 0); 4670 break; 4671 4672 case EM_SETMODIFY: 4673 if (wParam) 4674 es->flags |= EF_MODIFIED; 4675 else 4676 es->flags &= ~(EF_MODIFIED | EF_UPDATE); /* reset pending updates */ 4677 break; 4678 4679 case EM_GETLINECOUNT: 4680 result = (es->style & ES_MULTILINE) ? es->line_count : 1; 4681 break; 4682 4683 case EM_LINEINDEX: 4684 result = (LRESULT)EDIT_EM_LineIndex(es, (INT)wParam); 4685 break; 4686 4687 case EM_SETHANDLE: 4688 EDIT_EM_SetHandle(es, (HLOCAL)wParam); 4689 break; 4690 4691 case EM_GETHANDLE: 4692 result = (LRESULT)EDIT_EM_GetHandle(es); 4693 break; 4694 4695 case EM_GETTHUMB: 4696 result = EDIT_EM_GetThumb(es); 4697 break; 4698 4699 /* these messages missing from specs */ 4700 case 0x00bf: 4701 case 0x00c0: 4702 case 0x00c3: 4703 case 0x00ca: 4704 FIXME("undocumented message 0x%x, please report\n", msg); 4705 result = DefWindowProcW(hwnd, msg, wParam, lParam); 4706 break; 4707 4708 case EM_LINELENGTH: 4709 result = (LRESULT)EDIT_EM_LineLength(es, (INT)wParam); 4710 break; 4711 4712 case EM_REPLACESEL: 4713 { 4714 const WCHAR *textW = (const WCHAR *)lParam; 4715 4716 EDIT_EM_ReplaceSel(es, (BOOL)wParam, textW, strlenW(textW), TRUE, TRUE); 4717 result = 1; 4718 break; 4719 } 4720 4721 case EM_GETLINE: 4722 result = (LRESULT)EDIT_EM_GetLine(es, (INT)wParam, (LPWSTR)lParam); 4723 break; 4724 4725 case EM_SETLIMITTEXT: 4726 EDIT_EM_SetLimitText(es, wParam); 4727 break; 4728 4729 case EM_CANUNDO: 4730 result = (LRESULT)EDIT_EM_CanUndo(es); 4731 break; 4732 4733 case EM_UNDO: 4734 case WM_UNDO: 4735 result = (LRESULT)EDIT_EM_Undo(es); 4736 break; 4737 4738 case EM_FMTLINES: 4739 result = (LRESULT)EDIT_EM_FmtLines(es, (BOOL)wParam); 4740 break; 4741 4742 case EM_LINEFROMCHAR: 4743 result = (LRESULT)EDIT_EM_LineFromChar(es, (INT)wParam); 4744 break; 4745 4746 case EM_SETTABSTOPS: 4747 result = (LRESULT)EDIT_EM_SetTabStops(es, (INT)wParam, (LPINT)lParam); 4748 break; 4749 4750 case EM_SETPASSWORDCHAR: 4751 EDIT_EM_SetPasswordChar(es, wParam); 4752 break; 4753 4754 case EM_EMPTYUNDOBUFFER: 4755 EDIT_EM_EmptyUndoBuffer(es); 4756 break; 4757 4758 case EM_GETFIRSTVISIBLELINE: 4759 result = (es->style & ES_MULTILINE) ? es->y_offset : es->x_offset; 4760 break; 4761 4762 case EM_SETREADONLY: 4763 { 4764 DWORD old_style = es->style; 4765 4766 if (wParam) 4767 { 4768 SetWindowLongW(hwnd, GWL_STYLE, GetWindowLongW(hwnd, GWL_STYLE) | ES_READONLY); 4769 es->style |= ES_READONLY; 4770 } 4771 else 4772 { 4773 SetWindowLongW(hwnd, GWL_STYLE, GetWindowLongW(hwnd, GWL_STYLE) & ~ES_READONLY); 4774 es->style &= ~ES_READONLY; 4775 } 4776 4777 if (old_style ^ es->style) 4778 InvalidateRect(es->hwndSelf, NULL, TRUE); 4779 4780 result = 1; 4781 break; 4782 } 4783 4784 case EM_SETWORDBREAKPROC: 4785 EDIT_EM_SetWordBreakProc(es, (void *)lParam); 4786 result = 1; 4787 break; 4788 4789 case EM_GETWORDBREAKPROC: 4790 result = (LRESULT)es->word_break_proc; 4791 break; 4792 4793 case EM_GETPASSWORDCHAR: 4794 result = es->password_char; 4795 break; 4796 4797 case EM_SETMARGINS: 4798 EDIT_EM_SetMargins(es, (INT)wParam, LOWORD(lParam), HIWORD(lParam), TRUE); 4799 break; 4800 4801 case EM_GETMARGINS: 4802 result = MAKELONG(es->left_margin, es->right_margin); 4803 break; 4804 4805 case EM_GETLIMITTEXT: 4806 result = es->buffer_limit; 4807 break; 4808 4809 case EM_POSFROMCHAR: 4810 if ((INT)wParam >= get_text_length(es)) result = -1; 4811 else result = EDIT_EM_PosFromChar(es, (INT)wParam, FALSE); 4812 break; 4813 4814 case EM_CHARFROMPOS: 4815 result = EDIT_EM_CharFromPos(es, (short)LOWORD(lParam), (short)HIWORD(lParam)); 4816 break; 4817 4818 /* End of the EM_ messages which were in numerical order; what order 4819 * are these in? vaguely alphabetical? 4820 */ 4821 4822 case WM_NCCREATE: 4823 result = EDIT_WM_NCCreate(hwnd, (LPCREATESTRUCTW)lParam); 4824 break; 4825 4826 case WM_NCDESTROY: 4827 result = EDIT_WM_NCDestroy(es); 4828 es = NULL; 4829 break; 4830 4831 case WM_GETDLGCODE: 4832 result = DLGC_HASSETSEL | DLGC_WANTCHARS | DLGC_WANTARROWS; 4833 4834 if (es->style & ES_MULTILINE) 4835 result |= DLGC_WANTALLKEYS; 4836 4837 if (lParam) 4838 { 4839 MSG *msg = (MSG *)lParam; 4840 es->flags |= EF_DIALOGMODE; 4841 4842 if (msg->message == WM_KEYDOWN) 4843 { 4844 int vk = (int)msg->wParam; 4845 4846 if (es->hwndListBox) 4847 { 4848 if (vk == VK_RETURN || vk == VK_ESCAPE) 4849 if (SendMessageW(GetParent(hwnd), CB_GETDROPPEDSTATE, 0, 0)) 4850 result |= DLGC_WANTMESSAGE; 4851 } 4852 } 4853 } 4854 break; 4855 4856 case WM_IME_CHAR: 4857 #ifdef __REACTOS__ 4858 result = DefWindowProcW(hwnd, msg, wParam, lParam); 4859 break; 4860 #endif 4861 case WM_CHAR: 4862 { 4863 WCHAR charW = wParam; 4864 4865 if (es->hwndListBox) 4866 { 4867 if (charW == VK_RETURN || charW == VK_ESCAPE) 4868 { 4869 if (SendMessageW(GetParent(hwnd), CB_GETDROPPEDSTATE, 0, 0)) 4870 SendMessageW(GetParent(hwnd), WM_KEYDOWN, charW, 0); 4871 break; 4872 } 4873 } 4874 result = EDIT_WM_Char(es, charW); 4875 break; 4876 } 4877 4878 case WM_UNICHAR: 4879 if (wParam == UNICODE_NOCHAR) return TRUE; 4880 if (wParam <= 0x000fffff) 4881 { 4882 if (wParam > 0xffff) /* convert to surrogates */ 4883 { 4884 wParam -= 0x10000; 4885 EDIT_WM_Char(es, (wParam >> 10) + 0xd800); 4886 EDIT_WM_Char(es, (wParam & 0x03ff) + 0xdc00); 4887 } 4888 else 4889 EDIT_WM_Char(es, wParam); 4890 } 4891 return 0; 4892 4893 case WM_CLEAR: 4894 EDIT_WM_Clear(es); 4895 break; 4896 4897 case WM_CONTEXTMENU: 4898 EDIT_WM_ContextMenu(es, (short)LOWORD(lParam), (short)HIWORD(lParam)); 4899 break; 4900 4901 case WM_COPY: 4902 EDIT_WM_Copy(es); 4903 break; 4904 4905 case WM_CREATE: 4906 result = EDIT_WM_Create(es, ((LPCREATESTRUCTW)lParam)->lpszName); 4907 break; 4908 4909 case WM_CUT: 4910 EDIT_WM_Cut(es); 4911 break; 4912 4913 case WM_ENABLE: 4914 es->bEnableState = (BOOL) wParam; 4915 EDIT_UpdateText(es, NULL, TRUE); 4916 if (theme) 4917 RedrawWindow(hwnd, NULL, NULL, RDW_FRAME | RDW_INVALIDATE | RDW_UPDATENOW); 4918 break; 4919 4920 case WM_ERASEBKGND: 4921 /* we do the proper erase in EDIT_WM_Paint */ 4922 result = 1; 4923 break; 4924 4925 case WM_GETFONT: 4926 result = (LRESULT)es->font; 4927 break; 4928 4929 case WM_GETTEXT: 4930 result = (LRESULT)EDIT_WM_GetText(es, (INT)wParam, (LPWSTR)lParam); 4931 break; 4932 4933 case WM_GETTEXTLENGTH: 4934 result = get_text_length(es); 4935 break; 4936 4937 case WM_HSCROLL: 4938 result = EDIT_WM_HScroll(es, LOWORD(wParam), (short)HIWORD(wParam)); 4939 break; 4940 4941 case WM_KEYDOWN: 4942 result = EDIT_WM_KeyDown(es, (INT)wParam); 4943 break; 4944 4945 case WM_KILLFOCUS: 4946 result = EDIT_WM_KillFocus(theme, es); 4947 break; 4948 4949 case WM_LBUTTONDBLCLK: 4950 result = EDIT_WM_LButtonDblClk(es); 4951 break; 4952 4953 case WM_LBUTTONDOWN: 4954 result = EDIT_WM_LButtonDown(es, wParam, (short)LOWORD(lParam), (short)HIWORD(lParam)); 4955 break; 4956 4957 case WM_LBUTTONUP: 4958 result = EDIT_WM_LButtonUp(es); 4959 break; 4960 4961 case WM_MBUTTONDOWN: 4962 result = EDIT_WM_MButtonDown(es); 4963 break; 4964 4965 case WM_MOUSEMOVE: 4966 result = EDIT_WM_MouseMove(es, (short)LOWORD(lParam), (short)HIWORD(lParam)); 4967 break; 4968 4969 case WM_PRINTCLIENT: 4970 case WM_PAINT: 4971 EDIT_WM_Paint(es, (HDC)wParam); 4972 break; 4973 4974 case WM_NCPAINT: 4975 EDIT_WM_NCPaint(hwnd, (HRGN)wParam); 4976 break; 4977 4978 case WM_PASTE: 4979 EDIT_WM_Paste(es); 4980 break; 4981 4982 case WM_SETFOCUS: 4983 EDIT_WM_SetFocus(theme, es); 4984 break; 4985 4986 case WM_SETFONT: 4987 EDIT_WM_SetFont(es, (HFONT)wParam, LOWORD(lParam) != 0); 4988 break; 4989 4990 case WM_SETREDRAW: 4991 /* FIXME: actually set an internal flag and behave accordingly */ 4992 break; 4993 4994 case WM_SETTEXT: 4995 EDIT_WM_SetText(es, (const WCHAR *)lParam); 4996 result = TRUE; 4997 break; 4998 4999 case WM_SIZE: 5000 EDIT_WM_Size(es, (UINT)wParam); 5001 break; 5002 5003 case WM_STYLECHANGED: 5004 result = EDIT_WM_StyleChanged(es, wParam, (const STYLESTRUCT *)lParam); 5005 break; 5006 5007 case WM_STYLECHANGING: 5008 result = 0; /* See EDIT_WM_StyleChanged */ 5009 break; 5010 5011 case WM_SYSKEYDOWN: 5012 result = EDIT_WM_SysKeyDown(es, (INT)wParam, (DWORD)lParam); 5013 break; 5014 5015 case WM_TIMER: 5016 EDIT_WM_Timer(es); 5017 break; 5018 5019 case WM_VSCROLL: 5020 result = EDIT_WM_VScroll(es, LOWORD(wParam), (short)HIWORD(wParam)); 5021 break; 5022 5023 case WM_MOUSEWHEEL: 5024 { 5025 int wheelDelta; 5026 UINT pulScrollLines = 3; 5027 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0); 5028 5029 if (wParam & (MK_SHIFT | MK_CONTROL)) 5030 { 5031 result = DefWindowProcW(hwnd, msg, wParam, lParam); 5032 break; 5033 } 5034 5035 wheelDelta = GET_WHEEL_DELTA_WPARAM(wParam); 5036 /* if scrolling changes direction, ignore left overs */ 5037 if ((wheelDelta < 0 && es->wheelDeltaRemainder < 0) || 5038 (wheelDelta > 0 && es->wheelDeltaRemainder > 0)) 5039 es->wheelDeltaRemainder += wheelDelta; 5040 else 5041 es->wheelDeltaRemainder = wheelDelta; 5042 5043 if (es->wheelDeltaRemainder && pulScrollLines) 5044 { 5045 int cLineScroll; 5046 pulScrollLines = (int) min((UINT) es->line_count, pulScrollLines); 5047 cLineScroll = pulScrollLines * (float)es->wheelDeltaRemainder / WHEEL_DELTA; 5048 es->wheelDeltaRemainder -= WHEEL_DELTA * cLineScroll / (int)pulScrollLines; 5049 result = EDIT_EM_LineScroll(es, 0, -cLineScroll); 5050 } 5051 break; 5052 } 5053 5054 /* IME messages to make the edit control IME aware */ 5055 case WM_IME_SETCONTEXT: 5056 #ifdef __REACTOS__ 5057 if (FALSE) /* FIXME: Condition */ 5058 lParam &= ~ISC_SHOWUICOMPOSITIONWINDOW; 5059 5060 if (wParam) 5061 { 5062 HIMC hIMC = ImmGetContext(hwnd); 5063 LPINPUTCONTEXTDX pIC = (LPINPUTCONTEXTDX)ImmLockIMC(hIMC); 5064 if (pIC) 5065 { 5066 pIC->dwUIFlags &= ~0x40000; 5067 ImmUnlockIMC(hIMC); 5068 } 5069 if (FALSE) /* FIXME: Condition */ 5070 ImmNotifyIME(hIMC, NI_COMPOSITIONSTR, CPS_CANCEL, 0); 5071 ImmReleaseContext(hwnd, hIMC); 5072 } 5073 5074 result = DefWindowProcW(hwnd, msg, wParam, lParam); 5075 #endif 5076 break; 5077 5078 case WM_IME_STARTCOMPOSITION: 5079 #ifdef __REACTOS__ 5080 if (FALSE) /* FIXME: Condition */ 5081 return TRUE; 5082 result = DefWindowProcW(hwnd, msg, wParam, lParam); 5083 #else 5084 es->composition_start = es->selection_end; 5085 es->composition_len = 0; 5086 #endif 5087 break; 5088 5089 case WM_IME_COMPOSITION: 5090 EDIT_ImeComposition(hwnd, lParam, es); 5091 #ifdef __REACTOS__ 5092 result = DefWindowProcW(hwnd, msg, wParam, lParam); 5093 #endif 5094 break; 5095 5096 case WM_IME_ENDCOMPOSITION: 5097 #ifdef __REACTOS__ 5098 result = DefWindowProcW(hwnd, msg, wParam, lParam); 5099 #else 5100 if (es->composition_len > 0) 5101 { 5102 EDIT_EM_ReplaceSel(es, TRUE, NULL, 0, TRUE, TRUE); 5103 es->selection_end = es->selection_start; 5104 es->composition_len= 0; 5105 } 5106 #endif 5107 break; 5108 5109 case WM_IME_COMPOSITIONFULL: 5110 break; 5111 5112 case WM_IME_SELECT: 5113 #ifdef __REACTOS__ 5114 result = DefWindowProcW(hwnd, msg, wParam, lParam); 5115 #endif 5116 break; 5117 5118 case WM_IME_CONTROL: 5119 #ifdef __REACTOS__ 5120 result = DefWindowProcW(hwnd, msg, wParam, lParam); 5121 #endif 5122 break; 5123 5124 case WM_IME_REQUEST: 5125 switch (wParam) 5126 { 5127 case IMR_QUERYCHARPOSITION: 5128 { 5129 IMECHARPOSITION *chpos = (IMECHARPOSITION *)lParam; 5130 LRESULT pos; 5131 5132 pos = EDIT_EM_PosFromChar(es, es->selection_start + chpos->dwCharPos, FALSE); 5133 chpos->pt.x = LOWORD(pos); 5134 chpos->pt.y = HIWORD(pos); 5135 chpos->cLineHeight = es->line_height; 5136 chpos->rcDocument = es->format_rect; 5137 MapWindowPoints(hwnd, 0, &chpos->pt, 1); 5138 MapWindowPoints(hwnd, 0, (POINT*)&chpos->rcDocument, 2); 5139 result = 1; 5140 break; 5141 } 5142 } 5143 break; 5144 5145 case WM_THEMECHANGED: 5146 CloseThemeData (theme); 5147 OpenThemeData(hwnd, WC_EDITW); 5148 break; 5149 5150 default: 5151 result = DefWindowProcW(hwnd, msg, wParam, lParam); 5152 break; 5153 } 5154 5155 if (IsWindow(hwnd) && es && msg != EM_GETHANDLE) 5156 EDIT_UnlockBuffer(es, FALSE); 5157 5158 TRACE("hwnd=%p msg=%x -- 0x%08lx\n", hwnd, msg, result); 5159 5160 return result; 5161 } 5162 5163 void EDIT_Register(void) 5164 { 5165 WNDCLASSW wndClass; 5166 5167 memset(&wndClass, 0, sizeof(wndClass)); 5168 wndClass.style = CS_PARENTDC | CS_GLOBALCLASS | CS_DBLCLKS; 5169 wndClass.lpfnWndProc = EDIT_WindowProc; 5170 wndClass.cbClsExtra = 0; 5171 #ifdef __i386__ 5172 wndClass.cbWndExtra = sizeof(EDITSTATE *) + sizeof(WORD); 5173 #else 5174 wndClass.cbWndExtra = sizeof(EDITSTATE *); 5175 #endif 5176 wndClass.hCursor = LoadCursorW(0, (LPWSTR)IDC_IBEAM); 5177 wndClass.hbrBackground = NULL; 5178 wndClass.lpszClassName = WC_EDITW; 5179 RegisterClassW(&wndClass); 5180 } 5181 5182 #ifdef __REACTOS__ 5183 void EDIT_Unregister(void) 5184 { 5185 UnregisterClassW(WC_EDITW, NULL); 5186 } 5187 #endif 5188