Handle wParam in WM_PAINT properly: if non-null, it is the hdc we are
[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
37   - EM_FINDTEXTEX
38   - EM_FINDWORDBREAK
39   - EM_FMTLINES
40   - EM_FORMATRANGE
41   - EM_GETCHARFORMAT (partly done)
42   + EM_GETEVENTMASK
43   - EM_GETFIRSTVISIBLELINE
44   - EM_GETIMECOLOR 1.0asian
45   - EM_GETIMECOMPMODE 2.0
46   - EM_GETIMEOPTIONS 1.0asian
47   - EM_GETIMESTATUS
48   - EM_GETLANGOPTIONS 2.0
49   - EM_GETLIMITTEXT
50   - EM_GETLINE        
51   - EM_GETLINECOUNT   returns number of rows, not of paragraphs
52   + EM_GETMODIFY
53   - EM_GETOLEINTERFACE
54   - EM_GETOPTIONS
55   + EM_GETPARAFORMAT
56   - EM_GETPUNCTUATION 1.0asian
57   - EM_GETRECT
58   - EM_GETREDONAME 2.0
59   + EM_GETSEL
60   + EM_GETSELTEXT (ANSI&Unicode)
61 ! - EM_GETTHUMB
62   - EM_GETTEXTMODE 2.0
63 ? + EM_GETTEXTRANGE (ANSI&Unicode)
64   - EM_GETUNDONAME
65   - EM_GETWORDBREAKPROC
66   - EM_GETWORDBREAKPROCEX
67   - EM_GETWORDWRAPMODE 1.0asian
68   - EM_HIDESELECTION
69   - EM_LIMITTEXT
70   - EM_LINEFROMCHAR
71   - EM_LINEINDEX
72   - EM_LINELENGTH
73   - EM_LINESCROLL
74   - EM_PASTESPECIAL
75   - EM_POSFROMCHARS
76   - EM_REDO 2.0
77   - EM_REQUESTRESIZE
78   + EM_REPLACESEL (proper style?) ANSI&Unicode
79   - EM_SCROLL
80   - EM_SCROLLCARET
81   - EM_SELECTIONTYPE
82   + EM_SETBKGNDCOLOR
83   - EM_SETCHARFORMAT (partly done, no ANSI)
84   + EM_SETEVENTMASK (few notifications supported)
85   - EM_SETIMECOLOR 1.0asian
86   - EM_SETIMEOPTIONS 1.0asian
87   - EM_SETLANGOPTIONS 2.0
88   - EM_SETLIMITTEXT
89   + EM_SETMODIFY (not sure if implementation is correct)
90   - EM_SETOLECALLBACK
91   - EM_SETOPTIONS
92   + EM_SETPARAFORMAT
93   - EM_SETPUNCTUATION 1.0asian
94   + EM_SETREADONLY no beep on modification attempt
95   - EM_SETRECT
96   - EM_SETRECTNP (EM_SETRECT without repainting) - not supported in RICHEDIT
97   + EM_SETSEL
98   - EM_SETTARGETDEVICE
99   - EM_SETTEXTMODE 2.0
100   - EM_SETUNDOLIMIT 2.0
101   - EM_SETWORDBREAKPROC
102   - EM_SETWORDBREAKPROCEX
103   - EM_SETWORDWRAPMODE 1.0asian
104   - EM_STOPGROUPTYPING 2.0
105   - EM_STREAMIN
106   - EM_STREAMOUT
107   - EM_UNDO
108   + WM_CHAR
109   + WM_CLEAR
110   + WM_COPY
111   + WM_CUT
112   + WM_GETDLGCODE (the current implementation is incomplete)
113   + WM_GETTEXT (ANSI&Unicode)
114   + WM_GETTEXTLENGTH (ANSI version sucks)
115   + WM_PASTE
116   - WM_SETFONT
117   + WM_SETTEXT (resets undo stack !) (proper style?) ANSI&Unicode
118   - WM_STYLECHANGING
119   - WM_STYLECHANGED (things like read-only flag)
120   - WM_UNICHAR
121   
122   Notifications
123   
124   * EN_CHANGE (sent from the wrong place)
125   - EN_CORRECTTEXT
126   - EN_DROPFILES
127   - EN_ERRSPACE
128   - EN_HSCROLL
129   - EN_IMECHANGE
130   + EN_KILLFOCUS
131   - EN_LINK
132   - EN_MAXTEXT
133   - EN_MSGFILTER
134   - EN_OLEOPFAILED
135   - EN_PROTECTED
136   - EN_REQUESTRESIZE
137   - EN_SAVECLIPBOARD
138   + EN_SELCHANGE 
139   + EN_SETFOCUS
140   - EN_STOPNOUNDO
141   * EN_UPDATE (sent from the wrong place)
142   - EN_VSCROLL
143   
144   Styles
145   
146   - ES_AUTOHSCROLL
147   - ES_AUTOVSCROLL
148   - ES_CENTER
149   - ES_DISABLENOSCROLL (scrollbar is always visible)
150   - ES_EX_NOCALLOLEINIT
151   - ES_LEFT
152   - ES_MULTILINE (currently single line controls aren't supported)
153   - ES_NOIME
154   - ES_READONLY (I'm not sure if beeping is the proper behaviour)
155   - ES_RIGHT
156   - ES_SAVESEL
157   - ES_SELFIME
158   - ES_SUNKEN
159   - ES_VERTICAL
160   - ES_WANTRETURN (don't know how to do WM_GETDLGCODE part)
161   - WS_SETFONT
162   - WS_HSCROLL
163   - WS_VSCROLL
164 */
165
166 /*
167  * RICHED20 TODO (incomplete):
168  *
169  * - font caching
170  * - add remaining CHARFORMAT/PARAFORMAT fields
171  * - right/center align should strip spaces from the beginning
172  * - more advanced navigation (Ctrl-arrows, PageUp/PageDn)
173  * - tabs
174  * - pictures (not just smiling faces that lack API support ;-) )
175  * - OLE objects
176  * - calculate heights of pictures (half-done)
177  * - EM_STREAMIN/EM_STREAMOUT
178  * - horizontal scrolling (not even started)
179  * - fix scrollbars and refresh (it sucks bigtime)
180  * - hysteresis during wrapping (related to scrollbars appearing/disappearing)
181  * - should remember maximum row width for wrap hysteresis
182  * - find/replace
183  * - how to implement EM_FORMATRANGE and EM_DISPLAYBAND ? (Mission Impossible)
184  * - italic cursor with italic fonts
185  * - IME
186  * - most notifications aren't sent at all (the most important ones are)
187  * - when should EN_SELCHANGE be sent after text change ? (before/after EN_UPDATE?)
188  * - WM_SETTEXT may use wrong style (but I'm 80% sure it's OK)
189  * - EM_GETCHARFORMAT with SCF_SELECTION may not behave 100% like in original (but very close)
190  * - bugs in end-of-text handling (the gray bar) could get me in jail ;-)
191  * - determination of row size
192  * - end-of-paragraph marks should be of reasonable size
193  *
194  * Bugs that are probably fixed, but not so easy to verify:
195  * - EN_UPDATE/EN_CHANGE are handled very incorrectly (should be OK now)
196  * - undo for ME_JoinParagraphs doesn't store paragraph format ? (it does)
197  * - check/fix artificial EOL logic (bCursorAtEnd, hardly logical)
198  * - caret shouldn't be displayed when selection isn't empty
199  * - check refcounting in style management functions (looks perfect now, but no bugs is suspicious)
200  * - undo for setting default format (done, might be buggy)
201  * - styles might be not released properly (looks like they work like charm, but who knows?
202  *
203  */
204
205 #include "editor.h"
206 #include "ole2.h"
207 #include "richole.h"
208 #include "winreg.h"
209 #define NO_SHLWAPI_STREAM 
210 #include "shlwapi.h"
211
212 #include "rtf.h"
213  
214 WINE_DEFAULT_DEBUG_CHANNEL(richedit);
215
216 int me_debug = 0;
217 HANDLE me_heap = NULL;
218
219 ME_TextBuffer *ME_MakeText() {
220   
221   ME_TextBuffer *buf = ALLOC_OBJ(ME_TextBuffer);
222
223   ME_DisplayItem *p1 = ME_MakeDI(diTextStart);
224   ME_DisplayItem *p2 = ME_MakeDI(diTextEnd);
225   
226   p1->prev = NULL;
227   p1->next = p2;
228   p2->prev = p1;
229   p2->next = NULL;
230   p1->member.para.next_para = p2;
231   p2->member.para.prev_para = p1;
232   p2->member.para.nCharOfs = 0;  
233   
234   buf->pFirst = p1;
235   buf->pLast = p2;
236   buf->pCharStyle = NULL;
237   
238   return buf;
239 }
240
241 #define STREAMIN_BUFFER_SIZE 4096 /* M$ compatibility */
242
243 static LRESULT ME_StreamInText(ME_TextEditor *editor, DWORD dwFormat, EDITSTREAM *stream, ME_Style *style)
244 {
245   BYTE buffer[STREAMIN_BUFFER_SIZE+1];
246   WCHAR wszText[STREAMIN_BUFFER_SIZE+1];
247   
248   TRACE("%08lx %p\n", dwFormat, stream);
249   stream->dwError = 0;
250   
251   do {
252     long nDataSize = 0, nWideChars = 0;
253     stream->dwError = stream->pfnCallback(stream->dwCookie, 
254       (dwFormat & SF_UNICODE ? (BYTE *)wszText : buffer), 
255       STREAMIN_BUFFER_SIZE, &nDataSize);
256     
257     if (stream->dwError)
258       break;
259     if (!nDataSize)
260       break;
261       
262     if (!(dwFormat & SF_UNICODE))
263     {
264       /* FIXME? this is doomed to fail on true MBCS like UTF-8, luckily they're unlikely to be used as CP_ACP */
265       nWideChars = MultiByteToWideChar(CP_ACP, 0, buffer, nDataSize, wszText, STREAMIN_BUFFER_SIZE);
266     }
267     else
268       nWideChars = nDataSize>>1;
269     ME_InsertTextFromCursor(editor, 0, wszText, nWideChars, style);
270     if (nDataSize<STREAMIN_BUFFER_SIZE)
271       break;
272   } while(1);
273   ME_CommitUndo(editor);
274   ME_Repaint(editor);
275   return 0;
276 }
277
278 void ME_RTFCharAttrHook(RTF_Info *info)
279 {
280   CHARFORMAT2W fmt;
281   fmt.cbSize = sizeof(fmt);
282   fmt.dwMask = 0;
283   
284   switch(info->rtfMinor)
285   {
286     case rtfPlain:
287       /* FIXME add more flags once they're implemented */
288       fmt.dwMask = CFM_BOLD | CFM_ITALIC | CFM_UNDERLINE | CFM_STRIKEOUT | CFM_COLOR | CFM_BACKCOLOR | CFM_SIZE | CFM_WEIGHT;
289       fmt.dwEffects = CFE_AUTOCOLOR | CFE_AUTOBACKCOLOR;
290       fmt.yHeight = 12*20; /* 12pt */
291       fmt.wWeight = 400;
292       break;
293     case rtfBold:
294       fmt.dwMask = CFM_BOLD;
295       fmt.dwEffects = info->rtfParam ? fmt.dwMask : 0;
296       break;
297     case rtfItalic:
298       fmt.dwMask = CFM_ITALIC;
299       fmt.dwEffects = info->rtfParam ? fmt.dwMask : 0;
300       break;
301     case rtfUnderline:
302       fmt.dwMask = CFM_UNDERLINE;
303       fmt.dwEffects = info->rtfParam ? fmt.dwMask : 0;
304       break;
305     case rtfStrikeThru:
306       fmt.dwMask = CFM_STRIKEOUT;
307       fmt.dwEffects = info->rtfParam ? fmt.dwMask : 0;
308       break;
309     case rtfBackColor:
310       fmt.dwMask = CFM_BACKCOLOR;
311       fmt.dwEffects = 0;
312       if (info->rtfParam == 0)
313         fmt.dwEffects = CFE_AUTOBACKCOLOR;
314       else if (info->rtfParam != rtfNoParam)
315       {
316         RTFColor *c = RTFGetColor(info, info->rtfParam);
317         fmt.crTextColor = (c->rtfCBlue<<16)|(c->rtfCGreen<<8)|(c->rtfCRed);
318       }
319       break;
320     case rtfForeColor:
321       fmt.dwMask = CFM_COLOR;
322       fmt.dwEffects = 0;
323       if (info->rtfParam == 0)
324         fmt.dwEffects = CFE_AUTOCOLOR;
325       else if (info->rtfParam != rtfNoParam)
326       {
327         RTFColor *c = RTFGetColor(info, info->rtfParam);
328         fmt.crTextColor = (c->rtfCBlue<<16)|(c->rtfCGreen<<8)|(c->rtfCRed);
329       }
330       break;
331     case rtfFontNum:
332       if (info->rtfParam != rtfNoParam)
333       {
334         RTFFont *f = RTFGetFont(info, info->rtfParam);
335         if (f)
336         {
337           MultiByteToWideChar(CP_ACP, 0, f->rtfFName, -1, fmt.szFaceName, sizeof(fmt.szFaceName)/sizeof(WCHAR));
338           fmt.szFaceName[sizeof(fmt.szFaceName)/sizeof(WCHAR)-1] = '\0';
339           fmt.bCharSet = f->rtfFCharSet;
340           fmt.dwMask = CFM_FACE | CFM_CHARSET;
341         }
342       }
343       break;
344     case rtfFontSize:
345       fmt.dwMask = CFM_SIZE;
346       if (info->rtfParam != rtfNoParam)
347         fmt.yHeight = info->rtfParam*10;
348       break;
349   }
350   if (fmt.dwMask) {
351     ME_Style *style2;
352     RTFFlushOutputBuffer(info);
353     /* FIXME too slow ? how come ? */
354     style2 = ME_ApplyStyle(info->style, &fmt);
355     ME_ReleaseStyle(info->style);
356     info->style = style2;
357   }
358 }
359
360 void ME_RTFParAttrHook(RTF_Info *info)
361 {
362   PARAFORMAT2 fmt;
363   fmt.cbSize = sizeof(fmt);
364   fmt.dwMask = 0;
365   
366   switch(info->rtfMinor)
367   {
368   case rtfParDef: /* I'm not 100% sure what does it do, but I guess it restores default paragraph attributes */
369     fmt.dwMask = PFM_ALIGNMENT;
370     fmt.wAlignment = PFA_LEFT;
371     break;
372   case rtfQuadLeft:
373   case rtfQuadJust:
374     fmt.dwMask = PFM_ALIGNMENT;
375     fmt.wAlignment = PFA_LEFT;
376     break;
377   case rtfQuadRight:
378     fmt.dwMask = PFM_ALIGNMENT;
379     fmt.wAlignment = PFA_RIGHT;
380     break;
381   case rtfQuadCenter:
382     fmt.dwMask = PFM_ALIGNMENT;
383     fmt.wAlignment = PFA_CENTER;
384     break;
385   }  
386   if (fmt.dwMask) {
387     RTFFlushOutputBuffer(info);
388     /* FIXME too slow ? how come ?*/
389     ME_SetSelectionParaFormat(info->editor, &fmt);
390   }
391 }
392
393 void ME_RTFReadHook(RTF_Info *info) {
394   switch(info->rtfClass)
395   {
396     case rtfGroup:
397       switch(info->rtfMajor)
398       {
399         case rtfBeginGroup:
400           if (info->stackTop < maxStack) {
401             memcpy(&info->stack[info->stackTop].fmt, &info->style->fmt, sizeof(CHARFORMAT2W));
402             info->stack[info->stackTop].codePage = info->codePage;
403             info->stack[info->stackTop].unicodeLength = info->unicodeLength;
404           }
405           info->stackTop++;
406           break;
407         case rtfEndGroup:
408         {
409           ME_Style *s;
410           RTFFlushOutputBuffer(info);
411           info->stackTop--;
412           /* FIXME too slow ? how come ? */
413           s = ME_ApplyStyle(info->style, &info->stack[info->stackTop].fmt);
414           ME_ReleaseStyle(info->style);
415           info->style = s;
416           info->codePage = info->stack[info->stackTop].codePage;
417           info->unicodeLength = info->stack[info->stackTop].unicodeLength;
418           break;
419         }
420       }
421       break;
422     case rtfControl:
423       switch(info->rtfMajor)
424       {
425         case rtfCharAttr:
426           ME_RTFCharAttrHook(info);
427           break;
428         case rtfParAttr:
429           ME_RTFParAttrHook(info);
430           break;
431       }
432       break;
433   }
434 }
435
436 static LRESULT ME_StreamIn(ME_TextEditor *editor, DWORD format, EDITSTREAM *stream)
437 {
438   RTF_Info parser;
439   ME_Style *style;
440   int from, to, to2, nUndoMode;
441   ME_UndoItem *pUI;
442   int nEventMask = editor->nEventMask;
443
444   TRACE("%p %p\n", stream, editor->hWnd);
445   editor->nEventMask = 0;
446   
447   ME_GetSelection(editor, &from, &to);
448   if (format & SFF_SELECTION) {
449     style = ME_GetSelectionInsertStyle(editor);
450
451     ME_InternalDeleteText(editor, from, to-from);
452   }
453   else {
454     style = editor->pBuffer->pDefaultStyle;
455     ME_AddRefStyle(style);
456     SendMessageA(editor->hWnd, EM_SETSEL, 0, 0);    
457     ME_InternalDeleteText(editor, 0, ME_GetTextLength(editor));
458     from = to = 0;
459     ME_ClearTempStyle(editor);
460     /* FIXME restore default paragraph formatting ! */
461   }
462   
463   nUndoMode = editor->nUndoMode;
464   editor->nUndoMode = umIgnore;
465   if (format & SF_RTF) {
466     /* setup the RTF parser */
467     memset(&parser, 0, sizeof parser);
468     RTFSetEditStream(&parser, stream);
469     parser.rtfFormat = format&(SF_TEXT|SF_RTF);
470     parser.hwndEdit = editor->hWnd;
471     parser.editor = editor;
472     parser.style = style;
473     WriterInit(&parser);
474     RTFInit(&parser);
475     RTFSetReadHook(&parser, ME_RTFReadHook);
476     BeginFile(&parser);
477   
478     /* do the parsing */
479     RTFRead(&parser);
480     RTFFlushOutputBuffer(&parser);
481     RTFDestroy(&parser);
482
483     style = parser.style;
484   }
485   else if (format & SF_TEXT)
486     ME_StreamInText(editor, format, stream, style);
487   else
488     ERR("EM_STREAMIN without SF_TEXT or SF_RTF\n");
489   ME_GetSelection(editor, &to, &to2);
490   /* put the cursor at the top */
491   if (!(format & SFF_SELECTION))
492     SendMessageA(editor->hWnd, EM_SETSEL, 0, 0);
493   else
494   {
495     /* FIXME where to put cursor now ? */
496   }
497   
498   editor->nUndoMode = nUndoMode;
499   pUI = ME_AddUndoItem(editor, diUndoDeleteRun, NULL);
500   TRACE("from %d to %d\n", from, to);
501   if (pUI && from < to)
502   {
503     pUI->nStart = from;
504     pUI->nLen = to-from;
505   }
506   ME_CommitUndo(editor);
507   ME_ReleaseStyle(style); 
508   editor->nEventMask = nEventMask;
509   InvalidateRect(editor->hWnd, NULL, TRUE);
510   ME_UpdateRepaint(editor);
511   if (!(format & SFF_SELECTION)) {
512     ME_ClearTempStyle(editor);
513   }
514   ME_MoveCaret(editor);
515   ME_SendSelChange(editor);
516
517   return 0;
518 }
519
520
521 ME_DisplayItem *
522 ME_FindItemAtOffset(ME_TextEditor *editor, ME_DIType nItemType, int nOffset, int *nItemOffset)
523 {
524   ME_DisplayItem *item = ME_FindItemFwd(editor->pBuffer->pFirst, diParagraph);
525   
526   while (item && item->member.para.next_para->member.para.nCharOfs <= nOffset)
527     item = ME_FindItemFwd(item, diParagraph);
528
529   if (!item)
530     return item;
531
532   nOffset -= item->member.para.nCharOfs;
533   if (nItemType == diParagraph) {
534     if (nItemOffset)
535       *nItemOffset = nOffset;
536     return item;
537   }
538   
539   do {
540     item = ME_FindItemFwd(item, diRun);
541   } while (item && (item->member.run.nCharOfs + ME_StrLen(item->member.run.strText) <= nOffset));
542   if (item) {
543     nOffset -= item->member.run.nCharOfs;
544     if (nItemOffset)
545       *nItemOffset = nOffset;
546   }
547   return item;
548 }
549
550
551 ME_TextEditor *ME_MakeEditor(HWND hWnd) {
552   ME_TextEditor *ed = ALLOC_OBJ(ME_TextEditor);
553   HDC hDC;
554   int i;
555   ed->hWnd = hWnd;
556   ed->pBuffer = ME_MakeText();
557   hDC = GetDC(hWnd);
558   ME_MakeFirstParagraph(hDC, ed->pBuffer);
559   ReleaseDC(hWnd, hDC);
560   ed->bCaretShown = FALSE;
561   ed->nCursors = 3;
562   ed->pCursors = ALLOC_N_OBJ(ME_Cursor, ed->nCursors);
563   ed->pCursors[0].pRun = ME_FindItemFwd(ed->pBuffer->pFirst, diRun);
564   ed->pCursors[0].nOffset = 0;
565   ed->pCursors[1].pRun = ME_FindItemFwd(ed->pBuffer->pFirst, diRun);
566   ed->pCursors[1].nOffset = 0;
567   ed->nLastTotalLength = ed->nTotalLength = 0;
568   ed->nUDArrowX = -1;
569   ed->nSequence = 0;
570   ed->rgbBackColor = -1;
571   ed->bCaretAtEnd = FALSE;
572   ed->nEventMask = 0;
573   ed->nModifyStep = 0;
574   ed->pUndoStack = ed->pRedoStack = NULL;
575   ed->nUndoMode = umAddToUndo;
576   ed->nParagraphs = 1;
577   ed->nLastSelStart = ed->nLastSelEnd = 0;
578   ed->nScrollPosY = 0;
579   for (i=0; i<HFONT_CACHE_SIZE; i++)
580   {
581     ed->pFontCache[i].nRefs = 0;
582     ed->pFontCache[i].nAge = 0;
583     ed->pFontCache[i].hFont = NULL;
584   }
585   ME_CheckCharOffsets(ed);
586   return ed;
587 }
588
589 typedef struct tagME_GlobalDestStruct
590 {
591   HGLOBAL hData;
592   int nLength;
593 } ME_GlobalDestStruct;
594
595 static DWORD CALLBACK ME_AppendToHGLOBAL(DWORD_PTR dwCookie, LPBYTE lpBuff, LONG cb, LONG *pcb)
596 {
597   ME_GlobalDestStruct *pData = (ME_GlobalDestStruct *)dwCookie;
598   int nMaxSize;
599   BYTE *pDest;
600   
601   nMaxSize = GlobalSize(pData->hData);
602   if (pData->nLength+cb+1 >= cb)
603   {
604     /* round up to 2^17 */
605     int nNewSize = (((nMaxSize+cb+1)|0x1FFFF)+1) & 0xFFFE0000;
606     pData->hData = GlobalReAlloc(pData->hData, nNewSize, 0);
607   }
608   pDest = (BYTE *)GlobalLock(pData->hData);
609   memcpy(pDest + pData->nLength, lpBuff, cb);
610   pData->nLength += cb;
611   pDest[pData->nLength] = '\0';
612   GlobalUnlock(pData->hData);
613   *pcb = cb;
614   
615   return 0;
616 }
617
618 static DWORD CALLBACK ME_ReadFromHGLOBALUnicode(DWORD_PTR dwCookie, LPBYTE lpBuff, LONG cb, LONG *pcb)
619 {
620   ME_GlobalDestStruct *pData = (ME_GlobalDestStruct *)dwCookie;
621   int i;
622   WORD *pSrc, *pDest;
623   
624   cb = cb >> 1;
625   pDest = (WORD *)lpBuff;
626   pSrc = (WORD *)GlobalLock(pData->hData);
627   for (i = 0; i<cb && pSrc[pData->nLength+i]; i++) {
628     pDest[i] = pSrc[pData->nLength+i];
629   }    
630   pData->nLength += i;
631   *pcb = 2*i;
632   GlobalUnlock(pData->hData);
633   return 0;
634 }
635
636 static DWORD CALLBACK ME_ReadFromHGLOBALRTF(DWORD_PTR dwCookie, LPBYTE lpBuff, LONG cb, LONG *pcb)
637 {
638   ME_GlobalDestStruct *pData = (ME_GlobalDestStruct *)dwCookie;
639   int i;
640   BYTE *pSrc, *pDest;
641   
642   pDest = lpBuff;
643   pSrc = (BYTE *)GlobalLock(pData->hData);
644   for (i = 0; i<cb && pSrc[pData->nLength+i]; i++) {
645     pDest[i] = pSrc[pData->nLength+i];
646   }    
647   pData->nLength += i;
648   *pcb = i;
649   GlobalUnlock(pData->hData);
650   return 0;
651 }
652
653 void ME_DestroyEditor(ME_TextEditor *editor)
654 {
655   ME_DisplayItem *pFirst = editor->pBuffer->pFirst;
656   ME_DisplayItem *p = pFirst, *pNext = NULL;
657   int i;
658   
659   ME_ClearTempStyle(editor);
660   ME_EmptyUndoStack(editor);
661   while(p) {
662     pNext = p->next;
663     ME_DestroyDisplayItem(p);    
664     p = pNext;
665   }
666   ME_ReleaseStyle(editor->pBuffer->pDefaultStyle);
667   for (i=0; i<HFONT_CACHE_SIZE; i++)
668   {
669     if (editor->pFontCache[i].hFont)
670       DeleteObject(editor->pFontCache[i].hFont);
671   }
672     
673   FREE_OBJ(editor);
674 }
675
676 #define UNSUPPORTED_MSG(e) \
677   case e: \
678     FIXME(#e ": stub\n"); \
679     return DefWindowProcW(hWnd, msg, wParam, lParam);
680
681 /******************************************************************
682  *        RichEditANSIWndProc (RICHED20.10)
683  */
684 LRESULT WINAPI RichEditANSIWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) {
685   HDC hDC;
686   PAINTSTRUCT ps;
687   SCROLLINFO si;
688   ME_TextEditor *editor = (ME_TextEditor *)GetWindowLongW(hWnd, 0);
689   TRACE("msg %d %08x %08lx\n", msg, wParam, lParam);
690   switch(msg) {
691   
692   UNSUPPORTED_MSG(EM_AUTOURLDETECT)
693   UNSUPPORTED_MSG(EM_CANPASTE)
694   UNSUPPORTED_MSG(EM_CHARFROMPOS)
695   UNSUPPORTED_MSG(EM_DISPLAYBAND)
696   UNSUPPORTED_MSG(EM_EXLIMITTEXT)
697   UNSUPPORTED_MSG(EM_EXLINEFROMCHAR)
698   UNSUPPORTED_MSG(EM_FINDTEXT)
699   UNSUPPORTED_MSG(EM_FINDTEXTEX)
700   UNSUPPORTED_MSG(EM_FINDWORDBREAK)
701   UNSUPPORTED_MSG(EM_FMTLINES)
702   UNSUPPORTED_MSG(EM_FORMATRANGE)
703   UNSUPPORTED_MSG(EM_GETFIRSTVISIBLELINE)
704   UNSUPPORTED_MSG(EM_GETIMECOMPMODE)
705   /* UNSUPPORTED_MSG(EM_GETIMESTATUS) missing in Wine headers */
706   UNSUPPORTED_MSG(EM_GETLANGOPTIONS)
707   UNSUPPORTED_MSG(EM_GETLIMITTEXT)
708   UNSUPPORTED_MSG(EM_GETLINE)
709   UNSUPPORTED_MSG(EM_GETLINECOUNT)
710   /* UNSUPPORTED_MSG(EM_GETOLEINTERFACE) separate stub */
711   UNSUPPORTED_MSG(EM_GETOPTIONS)
712   UNSUPPORTED_MSG(EM_GETRECT)
713   UNSUPPORTED_MSG(EM_GETREDONAME)
714   UNSUPPORTED_MSG(EM_GETTEXTMODE)
715   UNSUPPORTED_MSG(EM_GETUNDONAME)
716   UNSUPPORTED_MSG(EM_GETWORDBREAKPROC)
717   UNSUPPORTED_MSG(EM_GETWORDBREAKPROCEX)
718   UNSUPPORTED_MSG(EM_HIDESELECTION)
719   UNSUPPORTED_MSG(EM_LIMITTEXT) /* also known as EM_SETLIMITTEXT */
720   UNSUPPORTED_MSG(EM_LINEFROMCHAR)
721   UNSUPPORTED_MSG(EM_LINEINDEX)
722   UNSUPPORTED_MSG(EM_LINELENGTH)
723   UNSUPPORTED_MSG(EM_LINESCROLL)
724   UNSUPPORTED_MSG(EM_PASTESPECIAL)
725 /*  UNSUPPORTED_MSG(EM_POSFROMCHARS) missing in Wine headers */
726   UNSUPPORTED_MSG(EM_REQUESTRESIZE)
727   UNSUPPORTED_MSG(EM_SCROLL)
728   UNSUPPORTED_MSG(EM_SCROLLCARET)
729   UNSUPPORTED_MSG(EM_SELECTIONTYPE)
730   UNSUPPORTED_MSG(EM_SETLANGOPTIONS)
731   UNSUPPORTED_MSG(EM_SETOLECALLBACK)
732   UNSUPPORTED_MSG(EM_SETOPTIONS)
733   UNSUPPORTED_MSG(EM_SETRECT)
734   UNSUPPORTED_MSG(EM_SETRECTNP)
735   UNSUPPORTED_MSG(EM_SETTARGETDEVICE)
736   UNSUPPORTED_MSG(EM_SETTEXTMODE)
737   UNSUPPORTED_MSG(EM_SETUNDOLIMIT)
738   UNSUPPORTED_MSG(EM_SETWORDBREAKPROC)
739   UNSUPPORTED_MSG(EM_SETWORDBREAKPROCEX)
740   UNSUPPORTED_MSG(WM_SETFONT)
741   UNSUPPORTED_MSG(WM_STYLECHANGING)
742   UNSUPPORTED_MSG(WM_STYLECHANGED)
743 /*  UNSUPPORTED_MSG(WM_UNICHAR) FIXME missing in Wine headers */
744     
745 /* Messages specific to Richedit controls */
746   
747   case EM_STREAMIN:
748    return ME_StreamIn(editor, wParam, (EDITSTREAM*)lParam);
749   case EM_STREAMOUT:
750    return ME_StreamOut(editor, wParam, (EDITSTREAM *)lParam);
751   case WM_GETDLGCODE:
752   {
753     UINT code = DLGC_WANTCHARS|DLGC_WANTARROWS;
754     if (GetWindowLongW(hWnd, GWL_STYLE)&ES_WANTRETURN)
755       code |= 0; /* FIXME what can we do here ? ask for messages and censor them ? */
756     return code;
757   }
758   case WM_NCCREATE:
759   {
760     CREATESTRUCTW *pcs = (CREATESTRUCTW *)lParam;
761     editor = ME_MakeEditor(hWnd);
762     SetWindowLongW(hWnd, 0, (long)editor);
763     pcs = 0; /* ignore */
764     return TRUE;
765   }
766   case EM_EMPTYUNDOBUFFER:
767     ME_EmptyUndoStack(editor);
768     return 0;
769   case EM_GETSEL:
770   {
771     ME_GetSelection(editor, (int *)wParam, (int *)lParam);
772     if (!((wParam|lParam) & 0xFFFF0000))
773       return (lParam<<16)|wParam;
774     return -1;
775   }
776   case EM_EXGETSEL:
777   {
778     CHARRANGE *pRange = (CHARRANGE *)lParam;
779     ME_GetSelection(editor, (int *)&pRange->cpMin, (int *)&pRange->cpMax);
780     return 0;
781   }
782   case EM_CANUNDO:
783     return editor->pUndoStack != NULL;
784   case EM_CANREDO:
785     return editor->pRedoStack != NULL;
786   case EM_UNDO:
787     ME_Undo(editor);
788     return 0;
789   case EM_REDO:
790     ME_Redo(editor);
791     return 0;
792   case EM_SETSEL:
793   {
794     ME_SetSelection(editor, wParam, lParam);
795     ME_Repaint(editor);
796     ME_SendSelChange(editor);
797     return 0;
798   }
799   case EM_EXSETSEL:
800   {
801     CHARRANGE *pRange = (CHARRANGE *)lParam;
802     ME_SetSelection(editor, pRange->cpMin, pRange->cpMax);
803     /* FIXME optimize */
804     ME_Repaint(editor);
805     ME_SendSelChange(editor);
806     return 0;
807   }
808   case EM_SETBKGNDCOLOR:
809   {
810     LRESULT lColor = ME_GetBackColor(editor);
811     if (wParam)
812       editor->rgbBackColor = -1;
813     else
814       editor->rgbBackColor = lParam; 
815     InvalidateRect(hWnd, NULL, TRUE);
816     UpdateWindow(hWnd);
817     return lColor;
818   }
819   case EM_GETMODIFY:
820     return editor->nModifyStep == 0 ? 0 : 1;
821   case EM_SETMODIFY:
822   {
823     if (wParam)
824       editor->nModifyStep = 0x80000000;
825     else
826       editor->nModifyStep = 0;
827     
828     return 0;
829   }
830   case EM_SETREADONLY:
831   {
832     long nStyle = GetWindowLongW(hWnd, GWL_STYLE);
833     if (wParam)
834       nStyle |= ES_READONLY;
835     else
836       nStyle &= ~ES_READONLY;
837     SetWindowLongW(hWnd, GWL_STYLE, nStyle);
838     ME_Repaint(editor);
839     return 0;
840   }
841   case EM_SETEVENTMASK:
842     editor->nEventMask = lParam;
843     return 0;
844   case EM_GETEVENTMASK:
845     return editor->nEventMask;
846   case EM_SETCHARFORMAT:
847   {
848     CHARFORMAT2W buf, *p;
849     BOOL bRepaint = TRUE;
850     p = ME_ToCF2W(&buf, (CHARFORMAT2W *)lParam);
851     if (!wParam)
852       ME_SetDefaultCharFormat(editor, p);
853     else if (wParam == (SCF_WORD | SCF_SELECTION))
854       FIXME("word selection not supported\n");
855     else if (wParam == SCF_ALL)
856       ME_SetCharFormat(editor, 0, ME_GetTextLength(editor), p);
857     else {
858       int from, to;
859       ME_GetSelection(editor, &from, &to);
860       bRepaint = (from != to);
861       ME_SetSelectionCharFormat(editor, p);
862     }
863     ME_CommitUndo(editor);
864     if (bRepaint)
865       ME_UpdateRepaint(editor);
866     return 0;
867   }
868   case EM_GETCHARFORMAT:
869   {
870     CHARFORMAT2W tmp;
871     tmp.cbSize = sizeof(tmp);
872     if (!wParam)
873       ME_GetDefaultCharFormat(editor, &tmp);
874     else
875       ME_GetSelectionCharFormat(editor, &tmp);
876     ME_CopyToCFAny((CHARFORMAT2W *)lParam, &tmp);
877     return 0;
878   }
879   case EM_SETPARAFORMAT:
880     ME_SetSelectionParaFormat(editor, (PARAFORMAT2 *)lParam);
881     ME_UpdateRepaint(editor);
882     ME_CommitUndo(editor);
883     return 0;
884   case EM_GETPARAFORMAT:
885     ME_GetSelectionParaFormat(editor, (PARAFORMAT2 *)lParam);
886     return 0;
887   case WM_CLEAR:
888   {
889     int from, to;
890     ME_GetSelection(editor, &from, &to);
891     ME_InternalDeleteText(editor, from, to-from);
892     ME_CommitUndo(editor);
893     ME_UpdateRepaint(editor);
894     return 0;
895   }
896   case EM_REPLACESEL:
897   {
898     int from, to;
899     ME_Style *style;
900     LPWSTR wszText = ME_ToUnicode(hWnd, (void *)lParam);
901     size_t len = lstrlenW(wszText);
902     TRACE("EM_REPLACESEL - %s\n", debugstr_w(wszText));
903     
904     ME_GetSelection(editor, &from, &to);
905     style = ME_GetSelectionInsertStyle(editor);
906     ME_InternalDeleteText(editor, from, to-from);
907     ME_InsertTextFromCursor(editor, 0, wszText, len, style);
908     ME_ReleaseStyle(style);
909     ME_EndToUnicode(hWnd, wszText);
910     /* drop temporary style if line end */
911     /* FIXME question: does abc\n mean: put abc, clear temp style, put \n? (would require a change) */  
912     if (len>0 && wszText[len-1] == '\n')
913       ME_ClearTempStyle(editor);
914       
915     ME_CommitUndo(editor);
916     if (!wParam)
917       ME_EmptyUndoStack(editor);
918     ME_UpdateRepaint(editor);
919     return 0;
920   }
921   case WM_SETTEXT:
922   {
923     LPWSTR wszText = ME_ToUnicode(hWnd, (void *)lParam);
924     TRACE("WM_SETTEXT - %s\n", (char *)(wszText)); /* debugstr_w() */
925     ME_InternalDeleteText(editor, 0, ME_GetTextLength(editor));
926     /* uses default style! */
927     ME_InsertTextFromCursor(editor, 0, wszText, -1, editor->pBuffer->pDefaultStyle);
928     ME_EndToUnicode(hWnd, wszText);
929     ME_CommitUndo(editor);
930     ME_EmptyUndoStack(editor);
931     ME_UpdateRepaint(editor);
932     return 0;
933   }
934   case WM_PASTE:
935   {    
936     DWORD dwFormat = 0;
937     EDITSTREAM es;
938     ME_GlobalDestStruct gds;
939     UINT nRTFFormat = RegisterClipboardFormatA("Rich Text Format");
940     UINT cf = 0;
941
942     if (!OpenClipboard(hWnd))
943       return 0;
944     if (IsClipboardFormatAvailable(nRTFFormat))
945       cf = nRTFFormat, dwFormat = SF_RTF;
946     else if (IsClipboardFormatAvailable(CF_UNICODETEXT))
947       cf = CF_UNICODETEXT, dwFormat = SF_TEXT|SF_UNICODE;
948     else
949       return 0;
950
951     gds.hData = GetClipboardData(cf);
952     gds.nLength = 0;
953     es.dwCookie = (DWORD)&gds;
954     es.pfnCallback = dwFormat == SF_RTF ? ME_ReadFromHGLOBALRTF : ME_ReadFromHGLOBALUnicode;
955     SendMessageW(hWnd, EM_STREAMIN, dwFormat|SFF_SELECTION, (LPARAM)&es);
956     
957     CloseClipboard();
958     return 0;
959   }
960   case WM_CUT:
961   case WM_COPY:
962   {
963     int from, to, pars;
964     WCHAR *data;
965     HANDLE hData;
966     EDITSTREAM es;
967     ME_GlobalDestStruct gds;
968     
969     if (!OpenClipboard(hWnd))
970       return 0;
971       
972     EmptyClipboard();
973     ME_GetSelection(editor, &from, &to);
974     pars = ME_CountParagraphsBetween(editor, from, to);
975     hData = GlobalAlloc(GMEM_MOVEABLE, sizeof(WCHAR)*(to-from+pars+1));
976     data = (WCHAR *)GlobalLock(hData);
977     ME_GetTextW(editor, data, from, to-from, TRUE);
978     GlobalUnlock(hData);
979
980     gds.hData = GlobalAlloc(GMEM_MOVEABLE, 0);
981     gds.nLength = 0;
982     es.dwCookie = (DWORD)&gds;
983     es.pfnCallback = ME_AppendToHGLOBAL;
984     SendMessageW(hWnd, EM_STREAMOUT, SFF_SELECTION|SF_RTF, (LPARAM)&es);
985     GlobalReAlloc(gds.hData, gds.nLength+1, 0);
986     
987     SetClipboardData(CF_UNICODETEXT, hData);    
988     SetClipboardData(RegisterClipboardFormatA("Rich Text Format"), gds.hData);
989     
990     CloseClipboard();
991     if (msg == WM_CUT)
992     {
993       ME_InternalDeleteText(editor, from, to-from);
994       ME_CommitUndo(editor);
995       ME_UpdateRepaint(editor);
996     }
997     return 0;
998   }
999   case WM_GETTEXTLENGTH:
1000     return ME_GetTextLength(editor);
1001   case WM_GETTEXT:
1002   {
1003     TEXTRANGEW tr; /* W and A differ only by rng->lpstrText */
1004     tr.chrg.cpMin = 0;
1005     tr.chrg.cpMax = wParam-1;
1006     tr.lpstrText = (WCHAR *)lParam;
1007     return RichEditANSIWndProc(hWnd, EM_GETTEXTRANGE, 0, (LPARAM)&tr);
1008   }
1009   case EM_GETSELTEXT:
1010   {
1011     int from, to;
1012     TEXTRANGEW tr; /* W and A differ only by rng->lpstrText */
1013     ME_GetSelection(editor, &from, &to);
1014     tr.chrg.cpMin = from;
1015     tr.chrg.cpMax = to;
1016     tr.lpstrText = (WCHAR *)lParam;
1017     return RichEditANSIWndProc(hWnd, EM_GETTEXTRANGE, 0, (LPARAM)&tr);
1018   }
1019   case EM_GETTEXTRANGE:
1020   {
1021     TEXTRANGEW *rng = (TEXTRANGEW *)lParam;
1022     if (IsWindowUnicode(hWnd))
1023       return ME_GetTextW(editor, rng->lpstrText, rng->chrg.cpMin, rng->chrg.cpMax-rng->chrg.cpMin, FALSE);
1024     else
1025     {
1026       int nLen = rng->chrg.cpMax-rng->chrg.cpMin;
1027       WCHAR *p = ALLOC_N_OBJ(WCHAR, nLen+1);
1028       int nChars = ME_GetTextW(editor, p, rng->chrg.cpMin, nLen, FALSE);
1029       /* FIXME this is a potential security hole (buffer overrun) 
1030          if you know more about wchar->mbyte conversion please explain
1031       */
1032       WideCharToMultiByte(CP_ACP, 0, p, nChars+1, (char *)rng->lpstrText, nLen+1, NULL, NULL);
1033       FREE_OBJ(p);
1034       return nChars;
1035     }
1036     return ME_GetTextW(editor, rng->lpstrText, rng->chrg.cpMin, rng->chrg.cpMax-rng->chrg.cpMin, FALSE);
1037   }
1038   case WM_CREATE:
1039     ME_CommitUndo(editor);
1040     ME_WrapMarkedParagraphs(editor);
1041     ME_MoveCaret(editor);
1042     return 0;
1043   case WM_DESTROY:
1044     ME_DestroyEditor(editor);
1045     SetWindowLongW(hWnd, 0, 0);
1046     return 0;
1047   case WM_LBUTTONDOWN:
1048     SetFocus(hWnd);
1049     ME_LButtonDown(editor, (short)LOWORD(lParam), (short)HIWORD(lParam));
1050     SetCapture(hWnd);
1051     break;
1052   case WM_MOUSEMOVE:
1053     if (GetCapture() == hWnd)
1054       ME_MouseMove(editor, (short)LOWORD(lParam), (short)HIWORD(lParam));
1055     break;
1056   case WM_LBUTTONUP:
1057     if (GetCapture() == hWnd)
1058       ReleaseCapture();
1059     break;
1060   case WM_PAINT:
1061     hDC = BeginPaint(hWnd, &ps);
1062     ME_PaintContent(editor, hDC, FALSE, &ps.rcPaint);
1063     EndPaint(hWnd, &ps);
1064     break;
1065   case WM_SETFOCUS:
1066     ME_ShowCaret(editor);
1067     ME_SendOldNotify(editor, EN_SETFOCUS);
1068     return 0;
1069   case WM_KILLFOCUS:
1070     ME_HideCaret(editor);
1071     ME_SendOldNotify(editor, EN_KILLFOCUS);
1072     return 0;
1073   case WM_ERASEBKGND:
1074   {
1075     HDC hDC = (HDC)wParam;
1076     RECT rc;
1077     COLORREF rgbBG = ME_GetBackColor(editor);
1078     if (GetUpdateRect(hWnd,&rc,TRUE))
1079     {
1080       HBRUSH hbr = CreateSolidBrush(rgbBG);
1081       FillRect(hDC, &rc, hbr);
1082       DeleteObject(hbr);
1083     }
1084     return 1;
1085   }
1086   case WM_COMMAND:
1087     TRACE("editor wnd command = %d\n", LOWORD(wParam));
1088     return 0;
1089   case WM_KEYDOWN:
1090     if (ME_ArrowKey(editor, LOWORD(wParam), GetKeyState(VK_CONTROL)<0)) {
1091       ME_CommitUndo(editor);
1092       ME_EnsureVisible(editor, editor->pCursors[0].pRun);
1093       HideCaret(hWnd);
1094       ME_MoveCaret(editor);
1095       ShowCaret(hWnd);
1096       return 0;
1097     }
1098     if (GetKeyState(VK_CONTROL)<0)
1099     {
1100       if (LOWORD(wParam)=='W')
1101       {
1102         CHARFORMAT2W chf;
1103         char buf[2048];
1104         ME_GetSelectionCharFormat(editor, &chf);
1105         ME_DumpStyleToBuf(&chf, buf);
1106         MessageBoxA(NULL, buf, "Style dump", MB_OK);
1107       }
1108       if (LOWORD(wParam)=='Q')
1109       {
1110         ME_CheckCharOffsets(editor);
1111       }
1112     }
1113     goto do_default;
1114   case WM_CHAR: 
1115   {
1116     WCHAR wstr;
1117     if (GetWindowLongW(editor->hWnd, GWL_STYLE) & ES_READONLY) {
1118       MessageBeep(MB_ICONERROR);
1119       return 0; /* FIXME really 0 ? */
1120     }
1121     wstr = LOWORD(wParam);
1122     if (((unsigned)wstr)>=' ' || wstr=='\r') {
1123       /* FIXME maybe it would make sense to call EM_REPLACESEL instead ? */
1124       ME_Style *style = ME_GetInsertStyle(editor, 0);
1125       ME_SaveTempStyle(editor);
1126       ME_InsertTextFromCursor(editor, 0, &wstr, 1, style);
1127       ME_ReleaseStyle(style);
1128       ME_CommitUndo(editor);
1129       ME_UpdateRepaint(editor);
1130     }
1131     return 0;
1132   }
1133   case WM_VSCROLL: 
1134   {
1135     int nPos = editor->nScrollPosY;
1136     si.cbSize = sizeof(SCROLLINFO);
1137     si.fMask = SIF_PAGE|SIF_POS|SIF_RANGE|SIF_TRACKPOS;
1138     GetScrollInfo(hWnd, SB_VERT, &si);
1139     switch(LOWORD(wParam)) {
1140     case SB_LINEUP:
1141       nPos -= 24; /* FIXME follow the original */
1142       if (nPos<0) nPos = 0;
1143       break;
1144     case SB_LINEDOWN:
1145     {
1146       int nEnd = editor->nTotalLength - editor->sizeWindow.cy;
1147       nPos += 24; /* FIXME follow the original */
1148       if (nPos>=nEnd) nPos = nEnd;
1149       break;
1150     }
1151     case SB_PAGEUP:
1152       nPos -= editor->sizeWindow.cy;
1153       if (nPos<0) nPos = 0;
1154       break;
1155     case SB_PAGEDOWN:
1156       nPos += editor->sizeWindow.cy;
1157       if (nPos>=editor->nTotalLength) nPos = editor->nTotalLength-1;
1158       break;
1159     case SB_THUMBTRACK:
1160     case SB_THUMBPOSITION:
1161       nPos = si.nTrackPos;
1162       break;
1163     }
1164     if (nPos != editor->nScrollPosY) {
1165       ScrollWindow(hWnd, 0, editor->nScrollPosY-nPos, NULL, NULL);
1166       editor->nScrollPosY = nPos;
1167       SetScrollPos(hWnd, SB_VERT, nPos, FALSE);
1168       UpdateWindow(hWnd);
1169     }
1170     break;
1171   }
1172   case WM_SIZE:
1173   {
1174     ME_MarkAllForWrapping(editor);
1175     ME_WrapMarkedParagraphs(editor);
1176     ME_UpdateScrollBar(editor);
1177     ME_Repaint(editor);
1178     return DefWindowProcW(hWnd, msg, wParam, lParam);
1179   }
1180   case EM_GETOLEINTERFACE:
1181   {
1182     LPVOID *ppvObj = (LPVOID*) lParam;
1183     FIXME("EM_GETOLEINTERFACE %p: stub\n", ppvObj);
1184     return CreateIRichEditOle(ppvObj);
1185   }
1186   default:
1187   do_default:
1188     return DefWindowProcW(hWnd, msg, wParam, lParam);
1189   }
1190   return 0L;
1191 }
1192
1193 /******************************************************************
1194  *        RichEdit10ANSIWndProc (RICHED20.9)
1195  */
1196 LRESULT WINAPI RichEdit10ANSIWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
1197 {
1198   /* FIXME: this is NOT the same as 2.0 version */
1199   return RichEditANSIWndProc(hWnd, msg, wParam, lParam);
1200 }
1201
1202 void ME_SendOldNotify(ME_TextEditor *editor, int nCode)
1203 {
1204   HWND hWnd = editor->hWnd;
1205   SendMessageA(GetParent(hWnd), WM_COMMAND, (nCode<<16)|GetWindowLongW(hWnd, GWLP_ID), (LPARAM)hWnd);
1206 }
1207
1208 int ME_CountParagraphsBetween(ME_TextEditor *editor, int from, int to)
1209 {
1210   ME_DisplayItem *item = ME_FindItemFwd(editor->pBuffer->pFirst, diParagraph);
1211   int i = 0;
1212   
1213   while(item && item->member.para.next_para->member.para.nCharOfs <= from)
1214     item = item->member.para.next_para;
1215   if (!item)
1216     return 0;
1217   while(item && item->member.para.next_para->member.para.nCharOfs <= to) {
1218     item = item->member.para.next_para;
1219     i++;
1220   }
1221   return i;
1222 }
1223
1224
1225 int ME_GetTextW(ME_TextEditor *editor, WCHAR *buffer, int nStart, int nChars, int bCRLF)
1226 {
1227   ME_DisplayItem *item = ME_FindItemAtOffset(editor, diRun, nStart, &nStart);
1228   int nWritten = 0;
1229   
1230   if (!item) {
1231     *buffer = L'\0';
1232     return 0;
1233   }
1234   assert(item);    
1235   
1236   if (nStart)
1237   {
1238     int nLen = ME_StrLen(item->member.run.strText) - nStart;
1239     if (nLen > nChars)
1240       nLen = nChars;
1241     CopyMemory(buffer, item->member.run.strText->szData + nStart, sizeof(WCHAR)*nLen);
1242     nChars -= nLen;
1243     nWritten += nLen;
1244     if (!nChars)
1245       return nWritten;
1246     buffer += nLen;
1247     nStart = 0;
1248     item = ME_FindItemFwd(item, diRun);
1249   }
1250   
1251   while(nChars && item)
1252   {
1253     int nLen = ME_StrLen(item->member.run.strText);
1254     if (nLen > nChars)
1255       nLen = nChars;
1256       
1257     if (item->member.run.nFlags & MERF_ENDPARA)
1258     {
1259       if (bCRLF) {
1260         *buffer++ = '\r';
1261         nWritten++;
1262       }        
1263       *buffer = '\n';
1264       assert(nLen == 1);
1265     }
1266     else      
1267       CopyMemory(buffer, item->member.run.strText->szData, sizeof(WCHAR)*nLen);
1268     nChars -= nLen;
1269     nWritten += nLen;
1270     buffer += nLen;    
1271       
1272     if (!nChars)
1273     {
1274       *buffer = L'\0';
1275       return nWritten;
1276     }
1277     item = ME_FindItemFwd(item, diRun);
1278   }
1279   *buffer = L'\0';
1280   return nWritten;  
1281 }
1282
1283 static WCHAR wszClassName[] = {'R', 'i', 'c', 'h', 'E', 'd', 'i', 't', '2', '0', 'W', 0};
1284 static WCHAR wszClassName50[] = {'R', 'i', 'c', 'h', 'E', 'd', 'i', 't', '5', '0', 'W', 0};
1285
1286 void ME_RegisterEditorClass(HINSTANCE hInstance)
1287 {
1288   BOOL bResult;
1289   WNDCLASSW wcW;
1290   WNDCLASSA wcA;
1291   
1292   wcW.style = CS_HREDRAW | CS_VREDRAW;
1293   wcW.lpfnWndProc = RichEditANSIWndProc;
1294   wcW.cbClsExtra = 0;
1295   wcW.cbWndExtra = 4;
1296   wcW.hInstance = NULL; /* hInstance would register DLL-local class */
1297   wcW.hIcon = NULL;
1298   wcW.hCursor = LoadCursorW(NULL, MAKEINTRESOURCEW(IDC_IBEAM));
1299   wcW.hbrBackground = (HBRUSH)GetStockObject(NULL_BRUSH);
1300   wcW.lpszMenuName = NULL;
1301   wcW.lpszClassName = wszClassName;
1302   bResult = RegisterClassW(&wcW);  
1303   assert(bResult);
1304   wcW.lpszClassName = wszClassName50;
1305   bResult = RegisterClassW(&wcW);  
1306   assert(bResult);
1307
1308   wcA.style = CS_HREDRAW | CS_VREDRAW;
1309   wcA.lpfnWndProc = RichEditANSIWndProc;
1310   wcA.cbClsExtra = 0;
1311   wcA.cbWndExtra = 4;
1312   wcA.hInstance = NULL; /* hInstance would register DLL-local class */
1313   wcA.hIcon = NULL;
1314   wcA.hCursor = LoadCursorW(NULL, MAKEINTRESOURCEW(IDC_IBEAM));
1315   wcA.hbrBackground = (HBRUSH)GetStockObject(NULL_BRUSH);
1316   wcA.lpszMenuName = NULL;
1317   wcA.lpszClassName = "RichEdit20A";
1318   bResult = RegisterClassA(&wcA);  
1319   assert(bResult);
1320   wcA.lpszClassName = "RichEdit50A";
1321   bResult = RegisterClassA(&wcA);  
1322   assert(bResult);
1323 }
1324
1325 BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
1326 {
1327     TRACE("\n");
1328     switch (fdwReason)
1329     {
1330     case DLL_PROCESS_ATTACH:
1331       DisableThreadLibraryCalls(hinstDLL);
1332       me_heap = HeapCreate (0, 0x10000, 0);
1333       ME_RegisterEditorClass(hinstDLL);
1334       break;
1335
1336     case DLL_PROCESS_DETACH:
1337       UnregisterClassW(wszClassName, 0);
1338       UnregisterClassW(wszClassName50, 0);
1339       UnregisterClassA("RichEdit20A", 0);
1340       UnregisterClassA("RichEdit50A", 0);
1341       HeapDestroy (me_heap);
1342       me_heap = NULL;
1343       break;
1344     }
1345     return TRUE;
1346 }
1347
1348 /******************************************************************
1349  *        CreateTextServices (RICHED20.4)
1350  *
1351  * FIXME should be ITextHost instead of void*
1352  */
1353 HRESULT WINAPI CreateTextServices(IUnknown *punkOuter, void *pITextHost,
1354     IUnknown **ppUnk)
1355 {
1356   FIXME("stub\n");
1357   /* FIXME should support aggregation */
1358   if (punkOuter)
1359     return CLASS_E_NOAGGREGATION;
1360     
1361   return E_FAIL; /* E_NOTIMPL isn't allowed by MSDN */
1362 }
1363
1364 /******************************************************************
1365  *        REExtendedRegisterClass (RICHED20.8)
1366  *
1367  * FIXME undocumented
1368  */
1369 void WINAPI REExtendedRegisterClass(void)
1370 {
1371   FIXME("stub\n");
1372 }