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