ReactOS  0.4.14-dev-556-g4c5b21f
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  LPRICHEDITOLE lpReOle = NULL;
1132  STGMEDIUM stgm;
1133  FORMATETC fm;
1134  CLSID clsid;
1135  HRESULT hr = E_FAIL;
1136  DWORD conn;
1137 
1138  if (hemf)
1139  {
1140  stgm.tymed = TYMED_ENHMF;
1141  stgm.u.hEnhMetaFile = hemf;
1142  fm.cfFormat = CF_ENHMETAFILE;
1143  }
1144  else if (hbmp)
1145  {
1146  stgm.tymed = TYMED_GDI;
1147  stgm.u.hBitmap = hbmp;
1148  fm.cfFormat = CF_BITMAP;
1149  }
1150  stgm.pUnkForRelease = NULL;
1151 
1152  fm.ptd = NULL;
1153  fm.dwAspect = DVASPECT_CONTENT;
1154  fm.lindex = -1;
1155  fm.tymed = stgm.tymed;
1156 
1157  if (!editor->reOle)
1158  {
1159  if (!CreateIRichEditOle(NULL, editor, (LPVOID *)&editor->reOle))
1160  return hr;
1161  }
1162 
1163  if (OleCreateDefaultHandler(&CLSID_NULL, NULL, &IID_IOleObject, (void**)&lpObject) == S_OK &&
1164  IUnknown_QueryInterface(editor->reOle, &IID_IRichEditOle, (void**)&lpReOle) == S_OK &&
1165  IRichEditOle_GetClientSite(lpReOle, &lpClientSite) == S_OK &&
1166  IOleObject_SetClientSite(lpObject, lpClientSite) == S_OK &&
1167  IOleObject_GetUserClassID(lpObject, &clsid) == S_OK &&
1168  IOleObject_QueryInterface(lpObject, &IID_IOleCache, (void**)&lpOleCache) == S_OK &&
1169  IOleCache_Cache(lpOleCache, &fm, 0, &conn) == S_OK &&
1170  IOleObject_QueryInterface(lpObject, &IID_IDataObject, (void**)&lpDataObject) == S_OK &&
1171  IDataObject_SetData(lpDataObject, &fm, &stgm, TRUE) == S_OK)
1172  {
1173  REOBJECT reobject;
1174 
1175  reobject.cbStruct = sizeof(reobject);
1176  reobject.cp = REO_CP_SELECTION;
1177  reobject.clsid = clsid;
1178  reobject.poleobj = lpObject;
1179  reobject.pstg = lpStorage;
1180  reobject.polesite = lpClientSite;
1181  /* convert from twips to .01 mm */
1182  reobject.sizel.cx = MulDiv(sz->cx, 254, 144);
1183  reobject.sizel.cy = MulDiv(sz->cy, 254, 144);
1184  reobject.dvaspect = DVASPECT_CONTENT;
1185  reobject.dwFlags = 0; /* FIXME */
1186  reobject.dwUser = 0;
1187 
1188  ME_InsertOLEFromCursor(editor, &reobject, 0);
1189  hr = S_OK;
1190  }
1191 
1192  if (lpObject) IOleObject_Release(lpObject);
1193  if (lpClientSite) IOleClientSite_Release(lpClientSite);
1194  if (lpStorage) IStorage_Release(lpStorage);
1195  if (lpDataObject) IDataObject_Release(lpDataObject);
1196  if (lpOleCache) IOleCache_Release(lpOleCache);
1197  if (lpReOle) IRichEditOle_Release(lpReOle);
1198 
1199  return hr;
1200 }
1201 
1203 {
1204  int level = 1;
1205 
1206  for (;;)
1207  {
1208  RTFGetToken (info);
1209 
1210  if (info->rtfClass == rtfEOF) return;
1212  {
1213  if (--level == 0) break;
1214  }
1215  else if (RTFCheckCM( info, rtfGroup, rtfBeginGroup ))
1216  {
1217  level++;
1218  }
1219  else
1220  {
1221  RTFRouteToken( info );
1223  level--;
1224  }
1225  }
1226 
1227  RTFRouteToken( info ); /* feed "}" back to router */
1228  return;
1229 }
1230 
1232 {
1233  DWORD read = 0, size = 1024;
1234  BYTE *buf, val;
1235  BOOL flip;
1236 
1237  *out = NULL;
1238 
1239  if (info->rtfClass != rtfText)
1240  {
1241  ERR("Called with incorrect token\n");
1242  return 0;
1243  }
1244 
1245  buf = HeapAlloc( GetProcessHeap(), 0, size );
1246  if (!buf) return 0;
1247 
1248  val = info->rtfMajor;
1249  for (flip = TRUE;; flip = !flip)
1250  {
1251  RTFGetToken( info );
1252  if (info->rtfClass == rtfEOF)
1253  {
1254  HeapFree( GetProcessHeap(), 0, buf );
1255  return 0;
1256  }
1257  if (info->rtfClass != rtfText) break;
1258  if (flip)
1259  {
1260  if (read >= size)
1261  {
1262  size *= 2;
1263  buf = HeapReAlloc( GetProcessHeap(), 0, buf, size );
1264  if (!buf) return 0;
1265  }
1266  buf[read++] = RTFCharToHex(val) * 16 + RTFCharToHex(info->rtfMajor);
1267  }
1268  else
1269  val = info->rtfMajor;
1270  }
1271  if (flip) FIXME("wrong hex string\n");
1272 
1273  *out = buf;
1274  return read;
1275 }
1276 
1278 {
1279  SIZEL sz;
1280  BYTE *buffer = NULL;
1281  DWORD size = 0;
1282  METAFILEPICT mfp;
1283  HENHMETAFILE hemf;
1284  HBITMAP hbmp;
1285  enum gfxkind {gfx_unknown = 0, gfx_enhmetafile, gfx_metafile, gfx_dib} gfx = gfx_unknown;
1286  int level = 1;
1287 
1288  mfp.mm = MM_TEXT;
1289  sz.cx = sz.cy = 0;
1290 
1291  for (;;)
1292  {
1293  RTFGetToken( info );
1294 
1295  if (info->rtfClass == rtfText)
1296  {
1297  if (level == 1)
1298  {
1299  if (!buffer)
1300  size = read_hex_data( info, &buffer );
1301  }
1302  else
1303  {
1304  RTFSkipGroup( info );
1305  }
1306  } /* We potentially have a new token so fall through. */
1307 
1308  if (info->rtfClass == rtfEOF) return;
1309 
1311  {
1312  if (--level == 0) break;
1313  continue;
1314  }
1316  {
1317  level++;
1318  continue;
1319  }
1321  {
1322  RTFRouteToken( info );
1324  level--;
1325  continue;
1326  }
1327 
1329  {
1330  mfp.mm = info->rtfParam;
1331  gfx = gfx_metafile;
1332  }
1334  {
1335  if (info->rtfParam != 0) FIXME("dibitmap should be 0 (%d)\n", info->rtfParam);
1336  gfx = gfx_dib;
1337  }
1338  else if (RTFCheckMM( info, rtfPictAttr, rtfEmfBlip ))
1339  gfx = gfx_enhmetafile;
1340  else if (RTFCheckMM( info, rtfPictAttr, rtfPicWid ))
1341  mfp.xExt = info->rtfParam;
1342  else if (RTFCheckMM( info, rtfPictAttr, rtfPicHt ))
1343  mfp.yExt = info->rtfParam;
1344  else if (RTFCheckMM( info, rtfPictAttr, rtfPicGoalWid ))
1345  sz.cx = info->rtfParam;
1346  else if (RTFCheckMM( info, rtfPictAttr, rtfPicGoalHt ))
1347  sz.cy = info->rtfParam;
1348  else
1349  FIXME("Non supported attribute: %d %d %d\n", info->rtfClass, info->rtfMajor, info->rtfMinor);
1350  }
1351 
1352  if (buffer)
1353  {
1354  switch (gfx)
1355  {
1356  case gfx_enhmetafile:
1357  if ((hemf = SetEnhMetaFileBits( size, buffer )))
1358  insert_static_object( info->editor, hemf, NULL, &sz );
1359  break;
1360  case gfx_metafile:
1361  if ((hemf = SetWinMetaFileBits( size, buffer, NULL, &mfp )))
1362  insert_static_object( info->editor, hemf, NULL, &sz );
1363  break;
1364  case gfx_dib:
1365  {
1366  BITMAPINFO *bi = (BITMAPINFO*)buffer;
1367  HDC hdc = GetDC(0);
1368  unsigned nc = bi->bmiHeader.biClrUsed;
1369 
1370  /* not quite right, especially for bitfields type of compression */
1371  if (!nc && bi->bmiHeader.biBitCount <= 8)
1372  nc = 1 << bi->bmiHeader.biBitCount;
1373  if ((hbmp = CreateDIBitmap( hdc, &bi->bmiHeader,
1374  CBM_INIT, (char*)(bi + 1) + nc * sizeof(RGBQUAD),
1375  bi, DIB_RGB_COLORS)) )
1376  insert_static_object( info->editor, NULL, hbmp, &sz );
1377  ReleaseDC( 0, hdc );
1378  break;
1379  }
1380  default:
1381  break;
1382  }
1383  }
1384  HeapFree( GetProcessHeap(), 0, buffer );
1385  RTFRouteToken( info ); /* feed "}" back to router */
1386  return;
1387 }
1388 
1389 /* for now, lookup the \result part and use it, whatever the object */
1391 {
1392  for (;;)
1393  {
1394  RTFGetToken (info);
1395  if (info->rtfClass == rtfEOF)
1396  return;
1398  break;
1400  {
1401  RTFGetToken (info);
1402  if (info->rtfClass == rtfEOF)
1403  return;
1405  {
1406  int level = 1;
1407 
1408  while (RTFGetToken (info) != rtfEOF)
1409  {
1410  if (info->rtfClass == rtfGroup)
1411  {
1412  if (info->rtfMajor == rtfBeginGroup) level++;
1413  else if (info->rtfMajor == rtfEndGroup && --level < 0) break;
1414  }
1416  }
1417  }
1418  else RTFSkipGroup(info);
1419  continue;
1420  }
1422  {
1423  FIXME("Non supported attribute: %d %d %d\n", info->rtfClass, info->rtfMajor, info->rtfMinor);
1424  return;
1425  }
1426  }
1427  RTFRouteToken(info); /* feed "}" back to router */
1428 }
1429 
1431 {
1432  int level = 1, type = -1;
1433  WORD indent = 0, start = 1;
1434  WCHAR txt_before = 0, txt_after = 0;
1435 
1436  for (;;)
1437  {
1438  RTFGetToken( info );
1439 
1442  {
1443  int loc = info->rtfMinor;
1444 
1445  RTFGetToken( info );
1446  if (info->rtfClass == rtfText)
1447  {
1448  if (loc == rtfParNumTextBefore)
1449  txt_before = info->rtfMajor;
1450  else
1451  txt_after = info->rtfMajor;
1452  continue;
1453  }
1454  /* falling through to catch EOFs and group level changes */
1455  }
1456 
1457  if (info->rtfClass == rtfEOF)
1458  return;
1459 
1461  {
1462  if (--level == 0) break;
1463  continue;
1464  }
1465 
1467  {
1468  level++;
1469  continue;
1470  }
1471 
1472  /* Ignore non para-attr */
1474  continue;
1475 
1476  switch (info->rtfMinor)
1477  {
1478  case rtfParLevel: /* Para level is ignored */
1479  case rtfParSimple:
1480  break;
1481  case rtfParBullet:
1482  type = PFN_BULLET;
1483  break;
1484 
1485  case rtfParNumDecimal:
1486  type = PFN_ARABIC;
1487  break;
1488  case rtfParNumULetter:
1489  type = PFN_UCLETTER;
1490  break;
1491  case rtfParNumURoman:
1492  type = PFN_UCROMAN;
1493  break;
1494  case rtfParNumLLetter:
1495  type = PFN_LCLETTER;
1496  break;
1497  case rtfParNumLRoman:
1498  type = PFN_LCROMAN;
1499  break;
1500 
1501  case rtfParNumIndent:
1502  indent = info->rtfParam;
1503  break;
1504  case rtfParNumStartAt:
1505  start = info->rtfParam;
1506  break;
1507  }
1508  }
1509 
1510  if (type != -1)
1511  {
1513  info->fmt.wNumbering = type;
1514  info->fmt.wNumberingStart = start;
1515  info->fmt.wNumberingStyle = PFNS_PAREN;
1516  if (type != PFN_BULLET)
1517  {
1518  if (txt_before == 0 && txt_after == 0)
1519  info->fmt.wNumberingStyle = PFNS_PLAIN;
1520  else if (txt_after == '.')
1521  info->fmt.wNumberingStyle = PFNS_PERIOD;
1522  else if (txt_before == '(' && txt_after == ')')
1523  info->fmt.wNumberingStyle = PFNS_PARENS;
1524  }
1525  info->fmt.wNumberingTab = indent;
1526  }
1527 
1528  TRACE("type %d indent %d start %d txt before %04x txt after %04x\n",
1529  type, indent, start, txt_before, txt_after);
1530 
1531  RTFRouteToken( info ); /* feed "}" back to router */
1532 }
1533 
1535 {
1536  switch(info->rtfClass)
1537  {
1538  case rtfGroup:
1539  switch(info->rtfMajor)
1540  {
1541  case rtfBeginGroup:
1542  if (info->stackTop < maxStack) {
1543  info->stack[info->stackTop].style = info->style;
1544  ME_AddRefStyle(info->style);
1545  info->stack[info->stackTop].codePage = info->codePage;
1546  info->stack[info->stackTop].unicodeLength = info->unicodeLength;
1547  }
1548  info->stackTop++;
1549  info->styleChanged = FALSE;
1550  break;
1551  case rtfEndGroup:
1552  {
1554  info->stackTop--;
1555  if (info->stackTop <= 0)
1556  info->rtfClass = rtfEOF;
1557  if (info->stackTop < 0)
1558  return;
1559 
1560  ME_ReleaseStyle(info->style);
1561  info->style = info->stack[info->stackTop].style;
1562  info->codePage = info->stack[info->stackTop].codePage;
1563  info->unicodeLength = info->stack[info->stackTop].unicodeLength;
1564  break;
1565  }
1566  }
1567  break;
1568  }
1569 }
1570 
1571 void
1573 {
1574  stream->editstream->dwError = stream->editstream->pfnCallback(stream->editstream->dwCookie,
1575  (BYTE *)stream->buffer,
1576  sizeof(stream->buffer),
1577  (LONG *)&stream->dwSize);
1578  stream->dwUsed = 0;
1579 }
1580 
1582 {
1583  RTF_Info parser;
1584  ME_Style *style;
1585  int from, to, nUndoMode;
1586  int nEventMask = editor->nEventMask;
1587  ME_InStream inStream;
1588  BOOL invalidRTF = FALSE;
1589  ME_Cursor *selStart, *selEnd;
1590  LRESULT num_read = 0; /* bytes read for SF_TEXT, non-control chars inserted for SF_RTF */
1591 
1592  TRACE("stream==%p editor==%p format==0x%X\n", stream, editor, format);
1593  editor->nEventMask = 0;
1594 
1595  ME_GetSelectionOfs(editor, &from, &to);
1596  if (format & SFF_SELECTION && editor->mode & TM_RICHTEXT)
1597  {
1598  ME_GetSelection(editor, &selStart, &selEnd);
1600 
1601  ME_InternalDeleteText(editor, selStart, to - from, FALSE);
1602 
1603  /* Don't insert text at the end of the table row */
1604  if (!editor->bEmulateVersion10) { /* v4.1 */
1605  ME_DisplayItem *para = editor->pCursors->pPara;
1606  if (para->member.para.nFlags & MEPF_ROWEND)
1607  {
1608  para = para->member.para.next_para;
1609  editor->pCursors[0].pPara = para;
1610  editor->pCursors[0].pRun = ME_FindItemFwd(para, diRun);
1611  editor->pCursors[0].nOffset = 0;
1612  }
1613  if (para->member.para.nFlags & MEPF_ROWSTART)
1614  {
1615  para = para->member.para.next_para;
1616  editor->pCursors[0].pPara = para;
1617  editor->pCursors[0].pRun = ME_FindItemFwd(para, diRun);
1618  editor->pCursors[0].nOffset = 0;
1619  }
1620  editor->pCursors[1] = editor->pCursors[0];
1621  } else { /* v1.0 - 3.0 */
1622  if (editor->pCursors[0].pRun->member.run.nFlags & MERF_ENDPARA &&
1623  ME_IsInTable(editor->pCursors[0].pRun))
1624  return 0;
1625  }
1626  } else {
1627  style = editor->pBuffer->pDefaultStyle;
1629  set_selection_cursors(editor, 0, 0);
1630  ME_InternalDeleteText(editor, &editor->pCursors[1],
1631  ME_GetTextLength(editor), FALSE);
1632  from = to = 0;
1633  ME_ClearTempStyle(editor);
1634  ME_SetDefaultParaFormat(editor, &editor->pCursors[0].pPara->member.para.fmt);
1635  }
1636 
1637 
1638  /* Back up undo mode to a local variable */
1639  nUndoMode = editor->nUndoMode;
1640 
1641  /* Only create an undo if SFF_SELECTION is set */
1642  if (!(format & SFF_SELECTION))
1643  editor->nUndoMode = umIgnore;
1644 
1645  inStream.editstream = stream;
1646  inStream.editstream->dwError = 0;
1647  inStream.dwSize = 0;
1648  inStream.dwUsed = 0;
1649 
1650  if (format & SF_RTF)
1651  {
1652  /* Check if it's really RTF, and if it is not, use plain text */
1653  ME_StreamInFill(&inStream);
1654  if (!inStream.editstream->dwError)
1655  {
1656  if ((!editor->bEmulateVersion10 && strncmp(inStream.buffer, "{\\rtf", 5) && strncmp(inStream.buffer, "{\\urtf", 6))
1657  || (editor->bEmulateVersion10 && *inStream.buffer != '{'))
1658  {
1659  invalidRTF = TRUE;
1660  inStream.editstream->dwError = -16;
1661  }
1662  }
1663  }
1664 
1665  if (!invalidRTF && !inStream.editstream->dwError)
1666  {
1667  ME_Cursor start;
1668  from = ME_GetCursorOfs(&editor->pCursors[0]);
1669  if (format & SF_RTF) {
1670 
1671  /* setup the RTF parser */
1672  memset(&parser, 0, sizeof parser);
1673  RTFSetEditStream(&parser, &inStream);
1674  parser.rtfFormat = format&(SF_TEXT|SF_RTF);
1675  parser.editor = editor;
1676  parser.style = style;
1677  WriterInit(&parser);
1678  RTFInit(&parser);
1684  if (!parser.editor->bEmulateVersion10) /* v4.1 */
1685  {
1688  }
1689  BeginFile(&parser);
1690 
1691  /* do the parsing */
1692  RTFRead(&parser);
1694  if (!editor->bEmulateVersion10) { /* v4.1 */
1695  if (parser.tableDef && parser.tableDef->tableRowStart &&
1696  (parser.nestingLevel > 0 || parser.canInheritInTbl))
1697  {
1698  /* Delete any incomplete table row at the end of the rich text. */
1699  int nOfs, nChars;
1700  ME_DisplayItem *para;
1701 
1702  parser.rtfMinor = rtfRow;
1703  /* Complete the table row before deleting it.
1704  * By doing it this way we will have the current paragraph format set
1705  * properly to reflect that is not in the complete table, and undo items
1706  * will be added for this change to the current paragraph format. */
1707  if (parser.nestingLevel > 0)
1708  {
1709  while (parser.nestingLevel > 1)
1710  ME_RTFSpecialCharHook(&parser); /* Decrements nestingLevel */
1711  para = parser.tableDef->tableRowStart;
1713  } else {
1714  para = parser.tableDef->tableRowStart;
1716  assert(para->member.para.nFlags & MEPF_ROWEND);
1717  para = para->member.para.next_para;
1718  }
1719 
1720  editor->pCursors[1].pPara = para;
1721  editor->pCursors[1].pRun = ME_FindItemFwd(para, diRun);
1722  editor->pCursors[1].nOffset = 0;
1723  nOfs = ME_GetCursorOfs(&editor->pCursors[1]);
1724  nChars = ME_GetCursorOfs(&editor->pCursors[0]) - nOfs;
1725  ME_InternalDeleteText(editor, &editor->pCursors[1], nChars, TRUE);
1726  if (parser.tableDef)
1727  parser.tableDef->tableRowStart = NULL;
1728  }
1729  }
1731  RTFDestroy(&parser);
1732 
1733  if (parser.stackTop > 0)
1734  {
1735  while (--parser.stackTop >= 0)
1736  {
1737  ME_ReleaseStyle(parser.style);
1738  parser.style = parser.stack[parser.stackTop].style;
1739  }
1740  if (!inStream.editstream->dwError)
1742  }
1743 
1744  /* Remove last line break, as mandated by tests. This is not affected by
1745  CR/LF counters, since RTF streaming presents only \para tokens, which
1746  are converted according to the standard rules: \r for 2.0, \r\n for 1.0
1747  */
1748  if (stripLastCR && !(format & SFF_SELECTION)) {
1749  int newto;
1750  ME_GetSelection(editor, &selStart, &selEnd);
1751  newto = ME_GetCursorOfs(selEnd);
1752  if (newto > to + (editor->bEmulateVersion10 ? 1 : 0)) {
1753  WCHAR lastchar[3] = {'\0', '\0'};
1754  int linebreakSize = editor->bEmulateVersion10 ? 2 : 1;
1755  ME_Cursor linebreakCursor = *selEnd, lastcharCursor = *selEnd;
1756  CHARFORMAT2W cf;
1757 
1758  /* Set the final eop to the char fmt of the last char */
1759  cf.cbSize = sizeof(cf);
1760  cf.dwMask = CFM_ALL2;
1761  ME_MoveCursorChars(editor, &lastcharCursor, -1, FALSE);
1762  ME_GetCharFormat(editor, &lastcharCursor, &linebreakCursor, &cf);
1763  set_selection_cursors(editor, newto, -1);
1764  ME_SetSelectionCharFormat(editor, &cf);
1765  set_selection_cursors(editor, newto, newto);
1766 
1767  ME_MoveCursorChars(editor, &linebreakCursor, -linebreakSize, FALSE);
1768  ME_GetTextW(editor, lastchar, 2, &linebreakCursor, linebreakSize, FALSE, FALSE);
1769  if (lastchar[0] == '\r' && (lastchar[1] == '\n' || lastchar[1] == '\0')) {
1770  ME_InternalDeleteText(editor, &linebreakCursor, linebreakSize, FALSE);
1771  }
1772  }
1773  }
1774  to = ME_GetCursorOfs(&editor->pCursors[0]);
1775  num_read = to - from;
1776 
1777  style = parser.style;
1778  }
1779  else if (format & SF_TEXT)
1780  {
1781  num_read = ME_StreamInText(editor, format, &inStream, style);
1782  to = ME_GetCursorOfs(&editor->pCursors[0]);
1783  }
1784  else
1785  ERR("EM_STREAMIN without SF_TEXT or SF_RTF\n");
1786  /* put the cursor at the top */
1787  if (!(format & SFF_SELECTION))
1788  set_selection_cursors(editor, 0, 0);
1789  ME_CursorFromCharOfs(editor, from, &start);
1790  ME_UpdateLinkAttribute(editor, &start, to - from);
1791  }
1792 
1793  /* Restore saved undo mode */
1794  editor->nUndoMode = nUndoMode;
1795 
1796  /* even if we didn't add an undo, we need to commit anything on the stack */
1797  ME_CommitUndo(editor);
1798 
1799  /* If SFF_SELECTION isn't set, delete any undos from before we started too */
1800  if (!(format & SFF_SELECTION))
1801  ME_EmptyUndoStack(editor);
1802 
1804  editor->nEventMask = nEventMask;
1805  ME_UpdateRepaint(editor, FALSE);
1806  if (!(format & SFF_SELECTION)) {
1807  ME_ClearTempStyle(editor);
1808  }
1809  update_caret(editor);
1810  ME_SendSelChange(editor);
1811  ME_SendRequestResize(editor, FALSE);
1812 
1813  return num_read;
1814 }
1815 
1816 
1818 {
1819  char *string;
1820  int pos;
1821  int length;
1823 
1825 {
1826  ME_RTFStringStreamStruct *pStruct = (ME_RTFStringStreamStruct *)dwCookie;
1827  int count;
1828 
1829  count = min(cb, pStruct->length - pStruct->pos);
1830  memmove(lpBuff, pStruct->string + pStruct->pos, count);
1831  pStruct->pos += count;
1832  *pcb = count;
1833  return 0;
1834 }
1835 
1836 static void
1838 {
1839  EDITSTREAM es;
1841 
1842  data.string = string;
1843  data.length = strlen(string);
1844  data.pos = 0;
1845  es.dwCookie = (DWORD_PTR)&data;
1846  es.pfnCallback = ME_ReadFromRTFString;
1847  ME_StreamIn(editor, SF_RTF | (selection ? SFF_SELECTION : 0), &es, TRUE);
1848 }
1849 
1850 
1851 static int
1852 ME_FindText(ME_TextEditor *editor, DWORD flags, const CHARRANGE *chrg, const WCHAR *text, CHARRANGE *chrgText)
1853 {
1854  const int nLen = lstrlenW(text);
1855  const int nTextLen = ME_GetTextLength(editor);
1856  int nMin, nMax;
1857  ME_Cursor cursor;
1858  WCHAR wLastChar = ' ';
1859 
1860  TRACE("flags==0x%08x, chrg->cpMin==%d, chrg->cpMax==%d text==%s\n",
1861  flags, chrg->cpMin, chrg->cpMax, debugstr_w(text));
1862 
1863  if (flags & ~(FR_DOWN | FR_MATCHCASE | FR_WHOLEWORD))
1864  FIXME("Flags 0x%08x not implemented\n",
1866 
1867  nMin = chrg->cpMin;
1868  if (chrg->cpMax == -1)
1869  nMax = nTextLen;
1870  else
1871  nMax = chrg->cpMax > nTextLen ? nTextLen : chrg->cpMax;
1872 
1873  /* In 1.0 emulation, if cpMax reaches end of text, add the FR_DOWN flag */
1874  if (editor->bEmulateVersion10 && nMax == nTextLen)
1875  {
1876  flags |= FR_DOWN;
1877  }
1878 
1879  /* In 1.0 emulation, cpMin must always be no greater than cpMax */
1880  if (editor->bEmulateVersion10 && nMax < nMin)
1881  {
1882  if (chrgText)
1883  {
1884  chrgText->cpMin = -1;
1885  chrgText->cpMax = -1;
1886  }
1887  return -1;
1888  }
1889 
1890  /* when searching up, if cpMin < cpMax, then instead of searching
1891  * on [cpMin,cpMax], we search on [0,cpMin], otherwise, search on
1892  * [cpMax, cpMin]. The exception is when cpMax is -1, in which
1893  * case, it is always bigger than cpMin.
1894  */
1895  if (!editor->bEmulateVersion10 && !(flags & FR_DOWN))
1896  {
1897  int nSwap = nMax;
1898 
1899  nMax = nMin > nTextLen ? nTextLen : nMin;
1900  if (nMin < nSwap || chrg->cpMax == -1)
1901  nMin = 0;
1902  else
1903  nMin = nSwap;
1904  }
1905 
1906  if (!nLen || nMin < 0 || nMax < 0 || nMax < nMin)
1907  {
1908  if (chrgText)
1909  chrgText->cpMin = chrgText->cpMax = -1;
1910  return -1;
1911  }
1912 
1913  if (flags & FR_DOWN) /* Forward search */
1914  {
1915  /* If possible, find the character before where the search starts */
1916  if ((flags & FR_WHOLEWORD) && nMin)
1917  {
1918  ME_CursorFromCharOfs(editor, nMin - 1, &cursor);
1919  wLastChar = *get_text( &cursor.pRun->member.run, cursor.nOffset );
1920  ME_MoveCursorChars(editor, &cursor, 1, FALSE);
1921  } else {
1922  ME_CursorFromCharOfs(editor, nMin, &cursor);
1923  }
1924 
1925  while (cursor.pRun && ME_GetCursorOfs(&cursor) + nLen <= nMax)
1926  {
1927  ME_DisplayItem *pCurItem = cursor.pRun;
1928  int nCurStart = cursor.nOffset;
1929  int nMatched = 0;
1930 
1931  while (pCurItem && ME_CharCompare( *get_text( &pCurItem->member.run, nCurStart + nMatched ), text[nMatched], (flags & FR_MATCHCASE)))
1932  {
1933  if ((flags & FR_WHOLEWORD) && iswalnum(wLastChar))
1934  break;
1935 
1936  nMatched++;
1937  if (nMatched == nLen)
1938  {
1939  ME_DisplayItem *pNextItem = pCurItem;
1940  int nNextStart = nCurStart;
1941  WCHAR wNextChar;
1942 
1943  /* Check to see if next character is a whitespace */
1944  if (flags & FR_WHOLEWORD)
1945  {
1946  if (nCurStart + nMatched == pCurItem->member.run.len)
1947  {
1948  pNextItem = ME_FindItemFwd(pCurItem, diRun);
1949  nNextStart = -nMatched;
1950  }
1951 
1952  if (pNextItem)
1953  wNextChar = *get_text( &pNextItem->member.run, nNextStart + nMatched );
1954  else
1955  wNextChar = ' ';
1956 
1957  if (iswalnum(wNextChar))
1958  break;
1959  }
1960 
1961  cursor.nOffset += cursor.pPara->member.para.nCharOfs + cursor.pRun->member.run.nCharOfs;
1962  if (chrgText)
1963  {
1964  chrgText->cpMin = cursor.nOffset;
1965  chrgText->cpMax = cursor.nOffset + nLen;
1966  }
1967  TRACE("found at %d-%d\n", cursor.nOffset, cursor.nOffset + nLen);
1968  return cursor.nOffset;
1969  }
1970  if (nCurStart + nMatched == pCurItem->member.run.len)
1971  {
1972  pCurItem = ME_FindItemFwd(pCurItem, diRun);
1973  nCurStart = -nMatched;
1974  }
1975  }
1976  if (pCurItem)
1977  wLastChar = *get_text( &pCurItem->member.run, nCurStart + nMatched );
1978  else
1979  wLastChar = ' ';
1980 
1981  cursor.nOffset++;
1982  if (cursor.nOffset == cursor.pRun->member.run.len)
1983  {
1984  ME_NextRun(&cursor.pPara, &cursor.pRun, TRUE);
1985  cursor.nOffset = 0;
1986  }
1987  }
1988  }
1989  else /* Backward search */
1990  {
1991  /* If possible, find the character after where the search ends */
1992  if ((flags & FR_WHOLEWORD) && nMax < nTextLen - 1)
1993  {
1994  ME_CursorFromCharOfs(editor, nMax + 1, &cursor);
1995  wLastChar = *get_text( &cursor.pRun->member.run, cursor.nOffset );
1996  ME_MoveCursorChars(editor, &cursor, -1, FALSE);
1997  } else {
1998  ME_CursorFromCharOfs(editor, nMax, &cursor);
1999  }
2000 
2001  while (cursor.pRun && ME_GetCursorOfs(&cursor) - nLen >= nMin)
2002  {
2003  ME_DisplayItem *pCurItem = cursor.pRun;
2004  ME_DisplayItem *pCurPara = cursor.pPara;
2005  int nCurEnd = cursor.nOffset;
2006  int nMatched = 0;
2007 
2008  if (nCurEnd == 0)
2009  {
2010  ME_PrevRun(&pCurPara, &pCurItem, TRUE);
2011  nCurEnd = pCurItem->member.run.len;
2012  }
2013 
2014  while (pCurItem && ME_CharCompare( *get_text( &pCurItem->member.run, nCurEnd - nMatched - 1 ),
2015  text[nLen - nMatched - 1], (flags & FR_MATCHCASE) ))
2016  {
2017  if ((flags & FR_WHOLEWORD) && iswalnum(wLastChar))
2018  break;
2019 
2020  nMatched++;
2021  if (nMatched == nLen)
2022  {
2023  ME_DisplayItem *pPrevItem = pCurItem;
2024  int nPrevEnd = nCurEnd;
2025  WCHAR wPrevChar;
2026  int nStart;
2027 
2028  /* Check to see if previous character is a whitespace */
2029  if (flags & FR_WHOLEWORD)
2030  {
2031  if (nPrevEnd - nMatched == 0)
2032  {
2033  pPrevItem = ME_FindItemBack(pCurItem, diRun);
2034  if (pPrevItem)
2035  nPrevEnd = pPrevItem->member.run.len + nMatched;
2036  }
2037 
2038  if (pPrevItem)
2039  wPrevChar = *get_text( &pPrevItem->member.run, nPrevEnd - nMatched - 1 );
2040  else
2041  wPrevChar = ' ';
2042 
2043  if (iswalnum(wPrevChar))
2044  break;
2045  }
2046 
2047  nStart = pCurPara->member.para.nCharOfs
2048  + pCurItem->member.run.nCharOfs + nCurEnd - nMatched;
2049  if (chrgText)
2050  {
2051  chrgText->cpMin = nStart;
2052  chrgText->cpMax = nStart + nLen;
2053  }
2054  TRACE("found at %d-%d\n", nStart, nStart + nLen);
2055  return nStart;
2056  }
2057  if (nCurEnd - nMatched == 0)
2058  {
2059  ME_PrevRun(&pCurPara, &pCurItem, TRUE);
2060  /* Don't care about pCurItem becoming NULL here; it's already taken
2061  * care of in the exterior loop condition */
2062  nCurEnd = pCurItem->member.run.len + nMatched;
2063  }
2064  }
2065  if (pCurItem)
2066  wLastChar = *get_text( &pCurItem->member.run, nCurEnd - nMatched - 1 );
2067  else
2068  wLastChar = ' ';
2069 
2070  cursor.nOffset--;
2071  if (cursor.nOffset < 0)
2072  {
2073  ME_PrevRun(&cursor.pPara, &cursor.pRun, TRUE);
2074  cursor.nOffset = cursor.pRun->member.run.len;
2075  }
2076  }
2077  }
2078  TRACE("not found\n");
2079  if (chrgText)
2080  chrgText->cpMin = chrgText->cpMax = -1;
2081  return -1;
2082 }
2083 
2085 {
2086  int nChars;
2087  ME_Cursor start;
2088 
2089  if (!ex->cb || !pText) return 0;
2090 
2091  if (ex->flags & ~(GT_SELECTION | GT_USECRLF))
2092  FIXME("GETTEXTEX flags 0x%08x not supported\n", ex->flags & ~(GT_SELECTION | GT_USECRLF));
2093 
2094  if (ex->flags & GT_SELECTION)
2095  {
2096  int from, to;
2097  int nStartCur = ME_GetSelectionOfs(editor, &from, &to);
2098  start = editor->pCursors[nStartCur];
2099  nChars = to - from;
2100  }
2101  else
2102  {
2103  ME_SetCursorToStart(editor, &start);
2104  nChars = INT_MAX;
2105  }
2106  if (ex->codepage == CP_UNICODE)
2107  {
2108  return ME_GetTextW(editor, (LPWSTR)pText, ex->cb / sizeof(WCHAR) - 1,
2109  &start, nChars, ex->flags & GT_USECRLF, FALSE);
2110  }
2111  else
2112  {
2113  /* potentially each char may be a CR, why calculate the exact value with O(N) when
2114  we can just take a bigger buffer? :)
2115  The above assumption still holds with CR/LF counters, since CR->CRLF expansion
2116  occurs only in richedit 2.0 mode, in which line breaks have only one CR
2117  */
2118  int crlfmul = (ex->flags & GT_USECRLF) ? 2 : 1;
2119  DWORD buflen;
2120  LPWSTR buffer;
2121  LRESULT rc;
2122 
2123  buflen = min(crlfmul * nChars, ex->cb - 1);
2124  buffer = heap_alloc((buflen + 1) * sizeof(WCHAR));
2125 
2126  nChars = ME_GetTextW(editor, buffer, buflen, &start, nChars, ex->flags & GT_USECRLF, FALSE);
2127  rc = WideCharToMultiByte(ex->codepage, 0, buffer, nChars + 1,
2128  (LPSTR)pText, ex->cb, ex->lpDefaultChar, ex->lpUsedDefChar);
2129  if (rc) rc--; /* do not count 0 terminator */
2130 
2131  heap_free(buffer);
2132  return rc;
2133  }
2134 }
2135 
2136 static int ME_GetTextRange(ME_TextEditor *editor, WCHAR *strText,
2137  const ME_Cursor *start, int nLen, BOOL unicode)
2138 {
2139  if (!strText) return 0;
2140  if (unicode) {
2141  return ME_GetTextW(editor, strText, INT_MAX, start, nLen, FALSE, FALSE);
2142  } else {
2143  int nChars;
2144  WCHAR *p = heap_alloc((nLen+1) * sizeof(*p));
2145  if (!p) return 0;
2146  nChars = ME_GetTextW(editor, p, nLen, start, nLen, FALSE, FALSE);
2147  WideCharToMultiByte(CP_ACP, 0, p, nChars+1, (char *)strText,
2148  nLen+1, NULL, NULL);
2149  heap_free(p);
2150  return nChars;
2151  }
2152 }
2153 
2154 int set_selection( ME_TextEditor *editor, int to, int from )
2155 {
2156  int end;
2157 
2158  TRACE("%d - %d\n", to, from );
2159 
2160  if (!editor->bHideSelection) ME_InvalidateSelection( editor );
2161  end = set_selection_cursors( editor, to, from );
2162  if (!editor->bHideSelection) ME_InvalidateSelection( editor );
2163  update_caret( 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  set_selection( 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  ed->caret_hidden = FALSE;
3123  ed->caret_height = 0;
3124  for (i=0; i<HFONT_CACHE_SIZE; i++)
3125  {
3126  ed->pFontCache[i].nRefs = 0;
3127  ed->pFontCache[i].nAge = 0;
3128  ed->pFontCache[i].hFont = NULL;
3129  }
3130 
3132  SetRectEmpty(&ed->rcFormat);
3133  ed->bDefaultFormatRect = TRUE;
3134  ITextHost_TxGetSelectionBarWidth(ed->texthost, &selbarwidth);
3135  if (selbarwidth) {
3136  /* FIXME: Convert selbarwidth from HIMETRIC to pixels */
3137  ed->selofs = SELECTIONBAR_WIDTH;
3138  ed->styleFlags |= ES_SELECTIONBAR;
3139  } else {
3140  ed->selofs = 0;
3141  }
3142  ed->nSelectionType = stPosition;
3143 
3144  ed->cPasswordMask = 0;
3145  if (props & TXTBIT_USEPASSWORD)
3146  ITextHost_TxGetPasswordChar(texthost, &ed->cPasswordMask);
3147 
3148  if (props & TXTBIT_AUTOWORDSEL)
3149  ed->styleFlags |= ECO_AUTOWORDSELECTION;
3150  if (props & TXTBIT_MULTILINE) {
3151  ed->styleFlags |= ES_MULTILINE;
3152  ed->bWordWrap = (props & TXTBIT_WORDWRAP) != 0;
3153  } else {
3154  ed->bWordWrap = FALSE;
3155  }
3156  if (props & TXTBIT_READONLY)
3157  ed->styleFlags |= ES_READONLY;
3158  if (!(props & TXTBIT_HIDESELECTION))
3159  ed->styleFlags |= ES_NOHIDESEL;
3161  ed->styleFlags |= ES_SAVESEL;
3162  if (props & TXTBIT_VERTICAL)
3163  ed->styleFlags |= ES_VERTICAL;
3164  if (props & TXTBIT_DISABLEDRAG)
3165  ed->styleFlags |= ES_NOOLEDRAGDROP;
3166 
3167  ed->notified_cr.cpMin = ed->notified_cr.cpMax = 0;
3168 
3169  /* Default scrollbar information */
3170  ed->vert_si.cbSize = sizeof(SCROLLINFO);
3171  ed->vert_si.nMin = 0;
3172  ed->vert_si.nMax = 0;
3173  ed->vert_si.nPage = 0;
3174  ed->vert_si.nPos = 0;
3175 
3176  ed->horz_si.cbSize = sizeof(SCROLLINFO);
3177  ed->horz_si.nMin = 0;
3178  ed->horz_si.nMax = 0;
3179  ed->horz_si.nPage = 0;
3180  ed->horz_si.nPos = 0;
3181 
3182  ed->wheel_remain = 0;
3183 
3184  list_init( &ed->reobj_list );
3186 
3187  return ed;
3188 }
3189 
3191 {
3192  ME_DisplayItem *p = editor->pBuffer->pFirst, *pNext = NULL;
3193  ME_Style *s, *cursor2;
3194  int i;
3195 
3196  ME_ClearTempStyle(editor);
3197  ME_EmptyUndoStack(editor);
3198  editor->pBuffer->pFirst = NULL;
3199  while(p) {
3200  pNext = p->next;
3201  if (p->type == diParagraph)
3202  destroy_para(editor, p);
3203  else
3205  p = pNext;
3206  }
3207 
3208  LIST_FOR_EACH_ENTRY_SAFE( s, cursor2, &editor->style_list, ME_Style, entry )
3209  ME_DestroyStyle( s );
3210 
3212  for (i=0; i<HFONT_CACHE_SIZE; i++)
3213  {
3214  if (editor->pFontCache[i].hFont)
3215  DeleteObject(editor->pFontCache[i].hFont);
3216  }
3217  if (editor->rgbBackColor != -1)
3218  DeleteObject(editor->hbrBackground);
3219  if(editor->lpOleCallback)
3220  IRichEditOleCallback_Release(editor->lpOleCallback);
3221  ITextHost_Release(editor->texthost);
3222  if (editor->reOle)
3223  {
3224  IUnknown_Release(editor->reOle);
3225  editor->reOle = NULL;
3226  }
3227  OleUninitialize();
3228 
3229  heap_free(editor->pBuffer);
3230  heap_free(editor->pCursors);
3231  heap_free(editor);
3232 }
3233 
3235 {
3236  TRACE("\n");
3237  switch (fdwReason)
3238  {
3239  case DLL_PROCESS_ATTACH:
3240  DisableThreadLibraryCalls(hinstDLL);
3241  me_heap = HeapCreate (0, 0x10000, 0);
3242  if (!ME_RegisterEditorClass(hinstDLL)) return FALSE;
3244  LookupInit();
3245  break;
3246 
3247  case DLL_PROCESS_DETACH:
3248  if (lpvReserved) break;
3252  UnregisterClassA("RichEdit50A", 0);
3257  LookupCleanup();
3258  HeapDestroy (me_heap);
3259  release_typelib();
3260  break;
3261  }
3262  return TRUE;
3263 }
3264 
3265 static inline int get_default_line_height( ME_TextEditor *editor )
3266 {
3267  int height = 0;
3268 
3269  if (editor->pBuffer && editor->pBuffer->pDefaultStyle)
3270  height = editor->pBuffer->pDefaultStyle->tm.tmHeight;
3271  if (height <= 0) height = 24;
3272 
3273  return height;
3274 }
3275 
3276 static inline int calc_wheel_change( int *remain, int amount_per_click )
3277 {
3278  int change = amount_per_click * (float)*remain / WHEEL_DELTA;
3279  *remain -= WHEEL_DELTA * change / amount_per_click;
3280  return change;
3281 }
3282 
3283 static const char * const edit_messages[] = {
3284  "EM_GETSEL",
3285  "EM_SETSEL",
3286  "EM_GETRECT",
3287  "EM_SETRECT",
3288  "EM_SETRECTNP",
3289  "EM_SCROLL",
3290  "EM_LINESCROLL",
3291  "EM_SCROLLCARET",
3292  "EM_GETMODIFY",
3293  "EM_SETMODIFY",
3294  "EM_GETLINECOUNT",
3295  "EM_LINEINDEX",
3296  "EM_SETHANDLE",
3297  "EM_GETHANDLE",
3298  "EM_GETTHUMB",
3299  "EM_UNKNOWN_BF",
3300  "EM_UNKNOWN_C0",
3301  "EM_LINELENGTH",
3302  "EM_REPLACESEL",
3303  "EM_UNKNOWN_C3",
3304  "EM_GETLINE",
3305  "EM_LIMITTEXT",
3306  "EM_CANUNDO",
3307  "EM_UNDO",
3308  "EM_FMTLINES",
3309  "EM_LINEFROMCHAR",
3310  "EM_UNKNOWN_CA",
3311  "EM_SETTABSTOPS",
3312  "EM_SETPASSWORDCHAR",
3313  "EM_EMPTYUNDOBUFFER",
3314  "EM_GETFIRSTVISIBLELINE",
3315  "EM_SETREADONLY",
3316  "EM_SETWORDBREAKPROC",
3317  "EM_GETWORDBREAKPROC",
3318  "EM_GETPASSWORDCHAR",
3319  "EM_SETMARGINS",
3320  "EM_GETMARGINS",
3321  "EM_GETLIMITTEXT",
3322  "EM_POSFROMCHAR",
3323  "EM_CHARFROMPOS",
3324  "EM_SETIMESTATUS",
3325  "EM_GETIMESTATUS"
3326 };
3327 
3328 static const char * const richedit_messages[] = {
3329  "EM_CANPASTE",
3330  "EM_DISPLAYBAND",
3331  "EM_EXGETSEL",
3332  "EM_EXLIMITTEXT",
3333  "EM_EXLINEFROMCHAR",
3334  "EM_EXSETSEL",
3335  "EM_FINDTEXT",
3336  "EM_FORMATRANGE",
3337  "EM_GETCHARFORMAT",
3338  "EM_GETEVENTMASK",
3339  "EM_GETOLEINTERFACE",
3340  "EM_GETPARAFORMAT",
3341  "EM_GETSELTEXT",
3342  "EM_HIDESELECTION",
3343  "EM_PASTESPECIAL",
3344  "EM_REQUESTRESIZE",
3345  "EM_SELECTIONTYPE",
3346  "EM_SETBKGNDCOLOR",
3347  "EM_SETCHARFORMAT",
3348  "EM_SETEVENTMASK",
3349  "EM_SETOLECALLBACK",
3350  "EM_SETPARAFORMAT",
3351  "EM_SETTARGETDEVICE",
3352  "EM_STREAMIN",
3353  "EM_STREAMOUT",
3354  "EM_GETTEXTRANGE",
3355  "EM_FINDWORDBREAK",
3356  "EM_SETOPTIONS",
3357  "EM_GETOPTIONS",
3358  "EM_FINDTEXTEX",
3359  "EM_GETWORDBREAKPROCEX",
3360  "EM_SETWORDBREAKPROCEX",
3361  "EM_SETUNDOLIMIT",
3362  "EM_UNKNOWN_USER_83",
3363  "EM_REDO",
3364  "EM_CANREDO",
3365  "EM_GETUNDONAME",
3366  "EM_GETREDONAME",
3367  "EM_STOPGROUPTYPING",
3368  "EM_SETTEXTMODE",
3369  "EM_GETTEXTMODE",
3370  "EM_AUTOURLDETECT",
3371  "EM_GETAUTOURLDETECT",
3372  "EM_SETPALETTE",
3373  "EM_GETTEXTEX",
3374  "EM_GETTEXTLENGTHEX",
3375  "EM_SHOWSCROLLBAR",
3376  "EM_SETTEXTEX",
3377  "EM_UNKNOWN_USER_98",
3378  "EM_UNKNOWN_USER_99",
3379  "EM_SETPUNCTUATION",
3380  "EM_GETPUNCTUATION",
3381  "EM_SETWORDWRAPMODE",
3382  "EM_GETWORDWRAPMODE",
3383  "EM_SETIMECOLOR",
3384  "EM_GETIMECOLOR",
3385  "EM_SETIMEOPTIONS",
3386  "EM_GETIMEOPTIONS",
3387  "EM_CONVPOSITION",
3388  "EM_UNKNOWN_USER_109",
3389  "EM_UNKNOWN_USER_110",
3390  "EM_UNKNOWN_USER_111",
3391  "EM_UNKNOWN_USER_112",
3392  "EM_UNKNOWN_USER_113",
3393  "EM_UNKNOWN_USER_114",
3394  "EM_UNKNOWN_USER_115",
3395  "EM_UNKNOWN_USER_116",
3396  "EM_UNKNOWN_USER_117",
3397  "EM_UNKNOWN_USER_118",
3398  "EM_UNKNOWN_USER_119",
3399  "EM_SETLANGOPTIONS",
3400  "EM_GETLANGOPTIONS",
3401  "EM_GETIMECOMPMODE",
3402  "EM_FINDTEXTW",
3403  "EM_FINDTEXTEXW",
3404  "EM_RECONVERSION",
3405  "EM_SETIMEMODEBIAS",
3406  "EM_GETIMEMODEBIAS"
3407 };
3408 
3409 static const char *
3411 {
3412  if (msg >= EM_GETSEL && msg <= EM_CHARFROMPOS)
3413  return edit_messages[msg - EM_GETSEL];
3414  if (msg >= EM_CANPASTE && msg <= EM_GETIMEMODEBIAS)
3415  return richedit_messages[msg - EM_CANPASTE];
3416  return "";
3417 }
3418 
3420 {
3421  int x,y;
3422  BOOL isExact;
3423  ME_Cursor cursor; /* The start of the clicked text. */
3424 
3425  ENLINK info;
3426  x = (short)LOWORD(lParam);
3427  y = (short)HIWORD(lParam);
3428  ME_CharFromPos(editor, x, y, &cursor, &isExact);
3429  if (!isExact) return;
3430 
3431  if (is_link( &cursor.pRun->member.run ))
3432  { /* The clicked run has CFE_LINK set */
3433  ME_DisplayItem *di;
3434 
3435  info.nmhdr.hwndFrom = NULL;
3436  info.nmhdr.idFrom = 0;
3437  info.nmhdr.code = EN_LINK;
3438  info.msg = msg;
3439  info.wParam = wParam;
3440  info.lParam = lParam;
3441  cursor.nOffset = 0;
3442 
3443  /* find the first contiguous run with CFE_LINK set */
3444  info.chrg.cpMin = ME_GetCursorOfs(&cursor);
3445  di = cursor.pRun;
3446  while (ME_PrevRun( NULL, &di, FALSE ) && is_link( &di->member.run ))
3447  info.chrg.cpMin -= di->member.run.len;
3448 
3449  /* find the last contiguous run with CFE_LINK set */
3450  info.chrg.cpMax = ME_GetCursorOfs(&cursor) + cursor.pRun->member.run.len;
3451  di = cursor.pRun;
3452  while (ME_NextRun( NULL, &di, FALSE ) && is_link( &di->member.run ))
3453  info.chrg.cpMax += di->member.run.len;
3454 
3455  ITextHost_TxNotify(editor->texthost, info.nmhdr.code, &info);
3456  }
3457 }
3458 
3459 void ME_ReplaceSel(ME_TextEditor *editor, BOOL can_undo, const WCHAR *str, int len)
3460 {
3461  int from, to, nStartCursor;
3462  ME_Style *style;
3463 
3464  nStartCursor = ME_GetSelectionOfs(editor, &from, &to);
3466  ME_InternalDeleteText(editor, &editor->pCursors[nStartCursor], to-from, FALSE);
3467  ME_InsertTextFromCursor(editor, 0, str, len, style);
3469  /* drop temporary style if line end */
3470  /*
3471  * FIXME question: does abc\n mean: put abc,
3472  * clear temp style, put \n? (would require a change)
3473  */
3474  if (len>0 && str[len-1] == '\n')
3475  ME_ClearTempStyle(editor);
3476  ME_CommitUndo(editor);
3478  if (!can_undo)
3479  ME_EmptyUndoStack(editor);
3480  ME_UpdateRepaint(editor, FALSE);
3481 }
3482 
3483 static void ME_SetText(ME_TextEditor *editor, void *text, BOOL unicode)
3484 {
3485  LONG codepage = unicode ? CP_UNICODE : CP_ACP;
3486  int textLen;
3487 
3488  LPWSTR wszText = ME_ToUnicode(codepage, text, &textLen);
3489  ME_InsertTextFromCursor(editor, 0, wszText, textLen, editor->pBuffer->pDefaultStyle);
3490  ME_EndToUnicode(codepage, wszText);
3491 }
3492 
3494 {
3496  CREATESTRUCTA *createA = (CREATESTRUCTA*)lParam;
3497  void *text = NULL;
3498  INT max;
3499 
3500  if (lParam)
3501  text = unicode ? (void*)createW->lpszName : (void*)createA->lpszName;
3502 
3503  ME_SetDefaultFormatRect(editor);
3504 
3505  max = (editor->styleFlags & ES_DISABLENOSCROLL) ? 1 : 0;
3506  if (~editor->styleFlags & ES_DISABLENOSCROLL || editor->styleFlags & WS_VSCROLL)
3508 
3509  if (~editor->styleFlags & ES_DISABLENOSCROLL || editor->styleFlags & WS_HSCROLL)
3511 
3512  if (editor->styleFlags & ES_DISABLENOSCROLL)
3513  {
3514  if (editor->styleFlags & WS_VSCROLL)
3515  {
3518  }
3519  if (editor->styleFlags & WS_HSCROLL)
3520  {
3523  }
3524  }
3525 
3526  if (text)
3527  {
3528  ME_SetText(editor, text, unicode);
3529  ME_SetCursorToStart(editor, &editor->pCursors[0]);
3530  ME_SetCursorToStart(editor, &editor->pCursors[1]);
3531  }
3532 
3533  ME_CommitUndo(editor);
3534  ME_WrapMarkedParagraphs(editor);
3535  update_caret(editor);
3536  return 0;
3537 }
3538 
3540 {
3541  CHARFORMAT2W fmt;
3542  BOOL changed = TRUE;
3543  ME_Cursor start, end;
3544 
3545  if (!cfany_to_cf2w( &fmt, fmt_in )) return 0;
3546 
3547  if (flags & SCF_ALL)
3548  {
3549  if (editor->mode & TM_PLAINTEXT)
3550  {
3551  ME_SetDefaultCharFormat( editor, &fmt );
3552  }
3553  else
3554  {
3555  ME_SetCursorToStart( editor, &start );
3556  ME_SetCharFormat( editor, &start, NULL, &fmt );
3557  editor->nModifyStep = 1;
3558  }
3559  }
3560  else if (flags & SCF_SELECTION)
3561  {
3562  if (editor->mode & TM_PLAINTEXT) return 0;
3563  if (flags & SCF_WORD)
3564  {
3565  end = editor->pCursors[0];
3566  ME_MoveCursorWords( editor, &end, +1 );
3567  start = end;
3568  ME_MoveCursorWords( editor, &start, -1 );
3569  ME_SetCharFormat( editor, &start, &end, &fmt );
3570  }
3571  changed = ME_IsSelection( editor );
3572  ME_SetSelectionCharFormat( editor, &fmt );
3573  if (changed) editor->nModifyStep = 1;
3574  }
3575  else /* SCF_DEFAULT */
3576  {
3577  ME_SetDefaultCharFormat( editor, &fmt );
3578  }
3579 
3580  ME_CommitUndo( editor );
3581  if (changed)
3582  {
3583  ME_WrapMarkedParagraphs( editor );
3584  ME_UpdateScrollBar( editor );
3585  }
3586  return 1;
3587 }
3588 
3589 #define UNSUPPORTED_MSG(e) \
3590  case e: \
3591  FIXME(#e ": stub\n"); \
3592  *phresult = S_FALSE; \
3593  return 0;
3594 
3595 /* Handle messages for windowless and windowed richedit controls.
3596  *
3597  * The LRESULT that is returned is a return value for window procs,
3598  * and the phresult parameter is the COM return code needed by the
3599  * text services interface. */
3601  LPARAM lParam, BOOL unicode, HRESULT* phresult)
3602 {
3603  *phresult = S_OK;
3604 
3605  switch(msg) {
3606 
3629 
3630 /* Messages specific to Richedit controls */
3631 
3632  case EM_STREAMIN:
3633  return ME_StreamIn(editor, wParam, (EDITSTREAM*)lParam, TRUE);
3634  case EM_STREAMOUT:
3635  return ME_StreamOut(editor, wParam, (EDITSTREAM *)lParam);
3636  case WM_GETDLGCODE:
3637  {
3639 
3640  if (lParam)
3641  editor->bDialogMode = TRUE;
3642  if (editor->styleFlags & ES_MULTILINE)
3644  if (!(editor->styleFlags & ES_SAVESEL))
3645  code |= DLGC_HASSETSEL;
3646  return code;
3647  }
3648  case EM_EMPTYUNDOBUFFER:
3649  ME_EmptyUndoStack(editor);
3650  return 0;
3651  case EM_GETSEL:
3652  {
3653  /* Note: wParam/lParam can be NULL */
3654  UINT from, to;
3655  PUINT pfrom = wParam ? (PUINT)wParam : &from;
3656  PUINT pto = lParam ? (PUINT)lParam : &to;
3657  ME_GetSelectionOfs(editor, (int *)pfrom, (int *)pto);
3658  if ((*pfrom|*pto) & 0xFFFF0000)
3659  return -1;
3660  return MAKELONG(*pfrom,*pto);
3661  }
3662  case EM_EXGETSEL:
3663  {
3664  CHARRANGE *pRange = (CHARRANGE *)lParam;
3665  ME_GetSelectionOfs(editor, &pRange->cpMin, &pRange->cpMax);
3666  TRACE("EM_EXGETSEL = (%d,%d)\n", pRange->cpMin, pRange->cpMax);
3667  return 0;
3668  }
3669  case EM_SETUNDOLIMIT:
3670  {
3671  if ((int)wParam < 0)
3672  editor->nUndoLimit = STACK_SIZE_DEFAULT;
3673  else
3674  editor->nUndoLimit = min(wParam, STACK_SIZE_MAX);
3675  /* Setting a max stack size keeps wine from getting killed
3676  for hogging memory. Windows allocates all this memory at once, so
3677  no program would realistically set a value above our maximum. */
3678  return editor->nUndoLimit;
3679  }
3680  case EM_CANUNDO:
3681  return !list_empty( &editor->undo_stack );
3682  case EM_CANREDO:
3683  return !list_empty( &editor->redo_stack );
3684  case WM_UNDO: /* FIXME: actually not the same */
3685  case EM_UNDO:
3686  return ME_Undo(editor);
3687  case EM_REDO:
3688  return ME_Redo(editor);
3689  case EM_GETOPTIONS:
3690  {
3691  /* these flags are equivalent to the ES_* counterparts */
3694  DWORD settings = editor->styleFlags & mask;
3695 
3696  return settings;
3697  }
3698  case EM_SETFONTSIZE:
3699  {
3700  CHARFORMAT2W cf;
3701  LONG tmp_size, size;
3702  BOOL is_increase = ((LONG)wParam > 0);
3703 
3704  if (editor->mode & TM_PLAINTEXT)
3705  return FALSE;
3706 
3707  cf.cbSize = sizeof(cf);
3708  cf.dwMask = CFM_SIZE;
3709  ME_GetSelectionCharFormat(editor, &cf);
3710  tmp_size = (cf.yHeight / 20) + wParam;
3711 
3712  if (tmp_size <= 1)
3713  size = 1;
3714  else if (tmp_size > 12 && tmp_size < 28 && tmp_size % 2)
3715  size = tmp_size + (is_increase ? 1 : -1);
3716  else if (tmp_size > 28 && tmp_size < 36)
3717  size = is_increase ? 36 : 28;
3718  else if (tmp_size > 36 && tmp_size < 48)
3719  size = is_increase ? 48 : 36;
3720  else if (tmp_size > 48 && tmp_size < 72)
3721  size = is_increase ? 72 : 48;
3722  else if (tmp_size > 72 && tmp_size < 80)
3723  size = is_increase ? 80 : 72;
3724  else if (tmp_size > 80 && tmp_size < 1638)
3725  size = 10 * (is_increase ? (tmp_size / 10 + 1) : (tmp_size / 10));
3726  else if (tmp_size >= 1638)
3727  size = 1638;
3728  else
3729  size = tmp_size;
3730 
3731  cf.yHeight = size * 20; /* convert twips to points */
3732  ME_SetSelectionCharFormat(editor, &cf);
3733  ME_CommitUndo(editor);
3734  ME_WrapMarkedParagraphs(editor);
3735  ME_UpdateScrollBar(editor);
3736 
3737  return TRUE;
3738  }
3739  case EM_SETOPTIONS:
3740  {
3741  /* these flags are equivalent to ES_* counterparts, except for
3742  * ECO_AUTOWORDSELECTION that doesn't have an ES_* counterpart,
3743  * but is still stored in editor->styleFlags. */
3747  DWORD settings = mask & editor->styleFlags;
3748  DWORD oldSettings = settings;
3749  DWORD changedSettings;
3750 
3751  switch(wParam)
3752  {
3753  case ECOOP_SET:
3754  settings = lParam;
3755  break;
3756  case ECOOP_OR:
3757  settings |= lParam;
3758  break;
3759  case ECOOP_AND:
3760  settings &= lParam;
3761  break;
3762  case ECOOP_XOR:
3763  settings ^= lParam;
3764  }
3765  changedSettings = oldSettings ^ settings;
3766 
3767  if (changedSettings) {
3768  editor->styleFlags = (editor->styleFlags & ~mask) | (settings & mask);
3769 
3770  if (changedSettings & ECO_SELECTIONBAR)
3771  {
3772  ITextHost_TxInvalidateRect(editor->texthost, &editor->rcFormat, TRUE);
3773  if (settings & ECO_SELECTIONBAR) {
3774  assert(!editor->selofs);
3775  editor->selofs = SELECTIONBAR_WIDTH;
3776  editor->rcFormat.left += editor->selofs;
3777  } else {
3778  editor->rcFormat.left -= editor->selofs;
3779  editor->selofs = 0;
3780  }
3781  ME_RewrapRepaint(editor);
3782  }
3783 
3784  if ((changedSettings & settings & ES_NOHIDESEL) && !editor->bHaveFocus)
3785  ME_InvalidateSelection( editor );
3786 
3787  if (changedSettings & settings & ECO_VERTICAL)
3788  FIXME("ECO_VERTICAL not implemented yet!\n");
3789  if (changedSettings & settings & ECO_AUTOHSCROLL)
3790  FIXME("ECO_AUTOHSCROLL not implemented yet!\n");
3791  if (changedSettings & settings & ECO_AUTOVSCROLL)
3792  FIXME("ECO_AUTOVSCROLL not implemented yet!\n");
3793  if (changedSettings & settings & ECO_WANTRETURN)
3794  FIXME("ECO_WANTRETURN not implemented yet!\n");
3795  if (changedSettings & settings & ECO_AUTOWORDSELECTION)
3796  FIXME("ECO_AUTOWORDSELECTION not implemented yet!\n");
3797  }
3798 
3799  return settings;
3800  }
3801  case EM_SETSEL:
3802  {
3803  return set_selection( editor, wParam, lParam );
3804  }
3805  case EM_SETSCROLLPOS:
3806  {
3807  POINT *point = (POINT *)lParam;
3808  ME_ScrollAbs(editor, point->x, point->y);
3809  return 0;
3810  }
3811  case EM_AUTOURLDETECT:
3812  {
3813  if (wParam==1 || wParam ==0)
3814  {
3815  editor->AutoURLDetect_bEnable = (BOOL)wParam;
3816  return 0;
3817  }
3818  return E_INVALIDARG;
3819  }
3820  case EM_GETAUTOURLDETECT:
3821  {
3822  return editor->AutoURLDetect_bEnable;
3823  }
3824  case EM_EXSETSEL:
3825  {
3827 
3828  return set_selection( editor, range.cpMin, range.cpMax );
3829  }
3830  case EM_SHOWSCROLLBAR:
3831  {
3832  DWORD flags;
3833 
3834  switch (wParam)
3835  {
3836  case SB_HORZ:
3837  flags = WS_HSCROLL;
3838  break;
3839  case SB_VERT:
3840  flags = WS_VSCROLL;
3841  break;
3842  case SB_BOTH:
3844  break;
3845  default:
3846  return 0;
3847  }
3848 
3849  if (lParam) {
3850  editor->styleFlags |= flags;
3851  if (flags & WS_HSCROLL)
3853  editor->nTotalWidth > editor->sizeWindow.cx);
3854  if (flags & WS_VSCROLL)
3856  editor->nTotalLength > editor->sizeWindow.cy);
3857  } else {
3858  editor->styleFlags &= ~flags;
3860  }
3861  return 0;
3862  }
3863  case EM_SETTEXTEX:
3864  {
3865  LPWSTR wszText;
3866  SETTEXTEX *pStruct = (SETTEXTEX *)wParam;
3867  int from, to, len;
3868  ME_Style *style;
3869  BOOL bRtf, bUnicode, bSelection, bUTF8;
3870  int oldModify = editor->nModifyStep;
3871  static const char utf8_bom[] = {0xef, 0xbb, 0xbf};
3872 
3873  if (!pStruct) return 0;
3874 
3875  /* If we detect ascii rtf at the start of the string,
3876  * we know it isn't unicode. */
3877  bRtf = (lParam && (!strncmp((char *)lParam, "{\\rtf", 5) ||
3878  !strncmp((char *)lParam, "{\\urtf", 6)));
3879  bUnicode = !bRtf && pStruct->codepage == CP_UNICODE;
3880  bUTF8 = (lParam && (!strncmp((char *)lParam, utf8_bom, 3)));
3881 
3882  TRACE("EM_SETTEXTEX - %s, flags %d, cp %d\n",
3883  bUnicode ? debugstr_w((LPCWSTR)lParam) : debugstr_a((LPCSTR)lParam),
3884  pStruct->flags, pStruct->codepage);
3885 
3886  bSelection = (pStruct->flags & ST_SELECTION) != 0;
3887  if (bSelection) {
3888  int nStartCursor = ME_GetSelectionOfs(editor, &from, &to);
3890  ME_InternalDeleteText(editor, &editor->pCursors[nStartCursor], to - from, FALSE);
3891  } else {
3892  ME_Cursor start;
3893  ME_SetCursorToStart(editor, &start);
3894  ME_InternalDeleteText(editor, &start, ME_GetTextLength(editor), FALSE);
3895  style = editor->pBuffer->pDefaultStyle;
3896  }
3897 
3898  if (bRtf) {
3899  ME_StreamInRTFString(editor, bSelection, (char *)lParam);
3900  if (bSelection) {
3901  /* FIXME: The length returned doesn't include the rtf control
3902  * characters, only the actual text. */
3903  len = lParam ? strlen((char *)lParam) : 0;
3904  }
3905  } else {
3906  if (bUTF8 && !bUnicode) {
3907  wszText = ME_ToUnicode(CP_UTF8, (void *)(lParam+3), &len);
3908  ME_InsertTextFromCursor(editor, 0, wszText, len, style);
3909  ME_EndToUnicode(CP_UTF8, wszText);
3910  } else {
3911  wszText = ME_ToUnicode(pStruct->codepage, (void *)lParam, &len);
3912  ME_InsertTextFromCursor(editor, 0, wszText, len, style);
3913  ME_EndToUnicode(pStruct->codepage, wszText);
3914  }
3915  }
3916 
3917  if (bSelection) {
3920  } else {
3921  ME_Cursor cursor;
3922  len = 1;
3923  ME_SetCursorToStart(editor, &cursor);
3925  }
3926  ME_CommitUndo(editor);
3927  if (!(pStruct->flags & ST_KEEPUNDO))
3928  {
3929  editor->nModifyStep = oldModify;
3930  ME_EmptyUndoStack(editor);
3931  }
3932  ME_UpdateRepaint(editor, FALSE);
3933  return len;
3934  }
3935  case EM_SELECTIONTYPE:
3936  return ME_GetSelectionType(editor);
3937  case EM_SETBKGNDCOLOR:
3938  {
3939  LRESULT lColor;
3940  if (editor->rgbBackColor != -1) {
3941  DeleteObject(editor->hbrBackground);
3942  lColor = editor->rgbBackColor;
3943  }
3944  else lColor = ITextHost_TxGetSysColor(editor->texthost, COLOR_WINDOW);
3945 
3946  if (wParam)
3947  {
3948  editor->rgbBackColor = -1;
3950  }
3951  else
3952  {
3953  editor->rgbBackColor = lParam;
3954  editor->hbrBackground = CreateSolidBrush(editor->rgbBackColor);
3955  }
3957  return lColor;
3958  }
3959  case EM_GETMODIFY:
3960  return editor->nModifyStep == 0 ? 0 : -1;
3961  case EM_SETMODIFY:
3962  {
3963  if (wParam)
3964  editor->nModifyStep = 1;
3965  else
3966  editor->nModifyStep = 0;
3967 
3968  return 0;
3969  }
3970  case EM_SETREADONLY:
3971  {
3972  if (wParam)
3973  editor->styleFlags |= ES_READONLY;
3974  else
3975  editor->styleFlags &= ~ES_READONLY;
3976  return 1;
3977  }
3978  case EM_SETEVENTMASK:
3979  {
3980  DWORD nOldMask = editor->nEventMask;
3981 
3982  editor->nEventMask = lParam;
3983  return nOldMask;
3984  }
3985  case EM_GETEVENTMASK:
3986  return editor->nEventMask;
3987  case EM_SETCHARFORMAT:
3988  return handle_EM_SETCHARFORMAT( editor, wParam, (CHARFORMAT2W *)lParam );
3989  case EM_GETCHARFORMAT:
3990  {
3991  CHARFORMAT2W tmp, *dst = (CHARFORMAT2W *)lParam;
3992  if (dst->cbSize != sizeof(CHARFORMATA) &&
3993  dst->cbSize != sizeof(CHARFORMATW) &&
3994  dst->cbSize != sizeof(CHARFORMAT2A) &&
3995  dst->cbSize != sizeof(CHARFORMAT2W))
3996  return 0;
3997  tmp.cbSize = sizeof(tmp);
3998  if (!wParam)
3999  ME_GetDefaultCharFormat(editor, &tmp);
4000  else
4001  ME_GetSelectionCharFormat(editor, &tmp);
4002  cf2w_to_cfany(dst, &tmp);
4003  return tmp.dwMask;
4004  }
4005  case EM_SETPARAFORMAT:
4006  {
4008  ME_WrapMarkedParagraphs(editor);
4009  ME_UpdateScrollBar(editor);
4010  ME_CommitUndo(editor);
4011  return result;
4012  }
4013  case EM_GETPARAFORMAT:
4015  return ((PARAFORMAT2 *)lParam)->dwMask;
4017  {
4018  ME_DisplayItem *p = editor->pBuffer->pFirst;
4019  int y = editor->vert_si.nPos;
4020  int ypara = 0;
4021  int count = 0;
4022  int ystart, yend;
4023  while(p) {
4025  if (p->type == diTextEnd)
4026  break;
4027  if (p->type == diParagraph) {
4028  ypara = p->member.para.pt.y;
4029  continue;
4030  }
4031  ystart = ypara + p->member.row.pt.y;
4032  yend = ystart + p->member.row.nHeight;
4033  if (y < yend) {
4034  break;
4035  }
4036  count++;
4037  }
4038  return count;
4039  }
4040  case EM_HIDESELECTION:
4041  {
4042  editor->bHideSelection = (wParam != 0);
4043  ME_InvalidateSelection(editor);
4044  return 0;
4045  }
4046  case EM_LINESCROLL:
4047  {
4048  if (!(editor->styleFlags & ES_MULTILINE))
4049  return FALSE;
4050  ME_ScrollDown( editor, lParam * get_default_line_height( editor ) );
4051  return TRUE;
4052  }
4053  case WM_CLEAR:
4054  {
4055  int from, to;
4056  int nStartCursor = ME_GetSelectionOfs(editor, &from, &to);
4057  ME_InternalDeleteText(editor, &editor->pCursors[nStartCursor], to-from, FALSE);
4058  ME_CommitUndo(editor);
4059  ME_UpdateRepaint(editor, TRUE);
4060  return 0;
4061  }
4062  case EM_REPLACESEL:
4063  {
4064  int len = 0;
4065  LONG codepage = unicode ? CP_UNICODE : CP_ACP;
4066  LPWSTR wszText = ME_ToUnicode(codepage, (void *)lParam, &len);
4067 
4068  TRACE("EM_REPLACESEL - %s\n", debugstr_w(wszText));
4069 
4070  ME_ReplaceSel(editor, !!wParam, wszText, len);
4071  ME_EndToUnicode(codepage, wszText);
4072  return len;
4073  }
4074  case EM_SCROLLCARET:
4075  ME_EnsureVisible(editor, &editor->pCursors[0]);
4076  return 0;
4077  case WM_SETFONT:
4078  {
4079  LOGFONTW lf;
4080  CHARFORMAT2W fmt;
4081  HDC hDC;
4082  BOOL bRepaint = LOWORD(lParam);
4083 
4084  if (!wParam)
4086 
4087  if (!GetObjectW((HGDIOBJ)wParam, sizeof(LOGFONTW), &lf))
4088  return 0;
4089 
4090  hDC = ITextHost_TxGetDC(editor->texthost);
4093  if (editor->mode & TM_RICHTEXT) {
4094  ME_Cursor start;
4095  ME_SetCursorToStart(editor, &start);
4096  ME_SetCharFormat(editor, &start, NULL, &fmt);
4097  }
4098  ME_SetDefaultCharFormat(editor, &fmt);
4099 
4100  ME_CommitUndo(editor);
4101  ME_MarkAllForWrapping(editor);
4102  ME_WrapMarkedParagraphs(editor);
4103  ME_UpdateScrollBar(editor);
4104  if (bRepaint)
4105  ME_Repaint(editor);
4106  return 0;
4107  }
4108  case WM_SETTEXT:
4109  {
4110  ME_Cursor cursor;
4111  ME_SetCursorToStart(editor, &cursor);
4112  ME_InternalDeleteText(editor, &cursor, ME_GetTextLength(editor), FALSE);
4113  if (lParam)
4114  {
4115  TRACE("WM_SETTEXT lParam==%lx\n",lParam);
4116  if (!strncmp((char *)lParam, "{\\rtf", 5) ||
4117  !strncmp((char *)lParam, "{\\urtf", 6))
4118  {
4119  /* Undocumented: WM_SETTEXT supports RTF text */
4120  ME_StreamInRTFString(editor, 0, (char *)lParam);
4121  }
4122  else
4123  ME_SetText(editor, (void*)lParam, unicode);
4124  }
4125  else
4126  TRACE("WM_SETTEXT - NULL\n");
4127  ME_SetCursorToStart(editor, &cursor);
4129  set_selection_cursors(editor, 0, 0);
4130  editor->nModifyStep = 0;
4131  ME_CommitUndo(editor);
4132  ME_EmptyUndoStack(editor);
4133  ME_UpdateRepaint(editor, FALSE);
4134  return 1;
4135  }
4136  case EM_CANPASTE:
4137  return paste_special( editor, 0, NULL, TRUE );
4138  case WM_PASTE:
4139  case WM_MBUTTONDOWN:
4140  wParam = 0;
4141  lParam = 0;
4142  /* fall through */
4143  case EM_PASTESPECIAL:
4145  return 0;
4146  case WM_CUT:
4147  case WM_COPY:
4148  copy_or_cut(editor, msg == WM_CUT);
4149  return 0;
4150  case WM_GETTEXTLENGTH:
4151  {
4152  GETTEXTLENGTHEX how;
4153 
4154  /* CR/LF conversion required in 2.0 mode, verbatim in 1.0 mode */
4155  how.flags = GTL_CLOSE | (editor->bEmulateVersion10 ? 0 : GTL_USECRLF) | GTL_NUMCHARS;
4156  how.codepage = unicode ? CP_UNICODE : CP_ACP;
4157  return ME_GetTextLengthEx(editor, &how);
4158  }
4159  case EM_GETTEXTLENGTHEX:
4160  return ME_GetTextLengthEx(editor, (GETTEXTLENGTHEX *)wParam);
4161  case WM_GETTEXT:
4162  {
4163  GETTEXTEX ex;
4164  ex.cb = wParam * (unicode ? sizeof(WCHAR) : sizeof(CHAR));
4165  ex.flags = GT_USECRLF;
4166  ex.codepage = unicode ? CP_UNICODE : CP_ACP;
4167  ex.lpDefaultChar = NULL;
4168  ex.lpUsedDefChar = NULL;
4169  return ME_GetTextEx(editor, &ex, lParam);
4170  }
4171  case EM_GETTEXTEX:
4172  return ME_GetTextEx(editor, (GETTEXTEX*)wParam, lParam);
4173  case EM_GETSELTEXT:
4174  {
4175  int nFrom, nTo, nStartCur = ME_GetSelectionOfs(editor, &nFrom, &nTo);
4176  ME_Cursor *from = &editor->pCursors[nStartCur];
4177  return ME_GetTextRange(editor, (WCHAR *)lParam, from,
4178  nTo - nFrom, unicode);
4179  }
4180  case EM_GETSCROLLPOS:
4181  {
4182  POINT *point = (POINT *)lParam;
4183  point->x = editor->horz_si.nPos;
4184  point->y = editor->vert_si.nPos;
4185  /* 16-bit scaled value is returned as stored in scrollinfo */
4186  if (editor->horz_si.nMax > 0xffff)
4187  point->x = MulDiv(point->x, 0xffff, editor->horz_si.nMax);
4188  if (editor->vert_si.nMax > 0xffff)
4189  point->y = MulDiv(point->y, 0xffff, editor->vert_si.nMax);
4190  return 1;
4191  }
4192  case EM_GETTEXTRANGE:
4193  {
4194  TEXTRANGEW *rng = (TEXTRANGEW *)lParam;
4195  ME_Cursor start;
4196  int nStart = rng->chrg.cpMin;
4197  int nEnd = rng->chrg.cpMax;
4198  int textlength = ME_GetTextLength(editor);
4199 
4200  TRACE("EM_GETTEXTRANGE min=%d max=%d unicode=%d textlength=%d\n",
4201  rng->chrg.cpMin, rng->chrg.cpMax, unicode, textlength);
4202  if (nStart < 0) return 0;
4203  if ((nStart == 0 && nEnd == -1) || nEnd > textlength)
4204  nEnd = textlength;
4205  if (nStart >= nEnd) return 0;
4206 
4207  ME_CursorFromCharOfs(editor, nStart, &start);
4208  return ME_GetTextRange(editor, rng->lpstrText, &start, nEnd - nStart, unicode);
4209  }
4210  case EM_GETLINE:
4211  {
4212  ME_DisplayItem *run;
4213  const unsigned int nMaxChars = *(WORD *) lParam;
4214  unsigned int nCharsLeft = nMaxChars;
4215  char *dest = (char *) lParam;
4216  BOOL wroteNull = FALSE;
4217 
4218  TRACE("EM_GETLINE: row=%d, nMaxChars=%d (%s)\n", (int) wParam, nMaxChars,
4219  unicode ? "Unicode" : "Ansi");
4220 
4221  run = ME_FindRowWithNumber(editor, wParam);
4222  if (run == NULL)
4223  return 0;
4224 
4225  while (nCharsLeft && (run = ME_FindItemFwd(run, diRunOrStartRow))
4226  && run->type == diRun)
4227  {
4228  WCHAR *str = get_text( &run->member.run, 0 );
4229  unsigned int nCopy;
4230 
4231  nCopy = min(nCharsLeft, run->member.run.len);
4232 
4233  if (unicode)
4234  memcpy(dest, str, nCopy * sizeof(WCHAR));
4235  else
4236  nCopy = WideCharToMultiByte(CP_ACP, 0, str, nCopy, dest,
4237  nCharsLeft, NULL, NULL);
4238  dest += nCopy * (unicode ? sizeof(WCHAR) : 1);
4239  nCharsLeft -= nCopy;
4240  }
4241 
4242  /* append line termination, space allowing */
4243  if (nCharsLeft > 0)
4244  {
4245  if (unicode)
4246  *((WCHAR *)dest) = '\0';
4247  else
4248  *dest = '\0';
4249  nCharsLeft--;
4250  wroteNull = TRUE;
4251  }
4252 
4253  TRACE("EM_GETLINE: got %u characters\n", nMaxChars - nCharsLeft);
4254  return nMaxChars - nCharsLeft - (wroteNull ? 1 : 0);
4255  }
4256  case EM_GETLINECOUNT:
4257  {
4258  ME_DisplayItem *item = editor->pBuffer->pLast;
4259  int nRows = editor->total_rows;
4260  ME_DisplayItem *prev_para = NULL, *last_para = NULL;
4261 
4262  last_para = ME_FindItemBack(item, diRun);
4263  prev_para = ME_FindItemBack(last_para, diRun);
4264  assert(last_para);
4265  assert(last_para->member.run.nFlags & MERF_ENDPARA);
4266  if (editor->bEmulateVersion10 && prev_para &&
4267  last_para->member.run.nCharOfs == 0 &&
4268  prev_para->member.run.len == 1 &&
4269  *get_text( &prev_para->member.run, 0 ) == '\r')
4270  {
4271  /* In 1.0 emulation, the last solitary \r at the very end of the text
4272  (if one exists) is NOT a line break.
4273  FIXME: this is an ugly hack. This should have a more regular model. */
4274  nRows--;
4275  }
4276 
4277  TRACE("EM_GETLINECOUNT: nRows==%d\n", nRows);
4278  return max(1, nRows);
4279  }
4280  case EM_LINEFROMCHAR:
4281  {
4282  if (wParam == -1)
4283  return ME_RowNumberFromCharOfs(editor, ME_GetCursorOfs(&editor->pCursors[1]));
4284  else
4285  return ME_RowNumberFromCharOfs(editor, wParam);
4286  }
4287  case EM_EXLINEFROMCHAR:
4288  {
4289  if (lParam == -1)
4290  return ME_RowNumberFromCharOfs(editor, ME_GetCursorOfs(&editor->pCursors[1]));
4291  else
4292  return ME_RowNumberFromCharOfs(editor, lParam);
4293  }
4294  case EM_LINEINDEX:
4295  {
4296  ME_DisplayItem *item, *para;
4297  int nCharOfs;
4298 
4299  if (wParam == -1)
4300  item = ME_FindItemBack(editor->pCursors[0].pRun, diStartRow);
4301  else
4302  item = ME_FindRowWithNumber(editor, wParam);
4303  if (!item)
4304  return -1;
4305  para = ME_GetParagraph(item);
4307  nCharOfs = para->member.para.nCharOfs + item->member.run.nCharOfs;
4308  TRACE("EM_LINEINDEX: nCharOfs==%d\n", nCharOfs);
4309  return nCharOfs;
4310  }
4311  case EM_LINELENGTH:
4312  {
4313  ME_DisplayItem *item, *item_end;
4314  int nChars = 0, nThisLineOfs = 0, nNextLineOfs = 0;
4315  ME_DisplayItem *para, *run;
4316 
4317  if (wParam > ME_GetTextLength(editor))
4318  return 0;
4319  if (wParam == -1)
4320  {
4321  FIXME("EM_LINELENGTH: returning number of unselected characters on lines with selection unsupported.\n");
4322  return 0;
4323  }
4324  ME_RunOfsFromCharOfs(editor, wParam, &para, &run, NULL);
4325  item = ME_RowStart(run);
4326  nThisLineOfs = ME_CharOfsFromRunOfs(editor, para, ME_FindItemFwd(item, diRun), 0);
4328  if (item_end->type == diStartRow) {
4329  nNextLineOfs = ME_CharOfsFromRunOfs(editor, para, ME_FindItemFwd(item_end, diRun), 0);
4330  } else {
4331  ME_DisplayItem *endRun = ME_FindItemBack(item_end, diRun);
4332  assert(endRun && endRun->member.run.nFlags & MERF_ENDPARA);
4333  nNextLineOfs = item_end->member.para.nCharOfs - endRun->member.run.len;
4334  }
4335  nChars = nNextLineOfs - nThisLineOfs;
4336  TRACE("EM_LINELENGTH(%ld)==%d\n",wParam, nChars);
4337  return nChars;
4338  }
4339  case EM_EXLIMITTEXT:
4340  {
4341  if ((int)lParam < 0)
4342  return 0;
4343  if (lParam == 0)
4344  editor->nTextLimit = 65536;
4345  else
4346  editor->nTextLimit = (int) lParam;
4347  return 0;
4348  }
4349  case EM_LIMITTEXT:
4350  {
4351  if (wParam == 0)
4352  editor->nTextLimit = 65536;
4353  else
4354  editor->nTextLimit = (int) wParam;
4355  return 0;
4356  }
4357  case EM_GETLIMITTEXT:
4358  {
4359  return editor->nTextLimit;
4360  }
4361  case EM_FINDTEXT:
4362  {
4363  LRESULT r;
4364  if(!unicode){
4365  FINDTEXTA *ft = (FINDTEXTA *)lParam;
4366  int nChars = MultiByteToWideChar(CP_ACP, 0, ft->lpstrText, -1, NULL, 0);
4367  WCHAR *tmp;
4368 
4369  if ((tmp = heap_alloc(nChars * sizeof(*tmp))) != NULL)
4370  MultiByteToWideChar(CP_ACP, 0, ft->lpstrText, -1, tmp, nChars);
4371  r = ME_FindText(editor, wParam, &ft->chrg, tmp, NULL);
4372  heap_free(tmp);
4373  }else{
4374  FINDTEXTW *ft = (FINDTEXTW *)lParam;
4375  r = ME_FindText(editor, wParam, &ft->chrg, ft->lpstrText, NULL);
4376  }
4377  return r;
4378  }
4379  case EM_FINDTEXTEX:
4380  {
4381  LRESULT r;
4382  if(!unicode){
4384  int nChars = MultiByteToWideChar(CP_ACP, 0, ex->lpstrText, -1, NULL, 0);
4385  WCHAR *tmp;
4386 
4387  if ((tmp = heap_alloc(nChars * sizeof(*tmp))) != NULL)
4388  MultiByteToWideChar(CP_ACP, 0, ex->lpstrText, -1, tmp, nChars);
4389  r = ME_FindText(editor, wParam, &ex->chrg, tmp, &ex->chrgText);
4390  heap_free(tmp);
4391  }else{
4393  r = ME_FindText(editor, wParam, &ex->chrg, ex->lpstrText, &ex->chrgText);
4394  }
4395  return r;
4396  }
4397  case EM_FINDTEXTW:
4398  {
4399  FINDTEXTW *ft = (FINDTEXTW *)lParam;
4400  return ME_FindText(editor, wParam, &ft->chrg, ft->lpstrText, NULL);
4401  }
4402  case EM_FINDTEXTEXW:
4403  {
4405  return ME_FindText(editor, wParam, &ex->chrg, ex->lpstrText, &ex->chrgText);
4406  }