riched20: Extend EM_FINDTEXT conformance tests and fix 2 problems they expose.
[wine] / dlls / riched20 / editor.c
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  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  */
22
23 /* 
24   API implementation status:
25   
26   Messages (ANSI versions not done yet)
27   - EM_AUTOURLDETECT 2.0
28   + EM_CANPASTE
29   + EM_CANREDO 2.0
30   + EM_CANUNDO
31   + EM_CHARFROMPOS
32   - EM_DISPLAYBAND
33   + EM_EMPTYUNDOBUFFER
34   + EM_EXGETSEL
35   - EM_EXLIMITTEXT
36   + EM_EXLINEFROMCHAR
37   + EM_EXSETSEL
38   + EM_FINDTEXT (only FR_DOWN flag implemented)
39   + EM_FINDTEXTEX (only FR_DOWN flag implemented)
40   - EM_FINDWORDBREAK
41   - EM_FMTLINES
42   - EM_FORMATRANGE
43   - EM_GETAUTOURLDETECT 2.0
44   - EM_GETBIDIOPTIONS 3.0
45   - EM_GETCHARFORMAT (partly done)
46   - EM_GETEDITSTYLE
47   + EM_GETEVENTMASK
48   + EM_GETFIRSTVISIBLELINE (can be optimized if needed)
49   - EM_GETIMECOLOR 1.0asian
50   - EM_GETIMECOMPMODE 2.0
51   - EM_GETIMEOPTIONS 1.0asian
52   - EM_GETIMESTATUS
53   - EM_GETLANGOPTIONS 2.0
54   - EM_GETLIMITTEXT
55   - EM_GETLINE        
56   + EM_GETLINECOUNT   returns number of rows, not of paragraphs
57   + EM_GETMODIFY
58   - EM_GETOLEINTERFACE
59   - EM_GETOPTIONS
60   + EM_GETPARAFORMAT
61   - EM_GETPASSWORDCHAR 2.0
62   - EM_GETPUNCTUATION 1.0asian
63   + EM_GETRECT
64   - EM_GETREDONAME 2.0
65   + EM_GETSEL
66   + EM_GETSELTEXT (ANSI&Unicode)
67   - EM_GETSCROLLPOS 3.0
68 ! - EM_GETTHUMB
69   - EM_GETTEXTEX 2.0
70   + EM_GETTEXTLENGTHEX (GTL_PRECISE unimplemented)
71   - EM_GETTEXTMODE 2.0
72 ? + EM_GETTEXTRANGE (ANSI&Unicode)
73   - EM_GETTYPOGRAPHYOPTIONS 3.0
74   - EM_GETUNDONAME
75   + EM_GETWORDBREAKPROC
76   - EM_GETWORDBREAKPROCEX
77   - EM_GETWORDWRAPMODE 1.0asian
78   + EM_GETZOOM 3.0
79   - EM_HIDESELECTION
80   - EM_LIMITTEXT
81   + EM_LINEFROMCHAR
82   + EM_LINEINDEX
83   + EM_LINELENGTH
84   + EM_LINESCROLL
85   - EM_PASTESPECIAL
86   + EM_POSFROMCHAR
87   + EM_REDO 2.0
88   + EM_REQUESTRESIZE
89   + EM_REPLACESEL (proper style?) ANSI&Unicode
90   - EM_SCROLL
91   - EM_SCROLLCARET
92   - EM_SELECTIONTYPE
93   - EM_SETBIDIOPTIONS 3.0
94   + EM_SETBKGNDCOLOR
95   - EM_SETCHARFORMAT (partly done, no ANSI)
96   - EM_SETEDITSTYLE
97   + EM_SETEVENTMASK (few notifications supported)
98   - EM_SETFONTSIZE
99   - EM_SETIMECOLOR 1.0asian
100   - EM_SETIMEOPTIONS 1.0asian
101   - EM_SETLANGOPTIONS 2.0
102   - EM_SETLIMITTEXT
103   + EM_SETMODIFY (not sure if implementation is correct)
104   - EM_SETOLECALLBACK
105   - EM_SETOPTIONS
106   - EM_SETPALETTE 2.0
107   + EM_SETPARAFORMAT
108   - EM_SETPASSWORDCHAR 2.0
109   - EM_SETPUNCTUATION 1.0asian
110   + EM_SETREADONLY no beep on modification attempt
111   + EM_SETRECT
112   + EM_SETRECTNP (EM_SETRECT without repainting)
113   + EM_SETSEL
114   - EM_SETSCROLLPOS 3.0
115   - EM_SETTABSTOPS 3.0
116   - EM_SETTARGETDEVICE
117   + EM_SETTEXTEX 3.0 (unicode only, no rich text insertion handling, proper style?)
118   - EM_SETTEXTMODE 2.0
119   - EM_SETTYPOGRAPHYOPTIONS 3.0
120   - EM_SETUNDOLIMIT 2.0
121   + EM_SETWORDBREAKPROC (used only for word movement at the moment)
122   - EM_SETWORDBREAKPROCEX
123   - EM_SETWORDWRAPMODE 1.0asian
124   + EM_SETZOOM 3.0
125   - EM_SHOWSCROLLBAR 2.0
126   - EM_STOPGROUPTYPING 2.0
127   + EM_STREAMIN
128   + EM_STREAMOUT
129   + EM_UNDO
130   + WM_CHAR
131   + WM_CLEAR
132   + WM_COPY
133   + WM_CUT
134   + WM_GETDLGCODE (the current implementation is incomplete)
135   + WM_GETTEXT (ANSI&Unicode)
136   + WM_GETTEXTLENGTH (ANSI version sucks)
137   + WM_PASTE
138   - WM_SETFONT
139   + WM_SETTEXT (resets undo stack !) (proper style?) ANSI&Unicode
140   - WM_STYLECHANGING
141   - WM_STYLECHANGED (things like read-only flag)
142   - WM_UNICHAR
143   
144   Notifications
145   
146   * EN_CHANGE (sent from the wrong place)
147   - EN_CORRECTTEXT
148   - EN_DROPFILES
149   - EN_ERRSPACE
150   - EN_HSCROLL
151   - EN_IMECHANGE
152   + EN_KILLFOCUS
153   - EN_LINK
154   - EN_MAXTEXT
155   - EN_MSGFILTER
156   - EN_OLEOPFAILED
157   - EN_PROTECTED
158   + EN_REQUESTRESIZE
159   - EN_SAVECLIPBOARD
160   + EN_SELCHANGE 
161   + EN_SETFOCUS
162   - EN_STOPNOUNDO
163   * EN_UPDATE (sent from the wrong place)
164   - EN_VSCROLL
165   
166   Styles
167   
168   - ES_AUTOHSCROLL
169   - ES_AUTOVSCROLL
170   - ES_CENTER
171   - ES_DISABLENOSCROLL (scrollbar is always visible)
172   - ES_EX_NOCALLOLEINIT
173   - ES_LEFT
174   - ES_MULTILINE (currently single line controls aren't supported)
175   - ES_NOIME
176   - ES_READONLY (I'm not sure if beeping is the proper behaviour)
177   - ES_RIGHT
178   - ES_SAVESEL
179   - ES_SELFIME
180   - ES_SUNKEN
181   - ES_VERTICAL
182   - ES_WANTRETURN (don't know how to do WM_GETDLGCODE part)
183   - WS_SETFONT
184   - WS_HSCROLL
185   - WS_VSCROLL
186 */
187
188 /*
189  * RICHED20 TODO (incomplete):
190  *
191  * - messages/styles/notifications listed above 
192  * - Undo coalescing 
193  * - add remaining CHARFORMAT/PARAFORMAT fields
194  * - right/center align should strip spaces from the beginning
195  * - pictures/OLE objects (not just smiling faces that lack API support ;-) )
196  * - COM interface (looks like a major pain in the TODO list)
197  * - calculate heights of pictures (half-done)
198  * - horizontal scrolling (not even started)
199  * - hysteresis during wrapping (related to scrollbars appearing/disappearing)
200  * - find/replace
201  * - how to implement EM_FORMATRANGE and EM_DISPLAYBAND ? (Mission Impossible)
202  * - italic caret with italic fonts
203  * - IME
204  * - most notifications aren't sent at all (the most important ones are)
205  * - when should EN_SELCHANGE be sent after text change ? (before/after EN_UPDATE?)
206  * - WM_SETTEXT may use wrong style (but I'm 80% sure it's OK)
207  * - EM_GETCHARFORMAT with SCF_SELECTION may not behave 100% like in original (but very close)
208  * - full justification
209  * - hyphenation
210  * - tables
211  * - ListBox & ComboBox not implemented
212  *
213  * Bugs that are probably fixed, but not so easy to verify:
214  * - EN_UPDATE/EN_CHANGE are handled very incorrectly (should be OK now)
215  * - undo for ME_JoinParagraphs doesn't store paragraph format ? (it does)
216  * - check/fix artificial EOL logic (bCursorAtEnd, hardly logical)
217  * - caret shouldn't be displayed when selection isn't empty
218  * - check refcounting in style management functions (looks perfect now, but no bugs is suspicious)
219  * - undo for setting default format (done, might be buggy)
220  * - styles might be not released properly (looks like they work like charm, but who knows?
221  *
222  */
223
224 #include "editor.h"
225 #include "commdlg.h"
226 #include "winreg.h"
227 #define NO_SHLWAPI_STREAM 
228 #include "shlwapi.h"
229 #include "imm.h"
230 #include "textserv.h"
231 #include "rtf.h"
232  
233 WINE_DEFAULT_DEBUG_CHANNEL(richedit);
234
235 int me_debug = 0;
236 HANDLE me_heap = NULL;
237
238 static BOOL ME_ListBoxRegistered = FALSE;
239 static BOOL ME_ComboBoxRegistered = FALSE;
240
241 static ME_TextBuffer *ME_MakeText(void) {
242   
243   ME_TextBuffer *buf = ALLOC_OBJ(ME_TextBuffer);
244
245   ME_DisplayItem *p1 = ME_MakeDI(diTextStart);
246   ME_DisplayItem *p2 = ME_MakeDI(diTextEnd);
247   
248   p1->prev = NULL;
249   p1->next = p2;
250   p2->prev = p1;
251   p2->next = NULL;
252   p1->member.para.next_para = p2;
253   p2->member.para.prev_para = p1;
254   p2->member.para.nCharOfs = 0;  
255   
256   buf->pFirst = p1;
257   buf->pLast = p2;
258   buf->pCharStyle = NULL;
259   
260   return buf;
261 }
262
263
264 static LRESULT ME_StreamInText(ME_TextEditor *editor, DWORD dwFormat, ME_InStream *stream, ME_Style *style)
265 {
266   WCHAR wszText[STREAMIN_BUFFER_SIZE+1];
267   WCHAR *pText;
268   
269   TRACE("%08lx %p\n", dwFormat, stream);
270   
271   do {
272     long nWideChars = 0;
273
274     if (!stream->dwSize)
275     {
276       ME_StreamInFill(stream);
277       if (stream->editstream->dwError)
278         break;
279       if (!stream->dwSize)
280         break;
281     }
282       
283     if (!(dwFormat & SF_UNICODE))
284     {
285       /* FIXME? this is doomed to fail on true MBCS like UTF-8, luckily they're unlikely to be used as CP_ACP */
286       nWideChars = MultiByteToWideChar(CP_ACP, 0, stream->buffer, stream->dwSize, wszText, STREAMIN_BUFFER_SIZE);
287       pText = wszText;
288     }
289     else
290     {
291       nWideChars = stream->dwSize >> 1;
292       pText = (WCHAR *)stream->buffer;
293     }
294     
295     ME_InsertTextFromCursor(editor, 0, pText, nWideChars, style);
296     if (stream->dwSize == 0)
297       break;
298     stream->dwSize = 0;
299   } while(1);
300   ME_CommitUndo(editor);
301   ME_UpdateRepaint(editor);
302   return 0;
303 }
304
305 static void ME_RTFCharAttrHook(RTF_Info *info)
306 {
307   CHARFORMAT2W fmt;
308   fmt.cbSize = sizeof(fmt);
309   fmt.dwMask = 0;
310   
311   switch(info->rtfMinor)
312   {
313     case rtfPlain:
314       /* FIXME add more flags once they're implemented */
315       fmt.dwMask = CFM_BOLD | CFM_ITALIC | CFM_UNDERLINE | CFM_STRIKEOUT | CFM_COLOR | CFM_BACKCOLOR | CFM_SIZE | CFM_WEIGHT;
316       fmt.dwEffects = CFE_AUTOCOLOR | CFE_AUTOBACKCOLOR;
317       fmt.yHeight = 12*20; /* 12pt */
318       fmt.wWeight = 400;
319       break;
320     case rtfBold:
321       fmt.dwMask = CFM_BOLD;
322       fmt.dwEffects = info->rtfParam ? fmt.dwMask : 0;
323       break;
324     case rtfItalic:
325       fmt.dwMask = CFM_ITALIC;
326       fmt.dwEffects = info->rtfParam ? fmt.dwMask : 0;
327       break;
328     case rtfUnderline:
329       fmt.dwMask = CFM_UNDERLINE;
330       fmt.dwEffects = info->rtfParam ? fmt.dwMask : 0;
331       fmt.bUnderlineType = CFU_CF1UNDERLINE;
332       break;
333     case rtfNoUnderline:
334       fmt.dwMask = CFM_UNDERLINE;
335       fmt.dwEffects = 0;
336       break;
337     case rtfStrikeThru:
338       fmt.dwMask = CFM_STRIKEOUT;
339       fmt.dwEffects = info->rtfParam ? fmt.dwMask : 0;
340       break;
341     case rtfSubScript:
342     case rtfSuperScript:
343     case rtfSubScrShrink:
344     case rtfSuperScrShrink:
345     case rtfNoSuperSub:
346       fmt.dwMask = CFM_SUBSCRIPT|CFM_SUPERSCRIPT;
347       if (info->rtfMinor == rtfSubScrShrink) fmt.dwEffects = CFE_SUBSCRIPT;
348       if (info->rtfMinor == rtfSuperScrShrink) fmt.dwEffects = CFE_SUPERSCRIPT;
349       if (info->rtfMinor == rtfNoSuperSub) fmt.dwEffects = 0;
350       break;
351     case rtfInvisible:
352       fmt.dwMask = CFM_HIDDEN;
353       fmt.dwEffects = info->rtfParam ? fmt.dwMask : 0;
354       break;
355     case rtfBackColor:
356       fmt.dwMask = CFM_BACKCOLOR;
357       fmt.dwEffects = 0;
358       if (info->rtfParam == 0)
359         fmt.dwEffects = CFE_AUTOBACKCOLOR;
360       else if (info->rtfParam != rtfNoParam)
361       {
362         RTFColor *c = RTFGetColor(info, info->rtfParam);
363         fmt.crTextColor = (c->rtfCBlue<<16)|(c->rtfCGreen<<8)|(c->rtfCRed);
364       }
365       break;
366     case rtfForeColor:
367       fmt.dwMask = CFM_COLOR;
368       fmt.dwEffects = 0;
369       if (info->rtfParam == 0)
370         fmt.dwEffects = CFE_AUTOCOLOR;
371       else if (info->rtfParam != rtfNoParam)
372       {
373         RTFColor *c = RTFGetColor(info, info->rtfParam);
374         fmt.crTextColor = (c->rtfCBlue<<16)|(c->rtfCGreen<<8)|(c->rtfCRed);
375       }
376       break;
377     case rtfFontNum:
378       if (info->rtfParam != rtfNoParam)
379       {
380         RTFFont *f = RTFGetFont(info, info->rtfParam);
381         if (f)
382         {
383           MultiByteToWideChar(CP_ACP, 0, f->rtfFName, -1, fmt.szFaceName, sizeof(fmt.szFaceName)/sizeof(WCHAR));
384           fmt.szFaceName[sizeof(fmt.szFaceName)/sizeof(WCHAR)-1] = '\0';
385           fmt.bCharSet = f->rtfFCharSet;
386           fmt.dwMask = CFM_FACE | CFM_CHARSET;
387         }
388       }
389       break;
390     case rtfFontSize:
391       fmt.dwMask = CFM_SIZE;
392       if (info->rtfParam != rtfNoParam)
393         fmt.yHeight = info->rtfParam*10;
394       break;
395   }
396   if (fmt.dwMask) {
397     ME_Style *style2;
398     RTFFlushOutputBuffer(info);
399     /* FIXME too slow ? how come ? */
400     style2 = ME_ApplyStyle(info->style, &fmt);
401     ME_ReleaseStyle(info->style);
402     info->style = style2;
403     info->styleChanged = TRUE;
404   }
405 }
406
407 /* FIXME this function doesn't get any information about context of the RTF tag, which is very bad,
408    the same tags mean different things in different contexts */
409 static void ME_RTFParAttrHook(RTF_Info *info)
410 {
411   PARAFORMAT2 fmt;
412   fmt.cbSize = sizeof(fmt);
413   fmt.dwMask = 0;
414   
415   switch(info->rtfMinor)
416   {
417   case rtfParDef: /* restores default paragraph attributes */
418     fmt.dwMask = PFM_ALIGNMENT | PFM_TABSTOPS | PFM_OFFSET | PFM_STARTINDENT;
419     fmt.wAlignment = PFA_LEFT;
420     fmt.cTabCount = 0;
421     fmt.dxOffset = fmt.dxStartIndent = 0;
422     RTFFlushOutputBuffer(info);
423     ME_GetParagraph(info->editor->pCursors[0].pRun)->member.para.bTable = FALSE;
424     break;
425   case rtfInTable:
426   {
427     ME_DisplayItem *para;
428     
429     RTFFlushOutputBuffer(info);
430     para = ME_GetParagraph(info->editor->pCursors[0].pRun);
431     assert(para->member.para.pCells);
432     para->member.para.bTable = TRUE;
433     return;
434   }
435   case rtfFirstIndent:
436     ME_GetSelectionParaFormat(info->editor, &fmt);
437     fmt.dwMask = PFM_STARTINDENT | PFM_OFFSET;
438     fmt.dxStartIndent += info->rtfParam + fmt.dxOffset;
439     fmt.dxOffset = -info->rtfParam;
440     break;
441   case rtfLeftIndent:
442     ME_GetSelectionParaFormat(info->editor, &fmt);
443     fmt.dwMask = PFM_STARTINDENT;
444     fmt.dxStartIndent = -fmt.dxOffset + info->rtfParam;
445     break;
446   case rtfRightIndent:
447     fmt.dwMask = PFM_RIGHTINDENT;
448     fmt.dxRightIndent = info->rtfParam;
449     break;
450   case rtfQuadLeft:
451   case rtfQuadJust:
452     fmt.dwMask = PFM_ALIGNMENT;
453     fmt.wAlignment = PFA_LEFT;
454     break;
455   case rtfQuadRight:
456     fmt.dwMask = PFM_ALIGNMENT;
457     fmt.wAlignment = PFA_RIGHT;
458     break;
459   case rtfQuadCenter:
460     fmt.dwMask = PFM_ALIGNMENT;
461     fmt.wAlignment = PFA_CENTER;
462     break;
463   case rtfTabPos:
464     ME_GetSelectionParaFormat(info->editor, &fmt);
465     if (!(fmt.dwMask & PFM_TABSTOPS))
466     {
467       fmt.dwMask |= PFM_TABSTOPS;
468       fmt.cTabCount = 0;
469     }
470     if (fmt.cTabCount < MAX_TAB_STOPS)
471       fmt.rgxTabs[fmt.cTabCount++] = info->rtfParam;
472     break;
473   }  
474   if (fmt.dwMask) {
475     RTFFlushOutputBuffer(info);
476     /* FIXME too slow ? how come ?*/
477     ME_SetSelectionParaFormat(info->editor, &fmt);
478   }
479 }
480
481 static void ME_RTFTblAttrHook(RTF_Info *info)
482 {
483   ME_DisplayItem *para;
484   
485   switch (info->rtfMinor)
486   {
487     case rtfRowDef:
488       RTFFlushOutputBuffer(info);
489       para = ME_GetParagraph(info->editor->pCursors[0].pRun);
490       
491       para->member.para.pCells = ALLOC_OBJ(ME_TableCell);
492       para->member.para.pCells->nRightBoundary = 0;
493       para->member.para.pCells->next = NULL;
494       para->member.para.pLastCell = para->member.para.pCells;
495       break;
496     case rtfCellPos:
497       RTFFlushOutputBuffer(info);
498       para = ME_GetParagraph(info->editor->pCursors[0].pRun);
499       
500       if (para->member.para.pLastCell->nRightBoundary)
501       {
502         ME_TableCell *pCell = ALLOC_OBJ(ME_TableCell);
503         
504         pCell->next = NULL;
505         para->member.para.pLastCell->next = pCell;
506         para->member.para.pLastCell = pCell;
507       }
508       para->member.para.pLastCell->nRightBoundary = info->rtfParam;
509       break;
510   }
511 }
512
513 static void ME_RTFReadHook(RTF_Info *info) {
514   switch(info->rtfClass)
515   {
516     case rtfGroup:
517       switch(info->rtfMajor)
518       {
519         case rtfBeginGroup:
520           if (info->stackTop < maxStack) {
521             memcpy(&info->stack[info->stackTop].fmt, &info->style->fmt, sizeof(CHARFORMAT2W));
522             info->stack[info->stackTop].codePage = info->codePage;
523             info->stack[info->stackTop].unicodeLength = info->unicodeLength;
524           }
525           info->stackTop++;
526           info->styleChanged = FALSE;
527           break;
528         case rtfEndGroup:
529         {
530           ME_Style *s;
531           RTFFlushOutputBuffer(info);
532           if (info->stackTop<=1) {
533             info->rtfClass = rtfEOF;
534             return;
535           }
536           info->stackTop--;
537           assert(info->stackTop >= 0);
538           if (info->styleChanged)
539           {
540             /* FIXME too slow ? how come ? */
541             s = ME_ApplyStyle(info->style, &info->stack[info->stackTop].fmt);
542             ME_ReleaseStyle(info->style);
543             info->style = s;
544             info->codePage = info->stack[info->stackTop].codePage;
545             info->unicodeLength = info->stack[info->stackTop].unicodeLength;
546           }
547           break;
548         }
549       }
550       break;
551     case rtfControl:
552       switch(info->rtfMajor)
553       {
554         case rtfCharAttr:
555           ME_RTFCharAttrHook(info);
556           break;
557         case rtfParAttr:
558           ME_RTFParAttrHook(info);
559           break;
560         case rtfTblAttr:
561           ME_RTFTblAttrHook(info);
562           break;
563         case rtfSpecialChar:
564           if (info->rtfMinor == rtfCell)
565           {
566             RTFFlushOutputBuffer(info);
567             ME_InsertTableCellFromCursor(info->editor, 0);
568           }
569       }
570       break;
571   }
572 }
573
574 void
575 ME_StreamInFill(ME_InStream *stream)
576 {
577   stream->editstream->dwError = stream->editstream->pfnCallback(stream->editstream->dwCookie,
578                                                                 (BYTE *)stream->buffer,
579                                                                 sizeof(stream->buffer),
580                                                                 (LONG *)&stream->dwSize);
581   stream->dwUsed = 0;
582 }
583
584 static LRESULT ME_StreamIn(ME_TextEditor *editor, DWORD format, EDITSTREAM *stream)
585 {
586   RTF_Info parser;
587   ME_Style *style;
588   int from, to, to2, nUndoMode;
589   ME_UndoItem *pUI;
590   int nEventMask = editor->nEventMask;
591   ME_InStream inStream;
592
593   TRACE("stream==%p hWnd==%p format==0x%X\n", stream, editor->hWnd, (UINT)format);
594   editor->nEventMask = 0;
595   
596   ME_GetSelection(editor, &from, &to);
597   if (format & SFF_SELECTION) {
598     style = ME_GetSelectionInsertStyle(editor);
599
600     ME_InternalDeleteText(editor, from, to-from);
601   }
602   else {
603     style = editor->pBuffer->pDefaultStyle;
604     ME_AddRefStyle(style);
605     SendMessageA(editor->hWnd, EM_SETSEL, 0, 0);    
606     ME_InternalDeleteText(editor, 0, ME_GetTextLength(editor));
607     from = to = 0;
608     ME_ClearTempStyle(editor);
609     /* FIXME restore default paragraph formatting ! */
610   }
611   
612   nUndoMode = editor->nUndoMode;
613   editor->nUndoMode = umIgnore;
614
615   inStream.editstream = stream;
616   inStream.editstream->dwError = 0;
617   inStream.dwSize = 0;
618   inStream.dwUsed = 0;
619
620   if (format & SF_RTF)
621   {
622     /* Check if it's really RTF, and if it is not, use plain text */
623     ME_StreamInFill(&inStream);
624     if (!inStream.editstream->dwError)
625     {
626       if (strncmp(inStream.buffer, "{\\rtf1", 6) && strncmp(inStream.buffer, "{\\urtf", 6))
627       {
628         format &= ~SF_RTF;
629         format |= SF_TEXT;
630       }
631     }
632   }
633
634   if (!inStream.editstream->dwError)
635   {
636     if (format & SF_RTF) {
637       /* setup the RTF parser */
638       memset(&parser, 0, sizeof parser);
639       RTFSetEditStream(&parser, &inStream);
640       parser.rtfFormat = format&(SF_TEXT|SF_RTF);
641       parser.hwndEdit = editor->hWnd;
642       parser.editor = editor;
643       parser.style = style;
644       WriterInit(&parser);
645       RTFInit(&parser);
646       RTFSetReadHook(&parser, ME_RTFReadHook);
647       BeginFile(&parser);
648   
649       /* do the parsing */
650       RTFRead(&parser);
651       RTFFlushOutputBuffer(&parser);
652       RTFDestroy(&parser);
653
654       style = parser.style;
655     }
656     else if (format & SF_TEXT)
657       ME_StreamInText(editor, format, &inStream, style);
658     else
659       ERR("EM_STREAMIN without SF_TEXT or SF_RTF\n");
660     ME_GetSelection(editor, &to, &to2);
661     /* put the cursor at the top */
662     if (!(format & SFF_SELECTION))
663       SendMessageA(editor->hWnd, EM_SETSEL, 0, 0);
664   }
665   
666   editor->nUndoMode = nUndoMode;
667   if (format & SFF_SELECTION)
668   {
669     pUI = ME_AddUndoItem(editor, diUndoDeleteRun, NULL);
670     TRACE("from %d to %d\n", from, to);
671     if (pUI && from < to)
672     {
673       pUI->nStart = from;
674       pUI->nLen = to-from;
675     }
676     ME_CommitUndo(editor);
677   }
678   else
679   {
680     ME_EmptyUndoStack(editor);
681   }
682
683   ME_ReleaseStyle(style); 
684   editor->nEventMask = nEventMask;
685   if (editor->bRedraw)
686   {
687     ME_UpdateRepaint(editor);
688   }
689   if (!(format & SFF_SELECTION)) {
690     ME_ClearTempStyle(editor);
691   }
692   ME_MoveCaret(editor);
693   ME_SendSelChange(editor);
694   ME_SendRequestResize(editor, FALSE);
695
696   return 0;
697 }
698
699
700 typedef struct tagME_RTFStringStreamStruct
701 {
702   char *string;
703   int pos;
704   int length;
705 } ME_RTFStringStreamStruct;
706
707 static DWORD CALLBACK ME_ReadFromRTFString(DWORD_PTR dwCookie, LPBYTE lpBuff, LONG cb, LONG *pcb)
708 {
709   ME_RTFStringStreamStruct *pStruct = (ME_RTFStringStreamStruct *)dwCookie;
710   int count;
711
712   count = min(cb, pStruct->length - pStruct->pos);
713   memmove(lpBuff, pStruct->string + pStruct->pos, count);
714   pStruct->pos += count;
715   *pcb = count;
716   return 0;
717 }
718
719 static void
720 ME_StreamInRTFString(ME_TextEditor *editor, BOOL selection, char *string)
721 {
722   EDITSTREAM es;
723   ME_RTFStringStreamStruct data;
724
725   data.string = string;
726   data.length = strlen(string);
727   data.pos = 0;
728   es.dwCookie = (DWORD)&data;
729   es.pfnCallback = ME_ReadFromRTFString;
730   ME_StreamIn(editor, SF_RTF | (selection ? SFF_SELECTION : 0), &es);
731 }
732
733
734 ME_DisplayItem *
735 ME_FindItemAtOffset(ME_TextEditor *editor, ME_DIType nItemType, int nOffset, int *nItemOffset)
736 {
737   ME_DisplayItem *item = ME_FindItemFwd(editor->pBuffer->pFirst, diParagraph);
738   
739   while (item && item->member.para.next_para->member.para.nCharOfs <= nOffset)
740     item = ME_FindItemFwd(item, diParagraph);
741
742   if (!item)
743     return item;
744
745   nOffset -= item->member.para.nCharOfs;
746   if (nItemType == diParagraph) {
747     if (nItemOffset)
748       *nItemOffset = nOffset;
749     return item;
750   }
751   
752   do {
753     item = ME_FindItemFwd(item, diRun);
754   } while (item && (item->member.run.nCharOfs + ME_StrLen(item->member.run.strText) <= nOffset));
755   if (item) {
756     nOffset -= item->member.run.nCharOfs;
757     if (nItemOffset)
758       *nItemOffset = nOffset;
759   }
760   return item;
761 }
762
763
764 static int
765 ME_FindText(ME_TextEditor *editor, DWORD flags, CHARRANGE *chrg, WCHAR *text, CHARRANGE *chrgText)
766 {
767   int nStart, nEnd;
768   int nLen = lstrlenW(text);
769   int nMin, nMax;
770   ME_DisplayItem *item;
771   ME_DisplayItem *para;
772
773   TRACE("flags==0x%08lx, chrg->cpMin==%ld, chrg->cpMax==%ld text==%s\n",
774         flags, chrg->cpMin, chrg->cpMax, debugstr_w(text));
775   
776   if (!(flags & FR_MATCHCASE))
777     FIXME("Case-insensitive search not implemented\n");
778   if (flags & ~(FR_DOWN | FR_MATCHCASE))
779     FIXME("Flags 0x%08lx not implemented\n", flags & ~(FR_DOWN | FR_MATCHCASE));
780
781   if (chrg->cpMax == -1)
782   {
783     nMin = chrg->cpMin;
784     nMax = ME_GetTextLength(editor);
785   }
786   else
787   {
788     nMin = min(chrg->cpMin, chrg->cpMax);
789     nMax = max(chrg->cpMin, chrg->cpMax);
790   }
791   
792   if (!nLen || nMin < 0 || nMax < 0)
793   {
794     if (chrgText)
795       chrgText->cpMin = chrgText->cpMax = -1;
796     return -1;
797   }
798  
799   if (flags & FR_DOWN) /* Forward search */
800   {
801     nStart = nMin;
802     item = ME_FindItemAtOffset(editor, diRun, nStart, &nStart);
803     if (!item)
804     {
805       if (chrgText)
806         chrgText->cpMin = chrgText->cpMax = -1;
807       return -1;
808     }
809
810     para = ME_GetParagraph(item);
811     while (item
812            && para->member.para.nCharOfs + item->member.run.nCharOfs + nStart + nLen <= nMax)
813     {
814       ME_DisplayItem *pCurItem = item;
815       int nCurStart = nStart;
816       int nMatched = 0;
817     
818       while (pCurItem && pCurItem->member.run.strText->szData[nCurStart + nMatched] == text[nMatched])
819       {
820         nMatched++;
821         if (nMatched == nLen)
822         {
823           nStart += para->member.para.nCharOfs + item->member.run.nCharOfs;
824           if (chrgText)
825           {
826             chrgText->cpMin = nStart;
827             chrgText->cpMax = nStart + nLen;
828           }
829           TRACE("found at %d-%d\n", nStart, nStart + nLen);
830           return nStart;
831         }
832         if (nCurStart + nMatched == ME_StrLen(pCurItem->member.run.strText))
833         {
834           pCurItem = ME_FindItemFwd(pCurItem, diRun);
835           para = ME_GetParagraph(pCurItem);
836           nCurStart = -nMatched;
837         }
838       }
839       nStart++;
840       if (nStart == ME_StrLen(item->member.run.strText))
841       {
842         item = ME_FindItemFwd(item, diRun);
843         para = ME_GetParagraph(item);
844         nStart = 0;
845       }
846     }
847   }
848   else /* Backward search */
849   {
850     nEnd = nMax;
851     item = ME_FindItemAtOffset(editor, diRun, nEnd, &nEnd);
852     if (!item) {
853       if (chrgText)
854         chrgText->cpMin = chrgText->cpMax = -1;
855       return -1;
856     }
857     
858     para = ME_GetParagraph(item);
859     
860     while (item
861            && para->member.para.nCharOfs + item->member.run.nCharOfs + nEnd - nLen >= nMin)
862     {
863       ME_DisplayItem *pCurItem = item;
864       int nCurEnd = nEnd;
865       int nMatched = 0;
866       
867       while (pCurItem && pCurItem->member.run.strText->szData[nCurEnd - nMatched - 1] == text[nLen - nMatched - 1])
868       {
869         nMatched++;
870         if (nMatched == nLen)
871         {
872           nStart = para->member.para.nCharOfs + item->member.run.nCharOfs + nCurEnd - nMatched;
873           if (chrgText)
874           {
875             chrgText->cpMin = nStart;
876             chrgText->cpMax = nStart + nLen;
877           }
878           TRACE("found at %d-%d\n", nStart, nStart + nLen);
879           return nStart;
880         }
881         if (nCurEnd - nMatched == 0)
882         {
883           pCurItem = ME_FindItemBack(pCurItem, diRun);
884           para = ME_GetParagraph(pCurItem);
885           nCurEnd = ME_StrLen(pCurItem->member.run.strText) + nMatched;
886         }
887       }
888       nEnd--;
889       if (nEnd < 0)
890       {
891         item = ME_FindItemBack(item, diRun);
892         para = ME_GetParagraph(item);
893         nEnd = ME_StrLen(item->member.run.strText);
894       }
895     }
896   }
897   TRACE("not found\n");
898   if (chrgText)
899     chrgText->cpMin = chrgText->cpMax = -1;
900   return -1;
901 }
902
903
904 static BOOL
905 ME_KeyDown(ME_TextEditor *editor, WORD nKey)
906 {
907   BOOL ctrl_is_down = GetKeyState(VK_CONTROL) & 0x8000;
908   BOOL shift_is_down = GetKeyState(VK_SHIFT) & 0x8000;
909   
910   switch (nKey)
911   {
912     case VK_LEFT:
913     case VK_RIGHT:
914     case VK_UP:
915     case VK_DOWN:
916     case VK_HOME:
917     case VK_END:
918     case VK_PRIOR:
919     case VK_NEXT:
920       ME_ArrowKey(editor, nKey, shift_is_down, ctrl_is_down);
921       return TRUE;
922     case VK_BACK:
923     case VK_DELETE:
924       /* FIXME backspace and delete aren't the same, they act different wrt paragraph style of the merged paragraph */
925       if (GetWindowLongW(editor->hWnd, GWL_STYLE) & ES_READONLY)
926         return FALSE;
927       if (ME_IsSelection(editor))
928         ME_DeleteSelection(editor);
929       else if (nKey == VK_DELETE || ME_ArrowKey(editor, VK_LEFT, FALSE, FALSE))
930         ME_DeleteTextAtCursor(editor, 1, 1);
931       else
932         return TRUE;
933       ME_QueueInvalidateFromCursor(editor, 1);
934       ME_UpdateRepaint(editor);
935       ME_SendRequestResize(editor, FALSE);
936       return TRUE;
937
938     default:
939       if (ctrl_is_down)
940       {
941         if (nKey == 'W')
942         {
943           CHARFORMAT2W chf;
944           char buf[2048];
945           
946           ME_GetSelectionCharFormat(editor, &chf);
947           ME_DumpStyleToBuf(&chf, buf);
948           MessageBoxA(NULL, buf, "Style dump", MB_OK);
949         }
950         if (nKey == 'Q')
951         {
952           ME_CheckCharOffsets(editor);
953         }
954       }
955   }
956   return FALSE;
957 }
958
959 static void ME_ShowContextMenu(ME_TextEditor *editor, int x, int y)
960 {
961   CHARRANGE selrange;
962   HMENU menu;
963   int seltype = 0;
964   if(!editor->lpOleCallback)
965     return;
966   ME_GetSelection(editor, (int *)&selrange.cpMin, (int *)&selrange.cpMax);
967   if(selrange.cpMin == selrange.cpMax)
968     seltype |= SEL_EMPTY;
969   else
970   {
971     /* FIXME: Handle objects */
972     seltype |= SEL_TEXT;
973     if(selrange.cpMax-selrange.cpMin > 1)
974       seltype |= SEL_MULTICHAR;
975   }
976   if(SUCCEEDED(IRichEditOleCallback_GetContextMenu(editor->lpOleCallback, seltype, NULL, &selrange, &menu)))
977   {
978     TrackPopupMenu(menu, TPM_LEFTALIGN | TPM_RIGHTBUTTON, x, y, 0, GetParent(editor->hWnd), NULL);
979     DestroyMenu(menu);
980   }
981 }
982
983 ME_TextEditor *ME_MakeEditor(HWND hWnd) {
984   ME_TextEditor *ed = ALLOC_OBJ(ME_TextEditor);
985   HDC hDC;
986   int i;
987   ed->hWnd = hWnd;
988   ed->bEmulateVersion10 = FALSE;
989   ed->pBuffer = ME_MakeText();
990   hDC = GetDC(hWnd);
991   ME_MakeFirstParagraph(hDC, ed->pBuffer);
992   ReleaseDC(hWnd, hDC);
993   ed->bCaretShown = FALSE;
994   ed->nCursors = 3;
995   ed->pCursors = ALLOC_N_OBJ(ME_Cursor, ed->nCursors);
996   ed->pCursors[0].pRun = ME_FindItemFwd(ed->pBuffer->pFirst, diRun);
997   ed->pCursors[0].nOffset = 0;
998   ed->pCursors[1].pRun = ME_FindItemFwd(ed->pBuffer->pFirst, diRun);
999   ed->pCursors[1].nOffset = 0;
1000   ed->nLastTotalLength = ed->nTotalLength = 0;
1001   ed->nUDArrowX = -1;
1002   ed->nSequence = 0;
1003   ed->rgbBackColor = -1;
1004   ed->hbrBackground = GetSysColorBrush(COLOR_WINDOW);
1005   ed->bCaretAtEnd = FALSE;
1006   ed->nEventMask = 0;
1007   ed->nModifyStep = 0;
1008   ed->pUndoStack = ed->pRedoStack = NULL;
1009   ed->nUndoMode = umAddToUndo;
1010   ed->nParagraphs = 1;
1011   ed->nLastSelStart = ed->nLastSelEnd = 0;
1012   ed->nScrollPosY = 0;
1013   ed->nZoomNumerator = ed->nZoomDenominator = 0;
1014   ed->bRedraw = TRUE;
1015   ed->nInvalidOfs = -1;
1016   ed->pfnWordBreak = NULL;
1017   ed->lpOleCallback = NULL;
1018   GetClientRect(hWnd, &ed->rcFormat);
1019   for (i=0; i<HFONT_CACHE_SIZE; i++)
1020   {
1021     ed->pFontCache[i].nRefs = 0;
1022     ed->pFontCache[i].nAge = 0;
1023     ed->pFontCache[i].hFont = NULL;
1024   }
1025   ME_CheckCharOffsets(ed);
1026   return ed;
1027 }
1028
1029 typedef struct tagME_GlobalDestStruct
1030 {
1031   HGLOBAL hData;
1032   int nLength;
1033 } ME_GlobalDestStruct;
1034
1035 static DWORD CALLBACK ME_AppendToHGLOBAL(DWORD_PTR dwCookie, LPBYTE lpBuff, LONG cb, LONG *pcb)
1036 {
1037   ME_GlobalDestStruct *pData = (ME_GlobalDestStruct *)dwCookie;
1038   int nMaxSize;
1039   BYTE *pDest;
1040   
1041   nMaxSize = GlobalSize(pData->hData);
1042   if (pData->nLength+cb+1 >= cb)
1043   {
1044     /* round up to 2^17 */
1045     int nNewSize = (((nMaxSize+cb+1)|0x1FFFF)+1) & 0xFFFE0000;
1046     pData->hData = GlobalReAlloc(pData->hData, nNewSize, 0);
1047   }
1048   pDest = (BYTE *)GlobalLock(pData->hData);
1049   memcpy(pDest + pData->nLength, lpBuff, cb);
1050   pData->nLength += cb;
1051   pDest[pData->nLength] = '\0';
1052   GlobalUnlock(pData->hData);
1053   *pcb = cb;
1054   
1055   return 0;
1056 }
1057
1058 static DWORD CALLBACK ME_ReadFromHGLOBALUnicode(DWORD_PTR dwCookie, LPBYTE lpBuff, LONG cb, LONG *pcb)
1059 {
1060   ME_GlobalDestStruct *pData = (ME_GlobalDestStruct *)dwCookie;
1061   int i;
1062   WORD *pSrc, *pDest;
1063   
1064   cb = cb >> 1;
1065   pDest = (WORD *)lpBuff;
1066   pSrc = (WORD *)GlobalLock(pData->hData);
1067   for (i = 0; i<cb && pSrc[pData->nLength+i]; i++) {
1068     pDest[i] = pSrc[pData->nLength+i];
1069   }    
1070   pData->nLength += i;
1071   *pcb = 2*i;
1072   GlobalUnlock(pData->hData);
1073   return 0;
1074 }
1075
1076 static DWORD CALLBACK ME_ReadFromHGLOBALRTF(DWORD_PTR dwCookie, LPBYTE lpBuff, LONG cb, LONG *pcb)
1077 {
1078   ME_GlobalDestStruct *pData = (ME_GlobalDestStruct *)dwCookie;
1079   int i;
1080   BYTE *pSrc, *pDest;
1081   
1082   pDest = lpBuff;
1083   pSrc = (BYTE *)GlobalLock(pData->hData);
1084   for (i = 0; i<cb && pSrc[pData->nLength+i]; i++) {
1085     pDest[i] = pSrc[pData->nLength+i];
1086   }    
1087   pData->nLength += i;
1088   *pcb = i;
1089   GlobalUnlock(pData->hData);
1090   return 0;
1091 }
1092
1093
1094 void ME_DestroyEditor(ME_TextEditor *editor)
1095 {
1096   ME_DisplayItem *pFirst = editor->pBuffer->pFirst;
1097   ME_DisplayItem *p = pFirst, *pNext = NULL;
1098   int i;
1099   
1100   ME_ClearTempStyle(editor);
1101   ME_EmptyUndoStack(editor);
1102   while(p) {
1103     pNext = p->next;
1104     ME_DestroyDisplayItem(p);    
1105     p = pNext;
1106   }
1107   ME_ReleaseStyle(editor->pBuffer->pDefaultStyle);
1108   for (i=0; i<HFONT_CACHE_SIZE; i++)
1109   {
1110     if (editor->pFontCache[i].hFont)
1111       DeleteObject(editor->pFontCache[i].hFont);
1112   }
1113   DeleteObject(editor->hbrBackground);
1114   if(editor->lpOleCallback)
1115     IUnknown_Release(editor->lpOleCallback);
1116
1117   FREE_OBJ(editor);
1118 }
1119
1120 static const WCHAR wszClassName[] = {'R', 'i', 'c', 'h', 'E', 'd', 'i', 't', '2', '0', 'W', 0};
1121 static const WCHAR wszClassName50[] = {'R', 'i', 'c', 'h', 'E', 'd', 'i', 't', '5', '0', 'W', 0};
1122 static const WCHAR wszClassNameListBox[] = {'R','E','L','i','s','t','B','o','x','2','0','W', 0};
1123 static const WCHAR wszClassNameComboBox[] = {'R','E','C','o','m','b','o','B','o','x','2','0','W', 0};
1124
1125 BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
1126 {
1127     TRACE("\n");
1128     switch (fdwReason)
1129     {
1130     case DLL_PROCESS_ATTACH:
1131       DisableThreadLibraryCalls(hinstDLL);
1132       me_heap = HeapCreate (0, 0x10000, 0);
1133       ME_RegisterEditorClass(hinstDLL);
1134       break;
1135
1136     case DLL_PROCESS_DETACH:
1137       UnregisterClassW(wszClassName, 0);
1138       UnregisterClassW(wszClassName50, 0);
1139       UnregisterClassA("RichEdit20A", 0);
1140       UnregisterClassA("RichEdit50A", 0);
1141       if (ME_ListBoxRegistered)
1142           UnregisterClassW(wszClassNameListBox, 0);
1143       if (ME_ComboBoxRegistered)
1144           UnregisterClassW(wszClassNameComboBox, 0);
1145       HeapDestroy (me_heap);
1146       me_heap = NULL;
1147       break;
1148     }
1149     return TRUE;
1150 }
1151
1152
1153 #define UNSUPPORTED_MSG(e) \
1154   case e: \
1155     FIXME(#e ": stub\n"); \
1156     return DefWindowProcW(hWnd, msg, wParam, lParam);
1157
1158 static const char * const edit_messages[] = {
1159   "EM_GETSEL",
1160   "EM_SETSEL",
1161   "EM_GETRECT",
1162   "EM_SETRECT",
1163   "EM_SETRECTNP",
1164   "EM_SCROLL",
1165   "EM_LINESCROLL",
1166   "EM_SCROLLCARET",
1167   "EM_GETMODIFY",
1168   "EM_SETMODIFY",
1169   "EM_GETLINECOUNT",
1170   "EM_LINEINDEX",
1171   "EM_SETHANDLE",
1172   "EM_GETHANDLE",
1173   "EM_GETTHUMB",
1174   "EM_UNKNOWN_BF",
1175   "EM_UNKNOWN_C0",
1176   "EM_LINELENGTH",
1177   "EM_REPLACESEL",
1178   "EM_UNKNOWN_C3",
1179   "EM_GETLINE",
1180   "EM_LIMITTEXT",
1181   "EM_CANUNDO",
1182   "EM_UNDO",
1183   "EM_FMTLINES",
1184   "EM_LINEFROMCHAR",
1185   "EM_UNKNOWN_CA",
1186   "EM_SETTABSTOPS",
1187   "EM_SETPASSWORDCHAR",
1188   "EM_EMPTYUNDOBUFFER",
1189   "EM_GETFIRSTVISIBLELINE",
1190   "EM_SETREADONLY",
1191   "EM_SETWORDBREAKPROC",
1192   "EM_GETWORDBREAKPROC",
1193   "EM_GETPASSWORDCHAR",
1194   "EM_SETMARGINS",
1195   "EM_GETMARGINS",
1196   "EM_GETLIMITTEXT",
1197   "EM_POSFROMCHAR",
1198   "EM_CHARFROMPOS"
1199 };
1200
1201 static const char * const richedit_messages[] = {
1202   "EM_CANPASTE",
1203   "EM_DISPLAYBAND",
1204   "EM_EXGETSEL",
1205   "EM_EXLIMITTEXT",
1206   "EM_EXLINEFROMCHAR",
1207   "EM_EXSETSEL",
1208   "EM_FINDTEXT",
1209   "EM_FORMATRANGE",
1210   "EM_GETCHARFORMAT",
1211   "EM_GETEVENTMASK",
1212   "EM_GETOLEINTERFACE",
1213   "EM_GETPARAFORMAT",
1214   "EM_GETSELTEXT",
1215   "EM_HIDESELECTION",
1216   "EM_PASTESPECIAL",
1217   "EM_REQUESTRESIZE",
1218   "EM_SELECTIONTYPE",
1219   "EM_SETBKGNDCOLOR",
1220   "EM_SETCHARFORMAT",
1221   "EM_SETEVENTMASK",
1222   "EM_SETOLECALLBACK",
1223   "EM_SETPARAFORMAT",
1224   "EM_SETTARGETDEVICE",
1225   "EM_STREAMIN",
1226   "EM_STREAMOUT",
1227   "EM_GETTEXTRANGE",
1228   "EM_FINDWORDBREAK",
1229   "EM_SETOPTIONS",
1230   "EM_GETOPTIONS",
1231   "EM_FINDTEXTEX",
1232   "EM_GETWORDBREAKPROCEX",
1233   "EM_SETWORDBREAKPROCEX",
1234   "EM_SETUNDOLIMIT",
1235   "EM_UNKNOWN_USER_83",
1236   "EM_REDO",
1237   "EM_CANREDO",
1238   "EM_GETUNDONAME",
1239   "EM_GETREDONAME",
1240   "EM_STOPGROUPTYPING",
1241   "EM_SETTEXTMODE",
1242   "EM_GETTEXTMODE",
1243   "EM_AUTOURLDETECT",
1244   "EM_GETAUTOURLDETECT",
1245   "EM_SETPALETTE",
1246   "EM_GETTEXTEX",
1247   "EM_GETTEXTLENGTHEX",
1248   "EM_SHOWSCROLLBAR",
1249   "EM_SETTEXTEX",
1250   "EM_UNKNOWN_USER_98",
1251   "EM_UNKNOWN_USER_99",
1252   "EM_SETPUNCTUATION",
1253   "EM_GETPUNCTUATION",
1254   "EM_SETWORDWRAPMODE",
1255   "EM_GETWORDWRAPMODE",
1256   "EM_SETIMECOLOR",
1257   "EM_GETIMECOLOR",
1258   "EM_SETIMEOPTIONS",
1259   "EM_GETIMEOPTIONS",
1260   "EM_CONVPOSITION",
1261   "EM_UNKNOWN_USER_109",
1262   "EM_UNKNOWN_USER_110",
1263   "EM_UNKNOWN_USER_111",
1264   "EM_UNKNOWN_USER_112",
1265   "EM_UNKNOWN_USER_113",
1266   "EM_UNKNOWN_USER_114",
1267   "EM_UNKNOWN_USER_115",
1268   "EM_UNKNOWN_USER_116",
1269   "EM_UNKNOWN_USER_117",
1270   "EM_UNKNOWN_USER_118",
1271   "EM_UNKNOWN_USER_119",
1272   "EM_SETLANGOPTIONS",
1273   "EM_GETLANGOPTIONS",
1274   "EM_GETIMECOMPMODE",
1275   "EM_FINDTEXTW",
1276   "EM_FINDTEXTEXW",
1277   "EM_RECONVERSION",
1278   "EM_SETIMEMODEBIAS",
1279   "EM_GETIMEMODEBIAS"
1280 };
1281
1282 static const char *
1283 get_msg_name(UINT msg)
1284 {
1285   if (msg >= EM_GETSEL && msg <= EM_SETLIMITTEXT)
1286     return edit_messages[msg - EM_GETSEL];
1287   if (msg >= EM_CANPASTE && msg <= EM_GETIMEMODEBIAS)
1288     return richedit_messages[msg - EM_CANPASTE];
1289   return "";
1290 }
1291
1292 /******************************************************************
1293  *        RichEditANSIWndProc (RICHED20.10)
1294  */
1295 LRESULT WINAPI RichEditANSIWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) {
1296   SCROLLINFO si;
1297   ME_TextEditor *editor = (ME_TextEditor *)GetWindowLongW(hWnd, 0);
1298   
1299   TRACE("hWnd %p msg %04x (%s) %08x %08lx\n",
1300         hWnd, msg, get_msg_name(msg), wParam, lParam);
1301   
1302   switch(msg) {
1303   
1304   UNSUPPORTED_MSG(EM_AUTOURLDETECT)
1305   UNSUPPORTED_MSG(EM_DISPLAYBAND)
1306   UNSUPPORTED_MSG(EM_EXLIMITTEXT)
1307   UNSUPPORTED_MSG(EM_FINDWORDBREAK)
1308   UNSUPPORTED_MSG(EM_FMTLINES)
1309   UNSUPPORTED_MSG(EM_FORMATRANGE)
1310   UNSUPPORTED_MSG(EM_GETAUTOURLDETECT)
1311   UNSUPPORTED_MSG(EM_GETBIDIOPTIONS)
1312   UNSUPPORTED_MSG(EM_GETEDITSTYLE)
1313   UNSUPPORTED_MSG(EM_GETIMECOMPMODE)
1314   /* UNSUPPORTED_MSG(EM_GETIMESTATUS) missing in Wine headers */
1315   UNSUPPORTED_MSG(EM_GETLANGOPTIONS)
1316   UNSUPPORTED_MSG(EM_GETLIMITTEXT)
1317   UNSUPPORTED_MSG(EM_GETLINE)
1318   /* UNSUPPORTED_MSG(EM_GETOLEINTERFACE) separate stub */
1319   UNSUPPORTED_MSG(EM_GETOPTIONS)
1320   UNSUPPORTED_MSG(EM_GETPASSWORDCHAR)
1321   UNSUPPORTED_MSG(EM_GETREDONAME)
1322   UNSUPPORTED_MSG(EM_GETSCROLLPOS)
1323   UNSUPPORTED_MSG(EM_GETTEXTMODE)
1324   UNSUPPORTED_MSG(EM_GETTYPOGRAPHYOPTIONS)
1325   UNSUPPORTED_MSG(EM_GETUNDONAME)
1326   UNSUPPORTED_MSG(EM_GETWORDBREAKPROCEX)
1327   UNSUPPORTED_MSG(EM_HIDESELECTION)
1328   UNSUPPORTED_MSG(EM_LIMITTEXT) /* also known as EM_SETLIMITTEXT */
1329   UNSUPPORTED_MSG(EM_PASTESPECIAL)
1330   UNSUPPORTED_MSG(EM_SCROLL)
1331   UNSUPPORTED_MSG(EM_SCROLLCARET)
1332   UNSUPPORTED_MSG(EM_SELECTIONTYPE)
1333   UNSUPPORTED_MSG(EM_SETBIDIOPTIONS)
1334   UNSUPPORTED_MSG(EM_SETEDITSTYLE)
1335   UNSUPPORTED_MSG(EM_SETFONTSIZE)
1336   UNSUPPORTED_MSG(EM_SETLANGOPTIONS)
1337   UNSUPPORTED_MSG(EM_SETOPTIONS)
1338   UNSUPPORTED_MSG(EM_SETPALETTE)
1339   UNSUPPORTED_MSG(EM_SETPASSWORDCHAR)
1340   UNSUPPORTED_MSG(EM_SETSCROLLPOS)
1341   UNSUPPORTED_MSG(EM_SETTABSTOPS)
1342   UNSUPPORTED_MSG(EM_SETTARGETDEVICE)
1343   UNSUPPORTED_MSG(EM_SETTEXTMODE)
1344   UNSUPPORTED_MSG(EM_SETTYPOGRAPHYOPTIONS)
1345   UNSUPPORTED_MSG(EM_SETUNDOLIMIT)
1346   UNSUPPORTED_MSG(EM_SETWORDBREAKPROCEX)
1347   UNSUPPORTED_MSG(EM_SHOWSCROLLBAR)
1348   UNSUPPORTED_MSG(WM_SETFONT)
1349   UNSUPPORTED_MSG(WM_STYLECHANGING)
1350   UNSUPPORTED_MSG(WM_STYLECHANGED)
1351 /*  UNSUPPORTED_MSG(WM_UNICHAR) FIXME missing in Wine headers */
1352     
1353 /* Messages specific to Richedit controls */
1354   
1355   case EM_STREAMIN:
1356    return ME_StreamIn(editor, wParam, (EDITSTREAM*)lParam);
1357   case EM_STREAMOUT:
1358    return ME_StreamOut(editor, wParam, (EDITSTREAM *)lParam);
1359   case WM_GETDLGCODE:
1360   {
1361     UINT code = DLGC_WANTCHARS|DLGC_WANTARROWS;
1362     if (GetWindowLongW(hWnd, GWL_STYLE)&ES_WANTRETURN)
1363       code |= 0; /* FIXME what can we do here ? ask for messages and censor them ? */
1364     return code;
1365   }
1366   case WM_NCCREATE:
1367   {
1368     CREATESTRUCTW *pcs = (CREATESTRUCTW *)lParam;
1369     TRACE("WM_NCCREATE: style 0x%08lx\n", pcs->style);
1370     editor = ME_MakeEditor(hWnd);
1371     SetWindowLongW(hWnd, 0, (long)editor);
1372     pcs = 0; /* ignore */
1373     return TRUE;
1374   }
1375   case EM_EMPTYUNDOBUFFER:
1376     ME_EmptyUndoStack(editor);
1377     return 0;
1378   case EM_GETSEL:
1379   {
1380     /* Note: wParam/lParam can be NULL */
1381     UINT from, to;
1382     PUINT pfrom = wParam ? (PUINT)wParam : &from;
1383     PUINT pto = lParam ? (PUINT)lParam : &to;
1384     ME_GetSelection(editor, (int *)pfrom, (int *)pto);
1385     if ((*pfrom|*pto) & 0xFFFF0000)
1386       return -1;
1387     return MAKELONG(*pfrom,*pto);
1388   }
1389   case EM_EXGETSEL:
1390   {
1391     CHARRANGE *pRange = (CHARRANGE *)lParam;
1392     ME_GetSelection(editor, (int *)&pRange->cpMin, (int *)&pRange->cpMax);
1393     TRACE("EM_EXGETSEL = (%ld,%ld)\n", pRange->cpMin, pRange->cpMax);
1394     return 0;
1395   }
1396   case EM_CANUNDO:
1397     return editor->pUndoStack != NULL;
1398   case EM_CANREDO:
1399     return editor->pRedoStack != NULL;
1400   case WM_UNDO: /* FIXME: actually not the same */
1401   case EM_UNDO:
1402     ME_Undo(editor);
1403     return 0;
1404   case EM_REDO:
1405     ME_Redo(editor);
1406     return 0;
1407   case EM_SETSEL:
1408   {
1409     ME_InvalidateSelection(editor);
1410     ME_SetSelection(editor, wParam, lParam);
1411     ME_InvalidateSelection(editor);
1412     ME_SendSelChange(editor);
1413     return 0;
1414   }
1415   case EM_EXSETSEL:
1416   {
1417     CHARRANGE *pRange = (CHARRANGE *)lParam;
1418     TRACE("EM_EXSETSEL (%ld,%ld)\n", pRange->cpMin, pRange->cpMax);
1419     ME_InvalidateSelection(editor);
1420     ME_SetSelection(editor, pRange->cpMin, pRange->cpMax);
1421     ME_InvalidateSelection(editor);
1422     ME_SendSelChange(editor);
1423     return 0;
1424   }
1425   case EM_SETTEXTEX:
1426   {
1427     LPWSTR wszText = (LPWSTR)lParam;
1428     SETTEXTEX *pStruct = (SETTEXTEX *)wParam;
1429     size_t len = lstrlenW(wszText);
1430     int from, to;
1431     ME_Style *style;
1432     TRACE("EM_SETTEXEX - %s, flags %d, cp %d\n", debugstr_w(wszText), (int)pStruct->flags, pStruct->codepage);
1433     if (pStruct->codepage != 1200) {
1434       FIXME("EM_SETTEXTEX only supports unicode right now!\n"); 
1435       return 0;
1436     }
1437     /* FIXME: this should support RTF strings too, according to MSDN */
1438     if (pStruct->flags & ST_SELECTION) {
1439       ME_GetSelection(editor, &from, &to);
1440       style = ME_GetSelectionInsertStyle(editor);
1441       ME_InternalDeleteText(editor, from, to - from);
1442       ME_InsertTextFromCursor(editor, 0, wszText, len, style);
1443       ME_ReleaseStyle(style);
1444     }
1445     else {
1446       ME_InternalDeleteText(editor, 0, ME_GetTextLength(editor));
1447       ME_InsertTextFromCursor(editor, 0, wszText, -1, editor->pBuffer->pDefaultStyle);
1448       len = 1;
1449     }
1450     ME_CommitUndo(editor);
1451     if (!(pStruct->flags & ST_KEEPUNDO))
1452       ME_EmptyUndoStack(editor);
1453     ME_UpdateRepaint(editor);
1454     return len;
1455   }
1456   case EM_SETBKGNDCOLOR:
1457   {
1458     LRESULT lColor = ME_GetBackColor(editor);
1459     if (editor->rgbBackColor != -1)
1460       DeleteObject(editor->hbrBackground);
1461     if (wParam)
1462     {
1463       editor->rgbBackColor = -1;
1464       editor->hbrBackground = GetSysColorBrush(COLOR_WINDOW);
1465     }
1466     else
1467     {
1468       editor->rgbBackColor = lParam;
1469       editor->hbrBackground = CreateSolidBrush(editor->rgbBackColor);
1470     }
1471     if (editor->bRedraw)
1472     {
1473       InvalidateRect(hWnd, NULL, TRUE);
1474       UpdateWindow(hWnd);
1475     }
1476     return lColor;
1477   }
1478   case EM_GETMODIFY:
1479     return editor->nModifyStep == 0 ? 0 : 1;
1480   case EM_SETMODIFY:
1481   {
1482     if (wParam)
1483       editor->nModifyStep = 0x80000000;
1484     else
1485       editor->nModifyStep = 0;
1486     
1487     return 0;
1488   }
1489   case EM_SETREADONLY:
1490   {
1491     long nStyle = GetWindowLongW(hWnd, GWL_STYLE);
1492     if (wParam)
1493       nStyle |= ES_READONLY;
1494     else
1495       nStyle &= ~ES_READONLY;
1496     SetWindowLongW(hWnd, GWL_STYLE, nStyle);
1497     return 0;
1498   }
1499   case EM_SETEVENTMASK:
1500   {
1501     DWORD nOldMask = editor->nEventMask;
1502     
1503     editor->nEventMask = lParam;
1504     return nOldMask;
1505   }
1506   case EM_GETEVENTMASK:
1507     return editor->nEventMask;
1508   case EM_SETCHARFORMAT:
1509   {
1510     CHARFORMAT2W buf, *p;
1511     BOOL bRepaint = TRUE;
1512     p = ME_ToCF2W(&buf, (CHARFORMAT2W *)lParam);
1513     if (!wParam)
1514       ME_SetDefaultCharFormat(editor, p);
1515     else if (wParam == (SCF_WORD | SCF_SELECTION))
1516       FIXME("EM_SETCHARFORMAT: word selection not supported\n");
1517     else if (wParam == SCF_ALL)
1518       ME_SetCharFormat(editor, 0, ME_GetTextLength(editor), p);
1519     else {
1520       int from, to;
1521       ME_GetSelection(editor, &from, &to);
1522       bRepaint = (from != to);
1523       ME_SetSelectionCharFormat(editor, p);
1524     }
1525     ME_CommitUndo(editor);
1526     if (bRepaint)
1527       ME_UpdateRepaint(editor);
1528     return 0;
1529   }
1530   case EM_GETCHARFORMAT:
1531   {
1532     CHARFORMAT2W tmp, *dst = (CHARFORMAT2W *)lParam;
1533     if (dst->cbSize != sizeof(CHARFORMATA) &&
1534         dst->cbSize != sizeof(CHARFORMATW) &&
1535         dst->cbSize != sizeof(CHARFORMAT2A) &&
1536         dst->cbSize != sizeof(CHARFORMAT2W))
1537       return 0;
1538     tmp.cbSize = sizeof(tmp);
1539     if (!wParam)
1540       ME_GetDefaultCharFormat(editor, &tmp);
1541     else
1542       ME_GetSelectionCharFormat(editor, &tmp);
1543     ME_CopyToCFAny(dst, &tmp);
1544     return tmp.dwMask;
1545   }
1546   case EM_SETPARAFORMAT:
1547     ME_SetSelectionParaFormat(editor, (PARAFORMAT2 *)lParam);
1548     ME_UpdateRepaint(editor);
1549     ME_CommitUndo(editor);
1550     return 0;
1551   case EM_GETPARAFORMAT:
1552     ME_GetSelectionParaFormat(editor, (PARAFORMAT2 *)lParam);
1553     return 0;
1554   case EM_GETFIRSTVISIBLELINE:
1555   {
1556     ME_DisplayItem *p = editor->pBuffer->pFirst;
1557     int y = editor->nScrollPosY;
1558     int ypara = 0;
1559     int count = 0;
1560     int ystart, yend;
1561     while(p) {
1562       p = ME_FindItemFwd(p, diStartRowOrParagraphOrEnd);
1563       if (p->type == diTextEnd)
1564         break;
1565       if (p->type == diParagraph) {
1566         ypara = p->member.para.nYPos;
1567         continue;
1568       }
1569       ystart = ypara + p->member.row.nYPos;
1570       yend = ystart + p->member.row.nHeight;
1571       if (y < yend) {
1572         break;
1573       }
1574       count++;
1575     }
1576     return count;
1577   }
1578   case EM_LINESCROLL:
1579   {
1580     int nPos = editor->nScrollPosY, nEnd= editor->nTotalLength - editor->sizeWindow.cy;
1581     nPos += 8 * lParam; /* FIXME follow the original */
1582     if (nPos>=nEnd)
1583       nPos = nEnd;
1584     if (nPos<0)
1585       nPos = 0;
1586     if (nPos != editor->nScrollPosY) {
1587       int dy = editor->nScrollPosY - nPos;
1588       editor->nScrollPosY = nPos;
1589       SetScrollPos(hWnd, SB_VERT, nPos, TRUE);
1590       if (editor->bRedraw)
1591       {
1592         ScrollWindow(hWnd, 0, dy, NULL, NULL);
1593         UpdateWindow(hWnd);
1594       }
1595     }
1596     return TRUE; /* Should return false if a single line richedit control */
1597   }
1598   case WM_CLEAR:
1599   {
1600     int from, to;
1601     ME_GetSelection(editor, &from, &to);
1602     ME_InternalDeleteText(editor, from, to-from);
1603     ME_CommitUndo(editor);
1604     ME_UpdateRepaint(editor);
1605     return 0;
1606   }
1607   case EM_REPLACESEL:
1608   {
1609     int from, to;
1610     ME_Style *style;
1611     LPWSTR wszText = ME_ToUnicode(hWnd, (void *)lParam);
1612     size_t len = lstrlenW(wszText);
1613     TRACE("EM_REPLACESEL - %s\n", debugstr_w(wszText));
1614     
1615     ME_GetSelection(editor, &from, &to);
1616     style = ME_GetSelectionInsertStyle(editor);
1617     ME_InternalDeleteText(editor, from, to-from);
1618     ME_InsertTextFromCursor(editor, 0, wszText, len, style);
1619     ME_ReleaseStyle(style);
1620     ME_EndToUnicode(hWnd, wszText);
1621     /* drop temporary style if line end */
1622     /* FIXME question: does abc\n mean: put abc, clear temp style, put \n? (would require a change) */  
1623     if (len>0 && wszText[len-1] == '\n')
1624       ME_ClearTempStyle(editor);
1625       
1626     ME_CommitUndo(editor);
1627     if (!wParam)
1628       ME_EmptyUndoStack(editor);
1629     ME_UpdateRepaint(editor);
1630     return 0;
1631   }
1632   case WM_SETTEXT:
1633   {
1634     ME_InternalDeleteText(editor, 0, ME_GetTextLength(editor));
1635     if (lParam)
1636     {
1637       TRACE("WM_SETTEXT lParam==%lx\n",lParam);
1638       if (!IsWindowUnicode(hWnd) && !strncmp((char *)lParam, "{\\rtf1", 6))
1639       {
1640         /* Undocumented: WM_SETTEXT supports RTF text */
1641         ME_StreamInRTFString(editor, 0, (char *)lParam);
1642       }
1643       else
1644       {
1645         LPWSTR wszText = ME_ToUnicode(hWnd, (void *)lParam);
1646         TRACE("WM_SETTEXT - %s\n", debugstr_w(wszText)); /* debugstr_w() */
1647         if (lstrlenW(wszText) > 0)
1648         {
1649           /* uses default style! */
1650           ME_InsertTextFromCursor(editor, 0, wszText, -1, editor->pBuffer->pDefaultStyle);
1651         }
1652         ME_EndToUnicode(hWnd, wszText);
1653       }
1654     }
1655     else
1656       TRACE("WM_SETTEXT - NULL\n");
1657     ME_CommitUndo(editor);
1658     ME_EmptyUndoStack(editor);
1659     ME_SetSelection(editor, 0, 0);
1660     ME_UpdateRepaint(editor);
1661     return 0;
1662   }
1663   case EM_CANPASTE:
1664   {
1665     UINT nRTFFormat = RegisterClipboardFormatA("Rich Text Format");
1666     if (IsClipboardFormatAvailable(nRTFFormat))
1667       return TRUE;
1668     if (IsClipboardFormatAvailable(CF_UNICODETEXT))
1669       return TRUE;
1670     return FALSE;
1671   }
1672   case WM_PASTE:
1673   {    
1674     DWORD dwFormat = 0;
1675     EDITSTREAM es;
1676     ME_GlobalDestStruct gds;
1677     UINT nRTFFormat = RegisterClipboardFormatA("Rich Text Format");
1678     UINT cf = 0;
1679
1680     if (IsClipboardFormatAvailable(nRTFFormat))
1681       cf = nRTFFormat, dwFormat = SF_RTF;
1682     else if (IsClipboardFormatAvailable(CF_UNICODETEXT))
1683       cf = CF_UNICODETEXT, dwFormat = SF_TEXT|SF_UNICODE;
1684     else
1685       return 0;
1686
1687     if (!OpenClipboard(hWnd))
1688       return 0;
1689     gds.hData = GetClipboardData(cf);
1690     gds.nLength = 0;
1691     es.dwCookie = (DWORD)&gds;
1692     es.pfnCallback = dwFormat == SF_RTF ? ME_ReadFromHGLOBALRTF : ME_ReadFromHGLOBALUnicode;
1693     ME_StreamIn(editor, dwFormat|SFF_SELECTION, &es);
1694
1695     CloseClipboard();
1696     return 0;
1697   }
1698   case WM_CUT:
1699   case WM_COPY:
1700   {
1701     int from, to, pars;
1702     WCHAR *data;
1703     HANDLE hData;
1704     EDITSTREAM es;
1705     ME_GlobalDestStruct gds;
1706     
1707     if (!OpenClipboard(hWnd))
1708       return 0;
1709       
1710     EmptyClipboard();
1711     ME_GetSelection(editor, &from, &to);
1712     pars = ME_CountParagraphsBetween(editor, from, to);
1713     hData = GlobalAlloc(GMEM_MOVEABLE, sizeof(WCHAR)*(to-from+pars+1));
1714     data = (WCHAR *)GlobalLock(hData);
1715     ME_GetTextW(editor, data, from, to-from, TRUE);
1716     GlobalUnlock(hData);
1717
1718     gds.hData = GlobalAlloc(GMEM_MOVEABLE, 0);
1719     gds.nLength = 0;
1720     es.dwCookie = (DWORD)&gds;
1721     es.pfnCallback = ME_AppendToHGLOBAL;
1722     ME_StreamOutRange(editor, SF_RTF, from, to, &es);
1723     GlobalReAlloc(gds.hData, gds.nLength+1, 0);
1724     
1725     SetClipboardData(CF_UNICODETEXT, hData);    
1726     SetClipboardData(RegisterClipboardFormatA("Rich Text Format"), gds.hData);
1727     
1728     CloseClipboard();
1729     if (msg == WM_CUT)
1730     {
1731       ME_InternalDeleteText(editor, from, to-from);
1732       ME_CommitUndo(editor);
1733       ME_UpdateRepaint(editor);
1734     }
1735     return 0;
1736   }
1737   case WM_GETTEXTLENGTH:
1738     return ME_GetTextLength(editor);
1739   case EM_GETTEXTLENGTHEX:
1740     return ME_GetTextLengthEx(editor, (GETTEXTLENGTHEX *)wParam);
1741   case WM_GETTEXT:
1742   {
1743     TEXTRANGEW tr; /* W and A differ only by rng->lpstrText */
1744     tr.chrg.cpMin = 0;
1745     tr.chrg.cpMax = wParam-1;
1746     tr.lpstrText = (WCHAR *)lParam;
1747     return RichEditANSIWndProc(hWnd, EM_GETTEXTRANGE, 0, (LPARAM)&tr);
1748   }
1749   case EM_GETTEXTEX:
1750   {
1751     GETTEXTEX *ex = (GETTEXTEX*)wParam;
1752     int nStart, nCount;
1753
1754     if (ex->flags & ~(GT_SELECTION | GT_USECRLF))
1755       FIXME("GETTEXTEX flags 0x%08lx not supported\n", ex->flags & ~(GT_SELECTION | GT_USECRLF));
1756
1757     if (ex->flags & GT_SELECTION)
1758     {
1759       ME_GetSelection(editor, &nStart, &nCount);
1760       nCount -= nStart;
1761       nCount = min(nCount, ex->cb - 1);
1762     }
1763     else
1764     {
1765       nStart = 0;
1766       nCount = ex->cb - 1;
1767     }
1768     if (ex->codepage == 1200 || IsWindowUnicode(hWnd))
1769     {
1770       nCount = min(nCount, ex->cb / sizeof(WCHAR) - 1);
1771       return ME_GetTextW(editor, (LPWSTR)lParam, nStart, nCount, ex->flags & GT_USECRLF);
1772     }
1773     else
1774     {
1775       /* potentially each char may be a CR, why calculate the exact value with O(N) when
1776         we can just take a bigger buffer? :) */
1777       int crlfmul = (ex->flags & GT_USECRLF) ? 2 : 1;
1778       LPWSTR buffer = HeapAlloc(GetProcessHeap(), 0, (crlfmul*nCount + 1) * sizeof(WCHAR));
1779       DWORD buflen = ex->cb;
1780       LRESULT rc;
1781       DWORD flags = 0;
1782
1783       buflen = ME_GetTextW(editor, buffer, nStart, nCount, ex->flags & GT_USECRLF);
1784       rc = WideCharToMultiByte(ex->codepage, flags, buffer, buflen, (LPSTR)lParam, ex->cb, ex->lpDefaultChar, ex->lpUsedDefaultChar);
1785
1786       HeapFree(GetProcessHeap(),0,buffer);
1787       return rc;
1788     }
1789   }
1790   case EM_GETSELTEXT:
1791   {
1792     int from, to;
1793     TEXTRANGEW tr; /* W and A differ only by rng->lpstrText */
1794     ME_GetSelection(editor, &from, &to);
1795     tr.chrg.cpMin = from;
1796     tr.chrg.cpMax = to;
1797     tr.lpstrText = (WCHAR *)lParam;
1798     return RichEditANSIWndProc(hWnd, EM_GETTEXTRANGE, 0, (LPARAM)&tr);
1799   }
1800   case EM_GETTEXTRANGE:
1801   {
1802     TEXTRANGEW *rng = (TEXTRANGEW *)lParam;
1803     TRACE("EM_GETTEXTRANGE min=%ld max=%ld unicode=%d emul1.0=%d length=%d\n",
1804       rng->chrg.cpMin, rng->chrg.cpMax, IsWindowUnicode(hWnd), 
1805       editor->bEmulateVersion10, ME_GetTextLength(editor));
1806     if (IsWindowUnicode(hWnd))
1807       return ME_GetTextW(editor, rng->lpstrText, rng->chrg.cpMin, rng->chrg.cpMax-rng->chrg.cpMin, editor->bEmulateVersion10);
1808     else
1809     {
1810       int nLen = rng->chrg.cpMax-rng->chrg.cpMin;
1811       WCHAR *p = ALLOC_N_OBJ(WCHAR, nLen+1);
1812       int nChars = ME_GetTextW(editor, p, rng->chrg.cpMin, nLen, editor->bEmulateVersion10);
1813       /* FIXME this is a potential security hole (buffer overrun) 
1814          if you know more about wchar->mbyte conversion please explain
1815       */
1816       WideCharToMultiByte(CP_ACP, 0, p, nChars+1, (char *)rng->lpstrText, nLen+1, NULL, NULL);
1817       FREE_OBJ(p);
1818       return nChars;
1819     }
1820   }
1821   case EM_GETLINECOUNT:
1822   {
1823     ME_DisplayItem *item = editor->pBuffer->pFirst->next;
1824     int nRows = 0;
1825
1826     while (item != editor->pBuffer->pLast)
1827     {
1828       assert(item->type == diParagraph);
1829       nRows += item->member.para.nRows;
1830       item = item->member.para.next_para;
1831     }
1832     TRACE("EM_GETLINECOUNT: nRows==%d\n", nRows);
1833     return max(1, nRows);
1834   }
1835   case EM_LINEFROMCHAR:
1836   {
1837     if (wParam == -1)
1838       return ME_RowNumberFromCharOfs(editor, ME_GetCursorOfs(editor, 1));
1839     else
1840       return ME_RowNumberFromCharOfs(editor, wParam);
1841   }
1842   case EM_EXLINEFROMCHAR:
1843   {
1844     return ME_RowNumberFromCharOfs(editor, lParam);
1845   }
1846   case EM_LINEINDEX:
1847   {
1848     ME_DisplayItem *item, *para;
1849     int nCharOfs;
1850     
1851     if (wParam == -1)
1852       item = ME_FindItemBack(editor->pCursors[0].pRun, diStartRow);
1853     else
1854       item = ME_FindRowWithNumber(editor, wParam);
1855     if (!item)
1856       return -1;
1857     para = ME_GetParagraph(item);
1858     item = ME_FindItemFwd(item, diRun);
1859     nCharOfs = para->member.para.nCharOfs + item->member.run.nCharOfs;
1860     TRACE("EM_LINEINDEX: nCharOfs==%d\n", nCharOfs);
1861     return nCharOfs;
1862   }
1863   case EM_LINELENGTH:
1864   {
1865     ME_DisplayItem *item, *item_end;
1866     int nChars = 0, nThisLineOfs = 0, nNextLineOfs = 0;
1867     
1868     if (wParam > ME_GetTextLength(editor))
1869       return 0;
1870     if (wParam == -1)
1871     {
1872       FIXME("EM_LINELENGTH: returning number of unselected characters on lines with selection unsupported.\n");
1873       return 0;
1874     }
1875     item = ME_FindItemAtOffset(editor, diRun, wParam, NULL);
1876     item = ME_RowStart(item);
1877     nThisLineOfs = ME_CharOfsFromRunOfs(editor, ME_FindItemFwd(item, diRun), 0);
1878     item_end = ME_FindItemFwd(item, diStartRow);
1879     if (item_end)
1880       nNextLineOfs = ME_CharOfsFromRunOfs(editor, ME_FindItemFwd(item_end, diRun), 0);
1881     else
1882       nNextLineOfs = ME_FindItemFwd(item, diParagraphOrEnd)->member.para.nCharOfs
1883        - (editor->bEmulateVersion10?2:1);
1884     nChars = nNextLineOfs - nThisLineOfs;
1885     TRACE("EM_LINELENGTH(%d)==%d\n",wParam, nChars);
1886     return nChars;
1887   }
1888   case EM_FINDTEXT:
1889   {
1890     FINDTEXTA *ft = (FINDTEXTA *)lParam;
1891     int nChars = MultiByteToWideChar(CP_ACP, 0, ft->lpstrText, -1, NULL, 0);
1892     WCHAR *tmp;
1893     
1894     if ((tmp = ALLOC_N_OBJ(WCHAR, nChars)) != NULL)
1895       MultiByteToWideChar(CP_ACP, 0, ft->lpstrText, -1, tmp, nChars);
1896     return ME_FindText(editor, wParam, &ft->chrg, tmp, NULL);
1897   }
1898   case EM_FINDTEXTEX:
1899   {
1900     FINDTEXTEXA *ex = (FINDTEXTEXA *)lParam;
1901     int nChars = MultiByteToWideChar(CP_ACP, 0, ex->lpstrText, -1, NULL, 0);
1902     WCHAR *tmp;
1903     
1904     if ((tmp = ALLOC_N_OBJ(WCHAR, nChars)) != NULL)
1905       MultiByteToWideChar(CP_ACP, 0, ex->lpstrText, -1, tmp, nChars);
1906     return ME_FindText(editor, wParam, &ex->chrg, tmp, &ex->chrgText);
1907   }
1908   case EM_FINDTEXTW:
1909   {
1910     FINDTEXTW *ft = (FINDTEXTW *)lParam;
1911     return ME_FindText(editor, wParam, &ft->chrg, ft->lpstrText, NULL);
1912   }
1913   case EM_FINDTEXTEXW:
1914   {
1915     FINDTEXTEXW *ex = (FINDTEXTEXW *)lParam;
1916     return ME_FindText(editor, wParam, &ex->chrg, ex->lpstrText, &ex->chrgText);
1917   }
1918   case EM_GETZOOM:
1919     if (!wParam || !lParam)
1920       return FALSE;
1921     *(int *)wParam = editor->nZoomNumerator;
1922     *(int *)lParam = editor->nZoomDenominator;
1923     return TRUE;
1924   case EM_SETZOOM:
1925     return ME_SetZoom(editor, wParam, lParam);
1926   case EM_CHARFROMPOS:
1927     return ME_CharFromPos(editor, ((POINTL *)lParam)->x, ((POINTL *)lParam)->y);
1928   case EM_POSFROMCHAR:
1929   {
1930     ME_DisplayItem *pRun;
1931     int nCharOfs, nOffset, nLength;
1932     POINTL pt = {0,0};
1933     
1934     nCharOfs = wParam; 
1935     /* detect which API version we're dealing with */
1936     if (wParam >= 0x40000)
1937         nCharOfs = lParam;
1938     nLength = ME_GetTextLength(editor);
1939     
1940     if (nCharOfs < nLength) { 
1941         ME_RunOfsFromCharOfs(editor, nCharOfs, &pRun, &nOffset);
1942         assert(pRun->type == diRun);
1943         pt.y = pRun->member.run.pt.y;
1944         pt.x = pRun->member.run.pt.x + ME_PointFromChar(editor, &pRun->member.run, nOffset);
1945         pt.y += ME_GetParagraph(pRun)->member.para.nYPos;
1946     } else {
1947         pt.x = 0;
1948         pt.y = editor->pBuffer->pLast->member.para.nYPos;
1949     }
1950     if (wParam >= 0x40000) {
1951         *(POINTL *)wParam = pt;
1952     }
1953     return MAKELONG( pt.x, pt.y );
1954   }
1955   case WM_CREATE:
1956     ME_CommitUndo(editor);
1957     ME_WrapMarkedParagraphs(editor);
1958     ME_MoveCaret(editor);
1959     return 0;
1960   case WM_DESTROY:
1961     ME_DestroyEditor(editor);
1962     SetWindowLongW(hWnd, 0, 0);
1963     return 0;
1964   case WM_LBUTTONDOWN:
1965     SetFocus(hWnd);
1966     ME_LButtonDown(editor, (short)LOWORD(lParam), (short)HIWORD(lParam));
1967     SetCapture(hWnd);
1968     break;
1969   case WM_MOUSEMOVE:
1970     if (GetCapture() == hWnd)
1971       ME_MouseMove(editor, (short)LOWORD(lParam), (short)HIWORD(lParam));
1972     break;
1973   case WM_LBUTTONUP:
1974     if (GetCapture() == hWnd)
1975       ReleaseCapture();
1976     break;
1977   case WM_LBUTTONDBLCLK:
1978     ME_SelectWord(editor);
1979     break;
1980   case WM_CONTEXTMENU:
1981     ME_ShowContextMenu(editor, (short)LOWORD(lParam), (short)HIWORD(lParam));
1982     break;
1983   case WM_PAINT:
1984     if (editor->bRedraw)
1985     {
1986       HDC hDC;
1987       PAINTSTRUCT ps;
1988
1989       hDC = BeginPaint(hWnd, &ps);
1990       ME_PaintContent(editor, hDC, FALSE, &ps.rcPaint);
1991       EndPaint(hWnd, &ps);
1992     }
1993     break;
1994   case WM_SETFOCUS:
1995     ME_ShowCaret(editor);
1996     ME_SendOldNotify(editor, EN_SETFOCUS);
1997     return 0;
1998   case WM_KILLFOCUS:
1999     ME_HideCaret(editor);
2000     ME_SendOldNotify(editor, EN_KILLFOCUS);
2001     return 0;
2002   case WM_ERASEBKGND:
2003   {
2004     if (editor->bRedraw)
2005     {
2006       HDC hDC = (HDC)wParam;
2007       RECT rc;
2008       if (GetUpdateRect(hWnd,&rc,TRUE))
2009       {
2010         FillRect(hDC, &rc, editor->hbrBackground);
2011       }
2012     }
2013     return 1;
2014   }
2015   case WM_COMMAND:
2016     TRACE("editor wnd command = %d\n", LOWORD(wParam));
2017     return 0;
2018   case WM_KEYDOWN:
2019     if (ME_KeyDown(editor, LOWORD(wParam)))
2020       return 0;
2021     goto do_default;
2022   case WM_CHAR: 
2023   {
2024     WCHAR wstr = LOWORD(wParam);
2025
2026     switch (wstr)
2027     {
2028     case 3: /* Ctrl-C */
2029       SendMessageW(editor->hWnd, WM_COPY, 0, 0);
2030       return 0;
2031     }
2032     
2033     if (GetWindowLongW(editor->hWnd, GWL_STYLE) & ES_READONLY) {
2034       MessageBeep(MB_ICONERROR);
2035       return 0; /* FIXME really 0 ? */
2036     }
2037
2038     switch (wstr)
2039     {
2040     case 1: /* Ctrl-A */
2041       ME_SetSelection(editor, 0, -1);
2042       return 0;
2043     case 22: /* Ctrl-V */
2044       SendMessageW(editor->hWnd, WM_PASTE, 0, 0);
2045       return 0;
2046     case 24: /* Ctrl-X */
2047       SendMessageW(editor->hWnd, WM_CUT, 0, 0);
2048       return 0;
2049     case 25: /* Ctrl-Y */
2050       SendMessageW(editor->hWnd, EM_REDO, 0, 0);
2051       return 0;
2052     case 26: /* Ctrl-Z */
2053       SendMessageW(editor->hWnd, EM_UNDO, 0, 0);
2054       return 0;
2055     }
2056     if (((unsigned)wstr)>=' ' || wstr=='\r' || wstr=='\t') {
2057       /* FIXME maybe it would make sense to call EM_REPLACESEL instead ? */
2058       ME_Style *style = ME_GetInsertStyle(editor, 0);
2059       ME_SaveTempStyle(editor);
2060       ME_InsertTextFromCursor(editor, 0, &wstr, 1, style);
2061       ME_ReleaseStyle(style);
2062       ME_CommitUndo(editor);
2063       ME_UpdateRepaint(editor);
2064     }
2065     return 0;
2066   }
2067   case WM_VSCROLL: 
2068   {
2069     int nPos = editor->nScrollPosY;
2070     si.cbSize = sizeof(SCROLLINFO);
2071     si.fMask = SIF_PAGE|SIF_POS|SIF_RANGE|SIF_TRACKPOS;
2072     GetScrollInfo(hWnd, SB_VERT, &si);
2073     switch(LOWORD(wParam)) {
2074     case SB_LINEUP:
2075       nPos -= 24; /* FIXME follow the original */
2076       if (nPos<0) nPos = 0;
2077       break;
2078     case SB_LINEDOWN:
2079     {
2080       int nEnd = editor->nTotalLength - editor->sizeWindow.cy;
2081       nPos += 24; /* FIXME follow the original */
2082       if (nPos>=nEnd) nPos = nEnd;
2083       break;
2084     }
2085     case SB_PAGEUP:
2086       nPos -= editor->sizeWindow.cy;
2087       if (nPos<0) nPos = 0;
2088       break;
2089     case SB_PAGEDOWN:
2090       nPos += editor->sizeWindow.cy;
2091       if (nPos>=editor->nTotalLength) nPos = editor->nTotalLength-1;
2092       break;
2093     case SB_THUMBTRACK:
2094     case SB_THUMBPOSITION:
2095       nPos = si.nTrackPos;
2096       break;
2097     }
2098     if (nPos != editor->nScrollPosY) {
2099       int dy = editor->nScrollPosY - nPos;
2100       editor->nScrollPosY = nPos;
2101       SetScrollPos(hWnd, SB_VERT, nPos, TRUE);
2102       if (editor->bRedraw)
2103       {
2104         ScrollWindow(hWnd, 0, dy, NULL, NULL);
2105         UpdateWindow(hWnd);
2106       }
2107     }
2108     break;
2109   }
2110   case WM_MOUSEWHEEL:
2111   {
2112     int gcWheelDelta = 0, nPos = editor->nScrollPosY, nEnd = editor->nTotalLength - editor->sizeWindow.cy; 
2113     UINT pulScrollLines;
2114     SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
2115     gcWheelDelta -= GET_WHEEL_DELTA_WPARAM(wParam);
2116     if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
2117       nPos += pulScrollLines * (gcWheelDelta / WHEEL_DELTA) * 8; /* FIXME follow the original */
2118     if (nPos>=nEnd)
2119       nPos = nEnd;
2120     if (nPos<0)
2121       nPos = 0;
2122     if (nPos != editor->nScrollPosY) {
2123       int dy = editor->nScrollPosY - nPos;
2124       editor->nScrollPosY = nPos;
2125       SetScrollPos(hWnd, SB_VERT, nPos, TRUE);
2126       if (editor->bRedraw)
2127       {
2128         ScrollWindow(hWnd, 0, dy, NULL, NULL);
2129         UpdateWindow(hWnd);
2130       }
2131     }
2132     break;
2133   }
2134   case EM_GETRECT:
2135   {
2136     *((RECT *)lParam) = editor->rcFormat;
2137     return 0;
2138   }
2139   case EM_SETRECT:
2140   case EM_SETRECTNP:
2141   {
2142     if (lParam)
2143     {
2144       RECT *rc = (RECT *)lParam;
2145       
2146       if (wParam)
2147       {
2148         editor->rcFormat.left += rc->left;
2149         editor->rcFormat.top += rc->top;
2150         editor->rcFormat.right += rc->right;
2151         editor->rcFormat.bottom += rc->bottom;
2152       }
2153       else
2154       {
2155         editor->rcFormat = *rc;
2156       }
2157     }
2158     else
2159     {
2160       GetClientRect(hWnd, &editor->rcFormat);
2161     }
2162     if (msg != EM_SETRECTNP)
2163       ME_RewrapRepaint(editor);
2164     return 0;
2165   }
2166   case EM_REQUESTRESIZE:
2167     ME_SendRequestResize(editor, TRUE);
2168     return 0;
2169   case WM_SETREDRAW:
2170     editor->bRedraw = wParam;
2171     return 0;
2172   case WM_SIZE:
2173   {
2174     GetClientRect(hWnd, &editor->rcFormat);
2175     ME_RewrapRepaint(editor);
2176     return DefWindowProcW(hWnd, msg, wParam, lParam);
2177   }
2178   case EM_GETOLEINTERFACE:
2179   {
2180     LPVOID *ppvObj = (LPVOID*) lParam;
2181     FIXME("EM_GETOLEINTERFACE %p: stub\n", ppvObj);
2182     return CreateIRichEditOle(ppvObj);
2183   }
2184   case EM_SETOLECALLBACK:
2185     if(editor->lpOleCallback)
2186       IUnknown_Release(editor->lpOleCallback);
2187     editor->lpOleCallback = (LPRICHEDITOLECALLBACK)lParam;
2188     if(editor->lpOleCallback)
2189       IUnknown_AddRef(editor->lpOleCallback);
2190     return TRUE;
2191   case EM_GETWORDBREAKPROC:
2192     return (LRESULT)editor->pfnWordBreak;
2193   case EM_SETWORDBREAKPROC:
2194   {
2195     EDITWORDBREAKPROCW pfnOld = editor->pfnWordBreak;
2196
2197     editor->pfnWordBreak = (EDITWORDBREAKPROCW)lParam;
2198     return (LRESULT)pfnOld;
2199   }
2200   default:
2201   do_default:
2202     return DefWindowProcW(hWnd, msg, wParam, lParam);
2203   }
2204   return 0L;
2205 }
2206
2207
2208 /******************************************************************
2209  *        RichEdit10ANSIWndProc (RICHED20.9)
2210  */
2211 LRESULT WINAPI RichEdit10ANSIWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
2212 {
2213   LRESULT result;
2214   
2215   /* FIXME: this is NOT the same as 2.0 version */
2216   result = RichEditANSIWndProc(hWnd, msg, wParam, lParam);
2217   if (msg == WM_NCCREATE)
2218   {
2219     ME_TextEditor *editor = (ME_TextEditor *)GetWindowLongW(hWnd, 0);
2220     
2221     editor->bEmulateVersion10 = TRUE;
2222     editor->pBuffer->pLast->member.para.nCharOfs = 2;
2223   }
2224   return result;
2225 }
2226
2227 void ME_SendOldNotify(ME_TextEditor *editor, int nCode)
2228 {
2229   HWND hWnd = editor->hWnd;
2230   SendMessageA(GetParent(hWnd), WM_COMMAND, (nCode<<16)|GetWindowLongW(hWnd, GWLP_ID), (LPARAM)hWnd);
2231 }
2232
2233 int ME_CountParagraphsBetween(ME_TextEditor *editor, int from, int to)
2234 {
2235   ME_DisplayItem *item = ME_FindItemFwd(editor->pBuffer->pFirst, diParagraph);
2236   int i = 0;
2237   
2238   while(item && item->member.para.next_para->member.para.nCharOfs <= from)
2239     item = item->member.para.next_para;
2240   if (!item)
2241     return 0;
2242   while(item && item->member.para.next_para->member.para.nCharOfs <= to) {
2243     item = item->member.para.next_para;
2244     i++;
2245   }
2246   return i;
2247 }
2248
2249
2250 int ME_GetTextW(ME_TextEditor *editor, WCHAR *buffer, int nStart, int nChars, int bCRLF)
2251 {
2252   ME_DisplayItem *item = ME_FindItemAtOffset(editor, diRun, nStart, &nStart);
2253   int nWritten = 0;
2254   WCHAR *pStart = buffer;
2255   
2256   if (!item) {
2257     *buffer = L'\0';
2258     return 0;
2259   }
2260   
2261   if (nStart)
2262   {
2263     int nLen = ME_StrLen(item->member.run.strText) - nStart;
2264     if (nLen > nChars)
2265       nLen = nChars;
2266     CopyMemory(buffer, item->member.run.strText->szData + nStart, sizeof(WCHAR)*nLen);
2267     nChars -= nLen;
2268     nWritten += nLen;
2269     if (!nChars)
2270       return nWritten;
2271     buffer += nLen;
2272     nStart = 0;
2273     item = ME_FindItemFwd(item, diRun);
2274   }
2275   
2276   while(nChars && item)
2277   {
2278     int nLen = ME_StrLen(item->member.run.strText);
2279     if (nLen > nChars)
2280       nLen = nChars;
2281       
2282     if (item->member.run.nFlags & MERF_ENDPARA)
2283     {
2284       *buffer = '\r';
2285       if (bCRLF)
2286       {
2287         *(++buffer) = '\n';
2288         nWritten++;
2289       }
2290       assert(nLen == 1);
2291       /* our end paragraph consists of 2 characters now */
2292       if (editor->bEmulateVersion10)
2293         nChars--;
2294     }
2295     else      
2296       CopyMemory(buffer, item->member.run.strText->szData, sizeof(WCHAR)*nLen);
2297     nChars -= nLen;
2298     nWritten += nLen;
2299     buffer += nLen;    
2300       
2301     if (!nChars)
2302     {
2303       TRACE("nWritten=%d, actual=%d\n", nWritten, buffer-pStart);
2304       *buffer = L'\0';
2305       return nWritten;
2306     }
2307     item = ME_FindItemFwd(item, diRun);
2308   }
2309   *buffer = L'\0';
2310   TRACE("nWritten=%d, actual=%d\n", nWritten, buffer-pStart);
2311   return nWritten;  
2312 }
2313
2314 void ME_RegisterEditorClass(HINSTANCE hInstance)
2315 {
2316   BOOL bResult;
2317   WNDCLASSW wcW;
2318   WNDCLASSA wcA;
2319   
2320   wcW.style = CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW | CS_GLOBALCLASS;
2321   wcW.lpfnWndProc = RichEditANSIWndProc;
2322   wcW.cbClsExtra = 0;
2323   wcW.cbWndExtra = 4;
2324   wcW.hInstance = NULL; /* hInstance would register DLL-local class */
2325   wcW.hIcon = NULL;
2326   wcW.hCursor = LoadCursorW(NULL, MAKEINTRESOURCEW(IDC_IBEAM));
2327   wcW.hbrBackground = (HBRUSH)GetStockObject(NULL_BRUSH);
2328   wcW.lpszMenuName = NULL;
2329   wcW.lpszClassName = wszClassName;
2330   bResult = RegisterClassW(&wcW);  
2331   assert(bResult);
2332   wcW.lpszClassName = wszClassName50;
2333   bResult = RegisterClassW(&wcW);  
2334   assert(bResult);
2335
2336   wcA.style = CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW | CS_GLOBALCLASS;
2337   wcA.lpfnWndProc = RichEditANSIWndProc;
2338   wcA.cbClsExtra = 0;
2339   wcA.cbWndExtra = 4;
2340   wcA.hInstance = NULL; /* hInstance would register DLL-local class */
2341   wcA.hIcon = NULL;
2342   wcA.hCursor = LoadCursorW(NULL, MAKEINTRESOURCEW(IDC_IBEAM));
2343   wcA.hbrBackground = (HBRUSH)GetStockObject(NULL_BRUSH);
2344   wcA.lpszMenuName = NULL;
2345   wcA.lpszClassName = "RichEdit20A";
2346   bResult = RegisterClassA(&wcA);  
2347   assert(bResult);
2348   wcA.lpszClassName = "RichEdit50A";
2349   bResult = RegisterClassA(&wcA);  
2350   assert(bResult);
2351 }
2352
2353 LRESULT WINAPI REComboWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) {
2354   /* FIXME: Not implemented */
2355   TRACE("hWnd %p msg %04x (%s) %08x %08lx\n",
2356         hWnd, msg, get_msg_name(msg), wParam, lParam);
2357   return DefWindowProcW(hWnd, msg, wParam, lParam);
2358 }
2359
2360 LRESULT WINAPI REListWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) {
2361   /* FIXME: Not implemented */
2362   TRACE("hWnd %p msg %04x (%s) %08x %08lx\n",
2363         hWnd, msg, get_msg_name(msg), wParam, lParam);
2364   return DefWindowProcW(hWnd, msg, wParam, lParam);
2365 }
2366
2367 /******************************************************************
2368  *        REExtendedRegisterClass (RICHED20.8)
2369  *
2370  * FIXME undocumented
2371  * Need to check for errors and implement controls and callbacks 
2372  */
2373 LRESULT WINAPI REExtendedRegisterClass(void)
2374 {
2375   WNDCLASSW wcW;
2376   UINT result;
2377
2378   FIXME("semi stub\n");
2379
2380   wcW.cbClsExtra = 0;
2381   wcW.cbWndExtra = 4;
2382   wcW.hInstance = NULL;
2383   wcW.hIcon = NULL;
2384   wcW.hCursor = NULL;
2385   wcW.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
2386   wcW.lpszMenuName = NULL;
2387
2388   if (!ME_ListBoxRegistered)
2389   {
2390       wcW.style = CS_PARENTDC | CS_DBLCLKS | CS_GLOBALCLASS;
2391       wcW.lpfnWndProc = REListWndProc;
2392       wcW.lpszClassName = wszClassNameListBox;
2393       if (RegisterClassW(&wcW)) ME_ListBoxRegistered = TRUE;
2394   }
2395
2396   if (!ME_ComboBoxRegistered)
2397   {
2398       wcW.style = CS_PARENTDC | CS_DBLCLKS | CS_GLOBALCLASS | CS_VREDRAW | CS_HREDRAW;
2399       wcW.lpfnWndProc = REComboWndProc;
2400       wcW.lpszClassName = wszClassNameComboBox;
2401       if (RegisterClassW(&wcW)) ME_ComboBoxRegistered = TRUE;  
2402   }
2403
2404   result = 0;
2405   if (ME_ListBoxRegistered)
2406       result += 1;
2407   if (ME_ComboBoxRegistered)
2408       result += 2;
2409
2410   return result;
2411 }