[RICHED20] Sync with Wine Staging 3.9. CORE-14656
[reactos.git] / dll / win32 / riched20 / editor.c
1 /*
2 * RichEdit - functions dealing with editor object
3 *
4 * Copyright 2004 by Krzysztof Foltman
5 * Copyright 2005 by Cihan Altinay
6 * Copyright 2005 by Phil Krylov
7 * Copyright 2008 Eric Pouech
8 *
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
13 *
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
18 *
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22 */
23
24 /*
25 API implementation status:
26
27 Messages (ANSI versions not done yet)
28 + EM_AUTOURLDETECT 2.0
29 + EM_CANPASTE
30 + EM_CANREDO 2.0
31 + EM_CANUNDO
32 + EM_CHARFROMPOS
33 - EM_DISPLAYBAND
34 + EM_EMPTYUNDOBUFFER
35 + EM_EXGETSEL
36 + EM_EXLIMITTEXT
37 + EM_EXLINEFROMCHAR
38 + EM_EXSETSEL
39 + EM_FINDTEXT (only FR_DOWN flag implemented)
40 + EM_FINDTEXTEX (only FR_DOWN flag implemented)
41 - EM_FINDWORDBREAK
42 - EM_FMTLINES
43 - EM_FORMATRANGE
44 + EM_GETAUTOURLDETECT 2.0
45 - EM_GETBIDIOPTIONS 3.0
46 - EM_GETCHARFORMAT (partly done)
47 - EM_GETEDITSTYLE
48 + EM_GETEVENTMASK
49 + EM_GETFIRSTVISIBLELINE (can be optimized if needed)
50 - EM_GETIMECOLOR 1.0asian
51 - EM_GETIMECOMPMODE 2.0
52 - EM_GETIMEOPTIONS 1.0asian
53 - EM_GETIMESTATUS
54 - EM_GETLANGOPTIONS 2.0
55 + EM_GETLIMITTEXT
56 + EM_GETLINE
57 + EM_GETLINECOUNT returns number of rows, not of paragraphs
58 + EM_GETMODIFY
59 + EM_GETOLEINTERFACE
60 + EM_GETOPTIONS
61 + EM_GETPARAFORMAT
62 + EM_GETPASSWORDCHAR 2.0
63 - EM_GETPUNCTUATION 1.0asian
64 + EM_GETRECT
65 - EM_GETREDONAME 2.0
66 + EM_GETSEL
67 + EM_GETSELTEXT (ANSI&Unicode)
68 + EM_GETSCROLLPOS 3.0
69 ! - EM_GETTHUMB
70 + EM_GETTEXTEX 2.0
71 + EM_GETTEXTLENGTHEX (GTL_PRECISE unimplemented)
72 + EM_GETTEXTMODE 2.0
73 ? + EM_GETTEXTRANGE (ANSI&Unicode)
74 - EM_GETTYPOGRAPHYOPTIONS 3.0
75 - EM_GETUNDONAME
76 + EM_GETWORDBREAKPROC
77 - EM_GETWORDBREAKPROCEX
78 - EM_GETWORDWRAPMODE 1.0asian
79 + EM_GETZOOM 3.0
80 + EM_HIDESELECTION
81 + EM_LIMITTEXT (Also called EM_SETLIMITTEXT)
82 + EM_LINEFROMCHAR
83 + EM_LINEINDEX
84 + EM_LINELENGTH
85 + EM_LINESCROLL
86 - EM_PASTESPECIAL
87 + EM_POSFROMCHAR
88 + EM_REDO 2.0
89 + EM_REQUESTRESIZE
90 + EM_REPLACESEL (proper style?) ANSI&Unicode
91 + EM_SCROLL
92 + EM_SCROLLCARET
93 + EM_SELECTIONTYPE
94 - EM_SETBIDIOPTIONS 3.0
95 + EM_SETBKGNDCOLOR
96 + EM_SETCHARFORMAT (partly done, no ANSI)
97 - EM_SETEDITSTYLE
98 + EM_SETEVENTMASK (few notifications supported)
99 + EM_SETFONTSIZE
100 - EM_SETIMECOLOR 1.0asian
101 - EM_SETIMEOPTIONS 1.0asian
102 - EM_SETIMESTATUS
103 - EM_SETLANGOPTIONS 2.0
104 - EM_SETLIMITTEXT
105 - EM_SETMARGINS
106 + EM_SETMODIFY (not sure if implementation is correct)
107 - EM_SETOLECALLBACK
108 + EM_SETOPTIONS (partially implemented)
109 - EM_SETPALETTE 2.0
110 + EM_SETPARAFORMAT
111 + EM_SETPASSWORDCHAR 2.0
112 - EM_SETPUNCTUATION 1.0asian
113 + EM_SETREADONLY no beep on modification attempt
114 + EM_SETRECT
115 + EM_SETRECTNP (EM_SETRECT without repainting)
116 + EM_SETSEL
117 + EM_SETSCROLLPOS 3.0
118 - EM_SETTABSTOPS 3.0
119 - EM_SETTARGETDEVICE (partial)
120 + EM_SETTEXTEX 3.0 (proper style?)
121 - EM_SETTEXTMODE 2.0
122 - EM_SETTYPOGRAPHYOPTIONS 3.0
123 + EM_SETUNDOLIMIT 2.0
124 + EM_SETWORDBREAKPROC (used only for word movement at the moment)
125 - EM_SETWORDBREAKPROCEX
126 - EM_SETWORDWRAPMODE 1.0asian
127 + EM_SETZOOM 3.0
128 + EM_SHOWSCROLLBAR 2.0
129 + EM_STOPGROUPTYPING 2.0
130 + EM_STREAMIN
131 + EM_STREAMOUT
132 + EM_UNDO
133 + WM_CHAR
134 + WM_CLEAR
135 + WM_COPY
136 + WM_CUT
137 + WM_GETDLGCODE (the current implementation is incomplete)
138 + WM_GETTEXT (ANSI&Unicode)
139 + WM_GETTEXTLENGTH (ANSI version sucks)
140 + WM_HSCROLL
141 + WM_PASTE
142 + WM_SETFONT
143 + WM_SETTEXT (resets undo stack !) (proper style?) ANSI&Unicode
144 + WM_STYLECHANGING (seems to do nothing)
145 + WM_STYLECHANGED (seems to do nothing)
146 + WM_UNICHAR
147 + WM_VSCROLL
148
149 Notifications
150
151 * EN_CHANGE (sent from the wrong place)
152 - EN_CORRECTTEXT
153 - EN_DROPFILES
154 - EN_ERRSPACE
155 - EN_HSCROLL
156 - EN_IMECHANGE
157 + EN_KILLFOCUS
158 - EN_LINK
159 - EN_MAXTEXT
160 - EN_MSGFILTER
161 - EN_OLEOPFAILED
162 - EN_PROTECTED
163 + EN_REQUESTRESIZE
164 - EN_SAVECLIPBOARD
165 + EN_SELCHANGE
166 + EN_SETFOCUS
167 - EN_STOPNOUNDO
168 * EN_UPDATE (sent from the wrong place)
169 - EN_VSCROLL
170
171 Styles
172
173 - ES_AUTOHSCROLL
174 - ES_AUTOVSCROLL
175 + ES_CENTER
176 + ES_DISABLENOSCROLL (scrollbar is always visible)
177 - ES_EX_NOCALLOLEINIT
178 + ES_LEFT
179 + ES_MULTILINE
180 - ES_NOIME
181 - ES_READONLY (I'm not sure if beeping is the proper behaviour)
182 + ES_RIGHT
183 - ES_SAVESEL
184 - ES_SELFIME
185 - ES_SUNKEN
186 - ES_VERTICAL
187 - ES_WANTRETURN (don't know how to do WM_GETDLGCODE part)
188 - WS_SETFONT
189 + WS_HSCROLL
190 + WS_VSCROLL
191 */
192
193 /*
194 * RICHED20 TODO (incomplete):
195 *
196 * - messages/styles/notifications listed above
197 * - add remaining CHARFORMAT/PARAFORMAT fields
198 * - right/center align should strip spaces from the beginning
199 * - pictures/OLE objects (not just smiling faces that lack API support ;-) )
200 * - COM interface (looks like a major pain in the TODO list)
201 * - calculate heights of pictures (half-done)
202 * - hysteresis during wrapping (related to scrollbars appearing/disappearing)
203 * - find/replace
204 * - how to implement EM_FORMATRANGE and EM_DISPLAYBAND ? (Mission Impossible)
205 * - italic caret with italic fonts
206 * - IME
207 * - most notifications aren't sent at all (the most important ones are)
208 * - when should EN_SELCHANGE be sent after text change ? (before/after EN_UPDATE?)
209 * - WM_SETTEXT may use wrong style (but I'm 80% sure it's OK)
210 * - EM_GETCHARFORMAT with SCF_SELECTION may not behave 100% like in original (but very close)
211 * - full justification
212 * - hyphenation
213 * - tables
214 * - ListBox & ComboBox not implemented
215 *
216 * Bugs that are probably fixed, but not so easy to verify:
217 * - EN_UPDATE/EN_CHANGE are handled very incorrectly (should be OK now)
218 * - undo for ME_JoinParagraphs doesn't store paragraph format ? (it does)
219 * - check/fix artificial EOL logic (bCursorAtEnd, hardly logical)
220 * - caret shouldn't be displayed when selection isn't empty
221 * - check refcounting in style management functions (looks perfect now, but no bugs is suspicious)
222 * - undo for setting default format (done, might be buggy)
223 * - styles might be not released properly (looks like they work like charm, but who knows?
224 *
225 */
226
227 #define NONAMELESSUNION
228
229 #include "editor.h"
230 #include "commdlg.h"
231 #include "winreg.h"
232 #define NO_SHLWAPI_STREAM
233 #include "shlwapi.h"
234 #include "rtf.h"
235 #include "imm.h"
236 #include "res.h"
237
238 #ifdef __REACTOS__
239 #include <reactos/undocuser.h>
240 #endif
241
242 #define STACK_SIZE_DEFAULT 100
243 #define STACK_SIZE_MAX 1000
244
245 #define TEXT_LIMIT_DEFAULT 32767
246
247 WINE_DEFAULT_DEBUG_CHANNEL(richedit);
248
249 static BOOL ME_RegisterEditorClass(HINSTANCE);
250 static BOOL ME_UpdateLinkAttribute(ME_TextEditor *editor, ME_Cursor *start, int nChars);
251
252 static const WCHAR REListBox20W[] = {'R','E','L','i','s','t','B','o','x','2','0','W', 0};
253 static const WCHAR REComboBox20W[] = {'R','E','C','o','m','b','o','B','o','x','2','0','W', 0};
254 static HCURSOR hLeft;
255
256 BOOL me_debug = FALSE;
257 HANDLE me_heap = NULL;
258
259 static BOOL ME_ListBoxRegistered = FALSE;
260 static BOOL ME_ComboBoxRegistered = FALSE;
261
262 static inline BOOL is_version_nt(void)
263 {
264 return !(GetVersion() & 0x80000000);
265 }
266
267 static ME_TextBuffer *ME_MakeText(void) {
268 ME_TextBuffer *buf = heap_alloc(sizeof(*buf));
269 ME_DisplayItem *p1 = ME_MakeDI(diTextStart);
270 ME_DisplayItem *p2 = ME_MakeDI(diTextEnd);
271
272 p1->prev = NULL;
273 p1->next = p2;
274 p2->prev = p1;
275 p2->next = NULL;
276 p1->member.para.next_para = p2;
277 p2->member.para.prev_para = p1;
278 p2->member.para.nCharOfs = 0;
279
280 buf->pFirst = p1;
281 buf->pLast = p2;
282 buf->pCharStyle = NULL;
283
284 return buf;
285 }
286
287
288 static LRESULT ME_StreamInText(ME_TextEditor *editor, DWORD dwFormat, ME_InStream *stream, ME_Style *style)
289 {
290 WCHAR *pText;
291 LRESULT total_bytes_read = 0;
292 BOOL is_read = FALSE;
293 DWORD cp = CP_ACP, copy = 0;
294 char conv_buf[4 + STREAMIN_BUFFER_SIZE]; /* up to 4 additional UTF-8 bytes */
295
296 static const char bom_utf8[] = {0xEF, 0xBB, 0xBF};
297
298 TRACE("%08x %p\n", dwFormat, stream);
299
300 do {
301 LONG nWideChars = 0;
302 WCHAR wszText[STREAMIN_BUFFER_SIZE+1];
303
304 if (!stream->dwSize)
305 {
306 ME_StreamInFill(stream);
307 if (stream->editstream->dwError)
308 break;
309 if (!stream->dwSize)
310 break;
311 total_bytes_read += stream->dwSize;
312 }
313
314 if (!(dwFormat & SF_UNICODE))
315 {
316 char * buf = stream->buffer;
317 DWORD size = stream->dwSize, end;
318
319 if (!is_read)
320 {
321 is_read = TRUE;
322 if (stream->dwSize >= 3 && !memcmp(stream->buffer, bom_utf8, 3))
323 {
324 cp = CP_UTF8;
325 buf += 3;
326 size -= 3;
327 }
328 }
329
330 if (cp == CP_UTF8)
331 {
332 if (copy)
333 {
334 memcpy(conv_buf + copy, buf, size);
335 buf = conv_buf;
336 size += copy;
337 }
338 end = size;
339 while ((buf[end-1] & 0xC0) == 0x80)
340 {
341 --end;
342 --total_bytes_read; /* strange, but seems to match windows */
343 }
344 if (buf[end-1] & 0x80)
345 {
346 DWORD need = 0;
347 if ((buf[end-1] & 0xE0) == 0xC0)
348 need = 1;
349 if ((buf[end-1] & 0xF0) == 0xE0)
350 need = 2;
351 if ((buf[end-1] & 0xF8) == 0xF0)
352 need = 3;
353
354 if (size - end >= need)
355 {
356 /* we have enough bytes for this sequence */
357 end = size;
358 }
359 else
360 {
361 /* need more bytes, so don't transcode this sequence */
362 --end;
363 }
364 }
365 }
366 else
367 end = size;
368
369 nWideChars = MultiByteToWideChar(cp, 0, buf, end, wszText, STREAMIN_BUFFER_SIZE);
370 pText = wszText;
371
372 if (cp == CP_UTF8)
373 {
374 if (end != size)
375 {
376 memcpy(conv_buf, buf + end, size - end);
377 copy = size - end;
378 }
379 }
380 }
381 else
382 {
383 nWideChars = stream->dwSize >> 1;
384 pText = (WCHAR *)stream->buffer;
385 }
386
387 ME_InsertTextFromCursor(editor, 0, pText, nWideChars, style);
388 if (stream->dwSize == 0)
389 break;
390 stream->dwSize = 0;
391 } while(1);
392 return total_bytes_read;
393 }
394
395 static void ME_ApplyBorderProperties(RTF_Info *info,
396 ME_BorderRect *borderRect,
397 RTFBorder *borderDef)
398 {
399 int i, colorNum;
400 ME_Border *pBorders[] = {&borderRect->top,
401 &borderRect->left,
402 &borderRect->bottom,
403 &borderRect->right};
404 for (i = 0; i < 4; i++)
405 {
406 RTFColor *colorDef = info->colorList;
407 pBorders[i]->width = borderDef[i].width;
408 colorNum = borderDef[i].color;
409 while (colorDef && colorDef->rtfCNum != colorNum)
410 colorDef = colorDef->rtfNextColor;
411 if (colorDef)
412 pBorders[i]->colorRef = RGB(
413 colorDef->rtfCRed >= 0 ? colorDef->rtfCRed : 0,
414 colorDef->rtfCGreen >= 0 ? colorDef->rtfCGreen : 0,
415 colorDef->rtfCBlue >= 0 ? colorDef->rtfCBlue : 0);
416 else
417 pBorders[i]->colorRef = RGB(0, 0, 0);
418 }
419 }
420
421 void ME_RTFCharAttrHook(RTF_Info *info)
422 {
423 CHARFORMAT2W fmt;
424 fmt.cbSize = sizeof(fmt);
425 fmt.dwMask = 0;
426 fmt.dwEffects = 0;
427
428 switch(info->rtfMinor)
429 {
430 case rtfPlain:
431 /* FIXME add more flags once they're implemented */
432 fmt.dwMask = CFM_BOLD | CFM_ITALIC | CFM_UNDERLINE | CFM_UNDERLINETYPE | CFM_STRIKEOUT |
433 CFM_COLOR | CFM_BACKCOLOR | CFM_SIZE | CFM_WEIGHT;
434 fmt.dwEffects = CFE_AUTOCOLOR | CFE_AUTOBACKCOLOR;
435 fmt.yHeight = 12*20; /* 12pt */
436 fmt.wWeight = FW_NORMAL;
437 fmt.bUnderlineType = CFU_UNDERLINE;
438 break;
439 case rtfBold:
440 fmt.dwMask = CFM_BOLD | CFM_WEIGHT;
441 fmt.dwEffects = info->rtfParam ? CFE_BOLD : 0;
442 fmt.wWeight = info->rtfParam ? FW_BOLD : FW_NORMAL;
443 break;
444 case rtfItalic:
445 fmt.dwMask = CFM_ITALIC;
446 fmt.dwEffects = info->rtfParam ? fmt.dwMask : 0;
447 break;
448 case rtfUnderline:
449 fmt.dwMask = CFM_UNDERLINETYPE | CFM_UNDERLINE;
450 fmt.bUnderlineType = CFU_UNDERLINE;
451 fmt.dwEffects = info->rtfParam ? CFE_UNDERLINE : 0;
452 break;
453 case rtfDotUnderline:
454 fmt.dwMask = CFM_UNDERLINETYPE | CFM_UNDERLINE;
455 fmt.bUnderlineType = CFU_UNDERLINEDOTTED;
456 fmt.dwEffects = info->rtfParam ? CFE_UNDERLINE : 0;
457 break;
458 case rtfDbUnderline:
459 fmt.dwMask = CFM_UNDERLINETYPE | CFM_UNDERLINE;
460 fmt.bUnderlineType = CFU_UNDERLINEDOUBLE;
461 fmt.dwEffects = info->rtfParam ? CFE_UNDERLINE : 0;
462 break;
463 case rtfWordUnderline:
464 fmt.dwMask = CFM_UNDERLINETYPE | CFM_UNDERLINE;
465 fmt.bUnderlineType = CFU_UNDERLINEWORD;
466 fmt.dwEffects = info->rtfParam ? CFE_UNDERLINE : 0;
467 break;
468 case rtfNoUnderline:
469 fmt.dwMask = CFM_UNDERLINE;
470 fmt.dwEffects = 0;
471 break;
472 case rtfStrikeThru:
473 fmt.dwMask = CFM_STRIKEOUT;
474 fmt.dwEffects = info->rtfParam ? fmt.dwMask : 0;
475 break;
476 case rtfSubScript:
477 case rtfSuperScript:
478 case rtfSubScrShrink:
479 case rtfSuperScrShrink:
480 case rtfNoSuperSub:
481 fmt.dwMask = CFM_SUBSCRIPT|CFM_SUPERSCRIPT;
482 if (info->rtfMinor == rtfSubScrShrink) fmt.dwEffects = CFE_SUBSCRIPT;
483 if (info->rtfMinor == rtfSuperScrShrink) fmt.dwEffects = CFE_SUPERSCRIPT;
484 if (info->rtfMinor == rtfNoSuperSub) fmt.dwEffects = 0;
485 break;
486 case rtfInvisible:
487 fmt.dwMask = CFM_HIDDEN;
488 fmt.dwEffects = info->rtfParam ? fmt.dwMask : 0;
489 break;
490 case rtfBackColor:
491 fmt.dwMask = CFM_BACKCOLOR;
492 fmt.dwEffects = 0;
493 if (info->rtfParam == 0)
494 fmt.dwEffects = CFE_AUTOBACKCOLOR;
495 else if (info->rtfParam != rtfNoParam)
496 {
497 RTFColor *c = RTFGetColor(info, info->rtfParam);
498 if (c && c->rtfCBlue >= 0)
499 fmt.crBackColor = (c->rtfCBlue<<16)|(c->rtfCGreen<<8)|(c->rtfCRed);
500 else
501 fmt.dwEffects = CFE_AUTOBACKCOLOR;
502 }
503 break;
504 case rtfForeColor:
505 fmt.dwMask = CFM_COLOR;
506 fmt.dwEffects = 0;
507 if (info->rtfParam == 0)
508 fmt.dwEffects = CFE_AUTOCOLOR;
509 else if (info->rtfParam != rtfNoParam)
510 {
511 RTFColor *c = RTFGetColor(info, info->rtfParam);
512 if (c && c->rtfCBlue >= 0)
513 fmt.crTextColor = (c->rtfCBlue<<16)|(c->rtfCGreen<<8)|(c->rtfCRed);
514 else {
515 fmt.dwEffects = CFE_AUTOCOLOR;
516 }
517 }
518 break;
519 case rtfFontNum:
520 if (info->rtfParam != rtfNoParam)
521 {
522 RTFFont *f = RTFGetFont(info, info->rtfParam);
523 if (f)
524 {
525 MultiByteToWideChar(CP_ACP, 0, f->rtfFName, -1, fmt.szFaceName, sizeof(fmt.szFaceName)/sizeof(WCHAR));
526 fmt.szFaceName[sizeof(fmt.szFaceName)/sizeof(WCHAR)-1] = '\0';
527 fmt.bCharSet = f->rtfFCharSet;
528 fmt.dwMask = CFM_FACE | CFM_CHARSET;
529 fmt.bPitchAndFamily = f->rtfFPitch | (f->rtfFFamily << 4);
530 }
531 }
532 break;
533 case rtfFontSize:
534 fmt.dwMask = CFM_SIZE;
535 if (info->rtfParam != rtfNoParam)
536 fmt.yHeight = info->rtfParam*10;
537 break;
538 }
539 if (fmt.dwMask) {
540 ME_Style *style2;
541 RTFFlushOutputBuffer(info);
542 /* FIXME too slow ? how come ? */
543 style2 = ME_ApplyStyle(info->editor, info->style, &fmt);
544 ME_ReleaseStyle(info->style);
545 info->style = style2;
546 info->styleChanged = TRUE;
547 }
548 }
549
550 /* FIXME this function doesn't get any information about context of the RTF tag, which is very bad,
551 the same tags mean different things in different contexts */
552 void ME_RTFParAttrHook(RTF_Info *info)
553 {
554 switch(info->rtfMinor)
555 {
556 case rtfParDef: /* restores default paragraph attributes */
557 if (!info->editor->bEmulateVersion10) /* v4.1 */
558 info->borderType = RTFBorderParaLeft;
559 else /* v1.0 - 3.0 */
560 info->borderType = RTFBorderParaTop;
561 info->fmt.dwMask = PFM_ALIGNMENT | PFM_BORDER | PFM_LINESPACING | PFM_TABSTOPS |
562 PFM_OFFSET | PFM_RIGHTINDENT | PFM_SPACEAFTER | PFM_SPACEBEFORE |
563 PFM_STARTINDENT | PFM_RTLPARA | PFM_NUMBERING | PFM_NUMBERINGSTART |
564 PFM_NUMBERINGSTYLE | PFM_NUMBERINGTAB;
565 /* TODO: shading */
566 info->fmt.wAlignment = PFA_LEFT;
567 info->fmt.cTabCount = 0;
568 info->fmt.dxOffset = info->fmt.dxStartIndent = info->fmt.dxRightIndent = 0;
569 info->fmt.wBorderWidth = info->fmt.wBorders = 0;
570 info->fmt.wBorderSpace = 0;
571 info->fmt.bLineSpacingRule = 0;
572 info->fmt.dySpaceBefore = info->fmt.dySpaceAfter = 0;
573 info->fmt.dyLineSpacing = 0;
574 info->fmt.wEffects &= ~PFE_RTLPARA;
575 info->fmt.wNumbering = 0;
576 info->fmt.wNumberingStart = 0;
577 info->fmt.wNumberingStyle = 0;
578 info->fmt.wNumberingTab = 0;
579
580 if (!info->editor->bEmulateVersion10) /* v4.1 */
581 {
582 if (info->tableDef && info->tableDef->tableRowStart &&
583 info->tableDef->tableRowStart->member.para.nFlags & MEPF_ROWEND)
584 {
585 ME_Cursor cursor;
586 ME_DisplayItem *para;
587 /* We are just after a table row. */
588 RTFFlushOutputBuffer(info);
589 cursor = info->editor->pCursors[0];
590 para = cursor.pPara;
591 if (para == info->tableDef->tableRowStart->member.para.next_para
592 && !cursor.nOffset && !cursor.pRun->member.run.nCharOfs)
593 {
594 /* Since the table row end, no text has been inserted, and the \intbl
595 * control word has not be used. We can confirm that we are not in a
596 * table anymore.
597 */
598 info->tableDef->tableRowStart = NULL;
599 info->canInheritInTbl = FALSE;
600 }
601 }
602 } else { /* v1.0 - v3.0 */
603 info->fmt.dwMask |= PFM_TABLE;
604 info->fmt.wEffects &= ~PFE_TABLE;
605 }
606 break;
607 case rtfNestLevel:
608 if (!info->editor->bEmulateVersion10) /* v4.1 */
609 {
610 while (info->rtfParam > info->nestingLevel) {
611 RTFTable *tableDef = heap_alloc_zero(sizeof(*tableDef));
612 tableDef->parent = info->tableDef;
613 info->tableDef = tableDef;
614
615 RTFFlushOutputBuffer(info);
616 if (tableDef->tableRowStart &&
617 tableDef->tableRowStart->member.para.nFlags & MEPF_ROWEND)
618 {
619 ME_DisplayItem *para = tableDef->tableRowStart;
620 para = para->member.para.next_para;
621 para = ME_InsertTableRowStartAtParagraph(info->editor, para);
622 tableDef->tableRowStart = para;
623 } else {
624 ME_Cursor cursor;
625 WCHAR endl = '\r';
626 cursor = info->editor->pCursors[0];
627 if (cursor.nOffset || cursor.pRun->member.run.nCharOfs)
628 ME_InsertTextFromCursor(info->editor, 0, &endl, 1, info->style);
629 tableDef->tableRowStart = ME_InsertTableRowStartFromCursor(info->editor);
630 }
631
632 info->nestingLevel++;
633 }
634 info->canInheritInTbl = FALSE;
635 }
636 break;
637 case rtfInTable:
638 {
639 if (!info->editor->bEmulateVersion10) /* v4.1 */
640 {
641 if (info->nestingLevel < 1)
642 {
643 RTFTable *tableDef;
644 if (!info->tableDef)
645 info->tableDef = heap_alloc_zero(sizeof(*info->tableDef));
646 tableDef = info->tableDef;
647 RTFFlushOutputBuffer(info);
648 if (tableDef->tableRowStart &&
649 tableDef->tableRowStart->member.para.nFlags & MEPF_ROWEND)
650 {
651 ME_DisplayItem *para = tableDef->tableRowStart;
652 para = para->member.para.next_para;
653 para = ME_InsertTableRowStartAtParagraph(info->editor, para);
654 tableDef->tableRowStart = para;
655 } else {
656 ME_Cursor cursor;
657 WCHAR endl = '\r';
658 cursor = info->editor->pCursors[0];
659 if (cursor.nOffset || cursor.pRun->member.run.nCharOfs)
660 ME_InsertTextFromCursor(info->editor, 0, &endl, 1, info->style);
661 tableDef->tableRowStart = ME_InsertTableRowStartFromCursor(info->editor);
662 }
663 info->nestingLevel = 1;
664 info->canInheritInTbl = TRUE;
665 }
666 return;
667 } else { /* v1.0 - v3.0 */
668 info->fmt.dwMask |= PFM_TABLE;
669 info->fmt.wEffects |= PFE_TABLE;
670 }
671 break;
672 }
673 case rtfFirstIndent:
674 case rtfLeftIndent:
675 if ((info->fmt.dwMask & (PFM_STARTINDENT | PFM_OFFSET)) != (PFM_STARTINDENT | PFM_OFFSET))
676 {
677 PARAFORMAT2 fmt;
678 fmt.cbSize = sizeof(fmt);
679 ME_GetSelectionParaFormat(info->editor, &fmt);
680 info->fmt.dwMask |= PFM_STARTINDENT | PFM_OFFSET;
681 info->fmt.dxStartIndent = fmt.dxStartIndent;
682 info->fmt.dxOffset = fmt.dxOffset;
683 }
684 if (info->rtfMinor == rtfFirstIndent)
685 {
686 info->fmt.dxStartIndent += info->fmt.dxOffset + info->rtfParam;
687 info->fmt.dxOffset = -info->rtfParam;
688 }
689 else
690 info->fmt.dxStartIndent = info->rtfParam - info->fmt.dxOffset;
691 break;
692 case rtfRightIndent:
693 info->fmt.dwMask |= PFM_RIGHTINDENT;
694 info->fmt.dxRightIndent = info->rtfParam;
695 break;
696 case rtfQuadLeft:
697 case rtfQuadJust:
698 info->fmt.dwMask |= PFM_ALIGNMENT;
699 info->fmt.wAlignment = PFA_LEFT;
700 break;
701 case rtfQuadRight:
702 info->fmt.dwMask |= PFM_ALIGNMENT;
703 info->fmt.wAlignment = PFA_RIGHT;
704 break;
705 case rtfQuadCenter:
706 info->fmt.dwMask |= PFM_ALIGNMENT;
707 info->fmt.wAlignment = PFA_CENTER;
708 break;
709 case rtfTabPos:
710 if (!(info->fmt.dwMask & PFM_TABSTOPS))
711 {
712 PARAFORMAT2 fmt;
713 fmt.cbSize = sizeof(fmt);
714 ME_GetSelectionParaFormat(info->editor, &fmt);
715 memcpy(info->fmt.rgxTabs, fmt.rgxTabs,
716 fmt.cTabCount * sizeof(fmt.rgxTabs[0]));
717 info->fmt.cTabCount = fmt.cTabCount;
718 info->fmt.dwMask |= PFM_TABSTOPS;
719 }
720 if (info->fmt.cTabCount < MAX_TAB_STOPS && info->rtfParam < 0x1000000)
721 info->fmt.rgxTabs[info->fmt.cTabCount++] = info->rtfParam;
722 break;
723 case rtfKeep:
724 info->fmt.dwMask |= PFM_KEEP;
725 info->fmt.wEffects |= PFE_KEEP;
726 break;
727 case rtfNoWidowControl:
728 info->fmt.dwMask |= PFM_NOWIDOWCONTROL;
729 info->fmt.wEffects |= PFE_NOWIDOWCONTROL;
730 break;
731 case rtfKeepNext:
732 info->fmt.dwMask |= PFM_KEEPNEXT;
733 info->fmt.wEffects |= PFE_KEEPNEXT;
734 break;
735 case rtfSpaceAfter:
736 info->fmt.dwMask |= PFM_SPACEAFTER;
737 info->fmt.dySpaceAfter = info->rtfParam;
738 break;
739 case rtfSpaceBefore:
740 info->fmt.dwMask |= PFM_SPACEBEFORE;
741 info->fmt.dySpaceBefore = info->rtfParam;
742 break;
743 case rtfSpaceBetween:
744 info->fmt.dwMask |= PFM_LINESPACING;
745 if ((int)info->rtfParam > 0)
746 {
747 info->fmt.dyLineSpacing = info->rtfParam;
748 info->fmt.bLineSpacingRule = 3;
749 }
750 else
751 {
752 info->fmt.dyLineSpacing = info->rtfParam;
753 info->fmt.bLineSpacingRule = 4;
754 }
755 break;
756 case rtfSpaceMultiply:
757 info->fmt.dwMask |= PFM_LINESPACING;
758 info->fmt.dyLineSpacing = info->rtfParam * 20;
759 info->fmt.bLineSpacingRule = 5;
760 break;
761 case rtfParBullet:
762 info->fmt.dwMask |= PFM_NUMBERING;
763 info->fmt.wNumbering = PFN_BULLET;
764 break;
765 case rtfParSimple:
766 info->fmt.dwMask |= PFM_NUMBERING;
767 info->fmt.wNumbering = 2; /* FIXME: MSDN says it's not used ?? */
768 break;
769 case rtfBorderLeft:
770 info->borderType = RTFBorderParaLeft;
771 info->fmt.wBorders |= 1;
772 info->fmt.dwMask |= PFM_BORDER;
773 break;
774 case rtfBorderRight:
775 info->borderType = RTFBorderParaRight;
776 info->fmt.wBorders |= 2;
777 info->fmt.dwMask |= PFM_BORDER;
778 break;
779 case rtfBorderTop:
780 info->borderType = RTFBorderParaTop;
781 info->fmt.wBorders |= 4;
782 info->fmt.dwMask |= PFM_BORDER;
783 break;
784 case rtfBorderBottom:
785 info->borderType = RTFBorderParaBottom;
786 info->fmt.wBorders |= 8;
787 info->fmt.dwMask |= PFM_BORDER;
788 break;
789 case rtfBorderSingle:
790 info->fmt.wBorders &= ~0x700;
791 info->fmt.wBorders |= 1 << 8;
792 info->fmt.dwMask |= PFM_BORDER;
793 break;
794 case rtfBorderThick:
795 info->fmt.wBorders &= ~0x700;
796 info->fmt.wBorders |= 2 << 8;
797 info->fmt.dwMask |= PFM_BORDER;
798 break;
799 case rtfBorderShadow:
800 info->fmt.wBorders &= ~0x700;
801 info->fmt.wBorders |= 10 << 8;
802 info->fmt.dwMask |= PFM_BORDER;
803 break;
804 case rtfBorderDouble:
805 info->fmt.wBorders &= ~0x700;
806 info->fmt.wBorders |= 7 << 8;
807 info->fmt.dwMask |= PFM_BORDER;
808 break;
809 case rtfBorderDot:
810 info->fmt.wBorders &= ~0x700;
811 info->fmt.wBorders |= 11 << 8;
812 info->fmt.dwMask |= PFM_BORDER;
813 break;
814 case rtfBorderWidth:
815 {
816 int borderSide = info->borderType & RTFBorderSideMask;
817 RTFTable *tableDef = info->tableDef;
818 if ((info->borderType & RTFBorderTypeMask) == RTFBorderTypeCell)
819 {
820 RTFBorder *border;
821 if (!tableDef || tableDef->numCellsDefined >= MAX_TABLE_CELLS)
822 break;
823 border = &tableDef->cells[tableDef->numCellsDefined].border[borderSide];
824 border->width = info->rtfParam;
825 break;
826 }
827 info->fmt.wBorderWidth = info->rtfParam;
828 info->fmt.dwMask |= PFM_BORDER;
829 break;
830 }
831 case rtfBorderSpace:
832 info->fmt.wBorderSpace = info->rtfParam;
833 info->fmt.dwMask |= PFM_BORDER;
834 break;
835 case rtfBorderColor:
836 {
837 RTFTable *tableDef = info->tableDef;
838 int borderSide = info->borderType & RTFBorderSideMask;
839 int borderType = info->borderType & RTFBorderTypeMask;
840 switch(borderType)
841 {
842 case RTFBorderTypePara:
843 if (!info->editor->bEmulateVersion10) /* v4.1 */
844 break;
845 /* v1.0 - 3.0 treat paragraph and row borders the same. */
846 case RTFBorderTypeRow:
847 if (tableDef) {
848 tableDef->border[borderSide].color = info->rtfParam;
849 }
850 break;
851 case RTFBorderTypeCell:
852 if (tableDef && tableDef->numCellsDefined < MAX_TABLE_CELLS) {
853 tableDef->cells[tableDef->numCellsDefined].border[borderSide].color = info->rtfParam;
854 }
855 break;
856 }
857 break;
858 }
859 case rtfRTLPar:
860 info->fmt.dwMask |= PFM_RTLPARA;
861 info->fmt.wEffects |= PFE_RTLPARA;
862 break;
863 case rtfLTRPar:
864 info->fmt.dwMask |= PFM_RTLPARA;
865 info->fmt.wEffects &= ~PFE_RTLPARA;
866 break;
867 }
868 }
869
870 void ME_RTFTblAttrHook(RTF_Info *info)
871 {
872 switch (info->rtfMinor)
873 {
874 case rtfRowDef:
875 {
876 if (!info->editor->bEmulateVersion10) /* v4.1 */
877 info->borderType = 0; /* Not sure */
878 else /* v1.0 - 3.0 */
879 info->borderType = RTFBorderRowTop;
880 if (!info->tableDef) {
881 info->tableDef = ME_MakeTableDef(info->editor);
882 } else {
883 ME_InitTableDef(info->editor, info->tableDef);
884 }
885 break;
886 }
887 case rtfCellPos:
888 {
889 int cellNum;
890 if (!info->tableDef)
891 {
892 info->tableDef = ME_MakeTableDef(info->editor);
893 }
894 cellNum = info->tableDef->numCellsDefined;
895 if (cellNum >= MAX_TABLE_CELLS)
896 break;
897 info->tableDef->cells[cellNum].rightBoundary = info->rtfParam;
898 if (cellNum < MAX_TAB_STOPS) {
899 /* Tab stops were used to store cell positions before v4.1 but v4.1
900 * still seems to set the tabstops without using them. */
901 ME_DisplayItem *para = info->editor->pCursors[0].pPara;
902 PARAFORMAT2 *pFmt = &para->member.para.fmt;
903 pFmt->rgxTabs[cellNum] &= ~0x00FFFFFF;
904 pFmt->rgxTabs[cellNum] |= 0x00FFFFFF & info->rtfParam;
905 }
906 info->tableDef->numCellsDefined++;
907 break;
908 }
909 case rtfRowBordTop:
910 info->borderType = RTFBorderRowTop;
911 break;
912 case rtfRowBordLeft:
913 info->borderType = RTFBorderRowLeft;
914 break;
915 case rtfRowBordBottom:
916 info->borderType = RTFBorderRowBottom;
917 break;
918 case rtfRowBordRight:
919 info->borderType = RTFBorderRowRight;
920 break;
921 case rtfCellBordTop:
922 info->borderType = RTFBorderCellTop;
923 break;
924 case rtfCellBordLeft:
925 info->borderType = RTFBorderCellLeft;
926 break;
927 case rtfCellBordBottom:
928 info->borderType = RTFBorderCellBottom;
929 break;
930 case rtfCellBordRight:
931 info->borderType = RTFBorderCellRight;
932 break;
933 case rtfRowGapH:
934 if (info->tableDef)
935 info->tableDef->gapH = info->rtfParam;
936 break;
937 case rtfRowLeftEdge:
938 if (info->tableDef)
939 info->tableDef->leftEdge = info->rtfParam;
940 break;
941 }
942 }
943
944 void ME_RTFSpecialCharHook(RTF_Info *info)
945 {
946 RTFTable *tableDef = info->tableDef;
947 switch (info->rtfMinor)
948 {
949 case rtfNestCell:
950 if (info->editor->bEmulateVersion10) /* v1.0 - v3.0 */
951 break;
952 /* else fall through since v4.1 treats rtfNestCell and rtfCell the same */
953 case rtfCell:
954 if (!tableDef)
955 break;
956 RTFFlushOutputBuffer(info);
957 if (!info->editor->bEmulateVersion10) { /* v4.1 */
958 if (tableDef->tableRowStart)
959 {
960 if (!info->nestingLevel &&
961 tableDef->tableRowStart->member.para.nFlags & MEPF_ROWEND)
962 {
963 ME_DisplayItem *para = tableDef->tableRowStart;
964 para = para->member.para.next_para;
965 para = ME_InsertTableRowStartAtParagraph(info->editor, para);
966 tableDef->tableRowStart = para;
967 info->nestingLevel = 1;
968 }
969 ME_InsertTableCellFromCursor(info->editor);
970 }
971 } else { /* v1.0 - v3.0 */
972 ME_DisplayItem *para = info->editor->pCursors[0].pPara;
973 PARAFORMAT2 *pFmt = &para->member.para.fmt;
974 if (pFmt->dwMask & PFM_TABLE && pFmt->wEffects & PFE_TABLE &&
975 tableDef->numCellsInserted < tableDef->numCellsDefined)
976 {
977 WCHAR tab = '\t';
978 ME_InsertTextFromCursor(info->editor, 0, &tab, 1, info->style);
979 tableDef->numCellsInserted++;
980 }
981 }
982 break;
983 case rtfNestRow:
984 if (info->editor->bEmulateVersion10) /* v1.0 - v3.0 */
985 break;
986 /* else fall through since v4.1 treats rtfNestRow and rtfRow the same */
987 case rtfRow:
988 {
989 ME_DisplayItem *para, *cell, *run;
990 int i;
991
992 if (!tableDef)
993 break;
994 RTFFlushOutputBuffer(info);
995 if (!info->editor->bEmulateVersion10) { /* v4.1 */
996 if (!tableDef->tableRowStart)
997 break;
998 if (!info->nestingLevel &&
999 tableDef->tableRowStart->member.para.nFlags & MEPF_ROWEND)
1000 {
1001 para = tableDef->tableRowStart;
1002 para = para->member.para.next_para;
1003 para = ME_InsertTableRowStartAtParagraph(info->editor, para);
1004 tableDef->tableRowStart = para;
1005 info->nestingLevel++;
1006 }
1007 para = tableDef->tableRowStart;
1008 cell = ME_FindItemFwd(para, diCell);
1009 assert(cell && !cell->member.cell.prev_cell);
1010 if (tableDef->numCellsDefined < 1)
1011 {
1012 /* 2000 twips appears to be the cell size that native richedit uses
1013 * when no cell sizes are specified. */
1014 const int defaultCellSize = 2000;
1015 int nRightBoundary = defaultCellSize;
1016 cell->member.cell.nRightBoundary = nRightBoundary;
1017 while (cell->member.cell.next_cell) {
1018 cell = cell->member.cell.next_cell;
1019 nRightBoundary += defaultCellSize;
1020 cell->member.cell.nRightBoundary = nRightBoundary;
1021 }
1022 para = ME_InsertTableCellFromCursor(info->editor);
1023 cell = para->member.para.pCell;
1024 cell->member.cell.nRightBoundary = nRightBoundary;
1025 } else {
1026 for (i = 0; i < tableDef->numCellsDefined; i++)
1027 {
1028 RTFCell *cellDef = &tableDef->cells[i];
1029 cell->member.cell.nRightBoundary = cellDef->rightBoundary;
1030 ME_ApplyBorderProperties(info, &cell->member.cell.border,
1031 cellDef->border);
1032 cell = cell->member.cell.next_cell;
1033 if (!cell)
1034 {
1035 para = ME_InsertTableCellFromCursor(info->editor);
1036 cell = para->member.para.pCell;
1037 }
1038 }
1039 /* Cell for table row delimiter is empty */
1040 cell->member.cell.nRightBoundary = tableDef->cells[i-1].rightBoundary;
1041 }
1042
1043 run = ME_FindItemFwd(cell, diRun);
1044 if (info->editor->pCursors[0].pRun != run ||
1045 info->editor->pCursors[0].nOffset)
1046 {
1047 int nOfs, nChars;
1048 /* Delete inserted cells that aren't defined. */
1049 info->editor->pCursors[1].pRun = run;
1050 info->editor->pCursors[1].pPara = ME_GetParagraph(run);
1051 info->editor->pCursors[1].nOffset = 0;
1052 nOfs = ME_GetCursorOfs(&info->editor->pCursors[1]);
1053 nChars = ME_GetCursorOfs(&info->editor->pCursors[0]) - nOfs;
1054 ME_InternalDeleteText(info->editor, &info->editor->pCursors[1],
1055 nChars, TRUE);
1056 }
1057
1058 para = ME_InsertTableRowEndFromCursor(info->editor);
1059 para->member.para.fmt.dxOffset = abs(info->tableDef->gapH);
1060 para->member.para.fmt.dxStartIndent = info->tableDef->leftEdge;
1061 ME_ApplyBorderProperties(info, &para->member.para.border,
1062 tableDef->border);
1063 info->nestingLevel--;
1064 if (!info->nestingLevel)
1065 {
1066 if (info->canInheritInTbl) {
1067 tableDef->tableRowStart = para;
1068 } else {
1069 while (info->tableDef) {
1070 tableDef = info->tableDef;
1071 info->tableDef = tableDef->parent;
1072 heap_free(tableDef);
1073 }
1074 }
1075 } else {
1076 info->tableDef = tableDef->parent;
1077 heap_free(tableDef);
1078 }
1079 } else { /* v1.0 - v3.0 */
1080 WCHAR endl = '\r';
1081 ME_DisplayItem *para = info->editor->pCursors[0].pPara;
1082 PARAFORMAT2 *pFmt = &para->member.para.fmt;
1083 pFmt->dxOffset = info->tableDef->gapH;
1084 pFmt->dxStartIndent = info->tableDef->leftEdge;
1085
1086 ME_ApplyBorderProperties(info, &para->member.para.border,
1087 tableDef->border);
1088 while (tableDef->numCellsInserted < tableDef->numCellsDefined)
1089 {
1090 WCHAR tab = '\t';
1091 ME_InsertTextFromCursor(info->editor, 0, &tab, 1, info->style);
1092 tableDef->numCellsInserted++;
1093 }
1094 pFmt->cTabCount = min(tableDef->numCellsDefined, MAX_TAB_STOPS);
1095 if (!tableDef->numCellsDefined)
1096 pFmt->wEffects &= ~PFE_TABLE;
1097 ME_InsertTextFromCursor(info->editor, 0, &endl, 1, info->style);
1098 tableDef->numCellsInserted = 0;
1099 }
1100 break;
1101 }
1102 case rtfTab:
1103 case rtfPar:
1104 if (info->editor->bEmulateVersion10) { /* v1.0 - 3.0 */
1105 ME_DisplayItem *para;
1106 PARAFORMAT2 *pFmt;
1107 RTFFlushOutputBuffer(info);
1108 para = info->editor->pCursors[0].pPara;
1109 pFmt = &para->member.para.fmt;
1110 if (pFmt->dwMask & PFM_TABLE && pFmt->wEffects & PFE_TABLE)
1111 {
1112 /* rtfPar is treated like a space within a table. */
1113 info->rtfClass = rtfText;
1114 info->rtfMajor = ' ';
1115 }
1116 else if (info->rtfMinor == rtfPar && tableDef)
1117 tableDef->numCellsInserted = 0;
1118 }
1119 break;
1120 }
1121 }
1122
1123 static HRESULT insert_static_object(ME_TextEditor *editor, HENHMETAFILE hemf, HBITMAP hbmp,
1124 const SIZEL* sz)
1125 {
1126 LPOLEOBJECT lpObject = NULL;
1127 LPSTORAGE lpStorage = NULL;
1128 LPOLECLIENTSITE lpClientSite = NULL;
1129 LPDATAOBJECT lpDataObject = NULL;
1130 LPOLECACHE lpOleCache = NULL;
1131 STGMEDIUM stgm;
1132 FORMATETC fm;
1133 CLSID clsid;
1134 HRESULT hr = E_FAIL;
1135 DWORD conn;
1136
1137 if (hemf)
1138 {
1139 stgm.tymed = TYMED_ENHMF;
1140 stgm.u.hEnhMetaFile = hemf;
1141 fm.cfFormat = CF_ENHMETAFILE;
1142 }
1143 else if (hbmp)
1144 {
1145 stgm.tymed = TYMED_GDI;
1146 stgm.u.hBitmap = hbmp;
1147 fm.cfFormat = CF_BITMAP;
1148 }
1149 stgm.pUnkForRelease = NULL;
1150
1151 fm.ptd = NULL;
1152 fm.dwAspect = DVASPECT_CONTENT;
1153 fm.lindex = -1;
1154 fm.tymed = stgm.tymed;
1155
1156 if (!editor->reOle)
1157 {
1158 if (!CreateIRichEditOle(NULL, editor, (LPVOID *)&editor->reOle))
1159 return hr;
1160 }
1161
1162 if (OleCreateDefaultHandler(&CLSID_NULL, NULL, &IID_IOleObject, (void**)&lpObject) == S_OK &&
1163 IRichEditOle_GetClientSite(editor->reOle, &lpClientSite) == S_OK &&
1164 IOleObject_SetClientSite(lpObject, lpClientSite) == S_OK &&
1165 IOleObject_GetUserClassID(lpObject, &clsid) == S_OK &&
1166 IOleObject_QueryInterface(lpObject, &IID_IOleCache, (void**)&lpOleCache) == S_OK &&
1167 IOleCache_Cache(lpOleCache, &fm, 0, &conn) == S_OK &&
1168 IOleObject_QueryInterface(lpObject, &IID_IDataObject, (void**)&lpDataObject) == S_OK &&
1169 IDataObject_SetData(lpDataObject, &fm, &stgm, TRUE) == S_OK)
1170 {
1171 REOBJECT reobject;
1172
1173 reobject.cbStruct = sizeof(reobject);
1174 reobject.cp = REO_CP_SELECTION;
1175 reobject.clsid = clsid;
1176 reobject.poleobj = lpObject;
1177 reobject.pstg = lpStorage;
1178 reobject.polesite = lpClientSite;
1179 /* convert from twips to .01 mm */
1180 reobject.sizel.cx = MulDiv(sz->cx, 254, 144);
1181 reobject.sizel.cy = MulDiv(sz->cy, 254, 144);
1182 reobject.dvaspect = DVASPECT_CONTENT;
1183 reobject.dwFlags = 0; /* FIXME */
1184 reobject.dwUser = 0;
1185
1186 ME_InsertOLEFromCursor(editor, &reobject, 0);
1187 hr = S_OK;
1188 }
1189
1190 if (lpObject) IOleObject_Release(lpObject);
1191 if (lpClientSite) IOleClientSite_Release(lpClientSite);
1192 if (lpStorage) IStorage_Release(lpStorage);
1193 if (lpDataObject) IDataObject_Release(lpDataObject);
1194 if (lpOleCache) IOleCache_Release(lpOleCache);
1195
1196 return hr;
1197 }
1198
1199 static void ME_RTFReadShpPictGroup( RTF_Info *info )
1200 {
1201 int level = 1;
1202
1203 for (;;)
1204 {
1205 RTFGetToken (info);
1206
1207 if (info->rtfClass == rtfEOF) return;
1208 if (RTFCheckCM( info, rtfGroup, rtfEndGroup ))
1209 {
1210 if (--level == 0) break;
1211 }
1212 else if (RTFCheckCM( info, rtfGroup, rtfBeginGroup ))
1213 {
1214 level++;
1215 }
1216 else
1217 {
1218 RTFRouteToken( info );
1219 if (RTFCheckCM( info, rtfGroup, rtfEndGroup ))
1220 level--;
1221 }
1222 }
1223
1224 RTFRouteToken( info ); /* feed "}" back to router */
1225 return;
1226 }
1227
1228 static DWORD read_hex_data( RTF_Info *info, BYTE **out )
1229 {
1230 DWORD read = 0, size = 1024;
1231 BYTE *buf, val;
1232 BOOL flip;
1233
1234 *out = NULL;
1235
1236 if (info->rtfClass != rtfText)
1237 {
1238 ERR("Called with incorrect token\n");
1239 return 0;
1240 }
1241
1242 buf = HeapAlloc( GetProcessHeap(), 0, size );
1243 if (!buf) return 0;
1244
1245 val = info->rtfMajor;
1246 for (flip = TRUE;; flip = !flip)
1247 {
1248 RTFGetToken( info );
1249 if (info->rtfClass == rtfEOF)
1250 {
1251 HeapFree( GetProcessHeap(), 0, buf );
1252 return 0;
1253 }
1254 if (info->rtfClass != rtfText) break;
1255 if (flip)
1256 {
1257 if (read >= size)
1258 {
1259 size *= 2;
1260 buf = HeapReAlloc( GetProcessHeap(), 0, buf, size );
1261 if (!buf) return 0;
1262 }
1263 buf[read++] = RTFCharToHex(val) * 16 + RTFCharToHex(info->rtfMajor);
1264 }
1265 else
1266 val = info->rtfMajor;
1267 }
1268 if (flip) FIXME("wrong hex string\n");
1269
1270 *out = buf;
1271 return read;
1272 }
1273
1274 static void ME_RTFReadPictGroup(RTF_Info *info)
1275 {
1276 SIZEL sz;
1277 BYTE *buffer = NULL;
1278 DWORD size = 0;
1279 METAFILEPICT mfp;
1280 HENHMETAFILE hemf;
1281 HBITMAP hbmp;
1282 enum gfxkind {gfx_unknown = 0, gfx_enhmetafile, gfx_metafile, gfx_dib} gfx = gfx_unknown;
1283 int level = 1;
1284
1285 mfp.mm = MM_TEXT;
1286 sz.cx = sz.cy = 0;
1287
1288 for (;;)
1289 {
1290 RTFGetToken( info );
1291
1292 if (info->rtfClass == rtfText)
1293 {
1294 if (level == 1)
1295 {
1296 if (!buffer)
1297 size = read_hex_data( info, &buffer );
1298 }
1299 else
1300 {
1301 RTFSkipGroup( info );
1302 }
1303 } /* We potentially have a new token so fall through. */
1304
1305 if (info->rtfClass == rtfEOF) return;
1306
1307 if (RTFCheckCM( info, rtfGroup, rtfEndGroup ))
1308 {
1309 if (--level == 0) break;
1310 continue;
1311 }
1312 if (RTFCheckCM( info, rtfGroup, rtfBeginGroup ))
1313 {
1314 level++;
1315 continue;
1316 }
1317 if (!RTFCheckCM( info, rtfControl, rtfPictAttr ))
1318 {
1319 RTFRouteToken( info );
1320 if (RTFCheckCM( info, rtfGroup, rtfEndGroup ))
1321 level--;
1322 continue;
1323 }
1324
1325 if (RTFCheckMM( info, rtfPictAttr, rtfWinMetafile ))
1326 {
1327 mfp.mm = info->rtfParam;
1328 gfx = gfx_metafile;
1329 }
1330 else if (RTFCheckMM( info, rtfPictAttr, rtfDevIndBitmap ))
1331 {
1332 if (info->rtfParam != 0) FIXME("dibitmap should be 0 (%d)\n", info->rtfParam);
1333 gfx = gfx_dib;
1334 }
1335 else if (RTFCheckMM( info, rtfPictAttr, rtfEmfBlip ))
1336 gfx = gfx_enhmetafile;
1337 else if (RTFCheckMM( info, rtfPictAttr, rtfPicWid ))
1338 mfp.xExt = info->rtfParam;
1339 else if (RTFCheckMM( info, rtfPictAttr, rtfPicHt ))
1340 mfp.yExt = info->rtfParam;
1341 else if (RTFCheckMM( info, rtfPictAttr, rtfPicGoalWid ))
1342 sz.cx = info->rtfParam;
1343 else if (RTFCheckMM( info, rtfPictAttr, rtfPicGoalHt ))
1344 sz.cy = info->rtfParam;
1345 else
1346 FIXME("Non supported attribute: %d %d %d\n", info->rtfClass, info->rtfMajor, info->rtfMinor);
1347 }
1348
1349 if (buffer)
1350 {
1351 switch (gfx)
1352 {
1353 case gfx_enhmetafile:
1354 if ((hemf = SetEnhMetaFileBits( size, buffer )))
1355 insert_static_object( info->editor, hemf, NULL, &sz );
1356 break;
1357 case gfx_metafile:
1358 if ((hemf = SetWinMetaFileBits( size, buffer, NULL, &mfp )))
1359 insert_static_object( info->editor, hemf, NULL, &sz );
1360 break;
1361 case gfx_dib:
1362 {
1363 BITMAPINFO *bi = (BITMAPINFO*)buffer;
1364 HDC hdc = GetDC(0);
1365 unsigned nc = bi->bmiHeader.biClrUsed;
1366
1367 /* not quite right, especially for bitfields type of compression */
1368 if (!nc && bi->bmiHeader.biBitCount <= 8)
1369 nc = 1 << bi->bmiHeader.biBitCount;
1370 if ((hbmp = CreateDIBitmap( hdc, &bi->bmiHeader,
1371 CBM_INIT, (char*)(bi + 1) + nc * sizeof(RGBQUAD),
1372 bi, DIB_RGB_COLORS)) )
1373 insert_static_object( info->editor, NULL, hbmp, &sz );
1374 ReleaseDC( 0, hdc );
1375 break;
1376 }
1377 default:
1378 break;
1379 }
1380 }
1381 HeapFree( GetProcessHeap(), 0, buffer );
1382 RTFRouteToken( info ); /* feed "}" back to router */
1383 return;
1384 }
1385
1386 /* for now, lookup the \result part and use it, whatever the object */
1387 static void ME_RTFReadObjectGroup(RTF_Info *info)
1388 {
1389 for (;;)
1390 {
1391 RTFGetToken (info);
1392 if (info->rtfClass == rtfEOF)
1393 return;
1394 if (RTFCheckCM(info, rtfGroup, rtfEndGroup))
1395 break;
1396 if (RTFCheckCM(info, rtfGroup, rtfBeginGroup))
1397 {
1398 RTFGetToken (info);
1399 if (info->rtfClass == rtfEOF)
1400 return;
1401 if (RTFCheckCMM(info, rtfControl, rtfDestination, rtfObjResult))
1402 {
1403 int level = 1;
1404
1405 while (RTFGetToken (info) != rtfEOF)
1406 {
1407 if (info->rtfClass == rtfGroup)
1408 {
1409 if (info->rtfMajor == rtfBeginGroup) level++;
1410 else if (info->rtfMajor == rtfEndGroup && --level < 0) break;
1411 }
1412 RTFRouteToken(info);
1413 }
1414 }
1415 else RTFSkipGroup(info);
1416 continue;
1417 }
1418 if (!RTFCheckCM (info, rtfControl, rtfObjAttr))
1419 {
1420 FIXME("Non supported attribute: %d %d %d\n", info->rtfClass, info->rtfMajor, info->rtfMinor);
1421 return;
1422 }
1423 }
1424 RTFRouteToken(info); /* feed "}" back to router */
1425 }
1426
1427 static void ME_RTFReadParnumGroup( RTF_Info *info )
1428 {
1429 int level = 1, type = -1;
1430 WORD indent = 0, start = 1;
1431 WCHAR txt_before = 0, txt_after = 0;
1432
1433 for (;;)
1434 {
1435 RTFGetToken( info );
1436
1437 if (RTFCheckCMM( info, rtfControl, rtfDestination, rtfParNumTextBefore ) ||
1438 RTFCheckCMM( info, rtfControl, rtfDestination, rtfParNumTextAfter ))
1439 {
1440 int loc = info->rtfMinor;
1441
1442 RTFGetToken( info );
1443 if (info->rtfClass == rtfText)
1444 {
1445 if (loc == rtfParNumTextBefore)
1446 txt_before = info->rtfMajor;
1447 else
1448 txt_after = info->rtfMajor;
1449 continue;
1450 }
1451 /* falling through to catch EOFs and group level changes */
1452 }
1453
1454 if (info->rtfClass == rtfEOF)
1455 return;
1456
1457 if (RTFCheckCM( info, rtfGroup, rtfEndGroup ))
1458 {
1459 if (--level == 0) break;
1460 continue;
1461 }
1462
1463 if (RTFCheckCM( info, rtfGroup, rtfBeginGroup ))
1464 {
1465 level++;
1466 continue;
1467 }
1468
1469 /* Ignore non para-attr */
1470 if (!RTFCheckCM( info, rtfControl, rtfParAttr ))
1471 continue;
1472
1473 switch (info->rtfMinor)
1474 {
1475 case rtfParLevel: /* Para level is ignored */
1476 case rtfParSimple:
1477 break;
1478 case rtfParBullet:
1479 type = PFN_BULLET;
1480 break;
1481
1482 case rtfParNumDecimal:
1483 type = PFN_ARABIC;
1484 break;
1485 case rtfParNumULetter:
1486 type = PFN_UCLETTER;
1487 break;
1488 case rtfParNumURoman:
1489 type = PFN_UCROMAN;
1490 break;
1491 case rtfParNumLLetter:
1492 type = PFN_LCLETTER;
1493 break;
1494 case rtfParNumLRoman:
1495 type = PFN_LCROMAN;
1496 break;
1497
1498 case rtfParNumIndent:
1499 indent = info->rtfParam;
1500 break;
1501 case rtfParNumStartAt:
1502 start = info->rtfParam;
1503 break;
1504 }
1505 }
1506
1507 if (type != -1)
1508 {
1509 info->fmt.dwMask |= (PFM_NUMBERING | PFM_NUMBERINGSTART | PFM_NUMBERINGSTYLE | PFM_NUMBERINGTAB);
1510 info->fmt.wNumbering = type;
1511 info->fmt.wNumberingStart = start;
1512 info->fmt.wNumberingStyle = PFNS_PAREN;
1513 if (type != PFN_BULLET)
1514 {
1515 if (txt_before == 0 && txt_after == 0)
1516 info->fmt.wNumberingStyle = PFNS_PLAIN;
1517 else if (txt_after == '.')
1518 info->fmt.wNumberingStyle = PFNS_PERIOD;
1519 else if (txt_before == '(' && txt_after == ')')
1520 info->fmt.wNumberingStyle = PFNS_PARENS;
1521 }
1522 info->fmt.wNumberingTab = indent;
1523 }
1524
1525 TRACE("type %d indent %d start %d txt before %04x txt after %04x\n",
1526 type, indent, start, txt_before, txt_after);
1527
1528 RTFRouteToken( info ); /* feed "}" back to router */
1529 }
1530
1531 static void ME_RTFReadHook(RTF_Info *info)
1532 {
1533 switch(info->rtfClass)
1534 {
1535 case rtfGroup:
1536 switch(info->rtfMajor)
1537 {
1538 case rtfBeginGroup:
1539 if (info->stackTop < maxStack) {
1540 info->stack[info->stackTop].style = info->style;
1541 ME_AddRefStyle(info->style);
1542 info->stack[info->stackTop].codePage = info->codePage;
1543 info->stack[info->stackTop].unicodeLength = info->unicodeLength;
1544 }
1545 info->stackTop++;
1546 info->styleChanged = FALSE;
1547 break;
1548 case rtfEndGroup:
1549 {
1550 RTFFlushOutputBuffer(info);
1551 info->stackTop--;
1552 if (info->stackTop <= 0)
1553 info->rtfClass = rtfEOF;
1554 if (info->stackTop < 0)
1555 return;
1556
1557 ME_ReleaseStyle(info->style);
1558 info->style = info->stack[info->stackTop].style;
1559 info->codePage = info->stack[info->stackTop].codePage;
1560 info->unicodeLength = info->stack[info->stackTop].unicodeLength;
1561 break;
1562 }
1563 }
1564 break;
1565 }
1566 }
1567
1568 void
1569 ME_StreamInFill(ME_InStream *stream)
1570 {
1571 stream->editstream->dwError = stream->editstream->pfnCallback(stream->editstream->dwCookie,
1572 (BYTE *)stream->buffer,
1573 sizeof(stream->buffer),
1574 (LONG *)&stream->dwSize);
1575 stream->dwUsed = 0;
1576 }
1577
1578 static LRESULT ME_StreamIn(ME_TextEditor *editor, DWORD format, EDITSTREAM *stream, BOOL stripLastCR)
1579 {
1580 RTF_Info parser;
1581 ME_Style *style;
1582 int from, to, nUndoMode;
1583 int nEventMask = editor->nEventMask;
1584 ME_InStream inStream;
1585 BOOL invalidRTF = FALSE;
1586 ME_Cursor *selStart, *selEnd;
1587 LRESULT num_read = 0; /* bytes read for SF_TEXT, non-control chars inserted for SF_RTF */
1588
1589 TRACE("stream==%p editor==%p format==0x%X\n", stream, editor, format);
1590 editor->nEventMask = 0;
1591
1592 ME_GetSelectionOfs(editor, &from, &to);
1593 if (format & SFF_SELECTION && editor->mode & TM_RICHTEXT)
1594 {
1595 ME_GetSelection(editor, &selStart, &selEnd);
1596 style = ME_GetSelectionInsertStyle(editor);
1597
1598 ME_InternalDeleteText(editor, selStart, to - from, FALSE);
1599
1600 /* Don't insert text at the end of the table row */
1601 if (!editor->bEmulateVersion10) { /* v4.1 */
1602 ME_DisplayItem *para = editor->pCursors->pPara;
1603 if (para->member.para.nFlags & MEPF_ROWEND)
1604 {
1605 para = para->member.para.next_para;
1606 editor->pCursors[0].pPara = para;
1607 editor->pCursors[0].pRun = ME_FindItemFwd(para, diRun);
1608 editor->pCursors[0].nOffset = 0;
1609 }
1610 if (para->member.para.nFlags & MEPF_ROWSTART)
1611 {
1612 para = para->member.para.next_para;
1613 editor->pCursors[0].pPara = para;
1614 editor->pCursors[0].pRun = ME_FindItemFwd(para, diRun);
1615 editor->pCursors[0].nOffset = 0;
1616 }
1617 editor->pCursors[1] = editor->pCursors[0];
1618 } else { /* v1.0 - 3.0 */
1619 if (editor->pCursors[0].pRun->member.run.nFlags & MERF_ENDPARA &&
1620 ME_IsInTable(editor->pCursors[0].pRun))
1621 return 0;
1622 }
1623 } else {
1624 style = editor->pBuffer->pDefaultStyle;
1625 ME_AddRefStyle(style);
1626 ME_SetSelection(editor, 0, 0);
1627 ME_InternalDeleteText(editor, &editor->pCursors[1],
1628 ME_GetTextLength(editor), FALSE);
1629 from = to = 0;
1630 ME_ClearTempStyle(editor);
1631 ME_SetDefaultParaFormat(editor, &editor->pCursors[0].pPara->member.para.fmt);
1632 }
1633
1634
1635 /* Back up undo mode to a local variable */
1636 nUndoMode = editor->nUndoMode;
1637
1638 /* Only create an undo if SFF_SELECTION is set */
1639 if (!(format & SFF_SELECTION))
1640 editor->nUndoMode = umIgnore;
1641
1642 inStream.editstream = stream;
1643 inStream.editstream->dwError = 0;
1644 inStream.dwSize = 0;
1645 inStream.dwUsed = 0;
1646
1647 if (format & SF_RTF)
1648 {
1649 /* Check if it's really RTF, and if it is not, use plain text */
1650 ME_StreamInFill(&inStream);
1651 if (!inStream.editstream->dwError)
1652 {
1653 if ((!editor->bEmulateVersion10 && strncmp(inStream.buffer, "{\\rtf", 5) && strncmp(inStream.buffer, "{\\urtf", 6))
1654 || (editor->bEmulateVersion10 && *inStream.buffer != '{'))
1655 {
1656 invalidRTF = TRUE;
1657 inStream.editstream->dwError = -16;
1658 }
1659 }
1660 }
1661
1662 if (!invalidRTF && !inStream.editstream->dwError)
1663 {
1664 ME_Cursor start;
1665 from = ME_GetCursorOfs(&editor->pCursors[0]);
1666 if (format & SF_RTF) {
1667
1668 /* setup the RTF parser */
1669 memset(&parser, 0, sizeof parser);
1670 RTFSetEditStream(&parser, &inStream);
1671 parser.rtfFormat = format&(SF_TEXT|SF_RTF);
1672 parser.editor = editor;
1673 parser.style = style;
1674 WriterInit(&parser);
1675 RTFInit(&parser);
1676 RTFSetReadHook(&parser, ME_RTFReadHook);
1677 RTFSetDestinationCallback(&parser, rtfShpPict, ME_RTFReadShpPictGroup);
1678 RTFSetDestinationCallback(&parser, rtfPict, ME_RTFReadPictGroup);
1679 RTFSetDestinationCallback(&parser, rtfObject, ME_RTFReadObjectGroup);
1680 RTFSetDestinationCallback(&parser, rtfParNumbering, ME_RTFReadParnumGroup);
1681 if (!parser.editor->bEmulateVersion10) /* v4.1 */
1682 {
1683 RTFSetDestinationCallback(&parser, rtfNoNestTables, RTFSkipGroup);
1684 RTFSetDestinationCallback(&parser, rtfNestTableProps, RTFReadGroup);
1685 }
1686 BeginFile(&parser);
1687
1688 /* do the parsing */
1689 RTFRead(&parser);
1690 RTFFlushOutputBuffer(&parser);
1691 if (!editor->bEmulateVersion10) { /* v4.1 */
1692 if (parser.tableDef && parser.tableDef->tableRowStart &&
1693 (parser.nestingLevel > 0 || parser.canInheritInTbl))
1694 {
1695 /* Delete any incomplete table row at the end of the rich text. */
1696 int nOfs, nChars;
1697 ME_DisplayItem *para;
1698
1699 parser.rtfMinor = rtfRow;
1700 /* Complete the table row before deleting it.
1701 * By doing it this way we will have the current paragraph format set
1702 * properly to reflect that is not in the complete table, and undo items
1703 * will be added for this change to the current paragraph format. */
1704 if (parser.nestingLevel > 0)
1705 {
1706 while (parser.nestingLevel > 1)
1707 ME_RTFSpecialCharHook(&parser); /* Decrements nestingLevel */
1708 para = parser.tableDef->tableRowStart;
1709 ME_RTFSpecialCharHook(&parser);
1710 } else {
1711 para = parser.tableDef->tableRowStart;
1712 ME_RTFSpecialCharHook(&parser);
1713 assert(para->member.para.nFlags & MEPF_ROWEND);
1714 para = para->member.para.next_para;
1715 }
1716
1717 editor->pCursors[1].pPara = para;
1718 editor->pCursors[1].pRun = ME_FindItemFwd(para, diRun);
1719 editor->pCursors[1].nOffset = 0;
1720 nOfs = ME_GetCursorOfs(&editor->pCursors[1]);
1721 nChars = ME_GetCursorOfs(&editor->pCursors[0]) - nOfs;
1722 ME_InternalDeleteText(editor, &editor->pCursors[1], nChars, TRUE);
1723 if (parser.tableDef)
1724 parser.tableDef->tableRowStart = NULL;
1725 }
1726 }
1727 ME_CheckTablesForCorruption(editor);
1728 RTFDestroy(&parser);
1729
1730 if (parser.stackTop > 0)
1731 {
1732 while (--parser.stackTop >= 0)
1733 {
1734 ME_ReleaseStyle(parser.style);
1735 parser.style = parser.stack[parser.stackTop].style;
1736 }
1737 if (!inStream.editstream->dwError)
1738 inStream.editstream->dwError = HRESULT_FROM_WIN32(ERROR_HANDLE_EOF);
1739 }
1740
1741 /* Remove last line break, as mandated by tests. This is not affected by
1742 CR/LF counters, since RTF streaming presents only \para tokens, which
1743 are converted according to the standard rules: \r for 2.0, \r\n for 1.0
1744 */
1745 if (stripLastCR && !(format & SFF_SELECTION)) {
1746 int newto;
1747 ME_GetSelection(editor, &selStart, &selEnd);
1748 newto = ME_GetCursorOfs(selEnd);
1749 if (newto > to + (editor->bEmulateVersion10 ? 1 : 0)) {
1750 WCHAR lastchar[3] = {'\0', '\0'};
1751 int linebreakSize = editor->bEmulateVersion10 ? 2 : 1;
1752 ME_Cursor linebreakCursor = *selEnd, lastcharCursor = *selEnd;
1753 CHARFORMAT2W cf;
1754
1755 /* Set the final eop to the char fmt of the last char */
1756 cf.cbSize = sizeof(cf);
1757 cf.dwMask = CFM_ALL2;
1758 ME_MoveCursorChars(editor, &lastcharCursor, -1, FALSE);
1759 ME_GetCharFormat(editor, &lastcharCursor, &linebreakCursor, &cf);
1760 ME_SetSelection(editor, newto, -1);
1761 ME_SetSelectionCharFormat(editor, &cf);
1762 ME_SetSelection(editor, newto, newto);
1763
1764 ME_MoveCursorChars(editor, &linebreakCursor, -linebreakSize, FALSE);
1765 ME_GetTextW(editor, lastchar, 2, &linebreakCursor, linebreakSize, FALSE, FALSE);
1766 if (lastchar[0] == '\r' && (lastchar[1] == '\n' || lastchar[1] == '\0')) {
1767 ME_InternalDeleteText(editor, &linebreakCursor, linebreakSize, FALSE);
1768 }
1769 }
1770 }
1771 to = ME_GetCursorOfs(&editor->pCursors[0]);
1772 num_read = to - from;
1773
1774 style = parser.style;
1775 }
1776 else if (format & SF_TEXT)
1777 {
1778 num_read = ME_StreamInText(editor, format, &inStream, style);
1779 to = ME_GetCursorOfs(&editor->pCursors[0]);
1780 }
1781 else
1782 ERR("EM_STREAMIN without SF_TEXT or SF_RTF\n");
1783 /* put the cursor at the top */
1784 if (!(format & SFF_SELECTION))
1785 ME_SetSelection(editor, 0, 0);
1786 ME_CursorFromCharOfs(editor, from, &start);
1787 ME_UpdateLinkAttribute(editor, &start, to - from);
1788 }
1789
1790 /* Restore saved undo mode */
1791 editor->nUndoMode = nUndoMode;
1792
1793 /* even if we didn't add an undo, we need to commit anything on the stack */
1794 ME_CommitUndo(editor);
1795
1796 /* If SFF_SELECTION isn't set, delete any undos from before we started too */
1797 if (!(format & SFF_SELECTION))
1798 ME_EmptyUndoStack(editor);
1799
1800 ME_ReleaseStyle(style);
1801 editor->nEventMask = nEventMask;
1802 ME_UpdateRepaint(editor, FALSE);
1803 if (!(format & SFF_SELECTION)) {
1804 ME_ClearTempStyle(editor);
1805 }
1806 ITextHost_TxShowCaret(editor->texthost, FALSE);
1807 ME_MoveCaret(editor);
1808 ITextHost_TxShowCaret(editor->texthost, TRUE);
1809 ME_SendSelChange(editor);
1810 ME_SendRequestResize(editor, FALSE);
1811
1812 return num_read;
1813 }
1814
1815
1816 typedef struct tagME_RTFStringStreamStruct
1817 {
1818 char *string;
1819 int pos;
1820 int length;
1821 } ME_RTFStringStreamStruct;
1822
1823 static DWORD CALLBACK ME_ReadFromRTFString(DWORD_PTR dwCookie, LPBYTE lpBuff, LONG cb, LONG *pcb)
1824 {
1825 ME_RTFStringStreamStruct *pStruct = (ME_RTFStringStreamStruct *)dwCookie;
1826 int count;
1827
1828 count = min(cb, pStruct->length - pStruct->pos);
1829 memmove(lpBuff, pStruct->string + pStruct->pos, count);
1830 pStruct->pos += count;
1831 *pcb = count;
1832 return 0;
1833 }
1834
1835 static void
1836 ME_StreamInRTFString(ME_TextEditor *editor, BOOL selection, char *string)
1837 {
1838 EDITSTREAM es;
1839 ME_RTFStringStreamStruct data;
1840
1841 data.string = string;
1842 data.length = strlen(string);
1843 data.pos = 0;
1844 es.dwCookie = (DWORD_PTR)&data;
1845 es.pfnCallback = ME_ReadFromRTFString;
1846 ME_StreamIn(editor, SF_RTF | (selection ? SFF_SELECTION : 0), &es, TRUE);
1847 }
1848
1849
1850 static int
1851 ME_FindText(ME_TextEditor *editor, DWORD flags, const CHARRANGE *chrg, const WCHAR *text, CHARRANGE *chrgText)
1852 {
1853 const int nLen = lstrlenW(text);
1854 const int nTextLen = ME_GetTextLength(editor);
1855 int nMin, nMax;
1856 ME_Cursor cursor;
1857 WCHAR wLastChar = ' ';
1858
1859 TRACE("flags==0x%08x, chrg->cpMin==%d, chrg->cpMax==%d text==%s\n",
1860 flags, chrg->cpMin, chrg->cpMax, debugstr_w(text));
1861
1862 if (flags & ~(FR_DOWN | FR_MATCHCASE | FR_WHOLEWORD))
1863 FIXME("Flags 0x%08x not implemented\n",
1864 flags & ~(FR_DOWN | FR_MATCHCASE | FR_WHOLEWORD));
1865
1866 nMin = chrg->cpMin;
1867 if (chrg->cpMax == -1)
1868 nMax = nTextLen;
1869 else
1870 nMax = chrg->cpMax > nTextLen ? nTextLen : chrg->cpMax;
1871
1872 /* In 1.0 emulation, if cpMax reaches end of text, add the FR_DOWN flag */
1873 if (editor->bEmulateVersion10 && nMax == nTextLen)
1874 {
1875 flags |= FR_DOWN;
1876 }
1877
1878 /* In 1.0 emulation, cpMin must always be no greater than cpMax */
1879 if (editor->bEmulateVersion10 && nMax < nMin)
1880 {
1881 if (chrgText)
1882 {
1883 chrgText->cpMin = -1;
1884 chrgText->cpMax = -1;
1885 }
1886 return -1;
1887 }
1888
1889 /* when searching up, if cpMin < cpMax, then instead of searching
1890 * on [cpMin,cpMax], we search on [0,cpMin], otherwise, search on
1891 * [cpMax, cpMin]. The exception is when cpMax is -1, in which
1892 * case, it is always bigger than cpMin.
1893 */
1894 if (!editor->bEmulateVersion10 && !(flags & FR_DOWN))
1895 {
1896 int nSwap = nMax;
1897
1898 nMax = nMin > nTextLen ? nTextLen : nMin;
1899 if (nMin < nSwap || chrg->cpMax == -1)
1900 nMin = 0;
1901 else
1902 nMin = nSwap;
1903 }
1904
1905 if (!nLen || nMin < 0 || nMax < 0 || nMax < nMin)
1906 {
1907 if (chrgText)
1908 chrgText->cpMin = chrgText->cpMax = -1;
1909 return -1;
1910 }
1911
1912 if (flags & FR_DOWN) /* Forward search */
1913 {
1914 /* If possible, find the character before where the search starts */
1915 if ((flags & FR_WHOLEWORD) && nMin)
1916 {
1917 ME_CursorFromCharOfs(editor, nMin - 1, &cursor);
1918 wLastChar = *get_text( &cursor.pRun->member.run, cursor.nOffset );
1919 ME_MoveCursorChars(editor, &cursor, 1, FALSE);
1920 } else {
1921 ME_CursorFromCharOfs(editor, nMin, &cursor);
1922 }
1923
1924 while (cursor.pRun && ME_GetCursorOfs(&cursor) + nLen <= nMax)
1925 {
1926 ME_DisplayItem *pCurItem = cursor.pRun;
1927 int nCurStart = cursor.nOffset;
1928 int nMatched = 0;
1929
1930 while (pCurItem && ME_CharCompare( *get_text( &pCurItem->member.run, nCurStart + nMatched ), text[nMatched], (flags & FR_MATCHCASE)))
1931 {
1932 if ((flags & FR_WHOLEWORD) && isalnumW(wLastChar))
1933 break;
1934
1935 nMatched++;
1936 if (nMatched == nLen)
1937 {
1938 ME_DisplayItem *pNextItem = pCurItem;
1939 int nNextStart = nCurStart;
1940 WCHAR wNextChar;
1941
1942 /* Check to see if next character is a whitespace */
1943 if (flags & FR_WHOLEWORD)
1944 {
1945 if (nCurStart + nMatched == pCurItem->member.run.len)
1946 {
1947 pNextItem = ME_FindItemFwd(pCurItem, diRun);
1948 nNextStart = -nMatched;
1949 }
1950
1951 if (pNextItem)
1952 wNextChar = *get_text( &pNextItem->member.run, nNextStart + nMatched );
1953 else
1954 wNextChar = ' ';
1955
1956 if (isalnumW(wNextChar))
1957 break;
1958 }
1959
1960 cursor.nOffset += cursor.pPara->member.para.nCharOfs + cursor.pRun->member.run.nCharOfs;
1961 if (chrgText)
1962 {
1963 chrgText->cpMin = cursor.nOffset;
1964 chrgText->cpMax = cursor.nOffset + nLen;
1965 }
1966 TRACE("found at %d-%d\n", cursor.nOffset, cursor.nOffset + nLen);
1967 return cursor.nOffset;
1968 }
1969 if (nCurStart + nMatched == pCurItem->member.run.len)
1970 {
1971 pCurItem = ME_FindItemFwd(pCurItem, diRun);
1972 nCurStart = -nMatched;
1973 }
1974 }
1975 if (pCurItem)
1976 wLastChar = *get_text( &pCurItem->member.run, nCurStart + nMatched );
1977 else
1978 wLastChar = ' ';
1979
1980 cursor.nOffset++;
1981 if (cursor.nOffset == cursor.pRun->member.run.len)
1982 {
1983 ME_NextRun(&cursor.pPara, &cursor.pRun, TRUE);
1984 cursor.nOffset = 0;
1985 }
1986 }
1987 }
1988 else /* Backward search */
1989 {
1990 /* If possible, find the character after where the search ends */
1991 if ((flags & FR_WHOLEWORD) && nMax < nTextLen - 1)
1992 {
1993 ME_CursorFromCharOfs(editor, nMax + 1, &cursor);
1994 wLastChar = *get_text( &cursor.pRun->member.run, cursor.nOffset );
1995 ME_MoveCursorChars(editor, &cursor, -1, FALSE);
1996 } else {
1997 ME_CursorFromCharOfs(editor, nMax, &cursor);
1998 }
1999
2000 while (cursor.pRun && ME_GetCursorOfs(&cursor) - nLen >= nMin)
2001 {
2002 ME_DisplayItem *pCurItem = cursor.pRun;
2003 ME_DisplayItem *pCurPara = cursor.pPara;
2004 int nCurEnd = cursor.nOffset;
2005 int nMatched = 0;
2006
2007 if (nCurEnd == 0)
2008 {
2009 ME_PrevRun(&pCurPara, &pCurItem, TRUE);
2010 nCurEnd = pCurItem->member.run.len;
2011 }
2012
2013 while (pCurItem && ME_CharCompare( *get_text( &pCurItem->member.run, nCurEnd - nMatched - 1 ),
2014 text[nLen - nMatched - 1], (flags & FR_MATCHCASE) ))
2015 {
2016 if ((flags & FR_WHOLEWORD) && isalnumW(wLastChar))
2017 break;
2018
2019 nMatched++;
2020 if (nMatched == nLen)
2021 {
2022 ME_DisplayItem *pPrevItem = pCurItem;
2023 int nPrevEnd = nCurEnd;
2024 WCHAR wPrevChar;
2025 int nStart;
2026
2027 /* Check to see if previous character is a whitespace */
2028 if (flags & FR_WHOLEWORD)
2029 {
2030 if (nPrevEnd - nMatched == 0)
2031 {
2032 pPrevItem = ME_FindItemBack(pCurItem, diRun);
2033 if (pPrevItem)
2034 nPrevEnd = pPrevItem->member.run.len + nMatched;
2035 }
2036
2037 if (pPrevItem)
2038 wPrevChar = *get_text( &pPrevItem->member.run, nPrevEnd - nMatched - 1 );
2039 else
2040 wPrevChar = ' ';
2041
2042 if (isalnumW(wPrevChar))
2043 break;
2044 }
2045
2046 nStart = pCurPara->member.para.nCharOfs
2047 + pCurItem->member.run.nCharOfs + nCurEnd - nMatched;
2048 if (chrgText)
2049 {
2050 chrgText->cpMin = nStart;
2051 chrgText->cpMax = nStart + nLen;
2052 }
2053 TRACE("found at %d-%d\n", nStart, nStart + nLen);
2054 return nStart;
2055 }
2056 if (nCurEnd - nMatched == 0)
2057 {
2058 ME_PrevRun(&pCurPara, &pCurItem, TRUE);
2059 /* Don't care about pCurItem becoming NULL here; it's already taken
2060 * care of in the exterior loop condition */
2061 nCurEnd = pCurItem->member.run.len + nMatched;
2062 }
2063 }
2064 if (pCurItem)
2065 wLastChar = *get_text( &pCurItem->member.run, nCurEnd - nMatched - 1 );
2066 else
2067 wLastChar = ' ';
2068
2069 cursor.nOffset--;
2070 if (cursor.nOffset < 0)
2071 {
2072 ME_PrevRun(&cursor.pPara, &cursor.pRun, TRUE);
2073 cursor.nOffset = cursor.pRun->member.run.len;
2074 }
2075 }
2076 }
2077 TRACE("not found\n");
2078 if (chrgText)
2079 chrgText->cpMin = chrgText->cpMax = -1;
2080 return -1;
2081 }
2082
2083 static int ME_GetTextEx(ME_TextEditor *editor, GETTEXTEX *ex, LPARAM pText)
2084 {
2085 int nChars;
2086 ME_Cursor start;
2087
2088 if (!ex->cb || !pText) return 0;
2089
2090 if (ex->flags & ~(GT_SELECTION | GT_USECRLF))
2091 FIXME("GETTEXTEX flags 0x%08x not supported\n", ex->flags & ~(GT_SELECTION | GT_USECRLF));
2092
2093 if (ex->flags & GT_SELECTION)
2094 {
2095 int from, to;
2096 int nStartCur = ME_GetSelectionOfs(editor, &from, &to);
2097 start = editor->pCursors[nStartCur];
2098 nChars = to - from;
2099 }
2100 else
2101 {
2102 ME_SetCursorToStart(editor, &start);
2103 nChars = INT_MAX;
2104 }
2105 if (ex->codepage == CP_UNICODE)
2106 {
2107 return ME_GetTextW(editor, (LPWSTR)pText, ex->cb / sizeof(WCHAR) - 1,
2108 &start, nChars, ex->flags & GT_USECRLF, FALSE);
2109 }
2110 else
2111 {
2112 /* potentially each char may be a CR, why calculate the exact value with O(N) when
2113 we can just take a bigger buffer? :)
2114 The above assumption still holds with CR/LF counters, since CR->CRLF expansion
2115 occurs only in richedit 2.0 mode, in which line breaks have only one CR
2116 */
2117 int crlfmul = (ex->flags & GT_USECRLF) ? 2 : 1;
2118 DWORD buflen;
2119 LPWSTR buffer;
2120 LRESULT rc;
2121
2122 buflen = min(crlfmul * nChars, ex->cb - 1);
2123 buffer = heap_alloc((buflen + 1) * sizeof(WCHAR));
2124
2125 nChars = ME_GetTextW(editor, buffer, buflen, &start, nChars, ex->flags & GT_USECRLF, FALSE);
2126 rc = WideCharToMultiByte(ex->codepage, 0, buffer, nChars + 1,
2127 (LPSTR)pText, ex->cb, ex->lpDefaultChar, ex->lpUsedDefChar);
2128 if (rc) rc--; /* do not count 0 terminator */
2129
2130 heap_free(buffer);
2131 return rc;
2132 }
2133 }
2134
2135 static int ME_GetTextRange(ME_TextEditor *editor, WCHAR *strText,
2136 const ME_Cursor *start, int nLen, BOOL unicode)
2137 {
2138 if (!strText) return 0;
2139 if (unicode) {
2140 return ME_GetTextW(editor, strText, INT_MAX, start, nLen, FALSE, FALSE);
2141 } else {
2142 int nChars;
2143 WCHAR *p = heap_alloc((nLen+1) * sizeof(*p));
2144 if (!p) return 0;
2145 nChars = ME_GetTextW(editor, p, nLen, start, nLen, FALSE, FALSE);
2146 WideCharToMultiByte(CP_ACP, 0, p, nChars+1, (char *)strText,
2147 nLen+1, NULL, NULL);
2148 heap_free(p);
2149 return nChars;
2150 }
2151 }
2152
2153 static int handle_EM_EXSETSEL( ME_TextEditor *editor, int to, int from )
2154 {
2155 int end;
2156
2157 TRACE("%d - %d\n", to, from );
2158
2159 ME_InvalidateSelection( editor );
2160 end = ME_SetSelection( editor, to, from );
2161 ME_InvalidateSelection( editor );
2162 ITextHost_TxShowCaret( editor->texthost, FALSE );
2163 ME_ShowCaret( editor );
2164 ME_SendSelChange( editor );
2165
2166 return end;
2167 }
2168
2169 typedef struct tagME_GlobalDestStruct
2170 {
2171 HGLOBAL hData;
2172 int nLength;
2173 } ME_GlobalDestStruct;
2174
2175 static DWORD CALLBACK ME_ReadFromHGLOBALUnicode(DWORD_PTR dwCookie, LPBYTE lpBuff, LONG cb, LONG *pcb)
2176 {
2177 ME_GlobalDestStruct *pData = (ME_GlobalDestStruct *)dwCookie;
2178 int i;
2179 WORD *pSrc, *pDest;
2180
2181 cb = cb >> 1;
2182 pDest = (WORD *)lpBuff;
2183 pSrc = GlobalLock(pData->hData);
2184 for (i = 0; i<cb && pSrc[pData->nLength+i]; i++) {
2185 pDest[i] = pSrc[pData->nLength+i];
2186 }
2187 pData->nLength += i;
2188 *pcb = 2*i;
2189 GlobalUnlock(pData->hData);
2190 return 0;
2191 }
2192
2193 static DWORD CALLBACK ME_ReadFromHGLOBALRTF(DWORD_PTR dwCookie, LPBYTE lpBuff, LONG cb, LONG *pcb)
2194 {
2195 ME_GlobalDestStruct *pData = (ME_GlobalDestStruct *)dwCookie;
2196 int i;
2197 BYTE *pSrc, *pDest;
2198
2199 pDest = lpBuff;
2200 pSrc = GlobalLock(pData->hData);
2201 for (i = 0; i<cb && pSrc[pData->nLength+i]; i++) {
2202 pDest[i] = pSrc[pData->nLength+i];
2203 }
2204 pData->nLength += i;
2205 *pcb = i;
2206 GlobalUnlock(pData->hData);
2207 return 0;
2208 }
2209
2210 static const WCHAR rtfW[] = {'R','i','c','h',' ','T','e','x','t',' ','F','o','r','m','a','t',0};
2211
2212 static HRESULT paste_rtf(ME_TextEditor *editor, FORMATETC *fmt, STGMEDIUM *med)
2213 {
2214 EDITSTREAM es;
2215 ME_GlobalDestStruct gds;
2216 HRESULT hr;
2217
2218 gds.hData = med->u.hGlobal;
2219 gds.nLength = 0;
2220 es.dwCookie = (DWORD_PTR)&gds;
2221 es.pfnCallback = ME_ReadFromHGLOBALRTF;
2222 hr = ME_StreamIn( editor, SF_RTF | SFF_SELECTION, &es, FALSE ) == 0 ? E_FAIL : S_OK;
2223 ReleaseStgMedium( med );
2224 return hr;
2225 }
2226
2227 static HRESULT paste_text(ME_TextEditor *editor, FORMATETC *fmt, STGMEDIUM *med)
2228 {
2229 EDITSTREAM es;
2230 ME_GlobalDestStruct gds;
2231 HRESULT hr;
2232
2233 gds.hData = med->u.hGlobal;
2234 gds.nLength = 0;
2235 es.dwCookie = (DWORD_PTR)&gds;
2236 es.pfnCallback = ME_ReadFromHGLOBALUnicode;
2237 hr = ME_StreamIn( editor, SF_TEXT | SF_UNICODE | SFF_SELECTION, &es, FALSE ) == 0 ? E_FAIL : S_OK;
2238 ReleaseStgMedium( med );
2239 return hr;
2240 }
2241
2242 static HRESULT paste_emf(ME_TextEditor *editor, FORMATETC *fmt, STGMEDIUM *med)
2243 {
2244 HRESULT hr;
2245 SIZEL sz = {0, 0};
2246
2247 hr = insert_static_object( editor, med->u.hEnhMetaFile, NULL, &sz );
2248 if (SUCCEEDED(hr))
2249 {
2250 ME_CommitUndo( editor );
2251 ME_UpdateRepaint( editor, FALSE );
2252 }
2253 else
2254 ReleaseStgMedium( med );
2255
2256 return hr;
2257 }
2258
2259 static struct paste_format
2260 {
2261 FORMATETC fmt;
2262 HRESULT (*paste)(ME_TextEditor *, FORMATETC *, STGMEDIUM *);
2263 const WCHAR *name;
2264 } paste_formats[] =
2265 {
2266 {{ -1, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }, paste_rtf, rtfW },
2267 {{ CF_UNICODETEXT, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }, paste_text },
2268 {{ CF_ENHMETAFILE, NULL, DVASPECT_CONTENT, -1, TYMED_ENHMF }, paste_emf },
2269 {{ 0 }}
2270 };
2271
2272 static void init_paste_formats(void)
2273 {
2274 struct paste_format *format;
2275 static int done;
2276
2277 if (!done)
2278 {
2279 for (format = paste_formats; format->fmt.cfFormat; format++)
2280 {
2281 if (format->name)
2282 format->fmt.cfFormat = RegisterClipboardFormatW( format->name );
2283 }
2284 done = 1;
2285 }
2286 }
2287
2288 static BOOL paste_special(ME_TextEditor *editor, UINT cf, REPASTESPECIAL *ps, BOOL check_only)
2289 {
2290 HRESULT hr;
2291 STGMEDIUM med;
2292 struct paste_format *format;
2293 IDataObject *data;
2294
2295 /* Protect read-only edit control from modification */
2296 if (editor->styleFlags & ES_READONLY)
2297 {
2298 if (!check_only)
2299 MessageBeep(MB_ICONERROR);
2300 return FALSE;
2301 }
2302
2303 init_paste_formats();
2304
2305 if (ps && ps->dwAspect != DVASPECT_CONTENT)
2306 FIXME("Ignoring aspect %x\n", ps->dwAspect);
2307
2308 hr = OleGetClipboard( &data );
2309 if (hr != S_OK) return FALSE;
2310
2311 if (cf == CF_TEXT) cf = CF_UNICODETEXT;
2312
2313 hr = S_FALSE;
2314 for (format = paste_formats; format->fmt.cfFormat; format++)
2315 {
2316 if (cf && cf != format->fmt.cfFormat) continue;
2317 hr = IDataObject_QueryGetData( data, &format->fmt );
2318 if (hr == S_OK)
2319 {
2320 if (!check_only)
2321 {
2322 hr = IDataObject_GetData( data, &format->fmt, &med );
2323 if (hr != S_OK) goto done;
2324 hr = format->paste( editor, &format->fmt, &med );
2325 }
2326 break;
2327 }
2328 }
2329
2330 done:
2331 IDataObject_Release( data );
2332
2333 return hr == S_OK;
2334 }
2335
2336 static BOOL ME_Copy(ME_TextEditor *editor, const ME_Cursor *start, int nChars)
2337 {
2338 LPDATAOBJECT dataObj = NULL;
2339 HRESULT hr = S_OK;
2340
2341 if (editor->cPasswordMask)
2342 return FALSE; /* Copying or Cutting masked text isn't allowed */
2343
2344 if(editor->lpOleCallback)
2345 {
2346 CHARRANGE range;
2347 range.cpMin = ME_GetCursorOfs(start);
2348 range.cpMax = range.cpMin + nChars;
2349 hr = IRichEditOleCallback_GetClipboardData(editor->lpOleCallback, &range, RECO_COPY, &dataObj);
2350 }
2351 if(FAILED(hr) || !dataObj)
2352 hr = ME_GetDataObject(editor, start, nChars, &dataObj);
2353 if(SUCCEEDED(hr)) {
2354 hr = OleSetClipboard(dataObj);
2355 IDataObject_Release(dataObj);
2356 }
2357 return SUCCEEDED(hr);
2358 }
2359
2360 static BOOL copy_or_cut(ME_TextEditor *editor, BOOL cut)
2361 {
2362 BOOL result;
2363 int offs, num_chars;
2364 int start_cursor = ME_GetSelectionOfs(editor, &offs, &num_chars);
2365 ME_Cursor *sel_start = &editor->pCursors[start_cursor];
2366
2367 if (cut && (editor->styleFlags & ES_READONLY))
2368 {
2369 MessageBeep(MB_ICONERROR);
2370 return FALSE;
2371 }
2372
2373 num_chars -= offs;
2374 result = ME_Copy(editor, sel_start, num_chars);
2375 if (result && cut)
2376 {
2377 ME_InternalDeleteText(editor, sel_start, num_chars, FALSE);
2378 ME_CommitUndo(editor);
2379 ME_UpdateRepaint(editor, TRUE);
2380 }
2381 return result;
2382 }
2383
2384 /* helper to send a msg filter notification */
2385 static BOOL
2386 ME_FilterEvent(ME_TextEditor *editor, UINT msg, WPARAM* wParam, LPARAM* lParam)
2387 {
2388 MSGFILTER msgf;
2389
2390 if (!editor->hWnd || !editor->hwndParent) return FALSE;
2391 msgf.nmhdr.hwndFrom = editor->hWnd;
2392 msgf.nmhdr.idFrom = GetWindowLongW(editor->hWnd, GWLP_ID);
2393 msgf.nmhdr.code = EN_MSGFILTER;
2394 msgf.msg = msg;
2395 msgf.wParam = *wParam;
2396 msgf.lParam = *lParam;
2397 if (SendMessageW(editor->hwndParent, WM_NOTIFY, msgf.nmhdr.idFrom, (LPARAM)&msgf))
2398 return FALSE;
2399 *wParam = msgf.wParam;
2400 *lParam = msgf.lParam;
2401 msgf.wParam = *wParam;
2402
2403 return TRUE;
2404 }
2405
2406 static void ME_UpdateSelectionLinkAttribute(ME_TextEditor *editor)
2407 {
2408 ME_DisplayItem *startPara, *endPara;
2409 ME_DisplayItem *prev_para;
2410 ME_Cursor *from, *to;
2411 ME_Cursor start;
2412 int nChars;
2413
2414 if (!editor->AutoURLDetect_bEnable) return;
2415
2416 ME_GetSelection(editor, &from, &to);
2417
2418 /* Find paragraph previous to the one that contains start cursor */
2419 startPara = from->pPara;
2420 prev_para = startPara->member.para.prev_para;
2421 if (prev_para->type == diParagraph) startPara = prev_para;
2422
2423 /* Find paragraph that contains end cursor */
2424 endPara = to->pPara->member.para.next_para;
2425
2426 start.pPara = startPara;
2427 start.pRun = ME_FindItemFwd(startPara, diRun);
2428 start.nOffset = 0;
2429 nChars = endPara->member.para.nCharOfs - startPara->member.para.nCharOfs;
2430
2431 ME_UpdateLinkAttribute(editor, &start, nChars);
2432 }
2433
2434 static BOOL
2435 ME_KeyDown(ME_TextEditor *editor, WORD nKey)
2436 {
2437 BOOL ctrl_is_down = GetKeyState(VK_CONTROL) & 0x8000;
2438 BOOL shift_is_down = GetKeyState(VK_SHIFT) & 0x8000;
2439
2440 if (editor->bMouseCaptured)
2441 return FALSE;
2442 if (nKey != VK_SHIFT && nKey != VK_CONTROL && nKey != VK_MENU)
2443 editor->nSelectionType = stPosition;
2444
2445 switch (nKey)
2446 {
2447 case VK_LEFT:
2448 case VK_RIGHT:
2449 case VK_HOME:
2450 case VK_END:
2451 editor->nUDArrowX = -1;
2452 /* fall through */
2453 case VK_UP:
2454 case VK_DOWN:
2455 case VK_PRIOR:
2456 case VK_NEXT:
2457 ME_CommitUndo(editor); /* End coalesced undos for typed characters */
2458 ME_ArrowKey(editor, nKey, shift_is_down, ctrl_is_down);
2459 return TRUE;
2460 case VK_BACK:
2461 case VK_DELETE:
2462 editor->nUDArrowX = -1;
2463 /* FIXME backspace and delete aren't the same, they act different wrt paragraph style of the merged paragraph */
2464 if (editor->styleFlags & ES_READONLY)
2465 return FALSE;
2466 if (ME_IsSelection(editor))
2467 {
2468 ME_DeleteSelection(editor);
2469 ME_CommitUndo(editor);
2470 }
2471 else if (nKey == VK_DELETE)
2472 {
2473 /* Delete stops group typing.
2474 * (See MSDN remarks on EM_STOPGROUPTYPING message) */
2475 ME_DeleteTextAtCursor(editor, 1, 1);
2476 ME_CommitUndo(editor);
2477 }
2478 else if (ME_ArrowKey(editor, VK_LEFT, FALSE, FALSE))
2479 {
2480 BOOL bDeletionSucceeded;
2481 /* Backspace can be grouped for a single undo */
2482 ME_ContinueCoalescingTransaction(editor);
2483 bDeletionSucceeded = ME_DeleteTextAtCursor(editor, 1, 1);
2484 if (!bDeletionSucceeded && !editor->bEmulateVersion10) { /* v4.1 */
2485 /* Deletion was prevented so the cursor is moved back to where it was.
2486 * (e.g. this happens when trying to delete cell boundaries)
2487 */
2488 ME_ArrowKey(editor, VK_RIGHT, FALSE, FALSE);
2489 }
2490 ME_CommitCoalescingUndo(editor);
2491 }
2492 else
2493 return TRUE;
2494 ME_MoveCursorFromTableRowStartParagraph(editor);
2495 ME_UpdateSelectionLinkAttribute(editor);
2496 ME_UpdateRepaint(editor, FALSE);
2497 ME_SendRequestResize(editor, FALSE);
2498 return TRUE;
2499 case VK_RETURN:
2500 if (editor->bDialogMode)
2501 {
2502 if (ctrl_is_down)
2503 return TRUE;
2504
2505 if (!(editor->styleFlags & ES_WANTRETURN))
2506 {
2507 if (editor->hwndParent)
2508 {
2509 DWORD dw;
2510 dw = SendMessageW(editor->hwndParent, DM_GETDEFID, 0, 0);
2511 if (HIWORD(dw) == DC_HASDEFID)
2512 {
2513 HWND hwDefCtrl = GetDlgItem(editor->hwndParent, LOWORD(dw));
2514 if (hwDefCtrl)
2515 {
2516 SendMessageW(editor->hwndParent, WM_NEXTDLGCTL, (WPARAM)hwDefCtrl, TRUE);
2517 PostMessageW(hwDefCtrl, WM_KEYDOWN, VK_RETURN, 0);
2518 }
2519 }
2520 }
2521 return TRUE;
2522 }
2523 }
2524
2525 if (editor->styleFlags & ES_MULTILINE)
2526 {
2527 ME_Cursor cursor = editor->pCursors[0];
2528 ME_DisplayItem *para = cursor.pPara;
2529 int from, to;
2530 const WCHAR endl = '\r';
2531 const WCHAR endlv10[] = {'\r','\n'};
2532 ME_Style *style, *eop_style;
2533
2534 if (editor->styleFlags & ES_READONLY) {
2535 MessageBeep(MB_ICONERROR);
2536 return TRUE;
2537 }
2538
2539 ME_GetSelectionOfs(editor, &from, &to);
2540 if (editor->nTextLimit > ME_GetTextLength(editor) - (to-from))
2541 {
2542 if (!editor->bEmulateVersion10) { /* v4.1 */
2543 if (para->member.para.nFlags & MEPF_ROWEND) {
2544 /* Add a new table row after this row. */
2545 para = ME_AppendTableRow(editor, para);
2546 para = para->member.para.next_para;
2547 editor->pCursors[0].pPara = para;
2548 editor->pCursors[0].pRun = ME_FindItemFwd(para, diRun);
2549 editor->pCursors[0].nOffset = 0;
2550 editor->pCursors[1] = editor->pCursors[0];
2551 ME_CommitUndo(editor);
2552 ME_CheckTablesForCorruption(editor);
2553 ME_UpdateRepaint(editor, FALSE);
2554 return TRUE;
2555 }
2556 else if (para == editor->pCursors[1].pPara &&
2557 cursor.nOffset + cursor.pRun->member.run.nCharOfs == 0 &&
2558 para->member.para.prev_para->member.para.nFlags & MEPF_ROWSTART &&
2559 !para->member.para.prev_para->member.para.nCharOfs)
2560 {
2561 /* Insert a newline before the table. */
2562 para = para->member.para.prev_para;
2563 para->member.para.nFlags &= ~MEPF_ROWSTART;
2564 editor->pCursors[0].pPara = para;
2565 editor->pCursors[0].pRun = ME_FindItemFwd(para, diRun);
2566 editor->pCursors[1] = editor->pCursors[0];
2567 ME_InsertTextFromCursor(editor, 0, &endl, 1,
2568 editor->pCursors[0].pRun->member.run.style);
2569 para = editor->pBuffer->pFirst->member.para.next_para;
2570 ME_SetDefaultParaFormat(editor, &para->member.para.fmt);
2571 para->member.para.nFlags = MEPF_REWRAP;
2572 editor->pCursors[0].pPara = para;
2573 editor->pCursors[0].pRun = ME_FindItemFwd(para, diRun);
2574 editor->pCursors[1] = editor->pCursors[0];
2575 para->member.para.next_para->member.para.nFlags |= MEPF_ROWSTART;
2576 ME_CommitCoalescingUndo(editor);
2577 ME_CheckTablesForCorruption(editor);
2578 ME_UpdateRepaint(editor, FALSE);
2579 return TRUE;
2580 }
2581 } else { /* v1.0 - 3.0 */
2582 ME_DisplayItem *para = cursor.pPara;
2583 if (ME_IsInTable(para))
2584 {
2585 if (cursor.pRun->member.run.nFlags & MERF_ENDPARA)
2586 {
2587 if (from == to) {
2588 ME_ContinueCoalescingTransaction(editor);
2589 para = ME_AppendTableRow(editor, para);
2590 editor->pCursors[0].pPara = para;
2591 editor->pCursors[0].pRun = ME_FindItemFwd(para, diRun);
2592 editor->pCursors[0].nOffset = 0;
2593 editor->pCursors[1] = editor->pCursors[0];
2594 ME_CommitCoalescingUndo(editor);
2595 ME_UpdateRepaint(editor, FALSE);
2596 return TRUE;
2597 }
2598 } else {
2599 ME_ContinueCoalescingTransaction(editor);
2600 if (cursor.pRun->member.run.nCharOfs + cursor.nOffset == 0 &&
2601 !ME_IsInTable(para->member.para.prev_para))
2602 {
2603 /* Insert newline before table */
2604 cursor.pRun = ME_FindItemBack(para, diRun);
2605 if (cursor.pRun) {
2606 editor->pCursors[0].pRun = cursor.pRun;
2607 editor->pCursors[0].pPara = para->member.para.prev_para;
2608 }
2609 editor->pCursors[0].nOffset = 0;
2610 editor->pCursors[1] = editor->pCursors[0];
2611 ME_InsertTextFromCursor(editor, 0, &endl, 1,
2612 editor->pCursors[0].pRun->member.run.style);
2613 } else {
2614 editor->pCursors[1] = editor->pCursors[0];
2615 para = ME_AppendTableRow(editor, para);
2616 editor->pCursors[0].pPara = para;
2617 editor->pCursors[0].pRun = ME_FindItemFwd(para, diRun);
2618 editor->pCursors[0].nOffset = 0;
2619 editor->pCursors[1] = editor->pCursors[0];
2620 }
2621 ME_CommitCoalescingUndo(editor);
2622 ME_UpdateRepaint(editor, FALSE);
2623 return TRUE;
2624 }
2625 }
2626 }
2627
2628 style = ME_GetInsertStyle(editor, 0);
2629
2630 /* Normally the new eop style is the insert style, however in a list it is copied from the existing
2631 eop style (this prevents the list label style changing when the new eop is inserted).
2632 No extra ref is taken here on eop_style. */
2633 if (para->member.para.fmt.wNumbering)
2634 eop_style = para->member.para.eop_run->style;
2635 else
2636 eop_style = style;
2637 ME_ContinueCoalescingTransaction(editor);
2638 if (shift_is_down)
2639 ME_InsertEndRowFromCursor(editor, 0);
2640 else
2641 if (!editor->bEmulateVersion10)
2642 ME_InsertTextFromCursor(editor, 0, &endl, 1, eop_style);
2643 else
2644 ME_InsertTextFromCursor(editor, 0, endlv10, 2, eop_style);
2645 ME_CommitCoalescingUndo(editor);
2646 SetCursor(NULL);
2647
2648 ME_UpdateSelectionLinkAttribute(editor);
2649 ME_UpdateRepaint(editor, FALSE);
2650 ME_SaveTempStyle(editor, style); /* set the temp insert style for the new para */
2651 ME_ReleaseStyle(style);
2652 }
2653 return TRUE;
2654 }
2655 break;
2656 case VK_ESCAPE:
2657 if (editor->bDialogMode && editor->hwndParent)
2658 PostMessageW(editor->hwndParent, WM_CLOSE, 0, 0);
2659 return TRUE;
2660 case VK_TAB:
2661 if (editor->bDialogMode && editor->hwndParent)
2662 SendMessageW(editor->hwndParent, WM_NEXTDLGCTL, shift_is_down, 0);
2663 return TRUE;
2664 case 'A':
2665 if (ctrl_is_down)
2666 {
2667 handle_EM_EXSETSEL( editor, 0, -1 );
2668 return TRUE;
2669 }
2670 break;
2671 case 'V':
2672 if (ctrl_is_down)
2673 return paste_special( editor, 0, NULL, FALSE );
2674 break;
2675 case 'C':
2676 case 'X':
2677 if (ctrl_is_down)
2678 return copy_or_cut(editor, nKey == 'X');
2679 break;
2680 case 'Z':
2681 if (ctrl_is_down)
2682 {
2683 ME_Undo(editor);
2684 return TRUE;
2685 }
2686 break;
2687 case 'Y':
2688 if (ctrl_is_down)
2689 {
2690 ME_Redo(editor);
2691 return TRUE;
2692 }
2693 break;
2694
2695 default:
2696 if (nKey != VK_SHIFT && nKey != VK_CONTROL && nKey && nKey != VK_MENU)
2697 editor->nUDArrowX = -1;
2698 if (ctrl_is_down)
2699 {
2700 if (nKey == 'W')
2701 {
2702 CHARFORMAT2W chf;
2703 char buf[2048];
2704 chf.cbSize = sizeof(chf);
2705
2706 ME_GetSelectionCharFormat(editor, &chf);
2707 ME_DumpStyleToBuf(&chf, buf);
2708 MessageBoxA(NULL, buf, "Style dump", MB_OK);
2709 }
2710 if (nKey == 'Q')
2711 {
2712 ME_CheckCharOffsets(editor);
2713 }
2714 }
2715 }
2716 return FALSE;
2717 }
2718
2719 static LRESULT ME_Char(ME_TextEditor *editor, WPARAM charCode,
2720 LPARAM flags, BOOL unicode)
2721 {
2722 WCHAR wstr;
2723
2724 if (editor->bMouseCaptured)
2725 return 0;
2726
2727 if (unicode)
2728 wstr = (WCHAR)charCode;
2729 else
2730 {
2731 CHAR charA = charCode;
2732 MultiByteToWideChar(CP_ACP, 0, &charA, 1, &wstr, 1);
2733 }
2734
2735 if (editor->styleFlags & ES_READONLY) {
2736 MessageBeep(MB_ICONERROR);
2737 return 0; /* FIXME really 0 ? */
2738 }
2739
2740 if ((unsigned)wstr >= ' ' || wstr == '\t')
2741 {
2742 ME_Cursor cursor = editor->pCursors[0];
2743 ME_DisplayItem *para = cursor.pPara;
2744 int from, to;
2745 BOOL ctrl_is_down = GetKeyState(VK_CONTROL) & 0x8000;
2746 ME_GetSelectionOfs(editor, &from, &to);
2747 if (wstr == '\t' &&
2748 /* v4.1 allows tabs to be inserted with ctrl key down */
2749 !(ctrl_is_down && !editor->bEmulateVersion10))
2750 {
2751 ME_DisplayItem *para;
2752 BOOL bSelectedRow = FALSE;
2753
2754 para = cursor.pPara;
2755 if (ME_IsSelection(editor) &&
2756 cursor.pRun->member.run.nCharOfs + cursor.nOffset == 0 &&
2757 to == ME_GetCursorOfs(&editor->pCursors[0]) &&
2758 para->member.para.prev_para->type == diParagraph)
2759 {
2760 para = para->member.para.prev_para;
2761 bSelectedRow = TRUE;
2762 }
2763 if (ME_IsInTable(para))
2764 {
2765 ME_TabPressedInTable(editor, bSelectedRow);
2766 ME_CommitUndo(editor);
2767 return 0;
2768 }
2769 } else if (!editor->bEmulateVersion10) { /* v4.1 */
2770 if (para->member.para.nFlags & MEPF_ROWEND) {
2771 if (from == to) {
2772 para = para->member.para.next_para;
2773 if (para->member.para.nFlags & MEPF_ROWSTART)
2774 para = para->member.para.next_para;
2775 editor->pCursors[0].pPara = para;
2776 editor->pCursors[0].pRun = ME_FindItemFwd(para, diRun);
2777 editor->pCursors[0].nOffset = 0;
2778 editor->pCursors[1] = editor->pCursors[0];
2779 }
2780 }
2781 } else { /* v1.0 - 3.0 */
2782 if (ME_IsInTable(cursor.pRun) &&
2783 cursor.pRun->member.run.nFlags & MERF_ENDPARA &&
2784 from == to)
2785 {
2786 /* Text should not be inserted at the end of the table. */
2787 MessageBeep(-1);
2788 return 0;
2789 }
2790 }
2791 /* FIXME maybe it would make sense to call EM_REPLACESEL instead ? */
2792 /* WM_CHAR is restricted to nTextLimit */
2793 if(editor->nTextLimit > ME_GetTextLength(editor) - (to-from))
2794 {
2795 ME_Style *style = ME_GetInsertStyle(editor, 0);
2796 ME_ContinueCoalescingTransaction(editor);
2797 ME_InsertTextFromCursor(editor, 0, &wstr, 1, style);
2798 ME_ReleaseStyle(style);
2799 ME_CommitCoalescingUndo(editor);
2800 ITextHost_TxSetCursor(editor->texthost, NULL, FALSE);
2801 }
2802
2803 ME_UpdateSelectionLinkAttribute(editor);
2804 ME_UpdateRepaint(editor, FALSE);
2805 }
2806 return 0;
2807 }
2808
2809 /* Process the message and calculate the new click count.
2810 *
2811 * returns: The click count if it is mouse down event, else returns 0. */
2812 static int ME_CalculateClickCount(ME_TextEditor *editor, UINT msg, WPARAM wParam,
2813 LPARAM lParam)
2814 {
2815 static int clickNum = 0;
2816 if (msg < WM_MOUSEFIRST || msg > WM_MOUSELAST)
2817 return 0;
2818
2819 if ((msg == WM_LBUTTONDBLCLK) ||
2820 (msg == WM_RBUTTONDBLCLK) ||
2821 (msg == WM_MBUTTONDBLCLK) ||
2822 (msg == WM_XBUTTONDBLCLK))
2823 {
2824 msg -= (WM_LBUTTONDBLCLK - WM_LBUTTONDOWN);
2825 }
2826
2827 if ((msg == WM_LBUTTONDOWN) ||
2828 (msg == WM_RBUTTONDOWN) ||
2829 (msg == WM_MBUTTONDOWN) ||
2830 (msg == WM_XBUTTONDOWN))
2831 {
2832 static MSG prevClickMsg;
2833 MSG clickMsg;
2834 /* Compare the editor instead of the hwnd so that the this
2835 * can still be done for windowless richedit controls. */
2836 clickMsg.hwnd = (HWND)editor;
2837 clickMsg.message = msg;
2838 clickMsg.wParam = wParam;
2839 clickMsg.lParam = lParam;
2840 clickMsg.time = GetMessageTime();
2841 clickMsg.pt.x = (short)LOWORD(lParam);
2842 clickMsg.pt.y = (short)HIWORD(lParam);
2843 if ((clickNum != 0) &&
2844 (clickMsg.message == prevClickMsg.message) &&
2845 (clickMsg.hwnd == prevClickMsg.hwnd) &&
2846 (clickMsg.wParam == prevClickMsg.wParam) &&
2847 (clickMsg.time - prevClickMsg.time < GetDoubleClickTime()) &&
2848 (abs(clickMsg.pt.x - prevClickMsg.pt.x) < GetSystemMetrics(SM_CXDOUBLECLK)/2) &&
2849 (abs(clickMsg.pt.y - prevClickMsg.pt.y) < GetSystemMetrics(SM_CYDOUBLECLK)/2))
2850 {
2851 clickNum++;
2852 } else {
2853 clickNum = 1;
2854 }
2855 prevClickMsg = clickMsg;
2856 } else {
2857 return 0;
2858 }
2859 return clickNum;
2860 }
2861
2862 static BOOL is_link( ME_Run *run )
2863 {
2864 return (run->style->fmt.dwMask & CFM_LINK) && (run->style->fmt.dwEffects & CFE_LINK);
2865 }
2866
2867 static BOOL ME_SetCursor(ME_TextEditor *editor)
2868 {
2869 ME_Cursor cursor;
2870 POINT pt;
2871 BOOL isExact;
2872 SCROLLBARINFO sbi;
2873 DWORD messagePos = GetMessagePos();
2874 pt.x = (short)LOWORD(messagePos);
2875 pt.y = (short)HIWORD(messagePos);
2876
2877 if (editor->hWnd)
2878 {
2879 sbi.cbSize = sizeof(sbi);
2880 GetScrollBarInfo(editor->hWnd, OBJID_HSCROLL, &sbi);
2881 if (!(sbi.rgstate[0] & (STATE_SYSTEM_INVISIBLE|STATE_SYSTEM_OFFSCREEN)) &&
2882 PtInRect(&sbi.rcScrollBar, pt))
2883 {
2884 ITextHost_TxSetCursor(editor->texthost,
2885 LoadCursorW(NULL, (WCHAR*)IDC_ARROW), FALSE);
2886 return TRUE;
2887 }
2888 sbi.cbSize = sizeof(sbi);
2889 GetScrollBarInfo(editor->hWnd, OBJID_VSCROLL, &sbi);
2890 if (!(sbi.rgstate[0] & (STATE_SYSTEM_INVISIBLE|STATE_SYSTEM_OFFSCREEN)) &&
2891 PtInRect(&sbi.rcScrollBar, pt))
2892 {
2893 ITextHost_TxSetCursor(editor->texthost,
2894 LoadCursorW(NULL, (WCHAR*)IDC_ARROW), FALSE);
2895 return TRUE;
2896 }
2897 }
2898 ITextHost_TxScreenToClient(editor->texthost, &pt);
2899
2900 if (editor->nSelectionType == stLine && editor->bMouseCaptured) {
2901 ITextHost_TxSetCursor(editor->texthost, hLeft, FALSE);
2902 return TRUE;
2903 }
2904 if (!editor->bEmulateVersion10 /* v4.1 */ &&
2905 pt.y < editor->rcFormat.top &&
2906 pt.x < editor->rcFormat.left)
2907 {
2908 ITextHost_TxSetCursor(editor->texthost, hLeft, FALSE);
2909 return TRUE;
2910 }
2911 if (pt.y < editor->rcFormat.top || pt.y > editor->rcFormat.bottom)
2912 {
2913 if (editor->bEmulateVersion10) /* v1.0 - 3.0 */
2914 ITextHost_TxSetCursor(editor->texthost,
2915 LoadCursorW(NULL, (WCHAR*)IDC_ARROW), FALSE);
2916 else /* v4.1 */
2917 ITextHost_TxSetCursor(editor->texthost,
2918 LoadCursorW(NULL, (WCHAR*)IDC_IBEAM), TRUE);
2919 return TRUE;
2920 }
2921 if (pt.x < editor->rcFormat.left)
2922 {
2923 ITextHost_TxSetCursor(editor->texthost, hLeft, FALSE);
2924 return TRUE;
2925 }
2926 ME_CharFromPos(editor, pt.x, pt.y, &cursor, &isExact);
2927 if (isExact)
2928 {
2929 ME_Run *run;
2930
2931 run = &cursor.pRun->member.run;
2932 if (is_link( run ))
2933 {
2934 ITextHost_TxSetCursor(editor->texthost,
2935 LoadCursorW(NULL, (WCHAR*)IDC_HAND),
2936 FALSE);
2937 return TRUE;
2938 }
2939
2940 if (ME_IsSelection(editor))
2941 {
2942 int selStart, selEnd;
2943 int offset = ME_GetCursorOfs(&cursor);
2944
2945 ME_GetSelectionOfs(editor, &selStart, &selEnd);
2946 if (selStart <= offset && selEnd >= offset) {
2947 ITextHost_TxSetCursor(editor->texthost,
2948 LoadCursorW(NULL, (WCHAR*)IDC_ARROW),
2949 FALSE);
2950 return TRUE;
2951 }
2952 }
2953 }
2954 ITextHost_TxSetCursor(editor->texthost,
2955 LoadCursorW(NULL, (WCHAR*)IDC_IBEAM), TRUE);
2956 return TRUE;
2957 }
2958
2959 static void ME_SetDefaultFormatRect(ME_TextEditor *editor)
2960 {
2961 ITextHost_TxGetClientRect(editor->texthost, &editor->rcFormat);
2962 editor->rcFormat.top += editor->exStyleFlags & WS_EX_CLIENTEDGE ? 1 : 0;
2963 editor->rcFormat.left += 1 + editor->selofs;
2964 editor->rcFormat.right -= 1;
2965 }
2966
2967 static LONG ME_GetSelectionType(ME_TextEditor *editor)
2968 {
2969 LONG sel_type = SEL_EMPTY;
2970 LONG start, end;
2971
2972 ME_GetSelectionOfs(editor, &start, &end);
2973 if (start == end)
2974 sel_type = SEL_EMPTY;
2975 else
2976 {
2977 LONG object_count = 0, character_count = 0;
2978 int i;
2979
2980 for (i = 0; i < end - start; i++)
2981 {
2982 ME_Cursor cursor;
2983
2984 ME_CursorFromCharOfs(editor, start + i, &cursor);
2985 if (cursor.pRun->member.run.reobj)
2986 object_count++;
2987 else
2988 character_count++;
2989 if (character_count >= 2 && object_count >= 2)
2990 return (SEL_TEXT | SEL_MULTICHAR | SEL_OBJECT | SEL_MULTIOBJECT);
2991 }
2992 if (character_count)
2993 {
2994 sel_type |= SEL_TEXT;
2995 if (character_count >= 2)
2996 sel_type |= SEL_MULTICHAR;
2997 }
2998 if (object_count)
2999 {
3000 sel_type |= SEL_OBJECT;
3001 if (object_count >= 2)
3002 sel_type |= SEL_MULTIOBJECT;
3003 }
3004 }
3005 return sel_type;
3006 }
3007
3008 static BOOL ME_ShowContextMenu(ME_TextEditor *editor, int x, int y)
3009 {
3010 CHARRANGE selrange;
3011 HMENU menu;
3012 int seltype;
3013
3014 if(!editor->lpOleCallback || !editor->hWnd)
3015 return FALSE;
3016 ME_GetSelectionOfs(editor, &selrange.cpMin, &selrange.cpMax);
3017 seltype = ME_GetSelectionType(editor);
3018 if(SUCCEEDED(IRichEditOleCallback_GetContextMenu(editor->lpOleCallback, seltype, NULL, &selrange, &menu)))
3019 {
3020 TrackPopupMenu(menu, TPM_LEFTALIGN | TPM_RIGHTBUTTON, x, y, 0, editor->hwndParent, NULL);
3021 DestroyMenu(menu);
3022 }
3023 return TRUE;
3024 }
3025
3026 ME_TextEditor *ME_MakeEditor(ITextHost *texthost, BOOL bEmulateVersion10)
3027 {
3028 ME_TextEditor *ed = heap_alloc(sizeof(*ed));
3029 int i;
3030 DWORD props;
3031 LONG selbarwidth;
3032
3033 ed->hWnd = NULL;
3034 ed->hwndParent = NULL;
3035 ed->sizeWindow.cx = ed->sizeWindow.cy = 0;
3036 ed->texthost = texthost;
3037 ed->reOle = NULL;
3038 ed->bEmulateVersion10 = bEmulateVersion10;
3039 ed->styleFlags = 0;
3040 ed->exStyleFlags = 0;
3041 ITextHost_TxGetPropertyBits(texthost,
3042 (TXTBIT_RICHTEXT|TXTBIT_MULTILINE|
3043 TXTBIT_READONLY|TXTBIT_USEPASSWORD|
3044 TXTBIT_HIDESELECTION|TXTBIT_SAVESELECTION|
3045 TXTBIT_AUTOWORDSEL|TXTBIT_VERTICAL|
3046 TXTBIT_WORDWRAP|TXTBIT_DISABLEDRAG),
3047 &props);
3048 ITextHost_TxGetScrollBars(texthost, &ed->styleFlags);
3049 ed->styleFlags &= (WS_VSCROLL|WS_HSCROLL|ES_AUTOVSCROLL|
3050 ES_AUTOHSCROLL|ES_DISABLENOSCROLL);
3051 ed->pBuffer = ME_MakeText();
3052 ed->nZoomNumerator = ed->nZoomDenominator = 0;
3053 ed->nAvailWidth = 0; /* wrap to client area */
3054 ME_MakeFirstParagraph(ed);
3055 /* The four cursors are for:
3056 * 0 - The position where the caret is shown
3057 * 1 - The anchored end of the selection (for normal selection)
3058 * 2 & 3 - The anchored start and end respectively for word, line,
3059 * or paragraph selection.
3060 */
3061 ed->nCursors = 4;
3062 ed->pCursors = heap_alloc(ed->nCursors * sizeof(*ed->pCursors));
3063 ME_SetCursorToStart(ed, &ed->pCursors[0]);
3064 ed->pCursors[1] = ed->pCursors[0];
3065 ed->pCursors[2] = ed->pCursors[0];
3066 ed->pCursors[3] = ed->pCursors[1];
3067 ed->nLastTotalLength = ed->nTotalLength = 0;
3068 ed->nLastTotalWidth = ed->nTotalWidth = 0;
3069 ed->nUDArrowX = -1;
3070 ed->rgbBackColor = -1;
3071 ed->hbrBackground = GetSysColorBrush(COLOR_WINDOW);
3072 ed->bCaretAtEnd = FALSE;
3073 ed->nEventMask = 0;
3074 ed->nModifyStep = 0;
3075 ed->nTextLimit = TEXT_LIMIT_DEFAULT;
3076 list_init( &ed->undo_stack );
3077 list_init( &ed->redo_stack );
3078 ed->nUndoStackSize = 0;
3079 ed->nUndoLimit = STACK_SIZE_DEFAULT;
3080 ed->nUndoMode = umAddToUndo;
3081 ed->nParagraphs = 1;
3082 ed->nLastSelStart = ed->nLastSelEnd = 0;
3083 ed->pLastSelStartPara = ed->pLastSelEndPara = ed->pCursors[0].pPara;
3084 ed->bHideSelection = FALSE;
3085 ed->pfnWordBreak = NULL;
3086 ed->lpOleCallback = NULL;
3087 ed->mode = TM_MULTILEVELUNDO | TM_MULTICODEPAGE;
3088 ed->mode |= (props & TXTBIT_RICHTEXT) ? TM_RICHTEXT : TM_PLAINTEXT;
3089 ed->AutoURLDetect_bEnable = FALSE;
3090 ed->bHaveFocus = FALSE;
3091 ed->bDialogMode = FALSE;
3092 ed->bMouseCaptured = FALSE;
3093 for (i=0; i<HFONT_CACHE_SIZE; i++)
3094 {
3095 ed->pFontCache[i].nRefs = 0;
3096 ed->pFontCache[i].nAge = 0;
3097 ed->pFontCache[i].hFont = NULL;
3098 }
3099
3100 ME_CheckCharOffsets(ed);
3101 SetRectEmpty(&ed->rcFormat);
3102 ed->bDefaultFormatRect = TRUE;
3103 ITextHost_TxGetSelectionBarWidth(ed->texthost, &selbarwidth);
3104 if (selbarwidth) {
3105 /* FIXME: Convert selbarwidth from HIMETRIC to pixels */
3106 ed->selofs = SELECTIONBAR_WIDTH;
3107 ed->styleFlags |= ES_SELECTIONBAR;
3108 } else {
3109 ed->selofs = 0;
3110 }
3111 ed->nSelectionType = stPosition;
3112
3113 ed->cPasswordMask = 0;