Implement EM_GETOLEINTERFACE.
[wine] / dlls / riched20 / editor.c
1 /*
2  * RichEdit - functions dealing with editor object
3  *
4  * Copyright 2004 by Krzysztof Foltman
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 /* 
22   API implementation status:
23   
24   Messages (ANSI versions not done yet)
25   - EM_AUTOURLDETECT 2.0
26   - EM_CANPASTE
27   + EM_CANREDO 2.0
28   + EM_CANUNDO
29   - EM_CHARFROMPOS
30   - EM_DISPLAYBAND
31   + EM_EMPTYUNDOBUFFER
32   + EM_EXGETSEL
33   - EM_EXLIMITTEXT
34   - EM_EXLINEFROMCHAR
35   + EM_EXSETSEL
36   - EM_FINDTEXT
37   - EM_FINDTEXTEX
38   - EM_FINDWORDBREAK
39   - EM_FMTLINES
40   - EM_FORMATRANGE
41   - EM_GETCHARFORMAT (partly done)
42   + EM_GETEVENTMASK
43   - EM_GETFIRSTVISIBLELINE
44   - EM_GETIMECOLOR 1.0asian
45   - EM_GETIMECOMPMODE 2.0
46   - EM_GETIMEOPTIONS 1.0asian
47   - EM_GETIMESTATUS
48   - EM_GETLANGOPTIONS 2.0
49   - EM_GETLIMITTEXT
50   - EM_GETLINE        
51   - EM_GETLINECOUNT   returns number of rows, not of paragraphs
52   + EM_GETMODIFY
53   - EM_GETOLEINTERFACE
54   - EM_GETOPTIONS
55   + EM_GETPARAFORMAT
56   - EM_GETPUNCTUATION 1.0asian
57   - EM_GETRECT
58   - EM_GETREDONAME 2.0
59   + EM_GETSEL
60   + EM_GETSELTEXT (ANSI&Unicode)
61 ! - EM_GETTHUMB
62   - EM_GETTEXTMODE 2.0
63 ? + EM_GETTEXTRANGE (ANSI&Unicode)
64   - EM_GETUNDONAME
65   - EM_GETWORDBREAKPROC
66   - EM_GETWORDBREAKPROCEX
67   - EM_GETWORDWRAPMODE 1.0asian
68   - EM_HIDESELECTION
69   - EM_LIMITTEXT
70   - EM_LINEFROMCHAR
71   - EM_LINEINDEX
72   - EM_LINELENGTH
73   - EM_LINESCROLL
74   - EM_PASTESPECIAL
75   - EM_POSFROMCHARS
76   - EM_REDO 2.0
77   - EM_REQUESTRESIZE
78   + EM_REPLACESEL (proper style?) ANSI&Unicode
79   - EM_SCROLL
80   - EM_SCROLLCARET
81   - EM_SELECTIONTYPE
82   + EM_SETBKGNDCOLOR
83   - EM_SETCHARFORMAT (partly done, no ANSI)
84   + EM_SETEVENTMASK (few notifications supported)
85   - EM_SETIMECOLOR 1.0asian
86   - EM_SETIMEOPTIONS 1.0asian
87   - EM_SETLANGOPTIONS 2.0
88   - EM_SETLIMITTEXT
89   + EM_SETMODIFY (not sure if implementation is correct)
90   - EM_SETOLECALLBACK
91   - EM_SETOPTIONS
92   + EM_SETPARAFORMAT
93   - EM_SETPUNCTUATION 1.0asian
94   + EM_SETREADONLY no beep on modification attempt
95   - EM_SETRECT
96   - EM_SETRECTNP (EM_SETRECT without repainting) - not supported in RICHEDIT
97   + EM_SETSEL
98   - EM_SETTARGETDEVICE
99   - EM_SETTEXTMODE 2.0
100   - EM_SETUNDOLIMIT 2.0
101   - EM_SETWORDBREAKPROC
102   - EM_SETWORDBREAKPROCEX
103   - EM_SETWORDWRAPMODE 1.0asian
104   - EM_STOPGROUPTYPING 2.0
105   - EM_STREAMIN
106   - EM_STREAMOUT
107   - EM_UNDO
108   + WM_CHAR
109   + WM_CLEAR
110   - WM_COPY (lame implementation, no RTF support)
111   - WM_CUT (lame implementation, no RTF support)
112   + WM_GETDLGCODE (the current implementation is incomplete)
113   + WM_GETTEXT (ANSI&Unicode)
114   + WM_GETTEXTLENGTH (ANSI version sucks)
115   - WM_PASTE
116   - WM_SETFONT
117   + WM_SETTEXT (resets undo stack !) (proper style?) ANSI&Unicode
118   - WM_STYLECHANGING
119   - WM_STYLECHANGED (things like read-only flag)
120   - WM_UNICHAR
121   
122   Notifications
123   
124   * EN_CHANGE (sent from the wrong place)
125   - EN_CORRECTTEXT
126   - EN_DROPFILES
127   - EN_ERRSPACE
128   - EN_HSCROLL
129   - EN_IMECHANGE
130   + EN_KILLFOCUS
131   - EN_LINK
132   - EN_MAXTEXT
133   - EN_MSGFILTER
134   - EN_OLEOPFAILED
135   - EN_PROTECTED
136   - EN_REQUESTRESIZE
137   - EN_SAVECLIPBOARD
138   + EN_SELCHANGE 
139   + EN_SETFOCUS
140   - EN_STOPNOUNDO
141   * EN_UPDATE (sent from the wrong place)
142   - EN_VSCROLL
143   
144   Styles
145   
146   - ES_AUTOHSCROLL
147   - ES_AUTOVSCROLL
148   - ES_CENTER
149   - ES_DISABLENOSCROLL (scrollbar is always visible)
150   - ES_EX_NOCALLOLEINIT
151   - ES_LEFT
152   - ES_MULTILINE (currently single line controls aren't supported)
153   - ES_NOIME
154   - ES_READONLY (I'm not sure if beeping is the proper behaviour)
155   - ES_RIGHT
156   - ES_SAVESEL
157   - ES_SELFIME
158   - ES_SUNKEN
159   - ES_VERTICAL
160   - ES_WANTRETURN (don't know how to do WM_GETDLGCODE part)
161   - WS_SETFONT
162   - WS_HSCROLL
163   - WS_VSCROLL
164 */
165
166 /*
167  * RICHED20 TODO (incomplete):
168  *
169  * - font caching
170  * - add remaining CHARFORMAT/PARAFORMAT fields
171  * - right/center align should strip spaces from the beginning
172  * - more advanced navigation (Ctrl-arrows, PageUp/PageDn)
173  * - tabs
174  * - pictures (not just smiling faces that lack API support ;-) )
175  * - OLE objects
176  * - calculate heights of pictures (half-done)
177  * - EM_STREAMIN/EM_STREAMOUT
178  * - horizontal scrolling (not even started)
179  * - fix scrollbars and refresh (it sucks bigtime)
180  * - hysteresis during wrapping (related to scrollbars appearing/disappearing)
181  * - should remember maximum row width for wrap hysteresis
182  * - find/replace
183  * - how to implement EM_FORMATRANGE and EM_DISPLAYBAND ? (Mission Impossible)
184  * - italic cursor with italic fonts
185  * - IME
186  * - most notifications aren't sent at all (the most important ones are)
187  * - when should EN_SELCHANGE be sent after text change ? (before/after EN_UPDATE?)
188  * - WM_SETTEXT may use wrong style (but I'm 80% sure it's OK)
189  * - EM_GETCHARFORMAT with SCF_SELECTION may not behave 100% like in original (but very close)
190  * - bugs in end-of-text handling (the gray bar) could get me in jail ;-)
191  * - determination of row size
192  * - end-of-paragraph marks should be of reasonable size
193  *
194  * Bugs that are probably fixed, but not so easy to verify:
195  * - EN_UPDATE/EN_CHANGE are handled very incorrectly (should be OK now)
196  * - undo for ME_JoinParagraphs doesn't store paragraph format ? (it does)
197  * - check/fix artificial EOL logic (bCursorAtEnd, hardly logical)
198  * - caret shouldn't be displayed when selection isn't empty
199  * - check refcounting in style management functions (looks perfect now, but no bugs is suspicious)
200  * - undo for setting default format (done, might be buggy)
201  * - styles might be not released properly (looks like they work like charm, but who knows?
202  *
203  */
204
205 #include "editor.h"
206 #include <ole2.h>
207 #include <richole.h>
208 #include <winreg.h>
209 #define NO_SHLWAPI_STREAM 
210 #include <shlwapi.h>
211
212 #include "rtf.h"
213  
214 WINE_DEFAULT_DEBUG_CHANNEL(richedit);
215
216 int me_debug = 0;
217 HANDLE me_heap = NULL;
218
219 void DoWrap(ME_TextEditor *editor) {
220   HDC hDC = GetDC(editor->hWnd);
221   ME_DisplayItem *item;
222   ME_Context c;
223   HWND hWnd = editor->hWnd;
224   int yLength = editor->nTotalLength;
225   int nSelFrom, nSelTo;
226   int nMinSel, nMaxSel;
227   
228   ME_GetSelection(editor, &nSelFrom, &nSelTo);
229   
230   nMinSel = nSelFrom < editor->nOldSelFrom ? nSelFrom : editor->nOldSelFrom;
231   nMaxSel = nSelTo > editor->nOldSelTo ? nSelTo : editor->nOldSelTo;
232   
233   ME_InitContext(&c, editor, hDC);
234   c.pt.x = 0;
235   c.pt.y = 0;
236   item = editor->pBuffer->pFirst->next;
237   while(item != editor->pBuffer->pLast) {
238     int para_from, para_to;
239     BOOL bRedraw = FALSE;
240     
241     para_from = item->member.para.nCharOfs;
242     para_to = item->member.para.next_para->member.para.nCharOfs;
243     
244     if (para_from <= nMaxSel && para_to >= nMinSel && nMinSel != nMaxSel)
245       bRedraw = TRUE;
246     
247     assert(item->type == diParagraph);
248     if (!(item->member.para.nFlags & MEPF_WRAPPED)
249      || (item->member.para.nYPos != c.pt.y))
250       bRedraw = TRUE;
251     item->member.para.nYPos = c.pt.y;
252     
253     ME_WrapTextParagraph(&c, item);
254
255     if (bRedraw) {
256       item->member.para.nFlags |= MEPF_REDRAW;
257     }
258     c.pt.y = item->member.para.nYPos + item->member.para.nHeight;
259     item = item->member.para.next_para;
260   }
261   editor->sizeWindow.cx = c.rcView.right-c.rcView.left;
262   editor->sizeWindow.cy = c.rcView.bottom-c.rcView.top;
263   editor->nTotalLength = c.pt.y-c.rcView.top;
264   
265   ME_UpdateScrollBar(editor, -1);
266   ME_EnsureVisible(editor, editor->pCursors[0].pRun);
267   
268   /* FIXME this should be marked for update too somehow, so that painting happens in ME_PaintContent */
269   if (yLength != c.pt.y-c.rcView.top) {
270     RECT rc;
271     rc.left = c.rcView.left;
272     rc.right = c.rcView.right;
273     rc.top = c.pt.y;
274     rc.bottom = c.rcView.bottom;
275     InvalidateRect(editor->hWnd, &rc, FALSE);
276     UpdateWindow(editor->hWnd);
277   }
278   
279   editor->nOldSelFrom = nSelFrom;
280   editor->nOldSelTo = nSelTo;
281   /* PatBlt(hDC, 0, c.pt.y, c.rcView.right, c.rcView.bottom, BLACKNESS);*/
282
283   ME_DestroyContext(&c);
284   ReleaseDC(hWnd, hDC);
285 }
286
287 ME_TextBuffer *ME_MakeText() {
288   
289   ME_TextBuffer *buf = ALLOC_OBJ(ME_TextBuffer);
290
291   ME_DisplayItem *p1 = ME_MakeDI(diTextStart);
292   ME_DisplayItem *p2 = ME_MakeDI(diTextEnd);
293   
294   p1->prev = NULL;
295   p1->next = p2;
296   p2->prev = p1;
297   p2->next = NULL;
298   p1->member.para.next_para = p2;
299   p2->member.para.prev_para = p1;
300   p2->member.para.nCharOfs = 0;  
301   
302   buf->pFirst = p1;
303   buf->pLast = p2;
304   buf->pCharStyle = NULL;
305   
306   return buf;
307 }
308
309 static LRESULT ME_StreamIn(HWND hwnd, DWORD format, EDITSTREAM *stream)
310 {
311   RTF_Info parser;
312
313   TRACE("%p %p\n", stream, hwnd);
314
315   /* setup the RTF parser */
316   memset(&parser, 0, sizeof parser);
317   RTFSetEditStream(&parser, stream);
318   parser.rtfFormat = format&(SF_TEXT|SF_RTF);
319   parser.hwndEdit = hwnd;
320   WriterInit(&parser);
321   RTFInit(&parser);
322   BeginFile(&parser);
323
324   /* do the parsing */
325   RTFRead(&parser);
326   RTFFlushOutputBuffer(&parser);
327
328   /* put the cursor at the top */
329   SendMessageA(hwnd, EM_SETSEL, 0, 0);
330
331   return 0;
332 }
333
334 ME_TextEditor *ME_MakeEditor(HWND hWnd) {
335   ME_TextEditor *ed = ALLOC_OBJ(ME_TextEditor);
336   HDC hDC;
337   int i;
338   ed->hWnd = hWnd;
339   ed->pBuffer = ME_MakeText();
340   hDC = GetDC(hWnd);
341   ME_MakeFirstParagraph(hDC, ed->pBuffer);
342   ReleaseDC(hWnd, hDC);
343   ed->bCaretShown = FALSE;
344   ed->nCursors = 3;
345   ed->pCursors = ALLOC_N_OBJ(ME_Cursor, ed->nCursors);
346   ed->pCursors[0].pRun = ME_FindItemFwd(ed->pBuffer->pFirst, diRun);
347   ed->pCursors[0].nOffset = 0;
348   ed->pCursors[1].pRun = ME_FindItemFwd(ed->pBuffer->pFirst, diRun);
349   ed->pCursors[1].nOffset = 0;
350   ed->nTotalLength = 0;
351   ed->nScrollPos = 0;
352   ed->nUDArrowX = -1;
353   ed->nSequence = 0;
354   ed->rgbBackColor = -1;
355   ed->bCaretAtEnd = FALSE;
356   ed->nEventMask = 0;
357   ed->nModifyStep = 0;
358   ed->pUndoStack = ed->pRedoStack = NULL;
359   ed->nUndoMode = umAddToUndo;
360   ed->nParagraphs = 1;
361   for (i=0; i<HFONT_CACHE_SIZE; i++)
362   {
363     ed->pFontCache[i].nRefs = 0;
364     ed->pFontCache[i].nAge = 0;
365     ed->pFontCache[i].hFont = NULL;
366   }
367   ME_CheckCharOffsets(ed);
368   return ed;
369 }
370
371 void ME_DestroyEditor(ME_TextEditor *editor)
372 {
373   ME_DisplayItem *pFirst = editor->pBuffer->pFirst;
374   ME_DisplayItem *p = pFirst, *pNext = NULL;
375   int i;
376   
377   ME_ClearTempStyle(editor);
378   ME_EmptyUndoStack(editor);
379   while(p) {
380     pNext = p->next;
381     ME_DestroyDisplayItem(p);    
382     p = pNext;
383   }
384   ME_ReleaseStyle(editor->pBuffer->pDefaultStyle);
385   for (i=0; i<HFONT_CACHE_SIZE; i++)
386   {
387     if (editor->pFontCache[i].hFont)
388       DeleteObject(editor->pFontCache[i].hFont);
389   }
390     
391   FREE_OBJ(editor);
392 }
393
394 #define UNSUPPORTED_MSG(e) \
395   case e: \
396     FIXME(#e ": stub\n"); \
397     return DefWindowProcW(hWnd, msg, wParam, lParam);
398
399 /******************************************************************
400  *        RichEditANSIWndProc (RICHED20.10)
401  */
402 LRESULT WINAPI RichEditANSIWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) {
403   HDC hDC;
404   PAINTSTRUCT ps;
405   SCROLLINFO si;
406   ME_TextEditor *editor = (ME_TextEditor *)GetWindowLongW(hWnd, 0);
407   switch(msg) {
408   
409   UNSUPPORTED_MSG(EM_AUTOURLDETECT)
410   UNSUPPORTED_MSG(EM_CANPASTE)
411   UNSUPPORTED_MSG(EM_CHARFROMPOS)
412   UNSUPPORTED_MSG(EM_DISPLAYBAND)
413   UNSUPPORTED_MSG(EM_EXLIMITTEXT)
414   UNSUPPORTED_MSG(EM_EXLINEFROMCHAR)
415   UNSUPPORTED_MSG(EM_FINDTEXT)
416   UNSUPPORTED_MSG(EM_FINDTEXTEX)
417   UNSUPPORTED_MSG(EM_FINDWORDBREAK)
418   UNSUPPORTED_MSG(EM_FMTLINES)
419   UNSUPPORTED_MSG(EM_FORMATRANGE)
420   UNSUPPORTED_MSG(EM_GETFIRSTVISIBLELINE)
421   UNSUPPORTED_MSG(EM_GETIMECOMPMODE)
422   /* UNSUPPORTED_MSG(EM_GETIMESTATUS) missing in Wine headers */
423   UNSUPPORTED_MSG(EM_GETLANGOPTIONS)
424   UNSUPPORTED_MSG(EM_GETLIMITTEXT)
425   UNSUPPORTED_MSG(EM_GETLINE)
426   UNSUPPORTED_MSG(EM_GETLINECOUNT)
427   /* UNSUPPORTED_MSG(EM_GETOLEINTERFACE) separate stub */
428   UNSUPPORTED_MSG(EM_GETOPTIONS)
429   UNSUPPORTED_MSG(EM_GETRECT)
430   UNSUPPORTED_MSG(EM_GETREDONAME)
431   UNSUPPORTED_MSG(EM_GETTEXTMODE)
432   UNSUPPORTED_MSG(EM_GETUNDONAME)
433   UNSUPPORTED_MSG(EM_GETWORDBREAKPROC)
434   UNSUPPORTED_MSG(EM_GETWORDBREAKPROCEX)
435   UNSUPPORTED_MSG(EM_HIDESELECTION)
436   UNSUPPORTED_MSG(EM_LIMITTEXT) /* also known as EM_SETLIMITTEXT */
437   UNSUPPORTED_MSG(EM_LINEFROMCHAR)
438   UNSUPPORTED_MSG(EM_LINEINDEX)
439   UNSUPPORTED_MSG(EM_LINELENGTH)
440   UNSUPPORTED_MSG(EM_LINESCROLL)
441   UNSUPPORTED_MSG(EM_PASTESPECIAL)
442 /*  UNSUPPORTED_MSG(EM_POSFROMCHARS) missing in Wine headers */
443   UNSUPPORTED_MSG(EM_REQUESTRESIZE)
444   UNSUPPORTED_MSG(EM_SCROLL)
445   UNSUPPORTED_MSG(EM_SCROLLCARET)
446   UNSUPPORTED_MSG(EM_SELECTIONTYPE)
447   UNSUPPORTED_MSG(EM_SETLANGOPTIONS)
448   UNSUPPORTED_MSG(EM_SETOLECALLBACK)
449   UNSUPPORTED_MSG(EM_SETOPTIONS)
450   UNSUPPORTED_MSG(EM_SETRECT)
451   UNSUPPORTED_MSG(EM_SETRECTNP)
452   UNSUPPORTED_MSG(EM_SETTARGETDEVICE)
453   UNSUPPORTED_MSG(EM_SETTEXTMODE)
454   UNSUPPORTED_MSG(EM_SETUNDOLIMIT)
455   UNSUPPORTED_MSG(EM_SETWORDBREAKPROC)
456   UNSUPPORTED_MSG(EM_SETWORDBREAKPROCEX)
457   UNSUPPORTED_MSG(EM_STREAMOUT)
458   UNSUPPORTED_MSG(WM_SETFONT)
459   UNSUPPORTED_MSG(WM_PASTE)
460   UNSUPPORTED_MSG(WM_STYLECHANGING)
461   UNSUPPORTED_MSG(WM_STYLECHANGED)
462 /*  UNSUPPORTED_MSG(WM_UNICHAR) FIXME missing in Wine headers */
463     
464 /* Messages specific to Richedit controls */
465   
466   case EM_STREAMIN:
467    return ME_StreamIn(hWnd, wParam, (EDITSTREAM*)lParam);
468   case WM_GETDLGCODE:
469   {
470     UINT code = DLGC_WANTCHARS|DLGC_WANTARROWS;
471     if (GetWindowLongW(hWnd, GWL_STYLE)&ES_WANTRETURN)
472       code |= 0; /* FIXME what can we do here ? ask for messages and censor them ? */
473     return code;
474   }
475   case WM_NCCREATE:
476   {
477     CREATESTRUCTW *pcs = (CREATESTRUCTW *)lParam;
478     editor = ME_MakeEditor(hWnd);
479     SetWindowLongW(hWnd, 0, (long)editor);
480     pcs = 0; /* ignore */
481     return TRUE;
482   }
483   case EM_EMPTYUNDOBUFFER:
484     ME_EmptyUndoStack(editor);
485     return 0;
486   case EM_GETSEL:
487   {
488     ME_GetSelection(editor, (int *)wParam, (int *)lParam);
489     if (!((wParam|lParam) & 0xFFFF0000))
490       return (lParam<<16)|wParam;
491     return -1;
492   }
493   case EM_EXGETSEL:
494   {
495     CHARRANGE *pRange = (CHARRANGE *)lParam;
496     ME_GetSelection(editor, (int *)&pRange->cpMin, (int *)&pRange->cpMax);
497     return 0;
498   }
499   case EM_CANUNDO:
500     return editor->pUndoStack != NULL;
501   case EM_CANREDO:
502     return editor->pRedoStack != NULL;
503   case EM_UNDO:
504     ME_Undo(editor);
505     return 0;
506   case EM_REDO:
507     ME_Redo(editor);
508     return 0;
509   case EM_SETSEL:
510   {
511     ME_SetSelection(editor, wParam, lParam);
512     ME_Repaint(editor);
513     ME_SendSelChange(editor);
514     return 0;
515   }
516   case EM_EXSETSEL:
517   {
518     CHARRANGE *pRange = (CHARRANGE *)lParam;
519     ME_SetSelection(editor, pRange->cpMin, pRange->cpMax);
520     /* FIXME optimize */
521     ME_Repaint(editor);
522     ME_SendSelChange(editor);
523     return 0;
524   }
525   case EM_SETBKGNDCOLOR:
526   {
527     LRESULT lColor = ME_GetBackColor(editor);
528     if (wParam)
529       editor->rgbBackColor = -1;
530     else
531       editor->rgbBackColor = lParam; 
532     InvalidateRect(hWnd, NULL, TRUE);
533     UpdateWindow(hWnd);
534     return lColor;
535   }
536   case EM_GETMODIFY:
537     return editor->nModifyStep == 0 ? 0 : 1;
538   case EM_SETMODIFY:
539   {
540     if (wParam)
541       editor->nModifyStep = 0x80000000;
542     else
543       editor->nModifyStep = 0;
544     
545     return 0;
546   }
547   case EM_SETREADONLY:
548   {
549     long nStyle = GetWindowLongW(hWnd, GWL_STYLE);
550     if (wParam)
551       nStyle |= ES_READONLY;
552     else
553       nStyle &= ~ES_READONLY;
554     SetWindowLongW(hWnd, GWL_STYLE, nStyle);
555     ME_Repaint(editor);
556     return 0;
557   }
558   case EM_SETEVENTMASK:
559     editor->nEventMask = lParam;
560     return 0;
561   case EM_GETEVENTMASK:
562     return editor->nEventMask;
563   case EM_SETCHARFORMAT:
564   {
565     CHARFORMAT2W buf, *p;
566     p = ME_ToCF2W(&buf, (CHARFORMAT2W *)lParam);
567     if (!wParam)
568       ME_SetDefaultCharFormat(editor, p);
569     else if (wParam == (SCF_WORD | SCF_SELECTION))
570       FIXME("word selection not supported\n");
571     else if (wParam == SCF_ALL)
572       ME_SetCharFormat(editor, 0, ME_GetTextLength(editor), p);
573     else
574       ME_SetSelectionCharFormat(editor, p);
575     ME_CommitUndo(editor);
576     ME_UpdateRepaint(editor);
577     return 0;
578   }
579   case EM_GETCHARFORMAT:
580   {
581     CHARFORMAT2W tmp;
582     tmp.cbSize = sizeof(tmp);
583     if (!wParam)
584       ME_GetDefaultCharFormat(editor, &tmp);
585     else
586       ME_GetSelectionCharFormat(editor, &tmp);
587     ME_CopyToCFAny((CHARFORMAT2W *)lParam, &tmp);
588     return 0;
589   }
590   case EM_SETPARAFORMAT:
591     ME_SetSelectionParaFormat(editor, (PARAFORMAT2 *)lParam);
592     ME_CommitUndo(editor);
593     return 0;
594   case EM_GETPARAFORMAT:
595     ME_GetSelectionParaFormat(editor, (PARAFORMAT2 *)lParam);
596     return 0;
597   case WM_CLEAR:
598   {
599     int from, to;
600     ME_GetSelection(editor, &from, &to);
601     ME_InternalDeleteText(editor, from, to-from);
602     ME_CommitUndo(editor);
603     ME_UpdateRepaint(editor);
604     return 0;
605   }
606   case EM_REPLACESEL:
607   {
608     int from, to;
609     ME_Cursor c;
610     ME_Style *style;
611     LPWSTR wszText = ME_ToUnicode(hWnd, (void *)lParam);
612     size_t len = lstrlenW(wszText);
613     TRACE("EM_REPLACESEL - %s\n", debugstr_w(wszText));
614     
615     ME_GetSelection(editor, &from, &to);
616     ME_CursorFromCharOfs(editor, from, &c);
617     if (from != to) {
618       style = c.pRun->member.run.style;
619       ME_AddRefStyle(style); /* ME_GetInsertStyle has already done that */
620     }
621     else
622       style = ME_GetInsertStyle(editor, 0);
623     ME_InternalDeleteText(editor, from, to-from);
624     ME_InsertTextFromCursor(editor, 0, wszText, len, style);
625     ME_ReleaseStyle(style);
626     ME_EndToUnicode(hWnd, wszText);
627     /* drop temporary style if line end */
628     /* FIXME question: does abc\n mean: put abc, clear temp style, put \n? (would require a change) */  
629     if (len>0 && wszText[len-1] == '\n')
630       ME_ClearTempStyle(editor);
631       
632     ME_CommitUndo(editor);
633     if (!wParam)
634       ME_EmptyUndoStack(editor);
635     ME_UpdateRepaint(editor);
636     return 0;
637   }
638   case WM_SETTEXT:
639   {
640     LPWSTR wszText = ME_ToUnicode(hWnd, (void *)lParam);
641     TRACE("WM_SETTEXT - %s\n", (char *)(wszText)); /* debugstr_w() */
642     ME_InternalDeleteText(editor, 0, ME_GetTextLength(editor));
643     /* uses default style! */
644     ME_InsertTextFromCursor(editor, 0, wszText, -1, editor->pBuffer->pDefaultStyle);
645     ME_EndToUnicode(hWnd, wszText);
646     ME_CommitUndo(editor);
647     ME_EmptyUndoStack(editor);
648     ME_UpdateRepaint(editor);
649     return 0;
650   }
651   case WM_CUT:
652   case WM_COPY:
653   {
654     int from, to, pars;
655     WCHAR *data;
656     HANDLE hData;
657     
658     if (!OpenClipboard(hWnd))
659       return 0;
660       
661     EmptyClipboard();
662     ME_GetSelection(editor, &from, &to);
663     pars = ME_CountParagraphsBetween(editor, from, to);
664     hData = GlobalAlloc(GMEM_MOVEABLE, sizeof(WCHAR)*(to-from+pars+1));
665     data = (WCHAR *)GlobalLock(hData);
666     ME_GetTextW(editor, data, from, to-from, TRUE);
667     GlobalUnlock(hData);
668     SetClipboardData(CF_UNICODETEXT, hData);
669     CloseClipboard();
670     if (msg == WM_CUT)
671     {
672       ME_InternalDeleteText(editor, from, to-from);
673       ME_CommitUndo(editor);
674       ME_UpdateRepaint(editor);
675     }
676     return 0;
677   }
678   case WM_GETTEXTLENGTH:
679     return ME_GetTextLength(editor);
680   case WM_GETTEXT:
681   {
682     TEXTRANGEW tr; /* W and A differ only by rng->lpstrText */
683     tr.chrg.cpMin = 0;
684     tr.chrg.cpMax = wParam-1;
685     tr.lpstrText = (WCHAR *)lParam;
686     return RichEditANSIWndProc(hWnd, EM_GETTEXTRANGE, 0, (LPARAM)&tr);
687   }
688   case EM_GETSELTEXT:
689   {
690     int from, to;
691     TEXTRANGEW tr; /* W and A differ only by rng->lpstrText */
692     ME_GetSelection(editor, &from, &to);
693     tr.chrg.cpMin = from;
694     tr.chrg.cpMax = to;
695     tr.lpstrText = (WCHAR *)lParam;
696     return RichEditANSIWndProc(hWnd, EM_GETTEXTRANGE, 0, (LPARAM)&tr);
697   }
698   case EM_GETTEXTRANGE:
699   {
700     TEXTRANGEW *rng = (TEXTRANGEW *)lParam;
701     if (IsWindowUnicode(hWnd))
702       return ME_GetTextW(editor, rng->lpstrText, rng->chrg.cpMin, rng->chrg.cpMax-rng->chrg.cpMin, FALSE);
703     else
704     {
705       int nLen = rng->chrg.cpMax-rng->chrg.cpMin;
706       WCHAR *p = ALLOC_N_OBJ(WCHAR, nLen+1);
707       int nChars = ME_GetTextW(editor, p, rng->chrg.cpMin, nLen, FALSE);
708       /* FIXME this is a potential security hole (buffer overrun) 
709          if you know more about wchar->mbyte conversion please explain
710       */
711       WideCharToMultiByte(CP_ACP, 0, p, nChars+1, (char *)rng->lpstrText, nLen+1, NULL, NULL);
712       FREE_OBJ(p);
713       return nChars;
714     }
715     return ME_GetTextW(editor, rng->lpstrText, rng->chrg.cpMin, rng->chrg.cpMax-rng->chrg.cpMin, FALSE);
716   }
717   case WM_CREATE:
718     ME_CommitUndo(editor);
719 /*    ME_InsertTextFromCursor(editor, 0, (WCHAR *)L"x", 1, editor->pBuffer->pDefaultStyle); */
720     DoWrap(editor);
721     ME_MoveCaret(editor);
722     return 0;
723   case WM_DESTROY:
724     ME_DestroyEditor(editor);
725     SetWindowLongW(hWnd, 0, 0);
726     PostQuitMessage(0);
727     break;
728   case WM_LBUTTONDOWN:
729     SetFocus(hWnd);
730     ME_LButtonDown(editor, (short)LOWORD(lParam), (short)HIWORD(lParam));
731     SetCapture(hWnd);
732     break;
733   case WM_MOUSEMOVE:
734     if (GetCapture() == hWnd)
735       ME_MouseMove(editor, (short)LOWORD(lParam), (short)HIWORD(lParam));
736     break;
737   case WM_LBUTTONUP:
738     if (GetCapture() == hWnd)
739       ReleaseCapture();
740     break;
741   case WM_PAINT:
742     hDC = BeginPaint(hWnd, &ps);
743     ME_PaintContent(editor, hDC, FALSE);
744     EndPaint(hWnd, &ps);
745     break;
746   case WM_SETFOCUS:
747     ME_ShowCaret(editor);
748     ME_SendOldNotify(editor, EN_SETFOCUS);
749     return 0;
750   case WM_KILLFOCUS:
751     ME_HideCaret(editor);
752     ME_SendOldNotify(editor, EN_KILLFOCUS);
753     return 0;
754   case WM_ERASEBKGND:
755   {
756     HDC hDC = (HDC)wParam;
757     RECT rc;
758     COLORREF rgbBG = ME_GetBackColor(editor);
759     if (GetUpdateRect(hWnd,&rc,TRUE))
760     {
761       HBRUSH hbr = CreateSolidBrush(rgbBG);
762       FillRect(hDC, &rc, hbr);
763       DeleteObject(hbr);
764     }
765     return 1;
766   }
767   case WM_COMMAND:
768     TRACE("editor wnd command = %d\n", LOWORD(wParam));
769     return 0;
770   case WM_KEYDOWN:
771     if (ME_ArrowKey(editor, LOWORD(wParam), GetKeyState(VK_CONTROL)<0)) {
772       ME_CommitUndo(editor);
773       ME_EnsureVisible(editor, editor->pCursors[0].pRun);
774       HideCaret(hWnd);
775       ME_MoveCaret(editor);
776       ShowCaret(hWnd);
777       return 0;
778     }
779     if (GetKeyState(VK_CONTROL)<0)
780     {
781       if (LOWORD(wParam)=='W')
782       {
783         CHARFORMAT2W chf;
784         char buf[2048];
785         ME_GetSelectionCharFormat(editor, &chf);
786         ME_DumpStyleToBuf(&chf, buf);
787         MessageBoxA(NULL, buf, "Style dump", MB_OK);
788       }
789       if (LOWORD(wParam)=='Q')
790       {
791         ME_CheckCharOffsets(editor);
792       }
793     }
794     goto do_default;
795   case WM_CHAR: 
796   {
797     WCHAR wstr;
798     if (GetWindowLongW(editor->hWnd, GWL_STYLE) & ES_READONLY) {
799       MessageBeep(MB_ICONERROR);
800       return 0; /* FIXME really 0 ? */
801     }
802     wstr = LOWORD(wParam);
803     if (((unsigned)wstr)>=' ' || wstr=='\r') {
804       /* FIXME maybe it would make sense to call EM_REPLACESEL instead ? */
805       ME_Style *style = ME_GetInsertStyle(editor, 0);
806       ME_SaveTempStyle(editor);
807       ME_InsertTextFromCursor(editor, 0, &wstr, 1, style);
808       ME_ReleaseStyle(style);
809       ME_CommitUndo(editor);
810       ME_UpdateRepaint(editor);
811     }
812     return 0;
813   }
814   case WM_VSCROLL: 
815   {
816     si.cbSize = sizeof(SCROLLINFO);
817     si.fMask = SIF_PAGE|SIF_POS|SIF_RANGE|SIF_TRACKPOS;
818     GetScrollInfo(hWnd, SB_VERT, &si);
819     switch(LOWORD(wParam)) {
820     case SB_THUMBTRACK:
821       SetScrollPos(hWnd, SB_VERT, si.nTrackPos, FALSE);
822       ScrollWindow(hWnd, 0, si.nPos-si.nTrackPos, NULL, NULL);
823       /* InvalidateRect(hWnd, NULL, TRUE); */
824       UpdateWindow(hWnd);
825       break;
826     }
827     break;
828   }
829   case WM_SIZE:
830   {
831     ME_DisplayItem *tp = editor->pBuffer->pFirst;
832     while(tp)
833     {
834       if (tp->type == diParagraph)
835       {
836         tp->member.para.nFlags &= ~MEPF_WRAPPED;
837         tp = tp->member.para.next_para;
838       }
839       else
840         tp = tp->next;
841     }
842     ME_Repaint(editor);
843     return DefWindowProcW(hWnd, msg, wParam, lParam);
844   }
845   case EM_GETOLEINTERFACE:
846   {
847     LPVOID *ppvObj = (LPVOID*) lParam;
848     TRACE("EM_GETOLEINTERFACE %p\n", ppvObj);
849     return CreateIRichEditOle(ppvObj);
850   }
851   default:
852   do_default:
853     return DefWindowProcW(hWnd, msg, wParam, lParam);
854   }
855   return 0L;
856 }
857
858 /******************************************************************
859  *        RichEdit10ANSIWndProc (RICHED20.9)
860  */
861 LRESULT WINAPI RichEdit10ANSIWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
862 {
863   /* FIXME: this is NOT the same as 2.0 version */
864   return RichEditANSIWndProc(hWnd, msg, wParam, lParam);
865 }
866
867 void ME_SendOldNotify(ME_TextEditor *editor, int nCode)
868 {
869   HWND hWnd = editor->hWnd;
870   SendMessageA(GetParent(hWnd), WM_COMMAND, (nCode<<16)|GetWindowLongW(hWnd, GWLP_ID), (LPARAM)hWnd);
871 }
872
873 int ME_CountParagraphsBetween(ME_TextEditor *editor, int from, int to)
874 {
875   ME_DisplayItem *item = ME_FindItemFwd(editor->pBuffer->pFirst, diParagraph);
876   int i = 0;
877   
878   while(item && item->member.para.next_para->member.para.nCharOfs <= from)
879     item = item->member.para.next_para;
880   if (!item)
881     return 0;
882   while(item && item->member.para.next_para->member.para.nCharOfs <= to) {
883     item = item->member.para.next_para;
884     i++;
885   }
886   return i;
887 }
888
889 int ME_GetTextW(ME_TextEditor *editor, WCHAR *buffer, int nStart, int nChars, int bCRLF)
890 {
891   ME_DisplayItem *item = ME_FindItemFwd(editor->pBuffer->pFirst, diParagraph);
892   int nWritten = 0;
893   
894   while(item && item->member.para.next_para->member.para.nCharOfs <= nStart)
895     item = ME_FindItemFwd(item, diParagraph);
896   if (!item) {
897     *buffer = L'\0';
898     return 0;
899   }
900   nStart -= item->member.para.nCharOfs;
901
902   do {  
903     item = ME_FindItemFwd(item, diRun);
904   } while(item && (item->member.run.nCharOfs + ME_StrLen(item->member.run.strText) <= nStart));
905   assert(item);    
906   
907   nStart -= item->member.run.nCharOfs;
908   
909   if (nStart)
910   {
911     int nLen = ME_StrLen(item->member.run.strText) - nStart;
912     if (nLen > nChars)
913       nLen = nChars;
914     CopyMemory(buffer, item->member.run.strText->szData + nStart, sizeof(WCHAR)*nLen);
915     nChars -= nLen;
916     nWritten += nLen;
917     if (!nChars)
918       return nWritten;
919     buffer += nLen;
920     nStart = 0;
921     item = ME_FindItemFwd(item, diRun);
922   }
923   
924   while(nChars && item)
925   {
926     int nLen = ME_StrLen(item->member.run.strText);
927     if (nLen > nChars)
928       nLen = nChars;
929       
930     if (item->member.run.nFlags & MERF_ENDPARA)
931     {
932       if (bCRLF) {
933         *buffer++ = '\r';
934         nWritten++;
935       }        
936       *buffer = '\n';
937       assert(nLen == 1);
938     }
939     else      
940       CopyMemory(buffer, item->member.run.strText->szData, sizeof(WCHAR)*nLen);
941     nChars -= nLen;
942     nWritten += nLen;
943     buffer += nLen;    
944       
945     if (!nChars)
946     {
947       *buffer = L'\0';
948       return nWritten;
949     }
950     item = ME_FindItemFwd(item, diRun);
951   }
952   *buffer = L'\0';
953   return nWritten;  
954 }
955
956 static WCHAR wszClassName[] = {'R', 'i', 'c', 'h', 'E', 'd', 'i', 't', '2', '0', 'W', 0};
957 static WCHAR wszClassName50[] = {'R', 'i', 'c', 'h', 'E', 'd', 'i', 't', '5', '0', 'W', 0};
958
959 void ME_RegisterEditorClass(HINSTANCE hInstance)
960 {
961   BOOL bResult;
962   WNDCLASSW wcW;
963   WNDCLASSA wcA;
964   
965   wcW.style = CS_HREDRAW | CS_VREDRAW;
966   wcW.lpfnWndProc = RichEditANSIWndProc;
967   wcW.cbClsExtra = 0;
968   wcW.cbWndExtra = 4;
969   wcW.hInstance = NULL; /* hInstance would register DLL-local class */
970   wcW.hIcon = NULL;
971   wcW.hCursor = LoadCursorW(NULL, MAKEINTRESOURCEW(IDC_IBEAM));
972   wcW.hbrBackground = (HBRUSH)GetStockObject(NULL_BRUSH);
973   wcW.lpszMenuName = NULL;
974   wcW.lpszClassName = wszClassName;
975   bResult = RegisterClassW(&wcW);  
976   assert(bResult);
977   wcW.lpszClassName = wszClassName50;
978   bResult = RegisterClassW(&wcW);  
979   assert(bResult);
980
981   wcA.style = CS_HREDRAW | CS_VREDRAW;
982   wcA.lpfnWndProc = RichEditANSIWndProc;
983   wcA.cbClsExtra = 0;
984   wcA.cbWndExtra = 4;
985   wcA.hInstance = NULL; /* hInstance would register DLL-local class */
986   wcA.hIcon = NULL;
987   wcA.hCursor = LoadCursorW(NULL, MAKEINTRESOURCEW(IDC_IBEAM));
988   wcA.hbrBackground = (HBRUSH)GetStockObject(NULL_BRUSH);
989   wcA.lpszMenuName = NULL;
990   wcA.lpszClassName = "RichEdit20A";
991   bResult = RegisterClassA(&wcA);  
992   assert(bResult);
993   wcA.lpszClassName = "RichEdit50A";
994   bResult = RegisterClassA(&wcA);  
995   assert(bResult);
996 }
997
998 BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
999 {
1000     TRACE("\n");
1001     switch (fdwReason)
1002     {
1003     case DLL_PROCESS_ATTACH:
1004       DisableThreadLibraryCalls(hinstDLL);
1005       me_heap = HeapCreate (0, 0x10000, 0);
1006       ME_RegisterEditorClass(hinstDLL);
1007       break;
1008
1009     case DLL_PROCESS_DETACH:
1010       UnregisterClassW(wszClassName, 0);
1011       UnregisterClassW(wszClassName50, 0);
1012       UnregisterClassA("RichEdit20A", 0);
1013       UnregisterClassA("RichEdit50A", 0);
1014       HeapDestroy (me_heap);
1015       me_heap = NULL;
1016       break;
1017     }
1018     return TRUE;
1019 }
1020
1021 /******************************************************************
1022  *        CreateTextServices (RICHED20.4)
1023  *
1024  * FIXME should be ITextHost instead of void*
1025  */
1026 HRESULT WINAPI CreateTextServices(IUnknown *punkOuter, void *pITextHost,
1027     IUnknown **ppUnk)
1028 {
1029   FIXME("stub\n");
1030   /* FIXME should support aggregation */
1031   if (punkOuter)
1032     return CLASS_E_NOAGGREGATION;
1033     
1034   return E_FAIL; /* E_NOTIMPL isn't allowed by MSDN */
1035 }
1036
1037 /******************************************************************
1038  *        REExtendedRegisterClass (RICHED20.8)
1039  *
1040  * FIXME undocumented
1041  */
1042 void WINAPI REExtendedRegisterClass(void)
1043 {
1044   FIXME("stub\n");
1045 }