Fix segmentation fault caused by incorrect referencing of client audio
[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     stream->dwSize = 0;
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 /* FIXME this function doesn't get any information about context of the RTF tag, which is very bad,
361    the same tags mean different things in different contexts */
362 void ME_RTFParAttrHook(RTF_Info *info)
363 {
364   PARAFORMAT2 fmt;
365   fmt.cbSize = sizeof(fmt);
366   fmt.dwMask = 0;
367   
368   switch(info->rtfMinor)
369   {
370   case rtfParDef: /* I'm not 100% sure what does it do, but I guess it restores default paragraph attributes */
371     fmt.dwMask = PFM_ALIGNMENT | PFM_TABSTOPS | PFM_OFFSET | PFM_STARTINDENT;
372     fmt.wAlignment = PFA_LEFT;
373     fmt.cTabCount = 0;
374     fmt.dxOffset = fmt.dxStartIndent = 0;
375     break;
376   case rtfFirstIndent:
377     ME_GetSelectionParaFormat(info->editor, &fmt);
378     fmt.dwMask = PFM_STARTINDENT;
379     fmt.dxStartIndent = info->rtfParam + fmt.dxOffset;
380     break;
381   case rtfLeftIndent:
382   {
383     int first, left;
384     ME_GetSelectionParaFormat(info->editor, &fmt);
385     first = fmt.dxStartIndent;
386     left = info->rtfParam;
387     fmt.dwMask = PFM_STARTINDENT|PFM_OFFSET;
388     fmt.dxStartIndent = first + left;
389     fmt.dxOffset = -first;
390     break;
391   }
392   case rtfRightIndent:
393     fmt.dwMask = PFM_RIGHTINDENT;
394     fmt.dxRightIndent = info->rtfParam;
395     break;
396   case rtfQuadLeft:
397   case rtfQuadJust:
398     fmt.dwMask = PFM_ALIGNMENT;
399     fmt.wAlignment = PFA_LEFT;
400     break;
401   case rtfQuadRight:
402     fmt.dwMask = PFM_ALIGNMENT;
403     fmt.wAlignment = PFA_RIGHT;
404     break;
405   case rtfQuadCenter:
406     fmt.dwMask = PFM_ALIGNMENT;
407     fmt.wAlignment = PFA_CENTER;
408     break;
409   case rtfTabPos:
410     ME_GetSelectionParaFormat(info->editor, &fmt);
411     if (!(fmt.dwMask & PFM_TABSTOPS))
412     {
413       fmt.dwMask |= PFM_TABSTOPS;
414       fmt.cTabCount = 0;
415     }
416     if (fmt.cTabCount < MAX_TAB_STOPS)
417       fmt.rgxTabs[fmt.cTabCount++] = info->rtfParam;
418     break;
419   }  
420   if (fmt.dwMask) {
421     RTFFlushOutputBuffer(info);
422     /* FIXME too slow ? how come ?*/
423     ME_SetSelectionParaFormat(info->editor, &fmt);
424   }
425 }
426
427 void ME_RTFReadHook(RTF_Info *info) {
428   switch(info->rtfClass)
429   {
430     case rtfGroup:
431       switch(info->rtfMajor)
432       {
433         case rtfBeginGroup:
434           if (info->stackTop < maxStack) {
435             memcpy(&info->stack[info->stackTop].fmt, &info->style->fmt, sizeof(CHARFORMAT2W));
436             info->stack[info->stackTop].codePage = info->codePage;
437             info->stack[info->stackTop].unicodeLength = info->unicodeLength;
438           }
439           info->stackTop++;
440           break;
441         case rtfEndGroup:
442         {
443           ME_Style *s;
444           RTFFlushOutputBuffer(info);
445           info->stackTop--;
446           /* FIXME too slow ? how come ? */
447           s = ME_ApplyStyle(info->style, &info->stack[info->stackTop].fmt);
448           ME_ReleaseStyle(info->style);
449           info->style = s;
450           info->codePage = info->stack[info->stackTop].codePage;
451           info->unicodeLength = info->stack[info->stackTop].unicodeLength;
452           break;
453         }
454       }
455       break;
456     case rtfControl:
457       switch(info->rtfMajor)
458       {
459         case rtfCharAttr:
460           ME_RTFCharAttrHook(info);
461           break;
462         case rtfParAttr:
463           ME_RTFParAttrHook(info);
464           break;
465       }
466       break;
467   }
468 }
469
470 void
471 ME_StreamInFill(ME_InStream *stream)
472 {
473   stream->editstream->dwError = stream->editstream->pfnCallback(stream->editstream->dwCookie,
474                                                                 stream->buffer,
475                                                                 sizeof(stream->buffer),
476                                                                 &stream->dwSize);
477   stream->dwUsed = 0;
478 }
479
480 static LRESULT ME_StreamIn(ME_TextEditor *editor, DWORD format, EDITSTREAM *stream)
481 {
482   RTF_Info parser;
483   ME_Style *style;
484   int from, to, to2, nUndoMode;
485   ME_UndoItem *pUI;
486   int nEventMask = editor->nEventMask;
487   ME_InStream inStream;
488
489   TRACE("stream==%p hWnd==%p format==0x%X\n", stream, editor->hWnd, (UINT)format);
490   editor->nEventMask = 0;
491   
492   ME_GetSelection(editor, &from, &to);
493   if (format & SFF_SELECTION) {
494     style = ME_GetSelectionInsertStyle(editor);
495
496     ME_InternalDeleteText(editor, from, to-from);
497   }
498   else {
499     style = editor->pBuffer->pDefaultStyle;
500     ME_AddRefStyle(style);
501     SendMessageA(editor->hWnd, EM_SETSEL, 0, 0);    
502     ME_InternalDeleteText(editor, 0, ME_GetTextLength(editor));
503     from = to = 0;
504     ME_ClearTempStyle(editor);
505     /* FIXME restore default paragraph formatting ! */
506   }
507   
508   nUndoMode = editor->nUndoMode;
509   editor->nUndoMode = umIgnore;
510
511   inStream.editstream = stream;
512   inStream.editstream->dwError = 0;
513   inStream.dwSize = 0;
514   inStream.dwUsed = 0;
515
516   if (format & SF_RTF)
517   {
518     /* Check if it's really RTF, and if it is not, use plain text */
519     ME_StreamInFill(&inStream);
520     if (!inStream.editstream->dwError)
521     {
522       if (strncmp(inStream.buffer, "{\\rtf1", 6) && strncmp(inStream.buffer, "{\\urtf", 6))
523       {
524         format &= ~SF_RTF;
525         format |= SF_TEXT;
526       }
527     }
528   }
529
530   if (!inStream.editstream->dwError)
531   {
532     if (format & SF_RTF) {
533       /* setup the RTF parser */
534       memset(&parser, 0, sizeof parser);
535       RTFSetEditStream(&parser, &inStream);
536       parser.rtfFormat = format&(SF_TEXT|SF_RTF);
537       parser.hwndEdit = editor->hWnd;
538       parser.editor = editor;
539       parser.style = style;
540       WriterInit(&parser);
541       RTFInit(&parser);
542       RTFSetReadHook(&parser, ME_RTFReadHook);
543       BeginFile(&parser);
544   
545       /* do the parsing */
546       RTFRead(&parser);
547       RTFFlushOutputBuffer(&parser);
548       RTFDestroy(&parser);
549
550       style = parser.style;
551     }
552     else if (format & SF_TEXT)
553       ME_StreamInText(editor, format, &inStream, style);
554     else
555       ERR("EM_STREAMIN without SF_TEXT or SF_RTF\n");
556     ME_GetSelection(editor, &to, &to2);
557     /* put the cursor at the top */
558     if (!(format & SFF_SELECTION))
559       SendMessageA(editor->hWnd, EM_SETSEL, 0, 0);
560     else
561     {
562       /* FIXME where to put cursor now ? */
563     }
564   }
565   
566   editor->nUndoMode = nUndoMode;
567   pUI = ME_AddUndoItem(editor, diUndoDeleteRun, NULL);
568   TRACE("from %d to %d\n", from, to);
569   if (pUI && from < to)
570   {
571     pUI->nStart = from;
572     pUI->nLen = to-from;
573   }
574   ME_CommitUndo(editor);
575   ME_ReleaseStyle(style); 
576   editor->nEventMask = nEventMask;
577   InvalidateRect(editor->hWnd, NULL, TRUE);
578   ME_UpdateRepaint(editor);
579   if (!(format & SFF_SELECTION)) {
580     ME_ClearTempStyle(editor);
581   }
582   ME_MoveCaret(editor);
583   ME_SendSelChange(editor);
584
585   return 0;
586 }
587
588
589 ME_DisplayItem *
590 ME_FindItemAtOffset(ME_TextEditor *editor, ME_DIType nItemType, int nOffset, int *nItemOffset)
591 {
592   ME_DisplayItem *item = ME_FindItemFwd(editor->pBuffer->pFirst, diParagraph);
593   
594   while (item && item->member.para.next_para->member.para.nCharOfs <= nOffset)
595     item = ME_FindItemFwd(item, diParagraph);
596
597   if (!item)
598     return item;
599
600   nOffset -= item->member.para.nCharOfs;
601   if (nItemType == diParagraph) {
602     if (nItemOffset)
603       *nItemOffset = nOffset;
604     return item;
605   }
606   
607   do {
608     item = ME_FindItemFwd(item, diRun);
609   } while (item && (item->member.run.nCharOfs + ME_StrLen(item->member.run.strText) <= nOffset));
610   if (item) {
611     nOffset -= item->member.run.nCharOfs;
612     if (nItemOffset)
613       *nItemOffset = nOffset;
614   }
615   return item;
616 }
617
618
619 ME_TextEditor *ME_MakeEditor(HWND hWnd) {
620   ME_TextEditor *ed = ALLOC_OBJ(ME_TextEditor);
621   HDC hDC;
622   int i;
623   ed->hWnd = hWnd;
624   ed->pBuffer = ME_MakeText();
625   hDC = GetDC(hWnd);
626   ME_MakeFirstParagraph(hDC, ed->pBuffer);
627   ReleaseDC(hWnd, hDC);
628   ed->bCaretShown = FALSE;
629   ed->nCursors = 3;
630   ed->pCursors = ALLOC_N_OBJ(ME_Cursor, ed->nCursors);
631   ed->pCursors[0].pRun = ME_FindItemFwd(ed->pBuffer->pFirst, diRun);
632   ed->pCursors[0].nOffset = 0;
633   ed->pCursors[1].pRun = ME_FindItemFwd(ed->pBuffer->pFirst, diRun);
634   ed->pCursors[1].nOffset = 0;
635   ed->nLastTotalLength = ed->nTotalLength = 0;
636   ed->nUDArrowX = -1;
637   ed->nSequence = 0;
638   ed->rgbBackColor = -1;
639   ed->bCaretAtEnd = FALSE;
640   ed->nEventMask = 0;
641   ed->nModifyStep = 0;
642   ed->pUndoStack = ed->pRedoStack = NULL;
643   ed->nUndoMode = umAddToUndo;
644   ed->nParagraphs = 1;
645   ed->nLastSelStart = ed->nLastSelEnd = 0;
646   ed->nScrollPosY = 0;
647   for (i=0; i<HFONT_CACHE_SIZE; i++)
648   {
649     ed->pFontCache[i].nRefs = 0;
650     ed->pFontCache[i].nAge = 0;
651     ed->pFontCache[i].hFont = NULL;
652   }
653   ME_CheckCharOffsets(ed);
654   return ed;
655 }
656
657 typedef struct tagME_GlobalDestStruct
658 {
659   HGLOBAL hData;
660   int nLength;
661 } ME_GlobalDestStruct;
662
663 static DWORD CALLBACK ME_AppendToHGLOBAL(DWORD_PTR dwCookie, LPBYTE lpBuff, LONG cb, LONG *pcb)
664 {
665   ME_GlobalDestStruct *pData = (ME_GlobalDestStruct *)dwCookie;
666   int nMaxSize;
667   BYTE *pDest;
668   
669   nMaxSize = GlobalSize(pData->hData);
670   if (pData->nLength+cb+1 >= cb)
671   {
672     /* round up to 2^17 */
673     int nNewSize = (((nMaxSize+cb+1)|0x1FFFF)+1) & 0xFFFE0000;
674     pData->hData = GlobalReAlloc(pData->hData, nNewSize, 0);
675   }
676   pDest = (BYTE *)GlobalLock(pData->hData);
677   memcpy(pDest + pData->nLength, lpBuff, cb);
678   pData->nLength += cb;
679   pDest[pData->nLength] = '\0';
680   GlobalUnlock(pData->hData);
681   *pcb = cb;
682   
683   return 0;
684 }
685
686 static DWORD CALLBACK ME_ReadFromHGLOBALUnicode(DWORD_PTR dwCookie, LPBYTE lpBuff, LONG cb, LONG *pcb)
687 {
688   ME_GlobalDestStruct *pData = (ME_GlobalDestStruct *)dwCookie;
689   int i;
690   WORD *pSrc, *pDest;
691   
692   cb = cb >> 1;
693   pDest = (WORD *)lpBuff;
694   pSrc = (WORD *)GlobalLock(pData->hData);
695   for (i = 0; i<cb && pSrc[pData->nLength+i]; i++) {
696     pDest[i] = pSrc[pData->nLength+i];
697   }    
698   pData->nLength += i;
699   *pcb = 2*i;
700   GlobalUnlock(pData->hData);
701   return 0;
702 }
703
704 static DWORD CALLBACK ME_ReadFromHGLOBALRTF(DWORD_PTR dwCookie, LPBYTE lpBuff, LONG cb, LONG *pcb)
705 {
706   ME_GlobalDestStruct *pData = (ME_GlobalDestStruct *)dwCookie;
707   int i;
708   BYTE *pSrc, *pDest;
709   
710   pDest = lpBuff;
711   pSrc = (BYTE *)GlobalLock(pData->hData);
712   for (i = 0; i<cb && pSrc[pData->nLength+i]; i++) {
713     pDest[i] = pSrc[pData->nLength+i];
714   }    
715   pData->nLength += i;
716   *pcb = i;
717   GlobalUnlock(pData->hData);
718   return 0;
719 }
720
721 void ME_DestroyEditor(ME_TextEditor *editor)
722 {
723   ME_DisplayItem *pFirst = editor->pBuffer->pFirst;
724   ME_DisplayItem *p = pFirst, *pNext = NULL;
725   int i;
726   
727   ME_ClearTempStyle(editor);
728   ME_EmptyUndoStack(editor);
729   while(p) {
730     pNext = p->next;
731     ME_DestroyDisplayItem(p);    
732     p = pNext;
733   }
734   ME_ReleaseStyle(editor->pBuffer->pDefaultStyle);
735   for (i=0; i<HFONT_CACHE_SIZE; i++)
736   {
737     if (editor->pFontCache[i].hFont)
738       DeleteObject(editor->pFontCache[i].hFont);
739   }
740     
741   FREE_OBJ(editor);
742 }
743
744 #define UNSUPPORTED_MSG(e) \
745   case e: \
746     FIXME(#e ": stub\n"); \
747     return DefWindowProcW(hWnd, msg, wParam, lParam);
748
749 /******************************************************************
750  *        RichEditANSIWndProc (RICHED20.10)
751  */
752 LRESULT WINAPI RichEditANSIWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) {
753   HDC hDC;
754   PAINTSTRUCT ps;
755   SCROLLINFO si;
756   ME_TextEditor *editor = (ME_TextEditor *)GetWindowLongW(hWnd, 0);
757   TRACE("msg %d %08x %08lx\n", msg, wParam, lParam);
758   switch(msg) {
759   
760   UNSUPPORTED_MSG(EM_AUTOURLDETECT)
761   UNSUPPORTED_MSG(EM_CHARFROMPOS)
762   UNSUPPORTED_MSG(EM_DISPLAYBAND)
763   UNSUPPORTED_MSG(EM_EXLIMITTEXT)
764   UNSUPPORTED_MSG(EM_EXLINEFROMCHAR)
765   UNSUPPORTED_MSG(EM_FINDTEXT)
766   UNSUPPORTED_MSG(EM_FINDTEXTEX)
767   UNSUPPORTED_MSG(EM_FINDWORDBREAK)
768   UNSUPPORTED_MSG(EM_FMTLINES)
769   UNSUPPORTED_MSG(EM_FORMATRANGE)
770   UNSUPPORTED_MSG(EM_GETFIRSTVISIBLELINE)
771   UNSUPPORTED_MSG(EM_GETIMECOMPMODE)
772   /* UNSUPPORTED_MSG(EM_GETIMESTATUS) missing in Wine headers */
773   UNSUPPORTED_MSG(EM_GETLANGOPTIONS)
774   UNSUPPORTED_MSG(EM_GETLIMITTEXT)
775   UNSUPPORTED_MSG(EM_GETLINE)
776   UNSUPPORTED_MSG(EM_GETLINECOUNT)
777   /* UNSUPPORTED_MSG(EM_GETOLEINTERFACE) separate stub */
778   UNSUPPORTED_MSG(EM_GETOPTIONS)
779   UNSUPPORTED_MSG(EM_GETRECT)
780   UNSUPPORTED_MSG(EM_GETREDONAME)
781   UNSUPPORTED_MSG(EM_GETTEXTMODE)
782   UNSUPPORTED_MSG(EM_GETUNDONAME)
783   UNSUPPORTED_MSG(EM_GETWORDBREAKPROC)
784   UNSUPPORTED_MSG(EM_GETWORDBREAKPROCEX)
785   UNSUPPORTED_MSG(EM_HIDESELECTION)
786   UNSUPPORTED_MSG(EM_LIMITTEXT) /* also known as EM_SETLIMITTEXT */
787   UNSUPPORTED_MSG(EM_LINEFROMCHAR)
788   UNSUPPORTED_MSG(EM_LINEINDEX)
789   UNSUPPORTED_MSG(EM_LINELENGTH)
790   UNSUPPORTED_MSG(EM_LINESCROLL)
791   UNSUPPORTED_MSG(EM_PASTESPECIAL)
792 /*  UNSUPPORTED_MSG(EM_POSFROMCHARS) missing in Wine headers */
793   UNSUPPORTED_MSG(EM_REQUESTRESIZE)
794   UNSUPPORTED_MSG(EM_SCROLL)
795   UNSUPPORTED_MSG(EM_SCROLLCARET)
796   UNSUPPORTED_MSG(EM_SELECTIONTYPE)
797   UNSUPPORTED_MSG(EM_SETLANGOPTIONS)
798   UNSUPPORTED_MSG(EM_SETOLECALLBACK)
799   UNSUPPORTED_MSG(EM_SETOPTIONS)
800   UNSUPPORTED_MSG(EM_SETRECT)
801   UNSUPPORTED_MSG(EM_SETRECTNP)
802   UNSUPPORTED_MSG(EM_SETTARGETDEVICE)
803   UNSUPPORTED_MSG(EM_SETTEXTMODE)
804   UNSUPPORTED_MSG(EM_SETUNDOLIMIT)
805   UNSUPPORTED_MSG(EM_SETWORDBREAKPROC)
806   UNSUPPORTED_MSG(EM_SETWORDBREAKPROCEX)
807   UNSUPPORTED_MSG(WM_SETFONT)
808   UNSUPPORTED_MSG(WM_STYLECHANGING)
809   UNSUPPORTED_MSG(WM_STYLECHANGED)
810 /*  UNSUPPORTED_MSG(WM_UNICHAR) FIXME missing in Wine headers */
811     
812 /* Messages specific to Richedit controls */
813   
814   case EM_STREAMIN:
815    return ME_StreamIn(editor, wParam, (EDITSTREAM*)lParam);
816   case EM_STREAMOUT:
817    return ME_StreamOut(editor, wParam, (EDITSTREAM *)lParam);
818   case WM_GETDLGCODE:
819   {
820     UINT code = DLGC_WANTCHARS|DLGC_WANTARROWS;
821     if (GetWindowLongW(hWnd, GWL_STYLE)&ES_WANTRETURN)
822       code |= 0; /* FIXME what can we do here ? ask for messages and censor them ? */
823     return code;
824   }
825   case WM_NCCREATE:
826   {
827     CREATESTRUCTW *pcs = (CREATESTRUCTW *)lParam;
828     editor = ME_MakeEditor(hWnd);
829     SetWindowLongW(hWnd, 0, (long)editor);
830     pcs = 0; /* ignore */
831     return TRUE;
832   }
833   case EM_EMPTYUNDOBUFFER:
834     ME_EmptyUndoStack(editor);
835     return 0;
836   case EM_GETSEL:
837   {
838     ME_GetSelection(editor, (int *)wParam, (int *)lParam);
839     if (!((wParam|lParam) & 0xFFFF0000))
840       return (lParam<<16)|wParam;
841     return -1;
842   }
843   case EM_EXGETSEL:
844   {
845     CHARRANGE *pRange = (CHARRANGE *)lParam;
846     ME_GetSelection(editor, (int *)&pRange->cpMin, (int *)&pRange->cpMax);
847     return 0;
848   }
849   case EM_CANUNDO:
850     return editor->pUndoStack != NULL;
851   case EM_CANREDO:
852     return editor->pRedoStack != NULL;
853   case EM_UNDO:
854     ME_Undo(editor);
855     return 0;
856   case EM_REDO:
857     ME_Redo(editor);
858     return 0;
859   case EM_SETSEL:
860   {
861     ME_SetSelection(editor, wParam, lParam);
862     ME_Repaint(editor);
863     ME_SendSelChange(editor);
864     return 0;
865   }
866   case EM_EXSETSEL:
867   {
868     CHARRANGE *pRange = (CHARRANGE *)lParam;
869     ME_SetSelection(editor, pRange->cpMin, pRange->cpMax);
870     /* FIXME optimize */
871     ME_Repaint(editor);
872     ME_SendSelChange(editor);
873     return 0;
874   }
875   case EM_SETBKGNDCOLOR:
876   {
877     LRESULT lColor = ME_GetBackColor(editor);
878     if (wParam)
879       editor->rgbBackColor = -1;
880     else
881       editor->rgbBackColor = lParam; 
882     InvalidateRect(hWnd, NULL, TRUE);
883     UpdateWindow(hWnd);
884     return lColor;
885   }
886   case EM_GETMODIFY:
887     return editor->nModifyStep == 0 ? 0 : 1;
888   case EM_SETMODIFY:
889   {
890     if (wParam)
891       editor->nModifyStep = 0x80000000;
892     else
893       editor->nModifyStep = 0;
894     
895     return 0;
896   }
897   case EM_SETREADONLY:
898   {
899     long nStyle = GetWindowLongW(hWnd, GWL_STYLE);
900     if (wParam)
901       nStyle |= ES_READONLY;
902     else
903       nStyle &= ~ES_READONLY;
904     SetWindowLongW(hWnd, GWL_STYLE, nStyle);
905     ME_Repaint(editor);
906     return 0;
907   }
908   case EM_SETEVENTMASK:
909     editor->nEventMask = lParam;
910     return 0;
911   case EM_GETEVENTMASK:
912     return editor->nEventMask;
913   case EM_SETCHARFORMAT:
914   {
915     CHARFORMAT2W buf, *p;
916     BOOL bRepaint = TRUE;
917     p = ME_ToCF2W(&buf, (CHARFORMAT2W *)lParam);
918     if (!wParam)
919       ME_SetDefaultCharFormat(editor, p);
920     else if (wParam == (SCF_WORD | SCF_SELECTION))
921       FIXME("word selection not supported\n");
922     else if (wParam == SCF_ALL)
923       ME_SetCharFormat(editor, 0, ME_GetTextLength(editor), p);
924     else {
925       int from, to;
926       ME_GetSelection(editor, &from, &to);
927       bRepaint = (from != to);
928       ME_SetSelectionCharFormat(editor, p);
929     }
930     ME_CommitUndo(editor);
931     if (bRepaint)
932       ME_UpdateRepaint(editor);
933     return 0;
934   }
935   case EM_GETCHARFORMAT:
936   {
937     CHARFORMAT2W tmp;
938     tmp.cbSize = sizeof(tmp);
939     if (!wParam)
940       ME_GetDefaultCharFormat(editor, &tmp);
941     else
942       ME_GetSelectionCharFormat(editor, &tmp);
943     ME_CopyToCFAny((CHARFORMAT2W *)lParam, &tmp);
944     return 0;
945   }
946   case EM_SETPARAFORMAT:
947     ME_SetSelectionParaFormat(editor, (PARAFORMAT2 *)lParam);
948     ME_UpdateRepaint(editor);
949     ME_CommitUndo(editor);
950     return 0;
951   case EM_GETPARAFORMAT:
952     ME_GetSelectionParaFormat(editor, (PARAFORMAT2 *)lParam);
953     return 0;
954   case WM_CLEAR:
955   {
956     int from, to;
957     ME_GetSelection(editor, &from, &to);
958     ME_InternalDeleteText(editor, from, to-from);
959     ME_CommitUndo(editor);
960     ME_UpdateRepaint(editor);
961     return 0;
962   }
963   case EM_REPLACESEL:
964   {
965     int from, to;
966     ME_Style *style;
967     LPWSTR wszText = ME_ToUnicode(hWnd, (void *)lParam);
968     size_t len = lstrlenW(wszText);
969     TRACE("EM_REPLACESEL - %s\n", debugstr_w(wszText));
970     
971     ME_GetSelection(editor, &from, &to);
972     style = ME_GetSelectionInsertStyle(editor);
973     ME_InternalDeleteText(editor, from, to-from);
974     ME_InsertTextFromCursor(editor, 0, wszText, len, style);
975     ME_ReleaseStyle(style);
976     ME_EndToUnicode(hWnd, wszText);
977     /* drop temporary style if line end */
978     /* FIXME question: does abc\n mean: put abc, clear temp style, put \n? (would require a change) */  
979     if (len>0 && wszText[len-1] == '\n')
980       ME_ClearTempStyle(editor);
981       
982     ME_CommitUndo(editor);
983     if (!wParam)
984       ME_EmptyUndoStack(editor);
985     ME_UpdateRepaint(editor);
986     return 0;
987   }
988   case WM_SETTEXT:
989   {
990     LPWSTR wszText = ME_ToUnicode(hWnd, (void *)lParam);
991     TRACE("WM_SETTEXT - %s\n", (char *)(wszText)); /* debugstr_w() */
992     ME_InternalDeleteText(editor, 0, ME_GetTextLength(editor));
993     /* uses default style! */
994     ME_InsertTextFromCursor(editor, 0, wszText, -1, editor->pBuffer->pDefaultStyle);
995     ME_EndToUnicode(hWnd, wszText);
996     ME_CommitUndo(editor);
997     ME_EmptyUndoStack(editor);
998     ME_UpdateRepaint(editor);
999     return 0;
1000   }
1001   case EM_CANPASTE:
1002   {
1003     UINT nRTFFormat = RegisterClipboardFormatA("Rich Text Format");
1004     if (IsClipboardFormatAvailable(nRTFFormat))
1005       return TRUE;
1006     if (IsClipboardFormatAvailable(CF_UNICODETEXT))
1007       return TRUE;
1008     return FALSE;
1009   }
1010   case WM_PASTE:
1011   {    
1012     DWORD dwFormat = 0;
1013     EDITSTREAM es;
1014     ME_GlobalDestStruct gds;
1015     UINT nRTFFormat = RegisterClipboardFormatA("Rich Text Format");
1016     UINT cf = 0;
1017
1018     if (IsClipboardFormatAvailable(nRTFFormat))
1019       cf = nRTFFormat, dwFormat = SF_RTF;
1020     else if (IsClipboardFormatAvailable(CF_UNICODETEXT))
1021       cf = CF_UNICODETEXT, dwFormat = SF_TEXT|SF_UNICODE;
1022     else
1023       return 0;
1024
1025     if (!OpenClipboard(hWnd))
1026       return 0;
1027     gds.hData = GetClipboardData(cf);
1028     gds.nLength = 0;
1029     es.dwCookie = (DWORD)&gds;
1030     es.pfnCallback = dwFormat == SF_RTF ? ME_ReadFromHGLOBALRTF : ME_ReadFromHGLOBALUnicode;
1031     SendMessageW(hWnd, EM_STREAMIN, dwFormat|SFF_SELECTION, (LPARAM)&es);
1032     
1033     CloseClipboard();
1034     return 0;
1035   }
1036   case WM_CUT:
1037   case WM_COPY:
1038   {
1039     int from, to, pars;
1040     WCHAR *data;
1041     HANDLE hData;
1042     EDITSTREAM es;
1043     ME_GlobalDestStruct gds;
1044     
1045     if (!OpenClipboard(hWnd))
1046       return 0;
1047       
1048     EmptyClipboard();
1049     ME_GetSelection(editor, &from, &to);
1050     pars = ME_CountParagraphsBetween(editor, from, to);
1051     hData = GlobalAlloc(GMEM_MOVEABLE, sizeof(WCHAR)*(to-from+pars+1));
1052     data = (WCHAR *)GlobalLock(hData);
1053     ME_GetTextW(editor, data, from, to-from, TRUE);
1054     GlobalUnlock(hData);
1055
1056     gds.hData = GlobalAlloc(GMEM_MOVEABLE, 0);
1057     gds.nLength = 0;
1058     es.dwCookie = (DWORD)&gds;
1059     es.pfnCallback = ME_AppendToHGLOBAL;
1060     SendMessageW(hWnd, EM_STREAMOUT, SFF_SELECTION|SF_RTF, (LPARAM)&es);
1061     GlobalReAlloc(gds.hData, gds.nLength+1, 0);
1062     
1063     SetClipboardData(CF_UNICODETEXT, hData);    
1064     SetClipboardData(RegisterClipboardFormatA("Rich Text Format"), gds.hData);
1065     
1066     CloseClipboard();
1067     if (msg == WM_CUT)
1068     {
1069       ME_InternalDeleteText(editor, from, to-from);
1070       ME_CommitUndo(editor);
1071       ME_UpdateRepaint(editor);
1072     }
1073     return 0;
1074   }
1075   case WM_GETTEXTLENGTH:
1076     return ME_GetTextLength(editor);
1077   case WM_GETTEXT:
1078   {
1079     TEXTRANGEW tr; /* W and A differ only by rng->lpstrText */
1080     tr.chrg.cpMin = 0;
1081     tr.chrg.cpMax = wParam-1;
1082     tr.lpstrText = (WCHAR *)lParam;
1083     return RichEditANSIWndProc(hWnd, EM_GETTEXTRANGE, 0, (LPARAM)&tr);
1084   }
1085   case EM_GETSELTEXT:
1086   {
1087     int from, to;
1088     TEXTRANGEW tr; /* W and A differ only by rng->lpstrText */
1089     ME_GetSelection(editor, &from, &to);
1090     tr.chrg.cpMin = from;
1091     tr.chrg.cpMax = to;
1092     tr.lpstrText = (WCHAR *)lParam;
1093     return RichEditANSIWndProc(hWnd, EM_GETTEXTRANGE, 0, (LPARAM)&tr);
1094   }
1095   case EM_GETTEXTRANGE:
1096   {
1097     TEXTRANGEW *rng = (TEXTRANGEW *)lParam;
1098     if (IsWindowUnicode(hWnd))
1099       return ME_GetTextW(editor, rng->lpstrText, rng->chrg.cpMin, rng->chrg.cpMax-rng->chrg.cpMin, FALSE);
1100     else
1101     {
1102       int nLen = rng->chrg.cpMax-rng->chrg.cpMin;
1103       WCHAR *p = ALLOC_N_OBJ(WCHAR, nLen+1);
1104       int nChars = ME_GetTextW(editor, p, rng->chrg.cpMin, nLen, FALSE);
1105       /* FIXME this is a potential security hole (buffer overrun) 
1106          if you know more about wchar->mbyte conversion please explain
1107       */
1108       WideCharToMultiByte(CP_ACP, 0, p, nChars+1, (char *)rng->lpstrText, nLen+1, NULL, NULL);
1109       FREE_OBJ(p);
1110       return nChars;
1111     }
1112     return ME_GetTextW(editor, rng->lpstrText, rng->chrg.cpMin, rng->chrg.cpMax-rng->chrg.cpMin, FALSE);
1113   }
1114   case WM_CREATE:
1115     ME_CommitUndo(editor);
1116     ME_WrapMarkedParagraphs(editor);
1117     ME_MoveCaret(editor);
1118     return 0;
1119   case WM_DESTROY:
1120     ME_DestroyEditor(editor);
1121     SetWindowLongW(hWnd, 0, 0);
1122     return 0;
1123   case WM_LBUTTONDOWN:
1124     SetFocus(hWnd);
1125     ME_LButtonDown(editor, (short)LOWORD(lParam), (short)HIWORD(lParam));
1126     SetCapture(hWnd);
1127     break;
1128   case WM_MOUSEMOVE:
1129     if (GetCapture() == hWnd)
1130       ME_MouseMove(editor, (short)LOWORD(lParam), (short)HIWORD(lParam));
1131     break;
1132   case WM_LBUTTONUP:
1133     if (GetCapture() == hWnd)
1134       ReleaseCapture();
1135     break;
1136   case WM_PAINT:
1137     hDC = BeginPaint(hWnd, &ps);
1138     ME_PaintContent(editor, hDC, FALSE, &ps.rcPaint);
1139     EndPaint(hWnd, &ps);
1140     break;
1141   case WM_SETFOCUS:
1142     ME_ShowCaret(editor);
1143     ME_SendOldNotify(editor, EN_SETFOCUS);
1144     return 0;
1145   case WM_KILLFOCUS:
1146     ME_HideCaret(editor);
1147     ME_SendOldNotify(editor, EN_KILLFOCUS);
1148     return 0;
1149   case WM_ERASEBKGND:
1150   {
1151     HDC hDC = (HDC)wParam;
1152     RECT rc;
1153     COLORREF rgbBG = ME_GetBackColor(editor);
1154     if (GetUpdateRect(hWnd,&rc,TRUE))
1155     {
1156       HBRUSH hbr = CreateSolidBrush(rgbBG);
1157       FillRect(hDC, &rc, hbr);
1158       DeleteObject(hbr);
1159     }
1160     return 1;
1161   }
1162   case WM_COMMAND:
1163     TRACE("editor wnd command = %d\n", LOWORD(wParam));
1164     return 0;
1165   case WM_KEYDOWN:
1166     if (ME_ArrowKey(editor, LOWORD(wParam), GetKeyState(VK_CONTROL)<0)) {
1167       ME_CommitUndo(editor);
1168       ME_EnsureVisible(editor, editor->pCursors[0].pRun);
1169       HideCaret(hWnd);
1170       ME_MoveCaret(editor);
1171       ShowCaret(hWnd);
1172       return 0;
1173     }
1174     if (GetKeyState(VK_CONTROL)<0)
1175     {
1176       if (LOWORD(wParam)=='W')
1177       {
1178         CHARFORMAT2W chf;
1179         char buf[2048];
1180         ME_GetSelectionCharFormat(editor, &chf);
1181         ME_DumpStyleToBuf(&chf, buf);
1182         MessageBoxA(NULL, buf, "Style dump", MB_OK);
1183       }
1184       if (LOWORD(wParam)=='Q')
1185       {
1186         ME_CheckCharOffsets(editor);
1187       }
1188     }
1189     goto do_default;
1190   case WM_CHAR: 
1191   {
1192     WCHAR wstr;
1193     if (GetWindowLongW(editor->hWnd, GWL_STYLE) & ES_READONLY) {
1194       MessageBeep(MB_ICONERROR);
1195       return 0; /* FIXME really 0 ? */
1196     }
1197     wstr = LOWORD(wParam);
1198     if (((unsigned)wstr)>=' ' || wstr=='\r' || wstr=='\t') {
1199       /* FIXME maybe it would make sense to call EM_REPLACESEL instead ? */
1200       ME_Style *style = ME_GetInsertStyle(editor, 0);
1201       ME_SaveTempStyle(editor);
1202       ME_InsertTextFromCursor(editor, 0, &wstr, 1, style);
1203       ME_ReleaseStyle(style);
1204       ME_CommitUndo(editor);
1205       ME_UpdateRepaint(editor);
1206     }
1207     return 0;
1208   }
1209   case WM_VSCROLL: 
1210   {
1211     int nPos = editor->nScrollPosY;
1212     si.cbSize = sizeof(SCROLLINFO);
1213     si.fMask = SIF_PAGE|SIF_POS|SIF_RANGE|SIF_TRACKPOS;
1214     GetScrollInfo(hWnd, SB_VERT, &si);
1215     switch(LOWORD(wParam)) {
1216     case SB_LINEUP:
1217       nPos -= 24; /* FIXME follow the original */
1218       if (nPos<0) nPos = 0;
1219       break;
1220     case SB_LINEDOWN:
1221     {
1222       int nEnd = editor->nTotalLength - editor->sizeWindow.cy;
1223       nPos += 24; /* FIXME follow the original */
1224       if (nPos>=nEnd) nPos = nEnd;
1225       break;
1226     }
1227     case SB_PAGEUP:
1228       nPos -= editor->sizeWindow.cy;
1229       if (nPos<0) nPos = 0;
1230       break;
1231     case SB_PAGEDOWN:
1232       nPos += editor->sizeWindow.cy;
1233       if (nPos>=editor->nTotalLength) nPos = editor->nTotalLength-1;
1234       break;
1235     case SB_THUMBTRACK:
1236     case SB_THUMBPOSITION:
1237       nPos = si.nTrackPos;
1238       break;
1239     }
1240     if (nPos != editor->nScrollPosY) {
1241       ScrollWindow(hWnd, 0, editor->nScrollPosY-nPos, NULL, NULL);
1242       editor->nScrollPosY = nPos;
1243       SetScrollPos(hWnd, SB_VERT, nPos, TRUE);
1244       UpdateWindow(hWnd);
1245     }
1246     break;
1247   }
1248   case WM_MOUSEWHEEL:
1249   {
1250     int gcWheelDelta = 0, nPos = editor->nScrollPosY;
1251     UINT pulScrollLines;
1252
1253     SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
1254     gcWheelDelta -= GET_WHEEL_DELTA_WPARAM(wParam);
1255     if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
1256       nPos += pulScrollLines * (gcWheelDelta / WHEEL_DELTA) * 8;
1257     if(nPos>=editor->nTotalLength)
1258       nPos = editor->nTotalLength - 1;
1259     if (nPos<0) 
1260       nPos = 0;
1261     if (nPos != editor->nScrollPosY) {
1262       ScrollWindow(hWnd, 0, editor->nScrollPosY-nPos, NULL, NULL);
1263       editor->nScrollPosY = nPos;
1264       SetScrollPos(hWnd, SB_VERT, nPos, TRUE);
1265       UpdateWindow(hWnd);
1266     }
1267     break;
1268   }
1269   case WM_SIZE:
1270   {
1271     ME_MarkAllForWrapping(editor);
1272     ME_WrapMarkedParagraphs(editor);
1273     ME_UpdateScrollBar(editor);
1274     ME_Repaint(editor);
1275     return DefWindowProcW(hWnd, msg, wParam, lParam);
1276   }
1277   case EM_GETOLEINTERFACE:
1278   {
1279     LPVOID *ppvObj = (LPVOID*) lParam;
1280     FIXME("EM_GETOLEINTERFACE %p: stub\n", ppvObj);
1281     return CreateIRichEditOle(ppvObj);
1282   }
1283   default:
1284   do_default:
1285     return DefWindowProcW(hWnd, msg, wParam, lParam);
1286   }
1287   return 0L;
1288 }
1289
1290 /******************************************************************
1291  *        RichEdit10ANSIWndProc (RICHED20.9)
1292  */
1293 LRESULT WINAPI RichEdit10ANSIWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
1294 {
1295   /* FIXME: this is NOT the same as 2.0 version */
1296   return RichEditANSIWndProc(hWnd, msg, wParam, lParam);
1297 }
1298
1299 void ME_SendOldNotify(ME_TextEditor *editor, int nCode)
1300 {
1301   HWND hWnd = editor->hWnd;
1302   SendMessageA(GetParent(hWnd), WM_COMMAND, (nCode<<16)|GetWindowLongW(hWnd, GWLP_ID), (LPARAM)hWnd);
1303 }
1304
1305 int ME_CountParagraphsBetween(ME_TextEditor *editor, int from, int to)
1306 {
1307   ME_DisplayItem *item = ME_FindItemFwd(editor->pBuffer->pFirst, diParagraph);
1308   int i = 0;
1309   
1310   while(item && item->member.para.next_para->member.para.nCharOfs <= from)
1311     item = item->member.para.next_para;
1312   if (!item)
1313     return 0;
1314   while(item && item->member.para.next_para->member.para.nCharOfs <= to) {
1315     item = item->member.para.next_para;
1316     i++;
1317   }
1318   return i;
1319 }
1320
1321
1322 int ME_GetTextW(ME_TextEditor *editor, WCHAR *buffer, int nStart, int nChars, int bCRLF)
1323 {
1324   ME_DisplayItem *item = ME_FindItemAtOffset(editor, diRun, nStart, &nStart);
1325   int nWritten = 0;
1326   
1327   if (!item) {
1328     *buffer = L'\0';
1329     return 0;
1330   }
1331   assert(item);    
1332   
1333   if (nStart)
1334   {
1335     int nLen = ME_StrLen(item->member.run.strText) - nStart;
1336     if (nLen > nChars)
1337       nLen = nChars;
1338     CopyMemory(buffer, item->member.run.strText->szData + nStart, sizeof(WCHAR)*nLen);
1339     nChars -= nLen;
1340     nWritten += nLen;
1341     if (!nChars)
1342       return nWritten;
1343     buffer += nLen;
1344     nStart = 0;
1345     item = ME_FindItemFwd(item, diRun);
1346   }
1347   
1348   while(nChars && item)
1349   {
1350     int nLen = ME_StrLen(item->member.run.strText);
1351     if (nLen > nChars)
1352       nLen = nChars;
1353       
1354     if (item->member.run.nFlags & MERF_ENDPARA)
1355     {
1356       if (bCRLF) {
1357         *buffer++ = '\r';
1358         nWritten++;
1359       }        
1360       *buffer = '\n';
1361       assert(nLen == 1);
1362     }
1363     else      
1364       CopyMemory(buffer, item->member.run.strText->szData, sizeof(WCHAR)*nLen);
1365     nChars -= nLen;
1366     nWritten += nLen;
1367     buffer += nLen;    
1368       
1369     if (!nChars)
1370     {
1371       *buffer = L'\0';
1372       return nWritten;
1373     }
1374     item = ME_FindItemFwd(item, diRun);
1375   }
1376   *buffer = L'\0';
1377   return nWritten;  
1378 }
1379
1380 static WCHAR wszClassName[] = {'R', 'i', 'c', 'h', 'E', 'd', 'i', 't', '2', '0', 'W', 0};
1381 static WCHAR wszClassName50[] = {'R', 'i', 'c', 'h', 'E', 'd', 'i', 't', '5', '0', 'W', 0};
1382
1383 void ME_RegisterEditorClass(HINSTANCE hInstance)
1384 {
1385   BOOL bResult;
1386   WNDCLASSW wcW;
1387   WNDCLASSA wcA;
1388   
1389   wcW.style = CS_HREDRAW | CS_VREDRAW;
1390   wcW.lpfnWndProc = RichEditANSIWndProc;
1391   wcW.cbClsExtra = 0;
1392   wcW.cbWndExtra = 4;
1393   wcW.hInstance = NULL; /* hInstance would register DLL-local class */
1394   wcW.hIcon = NULL;
1395   wcW.hCursor = LoadCursorW(NULL, MAKEINTRESOURCEW(IDC_IBEAM));
1396   wcW.hbrBackground = (HBRUSH)GetStockObject(NULL_BRUSH);
1397   wcW.lpszMenuName = NULL;
1398   wcW.lpszClassName = wszClassName;
1399   bResult = RegisterClassW(&wcW);  
1400   assert(bResult);
1401   wcW.lpszClassName = wszClassName50;
1402   bResult = RegisterClassW(&wcW);  
1403   assert(bResult);
1404
1405   wcA.style = CS_HREDRAW | CS_VREDRAW;
1406   wcA.lpfnWndProc = RichEditANSIWndProc;
1407   wcA.cbClsExtra = 0;
1408   wcA.cbWndExtra = 4;
1409   wcA.hInstance = NULL; /* hInstance would register DLL-local class */
1410   wcA.hIcon = NULL;
1411   wcA.hCursor = LoadCursorW(NULL, MAKEINTRESOURCEW(IDC_IBEAM));
1412   wcA.hbrBackground = (HBRUSH)GetStockObject(NULL_BRUSH);
1413   wcA.lpszMenuName = NULL;
1414   wcA.lpszClassName = "RichEdit20A";
1415   bResult = RegisterClassA(&wcA);  
1416   assert(bResult);
1417   wcA.lpszClassName = "RichEdit50A";
1418   bResult = RegisterClassA(&wcA);  
1419   assert(bResult);
1420 }
1421
1422 BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
1423 {
1424     TRACE("\n");
1425     switch (fdwReason)
1426     {
1427     case DLL_PROCESS_ATTACH:
1428       DisableThreadLibraryCalls(hinstDLL);
1429       me_heap = HeapCreate (0, 0x10000, 0);
1430       ME_RegisterEditorClass(hinstDLL);
1431       break;
1432
1433     case DLL_PROCESS_DETACH:
1434       UnregisterClassW(wszClassName, 0);
1435       UnregisterClassW(wszClassName50, 0);
1436       UnregisterClassA("RichEdit20A", 0);
1437       UnregisterClassA("RichEdit50A", 0);
1438       HeapDestroy (me_heap);
1439       me_heap = NULL;
1440       break;
1441     }
1442     return TRUE;
1443 }
1444
1445 /******************************************************************
1446  *        CreateTextServices (RICHED20.4)
1447  *
1448  * FIXME should be ITextHost instead of void*
1449  */
1450 HRESULT WINAPI CreateTextServices(IUnknown *punkOuter, void *pITextHost,
1451     IUnknown **ppUnk)
1452 {
1453   FIXME("stub\n");
1454   /* FIXME should support aggregation */
1455   if (punkOuter)
1456     return CLASS_E_NOAGGREGATION;
1457     
1458   return E_FAIL; /* E_NOTIMPL isn't allowed by MSDN */
1459 }
1460
1461 /******************************************************************
1462  *        REExtendedRegisterClass (RICHED20.8)
1463  *
1464  * FIXME undocumented
1465  */
1466 void WINAPI REExtendedRegisterClass(void)
1467 {
1468   FIXME("stub\n");
1469 }