1 /* 2 * RichEdit - functions dealing with editor object 3 * 4 * Copyright 2004 by Krzysztof Foltman 5 * Copyright 2005 by Cihan Altinay 6 * Copyright 2005 by Phil Krylov 7 * Copyright 2008 Eric Pouech 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 24 /* 25 API implementation status: 26 27 Messages (ANSI versions not done yet) 28 + EM_AUTOURLDETECT 2.0 29 + EM_CANPASTE 30 + EM_CANREDO 2.0 31 + EM_CANUNDO 32 + EM_CHARFROMPOS 33 - EM_DISPLAYBAND 34 + EM_EMPTYUNDOBUFFER 35 + EM_EXGETSEL 36 + EM_EXLIMITTEXT 37 + EM_EXLINEFROMCHAR 38 + EM_EXSETSEL 39 + EM_FINDTEXT (only FR_DOWN flag implemented) 40 + EM_FINDTEXTEX (only FR_DOWN flag implemented) 41 - EM_FINDWORDBREAK 42 - EM_FMTLINES 43 - EM_FORMATRANGE 44 + EM_GETAUTOURLDETECT 2.0 45 - EM_GETBIDIOPTIONS 3.0 46 - EM_GETCHARFORMAT (partly done) 47 - EM_GETEDITSTYLE 48 + EM_GETEVENTMASK 49 + EM_GETFIRSTVISIBLELINE (can be optimized if needed) 50 - EM_GETIMECOLOR 1.0asian 51 - EM_GETIMECOMPMODE 2.0 52 - EM_GETIMEOPTIONS 1.0asian 53 - EM_GETIMESTATUS 54 - EM_GETLANGOPTIONS 2.0 55 + EM_GETLIMITTEXT 56 + EM_GETLINE 57 + EM_GETLINECOUNT returns number of rows, not of paragraphs 58 + EM_GETMODIFY 59 + EM_GETOLEINTERFACE 60 + EM_GETOPTIONS 61 + EM_GETPARAFORMAT 62 + EM_GETPASSWORDCHAR 2.0 63 - EM_GETPUNCTUATION 1.0asian 64 + EM_GETRECT 65 - EM_GETREDONAME 2.0 66 + EM_GETSEL 67 + EM_GETSELTEXT (ANSI&Unicode) 68 + EM_GETSCROLLPOS 3.0 69 ! - EM_GETTHUMB 70 + EM_GETTEXTEX 2.0 71 + EM_GETTEXTLENGTHEX (GTL_PRECISE unimplemented) 72 + EM_GETTEXTMODE 2.0 73 ? + EM_GETTEXTRANGE (ANSI&Unicode) 74 - EM_GETTYPOGRAPHYOPTIONS 3.0 75 - EM_GETUNDONAME 76 + EM_GETWORDBREAKPROC 77 - EM_GETWORDBREAKPROCEX 78 - EM_GETWORDWRAPMODE 1.0asian 79 + EM_GETZOOM 3.0 80 + EM_HIDESELECTION 81 + EM_LIMITTEXT (Also called EM_SETLIMITTEXT) 82 + EM_LINEFROMCHAR 83 + EM_LINEINDEX 84 + EM_LINELENGTH 85 + EM_LINESCROLL 86 - EM_PASTESPECIAL 87 + EM_POSFROMCHAR 88 + EM_REDO 2.0 89 + EM_REQUESTRESIZE 90 + EM_REPLACESEL (proper style?) ANSI&Unicode 91 + EM_SCROLL 92 + EM_SCROLLCARET 93 + EM_SELECTIONTYPE 94 - EM_SETBIDIOPTIONS 3.0 95 + EM_SETBKGNDCOLOR 96 + EM_SETCHARFORMAT (partly done, no ANSI) 97 - EM_SETEDITSTYLE 98 + EM_SETEVENTMASK (few notifications supported) 99 + EM_SETFONTSIZE 100 - EM_SETIMECOLOR 1.0asian 101 - EM_SETIMEOPTIONS 1.0asian 102 - EM_SETIMESTATUS 103 - EM_SETLANGOPTIONS 2.0 104 - EM_SETLIMITTEXT 105 - EM_SETMARGINS 106 + EM_SETMODIFY (not sure if implementation is correct) 107 - EM_SETOLECALLBACK 108 + EM_SETOPTIONS (partially implemented) 109 - EM_SETPALETTE 2.0 110 + EM_SETPARAFORMAT 111 + EM_SETPASSWORDCHAR 2.0 112 - EM_SETPUNCTUATION 1.0asian 113 + EM_SETREADONLY no beep on modification attempt 114 + EM_SETRECT 115 + EM_SETRECTNP (EM_SETRECT without repainting) 116 + EM_SETSEL 117 + EM_SETSCROLLPOS 3.0 118 - EM_SETTABSTOPS 3.0 119 - EM_SETTARGETDEVICE (partial) 120 + EM_SETTEXTEX 3.0 (proper style?) 121 - EM_SETTEXTMODE 2.0 122 - EM_SETTYPOGRAPHYOPTIONS 3.0 123 + EM_SETUNDOLIMIT 2.0 124 + EM_SETWORDBREAKPROC (used only for word movement at the moment) 125 - EM_SETWORDBREAKPROCEX 126 - EM_SETWORDWRAPMODE 1.0asian 127 + EM_SETZOOM 3.0 128 + EM_SHOWSCROLLBAR 2.0 129 + EM_STOPGROUPTYPING 2.0 130 + EM_STREAMIN 131 + EM_STREAMOUT 132 + EM_UNDO 133 + WM_CHAR 134 + WM_CLEAR 135 + WM_COPY 136 + WM_CUT 137 + WM_GETDLGCODE (the current implementation is incomplete) 138 + WM_GETTEXT (ANSI&Unicode) 139 + WM_GETTEXTLENGTH (ANSI version sucks) 140 + WM_HSCROLL 141 + WM_PASTE 142 + WM_SETFONT 143 + WM_SETTEXT (resets undo stack !) (proper style?) ANSI&Unicode 144 + WM_STYLECHANGING (seems to do nothing) 145 + WM_STYLECHANGED (seems to do nothing) 146 + WM_UNICHAR 147 + WM_VSCROLL 148 149 Notifications 150 151 * EN_CHANGE (sent from the wrong place) 152 - EN_CORRECTTEXT 153 - EN_DROPFILES 154 - EN_ERRSPACE 155 - EN_HSCROLL 156 - EN_IMECHANGE 157 + EN_KILLFOCUS 158 - EN_LINK 159 - EN_MAXTEXT 160 - EN_MSGFILTER 161 - EN_OLEOPFAILED 162 - EN_PROTECTED 163 + EN_REQUESTRESIZE 164 - EN_SAVECLIPBOARD 165 + EN_SELCHANGE 166 + EN_SETFOCUS 167 - EN_STOPNOUNDO 168 * EN_UPDATE (sent from the wrong place) 169 - EN_VSCROLL 170 171 Styles 172 173 - ES_AUTOHSCROLL 174 - ES_AUTOVSCROLL 175 + ES_CENTER 176 + ES_DISABLENOSCROLL (scrollbar is always visible) 177 - ES_EX_NOCALLOLEINIT 178 + ES_LEFT 179 + ES_MULTILINE 180 - ES_NOIME 181 - ES_READONLY (I'm not sure if beeping is the proper behaviour) 182 + ES_RIGHT 183 - ES_SAVESEL 184 - ES_SELFIME 185 - ES_SUNKEN 186 - ES_VERTICAL 187 - ES_WANTRETURN (don't know how to do WM_GETDLGCODE part) 188 - WS_SETFONT 189 + WS_HSCROLL 190 + WS_VSCROLL 191 */ 192 193 /* 194 * RICHED20 TODO (incomplete): 195 * 196 * - messages/styles/notifications listed above 197 * - add remaining CHARFORMAT/PARAFORMAT fields 198 * - right/center align should strip spaces from the beginning 199 * - pictures/OLE objects (not just smiling faces that lack API support ;-) ) 200 * - COM interface (looks like a major pain in the TODO list) 201 * - calculate heights of pictures (half-done) 202 * - hysteresis during wrapping (related to scrollbars appearing/disappearing) 203 * - find/replace 204 * - how to implement EM_FORMATRANGE and EM_DISPLAYBAND ? (Mission Impossible) 205 * - italic caret with italic fonts 206 * - IME 207 * - most notifications aren't sent at all (the most important ones are) 208 * - when should EN_SELCHANGE be sent after text change ? (before/after EN_UPDATE?) 209 * - WM_SETTEXT may use wrong style (but I'm 80% sure it's OK) 210 * - EM_GETCHARFORMAT with SCF_SELECTION may not behave 100% like in original (but very close) 211 * - full justification 212 * - hyphenation 213 * - tables 214 * - ListBox & ComboBox not implemented 215 * 216 * Bugs that are probably fixed, but not so easy to verify: 217 * - EN_UPDATE/EN_CHANGE are handled very incorrectly (should be OK now) 218 * - undo for ME_JoinParagraphs doesn't store paragraph format ? (it does) 219 * - check/fix artificial EOL logic (bCursorAtEnd, hardly logical) 220 * - caret shouldn't be displayed when selection isn't empty 221 * - check refcounting in style management functions (looks perfect now, but no bugs is suspicious) 222 * - undo for setting default format (done, might be buggy) 223 * - styles might be not released properly (looks like they work like charm, but who knows? 224 * 225 */ 226 227 #define NONAMELESSUNION 228 229 #include "editor.h" 230 #include "commdlg.h" 231 #include "winreg.h" 232 #define NO_SHLWAPI_STREAM 233 #include "shlwapi.h" 234 #include "rtf.h" 235 #include "imm.h" 236 #include "res.h" 237 238 #ifdef __REACTOS__ 239 #include <reactos/undocuser.h> 240 #endif 241 242 #define STACK_SIZE_DEFAULT 100 243 #define STACK_SIZE_MAX 1000 244 245 #define TEXT_LIMIT_DEFAULT 32767 246 247 WINE_DEFAULT_DEBUG_CHANNEL(richedit); 248 249 static BOOL ME_RegisterEditorClass(HINSTANCE); 250 static BOOL ME_UpdateLinkAttribute(ME_TextEditor *editor, ME_Cursor *start, int nChars); 251 252 static const WCHAR REListBox20W[] = {'R','E','L','i','s','t','B','o','x','2','0','W', 0}; 253 static const WCHAR REComboBox20W[] = {'R','E','C','o','m','b','o','B','o','x','2','0','W', 0}; 254 static HCURSOR hLeft; 255 256 BOOL me_debug = FALSE; 257 HANDLE me_heap = NULL; 258 259 static BOOL ME_ListBoxRegistered = FALSE; 260 static BOOL ME_ComboBoxRegistered = FALSE; 261 262 static inline BOOL is_version_nt(void) 263 { 264 return !(GetVersion() & 0x80000000); 265 } 266 267 static ME_TextBuffer *ME_MakeText(void) { 268 ME_TextBuffer *buf = heap_alloc(sizeof(*buf)); 269 ME_DisplayItem *p1 = ME_MakeDI(diTextStart); 270 ME_DisplayItem *p2 = ME_MakeDI(diTextEnd); 271 272 p1->prev = NULL; 273 p1->next = p2; 274 p2->prev = p1; 275 p2->next = NULL; 276 p1->member.para.next_para = p2; 277 p2->member.para.prev_para = p1; 278 p2->member.para.nCharOfs = 0; 279 280 buf->pFirst = p1; 281 buf->pLast = p2; 282 buf->pCharStyle = NULL; 283 284 return buf; 285 } 286 287 288 static LRESULT ME_StreamInText(ME_TextEditor *editor, DWORD dwFormat, ME_InStream *stream, ME_Style *style) 289 { 290 WCHAR *pText; 291 LRESULT total_bytes_read = 0; 292 BOOL is_read = FALSE; 293 DWORD cp = CP_ACP, copy = 0; 294 char conv_buf[4 + STREAMIN_BUFFER_SIZE]; /* up to 4 additional UTF-8 bytes */ 295 296 static const char bom_utf8[] = {0xEF, 0xBB, 0xBF}; 297 298 TRACE("%08x %p\n", dwFormat, stream); 299 300 do { 301 LONG nWideChars = 0; 302 WCHAR wszText[STREAMIN_BUFFER_SIZE+1]; 303 304 if (!stream->dwSize) 305 { 306 ME_StreamInFill(stream); 307 if (stream->editstream->dwError) 308 break; 309 if (!stream->dwSize) 310 break; 311 total_bytes_read += stream->dwSize; 312 } 313 314 if (!(dwFormat & SF_UNICODE)) 315 { 316 char * buf = stream->buffer; 317 DWORD size = stream->dwSize, end; 318 319 if (!is_read) 320 { 321 is_read = TRUE; 322 if (stream->dwSize >= 3 && !memcmp(stream->buffer, bom_utf8, 3)) 323 { 324 cp = CP_UTF8; 325 buf += 3; 326 size -= 3; 327 } 328 } 329 330 if (cp == CP_UTF8) 331 { 332 if (copy) 333 { 334 memcpy(conv_buf + copy, buf, size); 335 buf = conv_buf; 336 size += copy; 337 } 338 end = size; 339 while ((buf[end-1] & 0xC0) == 0x80) 340 { 341 --end; 342 --total_bytes_read; /* strange, but seems to match windows */ 343 } 344 if (buf[end-1] & 0x80) 345 { 346 DWORD need = 0; 347 if ((buf[end-1] & 0xE0) == 0xC0) 348 need = 1; 349 if ((buf[end-1] & 0xF0) == 0xE0) 350 need = 2; 351 if ((buf[end-1] & 0xF8) == 0xF0) 352 need = 3; 353 354 if (size - end >= need) 355 { 356 /* we have enough bytes for this sequence */ 357 end = size; 358 } 359 else 360 { 361 /* need more bytes, so don't transcode this sequence */ 362 --end; 363 } 364 } 365 } 366 else 367 end = size; 368 369 nWideChars = MultiByteToWideChar(cp, 0, buf, end, wszText, STREAMIN_BUFFER_SIZE); 370 pText = wszText; 371 372 if (cp == CP_UTF8) 373 { 374 if (end != size) 375 { 376 memcpy(conv_buf, buf + end, size - end); 377 copy = size - end; 378 } 379 } 380 } 381 else 382 { 383 nWideChars = stream->dwSize >> 1; 384 pText = (WCHAR *)stream->buffer; 385 } 386 387 ME_InsertTextFromCursor(editor, 0, pText, nWideChars, style); 388 if (stream->dwSize == 0) 389 break; 390 stream->dwSize = 0; 391 } while(1); 392 return total_bytes_read; 393 } 394 395 static void ME_ApplyBorderProperties(RTF_Info *info, 396 ME_BorderRect *borderRect, 397 RTFBorder *borderDef) 398 { 399 int i, colorNum; 400 ME_Border *pBorders[] = {&borderRect->top, 401 &borderRect->left, 402 &borderRect->bottom, 403 &borderRect->right}; 404 for (i = 0; i < 4; i++) 405 { 406 RTFColor *colorDef = info->colorList; 407 pBorders[i]->width = borderDef[i].width; 408 colorNum = borderDef[i].color; 409 while (colorDef && colorDef->rtfCNum != colorNum) 410 colorDef = colorDef->rtfNextColor; 411 if (colorDef) 412 pBorders[i]->colorRef = RGB( 413 colorDef->rtfCRed >= 0 ? colorDef->rtfCRed : 0, 414 colorDef->rtfCGreen >= 0 ? colorDef->rtfCGreen : 0, 415 colorDef->rtfCBlue >= 0 ? colorDef->rtfCBlue : 0); 416 else 417 pBorders[i]->colorRef = RGB(0, 0, 0); 418 } 419 } 420 421 void ME_RTFCharAttrHook(RTF_Info *info) 422 { 423 CHARFORMAT2W fmt; 424 fmt.cbSize = sizeof(fmt); 425 fmt.dwMask = 0; 426 fmt.dwEffects = 0; 427 428 switch(info->rtfMinor) 429 { 430 case rtfPlain: 431 /* FIXME add more flags once they're implemented */ 432 fmt.dwMask = CFM_BOLD | CFM_ITALIC | CFM_UNDERLINE | CFM_UNDERLINETYPE | CFM_STRIKEOUT | 433 CFM_COLOR | CFM_BACKCOLOR | CFM_SIZE | CFM_WEIGHT; 434 fmt.dwEffects = CFE_AUTOCOLOR | CFE_AUTOBACKCOLOR; 435 fmt.yHeight = 12*20; /* 12pt */ 436 fmt.wWeight = FW_NORMAL; 437 fmt.bUnderlineType = CFU_UNDERLINE; 438 break; 439 case rtfBold: 440 fmt.dwMask = CFM_BOLD | CFM_WEIGHT; 441 fmt.dwEffects = info->rtfParam ? CFE_BOLD : 0; 442 fmt.wWeight = info->rtfParam ? FW_BOLD : FW_NORMAL; 443 break; 444 case rtfItalic: 445 fmt.dwMask = CFM_ITALIC; 446 fmt.dwEffects = info->rtfParam ? fmt.dwMask : 0; 447 break; 448 case rtfUnderline: 449 fmt.dwMask = CFM_UNDERLINETYPE | CFM_UNDERLINE; 450 fmt.bUnderlineType = CFU_UNDERLINE; 451 fmt.dwEffects = info->rtfParam ? CFE_UNDERLINE : 0; 452 break; 453 case rtfDotUnderline: 454 fmt.dwMask = CFM_UNDERLINETYPE | CFM_UNDERLINE; 455 fmt.bUnderlineType = CFU_UNDERLINEDOTTED; 456 fmt.dwEffects = info->rtfParam ? CFE_UNDERLINE : 0; 457 break; 458 case rtfDbUnderline: 459 fmt.dwMask = CFM_UNDERLINETYPE | CFM_UNDERLINE; 460 fmt.bUnderlineType = CFU_UNDERLINEDOUBLE; 461 fmt.dwEffects = info->rtfParam ? CFE_UNDERLINE : 0; 462 break; 463 case rtfWordUnderline: 464 fmt.dwMask = CFM_UNDERLINETYPE | CFM_UNDERLINE; 465 fmt.bUnderlineType = CFU_UNDERLINEWORD; 466 fmt.dwEffects = info->rtfParam ? CFE_UNDERLINE : 0; 467 break; 468 case rtfNoUnderline: 469 fmt.dwMask = CFM_UNDERLINE; 470 fmt.dwEffects = 0; 471 break; 472 case rtfStrikeThru: 473 fmt.dwMask = CFM_STRIKEOUT; 474 fmt.dwEffects = info->rtfParam ? fmt.dwMask : 0; 475 break; 476 case rtfSubScript: 477 case rtfSuperScript: 478 case rtfSubScrShrink: 479 case rtfSuperScrShrink: 480 case rtfNoSuperSub: 481 fmt.dwMask = CFM_SUBSCRIPT|CFM_SUPERSCRIPT; 482 if (info->rtfMinor == rtfSubScrShrink) fmt.dwEffects = CFE_SUBSCRIPT; 483 if (info->rtfMinor == rtfSuperScrShrink) fmt.dwEffects = CFE_SUPERSCRIPT; 484 if (info->rtfMinor == rtfNoSuperSub) fmt.dwEffects = 0; 485 break; 486 case rtfInvisible: 487 fmt.dwMask = CFM_HIDDEN; 488 fmt.dwEffects = info->rtfParam ? fmt.dwMask : 0; 489 break; 490 case rtfBackColor: 491 fmt.dwMask = CFM_BACKCOLOR; 492 fmt.dwEffects = 0; 493 if (info->rtfParam == 0) 494 fmt.dwEffects = CFE_AUTOBACKCOLOR; 495 else if (info->rtfParam != rtfNoParam) 496 { 497 RTFColor *c = RTFGetColor(info, info->rtfParam); 498 if (c && c->rtfCBlue >= 0) 499 fmt.crBackColor = (c->rtfCBlue<<16)|(c->rtfCGreen<<8)|(c->rtfCRed); 500 else 501 fmt.dwEffects = CFE_AUTOBACKCOLOR; 502 } 503 break; 504 case rtfForeColor: 505 fmt.dwMask = CFM_COLOR; 506 fmt.dwEffects = 0; 507 if (info->rtfParam == 0) 508 fmt.dwEffects = CFE_AUTOCOLOR; 509 else if (info->rtfParam != rtfNoParam) 510 { 511 RTFColor *c = RTFGetColor(info, info->rtfParam); 512 if (c && c->rtfCBlue >= 0) 513 fmt.crTextColor = (c->rtfCBlue<<16)|(c->rtfCGreen<<8)|(c->rtfCRed); 514 else { 515 fmt.dwEffects = CFE_AUTOCOLOR; 516 } 517 } 518 break; 519 case rtfFontNum: 520 if (info->rtfParam != rtfNoParam) 521 { 522 RTFFont *f = RTFGetFont(info, info->rtfParam); 523 if (f) 524 { 525 MultiByteToWideChar(CP_ACP, 0, f->rtfFName, -1, fmt.szFaceName, ARRAY_SIZE(fmt.szFaceName)); 526 fmt.szFaceName[ARRAY_SIZE(fmt.szFaceName)-1] = '\0'; 527 fmt.bCharSet = f->rtfFCharSet; 528 fmt.dwMask = CFM_FACE | CFM_CHARSET; 529 fmt.bPitchAndFamily = f->rtfFPitch | (f->rtfFFamily << 4); 530 } 531 } 532 break; 533 case rtfFontSize: 534 fmt.dwMask = CFM_SIZE; 535 if (info->rtfParam != rtfNoParam) 536 fmt.yHeight = info->rtfParam*10; 537 break; 538 } 539 if (fmt.dwMask) { 540 ME_Style *style2; 541 RTFFlushOutputBuffer(info); 542 /* FIXME too slow ? how come ? */ 543 style2 = ME_ApplyStyle(info->editor, info->style, &fmt); 544 ME_ReleaseStyle(info->style); 545 info->style = style2; 546 info->styleChanged = TRUE; 547 } 548 } 549 550 /* FIXME this function doesn't get any information about context of the RTF tag, which is very bad, 551 the same tags mean different things in different contexts */ 552 void ME_RTFParAttrHook(RTF_Info *info) 553 { 554 switch(info->rtfMinor) 555 { 556 case rtfParDef: /* restores default paragraph attributes */ 557 if (!info->editor->bEmulateVersion10) /* v4.1 */ 558 info->borderType = RTFBorderParaLeft; 559 else /* v1.0 - 3.0 */ 560 info->borderType = RTFBorderParaTop; 561 info->fmt.dwMask = PFM_ALIGNMENT | PFM_BORDER | PFM_LINESPACING | PFM_TABSTOPS | 562 PFM_OFFSET | PFM_RIGHTINDENT | PFM_SPACEAFTER | PFM_SPACEBEFORE | 563 PFM_STARTINDENT | PFM_RTLPARA | PFM_NUMBERING | PFM_NUMBERINGSTART | 564 PFM_NUMBERINGSTYLE | PFM_NUMBERINGTAB; 565 /* TODO: shading */ 566 info->fmt.wAlignment = PFA_LEFT; 567 info->fmt.cTabCount = 0; 568 info->fmt.dxOffset = info->fmt.dxStartIndent = info->fmt.dxRightIndent = 0; 569 info->fmt.wBorderWidth = info->fmt.wBorders = 0; 570 info->fmt.wBorderSpace = 0; 571 info->fmt.bLineSpacingRule = 0; 572 info->fmt.dySpaceBefore = info->fmt.dySpaceAfter = 0; 573 info->fmt.dyLineSpacing = 0; 574 info->fmt.wEffects &= ~PFE_RTLPARA; 575 info->fmt.wNumbering = 0; 576 info->fmt.wNumberingStart = 0; 577 info->fmt.wNumberingStyle = 0; 578 info->fmt.wNumberingTab = 0; 579 580 if (!info->editor->bEmulateVersion10) /* v4.1 */ 581 { 582 if (info->tableDef && info->tableDef->tableRowStart && 583 info->tableDef->tableRowStart->member.para.nFlags & MEPF_ROWEND) 584 { 585 ME_Cursor cursor; 586 ME_DisplayItem *para; 587 /* We are just after a table row. */ 588 RTFFlushOutputBuffer(info); 589 cursor = info->editor->pCursors[0]; 590 para = cursor.pPara; 591 if (para == info->tableDef->tableRowStart->member.para.next_para 592 && !cursor.nOffset && !cursor.pRun->member.run.nCharOfs) 593 { 594 /* Since the table row end, no text has been inserted, and the \intbl 595 * control word has not be used. We can confirm that we are not in a 596 * table anymore. 597 */ 598 info->tableDef->tableRowStart = NULL; 599 info->canInheritInTbl = FALSE; 600 } 601 } 602 } else { /* v1.0 - v3.0 */ 603 info->fmt.dwMask |= PFM_TABLE; 604 info->fmt.wEffects &= ~PFE_TABLE; 605 } 606 break; 607 case rtfNestLevel: 608 if (!info->editor->bEmulateVersion10) /* v4.1 */ 609 { 610 while (info->rtfParam > info->nestingLevel) { 611 RTFTable *tableDef = heap_alloc_zero(sizeof(*tableDef)); 612 tableDef->parent = info->tableDef; 613 info->tableDef = tableDef; 614 615 RTFFlushOutputBuffer(info); 616 if (tableDef->tableRowStart && 617 tableDef->tableRowStart->member.para.nFlags & MEPF_ROWEND) 618 { 619 ME_DisplayItem *para = tableDef->tableRowStart; 620 para = para->member.para.next_para; 621 para = ME_InsertTableRowStartAtParagraph(info->editor, para); 622 tableDef->tableRowStart = para; 623 } else { 624 ME_Cursor cursor; 625 WCHAR endl = '\r'; 626 cursor = info->editor->pCursors[0]; 627 if (cursor.nOffset || cursor.pRun->member.run.nCharOfs) 628 ME_InsertTextFromCursor(info->editor, 0, &endl, 1, info->style); 629 tableDef->tableRowStart = ME_InsertTableRowStartFromCursor(info->editor); 630 } 631 632 info->nestingLevel++; 633 } 634 info->canInheritInTbl = FALSE; 635 } 636 break; 637 case rtfInTable: 638 { 639 if (!info->editor->bEmulateVersion10) /* v4.1 */ 640 { 641 if (info->nestingLevel < 1) 642 { 643 RTFTable *tableDef; 644 if (!info->tableDef) 645 info->tableDef = heap_alloc_zero(sizeof(*info->tableDef)); 646 tableDef = info->tableDef; 647 RTFFlushOutputBuffer(info); 648 if (tableDef->tableRowStart && 649 tableDef->tableRowStart->member.para.nFlags & MEPF_ROWEND) 650 { 651 ME_DisplayItem *para = tableDef->tableRowStart; 652 para = para->member.para.next_para; 653 para = ME_InsertTableRowStartAtParagraph(info->editor, para); 654 tableDef->tableRowStart = para; 655 } else { 656 ME_Cursor cursor; 657 WCHAR endl = '\r'; 658 cursor = info->editor->pCursors[0]; 659 if (cursor.nOffset || cursor.pRun->member.run.nCharOfs) 660 ME_InsertTextFromCursor(info->editor, 0, &endl, 1, info->style); 661 tableDef->tableRowStart = ME_InsertTableRowStartFromCursor(info->editor); 662 } 663 info->nestingLevel = 1; 664 info->canInheritInTbl = TRUE; 665 } 666 return; 667 } else { /* v1.0 - v3.0 */ 668 info->fmt.dwMask |= PFM_TABLE; 669 info->fmt.wEffects |= PFE_TABLE; 670 } 671 break; 672 } 673 case rtfFirstIndent: 674 case rtfLeftIndent: 675 if ((info->fmt.dwMask & (PFM_STARTINDENT | PFM_OFFSET)) != (PFM_STARTINDENT | PFM_OFFSET)) 676 { 677 PARAFORMAT2 fmt; 678 fmt.cbSize = sizeof(fmt); 679 ME_GetSelectionParaFormat(info->editor, &fmt); 680 info->fmt.dwMask |= PFM_STARTINDENT | PFM_OFFSET; 681 info->fmt.dxStartIndent = fmt.dxStartIndent; 682 info->fmt.dxOffset = fmt.dxOffset; 683 } 684 if (info->rtfMinor == rtfFirstIndent) 685 { 686 info->fmt.dxStartIndent += info->fmt.dxOffset + info->rtfParam; 687 info->fmt.dxOffset = -info->rtfParam; 688 } 689 else 690 info->fmt.dxStartIndent = info->rtfParam - info->fmt.dxOffset; 691 break; 692 case rtfRightIndent: 693 info->fmt.dwMask |= PFM_RIGHTINDENT; 694 info->fmt.dxRightIndent = info->rtfParam; 695 break; 696 case rtfQuadLeft: 697 case rtfQuadJust: 698 info->fmt.dwMask |= PFM_ALIGNMENT; 699 info->fmt.wAlignment = PFA_LEFT; 700 break; 701 case rtfQuadRight: 702 info->fmt.dwMask |= PFM_ALIGNMENT; 703 info->fmt.wAlignment = PFA_RIGHT; 704 break; 705 case rtfQuadCenter: 706 info->fmt.dwMask |= PFM_ALIGNMENT; 707 info->fmt.wAlignment = PFA_CENTER; 708 break; 709 case rtfTabPos: 710 if (!(info->fmt.dwMask & PFM_TABSTOPS)) 711 { 712 PARAFORMAT2 fmt; 713 fmt.cbSize = sizeof(fmt); 714 ME_GetSelectionParaFormat(info->editor, &fmt); 715 memcpy(info->fmt.rgxTabs, fmt.rgxTabs, 716 fmt.cTabCount * sizeof(fmt.rgxTabs[0])); 717 info->fmt.cTabCount = fmt.cTabCount; 718 info->fmt.dwMask |= PFM_TABSTOPS; 719 } 720 if (info->fmt.cTabCount < MAX_TAB_STOPS && info->rtfParam < 0x1000000) 721 info->fmt.rgxTabs[info->fmt.cTabCount++] = info->rtfParam; 722 break; 723 case rtfKeep: 724 info->fmt.dwMask |= PFM_KEEP; 725 info->fmt.wEffects |= PFE_KEEP; 726 break; 727 case rtfNoWidowControl: 728 info->fmt.dwMask |= PFM_NOWIDOWCONTROL; 729 info->fmt.wEffects |= PFE_NOWIDOWCONTROL; 730 break; 731 case rtfKeepNext: 732 info->fmt.dwMask |= PFM_KEEPNEXT; 733 info->fmt.wEffects |= PFE_KEEPNEXT; 734 break; 735 case rtfSpaceAfter: 736 info->fmt.dwMask |= PFM_SPACEAFTER; 737 info->fmt.dySpaceAfter = info->rtfParam; 738 break; 739 case rtfSpaceBefore: 740 info->fmt.dwMask |= PFM_SPACEBEFORE; 741 info->fmt.dySpaceBefore = info->rtfParam; 742 break; 743 case rtfSpaceBetween: 744 info->fmt.dwMask |= PFM_LINESPACING; 745 if ((int)info->rtfParam > 0) 746 { 747 info->fmt.dyLineSpacing = info->rtfParam; 748 info->fmt.bLineSpacingRule = 3; 749 } 750 else 751 { 752 info->fmt.dyLineSpacing = info->rtfParam; 753 info->fmt.bLineSpacingRule = 4; 754 } 755 break; 756 case rtfSpaceMultiply: 757 info->fmt.dwMask |= PFM_LINESPACING; 758 info->fmt.dyLineSpacing = info->rtfParam * 20; 759 info->fmt.bLineSpacingRule = 5; 760 break; 761 case rtfParBullet: 762 info->fmt.dwMask |= PFM_NUMBERING; 763 info->fmt.wNumbering = PFN_BULLET; 764 break; 765 case rtfParSimple: 766 info->fmt.dwMask |= PFM_NUMBERING; 767 info->fmt.wNumbering = 2; /* FIXME: MSDN says it's not used ?? */ 768 break; 769 case rtfBorderLeft: 770 info->borderType = RTFBorderParaLeft; 771 info->fmt.wBorders |= 1; 772 info->fmt.dwMask |= PFM_BORDER; 773 break; 774 case rtfBorderRight: 775 info->borderType = RTFBorderParaRight; 776 info->fmt.wBorders |= 2; 777 info->fmt.dwMask |= PFM_BORDER; 778 break; 779 case rtfBorderTop: 780 info->borderType = RTFBorderParaTop; 781 info->fmt.wBorders |= 4; 782 info->fmt.dwMask |= PFM_BORDER; 783 break; 784 case rtfBorderBottom: 785 info->borderType = RTFBorderParaBottom; 786 info->fmt.wBorders |= 8; 787 info->fmt.dwMask |= PFM_BORDER; 788 break; 789 case rtfBorderSingle: 790 info->fmt.wBorders &= ~0x700; 791 info->fmt.wBorders |= 1 << 8; 792 info->fmt.dwMask |= PFM_BORDER; 793 break; 794 case rtfBorderThick: 795 info->fmt.wBorders &= ~0x700; 796 info->fmt.wBorders |= 2 << 8; 797 info->fmt.dwMask |= PFM_BORDER; 798 break; 799 case rtfBorderShadow: 800 info->fmt.wBorders &= ~0x700; 801 info->fmt.wBorders |= 10 << 8; 802 info->fmt.dwMask |= PFM_BORDER; 803 break; 804 case rtfBorderDouble: 805 info->fmt.wBorders &= ~0x700; 806 info->fmt.wBorders |= 7 << 8; 807 info->fmt.dwMask |= PFM_BORDER; 808 break; 809 case rtfBorderDot: 810 info->fmt.wBorders &= ~0x700; 811 info->fmt.wBorders |= 11 << 8; 812 info->fmt.dwMask |= PFM_BORDER; 813 break; 814 case rtfBorderWidth: 815 { 816 int borderSide = info->borderType & RTFBorderSideMask; 817 RTFTable *tableDef = info->tableDef; 818 if ((info->borderType & RTFBorderTypeMask) == RTFBorderTypeCell) 819 { 820 RTFBorder *border; 821 if (!tableDef || tableDef->numCellsDefined >= MAX_TABLE_CELLS) 822 break; 823 border = &tableDef->cells[tableDef->numCellsDefined].border[borderSide]; 824 border->width = info->rtfParam; 825 break; 826 } 827 info->fmt.wBorderWidth = info->rtfParam; 828 info->fmt.dwMask |= PFM_BORDER; 829 break; 830 } 831 case rtfBorderSpace: 832 info->fmt.wBorderSpace = info->rtfParam; 833 info->fmt.dwMask |= PFM_BORDER; 834 break; 835 case rtfBorderColor: 836 { 837 RTFTable *tableDef = info->tableDef; 838 int borderSide = info->borderType & RTFBorderSideMask; 839 int borderType = info->borderType & RTFBorderTypeMask; 840 switch(borderType) 841 { 842 case RTFBorderTypePara: 843 if (!info->editor->bEmulateVersion10) /* v4.1 */ 844 break; 845 /* v1.0 - 3.0 treat paragraph and row borders the same. */ 846 case RTFBorderTypeRow: 847 if (tableDef) { 848 tableDef->border[borderSide].color = info->rtfParam; 849 } 850 break; 851 case RTFBorderTypeCell: 852 if (tableDef && tableDef->numCellsDefined < MAX_TABLE_CELLS) { 853 tableDef->cells[tableDef->numCellsDefined].border[borderSide].color = info->rtfParam; 854 } 855 break; 856 } 857 break; 858 } 859 case rtfRTLPar: 860 info->fmt.dwMask |= PFM_RTLPARA; 861 info->fmt.wEffects |= PFE_RTLPARA; 862 break; 863 case rtfLTRPar: 864 info->fmt.dwMask |= PFM_RTLPARA; 865 info->fmt.wEffects &= ~PFE_RTLPARA; 866 break; 867 } 868 } 869 870 void ME_RTFTblAttrHook(RTF_Info *info) 871 { 872 switch (info->rtfMinor) 873 { 874 case rtfRowDef: 875 { 876 if (!info->editor->bEmulateVersion10) /* v4.1 */ 877 info->borderType = 0; /* Not sure */ 878 else /* v1.0 - 3.0 */ 879 info->borderType = RTFBorderRowTop; 880 if (!info->tableDef) { 881 info->tableDef = ME_MakeTableDef(info->editor); 882 } else { 883 ME_InitTableDef(info->editor, info->tableDef); 884 } 885 break; 886 } 887 case rtfCellPos: 888 { 889 int cellNum; 890 if (!info->tableDef) 891 { 892 info->tableDef = ME_MakeTableDef(info->editor); 893 } 894 cellNum = info->tableDef->numCellsDefined; 895 if (cellNum >= MAX_TABLE_CELLS) 896 break; 897 info->tableDef->cells[cellNum].rightBoundary = info->rtfParam; 898 if (cellNum < MAX_TAB_STOPS) { 899 /* Tab stops were used to store cell positions before v4.1 but v4.1 900 * still seems to set the tabstops without using them. */ 901 ME_DisplayItem *para = info->editor->pCursors[0].pPara; 902 PARAFORMAT2 *pFmt = ¶->member.para.fmt; 903 pFmt->rgxTabs[cellNum] &= ~0x00FFFFFF; 904 pFmt->rgxTabs[cellNum] |= 0x00FFFFFF & info->rtfParam; 905 } 906 info->tableDef->numCellsDefined++; 907 break; 908 } 909 case rtfRowBordTop: 910 info->borderType = RTFBorderRowTop; 911 break; 912 case rtfRowBordLeft: 913 info->borderType = RTFBorderRowLeft; 914 break; 915 case rtfRowBordBottom: 916 info->borderType = RTFBorderRowBottom; 917 break; 918 case rtfRowBordRight: 919 info->borderType = RTFBorderRowRight; 920 break; 921 case rtfCellBordTop: 922 info->borderType = RTFBorderCellTop; 923 break; 924 case rtfCellBordLeft: 925 info->borderType = RTFBorderCellLeft; 926 break; 927 case rtfCellBordBottom: 928 info->borderType = RTFBorderCellBottom; 929 break; 930 case rtfCellBordRight: 931 info->borderType = RTFBorderCellRight; 932 break; 933 case rtfRowGapH: 934 if (info->tableDef) 935 info->tableDef->gapH = info->rtfParam; 936 break; 937 case rtfRowLeftEdge: 938 if (info->tableDef) 939 info->tableDef->leftEdge = info->rtfParam; 940 break; 941 } 942 } 943 944 void ME_RTFSpecialCharHook(RTF_Info *info) 945 { 946 RTFTable *tableDef = info->tableDef; 947 switch (info->rtfMinor) 948 { 949 case rtfNestCell: 950 if (info->editor->bEmulateVersion10) /* v1.0 - v3.0 */ 951 break; 952 /* else fall through since v4.1 treats rtfNestCell and rtfCell the same */ 953 case rtfCell: 954 if (!tableDef) 955 break; 956 RTFFlushOutputBuffer(info); 957 if (!info->editor->bEmulateVersion10) { /* v4.1 */ 958 if (tableDef->tableRowStart) 959 { 960 if (!info->nestingLevel && 961 tableDef->tableRowStart->member.para.nFlags & MEPF_ROWEND) 962 { 963 ME_DisplayItem *para = tableDef->tableRowStart; 964 para = para->member.para.next_para; 965 para = ME_InsertTableRowStartAtParagraph(info->editor, para); 966 tableDef->tableRowStart = para; 967 info->nestingLevel = 1; 968 } 969 ME_InsertTableCellFromCursor(info->editor); 970 } 971 } else { /* v1.0 - v3.0 */ 972 ME_DisplayItem *para = info->editor->pCursors[0].pPara; 973 PARAFORMAT2 *pFmt = ¶->member.para.fmt; 974 if (pFmt->dwMask & PFM_TABLE && pFmt->wEffects & PFE_TABLE && 975 tableDef->numCellsInserted < tableDef->numCellsDefined) 976 { 977 WCHAR tab = '\t'; 978 ME_InsertTextFromCursor(info->editor, 0, &tab, 1, info->style); 979 tableDef->numCellsInserted++; 980 } 981 } 982 break; 983 case rtfNestRow: 984 if (info->editor->bEmulateVersion10) /* v1.0 - v3.0 */ 985 break; 986 /* else fall through since v4.1 treats rtfNestRow and rtfRow the same */ 987 case rtfRow: 988 { 989 ME_DisplayItem *para, *cell, *run; 990 int i; 991 992 if (!tableDef) 993 break; 994 RTFFlushOutputBuffer(info); 995 if (!info->editor->bEmulateVersion10) { /* v4.1 */ 996 if (!tableDef->tableRowStart) 997 break; 998 if (!info->nestingLevel && 999 tableDef->tableRowStart->member.para.nFlags & MEPF_ROWEND) 1000 { 1001 para = tableDef->tableRowStart; 1002 para = para->member.para.next_para; 1003 para = ME_InsertTableRowStartAtParagraph(info->editor, para); 1004 tableDef->tableRowStart = para; 1005 info->nestingLevel++; 1006 } 1007 para = tableDef->tableRowStart; 1008 cell = ME_FindItemFwd(para, diCell); 1009 assert(cell && !cell->member.cell.prev_cell); 1010 if (tableDef->numCellsDefined < 1) 1011 { 1012 /* 2000 twips appears to be the cell size that native richedit uses 1013 * when no cell sizes are specified. */ 1014 const int defaultCellSize = 2000; 1015 int nRightBoundary = defaultCellSize; 1016 cell->member.cell.nRightBoundary = nRightBoundary; 1017 while (cell->member.cell.next_cell) { 1018 cell = cell->member.cell.next_cell; 1019 nRightBoundary += defaultCellSize; 1020 cell->member.cell.nRightBoundary = nRightBoundary; 1021 } 1022 para = ME_InsertTableCellFromCursor(info->editor); 1023 cell = para->member.para.pCell; 1024 cell->member.cell.nRightBoundary = nRightBoundary; 1025 } else { 1026 for (i = 0; i < tableDef->numCellsDefined; i++) 1027 { 1028 RTFCell *cellDef = &tableDef->cells[i]; 1029 cell->member.cell.nRightBoundary = cellDef->rightBoundary; 1030 ME_ApplyBorderProperties(info, &cell->member.cell.border, 1031 cellDef->border); 1032 cell = cell->member.cell.next_cell; 1033 if (!cell) 1034 { 1035 para = ME_InsertTableCellFromCursor(info->editor); 1036 cell = para->member.para.pCell; 1037 } 1038 } 1039 /* Cell for table row delimiter is empty */ 1040 cell->member.cell.nRightBoundary = tableDef->cells[i-1].rightBoundary; 1041 } 1042 1043 run = ME_FindItemFwd(cell, diRun); 1044 if (info->editor->pCursors[0].pRun != run || 1045 info->editor->pCursors[0].nOffset) 1046 { 1047 int nOfs, nChars; 1048 /* Delete inserted cells that aren't defined. */ 1049 info->editor->pCursors[1].pRun = run; 1050 info->editor->pCursors[1].pPara = ME_GetParagraph(run); 1051 info->editor->pCursors[1].nOffset = 0; 1052 nOfs = ME_GetCursorOfs(&info->editor->pCursors[1]); 1053 nChars = ME_GetCursorOfs(&info->editor->pCursors[0]) - nOfs; 1054 ME_InternalDeleteText(info->editor, &info->editor->pCursors[1], 1055 nChars, TRUE); 1056 } 1057 1058 para = ME_InsertTableRowEndFromCursor(info->editor); 1059 para->member.para.fmt.dxOffset = abs(info->tableDef->gapH); 1060 para->member.para.fmt.dxStartIndent = info->tableDef->leftEdge; 1061 ME_ApplyBorderProperties(info, ¶->member.para.border, 1062 tableDef->border); 1063 info->nestingLevel--; 1064 if (!info->nestingLevel) 1065 { 1066 if (info->canInheritInTbl) { 1067 tableDef->tableRowStart = para; 1068 } else { 1069 while (info->tableDef) { 1070 tableDef = info->tableDef; 1071 info->tableDef = tableDef->parent; 1072 heap_free(tableDef); 1073 } 1074 } 1075 } else { 1076 info->tableDef = tableDef->parent; 1077 heap_free(tableDef); 1078 } 1079 } else { /* v1.0 - v3.0 */ 1080 WCHAR endl = '\r'; 1081 ME_DisplayItem *para = info->editor->pCursors[0].pPara; 1082 PARAFORMAT2 *pFmt = ¶->member.para.fmt; 1083 pFmt->dxOffset = info->tableDef->gapH; 1084 pFmt->dxStartIndent = info->tableDef->leftEdge; 1085 1086 ME_ApplyBorderProperties(info, ¶->member.para.border, 1087 tableDef->border); 1088 while (tableDef->numCellsInserted < tableDef->numCellsDefined) 1089 { 1090 WCHAR tab = '\t'; 1091 ME_InsertTextFromCursor(info->editor, 0, &tab, 1, info->style); 1092 tableDef->numCellsInserted++; 1093 } 1094 pFmt->cTabCount = min(tableDef->numCellsDefined, MAX_TAB_STOPS); 1095 if (!tableDef->numCellsDefined) 1096 pFmt->wEffects &= ~PFE_TABLE; 1097 ME_InsertTextFromCursor(info->editor, 0, &endl, 1, info->style); 1098 tableDef->numCellsInserted = 0; 1099 } 1100 break; 1101 } 1102 case rtfTab: 1103 case rtfPar: 1104 if (info->editor->bEmulateVersion10) { /* v1.0 - 3.0 */ 1105 ME_DisplayItem *para; 1106 PARAFORMAT2 *pFmt; 1107 RTFFlushOutputBuffer(info); 1108 para = info->editor->pCursors[0].pPara; 1109 pFmt = ¶->member.para.fmt; 1110 if (pFmt->dwMask & PFM_TABLE && pFmt->wEffects & PFE_TABLE) 1111 { 1112 /* rtfPar is treated like a space within a table. */ 1113 info->rtfClass = rtfText; 1114 info->rtfMajor = ' '; 1115 } 1116 else if (info->rtfMinor == rtfPar && tableDef) 1117 tableDef->numCellsInserted = 0; 1118 } 1119 break; 1120 } 1121 } 1122 1123 static HRESULT insert_static_object(ME_TextEditor *editor, HENHMETAFILE hemf, HBITMAP hbmp, 1124 const SIZEL* sz) 1125 { 1126 LPOLEOBJECT lpObject = NULL; 1127 LPSTORAGE lpStorage = NULL; 1128 LPOLECLIENTSITE lpClientSite = NULL; 1129 LPDATAOBJECT lpDataObject = NULL; 1130 LPOLECACHE lpOleCache = NULL; 1131 STGMEDIUM stgm; 1132 FORMATETC fm; 1133 CLSID clsid; 1134 HRESULT hr = E_FAIL; 1135 DWORD conn; 1136 1137 if (hemf) 1138 { 1139 stgm.tymed = TYMED_ENHMF; 1140 stgm.u.hEnhMetaFile = hemf; 1141 fm.cfFormat = CF_ENHMETAFILE; 1142 } 1143 else if (hbmp) 1144 { 1145 stgm.tymed = TYMED_GDI; 1146 stgm.u.hBitmap = hbmp; 1147 fm.cfFormat = CF_BITMAP; 1148 } 1149 stgm.pUnkForRelease = NULL; 1150 1151 fm.ptd = NULL; 1152 fm.dwAspect = DVASPECT_CONTENT; 1153 fm.lindex = -1; 1154 fm.tymed = stgm.tymed; 1155 1156 if (!editor->reOle) 1157 { 1158 if (!CreateIRichEditOle(NULL, editor, (LPVOID *)&editor->reOle)) 1159 return hr; 1160 } 1161 1162 if (OleCreateDefaultHandler(&CLSID_NULL, NULL, &IID_IOleObject, (void**)&lpObject) == S_OK && 1163 IRichEditOle_GetClientSite(editor->reOle, &lpClientSite) == S_OK && 1164 IOleObject_SetClientSite(lpObject, lpClientSite) == S_OK && 1165 IOleObject_GetUserClassID(lpObject, &clsid) == S_OK && 1166 IOleObject_QueryInterface(lpObject, &IID_IOleCache, (void**)&lpOleCache) == S_OK && 1167 IOleCache_Cache(lpOleCache, &fm, 0, &conn) == S_OK && 1168 IOleObject_QueryInterface(lpObject, &IID_IDataObject, (void**)&lpDataObject) == S_OK && 1169 IDataObject_SetData(lpDataObject, &fm, &stgm, TRUE) == S_OK) 1170 { 1171 REOBJECT reobject; 1172 1173 reobject.cbStruct = sizeof(reobject); 1174 reobject.cp = REO_CP_SELECTION; 1175 reobject.clsid = clsid; 1176 reobject.poleobj = lpObject; 1177 reobject.pstg = lpStorage; 1178 reobject.polesite = lpClientSite; 1179 /* convert from twips to .01 mm */ 1180 reobject.sizel.cx = MulDiv(sz->cx, 254, 144); 1181 reobject.sizel.cy = MulDiv(sz->cy, 254, 144); 1182 reobject.dvaspect = DVASPECT_CONTENT; 1183 reobject.dwFlags = 0; /* FIXME */ 1184 reobject.dwUser = 0; 1185 1186 ME_InsertOLEFromCursor(editor, &reobject, 0); 1187 hr = S_OK; 1188 } 1189 1190 if (lpObject) IOleObject_Release(lpObject); 1191 if (lpClientSite) IOleClientSite_Release(lpClientSite); 1192 if (lpStorage) IStorage_Release(lpStorage); 1193 if (lpDataObject) IDataObject_Release(lpDataObject); 1194 if (lpOleCache) IOleCache_Release(lpOleCache); 1195 1196 return hr; 1197 } 1198 1199 static void ME_RTFReadShpPictGroup( RTF_Info *info ) 1200 { 1201 int level = 1; 1202 1203 for (;;) 1204 { 1205 RTFGetToken (info); 1206 1207 if (info->rtfClass == rtfEOF) return; 1208 if (RTFCheckCM( info, rtfGroup, rtfEndGroup )) 1209 { 1210 if (--level == 0) break; 1211 } 1212 else if (RTFCheckCM( info, rtfGroup, rtfBeginGroup )) 1213 { 1214 level++; 1215 } 1216 else 1217 { 1218 RTFRouteToken( info ); 1219 if (RTFCheckCM( info, rtfGroup, rtfEndGroup )) 1220 level--; 1221 } 1222 } 1223 1224 RTFRouteToken( info ); /* feed "}" back to router */ 1225 return; 1226 } 1227 1228 static DWORD read_hex_data( RTF_Info *info, BYTE **out ) 1229 { 1230 DWORD read = 0, size = 1024; 1231 BYTE *buf, val; 1232 BOOL flip; 1233 1234 *out = NULL; 1235 1236 if (info->rtfClass != rtfText) 1237 { 1238 ERR("Called with incorrect token\n"); 1239 return 0; 1240 } 1241 1242 buf = HeapAlloc( GetProcessHeap(), 0, size ); 1243 if (!buf) return 0; 1244 1245 val = info->rtfMajor; 1246 for (flip = TRUE;; flip = !flip) 1247 { 1248 RTFGetToken( info ); 1249 if (info->rtfClass == rtfEOF) 1250 { 1251 HeapFree( GetProcessHeap(), 0, buf ); 1252 return 0; 1253 } 1254 if (info->rtfClass != rtfText) break; 1255 if (flip) 1256 { 1257 if (read >= size) 1258 { 1259 size *= 2; 1260 buf = HeapReAlloc( GetProcessHeap(), 0, buf, size ); 1261 if (!buf) return 0; 1262 } 1263 buf[read++] = RTFCharToHex(val) * 16 + RTFCharToHex(info->rtfMajor); 1264 } 1265 else 1266 val = info->rtfMajor; 1267 } 1268 if (flip) FIXME("wrong hex string\n"); 1269 1270 *out = buf; 1271 return read; 1272 } 1273 1274 static void ME_RTFReadPictGroup(RTF_Info *info) 1275 { 1276 SIZEL sz; 1277 BYTE *buffer = NULL; 1278 DWORD size = 0; 1279 METAFILEPICT mfp; 1280 HENHMETAFILE hemf; 1281 HBITMAP hbmp; 1282 enum gfxkind {gfx_unknown = 0, gfx_enhmetafile, gfx_metafile, gfx_dib} gfx = gfx_unknown; 1283 int level = 1; 1284 1285 mfp.mm = MM_TEXT; 1286 sz.cx = sz.cy = 0; 1287 1288 for (;;) 1289 { 1290 RTFGetToken( info ); 1291 1292 if (info->rtfClass == rtfText) 1293 { 1294 if (level == 1) 1295 { 1296 if (!buffer) 1297 size = read_hex_data( info, &buffer ); 1298 } 1299 else 1300 { 1301 RTFSkipGroup( info ); 1302 } 1303 } /* We potentially have a new token so fall through. */ 1304 1305 if (info->rtfClass == rtfEOF) return; 1306 1307 if (RTFCheckCM( info, rtfGroup, rtfEndGroup )) 1308 { 1309 if (--level == 0) break; 1310 continue; 1311 } 1312 if (RTFCheckCM( info, rtfGroup, rtfBeginGroup )) 1313 { 1314 level++; 1315 continue; 1316 } 1317 if (!RTFCheckCM( info, rtfControl, rtfPictAttr )) 1318 { 1319 RTFRouteToken( info ); 1320 if (RTFCheckCM( info, rtfGroup, rtfEndGroup )) 1321 level--; 1322 continue; 1323 } 1324 1325 if (RTFCheckMM( info, rtfPictAttr, rtfWinMetafile )) 1326 { 1327 mfp.mm = info->rtfParam; 1328 gfx = gfx_metafile; 1329 } 1330 else if (RTFCheckMM( info, rtfPictAttr, rtfDevIndBitmap )) 1331 { 1332 if (info->rtfParam != 0) FIXME("dibitmap should be 0 (%d)\n", info->rtfParam); 1333 gfx = gfx_dib; 1334 } 1335 else if (RTFCheckMM( info, rtfPictAttr, rtfEmfBlip )) 1336 gfx = gfx_enhmetafile; 1337 else if (RTFCheckMM( info, rtfPictAttr, rtfPicWid )) 1338 mfp.xExt = info->rtfParam; 1339 else if (RTFCheckMM( info, rtfPictAttr, rtfPicHt )) 1340 mfp.yExt = info->rtfParam; 1341 else if (RTFCheckMM( info, rtfPictAttr, rtfPicGoalWid )) 1342 sz.cx = info->rtfParam; 1343 else if (RTFCheckMM( info, rtfPictAttr, rtfPicGoalHt )) 1344 sz.cy = info->rtfParam; 1345 else 1346 FIXME("Non supported attribute: %d %d %d\n", info->rtfClass, info->rtfMajor, info->rtfMinor); 1347 } 1348 1349 if (buffer) 1350 { 1351 switch (gfx) 1352 { 1353 case gfx_enhmetafile: 1354 if ((hemf = SetEnhMetaFileBits( size, buffer ))) 1355 insert_static_object( info->editor, hemf, NULL, &sz ); 1356 break; 1357 case gfx_metafile: 1358 if ((hemf = SetWinMetaFileBits( size, buffer, NULL, &mfp ))) 1359 insert_static_object( info->editor, hemf, NULL, &sz ); 1360 break; 1361 case gfx_dib: 1362 { 1363 BITMAPINFO *bi = (BITMAPINFO*)buffer; 1364 HDC hdc = GetDC(0); 1365 unsigned nc = bi->bmiHeader.biClrUsed; 1366 1367 /* not quite right, especially for bitfields type of compression */ 1368 if (!nc && bi->bmiHeader.biBitCount <= 8) 1369 nc = 1 << bi->bmiHeader.biBitCount; 1370 if ((hbmp = CreateDIBitmap( hdc, &bi->bmiHeader, 1371 CBM_INIT, (char*)(bi + 1) + nc * sizeof(RGBQUAD), 1372 bi, DIB_RGB_COLORS)) ) 1373 insert_static_object( info->editor, NULL, hbmp, &sz ); 1374 ReleaseDC( 0, hdc ); 1375 break; 1376 } 1377 default: 1378 break; 1379 } 1380 } 1381 HeapFree( GetProcessHeap(), 0, buffer ); 1382 RTFRouteToken( info ); /* feed "}" back to router */ 1383 return; 1384 } 1385 1386 /* for now, lookup the \result part and use it, whatever the object */ 1387 static void ME_RTFReadObjectGroup(RTF_Info *info) 1388 { 1389 for (;;) 1390 { 1391 RTFGetToken (info); 1392 if (info->rtfClass == rtfEOF) 1393 return; 1394 if (RTFCheckCM(info, rtfGroup, rtfEndGroup)) 1395 break; 1396 if (RTFCheckCM(info, rtfGroup, rtfBeginGroup)) 1397 { 1398 RTFGetToken (info); 1399 if (info->rtfClass == rtfEOF) 1400 return; 1401 if (RTFCheckCMM(info, rtfControl, rtfDestination, rtfObjResult)) 1402 { 1403 int level = 1; 1404 1405 while (RTFGetToken (info) != rtfEOF) 1406 { 1407 if (info->rtfClass == rtfGroup) 1408 { 1409 if (info->rtfMajor == rtfBeginGroup) level++; 1410 else if (info->rtfMajor == rtfEndGroup && --level < 0) break; 1411 } 1412 RTFRouteToken(info); 1413 } 1414 } 1415 else RTFSkipGroup(info); 1416 continue; 1417 } 1418 if (!RTFCheckCM (info, rtfControl, rtfObjAttr)) 1419 { 1420 FIXME("Non supported attribute: %d %d %d\n", info->rtfClass, info->rtfMajor, info->rtfMinor); 1421 return; 1422 } 1423 } 1424 RTFRouteToken(info); /* feed "}" back to router */ 1425 } 1426 1427 static void ME_RTFReadParnumGroup( RTF_Info *info ) 1428 { 1429 int level = 1, type = -1; 1430 WORD indent = 0, start = 1; 1431 WCHAR txt_before = 0, txt_after = 0; 1432 1433 for (;;) 1434 { 1435 RTFGetToken( info ); 1436 1437 if (RTFCheckCMM( info, rtfControl, rtfDestination, rtfParNumTextBefore ) || 1438 RTFCheckCMM( info, rtfControl, rtfDestination, rtfParNumTextAfter )) 1439 { 1440 int loc = info->rtfMinor; 1441 1442 RTFGetToken( info ); 1443 if (info->rtfClass == rtfText) 1444 { 1445 if (loc == rtfParNumTextBefore) 1446 txt_before = info->rtfMajor; 1447 else 1448 txt_after = info->rtfMajor; 1449 continue; 1450 } 1451 /* falling through to catch EOFs and group level changes */ 1452 } 1453 1454 if (info->rtfClass == rtfEOF) 1455 return; 1456 1457 if (RTFCheckCM( info, rtfGroup, rtfEndGroup )) 1458 { 1459 if (--level == 0) break; 1460 continue; 1461 } 1462 1463 if (RTFCheckCM( info, rtfGroup, rtfBeginGroup )) 1464 { 1465 level++; 1466 continue; 1467 } 1468 1469 /* Ignore non para-attr */ 1470 if (!RTFCheckCM( info, rtfControl, rtfParAttr )) 1471 continue; 1472 1473 switch (info->rtfMinor) 1474 { 1475 case rtfParLevel: /* Para level is ignored */ 1476 case rtfParSimple: 1477 break; 1478 case rtfParBullet: 1479 type = PFN_BULLET; 1480 break; 1481 1482 case rtfParNumDecimal: 1483 type = PFN_ARABIC; 1484 break; 1485 case rtfParNumULetter: 1486 type = PFN_UCLETTER; 1487 break; 1488 case rtfParNumURoman: 1489 type = PFN_UCROMAN; 1490 break; 1491 case rtfParNumLLetter: 1492 type = PFN_LCLETTER; 1493 break; 1494 case rtfParNumLRoman: 1495 type = PFN_LCROMAN; 1496 break; 1497 1498 case rtfParNumIndent: 1499 indent = info->rtfParam; 1500 break; 1501 case rtfParNumStartAt: 1502 start = info->rtfParam; 1503 break; 1504 } 1505 } 1506 1507 if (type != -1) 1508 { 1509 info->fmt.dwMask |= (PFM_NUMBERING | PFM_NUMBERINGSTART | PFM_NUMBERINGSTYLE | PFM_NUMBERINGTAB); 1510 info->fmt.wNumbering = type; 1511 info->fmt.wNumberingStart = start; 1512 info->fmt.wNumberingStyle = PFNS_PAREN; 1513 if (type != PFN_BULLET) 1514 { 1515 if (txt_before == 0 && txt_after == 0) 1516 info->fmt.wNumberingStyle = PFNS_PLAIN; 1517 else if (txt_after == '.') 1518 info->fmt.wNumberingStyle = PFNS_PERIOD; 1519 else if (txt_before == '(' && txt_after == ')') 1520 info->fmt.wNumberingStyle = PFNS_PARENS; 1521 } 1522 info->fmt.wNumberingTab = indent; 1523 } 1524 1525 TRACE("type %d indent %d start %d txt before %04x txt after %04x\n", 1526 type, indent, start, txt_before, txt_after); 1527 1528 RTFRouteToken( info ); /* feed "}" back to router */ 1529 } 1530 1531 static void ME_RTFReadHook(RTF_Info *info) 1532 { 1533 switch(info->rtfClass) 1534 { 1535 case rtfGroup: 1536 switch(info->rtfMajor) 1537 { 1538 case rtfBeginGroup: 1539 if (info->stackTop < maxStack) { 1540 info->stack[info->stackTop].style = info->style; 1541 ME_AddRefStyle(info->style); 1542 info->stack[info->stackTop].codePage = info->codePage; 1543 info->stack[info->stackTop].unicodeLength = info->unicodeLength; 1544 } 1545 info->stackTop++; 1546 info->styleChanged = FALSE; 1547 break; 1548 case rtfEndGroup: 1549 { 1550 RTFFlushOutputBuffer(info); 1551 info->stackTop--; 1552 if (info->stackTop <= 0) 1553 info->rtfClass = rtfEOF; 1554 if (info->stackTop < 0) 1555 return; 1556 1557 ME_ReleaseStyle(info->style); 1558 info->style = info->stack[info->stackTop].style; 1559 info->codePage = info->stack[info->stackTop].codePage; 1560 info->unicodeLength = info->stack[info->stackTop].unicodeLength; 1561 break; 1562 } 1563 } 1564 break; 1565 } 1566 } 1567 1568 void 1569 ME_StreamInFill(ME_InStream *stream) 1570 { 1571 stream->editstream->dwError = stream->editstream->pfnCallback(stream->editstream->dwCookie, 1572 (BYTE *)stream->buffer, 1573 sizeof(stream->buffer), 1574 (LONG *)&stream->dwSize); 1575 stream->dwUsed = 0; 1576 } 1577 1578 static LRESULT ME_StreamIn(ME_TextEditor *editor, DWORD format, EDITSTREAM *stream, BOOL stripLastCR) 1579 { 1580 RTF_Info parser; 1581 ME_Style *style; 1582 int from, to, nUndoMode; 1583 int nEventMask = editor->nEventMask; 1584 ME_InStream inStream; 1585 BOOL invalidRTF = FALSE; 1586 ME_Cursor *selStart, *selEnd; 1587 LRESULT num_read = 0; /* bytes read for SF_TEXT, non-control chars inserted for SF_RTF */ 1588 1589 TRACE("stream==%p editor==%p format==0x%X\n", stream, editor, format); 1590 editor->nEventMask = 0; 1591 1592 ME_GetSelectionOfs(editor, &from, &to); 1593 if (format & SFF_SELECTION && editor->mode & TM_RICHTEXT) 1594 { 1595 ME_GetSelection(editor, &selStart, &selEnd); 1596 style = ME_GetSelectionInsertStyle(editor); 1597 1598 ME_InternalDeleteText(editor, selStart, to - from, FALSE); 1599 1600 /* Don't insert text at the end of the table row */ 1601 if (!editor->bEmulateVersion10) { /* v4.1 */ 1602 ME_DisplayItem *para = editor->pCursors->pPara; 1603 if (para->member.para.nFlags & MEPF_ROWEND) 1604 { 1605 para = para->member.para.next_para; 1606 editor->pCursors[0].pPara = para; 1607 editor->pCursors[0].pRun = ME_FindItemFwd(para, diRun); 1608 editor->pCursors[0].nOffset = 0; 1609 } 1610 if (para->member.para.nFlags & MEPF_ROWSTART) 1611 { 1612 para = para->member.para.next_para; 1613 editor->pCursors[0].pPara = para; 1614 editor->pCursors[0].pRun = ME_FindItemFwd(para, diRun); 1615 editor->pCursors[0].nOffset = 0; 1616 } 1617 editor->pCursors[1] = editor->pCursors[0]; 1618 } else { /* v1.0 - 3.0 */ 1619 if (editor->pCursors[0].pRun->member.run.nFlags & MERF_ENDPARA && 1620 ME_IsInTable(editor->pCursors[0].pRun)) 1621 return 0; 1622 } 1623 } else { 1624 style = editor->pBuffer->pDefaultStyle; 1625 ME_AddRefStyle(style); 1626 ME_SetSelection(editor, 0, 0); 1627 ME_InternalDeleteText(editor, &editor->pCursors[1], 1628 ME_GetTextLength(editor), FALSE); 1629 from = to = 0; 1630 ME_ClearTempStyle(editor); 1631 ME_SetDefaultParaFormat(editor, &editor->pCursors[0].pPara->member.para.fmt); 1632 } 1633 1634 1635 /* Back up undo mode to a local variable */ 1636 nUndoMode = editor->nUndoMode; 1637 1638 /* Only create an undo if SFF_SELECTION is set */ 1639 if (!(format & SFF_SELECTION)) 1640 editor->nUndoMode = umIgnore; 1641 1642 inStream.editstream = stream; 1643 inStream.editstream->dwError = 0; 1644 inStream.dwSize = 0; 1645 inStream.dwUsed = 0; 1646 1647 if (format & SF_RTF) 1648 { 1649 /* Check if it's really RTF, and if it is not, use plain text */ 1650 ME_StreamInFill(&inStream); 1651 if (!inStream.editstream->dwError) 1652 { 1653 if ((!editor->bEmulateVersion10 && strncmp(inStream.buffer, "{\\rtf", 5) && strncmp(inStream.buffer, "{\\urtf", 6)) 1654 || (editor->bEmulateVersion10 && *inStream.buffer != '{')) 1655 { 1656 invalidRTF = TRUE; 1657 inStream.editstream->dwError = -16; 1658 } 1659 } 1660 } 1661 1662 if (!invalidRTF && !inStream.editstream->dwError) 1663 { 1664 ME_Cursor start; 1665 from = ME_GetCursorOfs(&editor->pCursors[0]); 1666 if (format & SF_RTF) { 1667 1668 /* setup the RTF parser */ 1669 memset(&parser, 0, sizeof parser); 1670 RTFSetEditStream(&parser, &inStream); 1671 parser.rtfFormat = format&(SF_TEXT|SF_RTF); 1672 parser.editor = editor; 1673 parser.style = style; 1674 WriterInit(&parser); 1675 RTFInit(&parser); 1676 RTFSetReadHook(&parser, ME_RTFReadHook); 1677 RTFSetDestinationCallback(&parser, rtfShpPict, ME_RTFReadShpPictGroup); 1678 RTFSetDestinationCallback(&parser, rtfPict, ME_RTFReadPictGroup); 1679 RTFSetDestinationCallback(&parser, rtfObject, ME_RTFReadObjectGroup); 1680 RTFSetDestinationCallback(&parser, rtfParNumbering, ME_RTFReadParnumGroup); 1681 if (!parser.editor->bEmulateVersion10) /* v4.1 */ 1682 { 1683 RTFSetDestinationCallback(&parser, rtfNoNestTables, RTFSkipGroup); 1684 RTFSetDestinationCallback(&parser, rtfNestTableProps, RTFReadGroup); 1685 } 1686 BeginFile(&parser); 1687 1688 /* do the parsing */ 1689 RTFRead(&parser); 1690 RTFFlushOutputBuffer(&parser); 1691 if (!editor->bEmulateVersion10) { /* v4.1 */ 1692 if (parser.tableDef && parser.tableDef->tableRowStart && 1693 (parser.nestingLevel > 0 || parser.canInheritInTbl)) 1694 { 1695 /* Delete any incomplete table row at the end of the rich text. */ 1696 int nOfs, nChars; 1697 ME_DisplayItem *para; 1698 1699 parser.rtfMinor = rtfRow; 1700 /* Complete the table row before deleting it. 1701 * By doing it this way we will have the current paragraph format set 1702 * properly to reflect that is not in the complete table, and undo items 1703 * will be added for this change to the current paragraph format. */ 1704 if (parser.nestingLevel > 0) 1705 { 1706 while (parser.nestingLevel > 1) 1707 ME_RTFSpecialCharHook(&parser); /* Decrements nestingLevel */ 1708 para = parser.tableDef->tableRowStart; 1709 ME_RTFSpecialCharHook(&parser); 1710 } else { 1711 para = parser.tableDef->tableRowStart; 1712 ME_RTFSpecialCharHook(&parser); 1713 assert(para->member.para.nFlags & MEPF_ROWEND); 1714 para = para->member.para.next_para; 1715 } 1716 1717 editor->pCursors[1].pPara = para; 1718 editor->pCursors[1].pRun = ME_FindItemFwd(para, diRun); 1719 editor->pCursors[1].nOffset = 0; 1720 nOfs = ME_GetCursorOfs(&editor->pCursors[1]); 1721 nChars = ME_GetCursorOfs(&editor->pCursors[0]) - nOfs; 1722 ME_InternalDeleteText(editor, &editor->pCursors[1], nChars, TRUE); 1723 if (parser.tableDef) 1724 parser.tableDef->tableRowStart = NULL; 1725 } 1726 } 1727 ME_CheckTablesForCorruption(editor); 1728 RTFDestroy(&parser); 1729 1730 if (parser.stackTop > 0) 1731 { 1732 while (--parser.stackTop >= 0) 1733 { 1734 ME_ReleaseStyle(parser.style); 1735 parser.style = parser.stack[parser.stackTop].style; 1736 } 1737 if (!inStream.editstream->dwError) 1738 inStream.editstream->dwError = HRESULT_FROM_WIN32(ERROR_HANDLE_EOF); 1739 } 1740 1741 /* Remove last line break, as mandated by tests. This is not affected by 1742 CR/LF counters, since RTF streaming presents only \para tokens, which 1743 are converted according to the standard rules: \r for 2.0, \r\n for 1.0 1744 */ 1745 if (stripLastCR && !(format & SFF_SELECTION)) { 1746 int newto; 1747 ME_GetSelection(editor, &selStart, &selEnd); 1748 newto = ME_GetCursorOfs(selEnd); 1749 if (newto > to + (editor->bEmulateVersion10 ? 1 : 0)) { 1750 WCHAR lastchar[3] = {'\0', '\0'}; 1751 int linebreakSize = editor->bEmulateVersion10 ? 2 : 1; 1752 ME_Cursor linebreakCursor = *selEnd, lastcharCursor = *selEnd; 1753 CHARFORMAT2W cf; 1754 1755 /* Set the final eop to the char fmt of the last char */ 1756 cf.cbSize = sizeof(cf); 1757 cf.dwMask = CFM_ALL2; 1758 ME_MoveCursorChars(editor, &lastcharCursor, -1, FALSE); 1759 ME_GetCharFormat(editor, &lastcharCursor, &linebreakCursor, &cf); 1760 ME_SetSelection(editor, newto, -1); 1761 ME_SetSelectionCharFormat(editor, &cf); 1762 ME_SetSelection(editor, newto, newto); 1763 1764 ME_MoveCursorChars(editor, &linebreakCursor, -linebreakSize, FALSE); 1765 ME_GetTextW(editor, lastchar, 2, &linebreakCursor, linebreakSize, FALSE, FALSE); 1766 if (lastchar[0] == '\r' && (lastchar[1] == '\n' || lastchar[1] == '\0')) { 1767 ME_InternalDeleteText(editor, &linebreakCursor, linebreakSize, FALSE); 1768 } 1769 } 1770 } 1771 to = ME_GetCursorOfs(&editor->pCursors[0]); 1772 num_read = to - from; 1773 1774 style = parser.style; 1775 } 1776 else if (format & SF_TEXT) 1777 { 1778 num_read = ME_StreamInText(editor, format, &inStream, style); 1779 to = ME_GetCursorOfs(&editor->pCursors[0]); 1780 } 1781 else 1782 ERR("EM_STREAMIN without SF_TEXT or SF_RTF\n"); 1783 /* put the cursor at the top */ 1784 if (!(format & SFF_SELECTION)) 1785 ME_SetSelection(editor, 0, 0); 1786 ME_CursorFromCharOfs(editor, from, &start); 1787 ME_UpdateLinkAttribute(editor, &start, to - from); 1788 } 1789 1790 /* Restore saved undo mode */ 1791 editor->nUndoMode = nUndoMode; 1792 1793 /* even if we didn't add an undo, we need to commit anything on the stack */ 1794 ME_CommitUndo(editor); 1795 1796 /* If SFF_SELECTION isn't set, delete any undos from before we started too */ 1797 if (!(format & SFF_SELECTION)) 1798 ME_EmptyUndoStack(editor); 1799 1800 ME_ReleaseStyle(style); 1801 editor->nEventMask = nEventMask; 1802 ME_UpdateRepaint(editor, FALSE); 1803 if (!(format & SFF_SELECTION)) { 1804 ME_ClearTempStyle(editor); 1805 } 1806 ITextHost_TxShowCaret(editor->texthost, FALSE); 1807 ME_MoveCaret(editor); 1808 ITextHost_TxShowCaret(editor->texthost, TRUE); 1809 ME_SendSelChange(editor); 1810 ME_SendRequestResize(editor, FALSE); 1811 1812 return num_read; 1813 } 1814 1815 1816 typedef struct tagME_RTFStringStreamStruct 1817 { 1818 char *string; 1819 int pos; 1820 int length; 1821 } ME_RTFStringStreamStruct; 1822 1823 static DWORD CALLBACK ME_ReadFromRTFString(DWORD_PTR dwCookie, LPBYTE lpBuff, LONG cb, LONG *pcb) 1824 { 1825 ME_RTFStringStreamStruct *pStruct = (ME_RTFStringStreamStruct *)dwCookie; 1826 int count; 1827 1828 count = min(cb, pStruct->length - pStruct->pos); 1829 memmove(lpBuff, pStruct->string + pStruct->pos, count); 1830 pStruct->pos += count; 1831 *pcb = count; 1832 return 0; 1833 } 1834 1835 static void 1836 ME_StreamInRTFString(ME_TextEditor *editor, BOOL selection, char *string) 1837 { 1838 EDITSTREAM es; 1839 ME_RTFStringStreamStruct data; 1840 1841 data.string = string; 1842 data.length = strlen(string); 1843 data.pos = 0; 1844 es.dwCookie = (DWORD_PTR)&data; 1845 es.pfnCallback = ME_ReadFromRTFString; 1846 ME_StreamIn(editor, SF_RTF | (selection ? SFF_SELECTION : 0), &es, TRUE); 1847 } 1848 1849 1850 static int 1851 ME_FindText(ME_TextEditor *editor, DWORD flags, const CHARRANGE *chrg, const WCHAR *text, CHARRANGE *chrgText) 1852 { 1853 const int nLen = lstrlenW(text); 1854 const int nTextLen = ME_GetTextLength(editor); 1855 int nMin, nMax; 1856 ME_Cursor cursor; 1857 WCHAR wLastChar = ' '; 1858 1859 TRACE("flags==0x%08x, chrg->cpMin==%d, chrg->cpMax==%d text==%s\n", 1860 flags, chrg->cpMin, chrg->cpMax, debugstr_w(text)); 1861 1862 if (flags & ~(FR_DOWN | FR_MATCHCASE | FR_WHOLEWORD)) 1863 FIXME("Flags 0x%08x not implemented\n", 1864 flags & ~(FR_DOWN | FR_MATCHCASE | FR_WHOLEWORD)); 1865 1866 nMin = chrg->cpMin; 1867 if (chrg->cpMax == -1) 1868 nMax = nTextLen; 1869 else 1870 nMax = chrg->cpMax > nTextLen ? nTextLen : chrg->cpMax; 1871 1872 /* In 1.0 emulation, if cpMax reaches end of text, add the FR_DOWN flag */ 1873 if (editor->bEmulateVersion10 && nMax == nTextLen) 1874 { 1875 flags |= FR_DOWN; 1876 } 1877 1878 /* In 1.0 emulation, cpMin must always be no greater than cpMax */ 1879 if (editor->bEmulateVersion10 && nMax < nMin) 1880 { 1881 if (chrgText) 1882 { 1883 chrgText->cpMin = -1; 1884 chrgText->cpMax = -1; 1885 } 1886 return -1; 1887 } 1888 1889 /* when searching up, if cpMin < cpMax, then instead of searching 1890 * on [cpMin,cpMax], we search on [0,cpMin], otherwise, search on 1891 * [cpMax, cpMin]. The exception is when cpMax is -1, in which 1892 * case, it is always bigger than cpMin. 1893 */ 1894 if (!editor->bEmulateVersion10 && !(flags & FR_DOWN)) 1895 { 1896 int nSwap = nMax; 1897 1898 nMax = nMin > nTextLen ? nTextLen : nMin; 1899 if (nMin < nSwap || chrg->cpMax == -1) 1900 nMin = 0; 1901 else 1902 nMin = nSwap; 1903 } 1904 1905 if (!nLen || nMin < 0 || nMax < 0 || nMax < nMin) 1906 { 1907 if (chrgText) 1908 chrgText->cpMin = chrgText->cpMax = -1; 1909 return -1; 1910 } 1911 1912 if (flags & FR_DOWN) /* Forward search */ 1913 { 1914 /* If possible, find the character before where the search starts */ 1915 if ((flags & FR_WHOLEWORD) && nMin) 1916 { 1917 ME_CursorFromCharOfs(editor, nMin - 1, &cursor); 1918 wLastChar = *get_text( &cursor.pRun->member.run, cursor.nOffset ); 1919 ME_MoveCursorChars(editor, &cursor, 1, FALSE); 1920 } else { 1921 ME_CursorFromCharOfs(editor, nMin, &cursor); 1922 } 1923 1924 while (cursor.pRun && ME_GetCursorOfs(&cursor) + nLen <= nMax) 1925 { 1926 ME_DisplayItem *pCurItem = cursor.pRun; 1927 int nCurStart = cursor.nOffset; 1928 int nMatched = 0; 1929 1930 while (pCurItem && ME_CharCompare( *get_text( &pCurItem->member.run, nCurStart + nMatched ), text[nMatched], (flags & FR_MATCHCASE))) 1931 { 1932 if ((flags & FR_WHOLEWORD) && isalnumW(wLastChar)) 1933 break; 1934 1935 nMatched++; 1936 if (nMatched == nLen) 1937 { 1938 ME_DisplayItem *pNextItem = pCurItem; 1939 int nNextStart = nCurStart; 1940 WCHAR wNextChar; 1941 1942 /* Check to see if next character is a whitespace */ 1943 if (flags & FR_WHOLEWORD) 1944 { 1945 if (nCurStart + nMatched == pCurItem->member.run.len) 1946 { 1947 pNextItem = ME_FindItemFwd(pCurItem, diRun); 1948 nNextStart = -nMatched; 1949 } 1950 1951 if (pNextItem) 1952 wNextChar = *get_text( &pNextItem->member.run, nNextStart + nMatched ); 1953 else 1954 wNextChar = ' '; 1955 1956 if (isalnumW(wNextChar)) 1957 break; 1958 } 1959 1960 cursor.nOffset += cursor.pPara->member.para.nCharOfs + cursor.pRun->member.run.nCharOfs; 1961 if (chrgText) 1962 { 1963 chrgText->cpMin = cursor.nOffset; 1964 chrgText->cpMax = cursor.nOffset + nLen; 1965 } 1966 TRACE("found at %d-%d\n", cursor.nOffset, cursor.nOffset + nLen); 1967 return cursor.nOffset; 1968 } 1969 if (nCurStart + nMatched == pCurItem->member.run.len) 1970 { 1971 pCurItem = ME_FindItemFwd(pCurItem, diRun); 1972 nCurStart = -nMatched; 1973 } 1974 } 1975 if (pCurItem) 1976 wLastChar = *get_text( &pCurItem->member.run, nCurStart + nMatched ); 1977 else 1978 wLastChar = ' '; 1979 1980 cursor.nOffset++; 1981 if (cursor.nOffset == cursor.pRun->member.run.len) 1982 { 1983 ME_NextRun(&cursor.pPara, &cursor.pRun, TRUE); 1984 cursor.nOffset = 0; 1985 } 1986 } 1987 } 1988 else /* Backward search */ 1989 { 1990 /* If possible, find the character after where the search ends */ 1991 if ((flags & FR_WHOLEWORD) && nMax < nTextLen - 1) 1992 { 1993 ME_CursorFromCharOfs(editor, nMax + 1, &cursor); 1994 wLastChar = *get_text( &cursor.pRun->member.run, cursor.nOffset ); 1995 ME_MoveCursorChars(editor, &cursor, -1, FALSE); 1996 } else { 1997 ME_CursorFromCharOfs(editor, nMax, &cursor); 1998 } 1999 2000 while (cursor.pRun && ME_GetCursorOfs(&cursor) - nLen >= nMin) 2001 { 2002 ME_DisplayItem *pCurItem = cursor.pRun; 2003 ME_DisplayItem *pCurPara = cursor.pPara; 2004 int nCurEnd = cursor.nOffset; 2005 int nMatched = 0; 2006 2007 if (nCurEnd == 0) 2008 { 2009 ME_PrevRun(&pCurPara, &pCurItem, TRUE); 2010 nCurEnd = pCurItem->member.run.len; 2011 } 2012 2013 while (pCurItem && ME_CharCompare( *get_text( &pCurItem->member.run, nCurEnd - nMatched - 1 ), 2014 text[nLen - nMatched - 1], (flags & FR_MATCHCASE) )) 2015 { 2016 if ((flags & FR_WHOLEWORD) && isalnumW(wLastChar)) 2017 break; 2018 2019 nMatched++; 2020 if (nMatched == nLen) 2021 { 2022 ME_DisplayItem *pPrevItem = pCurItem; 2023 int nPrevEnd = nCurEnd; 2024 WCHAR wPrevChar; 2025 int nStart; 2026 2027 /* Check to see if previous character is a whitespace */ 2028 if (flags & FR_WHOLEWORD) 2029 { 2030 if (nPrevEnd - nMatched == 0) 2031 { 2032 pPrevItem = ME_FindItemBack(pCurItem, diRun); 2033 if (pPrevItem) 2034 nPrevEnd = pPrevItem->member.run.len + nMatched; 2035 } 2036 2037 if (pPrevItem) 2038 wPrevChar = *get_text( &pPrevItem->member.run, nPrevEnd - nMatched - 1 ); 2039 else 2040 wPrevChar = ' '; 2041 2042 if (isalnumW(wPrevChar)) 2043 break; 2044 } 2045 2046 nStart = pCurPara->member.para.nCharOfs 2047 + pCurItem->member.run.nCharOfs + nCurEnd - nMatched; 2048 if (chrgText) 2049 { 2050 chrgText->cpMin = nStart; 2051 chrgText->cpMax = nStart + nLen; 2052 } 2053 TRACE("found at %d-%d\n", nStart, nStart + nLen); 2054 return nStart; 2055 } 2056 if (nCurEnd - nMatched == 0) 2057 { 2058 ME_PrevRun(&pCurPara, &pCurItem, TRUE); 2059 /* Don't care about pCurItem becoming NULL here; it's already taken 2060 * care of in the exterior loop condition */ 2061 nCurEnd = pCurItem->member.run.len + nMatched; 2062 } 2063 } 2064 if (pCurItem) 2065 wLastChar = *get_text( &pCurItem->member.run, nCurEnd - nMatched - 1 ); 2066 else 2067 wLastChar = ' '; 2068 2069 cursor.nOffset--; 2070 if (cursor.nOffset < 0) 2071 { 2072 ME_PrevRun(&cursor.pPara, &cursor.pRun, TRUE); 2073 cursor.nOffset = cursor.pRun->member.run.len; 2074 } 2075 } 2076 } 2077 TRACE("not found\n"); 2078 if (chrgText) 2079 chrgText->cpMin = chrgText->cpMax = -1; 2080 return -1; 2081 } 2082 2083 static int ME_GetTextEx(ME_TextEditor *editor, GETTEXTEX *ex, LPARAM pText) 2084 { 2085 int nChars; 2086 ME_Cursor start; 2087 2088 if (!ex->cb || !pText) return 0; 2089 2090 if (ex->flags & ~(GT_SELECTION | GT_USECRLF)) 2091 FIXME("GETTEXTEX flags 0x%08x not supported\n", ex->flags & ~(GT_SELECTION | GT_USECRLF)); 2092 2093 if (ex->flags & GT_SELECTION) 2094 { 2095 int from, to; 2096 int nStartCur = ME_GetSelectionOfs(editor, &from, &to); 2097 start = editor->pCursors[nStartCur]; 2098 nChars = to - from; 2099 } 2100 else 2101 { 2102 ME_SetCursorToStart(editor, &start); 2103 nChars = INT_MAX; 2104 } 2105 if (ex->codepage == CP_UNICODE) 2106 { 2107 return ME_GetTextW(editor, (LPWSTR)pText, ex->cb / sizeof(WCHAR) - 1, 2108 &start, nChars, ex->flags & GT_USECRLF, FALSE); 2109 } 2110 else 2111 { 2112 /* potentially each char may be a CR, why calculate the exact value with O(N) when 2113 we can just take a bigger buffer? :) 2114 The above assumption still holds with CR/LF counters, since CR->CRLF expansion 2115 occurs only in richedit 2.0 mode, in which line breaks have only one CR 2116 */ 2117 int crlfmul = (ex->flags & GT_USECRLF) ? 2 : 1; 2118 DWORD buflen; 2119 LPWSTR buffer; 2120 LRESULT rc; 2121 2122 buflen = min(crlfmul * nChars, ex->cb - 1); 2123 buffer = heap_alloc((buflen + 1) * sizeof(WCHAR)); 2124 2125 nChars = ME_GetTextW(editor, buffer, buflen, &start, nChars, ex->flags & GT_USECRLF, FALSE); 2126 rc = WideCharToMultiByte(ex->codepage, 0, buffer, nChars + 1, 2127 (LPSTR)pText, ex->cb, ex->lpDefaultChar, ex->lpUsedDefChar); 2128 if (rc) rc--; /* do not count 0 terminator */ 2129 2130 heap_free(buffer); 2131 return rc; 2132 } 2133 } 2134 2135 static int ME_GetTextRange(ME_TextEditor *editor, WCHAR *strText, 2136 const ME_Cursor *start, int nLen, BOOL unicode) 2137 { 2138 if (!strText) return 0; 2139 if (unicode) { 2140 return ME_GetTextW(editor, strText, INT_MAX, start, nLen, FALSE, FALSE); 2141 } else { 2142 int nChars; 2143 WCHAR *p = heap_alloc((nLen+1) * sizeof(*p)); 2144 if (!p) return 0; 2145 nChars = ME_GetTextW(editor, p, nLen, start, nLen, FALSE, FALSE); 2146 WideCharToMultiByte(CP_ACP, 0, p, nChars+1, (char *)strText, 2147 nLen+1, NULL, NULL); 2148 heap_free(p); 2149 return nChars; 2150 } 2151 } 2152 2153 static int handle_EM_EXSETSEL( ME_TextEditor *editor, int to, int from ) 2154 { 2155 int end; 2156 2157 TRACE("%d - %d\n", to, from ); 2158 2159 ME_InvalidateSelection( editor ); 2160 end = ME_SetSelection( editor, to, from ); 2161 ME_InvalidateSelection( editor ); 2162 ITextHost_TxShowCaret( editor->texthost, FALSE ); 2163 ME_ShowCaret( editor ); 2164 ME_SendSelChange( editor ); 2165 2166 return end; 2167 } 2168 2169 typedef struct tagME_GlobalDestStruct 2170 { 2171 HGLOBAL hData; 2172 int nLength; 2173 } ME_GlobalDestStruct; 2174 2175 static DWORD CALLBACK ME_ReadFromHGLOBALUnicode(DWORD_PTR dwCookie, LPBYTE lpBuff, LONG cb, LONG *pcb) 2176 { 2177 ME_GlobalDestStruct *pData = (ME_GlobalDestStruct *)dwCookie; 2178 int i; 2179 WORD *pSrc, *pDest; 2180 2181 cb = cb >> 1; 2182 pDest = (WORD *)lpBuff; 2183 pSrc = GlobalLock(pData->hData); 2184 for (i = 0; i<cb && pSrc[pData->nLength+i]; i++) { 2185 pDest[i] = pSrc[pData->nLength+i]; 2186 } 2187 pData->nLength += i; 2188 *pcb = 2*i; 2189 GlobalUnlock(pData->hData); 2190 return 0; 2191 } 2192 2193 static DWORD CALLBACK ME_ReadFromHGLOBALRTF(DWORD_PTR dwCookie, LPBYTE lpBuff, LONG cb, LONG *pcb) 2194 { 2195 ME_GlobalDestStruct *pData = (ME_GlobalDestStruct *)dwCookie; 2196 int i; 2197 BYTE *pSrc, *pDest; 2198 2199 pDest = lpBuff; 2200 pSrc = GlobalLock(pData->hData); 2201 for (i = 0; i<cb && pSrc[pData->nLength+i]; i++) { 2202 pDest[i] = pSrc[pData->nLength+i]; 2203 } 2204 pData->nLength += i; 2205 *pcb = i; 2206 GlobalUnlock(pData->hData); 2207 return 0; 2208 } 2209 2210 static const WCHAR rtfW[] = {'R','i','c','h',' ','T','e','x','t',' ','F','o','r','m','a','t',0}; 2211 2212 static HRESULT paste_rtf(ME_TextEditor *editor, FORMATETC *fmt, STGMEDIUM *med) 2213 { 2214 EDITSTREAM es; 2215 ME_GlobalDestStruct gds; 2216 HRESULT hr; 2217 2218 gds.hData = med->u.hGlobal; 2219 gds.nLength = 0; 2220 es.dwCookie = (DWORD_PTR)&gds; 2221 es.pfnCallback = ME_ReadFromHGLOBALRTF; 2222 hr = ME_StreamIn( editor, SF_RTF | SFF_SELECTION, &es, FALSE ) == 0 ? E_FAIL : S_OK; 2223 ReleaseStgMedium( med ); 2224 return hr; 2225 } 2226 2227 static HRESULT paste_text(ME_TextEditor *editor, FORMATETC *fmt, STGMEDIUM *med) 2228 { 2229 EDITSTREAM es; 2230 ME_GlobalDestStruct gds; 2231 HRESULT hr; 2232 2233 gds.hData = med->u.hGlobal; 2234 gds.nLength = 0; 2235 es.dwCookie = (DWORD_PTR)&gds; 2236 es.pfnCallback = ME_ReadFromHGLOBALUnicode; 2237 hr = ME_StreamIn( editor, SF_TEXT | SF_UNICODE | SFF_SELECTION, &es, FALSE ) == 0 ? E_FAIL : S_OK; 2238 ReleaseStgMedium( med ); 2239 return hr; 2240 } 2241 2242 static HRESULT paste_emf(ME_TextEditor *editor, FORMATETC *fmt, STGMEDIUM *med) 2243 { 2244 HRESULT hr; 2245 SIZEL sz = {0, 0}; 2246 2247 hr = insert_static_object( editor, med->u.hEnhMetaFile, NULL, &sz ); 2248 if (SUCCEEDED(hr)) 2249 { 2250 ME_CommitUndo( editor ); 2251 ME_UpdateRepaint( editor, FALSE ); 2252 } 2253 else 2254 ReleaseStgMedium( med ); 2255 2256 return hr; 2257 } 2258 2259 static struct paste_format 2260 { 2261 FORMATETC fmt; 2262 HRESULT (*paste)(ME_TextEditor *, FORMATETC *, STGMEDIUM *); 2263 const WCHAR *name; 2264 } paste_formats[] = 2265 { 2266 {{ -1, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }, paste_rtf, rtfW }, 2267 {{ CF_UNICODETEXT, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }, paste_text }, 2268 {{ CF_ENHMETAFILE, NULL, DVASPECT_CONTENT, -1, TYMED_ENHMF }, paste_emf }, 2269 {{ 0 }} 2270 }; 2271 2272 static void init_paste_formats(void) 2273 { 2274 struct paste_format *format; 2275 static int done; 2276 2277 if (!done) 2278 { 2279 for (format = paste_formats; format->fmt.cfFormat; format++) 2280 { 2281 if (format->name) 2282 format->fmt.cfFormat = RegisterClipboardFormatW( format->name ); 2283 } 2284 done = 1; 2285 } 2286 } 2287 2288 static BOOL paste_special(ME_TextEditor *editor, UINT cf, REPASTESPECIAL *ps, BOOL check_only) 2289 { 2290 HRESULT hr; 2291 STGMEDIUM med; 2292 struct paste_format *format; 2293 IDataObject *data; 2294 2295 /* Protect read-only edit control from modification */ 2296 if (editor->styleFlags & ES_READONLY) 2297 { 2298 if (!check_only) 2299 MessageBeep(MB_ICONERROR); 2300 return FALSE; 2301 } 2302 2303 init_paste_formats(); 2304 2305 if (ps && ps->dwAspect != DVASPECT_CONTENT) 2306 FIXME("Ignoring aspect %x\n", ps->dwAspect); 2307 2308 hr = OleGetClipboard( &data ); 2309 if (hr != S_OK) return FALSE; 2310 2311 if (cf == CF_TEXT) cf = CF_UNICODETEXT; 2312 2313 hr = S_FALSE; 2314 for (format = paste_formats; format->fmt.cfFormat; format++) 2315 { 2316 if (cf && cf != format->fmt.cfFormat) continue; 2317 hr = IDataObject_QueryGetData( data, &format->fmt ); 2318 if (hr == S_OK) 2319 { 2320 if (!check_only) 2321 { 2322 hr = IDataObject_GetData( data, &format->fmt, &med ); 2323 if (hr != S_OK) goto done; 2324 hr = format->paste( editor, &format->fmt, &med ); 2325 } 2326 break; 2327 } 2328 } 2329 2330 done: 2331 IDataObject_Release( data ); 2332 2333 return hr == S_OK; 2334 } 2335 2336 static BOOL ME_Copy(ME_TextEditor *editor, const ME_Cursor *start, int nChars) 2337 { 2338 LPDATAOBJECT dataObj = NULL; 2339 HRESULT hr = S_OK; 2340 2341 if (editor->cPasswordMask) 2342 return FALSE; /* Copying or Cutting masked text isn't allowed */ 2343 2344 if(editor->lpOleCallback) 2345 { 2346 CHARRANGE range; 2347 range.cpMin = ME_GetCursorOfs(start); 2348 range.cpMax = range.cpMin + nChars; 2349 hr = IRichEditOleCallback_GetClipboardData(editor->lpOleCallback, &range, RECO_COPY, &dataObj); 2350 } 2351 if(FAILED(hr) || !dataObj) 2352 hr = ME_GetDataObject(editor, start, nChars, &dataObj); 2353 if(SUCCEEDED(hr)) { 2354 hr = OleSetClipboard(dataObj); 2355 IDataObject_Release(dataObj); 2356 } 2357 return SUCCEEDED(hr); 2358 } 2359 2360 static BOOL copy_or_cut(ME_TextEditor *editor, BOOL cut) 2361 { 2362 BOOL result; 2363 int offs, num_chars; 2364 int start_cursor = ME_GetSelectionOfs(editor, &offs, &num_chars); 2365 ME_Cursor *sel_start = &editor->pCursors[start_cursor]; 2366 2367 if (cut && (editor->styleFlags & ES_READONLY)) 2368 { 2369 MessageBeep(MB_ICONERROR); 2370 return FALSE; 2371 } 2372 2373 num_chars -= offs; 2374 result = ME_Copy(editor, sel_start, num_chars); 2375 if (result && cut) 2376 { 2377 ME_InternalDeleteText(editor, sel_start, num_chars, FALSE); 2378 ME_CommitUndo(editor); 2379 ME_UpdateRepaint(editor, TRUE); 2380 } 2381 return result; 2382 } 2383 2384 /* helper to send a msg filter notification */ 2385 static BOOL 2386 ME_FilterEvent(ME_TextEditor *editor, UINT msg, WPARAM* wParam, LPARAM* lParam) 2387 { 2388 MSGFILTER msgf; 2389 2390 if (!editor->hWnd || !editor->hwndParent) return FALSE; 2391 msgf.nmhdr.hwndFrom = editor->hWnd; 2392 msgf.nmhdr.idFrom = GetWindowLongW(editor->hWnd, GWLP_ID); 2393 msgf.nmhdr.code = EN_MSGFILTER; 2394 msgf.msg = msg; 2395 msgf.wParam = *wParam; 2396 msgf.lParam = *lParam; 2397 if (SendMessageW(editor->hwndParent, WM_NOTIFY, msgf.nmhdr.idFrom, (LPARAM)&msgf)) 2398 return FALSE; 2399 *wParam = msgf.wParam; 2400 *lParam = msgf.lParam; 2401 msgf.wParam = *wParam; 2402 2403 return TRUE; 2404 } 2405 2406 static void ME_UpdateSelectionLinkAttribute(ME_TextEditor *editor) 2407 { 2408 ME_DisplayItem *startPara, *endPara; 2409 ME_DisplayItem *prev_para; 2410 ME_Cursor *from, *to; 2411 ME_Cursor start; 2412 int nChars; 2413 2414 if (!editor->AutoURLDetect_bEnable) return; 2415 2416 ME_GetSelection(editor, &from, &to); 2417 2418 /* Find paragraph previous to the one that contains start cursor */ 2419 startPara = from->pPara; 2420 prev_para = startPara->member.para.prev_para; 2421 if (prev_para->type == diParagraph) startPara = prev_para; 2422 2423 /* Find paragraph that contains end cursor */ 2424 endPara = to->pPara->member.para.next_para; 2425 2426 start.pPara = startPara; 2427 start.pRun = ME_FindItemFwd(startPara, diRun); 2428 start.nOffset = 0; 2429 nChars = endPara->member.para.nCharOfs - startPara->member.para.nCharOfs; 2430 2431 ME_UpdateLinkAttribute(editor, &start, nChars); 2432 } 2433 2434 static BOOL handle_enter(ME_TextEditor *editor) 2435 { 2436 BOOL ctrl_is_down = GetKeyState(VK_CONTROL) & 0x8000; 2437 BOOL shift_is_down = GetKeyState(VK_SHIFT) & 0x8000; 2438 2439 if (editor->bDialogMode) 2440 { 2441 if (ctrl_is_down) 2442 return TRUE; 2443 2444 if (!(editor->styleFlags & ES_WANTRETURN)) 2445 { 2446 if (editor->hwndParent) 2447 { 2448 DWORD dw; 2449 dw = SendMessageW(editor->hwndParent, DM_GETDEFID, 0, 0); 2450 if (HIWORD(dw) == DC_HASDEFID) 2451 { 2452 HWND hwDefCtrl = GetDlgItem(editor->hwndParent, LOWORD(dw)); 2453 if (hwDefCtrl) 2454 { 2455 SendMessageW(editor->hwndParent, WM_NEXTDLGCTL, (WPARAM)hwDefCtrl, TRUE); 2456 PostMessageW(hwDefCtrl, WM_KEYDOWN, VK_RETURN, 0); 2457 } 2458 } 2459 } 2460 return TRUE; 2461 } 2462 } 2463 2464 if (editor->styleFlags & ES_MULTILINE) 2465 { 2466 static const WCHAR endl = '\r'; 2467 static const WCHAR endlv10[] = {'\r','\n'}; 2468 ME_Cursor cursor = editor->pCursors[0]; 2469 ME_DisplayItem *para = cursor.pPara; 2470 int from, to; 2471 ME_Style *style, *eop_style; 2472 2473 if (editor->styleFlags & ES_READONLY) 2474 { 2475 MessageBeep(MB_ICONERROR); 2476 return TRUE; 2477 } 2478 2479 ME_GetSelectionOfs(editor, &from, &to); 2480 if (editor->nTextLimit > ME_GetTextLength(editor) - (to-from)) 2481 { 2482 if (!editor->bEmulateVersion10) /* v4.1 */ 2483 { 2484 if (para->member.para.nFlags & MEPF_ROWEND) 2485 { 2486 /* Add a new table row after this row. */ 2487 para = ME_AppendTableRow(editor, para); 2488 para = para->member.para.next_para; 2489 editor->pCursors[0].pPara = para; 2490 editor->pCursors[0].pRun = ME_FindItemFwd(para, diRun); 2491 editor->pCursors[0].nOffset = 0; 2492 editor->pCursors[1] = editor->pCursors[0]; 2493 ME_CommitUndo(editor); 2494 ME_CheckTablesForCorruption(editor); 2495 ME_UpdateRepaint(editor, FALSE); 2496 return TRUE; 2497 } 2498 else if (para == editor->pCursors[1].pPara && 2499 cursor.nOffset + cursor.pRun->member.run.nCharOfs == 0 && 2500 para->member.para.prev_para->member.para.nFlags & MEPF_ROWSTART && 2501 !para->member.para.prev_para->member.para.nCharOfs) 2502 { 2503 /* Insert a newline before the table. */ 2504 para = para->member.para.prev_para; 2505 para->member.para.nFlags &= ~MEPF_ROWSTART; 2506 editor->pCursors[0].pPara = para; 2507 editor->pCursors[0].pRun = ME_FindItemFwd(para, diRun); 2508 editor->pCursors[1] = editor->pCursors[0]; 2509 ME_InsertTextFromCursor(editor, 0, &endl, 1, 2510 editor->pCursors[0].pRun->member.run.style); 2511 para = editor->pBuffer->pFirst->member.para.next_para; 2512 ME_SetDefaultParaFormat(editor, ¶->member.para.fmt); 2513 para->member.para.nFlags = 0; 2514 mark_para_rewrap(editor, para); 2515 editor->pCursors[0].pPara = para; 2516 editor->pCursors[0].pRun = ME_FindItemFwd(para, diRun); 2517 editor->pCursors[1] = editor->pCursors[0]; 2518 para->member.para.next_para->member.para.nFlags |= MEPF_ROWSTART; 2519 ME_CommitCoalescingUndo(editor); 2520 ME_CheckTablesForCorruption(editor); 2521 ME_UpdateRepaint(editor, FALSE); 2522 return TRUE; 2523 } 2524 } 2525 else /* v1.0 - 3.0 */ 2526 { 2527 ME_DisplayItem *para = cursor.pPara; 2528 if (ME_IsInTable(para)) 2529 { 2530 if (cursor.pRun->member.run.nFlags & MERF_ENDPARA) 2531 { 2532 if (from == to) 2533 { 2534 ME_ContinueCoalescingTransaction(editor); 2535 para = ME_AppendTableRow(editor, para); 2536 editor->pCursors[0].pPara = para; 2537 editor->pCursors[0].pRun = ME_FindItemFwd(para, diRun); 2538 editor->pCursors[0].nOffset = 0; 2539 editor->pCursors[1] = editor->pCursors[0]; 2540 ME_CommitCoalescingUndo(editor); 2541 ME_UpdateRepaint(editor, FALSE); 2542 return TRUE; 2543 } 2544 } 2545 else 2546 { 2547 ME_ContinueCoalescingTransaction(editor); 2548 if (cursor.pRun->member.run.nCharOfs + cursor.nOffset == 0 && 2549 !ME_IsInTable(para->member.para.prev_para)) 2550 { 2551 /* Insert newline before table */ 2552 cursor.pRun = ME_FindItemBack(para, diRun); 2553 if (cursor.pRun) 2554 { 2555 editor->pCursors[0].pRun = cursor.pRun; 2556 editor->pCursors[0].pPara = para->member.para.prev_para; 2557 } 2558 editor->pCursors[0].nOffset = 0; 2559 editor->pCursors[1] = editor->pCursors[0]; 2560 ME_InsertTextFromCursor(editor, 0, &endl, 1, 2561 editor->pCursors[0].pRun->member.run.style); 2562 } 2563 else 2564 { 2565 editor->pCursors[1] = editor->pCursors[0]; 2566 para = ME_AppendTableRow(editor, para); 2567 editor->pCursors[0].pPara = para; 2568 editor->pCursors[0].pRun = ME_FindItemFwd(para, diRun); 2569 editor->pCursors[0].nOffset = 0; 2570 editor->pCursors[1] = editor->pCursors[0]; 2571 } 2572 ME_CommitCoalescingUndo(editor); 2573 ME_UpdateRepaint(editor, FALSE); 2574 return TRUE; 2575 } 2576 } 2577 } 2578 2579 style = ME_GetInsertStyle(editor, 0); 2580 2581 /* Normally the new eop style is the insert style, however in a list it is copied from the existing 2582 eop style (this prevents the list label style changing when the new eop is inserted). 2583 No extra ref is taken here on eop_style. */ 2584 if (para->member.para.fmt.wNumbering) 2585 eop_style = para->member.para.eop_run->style; 2586 else 2587 eop_style = style; 2588 ME_ContinueCoalescingTransaction(editor); 2589 if (shift_is_down) 2590 ME_InsertEndRowFromCursor(editor, 0); 2591 else 2592 if (!editor->bEmulateVersion10) 2593 ME_InsertTextFromCursor(editor, 0, &endl, 1, eop_style); 2594 else 2595 ME_InsertTextFromCursor(editor, 0, endlv10, 2, eop_style); 2596 ME_CommitCoalescingUndo(editor); 2597 SetCursor(NULL); 2598 2599 ME_UpdateSelectionLinkAttribute(editor); 2600 ME_UpdateRepaint(editor, FALSE); 2601 ME_SaveTempStyle(editor, style); /* set the temp insert style for the new para */ 2602 ME_ReleaseStyle(style); 2603 } 2604 return TRUE; 2605 } 2606 return FALSE; 2607 } 2608 2609 static BOOL 2610 ME_KeyDown(ME_TextEditor *editor, WORD nKey) 2611 { 2612 BOOL ctrl_is_down = GetKeyState(VK_CONTROL) & 0x8000; 2613 BOOL shift_is_down = GetKeyState(VK_SHIFT) & 0x8000; 2614 2615 if (editor->bMouseCaptured) 2616 return FALSE; 2617 if (nKey != VK_SHIFT && nKey != VK_CONTROL && nKey != VK_MENU) 2618 editor->nSelectionType = stPosition; 2619 2620 switch (nKey) 2621 { 2622 case VK_LEFT: 2623 case VK_RIGHT: 2624 case VK_HOME: 2625 case VK_END: 2626 editor->nUDArrowX = -1; 2627 /* fall through */ 2628 case VK_UP: 2629 case VK_DOWN: 2630 case VK_PRIOR: 2631 case VK_NEXT: 2632 ME_CommitUndo(editor); /* End coalesced undos for typed characters */ 2633 ME_ArrowKey(editor, nKey, shift_is_down, ctrl_is_down); 2634 return TRUE; 2635 case VK_BACK: 2636 case VK_DELETE: 2637 editor->nUDArrowX = -1; 2638 /* FIXME backspace and delete aren't the same, they act different wrt paragraph style of the merged paragraph */ 2639 if (editor->styleFlags & ES_READONLY) 2640 return FALSE; 2641 if (ME_IsSelection(editor)) 2642 { 2643 ME_DeleteSelection(editor); 2644 ME_CommitUndo(editor); 2645 } 2646 else if (nKey == VK_DELETE) 2647 { 2648 /* Delete stops group typing. 2649 * (See MSDN remarks on EM_STOPGROUPTYPING message) */ 2650 ME_DeleteTextAtCursor(editor, 1, 1); 2651 ME_CommitUndo(editor); 2652 } 2653 else if (ME_ArrowKey(editor, VK_LEFT, FALSE, FALSE)) 2654 { 2655 BOOL bDeletionSucceeded; 2656 /* Backspace can be grouped for a single undo */ 2657 ME_ContinueCoalescingTransaction(editor); 2658 bDeletionSucceeded = ME_DeleteTextAtCursor(editor, 1, 1); 2659 if (!bDeletionSucceeded && !editor->bEmulateVersion10) { /* v4.1 */ 2660 /* Deletion was prevented so the cursor is moved back to where it was. 2661 * (e.g. this happens when trying to delete cell boundaries) 2662 */ 2663 ME_ArrowKey(editor, VK_RIGHT, FALSE, FALSE); 2664 } 2665 ME_CommitCoalescingUndo(editor); 2666 } 2667 else 2668 return TRUE; 2669 ME_MoveCursorFromTableRowStartParagraph(editor); 2670 ME_UpdateSelectionLinkAttribute(editor); 2671 ME_UpdateRepaint(editor, FALSE); 2672 ME_SendRequestResize(editor, FALSE); 2673 return TRUE; 2674 case VK_RETURN: 2675 if (!editor->bEmulateVersion10) 2676 return handle_enter(editor); 2677 break; 2678 case VK_ESCAPE: 2679 if (editor->bDialogMode && editor->hwndParent) 2680 PostMessageW(editor->hwndParent, WM_CLOSE, 0, 0); 2681 return TRUE; 2682 case VK_TAB: 2683 if (editor->bDialogMode && editor->hwndParent) 2684 SendMessageW(editor->hwndParent, WM_NEXTDLGCTL, shift_is_down, 0); 2685 return TRUE; 2686 case 'A': 2687 if (ctrl_is_down) 2688 { 2689 handle_EM_EXSETSEL( editor, 0, -1 ); 2690 return TRUE; 2691 } 2692 break; 2693 case 'V': 2694 if (ctrl_is_down) 2695 return paste_special( editor, 0, NULL, FALSE ); 2696 break; 2697 case 'C': 2698 case 'X': 2699 if (ctrl_is_down) 2700 return copy_or_cut(editor, nKey == 'X'); 2701 break; 2702 case 'Z': 2703 if (ctrl_is_down) 2704 { 2705 ME_Undo(editor); 2706 return TRUE; 2707 } 2708 break; 2709 case 'Y': 2710 if (ctrl_is_down) 2711 { 2712 ME_Redo(editor); 2713 return TRUE; 2714 } 2715 break; 2716 2717 default: 2718 if (nKey != VK_SHIFT && nKey != VK_CONTROL && nKey && nKey != VK_MENU) 2719 editor->nUDArrowX = -1; 2720 if (ctrl_is_down) 2721 { 2722 if (nKey == 'W') 2723 { 2724 CHARFORMAT2W chf; 2725 char buf[2048]; 2726 chf.cbSize = sizeof(chf); 2727 2728 ME_GetSelectionCharFormat(editor, &chf); 2729 ME_DumpStyleToBuf(&chf, buf); 2730 MessageBoxA(NULL, buf, "Style dump", MB_OK); 2731 } 2732 if (nKey == 'Q') 2733 { 2734 ME_CheckCharOffsets(editor); 2735 } 2736 } 2737 } 2738 return FALSE; 2739 } 2740 2741 static LRESULT ME_Char(ME_TextEditor *editor, WPARAM charCode, 2742 LPARAM flags, BOOL unicode) 2743 { 2744 WCHAR wstr; 2745 2746 if (editor->bMouseCaptured) 2747 return 0; 2748 2749 if (editor->styleFlags & ES_READONLY) 2750 { 2751 MessageBeep(MB_ICONERROR); 2752 return 0; /* FIXME really 0 ? */ 2753 } 2754 2755 if (unicode) 2756 wstr = (WCHAR)charCode; 2757 else 2758 { 2759 CHAR charA = charCode; 2760 MultiByteToWideChar(CP_ACP, 0, &charA, 1, &wstr, 1); 2761 } 2762 2763 if (editor->bEmulateVersion10 && wstr == '\r') 2764 handle_enter(editor); 2765 2766 if ((unsigned)wstr >= ' ' || wstr == '\t') 2767 { 2768 ME_Cursor cursor = editor->pCursors[0]; 2769 ME_DisplayItem *para = cursor.pPara; 2770 int from, to; 2771 BOOL ctrl_is_down = GetKeyState(VK_CONTROL) & 0x8000; 2772 ME_GetSelectionOfs(editor, &from, &to); 2773 if (wstr == '\t' && 2774 /* v4.1 allows tabs to be inserted with ctrl key down */ 2775 !(ctrl_is_down && !editor->bEmulateVersion10)) 2776 { 2777 ME_DisplayItem *para; 2778 BOOL bSelectedRow = FALSE; 2779 2780 para = cursor.pPara; 2781 if (ME_IsSelection(editor) && 2782 cursor.pRun->member.run.nCharOfs + cursor.nOffset == 0 && 2783 to == ME_GetCursorOfs(&editor->pCursors[0]) && 2784 para->member.para.prev_para->type == diParagraph) 2785 { 2786 para = para->member.para.prev_para; 2787 bSelectedRow = TRUE; 2788 } 2789 if (ME_IsInTable(para)) 2790 { 2791 ME_TabPressedInTable(editor, bSelectedRow); 2792 ME_CommitUndo(editor); 2793 return 0; 2794 } 2795 } else if (!editor->bEmulateVersion10) { /* v4.1 */ 2796 if (para->member.para.nFlags & MEPF_ROWEND) { 2797 if (from == to) { 2798 para = para->member.para.next_para; 2799 if (para->member.para.nFlags & MEPF_ROWSTART) 2800 para = para->member.para.next_para; 2801 editor->pCursors[0].pPara = para; 2802 editor->pCursors[0].pRun = ME_FindItemFwd(para, diRun); 2803 editor->pCursors[0].nOffset = 0; 2804 editor->pCursors[1] = editor->pCursors[0]; 2805 } 2806 } 2807 } else { /* v1.0 - 3.0 */ 2808 if (ME_IsInTable(cursor.pRun) && 2809 cursor.pRun->member.run.nFlags & MERF_ENDPARA && 2810 from == to) 2811 { 2812 /* Text should not be inserted at the end of the table. */ 2813 MessageBeep(-1); 2814 return 0; 2815 } 2816 } 2817 /* FIXME maybe it would make sense to call EM_REPLACESEL instead ? */ 2818 /* WM_CHAR is restricted to nTextLimit */ 2819 if(editor->nTextLimit > ME_GetTextLength(editor) - (to-from)) 2820 { 2821 ME_Style *style = ME_GetInsertStyle(editor, 0); 2822 ME_ContinueCoalescingTransaction(editor); 2823 ME_InsertTextFromCursor(editor, 0, &wstr, 1, style); 2824 ME_ReleaseStyle(style); 2825 ME_CommitCoalescingUndo(editor); 2826 ITextHost_TxSetCursor(editor->texthost, NULL, FALSE); 2827 } 2828 2829 ME_UpdateSelectionLinkAttribute(editor); 2830 ME_UpdateRepaint(editor, FALSE); 2831 } 2832 return 0; 2833 } 2834 2835 /* Process the message and calculate the new click count. 2836 * 2837 * returns: The click count if it is mouse down event, else returns 0. */ 2838 static int ME_CalculateClickCount(ME_TextEditor *editor, UINT msg, WPARAM wParam, 2839 LPARAM lParam) 2840 { 2841 static int clickNum = 0; 2842 if (msg < WM_MOUSEFIRST || msg > WM_MOUSELAST) 2843 return 0; 2844 2845 if ((msg == WM_LBUTTONDBLCLK) || 2846 (msg == WM_RBUTTONDBLCLK) || 2847 (msg == WM_MBUTTONDBLCLK) || 2848 (msg == WM_XBUTTONDBLCLK)) 2849 { 2850 msg -= (WM_LBUTTONDBLCLK - WM_LBUTTONDOWN); 2851 } 2852 2853 if ((msg == WM_LBUTTONDOWN) || 2854 (msg == WM_RBUTTONDOWN) || 2855 (msg == WM_MBUTTONDOWN) || 2856 (msg == WM_XBUTTONDOWN)) 2857 { 2858 static MSG prevClickMsg; 2859 MSG clickMsg; 2860 /* Compare the editor instead of the hwnd so that the this 2861 * can still be done for windowless richedit controls. */ 2862 clickMsg.hwnd = (HWND)editor; 2863 clickMsg.message = msg; 2864 clickMsg.wParam = wParam; 2865 clickMsg.lParam = lParam; 2866 clickMsg.time = GetMessageTime(); 2867 clickMsg.pt.x = (short)LOWORD(lParam); 2868 clickMsg.pt.y = (short)HIWORD(lParam); 2869 if ((clickNum != 0) && 2870 (clickMsg.message == prevClickMsg.message) && 2871 (clickMsg.hwnd == prevClickMsg.hwnd) && 2872 (clickMsg.wParam == prevClickMsg.wParam) && 2873 (clickMsg.time - prevClickMsg.time < GetDoubleClickTime()) && 2874 (abs(clickMsg.pt.x - prevClickMsg.pt.x) < GetSystemMetrics(SM_CXDOUBLECLK)/2) && 2875 (abs(clickMsg.pt.y - prevClickMsg.pt.y) < GetSystemMetrics(SM_CYDOUBLECLK)/2)) 2876 { 2877 clickNum++; 2878 } else { 2879 clickNum = 1; 2880 } 2881 prevClickMsg = clickMsg; 2882 } else { 2883 return 0; 2884 } 2885 return clickNum; 2886 } 2887 2888 static BOOL is_link( ME_Run *run ) 2889 { 2890 return (run->style->fmt.dwMask & CFM_LINK) && (run->style->fmt.dwEffects & CFE_LINK); 2891 } 2892 2893 static BOOL ME_SetCursor(ME_TextEditor *editor) 2894 { 2895 ME_Cursor cursor; 2896 POINT pt; 2897 BOOL isExact; 2898 SCROLLBARINFO sbi; 2899 DWORD messagePos = GetMessagePos(); 2900 pt.x = (short)LOWORD(messagePos); 2901 pt.y = (short)HIWORD(messagePos); 2902 2903 if (editor->hWnd) 2904 { 2905 sbi.cbSize = sizeof(sbi); 2906 GetScrollBarInfo(editor->hWnd, OBJID_HSCROLL, &sbi); 2907 if (!(sbi.rgstate[0] & (STATE_SYSTEM_INVISIBLE|STATE_SYSTEM_OFFSCREEN)) && 2908 PtInRect(&sbi.rcScrollBar, pt)) 2909 { 2910 ITextHost_TxSetCursor(editor->texthost, 2911 LoadCursorW(NULL, (WCHAR*)IDC_ARROW), FALSE); 2912 return TRUE; 2913 } 2914 sbi.cbSize = sizeof(sbi); 2915 GetScrollBarInfo(editor->hWnd, OBJID_VSCROLL, &sbi); 2916 if (!(sbi.rgstate[0] & (STATE_SYSTEM_INVISIBLE|STATE_SYSTEM_OFFSCREEN)) && 2917 PtInRect(&sbi.rcScrollBar, pt)) 2918 { 2919 ITextHost_TxSetCursor(editor->texthost, 2920 LoadCursorW(NULL, (WCHAR*)IDC_ARROW), FALSE); 2921 return TRUE; 2922 } 2923 } 2924 ITextHost_TxScreenToClient(editor->texthost, &pt); 2925 2926 if (editor->nSelectionType == stLine && editor->bMouseCaptured) { 2927 ITextHost_TxSetCursor(editor->texthost, hLeft, FALSE); 2928 return TRUE; 2929 } 2930 if (!editor->bEmulateVersion10 /* v4.1 */ && 2931 pt.y < editor->rcFormat.top && 2932 pt.x < editor->rcFormat.left) 2933 { 2934 ITextHost_TxSetCursor(editor->texthost, hLeft, FALSE); 2935 return TRUE; 2936 } 2937 if (pt.y < editor->rcFormat.top || pt.y > editor->rcFormat.bottom) 2938 { 2939 if (editor->bEmulateVersion10) /* v1.0 - 3.0 */ 2940 ITextHost_TxSetCursor(editor->texthost, 2941 LoadCursorW(NULL, (WCHAR*)IDC_ARROW), FALSE); 2942 else /* v4.1 */ 2943 ITextHost_TxSetCursor(editor->texthost, 2944 LoadCursorW(NULL, (WCHAR*)IDC_IBEAM), TRUE); 2945 return TRUE; 2946 } 2947 if (pt.x < editor->rcFormat.left) 2948 { 2949 ITextHost_TxSetCursor(editor->texthost, hLeft, FALSE); 2950 return TRUE; 2951 } 2952 ME_CharFromPos(editor, pt.x, pt.y, &cursor, &isExact); 2953 if (isExact) 2954 { 2955 ME_Run *run; 2956 2957 run = &cursor.pRun->member.run; 2958 if (is_link( run )) 2959 { 2960 ITextHost_TxSetCursor(editor->texthost, 2961 LoadCursorW(NULL, (WCHAR*)IDC_HAND), 2962 FALSE); 2963 return TRUE; 2964 } 2965 2966 if (ME_IsSelection(editor)) 2967 { 2968 int selStart, selEnd; 2969 int offset = ME_GetCursorOfs(&cursor); 2970 2971 ME_GetSelectionOfs(editor, &selStart, &selEnd); 2972 if (selStart <= offset && selEnd >= offset) { 2973 ITextHost_TxSetCursor(editor->texthost, 2974 LoadCursorW(NULL, (WCHAR*)IDC_ARROW), 2975 FALSE); 2976 return TRUE; 2977 } 2978 } 2979 } 2980 ITextHost_TxSetCursor(editor->texthost, 2981 LoadCursorW(NULL, (WCHAR*)IDC_IBEAM), TRUE); 2982 return TRUE; 2983 } 2984 2985 static void ME_SetDefaultFormatRect(ME_TextEditor *editor) 2986 { 2987 ITextHost_TxGetClientRect(editor->texthost, &editor->rcFormat); 2988 editor->rcFormat.top += editor->exStyleFlags & WS_EX_CLIENTEDGE ? 1 : 0; 2989 editor->rcFormat.left += 1 + editor->selofs; 2990 editor->rcFormat.right -= 1; 2991 } 2992 2993 static LONG ME_GetSelectionType(ME_TextEditor *editor) 2994 { 2995 LONG sel_type = SEL_EMPTY; 2996 LONG start, end; 2997 2998 ME_GetSelectionOfs(editor, &start, &end); 2999 if (start == end) 3000 sel_type = SEL_EMPTY; 3001 else 3002 { 3003 LONG object_count = 0, character_count = 0; 3004 int i; 3005 3006 for (i = 0; i < end - start; i++) 3007 { 3008 ME_Cursor cursor; 3009 3010 ME_CursorFromCharOfs(editor, start + i, &cursor); 3011 if (cursor.pRun->member.run.reobj) 3012 object_count++; 3013 else 3014 character_count++; 3015 if (character_count >= 2 && object_count >= 2) 3016 return (SEL_TEXT | SEL_MULTICHAR | SEL_OBJECT | SEL_MULTIOBJECT); 3017 } 3018 if (character_count) 3019 { 3020 sel_type |= SEL_TEXT; 3021 if (character_count >= 2) 3022 sel_type |= SEL_MULTICHAR; 3023 } 3024 if (object_count) 3025 { 3026 sel_type |= SEL_OBJECT; 3027 if (object_count >= 2) 3028 sel_type |= SEL_MULTIOBJECT; 3029 } 3030 } 3031 return sel_type; 3032 } 3033 3034 static BOOL ME_ShowContextMenu(ME_TextEditor *editor, int x, int y) 3035 { 3036 CHARRANGE selrange; 3037 HMENU menu; 3038 int seltype; 3039 3040 if(!editor->lpOleCallback || !editor->hWnd) 3041 return FALSE; 3042 ME_GetSelectionOfs(editor, &selrange.cpMin, &selrange.cpMax); 3043 seltype = ME_GetSelectionType(editor); 3044 if(SUCCEEDED(IRichEditOleCallback_GetContextMenu(editor->lpOleCallback, seltype, NULL, &selrange, &menu))) 3045 { 3046 TrackPopupMenu(menu, TPM_LEFTALIGN | TPM_RIGHTBUTTON, x, y, 0, editor->hwndParent, NULL); 3047 DestroyMenu(menu); 3048 } 3049 return TRUE; 3050 } 3051 3052 ME_TextEditor *ME_MakeEditor(ITextHost *texthost, BOOL bEmulateVersion10) 3053 { 3054 ME_TextEditor *ed = heap_alloc(sizeof(*ed)); 3055 int i; 3056 DWORD props; 3057 LONG selbarwidth; 3058 3059 ed->hWnd = NULL; 3060 ed->hwndParent = NULL; 3061 ed->sizeWindow.cx = ed->sizeWindow.cy = 0; 3062 ed->texthost = texthost; 3063 ed->reOle = NULL; 3064 ed->bEmulateVersion10 = bEmulateVersion10; 3065 ed->styleFlags = 0; 3066 ed->exStyleFlags = 0; 3067 ed->first_marked_para = NULL; 3068 ed->total_rows = 0; 3069 ITextHost_TxGetPropertyBits(texthost, 3070 (TXTBIT_RICHTEXT|TXTBIT_MULTILINE| 3071 TXTBIT_READONLY|TXTBIT_USEPASSWORD| 3072 TXTBIT_HIDESELECTION|TXTBIT_SAVESELECTION| 3073 TXTBIT_AUTOWORDSEL|TXTBIT_VERTICAL| 3074 TXTBIT_WORDWRAP|TXTBIT_DISABLEDRAG), 3075 &props); 3076 ITextHost_TxGetScrollBars(texthost, &ed->styleFlags); 3077 ed->styleFlags &= (WS_VSCROLL|WS_HSCROLL|ES_AUTOVSCROLL| 3078 ES_AUTOHSCROLL|ES_DISABLENOSCROLL); 3079 ed->pBuffer = ME_MakeText(); 3080 ed->nZoomNumerator = ed->nZoomDenominator = 0; 3081 ed->nAvailWidth = 0; /* wrap to client area */ 3082 list_init( &ed->style_list ); 3083 ME_MakeFirstParagraph(ed); 3084 /* The four cursors are for: 3085 * 0 - The position where the caret is shown 3086 * 1 - The anchored end of the selection (for normal selection) 3087 * 2 & 3 - The anchored start and end respectively for word, line, 3088 * or paragraph selection. 3089 */ 3090 ed->nCursors = 4; 3091 ed->pCursors = heap_alloc(ed->nCursors * sizeof(*ed->pCursors)); 3092 ME_SetCursorToStart(ed, &ed->pCursors[0]); 3093 ed->pCursors[1] = ed->pCursors[0]; 3094 ed->pCursors[2] = ed->pCursors[0]; 3095 ed->pCursors[3] = ed->pCursors[1]; 3096 ed->nLastTotalLength = ed->nTotalLength = 0; 3097 ed->nLastTotalWidth = ed->nTotalWidth = 0; 3098 ed->nUDArrowX = -1; 3099 ed->rgbBackColor = -1; 3100 ed->hbrBackground = GetSysColorBrush(COLOR_WINDOW); 3101 ed->bCaretAtEnd = FALSE; 3102 ed->nEventMask = 0; 3103 ed->nModifyStep = 0; 3104 ed->nTextLimit = TEXT_LIMIT_DEFAULT; 3105 list_init( &ed->undo_stack ); 3106 list_init( &ed->redo_stack ); 3107 ed->nUndoStackSize = 0; 3108 ed->nUndoLimit = STACK_SIZE_DEFAULT; 3109 ed->nUndoMode = umAddToUndo; 3110 ed->nParagraphs = 1; 3111 ed->nLastSelStart = ed->nLastSelEnd = 0; 3112 ed->pLastSelStartPara = ed->pLastSelEndPara = ed->pCursors[0].pPara; 3113 ed->bHideSelection = FALSE; 3114 ed->pfnWordBreak = NULL; 3115 ed->lpOleCallback = NULL; 3116 ed->mode = TM_MULTILEVELUNDO | TM_MULTICODEPAGE; 3117 ed->mode |= (props & TXTBIT_RICHTEXT) ? TM_RICHTEXT : TM_PLAINTEXT; 3118 ed->AutoURLDetect_bEnable = FALSE; 3119 ed->bHaveFocus = FALSE; 3120 ed->bDialogMode = FALSE; 3121 ed->bMouseCaptured = FALSE; 3122 for (i=0; i<HFONT_CACHE_SIZE; i++) 3123 { 3124 ed->pFontCache[i].nRefs = 0; 3125 ed->pFontCache[i].nAge = 0; 3126 ed->pFontCache[i].hFont = NULL; 3127 } 3128 3129 ME_CheckCharOffsets(ed); 3130 SetRectEmpty(&ed->rcFormat); 3131 ed->bDefaultFormatRect = TRUE; 3132 ITextHost_TxGetSelectionBarWidth(ed->texthost, &selbarwidth); 3133 if (selbarwidth) { 3134 /* FIXME: Convert selbarwidth from HIMETRIC to pixels */ 3135 ed->selofs = SELECTIONBAR_WIDTH; 3136 ed->styleFlags |= ES_SELECTIONBAR; 3137 } else { 3138 ed->selofs = 0; 3139 } 3140 ed->nSelectionType = stPosition; 3141 3142 ed->cPasswordMask = 0; 3143 if (props & TXTBIT_USEPASSWORD) 3144 ITextHost_TxGetPasswordChar(texthost, &ed->cPasswordMask); 3145 3146 if (props & TXTBIT_AUTOWORDSEL) 3147 ed->styleFlags |= ECO_AUTOWORDSELECTION; 3148 if (props & TXTBIT_MULTILINE) { 3149 ed->styleFlags |= ES_MULTILINE; 3150 ed->bWordWrap = (props & TXTBIT_WORDWRAP) != 0; 3151 } else { 3152 ed->bWordWrap = FALSE; 3153 } 3154 if (props & TXTBIT_READONLY) 3155 ed->styleFlags |= ES_READONLY; 3156 if (!(props & TXTBIT_HIDESELECTION)) 3157 ed->styleFlags |= ES_NOHIDESEL; 3158 if (props & TXTBIT_SAVESELECTION) 3159 ed->styleFlags |= ES_SAVESEL; 3160 if (props & TXTBIT_VERTICAL) 3161 ed->styleFlags |= ES_VERTICAL; 3162 if (props & TXTBIT_DISABLEDRAG) 3163 ed->styleFlags |= ES_NOOLEDRAGDROP; 3164 3165 ed->notified_cr.cpMin = ed->notified_cr.cpMax = 0; 3166 3167 /* Default scrollbar information */ 3168 ed->vert_si.cbSize = sizeof(SCROLLINFO); 3169 ed->vert_si.nMin = 0; 3170 ed->vert_si.nMax = 0; 3171 ed->vert_si.nPage = 0; 3172 ed->vert_si.nPos = 0; 3173 3174 ed->horz_si.cbSize = sizeof(SCROLLINFO); 3175 ed->horz_si.nMin = 0; 3176 ed->horz_si.nMax = 0; 3177 ed->horz_si.nPage = 0; 3178 ed->horz_si.nPos = 0; 3179 3180 ed->wheel_remain = 0; 3181 3182 list_init( &ed->reobj_list ); 3183 OleInitialize(NULL); 3184 3185 return ed; 3186 } 3187 3188 void ME_DestroyEditor(ME_TextEditor *editor) 3189 { 3190 ME_DisplayItem *p = editor->pBuffer->pFirst, *pNext = NULL; 3191 ME_Style *s, *cursor2; 3192 int i; 3193 3194 ME_ClearTempStyle(editor); 3195 ME_EmptyUndoStack(editor); 3196 editor->pBuffer->pFirst = NULL; 3197 while(p) { 3198 pNext = p->next; 3199 if (p->type == diParagraph) 3200 destroy_para(editor, p); 3201 else 3202 ME_DestroyDisplayItem(p); 3203 p = pNext; 3204 } 3205 3206 LIST_FOR_EACH_ENTRY_SAFE( s, cursor2, &editor->style_list, ME_Style, entry ) 3207 ME_DestroyStyle( s ); 3208 3209 ME_ReleaseStyle(editor->pBuffer->pDefaultStyle); 3210 for (i=0; i<HFONT_CACHE_SIZE; i++) 3211 { 3212 if (editor->pFontCache[i].hFont) 3213 DeleteObject(editor->pFontCache[i].hFont); 3214 } 3215 if (editor->rgbBackColor != -1) 3216 DeleteObject(editor->hbrBackground); 3217 if(editor->lpOleCallback) 3218 IRichEditOleCallback_Release(editor->lpOleCallback); 3219 ITextHost_Release(editor->texthost); 3220 if (editor->reOle) 3221 { 3222 IRichEditOle_Release(editor->reOle); 3223 editor->reOle = NULL; 3224 } 3225 OleUninitialize(); 3226 3227 heap_free(editor->pBuffer); 3228 heap_free(editor->pCursors); 3229 heap_free(editor); 3230 } 3231 3232 BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) 3233 { 3234 TRACE("\n"); 3235 switch (fdwReason) 3236 { 3237 case DLL_PROCESS_ATTACH: 3238 DisableThreadLibraryCalls(hinstDLL); 3239 me_heap = HeapCreate (0, 0x10000, 0); 3240 if (!ME_RegisterEditorClass(hinstDLL)) return FALSE; 3241 hLeft = LoadCursorW(hinstDLL, MAKEINTRESOURCEW(OCR_REVERSE)); 3242 LookupInit(); 3243 break; 3244 3245 case DLL_PROCESS_DETACH: 3246 if (lpvReserved) break; 3247 UnregisterClassW(RICHEDIT_CLASS20W, 0); 3248 UnregisterClassW(MSFTEDIT_CLASS, 0); 3249 UnregisterClassA(RICHEDIT_CLASS20A, 0); 3250 UnregisterClassA("RichEdit50A", 0); 3251 if (ME_ListBoxRegistered) 3252 UnregisterClassW(REListBox20W, 0); 3253 if (ME_ComboBoxRegistered) 3254 UnregisterClassW(REComboBox20W, 0); 3255 LookupCleanup(); 3256 HeapDestroy (me_heap); 3257 release_typelib(); 3258 break; 3259 } 3260 return TRUE; 3261 } 3262 3263 static inline int get_default_line_height( ME_TextEditor *editor ) 3264 { 3265 int height = 0; 3266 3267 if (editor->pBuffer && editor->pBuffer->pDefaultStyle) 3268 height = editor->pBuffer->pDefaultStyle->tm.tmHeight; 3269 if (height <= 0) height = 24; 3270 3271 return height; 3272 } 3273 3274 static inline int calc_wheel_change( int *remain, int amount_per_click ) 3275 { 3276 int change = amount_per_click * (float)*remain / WHEEL_DELTA; 3277 *remain -= WHEEL_DELTA * change / amount_per_click; 3278 return change; 3279 } 3280 3281 static const char * const edit_messages[] = { 3282 "EM_GETSEL", 3283 "EM_SETSEL", 3284 "EM_GETRECT", 3285 "EM_SETRECT", 3286 "EM_SETRECTNP", 3287 "EM_SCROLL", 3288 "EM_LINESCROLL", 3289 "EM_SCROLLCARET", 3290 "EM_GETMODIFY", 3291 "EM_SETMODIFY", 3292 "EM_GETLINECOUNT", 3293 "EM_LINEINDEX", 3294 "EM_SETHANDLE", 3295 "EM_GETHANDLE", 3296 "EM_GETTHUMB", 3297 "EM_UNKNOWN_BF", 3298 "EM_UNKNOWN_C0", 3299 "EM_LINELENGTH", 3300 "EM_REPLACESEL", 3301 "EM_UNKNOWN_C3", 3302 "EM_GETLINE", 3303 "EM_LIMITTEXT", 3304 "EM_CANUNDO", 3305 "EM_UNDO", 3306 "EM_FMTLINES", 3307 "EM_LINEFROMCHAR", 3308 "EM_UNKNOWN_CA", 3309 "EM_SETTABSTOPS", 3310 "EM_SETPASSWORDCHAR", 3311 "EM_EMPTYUNDOBUFFER", 3312 "EM_GETFIRSTVISIBLELINE", 3313 "EM_SETREADONLY", 3314 "EM_SETWORDBREAKPROC", 3315 "EM_GETWORDBREAKPROC", 3316 "EM_GETPASSWORDCHAR", 3317 "EM_SETMARGINS", 3318 "EM_GETMARGINS", 3319 "EM_GETLIMITTEXT", 3320 "EM_POSFROMCHAR", 3321 "EM_CHARFROMPOS", 3322 "EM_SETIMESTATUS", 3323 "EM_GETIMESTATUS" 3324 }; 3325 3326 static const char * const richedit_messages[] = { 3327 "EM_CANPASTE", 3328 "EM_DISPLAYBAND", 3329 "EM_EXGETSEL", 3330 "EM_EXLIMITTEXT", 3331 "EM_EXLINEFROMCHAR", 3332 "EM_EXSETSEL", 3333 "EM_FINDTEXT", 3334 "EM_FORMATRANGE", 3335 "EM_GETCHARFORMAT", 3336 "EM_GETEVENTMASK", 3337 "EM_GETOLEINTERFACE", 3338 "EM_GETPARAFORMAT", 3339 "EM_GETSELTEXT", 3340 "EM_HIDESELECTION", 3341 "EM_PASTESPECIAL", 3342 "EM_REQUESTRESIZE", 3343 "EM_SELECTIONTYPE", 3344 "EM_SETBKGNDCOLOR", 3345 "EM_SETCHARFORMAT", 3346 "EM_SETEVENTMASK", 3347 "EM_SETOLECALLBACK", 3348 "EM_SETPARAFORMAT", 3349 "EM_SETTARGETDEVICE", 3350 "EM_STREAMIN", 3351 "EM_STREAMOUT", 3352 "EM_GETTEXTRANGE", 3353 "EM_FINDWORDBREAK", 3354 "EM_SETOPTIONS", 3355 "EM_GETOPTIONS", 3356 "EM_FINDTEXTEX", 3357 "EM_GETWORDBREAKPROCEX", 3358 "EM_SETWORDBREAKPROCEX", 3359 "EM_SETUNDOLIMIT", 3360 "EM_UNKNOWN_USER_83", 3361 "EM_REDO", 3362 "EM_CANREDO", 3363 "EM_GETUNDONAME", 3364 "EM_GETREDONAME", 3365 "EM_STOPGROUPTYPING", 3366 "EM_SETTEXTMODE", 3367 "EM_GETTEXTMODE", 3368 "EM_AUTOURLDETECT", 3369 "EM_GETAUTOURLDETECT", 3370 "EM_SETPALETTE", 3371 "EM_GETTEXTEX", 3372 "EM_GETTEXTLENGTHEX", 3373 "EM_SHOWSCROLLBAR", 3374 "EM_SETTEXTEX", 3375 "EM_UNKNOWN_USER_98", 3376 "EM_UNKNOWN_USER_99", 3377 "EM_SETPUNCTUATION", 3378 "EM_GETPUNCTUATION", 3379 "EM_SETWORDWRAPMODE", 3380 "EM_GETWORDWRAPMODE", 3381 "EM_SETIMECOLOR", 3382 "EM_GETIMECOLOR", 3383 "EM_SETIMEOPTIONS", 3384 "EM_GETIMEOPTIONS", 3385 "EM_CONVPOSITION", 3386 "EM_UNKNOWN_USER_109", 3387 "EM_UNKNOWN_USER_110", 3388 "EM_UNKNOWN_USER_111", 3389 "EM_UNKNOWN_USER_112", 3390 "EM_UNKNOWN_USER_113", 3391 "EM_UNKNOWN_USER_114", 3392 "EM_UNKNOWN_USER_115", 3393 "EM_UNKNOWN_USER_116", 3394 "EM_UNKNOWN_USER_117", 3395 "EM_UNKNOWN_USER_118", 3396 "EM_UNKNOWN_USER_119", 3397 "EM_SETLANGOPTIONS", 3398 "EM_GETLANGOPTIONS", 3399 "EM_GETIMECOMPMODE", 3400 "EM_FINDTEXTW", 3401 "EM_FINDTEXTEXW", 3402 "EM_RECONVERSION", 3403 "EM_SETIMEMODEBIAS", 3404 "EM_GETIMEMODEBIAS" 3405 }; 3406 3407 static const char * 3408 get_msg_name(UINT msg) 3409 { 3410 if (msg >= EM_GETSEL && msg <= EM_CHARFROMPOS) 3411 return edit_messages[msg - EM_GETSEL]; 3412 if (msg >= EM_CANPASTE && msg <= EM_GETIMEMODEBIAS) 3413 return richedit_messages[msg - EM_CANPASTE]; 3414 return ""; 3415 } 3416 3417 static void ME_LinkNotify(ME_TextEditor *editor, UINT msg, WPARAM wParam, LPARAM lParam) 3418 { 3419 int x,y; 3420 BOOL isExact; 3421 ME_Cursor cursor; /* The start of the clicked text. */ 3422 3423 ENLINK info; 3424 x = (short)LOWORD(lParam); 3425 y = (short)HIWORD(lParam); 3426 ME_CharFromPos(editor, x, y, &cursor, &isExact); 3427 if (!isExact) return; 3428 3429 if (is_link( &cursor.pRun->member.run )) 3430 { /* The clicked run has CFE_LINK set */ 3431 ME_DisplayItem *di; 3432 3433 info.nmhdr.hwndFrom = NULL; 3434 info.nmhdr.idFrom = 0; 3435 info.nmhdr.code = EN_LINK; 3436 info.msg = msg; 3437 info.wParam = wParam; 3438 info.lParam = lParam; 3439 cursor.nOffset = 0; 3440 3441 /* find the first contiguous run with CFE_LINK set */ 3442 info.chrg.cpMin = ME_GetCursorOfs(&cursor); 3443 di = cursor.pRun; 3444 while (ME_PrevRun( NULL, &di, FALSE ) && is_link( &di->member.run )) 3445 info.chrg.cpMin -= di->member.run.len; 3446 3447 /* find the last contiguous run with CFE_LINK set */ 3448 info.chrg.cpMax = ME_GetCursorOfs(&cursor) + cursor.pRun->member.run.len; 3449 di = cursor.pRun; 3450 while (ME_NextRun( NULL, &di, FALSE ) && is_link( &di->member.run )) 3451 info.chrg.cpMax += di->member.run.len; 3452 3453 ITextHost_TxNotify(editor->texthost, info.nmhdr.code, &info); 3454 } 3455 } 3456 3457 void ME_ReplaceSel(ME_TextEditor *editor, BOOL can_undo, const WCHAR *str, int len) 3458 { 3459 int from, to, nStartCursor; 3460 ME_Style *style; 3461 3462 nStartCursor = ME_GetSelectionOfs(editor, &from, &to); 3463 style = ME_GetSelectionInsertStyle(editor); 3464 ME_InternalDeleteText(editor, &editor->pCursors[nStartCursor], to-from, FALSE); 3465 ME_InsertTextFromCursor(editor, 0, str, len, style); 3466 ME_ReleaseStyle(style); 3467 /* drop temporary style if line end */ 3468 /* 3469 * FIXME question: does abc\n mean: put abc, 3470 * clear temp style, put \n? (would require a change) 3471 */ 3472 if (len>0 && str[len-1] == '\n') 3473 ME_ClearTempStyle(editor); 3474 ME_CommitUndo(editor); 3475 ME_UpdateSelectionLinkAttribute(editor); 3476 if (!can_undo) 3477 ME_EmptyUndoStack(editor); 3478 ME_UpdateRepaint(editor, FALSE); 3479 } 3480 3481 static void ME_SetText(ME_TextEditor *editor, void *text, BOOL unicode) 3482 { 3483 LONG codepage = unicode ? CP_UNICODE : CP_ACP; 3484 int textLen; 3485 3486 LPWSTR wszText = ME_ToUnicode(codepage, text, &textLen); 3487 ME_InsertTextFromCursor(editor, 0, wszText, textLen, editor->pBuffer->pDefaultStyle); 3488 ME_EndToUnicode(codepage, wszText); 3489 } 3490 3491 static LRESULT ME_WmCreate(ME_TextEditor *editor, LPARAM lParam, BOOL unicode) 3492 { 3493 CREATESTRUCTW *createW = (CREATESTRUCTW*)lParam; 3494 CREATESTRUCTA *createA = (CREATESTRUCTA*)lParam; 3495 void *text = NULL; 3496 INT max; 3497 3498 if (lParam) 3499 text = unicode ? (void*)createW->lpszName : (void*)createA->lpszName; 3500 3501 ME_SetDefaultFormatRect(editor); 3502 3503 max = (editor->styleFlags & ES_DISABLENOSCROLL) ? 1 : 0; 3504 if (~editor->styleFlags & ES_DISABLENOSCROLL || editor->styleFlags & WS_VSCROLL) 3505 ITextHost_TxSetScrollRange(editor->texthost, SB_VERT, 0, max, TRUE); 3506 3507 if (~editor->styleFlags & ES_DISABLENOSCROLL || editor->styleFlags & WS_HSCROLL) 3508 ITextHost_TxSetScrollRange(editor->texthost, SB_HORZ, 0, max, TRUE); 3509 3510 if (editor->styleFlags & ES_DISABLENOSCROLL) 3511 { 3512 if (editor->styleFlags & WS_VSCROLL) 3513 { 3514 ITextHost_TxEnableScrollBar(editor->texthost, SB_VERT, ESB_DISABLE_BOTH); 3515 ITextHost_TxShowScrollBar(editor->texthost, SB_VERT, TRUE); 3516 } 3517 if (editor->styleFlags & WS_HSCROLL) 3518 { 3519 ITextHost_TxEnableScrollBar(editor->texthost, SB_HORZ, ESB_DISABLE_BOTH); 3520 ITextHost_TxShowScrollBar(editor->texthost, SB_HORZ, TRUE); 3521 } 3522 } 3523 3524 if (text) 3525 { 3526 ME_SetText(editor, text, unicode); 3527 ME_SetCursorToStart(editor, &editor->pCursors[0]); 3528 ME_SetCursorToStart(editor, &editor->pCursors[1]); 3529 } 3530 3531 ME_CommitUndo(editor); 3532 ME_WrapMarkedParagraphs(editor); 3533 ME_MoveCaret(editor); 3534 return 0; 3535 } 3536 3537 3538 #define UNSUPPORTED_MSG(e) \ 3539 case e: \ 3540 FIXME(#e ": stub\n"); \ 3541 *phresult = S_FALSE; \ 3542 return 0; 3543 3544 /* Handle messages for windowless and windowed richedit controls. 3545 * 3546 * The LRESULT that is returned is a return value for window procs, 3547 * and the phresult parameter is the COM return code needed by the 3548 * text services interface. */ 3549 LRESULT ME_HandleMessage(ME_TextEditor *editor, UINT msg, WPARAM wParam, 3550 LPARAM lParam, BOOL unicode, HRESULT* phresult) 3551 { 3552 *phresult = S_OK; 3553 3554 switch(msg) { 3555 3556 UNSUPPORTED_MSG(EM_DISPLAYBAND) 3557 UNSUPPORTED_MSG(EM_FINDWORDBREAK) 3558 UNSUPPORTED_MSG(EM_FMTLINES) 3559 UNSUPPORTED_MSG(EM_FORMATRANGE) 3560 UNSUPPORTED_MSG(EM_GETBIDIOPTIONS) 3561 UNSUPPORTED_MSG(EM_GETEDITSTYLE) 3562 UNSUPPORTED_MSG(EM_GETIMECOMPMODE) 3563 UNSUPPORTED_MSG(EM_GETIMESTATUS) 3564 UNSUPPORTED_MSG(EM_SETIMESTATUS) 3565 UNSUPPORTED_MSG(EM_GETLANGOPTIONS) 3566 UNSUPPORTED_MSG(EM_GETREDONAME) 3567 UNSUPPORTED_MSG(EM_GETTYPOGRAPHYOPTIONS) 3568 UNSUPPORTED_MSG(EM_GETUNDONAME) 3569 UNSUPPORTED_MSG(EM_GETWORDBREAKPROCEX) 3570 UNSUPPORTED_MSG(EM_SETBIDIOPTIONS) 3571 UNSUPPORTED_MSG(EM_SETEDITSTYLE) 3572 UNSUPPORTED_MSG(EM_SETLANGOPTIONS) 3573 UNSUPPORTED_MSG(EM_SETMARGINS) 3574 UNSUPPORTED_MSG(EM_SETPALETTE) 3575 UNSUPPORTED_MSG(EM_SETTABSTOPS) 3576 UNSUPPORTED_MSG(EM_SETTYPOGRAPHYOPTIONS) 3577 UNSUPPORTED_MSG(EM_SETWORDBREAKPROCEX) 3578 3579 /* Messages specific to Richedit controls */ 3580 3581 case EM_STREAMIN: 3582 return ME_StreamIn(editor, wParam, (EDITSTREAM*)lParam, TRUE); 3583 case EM_STREAMOUT: 3584 return ME_StreamOut(editor, wParam, (EDITSTREAM *)lParam); 3585 case WM_GETDLGCODE: 3586 { 3587 UINT code = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS; 3588 3589 if (lParam) 3590 editor->bDialogMode = TRUE; 3591 if (editor->styleFlags & ES_MULTILINE) 3592 code |= DLGC_WANTMESSAGE; 3593 if (!(editor->styleFlags & ES_SAVESEL)) 3594 code |= DLGC_HASSETSEL; 3595 return code; 3596 } 3597 case EM_EMPTYUNDOBUFFER: 3598 ME_EmptyUndoStack(editor); 3599 return 0; 3600 case EM_GETSEL: 3601 { 3602 /* Note: wParam/lParam can be NULL */ 3603 UINT from, to; 3604 PUINT pfrom = wParam ? (PUINT)wParam : &from; 3605 PUINT pto = lParam ? (PUINT)lParam : &to; 3606 ME_GetSelectionOfs(editor, (int *)pfrom, (int *)pto); 3607 if ((*pfrom|*pto) & 0xFFFF0000) 3608 return -1; 3609 return MAKELONG(*pfrom,*pto); 3610 } 3611 case EM_EXGETSEL: 3612 { 3613 CHARRANGE *pRange = (CHARRANGE *)lParam; 3614 ME_GetSelectionOfs(editor, &pRange->cpMin, &pRange->cpMax); 3615 TRACE("EM_EXGETSEL = (%d,%d)\n", pRange->cpMin, pRange->cpMax); 3616 return 0; 3617 } 3618 case EM_SETUNDOLIMIT: 3619 { 3620 if ((int)wParam < 0) 3621 editor->nUndoLimit = STACK_SIZE_DEFAULT; 3622 else 3623 editor->nUndoLimit = min(wParam, STACK_SIZE_MAX); 3624 /* Setting a max stack size keeps wine from getting killed 3625 for hogging memory. Windows allocates all this memory at once, so 3626 no program would realistically set a value above our maximum. */ 3627 return editor->nUndoLimit; 3628 } 3629 case EM_CANUNDO: 3630 return !list_empty( &editor->undo_stack ); 3631 case EM_CANREDO: 3632 return !list_empty( &editor->redo_stack ); 3633 case WM_UNDO: /* FIXME: actually not the same */ 3634 case EM_UNDO: 3635 return ME_Undo(editor); 3636 case EM_REDO: 3637 return ME_Redo(editor); 3638 case EM_GETOPTIONS: 3639 { 3640 /* these flags are equivalent to the ES_* counterparts */ 3641 DWORD mask = ECO_VERTICAL | ECO_AUTOHSCROLL | ECO_AUTOVSCROLL | 3642 ECO_NOHIDESEL | ECO_READONLY | ECO_WANTRETURN | ECO_SELECTIONBAR; 3643 DWORD settings = editor->styleFlags & mask; 3644 3645 return settings; 3646 } 3647 case EM_SETFONTSIZE: 3648 { 3649 CHARFORMAT2W cf; 3650 LONG tmp_size, size; 3651 BOOL is_increase = ((LONG)wParam > 0); 3652 3653 if (editor->mode & TM_PLAINTEXT) 3654 return FALSE; 3655 3656 cf.cbSize = sizeof(cf); 3657 cf.dwMask = CFM_SIZE; 3658 ME_GetSelectionCharFormat(editor, &cf); 3659 tmp_size = (cf.yHeight / 20) + wParam; 3660 3661 if (tmp_size <= 1) 3662 size = 1; 3663 else if (tmp_size > 12 && tmp_size < 28 && tmp_size % 2) 3664 size = tmp_size + (is_increase ? 1 : -1); 3665 else if (tmp_size > 28 && tmp_size < 36) 3666 size = is_increase ? 36 : 28; 3667 else if (tmp_size > 36 && tmp_size < 48) 3668 size = is_increase ? 48 : 36; 3669 else if (tmp_size > 48 && tmp_size < 72) 3670 size = is_increase ? 72 : 48; 3671 else if (tmp_size > 72 && tmp_size < 80) 3672 size = is_increase ? 80 : 72; 3673 else if (tmp_size > 80 && tmp_size < 1638) 3674 size = 10 * (is_increase ? (tmp_size / 10 + 1) : (tmp_size / 10)); 3675 else if (tmp_size >= 1638) 3676 size = 1638; 3677 else 3678 size = tmp_size; 3679 3680 cf.yHeight = size * 20; /* convert twips to points */ 3681 ME_SetSelectionCharFormat(editor, &cf); 3682 ME_CommitUndo(editor); 3683 ME_WrapMarkedParagraphs(editor); 3684 ME_UpdateScrollBar(editor); 3685 ME_Repaint(editor); 3686 3687 return TRUE; 3688 } 3689 case EM_SETOPTIONS: 3690 { 3691 /* these flags are equivalent to ES_* counterparts, except for 3692 * ECO_AUTOWORDSELECTION that doesn't have an ES_* counterpart, 3693 * but is still stored in editor->styleFlags. */ 3694 const DWORD mask = ECO_VERTICAL | ECO_AUTOHSCROLL | ECO_AUTOVSCROLL | 3695 ECO_NOHIDESEL | ECO_READONLY | ECO_WANTRETURN | 3696 ECO_SELECTIONBAR | ECO_AUTOWORDSELECTION; 3697 DWORD settings = mask & editor->styleFlags; 3698 DWORD oldSettings = settings; 3699 DWORD changedSettings; 3700 3701 switch(wParam) 3702 { 3703 case ECOOP_SET: 3704 settings = lParam; 3705 break; 3706 case ECOOP_OR: 3707 settings |= lParam; 3708 break; 3709 case ECOOP_AND: 3710 settings &= lParam; 3711 break; 3712 case ECOOP_XOR: 3713 settings ^= lParam; 3714 } 3715 changedSettings = oldSettings ^ settings; 3716 3717 if (changedSettings) { 3718 editor->styleFlags = (editor->styleFlags & ~mask) | (settings & mask); 3719 3720 if (changedSettings & ECO_SELECTIONBAR) 3721 { 3722 ITextHost_TxInvalidateRect(editor->texthost, &editor->rcFormat, TRUE); 3723 if (settings & ECO_SELECTIONBAR) { 3724 assert(!editor->selofs); 3725 editor->selofs = SELECTIONBAR_WIDTH; 3726 editor->rcFormat.left += editor->selofs; 3727 } else { 3728 editor->rcFormat.left -= editor->selofs; 3729 editor->selofs = 0; 3730 } 3731 ME_RewrapRepaint(editor); 3732 } 3733 3734 if ((changedSettings & settings & ES_NOHIDESEL) && !editor->bHaveFocus) 3735 ME_InvalidateSelection( editor ); 3736 3737 if (changedSettings & settings & ECO_VERTICAL) 3738 FIXME("ECO_VERTICAL not implemented yet!\n"); 3739 if (changedSettings & settings & ECO_AUTOHSCROLL) 3740 FIXME("ECO_AUTOHSCROLL not implemented yet!\n"); 3741 if (changedSettings & settings & ECO_AUTOVSCROLL) 3742 FIXME("ECO_AUTOVSCROLL not implemented yet!\n"); 3743 if (changedSettings & settings & ECO_WANTRETURN) 3744 FIXME("ECO_WANTRETURN not implemented yet!\n"); 3745 if (changedSettings & settings & ECO_AUTOWORDSELECTION) 3746 FIXME("ECO_AUTOWORDSELECTION not implemented yet!\n"); 3747 } 3748 3749 return settings; 3750 } 3751 case EM_SETSEL: 3752 { 3753 return handle_EM_EXSETSEL( editor, wParam, lParam ); 3754 } 3755 case EM_SETSCROLLPOS: 3756 { 3757 POINT *point = (POINT *)lParam; 3758 ME_ScrollAbs(editor, point->x, point->y); 3759 return 0; 3760 } 3761 case EM_AUTOURLDETECT: 3762 { 3763 if (wParam==1 || wParam ==0) 3764 { 3765 editor->AutoURLDetect_bEnable = (BOOL)wParam; 3766 return 0; 3767 } 3768 return E_INVALIDARG; 3769 } 3770 case EM_GETAUTOURLDETECT: 3771 { 3772 return editor->AutoURLDetect_bEnable; 3773 } 3774 case EM_EXSETSEL: 3775 { 3776 CHARRANGE range = *(CHARRANGE *)lParam; 3777 3778 return handle_EM_EXSETSEL( editor, range.cpMin, range.cpMax ); 3779 } 3780 case EM_SHOWSCROLLBAR: 3781 { 3782 DWORD flags; 3783 3784 switch (wParam) 3785 { 3786 case SB_HORZ: 3787 flags = WS_HSCROLL; 3788 break; 3789 case SB_VERT: 3790 flags = WS_VSCROLL; 3791 break; 3792 case SB_BOTH: 3793 flags = WS_HSCROLL|WS_VSCROLL; 3794 break; 3795 default: 3796 return 0; 3797 } 3798 3799 if (lParam) { 3800 editor->styleFlags |= flags; 3801 if (flags & WS_HSCROLL) 3802 ITextHost_TxShowScrollBar(editor->texthost, SB_HORZ, 3803 editor->nTotalWidth > editor->sizeWindow.cx); 3804 if (flags & WS_VSCROLL) 3805 ITextHost_TxShowScrollBar(editor->texthost, SB_VERT, 3806 editor->nTotalLength > editor->sizeWindow.cy); 3807 } else { 3808 editor->styleFlags &= ~flags; 3809 ITextHost_TxShowScrollBar(editor->texthost, wParam, FALSE); 3810 } 3811 return 0; 3812 } 3813 case EM_SETTEXTEX: 3814 { 3815 LPWSTR wszText; 3816 SETTEXTEX *pStruct = (SETTEXTEX *)wParam; 3817 int from, to, len; 3818 ME_Style *style; 3819 BOOL bRtf, bUnicode, bSelection, bUTF8; 3820 int oldModify = editor->nModifyStep; 3821 static const char utf8_bom[] = {0xef, 0xbb, 0xbf}; 3822 3823 if (!pStruct) return 0; 3824 3825 /* If we detect ascii rtf at the start of the string, 3826 * we know it isn't unicode. */ 3827 bRtf = (lParam && (!strncmp((char *)lParam, "{\\rtf", 5) || 3828 !strncmp((char *)lParam, "{\\urtf", 6))); 3829 bUnicode = !bRtf && pStruct->codepage == CP_UNICODE; 3830 bUTF8 = (lParam && (!strncmp((char *)lParam, utf8_bom, 3))); 3831 3832 TRACE("EM_SETTEXTEX - %s, flags %d, cp %d\n", 3833 bUnicode ? debugstr_w((LPCWSTR)lParam) : debugstr_a((LPCSTR)lParam), 3834 pStruct->flags, pStruct->codepage); 3835 3836 bSelection = (pStruct->flags & ST_SELECTION) != 0; 3837 if (bSelection) { 3838 int nStartCursor = ME_GetSelectionOfs(editor, &from, &to); 3839 style = ME_GetSelectionInsertStyle(editor); 3840 ME_InternalDeleteText(editor, &editor->pCursors[nStartCursor], to - from, FALSE); 3841 } else { 3842 ME_Cursor start; 3843 ME_SetCursorToStart(editor, &start); 3844 ME_InternalDeleteText(editor, &start, ME_GetTextLength(editor), FALSE); 3845 style = editor->pBuffer->pDefaultStyle; 3846 } 3847 3848 if (bRtf) { 3849 ME_StreamInRTFString(editor, bSelection, (char *)lParam); 3850 if (bSelection) { 3851 /* FIXME: The length returned doesn't include the rtf control 3852 * characters, only the actual text. */ 3853 len = lParam ? strlen((char *)lParam) : 0; 3854 } 3855 } else { 3856 if (bUTF8 && !bUnicode) { 3857 wszText = ME_ToUnicode(CP_UTF8, (void *)(lParam+3), &len); 3858 ME_InsertTextFromCursor(editor, 0, wszText, len, style); 3859 ME_EndToUnicode(CP_UTF8, wszText); 3860 } else { 3861 wszText = ME_ToUnicode(pStruct->codepage, (void *)lParam, &len); 3862 ME_InsertTextFromCursor(editor, 0, wszText, len, style); 3863 ME_EndToUnicode(pStruct->codepage, wszText); 3864 } 3865 } 3866 3867 if (bSelection) { 3868 ME_ReleaseStyle(style); 3869 ME_UpdateSelectionLinkAttribute(editor); 3870 } else { 3871 ME_Cursor cursor; 3872 len = 1; 3873 ME_SetCursorToStart(editor, &cursor); 3874 ME_UpdateLinkAttribute(editor, &cursor, INT_MAX); 3875 } 3876 ME_CommitUndo(editor); 3877 if (!(pStruct->flags & ST_KEEPUNDO)) 3878 { 3879 editor->nModifyStep = oldModify; 3880 ME_EmptyUndoStack(editor); 3881 } 3882 ME_UpdateRepaint(editor, FALSE); 3883 return len; 3884 } 3885 case EM_SELECTIONTYPE: 3886 return ME_GetSelectionType(editor); 3887 case EM_SETBKGNDCOLOR: 3888 { 3889 LRESULT lColor; 3890 if (editor->rgbBackColor != -1) { 3891 DeleteObject(editor->hbrBackground); 3892 lColor = editor->rgbBackColor; 3893 } 3894 else lColor = ITextHost_TxGetSysColor(editor->texthost, COLOR_WINDOW); 3895 3896 if (wParam) 3897 { 3898 editor->rgbBackColor = -1; 3899 editor->hbrBackground = GetSysColorBrush(COLOR_WINDOW); 3900 } 3901 else 3902 { 3903 editor->rgbBackColor = lParam; 3904 editor->hbrBackground = CreateSolidBrush(editor->rgbBackColor); 3905 } 3906 ITextHost_TxInvalidateRect(editor->texthost, NULL, TRUE); 3907 return lColor; 3908 } 3909 case EM_GETMODIFY: 3910 return editor->nModifyStep == 0 ? 0 : -1; 3911 case EM_SETMODIFY: 3912 { 3913 if (wParam) 3914 editor->nModifyStep = 1; 3915 else 3916 editor->nModifyStep = 0; 3917 3918 return 0; 3919 } 3920 case EM_SETREADONLY: 3921 { 3922 if (wParam) 3923 editor->styleFlags |= ES_READONLY; 3924 else 3925 editor->styleFlags &= ~ES_READONLY; 3926 return 1; 3927 } 3928 case EM_SETEVENTMASK: 3929 { 3930 DWORD nOldMask = editor->nEventMask; 3931 3932 editor->nEventMask = lParam; 3933 return nOldMask; 3934 } 3935 case EM_GETEVENTMASK: 3936 return editor->nEventMask; 3937 case EM_SETCHARFORMAT: 3938 { 3939 CHARFORMAT2W p; 3940 BOOL bRepaint = TRUE; 3941 if (!cfany_to_cf2w(&p, (CHARFORMAT2W *)lParam)) 3942 return 0; 3943 if (wParam & SCF_ALL) { 3944 if (editor->mode & TM_PLAINTEXT) { 3945 ME_SetDefaultCharFormat(editor, &p); 3946 } else { 3947 ME_Cursor start; 3948 ME_SetCursorToStart(editor, &start); 3949 ME_SetCharFormat(editor, &start, NULL, &p); 3950 editor->nModifyStep = 1; 3951 } 3952 } else if (wParam & SCF_SELECTION) { 3953 if (editor->mode & TM_PLAINTEXT) 3954 return 0; 3955 if (wParam & SCF_WORD) { 3956 ME_Cursor start; 3957 ME_Cursor end = editor->pCursors[0]; 3958 ME_MoveCursorWords(editor, &end, +1); 3959 start = end; 3960 ME_MoveCursorWords(editor, &start, -1); 3961 ME_SetCharFormat(editor, &start, &end, &p); 3962 } 3963 bRepaint = ME_IsSelection(editor); 3964 ME_SetSelectionCharFormat(editor, &p); 3965 if (bRepaint) editor->nModifyStep = 1; 3966 } else { /* SCF_DEFAULT */ 3967 ME_SetDefaultCharFormat(editor, &p); 3968 } 3969 ME_CommitUndo(editor); 3970 if (bRepaint) 3971 { 3972 ME_WrapMarkedParagraphs(editor); 3973 ME_UpdateScrollBar(editor); 3974 ME_Repaint(editor); 3975 } 3976 return 1; 3977 } 3978 case EM_GETCHARFORMAT: 3979 { 3980 CHARFORMAT2W tmp, *dst = (CHARFORMAT2W *)lParam; 3981 if (dst->cbSize != sizeof(CHARFORMATA) && 3982 dst->cbSize != sizeof(CHARFORMATW) && 3983 dst->cbSize != sizeof(CHARFORMAT2A) && 3984 dst->cbSize != sizeof(CHARFORMAT2W)) 3985 return 0; 3986 tmp.cbSize = sizeof(tmp); 3987 if (!wParam) 3988 ME_GetDefaultCharFormat(editor, &tmp); 3989 else 3990 ME_GetSelectionCharFormat(editor, &tmp); 3991 cf2w_to_cfany(dst, &tmp); 3992 return tmp.dwMask; 3993 } 3994 case EM_SETPARAFORMAT: 3995 { 3996 BOOL result = ME_SetSelectionParaFormat(editor, (PARAFORMAT2 *)lParam); 3997 ME_WrapMarkedParagraphs(editor); 3998 ME_UpdateScrollBar(editor); 3999 ME_Repaint(editor); 4000 ME_CommitUndo(editor); 4001 return result; 4002 } 4003 case EM_GETPARAFORMAT: 4004 ME_GetSelectionParaFormat(editor, (PARAFORMAT2 *)lParam); 4005 return ((PARAFORMAT2 *)lParam)->dwMask; 4006 case EM_GETFIRSTVISIBLELINE: 4007 { 4008 ME_DisplayItem *p = editor->pBuffer->pFirst; 4009 int y = editor->vert_si.nPos; 4010 int ypara = 0; 4011 int count = 0; 4012 int ystart, yend; 4013 while(p) { 4014 p = ME_FindItemFwd(p, diStartRowOrParagraphOrEnd); 4015 if (p->type == diTextEnd) 4016 break; 4017 if (p->type == diParagraph) { 4018 ypara = p->member.para.pt.y; 4019 continue; 4020 } 4021 ystart = ypara + p->member.row.pt.y; 4022 yend = ystart + p->member.row.nHeight; 4023 if (y < yend) { 4024 break; 4025 } 4026 count++; 4027 } 4028 return count; 4029 } 4030 case EM_HIDESELECTION: 4031 { 4032 editor->bHideSelection = (wParam != 0); 4033 ME_InvalidateSelection(editor); 4034 return 0; 4035 } 4036 case EM_LINESCROLL: 4037 { 4038 if (!(editor->styleFlags & ES_MULTILINE)) 4039 return FALSE; 4040 ME_ScrollDown( editor, lParam * get_default_line_height( editor ) ); 4041 return TRUE; 4042 } 4043 case WM_CLEAR: 4044 { 4045 int from, to; 4046 int nStartCursor = ME_GetSelectionOfs(editor, &from, &to); 4047 ME_InternalDeleteText(editor, &editor->pCursors[nStartCursor], to-from, FALSE); 4048 ME_CommitUndo(editor); 4049 ME_UpdateRepaint(editor, TRUE); 4050 return 0; 4051 } 4052 case EM_REPLACESEL: 4053 { 4054 int len = 0; 4055 LONG codepage = unicode ? CP_UNICODE : CP_ACP; 4056 LPWSTR wszText = ME_ToUnicode(codepage, (void *)lParam, &len); 4057 4058 TRACE("EM_REPLACESEL - %s\n", debugstr_w(wszText)); 4059 4060 ME_ReplaceSel(editor, !!wParam, wszText, len); 4061 ME_EndToUnicode(codepage, wszText); 4062 return len; 4063 } 4064 case EM_SCROLLCARET: 4065 ME_EnsureVisible(editor, &editor->pCursors[0]); 4066 return 0; 4067 case WM_SETFONT: 4068 { 4069 LOGFONTW lf; 4070 CHARFORMAT2W fmt; 4071 HDC hDC; 4072 BOOL bRepaint = LOWORD(lParam); 4073 4074 if (!wParam) 4075 wParam = (WPARAM)GetStockObject(SYSTEM_FONT); 4076 4077 if (!GetObjectW((HGDIOBJ)wParam, sizeof(LOGFONTW), &lf)) 4078 return 0; 4079 4080 hDC = ITextHost_TxGetDC(editor->texthost); 4081 ME_CharFormatFromLogFont(hDC, &lf, &fmt); 4082 ITextHost_TxReleaseDC(editor->texthost, hDC); 4083 if (editor->mode & TM_RICHTEXT) { 4084 ME_Cursor start; 4085 ME_SetCursorToStart(editor, &start); 4086 ME_SetCharFormat(editor, &start, NULL, &fmt); 4087 } 4088 ME_SetDefaultCharFormat(editor, &fmt); 4089 4090 ME_CommitUndo(editor); 4091 ME_MarkAllForWrapping(editor); 4092 ME_WrapMarkedParagraphs(editor); 4093 ME_UpdateScrollBar(editor); 4094 if (bRepaint) 4095 ME_Repaint(editor); 4096 return 0; 4097 } 4098 case WM_SETTEXT: 4099 { 4100 ME_Cursor cursor; 4101 ME_SetCursorToStart(editor, &cursor); 4102 ME_InternalDeleteText(editor, &cursor, ME_GetTextLength(editor), FALSE); 4103 if (lParam) 4104 { 4105 TRACE("WM_SETTEXT lParam==%lx\n",lParam); 4106 if (!strncmp((char *)lParam, "{\\rtf", 5) || 4107 !strncmp((char *)lParam, "{\\urtf", 6)) 4108 { 4109 /* Undocumented: WM_SETTEXT supports RTF text */ 4110 ME_StreamInRTFString(editor, 0, (char *)lParam); 4111 } 4112 else 4113 ME_SetText(editor, (void*)lParam, unicode); 4114 } 4115 else 4116 TRACE("WM_SETTEXT - NULL\n"); 4117 ME_SetCursorToStart(editor, &cursor); 4118 ME_UpdateLinkAttribute(editor, &cursor, INT_MAX); 4119 ME_SetSelection(editor, 0, 0); 4120 editor->nModifyStep = 0; 4121 ME_CommitUndo(editor); 4122 ME_EmptyUndoStack(editor); 4123 ME_UpdateRepaint(editor, FALSE); 4124 return 1; 4125 } 4126 case EM_CANPASTE: 4127 return paste_special( editor, 0, NULL, TRUE ); 4128 case WM_PASTE: 4129 case WM_MBUTTONDOWN: 4130 wParam = 0; 4131 lParam = 0; 4132 /* fall through */ 4133 case EM_PASTESPECIAL: 4134 paste_special( editor, wParam, (REPASTESPECIAL *)lParam, FALSE ); 4135 return 0; 4136 case WM_CUT: 4137 case WM_COPY: 4138 copy_or_cut(editor, msg == WM_CUT); 4139 return 0; 4140 case WM_GETTEXTLENGTH: 4141 { 4142 GETTEXTLENGTHEX how; 4143 4144 /* CR/LF conversion required in 2.0 mode, verbatim in 1.0 mode */ 4145 how.flags = GTL_CLOSE | (editor->bEmulateVersion10 ? 0 : GTL_USECRLF) | GTL_NUMCHARS; 4146 how.codepage = unicode ? CP_UNICODE : CP_ACP; 4147 return ME_GetTextLengthEx(editor, &how); 4148 } 4149 case EM_GETTEXTLENGTHEX: 4150 return ME_GetTextLengthEx(editor, (GETTEXTLENGTHEX *)wParam); 4151 case WM_GETTEXT: 4152 { 4153 GETTEXTEX ex; 4154 ex.cb = wParam * (unicode ? sizeof(WCHAR) : sizeof(CHAR)); 4155 ex.flags = GT_USECRLF; 4156 ex.codepage = unicode ? CP_UNICODE : CP_ACP; 4157 ex.lpDefaultChar = NULL; 4158 ex.lpUsedDefChar = NULL; 4159 return ME_GetTextEx(editor, &ex, lParam); 4160 } 4161 case EM_GETTEXTEX: 4162 return ME_GetTextEx(editor, (GETTEXTEX*)wParam, lParam); 4163 case EM_GETSELTEXT: 4164 { 4165 int nFrom, nTo, nStartCur = ME_GetSelectionOfs(editor, &nFrom, &nTo); 4166 ME_Cursor *from = &editor->pCursors[nStartCur]; 4167 return ME_GetTextRange(editor, (WCHAR *)lParam, from, 4168 nTo - nFrom, unicode); 4169 } 4170 case EM_GETSCROLLPOS: 4171 { 4172 POINT *point = (POINT *)lParam; 4173 point->x = editor->horz_si.nPos; 4174 point->y = editor->vert_si.nPos; 4175 /* 16-bit scaled value is returned as stored in scrollinfo */ 4176 if (editor->horz_si.nMax > 0xffff) 4177 point->x = MulDiv(point->x, 0xffff, editor->horz_si.nMax); 4178 if (editor->vert_si.nMax > 0xffff) 4179 point->y = MulDiv(point->y, 0xffff, editor->vert_si.nMax); 4180 return 1; 4181 } 4182 case EM_GETTEXTRANGE: 4183 { 4184 TEXTRANGEW *rng = (TEXTRANGEW *)lParam; 4185 ME_Cursor start; 4186 int nStart = rng->chrg.cpMin; 4187 int nEnd = rng->chrg.cpMax; 4188 int textlength = ME_GetTextLength(editor); 4189 4190 TRACE("EM_GETTEXTRANGE min=%d max=%d unicode=%d textlength=%d\n", 4191 rng->chrg.cpMin, rng->chrg.cpMax, unicode, textlength); 4192 if (nStart < 0) return 0; 4193 if ((nStart == 0 && nEnd == -1) || nEnd > textlength) 4194 nEnd = textlength; 4195 if (nStart >= nEnd) return 0; 4196 4197 ME_CursorFromCharOfs(editor, nStart, &start); 4198 return ME_GetTextRange(editor, rng->lpstrText, &start, nEnd - nStart, unicode); 4199 } 4200 case EM_GETLINE: 4201 { 4202 ME_DisplayItem *run; 4203 const unsigned int nMaxChars = *(WORD *) lParam; 4204 unsigned int nCharsLeft = nMaxChars; 4205 char *dest = (char *) lParam; 4206 BOOL wroteNull = FALSE; 4207 4208 TRACE("EM_GETLINE: row=%d, nMaxChars=%d (%s)\n", (int) wParam, nMaxChars, 4209 unicode ? "Unicode" : "Ansi"); 4210 4211 run = ME_FindRowWithNumber(editor, wParam); 4212 if (run == NULL) 4213 return 0; 4214 4215 while (nCharsLeft && (run = ME_FindItemFwd(run, diRunOrStartRow)) 4216 && run->type == diRun) 4217 { 4218 WCHAR *str = get_text( &run->member.run, 0 ); 4219 unsigned int nCopy; 4220 4221 nCopy = min(nCharsLeft, run->member.run.len); 4222 4223 if (unicode) 4224 memcpy(dest, str, nCopy * sizeof(WCHAR)); 4225 else 4226 nCopy = WideCharToMultiByte(CP_ACP, 0, str, nCopy, dest, 4227 nCharsLeft, NULL, NULL); 4228 dest += nCopy * (unicode ? sizeof(WCHAR) : 1); 4229 nCharsLeft -= nCopy; 4230 } 4231 4232 /* append line termination, space allowing */ 4233 if (nCharsLeft > 0) 4234 { 4235 if (unicode) 4236 *((WCHAR *)dest) = '\0'; 4237 else 4238 *dest = '\0'; 4239 nCharsLeft--; 4240 wroteNull = TRUE; 4241 } 4242 4243 TRACE("EM_GETLINE: got %u characters\n", nMaxChars - nCharsLeft); 4244 return nMaxChars - nCharsLeft - (wroteNull ? 1 : 0); 4245 } 4246 case EM_GETLINECOUNT: 4247 { 4248 ME_DisplayItem *item = editor->pBuffer->pLast; 4249 int nRows = editor->total_rows; 4250 ME_DisplayItem *prev_para = NULL, *last_para = NULL; 4251 4252 last_para = ME_FindItemBack(item, diRun); 4253 prev_para = ME_FindItemBack(last_para, diRun); 4254 assert(last_para); 4255 assert(last_para->member.run.nFlags & MERF_ENDPARA); 4256 if (editor->bEmulateVersion10 && prev_para && 4257 last_para->member.run.nCharOfs == 0 && 4258 prev_para->member.run.len == 1 && 4259 *get_text( &prev_para->member.run, 0 ) == '\r') 4260 { 4261 /* In 1.0 emulation, the last solitary \r at the very end of the text 4262 (if one exists) is NOT a line break. 4263 FIXME: this is an ugly hack. This should have a more regular model. */ 4264 nRows--; 4265 } 4266 4267 TRACE("EM_GETLINECOUNT: nRows==%d\n", nRows); 4268 return max(1, nRows); 4269 } 4270 case EM_LINEFROMCHAR: 4271 { 4272 if (wParam == -1) 4273 return ME_RowNumberFromCharOfs(editor, ME_GetCursorOfs(&editor->pCursors[1])); 4274 else 4275 return ME_RowNumberFromCharOfs(editor, wParam); 4276 } 4277 case EM_EXLINEFROMCHAR: 4278 { 4279 if (lParam == -1) 4280 return ME_RowNumberFromCharOfs(editor, ME_GetCursorOfs(&editor->pCursors[1])); 4281 else 4282 return ME_RowNumberFromCharOfs(editor, lParam); 4283 } 4284 case EM_LINEINDEX: 4285 { 4286 ME_DisplayItem *item, *para; 4287 int nCharOfs; 4288 4289 if (wParam == -1) 4290 item = ME_FindItemBack(editor->pCursors[0].pRun, diStartRow); 4291 else 4292 item = ME_FindRowWithNumber(editor, wParam); 4293 if (!item) 4294 return -1; 4295 para = ME_GetParagraph(item); 4296 item = ME_FindItemFwd(item, diRun); 4297 nCharOfs = para->member.para.nCharOfs + item->member.run.nCharOfs; 4298 TRACE("EM_LINEINDEX: nCharOfs==%d\n", nCharOfs); 4299 return nCharOfs; 4300 } 4301 case EM_LINELENGTH: 4302 { 4303 ME_DisplayItem *item, *item_end; 4304 int nChars = 0, nThisLineOfs = 0, nNextLineOfs = 0; 4305 ME_DisplayItem *para, *run; 4306 4307 if (wParam > ME_GetTextLength(editor)) 4308 return 0; 4309 if (wParam == -1) 4310 { 4311 FIXME("EM_LINELENGTH: returning number of unselected characters on lines with selection unsupported.\n"); 4312 return 0; 4313 } 4314 ME_RunOfsFromCharOfs(editor, wParam, ¶, &run, NULL); 4315 item = ME_RowStart(run); 4316 nThisLineOfs = ME_CharOfsFromRunOfs(editor, para, ME_FindItemFwd(item, diRun), 0); 4317 item_end = ME_FindItemFwd(item, diStartRowOrParagraphOrEnd); 4318 if (item_end->type == diStartRow) { 4319 nNextLineOfs = ME_CharOfsFromRunOfs(editor, para, ME_FindItemFwd(item_end, diRun), 0); 4320 } else { 4321 ME_DisplayItem *endRun = ME_FindItemBack(item_end, diRun); 4322 assert(endRun && endRun->member.run.nFlags & MERF_ENDPARA); 4323 nNextLineOfs = item_end->member.para.nCharOfs - endRun->member.run.len; 4324 } 4325 nChars = nNextLineOfs - nThisLineOfs; 4326 TRACE("EM_LINELENGTH(%ld)==%d\n",wParam, nChars); 4327 return nChars; 4328 } 4329 case EM_EXLIMITTEXT: 4330 { 4331 if ((int)lParam < 0) 4332 return 0; 4333 if (lParam == 0) 4334 editor->nTextLimit = 65536; 4335 else 4336 editor->nTextLimit = (int) lParam; 4337 return 0; 4338 } 4339 case EM_LIMITTEXT: 4340 { 4341 if (wParam == 0) 4342 editor->nTextLimit = 65536; 4343 else 4344 editor->nTextLimit = (int) wParam; 4345 return 0; 4346 } 4347 case EM_GETLIMITTEXT: 4348 { 4349 return editor->nTextLimit; 4350 } 4351 case EM_FINDTEXT: 4352 { 4353 LRESULT r; 4354 if(!unicode){ 4355 FINDTEXTA *ft = (FINDTEXTA *)lParam; 4356 int nChars = MultiByteToWideChar(CP_ACP, 0, ft->lpstrText, -1, NULL, 0); 4357 WCHAR *tmp; 4358 4359 if ((tmp = heap_alloc(nChars * sizeof(*tmp))) != NULL) 4360 MultiByteToWideChar(CP_ACP, 0, ft->lpstrText, -1, tmp, nChars); 4361 r = ME_FindText(editor, wParam, &ft->chrg, tmp, NULL); 4362 heap_free(tmp); 4363 }else{ 4364 FINDTEXTW *ft = (FINDTEXTW *)lParam; 4365 r = ME_FindText(editor, wParam, &ft->chrg, ft->lpstrText, NULL); 4366 } 4367 return r; 4368 } 4369 case EM_FINDTEXTEX: 4370 { 4371 LRESULT r; 4372 if(!unicode){ 4373 FINDTEXTEXA *ex = (FINDTEXTEXA *)lParam; 4374 int nChars = MultiByteToWideChar(CP_ACP, 0, ex->lpstrText, -1, NULL, 0); 4375 WCHAR *tmp; 4376 4377 if ((tmp = heap_alloc(nChars * sizeof(*tmp))) != NULL) 4378 MultiByteToWideChar(CP_ACP, 0, ex->lpstrText, -1, tmp, nChars); 4379 r = ME_FindText(editor, wParam, &ex->chrg, tmp, &ex->chrgText); 4380 heap_free(tmp); 4381 }else{ 4382 FINDTEXTEXW *ex = (FINDTEXTEXW *)lParam; 4383 r = ME_FindText(editor, wParam, &ex->chrg, ex->lpstrText, &ex->chrgText); 4384 } 4385 return r; 4386 } 4387 case EM_FINDTEXTW: 4388 { 4389 FINDTEXTW *ft = (FINDTEXTW *)lParam; 4390 return ME_FindText(editor, wParam, &ft->chrg, ft->lpstrText, NULL); 4391 } 4392 case EM_FINDTEXTEXW: 4393 { 4394 FINDTEXTEXW *ex = (FINDTEXTEXW *)lParam; 4395 return ME_FindText(editor, wParam, &ex->chrg, ex->lpstrText, &ex->chrgText); 4396 } 4397 case EM_GETZOOM: 4398 if (!wParam || !lParam) 4399 return FALSE; 4400 *(int *)wParam = editor->nZoomNumerator; 4401 *(int *)lParam = editor->nZoomDenominator; 4402 return TRUE; 4403 case EM_SETZOOM: 4404 return ME_SetZoom(editor, wParam, lParam); 4405 case EM_CHARFROMPOS: 4406 { 4407 ME_Cursor cursor; 4408 if (ME_CharFromPos(editor, ((POINTL *)lParam)->x, ((POINTL *)lParam)->y, 4409 &cursor, NULL)) 4410 return ME_GetCursorOfs(&cursor); 4411 else 4412 return -1; 4413 } 4414 case EM_POSFROMCHAR: 4415 { 4416 ME_DisplayItem *pPara, *pRun; 4417 int nCharOfs, nOffset, nLength; 4418 POINTL pt = {0,0}; 4419 4420 nCharOfs = wParam; 4421 /* detect which API version we're dealing with */ 4422 if (wParam >= 0x40000) 4423 nCharOfs = lParam; 4424 nLength = ME_GetTextLength(editor); 4425 nCharOfs = min(nCharOfs, nLength); 4426 nCharOfs = max(nCharOfs, 0); 4427 4428 ME_RunOfsFromCharOfs(editor, nCharOfs, &pPara, &pRun, &nOffset); 4429 assert(pRun->type == diRun); 4430 pt.y = pRun->member.run.pt.y; 4431 pt.x = pRun->member.run.pt.x + ME_PointFromChar(editor, &pRun->member.run, nOffset, TRUE); 4432 pt.y += pPara->member.para.pt.y + editor->rcFormat.top; 4433 pt.x += editor->rcFormat.left; 4434 4435 pt.x -= editor->horz_si.nPos; 4436 pt.y -= editor->vert_si.nPos; 4437 4438 if (wParam >= 0x40000) { 4439 *(POINTL *)wParam = pt; 4440 } 4441 return (wParam >= 0x40000) ? 0 : MAKELONG( pt.x, pt.y ); 4442 } 4443 case WM_CREATE: 4444 return ME_WmCreate(editor, lParam, unicode); 4445 case WM_DESTROY: 4446 ME_DestroyEditor(editor); 4447 return 0; 4448 case WM_SETCURSOR: 4449 { 4450 POINT cursor_pos; 4451 if (wParam == (WPARAM)editor->hWnd && GetCursorPos(&cursor_pos) && 4452 ScreenToClient(editor->hWnd, &cursor_pos)) 4453 ME_LinkNotify(editor, msg, 0, MAKELPARAM(cursor_pos.x, cursor_pos.y)); 4454 return ME_SetCursor(editor); 4455 } 4456 case WM_LBUTTONDBLCLK: 4457 case WM_LBUTTONDOWN: 4458 { 4459 ME_CommitUndo(editor); /* End coalesced undos for typed characters */ 4460 if ((editor->nEventMask & ENM_MOUSEEVENTS) && 4461 !ME_FilterEvent(editor, msg, &wParam, &lParam)) 4462 return 0; 4463 ITextHost_TxSetFocus(editor->texthost); 4464 ME_LButtonDown(editor, (short)LOWORD(lParam), (short)HIWORD(lParam), 4465 ME_CalculateClickCount(editor, msg, wParam, lParam)); 4466 ITextHost_TxSetCapture(editor->texthost, TRUE); 4467 editor->bMouseCaptured = TRUE; 4468 ME_LinkNotify(editor, msg, wParam, lParam); 4469 if (!ME_SetCursor(editor)) goto do_default; 4470 break; 4471 } 4472 case WM_MOUSEMOVE: 4473 if ((editor->nEventMask & ENM_MOUSEEVENTS) && 4474 !ME_FilterEvent(editor, msg, &wParam, &lParam)) 4475 return 0; 4476 if (editor->bMouseCaptured) 4477 ME_MouseMove(editor, (short)LOWORD(lParam), (short)HIWORD(lParam)); 4478 else 4479 ME_LinkNotify(editor, msg, wParam, lParam); 4480 /* Set cursor if mouse is captured, since WM_SETCURSOR won't be received. */ 4481 if (editor->bMouseCaptured) 4482 ME_SetCursor(editor); 4483 break; 4484 case WM_LBUTTONUP: 4485 if (editor->bMouseCaptured) { 4486 ITextHost_TxSetCapture(editor->texthost, FALSE); 4487 editor->bMouseCaptured = FALSE; 4488 } 4489 if (editor->nSelectionType == stDocument) 4490 editor->nSelectionType = stPosition; 4491 if ((editor->nEventMask & ENM_MOUSEEVENTS) && 4492 !ME_FilterEvent(editor, msg, &wParam, &lParam)) 4493 return 0; 4494 else 4495 { 4496 ME_SetCursor(editor); 4497 ME_LinkNotify(editor, msg, wParam, lParam); 4498 } 4499 break; 4500 case WM_RBUTTONUP: 4501 case WM_RBUTTONDOWN: 4502 case WM_RBUTTONDBLCLK: 4503 ME_CommitUndo(editor); /* End coalesced undos for typed characters */ 4504 if ((editor->nEventMask & ENM_MOUSEEVENTS) && 4505 !ME_FilterEvent(editor, msg, &wParam, &lParam)) 4506 return 0; 4507 ME_LinkNotify(editor, msg, wParam, lParam); 4508 goto do_default; 4509 case WM_CONTEXTMENU: 4510 if (!ME_ShowContextMenu(editor, (short)LOWORD(lParam), (short)HIWORD(lParam))) 4511 goto do_default; 4512 break; 4513 case WM_SETFOCUS: 4514 editor->bHaveFocus = TRUE; 4515 ME_ShowCaret(editor); 4516 ME_SendOldNotify(editor, EN_SETFOCUS); 4517 if (!editor->bHideSelection && !(editor->styleFlags & ES_NOHIDESEL)) 4518 ME_InvalidateSelection( editor ); 4519 return 0; 4520 case WM_KILLFOCUS: 4521 ME_CommitUndo(editor); /* End coalesced undos for typed characters */ 4522 editor->bHaveFocus = FALSE; 4523 editor->wheel_remain = 0; 4524 ME_HideCaret(editor); 4525 ME_SendOldNotify(editor, EN_KILLFOCUS); 4526 if (!editor->bHideSelection && !(editor->styleFlags & ES_NOHIDESEL)) 4527 ME_InvalidateSelection( editor ); 4528 return 0; 4529 case WM_COMMAND: 4530 TRACE("editor wnd command = %d\n", LOWORD(wParam)); 4531 return 0; 4532 case WM_KEYUP: 4533 if ((editor->nEventMask & ENM_KEYEVENTS) && 4534 !ME_FilterEvent(editor, msg, &wParam, &lParam)) 4535 return 0; 4536 goto do_default; 4537 case WM_KEYDOWN: 4538 if ((editor->nEventMask & ENM_KEYEVENTS) && 4539 !ME_FilterEvent(editor, msg, &wParam, &lParam)) 4540 return 0; 4541 if (ME_KeyDown(editor, LOWORD(wParam))) 4542 return 0; 4543 goto do_default; 4544 case WM_CHAR: 4545 if ((editor->nEventMask & ENM_KEYEVENTS) && 4546 !ME_FilterEvent(editor, msg, &wParam, &lParam)) 4547 return 0; 4548 return ME_Char(editor, wParam, lParam, unicode); 4549 case WM_UNICHAR: 4550 if (unicode) 4551 { 4552 if(wParam == UNICODE_NOCHAR) return TRUE; 4553 if(wParam <= 0x000fffff) 4554 { 4555 if(wParam > 0xffff) /* convert to surrogates */ 4556 { 4557 wParam -= 0x10000; 4558 ME_Char(editor, (wParam >> 10) + 0xd800, 0, TRUE); 4559 ME_Char(editor, (wParam & 0x03ff) + 0xdc00, 0, TRUE); 4560 } else { 4561 ME_Char(editor, wParam, 0, TRUE); 4562 } 4563 } 4564 return 0; 4565 } 4566 break; 4567 case EM_STOPGROUPTYPING: 4568 ME_CommitUndo(editor); /* End coalesced undos for typed characters */ 4569 return 0; 4570 case WM_HSCROLL: 4571 { 4572 const int scrollUnit = 7; 4573 4574 switch(LOWORD(wParam)) 4575 { 4576 case SB_LEFT: 4577 ME_ScrollAbs(editor, 0, 0); 4578 break; 4579 case SB_RIGHT: 4580 ME_ScrollAbs(editor, 4581 editor->horz_si.nMax - (int)editor->horz_si.nPage, 4582 editor->vert_si.nMax - (int)editor->vert_si.nPage); 4583 break; 4584 case SB_LINELEFT: 4585 ME_ScrollLeft(editor, scrollUnit); 4586 break; 4587 case SB_LINERIGHT: 4588 ME_ScrollRight(editor, scrollUnit); 4589 break; 4590 case SB_PAGELEFT: 4591 ME_ScrollLeft(editor, editor->sizeWindow.cx); 4592 break; 4593 case SB_PAGERIGHT: 4594 ME_ScrollRight(editor, editor->sizeWindow.cx); 4595 break; 4596 case SB_THUMBTRACK: 4597 case SB_THUMBPOSITION: 4598 { 4599 int pos = HIWORD(wParam); 4600 if (editor->horz_si.nMax > 0xffff) 4601 pos = MulDiv(pos, editor->horz_si.nMax, 0xffff); 4602 ME_HScrollAbs(editor, pos); 4603 break; 4604 } 4605 } 4606 break; 4607 } 4608 case EM_SCROLL: /* fall through */ 4609 case WM_VSCROLL: 4610 { 4611 int origNPos; 4612 int lineHeight = get_default_line_height( editor ); 4613 4614 origNPos = editor->vert_si.nPos; 4615 4616 switch(LOWORD(wParam)) 4617 { 4618 case SB_TOP: 4619 ME_ScrollAbs(editor, 0, 0); 4620 break; 4621 case SB_BOTTOM: 4622 ME_ScrollAbs(editor, 4623 editor->horz_si.nMax - (int)editor->horz_si.nPage, 4624 editor->vert_si.nMax - (int)editor->vert_si.nPage); 4625 break; 4626 case SB_LINEUP: 4627 ME_ScrollUp(editor,lineHeight); 4628 break; 4629 case SB_LINEDOWN: 4630 ME_ScrollDown(editor,lineHeight); 4631 break; 4632 case SB_PAGEUP: 4633 ME_ScrollUp(editor,editor->sizeWindow.cy); 4634 break; 4635 case SB_PAGEDOWN: 4636 ME_ScrollDown(editor,editor->sizeWindow.cy); 4637 break; 4638 case SB_THUMBTRACK: 4639 case SB_THUMBPOSITION: 4640 { 4641 int pos = HIWORD(wParam); 4642 if (editor->vert_si.nMax > 0xffff) 4643 pos = MulDiv(pos, editor->vert_si.nMax, 0xffff); 4644 ME_VScrollAbs(editor, pos); 4645 break; 4646 } 4647 } 4648 if (msg == EM_SCROLL) 4649 return 0x00010000 | (((editor->vert_si.nPos - origNPos)/lineHeight) & 0xffff); 4650 break; 4651 } 4652 case WM_MOUSEWHEEL: 4653 { 4654 int delta; 4655 BOOL ctrl_is_down; 4656 4657 if ((editor->nEventMask & ENM_MOUSEEVENTS) && 4658 !ME_FilterEvent(editor, msg, &wParam, &lParam)) 4659 return 0; 4660 4661 ctrl_is_down = GetKeyState(VK_CONTROL) & 0x8000; 4662 4663 delta = GET_WHEEL_DELTA_WPARAM(wParam); 4664 4665 /* if scrolling changes direction, ignore left overs */ 4666 if ((delta < 0 && editor->wheel_remain < 0) || 4667 (delta > 0 && editor->wheel_remain > 0)) 4668 editor->wheel_remain += delta; 4669 else 4670 editor->wheel_remain = delta; 4671 4672 if (editor->wheel_remain) 4673 { 4674 if (ctrl_is_down) { 4675 int numerator; 4676 if (!editor->nZoomNumerator || !editor->nZoomDenominator) 4677 { 4678 numerator = 100; 4679 } else { 4680 numerator = editor->nZoomNumerator * 100 / editor->nZoomDenominator; 4681 } 4682 numerator += calc_wheel_change( &editor->wheel_remain, 10 ); 4683 if (numerator >= 10 && numerator <= 500) 4684 ME_SetZoom(editor, numerator, 100); 4685 } else { 4686 UINT max_lines = 3; 4687 int lines = 0; 4688 4689 SystemParametersInfoW( SPI_GETWHEELSCROLLLINES, 0, &max_lines, 0 ); 4690 if (max_lines) 4691 lines = calc_wheel_change( &editor->wheel_remain, (int)max_lines ); 4692 if (lines) 4693 ME_ScrollDown( editor, -lines * get_default_line_height( editor ) ); 4694 } 4695 } 4696 break; 4697 } 4698 case EM_GETRECT: 4699 { 4700 *((RECT *)lParam) = editor->rcFormat; 4701 if (editor->bDefaultFormatRect) 4702 ((RECT *)lParam)->left -= editor->selofs; 4703 return 0; 4704 } 4705 case EM_SETRECT: 4706 case EM_SETRECTNP: 4707 { 4708 if (lParam) 4709 { 4710 int border = 0; 4711 RECT clientRect; 4712 RECT *rc = (RECT *)lParam; 4713 4714 border = editor->exStyleFlags & WS_EX_CLIENTEDGE ? 1 : 0; 4715 ITextHost_TxGetClientRect(editor->texthost, &clientRect); 4716 if (wParam == 0) 4717 { 4718 editor->rcFormat.top = max(0, rc->top - border); 4719 editor->rcFormat.left = max(0, rc->left - border); 4720 editor->rcFormat.bottom = min(clientRect.bottom, rc->bottom); 4721 editor->rcFormat.right = min(clientRect.right, rc->right + border); 4722 } else if (wParam == 1) { 4723 /* MSDN incorrectly says a wParam value of 1 causes the 4724 * lParam rect to be used as a relative offset, 4725 * however, the tests show it just prevents min/max bound 4726 * checking. */ 4727 editor->rcFormat.top = rc->top - border; 4728 editor->rcFormat.left = rc->left - border; 4729 editor->rcFormat.bottom = rc->bottom; 4730 editor->rcFormat.right = rc->right + border; 4731 } else { 4732 return 0; 4733 } 4734 editor->bDefaultFormatRect = FALSE; 4735 } 4736 else 4737 { 4738 ME_SetDefaultFormatRect(editor); 4739 editor->bDefaultFormatRect = TRUE; 4740 } 4741 ME_MarkAllForWrapping(editor); 4742 ME_WrapMarkedParagraphs(editor); 4743 ME_UpdateScrollBar(editor); 4744 if (msg != EM_SETRECTNP) 4745 ME_Repaint(editor); 4746 return 0; 4747 } 4748 case EM_REQUESTRESIZE: 4749 ME_SendRequestResize(editor, TRUE); 4750 return 0; 4751 case WM_SETREDRAW: 4752 goto do_default; 4753 case WM_WINDOWPOSCHANGED: 4754 { 4755 RECT clientRect; 4756 WINDOWPOS *winpos = (WINDOWPOS *)lParam; 4757 4758 if (winpos->flags & SWP_NOCLIENTSIZE) goto do_default; 4759 ITextHost_TxGetClientRect(editor->texthost, &clientRect); 4760 if (editor->bDefaultFormatRect) { 4761 ME_SetDefaultFormatRect(editor); 4762 } else { 4763 editor->rcFormat.right += clientRect.right - editor->prevClientRect.right; 4764 editor->rcFormat.bottom += clientRect.bottom - editor->prevClientRect.bottom; 4765 } 4766 editor->prevClientRect = clientRect; 4767 ME_RewrapRepaint(editor); 4768 goto do_default; 4769 } 4770 /* IME messages to make richedit controls IME aware */ 4771 case WM_IME_SETCONTEXT: 4772 case WM_IME_CONTROL: 4773 case WM_IME_SELECT: 4774 case WM_IME_COMPOSITIONFULL: 4775 return 0; 4776 case WM_IME_STARTCOMPOSITION: 4777 { 4778 editor->imeStartIndex=ME_GetCursorOfs(&editor->pCursors[0]); 4779 ME_DeleteSelection(editor); 4780 ME_CommitUndo(editor); 4781 ME_UpdateRepaint(editor, FALSE); 4782 return 0; 4783 } 4784 case WM_IME_COMPOSITION: 4785 { 4786 HIMC hIMC; 4787 4788 ME_Style *style = ME_GetInsertStyle(editor, 0); 4789 hIMC = ITextHost_TxImmGetContext(editor->texthost); 4790 ME_DeleteSelection(editor); 4791 ME_SaveTempStyle(editor, style); 4792 if (lParam & (GCS_RESULTSTR|GCS_COMPSTR)) 4793 { 4794 LPWSTR lpCompStr = NULL; 4795 DWORD dwBufLen; 4796 DWORD dwIndex = lParam & GCS_RESULTSTR; 4797 if (!dwIndex) 4798 dwIndex = GCS_COMPSTR; 4799 4800 dwBufLen = ImmGetCompositionStringW(hIMC, dwIndex, NULL, 0); 4801 lpCompStr = HeapAlloc(GetProcessHeap(),0,dwBufLen + sizeof(WCHAR)); 4802 ImmGetCompositionStringW(hIMC, dwIndex, lpCompStr, dwBufLen); 4803 lpCompStr[dwBufLen/sizeof(WCHAR)] = 0; 4804 ME_InsertTextFromCursor(editor,0,lpCompStr,dwBufLen/sizeof(WCHAR),style); 4805 HeapFree(GetProcessHeap(), 0, lpCompStr); 4806 4807 if (dwIndex == GCS_COMPSTR) 4808 ME_SetSelection(editor,editor->imeStartIndex, 4809 editor->imeStartIndex + dwBufLen/sizeof(WCHAR)); 4810 } 4811 ME_ReleaseStyle(style); 4812 ME_CommitUndo(editor); 4813 ME_UpdateRepaint(editor, FALSE); 4814 return 0; 4815 } 4816 case WM_IME_ENDCOMPOSITION: 4817 { 4818 ME_DeleteSelection(editor); 4819 editor->imeStartIndex=-1; 4820 return 0; 4821 } 4822 case EM_GETOLEINTERFACE: 4823 { 4824 if (!editor->reOle) 4825 if (!CreateIRichEditOle(NULL, editor, (LPVOID *)&editor->reOle)) 4826 return 0; 4827 *(LPVOID *)lParam = editor->reOle; 4828 IRichEditOle_AddRef(editor->reOle); 4829 return 1; 4830 } 4831 case EM_GETPASSWORDCHAR: 4832 { 4833 return editor->cPasswordMask; 4834 } 4835 case EM_SETOLECALLBACK: 4836 if(editor->lpOleCallback) 4837 IRichEditOleCallback_Release(editor->lpOleCallback); 4838 editor->lpOleCallback = (IRichEditOleCallback*)lParam; 4839 if(editor->lpOleCallback) 4840 IRichEditOleCallback_AddRef(editor->lpOleCallback); 4841 return TRUE; 4842 case EM_GETWORDBREAKPROC: 4843 return (LRESULT)editor->pfnWordBreak; 4844 case EM_SETWORDBREAKPROC: 4845 { 4846 EDITWORDBREAKPROCW pfnOld = editor->pfnWordBreak; 4847 4848 editor->pfnWordBreak = (EDITWORDBREAKPROCW)lParam; 4849 return (LRESULT)pfnOld; 4850 } 4851 case EM_GETTEXTMODE: 4852 return editor->mode; 4853 case EM_SETTEXTMODE: 4854 { 4855 int mask = 0; 4856 int changes = 0; 4857 4858 if (ME_GetTextLength(editor) || 4859 !list_empty( &editor->undo_stack ) || !list_empty( &editor->redo_stack )) 4860 return E_UNEXPECTED; 4861 4862 /* Check for mutually exclusive flags in adjacent bits of wParam */ 4863 if ((wParam & (TM_RICHTEXT | TM_MULTILEVELUNDO | TM_MULTICODEPAGE)) & 4864 (wParam & (TM_PLAINTEXT | TM_SINGLELEVELUNDO | TM_SINGLECODEPAGE)) << 1) 4865 return E_INVALIDARG; 4866 4867 if (wParam & (TM_RICHTEXT | TM_PLAINTEXT)) 4868 { 4869 mask |= TM_RICHTEXT | TM_PLAINTEXT; 4870 changes |= wParam & (TM_RICHTEXT | TM_PLAINTEXT); 4871 if (wParam & TM_PLAINTEXT) { 4872 /* Clear selection since it should be possible to select the 4873 * end of text run for rich text */ 4874 ME_InvalidateSelection(editor); 4875 ME_SetCursorToStart(editor, &editor->pCursors[0]); 4876 editor->pCursors[1] = editor->pCursors[0]; 4877 /* plain text can only have the default style. */ 4878 ME_ClearTempStyle(editor); 4879 ME_AddRefStyle(editor->pBuffer->pDefaultStyle); 4880 ME_ReleaseStyle(editor->pCursors[0].pRun->member.run.style); 4881 editor->pCursors[0].pRun->member.run.style = editor->pBuffer->pDefaultStyle; 4882 } 4883 } 4884 /* FIXME: Currently no support for undo level and code page options */ 4885 editor->mode = (editor->mode & ~mask) | changes; 4886 return 0; 4887 } 4888 case EM_SETPASSWORDCHAR: 4889 { 4890 editor->cPasswordMask = wParam; 4891 ME_RewrapRepaint(editor); 4892 return 0; 4893 } 4894 case EM_SETTARGETDEVICE: 4895 if (wParam == 0) 4896 { 4897 BOOL new = (lParam == 0 && (editor->styleFlags & ES_MULTILINE)); 4898 if (editor->nAvailWidth || editor->bWordWrap != new) 4899 { 4900 editor->bWordWrap = new; 4901 editor->nAvailWidth = 0; /* wrap to client area */ 4902 ME_RewrapRepaint(editor); 4903 } 4904 } else { 4905 int width = max(0, lParam); 4906 if ((editor->styleFlags & ES_MULTILINE) && 4907 (!editor->bWordWrap || editor->nAvailWidth != width)) 4908 { 4909 editor->nAvailWidth = width; 4910 editor->bWordWrap = TRUE; 4911 ME_RewrapRepaint(editor); 4912 } 4913 FIXME("EM_SETTARGETDEVICE doesn't use non-NULL target devices\n"); 4914 } 4915 return TRUE; 4916 default: 4917 do_default: 4918 *phresult = S_FALSE; 4919 break; 4920 } 4921 return 0L; 4922 } 4923 4924 static BOOL create_windowed_editor(HWND hwnd, CREATESTRUCTW *create, BOOL emulate_10) 4925 { 4926 ITextHost *host = ME_CreateTextHost( hwnd, create, emulate_10 ); 4927 ME_TextEditor *editor; 4928 4929 if (!host) return FALSE; 4930 4931 editor = ME_MakeEditor( host, emulate_10 ); 4932 if (!editor) 4933 { 4934 ITextHost_Release( host ); 4935 return FALSE; 4936 } 4937 4938 editor->exStyleFlags = GetWindowLongW( hwnd, GWL_EXSTYLE ); 4939 editor->styleFlags |= GetWindowLongW( hwnd, GWL_STYLE ) & ES_WANTRETURN; 4940 editor->hWnd = hwnd; /* FIXME: Remove editor's dependence on hWnd */ 4941 editor->hwndParent = create->hwndParent; 4942 4943 SetWindowLongPtrW( hwnd, 0, (LONG_PTR)editor ); 4944 4945 return TRUE; 4946 } 4947 4948 static LRESULT RichEditWndProc_common(HWND hWnd, UINT msg, WPARAM wParam, 4949 LPARAM lParam, BOOL unicode) 4950 { 4951 ME_TextEditor *editor; 4952 HRESULT hresult; 4953 LRESULT lresult = 0; 4954 4955 TRACE("enter hwnd %p msg %04x (%s) %lx %lx, unicode %d\n", 4956 hWnd, msg, get_msg_name(msg), wParam, lParam, unicode); 4957 4958 editor = (ME_TextEditor *)GetWindowLongPtrW(hWnd, 0); 4959 if (!editor) 4960 { 4961 if (msg == WM_NCCREATE) 4962 { 4963 CREATESTRUCTW *pcs = (CREATESTRUCTW *)lParam; 4964 4965 TRACE("WM_NCCREATE: hWnd %p style 0x%08x\n", hWnd, pcs->style); 4966 return create_windowed_editor( hWnd, pcs, FALSE ); 4967 } 4968 else 4969 { 4970 return DefWindowProcW(hWnd, msg, wParam, lParam); 4971 } 4972 } 4973 4974 switch (msg) 4975 { 4976 case WM_PAINT: 4977 { 4978 HDC hDC; 4979 RECT rc; 4980 PAINTSTRUCT ps; 4981 4982 hDC = BeginPaint(editor->hWnd, &ps); 4983 if (!editor->bEmulateVersion10 || (editor->nEventMask & ENM_UPDATE)) 4984 ME_SendOldNotify(editor, EN_UPDATE); 4985 /* Erase area outside of the formatting rectangle */ 4986 if (ps.rcPaint.top < editor->rcFormat.top) 4987 { 4988 rc = ps.rcPaint; 4989 rc.bottom = editor->rcFormat.top; 4990 FillRect(hDC, &rc, editor->hbrBackground); 4991 ps.rcPaint.top = editor->rcFormat.top; 4992 } 4993 if (ps.rcPaint.bottom > editor->rcFormat.bottom) { 4994 rc = ps.rcPaint; 4995 rc.top = editor->rcFormat.bottom; 4996 FillRect(hDC, &rc, editor->hbrBackground); 4997 ps.rcPaint.bottom = editor->rcFormat.bottom; 4998 } 4999 if (ps.rcPaint.left < editor->rcFormat.left) { 5000 rc = ps.rcPaint; 5001 rc.right = editor->rcFormat.left; 5002 FillRect(hDC, &rc, editor->hbrBackground); 5003 ps.rcPaint.left = editor->rcFormat.left; 5004 } 5005 if (ps.rcPaint.right > editor->rcFormat.right) { 5006 rc = ps.rcPaint; 5007 rc.left = editor->rcFormat.right; 5008 FillRect(hDC, &rc, editor->hbrBackground); 5009 ps.rcPaint.right = editor->rcFormat.right; 5010 } 5011 5012 ME_PaintContent(editor, hDC, &ps.rcPaint); 5013 EndPaint(editor->hWnd, &ps); 5014 return 0; 5015 } 5016 case WM_ERASEBKGND: 5017 { 5018 HDC hDC = (HDC)wParam; 5019 RECT rc; 5020 5021 if (GetUpdateRect(editor->hWnd, &rc, TRUE)) 5022 FillRect(hDC, &rc, editor->hbrBackground); 5023 return 1; 5024 } 5025 case EM_SETOPTIONS: 5026 { 5027 DWORD dwStyle; 5028 const DWORD mask = ECO_VERTICAL | ECO_AUTOHSCROLL | ECO_AUTOVSCROLL | 5029 ECO_NOHIDESEL | ECO_READONLY | ECO_WANTRETURN | 5030 ECO_SELECTIONBAR; 5031 lresult = ME_HandleMessage(editor, msg, wParam, lParam, unicode, &hresult); 5032 dwStyle = GetWindowLongW(hWnd, GWL_STYLE); 5033 dwStyle = (dwStyle & ~mask) | (lresult & mask); 5034 SetWindowLongW(hWnd, GWL_STYLE, dwStyle); 5035 return lresult; 5036 } 5037 case EM_SETREADONLY: 5038 { 5039 DWORD dwStyle; 5040 lresult = ME_HandleMessage(editor, msg, wParam, lParam, unicode, &hresult); 5041 dwStyle = GetWindowLongW(hWnd, GWL_STYLE); 5042 dwStyle &= ~ES_READONLY; 5043 if (wParam) 5044 dwStyle |= ES_READONLY; 5045 SetWindowLongW(hWnd, GWL_STYLE, dwStyle); 5046 return lresult; 5047 } 5048 default: 5049 lresult = ME_HandleMessage(editor, msg, wParam, lParam, unicode, &hresult); 5050 } 5051 5052 if (hresult == S_FALSE) 5053 lresult = DefWindowProcW(hWnd, msg, wParam, lParam); 5054 5055 TRACE("exit hwnd %p msg %04x (%s) %lx %lx, unicode %d -> %lu\n", 5056 hWnd, msg, get_msg_name(msg), wParam, lParam, unicode, lresult); 5057 5058 return lresult; 5059 } 5060 5061 static LRESULT WINAPI RichEditWndProcW(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) 5062 { 5063 BOOL unicode = TRUE; 5064 5065 /* Under Win9x RichEdit20W returns ANSI strings, see the tests. */ 5066 if (msg == WM_GETTEXT && (GetVersion() & 0x80000000)) 5067 unicode = FALSE; 5068 5069 return RichEditWndProc_common(hWnd, msg, wParam, lParam, unicode); 5070 } 5071 5072 static LRESULT WINAPI RichEditWndProcA(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) 5073 { 5074 return RichEditWndProc_common(hWnd, msg, wParam, lParam, FALSE); 5075 } 5076 5077 /****************************************************************** 5078 * RichEditANSIWndProc (RICHED20.10) 5079 */ 5080 LRESULT WINAPI RichEditANSIWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) 5081 { 5082 return RichEditWndProcA(hWnd, msg, wParam, lParam); 5083 } 5084 5085 /****************************************************************** 5086 * RichEdit10ANSIWndProc (RICHED20.9) 5087 */ 5088 LRESULT WINAPI RichEdit10ANSIWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) 5089 { 5090 if (msg == WM_NCCREATE && !GetWindowLongPtrW(hWnd, 0)) 5091 { 5092 CREATESTRUCTW *pcs = (CREATESTRUCTW *)lParam; 5093 5094 TRACE("WM_NCCREATE: hWnd %p style 0x%08x\n", hWnd, pcs->style); 5095 return create_windowed_editor( hWnd, pcs, TRUE ); 5096 } 5097 return RichEditANSIWndProc(hWnd, msg, wParam, lParam); 5098 } 5099 5100 void ME_SendOldNotify(ME_TextEditor *editor, int nCode) 5101 { 5102 ITextHost_TxNotify(editor->texthost, nCode, NULL); 5103 } 5104 5105 /* Fill buffer with srcChars unicode characters from the start cursor. 5106 * 5107 * buffer: destination buffer 5108 * buflen: length of buffer in characters excluding the NULL terminator. 5109 * start: start of editor text to copy into buffer. 5110 * srcChars: Number of characters to use from the editor text. 5111 * bCRLF: if true, replaces all end of lines with \r\n pairs. 5112 * 5113 * returns the number of characters written excluding the NULL terminator. 5114 * 5115 * The written text is always NULL terminated. 5116 */ 5117 int ME_GetTextW(ME_TextEditor *editor, WCHAR *buffer, int buflen, 5118 const ME_Cursor *start, int srcChars, BOOL bCRLF, 5119 BOOL bEOP) 5120 { 5121 ME_DisplayItem *pRun, *pNextRun; 5122 const WCHAR *pStart = buffer; 5123 const WCHAR cr_lf[] = {'\r', '\n', 0}; 5124 const WCHAR *str; 5125 int nLen; 5126 5127 /* bCRLF flag is only honored in 2.0 and up. 1.0 must always return text verbatim */ 5128 if (editor->bEmulateVersion10) bCRLF = FALSE; 5129 5130 pRun = start->pRun; 5131 assert(pRun); 5132 pNextRun = ME_FindItemFwd(pRun, diRun); 5133 5134 nLen = pRun->member.run.len - start->nOffset; 5135 str = get_text( &pRun->member.run, start->nOffset ); 5136 5137 while (srcChars && buflen && pNextRun) 5138 { 5139 int nFlags = pRun->member.run.nFlags; 5140 5141 if (bCRLF && nFlags & MERF_ENDPARA && ~nFlags & MERF_ENDCELL) 5142 { 5143 if (buflen == 1) break; 5144 /* FIXME: native fails to reduce srcChars here for WM_GETTEXT or 5145 * EM_GETTEXTEX, however, this is done for copying text which 5146 * also uses this function. */ 5147 srcChars -= min(nLen, srcChars); 5148 nLen = 2; 5149 str = cr_lf; 5150 } else { 5151 nLen = min(nLen, srcChars); 5152 srcChars -= nLen; 5153 } 5154 5155 nLen = min(nLen, buflen); 5156 buflen -= nLen; 5157 5158 CopyMemory(buffer, str, sizeof(WCHAR) * nLen); 5159 5160 buffer += nLen; 5161 5162 pRun = pNextRun; 5163 pNextRun = ME_FindItemFwd(pRun, diRun); 5164 5165 nLen = pRun->member.run.len; 5166 str = get_text( &pRun->member.run, 0 ); 5167 } 5168 /* append '\r' to the last paragraph. */ 5169 if (pRun->next->type == diTextEnd && bEOP) 5170 { 5171 *buffer = '\r'; 5172 buffer ++; 5173 } 5174 *buffer = 0; 5175 return buffer - pStart; 5176 } 5177 5178 static BOOL ME_RegisterEditorClass(HINSTANCE hInstance) 5179 { 5180 WNDCLASSW wcW; 5181 WNDCLASSA wcA; 5182 5183 wcW.style = CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW | CS_GLOBALCLASS; 5184 wcW.lpfnWndProc = RichEditWndProcW; 5185 wcW.cbClsExtra = 0; 5186 wcW.cbWndExtra = sizeof(ME_TextEditor *); 5187 wcW.hInstance = NULL; /* hInstance would register DLL-local class */ 5188 wcW.hIcon = NULL; 5189 wcW.hCursor = LoadCursorW(NULL, (LPWSTR)IDC_IBEAM); 5190 wcW.hbrBackground = GetStockObject(NULL_BRUSH); 5191 wcW.lpszMenuName = NULL; 5192 5193 if (is_version_nt()) 5194 { 5195 wcW.lpszClassName = RICHEDIT_CLASS20W; 5196 if (!RegisterClassW(&wcW)) return FALSE; 5197 wcW.lpszClassName = MSFTEDIT_CLASS; 5198 if (!RegisterClassW(&wcW)) return FALSE; 5199 } 5200 else 5201 { 5202 /* WNDCLASSA/W have the same layout */ 5203 wcW.lpszClassName = (LPCWSTR)"RichEdit20W"; 5204 if (!RegisterClassA((WNDCLASSA *)&wcW)) return FALSE; 5205 wcW.lpszClassName = (LPCWSTR)"RichEdit50W"; 5206 if (!RegisterClassA((WNDCLASSA *)&wcW)) return FALSE; 5207 } 5208 5209 wcA.style = CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW | CS_GLOBALCLASS; 5210 wcA.lpfnWndProc = RichEditWndProcA; 5211 wcA.cbClsExtra = 0; 5212 wcA.cbWndExtra = sizeof(ME_TextEditor *); 5213 wcA.hInstance = NULL; /* hInstance would register DLL-local class */ 5214 wcA.hIcon = NULL; 5215 wcA.hCursor = LoadCursorW(NULL, (LPWSTR)IDC_IBEAM); 5216 wcA.hbrBackground = GetStockObject(NULL_BRUSH); 5217 wcA.lpszMenuName = NULL; 5218 wcA.lpszClassName = RICHEDIT_CLASS20A; 5219 if (!RegisterClassA(&wcA)) return FALSE; 5220 wcA.lpszClassName = "RichEdit50A"; 5221 if (!RegisterClassA(&wcA)) return FALSE; 5222 5223 return TRUE; 5224 } 5225 5226 static LRESULT WINAPI REComboWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { 5227 /* FIXME: Not implemented */ 5228 TRACE("hWnd %p msg %04x (%s) %08lx %08lx\n", 5229 hWnd, msg, get_msg_name(msg), wParam, lParam); 5230 return DefWindowProcW(hWnd, msg, wParam, lParam); 5231 } 5232 5233 static LRESULT WINAPI REListWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { 5234 /* FIXME: Not implemented */ 5235 TRACE("hWnd %p msg %04x (%s) %08lx %08lx\n", 5236 hWnd, msg, get_msg_name(msg), wParam, lParam); 5237 return DefWindowProcW(hWnd, msg, wParam, lParam); 5238 } 5239 5240 /****************************************************************** 5241 * REExtendedRegisterClass (RICHED20.8) 5242 * 5243 * FIXME undocumented 5244 * Need to check for errors and implement controls and callbacks 5245 */ 5246 LRESULT WINAPI REExtendedRegisterClass(void) 5247 { 5248 WNDCLASSW wcW; 5249 UINT result; 5250 5251 FIXME("semi stub\n"); 5252 5253 wcW.cbClsExtra = 0; 5254 wcW.cbWndExtra = 4; 5255 wcW.hInstance = NULL; 5256 wcW.hIcon = NULL; 5257 wcW.hCursor = NULL; 5258 wcW.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); 5259 wcW.lpszMenuName = NULL; 5260 5261 if (!ME_ListBoxRegistered) 5262 { 5263 wcW.style = CS_PARENTDC | CS_DBLCLKS | CS_GLOBALCLASS; 5264 wcW.lpfnWndProc = REListWndProc; 5265 wcW.lpszClassName = REListBox20W; 5266 if (RegisterClassW(&wcW)) ME_ListBoxRegistered = TRUE; 5267 } 5268 5269 if (!ME_ComboBoxRegistered) 5270 { 5271 wcW.style = CS_PARENTDC | CS_DBLCLKS | CS_GLOBALCLASS | CS_VREDRAW | CS_HREDRAW; 5272 wcW.lpfnWndProc = REComboWndProc; 5273 wcW.lpszClassName = REComboBox20W; 5274 if (RegisterClassW(&wcW)) ME_ComboBoxRegistered = TRUE; 5275 } 5276 5277 result = 0; 5278 if (ME_ListBoxRegistered) 5279 result += 1; 5280 if (ME_ComboBoxRegistered) 5281 result += 2; 5282 5283 return result; 5284 } 5285 5286 static int wchar_comp( const void *key, const void *elem ) 5287 { 5288 return *(const WCHAR *)key - *(const WCHAR *)elem; 5289 } 5290 5291 /* neutral characters end the url if the next non-neutral character is a space character, 5292 otherwise they are included in the url. */ 5293 static BOOL isurlneutral( WCHAR c ) 5294 { 5295 /* NB this list is sorted */ 5296 static const WCHAR neutral_chars[] = {'!','\"','\'','(',')',',','-','.',':',';','<','>','?','[',']','{','}'}; 5297 5298 /* Some shortcuts */ 5299 if (isalnum( c )) return FALSE; 5300 if (c > neutral_chars[ARRAY_SIZE( neutral_chars ) - 1]) return FALSE; 5301 5302 return !!bsearch( &c, neutral_chars, ARRAY_SIZE( neutral_chars ), sizeof(c), wchar_comp ); 5303 } 5304 5305 /** 5306 * This proc takes a selection, and scans it forward in order to select the span 5307 * of a possible URL candidate. A possible URL candidate must start with isalnum 5308 * or one of the following special characters: *|/\+%#@ and must consist entirely 5309 * of the characters allowed to start the URL, plus : (colon) which may occur 5310 * at most once, and not at either end. 5311 */ 5312 static BOOL ME_FindNextURLCandidate(ME_TextEditor *editor, 5313 const ME_Cursor *start, 5314 int nChars, 5315 ME_Cursor *candidate_min, 5316 ME_Cursor *candidate_max) 5317 { 5318 ME_Cursor cursor = *start, neutral_end, space_end; 5319 BOOL candidateStarted = FALSE, quoted = FALSE; 5320 WCHAR c; 5321 5322 while (nChars > 0) 5323 { 5324 WCHAR *str = get_text( &cursor.pRun->member.run, 0 ); 5325 int run_len = cursor.pRun->member.run.len; 5326 5327 nChars -= run_len - cursor.nOffset; 5328 5329 /* Find start of candidate */ 5330 if (!candidateStarted) 5331 { 5332 while (cursor.nOffset < run_len) 5333 { 5334 c = str[cursor.nOffset]; 5335 if (!isspaceW( c ) && !isurlneutral( c )) 5336 { 5337 *candidate_min = cursor; 5338 candidateStarted = TRUE; 5339 neutral_end.pPara = NULL; 5340 space_end.pPara = NULL; 5341 cursor.nOffset++; 5342 break; 5343 } 5344 quoted = (c == '<'); 5345 cursor.nOffset++; 5346 } 5347 } 5348 5349 /* Find end of candidate */ 5350 if (candidateStarted) 5351 { 5352 while (cursor.nOffset < run_len) 5353 { 5354 c = str[cursor.nOffset]; 5355 if (isspaceW( c )) 5356 { 5357 if (quoted && c != '\r') 5358 { 5359 if (!space_end.pPara) 5360 { 5361 if (neutral_end.pPara) 5362 space_end = neutral_end; 5363 else 5364 space_end = cursor; 5365 } 5366 } 5367 else 5368 goto done; 5369 } 5370 else if (isurlneutral( c )) 5371 { 5372 if (quoted && c == '>') 5373 { 5374 neutral_end.pPara = NULL; 5375 space_end.pPara = NULL; 5376 goto done; 5377 } 5378 if (!neutral_end.pPara) 5379 neutral_end = cursor; 5380 } 5381 else 5382 neutral_end.pPara = NULL; 5383 5384 cursor.nOffset++; 5385 } 5386 } 5387 5388 cursor.nOffset = 0; 5389 if (!ME_NextRun(&cursor.pPara, &cursor.pRun, TRUE)) 5390 goto done; 5391 } 5392 5393 done: 5394 if (candidateStarted) 5395 { 5396 if (space_end.pPara) 5397 *candidate_max = space_end; 5398 else if (neutral_end.pPara) 5399 *candidate_max = neutral_end; 5400 else 5401 *candidate_max = cursor; 5402 return TRUE; 5403 } 5404 *candidate_max = *candidate_min = cursor; 5405 return FALSE; 5406 } 5407 5408 /** 5409 * This proc evaluates the selection and returns TRUE if it can be considered an URL 5410 */ 5411 static BOOL ME_IsCandidateAnURL(ME_TextEditor *editor, const ME_Cursor *start, int nChars) 5412 { 5413 #define MAX_PREFIX_LEN 9 5414 struct prefix_s { 5415 const WCHAR text[MAX_PREFIX_LEN]; 5416 int length; 5417 }prefixes[] = { 5418 {{'p','r','o','s','p','e','r','o',':'}, 9}, 5419 {{'t','e','l','n','e','t',':'}, 7}, 5420 {{'g','o','p','h','e','r',':'}, 7}, 5421 {{'m','a','i','l','t','o',':'}, 7}, 5422 {{'h','t','t','p','s',':'}, 6}, 5423 {{'f','i','l','e',':'}, 5}, 5424 {{'n','e','w','s',':'}, 5}, 5425 {{'w','a','i','s',':'}, 5}, 5426 {{'n','n','t','p',':'}, 5}, 5427 {{'h','t','t','p',':'}, 5}, 5428 {{'w','w','w','.'}, 4}, 5429 {{'f','t','p',':'}, 4}, 5430 }; 5431 WCHAR bufferW[MAX_PREFIX_LEN + 1]; 5432 unsigned int i; 5433 5434 ME_GetTextW(editor, bufferW, MAX_PREFIX_LEN, start, nChars, FALSE, FALSE); 5435 for (i = 0; i < ARRAY_SIZE(prefixes); i++) 5436 { 5437 if (nChars < prefixes[i].length) continue; 5438 if (!memcmp(prefixes[i].text, bufferW, prefixes[i].length * sizeof(WCHAR))) 5439 return TRUE; 5440 } 5441 return FALSE; 5442 #undef MAX_PREFIX_LEN 5443 } 5444 5445 /** 5446 * This proc walks through the indicated selection and evaluates whether each 5447 * section identified by ME_FindNextURLCandidate and in-between sections have 5448 * their proper CFE_LINK attributes set or unset. If the CFE_LINK attribute is 5449 * not what it is supposed to be, this proc sets or unsets it as appropriate. 5450 * 5451 * Since this function can cause runs to be split, do not depend on the value 5452 * of the start cursor at the end of the function. 5453 * 5454 * nChars may be set to INT_MAX to update to the end of the text. 5455 * 5456 * Returns TRUE if at least one section was modified. 5457 */ 5458 static BOOL ME_UpdateLinkAttribute(ME_TextEditor *editor, ME_Cursor *start, int nChars) 5459 { 5460 BOOL modified = FALSE; 5461 ME_Cursor startCur = *start; 5462 5463 if (!editor->AutoURLDetect_bEnable) return FALSE; 5464 5465 do 5466 { 5467 CHARFORMAT2W link; 5468 ME_Cursor candidateStart, candidateEnd; 5469 5470 if (ME_FindNextURLCandidate(editor, &startCur, nChars, 5471 &candidateStart, &candidateEnd)) 5472 { 5473 /* Section before candidate is not an URL */ 5474 int cMin = ME_GetCursorOfs(&candidateStart); 5475 int cMax = ME_GetCursorOfs(&candidateEnd); 5476 5477 if (!ME_IsCandidateAnURL(editor, &candidateStart, cMax - cMin)) 5478 candidateStart = candidateEnd; 5479 nChars -= cMax - ME_GetCursorOfs(&startCur); 5480 } 5481 else 5482 { 5483 /* No more candidates until end of selection */ 5484 nChars = 0; 5485 } 5486 5487 if (startCur.pRun != candidateStart.pRun || 5488 startCur.nOffset != candidateStart.nOffset) 5489 { 5490 /* CFE_LINK effect should be consistently unset */ 5491 link.cbSize = sizeof(link); 5492 ME_GetCharFormat(editor, &startCur, &candidateStart, &link); 5493 if (!(link.dwMask & CFM_LINK) || (link.dwEffects & CFE_LINK)) 5494 { 5495 /* CFE_LINK must be unset from this range */ 5496 memset(&link, 0, sizeof(CHARFORMAT2W)); 5497 link.cbSize = sizeof(link); 5498 link.dwMask = CFM_LINK; 5499 link.dwEffects = 0; 5500 ME_SetCharFormat(editor, &startCur, &candidateStart, &link); 5501 /* Update candidateEnd since setting character formats may split 5502 * runs, which can cause a cursor to be at an invalid offset within 5503 * a split run. */ 5504 while (candidateEnd.nOffset >= candidateEnd.pRun->member.run.len) 5505 { 5506 candidateEnd.nOffset -= candidateEnd.pRun->member.run.len; 5507 candidateEnd.pRun = ME_FindItemFwd(candidateEnd.pRun, diRun); 5508 } 5509 modified = TRUE; 5510 } 5511 } 5512 if (candidateStart.pRun != candidateEnd.pRun || 5513 candidateStart.nOffset != candidateEnd.nOffset) 5514 { 5515 /* CFE_LINK effect should be consistently set */ 5516 link.cbSize = sizeof(link); 5517 ME_GetCharFormat(editor, &candidateStart, &candidateEnd, &link); 5518 if (!(link.dwMask & CFM_LINK) || !(link.dwEffects & CFE_LINK)) 5519 { 5520 /* CFE_LINK must be set on this range */ 5521 memset(&link, 0, sizeof(CHARFORMAT2W)); 5522 link.cbSize = sizeof(link); 5523 link.dwMask = CFM_LINK; 5524 link.dwEffects = CFE_LINK; 5525 ME_SetCharFormat(editor, &candidateStart, &candidateEnd, &link); 5526 modified = TRUE; 5527 } 5528 } 5529 startCur = candidateEnd; 5530 } while (nChars > 0); 5531 return modified; 5532 } 5533