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 LPRICHEDITOLE lpReOle = NULL; 1132 STGMEDIUM stgm; 1133 FORMATETC fm; 1134 CLSID clsid; 1135 HRESULT hr = E_FAIL; 1136 DWORD conn; 1137 1138 if (hemf) 1139 { 1140 stgm.tymed = TYMED_ENHMF; 1141 stgm.u.hEnhMetaFile = hemf; 1142 fm.cfFormat = CF_ENHMETAFILE; 1143 } 1144 else if (hbmp) 1145 { 1146 stgm.tymed = TYMED_GDI; 1147 stgm.u.hBitmap = hbmp; 1148 fm.cfFormat = CF_BITMAP; 1149 } 1150 stgm.pUnkForRelease = NULL; 1151 1152 fm.ptd = NULL; 1153 fm.dwAspect = DVASPECT_CONTENT; 1154 fm.lindex = -1; 1155 fm.tymed = stgm.tymed; 1156 1157 if (!editor->reOle) 1158 { 1159 if (!CreateIRichEditOle(NULL, editor, (LPVOID *)&editor->reOle)) 1160 return hr; 1161 } 1162 1163 if (OleCreateDefaultHandler(&CLSID_NULL, NULL, &IID_IOleObject, (void**)&lpObject) == S_OK && 1164 IUnknown_QueryInterface(editor->reOle, &IID_IRichEditOle, (void**)&lpReOle) == S_OK && 1165 IRichEditOle_GetClientSite(lpReOle, &lpClientSite) == S_OK && 1166 IOleObject_SetClientSite(lpObject, lpClientSite) == S_OK && 1167 IOleObject_GetUserClassID(lpObject, &clsid) == S_OK && 1168 IOleObject_QueryInterface(lpObject, &IID_IOleCache, (void**)&lpOleCache) == S_OK && 1169 IOleCache_Cache(lpOleCache, &fm, 0, &conn) == S_OK && 1170 IOleObject_QueryInterface(lpObject, &IID_IDataObject, (void**)&lpDataObject) == S_OK && 1171 IDataObject_SetData(lpDataObject, &fm, &stgm, TRUE) == S_OK) 1172 { 1173 REOBJECT reobject; 1174 1175 reobject.cbStruct = sizeof(reobject); 1176 reobject.cp = REO_CP_SELECTION; 1177 reobject.clsid = clsid; 1178 reobject.poleobj = lpObject; 1179 reobject.pstg = lpStorage; 1180 reobject.polesite = lpClientSite; 1181 /* convert from twips to .01 mm */ 1182 reobject.sizel.cx = MulDiv(sz->cx, 254, 144); 1183 reobject.sizel.cy = MulDiv(sz->cy, 254, 144); 1184 reobject.dvaspect = DVASPECT_CONTENT; 1185 reobject.dwFlags = 0; /* FIXME */ 1186 reobject.dwUser = 0; 1187 1188 ME_InsertOLEFromCursor(editor, &reobject, 0); 1189 hr = S_OK; 1190 } 1191 1192 if (lpObject) IOleObject_Release(lpObject); 1193 if (lpClientSite) IOleClientSite_Release(lpClientSite); 1194 if (lpStorage) IStorage_Release(lpStorage); 1195 if (lpDataObject) IDataObject_Release(lpDataObject); 1196 if (lpOleCache) IOleCache_Release(lpOleCache); 1197 if (lpReOle) IRichEditOle_Release(lpReOle); 1198 1199 return hr; 1200 } 1201 1202 static void ME_RTFReadShpPictGroup( RTF_Info *info ) 1203 { 1204 int level = 1; 1205 1206 for (;;) 1207 { 1208 RTFGetToken (info); 1209 1210 if (info->rtfClass == rtfEOF) return; 1211 if (RTFCheckCM( info, rtfGroup, rtfEndGroup )) 1212 { 1213 if (--level == 0) break; 1214 } 1215 else if (RTFCheckCM( info, rtfGroup, rtfBeginGroup )) 1216 { 1217 level++; 1218 } 1219 else 1220 { 1221 RTFRouteToken( info ); 1222 if (RTFCheckCM( info, rtfGroup, rtfEndGroup )) 1223 level--; 1224 } 1225 } 1226 1227 RTFRouteToken( info ); /* feed "}" back to router */ 1228 return; 1229 } 1230 1231 static DWORD read_hex_data( RTF_Info *info, BYTE **out ) 1232 { 1233 DWORD read = 0, size = 1024; 1234 BYTE *buf, val; 1235 BOOL flip; 1236 1237 *out = NULL; 1238 1239 if (info->rtfClass != rtfText) 1240 { 1241 ERR("Called with incorrect token\n"); 1242 return 0; 1243 } 1244 1245 buf = HeapAlloc( GetProcessHeap(), 0, size ); 1246 if (!buf) return 0; 1247 1248 val = info->rtfMajor; 1249 for (flip = TRUE;; flip = !flip) 1250 { 1251 RTFGetToken( info ); 1252 if (info->rtfClass == rtfEOF) 1253 { 1254 HeapFree( GetProcessHeap(), 0, buf ); 1255 return 0; 1256 } 1257 if (info->rtfClass != rtfText) break; 1258 if (flip) 1259 { 1260 if (read >= size) 1261 { 1262 size *= 2; 1263 buf = HeapReAlloc( GetProcessHeap(), 0, buf, size ); 1264 if (!buf) return 0; 1265 } 1266 buf[read++] = RTFCharToHex(val) * 16 + RTFCharToHex(info->rtfMajor); 1267 } 1268 else 1269 val = info->rtfMajor; 1270 } 1271 if (flip) FIXME("wrong hex string\n"); 1272 1273 *out = buf; 1274 return read; 1275 } 1276 1277 static void ME_RTFReadPictGroup(RTF_Info *info) 1278 { 1279 SIZEL sz; 1280 BYTE *buffer = NULL; 1281 DWORD size = 0; 1282 METAFILEPICT mfp; 1283 HENHMETAFILE hemf; 1284 HBITMAP hbmp; 1285 enum gfxkind {gfx_unknown = 0, gfx_enhmetafile, gfx_metafile, gfx_dib} gfx = gfx_unknown; 1286 int level = 1; 1287 1288 mfp.mm = MM_TEXT; 1289 sz.cx = sz.cy = 0; 1290 1291 for (;;) 1292 { 1293 RTFGetToken( info ); 1294 1295 if (info->rtfClass == rtfText) 1296 { 1297 if (level == 1) 1298 { 1299 if (!buffer) 1300 size = read_hex_data( info, &buffer ); 1301 } 1302 else 1303 { 1304 RTFSkipGroup( info ); 1305 } 1306 } /* We potentially have a new token so fall through. */ 1307 1308 if (info->rtfClass == rtfEOF) return; 1309 1310 if (RTFCheckCM( info, rtfGroup, rtfEndGroup )) 1311 { 1312 if (--level == 0) break; 1313 continue; 1314 } 1315 if (RTFCheckCM( info, rtfGroup, rtfBeginGroup )) 1316 { 1317 level++; 1318 continue; 1319 } 1320 if (!RTFCheckCM( info, rtfControl, rtfPictAttr )) 1321 { 1322 RTFRouteToken( info ); 1323 if (RTFCheckCM( info, rtfGroup, rtfEndGroup )) 1324 level--; 1325 continue; 1326 } 1327 1328 if (RTFCheckMM( info, rtfPictAttr, rtfWinMetafile )) 1329 { 1330 mfp.mm = info->rtfParam; 1331 gfx = gfx_metafile; 1332 } 1333 else if (RTFCheckMM( info, rtfPictAttr, rtfDevIndBitmap )) 1334 { 1335 if (info->rtfParam != 0) FIXME("dibitmap should be 0 (%d)\n", info->rtfParam); 1336 gfx = gfx_dib; 1337 } 1338 else if (RTFCheckMM( info, rtfPictAttr, rtfEmfBlip )) 1339 gfx = gfx_enhmetafile; 1340 else if (RTFCheckMM( info, rtfPictAttr, rtfPicWid )) 1341 mfp.xExt = info->rtfParam; 1342 else if (RTFCheckMM( info, rtfPictAttr, rtfPicHt )) 1343 mfp.yExt = info->rtfParam; 1344 else if (RTFCheckMM( info, rtfPictAttr, rtfPicGoalWid )) 1345 sz.cx = info->rtfParam; 1346 else if (RTFCheckMM( info, rtfPictAttr, rtfPicGoalHt )) 1347 sz.cy = info->rtfParam; 1348 else 1349 FIXME("Non supported attribute: %d %d %d\n", info->rtfClass, info->rtfMajor, info->rtfMinor); 1350 } 1351 1352 if (buffer) 1353 { 1354 switch (gfx) 1355 { 1356 case gfx_enhmetafile: 1357 if ((hemf = SetEnhMetaFileBits( size, buffer ))) 1358 insert_static_object( info->editor, hemf, NULL, &sz ); 1359 break; 1360 case gfx_metafile: 1361 if ((hemf = SetWinMetaFileBits( size, buffer, NULL, &mfp ))) 1362 insert_static_object( info->editor, hemf, NULL, &sz ); 1363 break; 1364 case gfx_dib: 1365 { 1366 BITMAPINFO *bi = (BITMAPINFO*)buffer; 1367 HDC hdc = GetDC(0); 1368 unsigned nc = bi->bmiHeader.biClrUsed; 1369 1370 /* not quite right, especially for bitfields type of compression */ 1371 if (!nc && bi->bmiHeader.biBitCount <= 8) 1372 nc = 1 << bi->bmiHeader.biBitCount; 1373 if ((hbmp = CreateDIBitmap( hdc, &bi->bmiHeader, 1374 CBM_INIT, (char*)(bi + 1) + nc * sizeof(RGBQUAD), 1375 bi, DIB_RGB_COLORS)) ) 1376 insert_static_object( info->editor, NULL, hbmp, &sz ); 1377 ReleaseDC( 0, hdc ); 1378 break; 1379 } 1380 default: 1381 break; 1382 } 1383 } 1384 HeapFree( GetProcessHeap(), 0, buffer ); 1385 RTFRouteToken( info ); /* feed "}" back to router */ 1386 return; 1387 } 1388 1389 /* for now, lookup the \result part and use it, whatever the object */ 1390 static void ME_RTFReadObjectGroup(RTF_Info *info) 1391 { 1392 for (;;) 1393 { 1394 RTFGetToken (info); 1395 if (info->rtfClass == rtfEOF) 1396 return; 1397 if (RTFCheckCM(info, rtfGroup, rtfEndGroup)) 1398 break; 1399 if (RTFCheckCM(info, rtfGroup, rtfBeginGroup)) 1400 { 1401 RTFGetToken (info); 1402 if (info->rtfClass == rtfEOF) 1403 return; 1404 if (RTFCheckCMM(info, rtfControl, rtfDestination, rtfObjResult)) 1405 { 1406 int level = 1; 1407 1408 while (RTFGetToken (info) != rtfEOF) 1409 { 1410 if (info->rtfClass == rtfGroup) 1411 { 1412 if (info->rtfMajor == rtfBeginGroup) level++; 1413 else if (info->rtfMajor == rtfEndGroup && --level < 0) break; 1414 } 1415 RTFRouteToken(info); 1416 } 1417 } 1418 else RTFSkipGroup(info); 1419 continue; 1420 } 1421 if (!RTFCheckCM (info, rtfControl, rtfObjAttr)) 1422 { 1423 FIXME("Non supported attribute: %d %d %d\n", info->rtfClass, info->rtfMajor, info->rtfMinor); 1424 return; 1425 } 1426 } 1427 RTFRouteToken(info); /* feed "}" back to router */ 1428 } 1429 1430 static void ME_RTFReadParnumGroup( RTF_Info *info ) 1431 { 1432 int level = 1, type = -1; 1433 WORD indent = 0, start = 1; 1434 WCHAR txt_before = 0, txt_after = 0; 1435 1436 for (;;) 1437 { 1438 RTFGetToken( info ); 1439 1440 if (RTFCheckCMM( info, rtfControl, rtfDestination, rtfParNumTextBefore ) || 1441 RTFCheckCMM( info, rtfControl, rtfDestination, rtfParNumTextAfter )) 1442 { 1443 int loc = info->rtfMinor; 1444 1445 RTFGetToken( info ); 1446 if (info->rtfClass == rtfText) 1447 { 1448 if (loc == rtfParNumTextBefore) 1449 txt_before = info->rtfMajor; 1450 else 1451 txt_after = info->rtfMajor; 1452 continue; 1453 } 1454 /* falling through to catch EOFs and group level changes */ 1455 } 1456 1457 if (info->rtfClass == rtfEOF) 1458 return; 1459 1460 if (RTFCheckCM( info, rtfGroup, rtfEndGroup )) 1461 { 1462 if (--level == 0) break; 1463 continue; 1464 } 1465 1466 if (RTFCheckCM( info, rtfGroup, rtfBeginGroup )) 1467 { 1468 level++; 1469 continue; 1470 } 1471 1472 /* Ignore non para-attr */ 1473 if (!RTFCheckCM( info, rtfControl, rtfParAttr )) 1474 continue; 1475 1476 switch (info->rtfMinor) 1477 { 1478 case rtfParLevel: /* Para level is ignored */ 1479 case rtfParSimple: 1480 break; 1481 case rtfParBullet: 1482 type = PFN_BULLET; 1483 break; 1484 1485 case rtfParNumDecimal: 1486 type = PFN_ARABIC; 1487 break; 1488 case rtfParNumULetter: 1489 type = PFN_UCLETTER; 1490 break; 1491 case rtfParNumURoman: 1492 type = PFN_UCROMAN; 1493 break; 1494 case rtfParNumLLetter: 1495 type = PFN_LCLETTER; 1496 break; 1497 case rtfParNumLRoman: 1498 type = PFN_LCROMAN; 1499 break; 1500 1501 case rtfParNumIndent: 1502 indent = info->rtfParam; 1503 break; 1504 case rtfParNumStartAt: 1505 start = info->rtfParam; 1506 break; 1507 } 1508 } 1509 1510 if (type != -1) 1511 { 1512 info->fmt.dwMask |= (PFM_NUMBERING | PFM_NUMBERINGSTART | PFM_NUMBERINGSTYLE | PFM_NUMBERINGTAB); 1513 info->fmt.wNumbering = type; 1514 info->fmt.wNumberingStart = start; 1515 info->fmt.wNumberingStyle = PFNS_PAREN; 1516 if (type != PFN_BULLET) 1517 { 1518 if (txt_before == 0 && txt_after == 0) 1519 info->fmt.wNumberingStyle = PFNS_PLAIN; 1520 else if (txt_after == '.') 1521 info->fmt.wNumberingStyle = PFNS_PERIOD; 1522 else if (txt_before == '(' && txt_after == ')') 1523 info->fmt.wNumberingStyle = PFNS_PARENS; 1524 } 1525 info->fmt.wNumberingTab = indent; 1526 } 1527 1528 TRACE("type %d indent %d start %d txt before %04x txt after %04x\n", 1529 type, indent, start, txt_before, txt_after); 1530 1531 RTFRouteToken( info ); /* feed "}" back to router */ 1532 } 1533 1534 static void ME_RTFReadHook(RTF_Info *info) 1535 { 1536 switch(info->rtfClass) 1537 { 1538 case rtfGroup: 1539 switch(info->rtfMajor) 1540 { 1541 case rtfBeginGroup: 1542 if (info->stackTop < maxStack) { 1543 info->stack[info->stackTop].style = info->style; 1544 ME_AddRefStyle(info->style); 1545 info->stack[info->stackTop].codePage = info->codePage; 1546 info->stack[info->stackTop].unicodeLength = info->unicodeLength; 1547 } 1548 info->stackTop++; 1549 info->styleChanged = FALSE; 1550 break; 1551 case rtfEndGroup: 1552 { 1553 RTFFlushOutputBuffer(info); 1554 info->stackTop--; 1555 if (info->stackTop <= 0) 1556 info->rtfClass = rtfEOF; 1557 if (info->stackTop < 0) 1558 return; 1559 1560 ME_ReleaseStyle(info->style); 1561 info->style = info->stack[info->stackTop].style; 1562 info->codePage = info->stack[info->stackTop].codePage; 1563 info->unicodeLength = info->stack[info->stackTop].unicodeLength; 1564 break; 1565 } 1566 } 1567 break; 1568 } 1569 } 1570 1571 void 1572 ME_StreamInFill(ME_InStream *stream) 1573 { 1574 stream->editstream->dwError = stream->editstream->pfnCallback(stream->editstream->dwCookie, 1575 (BYTE *)stream->buffer, 1576 sizeof(stream->buffer), 1577 (LONG *)&stream->dwSize); 1578 stream->dwUsed = 0; 1579 } 1580 1581 static LRESULT ME_StreamIn(ME_TextEditor *editor, DWORD format, EDITSTREAM *stream, BOOL stripLastCR) 1582 { 1583 RTF_Info parser; 1584 ME_Style *style; 1585 int from, to, nUndoMode; 1586 int nEventMask = editor->nEventMask; 1587 ME_InStream inStream; 1588 BOOL invalidRTF = FALSE; 1589 ME_Cursor *selStart, *selEnd; 1590 LRESULT num_read = 0; /* bytes read for SF_TEXT, non-control chars inserted for SF_RTF */ 1591 1592 TRACE("stream==%p editor==%p format==0x%X\n", stream, editor, format); 1593 editor->nEventMask = 0; 1594 1595 ME_GetSelectionOfs(editor, &from, &to); 1596 if (format & SFF_SELECTION && editor->mode & TM_RICHTEXT) 1597 { 1598 ME_GetSelection(editor, &selStart, &selEnd); 1599 style = ME_GetSelectionInsertStyle(editor); 1600 1601 ME_InternalDeleteText(editor, selStart, to - from, FALSE); 1602 1603 /* Don't insert text at the end of the table row */ 1604 if (!editor->bEmulateVersion10) { /* v4.1 */ 1605 ME_DisplayItem *para = editor->pCursors->pPara; 1606 if (para->member.para.nFlags & MEPF_ROWEND) 1607 { 1608 para = para->member.para.next_para; 1609 editor->pCursors[0].pPara = para; 1610 editor->pCursors[0].pRun = ME_FindItemFwd(para, diRun); 1611 editor->pCursors[0].nOffset = 0; 1612 } 1613 if (para->member.para.nFlags & MEPF_ROWSTART) 1614 { 1615 para = para->member.para.next_para; 1616 editor->pCursors[0].pPara = para; 1617 editor->pCursors[0].pRun = ME_FindItemFwd(para, diRun); 1618 editor->pCursors[0].nOffset = 0; 1619 } 1620 editor->pCursors[1] = editor->pCursors[0]; 1621 } else { /* v1.0 - 3.0 */ 1622 if (editor->pCursors[0].pRun->member.run.nFlags & MERF_ENDPARA && 1623 ME_IsInTable(editor->pCursors[0].pRun)) 1624 return 0; 1625 } 1626 } else { 1627 style = editor->pBuffer->pDefaultStyle; 1628 ME_AddRefStyle(style); 1629 set_selection_cursors(editor, 0, 0); 1630 ME_InternalDeleteText(editor, &editor->pCursors[1], 1631 ME_GetTextLength(editor), FALSE); 1632 from = to = 0; 1633 ME_ClearTempStyle(editor); 1634 ME_SetDefaultParaFormat(editor, &editor->pCursors[0].pPara->member.para.fmt); 1635 } 1636 1637 1638 /* Back up undo mode to a local variable */ 1639 nUndoMode = editor->nUndoMode; 1640 1641 /* Only create an undo if SFF_SELECTION is set */ 1642 if (!(format & SFF_SELECTION)) 1643 editor->nUndoMode = umIgnore; 1644 1645 inStream.editstream = stream; 1646 inStream.editstream->dwError = 0; 1647 inStream.dwSize = 0; 1648 inStream.dwUsed = 0; 1649 1650 if (format & SF_RTF) 1651 { 1652 /* Check if it's really RTF, and if it is not, use plain text */ 1653 ME_StreamInFill(&inStream); 1654 if (!inStream.editstream->dwError) 1655 { 1656 if ((!editor->bEmulateVersion10 && strncmp(inStream.buffer, "{\\rtf", 5) && strncmp(inStream.buffer, "{\\urtf", 6)) 1657 || (editor->bEmulateVersion10 && *inStream.buffer != '{')) 1658 { 1659 invalidRTF = TRUE; 1660 inStream.editstream->dwError = -16; 1661 } 1662 } 1663 } 1664 1665 if (!invalidRTF && !inStream.editstream->dwError) 1666 { 1667 ME_Cursor start; 1668 from = ME_GetCursorOfs(&editor->pCursors[0]); 1669 if (format & SF_RTF) { 1670 1671 /* setup the RTF parser */ 1672 memset(&parser, 0, sizeof parser); 1673 RTFSetEditStream(&parser, &inStream); 1674 parser.rtfFormat = format&(SF_TEXT|SF_RTF); 1675 parser.editor = editor; 1676 parser.style = style; 1677 WriterInit(&parser); 1678 RTFInit(&parser); 1679 RTFSetReadHook(&parser, ME_RTFReadHook); 1680 RTFSetDestinationCallback(&parser, rtfShpPict, ME_RTFReadShpPictGroup); 1681 RTFSetDestinationCallback(&parser, rtfPict, ME_RTFReadPictGroup); 1682 RTFSetDestinationCallback(&parser, rtfObject, ME_RTFReadObjectGroup); 1683 RTFSetDestinationCallback(&parser, rtfParNumbering, ME_RTFReadParnumGroup); 1684 if (!parser.editor->bEmulateVersion10) /* v4.1 */ 1685 { 1686 RTFSetDestinationCallback(&parser, rtfNoNestTables, RTFSkipGroup); 1687 RTFSetDestinationCallback(&parser, rtfNestTableProps, RTFReadGroup); 1688 } 1689 BeginFile(&parser); 1690 1691 /* do the parsing */ 1692 RTFRead(&parser); 1693 RTFFlushOutputBuffer(&parser); 1694 if (!editor->bEmulateVersion10) { /* v4.1 */ 1695 if (parser.tableDef && parser.tableDef->tableRowStart && 1696 (parser.nestingLevel > 0 || parser.canInheritInTbl)) 1697 { 1698 /* Delete any incomplete table row at the end of the rich text. */ 1699 int nOfs, nChars; 1700 ME_DisplayItem *para; 1701 1702 parser.rtfMinor = rtfRow; 1703 /* Complete the table row before deleting it. 1704 * By doing it this way we will have the current paragraph format set 1705 * properly to reflect that is not in the complete table, and undo items 1706 * will be added for this change to the current paragraph format. */ 1707 if (parser.nestingLevel > 0) 1708 { 1709 while (parser.nestingLevel > 1) 1710 ME_RTFSpecialCharHook(&parser); /* Decrements nestingLevel */ 1711 para = parser.tableDef->tableRowStart; 1712 ME_RTFSpecialCharHook(&parser); 1713 } else { 1714 para = parser.tableDef->tableRowStart; 1715 ME_RTFSpecialCharHook(&parser); 1716 assert(para->member.para.nFlags & MEPF_ROWEND); 1717 para = para->member.para.next_para; 1718 } 1719 1720 editor->pCursors[1].pPara = para; 1721 editor->pCursors[1].pRun = ME_FindItemFwd(para, diRun); 1722 editor->pCursors[1].nOffset = 0; 1723 nOfs = ME_GetCursorOfs(&editor->pCursors[1]); 1724 nChars = ME_GetCursorOfs(&editor->pCursors[0]) - nOfs; 1725 ME_InternalDeleteText(editor, &editor->pCursors[1], nChars, TRUE); 1726 if (parser.tableDef) 1727 parser.tableDef->tableRowStart = NULL; 1728 } 1729 } 1730 ME_CheckTablesForCorruption(editor); 1731 RTFDestroy(&parser); 1732 1733 if (parser.stackTop > 0) 1734 { 1735 while (--parser.stackTop >= 0) 1736 { 1737 ME_ReleaseStyle(parser.style); 1738 parser.style = parser.stack[parser.stackTop].style; 1739 } 1740 if (!inStream.editstream->dwError) 1741 inStream.editstream->dwError = HRESULT_FROM_WIN32(ERROR_HANDLE_EOF); 1742 } 1743 1744 /* Remove last line break, as mandated by tests. This is not affected by 1745 CR/LF counters, since RTF streaming presents only \para tokens, which 1746 are converted according to the standard rules: \r for 2.0, \r\n for 1.0 1747 */ 1748 if (stripLastCR && !(format & SFF_SELECTION)) { 1749 int newto; 1750 ME_GetSelection(editor, &selStart, &selEnd); 1751 newto = ME_GetCursorOfs(selEnd); 1752 if (newto > to + (editor->bEmulateVersion10 ? 1 : 0)) { 1753 WCHAR lastchar[3] = {'\0', '\0'}; 1754 int linebreakSize = editor->bEmulateVersion10 ? 2 : 1; 1755 ME_Cursor linebreakCursor = *selEnd, lastcharCursor = *selEnd; 1756 CHARFORMAT2W cf; 1757 1758 /* Set the final eop to the char fmt of the last char */ 1759 cf.cbSize = sizeof(cf); 1760 cf.dwMask = CFM_ALL2; 1761 ME_MoveCursorChars(editor, &lastcharCursor, -1, FALSE); 1762 ME_GetCharFormat(editor, &lastcharCursor, &linebreakCursor, &cf); 1763 set_selection_cursors(editor, newto, -1); 1764 ME_SetSelectionCharFormat(editor, &cf); 1765 set_selection_cursors(editor, newto, newto); 1766 1767 ME_MoveCursorChars(editor, &linebreakCursor, -linebreakSize, FALSE); 1768 ME_GetTextW(editor, lastchar, 2, &linebreakCursor, linebreakSize, FALSE, FALSE); 1769 if (lastchar[0] == '\r' && (lastchar[1] == '\n' || lastchar[1] == '\0')) { 1770 ME_InternalDeleteText(editor, &linebreakCursor, linebreakSize, FALSE); 1771 } 1772 } 1773 } 1774 to = ME_GetCursorOfs(&editor->pCursors[0]); 1775 num_read = to - from; 1776 1777 style = parser.style; 1778 } 1779 else if (format & SF_TEXT) 1780 { 1781 num_read = ME_StreamInText(editor, format, &inStream, style); 1782 to = ME_GetCursorOfs(&editor->pCursors[0]); 1783 } 1784 else 1785 ERR("EM_STREAMIN without SF_TEXT or SF_RTF\n"); 1786 /* put the cursor at the top */ 1787 if (!(format & SFF_SELECTION)) 1788 set_selection_cursors(editor, 0, 0); 1789 ME_CursorFromCharOfs(editor, from, &start); 1790 ME_UpdateLinkAttribute(editor, &start, to - from); 1791 } 1792 1793 /* Restore saved undo mode */ 1794 editor->nUndoMode = nUndoMode; 1795 1796 /* even if we didn't add an undo, we need to commit anything on the stack */ 1797 ME_CommitUndo(editor); 1798 1799 /* If SFF_SELECTION isn't set, delete any undos from before we started too */ 1800 if (!(format & SFF_SELECTION)) 1801 ME_EmptyUndoStack(editor); 1802 1803 ME_ReleaseStyle(style); 1804 editor->nEventMask = nEventMask; 1805 ME_UpdateRepaint(editor, FALSE); 1806 if (!(format & SFF_SELECTION)) { 1807 ME_ClearTempStyle(editor); 1808 } 1809 update_caret(editor); 1810 ME_SendSelChange(editor); 1811 ME_SendRequestResize(editor, FALSE); 1812 1813 return num_read; 1814 } 1815 1816 1817 typedef struct tagME_RTFStringStreamStruct 1818 { 1819 char *string; 1820 int pos; 1821 int length; 1822 } ME_RTFStringStreamStruct; 1823 1824 static DWORD CALLBACK ME_ReadFromRTFString(DWORD_PTR dwCookie, LPBYTE lpBuff, LONG cb, LONG *pcb) 1825 { 1826 ME_RTFStringStreamStruct *pStruct = (ME_RTFStringStreamStruct *)dwCookie; 1827 int count; 1828 1829 count = min(cb, pStruct->length - pStruct->pos); 1830 memmove(lpBuff, pStruct->string + pStruct->pos, count); 1831 pStruct->pos += count; 1832 *pcb = count; 1833 return 0; 1834 } 1835 1836 static void 1837 ME_StreamInRTFString(ME_TextEditor *editor, BOOL selection, char *string) 1838 { 1839 EDITSTREAM es; 1840 ME_RTFStringStreamStruct data; 1841 1842 data.string = string; 1843 data.length = strlen(string); 1844 data.pos = 0; 1845 es.dwCookie = (DWORD_PTR)&data; 1846 es.pfnCallback = ME_ReadFromRTFString; 1847 ME_StreamIn(editor, SF_RTF | (selection ? SFF_SELECTION : 0), &es, TRUE); 1848 } 1849 1850 1851 static int 1852 ME_FindText(ME_TextEditor *editor, DWORD flags, const CHARRANGE *chrg, const WCHAR *text, CHARRANGE *chrgText) 1853 { 1854 const int nLen = lstrlenW(text); 1855 const int nTextLen = ME_GetTextLength(editor); 1856 int nMin, nMax; 1857 ME_Cursor cursor; 1858 WCHAR wLastChar = ' '; 1859 1860 TRACE("flags==0x%08x, chrg->cpMin==%d, chrg->cpMax==%d text==%s\n", 1861 flags, chrg->cpMin, chrg->cpMax, debugstr_w(text)); 1862 1863 if (flags & ~(FR_DOWN | FR_MATCHCASE | FR_WHOLEWORD)) 1864 FIXME("Flags 0x%08x not implemented\n", 1865 flags & ~(FR_DOWN | FR_MATCHCASE | FR_WHOLEWORD)); 1866 1867 nMin = chrg->cpMin; 1868 if (chrg->cpMax == -1) 1869 nMax = nTextLen; 1870 else 1871 nMax = chrg->cpMax > nTextLen ? nTextLen : chrg->cpMax; 1872 1873 /* In 1.0 emulation, if cpMax reaches end of text, add the FR_DOWN flag */ 1874 if (editor->bEmulateVersion10 && nMax == nTextLen) 1875 { 1876 flags |= FR_DOWN; 1877 } 1878 1879 /* In 1.0 emulation, cpMin must always be no greater than cpMax */ 1880 if (editor->bEmulateVersion10 && nMax < nMin) 1881 { 1882 if (chrgText) 1883 { 1884 chrgText->cpMin = -1; 1885 chrgText->cpMax = -1; 1886 } 1887 return -1; 1888 } 1889 1890 /* when searching up, if cpMin < cpMax, then instead of searching 1891 * on [cpMin,cpMax], we search on [0,cpMin], otherwise, search on 1892 * [cpMax, cpMin]. The exception is when cpMax is -1, in which 1893 * case, it is always bigger than cpMin. 1894 */ 1895 if (!editor->bEmulateVersion10 && !(flags & FR_DOWN)) 1896 { 1897 int nSwap = nMax; 1898 1899 nMax = nMin > nTextLen ? nTextLen : nMin; 1900 if (nMin < nSwap || chrg->cpMax == -1) 1901 nMin = 0; 1902 else 1903 nMin = nSwap; 1904 } 1905 1906 if (!nLen || nMin < 0 || nMax < 0 || nMax < nMin) 1907 { 1908 if (chrgText) 1909 chrgText->cpMin = chrgText->cpMax = -1; 1910 return -1; 1911 } 1912 1913 if (flags & FR_DOWN) /* Forward search */ 1914 { 1915 /* If possible, find the character before where the search starts */ 1916 if ((flags & FR_WHOLEWORD) && nMin) 1917 { 1918 ME_CursorFromCharOfs(editor, nMin - 1, &cursor); 1919 wLastChar = *get_text( &cursor.pRun->member.run, cursor.nOffset ); 1920 ME_MoveCursorChars(editor, &cursor, 1, FALSE); 1921 } else { 1922 ME_CursorFromCharOfs(editor, nMin, &cursor); 1923 } 1924 1925 while (cursor.pRun && ME_GetCursorOfs(&cursor) + nLen <= nMax) 1926 { 1927 ME_DisplayItem *pCurItem = cursor.pRun; 1928 int nCurStart = cursor.nOffset; 1929 int nMatched = 0; 1930 1931 while (pCurItem && ME_CharCompare( *get_text( &pCurItem->member.run, nCurStart + nMatched ), text[nMatched], (flags & FR_MATCHCASE))) 1932 { 1933 if ((flags & FR_WHOLEWORD) && iswalnum(wLastChar)) 1934 break; 1935 1936 nMatched++; 1937 if (nMatched == nLen) 1938 { 1939 ME_DisplayItem *pNextItem = pCurItem; 1940 int nNextStart = nCurStart; 1941 WCHAR wNextChar; 1942 1943 /* Check to see if next character is a whitespace */ 1944 if (flags & FR_WHOLEWORD) 1945 { 1946 if (nCurStart + nMatched == pCurItem->member.run.len) 1947 { 1948 pNextItem = ME_FindItemFwd(pCurItem, diRun); 1949 nNextStart = -nMatched; 1950 } 1951 1952 if (pNextItem) 1953 wNextChar = *get_text( &pNextItem->member.run, nNextStart + nMatched ); 1954 else 1955 wNextChar = ' '; 1956 1957 if (iswalnum(wNextChar)) 1958 break; 1959 } 1960 1961 cursor.nOffset += cursor.pPara->member.para.nCharOfs + cursor.pRun->member.run.nCharOfs; 1962 if (chrgText) 1963 { 1964 chrgText->cpMin = cursor.nOffset; 1965 chrgText->cpMax = cursor.nOffset + nLen; 1966 } 1967 TRACE("found at %d-%d\n", cursor.nOffset, cursor.nOffset + nLen); 1968 return cursor.nOffset; 1969 } 1970 if (nCurStart + nMatched == pCurItem->member.run.len) 1971 { 1972 pCurItem = ME_FindItemFwd(pCurItem, diRun); 1973 nCurStart = -nMatched; 1974 } 1975 } 1976 if (pCurItem) 1977 wLastChar = *get_text( &pCurItem->member.run, nCurStart + nMatched ); 1978 else 1979 wLastChar = ' '; 1980 1981 cursor.nOffset++; 1982 if (cursor.nOffset == cursor.pRun->member.run.len) 1983 { 1984 ME_NextRun(&cursor.pPara, &cursor.pRun, TRUE); 1985 cursor.nOffset = 0; 1986 } 1987 } 1988 } 1989 else /* Backward search */ 1990 { 1991 /* If possible, find the character after where the search ends */ 1992 if ((flags & FR_WHOLEWORD) && nMax < nTextLen - 1) 1993 { 1994 ME_CursorFromCharOfs(editor, nMax + 1, &cursor); 1995 wLastChar = *get_text( &cursor.pRun->member.run, cursor.nOffset ); 1996 ME_MoveCursorChars(editor, &cursor, -1, FALSE); 1997 } else { 1998 ME_CursorFromCharOfs(editor, nMax, &cursor); 1999 } 2000 2001 while (cursor.pRun && ME_GetCursorOfs(&cursor) - nLen >= nMin) 2002 { 2003 ME_DisplayItem *pCurItem = cursor.pRun; 2004 ME_DisplayItem *pCurPara = cursor.pPara; 2005 int nCurEnd = cursor.nOffset; 2006 int nMatched = 0; 2007 2008 if (nCurEnd == 0) 2009 { 2010 ME_PrevRun(&pCurPara, &pCurItem, TRUE); 2011 nCurEnd = pCurItem->member.run.len; 2012 } 2013 2014 while (pCurItem && ME_CharCompare( *get_text( &pCurItem->member.run, nCurEnd - nMatched - 1 ), 2015 text[nLen - nMatched - 1], (flags & FR_MATCHCASE) )) 2016 { 2017 if ((flags & FR_WHOLEWORD) && iswalnum(wLastChar)) 2018 break; 2019 2020 nMatched++; 2021 if (nMatched == nLen) 2022 { 2023 ME_DisplayItem *pPrevItem = pCurItem; 2024 int nPrevEnd = nCurEnd; 2025 WCHAR wPrevChar; 2026 int nStart; 2027 2028 /* Check to see if previous character is a whitespace */ 2029 if (flags & FR_WHOLEWORD) 2030 { 2031 if (nPrevEnd - nMatched == 0) 2032 { 2033 pPrevItem = ME_FindItemBack(pCurItem, diRun); 2034 if (pPrevItem) 2035 nPrevEnd = pPrevItem->member.run.len + nMatched; 2036 } 2037 2038 if (pPrevItem) 2039 wPrevChar = *get_text( &pPrevItem->member.run, nPrevEnd - nMatched - 1 ); 2040 else 2041 wPrevChar = ' '; 2042 2043 if (iswalnum(wPrevChar)) 2044 break; 2045 } 2046 2047 nStart = pCurPara->member.para.nCharOfs 2048 + pCurItem->member.run.nCharOfs + nCurEnd - nMatched; 2049 if (chrgText) 2050 { 2051 chrgText->cpMin = nStart; 2052 chrgText->cpMax = nStart + nLen; 2053 } 2054 TRACE("found at %d-%d\n", nStart, nStart + nLen); 2055 return nStart; 2056 } 2057 if (nCurEnd - nMatched == 0) 2058 { 2059 ME_PrevRun(&pCurPara, &pCurItem, TRUE); 2060 /* Don't care about pCurItem becoming NULL here; it's already taken 2061 * care of in the exterior loop condition */ 2062 nCurEnd = pCurItem->member.run.len + nMatched; 2063 } 2064 } 2065 if (pCurItem) 2066 wLastChar = *get_text( &pCurItem->member.run, nCurEnd - nMatched - 1 ); 2067 else 2068 wLastChar = ' '; 2069 2070 cursor.nOffset--; 2071 if (cursor.nOffset < 0) 2072 { 2073 ME_PrevRun(&cursor.pPara, &cursor.pRun, TRUE); 2074 cursor.nOffset = cursor.pRun->member.run.len; 2075 } 2076 } 2077 } 2078 TRACE("not found\n"); 2079 if (chrgText) 2080 chrgText->cpMin = chrgText->cpMax = -1; 2081 return -1; 2082 } 2083 2084 static int ME_GetTextEx(ME_TextEditor *editor, GETTEXTEX *ex, LPARAM pText) 2085 { 2086 int nChars; 2087 ME_Cursor start; 2088 2089 if (!ex->cb || !pText) return 0; 2090 2091 if (ex->flags & ~(GT_SELECTION | GT_USECRLF)) 2092 FIXME("GETTEXTEX flags 0x%08x not supported\n", ex->flags & ~(GT_SELECTION | GT_USECRLF)); 2093 2094 if (ex->flags & GT_SELECTION) 2095 { 2096 int from, to; 2097 int nStartCur = ME_GetSelectionOfs(editor, &from, &to); 2098 start = editor->pCursors[nStartCur]; 2099 nChars = to - from; 2100 } 2101 else 2102 { 2103 ME_SetCursorToStart(editor, &start); 2104 nChars = INT_MAX; 2105 } 2106 if (ex->codepage == CP_UNICODE) 2107 { 2108 return ME_GetTextW(editor, (LPWSTR)pText, ex->cb / sizeof(WCHAR) - 1, 2109 &start, nChars, ex->flags & GT_USECRLF, FALSE); 2110 } 2111 else 2112 { 2113 /* potentially each char may be a CR, why calculate the exact value with O(N) when 2114 we can just take a bigger buffer? :) 2115 The above assumption still holds with CR/LF counters, since CR->CRLF expansion 2116 occurs only in richedit 2.0 mode, in which line breaks have only one CR 2117 */ 2118 int crlfmul = (ex->flags & GT_USECRLF) ? 2 : 1; 2119 DWORD buflen; 2120 LPWSTR buffer; 2121 LRESULT rc; 2122 2123 buflen = min(crlfmul * nChars, ex->cb - 1); 2124 buffer = heap_alloc((buflen + 1) * sizeof(WCHAR)); 2125 2126 nChars = ME_GetTextW(editor, buffer, buflen, &start, nChars, ex->flags & GT_USECRLF, FALSE); 2127 rc = WideCharToMultiByte(ex->codepage, 0, buffer, nChars + 1, 2128 (LPSTR)pText, ex->cb, ex->lpDefaultChar, ex->lpUsedDefChar); 2129 if (rc) rc--; /* do not count 0 terminator */ 2130 2131 heap_free(buffer); 2132 return rc; 2133 } 2134 } 2135 2136 static int ME_GetTextRange(ME_TextEditor *editor, WCHAR *strText, 2137 const ME_Cursor *start, int nLen, BOOL unicode) 2138 { 2139 if (!strText) return 0; 2140 if (unicode) { 2141 return ME_GetTextW(editor, strText, INT_MAX, start, nLen, FALSE, FALSE); 2142 } else { 2143 int nChars; 2144 WCHAR *p = heap_alloc((nLen+1) * sizeof(*p)); 2145 if (!p) return 0; 2146 nChars = ME_GetTextW(editor, p, nLen, start, nLen, FALSE, FALSE); 2147 WideCharToMultiByte(CP_ACP, 0, p, nChars+1, (char *)strText, 2148 nLen+1, NULL, NULL); 2149 heap_free(p); 2150 return nChars; 2151 } 2152 } 2153 2154 int set_selection( ME_TextEditor *editor, int to, int from ) 2155 { 2156 int end; 2157 2158 TRACE("%d - %d\n", to, from ); 2159 2160 if (!editor->bHideSelection) ME_InvalidateSelection( editor ); 2161 end = set_selection_cursors( editor, to, from ); 2162 if (!editor->bHideSelection) ME_InvalidateSelection( editor ); 2163 update_caret( 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 set_selection( 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 ed->caret_hidden = FALSE; 3123 ed->caret_height = 0; 3124 for (i=0; i<HFONT_CACHE_SIZE; i++) 3125 { 3126 ed->pFontCache[i].nRefs = 0; 3127 ed->pFontCache[i].nAge = 0; 3128 ed->pFontCache[i].hFont = NULL; 3129 } 3130 3131 ME_CheckCharOffsets(ed); 3132 SetRectEmpty(&ed->rcFormat); 3133 ed->bDefaultFormatRect = TRUE; 3134 ITextHost_TxGetSelectionBarWidth(ed->texthost, &selbarwidth); 3135 if (selbarwidth) { 3136 /* FIXME: Convert selbarwidth from HIMETRIC to pixels */ 3137 ed->selofs = SELECTIONBAR_WIDTH; 3138 ed->styleFlags |= ES_SELECTIONBAR; 3139 } else { 3140 ed->selofs = 0; 3141 } 3142 ed->nSelectionType = stPosition; 3143 3144 ed->cPasswordMask = 0; 3145 if (props & TXTBIT_USEPASSWORD) 3146 ITextHost_TxGetPasswordChar(texthost, &ed->cPasswordMask); 3147 3148 if (props & TXTBIT_AUTOWORDSEL) 3149 ed->styleFlags |= ECO_AUTOWORDSELECTION; 3150 if (props & TXTBIT_MULTILINE) { 3151 ed->styleFlags |= ES_MULTILINE; 3152 ed->bWordWrap = (props & TXTBIT_WORDWRAP) != 0; 3153 } else { 3154 ed->bWordWrap = FALSE; 3155 } 3156 if (props & TXTBIT_READONLY) 3157 ed->styleFlags |= ES_READONLY; 3158 if (!(props & TXTBIT_HIDESELECTION)) 3159 ed->styleFlags |= ES_NOHIDESEL; 3160 if (props & TXTBIT_SAVESELECTION) 3161 ed->styleFlags |= ES_SAVESEL; 3162 if (props & TXTBIT_VERTICAL) 3163 ed->styleFlags |= ES_VERTICAL; 3164 if (props & TXTBIT_DISABLEDRAG) 3165 ed->styleFlags |= ES_NOOLEDRAGDROP; 3166 3167 ed->notified_cr.cpMin = ed->notified_cr.cpMax = 0; 3168 3169 /* Default scrollbar information */ 3170 ed->vert_si.cbSize = sizeof(SCROLLINFO); 3171 ed->vert_si.nMin = 0; 3172 ed->vert_si.nMax = 0; 3173 ed->vert_si.nPage = 0; 3174 ed->vert_si.nPos = 0; 3175 3176 ed->horz_si.cbSize = sizeof(SCROLLINFO); 3177 ed->horz_si.nMin = 0; 3178 ed->horz_si.nMax = 0; 3179 ed->horz_si.nPage = 0; 3180 ed->horz_si.nPos = 0; 3181 3182 ed->wheel_remain = 0; 3183 3184 list_init( &ed->reobj_list ); 3185 OleInitialize(NULL); 3186 3187 return ed; 3188 } 3189 3190 void ME_DestroyEditor(ME_TextEditor *editor) 3191 { 3192 ME_DisplayItem *p = editor->pBuffer->pFirst, *pNext = NULL; 3193 ME_Style *s, *cursor2; 3194 int i; 3195 3196 ME_ClearTempStyle(editor); 3197 ME_EmptyUndoStack(editor); 3198 editor->pBuffer->pFirst = NULL; 3199 while(p) { 3200 pNext = p->next; 3201 if (p->type == diParagraph) 3202 destroy_para(editor, p); 3203 else 3204 ME_DestroyDisplayItem(p); 3205 p = pNext; 3206 } 3207 3208 LIST_FOR_EACH_ENTRY_SAFE( s, cursor2, &editor->style_list, ME_Style, entry ) 3209 ME_DestroyStyle( s ); 3210 3211 ME_ReleaseStyle(editor->pBuffer->pDefaultStyle); 3212 for (i=0; i<HFONT_CACHE_SIZE; i++) 3213 { 3214 if (editor->pFontCache[i].hFont) 3215 DeleteObject(editor->pFontCache[i].hFont); 3216 } 3217 if (editor->rgbBackColor != -1) 3218 DeleteObject(editor->hbrBackground); 3219 if(editor->lpOleCallback) 3220 IRichEditOleCallback_Release(editor->lpOleCallback); 3221 ITextHost_Release(editor->texthost); 3222 if (editor->reOle) 3223 { 3224 IUnknown_Release(editor->reOle); 3225 editor->reOle = NULL; 3226 } 3227 OleUninitialize(); 3228 3229 heap_free(editor->pBuffer); 3230 heap_free(editor->pCursors); 3231 heap_free(editor); 3232 } 3233 3234 BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) 3235 { 3236 TRACE("\n"); 3237 switch (fdwReason) 3238 { 3239 case DLL_PROCESS_ATTACH: 3240 DisableThreadLibraryCalls(hinstDLL); 3241 me_heap = HeapCreate (0, 0x10000, 0); 3242 if (!ME_RegisterEditorClass(hinstDLL)) return FALSE; 3243 hLeft = LoadCursorW(hinstDLL, MAKEINTRESOURCEW(OCR_REVERSE)); 3244 LookupInit(); 3245 break; 3246 3247 case DLL_PROCESS_DETACH: 3248 if (lpvReserved) break; 3249 UnregisterClassW(RICHEDIT_CLASS20W, 0); 3250 UnregisterClassW(MSFTEDIT_CLASS, 0); 3251 UnregisterClassA(RICHEDIT_CLASS20A, 0); 3252 UnregisterClassA("RichEdit50A", 0); 3253 if (ME_ListBoxRegistered) 3254 UnregisterClassW(REListBox20W, 0); 3255 if (ME_ComboBoxRegistered) 3256 UnregisterClassW(REComboBox20W, 0); 3257 LookupCleanup(); 3258 HeapDestroy (me_heap); 3259 release_typelib(); 3260 break; 3261 } 3262 return TRUE; 3263 } 3264 3265 static inline int get_default_line_height( ME_TextEditor *editor ) 3266 { 3267 int height = 0; 3268 3269 if (editor->pBuffer && editor->pBuffer->pDefaultStyle) 3270 height = editor->pBuffer->pDefaultStyle->tm.tmHeight; 3271 if (height <= 0) height = 24; 3272 3273 return height; 3274 } 3275 3276 static inline int calc_wheel_change( int *remain, int amount_per_click ) 3277 { 3278 int change = amount_per_click * (float)*remain / WHEEL_DELTA; 3279 *remain -= WHEEL_DELTA * change / amount_per_click; 3280 return change; 3281 } 3282 3283 static const char * const edit_messages[] = { 3284 "EM_GETSEL", 3285 "EM_SETSEL", 3286 "EM_GETRECT", 3287 "EM_SETRECT", 3288 "EM_SETRECTNP", 3289 "EM_SCROLL", 3290 "EM_LINESCROLL", 3291 "EM_SCROLLCARET", 3292 "EM_GETMODIFY", 3293 "EM_SETMODIFY", 3294 "EM_GETLINECOUNT", 3295 "EM_LINEINDEX", 3296 "EM_SETHANDLE", 3297 "EM_GETHANDLE", 3298 "EM_GETTHUMB", 3299 "EM_UNKNOWN_BF", 3300 "EM_UNKNOWN_C0", 3301 "EM_LINELENGTH", 3302 "EM_REPLACESEL", 3303 "EM_UNKNOWN_C3", 3304 "EM_GETLINE", 3305 "EM_LIMITTEXT", 3306 "EM_CANUNDO", 3307 "EM_UNDO", 3308 "EM_FMTLINES", 3309 "EM_LINEFROMCHAR", 3310 "EM_UNKNOWN_CA", 3311 "EM_SETTABSTOPS", 3312 "EM_SETPASSWORDCHAR", 3313 "EM_EMPTYUNDOBUFFER", 3314 "EM_GETFIRSTVISIBLELINE", 3315 "EM_SETREADONLY", 3316 "EM_SETWORDBREAKPROC", 3317 "EM_GETWORDBREAKPROC", 3318 "EM_GETPASSWORDCHAR", 3319 "EM_SETMARGINS", 3320 "EM_GETMARGINS", 3321 "EM_GETLIMITTEXT", 3322 "EM_POSFROMCHAR", 3323 "EM_CHARFROMPOS", 3324 "EM_SETIMESTATUS", 3325 "EM_GETIMESTATUS" 3326 }; 3327 3328 static const char * const richedit_messages[] = { 3329 "EM_CANPASTE", 3330 "EM_DISPLAYBAND", 3331 "EM_EXGETSEL", 3332 "EM_EXLIMITTEXT", 3333 "EM_EXLINEFROMCHAR", 3334 "EM_EXSETSEL", 3335 "EM_FINDTEXT", 3336 "EM_FORMATRANGE", 3337 "EM_GETCHARFORMAT", 3338 "EM_GETEVENTMASK", 3339 "EM_GETOLEINTERFACE", 3340 "EM_GETPARAFORMAT", 3341 "EM_GETSELTEXT", 3342 "EM_HIDESELECTION", 3343 "EM_PASTESPECIAL", 3344 "EM_REQUESTRESIZE", 3345 "EM_SELECTIONTYPE", 3346 "EM_SETBKGNDCOLOR", 3347 "EM_SETCHARFORMAT", 3348 "EM_SETEVENTMASK", 3349 "EM_SETOLECALLBACK", 3350 "EM_SETPARAFORMAT", 3351 "EM_SETTARGETDEVICE", 3352 "EM_STREAMIN", 3353 "EM_STREAMOUT", 3354 "EM_GETTEXTRANGE", 3355 "EM_FINDWORDBREAK", 3356 "EM_SETOPTIONS", 3357 "EM_GETOPTIONS", 3358 "EM_FINDTEXTEX", 3359 "EM_GETWORDBREAKPROCEX", 3360 "EM_SETWORDBREAKPROCEX", 3361 "EM_SETUNDOLIMIT", 3362 "EM_UNKNOWN_USER_83", 3363 "EM_REDO", 3364 "EM_CANREDO", 3365 "EM_GETUNDONAME", 3366 "EM_GETREDONAME", 3367 "EM_STOPGROUPTYPING", 3368 "EM_SETTEXTMODE", 3369 "EM_GETTEXTMODE", 3370 "EM_AUTOURLDETECT", 3371 "EM_GETAUTOURLDETECT", 3372 "EM_SETPALETTE", 3373 "EM_GETTEXTEX", 3374 "EM_GETTEXTLENGTHEX", 3375 "EM_SHOWSCROLLBAR", 3376 "EM_SETTEXTEX", 3377 "EM_UNKNOWN_USER_98", 3378 "EM_UNKNOWN_USER_99", 3379 "EM_SETPUNCTUATION", 3380 "EM_GETPUNCTUATION", 3381 "EM_SETWORDWRAPMODE", 3382 "EM_GETWORDWRAPMODE", 3383 "EM_SETIMECOLOR", 3384 "EM_GETIMECOLOR", 3385 "EM_SETIMEOPTIONS", 3386 "EM_GETIMEOPTIONS", 3387 "EM_CONVPOSITION", 3388 "EM_UNKNOWN_USER_109", 3389 "EM_UNKNOWN_USER_110", 3390 "EM_UNKNOWN_USER_111", 3391 "EM_UNKNOWN_USER_112", 3392 "EM_UNKNOWN_USER_113", 3393 "EM_UNKNOWN_USER_114", 3394 "EM_UNKNOWN_USER_115", 3395 "EM_UNKNOWN_USER_116", 3396 "EM_UNKNOWN_USER_117", 3397 "EM_UNKNOWN_USER_118", 3398 "EM_UNKNOWN_USER_119", 3399 "EM_SETLANGOPTIONS", 3400 "EM_GETLANGOPTIONS", 3401 "EM_GETIMECOMPMODE", 3402 "EM_FINDTEXTW", 3403 "EM_FINDTEXTEXW", 3404 "EM_RECONVERSION", 3405 "EM_SETIMEMODEBIAS", 3406 "EM_GETIMEMODEBIAS" 3407 }; 3408 3409 static const char * 3410 get_msg_name(UINT msg) 3411 { 3412 if (msg >= EM_GETSEL && msg <= EM_CHARFROMPOS) 3413 return edit_messages[msg - EM_GETSEL]; 3414 if (msg >= EM_CANPASTE && msg <= EM_GETIMEMODEBIAS) 3415 return richedit_messages[msg - EM_CANPASTE]; 3416 return ""; 3417 } 3418 3419 static void ME_LinkNotify(ME_TextEditor *editor, UINT msg, WPARAM wParam, LPARAM lParam) 3420 { 3421 int x,y; 3422 BOOL isExact; 3423 ME_Cursor cursor; /* The start of the clicked text. */ 3424 3425 ENLINK info; 3426 x = (short)LOWORD(lParam); 3427 y = (short)HIWORD(lParam); 3428 ME_CharFromPos(editor, x, y, &cursor, &isExact); 3429 if (!isExact) return; 3430 3431 if (is_link( &cursor.pRun->member.run )) 3432 { /* The clicked run has CFE_LINK set */ 3433 ME_DisplayItem *di; 3434 3435 info.nmhdr.hwndFrom = NULL; 3436 info.nmhdr.idFrom = 0; 3437 info.nmhdr.code = EN_LINK; 3438 info.msg = msg; 3439 info.wParam = wParam; 3440 info.lParam = lParam; 3441 cursor.nOffset = 0; 3442 3443 /* find the first contiguous run with CFE_LINK set */ 3444 info.chrg.cpMin = ME_GetCursorOfs(&cursor); 3445 di = cursor.pRun; 3446 while (ME_PrevRun( NULL, &di, FALSE ) && is_link( &di->member.run )) 3447 info.chrg.cpMin -= di->member.run.len; 3448 3449 /* find the last contiguous run with CFE_LINK set */ 3450 info.chrg.cpMax = ME_GetCursorOfs(&cursor) + cursor.pRun->member.run.len; 3451 di = cursor.pRun; 3452 while (ME_NextRun( NULL, &di, FALSE ) && is_link( &di->member.run )) 3453 info.chrg.cpMax += di->member.run.len; 3454 3455 ITextHost_TxNotify(editor->texthost, info.nmhdr.code, &info); 3456 } 3457 } 3458 3459 void ME_ReplaceSel(ME_TextEditor *editor, BOOL can_undo, const WCHAR *str, int len) 3460 { 3461 int from, to, nStartCursor; 3462 ME_Style *style; 3463 3464 nStartCursor = ME_GetSelectionOfs(editor, &from, &to); 3465 style = ME_GetSelectionInsertStyle(editor); 3466 ME_InternalDeleteText(editor, &editor->pCursors[nStartCursor], to-from, FALSE); 3467 ME_InsertTextFromCursor(editor, 0, str, len, style); 3468 ME_ReleaseStyle(style); 3469 /* drop temporary style if line end */ 3470 /* 3471 * FIXME question: does abc\n mean: put abc, 3472 * clear temp style, put \n? (would require a change) 3473 */ 3474 if (len>0 && str[len-1] == '\n') 3475 ME_ClearTempStyle(editor); 3476 ME_CommitUndo(editor); 3477 ME_UpdateSelectionLinkAttribute(editor); 3478 if (!can_undo) 3479 ME_EmptyUndoStack(editor); 3480 ME_UpdateRepaint(editor, FALSE); 3481 } 3482 3483 static void ME_SetText(ME_TextEditor *editor, void *text, BOOL unicode) 3484 { 3485 LONG codepage = unicode ? CP_UNICODE : CP_ACP; 3486 int textLen; 3487 3488 LPWSTR wszText = ME_ToUnicode(codepage, text, &textLen); 3489 ME_InsertTextFromCursor(editor, 0, wszText, textLen, editor->pBuffer->pDefaultStyle); 3490 ME_EndToUnicode(codepage, wszText); 3491 } 3492 3493 static LRESULT ME_WmCreate(ME_TextEditor *editor, LPARAM lParam, BOOL unicode) 3494 { 3495 CREATESTRUCTW *createW = (CREATESTRUCTW*)lParam; 3496 CREATESTRUCTA *createA = (CREATESTRUCTA*)lParam; 3497 void *text = NULL; 3498 INT max; 3499 3500 if (lParam) 3501 text = unicode ? (void*)createW->lpszName : (void*)createA->lpszName; 3502 3503 ME_SetDefaultFormatRect(editor); 3504 3505 max = (editor->styleFlags & ES_DISABLENOSCROLL) ? 1 : 0; 3506 if (~editor->styleFlags & ES_DISABLENOSCROLL || editor->styleFlags & WS_VSCROLL) 3507 ITextHost_TxSetScrollRange(editor->texthost, SB_VERT, 0, max, TRUE); 3508 3509 if (~editor->styleFlags & ES_DISABLENOSCROLL || editor->styleFlags & WS_HSCROLL) 3510 ITextHost_TxSetScrollRange(editor->texthost, SB_HORZ, 0, max, TRUE); 3511 3512 if (editor->styleFlags & ES_DISABLENOSCROLL) 3513 { 3514 if (editor->styleFlags & WS_VSCROLL) 3515 { 3516 ITextHost_TxEnableScrollBar(editor->texthost, SB_VERT, ESB_DISABLE_BOTH); 3517 ITextHost_TxShowScrollBar(editor->texthost, SB_VERT, TRUE); 3518 } 3519 if (editor->styleFlags & WS_HSCROLL) 3520 { 3521 ITextHost_TxEnableScrollBar(editor->texthost, SB_HORZ, ESB_DISABLE_BOTH); 3522 ITextHost_TxShowScrollBar(editor->texthost, SB_HORZ, TRUE); 3523 } 3524 } 3525 3526 if (text) 3527 { 3528 ME_SetText(editor, text, unicode); 3529 ME_SetCursorToStart(editor, &editor->pCursors[0]); 3530 ME_SetCursorToStart(editor, &editor->pCursors[1]); 3531 } 3532 3533 ME_CommitUndo(editor); 3534 ME_WrapMarkedParagraphs(editor); 3535 update_caret(editor); 3536 return 0; 3537 } 3538 3539 static LRESULT handle_EM_SETCHARFORMAT( ME_TextEditor *editor, WPARAM flags, const CHARFORMAT2W *fmt_in ) 3540 { 3541 CHARFORMAT2W fmt; 3542 BOOL changed = TRUE; 3543 ME_Cursor start, end; 3544 3545 if (!cfany_to_cf2w( &fmt, fmt_in )) return 0; 3546 3547 if (flags & SCF_ALL) 3548 { 3549 if (editor->mode & TM_PLAINTEXT) 3550 { 3551 ME_SetDefaultCharFormat( editor, &fmt ); 3552 } 3553 else 3554 { 3555 ME_SetCursorToStart( editor, &start ); 3556 ME_SetCharFormat( editor, &start, NULL, &fmt ); 3557 editor->nModifyStep = 1; 3558 } 3559 } 3560 else if (flags & SCF_SELECTION) 3561 { 3562 if (editor->mode & TM_PLAINTEXT) return 0; 3563 if (flags & SCF_WORD) 3564 { 3565 end = editor->pCursors[0]; 3566 ME_MoveCursorWords( editor, &end, +1 ); 3567 start = end; 3568 ME_MoveCursorWords( editor, &start, -1 ); 3569 ME_SetCharFormat( editor, &start, &end, &fmt ); 3570 } 3571 changed = ME_IsSelection( editor ); 3572 ME_SetSelectionCharFormat( editor, &fmt ); 3573 if (changed) editor->nModifyStep = 1; 3574 } 3575 else /* SCF_DEFAULT */ 3576 { 3577 ME_SetDefaultCharFormat( editor, &fmt ); 3578 } 3579 3580 ME_CommitUndo( editor ); 3581 if (changed) 3582 { 3583 ME_WrapMarkedParagraphs( editor ); 3584 ME_UpdateScrollBar( editor ); 3585 } 3586 return 1; 3587 } 3588 3589 #define UNSUPPORTED_MSG(e) \ 3590 case e: \ 3591 FIXME(#e ": stub\n"); \ 3592 *phresult = S_FALSE; \ 3593 return 0; 3594 3595 /* Handle messages for windowless and windowed richedit controls. 3596 * 3597 * The LRESULT that is returned is a return value for window procs, 3598 * and the phresult parameter is the COM return code needed by the 3599 * text services interface. */ 3600 LRESULT ME_HandleMessage(ME_TextEditor *editor, UINT msg, WPARAM wParam, 3601 LPARAM lParam, BOOL unicode, HRESULT* phresult) 3602 { 3603 *phresult = S_OK; 3604 3605 switch(msg) { 3606 3607 UNSUPPORTED_MSG(EM_DISPLAYBAND) 3608 UNSUPPORTED_MSG(EM_FINDWORDBREAK) 3609 UNSUPPORTED_MSG(EM_FMTLINES) 3610 UNSUPPORTED_MSG(EM_FORMATRANGE) 3611 UNSUPPORTED_MSG(EM_GETBIDIOPTIONS) 3612 UNSUPPORTED_MSG(EM_GETEDITSTYLE) 3613 UNSUPPORTED_MSG(EM_GETIMECOMPMODE) 3614 UNSUPPORTED_MSG(EM_GETIMESTATUS) 3615 UNSUPPORTED_MSG(EM_SETIMESTATUS) 3616 UNSUPPORTED_MSG(EM_GETLANGOPTIONS) 3617 UNSUPPORTED_MSG(EM_GETREDONAME) 3618 UNSUPPORTED_MSG(EM_GETTYPOGRAPHYOPTIONS) 3619 UNSUPPORTED_MSG(EM_GETUNDONAME) 3620 UNSUPPORTED_MSG(EM_GETWORDBREAKPROCEX) 3621 UNSUPPORTED_MSG(EM_SETBIDIOPTIONS) 3622 UNSUPPORTED_MSG(EM_SETEDITSTYLE) 3623 UNSUPPORTED_MSG(EM_SETLANGOPTIONS) 3624 UNSUPPORTED_MSG(EM_SETMARGINS) 3625 UNSUPPORTED_MSG(EM_SETPALETTE) 3626 UNSUPPORTED_MSG(EM_SETTABSTOPS) 3627 UNSUPPORTED_MSG(EM_SETTYPOGRAPHYOPTIONS) 3628 UNSUPPORTED_MSG(EM_SETWORDBREAKPROCEX) 3629 3630 /* Messages specific to Richedit controls */ 3631 3632 case EM_STREAMIN: 3633 return ME_StreamIn(editor, wParam, (EDITSTREAM*)lParam, TRUE); 3634 case EM_STREAMOUT: 3635 return ME_StreamOut(editor, wParam, (EDITSTREAM *)lParam); 3636 case WM_GETDLGCODE: 3637 { 3638 UINT code = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS; 3639 3640 if (lParam) 3641 editor->bDialogMode = TRUE; 3642 if (editor->styleFlags & ES_MULTILINE) 3643 code |= DLGC_WANTMESSAGE; 3644 if (!(editor->styleFlags & ES_SAVESEL)) 3645 code |= DLGC_HASSETSEL; 3646 return code; 3647 } 3648 case EM_EMPTYUNDOBUFFER: 3649 ME_EmptyUndoStack(editor); 3650 return 0; 3651 case EM_GETSEL: 3652 { 3653 /* Note: wParam/lParam can be NULL */ 3654 UINT from, to; 3655 PUINT pfrom = wParam ? (PUINT)wParam : &from; 3656 PUINT pto = lParam ? (PUINT)lParam : &to; 3657 ME_GetSelectionOfs(editor, (int *)pfrom, (int *)pto); 3658 if ((*pfrom|*pto) & 0xFFFF0000) 3659 return -1; 3660 return MAKELONG(*pfrom,*pto); 3661 } 3662 case EM_EXGETSEL: 3663 { 3664 CHARRANGE *pRange = (CHARRANGE *)lParam; 3665 ME_GetSelectionOfs(editor, &pRange->cpMin, &pRange->cpMax); 3666 TRACE("EM_EXGETSEL = (%d,%d)\n", pRange->cpMin, pRange->cpMax); 3667 return 0; 3668 } 3669 case EM_SETUNDOLIMIT: 3670 { 3671 if ((int)wParam < 0) 3672 editor->nUndoLimit = STACK_SIZE_DEFAULT; 3673 else 3674 editor->nUndoLimit = min(wParam, STACK_SIZE_MAX); 3675 /* Setting a max stack size keeps wine from getting killed 3676 for hogging memory. Windows allocates all this memory at once, so 3677 no program would realistically set a value above our maximum. */ 3678 return editor->nUndoLimit; 3679 } 3680 case EM_CANUNDO: 3681 return !list_empty( &editor->undo_stack ); 3682 case EM_CANREDO: 3683 return !list_empty( &editor->redo_stack ); 3684 case WM_UNDO: /* FIXME: actually not the same */ 3685 case EM_UNDO: 3686 return ME_Undo(editor); 3687 case EM_REDO: 3688 return ME_Redo(editor); 3689 case EM_GETOPTIONS: 3690 { 3691 /* these flags are equivalent to the ES_* counterparts */ 3692 DWORD mask = ECO_VERTICAL | ECO_AUTOHSCROLL | ECO_AUTOVSCROLL | 3693 ECO_NOHIDESEL | ECO_READONLY | ECO_WANTRETURN | ECO_SELECTIONBAR; 3694 DWORD settings = editor->styleFlags & mask; 3695 3696 return settings; 3697 } 3698 case EM_SETFONTSIZE: 3699 { 3700 CHARFORMAT2W cf; 3701 LONG tmp_size, size; 3702 BOOL is_increase = ((LONG)wParam > 0); 3703 3704 if (editor->mode & TM_PLAINTEXT) 3705 return FALSE; 3706 3707 cf.cbSize = sizeof(cf); 3708 cf.dwMask = CFM_SIZE; 3709 ME_GetSelectionCharFormat(editor, &cf); 3710 tmp_size = (cf.yHeight / 20) + wParam; 3711 3712 if (tmp_size <= 1) 3713 size = 1; 3714 else if (tmp_size > 12 && tmp_size < 28 && tmp_size % 2) 3715 size = tmp_size + (is_increase ? 1 : -1); 3716 else if (tmp_size > 28 && tmp_size < 36) 3717 size = is_increase ? 36 : 28; 3718 else if (tmp_size > 36 && tmp_size < 48) 3719 size = is_increase ? 48 : 36; 3720 else if (tmp_size > 48 && tmp_size < 72) 3721 size = is_increase ? 72 : 48; 3722 else if (tmp_size > 72 && tmp_size < 80) 3723 size = is_increase ? 80 : 72; 3724 else if (tmp_size > 80 && tmp_size < 1638) 3725 size = 10 * (is_increase ? (tmp_size / 10 + 1) : (tmp_size / 10)); 3726 else if (tmp_size >= 1638) 3727 size = 1638; 3728 else 3729 size = tmp_size; 3730 3731 cf.yHeight = size * 20; /* convert twips to points */ 3732 ME_SetSelectionCharFormat(editor, &cf); 3733 ME_CommitUndo(editor); 3734 ME_WrapMarkedParagraphs(editor); 3735 ME_UpdateScrollBar(editor); 3736 3737 return TRUE; 3738 } 3739 case EM_SETOPTIONS: 3740 { 3741 /* these flags are equivalent to ES_* counterparts, except for 3742 * ECO_AUTOWORDSELECTION that doesn't have an ES_* counterpart, 3743 * but is still stored in editor->styleFlags. */ 3744 const DWORD mask = ECO_VERTICAL | ECO_AUTOHSCROLL | ECO_AUTOVSCROLL | 3745 ECO_NOHIDESEL | ECO_READONLY | ECO_WANTRETURN | 3746 ECO_SELECTIONBAR | ECO_AUTOWORDSELECTION; 3747 DWORD settings = mask & editor->styleFlags; 3748 DWORD oldSettings = settings; 3749 DWORD changedSettings; 3750 3751 switch(wParam) 3752 { 3753 case ECOOP_SET: 3754 settings = lParam; 3755 break; 3756 case ECOOP_OR: 3757 settings |= lParam; 3758 break; 3759 case ECOOP_AND: 3760 settings &= lParam; 3761 break; 3762 case ECOOP_XOR: 3763 settings ^= lParam; 3764 } 3765 changedSettings = oldSettings ^ settings; 3766 3767 if (changedSettings) { 3768 editor->styleFlags = (editor->styleFlags & ~mask) | (settings & mask); 3769 3770 if (changedSettings & ECO_SELECTIONBAR) 3771 { 3772 ITextHost_TxInvalidateRect(editor->texthost, &editor->rcFormat, TRUE); 3773 if (settings & ECO_SELECTIONBAR) { 3774 assert(!editor->selofs); 3775 editor->selofs = SELECTIONBAR_WIDTH; 3776 editor->rcFormat.left += editor->selofs; 3777 } else { 3778 editor->rcFormat.left -= editor->selofs; 3779 editor->selofs = 0; 3780 } 3781 ME_RewrapRepaint(editor); 3782 } 3783 3784 if ((changedSettings & settings & ES_NOHIDESEL) && !editor->bHaveFocus) 3785 ME_InvalidateSelection( editor ); 3786 3787 if (changedSettings & settings & ECO_VERTICAL) 3788 FIXME("ECO_VERTICAL not implemented yet!\n"); 3789 if (changedSettings & settings & ECO_AUTOHSCROLL) 3790 FIXME("ECO_AUTOHSCROLL not implemented yet!\n"); 3791 if (changedSettings & settings & ECO_AUTOVSCROLL) 3792 FIXME("ECO_AUTOVSCROLL not implemented yet!\n"); 3793 if (changedSettings & settings & ECO_WANTRETURN) 3794 FIXME("ECO_WANTRETURN not implemented yet!\n"); 3795 if (changedSettings & settings & ECO_AUTOWORDSELECTION) 3796 FIXME("ECO_AUTOWORDSELECTION not implemented yet!\n"); 3797 } 3798 3799 return settings; 3800 } 3801 case EM_SETSEL: 3802 { 3803 return set_selection( editor, wParam, lParam ); 3804 } 3805 case EM_SETSCROLLPOS: 3806 { 3807 POINT *point = (POINT *)lParam; 3808 ME_ScrollAbs(editor, point->x, point->y); 3809 return 0; 3810 } 3811 case EM_AUTOURLDETECT: 3812 { 3813 if (wParam==1 || wParam ==0) 3814 { 3815 editor->AutoURLDetect_bEnable = (BOOL)wParam; 3816 return 0; 3817 } 3818 return E_INVALIDARG; 3819 } 3820 case EM_GETAUTOURLDETECT: 3821 { 3822 return editor->AutoURLDetect_bEnable; 3823 } 3824 case EM_EXSETSEL: 3825 { 3826 CHARRANGE range = *(CHARRANGE *)lParam; 3827 3828 return set_selection( editor, range.cpMin, range.cpMax ); 3829 } 3830 case EM_SHOWSCROLLBAR: 3831 { 3832 DWORD flags; 3833 3834 switch (wParam) 3835 { 3836 case SB_HORZ: 3837 flags = WS_HSCROLL; 3838 break; 3839 case SB_VERT: 3840 flags = WS_VSCROLL; 3841 break; 3842 case SB_BOTH: 3843 flags = WS_HSCROLL|WS_VSCROLL; 3844 break; 3845 default: 3846 return 0; 3847 } 3848 3849 if (lParam) { 3850 editor->styleFlags |= flags; 3851 if (flags & WS_HSCROLL) 3852 ITextHost_TxShowScrollBar(editor->texthost, SB_HORZ, 3853 editor->nTotalWidth > editor->sizeWindow.cx); 3854 if (flags & WS_VSCROLL) 3855 ITextHost_TxShowScrollBar(editor->texthost, SB_VERT, 3856 editor->nTotalLength > editor->sizeWindow.cy); 3857 } else { 3858 editor->styleFlags &= ~flags; 3859 ITextHost_TxShowScrollBar(editor->texthost, wParam, FALSE); 3860 } 3861 return 0; 3862 } 3863 case EM_SETTEXTEX: 3864 { 3865 LPWSTR wszText; 3866 SETTEXTEX *pStruct = (SETTEXTEX *)wParam; 3867 int from, to, len; 3868 ME_Style *style; 3869 BOOL bRtf, bUnicode, bSelection, bUTF8; 3870 int oldModify = editor->nModifyStep; 3871 static const char utf8_bom[] = {0xef, 0xbb, 0xbf}; 3872 3873 if (!pStruct) return 0; 3874 3875 /* If we detect ascii rtf at the start of the string, 3876 * we know it isn't unicode. */ 3877 bRtf = (lParam && (!strncmp((char *)lParam, "{\\rtf", 5) || 3878 !strncmp((char *)lParam, "{\\urtf", 6))); 3879 bUnicode = !bRtf && pStruct->codepage == CP_UNICODE; 3880 bUTF8 = (lParam && (!strncmp((char *)lParam, utf8_bom, 3))); 3881 3882 TRACE("EM_SETTEXTEX - %s, flags %d, cp %d\n", 3883 bUnicode ? debugstr_w((LPCWSTR)lParam) : debugstr_a((LPCSTR)lParam), 3884 pStruct->flags, pStruct->codepage); 3885 3886 bSelection = (pStruct->flags & ST_SELECTION) != 0; 3887 if (bSelection) { 3888 int nStartCursor = ME_GetSelectionOfs(editor, &from, &to); 3889 style = ME_GetSelectionInsertStyle(editor); 3890 ME_InternalDeleteText(editor, &editor->pCursors[nStartCursor], to - from, FALSE); 3891 } else { 3892 ME_Cursor start; 3893 ME_SetCursorToStart(editor, &start); 3894 ME_InternalDeleteText(editor, &start, ME_GetTextLength(editor), FALSE); 3895 style = editor->pBuffer->pDefaultStyle; 3896 } 3897 3898 if (bRtf) { 3899 ME_StreamInRTFString(editor, bSelection, (char *)lParam); 3900 if (bSelection) { 3901 /* FIXME: The length returned doesn't include the rtf control 3902 * characters, only the actual text. */ 3903 len = lParam ? strlen((char *)lParam) : 0; 3904 } 3905 } else { 3906 if (bUTF8 && !bUnicode) { 3907 wszText = ME_ToUnicode(CP_UTF8, (void *)(lParam+3), &len); 3908 ME_InsertTextFromCursor(editor, 0, wszText, len, style); 3909 ME_EndToUnicode(CP_UTF8, wszText); 3910 } else { 3911 wszText = ME_ToUnicode(pStruct->codepage, (void *)lParam, &len); 3912 ME_InsertTextFromCursor(editor, 0, wszText, len, style); 3913 ME_EndToUnicode(pStruct->codepage, wszText); 3914 } 3915 } 3916 3917 if (bSelection) { 3918 ME_ReleaseStyle(style); 3919 ME_UpdateSelectionLinkAttribute(editor); 3920 } else { 3921 ME_Cursor cursor; 3922 len = 1; 3923 ME_SetCursorToStart(editor, &cursor); 3924 ME_UpdateLinkAttribute(editor, &cursor, INT_MAX); 3925 } 3926 ME_CommitUndo(editor); 3927 if (!(pStruct->flags & ST_KEEPUNDO)) 3928 { 3929 editor->nModifyStep = oldModify; 3930 ME_EmptyUndoStack(editor); 3931 } 3932 ME_UpdateRepaint(editor, FALSE); 3933 return len; 3934 } 3935 case EM_SELECTIONTYPE: 3936 return ME_GetSelectionType(editor); 3937 case EM_SETBKGNDCOLOR: 3938 { 3939 LRESULT lColor; 3940 if (editor->rgbBackColor != -1) { 3941 DeleteObject(editor->hbrBackground); 3942 lColor = editor->rgbBackColor; 3943 } 3944 else lColor = ITextHost_TxGetSysColor(editor->texthost, COLOR_WINDOW); 3945 3946 if (wParam) 3947 { 3948 editor->rgbBackColor = -1; 3949 editor->hbrBackground = GetSysColorBrush(COLOR_WINDOW); 3950 } 3951 else 3952 { 3953 editor->rgbBackColor = lParam; 3954 editor->hbrBackground = CreateSolidBrush(editor->rgbBackColor); 3955 } 3956 ITextHost_TxInvalidateRect(editor->texthost, NULL, TRUE); 3957 return lColor; 3958 } 3959 case EM_GETMODIFY: 3960 return editor->nModifyStep == 0 ? 0 : -1; 3961 case EM_SETMODIFY: 3962 { 3963 if (wParam) 3964 editor->nModifyStep = 1; 3965 else 3966 editor->nModifyStep = 0; 3967 3968 return 0; 3969 } 3970 case EM_SETREADONLY: 3971 { 3972 if (wParam) 3973 editor->styleFlags |= ES_READONLY; 3974 else 3975 editor->styleFlags &= ~ES_READONLY; 3976 return 1; 3977 } 3978 case EM_SETEVENTMASK: 3979 { 3980 DWORD nOldMask = editor->nEventMask; 3981 3982 editor->nEventMask = lParam; 3983 return nOldMask; 3984 } 3985 case EM_GETEVENTMASK: 3986 return editor->nEventMask; 3987 case EM_SETCHARFORMAT: 3988 return handle_EM_SETCHARFORMAT( editor, wParam, (CHARFORMAT2W *)lParam ); 3989 case EM_GETCHARFORMAT: 3990 { 3991 CHARFORMAT2W tmp, *dst = (CHARFORMAT2W *)lParam; 3992 if (dst->cbSize != sizeof(CHARFORMATA) && 3993 dst->cbSize != sizeof(CHARFORMATW) && 3994 dst->cbSize != sizeof(CHARFORMAT2A) && 3995 dst->cbSize != sizeof(CHARFORMAT2W)) 3996 return 0; 3997 tmp.cbSize = sizeof(tmp); 3998 if (!wParam) 3999 ME_GetDefaultCharFormat(editor, &tmp); 4000 else 4001 ME_GetSelectionCharFormat(editor, &tmp); 4002 cf2w_to_cfany(dst, &tmp); 4003 return tmp.dwMask; 4004 } 4005 case EM_SETPARAFORMAT: 4006 { 4007 BOOL result = ME_SetSelectionParaFormat(editor, (PARAFORMAT2 *)lParam); 4008 ME_WrapMarkedParagraphs(editor); 4009 ME_UpdateScrollBar(editor); 4010 ME_CommitUndo(editor); 4011 return result; 4012 } 4013 case EM_GETPARAFORMAT: 4014 ME_GetSelectionParaFormat(editor, (PARAFORMAT2 *)lParam); 4015 return ((PARAFORMAT2 *)lParam)->dwMask; 4016 case EM_GETFIRSTVISIBLELINE: 4017 { 4018 ME_DisplayItem *p = editor->pBuffer->pFirst; 4019 int y = editor->vert_si.nPos; 4020 int ypara = 0; 4021 int count = 0; 4022 int ystart, yend; 4023 while(p) { 4024 p = ME_FindItemFwd(p, diStartRowOrParagraphOrEnd); 4025 if (p->type == diTextEnd) 4026 break; 4027 if (p->type == diParagraph) { 4028 ypara = p->member.para.pt.y; 4029 continue; 4030 } 4031 ystart = ypara + p->member.row.pt.y; 4032 yend = ystart + p->member.row.nHeight; 4033 if (y < yend) { 4034 break; 4035 } 4036 count++; 4037 } 4038 return count; 4039 } 4040 case EM_HIDESELECTION: 4041 { 4042 editor->bHideSelection = (wParam != 0); 4043 ME_InvalidateSelection(editor); 4044 return 0; 4045 } 4046 case EM_LINESCROLL: 4047 { 4048 if (!(editor->styleFlags & ES_MULTILINE)) 4049 return FALSE; 4050 ME_ScrollDown( editor, lParam * get_default_line_height( editor ) ); 4051 return TRUE; 4052 } 4053 case WM_CLEAR: 4054 { 4055 int from, to; 4056 int nStartCursor = ME_GetSelectionOfs(editor, &from, &to); 4057 ME_InternalDeleteText(editor, &editor->pCursors[nStartCursor], to-from, FALSE); 4058 ME_CommitUndo(editor); 4059 ME_UpdateRepaint(editor, TRUE); 4060 return 0; 4061 } 4062 case EM_REPLACESEL: 4063 { 4064 int len = 0; 4065 LONG codepage = unicode ? CP_UNICODE : CP_ACP; 4066 LPWSTR wszText = ME_ToUnicode(codepage, (void *)lParam, &len); 4067 4068 TRACE("EM_REPLACESEL - %s\n", debugstr_w(wszText)); 4069 4070 ME_ReplaceSel(editor, !!wParam, wszText, len); 4071 ME_EndToUnicode(codepage, wszText); 4072 return len; 4073 } 4074 case EM_SCROLLCARET: 4075 ME_EnsureVisible(editor, &editor->pCursors[0]); 4076 return 0; 4077 case WM_SETFONT: 4078 { 4079 LOGFONTW lf; 4080 CHARFORMAT2W fmt; 4081 HDC hDC; 4082 BOOL bRepaint = LOWORD(lParam); 4083 4084 if (!wParam) 4085 wParam = (WPARAM)GetStockObject(SYSTEM_FONT); 4086 4087 if (!GetObjectW((HGDIOBJ)wParam, sizeof(LOGFONTW), &lf)) 4088 return 0; 4089 4090 hDC = ITextHost_TxGetDC(editor->texthost); 4091 ME_CharFormatFromLogFont(hDC, &lf, &fmt); 4092 ITextHost_TxReleaseDC(editor->texthost, hDC); 4093 if (editor->mode & TM_RICHTEXT) { 4094 ME_Cursor start; 4095 ME_SetCursorToStart(editor, &start); 4096 ME_SetCharFormat(editor, &start, NULL, &fmt); 4097 } 4098 ME_SetDefaultCharFormat(editor, &fmt); 4099 4100 ME_CommitUndo(editor); 4101 ME_MarkAllForWrapping(editor); 4102 ME_WrapMarkedParagraphs(editor); 4103 ME_UpdateScrollBar(editor); 4104 if (bRepaint) 4105 ME_Repaint(editor); 4106 return 0; 4107 } 4108 case WM_SETTEXT: 4109 { 4110 ME_Cursor cursor; 4111 ME_SetCursorToStart(editor, &cursor); 4112 ME_InternalDeleteText(editor, &cursor, ME_GetTextLength(editor), FALSE); 4113 if (lParam) 4114 { 4115 TRACE("WM_SETTEXT lParam==%lx\n",lParam); 4116 if (!strncmp((char *)lParam, "{\\rtf", 5) || 4117 !strncmp((char *)lParam, "{\\urtf", 6)) 4118 { 4119 /* Undocumented: WM_SETTEXT supports RTF text */ 4120 ME_StreamInRTFString(editor, 0, (char *)lParam); 4121 } 4122 else 4123 ME_SetText(editor, (void*)lParam, unicode); 4124 } 4125 else 4126 TRACE("WM_SETTEXT - NULL\n"); 4127 ME_SetCursorToStart(editor, &cursor); 4128 ME_UpdateLinkAttribute(editor, &cursor, INT_MAX); 4129 set_selection_cursors(editor, 0, 0); 4130 editor->nModifyStep = 0; 4131 ME_CommitUndo(editor); 4132 ME_EmptyUndoStack(editor); 4133 ME_UpdateRepaint(editor, FALSE); 4134 return 1; 4135 } 4136 case EM_CANPASTE: 4137 return paste_special( editor, 0, NULL, TRUE ); 4138 case WM_PASTE: 4139 case WM_MBUTTONDOWN: 4140 wParam = 0; 4141 lParam = 0; 4142 /* fall through */ 4143 case EM_PASTESPECIAL: 4144 paste_special( editor, wParam, (REPASTESPECIAL *)lParam, FALSE ); 4145 return 0; 4146 case WM_CUT: 4147 case WM_COPY: 4148 copy_or_cut(editor, msg == WM_CUT); 4149 return 0; 4150 case WM_GETTEXTLENGTH: 4151 { 4152 GETTEXTLENGTHEX how; 4153 4154 /* CR/LF conversion required in 2.0 mode, verbatim in 1.0 mode */ 4155 how.flags = GTL_CLOSE | (editor->bEmulateVersion10 ? 0 : GTL_USECRLF) | GTL_NUMCHARS; 4156 how.codepage = unicode ? CP_UNICODE : CP_ACP; 4157 return ME_GetTextLengthEx(editor, &how); 4158 } 4159 case EM_GETTEXTLENGTHEX: 4160 return ME_GetTextLengthEx(editor, (GETTEXTLENGTHEX *)wParam); 4161 case WM_GETTEXT: 4162 { 4163 GETTEXTEX ex; 4164 ex.cb = wParam * (unicode ? sizeof(WCHAR) : sizeof(CHAR)); 4165 ex.flags = GT_USECRLF; 4166 ex.codepage = unicode ? CP_UNICODE : CP_ACP; 4167 ex.lpDefaultChar = NULL; 4168 ex.lpUsedDefChar = NULL; 4169 return ME_GetTextEx(editor, &ex, lParam); 4170 } 4171 case EM_GETTEXTEX: 4172 return ME_GetTextEx(editor, (GETTEXTEX*)wParam, lParam); 4173 case EM_GETSELTEXT: 4174 { 4175 int nFrom, nTo, nStartCur = ME_GetSelectionOfs(editor, &nFrom, &nTo); 4176 ME_Cursor *from = &editor->pCursors[nStartCur]; 4177 return ME_GetTextRange(editor, (WCHAR *)lParam, from, 4178 nTo - nFrom, unicode); 4179 } 4180 case EM_GETSCROLLPOS: 4181 { 4182 POINT *point = (POINT *)lParam; 4183 point->x = editor->horz_si.nPos; 4184 point->y = editor->vert_si.nPos; 4185 /* 16-bit scaled value is returned as stored in scrollinfo */ 4186 if (editor->horz_si.nMax > 0xffff) 4187 point->x = MulDiv(point->x, 0xffff, editor->horz_si.nMax); 4188 if (editor->vert_si.nMax > 0xffff) 4189 point->y = MulDiv(point->y, 0xffff, editor->vert_si.nMax); 4190 return 1; 4191 } 4192 case EM_GETTEXTRANGE: 4193 { 4194 TEXTRANGEW *rng = (TEXTRANGEW *)lParam; 4195 ME_Cursor start; 4196 int nStart = rng->chrg.cpMin; 4197 int nEnd = rng->chrg.cpMax; 4198 int textlength = ME_GetTextLength(editor); 4199 4200 TRACE("EM_GETTEXTRANGE min=%d max=%d unicode=%d textlength=%d\n", 4201 rng->chrg.cpMin, rng->chrg.cpMax, unicode, textlength); 4202 if (nStart < 0) return 0; 4203 if ((nStart == 0 && nEnd == -1) || nEnd > textlength) 4204 nEnd = textlength; 4205 if (nStart >= nEnd) return 0; 4206 4207 ME_CursorFromCharOfs(editor, nStart, &start); 4208 return ME_GetTextRange(editor, rng->lpstrText, &start, nEnd - nStart, unicode); 4209 } 4210 case EM_GETLINE: 4211 { 4212 ME_DisplayItem *run; 4213 const unsigned int nMaxChars = *(WORD *) lParam; 4214 unsigned int nCharsLeft = nMaxChars; 4215 char *dest = (char *) lParam; 4216 BOOL wroteNull = FALSE; 4217 4218 TRACE("EM_GETLINE: row=%d, nMaxChars=%d (%s)\n", (int) wParam, nMaxChars, 4219 unicode ? "Unicode" : "Ansi"); 4220 4221 run = ME_FindRowWithNumber(editor, wParam); 4222 if (run == NULL) 4223 return 0; 4224 4225 while (nCharsLeft && (run = ME_FindItemFwd(run, diRunOrStartRow)) 4226 && run->type == diRun) 4227 { 4228 WCHAR *str = get_text( &run->member.run, 0 ); 4229 unsigned int nCopy; 4230 4231 nCopy = min(nCharsLeft, run->member.run.len); 4232 4233 if (unicode) 4234 memcpy(dest, str, nCopy * sizeof(WCHAR)); 4235 else 4236 nCopy = WideCharToMultiByte(CP_ACP, 0, str, nCopy, dest, 4237 nCharsLeft, NULL, NULL); 4238 dest += nCopy * (unicode ? sizeof(WCHAR) : 1); 4239 nCharsLeft -= nCopy; 4240 } 4241 4242 /* append line termination, space allowing */ 4243 if (nCharsLeft > 0) 4244 { 4245 if (unicode) 4246 *((WCHAR *)dest) = '\0'; 4247 else 4248 *dest = '\0'; 4249 nCharsLeft--; 4250 wroteNull = TRUE; 4251 } 4252 4253 TRACE("EM_GETLINE: got %u characters\n", nMaxChars - nCharsLeft); 4254 return nMaxChars - nCharsLeft - (wroteNull ? 1 : 0); 4255 } 4256 case EM_GETLINECOUNT: 4257 { 4258 ME_DisplayItem *item = editor->pBuffer->pLast; 4259 int nRows = editor->total_rows; 4260 ME_DisplayItem *prev_para = NULL, *last_para = NULL; 4261 4262 last_para = ME_FindItemBack(item, diRun); 4263 prev_para = ME_FindItemBack(last_para, diRun); 4264 assert(last_para); 4265 assert(last_para->member.run.nFlags & MERF_ENDPARA); 4266 if (editor->bEmulateVersion10 && prev_para && 4267 last_para->member.run.nCharOfs == 0 && 4268 prev_para->member.run.len == 1 && 4269 *get_text( &prev_para->member.run, 0 ) == '\r') 4270 { 4271 /* In 1.0 emulation, the last solitary \r at the very end of the text 4272 (if one exists) is NOT a line break. 4273 FIXME: this is an ugly hack. This should have a more regular model. */ 4274 nRows--; 4275 } 4276 4277 TRACE("EM_GETLINECOUNT: nRows==%d\n", nRows); 4278 return max(1, nRows); 4279 } 4280 case EM_LINEFROMCHAR: 4281 { 4282 if (wParam == -1) 4283 return ME_RowNumberFromCharOfs(editor, ME_GetCursorOfs(&editor->pCursors[1])); 4284 else 4285 return ME_RowNumberFromCharOfs(editor, wParam); 4286 } 4287 case EM_EXLINEFROMCHAR: 4288 { 4289 if (lParam == -1) 4290 return ME_RowNumberFromCharOfs(editor, ME_GetCursorOfs(&editor->pCursors[1])); 4291 else 4292 return ME_RowNumberFromCharOfs(editor, lParam); 4293 } 4294 case EM_LINEINDEX: 4295 { 4296 ME_DisplayItem *item, *para; 4297 int nCharOfs; 4298 4299 if (wParam == -1) 4300 item = ME_FindItemBack(editor->pCursors[0].pRun, diStartRow); 4301 else 4302 item = ME_FindRowWithNumber(editor, wParam); 4303 if (!item) 4304 return -1; 4305 para = ME_GetParagraph(item); 4306 item = ME_FindItemFwd(item, diRun); 4307 nCharOfs = para->member.para.nCharOfs + item->member.run.nCharOfs; 4308 TRACE("EM_LINEINDEX: nCharOfs==%d\n", nCharOfs); 4309 return nCharOfs; 4310 } 4311 case EM_LINELENGTH: 4312 { 4313 ME_DisplayItem *item, *item_end; 4314 int nChars = 0, nThisLineOfs = 0, nNextLineOfs = 0; 4315 ME_DisplayItem *para, *run; 4316 4317 if (wParam > ME_GetTextLength(editor)) 4318 return 0; 4319 if (wParam == -1) 4320 { 4321 FIXME("EM_LINELENGTH: returning number of unselected characters on lines with selection unsupported.\n"); 4322 return 0; 4323 } 4324 ME_RunOfsFromCharOfs(editor, wParam, ¶, &run, NULL); 4325 item = ME_RowStart(run); 4326 nThisLineOfs = ME_CharOfsFromRunOfs(editor, para, ME_FindItemFwd(item, diRun), 0); 4327 item_end = ME_FindItemFwd(item, diStartRowOrParagraphOrEnd); 4328 if (item_end->type == diStartRow) { 4329 nNextLineOfs = ME_CharOfsFromRunOfs(editor, para, ME_FindItemFwd(item_end, diRun), 0); 4330 } else { 4331 ME_DisplayItem *endRun = ME_FindItemBack(item_end, diRun); 4332 assert(endRun && endRun->member.run.nFlags & MERF_ENDPARA); 4333 nNextLineOfs = item_end->member.para.nCharOfs - endRun->member.run.len; 4334 } 4335 nChars = nNextLineOfs - nThisLineOfs; 4336 TRACE("EM_LINELENGTH(%ld)==%d\n",wParam, nChars); 4337 return nChars; 4338 } 4339 case EM_EXLIMITTEXT: 4340 { 4341 if ((int)lParam < 0) 4342 return 0; 4343 if (lParam == 0) 4344 editor->nTextLimit = 65536; 4345 else 4346 editor->nTextLimit = (int) lParam; 4347 return 0; 4348 } 4349 case EM_LIMITTEXT: 4350 { 4351 if (wParam == 0) 4352 editor->nTextLimit = 65536; 4353 else 4354 editor->nTextLimit = (int) wParam; 4355 return 0; 4356 } 4357 case EM_GETLIMITTEXT: 4358 { 4359 return editor->nTextLimit; 4360 } 4361 case EM_FINDTEXT: 4362 { 4363 LRESULT r; 4364 if(!unicode){ 4365 FINDTEXTA *ft = (FINDTEXTA *)lParam; 4366 int nChars = MultiByteToWideChar(CP_ACP, 0, ft->lpstrText, -1, NULL, 0); 4367 WCHAR *tmp; 4368 4369 if ((tmp = heap_alloc(nChars * sizeof(*tmp))) != NULL) 4370 MultiByteToWideChar(CP_ACP, 0, ft->lpstrText, -1, tmp, nChars); 4371 r = ME_FindText(editor, wParam, &ft->chrg, tmp, NULL); 4372 heap_free(tmp); 4373 }else{ 4374 FINDTEXTW *ft = (FINDTEXTW *)lParam; 4375 r = ME_FindText(editor, wParam, &ft->chrg, ft->lpstrText, NULL); 4376 } 4377 return r; 4378 } 4379 case EM_FINDTEXTEX: 4380 { 4381 LRESULT r; 4382 if(!unicode){ 4383 FINDTEXTEXA *ex = (FINDTEXTEXA *)lParam; 4384 int nChars = MultiByteToWideChar(CP_ACP, 0, ex->lpstrText, -1, NULL, 0); 4385 WCHAR *tmp; 4386 4387 if ((tmp = heap_alloc(nChars * sizeof(*tmp))) != NULL) 4388 MultiByteToWideChar(CP_ACP, 0, ex->lpstrText, -1, tmp, nChars); 4389 r = ME_FindText(editor, wParam, &ex->chrg, tmp, &ex->chrgText); 4390 heap_free(tmp); 4391 }else{ 4392 FINDTEXTEXW *ex = (FINDTEXTEXW *)lParam; 4393 r = ME_FindText(editor, wParam, &ex->chrg, ex->lpstrText, &ex->chrgText); 4394 } 4395 return r; 4396 } 4397 case EM_FINDTEXTW: 4398 { 4399 FINDTEXTW *ft = (FINDTEXTW *)lParam; 4400 return ME_FindText(editor, wParam, &ft->chrg, ft->lpstrText, NULL); 4401 } 4402 case EM_FINDTEXTEXW: 4403 { 4404 FINDTEXTEXW *ex = (FINDTEXTEXW *)lParam; 4405 return ME_FindText(editor, wParam, &ex->chrg, ex->lpstrText, &ex->chrgText); 4406 } 4407 case EM_GETZOOM: 4408 if (!wParam || !lParam) 4409 return FALSE; 4410 *(int *)wParam = editor->nZoomNumerator; 4411 *(int *)lParam = editor->nZoomDenominator; 4412 return TRUE; 4413 case EM_SETZOOM: 4414 return ME_SetZoom(editor, wParam, lParam); 4415 case EM_CHARFROMPOS: 4416 { 4417 ME_Cursor cursor; 4418 if (ME_CharFromPos(editor, ((POINTL *)lParam)->x, ((POINTL *)lParam)->y, 4419 &cursor, NULL)) 4420 return ME_GetCursorOfs(&cursor); 4421 else 4422 return -1; 4423 } 4424 case EM_POSFROMCHAR: 4425 { 4426 ME_DisplayItem *pPara, *pRun; 4427 int nCharOfs, nOffset, nLength; 4428 POINTL pt = {0,0}; 4429 4430 nCharOfs = wParam; 4431 /* detect which API version we're dealing with */ 4432 if (wParam >= 0x40000) 4433 nCharOfs = lParam; 4434 nLength = ME_GetTextLength(editor); 4435 nCharOfs = min(nCharOfs, nLength); 4436 nCharOfs = max(nCharOfs, 0); 4437 4438 ME_RunOfsFromCharOfs(editor, nCharOfs, &pPara, &pRun, &nOffset); 4439 assert(pRun->type == diRun); 4440 pt.y = pRun->member.run.pt.y; 4441 pt.x = pRun->member.run.pt.x + ME_PointFromChar(editor, &pRun->member.run, nOffset, TRUE); 4442 pt.y += pPara->member.para.pt.y + editor->rcFormat.top; 4443 pt.x += editor->rcFormat.left; 4444 4445 pt.x -= editor->horz_si.nPos; 4446 pt.y -= editor->vert_si.nPos; 4447 4448 if (wParam >= 0x40000) { 4449 *(POINTL *)wParam = pt; 4450 } 4451 return (wParam >= 0x40000) ? 0 : MAKELONG( pt.x, pt.y ); 4452 } 4453 case WM_CREATE: 4454 return ME_WmCreate(editor, lParam, unicode); 4455 case WM_DESTROY: 4456 ME_DestroyEditor(editor); 4457 return 0; 4458 case WM_SETCURSOR: 4459 { 4460 POINT cursor_pos; 4461 if (wParam == (WPARAM)editor->hWnd && GetCursorPos(&cursor_pos) && 4462 ScreenToClient(editor->hWnd, &cursor_pos)) 4463 ME_LinkNotify(editor, msg, 0, MAKELPARAM(cursor_pos.x, cursor_pos.y)); 4464 return ME_SetCursor(editor); 4465 } 4466 case WM_LBUTTONDBLCLK: 4467 case WM_LBUTTONDOWN: 4468 { 4469 ME_CommitUndo(editor); /* End coalesced undos for typed characters */ 4470 if ((editor->nEventMask & ENM_MOUSEEVENTS) && 4471 !ME_FilterEvent(editor, msg, &wParam, &lParam)) 4472 return 0; 4473 ITextHost_TxSetFocus(editor->texthost); 4474 ME_LButtonDown(editor, (short)LOWORD(lParam), (short)HIWORD(lParam), 4475 ME_CalculateClickCount(editor, msg, wParam, lParam)); 4476 ITextHost_TxSetCapture(editor->texthost, TRUE); 4477 editor->bMouseCaptured = TRUE; 4478 ME_LinkNotify(editor, msg, wParam, lParam); 4479 if (!ME_SetCursor(editor)) goto do_default; 4480 break; 4481 } 4482 case WM_MOUSEMOVE: 4483 if ((editor->nEventMask & ENM_MOUSEEVENTS) && 4484 !ME_FilterEvent(editor, msg, &wParam, &lParam)) 4485 return 0; 4486 if (editor->bMouseCaptured) 4487 ME_MouseMove(editor, (short)LOWORD(lParam), (short)HIWORD(lParam)); 4488 else 4489 ME_LinkNotify(editor, msg, wParam, lParam); 4490 /* Set cursor if mouse is captured, since WM_SETCURSOR won't be received. */ 4491 if (editor->bMouseCaptured) 4492 ME_SetCursor(editor); 4493 break; 4494 case WM_LBUTTONUP: 4495 if (editor->bMouseCaptured) { 4496 ITextHost_TxSetCapture(editor->texthost, FALSE); 4497 editor->bMouseCaptured = FALSE; 4498 } 4499 if (editor->nSelectionType == stDocument) 4500 editor->nSelectionType = stPosition; 4501 if ((editor->nEventMask & ENM_MOUSEEVENTS) && 4502 !ME_FilterEvent(editor, msg, &wParam, &lParam)) 4503 return 0; 4504 else 4505 { 4506 ME_SetCursor(editor); 4507 ME_LinkNotify(editor, msg, wParam, lParam); 4508 } 4509 break; 4510 case WM_RBUTTONUP: 4511 case WM_RBUTTONDOWN: 4512 case WM_RBUTTONDBLCLK: 4513 ME_CommitUndo(editor); /* End coalesced undos for typed characters */ 4514 if ((editor->nEventMask & ENM_MOUSEEVENTS) && 4515 !ME_FilterEvent(editor, msg, &wParam, &lParam)) 4516 return 0; 4517 ME_LinkNotify(editor, msg, wParam, lParam); 4518 goto do_default; 4519 case WM_CONTEXTMENU: 4520 if (!ME_ShowContextMenu(editor, (short)LOWORD(lParam), (short)HIWORD(lParam))) 4521 goto do_default; 4522 break; 4523 case WM_SETFOCUS: 4524 editor->bHaveFocus = TRUE; 4525 create_caret(editor); 4526 update_caret(editor); 4527 ME_SendOldNotify(editor, EN_SETFOCUS); 4528 if (!editor->bHideSelection && !(editor->styleFlags & ES_NOHIDESEL)) 4529 ME_InvalidateSelection( editor ); 4530 return 0; 4531 case WM_KILLFOCUS: 4532 ME_CommitUndo(editor); /* End coalesced undos for typed characters */ 4533 editor->bHaveFocus = FALSE; 4534 editor->wheel_remain = 0; 4535 hide_caret(editor); 4536 DestroyCaret(); 4537 ME_SendOldNotify(editor, EN_KILLFOCUS); 4538 if (!editor->bHideSelection && !(editor->styleFlags & ES_NOHIDESEL)) 4539 ME_InvalidateSelection( editor ); 4540 return 0; 4541 case WM_COMMAND: 4542 TRACE("editor wnd command = %d\n", LOWORD(wParam)); 4543 return 0; 4544 case WM_KEYUP: 4545 if ((editor->nEventMask & ENM_KEYEVENTS) && 4546 !ME_FilterEvent(editor, msg, &wParam, &lParam)) 4547 return 0; 4548 goto do_default; 4549 case WM_KEYDOWN: 4550 if ((editor->nEventMask & ENM_KEYEVENTS) && 4551 !ME_FilterEvent(editor, msg, &wParam, &lParam)) 4552 return 0; 4553 if (ME_KeyDown(editor, LOWORD(wParam))) 4554 return 0; 4555 goto do_default; 4556 case WM_CHAR: 4557 if ((editor->nEventMask & ENM_KEYEVENTS) && 4558 !ME_FilterEvent(editor, msg, &wParam, &lParam)) 4559 return 0; 4560 return ME_Char(editor, wParam, lParam, unicode); 4561 case WM_UNICHAR: 4562 if (unicode) 4563 { 4564 if(wParam == UNICODE_NOCHAR) return TRUE; 4565 if(wParam <= 0x000fffff) 4566 { 4567 if(wParam > 0xffff) /* convert to surrogates */ 4568 { 4569 wParam -= 0x10000; 4570 ME_Char(editor, (wParam >> 10) + 0xd800, 0, TRUE); 4571 ME_Char(editor, (wParam & 0x03ff) + 0xdc00, 0, TRUE); 4572 } else { 4573 ME_Char(editor, wParam, 0, TRUE); 4574 } 4575 } 4576 return 0; 4577 } 4578 break; 4579 case EM_STOPGROUPTYPING: 4580 ME_CommitUndo(editor); /* End coalesced undos for typed characters */ 4581 return 0; 4582 case WM_HSCROLL: 4583 { 4584 const int scrollUnit = 7; 4585 4586 switch(LOWORD(wParam)) 4587 { 4588 case SB_LEFT: 4589 ME_ScrollAbs(editor, 0, 0); 4590 break; 4591 case SB_RIGHT: 4592 ME_ScrollAbs(editor, 4593 editor->horz_si.nMax - (int)editor->horz_si.nPage, 4594 editor->vert_si.nMax - (int)editor->vert_si.nPage); 4595 break; 4596 case SB_LINELEFT: 4597 ME_ScrollLeft(editor, scrollUnit); 4598 break; 4599 case SB_LINERIGHT: 4600 ME_ScrollRight(editor, scrollUnit); 4601 break; 4602 case SB_PAGELEFT: 4603 ME_ScrollLeft(editor, editor->sizeWindow.cx); 4604 break; 4605 case SB_PAGERIGHT: 4606 ME_ScrollRight(editor, editor->sizeWindow.cx); 4607 break; 4608 case SB_THUMBTRACK: 4609 case SB_THUMBPOSITION: 4610 { 4611 int pos = HIWORD(wParam); 4612 if (editor->horz_si.nMax > 0xffff) 4613 pos = MulDiv(pos, editor->horz_si.nMax, 0xffff); 4614 ME_HScrollAbs(editor, pos); 4615 break; 4616 } 4617 } 4618 break; 4619 } 4620 case EM_SCROLL: /* fall through */ 4621 case WM_VSCROLL: 4622 { 4623 int origNPos; 4624 int lineHeight = get_default_line_height( editor ); 4625 4626 origNPos = editor->vert_si.nPos; 4627 4628 switch(LOWORD(wParam)) 4629 { 4630 case SB_TOP: 4631 ME_ScrollAbs(editor, 0, 0); 4632 break; 4633 case SB_BOTTOM: 4634 ME_ScrollAbs(editor, 4635 editor->horz_si.nMax - (int)editor->horz_si.nPage, 4636 editor->vert_si.nMax - (int)editor->vert_si.nPage); 4637 break; 4638 case SB_LINEUP: 4639 ME_ScrollUp(editor,lineHeight); 4640 break; 4641 case SB_LINEDOWN: 4642 ME_ScrollDown(editor,lineHeight); 4643 break; 4644 case SB_PAGEUP: 4645 ME_ScrollUp(editor,editor->sizeWindow.cy); 4646 break; 4647 case SB_PAGEDOWN: 4648 ME_ScrollDown(editor,editor->sizeWindow.cy); 4649 break; 4650 case SB_THUMBTRACK: 4651 case SB_THUMBPOSITION: 4652 { 4653 int pos = HIWORD(wParam); 4654 if (editor->vert_si.nMax > 0xffff) 4655 pos = MulDiv(pos, editor->vert_si.nMax, 0xffff); 4656 ME_VScrollAbs(editor, pos); 4657 break; 4658 } 4659 } 4660 if (msg == EM_SCROLL) 4661 return 0x00010000 | (((editor->vert_si.nPos - origNPos)/lineHeight) & 0xffff); 4662 break; 4663 } 4664 case WM_MOUSEWHEEL: 4665 { 4666 int delta; 4667 BOOL ctrl_is_down; 4668 4669 if ((editor->nEventMask & ENM_MOUSEEVENTS) && 4670 !ME_FilterEvent(editor, msg, &wParam, &lParam)) 4671 return 0; 4672 4673 ctrl_is_down = GetKeyState(VK_CONTROL) & 0x8000; 4674 4675 delta = GET_WHEEL_DELTA_WPARAM(wParam); 4676 4677 /* if scrolling changes direction, ignore left overs */ 4678 if ((delta < 0 && editor->wheel_remain < 0) || 4679 (delta > 0 && editor->wheel_remain > 0)) 4680 editor->wheel_remain += delta; 4681 else 4682 editor->wheel_remain = delta; 4683 4684 if (editor->wheel_remain) 4685 { 4686 if (ctrl_is_down) { 4687 int numerator; 4688 if (!editor->nZoomNumerator || !editor->nZoomDenominator) 4689 { 4690 numerator = 100; 4691 } else { 4692 numerator = editor->nZoomNumerator * 100 / editor->nZoomDenominator; 4693 } 4694 numerator += calc_wheel_change( &editor->wheel_remain, 10 ); 4695 if (numerator >= 10 && numerator <= 500) 4696 ME_SetZoom(editor, numerator, 100); 4697 } else { 4698 UINT max_lines = 3; 4699 int lines = 0; 4700 4701 SystemParametersInfoW( SPI_GETWHEELSCROLLLINES, 0, &max_lines, 0 ); 4702 if (max_lines) 4703 lines = calc_wheel_change( &editor->wheel_remain, (int)max_lines ); 4704 if (lines) 4705 ME_ScrollDown( editor, -lines * get_default_line_height( editor ) ); 4706 } 4707 } 4708 break; 4709 } 4710 case EM_GETRECT: 4711 { 4712 *((RECT *)lParam) = editor->rcFormat; 4713 if (editor->bDefaultFormatRect) 4714 ((RECT *)lParam)->left -= editor->selofs; 4715 return 0; 4716 } 4717 case EM_SETRECT: 4718 case EM_SETRECTNP: 4719 { 4720 if (lParam) 4721 { 4722 int border = 0; 4723 RECT clientRect; 4724 RECT *rc = (RECT *)lParam; 4725 4726 border = editor->exStyleFlags & WS_EX_CLIENTEDGE ? 1 : 0; 4727 ITextHost_TxGetClientRect(editor->texthost, &clientRect); 4728 if (wParam == 0) 4729 { 4730 editor->rcFormat.top = max(0, rc->top - border); 4731 editor->rcFormat.left = max(0, rc->left - border); 4732 editor->rcFormat.bottom = min(clientRect.bottom, rc->bottom); 4733 editor->rcFormat.right = min(clientRect.right, rc->right + border); 4734 } else if (wParam == 1) { 4735 /* MSDN incorrectly says a wParam value of 1 causes the 4736 * lParam rect to be used as a relative offset, 4737 * however, the tests show it just prevents min/max bound 4738 * checking. */ 4739 editor->rcFormat.top = rc->top - border; 4740 editor->rcFormat.left = rc->left - border; 4741 editor->rcFormat.bottom = rc->bottom; 4742 editor->rcFormat.right = rc->right + border; 4743 } else { 4744 return 0; 4745 } 4746 editor->bDefaultFormatRect = FALSE; 4747 } 4748 else 4749 { 4750 ME_SetDefaultFormatRect(editor); 4751 editor->bDefaultFormatRect = TRUE; 4752 } 4753 ME_MarkAllForWrapping(editor); 4754 ME_WrapMarkedParagraphs(editor); 4755 ME_UpdateScrollBar(editor); 4756 if (msg != EM_SETRECTNP) 4757 ME_Repaint(editor); 4758 return 0; 4759 } 4760 case EM_REQUESTRESIZE: 4761 ME_SendRequestResize(editor, TRUE); 4762 return 0; 4763 case WM_SETREDRAW: 4764 goto do_default; 4765 case WM_WINDOWPOSCHANGED: 4766 { 4767 RECT clientRect; 4768 WINDOWPOS *winpos = (WINDOWPOS *)lParam; 4769 4770 if (winpos->flags & SWP_NOCLIENTSIZE) goto do_default; 4771 ITextHost_TxGetClientRect(editor->texthost, &clientRect); 4772 if (editor->bDefaultFormatRect) { 4773 ME_SetDefaultFormatRect(editor); 4774 } else { 4775 editor->rcFormat.right += clientRect.right - editor->prevClientRect.right; 4776 editor->rcFormat.bottom += clientRect.bottom - editor->prevClientRect.bottom; 4777 } 4778 editor->prevClientRect = clientRect; 4779 ME_RewrapRepaint(editor); 4780 goto do_default; 4781 } 4782 /* IME messages to make richedit controls IME aware */ 4783 case WM_IME_SETCONTEXT: 4784 case WM_IME_CONTROL: 4785 case WM_IME_SELECT: 4786 case WM_IME_COMPOSITIONFULL: 4787 return 0; 4788 case WM_IME_STARTCOMPOSITION: 4789 { 4790 editor->imeStartIndex=ME_GetCursorOfs(&editor->pCursors[0]); 4791 ME_DeleteSelection(editor); 4792 ME_CommitUndo(editor); 4793 ME_UpdateRepaint(editor, FALSE); 4794 return 0; 4795 } 4796 case WM_IME_COMPOSITION: 4797 { 4798 HIMC hIMC; 4799 4800 ME_Style *style = ME_GetInsertStyle(editor, 0); 4801 hIMC = ITextHost_TxImmGetContext(editor->texthost); 4802 ME_DeleteSelection(editor); 4803 ME_SaveTempStyle(editor, style); 4804 if (lParam & (GCS_RESULTSTR|GCS_COMPSTR)) 4805 { 4806 LPWSTR lpCompStr = NULL; 4807 DWORD dwBufLen; 4808 DWORD dwIndex = lParam & GCS_RESULTSTR; 4809 if (!dwIndex) 4810 dwIndex = GCS_COMPSTR; 4811 4812 dwBufLen = ImmGetCompositionStringW(hIMC, dwIndex, NULL, 0); 4813 lpCompStr = HeapAlloc(GetProcessHeap(),0,dwBufLen + sizeof(WCHAR)); 4814 ImmGetCompositionStringW(hIMC, dwIndex, lpCompStr, dwBufLen); 4815 lpCompStr[dwBufLen/sizeof(WCHAR)] = 0; 4816 ME_InsertTextFromCursor(editor,0,lpCompStr,dwBufLen/sizeof(WCHAR),style); 4817 HeapFree(GetProcessHeap(), 0, lpCompStr); 4818 4819 if (dwIndex == GCS_COMPSTR) 4820 set_selection_cursors(editor,editor->imeStartIndex, 4821 editor->imeStartIndex + dwBufLen/sizeof(WCHAR)); 4822 } 4823 ME_ReleaseStyle(style); 4824 ME_CommitUndo(editor); 4825 ME_UpdateRepaint(editor, FALSE); 4826 return 0; 4827 } 4828 case WM_IME_ENDCOMPOSITION: 4829 { 4830 ME_DeleteSelection(editor); 4831 editor->imeStartIndex=-1; 4832 return 0; 4833 } 4834 case EM_GETOLEINTERFACE: 4835 { 4836 if (!editor->reOle) 4837 if (!CreateIRichEditOle(NULL, editor, (LPVOID *)&editor->reOle)) 4838 return 0; 4839 if (IUnknown_QueryInterface(editor->reOle, &IID_IRichEditOle, (LPVOID *)lParam) == S_OK) 4840 return 1; 4841 return 0; 4842 } 4843 case EM_GETPASSWORDCHAR: 4844 { 4845 return editor->cPasswordMask; 4846 } 4847 case EM_SETOLECALLBACK: 4848 if(editor->lpOleCallback) 4849 IRichEditOleCallback_Release(editor->lpOleCallback); 4850 editor->lpOleCallback = (IRichEditOleCallback*)lParam; 4851 if(editor->lpOleCallback) 4852 IRichEditOleCallback_AddRef(editor->lpOleCallback); 4853 return TRUE; 4854 case EM_GETWORDBREAKPROC: 4855 return (LRESULT)editor->pfnWordBreak; 4856 case EM_SETWORDBREAKPROC: 4857 { 4858 EDITWORDBREAKPROCW pfnOld = editor->pfnWordBreak; 4859 4860 editor->pfnWordBreak = (EDITWORDBREAKPROCW)lParam; 4861 return (LRESULT)pfnOld; 4862 } 4863 case EM_GETTEXTMODE: 4864 return editor->mode; 4865 case EM_SETTEXTMODE: 4866 { 4867 int mask = 0; 4868 int changes = 0; 4869 4870 if (ME_GetTextLength(editor) || 4871 !list_empty( &editor->undo_stack ) || !list_empty( &editor->redo_stack )) 4872 return E_UNEXPECTED; 4873 4874 /* Check for mutually exclusive flags in adjacent bits of wParam */ 4875 if ((wParam & (TM_RICHTEXT | TM_MULTILEVELUNDO | TM_MULTICODEPAGE)) & 4876 (wParam & (TM_PLAINTEXT | TM_SINGLELEVELUNDO | TM_SINGLECODEPAGE)) << 1) 4877 return E_INVALIDARG; 4878 4879 if (wParam & (TM_RICHTEXT | TM_PLAINTEXT)) 4880 { 4881 mask |= TM_RICHTEXT | TM_PLAINTEXT; 4882 changes |= wParam & (TM_RICHTEXT | TM_PLAINTEXT); 4883 if (wParam & TM_PLAINTEXT) { 4884 /* Clear selection since it should be possible to select the 4885 * end of text run for rich text */ 4886 ME_InvalidateSelection(editor); 4887 ME_SetCursorToStart(editor, &editor->pCursors[0]); 4888 editor->pCursors[1] = editor->pCursors[0]; 4889 /* plain text can only have the default style. */ 4890 ME_ClearTempStyle(editor); 4891 ME_AddRefStyle(editor->pBuffer->pDefaultStyle); 4892 ME_ReleaseStyle(editor->pCursors[0].pRun->member.run.style); 4893 editor->pCursors[0].pRun->member.run.style = editor->pBuffer->pDefaultStyle; 4894 } 4895 } 4896 /* FIXME: Currently no support for undo level and code page options */ 4897 editor->mode = (editor->mode & ~mask) | changes; 4898 return 0; 4899 } 4900 case EM_SETPASSWORDCHAR: 4901 { 4902 editor->cPasswordMask = wParam; 4903 ME_RewrapRepaint(editor); 4904 return 0; 4905 } 4906 case EM_SETTARGETDEVICE: 4907 if (wParam == 0) 4908 { 4909 BOOL new = (lParam == 0 && (editor->styleFlags & ES_MULTILINE)); 4910 if (editor->nAvailWidth || editor->bWordWrap != new) 4911 { 4912 editor->bWordWrap = new; 4913 editor->nAvailWidth = 0; /* wrap to client area */ 4914 ME_RewrapRepaint(editor); 4915 } 4916 } else { 4917 int width = max(0, lParam); 4918 if ((editor->styleFlags & ES_MULTILINE) && 4919 (!editor->bWordWrap || editor->nAvailWidth != width)) 4920 { 4921 editor->nAvailWidth = width; 4922 editor->bWordWrap = TRUE; 4923 ME_RewrapRepaint(editor); 4924 } 4925 FIXME("EM_SETTARGETDEVICE doesn't use non-NULL target devices\n"); 4926 } 4927 return TRUE; 4928 default: 4929 do_default: 4930 *phresult = S_FALSE; 4931 break; 4932 } 4933 return 0L; 4934 } 4935 4936 static BOOL create_windowed_editor(HWND hwnd, CREATESTRUCTW *create, BOOL emulate_10) 4937 { 4938 ITextHost *host = ME_CreateTextHost( hwnd, create, emulate_10 ); 4939 ME_TextEditor *editor; 4940 4941 if (!host) return FALSE; 4942 4943 editor = ME_MakeEditor( host, emulate_10 ); 4944 if (!editor) 4945 { 4946 ITextHost_Release( host ); 4947 return FALSE; 4948 } 4949 4950 editor->exStyleFlags = GetWindowLongW( hwnd, GWL_EXSTYLE ); 4951 editor->styleFlags |= GetWindowLongW( hwnd, GWL_STYLE ) & ES_WANTRETURN; 4952 editor->hWnd = hwnd; /* FIXME: Remove editor's dependence on hWnd */ 4953 editor->hwndParent = create->hwndParent; 4954 4955 SetWindowLongPtrW( hwnd, 0, (LONG_PTR)editor ); 4956 4957 return TRUE; 4958 } 4959 4960 static LRESULT RichEditWndProc_common(HWND hWnd, UINT msg, WPARAM wParam, 4961 LPARAM lParam, BOOL unicode) 4962 { 4963 ME_TextEditor *editor; 4964 HRESULT hresult; 4965 LRESULT lresult = 0; 4966 4967 TRACE("enter hwnd %p msg %04x (%s) %lx %lx, unicode %d\n", 4968 hWnd, msg, get_msg_name(msg), wParam, lParam, unicode); 4969 4970 editor = (ME_TextEditor *)GetWindowLongPtrW(hWnd, 0); 4971 if (!editor) 4972 { 4973 if (msg == WM_NCCREATE) 4974 { 4975 CREATESTRUCTW *pcs = (CREATESTRUCTW *)lParam; 4976 4977 TRACE("WM_NCCREATE: hWnd %p style 0x%08x\n", hWnd, pcs->style); 4978 return create_windowed_editor( hWnd, pcs, FALSE ); 4979 } 4980 else 4981 { 4982 return DefWindowProcW(hWnd, msg, wParam, lParam); 4983 } 4984 } 4985 4986 switch (msg) 4987 { 4988 case WM_PAINT: 4989 { 4990 HDC hdc; 4991 RECT rc; 4992 PAINTSTRUCT ps; 4993 HBRUSH old_brush; 4994 4995 update_caret(editor); 4996 hdc = BeginPaint(editor->hWnd, &ps); 4997 if (!editor->bEmulateVersion10 || (editor->nEventMask & ENM_UPDATE)) 4998 ME_SendOldNotify(editor, EN_UPDATE); 4999 old_brush = SelectObject(hdc, editor->hbrBackground); 5000 5001 /* Erase area outside of the formatting rectangle */ 5002 if (ps.rcPaint.top < editor->rcFormat.top) 5003 { 5004 rc = ps.rcPaint; 5005 rc.bottom = editor->rcFormat.top; 5006 PatBlt(hdc, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, PATCOPY); 5007 ps.rcPaint.top = editor->rcFormat.top; 5008 } 5009 if (ps.rcPaint.bottom > editor->rcFormat.bottom) { 5010 rc = ps.rcPaint; 5011 rc.top = editor->rcFormat.bottom; 5012 PatBlt(hdc, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, PATCOPY); 5013 ps.rcPaint.bottom = editor->rcFormat.bottom; 5014 } 5015 if (ps.rcPaint.left < editor->rcFormat.left) { 5016 rc = ps.rcPaint; 5017 rc.right = editor->rcFormat.left; 5018 PatBlt(hdc, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, PATCOPY); 5019 ps.rcPaint.left = editor->rcFormat.left; 5020 } 5021 if (ps.rcPaint.right > editor->rcFormat.right) { 5022 rc = ps.rcPaint; 5023 rc.left = editor->rcFormat.right; 5024 PatBlt(hdc, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, PATCOPY); 5025 ps.rcPaint.right = editor->rcFormat.right; 5026 } 5027 5028 ME_PaintContent(editor, hdc, &ps.rcPaint); 5029 SelectObject(hdc, old_brush); 5030 EndPaint(editor->hWnd, &ps); 5031 return 0; 5032 } 5033 case WM_ERASEBKGND: 5034 { 5035 HDC hDC = (HDC)wParam; 5036 RECT rc; 5037 5038 if (GetUpdateRect(editor->hWnd, &rc, TRUE)) 5039 FillRect(hDC, &rc, editor->hbrBackground); 5040 return 1; 5041 } 5042 case EM_SETOPTIONS: 5043 { 5044 DWORD dwStyle; 5045 const DWORD mask = ECO_VERTICAL | ECO_AUTOHSCROLL | ECO_AUTOVSCROLL | 5046 ECO_NOHIDESEL | ECO_READONLY | ECO_WANTRETURN | 5047 ECO_SELECTIONBAR; 5048 lresult = ME_HandleMessage(editor, msg, wParam, lParam, unicode, &hresult); 5049 dwStyle = GetWindowLongW(hWnd, GWL_STYLE); 5050 dwStyle = (dwStyle & ~mask) | (lresult & mask); 5051 SetWindowLongW(hWnd, GWL_STYLE, dwStyle); 5052 return lresult; 5053 } 5054 case EM_SETREADONLY: 5055 { 5056 DWORD dwStyle; 5057 lresult = ME_HandleMessage(editor, msg, wParam, lParam, unicode, &hresult); 5058 dwStyle = GetWindowLongW(hWnd, GWL_STYLE); 5059 dwStyle &= ~ES_READONLY; 5060 if (wParam) 5061 dwStyle |= ES_READONLY; 5062 SetWindowLongW(hWnd, GWL_STYLE, dwStyle); 5063 return lresult; 5064 } 5065 default: 5066 lresult = ME_HandleMessage(editor, msg, wParam, lParam, unicode, &hresult); 5067 } 5068 5069 if (hresult == S_FALSE) 5070 lresult = DefWindowProcW(hWnd, msg, wParam, lParam); 5071 5072 TRACE("exit hwnd %p msg %04x (%s) %lx %lx, unicode %d -> %lu\n", 5073 hWnd, msg, get_msg_name(msg), wParam, lParam, unicode, lresult); 5074 5075 return lresult; 5076 } 5077 5078 static LRESULT WINAPI RichEditWndProcW(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) 5079 { 5080 BOOL unicode = TRUE; 5081 5082 /* Under Win9x RichEdit20W returns ANSI strings, see the tests. */ 5083 if (msg == WM_GETTEXT && (GetVersion() & 0x80000000)) 5084 unicode = FALSE; 5085 5086 return RichEditWndProc_common(hWnd, msg, wParam, lParam, unicode); 5087 } 5088 5089 static LRESULT WINAPI RichEditWndProcA(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) 5090 { 5091 return RichEditWndProc_common(hWnd, msg, wParam, lParam, FALSE); 5092 } 5093 5094 /****************************************************************** 5095 * RichEditANSIWndProc (RICHED20.10) 5096 */ 5097 LRESULT WINAPI RichEditANSIWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) 5098 { 5099 return RichEditWndProcA(hWnd, msg, wParam, lParam); 5100 } 5101 5102 /****************************************************************** 5103 * RichEdit10ANSIWndProc (RICHED20.9) 5104 */ 5105 LRESULT WINAPI RichEdit10ANSIWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) 5106 { 5107 if (msg == WM_NCCREATE && !GetWindowLongPtrW(hWnd, 0)) 5108 { 5109 CREATESTRUCTW *pcs = (CREATESTRUCTW *)lParam; 5110 5111 TRACE("WM_NCCREATE: hWnd %p style 0x%08x\n", hWnd, pcs->style); 5112 return create_windowed_editor( hWnd, pcs, TRUE ); 5113 } 5114 return RichEditANSIWndProc(hWnd, msg, wParam, lParam); 5115 } 5116 5117 void ME_SendOldNotify(ME_TextEditor *editor, int nCode) 5118 { 5119 ITextHost_TxNotify(editor->texthost, nCode, NULL); 5120 } 5121 5122 /* Fill buffer with srcChars unicode characters from the start cursor. 5123 * 5124 * buffer: destination buffer 5125 * buflen: length of buffer in characters excluding the NULL terminator. 5126 * start: start of editor text to copy into buffer. 5127 * srcChars: Number of characters to use from the editor text. 5128 * bCRLF: if true, replaces all end of lines with \r\n pairs. 5129 * 5130 * returns the number of characters written excluding the NULL terminator. 5131 * 5132 * The written text is always NULL terminated. 5133 */ 5134 int ME_GetTextW(ME_TextEditor *editor, WCHAR *buffer, int buflen, 5135 const ME_Cursor *start, int srcChars, BOOL bCRLF, 5136 BOOL bEOP) 5137 { 5138 ME_DisplayItem *pRun, *pNextRun; 5139 const WCHAR *pStart = buffer; 5140 const WCHAR cr_lf[] = {'\r', '\n', 0}; 5141 const WCHAR *str; 5142 int nLen; 5143 5144 /* bCRLF flag is only honored in 2.0 and up. 1.0 must always return text verbatim */ 5145 if (editor->bEmulateVersion10) bCRLF = FALSE; 5146 5147 pRun = start->pRun; 5148 assert(pRun); 5149 pNextRun = ME_FindItemFwd(pRun, diRun); 5150 5151 nLen = pRun->member.run.len - start->nOffset; 5152 str = get_text( &pRun->member.run, start->nOffset ); 5153 5154 while (srcChars && buflen && pNextRun) 5155 { 5156 int nFlags = pRun->member.run.nFlags; 5157 5158 if (bCRLF && nFlags & MERF_ENDPARA && ~nFlags & MERF_ENDCELL) 5159 { 5160 if (buflen == 1) break; 5161 /* FIXME: native fails to reduce srcChars here for WM_GETTEXT or 5162 * EM_GETTEXTEX, however, this is done for copying text which 5163 * also uses this function. */ 5164 srcChars -= min(nLen, srcChars); 5165 nLen = 2; 5166 str = cr_lf; 5167 } else { 5168 nLen = min(nLen, srcChars); 5169 srcChars -= nLen; 5170 } 5171 5172 nLen = min(nLen, buflen); 5173 buflen -= nLen; 5174 5175 CopyMemory(buffer, str, sizeof(WCHAR) * nLen); 5176 5177 buffer += nLen; 5178 5179 pRun = pNextRun; 5180 pNextRun = ME_FindItemFwd(pRun, diRun); 5181 5182 nLen = pRun->member.run.len; 5183 str = get_text( &pRun->member.run, 0 ); 5184 } 5185 /* append '\r' to the last paragraph. */ 5186 if (pRun->next->type == diTextEnd && bEOP) 5187 { 5188 *buffer = '\r'; 5189 buffer ++; 5190 } 5191 *buffer = 0; 5192 return buffer - pStart; 5193 } 5194 5195 static BOOL ME_RegisterEditorClass(HINSTANCE hInstance) 5196 { 5197 WNDCLASSW wcW; 5198 WNDCLASSA wcA; 5199 5200 wcW.style = CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW | CS_GLOBALCLASS; 5201 wcW.lpfnWndProc = RichEditWndProcW; 5202 wcW.cbClsExtra = 0; 5203 wcW.cbWndExtra = sizeof(ME_TextEditor *); 5204 wcW.hInstance = NULL; /* hInstance would register DLL-local class */ 5205 wcW.hIcon = NULL; 5206 wcW.hCursor = LoadCursorW(NULL, (LPWSTR)IDC_IBEAM); 5207 wcW.hbrBackground = GetStockObject(NULL_BRUSH); 5208 wcW.lpszMenuName = NULL; 5209 5210 if (is_version_nt()) 5211 { 5212 wcW.lpszClassName = RICHEDIT_CLASS20W; 5213 if (!RegisterClassW(&wcW)) return FALSE; 5214 wcW.lpszClassName = MSFTEDIT_CLASS; 5215 if (!RegisterClassW(&wcW)) return FALSE; 5216 } 5217 else 5218 { 5219 /* WNDCLASSA/W have the same layout */ 5220 wcW.lpszClassName = (LPCWSTR)"RichEdit20W"; 5221 if (!RegisterClassA((WNDCLASSA *)&wcW)) return FALSE; 5222 wcW.lpszClassName = (LPCWSTR)"RichEdit50W"; 5223 if (!RegisterClassA((WNDCLASSA *)&wcW)) return FALSE; 5224 } 5225 5226 wcA.style = CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW | CS_GLOBALCLASS; 5227 wcA.lpfnWndProc = RichEditWndProcA; 5228 wcA.cbClsExtra = 0; 5229 wcA.cbWndExtra = sizeof(ME_TextEditor *); 5230 wcA.hInstance = NULL; /* hInstance would register DLL-local class */ 5231 wcA.hIcon = NULL; 5232 wcA.hCursor = LoadCursorW(NULL, (LPWSTR)IDC_IBEAM); 5233 wcA.hbrBackground = GetStockObject(NULL_BRUSH); 5234 wcA.lpszMenuName = NULL; 5235 wcA.lpszClassName = RICHEDIT_CLASS20A; 5236 if (!RegisterClassA(&wcA)) return FALSE; 5237 wcA.lpszClassName = "RichEdit50A"; 5238 if (!RegisterClassA(&wcA)) return FALSE; 5239 5240 return TRUE; 5241 } 5242 5243 static LRESULT WINAPI REComboWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { 5244 /* FIXME: Not implemented */ 5245 TRACE("hWnd %p msg %04x (%s) %08lx %08lx\n", 5246 hWnd, msg, get_msg_name(msg), wParam, lParam); 5247 return DefWindowProcW(hWnd, msg, wParam, lParam); 5248 } 5249 5250 static LRESULT WINAPI REListWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { 5251 /* FIXME: Not implemented */ 5252 TRACE("hWnd %p msg %04x (%s) %08lx %08lx\n", 5253 hWnd, msg, get_msg_name(msg), wParam, lParam); 5254 return DefWindowProcW(hWnd, msg, wParam, lParam); 5255 } 5256 5257 /****************************************************************** 5258 * REExtendedRegisterClass (RICHED20.8) 5259 * 5260 * FIXME undocumented 5261 * Need to check for errors and implement controls and callbacks 5262 */ 5263 LRESULT WINAPI REExtendedRegisterClass(void) 5264 { 5265 WNDCLASSW wcW; 5266 UINT result; 5267 5268 FIXME("semi stub\n"); 5269 5270 wcW.cbClsExtra = 0; 5271 wcW.cbWndExtra = 4; 5272 wcW.hInstance = NULL; 5273 wcW.hIcon = NULL; 5274 wcW.hCursor = NULL; 5275 wcW.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); 5276 wcW.lpszMenuName = NULL; 5277 5278 if (!ME_ListBoxRegistered) 5279 { 5280 wcW.style = CS_PARENTDC | CS_DBLCLKS | CS_GLOBALCLASS; 5281 wcW.lpfnWndProc = REListWndProc; 5282 wcW.lpszClassName = REListBox20W; 5283 if (RegisterClassW(&wcW)) ME_ListBoxRegistered = TRUE; 5284 } 5285 5286 if (!ME_ComboBoxRegistered) 5287 { 5288 wcW.style = CS_PARENTDC | CS_DBLCLKS | CS_GLOBALCLASS | CS_VREDRAW | CS_HREDRAW; 5289 wcW.lpfnWndProc = REComboWndProc; 5290 wcW.lpszClassName = REComboBox20W; 5291 if (RegisterClassW(&wcW)) ME_ComboBoxRegistered = TRUE; 5292 } 5293 5294 result = 0; 5295 if (ME_ListBoxRegistered) 5296 result += 1; 5297 if (ME_ComboBoxRegistered) 5298 result += 2; 5299 5300 return result; 5301 } 5302 5303 static int __cdecl wchar_comp( const void *key, const void *elem ) 5304 { 5305 return *(const WCHAR *)key - *(const WCHAR *)elem; 5306 } 5307 5308 /* neutral characters end the url if the next non-neutral character is a space character, 5309 otherwise they are included in the url. */ 5310 static BOOL isurlneutral( WCHAR c ) 5311 { 5312 /* NB this list is sorted */ 5313 static const WCHAR neutral_chars[] = {'!','\"','\'','(',')',',','-','.',':',';','<','>','?','[',']','{','}'}; 5314 5315 /* Some shortcuts */ 5316 if (isalnum( c )) return FALSE; 5317 if (c > neutral_chars[ARRAY_SIZE( neutral_chars ) - 1]) return FALSE; 5318 5319 return !!bsearch( &c, neutral_chars, ARRAY_SIZE( neutral_chars ), sizeof(c), wchar_comp ); 5320 } 5321 5322 /** 5323 * This proc takes a selection, and scans it forward in order to select the span 5324 * of a possible URL candidate. A possible URL candidate must start with isalnum 5325 * or one of the following special characters: *|/\+%#@ and must consist entirely 5326 * of the characters allowed to start the URL, plus : (colon) which may occur 5327 * at most once, and not at either end. 5328 */ 5329 static BOOL ME_FindNextURLCandidate(ME_TextEditor *editor, 5330 const ME_Cursor *start, 5331 int nChars, 5332 ME_Cursor *candidate_min, 5333 ME_Cursor *candidate_max) 5334 { 5335 ME_Cursor cursor = *start, neutral_end, space_end; 5336 BOOL candidateStarted = FALSE, quoted = FALSE; 5337 WCHAR c; 5338 5339 while (nChars > 0) 5340 { 5341 WCHAR *str = get_text( &cursor.pRun->member.run, 0 ); 5342 int run_len = cursor.pRun->member.run.len; 5343 5344 nChars -= run_len - cursor.nOffset; 5345 5346 /* Find start of candidate */ 5347 if (!candidateStarted) 5348 { 5349 while (cursor.nOffset < run_len) 5350 { 5351 c = str[cursor.nOffset]; 5352 if (!iswspace( c ) && !isurlneutral( c )) 5353 { 5354 *candidate_min = cursor; 5355 candidateStarted = TRUE; 5356 neutral_end.pPara = NULL; 5357 space_end.pPara = NULL; 5358 cursor.nOffset++; 5359 break; 5360 } 5361 quoted = (c == '<'); 5362 cursor.nOffset++; 5363 } 5364 } 5365 5366 /* Find end of candidate */ 5367 if (candidateStarted) 5368 { 5369 while (cursor.nOffset < run_len) 5370 { 5371 c = str[cursor.nOffset]; 5372 if (iswspace( c )) 5373 { 5374 if (quoted && c != '\r') 5375 { 5376 if (!space_end.pPara) 5377 { 5378 if (neutral_end.pPara) 5379 space_end = neutral_end; 5380 else 5381 space_end = cursor; 5382 } 5383 } 5384 else 5385 goto done; 5386 } 5387 else if (isurlneutral( c )) 5388 { 5389 if (quoted && c == '>') 5390 { 5391 neutral_end.pPara = NULL; 5392 space_end.pPara = NULL; 5393 goto done; 5394 } 5395 if (!neutral_end.pPara) 5396 neutral_end = cursor; 5397 } 5398 else 5399 neutral_end.pPara = NULL; 5400 5401 cursor.nOffset++; 5402 } 5403 } 5404 5405 cursor.nOffset = 0; 5406 if (!ME_NextRun(&cursor.pPara, &cursor.pRun, TRUE)) 5407 goto done; 5408 } 5409 5410 done: 5411 if (candidateStarted) 5412 { 5413 if (space_end.pPara) 5414 *candidate_max = space_end; 5415 else if (neutral_end.pPara) 5416 *candidate_max = neutral_end; 5417 else 5418 *candidate_max = cursor; 5419 return TRUE; 5420 } 5421 *candidate_max = *candidate_min = cursor; 5422 return FALSE; 5423 } 5424 5425 /** 5426 * This proc evaluates the selection and returns TRUE if it can be considered an URL 5427 */ 5428 static BOOL ME_IsCandidateAnURL(ME_TextEditor *editor, const ME_Cursor *start, int nChars) 5429 { 5430 #define MAX_PREFIX_LEN 9 5431 struct prefix_s { 5432 const WCHAR text[MAX_PREFIX_LEN]; 5433 int length; 5434 }prefixes[] = { 5435 {{'p','r','o','s','p','e','r','o',':'}, 9}, 5436 {{'t','e','l','n','e','t',':'}, 7}, 5437 {{'g','o','p','h','e','r',':'}, 7}, 5438 {{'m','a','i','l','t','o',':'}, 7}, 5439 {{'h','t','t','p','s',':'}, 6}, 5440 {{'f','i','l','e',':'}, 5}, 5441 {{'n','e','w','s',':'}, 5}, 5442 {{'w','a','i','s',':'}, 5}, 5443 {{'n','n','t','p',':'}, 5}, 5444 {{'h','t','t','p',':'}, 5}, 5445 {{'w','w','w','.'}, 4}, 5446 {{'f','t','p',':'}, 4}, 5447 }; 5448 WCHAR bufferW[MAX_PREFIX_LEN + 1]; 5449 unsigned int i; 5450 5451 ME_GetTextW(editor, bufferW, MAX_PREFIX_LEN, start, nChars, FALSE, FALSE); 5452 for (i = 0; i < ARRAY_SIZE(prefixes); i++) 5453 { 5454 if (nChars < prefixes[i].length) continue; 5455 if (!memcmp(prefixes[i].text, bufferW, prefixes[i].length * sizeof(WCHAR))) 5456 return TRUE; 5457 } 5458 return FALSE; 5459 #undef MAX_PREFIX_LEN 5460 } 5461 5462 /** 5463 * This proc walks through the indicated selection and evaluates whether each 5464 * section identified by ME_FindNextURLCandidate and in-between sections have 5465 * their proper CFE_LINK attributes set or unset. If the CFE_LINK attribute is 5466 * not what it is supposed to be, this proc sets or unsets it as appropriate. 5467 * 5468 * Since this function can cause runs to be split, do not depend on the value 5469 * of the start cursor at the end of the function. 5470 * 5471 * nChars may be set to INT_MAX to update to the end of the text. 5472 * 5473 * Returns TRUE if at least one section was modified. 5474 */ 5475 static BOOL ME_UpdateLinkAttribute(ME_TextEditor *editor, ME_Cursor *start, int nChars) 5476 { 5477 BOOL modified = FALSE; 5478 ME_Cursor startCur = *start; 5479 5480 if (!editor->AutoURLDetect_bEnable) return FALSE; 5481 5482 do 5483 { 5484 CHARFORMAT2W link; 5485 ME_Cursor candidateStart, candidateEnd; 5486 5487 if (ME_FindNextURLCandidate(editor, &startCur, nChars, 5488 &candidateStart, &candidateEnd)) 5489 { 5490 /* Section before candidate is not an URL */ 5491 int cMin = ME_GetCursorOfs(&candidateStart); 5492 int cMax = ME_GetCursorOfs(&candidateEnd); 5493 5494 if (!ME_IsCandidateAnURL(editor, &candidateStart, cMax - cMin)) 5495 candidateStart = candidateEnd; 5496 nChars -= cMax - ME_GetCursorOfs(&startCur); 5497 } 5498 else 5499 { 5500 /* No more candidates until end of selection */ 5501 nChars = 0; 5502 } 5503 5504 if (startCur.pRun != candidateStart.pRun || 5505 startCur.nOffset != candidateStart.nOffset) 5506 { 5507 /* CFE_LINK effect should be consistently unset */ 5508 link.cbSize = sizeof(link); 5509 ME_GetCharFormat(editor, &startCur, &candidateStart, &link); 5510 if (!(link.dwMask & CFM_LINK) || (link.dwEffects & CFE_LINK)) 5511 { 5512 /* CFE_LINK must be unset from this range */ 5513 memset(&link, 0, sizeof(CHARFORMAT2W)); 5514 link.cbSize = sizeof(link); 5515 link.dwMask = CFM_LINK; 5516 link.dwEffects = 0; 5517 ME_SetCharFormat(editor, &startCur, &candidateStart, &link); 5518 /* Update candidateEnd since setting character formats may split 5519 * runs, which can cause a cursor to be at an invalid offset within 5520 * a split run. */ 5521 while (candidateEnd.nOffset >= candidateEnd.pRun->member.run.len) 5522 { 5523 candidateEnd.nOffset -= candidateEnd.pRun->member.run.len; 5524 candidateEnd.pRun = ME_FindItemFwd(candidateEnd.pRun, diRun); 5525 } 5526 modified = TRUE; 5527 } 5528 } 5529 if (candidateStart.pRun != candidateEnd.pRun || 5530 candidateStart.nOffset != candidateEnd.nOffset) 5531 { 5532 /* CFE_LINK effect should be consistently set */ 5533 link.cbSize = sizeof(link); 5534 ME_GetCharFormat(editor, &candidateStart, &candidateEnd, &link); 5535 if (!(link.dwMask & CFM_LINK) || !(link.dwEffects & CFE_LINK)) 5536 { 5537 /* CFE_LINK must be set on this range */ 5538 memset(&link, 0, sizeof(CHARFORMAT2W)); 5539 link.cbSize = sizeof(link); 5540 link.dwMask = CFM_LINK; 5541 link.dwEffects = CFE_LINK; 5542 ME_SetCharFormat(editor, &candidateStart, &candidateEnd, &link); 5543 modified = TRUE; 5544 } 5545 } 5546 startCur = candidateEnd; 5547 } while (nChars > 0); 5548 return modified; 5549 } 5550