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