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