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