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