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