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