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