Added a few more Unicode digits from Unicode version 4.1.
[wine] / dlls / riched20 / editor.c
1 /*
2  * RichEdit - functions dealing with editor object
3  *
4  * Copyright 2004 by Krzysztof Foltman
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 /* 
22   API implementation status:
23   
24   Messages (ANSI versions not done yet)
25   - EM_AUTOURLDETECT 2.0
26   + EM_CANPASTE
27   + EM_CANREDO 2.0
28   + EM_CANUNDO
29   + EM_CHARFROMPOS
30   - EM_DISPLAYBAND
31   + EM_EMPTYUNDOBUFFER
32   + EM_EXGETSEL
33   - EM_EXLIMITTEXT
34   + EM_EXLINEFROMCHAR
35   + EM_EXSETSEL
36   + EM_FINDTEXT (only FR_DOWN flag implemented)
37   + EM_FINDTEXTEX (only FR_DOWN flag implemented)
38   - EM_FINDWORDBREAK
39   - EM_FMTLINES
40   - EM_FORMATRANGE
41   - EM_GETAUTOURLDETECT 2.0
42   - EM_GETBIDIOPTIONS 3.0
43   - EM_GETCHARFORMAT (partly done)
44   - EM_GETEDITSTYLE
45   + EM_GETEVENTMASK
46   + EM_GETFIRSTVISIBLELINE (can be optimized if needed)
47   - EM_GETIMECOLOR 1.0asian
48   - EM_GETIMECOMPMODE 2.0
49   - EM_GETIMEOPTIONS 1.0asian
50   - EM_GETIMESTATUS
51   - EM_GETLANGOPTIONS 2.0
52   - EM_GETLIMITTEXT
53   - EM_GETLINE        
54   + EM_GETLINECOUNT   returns number of rows, not of paragraphs
55   + EM_GETMODIFY
56   - EM_GETOLEINTERFACE
57   - EM_GETOPTIONS
58   + EM_GETPARAFORMAT
59   - EM_GETPASSWORDCHAR 2.0
60   - EM_GETPUNCTUATION 1.0asian
61   + EM_GETRECT
62   - EM_GETREDONAME 2.0
63   + EM_GETSEL
64   + EM_GETSELTEXT (ANSI&Unicode)
65   - EM_GETSCROLLPOS 3.0
66 ! - EM_GETTHUMB
67   - EM_GETTEXTEX 2.0
68   + EM_GETTEXTLENGTHEX (GTL_PRECISE unimplemented)
69   - EM_GETTEXTMODE 2.0
70 ? + EM_GETTEXTRANGE (ANSI&Unicode)
71   - EM_GETTYPOGRAPHYOPTIONS 3.0
72   - EM_GETUNDONAME
73   - EM_GETWORDBREAKPROC
74   - EM_GETWORDBREAKPROCEX
75   - EM_GETWORDWRAPMODE 1.0asian
76   + EM_GETZOOM 3.0
77   - EM_HIDESELECTION
78   - EM_LIMITTEXT
79   + EM_LINEFROMCHAR
80   + EM_LINEINDEX
81   + EM_LINELENGTH
82   + EM_LINESCROLL
83   - EM_PASTESPECIAL
84   + EM_POSFROMCHAR
85   + EM_REDO 2.0
86   + EM_REQUESTRESIZE
87   + EM_REPLACESEL (proper style?) ANSI&Unicode
88   - EM_SCROLL
89   - EM_SCROLLCARET
90   - EM_SELECTIONTYPE
91   - EM_SETBIDIOPTIONS 3.0
92   + EM_SETBKGNDCOLOR
93   - EM_SETCHARFORMAT (partly done, no ANSI)
94   - EM_SETEDITSTYLE
95   + EM_SETEVENTMASK (few notifications supported)
96   - EM_SETFONTSIZE
97   - EM_SETIMECOLOR 1.0asian
98   - EM_SETIMEOPTIONS 1.0asian
99   - EM_SETLANGOPTIONS 2.0
100   - EM_SETLIMITTEXT
101   + EM_SETMODIFY (not sure if implementation is correct)
102   - EM_SETOLECALLBACK
103   - EM_SETOPTIONS
104   - EM_SETPALETTE 2.0
105   + EM_SETPARAFORMAT
106   - EM_SETPASSWORDCHAR 2.0
107   - EM_SETPUNCTUATION 1.0asian
108   + EM_SETREADONLY no beep on modification attempt
109   + EM_SETRECT
110   + EM_SETRECTNP (EM_SETRECT without repainting)
111   + EM_SETSEL
112   - EM_SETSCROLLPOS 3.0
113   - EM_SETTABSTOPS 3.0
114   - EM_SETTARGETDEVICE
115   + EM_SETTEXTEX 3.0 (unicode only, no rich text insertion handling, proper style?)
116   - EM_SETTEXTMODE 2.0
117   - EM_SETTYPOGRAPHYOPTIONS 3.0
118   - EM_SETUNDOLIMIT 2.0
119   - EM_SETWORDBREAKPROC
120   - EM_SETWORDBREAKPROCEX
121   - EM_SETWORDWRAPMODE 1.0asian
122   + EM_SETZOOM 3.0
123   - EM_SHOWSCROLLBAR 2.0
124   - EM_STOPGROUPTYPING 2.0
125   + EM_STREAMIN
126   + EM_STREAMOUT
127   + EM_UNDO
128   + WM_CHAR
129   + WM_CLEAR
130   + WM_COPY
131   + WM_CUT
132   + WM_GETDLGCODE (the current implementation is incomplete)
133   + WM_GETTEXT (ANSI&Unicode)
134   + WM_GETTEXTLENGTH (ANSI version sucks)
135   + WM_PASTE
136   - WM_SETFONT
137   + WM_SETTEXT (resets undo stack !) (proper style?) ANSI&Unicode
138   - WM_STYLECHANGING
139   - WM_STYLECHANGED (things like read-only flag)
140   - WM_UNICHAR
141   
142   Notifications
143   
144   * EN_CHANGE (sent from the wrong place)
145   - EN_CORRECTTEXT
146   - EN_DROPFILES
147   - EN_ERRSPACE
148   - EN_HSCROLL
149   - EN_IMECHANGE
150   + EN_KILLFOCUS
151   - EN_LINK
152   - EN_MAXTEXT
153   - EN_MSGFILTER
154   - EN_OLEOPFAILED
155   - EN_PROTECTED
156   + EN_REQUESTRESIZE
157   - EN_SAVECLIPBOARD
158   + EN_SELCHANGE 
159   + EN_SETFOCUS
160   - EN_STOPNOUNDO
161   * EN_UPDATE (sent from the wrong place)
162   - EN_VSCROLL
163   
164   Styles
165   
166   - ES_AUTOHSCROLL
167   - ES_AUTOVSCROLL
168   - ES_CENTER
169   - ES_DISABLENOSCROLL (scrollbar is always visible)
170   - ES_EX_NOCALLOLEINIT
171   - ES_LEFT
172   - ES_MULTILINE (currently single line controls aren't supported)
173   - ES_NOIME
174   - ES_READONLY (I'm not sure if beeping is the proper behaviour)
175   - ES_RIGHT
176   - ES_SAVESEL
177   - ES_SELFIME
178   - ES_SUNKEN
179   - ES_VERTICAL
180   - ES_WANTRETURN (don't know how to do WM_GETDLGCODE part)
181   - WS_SETFONT
182   - WS_HSCROLL
183   - WS_VSCROLL
184 */
185
186 /*
187  * RICHED20 TODO (incomplete):
188  *
189  * - messages/styles/notifications listed above 
190  * - Undo coalescing 
191  * - add remaining CHARFORMAT/PARAFORMAT fields
192  * - right/center align should strip spaces from the beginning
193  * - more advanced navigation (Ctrl-arrows)
194  * - tabs
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  *
212  * Bugs that are probably fixed, but not so easy to verify:
213  * - EN_UPDATE/EN_CHANGE are handled very incorrectly (should be OK now)
214  * - undo for ME_JoinParagraphs doesn't store paragraph format ? (it does)
215  * - check/fix artificial EOL logic (bCursorAtEnd, hardly logical)
216  * - caret shouldn't be displayed when selection isn't empty
217  * - check refcounting in style management functions (looks perfect now, but no bugs is suspicious)
218  * - undo for setting default format (done, might be buggy)
219  * - styles might be not released properly (looks like they work like charm, but who knows?
220  *
221  */
222
223 #include "editor.h"
224 #include "commdlg.h"
225 #include "ole2.h"
226 #include "richole.h"
227 #include "winreg.h"
228 #define NO_SHLWAPI_STREAM 
229 #include "shlwapi.h"
230
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 ME_TextBuffer *ME_MakeText(void) {
239   
240   ME_TextBuffer *buf = ALLOC_OBJ(ME_TextBuffer);
241
242   ME_DisplayItem *p1 = ME_MakeDI(diTextStart);
243   ME_DisplayItem *p2 = ME_MakeDI(diTextEnd);
244   
245   p1->prev = NULL;
246   p1->next = p2;
247   p2->prev = p1;
248   p2->next = NULL;
249   p1->member.para.next_para = p2;
250   p2->member.para.prev_para = p1;
251   p2->member.para.nCharOfs = 0;  
252   
253   buf->pFirst = p1;
254   buf->pLast = p2;
255   buf->pCharStyle = NULL;
256   
257   return buf;
258 }
259
260
261 static LRESULT ME_StreamInText(ME_TextEditor *editor, DWORD dwFormat, ME_InStream *stream, ME_Style *style)
262 {
263   WCHAR wszText[STREAMIN_BUFFER_SIZE+1];
264   WCHAR *pText;
265   
266   TRACE("%08lx %p\n", dwFormat, stream);
267   
268   do {
269     long nWideChars = 0;
270
271     if (!stream->dwSize)
272     {
273       ME_StreamInFill(stream);
274       if (stream->editstream->dwError)
275         break;
276       if (!stream->dwSize)
277         break;
278     }
279       
280     if (!(dwFormat & SF_UNICODE))
281     {
282       /* FIXME? this is doomed to fail on true MBCS like UTF-8, luckily they're unlikely to be used as CP_ACP */
283       nWideChars = MultiByteToWideChar(CP_ACP, 0, stream->buffer, stream->dwSize, wszText, STREAMIN_BUFFER_SIZE);
284       pText = wszText;
285     }
286     else
287     {
288       nWideChars = stream->dwSize >> 1;
289       pText = (WCHAR *)stream->buffer;
290     }
291     
292     ME_InsertTextFromCursor(editor, 0, pText, nWideChars, style);
293     if (stream->dwSize == 0)
294       break;
295     stream->dwSize = 0;
296   } while(1);
297   ME_CommitUndo(editor);
298   ME_Repaint(editor);
299   return 0;
300 }
301
302 static void ME_RTFCharAttrHook(RTF_Info *info)
303 {
304   CHARFORMAT2W fmt;
305   fmt.cbSize = sizeof(fmt);
306   fmt.dwMask = 0;
307   
308   switch(info->rtfMinor)
309   {
310     case rtfPlain:
311       /* FIXME add more flags once they're implemented */
312       fmt.dwMask = CFM_BOLD | CFM_ITALIC | CFM_UNDERLINE | CFM_STRIKEOUT | CFM_COLOR | CFM_BACKCOLOR | CFM_SIZE | CFM_WEIGHT;
313       fmt.dwEffects = CFE_AUTOCOLOR | CFE_AUTOBACKCOLOR;
314       fmt.yHeight = 12*20; /* 12pt */
315       fmt.wWeight = 400;
316       break;
317     case rtfBold:
318       fmt.dwMask = CFM_BOLD;
319       fmt.dwEffects = info->rtfParam ? fmt.dwMask : 0;
320       break;
321     case rtfItalic:
322       fmt.dwMask = CFM_ITALIC;
323       fmt.dwEffects = info->rtfParam ? fmt.dwMask : 0;
324       break;
325     case rtfUnderline:
326       fmt.dwMask = CFM_UNDERLINE;
327       fmt.dwEffects = info->rtfParam ? fmt.dwMask : 0;
328       fmt.bUnderlineType = CFU_CF1UNDERLINE;
329       break;
330     case rtfNoUnderline:
331       fmt.dwMask = CFM_UNDERLINE;
332       fmt.dwEffects = 0;
333       break;
334     case rtfStrikeThru:
335       fmt.dwMask = CFM_STRIKEOUT;
336       fmt.dwEffects = info->rtfParam ? fmt.dwMask : 0;
337       break;
338     case rtfSubScript:
339     case rtfSuperScript:
340     case rtfSubScrShrink:
341     case rtfSuperScrShrink:
342     case rtfNoSuperSub:
343       fmt.dwMask = CFM_SUBSCRIPT|CFM_SUPERSCRIPT;
344       if (info->rtfMinor == rtfSubScrShrink) fmt.dwEffects = CFE_SUBSCRIPT;
345       if (info->rtfMinor == rtfSuperScrShrink) fmt.dwEffects = CFE_SUPERSCRIPT;
346       if (info->rtfMinor == rtfNoSuperSub) fmt.dwEffects = 0;
347       break;
348     case rtfBackColor:
349       fmt.dwMask = CFM_BACKCOLOR;
350       fmt.dwEffects = 0;
351       if (info->rtfParam == 0)
352         fmt.dwEffects = CFE_AUTOBACKCOLOR;
353       else if (info->rtfParam != rtfNoParam)
354       {
355         RTFColor *c = RTFGetColor(info, info->rtfParam);
356         fmt.crTextColor = (c->rtfCBlue<<16)|(c->rtfCGreen<<8)|(c->rtfCRed);
357       }
358       break;
359     case rtfForeColor:
360       fmt.dwMask = CFM_COLOR;
361       fmt.dwEffects = 0;
362       if (info->rtfParam == 0)
363         fmt.dwEffects = CFE_AUTOCOLOR;
364       else if (info->rtfParam != rtfNoParam)
365       {
366         RTFColor *c = RTFGetColor(info, info->rtfParam);
367         fmt.crTextColor = (c->rtfCBlue<<16)|(c->rtfCGreen<<8)|(c->rtfCRed);
368       }
369       break;
370     case rtfFontNum:
371       if (info->rtfParam != rtfNoParam)
372       {
373         RTFFont *f = RTFGetFont(info, info->rtfParam);
374         if (f)
375         {
376           MultiByteToWideChar(CP_ACP, 0, f->rtfFName, -1, fmt.szFaceName, sizeof(fmt.szFaceName)/sizeof(WCHAR));
377           fmt.szFaceName[sizeof(fmt.szFaceName)/sizeof(WCHAR)-1] = '\0';
378           fmt.bCharSet = f->rtfFCharSet;
379           fmt.dwMask = CFM_FACE | CFM_CHARSET;
380         }
381       }
382       break;
383     case rtfFontSize:
384       fmt.dwMask = CFM_SIZE;
385       if (info->rtfParam != rtfNoParam)
386         fmt.yHeight = info->rtfParam*10;
387       break;
388   }
389   if (fmt.dwMask) {
390     ME_Style *style2;
391     RTFFlushOutputBuffer(info);
392     /* FIXME too slow ? how come ? */
393     style2 = ME_ApplyStyle(info->style, &fmt);
394     ME_ReleaseStyle(info->style);
395     info->style = style2;
396     info->styleChanged = TRUE;
397   }
398 }
399
400 /* FIXME this function doesn't get any information about context of the RTF tag, which is very bad,
401    the same tags mean different things in different contexts */
402 static void ME_RTFParAttrHook(RTF_Info *info)
403 {
404   PARAFORMAT2 fmt;
405   fmt.cbSize = sizeof(fmt);
406   fmt.dwMask = 0;
407   
408   switch(info->rtfMinor)
409   {
410   case rtfParDef: /* I'm not 100% sure what does it do, but I guess it restores default paragraph attributes */
411     fmt.dwMask = PFM_ALIGNMENT | PFM_TABSTOPS | PFM_OFFSET | PFM_STARTINDENT;
412     fmt.wAlignment = PFA_LEFT;
413     fmt.cTabCount = 0;
414     fmt.dxOffset = fmt.dxStartIndent = 0;
415     break;
416   case rtfFirstIndent:
417     ME_GetSelectionParaFormat(info->editor, &fmt);
418     fmt.dwMask = PFM_STARTINDENT;
419     fmt.dxStartIndent = info->rtfParam + fmt.dxOffset;
420     break;
421   case rtfLeftIndent:
422   {
423     int first, left;
424     ME_GetSelectionParaFormat(info->editor, &fmt);
425     first = fmt.dxStartIndent;
426     left = info->rtfParam;
427     fmt.dwMask = PFM_STARTINDENT|PFM_OFFSET;
428     fmt.dxStartIndent = first + left;
429     fmt.dxOffset = -first;
430     break;
431   }
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
938 BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
939 {
940     TRACE("\n");
941     switch (fdwReason)
942     {
943     case DLL_PROCESS_ATTACH:
944       DisableThreadLibraryCalls(hinstDLL);
945       me_heap = HeapCreate (0, 0x10000, 0);
946       ME_RegisterEditorClass(hinstDLL);
947       break;
948
949     case DLL_PROCESS_DETACH:
950       UnregisterClassW(wszClassName, 0);
951       UnregisterClassW(wszClassName50, 0);
952       UnregisterClassA("RichEdit20A", 0);
953       UnregisterClassA("RichEdit50A", 0);
954       HeapDestroy (me_heap);
955       me_heap = NULL;
956       break;
957     }
958     return TRUE;
959 }
960
961
962 #define UNSUPPORTED_MSG(e) \
963   case e: \
964     FIXME(#e ": stub\n"); \
965     return DefWindowProcW(hWnd, msg, wParam, lParam);
966
967 static const char * const edit_messages[] = {
968   "EM_GETSEL",
969   "EM_SETSEL",
970   "EM_GETRECT",
971   "EM_SETRECT",
972   "EM_SETRECTNP",
973   "EM_SCROLL",
974   "EM_LINESCROLL",
975   "EM_SCROLLCARET",
976   "EM_GETMODIFY",
977   "EM_SETMODIFY",
978   "EM_GETLINECOUNT",
979   "EM_LINEINDEX",
980   "EM_SETHANDLE",
981   "EM_GETHANDLE",
982   "EM_GETTHUMB",
983   "EM_UNKNOWN_BF",
984   "EM_UNKNOWN_C0",
985   "EM_LINELENGTH",
986   "EM_REPLACESEL",
987   "EM_UNKNOWN_C3",
988   "EM_GETLINE",
989   "EM_LIMITTEXT",
990   "EM_CANUNDO",
991   "EM_UNDO",
992   "EM_FMTLINES",
993   "EM_LINEFROMCHAR",
994   "EM_UNKNOWN_CA",
995   "EM_SETTABSTOPS",
996   "EM_SETPASSWORDCHAR",
997   "EM_EMPTYUNDOBUFFER",
998   "EM_GETFIRSTVISIBLELINE",
999   "EM_SETREADONLY",
1000   "EM_SETWORDBREAKPROC",
1001   "EM_GETWORDBREAKPROC",
1002   "EM_GETPASSWORDCHAR",
1003   "EM_SETMARGINS",
1004   "EM_GETMARGINS",
1005   "EM_GETLIMITTEXT",
1006   "EM_POSFROMCHAR",
1007   "EM_CHARFROMPOS"
1008 };
1009
1010 static const char * const richedit_messages[] = {
1011   "EM_CANPASTE",
1012   "EM_DISPLAYBAND",
1013   "EM_EXGETSEL",
1014   "EM_EXLIMITTEXT",
1015   "EM_EXLINEFROMCHAR",
1016   "EM_EXSETSEL",
1017   "EM_FINDTEXT",
1018   "EM_FORMATRANGE",
1019   "EM_GETCHARFORMAT",
1020   "EM_GETEVENTMASK",
1021   "EM_GETOLEINTERFACE",
1022   "EM_GETPARAFORMAT",
1023   "EM_GETSELTEXT",
1024   "EM_HIDESELECTION",
1025   "EM_PASTESPECIAL",
1026   "EM_REQUESTRESIZE",
1027   "EM_SELECTIONTYPE",
1028   "EM_SETBKGNDCOLOR",
1029   "EM_SETCHARFORMAT",
1030   "EM_SETEVENTMASK",
1031   "EM_SETOLECALLBACK",
1032   "EM_SETPARAFORMAT",
1033   "EM_SETTARGETDEVICE",
1034   "EM_STREAMIN",
1035   "EM_STREAMOUT",
1036   "EM_GETTEXTRANGE",
1037   "EM_FINDWORDBREAK",
1038   "EM_SETOPTIONS",
1039   "EM_GETOPTIONS",
1040   "EM_FINDTEXTEX",
1041   "EM_GETWORDBREAKPROCEX",
1042   "EM_SETWORDBREAKPROCEX",
1043   "EM_SETUNDOLIMIT",
1044   "EM_UNKNOWN_USER_83",
1045   "EM_REDO",
1046   "EM_CANREDO",
1047   "EM_GETUNDONAME",
1048   "EM_GETREDONAME",
1049   "EM_STOPGROUPTYPING",
1050   "EM_SETTEXTMODE",
1051   "EM_GETTEXTMODE",
1052   "EM_AUTOURLDETECT",
1053   "EM_GETAUTOURLDETECT",
1054   "EM_SETPALETTE",
1055   "EM_GETTEXTEX",
1056   "EM_GETTEXTLENGTHEX",
1057   "EM_SHOWSCROLLBAR",
1058   "EM_SETTEXTEX",
1059   "EM_UNKNOWN_USER_98",
1060   "EM_UNKNOWN_USER_99",
1061   "EM_SETPUNCTUATION",
1062   "EM_GETPUNCTUATION",
1063   "EM_SETWORDWRAPMODE",
1064   "EM_GETWORDWRAPMODE",
1065   "EM_SETIMECOLOR",
1066   "EM_GETIMECOLOR",
1067   "EM_SETIMEOPTIONS",
1068   "EM_GETIMEOPTIONS",
1069   "EM_CONVPOSITION",
1070   "EM_UNKNOWN_USER_109",
1071   "EM_UNKNOWN_USER_110",
1072   "EM_UNKNOWN_USER_111",
1073   "EM_UNKNOWN_USER_112",
1074   "EM_UNKNOWN_USER_113",
1075   "EM_UNKNOWN_USER_114",
1076   "EM_UNKNOWN_USER_115",
1077   "EM_UNKNOWN_USER_116",
1078   "EM_UNKNOWN_USER_117",
1079   "EM_UNKNOWN_USER_118",
1080   "EM_UNKNOWN_USER_119",
1081   "EM_SETLANGOPTIONS",
1082   "EM_GETLANGOPTIONS",
1083   "EM_GETIMECOMPMODE",
1084   "EM_FINDTEXTW",
1085   "EM_FINDTEXTEXW",
1086   "EM_RECONVERSION",
1087   "EM_SETIMEMODEBIAS",
1088   "EM_GETIMEMODEBIAS"
1089 };
1090
1091 static const char *
1092 get_msg_name(UINT msg)
1093 {
1094   if (msg >= EM_GETSEL && msg <= EM_SETLIMITTEXT)
1095     return edit_messages[msg - EM_GETSEL];
1096   if (msg >= EM_CANPASTE && msg <= EM_GETIMEMODEBIAS)
1097     return richedit_messages[msg - EM_CANPASTE];
1098   return "";
1099 }
1100
1101 /******************************************************************
1102  *        RichEditANSIWndProc (RICHED20.10)
1103  */
1104 LRESULT WINAPI RichEditANSIWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) {
1105   SCROLLINFO si;
1106   ME_TextEditor *editor = (ME_TextEditor *)GetWindowLongW(hWnd, 0);
1107   
1108   TRACE("hWnd %p msg %04x (%s) %08x %08lx\n",
1109         hWnd, msg, get_msg_name(msg), wParam, lParam);
1110   
1111   switch(msg) {
1112   
1113   UNSUPPORTED_MSG(EM_AUTOURLDETECT)
1114   UNSUPPORTED_MSG(EM_DISPLAYBAND)
1115   UNSUPPORTED_MSG(EM_EXLIMITTEXT)
1116   UNSUPPORTED_MSG(EM_FINDWORDBREAK)
1117   UNSUPPORTED_MSG(EM_FMTLINES)
1118   UNSUPPORTED_MSG(EM_FORMATRANGE)
1119   UNSUPPORTED_MSG(EM_GETAUTOURLDETECT)
1120   UNSUPPORTED_MSG(EM_GETBIDIOPTIONS)
1121   UNSUPPORTED_MSG(EM_GETEDITSTYLE)
1122   UNSUPPORTED_MSG(EM_GETIMECOMPMODE)
1123   /* UNSUPPORTED_MSG(EM_GETIMESTATUS) missing in Wine headers */
1124   UNSUPPORTED_MSG(EM_GETLANGOPTIONS)
1125   UNSUPPORTED_MSG(EM_GETLIMITTEXT)
1126   UNSUPPORTED_MSG(EM_GETLINE)
1127   /* UNSUPPORTED_MSG(EM_GETOLEINTERFACE) separate stub */
1128   UNSUPPORTED_MSG(EM_GETOPTIONS)
1129   UNSUPPORTED_MSG(EM_GETPASSWORDCHAR)
1130   UNSUPPORTED_MSG(EM_GETREDONAME)
1131   UNSUPPORTED_MSG(EM_GETSCROLLPOS)
1132   UNSUPPORTED_MSG(EM_GETTEXTMODE)
1133   UNSUPPORTED_MSG(EM_GETTYPOGRAPHYOPTIONS)
1134   UNSUPPORTED_MSG(EM_GETUNDONAME)
1135   UNSUPPORTED_MSG(EM_GETWORDBREAKPROC)
1136   UNSUPPORTED_MSG(EM_GETWORDBREAKPROCEX)
1137   UNSUPPORTED_MSG(EM_HIDESELECTION)
1138   UNSUPPORTED_MSG(EM_LIMITTEXT) /* also known as EM_SETLIMITTEXT */
1139   UNSUPPORTED_MSG(EM_PASTESPECIAL)
1140   UNSUPPORTED_MSG(EM_SCROLL)
1141   UNSUPPORTED_MSG(EM_SCROLLCARET)
1142   UNSUPPORTED_MSG(EM_SELECTIONTYPE)
1143   UNSUPPORTED_MSG(EM_SETBIDIOPTIONS)
1144   UNSUPPORTED_MSG(EM_SETEDITSTYLE)
1145   UNSUPPORTED_MSG(EM_SETFONTSIZE)
1146   UNSUPPORTED_MSG(EM_SETLANGOPTIONS)
1147   UNSUPPORTED_MSG(EM_SETOLECALLBACK)
1148   UNSUPPORTED_MSG(EM_SETOPTIONS)
1149   UNSUPPORTED_MSG(EM_SETPALETTE)
1150   UNSUPPORTED_MSG(EM_SETPASSWORDCHAR)
1151   UNSUPPORTED_MSG(EM_SETSCROLLPOS)
1152   UNSUPPORTED_MSG(EM_SETTABSTOPS)
1153   UNSUPPORTED_MSG(EM_SETTARGETDEVICE)
1154   UNSUPPORTED_MSG(EM_SETTEXTMODE)
1155   UNSUPPORTED_MSG(EM_SETTYPOGRAPHYOPTIONS)
1156   UNSUPPORTED_MSG(EM_SETUNDOLIMIT)
1157   UNSUPPORTED_MSG(EM_SETWORDBREAKPROC)
1158   UNSUPPORTED_MSG(EM_SETWORDBREAKPROCEX)
1159   UNSUPPORTED_MSG(EM_SHOWSCROLLBAR)
1160   UNSUPPORTED_MSG(WM_SETFONT)
1161   UNSUPPORTED_MSG(WM_STYLECHANGING)
1162   UNSUPPORTED_MSG(WM_STYLECHANGED)
1163 /*  UNSUPPORTED_MSG(WM_UNICHAR) FIXME missing in Wine headers */
1164     
1165 /* Messages specific to Richedit controls */
1166   
1167   case EM_STREAMIN:
1168    return ME_StreamIn(editor, wParam, (EDITSTREAM*)lParam);
1169   case EM_STREAMOUT:
1170    return ME_StreamOut(editor, wParam, (EDITSTREAM *)lParam);
1171   case WM_GETDLGCODE:
1172   {
1173     UINT code = DLGC_WANTCHARS|DLGC_WANTARROWS;
1174     if (GetWindowLongW(hWnd, GWL_STYLE)&ES_WANTRETURN)
1175       code |= 0; /* FIXME what can we do here ? ask for messages and censor them ? */
1176     return code;
1177   }
1178   case WM_NCCREATE:
1179   {
1180     CREATESTRUCTW *pcs = (CREATESTRUCTW *)lParam;
1181     TRACE("WM_NCCREATE: style 0x%08lx\n", pcs->style);
1182     editor = ME_MakeEditor(hWnd);
1183     SetWindowLongW(hWnd, 0, (long)editor);
1184     pcs = 0; /* ignore */
1185     return TRUE;
1186   }
1187   case EM_EMPTYUNDOBUFFER:
1188     ME_EmptyUndoStack(editor);
1189     return 0;
1190   case EM_GETSEL:
1191   {
1192     /* Note: wParam/lParam can be NULL */
1193     UINT from, to;
1194     PUINT pfrom = wParam ? (PUINT)wParam : &from;
1195     PUINT pto = lParam ? (PUINT)lParam : &to;
1196     ME_GetSelection(editor, (int *)pfrom, (int *)pto);
1197     if ((*pfrom|*pto) & 0xFFFF0000)
1198       return -1;
1199     return MAKELONG(*pfrom,*pto);
1200   }
1201   case EM_EXGETSEL:
1202   {
1203     CHARRANGE *pRange = (CHARRANGE *)lParam;
1204     ME_GetSelection(editor, (int *)&pRange->cpMin, (int *)&pRange->cpMax);
1205     TRACE("EM_EXGETSEL = (%ld,%ld)\n", pRange->cpMin, pRange->cpMax);
1206     return 0;
1207   }
1208   case EM_CANUNDO:
1209     return editor->pUndoStack != NULL;
1210   case EM_CANREDO:
1211     return editor->pRedoStack != NULL;
1212   case WM_UNDO: /* FIXME: actually not the same */
1213   case EM_UNDO:
1214     ME_Undo(editor);
1215     return 0;
1216   case EM_REDO:
1217     ME_Redo(editor);
1218     return 0;
1219   case EM_SETSEL:
1220   {
1221     ME_SetSelection(editor, wParam, lParam);
1222     ME_Repaint(editor);
1223     ME_SendSelChange(editor);
1224     return 0;
1225   }
1226   case EM_EXSETSEL:
1227   {
1228     CHARRANGE *pRange = (CHARRANGE *)lParam;
1229     TRACE("EM_EXSETSEL (%ld,%ld)\n", pRange->cpMin, pRange->cpMax);
1230     ME_SetSelection(editor, pRange->cpMin, pRange->cpMax);
1231     /* FIXME optimize */
1232     ME_Repaint(editor);
1233     ME_SendSelChange(editor);
1234     return 0;
1235   }
1236   case EM_SETTEXTEX:
1237   {
1238     LPWSTR wszText = (LPWSTR)lParam;
1239     SETTEXTEX *pStruct = (SETTEXTEX *)wParam;
1240     size_t len = lstrlenW(wszText);
1241     int from, to;
1242     ME_Style *style;
1243     TRACE("EM_SETTEXEX - %s, flags %d, cp %d\n", debugstr_w(wszText), (int)pStruct->flags, pStruct->codepage);
1244     if (pStruct->codepage != 1200) {
1245       FIXME("EM_SETTEXTEX only supports unicode right now!\n"); 
1246       return 0;
1247     }
1248     /* FIXME: this should support RTF strings too, according to MSDN */
1249     if (pStruct->flags & ST_SELECTION) {
1250       ME_GetSelection(editor, &from, &to);
1251       style = ME_GetSelectionInsertStyle(editor);
1252       ME_InternalDeleteText(editor, from, to - from);
1253       ME_InsertTextFromCursor(editor, 0, wszText, len, style);
1254       ME_ReleaseStyle(style);
1255     }
1256     else {
1257       ME_InternalDeleteText(editor, 0, ME_GetTextLength(editor));
1258       ME_InsertTextFromCursor(editor, 0, wszText, -1, editor->pBuffer->pDefaultStyle);
1259       len = 1;
1260     }
1261     ME_CommitUndo(editor);
1262     if (!(pStruct->flags & ST_KEEPUNDO))
1263       ME_EmptyUndoStack(editor);
1264     ME_UpdateRepaint(editor);
1265     return len;
1266   }
1267   case EM_SETBKGNDCOLOR:
1268   {
1269     LRESULT lColor = ME_GetBackColor(editor);
1270     if (editor->rgbBackColor != -1)
1271       DeleteObject(editor->hbrBackground);
1272     if (wParam)
1273     {
1274       editor->rgbBackColor = -1;
1275       editor->hbrBackground = GetSysColorBrush(COLOR_WINDOW);
1276     }
1277     else
1278     {
1279       editor->rgbBackColor = lParam;
1280       editor->hbrBackground = CreateSolidBrush(editor->rgbBackColor);
1281     }
1282     if (editor->bRedraw)
1283     {
1284       InvalidateRect(hWnd, NULL, TRUE);
1285       UpdateWindow(hWnd);
1286     }
1287     return lColor;
1288   }
1289   case EM_GETMODIFY:
1290     return editor->nModifyStep == 0 ? 0 : 1;
1291   case EM_SETMODIFY:
1292   {
1293     if (wParam)
1294       editor->nModifyStep = 0x80000000;
1295     else
1296       editor->nModifyStep = 0;
1297     
1298     return 0;
1299   }
1300   case EM_SETREADONLY:
1301   {
1302     long nStyle = GetWindowLongW(hWnd, GWL_STYLE);
1303     if (wParam)
1304       nStyle |= ES_READONLY;
1305     else
1306       nStyle &= ~ES_READONLY;
1307     SetWindowLongW(hWnd, GWL_STYLE, nStyle);
1308     ME_Repaint(editor);
1309     return 0;
1310   }
1311   case EM_SETEVENTMASK:
1312   {
1313     DWORD nOldMask = editor->nEventMask;
1314     
1315     editor->nEventMask = lParam;
1316     return nOldMask;
1317   }
1318   case EM_GETEVENTMASK:
1319     return editor->nEventMask;
1320   case EM_SETCHARFORMAT:
1321   {
1322     CHARFORMAT2W buf, *p;
1323     BOOL bRepaint = TRUE;
1324     p = ME_ToCF2W(&buf, (CHARFORMAT2W *)lParam);
1325     if (!wParam)
1326       ME_SetDefaultCharFormat(editor, p);
1327     else if (wParam == (SCF_WORD | SCF_SELECTION))
1328       FIXME("EM_SETCHARFORMAT: word selection not supported\n");
1329     else if (wParam == SCF_ALL)
1330       ME_SetCharFormat(editor, 0, ME_GetTextLength(editor), p);
1331     else {
1332       int from, to;
1333       ME_GetSelection(editor, &from, &to);
1334       bRepaint = (from != to);
1335       ME_SetSelectionCharFormat(editor, p);
1336     }
1337     ME_CommitUndo(editor);
1338     if (bRepaint)
1339       ME_UpdateRepaint(editor);
1340     return 0;
1341   }
1342   case EM_GETCHARFORMAT:
1343   {
1344     CHARFORMAT2W tmp, *dst = (CHARFORMAT2W *)lParam;
1345     if (dst->cbSize != sizeof(CHARFORMATA) &&
1346         dst->cbSize != sizeof(CHARFORMATW) &&
1347         dst->cbSize != sizeof(CHARFORMAT2A) &&
1348         dst->cbSize != sizeof(CHARFORMAT2W))
1349       return 0;
1350     tmp.cbSize = sizeof(tmp);
1351     if (!wParam)
1352       ME_GetDefaultCharFormat(editor, &tmp);
1353     else
1354       ME_GetSelectionCharFormat(editor, &tmp);
1355     ME_CopyToCFAny(dst, &tmp);
1356     return tmp.dwMask;
1357   }
1358   case EM_SETPARAFORMAT:
1359     ME_SetSelectionParaFormat(editor, (PARAFORMAT2 *)lParam);
1360     ME_UpdateRepaint(editor);
1361     ME_CommitUndo(editor);
1362     return 0;
1363   case EM_GETPARAFORMAT:
1364     ME_GetSelectionParaFormat(editor, (PARAFORMAT2 *)lParam);
1365     return 0;
1366   case EM_GETFIRSTVISIBLELINE:
1367   {
1368     ME_DisplayItem *p = editor->pBuffer->pFirst;
1369     int y = editor->nScrollPosY;
1370     int ypara = 0;
1371     int count = 0;
1372     int ystart, yend;
1373     while(p) {
1374       p = ME_FindItemFwd(p, diStartRowOrParagraphOrEnd);
1375       if (p->type == diTextEnd)
1376         break;
1377       if (p->type == diParagraph) {
1378         ypara = p->member.para.nYPos;
1379         continue;
1380       }
1381       ystart = ypara + p->member.row.nYPos;
1382       yend = ystart + p->member.row.nHeight;
1383       if (y < yend) {
1384         break;
1385       }
1386       count++;
1387     }
1388     return count;
1389   }
1390   case EM_LINESCROLL:
1391   {
1392     int nPos = editor->nScrollPosY, nEnd= editor->nTotalLength - editor->sizeWindow.cy;
1393     nPos += 8 * lParam; /* FIXME follow the original */
1394     if (nPos>=nEnd)
1395       nPos = nEnd;
1396     if (nPos<0)
1397       nPos = 0;
1398     if (nPos != editor->nScrollPosY) {
1399       int dy = editor->nScrollPosY - nPos;
1400       editor->nScrollPosY = nPos;
1401       SetScrollPos(hWnd, SB_VERT, nPos, TRUE);
1402       if (editor->bRedraw)
1403       {
1404         ScrollWindow(hWnd, 0, dy, NULL, NULL);
1405         UpdateWindow(hWnd);
1406       }
1407     }
1408     return TRUE; /* Should return false if a single line richedit control */
1409   }
1410   case WM_CLEAR:
1411   {
1412     int from, to;
1413     ME_GetSelection(editor, &from, &to);
1414     ME_InternalDeleteText(editor, from, to-from);
1415     ME_CommitUndo(editor);
1416     ME_UpdateRepaint(editor);
1417     return 0;
1418   }
1419   case EM_REPLACESEL:
1420   {
1421     int from, to;
1422     ME_Style *style;
1423     LPWSTR wszText = ME_ToUnicode(hWnd, (void *)lParam);
1424     size_t len = lstrlenW(wszText);
1425     TRACE("EM_REPLACESEL - %s\n", debugstr_w(wszText));
1426     
1427     ME_GetSelection(editor, &from, &to);
1428     style = ME_GetSelectionInsertStyle(editor);
1429     ME_InternalDeleteText(editor, from, to-from);
1430     ME_InsertTextFromCursor(editor, 0, wszText, len, style);
1431     ME_ReleaseStyle(style);
1432     ME_EndToUnicode(hWnd, wszText);
1433     /* drop temporary style if line end */
1434     /* FIXME question: does abc\n mean: put abc, clear temp style, put \n? (would require a change) */  
1435     if (len>0 && wszText[len-1] == '\n')
1436       ME_ClearTempStyle(editor);
1437       
1438     ME_CommitUndo(editor);
1439     if (!wParam)
1440       ME_EmptyUndoStack(editor);
1441     ME_UpdateRepaint(editor);
1442     return 0;
1443   }
1444   case WM_SETTEXT:
1445   {
1446     ME_InternalDeleteText(editor, 0, ME_GetTextLength(editor));
1447     if (lParam)
1448     {
1449       LPWSTR wszText = ME_ToUnicode(hWnd, (void *)lParam);
1450       TRACE("WM_SETTEXT lParam==%lx\n",lParam);
1451       TRACE("WM_SETTEXT - %s\n", debugstr_w(wszText)); /* debugstr_w() */
1452       if (lstrlenW(wszText) > 0)
1453       {
1454         /* uses default style! */
1455         ME_InsertTextFromCursor(editor, 0, wszText, -1, editor->pBuffer->pDefaultStyle);
1456       }
1457       ME_EndToUnicode(hWnd, wszText);
1458     }
1459     else
1460       TRACE("WM_SETTEXT - NULL\n");
1461     ME_CommitUndo(editor);
1462     ME_EmptyUndoStack(editor);
1463     ME_UpdateRepaint(editor);
1464     return 0;
1465   }
1466   case EM_CANPASTE:
1467   {
1468     UINT nRTFFormat = RegisterClipboardFormatA("Rich Text Format");
1469     if (IsClipboardFormatAvailable(nRTFFormat))
1470       return TRUE;
1471     if (IsClipboardFormatAvailable(CF_UNICODETEXT))
1472       return TRUE;
1473     return FALSE;
1474   }
1475   case WM_PASTE:
1476   {    
1477     DWORD dwFormat = 0;
1478     EDITSTREAM es;
1479     ME_GlobalDestStruct gds;
1480     UINT nRTFFormat = RegisterClipboardFormatA("Rich Text Format");
1481     UINT cf = 0;
1482
1483     if (IsClipboardFormatAvailable(nRTFFormat))
1484       cf = nRTFFormat, dwFormat = SF_RTF;
1485     else if (IsClipboardFormatAvailable(CF_UNICODETEXT))
1486       cf = CF_UNICODETEXT, dwFormat = SF_TEXT|SF_UNICODE;
1487     else
1488       return 0;
1489
1490     if (!OpenClipboard(hWnd))
1491       return 0;
1492     gds.hData = GetClipboardData(cf);
1493     gds.nLength = 0;
1494     es.dwCookie = (DWORD)&gds;
1495     es.pfnCallback = dwFormat == SF_RTF ? ME_ReadFromHGLOBALRTF : ME_ReadFromHGLOBALUnicode;
1496     SendMessageW(hWnd, EM_STREAMIN, dwFormat|SFF_SELECTION, (LPARAM)&es);
1497     
1498     CloseClipboard();
1499     return 0;
1500   }
1501   case WM_CUT:
1502   case WM_COPY:
1503   {
1504     int from, to, pars;
1505     WCHAR *data;
1506     HANDLE hData;
1507     EDITSTREAM es;
1508     ME_GlobalDestStruct gds;
1509     
1510     if (!OpenClipboard(hWnd))
1511       return 0;
1512       
1513     EmptyClipboard();
1514     ME_GetSelection(editor, &from, &to);
1515     pars = ME_CountParagraphsBetween(editor, from, to);
1516     hData = GlobalAlloc(GMEM_MOVEABLE, sizeof(WCHAR)*(to-from+pars+1));
1517     data = (WCHAR *)GlobalLock(hData);
1518     ME_GetTextW(editor, data, from, to-from, TRUE);
1519     GlobalUnlock(hData);
1520
1521     gds.hData = GlobalAlloc(GMEM_MOVEABLE, 0);
1522     gds.nLength = 0;
1523     es.dwCookie = (DWORD)&gds;
1524     es.pfnCallback = ME_AppendToHGLOBAL;
1525     SendMessageW(hWnd, EM_STREAMOUT, SFF_SELECTION|SF_RTF, (LPARAM)&es);
1526     GlobalReAlloc(gds.hData, gds.nLength+1, 0);
1527     
1528     SetClipboardData(CF_UNICODETEXT, hData);    
1529     SetClipboardData(RegisterClipboardFormatA("Rich Text Format"), gds.hData);
1530     
1531     CloseClipboard();
1532     if (msg == WM_CUT)
1533     {
1534       ME_InternalDeleteText(editor, from, to-from);
1535       ME_CommitUndo(editor);
1536       ME_UpdateRepaint(editor);
1537     }
1538     return 0;
1539   }
1540   case WM_GETTEXTLENGTH:
1541     return ME_GetTextLength(editor);
1542   case EM_GETTEXTLENGTHEX:
1543     return ME_GetTextLengthEx(editor, (GETTEXTLENGTHEX *)wParam);
1544   case WM_GETTEXT:
1545   {
1546     TEXTRANGEW tr; /* W and A differ only by rng->lpstrText */
1547     tr.chrg.cpMin = 0;
1548     tr.chrg.cpMax = wParam-1;
1549     tr.lpstrText = (WCHAR *)lParam;
1550     return RichEditANSIWndProc(hWnd, EM_GETTEXTRANGE, 0, (LPARAM)&tr);
1551   }
1552   case EM_GETTEXTEX:
1553   {
1554     GETTEXTEX *ex = (GETTEXTEX*)wParam;
1555     int nStart, nCount;
1556
1557     if (ex->flags & ~(GT_SELECTION | GT_USECRLF))
1558       FIXME("GETTEXTEX flags 0x%08lx not supported\n", ex->flags & ~(GT_SELECTION | GT_USECRLF));
1559
1560     if (ex->flags & GT_SELECTION)
1561     {
1562       ME_GetSelection(editor, &nStart, &nCount);
1563       nCount -= nStart;
1564       nCount = min(nCount, ex->cb - 1);
1565     }
1566     else
1567     {
1568       nStart = 0;
1569       nCount = ex->cb - 1;
1570     }
1571     if (ex->codepage == 1200 || IsWindowUnicode(hWnd))
1572     {
1573       nCount = min(nCount, ex->cb / sizeof(WCHAR) - 1);
1574       return ME_GetTextW(editor, (LPWSTR)lParam, nStart, nCount, ex->flags & GT_USECRLF);
1575     }
1576     else
1577     {
1578       /* potentially each char may be a CR, why calculate the exact value with O(N) when
1579         we can just take a bigger buffer? :) */
1580       int crlfmul = (ex->flags & GT_USECRLF) ? 2 : 1;
1581       LPWSTR buffer = HeapAlloc(GetProcessHeap(), 0, (crlfmul*nCount + 1) * sizeof(WCHAR));
1582       DWORD buflen = ex->cb;
1583       LRESULT rc;
1584       DWORD flags = 0;
1585
1586       buflen = ME_GetTextW(editor, buffer, nStart, nCount, ex->flags & GT_USECRLF);
1587       rc = WideCharToMultiByte(ex->codepage, flags, buffer, buflen, (LPSTR)lParam, ex->cb, ex->lpDefaultChar, ex->lpUsedDefaultChar);
1588
1589       HeapFree(GetProcessHeap(),0,buffer);
1590       return rc;
1591     }
1592   }
1593   case EM_GETSELTEXT:
1594   {
1595     int from, to;
1596     TEXTRANGEW tr; /* W and A differ only by rng->lpstrText */
1597     ME_GetSelection(editor, &from, &to);
1598     tr.chrg.cpMin = from;
1599     tr.chrg.cpMax = to;
1600     tr.lpstrText = (WCHAR *)lParam;
1601     return RichEditANSIWndProc(hWnd, EM_GETTEXTRANGE, 0, (LPARAM)&tr);
1602   }
1603   case EM_GETTEXTRANGE:
1604   {
1605     TEXTRANGEW *rng = (TEXTRANGEW *)lParam;
1606     TRACE("EM_GETTEXTRANGE min=%ld max=%ld unicode=%d emul1.0=%d length=%d\n",
1607       rng->chrg.cpMin, rng->chrg.cpMax, IsWindowUnicode(hWnd), 
1608       editor->bEmulateVersion10, ME_GetTextLength(editor));
1609     if (IsWindowUnicode(hWnd))
1610       return ME_GetTextW(editor, rng->lpstrText, rng->chrg.cpMin, rng->chrg.cpMax-rng->chrg.cpMin, editor->bEmulateVersion10);
1611     else
1612     {
1613       int nLen = rng->chrg.cpMax-rng->chrg.cpMin;
1614       WCHAR *p = ALLOC_N_OBJ(WCHAR, nLen+1);
1615       int nChars = ME_GetTextW(editor, p, rng->chrg.cpMin, nLen, editor->bEmulateVersion10);
1616       /* FIXME this is a potential security hole (buffer overrun) 
1617          if you know more about wchar->mbyte conversion please explain
1618       */
1619       WideCharToMultiByte(CP_ACP, 0, p, nChars+1, (char *)rng->lpstrText, nLen+1, NULL, NULL);
1620       FREE_OBJ(p);
1621       return nChars;
1622     }
1623   }
1624   case EM_GETLINECOUNT:
1625   {
1626     ME_DisplayItem *item = editor->pBuffer->pFirst->next;
1627     int nRows = 0;
1628
1629     while (item != editor->pBuffer->pLast)
1630     {
1631       assert(item->type == diParagraph);
1632       nRows += item->member.para.nRows;
1633       item = item->member.para.next_para;
1634     }
1635     TRACE("EM_GETLINECOUNT: nRows==%d\n", nRows);
1636     return max(1, nRows);
1637   }
1638   case EM_LINEFROMCHAR:
1639   {
1640     if (wParam == -1)
1641       return ME_RowNumberFromCharOfs(editor, ME_GetCursorOfs(editor, 1));
1642     else
1643       return ME_RowNumberFromCharOfs(editor, wParam);
1644   }
1645   case EM_EXLINEFROMCHAR:
1646   {
1647     return ME_RowNumberFromCharOfs(editor, lParam);
1648   }
1649   case EM_LINEINDEX:
1650   {
1651     ME_DisplayItem *item, *para;
1652     int nCharOfs;
1653     
1654     if (wParam == -1)
1655       item = ME_FindItemBack(editor->pCursors[0].pRun, diStartRow);
1656     else
1657       item = ME_FindRowWithNumber(editor, wParam);
1658     if (!item)
1659       return -1;
1660     para = ME_GetParagraph(item);
1661     item = ME_FindItemFwd(item, diRun);
1662     nCharOfs = para->member.para.nCharOfs + item->member.run.nCharOfs;
1663     TRACE("EM_LINEINDEX: nCharOfs==%d\n", nCharOfs);
1664     return nCharOfs;
1665   }
1666   case EM_LINELENGTH:
1667   {
1668     ME_DisplayItem *item, *item_end;
1669     int nChars = 0, nThisLineOfs = 0, nNextLineOfs = 0;
1670     
1671     if (wParam > ME_GetTextLength(editor))
1672       return 0;
1673     if (wParam == -1)
1674     {
1675       FIXME("EM_LINELENGTH: returning number of unselected characters on lines with selection unsupported.\n");
1676       return 0;
1677     }
1678     item = ME_FindItemAtOffset(editor, diRun, wParam, NULL);
1679     item = ME_RowStart(item);
1680     nThisLineOfs = ME_CharOfsFromRunOfs(editor, ME_FindItemFwd(item, diRun), 0);
1681     item_end = ME_FindItemFwd(item, diStartRow);
1682     if (item_end)
1683       nNextLineOfs = ME_CharOfsFromRunOfs(editor, ME_FindItemFwd(item_end, diRun), 0);
1684     else
1685       nNextLineOfs = ME_FindItemFwd(item, diParagraphOrEnd)->member.para.nCharOfs
1686        - (editor->bEmulateVersion10?2:1);
1687     nChars = nNextLineOfs - nThisLineOfs;
1688     TRACE("EM_LINELENGTH(%d)==%d\n",wParam, nChars);
1689     return nChars;
1690   }
1691   case EM_FINDTEXT:
1692   {
1693     FINDTEXTA *ft = (FINDTEXTA *)lParam;
1694     int nChars = MultiByteToWideChar(CP_ACP, 0, ft->lpstrText, -1, NULL, 0);
1695     WCHAR *tmp;
1696     
1697     if ((tmp = ALLOC_N_OBJ(WCHAR, nChars)) != NULL)
1698       MultiByteToWideChar(CP_ACP, 0, ft->lpstrText, -1, tmp, nChars);
1699     return ME_FindText(editor, wParam, &ft->chrg, tmp, NULL);
1700   }
1701   case EM_FINDTEXTEX:
1702   {
1703     FINDTEXTEXA *ex = (FINDTEXTEXA *)lParam;
1704     int nChars = MultiByteToWideChar(CP_ACP, 0, ex->lpstrText, -1, NULL, 0);
1705     WCHAR *tmp;
1706     
1707     if ((tmp = ALLOC_N_OBJ(WCHAR, nChars)) != NULL)
1708       MultiByteToWideChar(CP_ACP, 0, ex->lpstrText, -1, tmp, nChars);
1709     return ME_FindText(editor, wParam, &ex->chrg, tmp, &ex->chrgText);
1710   }
1711   case EM_FINDTEXTW:
1712   {
1713     FINDTEXTW *ft = (FINDTEXTW *)lParam;
1714     return ME_FindText(editor, wParam, &ft->chrg, ft->lpstrText, NULL);
1715   }
1716   case EM_FINDTEXTEXW:
1717   {
1718     FINDTEXTEXW *ex = (FINDTEXTEXW *)lParam;
1719     return ME_FindText(editor, wParam, &ex->chrg, ex->lpstrText, &ex->chrgText);
1720   }
1721   case EM_GETZOOM:
1722     if (!wParam || !lParam)
1723       return FALSE;
1724     *(int *)wParam = editor->nZoomNumerator;
1725     *(int *)lParam = editor->nZoomDenominator;
1726     return TRUE;
1727   case EM_SETZOOM:
1728     return ME_SetZoom(editor, wParam, lParam);
1729   case EM_CHARFROMPOS:
1730     return ME_CharFromPos(editor, ((POINTL *)lParam)->x, ((POINTL *)lParam)->y);
1731   case EM_POSFROMCHAR:
1732   {
1733     ME_DisplayItem *pRun;
1734     int nCharOfs, nOffset, nLength;
1735     POINTL pt = {0,0};
1736     
1737     nCharOfs = wParam; 
1738     /* detect which API version we're dealing with */
1739     if (wParam >= 0x40000)
1740         nCharOfs = lParam;
1741     nLength = ME_GetTextLength(editor);
1742     
1743     if (nCharOfs < nLength) { 
1744         ME_RunOfsFromCharOfs(editor, nCharOfs, &pRun, &nOffset);
1745         assert(pRun->type == diRun);
1746         pt.y = pRun->member.run.pt.y;
1747         pt.x = pRun->member.run.pt.x + ME_PointFromChar(editor, &pRun->member.run, nOffset);
1748         pt.y += ME_GetParagraph(pRun)->member.para.nYPos;
1749     } else {
1750         pt.x = 0;
1751         pt.y = editor->pBuffer->pLast->member.para.nYPos;
1752     }
1753     if (wParam >= 0x40000) {
1754         *(POINTL *)wParam = pt;
1755     }
1756     return MAKELONG( pt.x, pt.y );
1757   }
1758   case WM_CREATE:
1759     ME_CommitUndo(editor);
1760     ME_WrapMarkedParagraphs(editor);
1761     ME_MoveCaret(editor);
1762     return 0;
1763   case WM_DESTROY:
1764     ME_DestroyEditor(editor);
1765     SetWindowLongW(hWnd, 0, 0);
1766     return 0;
1767   case WM_LBUTTONDOWN:
1768     SetFocus(hWnd);
1769     ME_LButtonDown(editor, (short)LOWORD(lParam), (short)HIWORD(lParam));
1770     SetCapture(hWnd);
1771     break;
1772   case WM_MOUSEMOVE:
1773     if (GetCapture() == hWnd)
1774       ME_MouseMove(editor, (short)LOWORD(lParam), (short)HIWORD(lParam));
1775     break;
1776   case WM_LBUTTONUP:
1777     if (GetCapture() == hWnd)
1778       ReleaseCapture();
1779     break;
1780   case WM_PAINT:
1781     if (editor->bRedraw)
1782     {
1783       HDC hDC;
1784       PAINTSTRUCT ps;
1785
1786       hDC = BeginPaint(hWnd, &ps);
1787       ME_PaintContent(editor, hDC, FALSE, &ps.rcPaint);
1788       EndPaint(hWnd, &ps);
1789     }
1790     break;
1791   case WM_SETFOCUS:
1792     ME_ShowCaret(editor);
1793     ME_SendOldNotify(editor, EN_SETFOCUS);
1794     return 0;
1795   case WM_KILLFOCUS:
1796     ME_HideCaret(editor);
1797     ME_SendOldNotify(editor, EN_KILLFOCUS);
1798     return 0;
1799   case WM_ERASEBKGND:
1800   {
1801     if (editor->bRedraw)
1802     {
1803       HDC hDC = (HDC)wParam;
1804       RECT rc;
1805       if (GetUpdateRect(hWnd,&rc,TRUE))
1806       {
1807         FillRect(hDC, &rc, editor->hbrBackground);
1808       }
1809     }
1810     return 1;
1811   }
1812   case WM_COMMAND:
1813     TRACE("editor wnd command = %d\n", LOWORD(wParam));
1814     return 0;
1815   case WM_KEYDOWN:
1816     if (ME_ArrowKey(editor, LOWORD(wParam), GetKeyState(VK_CONTROL)<0)) {
1817       ME_CommitUndo(editor);
1818       ME_EnsureVisible(editor, editor->pCursors[0].pRun);
1819       HideCaret(hWnd);
1820       ME_MoveCaret(editor);
1821       ShowCaret(hWnd);
1822       return 0;
1823     }
1824     if (GetKeyState(VK_CONTROL)<0)
1825     {
1826       if (LOWORD(wParam)=='W')
1827       {
1828         CHARFORMAT2W chf;
1829         char buf[2048];
1830         ME_GetSelectionCharFormat(editor, &chf);
1831         ME_DumpStyleToBuf(&chf, buf);
1832         MessageBoxA(NULL, buf, "Style dump", MB_OK);
1833       }
1834       if (LOWORD(wParam)=='Q')
1835       {
1836         ME_CheckCharOffsets(editor);
1837       }
1838     }
1839     goto do_default;
1840   case WM_CHAR: 
1841   {
1842     WCHAR wstr = LOWORD(wParam);
1843
1844     switch (wstr)
1845     {
1846     case 3: /* Ctrl-C */
1847       SendMessageW(editor->hWnd, WM_COPY, 0, 0);
1848       return 0;
1849     }
1850     
1851     if (GetWindowLongW(editor->hWnd, GWL_STYLE) & ES_READONLY) {
1852       MessageBeep(MB_ICONERROR);
1853       return 0; /* FIXME really 0 ? */
1854     }
1855
1856     switch (wstr)
1857     {
1858     case 1: /* Ctrl-A */
1859       ME_SetSelection(editor, 0, -1);
1860       return 0;
1861     case 22: /* Ctrl-V */
1862       SendMessageW(editor->hWnd, WM_PASTE, 0, 0);
1863       return 0;
1864     case 24: /* Ctrl-X */
1865       SendMessageW(editor->hWnd, WM_CUT, 0, 0);
1866       return 0;
1867     case 25: /* Ctrl-Y */
1868       SendMessageW(editor->hWnd, EM_REDO, 0, 0);
1869       return 0;
1870     case 26: /* Ctrl-Z */
1871       SendMessageW(editor->hWnd, EM_UNDO, 0, 0);
1872       return 0;
1873     }
1874     if (((unsigned)wstr)>=' ' || wstr=='\r' || wstr=='\t') {
1875       /* FIXME maybe it would make sense to call EM_REPLACESEL instead ? */
1876       ME_Style *style = ME_GetInsertStyle(editor, 0);
1877       ME_SaveTempStyle(editor);
1878       ME_InsertTextFromCursor(editor, 0, &wstr, 1, style);
1879       ME_ReleaseStyle(style);
1880       ME_CommitUndo(editor);
1881       ME_UpdateRepaint(editor);
1882     }
1883     return 0;
1884   }
1885   case WM_VSCROLL: 
1886   {
1887     int nPos = editor->nScrollPosY;
1888     si.cbSize = sizeof(SCROLLINFO);
1889     si.fMask = SIF_PAGE|SIF_POS|SIF_RANGE|SIF_TRACKPOS;
1890     GetScrollInfo(hWnd, SB_VERT, &si);
1891     switch(LOWORD(wParam)) {
1892     case SB_LINEUP:
1893       nPos -= 24; /* FIXME follow the original */
1894       if (nPos<0) nPos = 0;
1895       break;
1896     case SB_LINEDOWN:
1897     {
1898       int nEnd = editor->nTotalLength - editor->sizeWindow.cy;
1899       nPos += 24; /* FIXME follow the original */
1900       if (nPos>=nEnd) nPos = nEnd;
1901       break;
1902     }
1903     case SB_PAGEUP:
1904       nPos -= editor->sizeWindow.cy;
1905       if (nPos<0) nPos = 0;
1906       break;
1907     case SB_PAGEDOWN:
1908       nPos += editor->sizeWindow.cy;
1909       if (nPos>=editor->nTotalLength) nPos = editor->nTotalLength-1;
1910       break;
1911     case SB_THUMBTRACK:
1912     case SB_THUMBPOSITION:
1913       nPos = si.nTrackPos;
1914       break;
1915     }
1916     if (nPos != editor->nScrollPosY) {
1917       int dy = editor->nScrollPosY - nPos;
1918       editor->nScrollPosY = nPos;
1919       SetScrollPos(hWnd, SB_VERT, nPos, TRUE);
1920       if (editor->bRedraw)
1921       {
1922         ScrollWindow(hWnd, 0, dy, NULL, NULL);
1923         UpdateWindow(hWnd);
1924       }
1925     }
1926     break;
1927   }
1928   case WM_MOUSEWHEEL:
1929   {
1930     int gcWheelDelta = 0, nPos = editor->nScrollPosY, nEnd = editor->nTotalLength - editor->sizeWindow.cy; 
1931     UINT pulScrollLines;
1932     SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
1933     gcWheelDelta -= GET_WHEEL_DELTA_WPARAM(wParam);
1934     if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
1935       nPos += pulScrollLines * (gcWheelDelta / WHEEL_DELTA) * 8; /* FIXME follow the original */
1936     if (nPos>=nEnd)
1937       nPos = nEnd;
1938     if (nPos<0)
1939       nPos = 0;
1940     if (nPos != editor->nScrollPosY) {
1941       int dy = editor->nScrollPosY - nPos;
1942       editor->nScrollPosY = nPos;
1943       SetScrollPos(hWnd, SB_VERT, nPos, TRUE);
1944       if (editor->bRedraw)
1945       {
1946         ScrollWindow(hWnd, 0, dy, NULL, NULL);
1947         UpdateWindow(hWnd);
1948       }
1949     }
1950     break;
1951   }
1952   case EM_GETRECT:
1953   {
1954     *((RECT *)lParam) = editor->rcFormat;
1955     return 0;
1956   }
1957   case EM_SETRECT:
1958   case EM_SETRECTNP:
1959   {
1960     if (lParam)
1961     {
1962       RECT *rc = (RECT *)lParam;
1963       
1964       if (wParam)
1965       {
1966         editor->rcFormat.left += rc->left;
1967         editor->rcFormat.top += rc->top;
1968         editor->rcFormat.right += rc->right;
1969         editor->rcFormat.bottom += rc->bottom;
1970       }
1971       else
1972       {
1973         editor->rcFormat = *rc;
1974       }
1975     }
1976     else
1977     {
1978       GetClientRect(hWnd, &editor->rcFormat);
1979     }
1980     if (msg != EM_SETRECTNP)
1981       ME_RewrapRepaint(editor);
1982     return 0;
1983   }
1984   case EM_REQUESTRESIZE:
1985     ME_SendRequestResize(editor, TRUE);
1986     return 0;
1987   case WM_SETREDRAW:
1988     editor->bRedraw = wParam;
1989     return 0;
1990   case WM_SIZE:
1991   {
1992     GetClientRect(hWnd, &editor->rcFormat);
1993     ME_RewrapRepaint(editor);
1994     return DefWindowProcW(hWnd, msg, wParam, lParam);
1995   }
1996   case EM_GETOLEINTERFACE:
1997   {
1998     LPVOID *ppvObj = (LPVOID*) lParam;
1999     FIXME("EM_GETOLEINTERFACE %p: stub\n", ppvObj);
2000     return CreateIRichEditOle(ppvObj);
2001   }
2002   default:
2003   do_default:
2004     return DefWindowProcW(hWnd, msg, wParam, lParam);
2005   }
2006   return 0L;
2007 }
2008
2009
2010 /******************************************************************
2011  *        RichEdit10ANSIWndProc (RICHED20.9)
2012  */
2013 LRESULT WINAPI RichEdit10ANSIWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
2014 {
2015   LRESULT result;
2016   
2017   /* FIXME: this is NOT the same as 2.0 version */
2018   result = RichEditANSIWndProc(hWnd, msg, wParam, lParam);
2019   if (msg == WM_NCCREATE)
2020   {
2021     ME_TextEditor *editor = (ME_TextEditor *)GetWindowLongW(hWnd, 0);
2022     
2023     editor->bEmulateVersion10 = TRUE;
2024     editor->pBuffer->pLast->member.para.nCharOfs = 2;
2025   }
2026   return result;
2027 }
2028
2029 void ME_SendOldNotify(ME_TextEditor *editor, int nCode)
2030 {
2031   HWND hWnd = editor->hWnd;
2032   SendMessageA(GetParent(hWnd), WM_COMMAND, (nCode<<16)|GetWindowLongW(hWnd, GWLP_ID), (LPARAM)hWnd);
2033 }
2034
2035 int ME_CountParagraphsBetween(ME_TextEditor *editor, int from, int to)
2036 {
2037   ME_DisplayItem *item = ME_FindItemFwd(editor->pBuffer->pFirst, diParagraph);
2038   int i = 0;
2039   
2040   while(item && item->member.para.next_para->member.para.nCharOfs <= from)
2041     item = item->member.para.next_para;
2042   if (!item)
2043     return 0;
2044   while(item && item->member.para.next_para->member.para.nCharOfs <= to) {
2045     item = item->member.para.next_para;
2046     i++;
2047   }
2048   return i;
2049 }
2050
2051
2052 int ME_GetTextW(ME_TextEditor *editor, WCHAR *buffer, int nStart, int nChars, int bCRLF)
2053 {
2054   ME_DisplayItem *item = ME_FindItemAtOffset(editor, diRun, nStart, &nStart);
2055   int nWritten = 0;
2056   WCHAR *pStart = buffer;
2057   
2058   if (!item) {
2059     *buffer = L'\0';
2060     return 0;
2061   }
2062   
2063   if (nStart)
2064   {
2065     int nLen = ME_StrLen(item->member.run.strText) - nStart;
2066     if (nLen > nChars)
2067       nLen = nChars;
2068     CopyMemory(buffer, item->member.run.strText->szData + nStart, sizeof(WCHAR)*nLen);
2069     nChars -= nLen;
2070     nWritten += nLen;
2071     if (!nChars)
2072       return nWritten;
2073     buffer += nLen;
2074     nStart = 0;
2075     item = ME_FindItemFwd(item, diRun);
2076   }
2077   
2078   while(nChars && item)
2079   {
2080     int nLen = ME_StrLen(item->member.run.strText);
2081     if (nLen > nChars)
2082       nLen = nChars;
2083       
2084     if (item->member.run.nFlags & MERF_ENDPARA)
2085     {
2086       *buffer = '\r';
2087       if (bCRLF)
2088       {
2089         *(++buffer) = '\n';
2090         nWritten++;
2091       }
2092       assert(nLen == 1);
2093       /* our end paragraph consists of 2 characters now */
2094       if (editor->bEmulateVersion10)
2095         nChars--;
2096     }
2097     else      
2098       CopyMemory(buffer, item->member.run.strText->szData, sizeof(WCHAR)*nLen);
2099     nChars -= nLen;
2100     nWritten += nLen;
2101     buffer += nLen;    
2102       
2103     if (!nChars)
2104     {
2105       TRACE("nWritten=%d, actual=%d\n", nWritten, buffer-pStart);
2106       *buffer = L'\0';
2107       return nWritten;
2108     }
2109     item = ME_FindItemFwd(item, diRun);
2110   }
2111   *buffer = L'\0';
2112   TRACE("nWritten=%d, actual=%d\n", nWritten, buffer-pStart);
2113   return nWritten;  
2114 }
2115
2116 void ME_RegisterEditorClass(HINSTANCE hInstance)
2117 {
2118   BOOL bResult;
2119   WNDCLASSW wcW;
2120   WNDCLASSA wcA;
2121   
2122   wcW.style = CS_HREDRAW | CS_VREDRAW | CS_GLOBALCLASS;
2123   wcW.lpfnWndProc = RichEditANSIWndProc;
2124   wcW.cbClsExtra = 0;
2125   wcW.cbWndExtra = 4;
2126   wcW.hInstance = NULL; /* hInstance would register DLL-local class */
2127   wcW.hIcon = NULL;
2128   wcW.hCursor = LoadCursorW(NULL, MAKEINTRESOURCEW(IDC_IBEAM));
2129   wcW.hbrBackground = (HBRUSH)GetStockObject(NULL_BRUSH);
2130   wcW.lpszMenuName = NULL;
2131   wcW.lpszClassName = wszClassName;
2132   bResult = RegisterClassW(&wcW);  
2133   assert(bResult);
2134   wcW.lpszClassName = wszClassName50;
2135   bResult = RegisterClassW(&wcW);  
2136   assert(bResult);
2137
2138   wcA.style = CS_HREDRAW | CS_VREDRAW | CS_GLOBALCLASS;
2139   wcA.lpfnWndProc = RichEditANSIWndProc;
2140   wcA.cbClsExtra = 0;
2141   wcA.cbWndExtra = 4;
2142   wcA.hInstance = NULL; /* hInstance would register DLL-local class */
2143   wcA.hIcon = NULL;
2144   wcA.hCursor = LoadCursorW(NULL, MAKEINTRESOURCEW(IDC_IBEAM));
2145   wcA.hbrBackground = (HBRUSH)GetStockObject(NULL_BRUSH);
2146   wcA.lpszMenuName = NULL;
2147   wcA.lpszClassName = "RichEdit20A";
2148   bResult = RegisterClassA(&wcA);  
2149   assert(bResult);
2150   wcA.lpszClassName = "RichEdit50A";
2151   bResult = RegisterClassA(&wcA);  
2152   assert(bResult);
2153 }
2154 /******************************************************************
2155  *        CreateTextServices (RICHED20.4)
2156  *
2157  * FIXME should be ITextHost instead of void*
2158  */
2159 HRESULT WINAPI CreateTextServices(IUnknown *punkOuter, void *pITextHost,
2160     IUnknown **ppUnk)
2161 {
2162   FIXME("stub\n");
2163   /* FIXME should support aggregation */
2164   if (punkOuter)
2165     return CLASS_E_NOAGGREGATION;
2166     
2167   return E_FAIL; /* E_NOTIMPL isn't allowed by MSDN */
2168 }
2169
2170 /******************************************************************
2171  *        REExtendedRegisterClass (RICHED20.8)
2172  *
2173  * FIXME undocumented
2174  */
2175 void WINAPI REExtendedRegisterClass(void)
2176 {
2177   FIXME("stub\n");
2178 }