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