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