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