xref: /reactos/dll/win32/riched20/editor.c (revision 23373acb)
1 /*
2  * RichEdit - functions dealing with editor object
3  *
4  * Copyright 2004 by Krzysztof Foltman
5  * Copyright 2005 by Cihan Altinay
6  * Copyright 2005 by Phil Krylov
7  * Copyright 2008 Eric Pouech
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 2.1 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with this library; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22  */
23 
24 /*
25   API implementation status:
26 
27   Messages (ANSI versions not done yet)
28   + EM_AUTOURLDETECT 2.0
29   + EM_CANPASTE
30   + EM_CANREDO 2.0
31   + EM_CANUNDO
32   + EM_CHARFROMPOS
33   - EM_DISPLAYBAND
34   + EM_EMPTYUNDOBUFFER
35   + EM_EXGETSEL
36   + EM_EXLIMITTEXT
37   + EM_EXLINEFROMCHAR
38   + EM_EXSETSEL
39   + EM_FINDTEXT (only FR_DOWN flag implemented)
40   + EM_FINDTEXTEX (only FR_DOWN flag implemented)
41   - EM_FINDWORDBREAK
42   - EM_FMTLINES
43   - EM_FORMATRANGE
44   + EM_GETAUTOURLDETECT 2.0
45   - EM_GETBIDIOPTIONS 3.0
46   - EM_GETCHARFORMAT (partly done)
47   - EM_GETEDITSTYLE
48   + EM_GETEVENTMASK
49   + EM_GETFIRSTVISIBLELINE (can be optimized if needed)
50   - EM_GETIMECOLOR 1.0asian
51   - EM_GETIMECOMPMODE 2.0
52   - EM_GETIMEOPTIONS 1.0asian
53   - EM_GETIMESTATUS
54   - EM_GETLANGOPTIONS 2.0
55   + EM_GETLIMITTEXT
56   + EM_GETLINE
57   + EM_GETLINECOUNT   returns number of rows, not of paragraphs
58   + EM_GETMODIFY
59   + EM_GETOLEINTERFACE
60   + EM_GETOPTIONS
61   + EM_GETPARAFORMAT
62   + EM_GETPASSWORDCHAR 2.0
63   - EM_GETPUNCTUATION 1.0asian
64   + EM_GETRECT
65   - EM_GETREDONAME 2.0
66   + EM_GETSEL
67   + EM_GETSELTEXT (ANSI&Unicode)
68   + EM_GETSCROLLPOS 3.0
69 ! - EM_GETTHUMB
70   + EM_GETTEXTEX 2.0
71   + EM_GETTEXTLENGTHEX (GTL_PRECISE unimplemented)
72   + EM_GETTEXTMODE 2.0
73 ? + EM_GETTEXTRANGE (ANSI&Unicode)
74   - EM_GETTYPOGRAPHYOPTIONS 3.0
75   - EM_GETUNDONAME
76   + EM_GETWORDBREAKPROC
77   - EM_GETWORDBREAKPROCEX
78   - EM_GETWORDWRAPMODE 1.0asian
79   + EM_GETZOOM 3.0
80   + EM_HIDESELECTION
81   + EM_LIMITTEXT (Also called EM_SETLIMITTEXT)
82   + EM_LINEFROMCHAR
83   + EM_LINEINDEX
84   + EM_LINELENGTH
85   + EM_LINESCROLL
86   - EM_PASTESPECIAL
87   + EM_POSFROMCHAR
88   + EM_REDO 2.0
89   + EM_REQUESTRESIZE
90   + EM_REPLACESEL (proper style?) ANSI&Unicode
91   + EM_SCROLL
92   + EM_SCROLLCARET
93   + EM_SELECTIONTYPE
94   - EM_SETBIDIOPTIONS 3.0
95   + EM_SETBKGNDCOLOR
96   + EM_SETCHARFORMAT (partly done, no ANSI)
97   - EM_SETEDITSTYLE
98   + EM_SETEVENTMASK (few notifications supported)
99   + EM_SETFONTSIZE
100   - EM_SETIMECOLOR 1.0asian
101   - EM_SETIMEOPTIONS 1.0asian
102   - EM_SETIMESTATUS
103   - EM_SETLANGOPTIONS 2.0
104   - EM_SETLIMITTEXT
105   - EM_SETMARGINS
106   + EM_SETMODIFY (not sure if implementation is correct)
107   - EM_SETOLECALLBACK
108   + EM_SETOPTIONS (partially implemented)
109   - EM_SETPALETTE 2.0
110   + EM_SETPARAFORMAT
111   + EM_SETPASSWORDCHAR 2.0
112   - EM_SETPUNCTUATION 1.0asian
113   + EM_SETREADONLY no beep on modification attempt
114   + EM_SETRECT
115   + EM_SETRECTNP (EM_SETRECT without repainting)
116   + EM_SETSEL
117   + EM_SETSCROLLPOS 3.0
118   - EM_SETTABSTOPS 3.0
119   - EM_SETTARGETDEVICE (partial)
120   + EM_SETTEXTEX 3.0 (proper style?)
121   - EM_SETTEXTMODE 2.0
122   - EM_SETTYPOGRAPHYOPTIONS 3.0
123   + EM_SETUNDOLIMIT 2.0
124   + EM_SETWORDBREAKPROC (used only for word movement at the moment)
125   - EM_SETWORDBREAKPROCEX
126   - EM_SETWORDWRAPMODE 1.0asian
127   + EM_SETZOOM 3.0
128   + EM_SHOWSCROLLBAR 2.0
129   + EM_STOPGROUPTYPING 2.0
130   + EM_STREAMIN
131   + EM_STREAMOUT
132   + EM_UNDO
133   + WM_CHAR
134   + WM_CLEAR
135   + WM_COPY
136   + WM_CUT
137   + WM_GETDLGCODE (the current implementation is incomplete)
138   + WM_GETTEXT (ANSI&Unicode)
139   + WM_GETTEXTLENGTH (ANSI version sucks)
140   + WM_HSCROLL
141   + WM_PASTE
142   + WM_SETFONT
143   + WM_SETTEXT (resets undo stack !) (proper style?) ANSI&Unicode
144   + WM_STYLECHANGING (seems to do nothing)
145   + WM_STYLECHANGED (seems to do nothing)
146   + WM_UNICHAR
147   + WM_VSCROLL
148 
149   Notifications
150 
151   * EN_CHANGE (sent from the wrong place)
152   - EN_CORRECTTEXT
153   - EN_DROPFILES
154   - EN_ERRSPACE
155   - EN_HSCROLL
156   - EN_IMECHANGE
157   + EN_KILLFOCUS
158   - EN_LINK
159   - EN_MAXTEXT
160   - EN_MSGFILTER
161   - EN_OLEOPFAILED
162   - EN_PROTECTED
163   + EN_REQUESTRESIZE
164   - EN_SAVECLIPBOARD
165   + EN_SELCHANGE
166   + EN_SETFOCUS
167   - EN_STOPNOUNDO
168   * EN_UPDATE (sent from the wrong place)
169   - EN_VSCROLL
170 
171   Styles
172 
173   - ES_AUTOHSCROLL
174   - ES_AUTOVSCROLL
175   + ES_CENTER
176   + ES_DISABLENOSCROLL (scrollbar is always visible)
177   - ES_EX_NOCALLOLEINIT
178   + ES_LEFT
179   + ES_MULTILINE
180   - ES_NOIME
181   - ES_READONLY (I'm not sure if beeping is the proper behaviour)
182   + ES_RIGHT
183   - ES_SAVESEL
184   - ES_SELFIME
185   - ES_SUNKEN
186   - ES_VERTICAL
187   - ES_WANTRETURN (don't know how to do WM_GETDLGCODE part)
188   - WS_SETFONT
189   + WS_HSCROLL
190   + WS_VSCROLL
191 */
192 
193 /*
194  * RICHED20 TODO (incomplete):
195  *
196  * - messages/styles/notifications listed above
197  * - add remaining CHARFORMAT/PARAFORMAT fields
198  * - right/center align should strip spaces from the beginning
199  * - pictures/OLE objects (not just smiling faces that lack API support ;-) )
200  * - COM interface (looks like a major pain in the TODO list)
201  * - calculate heights of pictures (half-done)
202  * - hysteresis during wrapping (related to scrollbars appearing/disappearing)
203  * - find/replace
204  * - how to implement EM_FORMATRANGE and EM_DISPLAYBAND ? (Mission Impossible)
205  * - italic caret with italic fonts
206  * - IME
207  * - most notifications aren't sent at all (the most important ones are)
208  * - when should EN_SELCHANGE be sent after text change ? (before/after EN_UPDATE?)
209  * - WM_SETTEXT may use wrong style (but I'm 80% sure it's OK)
210  * - EM_GETCHARFORMAT with SCF_SELECTION may not behave 100% like in original (but very close)
211  * - full justification
212  * - hyphenation
213  * - tables
214  * - ListBox & ComboBox not implemented
215  *
216  * Bugs that are probably fixed, but not so easy to verify:
217  * - EN_UPDATE/EN_CHANGE are handled very incorrectly (should be OK now)
218  * - undo for ME_JoinParagraphs doesn't store paragraph format ? (it does)
219  * - check/fix artificial EOL logic (bCursorAtEnd, hardly logical)
220  * - caret shouldn't be displayed when selection isn't empty
221  * - check refcounting in style management functions (looks perfect now, but no bugs is suspicious)
222  * - undo for setting default format (done, might be buggy)
223  * - styles might be not released properly (looks like they work like charm, but who knows?
224  *
225  */
226 
227 #define NONAMELESSUNION
228 
229 #include "editor.h"
230 #include "commdlg.h"
231 #include "winreg.h"
232 #define NO_SHLWAPI_STREAM
233 #include "shlwapi.h"
234 #include "rtf.h"
235 #include "imm.h"
236 #include "res.h"
237 
238 #ifdef __REACTOS__
239 #include <reactos/undocuser.h>
240 #endif
241 
242 #define STACK_SIZE_DEFAULT  100
243 #define STACK_SIZE_MAX     1000
244 
245 #define TEXT_LIMIT_DEFAULT 32767
246 
247 WINE_DEFAULT_DEBUG_CHANNEL(richedit);
248 
249 static BOOL ME_RegisterEditorClass(HINSTANCE);
250 static BOOL ME_UpdateLinkAttribute(ME_TextEditor *editor, ME_Cursor *start, int nChars);
251 
252 static const WCHAR REListBox20W[] = {'R','E','L','i','s','t','B','o','x','2','0','W', 0};
253 static const WCHAR REComboBox20W[] = {'R','E','C','o','m','b','o','B','o','x','2','0','W', 0};
254 static HCURSOR hLeft;
255 
256 BOOL me_debug = FALSE;
257 HANDLE me_heap = NULL;
258 
259 static BOOL ME_ListBoxRegistered = FALSE;
260 static BOOL ME_ComboBoxRegistered = FALSE;
261 
262 static inline BOOL is_version_nt(void)
263 {
264     return !(GetVersion() & 0x80000000);
265 }
266 
267 static ME_TextBuffer *ME_MakeText(void) {
268   ME_TextBuffer *buf = heap_alloc(sizeof(*buf));
269   ME_DisplayItem *p1 = ME_MakeDI(diTextStart);
270   ME_DisplayItem *p2 = ME_MakeDI(diTextEnd);
271 
272   p1->prev = NULL;
273   p1->next = p2;
274   p2->prev = p1;
275   p2->next = NULL;
276   p1->member.para.next_para = p2;
277   p2->member.para.prev_para = p1;
278   p2->member.para.nCharOfs = 0;
279 
280   buf->pFirst = p1;
281   buf->pLast = p2;
282   buf->pCharStyle = NULL;
283 
284   return buf;
285 }
286 
287 
288 static LRESULT ME_StreamInText(ME_TextEditor *editor, DWORD dwFormat, ME_InStream *stream, ME_Style *style)
289 {
290   WCHAR *pText;
291   LRESULT total_bytes_read = 0;
292   BOOL is_read = FALSE;
293   DWORD cp = CP_ACP, copy = 0;
294   char conv_buf[4 + STREAMIN_BUFFER_SIZE]; /* up to 4 additional UTF-8 bytes */
295 
296   static const char bom_utf8[] = {0xEF, 0xBB, 0xBF};
297 
298   TRACE("%08x %p\n", dwFormat, stream);
299 
300   do {
301     LONG nWideChars = 0;
302     WCHAR wszText[STREAMIN_BUFFER_SIZE+1];
303 
304     if (!stream->dwSize)
305     {
306       ME_StreamInFill(stream);
307       if (stream->editstream->dwError)
308         break;
309       if (!stream->dwSize)
310         break;
311       total_bytes_read += stream->dwSize;
312     }
313 
314     if (!(dwFormat & SF_UNICODE))
315     {
316       char * buf = stream->buffer;
317       DWORD size = stream->dwSize, end;
318 
319       if (!is_read)
320       {
321         is_read = TRUE;
322         if (stream->dwSize >= 3 && !memcmp(stream->buffer, bom_utf8, 3))
323         {
324           cp = CP_UTF8;
325           buf += 3;
326           size -= 3;
327         }
328       }
329 
330       if (cp == CP_UTF8)
331       {
332         if (copy)
333         {
334           memcpy(conv_buf + copy, buf, size);
335           buf = conv_buf;
336           size += copy;
337         }
338         end = size;
339         while ((buf[end-1] & 0xC0) == 0x80)
340         {
341           --end;
342           --total_bytes_read; /* strange, but seems to match windows */
343         }
344         if (buf[end-1] & 0x80)
345         {
346           DWORD need = 0;
347           if ((buf[end-1] & 0xE0) == 0xC0)
348             need = 1;
349           if ((buf[end-1] & 0xF0) == 0xE0)
350             need = 2;
351           if ((buf[end-1] & 0xF8) == 0xF0)
352             need = 3;
353 
354           if (size - end >= need)
355           {
356             /* we have enough bytes for this sequence */
357             end = size;
358           }
359           else
360           {
361             /* need more bytes, so don't transcode this sequence */
362             --end;
363           }
364         }
365       }
366       else
367         end = size;
368 
369       nWideChars = MultiByteToWideChar(cp, 0, buf, end, wszText, STREAMIN_BUFFER_SIZE);
370       pText = wszText;
371 
372       if (cp == CP_UTF8)
373       {
374         if (end != size)
375         {
376           memcpy(conv_buf, buf + end, size - end);
377           copy = size - end;
378         }
379       }
380     }
381     else
382     {
383       nWideChars = stream->dwSize >> 1;
384       pText = (WCHAR *)stream->buffer;
385     }
386 
387     ME_InsertTextFromCursor(editor, 0, pText, nWideChars, style);
388     if (stream->dwSize == 0)
389       break;
390     stream->dwSize = 0;
391   } while(1);
392   return total_bytes_read;
393 }
394 
395 static void ME_ApplyBorderProperties(RTF_Info *info,
396                                      ME_BorderRect *borderRect,
397                                      RTFBorder *borderDef)
398 {
399   int i, colorNum;
400   ME_Border *pBorders[] = {&borderRect->top,
401                            &borderRect->left,
402                            &borderRect->bottom,
403                            &borderRect->right};
404   for (i = 0; i < 4; i++)
405   {
406     RTFColor *colorDef = info->colorList;
407     pBorders[i]->width = borderDef[i].width;
408     colorNum = borderDef[i].color;
409     while (colorDef && colorDef->rtfCNum != colorNum)
410       colorDef = colorDef->rtfNextColor;
411     if (colorDef)
412       pBorders[i]->colorRef = RGB(
413                            colorDef->rtfCRed >= 0 ? colorDef->rtfCRed : 0,
414                            colorDef->rtfCGreen >= 0 ? colorDef->rtfCGreen : 0,
415                            colorDef->rtfCBlue >= 0 ? colorDef->rtfCBlue : 0);
416     else
417       pBorders[i]->colorRef = RGB(0, 0, 0);
418   }
419 }
420 
421 void ME_RTFCharAttrHook(RTF_Info *info)
422 {
423   CHARFORMAT2W fmt;
424   fmt.cbSize = sizeof(fmt);
425   fmt.dwMask = 0;
426   fmt.dwEffects = 0;
427 
428   switch(info->rtfMinor)
429   {
430     case rtfPlain:
431       /* FIXME add more flags once they're implemented */
432       fmt.dwMask = CFM_BOLD | CFM_ITALIC | CFM_UNDERLINE | CFM_UNDERLINETYPE | CFM_STRIKEOUT |
433           CFM_COLOR | CFM_BACKCOLOR | CFM_SIZE | CFM_WEIGHT;
434       fmt.dwEffects = CFE_AUTOCOLOR | CFE_AUTOBACKCOLOR;
435       fmt.yHeight = 12*20; /* 12pt */
436       fmt.wWeight = FW_NORMAL;
437       fmt.bUnderlineType = CFU_UNDERLINE;
438       break;
439     case rtfBold:
440       fmt.dwMask = CFM_BOLD | CFM_WEIGHT;
441       fmt.dwEffects = info->rtfParam ? CFE_BOLD : 0;
442       fmt.wWeight = info->rtfParam ? FW_BOLD : FW_NORMAL;
443       break;
444     case rtfItalic:
445       fmt.dwMask = CFM_ITALIC;
446       fmt.dwEffects = info->rtfParam ? fmt.dwMask : 0;
447       break;
448     case rtfUnderline:
449       fmt.dwMask = CFM_UNDERLINETYPE | CFM_UNDERLINE;
450       fmt.bUnderlineType = CFU_UNDERLINE;
451       fmt.dwEffects = info->rtfParam ? CFE_UNDERLINE : 0;
452       break;
453     case rtfDotUnderline:
454       fmt.dwMask = CFM_UNDERLINETYPE | CFM_UNDERLINE;
455       fmt.bUnderlineType = CFU_UNDERLINEDOTTED;
456       fmt.dwEffects = info->rtfParam ? CFE_UNDERLINE : 0;
457       break;
458     case rtfDbUnderline:
459       fmt.dwMask = CFM_UNDERLINETYPE | CFM_UNDERLINE;
460       fmt.bUnderlineType = CFU_UNDERLINEDOUBLE;
461       fmt.dwEffects = info->rtfParam ? CFE_UNDERLINE : 0;
462       break;
463     case rtfWordUnderline:
464       fmt.dwMask = CFM_UNDERLINETYPE | CFM_UNDERLINE;
465       fmt.bUnderlineType = CFU_UNDERLINEWORD;
466       fmt.dwEffects = info->rtfParam ? CFE_UNDERLINE : 0;
467       break;
468     case rtfNoUnderline:
469       fmt.dwMask = CFM_UNDERLINE;
470       fmt.dwEffects = 0;
471       break;
472     case rtfStrikeThru:
473       fmt.dwMask = CFM_STRIKEOUT;
474       fmt.dwEffects = info->rtfParam ? fmt.dwMask : 0;
475       break;
476     case rtfSubScript:
477     case rtfSuperScript:
478     case rtfSubScrShrink:
479     case rtfSuperScrShrink:
480     case rtfNoSuperSub:
481       fmt.dwMask = CFM_SUBSCRIPT|CFM_SUPERSCRIPT;
482       if (info->rtfMinor == rtfSubScrShrink) fmt.dwEffects = CFE_SUBSCRIPT;
483       if (info->rtfMinor == rtfSuperScrShrink) fmt.dwEffects = CFE_SUPERSCRIPT;
484       if (info->rtfMinor == rtfNoSuperSub) fmt.dwEffects = 0;
485       break;
486     case rtfInvisible:
487       fmt.dwMask = CFM_HIDDEN;
488       fmt.dwEffects = info->rtfParam ? fmt.dwMask : 0;
489       break;
490     case rtfBackColor:
491       fmt.dwMask = CFM_BACKCOLOR;
492       fmt.dwEffects = 0;
493       if (info->rtfParam == 0)
494         fmt.dwEffects = CFE_AUTOBACKCOLOR;
495       else if (info->rtfParam != rtfNoParam)
496       {
497         RTFColor *c = RTFGetColor(info, info->rtfParam);
498         if (c && c->rtfCBlue >= 0)
499           fmt.crBackColor = (c->rtfCBlue<<16)|(c->rtfCGreen<<8)|(c->rtfCRed);
500         else
501           fmt.dwEffects = CFE_AUTOBACKCOLOR;
502       }
503       break;
504     case rtfForeColor:
505       fmt.dwMask = CFM_COLOR;
506       fmt.dwEffects = 0;
507       if (info->rtfParam == 0)
508         fmt.dwEffects = CFE_AUTOCOLOR;
509       else if (info->rtfParam != rtfNoParam)
510       {
511         RTFColor *c = RTFGetColor(info, info->rtfParam);
512         if (c && c->rtfCBlue >= 0)
513           fmt.crTextColor = (c->rtfCBlue<<16)|(c->rtfCGreen<<8)|(c->rtfCRed);
514         else {
515           fmt.dwEffects = CFE_AUTOCOLOR;
516         }
517       }
518       break;
519     case rtfFontNum:
520       if (info->rtfParam != rtfNoParam)
521       {
522         RTFFont *f = RTFGetFont(info, info->rtfParam);
523         if (f)
524         {
525           MultiByteToWideChar(CP_ACP, 0, f->rtfFName, -1, fmt.szFaceName, ARRAY_SIZE(fmt.szFaceName));
526           fmt.szFaceName[ARRAY_SIZE(fmt.szFaceName)-1] = '\0';
527           fmt.bCharSet = f->rtfFCharSet;
528           fmt.dwMask = CFM_FACE | CFM_CHARSET;
529           fmt.bPitchAndFamily = f->rtfFPitch | (f->rtfFFamily << 4);
530         }
531       }
532       break;
533     case rtfFontSize:
534       fmt.dwMask = CFM_SIZE;
535       if (info->rtfParam != rtfNoParam)
536         fmt.yHeight = info->rtfParam*10;
537       break;
538   }
539   if (fmt.dwMask) {
540     ME_Style *style2;
541     RTFFlushOutputBuffer(info);
542     /* FIXME too slow ? how come ? */
543     style2 = ME_ApplyStyle(info->editor, info->style, &fmt);
544     ME_ReleaseStyle(info->style);
545     info->style = style2;
546     info->styleChanged = TRUE;
547   }
548 }
549 
550 /* FIXME this function doesn't get any information about context of the RTF tag, which is very bad,
551    the same tags mean different things in different contexts */
552 void ME_RTFParAttrHook(RTF_Info *info)
553 {
554   switch(info->rtfMinor)
555   {
556   case rtfParDef: /* restores default paragraph attributes */
557     if (!info->editor->bEmulateVersion10) /* v4.1 */
558       info->borderType = RTFBorderParaLeft;
559     else /* v1.0 - 3.0 */
560       info->borderType = RTFBorderParaTop;
561     info->fmt.dwMask = PFM_ALIGNMENT | PFM_BORDER | PFM_LINESPACING | PFM_TABSTOPS |
562         PFM_OFFSET | PFM_RIGHTINDENT | PFM_SPACEAFTER | PFM_SPACEBEFORE |
563         PFM_STARTINDENT | PFM_RTLPARA | PFM_NUMBERING | PFM_NUMBERINGSTART |
564         PFM_NUMBERINGSTYLE | PFM_NUMBERINGTAB;
565     /* TODO: shading */
566     info->fmt.wAlignment = PFA_LEFT;
567     info->fmt.cTabCount = 0;
568     info->fmt.dxOffset = info->fmt.dxStartIndent = info->fmt.dxRightIndent = 0;
569     info->fmt.wBorderWidth = info->fmt.wBorders = 0;
570     info->fmt.wBorderSpace = 0;
571     info->fmt.bLineSpacingRule = 0;
572     info->fmt.dySpaceBefore = info->fmt.dySpaceAfter = 0;
573     info->fmt.dyLineSpacing = 0;
574     info->fmt.wEffects &= ~PFE_RTLPARA;
575     info->fmt.wNumbering = 0;
576     info->fmt.wNumberingStart = 0;
577     info->fmt.wNumberingStyle = 0;
578     info->fmt.wNumberingTab = 0;
579 
580     if (!info->editor->bEmulateVersion10) /* v4.1 */
581     {
582       if (info->tableDef && info->tableDef->tableRowStart &&
583           info->tableDef->tableRowStart->member.para.nFlags & MEPF_ROWEND)
584       {
585         ME_Cursor cursor;
586         ME_DisplayItem *para;
587         /* We are just after a table row. */
588         RTFFlushOutputBuffer(info);
589         cursor = info->editor->pCursors[0];
590         para = cursor.pPara;
591         if (para  == info->tableDef->tableRowStart->member.para.next_para
592             && !cursor.nOffset && !cursor.pRun->member.run.nCharOfs)
593         {
594           /* Since the table row end, no text has been inserted, and the \intbl
595            * control word has not be used.  We can confirm that we are not in a
596            * table anymore.
597            */
598           info->tableDef->tableRowStart = NULL;
599           info->canInheritInTbl = FALSE;
600         }
601       }
602     } else { /* v1.0 - v3.0 */
603       info->fmt.dwMask |= PFM_TABLE;
604       info->fmt.wEffects &= ~PFE_TABLE;
605     }
606     break;
607   case rtfNestLevel:
608     if (!info->editor->bEmulateVersion10) /* v4.1 */
609     {
610       while (info->rtfParam > info->nestingLevel) {
611         RTFTable *tableDef = heap_alloc_zero(sizeof(*tableDef));
612         tableDef->parent = info->tableDef;
613         info->tableDef = tableDef;
614 
615         RTFFlushOutputBuffer(info);
616         if (tableDef->tableRowStart &&
617             tableDef->tableRowStart->member.para.nFlags & MEPF_ROWEND)
618         {
619           ME_DisplayItem *para = tableDef->tableRowStart;
620           para = para->member.para.next_para;
621           para = ME_InsertTableRowStartAtParagraph(info->editor, para);
622           tableDef->tableRowStart = para;
623         } else {
624           ME_Cursor cursor;
625           WCHAR endl = '\r';
626           cursor = info->editor->pCursors[0];
627           if (cursor.nOffset || cursor.pRun->member.run.nCharOfs)
628             ME_InsertTextFromCursor(info->editor, 0, &endl, 1, info->style);
629           tableDef->tableRowStart = ME_InsertTableRowStartFromCursor(info->editor);
630         }
631 
632         info->nestingLevel++;
633       }
634       info->canInheritInTbl = FALSE;
635     }
636     break;
637   case rtfInTable:
638   {
639     if (!info->editor->bEmulateVersion10) /* v4.1 */
640     {
641       if (info->nestingLevel < 1)
642       {
643         RTFTable *tableDef;
644         if (!info->tableDef)
645             info->tableDef = heap_alloc_zero(sizeof(*info->tableDef));
646         tableDef = info->tableDef;
647         RTFFlushOutputBuffer(info);
648         if (tableDef->tableRowStart &&
649             tableDef->tableRowStart->member.para.nFlags & MEPF_ROWEND)
650         {
651           ME_DisplayItem *para = tableDef->tableRowStart;
652           para = para->member.para.next_para;
653           para = ME_InsertTableRowStartAtParagraph(info->editor, para);
654           tableDef->tableRowStart = para;
655         } else {
656           ME_Cursor cursor;
657           WCHAR endl = '\r';
658           cursor = info->editor->pCursors[0];
659           if (cursor.nOffset || cursor.pRun->member.run.nCharOfs)
660             ME_InsertTextFromCursor(info->editor, 0, &endl, 1, info->style);
661           tableDef->tableRowStart = ME_InsertTableRowStartFromCursor(info->editor);
662         }
663         info->nestingLevel = 1;
664         info->canInheritInTbl = TRUE;
665       }
666       return;
667     } else { /* v1.0 - v3.0 */
668       info->fmt.dwMask |= PFM_TABLE;
669       info->fmt.wEffects |= PFE_TABLE;
670     }
671     break;
672   }
673   case rtfFirstIndent:
674   case rtfLeftIndent:
675     if ((info->fmt.dwMask & (PFM_STARTINDENT | PFM_OFFSET)) != (PFM_STARTINDENT | PFM_OFFSET))
676     {
677       PARAFORMAT2 fmt;
678       fmt.cbSize = sizeof(fmt);
679       ME_GetSelectionParaFormat(info->editor, &fmt);
680       info->fmt.dwMask |= PFM_STARTINDENT | PFM_OFFSET;
681       info->fmt.dxStartIndent = fmt.dxStartIndent;
682       info->fmt.dxOffset = fmt.dxOffset;
683     }
684     if (info->rtfMinor == rtfFirstIndent)
685     {
686       info->fmt.dxStartIndent += info->fmt.dxOffset + info->rtfParam;
687       info->fmt.dxOffset = -info->rtfParam;
688     }
689     else
690       info->fmt.dxStartIndent = info->rtfParam - info->fmt.dxOffset;
691     break;
692   case rtfRightIndent:
693     info->fmt.dwMask |= PFM_RIGHTINDENT;
694     info->fmt.dxRightIndent = info->rtfParam;
695     break;
696   case rtfQuadLeft:
697   case rtfQuadJust:
698     info->fmt.dwMask |= PFM_ALIGNMENT;
699     info->fmt.wAlignment = PFA_LEFT;
700     break;
701   case rtfQuadRight:
702     info->fmt.dwMask |= PFM_ALIGNMENT;
703     info->fmt.wAlignment = PFA_RIGHT;
704     break;
705   case rtfQuadCenter:
706     info->fmt.dwMask |= PFM_ALIGNMENT;
707     info->fmt.wAlignment = PFA_CENTER;
708     break;
709   case rtfTabPos:
710     if (!(info->fmt.dwMask & PFM_TABSTOPS))
711     {
712       PARAFORMAT2 fmt;
713       fmt.cbSize = sizeof(fmt);
714       ME_GetSelectionParaFormat(info->editor, &fmt);
715       memcpy(info->fmt.rgxTabs, fmt.rgxTabs,
716              fmt.cTabCount * sizeof(fmt.rgxTabs[0]));
717       info->fmt.cTabCount = fmt.cTabCount;
718       info->fmt.dwMask |= PFM_TABSTOPS;
719     }
720     if (info->fmt.cTabCount < MAX_TAB_STOPS && info->rtfParam < 0x1000000)
721       info->fmt.rgxTabs[info->fmt.cTabCount++] = info->rtfParam;
722     break;
723   case rtfKeep:
724     info->fmt.dwMask |= PFM_KEEP;
725     info->fmt.wEffects |= PFE_KEEP;
726     break;
727   case rtfNoWidowControl:
728     info->fmt.dwMask |= PFM_NOWIDOWCONTROL;
729     info->fmt.wEffects |= PFE_NOWIDOWCONTROL;
730     break;
731   case rtfKeepNext:
732     info->fmt.dwMask |= PFM_KEEPNEXT;
733     info->fmt.wEffects |= PFE_KEEPNEXT;
734     break;
735   case rtfSpaceAfter:
736     info->fmt.dwMask |= PFM_SPACEAFTER;
737     info->fmt.dySpaceAfter = info->rtfParam;
738     break;
739   case rtfSpaceBefore:
740     info->fmt.dwMask |= PFM_SPACEBEFORE;
741     info->fmt.dySpaceBefore = info->rtfParam;
742     break;
743   case rtfSpaceBetween:
744     info->fmt.dwMask |= PFM_LINESPACING;
745     if ((int)info->rtfParam > 0)
746     {
747       info->fmt.dyLineSpacing = info->rtfParam;
748       info->fmt.bLineSpacingRule = 3;
749     }
750     else
751     {
752       info->fmt.dyLineSpacing = info->rtfParam;
753       info->fmt.bLineSpacingRule = 4;
754     }
755     break;
756   case rtfSpaceMultiply:
757     info->fmt.dwMask |= PFM_LINESPACING;
758     info->fmt.dyLineSpacing = info->rtfParam * 20;
759     info->fmt.bLineSpacingRule = 5;
760     break;
761   case rtfParBullet:
762     info->fmt.dwMask |= PFM_NUMBERING;
763     info->fmt.wNumbering = PFN_BULLET;
764     break;
765   case rtfParSimple:
766     info->fmt.dwMask |= PFM_NUMBERING;
767     info->fmt.wNumbering = 2; /* FIXME: MSDN says it's not used ?? */
768     break;
769   case rtfBorderLeft:
770     info->borderType = RTFBorderParaLeft;
771     info->fmt.wBorders |= 1;
772     info->fmt.dwMask |= PFM_BORDER;
773     break;
774   case rtfBorderRight:
775     info->borderType = RTFBorderParaRight;
776     info->fmt.wBorders |= 2;
777     info->fmt.dwMask |= PFM_BORDER;
778     break;
779   case rtfBorderTop:
780     info->borderType = RTFBorderParaTop;
781     info->fmt.wBorders |= 4;
782     info->fmt.dwMask |= PFM_BORDER;
783     break;
784   case rtfBorderBottom:
785     info->borderType = RTFBorderParaBottom;
786     info->fmt.wBorders |= 8;
787     info->fmt.dwMask |= PFM_BORDER;
788     break;
789   case rtfBorderSingle:
790     info->fmt.wBorders &= ~0x700;
791     info->fmt.wBorders |= 1 << 8;
792     info->fmt.dwMask |= PFM_BORDER;
793     break;
794   case rtfBorderThick:
795     info->fmt.wBorders &= ~0x700;
796     info->fmt.wBorders |= 2 << 8;
797     info->fmt.dwMask |= PFM_BORDER;
798     break;
799   case rtfBorderShadow:
800     info->fmt.wBorders &= ~0x700;
801     info->fmt.wBorders |= 10 << 8;
802     info->fmt.dwMask |= PFM_BORDER;
803     break;
804   case rtfBorderDouble:
805     info->fmt.wBorders &= ~0x700;
806     info->fmt.wBorders |= 7 << 8;
807     info->fmt.dwMask |= PFM_BORDER;
808     break;
809   case rtfBorderDot:
810     info->fmt.wBorders &= ~0x700;
811     info->fmt.wBorders |= 11 << 8;
812     info->fmt.dwMask |= PFM_BORDER;
813     break;
814   case rtfBorderWidth:
815   {
816     int borderSide = info->borderType & RTFBorderSideMask;
817     RTFTable *tableDef = info->tableDef;
818     if ((info->borderType & RTFBorderTypeMask) == RTFBorderTypeCell)
819     {
820       RTFBorder *border;
821       if (!tableDef || tableDef->numCellsDefined >= MAX_TABLE_CELLS)
822         break;
823       border = &tableDef->cells[tableDef->numCellsDefined].border[borderSide];
824       border->width = info->rtfParam;
825       break;
826     }
827     info->fmt.wBorderWidth = info->rtfParam;
828     info->fmt.dwMask |= PFM_BORDER;
829     break;
830   }
831   case rtfBorderSpace:
832     info->fmt.wBorderSpace = info->rtfParam;
833     info->fmt.dwMask |= PFM_BORDER;
834     break;
835   case rtfBorderColor:
836   {
837     RTFTable *tableDef = info->tableDef;
838     int borderSide = info->borderType & RTFBorderSideMask;
839     int borderType = info->borderType & RTFBorderTypeMask;
840     switch(borderType)
841     {
842     case RTFBorderTypePara:
843       if (!info->editor->bEmulateVersion10) /* v4.1 */
844         break;
845       /* v1.0 - 3.0 treat paragraph and row borders the same. */
846     case RTFBorderTypeRow:
847       if (tableDef) {
848         tableDef->border[borderSide].color = info->rtfParam;
849       }
850       break;
851     case RTFBorderTypeCell:
852       if (tableDef && tableDef->numCellsDefined < MAX_TABLE_CELLS) {
853         tableDef->cells[tableDef->numCellsDefined].border[borderSide].color = info->rtfParam;
854       }
855       break;
856     }
857     break;
858   }
859   case rtfRTLPar:
860     info->fmt.dwMask |= PFM_RTLPARA;
861     info->fmt.wEffects |= PFE_RTLPARA;
862     break;
863   case rtfLTRPar:
864     info->fmt.dwMask |= PFM_RTLPARA;
865     info->fmt.wEffects &= ~PFE_RTLPARA;
866     break;
867   }
868 }
869 
870 void ME_RTFTblAttrHook(RTF_Info *info)
871 {
872   switch (info->rtfMinor)
873   {
874     case rtfRowDef:
875     {
876       if (!info->editor->bEmulateVersion10) /* v4.1 */
877         info->borderType = 0; /* Not sure */
878       else /* v1.0 - 3.0 */
879         info->borderType = RTFBorderRowTop;
880       if (!info->tableDef) {
881         info->tableDef = ME_MakeTableDef(info->editor);
882       } else {
883         ME_InitTableDef(info->editor, info->tableDef);
884       }
885       break;
886     }
887     case rtfCellPos:
888     {
889       int cellNum;
890       if (!info->tableDef)
891       {
892         info->tableDef = ME_MakeTableDef(info->editor);
893       }
894       cellNum = info->tableDef->numCellsDefined;
895       if (cellNum >= MAX_TABLE_CELLS)
896         break;
897       info->tableDef->cells[cellNum].rightBoundary = info->rtfParam;
898       if (cellNum < MAX_TAB_STOPS) {
899         /* Tab stops were used to store cell positions before v4.1 but v4.1
900          * still seems to set the tabstops without using them. */
901         ME_DisplayItem *para = info->editor->pCursors[0].pPara;
902         PARAFORMAT2 *pFmt = &para->member.para.fmt;
903         pFmt->rgxTabs[cellNum] &= ~0x00FFFFFF;
904         pFmt->rgxTabs[cellNum] |= 0x00FFFFFF & info->rtfParam;
905       }
906       info->tableDef->numCellsDefined++;
907       break;
908     }
909     case rtfRowBordTop:
910       info->borderType = RTFBorderRowTop;
911       break;
912     case rtfRowBordLeft:
913       info->borderType = RTFBorderRowLeft;
914       break;
915     case rtfRowBordBottom:
916       info->borderType = RTFBorderRowBottom;
917       break;
918     case rtfRowBordRight:
919       info->borderType = RTFBorderRowRight;
920       break;
921     case rtfCellBordTop:
922       info->borderType = RTFBorderCellTop;
923       break;
924     case rtfCellBordLeft:
925       info->borderType = RTFBorderCellLeft;
926       break;
927     case rtfCellBordBottom:
928       info->borderType = RTFBorderCellBottom;
929       break;
930     case rtfCellBordRight:
931       info->borderType = RTFBorderCellRight;
932       break;
933     case rtfRowGapH:
934       if (info->tableDef)
935         info->tableDef->gapH = info->rtfParam;
936       break;
937     case rtfRowLeftEdge:
938       if (info->tableDef)
939         info->tableDef->leftEdge = info->rtfParam;
940       break;
941   }
942 }
943 
944 void ME_RTFSpecialCharHook(RTF_Info *info)
945 {
946   RTFTable *tableDef = info->tableDef;
947   switch (info->rtfMinor)
948   {
949     case rtfNestCell:
950       if (info->editor->bEmulateVersion10) /* v1.0 - v3.0 */
951         break;
952       /* else fall through since v4.1 treats rtfNestCell and rtfCell the same */
953     case rtfCell:
954       if (!tableDef)
955         break;
956       RTFFlushOutputBuffer(info);
957       if (!info->editor->bEmulateVersion10) { /* v4.1 */
958         if (tableDef->tableRowStart)
959         {
960           if (!info->nestingLevel &&
961               tableDef->tableRowStart->member.para.nFlags & MEPF_ROWEND)
962           {
963             ME_DisplayItem *para = tableDef->tableRowStart;
964             para = para->member.para.next_para;
965             para = ME_InsertTableRowStartAtParagraph(info->editor, para);
966             tableDef->tableRowStart = para;
967             info->nestingLevel = 1;
968           }
969           ME_InsertTableCellFromCursor(info->editor);
970         }
971       } else { /* v1.0 - v3.0 */
972         ME_DisplayItem *para = info->editor->pCursors[0].pPara;
973         PARAFORMAT2 *pFmt = &para->member.para.fmt;
974         if (pFmt->dwMask & PFM_TABLE && pFmt->wEffects & PFE_TABLE &&
975             tableDef->numCellsInserted < tableDef->numCellsDefined)
976         {
977           WCHAR tab = '\t';
978           ME_InsertTextFromCursor(info->editor, 0, &tab, 1, info->style);
979           tableDef->numCellsInserted++;
980         }
981       }
982       break;
983     case rtfNestRow:
984       if (info->editor->bEmulateVersion10) /* v1.0 - v3.0 */
985         break;
986       /* else fall through since v4.1 treats rtfNestRow and rtfRow the same */
987     case rtfRow:
988     {
989       ME_DisplayItem *para, *cell, *run;
990       int i;
991 
992       if (!tableDef)
993         break;
994       RTFFlushOutputBuffer(info);
995       if (!info->editor->bEmulateVersion10) { /* v4.1 */
996         if (!tableDef->tableRowStart)
997           break;
998         if (!info->nestingLevel &&
999             tableDef->tableRowStart->member.para.nFlags & MEPF_ROWEND)
1000         {
1001           para = tableDef->tableRowStart;
1002           para = para->member.para.next_para;
1003           para = ME_InsertTableRowStartAtParagraph(info->editor, para);
1004           tableDef->tableRowStart = para;
1005           info->nestingLevel++;
1006         }
1007         para = tableDef->tableRowStart;
1008         cell = ME_FindItemFwd(para, diCell);
1009         assert(cell && !cell->member.cell.prev_cell);
1010         if (tableDef->numCellsDefined < 1)
1011         {
1012           /* 2000 twips appears to be the cell size that native richedit uses
1013            * when no cell sizes are specified. */
1014           const int defaultCellSize = 2000;
1015           int nRightBoundary = defaultCellSize;
1016           cell->member.cell.nRightBoundary = nRightBoundary;
1017           while (cell->member.cell.next_cell) {
1018             cell = cell->member.cell.next_cell;
1019             nRightBoundary += defaultCellSize;
1020             cell->member.cell.nRightBoundary = nRightBoundary;
1021           }
1022           para = ME_InsertTableCellFromCursor(info->editor);
1023           cell = para->member.para.pCell;
1024           cell->member.cell.nRightBoundary = nRightBoundary;
1025         } else {
1026           for (i = 0; i < tableDef->numCellsDefined; i++)
1027           {
1028             RTFCell *cellDef = &tableDef->cells[i];
1029             cell->member.cell.nRightBoundary = cellDef->rightBoundary;
1030             ME_ApplyBorderProperties(info, &cell->member.cell.border,
1031                                      cellDef->border);
1032             cell = cell->member.cell.next_cell;
1033             if (!cell)
1034             {
1035               para = ME_InsertTableCellFromCursor(info->editor);
1036               cell = para->member.para.pCell;
1037             }
1038           }
1039           /* Cell for table row delimiter is empty */
1040           cell->member.cell.nRightBoundary = tableDef->cells[i-1].rightBoundary;
1041         }
1042 
1043         run = ME_FindItemFwd(cell, diRun);
1044         if (info->editor->pCursors[0].pRun != run ||
1045             info->editor->pCursors[0].nOffset)
1046         {
1047           int nOfs, nChars;
1048           /* Delete inserted cells that aren't defined. */
1049           info->editor->pCursors[1].pRun = run;
1050           info->editor->pCursors[1].pPara = ME_GetParagraph(run);
1051           info->editor->pCursors[1].nOffset = 0;
1052           nOfs = ME_GetCursorOfs(&info->editor->pCursors[1]);
1053           nChars = ME_GetCursorOfs(&info->editor->pCursors[0]) - nOfs;
1054           ME_InternalDeleteText(info->editor, &info->editor->pCursors[1],
1055                                 nChars, TRUE);
1056         }
1057 
1058         para = ME_InsertTableRowEndFromCursor(info->editor);
1059         para->member.para.fmt.dxOffset = abs(info->tableDef->gapH);
1060         para->member.para.fmt.dxStartIndent = info->tableDef->leftEdge;
1061         ME_ApplyBorderProperties(info, &para->member.para.border,
1062                                  tableDef->border);
1063         info->nestingLevel--;
1064         if (!info->nestingLevel)
1065         {
1066           if (info->canInheritInTbl) {
1067             tableDef->tableRowStart = para;
1068           } else {
1069             while (info->tableDef) {
1070               tableDef = info->tableDef;
1071               info->tableDef = tableDef->parent;
1072               heap_free(tableDef);
1073             }
1074           }
1075         } else {
1076           info->tableDef = tableDef->parent;
1077           heap_free(tableDef);
1078         }
1079       } else { /* v1.0 - v3.0 */
1080         WCHAR endl = '\r';
1081         ME_DisplayItem *para = info->editor->pCursors[0].pPara;
1082         PARAFORMAT2 *pFmt = &para->member.para.fmt;
1083         pFmt->dxOffset = info->tableDef->gapH;
1084         pFmt->dxStartIndent = info->tableDef->leftEdge;
1085 
1086         ME_ApplyBorderProperties(info, &para->member.para.border,
1087                                  tableDef->border);
1088         while (tableDef->numCellsInserted < tableDef->numCellsDefined)
1089         {
1090           WCHAR tab = '\t';
1091           ME_InsertTextFromCursor(info->editor, 0, &tab, 1, info->style);
1092           tableDef->numCellsInserted++;
1093         }
1094         pFmt->cTabCount = min(tableDef->numCellsDefined, MAX_TAB_STOPS);
1095         if (!tableDef->numCellsDefined)
1096           pFmt->wEffects &= ~PFE_TABLE;
1097         ME_InsertTextFromCursor(info->editor, 0, &endl, 1, info->style);
1098         tableDef->numCellsInserted = 0;
1099       }
1100       break;
1101     }
1102     case rtfTab:
1103     case rtfPar:
1104       if (info->editor->bEmulateVersion10) { /* v1.0 - 3.0 */
1105         ME_DisplayItem *para;
1106         PARAFORMAT2 *pFmt;
1107         RTFFlushOutputBuffer(info);
1108         para = info->editor->pCursors[0].pPara;
1109         pFmt = &para->member.para.fmt;
1110         if (pFmt->dwMask & PFM_TABLE && pFmt->wEffects & PFE_TABLE)
1111         {
1112           /* rtfPar is treated like a space within a table. */
1113           info->rtfClass = rtfText;
1114           info->rtfMajor = ' ';
1115         }
1116         else if (info->rtfMinor == rtfPar && tableDef)
1117           tableDef->numCellsInserted = 0;
1118       }
1119       break;
1120   }
1121 }
1122 
1123 static HRESULT insert_static_object(ME_TextEditor *editor, HENHMETAFILE hemf, HBITMAP hbmp,
1124                                     const SIZEL* sz)
1125 {
1126   LPOLEOBJECT         lpObject = NULL;
1127   LPSTORAGE           lpStorage = NULL;
1128   LPOLECLIENTSITE     lpClientSite = NULL;
1129   LPDATAOBJECT        lpDataObject = NULL;
1130   LPOLECACHE          lpOleCache = NULL;
1131   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 handle_enter(ME_TextEditor *editor)
2435 {
2436     BOOL ctrl_is_down = GetKeyState(VK_CONTROL) & 0x8000;
2437     BOOL shift_is_down = GetKeyState(VK_SHIFT) & 0x8000;
2438 
2439     if (editor->bDialogMode)
2440     {
2441         if (ctrl_is_down)
2442             return TRUE;
2443 
2444         if (!(editor->styleFlags & ES_WANTRETURN))
2445         {
2446             if (editor->hwndParent)
2447             {
2448                 DWORD dw;
2449                 dw = SendMessageW(editor->hwndParent, DM_GETDEFID, 0, 0);
2450                 if (HIWORD(dw) == DC_HASDEFID)
2451                 {
2452                     HWND hwDefCtrl = GetDlgItem(editor->hwndParent, LOWORD(dw));
2453                     if (hwDefCtrl)
2454                     {
2455                         SendMessageW(editor->hwndParent, WM_NEXTDLGCTL, (WPARAM)hwDefCtrl, TRUE);
2456                         PostMessageW(hwDefCtrl, WM_KEYDOWN, VK_RETURN, 0);
2457                     }
2458                 }
2459             }
2460             return TRUE;
2461         }
2462     }
2463 
2464     if (editor->styleFlags & ES_MULTILINE)
2465     {
2466         static const WCHAR endl = '\r';
2467         static const WCHAR endlv10[] = {'\r','\n'};
2468         ME_Cursor cursor = editor->pCursors[0];
2469         ME_DisplayItem *para = cursor.pPara;
2470         int from, to;
2471         ME_Style *style, *eop_style;
2472 
2473         if (editor->styleFlags & ES_READONLY)
2474         {
2475             MessageBeep(MB_ICONERROR);
2476             return TRUE;
2477         }
2478 
2479         ME_GetSelectionOfs(editor, &from, &to);
2480         if (editor->nTextLimit > ME_GetTextLength(editor) - (to-from))
2481         {
2482             if (!editor->bEmulateVersion10) /* v4.1 */
2483             {
2484                 if (para->member.para.nFlags & MEPF_ROWEND)
2485                 {
2486                     /* Add a new table row after this row. */
2487                     para = ME_AppendTableRow(editor, para);
2488                     para = para->member.para.next_para;
2489                     editor->pCursors[0].pPara = para;
2490                     editor->pCursors[0].pRun = ME_FindItemFwd(para, diRun);
2491                     editor->pCursors[0].nOffset = 0;
2492                     editor->pCursors[1] = editor->pCursors[0];
2493                     ME_CommitUndo(editor);
2494                     ME_CheckTablesForCorruption(editor);
2495                     ME_UpdateRepaint(editor, FALSE);
2496                     return TRUE;
2497                 }
2498                 else if (para == editor->pCursors[1].pPara &&
2499                     cursor.nOffset + cursor.pRun->member.run.nCharOfs == 0 &&
2500                     para->member.para.prev_para->member.para.nFlags & MEPF_ROWSTART &&
2501                     !para->member.para.prev_para->member.para.nCharOfs)
2502                 {
2503                     /* Insert a newline before the table. */
2504                     para = para->member.para.prev_para;
2505                     para->member.para.nFlags &= ~MEPF_ROWSTART;
2506                     editor->pCursors[0].pPara = para;
2507                     editor->pCursors[0].pRun = ME_FindItemFwd(para, diRun);
2508                     editor->pCursors[1] = editor->pCursors[0];
2509                     ME_InsertTextFromCursor(editor, 0, &endl, 1,
2510                     editor->pCursors[0].pRun->member.run.style);
2511                     para = editor->pBuffer->pFirst->member.para.next_para;
2512                     ME_SetDefaultParaFormat(editor, &para->member.para.fmt);
2513                     para->member.para.nFlags = 0;
2514                     mark_para_rewrap(editor, para);
2515                     editor->pCursors[0].pPara = para;
2516                     editor->pCursors[0].pRun = ME_FindItemFwd(para, diRun);
2517                     editor->pCursors[1] = editor->pCursors[0];
2518                     para->member.para.next_para->member.para.nFlags |= MEPF_ROWSTART;
2519                     ME_CommitCoalescingUndo(editor);
2520                     ME_CheckTablesForCorruption(editor);
2521                     ME_UpdateRepaint(editor, FALSE);
2522                     return TRUE;
2523                 }
2524             }
2525             else /* v1.0 - 3.0 */
2526             {
2527                 ME_DisplayItem *para = cursor.pPara;
2528                 if (ME_IsInTable(para))
2529                 {
2530                     if (cursor.pRun->member.run.nFlags & MERF_ENDPARA)
2531                     {
2532                         if (from == to)
2533                         {
2534                             ME_ContinueCoalescingTransaction(editor);
2535                             para = ME_AppendTableRow(editor, para);
2536                             editor->pCursors[0].pPara = para;
2537                             editor->pCursors[0].pRun = ME_FindItemFwd(para, diRun);
2538                             editor->pCursors[0].nOffset = 0;
2539                             editor->pCursors[1] = editor->pCursors[0];
2540                             ME_CommitCoalescingUndo(editor);
2541                             ME_UpdateRepaint(editor, FALSE);
2542                             return TRUE;
2543                         }
2544                     }
2545                     else
2546                     {
2547                         ME_ContinueCoalescingTransaction(editor);
2548                         if (cursor.pRun->member.run.nCharOfs + cursor.nOffset == 0 &&
2549                                 !ME_IsInTable(para->member.para.prev_para))
2550                         {
2551                             /* Insert newline before table */
2552                             cursor.pRun = ME_FindItemBack(para, diRun);
2553                             if (cursor.pRun)
2554                             {
2555                                 editor->pCursors[0].pRun = cursor.pRun;
2556                                 editor->pCursors[0].pPara = para->member.para.prev_para;
2557                             }
2558                             editor->pCursors[0].nOffset = 0;
2559                             editor->pCursors[1] = editor->pCursors[0];
2560                             ME_InsertTextFromCursor(editor, 0, &endl, 1,
2561                             editor->pCursors[0].pRun->member.run.style);
2562                         }
2563                         else
2564                         {
2565                             editor->pCursors[1] = editor->pCursors[0];
2566                             para = ME_AppendTableRow(editor, para);
2567                             editor->pCursors[0].pPara = para;
2568                             editor->pCursors[0].pRun = ME_FindItemFwd(para, diRun);
2569                             editor->pCursors[0].nOffset = 0;
2570                             editor->pCursors[1] = editor->pCursors[0];
2571                         }
2572                         ME_CommitCoalescingUndo(editor);
2573                         ME_UpdateRepaint(editor, FALSE);
2574                         return TRUE;
2575                     }
2576                 }
2577             }
2578 
2579             style = ME_GetInsertStyle(editor, 0);
2580 
2581             /* Normally the new eop style is the insert style, however in a list it is copied from the existing
2582             eop style (this prevents the list label style changing when the new eop is inserted).
2583             No extra ref is taken here on eop_style. */
2584             if (para->member.para.fmt.wNumbering)
2585                 eop_style = para->member.para.eop_run->style;
2586             else
2587                 eop_style = style;
2588             ME_ContinueCoalescingTransaction(editor);
2589             if (shift_is_down)
2590                 ME_InsertEndRowFromCursor(editor, 0);
2591             else
2592                 if (!editor->bEmulateVersion10)
2593                     ME_InsertTextFromCursor(editor, 0, &endl, 1, eop_style);
2594                 else
2595                     ME_InsertTextFromCursor(editor, 0, endlv10, 2, eop_style);
2596             ME_CommitCoalescingUndo(editor);
2597             SetCursor(NULL);
2598 
2599             ME_UpdateSelectionLinkAttribute(editor);
2600             ME_UpdateRepaint(editor, FALSE);
2601             ME_SaveTempStyle(editor, style); /* set the temp insert style for the new para */
2602             ME_ReleaseStyle(style);
2603         }
2604         return TRUE;
2605     }
2606     return FALSE;
2607 }
2608 
2609 static BOOL
2610 ME_KeyDown(ME_TextEditor *editor, WORD nKey)
2611 {
2612   BOOL ctrl_is_down = GetKeyState(VK_CONTROL) & 0x8000;
2613   BOOL shift_is_down = GetKeyState(VK_SHIFT) & 0x8000;
2614 
2615   if (editor->bMouseCaptured)
2616       return FALSE;
2617   if (nKey != VK_SHIFT && nKey != VK_CONTROL && nKey != VK_MENU)
2618       editor->nSelectionType = stPosition;
2619 
2620   switch (nKey)
2621   {
2622     case VK_LEFT:
2623     case VK_RIGHT:
2624     case VK_HOME:
2625     case VK_END:
2626         editor->nUDArrowX = -1;
2627         /* fall through */
2628     case VK_UP:
2629     case VK_DOWN:
2630     case VK_PRIOR:
2631     case VK_NEXT:
2632       ME_CommitUndo(editor); /* End coalesced undos for typed characters */
2633       ME_ArrowKey(editor, nKey, shift_is_down, ctrl_is_down);
2634       return TRUE;
2635     case VK_BACK:
2636     case VK_DELETE:
2637       editor->nUDArrowX = -1;
2638       /* FIXME backspace and delete aren't the same, they act different wrt paragraph style of the merged paragraph */
2639       if (editor->styleFlags & ES_READONLY)
2640         return FALSE;
2641       if (ME_IsSelection(editor))
2642       {
2643         ME_DeleteSelection(editor);
2644         ME_CommitUndo(editor);
2645       }
2646       else if (nKey == VK_DELETE)
2647       {
2648         /* Delete stops group typing.
2649          * (See MSDN remarks on EM_STOPGROUPTYPING message) */
2650         ME_DeleteTextAtCursor(editor, 1, 1);
2651         ME_CommitUndo(editor);
2652       }
2653       else if (ME_ArrowKey(editor, VK_LEFT, FALSE, FALSE))
2654       {
2655         BOOL bDeletionSucceeded;
2656         /* Backspace can be grouped for a single undo */
2657         ME_ContinueCoalescingTransaction(editor);
2658         bDeletionSucceeded = ME_DeleteTextAtCursor(editor, 1, 1);
2659         if (!bDeletionSucceeded && !editor->bEmulateVersion10) { /* v4.1 */
2660           /* Deletion was prevented so the cursor is moved back to where it was.
2661            * (e.g. this happens when trying to delete cell boundaries)
2662            */
2663           ME_ArrowKey(editor, VK_RIGHT, FALSE, FALSE);
2664         }
2665         ME_CommitCoalescingUndo(editor);
2666       }
2667       else
2668         return TRUE;
2669       ME_MoveCursorFromTableRowStartParagraph(editor);
2670       ME_UpdateSelectionLinkAttribute(editor);
2671       ME_UpdateRepaint(editor, FALSE);
2672       ME_SendRequestResize(editor, FALSE);
2673       return TRUE;
2674     case VK_RETURN:
2675       if (!editor->bEmulateVersion10)
2676           return handle_enter(editor);
2677       break;
2678     case VK_ESCAPE:
2679       if (editor->bDialogMode && editor->hwndParent)
2680         PostMessageW(editor->hwndParent, WM_CLOSE, 0, 0);
2681       return TRUE;
2682     case VK_TAB:
2683       if (editor->bDialogMode && editor->hwndParent)
2684         SendMessageW(editor->hwndParent, WM_NEXTDLGCTL, shift_is_down, 0);
2685       return TRUE;
2686     case 'A':
2687       if (ctrl_is_down)
2688       {
2689         handle_EM_EXSETSEL( editor, 0, -1 );
2690         return TRUE;
2691       }
2692       break;
2693     case 'V':
2694       if (ctrl_is_down)
2695         return paste_special( editor, 0, NULL, FALSE );
2696       break;
2697     case 'C':
2698     case 'X':
2699       if (ctrl_is_down)
2700         return copy_or_cut(editor, nKey == 'X');
2701       break;
2702     case 'Z':
2703       if (ctrl_is_down)
2704       {
2705         ME_Undo(editor);
2706         return TRUE;
2707       }
2708       break;
2709     case 'Y':
2710       if (ctrl_is_down)
2711       {
2712         ME_Redo(editor);
2713         return TRUE;
2714       }
2715       break;
2716 
2717     default:
2718       if (nKey != VK_SHIFT && nKey != VK_CONTROL && nKey && nKey != VK_MENU)
2719           editor->nUDArrowX = -1;
2720       if (ctrl_is_down)
2721       {
2722         if (nKey == 'W')
2723         {
2724           CHARFORMAT2W chf;
2725           char buf[2048];
2726           chf.cbSize = sizeof(chf);
2727 
2728           ME_GetSelectionCharFormat(editor, &chf);
2729           ME_DumpStyleToBuf(&chf, buf);
2730           MessageBoxA(NULL, buf, "Style dump", MB_OK);
2731         }
2732         if (nKey == 'Q')
2733         {
2734           ME_CheckCharOffsets(editor);
2735         }
2736       }
2737   }
2738   return FALSE;
2739 }
2740 
2741 static LRESULT ME_Char(ME_TextEditor *editor, WPARAM charCode,
2742                        LPARAM flags, BOOL unicode)
2743 {
2744   WCHAR wstr;
2745 
2746   if (editor->bMouseCaptured)
2747     return 0;
2748 
2749   if (editor->styleFlags & ES_READONLY)
2750   {
2751     MessageBeep(MB_ICONERROR);
2752     return 0; /* FIXME really 0 ? */
2753   }
2754 
2755   if (unicode)
2756       wstr = (WCHAR)charCode;
2757   else
2758   {
2759       CHAR charA = charCode;
2760       MultiByteToWideChar(CP_ACP, 0, &charA, 1, &wstr, 1);
2761   }
2762 
2763   if (editor->bEmulateVersion10 && wstr == '\r')
2764       handle_enter(editor);
2765 
2766   if ((unsigned)wstr >= ' ' || wstr == '\t')
2767   {
2768     ME_Cursor cursor = editor->pCursors[0];
2769     ME_DisplayItem *para = cursor.pPara;
2770     int from, to;
2771     BOOL ctrl_is_down = GetKeyState(VK_CONTROL) & 0x8000;
2772     ME_GetSelectionOfs(editor, &from, &to);
2773     if (wstr == '\t' &&
2774         /* v4.1 allows tabs to be inserted with ctrl key down */
2775         !(ctrl_is_down && !editor->bEmulateVersion10))
2776     {
2777       ME_DisplayItem *para;
2778       BOOL bSelectedRow = FALSE;
2779 
2780       para = cursor.pPara;
2781       if (ME_IsSelection(editor) &&
2782           cursor.pRun->member.run.nCharOfs + cursor.nOffset == 0 &&
2783           to == ME_GetCursorOfs(&editor->pCursors[0]) &&
2784           para->member.para.prev_para->type == diParagraph)
2785       {
2786         para = para->member.para.prev_para;
2787         bSelectedRow = TRUE;
2788       }
2789       if (ME_IsInTable(para))
2790       {
2791         ME_TabPressedInTable(editor, bSelectedRow);
2792         ME_CommitUndo(editor);
2793         return 0;
2794       }
2795     } else if (!editor->bEmulateVersion10) { /* v4.1 */
2796       if (para->member.para.nFlags & MEPF_ROWEND) {
2797         if (from == to) {
2798           para = para->member.para.next_para;
2799           if (para->member.para.nFlags & MEPF_ROWSTART)
2800             para = para->member.para.next_para;
2801           editor->pCursors[0].pPara = para;
2802           editor->pCursors[0].pRun = ME_FindItemFwd(para, diRun);
2803           editor->pCursors[0].nOffset = 0;
2804           editor->pCursors[1] = editor->pCursors[0];
2805         }
2806       }
2807     } else { /* v1.0 - 3.0 */
2808       if (ME_IsInTable(cursor.pRun) &&
2809           cursor.pRun->member.run.nFlags & MERF_ENDPARA &&
2810           from == to)
2811       {
2812         /* Text should not be inserted at the end of the table. */
2813         MessageBeep(-1);
2814         return 0;
2815       }
2816     }
2817     /* FIXME maybe it would make sense to call EM_REPLACESEL instead ? */
2818     /* WM_CHAR is restricted to nTextLimit */
2819     if(editor->nTextLimit > ME_GetTextLength(editor) - (to-from))
2820     {
2821       ME_Style *style = ME_GetInsertStyle(editor, 0);
2822       ME_ContinueCoalescingTransaction(editor);
2823       ME_InsertTextFromCursor(editor, 0, &wstr, 1, style);
2824       ME_ReleaseStyle(style);
2825       ME_CommitCoalescingUndo(editor);
2826       ITextHost_TxSetCursor(editor->texthost, NULL, FALSE);
2827     }
2828 
2829     ME_UpdateSelectionLinkAttribute(editor);
2830     ME_UpdateRepaint(editor, FALSE);
2831   }
2832   return 0;
2833 }
2834 
2835 /* Process the message and calculate the new click count.
2836  *
2837  * returns: The click count if it is mouse down event, else returns 0. */
2838 static int ME_CalculateClickCount(ME_TextEditor *editor, UINT msg, WPARAM wParam,
2839                                   LPARAM lParam)
2840 {
2841     static int clickNum = 0;
2842     if (msg < WM_MOUSEFIRST || msg > WM_MOUSELAST)
2843         return 0;
2844 
2845     if ((msg == WM_LBUTTONDBLCLK) ||
2846         (msg == WM_RBUTTONDBLCLK) ||
2847         (msg == WM_MBUTTONDBLCLK) ||
2848         (msg == WM_XBUTTONDBLCLK))
2849     {
2850         msg -= (WM_LBUTTONDBLCLK - WM_LBUTTONDOWN);
2851     }
2852 
2853     if ((msg == WM_LBUTTONDOWN) ||
2854         (msg == WM_RBUTTONDOWN) ||
2855         (msg == WM_MBUTTONDOWN) ||
2856         (msg == WM_XBUTTONDOWN))
2857     {
2858         static MSG prevClickMsg;
2859         MSG clickMsg;
2860         /* Compare the editor instead of the hwnd so that the this
2861          * can still be done for windowless richedit controls. */
2862         clickMsg.hwnd = (HWND)editor;
2863         clickMsg.message = msg;
2864         clickMsg.wParam = wParam;
2865         clickMsg.lParam = lParam;
2866         clickMsg.time = GetMessageTime();
2867         clickMsg.pt.x = (short)LOWORD(lParam);
2868         clickMsg.pt.y = (short)HIWORD(lParam);
2869         if ((clickNum != 0) &&
2870             (clickMsg.message == prevClickMsg.message) &&
2871             (clickMsg.hwnd == prevClickMsg.hwnd) &&
2872             (clickMsg.wParam == prevClickMsg.wParam) &&
2873             (clickMsg.time - prevClickMsg.time < GetDoubleClickTime()) &&
2874             (abs(clickMsg.pt.x - prevClickMsg.pt.x) < GetSystemMetrics(SM_CXDOUBLECLK)/2) &&
2875             (abs(clickMsg.pt.y - prevClickMsg.pt.y) < GetSystemMetrics(SM_CYDOUBLECLK)/2))
2876         {
2877             clickNum++;
2878         } else {
2879             clickNum = 1;
2880         }
2881         prevClickMsg = clickMsg;
2882     } else {
2883         return 0;
2884     }
2885     return clickNum;
2886 }
2887 
2888 static BOOL is_link( ME_Run *run )
2889 {
2890     return (run->style->fmt.dwMask & CFM_LINK) && (run->style->fmt.dwEffects & CFE_LINK);
2891 }
2892 
2893 static BOOL ME_SetCursor(ME_TextEditor *editor)
2894 {
2895   ME_Cursor cursor;
2896   POINT pt;
2897   BOOL isExact;
2898   SCROLLBARINFO sbi;
2899   DWORD messagePos = GetMessagePos();
2900   pt.x = (short)LOWORD(messagePos);
2901   pt.y = (short)HIWORD(messagePos);
2902 
2903   if (editor->hWnd)
2904   {
2905     sbi.cbSize = sizeof(sbi);
2906     GetScrollBarInfo(editor->hWnd, OBJID_HSCROLL, &sbi);
2907     if (!(sbi.rgstate[0] & (STATE_SYSTEM_INVISIBLE|STATE_SYSTEM_OFFSCREEN)) &&
2908         PtInRect(&sbi.rcScrollBar, pt))
2909     {
2910         ITextHost_TxSetCursor(editor->texthost,
2911                               LoadCursorW(NULL, (WCHAR*)IDC_ARROW), FALSE);
2912         return TRUE;
2913     }
2914     sbi.cbSize = sizeof(sbi);
2915     GetScrollBarInfo(editor->hWnd, OBJID_VSCROLL, &sbi);
2916     if (!(sbi.rgstate[0] & (STATE_SYSTEM_INVISIBLE|STATE_SYSTEM_OFFSCREEN)) &&
2917         PtInRect(&sbi.rcScrollBar, pt))
2918     {
2919         ITextHost_TxSetCursor(editor->texthost,
2920                               LoadCursorW(NULL, (WCHAR*)IDC_ARROW), FALSE);
2921         return TRUE;
2922     }
2923   }
2924   ITextHost_TxScreenToClient(editor->texthost, &pt);
2925 
2926   if (editor->nSelectionType == stLine && editor->bMouseCaptured) {
2927       ITextHost_TxSetCursor(editor->texthost, hLeft, FALSE);
2928       return TRUE;
2929   }
2930   if (!editor->bEmulateVersion10 /* v4.1 */ &&
2931       pt.y < editor->rcFormat.top &&
2932       pt.x < editor->rcFormat.left)
2933   {
2934       ITextHost_TxSetCursor(editor->texthost, hLeft, FALSE);
2935       return TRUE;
2936   }
2937   if (pt.y < editor->rcFormat.top || pt.y > editor->rcFormat.bottom)
2938   {
2939       if (editor->bEmulateVersion10) /* v1.0 - 3.0 */
2940           ITextHost_TxSetCursor(editor->texthost,
2941                                 LoadCursorW(NULL, (WCHAR*)IDC_ARROW), FALSE);
2942       else /* v4.1 */
2943           ITextHost_TxSetCursor(editor->texthost,
2944                                 LoadCursorW(NULL, (WCHAR*)IDC_IBEAM), TRUE);
2945       return TRUE;
2946   }
2947   if (pt.x < editor->rcFormat.left)
2948   {
2949       ITextHost_TxSetCursor(editor->texthost, hLeft, FALSE);
2950       return TRUE;
2951   }
2952   ME_CharFromPos(editor, pt.x, pt.y, &cursor, &isExact);
2953   if (isExact)
2954   {
2955       ME_Run *run;
2956 
2957       run = &cursor.pRun->member.run;
2958       if (is_link( run ))
2959       {
2960           ITextHost_TxSetCursor(editor->texthost,
2961                                 LoadCursorW(NULL, (WCHAR*)IDC_HAND),
2962                                 FALSE);
2963           return TRUE;
2964       }
2965 
2966       if (ME_IsSelection(editor))
2967       {
2968           int selStart, selEnd;
2969           int offset = ME_GetCursorOfs(&cursor);
2970 
2971           ME_GetSelectionOfs(editor, &selStart, &selEnd);
2972           if (selStart <= offset && selEnd >= offset) {
2973               ITextHost_TxSetCursor(editor->texthost,
2974                                     LoadCursorW(NULL, (WCHAR*)IDC_ARROW),
2975                                     FALSE);
2976               return TRUE;
2977           }
2978       }
2979   }
2980   ITextHost_TxSetCursor(editor->texthost,
2981                         LoadCursorW(NULL, (WCHAR*)IDC_IBEAM), TRUE);
2982   return TRUE;
2983 }
2984 
2985 static void ME_SetDefaultFormatRect(ME_TextEditor *editor)
2986 {
2987   ITextHost_TxGetClientRect(editor->texthost, &editor->rcFormat);
2988   editor->rcFormat.top += editor->exStyleFlags & WS_EX_CLIENTEDGE ? 1 : 0;
2989   editor->rcFormat.left += 1 + editor->selofs;
2990   editor->rcFormat.right -= 1;
2991 }
2992 
2993 static LONG ME_GetSelectionType(ME_TextEditor *editor)
2994 {
2995     LONG sel_type = SEL_EMPTY;
2996     LONG start, end;
2997 
2998     ME_GetSelectionOfs(editor, &start, &end);
2999     if (start == end)
3000         sel_type = SEL_EMPTY;
3001     else
3002     {
3003         LONG object_count = 0, character_count = 0;
3004         int i;
3005 
3006         for (i = 0; i < end - start; i++)
3007         {
3008             ME_Cursor cursor;
3009 
3010             ME_CursorFromCharOfs(editor, start + i, &cursor);
3011             if (cursor.pRun->member.run.reobj)
3012                 object_count++;
3013             else
3014                 character_count++;
3015             if (character_count >= 2 && object_count >= 2)
3016                 return (SEL_TEXT | SEL_MULTICHAR | SEL_OBJECT | SEL_MULTIOBJECT);
3017         }
3018         if (character_count)
3019         {
3020             sel_type |= SEL_TEXT;
3021             if (character_count >= 2)
3022                 sel_type |= SEL_MULTICHAR;
3023         }
3024         if (object_count)
3025         {
3026             sel_type |= SEL_OBJECT;
3027             if (object_count >= 2)
3028                 sel_type |= SEL_MULTIOBJECT;
3029         }
3030     }
3031     return sel_type;
3032 }
3033 
3034 static BOOL ME_ShowContextMenu(ME_TextEditor *editor, int x, int y)
3035 {
3036   CHARRANGE selrange;
3037   HMENU menu;
3038   int seltype;
3039 
3040   if(!editor->lpOleCallback || !editor->hWnd)
3041     return FALSE;
3042   ME_GetSelectionOfs(editor, &selrange.cpMin, &selrange.cpMax);
3043   seltype = ME_GetSelectionType(editor);
3044   if(SUCCEEDED(IRichEditOleCallback_GetContextMenu(editor->lpOleCallback, seltype, NULL, &selrange, &menu)))
3045   {
3046     TrackPopupMenu(menu, TPM_LEFTALIGN | TPM_RIGHTBUTTON, x, y, 0, editor->hwndParent, NULL);
3047     DestroyMenu(menu);
3048   }
3049   return TRUE;
3050 }
3051 
3052 ME_TextEditor *ME_MakeEditor(ITextHost *texthost, BOOL bEmulateVersion10)
3053 {
3054   ME_TextEditor *ed = heap_alloc(sizeof(*ed));
3055   int i;
3056   DWORD props;
3057   LONG selbarwidth;
3058 
3059   ed->hWnd = NULL;
3060   ed->hwndParent = NULL;
3061   ed->sizeWindow.cx = ed->sizeWindow.cy = 0;
3062   ed->texthost = texthost;
3063   ed->reOle = NULL;
3064   ed->bEmulateVersion10 = bEmulateVersion10;
3065   ed->styleFlags = 0;
3066   ed->exStyleFlags = 0;
3067   ed->first_marked_para = NULL;
3068   ed->total_rows = 0;
3069   ITextHost_TxGetPropertyBits(texthost,
3070                               (TXTBIT_RICHTEXT|TXTBIT_MULTILINE|
3071                                TXTBIT_READONLY|TXTBIT_USEPASSWORD|
3072                                TXTBIT_HIDESELECTION|TXTBIT_SAVESELECTION|
3073                                TXTBIT_AUTOWORDSEL|TXTBIT_VERTICAL|
3074                                TXTBIT_WORDWRAP|TXTBIT_DISABLEDRAG),
3075                               &props);
3076   ITextHost_TxGetScrollBars(texthost, &ed->styleFlags);
3077   ed->styleFlags &= (WS_VSCROLL|WS_HSCROLL|ES_AUTOVSCROLL|
3078                      ES_AUTOHSCROLL|ES_DISABLENOSCROLL);
3079   ed->pBuffer = ME_MakeText();
3080   ed->nZoomNumerator = ed->nZoomDenominator = 0;
3081   ed->nAvailWidth = 0; /* wrap to client area */
3082   list_init( &ed->style_list );
3083   ME_MakeFirstParagraph(ed);
3084   /* The four cursors are for:
3085    * 0 - The position where the caret is shown
3086    * 1 - The anchored end of the selection (for normal selection)
3087    * 2 & 3 - The anchored start and end respectively for word, line,
3088    * or paragraph selection.
3089    */
3090   ed->nCursors = 4;
3091   ed->pCursors = heap_alloc(ed->nCursors * sizeof(*ed->pCursors));
3092   ME_SetCursorToStart(ed, &ed->pCursors[0]);
3093   ed->pCursors[1] = ed->pCursors[0];
3094   ed->pCursors[2] = ed->pCursors[0];
3095   ed->pCursors[3] = ed->pCursors[1];
3096   ed->nLastTotalLength = ed->nTotalLength = 0;
3097   ed->nLastTotalWidth = ed->nTotalWidth = 0;
3098   ed->nUDArrowX = -1;
3099   ed->rgbBackColor = -1;
3100   ed->hbrBackground = GetSysColorBrush(COLOR_WINDOW);
3101   ed->bCaretAtEnd = FALSE;
3102   ed->nEventMask = 0;
3103   ed->nModifyStep = 0;
3104   ed->nTextLimit = TEXT_LIMIT_DEFAULT;
3105   list_init( &ed->undo_stack );
3106   list_init( &ed->redo_stack );
3107   ed->nUndoStackSize = 0;
3108   ed->nUndoLimit = STACK_SIZE_DEFAULT;
3109   ed->nUndoMode = umAddToUndo;
3110   ed->nParagraphs = 1;
3111   ed->nLastSelStart = ed->nLastSelEnd = 0;
3112   ed->pLastSelStartPara = ed->pLastSelEndPara = ed->pCursors[0].pPara;
3113   ed->bHideSelection = FALSE;
3114   ed->pfnWordBreak = NULL;
3115   ed->lpOleCallback = NULL;
3116   ed->mode = TM_MULTILEVELUNDO | TM_MULTICODEPAGE;
3117   ed->mode |= (props & TXTBIT_RICHTEXT) ? TM_RICHTEXT : TM_PLAINTEXT;
3118   ed->AutoURLDetect_bEnable = FALSE;
3119   ed->bHaveFocus = FALSE;
3120   ed->bDialogMode = FALSE;
3121   ed->bMouseCaptured = FALSE;
3122   for (i=0; i<HFONT_CACHE_SIZE; i++)
3123   {
3124     ed->pFontCache[i].nRefs = 0;
3125     ed->pFontCache[i].nAge = 0;
3126     ed->pFontCache[i].hFont = NULL;
3127   }
3128 
3129   ME_CheckCharOffsets(ed);
3130   SetRectEmpty(&ed->rcFormat);
3131   ed->bDefaultFormatRect = TRUE;
3132   ITextHost_TxGetSelectionBarWidth(ed->texthost, &selbarwidth);
3133   if (selbarwidth) {
3134     /* FIXME: Convert selbarwidth from HIMETRIC to pixels */
3135     ed->selofs = SELECTIONBAR_WIDTH;
3136     ed->styleFlags |= ES_SELECTIONBAR;
3137   } else {
3138     ed->selofs = 0;
3139   }
3140   ed->nSelectionType = stPosition;
3141 
3142   ed->cPasswordMask = 0;
3143   if (props & TXTBIT_USEPASSWORD)
3144     ITextHost_TxGetPasswordChar(texthost, &ed->cPasswordMask);
3145 
3146   if (props & TXTBIT_AUTOWORDSEL)
3147     ed->styleFlags |= ECO_AUTOWORDSELECTION;
3148   if (props & TXTBIT_MULTILINE) {
3149     ed->styleFlags |= ES_MULTILINE;
3150     ed->bWordWrap = (props & TXTBIT_WORDWRAP) != 0;
3151   } else {
3152     ed->bWordWrap = FALSE;
3153   }
3154   if (props & TXTBIT_READONLY)
3155     ed->styleFlags |= ES_READONLY;
3156   if (!(props & TXTBIT_HIDESELECTION))
3157     ed->styleFlags |= ES_NOHIDESEL;
3158   if (props & TXTBIT_SAVESELECTION)
3159     ed->styleFlags |= ES_SAVESEL;
3160   if (props & TXTBIT_VERTICAL)
3161     ed->styleFlags |= ES_VERTICAL;
3162   if (props & TXTBIT_DISABLEDRAG)
3163     ed->styleFlags |= ES_NOOLEDRAGDROP;
3164 
3165   ed->notified_cr.cpMin = ed->notified_cr.cpMax = 0;
3166 
3167   /* Default scrollbar information */
3168   ed->vert_si.cbSize = sizeof(SCROLLINFO);
3169   ed->vert_si.nMin = 0;
3170   ed->vert_si.nMax = 0;
3171   ed->vert_si.nPage = 0;
3172   ed->vert_si.nPos = 0;
3173 
3174   ed->horz_si.cbSize = sizeof(SCROLLINFO);
3175   ed->horz_si.nMin = 0;
3176   ed->horz_si.nMax = 0;
3177   ed->horz_si.nPage = 0;
3178   ed->horz_si.nPos = 0;
3179 
3180   ed->wheel_remain = 0;
3181 
3182   list_init( &ed->reobj_list );
3183   OleInitialize(NULL);
3184 
3185   return ed;
3186 }
3187 
3188 void ME_DestroyEditor(ME_TextEditor *editor)
3189 {
3190   ME_DisplayItem *p = editor->pBuffer->pFirst, *pNext = NULL;
3191   ME_Style *s, *cursor2;
3192   int i;
3193 
3194   ME_ClearTempStyle(editor);
3195   ME_EmptyUndoStack(editor);
3196   editor->pBuffer->pFirst = NULL;
3197   while(p) {
3198     pNext = p->next;
3199     if (p->type == diParagraph)
3200       destroy_para(editor, p);
3201     else
3202       ME_DestroyDisplayItem(p);
3203     p = pNext;
3204   }
3205 
3206   LIST_FOR_EACH_ENTRY_SAFE( s, cursor2, &editor->style_list, ME_Style, entry )
3207       ME_DestroyStyle( s );
3208 
3209   ME_ReleaseStyle(editor->pBuffer->pDefaultStyle);
3210   for (i=0; i<HFONT_CACHE_SIZE; i++)
3211   {
3212     if (editor->pFontCache[i].hFont)
3213       DeleteObject(editor->pFontCache[i].hFont);
3214   }
3215   if (editor->rgbBackColor != -1)
3216     DeleteObject(editor->hbrBackground);
3217   if(editor->lpOleCallback)
3218     IRichEditOleCallback_Release(editor->lpOleCallback);
3219   ITextHost_Release(editor->texthost);
3220   if (editor->reOle)
3221   {
3222     IRichEditOle_Release(editor->reOle);
3223     editor->reOle = NULL;
3224   }
3225   OleUninitialize();
3226 
3227   heap_free(editor->pBuffer);
3228   heap_free(editor->pCursors);
3229   heap_free(editor);
3230 }
3231 
3232 BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
3233 {
3234     TRACE("\n");
3235     switch (fdwReason)
3236     {
3237     case DLL_PROCESS_ATTACH:
3238       DisableThreadLibraryCalls(hinstDLL);
3239       me_heap = HeapCreate (0, 0x10000, 0);
3240       if (!ME_RegisterEditorClass(hinstDLL)) return FALSE;
3241       hLeft = LoadCursorW(hinstDLL, MAKEINTRESOURCEW(OCR_REVERSE));
3242       LookupInit();
3243       break;
3244 
3245     case DLL_PROCESS_DETACH:
3246       if (lpvReserved) break;
3247       UnregisterClassW(RICHEDIT_CLASS20W, 0);
3248       UnregisterClassW(MSFTEDIT_CLASS, 0);
3249       UnregisterClassA(RICHEDIT_CLASS20A, 0);
3250       UnregisterClassA("RichEdit50A", 0);
3251       if (ME_ListBoxRegistered)
3252           UnregisterClassW(REListBox20W, 0);
3253       if (ME_ComboBoxRegistered)
3254           UnregisterClassW(REComboBox20W, 0);
3255       LookupCleanup();
3256       HeapDestroy (me_heap);
3257       release_typelib();
3258       break;
3259     }
3260     return TRUE;
3261 }
3262 
3263 static inline int get_default_line_height( ME_TextEditor *editor )
3264 {
3265     int height = 0;
3266 
3267     if (editor->pBuffer && editor->pBuffer->pDefaultStyle)
3268         height = editor->pBuffer->pDefaultStyle->tm.tmHeight;
3269     if (height <= 0) height = 24;
3270 
3271     return height;
3272 }
3273 
3274 static inline int calc_wheel_change( int *remain, int amount_per_click )
3275 {
3276     int change = amount_per_click * (float)*remain / WHEEL_DELTA;
3277     *remain -= WHEEL_DELTA * change / amount_per_click;
3278     return change;
3279 }
3280 
3281 static const char * const edit_messages[] = {
3282   "EM_GETSEL",
3283   "EM_SETSEL",
3284   "EM_GETRECT",
3285   "EM_SETRECT",
3286   "EM_SETRECTNP",
3287   "EM_SCROLL",
3288   "EM_LINESCROLL",
3289   "EM_SCROLLCARET",
3290   "EM_GETMODIFY",
3291   "EM_SETMODIFY",
3292   "EM_GETLINECOUNT",
3293   "EM_LINEINDEX",
3294   "EM_SETHANDLE",
3295   "EM_GETHANDLE",
3296   "EM_GETTHUMB",
3297   "EM_UNKNOWN_BF",
3298   "EM_UNKNOWN_C0",
3299   "EM_LINELENGTH",
3300   "EM_REPLACESEL",
3301   "EM_UNKNOWN_C3",
3302   "EM_GETLINE",
3303   "EM_LIMITTEXT",
3304   "EM_CANUNDO",
3305   "EM_UNDO",
3306   "EM_FMTLINES",
3307   "EM_LINEFROMCHAR",
3308   "EM_UNKNOWN_CA",
3309   "EM_SETTABSTOPS",
3310   "EM_SETPASSWORDCHAR",
3311   "EM_EMPTYUNDOBUFFER",
3312   "EM_GETFIRSTVISIBLELINE",
3313   "EM_SETREADONLY",
3314   "EM_SETWORDBREAKPROC",
3315   "EM_GETWORDBREAKPROC",
3316   "EM_GETPASSWORDCHAR",
3317   "EM_SETMARGINS",
3318   "EM_GETMARGINS",
3319   "EM_GETLIMITTEXT",
3320   "EM_POSFROMCHAR",
3321   "EM_CHARFROMPOS",
3322   "EM_SETIMESTATUS",
3323   "EM_GETIMESTATUS"
3324 };
3325 
3326 static const char * const richedit_messages[] = {
3327   "EM_CANPASTE",
3328   "EM_DISPLAYBAND",
3329   "EM_EXGETSEL",
3330   "EM_EXLIMITTEXT",
3331   "EM_EXLINEFROMCHAR",
3332   "EM_EXSETSEL",
3333   "EM_FINDTEXT",
3334   "EM_FORMATRANGE",
3335   "EM_GETCHARFORMAT",
3336   "EM_GETEVENTMASK",
3337   "EM_GETOLEINTERFACE",
3338   "EM_GETPARAFORMAT",
3339   "EM_GETSELTEXT",
3340   "EM_HIDESELECTION",
3341   "EM_PASTESPECIAL",
3342   "EM_REQUESTRESIZE",
3343   "EM_SELECTIONTYPE",
3344   "EM_SETBKGNDCOLOR",
3345   "EM_SETCHARFORMAT",
3346   "EM_SETEVENTMASK",
3347   "EM_SETOLECALLBACK",
3348   "EM_SETPARAFORMAT",
3349   "EM_SETTARGETDEVICE",
3350   "EM_STREAMIN",
3351   "EM_STREAMOUT",
3352   "EM_GETTEXTRANGE",
3353   "EM_FINDWORDBREAK",
3354   "EM_SETOPTIONS",
3355   "EM_GETOPTIONS",
3356   "EM_FINDTEXTEX",
3357   "EM_GETWORDBREAKPROCEX",
3358   "EM_SETWORDBREAKPROCEX",
3359   "EM_SETUNDOLIMIT",
3360   "EM_UNKNOWN_USER_83",
3361   "EM_REDO",
3362   "EM_CANREDO",
3363   "EM_GETUNDONAME",
3364   "EM_GETREDONAME",
3365   "EM_STOPGROUPTYPING",
3366   "EM_SETTEXTMODE",
3367   "EM_GETTEXTMODE",
3368   "EM_AUTOURLDETECT",
3369   "EM_GETAUTOURLDETECT",
3370   "EM_SETPALETTE",
3371   "EM_GETTEXTEX",
3372   "EM_GETTEXTLENGTHEX",
3373   "EM_SHOWSCROLLBAR",
3374   "EM_SETTEXTEX",
3375   "EM_UNKNOWN_USER_98",
3376   "EM_UNKNOWN_USER_99",
3377   "EM_SETPUNCTUATION",
3378   "EM_GETPUNCTUATION",
3379   "EM_SETWORDWRAPMODE",
3380   "EM_GETWORDWRAPMODE",
3381   "EM_SETIMECOLOR",
3382   "EM_GETIMECOLOR",
3383   "EM_SETIMEOPTIONS",
3384   "EM_GETIMEOPTIONS",
3385   "EM_CONVPOSITION",
3386   "EM_UNKNOWN_USER_109",
3387   "EM_UNKNOWN_USER_110",
3388   "EM_UNKNOWN_USER_111",
3389   "EM_UNKNOWN_USER_112",
3390   "EM_UNKNOWN_USER_113",
3391   "EM_UNKNOWN_USER_114",
3392   "EM_UNKNOWN_USER_115",
3393   "EM_UNKNOWN_USER_116",
3394   "EM_UNKNOWN_USER_117",
3395   "EM_UNKNOWN_USER_118",
3396   "EM_UNKNOWN_USER_119",
3397   "EM_SETLANGOPTIONS",
3398   "EM_GETLANGOPTIONS",
3399   "EM_GETIMECOMPMODE",
3400   "EM_FINDTEXTW",
3401   "EM_FINDTEXTEXW",
3402   "EM_RECONVERSION",
3403   "EM_SETIMEMODEBIAS",
3404   "EM_GETIMEMODEBIAS"
3405 };
3406 
3407 static const char *
3408 get_msg_name(UINT msg)
3409 {
3410   if (msg >= EM_GETSEL && msg <= EM_CHARFROMPOS)
3411     return edit_messages[msg - EM_GETSEL];
3412   if (msg >= EM_CANPASTE && msg <= EM_GETIMEMODEBIAS)
3413     return richedit_messages[msg - EM_CANPASTE];
3414   return "";
3415 }
3416 
3417 static void ME_LinkNotify(ME_TextEditor *editor, UINT msg, WPARAM wParam, LPARAM lParam)
3418 {
3419   int x,y;
3420   BOOL isExact;
3421   ME_Cursor cursor; /* The start of the clicked text. */
3422 
3423   ENLINK info;
3424   x = (short)LOWORD(lParam);
3425   y = (short)HIWORD(lParam);
3426   ME_CharFromPos(editor, x, y, &cursor, &isExact);
3427   if (!isExact) return;
3428 
3429   if (is_link( &cursor.pRun->member.run ))
3430   { /* The clicked run has CFE_LINK set */
3431     ME_DisplayItem *di;
3432 
3433     info.nmhdr.hwndFrom = NULL;
3434     info.nmhdr.idFrom = 0;
3435     info.nmhdr.code = EN_LINK;
3436     info.msg = msg;
3437     info.wParam = wParam;
3438     info.lParam = lParam;
3439     cursor.nOffset = 0;
3440 
3441     /* find the first contiguous run with CFE_LINK set */
3442     info.chrg.cpMin = ME_GetCursorOfs(&cursor);
3443     di = cursor.pRun;
3444     while (ME_PrevRun( NULL, &di, FALSE ) && is_link( &di->member.run ))
3445         info.chrg.cpMin -= di->member.run.len;
3446 
3447     /* find the last contiguous run with CFE_LINK set */
3448     info.chrg.cpMax = ME_GetCursorOfs(&cursor) + cursor.pRun->member.run.len;
3449     di = cursor.pRun;
3450     while (ME_NextRun( NULL, &di, FALSE ) && is_link( &di->member.run ))
3451         info.chrg.cpMax += di->member.run.len;
3452 
3453     ITextHost_TxNotify(editor->texthost, info.nmhdr.code, &info);
3454   }
3455 }
3456 
3457 void ME_ReplaceSel(ME_TextEditor *editor, BOOL can_undo, const WCHAR *str, int len)
3458 {
3459   int from, to, nStartCursor;
3460   ME_Style *style;
3461 
3462   nStartCursor = ME_GetSelectionOfs(editor, &from, &to);
3463   style = ME_GetSelectionInsertStyle(editor);
3464   ME_InternalDeleteText(editor, &editor->pCursors[nStartCursor], to-from, FALSE);
3465   ME_InsertTextFromCursor(editor, 0, str, len, style);
3466   ME_ReleaseStyle(style);
3467   /* drop temporary style if line end */
3468   /*
3469    * FIXME question: does abc\n mean: put abc,
3470    * clear temp style, put \n? (would require a change)
3471    */
3472   if (len>0 && str[len-1] == '\n')
3473     ME_ClearTempStyle(editor);
3474   ME_CommitUndo(editor);
3475   ME_UpdateSelectionLinkAttribute(editor);
3476   if (!can_undo)
3477     ME_EmptyUndoStack(editor);
3478   ME_UpdateRepaint(editor, FALSE);
3479 }
3480 
3481 static void ME_SetText(ME_TextEditor *editor, void *text, BOOL unicode)
3482 {
3483   LONG codepage = unicode ? CP_UNICODE : CP_ACP;
3484   int textLen;
3485 
3486   LPWSTR wszText = ME_ToUnicode(codepage, text, &textLen);
3487   ME_InsertTextFromCursor(editor, 0, wszText, textLen, editor->pBuffer->pDefaultStyle);
3488   ME_EndToUnicode(codepage, wszText);
3489 }
3490 
3491 static LRESULT ME_WmCreate(ME_TextEditor *editor, LPARAM lParam, BOOL unicode)
3492 {
3493   CREATESTRUCTW *createW = (CREATESTRUCTW*)lParam;
3494   CREATESTRUCTA *createA = (CREATESTRUCTA*)lParam;
3495   void *text = NULL;
3496   INT max;
3497 
3498   if (lParam)
3499     text = unicode ? (void*)createW->lpszName : (void*)createA->lpszName;
3500 
3501   ME_SetDefaultFormatRect(editor);
3502 
3503   max = (editor->styleFlags & ES_DISABLENOSCROLL) ? 1 : 0;
3504   if (~editor->styleFlags & ES_DISABLENOSCROLL || editor->styleFlags & WS_VSCROLL)
3505     ITextHost_TxSetScrollRange(editor->texthost, SB_VERT, 0, max, TRUE);
3506 
3507   if (~editor->styleFlags & ES_DISABLENOSCROLL || editor->styleFlags & WS_HSCROLL)
3508     ITextHost_TxSetScrollRange(editor->texthost, SB_HORZ, 0, max, TRUE);
3509 
3510   if (editor->styleFlags & ES_DISABLENOSCROLL)
3511   {
3512     if (editor->styleFlags & WS_VSCROLL)
3513     {
3514       ITextHost_TxEnableScrollBar(editor->texthost, SB_VERT, ESB_DISABLE_BOTH);
3515       ITextHost_TxShowScrollBar(editor->texthost, SB_VERT, TRUE);
3516     }
3517     if (editor->styleFlags & WS_HSCROLL)
3518     {
3519       ITextHost_TxEnableScrollBar(editor->texthost, SB_HORZ, ESB_DISABLE_BOTH);
3520       ITextHost_TxShowScrollBar(editor->texthost, SB_HORZ, TRUE);
3521     }
3522   }
3523 
3524   if (text)
3525   {
3526     ME_SetText(editor, text, unicode);
3527     ME_SetCursorToStart(editor, &editor->pCursors[0]);
3528     ME_SetCursorToStart(editor, &editor->pCursors[1]);
3529   }
3530 
3531   ME_CommitUndo(editor);
3532   ME_WrapMarkedParagraphs(editor);
3533   ME_MoveCaret(editor);
3534   return 0;
3535 }
3536 
3537 
3538 #define UNSUPPORTED_MSG(e) \
3539   case e:                  \
3540     FIXME(#e ": stub\n");  \
3541     *phresult = S_FALSE;   \
3542     return 0;
3543 
3544 /* Handle messages for windowless and windowed richedit controls.
3545  *
3546  * The LRESULT that is returned is a return value for window procs,
3547  * and the phresult parameter is the COM return code needed by the
3548  * text services interface. */
3549 LRESULT ME_HandleMessage(ME_TextEditor *editor, UINT msg, WPARAM wParam,
3550                          LPARAM lParam, BOOL unicode, HRESULT* phresult)
3551 {
3552   *phresult = S_OK;
3553 
3554   switch(msg) {
3555 
3556   UNSUPPORTED_MSG(EM_DISPLAYBAND)
3557   UNSUPPORTED_MSG(EM_FINDWORDBREAK)
3558   UNSUPPORTED_MSG(EM_FMTLINES)
3559   UNSUPPORTED_MSG(EM_FORMATRANGE)
3560   UNSUPPORTED_MSG(EM_GETBIDIOPTIONS)
3561   UNSUPPORTED_MSG(EM_GETEDITSTYLE)
3562   UNSUPPORTED_MSG(EM_GETIMECOMPMODE)
3563   UNSUPPORTED_MSG(EM_GETIMESTATUS)
3564   UNSUPPORTED_MSG(EM_SETIMESTATUS)
3565   UNSUPPORTED_MSG(EM_GETLANGOPTIONS)
3566   UNSUPPORTED_MSG(EM_GETREDONAME)
3567   UNSUPPORTED_MSG(EM_GETTYPOGRAPHYOPTIONS)
3568   UNSUPPORTED_MSG(EM_GETUNDONAME)
3569   UNSUPPORTED_MSG(EM_GETWORDBREAKPROCEX)
3570   UNSUPPORTED_MSG(EM_SETBIDIOPTIONS)
3571   UNSUPPORTED_MSG(EM_SETEDITSTYLE)
3572   UNSUPPORTED_MSG(EM_SETLANGOPTIONS)
3573   UNSUPPORTED_MSG(EM_SETMARGINS)
3574   UNSUPPORTED_MSG(EM_SETPALETTE)
3575   UNSUPPORTED_MSG(EM_SETTABSTOPS)
3576   UNSUPPORTED_MSG(EM_SETTYPOGRAPHYOPTIONS)
3577   UNSUPPORTED_MSG(EM_SETWORDBREAKPROCEX)
3578 
3579 /* Messages specific to Richedit controls */
3580 
3581   case EM_STREAMIN:
3582    return ME_StreamIn(editor, wParam, (EDITSTREAM*)lParam, TRUE);
3583   case EM_STREAMOUT:
3584    return ME_StreamOut(editor, wParam, (EDITSTREAM *)lParam);
3585   case WM_GETDLGCODE:
3586   {
3587     UINT code = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS;
3588 
3589     if (lParam)
3590       editor->bDialogMode = TRUE;
3591     if (editor->styleFlags & ES_MULTILINE)
3592       code |= DLGC_WANTMESSAGE;
3593     if (!(editor->styleFlags & ES_SAVESEL))
3594       code |= DLGC_HASSETSEL;
3595     return code;
3596   }
3597   case EM_EMPTYUNDOBUFFER:
3598     ME_EmptyUndoStack(editor);
3599     return 0;
3600   case EM_GETSEL:
3601   {
3602     /* Note: wParam/lParam can be NULL */
3603     UINT from, to;
3604     PUINT pfrom = wParam ? (PUINT)wParam : &from;
3605     PUINT pto = lParam ? (PUINT)lParam : &to;
3606     ME_GetSelectionOfs(editor, (int *)pfrom, (int *)pto);
3607     if ((*pfrom|*pto) & 0xFFFF0000)
3608       return -1;
3609     return MAKELONG(*pfrom,*pto);
3610   }
3611   case EM_EXGETSEL:
3612   {
3613     CHARRANGE *pRange = (CHARRANGE *)lParam;
3614     ME_GetSelectionOfs(editor, &pRange->cpMin, &pRange->cpMax);
3615     TRACE("EM_EXGETSEL = (%d,%d)\n", pRange->cpMin, pRange->cpMax);
3616     return 0;
3617   }
3618   case EM_SETUNDOLIMIT:
3619   {
3620     if ((int)wParam < 0)
3621       editor->nUndoLimit = STACK_SIZE_DEFAULT;
3622     else
3623       editor->nUndoLimit = min(wParam, STACK_SIZE_MAX);
3624     /* Setting a max stack size keeps wine from getting killed
3625       for hogging memory. Windows allocates all this memory at once, so
3626       no program would realistically set a value above our maximum. */
3627     return editor->nUndoLimit;
3628   }
3629   case EM_CANUNDO:
3630     return !list_empty( &editor->undo_stack );
3631   case EM_CANREDO:
3632     return !list_empty( &editor->redo_stack );
3633   case WM_UNDO: /* FIXME: actually not the same */
3634   case EM_UNDO:
3635     return ME_Undo(editor);
3636   case EM_REDO:
3637     return ME_Redo(editor);
3638   case EM_GETOPTIONS:
3639   {
3640     /* these flags are equivalent to the ES_* counterparts */
3641     DWORD mask = ECO_VERTICAL | ECO_AUTOHSCROLL | ECO_AUTOVSCROLL |
3642                  ECO_NOHIDESEL | ECO_READONLY | ECO_WANTRETURN | ECO_SELECTIONBAR;
3643     DWORD settings = editor->styleFlags & mask;
3644 
3645     return settings;
3646   }
3647   case EM_SETFONTSIZE:
3648   {
3649       CHARFORMAT2W cf;
3650       LONG tmp_size, size;
3651       BOOL is_increase = ((LONG)wParam > 0);
3652 
3653       if (editor->mode & TM_PLAINTEXT)
3654           return FALSE;
3655 
3656       cf.cbSize = sizeof(cf);
3657       cf.dwMask = CFM_SIZE;
3658       ME_GetSelectionCharFormat(editor, &cf);
3659       tmp_size = (cf.yHeight / 20) + wParam;
3660 
3661       if (tmp_size <= 1)
3662           size = 1;
3663       else if (tmp_size > 12 && tmp_size < 28 && tmp_size % 2)
3664           size = tmp_size + (is_increase ? 1 : -1);
3665       else if (tmp_size > 28 && tmp_size < 36)
3666           size = is_increase ? 36 : 28;
3667       else if (tmp_size > 36 && tmp_size < 48)
3668           size = is_increase ? 48 : 36;
3669       else if (tmp_size > 48 && tmp_size < 72)
3670           size = is_increase ? 72 : 48;
3671       else if (tmp_size > 72 && tmp_size < 80)
3672           size = is_increase ? 80 : 72;
3673       else if (tmp_size > 80 && tmp_size < 1638)
3674           size = 10 * (is_increase ? (tmp_size / 10 + 1) : (tmp_size / 10));
3675       else if (tmp_size >= 1638)
3676           size = 1638;
3677       else
3678           size = tmp_size;
3679 
3680       cf.yHeight = size * 20; /*  convert twips to points */
3681       ME_SetSelectionCharFormat(editor, &cf);
3682       ME_CommitUndo(editor);
3683       ME_WrapMarkedParagraphs(editor);
3684       ME_UpdateScrollBar(editor);
3685       ME_Repaint(editor);
3686 
3687       return TRUE;
3688   }
3689   case EM_SETOPTIONS:
3690   {
3691     /* these flags are equivalent to ES_* counterparts, except for
3692      * ECO_AUTOWORDSELECTION that doesn't have an ES_* counterpart,
3693      * but is still stored in editor->styleFlags. */
3694     const DWORD mask = ECO_VERTICAL | ECO_AUTOHSCROLL | ECO_AUTOVSCROLL |
3695                        ECO_NOHIDESEL | ECO_READONLY | ECO_WANTRETURN |
3696                        ECO_SELECTIONBAR | ECO_AUTOWORDSELECTION;
3697     DWORD settings = mask & editor->styleFlags;
3698     DWORD oldSettings = settings;
3699     DWORD changedSettings;
3700 
3701     switch(wParam)
3702     {
3703       case ECOOP_SET:
3704         settings = lParam;
3705         break;
3706       case ECOOP_OR:
3707         settings |= lParam;
3708         break;
3709       case ECOOP_AND:
3710         settings &= lParam;
3711         break;
3712       case ECOOP_XOR:
3713         settings ^= lParam;
3714     }
3715     changedSettings = oldSettings ^ settings;
3716 
3717     if (changedSettings) {
3718       editor->styleFlags = (editor->styleFlags & ~mask) | (settings & mask);
3719 
3720       if (changedSettings & ECO_SELECTIONBAR)
3721       {
3722         ITextHost_TxInvalidateRect(editor->texthost, &editor->rcFormat, TRUE);
3723         if (settings & ECO_SELECTIONBAR) {
3724           assert(!editor->selofs);
3725           editor->selofs = SELECTIONBAR_WIDTH;
3726           editor->rcFormat.left += editor->selofs;
3727         } else {
3728           editor->rcFormat.left -= editor->selofs;
3729           editor->selofs = 0;
3730         }
3731         ME_RewrapRepaint(editor);
3732       }
3733 
3734       if ((changedSettings & settings & ES_NOHIDESEL) && !editor->bHaveFocus)
3735           ME_InvalidateSelection( editor );
3736 
3737       if (changedSettings & settings & ECO_VERTICAL)
3738         FIXME("ECO_VERTICAL not implemented yet!\n");
3739       if (changedSettings & settings & ECO_AUTOHSCROLL)
3740         FIXME("ECO_AUTOHSCROLL not implemented yet!\n");
3741       if (changedSettings & settings & ECO_AUTOVSCROLL)
3742         FIXME("ECO_AUTOVSCROLL not implemented yet!\n");
3743       if (changedSettings & settings & ECO_WANTRETURN)
3744         FIXME("ECO_WANTRETURN not implemented yet!\n");
3745       if (changedSettings & settings & ECO_AUTOWORDSELECTION)
3746         FIXME("ECO_AUTOWORDSELECTION not implemented yet!\n");
3747     }
3748 
3749     return settings;
3750   }
3751   case EM_SETSEL:
3752   {
3753     return handle_EM_EXSETSEL( editor, wParam, lParam );
3754   }
3755   case EM_SETSCROLLPOS:
3756   {
3757     POINT *point = (POINT *)lParam;
3758     ME_ScrollAbs(editor, point->x, point->y);
3759     return 0;
3760   }
3761   case EM_AUTOURLDETECT:
3762   {
3763     if (wParam==1 || wParam ==0)
3764     {
3765         editor->AutoURLDetect_bEnable = (BOOL)wParam;
3766         return 0;
3767     }
3768     return E_INVALIDARG;
3769   }
3770   case EM_GETAUTOURLDETECT:
3771   {
3772     return editor->AutoURLDetect_bEnable;
3773   }
3774   case EM_EXSETSEL:
3775   {
3776     CHARRANGE range = *(CHARRANGE *)lParam;
3777 
3778     return handle_EM_EXSETSEL( editor, range.cpMin, range.cpMax );
3779   }
3780   case EM_SHOWSCROLLBAR:
3781   {
3782     DWORD flags;
3783 
3784     switch (wParam)
3785     {
3786       case SB_HORZ:
3787         flags = WS_HSCROLL;
3788         break;
3789       case SB_VERT:
3790         flags = WS_VSCROLL;
3791         break;
3792       case SB_BOTH:
3793         flags = WS_HSCROLL|WS_VSCROLL;
3794         break;
3795       default:
3796         return 0;
3797     }
3798 
3799     if (lParam) {
3800       editor->styleFlags |= flags;
3801       if (flags & WS_HSCROLL)
3802         ITextHost_TxShowScrollBar(editor->texthost, SB_HORZ,
3803                           editor->nTotalWidth > editor->sizeWindow.cx);
3804       if (flags & WS_VSCROLL)
3805         ITextHost_TxShowScrollBar(editor->texthost, SB_VERT,
3806                           editor->nTotalLength > editor->sizeWindow.cy);
3807     } else {
3808       editor->styleFlags &= ~flags;
3809       ITextHost_TxShowScrollBar(editor->texthost, wParam, FALSE);
3810     }
3811     return 0;
3812   }
3813   case EM_SETTEXTEX:
3814   {
3815     LPWSTR wszText;
3816     SETTEXTEX *pStruct = (SETTEXTEX *)wParam;
3817     int from, to, len;
3818     ME_Style *style;
3819     BOOL bRtf, bUnicode, bSelection, bUTF8;
3820     int oldModify = editor->nModifyStep;
3821     static const char utf8_bom[] = {0xef, 0xbb, 0xbf};
3822 
3823     if (!pStruct) return 0;
3824 
3825     /* If we detect ascii rtf at the start of the string,
3826      * we know it isn't unicode. */
3827     bRtf = (lParam && (!strncmp((char *)lParam, "{\\rtf", 5) ||
3828                          !strncmp((char *)lParam, "{\\urtf", 6)));
3829     bUnicode = !bRtf && pStruct->codepage == CP_UNICODE;
3830     bUTF8 = (lParam && (!strncmp((char *)lParam, utf8_bom, 3)));
3831 
3832     TRACE("EM_SETTEXTEX - %s, flags %d, cp %d\n",
3833           bUnicode ? debugstr_w((LPCWSTR)lParam) : debugstr_a((LPCSTR)lParam),
3834           pStruct->flags, pStruct->codepage);
3835 
3836     bSelection = (pStruct->flags & ST_SELECTION) != 0;
3837     if (bSelection) {
3838       int nStartCursor = ME_GetSelectionOfs(editor, &from, &to);
3839       style = ME_GetSelectionInsertStyle(editor);
3840       ME_InternalDeleteText(editor, &editor->pCursors[nStartCursor], to - from, FALSE);
3841     } else {
3842       ME_Cursor start;
3843       ME_SetCursorToStart(editor, &start);
3844       ME_InternalDeleteText(editor, &start, ME_GetTextLength(editor), FALSE);
3845       style = editor->pBuffer->pDefaultStyle;
3846     }
3847 
3848     if (bRtf) {
3849       ME_StreamInRTFString(editor, bSelection, (char *)lParam);
3850       if (bSelection) {
3851         /* FIXME: The length returned doesn't include the rtf control
3852          * characters, only the actual text. */
3853         len = lParam ? strlen((char *)lParam) : 0;
3854       }
3855     } else {
3856       if (bUTF8 && !bUnicode) {
3857         wszText = ME_ToUnicode(CP_UTF8, (void *)(lParam+3), &len);
3858         ME_InsertTextFromCursor(editor, 0, wszText, len, style);
3859         ME_EndToUnicode(CP_UTF8, wszText);
3860       } else {
3861         wszText = ME_ToUnicode(pStruct->codepage, (void *)lParam, &len);
3862         ME_InsertTextFromCursor(editor, 0, wszText, len, style);
3863         ME_EndToUnicode(pStruct->codepage, wszText);
3864       }
3865     }
3866 
3867     if (bSelection) {
3868       ME_ReleaseStyle(style);
3869       ME_UpdateSelectionLinkAttribute(editor);
3870     } else {
3871       ME_Cursor cursor;
3872       len = 1;
3873       ME_SetCursorToStart(editor, &cursor);
3874       ME_UpdateLinkAttribute(editor, &cursor, INT_MAX);
3875     }
3876     ME_CommitUndo(editor);
3877     if (!(pStruct->flags & ST_KEEPUNDO))
3878     {
3879       editor->nModifyStep = oldModify;
3880       ME_EmptyUndoStack(editor);
3881     }
3882     ME_UpdateRepaint(editor, FALSE);
3883     return len;
3884   }
3885   case EM_SELECTIONTYPE:
3886     return ME_GetSelectionType(editor);
3887   case EM_SETBKGNDCOLOR:
3888   {
3889     LRESULT lColor;
3890     if (editor->rgbBackColor != -1) {
3891       DeleteObject(editor->hbrBackground);
3892       lColor = editor->rgbBackColor;
3893     }
3894     else lColor = ITextHost_TxGetSysColor(editor->texthost, COLOR_WINDOW);
3895 
3896     if (wParam)
3897     {
3898       editor->rgbBackColor = -1;
3899       editor->hbrBackground = GetSysColorBrush(COLOR_WINDOW);
3900     }
3901     else
3902     {
3903       editor->rgbBackColor = lParam;
3904       editor->hbrBackground = CreateSolidBrush(editor->rgbBackColor);
3905     }
3906     ITextHost_TxInvalidateRect(editor->texthost, NULL, TRUE);
3907     return lColor;
3908   }
3909   case EM_GETMODIFY:
3910     return editor->nModifyStep == 0 ? 0 : -1;
3911   case EM_SETMODIFY:
3912   {
3913     if (wParam)
3914       editor->nModifyStep = 1;
3915     else
3916       editor->nModifyStep = 0;
3917 
3918     return 0;
3919   }
3920   case EM_SETREADONLY:
3921   {
3922     if (wParam)
3923       editor->styleFlags |= ES_READONLY;
3924     else
3925       editor->styleFlags &= ~ES_READONLY;
3926     return 1;
3927   }
3928   case EM_SETEVENTMASK:
3929   {
3930     DWORD nOldMask = editor->nEventMask;
3931 
3932     editor->nEventMask = lParam;
3933     return nOldMask;
3934   }
3935   case EM_GETEVENTMASK:
3936     return editor->nEventMask;
3937   case EM_SETCHARFORMAT:
3938   {
3939     CHARFORMAT2W p;
3940     BOOL bRepaint = TRUE;
3941     if (!cfany_to_cf2w(&p, (CHARFORMAT2W *)lParam))
3942       return 0;
3943     if (wParam & SCF_ALL) {
3944       if (editor->mode & TM_PLAINTEXT) {
3945         ME_SetDefaultCharFormat(editor, &p);
3946       } else {
3947         ME_Cursor start;
3948         ME_SetCursorToStart(editor, &start);
3949         ME_SetCharFormat(editor, &start, NULL, &p);
3950         editor->nModifyStep = 1;
3951       }
3952     } else if (wParam & SCF_SELECTION) {
3953       if (editor->mode & TM_PLAINTEXT)
3954         return 0;
3955       if (wParam & SCF_WORD) {
3956         ME_Cursor start;
3957         ME_Cursor end = editor->pCursors[0];
3958         ME_MoveCursorWords(editor, &end, +1);
3959         start = end;
3960         ME_MoveCursorWords(editor, &start, -1);
3961         ME_SetCharFormat(editor, &start, &end, &p);
3962       }
3963       bRepaint = ME_IsSelection(editor);
3964       ME_SetSelectionCharFormat(editor, &p);
3965       if (bRepaint) editor->nModifyStep = 1;
3966     } else { /* SCF_DEFAULT */
3967       ME_SetDefaultCharFormat(editor, &p);
3968     }
3969     ME_CommitUndo(editor);
3970     if (bRepaint)
3971     {
3972       ME_WrapMarkedParagraphs(editor);
3973       ME_UpdateScrollBar(editor);
3974       ME_Repaint(editor);
3975     }
3976     return 1;
3977   }
3978   case EM_GETCHARFORMAT:
3979   {
3980     CHARFORMAT2W tmp, *dst = (CHARFORMAT2W *)lParam;
3981     if (dst->cbSize != sizeof(CHARFORMATA) &&
3982         dst->cbSize != sizeof(CHARFORMATW) &&
3983         dst->cbSize != sizeof(CHARFORMAT2A) &&
3984         dst->cbSize != sizeof(CHARFORMAT2W))
3985       return 0;
3986     tmp.cbSize = sizeof(tmp);
3987     if (!wParam)
3988       ME_GetDefaultCharFormat(editor, &tmp);
3989     else
3990       ME_GetSelectionCharFormat(editor, &tmp);
3991     cf2w_to_cfany(dst, &tmp);
3992     return tmp.dwMask;
3993   }
3994   case EM_SETPARAFORMAT:
3995   {
3996     BOOL result = ME_SetSelectionParaFormat(editor, (PARAFORMAT2 *)lParam);
3997     ME_WrapMarkedParagraphs(editor);
3998     ME_UpdateScrollBar(editor);
3999     ME_Repaint(editor);
4000     ME_CommitUndo(editor);
4001     return result;
4002   }
4003   case EM_GETPARAFORMAT:
4004     ME_GetSelectionParaFormat(editor, (PARAFORMAT2 *)lParam);
4005     return ((PARAFORMAT2 *)lParam)->dwMask;
4006   case EM_GETFIRSTVISIBLELINE:
4007   {
4008     ME_DisplayItem *p = editor->pBuffer->pFirst;
4009     int y = editor->vert_si.nPos;
4010     int ypara = 0;
4011     int count = 0;
4012     int ystart, yend;
4013     while(p) {
4014       p = ME_FindItemFwd(p, diStartRowOrParagraphOrEnd);
4015       if (p->type == diTextEnd)
4016         break;
4017       if (p->type == diParagraph) {
4018         ypara = p->member.para.pt.y;
4019         continue;
4020       }
4021       ystart = ypara + p->member.row.pt.y;
4022       yend = ystart + p->member.row.nHeight;
4023       if (y < yend) {
4024         break;
4025       }
4026       count++;
4027     }
4028     return count;
4029   }
4030   case EM_HIDESELECTION:
4031   {
4032      editor->bHideSelection = (wParam != 0);
4033      ME_InvalidateSelection(editor);
4034      return 0;
4035   }
4036   case EM_LINESCROLL:
4037   {
4038     if (!(editor->styleFlags & ES_MULTILINE))
4039       return FALSE;
4040     ME_ScrollDown( editor, lParam * get_default_line_height( editor ) );
4041     return TRUE;
4042   }
4043   case WM_CLEAR:
4044   {
4045     int from, to;
4046     int nStartCursor = ME_GetSelectionOfs(editor, &from, &to);
4047     ME_InternalDeleteText(editor, &editor->pCursors[nStartCursor], to-from, FALSE);
4048     ME_CommitUndo(editor);
4049     ME_UpdateRepaint(editor, TRUE);
4050     return 0;
4051   }
4052   case EM_REPLACESEL:
4053   {
4054     int len = 0;
4055     LONG codepage = unicode ? CP_UNICODE : CP_ACP;
4056     LPWSTR wszText = ME_ToUnicode(codepage, (void *)lParam, &len);
4057 
4058     TRACE("EM_REPLACESEL - %s\n", debugstr_w(wszText));
4059 
4060     ME_ReplaceSel(editor, !!wParam, wszText, len);
4061     ME_EndToUnicode(codepage, wszText);
4062     return len;
4063   }
4064   case EM_SCROLLCARET:
4065     ME_EnsureVisible(editor, &editor->pCursors[0]);
4066     return 0;
4067   case WM_SETFONT:
4068   {
4069     LOGFONTW lf;
4070     CHARFORMAT2W fmt;
4071     HDC hDC;
4072     BOOL bRepaint = LOWORD(lParam);
4073 
4074     if (!wParam)
4075       wParam = (WPARAM)GetStockObject(SYSTEM_FONT);
4076 
4077     if (!GetObjectW((HGDIOBJ)wParam, sizeof(LOGFONTW), &lf))
4078       return 0;
4079 
4080     hDC = ITextHost_TxGetDC(editor->texthost);
4081     ME_CharFormatFromLogFont(hDC, &lf, &fmt);
4082     ITextHost_TxReleaseDC(editor->texthost, hDC);
4083     if (editor->mode & TM_RICHTEXT) {
4084       ME_Cursor start;
4085       ME_SetCursorToStart(editor, &start);
4086       ME_SetCharFormat(editor, &start, NULL, &fmt);
4087     }
4088     ME_SetDefaultCharFormat(editor, &fmt);
4089 
4090     ME_CommitUndo(editor);
4091     ME_MarkAllForWrapping(editor);
4092     ME_WrapMarkedParagraphs(editor);
4093     ME_UpdateScrollBar(editor);
4094     if (bRepaint)
4095       ME_Repaint(editor);
4096     return 0;
4097   }
4098   case WM_SETTEXT:
4099   {
4100     ME_Cursor cursor;
4101     ME_SetCursorToStart(editor, &cursor);
4102     ME_InternalDeleteText(editor, &cursor, ME_GetTextLength(editor), FALSE);
4103     if (lParam)
4104     {
4105       TRACE("WM_SETTEXT lParam==%lx\n",lParam);
4106       if (!strncmp((char *)lParam, "{\\rtf", 5) ||
4107           !strncmp((char *)lParam, "{\\urtf", 6))
4108       {
4109         /* Undocumented: WM_SETTEXT supports RTF text */
4110         ME_StreamInRTFString(editor, 0, (char *)lParam);
4111       }
4112       else
4113         ME_SetText(editor, (void*)lParam, unicode);
4114     }
4115     else
4116       TRACE("WM_SETTEXT - NULL\n");
4117     ME_SetCursorToStart(editor, &cursor);
4118     ME_UpdateLinkAttribute(editor, &cursor, INT_MAX);
4119     ME_SetSelection(editor, 0, 0);
4120     editor->nModifyStep = 0;
4121     ME_CommitUndo(editor);
4122     ME_EmptyUndoStack(editor);
4123     ME_UpdateRepaint(editor, FALSE);
4124     return 1;
4125   }
4126   case EM_CANPASTE:
4127     return paste_special( editor, 0, NULL, TRUE );
4128   case WM_PASTE:
4129   case WM_MBUTTONDOWN:
4130     wParam = 0;
4131     lParam = 0;
4132     /* fall through */
4133   case EM_PASTESPECIAL:
4134     paste_special( editor, wParam, (REPASTESPECIAL *)lParam, FALSE );
4135     return 0;
4136   case WM_CUT:
4137   case WM_COPY:
4138     copy_or_cut(editor, msg == WM_CUT);
4139     return 0;
4140   case WM_GETTEXTLENGTH:
4141   {
4142     GETTEXTLENGTHEX how;
4143 
4144     /* CR/LF conversion required in 2.0 mode, verbatim in 1.0 mode */
4145     how.flags = GTL_CLOSE | (editor->bEmulateVersion10 ? 0 : GTL_USECRLF) | GTL_NUMCHARS;
4146     how.codepage = unicode ? CP_UNICODE : CP_ACP;
4147     return ME_GetTextLengthEx(editor, &how);
4148   }
4149   case EM_GETTEXTLENGTHEX:
4150     return ME_GetTextLengthEx(editor, (GETTEXTLENGTHEX *)wParam);
4151   case WM_GETTEXT:
4152   {
4153     GETTEXTEX ex;
4154     ex.cb = wParam * (unicode ? sizeof(WCHAR) : sizeof(CHAR));
4155     ex.flags = GT_USECRLF;
4156     ex.codepage = unicode ? CP_UNICODE : CP_ACP;
4157     ex.lpDefaultChar = NULL;
4158     ex.lpUsedDefChar = NULL;
4159     return ME_GetTextEx(editor, &ex, lParam);
4160   }
4161   case EM_GETTEXTEX:
4162     return ME_GetTextEx(editor, (GETTEXTEX*)wParam, lParam);
4163   case EM_GETSELTEXT:
4164   {
4165     int nFrom, nTo, nStartCur = ME_GetSelectionOfs(editor, &nFrom, &nTo);
4166     ME_Cursor *from = &editor->pCursors[nStartCur];
4167     return ME_GetTextRange(editor, (WCHAR *)lParam, from,
4168                            nTo - nFrom, unicode);
4169   }
4170   case EM_GETSCROLLPOS:
4171   {
4172     POINT *point = (POINT *)lParam;
4173     point->x = editor->horz_si.nPos;
4174     point->y = editor->vert_si.nPos;
4175     /* 16-bit scaled value is returned as stored in scrollinfo */
4176     if (editor->horz_si.nMax > 0xffff)
4177       point->x = MulDiv(point->x, 0xffff, editor->horz_si.nMax);
4178     if (editor->vert_si.nMax > 0xffff)
4179       point->y = MulDiv(point->y, 0xffff, editor->vert_si.nMax);
4180     return 1;
4181   }
4182   case EM_GETTEXTRANGE:
4183   {
4184     TEXTRANGEW *rng = (TEXTRANGEW *)lParam;
4185     ME_Cursor start;
4186     int nStart = rng->chrg.cpMin;
4187     int nEnd = rng->chrg.cpMax;
4188     int textlength = ME_GetTextLength(editor);
4189 
4190     TRACE("EM_GETTEXTRANGE min=%d max=%d unicode=%d textlength=%d\n",
4191           rng->chrg.cpMin, rng->chrg.cpMax, unicode, textlength);
4192     if (nStart < 0) return 0;
4193     if ((nStart == 0 && nEnd == -1) || nEnd > textlength)
4194       nEnd = textlength;
4195     if (nStart >= nEnd) return 0;
4196 
4197     ME_CursorFromCharOfs(editor, nStart, &start);
4198     return ME_GetTextRange(editor, rng->lpstrText, &start, nEnd - nStart, unicode);
4199   }
4200   case EM_GETLINE:
4201   {
4202     ME_DisplayItem *run;
4203     const unsigned int nMaxChars = *(WORD *) lParam;
4204     unsigned int nCharsLeft = nMaxChars;
4205     char *dest = (char *) lParam;
4206     BOOL wroteNull = FALSE;
4207 
4208     TRACE("EM_GETLINE: row=%d, nMaxChars=%d (%s)\n", (int) wParam, nMaxChars,
4209           unicode ? "Unicode" : "Ansi");
4210 
4211     run = ME_FindRowWithNumber(editor, wParam);
4212     if (run == NULL)
4213       return 0;
4214 
4215     while (nCharsLeft && (run = ME_FindItemFwd(run, diRunOrStartRow))
4216            && run->type == diRun)
4217     {
4218       WCHAR *str = get_text( &run->member.run, 0 );
4219       unsigned int nCopy;
4220 
4221       nCopy = min(nCharsLeft, run->member.run.len);
4222 
4223       if (unicode)
4224         memcpy(dest, str, nCopy * sizeof(WCHAR));
4225       else
4226         nCopy = WideCharToMultiByte(CP_ACP, 0, str, nCopy, dest,
4227                                     nCharsLeft, NULL, NULL);
4228       dest += nCopy * (unicode ? sizeof(WCHAR) : 1);
4229       nCharsLeft -= nCopy;
4230     }
4231 
4232     /* append line termination, space allowing */
4233     if (nCharsLeft > 0)
4234     {
4235       if (unicode)
4236         *((WCHAR *)dest) = '\0';
4237       else
4238         *dest = '\0';
4239       nCharsLeft--;
4240       wroteNull = TRUE;
4241     }
4242 
4243     TRACE("EM_GETLINE: got %u characters\n", nMaxChars - nCharsLeft);
4244     return nMaxChars - nCharsLeft - (wroteNull ? 1 : 0);
4245   }
4246   case EM_GETLINECOUNT:
4247   {
4248     ME_DisplayItem *item = editor->pBuffer->pLast;
4249     int nRows = editor->total_rows;
4250     ME_DisplayItem *prev_para = NULL, *last_para = NULL;
4251 
4252     last_para = ME_FindItemBack(item, diRun);
4253     prev_para = ME_FindItemBack(last_para, diRun);
4254     assert(last_para);
4255     assert(last_para->member.run.nFlags & MERF_ENDPARA);
4256     if (editor->bEmulateVersion10 && prev_para &&
4257         last_para->member.run.nCharOfs == 0 &&
4258         prev_para->member.run.len == 1 &&
4259         *get_text( &prev_para->member.run, 0 ) == '\r')
4260     {
4261       /* In 1.0 emulation, the last solitary \r at the very end of the text
4262          (if one exists) is NOT a line break.
4263          FIXME: this is an ugly hack. This should have a more regular model. */
4264       nRows--;
4265     }
4266 
4267     TRACE("EM_GETLINECOUNT: nRows==%d\n", nRows);
4268     return max(1, nRows);
4269   }
4270   case EM_LINEFROMCHAR:
4271   {
4272     if (wParam == -1)
4273       return ME_RowNumberFromCharOfs(editor, ME_GetCursorOfs(&editor->pCursors[1]));
4274     else
4275       return ME_RowNumberFromCharOfs(editor, wParam);
4276   }
4277   case EM_EXLINEFROMCHAR:
4278   {
4279     if (lParam == -1)
4280       return ME_RowNumberFromCharOfs(editor, ME_GetCursorOfs(&editor->pCursors[1]));
4281     else
4282       return ME_RowNumberFromCharOfs(editor, lParam);
4283   }
4284   case EM_LINEINDEX:
4285   {
4286     ME_DisplayItem *item, *para;
4287     int nCharOfs;
4288 
4289     if (wParam == -1)
4290       item = ME_FindItemBack(editor->pCursors[0].pRun, diStartRow);
4291     else
4292       item = ME_FindRowWithNumber(editor, wParam);
4293     if (!item)
4294       return -1;
4295     para = ME_GetParagraph(item);
4296     item = ME_FindItemFwd(item, diRun);
4297     nCharOfs = para->member.para.nCharOfs + item->member.run.nCharOfs;
4298     TRACE("EM_LINEINDEX: nCharOfs==%d\n", nCharOfs);
4299     return nCharOfs;
4300   }
4301   case EM_LINELENGTH:
4302   {
4303     ME_DisplayItem *item, *item_end;
4304     int nChars = 0, nThisLineOfs = 0, nNextLineOfs = 0;
4305     ME_DisplayItem *para, *run;
4306 
4307     if (wParam > ME_GetTextLength(editor))
4308       return 0;
4309     if (wParam == -1)
4310     {
4311       FIXME("EM_LINELENGTH: returning number of unselected characters on lines with selection unsupported.\n");
4312       return 0;
4313     }
4314     ME_RunOfsFromCharOfs(editor, wParam, &para, &run, NULL);
4315     item = ME_RowStart(run);
4316     nThisLineOfs = ME_CharOfsFromRunOfs(editor, para, ME_FindItemFwd(item, diRun), 0);
4317     item_end = ME_FindItemFwd(item, diStartRowOrParagraphOrEnd);
4318     if (item_end->type == diStartRow) {
4319       nNextLineOfs = ME_CharOfsFromRunOfs(editor, para, ME_FindItemFwd(item_end, diRun), 0);
4320     } else {
4321       ME_DisplayItem *endRun = ME_FindItemBack(item_end, diRun);
4322       assert(endRun && endRun->member.run.nFlags & MERF_ENDPARA);
4323       nNextLineOfs = item_end->member.para.nCharOfs - endRun->member.run.len;
4324     }
4325     nChars = nNextLineOfs - nThisLineOfs;
4326     TRACE("EM_LINELENGTH(%ld)==%d\n",wParam, nChars);
4327     return nChars;
4328   }
4329   case EM_EXLIMITTEXT:
4330   {
4331     if ((int)lParam < 0)
4332      return 0;
4333     if (lParam == 0)
4334       editor->nTextLimit = 65536;
4335     else
4336       editor->nTextLimit = (int) lParam;
4337     return 0;
4338   }
4339   case EM_LIMITTEXT:
4340   {
4341     if (wParam == 0)
4342       editor->nTextLimit = 65536;
4343     else
4344       editor->nTextLimit = (int) wParam;
4345     return 0;
4346   }
4347   case EM_GETLIMITTEXT:
4348   {
4349     return editor->nTextLimit;
4350   }
4351   case EM_FINDTEXT:
4352   {
4353     LRESULT r;
4354     if(!unicode){
4355       FINDTEXTA *ft = (FINDTEXTA *)lParam;
4356       int nChars = MultiByteToWideChar(CP_ACP, 0, ft->lpstrText, -1, NULL, 0);
4357       WCHAR *tmp;
4358 
4359       if ((tmp = heap_alloc(nChars * sizeof(*tmp))) != NULL)
4360         MultiByteToWideChar(CP_ACP, 0, ft->lpstrText, -1, tmp, nChars);
4361       r = ME_FindText(editor, wParam, &ft->chrg, tmp, NULL);
4362       heap_free(tmp);
4363     }else{
4364       FINDTEXTW *ft = (FINDTEXTW *)lParam;
4365       r = ME_FindText(editor, wParam, &ft->chrg, ft->lpstrText, NULL);
4366     }
4367     return r;
4368   }
4369   case EM_FINDTEXTEX:
4370   {
4371     LRESULT r;
4372     if(!unicode){
4373       FINDTEXTEXA *ex = (FINDTEXTEXA *)lParam;
4374       int nChars = MultiByteToWideChar(CP_ACP, 0, ex->lpstrText, -1, NULL, 0);
4375       WCHAR *tmp;
4376 
4377       if ((tmp = heap_alloc(nChars * sizeof(*tmp))) != NULL)
4378         MultiByteToWideChar(CP_ACP, 0, ex->lpstrText, -1, tmp, nChars);
4379       r = ME_FindText(editor, wParam, &ex->chrg, tmp, &ex->chrgText);
4380       heap_free(tmp);
4381     }else{
4382       FINDTEXTEXW *ex = (FINDTEXTEXW *)lParam;
4383       r = ME_FindText(editor, wParam, &ex->chrg, ex->lpstrText, &ex->chrgText);
4384     }
4385     return r;
4386   }
4387   case EM_FINDTEXTW:
4388   {
4389     FINDTEXTW *ft = (FINDTEXTW *)lParam;
4390     return ME_FindText(editor, wParam, &ft->chrg, ft->lpstrText, NULL);
4391   }
4392   case EM_FINDTEXTEXW:
4393   {
4394     FINDTEXTEXW *ex = (FINDTEXTEXW *)lParam;
4395     return ME_FindText(editor, wParam, &ex->chrg, ex->lpstrText, &ex->chrgText);
4396   }
4397   case EM_GETZOOM:
4398     if (!wParam || !lParam)
4399       return FALSE;
4400     *(int *)wParam = editor->nZoomNumerator;
4401     *(int *)lParam = editor->nZoomDenominator;
4402     return TRUE;
4403   case EM_SETZOOM:
4404     return ME_SetZoom(editor, wParam, lParam);
4405   case EM_CHARFROMPOS:
4406   {
4407     ME_Cursor cursor;
4408     if (ME_CharFromPos(editor, ((POINTL *)lParam)->x, ((POINTL *)lParam)->y,
4409                        &cursor, NULL))
4410       return ME_GetCursorOfs(&cursor);
4411     else
4412       return -1;
4413   }
4414   case EM_POSFROMCHAR:
4415   {
4416     ME_DisplayItem *pPara, *pRun;
4417     int nCharOfs, nOffset, nLength;
4418     POINTL pt = {0,0};
4419 
4420     nCharOfs = wParam;
4421     /* detect which API version we're dealing with */
4422     if (wParam >= 0x40000)
4423         nCharOfs = lParam;
4424     nLength = ME_GetTextLength(editor);
4425     nCharOfs = min(nCharOfs, nLength);
4426     nCharOfs = max(nCharOfs, 0);
4427 
4428     ME_RunOfsFromCharOfs(editor, nCharOfs, &pPara, &pRun, &nOffset);
4429     assert(pRun->type == diRun);
4430     pt.y = pRun->member.run.pt.y;
4431     pt.x = pRun->member.run.pt.x + ME_PointFromChar(editor, &pRun->member.run, nOffset, TRUE);
4432     pt.y += pPara->member.para.pt.y + editor->rcFormat.top;
4433     pt.x += editor->rcFormat.left;
4434 
4435     pt.x -= editor->horz_si.nPos;
4436     pt.y -= editor->vert_si.nPos;
4437 
4438     if (wParam >= 0x40000) {
4439         *(POINTL *)wParam = pt;
4440     }
4441     return (wParam >= 0x40000) ? 0 : MAKELONG( pt.x, pt.y );
4442   }
4443   case WM_CREATE:
4444     return ME_WmCreate(editor, lParam, unicode);
4445   case WM_DESTROY:
4446     ME_DestroyEditor(editor);
4447     return 0;
4448   case WM_SETCURSOR:
4449   {
4450     POINT cursor_pos;
4451     if (wParam == (WPARAM)editor->hWnd && GetCursorPos(&cursor_pos) &&
4452         ScreenToClient(editor->hWnd, &cursor_pos))
4453       ME_LinkNotify(editor, msg, 0, MAKELPARAM(cursor_pos.x, cursor_pos.y));
4454     return ME_SetCursor(editor);
4455   }
4456   case WM_LBUTTONDBLCLK:
4457   case WM_LBUTTONDOWN:
4458   {
4459     ME_CommitUndo(editor); /* End coalesced undos for typed characters */
4460     if ((editor->nEventMask & ENM_MOUSEEVENTS) &&
4461         !ME_FilterEvent(editor, msg, &wParam, &lParam))
4462       return 0;
4463     ITextHost_TxSetFocus(editor->texthost);
4464     ME_LButtonDown(editor, (short)LOWORD(lParam), (short)HIWORD(lParam),
4465                    ME_CalculateClickCount(editor, msg, wParam, lParam));
4466     ITextHost_TxSetCapture(editor->texthost, TRUE);
4467     editor->bMouseCaptured = TRUE;
4468     ME_LinkNotify(editor, msg, wParam, lParam);
4469     if (!ME_SetCursor(editor)) goto do_default;
4470     break;
4471   }
4472   case WM_MOUSEMOVE:
4473     if ((editor->nEventMask & ENM_MOUSEEVENTS) &&
4474         !ME_FilterEvent(editor, msg, &wParam, &lParam))
4475       return 0;
4476     if (editor->bMouseCaptured)
4477       ME_MouseMove(editor, (short)LOWORD(lParam), (short)HIWORD(lParam));
4478     else
4479       ME_LinkNotify(editor, msg, wParam, lParam);
4480     /* Set cursor if mouse is captured, since WM_SETCURSOR won't be received. */
4481     if (editor->bMouseCaptured)
4482         ME_SetCursor(editor);
4483     break;
4484   case WM_LBUTTONUP:
4485     if (editor->bMouseCaptured) {
4486       ITextHost_TxSetCapture(editor->texthost, FALSE);
4487       editor->bMouseCaptured = FALSE;
4488     }
4489     if (editor->nSelectionType == stDocument)
4490       editor->nSelectionType = stPosition;
4491     if ((editor->nEventMask & ENM_MOUSEEVENTS) &&
4492         !ME_FilterEvent(editor, msg, &wParam, &lParam))
4493       return 0;
4494     else
4495     {
4496       ME_SetCursor(editor);
4497       ME_LinkNotify(editor, msg, wParam, lParam);
4498     }
4499     break;
4500   case WM_RBUTTONUP:
4501   case WM_RBUTTONDOWN:
4502   case WM_RBUTTONDBLCLK:
4503     ME_CommitUndo(editor); /* End coalesced undos for typed characters */
4504     if ((editor->nEventMask & ENM_MOUSEEVENTS) &&
4505         !ME_FilterEvent(editor, msg, &wParam, &lParam))
4506       return 0;
4507     ME_LinkNotify(editor, msg, wParam, lParam);
4508     goto do_default;
4509   case WM_CONTEXTMENU:
4510     if (!ME_ShowContextMenu(editor, (short)LOWORD(lParam), (short)HIWORD(lParam)))
4511       goto do_default;
4512     break;
4513   case WM_SETFOCUS:
4514     editor->bHaveFocus = TRUE;
4515     ME_ShowCaret(editor);
4516     ME_SendOldNotify(editor, EN_SETFOCUS);
4517     if (!editor->bHideSelection && !(editor->styleFlags & ES_NOHIDESEL))
4518         ME_InvalidateSelection( editor );
4519     return 0;
4520   case WM_KILLFOCUS:
4521     ME_CommitUndo(editor); /* End coalesced undos for typed characters */
4522     editor->bHaveFocus = FALSE;
4523     editor->wheel_remain = 0;
4524     ME_HideCaret(editor);
4525     ME_SendOldNotify(editor, EN_KILLFOCUS);
4526     if (!editor->bHideSelection && !(editor->styleFlags & ES_NOHIDESEL))
4527         ME_InvalidateSelection( editor );
4528     return 0;
4529   case WM_COMMAND:
4530     TRACE("editor wnd command = %d\n", LOWORD(wParam));
4531     return 0;
4532   case WM_KEYUP:
4533     if ((editor->nEventMask & ENM_KEYEVENTS) &&
4534         !ME_FilterEvent(editor, msg, &wParam, &lParam))
4535       return 0;
4536     goto do_default;
4537   case WM_KEYDOWN:
4538     if ((editor->nEventMask & ENM_KEYEVENTS) &&
4539         !ME_FilterEvent(editor, msg, &wParam, &lParam))
4540       return 0;
4541     if (ME_KeyDown(editor, LOWORD(wParam)))
4542       return 0;
4543     goto do_default;
4544   case WM_CHAR:
4545     if ((editor->nEventMask & ENM_KEYEVENTS) &&
4546         !ME_FilterEvent(editor, msg, &wParam, &lParam))
4547       return 0;
4548     return ME_Char(editor, wParam, lParam, unicode);
4549   case WM_UNICHAR:
4550     if (unicode)
4551     {
4552         if(wParam == UNICODE_NOCHAR) return TRUE;
4553         if(wParam <= 0x000fffff)
4554         {
4555             if(wParam > 0xffff) /* convert to surrogates */
4556             {
4557                 wParam -= 0x10000;
4558                 ME_Char(editor, (wParam >> 10) + 0xd800, 0, TRUE);
4559                 ME_Char(editor, (wParam & 0x03ff) + 0xdc00, 0, TRUE);
4560             } else {
4561               ME_Char(editor, wParam, 0, TRUE);
4562             }
4563         }
4564         return 0;
4565     }
4566     break;
4567   case EM_STOPGROUPTYPING:
4568     ME_CommitUndo(editor); /* End coalesced undos for typed characters */
4569     return 0;
4570   case WM_HSCROLL:
4571   {
4572     const int scrollUnit = 7;
4573 
4574     switch(LOWORD(wParam))
4575     {
4576       case SB_LEFT:
4577         ME_ScrollAbs(editor, 0, 0);
4578         break;
4579       case SB_RIGHT:
4580         ME_ScrollAbs(editor,
4581                      editor->horz_si.nMax - (int)editor->horz_si.nPage,
4582                      editor->vert_si.nMax - (int)editor->vert_si.nPage);
4583         break;
4584       case SB_LINELEFT:
4585         ME_ScrollLeft(editor, scrollUnit);
4586         break;
4587       case SB_LINERIGHT:
4588         ME_ScrollRight(editor, scrollUnit);
4589         break;
4590       case SB_PAGELEFT:
4591         ME_ScrollLeft(editor, editor->sizeWindow.cx);
4592         break;
4593       case SB_PAGERIGHT:
4594         ME_ScrollRight(editor, editor->sizeWindow.cx);
4595         break;
4596       case SB_THUMBTRACK:
4597       case SB_THUMBPOSITION:
4598       {
4599         int pos = HIWORD(wParam);
4600         if (editor->horz_si.nMax > 0xffff)
4601           pos = MulDiv(pos, editor->horz_si.nMax, 0xffff);
4602         ME_HScrollAbs(editor, pos);
4603         break;
4604       }
4605     }
4606     break;
4607   }
4608   case EM_SCROLL: /* fall through */
4609   case WM_VSCROLL:
4610   {
4611     int origNPos;
4612     int lineHeight = get_default_line_height( editor );
4613 
4614     origNPos = editor->vert_si.nPos;
4615 
4616     switch(LOWORD(wParam))
4617     {
4618       case SB_TOP:
4619         ME_ScrollAbs(editor, 0, 0);
4620         break;
4621       case SB_BOTTOM:
4622         ME_ScrollAbs(editor,
4623                      editor->horz_si.nMax - (int)editor->horz_si.nPage,
4624                      editor->vert_si.nMax - (int)editor->vert_si.nPage);
4625         break;
4626       case SB_LINEUP:
4627         ME_ScrollUp(editor,lineHeight);
4628         break;
4629       case SB_LINEDOWN:
4630         ME_ScrollDown(editor,lineHeight);
4631         break;
4632       case SB_PAGEUP:
4633         ME_ScrollUp(editor,editor->sizeWindow.cy);
4634         break;
4635       case SB_PAGEDOWN:
4636         ME_ScrollDown(editor,editor->sizeWindow.cy);
4637         break;
4638       case SB_THUMBTRACK:
4639       case SB_THUMBPOSITION:
4640       {
4641         int pos = HIWORD(wParam);
4642         if (editor->vert_si.nMax > 0xffff)
4643           pos = MulDiv(pos, editor->vert_si.nMax, 0xffff);
4644         ME_VScrollAbs(editor, pos);
4645         break;
4646       }
4647     }
4648     if (msg == EM_SCROLL)
4649       return 0x00010000 | (((editor->vert_si.nPos - origNPos)/lineHeight) & 0xffff);
4650     break;
4651   }
4652   case WM_MOUSEWHEEL:
4653   {
4654     int delta;
4655     BOOL ctrl_is_down;
4656 
4657     if ((editor->nEventMask & ENM_MOUSEEVENTS) &&
4658         !ME_FilterEvent(editor, msg, &wParam, &lParam))
4659       return 0;
4660 
4661     ctrl_is_down = GetKeyState(VK_CONTROL) & 0x8000;
4662 
4663     delta = GET_WHEEL_DELTA_WPARAM(wParam);
4664 
4665     /* if scrolling changes direction, ignore left overs */
4666     if ((delta < 0 && editor->wheel_remain < 0) ||
4667         (delta > 0 && editor->wheel_remain > 0))
4668       editor->wheel_remain += delta;
4669     else
4670       editor->wheel_remain = delta;
4671 
4672     if (editor->wheel_remain)
4673     {
4674       if (ctrl_is_down) {
4675         int numerator;
4676         if (!editor->nZoomNumerator || !editor->nZoomDenominator)
4677         {
4678           numerator = 100;
4679         } else {
4680           numerator = editor->nZoomNumerator * 100 / editor->nZoomDenominator;
4681         }
4682         numerator += calc_wheel_change( &editor->wheel_remain, 10 );
4683         if (numerator >= 10 && numerator <= 500)
4684           ME_SetZoom(editor, numerator, 100);
4685       } else {
4686         UINT max_lines = 3;
4687         int lines = 0;
4688 
4689         SystemParametersInfoW( SPI_GETWHEELSCROLLLINES, 0, &max_lines, 0 );
4690         if (max_lines)
4691           lines = calc_wheel_change( &editor->wheel_remain, (int)max_lines );
4692         if (lines)
4693           ME_ScrollDown( editor, -lines * get_default_line_height( editor ) );
4694       }
4695     }
4696     break;
4697   }
4698   case EM_GETRECT:
4699   {
4700     *((RECT *)lParam) = editor->rcFormat;
4701     if (editor->bDefaultFormatRect)
4702       ((RECT *)lParam)->left -= editor->selofs;
4703     return 0;
4704   }
4705   case EM_SETRECT:
4706   case EM_SETRECTNP:
4707   {
4708     if (lParam)
4709     {
4710       int border = 0;
4711       RECT clientRect;
4712       RECT *rc = (RECT *)lParam;
4713 
4714       border = editor->exStyleFlags & WS_EX_CLIENTEDGE ? 1 : 0;
4715       ITextHost_TxGetClientRect(editor->texthost, &clientRect);
4716       if (wParam == 0)
4717       {
4718         editor->rcFormat.top = max(0, rc->top - border);
4719         editor->rcFormat.left = max(0, rc->left - border);
4720         editor->rcFormat.bottom = min(clientRect.bottom, rc->bottom);
4721         editor->rcFormat.right = min(clientRect.right, rc->right + border);
4722       } else if (wParam == 1) {
4723         /* MSDN incorrectly says a wParam value of 1 causes the
4724          * lParam rect to be used as a relative offset,
4725          * however, the tests show it just prevents min/max bound
4726          * checking. */
4727         editor->rcFormat.top = rc->top - border;
4728         editor->rcFormat.left = rc->left - border;
4729         editor->rcFormat.bottom = rc->bottom;
4730         editor->rcFormat.right = rc->right + border;
4731       } else {
4732         return 0;
4733       }
4734       editor->bDefaultFormatRect = FALSE;
4735     }
4736     else
4737     {
4738       ME_SetDefaultFormatRect(editor);
4739       editor->bDefaultFormatRect = TRUE;
4740     }
4741     ME_MarkAllForWrapping(editor);
4742     ME_WrapMarkedParagraphs(editor);
4743     ME_UpdateScrollBar(editor);
4744     if (msg != EM_SETRECTNP)
4745       ME_Repaint(editor);
4746     return 0;
4747   }
4748   case EM_REQUESTRESIZE:
4749     ME_SendRequestResize(editor, TRUE);
4750     return 0;
4751   case WM_SETREDRAW:
4752     goto do_default;
4753   case WM_WINDOWPOSCHANGED:
4754   {
4755     RECT clientRect;
4756     WINDOWPOS *winpos = (WINDOWPOS *)lParam;
4757 
4758     if (winpos->flags & SWP_NOCLIENTSIZE) goto do_default;
4759     ITextHost_TxGetClientRect(editor->texthost, &clientRect);
4760     if (editor->bDefaultFormatRect) {
4761       ME_SetDefaultFormatRect(editor);
4762     } else {
4763       editor->rcFormat.right += clientRect.right - editor->prevClientRect.right;
4764       editor->rcFormat.bottom += clientRect.bottom - editor->prevClientRect.bottom;
4765     }
4766     editor->prevClientRect = clientRect;
4767     ME_RewrapRepaint(editor);
4768     goto do_default;
4769   }
4770   /* IME messages to make richedit controls IME aware */
4771   case WM_IME_SETCONTEXT:
4772   case WM_IME_CONTROL:
4773   case WM_IME_SELECT:
4774   case WM_IME_COMPOSITIONFULL:
4775     return 0;
4776   case WM_IME_STARTCOMPOSITION:
4777   {
4778     editor->imeStartIndex=ME_GetCursorOfs(&editor->pCursors[0]);
4779     ME_DeleteSelection(editor);
4780     ME_CommitUndo(editor);
4781     ME_UpdateRepaint(editor, FALSE);
4782     return 0;
4783   }
4784   case WM_IME_COMPOSITION:
4785   {
4786     HIMC hIMC;
4787 
4788     ME_Style *style = ME_GetInsertStyle(editor, 0);
4789     hIMC = ITextHost_TxImmGetContext(editor->texthost);
4790     ME_DeleteSelection(editor);
4791     ME_SaveTempStyle(editor, style);
4792     if (lParam & (GCS_RESULTSTR|GCS_COMPSTR))
4793     {
4794         LPWSTR lpCompStr = NULL;
4795         DWORD dwBufLen;
4796         DWORD dwIndex = lParam & GCS_RESULTSTR;
4797         if (!dwIndex)
4798           dwIndex = GCS_COMPSTR;
4799 
4800         dwBufLen = ImmGetCompositionStringW(hIMC, dwIndex, NULL, 0);
4801         lpCompStr = HeapAlloc(GetProcessHeap(),0,dwBufLen + sizeof(WCHAR));
4802         ImmGetCompositionStringW(hIMC, dwIndex, lpCompStr, dwBufLen);
4803         lpCompStr[dwBufLen/sizeof(WCHAR)] = 0;
4804         ME_InsertTextFromCursor(editor,0,lpCompStr,dwBufLen/sizeof(WCHAR),style);
4805         HeapFree(GetProcessHeap(), 0, lpCompStr);
4806 
4807         if (dwIndex == GCS_COMPSTR)
4808           ME_SetSelection(editor,editor->imeStartIndex,
4809                           editor->imeStartIndex + dwBufLen/sizeof(WCHAR));
4810     }
4811     ME_ReleaseStyle(style);
4812     ME_CommitUndo(editor);
4813     ME_UpdateRepaint(editor, FALSE);
4814     return 0;
4815   }
4816   case WM_IME_ENDCOMPOSITION:
4817   {
4818     ME_DeleteSelection(editor);
4819     editor->imeStartIndex=-1;
4820     return 0;
4821   }
4822   case EM_GETOLEINTERFACE:
4823   {
4824     if (!editor->reOle)
4825       if (!CreateIRichEditOle(NULL, editor, (LPVOID *)&editor->reOle))
4826         return 0;
4827     *(LPVOID *)lParam = editor->reOle;
4828     IRichEditOle_AddRef(editor->reOle);
4829     return 1;
4830   }
4831   case EM_GETPASSWORDCHAR:
4832   {
4833     return editor->cPasswordMask;
4834   }
4835   case EM_SETOLECALLBACK:
4836     if(editor->lpOleCallback)
4837       IRichEditOleCallback_Release(editor->lpOleCallback);
4838     editor->lpOleCallback = (IRichEditOleCallback*)lParam;
4839     if(editor->lpOleCallback)
4840       IRichEditOleCallback_AddRef(editor->lpOleCallback);
4841     return TRUE;
4842   case EM_GETWORDBREAKPROC:
4843     return (LRESULT)editor->pfnWordBreak;
4844   case EM_SETWORDBREAKPROC:
4845   {
4846     EDITWORDBREAKPROCW pfnOld = editor->pfnWordBreak;
4847 
4848     editor->pfnWordBreak = (EDITWORDBREAKPROCW)lParam;
4849     return (LRESULT)pfnOld;
4850   }
4851   case EM_GETTEXTMODE:
4852     return editor->mode;
4853   case EM_SETTEXTMODE:
4854   {
4855     int mask = 0;
4856     int changes = 0;
4857 
4858     if (ME_GetTextLength(editor) ||
4859         !list_empty( &editor->undo_stack ) || !list_empty( &editor->redo_stack ))
4860       return E_UNEXPECTED;
4861 
4862     /* Check for mutually exclusive flags in adjacent bits of wParam */
4863     if ((wParam & (TM_RICHTEXT | TM_MULTILEVELUNDO | TM_MULTICODEPAGE)) &
4864         (wParam & (TM_PLAINTEXT | TM_SINGLELEVELUNDO | TM_SINGLECODEPAGE)) << 1)
4865       return E_INVALIDARG;
4866 
4867     if (wParam & (TM_RICHTEXT | TM_PLAINTEXT))
4868     {
4869       mask |= TM_RICHTEXT | TM_PLAINTEXT;
4870       changes |= wParam & (TM_RICHTEXT | TM_PLAINTEXT);
4871       if (wParam & TM_PLAINTEXT) {
4872         /* Clear selection since it should be possible to select the
4873          * end of text run for rich text */
4874         ME_InvalidateSelection(editor);
4875         ME_SetCursorToStart(editor, &editor->pCursors[0]);
4876         editor->pCursors[1] = editor->pCursors[0];
4877         /* plain text can only have the default style. */
4878         ME_ClearTempStyle(editor);
4879         ME_AddRefStyle(editor->pBuffer->pDefaultStyle);
4880         ME_ReleaseStyle(editor->pCursors[0].pRun->member.run.style);
4881         editor->pCursors[0].pRun->member.run.style = editor->pBuffer->pDefaultStyle;
4882       }
4883     }
4884     /* FIXME: Currently no support for undo level and code page options */
4885     editor->mode = (editor->mode & ~mask) | changes;
4886     return 0;
4887   }
4888   case EM_SETPASSWORDCHAR:
4889   {
4890     editor->cPasswordMask = wParam;
4891     ME_RewrapRepaint(editor);
4892     return 0;
4893   }
4894   case EM_SETTARGETDEVICE:
4895     if (wParam == 0)
4896     {
4897       BOOL new = (lParam == 0 && (editor->styleFlags & ES_MULTILINE));
4898       if (editor->nAvailWidth || editor->bWordWrap != new)
4899       {
4900         editor->bWordWrap = new;
4901         editor->nAvailWidth = 0; /* wrap to client area */
4902         ME_RewrapRepaint(editor);
4903       }
4904     } else {
4905       int width = max(0, lParam);
4906       if ((editor->styleFlags & ES_MULTILINE) &&
4907           (!editor->bWordWrap || editor->nAvailWidth != width))
4908       {
4909         editor->nAvailWidth = width;
4910         editor->bWordWrap = TRUE;
4911         ME_RewrapRepaint(editor);
4912       }
4913       FIXME("EM_SETTARGETDEVICE doesn't use non-NULL target devices\n");
4914     }
4915     return TRUE;
4916   default:
4917   do_default:
4918     *phresult = S_FALSE;
4919     break;
4920   }
4921   return 0L;
4922 }
4923 
4924 static BOOL create_windowed_editor(HWND hwnd, CREATESTRUCTW *create, BOOL emulate_10)
4925 {
4926     ITextHost *host = ME_CreateTextHost( hwnd, create, emulate_10 );
4927     ME_TextEditor *editor;
4928 
4929     if (!host) return FALSE;
4930 
4931     editor = ME_MakeEditor( host, emulate_10 );
4932     if (!editor)
4933     {
4934         ITextHost_Release( host );
4935         return FALSE;
4936     }
4937 
4938     editor->exStyleFlags = GetWindowLongW( hwnd, GWL_EXSTYLE );
4939     editor->styleFlags |= GetWindowLongW( hwnd, GWL_STYLE ) & ES_WANTRETURN;
4940     editor->hWnd = hwnd; /* FIXME: Remove editor's dependence on hWnd */
4941     editor->hwndParent = create->hwndParent;
4942 
4943     SetWindowLongPtrW( hwnd, 0, (LONG_PTR)editor );
4944 
4945     return TRUE;
4946 }
4947 
4948 static LRESULT RichEditWndProc_common(HWND hWnd, UINT msg, WPARAM wParam,
4949                                       LPARAM lParam, BOOL unicode)
4950 {
4951   ME_TextEditor *editor;
4952   HRESULT hresult;
4953   LRESULT lresult = 0;
4954 
4955   TRACE("enter hwnd %p msg %04x (%s) %lx %lx, unicode %d\n",
4956         hWnd, msg, get_msg_name(msg), wParam, lParam, unicode);
4957 
4958   editor = (ME_TextEditor *)GetWindowLongPtrW(hWnd, 0);
4959   if (!editor)
4960   {
4961     if (msg == WM_NCCREATE)
4962     {
4963       CREATESTRUCTW *pcs = (CREATESTRUCTW *)lParam;
4964 
4965       TRACE("WM_NCCREATE: hWnd %p style 0x%08x\n", hWnd, pcs->style);
4966       return create_windowed_editor( hWnd, pcs, FALSE );
4967     }
4968     else
4969     {
4970       return DefWindowProcW(hWnd, msg, wParam, lParam);
4971     }
4972   }
4973 
4974   switch (msg)
4975   {
4976     case WM_PAINT:
4977     {
4978       HDC hDC;
4979       RECT rc;
4980       PAINTSTRUCT ps;
4981 
4982       hDC = BeginPaint(editor->hWnd, &ps);
4983       if (!editor->bEmulateVersion10 || (editor->nEventMask & ENM_UPDATE))
4984         ME_SendOldNotify(editor, EN_UPDATE);
4985       /* Erase area outside of the formatting rectangle */
4986       if (ps.rcPaint.top < editor->rcFormat.top)
4987       {
4988         rc = ps.rcPaint;
4989         rc.bottom = editor->rcFormat.top;
4990         FillRect(hDC, &rc, editor->hbrBackground);
4991         ps.rcPaint.top = editor->rcFormat.top;
4992       }
4993       if (ps.rcPaint.bottom > editor->rcFormat.bottom) {
4994         rc = ps.rcPaint;
4995         rc.top = editor->rcFormat.bottom;
4996         FillRect(hDC, &rc, editor->hbrBackground);
4997         ps.rcPaint.bottom = editor->rcFormat.bottom;
4998       }
4999       if (ps.rcPaint.left < editor->rcFormat.left) {
5000         rc = ps.rcPaint;
5001         rc.right = editor->rcFormat.left;
5002         FillRect(hDC, &rc, editor->hbrBackground);
5003         ps.rcPaint.left = editor->rcFormat.left;
5004       }
5005       if (ps.rcPaint.right > editor->rcFormat.right) {
5006         rc = ps.rcPaint;
5007         rc.left = editor->rcFormat.right;
5008         FillRect(hDC, &rc, editor->hbrBackground);
5009         ps.rcPaint.right = editor->rcFormat.right;
5010       }
5011 
5012       ME_PaintContent(editor, hDC, &ps.rcPaint);
5013       EndPaint(editor->hWnd, &ps);
5014       return 0;
5015     }
5016     case WM_ERASEBKGND:
5017     {
5018       HDC hDC = (HDC)wParam;
5019       RECT rc;
5020 
5021       if (GetUpdateRect(editor->hWnd, &rc, TRUE))
5022         FillRect(hDC, &rc, editor->hbrBackground);
5023       return 1;
5024     }
5025     case EM_SETOPTIONS:
5026     {
5027       DWORD dwStyle;
5028       const DWORD mask = ECO_VERTICAL | ECO_AUTOHSCROLL | ECO_AUTOVSCROLL |
5029                          ECO_NOHIDESEL | ECO_READONLY | ECO_WANTRETURN |
5030                          ECO_SELECTIONBAR;
5031       lresult = ME_HandleMessage(editor, msg, wParam, lParam, unicode, &hresult);
5032       dwStyle = GetWindowLongW(hWnd, GWL_STYLE);
5033       dwStyle = (dwStyle & ~mask) | (lresult & mask);
5034       SetWindowLongW(hWnd, GWL_STYLE, dwStyle);
5035       return lresult;
5036     }
5037     case EM_SETREADONLY:
5038     {
5039       DWORD dwStyle;
5040       lresult = ME_HandleMessage(editor, msg, wParam, lParam, unicode, &hresult);
5041       dwStyle = GetWindowLongW(hWnd, GWL_STYLE);
5042       dwStyle &= ~ES_READONLY;
5043       if (wParam)
5044         dwStyle |= ES_READONLY;
5045       SetWindowLongW(hWnd, GWL_STYLE, dwStyle);
5046       return lresult;
5047     }
5048     default:
5049       lresult = ME_HandleMessage(editor, msg, wParam, lParam, unicode, &hresult);
5050   }
5051 
5052   if (hresult == S_FALSE)
5053     lresult = DefWindowProcW(hWnd, msg, wParam, lParam);
5054 
5055   TRACE("exit hwnd %p msg %04x (%s) %lx %lx, unicode %d -> %lu\n",
5056         hWnd, msg, get_msg_name(msg), wParam, lParam, unicode, lresult);
5057 
5058   return lresult;
5059 }
5060 
5061 static LRESULT WINAPI RichEditWndProcW(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
5062 {
5063     BOOL unicode = TRUE;
5064 
5065     /* Under Win9x RichEdit20W returns ANSI strings, see the tests. */
5066     if (msg == WM_GETTEXT && (GetVersion() & 0x80000000))
5067         unicode = FALSE;
5068 
5069     return RichEditWndProc_common(hWnd, msg, wParam, lParam, unicode);
5070 }
5071 
5072 static LRESULT WINAPI RichEditWndProcA(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
5073 {
5074     return RichEditWndProc_common(hWnd, msg, wParam, lParam, FALSE);
5075 }
5076 
5077 /******************************************************************
5078  *        RichEditANSIWndProc (RICHED20.10)
5079  */
5080 LRESULT WINAPI RichEditANSIWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
5081 {
5082     return RichEditWndProcA(hWnd, msg, wParam, lParam);
5083 }
5084 
5085 /******************************************************************
5086  *        RichEdit10ANSIWndProc (RICHED20.9)
5087  */
5088 LRESULT WINAPI RichEdit10ANSIWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
5089 {
5090   if (msg == WM_NCCREATE && !GetWindowLongPtrW(hWnd, 0))
5091   {
5092     CREATESTRUCTW *pcs = (CREATESTRUCTW *)lParam;
5093 
5094     TRACE("WM_NCCREATE: hWnd %p style 0x%08x\n", hWnd, pcs->style);
5095     return create_windowed_editor( hWnd, pcs, TRUE );
5096   }
5097   return RichEditANSIWndProc(hWnd, msg, wParam, lParam);
5098 }
5099 
5100 void ME_SendOldNotify(ME_TextEditor *editor, int nCode)
5101 {
5102   ITextHost_TxNotify(editor->texthost, nCode, NULL);
5103 }
5104 
5105 /* Fill buffer with srcChars unicode characters from the start cursor.
5106  *
5107  * buffer: destination buffer
5108  * buflen: length of buffer in characters excluding the NULL terminator.
5109  * start: start of editor text to copy into buffer.
5110  * srcChars: Number of characters to use from the editor text.
5111  * bCRLF: if true, replaces all end of lines with \r\n pairs.
5112  *
5113  * returns the number of characters written excluding the NULL terminator.
5114  *
5115  * The written text is always NULL terminated.
5116  */
5117 int ME_GetTextW(ME_TextEditor *editor, WCHAR *buffer, int buflen,
5118                 const ME_Cursor *start, int srcChars, BOOL bCRLF,
5119                 BOOL bEOP)
5120 {
5121   ME_DisplayItem *pRun, *pNextRun;
5122   const WCHAR *pStart = buffer;
5123   const WCHAR cr_lf[] = {'\r', '\n', 0};
5124   const WCHAR *str;
5125   int nLen;
5126 
5127   /* bCRLF flag is only honored in 2.0 and up. 1.0 must always return text verbatim */
5128   if (editor->bEmulateVersion10) bCRLF = FALSE;
5129 
5130   pRun = start->pRun;
5131   assert(pRun);
5132   pNextRun = ME_FindItemFwd(pRun, diRun);
5133 
5134   nLen = pRun->member.run.len - start->nOffset;
5135   str = get_text( &pRun->member.run, start->nOffset );
5136 
5137   while (srcChars && buflen && pNextRun)
5138   {
5139     int nFlags = pRun->member.run.nFlags;
5140 
5141     if (bCRLF && nFlags & MERF_ENDPARA && ~nFlags & MERF_ENDCELL)
5142     {
5143       if (buflen == 1) break;
5144       /* FIXME: native fails to reduce srcChars here for WM_GETTEXT or
5145        *        EM_GETTEXTEX, however, this is done for copying text which
5146        *        also uses this function. */
5147       srcChars -= min(nLen, srcChars);
5148       nLen = 2;
5149       str = cr_lf;
5150     } else {
5151       nLen = min(nLen, srcChars);
5152       srcChars -= nLen;
5153     }
5154 
5155     nLen = min(nLen, buflen);
5156     buflen -= nLen;
5157 
5158     CopyMemory(buffer, str, sizeof(WCHAR) * nLen);
5159 
5160     buffer += nLen;
5161 
5162     pRun = pNextRun;
5163     pNextRun = ME_FindItemFwd(pRun, diRun);
5164 
5165     nLen = pRun->member.run.len;
5166     str = get_text( &pRun->member.run, 0 );
5167   }
5168   /* append '\r' to the last paragraph. */
5169   if (pRun->next->type == diTextEnd && bEOP)
5170   {
5171     *buffer = '\r';
5172     buffer ++;
5173   }
5174   *buffer = 0;
5175   return buffer - pStart;
5176 }
5177 
5178 static BOOL ME_RegisterEditorClass(HINSTANCE hInstance)
5179 {
5180   WNDCLASSW wcW;
5181   WNDCLASSA wcA;
5182 
5183   wcW.style = CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW | CS_GLOBALCLASS;
5184   wcW.lpfnWndProc = RichEditWndProcW;
5185   wcW.cbClsExtra = 0;
5186   wcW.cbWndExtra = sizeof(ME_TextEditor *);
5187   wcW.hInstance = NULL; /* hInstance would register DLL-local class */
5188   wcW.hIcon = NULL;
5189   wcW.hCursor = LoadCursorW(NULL, (LPWSTR)IDC_IBEAM);
5190   wcW.hbrBackground = GetStockObject(NULL_BRUSH);
5191   wcW.lpszMenuName = NULL;
5192 
5193   if (is_version_nt())
5194   {
5195     wcW.lpszClassName = RICHEDIT_CLASS20W;
5196     if (!RegisterClassW(&wcW)) return FALSE;
5197     wcW.lpszClassName = MSFTEDIT_CLASS;
5198     if (!RegisterClassW(&wcW)) return FALSE;
5199   }
5200   else
5201   {
5202     /* WNDCLASSA/W have the same layout */
5203     wcW.lpszClassName = (LPCWSTR)"RichEdit20W";
5204     if (!RegisterClassA((WNDCLASSA *)&wcW)) return FALSE;
5205     wcW.lpszClassName = (LPCWSTR)"RichEdit50W";
5206     if (!RegisterClassA((WNDCLASSA *)&wcW)) return FALSE;
5207   }
5208 
5209   wcA.style = CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW | CS_GLOBALCLASS;
5210   wcA.lpfnWndProc = RichEditWndProcA;
5211   wcA.cbClsExtra = 0;
5212   wcA.cbWndExtra = sizeof(ME_TextEditor *);
5213   wcA.hInstance = NULL; /* hInstance would register DLL-local class */
5214   wcA.hIcon = NULL;
5215   wcA.hCursor = LoadCursorW(NULL, (LPWSTR)IDC_IBEAM);
5216   wcA.hbrBackground = GetStockObject(NULL_BRUSH);
5217   wcA.lpszMenuName = NULL;
5218   wcA.lpszClassName = RICHEDIT_CLASS20A;
5219   if (!RegisterClassA(&wcA)) return FALSE;
5220   wcA.lpszClassName = "RichEdit50A";
5221   if (!RegisterClassA(&wcA)) return FALSE;
5222 
5223   return TRUE;
5224 }
5225 
5226 static LRESULT WINAPI REComboWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) {
5227   /* FIXME: Not implemented */
5228   TRACE("hWnd %p msg %04x (%s) %08lx %08lx\n",
5229         hWnd, msg, get_msg_name(msg), wParam, lParam);
5230   return DefWindowProcW(hWnd, msg, wParam, lParam);
5231 }
5232 
5233 static LRESULT WINAPI REListWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) {
5234   /* FIXME: Not implemented */
5235   TRACE("hWnd %p msg %04x (%s) %08lx %08lx\n",
5236         hWnd, msg, get_msg_name(msg), wParam, lParam);
5237   return DefWindowProcW(hWnd, msg, wParam, lParam);
5238 }
5239 
5240 /******************************************************************
5241  *        REExtendedRegisterClass (RICHED20.8)
5242  *
5243  * FIXME undocumented
5244  * Need to check for errors and implement controls and callbacks
5245  */
5246 LRESULT WINAPI REExtendedRegisterClass(void)
5247 {
5248   WNDCLASSW wcW;
5249   UINT result;
5250 
5251   FIXME("semi stub\n");
5252 
5253   wcW.cbClsExtra = 0;
5254   wcW.cbWndExtra = 4;
5255   wcW.hInstance = NULL;
5256   wcW.hIcon = NULL;
5257   wcW.hCursor = NULL;
5258   wcW.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
5259   wcW.lpszMenuName = NULL;
5260 
5261   if (!ME_ListBoxRegistered)
5262   {
5263       wcW.style = CS_PARENTDC | CS_DBLCLKS | CS_GLOBALCLASS;
5264       wcW.lpfnWndProc = REListWndProc;
5265       wcW.lpszClassName = REListBox20W;
5266       if (RegisterClassW(&wcW)) ME_ListBoxRegistered = TRUE;
5267   }
5268 
5269   if (!ME_ComboBoxRegistered)
5270   {
5271       wcW.style = CS_PARENTDC | CS_DBLCLKS | CS_GLOBALCLASS | CS_VREDRAW | CS_HREDRAW;
5272       wcW.lpfnWndProc = REComboWndProc;
5273       wcW.lpszClassName = REComboBox20W;
5274       if (RegisterClassW(&wcW)) ME_ComboBoxRegistered = TRUE;
5275   }
5276 
5277   result = 0;
5278   if (ME_ListBoxRegistered)
5279       result += 1;
5280   if (ME_ComboBoxRegistered)
5281       result += 2;
5282 
5283   return result;
5284 }
5285 
5286 static int wchar_comp( const void *key, const void *elem )
5287 {
5288     return *(const WCHAR *)key - *(const WCHAR *)elem;
5289 }
5290 
5291 /* neutral characters end the url if the next non-neutral character is a space character,
5292    otherwise they are included in the url. */
5293 static BOOL isurlneutral( WCHAR c )
5294 {
5295     /* NB this list is sorted */
5296     static const WCHAR neutral_chars[] = {'!','\"','\'','(',')',',','-','.',':',';','<','>','?','[',']','{','}'};
5297 
5298     /* Some shortcuts */
5299     if (isalnum( c )) return FALSE;
5300     if (c > neutral_chars[ARRAY_SIZE( neutral_chars ) - 1]) return FALSE;
5301 
5302     return !!bsearch( &c, neutral_chars, ARRAY_SIZE( neutral_chars ), sizeof(c), wchar_comp );
5303 }
5304 
5305 /**
5306  * This proc takes a selection, and scans it forward in order to select the span
5307  * of a possible URL candidate. A possible URL candidate must start with isalnum
5308  * or one of the following special characters: *|/\+%#@ and must consist entirely
5309  * of the characters allowed to start the URL, plus : (colon) which may occur
5310  * at most once, and not at either end.
5311  */
5312 static BOOL ME_FindNextURLCandidate(ME_TextEditor *editor,
5313                                     const ME_Cursor *start,
5314                                     int nChars,
5315                                     ME_Cursor *candidate_min,
5316                                     ME_Cursor *candidate_max)
5317 {
5318   ME_Cursor cursor = *start, neutral_end, space_end;
5319   BOOL candidateStarted = FALSE, quoted = FALSE;
5320   WCHAR c;
5321 
5322   while (nChars > 0)
5323   {
5324     WCHAR *str = get_text( &cursor.pRun->member.run, 0 );
5325     int run_len = cursor.pRun->member.run.len;
5326 
5327     nChars -= run_len - cursor.nOffset;
5328 
5329     /* Find start of candidate */
5330     if (!candidateStarted)
5331     {
5332       while (cursor.nOffset < run_len)
5333       {
5334         c = str[cursor.nOffset];
5335         if (!isspaceW( c ) && !isurlneutral( c ))
5336         {
5337           *candidate_min = cursor;
5338           candidateStarted = TRUE;
5339           neutral_end.pPara = NULL;
5340           space_end.pPara = NULL;
5341           cursor.nOffset++;
5342           break;
5343         }
5344         quoted = (c == '<');
5345         cursor.nOffset++;
5346       }
5347     }
5348 
5349     /* Find end of candidate */
5350     if (candidateStarted)
5351     {
5352       while (cursor.nOffset < run_len)
5353       {
5354         c = str[cursor.nOffset];
5355         if (isspaceW( c ))
5356         {
5357           if (quoted && c != '\r')
5358           {
5359             if (!space_end.pPara)
5360             {
5361               if (neutral_end.pPara)
5362                 space_end = neutral_end;
5363               else
5364                 space_end = cursor;
5365             }
5366           }
5367           else
5368             goto done;
5369         }
5370         else if (isurlneutral( c ))
5371         {
5372           if (quoted && c == '>')
5373           {
5374             neutral_end.pPara = NULL;
5375             space_end.pPara = NULL;
5376             goto done;
5377           }
5378           if (!neutral_end.pPara)
5379             neutral_end = cursor;
5380         }
5381         else
5382           neutral_end.pPara = NULL;
5383 
5384         cursor.nOffset++;
5385       }
5386     }
5387 
5388     cursor.nOffset = 0;
5389     if (!ME_NextRun(&cursor.pPara, &cursor.pRun, TRUE))
5390       goto done;
5391   }
5392 
5393 done:
5394   if (candidateStarted)
5395   {
5396     if (space_end.pPara)
5397       *candidate_max = space_end;
5398     else if (neutral_end.pPara)
5399       *candidate_max = neutral_end;
5400     else
5401       *candidate_max = cursor;
5402     return TRUE;
5403   }
5404   *candidate_max = *candidate_min = cursor;
5405   return FALSE;
5406 }
5407 
5408 /**
5409  * This proc evaluates the selection and returns TRUE if it can be considered an URL
5410  */
5411 static BOOL ME_IsCandidateAnURL(ME_TextEditor *editor, const ME_Cursor *start, int nChars)
5412 {
5413 #define MAX_PREFIX_LEN 9
5414   struct prefix_s {
5415     const WCHAR text[MAX_PREFIX_LEN];
5416     int length;
5417   }prefixes[] = {
5418     {{'p','r','o','s','p','e','r','o',':'}, 9},
5419     {{'t','e','l','n','e','t',':'}, 7},
5420     {{'g','o','p','h','e','r',':'}, 7},
5421     {{'m','a','i','l','t','o',':'}, 7},
5422     {{'h','t','t','p','s',':'}, 6},
5423     {{'f','i','l','e',':'}, 5},
5424     {{'n','e','w','s',':'}, 5},
5425     {{'w','a','i','s',':'}, 5},
5426     {{'n','n','t','p',':'}, 5},
5427     {{'h','t','t','p',':'}, 5},
5428     {{'w','w','w','.'}, 4},
5429     {{'f','t','p',':'}, 4},
5430   };
5431   WCHAR bufferW[MAX_PREFIX_LEN + 1];
5432   unsigned int i;
5433 
5434   ME_GetTextW(editor, bufferW, MAX_PREFIX_LEN, start, nChars, FALSE, FALSE);
5435   for (i = 0; i < ARRAY_SIZE(prefixes); i++)
5436   {
5437     if (nChars < prefixes[i].length) continue;
5438     if (!memcmp(prefixes[i].text, bufferW, prefixes[i].length * sizeof(WCHAR)))
5439       return TRUE;
5440   }
5441   return FALSE;
5442 #undef MAX_PREFIX_LEN
5443 }
5444 
5445 /**
5446  * This proc walks through the indicated selection and evaluates whether each
5447  * section identified by ME_FindNextURLCandidate and in-between sections have
5448  * their proper CFE_LINK attributes set or unset. If the CFE_LINK attribute is
5449  * not what it is supposed to be, this proc sets or unsets it as appropriate.
5450  *
5451  * Since this function can cause runs to be split, do not depend on the value
5452  * of the start cursor at the end of the function.
5453  *
5454  * nChars may be set to INT_MAX to update to the end of the text.
5455  *
5456  * Returns TRUE if at least one section was modified.
5457  */
5458 static BOOL ME_UpdateLinkAttribute(ME_TextEditor *editor, ME_Cursor *start, int nChars)
5459 {
5460   BOOL modified = FALSE;
5461   ME_Cursor startCur = *start;
5462 
5463   if (!editor->AutoURLDetect_bEnable) return FALSE;
5464 
5465   do
5466   {
5467     CHARFORMAT2W link;
5468     ME_Cursor candidateStart, candidateEnd;
5469 
5470     if (ME_FindNextURLCandidate(editor, &startCur, nChars,
5471                                 &candidateStart, &candidateEnd))
5472     {
5473       /* Section before candidate is not an URL */
5474       int cMin = ME_GetCursorOfs(&candidateStart);
5475       int cMax = ME_GetCursorOfs(&candidateEnd);
5476 
5477       if (!ME_IsCandidateAnURL(editor, &candidateStart, cMax - cMin))
5478         candidateStart = candidateEnd;
5479       nChars -= cMax - ME_GetCursorOfs(&startCur);
5480     }
5481     else
5482     {
5483       /* No more candidates until end of selection */
5484       nChars = 0;
5485     }
5486 
5487     if (startCur.pRun != candidateStart.pRun ||
5488         startCur.nOffset != candidateStart.nOffset)
5489     {
5490       /* CFE_LINK effect should be consistently unset */
5491       link.cbSize = sizeof(link);
5492       ME_GetCharFormat(editor, &startCur, &candidateStart, &link);
5493       if (!(link.dwMask & CFM_LINK) || (link.dwEffects & CFE_LINK))
5494       {
5495         /* CFE_LINK must be unset from this range */
5496         memset(&link, 0, sizeof(CHARFORMAT2W));
5497         link.cbSize = sizeof(link);
5498         link.dwMask = CFM_LINK;
5499         link.dwEffects = 0;
5500         ME_SetCharFormat(editor, &startCur, &candidateStart, &link);
5501         /* Update candidateEnd since setting character formats may split
5502          * runs, which can cause a cursor to be at an invalid offset within
5503          * a split run. */
5504         while (candidateEnd.nOffset >= candidateEnd.pRun->member.run.len)
5505         {
5506           candidateEnd.nOffset -= candidateEnd.pRun->member.run.len;
5507           candidateEnd.pRun = ME_FindItemFwd(candidateEnd.pRun, diRun);
5508         }
5509         modified = TRUE;
5510       }
5511     }
5512     if (candidateStart.pRun != candidateEnd.pRun ||
5513         candidateStart.nOffset != candidateEnd.nOffset)
5514     {
5515       /* CFE_LINK effect should be consistently set */
5516       link.cbSize = sizeof(link);
5517       ME_GetCharFormat(editor, &candidateStart, &candidateEnd, &link);
5518       if (!(link.dwMask & CFM_LINK) || !(link.dwEffects & CFE_LINK))
5519       {
5520         /* CFE_LINK must be set on this range */
5521         memset(&link, 0, sizeof(CHARFORMAT2W));
5522         link.cbSize = sizeof(link);
5523         link.dwMask = CFM_LINK;
5524         link.dwEffects = CFE_LINK;
5525         ME_SetCharFormat(editor, &candidateStart, &candidateEnd, &link);
5526         modified = TRUE;
5527       }
5528     }
5529     startCur = candidateEnd;
5530   } while (nChars > 0);
5531   return modified;
5532 }
5533