xref: /reactos/dll/win32/riched20/editor.c (revision 8ea93d2a)
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  * Copyright 2008 Eric Pouech
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 2.1 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with this library; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22  */
23 
24 /*
25   API implementation status:
26 
27   Messages (ANSI versions not done yet)
28   + EM_AUTOURLDETECT 2.0
29   + EM_CANPASTE
30   + EM_CANREDO 2.0
31   + EM_CANUNDO
32   + EM_CHARFROMPOS
33   - EM_DISPLAYBAND
34   + EM_EMPTYUNDOBUFFER
35   + EM_EXGETSEL
36   + EM_EXLIMITTEXT
37   + EM_EXLINEFROMCHAR
38   + EM_EXSETSEL
39   + EM_FINDTEXT (only FR_DOWN flag implemented)
40   + EM_FINDTEXTEX (only FR_DOWN flag implemented)
41   - EM_FINDWORDBREAK
42   - EM_FMTLINES
43   - EM_FORMATRANGE
44   + EM_GETAUTOURLDETECT 2.0
45   - EM_GETBIDIOPTIONS 3.0
46   - EM_GETCHARFORMAT (partly done)
47   - EM_GETEDITSTYLE
48   + EM_GETEVENTMASK
49   + EM_GETFIRSTVISIBLELINE (can be optimized if needed)
50   - EM_GETIMECOLOR 1.0asian
51   - EM_GETIMECOMPMODE 2.0
52   - EM_GETIMEOPTIONS 1.0asian
53   - EM_GETIMESTATUS
54   - EM_GETLANGOPTIONS 2.0
55   + EM_GETLIMITTEXT
56   + EM_GETLINE
57   + EM_GETLINECOUNT   returns number of rows, not of paragraphs
58   + EM_GETMODIFY
59   + EM_GETOLEINTERFACE
60   + EM_GETOPTIONS
61   + EM_GETPARAFORMAT
62   + EM_GETPASSWORDCHAR 2.0
63   - EM_GETPUNCTUATION 1.0asian
64   + EM_GETRECT
65   - EM_GETREDONAME 2.0
66   + EM_GETSEL
67   + EM_GETSELTEXT (ANSI&Unicode)
68   + EM_GETSCROLLPOS 3.0
69 ! - EM_GETTHUMB
70   + EM_GETTEXTEX 2.0
71   + EM_GETTEXTLENGTHEX (GTL_PRECISE unimplemented)
72   + EM_GETTEXTMODE 2.0
73 ? + EM_GETTEXTRANGE (ANSI&Unicode)
74   - EM_GETTYPOGRAPHYOPTIONS 3.0
75   - EM_GETUNDONAME
76   + EM_GETWORDBREAKPROC
77   - EM_GETWORDBREAKPROCEX
78   - EM_GETWORDWRAPMODE 1.0asian
79   + EM_GETZOOM 3.0
80   + EM_HIDESELECTION
81   + EM_LIMITTEXT (Also called EM_SETLIMITTEXT)
82   + EM_LINEFROMCHAR
83   + EM_LINEINDEX
84   + EM_LINELENGTH
85   + EM_LINESCROLL
86   - EM_PASTESPECIAL
87   + EM_POSFROMCHAR
88   + EM_REDO 2.0
89   + EM_REQUESTRESIZE
90   + EM_REPLACESEL (proper style?) ANSI&Unicode
91   + EM_SCROLL
92   + EM_SCROLLCARET
93   + EM_SELECTIONTYPE
94   - EM_SETBIDIOPTIONS 3.0
95   + EM_SETBKGNDCOLOR
96   + EM_SETCHARFORMAT (partly done, no ANSI)
97   - EM_SETEDITSTYLE
98   + EM_SETEVENTMASK (few notifications supported)
99   + EM_SETFONTSIZE
100   - EM_SETIMECOLOR 1.0asian
101   - EM_SETIMEOPTIONS 1.0asian
102   - EM_SETIMESTATUS
103   - EM_SETLANGOPTIONS 2.0
104   - EM_SETLIMITTEXT
105   - EM_SETMARGINS
106   + EM_SETMODIFY (not sure if implementation is correct)
107   - EM_SETOLECALLBACK
108   + EM_SETOPTIONS (partially implemented)
109   - EM_SETPALETTE 2.0
110   + EM_SETPARAFORMAT
111   + EM_SETPASSWORDCHAR 2.0
112   - EM_SETPUNCTUATION 1.0asian
113   + EM_SETREADONLY no beep on modification attempt
114   + EM_SETRECT
115   + EM_SETRECTNP (EM_SETRECT without repainting)
116   + EM_SETSEL
117   + EM_SETSCROLLPOS 3.0
118   - EM_SETTABSTOPS 3.0
119   - EM_SETTARGETDEVICE (partial)
120   + EM_SETTEXTEX 3.0 (proper style?)
121   - EM_SETTEXTMODE 2.0
122   - EM_SETTYPOGRAPHYOPTIONS 3.0
123   + EM_SETUNDOLIMIT 2.0
124   + EM_SETWORDBREAKPROC (used only for word movement at the moment)
125   - EM_SETWORDBREAKPROCEX
126   - EM_SETWORDWRAPMODE 1.0asian
127   + EM_SETZOOM 3.0
128   + EM_SHOWSCROLLBAR 2.0
129   + EM_STOPGROUPTYPING 2.0
130   + EM_STREAMIN
131   + EM_STREAMOUT
132   + EM_UNDO
133   + WM_CHAR
134   + WM_CLEAR
135   + WM_COPY
136   + WM_CUT
137   + WM_GETDLGCODE (the current implementation is incomplete)
138   + WM_GETTEXT (ANSI&Unicode)
139   + WM_GETTEXTLENGTH (ANSI version sucks)
140   + WM_HSCROLL
141   + WM_PASTE
142   + WM_SETFONT
143   + WM_SETTEXT (resets undo stack !) (proper style?) ANSI&Unicode
144   + WM_STYLECHANGING (seems to do nothing)
145   + WM_STYLECHANGED (seems to do nothing)
146   + WM_UNICHAR
147   + WM_VSCROLL
148 
149   Notifications
150 
151   * EN_CHANGE (sent from the wrong place)
152   - EN_CORRECTTEXT
153   - EN_DROPFILES
154   - EN_ERRSPACE
155   - EN_HSCROLL
156   - EN_IMECHANGE
157   + EN_KILLFOCUS
158   - EN_LINK
159   - EN_MAXTEXT
160   - EN_MSGFILTER
161   - EN_OLEOPFAILED
162   - EN_PROTECTED
163   + EN_REQUESTRESIZE
164   - EN_SAVECLIPBOARD
165   + EN_SELCHANGE
166   + EN_SETFOCUS
167   - EN_STOPNOUNDO
168   * EN_UPDATE (sent from the wrong place)
169   - EN_VSCROLL
170 
171   Styles
172 
173   - ES_AUTOHSCROLL
174   - ES_AUTOVSCROLL
175   + ES_CENTER
176   + ES_DISABLENOSCROLL (scrollbar is always visible)
177   - ES_EX_NOCALLOLEINIT
178   + ES_LEFT
179   + ES_MULTILINE
180   - ES_NOIME
181   - ES_READONLY (I'm not sure if beeping is the proper behaviour)
182   + ES_RIGHT
183   - ES_SAVESEL
184   - ES_SELFIME
185   - ES_SUNKEN
186   - ES_VERTICAL
187   - ES_WANTRETURN (don't know how to do WM_GETDLGCODE part)
188   - WS_SETFONT
189   + WS_HSCROLL
190   + WS_VSCROLL
191 */
192 
193 /*
194  * RICHED20 TODO (incomplete):
195  *
196  * - messages/styles/notifications listed above
197  * - add remaining CHARFORMAT/PARAFORMAT fields
198  * - right/center align should strip spaces from the beginning
199  * - pictures/OLE objects (not just smiling faces that lack API support ;-) )
200  * - COM interface (looks like a major pain in the TODO list)
201  * - calculate heights of pictures (half-done)
202  * - hysteresis during wrapping (related to scrollbars appearing/disappearing)
203  * - find/replace
204  * - how to implement EM_FORMATRANGE and EM_DISPLAYBAND ? (Mission Impossible)
205  * - italic caret with italic fonts
206  * - IME
207  * - most notifications aren't sent at all (the most important ones are)
208  * - when should EN_SELCHANGE be sent after text change ? (before/after EN_UPDATE?)
209  * - WM_SETTEXT may use wrong style (but I'm 80% sure it's OK)
210  * - EM_GETCHARFORMAT with SCF_SELECTION may not behave 100% like in original (but very close)
211  * - full justification
212  * - hyphenation
213  * - tables
214  * - ListBox & ComboBox not implemented
215  *
216  * Bugs that are probably fixed, but not so easy to verify:
217  * - EN_UPDATE/EN_CHANGE are handled very incorrectly (should be OK now)
218  * - undo for ME_JoinParagraphs doesn't store paragraph format ? (it does)
219  * - check/fix artificial EOL logic (bCursorAtEnd, hardly logical)
220  * - caret shouldn't be displayed when selection isn't empty
221  * - check refcounting in style management functions (looks perfect now, but no bugs is suspicious)
222  * - undo for setting default format (done, might be buggy)
223  * - styles might be not released properly (looks like they work like charm, but who knows?
224  *
225  */
226 
227 #define NONAMELESSUNION
228 
229 #include "editor.h"
230 #include "commdlg.h"
231 #include "winreg.h"
232 #define NO_SHLWAPI_STREAM
233 #include "shlwapi.h"
234 #include "rtf.h"
235 #include "imm.h"
236 #ifdef __REACTOS__
237   #include <immdev.h>
238   #include <imm32_undoc.h>
239 #endif
240 #include "res.h"
241 
242 #ifdef __REACTOS__
243 #include <reactos/undocuser.h>
244 #endif
245 
246 #define STACK_SIZE_DEFAULT  100
247 #define STACK_SIZE_MAX     1000
248 
249 #define TEXT_LIMIT_DEFAULT 32767
250 
251 WINE_DEFAULT_DEBUG_CHANNEL(richedit);
252 
253 static BOOL ME_RegisterEditorClass(HINSTANCE);
254 static BOOL ME_UpdateLinkAttribute(ME_TextEditor *editor, ME_Cursor *start, int nChars);
255 
256 static const WCHAR REListBox20W[] = {'R','E','L','i','s','t','B','o','x','2','0','W', 0};
257 static const WCHAR REComboBox20W[] = {'R','E','C','o','m','b','o','B','o','x','2','0','W', 0};
258 static HCURSOR hLeft;
259 
260 BOOL me_debug = FALSE;
261 HANDLE me_heap = NULL;
262 
263 static BOOL ME_ListBoxRegistered = FALSE;
264 static BOOL ME_ComboBoxRegistered = FALSE;
265 
is_version_nt(void)266 static inline BOOL is_version_nt(void)
267 {
268     return !(GetVersion() & 0x80000000);
269 }
270 
ME_MakeText(void)271 static ME_TextBuffer *ME_MakeText(void) {
272   ME_TextBuffer *buf = heap_alloc(sizeof(*buf));
273   ME_DisplayItem *p1 = ME_MakeDI(diTextStart);
274   ME_DisplayItem *p2 = ME_MakeDI(diTextEnd);
275 
276   p1->prev = NULL;
277   p1->next = p2;
278   p2->prev = p1;
279   p2->next = NULL;
280   p1->member.para.next_para = p2;
281   p2->member.para.prev_para = p1;
282   p2->member.para.nCharOfs = 0;
283 
284   buf->pFirst = p1;
285   buf->pLast = p2;
286   buf->pCharStyle = NULL;
287 
288   return buf;
289 }
290 
291 
ME_StreamInText(ME_TextEditor * editor,DWORD dwFormat,ME_InStream * stream,ME_Style * style)292 static LRESULT ME_StreamInText(ME_TextEditor *editor, DWORD dwFormat, ME_InStream *stream, ME_Style *style)
293 {
294   WCHAR *pText;
295   LRESULT total_bytes_read = 0;
296   BOOL is_read = FALSE;
297   DWORD cp = CP_ACP, copy = 0;
298   char conv_buf[4 + STREAMIN_BUFFER_SIZE]; /* up to 4 additional UTF-8 bytes */
299 
300   static const char bom_utf8[] = {0xEF, 0xBB, 0xBF};
301 
302   TRACE("%08x %p\n", dwFormat, stream);
303 
304   do {
305     LONG nWideChars = 0;
306     WCHAR wszText[STREAMIN_BUFFER_SIZE+1];
307 
308     if (!stream->dwSize)
309     {
310       ME_StreamInFill(stream);
311       if (stream->editstream->dwError)
312         break;
313       if (!stream->dwSize)
314         break;
315       total_bytes_read += stream->dwSize;
316     }
317 
318     if (!(dwFormat & SF_UNICODE))
319     {
320       char * buf = stream->buffer;
321       DWORD size = stream->dwSize, end;
322 
323       if (!is_read)
324       {
325         is_read = TRUE;
326         if (stream->dwSize >= 3 && !memcmp(stream->buffer, bom_utf8, 3))
327         {
328           cp = CP_UTF8;
329           buf += 3;
330           size -= 3;
331         }
332       }
333 
334       if (cp == CP_UTF8)
335       {
336         if (copy)
337         {
338           memcpy(conv_buf + copy, buf, size);
339           buf = conv_buf;
340           size += copy;
341         }
342         end = size;
343         while ((buf[end-1] & 0xC0) == 0x80)
344         {
345           --end;
346           --total_bytes_read; /* strange, but seems to match windows */
347         }
348         if (buf[end-1] & 0x80)
349         {
350           DWORD need = 0;
351           if ((buf[end-1] & 0xE0) == 0xC0)
352             need = 1;
353           if ((buf[end-1] & 0xF0) == 0xE0)
354             need = 2;
355           if ((buf[end-1] & 0xF8) == 0xF0)
356             need = 3;
357 
358           if (size - end >= need)
359           {
360             /* we have enough bytes for this sequence */
361             end = size;
362           }
363           else
364           {
365             /* need more bytes, so don't transcode this sequence */
366             --end;
367           }
368         }
369       }
370       else
371         end = size;
372 
373       nWideChars = MultiByteToWideChar(cp, 0, buf, end, wszText, STREAMIN_BUFFER_SIZE);
374       pText = wszText;
375 
376       if (cp == CP_UTF8)
377       {
378         if (end != size)
379         {
380           memcpy(conv_buf, buf + end, size - end);
381           copy = size - end;
382         }
383       }
384     }
385     else
386     {
387       nWideChars = stream->dwSize >> 1;
388       pText = (WCHAR *)stream->buffer;
389     }
390 
391     ME_InsertTextFromCursor(editor, 0, pText, nWideChars, style);
392     if (stream->dwSize == 0)
393       break;
394     stream->dwSize = 0;
395   } while(1);
396   return total_bytes_read;
397 }
398 
ME_ApplyBorderProperties(RTF_Info * info,ME_BorderRect * borderRect,RTFBorder * borderDef)399 static void ME_ApplyBorderProperties(RTF_Info *info,
400                                      ME_BorderRect *borderRect,
401                                      RTFBorder *borderDef)
402 {
403   int i, colorNum;
404   ME_Border *pBorders[] = {&borderRect->top,
405                            &borderRect->left,
406                            &borderRect->bottom,
407                            &borderRect->right};
408   for (i = 0; i < 4; i++)
409   {
410     RTFColor *colorDef = info->colorList;
411     pBorders[i]->width = borderDef[i].width;
412     colorNum = borderDef[i].color;
413     while (colorDef && colorDef->rtfCNum != colorNum)
414       colorDef = colorDef->rtfNextColor;
415     if (colorDef)
416       pBorders[i]->colorRef = RGB(
417                            colorDef->rtfCRed >= 0 ? colorDef->rtfCRed : 0,
418                            colorDef->rtfCGreen >= 0 ? colorDef->rtfCGreen : 0,
419                            colorDef->rtfCBlue >= 0 ? colorDef->rtfCBlue : 0);
420     else
421       pBorders[i]->colorRef = RGB(0, 0, 0);
422   }
423 }
424 
ME_RTFCharAttrHook(RTF_Info * info)425 void ME_RTFCharAttrHook(RTF_Info *info)
426 {
427   CHARFORMAT2W fmt;
428   fmt.cbSize = sizeof(fmt);
429   fmt.dwMask = 0;
430   fmt.dwEffects = 0;
431 
432   switch(info->rtfMinor)
433   {
434     case rtfPlain:
435       /* FIXME add more flags once they're implemented */
436       fmt.dwMask = CFM_BOLD | CFM_ITALIC | CFM_UNDERLINE | CFM_UNDERLINETYPE | CFM_STRIKEOUT |
437           CFM_COLOR | CFM_BACKCOLOR | CFM_SIZE | CFM_WEIGHT;
438       fmt.dwEffects = CFE_AUTOCOLOR | CFE_AUTOBACKCOLOR;
439       fmt.yHeight = 12*20; /* 12pt */
440       fmt.wWeight = FW_NORMAL;
441       fmt.bUnderlineType = CFU_UNDERLINE;
442       break;
443     case rtfBold:
444       fmt.dwMask = CFM_BOLD | CFM_WEIGHT;
445       fmt.dwEffects = info->rtfParam ? CFE_BOLD : 0;
446       fmt.wWeight = info->rtfParam ? FW_BOLD : FW_NORMAL;
447       break;
448     case rtfItalic:
449       fmt.dwMask = CFM_ITALIC;
450       fmt.dwEffects = info->rtfParam ? fmt.dwMask : 0;
451       break;
452     case rtfUnderline:
453       fmt.dwMask = CFM_UNDERLINETYPE | CFM_UNDERLINE;
454       fmt.bUnderlineType = CFU_UNDERLINE;
455       fmt.dwEffects = info->rtfParam ? CFE_UNDERLINE : 0;
456       break;
457     case rtfDotUnderline:
458       fmt.dwMask = CFM_UNDERLINETYPE | CFM_UNDERLINE;
459       fmt.bUnderlineType = CFU_UNDERLINEDOTTED;
460       fmt.dwEffects = info->rtfParam ? CFE_UNDERLINE : 0;
461       break;
462     case rtfDbUnderline:
463       fmt.dwMask = CFM_UNDERLINETYPE | CFM_UNDERLINE;
464       fmt.bUnderlineType = CFU_UNDERLINEDOUBLE;
465       fmt.dwEffects = info->rtfParam ? CFE_UNDERLINE : 0;
466       break;
467     case rtfWordUnderline:
468       fmt.dwMask = CFM_UNDERLINETYPE | CFM_UNDERLINE;
469       fmt.bUnderlineType = CFU_UNDERLINEWORD;
470       fmt.dwEffects = info->rtfParam ? CFE_UNDERLINE : 0;
471       break;
472     case rtfNoUnderline:
473       fmt.dwMask = CFM_UNDERLINE;
474       fmt.dwEffects = 0;
475       break;
476     case rtfStrikeThru:
477       fmt.dwMask = CFM_STRIKEOUT;
478       fmt.dwEffects = info->rtfParam ? fmt.dwMask : 0;
479       break;
480     case rtfSubScript:
481     case rtfSuperScript:
482     case rtfSubScrShrink:
483     case rtfSuperScrShrink:
484     case rtfNoSuperSub:
485       fmt.dwMask = CFM_SUBSCRIPT|CFM_SUPERSCRIPT;
486       if (info->rtfMinor == rtfSubScrShrink) fmt.dwEffects = CFE_SUBSCRIPT;
487       if (info->rtfMinor == rtfSuperScrShrink) fmt.dwEffects = CFE_SUPERSCRIPT;
488       if (info->rtfMinor == rtfNoSuperSub) fmt.dwEffects = 0;
489       break;
490     case rtfInvisible:
491       fmt.dwMask = CFM_HIDDEN;
492       fmt.dwEffects = info->rtfParam ? fmt.dwMask : 0;
493       break;
494     case rtfBackColor:
495       fmt.dwMask = CFM_BACKCOLOR;
496       fmt.dwEffects = 0;
497       if (info->rtfParam == 0)
498         fmt.dwEffects = CFE_AUTOBACKCOLOR;
499       else if (info->rtfParam != rtfNoParam)
500       {
501         RTFColor *c = RTFGetColor(info, info->rtfParam);
502         if (c && c->rtfCBlue >= 0)
503           fmt.crBackColor = (c->rtfCBlue<<16)|(c->rtfCGreen<<8)|(c->rtfCRed);
504         else
505           fmt.dwEffects = CFE_AUTOBACKCOLOR;
506       }
507       break;
508     case rtfForeColor:
509       fmt.dwMask = CFM_COLOR;
510       fmt.dwEffects = 0;
511       if (info->rtfParam == 0)
512         fmt.dwEffects = CFE_AUTOCOLOR;
513       else if (info->rtfParam != rtfNoParam)
514       {
515         RTFColor *c = RTFGetColor(info, info->rtfParam);
516         if (c && c->rtfCBlue >= 0)
517           fmt.crTextColor = (c->rtfCBlue<<16)|(c->rtfCGreen<<8)|(c->rtfCRed);
518         else {
519           fmt.dwEffects = CFE_AUTOCOLOR;
520         }
521       }
522       break;
523     case rtfFontNum:
524       if (info->rtfParam != rtfNoParam)
525       {
526         RTFFont *f = RTFGetFont(info, info->rtfParam);
527         if (f)
528         {
529           MultiByteToWideChar(CP_ACP, 0, f->rtfFName, -1, fmt.szFaceName, ARRAY_SIZE(fmt.szFaceName));
530           fmt.szFaceName[ARRAY_SIZE(fmt.szFaceName)-1] = '\0';
531           fmt.bCharSet = f->rtfFCharSet;
532           fmt.dwMask = CFM_FACE | CFM_CHARSET;
533           fmt.bPitchAndFamily = f->rtfFPitch | (f->rtfFFamily << 4);
534         }
535       }
536       break;
537     case rtfFontSize:
538       fmt.dwMask = CFM_SIZE;
539       if (info->rtfParam != rtfNoParam)
540         fmt.yHeight = info->rtfParam*10;
541       break;
542   }
543   if (fmt.dwMask) {
544     ME_Style *style2;
545     RTFFlushOutputBuffer(info);
546     /* FIXME too slow ? how come ? */
547     style2 = ME_ApplyStyle(info->editor, info->style, &fmt);
548     ME_ReleaseStyle(info->style);
549     info->style = style2;
550     info->styleChanged = TRUE;
551   }
552 }
553 
554 /* FIXME this function doesn't get any information about context of the RTF tag, which is very bad,
555    the same tags mean different things in different contexts */
ME_RTFParAttrHook(RTF_Info * info)556 void ME_RTFParAttrHook(RTF_Info *info)
557 {
558   switch(info->rtfMinor)
559   {
560   case rtfParDef: /* restores default paragraph attributes */
561     if (!info->editor->bEmulateVersion10) /* v4.1 */
562       info->borderType = RTFBorderParaLeft;
563     else /* v1.0 - 3.0 */
564       info->borderType = RTFBorderParaTop;
565     info->fmt.dwMask = PFM_ALIGNMENT | PFM_BORDER | PFM_LINESPACING | PFM_TABSTOPS |
566         PFM_OFFSET | PFM_RIGHTINDENT | PFM_SPACEAFTER | PFM_SPACEBEFORE |
567         PFM_STARTINDENT | PFM_RTLPARA | PFM_NUMBERING | PFM_NUMBERINGSTART |
568         PFM_NUMBERINGSTYLE | PFM_NUMBERINGTAB;
569     /* TODO: shading */
570     info->fmt.wAlignment = PFA_LEFT;
571     info->fmt.cTabCount = 0;
572     info->fmt.dxOffset = info->fmt.dxStartIndent = info->fmt.dxRightIndent = 0;
573     info->fmt.wBorderWidth = info->fmt.wBorders = 0;
574     info->fmt.wBorderSpace = 0;
575     info->fmt.bLineSpacingRule = 0;
576     info->fmt.dySpaceBefore = info->fmt.dySpaceAfter = 0;
577     info->fmt.dyLineSpacing = 0;
578     info->fmt.wEffects &= ~PFE_RTLPARA;
579     info->fmt.wNumbering = 0;
580     info->fmt.wNumberingStart = 0;
581     info->fmt.wNumberingStyle = 0;
582     info->fmt.wNumberingTab = 0;
583 
584     if (!info->editor->bEmulateVersion10) /* v4.1 */
585     {
586       if (info->tableDef && info->tableDef->tableRowStart &&
587           info->tableDef->tableRowStart->member.para.nFlags & MEPF_ROWEND)
588       {
589         ME_Cursor cursor;
590         ME_DisplayItem *para;
591         /* We are just after a table row. */
592         RTFFlushOutputBuffer(info);
593         cursor = info->editor->pCursors[0];
594         para = cursor.pPara;
595         if (para  == info->tableDef->tableRowStart->member.para.next_para
596             && !cursor.nOffset && !cursor.pRun->member.run.nCharOfs)
597         {
598           /* Since the table row end, no text has been inserted, and the \intbl
599            * control word has not be used.  We can confirm that we are not in a
600            * table anymore.
601            */
602           info->tableDef->tableRowStart = NULL;
603           info->canInheritInTbl = FALSE;
604         }
605       }
606     } else { /* v1.0 - v3.0 */
607       info->fmt.dwMask |= PFM_TABLE;
608       info->fmt.wEffects &= ~PFE_TABLE;
609     }
610     break;
611   case rtfNestLevel:
612     if (!info->editor->bEmulateVersion10) /* v4.1 */
613     {
614       while (info->rtfParam > info->nestingLevel) {
615         RTFTable *tableDef = heap_alloc_zero(sizeof(*tableDef));
616         tableDef->parent = info->tableDef;
617         info->tableDef = tableDef;
618 
619         RTFFlushOutputBuffer(info);
620         if (tableDef->tableRowStart &&
621             tableDef->tableRowStart->member.para.nFlags & MEPF_ROWEND)
622         {
623           ME_DisplayItem *para = tableDef->tableRowStart;
624           para = para->member.para.next_para;
625           para = ME_InsertTableRowStartAtParagraph(info->editor, para);
626           tableDef->tableRowStart = para;
627         } else {
628           ME_Cursor cursor;
629           WCHAR endl = '\r';
630           cursor = info->editor->pCursors[0];
631           if (cursor.nOffset || cursor.pRun->member.run.nCharOfs)
632             ME_InsertTextFromCursor(info->editor, 0, &endl, 1, info->style);
633           tableDef->tableRowStart = ME_InsertTableRowStartFromCursor(info->editor);
634         }
635 
636         info->nestingLevel++;
637       }
638       info->canInheritInTbl = FALSE;
639     }
640     break;
641   case rtfInTable:
642   {
643     if (!info->editor->bEmulateVersion10) /* v4.1 */
644     {
645       if (info->nestingLevel < 1)
646       {
647         RTFTable *tableDef;
648         if (!info->tableDef)
649             info->tableDef = heap_alloc_zero(sizeof(*info->tableDef));
650         tableDef = info->tableDef;
651         RTFFlushOutputBuffer(info);
652         if (tableDef->tableRowStart &&
653             tableDef->tableRowStart->member.para.nFlags & MEPF_ROWEND)
654         {
655           ME_DisplayItem *para = tableDef->tableRowStart;
656           para = para->member.para.next_para;
657           para = ME_InsertTableRowStartAtParagraph(info->editor, para);
658           tableDef->tableRowStart = para;
659         } else {
660           ME_Cursor cursor;
661           WCHAR endl = '\r';
662           cursor = info->editor->pCursors[0];
663           if (cursor.nOffset || cursor.pRun->member.run.nCharOfs)
664             ME_InsertTextFromCursor(info->editor, 0, &endl, 1, info->style);
665           tableDef->tableRowStart = ME_InsertTableRowStartFromCursor(info->editor);
666         }
667         info->nestingLevel = 1;
668         info->canInheritInTbl = TRUE;
669       }
670       return;
671     } else { /* v1.0 - v3.0 */
672       info->fmt.dwMask |= PFM_TABLE;
673       info->fmt.wEffects |= PFE_TABLE;
674     }
675     break;
676   }
677   case rtfFirstIndent:
678   case rtfLeftIndent:
679     if ((info->fmt.dwMask & (PFM_STARTINDENT | PFM_OFFSET)) != (PFM_STARTINDENT | PFM_OFFSET))
680     {
681       PARAFORMAT2 fmt;
682       fmt.cbSize = sizeof(fmt);
683       ME_GetSelectionParaFormat(info->editor, &fmt);
684       info->fmt.dwMask |= PFM_STARTINDENT | PFM_OFFSET;
685       info->fmt.dxStartIndent = fmt.dxStartIndent;
686       info->fmt.dxOffset = fmt.dxOffset;
687     }
688     if (info->rtfMinor == rtfFirstIndent)
689     {
690       info->fmt.dxStartIndent += info->fmt.dxOffset + info->rtfParam;
691       info->fmt.dxOffset = -info->rtfParam;
692     }
693     else
694       info->fmt.dxStartIndent = info->rtfParam - info->fmt.dxOffset;
695     break;
696   case rtfRightIndent:
697     info->fmt.dwMask |= PFM_RIGHTINDENT;
698     info->fmt.dxRightIndent = info->rtfParam;
699     break;
700   case rtfQuadLeft:
701   case rtfQuadJust:
702     info->fmt.dwMask |= PFM_ALIGNMENT;
703     info->fmt.wAlignment = PFA_LEFT;
704     break;
705   case rtfQuadRight:
706     info->fmt.dwMask |= PFM_ALIGNMENT;
707     info->fmt.wAlignment = PFA_RIGHT;
708     break;
709   case rtfQuadCenter:
710     info->fmt.dwMask |= PFM_ALIGNMENT;
711     info->fmt.wAlignment = PFA_CENTER;
712     break;
713   case rtfTabPos:
714     if (!(info->fmt.dwMask & PFM_TABSTOPS))
715     {
716       PARAFORMAT2 fmt;
717       fmt.cbSize = sizeof(fmt);
718       ME_GetSelectionParaFormat(info->editor, &fmt);
719       memcpy(info->fmt.rgxTabs, fmt.rgxTabs,
720              fmt.cTabCount * sizeof(fmt.rgxTabs[0]));
721       info->fmt.cTabCount = fmt.cTabCount;
722       info->fmt.dwMask |= PFM_TABSTOPS;
723     }
724     if (info->fmt.cTabCount < MAX_TAB_STOPS && info->rtfParam < 0x1000000)
725       info->fmt.rgxTabs[info->fmt.cTabCount++] = info->rtfParam;
726     break;
727   case rtfKeep:
728     info->fmt.dwMask |= PFM_KEEP;
729     info->fmt.wEffects |= PFE_KEEP;
730     break;
731   case rtfNoWidowControl:
732     info->fmt.dwMask |= PFM_NOWIDOWCONTROL;
733     info->fmt.wEffects |= PFE_NOWIDOWCONTROL;
734     break;
735   case rtfKeepNext:
736     info->fmt.dwMask |= PFM_KEEPNEXT;
737     info->fmt.wEffects |= PFE_KEEPNEXT;
738     break;
739   case rtfSpaceAfter:
740     info->fmt.dwMask |= PFM_SPACEAFTER;
741     info->fmt.dySpaceAfter = info->rtfParam;
742     break;
743   case rtfSpaceBefore:
744     info->fmt.dwMask |= PFM_SPACEBEFORE;
745     info->fmt.dySpaceBefore = info->rtfParam;
746     break;
747   case rtfSpaceBetween:
748     info->fmt.dwMask |= PFM_LINESPACING;
749     if ((int)info->rtfParam > 0)
750     {
751       info->fmt.dyLineSpacing = info->rtfParam;
752       info->fmt.bLineSpacingRule = 3;
753     }
754     else
755     {
756       info->fmt.dyLineSpacing = info->rtfParam;
757       info->fmt.bLineSpacingRule = 4;
758     }
759     break;
760   case rtfSpaceMultiply:
761     info->fmt.dwMask |= PFM_LINESPACING;
762     info->fmt.dyLineSpacing = info->rtfParam * 20;
763     info->fmt.bLineSpacingRule = 5;
764     break;
765   case rtfParBullet:
766     info->fmt.dwMask |= PFM_NUMBERING;
767     info->fmt.wNumbering = PFN_BULLET;
768     break;
769   case rtfParSimple:
770     info->fmt.dwMask |= PFM_NUMBERING;
771     info->fmt.wNumbering = 2; /* FIXME: MSDN says it's not used ?? */
772     break;
773   case rtfBorderLeft:
774     info->borderType = RTFBorderParaLeft;
775     info->fmt.wBorders |= 1;
776     info->fmt.dwMask |= PFM_BORDER;
777     break;
778   case rtfBorderRight:
779     info->borderType = RTFBorderParaRight;
780     info->fmt.wBorders |= 2;
781     info->fmt.dwMask |= PFM_BORDER;
782     break;
783   case rtfBorderTop:
784     info->borderType = RTFBorderParaTop;
785     info->fmt.wBorders |= 4;
786     info->fmt.dwMask |= PFM_BORDER;
787     break;
788   case rtfBorderBottom:
789     info->borderType = RTFBorderParaBottom;
790     info->fmt.wBorders |= 8;
791     info->fmt.dwMask |= PFM_BORDER;
792     break;
793   case rtfBorderSingle:
794     info->fmt.wBorders &= ~0x700;
795     info->fmt.wBorders |= 1 << 8;
796     info->fmt.dwMask |= PFM_BORDER;
797     break;
798   case rtfBorderThick:
799     info->fmt.wBorders &= ~0x700;
800     info->fmt.wBorders |= 2 << 8;
801     info->fmt.dwMask |= PFM_BORDER;
802     break;
803   case rtfBorderShadow:
804     info->fmt.wBorders &= ~0x700;
805     info->fmt.wBorders |= 10 << 8;
806     info->fmt.dwMask |= PFM_BORDER;
807     break;
808   case rtfBorderDouble:
809     info->fmt.wBorders &= ~0x700;
810     info->fmt.wBorders |= 7 << 8;
811     info->fmt.dwMask |= PFM_BORDER;
812     break;
813   case rtfBorderDot:
814     info->fmt.wBorders &= ~0x700;
815     info->fmt.wBorders |= 11 << 8;
816     info->fmt.dwMask |= PFM_BORDER;
817     break;
818   case rtfBorderWidth:
819   {
820     int borderSide = info->borderType & RTFBorderSideMask;
821     RTFTable *tableDef = info->tableDef;
822     if ((info->borderType & RTFBorderTypeMask) == RTFBorderTypeCell)
823     {
824       RTFBorder *border;
825       if (!tableDef || tableDef->numCellsDefined >= MAX_TABLE_CELLS)
826         break;
827       border = &tableDef->cells[tableDef->numCellsDefined].border[borderSide];
828       border->width = info->rtfParam;
829       break;
830     }
831     info->fmt.wBorderWidth = info->rtfParam;
832     info->fmt.dwMask |= PFM_BORDER;
833     break;
834   }
835   case rtfBorderSpace:
836     info->fmt.wBorderSpace = info->rtfParam;
837     info->fmt.dwMask |= PFM_BORDER;
838     break;
839   case rtfBorderColor:
840   {
841     RTFTable *tableDef = info->tableDef;
842     int borderSide = info->borderType & RTFBorderSideMask;
843     int borderType = info->borderType & RTFBorderTypeMask;
844     switch(borderType)
845     {
846     case RTFBorderTypePara:
847       if (!info->editor->bEmulateVersion10) /* v4.1 */
848         break;
849       /* v1.0 - 3.0 treat paragraph and row borders the same. */
850     case RTFBorderTypeRow:
851       if (tableDef) {
852         tableDef->border[borderSide].color = info->rtfParam;
853       }
854       break;
855     case RTFBorderTypeCell:
856       if (tableDef && tableDef->numCellsDefined < MAX_TABLE_CELLS) {
857         tableDef->cells[tableDef->numCellsDefined].border[borderSide].color = info->rtfParam;
858       }
859       break;
860     }
861     break;
862   }
863   case rtfRTLPar:
864     info->fmt.dwMask |= PFM_RTLPARA;
865     info->fmt.wEffects |= PFE_RTLPARA;
866     break;
867   case rtfLTRPar:
868     info->fmt.dwMask |= PFM_RTLPARA;
869     info->fmt.wEffects &= ~PFE_RTLPARA;
870     break;
871   }
872 }
873 
ME_RTFTblAttrHook(RTF_Info * info)874 void ME_RTFTblAttrHook(RTF_Info *info)
875 {
876   switch (info->rtfMinor)
877   {
878     case rtfRowDef:
879     {
880       if (!info->editor->bEmulateVersion10) /* v4.1 */
881         info->borderType = 0; /* Not sure */
882       else /* v1.0 - 3.0 */
883         info->borderType = RTFBorderRowTop;
884       if (!info->tableDef) {
885         info->tableDef = ME_MakeTableDef(info->editor);
886       } else {
887         ME_InitTableDef(info->editor, info->tableDef);
888       }
889       break;
890     }
891     case rtfCellPos:
892     {
893       int cellNum;
894       if (!info->tableDef)
895       {
896         info->tableDef = ME_MakeTableDef(info->editor);
897       }
898       cellNum = info->tableDef->numCellsDefined;
899       if (cellNum >= MAX_TABLE_CELLS)
900         break;
901       info->tableDef->cells[cellNum].rightBoundary = info->rtfParam;
902       if (cellNum < MAX_TAB_STOPS) {
903         /* Tab stops were used to store cell positions before v4.1 but v4.1
904          * still seems to set the tabstops without using them. */
905         ME_DisplayItem *para = info->editor->pCursors[0].pPara;
906         PARAFORMAT2 *pFmt = &para->member.para.fmt;
907         pFmt->rgxTabs[cellNum] &= ~0x00FFFFFF;
908         pFmt->rgxTabs[cellNum] |= 0x00FFFFFF & info->rtfParam;
909       }
910       info->tableDef->numCellsDefined++;
911       break;
912     }
913     case rtfRowBordTop:
914       info->borderType = RTFBorderRowTop;
915       break;
916     case rtfRowBordLeft:
917       info->borderType = RTFBorderRowLeft;
918       break;
919     case rtfRowBordBottom:
920       info->borderType = RTFBorderRowBottom;
921       break;
922     case rtfRowBordRight:
923       info->borderType = RTFBorderRowRight;
924       break;
925     case rtfCellBordTop:
926       info->borderType = RTFBorderCellTop;
927       break;
928     case rtfCellBordLeft:
929       info->borderType = RTFBorderCellLeft;
930       break;
931     case rtfCellBordBottom:
932       info->borderType = RTFBorderCellBottom;
933       break;
934     case rtfCellBordRight:
935       info->borderType = RTFBorderCellRight;
936       break;
937     case rtfRowGapH:
938       if (info->tableDef)
939         info->tableDef->gapH = info->rtfParam;
940       break;
941     case rtfRowLeftEdge:
942       if (info->tableDef)
943         info->tableDef->leftEdge = info->rtfParam;
944       break;
945   }
946 }
947 
ME_RTFSpecialCharHook(RTF_Info * info)948 void ME_RTFSpecialCharHook(RTF_Info *info)
949 {
950   RTFTable *tableDef = info->tableDef;
951   switch (info->rtfMinor)
952   {
953     case rtfNestCell:
954       if (info->editor->bEmulateVersion10) /* v1.0 - v3.0 */
955         break;
956       /* else fall through since v4.1 treats rtfNestCell and rtfCell the same */
957     case rtfCell:
958       if (!tableDef)
959         break;
960       RTFFlushOutputBuffer(info);
961       if (!info->editor->bEmulateVersion10) { /* v4.1 */
962         if (tableDef->tableRowStart)
963         {
964           if (!info->nestingLevel &&
965               tableDef->tableRowStart->member.para.nFlags & MEPF_ROWEND)
966           {
967             ME_DisplayItem *para = tableDef->tableRowStart;
968             para = para->member.para.next_para;
969             para = ME_InsertTableRowStartAtParagraph(info->editor, para);
970             tableDef->tableRowStart = para;
971             info->nestingLevel = 1;
972           }
973           ME_InsertTableCellFromCursor(info->editor);
974         }
975       } else { /* v1.0 - v3.0 */
976         ME_DisplayItem *para = info->editor->pCursors[0].pPara;
977         PARAFORMAT2 *pFmt = &para->member.para.fmt;
978         if (pFmt->dwMask & PFM_TABLE && pFmt->wEffects & PFE_TABLE &&
979             tableDef->numCellsInserted < tableDef->numCellsDefined)
980         {
981           WCHAR tab = '\t';
982           ME_InsertTextFromCursor(info->editor, 0, &tab, 1, info->style);
983           tableDef->numCellsInserted++;
984         }
985       }
986       break;
987     case rtfNestRow:
988       if (info->editor->bEmulateVersion10) /* v1.0 - v3.0 */
989         break;
990       /* else fall through since v4.1 treats rtfNestRow and rtfRow the same */
991     case rtfRow:
992     {
993       ME_DisplayItem *para, *cell, *run;
994       int i;
995 
996       if (!tableDef)
997         break;
998       RTFFlushOutputBuffer(info);
999       if (!info->editor->bEmulateVersion10) { /* v4.1 */
1000         if (!tableDef->tableRowStart)
1001           break;
1002         if (!info->nestingLevel &&
1003             tableDef->tableRowStart->member.para.nFlags & MEPF_ROWEND)
1004         {
1005           para = tableDef->tableRowStart;
1006           para = para->member.para.next_para;
1007           para = ME_InsertTableRowStartAtParagraph(info->editor, para);
1008           tableDef->tableRowStart = para;
1009           info->nestingLevel++;
1010         }
1011         para = tableDef->tableRowStart;
1012         cell = ME_FindItemFwd(para, diCell);
1013         assert(cell && !cell->member.cell.prev_cell);
1014         if (tableDef->numCellsDefined < 1)
1015         {
1016           /* 2000 twips appears to be the cell size that native richedit uses
1017            * when no cell sizes are specified. */
1018           const int defaultCellSize = 2000;
1019           int nRightBoundary = defaultCellSize;
1020           cell->member.cell.nRightBoundary = nRightBoundary;
1021           while (cell->member.cell.next_cell) {
1022             cell = cell->member.cell.next_cell;
1023             nRightBoundary += defaultCellSize;
1024             cell->member.cell.nRightBoundary = nRightBoundary;
1025           }
1026           para = ME_InsertTableCellFromCursor(info->editor);
1027           cell = para->member.para.pCell;
1028           cell->member.cell.nRightBoundary = nRightBoundary;
1029         } else {
1030           for (i = 0; i < tableDef->numCellsDefined; i++)
1031           {
1032             RTFCell *cellDef = &tableDef->cells[i];
1033             cell->member.cell.nRightBoundary = cellDef->rightBoundary;
1034             ME_ApplyBorderProperties(info, &cell->member.cell.border,
1035                                      cellDef->border);
1036             cell = cell->member.cell.next_cell;
1037             if (!cell)
1038             {
1039               para = ME_InsertTableCellFromCursor(info->editor);
1040               cell = para->member.para.pCell;
1041             }
1042           }
1043           /* Cell for table row delimiter is empty */
1044           cell->member.cell.nRightBoundary = tableDef->cells[i-1].rightBoundary;
1045         }
1046 
1047         run = ME_FindItemFwd(cell, diRun);
1048         if (info->editor->pCursors[0].pRun != run ||
1049             info->editor->pCursors[0].nOffset)
1050         {
1051           int nOfs, nChars;
1052           /* Delete inserted cells that aren't defined. */
1053           info->editor->pCursors[1].pRun = run;
1054           info->editor->pCursors[1].pPara = ME_GetParagraph(run);
1055           info->editor->pCursors[1].nOffset = 0;
1056           nOfs = ME_GetCursorOfs(&info->editor->pCursors[1]);
1057           nChars = ME_GetCursorOfs(&info->editor->pCursors[0]) - nOfs;
1058           ME_InternalDeleteText(info->editor, &info->editor->pCursors[1],
1059                                 nChars, TRUE);
1060         }
1061 
1062         para = ME_InsertTableRowEndFromCursor(info->editor);
1063         para->member.para.fmt.dxOffset = abs(info->tableDef->gapH);
1064         para->member.para.fmt.dxStartIndent = info->tableDef->leftEdge;
1065         ME_ApplyBorderProperties(info, &para->member.para.border,
1066                                  tableDef->border);
1067         info->nestingLevel--;
1068         if (!info->nestingLevel)
1069         {
1070           if (info->canInheritInTbl) {
1071             tableDef->tableRowStart = para;
1072           } else {
1073             while (info->tableDef) {
1074               tableDef = info->tableDef;
1075               info->tableDef = tableDef->parent;
1076               heap_free(tableDef);
1077             }
1078           }
1079         } else {
1080           info->tableDef = tableDef->parent;
1081           heap_free(tableDef);
1082         }
1083       } else { /* v1.0 - v3.0 */
1084         WCHAR endl = '\r';
1085         ME_DisplayItem *para = info->editor->pCursors[0].pPara;
1086         PARAFORMAT2 *pFmt = &para->member.para.fmt;
1087         pFmt->dxOffset = info->tableDef->gapH;
1088         pFmt->dxStartIndent = info->tableDef->leftEdge;
1089 
1090         ME_ApplyBorderProperties(info, &para->member.para.border,
1091                                  tableDef->border);
1092         while (tableDef->numCellsInserted < tableDef->numCellsDefined)
1093         {
1094           WCHAR tab = '\t';
1095           ME_InsertTextFromCursor(info->editor, 0, &tab, 1, info->style);
1096           tableDef->numCellsInserted++;
1097         }
1098         pFmt->cTabCount = min(tableDef->numCellsDefined, MAX_TAB_STOPS);
1099         if (!tableDef->numCellsDefined)
1100           pFmt->wEffects &= ~PFE_TABLE;
1101         ME_InsertTextFromCursor(info->editor, 0, &endl, 1, info->style);
1102         tableDef->numCellsInserted = 0;
1103       }
1104       break;
1105     }
1106     case rtfTab:
1107     case rtfPar:
1108       if (info->editor->bEmulateVersion10) { /* v1.0 - 3.0 */
1109         ME_DisplayItem *para;
1110         PARAFORMAT2 *pFmt;
1111         RTFFlushOutputBuffer(info);
1112         para = info->editor->pCursors[0].pPara;
1113         pFmt = &para->member.para.fmt;
1114         if (pFmt->dwMask & PFM_TABLE && pFmt->wEffects & PFE_TABLE)
1115         {
1116           /* rtfPar is treated like a space within a table. */
1117           info->rtfClass = rtfText;
1118           info->rtfMajor = ' ';
1119         }
1120         else if (info->rtfMinor == rtfPar && tableDef)
1121           tableDef->numCellsInserted = 0;
1122       }
1123       break;
1124   }
1125 }
1126 
insert_static_object(ME_TextEditor * editor,HENHMETAFILE hemf,HBITMAP hbmp,const SIZEL * sz)1127 static HRESULT insert_static_object(ME_TextEditor *editor, HENHMETAFILE hemf, HBITMAP hbmp,
1128                                     const SIZEL* sz)
1129 {
1130   LPOLEOBJECT         lpObject = NULL;
1131   LPSTORAGE           lpStorage = NULL;
1132   LPOLECLIENTSITE     lpClientSite = NULL;
1133   LPDATAOBJECT        lpDataObject = NULL;
1134   LPOLECACHE          lpOleCache = NULL;
1135   LPRICHEDITOLE       lpReOle = NULL;
1136   STGMEDIUM           stgm;
1137   FORMATETC           fm;
1138   CLSID               clsid;
1139   HRESULT             hr = E_FAIL;
1140   DWORD               conn;
1141 
1142   if (hemf)
1143   {
1144       stgm.tymed = TYMED_ENHMF;
1145       stgm.u.hEnhMetaFile = hemf;
1146       fm.cfFormat = CF_ENHMETAFILE;
1147   }
1148   else if (hbmp)
1149   {
1150       stgm.tymed = TYMED_GDI;
1151       stgm.u.hBitmap = hbmp;
1152       fm.cfFormat = CF_BITMAP;
1153   }
1154   stgm.pUnkForRelease = NULL;
1155 
1156   fm.ptd = NULL;
1157   fm.dwAspect = DVASPECT_CONTENT;
1158   fm.lindex = -1;
1159   fm.tymed = stgm.tymed;
1160 
1161   if (!editor->reOle)
1162   {
1163     if (!CreateIRichEditOle(NULL, editor, (LPVOID *)&editor->reOle))
1164       return hr;
1165   }
1166 
1167   if (OleCreateDefaultHandler(&CLSID_NULL, NULL, &IID_IOleObject, (void**)&lpObject) == S_OK &&
1168       IUnknown_QueryInterface(editor->reOle, &IID_IRichEditOle, (void**)&lpReOle) == S_OK &&
1169       IRichEditOle_GetClientSite(lpReOle, &lpClientSite) == S_OK &&
1170       IOleObject_SetClientSite(lpObject, lpClientSite) == S_OK &&
1171       IOleObject_GetUserClassID(lpObject, &clsid) == S_OK &&
1172       IOleObject_QueryInterface(lpObject, &IID_IOleCache, (void**)&lpOleCache) == S_OK &&
1173       IOleCache_Cache(lpOleCache, &fm, 0, &conn) == S_OK &&
1174       IOleObject_QueryInterface(lpObject, &IID_IDataObject, (void**)&lpDataObject) == S_OK &&
1175       IDataObject_SetData(lpDataObject, &fm, &stgm, TRUE) == S_OK)
1176   {
1177     REOBJECT            reobject;
1178 
1179     reobject.cbStruct = sizeof(reobject);
1180     reobject.cp = REO_CP_SELECTION;
1181     reobject.clsid = clsid;
1182     reobject.poleobj = lpObject;
1183     reobject.pstg = lpStorage;
1184     reobject.polesite = lpClientSite;
1185     /* convert from twips to .01 mm */
1186     reobject.sizel.cx = MulDiv(sz->cx, 254, 144);
1187     reobject.sizel.cy = MulDiv(sz->cy, 254, 144);
1188     reobject.dvaspect = DVASPECT_CONTENT;
1189     reobject.dwFlags = 0; /* FIXME */
1190     reobject.dwUser = 0;
1191 
1192     ME_InsertOLEFromCursor(editor, &reobject, 0);
1193     hr = S_OK;
1194   }
1195 
1196   if (lpObject)       IOleObject_Release(lpObject);
1197   if (lpClientSite)   IOleClientSite_Release(lpClientSite);
1198   if (lpStorage)      IStorage_Release(lpStorage);
1199   if (lpDataObject)   IDataObject_Release(lpDataObject);
1200   if (lpOleCache)     IOleCache_Release(lpOleCache);
1201   if (lpReOle)        IRichEditOle_Release(lpReOle);
1202 
1203   return hr;
1204 }
1205 
ME_RTFReadShpPictGroup(RTF_Info * info)1206 static void ME_RTFReadShpPictGroup( RTF_Info *info )
1207 {
1208     int level = 1;
1209 
1210     for (;;)
1211     {
1212         RTFGetToken (info);
1213 
1214         if (info->rtfClass == rtfEOF) return;
1215         if (RTFCheckCM( info, rtfGroup, rtfEndGroup ))
1216         {
1217             if (--level == 0) break;
1218         }
1219         else if (RTFCheckCM( info, rtfGroup, rtfBeginGroup ))
1220         {
1221             level++;
1222         }
1223         else
1224         {
1225             RTFRouteToken( info );
1226             if (RTFCheckCM( info, rtfGroup, rtfEndGroup ))
1227                 level--;
1228         }
1229     }
1230 
1231     RTFRouteToken( info ); /* feed "}" back to router */
1232     return;
1233 }
1234 
read_hex_data(RTF_Info * info,BYTE ** out)1235 static DWORD read_hex_data( RTF_Info *info, BYTE **out )
1236 {
1237     DWORD read = 0, size = 1024;
1238     BYTE *buf, val;
1239     BOOL flip;
1240 
1241     *out = NULL;
1242 
1243     if (info->rtfClass != rtfText)
1244     {
1245         ERR("Called with incorrect token\n");
1246         return 0;
1247     }
1248 
1249     buf = HeapAlloc( GetProcessHeap(), 0, size );
1250     if (!buf) return 0;
1251 
1252     val = info->rtfMajor;
1253     for (flip = TRUE;; flip = !flip)
1254     {
1255         RTFGetToken( info );
1256         if (info->rtfClass == rtfEOF)
1257         {
1258             HeapFree( GetProcessHeap(), 0, buf );
1259             return 0;
1260         }
1261         if (info->rtfClass != rtfText) break;
1262         if (flip)
1263         {
1264             if (read >= size)
1265             {
1266                 size *= 2;
1267                 buf = HeapReAlloc( GetProcessHeap(), 0, buf, size );
1268                 if (!buf) return 0;
1269             }
1270             buf[read++] = RTFCharToHex(val) * 16 + RTFCharToHex(info->rtfMajor);
1271         }
1272         else
1273             val = info->rtfMajor;
1274     }
1275     if (flip) FIXME("wrong hex string\n");
1276 
1277     *out = buf;
1278     return read;
1279 }
1280 
ME_RTFReadPictGroup(RTF_Info * info)1281 static void ME_RTFReadPictGroup(RTF_Info *info)
1282 {
1283     SIZEL sz;
1284     BYTE *buffer = NULL;
1285     DWORD size = 0;
1286     METAFILEPICT mfp;
1287     HENHMETAFILE hemf;
1288     HBITMAP hbmp;
1289     enum gfxkind {gfx_unknown = 0, gfx_enhmetafile, gfx_metafile, gfx_dib} gfx = gfx_unknown;
1290     int level = 1;
1291 
1292     mfp.mm = MM_TEXT;
1293     sz.cx = sz.cy = 0;
1294 
1295     for (;;)
1296     {
1297         RTFGetToken( info );
1298 
1299         if (info->rtfClass == rtfText)
1300         {
1301             if (level == 1)
1302             {
1303                 if (!buffer)
1304                     size = read_hex_data( info, &buffer );
1305             }
1306             else
1307             {
1308                 RTFSkipGroup( info );
1309             }
1310         } /* We potentially have a new token so fall through. */
1311 
1312         if (info->rtfClass == rtfEOF) return;
1313 
1314         if (RTFCheckCM( info, rtfGroup, rtfEndGroup ))
1315         {
1316             if (--level == 0) break;
1317             continue;
1318         }
1319         if (RTFCheckCM( info, rtfGroup, rtfBeginGroup ))
1320         {
1321             level++;
1322             continue;
1323         }
1324         if (!RTFCheckCM( info, rtfControl, rtfPictAttr ))
1325         {
1326             RTFRouteToken( info );
1327             if (RTFCheckCM( info, rtfGroup, rtfEndGroup ))
1328                 level--;
1329             continue;
1330         }
1331 
1332         if (RTFCheckMM( info, rtfPictAttr, rtfWinMetafile ))
1333         {
1334             mfp.mm = info->rtfParam;
1335             gfx = gfx_metafile;
1336         }
1337         else if (RTFCheckMM( info, rtfPictAttr, rtfDevIndBitmap ))
1338         {
1339             if (info->rtfParam != 0) FIXME("dibitmap should be 0 (%d)\n", info->rtfParam);
1340             gfx = gfx_dib;
1341         }
1342         else if (RTFCheckMM( info, rtfPictAttr, rtfEmfBlip ))
1343             gfx = gfx_enhmetafile;
1344         else if (RTFCheckMM( info, rtfPictAttr, rtfPicWid ))
1345             mfp.xExt = info->rtfParam;
1346         else if (RTFCheckMM( info, rtfPictAttr, rtfPicHt ))
1347             mfp.yExt = info->rtfParam;
1348         else if (RTFCheckMM( info, rtfPictAttr, rtfPicGoalWid ))
1349             sz.cx = info->rtfParam;
1350         else if (RTFCheckMM( info, rtfPictAttr, rtfPicGoalHt ))
1351             sz.cy = info->rtfParam;
1352         else
1353             FIXME("Non supported attribute: %d %d %d\n", info->rtfClass, info->rtfMajor, info->rtfMinor);
1354     }
1355 
1356     if (buffer)
1357     {
1358         switch (gfx)
1359         {
1360         case gfx_enhmetafile:
1361             if ((hemf = SetEnhMetaFileBits( size, buffer )))
1362                 insert_static_object( info->editor, hemf, NULL, &sz );
1363             break;
1364         case gfx_metafile:
1365             if ((hemf = SetWinMetaFileBits( size, buffer, NULL, &mfp )))
1366                 insert_static_object( info->editor, hemf, NULL, &sz );
1367             break;
1368         case gfx_dib:
1369         {
1370             BITMAPINFO *bi = (BITMAPINFO*)buffer;
1371             HDC hdc = GetDC(0);
1372             unsigned nc = bi->bmiHeader.biClrUsed;
1373 
1374             /* not quite right, especially for bitfields type of compression */
1375             if (!nc && bi->bmiHeader.biBitCount <= 8)
1376                 nc = 1 << bi->bmiHeader.biBitCount;
1377             if ((hbmp = CreateDIBitmap( hdc, &bi->bmiHeader,
1378                                         CBM_INIT, (char*)(bi + 1) + nc * sizeof(RGBQUAD),
1379                                         bi, DIB_RGB_COLORS)) )
1380                 insert_static_object( info->editor, NULL, hbmp, &sz );
1381             ReleaseDC( 0, hdc );
1382             break;
1383         }
1384         default:
1385             break;
1386         }
1387     }
1388     HeapFree( GetProcessHeap(), 0, buffer );
1389     RTFRouteToken( info ); /* feed "}" back to router */
1390     return;
1391 }
1392 
1393 /* for now, lookup the \result part and use it, whatever the object */
ME_RTFReadObjectGroup(RTF_Info * info)1394 static void ME_RTFReadObjectGroup(RTF_Info *info)
1395 {
1396   for (;;)
1397   {
1398     RTFGetToken (info);
1399     if (info->rtfClass == rtfEOF)
1400       return;
1401     if (RTFCheckCM(info, rtfGroup, rtfEndGroup))
1402       break;
1403     if (RTFCheckCM(info, rtfGroup, rtfBeginGroup))
1404     {
1405       RTFGetToken (info);
1406       if (info->rtfClass == rtfEOF)
1407         return;
1408       if (RTFCheckCMM(info, rtfControl, rtfDestination, rtfObjResult))
1409       {
1410 	int	level = 1;
1411 
1412 	while (RTFGetToken (info) != rtfEOF)
1413 	{
1414           if (info->rtfClass == rtfGroup)
1415           {
1416             if (info->rtfMajor == rtfBeginGroup) level++;
1417             else if (info->rtfMajor == rtfEndGroup && --level < 0) break;
1418           }
1419           RTFRouteToken(info);
1420 	}
1421       }
1422       else RTFSkipGroup(info);
1423       continue;
1424     }
1425     if (!RTFCheckCM (info, rtfControl, rtfObjAttr))
1426     {
1427       FIXME("Non supported attribute: %d %d %d\n", info->rtfClass, info->rtfMajor, info->rtfMinor);
1428       return;
1429     }
1430   }
1431   RTFRouteToken(info);	/* feed "}" back to router */
1432 }
1433 
ME_RTFReadParnumGroup(RTF_Info * info)1434 static void ME_RTFReadParnumGroup( RTF_Info *info )
1435 {
1436     int level = 1, type = -1;
1437     WORD indent = 0, start = 1;
1438     WCHAR txt_before = 0, txt_after = 0;
1439 
1440     for (;;)
1441     {
1442         RTFGetToken( info );
1443 
1444         if (RTFCheckCMM( info, rtfControl, rtfDestination, rtfParNumTextBefore ) ||
1445             RTFCheckCMM( info, rtfControl, rtfDestination, rtfParNumTextAfter ))
1446         {
1447             int loc = info->rtfMinor;
1448 
1449             RTFGetToken( info );
1450             if (info->rtfClass == rtfText)
1451             {
1452                 if (loc == rtfParNumTextBefore)
1453                     txt_before = info->rtfMajor;
1454                 else
1455                     txt_after = info->rtfMajor;
1456                 continue;
1457             }
1458             /* falling through to catch EOFs and group level changes */
1459         }
1460 
1461         if (info->rtfClass == rtfEOF)
1462             return;
1463 
1464         if (RTFCheckCM( info, rtfGroup, rtfEndGroup ))
1465         {
1466             if (--level == 0) break;
1467             continue;
1468         }
1469 
1470         if (RTFCheckCM( info, rtfGroup, rtfBeginGroup ))
1471         {
1472             level++;
1473             continue;
1474         }
1475 
1476         /* Ignore non para-attr */
1477         if (!RTFCheckCM( info, rtfControl, rtfParAttr ))
1478             continue;
1479 
1480         switch (info->rtfMinor)
1481         {
1482         case rtfParLevel: /* Para level is ignored */
1483         case rtfParSimple:
1484             break;
1485         case rtfParBullet:
1486             type = PFN_BULLET;
1487             break;
1488 
1489         case rtfParNumDecimal:
1490             type = PFN_ARABIC;
1491             break;
1492         case rtfParNumULetter:
1493             type = PFN_UCLETTER;
1494             break;
1495         case rtfParNumURoman:
1496             type = PFN_UCROMAN;
1497             break;
1498         case rtfParNumLLetter:
1499             type = PFN_LCLETTER;
1500             break;
1501         case rtfParNumLRoman:
1502             type = PFN_LCROMAN;
1503             break;
1504 
1505         case rtfParNumIndent:
1506             indent = info->rtfParam;
1507             break;
1508         case rtfParNumStartAt:
1509             start = info->rtfParam;
1510             break;
1511         }
1512     }
1513 
1514     if (type != -1)
1515     {
1516         info->fmt.dwMask |= (PFM_NUMBERING | PFM_NUMBERINGSTART | PFM_NUMBERINGSTYLE | PFM_NUMBERINGTAB);
1517         info->fmt.wNumbering = type;
1518         info->fmt.wNumberingStart = start;
1519         info->fmt.wNumberingStyle = PFNS_PAREN;
1520         if (type != PFN_BULLET)
1521         {
1522             if (txt_before == 0 && txt_after == 0)
1523                 info->fmt.wNumberingStyle = PFNS_PLAIN;
1524             else if (txt_after == '.')
1525                 info->fmt.wNumberingStyle = PFNS_PERIOD;
1526             else if (txt_before == '(' && txt_after == ')')
1527                 info->fmt.wNumberingStyle = PFNS_PARENS;
1528         }
1529         info->fmt.wNumberingTab = indent;
1530     }
1531 
1532     TRACE("type %d indent %d start %d txt before %04x txt after %04x\n",
1533           type, indent, start, txt_before, txt_after);
1534 
1535     RTFRouteToken( info );     /* feed "}" back to router */
1536 }
1537 
ME_RTFReadHook(RTF_Info * info)1538 static void ME_RTFReadHook(RTF_Info *info)
1539 {
1540   switch(info->rtfClass)
1541   {
1542     case rtfGroup:
1543       switch(info->rtfMajor)
1544       {
1545         case rtfBeginGroup:
1546           if (info->stackTop < maxStack) {
1547             info->stack[info->stackTop].style = info->style;
1548             ME_AddRefStyle(info->style);
1549             info->stack[info->stackTop].codePage = info->codePage;
1550             info->stack[info->stackTop].unicodeLength = info->unicodeLength;
1551           }
1552           info->stackTop++;
1553           info->styleChanged = FALSE;
1554           break;
1555         case rtfEndGroup:
1556         {
1557           RTFFlushOutputBuffer(info);
1558           info->stackTop--;
1559           if (info->stackTop <= 0)
1560             info->rtfClass = rtfEOF;
1561           if (info->stackTop < 0)
1562             return;
1563 
1564           ME_ReleaseStyle(info->style);
1565           info->style = info->stack[info->stackTop].style;
1566           info->codePage = info->stack[info->stackTop].codePage;
1567           info->unicodeLength = info->stack[info->stackTop].unicodeLength;
1568           break;
1569         }
1570       }
1571       break;
1572   }
1573 }
1574 
1575 void
ME_StreamInFill(ME_InStream * stream)1576 ME_StreamInFill(ME_InStream *stream)
1577 {
1578   stream->editstream->dwError = stream->editstream->pfnCallback(stream->editstream->dwCookie,
1579                                                                 (BYTE *)stream->buffer,
1580                                                                 sizeof(stream->buffer),
1581                                                                 (LONG *)&stream->dwSize);
1582   stream->dwUsed = 0;
1583 }
1584 
ME_StreamIn(ME_TextEditor * editor,DWORD format,EDITSTREAM * stream,BOOL stripLastCR)1585 static LRESULT ME_StreamIn(ME_TextEditor *editor, DWORD format, EDITSTREAM *stream, BOOL stripLastCR)
1586 {
1587   RTF_Info parser;
1588   ME_Style *style;
1589   int from, to, nUndoMode;
1590   int nEventMask = editor->nEventMask;
1591   ME_InStream inStream;
1592   BOOL invalidRTF = FALSE;
1593   ME_Cursor *selStart, *selEnd;
1594   LRESULT num_read = 0; /* bytes read for SF_TEXT, non-control chars inserted for SF_RTF */
1595 
1596   TRACE("stream==%p editor==%p format==0x%X\n", stream, editor, format);
1597   editor->nEventMask = 0;
1598 
1599   ME_GetSelectionOfs(editor, &from, &to);
1600   if (format & SFF_SELECTION && editor->mode & TM_RICHTEXT)
1601   {
1602     ME_GetSelection(editor, &selStart, &selEnd);
1603     style = ME_GetSelectionInsertStyle(editor);
1604 
1605     ME_InternalDeleteText(editor, selStart, to - from, FALSE);
1606 
1607     /* Don't insert text at the end of the table row */
1608     if (!editor->bEmulateVersion10) { /* v4.1 */
1609       ME_DisplayItem *para = editor->pCursors->pPara;
1610       if (para->member.para.nFlags & MEPF_ROWEND)
1611       {
1612         para = para->member.para.next_para;
1613         editor->pCursors[0].pPara = para;
1614         editor->pCursors[0].pRun = ME_FindItemFwd(para, diRun);
1615         editor->pCursors[0].nOffset = 0;
1616       }
1617       if (para->member.para.nFlags & MEPF_ROWSTART)
1618       {
1619         para = para->member.para.next_para;
1620         editor->pCursors[0].pPara = para;
1621         editor->pCursors[0].pRun = ME_FindItemFwd(para, diRun);
1622         editor->pCursors[0].nOffset = 0;
1623       }
1624       editor->pCursors[1] = editor->pCursors[0];
1625     } else { /* v1.0 - 3.0 */
1626       if (editor->pCursors[0].pRun->member.run.nFlags & MERF_ENDPARA &&
1627           ME_IsInTable(editor->pCursors[0].pRun))
1628         return 0;
1629     }
1630   } else {
1631     style = editor->pBuffer->pDefaultStyle;
1632     ME_AddRefStyle(style);
1633     set_selection_cursors(editor, 0, 0);
1634     ME_InternalDeleteText(editor, &editor->pCursors[1],
1635                           ME_GetTextLength(editor), FALSE);
1636     from = to = 0;
1637     ME_ClearTempStyle(editor);
1638     ME_SetDefaultParaFormat(editor, &editor->pCursors[0].pPara->member.para.fmt);
1639   }
1640 
1641 
1642   /* Back up undo mode to a local variable */
1643   nUndoMode = editor->nUndoMode;
1644 
1645   /* Only create an undo if SFF_SELECTION is set */
1646   if (!(format & SFF_SELECTION))
1647     editor->nUndoMode = umIgnore;
1648 
1649   inStream.editstream = stream;
1650   inStream.editstream->dwError = 0;
1651   inStream.dwSize = 0;
1652   inStream.dwUsed = 0;
1653 
1654   if (format & SF_RTF)
1655   {
1656     /* Check if it's really RTF, and if it is not, use plain text */
1657     ME_StreamInFill(&inStream);
1658     if (!inStream.editstream->dwError)
1659     {
1660       if ((!editor->bEmulateVersion10 && strncmp(inStream.buffer, "{\\rtf", 5) && strncmp(inStream.buffer, "{\\urtf", 6))
1661 	|| (editor->bEmulateVersion10 && *inStream.buffer != '{'))
1662       {
1663         invalidRTF = TRUE;
1664         inStream.editstream->dwError = -16;
1665       }
1666     }
1667   }
1668 
1669   if (!invalidRTF && !inStream.editstream->dwError)
1670   {
1671     ME_Cursor start;
1672     from = ME_GetCursorOfs(&editor->pCursors[0]);
1673     if (format & SF_RTF) {
1674 
1675       /* setup the RTF parser */
1676       memset(&parser, 0, sizeof parser);
1677       RTFSetEditStream(&parser, &inStream);
1678       parser.rtfFormat = format&(SF_TEXT|SF_RTF);
1679       parser.editor = editor;
1680       parser.style = style;
1681       WriterInit(&parser);
1682       RTFInit(&parser);
1683       RTFSetReadHook(&parser, ME_RTFReadHook);
1684       RTFSetDestinationCallback(&parser, rtfShpPict, ME_RTFReadShpPictGroup);
1685       RTFSetDestinationCallback(&parser, rtfPict, ME_RTFReadPictGroup);
1686       RTFSetDestinationCallback(&parser, rtfObject, ME_RTFReadObjectGroup);
1687       RTFSetDestinationCallback(&parser, rtfParNumbering, ME_RTFReadParnumGroup);
1688       if (!parser.editor->bEmulateVersion10) /* v4.1 */
1689       {
1690         RTFSetDestinationCallback(&parser, rtfNoNestTables, RTFSkipGroup);
1691         RTFSetDestinationCallback(&parser, rtfNestTableProps, RTFReadGroup);
1692       }
1693       BeginFile(&parser);
1694 
1695       /* do the parsing */
1696       RTFRead(&parser);
1697       RTFFlushOutputBuffer(&parser);
1698       if (!editor->bEmulateVersion10) { /* v4.1 */
1699         if (parser.tableDef && parser.tableDef->tableRowStart &&
1700             (parser.nestingLevel > 0 || parser.canInheritInTbl))
1701         {
1702           /* Delete any incomplete table row at the end of the rich text. */
1703           int nOfs, nChars;
1704           ME_DisplayItem *para;
1705 
1706           parser.rtfMinor = rtfRow;
1707           /* Complete the table row before deleting it.
1708            * By doing it this way we will have the current paragraph format set
1709            * properly to reflect that is not in the complete table, and undo items
1710            * will be added for this change to the current paragraph format. */
1711           if (parser.nestingLevel > 0)
1712           {
1713             while (parser.nestingLevel > 1)
1714               ME_RTFSpecialCharHook(&parser); /* Decrements nestingLevel */
1715             para = parser.tableDef->tableRowStart;
1716             ME_RTFSpecialCharHook(&parser);
1717           } else {
1718             para = parser.tableDef->tableRowStart;
1719             ME_RTFSpecialCharHook(&parser);
1720             assert(para->member.para.nFlags & MEPF_ROWEND);
1721             para = para->member.para.next_para;
1722           }
1723 
1724           editor->pCursors[1].pPara = para;
1725           editor->pCursors[1].pRun = ME_FindItemFwd(para, diRun);
1726           editor->pCursors[1].nOffset = 0;
1727           nOfs = ME_GetCursorOfs(&editor->pCursors[1]);
1728           nChars = ME_GetCursorOfs(&editor->pCursors[0]) - nOfs;
1729           ME_InternalDeleteText(editor, &editor->pCursors[1], nChars, TRUE);
1730           if (parser.tableDef)
1731             parser.tableDef->tableRowStart = NULL;
1732         }
1733       }
1734       ME_CheckTablesForCorruption(editor);
1735       RTFDestroy(&parser);
1736 
1737       if (parser.stackTop > 0)
1738       {
1739         while (--parser.stackTop >= 0)
1740         {
1741           ME_ReleaseStyle(parser.style);
1742           parser.style = parser.stack[parser.stackTop].style;
1743         }
1744         if (!inStream.editstream->dwError)
1745           inStream.editstream->dwError = HRESULT_FROM_WIN32(ERROR_HANDLE_EOF);
1746       }
1747 
1748       /* Remove last line break, as mandated by tests. This is not affected by
1749          CR/LF counters, since RTF streaming presents only \para tokens, which
1750          are converted according to the standard rules: \r for 2.0, \r\n for 1.0
1751        */
1752       if (stripLastCR && !(format & SFF_SELECTION)) {
1753         int newto;
1754         ME_GetSelection(editor, &selStart, &selEnd);
1755         newto = ME_GetCursorOfs(selEnd);
1756         if (newto > to + (editor->bEmulateVersion10 ? 1 : 0)) {
1757           WCHAR lastchar[3] = {'\0', '\0'};
1758           int linebreakSize = editor->bEmulateVersion10 ? 2 : 1;
1759           ME_Cursor linebreakCursor = *selEnd, lastcharCursor = *selEnd;
1760           CHARFORMAT2W cf;
1761 
1762           /* Set the final eop to the char fmt of the last char */
1763           cf.cbSize = sizeof(cf);
1764           cf.dwMask = CFM_ALL2;
1765           ME_MoveCursorChars(editor, &lastcharCursor, -1, FALSE);
1766           ME_GetCharFormat(editor, &lastcharCursor, &linebreakCursor, &cf);
1767           set_selection_cursors(editor, newto, -1);
1768           ME_SetSelectionCharFormat(editor, &cf);
1769           set_selection_cursors(editor, newto, newto);
1770 
1771           ME_MoveCursorChars(editor, &linebreakCursor, -linebreakSize, FALSE);
1772           ME_GetTextW(editor, lastchar, 2, &linebreakCursor, linebreakSize, FALSE, FALSE);
1773           if (lastchar[0] == '\r' && (lastchar[1] == '\n' || lastchar[1] == '\0')) {
1774             ME_InternalDeleteText(editor, &linebreakCursor, linebreakSize, FALSE);
1775           }
1776         }
1777       }
1778       to = ME_GetCursorOfs(&editor->pCursors[0]);
1779       num_read = to - from;
1780 
1781       style = parser.style;
1782     }
1783     else if (format & SF_TEXT)
1784     {
1785       num_read = ME_StreamInText(editor, format, &inStream, style);
1786       to = ME_GetCursorOfs(&editor->pCursors[0]);
1787     }
1788     else
1789       ERR("EM_STREAMIN without SF_TEXT or SF_RTF\n");
1790     /* put the cursor at the top */
1791     if (!(format & SFF_SELECTION))
1792       set_selection_cursors(editor, 0, 0);
1793     ME_CursorFromCharOfs(editor, from, &start);
1794     ME_UpdateLinkAttribute(editor, &start, to - from);
1795   }
1796 
1797   /* Restore saved undo mode */
1798   editor->nUndoMode = nUndoMode;
1799 
1800   /* even if we didn't add an undo, we need to commit anything on the stack */
1801   ME_CommitUndo(editor);
1802 
1803   /* If SFF_SELECTION isn't set, delete any undos from before we started too */
1804   if (!(format & SFF_SELECTION))
1805     ME_EmptyUndoStack(editor);
1806 
1807   ME_ReleaseStyle(style);
1808   editor->nEventMask = nEventMask;
1809   ME_UpdateRepaint(editor, FALSE);
1810   if (!(format & SFF_SELECTION)) {
1811     ME_ClearTempStyle(editor);
1812   }
1813   update_caret(editor);
1814   ME_SendSelChange(editor);
1815   ME_SendRequestResize(editor, FALSE);
1816 
1817   return num_read;
1818 }
1819 
1820 
1821 typedef struct tagME_RTFStringStreamStruct
1822 {
1823   char *string;
1824   int pos;
1825   int length;
1826 } ME_RTFStringStreamStruct;
1827 
ME_ReadFromRTFString(DWORD_PTR dwCookie,LPBYTE lpBuff,LONG cb,LONG * pcb)1828 static DWORD CALLBACK ME_ReadFromRTFString(DWORD_PTR dwCookie, LPBYTE lpBuff, LONG cb, LONG *pcb)
1829 {
1830   ME_RTFStringStreamStruct *pStruct = (ME_RTFStringStreamStruct *)dwCookie;
1831   int count;
1832 
1833   count = min(cb, pStruct->length - pStruct->pos);
1834   memmove(lpBuff, pStruct->string + pStruct->pos, count);
1835   pStruct->pos += count;
1836   *pcb = count;
1837   return 0;
1838 }
1839 
1840 static void
ME_StreamInRTFString(ME_TextEditor * editor,BOOL selection,char * string)1841 ME_StreamInRTFString(ME_TextEditor *editor, BOOL selection, char *string)
1842 {
1843   EDITSTREAM es;
1844   ME_RTFStringStreamStruct data;
1845 
1846   data.string = string;
1847   data.length = strlen(string);
1848   data.pos = 0;
1849   es.dwCookie = (DWORD_PTR)&data;
1850   es.pfnCallback = ME_ReadFromRTFString;
1851   ME_StreamIn(editor, SF_RTF | (selection ? SFF_SELECTION : 0), &es, TRUE);
1852 }
1853 
1854 
1855 static int
ME_FindText(ME_TextEditor * editor,DWORD flags,const CHARRANGE * chrg,const WCHAR * text,CHARRANGE * chrgText)1856 ME_FindText(ME_TextEditor *editor, DWORD flags, const CHARRANGE *chrg, const WCHAR *text, CHARRANGE *chrgText)
1857 {
1858   const int nLen = lstrlenW(text);
1859   const int nTextLen = ME_GetTextLength(editor);
1860   int nMin, nMax;
1861   ME_Cursor cursor;
1862   WCHAR wLastChar = ' ';
1863 
1864   TRACE("flags==0x%08x, chrg->cpMin==%d, chrg->cpMax==%d text==%s\n",
1865         flags, chrg->cpMin, chrg->cpMax, debugstr_w(text));
1866 
1867   if (flags & ~(FR_DOWN | FR_MATCHCASE | FR_WHOLEWORD))
1868     FIXME("Flags 0x%08x not implemented\n",
1869         flags & ~(FR_DOWN | FR_MATCHCASE | FR_WHOLEWORD));
1870 
1871   nMin = chrg->cpMin;
1872   if (chrg->cpMax == -1)
1873     nMax = nTextLen;
1874   else
1875     nMax = chrg->cpMax > nTextLen ? nTextLen : chrg->cpMax;
1876 
1877   /* In 1.0 emulation, if cpMax reaches end of text, add the FR_DOWN flag */
1878   if (editor->bEmulateVersion10 && nMax == nTextLen)
1879   {
1880     flags |= FR_DOWN;
1881   }
1882 
1883   /* In 1.0 emulation, cpMin must always be no greater than cpMax */
1884   if (editor->bEmulateVersion10 && nMax < nMin)
1885   {
1886     if (chrgText)
1887     {
1888       chrgText->cpMin = -1;
1889       chrgText->cpMax = -1;
1890     }
1891     return -1;
1892   }
1893 
1894   /* when searching up, if cpMin < cpMax, then instead of searching
1895    * on [cpMin,cpMax], we search on [0,cpMin], otherwise, search on
1896    * [cpMax, cpMin]. The exception is when cpMax is -1, in which
1897    * case, it is always bigger than cpMin.
1898    */
1899   if (!editor->bEmulateVersion10 && !(flags & FR_DOWN))
1900   {
1901     int nSwap = nMax;
1902 
1903     nMax = nMin > nTextLen ? nTextLen : nMin;
1904     if (nMin < nSwap || chrg->cpMax == -1)
1905       nMin = 0;
1906     else
1907       nMin = nSwap;
1908   }
1909 
1910   if (!nLen || nMin < 0 || nMax < 0 || nMax < nMin)
1911   {
1912     if (chrgText)
1913       chrgText->cpMin = chrgText->cpMax = -1;
1914     return -1;
1915   }
1916 
1917   if (flags & FR_DOWN) /* Forward search */
1918   {
1919     /* If possible, find the character before where the search starts */
1920     if ((flags & FR_WHOLEWORD) && nMin)
1921     {
1922       ME_CursorFromCharOfs(editor, nMin - 1, &cursor);
1923       wLastChar = *get_text( &cursor.pRun->member.run, cursor.nOffset );
1924       ME_MoveCursorChars(editor, &cursor, 1, FALSE);
1925     } else {
1926       ME_CursorFromCharOfs(editor, nMin, &cursor);
1927     }
1928 
1929     while (cursor.pRun && ME_GetCursorOfs(&cursor) + nLen <= nMax)
1930     {
1931       ME_DisplayItem *pCurItem = cursor.pRun;
1932       int nCurStart = cursor.nOffset;
1933       int nMatched = 0;
1934 
1935       while (pCurItem && ME_CharCompare( *get_text( &pCurItem->member.run, nCurStart + nMatched ), text[nMatched], (flags & FR_MATCHCASE)))
1936       {
1937         if ((flags & FR_WHOLEWORD) && iswalnum(wLastChar))
1938           break;
1939 
1940         nMatched++;
1941         if (nMatched == nLen)
1942         {
1943           ME_DisplayItem *pNextItem = pCurItem;
1944           int nNextStart = nCurStart;
1945           WCHAR wNextChar;
1946 
1947           /* Check to see if next character is a whitespace */
1948           if (flags & FR_WHOLEWORD)
1949           {
1950             if (nCurStart + nMatched == pCurItem->member.run.len)
1951             {
1952               pNextItem = ME_FindItemFwd(pCurItem, diRun);
1953               nNextStart = -nMatched;
1954             }
1955 
1956             if (pNextItem)
1957               wNextChar = *get_text( &pNextItem->member.run, nNextStart + nMatched );
1958             else
1959               wNextChar = ' ';
1960 
1961             if (iswalnum(wNextChar))
1962               break;
1963           }
1964 
1965           cursor.nOffset += cursor.pPara->member.para.nCharOfs + cursor.pRun->member.run.nCharOfs;
1966           if (chrgText)
1967           {
1968             chrgText->cpMin = cursor.nOffset;
1969             chrgText->cpMax = cursor.nOffset + nLen;
1970           }
1971           TRACE("found at %d-%d\n", cursor.nOffset, cursor.nOffset + nLen);
1972           return cursor.nOffset;
1973         }
1974         if (nCurStart + nMatched == pCurItem->member.run.len)
1975         {
1976           pCurItem = ME_FindItemFwd(pCurItem, diRun);
1977           nCurStart = -nMatched;
1978         }
1979       }
1980       if (pCurItem)
1981         wLastChar = *get_text( &pCurItem->member.run, nCurStart + nMatched );
1982       else
1983         wLastChar = ' ';
1984 
1985       cursor.nOffset++;
1986       if (cursor.nOffset == cursor.pRun->member.run.len)
1987       {
1988         ME_NextRun(&cursor.pPara, &cursor.pRun, TRUE);
1989         cursor.nOffset = 0;
1990       }
1991     }
1992   }
1993   else /* Backward search */
1994   {
1995     /* If possible, find the character after where the search ends */
1996     if ((flags & FR_WHOLEWORD) && nMax < nTextLen - 1)
1997     {
1998       ME_CursorFromCharOfs(editor, nMax + 1, &cursor);
1999       wLastChar = *get_text( &cursor.pRun->member.run, cursor.nOffset );
2000       ME_MoveCursorChars(editor, &cursor, -1, FALSE);
2001     } else {
2002       ME_CursorFromCharOfs(editor, nMax, &cursor);
2003     }
2004 
2005     while (cursor.pRun && ME_GetCursorOfs(&cursor) - nLen >= nMin)
2006     {
2007       ME_DisplayItem *pCurItem = cursor.pRun;
2008       ME_DisplayItem *pCurPara = cursor.pPara;
2009       int nCurEnd = cursor.nOffset;
2010       int nMatched = 0;
2011 
2012       if (nCurEnd == 0)
2013       {
2014         ME_PrevRun(&pCurPara, &pCurItem, TRUE);
2015         nCurEnd = pCurItem->member.run.len;
2016       }
2017 
2018       while (pCurItem && ME_CharCompare( *get_text( &pCurItem->member.run, nCurEnd - nMatched - 1 ),
2019                                          text[nLen - nMatched - 1], (flags & FR_MATCHCASE) ))
2020       {
2021         if ((flags & FR_WHOLEWORD) && iswalnum(wLastChar))
2022           break;
2023 
2024         nMatched++;
2025         if (nMatched == nLen)
2026         {
2027           ME_DisplayItem *pPrevItem = pCurItem;
2028           int nPrevEnd = nCurEnd;
2029           WCHAR wPrevChar;
2030           int nStart;
2031 
2032           /* Check to see if previous character is a whitespace */
2033           if (flags & FR_WHOLEWORD)
2034           {
2035             if (nPrevEnd - nMatched == 0)
2036             {
2037               pPrevItem = ME_FindItemBack(pCurItem, diRun);
2038               if (pPrevItem)
2039                 nPrevEnd = pPrevItem->member.run.len + nMatched;
2040             }
2041 
2042             if (pPrevItem)
2043               wPrevChar = *get_text( &pPrevItem->member.run, nPrevEnd - nMatched - 1 );
2044             else
2045               wPrevChar = ' ';
2046 
2047             if (iswalnum(wPrevChar))
2048               break;
2049           }
2050 
2051           nStart = pCurPara->member.para.nCharOfs
2052                    + pCurItem->member.run.nCharOfs + nCurEnd - nMatched;
2053           if (chrgText)
2054           {
2055             chrgText->cpMin = nStart;
2056             chrgText->cpMax = nStart + nLen;
2057           }
2058           TRACE("found at %d-%d\n", nStart, nStart + nLen);
2059           return nStart;
2060         }
2061         if (nCurEnd - nMatched == 0)
2062         {
2063           ME_PrevRun(&pCurPara, &pCurItem, TRUE);
2064           /* Don't care about pCurItem becoming NULL here; it's already taken
2065            * care of in the exterior loop condition */
2066           nCurEnd = pCurItem->member.run.len + nMatched;
2067         }
2068       }
2069       if (pCurItem)
2070         wLastChar = *get_text( &pCurItem->member.run, nCurEnd - nMatched - 1 );
2071       else
2072         wLastChar = ' ';
2073 
2074       cursor.nOffset--;
2075       if (cursor.nOffset < 0)
2076       {
2077         ME_PrevRun(&cursor.pPara, &cursor.pRun, TRUE);
2078         cursor.nOffset = cursor.pRun->member.run.len;
2079       }
2080     }
2081   }
2082   TRACE("not found\n");
2083   if (chrgText)
2084     chrgText->cpMin = chrgText->cpMax = -1;
2085   return -1;
2086 }
2087 
ME_GetTextEx(ME_TextEditor * editor,GETTEXTEX * ex,LPARAM pText)2088 static int ME_GetTextEx(ME_TextEditor *editor, GETTEXTEX *ex, LPARAM pText)
2089 {
2090     int nChars;
2091     ME_Cursor start;
2092 
2093     if (!ex->cb || !pText) return 0;
2094 
2095     if (ex->flags & ~(GT_SELECTION | GT_USECRLF))
2096       FIXME("GETTEXTEX flags 0x%08x not supported\n", ex->flags & ~(GT_SELECTION | GT_USECRLF));
2097 
2098     if (ex->flags & GT_SELECTION)
2099     {
2100       int from, to;
2101       int nStartCur = ME_GetSelectionOfs(editor, &from, &to);
2102       start = editor->pCursors[nStartCur];
2103       nChars = to - from;
2104     }
2105     else
2106     {
2107       ME_SetCursorToStart(editor, &start);
2108       nChars = INT_MAX;
2109     }
2110     if (ex->codepage == CP_UNICODE)
2111     {
2112       return ME_GetTextW(editor, (LPWSTR)pText, ex->cb / sizeof(WCHAR) - 1,
2113                          &start, nChars, ex->flags & GT_USECRLF, FALSE);
2114     }
2115     else
2116     {
2117       /* potentially each char may be a CR, why calculate the exact value with O(N) when
2118         we can just take a bigger buffer? :)
2119         The above assumption still holds with CR/LF counters, since CR->CRLF expansion
2120         occurs only in richedit 2.0 mode, in which line breaks have only one CR
2121        */
2122       int crlfmul = (ex->flags & GT_USECRLF) ? 2 : 1;
2123       DWORD buflen;
2124       LPWSTR buffer;
2125       LRESULT rc;
2126 
2127       buflen = min(crlfmul * nChars, ex->cb - 1);
2128       buffer = heap_alloc((buflen + 1) * sizeof(WCHAR));
2129 
2130       nChars = ME_GetTextW(editor, buffer, buflen, &start, nChars, ex->flags & GT_USECRLF, FALSE);
2131       rc = WideCharToMultiByte(ex->codepage, 0, buffer, nChars + 1,
2132                                (LPSTR)pText, ex->cb, ex->lpDefaultChar, ex->lpUsedDefChar);
2133       if (rc) rc--; /* do not count 0 terminator */
2134 
2135       heap_free(buffer);
2136       return rc;
2137     }
2138 }
2139 
ME_GetTextRange(ME_TextEditor * editor,WCHAR * strText,const ME_Cursor * start,int nLen,BOOL unicode)2140 static int ME_GetTextRange(ME_TextEditor *editor, WCHAR *strText,
2141                            const ME_Cursor *start, int nLen, BOOL unicode)
2142 {
2143     if (!strText) return 0;
2144     if (unicode) {
2145       return ME_GetTextW(editor, strText, INT_MAX, start, nLen, FALSE, FALSE);
2146     } else {
2147       int nChars;
2148       WCHAR *p = heap_alloc((nLen+1) * sizeof(*p));
2149       if (!p) return 0;
2150       nChars = ME_GetTextW(editor, p, nLen, start, nLen, FALSE, FALSE);
2151       WideCharToMultiByte(CP_ACP, 0, p, nChars+1, (char *)strText,
2152                           nLen+1, NULL, NULL);
2153       heap_free(p);
2154       return nChars;
2155     }
2156 }
2157 
set_selection(ME_TextEditor * editor,int to,int from)2158 int set_selection( ME_TextEditor *editor, int to, int from )
2159 {
2160     int end;
2161 
2162     TRACE("%d - %d\n", to, from );
2163 
2164     if (!editor->bHideSelection) ME_InvalidateSelection( editor );
2165     end = set_selection_cursors( editor, to, from );
2166     if (!editor->bHideSelection) ME_InvalidateSelection( editor );
2167     update_caret( editor );
2168     ME_SendSelChange( editor );
2169 
2170     return end;
2171 }
2172 
2173 typedef struct tagME_GlobalDestStruct
2174 {
2175   HGLOBAL hData;
2176   int nLength;
2177 } ME_GlobalDestStruct;
2178 
ME_ReadFromHGLOBALUnicode(DWORD_PTR dwCookie,LPBYTE lpBuff,LONG cb,LONG * pcb)2179 static DWORD CALLBACK ME_ReadFromHGLOBALUnicode(DWORD_PTR dwCookie, LPBYTE lpBuff, LONG cb, LONG *pcb)
2180 {
2181   ME_GlobalDestStruct *pData = (ME_GlobalDestStruct *)dwCookie;
2182   int i;
2183   WORD *pSrc, *pDest;
2184 
2185   cb = cb >> 1;
2186   pDest = (WORD *)lpBuff;
2187   pSrc = GlobalLock(pData->hData);
2188   for (i = 0; i<cb && pSrc[pData->nLength+i]; i++) {
2189     pDest[i] = pSrc[pData->nLength+i];
2190   }
2191   pData->nLength += i;
2192   *pcb = 2*i;
2193   GlobalUnlock(pData->hData);
2194   return 0;
2195 }
2196 
ME_ReadFromHGLOBALRTF(DWORD_PTR dwCookie,LPBYTE lpBuff,LONG cb,LONG * pcb)2197 static DWORD CALLBACK ME_ReadFromHGLOBALRTF(DWORD_PTR dwCookie, LPBYTE lpBuff, LONG cb, LONG *pcb)
2198 {
2199   ME_GlobalDestStruct *pData = (ME_GlobalDestStruct *)dwCookie;
2200   int i;
2201   BYTE *pSrc, *pDest;
2202 
2203   pDest = lpBuff;
2204   pSrc = GlobalLock(pData->hData);
2205   for (i = 0; i<cb && pSrc[pData->nLength+i]; i++) {
2206     pDest[i] = pSrc[pData->nLength+i];
2207   }
2208   pData->nLength += i;
2209   *pcb = i;
2210   GlobalUnlock(pData->hData);
2211   return 0;
2212 }
2213 
2214 static const WCHAR rtfW[] = {'R','i','c','h',' ','T','e','x','t',' ','F','o','r','m','a','t',0};
2215 
paste_rtf(ME_TextEditor * editor,FORMATETC * fmt,STGMEDIUM * med)2216 static HRESULT paste_rtf(ME_TextEditor *editor, FORMATETC *fmt, STGMEDIUM *med)
2217 {
2218     EDITSTREAM es;
2219     ME_GlobalDestStruct gds;
2220     HRESULT hr;
2221 
2222     gds.hData = med->u.hGlobal;
2223     gds.nLength = 0;
2224     es.dwCookie = (DWORD_PTR)&gds;
2225     es.pfnCallback = ME_ReadFromHGLOBALRTF;
2226     hr = ME_StreamIn( editor, SF_RTF | SFF_SELECTION, &es, FALSE ) == 0 ? E_FAIL : S_OK;
2227     ReleaseStgMedium( med );
2228     return hr;
2229 }
2230 
paste_text(ME_TextEditor * editor,FORMATETC * fmt,STGMEDIUM * med)2231 static HRESULT paste_text(ME_TextEditor *editor, FORMATETC *fmt, STGMEDIUM *med)
2232 {
2233     EDITSTREAM es;
2234     ME_GlobalDestStruct gds;
2235     HRESULT hr;
2236 
2237     gds.hData = med->u.hGlobal;
2238     gds.nLength = 0;
2239     es.dwCookie = (DWORD_PTR)&gds;
2240     es.pfnCallback = ME_ReadFromHGLOBALUnicode;
2241     hr = ME_StreamIn( editor, SF_TEXT | SF_UNICODE | SFF_SELECTION, &es, FALSE ) == 0 ? E_FAIL : S_OK;
2242     ReleaseStgMedium( med );
2243     return hr;
2244 }
2245 
paste_emf(ME_TextEditor * editor,FORMATETC * fmt,STGMEDIUM * med)2246 static HRESULT paste_emf(ME_TextEditor *editor, FORMATETC *fmt, STGMEDIUM *med)
2247 {
2248     HRESULT hr;
2249     SIZEL sz = {0, 0};
2250 
2251     hr = insert_static_object( editor, med->u.hEnhMetaFile, NULL, &sz );
2252     if (SUCCEEDED(hr))
2253     {
2254         ME_CommitUndo( editor );
2255         ME_UpdateRepaint( editor, FALSE );
2256     }
2257     else
2258         ReleaseStgMedium( med );
2259 
2260     return hr;
2261 }
2262 
2263 static struct paste_format
2264 {
2265     FORMATETC fmt;
2266     HRESULT (*paste)(ME_TextEditor *, FORMATETC *, STGMEDIUM *);
2267     const WCHAR *name;
2268 } paste_formats[] =
2269 {
2270     {{ -1,             NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }, paste_rtf, rtfW },
2271     {{ CF_UNICODETEXT, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }, paste_text },
2272     {{ CF_ENHMETAFILE, NULL, DVASPECT_CONTENT, -1, TYMED_ENHMF },   paste_emf },
2273     {{ 0 }}
2274 };
2275 
init_paste_formats(void)2276 static void init_paste_formats(void)
2277 {
2278     struct paste_format *format;
2279     static int done;
2280 
2281     if (!done)
2282     {
2283         for (format = paste_formats; format->fmt.cfFormat; format++)
2284         {
2285             if (format->name)
2286                 format->fmt.cfFormat = RegisterClipboardFormatW( format->name );
2287         }
2288         done = 1;
2289     }
2290 }
2291 
paste_special(ME_TextEditor * editor,UINT cf,REPASTESPECIAL * ps,BOOL check_only)2292 static BOOL paste_special(ME_TextEditor *editor, UINT cf, REPASTESPECIAL *ps, BOOL check_only)
2293 {
2294     HRESULT hr;
2295     STGMEDIUM med;
2296     struct paste_format *format;
2297     IDataObject *data;
2298 
2299     /* Protect read-only edit control from modification */
2300     if (editor->styleFlags & ES_READONLY)
2301     {
2302         if (!check_only)
2303             MessageBeep(MB_ICONERROR);
2304         return FALSE;
2305     }
2306 
2307     init_paste_formats();
2308 
2309     if (ps && ps->dwAspect != DVASPECT_CONTENT)
2310         FIXME("Ignoring aspect %x\n", ps->dwAspect);
2311 
2312     hr = OleGetClipboard( &data );
2313     if (hr != S_OK) return FALSE;
2314 
2315     if (cf == CF_TEXT) cf = CF_UNICODETEXT;
2316 
2317     hr = S_FALSE;
2318     for (format = paste_formats; format->fmt.cfFormat; format++)
2319     {
2320         if (cf && cf != format->fmt.cfFormat) continue;
2321         hr = IDataObject_QueryGetData( data, &format->fmt );
2322         if (hr == S_OK)
2323         {
2324             if (!check_only)
2325             {
2326                 hr = IDataObject_GetData( data, &format->fmt, &med );
2327                 if (hr != S_OK) goto done;
2328                 hr = format->paste( editor, &format->fmt, &med );
2329             }
2330             break;
2331         }
2332     }
2333 
2334 done:
2335     IDataObject_Release( data );
2336 
2337     return hr == S_OK;
2338 }
2339 
ME_Copy(ME_TextEditor * editor,const ME_Cursor * start,int nChars)2340 static BOOL ME_Copy(ME_TextEditor *editor, const ME_Cursor *start, int nChars)
2341 {
2342   LPDATAOBJECT dataObj = NULL;
2343   HRESULT hr = S_OK;
2344 
2345   if (editor->cPasswordMask)
2346     return FALSE; /* Copying or Cutting masked text isn't allowed */
2347 
2348   if(editor->lpOleCallback)
2349   {
2350     CHARRANGE range;
2351     range.cpMin = ME_GetCursorOfs(start);
2352     range.cpMax = range.cpMin + nChars;
2353     hr = IRichEditOleCallback_GetClipboardData(editor->lpOleCallback, &range, RECO_COPY, &dataObj);
2354   }
2355   if(FAILED(hr) || !dataObj)
2356     hr = ME_GetDataObject(editor, start, nChars, &dataObj);
2357   if(SUCCEEDED(hr)) {
2358     hr = OleSetClipboard(dataObj);
2359     IDataObject_Release(dataObj);
2360   }
2361   return SUCCEEDED(hr);
2362 }
2363 
copy_or_cut(ME_TextEditor * editor,BOOL cut)2364 static BOOL copy_or_cut(ME_TextEditor *editor, BOOL cut)
2365 {
2366     BOOL result;
2367     int offs, num_chars;
2368     int start_cursor = ME_GetSelectionOfs(editor, &offs, &num_chars);
2369     ME_Cursor *sel_start = &editor->pCursors[start_cursor];
2370 
2371     if (cut && (editor->styleFlags & ES_READONLY))
2372     {
2373         MessageBeep(MB_ICONERROR);
2374         return FALSE;
2375     }
2376 
2377     num_chars -= offs;
2378     result = ME_Copy(editor, sel_start, num_chars);
2379     if (result && cut)
2380     {
2381         ME_InternalDeleteText(editor, sel_start, num_chars, FALSE);
2382         ME_CommitUndo(editor);
2383         ME_UpdateRepaint(editor, TRUE);
2384     }
2385     return result;
2386 }
2387 
2388 /* helper to send a msg filter notification */
2389 static BOOL
ME_FilterEvent(ME_TextEditor * editor,UINT msg,WPARAM * wParam,LPARAM * lParam)2390 ME_FilterEvent(ME_TextEditor *editor, UINT msg, WPARAM* wParam, LPARAM* lParam)
2391 {
2392     MSGFILTER msgf;
2393 
2394     if (!editor->hWnd || !editor->hwndParent) return FALSE;
2395     msgf.nmhdr.hwndFrom = editor->hWnd;
2396     msgf.nmhdr.idFrom = GetWindowLongW(editor->hWnd, GWLP_ID);
2397     msgf.nmhdr.code = EN_MSGFILTER;
2398     msgf.msg = msg;
2399     msgf.wParam = *wParam;
2400     msgf.lParam = *lParam;
2401     if (SendMessageW(editor->hwndParent, WM_NOTIFY, msgf.nmhdr.idFrom, (LPARAM)&msgf))
2402         return FALSE;
2403     *wParam = msgf.wParam;
2404     *lParam = msgf.lParam;
2405     msgf.wParam = *wParam;
2406 
2407     return TRUE;
2408 }
2409 
ME_UpdateSelectionLinkAttribute(ME_TextEditor * editor)2410 static void ME_UpdateSelectionLinkAttribute(ME_TextEditor *editor)
2411 {
2412   ME_DisplayItem *startPara, *endPara;
2413   ME_DisplayItem *prev_para;
2414   ME_Cursor *from, *to;
2415   ME_Cursor start;
2416   int nChars;
2417 
2418   if (!editor->AutoURLDetect_bEnable) return;
2419 
2420   ME_GetSelection(editor, &from, &to);
2421 
2422   /* Find paragraph previous to the one that contains start cursor */
2423   startPara = from->pPara;
2424   prev_para = startPara->member.para.prev_para;
2425   if (prev_para->type == diParagraph) startPara = prev_para;
2426 
2427   /* Find paragraph that contains end cursor */
2428   endPara = to->pPara->member.para.next_para;
2429 
2430   start.pPara = startPara;
2431   start.pRun = ME_FindItemFwd(startPara, diRun);
2432   start.nOffset = 0;
2433   nChars = endPara->member.para.nCharOfs - startPara->member.para.nCharOfs;
2434 
2435   ME_UpdateLinkAttribute(editor, &start, nChars);
2436 }
2437 
handle_enter(ME_TextEditor * editor)2438 static BOOL handle_enter(ME_TextEditor *editor)
2439 {
2440     BOOL ctrl_is_down = GetKeyState(VK_CONTROL) & 0x8000;
2441     BOOL shift_is_down = GetKeyState(VK_SHIFT) & 0x8000;
2442 
2443     if (editor->bDialogMode)
2444     {
2445         if (ctrl_is_down)
2446             return TRUE;
2447 
2448         if (!(editor->styleFlags & ES_WANTRETURN))
2449         {
2450             if (editor->hwndParent)
2451             {
2452                 DWORD dw;
2453                 dw = SendMessageW(editor->hwndParent, DM_GETDEFID, 0, 0);
2454                 if (HIWORD(dw) == DC_HASDEFID)
2455                 {
2456                     HWND hwDefCtrl = GetDlgItem(editor->hwndParent, LOWORD(dw));
2457                     if (hwDefCtrl)
2458                     {
2459                         SendMessageW(editor->hwndParent, WM_NEXTDLGCTL, (WPARAM)hwDefCtrl, TRUE);
2460                         PostMessageW(hwDefCtrl, WM_KEYDOWN, VK_RETURN, 0);
2461                     }
2462                 }
2463             }
2464             return TRUE;
2465         }
2466     }
2467 
2468     if (editor->styleFlags & ES_MULTILINE)
2469     {
2470         static const WCHAR endl = '\r';
2471         static const WCHAR endlv10[] = {'\r','\n'};
2472         ME_Cursor cursor = editor->pCursors[0];
2473         ME_DisplayItem *para = cursor.pPara;
2474         int from, to;
2475         ME_Style *style, *eop_style;
2476 
2477         if (editor->styleFlags & ES_READONLY)
2478         {
2479             MessageBeep(MB_ICONERROR);
2480             return TRUE;
2481         }
2482 
2483         ME_GetSelectionOfs(editor, &from, &to);
2484         if (editor->nTextLimit > ME_GetTextLength(editor) - (to-from))
2485         {
2486             if (!editor->bEmulateVersion10) /* v4.1 */
2487             {
2488                 if (para->member.para.nFlags & MEPF_ROWEND)
2489                 {
2490                     /* Add a new table row after this row. */
2491                     para = ME_AppendTableRow(editor, para);
2492                     para = para->member.para.next_para;
2493                     editor->pCursors[0].pPara = para;
2494                     editor->pCursors[0].pRun = ME_FindItemFwd(para, diRun);
2495                     editor->pCursors[0].nOffset = 0;
2496                     editor->pCursors[1] = editor->pCursors[0];
2497                     ME_CommitUndo(editor);
2498                     ME_CheckTablesForCorruption(editor);
2499                     ME_UpdateRepaint(editor, FALSE);
2500                     return TRUE;
2501                 }
2502                 else if (para == editor->pCursors[1].pPara &&
2503                     cursor.nOffset + cursor.pRun->member.run.nCharOfs == 0 &&
2504                     para->member.para.prev_para->member.para.nFlags & MEPF_ROWSTART &&
2505                     !para->member.para.prev_para->member.para.nCharOfs)
2506                 {
2507                     /* Insert a newline before the table. */
2508                     para = para->member.para.prev_para;
2509                     para->member.para.nFlags &= ~MEPF_ROWSTART;
2510                     editor->pCursors[0].pPara = para;
2511                     editor->pCursors[0].pRun = ME_FindItemFwd(para, diRun);
2512                     editor->pCursors[1] = editor->pCursors[0];
2513                     ME_InsertTextFromCursor(editor, 0, &endl, 1,
2514                     editor->pCursors[0].pRun->member.run.style);
2515                     para = editor->pBuffer->pFirst->member.para.next_para;
2516                     ME_SetDefaultParaFormat(editor, &para->member.para.fmt);
2517                     para->member.para.nFlags = 0;
2518                     mark_para_rewrap(editor, para);
2519                     editor->pCursors[0].pPara = para;
2520                     editor->pCursors[0].pRun = ME_FindItemFwd(para, diRun);
2521                     editor->pCursors[1] = editor->pCursors[0];
2522                     para->member.para.next_para->member.para.nFlags |= MEPF_ROWSTART;
2523                     ME_CommitCoalescingUndo(editor);
2524                     ME_CheckTablesForCorruption(editor);
2525                     ME_UpdateRepaint(editor, FALSE);
2526                     return TRUE;
2527                 }
2528             }
2529             else /* v1.0 - 3.0 */
2530             {
2531                 ME_DisplayItem *para = cursor.pPara;
2532                 if (ME_IsInTable(para))
2533                 {
2534                     if (cursor.pRun->member.run.nFlags & MERF_ENDPARA)
2535                     {
2536                         if (from == to)
2537                         {
2538                             ME_ContinueCoalescingTransaction(editor);
2539                             para = ME_AppendTableRow(editor, para);
2540                             editor->pCursors[0].pPara = para;
2541                             editor->pCursors[0].pRun = ME_FindItemFwd(para, diRun);
2542                             editor->pCursors[0].nOffset = 0;
2543                             editor->pCursors[1] = editor->pCursors[0];
2544                             ME_CommitCoalescingUndo(editor);
2545                             ME_UpdateRepaint(editor, FALSE);
2546                             return TRUE;
2547                         }
2548                     }
2549                     else
2550                     {
2551                         ME_ContinueCoalescingTransaction(editor);
2552                         if (cursor.pRun->member.run.nCharOfs + cursor.nOffset == 0 &&
2553                                 !ME_IsInTable(para->member.para.prev_para))
2554                         {
2555                             /* Insert newline before table */
2556                             cursor.pRun = ME_FindItemBack(para, diRun);
2557                             if (cursor.pRun)
2558                             {
2559                                 editor->pCursors[0].pRun = cursor.pRun;
2560                                 editor->pCursors[0].pPara = para->member.para.prev_para;
2561                             }
2562                             editor->pCursors[0].nOffset = 0;
2563                             editor->pCursors[1] = editor->pCursors[0];
2564                             ME_InsertTextFromCursor(editor, 0, &endl, 1,
2565                             editor->pCursors[0].pRun->member.run.style);
2566                         }
2567                         else
2568                         {
2569                             editor->pCursors[1] = editor->pCursors[0];
2570                             para = ME_AppendTableRow(editor, para);
2571                             editor->pCursors[0].pPara = para;
2572                             editor->pCursors[0].pRun = ME_FindItemFwd(para, diRun);
2573                             editor->pCursors[0].nOffset = 0;
2574                             editor->pCursors[1] = editor->pCursors[0];
2575                         }
2576                         ME_CommitCoalescingUndo(editor);
2577                         ME_UpdateRepaint(editor, FALSE);
2578                         return TRUE;
2579                     }
2580                 }
2581             }
2582 
2583             style = ME_GetInsertStyle(editor, 0);
2584 
2585             /* Normally the new eop style is the insert style, however in a list it is copied from the existing
2586             eop style (this prevents the list label style changing when the new eop is inserted).
2587             No extra ref is taken here on eop_style. */
2588             if (para->member.para.fmt.wNumbering)
2589                 eop_style = para->member.para.eop_run->style;
2590             else
2591                 eop_style = style;
2592             ME_ContinueCoalescingTransaction(editor);
2593             if (shift_is_down)
2594                 ME_InsertEndRowFromCursor(editor, 0);
2595             else
2596                 if (!editor->bEmulateVersion10)
2597                     ME_InsertTextFromCursor(editor, 0, &endl, 1, eop_style);
2598                 else
2599                     ME_InsertTextFromCursor(editor, 0, endlv10, 2, eop_style);
2600             ME_CommitCoalescingUndo(editor);
2601             SetCursor(NULL);
2602 
2603             ME_UpdateSelectionLinkAttribute(editor);
2604             ME_UpdateRepaint(editor, FALSE);
2605             ME_SaveTempStyle(editor, style); /* set the temp insert style for the new para */
2606             ME_ReleaseStyle(style);
2607         }
2608         return TRUE;
2609     }
2610     return FALSE;
2611 }
2612 
2613 static BOOL
ME_KeyDown(ME_TextEditor * editor,WORD nKey)2614 ME_KeyDown(ME_TextEditor *editor, WORD nKey)
2615 {
2616   BOOL ctrl_is_down = GetKeyState(VK_CONTROL) & 0x8000;
2617   BOOL shift_is_down = GetKeyState(VK_SHIFT) & 0x8000;
2618 
2619   if (editor->bMouseCaptured)
2620       return FALSE;
2621   if (nKey != VK_SHIFT && nKey != VK_CONTROL && nKey != VK_MENU)
2622       editor->nSelectionType = stPosition;
2623 
2624   switch (nKey)
2625   {
2626     case VK_LEFT:
2627     case VK_RIGHT:
2628     case VK_HOME:
2629     case VK_END:
2630         editor->nUDArrowX = -1;
2631         /* fall through */
2632     case VK_UP:
2633     case VK_DOWN:
2634     case VK_PRIOR:
2635     case VK_NEXT:
2636       ME_CommitUndo(editor); /* End coalesced undos for typed characters */
2637       ME_ArrowKey(editor, nKey, shift_is_down, ctrl_is_down);
2638       return TRUE;
2639     case VK_BACK:
2640     case VK_DELETE:
2641       editor->nUDArrowX = -1;
2642       /* FIXME backspace and delete aren't the same, they act different wrt paragraph style of the merged paragraph */
2643       if (editor->styleFlags & ES_READONLY)
2644         return FALSE;
2645       if (ME_IsSelection(editor))
2646       {
2647         ME_DeleteSelection(editor);
2648         ME_CommitUndo(editor);
2649       }
2650       else if (nKey == VK_DELETE)
2651       {
2652         /* Delete stops group typing.
2653          * (See MSDN remarks on EM_STOPGROUPTYPING message) */
2654         ME_DeleteTextAtCursor(editor, 1, 1);
2655         ME_CommitUndo(editor);
2656       }
2657       else if (ME_ArrowKey(editor, VK_LEFT, FALSE, FALSE))
2658       {
2659         BOOL bDeletionSucceeded;
2660         /* Backspace can be grouped for a single undo */
2661         ME_ContinueCoalescingTransaction(editor);
2662         bDeletionSucceeded = ME_DeleteTextAtCursor(editor, 1, 1);
2663         if (!bDeletionSucceeded && !editor->bEmulateVersion10) { /* v4.1 */
2664           /* Deletion was prevented so the cursor is moved back to where it was.
2665            * (e.g. this happens when trying to delete cell boundaries)
2666            */
2667           ME_ArrowKey(editor, VK_RIGHT, FALSE, FALSE);
2668         }
2669         ME_CommitCoalescingUndo(editor);
2670       }
2671       else
2672         return TRUE;
2673       ME_MoveCursorFromTableRowStartParagraph(editor);
2674       ME_UpdateSelectionLinkAttribute(editor);
2675       ME_UpdateRepaint(editor, FALSE);
2676       ME_SendRequestResize(editor, FALSE);
2677       return TRUE;
2678     case VK_RETURN:
2679       if (!editor->bEmulateVersion10)
2680           return handle_enter(editor);
2681       break;
2682     case VK_ESCAPE:
2683       if (editor->bDialogMode && editor->hwndParent)
2684         PostMessageW(editor->hwndParent, WM_CLOSE, 0, 0);
2685       return TRUE;
2686     case VK_TAB:
2687       if (editor->bDialogMode && editor->hwndParent)
2688         SendMessageW(editor->hwndParent, WM_NEXTDLGCTL, shift_is_down, 0);
2689       return TRUE;
2690     case 'A':
2691       if (ctrl_is_down)
2692       {
2693         set_selection( editor, 0, -1 );
2694         return TRUE;
2695       }
2696       break;
2697     case 'V':
2698       if (ctrl_is_down)
2699         return paste_special( editor, 0, NULL, FALSE );
2700       break;
2701     case 'C':
2702     case 'X':
2703       if (ctrl_is_down)
2704         return copy_or_cut(editor, nKey == 'X');
2705       break;
2706     case 'Z':
2707       if (ctrl_is_down)
2708       {
2709         ME_Undo(editor);
2710         return TRUE;
2711       }
2712       break;
2713     case 'Y':
2714       if (ctrl_is_down)
2715       {
2716         ME_Redo(editor);
2717         return TRUE;
2718       }
2719       break;
2720 
2721     default:
2722       if (nKey != VK_SHIFT && nKey != VK_CONTROL && nKey && nKey != VK_MENU)
2723           editor->nUDArrowX = -1;
2724       if (ctrl_is_down)
2725       {
2726         if (nKey == 'W')
2727         {
2728           CHARFORMAT2W chf;
2729           char buf[2048];
2730           chf.cbSize = sizeof(chf);
2731 
2732           ME_GetSelectionCharFormat(editor, &chf);
2733           ME_DumpStyleToBuf(&chf, buf);
2734           MessageBoxA(NULL, buf, "Style dump", MB_OK);
2735         }
2736         if (nKey == 'Q')
2737         {
2738           ME_CheckCharOffsets(editor);
2739         }
2740       }
2741   }
2742   return FALSE;
2743 }
2744 
ME_Char(ME_TextEditor * editor,WPARAM charCode,LPARAM flags,BOOL unicode)2745 static LRESULT ME_Char(ME_TextEditor *editor, WPARAM charCode,
2746                        LPARAM flags, BOOL unicode)
2747 {
2748   WCHAR wstr;
2749 
2750   if (editor->bMouseCaptured)
2751     return 0;
2752 
2753   if (editor->styleFlags & ES_READONLY)
2754   {
2755     MessageBeep(MB_ICONERROR);
2756     return 0; /* FIXME really 0 ? */
2757   }
2758 
2759   if (unicode)
2760       wstr = (WCHAR)charCode;
2761   else
2762   {
2763       CHAR charA = charCode;
2764       MultiByteToWideChar(CP_ACP, 0, &charA, 1, &wstr, 1);
2765   }
2766 
2767   if (editor->bEmulateVersion10 && wstr == '\r')
2768       handle_enter(editor);
2769 
2770   if ((unsigned)wstr >= ' ' || wstr == '\t')
2771   {
2772     ME_Cursor cursor = editor->pCursors[0];
2773     ME_DisplayItem *para = cursor.pPara;
2774     int from, to;
2775     BOOL ctrl_is_down = GetKeyState(VK_CONTROL) & 0x8000;
2776     ME_GetSelectionOfs(editor, &from, &to);
2777     if (wstr == '\t' &&
2778         /* v4.1 allows tabs to be inserted with ctrl key down */
2779         !(ctrl_is_down && !editor->bEmulateVersion10))
2780     {
2781       ME_DisplayItem *para;
2782       BOOL bSelectedRow = FALSE;
2783 
2784       para = cursor.pPara;
2785       if (ME_IsSelection(editor) &&
2786           cursor.pRun->member.run.nCharOfs + cursor.nOffset == 0 &&
2787           to == ME_GetCursorOfs(&editor->pCursors[0]) &&
2788           para->member.para.prev_para->type == diParagraph)
2789       {
2790         para = para->member.para.prev_para;
2791         bSelectedRow = TRUE;
2792       }
2793       if (ME_IsInTable(para))
2794       {
2795         ME_TabPressedInTable(editor, bSelectedRow);
2796         ME_CommitUndo(editor);
2797         return 0;
2798       }
2799     } else if (!editor->bEmulateVersion10) { /* v4.1 */
2800       if (para->member.para.nFlags & MEPF_ROWEND) {
2801         if (from == to) {
2802           para = para->member.para.next_para;
2803           if (para->member.para.nFlags & MEPF_ROWSTART)
2804             para = para->member.para.next_para;
2805           editor->pCursors[0].pPara = para;
2806           editor->pCursors[0].pRun = ME_FindItemFwd(para, diRun);
2807           editor->pCursors[0].nOffset = 0;
2808           editor->pCursors[1] = editor->pCursors[0];
2809         }
2810       }
2811     } else { /* v1.0 - 3.0 */
2812       if (ME_IsInTable(cursor.pRun) &&
2813           cursor.pRun->member.run.nFlags & MERF_ENDPARA &&
2814           from == to)
2815       {
2816         /* Text should not be inserted at the end of the table. */
2817         MessageBeep(-1);
2818         return 0;
2819       }
2820     }
2821     /* FIXME maybe it would make sense to call EM_REPLACESEL instead ? */
2822     /* WM_CHAR is restricted to nTextLimit */
2823     if(editor->nTextLimit > ME_GetTextLength(editor) - (to-from))
2824     {
2825       ME_Style *style = ME_GetInsertStyle(editor, 0);
2826       ME_ContinueCoalescingTransaction(editor);
2827       ME_InsertTextFromCursor(editor, 0, &wstr, 1, style);
2828       ME_ReleaseStyle(style);
2829       ME_CommitCoalescingUndo(editor);
2830       ITextHost_TxSetCursor(editor->texthost, NULL, FALSE);
2831     }
2832 
2833     ME_UpdateSelectionLinkAttribute(editor);
2834     ME_UpdateRepaint(editor, FALSE);
2835   }
2836   return 0;
2837 }
2838 
2839 /* Process the message and calculate the new click count.
2840  *
2841  * returns: The click count if it is mouse down event, else returns 0. */
ME_CalculateClickCount(ME_TextEditor * editor,UINT msg,WPARAM wParam,LPARAM lParam)2842 static int ME_CalculateClickCount(ME_TextEditor *editor, UINT msg, WPARAM wParam,
2843                                   LPARAM lParam)
2844 {
2845     static int clickNum = 0;
2846     if (msg < WM_MOUSEFIRST || msg > WM_MOUSELAST)
2847         return 0;
2848 
2849     if ((msg == WM_LBUTTONDBLCLK) ||
2850         (msg == WM_RBUTTONDBLCLK) ||
2851         (msg == WM_MBUTTONDBLCLK) ||
2852         (msg == WM_XBUTTONDBLCLK))
2853     {
2854         msg -= (WM_LBUTTONDBLCLK - WM_LBUTTONDOWN);
2855     }
2856 
2857     if ((msg == WM_LBUTTONDOWN) ||
2858         (msg == WM_RBUTTONDOWN) ||
2859         (msg == WM_MBUTTONDOWN) ||
2860         (msg == WM_XBUTTONDOWN))
2861     {
2862         static MSG prevClickMsg;
2863         MSG clickMsg;
2864         /* Compare the editor instead of the hwnd so that the this
2865          * can still be done for windowless richedit controls. */
2866         clickMsg.hwnd = (HWND)editor;
2867         clickMsg.message = msg;
2868         clickMsg.wParam = wParam;
2869         clickMsg.lParam = lParam;
2870         clickMsg.time = GetMessageTime();
2871         clickMsg.pt.x = (short)LOWORD(lParam);
2872         clickMsg.pt.y = (short)HIWORD(lParam);
2873         if ((clickNum != 0) &&
2874             (clickMsg.message == prevClickMsg.message) &&
2875             (clickMsg.hwnd == prevClickMsg.hwnd) &&
2876             (clickMsg.wParam == prevClickMsg.wParam) &&
2877             (clickMsg.time - prevClickMsg.time < GetDoubleClickTime()) &&
2878             (abs(clickMsg.pt.x - prevClickMsg.pt.x) < GetSystemMetrics(SM_CXDOUBLECLK)/2) &&
2879             (abs(clickMsg.pt.y - prevClickMsg.pt.y) < GetSystemMetrics(SM_CYDOUBLECLK)/2))
2880         {
2881             clickNum++;
2882         } else {
2883             clickNum = 1;
2884         }
2885         prevClickMsg = clickMsg;
2886     } else {
2887         return 0;
2888     }
2889     return clickNum;
2890 }
2891 
is_link(ME_Run * run)2892 static BOOL is_link( ME_Run *run )
2893 {
2894     return (run->style->fmt.dwMask & CFM_LINK) && (run->style->fmt.dwEffects & CFE_LINK);
2895 }
2896 
ME_SetCursor(ME_TextEditor * editor)2897 static BOOL ME_SetCursor(ME_TextEditor *editor)
2898 {
2899   ME_Cursor cursor;
2900   POINT pt;
2901   BOOL isExact;
2902   SCROLLBARINFO sbi;
2903   DWORD messagePos = GetMessagePos();
2904   pt.x = (short)LOWORD(messagePos);
2905   pt.y = (short)HIWORD(messagePos);
2906 
2907   if (editor->hWnd)
2908   {
2909     sbi.cbSize = sizeof(sbi);
2910     GetScrollBarInfo(editor->hWnd, OBJID_HSCROLL, &sbi);
2911     if (!(sbi.rgstate[0] & (STATE_SYSTEM_INVISIBLE|STATE_SYSTEM_OFFSCREEN)) &&
2912         PtInRect(&sbi.rcScrollBar, pt))
2913     {
2914         ITextHost_TxSetCursor(editor->texthost,
2915                               LoadCursorW(NULL, (WCHAR*)IDC_ARROW), FALSE);
2916         return TRUE;
2917     }
2918     sbi.cbSize = sizeof(sbi);
2919     GetScrollBarInfo(editor->hWnd, OBJID_VSCROLL, &sbi);
2920     if (!(sbi.rgstate[0] & (STATE_SYSTEM_INVISIBLE|STATE_SYSTEM_OFFSCREEN)) &&
2921         PtInRect(&sbi.rcScrollBar, pt))
2922     {
2923         ITextHost_TxSetCursor(editor->texthost,
2924                               LoadCursorW(NULL, (WCHAR*)IDC_ARROW), FALSE);
2925         return TRUE;
2926     }
2927   }
2928   ITextHost_TxScreenToClient(editor->texthost, &pt);
2929 
2930   if (editor->nSelectionType == stLine && editor->bMouseCaptured) {
2931       ITextHost_TxSetCursor(editor->texthost, hLeft, FALSE);
2932       return TRUE;
2933   }
2934   if (!editor->bEmulateVersion10 /* v4.1 */ &&
2935       pt.y < editor->rcFormat.top &&
2936       pt.x < editor->rcFormat.left)
2937   {
2938       ITextHost_TxSetCursor(editor->texthost, hLeft, FALSE);
2939       return TRUE;
2940   }
2941   if (pt.y < editor->rcFormat.top || pt.y > editor->rcFormat.bottom)
2942   {
2943       if (editor->bEmulateVersion10) /* v1.0 - 3.0 */
2944           ITextHost_TxSetCursor(editor->texthost,
2945                                 LoadCursorW(NULL, (WCHAR*)IDC_ARROW), FALSE);
2946       else /* v4.1 */
2947           ITextHost_TxSetCursor(editor->texthost,
2948                                 LoadCursorW(NULL, (WCHAR*)IDC_IBEAM), TRUE);
2949       return TRUE;
2950   }
2951   if (pt.x < editor->rcFormat.left)
2952   {
2953       ITextHost_TxSetCursor(editor->texthost, hLeft, FALSE);
2954       return TRUE;
2955   }
2956   ME_CharFromPos(editor, pt.x, pt.y, &cursor, &isExact);
2957   if (isExact)
2958   {
2959       ME_Run *run;
2960 
2961       run = &cursor.pRun->member.run;
2962       if (is_link( run ))
2963       {
2964           ITextHost_TxSetCursor(editor->texthost,
2965                                 LoadCursorW(NULL, (WCHAR*)IDC_HAND),
2966                                 FALSE);
2967           return TRUE;
2968       }
2969 
2970       if (ME_IsSelection(editor))
2971       {
2972           int selStart, selEnd;
2973           int offset = ME_GetCursorOfs(&cursor);
2974 
2975           ME_GetSelectionOfs(editor, &selStart, &selEnd);
2976           if (selStart <= offset && selEnd >= offset) {
2977               ITextHost_TxSetCursor(editor->texthost,
2978                                     LoadCursorW(NULL, (WCHAR*)IDC_ARROW),
2979                                     FALSE);
2980               return TRUE;
2981           }
2982       }
2983   }
2984   ITextHost_TxSetCursor(editor->texthost,
2985                         LoadCursorW(NULL, (WCHAR*)IDC_IBEAM), TRUE);
2986   return TRUE;
2987 }
2988 
ME_SetDefaultFormatRect(ME_TextEditor * editor)2989 static void ME_SetDefaultFormatRect(ME_TextEditor *editor)
2990 {
2991   ITextHost_TxGetClientRect(editor->texthost, &editor->rcFormat);
2992   editor->rcFormat.top += editor->exStyleFlags & WS_EX_CLIENTEDGE ? 1 : 0;
2993   editor->rcFormat.left += 1 + editor->selofs;
2994   editor->rcFormat.right -= 1;
2995 }
2996 
ME_GetSelectionType(ME_TextEditor * editor)2997 static LONG ME_GetSelectionType(ME_TextEditor *editor)
2998 {
2999     LONG sel_type = SEL_EMPTY;
3000     LONG start, end;
3001 
3002     ME_GetSelectionOfs(editor, &start, &end);
3003     if (start == end)
3004         sel_type = SEL_EMPTY;
3005     else
3006     {
3007         LONG object_count = 0, character_count = 0;
3008         int i;
3009 
3010         for (i = 0; i < end - start; i++)
3011         {
3012             ME_Cursor cursor;
3013 
3014             ME_CursorFromCharOfs(editor, start + i, &cursor);
3015             if (cursor.pRun->member.run.reobj)
3016                 object_count++;
3017             else
3018                 character_count++;
3019             if (character_count >= 2 && object_count >= 2)
3020                 return (SEL_TEXT | SEL_MULTICHAR | SEL_OBJECT | SEL_MULTIOBJECT);
3021         }
3022         if (character_count)
3023         {
3024             sel_type |= SEL_TEXT;
3025             if (character_count >= 2)
3026                 sel_type |= SEL_MULTICHAR;
3027         }
3028         if (object_count)
3029         {
3030             sel_type |= SEL_OBJECT;
3031             if (object_count >= 2)
3032                 sel_type |= SEL_MULTIOBJECT;
3033         }
3034     }
3035     return sel_type;
3036 }
3037 
ME_ShowContextMenu(ME_TextEditor * editor,int x,int y)3038 static BOOL ME_ShowContextMenu(ME_TextEditor *editor, int x, int y)
3039 {
3040   CHARRANGE selrange;
3041   HMENU menu;
3042   int seltype;
3043 
3044   if(!editor->lpOleCallback || !editor->hWnd)
3045     return FALSE;
3046   ME_GetSelectionOfs(editor, &selrange.cpMin, &selrange.cpMax);
3047   seltype = ME_GetSelectionType(editor);
3048   if(SUCCEEDED(IRichEditOleCallback_GetContextMenu(editor->lpOleCallback, seltype, NULL, &selrange, &menu)))
3049   {
3050     TrackPopupMenu(menu, TPM_LEFTALIGN | TPM_RIGHTBUTTON, x, y, 0, editor->hwndParent, NULL);
3051     DestroyMenu(menu);
3052   }
3053   return TRUE;
3054 }
3055 
ME_MakeEditor(ITextHost * texthost,BOOL bEmulateVersion10)3056 ME_TextEditor *ME_MakeEditor(ITextHost *texthost, BOOL bEmulateVersion10)
3057 {
3058   ME_TextEditor *ed = heap_alloc(sizeof(*ed));
3059   int i;
3060   DWORD props;
3061   LONG selbarwidth;
3062 
3063   ed->hWnd = NULL;
3064   ed->hwndParent = NULL;
3065   ed->sizeWindow.cx = ed->sizeWindow.cy = 0;
3066   ed->texthost = texthost;
3067   ed->reOle = NULL;
3068   ed->bEmulateVersion10 = bEmulateVersion10;
3069   ed->styleFlags = 0;
3070   ed->exStyleFlags = 0;
3071   ed->first_marked_para = NULL;
3072   ed->total_rows = 0;
3073   ITextHost_TxGetPropertyBits(texthost,
3074                               (TXTBIT_RICHTEXT|TXTBIT_MULTILINE|
3075                                TXTBIT_READONLY|TXTBIT_USEPASSWORD|
3076                                TXTBIT_HIDESELECTION|TXTBIT_SAVESELECTION|
3077                                TXTBIT_AUTOWORDSEL|TXTBIT_VERTICAL|
3078                                TXTBIT_WORDWRAP|TXTBIT_DISABLEDRAG),
3079                               &props);
3080   ITextHost_TxGetScrollBars(texthost, &ed->styleFlags);
3081   ed->styleFlags &= (WS_VSCROLL|WS_HSCROLL|ES_AUTOVSCROLL|
3082                      ES_AUTOHSCROLL|ES_DISABLENOSCROLL);
3083   ed->pBuffer = ME_MakeText();
3084   ed->nZoomNumerator = ed->nZoomDenominator = 0;
3085   ed->nAvailWidth = 0; /* wrap to client area */
3086   list_init( &ed->style_list );
3087   ME_MakeFirstParagraph(ed);
3088   /* The four cursors are for:
3089    * 0 - The position where the caret is shown
3090    * 1 - The anchored end of the selection (for normal selection)
3091    * 2 & 3 - The anchored start and end respectively for word, line,
3092    * or paragraph selection.
3093    */
3094   ed->nCursors = 4;
3095   ed->pCursors = heap_alloc(ed->nCursors * sizeof(*ed->pCursors));
3096   ME_SetCursorToStart(ed, &ed->pCursors[0]);
3097   ed->pCursors[1] = ed->pCursors[0];
3098   ed->pCursors[2] = ed->pCursors[0];
3099   ed->pCursors[3] = ed->pCursors[1];
3100   ed->nLastTotalLength = ed->nTotalLength = 0;
3101   ed->nLastTotalWidth = ed->nTotalWidth = 0;
3102   ed->nUDArrowX = -1;
3103   ed->rgbBackColor = -1;
3104   ed->hbrBackground = GetSysColorBrush(COLOR_WINDOW);
3105   ed->bCaretAtEnd = FALSE;
3106   ed->nEventMask = 0;
3107   ed->nModifyStep = 0;
3108   ed->nTextLimit = TEXT_LIMIT_DEFAULT;
3109   list_init( &ed->undo_stack );
3110   list_init( &ed->redo_stack );
3111   ed->nUndoStackSize = 0;
3112   ed->nUndoLimit = STACK_SIZE_DEFAULT;
3113   ed->nUndoMode = umAddToUndo;
3114   ed->nParagraphs = 1;
3115   ed->nLastSelStart = ed->nLastSelEnd = 0;
3116   ed->pLastSelStartPara = ed->pLastSelEndPara = ed->pCursors[0].pPara;
3117   ed->bHideSelection = FALSE;
3118   ed->pfnWordBreak = NULL;
3119   ed->lpOleCallback = NULL;
3120   ed->mode = TM_MULTILEVELUNDO | TM_MULTICODEPAGE;
3121   ed->mode |= (props & TXTBIT_RICHTEXT) ? TM_RICHTEXT : TM_PLAINTEXT;
3122   ed->AutoURLDetect_bEnable = FALSE;
3123   ed->bHaveFocus = FALSE;
3124   ed->bDialogMode = FALSE;
3125   ed->bMouseCaptured = FALSE;
3126   ed->caret_hidden = FALSE;
3127   ed->caret_height = 0;
3128   for (i=0; i<HFONT_CACHE_SIZE; i++)
3129   {
3130     ed->pFontCache[i].nRefs = 0;
3131     ed->pFontCache[i].nAge = 0;
3132     ed->pFontCache[i].hFont = NULL;
3133   }
3134 
3135   ME_CheckCharOffsets(ed);
3136   SetRectEmpty(&ed->rcFormat);
3137   ed->bDefaultFormatRect = TRUE;
3138   ITextHost_TxGetSelectionBarWidth(ed->texthost, &selbarwidth);
3139   if (selbarwidth) {
3140     /* FIXME: Convert selbarwidth from HIMETRIC to pixels */
3141     ed->selofs = SELECTIONBAR_WIDTH;
3142     ed->styleFlags |= ES_SELECTIONBAR;
3143   } else {
3144     ed->selofs = 0;
3145   }
3146   ed->nSelectionType = stPosition;
3147 
3148   ed->cPasswordMask = 0;
3149   if (props & TXTBIT_USEPASSWORD)
3150     ITextHost_TxGetPasswordChar(texthost, &ed->cPasswordMask);
3151 
3152   if (props & TXTBIT_AUTOWORDSEL)
3153     ed->styleFlags |= ECO_AUTOWORDSELECTION;
3154   if (props & TXTBIT_MULTILINE) {
3155     ed->styleFlags |= ES_MULTILINE;
3156     ed->bWordWrap = (props & TXTBIT_WORDWRAP) != 0;
3157   } else {
3158     ed->bWordWrap = FALSE;
3159   }
3160   if (props & TXTBIT_READONLY)
3161     ed->styleFlags |= ES_READONLY;
3162   if (!(props & TXTBIT_HIDESELECTION))
3163     ed->styleFlags |= ES_NOHIDESEL;
3164   if (props & TXTBIT_SAVESELECTION)
3165     ed->styleFlags |= ES_SAVESEL;
3166   if (props & TXTBIT_VERTICAL)
3167     ed->styleFlags |= ES_VERTICAL;
3168   if (props & TXTBIT_DISABLEDRAG)
3169     ed->styleFlags |= ES_NOOLEDRAGDROP;
3170 
3171   ed->notified_cr.cpMin = ed->notified_cr.cpMax = 0;
3172 
3173   /* Default scrollbar information */
3174   ed->vert_si.cbSize = sizeof(SCROLLINFO);
3175   ed->vert_si.nMin = 0;
3176   ed->vert_si.nMax = 0;
3177   ed->vert_si.nPage = 0;
3178   ed->vert_si.nPos = 0;
3179 
3180   ed->horz_si.cbSize = sizeof(SCROLLINFO);
3181   ed->horz_si.nMin = 0;
3182   ed->horz_si.nMax = 0;
3183   ed->horz_si.nPage = 0;
3184   ed->horz_si.nPos = 0;
3185 
3186   ed->wheel_remain = 0;
3187 
3188   list_init( &ed->reobj_list );
3189   OleInitialize(NULL);
3190 
3191   return ed;
3192 }
3193 
ME_DestroyEditor(ME_TextEditor * editor)3194 void ME_DestroyEditor(ME_TextEditor *editor)
3195 {
3196   ME_DisplayItem *p = editor->pBuffer->pFirst, *pNext = NULL;
3197   ME_Style *s, *cursor2;
3198   int i;
3199 
3200   ME_ClearTempStyle(editor);
3201   ME_EmptyUndoStack(editor);
3202   editor->pBuffer->pFirst = NULL;
3203   while(p) {
3204     pNext = p->next;
3205     if (p->type == diParagraph)
3206       destroy_para(editor, p);
3207     else
3208       ME_DestroyDisplayItem(p);
3209     p = pNext;
3210   }
3211 
3212   LIST_FOR_EACH_ENTRY_SAFE( s, cursor2, &editor->style_list, ME_Style, entry )
3213       ME_DestroyStyle( s );
3214 
3215   ME_ReleaseStyle(editor->pBuffer->pDefaultStyle);
3216   for (i=0; i<HFONT_CACHE_SIZE; i++)
3217   {
3218     if (editor->pFontCache[i].hFont)
3219       DeleteObject(editor->pFontCache[i].hFont);
3220   }
3221   if (editor->rgbBackColor != -1)
3222     DeleteObject(editor->hbrBackground);
3223   if(editor->lpOleCallback)
3224     IRichEditOleCallback_Release(editor->lpOleCallback);
3225   ITextHost_Release(editor->texthost);
3226   if (editor->reOle)
3227   {
3228     IUnknown_Release(editor->reOle);
3229     editor->reOle = NULL;
3230   }
3231   OleUninitialize();
3232 
3233   heap_free(editor->pBuffer);
3234   heap_free(editor->pCursors);
3235   heap_free(editor);
3236 }
3237 
DllMain(HINSTANCE hinstDLL,DWORD fdwReason,LPVOID lpvReserved)3238 BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
3239 {
3240     TRACE("\n");
3241     switch (fdwReason)
3242     {
3243     case DLL_PROCESS_ATTACH:
3244       DisableThreadLibraryCalls(hinstDLL);
3245       me_heap = HeapCreate (0, 0x10000, 0);
3246       if (!ME_RegisterEditorClass(hinstDLL)) return FALSE;
3247       hLeft = LoadCursorW(hinstDLL, MAKEINTRESOURCEW(OCR_REVERSE));
3248       LookupInit();
3249       break;
3250 
3251     case DLL_PROCESS_DETACH:
3252       if (lpvReserved) break;
3253       UnregisterClassW(RICHEDIT_CLASS20W, 0);
3254       UnregisterClassW(MSFTEDIT_CLASS, 0);
3255       UnregisterClassA(RICHEDIT_CLASS20A, 0);
3256       UnregisterClassA("RichEdit50A", 0);
3257       if (ME_ListBoxRegistered)
3258           UnregisterClassW(REListBox20W, 0);
3259       if (ME_ComboBoxRegistered)
3260           UnregisterClassW(REComboBox20W, 0);
3261       LookupCleanup();
3262       HeapDestroy (me_heap);
3263       release_typelib();
3264       break;
3265     }
3266     return TRUE;
3267 }
3268 
get_default_line_height(ME_TextEditor * editor)3269 static inline int get_default_line_height( ME_TextEditor *editor )
3270 {
3271     int height = 0;
3272 
3273     if (editor->pBuffer && editor->pBuffer->pDefaultStyle)
3274         height = editor->pBuffer->pDefaultStyle->tm.tmHeight;
3275     if (height <= 0) height = 24;
3276 
3277     return height;
3278 }
3279 
calc_wheel_change(int * remain,int amount_per_click)3280 static inline int calc_wheel_change( int *remain, int amount_per_click )
3281 {
3282     int change = amount_per_click * (float)*remain / WHEEL_DELTA;
3283     *remain -= WHEEL_DELTA * change / amount_per_click;
3284     return change;
3285 }
3286 
3287 static const char * const edit_messages[] = {
3288   "EM_GETSEL",
3289   "EM_SETSEL",
3290   "EM_GETRECT",
3291   "EM_SETRECT",
3292   "EM_SETRECTNP",
3293   "EM_SCROLL",
3294   "EM_LINESCROLL",
3295   "EM_SCROLLCARET",
3296   "EM_GETMODIFY",
3297   "EM_SETMODIFY",
3298   "EM_GETLINECOUNT",
3299   "EM_LINEINDEX",
3300   "EM_SETHANDLE",
3301   "EM_GETHANDLE",
3302   "EM_GETTHUMB",
3303   "EM_UNKNOWN_BF",
3304   "EM_UNKNOWN_C0",
3305   "EM_LINELENGTH",
3306   "EM_REPLACESEL",
3307   "EM_UNKNOWN_C3",
3308   "EM_GETLINE",
3309   "EM_LIMITTEXT",
3310   "EM_CANUNDO",
3311   "EM_UNDO",
3312   "EM_FMTLINES",
3313   "EM_LINEFROMCHAR",
3314   "EM_UNKNOWN_CA",
3315   "EM_SETTABSTOPS",
3316   "EM_SETPASSWORDCHAR",
3317   "EM_EMPTYUNDOBUFFER",
3318   "EM_GETFIRSTVISIBLELINE",
3319   "EM_SETREADONLY",
3320   "EM_SETWORDBREAKPROC",
3321   "EM_GETWORDBREAKPROC",
3322   "EM_GETPASSWORDCHAR",
3323   "EM_SETMARGINS",
3324   "EM_GETMARGINS",
3325   "EM_GETLIMITTEXT",
3326   "EM_POSFROMCHAR",
3327   "EM_CHARFROMPOS",
3328   "EM_SETIMESTATUS",
3329   "EM_GETIMESTATUS"
3330 };
3331 
3332 static const char * const richedit_messages[] = {
3333   "EM_CANPASTE",
3334   "EM_DISPLAYBAND",
3335   "EM_EXGETSEL",
3336   "EM_EXLIMITTEXT",
3337   "EM_EXLINEFROMCHAR",
3338   "EM_EXSETSEL",
3339   "EM_FINDTEXT",
3340   "EM_FORMATRANGE",
3341   "EM_GETCHARFORMAT",
3342   "EM_GETEVENTMASK",
3343   "EM_GETOLEINTERFACE",
3344   "EM_GETPARAFORMAT",
3345   "EM_GETSELTEXT",
3346   "EM_HIDESELECTION",
3347   "EM_PASTESPECIAL",
3348   "EM_REQUESTRESIZE",
3349   "EM_SELECTIONTYPE",
3350   "EM_SETBKGNDCOLOR",
3351   "EM_SETCHARFORMAT",
3352   "EM_SETEVENTMASK",
3353   "EM_SETOLECALLBACK",
3354   "EM_SETPARAFORMAT",
3355   "EM_SETTARGETDEVICE",
3356   "EM_STREAMIN",
3357   "EM_STREAMOUT",
3358   "EM_GETTEXTRANGE",
3359   "EM_FINDWORDBREAK",
3360   "EM_SETOPTIONS",
3361   "EM_GETOPTIONS",
3362   "EM_FINDTEXTEX",
3363   "EM_GETWORDBREAKPROCEX",
3364   "EM_SETWORDBREAKPROCEX",
3365   "EM_SETUNDOLIMIT",
3366   "EM_UNKNOWN_USER_83",
3367   "EM_REDO",
3368   "EM_CANREDO",
3369   "EM_GETUNDONAME",
3370   "EM_GETREDONAME",
3371   "EM_STOPGROUPTYPING",
3372   "EM_SETTEXTMODE",
3373   "EM_GETTEXTMODE",
3374   "EM_AUTOURLDETECT",
3375   "EM_GETAUTOURLDETECT",
3376   "EM_SETPALETTE",
3377   "EM_GETTEXTEX",
3378   "EM_GETTEXTLENGTHEX",
3379   "EM_SHOWSCROLLBAR",
3380   "EM_SETTEXTEX",
3381   "EM_UNKNOWN_USER_98",
3382   "EM_UNKNOWN_USER_99",
3383   "EM_SETPUNCTUATION",
3384   "EM_GETPUNCTUATION",
3385   "EM_SETWORDWRAPMODE",
3386   "EM_GETWORDWRAPMODE",
3387   "EM_SETIMECOLOR",
3388   "EM_GETIMECOLOR",
3389   "EM_SETIMEOPTIONS",
3390   "EM_GETIMEOPTIONS",
3391   "EM_CONVPOSITION",
3392   "EM_UNKNOWN_USER_109",
3393   "EM_UNKNOWN_USER_110",
3394   "EM_UNKNOWN_USER_111",
3395   "EM_UNKNOWN_USER_112",
3396   "EM_UNKNOWN_USER_113",
3397   "EM_UNKNOWN_USER_114",
3398   "EM_UNKNOWN_USER_115",
3399   "EM_UNKNOWN_USER_116",
3400   "EM_UNKNOWN_USER_117",
3401   "EM_UNKNOWN_USER_118",
3402   "EM_UNKNOWN_USER_119",
3403   "EM_SETLANGOPTIONS",
3404   "EM_GETLANGOPTIONS",
3405   "EM_GETIMECOMPMODE",
3406   "EM_FINDTEXTW",
3407   "EM_FINDTEXTEXW",
3408   "EM_RECONVERSION",
3409   "EM_SETIMEMODEBIAS",
3410   "EM_GETIMEMODEBIAS"
3411 };
3412 
3413 static const char *
get_msg_name(UINT msg)3414 get_msg_name(UINT msg)
3415 {
3416   if (msg >= EM_GETSEL && msg <= EM_CHARFROMPOS)
3417     return edit_messages[msg - EM_GETSEL];
3418   if (msg >= EM_CANPASTE && msg <= EM_GETIMEMODEBIAS)
3419     return richedit_messages[msg - EM_CANPASTE];
3420   return "";
3421 }
3422 
ME_LinkNotify(ME_TextEditor * editor,UINT msg,WPARAM wParam,LPARAM lParam)3423 static void ME_LinkNotify(ME_TextEditor *editor, UINT msg, WPARAM wParam, LPARAM lParam)
3424 {
3425   int x,y;
3426   BOOL isExact;
3427   ME_Cursor cursor; /* The start of the clicked text. */
3428 
3429   ENLINK info;
3430   x = (short)LOWORD(lParam);
3431   y = (short)HIWORD(lParam);
3432   ME_CharFromPos(editor, x, y, &cursor, &isExact);
3433   if (!isExact) return;
3434 
3435   if (is_link( &cursor.pRun->member.run ))
3436   { /* The clicked run has CFE_LINK set */
3437     ME_DisplayItem *di;
3438 
3439     info.nmhdr.hwndFrom = NULL;
3440     info.nmhdr.idFrom = 0;
3441     info.nmhdr.code = EN_LINK;
3442     info.msg = msg;
3443     info.wParam = wParam;
3444     info.lParam = lParam;
3445     cursor.nOffset = 0;
3446 
3447     /* find the first contiguous run with CFE_LINK set */
3448     info.chrg.cpMin = ME_GetCursorOfs(&cursor);
3449     di = cursor.pRun;
3450     while (ME_PrevRun( NULL, &di, FALSE ) && is_link( &di->member.run ))
3451         info.chrg.cpMin -= di->member.run.len;
3452 
3453     /* find the last contiguous run with CFE_LINK set */
3454     info.chrg.cpMax = ME_GetCursorOfs(&cursor) + cursor.pRun->member.run.len;
3455     di = cursor.pRun;
3456     while (ME_NextRun( NULL, &di, FALSE ) && is_link( &di->member.run ))
3457         info.chrg.cpMax += di->member.run.len;
3458 
3459     ITextHost_TxNotify(editor->texthost, info.nmhdr.code, &info);
3460   }
3461 }
3462 
ME_ReplaceSel(ME_TextEditor * editor,BOOL can_undo,const WCHAR * str,int len)3463 void ME_ReplaceSel(ME_TextEditor *editor, BOOL can_undo, const WCHAR *str, int len)
3464 {
3465   int from, to, nStartCursor;
3466   ME_Style *style;
3467 
3468   nStartCursor = ME_GetSelectionOfs(editor, &from, &to);
3469   style = ME_GetSelectionInsertStyle(editor);
3470   ME_InternalDeleteText(editor, &editor->pCursors[nStartCursor], to-from, FALSE);
3471   ME_InsertTextFromCursor(editor, 0, str, len, style);
3472   ME_ReleaseStyle(style);
3473   /* drop temporary style if line end */
3474   /*
3475    * FIXME question: does abc\n mean: put abc,
3476    * clear temp style, put \n? (would require a change)
3477    */
3478   if (len>0 && str[len-1] == '\n')
3479     ME_ClearTempStyle(editor);
3480   ME_CommitUndo(editor);
3481   ME_UpdateSelectionLinkAttribute(editor);
3482   if (!can_undo)
3483     ME_EmptyUndoStack(editor);
3484   ME_UpdateRepaint(editor, FALSE);
3485 }
3486 
ME_SetText(ME_TextEditor * editor,void * text,BOOL unicode)3487 static void ME_SetText(ME_TextEditor *editor, void *text, BOOL unicode)
3488 {
3489   LONG codepage = unicode ? CP_UNICODE : CP_ACP;
3490   int textLen;
3491 
3492   LPWSTR wszText = ME_ToUnicode(codepage, text, &textLen);
3493   ME_InsertTextFromCursor(editor, 0, wszText, textLen, editor->pBuffer->pDefaultStyle);
3494   ME_EndToUnicode(codepage, wszText);
3495 }
3496 
ME_WmCreate(ME_TextEditor * editor,LPARAM lParam,BOOL unicode)3497 static LRESULT ME_WmCreate(ME_TextEditor *editor, LPARAM lParam, BOOL unicode)
3498 {
3499   CREATESTRUCTW *createW = (CREATESTRUCTW*)lParam;
3500   CREATESTRUCTA *createA = (CREATESTRUCTA*)lParam;
3501   void *text = NULL;
3502   INT max;
3503 
3504   if (lParam)
3505     text = unicode ? (void*)createW->lpszName : (void*)createA->lpszName;
3506 
3507   ME_SetDefaultFormatRect(editor);
3508 
3509   max = (editor->styleFlags & ES_DISABLENOSCROLL) ? 1 : 0;
3510   if (~editor->styleFlags & ES_DISABLENOSCROLL || editor->styleFlags & WS_VSCROLL)
3511     ITextHost_TxSetScrollRange(editor->texthost, SB_VERT, 0, max, TRUE);
3512 
3513   if (~editor->styleFlags & ES_DISABLENOSCROLL || editor->styleFlags & WS_HSCROLL)
3514     ITextHost_TxSetScrollRange(editor->texthost, SB_HORZ, 0, max, TRUE);
3515 
3516   if (editor->styleFlags & ES_DISABLENOSCROLL)
3517   {
3518     if (editor->styleFlags & WS_VSCROLL)
3519     {
3520       ITextHost_TxEnableScrollBar(editor->texthost, SB_VERT, ESB_DISABLE_BOTH);
3521       ITextHost_TxShowScrollBar(editor->texthost, SB_VERT, TRUE);
3522     }
3523     if (editor->styleFlags & WS_HSCROLL)
3524     {
3525       ITextHost_TxEnableScrollBar(editor->texthost, SB_HORZ, ESB_DISABLE_BOTH);
3526       ITextHost_TxShowScrollBar(editor->texthost, SB_HORZ, TRUE);
3527     }
3528   }
3529 
3530   if (text)
3531   {
3532     ME_SetText(editor, text, unicode);
3533     ME_SetCursorToStart(editor, &editor->pCursors[0]);
3534     ME_SetCursorToStart(editor, &editor->pCursors[1]);
3535   }
3536 
3537   ME_CommitUndo(editor);
3538   ME_WrapMarkedParagraphs(editor);
3539   update_caret(editor);
3540   return 0;
3541 }
3542 
handle_EM_SETCHARFORMAT(ME_TextEditor * editor,WPARAM flags,const CHARFORMAT2W * fmt_in)3543 static LRESULT handle_EM_SETCHARFORMAT( ME_TextEditor *editor, WPARAM flags, const CHARFORMAT2W *fmt_in )
3544 {
3545     CHARFORMAT2W fmt;
3546     BOOL changed = TRUE;
3547     ME_Cursor start, end;
3548 
3549     if (!cfany_to_cf2w( &fmt, fmt_in )) return 0;
3550 
3551     if (flags & SCF_ALL)
3552     {
3553         if (editor->mode & TM_PLAINTEXT)
3554         {
3555             ME_SetDefaultCharFormat( editor, &fmt );
3556         }
3557         else
3558         {
3559             ME_SetCursorToStart( editor, &start );
3560             ME_SetCharFormat( editor, &start, NULL, &fmt );
3561             editor->nModifyStep = 1;
3562         }
3563     }
3564     else if (flags & SCF_SELECTION)
3565     {
3566         if (editor->mode & TM_PLAINTEXT) return 0;
3567         if (flags & SCF_WORD)
3568         {
3569             end = editor->pCursors[0];
3570             ME_MoveCursorWords( editor, &end, +1 );
3571             start = end;
3572             ME_MoveCursorWords( editor, &start, -1 );
3573             ME_SetCharFormat( editor, &start, &end, &fmt );
3574         }
3575         changed = ME_IsSelection( editor );
3576         ME_SetSelectionCharFormat( editor, &fmt );
3577         if (changed) editor->nModifyStep = 1;
3578     }
3579     else /* SCF_DEFAULT */
3580     {
3581         ME_SetDefaultCharFormat( editor, &fmt );
3582     }
3583 
3584     ME_CommitUndo( editor );
3585     if (changed)
3586     {
3587         ME_WrapMarkedParagraphs( editor );
3588         ME_UpdateScrollBar( editor );
3589     }
3590     return 1;
3591 }
3592 
3593 #define UNSUPPORTED_MSG(e) \
3594   case e:                  \
3595     FIXME(#e ": stub\n");  \
3596     *phresult = S_FALSE;   \
3597     return 0;
3598 
3599 /* Handle messages for windowless and windowed richedit controls.
3600  *
3601  * The LRESULT that is returned is a return value for window procs,
3602  * and the phresult parameter is the COM return code needed by the
3603  * text services interface. */
ME_HandleMessage(ME_TextEditor * editor,UINT msg,WPARAM wParam,LPARAM lParam,BOOL unicode,HRESULT * phresult)3604 LRESULT ME_HandleMessage(ME_TextEditor *editor, UINT msg, WPARAM wParam,
3605                          LPARAM lParam, BOOL unicode, HRESULT* phresult)
3606 {
3607   *phresult = S_OK;
3608 
3609   switch(msg) {
3610 
3611   UNSUPPORTED_MSG(EM_DISPLAYBAND)
3612   UNSUPPORTED_MSG(EM_FINDWORDBREAK)
3613   UNSUPPORTED_MSG(EM_FMTLINES)
3614   UNSUPPORTED_MSG(EM_FORMATRANGE)
3615   UNSUPPORTED_MSG(EM_GETBIDIOPTIONS)
3616   UNSUPPORTED_MSG(EM_GETEDITSTYLE)
3617   UNSUPPORTED_MSG(EM_GETIMECOMPMODE)
3618   UNSUPPORTED_MSG(EM_GETIMESTATUS)
3619   UNSUPPORTED_MSG(EM_SETIMESTATUS)
3620   UNSUPPORTED_MSG(EM_GETLANGOPTIONS)
3621   UNSUPPORTED_MSG(EM_GETREDONAME)
3622   UNSUPPORTED_MSG(EM_GETTYPOGRAPHYOPTIONS)
3623   UNSUPPORTED_MSG(EM_GETUNDONAME)
3624   UNSUPPORTED_MSG(EM_GETWORDBREAKPROCEX)
3625   UNSUPPORTED_MSG(EM_SETBIDIOPTIONS)
3626   UNSUPPORTED_MSG(EM_SETEDITSTYLE)
3627   UNSUPPORTED_MSG(EM_SETLANGOPTIONS)
3628   UNSUPPORTED_MSG(EM_SETMARGINS)
3629   UNSUPPORTED_MSG(EM_SETPALETTE)
3630   UNSUPPORTED_MSG(EM_SETTABSTOPS)
3631   UNSUPPORTED_MSG(EM_SETTYPOGRAPHYOPTIONS)
3632   UNSUPPORTED_MSG(EM_SETWORDBREAKPROCEX)
3633 
3634 /* Messages specific to Richedit controls */
3635 
3636   case EM_STREAMIN:
3637    return ME_StreamIn(editor, wParam, (EDITSTREAM*)lParam, TRUE);
3638   case EM_STREAMOUT:
3639    return ME_StreamOut(editor, wParam, (EDITSTREAM *)lParam);
3640   case WM_GETDLGCODE:
3641   {
3642     UINT code = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS;
3643 
3644     if (lParam)
3645       editor->bDialogMode = TRUE;
3646     if (editor->styleFlags & ES_MULTILINE)
3647       code |= DLGC_WANTMESSAGE;
3648     if (!(editor->styleFlags & ES_SAVESEL))
3649       code |= DLGC_HASSETSEL;
3650     return code;
3651   }
3652   case EM_EMPTYUNDOBUFFER:
3653     ME_EmptyUndoStack(editor);
3654     return 0;
3655   case EM_GETSEL:
3656   {
3657     /* Note: wParam/lParam can be NULL */
3658     UINT from, to;
3659     PUINT pfrom = wParam ? (PUINT)wParam : &from;
3660     PUINT pto = lParam ? (PUINT)lParam : &to;
3661     ME_GetSelectionOfs(editor, (int *)pfrom, (int *)pto);
3662     if ((*pfrom|*pto) & 0xFFFF0000)
3663       return -1;
3664     return MAKELONG(*pfrom,*pto);
3665   }
3666   case EM_EXGETSEL:
3667   {
3668     CHARRANGE *pRange = (CHARRANGE *)lParam;
3669     ME_GetSelectionOfs(editor, &pRange->cpMin, &pRange->cpMax);
3670     TRACE("EM_EXGETSEL = (%d,%d)\n", pRange->cpMin, pRange->cpMax);
3671     return 0;
3672   }
3673   case EM_SETUNDOLIMIT:
3674   {
3675     if ((int)wParam < 0)
3676       editor->nUndoLimit = STACK_SIZE_DEFAULT;
3677     else
3678       editor->nUndoLimit = min(wParam, STACK_SIZE_MAX);
3679     /* Setting a max stack size keeps wine from getting killed
3680       for hogging memory. Windows allocates all this memory at once, so
3681       no program would realistically set a value above our maximum. */
3682     return editor->nUndoLimit;
3683   }
3684   case EM_CANUNDO:
3685     return !list_empty( &editor->undo_stack );
3686   case EM_CANREDO:
3687     return !list_empty( &editor->redo_stack );
3688   case WM_UNDO: /* FIXME: actually not the same */
3689   case EM_UNDO:
3690     return ME_Undo(editor);
3691   case EM_REDO:
3692     return ME_Redo(editor);
3693   case EM_GETOPTIONS:
3694   {
3695     /* these flags are equivalent to the ES_* counterparts */
3696     DWORD mask = ECO_VERTICAL | ECO_AUTOHSCROLL | ECO_AUTOVSCROLL |
3697                  ECO_NOHIDESEL | ECO_READONLY | ECO_WANTRETURN | ECO_SELECTIONBAR;
3698     DWORD settings = editor->styleFlags & mask;
3699 
3700     return settings;
3701   }
3702   case EM_SETFONTSIZE:
3703   {
3704       CHARFORMAT2W cf;
3705       LONG tmp_size, size;
3706       BOOL is_increase = ((LONG)wParam > 0);
3707 
3708       if (editor->mode & TM_PLAINTEXT)
3709           return FALSE;
3710 
3711       cf.cbSize = sizeof(cf);
3712       cf.dwMask = CFM_SIZE;
3713       ME_GetSelectionCharFormat(editor, &cf);
3714       tmp_size = (cf.yHeight / 20) + wParam;
3715 
3716       if (tmp_size <= 1)
3717           size = 1;
3718       else if (tmp_size > 12 && tmp_size < 28 && tmp_size % 2)
3719           size = tmp_size + (is_increase ? 1 : -1);
3720       else if (tmp_size > 28 && tmp_size < 36)
3721           size = is_increase ? 36 : 28;
3722       else if (tmp_size > 36 && tmp_size < 48)
3723           size = is_increase ? 48 : 36;
3724       else if (tmp_size > 48 && tmp_size < 72)
3725           size = is_increase ? 72 : 48;
3726       else if (tmp_size > 72 && tmp_size < 80)
3727           size = is_increase ? 80 : 72;
3728       else if (tmp_size > 80 && tmp_size < 1638)
3729           size = 10 * (is_increase ? (tmp_size / 10 + 1) : (tmp_size / 10));
3730       else if (tmp_size >= 1638)
3731           size = 1638;
3732       else
3733           size = tmp_size;
3734 
3735       cf.yHeight = size * 20; /*  convert twips to points */
3736       ME_SetSelectionCharFormat(editor, &cf);
3737       ME_CommitUndo(editor);
3738       ME_WrapMarkedParagraphs(editor);
3739       ME_UpdateScrollBar(editor);
3740 
3741       return TRUE;
3742   }
3743   case EM_SETOPTIONS:
3744   {
3745     /* these flags are equivalent to ES_* counterparts, except for
3746      * ECO_AUTOWORDSELECTION that doesn't have an ES_* counterpart,
3747      * but is still stored in editor->styleFlags. */
3748     const DWORD mask = ECO_VERTICAL | ECO_AUTOHSCROLL | ECO_AUTOVSCROLL |
3749                        ECO_NOHIDESEL | ECO_READONLY | ECO_WANTRETURN |
3750                        ECO_SELECTIONBAR | ECO_AUTOWORDSELECTION;
3751     DWORD settings = mask & editor->styleFlags;
3752     DWORD oldSettings = settings;
3753     DWORD changedSettings;
3754 
3755     switch(wParam)
3756     {
3757       case ECOOP_SET:
3758         settings = lParam;
3759         break;
3760       case ECOOP_OR:
3761         settings |= lParam;
3762         break;
3763       case ECOOP_AND:
3764         settings &= lParam;
3765         break;
3766       case ECOOP_XOR:
3767         settings ^= lParam;
3768     }
3769     changedSettings = oldSettings ^ settings;
3770 
3771     if (changedSettings) {
3772       editor->styleFlags = (editor->styleFlags & ~mask) | (settings & mask);
3773 
3774       if (changedSettings & ECO_SELECTIONBAR)
3775       {
3776         ITextHost_TxInvalidateRect(editor->texthost, &editor->rcFormat, TRUE);
3777         if (settings & ECO_SELECTIONBAR) {
3778           assert(!editor->selofs);
3779           editor->selofs = SELECTIONBAR_WIDTH;
3780           editor->rcFormat.left += editor->selofs;
3781         } else {
3782           editor->rcFormat.left -= editor->selofs;
3783           editor->selofs = 0;
3784         }
3785         ME_RewrapRepaint(editor);
3786       }
3787 
3788       if ((changedSettings & settings & ES_NOHIDESEL) && !editor->bHaveFocus)
3789           ME_InvalidateSelection( editor );
3790 
3791       if (changedSettings & settings & ECO_VERTICAL)
3792         FIXME("ECO_VERTICAL not implemented yet!\n");
3793       if (changedSettings & settings & ECO_AUTOHSCROLL)
3794         FIXME("ECO_AUTOHSCROLL not implemented yet!\n");
3795       if (changedSettings & settings & ECO_AUTOVSCROLL)
3796         FIXME("ECO_AUTOVSCROLL not implemented yet!\n");
3797       if (changedSettings & settings & ECO_WANTRETURN)
3798         FIXME("ECO_WANTRETURN not implemented yet!\n");
3799       if (changedSettings & settings & ECO_AUTOWORDSELECTION)
3800         FIXME("ECO_AUTOWORDSELECTION not implemented yet!\n");
3801     }
3802 
3803     return settings;
3804   }
3805   case EM_SETSEL:
3806   {
3807     return set_selection( editor, wParam, lParam );
3808   }
3809   case EM_SETSCROLLPOS:
3810   {
3811     POINT *point = (POINT *)lParam;
3812     ME_ScrollAbs(editor, point->x, point->y);
3813     return 0;
3814   }
3815   case EM_AUTOURLDETECT:
3816   {
3817     if (wParam==1 || wParam ==0)
3818     {
3819         editor->AutoURLDetect_bEnable = (BOOL)wParam;
3820         return 0;
3821     }
3822     return E_INVALIDARG;
3823   }
3824   case EM_GETAUTOURLDETECT:
3825   {
3826     return editor->AutoURLDetect_bEnable;
3827   }
3828   case EM_EXSETSEL:
3829   {
3830     CHARRANGE range = *(CHARRANGE *)lParam;
3831 
3832     return set_selection( editor, range.cpMin, range.cpMax );
3833   }
3834   case EM_SHOWSCROLLBAR:
3835   {
3836     DWORD flags;
3837 
3838     switch (wParam)
3839     {
3840       case SB_HORZ:
3841         flags = WS_HSCROLL;
3842         break;
3843       case SB_VERT:
3844         flags = WS_VSCROLL;
3845         break;
3846       case SB_BOTH:
3847         flags = WS_HSCROLL|WS_VSCROLL;
3848         break;
3849       default:
3850         return 0;
3851     }
3852 
3853     if (lParam) {
3854       editor->styleFlags |= flags;
3855       if (flags & WS_HSCROLL)
3856         ITextHost_TxShowScrollBar(editor->texthost, SB_HORZ,
3857                           editor->nTotalWidth > editor->sizeWindow.cx);
3858       if (flags & WS_VSCROLL)
3859         ITextHost_TxShowScrollBar(editor->texthost, SB_VERT,
3860                           editor->nTotalLength > editor->sizeWindow.cy);
3861     } else {
3862       editor->styleFlags &= ~flags;
3863       ITextHost_TxShowScrollBar(editor->texthost, wParam, FALSE);
3864     }
3865     return 0;
3866   }
3867   case EM_SETTEXTEX:
3868   {
3869     LPWSTR wszText;
3870     SETTEXTEX *pStruct = (SETTEXTEX *)wParam;
3871     int from, to, len;
3872     ME_Style *style;
3873     BOOL bRtf, bUnicode, bSelection, bUTF8;
3874     int oldModify = editor->nModifyStep;
3875     static const char utf8_bom[] = {0xef, 0xbb, 0xbf};
3876 
3877     if (!pStruct) return 0;
3878 
3879     /* If we detect ascii rtf at the start of the string,
3880      * we know it isn't unicode. */
3881     bRtf = (lParam && (!strncmp((char *)lParam, "{\\rtf", 5) ||
3882                          !strncmp((char *)lParam, "{\\urtf", 6)));
3883     bUnicode = !bRtf && pStruct->codepage == CP_UNICODE;
3884     bUTF8 = (lParam && (!strncmp((char *)lParam, utf8_bom, 3)));
3885 
3886     TRACE("EM_SETTEXTEX - %s, flags %d, cp %d\n",
3887           bUnicode ? debugstr_w((LPCWSTR)lParam) : debugstr_a((LPCSTR)lParam),
3888           pStruct->flags, pStruct->codepage);
3889 
3890     bSelection = (pStruct->flags & ST_SELECTION) != 0;
3891     if (bSelection) {
3892       int nStartCursor = ME_GetSelectionOfs(editor, &from, &to);
3893       style = ME_GetSelectionInsertStyle(editor);
3894       ME_InternalDeleteText(editor, &editor->pCursors[nStartCursor], to - from, FALSE);
3895     } else {
3896       ME_Cursor start;
3897       ME_SetCursorToStart(editor, &start);
3898       ME_InternalDeleteText(editor, &start, ME_GetTextLength(editor), FALSE);
3899       style = editor->pBuffer->pDefaultStyle;
3900     }
3901 
3902     if (bRtf) {
3903       ME_StreamInRTFString(editor, bSelection, (char *)lParam);
3904       if (bSelection) {
3905         /* FIXME: The length returned doesn't include the rtf control
3906          * characters, only the actual text. */
3907         len = lParam ? strlen((char *)lParam) : 0;
3908       }
3909     } else {
3910       if (bUTF8 && !bUnicode) {
3911         wszText = ME_ToUnicode(CP_UTF8, (void *)(lParam+3), &len);
3912         ME_InsertTextFromCursor(editor, 0, wszText, len, style);
3913         ME_EndToUnicode(CP_UTF8, wszText);
3914       } else {
3915         wszText = ME_ToUnicode(pStruct->codepage, (void *)lParam, &len);
3916         ME_InsertTextFromCursor(editor, 0, wszText, len, style);
3917         ME_EndToUnicode(pStruct->codepage, wszText);
3918       }
3919     }
3920 
3921     if (bSelection) {
3922       ME_ReleaseStyle(style);
3923       ME_UpdateSelectionLinkAttribute(editor);
3924     } else {
3925       ME_Cursor cursor;
3926       len = 1;
3927       ME_SetCursorToStart(editor, &cursor);
3928       ME_UpdateLinkAttribute(editor, &cursor, INT_MAX);
3929     }
3930     ME_CommitUndo(editor);
3931     if (!(pStruct->flags & ST_KEEPUNDO))
3932     {
3933       editor->nModifyStep = oldModify;
3934       ME_EmptyUndoStack(editor);
3935     }
3936     ME_UpdateRepaint(editor, FALSE);
3937     return len;
3938   }
3939   case EM_SELECTIONTYPE:
3940     return ME_GetSelectionType(editor);
3941   case EM_SETBKGNDCOLOR:
3942   {
3943     LRESULT lColor;
3944     if (editor->rgbBackColor != -1) {
3945       DeleteObject(editor->hbrBackground);
3946       lColor = editor->rgbBackColor;
3947     }
3948     else lColor = ITextHost_TxGetSysColor(editor->texthost, COLOR_WINDOW);
3949 
3950     if (wParam)
3951     {
3952       editor->rgbBackColor = -1;
3953       editor->hbrBackground = GetSysColorBrush(COLOR_WINDOW);
3954     }
3955     else
3956     {
3957       editor->rgbBackColor = lParam;
3958       editor->hbrBackground = CreateSolidBrush(editor->rgbBackColor);
3959     }
3960     ITextHost_TxInvalidateRect(editor->texthost, NULL, TRUE);
3961     return lColor;
3962   }
3963   case EM_GETMODIFY:
3964     return editor->nModifyStep == 0 ? 0 : -1;
3965   case EM_SETMODIFY:
3966   {
3967     if (wParam)
3968       editor->nModifyStep = 1;
3969     else
3970       editor->nModifyStep = 0;
3971 
3972     return 0;
3973   }
3974   case EM_SETREADONLY:
3975   {
3976     if (wParam)
3977       editor->styleFlags |= ES_READONLY;
3978     else
3979       editor->styleFlags &= ~ES_READONLY;
3980     return 1;
3981   }
3982   case EM_SETEVENTMASK:
3983   {
3984     DWORD nOldMask = editor->nEventMask;
3985 
3986     editor->nEventMask = lParam;
3987     return nOldMask;
3988   }
3989   case EM_GETEVENTMASK:
3990     return editor->nEventMask;
3991   case EM_SETCHARFORMAT:
3992     return handle_EM_SETCHARFORMAT( editor, wParam, (CHARFORMAT2W *)lParam );
3993   case EM_GETCHARFORMAT:
3994   {
3995     CHARFORMAT2W tmp, *dst = (CHARFORMAT2W *)lParam;
3996     if (dst->cbSize != sizeof(CHARFORMATA) &&
3997         dst->cbSize != sizeof(CHARFORMATW) &&
3998         dst->cbSize != sizeof(CHARFORMAT2A) &&
3999         dst->cbSize != sizeof(CHARFORMAT2W))
4000       return 0;
4001     tmp.cbSize = sizeof(tmp);
4002     if (!wParam)
4003       ME_GetDefaultCharFormat(editor, &tmp);
4004     else
4005       ME_GetSelectionCharFormat(editor, &tmp);
4006     cf2w_to_cfany(dst, &tmp);
4007     return tmp.dwMask;
4008   }
4009   case EM_SETPARAFORMAT:
4010   {
4011     BOOL result = ME_SetSelectionParaFormat(editor, (PARAFORMAT2 *)lParam);
4012     ME_WrapMarkedParagraphs(editor);
4013     ME_UpdateScrollBar(editor);
4014     ME_CommitUndo(editor);
4015     return result;
4016   }
4017   case EM_GETPARAFORMAT:
4018     ME_GetSelectionParaFormat(editor, (PARAFORMAT2 *)lParam);
4019     return ((PARAFORMAT2 *)lParam)->dwMask;
4020   case EM_GETFIRSTVISIBLELINE:
4021   {
4022     ME_DisplayItem *p = editor->pBuffer->pFirst;
4023     int y = editor->vert_si.nPos;
4024     int ypara = 0;
4025     int count = 0;
4026     int ystart, yend;
4027     while(p) {
4028       p = ME_FindItemFwd(p, diStartRowOrParagraphOrEnd);
4029       if (p->type == diTextEnd)
4030         break;
4031       if (p->type == diParagraph) {
4032         ypara = p->member.para.pt.y;
4033         continue;
4034       }
4035       ystart = ypara + p->member.row.pt.y;
4036       yend = ystart + p->member.row.nHeight;
4037       if (y < yend) {
4038         break;
4039       }
4040       count++;
4041     }
4042     return count;
4043   }
4044   case EM_HIDESELECTION:
4045   {
4046      editor->bHideSelection = (wParam != 0);
4047      ME_InvalidateSelection(editor);
4048      return 0;
4049   }
4050   case EM_LINESCROLL:
4051   {
4052     if (!(editor->styleFlags & ES_MULTILINE))
4053       return FALSE;
4054     ME_ScrollDown( editor, lParam * get_default_line_height( editor ) );
4055     return TRUE;
4056   }
4057   case WM_CLEAR:
4058   {
4059     int from, to;
4060     int nStartCursor = ME_GetSelectionOfs(editor, &from, &to);
4061     ME_InternalDeleteText(editor, &editor->pCursors[nStartCursor], to-from, FALSE);
4062     ME_CommitUndo(editor);
4063     ME_UpdateRepaint(editor, TRUE);
4064     return 0;
4065   }
4066   case EM_REPLACESEL:
4067   {
4068     int len = 0;
4069     LONG codepage = unicode ? CP_UNICODE : CP_ACP;
4070     LPWSTR wszText = ME_ToUnicode(codepage, (void *)lParam, &len);
4071 
4072     TRACE("EM_REPLACESEL - %s\n", debugstr_w(wszText));
4073 
4074     ME_ReplaceSel(editor, !!wParam, wszText, len);
4075     ME_EndToUnicode(codepage, wszText);
4076     return len;
4077   }
4078   case EM_SCROLLCARET:
4079     ME_EnsureVisible(editor, &editor->pCursors[0]);
4080     return 0;
4081   case WM_SETFONT:
4082   {
4083     LOGFONTW lf;
4084     CHARFORMAT2W fmt;
4085     HDC hDC;
4086     BOOL bRepaint = LOWORD(lParam);
4087 
4088     if (!wParam)
4089       wParam = (WPARAM)GetStockObject(SYSTEM_FONT);
4090 
4091     if (!GetObjectW((HGDIOBJ)wParam, sizeof(LOGFONTW), &lf))
4092       return 0;
4093 
4094     hDC = ITextHost_TxGetDC(editor->texthost);
4095     ME_CharFormatFromLogFont(hDC, &lf, &fmt);
4096     ITextHost_TxReleaseDC(editor->texthost, hDC);
4097     if (editor->mode & TM_RICHTEXT) {
4098       ME_Cursor start;
4099       ME_SetCursorToStart(editor, &start);
4100       ME_SetCharFormat(editor, &start, NULL, &fmt);
4101     }
4102     ME_SetDefaultCharFormat(editor, &fmt);
4103 
4104     ME_CommitUndo(editor);
4105     ME_MarkAllForWrapping(editor);
4106     ME_WrapMarkedParagraphs(editor);
4107     ME_UpdateScrollBar(editor);
4108     if (bRepaint)
4109       ME_Repaint(editor);
4110 #ifdef __REACTOS__
4111     if (ImmIsIME(GetKeyboardLayout(0)))
4112     {
4113       HIMC hIMC = ImmGetContext(editor->hWnd);
4114       ImmSetCompositionFontW(hIMC, &lf);
4115       ImmReleaseContext(editor->hWnd, hIMC);
4116     }
4117 #endif
4118     return 0;
4119   }
4120   case WM_SETTEXT:
4121   {
4122     ME_Cursor cursor;
4123     ME_SetCursorToStart(editor, &cursor);
4124     ME_InternalDeleteText(editor, &cursor, ME_GetTextLength(editor), FALSE);
4125     if (lParam)
4126     {
4127       TRACE("WM_SETTEXT lParam==%lx\n",lParam);
4128       if (!strncmp((char *)lParam, "{\\rtf", 5) ||
4129           !strncmp((char *)lParam, "{\\urtf", 6))
4130       {
4131         /* Undocumented: WM_SETTEXT supports RTF text */
4132         ME_StreamInRTFString(editor, 0, (char *)lParam);
4133       }
4134       else
4135         ME_SetText(editor, (void*)lParam, unicode);
4136     }
4137     else
4138       TRACE("WM_SETTEXT - NULL\n");
4139     ME_SetCursorToStart(editor, &cursor);
4140     ME_UpdateLinkAttribute(editor, &cursor, INT_MAX);
4141     set_selection_cursors(editor, 0, 0);
4142     editor->nModifyStep = 0;
4143     ME_CommitUndo(editor);
4144     ME_EmptyUndoStack(editor);
4145     ME_UpdateRepaint(editor, FALSE);
4146     return 1;
4147   }
4148   case EM_CANPASTE:
4149     return paste_special( editor, 0, NULL, TRUE );
4150   case WM_PASTE:
4151   case WM_MBUTTONDOWN:
4152     wParam = 0;
4153     lParam = 0;
4154     /* fall through */
4155   case EM_PASTESPECIAL:
4156     paste_special( editor, wParam, (REPASTESPECIAL *)lParam, FALSE );
4157     return 0;
4158   case WM_CUT:
4159   case WM_COPY:
4160     copy_or_cut(editor, msg == WM_CUT);
4161     return 0;
4162   case WM_GETTEXTLENGTH:
4163   {
4164     GETTEXTLENGTHEX how;
4165 
4166     /* CR/LF conversion required in 2.0 mode, verbatim in 1.0 mode */
4167     how.flags = GTL_CLOSE | (editor->bEmulateVersion10 ? 0 : GTL_USECRLF) | GTL_NUMCHARS;
4168     how.codepage = unicode ? CP_UNICODE : CP_ACP;
4169     return ME_GetTextLengthEx(editor, &how);
4170   }
4171   case EM_GETTEXTLENGTHEX:
4172     return ME_GetTextLengthEx(editor, (GETTEXTLENGTHEX *)wParam);
4173   case WM_GETTEXT:
4174   {
4175     GETTEXTEX ex;
4176     ex.cb = wParam * (unicode ? sizeof(WCHAR) : sizeof(CHAR));
4177     ex.flags = GT_USECRLF;
4178     ex.codepage = unicode ? CP_UNICODE : CP_ACP;
4179     ex.lpDefaultChar = NULL;
4180     ex.lpUsedDefChar = NULL;
4181     return ME_GetTextEx(editor, &ex, lParam);
4182   }
4183   case EM_GETTEXTEX:
4184     return ME_GetTextEx(editor, (GETTEXTEX*)wParam, lParam);
4185   case EM_GETSELTEXT:
4186   {
4187     int nFrom, nTo, nStartCur = ME_GetSelectionOfs(editor, &nFrom, &nTo);
4188     ME_Cursor *from = &editor->pCursors[nStartCur];
4189     return ME_GetTextRange(editor, (WCHAR *)lParam, from,
4190                            nTo - nFrom, unicode);
4191   }
4192   case EM_GETSCROLLPOS:
4193   {
4194     POINT *point = (POINT *)lParam;
4195     point->x = editor->horz_si.nPos;
4196     point->y = editor->vert_si.nPos;
4197     /* 16-bit scaled value is returned as stored in scrollinfo */
4198     if (editor->horz_si.nMax > 0xffff)
4199       point->x = MulDiv(point->x, 0xffff, editor->horz_si.nMax);
4200     if (editor->vert_si.nMax > 0xffff)
4201       point->y = MulDiv(point->y, 0xffff, editor->vert_si.nMax);
4202     return 1;
4203   }
4204   case EM_GETTEXTRANGE:
4205   {
4206     TEXTRANGEW *rng = (TEXTRANGEW *)lParam;
4207     ME_Cursor start;
4208     int nStart = rng->chrg.cpMin;
4209     int nEnd = rng->chrg.cpMax;
4210     int textlength = ME_GetTextLength(editor);
4211 
4212     TRACE("EM_GETTEXTRANGE min=%d max=%d unicode=%d textlength=%d\n",
4213           rng->chrg.cpMin, rng->chrg.cpMax, unicode, textlength);
4214     if (nStart < 0) return 0;
4215     if ((nStart == 0 && nEnd == -1) || nEnd > textlength)
4216       nEnd = textlength;
4217     if (nStart >= nEnd) return 0;
4218 
4219     ME_CursorFromCharOfs(editor, nStart, &start);
4220     return ME_GetTextRange(editor, rng->lpstrText, &start, nEnd - nStart, unicode);
4221   }
4222   case EM_GETLINE:
4223   {
4224     ME_DisplayItem *run;
4225     const unsigned int nMaxChars = *(WORD *) lParam;
4226     unsigned int nCharsLeft = nMaxChars;
4227     char *dest = (char *) lParam;
4228     BOOL wroteNull = FALSE;
4229 
4230     TRACE("EM_GETLINE: row=%d, nMaxChars=%d (%s)\n", (int) wParam, nMaxChars,
4231           unicode ? "Unicode" : "Ansi");
4232 
4233     run = ME_FindRowWithNumber(editor, wParam);
4234     if (run == NULL)
4235       return 0;
4236 
4237     while (nCharsLeft && (run = ME_FindItemFwd(run, diRunOrStartRow))
4238            && run->type == diRun)
4239     {
4240       WCHAR *str = get_text( &run->member.run, 0 );
4241       unsigned int nCopy;
4242 
4243       nCopy = min(nCharsLeft, run->member.run.len);
4244 
4245       if (unicode)
4246         memcpy(dest, str, nCopy * sizeof(WCHAR));
4247       else
4248         nCopy = WideCharToMultiByte(CP_ACP, 0, str, nCopy, dest,
4249                                     nCharsLeft, NULL, NULL);
4250       dest += nCopy * (unicode ? sizeof(WCHAR) : 1);
4251       nCharsLeft -= nCopy;
4252     }
4253 
4254     /* append line termination, space allowing */
4255     if (nCharsLeft > 0)
4256     {
4257       if (unicode)
4258         *((WCHAR *)dest) = '\0';
4259       else
4260         *dest = '\0';
4261       nCharsLeft--;
4262       wroteNull = TRUE;
4263     }
4264 
4265     TRACE("EM_GETLINE: got %u characters\n", nMaxChars - nCharsLeft);
4266     return nMaxChars - nCharsLeft - (wroteNull ? 1 : 0);
4267   }
4268   case EM_GETLINECOUNT:
4269   {
4270     ME_DisplayItem *item = editor->pBuffer->pLast;
4271     int nRows = editor->total_rows;
4272     ME_DisplayItem *prev_para = NULL, *last_para = NULL;
4273 
4274     last_para = ME_FindItemBack(item, diRun);
4275     prev_para = ME_FindItemBack(last_para, diRun);
4276     assert(last_para);
4277     assert(last_para->member.run.nFlags & MERF_ENDPARA);
4278     if (editor->bEmulateVersion10 && prev_para &&
4279         last_para->member.run.nCharOfs == 0 &&
4280         prev_para->member.run.len == 1 &&
4281         *get_text( &prev_para->member.run, 0 ) == '\r')
4282     {
4283       /* In 1.0 emulation, the last solitary \r at the very end of the text
4284          (if one exists) is NOT a line break.
4285          FIXME: this is an ugly hack. This should have a more regular model. */
4286       nRows--;
4287     }
4288 
4289     TRACE("EM_GETLINECOUNT: nRows==%d\n", nRows);
4290     return max(1, nRows);
4291   }
4292   case EM_LINEFROMCHAR:
4293   {
4294     if (wParam == -1)
4295       return ME_RowNumberFromCharOfs(editor, ME_GetCursorOfs(&editor->pCursors[1]));
4296     else
4297       return ME_RowNumberFromCharOfs(editor, wParam);
4298   }
4299   case EM_EXLINEFROMCHAR:
4300   {
4301     if (lParam == -1)
4302       return ME_RowNumberFromCharOfs(editor, ME_GetCursorOfs(&editor->pCursors[1]));
4303     else
4304       return ME_RowNumberFromCharOfs(editor, lParam);
4305   }
4306   case EM_LINEINDEX:
4307   {
4308     ME_DisplayItem *item, *para;
4309     int nCharOfs;
4310 
4311     if (wParam == -1)
4312       item = ME_FindItemBack(editor->pCursors[0].pRun, diStartRow);
4313     else
4314       item = ME_FindRowWithNumber(editor, wParam);
4315     if (!item)
4316       return -1;
4317     para = ME_GetParagraph(item);
4318     item = ME_FindItemFwd(item, diRun);
4319     nCharOfs = para->member.para.nCharOfs + item->member.run.nCharOfs;
4320     TRACE("EM_LINEINDEX: nCharOfs==%d\n", nCharOfs);
4321     return nCharOfs;
4322   }
4323   case EM_LINELENGTH:
4324   {
4325     ME_DisplayItem *item, *item_end;
4326     int nChars = 0, nThisLineOfs = 0, nNextLineOfs = 0;
4327     ME_DisplayItem *para, *run;
4328 
4329     if (wParam > ME_GetTextLength(editor))
4330       return 0;
4331     if (wParam == -1)
4332     {
4333       FIXME("EM_LINELENGTH: returning number of unselected characters on lines with selection unsupported.\n");
4334       return 0;
4335     }
4336     ME_RunOfsFromCharOfs(editor, wParam, &para, &run, NULL);
4337     item = ME_RowStart(run);
4338     nThisLineOfs = ME_CharOfsFromRunOfs(editor, para, ME_FindItemFwd(item, diRun), 0);
4339     item_end = ME_FindItemFwd(item, diStartRowOrParagraphOrEnd);
4340     if (item_end->type == diStartRow) {
4341       nNextLineOfs = ME_CharOfsFromRunOfs(editor, para, ME_FindItemFwd(item_end, diRun), 0);
4342     } else {
4343       ME_DisplayItem *endRun = ME_FindItemBack(item_end, diRun);
4344       assert(endRun && endRun->member.run.nFlags & MERF_ENDPARA);
4345       nNextLineOfs = item_end->member.para.nCharOfs - endRun->member.run.len;
4346     }
4347     nChars = nNextLineOfs - nThisLineOfs;
4348     TRACE("EM_LINELENGTH(%ld)==%d\n",wParam, nChars);
4349     return nChars;
4350   }
4351   case EM_EXLIMITTEXT:
4352   {
4353     if ((int)lParam < 0)
4354      return 0;
4355     if (lParam == 0)
4356       editor->nTextLimit = 65536;
4357     else
4358       editor->nTextLimit = (int) lParam;
4359     return 0;
4360   }
4361   case EM_LIMITTEXT:
4362   {
4363     if (wParam == 0)
4364       editor->nTextLimit = 65536;
4365     else
4366       editor->nTextLimit = (int) wParam;
4367     return 0;
4368   }
4369   case EM_GETLIMITTEXT:
4370   {
4371     return editor->nTextLimit;
4372   }
4373   case EM_FINDTEXT:
4374   {
4375     LRESULT r;
4376     if(!unicode){
4377       FINDTEXTA *ft = (FINDTEXTA *)lParam;
4378       int nChars = MultiByteToWideChar(CP_ACP, 0, ft->lpstrText, -1, NULL, 0);
4379       WCHAR *tmp;
4380 
4381       if ((tmp = heap_alloc(nChars * sizeof(*tmp))) != NULL)
4382         MultiByteToWideChar(CP_ACP, 0, ft->lpstrText, -1, tmp, nChars);
4383       r = ME_FindText(editor, wParam, &ft->chrg, tmp, NULL);
4384       heap_free(tmp);
4385     }else{
4386       FINDTEXTW *ft = (FINDTEXTW *)lParam;
4387       r = ME_FindText(editor, wParam, &ft->chrg, ft->lpstrText, NULL);
4388     }
4389     return r;
4390   }
4391   case EM_FINDTEXTEX:
4392   {
4393     LRESULT r;
4394     if(!unicode){
4395       FINDTEXTEXA *ex = (FINDTEXTEXA *)lParam;
4396       int nChars = MultiByteToWideChar(CP_ACP, 0, ex->lpstrText, -1, NULL, 0);
4397       WCHAR *tmp;
4398 
4399       if ((tmp = heap_alloc(nChars * sizeof(*tmp))) != NULL)
4400         MultiByteToWideChar(CP_ACP, 0, ex->lpstrText, -1, tmp, nChars);
4401       r = ME_FindText(editor, wParam, &ex->chrg, tmp, &ex->chrgText);
4402       heap_free(tmp);
4403     }else{
4404       FINDTEXTEXW *ex = (FINDTEXTEXW *)lParam;
4405       r = ME_FindText(editor, wParam, &ex->chrg, ex->lpstrText, &ex->chrgText);
4406     }
4407     return r;
4408   }
4409   case EM_FINDTEXTW:
4410   {
4411     FINDTEXTW *ft = (FINDTEXTW *)lParam;
4412     return ME_FindText(editor, wParam, &ft->chrg, ft->lpstrText, NULL);
4413   }
4414   case EM_FINDTEXTEXW:
4415   {
4416     FINDTEXTEXW *ex = (FINDTEXTEXW *)lParam;
4417     return ME_FindText(editor, wParam, &ex->chrg, ex->lpstrText, &ex->chrgText);
4418   }
4419   case EM_GETZOOM:
4420     if (!wParam || !lParam)
4421       return FALSE;
4422     *(int *)wParam = editor->nZoomNumerator;
4423     *(int *)lParam = editor->nZoomDenominator;
4424     return TRUE;
4425   case EM_SETZOOM:
4426     return ME_SetZoom(editor, wParam, lParam);
4427   case EM_CHARFROMPOS:
4428   {
4429     ME_Cursor cursor;
4430     if (ME_CharFromPos(editor, ((POINTL *)lParam)->x, ((POINTL *)lParam)->y,
4431                        &cursor, NULL))
4432       return ME_GetCursorOfs(&cursor);
4433     else
4434       return -1;
4435   }
4436   case EM_POSFROMCHAR:
4437   {
4438     ME_DisplayItem *pPara, *pRun;
4439     int nCharOfs, nOffset, nLength;
4440     POINTL pt = {0,0};
4441 
4442     nCharOfs = wParam;
4443     /* detect which API version we're dealing with */
4444     if (wParam >= 0x40000)
4445         nCharOfs = lParam;
4446     nLength = ME_GetTextLength(editor);
4447     nCharOfs = min(nCharOfs, nLength);
4448     nCharOfs = max(nCharOfs, 0);
4449 
4450     ME_RunOfsFromCharOfs(editor, nCharOfs, &pPara, &pRun, &nOffset);
4451     assert(pRun->type == diRun);
4452     pt.y = pRun->member.run.pt.y;
4453     pt.x = pRun->member.run.pt.x + ME_PointFromChar(editor, &pRun->member.run, nOffset, TRUE);
4454     pt.y += pPara->member.para.pt.y + editor->rcFormat.top;
4455     pt.x += editor->rcFormat.left;
4456 
4457     pt.x -= editor->horz_si.nPos;
4458     pt.y -= editor->vert_si.nPos;
4459 
4460     if (wParam >= 0x40000) {
4461         *(POINTL *)wParam = pt;
4462     }
4463     return (wParam >= 0x40000) ? 0 : MAKELONG( pt.x, pt.y );
4464   }
4465   case WM_CREATE:
4466     return ME_WmCreate(editor, lParam, unicode);
4467   case WM_DESTROY:
4468     ME_DestroyEditor(editor);
4469     return 0;
4470   case WM_SETCURSOR:
4471   {
4472     POINT cursor_pos;
4473     if (wParam == (WPARAM)editor->hWnd && GetCursorPos(&cursor_pos) &&
4474         ScreenToClient(editor->hWnd, &cursor_pos))
4475       ME_LinkNotify(editor, msg, 0, MAKELPARAM(cursor_pos.x, cursor_pos.y));
4476     return ME_SetCursor(editor);
4477   }
4478   case WM_LBUTTONDBLCLK:
4479   case WM_LBUTTONDOWN:
4480   {
4481     ME_CommitUndo(editor); /* End coalesced undos for typed characters */
4482     if ((editor->nEventMask & ENM_MOUSEEVENTS) &&
4483         !ME_FilterEvent(editor, msg, &wParam, &lParam))
4484       return 0;
4485     ITextHost_TxSetFocus(editor->texthost);
4486     ME_LButtonDown(editor, (short)LOWORD(lParam), (short)HIWORD(lParam),
4487                    ME_CalculateClickCount(editor, msg, wParam, lParam));
4488     ITextHost_TxSetCapture(editor->texthost, TRUE);
4489     editor->bMouseCaptured = TRUE;
4490     ME_LinkNotify(editor, msg, wParam, lParam);
4491     if (!ME_SetCursor(editor)) goto do_default;
4492     break;
4493   }
4494   case WM_MOUSEMOVE:
4495     if ((editor->nEventMask & ENM_MOUSEEVENTS) &&
4496         !ME_FilterEvent(editor, msg, &wParam, &lParam))
4497       return 0;
4498     if (editor->bMouseCaptured)
4499       ME_MouseMove(editor, (short)LOWORD(lParam), (short)HIWORD(lParam));
4500     else
4501       ME_LinkNotify(editor, msg, wParam, lParam);
4502     /* Set cursor if mouse is captured, since WM_SETCURSOR won't be received. */
4503     if (editor->bMouseCaptured)
4504         ME_SetCursor(editor);
4505     break;
4506   case WM_LBUTTONUP:
4507     if (editor->bMouseCaptured) {
4508       ITextHost_TxSetCapture(editor->texthost, FALSE);
4509       editor->bMouseCaptured = FALSE;
4510     }
4511     if (editor->nSelectionType == stDocument)
4512       editor->nSelectionType = stPosition;
4513     if ((editor->nEventMask & ENM_MOUSEEVENTS) &&
4514         !ME_FilterEvent(editor, msg, &wParam, &lParam))
4515       return 0;
4516     else
4517     {
4518       ME_SetCursor(editor);
4519       ME_LinkNotify(editor, msg, wParam, lParam);
4520     }
4521     break;
4522   case WM_RBUTTONUP:
4523   case WM_RBUTTONDOWN:
4524   case WM_RBUTTONDBLCLK:
4525     ME_CommitUndo(editor); /* End coalesced undos for typed characters */
4526     if ((editor->nEventMask & ENM_MOUSEEVENTS) &&
4527         !ME_FilterEvent(editor, msg, &wParam, &lParam))
4528       return 0;
4529     ME_LinkNotify(editor, msg, wParam, lParam);
4530     goto do_default;
4531   case WM_CONTEXTMENU:
4532     if (!ME_ShowContextMenu(editor, (short)LOWORD(lParam), (short)HIWORD(lParam)))
4533       goto do_default;
4534     break;
4535   case WM_SETFOCUS:
4536     editor->bHaveFocus = TRUE;
4537     create_caret(editor);
4538     update_caret(editor);
4539     ME_SendOldNotify(editor, EN_SETFOCUS);
4540     if (!editor->bHideSelection && !(editor->styleFlags & ES_NOHIDESEL))
4541         ME_InvalidateSelection( editor );
4542     return 0;
4543   case WM_KILLFOCUS:
4544     ME_CommitUndo(editor); /* End coalesced undos for typed characters */
4545     editor->bHaveFocus = FALSE;
4546     editor->wheel_remain = 0;
4547     hide_caret(editor);
4548     DestroyCaret();
4549     ME_SendOldNotify(editor, EN_KILLFOCUS);
4550     if (!editor->bHideSelection && !(editor->styleFlags & ES_NOHIDESEL))
4551         ME_InvalidateSelection( editor );
4552     return 0;
4553   case WM_COMMAND:
4554     TRACE("editor wnd command = %d\n", LOWORD(wParam));
4555     return 0;
4556   case WM_KEYUP:
4557     if ((editor->nEventMask & ENM_KEYEVENTS) &&
4558         !ME_FilterEvent(editor, msg, &wParam, &lParam))
4559       return 0;
4560     goto do_default;
4561   case WM_KEYDOWN:
4562     if ((editor->nEventMask & ENM_KEYEVENTS) &&
4563         !ME_FilterEvent(editor, msg, &wParam, &lParam))
4564       return 0;
4565     if (ME_KeyDown(editor, LOWORD(wParam)))
4566       return 0;
4567     goto do_default;
4568   case WM_CHAR:
4569     if ((editor->nEventMask & ENM_KEYEVENTS) &&
4570         !ME_FilterEvent(editor, msg, &wParam, &lParam))
4571       return 0;
4572     return ME_Char(editor, wParam, lParam, unicode);
4573   case WM_UNICHAR:
4574     if (unicode)
4575     {
4576         if(wParam == UNICODE_NOCHAR) return TRUE;
4577         if(wParam <= 0x000fffff)
4578         {
4579             if(wParam > 0xffff) /* convert to surrogates */
4580             {
4581                 wParam -= 0x10000;
4582                 ME_Char(editor, (wParam >> 10) + 0xd800, 0, TRUE);
4583                 ME_Char(editor, (wParam & 0x03ff) + 0xdc00, 0, TRUE);
4584             } else {
4585               ME_Char(editor, wParam, 0, TRUE);
4586             }
4587         }
4588         return 0;
4589     }
4590     break;
4591   case EM_STOPGROUPTYPING:
4592     ME_CommitUndo(editor); /* End coalesced undos for typed characters */
4593     return 0;
4594   case WM_HSCROLL:
4595   {
4596     const int scrollUnit = 7;
4597 
4598     switch(LOWORD(wParam))
4599     {
4600       case SB_LEFT:
4601         ME_ScrollAbs(editor, 0, 0);
4602         break;
4603       case SB_RIGHT:
4604         ME_ScrollAbs(editor,
4605                      editor->horz_si.nMax - (int)editor->horz_si.nPage,
4606                      editor->vert_si.nMax - (int)editor->vert_si.nPage);
4607         break;
4608       case SB_LINELEFT:
4609         ME_ScrollLeft(editor, scrollUnit);
4610         break;
4611       case SB_LINERIGHT:
4612         ME_ScrollRight(editor, scrollUnit);
4613         break;
4614       case SB_PAGELEFT:
4615         ME_ScrollLeft(editor, editor->sizeWindow.cx);
4616         break;
4617       case SB_PAGERIGHT:
4618         ME_ScrollRight(editor, editor->sizeWindow.cx);
4619         break;
4620       case SB_THUMBTRACK:
4621       case SB_THUMBPOSITION:
4622       {
4623         int pos = HIWORD(wParam);
4624         if (editor->horz_si.nMax > 0xffff)
4625           pos = MulDiv(pos, editor->horz_si.nMax, 0xffff);
4626         ME_HScrollAbs(editor, pos);
4627         break;
4628       }
4629     }
4630     break;
4631   }
4632   case EM_SCROLL: /* fall through */
4633   case WM_VSCROLL:
4634   {
4635     int origNPos;
4636     int lineHeight = get_default_line_height( editor );
4637 
4638     origNPos = editor->vert_si.nPos;
4639 
4640     switch(LOWORD(wParam))
4641     {
4642       case SB_TOP:
4643         ME_ScrollAbs(editor, 0, 0);
4644         break;
4645       case SB_BOTTOM:
4646         ME_ScrollAbs(editor,
4647                      editor->horz_si.nMax - (int)editor->horz_si.nPage,
4648                      editor->vert_si.nMax - (int)editor->vert_si.nPage);
4649         break;
4650       case SB_LINEUP:
4651         ME_ScrollUp(editor,lineHeight);
4652         break;
4653       case SB_LINEDOWN:
4654         ME_ScrollDown(editor,lineHeight);
4655         break;
4656       case SB_PAGEUP:
4657         ME_ScrollUp(editor,editor->sizeWindow.cy);
4658         break;
4659       case SB_PAGEDOWN:
4660         ME_ScrollDown(editor,editor->sizeWindow.cy);
4661         break;
4662       case SB_THUMBTRACK:
4663       case SB_THUMBPOSITION:
4664       {
4665         int pos = HIWORD(wParam);
4666         if (editor->vert_si.nMax > 0xffff)
4667           pos = MulDiv(pos, editor->vert_si.nMax, 0xffff);
4668         ME_VScrollAbs(editor, pos);
4669         break;
4670       }
4671     }
4672     if (msg == EM_SCROLL)
4673       return 0x00010000 | (((editor->vert_si.nPos - origNPos)/lineHeight) & 0xffff);
4674     break;
4675   }
4676   case WM_MOUSEWHEEL:
4677   {
4678     int delta;
4679     BOOL ctrl_is_down;
4680 
4681     if ((editor->nEventMask & ENM_MOUSEEVENTS) &&
4682         !ME_FilterEvent(editor, msg, &wParam, &lParam))
4683       return 0;
4684 
4685     ctrl_is_down = GetKeyState(VK_CONTROL) & 0x8000;
4686 
4687     delta = GET_WHEEL_DELTA_WPARAM(wParam);
4688 
4689     /* if scrolling changes direction, ignore left overs */
4690     if ((delta < 0 && editor->wheel_remain < 0) ||
4691         (delta > 0 && editor->wheel_remain > 0))
4692       editor->wheel_remain += delta;
4693     else
4694       editor->wheel_remain = delta;
4695 
4696     if (editor->wheel_remain)
4697     {
4698       if (ctrl_is_down) {
4699         int numerator;
4700         if (!editor->nZoomNumerator || !editor->nZoomDenominator)
4701         {
4702           numerator = 100;
4703         } else {
4704           numerator = editor->nZoomNumerator * 100 / editor->nZoomDenominator;
4705         }
4706         numerator += calc_wheel_change( &editor->wheel_remain, 10 );
4707         if (numerator >= 10 && numerator <= 500)
4708           ME_SetZoom(editor, numerator, 100);
4709       } else {
4710         UINT max_lines = 3;
4711         int lines = 0;
4712 
4713         SystemParametersInfoW( SPI_GETWHEELSCROLLLINES, 0, &max_lines, 0 );
4714         if (max_lines)
4715           lines = calc_wheel_change( &editor->wheel_remain, (int)max_lines );
4716         if (lines)
4717           ME_ScrollDown( editor, -lines * get_default_line_height( editor ) );
4718       }
4719     }
4720     break;
4721   }
4722   case EM_GETRECT:
4723   {
4724     *((RECT *)lParam) = editor->rcFormat;
4725     if (editor->bDefaultFormatRect)
4726       ((RECT *)lParam)->left -= editor->selofs;
4727     return 0;
4728   }
4729   case EM_SETRECT:
4730   case EM_SETRECTNP:
4731   {
4732     if (lParam)
4733     {
4734       int border = 0;
4735       RECT clientRect;
4736       RECT *rc = (RECT *)lParam;
4737 
4738       border = editor->exStyleFlags & WS_EX_CLIENTEDGE ? 1 : 0;
4739       ITextHost_TxGetClientRect(editor->texthost, &clientRect);
4740       if (wParam == 0)
4741       {
4742         editor->rcFormat.top = max(0, rc->top - border);
4743         editor->rcFormat.left = max(0, rc->left - border);
4744         editor->rcFormat.bottom = min(clientRect.bottom, rc->bottom);
4745         editor->rcFormat.right = min(clientRect.right, rc->right + border);
4746       } else if (wParam == 1) {
4747         /* MSDN incorrectly says a wParam value of 1 causes the
4748          * lParam rect to be used as a relative offset,
4749          * however, the tests show it just prevents min/max bound
4750          * checking. */
4751         editor->rcFormat.top = rc->top - border;
4752         editor->rcFormat.left = rc->left - border;
4753         editor->rcFormat.bottom = rc->bottom;
4754         editor->rcFormat.right = rc->right + border;
4755       } else {
4756         return 0;
4757       }
4758       editor->bDefaultFormatRect = FALSE;
4759     }
4760     else
4761     {
4762       ME_SetDefaultFormatRect(editor);
4763       editor->bDefaultFormatRect = TRUE;
4764     }
4765     ME_MarkAllForWrapping(editor);
4766     ME_WrapMarkedParagraphs(editor);
4767     ME_UpdateScrollBar(editor);
4768     if (msg != EM_SETRECTNP)
4769       ME_Repaint(editor);
4770     return 0;
4771   }
4772   case EM_REQUESTRESIZE:
4773     ME_SendRequestResize(editor, TRUE);
4774     return 0;
4775   case WM_SETREDRAW:
4776     goto do_default;
4777   case WM_WINDOWPOSCHANGED:
4778   {
4779     RECT clientRect;
4780     WINDOWPOS *winpos = (WINDOWPOS *)lParam;
4781 
4782     if (winpos->flags & SWP_NOCLIENTSIZE) goto do_default;
4783     ITextHost_TxGetClientRect(editor->texthost, &clientRect);
4784     if (editor->bDefaultFormatRect) {
4785       ME_SetDefaultFormatRect(editor);
4786     } else {
4787       editor->rcFormat.right += clientRect.right - editor->prevClientRect.right;
4788       editor->rcFormat.bottom += clientRect.bottom - editor->prevClientRect.bottom;
4789     }
4790     editor->prevClientRect = clientRect;
4791     ME_RewrapRepaint(editor);
4792     goto do_default;
4793   }
4794 #ifndef __REACTOS__
4795   /* IME messages to make richedit controls IME aware */
4796 #endif
4797   case WM_IME_SETCONTEXT:
4798 #ifdef __REACTOS__
4799   {
4800     if (FALSE) /* FIXME: Condition */
4801       lParam &= ~ISC_SHOWUICOMPOSITIONWINDOW;
4802 
4803     if (wParam)
4804     {
4805       HIMC hIMC = ImmGetContext(editor->hWnd);
4806       LPINPUTCONTEXTDX pIC = (LPINPUTCONTEXTDX)ImmLockIMC(hIMC);
4807       if (pIC)
4808       {
4809         pIC->dwUIFlags &= ~0x40000;
4810         ImmUnlockIMC(hIMC);
4811       }
4812       if (FALSE) /* FIXME: Condition */
4813         ImmNotifyIME(hIMC, NI_COMPOSITIONSTR, CPS_CANCEL, 0);
4814       ImmReleaseContext(editor->hWnd, hIMC);
4815     }
4816 
4817     return DefWindowProcW(editor->hWnd, msg, wParam, lParam);
4818   }
4819 #endif
4820   case WM_IME_CONTROL:
4821 #ifdef __REACTOS__
4822     return DefWindowProcW(editor->hWnd, msg, wParam, lParam);
4823 #endif
4824   case WM_IME_SELECT:
4825 #ifdef __REACTOS__
4826     return DefWindowProcW(editor->hWnd, msg, wParam, lParam);
4827 #endif
4828   case WM_IME_COMPOSITIONFULL:
4829     return 0;
4830   case WM_IME_STARTCOMPOSITION:
4831   {
4832 #ifdef __REACTOS__
4833     return DefWindowProcW(editor->hWnd, msg, wParam, lParam);
4834 #else
4835     editor->imeStartIndex=ME_GetCursorOfs(&editor->pCursors[0]);
4836     ME_DeleteSelection(editor);
4837     ME_CommitUndo(editor);
4838     ME_UpdateRepaint(editor, FALSE);
4839 #endif
4840     return 0;
4841   }
4842   case WM_IME_COMPOSITION:
4843   {
4844     HIMC hIMC;
4845 
4846     ME_Style *style = ME_GetInsertStyle(editor, 0);
4847     hIMC = ITextHost_TxImmGetContext(editor->texthost);
4848     ME_DeleteSelection(editor);
4849     ME_SaveTempStyle(editor, style);
4850     if (lParam & (GCS_RESULTSTR|GCS_COMPSTR))
4851     {
4852         LPWSTR lpCompStr = NULL;
4853         DWORD dwBufLen;
4854         DWORD dwIndex = lParam & GCS_RESULTSTR;
4855         if (!dwIndex)
4856           dwIndex = GCS_COMPSTR;
4857 
4858         dwBufLen = ImmGetCompositionStringW(hIMC, dwIndex, NULL, 0);
4859         lpCompStr = HeapAlloc(GetProcessHeap(),0,dwBufLen + sizeof(WCHAR));
4860         ImmGetCompositionStringW(hIMC, dwIndex, lpCompStr, dwBufLen);
4861         lpCompStr[dwBufLen/sizeof(WCHAR)] = 0;
4862 #ifndef __REACTOS__
4863         ME_InsertTextFromCursor(editor,0,lpCompStr,dwBufLen/sizeof(WCHAR),style);
4864 #endif
4865         HeapFree(GetProcessHeap(), 0, lpCompStr);
4866 
4867 #ifndef __REACTOS__
4868         if (dwIndex == GCS_COMPSTR)
4869           set_selection_cursors(editor,editor->imeStartIndex,
4870                           editor->imeStartIndex + dwBufLen/sizeof(WCHAR));
4871 #endif
4872     }
4873     ME_ReleaseStyle(style);
4874     ME_CommitUndo(editor);
4875     ME_UpdateRepaint(editor, FALSE);
4876 #ifdef __REACTOS__
4877     return DefWindowProcW(editor->hWnd, msg, wParam, lParam);
4878 #else
4879     return 0;
4880 #endif
4881   }
4882   case WM_IME_ENDCOMPOSITION:
4883   {
4884 #ifdef __REACTOS__
4885     return DefWindowProcW(editor->hWnd, msg, wParam, lParam);
4886 #else
4887     ME_DeleteSelection(editor);
4888     editor->imeStartIndex=-1;
4889     return 0;
4890 #endif
4891   }
4892   case EM_GETOLEINTERFACE:
4893   {
4894     if (!editor->reOle)
4895       if (!CreateIRichEditOle(NULL, editor, (LPVOID *)&editor->reOle))
4896         return 0;
4897     if (IUnknown_QueryInterface(editor->reOle, &IID_IRichEditOle, (LPVOID *)lParam) == S_OK)
4898       return 1;
4899     return 0;
4900   }
4901   case EM_GETPASSWORDCHAR:
4902   {
4903     return editor->cPasswordMask;
4904   }
4905   case EM_SETOLECALLBACK:
4906     if(editor->lpOleCallback)
4907       IRichEditOleCallback_Release(editor->lpOleCallback);
4908     editor->lpOleCallback = (IRichEditOleCallback*)lParam;
4909     if(editor->lpOleCallback)
4910       IRichEditOleCallback_AddRef(editor->lpOleCallback);
4911     return TRUE;
4912   case EM_GETWORDBREAKPROC:
4913     return (LRESULT)editor->pfnWordBreak;
4914   case EM_SETWORDBREAKPROC:
4915   {
4916     EDITWORDBREAKPROCW pfnOld = editor->pfnWordBreak;
4917 
4918     editor->pfnWordBreak = (EDITWORDBREAKPROCW)lParam;
4919     return (LRESULT)pfnOld;
4920   }
4921   case EM_GETTEXTMODE:
4922     return editor->mode;
4923   case EM_SETTEXTMODE:
4924   {
4925     int mask = 0;
4926     int changes = 0;
4927 
4928     if (ME_GetTextLength(editor) ||
4929         !list_empty( &editor->undo_stack ) || !list_empty( &editor->redo_stack ))
4930       return E_UNEXPECTED;
4931 
4932     /* Check for mutually exclusive flags in adjacent bits of wParam */
4933     if ((wParam & (TM_RICHTEXT | TM_MULTILEVELUNDO | TM_MULTICODEPAGE)) &
4934         (wParam & (TM_PLAINTEXT | TM_SINGLELEVELUNDO | TM_SINGLECODEPAGE)) << 1)
4935       return E_INVALIDARG;
4936 
4937     if (wParam & (TM_RICHTEXT | TM_PLAINTEXT))
4938     {
4939       mask |= TM_RICHTEXT | TM_PLAINTEXT;
4940       changes |= wParam & (TM_RICHTEXT | TM_PLAINTEXT);
4941       if (wParam & TM_PLAINTEXT) {
4942         /* Clear selection since it should be possible to select the
4943          * end of text run for rich text */
4944         ME_InvalidateSelection(editor);
4945         ME_SetCursorToStart(editor, &editor->pCursors[0]);
4946         editor->pCursors[1] = editor->pCursors[0];
4947         /* plain text can only have the default style. */
4948         ME_ClearTempStyle(editor);
4949         ME_AddRefStyle(editor->pBuffer->pDefaultStyle);
4950         ME_ReleaseStyle(editor->pCursors[0].pRun->member.run.style);
4951         editor->pCursors[0].pRun->member.run.style = editor->pBuffer->pDefaultStyle;
4952       }
4953     }
4954     /* FIXME: Currently no support for undo level and code page options */
4955     editor->mode = (editor->mode & ~mask) | changes;
4956     return 0;
4957   }
4958   case EM_SETPASSWORDCHAR:
4959   {
4960     editor->cPasswordMask = wParam;
4961     ME_RewrapRepaint(editor);
4962     return 0;
4963   }
4964   case EM_SETTARGETDEVICE:
4965     if (wParam == 0)
4966     {
4967       BOOL new = (lParam == 0 && (editor->styleFlags & ES_MULTILINE));
4968       if (editor->nAvailWidth || editor->bWordWrap != new)
4969       {
4970         editor->bWordWrap = new;
4971         editor->nAvailWidth = 0; /* wrap to client area */
4972         ME_RewrapRepaint(editor);
4973       }
4974     } else {
4975       int width = max(0, lParam);
4976       if ((editor->styleFlags & ES_MULTILINE) &&
4977           (!editor->bWordWrap || editor->nAvailWidth != width))
4978       {
4979         editor->nAvailWidth = width;
4980         editor->bWordWrap = TRUE;
4981         ME_RewrapRepaint(editor);
4982       }
4983       FIXME("EM_SETTARGETDEVICE doesn't use non-NULL target devices\n");
4984     }
4985     return TRUE;
4986   default:
4987   do_default:
4988     *phresult = S_FALSE;
4989     break;
4990   }
4991   return 0L;
4992 }
4993 
create_windowed_editor(HWND hwnd,CREATESTRUCTW * create,BOOL emulate_10)4994 static BOOL create_windowed_editor(HWND hwnd, CREATESTRUCTW *create, BOOL emulate_10)
4995 {
4996     ITextHost *host = ME_CreateTextHost( hwnd, create, emulate_10 );
4997     ME_TextEditor *editor;
4998 
4999     if (!host) return FALSE;
5000 
5001     editor = ME_MakeEditor( host, emulate_10 );
5002     if (!editor)
5003     {
5004         ITextHost_Release( host );
5005         return FALSE;
5006     }
5007 
5008     editor->exStyleFlags = GetWindowLongW( hwnd, GWL_EXSTYLE );
5009     editor->styleFlags |= GetWindowLongW( hwnd, GWL_STYLE ) & ES_WANTRETURN;
5010     editor->hWnd = hwnd; /* FIXME: Remove editor's dependence on hWnd */
5011     editor->hwndParent = create->hwndParent;
5012 
5013     SetWindowLongPtrW( hwnd, 0, (LONG_PTR)editor );
5014 
5015     return TRUE;
5016 }
5017 
RichEditWndProc_common(HWND hWnd,UINT msg,WPARAM wParam,LPARAM lParam,BOOL unicode)5018 static LRESULT RichEditWndProc_common(HWND hWnd, UINT msg, WPARAM wParam,
5019                                       LPARAM lParam, BOOL unicode)
5020 {
5021   ME_TextEditor *editor;
5022   HRESULT hresult;
5023   LRESULT lresult = 0;
5024 
5025   TRACE("enter hwnd %p msg %04x (%s) %lx %lx, unicode %d\n",
5026         hWnd, msg, get_msg_name(msg), wParam, lParam, unicode);
5027 
5028   editor = (ME_TextEditor *)GetWindowLongPtrW(hWnd, 0);
5029   if (!editor)
5030   {
5031     if (msg == WM_NCCREATE)
5032     {
5033       CREATESTRUCTW *pcs = (CREATESTRUCTW *)lParam;
5034 
5035       TRACE("WM_NCCREATE: hWnd %p style 0x%08x\n", hWnd, pcs->style);
5036       return create_windowed_editor( hWnd, pcs, FALSE );
5037     }
5038     else
5039     {
5040       return DefWindowProcW(hWnd, msg, wParam, lParam);
5041     }
5042   }
5043 
5044   switch (msg)
5045   {
5046     case WM_PAINT:
5047     {
5048       HDC hdc;
5049       RECT rc;
5050       PAINTSTRUCT ps;
5051       HBRUSH old_brush;
5052 
5053       update_caret(editor);
5054       hdc = BeginPaint(editor->hWnd, &ps);
5055       if (!editor->bEmulateVersion10 || (editor->nEventMask & ENM_UPDATE))
5056         ME_SendOldNotify(editor, EN_UPDATE);
5057       old_brush = SelectObject(hdc, editor->hbrBackground);
5058 
5059       /* Erase area outside of the formatting rectangle */
5060       if (ps.rcPaint.top < editor->rcFormat.top)
5061       {
5062         rc = ps.rcPaint;
5063         rc.bottom = editor->rcFormat.top;
5064         PatBlt(hdc, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, PATCOPY);
5065         ps.rcPaint.top = editor->rcFormat.top;
5066       }
5067       if (ps.rcPaint.bottom > editor->rcFormat.bottom) {
5068         rc = ps.rcPaint;
5069         rc.top = editor->rcFormat.bottom;
5070         PatBlt(hdc, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, PATCOPY);
5071         ps.rcPaint.bottom = editor->rcFormat.bottom;
5072       }
5073       if (ps.rcPaint.left < editor->rcFormat.left) {
5074         rc = ps.rcPaint;
5075         rc.right = editor->rcFormat.left;
5076         PatBlt(hdc, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, PATCOPY);
5077         ps.rcPaint.left = editor->rcFormat.left;
5078       }
5079       if (ps.rcPaint.right > editor->rcFormat.right) {
5080         rc = ps.rcPaint;
5081         rc.left = editor->rcFormat.right;
5082         PatBlt(hdc, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, PATCOPY);
5083         ps.rcPaint.right = editor->rcFormat.right;
5084       }
5085 
5086       ME_PaintContent(editor, hdc, &ps.rcPaint);
5087       SelectObject(hdc, old_brush);
5088       EndPaint(editor->hWnd, &ps);
5089       return 0;
5090     }
5091     case WM_ERASEBKGND:
5092     {
5093       HDC hDC = (HDC)wParam;
5094       RECT rc;
5095 
5096       if (GetUpdateRect(editor->hWnd, &rc, TRUE))
5097         FillRect(hDC, &rc, editor->hbrBackground);
5098       return 1;
5099     }
5100     case EM_SETOPTIONS:
5101     {
5102       DWORD dwStyle;
5103       const DWORD mask = ECO_VERTICAL | ECO_AUTOHSCROLL | ECO_AUTOVSCROLL |
5104                          ECO_NOHIDESEL | ECO_READONLY | ECO_WANTRETURN |
5105                          ECO_SELECTIONBAR;
5106       lresult = ME_HandleMessage(editor, msg, wParam, lParam, unicode, &hresult);
5107       dwStyle = GetWindowLongW(hWnd, GWL_STYLE);
5108       dwStyle = (dwStyle & ~mask) | (lresult & mask);
5109       SetWindowLongW(hWnd, GWL_STYLE, dwStyle);
5110       return lresult;
5111     }
5112     case EM_SETREADONLY:
5113     {
5114       DWORD dwStyle;
5115       lresult = ME_HandleMessage(editor, msg, wParam, lParam, unicode, &hresult);
5116       dwStyle = GetWindowLongW(hWnd, GWL_STYLE);
5117       dwStyle &= ~ES_READONLY;
5118       if (wParam)
5119         dwStyle |= ES_READONLY;
5120       SetWindowLongW(hWnd, GWL_STYLE, dwStyle);
5121       return lresult;
5122     }
5123     default:
5124       lresult = ME_HandleMessage(editor, msg, wParam, lParam, unicode, &hresult);
5125   }
5126 
5127   if (hresult == S_FALSE)
5128     lresult = DefWindowProcW(hWnd, msg, wParam, lParam);
5129 
5130   TRACE("exit hwnd %p msg %04x (%s) %lx %lx, unicode %d -> %lu\n",
5131         hWnd, msg, get_msg_name(msg), wParam, lParam, unicode, lresult);
5132 
5133   return lresult;
5134 }
5135 
RichEditWndProcW(HWND hWnd,UINT msg,WPARAM wParam,LPARAM lParam)5136 static LRESULT WINAPI RichEditWndProcW(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
5137 {
5138     BOOL unicode = TRUE;
5139 
5140     /* Under Win9x RichEdit20W returns ANSI strings, see the tests. */
5141     if (msg == WM_GETTEXT && (GetVersion() & 0x80000000))
5142         unicode = FALSE;
5143 
5144     return RichEditWndProc_common(hWnd, msg, wParam, lParam, unicode);
5145 }
5146 
RichEditWndProcA(HWND hWnd,UINT msg,WPARAM wParam,LPARAM lParam)5147 static LRESULT WINAPI RichEditWndProcA(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
5148 {
5149     return RichEditWndProc_common(hWnd, msg, wParam, lParam, FALSE);
5150 }
5151 
5152 /******************************************************************
5153  *        RichEditANSIWndProc (RICHED20.10)
5154  */
RichEditANSIWndProc(HWND hWnd,UINT msg,WPARAM wParam,LPARAM lParam)5155 LRESULT WINAPI RichEditANSIWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
5156 {
5157     return RichEditWndProcA(hWnd, msg, wParam, lParam);
5158 }
5159 
5160 /******************************************************************
5161  *        RichEdit10ANSIWndProc (RICHED20.9)
5162  */
RichEdit10ANSIWndProc(HWND hWnd,UINT msg,WPARAM wParam,LPARAM lParam)5163 LRESULT WINAPI RichEdit10ANSIWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
5164 {
5165   if (msg == WM_NCCREATE && !GetWindowLongPtrW(hWnd, 0))
5166   {
5167     CREATESTRUCTW *pcs = (CREATESTRUCTW *)lParam;
5168 
5169     TRACE("WM_NCCREATE: hWnd %p style 0x%08x\n", hWnd, pcs->style);
5170     return create_windowed_editor( hWnd, pcs, TRUE );
5171   }
5172   return RichEditANSIWndProc(hWnd, msg, wParam, lParam);
5173 }
5174 
ME_SendOldNotify(ME_TextEditor * editor,int nCode)5175 void ME_SendOldNotify(ME_TextEditor *editor, int nCode)
5176 {
5177   ITextHost_TxNotify(editor->texthost, nCode, NULL);
5178 }
5179 
5180 /* Fill buffer with srcChars unicode characters from the start cursor.
5181  *
5182  * buffer: destination buffer
5183  * buflen: length of buffer in characters excluding the NULL terminator.
5184  * start: start of editor text to copy into buffer.
5185  * srcChars: Number of characters to use from the editor text.
5186  * bCRLF: if true, replaces all end of lines with \r\n pairs.
5187  *
5188  * returns the number of characters written excluding the NULL terminator.
5189  *
5190  * The written text is always NULL terminated.
5191  */
ME_GetTextW(ME_TextEditor * editor,WCHAR * buffer,int buflen,const ME_Cursor * start,int srcChars,BOOL bCRLF,BOOL bEOP)5192 int ME_GetTextW(ME_TextEditor *editor, WCHAR *buffer, int buflen,
5193                 const ME_Cursor *start, int srcChars, BOOL bCRLF,
5194                 BOOL bEOP)
5195 {
5196   ME_DisplayItem *pRun, *pNextRun;
5197   const WCHAR *pStart = buffer;
5198   const WCHAR cr_lf[] = {'\r', '\n', 0};
5199   const WCHAR *str;
5200   int nLen;
5201 
5202   /* bCRLF flag is only honored in 2.0 and up. 1.0 must always return text verbatim */
5203   if (editor->bEmulateVersion10) bCRLF = FALSE;
5204 
5205   pRun = start->pRun;
5206   assert(pRun);
5207   pNextRun = ME_FindItemFwd(pRun, diRun);
5208 
5209   nLen = pRun->member.run.len - start->nOffset;
5210   str = get_text( &pRun->member.run, start->nOffset );
5211 
5212   while (srcChars && buflen && pNextRun)
5213   {
5214     int nFlags = pRun->member.run.nFlags;
5215 
5216     if (bCRLF && nFlags & MERF_ENDPARA && ~nFlags & MERF_ENDCELL)
5217     {
5218       if (buflen == 1) break;
5219       /* FIXME: native fails to reduce srcChars here for WM_GETTEXT or
5220        *        EM_GETTEXTEX, however, this is done for copying text which
5221        *        also uses this function. */
5222       srcChars -= min(nLen, srcChars);
5223       nLen = 2;
5224       str = cr_lf;
5225     } else {
5226       nLen = min(nLen, srcChars);
5227       srcChars -= nLen;
5228     }
5229 
5230     nLen = min(nLen, buflen);
5231     buflen -= nLen;
5232 
5233     CopyMemory(buffer, str, sizeof(WCHAR) * nLen);
5234 
5235     buffer += nLen;
5236 
5237     pRun = pNextRun;
5238     pNextRun = ME_FindItemFwd(pRun, diRun);
5239 
5240     nLen = pRun->member.run.len;
5241     str = get_text( &pRun->member.run, 0 );
5242   }
5243   /* append '\r' to the last paragraph. */
5244   if (pRun->next->type == diTextEnd && bEOP)
5245   {
5246     *buffer = '\r';
5247     buffer ++;
5248   }
5249   *buffer = 0;
5250   return buffer - pStart;
5251 }
5252 
ME_RegisterEditorClass(HINSTANCE hInstance)5253 static BOOL ME_RegisterEditorClass(HINSTANCE hInstance)
5254 {
5255   WNDCLASSW wcW;
5256   WNDCLASSA wcA;
5257 
5258   wcW.style = CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW | CS_GLOBALCLASS;
5259   wcW.lpfnWndProc = RichEditWndProcW;
5260   wcW.cbClsExtra = 0;
5261   wcW.cbWndExtra = sizeof(ME_TextEditor *);
5262   wcW.hInstance = NULL; /* hInstance would register DLL-local class */
5263   wcW.hIcon = NULL;
5264   wcW.hCursor = LoadCursorW(NULL, (LPWSTR)IDC_IBEAM);
5265   wcW.hbrBackground = GetStockObject(NULL_BRUSH);
5266   wcW.lpszMenuName = NULL;
5267 
5268   if (is_version_nt())
5269   {
5270     wcW.lpszClassName = RICHEDIT_CLASS20W;
5271     if (!RegisterClassW(&wcW)) return FALSE;
5272     wcW.lpszClassName = MSFTEDIT_CLASS;
5273     if (!RegisterClassW(&wcW)) return FALSE;
5274   }
5275   else
5276   {
5277     /* WNDCLASSA/W have the same layout */
5278     wcW.lpszClassName = (LPCWSTR)"RichEdit20W";
5279     if (!RegisterClassA((WNDCLASSA *)&wcW)) return FALSE;
5280     wcW.lpszClassName = (LPCWSTR)"RichEdit50W";
5281     if (!RegisterClassA((WNDCLASSA *)&wcW)) return FALSE;
5282   }
5283 
5284   wcA.style = CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW | CS_GLOBALCLASS;
5285   wcA.lpfnWndProc = RichEditWndProcA;
5286   wcA.cbClsExtra = 0;
5287   wcA.cbWndExtra = sizeof(ME_TextEditor *);
5288   wcA.hInstance = NULL; /* hInstance would register DLL-local class */
5289   wcA.hIcon = NULL;
5290   wcA.hCursor = LoadCursorW(NULL, (LPWSTR)IDC_IBEAM);
5291   wcA.hbrBackground = GetStockObject(NULL_BRUSH);
5292   wcA.lpszMenuName = NULL;
5293   wcA.lpszClassName = RICHEDIT_CLASS20A;
5294   if (!RegisterClassA(&wcA)) return FALSE;
5295   wcA.lpszClassName = "RichEdit50A";
5296   if (!RegisterClassA(&wcA)) return FALSE;
5297 
5298   return TRUE;
5299 }
5300 
REComboWndProc(HWND hWnd,UINT msg,WPARAM wParam,LPARAM lParam)5301 static LRESULT WINAPI REComboWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) {
5302   /* FIXME: Not implemented */
5303   TRACE("hWnd %p msg %04x (%s) %08lx %08lx\n",
5304         hWnd, msg, get_msg_name(msg), wParam, lParam);
5305   return DefWindowProcW(hWnd, msg, wParam, lParam);
5306 }
5307 
REListWndProc(HWND hWnd,UINT msg,WPARAM wParam,LPARAM lParam)5308 static LRESULT WINAPI REListWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) {
5309   /* FIXME: Not implemented */
5310   TRACE("hWnd %p msg %04x (%s) %08lx %08lx\n",
5311         hWnd, msg, get_msg_name(msg), wParam, lParam);
5312   return DefWindowProcW(hWnd, msg, wParam, lParam);
5313 }
5314 
5315 /******************************************************************
5316  *        REExtendedRegisterClass (RICHED20.8)
5317  *
5318  * FIXME undocumented
5319  * Need to check for errors and implement controls and callbacks
5320  */
REExtendedRegisterClass(void)5321 LRESULT WINAPI REExtendedRegisterClass(void)
5322 {
5323   WNDCLASSW wcW;
5324   UINT result;
5325 
5326   FIXME("semi stub\n");
5327 
5328   wcW.cbClsExtra = 0;
5329   wcW.cbWndExtra = 4;
5330   wcW.hInstance = NULL;
5331   wcW.hIcon = NULL;
5332   wcW.hCursor = NULL;
5333   wcW.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
5334   wcW.lpszMenuName = NULL;
5335 
5336   if (!ME_ListBoxRegistered)
5337   {
5338       wcW.style = CS_PARENTDC | CS_DBLCLKS | CS_GLOBALCLASS;
5339       wcW.lpfnWndProc = REListWndProc;
5340       wcW.lpszClassName = REListBox20W;
5341       if (RegisterClassW(&wcW)) ME_ListBoxRegistered = TRUE;
5342   }
5343 
5344   if (!ME_ComboBoxRegistered)
5345   {
5346       wcW.style = CS_PARENTDC | CS_DBLCLKS | CS_GLOBALCLASS | CS_VREDRAW | CS_HREDRAW;
5347       wcW.lpfnWndProc = REComboWndProc;
5348       wcW.lpszClassName = REComboBox20W;
5349       if (RegisterClassW(&wcW)) ME_ComboBoxRegistered = TRUE;
5350   }
5351 
5352   result = 0;
5353   if (ME_ListBoxRegistered)
5354       result += 1;
5355   if (ME_ComboBoxRegistered)
5356       result += 2;
5357 
5358   return result;
5359 }
5360 
wchar_comp(const void * key,const void * elem)5361 static int __cdecl wchar_comp( const void *key, const void *elem )
5362 {
5363     return *(const WCHAR *)key - *(const WCHAR *)elem;
5364 }
5365 
5366 /* neutral characters end the url if the next non-neutral character is a space character,
5367    otherwise they are included in the url. */
isurlneutral(WCHAR c)5368 static BOOL isurlneutral( WCHAR c )
5369 {
5370     /* NB this list is sorted */
5371     static const WCHAR neutral_chars[] = {'!','\"','\'','(',')',',','-','.',':',';','<','>','?','[',']','{','}'};
5372 
5373     /* Some shortcuts */
5374     if (isalnum( c )) return FALSE;
5375     if (c > neutral_chars[ARRAY_SIZE( neutral_chars ) - 1]) return FALSE;
5376 
5377     return !!bsearch( &c, neutral_chars, ARRAY_SIZE( neutral_chars ), sizeof(c), wchar_comp );
5378 }
5379 
5380 /**
5381  * This proc takes a selection, and scans it forward in order to select the span
5382  * of a possible URL candidate. A possible URL candidate must start with isalnum
5383  * or one of the following special characters: *|/\+%#@ and must consist entirely
5384  * of the characters allowed to start the URL, plus : (colon) which may occur
5385  * at most once, and not at either end.
5386  */
ME_FindNextURLCandidate(ME_TextEditor * editor,const ME_Cursor * start,int nChars,ME_Cursor * candidate_min,ME_Cursor * candidate_max)5387 static BOOL ME_FindNextURLCandidate(ME_TextEditor *editor,
5388                                     const ME_Cursor *start,
5389                                     int nChars,
5390                                     ME_Cursor *candidate_min,
5391                                     ME_Cursor *candidate_max)
5392 {
5393   ME_Cursor cursor = *start, neutral_end, space_end;
5394   BOOL candidateStarted = FALSE, quoted = FALSE;
5395   WCHAR c;
5396 
5397   while (nChars > 0)
5398   {
5399     WCHAR *str = get_text( &cursor.pRun->member.run, 0 );
5400     int run_len = cursor.pRun->member.run.len;
5401 
5402     nChars -= run_len - cursor.nOffset;
5403 
5404     /* Find start of candidate */
5405     if (!candidateStarted)
5406     {
5407       while (cursor.nOffset < run_len)
5408       {
5409         c = str[cursor.nOffset];
5410         if (!iswspace( c ) && !isurlneutral( c ))
5411         {
5412           *candidate_min = cursor;
5413           candidateStarted = TRUE;
5414           neutral_end.pPara = NULL;
5415           space_end.pPara = NULL;
5416           cursor.nOffset++;
5417           break;
5418         }
5419         quoted = (c == '<');
5420         cursor.nOffset++;
5421       }
5422     }
5423 
5424     /* Find end of candidate */
5425     if (candidateStarted)
5426     {
5427       while (cursor.nOffset < run_len)
5428       {
5429         c = str[cursor.nOffset];
5430         if (iswspace( c ))
5431         {
5432           if (quoted && c != '\r')
5433           {
5434             if (!space_end.pPara)
5435             {
5436               if (neutral_end.pPara)
5437                 space_end = neutral_end;
5438               else
5439                 space_end = cursor;
5440             }
5441           }
5442           else
5443             goto done;
5444         }
5445         else if (isurlneutral( c ))
5446         {
5447           if (quoted && c == '>')
5448           {
5449             neutral_end.pPara = NULL;
5450             space_end.pPara = NULL;
5451             goto done;
5452           }
5453           if (!neutral_end.pPara)
5454             neutral_end = cursor;
5455         }
5456         else
5457           neutral_end.pPara = NULL;
5458 
5459         cursor.nOffset++;
5460       }
5461     }
5462 
5463     cursor.nOffset = 0;
5464     if (!ME_NextRun(&cursor.pPara, &cursor.pRun, TRUE))
5465       goto done;
5466   }
5467 
5468 done:
5469   if (candidateStarted)
5470   {
5471     if (space_end.pPara)
5472       *candidate_max = space_end;
5473     else if (neutral_end.pPara)
5474       *candidate_max = neutral_end;
5475     else
5476       *candidate_max = cursor;
5477     return TRUE;
5478   }
5479   *candidate_max = *candidate_min = cursor;
5480   return FALSE;
5481 }
5482 
5483 /**
5484  * This proc evaluates the selection and returns TRUE if it can be considered an URL
5485  */
ME_IsCandidateAnURL(ME_TextEditor * editor,const ME_Cursor * start,int nChars)5486 static BOOL ME_IsCandidateAnURL(ME_TextEditor *editor, const ME_Cursor *start, int nChars)
5487 {
5488 #define MAX_PREFIX_LEN 9
5489   struct prefix_s {
5490     const WCHAR text[MAX_PREFIX_LEN];
5491     int length;
5492   }prefixes[] = {
5493     {{'p','r','o','s','p','e','r','o',':'}, 9},
5494     {{'t','e','l','n','e','t',':'}, 7},
5495     {{'g','o','p','h','e','r',':'}, 7},
5496     {{'m','a','i','l','t','o',':'}, 7},
5497     {{'h','t','t','p','s',':'}, 6},
5498     {{'f','i','l','e',':'}, 5},
5499     {{'n','e','w','s',':'}, 5},
5500     {{'w','a','i','s',':'}, 5},
5501     {{'n','n','t','p',':'}, 5},
5502     {{'h','t','t','p',':'}, 5},
5503     {{'w','w','w','.'}, 4},
5504     {{'f','t','p',':'}, 4},
5505   };
5506   WCHAR bufferW[MAX_PREFIX_LEN + 1];
5507   unsigned int i;
5508 
5509   ME_GetTextW(editor, bufferW, MAX_PREFIX_LEN, start, nChars, FALSE, FALSE);
5510   for (i = 0; i < ARRAY_SIZE(prefixes); i++)
5511   {
5512     if (nChars < prefixes[i].length) continue;
5513     if (!memcmp(prefixes[i].text, bufferW, prefixes[i].length * sizeof(WCHAR)))
5514       return TRUE;
5515   }
5516   return FALSE;
5517 #undef MAX_PREFIX_LEN
5518 }
5519 
5520 /**
5521  * This proc walks through the indicated selection and evaluates whether each
5522  * section identified by ME_FindNextURLCandidate and in-between sections have
5523  * their proper CFE_LINK attributes set or unset. If the CFE_LINK attribute is
5524  * not what it is supposed to be, this proc sets or unsets it as appropriate.
5525  *
5526  * Since this function can cause runs to be split, do not depend on the value
5527  * of the start cursor at the end of the function.
5528  *
5529  * nChars may be set to INT_MAX to update to the end of the text.
5530  *
5531  * Returns TRUE if at least one section was modified.
5532  */
ME_UpdateLinkAttribute(ME_TextEditor * editor,ME_Cursor * start,int nChars)5533 static BOOL ME_UpdateLinkAttribute(ME_TextEditor *editor, ME_Cursor *start, int nChars)
5534 {
5535   BOOL modified = FALSE;
5536   ME_Cursor startCur = *start;
5537 
5538   if (!editor->AutoURLDetect_bEnable) return FALSE;
5539 
5540   do
5541   {
5542     CHARFORMAT2W link;
5543     ME_Cursor candidateStart, candidateEnd;
5544 
5545     if (ME_FindNextURLCandidate(editor, &startCur, nChars,
5546                                 &candidateStart, &candidateEnd))
5547     {
5548       /* Section before candidate is not an URL */
5549       int cMin = ME_GetCursorOfs(&candidateStart);
5550       int cMax = ME_GetCursorOfs(&candidateEnd);
5551 
5552       if (!ME_IsCandidateAnURL(editor, &candidateStart, cMax - cMin))
5553         candidateStart = candidateEnd;
5554       nChars -= cMax - ME_GetCursorOfs(&startCur);
5555     }
5556     else
5557     {
5558       /* No more candidates until end of selection */
5559       nChars = 0;
5560     }
5561 
5562     if (startCur.pRun != candidateStart.pRun ||
5563         startCur.nOffset != candidateStart.nOffset)
5564     {
5565       /* CFE_LINK effect should be consistently unset */
5566       link.cbSize = sizeof(link);
5567       ME_GetCharFormat(editor, &startCur, &candidateStart, &link);
5568       if (!(link.dwMask & CFM_LINK) || (link.dwEffects & CFE_LINK))
5569       {
5570         /* CFE_LINK must be unset from this range */
5571         memset(&link, 0, sizeof(CHARFORMAT2W));
5572         link.cbSize = sizeof(link);
5573         link.dwMask = CFM_LINK;
5574         link.dwEffects = 0;
5575         ME_SetCharFormat(editor, &startCur, &candidateStart, &link);
5576         /* Update candidateEnd since setting character formats may split
5577          * runs, which can cause a cursor to be at an invalid offset within
5578          * a split run. */
5579         while (candidateEnd.nOffset >= candidateEnd.pRun->member.run.len)
5580         {
5581           candidateEnd.nOffset -= candidateEnd.pRun->member.run.len;
5582           candidateEnd.pRun = ME_FindItemFwd(candidateEnd.pRun, diRun);
5583         }
5584         modified = TRUE;
5585       }
5586     }
5587     if (candidateStart.pRun != candidateEnd.pRun ||
5588         candidateStart.nOffset != candidateEnd.nOffset)
5589     {
5590       /* CFE_LINK effect should be consistently set */
5591       link.cbSize = sizeof(link);
5592       ME_GetCharFormat(editor, &candidateStart, &candidateEnd, &link);
5593       if (!(link.dwMask & CFM_LINK) || !(link.dwEffects & CFE_LINK))
5594       {
5595         /* CFE_LINK must be set on this range */
5596         memset(&link, 0, sizeof(CHARFORMAT2W));
5597         link.cbSize = sizeof(link);
5598         link.dwMask = CFM_LINK;
5599         link.dwEffects = CFE_LINK;
5600         ME_SetCharFormat(editor, &candidateStart, &candidateEnd, &link);
5601         modified = TRUE;
5602       }
5603     }
5604     startCur = candidateEnd;
5605   } while (nChars > 0);
5606   return modified;
5607 }
5608