xref: /reactos/dll/win32/riched20/editor.c (revision aad80191)
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 LONG ME_GetSelectionType(ME_TextEditor *editor)
2968 {
2969     LONG sel_type = SEL_EMPTY;
2970     LONG start, end;
2971 
2972     ME_GetSelectionOfs(editor, &start, &end);
2973     if (start == end)
2974         sel_type = SEL_EMPTY;
2975     else
2976     {
2977         LONG object_count = 0, character_count = 0;
2978         int i;
2979 
2980         for (i = 0; i < end - start; i++)
2981         {
2982             ME_Cursor cursor;
2983 
2984             ME_CursorFromCharOfs(editor, start + i, &cursor);
2985             if (cursor.pRun->member.run.reobj)
2986                 object_count++;
2987             else
2988                 character_count++;
2989             if (character_count >= 2 && object_count >= 2)
2990                 return (SEL_TEXT | SEL_MULTICHAR | SEL_OBJECT | SEL_MULTIOBJECT);
2991         }
2992         if (character_count)
2993         {
2994             sel_type |= SEL_TEXT;
2995             if (character_count >= 2)
2996                 sel_type |= SEL_MULTICHAR;
2997         }
2998         if (object_count)
2999         {
3000             sel_type |= SEL_OBJECT;
3001             if (object_count >= 2)
3002                 sel_type |= SEL_MULTIOBJECT;
3003         }
3004     }
3005     return sel_type;
3006 }
3007 
3008 static BOOL ME_ShowContextMenu(ME_TextEditor *editor, int x, int y)
3009 {
3010   CHARRANGE selrange;
3011   HMENU menu;
3012   int seltype;
3013 
3014   if(!editor->lpOleCallback || !editor->hWnd)
3015     return FALSE;
3016   ME_GetSelectionOfs(editor, &selrange.cpMin, &selrange.cpMax);
3017   seltype = ME_GetSelectionType(editor);
3018   if(SUCCEEDED(IRichEditOleCallback_GetContextMenu(editor->lpOleCallback, seltype, NULL, &selrange, &menu)))
3019   {
3020     TrackPopupMenu(menu, TPM_LEFTALIGN | TPM_RIGHTBUTTON, x, y, 0, editor->hwndParent, NULL);
3021     DestroyMenu(menu);
3022   }
3023   return TRUE;
3024 }
3025 
3026 ME_TextEditor *ME_MakeEditor(ITextHost *texthost, BOOL bEmulateVersion10)
3027 {
3028   ME_TextEditor *ed = heap_alloc(sizeof(*ed));
3029   int i;
3030   DWORD props;
3031   LONG selbarwidth;
3032 
3033   ed->hWnd = NULL;
3034   ed->hwndParent = NULL;
3035   ed->sizeWindow.cx = ed->sizeWindow.cy = 0;
3036   ed->texthost = texthost;
3037   ed->reOle = NULL;
3038   ed->bEmulateVersion10 = bEmulateVersion10;
3039   ed->styleFlags = 0;
3040   ed->exStyleFlags = 0;
3041   ITextHost_TxGetPropertyBits(texthost,
3042                               (TXTBIT_RICHTEXT|TXTBIT_MULTILINE|
3043                                TXTBIT_READONLY|TXTBIT_USEPASSWORD|
3044                                TXTBIT_HIDESELECTION|TXTBIT_SAVESELECTION|
3045                                TXTBIT_AUTOWORDSEL|TXTBIT_VERTICAL|
3046                                TXTBIT_WORDWRAP|TXTBIT_DISABLEDRAG),
3047                               &props);
3048   ITextHost_TxGetScrollBars(texthost, &ed->styleFlags);
3049   ed->styleFlags &= (WS_VSCROLL|WS_HSCROLL|ES_AUTOVSCROLL|
3050                      ES_AUTOHSCROLL|ES_DISABLENOSCROLL);
3051   ed->pBuffer = ME_MakeText();
3052   ed->nZoomNumerator = ed->nZoomDenominator = 0;
3053   ed->nAvailWidth = 0; /* wrap to client area */
3054   ME_MakeFirstParagraph(ed);
3055   /* The four cursors are for:
3056    * 0 - The position where the caret is shown
3057    * 1 - The anchored end of the selection (for normal selection)
3058    * 2 & 3 - The anchored start and end respectively for word, line,
3059    * or paragraph selection.
3060    */
3061   ed->nCursors = 4;
3062   ed->pCursors = heap_alloc(ed->nCursors * sizeof(*ed->pCursors));
3063   ME_SetCursorToStart(ed, &ed->pCursors[0]);
3064   ed->pCursors[1] = ed->pCursors[0];
3065   ed->pCursors[2] = ed->pCursors[0];
3066   ed->pCursors[3] = ed->pCursors[1];
3067   ed->nLastTotalLength = ed->nTotalLength = 0;
3068   ed->nLastTotalWidth = ed->nTotalWidth = 0;
3069   ed->nUDArrowX = -1;
3070   ed->rgbBackColor = -1;
3071   ed->hbrBackground = GetSysColorBrush(COLOR_WINDOW);
3072   ed->bCaretAtEnd = FALSE;
3073   ed->nEventMask = 0;
3074   ed->nModifyStep = 0;
3075   ed->nTextLimit = TEXT_LIMIT_DEFAULT;
3076   list_init( &ed->undo_stack );
3077   list_init( &ed->redo_stack );
3078   ed->nUndoStackSize = 0;
3079   ed->nUndoLimit = STACK_SIZE_DEFAULT;
3080   ed->nUndoMode = umAddToUndo;
3081   ed->nParagraphs = 1;
3082   ed->nLastSelStart = ed->nLastSelEnd = 0;
3083   ed->pLastSelStartPara = ed->pLastSelEndPara = ed->pCursors[0].pPara;
3084   ed->bHideSelection = FALSE;
3085   ed->pfnWordBreak = NULL;
3086   ed->lpOleCallback = NULL;
3087   ed->mode = TM_MULTILEVELUNDO | TM_MULTICODEPAGE;
3088   ed->mode |= (props & TXTBIT_RICHTEXT) ? TM_RICHTEXT : TM_PLAINTEXT;
3089   ed->AutoURLDetect_bEnable = FALSE;
3090   ed->bHaveFocus = FALSE;
3091   ed->bDialogMode = FALSE;
3092   ed->bMouseCaptured = FALSE;
3093   for (i=0; i<HFONT_CACHE_SIZE; i++)
3094   {
3095     ed->pFontCache[i].nRefs = 0;
3096     ed->pFontCache[i].nAge = 0;
3097     ed->pFontCache[i].hFont = NULL;
3098   }
3099 
3100   ME_CheckCharOffsets(ed);
3101   SetRectEmpty(&ed->rcFormat);
3102   ed->bDefaultFormatRect = TRUE;
3103   ITextHost_TxGetSelectionBarWidth(ed->texthost, &selbarwidth);
3104   if (selbarwidth) {
3105     /* FIXME: Convert selbarwidth from HIMETRIC to pixels */
3106     ed->selofs = SELECTIONBAR_WIDTH;
3107     ed->styleFlags |= ES_SELECTIONBAR;
3108   } else {
3109     ed->selofs = 0;
3110   }
3111   ed->nSelectionType = stPosition;
3112 
3113   ed->cPasswordMask = 0;
3114   if (props & TXTBIT_USEPASSWORD)
3115     ITextHost_TxGetPasswordChar(texthost, &ed->cPasswordMask);
3116 
3117   if (props & TXTBIT_AUTOWORDSEL)
3118     ed->styleFlags |= ECO_AUTOWORDSELECTION;
3119   if (props & TXTBIT_MULTILINE) {
3120     ed->styleFlags |= ES_MULTILINE;
3121     ed->bWordWrap = (props & TXTBIT_WORDWRAP) != 0;
3122   } else {
3123     ed->bWordWrap = FALSE;
3124   }
3125   if (props & TXTBIT_READONLY)
3126     ed->styleFlags |= ES_READONLY;
3127   if (!(props & TXTBIT_HIDESELECTION))
3128     ed->styleFlags |= ES_NOHIDESEL;
3129   if (props & TXTBIT_SAVESELECTION)
3130     ed->styleFlags |= ES_SAVESEL;
3131   if (props & TXTBIT_VERTICAL)
3132     ed->styleFlags |= ES_VERTICAL;
3133   if (props & TXTBIT_DISABLEDRAG)
3134     ed->styleFlags |= ES_NOOLEDRAGDROP;
3135 
3136   ed->notified_cr.cpMin = ed->notified_cr.cpMax = 0;
3137 
3138   /* Default scrollbar information */
3139   ed->vert_si.cbSize = sizeof(SCROLLINFO);
3140   ed->vert_si.nMin = 0;
3141   ed->vert_si.nMax = 0;
3142   ed->vert_si.nPage = 0;
3143   ed->vert_si.nPos = 0;
3144 
3145   ed->horz_si.cbSize = sizeof(SCROLLINFO);
3146   ed->horz_si.nMin = 0;
3147   ed->horz_si.nMax = 0;
3148   ed->horz_si.nPage = 0;
3149   ed->horz_si.nPos = 0;
3150 
3151   ed->wheel_remain = 0;
3152 
3153   list_init( &ed->style_list );
3154   list_init( &ed->reobj_list );
3155   OleInitialize(NULL);
3156 
3157   return ed;
3158 }
3159 
3160 void ME_DestroyEditor(ME_TextEditor *editor)
3161 {
3162   ME_DisplayItem *pFirst = editor->pBuffer->pFirst;
3163   ME_DisplayItem *p = pFirst, *pNext = NULL;
3164   ME_Style *s, *cursor2;
3165   int i;
3166 
3167   ME_ClearTempStyle(editor);
3168   ME_EmptyUndoStack(editor);
3169   while(p) {
3170     pNext = p->next;
3171     ME_DestroyDisplayItem(p);
3172     p = pNext;
3173   }
3174 
3175   LIST_FOR_EACH_ENTRY_SAFE( s, cursor2, &editor->style_list, ME_Style, entry )
3176       ME_DestroyStyle( s );
3177 
3178   ME_ReleaseStyle(editor->pBuffer->pDefaultStyle);
3179   for (i=0; i<HFONT_CACHE_SIZE; i++)
3180   {
3181     if (editor->pFontCache[i].hFont)
3182       DeleteObject(editor->pFontCache[i].hFont);
3183   }
3184   if (editor->rgbBackColor != -1)
3185     DeleteObject(editor->hbrBackground);
3186   if(editor->lpOleCallback)
3187     IRichEditOleCallback_Release(editor->lpOleCallback);
3188   ITextHost_Release(editor->texthost);
3189   if (editor->reOle)
3190   {
3191     IRichEditOle_Release(editor->reOle);
3192     editor->reOle = NULL;
3193   }
3194   OleUninitialize();
3195 
3196   heap_free(editor->pBuffer);
3197   heap_free(editor->pCursors);
3198   heap_free(editor);
3199 }
3200 
3201 BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
3202 {
3203     TRACE("\n");
3204     switch (fdwReason)
3205     {
3206     case DLL_PROCESS_ATTACH:
3207       DisableThreadLibraryCalls(hinstDLL);
3208       me_heap = HeapCreate (0, 0x10000, 0);
3209       if (!ME_RegisterEditorClass(hinstDLL)) return FALSE;
3210       hLeft = LoadCursorW(hinstDLL, MAKEINTRESOURCEW(OCR_REVERSE));
3211       LookupInit();
3212       break;
3213 
3214     case DLL_PROCESS_DETACH:
3215       if (lpvReserved) break;
3216       UnregisterClassW(RICHEDIT_CLASS20W, 0);
3217       UnregisterClassW(MSFTEDIT_CLASS, 0);
3218       UnregisterClassA(RICHEDIT_CLASS20A, 0);
3219       UnregisterClassA("RichEdit50A", 0);
3220       if (ME_ListBoxRegistered)
3221           UnregisterClassW(REListBox20W, 0);
3222       if (ME_ComboBoxRegistered)
3223           UnregisterClassW(REComboBox20W, 0);
3224       LookupCleanup();
3225       HeapDestroy (me_heap);
3226       release_typelib();
3227       break;
3228     }
3229     return TRUE;
3230 }
3231 
3232 static inline int get_default_line_height( ME_TextEditor *editor )
3233 {
3234     int height = 0;
3235 
3236     if (editor->pBuffer && editor->pBuffer->pDefaultStyle)
3237         height = editor->pBuffer->pDefaultStyle->tm.tmHeight;
3238     if (height <= 0) height = 24;
3239 
3240     return height;
3241 }
3242 
3243 static inline int calc_wheel_change( int *remain, int amount_per_click )
3244 {
3245     int change = amount_per_click * (float)*remain / WHEEL_DELTA;
3246     *remain -= WHEEL_DELTA * change / amount_per_click;
3247     return change;
3248 }
3249 
3250 static const char * const edit_messages[] = {
3251   "EM_GETSEL",
3252   "EM_SETSEL",
3253   "EM_GETRECT",
3254   "EM_SETRECT",
3255   "EM_SETRECTNP",
3256   "EM_SCROLL",
3257   "EM_LINESCROLL",
3258   "EM_SCROLLCARET",
3259   "EM_GETMODIFY",
3260   "EM_SETMODIFY",
3261   "EM_GETLINECOUNT",
3262   "EM_LINEINDEX",
3263   "EM_SETHANDLE",
3264   "EM_GETHANDLE",
3265   "EM_GETTHUMB",
3266   "EM_UNKNOWN_BF",
3267   "EM_UNKNOWN_C0",
3268   "EM_LINELENGTH",
3269   "EM_REPLACESEL",
3270   "EM_UNKNOWN_C3",
3271   "EM_GETLINE",
3272   "EM_LIMITTEXT",
3273   "EM_CANUNDO",
3274   "EM_UNDO",
3275   "EM_FMTLINES",
3276   "EM_LINEFROMCHAR",
3277   "EM_UNKNOWN_CA",
3278   "EM_SETTABSTOPS",
3279   "EM_SETPASSWORDCHAR",
3280   "EM_EMPTYUNDOBUFFER",
3281   "EM_GETFIRSTVISIBLELINE",
3282   "EM_SETREADONLY",
3283   "EM_SETWORDBREAKPROC",
3284   "EM_GETWORDBREAKPROC",
3285   "EM_GETPASSWORDCHAR",
3286   "EM_SETMARGINS",
3287   "EM_GETMARGINS",
3288   "EM_GETLIMITTEXT",
3289   "EM_POSFROMCHAR",
3290   "EM_CHARFROMPOS",
3291   "EM_SETIMESTATUS",
3292   "EM_GETIMESTATUS"
3293 };
3294 
3295 static const char * const richedit_messages[] = {
3296   "EM_CANPASTE",
3297   "EM_DISPLAYBAND",
3298   "EM_EXGETSEL",
3299   "EM_EXLIMITTEXT",
3300   "EM_EXLINEFROMCHAR",
3301   "EM_EXSETSEL",
3302   "EM_FINDTEXT",
3303   "EM_FORMATRANGE",
3304   "EM_GETCHARFORMAT",
3305   "EM_GETEVENTMASK",
3306   "EM_GETOLEINTERFACE",
3307   "EM_GETPARAFORMAT",
3308   "EM_GETSELTEXT",
3309   "EM_HIDESELECTION",
3310   "EM_PASTESPECIAL",
3311   "EM_REQUESTRESIZE",
3312   "EM_SELECTIONTYPE",
3313   "EM_SETBKGNDCOLOR",
3314   "EM_SETCHARFORMAT",
3315   "EM_SETEVENTMASK",
3316   "EM_SETOLECALLBACK",
3317   "EM_SETPARAFORMAT",
3318   "EM_SETTARGETDEVICE",
3319   "EM_STREAMIN",
3320   "EM_STREAMOUT",
3321   "EM_GETTEXTRANGE",
3322   "EM_FINDWORDBREAK",
3323   "EM_SETOPTIONS",
3324   "EM_GETOPTIONS",
3325   "EM_FINDTEXTEX",
3326   "EM_GETWORDBREAKPROCEX",
3327   "EM_SETWORDBREAKPROCEX",
3328   "EM_SETUNDOLIMIT",
3329   "EM_UNKNOWN_USER_83",
3330   "EM_REDO",
3331   "EM_CANREDO",
3332   "EM_GETUNDONAME",
3333   "EM_GETREDONAME",
3334   "EM_STOPGROUPTYPING",
3335   "EM_SETTEXTMODE",
3336   "EM_GETTEXTMODE",
3337   "EM_AUTOURLDETECT",
3338   "EM_GETAUTOURLDETECT",
3339   "EM_SETPALETTE",
3340   "EM_GETTEXTEX",
3341   "EM_GETTEXTLENGTHEX",
3342   "EM_SHOWSCROLLBAR",
3343   "EM_SETTEXTEX",
3344   "EM_UNKNOWN_USER_98",
3345   "EM_UNKNOWN_USER_99",
3346   "EM_SETPUNCTUATION",
3347   "EM_GETPUNCTUATION",
3348   "EM_SETWORDWRAPMODE",
3349   "EM_GETWORDWRAPMODE",
3350   "EM_SETIMECOLOR",
3351   "EM_GETIMECOLOR",
3352   "EM_SETIMEOPTIONS",
3353   "EM_GETIMEOPTIONS",
3354   "EM_CONVPOSITION",
3355   "EM_UNKNOWN_USER_109",
3356   "EM_UNKNOWN_USER_110",
3357   "EM_UNKNOWN_USER_111",
3358   "EM_UNKNOWN_USER_112",
3359   "EM_UNKNOWN_USER_113",
3360   "EM_UNKNOWN_USER_114",
3361   "EM_UNKNOWN_USER_115",
3362   "EM_UNKNOWN_USER_116",
3363   "EM_UNKNOWN_USER_117",
3364   "EM_UNKNOWN_USER_118",
3365   "EM_UNKNOWN_USER_119",
3366   "EM_SETLANGOPTIONS",
3367   "EM_GETLANGOPTIONS",
3368   "EM_GETIMECOMPMODE",
3369   "EM_FINDTEXTW",
3370   "EM_FINDTEXTEXW",
3371   "EM_RECONVERSION",
3372   "EM_SETIMEMODEBIAS",
3373   "EM_GETIMEMODEBIAS"
3374 };
3375 
3376 static const char *
3377 get_msg_name(UINT msg)
3378 {
3379   if (msg >= EM_GETSEL && msg <= EM_CHARFROMPOS)
3380     return edit_messages[msg - EM_GETSEL];
3381   if (msg >= EM_CANPASTE && msg <= EM_GETIMEMODEBIAS)
3382     return richedit_messages[msg - EM_CANPASTE];
3383   return "";
3384 }
3385 
3386 static void ME_LinkNotify(ME_TextEditor *editor, UINT msg, WPARAM wParam, LPARAM lParam)
3387 {
3388   int x,y;
3389   BOOL isExact;
3390   ME_Cursor cursor; /* The start of the clicked text. */
3391 
3392   ENLINK info;
3393   x = (short)LOWORD(lParam);
3394   y = (short)HIWORD(lParam);
3395   ME_CharFromPos(editor, x, y, &cursor, &isExact);
3396   if (!isExact) return;
3397 
3398   if (is_link( &cursor.pRun->member.run ))
3399   { /* The clicked run has CFE_LINK set */
3400     ME_DisplayItem *di;
3401 
3402     info.nmhdr.hwndFrom = NULL;
3403     info.nmhdr.idFrom = 0;
3404     info.nmhdr.code = EN_LINK;
3405     info.msg = msg;
3406     info.wParam = wParam;
3407     info.lParam = lParam;
3408     cursor.nOffset = 0;
3409 
3410     /* find the first contiguous run with CFE_LINK set */
3411     info.chrg.cpMin = ME_GetCursorOfs(&cursor);
3412     di = cursor.pRun;
3413     while (ME_PrevRun( NULL, &di, FALSE ) && is_link( &di->member.run ))
3414         info.chrg.cpMin -= di->member.run.len;
3415 
3416     /* find the last contiguous run with CFE_LINK set */
3417     info.chrg.cpMax = ME_GetCursorOfs(&cursor) + cursor.pRun->member.run.len;
3418     di = cursor.pRun;
3419     while (ME_NextRun( NULL, &di, FALSE ) && is_link( &di->member.run ))
3420         info.chrg.cpMax += di->member.run.len;
3421 
3422     ITextHost_TxNotify(editor->texthost, info.nmhdr.code, &info);
3423   }
3424 }
3425 
3426 void ME_ReplaceSel(ME_TextEditor *editor, BOOL can_undo, const WCHAR *str, int len)
3427 {
3428   int from, to, nStartCursor;
3429   ME_Style *style;
3430 
3431   nStartCursor = ME_GetSelectionOfs(editor, &from, &to);
3432   style = ME_GetSelectionInsertStyle(editor);
3433   ME_InternalDeleteText(editor, &editor->pCursors[nStartCursor], to-from, FALSE);
3434   ME_InsertTextFromCursor(editor, 0, str, len, style);
3435   ME_ReleaseStyle(style);
3436   /* drop temporary style if line end */
3437   /*
3438    * FIXME question: does abc\n mean: put abc,
3439    * clear temp style, put \n? (would require a change)
3440    */
3441   if (len>0 && str[len-1] == '\n')
3442     ME_ClearTempStyle(editor);
3443   ME_CommitUndo(editor);
3444   ME_UpdateSelectionLinkAttribute(editor);
3445   if (!can_undo)
3446     ME_EmptyUndoStack(editor);
3447   ME_UpdateRepaint(editor, FALSE);
3448 }
3449 
3450 static void ME_SetText(ME_TextEditor *editor, void *text, BOOL unicode)
3451 {
3452   LONG codepage = unicode ? CP_UNICODE : CP_ACP;
3453   int textLen;
3454 
3455   LPWSTR wszText = ME_ToUnicode(codepage, text, &textLen);
3456   ME_InsertTextFromCursor(editor, 0, wszText, textLen, editor->pBuffer->pDefaultStyle);
3457   ME_EndToUnicode(codepage, wszText);
3458 }
3459 
3460 static LRESULT ME_WmCreate(ME_TextEditor *editor, LPARAM lParam, BOOL unicode)
3461 {
3462   CREATESTRUCTW *createW = (CREATESTRUCTW*)lParam;
3463   CREATESTRUCTA *createA = (CREATESTRUCTA*)lParam;
3464   void *text = NULL;
3465   INT max;
3466 
3467   if (lParam)
3468     text = unicode ? (void*)createW->lpszName : (void*)createA->lpszName;
3469 
3470   ME_SetDefaultFormatRect(editor);
3471 
3472   max = (editor->styleFlags & ES_DISABLENOSCROLL) ? 1 : 0;
3473   if (~editor->styleFlags & ES_DISABLENOSCROLL || editor->styleFlags & WS_VSCROLL)
3474     ITextHost_TxSetScrollRange(editor->texthost, SB_VERT, 0, max, TRUE);
3475 
3476   if (~editor->styleFlags & ES_DISABLENOSCROLL || editor->styleFlags & WS_HSCROLL)
3477     ITextHost_TxSetScrollRange(editor->texthost, SB_HORZ, 0, max, TRUE);
3478 
3479   if (editor->styleFlags & ES_DISABLENOSCROLL)
3480   {
3481     if (editor->styleFlags & WS_VSCROLL)
3482     {
3483       ITextHost_TxEnableScrollBar(editor->texthost, SB_VERT, ESB_DISABLE_BOTH);
3484       ITextHost_TxShowScrollBar(editor->texthost, SB_VERT, TRUE);
3485     }
3486     if (editor->styleFlags & WS_HSCROLL)
3487     {
3488       ITextHost_TxEnableScrollBar(editor->texthost, SB_HORZ, ESB_DISABLE_BOTH);
3489       ITextHost_TxShowScrollBar(editor->texthost, SB_HORZ, TRUE);
3490     }
3491   }
3492 
3493   if (text)
3494   {
3495     ME_SetText(editor, text, unicode);
3496     ME_SetCursorToStart(editor, &editor->pCursors[0]);
3497     ME_SetCursorToStart(editor, &editor->pCursors[1]);
3498   }
3499 
3500   ME_CommitUndo(editor);
3501   ME_WrapMarkedParagraphs(editor);
3502   ME_MoveCaret(editor);
3503   return 0;
3504 }
3505 
3506 
3507 #define UNSUPPORTED_MSG(e) \
3508   case e:                  \
3509     FIXME(#e ": stub\n");  \
3510     *phresult = S_FALSE;   \
3511     return 0;
3512 
3513 /* Handle messages for windowless and windowed richedit controls.
3514  *
3515  * The LRESULT that is returned is a return value for window procs,
3516  * and the phresult parameter is the COM return code needed by the
3517  * text services interface. */
3518 LRESULT ME_HandleMessage(ME_TextEditor *editor, UINT msg, WPARAM wParam,
3519                          LPARAM lParam, BOOL unicode, HRESULT* phresult)
3520 {
3521   *phresult = S_OK;
3522 
3523   switch(msg) {
3524 
3525   UNSUPPORTED_MSG(EM_DISPLAYBAND)
3526   UNSUPPORTED_MSG(EM_FINDWORDBREAK)
3527   UNSUPPORTED_MSG(EM_FMTLINES)
3528   UNSUPPORTED_MSG(EM_FORMATRANGE)
3529   UNSUPPORTED_MSG(EM_GETBIDIOPTIONS)
3530   UNSUPPORTED_MSG(EM_GETEDITSTYLE)
3531   UNSUPPORTED_MSG(EM_GETIMECOMPMODE)
3532   UNSUPPORTED_MSG(EM_GETIMESTATUS)
3533   UNSUPPORTED_MSG(EM_SETIMESTATUS)
3534   UNSUPPORTED_MSG(EM_GETLANGOPTIONS)
3535   UNSUPPORTED_MSG(EM_GETREDONAME)
3536   UNSUPPORTED_MSG(EM_GETTYPOGRAPHYOPTIONS)
3537   UNSUPPORTED_MSG(EM_GETUNDONAME)
3538   UNSUPPORTED_MSG(EM_GETWORDBREAKPROCEX)
3539   UNSUPPORTED_MSG(EM_SETBIDIOPTIONS)
3540   UNSUPPORTED_MSG(EM_SETEDITSTYLE)
3541   UNSUPPORTED_MSG(EM_SETLANGOPTIONS)
3542   UNSUPPORTED_MSG(EM_SETMARGINS)
3543   UNSUPPORTED_MSG(EM_SETPALETTE)
3544   UNSUPPORTED_MSG(EM_SETTABSTOPS)
3545   UNSUPPORTED_MSG(EM_SETTYPOGRAPHYOPTIONS)
3546   UNSUPPORTED_MSG(EM_SETWORDBREAKPROCEX)
3547 
3548 /* Messages specific to Richedit controls */
3549 
3550   case EM_STREAMIN:
3551    return ME_StreamIn(editor, wParam, (EDITSTREAM*)lParam, TRUE);
3552   case EM_STREAMOUT:
3553    return ME_StreamOut(editor, wParam, (EDITSTREAM *)lParam);
3554   case WM_GETDLGCODE:
3555   {
3556     UINT code = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS;
3557 
3558     if (lParam)
3559       editor->bDialogMode = TRUE;
3560     if (editor->styleFlags & ES_MULTILINE)
3561       code |= DLGC_WANTMESSAGE;
3562     if (!(editor->styleFlags & ES_SAVESEL))
3563       code |= DLGC_HASSETSEL;
3564     return code;
3565   }
3566   case EM_EMPTYUNDOBUFFER:
3567     ME_EmptyUndoStack(editor);
3568     return 0;
3569   case EM_GETSEL:
3570   {
3571     /* Note: wParam/lParam can be NULL */
3572     UINT from, to;
3573     PUINT pfrom = wParam ? (PUINT)wParam : &from;
3574     PUINT pto = lParam ? (PUINT)lParam : &to;
3575     ME_GetSelectionOfs(editor, (int *)pfrom, (int *)pto);
3576     if ((*pfrom|*pto) & 0xFFFF0000)
3577       return -1;
3578     return MAKELONG(*pfrom,*pto);
3579   }
3580   case EM_EXGETSEL:
3581   {
3582     CHARRANGE *pRange = (CHARRANGE *)lParam;
3583     ME_GetSelectionOfs(editor, &pRange->cpMin, &pRange->cpMax);
3584     TRACE("EM_EXGETSEL = (%d,%d)\n", pRange->cpMin, pRange->cpMax);
3585     return 0;
3586   }
3587   case EM_SETUNDOLIMIT:
3588   {
3589     if ((int)wParam < 0)
3590       editor->nUndoLimit = STACK_SIZE_DEFAULT;
3591     else
3592       editor->nUndoLimit = min(wParam, STACK_SIZE_MAX);
3593     /* Setting a max stack size keeps wine from getting killed
3594       for hogging memory. Windows allocates all this memory at once, so
3595       no program would realistically set a value above our maximum. */
3596     return editor->nUndoLimit;
3597   }
3598   case EM_CANUNDO:
3599     return !list_empty( &editor->undo_stack );
3600   case EM_CANREDO:
3601     return !list_empty( &editor->redo_stack );
3602   case WM_UNDO: /* FIXME: actually not the same */
3603   case EM_UNDO:
3604     return ME_Undo(editor);
3605   case EM_REDO:
3606     return ME_Redo(editor);
3607   case EM_GETOPTIONS:
3608   {
3609     /* these flags are equivalent to the ES_* counterparts */
3610     DWORD mask = ECO_VERTICAL | ECO_AUTOHSCROLL | ECO_AUTOVSCROLL |
3611                  ECO_NOHIDESEL | ECO_READONLY | ECO_WANTRETURN | ECO_SELECTIONBAR;
3612     DWORD settings = editor->styleFlags & mask;
3613 
3614     return settings;
3615   }
3616   case EM_SETFONTSIZE:
3617   {
3618       CHARFORMAT2W cf;
3619       LONG tmp_size, size;
3620       BOOL is_increase = ((LONG)wParam > 0);
3621 
3622       if (editor->mode & TM_PLAINTEXT)
3623           return FALSE;
3624 
3625       cf.cbSize = sizeof(cf);
3626       cf.dwMask = CFM_SIZE;
3627       ME_GetSelectionCharFormat(editor, &cf);
3628       tmp_size = (cf.yHeight / 20) + wParam;
3629 
3630       if (tmp_size <= 1)
3631           size = 1;
3632       else if (tmp_size > 12 && tmp_size < 28 && tmp_size % 2)
3633           size = tmp_size + (is_increase ? 1 : -1);
3634       else if (tmp_size > 28 && tmp_size < 36)
3635           size = is_increase ? 36 : 28;
3636       else if (tmp_size > 36 && tmp_size < 48)
3637           size = is_increase ? 48 : 36;
3638       else if (tmp_size > 48 && tmp_size < 72)
3639           size = is_increase ? 72 : 48;
3640       else if (tmp_size > 72 && tmp_size < 80)
3641           size = is_increase ? 80 : 72;
3642       else if (tmp_size > 80 && tmp_size < 1638)
3643           size = 10 * (is_increase ? (tmp_size / 10 + 1) : (tmp_size / 10));
3644       else if (tmp_size >= 1638)
3645           size = 1638;
3646       else
3647           size = tmp_size;
3648 
3649       cf.yHeight = size * 20; /*  convert twips to points */
3650       ME_SetSelectionCharFormat(editor, &cf);
3651       ME_CommitUndo(editor);
3652       ME_WrapMarkedParagraphs(editor);
3653       ME_UpdateScrollBar(editor);
3654       ME_Repaint(editor);
3655 
3656       return TRUE;
3657   }
3658   case EM_SETOPTIONS:
3659   {
3660     /* these flags are equivalent to ES_* counterparts, except for
3661      * ECO_AUTOWORDSELECTION that doesn't have an ES_* counterpart,
3662      * but is still stored in editor->styleFlags. */
3663     const DWORD mask = ECO_VERTICAL | ECO_AUTOHSCROLL | ECO_AUTOVSCROLL |
3664                        ECO_NOHIDESEL | ECO_READONLY | ECO_WANTRETURN |
3665                        ECO_SELECTIONBAR | ECO_AUTOWORDSELECTION;
3666     DWORD settings = mask & editor->styleFlags;
3667     DWORD oldSettings = settings;
3668     DWORD changedSettings;
3669 
3670     switch(wParam)
3671     {
3672       case ECOOP_SET:
3673         settings = lParam;
3674         break;
3675       case ECOOP_OR:
3676         settings |= lParam;
3677         break;
3678       case ECOOP_AND:
3679         settings &= lParam;
3680         break;
3681       case ECOOP_XOR:
3682         settings ^= lParam;
3683     }
3684     changedSettings = oldSettings ^ settings;
3685 
3686     if (changedSettings) {
3687       editor->styleFlags = (editor->styleFlags & ~mask) | (settings & mask);
3688 
3689       if (changedSettings & ECO_SELECTIONBAR)
3690       {
3691         ITextHost_TxInvalidateRect(editor->texthost, &editor->rcFormat, TRUE);
3692         if (settings & ECO_SELECTIONBAR) {
3693           assert(!editor->selofs);
3694           editor->selofs = SELECTIONBAR_WIDTH;
3695           editor->rcFormat.left += editor->selofs;
3696         } else {
3697           editor->rcFormat.left -= editor->selofs;
3698           editor->selofs = 0;
3699         }
3700         ME_RewrapRepaint(editor);
3701       }
3702 
3703       if ((changedSettings & settings & ES_NOHIDESEL) && !editor->bHaveFocus)
3704           ME_InvalidateSelection( editor );
3705 
3706       if (changedSettings & settings & ECO_VERTICAL)
3707         FIXME("ECO_VERTICAL not implemented yet!\n");
3708       if (changedSettings & settings & ECO_AUTOHSCROLL)
3709         FIXME("ECO_AUTOHSCROLL not implemented yet!\n");
3710       if (changedSettings & settings & ECO_AUTOVSCROLL)
3711         FIXME("ECO_AUTOVSCROLL not implemented yet!\n");
3712       if (changedSettings & settings & ECO_WANTRETURN)
3713         FIXME("ECO_WANTRETURN not implemented yet!\n");
3714       if (changedSettings & settings & ECO_AUTOWORDSELECTION)
3715         FIXME("ECO_AUTOWORDSELECTION not implemented yet!\n");
3716     }
3717 
3718     return settings;
3719   }
3720   case EM_SETSEL:
3721   {
3722     return handle_EM_EXSETSEL( editor, wParam, lParam );
3723   }
3724   case EM_SETSCROLLPOS:
3725   {
3726     POINT *point = (POINT *)lParam;
3727     ME_ScrollAbs(editor, point->x, point->y);
3728     return 0;
3729   }
3730   case EM_AUTOURLDETECT:
3731   {
3732     if (wParam==1 || wParam ==0)
3733     {
3734         editor->AutoURLDetect_bEnable = (BOOL)wParam;
3735         return 0;
3736     }
3737     return E_INVALIDARG;
3738   }
3739   case EM_GETAUTOURLDETECT:
3740   {
3741     return editor->AutoURLDetect_bEnable;
3742   }
3743   case EM_EXSETSEL:
3744   {
3745     CHARRANGE range = *(CHARRANGE *)lParam;
3746 
3747     return handle_EM_EXSETSEL( editor, range.cpMin, range.cpMax );
3748   }
3749   case EM_SHOWSCROLLBAR:
3750   {
3751     DWORD flags;
3752 
3753     switch (wParam)
3754     {
3755       case SB_HORZ:
3756         flags = WS_HSCROLL;
3757         break;
3758       case SB_VERT:
3759         flags = WS_VSCROLL;
3760         break;
3761       case SB_BOTH:
3762         flags = WS_HSCROLL|WS_VSCROLL;
3763         break;
3764       default:
3765         return 0;
3766     }
3767 
3768     if (lParam) {
3769       editor->styleFlags |= flags;
3770       if (flags & WS_HSCROLL)
3771         ITextHost_TxShowScrollBar(editor->texthost, SB_HORZ,
3772                           editor->nTotalWidth > editor->sizeWindow.cx);
3773       if (flags & WS_VSCROLL)
3774         ITextHost_TxShowScrollBar(editor->texthost, SB_VERT,
3775                           editor->nTotalLength > editor->sizeWindow.cy);
3776     } else {
3777       editor->styleFlags &= ~flags;
3778       ITextHost_TxShowScrollBar(editor->texthost, wParam, FALSE);
3779     }
3780     return 0;
3781   }
3782   case EM_SETTEXTEX:
3783   {
3784     LPWSTR wszText;
3785     SETTEXTEX *pStruct = (SETTEXTEX *)wParam;
3786     int from, to, len;
3787     ME_Style *style;
3788     BOOL bRtf, bUnicode, bSelection, bUTF8;
3789     int oldModify = editor->nModifyStep;
3790     static const char utf8_bom[] = {0xef, 0xbb, 0xbf};
3791 
3792     if (!pStruct) return 0;
3793 
3794     /* If we detect ascii rtf at the start of the string,
3795      * we know it isn't unicode. */
3796     bRtf = (lParam && (!strncmp((char *)lParam, "{\\rtf", 5) ||
3797                          !strncmp((char *)lParam, "{\\urtf", 6)));
3798     bUnicode = !bRtf && pStruct->codepage == CP_UNICODE;
3799     bUTF8 = (lParam && (!strncmp((char *)lParam, utf8_bom, 3)));
3800 
3801     TRACE("EM_SETTEXTEX - %s, flags %d, cp %d\n",
3802           bUnicode ? debugstr_w((LPCWSTR)lParam) : debugstr_a((LPCSTR)lParam),
3803           pStruct->flags, pStruct->codepage);
3804 
3805     bSelection = (pStruct->flags & ST_SELECTION) != 0;
3806     if (bSelection) {
3807       int nStartCursor = ME_GetSelectionOfs(editor, &from, &to);
3808       style = ME_GetSelectionInsertStyle(editor);
3809       ME_InternalDeleteText(editor, &editor->pCursors[nStartCursor], to - from, FALSE);
3810     } else {
3811       ME_Cursor start;
3812       ME_SetCursorToStart(editor, &start);
3813       ME_InternalDeleteText(editor, &start, ME_GetTextLength(editor), FALSE);
3814       style = editor->pBuffer->pDefaultStyle;
3815     }
3816 
3817     if (bRtf) {
3818       ME_StreamInRTFString(editor, bSelection, (char *)lParam);
3819       if (bSelection) {
3820         /* FIXME: The length returned doesn't include the rtf control
3821          * characters, only the actual text. */
3822         len = lParam ? strlen((char *)lParam) : 0;
3823       }
3824     } else {
3825       if (bUTF8 && !bUnicode) {
3826         wszText = ME_ToUnicode(CP_UTF8, (void *)(lParam+3), &len);
3827         ME_InsertTextFromCursor(editor, 0, wszText, len, style);
3828         ME_EndToUnicode(CP_UTF8, wszText);
3829       } else {
3830         wszText = ME_ToUnicode(pStruct->codepage, (void *)lParam, &len);
3831         ME_InsertTextFromCursor(editor, 0, wszText, len, style);
3832         ME_EndToUnicode(pStruct->codepage, wszText);
3833       }
3834     }
3835 
3836     if (bSelection) {
3837       ME_ReleaseStyle(style);
3838       ME_UpdateSelectionLinkAttribute(editor);
3839     } else {
3840       ME_Cursor cursor;
3841       len = 1;
3842       ME_SetCursorToStart(editor, &cursor);
3843       ME_UpdateLinkAttribute(editor, &cursor, INT_MAX);
3844     }
3845     ME_CommitUndo(editor);
3846     if (!(pStruct->flags & ST_KEEPUNDO))
3847     {
3848       editor->nModifyStep = oldModify;
3849       ME_EmptyUndoStack(editor);
3850     }
3851     ME_UpdateRepaint(editor, FALSE);
3852     return len;
3853   }
3854   case EM_SELECTIONTYPE:
3855     return ME_GetSelectionType(editor);
3856   case EM_SETBKGNDCOLOR:
3857   {
3858     LRESULT lColor;
3859     if (editor->rgbBackColor != -1) {
3860       DeleteObject(editor->hbrBackground);
3861       lColor = editor->rgbBackColor;
3862     }
3863     else lColor = ITextHost_TxGetSysColor(editor->texthost, COLOR_WINDOW);
3864 
3865     if (wParam)
3866     {
3867       editor->rgbBackColor = -1;
3868       editor->hbrBackground = GetSysColorBrush(COLOR_WINDOW);
3869     }
3870     else
3871     {
3872       editor->rgbBackColor = lParam;
3873       editor->hbrBackground = CreateSolidBrush(editor->rgbBackColor);
3874     }
3875     ITextHost_TxInvalidateRect(editor->texthost, NULL, TRUE);
3876     return lColor;
3877   }
3878   case EM_GETMODIFY:
3879     return editor->nModifyStep == 0 ? 0 : -1;
3880   case EM_SETMODIFY:
3881   {
3882     if (wParam)
3883       editor->nModifyStep = 1;
3884     else
3885       editor->nModifyStep = 0;
3886 
3887     return 0;
3888   }
3889   case EM_SETREADONLY:
3890   {
3891     if (wParam)
3892       editor->styleFlags |= ES_READONLY;
3893     else
3894       editor->styleFlags &= ~ES_READONLY;
3895     return 1;
3896   }
3897   case EM_SETEVENTMASK:
3898   {
3899     DWORD nOldMask = editor->nEventMask;
3900 
3901     editor->nEventMask = lParam;
3902     return nOldMask;
3903   }
3904   case EM_GETEVENTMASK:
3905     return editor->nEventMask;
3906   case EM_SETCHARFORMAT:
3907   {
3908     CHARFORMAT2W buf, *p;
3909     BOOL bRepaint = TRUE;
3910     p = ME_ToCF2W(&buf, (CHARFORMAT2W *)lParam);
3911     if (p == NULL) return 0;
3912     if (wParam & SCF_ALL) {
3913       if (editor->mode & TM_PLAINTEXT) {
3914         ME_SetDefaultCharFormat(editor, p);
3915       } else {
3916         ME_Cursor start;
3917         ME_SetCursorToStart(editor, &start);
3918         ME_SetCharFormat(editor, &start, NULL, p);
3919         editor->nModifyStep = 1;
3920       }
3921     } else if (wParam & SCF_SELECTION) {
3922       if (editor->mode & TM_PLAINTEXT)
3923         return 0;
3924       if (wParam & SCF_WORD) {
3925         ME_Cursor start;
3926         ME_Cursor end = editor->pCursors[0];
3927         ME_MoveCursorWords(editor, &end, +1);
3928         start = end;
3929         ME_MoveCursorWords(editor, &start, -1);
3930         ME_SetCharFormat(editor, &start, &end, p);
3931       }
3932       bRepaint = ME_IsSelection(editor);
3933       ME_SetSelectionCharFormat(editor, p);
3934       if (bRepaint) editor->nModifyStep = 1;
3935     } else { /* SCF_DEFAULT */
3936       ME_SetDefaultCharFormat(editor, p);
3937     }
3938     ME_CommitUndo(editor);
3939     if (bRepaint)
3940     {
3941       ME_WrapMarkedParagraphs(editor);
3942       ME_UpdateScrollBar(editor);
3943       ME_Repaint(editor);
3944     }
3945     return 1;
3946   }
3947   case EM_GETCHARFORMAT:
3948   {
3949     CHARFORMAT2W tmp, *dst = (CHARFORMAT2W *)lParam;
3950     if (dst->cbSize != sizeof(CHARFORMATA) &&
3951         dst->cbSize != sizeof(CHARFORMATW) &&
3952         dst->cbSize != sizeof(CHARFORMAT2A) &&
3953         dst->cbSize != sizeof(CHARFORMAT2W))
3954       return 0;
3955     tmp.cbSize = sizeof(tmp);
3956     if (!wParam)
3957       ME_GetDefaultCharFormat(editor, &tmp);
3958     else
3959       ME_GetSelectionCharFormat(editor, &tmp);
3960     ME_CopyToCFAny(dst, &tmp);
3961     return tmp.dwMask;
3962   }
3963   case EM_SETPARAFORMAT:
3964   {
3965     BOOL result = ME_SetSelectionParaFormat(editor, (PARAFORMAT2 *)lParam);
3966     ME_WrapMarkedParagraphs(editor);
3967     ME_UpdateScrollBar(editor);
3968     ME_Repaint(editor);
3969     ME_CommitUndo(editor);
3970     return result;
3971   }
3972   case EM_GETPARAFORMAT:
3973     ME_GetSelectionParaFormat(editor, (PARAFORMAT2 *)lParam);
3974     return ((PARAFORMAT2 *)lParam)->dwMask;
3975   case EM_GETFIRSTVISIBLELINE:
3976   {
3977     ME_DisplayItem *p = editor->pBuffer->pFirst;
3978     int y = editor->vert_si.nPos;
3979     int ypara = 0;
3980     int count = 0;
3981     int ystart, yend;
3982     while(p) {
3983       p = ME_FindItemFwd(p, diStartRowOrParagraphOrEnd);
3984       if (p->type == diTextEnd)
3985         break;
3986       if (p->type == diParagraph) {
3987         ypara = p->member.para.pt.y;
3988         continue;
3989       }
3990       ystart = ypara + p->member.row.pt.y;
3991       yend = ystart + p->member.row.nHeight;
3992       if (y < yend) {
3993         break;
3994       }
3995       count++;
3996     }
3997     return count;
3998   }
3999   case EM_HIDESELECTION:
4000   {
4001      editor->bHideSelection = (wParam != 0);
4002      ME_InvalidateSelection(editor);
4003      return 0;
4004   }
4005   case EM_LINESCROLL:
4006   {
4007     if (!(editor->styleFlags & ES_MULTILINE))
4008       return FALSE;
4009     ME_ScrollDown( editor, lParam * get_default_line_height( editor ) );
4010     return TRUE;
4011   }
4012   case WM_CLEAR:
4013   {
4014     int from, to;
4015     int nStartCursor = ME_GetSelectionOfs(editor, &from, &to);
4016     ME_InternalDeleteText(editor, &editor->pCursors[nStartCursor], to-from, FALSE);
4017     ME_CommitUndo(editor);
4018     ME_UpdateRepaint(editor, TRUE);
4019     return 0;
4020   }
4021   case EM_REPLACESEL:
4022   {
4023     int len = 0;
4024     LONG codepage = unicode ? CP_UNICODE : CP_ACP;
4025     LPWSTR wszText = ME_ToUnicode(codepage, (void *)lParam, &len);
4026 
4027     TRACE("EM_REPLACESEL - %s\n", debugstr_w(wszText));
4028 
4029     ME_ReplaceSel(editor, !!wParam, wszText, len);
4030     ME_EndToUnicode(codepage, wszText);
4031     return len;
4032   }
4033   case EM_SCROLLCARET:
4034     ME_EnsureVisible(editor, &editor->pCursors[0]);
4035     return 0;
4036   case WM_SETFONT:
4037   {
4038     LOGFONTW lf;
4039     CHARFORMAT2W fmt;
4040     HDC hDC;
4041     BOOL bRepaint = LOWORD(lParam);
4042 
4043     if (!wParam)
4044       wParam = (WPARAM)GetStockObject(SYSTEM_FONT);
4045 
4046     if (!GetObjectW((HGDIOBJ)wParam, sizeof(LOGFONTW), &lf))
4047       return 0;
4048 
4049     hDC = ITextHost_TxGetDC(editor->texthost);
4050     ME_CharFormatFromLogFont(hDC, &lf, &fmt);
4051     ITextHost_TxReleaseDC(editor->texthost, hDC);
4052     if (editor->mode & TM_RICHTEXT) {
4053       ME_Cursor start;
4054       ME_SetCursorToStart(editor, &start);
4055       ME_SetCharFormat(editor, &start, NULL, &fmt);
4056     }
4057     ME_SetDefaultCharFormat(editor, &fmt);
4058 
4059     ME_CommitUndo(editor);
4060     ME_MarkAllForWrapping(editor);
4061     ME_WrapMarkedParagraphs(editor);
4062     ME_UpdateScrollBar(editor);
4063     if (bRepaint)
4064       ME_Repaint(editor);
4065     return 0;
4066   }
4067   case WM_SETTEXT:
4068   {
4069     ME_Cursor cursor;
4070     ME_SetCursorToStart(editor, &cursor);
4071     ME_InternalDeleteText(editor, &cursor, ME_GetTextLength(editor), FALSE);
4072     if (lParam)
4073     {
4074       TRACE("WM_SETTEXT lParam==%lx\n",lParam);
4075       if (!strncmp((char *)lParam, "{\\rtf", 5) ||
4076           !strncmp((char *)lParam, "{\\urtf", 6))
4077       {
4078         /* Undocumented: WM_SETTEXT supports RTF text */
4079         ME_StreamInRTFString(editor, 0, (char *)lParam);
4080       }
4081       else
4082         ME_SetText(editor, (void*)lParam, unicode);
4083     }
4084     else
4085       TRACE("WM_SETTEXT - NULL\n");
4086     ME_SetCursorToStart(editor, &cursor);
4087     ME_UpdateLinkAttribute(editor, &cursor, INT_MAX);
4088     ME_SetSelection(editor, 0, 0);
4089     editor->nModifyStep = 0;
4090     ME_CommitUndo(editor);
4091     ME_EmptyUndoStack(editor);
4092     ME_UpdateRepaint(editor, FALSE);
4093     return 1;
4094   }
4095   case EM_CANPASTE:
4096     return paste_special( editor, 0, NULL, TRUE );
4097   case WM_PASTE:
4098   case WM_MBUTTONDOWN:
4099     wParam = 0;
4100     lParam = 0;
4101     /* fall through */
4102   case EM_PASTESPECIAL:
4103     paste_special( editor, wParam, (REPASTESPECIAL *)lParam, FALSE );
4104     return 0;
4105   case WM_CUT:
4106   case WM_COPY:
4107     copy_or_cut(editor, msg == WM_CUT);
4108     return 0;
4109   case WM_GETTEXTLENGTH:
4110   {
4111     GETTEXTLENGTHEX how;
4112 
4113     /* CR/LF conversion required in 2.0 mode, verbatim in 1.0 mode */
4114     how.flags = GTL_CLOSE | (editor->bEmulateVersion10 ? 0 : GTL_USECRLF) | GTL_NUMCHARS;
4115     how.codepage = unicode ? CP_UNICODE : CP_ACP;
4116     return ME_GetTextLengthEx(editor, &how);
4117   }
4118   case EM_GETTEXTLENGTHEX:
4119     return ME_GetTextLengthEx(editor, (GETTEXTLENGTHEX *)wParam);
4120   case WM_GETTEXT:
4121   {
4122     GETTEXTEX ex;
4123     ex.cb = wParam * (unicode ? sizeof(WCHAR) : sizeof(CHAR));
4124     ex.flags = GT_USECRLF;
4125     ex.codepage = unicode ? CP_UNICODE : CP_ACP;
4126     ex.lpDefaultChar = NULL;
4127     ex.lpUsedDefChar = NULL;
4128     return ME_GetTextEx(editor, &ex, lParam);
4129   }
4130   case EM_GETTEXTEX:
4131     return ME_GetTextEx(editor, (GETTEXTEX*)wParam, lParam);
4132   case EM_GETSELTEXT:
4133   {
4134     int nFrom, nTo, nStartCur = ME_GetSelectionOfs(editor, &nFrom, &nTo);
4135     ME_Cursor *from = &editor->pCursors[nStartCur];
4136     return ME_GetTextRange(editor, (WCHAR *)lParam, from,
4137                            nTo - nFrom, unicode);
4138   }
4139   case EM_GETSCROLLPOS:
4140   {
4141     POINT *point = (POINT *)lParam;
4142     point->x = editor->horz_si.nPos;
4143     point->y = editor->vert_si.nPos;
4144     /* 16-bit scaled value is returned as stored in scrollinfo */
4145     if (editor->horz_si.nMax > 0xffff)
4146       point->x = MulDiv(point->x, 0xffff, editor->horz_si.nMax);
4147     if (editor->vert_si.nMax > 0xffff)
4148       point->y = MulDiv(point->y, 0xffff, editor->vert_si.nMax);
4149     return 1;
4150   }
4151   case EM_GETTEXTRANGE:
4152   {
4153     TEXTRANGEW *rng = (TEXTRANGEW *)lParam;
4154     ME_Cursor start;
4155     int nStart = rng->chrg.cpMin;
4156     int nEnd = rng->chrg.cpMax;
4157     int textlength = ME_GetTextLength(editor);
4158 
4159     TRACE("EM_GETTEXTRANGE min=%d max=%d unicode=%d textlength=%d\n",
4160           rng->chrg.cpMin, rng->chrg.cpMax, unicode, textlength);
4161     if (nStart < 0) return 0;
4162     if ((nStart == 0 && nEnd == -1) || nEnd > textlength)
4163       nEnd = textlength;
4164     if (nStart >= nEnd) return 0;
4165 
4166     ME_CursorFromCharOfs(editor, nStart, &start);
4167     return ME_GetTextRange(editor, rng->lpstrText, &start, nEnd - nStart, unicode);
4168   }
4169   case EM_GETLINE:
4170   {
4171     ME_DisplayItem *run;
4172     const unsigned int nMaxChars = *(WORD *) lParam;
4173     unsigned int nCharsLeft = nMaxChars;
4174     char *dest = (char *) lParam;
4175     BOOL wroteNull = FALSE;
4176 
4177     TRACE("EM_GETLINE: row=%d, nMaxChars=%d (%s)\n", (int) wParam, nMaxChars,
4178           unicode ? "Unicode" : "Ansi");
4179 
4180     run = ME_FindRowWithNumber(editor, wParam);
4181     if (run == NULL)
4182       return 0;
4183 
4184     while (nCharsLeft && (run = ME_FindItemFwd(run, diRunOrStartRow))
4185            && run->type == diRun)
4186     {
4187       WCHAR *str = get_text( &run->member.run, 0 );
4188       unsigned int nCopy;
4189 
4190       nCopy = min(nCharsLeft, run->member.run.len);
4191 
4192       if (unicode)
4193         memcpy(dest, str, nCopy * sizeof(WCHAR));
4194       else
4195         nCopy = WideCharToMultiByte(CP_ACP, 0, str, nCopy, dest,
4196                                     nCharsLeft, NULL, NULL);
4197       dest += nCopy * (unicode ? sizeof(WCHAR) : 1);
4198       nCharsLeft -= nCopy;
4199     }
4200 
4201     /* append line termination, space allowing */
4202     if (nCharsLeft > 0)
4203     {
4204       if (unicode)
4205         *((WCHAR *)dest) = '\0';
4206       else
4207         *dest = '\0';
4208       nCharsLeft--;
4209       wroteNull = TRUE;
4210     }
4211 
4212     TRACE("EM_GETLINE: got %u characters\n", nMaxChars - nCharsLeft);
4213     return nMaxChars - nCharsLeft - (wroteNull ? 1 : 0);
4214   }
4215   case EM_GETLINECOUNT:
4216   {
4217     ME_DisplayItem *item = editor->pBuffer->pFirst->next;
4218     int nRows = 0;
4219 
4220     ME_DisplayItem *prev_para = NULL, *last_para = NULL;
4221 
4222     while (item != editor->pBuffer->pLast)
4223     {
4224       assert(item->type == diParagraph);
4225       prev_para = ME_FindItemBack(item, diRun);
4226       if (prev_para) {
4227         assert(prev_para->member.run.nFlags & MERF_ENDPARA);
4228       }
4229       nRows += item->member.para.nRows;
4230       item = item->member.para.next_para;
4231     }
4232     last_para = ME_FindItemBack(item, diRun);
4233     assert(last_para);
4234     assert(last_para->member.run.nFlags & MERF_ENDPARA);
4235     if (editor->bEmulateVersion10 && prev_para &&
4236         last_para->member.run.nCharOfs == 0 &&
4237         prev_para->member.run.len == 1 &&
4238         *get_text( &prev_para->member.run, 0 ) == '\r')
4239     {
4240       /* In 1.0 emulation, the last solitary \r at the very end of the text
4241          (if one exists) is NOT a line break.
4242          FIXME: this is an ugly hack. This should have a more regular model. */
4243       nRows--;
4244     }
4245 
4246     TRACE("EM_GETLINECOUNT: nRows==%d\n", nRows);
4247     return max(1, nRows);
4248   }
4249   case EM_LINEFROMCHAR:
4250   {
4251     if (wParam == -1)
4252       return ME_RowNumberFromCharOfs(editor, ME_GetCursorOfs(&editor->pCursors[1]));
4253     else
4254       return ME_RowNumberFromCharOfs(editor, wParam);
4255   }
4256   case EM_EXLINEFROMCHAR:
4257   {
4258     if (lParam == -1)
4259       return ME_RowNumberFromCharOfs(editor, ME_GetCursorOfs(&editor->pCursors[1]));
4260     else
4261       return ME_RowNumberFromCharOfs(editor, lParam);
4262   }
4263   case EM_LINEINDEX:
4264   {
4265     ME_DisplayItem *item, *para;
4266     int nCharOfs;
4267 
4268     if (wParam == -1)
4269       item = ME_FindItemBack(editor->pCursors[0].pRun, diStartRow);
4270     else
4271       item = ME_FindRowWithNumber(editor, wParam);
4272     if (!item)
4273       return -1;
4274     para = ME_GetParagraph(item);
4275     item = ME_FindItemFwd(item, diRun);
4276     nCharOfs = para->member.para.nCharOfs + item->member.run.nCharOfs;
4277     TRACE("EM_LINEINDEX: nCharOfs==%d\n", nCharOfs);
4278     return nCharOfs;
4279   }
4280   case EM_LINELENGTH:
4281   {
4282     ME_DisplayItem *item, *item_end;
4283     int nChars = 0, nThisLineOfs = 0, nNextLineOfs = 0;
4284     ME_DisplayItem *para, *run;
4285 
4286     if (wParam > ME_GetTextLength(editor))
4287       return 0;
4288     if (wParam == -1)
4289     {
4290       FIXME("EM_LINELENGTH: returning number of unselected characters on lines with selection unsupported.\n");
4291       return 0;
4292     }
4293     ME_RunOfsFromCharOfs(editor, wParam, &para, &run, NULL);
4294     item = ME_RowStart(run);
4295     nThisLineOfs = ME_CharOfsFromRunOfs(editor, para, ME_FindItemFwd(item, diRun), 0);
4296     item_end = ME_FindItemFwd(item, diStartRowOrParagraphOrEnd);
4297     if (item_end->type == diStartRow) {
4298       nNextLineOfs = ME_CharOfsFromRunOfs(editor, para, ME_FindItemFwd(item_end, diRun), 0);
4299     } else {
4300       ME_DisplayItem *endRun = ME_FindItemBack(item_end, diRun);
4301       assert(endRun && endRun->member.run.nFlags & MERF_ENDPARA);
4302       nNextLineOfs = item_end->member.para.nCharOfs - endRun->member.run.len;
4303     }
4304     nChars = nNextLineOfs - nThisLineOfs;
4305     TRACE("EM_LINELENGTH(%ld)==%d\n",wParam, nChars);
4306     return nChars;
4307   }
4308   case EM_EXLIMITTEXT:
4309   {
4310     if ((int)lParam < 0)
4311      return 0;
4312     if (lParam == 0)
4313       editor->nTextLimit = 65536;
4314     else
4315       editor->nTextLimit = (int) lParam;
4316     return 0;
4317   }
4318   case EM_LIMITTEXT:
4319   {
4320     if (wParam == 0)
4321       editor->nTextLimit = 65536;
4322     else
4323       editor->nTextLimit = (int) wParam;
4324     return 0;
4325   }
4326   case EM_GETLIMITTEXT:
4327   {
4328     return editor->nTextLimit;
4329   }
4330   case EM_FINDTEXT:
4331   {
4332     LRESULT r;
4333     if(!unicode){
4334       FINDTEXTA *ft = (FINDTEXTA *)lParam;
4335       int nChars = MultiByteToWideChar(CP_ACP, 0, ft->lpstrText, -1, NULL, 0);
4336       WCHAR *tmp;
4337 
4338       if ((tmp = heap_alloc(nChars * sizeof(*tmp))) != NULL)
4339         MultiByteToWideChar(CP_ACP, 0, ft->lpstrText, -1, tmp, nChars);
4340       r = ME_FindText(editor, wParam, &ft->chrg, tmp, NULL);
4341       heap_free(tmp);
4342     }else{
4343       FINDTEXTW *ft = (FINDTEXTW *)lParam;
4344       r = ME_FindText(editor, wParam, &ft->chrg, ft->lpstrText, NULL);
4345     }
4346     return r;
4347   }
4348   case EM_FINDTEXTEX:
4349   {
4350     LRESULT r;
4351     if(!unicode){
4352       FINDTEXTEXA *ex = (FINDTEXTEXA *)lParam;
4353       int nChars = MultiByteToWideChar(CP_ACP, 0, ex->lpstrText, -1, NULL, 0);
4354       WCHAR *tmp;
4355 
4356       if ((tmp = heap_alloc(nChars * sizeof(*tmp))) != NULL)
4357         MultiByteToWideChar(CP_ACP, 0, ex->lpstrText, -1, tmp, nChars);
4358       r = ME_FindText(editor, wParam, &ex->chrg, tmp, &ex->chrgText);
4359       heap_free(tmp);
4360     }else{
4361       FINDTEXTEXW *ex = (FINDTEXTEXW *)lParam;
4362       r = ME_FindText(editor, wParam, &ex->chrg, ex->lpstrText, &ex->chrgText);
4363     }
4364     return r;
4365   }
4366   case EM_FINDTEXTW:
4367   {
4368     FINDTEXTW *ft = (FINDTEXTW *)lParam;
4369     return ME_FindText(editor, wParam, &ft->chrg, ft->lpstrText, NULL);
4370   }
4371   case EM_FINDTEXTEXW:
4372   {
4373     FINDTEXTEXW *ex = (FINDTEXTEXW *)lParam;
4374     return ME_FindText(editor, wParam, &ex->chrg, ex->lpstrText, &ex->chrgText);
4375   }
4376   case EM_GETZOOM:
4377     if (!wParam || !lParam)
4378       return FALSE;
4379     *(int *)wParam = editor->nZoomNumerator;
4380     *(int *)lParam = editor->nZoomDenominator;
4381     return TRUE;
4382   case EM_SETZOOM:
4383     return ME_SetZoom(editor, wParam, lParam);
4384   case EM_CHARFROMPOS:
4385   {
4386     ME_Cursor cursor;
4387     if (ME_CharFromPos(editor, ((POINTL *)lParam)->x, ((POINTL *)lParam)->y,
4388                        &cursor, NULL))
4389       return ME_GetCursorOfs(&cursor);
4390     else
4391       return -1;
4392   }
4393   case EM_POSFROMCHAR:
4394   {
4395     ME_DisplayItem *pPara, *pRun;
4396     int nCharOfs, nOffset, nLength;
4397     POINTL pt = {0,0};
4398 
4399     nCharOfs = wParam;
4400     /* detect which API version we're dealing with */
4401     if (wParam >= 0x40000)
4402         nCharOfs = lParam;
4403     nLength = ME_GetTextLength(editor);
4404     nCharOfs = min(nCharOfs, nLength);
4405     nCharOfs = max(nCharOfs, 0);
4406 
4407     ME_RunOfsFromCharOfs(editor, nCharOfs, &pPara, &pRun, &nOffset);
4408     assert(pRun->type == diRun);
4409     pt.y = pRun->member.run.pt.y;
4410     pt.x = pRun->member.run.pt.x + ME_PointFromChar(editor, &pRun->member.run, nOffset, TRUE);
4411     pt.y += pPara->member.para.pt.y + editor->rcFormat.top;
4412     pt.x += editor->rcFormat.left;
4413 
4414     pt.x -= editor->horz_si.nPos;
4415     pt.y -= editor->vert_si.nPos;
4416 
4417     if (wParam >= 0x40000) {
4418         *(POINTL *)wParam = pt;
4419     }
4420     return (wParam >= 0x40000) ? 0 : MAKELONG( pt.x, pt.y );
4421   }
4422   case WM_CREATE:
4423     return ME_WmCreate(editor, lParam, unicode);
4424   case WM_DESTROY:
4425     ME_DestroyEditor(editor);
4426     return 0;
4427   case WM_SETCURSOR:
4428   {
4429     POINT cursor_pos;
4430     if (wParam == (WPARAM)editor->hWnd && GetCursorPos(&cursor_pos) &&
4431         ScreenToClient(editor->hWnd, &cursor_pos))
4432       ME_LinkNotify(editor, msg, 0, MAKELPARAM(cursor_pos.x, cursor_pos.y));
4433     return ME_SetCursor(editor);
4434   }
4435   case WM_LBUTTONDBLCLK:
4436   case WM_LBUTTONDOWN:
4437   {
4438     ME_CommitUndo(editor); /* End coalesced undos for typed characters */
4439     if ((editor->nEventMask & ENM_MOUSEEVENTS) &&
4440         !ME_FilterEvent(editor, msg, &wParam, &lParam))
4441       return 0;
4442     ITextHost_TxSetFocus(editor->texthost);
4443     ME_LButtonDown(editor, (short)LOWORD(lParam), (short)HIWORD(lParam),
4444                    ME_CalculateClickCount(editor, msg, wParam, lParam));
4445     ITextHost_TxSetCapture(editor->texthost, TRUE);
4446     editor->bMouseCaptured = TRUE;
4447     ME_LinkNotify(editor, msg, wParam, lParam);
4448     if (!ME_SetCursor(editor)) goto do_default;
4449     break;
4450   }
4451   case WM_MOUSEMOVE:
4452     if ((editor->nEventMask & ENM_MOUSEEVENTS) &&
4453         !ME_FilterEvent(editor, msg, &wParam, &lParam))
4454       return 0;
4455     if (editor->bMouseCaptured)
4456       ME_MouseMove(editor, (short)LOWORD(lParam), (short)HIWORD(lParam));
4457     else
4458       ME_LinkNotify(editor, msg, wParam, lParam);
4459     /* Set cursor if mouse is captured, since WM_SETCURSOR won't be received. */
4460     if (editor->bMouseCaptured)
4461         ME_SetCursor(editor);
4462     break;
4463   case WM_LBUTTONUP:
4464     if (editor->bMouseCaptured) {
4465       ITextHost_TxSetCapture(editor->texthost, FALSE);
4466       editor->bMouseCaptured = FALSE;
4467     }
4468     if (editor->nSelectionType == stDocument)
4469       editor->nSelectionType = stPosition;
4470     if ((editor->nEventMask & ENM_MOUSEEVENTS) &&
4471         !ME_FilterEvent(editor, msg, &wParam, &lParam))
4472       return 0;
4473     else
4474     {
4475       ME_SetCursor(editor);
4476       ME_LinkNotify(editor, msg, wParam, lParam);
4477     }
4478     break;
4479   case WM_RBUTTONUP:
4480   case WM_RBUTTONDOWN:
4481   case WM_RBUTTONDBLCLK:
4482     ME_CommitUndo(editor); /* End coalesced undos for typed characters */
4483     if ((editor->nEventMask & ENM_MOUSEEVENTS) &&
4484         !ME_FilterEvent(editor, msg, &wParam, &lParam))
4485       return 0;
4486     ME_LinkNotify(editor, msg, wParam, lParam);
4487     goto do_default;
4488   case WM_CONTEXTMENU:
4489     if (!ME_ShowContextMenu(editor, (short)LOWORD(lParam), (short)HIWORD(lParam)))
4490       goto do_default;
4491     break;
4492   case WM_SETFOCUS:
4493     editor->bHaveFocus = TRUE;
4494     ME_ShowCaret(editor);
4495     ME_SendOldNotify(editor, EN_SETFOCUS);
4496     if (!editor->bHideSelection && !(editor->styleFlags & ES_NOHIDESEL))
4497         ME_InvalidateSelection( editor );
4498     return 0;
4499   case WM_KILLFOCUS:
4500     ME_CommitUndo(editor); /* End coalesced undos for typed characters */
4501     editor->bHaveFocus = FALSE;
4502     editor->wheel_remain = 0;
4503     ME_HideCaret(editor);
4504     ME_SendOldNotify(editor, EN_KILLFOCUS);
4505     if (!editor->bHideSelection && !(editor->styleFlags & ES_NOHIDESEL))
4506         ME_InvalidateSelection( editor );
4507     return 0;
4508   case WM_COMMAND:
4509     TRACE("editor wnd command = %d\n", LOWORD(wParam));
4510     return 0;
4511   case WM_KEYUP:
4512     if ((editor->nEventMask & ENM_KEYEVENTS) &&
4513         !ME_FilterEvent(editor, msg, &wParam, &lParam))
4514       return 0;
4515     goto do_default;
4516   case WM_KEYDOWN:
4517     if ((editor->nEventMask & ENM_KEYEVENTS) &&
4518         !ME_FilterEvent(editor, msg, &wParam, &lParam))
4519       return 0;
4520     if (ME_KeyDown(editor, LOWORD(wParam)))
4521       return 0;
4522     goto do_default;
4523   case WM_CHAR:
4524     if ((editor->nEventMask & ENM_KEYEVENTS) &&
4525         !ME_FilterEvent(editor, msg, &wParam, &lParam))
4526       return 0;
4527     return ME_Char(editor, wParam, lParam, unicode);
4528   case WM_UNICHAR:
4529     if (unicode)
4530     {
4531         if(wParam == UNICODE_NOCHAR) return TRUE;
4532         if(wParam <= 0x000fffff)
4533         {
4534             if(wParam > 0xffff) /* convert to surrogates */
4535             {
4536                 wParam -= 0x10000;
4537                 ME_Char(editor, (wParam >> 10) + 0xd800, 0, TRUE);
4538                 ME_Char(editor, (wParam & 0x03ff) + 0xdc00, 0, TRUE);
4539             } else {
4540               ME_Char(editor, wParam, 0, TRUE);
4541             }
4542         }
4543         return 0;
4544     }
4545     break;
4546   case EM_STOPGROUPTYPING:
4547     ME_CommitUndo(editor); /* End coalesced undos for typed characters */
4548     return 0;
4549   case WM_HSCROLL:
4550   {
4551     const int scrollUnit = 7;
4552 
4553     switch(LOWORD(wParam))
4554     {
4555       case SB_LEFT:
4556         ME_ScrollAbs(editor, 0, 0);
4557         break;
4558       case SB_RIGHT:
4559         ME_ScrollAbs(editor,
4560                      editor->horz_si.nMax - (int)editor->horz_si.nPage,
4561                      editor->vert_si.nMax - (int)editor->vert_si.nPage);
4562         break;
4563       case SB_LINELEFT:
4564         ME_ScrollLeft(editor, scrollUnit);
4565         break;
4566       case SB_LINERIGHT:
4567         ME_ScrollRight(editor, scrollUnit);
4568         break;
4569       case SB_PAGELEFT:
4570         ME_ScrollLeft(editor, editor->sizeWindow.cx);
4571         break;
4572       case SB_PAGERIGHT:
4573         ME_ScrollRight(editor, editor->sizeWindow.cx);
4574         break;
4575       case SB_THUMBTRACK:
4576       case SB_THUMBPOSITION:
4577       {
4578         int pos = HIWORD(wParam);
4579         if (editor->horz_si.nMax > 0xffff)
4580           pos = MulDiv(pos, editor->horz_si.nMax, 0xffff);
4581         ME_HScrollAbs(editor, pos);
4582         break;
4583       }
4584     }
4585     break;
4586   }
4587   case EM_SCROLL: /* fall through */
4588   case WM_VSCROLL:
4589   {
4590     int origNPos;
4591     int lineHeight = get_default_line_height( editor );
4592 
4593     origNPos = editor->vert_si.nPos;
4594 
4595     switch(LOWORD(wParam))
4596     {
4597       case SB_TOP:
4598         ME_ScrollAbs(editor, 0, 0);
4599         break;
4600       case SB_BOTTOM:
4601         ME_ScrollAbs(editor,
4602                      editor->horz_si.nMax - (int)editor->horz_si.nPage,
4603                      editor->vert_si.nMax - (int)editor->vert_si.nPage);
4604         break;
4605       case SB_LINEUP:
4606         ME_ScrollUp(editor,lineHeight);
4607         break;
4608       case SB_LINEDOWN:
4609         ME_ScrollDown(editor,lineHeight);
4610         break;
4611       case SB_PAGEUP:
4612         ME_ScrollUp(editor,editor->sizeWindow.cy);
4613         break;
4614       case SB_PAGEDOWN:
4615         ME_ScrollDown(editor,editor->sizeWindow.cy);
4616         break;
4617       case SB_THUMBTRACK:
4618       case SB_THUMBPOSITION:
4619       {
4620         int pos = HIWORD(wParam);
4621         if (editor->vert_si.nMax > 0xffff)
4622           pos = MulDiv(pos, editor->vert_si.nMax, 0xffff);
4623         ME_VScrollAbs(editor, pos);
4624         break;
4625       }
4626     }
4627     if (msg == EM_SCROLL)
4628       return 0x00010000 | (((editor->vert_si.nPos - origNPos)/lineHeight) & 0xffff);
4629     break;
4630   }
4631   case WM_MOUSEWHEEL:
4632   {
4633     int delta;
4634     BOOL ctrl_is_down;
4635 
4636     if ((editor->nEventMask & ENM_MOUSEEVENTS) &&
4637         !ME_FilterEvent(editor, msg, &wParam, &lParam))
4638       return 0;
4639 
4640     ctrl_is_down = GetKeyState(VK_CONTROL) & 0x8000;
4641 
4642     delta = GET_WHEEL_DELTA_WPARAM(wParam);
4643 
4644     /* if scrolling changes direction, ignore left overs */
4645     if ((delta < 0 && editor->wheel_remain < 0) ||
4646         (delta > 0 && editor->wheel_remain > 0))
4647       editor->wheel_remain += delta;
4648     else
4649       editor->wheel_remain = delta;
4650 
4651     if (editor->wheel_remain)
4652     {
4653       if (ctrl_is_down) {
4654         int numerator;
4655         if (!editor->nZoomNumerator || !editor->nZoomDenominator)
4656         {
4657           numerator = 100;
4658         } else {
4659           numerator = editor->nZoomNumerator * 100 / editor->nZoomDenominator;
4660         }
4661         numerator += calc_wheel_change( &editor->wheel_remain, 10 );
4662         if (numerator >= 10 && numerator <= 500)
4663           ME_SetZoom(editor, numerator, 100);
4664       } else {
4665         UINT max_lines = 3;
4666         int lines = 0;
4667 
4668         SystemParametersInfoW( SPI_GETWHEELSCROLLLINES, 0, &max_lines, 0 );
4669         if (max_lines)
4670           lines = calc_wheel_change( &editor->wheel_remain, (int)max_lines );
4671         if (lines)
4672           ME_ScrollDown( editor, -lines * get_default_line_height( editor ) );
4673       }
4674     }
4675     break;
4676   }
4677   case EM_GETRECT:
4678   {
4679     *((RECT *)lParam) = editor->rcFormat;
4680     if (editor->bDefaultFormatRect)
4681       ((RECT *)lParam)->left -= editor->selofs;
4682     return 0;
4683   }
4684   case EM_SETRECT:
4685   case EM_SETRECTNP:
4686   {
4687     if (lParam)
4688     {
4689       int border = 0;
4690       RECT clientRect;
4691       RECT *rc = (RECT *)lParam;
4692 
4693       border = editor->exStyleFlags & WS_EX_CLIENTEDGE ? 1 : 0;
4694       ITextHost_TxGetClientRect(editor->texthost, &clientRect);
4695       if (wParam == 0)
4696       {
4697         editor->rcFormat.top = max(0, rc->top - border);
4698         editor->rcFormat.left = max(0, rc->left - border);
4699         editor->rcFormat.bottom = min(clientRect.bottom, rc->bottom);
4700         editor->rcFormat.right = min(clientRect.right, rc->right + border);
4701       } else if (wParam == 1) {
4702         /* MSDN incorrectly says a wParam value of 1 causes the
4703          * lParam rect to be used as a relative offset,
4704          * however, the tests show it just prevents min/max bound
4705          * checking. */
4706         editor->rcFormat.top = rc->top - border;
4707         editor->rcFormat.left = rc->left - border;
4708         editor->rcFormat.bottom = rc->bottom;
4709         editor->rcFormat.right = rc->right + border;
4710       } else {
4711         return 0;
4712       }
4713       editor->bDefaultFormatRect = FALSE;
4714     }
4715     else
4716     {
4717       ME_SetDefaultFormatRect(editor);
4718       editor->bDefaultFormatRect = TRUE;
4719     }
4720     ME_MarkAllForWrapping(editor);
4721     ME_WrapMarkedParagraphs(editor);
4722     ME_UpdateScrollBar(editor);
4723     if (msg != EM_SETRECTNP)
4724       ME_Repaint(editor);
4725     return 0;
4726   }
4727   case EM_REQUESTRESIZE:
4728     ME_SendRequestResize(editor, TRUE);
4729     return 0;
4730   case WM_SETREDRAW:
4731     goto do_default;
4732   case WM_WINDOWPOSCHANGED:
4733   {
4734     RECT clientRect;
4735     WINDOWPOS *winpos = (WINDOWPOS *)lParam;
4736 
4737     if (winpos->flags & SWP_NOCLIENTSIZE) goto do_default;
4738     ITextHost_TxGetClientRect(editor->texthost, &clientRect);
4739     if (editor->bDefaultFormatRect) {
4740       ME_SetDefaultFormatRect(editor);
4741     } else {
4742       editor->rcFormat.right += clientRect.right - editor->prevClientRect.right;
4743       editor->rcFormat.bottom += clientRect.bottom - editor->prevClientRect.bottom;
4744     }
4745     editor->prevClientRect = clientRect;
4746     ME_RewrapRepaint(editor);
4747     goto do_default;
4748   }
4749   /* IME messages to make richedit controls IME aware */
4750   case WM_IME_SETCONTEXT:
4751   case WM_IME_CONTROL:
4752   case WM_IME_SELECT:
4753   case WM_IME_COMPOSITIONFULL:
4754     return 0;
4755   case WM_IME_STARTCOMPOSITION:
4756   {
4757     editor->imeStartIndex=ME_GetCursorOfs(&editor->pCursors[0]);
4758     ME_DeleteSelection(editor);
4759     ME_CommitUndo(editor);
4760     ME_UpdateRepaint(editor, FALSE);
4761     return 0;
4762   }
4763   case WM_IME_COMPOSITION:
4764   {
4765     HIMC hIMC;
4766 
4767     ME_Style *style = ME_GetInsertStyle(editor, 0);
4768     hIMC = ITextHost_TxImmGetContext(editor->texthost);
4769     ME_DeleteSelection(editor);
4770     ME_SaveTempStyle(editor, style);
4771     if (lParam & (GCS_RESULTSTR|GCS_COMPSTR))
4772     {
4773         LPWSTR lpCompStr = NULL;
4774         DWORD dwBufLen;
4775         DWORD dwIndex = lParam & GCS_RESULTSTR;
4776         if (!dwIndex)
4777           dwIndex = GCS_COMPSTR;
4778 
4779         dwBufLen = ImmGetCompositionStringW(hIMC, dwIndex, NULL, 0);
4780         lpCompStr = HeapAlloc(GetProcessHeap(),0,dwBufLen + sizeof(WCHAR));
4781         ImmGetCompositionStringW(hIMC, dwIndex, lpCompStr, dwBufLen);
4782         lpCompStr[dwBufLen/sizeof(WCHAR)] = 0;
4783         ME_InsertTextFromCursor(editor,0,lpCompStr,dwBufLen/sizeof(WCHAR),style);
4784         HeapFree(GetProcessHeap(), 0, lpCompStr);
4785 
4786         if (dwIndex == GCS_COMPSTR)
4787           ME_SetSelection(editor,editor->imeStartIndex,
4788                           editor->imeStartIndex + dwBufLen/sizeof(WCHAR));
4789     }
4790     ME_ReleaseStyle(style);
4791     ME_CommitUndo(editor);
4792     ME_UpdateRepaint(editor, FALSE);
4793     return 0;
4794   }
4795   case WM_IME_ENDCOMPOSITION:
4796   {
4797     ME_DeleteSelection(editor);
4798     editor->imeStartIndex=-1;
4799     return 0;
4800   }
4801   case EM_GETOLEINTERFACE:
4802   {
4803     if (!editor->reOle)
4804       if (!CreateIRichEditOle(NULL, editor, (LPVOID *)&editor->reOle))
4805         return 0;
4806     *(LPVOID *)lParam = editor->reOle;
4807     IRichEditOle_AddRef(editor->reOle);
4808     return 1;
4809   }
4810   case EM_GETPASSWORDCHAR:
4811   {
4812     return editor->cPasswordMask;
4813   }
4814   case EM_SETOLECALLBACK:
4815     if(editor->lpOleCallback)
4816       IRichEditOleCallback_Release(editor->lpOleCallback);
4817     editor->lpOleCallback = (IRichEditOleCallback*)lParam;
4818     if(editor->lpOleCallback)
4819       IRichEditOleCallback_AddRef(editor->lpOleCallback);
4820     return TRUE;
4821   case EM_GETWORDBREAKPROC:
4822     return (LRESULT)editor->pfnWordBreak;
4823   case EM_SETWORDBREAKPROC:
4824   {
4825     EDITWORDBREAKPROCW pfnOld = editor->pfnWordBreak;
4826 
4827     editor->pfnWordBreak = (EDITWORDBREAKPROCW)lParam;
4828     return (LRESULT)pfnOld;
4829   }
4830   case EM_GETTEXTMODE:
4831     return editor->mode;
4832   case EM_SETTEXTMODE:
4833   {
4834     int mask = 0;
4835     int changes = 0;
4836 
4837     if (ME_GetTextLength(editor) ||
4838         !list_empty( &editor->undo_stack ) || !list_empty( &editor->redo_stack ))
4839       return E_UNEXPECTED;
4840 
4841     /* Check for mutually exclusive flags in adjacent bits of wParam */
4842     if ((wParam & (TM_RICHTEXT | TM_MULTILEVELUNDO | TM_MULTICODEPAGE)) &
4843         (wParam & (TM_PLAINTEXT | TM_SINGLELEVELUNDO | TM_SINGLECODEPAGE)) << 1)
4844       return E_INVALIDARG;
4845 
4846     if (wParam & (TM_RICHTEXT | TM_PLAINTEXT))
4847     {
4848       mask |= TM_RICHTEXT | TM_PLAINTEXT;
4849       changes |= wParam & (TM_RICHTEXT | TM_PLAINTEXT);
4850       if (wParam & TM_PLAINTEXT) {
4851         /* Clear selection since it should be possible to select the
4852          * end of text run for rich text */
4853         ME_InvalidateSelection(editor);
4854         ME_SetCursorToStart(editor, &editor->pCursors[0]);
4855         editor->pCursors[1] = editor->pCursors[0];
4856         /* plain text can only have the default style. */
4857         ME_ClearTempStyle(editor);
4858         ME_AddRefStyle(editor->pBuffer->pDefaultStyle);
4859         ME_ReleaseStyle(editor->pCursors[0].pRun->member.run.style);
4860         editor->pCursors[0].pRun->member.run.style = editor->pBuffer->pDefaultStyle;
4861       }
4862     }
4863     /* FIXME: Currently no support for undo level and code page options */
4864     editor->mode = (editor->mode & ~mask) | changes;
4865     return 0;
4866   }
4867   case EM_SETPASSWORDCHAR:
4868   {
4869     editor->cPasswordMask = wParam;
4870     ME_RewrapRepaint(editor);
4871     return 0;
4872   }
4873   case EM_SETTARGETDEVICE:
4874     if (wParam == 0)
4875     {
4876       BOOL new = (lParam == 0 && (editor->styleFlags & ES_MULTILINE));
4877       if (editor->nAvailWidth || editor->bWordWrap != new)
4878       {
4879         editor->bWordWrap = new;
4880         editor->nAvailWidth = 0; /* wrap to client area */
4881         ME_RewrapRepaint(editor);
4882       }
4883     } else {
4884       int width = max(0, lParam);
4885       if ((editor->styleFlags & ES_MULTILINE) &&
4886           (!editor->bWordWrap || editor->nAvailWidth != width))
4887       {
4888         editor->nAvailWidth = width;
4889         editor->bWordWrap = TRUE;
4890         ME_RewrapRepaint(editor);
4891       }
4892       FIXME("EM_SETTARGETDEVICE doesn't use non-NULL target devices\n");
4893     }
4894     return TRUE;
4895   default:
4896   do_default:
4897     *phresult = S_FALSE;
4898     break;
4899   }
4900   return 0L;
4901 }
4902 
4903 static BOOL create_windowed_editor(HWND hwnd, CREATESTRUCTW *create, BOOL emulate_10)
4904 {
4905     ITextHost *host = ME_CreateTextHost( hwnd, create, emulate_10 );
4906     ME_TextEditor *editor;
4907 
4908     if (!host) return FALSE;
4909 
4910     editor = ME_MakeEditor( host, emulate_10 );
4911     if (!editor)
4912     {
4913         ITextHost_Release( host );
4914         return FALSE;
4915     }
4916 
4917     editor->exStyleFlags = GetWindowLongW( hwnd, GWL_EXSTYLE );
4918     editor->styleFlags |= GetWindowLongW( hwnd, GWL_STYLE ) & ES_WANTRETURN;
4919     editor->hWnd = hwnd; /* FIXME: Remove editor's dependence on hWnd */
4920     editor->hwndParent = create->hwndParent;
4921 
4922     SetWindowLongPtrW( hwnd, 0, (LONG_PTR)editor );
4923 
4924     return TRUE;
4925 }
4926 
4927 static LRESULT RichEditWndProc_common(HWND hWnd, UINT msg, WPARAM wParam,
4928                                       LPARAM lParam, BOOL unicode)
4929 {
4930   ME_TextEditor *editor;
4931   HRESULT hresult;
4932   LRESULT lresult = 0;
4933 
4934   TRACE("enter hwnd %p msg %04x (%s) %lx %lx, unicode %d\n",
4935         hWnd, msg, get_msg_name(msg), wParam, lParam, unicode);
4936 
4937   editor = (ME_TextEditor *)GetWindowLongPtrW(hWnd, 0);
4938   if (!editor)
4939   {
4940     if (msg == WM_NCCREATE)
4941     {
4942       CREATESTRUCTW *pcs = (CREATESTRUCTW *)lParam;
4943 
4944       TRACE("WM_NCCREATE: hWnd %p style 0x%08x\n", hWnd, pcs->style);
4945       return create_windowed_editor( hWnd, pcs, FALSE );
4946     }
4947     else
4948     {
4949       return DefWindowProcW(hWnd, msg, wParam, lParam);
4950     }
4951   }
4952 
4953   switch (msg)
4954   {
4955     case WM_PAINT:
4956     {
4957       HDC hDC;
4958       RECT rc;
4959       PAINTSTRUCT ps;
4960 
4961       hDC = BeginPaint(editor->hWnd, &ps);
4962       if (!editor->bEmulateVersion10 || (editor->nEventMask & ENM_UPDATE))
4963         ME_SendOldNotify(editor, EN_UPDATE);
4964       /* Erase area outside of the formatting rectangle */
4965       if (ps.rcPaint.top < editor->rcFormat.top)
4966       {
4967         rc = ps.rcPaint;
4968         rc.bottom = editor->rcFormat.top;
4969         FillRect(hDC, &rc, editor->hbrBackground);
4970         ps.rcPaint.top = editor->rcFormat.top;
4971       }
4972       if (ps.rcPaint.bottom > editor->rcFormat.bottom) {
4973         rc = ps.rcPaint;
4974         rc.top = editor->rcFormat.bottom;
4975         FillRect(hDC, &rc, editor->hbrBackground);
4976         ps.rcPaint.bottom = editor->rcFormat.bottom;
4977       }
4978       if (ps.rcPaint.left < editor->rcFormat.left) {
4979         rc = ps.rcPaint;
4980         rc.right = editor->rcFormat.left;
4981         FillRect(hDC, &rc, editor->hbrBackground);
4982         ps.rcPaint.left = editor->rcFormat.left;
4983       }
4984       if (ps.rcPaint.right > editor->rcFormat.right) {
4985         rc = ps.rcPaint;
4986         rc.left = editor->rcFormat.right;
4987         FillRect(hDC, &rc, editor->hbrBackground);
4988         ps.rcPaint.right = editor->rcFormat.right;
4989       }
4990 
4991       ME_PaintContent(editor, hDC, &ps.rcPaint);
4992       EndPaint(editor->hWnd, &ps);
4993       return 0;
4994     }
4995     case WM_ERASEBKGND:
4996     {
4997       HDC hDC = (HDC)wParam;
4998       RECT rc;
4999 
5000       if (GetUpdateRect(editor->hWnd, &rc, TRUE))
5001         FillRect(hDC, &rc, editor->hbrBackground);
5002       return 1;
5003     }
5004     case EM_SETOPTIONS:
5005     {
5006       DWORD dwStyle;
5007       const DWORD mask = ECO_VERTICAL | ECO_AUTOHSCROLL | ECO_AUTOVSCROLL |
5008                          ECO_NOHIDESEL | ECO_READONLY | ECO_WANTRETURN |
5009                          ECO_SELECTIONBAR;
5010       lresult = ME_HandleMessage(editor, msg, wParam, lParam, unicode, &hresult);
5011       dwStyle = GetWindowLongW(hWnd, GWL_STYLE);
5012       dwStyle = (dwStyle & ~mask) | (lresult & mask);
5013       SetWindowLongW(hWnd, GWL_STYLE, dwStyle);
5014       return lresult;
5015     }
5016     case EM_SETREADONLY:
5017     {
5018       DWORD dwStyle;
5019       lresult = ME_HandleMessage(editor, msg, wParam, lParam, unicode, &hresult);
5020       dwStyle = GetWindowLongW(hWnd, GWL_STYLE);
5021       dwStyle &= ~ES_READONLY;
5022       if (wParam)
5023         dwStyle |= ES_READONLY;
5024       SetWindowLongW(hWnd, GWL_STYLE, dwStyle);
5025       return lresult;
5026     }
5027     default:
5028       lresult = ME_HandleMessage(editor, msg, wParam, lParam, unicode, &hresult);
5029   }
5030 
5031   if (hresult == S_FALSE)
5032     lresult = DefWindowProcW(hWnd, msg, wParam, lParam);
5033 
5034   TRACE("exit hwnd %p msg %04x (%s) %lx %lx, unicode %d -> %lu\n",
5035         hWnd, msg, get_msg_name(msg), wParam, lParam, unicode, lresult);
5036 
5037   return lresult;
5038 }
5039 
5040 static LRESULT WINAPI RichEditWndProcW(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
5041 {
5042     BOOL unicode = TRUE;
5043 
5044     /* Under Win9x RichEdit20W returns ANSI strings, see the tests. */
5045     if (msg == WM_GETTEXT && (GetVersion() & 0x80000000))
5046         unicode = FALSE;
5047 
5048     return RichEditWndProc_common(hWnd, msg, wParam, lParam, unicode);
5049 }
5050 
5051 static LRESULT WINAPI RichEditWndProcA(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
5052 {
5053     return RichEditWndProc_common(hWnd, msg, wParam, lParam, FALSE);
5054 }
5055 
5056 /******************************************************************
5057  *        RichEditANSIWndProc (RICHED20.10)
5058  */
5059 LRESULT WINAPI RichEditANSIWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
5060 {
5061     return RichEditWndProcA(hWnd, msg, wParam, lParam);
5062 }
5063 
5064 /******************************************************************
5065  *        RichEdit10ANSIWndProc (RICHED20.9)
5066  */
5067 LRESULT WINAPI RichEdit10ANSIWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
5068 {
5069   if (msg == WM_NCCREATE && !GetWindowLongPtrW(hWnd, 0))
5070   {
5071     CREATESTRUCTW *pcs = (CREATESTRUCTW *)lParam;
5072 
5073     TRACE("WM_NCCREATE: hWnd %p style 0x%08x\n", hWnd, pcs->style);
5074     return create_windowed_editor( hWnd, pcs, TRUE );
5075   }
5076   return RichEditANSIWndProc(hWnd, msg, wParam, lParam);
5077 }
5078 
5079 void ME_SendOldNotify(ME_TextEditor *editor, int nCode)
5080 {
5081   ITextHost_TxNotify(editor->texthost, nCode, NULL);
5082 }
5083 
5084 /* Fill buffer with srcChars unicode characters from the start cursor.
5085  *
5086  * buffer: destination buffer
5087  * buflen: length of buffer in characters excluding the NULL terminator.
5088  * start: start of editor text to copy into buffer.
5089  * srcChars: Number of characters to use from the editor text.
5090  * bCRLF: if true, replaces all end of lines with \r\n pairs.
5091  *
5092  * returns the number of characters written excluding the NULL terminator.
5093  *
5094  * The written text is always NULL terminated.
5095  */
5096 int ME_GetTextW(ME_TextEditor *editor, WCHAR *buffer, int buflen,
5097                 const ME_Cursor *start, int srcChars, BOOL bCRLF,
5098                 BOOL bEOP)
5099 {
5100   ME_DisplayItem *pRun, *pNextRun;
5101   const WCHAR *pStart = buffer;
5102   const WCHAR cr_lf[] = {'\r', '\n', 0};
5103   const WCHAR *str;
5104   int nLen;
5105 
5106   /* bCRLF flag is only honored in 2.0 and up. 1.0 must always return text verbatim */
5107   if (editor->bEmulateVersion10) bCRLF = FALSE;
5108 
5109   pRun = start->pRun;
5110   assert(pRun);
5111   pNextRun = ME_FindItemFwd(pRun, diRun);
5112 
5113   nLen = pRun->member.run.len - start->nOffset;
5114   str = get_text( &pRun->member.run, start->nOffset );
5115 
5116   while (srcChars && buflen && pNextRun)
5117   {
5118     int nFlags = pRun->member.run.nFlags;
5119 
5120     if (bCRLF && nFlags & MERF_ENDPARA && ~nFlags & MERF_ENDCELL)
5121     {
5122       if (buflen == 1) break;
5123       /* FIXME: native fails to reduce srcChars here for WM_GETTEXT or
5124        *        EM_GETTEXTEX, however, this is done for copying text which
5125        *        also uses this function. */
5126       srcChars -= min(nLen, srcChars);
5127       nLen = 2;
5128       str = cr_lf;
5129     } else {
5130       nLen = min(nLen, srcChars);
5131       srcChars -= nLen;
5132     }
5133 
5134     nLen = min(nLen, buflen);
5135     buflen -= nLen;
5136 
5137     CopyMemory(buffer, str, sizeof(WCHAR) * nLen);
5138 
5139     buffer += nLen;
5140 
5141     pRun = pNextRun;
5142     pNextRun = ME_FindItemFwd(pRun, diRun);
5143 
5144     nLen = pRun->member.run.len;
5145     str = get_text( &pRun->member.run, 0 );
5146   }
5147   /* append '\r' to the last paragraph. */
5148   if (pRun->next->type == diTextEnd && bEOP)
5149   {
5150     *buffer = '\r';
5151     buffer ++;
5152   }
5153   *buffer = 0;
5154   return buffer - pStart;
5155 }
5156 
5157 static BOOL ME_RegisterEditorClass(HINSTANCE hInstance)
5158 {
5159   WNDCLASSW wcW;
5160   WNDCLASSA wcA;
5161 
5162   wcW.style = CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW | CS_GLOBALCLASS;
5163   wcW.lpfnWndProc = RichEditWndProcW;
5164   wcW.cbClsExtra = 0;
5165   wcW.cbWndExtra = sizeof(ME_TextEditor *);
5166   wcW.hInstance = NULL; /* hInstance would register DLL-local class */
5167   wcW.hIcon = NULL;
5168   wcW.hCursor = LoadCursorW(NULL, (LPWSTR)IDC_IBEAM);
5169   wcW.hbrBackground = GetStockObject(NULL_BRUSH);
5170   wcW.lpszMenuName = NULL;
5171 
5172   if (is_version_nt())
5173   {
5174     wcW.lpszClassName = RICHEDIT_CLASS20W;
5175     if (!RegisterClassW(&wcW)) return FALSE;
5176     wcW.lpszClassName = MSFTEDIT_CLASS;
5177     if (!RegisterClassW(&wcW)) return FALSE;
5178   }
5179   else
5180   {
5181     /* WNDCLASSA/W have the same layout */
5182     wcW.lpszClassName = (LPCWSTR)"RichEdit20W";
5183     if (!RegisterClassA((WNDCLASSA *)&wcW)) return FALSE;
5184     wcW.lpszClassName = (LPCWSTR)"RichEdit50W";
5185     if (!RegisterClassA((WNDCLASSA *)&wcW)) return FALSE;
5186   }
5187 
5188   wcA.style = CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW | CS_GLOBALCLASS;
5189   wcA.lpfnWndProc = RichEditWndProcA;
5190   wcA.cbClsExtra = 0;
5191   wcA.cbWndExtra = sizeof(ME_TextEditor *);
5192   wcA.hInstance = NULL; /* hInstance would register DLL-local class */
5193   wcA.hIcon = NULL;
5194   wcA.hCursor = LoadCursorW(NULL, (LPWSTR)IDC_IBEAM);
5195   wcA.hbrBackground = GetStockObject(NULL_BRUSH);
5196   wcA.lpszMenuName = NULL;
5197   wcA.lpszClassName = RICHEDIT_CLASS20A;
5198   if (!RegisterClassA(&wcA)) return FALSE;
5199   wcA.lpszClassName = "RichEdit50A";
5200   if (!RegisterClassA(&wcA)) return FALSE;
5201 
5202   return TRUE;
5203 }
5204 
5205 static LRESULT WINAPI REComboWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) {
5206   /* FIXME: Not implemented */
5207   TRACE("hWnd %p msg %04x (%s) %08lx %08lx\n",
5208         hWnd, msg, get_msg_name(msg), wParam, lParam);
5209   return DefWindowProcW(hWnd, msg, wParam, lParam);
5210 }
5211 
5212 static LRESULT WINAPI REListWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) {
5213   /* FIXME: Not implemented */
5214   TRACE("hWnd %p msg %04x (%s) %08lx %08lx\n",
5215         hWnd, msg, get_msg_name(msg), wParam, lParam);
5216   return DefWindowProcW(hWnd, msg, wParam, lParam);
5217 }
5218 
5219 /******************************************************************
5220  *        REExtendedRegisterClass (RICHED20.8)
5221  *
5222  * FIXME undocumented
5223  * Need to check for errors and implement controls and callbacks
5224  */
5225 LRESULT WINAPI REExtendedRegisterClass(void)
5226 {
5227   WNDCLASSW wcW;
5228   UINT result;
5229 
5230   FIXME("semi stub\n");
5231 
5232   wcW.cbClsExtra = 0;
5233   wcW.cbWndExtra = 4;
5234   wcW.hInstance = NULL;
5235   wcW.hIcon = NULL;
5236   wcW.hCursor = NULL;
5237   wcW.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
5238   wcW.lpszMenuName = NULL;
5239 
5240   if (!ME_ListBoxRegistered)
5241   {
5242       wcW.style = CS_PARENTDC | CS_DBLCLKS | CS_GLOBALCLASS;
5243       wcW.lpfnWndProc = REListWndProc;
5244       wcW.lpszClassName = REListBox20W;
5245       if (RegisterClassW(&wcW)) ME_ListBoxRegistered = TRUE;
5246   }
5247 
5248   if (!ME_ComboBoxRegistered)
5249   {
5250       wcW.style = CS_PARENTDC | CS_DBLCLKS | CS_GLOBALCLASS | CS_VREDRAW | CS_HREDRAW;
5251       wcW.lpfnWndProc = REComboWndProc;
5252       wcW.lpszClassName = REComboBox20W;
5253       if (RegisterClassW(&wcW)) ME_ComboBoxRegistered = TRUE;
5254   }
5255 
5256   result = 0;
5257   if (ME_ListBoxRegistered)
5258       result += 1;
5259   if (ME_ComboBoxRegistered)
5260       result += 2;
5261 
5262   return result;
5263 }
5264 
5265 static int wchar_comp( const void *key, const void *elem )
5266 {
5267     return *(const WCHAR *)key - *(const WCHAR *)elem;
5268 }
5269 
5270 /* neutral characters end the url if the next non-neutral character is a space character,
5271    otherwise they are included in the url. */
5272 static BOOL isurlneutral( WCHAR c )
5273 {
5274     /* NB this list is sorted */
5275     static const WCHAR neutral_chars[] = {'!','\"','\'','(',')',',','-','.',':',';','<','>','?','[',']','{','}'};
5276 
5277     /* Some shortcuts */
5278     if (isalnum( c )) return FALSE;
5279     if (c > neutral_chars[sizeof(neutral_chars) / sizeof(neutral_chars[0]) - 1]) return FALSE;
5280 
5281     return !!bsearch( &c, neutral_chars, sizeof(neutral_chars) / sizeof(neutral_chars[0]),
5282                       sizeof(c), wchar_comp );
5283 }
5284 
5285 /**
5286  * This proc takes a selection, and scans it forward in order to select the span
5287  * of a possible URL candidate. A possible URL candidate must start with isalnum
5288  * or one of the following special characters: *|/\+%#@ and must consist entirely
5289  * of the characters allowed to start the URL, plus : (colon) which may occur
5290  * at most once, and not at either end.
5291  */
5292 static BOOL ME_FindNextURLCandidate(ME_TextEditor *editor,
5293                                     const ME_Cursor *start,
5294                                     int nChars,
5295                                     ME_Cursor *candidate_min,
5296                                     ME_Cursor *candidate_max)
5297 {
5298   ME_Cursor cursor = *start, neutral_end, space_end;
5299   BOOL candidateStarted = FALSE, quoted = FALSE;
5300   WCHAR c;
5301 
5302   while (nChars > 0)
5303   {
5304     WCHAR *str = get_text( &cursor.pRun->member.run, 0 );
5305     int run_len = cursor.pRun->member.run.len;
5306 
5307     nChars -= run_len - cursor.nOffset;
5308 
5309     /* Find start of candidate */
5310     if (!candidateStarted)
5311     {
5312       while (cursor.nOffset < run_len)
5313       {
5314         c = str[cursor.nOffset];
5315         if (!isspaceW( c ) && !isurlneutral( c ))
5316         {
5317           *candidate_min = cursor;
5318           candidateStarted = TRUE;
5319           neutral_end.pPara = NULL;
5320           space_end.pPara = NULL;
5321           cursor.nOffset++;
5322           break;
5323         }
5324         quoted = (c == '<');
5325         cursor.nOffset++;
5326       }
5327     }
5328 
5329     /* Find end of candidate */
5330     if (candidateStarted)
5331     {
5332       while (cursor.nOffset < run_len)
5333       {
5334         c = str[cursor.nOffset];
5335         if (isspaceW( c ))
5336         {
5337           if (quoted && c != '\r')
5338           {
5339             if (!space_end.pPara)
5340             {
5341               if (neutral_end.pPara)
5342                 space_end = neutral_end;
5343               else
5344                 space_end = cursor;
5345             }
5346           }
5347           else
5348             goto done;
5349         }
5350         else if (isurlneutral( c ))
5351         {
5352           if (quoted && c == '>')
5353           {
5354             neutral_end.pPara = NULL;
5355             space_end.pPara = NULL;
5356             goto done;
5357           }
5358           if (!neutral_end.pPara)
5359             neutral_end = cursor;
5360         }
5361         else
5362           neutral_end.pPara = NULL;
5363 
5364         cursor.nOffset++;
5365       }
5366     }
5367 
5368     cursor.nOffset = 0;
5369     if (!ME_NextRun(&cursor.pPara, &cursor.pRun, TRUE))
5370       goto done;
5371   }
5372 
5373 done:
5374   if (candidateStarted)
5375   {
5376     if (space_end.pPara)
5377       *candidate_max = space_end;
5378     else if (neutral_end.pPara)
5379       *candidate_max = neutral_end;
5380     else
5381       *candidate_max = cursor;
5382     return TRUE;
5383   }
5384   *candidate_max = *candidate_min = cursor;
5385   return FALSE;
5386 }
5387 
5388 /**
5389  * This proc evaluates the selection and returns TRUE if it can be considered an URL
5390  */
5391 static BOOL ME_IsCandidateAnURL(ME_TextEditor *editor, const ME_Cursor *start, int nChars)
5392 {
5393 #define MAX_PREFIX_LEN 9
5394   struct prefix_s {
5395     const WCHAR text[MAX_PREFIX_LEN];
5396     int length;
5397   }prefixes[] = {
5398     {{'p','r','o','s','p','e','r','o',':'}, 9},
5399     {{'t','e','l','n','e','t',':'}, 7},
5400     {{'g','o','p','h','e','r',':'}, 7},
5401     {{'m','a','i','l','t','o',':'}, 7},
5402     {{'h','t','t','p','s',':'}, 6},
5403     {{'f','i','l','e',':'}, 5},
5404     {{'n','e','w','s',':'}, 5},
5405     {{'w','a','i','s',':'}, 5},
5406     {{'n','n','t','p',':'}, 5},
5407     {{'h','t','t','p',':'}, 5},
5408     {{'w','w','w','.'}, 4},
5409     {{'f','t','p',':'}, 4},
5410   };
5411   WCHAR bufferW[MAX_PREFIX_LEN + 1];
5412   unsigned int i;
5413 
5414   ME_GetTextW(editor, bufferW, MAX_PREFIX_LEN, start, nChars, FALSE, FALSE);
5415   for (i = 0; i < sizeof(prefixes) / sizeof(*prefixes); i++)
5416   {
5417     if (nChars < prefixes[i].length) continue;
5418     if (!memcmp(prefixes[i].text, bufferW, prefixes[i].length * sizeof(WCHAR)))
5419       return TRUE;
5420   }
5421   return FALSE;
5422 #undef MAX_PREFIX_LEN
5423 }
5424 
5425 /**
5426  * This proc walks through the indicated selection and evaluates whether each
5427  * section identified by ME_FindNextURLCandidate and in-between sections have
5428  * their proper CFE_LINK attributes set or unset. If the CFE_LINK attribute is
5429  * not what it is supposed to be, this proc sets or unsets it as appropriate.
5430  *
5431  * Since this function can cause runs to be split, do not depend on the value
5432  * of the start cursor at the end of the function.
5433  *
5434  * nChars may be set to INT_MAX to update to the end of the text.
5435  *
5436  * Returns TRUE if at least one section was modified.
5437  */
5438 static BOOL ME_UpdateLinkAttribute(ME_TextEditor *editor, ME_Cursor *start, int nChars)
5439 {
5440   BOOL modified = FALSE;
5441   ME_Cursor startCur = *start;
5442 
5443   if (!editor->AutoURLDetect_bEnable) return FALSE;
5444 
5445   do
5446   {
5447     CHARFORMAT2W link;
5448     ME_Cursor candidateStart, candidateEnd;
5449 
5450     if (ME_FindNextURLCandidate(editor, &startCur, nChars,
5451                                 &candidateStart, &candidateEnd))
5452     {
5453       /* Section before candidate is not an URL */
5454       int cMin = ME_GetCursorOfs(&candidateStart);
5455       int cMax = ME_GetCursorOfs(&candidateEnd);
5456 
5457       if (!ME_IsCandidateAnURL(editor, &candidateStart, cMax - cMin))
5458         candidateStart = candidateEnd;
5459       nChars -= cMax - ME_GetCursorOfs(&startCur);
5460     }
5461     else
5462     {
5463       /* No more candidates until end of selection */
5464       nChars = 0;
5465     }
5466 
5467     if (startCur.pRun != candidateStart.pRun ||
5468         startCur.nOffset != candidateStart.nOffset)
5469     {
5470       /* CFE_LINK effect should be consistently unset */
5471       link.cbSize = sizeof(link);
5472       ME_GetCharFormat(editor, &startCur, &candidateStart, &link);
5473       if (!(link.dwMask & CFM_LINK) || (link.dwEffects & CFE_LINK))
5474       {
5475         /* CFE_LINK must be unset from this range */
5476         memset(&link, 0, sizeof(CHARFORMAT2W));
5477         link.cbSize = sizeof(link);
5478         link.dwMask = CFM_LINK;
5479         link.dwEffects = 0;
5480         ME_SetCharFormat(editor, &startCur, &candidateStart, &link);
5481         /* Update candidateEnd since setting character formats may split
5482          * runs, which can cause a cursor to be at an invalid offset within
5483          * a split run. */
5484         while (candidateEnd.nOffset >= candidateEnd.pRun->member.run.len)
5485         {
5486           candidateEnd.nOffset -= candidateEnd.pRun->member.run.len;
5487           candidateEnd.pRun = ME_FindItemFwd(candidateEnd.pRun, diRun);
5488         }
5489         modified = TRUE;
5490       }
5491     }
5492     if (candidateStart.pRun != candidateEnd.pRun ||
5493         candidateStart.nOffset != candidateEnd.nOffset)
5494     {
5495       /* CFE_LINK effect should be consistently set */
5496       link.cbSize = sizeof(link);
5497       ME_GetCharFormat(editor, &candidateStart, &candidateEnd, &link);
5498       if (!(link.dwMask & CFM_LINK) || !(link.dwEffects & CFE_LINK))
5499       {
5500         /* CFE_LINK must be set on this range */
5501         memset(&link, 0, sizeof(CHARFORMAT2W));
5502         link.cbSize = sizeof(link);
5503         link.dwMask = CFM_LINK;
5504         link.dwEffects = CFE_LINK;
5505         ME_SetCharFormat(editor, &candidateStart, &candidateEnd, &link);
5506         modified = TRUE;
5507       }
5508     }
5509     startCur = candidateEnd;
5510   } while (nChars > 0);
5511   return modified;
5512 }
5513