secur32: Update ntlm_auth version detection to detect new samba4 version numbers.
[wine] / dlls / riched20 / editor.c
1 /*
2  * RichEdit - functions dealing with editor object
3  *
4  * Copyright 2004 by Krzysztof Foltman
5  * Copyright 2005 by Cihan Altinay
6  * Copyright 2005 by Phil Krylov
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  */
22
23 /* 
24   API implementation status:
25   
26   Messages (ANSI versions not done yet)
27   + EM_AUTOURLDETECT 2.0
28   + EM_CANPASTE
29   + EM_CANREDO 2.0
30   + EM_CANUNDO
31   + EM_CHARFROMPOS
32   - EM_DISPLAYBAND
33   + EM_EMPTYUNDOBUFFER
34   + EM_EXGETSEL
35   - EM_EXLIMITTEXT
36   + EM_EXLINEFROMCHAR
37   + EM_EXSETSEL
38   + EM_FINDTEXT (only FR_DOWN flag implemented)
39   + EM_FINDTEXTEX (only FR_DOWN flag implemented)
40   - EM_FINDWORDBREAK
41   - EM_FMTLINES
42   - EM_FORMATRANGE
43   + EM_GETAUTOURLDETECT 2.0
44   - EM_GETBIDIOPTIONS 3.0
45   - EM_GETCHARFORMAT (partly done)
46   - EM_GETEDITSTYLE
47   + EM_GETEVENTMASK
48   + EM_GETFIRSTVISIBLELINE (can be optimized if needed)
49   - EM_GETIMECOLOR 1.0asian
50   - EM_GETIMECOMPMODE 2.0
51   - EM_GETIMEOPTIONS 1.0asian
52   - EM_GETIMESTATUS
53   - EM_GETLANGOPTIONS 2.0
54   - EM_GETLIMITTEXT
55   - EM_GETLINE        
56   + EM_GETLINECOUNT   returns number of rows, not of paragraphs
57   + EM_GETMODIFY
58   - EM_GETOLEINTERFACE
59   + EM_GETOPTIONS
60   + EM_GETPARAFORMAT
61   - EM_GETPASSWORDCHAR 2.0
62   - EM_GETPUNCTUATION 1.0asian
63   + EM_GETRECT
64   - EM_GETREDONAME 2.0
65   + EM_GETSEL
66   + EM_GETSELTEXT (ANSI&Unicode)
67   + EM_GETSCROLLPOS 3.0 (only Y value valid)
68 ! - EM_GETTHUMB
69   - EM_GETTEXTEX 2.0
70   + EM_GETTEXTLENGTHEX (GTL_PRECISE unimplemented)
71   - EM_GETTEXTMODE 2.0
72 ? + EM_GETTEXTRANGE (ANSI&Unicode)
73   - EM_GETTYPOGRAPHYOPTIONS 3.0
74   - EM_GETUNDONAME
75   + EM_GETWORDBREAKPROC
76   - EM_GETWORDBREAKPROCEX
77   - EM_GETWORDWRAPMODE 1.0asian
78   + EM_GETZOOM 3.0
79   + EM_HIDESELECTION
80   - EM_LIMITTEXT
81   + EM_LINEFROMCHAR
82   + EM_LINEINDEX
83   + EM_LINELENGTH
84   + EM_LINESCROLL
85   - EM_PASTESPECIAL
86   + EM_POSFROMCHAR
87   + EM_REDO 2.0
88   + EM_REQUESTRESIZE
89   + EM_REPLACESEL (proper style?) ANSI&Unicode
90   - EM_SCROLL
91   + EM_SCROLLCARET
92   - EM_SELECTIONTYPE
93   - EM_SETBIDIOPTIONS 3.0
94   + EM_SETBKGNDCOLOR
95   - EM_SETCHARFORMAT (partly done, no ANSI)
96   - EM_SETEDITSTYLE
97   + EM_SETEVENTMASK (few notifications supported)
98   - EM_SETFONTSIZE
99   - EM_SETIMECOLOR 1.0asian
100   - EM_SETIMEOPTIONS 1.0asian
101   - EM_SETLANGOPTIONS 2.0
102   - EM_SETLIMITTEXT
103   + EM_SETMODIFY (not sure if implementation is correct)
104   - EM_SETOLECALLBACK
105   + EM_SETOPTIONS (partially implemented)
106   - EM_SETPALETTE 2.0
107   + EM_SETPARAFORMAT
108   - EM_SETPASSWORDCHAR 2.0
109   - EM_SETPUNCTUATION 1.0asian
110   + EM_SETREADONLY no beep on modification attempt
111   + EM_SETRECT
112   + EM_SETRECTNP (EM_SETRECT without repainting)
113   + EM_SETSEL
114   - EM_SETSCROLLPOS 3.0
115   - EM_SETTABSTOPS 3.0
116   - EM_SETTARGETDEVICE
117   + EM_SETTEXTEX 3.0 (unicode only, no rich text insertion handling, proper style?)
118   - EM_SETTEXTMODE 2.0
119   - EM_SETTYPOGRAPHYOPTIONS 3.0
120   - EM_SETUNDOLIMIT 2.0
121   + EM_SETWORDBREAKPROC (used only for word movement at the moment)
122   - EM_SETWORDBREAKPROCEX
123   - EM_SETWORDWRAPMODE 1.0asian
124   + EM_SETZOOM 3.0
125   - EM_SHOWSCROLLBAR 2.0
126   - EM_STOPGROUPTYPING 2.0
127   + EM_STREAMIN
128   + EM_STREAMOUT
129   + EM_UNDO
130   + WM_CHAR
131   + WM_CLEAR
132   + WM_COPY
133   + WM_CUT
134   + WM_GETDLGCODE (the current implementation is incomplete)
135   + WM_GETTEXT (ANSI&Unicode)
136   + WM_GETTEXTLENGTH (ANSI version sucks)
137   + WM_PASTE
138   - WM_SETFONT
139   + WM_SETTEXT (resets undo stack !) (proper style?) ANSI&Unicode
140   - WM_STYLECHANGING
141   - WM_STYLECHANGED (things like read-only flag)
142   - WM_UNICHAR
143   
144   Notifications
145   
146   * EN_CHANGE (sent from the wrong place)
147   - EN_CORRECTTEXT
148   - EN_DROPFILES
149   - EN_ERRSPACE
150   - EN_HSCROLL
151   - EN_IMECHANGE
152   + EN_KILLFOCUS
153   - EN_LINK
154   - EN_MAXTEXT
155   - EN_MSGFILTER
156   - EN_OLEOPFAILED
157   - EN_PROTECTED
158   + EN_REQUESTRESIZE
159   - EN_SAVECLIPBOARD
160   + EN_SELCHANGE 
161   + EN_SETFOCUS
162   - EN_STOPNOUNDO
163   * EN_UPDATE (sent from the wrong place)
164   - EN_VSCROLL
165   
166   Styles
167   
168   - ES_AUTOHSCROLL
169   - ES_AUTOVSCROLL
170   - ES_CENTER
171   - ES_DISABLENOSCROLL (scrollbar is always visible)
172   - ES_EX_NOCALLOLEINIT
173   - ES_LEFT
174   - ES_MULTILINE (currently single line controls aren't supported)
175   - ES_NOIME
176   - ES_READONLY (I'm not sure if beeping is the proper behaviour)
177   - ES_RIGHT
178   - ES_SAVESEL
179   - ES_SELFIME
180   - ES_SUNKEN
181   - ES_VERTICAL
182   - ES_WANTRETURN (don't know how to do WM_GETDLGCODE part)
183   - WS_SETFONT
184   - WS_HSCROLL
185   - WS_VSCROLL
186 */
187
188 /*
189  * RICHED20 TODO (incomplete):
190  *
191  * - messages/styles/notifications listed above 
192  * - Undo coalescing 
193  * - add remaining CHARFORMAT/PARAFORMAT fields
194  * - right/center align should strip spaces from the beginning
195  * - pictures/OLE objects (not just smiling faces that lack API support ;-) )
196  * - COM interface (looks like a major pain in the TODO list)
197  * - calculate heights of pictures (half-done)
198  * - horizontal scrolling (not even started)
199  * - hysteresis during wrapping (related to scrollbars appearing/disappearing)
200  * - find/replace
201  * - how to implement EM_FORMATRANGE and EM_DISPLAYBAND ? (Mission Impossible)
202  * - italic caret with italic fonts
203  * - IME
204  * - most notifications aren't sent at all (the most important ones are)
205  * - when should EN_SELCHANGE be sent after text change ? (before/after EN_UPDATE?)
206  * - WM_SETTEXT may use wrong style (but I'm 80% sure it's OK)
207  * - EM_GETCHARFORMAT with SCF_SELECTION may not behave 100% like in original (but very close)
208  * - full justification
209  * - hyphenation
210  * - tables
211  * - ListBox & ComboBox not implemented
212  *
213  * Bugs that are probably fixed, but not so easy to verify:
214  * - EN_UPDATE/EN_CHANGE are handled very incorrectly (should be OK now)
215  * - undo for ME_JoinParagraphs doesn't store paragraph format ? (it does)
216  * - check/fix artificial EOL logic (bCursorAtEnd, hardly logical)
217  * - caret shouldn't be displayed when selection isn't empty
218  * - check refcounting in style management functions (looks perfect now, but no bugs is suspicious)
219  * - undo for setting default format (done, might be buggy)
220  * - styles might be not released properly (looks like they work like charm, but who knows?
221  *
222  */
223
224 #include "editor.h"
225 #include "commdlg.h"
226 #include "winreg.h"
227 #define NO_SHLWAPI_STREAM 
228 #include "shlwapi.h"
229 #include "imm.h"
230 #include "textserv.h"
231 #include "rtf.h"
232  
233 WINE_DEFAULT_DEBUG_CHANNEL(richedit);
234
235 int me_debug = 0;
236 HANDLE me_heap = NULL;
237
238 static BOOL ME_ListBoxRegistered = FALSE;
239 static BOOL ME_ComboBoxRegistered = FALSE;
240
241 static ME_TextBuffer *ME_MakeText(void) {
242   
243   ME_TextBuffer *buf = ALLOC_OBJ(ME_TextBuffer);
244
245   ME_DisplayItem *p1 = ME_MakeDI(diTextStart);
246   ME_DisplayItem *p2 = ME_MakeDI(diTextEnd);
247   
248   p1->prev = NULL;
249   p1->next = p2;
250   p2->prev = p1;
251   p2->next = NULL;
252   p1->member.para.next_para = p2;
253   p2->member.para.prev_para = p1;
254   p2->member.para.nCharOfs = 0;  
255   
256   buf->pFirst = p1;
257   buf->pLast = p2;
258   buf->pCharStyle = NULL;
259   
260   return buf;
261 }
262
263
264 static LRESULT ME_StreamInText(ME_TextEditor *editor, DWORD dwFormat, ME_InStream *stream, ME_Style *style)
265 {
266   WCHAR wszText[STREAMIN_BUFFER_SIZE+1];
267   WCHAR *pText;
268   
269   TRACE("%08lx %p\n", dwFormat, stream);
270   
271   do {
272     long nWideChars = 0;
273
274     if (!stream->dwSize)
275     {
276       ME_StreamInFill(stream);
277       if (stream->editstream->dwError)
278         break;
279       if (!stream->dwSize)
280         break;
281     }
282       
283     if (!(dwFormat & SF_UNICODE))
284     {
285       /* FIXME? this is doomed to fail on true MBCS like UTF-8, luckily they're unlikely to be used as CP_ACP */
286       nWideChars = MultiByteToWideChar(CP_ACP, 0, stream->buffer, stream->dwSize, wszText, STREAMIN_BUFFER_SIZE);
287       pText = wszText;
288     }
289     else
290     {
291       nWideChars = stream->dwSize >> 1;
292       pText = (WCHAR *)stream->buffer;
293     }
294     
295     ME_InsertTextFromCursor(editor, 0, pText, nWideChars, style);
296     if (stream->dwSize == 0)
297       break;
298     stream->dwSize = 0;
299   } while(1);
300   ME_CommitUndo(editor);
301   ME_UpdateRepaint(editor);
302   return 0;
303 }
304
305 static void ME_RTFCharAttrHook(RTF_Info *info)
306 {
307   CHARFORMAT2W fmt;
308   fmt.cbSize = sizeof(fmt);
309   fmt.dwMask = 0;
310   
311   switch(info->rtfMinor)
312   {
313     case rtfPlain:
314       /* FIXME add more flags once they're implemented */
315       fmt.dwMask = CFM_BOLD | CFM_ITALIC | CFM_UNDERLINE | CFM_STRIKEOUT | CFM_COLOR | CFM_BACKCOLOR | CFM_SIZE | CFM_WEIGHT;
316       fmt.dwEffects = CFE_AUTOCOLOR | CFE_AUTOBACKCOLOR;
317       fmt.yHeight = 12*20; /* 12pt */
318       fmt.wWeight = 400;
319       break;
320     case rtfBold:
321       fmt.dwMask = CFM_BOLD;
322       fmt.dwEffects = info->rtfParam ? fmt.dwMask : 0;
323       break;
324     case rtfItalic:
325       fmt.dwMask = CFM_ITALIC;
326       fmt.dwEffects = info->rtfParam ? fmt.dwMask : 0;
327       break;
328     case rtfUnderline:
329       fmt.dwMask = CFM_UNDERLINE;
330       fmt.dwEffects = info->rtfParam ? fmt.dwMask : 0;
331       fmt.bUnderlineType = CFU_CF1UNDERLINE;
332       break;
333     case rtfNoUnderline:
334       fmt.dwMask = CFM_UNDERLINE;
335       fmt.dwEffects = 0;
336       break;
337     case rtfStrikeThru:
338       fmt.dwMask = CFM_STRIKEOUT;
339       fmt.dwEffects = info->rtfParam ? fmt.dwMask : 0;
340       break;
341     case rtfSubScript:
342     case rtfSuperScript:
343     case rtfSubScrShrink:
344     case rtfSuperScrShrink:
345     case rtfNoSuperSub:
346       fmt.dwMask = CFM_SUBSCRIPT|CFM_SUPERSCRIPT;
347       if (info->rtfMinor == rtfSubScrShrink) fmt.dwEffects = CFE_SUBSCRIPT;
348       if (info->rtfMinor == rtfSuperScrShrink) fmt.dwEffects = CFE_SUPERSCRIPT;
349       if (info->rtfMinor == rtfNoSuperSub) fmt.dwEffects = 0;
350       break;
351     case rtfInvisible:
352       fmt.dwMask = CFM_HIDDEN;
353       fmt.dwEffects = info->rtfParam ? fmt.dwMask : 0;
354       break;
355     case rtfBackColor:
356       fmt.dwMask = CFM_BACKCOLOR;
357       fmt.dwEffects = 0;
358       if (info->rtfParam == 0)
359         fmt.dwEffects = CFE_AUTOBACKCOLOR;
360       else if (info->rtfParam != rtfNoParam)
361       {
362         RTFColor *c = RTFGetColor(info, info->rtfParam);
363         fmt.crTextColor = (c->rtfCBlue<<16)|(c->rtfCGreen<<8)|(c->rtfCRed);
364       }
365       break;
366     case rtfForeColor:
367       fmt.dwMask = CFM_COLOR;
368       fmt.dwEffects = 0;
369       if (info->rtfParam == 0)
370         fmt.dwEffects = CFE_AUTOCOLOR;
371       else if (info->rtfParam != rtfNoParam)
372       {
373         RTFColor *c = RTFGetColor(info, info->rtfParam);
374         fmt.crTextColor = (c->rtfCBlue<<16)|(c->rtfCGreen<<8)|(c->rtfCRed);
375       }
376       break;
377     case rtfFontNum:
378       if (info->rtfParam != rtfNoParam)
379       {
380         RTFFont *f = RTFGetFont(info, info->rtfParam);
381         if (f)
382         {
383           MultiByteToWideChar(CP_ACP, 0, f->rtfFName, -1, fmt.szFaceName, sizeof(fmt.szFaceName)/sizeof(WCHAR));
384           fmt.szFaceName[sizeof(fmt.szFaceName)/sizeof(WCHAR)-1] = '\0';
385           fmt.bCharSet = f->rtfFCharSet;
386           fmt.dwMask = CFM_FACE | CFM_CHARSET;
387         }
388       }
389       break;
390     case rtfFontSize:
391       fmt.dwMask = CFM_SIZE;
392       if (info->rtfParam != rtfNoParam)
393         fmt.yHeight = info->rtfParam*10;
394       break;
395   }
396   if (fmt.dwMask) {
397     ME_Style *style2;
398     RTFFlushOutputBuffer(info);
399     /* FIXME too slow ? how come ? */
400     style2 = ME_ApplyStyle(info->style, &fmt);
401     ME_ReleaseStyle(info->style);
402     info->style = style2;
403     info->styleChanged = TRUE;
404   }
405 }
406
407 /* FIXME this function doesn't get any information about context of the RTF tag, which is very bad,
408    the same tags mean different things in different contexts */
409 static void ME_RTFParAttrHook(RTF_Info *info)
410 {
411   PARAFORMAT2 fmt;
412   fmt.cbSize = sizeof(fmt);
413   fmt.dwMask = 0;
414   
415   switch(info->rtfMinor)
416   {
417   case rtfParDef: /* restores default paragraph attributes */
418     fmt.dwMask = PFM_ALIGNMENT | PFM_TABSTOPS | PFM_OFFSET | PFM_STARTINDENT;
419     fmt.wAlignment = PFA_LEFT;
420     fmt.cTabCount = 0;
421     fmt.dxOffset = fmt.dxStartIndent = 0;
422     RTFFlushOutputBuffer(info);
423     ME_GetParagraph(info->editor->pCursors[0].pRun)->member.para.bTable = FALSE;
424     break;
425   case rtfInTable:
426   {
427     ME_DisplayItem *para;
428     
429     RTFFlushOutputBuffer(info);
430     para = ME_GetParagraph(info->editor->pCursors[0].pRun);
431     assert(para->member.para.pCells);
432     para->member.para.bTable = TRUE;
433     return;
434   }
435   case rtfFirstIndent:
436     ME_GetSelectionParaFormat(info->editor, &fmt);
437     fmt.dwMask = PFM_STARTINDENT | PFM_OFFSET;
438     fmt.dxStartIndent += info->rtfParam + fmt.dxOffset;
439     fmt.dxOffset = -info->rtfParam;
440     break;
441   case rtfLeftIndent:
442     ME_GetSelectionParaFormat(info->editor, &fmt);
443     fmt.dwMask = PFM_STARTINDENT;
444     fmt.dxStartIndent = -fmt.dxOffset + info->rtfParam;
445     break;
446   case rtfRightIndent:
447     fmt.dwMask = PFM_RIGHTINDENT;
448     fmt.dxRightIndent = info->rtfParam;
449     break;
450   case rtfQuadLeft:
451   case rtfQuadJust:
452     fmt.dwMask = PFM_ALIGNMENT;
453     fmt.wAlignment = PFA_LEFT;
454     break;
455   case rtfQuadRight:
456     fmt.dwMask = PFM_ALIGNMENT;
457     fmt.wAlignment = PFA_RIGHT;
458     break;
459   case rtfQuadCenter:
460     fmt.dwMask = PFM_ALIGNMENT;
461     fmt.wAlignment = PFA_CENTER;
462     break;
463   case rtfTabPos:
464     ME_GetSelectionParaFormat(info->editor, &fmt);
465     if (!(fmt.dwMask & PFM_TABSTOPS))
466     {
467       fmt.dwMask |= PFM_TABSTOPS;
468       fmt.cTabCount = 0;
469     }
470     if (fmt.cTabCount < MAX_TAB_STOPS)
471       fmt.rgxTabs[fmt.cTabCount++] = info->rtfParam;
472     break;
473   }  
474   if (fmt.dwMask) {
475     RTFFlushOutputBuffer(info);
476     /* FIXME too slow ? how come ?*/
477     ME_SetSelectionParaFormat(info->editor, &fmt);
478   }
479 }
480
481 static void ME_RTFTblAttrHook(RTF_Info *info)
482 {
483   ME_DisplayItem *para;
484   
485   switch (info->rtfMinor)
486   {
487     case rtfRowDef:
488       RTFFlushOutputBuffer(info);
489       para = ME_GetParagraph(info->editor->pCursors[0].pRun);
490       
491       para->member.para.pCells = ALLOC_OBJ(ME_TableCell);
492       para->member.para.pCells->nRightBoundary = 0;
493       para->member.para.pCells->next = NULL;
494       para->member.para.pLastCell = para->member.para.pCells;
495       break;
496     case rtfCellPos:
497       RTFFlushOutputBuffer(info);
498       para = ME_GetParagraph(info->editor->pCursors[0].pRun);
499       
500       if (para->member.para.pLastCell->nRightBoundary)
501       {
502         ME_TableCell *pCell = ALLOC_OBJ(ME_TableCell);
503         
504         pCell->next = NULL;
505         para->member.para.pLastCell->next = pCell;
506         para->member.para.pLastCell = pCell;
507       }
508       para->member.para.pLastCell->nRightBoundary = info->rtfParam;
509       break;
510   }
511 }
512
513 static void ME_RTFReadHook(RTF_Info *info) {
514   switch(info->rtfClass)
515   {
516     case rtfGroup:
517       switch(info->rtfMajor)
518       {
519         case rtfBeginGroup:
520           if (info->stackTop < maxStack) {
521             memcpy(&info->stack[info->stackTop].fmt, &info->style->fmt, sizeof(CHARFORMAT2W));
522             info->stack[info->stackTop].codePage = info->codePage;
523             info->stack[info->stackTop].unicodeLength = info->unicodeLength;
524           }
525           info->stackTop++;
526           info->styleChanged = FALSE;
527           break;
528         case rtfEndGroup:
529         {
530           ME_Style *s;
531           RTFFlushOutputBuffer(info);
532           if (info->stackTop<=1) {
533             info->rtfClass = rtfEOF;
534             return;
535           }
536           info->stackTop--;
537           assert(info->stackTop >= 0);
538           if (info->styleChanged)
539           {
540             /* FIXME too slow ? how come ? */
541             s = ME_ApplyStyle(info->style, &info->stack[info->stackTop].fmt);
542             ME_ReleaseStyle(info->style);
543             info->style = s;
544             info->codePage = info->stack[info->stackTop].codePage;
545             info->unicodeLength = info->stack[info->stackTop].unicodeLength;
546           }
547           break;
548         }
549       }
550       break;
551     case rtfControl:
552       switch(info->rtfMajor)
553       {
554         case rtfCharAttr:
555           ME_RTFCharAttrHook(info);
556           break;
557         case rtfParAttr:
558           ME_RTFParAttrHook(info);
559           break;
560         case rtfTblAttr:
561           ME_RTFTblAttrHook(info);
562           break;
563         case rtfSpecialChar:
564           if (info->rtfMinor == rtfCell)
565           {
566             RTFFlushOutputBuffer(info);
567             ME_InsertTableCellFromCursor(info->editor, 0);
568           }
569       }
570       break;
571   }
572 }
573
574 void
575 ME_StreamInFill(ME_InStream *stream)
576 {
577   stream->editstream->dwError = stream->editstream->pfnCallback(stream->editstream->dwCookie,
578                                                                 (BYTE *)stream->buffer,
579                                                                 sizeof(stream->buffer),
580                                                                 (LONG *)&stream->dwSize);
581   stream->dwUsed = 0;
582 }
583
584 static LRESULT ME_StreamIn(ME_TextEditor *editor, DWORD format, EDITSTREAM *stream)
585 {
586   RTF_Info parser;
587   ME_Style *style;
588   int from, to, to2, nUndoMode;
589   ME_UndoItem *pUI;
590   int nEventMask = editor->nEventMask;
591   ME_InStream inStream;
592
593   TRACE("stream==%p hWnd==%p format==0x%X\n", stream, editor->hWnd, (UINT)format);
594   editor->nEventMask = 0;
595   
596   ME_GetSelection(editor, &from, &to);
597   if ((format & SFF_SELECTION) && (editor->mode & TM_RICHTEXT)) {
598     style = ME_GetSelectionInsertStyle(editor);
599
600     ME_InternalDeleteText(editor, from, to-from);
601   }
602   else {
603     style = editor->pBuffer->pDefaultStyle;
604     ME_AddRefStyle(style);
605     SendMessageA(editor->hWnd, EM_SETSEL, 0, 0);    
606     ME_InternalDeleteText(editor, 0, ME_GetTextLength(editor));
607     from = to = 0;
608     ME_ClearTempStyle(editor);
609     /* FIXME restore default paragraph formatting ! */
610   }
611   
612   nUndoMode = editor->nUndoMode;
613   editor->nUndoMode = umIgnore;
614
615   inStream.editstream = stream;
616   inStream.editstream->dwError = 0;
617   inStream.dwSize = 0;
618   inStream.dwUsed = 0;
619
620   if (format & SF_RTF)
621   {
622     /* Check if it's really RTF, and if it is not, use plain text */
623     ME_StreamInFill(&inStream);
624     if (!inStream.editstream->dwError)
625     {
626       if (strncmp(inStream.buffer, "{\\rtf1", 6) && strncmp(inStream.buffer, "{\\urtf", 6))
627       {
628         format &= ~SF_RTF;
629         format |= SF_TEXT;
630       }
631     }
632   }
633
634   if (!inStream.editstream->dwError)
635   {
636     if (format & SF_RTF) {
637       /* setup the RTF parser */
638       memset(&parser, 0, sizeof parser);
639       RTFSetEditStream(&parser, &inStream);
640       parser.rtfFormat = format&(SF_TEXT|SF_RTF);
641       parser.hwndEdit = editor->hWnd;
642       parser.editor = editor;
643       parser.style = style;
644       WriterInit(&parser);
645       RTFInit(&parser);
646       RTFSetReadHook(&parser, ME_RTFReadHook);
647       BeginFile(&parser);
648   
649       /* do the parsing */
650       RTFRead(&parser);
651       RTFFlushOutputBuffer(&parser);
652       RTFDestroy(&parser);
653
654       style = parser.style;
655     }
656     else if (format & SF_TEXT)
657       ME_StreamInText(editor, format, &inStream, style);
658     else
659       ERR("EM_STREAMIN without SF_TEXT or SF_RTF\n");
660     ME_GetSelection(editor, &to, &to2);
661     /* put the cursor at the top */
662     if (!(format & SFF_SELECTION))
663       SendMessageA(editor->hWnd, EM_SETSEL, 0, 0);
664   }
665   
666   editor->nUndoMode = nUndoMode;
667   if (format & SFF_SELECTION)
668   {
669     pUI = ME_AddUndoItem(editor, diUndoDeleteRun, NULL);
670     TRACE("from %d to %d\n", from, to);
671     if (pUI && from < to)
672     {
673       pUI->nStart = from;
674       pUI->nLen = to-from;
675     }
676     ME_CommitUndo(editor);
677   }
678   else
679   {
680     ME_EmptyUndoStack(editor);
681   }
682
683   ME_ReleaseStyle(style); 
684   editor->nEventMask = nEventMask;
685   if (editor->bRedraw)
686   {
687     ME_UpdateRepaint(editor);
688   }
689   if (!(format & SFF_SELECTION)) {
690     ME_ClearTempStyle(editor);
691   }
692   ME_MoveCaret(editor);
693   ME_SendSelChange(editor);
694   ME_SendRequestResize(editor, FALSE);
695
696   return 0;
697 }
698
699
700 typedef struct tagME_RTFStringStreamStruct
701 {
702   char *string;
703   int pos;
704   int length;
705 } ME_RTFStringStreamStruct;
706
707 static DWORD CALLBACK ME_ReadFromRTFString(DWORD_PTR dwCookie, LPBYTE lpBuff, LONG cb, LONG *pcb)
708 {
709   ME_RTFStringStreamStruct *pStruct = (ME_RTFStringStreamStruct *)dwCookie;
710   int count;
711
712   count = min(cb, pStruct->length - pStruct->pos);
713   memmove(lpBuff, pStruct->string + pStruct->pos, count);
714   pStruct->pos += count;
715   *pcb = count;
716   return 0;
717 }
718
719 static void
720 ME_StreamInRTFString(ME_TextEditor *editor, BOOL selection, char *string)
721 {
722   EDITSTREAM es;
723   ME_RTFStringStreamStruct data;
724
725   data.string = string;
726   data.length = strlen(string);
727   data.pos = 0;
728   es.dwCookie = (DWORD)&data;
729   es.pfnCallback = ME_ReadFromRTFString;
730   ME_StreamIn(editor, SF_RTF | (selection ? SFF_SELECTION : 0), &es);
731 }
732
733
734 ME_DisplayItem *
735 ME_FindItemAtOffset(ME_TextEditor *editor, ME_DIType nItemType, int nOffset, int *nItemOffset)
736 {
737   ME_DisplayItem *item = ME_FindItemFwd(editor->pBuffer->pFirst, diParagraph);
738   
739   while (item && item->member.para.next_para->member.para.nCharOfs <= nOffset)
740     item = ME_FindItemFwd(item, diParagraph);
741
742   if (!item)
743     return item;
744
745   nOffset -= item->member.para.nCharOfs;
746   if (nItemType == diParagraph) {
747     if (nItemOffset)
748       *nItemOffset = nOffset;
749     return item;
750   }
751   
752   do {
753     item = ME_FindItemFwd(item, diRun);
754   } while (item && (item->member.run.nCharOfs + ME_StrLen(item->member.run.strText) <= nOffset));
755   if (item) {
756     nOffset -= item->member.run.nCharOfs;
757     if (nItemOffset)
758       *nItemOffset = nOffset;
759   }
760   return item;
761 }
762
763
764 static int
765 ME_FindText(ME_TextEditor *editor, DWORD flags, CHARRANGE *chrg, WCHAR *text, CHARRANGE *chrgText)
766 {
767   int nStart, nEnd;
768   int nLen = lstrlenW(text);
769   int nMin, nMax;
770   ME_DisplayItem *item;
771   ME_DisplayItem *para;
772
773   TRACE("flags==0x%08lx, chrg->cpMin==%ld, chrg->cpMax==%ld text==%s\n",
774         flags, chrg->cpMin, chrg->cpMax, debugstr_w(text));
775   
776   if (flags & ~(FR_DOWN | FR_MATCHCASE))
777     FIXME("Flags 0x%08lx not implemented\n", flags & ~(FR_DOWN | FR_MATCHCASE));
778
779   nMin = chrg->cpMin;
780   if (chrg->cpMax == -1)
781     nMax = ME_GetTextLength(editor);
782   else
783     nMax = chrg->cpMax;
784   
785   /* when searching up, if cpMin < cpMax, then instead of searching
786    * on [cpMin,cpMax], we search on [0,cpMin], otherwise, search on
787    * [cpMax, cpMin]
788    */
789   if (!(flags & FR_DOWN))
790   {
791     int nSwap = nMax;
792
793     nMax = nMin;
794     if (nMin < nSwap)
795       nMin = 0;
796     else
797       nMin = nSwap;
798   }
799
800   if (!nLen || nMin < 0 || nMax < 0 || nMax < nMin)
801   {
802     if (chrgText)
803       chrgText->cpMin = chrgText->cpMax = -1;
804     return -1;
805   }
806  
807   if (flags & FR_DOWN) /* Forward search */
808   {
809     nStart = nMin;
810     item = ME_FindItemAtOffset(editor, diRun, nStart, &nStart);
811     if (!item)
812     {
813       if (chrgText)
814         chrgText->cpMin = chrgText->cpMax = -1;
815       return -1;
816     }
817
818     para = ME_GetParagraph(item);
819     while (item
820            && para->member.para.nCharOfs + item->member.run.nCharOfs + nStart + nLen <= nMax)
821     {
822       ME_DisplayItem *pCurItem = item;
823       int nCurStart = nStart;
824       int nMatched = 0;
825     
826       while (pCurItem && ME_CharCompare(pCurItem->member.run.strText->szData[nCurStart + nMatched], text[nMatched], (flags & FR_MATCHCASE)))
827       {
828         nMatched++;
829         if (nMatched == nLen)
830         {
831           nStart += para->member.para.nCharOfs + item->member.run.nCharOfs;
832           if (chrgText)
833           {
834             chrgText->cpMin = nStart;
835             chrgText->cpMax = nStart + nLen;
836           }
837           TRACE("found at %d-%d\n", nStart, nStart + nLen);
838           return nStart;
839         }
840         if (nCurStart + nMatched == ME_StrLen(pCurItem->member.run.strText))
841         {
842           pCurItem = ME_FindItemFwd(pCurItem, diRun);
843           para = ME_GetParagraph(pCurItem);
844           nCurStart = -nMatched;
845         }
846       }
847       nStart++;
848       if (nStart == ME_StrLen(item->member.run.strText))
849       {
850         item = ME_FindItemFwd(item, diRun);
851         para = ME_GetParagraph(item);
852         nStart = 0;
853       }
854     }
855   }
856   else /* Backward search */
857   {
858     nEnd = nMax;
859     item = ME_FindItemAtOffset(editor, diRun, nEnd, &nEnd);
860     if (!item) {
861       if (chrgText)
862         chrgText->cpMin = chrgText->cpMax = -1;
863       return -1;
864     }
865     
866     para = ME_GetParagraph(item);
867     
868     while (item
869            && para->member.para.nCharOfs + item->member.run.nCharOfs + nEnd - nLen >= nMin)
870     {
871       ME_DisplayItem *pCurItem = item;
872       int nCurEnd = nEnd;
873       int nMatched = 0;
874       
875       while (ME_CharCompare(pCurItem->member.run.strText->szData[nCurEnd - nMatched - 1], text[nLen - nMatched - 1], (flags & FR_MATCHCASE)))
876       {
877         nMatched++;
878         if (nMatched == nLen)
879         {
880           nStart = para->member.para.nCharOfs + item->member.run.nCharOfs + nCurEnd - nMatched;
881           if (chrgText)
882           {
883             chrgText->cpMin = nStart;
884             chrgText->cpMax = nStart + nLen;
885           }
886           TRACE("found at %d-%d\n", nStart, nStart + nLen);
887           return nStart;
888         }
889         if (nCurEnd - nMatched == 0)
890         {
891           pCurItem = ME_FindItemBack(pCurItem, diRun);
892           /* Don't care about pCurItem becoming NULL here; it's already taken
893            * care of in the exterior loop condition */
894           para = ME_GetParagraph(pCurItem);
895           nCurEnd = ME_StrLen(pCurItem->member.run.strText) + nMatched;
896         }
897       }
898       nEnd--;
899       if (nEnd < 0)
900       {
901         item = ME_FindItemBack(item, diRun);
902         para = ME_GetParagraph(item);
903         nEnd = ME_StrLen(item->member.run.strText);
904       }
905     }
906   }
907   TRACE("not found\n");
908   if (chrgText)
909     chrgText->cpMin = chrgText->cpMax = -1;
910   return -1;
911 }
912
913
914 static BOOL
915 ME_KeyDown(ME_TextEditor *editor, WORD nKey)
916 {
917   BOOL ctrl_is_down = GetKeyState(VK_CONTROL) & 0x8000;
918   BOOL shift_is_down = GetKeyState(VK_SHIFT) & 0x8000;
919   
920   switch (nKey)
921   {
922     case VK_LEFT:
923     case VK_RIGHT:
924     case VK_UP:
925     case VK_DOWN:
926     case VK_HOME:
927     case VK_END:
928     case VK_PRIOR:
929     case VK_NEXT:
930       ME_ArrowKey(editor, nKey, shift_is_down, ctrl_is_down);
931       return TRUE;
932     case VK_BACK:
933     case VK_DELETE:
934       /* FIXME backspace and delete aren't the same, they act different wrt paragraph style of the merged paragraph */
935       if (GetWindowLongW(editor->hWnd, GWL_STYLE) & ES_READONLY)
936         return FALSE;
937       if (ME_IsSelection(editor))
938         ME_DeleteSelection(editor);
939       else if (nKey == VK_DELETE || ME_ArrowKey(editor, VK_LEFT, FALSE, FALSE))
940         ME_DeleteTextAtCursor(editor, 1, 1);
941       else
942         return TRUE;
943       ME_QueueInvalidateFromCursor(editor, 1);
944       ME_UpdateRepaint(editor);
945       ME_SendRequestResize(editor, FALSE);
946       return TRUE;
947
948     default:
949       if (ctrl_is_down)
950       {
951         if (nKey == 'W')
952         {
953           CHARFORMAT2W chf;
954           char buf[2048];
955           
956           ME_GetSelectionCharFormat(editor, &chf);
957           ME_DumpStyleToBuf(&chf, buf);
958           MessageBoxA(NULL, buf, "Style dump", MB_OK);
959         }
960         if (nKey == 'Q')
961         {
962           ME_CheckCharOffsets(editor);
963         }
964       }
965   }
966   return FALSE;
967 }
968
969 static void ME_ShowContextMenu(ME_TextEditor *editor, int x, int y)
970 {
971   CHARRANGE selrange;
972   HMENU menu;
973   int seltype = 0;
974   if(!editor->lpOleCallback)
975     return;
976   ME_GetSelection(editor, (int *)&selrange.cpMin, (int *)&selrange.cpMax);
977   if(selrange.cpMin == selrange.cpMax)
978     seltype |= SEL_EMPTY;
979   else
980   {
981     /* FIXME: Handle objects */
982     seltype |= SEL_TEXT;
983     if(selrange.cpMax-selrange.cpMin > 1)
984       seltype |= SEL_MULTICHAR;
985   }
986   if(SUCCEEDED(IRichEditOleCallback_GetContextMenu(editor->lpOleCallback, seltype, NULL, &selrange, &menu)))
987   {
988     TrackPopupMenu(menu, TPM_LEFTALIGN | TPM_RIGHTBUTTON, x, y, 0, GetParent(editor->hWnd), NULL);
989     DestroyMenu(menu);
990   }
991 }
992
993 ME_TextEditor *ME_MakeEditor(HWND hWnd) {
994   ME_TextEditor *ed = ALLOC_OBJ(ME_TextEditor);
995   HDC hDC;
996   int i;
997   ed->hWnd = hWnd;
998   ed->bEmulateVersion10 = FALSE;
999   ed->pBuffer = ME_MakeText();
1000   hDC = GetDC(hWnd);
1001   ME_MakeFirstParagraph(hDC, ed->pBuffer);
1002   ReleaseDC(hWnd, hDC);
1003   ed->bCaretShown = FALSE;
1004   ed->nCursors = 3;
1005   ed->pCursors = ALLOC_N_OBJ(ME_Cursor, ed->nCursors);
1006   ed->pCursors[0].pRun = ME_FindItemFwd(ed->pBuffer->pFirst, diRun);
1007   ed->pCursors[0].nOffset = 0;
1008   ed->pCursors[1].pRun = ME_FindItemFwd(ed->pBuffer->pFirst, diRun);
1009   ed->pCursors[1].nOffset = 0;
1010   ed->nLastTotalLength = ed->nTotalLength = 0;
1011   ed->nUDArrowX = -1;
1012   ed->nSequence = 0;
1013   ed->rgbBackColor = -1;
1014   ed->hbrBackground = GetSysColorBrush(COLOR_WINDOW);
1015   ed->bCaretAtEnd = FALSE;
1016   ed->nEventMask = 0;
1017   ed->nModifyStep = 0;
1018   ed->pUndoStack = ed->pRedoStack = NULL;
1019   ed->nUndoMode = umAddToUndo;
1020   ed->nParagraphs = 1;
1021   ed->nLastSelStart = ed->nLastSelEnd = 0;
1022   ed->nScrollPosY = 0;
1023   ed->nZoomNumerator = ed->nZoomDenominator = 0;
1024   ed->bRedraw = TRUE;
1025   ed->bHideSelection = FALSE;
1026   ed->nInvalidOfs = -1;
1027   ed->pfnWordBreak = NULL;
1028   ed->lpOleCallback = NULL;
1029   ed->mode = TM_RICHTEXT | TM_MULTILEVELUNDO | TM_MULTICODEPAGE;
1030   GetClientRect(hWnd, &ed->rcFormat);
1031   for (i=0; i<HFONT_CACHE_SIZE; i++)
1032   {
1033     ed->pFontCache[i].nRefs = 0;
1034     ed->pFontCache[i].nAge = 0;
1035     ed->pFontCache[i].hFont = NULL;
1036   }
1037   ME_CheckCharOffsets(ed);
1038   return ed;
1039 }
1040
1041 typedef struct tagME_GlobalDestStruct
1042 {
1043   HGLOBAL hData;
1044   int nLength;
1045 } ME_GlobalDestStruct;
1046
1047 static DWORD CALLBACK ME_ReadFromHGLOBALUnicode(DWORD_PTR dwCookie, LPBYTE lpBuff, LONG cb, LONG *pcb)
1048 {
1049   ME_GlobalDestStruct *pData = (ME_GlobalDestStruct *)dwCookie;
1050   int i;
1051   WORD *pSrc, *pDest;
1052   
1053   cb = cb >> 1;
1054   pDest = (WORD *)lpBuff;
1055   pSrc = (WORD *)GlobalLock(pData->hData);
1056   for (i = 0; i<cb && pSrc[pData->nLength+i]; i++) {
1057     pDest[i] = pSrc[pData->nLength+i];
1058   }    
1059   pData->nLength += i;
1060   *pcb = 2*i;
1061   GlobalUnlock(pData->hData);
1062   return 0;
1063 }
1064
1065 static DWORD CALLBACK ME_ReadFromHGLOBALRTF(DWORD_PTR dwCookie, LPBYTE lpBuff, LONG cb, LONG *pcb)
1066 {
1067   ME_GlobalDestStruct *pData = (ME_GlobalDestStruct *)dwCookie;
1068   int i;
1069   BYTE *pSrc, *pDest;
1070   
1071   pDest = lpBuff;
1072   pSrc = (BYTE *)GlobalLock(pData->hData);
1073   for (i = 0; i<cb && pSrc[pData->nLength+i]; i++) {
1074     pDest[i] = pSrc[pData->nLength+i];
1075   }    
1076   pData->nLength += i;
1077   *pcb = i;
1078   GlobalUnlock(pData->hData);
1079   return 0;
1080 }
1081
1082
1083 void ME_DestroyEditor(ME_TextEditor *editor)
1084 {
1085   ME_DisplayItem *pFirst = editor->pBuffer->pFirst;
1086   ME_DisplayItem *p = pFirst, *pNext = NULL;
1087   int i;
1088   
1089   ME_ClearTempStyle(editor);
1090   ME_EmptyUndoStack(editor);
1091   while(p) {
1092     pNext = p->next;
1093     ME_DestroyDisplayItem(p);    
1094     p = pNext;
1095   }
1096   ME_ReleaseStyle(editor->pBuffer->pDefaultStyle);
1097   for (i=0; i<HFONT_CACHE_SIZE; i++)
1098   {
1099     if (editor->pFontCache[i].hFont)
1100       DeleteObject(editor->pFontCache[i].hFont);
1101   }
1102   DeleteObject(editor->hbrBackground);
1103   if(editor->lpOleCallback)
1104     IUnknown_Release(editor->lpOleCallback);
1105
1106   FREE_OBJ(editor);
1107 }
1108
1109 static const WCHAR wszClassName[] = {'R', 'i', 'c', 'h', 'E', 'd', 'i', 't', '2', '0', 'W', 0};
1110 static const WCHAR wszClassName50[] = {'R', 'i', 'c', 'h', 'E', 'd', 'i', 't', '5', '0', 'W', 0};
1111 static const WCHAR wszClassNameListBox[] = {'R','E','L','i','s','t','B','o','x','2','0','W', 0};
1112 static const WCHAR wszClassNameComboBox[] = {'R','E','C','o','m','b','o','B','o','x','2','0','W', 0};
1113
1114 BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
1115 {
1116     TRACE("\n");
1117     switch (fdwReason)
1118     {
1119     case DLL_PROCESS_ATTACH:
1120       DisableThreadLibraryCalls(hinstDLL);
1121       me_heap = HeapCreate (0, 0x10000, 0);
1122       ME_RegisterEditorClass(hinstDLL);
1123       break;
1124
1125     case DLL_PROCESS_DETACH:
1126       UnregisterClassW(wszClassName, 0);
1127       UnregisterClassW(wszClassName50, 0);
1128       UnregisterClassA("RichEdit20A", 0);
1129       UnregisterClassA("RichEdit50A", 0);
1130       if (ME_ListBoxRegistered)
1131           UnregisterClassW(wszClassNameListBox, 0);
1132       if (ME_ComboBoxRegistered)
1133           UnregisterClassW(wszClassNameComboBox, 0);
1134       HeapDestroy (me_heap);
1135       me_heap = NULL;
1136       break;
1137     }
1138     return TRUE;
1139 }
1140
1141
1142 #define UNSUPPORTED_MSG(e) \
1143   case e: \
1144     FIXME(#e ": stub\n"); \
1145     return DefWindowProcW(hWnd, msg, wParam, lParam);
1146
1147 static const char * const edit_messages[] = {
1148   "EM_GETSEL",
1149   "EM_SETSEL",
1150   "EM_GETRECT",
1151   "EM_SETRECT",
1152   "EM_SETRECTNP",
1153   "EM_SCROLL",
1154   "EM_LINESCROLL",
1155   "EM_SCROLLCARET",
1156   "EM_GETMODIFY",
1157   "EM_SETMODIFY",
1158   "EM_GETLINECOUNT",
1159   "EM_LINEINDEX",
1160   "EM_SETHANDLE",
1161   "EM_GETHANDLE",
1162   "EM_GETTHUMB",
1163   "EM_UNKNOWN_BF",
1164   "EM_UNKNOWN_C0",
1165   "EM_LINELENGTH",
1166   "EM_REPLACESEL",
1167   "EM_UNKNOWN_C3",
1168   "EM_GETLINE",
1169   "EM_LIMITTEXT",
1170   "EM_CANUNDO",
1171   "EM_UNDO",
1172   "EM_FMTLINES",
1173   "EM_LINEFROMCHAR",
1174   "EM_UNKNOWN_CA",
1175   "EM_SETTABSTOPS",
1176   "EM_SETPASSWORDCHAR",
1177   "EM_EMPTYUNDOBUFFER",
1178   "EM_GETFIRSTVISIBLELINE",
1179   "EM_SETREADONLY",
1180   "EM_SETWORDBREAKPROC",
1181   "EM_GETWORDBREAKPROC",
1182   "EM_GETPASSWORDCHAR",
1183   "EM_SETMARGINS",
1184   "EM_GETMARGINS",
1185   "EM_GETLIMITTEXT",
1186   "EM_POSFROMCHAR",
1187   "EM_CHARFROMPOS"
1188 };
1189
1190 static const char * const richedit_messages[] = {
1191   "EM_CANPASTE",
1192   "EM_DISPLAYBAND",
1193   "EM_EXGETSEL",
1194   "EM_EXLIMITTEXT",
1195   "EM_EXLINEFROMCHAR",
1196   "EM_EXSETSEL",
1197   "EM_FINDTEXT",
1198   "EM_FORMATRANGE",
1199   "EM_GETCHARFORMAT",
1200   "EM_GETEVENTMASK",
1201   "EM_GETOLEINTERFACE",
1202   "EM_GETPARAFORMAT",
1203   "EM_GETSELTEXT",
1204   "EM_HIDESELECTION", 
1205   "EM_PASTESPECIAL",
1206   "EM_REQUESTRESIZE",
1207   "EM_SELECTIONTYPE",
1208   "EM_SETBKGNDCOLOR",
1209   "EM_SETCHARFORMAT",
1210   "EM_SETEVENTMASK",
1211   "EM_SETOLECALLBACK",
1212   "EM_SETPARAFORMAT",
1213   "EM_SETTARGETDEVICE",
1214   "EM_STREAMIN",
1215   "EM_STREAMOUT",
1216   "EM_GETTEXTRANGE",
1217   "EM_FINDWORDBREAK",
1218   "EM_SETOPTIONS",
1219   "EM_GETOPTIONS",
1220   "EM_FINDTEXTEX",
1221   "EM_GETWORDBREAKPROCEX",
1222   "EM_SETWORDBREAKPROCEX",
1223   "EM_SETUNDOLIMIT",
1224   "EM_UNKNOWN_USER_83",
1225   "EM_REDO",
1226   "EM_CANREDO",
1227   "EM_GETUNDONAME",
1228   "EM_GETREDONAME",
1229   "EM_STOPGROUPTYPING",
1230   "EM_SETTEXTMODE",
1231   "EM_GETTEXTMODE",
1232   "EM_AUTOURLDETECT",
1233   "EM_GETAUTOURLDETECT",
1234   "EM_SETPALETTE",
1235   "EM_GETTEXTEX",
1236   "EM_GETTEXTLENGTHEX",
1237   "EM_SHOWSCROLLBAR",
1238   "EM_SETTEXTEX",
1239   "EM_UNKNOWN_USER_98",
1240   "EM_UNKNOWN_USER_99",
1241   "EM_SETPUNCTUATION",
1242   "EM_GETPUNCTUATION",
1243   "EM_SETWORDWRAPMODE",
1244   "EM_GETWORDWRAPMODE",
1245   "EM_SETIMECOLOR",
1246   "EM_GETIMECOLOR",
1247   "EM_SETIMEOPTIONS",
1248   "EM_GETIMEOPTIONS",
1249   "EM_CONVPOSITION",
1250   "EM_UNKNOWN_USER_109",
1251   "EM_UNKNOWN_USER_110",
1252   "EM_UNKNOWN_USER_111",
1253   "EM_UNKNOWN_USER_112",
1254   "EM_UNKNOWN_USER_113",
1255   "EM_UNKNOWN_USER_114",
1256   "EM_UNKNOWN_USER_115",
1257   "EM_UNKNOWN_USER_116",
1258   "EM_UNKNOWN_USER_117",
1259   "EM_UNKNOWN_USER_118",
1260   "EM_UNKNOWN_USER_119",
1261   "EM_SETLANGOPTIONS",
1262   "EM_GETLANGOPTIONS",
1263   "EM_GETIMECOMPMODE",
1264   "EM_FINDTEXTW",
1265   "EM_FINDTEXTEXW",
1266   "EM_RECONVERSION",
1267   "EM_SETIMEMODEBIAS",
1268   "EM_GETIMEMODEBIAS"
1269 };
1270
1271 static const char *
1272 get_msg_name(UINT msg)
1273 {
1274   if (msg >= EM_GETSEL && msg <= EM_SETLIMITTEXT)
1275     return edit_messages[msg - EM_GETSEL];
1276   if (msg >= EM_CANPASTE && msg <= EM_GETIMEMODEBIAS)
1277     return richedit_messages[msg - EM_CANPASTE];
1278   return "";
1279 }
1280
1281 /******************************************************************
1282  *        RichEditANSIWndProc (RICHED20.10)
1283  */
1284 LRESULT WINAPI RichEditANSIWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) {
1285   SCROLLINFO si;
1286   ME_TextEditor *editor = (ME_TextEditor *)GetWindowLongW(hWnd, 0);
1287   
1288   TRACE("hWnd %p msg %04x (%s) %08x %08lx\n",
1289         hWnd, msg, get_msg_name(msg), wParam, lParam);
1290   
1291   switch(msg) {
1292   
1293   UNSUPPORTED_MSG(EM_DISPLAYBAND)
1294   UNSUPPORTED_MSG(EM_EXLIMITTEXT)
1295   UNSUPPORTED_MSG(EM_FINDWORDBREAK)
1296   UNSUPPORTED_MSG(EM_FMTLINES)
1297   UNSUPPORTED_MSG(EM_FORMATRANGE)
1298   UNSUPPORTED_MSG(EM_GETBIDIOPTIONS)
1299   UNSUPPORTED_MSG(EM_GETEDITSTYLE)
1300   UNSUPPORTED_MSG(EM_GETIMECOMPMODE)
1301   /* UNSUPPORTED_MSG(EM_GETIMESTATUS) missing in Wine headers */
1302   UNSUPPORTED_MSG(EM_GETLANGOPTIONS)
1303   UNSUPPORTED_MSG(EM_GETLIMITTEXT)
1304   UNSUPPORTED_MSG(EM_GETLINE)
1305   /* UNSUPPORTED_MSG(EM_GETOLEINTERFACE) separate stub */
1306   UNSUPPORTED_MSG(EM_GETPASSWORDCHAR)
1307   UNSUPPORTED_MSG(EM_GETREDONAME)
1308   UNSUPPORTED_MSG(EM_GETTEXTMODE)
1309   UNSUPPORTED_MSG(EM_GETTYPOGRAPHYOPTIONS)
1310   UNSUPPORTED_MSG(EM_GETUNDONAME)
1311   UNSUPPORTED_MSG(EM_GETWORDBREAKPROCEX)
1312   UNSUPPORTED_MSG(EM_LIMITTEXT) /* also known as EM_SETLIMITTEXT */
1313   UNSUPPORTED_MSG(EM_PASTESPECIAL)
1314   UNSUPPORTED_MSG(EM_SELECTIONTYPE)
1315   UNSUPPORTED_MSG(EM_SETBIDIOPTIONS)
1316   UNSUPPORTED_MSG(EM_SETEDITSTYLE)
1317   UNSUPPORTED_MSG(EM_SETFONTSIZE)
1318   UNSUPPORTED_MSG(EM_SETLANGOPTIONS)
1319   UNSUPPORTED_MSG(EM_SETPALETTE)
1320   UNSUPPORTED_MSG(EM_SETPASSWORDCHAR)
1321   UNSUPPORTED_MSG(EM_SETSCROLLPOS)
1322   UNSUPPORTED_MSG(EM_SETTABSTOPS)
1323   UNSUPPORTED_MSG(EM_SETTARGETDEVICE)
1324   UNSUPPORTED_MSG(EM_SETTYPOGRAPHYOPTIONS)
1325   UNSUPPORTED_MSG(EM_SETUNDOLIMIT)
1326   UNSUPPORTED_MSG(EM_SETWORDBREAKPROCEX)
1327   UNSUPPORTED_MSG(EM_SHOWSCROLLBAR)
1328   UNSUPPORTED_MSG(WM_SETFONT)
1329   UNSUPPORTED_MSG(WM_STYLECHANGING)
1330   UNSUPPORTED_MSG(WM_STYLECHANGED)
1331 /*  UNSUPPORTED_MSG(WM_UNICHAR) FIXME missing in Wine headers */
1332     
1333 /* Messages specific to Richedit controls */
1334   
1335   case EM_STREAMIN:
1336    return ME_StreamIn(editor, wParam, (EDITSTREAM*)lParam);
1337   case EM_STREAMOUT:
1338    return ME_StreamOut(editor, wParam, (EDITSTREAM *)lParam);
1339   case WM_GETDLGCODE:
1340   {
1341     UINT code = DLGC_WANTCHARS|DLGC_WANTARROWS;
1342     if (GetWindowLongW(hWnd, GWL_STYLE)&ES_WANTRETURN)
1343       code |= 0; /* FIXME what can we do here ? ask for messages and censor them ? */
1344     return code;
1345   }
1346   case WM_NCCREATE:
1347   {
1348     CREATESTRUCTW *pcs = (CREATESTRUCTW *)lParam;
1349     TRACE("WM_NCCREATE: style 0x%08lx\n", pcs->style);
1350     editor = ME_MakeEditor(hWnd);
1351     SetWindowLongW(hWnd, 0, (long)editor);
1352     pcs = 0; /* ignore */
1353     return TRUE;
1354   }
1355   case EM_EMPTYUNDOBUFFER:
1356     ME_EmptyUndoStack(editor);
1357     return 0;
1358   case EM_GETSEL:
1359   {
1360     /* Note: wParam/lParam can be NULL */
1361     UINT from, to;
1362     PUINT pfrom = wParam ? (PUINT)wParam : &from;
1363     PUINT pto = lParam ? (PUINT)lParam : &to;
1364     ME_GetSelection(editor, (int *)pfrom, (int *)pto);
1365     if ((*pfrom|*pto) & 0xFFFF0000)
1366       return -1;
1367     return MAKELONG(*pfrom,*pto);
1368   }
1369   case EM_EXGETSEL:
1370   {
1371     CHARRANGE *pRange = (CHARRANGE *)lParam;
1372     ME_GetSelection(editor, (int *)&pRange->cpMin, (int *)&pRange->cpMax);
1373     TRACE("EM_EXGETSEL = (%ld,%ld)\n", pRange->cpMin, pRange->cpMax);
1374     return 0;
1375   }
1376   case EM_CANUNDO:
1377     return editor->pUndoStack != NULL;
1378   case EM_CANREDO:
1379     return editor->pRedoStack != NULL;
1380   case WM_UNDO: /* FIXME: actually not the same */
1381   case EM_UNDO:
1382     ME_Undo(editor);
1383     return 0;
1384   case EM_REDO:
1385     ME_Redo(editor);
1386     return 0;
1387   case EM_GETOPTIONS:
1388   {
1389     /* these flags are equivalent to the ES_* counterparts */
1390     DWORD mask = ECO_VERTICAL | ECO_AUTOHSCROLL | ECO_AUTOVSCROLL |
1391                  ECO_NOHIDESEL | ECO_READONLY | ECO_WANTRETURN;
1392     DWORD settings = GetWindowLongW(hWnd, GWL_STYLE) & mask;
1393
1394     return settings;
1395   }
1396   case EM_SETOPTIONS:
1397   {
1398     /* these flags are equivalent to ES_* counterparts                      
1399      * ECO_READONLY is already implemented in the code, only requires 
1400      * setting the bit to work                                        
1401      */
1402     DWORD mask = ECO_VERTICAL | ECO_AUTOHSCROLL | ECO_AUTOVSCROLL |
1403                  ECO_NOHIDESEL | ECO_READONLY | ECO_WANTRETURN;
1404     DWORD raw = GetWindowLongW(hWnd, GWL_STYLE);
1405     DWORD settings = mask & raw;
1406
1407     switch(wParam)
1408     {
1409       case ECOOP_SET:
1410         settings = lParam;
1411         break;
1412       case ECOOP_OR:
1413         settings |= lParam;
1414         break;
1415       case ECOOP_AND:
1416         settings &= lParam;
1417         break;
1418       case ECOOP_XOR:
1419         settings ^= lParam;
1420     }
1421     SetWindowLongW(hWnd, GWL_STYLE, (raw & ~mask) | (settings & mask));
1422
1423     if (lParam & ECO_AUTOWORDSELECTION)
1424       FIXME("ECO_AUTOWORDSELECTION not implemented yet!\n");
1425     if (lParam & ECO_SELECTIONBAR)
1426       FIXME("ECO_SELECTIONBAR not implemented yet!\n");
1427     if (lParam & ECO_VERTICAL)
1428       FIXME("ECO_VERTICAL not implemented yet!\n");
1429     if (lParam & ECO_AUTOHSCROLL)
1430       FIXME("ECO_AUTOHSCROLL not implemented yet!\n");
1431     if (lParam & ECO_AUTOVSCROLL)
1432       FIXME("ECO_AUTOVSCROLL not implemented yet!\n");
1433     if (lParam & ECO_NOHIDESEL)
1434       FIXME("ECO_NOHIDESEL not implemented yet!\n");
1435     if (lParam & ECO_WANTRETURN)
1436       FIXME("ECO_WANTRETURN not implemented yet!\n");
1437
1438     return settings;
1439   }
1440   case EM_SETSEL:
1441   {
1442     ME_InvalidateSelection(editor);
1443     ME_SetSelection(editor, wParam, lParam);
1444     ME_InvalidateSelection(editor);
1445     ME_SendSelChange(editor);
1446     return 0;
1447   }
1448   case EM_AUTOURLDETECT:
1449   {
1450     if (wParam==1 || wParam ==0) 
1451     {
1452         editor->AutoURLDetect_bEnable = (BOOL)wParam;
1453         return 0;
1454     }
1455     return E_INVALIDARG;
1456   }
1457   case EM_GETAUTOURLDETECT:
1458   {
1459         return editor->AutoURLDetect_bEnable;
1460   }
1461   case EM_EXSETSEL:
1462   {
1463     CHARRANGE *pRange = (CHARRANGE *)lParam;
1464     TRACE("EM_EXSETSEL (%ld,%ld)\n", pRange->cpMin, pRange->cpMax);
1465     ME_InvalidateSelection(editor);
1466     ME_SetSelection(editor, pRange->cpMin, pRange->cpMax);
1467     ME_InvalidateSelection(editor);
1468     ME_SendSelChange(editor);
1469     return 0;
1470   }
1471   case EM_SETTEXTEX:
1472   {
1473     LPWSTR wszText = (LPWSTR)lParam;
1474     SETTEXTEX *pStruct = (SETTEXTEX *)wParam;
1475     size_t len = lstrlenW(wszText);
1476     int from, to;
1477     ME_Style *style;
1478     TRACE("EM_SETTEXEX - %s, flags %d, cp %d\n", debugstr_w(wszText), (int)pStruct->flags, pStruct->codepage);
1479     if (pStruct->codepage != 1200) {
1480       FIXME("EM_SETTEXTEX only supports unicode right now!\n"); 
1481       return 0;
1482     }
1483     /* FIXME: this should support RTF strings too, according to MSDN */
1484     if (pStruct->flags & ST_SELECTION) {
1485       ME_GetSelection(editor, &from, &to);
1486       style = ME_GetSelectionInsertStyle(editor);
1487       ME_InternalDeleteText(editor, from, to - from);
1488       ME_InsertTextFromCursor(editor, 0, wszText, len, style);
1489       ME_ReleaseStyle(style);
1490     }
1491     else {
1492       ME_InternalDeleteText(editor, 0, ME_GetTextLength(editor));
1493       ME_InsertTextFromCursor(editor, 0, wszText, -1, editor->pBuffer->pDefaultStyle);
1494       len = 1;
1495     }
1496     ME_CommitUndo(editor);
1497     if (!(pStruct->flags & ST_KEEPUNDO))
1498       ME_EmptyUndoStack(editor);
1499     ME_UpdateRepaint(editor);
1500     return len;
1501   }
1502   case EM_SETBKGNDCOLOR:
1503   {
1504     LRESULT lColor = ME_GetBackColor(editor);
1505     if (editor->rgbBackColor != -1)
1506       DeleteObject(editor->hbrBackground);
1507     if (wParam)
1508     {
1509       editor->rgbBackColor = -1;
1510       editor->hbrBackground = GetSysColorBrush(COLOR_WINDOW);
1511     }
1512     else
1513     {
1514       editor->rgbBackColor = lParam;
1515       editor->hbrBackground = CreateSolidBrush(editor->rgbBackColor);
1516     }
1517     if (editor->bRedraw)
1518     {
1519       InvalidateRect(hWnd, NULL, TRUE);
1520       UpdateWindow(hWnd);
1521     }
1522     return lColor;
1523   }
1524   case EM_GETMODIFY:
1525     return editor->nModifyStep == 0 ? 0 : 1;
1526   case EM_SETMODIFY:
1527   {
1528     if (wParam)
1529       editor->nModifyStep = 0x80000000;
1530     else
1531       editor->nModifyStep = 0;
1532     
1533     return 0;
1534   }
1535   case EM_SETREADONLY:
1536   {
1537     long nStyle = GetWindowLongW(hWnd, GWL_STYLE);
1538     if (wParam)
1539       nStyle |= ES_READONLY;
1540     else
1541       nStyle &= ~ES_READONLY;
1542     SetWindowLongW(hWnd, GWL_STYLE, nStyle);
1543     return 0;
1544   }
1545   case EM_SETEVENTMASK:
1546   {
1547     DWORD nOldMask = editor->nEventMask;
1548     
1549     editor->nEventMask = lParam;
1550     return nOldMask;
1551   }
1552   case EM_GETEVENTMASK:
1553     return editor->nEventMask;
1554   case EM_SETCHARFORMAT:
1555   {
1556     CHARFORMAT2W buf, *p;
1557     BOOL bRepaint = TRUE;
1558     p = ME_ToCF2W(&buf, (CHARFORMAT2W *)lParam);
1559     if (!wParam || (editor->mode & TM_PLAINTEXT))
1560       ME_SetDefaultCharFormat(editor, p);
1561     else if (wParam == (SCF_WORD | SCF_SELECTION))
1562       FIXME("EM_SETCHARFORMAT: word selection not supported\n");
1563     else if (wParam == SCF_ALL)
1564       ME_SetCharFormat(editor, 0, ME_GetTextLength(editor), p);
1565     else {
1566       int from, to;
1567       ME_GetSelection(editor, &from, &to);
1568       bRepaint = (from != to);
1569       ME_SetSelectionCharFormat(editor, p);
1570     }
1571     ME_CommitUndo(editor);
1572     if (bRepaint)
1573       ME_UpdateRepaint(editor);
1574     return 0;
1575   }
1576   case EM_GETCHARFORMAT:
1577   {
1578     CHARFORMAT2W tmp, *dst = (CHARFORMAT2W *)lParam;
1579     if (dst->cbSize != sizeof(CHARFORMATA) &&
1580         dst->cbSize != sizeof(CHARFORMATW) &&
1581         dst->cbSize != sizeof(CHARFORMAT2A) &&
1582         dst->cbSize != sizeof(CHARFORMAT2W))
1583       return 0;
1584     tmp.cbSize = sizeof(tmp);
1585     if (!wParam)
1586       ME_GetDefaultCharFormat(editor, &tmp);
1587     else
1588       ME_GetSelectionCharFormat(editor, &tmp);
1589     ME_CopyToCFAny(dst, &tmp);
1590     return tmp.dwMask;
1591   }
1592   case EM_SETPARAFORMAT:
1593     ME_SetSelectionParaFormat(editor, (PARAFORMAT2 *)lParam);
1594     ME_UpdateRepaint(editor);
1595     ME_CommitUndo(editor);
1596     return 0;
1597   case EM_GETPARAFORMAT:
1598     ME_GetSelectionParaFormat(editor, (PARAFORMAT2 *)lParam);
1599     return 0;
1600   case EM_GETFIRSTVISIBLELINE:
1601   {
1602     ME_DisplayItem *p = editor->pBuffer->pFirst;
1603     int y = editor->nScrollPosY;
1604     int ypara = 0;
1605     int count = 0;
1606     int ystart, yend;
1607     while(p) {
1608       p = ME_FindItemFwd(p, diStartRowOrParagraphOrEnd);
1609       if (p->type == diTextEnd)
1610         break;
1611       if (p->type == diParagraph) {
1612         ypara = p->member.para.nYPos;
1613         continue;
1614       }
1615       ystart = ypara + p->member.row.nYPos;
1616       yend = ystart + p->member.row.nHeight;
1617       if (y < yend) {
1618         break;
1619       }
1620       count++;
1621     }
1622     return count;
1623   }
1624   case EM_HIDESELECTION:
1625   {
1626      editor->bHideSelection = (wParam != 0);
1627      ME_InvalidateSelection(editor);
1628      return 0;
1629   }
1630   case EM_LINESCROLL:
1631   {
1632     int nPos = editor->nScrollPosY, nEnd= editor->nTotalLength - editor->sizeWindow.cy;
1633     nPos += 8 * lParam; /* FIXME follow the original */
1634     if (nPos>=nEnd)
1635       nPos = nEnd;
1636     if (nPos<0)
1637       nPos = 0;
1638     if (nPos != editor->nScrollPosY) {
1639       int dy = editor->nScrollPosY - nPos;
1640       editor->nScrollPosY = nPos;
1641       SetScrollPos(hWnd, SB_VERT, nPos, TRUE);
1642       if (editor->bRedraw)
1643       {
1644         ScrollWindow(hWnd, 0, dy, NULL, NULL);
1645         UpdateWindow(hWnd);
1646       }
1647     }
1648     return TRUE; /* Should return false if a single line richedit control */
1649   }
1650   case WM_CLEAR:
1651   {
1652     int from, to;
1653     ME_GetSelection(editor, &from, &to);
1654     ME_InternalDeleteText(editor, from, to-from);
1655     ME_CommitUndo(editor);
1656     ME_UpdateRepaint(editor);
1657     return 0;
1658   }
1659   case EM_REPLACESEL:
1660   {
1661     int from, to;
1662     ME_Style *style;
1663     LPWSTR wszText = ME_ToUnicode(hWnd, (void *)lParam);
1664     size_t len = lstrlenW(wszText);
1665     TRACE("EM_REPLACESEL - %s\n", debugstr_w(wszText));
1666     
1667     ME_GetSelection(editor, &from, &to);
1668     style = ME_GetSelectionInsertStyle(editor);
1669     ME_InternalDeleteText(editor, from, to-from);
1670     ME_InsertTextFromCursor(editor, 0, wszText, len, style);
1671     ME_ReleaseStyle(style);
1672     ME_EndToUnicode(hWnd, wszText);
1673     /* drop temporary style if line end */
1674     /* FIXME question: does abc\n mean: put abc, clear temp style, put \n? (would require a change) */  
1675     if (len>0 && wszText[len-1] == '\n')
1676       ME_ClearTempStyle(editor);
1677       
1678     ME_CommitUndo(editor);
1679     if (!wParam)
1680       ME_EmptyUndoStack(editor);
1681     ME_UpdateRepaint(editor);
1682     return 0;
1683   }
1684   case EM_SCROLLCARET:
1685   {
1686     int top, bottom; /* row's edges relative to document top */
1687     ME_DisplayItem *para, *row;
1688
1689     row = ME_RowStart(editor->pCursors[0].pRun);
1690     para = ME_GetParagraph(row);
1691     top = para->member.para.nYPos + row->member.row.nYPos;
1692     bottom = top + row->member.row.nHeight;
1693
1694     if ((top < editor->nScrollPosY)
1695         || (editor->nScrollPosY + editor->sizeWindow.cy < bottom))
1696     {
1697       int dy;
1698       int prevScrollPosY = editor->nScrollPosY;
1699
1700       if (top < editor->nScrollPosY) /* caret above window */
1701         editor->nScrollPosY = top;
1702       else /* caret below window */
1703         editor->nScrollPosY = bottom - editor->sizeWindow.cy;
1704
1705       if (editor->nScrollPosY < 0)
1706         editor->nScrollPosY = 0;
1707
1708       dy = prevScrollPosY - editor->nScrollPosY;
1709       SetScrollPos(hWnd, SB_VERT, editor->nScrollPosY, TRUE);
1710       if (editor->bRedraw)
1711       {
1712         ScrollWindow(hWnd, 0, dy, NULL, NULL);
1713         UpdateWindow(hWnd);
1714       }
1715     }
1716
1717     return 0;
1718   }
1719   case WM_SETTEXT:
1720   {
1721     ME_InternalDeleteText(editor, 0, ME_GetTextLength(editor));
1722     if (lParam)
1723     {
1724       TRACE("WM_SETTEXT lParam==%lx\n",lParam);
1725       if (!IsWindowUnicode(hWnd) && !strncmp((char *)lParam, "{\\rtf1", 6))
1726       {
1727         /* Undocumented: WM_SETTEXT supports RTF text */
1728         ME_StreamInRTFString(editor, 0, (char *)lParam);
1729       }
1730       else
1731       {
1732         LPWSTR wszText = ME_ToUnicode(hWnd, (void *)lParam);
1733         TRACE("WM_SETTEXT - %s\n", debugstr_w(wszText)); /* debugstr_w() */
1734         if (lstrlenW(wszText) > 0)
1735         {
1736           /* uses default style! */
1737           ME_InsertTextFromCursor(editor, 0, wszText, -1, editor->pBuffer->pDefaultStyle);
1738         }
1739         ME_EndToUnicode(hWnd, wszText);
1740       }
1741     }
1742     else
1743       TRACE("WM_SETTEXT - NULL\n");
1744     ME_CommitUndo(editor);
1745     ME_EmptyUndoStack(editor);
1746     ME_SetSelection(editor, 0, 0);
1747     ME_UpdateRepaint(editor);
1748     return 0;
1749   }
1750   case EM_CANPASTE:
1751   {
1752     UINT nRTFFormat = RegisterClipboardFormatA("Rich Text Format");
1753     if (IsClipboardFormatAvailable(nRTFFormat))
1754       return TRUE;
1755     if (IsClipboardFormatAvailable(CF_UNICODETEXT))
1756       return TRUE;
1757     return FALSE;
1758   }
1759   case WM_PASTE:
1760   {    
1761     DWORD dwFormat = 0;
1762     EDITSTREAM es;
1763     ME_GlobalDestStruct gds;
1764     UINT nRTFFormat = RegisterClipboardFormatA("Rich Text Format");
1765     UINT cf = 0;
1766
1767     if (IsClipboardFormatAvailable(nRTFFormat))
1768       cf = nRTFFormat, dwFormat = SF_RTF;
1769     else if (IsClipboardFormatAvailable(CF_UNICODETEXT))
1770       cf = CF_UNICODETEXT, dwFormat = SF_TEXT|SF_UNICODE;
1771     else
1772       return 0;
1773
1774     if (!OpenClipboard(hWnd))
1775       return 0;
1776     gds.hData = GetClipboardData(cf);
1777     gds.nLength = 0;
1778     es.dwCookie = (DWORD)&gds;
1779     es.pfnCallback = dwFormat == SF_RTF ? ME_ReadFromHGLOBALRTF : ME_ReadFromHGLOBALUnicode;
1780     ME_StreamIn(editor, dwFormat|SFF_SELECTION, &es);
1781
1782     CloseClipboard();
1783     return 0;
1784   }
1785   case WM_CUT:
1786   case WM_COPY:
1787   {
1788     LPDATAOBJECT dataObj = NULL;
1789     CHARRANGE range;
1790     HRESULT hr = S_OK;
1791     ME_GetSelection(editor, (int*)&range.cpMin, (int*)&range.cpMax);
1792     if(editor->lpOleCallback)
1793         hr = IRichEditOleCallback_GetClipboardData(editor->lpOleCallback, &range, RECO_COPY, &dataObj);
1794     if(FAILED(hr) || !dataObj)
1795         hr = ME_GetDataObject(editor, &range, &dataObj);
1796     if(SUCCEEDED(hr)) {
1797         hr = OleSetClipboard(dataObj);
1798         IDataObject_Release(dataObj);
1799     }
1800     if (SUCCEEDED(hr) && msg == WM_CUT)
1801     {
1802       ME_InternalDeleteText(editor, range.cpMin, range.cpMax-range.cpMin);
1803       ME_CommitUndo(editor);
1804       ME_UpdateRepaint(editor);
1805     }
1806     return 0;
1807   }
1808   case WM_GETTEXTLENGTH:
1809     return ME_GetTextLength(editor);
1810   case EM_GETTEXTLENGTHEX:
1811     return ME_GetTextLengthEx(editor, (GETTEXTLENGTHEX *)wParam);
1812   case WM_GETTEXT:
1813   {
1814     TEXTRANGEW tr; /* W and A differ only by rng->lpstrText */
1815     tr.chrg.cpMin = 0;
1816     tr.chrg.cpMax = wParam-1;
1817     tr.lpstrText = (WCHAR *)lParam;
1818     return RichEditANSIWndProc(hWnd, EM_GETTEXTRANGE, 0, (LPARAM)&tr);
1819   }
1820   case EM_GETTEXTEX:
1821   {
1822     GETTEXTEX *ex = (GETTEXTEX*)wParam;
1823     int nStart, nCount;
1824
1825     if (ex->flags & ~(GT_SELECTION | GT_USECRLF))
1826       FIXME("GETTEXTEX flags 0x%08lx not supported\n", ex->flags & ~(GT_SELECTION | GT_USECRLF));
1827
1828     if (ex->flags & GT_SELECTION)
1829     {
1830       ME_GetSelection(editor, &nStart, &nCount);
1831       nCount -= nStart;
1832       nCount = min(nCount, ex->cb - 1);
1833     }
1834     else
1835     {
1836       nStart = 0;
1837       nCount = ex->cb - 1;
1838     }
1839     if (ex->codepage == 1200 || IsWindowUnicode(hWnd))
1840     {
1841       nCount = min(nCount, ex->cb / sizeof(WCHAR) - 1);
1842       return ME_GetTextW(editor, (LPWSTR)lParam, nStart, nCount, ex->flags & GT_USECRLF);
1843     }
1844     else
1845     {
1846       /* potentially each char may be a CR, why calculate the exact value with O(N) when
1847         we can just take a bigger buffer? :) */
1848       int crlfmul = (ex->flags & GT_USECRLF) ? 2 : 1;
1849       LPWSTR buffer = HeapAlloc(GetProcessHeap(), 0, (crlfmul*nCount + 1) * sizeof(WCHAR));
1850       DWORD buflen = ex->cb;
1851       LRESULT rc;
1852       DWORD flags = 0;
1853
1854       buflen = ME_GetTextW(editor, buffer, nStart, nCount, ex->flags & GT_USECRLF);
1855       rc = WideCharToMultiByte(ex->codepage, flags, buffer, buflen, (LPSTR)lParam, ex->cb, ex->lpDefaultChar, ex->lpUsedDefaultChar);
1856
1857       HeapFree(GetProcessHeap(),0,buffer);
1858       return rc;
1859     }
1860   }
1861   case EM_GETSELTEXT:
1862   {
1863     int from, to;
1864     TEXTRANGEW tr; /* W and A differ only by rng->lpstrText */
1865     ME_GetSelection(editor, &from, &to);
1866     tr.chrg.cpMin = from;
1867     tr.chrg.cpMax = to;
1868     tr.lpstrText = (WCHAR *)lParam;
1869     return RichEditANSIWndProc(hWnd, EM_GETTEXTRANGE, 0, (LPARAM)&tr);
1870   }
1871   case EM_GETSCROLLPOS:
1872   {
1873       POINT *point = (POINT *)lParam;
1874       point->x = 0; /* FIXME side scrolling not implemented */
1875       point->y = editor->nScrollPosY;
1876       return 1;
1877   }
1878   case EM_GETTEXTRANGE:
1879   {
1880     TEXTRANGEW *rng = (TEXTRANGEW *)lParam;
1881     TRACE("EM_GETTEXTRANGE min=%ld max=%ld unicode=%d emul1.0=%d length=%d\n",
1882       rng->chrg.cpMin, rng->chrg.cpMax, IsWindowUnicode(hWnd), 
1883       editor->bEmulateVersion10, ME_GetTextLength(editor));
1884     if (IsWindowUnicode(hWnd))
1885       return ME_GetTextW(editor, rng->lpstrText, rng->chrg.cpMin, rng->chrg.cpMax-rng->chrg.cpMin, editor->bEmulateVersion10);
1886     else
1887     {
1888       int nLen = rng->chrg.cpMax-rng->chrg.cpMin;
1889       WCHAR *p = ALLOC_N_OBJ(WCHAR, nLen+1);
1890       int nChars = ME_GetTextW(editor, p, rng->chrg.cpMin, nLen, editor->bEmulateVersion10);
1891       /* FIXME this is a potential security hole (buffer overrun) 
1892          if you know more about wchar->mbyte conversion please explain
1893       */
1894       WideCharToMultiByte(CP_ACP, 0, p, nChars+1, (char *)rng->lpstrText, nLen+1, NULL, NULL);
1895       FREE_OBJ(p);
1896       return nChars;
1897     }
1898   }
1899   case EM_GETLINECOUNT:
1900   {
1901     ME_DisplayItem *item = editor->pBuffer->pFirst->next;
1902     int nRows = 0;
1903
1904     while (item != editor->pBuffer->pLast)
1905     {
1906       assert(item->type == diParagraph);
1907       nRows += item->member.para.nRows;
1908       item = item->member.para.next_para;
1909     }
1910     TRACE("EM_GETLINECOUNT: nRows==%d\n", nRows);
1911     return max(1, nRows);
1912   }
1913   case EM_LINEFROMCHAR:
1914   {
1915     if (wParam == -1)
1916       return ME_RowNumberFromCharOfs(editor, ME_GetCursorOfs(editor, 1));
1917     else
1918       return ME_RowNumberFromCharOfs(editor, wParam);
1919   }
1920   case EM_EXLINEFROMCHAR:
1921   {
1922     return ME_RowNumberFromCharOfs(editor, lParam);
1923   }
1924   case EM_LINEINDEX:
1925   {
1926     ME_DisplayItem *item, *para;
1927     int nCharOfs;
1928     
1929     if (wParam == -1)
1930       item = ME_FindItemBack(editor->pCursors[0].pRun, diStartRow);
1931     else
1932       item = ME_FindRowWithNumber(editor, wParam);
1933     if (!item)
1934       return -1;
1935     para = ME_GetParagraph(item);
1936     item = ME_FindItemFwd(item, diRun);
1937     nCharOfs = para->member.para.nCharOfs + item->member.run.nCharOfs;
1938     TRACE("EM_LINEINDEX: nCharOfs==%d\n", nCharOfs);
1939     return nCharOfs;
1940   }
1941   case EM_LINELENGTH:
1942   {
1943     ME_DisplayItem *item, *item_end;
1944     int nChars = 0, nThisLineOfs = 0, nNextLineOfs = 0;
1945     
1946     if (wParam > ME_GetTextLength(editor))
1947       return 0;
1948     if (wParam == -1)
1949     {
1950       FIXME("EM_LINELENGTH: returning number of unselected characters on lines with selection unsupported.\n");
1951       return 0;
1952     }
1953     item = ME_FindItemAtOffset(editor, diRun, wParam, NULL);
1954     item = ME_RowStart(item);
1955     nThisLineOfs = ME_CharOfsFromRunOfs(editor, ME_FindItemFwd(item, diRun), 0);
1956     item_end = ME_FindItemFwd(item, diStartRow);
1957     if (item_end)
1958       nNextLineOfs = ME_CharOfsFromRunOfs(editor, ME_FindItemFwd(item_end, diRun), 0);
1959     else
1960       nNextLineOfs = ME_FindItemFwd(item, diParagraphOrEnd)->member.para.nCharOfs
1961        - (editor->bEmulateVersion10?2:1);
1962     nChars = nNextLineOfs - nThisLineOfs;
1963     TRACE("EM_LINELENGTH(%d)==%d\n",wParam, nChars);
1964     return nChars;
1965   }
1966   case EM_FINDTEXT:
1967   {
1968     FINDTEXTA *ft = (FINDTEXTA *)lParam;
1969     int nChars = MultiByteToWideChar(CP_ACP, 0, ft->lpstrText, -1, NULL, 0);
1970     WCHAR *tmp;
1971     
1972     if ((tmp = ALLOC_N_OBJ(WCHAR, nChars)) != NULL)
1973       MultiByteToWideChar(CP_ACP, 0, ft->lpstrText, -1, tmp, nChars);
1974     return ME_FindText(editor, wParam, &ft->chrg, tmp, NULL);
1975   }
1976   case EM_FINDTEXTEX:
1977   {
1978     FINDTEXTEXA *ex = (FINDTEXTEXA *)lParam;
1979     int nChars = MultiByteToWideChar(CP_ACP, 0, ex->lpstrText, -1, NULL, 0);
1980     WCHAR *tmp;
1981     
1982     if ((tmp = ALLOC_N_OBJ(WCHAR, nChars)) != NULL)
1983       MultiByteToWideChar(CP_ACP, 0, ex->lpstrText, -1, tmp, nChars);
1984     return ME_FindText(editor, wParam, &ex->chrg, tmp, &ex->chrgText);
1985   }
1986   case EM_FINDTEXTW:
1987   {
1988     FINDTEXTW *ft = (FINDTEXTW *)lParam;
1989     return ME_FindText(editor, wParam, &ft->chrg, ft->lpstrText, NULL);
1990   }
1991   case EM_FINDTEXTEXW:
1992   {
1993     FINDTEXTEXW *ex = (FINDTEXTEXW *)lParam;
1994     return ME_FindText(editor, wParam, &ex->chrg, ex->lpstrText, &ex->chrgText);
1995   }
1996   case EM_GETZOOM:
1997     if (!wParam || !lParam)
1998       return FALSE;
1999     *(int *)wParam = editor->nZoomNumerator;
2000     *(int *)lParam = editor->nZoomDenominator;
2001     return TRUE;
2002   case EM_SETZOOM:
2003     return ME_SetZoom(editor, wParam, lParam);
2004   case EM_CHARFROMPOS:
2005     return ME_CharFromPos(editor, ((POINTL *)lParam)->x, ((POINTL *)lParam)->y);
2006   case EM_POSFROMCHAR:
2007   {
2008     ME_DisplayItem *pRun;
2009     int nCharOfs, nOffset, nLength;
2010     POINTL pt = {0,0};
2011     
2012     nCharOfs = wParam; 
2013     /* detect which API version we're dealing with */
2014     if (wParam >= 0x40000)
2015         nCharOfs = lParam;
2016     nLength = ME_GetTextLength(editor);
2017     
2018     if (nCharOfs < nLength) { 
2019         ME_RunOfsFromCharOfs(editor, nCharOfs, &pRun, &nOffset);
2020         assert(pRun->type == diRun);
2021         pt.y = pRun->member.run.pt.y;
2022         pt.x = pRun->member.run.pt.x + ME_PointFromChar(editor, &pRun->member.run, nOffset);
2023         pt.y += ME_GetParagraph(pRun)->member.para.nYPos;
2024     } else {
2025         pt.x = 0;
2026         pt.y = editor->pBuffer->pLast->member.para.nYPos;
2027     }
2028     if (wParam >= 0x40000) {
2029         *(POINTL *)wParam = pt;
2030     }
2031     return MAKELONG( pt.x, pt.y );
2032   }
2033   case WM_CREATE:
2034     ME_CommitUndo(editor);
2035     ME_WrapMarkedParagraphs(editor);
2036     ME_MoveCaret(editor);
2037     return 0;
2038   case WM_DESTROY:
2039     ME_DestroyEditor(editor);
2040     SetWindowLongW(hWnd, 0, 0);
2041     return 0;
2042   case WM_LBUTTONDOWN:
2043     SetFocus(hWnd);
2044     ME_LButtonDown(editor, (short)LOWORD(lParam), (short)HIWORD(lParam));
2045     SetCapture(hWnd);
2046     break;
2047   case WM_MOUSEMOVE:
2048     if (GetCapture() == hWnd)
2049       ME_MouseMove(editor, (short)LOWORD(lParam), (short)HIWORD(lParam));
2050     break;
2051   case WM_LBUTTONUP:
2052     if (GetCapture() == hWnd)
2053       ReleaseCapture();
2054     break;
2055   case WM_LBUTTONDBLCLK:
2056     ME_SelectWord(editor);
2057     break;
2058   case WM_CONTEXTMENU:
2059     ME_ShowContextMenu(editor, (short)LOWORD(lParam), (short)HIWORD(lParam));
2060     break;
2061   case WM_PAINT:
2062     if (editor->bRedraw)
2063     {
2064       HDC hDC;
2065       PAINTSTRUCT ps;
2066
2067       hDC = BeginPaint(hWnd, &ps);
2068       ME_PaintContent(editor, hDC, FALSE, &ps.rcPaint);
2069       EndPaint(hWnd, &ps);
2070     }
2071     break;
2072   case WM_SETFOCUS:
2073     ME_ShowCaret(editor);
2074     ME_SendOldNotify(editor, EN_SETFOCUS);
2075     return 0;
2076   case WM_KILLFOCUS:
2077     ME_HideCaret(editor);
2078     ME_SendOldNotify(editor, EN_KILLFOCUS);
2079     return 0;
2080   case WM_ERASEBKGND:
2081   {
2082     if (editor->bRedraw)
2083     {
2084       HDC hDC = (HDC)wParam;
2085       RECT rc;
2086       if (GetUpdateRect(hWnd,&rc,TRUE))
2087       {
2088         FillRect(hDC, &rc, editor->hbrBackground);
2089       }
2090     }
2091     return 1;
2092   }
2093   case WM_COMMAND:
2094     TRACE("editor wnd command = %d\n", LOWORD(wParam));
2095     return 0;
2096   case WM_KEYDOWN:
2097     if (ME_KeyDown(editor, LOWORD(wParam)))
2098       return 0;
2099     goto do_default;
2100   case WM_CHAR: 
2101   {
2102     WCHAR wstr = LOWORD(wParam);
2103     if (editor->AutoURLDetect_bEnable)
2104       ME_AutoURLDetect(editor, wstr);
2105         
2106     switch (wstr)
2107     {
2108     case 3: /* Ctrl-C */
2109       SendMessageW(editor->hWnd, WM_COPY, 0, 0);
2110       return 0;
2111     }
2112     
2113     if (GetWindowLongW(editor->hWnd, GWL_STYLE) & ES_READONLY) {
2114       MessageBeep(MB_ICONERROR);
2115       return 0; /* FIXME really 0 ? */
2116     }
2117
2118     switch (wstr)
2119     {
2120     case 1: /* Ctrl-A */
2121       ME_SetSelection(editor, 0, -1);
2122       return 0;
2123     case 22: /* Ctrl-V */
2124       SendMessageW(editor->hWnd, WM_PASTE, 0, 0);
2125       return 0;
2126     case 24: /* Ctrl-X */
2127       SendMessageW(editor->hWnd, WM_CUT, 0, 0);
2128       return 0;
2129     case 25: /* Ctrl-Y */
2130       SendMessageW(editor->hWnd, EM_REDO, 0, 0);
2131       return 0;
2132     case 26: /* Ctrl-Z */
2133       SendMessageW(editor->hWnd, EM_UNDO, 0, 0);
2134       return 0;
2135     }
2136     if (((unsigned)wstr)>=' ' || wstr=='\r' || wstr=='\t') {
2137       /* FIXME maybe it would make sense to call EM_REPLACESEL instead ? */
2138       ME_Style *style = ME_GetInsertStyle(editor, 0);
2139       ME_SaveTempStyle(editor);
2140       ME_InsertTextFromCursor(editor, 0, &wstr, 1, style);
2141       ME_ReleaseStyle(style);
2142       ME_CommitUndo(editor);
2143       ME_UpdateRepaint(editor);
2144     }
2145     return 0;
2146   }
2147   case EM_SCROLL: /* fall through */
2148   case WM_VSCROLL: 
2149   {
2150     int nEnd;
2151     int nPos = editor->nScrollPosY;
2152     int origNPos = nPos;
2153     int lineHeight = 24;
2154     si.cbSize = sizeof(SCROLLINFO);
2155     si.fMask = SIF_PAGE|SIF_POS|SIF_RANGE|SIF_TRACKPOS;
2156     GetScrollInfo(hWnd, SB_VERT, &si);
2157     if (editor && editor->pBuffer && editor->pBuffer->pDefaultStyle)
2158         lineHeight = editor->pBuffer->pDefaultStyle->tm.tmHeight;
2159     if (lineHeight <= 0) lineHeight = 24;
2160     switch(LOWORD(wParam)) {
2161     case SB_LINEUP:
2162       nPos -= lineHeight;
2163       if (nPos<0) nPos = 0;
2164       break;
2165     case SB_LINEDOWN:
2166     {
2167       nEnd = editor->nTotalLength - editor->sizeWindow.cy;
2168       if (nEnd < 0) nEnd = 0;
2169       nPos += lineHeight;
2170       if (nPos>=nEnd) nPos = nEnd;
2171       break;
2172     }
2173     case SB_PAGEUP:
2174       nPos -= editor->sizeWindow.cy;
2175       if (nPos<0) nPos = 0;
2176       break;
2177     case SB_PAGEDOWN:
2178       nEnd = editor->nTotalLength - editor->sizeWindow.cy;
2179       if (nEnd < 0) nEnd = 0;
2180       nPos += editor->sizeWindow.cy;
2181       if (nPos>=nEnd) nPos = nEnd;
2182       break;
2183     case SB_THUMBTRACK:
2184     case SB_THUMBPOSITION:
2185       nPos = si.nTrackPos;
2186       break;
2187     }
2188     if (nPos != editor->nScrollPosY) {
2189       int dy = editor->nScrollPosY - nPos;
2190       editor->nScrollPosY = nPos;
2191       SetScrollPos(hWnd, SB_VERT, nPos, TRUE);
2192       if (editor->bRedraw)
2193       {
2194         ScrollWindow(hWnd, 0, dy, NULL, NULL);
2195         UpdateWindow(hWnd);
2196       }
2197     }
2198     if (msg == EM_SCROLL)
2199       return 0x00010000 | (((nPos - origNPos)/lineHeight) & 0xffff);
2200     break;
2201   }
2202   case WM_MOUSEWHEEL:
2203   {
2204     int gcWheelDelta = 0, nPos = editor->nScrollPosY, nEnd = editor->nTotalLength - editor->sizeWindow.cy; 
2205     UINT pulScrollLines;
2206     SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
2207     gcWheelDelta -= GET_WHEEL_DELTA_WPARAM(wParam);
2208     if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
2209       nPos += pulScrollLines * (gcWheelDelta / WHEEL_DELTA) * 8; /* FIXME follow the original */
2210     if (nPos>=nEnd)
2211       nPos = nEnd;
2212     if (nPos<0)
2213       nPos = 0;
2214     if (nPos != editor->nScrollPosY) {
2215       int dy = editor->nScrollPosY - nPos;
2216       editor->nScrollPosY = nPos;
2217       SetScrollPos(hWnd, SB_VERT, nPos, TRUE);
2218       if (editor->bRedraw)
2219       {
2220         ScrollWindow(hWnd, 0, dy, NULL, NULL);
2221         UpdateWindow(hWnd);
2222       }
2223     }
2224     break;
2225   }
2226   case EM_GETRECT:
2227   {
2228     *((RECT *)lParam) = editor->rcFormat;
2229     return 0;
2230   }
2231   case EM_SETRECT:
2232   case EM_SETRECTNP:
2233   {
2234     if (lParam)
2235     {
2236       RECT *rc = (RECT *)lParam;
2237       
2238       if (wParam)
2239       {
2240         editor->rcFormat.left += rc->left;
2241         editor->rcFormat.top += rc->top;
2242         editor->rcFormat.right += rc->right;
2243         editor->rcFormat.bottom += rc->bottom;
2244       }
2245       else
2246       {
2247         editor->rcFormat = *rc;
2248       }
2249     }
2250     else
2251     {
2252       GetClientRect(hWnd, &editor->rcFormat);
2253     }
2254     if (msg != EM_SETRECTNP)
2255       ME_RewrapRepaint(editor);
2256     return 0;
2257   }
2258   case EM_REQUESTRESIZE:
2259     ME_SendRequestResize(editor, TRUE);
2260     return 0;
2261   case WM_SETREDRAW:
2262     editor->bRedraw = wParam;
2263     return 0;
2264   case WM_SIZE:
2265   {
2266     GetClientRect(hWnd, &editor->rcFormat);
2267     ME_RewrapRepaint(editor);
2268     return DefWindowProcW(hWnd, msg, wParam, lParam);
2269   }
2270   case EM_GETOLEINTERFACE:
2271   {
2272     LPVOID *ppvObj = (LPVOID*) lParam;
2273     return CreateIRichEditOle(editor, ppvObj);
2274   }
2275   case EM_SETOLECALLBACK:
2276     if(editor->lpOleCallback)
2277       IUnknown_Release(editor->lpOleCallback);
2278     editor->lpOleCallback = (LPRICHEDITOLECALLBACK)lParam;
2279     if(editor->lpOleCallback)
2280       IUnknown_AddRef(editor->lpOleCallback);
2281     return TRUE;
2282   case EM_GETWORDBREAKPROC:
2283     return (LRESULT)editor->pfnWordBreak;
2284   case EM_SETWORDBREAKPROC:
2285   {
2286     EDITWORDBREAKPROCW pfnOld = editor->pfnWordBreak;
2287
2288     editor->pfnWordBreak = (EDITWORDBREAKPROCW)lParam;
2289     return (LRESULT)pfnOld;
2290   }
2291   case EM_SETTEXTMODE:
2292   {
2293     LRESULT ret;
2294     int mask = 0;
2295     int changes = 0;
2296     if ((ret = RichEditANSIWndProc(hWnd, WM_GETTEXTLENGTH, 0, 0)) == 0)
2297     {
2298       /*Check for valid wParam*/
2299       if ((((wParam & TM_RICHTEXT) && ((wParam & TM_PLAINTEXT) << 1))) ||
2300           (((wParam & TM_MULTILEVELUNDO) && ((wParam & TM_SINGLELEVELUNDO) << 1))) ||
2301           (((wParam & TM_MULTICODEPAGE) && ((wParam & TM_SINGLECODEPAGE) << 1))))
2302         return 1;
2303       else
2304       {
2305         if (wParam & (TM_RICHTEXT | TM_PLAINTEXT))
2306         {
2307           mask |= (TM_RICHTEXT | TM_PLAINTEXT);
2308           changes |= (wParam & (TM_RICHTEXT | TM_PLAINTEXT));
2309         }
2310         /*FIXME: Currently no support for undo level and code page options*/ 
2311         editor->mode = (editor->mode & (~mask)) | changes;
2312         return 0;
2313       }
2314     }
2315     return ret;
2316   }      
2317   default:
2318   do_default:
2319     return DefWindowProcW(hWnd, msg, wParam, lParam);
2320   }
2321   return 0L;
2322 }
2323
2324
2325 /******************************************************************
2326  *        RichEdit10ANSIWndProc (RICHED20.9)
2327  */
2328 LRESULT WINAPI RichEdit10ANSIWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
2329 {
2330   LRESULT result;
2331   
2332   /* FIXME: this is NOT the same as 2.0 version */
2333   result = RichEditANSIWndProc(hWnd, msg, wParam, lParam);
2334   if (msg == WM_NCCREATE)
2335   {
2336     ME_TextEditor *editor = (ME_TextEditor *)GetWindowLongW(hWnd, 0);
2337     
2338     editor->bEmulateVersion10 = TRUE;
2339     editor->pBuffer->pLast->member.para.nCharOfs = 2;
2340   }
2341   return result;
2342 }
2343
2344 void ME_SendOldNotify(ME_TextEditor *editor, int nCode)
2345 {
2346   HWND hWnd = editor->hWnd;
2347   SendMessageA(GetParent(hWnd), WM_COMMAND, (nCode<<16)|GetWindowLongW(hWnd, GWLP_ID), (LPARAM)hWnd);
2348 }
2349
2350 int ME_CountParagraphsBetween(ME_TextEditor *editor, int from, int to)
2351 {
2352   ME_DisplayItem *item = ME_FindItemFwd(editor->pBuffer->pFirst, diParagraph);
2353   int i = 0;
2354   
2355   while(item && item->member.para.next_para->member.para.nCharOfs <= from)
2356     item = item->member.para.next_para;
2357   if (!item)
2358     return 0;
2359   while(item && item->member.para.next_para->member.para.nCharOfs <= to) {
2360     item = item->member.para.next_para;
2361     i++;
2362   }
2363   return i;
2364 }
2365
2366
2367 int ME_GetTextW(ME_TextEditor *editor, WCHAR *buffer, int nStart, int nChars, int bCRLF)
2368 {
2369   ME_DisplayItem *item = ME_FindItemAtOffset(editor, diRun, nStart, &nStart);
2370   int nWritten = 0;
2371   WCHAR *pStart = buffer;
2372   
2373   if (!item) {
2374     *buffer = L'\0';
2375     return 0;
2376   }
2377   
2378   if (nStart)
2379   {
2380     int nLen = ME_StrLen(item->member.run.strText) - nStart;
2381     if (nLen > nChars)
2382       nLen = nChars;
2383     CopyMemory(buffer, item->member.run.strText->szData + nStart, sizeof(WCHAR)*nLen);
2384     nChars -= nLen;
2385     nWritten += nLen;
2386     if (!nChars)
2387       return nWritten;
2388     buffer += nLen;
2389     nStart = 0;
2390     item = ME_FindItemFwd(item, diRun);
2391   }
2392   
2393   while(nChars && item)
2394   {
2395     int nLen = ME_StrLen(item->member.run.strText);
2396     if (nLen > nChars)
2397       nLen = nChars;
2398       
2399     if (item->member.run.nFlags & MERF_ENDPARA)
2400     {
2401       *buffer = '\r';
2402       if (bCRLF)
2403       {
2404         *(++buffer) = '\n';
2405         nWritten++;
2406       }
2407       assert(nLen == 1);
2408       /* our end paragraph consists of 2 characters now */
2409       if (editor->bEmulateVersion10)
2410         nChars--;
2411     }
2412     else      
2413       CopyMemory(buffer, item->member.run.strText->szData, sizeof(WCHAR)*nLen);
2414     nChars -= nLen;
2415     nWritten += nLen;
2416     buffer += nLen;    
2417       
2418     if (!nChars)
2419     {
2420       TRACE("nWritten=%d, actual=%d\n", nWritten, buffer-pStart);
2421       *buffer = L'\0';
2422       return nWritten;
2423     }
2424     item = ME_FindItemFwd(item, diRun);
2425   }
2426   *buffer = L'\0';
2427   TRACE("nWritten=%d, actual=%d\n", nWritten, buffer-pStart);
2428   return nWritten;  
2429 }
2430
2431 void ME_RegisterEditorClass(HINSTANCE hInstance)
2432 {
2433   BOOL bResult;
2434   WNDCLASSW wcW;
2435   WNDCLASSA wcA;
2436   
2437   wcW.style = CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW | CS_GLOBALCLASS;
2438   wcW.lpfnWndProc = RichEditANSIWndProc;
2439   wcW.cbClsExtra = 0;
2440   wcW.cbWndExtra = 4;
2441   wcW.hInstance = NULL; /* hInstance would register DLL-local class */
2442   wcW.hIcon = NULL;
2443   wcW.hCursor = LoadCursorW(NULL, MAKEINTRESOURCEW(IDC_IBEAM));
2444   wcW.hbrBackground = (HBRUSH)GetStockObject(NULL_BRUSH);
2445   wcW.lpszMenuName = NULL;
2446   wcW.lpszClassName = wszClassName;
2447   bResult = RegisterClassW(&wcW);  
2448   assert(bResult);
2449   wcW.lpszClassName = wszClassName50;
2450   bResult = RegisterClassW(&wcW);  
2451   assert(bResult);
2452
2453   wcA.style = CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW | CS_GLOBALCLASS;
2454   wcA.lpfnWndProc = RichEditANSIWndProc;
2455   wcA.cbClsExtra = 0;
2456   wcA.cbWndExtra = 4;
2457   wcA.hInstance = NULL; /* hInstance would register DLL-local class */
2458   wcA.hIcon = NULL;
2459   wcA.hCursor = LoadCursorW(NULL, MAKEINTRESOURCEW(IDC_IBEAM));
2460   wcA.hbrBackground = (HBRUSH)GetStockObject(NULL_BRUSH);
2461   wcA.lpszMenuName = NULL;
2462   wcA.lpszClassName = "RichEdit20A";
2463   bResult = RegisterClassA(&wcA);  
2464   assert(bResult);
2465   wcA.lpszClassName = "RichEdit50A";
2466   bResult = RegisterClassA(&wcA);  
2467   assert(bResult);
2468 }
2469
2470 LRESULT WINAPI REComboWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) {
2471   /* FIXME: Not implemented */
2472   TRACE("hWnd %p msg %04x (%s) %08x %08lx\n",
2473         hWnd, msg, get_msg_name(msg), wParam, lParam);
2474   return DefWindowProcW(hWnd, msg, wParam, lParam);
2475 }
2476
2477 LRESULT WINAPI REListWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) {
2478   /* FIXME: Not implemented */
2479   TRACE("hWnd %p msg %04x (%s) %08x %08lx\n",
2480         hWnd, msg, get_msg_name(msg), wParam, lParam);
2481   return DefWindowProcW(hWnd, msg, wParam, lParam);
2482 }
2483
2484 /******************************************************************
2485  *        REExtendedRegisterClass (RICHED20.8)
2486  *
2487  * FIXME undocumented
2488  * Need to check for errors and implement controls and callbacks 
2489  */
2490 LRESULT WINAPI REExtendedRegisterClass(void)
2491 {
2492   WNDCLASSW wcW;
2493   UINT result;
2494
2495   FIXME("semi stub\n");
2496
2497   wcW.cbClsExtra = 0;
2498   wcW.cbWndExtra = 4;
2499   wcW.hInstance = NULL;
2500   wcW.hIcon = NULL;
2501   wcW.hCursor = NULL;
2502   wcW.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
2503   wcW.lpszMenuName = NULL;
2504
2505   if (!ME_ListBoxRegistered)
2506   {
2507       wcW.style = CS_PARENTDC | CS_DBLCLKS | CS_GLOBALCLASS;
2508       wcW.lpfnWndProc = REListWndProc;
2509       wcW.lpszClassName = wszClassNameListBox;
2510       if (RegisterClassW(&wcW)) ME_ListBoxRegistered = TRUE;
2511   }
2512
2513   if (!ME_ComboBoxRegistered)
2514   {
2515       wcW.style = CS_PARENTDC | CS_DBLCLKS | CS_GLOBALCLASS | CS_VREDRAW | CS_HREDRAW;
2516       wcW.lpfnWndProc = REComboWndProc;
2517       wcW.lpszClassName = wszClassNameComboBox;
2518       if (RegisterClassW(&wcW)) ME_ComboBoxRegistered = TRUE;  
2519   }
2520
2521   result = 0;
2522   if (ME_ListBoxRegistered)
2523       result += 1;
2524   if (ME_ComboBoxRegistered)
2525       result += 2;
2526
2527   return result;
2528 }
2529
2530 int ME_AutoURLDetect(ME_TextEditor *editor, WCHAR curChar) 
2531 {
2532   struct prefix_s {
2533     char *text;
2534     int length;
2535   } prefixes[12] = {
2536     {"http:", 5},
2537     {"file:", 6},
2538     {"mailto:", 8},
2539     {"ftp:", 5},
2540     {"https:", 7},
2541     {"gopher:", 8},
2542     {"nntp:", 6},
2543     {"prospero:", 10},
2544     {"telnet:", 8},
2545     {"news:", 6},
2546     {"wais:", 6},
2547     {"www.", 5}
2548   };
2549   CHARRANGE ins_pt;
2550   int curf_ef, link_ef, def_ef;
2551   int cur_prefx, prefx_cnt;
2552   int sel_min, sel_max;
2553   int car_pos = 0;
2554   int text_pos=-1;
2555   int URLmin, URLmax = 0;
2556   CHARRANGE url;
2557   FINDTEXTA ft;
2558   CHARFORMAT2W cur_format;
2559   CHARFORMAT2W default_format;
2560   CHARFORMAT2W link;
2561   RichEditANSIWndProc(editor->hWnd, EM_EXGETSEL, (WPARAM) 0, (LPARAM) &ins_pt);
2562   sel_min = ins_pt.cpMin;
2563   sel_max = ins_pt.cpMax;
2564   if (sel_min==sel_max) 
2565     car_pos = sel_min;
2566   if (sel_min!=sel_max)
2567     car_pos = ME_GetTextLength(editor)+1;   
2568   cur_format.cbSize = sizeof(cur_format);
2569   default_format.cbSize = sizeof(default_format);
2570   RichEditANSIWndProc(editor->hWnd, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cur_format);
2571   RichEditANSIWndProc(editor->hWnd, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &default_format);
2572   link.cbSize = sizeof(link);
2573   link.dwMask = CFM_LINK | CFM_COLOR | CFM_UNDERLINE;
2574   link.dwEffects = CFE_LINK | CFE_UNDERLINE;
2575   link.crTextColor = RGB(0,0,255);
2576   curf_ef = cur_format.dwEffects & link.dwEffects;
2577   def_ef = default_format.dwEffects & link.dwEffects;
2578   link_ef = link.dwEffects & link.dwEffects;
2579   if (curf_ef == link_ef) 
2580   {
2581     if( curChar == '\n' || curChar=='\r' || curChar==' ') 
2582     {
2583       ME_SetSelection(editor, car_pos, car_pos);
2584       RichEditANSIWndProc(editor->hWnd, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &default_format);
2585       text_pos=-1;
2586       return 0;
2587     }
2588   }
2589   if (curf_ef == def_ef)
2590   {
2591     cur_prefx = 0;
2592     prefx_cnt = (sizeof(prefixes)/sizeof(struct prefix_s))-1;
2593     while (cur_prefx<=prefx_cnt) 
2594     {
2595       if (text_pos == -1) 
2596       {
2597         ft.lpstrText = prefixes[cur_prefx].text;
2598         URLmin=max(0,(car_pos-prefixes[cur_prefx].length));
2599         URLmax=max(0, car_pos);
2600         if ((car_pos == 0) && (ME_GetTextLength(editor) != 0))
2601         {
2602         URLmax = ME_GetTextLength(editor)+1;
2603         }
2604         ft.chrg.cpMin = URLmin;
2605         ft.chrg.cpMax = URLmax;
2606         text_pos=RichEditANSIWndProc(editor->hWnd, EM_FINDTEXT, FR_DOWN, (LPARAM)&ft);   
2607         cur_prefx++;
2608       }
2609       if (text_pos != -1) 
2610       {
2611         url.cpMin=text_pos;
2612         url.cpMax=car_pos-1;
2613         ME_SetCharFormat(editor, text_pos, (URLmax-text_pos), &link);
2614         ME_Repaint(editor);
2615         break;
2616       }
2617     }
2618   }
2619   return 0;
2620 }