ReactOS  0.4.13-dev-73-gcfe54aa
editor.c
Go to the documentation of this file.
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 #include "res.h"
237 
238 #ifdef __REACTOS__
239 #include <reactos/undocuser.h>
240 #endif
241 
242 #define STACK_SIZE_DEFAULT 100
243 #define STACK_SIZE_MAX 1000
244 
245 #define TEXT_LIMIT_DEFAULT 32767
246 
248 
250 static BOOL ME_UpdateLinkAttribute(ME_TextEditor *editor, ME_Cursor *start, int nChars);
251 
252 static const WCHAR REListBox20W[] = {'R','E','L','i','s','t','B','o','x','2','0','W', 0};
253 static const WCHAR REComboBox20W[] = {'R','E','C','o','m','b','o','B','o','x','2','0','W', 0};
254 static HCURSOR hLeft;
255 
258 
261 
262 static inline BOOL is_version_nt(void)
263 {
264  return !(GetVersion() & 0x80000000);
265 }
266 
267 static ME_TextBuffer *ME_MakeText(void) {
268  ME_TextBuffer *buf = heap_alloc(sizeof(*buf));
271 
272  p1->prev = NULL;
273  p1->next = p2;
274  p2->prev = p1;
275  p2->next = NULL;
276  p1->member.para.next_para = p2;
277  p2->member.para.prev_para = p1;
278  p2->member.para.nCharOfs = 0;
279 
280  buf->pFirst = p1;
281  buf->pLast = p2;
282  buf->pCharStyle = NULL;
283 
284  return buf;
285 }
286 
287 
289 {
290  WCHAR *pText;
291  LRESULT total_bytes_read = 0;
292  BOOL is_read = FALSE;
293  DWORD cp = CP_ACP, copy = 0;
294  char conv_buf[4 + STREAMIN_BUFFER_SIZE]; /* up to 4 additional UTF-8 bytes */
295 
296  static const char bom_utf8[] = {0xEF, 0xBB, 0xBF};
297 
298  TRACE("%08x %p\n", dwFormat, stream);
299 
300  do {
301  LONG nWideChars = 0;
302  WCHAR wszText[STREAMIN_BUFFER_SIZE+1];
303 
304  if (!stream->dwSize)
305  {
307  if (stream->editstream->dwError)
308  break;
309  if (!stream->dwSize)
310  break;
311  total_bytes_read += stream->dwSize;
312  }
313 
314  if (!(dwFormat & SF_UNICODE))
315  {
316  char * buf = stream->buffer;
317  DWORD size = stream->dwSize, end;
318 
319  if (!is_read)
320  {
321  is_read = TRUE;
322  if (stream->dwSize >= 3 && !memcmp(stream->buffer, bom_utf8, 3))
323  {
324  cp = CP_UTF8;
325  buf += 3;
326  size -= 3;
327  }
328  }
329 
330  if (cp == CP_UTF8)
331  {
332  if (copy)
333  {
334  memcpy(conv_buf + copy, buf, size);
335  buf = conv_buf;
336  size += copy;
337  }
338  end = size;
339  while ((buf[end-1] & 0xC0) == 0x80)
340  {
341  --end;
342  --total_bytes_read; /* strange, but seems to match windows */
343  }
344  if (buf[end-1] & 0x80)
345  {
346  DWORD need = 0;
347  if ((buf[end-1] & 0xE0) == 0xC0)
348  need = 1;
349  if ((buf[end-1] & 0xF0) == 0xE0)
350  need = 2;
351  if ((buf[end-1] & 0xF8) == 0xF0)
352  need = 3;
353 
354  if (size - end >= need)
355  {
356  /* we have enough bytes for this sequence */
357  end = size;
358  }
359  else
360  {
361  /* need more bytes, so don't transcode this sequence */
362  --end;
363  }
364  }
365  }
366  else
367  end = size;
368 
369  nWideChars = MultiByteToWideChar(cp, 0, buf, end, wszText, STREAMIN_BUFFER_SIZE);
370  pText = wszText;
371 
372  if (cp == CP_UTF8)
373  {
374  if (end != size)
375  {
376  memcpy(conv_buf, buf + end, size - end);
377  copy = size - end;
378  }
379  }
380  }
381  else
382  {
383  nWideChars = stream->dwSize >> 1;
384  pText = (WCHAR *)stream->buffer;
385  }
386 
387  ME_InsertTextFromCursor(editor, 0, pText, nWideChars, style);
388  if (stream->dwSize == 0)
389  break;
390  stream->dwSize = 0;
391  } while(1);
392  return total_bytes_read;
393 }
394 
396  ME_BorderRect *borderRect,
397  RTFBorder *borderDef)
398 {
399  int i, colorNum;
400  ME_Border *pBorders[] = {&borderRect->top,
401  &borderRect->left,
402  &borderRect->bottom,
403  &borderRect->right};
404  for (i = 0; i < 4; i++)
405  {
406  RTFColor *colorDef = info->colorList;
407  pBorders[i]->width = borderDef[i].width;
408  colorNum = borderDef[i].color;
409  while (colorDef && colorDef->rtfCNum != colorNum)
410  colorDef = colorDef->rtfNextColor;
411  if (colorDef)
412  pBorders[i]->colorRef = RGB(
413  colorDef->rtfCRed >= 0 ? colorDef->rtfCRed : 0,
414  colorDef->rtfCGreen >= 0 ? colorDef->rtfCGreen : 0,
415  colorDef->rtfCBlue >= 0 ? colorDef->rtfCBlue : 0);
416  else
417  pBorders[i]->colorRef = RGB(0, 0, 0);
418  }
419 }
420 
422 {
424  fmt.cbSize = sizeof(fmt);
425  fmt.dwMask = 0;
426  fmt.dwEffects = 0;
427 
428  switch(info->rtfMinor)
429  {
430  case rtfPlain:
431  /* FIXME add more flags once they're implemented */
434  fmt.dwEffects = CFE_AUTOCOLOR | CFE_AUTOBACKCOLOR;
435  fmt.yHeight = 12*20; /* 12pt */
436  fmt.wWeight = FW_NORMAL;
437  fmt.bUnderlineType = CFU_UNDERLINE;
438  break;
439  case rtfBold:
440  fmt.dwMask = CFM_BOLD | CFM_WEIGHT;
441  fmt.dwEffects = info->rtfParam ? CFE_BOLD : 0;
442  fmt.wWeight = info->rtfParam ? FW_BOLD : FW_NORMAL;
443  break;
444  case rtfItalic:
445  fmt.dwMask = CFM_ITALIC;
446  fmt.dwEffects = info->rtfParam ? fmt.dwMask : 0;
447  break;
448  case rtfUnderline:
450  fmt.bUnderlineType = CFU_UNDERLINE;
451  fmt.dwEffects = info->rtfParam ? CFE_UNDERLINE : 0;
452  break;
453  case rtfDotUnderline:
455  fmt.bUnderlineType = CFU_UNDERLINEDOTTED;
456  fmt.dwEffects = info->rtfParam ? CFE_UNDERLINE : 0;
457  break;
458  case rtfDbUnderline:
460  fmt.bUnderlineType = CFU_UNDERLINEDOUBLE;
461  fmt.dwEffects = info->rtfParam ? CFE_UNDERLINE : 0;
462  break;
463  case rtfWordUnderline:
465  fmt.bUnderlineType = CFU_UNDERLINEWORD;
466  fmt.dwEffects = info->rtfParam ? CFE_UNDERLINE : 0;
467  break;
468  case rtfNoUnderline:
469  fmt.dwMask = CFM_UNDERLINE;
470  fmt.dwEffects = 0;
471  break;
472  case rtfStrikeThru:
473  fmt.dwMask = CFM_STRIKEOUT;
474  fmt.dwEffects = info->rtfParam ? fmt.dwMask : 0;
475  break;
476  case rtfSubScript:
477  case rtfSuperScript:
478  case rtfSubScrShrink:
479  case rtfSuperScrShrink:
480  case rtfNoSuperSub:
482  if (info->rtfMinor == rtfSubScrShrink) fmt.dwEffects = CFE_SUBSCRIPT;
483  if (info->rtfMinor == rtfSuperScrShrink) fmt.dwEffects = CFE_SUPERSCRIPT;
484  if (info->rtfMinor == rtfNoSuperSub) fmt.dwEffects = 0;
485  break;
486  case rtfInvisible:
487  fmt.dwMask = CFM_HIDDEN;
488  fmt.dwEffects = info->rtfParam ? fmt.dwMask : 0;
489  break;
490  case rtfBackColor:
491  fmt.dwMask = CFM_BACKCOLOR;
492  fmt.dwEffects = 0;
493  if (info->rtfParam == 0)
494  fmt.dwEffects = CFE_AUTOBACKCOLOR;
495  else if (info->rtfParam != rtfNoParam)
496  {
497  RTFColor *c = RTFGetColor(info, info->rtfParam);
498  if (c && c->rtfCBlue >= 0)
499  fmt.crBackColor = (c->rtfCBlue<<16)|(c->rtfCGreen<<8)|(c->rtfCRed);
500  else
501  fmt.dwEffects = CFE_AUTOBACKCOLOR;
502  }
503  break;
504  case rtfForeColor:
505  fmt.dwMask = CFM_COLOR;
506  fmt.dwEffects = 0;
507  if (info->rtfParam == 0)
508  fmt.dwEffects = CFE_AUTOCOLOR;
509  else if (info->rtfParam != rtfNoParam)
510  {
511  RTFColor *c = RTFGetColor(info, info->rtfParam);
512  if (c && c->rtfCBlue >= 0)
513  fmt.crTextColor = (c->rtfCBlue<<16)|(c->rtfCGreen<<8)|(c->rtfCRed);
514  else {
515  fmt.dwEffects = CFE_AUTOCOLOR;
516  }
517  }
518  break;
519  case rtfFontNum:
520  if (info->rtfParam != rtfNoParam)
521  {
522  RTFFont *f = RTFGetFont(info, info->rtfParam);
523  if (f)
524  {
525  MultiByteToWideChar(CP_ACP, 0, f->rtfFName, -1, fmt.szFaceName, ARRAY_SIZE(fmt.szFaceName));
526  fmt.szFaceName[ARRAY_SIZE(fmt.szFaceName)-1] = '\0';
527  fmt.bCharSet = f->rtfFCharSet;
528  fmt.dwMask = CFM_FACE | CFM_CHARSET;
529  fmt.bPitchAndFamily = f->rtfFPitch | (f->rtfFFamily << 4);
530  }
531  }
532  break;
533  case rtfFontSize:
534  fmt.dwMask = CFM_SIZE;
535  if (info->rtfParam != rtfNoParam)
536  fmt.yHeight = info->rtfParam*10;
537  break;
538  }
539  if (fmt.dwMask) {
540  ME_Style *style2;
542  /* FIXME too slow ? how come ? */
543  style2 = ME_ApplyStyle(info->editor, info->style, &fmt);
544  ME_ReleaseStyle(info->style);
545  info->style = style2;
546  info->styleChanged = TRUE;
547  }
548 }
549 
550 /* FIXME this function doesn't get any information about context of the RTF tag, which is very bad,
551  the same tags mean different things in different contexts */
553 {
554  switch(info->rtfMinor)
555  {
556  case rtfParDef: /* restores default paragraph attributes */
557  if (!info->editor->bEmulateVersion10) /* v4.1 */
558  info->borderType = RTFBorderParaLeft;
559  else /* v1.0 - 3.0 */
560  info->borderType = RTFBorderParaTop;
565  /* TODO: shading */
566  info->fmt.wAlignment = PFA_LEFT;
567  info->fmt.cTabCount = 0;
568  info->fmt.dxOffset = info->fmt.dxStartIndent = info->fmt.dxRightIndent = 0;
569  info->fmt.wBorderWidth = info->fmt.wBorders = 0;
570  info->fmt.wBorderSpace = 0;
571  info->fmt.bLineSpacingRule = 0;
572  info->fmt.dySpaceBefore = info->fmt.dySpaceAfter = 0;
573  info->fmt.dyLineSpacing = 0;
574  info->fmt.wEffects &= ~PFE_RTLPARA;
575  info->fmt.wNumbering = 0;
576  info->fmt.wNumberingStart = 0;
577  info->fmt.wNumberingStyle = 0;
578  info->fmt.wNumberingTab = 0;
579 
580  if (!info->editor->bEmulateVersion10) /* v4.1 */
581  {
582  if (info->tableDef && info->tableDef->tableRowStart &&
583  info->tableDef->tableRowStart->member.para.nFlags & MEPF_ROWEND)
584  {
586  ME_DisplayItem *para;
587  /* We are just after a table row. */
589  cursor = info->editor->pCursors[0];
590  para = cursor.pPara;
591  if (para == info->tableDef->tableRowStart->member.para.next_para
592  && !cursor.nOffset && !cursor.pRun->member.run.nCharOfs)
593  {
594  /* Since the table row end, no text has been inserted, and the \intbl
595  * control word has not be used. We can confirm that we are not in a
596  * table anymore.
597  */
598  info->tableDef->tableRowStart = NULL;
599  info->canInheritInTbl = FALSE;
600  }
601  }
602  } else { /* v1.0 - v3.0 */
603  info->fmt.dwMask |= PFM_TABLE;
604  info->fmt.wEffects &= ~PFE_TABLE;
605  }
606  break;
607  case rtfNestLevel:
608  if (!info->editor->bEmulateVersion10) /* v4.1 */
609  {
610  while (info->rtfParam > info->nestingLevel) {
611  RTFTable *tableDef = heap_alloc_zero(sizeof(*tableDef));
612  tableDef->parent = info->tableDef;
613  info->tableDef = tableDef;
614 
616  if (tableDef->tableRowStart &&
618  {
619  ME_DisplayItem *para = tableDef->tableRowStart;
620  para = para->member.para.next_para;
621  para = ME_InsertTableRowStartAtParagraph(info->editor, para);
622  tableDef->tableRowStart = para;
623  } else {
625  WCHAR endl = '\r';
626  cursor = info->editor->pCursors[0];
627  if (cursor.nOffset || cursor.pRun->member.run.nCharOfs)
628  ME_InsertTextFromCursor(info->editor, 0, &endl, 1, info->style);
630  }
631 
632  info->nestingLevel++;
633  }
634  info->canInheritInTbl = FALSE;
635  }
636  break;
637  case rtfInTable:
638  {
639  if (!info->editor->bEmulateVersion10) /* v4.1 */
640  {
641  if (info->nestingLevel < 1)
642  {
643  RTFTable *tableDef;
644  if (!info->tableDef)
645  info->tableDef = heap_alloc_zero(sizeof(*info->tableDef));
646  tableDef = info->tableDef;
648  if (tableDef->tableRowStart &&
650  {
651  ME_DisplayItem *para = tableDef->tableRowStart;
652  para = para->member.para.next_para;
653  para = ME_InsertTableRowStartAtParagraph(info->editor, para);
654  tableDef->tableRowStart = para;
655  } else {
657  WCHAR endl = '\r';
658  cursor = info->editor->pCursors[0];
659  if (cursor.nOffset || cursor.pRun->member.run.nCharOfs)
660  ME_InsertTextFromCursor(info->editor, 0, &endl, 1, info->style);
662  }
663  info->nestingLevel = 1;
664  info->canInheritInTbl = TRUE;
665  }
666  return;
667  } else { /* v1.0 - v3.0 */
668  info->fmt.dwMask |= PFM_TABLE;
669  info->fmt.wEffects |= PFE_TABLE;
670  }
671  break;
672  }
673  case rtfFirstIndent:
674  case rtfLeftIndent:
675  if ((info->fmt.dwMask & (PFM_STARTINDENT | PFM_OFFSET)) != (PFM_STARTINDENT | PFM_OFFSET))
676  {
678  fmt.cbSize = sizeof(fmt);
680  info->fmt.dwMask |= PFM_STARTINDENT | PFM_OFFSET;
681  info->fmt.dxStartIndent = fmt.dxStartIndent;
682  info->fmt.dxOffset = fmt.dxOffset;
683  }
684  if (info->rtfMinor == rtfFirstIndent)
685  {
686  info->fmt.dxStartIndent += info->fmt.dxOffset + info->rtfParam;
687  info->fmt.dxOffset = -info->rtfParam;
688  }
689  else
690  info->fmt.dxStartIndent = info->rtfParam - info->fmt.dxOffset;
691  break;
692  case rtfRightIndent:
693  info->fmt.dwMask |= PFM_RIGHTINDENT;
694  info->fmt.dxRightIndent = info->rtfParam;
695  break;
696  case rtfQuadLeft:
697  case rtfQuadJust:
698  info->fmt.dwMask |= PFM_ALIGNMENT;
699  info->fmt.wAlignment = PFA_LEFT;
700  break;
701  case rtfQuadRight:
702  info->fmt.dwMask |= PFM_ALIGNMENT;
703  info->fmt.wAlignment = PFA_RIGHT;
704  break;
705  case rtfQuadCenter:
706  info->fmt.dwMask |= PFM_ALIGNMENT;
707  info->fmt.wAlignment = PFA_CENTER;
708  break;
709  case rtfTabPos:
710  if (!(info->fmt.dwMask & PFM_TABSTOPS))
711  {
713  fmt.cbSize = sizeof(fmt);
715  memcpy(info->fmt.rgxTabs, fmt.rgxTabs,
716  fmt.cTabCount * sizeof(fmt.rgxTabs[0]));
717  info->fmt.cTabCount = fmt.cTabCount;
718  info->fmt.dwMask |= PFM_TABSTOPS;
719  }
720  if (info->fmt.cTabCount < MAX_TAB_STOPS && info->rtfParam < 0x1000000)
721  info->fmt.rgxTabs[info->fmt.cTabCount++] = info->rtfParam;
722  break;
723  case rtfKeep:
724  info->fmt.dwMask |= PFM_KEEP;
725  info->fmt.wEffects |= PFE_KEEP;
726  break;
727  case rtfNoWidowControl:
728  info->fmt.dwMask |= PFM_NOWIDOWCONTROL;
729  info->fmt.wEffects |= PFE_NOWIDOWCONTROL;
730  break;
731  case rtfKeepNext:
732  info->fmt.dwMask |= PFM_KEEPNEXT;
733  info->fmt.wEffects |= PFE_KEEPNEXT;
734  break;
735  case rtfSpaceAfter:
736  info->fmt.dwMask |= PFM_SPACEAFTER;
737  info->fmt.dySpaceAfter = info->rtfParam;
738  break;
739  case rtfSpaceBefore:
740  info->fmt.dwMask |= PFM_SPACEBEFORE;
741  info->fmt.dySpaceBefore = info->rtfParam;
742  break;
743  case rtfSpaceBetween:
744  info->fmt.dwMask |= PFM_LINESPACING;
745  if ((int)info->rtfParam > 0)
746  {
747  info->fmt.dyLineSpacing = info->rtfParam;
748  info->fmt.bLineSpacingRule = 3;
749  }
750  else
751  {
752  info->fmt.dyLineSpacing = info->rtfParam;
753  info->fmt.bLineSpacingRule = 4;
754  }
755  break;
756  case rtfSpaceMultiply:
757  info->fmt.dwMask |= PFM_LINESPACING;
758  info->fmt.dyLineSpacing = info->rtfParam * 20;
759  info->fmt.bLineSpacingRule = 5;
760  break;
761  case rtfParBullet:
762  info->fmt.dwMask |= PFM_NUMBERING;
763  info->fmt.wNumbering = PFN_BULLET;
764  break;
765  case rtfParSimple:
766  info->fmt.dwMask |= PFM_NUMBERING;
767  info->fmt.wNumbering = 2; /* FIXME: MSDN says it's not used ?? */
768  break;
769  case rtfBorderLeft:
770  info->borderType = RTFBorderParaLeft;
771  info->fmt.wBorders |= 1;
772  info->fmt.dwMask |= PFM_BORDER;
773  break;
774  case rtfBorderRight:
775  info->borderType = RTFBorderParaRight;
776  info->fmt.wBorders |= 2;
777  info->fmt.dwMask |= PFM_BORDER;
778  break;
779  case rtfBorderTop:
780  info->borderType = RTFBorderParaTop;
781  info->fmt.wBorders |= 4;
782  info->fmt.dwMask |= PFM_BORDER;
783  break;
784  case rtfBorderBottom:
785  info->borderType = RTFBorderParaBottom;
786  info->fmt.wBorders |= 8;
787  info->fmt.dwMask |= PFM_BORDER;
788  break;
789  case rtfBorderSingle:
790  info->fmt.wBorders &= ~0x700;
791  info->fmt.wBorders |= 1 << 8;
792  info->fmt.dwMask |= PFM_BORDER;
793  break;
794  case rtfBorderThick:
795  info->fmt.wBorders &= ~0x700;
796  info->fmt.wBorders |= 2 << 8;
797  info->fmt.dwMask |= PFM_BORDER;
798  break;
799  case rtfBorderShadow:
800  info->fmt.wBorders &= ~0x700;
801  info->fmt.wBorders |= 10 << 8;
802  info->fmt.dwMask |= PFM_BORDER;
803  break;
804  case rtfBorderDouble:
805  info->fmt.wBorders &= ~0x700;
806  info->fmt.wBorders |= 7 << 8;
807  info->fmt.dwMask |= PFM_BORDER;
808  break;
809  case rtfBorderDot:
810  info->fmt.wBorders &= ~0x700;
811  info->fmt.wBorders |= 11 << 8;
812  info->fmt.dwMask |= PFM_BORDER;
813  break;
814  case rtfBorderWidth:
815  {
816  int borderSide = info->borderType & RTFBorderSideMask;
817  RTFTable *tableDef = info->tableDef;
818  if ((info->borderType & RTFBorderTypeMask) == RTFBorderTypeCell)
819  {
820  RTFBorder *border;
821  if (!tableDef || tableDef->numCellsDefined >= MAX_TABLE_CELLS)
822  break;
823  border = &tableDef->cells[tableDef->numCellsDefined].border[borderSide];
824  border->width = info->rtfParam;
825  break;
826  }
827  info->fmt.wBorderWidth = info->rtfParam;
828  info->fmt.dwMask |= PFM_BORDER;
829  break;
830  }
831  case rtfBorderSpace:
832  info->fmt.wBorderSpace = info->rtfParam;
833  info->fmt.dwMask |= PFM_BORDER;
834  break;
835  case rtfBorderColor:
836  {
837  RTFTable *tableDef = info->tableDef;
838  int borderSide = info->borderType & RTFBorderSideMask;
839  int borderType = info->borderType & RTFBorderTypeMask;
840  switch(borderType)
841  {
842  case RTFBorderTypePara:
843  if (!info->editor->bEmulateVersion10) /* v4.1 */
844  break;
845  /* v1.0 - 3.0 treat paragraph and row borders the same. */
846  case RTFBorderTypeRow:
847  if (tableDef) {
848  tableDef->border[borderSide].color = info->rtfParam;
849  }
850  break;
851  case RTFBorderTypeCell:
852  if (tableDef && tableDef->numCellsDefined < MAX_TABLE_CELLS) {
853  tableDef->cells[tableDef->numCellsDefined].border[borderSide].color = info->rtfParam;
854  }
855  break;
856  }
857  break;
858  }
859  case rtfRTLPar:
860  info->fmt.dwMask |= PFM_RTLPARA;
861  info->fmt.wEffects |= PFE_RTLPARA;
862  break;
863  case rtfLTRPar:
864  info->fmt.dwMask |= PFM_RTLPARA;
865  info->fmt.wEffects &= ~PFE_RTLPARA;
866  break;
867  }
868 }
869 
871 {
872  switch (info->rtfMinor)
873  {
874  case rtfRowDef:
875  {
876  if (!info->editor->bEmulateVersion10) /* v4.1 */
877  info->borderType = 0; /* Not sure */
878  else /* v1.0 - 3.0 */
879  info->borderType = RTFBorderRowTop;
880  if (!info->tableDef) {
881  info->tableDef = ME_MakeTableDef(info->editor);
882  } else {
883  ME_InitTableDef(info->editor, info->tableDef);
884  }
885  break;
886  }
887  case rtfCellPos:
888  {
889  int cellNum;
890  if (!info->tableDef)
891  {
892  info->tableDef = ME_MakeTableDef(info->editor);
893  }
894  cellNum = info->tableDef->numCellsDefined;
895  if (cellNum >= MAX_TABLE_CELLS)
896  break;
897  info->tableDef->cells[cellNum].rightBoundary = info->rtfParam;
898  if (cellNum < MAX_TAB_STOPS) {
899  /* Tab stops were used to store cell positions before v4.1 but v4.1
900  * still seems to set the tabstops without using them. */
901  ME_DisplayItem *para = info->editor->pCursors[0].pPara;
902  PARAFORMAT2 *pFmt = &para->member.para.fmt;
903  pFmt->rgxTabs[cellNum] &= ~0x00FFFFFF;
904  pFmt->rgxTabs[cellNum] |= 0x00FFFFFF & info->rtfParam;
905  }
906  info->tableDef->numCellsDefined++;
907  break;
908  }
909  case rtfRowBordTop:
910  info->borderType = RTFBorderRowTop;
911  break;
912  case rtfRowBordLeft:
913  info->borderType = RTFBorderRowLeft;
914  break;
915  case rtfRowBordBottom:
916  info->borderType = RTFBorderRowBottom;
917  break;
918  case rtfRowBordRight:
919  info->borderType = RTFBorderRowRight;
920  break;
921  case rtfCellBordTop:
922  info->borderType = RTFBorderCellTop;
923  break;
924  case rtfCellBordLeft:
925  info->borderType = RTFBorderCellLeft;
926  break;
927  case rtfCellBordBottom:
928  info->borderType = RTFBorderCellBottom;
929  break;
930  case rtfCellBordRight:
931  info->borderType = RTFBorderCellRight;
932  break;
933  case rtfRowGapH:
934  if (info->tableDef)
935  info->tableDef->gapH = info->rtfParam;
936  break;
937  case rtfRowLeftEdge:
938  if (info->tableDef)
939  info->tableDef->leftEdge = info->rtfParam;
940  break;
941  }
942 }
943 
945 {
946  RTFTable *tableDef = info->tableDef;
947  switch (info->rtfMinor)
948  {
949  case rtfNestCell:
950  if (info->editor->bEmulateVersion10) /* v1.0 - v3.0 */
951  break;
952  /* else fall through since v4.1 treats rtfNestCell and rtfCell the same */
953  case rtfCell:
954  if (!tableDef)
955  break;
957  if (!info->editor->bEmulateVersion10) { /* v4.1 */
958  if (tableDef->tableRowStart)
959  {
960  if (!info->nestingLevel &&
962  {
963  ME_DisplayItem *para = tableDef->tableRowStart;
964  para = para->member.para.next_para;
965  para = ME_InsertTableRowStartAtParagraph(info->editor, para);
966  tableDef->tableRowStart = para;
967  info->nestingLevel = 1;
968  }
970  }
971  } else { /* v1.0 - v3.0 */
972  ME_DisplayItem *para = info->editor->pCursors[0].pPara;
973  PARAFORMAT2 *pFmt = &para->member.para.fmt;
974  if (pFmt->dwMask & PFM_TABLE && pFmt->wEffects & PFE_TABLE &&
975  tableDef->numCellsInserted < tableDef->numCellsDefined)
976  {
977  WCHAR tab = '\t';
978  ME_InsertTextFromCursor(info->editor, 0, &tab, 1, info->style);
979  tableDef->numCellsInserted++;
980  }
981  }
982  break;
983  case rtfNestRow:
984  if (info->editor->bEmulateVersion10) /* v1.0 - v3.0 */
985  break;
986  /* else fall through since v4.1 treats rtfNestRow and rtfRow the same */
987  case rtfRow:
988  {
989  ME_DisplayItem *para, *cell, *run;
990  int i;
991 
992  if (!tableDef)
993  break;
995  if (!info->editor->bEmulateVersion10) { /* v4.1 */
996  if (!tableDef->tableRowStart)
997  break;
998  if (!info->nestingLevel &&
1000  {
1001  para = tableDef->tableRowStart;
1002  para = para->member.para.next_para;
1003  para = ME_InsertTableRowStartAtParagraph(info->editor, para);
1004  tableDef->tableRowStart = para;
1005  info->nestingLevel++;
1006  }
1007  para = tableDef->tableRowStart;
1008  cell = ME_FindItemFwd(para, diCell);
1009  assert(cell && !cell->member.cell.prev_cell);
1010  if (tableDef->numCellsDefined < 1)
1011  {
1012  /* 2000 twips appears to be the cell size that native richedit uses
1013  * when no cell sizes are specified. */
1014  const int defaultCellSize = 2000;
1015  int nRightBoundary = defaultCellSize;
1016  cell->member.cell.nRightBoundary = nRightBoundary;
1017  while (cell->member.cell.next_cell) {
1018  cell = cell->member.cell.next_cell;
1019  nRightBoundary += defaultCellSize;
1020  cell->member.cell.nRightBoundary = nRightBoundary;
1021  }
1022  para = ME_InsertTableCellFromCursor(info->editor);
1023  cell = para->member.para.pCell;
1024  cell->member.cell.nRightBoundary = nRightBoundary;
1025  } else {
1026  for (i = 0; i < tableDef->numCellsDefined; i++)
1027  {
1028  RTFCell *cellDef = &tableDef->cells[i];
1029  cell->member.cell.nRightBoundary = cellDef->rightBoundary;
1031  cellDef->border);
1032  cell = cell->member.cell.next_cell;
1033  if (!cell)
1034  {
1035  para = ME_InsertTableCellFromCursor(info->editor);
1036  cell = para->member.para.pCell;
1037  }
1038  }
1039  /* Cell for table row delimiter is empty */
1040  cell->member.cell.nRightBoundary = tableDef->cells[i-1].rightBoundary;
1041  }
1042 
1043  run = ME_FindItemFwd(cell, diRun);
1044  if (info->editor->pCursors[0].pRun != run ||
1045  info->editor->pCursors[0].nOffset)
1046  {
1047  int nOfs, nChars;
1048  /* Delete inserted cells that aren't defined. */
1049  info->editor->pCursors[1].pRun = run;
1050  info->editor->pCursors[1].pPara = ME_GetParagraph(run);
1051  info->editor->pCursors[1].nOffset = 0;
1052  nOfs = ME_GetCursorOfs(&info->editor->pCursors[1]);
1053  nChars = ME_GetCursorOfs(&info->editor->pCursors[0]) - nOfs;
1054  ME_InternalDeleteText(info->editor, &info->editor->pCursors[1],
1055  nChars, TRUE);
1056  }
1057 
1058  para = ME_InsertTableRowEndFromCursor(info->editor);
1059  para->member.para.fmt.dxOffset = abs(info->tableDef->gapH);
1060  para->member.para.fmt.dxStartIndent = info->tableDef->leftEdge;
1062  tableDef->border);
1063  info->nestingLevel--;
1064  if (!info->nestingLevel)
1065  {
1066  if (info->canInheritInTbl) {
1067  tableDef->tableRowStart = para;
1068  } else {
1069  while (info->tableDef) {
1070  tableDef = info->tableDef;
1071  info->tableDef = tableDef->parent;
1072  heap_free(tableDef);
1073  }
1074  }
1075  } else {
1076  info->tableDef = tableDef->parent;
1077  heap_free(tableDef);
1078  }
1079  } else { /* v1.0 - v3.0 */
1080  WCHAR endl = '\r';
1081  ME_DisplayItem *para = info->editor->pCursors[0].pPara;
1082  PARAFORMAT2 *pFmt = &para->member.para.fmt;
1083  pFmt->dxOffset = info->tableDef->gapH;
1084  pFmt->dxStartIndent = info->tableDef->leftEdge;
1085 
1087  tableDef->border);
1088  while (tableDef->numCellsInserted < tableDef->numCellsDefined)
1089  {
1090  WCHAR tab = '\t';
1091  ME_InsertTextFromCursor(info->editor, 0, &tab, 1, info->style);
1092  tableDef->numCellsInserted++;
1093  }
1094  pFmt->cTabCount = min(tableDef->numCellsDefined, MAX_TAB_STOPS);
1095  if (!tableDef->numCellsDefined)
1096  pFmt->wEffects &= ~PFE_TABLE;
1097  ME_InsertTextFromCursor(info->editor, 0, &endl, 1, info->style);
1098  tableDef->numCellsInserted = 0;
1099  }
1100  break;
1101  }
1102  case rtfTab:
1103  case rtfPar:
1104  if (info->editor->bEmulateVersion10) { /* v1.0 - 3.0 */
1105  ME_DisplayItem *para;
1106  PARAFORMAT2 *pFmt;
1108  para = info->editor->pCursors[0].pPara;
1109  pFmt = &para->member.para.fmt;
1110  if (pFmt->dwMask & PFM_TABLE && pFmt->wEffects & PFE_TABLE)
1111  {
1112  /* rtfPar is treated like a space within a table. */
1113  info->rtfClass = rtfText;
1114  info->rtfMajor = ' ';
1115  }
1116  else if (info->rtfMinor == rtfPar && tableDef)
1117  tableDef->numCellsInserted = 0;
1118  }
1119  break;
1120  }
1121 }
1122 
1123 static HRESULT insert_static_object(ME_TextEditor *editor, HENHMETAFILE hemf, HBITMAP hbmp,
1124  const SIZEL* sz)
1125 {
1126  LPOLEOBJECT lpObject = NULL;
1127  LPSTORAGE lpStorage = NULL;
1128  LPOLECLIENTSITE lpClientSite = NULL;
1129  LPDATAOBJECT lpDataObject = NULL;
1130  LPOLECACHE lpOleCache = NULL;
1131  STGMEDIUM stgm;
1132  FORMATETC fm;
1133  CLSID clsid;
1134  HRESULT hr = E_FAIL;
1135  DWORD conn;
1136 
1137  if (hemf)
1138  {
1139  stgm.tymed = TYMED_ENHMF;
1140  stgm.u.hEnhMetaFile = hemf;
1141  fm.cfFormat = CF_ENHMETAFILE;
1142  }
1143  else if (hbmp)
1144  {
1145  stgm.tymed = TYMED_GDI;
1146  stgm.u.hBitmap = hbmp;
1147  fm.cfFormat = CF_BITMAP;
1148  }
1149  stgm.pUnkForRelease = NULL;
1150 
1151  fm.ptd = NULL;
1152  fm.dwAspect = DVASPECT_CONTENT;
1153  fm.lindex = -1;
1154  fm.tymed = stgm.tymed;
1155 
1156  if (!editor->reOle)
1157  {
1158  if (!CreateIRichEditOle(NULL, editor, (LPVOID *)&editor->reOle))
1159  return hr;
1160  }
1161 
1162  if (OleCreateDefaultHandler(&CLSID_NULL, NULL, &IID_IOleObject, (void**)&lpObject) == S_OK &&
1163  IRichEditOle_GetClientSite(editor->reOle, &lpClientSite) == S_OK &&
1164  IOleObject_SetClientSite(lpObject, lpClientSite) == S_OK &&
1165  IOleObject_GetUserClassID(lpObject, &clsid) == S_OK &&
1166  IOleObject_QueryInterface(lpObject, &IID_IOleCache, (void**)&lpOleCache) == S_OK &&
1167  IOleCache_Cache(lpOleCache, &fm, 0, &conn) == S_OK &&
1168  IOleObject_QueryInterface(lpObject, &IID_IDataObject, (void**)&lpDataObject) == S_OK &&
1169  IDataObject_SetData(lpDataObject, &fm, &stgm, TRUE) == S_OK)
1170  {
1171  REOBJECT reobject;
1172 
1173  reobject.cbStruct = sizeof(reobject);
1174  reobject.cp = REO_CP_SELECTION;
1175  reobject.clsid = clsid;
1176  reobject.poleobj = lpObject;
1177  reobject.pstg = lpStorage;
1178  reobject.polesite = lpClientSite;
1179  /* convert from twips to .01 mm */
1180  reobject.sizel.cx = MulDiv(sz->cx, 254, 144);
1181  reobject.sizel.cy = MulDiv(sz->cy, 254, 144);
1182  reobject.dvaspect = DVASPECT_CONTENT;
1183  reobject.dwFlags = 0; /* FIXME */
1184  reobject.dwUser = 0;
1185 
1186  ME_InsertOLEFromCursor(editor, &reobject, 0);
1187  hr = S_OK;
1188  }
1189 
1190  if (lpObject) IOleObject_Release(lpObject);
1191  if (lpClientSite) IOleClientSite_Release(lpClientSite);
1192  if (lpStorage) IStorage_Release(lpStorage);
1193  if (lpDataObject) IDataObject_Release(lpDataObject);
1194  if (lpOleCache) IOleCache_Release(lpOleCache);
1195 
1196  return hr;
1197 }
1198 
1200 {
1201  int level = 1;
1202 
1203  for (;;)
1204  {
1205  RTFGetToken (info);
1206 
1207  if (info->rtfClass == rtfEOF) return;
1209  {
1210  if (--level == 0) break;
1211  }
1212  else if (RTFCheckCM( info, rtfGroup, rtfBeginGroup ))
1213  {
1214  level++;
1215  }
1216  else
1217  {
1218  RTFRouteToken( info );
1220  level--;
1221  }
1222  }
1223 
1224  RTFRouteToken( info ); /* feed "}" back to router */
1225  return;
1226 }
1227 
1229 {
1230  DWORD read = 0, size = 1024;
1231  BYTE *buf, val;
1232  BOOL flip;
1233 
1234  *out = NULL;
1235 
1236  if (info->rtfClass != rtfText)
1237  {
1238  ERR("Called with incorrect token\n");
1239  return 0;
1240  }
1241 
1242  buf = HeapAlloc( GetProcessHeap(), 0, size );
1243  if (!buf) return 0;
1244 
1245  val = info->rtfMajor;
1246  for (flip = TRUE;; flip = !flip)
1247  {
1248  RTFGetToken( info );
1249  if (info->rtfClass == rtfEOF)
1250  {
1251  HeapFree( GetProcessHeap(), 0, buf );
1252  return 0;
1253  }
1254  if (info->rtfClass != rtfText) break;
1255  if (flip)
1256  {
1257  if (read >= size)
1258  {
1259  size *= 2;
1260  buf = HeapReAlloc( GetProcessHeap(), 0, buf, size );
1261  if (!buf) return 0;
1262  }
1263  buf[read++] = RTFCharToHex(val) * 16 + RTFCharToHex(info->rtfMajor);
1264  }
1265  else
1266  val = info->rtfMajor;
1267  }
1268  if (flip) FIXME("wrong hex string\n");
1269 
1270  *out = buf;
1271  return read;
1272 }
1273 
1275 {
1276  SIZEL sz;
1277  BYTE *buffer = NULL;
1278  DWORD size = 0;
1279  METAFILEPICT mfp;
1280  HENHMETAFILE hemf;
1281  HBITMAP hbmp;
1282  enum gfxkind {gfx_unknown = 0, gfx_enhmetafile, gfx_metafile, gfx_dib} gfx = gfx_unknown;
1283  int level = 1;
1284 
1285  mfp.mm = MM_TEXT;
1286  sz.cx = sz.cy = 0;
1287 
1288  for (;;)
1289  {
1290  RTFGetToken( info );
1291 
1292  if (info->rtfClass == rtfText)
1293  {
1294  if (level == 1)
1295  {
1296  if (!buffer)
1297  size = read_hex_data( info, &buffer );
1298  }
1299  else
1300  {
1301  RTFSkipGroup( info );
1302  }
1303  } /* We potentially have a new token so fall through. */
1304 
1305  if (info->rtfClass == rtfEOF) return;
1306 
1308  {
1309  if (--level == 0) break;
1310  continue;
1311  }
1313  {
1314  level++;
1315  continue;
1316  }
1318  {
1319  RTFRouteToken( info );
1321  level--;
1322  continue;
1323  }
1324 
1326  {
1327  mfp.mm = info->rtfParam;
1328  gfx = gfx_metafile;
1329  }
1331  {
1332  if (info->rtfParam != 0) FIXME("dibitmap should be 0 (%d)\n", info->rtfParam);
1333  gfx = gfx_dib;
1334  }
1335  else if (RTFCheckMM( info, rtfPictAttr, rtfEmfBlip ))
1336  gfx = gfx_enhmetafile;
1337  else if (RTFCheckMM( info, rtfPictAttr, rtfPicWid ))
1338  mfp.xExt = info->rtfParam;
1339  else if (RTFCheckMM( info, rtfPictAttr, rtfPicHt ))
1340  mfp.yExt = info->rtfParam;
1341  else if (RTFCheckMM( info, rtfPictAttr, rtfPicGoalWid ))
1342  sz.cx = info->rtfParam;
1343  else if (RTFCheckMM( info, rtfPictAttr, rtfPicGoalHt ))
1344  sz.cy = info->rtfParam;
1345  else
1346  FIXME("Non supported attribute: %d %d %d\n", info->rtfClass, info->rtfMajor, info->rtfMinor);
1347  }
1348 
1349  if (buffer)
1350  {
1351  switch (gfx)
1352  {
1353  case gfx_enhmetafile:
1354  if ((hemf = SetEnhMetaFileBits( size, buffer )))
1355  insert_static_object( info->editor, hemf, NULL, &sz );
1356  break;
1357  case gfx_metafile:
1358  if ((hemf = SetWinMetaFileBits( size, buffer, NULL, &mfp )))
1359  insert_static_object( info->editor, hemf, NULL, &sz );
1360  break;
1361  case gfx_dib:
1362  {
1363  BITMAPINFO *bi = (BITMAPINFO*)buffer;
1364  HDC hdc = GetDC(0);
1365  unsigned nc = bi->bmiHeader.biClrUsed;
1366 
1367  /* not quite right, especially for bitfields type of compression */
1368  if (!nc && bi->bmiHeader.biBitCount <= 8)
1369  nc = 1 << bi->bmiHeader.biBitCount;
1370  if ((hbmp = CreateDIBitmap( hdc, &bi->bmiHeader,
1371  CBM_INIT, (char*)(bi + 1) + nc * sizeof(RGBQUAD),
1372  bi, DIB_RGB_COLORS)) )
1373  insert_static_object( info->editor, NULL, hbmp, &sz );
1374  ReleaseDC( 0, hdc );
1375  break;
1376  }
1377  default:
1378  break;
1379  }
1380  }
1381  HeapFree( GetProcessHeap(), 0, buffer );
1382  RTFRouteToken( info ); /* feed "}" back to router */
1383  return;
1384 }
1385 
1386 /* for now, lookup the \result part and use it, whatever the object */
1388 {
1389  for (;;)
1390  {
1391  RTFGetToken (info);
1392  if (info->rtfClass == rtfEOF)
1393  return;
1395  break;
1397  {
1398  RTFGetToken (info);
1399  if (info->rtfClass == rtfEOF)
1400  return;
1402  {
1403  int level = 1;
1404 
1405  while (RTFGetToken (info) != rtfEOF)
1406  {
1407  if (info->rtfClass == rtfGroup)
1408  {
1409  if (info->rtfMajor == rtfBeginGroup) level++;
1410  else if (info->rtfMajor == rtfEndGroup && --level < 0) break;
1411  }
1413  }
1414  }
1415  else RTFSkipGroup(info);
1416  continue;
1417  }
1419  {
1420  FIXME("Non supported attribute: %d %d %d\n", info->rtfClass, info->rtfMajor, info->rtfMinor);
1421  return;
1422  }
1423  }
1424  RTFRouteToken(info); /* feed "}" back to router */
1425 }
1426 
1428 {
1429  int level = 1, type = -1;
1430  WORD indent = 0, start = 1;
1431  WCHAR txt_before = 0, txt_after = 0;
1432 
1433  for (;;)
1434  {
1435  RTFGetToken( info );
1436 
1439  {
1440  int loc = info->rtfMinor;
1441 
1442  RTFGetToken( info );
1443  if (info->rtfClass == rtfText)
1444  {
1445  if (loc == rtfParNumTextBefore)
1446  txt_before = info->rtfMajor;
1447  else
1448  txt_after = info->rtfMajor;
1449  continue;
1450  }
1451  /* falling through to catch EOFs and group level changes */
1452  }
1453 
1454  if (info->rtfClass == rtfEOF)
1455  return;
1456 
1458  {
1459  if (--level == 0) break;
1460  continue;
1461  }
1462 
1464  {
1465  level++;
1466  continue;
1467  }
1468 
1469  /* Ignore non para-attr */
1471  continue;
1472 
1473  switch (info->rtfMinor)
1474  {
1475  case rtfParLevel: /* Para level is ignored */
1476  case rtfParSimple:
1477  break;
1478  case rtfParBullet:
1479  type = PFN_BULLET;
1480  break;
1481 
1482  case rtfParNumDecimal:
1483  type = PFN_ARABIC;
1484  break;
1485  case rtfParNumULetter:
1486  type = PFN_UCLETTER;
1487  break;
1488  case rtfParNumURoman:
1489  type = PFN_UCROMAN;
1490  break;
1491  case rtfParNumLLetter:
1492  type = PFN_LCLETTER;
1493  break;
1494  case rtfParNumLRoman:
1495  type = PFN_LCROMAN;
1496  break;
1497 
1498  case rtfParNumIndent:
1499  indent = info->rtfParam;
1500  break;
1501  case rtfParNumStartAt:
1502  start = info->rtfParam;
1503  break;
1504  }
1505  }
1506 
1507  if (type != -1)
1508  {
1510  info->fmt.wNumbering = type;
1511  info->fmt.wNumberingStart = start;
1512  info->fmt.wNumberingStyle = PFNS_PAREN;
1513  if (type != PFN_BULLET)
1514  {
1515  if (txt_before == 0 && txt_after == 0)
1516  info->fmt.wNumberingStyle = PFNS_PLAIN;
1517  else if (txt_after == '.')
1518  info->fmt.wNumberingStyle = PFNS_PERIOD;
1519  else if (txt_before == '(' && txt_after == ')')
1520  info->fmt.wNumberingStyle = PFNS_PARENS;
1521  }
1522  info->fmt.wNumberingTab = indent;
1523  }
1524 
1525  TRACE("type %d indent %d start %d txt before %04x txt after %04x\n",
1526  type, indent, start, txt_before, txt_after);
1527 
1528  RTFRouteToken( info ); /* feed "}" back to router */
1529 }
1530 
1532 {
1533  switch(info->rtfClass)
1534  {
1535  case rtfGroup:
1536  switch(info->rtfMajor)
1537  {
1538  case rtfBeginGroup:
1539  if (info->stackTop < maxStack) {
1540  info->stack[info->stackTop].style = info->style;
1541  ME_AddRefStyle(info->style);
1542  info->stack[info->stackTop].codePage = info->codePage;
1543  info->stack[info->stackTop].unicodeLength = info->unicodeLength;
1544  }
1545  info->stackTop++;
1546  info->styleChanged = FALSE;
1547  break;
1548  case rtfEndGroup:
1549  {
1551  info->stackTop--;
1552  if (info->stackTop <= 0)
1553  info->rtfClass = rtfEOF;
1554  if (info->stackTop < 0)
1555  return;
1556 
1557  ME_ReleaseStyle(info->style);
1558  info->style = info->stack[info->stackTop].style;
1559  info->codePage = info->stack[info->stackTop].codePage;
1560  info->unicodeLength = info->stack[info->stackTop].unicodeLength;
1561  break;
1562  }
1563  }
1564  break;
1565  }
1566 }
1567 
1568 void
1570 {
1571  stream->editstream->dwError = stream->editstream->pfnCallback(stream->editstream->dwCookie,
1572  (BYTE *)stream->buffer,
1573  sizeof(stream->buffer),
1574  (LONG *)&stream->dwSize);
1575  stream->dwUsed = 0;
1576 }
1577 
1579 {
1580  RTF_Info parser;
1581  ME_Style *style;
1582  int from, to, nUndoMode;
1583  int nEventMask = editor->nEventMask;
1584  ME_InStream inStream;
1585  BOOL invalidRTF = FALSE;
1586  ME_Cursor *selStart, *selEnd;
1587  LRESULT num_read = 0; /* bytes read for SF_TEXT, non-control chars inserted for SF_RTF */
1588 
1589  TRACE("stream==%p editor==%p format==0x%X\n", stream, editor, format);
1590  editor->nEventMask = 0;
1591 
1592  ME_GetSelectionOfs(editor, &from, &to);
1593  if (format & SFF_SELECTION && editor->mode & TM_RICHTEXT)
1594  {
1595  ME_GetSelection(editor, &selStart, &selEnd);
1597 
1598  ME_InternalDeleteText(editor, selStart, to - from, FALSE);
1599 
1600  /* Don't insert text at the end of the table row */
1601  if (!editor->bEmulateVersion10) { /* v4.1 */
1602  ME_DisplayItem *para = editor->pCursors->pPara;
1603  if (para->member.para.nFlags & MEPF_ROWEND)
1604  {
1605  para = para->member.para.next_para;
1606  editor->pCursors[0].pPara = para;
1607  editor->pCursors[0].pRun = ME_FindItemFwd(para, diRun);
1608  editor->pCursors[0].nOffset = 0;
1609  }
1610  if (para->member.para.nFlags & MEPF_ROWSTART)
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  editor->pCursors[1] = editor->pCursors[0];
1618  } else { /* v1.0 - 3.0 */
1619  if (editor->pCursors[0].pRun->member.run.nFlags & MERF_ENDPARA &&
1620  ME_IsInTable(editor->pCursors[0].pRun))
1621  return 0;
1622  }
1623  } else {
1624  style = editor->pBuffer->pDefaultStyle;
1626  ME_SetSelection(editor, 0, 0);
1627  ME_InternalDeleteText(editor, &editor->pCursors[1],
1628  ME_GetTextLength(editor), FALSE);
1629  from = to = 0;
1630  ME_ClearTempStyle(editor);
1631  ME_SetDefaultParaFormat(editor, &editor->pCursors[0].pPara->member.para.fmt);
1632  }
1633 
1634 
1635  /* Back up undo mode to a local variable */
1636  nUndoMode = editor->nUndoMode;
1637 
1638  /* Only create an undo if SFF_SELECTION is set */
1639  if (!(format & SFF_SELECTION))
1640  editor->nUndoMode = umIgnore;
1641 
1642  inStream.editstream = stream;
1643  inStream.editstream->dwError = 0;
1644  inStream.dwSize = 0;
1645  inStream.dwUsed = 0;
1646 
1647  if (format & SF_RTF)
1648  {
1649  /* Check if it's really RTF, and if it is not, use plain text */
1650  ME_StreamInFill(&inStream);
1651  if (!inStream.editstream->dwError)
1652  {
1653  if ((!editor->bEmulateVersion10 && strncmp(inStream.buffer, "{\\rtf", 5) && strncmp(inStream.buffer, "{\\urtf", 6))
1654  || (editor->bEmulateVersion10 && *inStream.buffer != '{'))
1655  {
1656  invalidRTF = TRUE;
1657  inStream.editstream->dwError = -16;
1658  }
1659  }
1660  }
1661 
1662  if (!invalidRTF && !inStream.editstream->dwError)
1663  {
1664  ME_Cursor start;
1665  from = ME_GetCursorOfs(&editor->pCursors[0]);
1666  if (format & SF_RTF) {
1667 
1668  /* setup the RTF parser */
1669  memset(&parser, 0, sizeof parser);
1670  RTFSetEditStream(&parser, &inStream);
1671  parser.rtfFormat = format&(SF_TEXT|SF_RTF);
1672  parser.editor = editor;
1673  parser.style = style;
1674  WriterInit(&parser);
1675  RTFInit(&parser);
1681  if (!parser.editor->bEmulateVersion10) /* v4.1 */
1682  {
1685  }
1686  BeginFile(&parser);
1687 
1688  /* do the parsing */
1689  RTFRead(&parser);
1691  if (!editor->bEmulateVersion10) { /* v4.1 */
1692  if (parser.tableDef && parser.tableDef->tableRowStart &&
1693  (parser.nestingLevel > 0 || parser.canInheritInTbl))
1694  {
1695  /* Delete any incomplete table row at the end of the rich text. */
1696  int nOfs, nChars;
1697  ME_DisplayItem *para;
1698 
1699  parser.rtfMinor = rtfRow;
1700  /* Complete the table row before deleting it.
1701  * By doing it this way we will have the current paragraph format set
1702  * properly to reflect that is not in the complete table, and undo items
1703  * will be added for this change to the current paragraph format. */
1704  if (parser.nestingLevel > 0)
1705  {
1706  while (parser.nestingLevel > 1)
1707  ME_RTFSpecialCharHook(&parser); /* Decrements nestingLevel */
1708  para = parser.tableDef->tableRowStart;
1710  } else {
1711  para = parser.tableDef->tableRowStart;
1713  assert(para->member.para.nFlags & MEPF_ROWEND);
1714  para = para->member.para.next_para;
1715  }
1716 
1717  editor->pCursors[1].pPara = para;
1718  editor->pCursors[1].pRun = ME_FindItemFwd(para, diRun);
1719  editor->pCursors[1].nOffset = 0;
1720  nOfs = ME_GetCursorOfs(&editor->pCursors[1]);
1721  nChars = ME_GetCursorOfs(&editor->pCursors[0]) - nOfs;
1722  ME_InternalDeleteText(editor, &editor->pCursors[1], nChars, TRUE);
1723  if (parser.tableDef)
1724  parser.tableDef->tableRowStart = NULL;
1725  }
1726  }
1728  RTFDestroy(&parser);
1729 
1730  if (parser.stackTop > 0)
1731  {
1732  while (--parser.stackTop >= 0)
1733  {
1734  ME_ReleaseStyle(parser.style);
1735  parser.style = parser.stack[parser.stackTop].style;
1736  }
1737  if (!inStream.editstream->dwError)
1739  }
1740 
1741  /* Remove last line break, as mandated by tests. This is not affected by
1742  CR/LF counters, since RTF streaming presents only \para tokens, which
1743  are converted according to the standard rules: \r for 2.0, \r\n for 1.0
1744  */
1745  if (stripLastCR && !(format & SFF_SELECTION)) {
1746  int newto;
1747  ME_GetSelection(editor, &selStart, &selEnd);
1748  newto = ME_GetCursorOfs(selEnd);
1749  if (newto > to + (editor->bEmulateVersion10 ? 1 : 0)) {
1750  WCHAR lastchar[3] = {'\0', '\0'};
1751  int linebreakSize = editor->bEmulateVersion10 ? 2 : 1;
1752  ME_Cursor linebreakCursor = *selEnd, lastcharCursor = *selEnd;
1753  CHARFORMAT2W cf;
1754 
1755  /* Set the final eop to the char fmt of the last char */
1756  cf.cbSize = sizeof(cf);
1757  cf.dwMask = CFM_ALL2;
1758  ME_MoveCursorChars(editor, &lastcharCursor, -1, FALSE);
1759  ME_GetCharFormat(editor, &lastcharCursor, &linebreakCursor, &cf);
1760  ME_SetSelection(editor, newto, -1);
1761  ME_SetSelectionCharFormat(editor, &cf);
1762  ME_SetSelection(editor, newto, newto);
1763 
1764  ME_MoveCursorChars(editor, &linebreakCursor, -linebreakSize, FALSE);
1765  ME_GetTextW(editor, lastchar, 2, &linebreakCursor, linebreakSize, FALSE, FALSE);
1766  if (lastchar[0] == '\r' && (lastchar[1] == '\n' || lastchar[1] == '\0')) {
1767  ME_InternalDeleteText(editor, &linebreakCursor, linebreakSize, FALSE);
1768  }
1769  }
1770  }
1771  to = ME_GetCursorOfs(&editor->pCursors[0]);
1772  num_read = to - from;
1773 
1774  style = parser.style;
1775  }
1776  else if (format & SF_TEXT)
1777  {
1778  num_read = ME_StreamInText(editor, format, &inStream, style);
1779  to = ME_GetCursorOfs(&editor->pCursors[0]);
1780  }
1781  else
1782  ERR("EM_STREAMIN without SF_TEXT or SF_RTF\n");
1783  /* put the cursor at the top */
1784  if (!(format & SFF_SELECTION))
1785  ME_SetSelection(editor, 0, 0);
1786  ME_CursorFromCharOfs(editor, from, &start);
1787  ME_UpdateLinkAttribute(editor, &start, to - from);
1788  }
1789 
1790  /* Restore saved undo mode */
1791  editor->nUndoMode = nUndoMode;
1792 
1793  /* even if we didn't add an undo, we need to commit anything on the stack */
1794  ME_CommitUndo(editor);
1795 
1796  /* If SFF_SELECTION isn't set, delete any undos from before we started too */
1797  if (!(format & SFF_SELECTION))
1798  ME_EmptyUndoStack(editor);
1799 
1801  editor->nEventMask = nEventMask;
1802  ME_UpdateRepaint(editor, FALSE);
1803  if (!(format & SFF_SELECTION)) {
1804  ME_ClearTempStyle(editor);
1805  }
1807  ME_MoveCaret(editor);
1809  ME_SendSelChange(editor);
1810  ME_SendRequestResize(editor, FALSE);
1811 
1812  return num_read;
1813 }
1814 
1815 
1817 {
1818  char *string;
1819  int pos;
1820  int length;
1822 
1824 {
1825  ME_RTFStringStreamStruct *pStruct = (ME_RTFStringStreamStruct *)dwCookie;
1826  int count;
1827 
1828  count = min(cb, pStruct->length - pStruct->pos);
1829  memmove(lpBuff, pStruct->string + pStruct->pos, count);
1830  pStruct->pos += count;
1831  *pcb = count;
1832  return 0;
1833 }
1834 
1835 static void
1837 {
1838  EDITSTREAM es;
1840 
1841  data.string = string;
1842  data.length = strlen(string);
1843  data.pos = 0;
1844  es.dwCookie = (DWORD_PTR)&data;
1845  es.pfnCallback = ME_ReadFromRTFString;
1846  ME_StreamIn(editor, SF_RTF | (selection ? SFF_SELECTION : 0), &es, TRUE);
1847 }
1848 
1849 
1850 static int
1851 ME_FindText(ME_TextEditor *editor, DWORD flags, const CHARRANGE *chrg, const WCHAR *text, CHARRANGE *chrgText)
1852 {
1853  const int nLen = lstrlenW(text);
1854  const int nTextLen = ME_GetTextLength(editor);
1855  int nMin, nMax;
1856  ME_Cursor cursor;
1857  WCHAR wLastChar = ' ';
1858 
1859  TRACE("flags==0x%08x, chrg->cpMin==%d, chrg->cpMax==%d text==%s\n",
1860  flags, chrg->cpMin, chrg->cpMax, debugstr_w(text));
1861 
1862  if (flags & ~(FR_DOWN | FR_MATCHCASE | FR_WHOLEWORD))
1863  FIXME("Flags 0x%08x not implemented\n",
1865 
1866  nMin = chrg->cpMin;
1867  if (chrg->cpMax == -1)
1868  nMax = nTextLen;
1869  else
1870  nMax = chrg->cpMax > nTextLen ? nTextLen : chrg->cpMax;
1871 
1872  /* In 1.0 emulation, if cpMax reaches end of text, add the FR_DOWN flag */
1873  if (editor->bEmulateVersion10 && nMax == nTextLen)
1874  {
1875  flags |= FR_DOWN;
1876  }
1877 
1878  /* In 1.0 emulation, cpMin must always be no greater than cpMax */
1879  if (editor->bEmulateVersion10 && nMax < nMin)
1880  {
1881  if (chrgText)
1882  {
1883  chrgText->cpMin = -1;
1884  chrgText->cpMax = -1;
1885  }
1886  return -1;
1887  }
1888 
1889  /* when searching up, if cpMin < cpMax, then instead of searching
1890  * on [cpMin,cpMax], we search on [0,cpMin], otherwise, search on
1891  * [cpMax, cpMin]. The exception is when cpMax is -1, in which
1892  * case, it is always bigger than cpMin.
1893  */
1894  if (!editor->bEmulateVersion10 && !(flags & FR_DOWN))
1895  {
1896  int nSwap = nMax;
1897 
1898  nMax = nMin > nTextLen ? nTextLen : nMin;
1899  if (nMin < nSwap || chrg->cpMax == -1)
1900  nMin = 0;
1901  else
1902  nMin = nSwap;
1903  }
1904 
1905  if (!nLen || nMin < 0 || nMax < 0 || nMax < nMin)
1906  {
1907  if (chrgText)
1908  chrgText->cpMin = chrgText->cpMax = -1;
1909  return -1;
1910  }
1911 
1912  if (flags & FR_DOWN) /* Forward search */
1913  {
1914  /* If possible, find the character before where the search starts */
1915  if ((flags & FR_WHOLEWORD) && nMin)
1916  {
1917  ME_CursorFromCharOfs(editor, nMin - 1, &cursor);
1918  wLastChar = *get_text( &cursor.pRun->member.run, cursor.nOffset );
1919  ME_MoveCursorChars(editor, &cursor, 1, FALSE);
1920  } else {
1921  ME_CursorFromCharOfs(editor, nMin, &cursor);
1922  }
1923 
1924  while (cursor.pRun && ME_GetCursorOfs(&cursor) + nLen <= nMax)
1925  {
1926  ME_DisplayItem *pCurItem = cursor.pRun;
1927  int nCurStart = cursor.nOffset;
1928  int nMatched = 0;
1929 
1930  while (pCurItem && ME_CharCompare( *get_text( &pCurItem->member.run, nCurStart + nMatched ), text[nMatched], (flags & FR_MATCHCASE)))
1931  {
1932  if ((flags & FR_WHOLEWORD) && isalnumW(wLastChar))
1933  break;
1934 
1935  nMatched++;
1936  if (nMatched == nLen)
1937  {
1938  ME_DisplayItem *pNextItem = pCurItem;
1939  int nNextStart = nCurStart;
1940  WCHAR wNextChar;
1941 
1942  /* Check to see if next character is a whitespace */
1943  if (flags & FR_WHOLEWORD)
1944  {
1945  if (nCurStart + nMatched == pCurItem->member.run.len)
1946  {
1947  pNextItem = ME_FindItemFwd(pCurItem, diRun);
1948  nNextStart = -nMatched;
1949  }
1950 
1951  if (pNextItem)
1952  wNextChar = *get_text( &pNextItem->member.run, nNextStart + nMatched );
1953  else
1954  wNextChar = ' ';
1955 
1956  if (isalnumW(wNextChar))
1957  break;
1958  }
1959 
1960  cursor.nOffset += cursor.pPara->member.para.nCharOfs + cursor.pRun->member.run.nCharOfs;
1961  if (chrgText)
1962  {
1963  chrgText->cpMin = cursor.nOffset;
1964  chrgText->cpMax = cursor.nOffset + nLen;
1965  }
1966  TRACE("found at %d-%d\n", cursor.nOffset, cursor.nOffset + nLen);
1967  return cursor.nOffset;
1968  }
1969  if (nCurStart + nMatched == pCurItem->member.run.len)
1970  {
1971  pCurItem = ME_FindItemFwd(pCurItem, diRun);
1972  nCurStart = -nMatched;
1973  }
1974  }
1975  if (pCurItem)
1976  wLastChar = *get_text( &pCurItem->member.run, nCurStart + nMatched );
1977  else
1978  wLastChar = ' ';
1979 
1980  cursor.nOffset++;
1981  if (cursor.nOffset == cursor.pRun->member.run.len)
1982  {
1983  ME_NextRun(&cursor.pPara, &cursor.pRun, TRUE);
1984  cursor.nOffset = 0;
1985  }
1986  }
1987  }
1988  else /* Backward search */
1989  {
1990  /* If possible, find the character after where the search ends */
1991  if ((flags & FR_WHOLEWORD) && nMax < nTextLen - 1)
1992  {
1993  ME_CursorFromCharOfs(editor, nMax + 1, &cursor);
1994  wLastChar = *get_text( &cursor.pRun->member.run, cursor.nOffset );
1995  ME_MoveCursorChars(editor, &cursor, -1, FALSE);
1996  } else {
1997  ME_CursorFromCharOfs(editor, nMax, &cursor);
1998  }
1999 
2000  while (cursor.pRun && ME_GetCursorOfs(&cursor) - nLen >= nMin)
2001  {
2002  ME_DisplayItem *pCurItem = cursor.pRun;
2003  ME_DisplayItem *pCurPara = cursor.pPara;
2004  int nCurEnd = cursor.nOffset;
2005  int nMatched = 0;
2006 
2007  if (nCurEnd == 0)
2008  {
2009  ME_PrevRun(&pCurPara, &pCurItem, TRUE);
2010  nCurEnd = pCurItem->member.run.len;
2011  }
2012 
2013  while (pCurItem && ME_CharCompare( *get_text( &pCurItem->member.run, nCurEnd - nMatched - 1 ),
2014  text[nLen - nMatched - 1], (flags & FR_MATCHCASE) ))
2015  {
2016  if ((flags & FR_WHOLEWORD) && isalnumW(wLastChar))
2017  break;
2018 
2019  nMatched++;
2020  if (nMatched == nLen)
2021  {
2022  ME_DisplayItem *pPrevItem = pCurItem;
2023  int nPrevEnd = nCurEnd;
2024  WCHAR wPrevChar;
2025  int nStart;
2026 
2027  /* Check to see if previous character is a whitespace */
2028  if (flags & FR_WHOLEWORD)
2029  {
2030  if (nPrevEnd - nMatched == 0)
2031  {
2032  pPrevItem = ME_FindItemBack(pCurItem, diRun);
2033  if (pPrevItem)
2034  nPrevEnd = pPrevItem->member.run.len + nMatched;
2035  }
2036 
2037  if (pPrevItem)
2038  wPrevChar = *get_text( &pPrevItem->member.run, nPrevEnd - nMatched - 1 );
2039  else
2040  wPrevChar = ' ';
2041 
2042  if (isalnumW(wPrevChar))
2043  break;
2044  }
2045 
2046  nStart = pCurPara->member.para.nCharOfs
2047  + pCurItem->member.run.nCharOfs + nCurEnd - nMatched;
2048  if (chrgText)
2049  {
2050  chrgText->cpMin = nStart;
2051  chrgText->cpMax = nStart + nLen;
2052  }
2053  TRACE("found at %d-%d\n", nStart, nStart + nLen);
2054  return nStart;
2055  }
2056  if (nCurEnd - nMatched == 0)
2057  {
2058  ME_PrevRun(&pCurPara, &pCurItem, TRUE);
2059  /* Don't care about pCurItem becoming NULL here; it's already taken
2060  * care of in the exterior loop condition */
2061  nCurEnd = pCurItem->member.run.len + nMatched;
2062  }
2063  }
2064  if (pCurItem)
2065  wLastChar = *get_text( &pCurItem->member.run, nCurEnd - nMatched - 1 );
2066  else
2067  wLastChar = ' ';
2068 
2069  cursor.nOffset--;
2070  if (cursor.nOffset < 0)
2071  {
2072  ME_PrevRun(&cursor.pPara, &cursor.pRun, TRUE);
2073  cursor.nOffset = cursor.pRun->member.run.len;
2074  }
2075  }
2076  }
2077  TRACE("not found\n");
2078  if (chrgText)
2079  chrgText->cpMin = chrgText->cpMax = -1;
2080  return -1;
2081 }
2082 
2084 {
2085  int nChars;
2086  ME_Cursor start;
2087 
2088  if (!ex->cb || !pText) return 0;
2089 
2090  if (ex->flags & ~(GT_SELECTION | GT_USECRLF))
2091  FIXME("GETTEXTEX flags 0x%08x not supported\n", ex->flags & ~(GT_SELECTION | GT_USECRLF));
2092 
2093  if (ex->flags & GT_SELECTION)
2094  {
2095  int from, to;
2096  int nStartCur = ME_GetSelectionOfs(editor, &from, &to);
2097  start = editor->pCursors[nStartCur];
2098  nChars = to - from;
2099  }
2100  else
2101  {
2102  ME_SetCursorToStart(editor, &start);
2103  nChars = INT_MAX;
2104  }
2105  if (ex->codepage == CP_UNICODE)
2106  {
2107  return ME_GetTextW(editor, (LPWSTR)pText, ex->cb / sizeof(WCHAR) - 1,
2108  &start, nChars, ex->flags & GT_USECRLF, FALSE);
2109  }
2110  else
2111  {
2112  /* potentially each char may be a CR, why calculate the exact value with O(N) when
2113  we can just take a bigger buffer? :)
2114  The above assumption still holds with CR/LF counters, since CR->CRLF expansion
2115  occurs only in richedit 2.0 mode, in which line breaks have only one CR
2116  */
2117  int crlfmul = (ex->flags & GT_USECRLF) ? 2 : 1;
2118  DWORD buflen;
2119  LPWSTR buffer;
2120  LRESULT rc;
2121 
2122  buflen = min(crlfmul * nChars, ex->cb - 1);
2123  buffer = heap_alloc((buflen + 1) * sizeof(WCHAR));
2124 
2125  nChars = ME_GetTextW(editor, buffer, buflen, &start, nChars, ex->flags & GT_USECRLF, FALSE);
2126  rc = WideCharToMultiByte(ex->codepage, 0, buffer, nChars + 1,
2127  (LPSTR)pText, ex->cb, ex->lpDefaultChar, ex->lpUsedDefChar);
2128  if (rc) rc--; /* do not count 0 terminator */
2129 
2130  heap_free(buffer);
2131  return rc;
2132  }
2133 }
2134 
2135 static int ME_GetTextRange(ME_TextEditor *editor, WCHAR *strText,
2136  const ME_Cursor *start, int nLen, BOOL unicode)
2137 {
2138  if (!strText) return 0;
2139  if (unicode) {
2140  return ME_GetTextW(editor, strText, INT_MAX, start, nLen, FALSE, FALSE);
2141  } else {
2142  int nChars;
2143  WCHAR *p = heap_alloc((nLen+1) * sizeof(*p));
2144  if (!p) return 0;
2145  nChars = ME_GetTextW(editor, p, nLen, start, nLen, FALSE, FALSE);
2146  WideCharToMultiByte(CP_ACP, 0, p, nChars+1, (char *)strText,
2147  nLen+1, NULL, NULL);
2148  heap_free(p);
2149  return nChars;
2150  }
2151 }
2152 
2153 static int handle_EM_EXSETSEL( ME_TextEditor *editor, int to, int from )
2154 {
2155  int end;
2156 
2157  TRACE("%d - %d\n", to, from );
2158 
2159  ME_InvalidateSelection( editor );
2160  end = ME_SetSelection( editor, to, from );
2161  ME_InvalidateSelection( editor );
2162  ITextHost_TxShowCaret( editor->texthost, FALSE );
2163  ME_ShowCaret( editor );
2164  ME_SendSelChange( editor );
2165 
2166  return end;
2167 }
2168 
2169 typedef struct tagME_GlobalDestStruct
2170 {
2171  HGLOBAL hData;
2172  int nLength;
2174 
2176 {
2178  int i;
2179  WORD *pSrc, *pDest;
2180 
2181  cb = cb >> 1;
2182  pDest = (WORD *)lpBuff;
2183  pSrc = GlobalLock(pData->hData);
2184  for (i = 0; i<cb && pSrc[pData->nLength+i]; i++) {
2185  pDest[i] = pSrc[pData->nLength+i];
2186  }
2187  pData->nLength += i;
2188  *pcb = 2*i;
2189  GlobalUnlock(pData->hData);
2190  return 0;
2191 }
2192 
2194 {
2196  int i;
2197  BYTE *pSrc, *pDest;
2198 
2199  pDest = lpBuff;
2200  pSrc = GlobalLock(pData->hData);
2201  for (i = 0; i<cb && pSrc[pData->nLength+i]; i++) {
2202  pDest[i] = pSrc[pData->nLength+i];
2203  }
2204  pData->nLength += i;
2205  *pcb = i;
2206  GlobalUnlock(pData->hData);
2207  return 0;
2208 }
2209 
2210 static const WCHAR rtfW[] = {'R','i','c','h',' ','T','e','x','t',' ','F','o','r','m','a','t',0};
2211 
2212 static HRESULT paste_rtf(ME_TextEditor *editor, FORMATETC *fmt, STGMEDIUM *med)
2213 {
2214  EDITSTREAM es;
2215  ME_GlobalDestStruct gds;
2216  HRESULT hr;
2217 
2218  gds.hData = med->u.hGlobal;
2219  gds.nLength = 0;
2220  es.dwCookie = (DWORD_PTR)&gds;
2221  es.pfnCallback = ME_ReadFromHGLOBALRTF;
2222  hr = ME_StreamIn( editor, SF_RTF | SFF_SELECTION, &es, FALSE ) == 0 ? E_FAIL : S_OK;
2223  ReleaseStgMedium( med );
2224  return hr;
2225 }
2226 
2227 static HRESULT paste_text(ME_TextEditor *editor, FORMATETC *fmt, STGMEDIUM *med)
2228 {
2229  EDITSTREAM es;
2230  ME_GlobalDestStruct gds;
2231  HRESULT hr;
2232 
2233  gds.hData = med->u.hGlobal;
2234  gds.nLength = 0;
2235  es.dwCookie = (DWORD_PTR)&gds;
2236  es.pfnCallback = ME_ReadFromHGLOBALUnicode;
2237  hr = ME_StreamIn( editor, SF_TEXT | SF_UNICODE | SFF_SELECTION, &es, FALSE ) == 0 ? E_FAIL : S_OK;
2238  ReleaseStgMedium( med );
2239  return hr;
2240 }
2241 
2242 static HRESULT paste_emf(ME_TextEditor *editor, FORMATETC *fmt, STGMEDIUM *med)
2243 {
2244  HRESULT hr;
2245  SIZEL sz = {0, 0};
2246 
2247  hr = insert_static_object( editor, med->u.hEnhMetaFile, NULL, &sz );
2248  if (SUCCEEDED(hr))
2249  {
2250  ME_CommitUndo( editor );
2251  ME_UpdateRepaint( editor, FALSE );
2252  }
2253  else
2254  ReleaseStgMedium( med );
2255 
2256  return hr;
2257 }
2258 
2259 static struct paste_format
2260 {
2261  FORMATETC fmt;
2262  HRESULT (*paste)(ME_TextEditor *, FORMATETC *, STGMEDIUM *);
2263  const WCHAR *name;
2264 } paste_formats[] =
2265 {
2266  {{ -1, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }, paste_rtf, rtfW },
2267  {{ CF_UNICODETEXT, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }, paste_text },
2268  {{ CF_ENHMETAFILE, NULL, DVASPECT_CONTENT, -1, TYMED_ENHMF }, paste_emf },
2269  {{ 0 }}
2270 };
2271 
2272 static void init_paste_formats(void)
2273 {
2274  struct paste_format *format;
2275  static int done;
2276 
2277  if (!done)
2278  {
2279  for (format = paste_formats; format->fmt.cfFormat; format++)
2280  {
2281  if (format->name)
2282  format->fmt.cfFormat = RegisterClipboardFormatW( format->name );
2283  }
2284  done = 1;
2285  }
2286 }
2287 
2288 static BOOL paste_special(ME_TextEditor *editor, UINT cf, REPASTESPECIAL *ps, BOOL check_only)
2289 {
2290  HRESULT hr;
2291  STGMEDIUM med;
2292  struct paste_format *format;
2293  IDataObject *data;
2294 
2295  /* Protect read-only edit control from modification */
2296  if (editor->styleFlags & ES_READONLY)
2297  {
2298  if (!check_only)
2300  return FALSE;
2301  }
2302 
2304 
2305  if (ps && ps->dwAspect != DVASPECT_CONTENT)
2306  FIXME("Ignoring aspect %x\n", ps->dwAspect);
2307 
2308  hr = OleGetClipboard( &data );
2309  if (hr != S_OK) return FALSE;
2310 
2311  if (cf == CF_TEXT) cf = CF_UNICODETEXT;
2312 
2313  hr = S_FALSE;
2314  for (format = paste_formats; format->fmt.cfFormat; format++)
2315  {
2316  if (cf && cf != format->fmt.cfFormat) continue;
2317  hr = IDataObject_QueryGetData( data, &format->fmt );
2318  if (hr == S_OK)
2319  {
2320  if (!check_only)
2321  {
2322  hr = IDataObject_GetData( data, &format->fmt, &med );
2323  if (hr != S_OK) goto done;
2324  hr = format->paste( editor, &format->fmt, &med );
2325  }
2326  break;
2327  }
2328  }
2329 
2330 done:
2331  IDataObject_Release( data );
2332 
2333  return hr == S_OK;
2334 }
2335 
2336 static BOOL ME_Copy(ME_TextEditor *editor, const ME_Cursor *start, int nChars)
2337 {
2338  LPDATAOBJECT dataObj = NULL;
2339  HRESULT hr = S_OK;
2340 
2341  if (editor->cPasswordMask)
2342  return FALSE; /* Copying or Cutting masked text isn't allowed */
2343 
2344  if(editor->lpOleCallback)
2345  {
2346  CHARRANGE range;
2347  range.cpMin = ME_GetCursorOfs(start);
2348  range.cpMax = range.cpMin + nChars;
2349  hr = IRichEditOleCallback_GetClipboardData(editor->lpOleCallback, &range, RECO_COPY, &dataObj);
2350  }
2351  if(FAILED(hr) || !dataObj)
2352  hr = ME_GetDataObject(editor, start, nChars, &dataObj);
2353  if(SUCCEEDED(hr)) {
2354  hr = OleSetClipboard(dataObj);
2355  IDataObject_Release(dataObj);
2356  }
2357  return SUCCEEDED(hr);
2358 }
2359 
2360 static BOOL copy_or_cut(ME_TextEditor *editor, BOOL cut)
2361 {
2362  BOOL result;
2363  int offs, num_chars;
2364  int start_cursor = ME_GetSelectionOfs(editor, &offs, &num_chars);
2365  ME_Cursor *sel_start = &editor->pCursors[start_cursor];
2366 
2367  if (cut && (editor->styleFlags & ES_READONLY))
2368  {
2370  return FALSE;
2371  }
2372 
2373  num_chars -= offs;
2374  result = ME_Copy(editor, sel_start, num_chars);
2375  if (result && cut)
2376  {
2377  ME_InternalDeleteText(editor, sel_start, num_chars, FALSE);
2378  ME_CommitUndo(editor);
2379  ME_UpdateRepaint(editor, TRUE);
2380  }
2381  return result;
2382 }
2383 
2384 /* helper to send a msg filter notification */
2385 static BOOL
2387 {
2388  MSGFILTER msgf;
2389 
2390  if (!editor->hWnd || !editor->hwndParent) return FALSE;
2391  msgf.nmhdr.hwndFrom = editor->hWnd;
2392  msgf.nmhdr.idFrom = GetWindowLongW(editor->hWnd, GWLP_ID);
2393  msgf.nmhdr.code = EN_MSGFILTER;
2394  msgf.msg = msg;
2395  msgf.wParam = *wParam;
2396  msgf.lParam = *lParam;
2397  if (SendMessageW(editor->hwndParent, WM_NOTIFY, msgf.nmhdr.idFrom, (LPARAM)&msgf))
2398  return FALSE;
2399  *wParam = msgf.wParam;
2400  *lParam = msgf.lParam;
2401  msgf.wParam = *wParam;
2402 
2403  return TRUE;
2404 }
2405 
2407 {
2408  ME_DisplayItem *startPara, *endPara;
2409  ME_DisplayItem *prev_para;
2410  ME_Cursor *from, *to;
2411  ME_Cursor start;
2412  int nChars;
2413 
2414  if (!editor->AutoURLDetect_bEnable) return;
2415 
2416  ME_GetSelection(editor, &from, &to);
2417 
2418  /* Find paragraph previous to the one that contains start cursor */
2419  startPara = from->pPara;
2420  prev_para = startPara->member.para.prev_para;
2421  if (prev_para->type == diParagraph) startPara = prev_para;
2422 
2423  /* Find paragraph that contains end cursor */
2424  endPara = to->pPara->member.para.next_para;
2425 
2426  start.pPara = startPara;
2427  start.pRun = ME_FindItemFwd(startPara, diRun);
2428  start.nOffset = 0;
2429  nChars = endPara->member.para.nCharOfs - startPara->member.para.nCharOfs;
2430 
2431  ME_UpdateLinkAttribute(editor, &start, nChars);
2432 }
2433 
2435 {
2436  BOOL ctrl_is_down = GetKeyState(VK_CONTROL) & 0x8000;
2437  BOOL shift_is_down = GetKeyState(VK_SHIFT) & 0x8000;
2438 
2439  if (editor->bDialogMode)
2440  {
2441  if (ctrl_is_down)
2442  return TRUE;
2443 
2444  if (!(editor->styleFlags & ES_WANTRETURN))
2445  {
2446  if (editor->hwndParent)
2447  {
2448  DWORD dw;
2449  dw = SendMessageW(editor->hwndParent, DM_GETDEFID, 0, 0);
2450  if (HIWORD(dw) == DC_HASDEFID)
2451  {
2452  HWND hwDefCtrl = GetDlgItem(editor->hwndParent, LOWORD(dw));
2453  if (hwDefCtrl)
2454  {
2455  SendMessageW(editor->hwndParent, WM_NEXTDLGCTL, (WPARAM)hwDefCtrl, TRUE);
2456  PostMessageW(hwDefCtrl, WM_KEYDOWN, VK_RETURN, 0);
2457  }
2458  }
2459  }
2460  return TRUE;
2461  }
2462  }
2463 
2464  if (editor->styleFlags & ES_MULTILINE)
2465  {
2466  static const WCHAR endl = '\r';
2467  static const WCHAR endlv10[] = {'\r','\n'};
2468  ME_Cursor cursor = editor->pCursors[0];
2469  ME_DisplayItem *para = cursor.pPara;
2470  int from, to;
2471  ME_Style *style, *eop_style;
2472 
2473  if (editor->styleFlags & ES_READONLY)
2474  {
2476  return TRUE;
2477  }
2478 
2479  ME_GetSelectionOfs(editor, &from, &to);
2480  if (editor->nTextLimit > ME_GetTextLength(editor) - (to-from))
2481  {
2482  if (!editor->bEmulateVersion10) /* v4.1 */
2483  {
2484  if (para->member.para.nFlags & MEPF_ROWEND)
2485  {
2486  /* Add a new table row after this row. */
2487  para = ME_AppendTableRow(editor, para);
2488  para = para->member.para.next_para;
2489  editor->pCursors[0].pPara = para;
2490  editor->pCursors[0].pRun = ME_FindItemFwd(para, diRun);
2491  editor->pCursors[0].nOffset = 0;
2492  editor->pCursors[1] = editor->pCursors[0];
2493  ME_CommitUndo(editor);
2495  ME_UpdateRepaint(editor, FALSE);
2496  return TRUE;
2497  }
2498  else if (para == editor->pCursors[1].pPara &&
2499  cursor.nOffset + cursor.pRun->member.run.nCharOfs == 0 &&
2500  para->member.para.prev_para->member.para.nFlags & MEPF_ROWSTART &&
2501  !para->member.para.prev_para->member.para.nCharOfs)
2502  {
2503  /* Insert a newline before the table. */
2504  para = para->member.para.prev_para;
2505  para->member.para.nFlags &= ~MEPF_ROWSTART;
2506  editor->pCursors[0].pPara = para;
2507  editor->pCursors[0].pRun = ME_FindItemFwd(para, diRun);
2508  editor->pCursors[1] = editor->pCursors[0];
2509  ME_InsertTextFromCursor(editor, 0, &endl, 1,
2510  editor->pCursors[0].pRun->member.run.style);
2511  para = editor->pBuffer->pFirst->member.para.next_para;
2512  ME_SetDefaultParaFormat(editor, &para->member.para.fmt);
2513  para->member.para.nFlags = 0;
2514  mark_para_rewrap(editor, para);
2515  editor->pCursors[0].pPara = para;
2516  editor->pCursors[0].pRun = ME_FindItemFwd(para, diRun);
2517  editor->pCursors[1] = editor->pCursors[0];
2518  para->member.para.next_para->member.para.nFlags |= MEPF_ROWSTART;
2519  ME_CommitCoalescingUndo(editor);
2521  ME_UpdateRepaint(editor, FALSE);
2522  return TRUE;
2523  }
2524  }
2525  else /* v1.0 - 3.0 */
2526  {
2527  ME_DisplayItem *para = cursor.pPara;
2528  if (ME_IsInTable(para))
2529  {
2530  if (cursor.pRun->member.run.nFlags & MERF_ENDPARA)
2531  {
2532  if (from == to)
2533  {
2535  para = ME_AppendTableRow(editor, para);
2536  editor->pCursors[0].pPara = para;
2537  editor->pCursors[0].pRun = ME_FindItemFwd(para, diRun);
2538  editor->pCursors[0].nOffset = 0;
2539  editor->pCursors[1] = editor->pCursors[0];
2540  ME_CommitCoalescingUndo(editor);
2541  ME_UpdateRepaint(editor, FALSE);
2542  return TRUE;
2543  }
2544  }
2545  else
2546  {
2548  if (cursor.pRun->member.run.nCharOfs + cursor.nOffset == 0 &&
2550  {
2551  /* Insert newline before table */
2552  cursor.pRun = ME_FindItemBack(para, diRun);
2553  if (cursor.pRun)
2554  {
2555  editor->pCursors[0].pRun = cursor.pRun;
2556  editor->pCursors[0].pPara = para->member.para.prev_para;
2557  }
2558  editor->pCursors[0].nOffset = 0;
2559  editor->pCursors[1] = editor->pCursors[0];
2560  ME_InsertTextFromCursor(editor, 0, &endl, 1,
2561  editor->pCursors[0].pRun->member.run.style);
2562  }
2563  else
2564  {
2565  editor->pCursors[1] = editor->pCursors[0];
2566  para = ME_AppendTableRow(editor, para);
2567  editor->pCursors[0].pPara = para;
2568  editor->pCursors[0].pRun = ME_FindItemFwd(para, diRun);
2569  editor->pCursors[0].nOffset = 0;
2570  editor->pCursors[1] = editor->pCursors[0];
2571  }
2572  ME_CommitCoalescingUndo(editor);
2573  ME_UpdateRepaint(editor, FALSE);
2574  return TRUE;
2575  }
2576  }
2577  }
2578 
2579  style = ME_GetInsertStyle(editor, 0);
2580 
2581  /* Normally the new eop style is the insert style, however in a list it is copied from the existing
2582  eop style (this prevents the list label style changing when the new eop is inserted).
2583  No extra ref is taken here on eop_style. */
2584  if (para->member.para.fmt.wNumbering)
2585  eop_style = para->member.para.eop_run->style;
2586  else
2587  eop_style = style;
2589  if (shift_is_down)
2590  ME_InsertEndRowFromCursor(editor, 0);
2591  else
2592  if (!editor->bEmulateVersion10)
2593  ME_InsertTextFromCursor(editor, 0, &endl, 1, eop_style);
2594  else
2595  ME_InsertTextFromCursor(editor, 0, endlv10, 2, eop_style);
2596  ME_CommitCoalescingUndo(editor);
2597  SetCursor(NULL);
2598 
2600  ME_UpdateRepaint(editor, FALSE);
2601  ME_SaveTempStyle(editor, style); /* set the temp insert style for the new para */
2603  }
2604  return TRUE;
2605  }
2606  return FALSE;
2607 }
2608 
2609 static BOOL
2611 {
2612  BOOL ctrl_is_down = GetKeyState(VK_CONTROL) & 0x8000;
2613  BOOL shift_is_down = GetKeyState(VK_SHIFT) & 0x8000;
2614 
2615  if (editor->bMouseCaptured)
2616  return FALSE;
2617  if (nKey != VK_SHIFT && nKey != VK_CONTROL && nKey != VK_MENU)
2618  editor->nSelectionType = stPosition;
2619 
2620  switch (nKey)
2621  {
2622  case VK_LEFT:
2623  case VK_RIGHT:
2624  case VK_HOME:
2625  case VK_END:
2626  editor->nUDArrowX = -1;
2627  /* fall through */
2628  case VK_UP:
2629  case VK_DOWN:
2630  case VK_PRIOR:
2631  case VK_NEXT:
2632  ME_CommitUndo(editor); /* End coalesced undos for typed characters */
2633  ME_ArrowKey(editor, nKey, shift_is_down, ctrl_is_down);
2634  return TRUE;
2635  case VK_BACK:
2636  case VK_DELETE:
2637  editor->nUDArrowX = -1;
2638  /* FIXME backspace and delete aren't the same, they act different wrt paragraph style of the merged paragraph */
2639  if (editor->styleFlags & ES_READONLY)
2640  return FALSE;
2641  if (ME_IsSelection(editor))
2642  {
2643  ME_DeleteSelection(editor);
2644  ME_CommitUndo(editor);
2645  }
2646  else if (nKey == VK_DELETE)
2647  {
2648  /* Delete stops group typing.
2649  * (See MSDN remarks on EM_STOPGROUPTYPING message) */
2650  ME_DeleteTextAtCursor(editor, 1, 1);
2651  ME_CommitUndo(editor);
2652  }
2653  else if (ME_ArrowKey(editor, VK_LEFT, FALSE, FALSE))
2654  {
2655  BOOL bDeletionSucceeded;
2656  /* Backspace can be grouped for a single undo */
2658  bDeletionSucceeded = ME_DeleteTextAtCursor(editor, 1, 1);
2659  if (!bDeletionSucceeded && !editor->bEmulateVersion10) { /* v4.1 */
2660  /* Deletion was prevented so the cursor is moved back to where it was.
2661  * (e.g. this happens when trying to delete cell boundaries)
2662  */
2663  ME_ArrowKey(editor, VK_RIGHT, FALSE, FALSE);
2664  }
2665  ME_CommitCoalescingUndo(editor);
2666  }
2667  else
2668  return TRUE;
2671  ME_UpdateRepaint(editor, FALSE);
2672  ME_SendRequestResize(editor, FALSE);
2673  return TRUE;
2674  case VK_RETURN:
2675  if (!editor->bEmulateVersion10)
2676  return handle_enter(editor);
2677  break;
2678  case VK_ESCAPE:
2679  if (editor->bDialogMode && editor->hwndParent)
2680  PostMessageW(editor->hwndParent, WM_CLOSE, 0, 0);
2681  return TRUE;
2682  case VK_TAB:
2683  if (editor->bDialogMode && editor->hwndParent)
2684  SendMessageW(editor->hwndParent, WM_NEXTDLGCTL, shift_is_down, 0);
2685  return TRUE;
2686  case 'A':
2687  if (ctrl_is_down)
2688  {
2689  handle_EM_EXSETSEL( editor, 0, -1 );
2690  return TRUE;
2691  }
2692  break;
2693  case 'V':
2694  if (ctrl_is_down)
2695  return paste_special( editor, 0, NULL, FALSE );
2696  break;
2697  case 'C':
2698  case 'X':
2699  if (ctrl_is_down)
2700  return copy_or_cut(editor, nKey == 'X');
2701  break;
2702  case 'Z':
2703  if (ctrl_is_down)
2704  {
2705  ME_Undo(editor);
2706  return TRUE;
2707  }
2708  break;
2709  case 'Y':
2710  if (ctrl_is_down)
2711  {
2712  ME_Redo(editor);
2713  return TRUE;
2714  }
2715  break;
2716 
2717  default:
2718  if (nKey != VK_SHIFT && nKey != VK_CONTROL && nKey && nKey != VK_MENU)
2719  editor->nUDArrowX = -1;
2720  if (ctrl_is_down)
2721  {
2722  if (nKey == 'W')
2723  {
2724  CHARFORMAT2W chf;
2725  char buf[2048];
2726  chf.cbSize = sizeof(chf);
2727 
2728  ME_GetSelectionCharFormat(editor, &chf);
2729  ME_DumpStyleToBuf(&chf, buf);
2730  MessageBoxA(NULL, buf, "Style dump", MB_OK);
2731  }
2732  if (nKey == 'Q')
2733  {
2734  ME_CheckCharOffsets(editor);
2735  }
2736  }
2737  }
2738  return FALSE;
2739 }
2740 
2741 static LRESULT ME_Char(ME_TextEditor *editor, WPARAM charCode,
2742  LPARAM flags, BOOL unicode)
2743 {
2744  WCHAR wstr;
2745 
2746  if (editor->bMouseCaptured)
2747  return 0;
2748 
2749  if (editor->styleFlags & ES_READONLY)
2750  {
2752  return 0; /* FIXME really 0 ? */
2753  }
2754 
2755  if (unicode)
2756  wstr = (WCHAR)charCode;
2757  else
2758  {
2759  CHAR charA = charCode;
2760  MultiByteToWideChar(CP_ACP, 0, &charA, 1, &wstr, 1);
2761  }
2762 
2763  if (editor->bEmulateVersion10 && wstr == '\r')
2764  handle_enter(editor);
2765 
2766  if ((unsigned)wstr >= ' ' || wstr == '\t')
2767  {
2768  ME_Cursor cursor = editor->pCursors[0];
2769  ME_DisplayItem *para = cursor.pPara;
2770  int from, to;
2771  BOOL ctrl_is_down = GetKeyState(VK_CONTROL) & 0x8000;
2772  ME_GetSelectionOfs(editor, &from, &to);
2773  if (wstr == '\t' &&
2774  /* v4.1 allows tabs to be inserted with ctrl key down */
2775  !(ctrl_is_down && !editor->bEmulateVersion10))
2776  {
2777  ME_DisplayItem *para;
2778  BOOL bSelectedRow = FALSE;
2779 
2780  para = cursor.pPara;
2781  if (ME_IsSelection(editor) &&
2782  cursor.pRun->member.run.nCharOfs + cursor.nOffset == 0 &&
2783  to == ME_GetCursorOfs(&editor->pCursors[0]) &&
2784  para->member.para.prev_para->type == diParagraph)
2785  {
2786  para = para->member.para.prev_para;
2787  bSelectedRow = TRUE;
2788  }
2789  if (ME_IsInTable(para))
2790  {
2791  ME_TabPressedInTable(editor, bSelectedRow);
2792  ME_CommitUndo(editor);
2793  return 0;
2794  }
2795  } else if (!editor->bEmulateVersion10) { /* v4.1 */
2796  if (para->member.para.nFlags & MEPF_ROWEND) {
2797  if (from == to) {
2798  para = para->member.para.next_para;
2799  if (para->member.para.nFlags & MEPF_ROWSTART)
2800  para = para->member.para.next_para;
2801  editor->pCursors[0].pPara = para;
2802  editor->pCursors[0].pRun = ME_FindItemFwd(para, diRun);
2803  editor->pCursors[0].nOffset = 0;
2804  editor->pCursors[1] = editor->pCursors[0];
2805  }
2806  }
2807  } else { /* v1.0 - 3.0 */
2808  if (ME_IsInTable(cursor.pRun) &&
2809  cursor.pRun->member.run.nFlags & MERF_ENDPARA &&
2810  from == to)
2811  {
2812  /* Text should not be inserted at the end of the table. */
2813  MessageBeep(-1);
2814  return 0;
2815  }
2816  }
2817  /* FIXME maybe it would make sense to call EM_REPLACESEL instead ? */
2818  /* WM_CHAR is restricted to nTextLimit */
2819  if(editor->nTextLimit > ME_GetTextLength(editor) - (to-from))
2820  {
2821  ME_Style *style = ME_GetInsertStyle(editor, 0);
2823  ME_InsertTextFromCursor(editor, 0, &wstr, 1, style);
2825  ME_CommitCoalescingUndo(editor);
2827  }
2828 
2830  ME_UpdateRepaint(editor, FALSE);
2831  }
2832  return 0;
2833 }
2834 
2835 /* Process the message and calculate the new click count.
2836  *
2837  * returns: The click count if it is mouse down event, else returns 0. */
2839  LPARAM lParam)
2840 {
2841  static int clickNum = 0;
2842  if (msg < WM_MOUSEFIRST || msg > WM_MOUSELAST)
2843  return 0;
2844 
2845  if ((msg == WM_LBUTTONDBLCLK) ||
2846  (msg == WM_RBUTTONDBLCLK) ||
2847  (msg == WM_MBUTTONDBLCLK) ||
2848  (msg == WM_XBUTTONDBLCLK))
2849  {
2851  }
2852 
2853  if ((msg == WM_LBUTTONDOWN) ||
2854  (msg == WM_RBUTTONDOWN) ||
2855  (msg == WM_MBUTTONDOWN) ||
2856  (msg == WM_XBUTTONDOWN))
2857  {
2858  static MSG prevClickMsg;
2859  MSG clickMsg;
2860  /* Compare the editor instead of the hwnd so that the this
2861  * can still be done for windowless richedit controls. */
2862  clickMsg.hwnd = (HWND)editor;
2863  clickMsg.message = msg;
2864  clickMsg.wParam = wParam;
2865  clickMsg.lParam = lParam;
2866  clickMsg.time = GetMessageTime();
2867  clickMsg.pt.x = (short)LOWORD(lParam);
2868  clickMsg.pt.y = (short)HIWORD(lParam);
2869  if ((clickNum != 0) &&
2870  (clickMsg.message == prevClickMsg.message) &&
2871  (clickMsg.hwnd == prevClickMsg.hwnd) &&
2872  (clickMsg.wParam == prevClickMsg.wParam) &&
2873  (clickMsg.time - prevClickMsg.time < GetDoubleClickTime()) &&
2874  (abs(clickMsg.pt.x - prevClickMsg.pt.x) < GetSystemMetrics(SM_CXDOUBLECLK)/2) &&
2875  (abs(clickMsg.pt.y - prevClickMsg.pt.y) < GetSystemMetrics(SM_CYDOUBLECLK)/2))
2876  {
2877  clickNum++;
2878  } else {
2879  clickNum = 1;
2880  }
2881  prevClickMsg = clickMsg;
2882  } else {
2883  return 0;
2884  }
2885  return clickNum;
2886 }
2887 
2888 static BOOL is_link( ME_Run *run )
2889 {
2890  return (run->style->fmt.dwMask & CFM_LINK) && (run->style->fmt.dwEffects & CFE_LINK);
2891 }
2892 
2894 {
2895  ME_Cursor cursor;
2896  POINT pt;
2897  BOOL isExact;
2898  SCROLLBARINFO sbi;
2899  DWORD messagePos = GetMessagePos();
2900  pt.x = (short)LOWORD(messagePos);
2901  pt.y = (short)HIWORD(messagePos);
2902 
2903  if (editor->hWnd)
2904  {
2905  sbi.cbSize = sizeof(sbi);
2906  GetScrollBarInfo(editor->hWnd, OBJID_HSCROLL, &sbi);
2908  PtInRect(&sbi.rcScrollBar, pt))
2909  {
2912  return TRUE;
2913  }
2914  sbi.cbSize = sizeof(sbi);
2915  GetScrollBarInfo(editor->hWnd, OBJID_VSCROLL, &sbi);
2917  PtInRect(&sbi.rcScrollBar, pt))
2918  {
2921  return TRUE;
2922  }
2923  }
2925 
2926  if (editor->nSelectionType == stLine && editor->bMouseCaptured) {
2928  return TRUE;
2929  }
2930  if (!editor->bEmulateVersion10 /* v4.1 */ &&
2931  pt.y < editor->rcFormat.top &&
2932  pt.x < editor->rcFormat.left)
2933  {
2935  return TRUE;
2936  }
2937  if (pt.y < editor->rcFormat.top || pt.y > editor->rcFormat.bottom)
2938  {
2939  if (editor->bEmulateVersion10) /* v1.0 - 3.0 */
2942  else /* v4.1 */
2945  return TRUE;
2946  }
2947  if (pt.x < editor->rcFormat.left)
2948  {
2950  return TRUE;
2951  }
2952  ME_CharFromPos(editor, pt.x, pt.y, &cursor, &isExact);
2953  if (isExact)
2954  {
2955  ME_Run *run;
2956 
2957  run = &cursor.pRun->member.run;
2958  if (is_link( run ))
2959  {
2962  FALSE);
2963  return TRUE;
2964  }
2965 
2966  if (ME_IsSelection(editor))
2967  {
2968  int selStart, selEnd;
2969  int offset = ME_GetCursorOfs(&cursor);
2970 
2971  ME_GetSelectionOfs(editor, &selStart, &selEnd);
2972  if (selStart <= offset && selEnd >= offset) {
2975  FALSE);
2976  return TRUE;
2977  }
2978  }
2979  }
2982  return TRUE;
2983 }
2984 
2986 {
2987  ITextHost_TxGetClientRect(editor->texthost, &editor->rcFormat);
2988  editor->rcFormat.top += editor->exStyleFlags & WS_EX_CLIENTEDGE ? 1 : 0;
2989  editor->rcFormat.left += 1 + editor->selofs;
2990  editor->rcFormat.right -= 1;
2991 }
2992 
2994 {
2995  LONG sel_type = SEL_EMPTY;
2996  LONG start, end;
2997 
2998  ME_GetSelectionOfs(editor, &start, &end);
2999  if (start == end)
3000  sel_type = SEL_EMPTY;
3001  else
3002  {
3003  LONG object_count = 0, character_count = 0;
3004  int i;
3005 
3006  for (i = 0; i < end - start; i++)
3007  {
3008  ME_Cursor cursor;
3009 
3010  ME_CursorFromCharOfs(editor, start + i, &cursor);
3011  if (cursor.pRun->member.run.reobj)
3012  object_count++;
3013  else
3014  character_count++;
3015  if (character_count >= 2 && object_count >= 2)
3017  }
3018  if (character_count)
3019  {
3020  sel_type |= SEL_TEXT;
3021  if (character_count >= 2)
3022  sel_type |= SEL_MULTICHAR;
3023  }
3024  if (object_count)
3025  {
3026  sel_type |= SEL_OBJECT;
3027  if (object_count >= 2)
3028  sel_type |= SEL_MULTIOBJECT;
3029  }
3030  }
3031  return sel_type;
3032 }
3033 
3034 static BOOL ME_ShowContextMenu(ME_TextEditor *editor, int x, int y)
3035 {
3036  CHARRANGE selrange;
3037  HMENU menu;
3038  int seltype;
3039 
3040  if(!editor->lpOleCallback || !editor->hWnd)
3041  return FALSE;
3042  ME_GetSelectionOfs(editor, &selrange.cpMin, &selrange.cpMax);
3043  seltype = ME_GetSelectionType(editor);
3044  if(SUCCEEDED(IRichEditOleCallback_GetContextMenu(editor->lpOleCallback, seltype, NULL, &selrange, &menu)))
3045  {
3046  TrackPopupMenu(menu, TPM_LEFTALIGN | TPM_RIGHTBUTTON, x, y, 0, editor->hwndParent, NULL);
3047  DestroyMenu(menu);
3048  }
3049  return TRUE;
3050 }
3051 
3052 ME_TextEditor *ME_MakeEditor(ITextHost *texthost, BOOL bEmulateVersion10)
3053 {
3054  ME_TextEditor *ed = heap_alloc(sizeof(*ed));
3055  int i;
3056  DWORD props;
3057  LONG selbarwidth;
3058 
3059  ed->hWnd = NULL;
3060  ed->hwndParent = NULL;
3061  ed->sizeWindow.cx = ed->sizeWindow.cy = 0;
3062  ed->texthost = texthost;
3063  ed->reOle = NULL;
3064  ed->bEmulateVersion10 = bEmulateVersion10;
3065  ed->styleFlags = 0;
3066  ed->exStyleFlags = 0;
3067  ed->first_marked_para = NULL;
3068  ed->total_rows = 0;
3069  ITextHost_TxGetPropertyBits(texthost,
3075  &props);
3076  ITextHost_TxGetScrollBars(texthost, &ed->styleFlags);
3077  ed->styleFlags &= (WS_VSCROLL|WS_HSCROLL|ES_AUTOVSCROLL|
3079  ed->pBuffer = ME_MakeText();
3080  ed->nZoomNumerator = ed->nZoomDenominator = 0;
3081  ed->nAvailWidth = 0; /* wrap to client area */
3082  list_init( &ed->style_list );
3084  /* The four cursors are for:
3085  * 0 - The position where the caret is shown
3086  * 1 - The anchored end of the selection (for normal selection)
3087  * 2 & 3 - The anchored start and end respectively for word, line,
3088  * or paragraph selection.
3089  */
3090  ed->nCursors = 4;
3091  ed->pCursors = heap_alloc(ed->nCursors * sizeof(*ed->pCursors));
3092  ME_SetCursorToStart(ed, &ed->pCursors[0]);
3093  ed->pCursors[1] = ed->pCursors[0];
3094  ed->pCursors[2] = ed->pCursors[0];
3095  ed->pCursors[3] = ed->pCursors[1];
3096  ed->nLastTotalLength = ed->nTotalLength = 0;
3097  ed->nLastTotalWidth = ed->nTotalWidth = 0;
3098  ed->nUDArrowX = -1;
3099  ed->rgbBackColor = -1;
3100  ed->hbrBackground = GetSysColorBrush(COLOR_WINDOW);
3101  ed->bCaretAtEnd = FALSE;
3102  ed->nEventMask = 0;
3103  ed->nModifyStep = 0;
3104  ed->nTextLimit = TEXT_LIMIT_DEFAULT;
3105  list_init( &ed->undo_stack );
3106  list_init( &ed->redo_stack );
3107  ed->nUndoStackSize = 0;
3108  ed->nUndoLimit = STACK_SIZE_DEFAULT;
3109  ed->nUndoMode = umAddToUndo;
3110  ed->nParagraphs = 1;
3111  ed->nLastSelStart = ed->nLastSelEnd = 0;
3112  ed->pLastSelStartPara = ed->pLastSelEndPara = ed->pCursors[0].pPara;
3113  ed->bHideSelection = FALSE;
3114  ed->pfnWordBreak = NULL;
3115  ed->lpOleCallback = NULL;
3117  ed->mode |= (props & TXTBIT_RICHTEXT) ? TM_RICHTEXT : TM_PLAINTEXT;
3118  ed->AutoURLDetect_bEnable = FALSE;
3119  ed->bHaveFocus = FALSE;
3120  ed->bDialogMode = FALSE;
3121  ed->bMouseCaptured = FALSE;
3122  for (i=0; i<HFONT_CACHE_SIZE; i++)
3123  {
3124  ed->pFontCache[i].nRefs = 0;
3125  ed->pFontCache[i].nAge = 0;
3126  ed->pFontCache[i].hFont = NULL;
3127  }
3128 
3130  SetRectEmpty(&ed->rcFormat);
3131  ed->bDefaultFormatRect = TRUE;
3132  ITextHost_TxGetSelectionBarWidth(ed->texthost, &selbarwidth);
3133  if (selbarwidth) {
3134  /* FIXME: Convert selbarwidth from HIMETRIC to pixels */
3135  ed->selofs = SELECTIONBAR_WIDTH;
3136  ed->styleFlags |= ES_SELECTIONBAR;
3137  } else {
3138  ed->selofs = 0;
3139  }
3140  ed->nSelectionType = stPosition;
3141 
3142  ed->cPasswordMask = 0;
3143  if (props & TXTBIT_USEPASSWORD)
3144  ITextHost_TxGetPasswordChar(texthost, &ed->cPasswordMask);
3145 
3146  if (props & TXTBIT_AUTOWORDSEL)
3147  ed->styleFlags |= ECO_AUTOWORDSELECTION;
3148  if (props & TXTBIT_MULTILINE) {
3149  ed->styleFlags |= ES_MULTILINE;
3150  ed->bWordWrap = (props & TXTBIT_WORDWRAP) != 0;
3151  } else {
3152  ed->bWordWrap = FALSE;
3153  }
3154  if (props & TXTBIT_READONLY)
3155  ed->styleFlags |= ES_READONLY;
3156  if (!(props & TXTBIT_HIDESELECTION))
3157  ed->styleFlags |= ES_NOHIDESEL;
3159  ed->styleFlags |= ES_SAVESEL;
3160  if (props & TXTBIT_VERTICAL)
3161  ed->styleFlags |= ES_VERTICAL;
3162  if (props & TXTBIT_DISABLEDRAG)
3163  ed->styleFlags |= ES_NOOLEDRAGDROP;
3164 
3165  ed->notified_cr.cpMin = ed->notified_cr.cpMax = 0;
3166 
3167  /* Default scrollbar information */
3168  ed->vert_si.cbSize = sizeof(SCROLLINFO);
3169  ed->vert_si.nMin = 0;
3170  ed->vert_si.nMax = 0;
3171  ed->vert_si.nPage = 0;
3172  ed->vert_si.nPos = 0;
3173 
3174  ed->horz_si.cbSize = sizeof(SCROLLINFO);
3175  ed->horz_si.nMin = 0;
3176  ed->horz_si.nMax = 0;
3177  ed->horz_si.nPage = 0;
3178  ed->horz_si.nPos = 0;
3179 
3180  ed->wheel_remain = 0;
3181 
3182  list_init( &ed->reobj_list );
3184 
3185  return ed;
3186 }
3187 
3189 {
3190  ME_DisplayItem *p = editor->pBuffer->pFirst, *pNext = NULL;
3191  ME_Style *s, *cursor2;
3192  int i;
3193 
3194  ME_ClearTempStyle(editor);
3195  ME_EmptyUndoStack(editor);
3196  editor->pBuffer->pFirst = NULL;
3197  while(p) {
3198  pNext = p->next;
3199  if (p->type == diParagraph)
3200  destroy_para(editor, p);
3201  else
3203  p = pNext;
3204  }
3205 
3206  LIST_FOR_EACH_ENTRY_SAFE( s, cursor2, &editor->style_list, ME_Style, entry )
3207  ME_DestroyStyle( s );
3208 
3210  for (i=0; i<HFONT_CACHE_SIZE; i++)
3211  {
3212  if (editor->pFontCache[i].hFont)
3213  DeleteObject(editor->pFontCache[i].hFont);
3214  }
3215  if (editor->rgbBackColor != -1)
3216  DeleteObject(editor->hbrBackground);
3217  if(editor->lpOleCallback)
3218  IRichEditOleCallback_Release(editor->lpOleCallback);
3219  ITextHost_Release(editor->texthost);
3220  if (editor->reOle)
3221  {
3222  IRichEditOle_Release(editor->reOle);
3223  editor->reOle = NULL;
3224  }
3225  OleUninitialize();
3226 
3227  heap_free(editor->pBuffer);
3228  heap_free(editor->pCursors);
3229  heap_free(editor);
3230 }
3231 
3233 {
3234  TRACE("\n");
3235  switch (fdwReason)
3236  {
3237  case DLL_PROCESS_ATTACH:
3238  DisableThreadLibraryCalls(hinstDLL);
3239  me_heap = HeapCreate (0, 0x10000, 0);
3240  if (!ME_RegisterEditorClass(hinstDLL)) return FALSE;
3242  LookupInit();
3243  break;
3244 
3245  case DLL_PROCESS_DETACH:
3246  if (lpvReserved) break;
3250  UnregisterClassA("RichEdit50A", 0);
3255  LookupCleanup();
3256  HeapDestroy (me_heap);
3257  release_typelib();
3258  break;
3259  }
3260  return TRUE;
3261 }
3262 
3263 static inline int get_default_line_height( ME_TextEditor *editor )
3264 {
3265  int height = 0;
3266 
3267  if (editor->pBuffer && editor->pBuffer->pDefaultStyle)
3268  height = editor->pBuffer->pDefaultStyle->tm.tmHeight;
3269  if (height <= 0) height = 24;
3270 
3271  return height;
3272 }
3273 
3274 static inline int calc_wheel_change( int *remain, int amount_per_click )
3275 {
3276  int change = amount_per_click * (float)*remain / WHEEL_DELTA;
3277  *remain -= WHEEL_DELTA * change / amount_per_click;
3278  return change;
3279 }
3280 
3281 static const char * const edit_messages[] = {
3282  "EM_GETSEL",
3283  "EM_SETSEL",
3284  "EM_GETRECT",
3285  "EM_SETRECT",
3286  "EM_SETRECTNP",
3287  "EM_SCROLL",
3288  "EM_LINESCROLL",
3289  "EM_SCROLLCARET",
3290  "EM_GETMODIFY",
3291  "EM_SETMODIFY",
3292  "EM_GETLINECOUNT",
3293  "EM_LINEINDEX",
3294  "EM_SETHANDLE",
3295  "EM_GETHANDLE",
3296  "EM_GETTHUMB",
3297  "EM_UNKNOWN_BF",
3298  "EM_UNKNOWN_C0",
3299  "EM_LINELENGTH",
3300  "EM_REPLACESEL",
3301  "EM_UNKNOWN_C3",
3302  "EM_GETLINE",
3303  "EM_LIMITTEXT",
3304  "EM_CANUNDO",
3305  "EM_UNDO",
3306  "EM_FMTLINES",
3307  "EM_LINEFROMCHAR",
3308  "EM_UNKNOWN_CA",
3309  "EM_SETTABSTOPS",
3310  "EM_SETPASSWORDCHAR",
3311  "EM_EMPTYUNDOBUFFER",
3312  "EM_GETFIRSTVISIBLELINE",
3313  "EM_SETREADONLY",
3314  "EM_SETWORDBREAKPROC",
3315  "EM_GETWORDBREAKPROC",
3316  "EM_GETPASSWORDCHAR",
3317  "EM_SETMARGINS",
3318  "EM_GETMARGINS",
3319  "EM_GETLIMITTEXT",
3320  "EM_POSFROMCHAR",
3321  "EM_CHARFROMPOS",
3322  "EM_SETIMESTATUS",
3323  "EM_GETIMESTATUS"
3324 };
3325 
3326 static const char * const richedit_messages[] = {
3327  "EM_CANPASTE",
3328  "EM_DISPLAYBAND",
3329  "EM_EXGETSEL",
3330  "EM_EXLIMITTEXT",
3331  "EM_EXLINEFROMCHAR",
3332  "EM_EXSETSEL",
3333  "EM_FINDTEXT",
3334  "EM_FORMATRANGE",
3335  "EM_GETCHARFORMAT",
3336  "EM_GETEVENTMASK",
3337  "EM_GETOLEINTERFACE",
3338  "EM_GETPARAFORMAT",
3339  "EM_GETSELTEXT",
3340  "EM_HIDESELECTION",
3341  "EM_PASTESPECIAL",
3342  "EM_REQUESTRESIZE",
3343  "EM_SELECTIONTYPE",
3344  "EM_SETBKGNDCOLOR",
3345  "EM_SETCHARFORMAT",
3346  "EM_SETEVENTMASK",
3347  "EM_SETOLECALLBACK",
3348  "EM_SETPARAFORMAT",
3349  "EM_SETTARGETDEVICE",
3350  "EM_STREAMIN",
3351  "EM_STREAMOUT",
3352  "EM_GETTEXTRANGE",
3353  "EM_FINDWORDBREAK",
3354  "EM_SETOPTIONS",
3355  "EM_GETOPTIONS",
3356  "EM_FINDTEXTEX",
3357  "EM_GETWORDBREAKPROCEX",
3358  "EM_SETWORDBREAKPROCEX",
3359  "EM_SETUNDOLIMIT",
3360  "EM_UNKNOWN_USER_83",
3361  "EM_REDO",
3362  "EM_CANREDO",
3363  "EM_GETUNDONAME",
3364  "EM_GETREDONAME",
3365  "EM_STOPGROUPTYPING",
3366  "EM_SETTEXTMODE",
3367  "EM_GETTEXTMODE",
3368  "EM_AUTOURLDETECT",
3369  "EM_GETAUTOURLDETECT",
3370  "EM_SETPALETTE",
3371  "EM_GETTEXTEX",
3372  "EM_GETTEXTLENGTHEX",
3373  "EM_SHOWSCROLLBAR",
3374  "EM_SETTEXTEX",
3375  "EM_UNKNOWN_USER_98",
3376  "EM_UNKNOWN_USER_99",
3377  "EM_SETPUNCTUATION",
3378  "EM_GETPUNCTUATION",
3379  "EM_SETWORDWRAPMODE",
3380  "EM_GETWORDWRAPMODE",
3381  "EM_SETIMECOLOR",
3382  "EM_GETIMECOLOR",
3383  "EM_SETIMEOPTIONS",
3384  "EM_GETIMEOPTIONS",
3385  "EM_CONVPOSITION",
3386  "EM_UNKNOWN_USER_109",
3387  "EM_UNKNOWN_USER_110",
3388  "EM_UNKNOWN_USER_111",
3389  "EM_UNKNOWN_USER_112",
3390  "EM_UNKNOWN_USER_113",
3391  "EM_UNKNOWN_USER_114",
3392  "EM_UNKNOWN_USER_115",
3393  "EM_UNKNOWN_USER_116",
3394  "EM_UNKNOWN_USER_117",
3395  "EM_UNKNOWN_USER_118",
3396  "EM_UNKNOWN_USER_119",
3397  "EM_SETLANGOPTIONS",
3398  "EM_GETLANGOPTIONS",
3399  "EM_GETIMECOMPMODE",
3400  "EM_FINDTEXTW",
3401  "EM_FINDTEXTEXW",
3402  "EM_RECONVERSION",
3403  "EM_SETIMEMODEBIAS",
3404  "EM_GETIMEMODEBIAS"
3405 };
3406 
3407 static const char *
3409 {
3410  if (msg >= EM_GETSEL && msg <= EM_CHARFROMPOS)
3411  return edit_messages[msg - EM_GETSEL];
3412  if (msg >= EM_CANPASTE && msg <= EM_GETIMEMODEBIAS)
3413  return richedit_messages[msg - EM_CANPASTE];
3414  return "";
3415 }
3416 
3418 {
3419  int x,y;
3420  BOOL isExact;
3421  ME_Cursor cursor; /* The start of the clicked text. */
3422 
3423  ENLINK info;
3424  x = (short)LOWORD(lParam);
3425  y = (short)HIWORD(lParam);
3426  ME_CharFromPos(editor, x, y, &cursor, &isExact);
3427  if (!isExact) return;
3428 
3429  if (is_link( &cursor.pRun->member.run ))
3430  { /* The clicked run has CFE_LINK set */
3431  ME_DisplayItem *di;
3432 
3433  info.nmhdr.hwndFrom = NULL;
3434  info.nmhdr.idFrom = 0;
3435  info.nmhdr.code = EN_LINK;
3436  info.msg = msg;
3437  info.wParam = wParam;
3438  info.lParam = lParam;
3439  cursor.nOffset = 0;
3440 
3441  /* find the first contiguous run with CFE_LINK set */
3442  info.chrg.cpMin = ME_GetCursorOfs(&cursor);
3443  di = cursor.pRun;
3444  while (ME_PrevRun( NULL, &di, FALSE ) && is_link( &di->member.run ))
3445  info.chrg.cpMin -= di->member.run.len;
3446 
3447  /* find the last contiguous run with CFE_LINK set */
3448  info.chrg.cpMax = ME_GetCursorOfs(&cursor) + cursor.pRun->member.run.len;
3449  di = cursor.pRun;
3450  while (ME_NextRun( NULL, &di, FALSE ) && is_link( &di->member.run ))
3451  info.chrg.cpMax += di->member.run.len;
3452 
3453  ITextHost_TxNotify(editor->texthost, info.nmhdr.code, &info);
3454  }
3455 }
3456 
3457 void ME_ReplaceSel(ME_TextEditor *editor, BOOL can_undo, const WCHAR *str, int len)
3458 {
3459  int from, to, nStartCursor;
3460  ME_Style *style;
3461 
3462  nStartCursor = ME_GetSelectionOfs(editor, &from, &to);
3464  ME_InternalDeleteText(editor, &editor->pCursors[nStartCursor], to-from, FALSE);
3465  ME_InsertTextFromCursor(editor, 0, str, len, style);
3467  /* drop temporary style if line end */
3468  /*
3469  * FIXME question: does abc\n mean: put abc,
3470  * clear temp style, put \n? (would require a change)
3471  */
3472  if (len>0 && str[len-1] == '\n')
3473  ME_ClearTempStyle(editor);
3474  ME_CommitUndo(editor);
3476  if (!can_undo)
3477  ME_EmptyUndoStack(editor);
3478  ME_UpdateRepaint(editor, FALSE);
3479 }
3480 
3481 static void ME_SetText(ME_TextEditor *editor, void *text, BOOL unicode)
3482 {
3483  LONG codepage = unicode ? CP_UNICODE : CP_ACP;
3484  int textLen;
3485 
3486  LPWSTR wszText = ME_ToUnicode(codepage, text, &textLen);
3487  ME_InsertTextFromCursor(editor, 0, wszText, textLen, editor->pBuffer->pDefaultStyle);
3488  ME_EndToUnicode(codepage, wszText);
3489 }
3490 
3492 {
3494  CREATESTRUCTA *createA = (CREATESTRUCTA*)lParam;
3495  void *text = NULL;
3496  INT max;
3497 
3498  if (lParam)
3499  text = unicode ? (void*)createW->lpszName : (void*)createA->lpszName;
3500 
3501  ME_SetDefaultFormatRect(editor);
3502 
3503  max = (editor->styleFlags & ES_DISABLENOSCROLL) ? 1 : 0;
3504  if (~editor->styleFlags & ES_DISABLENOSCROLL || editor->styleFlags & WS_VSCROLL)
3506 
3507  if (~editor->styleFlags & ES_DISABLENOSCROLL || editor->styleFlags & WS_HSCROLL)
3509 
3510  if (editor->styleFlags & ES_DISABLENOSCROLL)
3511  {
3512  if (editor->styleFlags & WS_VSCROLL)
3513  {
3516  }
3517  if (editor->styleFlags & WS_HSCROLL)
3518  {
3521  }
3522  }
3523 
3524  if (text)
3525  {
3526  ME_SetText(editor, text, unicode);
3527  ME_SetCursorToStart(editor, &editor->pCursors[0]);
3528  ME_SetCursorToStart(editor, &editor->pCursors[1]);
3529  }
3530 
3531  ME_CommitUndo(editor);
3532  ME_WrapMarkedParagraphs(editor);
3533  ME_MoveCaret(editor);
3534  return 0;
3535 }
3536 
3537 
3538 #define UNSUPPORTED_MSG(e) \
3539  case e: \
3540  FIXME(#e ": stub\n"); \
3541  *phresult = S_FALSE; \
3542  return 0;
3543 
3544 /* Handle messages for windowless and windowed richedit controls.
3545  *
3546  * The LRESULT that is returned is a return value for window procs,
3547  * and the phresult parameter is the COM return code needed by the
3548  * text services interface. */
3550  LPARAM lParam, BOOL unicode, HRESULT* phresult)
3551 {
3552  *phresult = S_OK;
3553 
3554  switch(msg) {
3555 
3578 
3579 /* Messages specific to Richedit controls */
3580 
3581  case EM_STREAMIN:
3582  return ME_StreamIn(editor, wParam, (EDITSTREAM*)lParam, TRUE);
3583  case EM_STREAMOUT:
3584  return ME_StreamOut(editor, wParam, (EDITSTREAM *)lParam);
3585  case WM_GETDLGCODE:
3586  {
3588 
3589  if (lParam)
3590  editor->bDialogMode = TRUE;
3591  if (editor->styleFlags & ES_MULTILINE)
3593  if (!(editor->styleFlags & ES_SAVESEL))
3594  code |= DLGC_HASSETSEL;
3595  return code;
3596  }
3597  case EM_EMPTYUNDOBUFFER:
3598  ME_EmptyUndoStack(editor);
3599  return 0;
3600  case EM_GETSEL:
3601  {
3602  /* Note: wParam/lParam can be NULL */
3603  UINT from, to;
3604  PUINT pfrom = wParam ? (PUINT)wParam : &from;
3605  PUINT pto = lParam ? (PUINT)lParam : &to;
3606  ME_GetSelectionOfs(editor, (int *)pfrom, (int *)pto);
3607  if ((*pfrom|*pto) & 0xFFFF0000)
3608  return -1;
3609  return MAKELONG(*pfrom,*pto);
3610  }
3611  case EM_EXGETSEL:
3612  {
3613  CHARRANGE *pRange = (CHARRANGE *)lParam;
3614  ME_GetSelectionOfs(editor, &pRange->cpMin, &pRange->cpMax);
3615  TRACE("EM_EXGETSEL = (%d,%d)\n", pRange->cpMin, pRange->cpMax);
3616  return 0;
3617  }
3618  case EM_SETUNDOLIMIT:
3619  {
3620  if ((int)wParam < 0)
3621  editor->nUndoLimit = STACK_SIZE_DEFAULT;
3622  else
3623  editor->nUndoLimit = min(wParam, STACK_SIZE_MAX);
3624  /* Setting a max stack size keeps wine from getting killed
3625  for hogging memory. Windows allocates all this memory at once, so
3626  no program would realistically set a value above our maximum. */
3627  return editor->nUndoLimit;
3628  }
3629  case EM_CANUNDO:
3630  return !list_empty( &editor->undo_stack );
3631  case EM_CANREDO:
3632  return !list_empty( &editor->redo_stack );
3633  case WM_UNDO: /* FIXME: actually not the same */
3634  case EM_UNDO:
3635  return ME_Undo(editor);
3636  case EM_REDO:
3637  return ME_Redo(editor);
3638  case EM_GETOPTIONS:
3639  {
3640  /* these flags are equivalent to the ES_* counterparts */
3643  DWORD settings = editor->styleFlags & mask;
3644 
3645  return settings;
3646  }
3647  case EM_SETFONTSIZE:
3648  {
3649  CHARFORMAT2W cf;
3650  LONG tmp_size, size;
3651  BOOL is_increase = ((LONG)wParam > 0);
3652 
3653  if (editor->mode & TM_PLAINTEXT)
3654  return FALSE;
3655 
3656  cf.cbSize = sizeof(cf);
3657  cf.dwMask = CFM_SIZE;
3658  ME_GetSelectionCharFormat(editor, &cf);
3659  tmp_size = (cf.yHeight / 20) + wParam;
3660 
3661  if (tmp_size <= 1)
3662  size = 1;
3663  else if (tmp_size > 12 && tmp_size < 28 && tmp_size % 2)
3664  size = tmp_size + (is_increase ? 1 : -1);
3665  else if (tmp_size > 28 && tmp_size < 36)
3666  size = is_increase ? 36 : 28;
3667  else if (tmp_size > 36 && tmp_size < 48)
3668  size = is_increase ? 48 : 36;
3669  else if (tmp_size > 48 && tmp_size < 72)
3670  size = is_increase ? 72 : 48;
3671  else if (tmp_size > 72 && tmp_size < 80)
3672  size = is_increase ? 80 : 72;
3673  else if (tmp_size > 80 && tmp_size < 1638)
3674  size = 10 * (is_increase ? (tmp_size / 10 + 1) : (tmp_size / 10));
3675  else if (tmp_size >= 1638)
3676  size = 1638;
3677  else
3678  size = tmp_size;
3679 
3680  cf.yHeight = size * 20; /* convert twips to points */
3681  ME_SetSelectionCharFormat(editor, &cf);
3682  ME_CommitUndo(editor);
3683  ME_WrapMarkedParagraphs(editor);
3684  ME_UpdateScrollBar(editor);
3685  ME_Repaint(editor);
3686 
3687  return TRUE;
3688  }
3689  case EM_SETOPTIONS:
3690  {
3691  /* these flags are equivalent to ES_* counterparts, except for
3692  * ECO_AUTOWORDSELECTION that doesn't have an ES_* counterpart,
3693  * but is still stored in editor->styleFlags. */
3697  DWORD settings = mask & editor->styleFlags;
3698  DWORD oldSettings = settings;
3699  DWORD changedSettings;
3700 
3701  switch(wParam)
3702  {
3703  case ECOOP_SET:
3704  settings = lParam;
3705  break;
3706  case ECOOP_OR:
3707  settings |= lParam;
3708  break;
3709  case ECOOP_AND:
3710  settings &= lParam;
3711  break;
3712  case ECOOP_XOR:
3713  settings ^= lParam;
3714  }
3715  changedSettings = oldSettings ^ settings;
3716 
3717  if (changedSettings) {
3718  editor->styleFlags = (editor->styleFlags & ~mask) | (settings & mask);
3719 
3720  if (changedSettings & ECO_SELECTIONBAR)
3721  {
3722  ITextHost_TxInvalidateRect(editor->texthost, &editor->rcFormat, TRUE);
3723  if (settings & ECO_SELECTIONBAR) {
3724  assert(!editor->selofs);
3725  editor->selofs = SELECTIONBAR_WIDTH;
3726  editor->rcFormat.left += editor->selofs;
3727  } else {
3728  editor->rcFormat.left -= editor->selofs;
3729  editor->selofs = 0;
3730  }
3731  ME_RewrapRepaint(editor);
3732  }
3733 
3734  if ((changedSettings & settings & ES_NOHIDESEL) && !editor->bHaveFocus)
3735  ME_InvalidateSelection( editor );
3736 
3737  if (changedSettings & settings & ECO_VERTICAL)
3738  FIXME("ECO_VERTICAL not implemented yet!\n");
3739  if (changedSettings & settings & ECO_AUTOHSCROLL)
3740  FIXME("ECO_AUTOHSCROLL not implemented yet!\n");
3741  if (changedSettings & settings & ECO_AUTOVSCROLL)
3742  FIXME("ECO_AUTOVSCROLL not implemented yet!\n");
3743  if (changedSettings & settings & ECO_WANTRETURN)
3744  FIXME("ECO_WANTRETURN not implemented yet!\n");
3745  if (changedSettings & settings & ECO_AUTOWORDSELECTION)
3746  FIXME("ECO_AUTOWORDSELECTION not implemented yet!\n");
3747  }
3748 
3749  return settings;
3750  }
3751  case EM_SETSEL:
3752  {
3753  return handle_EM_EXSETSEL( editor, wParam, lParam );
3754  }
3755  case EM_SETSCROLLPOS:
3756  {
3757  POINT *point = (POINT *)lParam;
3758  ME_ScrollAbs(editor, point->x, point->y);
3759  return 0;
3760  }
3761  case EM_AUTOURLDETECT:
3762  {
3763  if (wParam==1 || wParam ==0)
3764  {
3765  editor->AutoURLDetect_bEnable = (BOOL)wParam;
3766  return 0;
3767  }
3768  return E_INVALIDARG;
3769  }
3770  case EM_GETAUTOURLDETECT:
3771  {
3772  return editor->AutoURLDetect_bEnable;
3773  }
3774  case EM_EXSETSEL:
3775  {
3777 
3778  return handle_EM_EXSETSEL( editor, range.cpMin, range.cpMax );
3779  }
3780  case EM_SHOWSCROLLBAR:
3781  {
3782  DWORD flags;
3783 
3784  switch (wParam)
3785  {
3786  case SB_HORZ:
3787  flags = WS_HSCROLL;
3788  break;
3789  case SB_VERT:
3790  flags = WS_VSCROLL;
3791  break;
3792  case SB_BOTH:
3794  break;
3795  default:
3796  return 0;
3797  }
3798 
3799  if (lParam) {
3800  editor->styleFlags |= flags;
3801  if (flags & WS_HSCROLL)
3803  editor->nTotalWidth > editor->sizeWindow.cx);
3804  if (flags & WS_VSCROLL)
3806  editor->nTotalLength > editor->sizeWindow.cy);
3807  } else {
3808  editor->styleFlags &= ~flags;
3810  }
3811  return 0;
3812  }
3813  case EM_SETTEXTEX:
3814  {
3815  LPWSTR wszText;
3816  SETTEXTEX *pStruct = (SETTEXTEX *)wParam;
3817  int from, to, len;
3818  ME_Style *style;
3819  BOOL bRtf, bUnicode, bSelection, bUTF8;
3820  int oldModify = editor->nModifyStep;
3821  static const char utf8_bom[] = {0xef, 0xbb, 0xbf};
3822 
3823  if (!pStruct) return 0;
3824 
3825  /* If we detect ascii rtf at the start of the string,
3826  * we know it isn't unicode. */
3827  bRtf = (lParam && (!strncmp((char *)lParam, "{\\rtf", 5) ||
3828  !strncmp((char *)lParam, "{\\urtf", 6)));
3829  bUnicode = !bRtf && pStruct->codepage == CP_UNICODE;
3830  bUTF8 = (lParam && (!strncmp((char *)lParam, utf8_bom, 3)));
3831 
3832  TRACE("EM_SETTEXTEX - %s, flags %d, cp %d\n",
3833  bUnicode ? debugstr_w((LPCWSTR)lParam) : debugstr_a((LPCSTR)lParam),
3834  pStruct->flags, pStruct->codepage);
3835 
3836  bSelection = (pStruct->flags & ST_SELECTION) != 0;
3837  if (bSelection) {
3838  int nStartCursor = ME_GetSelectionOfs(editor, &from, &to);
3840  ME_InternalDeleteText(editor, &editor->pCursors[nStartCursor], to - from, FALSE);
3841  } else {
3842  ME_Cursor start;
3843  ME_SetCursorToStart(editor, &start);
3844  ME_InternalDeleteText(editor, &start, ME_GetTextLength(editor), FALSE);
3845  style = editor->pBuffer->pDefaultStyle;
3846  }
3847 
3848  if (bRtf) {
3849  ME_StreamInRTFString(editor, bSelection, (char *)lParam);
3850  if (bSelection) {
3851  /* FIXME: The length returned doesn't include the rtf control
3852  * characters, only the actual text. */
3853  len = lParam ? strlen((char *)lParam) : 0;
3854  }
3855  } else {
3856  if (bUTF8 && !bUnicode) {
3857  wszText = ME_ToUnicode(CP_UTF8, (void *)(lParam+3), &len);
3858  ME_InsertTextFromCursor(editor, 0, wszText, len, style);
3859  ME_EndToUnicode(CP_UTF8, wszText);
3860  } else {
3861  wszText = ME_ToUnicode(pStruct->codepage, (void *)lParam, &len);
3862  ME_InsertTextFromCursor(editor, 0, wszText, len, style);
3863  ME_EndToUnicode(pStruct->codepage, wszText);
3864  }
3865  }
3866 
3867  if (bSelection) {
3870  } else {
3871  ME_Cursor cursor;
3872  len = 1;
3873  ME_SetCursorToStart(editor, &cursor);
3875  }
3876  ME_CommitUndo(editor);
3877  if (!(pStruct->flags & ST_KEEPUNDO))
3878  {
3879  editor->nModifyStep = oldModify;
3880  ME_EmptyUndoStack(editor);
3881  }
3882  ME_UpdateRepaint(editor, FALSE);
3883  return len;
3884  }
3885  case EM_SELECTIONTYPE:
3886  return ME_GetSelectionType(editor);
3887  case EM_SETBKGNDCOLOR:
3888  {
3889  LRESULT lColor;
3890  if (editor->rgbBackColor != -1) {
3891  DeleteObject(editor->hbrBackground);
3892  lColor = editor->rgbBackColor;
3893  }
3894  else lColor = ITextHost_TxGetSysColor(editor->texthost, COLOR_WINDOW);
3895 
3896  if (wParam)
3897  {
3898  editor->rgbBackColor = -1;
3900  }
3901  else
3902  {
3903  editor->rgbBackColor = lParam;
3904  editor->hbrBackground = CreateSolidBrush(editor->rgbBackColor);
3905  }
3907  return lColor;
3908  }
3909  case EM_GETMODIFY:
3910  return editor->nModifyStep == 0 ? 0 : -1;
3911  case EM_SETMODIFY:
3912  {
3913  if (wParam)
3914  editor->nModifyStep = 1;
3915  else
3916  editor->nModifyStep = 0;
3917 
3918  return 0;
3919  }
3920  case EM_SETREADONLY:
3921  {
3922  if (wParam)
3923  editor->styleFlags |= ES_READONLY;
3924  else
3925  editor->styleFlags &= ~ES_READONLY;
3926  return 1;
3927  }
3928  case EM_SETEVENTMASK:
3929  {
3930  DWORD nOldMask = editor->nEventMask;
3931 
3932  editor->nEventMask = lParam;
3933  return nOldMask;
3934  }
3935  case EM_GETEVENTMASK:
3936  return editor->nEventMask;
3937  case EM_SETCHARFORMAT:
3938  {
3939  CHARFORMAT2W p;
3940  BOOL bRepaint = TRUE;
3941  if (!cfany_to_cf2w(&p, (CHARFORMAT2W *)lParam))
3942  return 0;
3943  if (wParam & SCF_ALL) {
3944  if (editor->mode & TM_PLAINTEXT) {
3945  ME_SetDefaultCharFormat(editor, &p);
3946  } else {
3947  ME_Cursor start;
3948  ME_SetCursorToStart(editor, &start);
3949  ME_SetCharFormat(editor, &start, NULL, &p);
3950  editor->nModifyStep = 1;
3951  }
3952  } else if (wParam & SCF_SELECTION) {
3953  if (editor->mode & TM_PLAINTEXT)
3954  return 0;
3955  if (wParam & SCF_WORD) {
3956  ME_Cursor start;
3957  ME_Cursor end = editor->pCursors[0];
3958  ME_MoveCursorWords(editor, &end, +1);
3959  start = end;
3960  ME_MoveCursorWords(editor, &start, -1);
3961  ME_SetCharFormat(editor, &start, &end, &p);
3962  }
3963  bRepaint = ME_IsSelection(editor);
3964  ME_SetSelectionCharFormat(editor, &p);
3965  if (bRepaint) editor->nModifyStep = 1;
3966  } else { /* SCF_DEFAULT */
3967  ME_SetDefaultCharFormat(editor, &p);
3968  }
3969  ME_CommitUndo(editor);
3970  if (bRepaint)
3971  {
3972  ME_WrapMarkedParagraphs(editor);
3973  ME_UpdateScrollBar(editor);
3974  ME_Repaint(editor);
3975  }
3976  return 1;
3977  }
3978  case EM_GETCHARFORMAT:
3979  {
3980  CHARFORMAT2W tmp, *dst = (CHARFORMAT2W *)lParam;
3981  if (dst->cbSize != sizeof(CHARFORMATA) &&
3982  dst->cbSize != sizeof(CHARFORMATW) &&
3983  dst->cbSize != sizeof(CHARFORMAT2A) &&
3984  dst->cbSize != sizeof(CHARFORMAT2W))
3985  return 0;
3986  tmp.cbSize = sizeof(tmp);
3987  if (!wParam)
3988  ME_GetDefaultCharFormat(editor, &tmp);
3989  else
3990  ME_GetSelectionCharFormat(editor, &tmp);
3991  cf2w_to_cfany(dst, &tmp);
3992  return tmp.dwMask;
3993  }
3994  case EM_SETPARAFORMAT:
3995  {
3997  ME_WrapMarkedParagraphs(editor);
3998  ME_UpdateScrollBar(editor);
3999  ME_Repaint(editor);
4000  ME_CommitUndo(editor);
4001  return result;
4002  }
4003  case EM_GETPARAFORMAT:
4005  return ((PARAFORMAT2 *)lParam)->dwMask;
4007  {
4008  ME_DisplayItem *p = editor->pBuffer->pFirst;
4009  int y = editor->vert_si.nPos;
4010  int ypara = 0;
4011  int count = 0;
4012  int ystart, yend;
4013  while(p) {
4015  if (p->type == diTextEnd)
4016  break;
4017  if (p->type == diParagraph) {
4018  ypara = p->member.para.pt.y;
4019  continue;
4020  }
4021  ystart = ypara + p->member.row.pt.y;
4022  yend = ystart + p->member.row.nHeight;
4023  if (y < yend) {
4024  break;
4025  }
4026  count++;
4027  }
4028  return count;
4029  }
4030  case EM_HIDESELECTION:
4031  {
4032  editor->bHideSelection = (wParam != 0);
4033  ME_InvalidateSelection(editor);
4034  return 0;
4035  }
4036  case EM_LINESCROLL:
4037  {
4038  if (!(editor->styleFlags & ES_MULTILINE))
4039  return FALSE;
4040  ME_ScrollDown( editor, lParam * get_default_line_height( editor ) );
4041  return TRUE;
4042  }
4043  case WM_CLEAR:
4044  {
4045  int from, to;
4046  int nStartCursor = ME_GetSelectionOfs(editor, &from, &to);
4047  ME_InternalDeleteText(editor, &editor->pCursors[nStartCursor], to-from, FALSE);
4048  ME_CommitUndo(editor);
4049  ME_UpdateRepaint(editor, TRUE);
4050  return 0;
4051  }
4052  case EM_REPLACESEL:
4053  {
4054  int len = 0;
4055  LONG codepage = unicode ? CP_UNICODE : CP_ACP;
4056  LPWSTR wszText = ME_ToUnicode(codepage, (void *)lParam, &len);
4057 
4058  TRACE("EM_REPLACESEL - %s\n", debugstr_w(wszText));
4059 
4060  ME_ReplaceSel(editor, !!wParam, wszText, len);
4061  ME_EndToUnicode(codepage, wszText);
4062  return len;
4063  }
4064  case EM_SCROLLCARET:
4065  ME_EnsureVisible(editor, &editor->pCursors[0]);
4066  return 0;
4067  case WM_SETFONT:
4068  {
4069  LOGFONTW lf;
4070  CHARFORMAT2W fmt;
4071  HDC hDC;
4072  BOOL bRepaint = LOWORD(lParam);
4073 
4074  if (!wParam)
4076 
4077  if (!GetObjectW((HGDIOBJ)wParam, sizeof(LOGFONTW), &lf))
4078  return 0;
4079 
4080  hDC = ITextHost_TxGetDC(editor->texthost);
4083  if (editor->mode & TM_RICHTEXT) {
4084  ME_Cursor start;
4085  ME_SetCursorToStart(editor, &start);
4086  ME_SetCharFormat(editor, &start, NULL, &fmt);
4087  }
4088  ME_SetDefaultCharFormat(editor, &fmt);
4089 
4090  ME_CommitUndo(editor);
4091  ME_MarkAllForWrapping(editor);
4092  ME_WrapMarkedParagraphs(editor);
4093  ME_UpdateScrollBar(editor);
4094  if (bRepaint)
4095  ME_Repaint(editor);
4096  return 0;
4097  }
4098  case WM_SETTEXT:
4099  {
4100  ME_Cursor cursor;
4101  ME_SetCursorToStart(editor, &cursor);
4102  ME_InternalDeleteText(editor, &cursor, ME_GetTextLength(editor), FALSE);
4103  if (lParam)
4104  {
4105  TRACE("WM_SETTEXT lParam==%lx\n",lParam);
4106  if (!strncmp((char *)lParam, "{\\rtf", 5) ||
4107  !strncmp((char *)lParam, "{\\urtf", 6))
4108  {
4109  /* Undocumented: WM_SETTEXT supports RTF text */
4110  ME_StreamInRTFString(editor, 0, (char *)lParam);
4111  }
4112  else
4113  ME_SetText(editor, (void*)lParam, unicode);
4114  }
4115  else
4116  TRACE("WM_SETTEXT - NULL\n");
4117  ME_SetCursorToStart(editor, &cursor);
4119  ME_SetSelection(editor, 0, 0);
4120  editor->nModifyStep = 0;
4121  ME_CommitUndo(editor);
4122  ME_EmptyUndoStack(editor);
4123  ME_UpdateRepaint(editor, FALSE);
4124  return 1;
4125  }
4126  case EM_CANPASTE:
4127  return paste_special( editor, 0, NULL, TRUE );
4128  case WM_PASTE:
4129  case WM_MBUTTONDOWN:
4130  wParam = 0;
4131  lParam = 0;
4132  /* fall through */
4133  case EM_PASTESPECIAL:
4135  return 0;
4136  case WM_CUT:
4137  case WM_COPY:
4138  copy_or_cut(editor, msg == WM_CUT);
4139  return 0;
4140  case WM_GETTEXTLENGTH:
4141  {
4142  GETTEXTLENGTHEX how;
4143 
4144  /* CR/LF conversion required in 2.0 mode, verbatim in 1.0 mode */
4145  how.flags = GTL_CLOSE | (editor->bEmulateVersion10 ? 0 : GTL_USECRLF) | GTL_NUMCHARS;
4146  how.codepage = unicode ? CP_UNICODE : CP_ACP;
4147  return ME_GetTextLengthEx(editor, &how);
4148  }
4149  case EM_GETTEXTLENGTHEX:
4150  return ME_GetTextLengthEx(editor, (GETTEXTLENGTHEX *)wParam);
4151  case WM_GETTEXT:
4152  {
4153  GETTEXTEX ex;
4154  ex.cb = wParam * (unicode ? sizeof(WCHAR) : sizeof(CHAR));
4155  ex.flags = GT_USECRLF;
4156  ex.codepage = unicode ? CP_UNICODE : CP_ACP;
4157  ex.lpDefaultChar = NULL;
4158  ex.lpUsedDefChar = NULL;
4159  return ME_GetTextEx(editor, &ex, lParam);
4160  }
4161  case EM_GETTEXTEX:
4162  return ME_GetTextEx(editor, (GETTEXTEX*)wParam, lParam);
4163  case EM_GETSELTEXT:
4164  {
4165  int nFrom, nTo, nStartCur = ME_GetSelectionOfs(editor, &nFrom, &nTo);
4166  ME_Cursor *from = &editor->pCursors[nStartCur];
4167  return ME_GetTextRange(editor, (WCHAR *)lParam, from,
4168  nTo - nFrom, unicode);
4169  }
4170  case EM_GETSCROLLPOS:
4171  {
4172  POINT *point = (POINT *)lParam;
4173  point->x = editor->horz_si.nPos;
4174  point->y = editor->vert_si.nPos;
4175  /* 16-bit scaled value is returned as stored in scrollinfo */
4176  if (editor->horz_si.nMax > 0xffff)
4177  point->x = MulDiv(point->x, 0xffff, editor->horz_si.nMax);
4178  if (editor->vert_si.nMax > 0xffff)
4179  point->y = MulDiv(point->y, 0xffff, editor->vert_si.nMax);
4180  return 1;
4181  }
4182  case EM_GETTEXTRANGE:
4183  {
4184  TEXTRANGEW *rng = (TEXTRANGEW *)lParam;
4185  ME_Cursor start;
4186  int nStart = rng->chrg.cpMin;
4187  int nEnd = rng->chrg.cpMax;
4188  int textlength = ME_GetTextLength(editor);
4189 
4190  TRACE("EM_GETTEXTRANGE min=%d max=%d unicode=%d textlength=%d\n",
4191  rng->chrg.cpMin, rng->chrg.cpMax, unicode, textlength);
4192  if (nStart < 0) return 0;
4193  if ((nStart == 0 && nEnd == -1) || nEnd > textlength)
4194  nEnd = textlength;
4195  if (nStart >= nEnd) return 0;
4196 
4197  ME_CursorFromCharOfs(editor, nStart, &start);
4198  return ME_GetTextRange(editor, rng->lpstrText, &start, nEnd - nStart, unicode);
4199  }
4200  case EM_GETLINE:
4201  {
4202  ME_DisplayItem *run;
4203  const unsigned int nMaxChars = *(WORD *) lParam;
4204  unsigned int nCharsLeft = nMaxChars;
4205  char *dest = (char *) lParam;
4206  BOOL wroteNull = FALSE;
4207 
4208  TRACE("EM_GETLINE: row=%d, nMaxChars=%d (%s)\n", (int) wParam, nMaxChars,
4209  unicode ? "Unicode" : "Ansi");
4210 
4211  run = ME_FindRowWithNumber(editor, wParam);
4212  if (run == NULL)
4213  return 0;
4214 
4215  while (nCharsLeft && (run = ME_FindItemFwd(run, diRunOrStartRow))
4216  && run->type == diRun)
4217  {
4218  WCHAR *str = get_text( &run->member.run, 0 );
4219  unsigned int nCopy;
4220 
4221  nCopy = min(nCharsLeft, run->member.run.len);
4222 
4223  if (unicode)
4224  memcpy(dest, str, nCopy * sizeof(WCHAR));
4225  else
4226  nCopy = WideCharToMultiByte(CP_ACP, 0, str, nCopy, dest,
4227  nCharsLeft, NULL, NULL);
4228  dest += nCopy * (unicode ? sizeof(WCHAR) : 1);
4229  nCharsLeft -= nCopy;
4230  }
4231 
4232  /* append line termination, space allowing */
4233  if (nCharsLeft > 0)
4234  {
4235  if (unicode)
4236  *((WCHAR *)dest) = '\0';
4237  else
4238  *dest = '\0';
4239  nCharsLeft--;
4240  wroteNull = TRUE;
4241  }
4242 
4243  TRACE("EM_GETLINE: got %u characters\n", nMaxChars - nCharsLeft);
4244  return nMaxChars - nCharsLeft - (wroteNull ? 1 : 0);
4245  }
4246  case EM_GETLINECOUNT:
4247  {
4248  ME_DisplayItem *item = editor->pBuffer->pLast;
4249  int nRows = editor->total_rows;
4250  ME_DisplayItem *prev_para = NULL, *last_para = NULL;
4251 
4252  last_para = ME_FindItemBack(item, diRun);
4253  prev_para = ME_FindItemBack(last_para, diRun);
4254  assert(last_para);
4255  assert(last_para->member.run.nFlags & MERF_ENDPARA);
4256  if (editor->bEmulateVersion10 && prev_para &&
4257  last_para->member.run.nCharOfs == 0 &&
4258  prev_para->member.run.len == 1 &&
4259  *get_text( &prev_para->member.run, 0 ) == '\r')
4260  {
4261  /* In 1.0 emulation, the last solitary \r at the very end of the text
4262  (if one exists) is NOT a line break.
4263  FIXME: this is an ugly hack. This should have a more regular model. */
4264  nRows--;
4265  }
4266 
4267  TRACE("EM_GETLINECOUNT: nRows==%d\n", nRows);
4268  return max(1, nRows);
4269  }
4270  case EM_LINEFROMCHAR:
4271  {
4272  if (wParam == -1)
4273  return ME_RowNumberFromCharOfs(editor, ME_GetCursorOfs(&editor->pCursors[1]));
4274  else
4275  return ME_RowNumberFromCharOfs(editor, wParam);
4276  }
4277  case EM_EXLINEFROMCHAR:
4278  {
4279  if (lParam == -1)
4280  return ME_RowNumberFromCharOfs(editor, ME_GetCursorOfs(&editor->pCursors[1]));
4281  else
4282  return ME_RowNumberFromCharOfs(editor, lParam);
4283  }
4284  case EM_LINEINDEX:
4285  {
4286  ME_DisplayItem *item, *para;
4287  int nCharOfs;
4288 
4289  if (wParam == -1)
4290  item = ME_FindItemBack(editor->pCursors[0].pRun, diStartRow);
4291  else
4292  item = ME_FindRowWithNumber(editor, wParam);
4293  if (!item)
4294  return -1;
4295  para = ME_GetParagraph(item);
4297  nCharOfs = para->member.para.nCharOfs + item->member.run.nCharOfs;
4298  TRACE("EM_LINEINDEX: nCharOfs==%d\n", nCharOfs);
4299  return nCharOfs;
4300  }
4301  case EM_LINELENGTH:
4302  {
4303  ME_DisplayItem *item, *item_end;
4304  int nChars = 0, nThisLineOfs = 0, nNextLineOfs = 0;
4305  ME_DisplayItem *para, *run;
4306 
4307  if (wParam > ME_GetTextLength(editor))
4308  return 0;
4309  if (wParam == -1)
4310  {
4311  FIXME("EM_LINELENGTH: returning number of unselected characters on lines with selection unsupported.\n");
4312  return 0;
4313  }
4314  ME_RunOfsFromCharOfs(editor, wParam, &para, &run, NULL);
4315  item = ME_RowStart(run);
4316  nThisLineOfs = ME_CharOfsFromRunOfs(editor, para, ME_FindItemFwd(item, diRun), 0);
4318  if (item_end->type == diStartRow) {
4319  nNextLineOfs = ME_CharOfsFromRunOfs(editor, para, ME_FindItemFwd(item_end, diRun), 0);
4320  } else {
4321  ME_DisplayItem *endRun = ME_FindItemBack(item_end, diRun);
4322  assert(endRun && endRun->member.run.nFlags & MERF_ENDPARA);
4323  nNextLineOfs = item_end->member.para.nCharOfs - endRun->member.run.len;
4324  }
4325  nChars = nNextLineOfs - nThisLineOfs;
4326  TRACE("EM_LINELENGTH(%ld)==%d\n",wParam, nChars);
4327  return nChars;
4328  }
4329  case EM_EXLIMITTEXT:
4330  {
4331  if ((int)lParam < 0)
4332  return 0;
4333  if (lParam == 0)
4334  editor->nTextLimit = 65536;
4335  else
4336  editor->nTextLimit = (int) lParam;
4337  return 0;
4338  }
4339  case EM_LIMITTEXT:
4340  {
4341  if (wParam == 0)
4342  editor->nTextLimit = 65536;
4343  else
4344  editor->nTextLimit = (int) wParam;
4345  return 0;
4346  }
4347  case EM_GETLIMITTEXT:
4348  {
4349  return editor->nTextLimit;
4350  }
4351  case EM_FINDTEXT:
4352  {
4353  LRESULT r;
4354  if(!unicode){
4355  FINDTEXTA *ft = (FINDTEXTA *)lParam;
4356  int nChars = MultiByteToWideChar(CP_ACP, 0, ft->lpstrText, -1, NULL, 0);
4357  WCHAR *tmp;
4358 
4359  if ((tmp = heap_alloc(nChars * sizeof(*tmp))) != NULL)
4360  MultiByteToWideChar(CP_ACP, 0, ft->lpstrText, -1, tmp, nChars);
4361  r = ME_FindText(editor, wParam, &ft->chrg, tmp, NULL);
4362  heap_free(tmp);
4363  }else{
4364  FINDTEXTW *ft = (FINDTEXTW *)lParam;
4365  r = ME_FindText(editor, wParam, &ft->chrg, ft->lpstrText, NULL);
4366  }
4367  return r;
4368  }
4369  case EM_FINDTEXTEX:
4370  {
4371  LRESULT r;
4372  if(!unicode){
4374  int nChars = MultiByteToWideChar(CP_ACP, 0, ex->lpstrText, -1, NULL, 0);
4375  WCHAR *tmp;
4376 
4377  if ((tmp = heap_alloc(nChars * sizeof(*tmp))) != NULL)
4378  MultiByteToWideChar(CP_ACP, 0, ex->lpstrText, -1, tmp, nChars);
4379  r = ME_FindText(editor, wParam, &ex->chrg, tmp, &ex->chrgText);
4380  heap_free(tmp);
4381  }else{
4383  r = ME_FindText(editor, wParam, &ex->chrg, ex->lpstrText, &ex->chrgText);
4384  }
4385  return r;
4386  }
4387  case EM_FINDTEXTW:
4388  {
4389  FINDTEXTW *ft = (FINDTEXTW *)lParam;
4390  return ME_FindText(editor, wParam, &ft->chrg, ft->lpstrText, NULL);
4391  }
4392  case EM_FINDTEXTEXW:
4393  {
4395  return ME_FindText(editor, wParam, &ex->chrg, ex->lpstrText, &ex->chrgText);
4396  }
4397  case EM_GETZOOM:
4398  if (!wParam || !lParam)
4399  return FALSE;
4400  *(int *)wParam = editor->nZoomNumerator;
4401  *(int *)