c0843dd4e5cac6fd6b3124f22faf407d684c9696
[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 BOOL ME_ShowContextMenu(ME_TextEditor *editor, int x, int y)
2968 {
2969 CHARRANGE selrange;
2970 HMENU menu;
2971 int seltype = 0;
2972 if(!editor->lpOleCallback || !editor->hWnd)
2973 return FALSE;
2974 ME_GetSelectionOfs(editor, &selrange.cpMin, &selrange.cpMax);
2975 if(selrange.cpMin == selrange.cpMax)
2976 seltype |= SEL_EMPTY;
2977 else
2978 {
2979 /* FIXME: Handle objects */
2980 seltype |= SEL_TEXT;
2981 if(selrange.cpMax-selrange.cpMin > 1)
2982 seltype |= SEL_MULTICHAR;
2983 }
2984 if(SUCCEEDED(IRichEditOleCallback_GetContextMenu(editor->lpOleCallback, seltype, NULL, &selrange, &menu)))
2985 {
2986 TrackPopupMenu(menu, TPM_LEFTALIGN | TPM_RIGHTBUTTON, x, y, 0, editor->hwndParent, NULL);
2987 DestroyMenu(menu);
2988 }
2989 return TRUE;
2990 }
2991
2992 ME_TextEditor *ME_MakeEditor(ITextHost *texthost, BOOL bEmulateVersion10)
2993 {
2994 ME_TextEditor *ed = heap_alloc(sizeof(*ed));
2995 int i;
2996 DWORD props;
2997 LONG selbarwidth;
2998
2999 ed->hWnd = NULL;
3000 ed->hwndParent = NULL;
3001 ed->sizeWindow.cx = ed->sizeWindow.cy = 0;
3002 ed->texthost = texthost;
3003 ed->reOle = NULL;
3004 ed->bEmulateVersion10 = bEmulateVersion10;
3005 ed->styleFlags = 0;
3006 ed->exStyleFlags = 0;
3007 ITextHost_TxGetPropertyBits(texthost,
3008 (TXTBIT_RICHTEXT|TXTBIT_MULTILINE|
3009 TXTBIT_READONLY|TXTBIT_USEPASSWORD|
3010 TXTBIT_HIDESELECTION|TXTBIT_SAVESELECTION|
3011 TXTBIT_AUTOWORDSEL|TXTBIT_VERTICAL|
3012 TXTBIT_WORDWRAP|TXTBIT_DISABLEDRAG),
3013 &props);
3014 ITextHost_TxGetScrollBars(texthost, &ed->styleFlags);
3015 ed->styleFlags &= (WS_VSCROLL|WS_HSCROLL|ES_AUTOVSCROLL|
3016 ES_AUTOHSCROLL|ES_DISABLENOSCROLL);
3017 ed->pBuffer = ME_MakeText();
3018 ed->nZoomNumerator = ed->nZoomDenominator = 0;
3019 ed->nAvailWidth = 0; /* wrap to client area */
3020 ME_MakeFirstParagraph(ed);
3021 /* The four cursors are for:
3022 * 0 - The position where the caret is shown
3023 * 1 - The anchored end of the selection (for normal selection)
3024 * 2 & 3 - The anchored start and end respectively for word, line,
3025 * or paragraph selection.
3026 */
3027 ed->nCursors = 4;
3028 ed->pCursors = heap_alloc(ed->nCursors * sizeof(*ed->pCursors));
3029 ME_SetCursorToStart(ed, &ed->pCursors[0]);
3030 ed->pCursors[1] = ed->pCursors[0];
3031 ed->pCursors[2] = ed->pCursors[0];
3032 ed->pCursors[3] = ed->pCursors[1];
3033 ed->nLastTotalLength = ed->nTotalLength = 0;
3034 ed->nLastTotalWidth = ed->nTotalWidth = 0;
3035 ed->nUDArrowX = -1;
3036 ed->rgbBackColor = -1;
3037 ed->hbrBackground = GetSysColorBrush(COLOR_WINDOW);
3038 ed->bCaretAtEnd = FALSE;
3039 ed->nEventMask = 0;
3040 ed->nModifyStep = 0;
3041 ed->nTextLimit = TEXT_LIMIT_DEFAULT;
3042 list_init( &ed->undo_stack );
3043 list_init( &ed->redo_stack );
3044 ed->nUndoStackSize = 0;
3045 ed->nUndoLimit = STACK_SIZE_DEFAULT;
3046 ed->nUndoMode = umAddToUndo;
3047 ed->nParagraphs = 1;
3048 ed->nLastSelStart = ed->nLastSelEnd = 0;
3049 ed->pLastSelStartPara = ed->pLastSelEndPara = ed->pCursors[0].pPara;
3050 ed->bHideSelection = FALSE;
3051 ed->pfnWordBreak = NULL;
3052 ed->lpOleCallback = NULL;
3053 ed->mode = TM_MULTILEVELUNDO | TM_MULTICODEPAGE;
3054 ed->mode |= (props & TXTBIT_RICHTEXT) ? TM_RICHTEXT : TM_PLAINTEXT;
3055 ed->AutoURLDetect_bEnable = FALSE;
3056 ed->bHaveFocus = FALSE;
3057 ed->bDialogMode = FALSE;
3058 ed->bMouseCaptured = FALSE;
3059 for (i=0; i<HFONT_CACHE_SIZE; i++)
3060 {
3061 ed->pFontCache[i].nRefs = 0;
3062 ed->pFontCache[i].nAge = 0;
3063 ed->pFontCache[i].hFont = NULL;
3064 }
3065
3066 ME_CheckCharOffsets(ed);
3067 SetRectEmpty(&ed->rcFormat);
3068 ed->bDefaultFormatRect = TRUE;
3069 ITextHost_TxGetSelectionBarWidth(ed->texthost, &selbarwidth);
3070 if (selbarwidth) {
3071 /* FIXME: Convert selbarwidth from HIMETRIC to pixels */
3072 ed->selofs = SELECTIONBAR_WIDTH;
3073 ed->styleFlags |= ES_SELECTIONBAR;
3074 } else {
3075 ed->selofs = 0;
3076 }
3077 ed->nSelectionType = stPosition;
3078
3079 ed->cPasswordMask = 0;
3080 if (props & TXTBIT_USEPASSWORD)
3081 ITextHost_TxGetPasswordChar(texthost, &ed->cPasswordMask);
3082
3083 if (props & TXTBIT_AUTOWORDSEL)
3084 ed->styleFlags |= ECO_AUTOWORDSELECTION;
3085 if (props & TXTBIT_MULTILINE) {
3086 ed->styleFlags |= ES_MULTILINE;
3087 ed->bWordWrap = (props & TXTBIT_WORDWRAP) != 0;
3088 } else {
3089 ed->bWordWrap = FALSE;
3090 }
3091 if (props & TXTBIT_READONLY)
3092 ed->styleFlags |= ES_READONLY;
3093 if (!(props & TXTBIT_HIDESELECTION))
3094 ed->styleFlags |= ES_NOHIDESEL;
3095 if (props & TXTBIT_SAVESELECTION)
3096 ed->styleFlags |= ES_SAVESEL;
3097 if (props & TXTBIT_VERTICAL)
3098 ed->styleFlags |= ES_VERTICAL;
3099 if (props & TXTBIT_DISABLEDRAG)
3100 ed->styleFlags |= ES_NOOLEDRAGDROP;
3101
3102 ed->notified_cr.cpMin = ed->notified_cr.cpMax = 0;
3103
3104 /* Default scrollbar information */
3105 ed->vert_si.cbSize = sizeof(SCROLLINFO);
3106 ed->vert_si.nMin = 0;
3107 ed->vert_si.nMax = 0;
3108 ed->vert_si.nPage = 0;
3109 ed->vert_si.nPos = 0;
3110
3111 ed->horz_si.cbSize = sizeof(SCROLLINFO);
3112 ed->horz_si.nMin = 0;
3113 ed->horz_si.nMax = 0;
3114 ed->horz_si.nPage = 0;
3115 ed->horz_si.nPos = 0;
3116
3117 ed->wheel_remain = 0;
3118
3119 list_init( &ed->style_list );
3120 OleInitialize(NULL);
3121
3122 return ed;
3123 }
3124
3125 void ME_DestroyEditor(ME_TextEditor *editor)
3126 {
3127 ME_DisplayItem *pFirst = editor->pBuffer->pFirst;
3128 ME_DisplayItem *p = pFirst, *pNext = NULL;
3129 ME_Style *s, *cursor2;
3130 int i;
3131
3132 ME_ClearTempStyle(editor);
3133 ME_EmptyUndoStack(editor);
3134 while(p) {
3135 pNext = p->next;
3136 ME_DestroyDisplayItem(p);
3137 p = pNext;
3138 }
3139
3140 LIST_FOR_EACH_ENTRY_SAFE( s, cursor2, &editor->style_list, ME_Style, entry )
3141 ME_DestroyStyle( s );
3142
3143 ME_ReleaseStyle(editor->pBuffer->pDefaultStyle);
3144 for (i=0; i<HFONT_CACHE_SIZE; i++)
3145 {
3146 if (editor->pFontCache[i].hFont)
3147 DeleteObject(editor->pFontCache[i].hFont);
3148 }
3149 if (editor->rgbBackColor != -1)
3150 DeleteObject(editor->hbrBackground);
3151 if(editor->lpOleCallback)
3152 IRichEditOleCallback_Release(editor->lpOleCallback);
3153 ITextHost_Release(editor->texthost);
3154 if (editor->reOle)
3155 {
3156 IRichEditOle_Release(editor->reOle);
3157 editor->reOle = NULL;
3158 }
3159 OleUninitialize();
3160
3161 heap_free(editor->pBuffer);
3162 heap_free(editor->pCursors);
3163 heap_free(editor);
3164 }
3165
3166 BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
3167 {
3168 TRACE("\n");
3169 switch (fdwReason)
3170 {
3171 case DLL_PROCESS_ATTACH:
3172 DisableThreadLibraryCalls(hinstDLL);
3173 me_heap = HeapCreate (0, 0x10000, 0);
3174 if (!ME_RegisterEditorClass(hinstDLL)) return FALSE;
3175 hLeft = LoadCursorW(hinstDLL, MAKEINTRESOURCEW(OCR_REVERSE));
3176 LookupInit();
3177 break;
3178
3179 case DLL_PROCESS_DETACH:
3180 if (lpvReserved) break;
3181 UnregisterClassW(RICHEDIT_CLASS20W, 0);
3182 UnregisterClassW(MSFTEDIT_CLASS, 0);
3183 UnregisterClassA(RICHEDIT_CLASS20A, 0);
3184 UnregisterClassA("RichEdit50A", 0);
3185 if (ME_ListBoxRegistered)
3186 UnregisterClassW(REListBox20W, 0);
3187 if (ME_ComboBoxRegistered)
3188 UnregisterClassW(REComboBox20W, 0);
3189 LookupCleanup();
3190 HeapDestroy (me_heap);
3191 release_typelib();
3192 break;
3193 }
3194 return TRUE;
3195 }
3196
3197 static inline int get_default_line_height( ME_TextEditor *editor )
3198 {
3199 int height = 0;
3200
3201 if (editor->pBuffer && editor->pBuffer->pDefaultStyle)
3202 height = editor->pBuffer->pDefaultStyle->tm.tmHeight;
3203 if (height <= 0) height = 24;
3204
3205 return height;
3206 }
3207
3208 static inline int calc_wheel_change( int *remain, int amount_per_click )
3209 {
3210 int change = amount_per_click * (float)*remain / WHEEL_DELTA;
3211 *remain -= WHEEL_DELTA * change / amount_per_click;
3212 return change;
3213 }
3214
3215 static const char * const edit_messages[] = {
3216 "EM_GETSEL",
3217 "EM_SETSEL",
3218 "EM_GETRECT",
3219 "EM_SETRECT",
3220 "EM_SETRECTNP",
3221 "EM_SCROLL",
3222 "EM_LINESCROLL",
3223 "EM_SCROLLCARET",
3224 "EM_GETMODIFY",
3225 "EM_SETMODIFY",
3226 "EM_GETLINECOUNT",
3227 "EM_LINEINDEX",
3228 "EM_SETHANDLE",
3229 "EM_GETHANDLE",
3230 "EM_GETTHUMB",
3231 "EM_UNKNOWN_BF",
3232 "EM_UNKNOWN_C0",
3233 "EM_LINELENGTH",
3234 "EM_REPLACESEL",
3235 "EM_UNKNOWN_C3",
3236 "EM_GETLINE",
3237 "EM_LIMITTEXT",
3238 "EM_CANUNDO",
3239 "EM_UNDO",
3240 "EM_FMTLINES",
3241 "EM_LINEFROMCHAR",
3242 "EM_UNKNOWN_CA",
3243 "EM_SETTABSTOPS",
3244 "EM_SETPASSWORDCHAR",
3245 "EM_EMPTYUNDOBUFFER",
3246 "EM_GETFIRSTVISIBLELINE",
3247 "EM_SETREADONLY",
3248 "EM_SETWORDBREAKPROC",
3249 "EM_GETWORDBREAKPROC",
3250 "EM_GETPASSWORDCHAR",
3251 "EM_SETMARGINS",
3252 "EM_GETMARGINS",
3253 "EM_GETLIMITTEXT",
3254 "EM_POSFROMCHAR",
3255 "EM_CHARFROMPOS",
3256 "EM_SETIMESTATUS",
3257 "EM_GETIMESTATUS"
3258 };
3259
3260 static const char * const richedit_messages[] = {
3261 "EM_CANPASTE",
3262 "EM_DISPLAYBAND",
3263 "EM_EXGETSEL",
3264 "EM_EXLIMITTEXT",
3265 "EM_EXLINEFROMCHAR",
3266 "EM_EXSETSEL",
3267 "EM_FINDTEXT",
3268 "EM_FORMATRANGE",
3269 "EM_GETCHARFORMAT",
3270 "EM_GETEVENTMASK",
3271 "EM_GETOLEINTERFACE",
3272 "EM_GETPARAFORMAT",
3273 "EM_GETSELTEXT",
3274 "EM_HIDESELECTION",
3275 "EM_PASTESPECIAL",
3276 "EM_REQUESTRESIZE",
3277 "EM_SELECTIONTYPE",
3278 "EM_SETBKGNDCOLOR",
3279 "EM_SETCHARFORMAT",
3280 "EM_SETEVENTMASK",
3281 "EM_SETOLECALLBACK",
3282 "EM_SETPARAFORMAT",
3283 "EM_SETTARGETDEVICE",
3284 "EM_STREAMIN",
3285 "EM_STREAMOUT",
3286 "EM_GETTEXTRANGE",
3287 "EM_FINDWORDBREAK",
3288 "EM_SETOPTIONS",
3289 "EM_GETOPTIONS",
3290 "EM_FINDTEXTEX",
3291 "EM_GETWORDBREAKPROCEX",
3292 "EM_SETWORDBREAKPROCEX",
3293 "EM_SETUNDOLIMIT",
3294 "EM_UNKNOWN_USER_83",
3295 "EM_REDO",
3296 "EM_CANREDO",
3297 "EM_GETUNDONAME",
3298 "EM_GETREDONAME",
3299 "EM_STOPGROUPTYPING",
3300 "EM_SETTEXTMODE",
3301 "EM_GETTEXTMODE",
3302 "EM_AUTOURLDETECT",
3303 "EM_GETAUTOURLDETECT",
3304 "EM_SETPALETTE",
3305 "EM_GETTEXTEX",
3306 "EM_GETTEXTLENGTHEX",
3307 "EM_SHOWSCROLLBAR",
3308 "EM_SETTEXTEX",
3309 "EM_UNKNOWN_USER_98",
3310 "EM_UNKNOWN_USER_99",
3311 "EM_SETPUNCTUATION",
3312 "EM_GETPUNCTUATION",
3313 "EM_SETWORDWRAPMODE",
3314 "EM_GETWORDWRAPMODE",
3315 "EM_SETIMECOLOR",
3316 "EM_GETIMECOLOR",
3317 "EM_SETIMEOPTIONS",
3318 "EM_GETIMEOPTIONS",
3319 "EM_CONVPOSITION",
3320 "EM_UNKNOWN_USER_109",
3321 "EM_UNKNOWN_USER_110",
3322 "EM_UNKNOWN_USER_111",
3323 "EM_UNKNOWN_USER_112",
3324 "EM_UNKNOWN_USER_113",
3325 "EM_UNKNOWN_USER_114",
3326 "EM_UNKNOWN_USER_115",
3327 "EM_UNKNOWN_USER_116",
3328 "EM_UNKNOWN_USER_117",
3329 "EM_UNKNOWN_USER_118",
3330 "EM_UNKNOWN_USER_119",
3331 "EM_SETLANGOPTIONS",
3332 "EM_GETLANGOPTIONS",
3333 "EM_GETIMECOMPMODE",
3334 "EM_FINDTEXTW",
3335 "EM_FINDTEXTEXW",
3336 "EM_RECONVERSION",
3337 "EM_SETIMEMODEBIAS",
3338 "EM_GETIMEMODEBIAS"
3339 };
3340
3341 static const char *
3342 get_msg_name(UINT msg)
3343 {
3344 if (msg >= EM_GETSEL && msg <= EM_CHARFROMPOS)
3345 return edit_messages[msg - EM_GETSEL];
3346 if (msg >= EM_CANPASTE && msg <= EM_GETIMEMODEBIAS)
3347 return richedit_messages[msg - EM_CANPASTE];
3348 return "";
3349 }
3350
3351 static void ME_LinkNotify(ME_TextEditor *editor, UINT msg, WPARAM wParam, LPARAM lParam)
3352 {
3353 int x,y;
3354 BOOL isExact;
3355 ME_Cursor cursor; /* The start of the clicked text. */
3356
3357 ENLINK info;
3358 x = (short)LOWORD(lParam);
3359 y = (short)HIWORD(lParam);
3360 ME_CharFromPos(editor, x, y, &cursor, &isExact);
3361 if (!isExact) return;
3362
3363 if (is_link( &cursor.pRun->member.run ))
3364 { /* The clicked run has CFE_LINK set */
3365 ME_DisplayItem *di;
3366
3367 info.nmhdr.hwndFrom = NULL;
3368 info.nmhdr.idFrom = 0;
3369 info.nmhdr.code = EN_LINK;
3370 info.msg = msg;
3371 info.wParam = wParam;
3372 info.lParam = lParam;
3373 cursor.nOffset = 0;
3374
3375 /* find the first contiguous run with CFE_LINK set */
3376 info.chrg.cpMin = ME_GetCursorOfs(&cursor);
3377 di = cursor.pRun;
3378 while (ME_PrevRun( NULL, &di, FALSE ) && is_link( &di->member.run ))
3379 info.chrg.cpMin -= di->member.run.len;
3380
3381 /* find the last contiguous run with CFE_LINK set */
3382 info.chrg.cpMax = ME_GetCursorOfs(&cursor) + cursor.pRun->member.run.len;
3383 di = cursor.pRun;
3384 while (ME_NextRun( NULL, &di, FALSE ) && is_link( &di->member.run ))
3385 info.chrg.cpMax += di->member.run.len;
3386
3387 ITextHost_TxNotify(editor->texthost, info.nmhdr.code, &info);
3388 }
3389 }
3390
3391 void ME_ReplaceSel(ME_TextEditor *editor, BOOL can_undo, const WCHAR *str, int len)
3392 {
3393 int from, to, nStartCursor;
3394 ME_Style *style;
3395
3396 nStartCursor = ME_GetSelectionOfs(editor, &from, &to);
3397 style = ME_GetSelectionInsertStyle(editor);
3398 ME_InternalDeleteText(editor, &editor->pCursors[nStartCursor], to-from, FALSE);
3399 ME_InsertTextFromCursor(editor, 0, str, len, style);
3400 ME_ReleaseStyle(style);
3401 /* drop temporary style if line end */
3402 /*
3403 * FIXME question: does abc\n mean: put abc,
3404 * clear temp style, put \n? (would require a change)
3405 */
3406 if (len>0 && str[len-1] == '\n')
3407 ME_ClearTempStyle(editor);
3408 ME_CommitUndo(editor);
3409 ME_UpdateSelectionLinkAttribute(editor);
3410 if (!can_undo)
3411 ME_EmptyUndoStack(editor);
3412 ME_UpdateRepaint(editor, FALSE);
3413 }
3414
3415 static void ME_SetText(ME_TextEditor *editor, void *text, BOOL unicode)
3416 {
3417 LONG codepage = unicode ? CP_UNICODE : CP_ACP;
3418 int textLen;
3419
3420 LPWSTR wszText = ME_ToUnicode(codepage, text, &textLen);
3421 ME_InsertTextFromCursor(editor, 0, wszText, textLen, editor->pBuffer->pDefaultStyle);
3422 ME_EndToUnicode(codepage, wszText);
3423 }
3424
3425 static LRESULT ME_WmCreate(ME_TextEditor *editor, LPARAM lParam, BOOL unicode)
3426 {
3427 CREATESTRUCTW *createW = (CREATESTRUCTW*)lParam;
3428 CREATESTRUCTA *createA = (CREATESTRUCTA*)lParam;
3429 void *text = NULL;
3430 INT max;
3431
3432 if (lParam)
3433 text = unicode ? (void*)createW->lpszName : (void*)createA->lpszName;
3434
3435 ME_SetDefaultFormatRect(editor);
3436
3437 max = (editor->styleFlags & ES_DISABLENOSCROLL) ? 1 : 0;
3438 if (~editor->styleFlags & ES_DISABLENOSCROLL || editor->styleFlags & WS_VSCROLL)
3439 ITextHost_TxSetScrollRange(editor->texthost, SB_VERT, 0, max, TRUE);
3440
3441 if (~editor->styleFlags & ES_DISABLENOSCROLL || editor->styleFlags & WS_HSCROLL)
3442 ITextHost_TxSetScrollRange(editor->texthost, SB_HORZ, 0, max, TRUE);
3443
3444 if (editor->styleFlags & ES_DISABLENOSCROLL)
3445 {
3446 if (editor->styleFlags & WS_VSCROLL)
3447 {
3448 ITextHost_TxEnableScrollBar(editor->texthost, SB_VERT, ESB_DISABLE_BOTH);
3449 ITextHost_TxShowScrollBar(editor->texthost, SB_VERT, TRUE);
3450 }
3451 if (editor->styleFlags & WS_HSCROLL)
3452 {
3453 ITextHost_TxEnableScrollBar(editor->texthost, SB_HORZ, ESB_DISABLE_BOTH);
3454 ITextHost_TxShowScrollBar(editor->texthost, SB_HORZ, TRUE);
3455 }
3456 }
3457
3458 if (text)
3459 {
3460 ME_SetText(editor, text, unicode);
3461 ME_SetCursorToStart(editor, &editor->pCursors[0]);
3462 ME_SetCursorToStart(editor, &editor->pCursors[1]);
3463 }
3464
3465 ME_CommitUndo(editor);
3466 ME_WrapMarkedParagraphs(editor);
3467 ME_MoveCaret(editor);
3468 return 0;
3469 }
3470
3471
3472 #define UNSUPPORTED_MSG(e) \
3473 case e: \
3474 FIXME(#e ": stub\n"); \
3475 *phresult = S_FALSE; \
3476 return 0;
3477
3478 /* Handle messages for windowless and windowed richedit controls.
3479 *
3480 * The LRESULT that is returned is a return value for window procs,
3481 * and the phresult parameter is the COM return code needed by the
3482 * text services interface. */
3483 LRESULT ME_HandleMessage(ME_TextEditor *editor, UINT msg, WPARAM wParam,
3484 LPARAM lParam, BOOL unicode, HRESULT* phresult)
3485 {
3486 *phresult = S_OK;
3487
3488 switch(msg) {
3489
3490 UNSUPPORTED_MSG(EM_DISPLAYBAND)
3491 UNSUPPORTED_MSG(EM_FINDWORDBREAK)
3492 UNSUPPORTED_MSG(EM_FMTLINES)
3493 UNSUPPORTED_MSG(EM_FORMATRANGE)
3494 UNSUPPORTED_MSG(EM_GETBIDIOPTIONS)
3495 UNSUPPORTED_MSG(EM_GETEDITSTYLE)
3496 UNSUPPORTED_MSG(EM_GETIMECOMPMODE)
3497 UNSUPPORTED_MSG(EM_GETIMESTATUS)
3498 UNSUPPORTED_MSG(EM_SETIMESTATUS)
3499 UNSUPPORTED_MSG(EM_GETLANGOPTIONS)
3500 UNSUPPORTED_MSG(EM_GETREDONAME)
3501 UNSUPPORTED_MSG(EM_GETTYPOGRAPHYOPTIONS)
3502 UNSUPPORTED_MSG(EM_GETUNDONAME)
3503 UNSUPPORTED_MSG(EM_GETWORDBREAKPROCEX)
3504 UNSUPPORTED_MSG(EM_SELECTIONTYPE)
3505 UNSUPPORTED_MSG(EM_SETBIDIOPTIONS)
3506 UNSUPPORTED_MSG(EM_SETEDITSTYLE)
3507 UNSUPPORTED_MSG(EM_SETLANGOPTIONS)
3508 UNSUPPORTED_MSG(EM_SETMARGINS)
3509 UNSUPPORTED_MSG(EM_SETPALETTE)
3510 UNSUPPORTED_MSG(EM_SETTABSTOPS)
3511 UNSUPPORTED_MSG(EM_SETTYPOGRAPHYOPTIONS)
3512 UNSUPPORTED_MSG(EM_SETWORDBREAKPROCEX)
3513
3514 /* Messages specific to Richedit controls */
3515
3516 case EM_STREAMIN:
3517 return ME_StreamIn(editor, wParam, (EDITSTREAM*)lParam, TRUE);
3518 case EM_STREAMOUT:
3519 return ME_StreamOut(editor, wParam, (EDITSTREAM *)lParam);
3520 case WM_GETDLGCODE:
3521 {
3522 UINT code = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS;
3523
3524 if (lParam)
3525 editor->bDialogMode = TRUE;
3526 if (editor->styleFlags & ES_MULTILINE)
3527 code |= DLGC_WANTMESSAGE;
3528 if (!(editor->styleFlags & ES_SAVESEL))
3529 code |= DLGC_HASSETSEL;
3530 return code;
3531 }
3532 case EM_EMPTYUNDOBUFFER:
3533 ME_EmptyUndoStack(editor);
3534 return 0;
3535 case EM_GETSEL:
3536 {
3537 /* Note: wParam/lParam can be NULL */
3538 UINT from, to;
3539 PUINT pfrom = wParam ? (PUINT)wParam : &from;
3540 PUINT pto = lParam ? (PUINT)lParam : &to;
3541 ME_GetSelectionOfs(editor, (int *)pfrom, (int *)pto);
3542 if ((*pfrom|*pto) & 0xFFFF0000)
3543 return -1;
3544 return MAKELONG(*pfrom,*pto);
3545 }
3546 case EM_EXGETSEL:
3547 {
3548 CHARRANGE *pRange = (CHARRANGE *)lParam;
3549 ME_GetSelectionOfs(editor, &pRange->cpMin, &pRange->cpMax);
3550 TRACE("EM_EXGETSEL = (%d,%d)\n", pRange->cpMin, pRange->cpMax);
3551 return 0;
3552 }
3553 case EM_SETUNDOLIMIT:
3554 {
3555 if ((int)wParam < 0)
3556 editor->nUndoLimit = STACK_SIZE_DEFAULT;
3557 else
3558 editor->nUndoLimit = min(wParam, STACK_SIZE_MAX);
3559 /* Setting a max stack size keeps wine from getting killed
3560 for hogging memory. Windows allocates all this memory at once, so
3561 no program would realistically set a value above our maximum. */
3562 return editor->nUndoLimit;
3563 }
3564 case EM_CANUNDO:
3565 return !list_empty( &editor->undo_stack );
3566 case EM_CANREDO:
3567 return !list_empty( &editor->redo_stack );
3568 case WM_UNDO: /* FIXME: actually not the same */
3569 case EM_UNDO:
3570 return ME_Undo(editor);
3571 case EM_REDO:
3572 return ME_Redo(editor);
3573 case EM_GETOPTIONS:
3574 {
3575 /* these flags are equivalent to the ES_* counterparts */
3576 DWORD mask = ECO_VERTICAL | ECO_AUTOHSCROLL | ECO_AUTOVSCROLL |
3577 ECO_NOHIDESEL | ECO_READONLY | ECO_WANTRETURN | ECO_SELECTIONBAR;
3578 DWORD settings = editor->styleFlags & mask;
3579
3580 return settings;
3581 }
3582 case EM_SETFONTSIZE:
3583 {
3584 CHARFORMAT2W cf;
3585 LONG tmp_size, size;
3586 BOOL is_increase = ((LONG)wParam > 0);
3587
3588 if (editor->mode & TM_PLAINTEXT)
3589 return FALSE;
3590
3591 cf.cbSize = sizeof(cf);
3592 cf.dwMask = CFM_SIZE;
3593 ME_GetSelectionCharFormat(editor, &cf);
3594 tmp_size = (cf.yHeight / 20) + wParam;
3595
3596 if (tmp_size <= 1)
3597 size = 1;
3598 else if (tmp_size > 12 && tmp_size < 28 && tmp_size % 2)
3599 size = tmp_size + (is_increase ? 1 : -1);
3600 else if (tmp_size > 28 && tmp_size < 36)
3601 size = is_increase ? 36 : 28;
3602 else if (tmp_size > 36 && tmp_size < 48)
3603 size = is_increase ? 48 : 36;
3604 else if (tmp_size > 48 && tmp_size < 72)
3605 size = is_increase ? 72 : 48;
3606 else if (tmp_size > 72 && tmp_size < 80)
3607 size = is_increase ? 80 : 72;
3608 else if (tmp_size > 80 && tmp_size < 1638)
3609 size = 10 * (is_increase ? (tmp_size / 10 + 1) : (tmp_size / 10));
3610 else if (tmp_size >= 1638)
3611 size = 1638;
3612 else
3613 size = tmp_size;
3614
3615 cf.yHeight = size * 20; /* convert twips to points */
3616 ME_SetSelectionCharFormat(editor, &cf);
3617 ME_CommitUndo(editor);
3618 ME_WrapMarkedParagraphs(editor);
3619 ME_UpdateScrollBar(editor);
3620 ME_Repaint(editor);
3621
3622 return TRUE;
3623 }
3624 case EM_SETOPTIONS:
3625 {
3626 /* these flags are equivalent to ES_* counterparts, except for
3627 * ECO_AUTOWORDSELECTION that doesn't have an ES_* counterpart,
3628 * but is still stored in editor->styleFlags. */
3629 const DWORD mask = ECO_VERTICAL | ECO_AUTOHSCROLL | ECO_AUTOVSCROLL |
3630 ECO_NOHIDESEL | ECO_READONLY | ECO_WANTRETURN |
3631 ECO_SELECTIONBAR | ECO_AUTOWORDSELECTION;
3632 DWORD settings = mask & editor->styleFlags;
3633 DWORD oldSettings = settings;
3634 DWORD changedSettings;
3635
3636 switch(wParam)
3637 {
3638 case ECOOP_SET:
3639 settings = lParam;
3640 break;
3641 case ECOOP_OR:
3642 settings |= lParam;
3643 break;
3644 case ECOOP_AND:
3645 settings &= lParam;
3646 break;
3647 case ECOOP_XOR:
3648 settings ^= lParam;
3649 }
3650 changedSettings = oldSettings ^ settings;
3651
3652 if (changedSettings) {
3653 editor->styleFlags = (editor->styleFlags & ~mask) | (settings & mask);
3654
3655 if (changedSettings & ECO_SELECTIONBAR)
3656 {
3657 ITextHost_TxInvalidateRect(editor->texthost, &editor->rcFormat, TRUE);
3658 if (settings & ECO_SELECTIONBAR) {
3659 assert(!editor->selofs);
3660 editor->selofs = SELECTIONBAR_WIDTH;
3661 editor->rcFormat.left += editor->selofs;
3662 } else {
3663 editor->rcFormat.left -= editor->selofs;
3664 editor->selofs = 0;
3665 }
3666 ME_RewrapRepaint(editor);
3667 }
3668
3669 if ((changedSettings & settings & ES_NOHIDESEL) && !editor->bHaveFocus)
3670 ME_InvalidateSelection( editor );
3671
3672 if (changedSettings & settings & ECO_VERTICAL)
3673 FIXME("ECO_VERTICAL not implemented yet!\n");
3674 if (changedSettings & settings & ECO_AUTOHSCROLL)
3675 FIXME("ECO_AUTOHSCROLL not implemented yet!\n");
3676 if (changedSettings & settings & ECO_AUTOVSCROLL)
3677 FIXME("ECO_AUTOVSCROLL not implemented yet!\n");
3678 if (changedSettings & settings & ECO_WANTRETURN)
3679 FIXME("ECO_WANTRETURN not implemented yet!\n");
3680 if (changedSettings & settings & ECO_AUTOWORDSELECTION)
3681 FIXME("ECO_AUTOWORDSELECTION not implemented yet!\n");
3682 }
3683
3684 return settings;
3685 }
3686 case EM_SETSEL:
3687 {
3688 return handle_EM_EXSETSEL( editor, wParam, lParam );
3689 }
3690 case EM_SETSCROLLPOS:
3691 {
3692 POINT *point = (POINT *)lParam;
3693 ME_ScrollAbs(editor, point->x, point->y);
3694 return 0;
3695 }
3696 case EM_AUTOURLDETECT:
3697 {
3698 if (wParam==1 || wParam ==0)
3699 {
3700 editor->AutoURLDetect_bEnable = (BOOL)wParam;
3701 return 0;
3702 }
3703 return E_INVALIDARG;
3704 }
3705 case EM_GETAUTOURLDETECT:
3706 {
3707 return editor->AutoURLDetect_bEnable;
3708 }
3709 case EM_EXSETSEL:
3710 {
3711 CHARRANGE range = *(CHARRANGE *)lParam;
3712
3713 return handle_EM_EXSETSEL( editor, range.cpMin, range.cpMax );
3714 }
3715 case EM_SHOWSCROLLBAR:
3716 {
3717 DWORD flags;
3718
3719 switch (wParam)
3720 {
3721 case SB_HORZ:
3722 flags = WS_HSCROLL;
3723 break;
3724 case SB_VERT:
3725 flags = WS_VSCROLL;
3726 break;
3727 case SB_BOTH:
3728 flags = WS_HSCROLL|WS_VSCROLL;
3729 break;
3730 default:
3731 return 0;
3732 }
3733
3734 if (lParam) {
3735 editor->styleFlags |= flags;
3736 if (flags & WS_HSCROLL)
3737 ITextHost_TxShowScrollBar(editor->texthost, SB_HORZ,
3738 editor->nTotalWidth > editor->sizeWindow.cx);
3739 if (flags & WS_VSCROLL)
3740 ITextHost_TxShowScrollBar(editor->texthost, SB_VERT,
3741 editor->nTotalLength > editor->sizeWindow.cy);
3742 } else {
3743 editor->styleFlags &= ~flags;
3744 ITextHost_TxShowScrollBar(editor->texthost, wParam, FALSE);
3745 }
3746 return 0;
3747 }
3748 case EM_SETTEXTEX:
3749 {
3750 LPWSTR wszText;
3751 SETTEXTEX *pStruct = (SETTEXTEX *)wParam;
3752 int from, to, len;
3753 ME_Style *style;
3754 BOOL bRtf, bUnicode, bSelection, bUTF8;
3755 int oldModify = editor->nModifyStep;
3756 static const char utf8_bom[] = {0xef, 0xbb, 0xbf};
3757
3758 if (!pStruct) return 0;
3759
3760 /* If we detect ascii rtf at the start of the string,
3761 * we know it isn't unicode. */
3762 bRtf = (lParam && (!strncmp((char *)lParam, "{\\rtf", 5) ||
3763 !strncmp((char *)lParam, "{\\urtf", 6)));
3764 bUnicode = !bRtf && pStruct->codepage == CP_UNICODE;
3765 bUTF8 = (lParam && (!strncmp((char *)lParam, utf8_bom, 3)));
3766
3767 TRACE("EM_SETTEXTEX - %s, flags %d, cp %d\n",
3768 bUnicode ? debugstr_w((LPCWSTR)lParam) : debugstr_a((LPCSTR)lParam),
3769 pStruct->flags, pStruct->codepage);
3770
3771 bSelection = (pStruct->flags & ST_SELECTION) != 0;
3772 if (bSelection) {
3773 int nStartCursor = ME_GetSelectionOfs(editor, &from, &to);
3774 style = ME_GetSelectionInsertStyle(editor);
3775 ME_InternalDeleteText(editor, &editor->pCursors[nStartCursor], to - from, FALSE);
3776 } else {
3777 ME_Cursor start;
3778 ME_SetCursorToStart(editor, &start);
3779 ME_InternalDeleteText(editor, &start, ME_GetTextLength(editor), FALSE);
3780 style = editor->pBuffer->pDefaultStyle;
3781 }
3782
3783 if (bRtf) {
3784 ME_StreamInRTFString(editor, bSelection, (char *)lParam);
3785 if (bSelection) {
3786 /* FIXME: The length returned doesn't include the rtf control
3787 * characters, only the actual text. */
3788 len = lParam ? strlen((char *)lParam) : 0;
3789 }
3790 } else {
3791 if (bUTF8 && !bUnicode) {
3792 wszText = ME_ToUnicode(CP_UTF8, (void *)(lParam+3), &len);
3793 ME_InsertTextFromCursor(editor, 0, wszText, len, style);
3794 ME_EndToUnicode(CP_UTF8, wszText);
3795 } else {
3796 wszText = ME_ToUnicode(pStruct->codepage, (void *)lParam, &len);
3797 ME_InsertTextFromCursor(editor, 0, wszText, len, style);
3798 ME_EndToUnicode(pStruct->codepage, wszText);
3799 }
3800 }
3801
3802 if (bSelection) {
3803 ME_ReleaseStyle(style);
3804 ME_UpdateSelectionLinkAttribute(editor);
3805 } else {
3806 ME_Cursor cursor;
3807 len = 1;
3808 ME_SetCursorToStart(editor, &cursor);
3809 ME_UpdateLinkAttribute(editor, &cursor, INT_MAX);
3810 }
3811 ME_CommitUndo(editor);
3812 if (!(pStruct->flags & ST_KEEPUNDO))
3813 {
3814 editor->nModifyStep = oldModify;
3815 ME_EmptyUndoStack(editor);
3816 }
3817 ME_UpdateRepaint(editor, FALSE);
3818 return len;
3819 }
3820 case EM_SETBKGNDCOLOR:
3821 {
3822 LRESULT lColor;
3823 if (editor->rgbBackColor != -1) {
3824 DeleteObject(editor->hbrBackground);
3825 lColor = editor->rgbBackColor;
3826 }
3827 else lColor = ITextHost_TxGetSysColor(editor->texthost, COLOR_WINDOW);
3828
3829 if (wParam)
3830 {
3831 editor->rgbBackColor = -1;
3832 editor->hbrBackground = GetSysColorBrush(COLOR_WINDOW);
3833 }
3834 else
3835 {
3836 editor->rgbBackColor = lParam;
3837 editor->hbrBackground = CreateSolidBrush(editor->rgbBackColor);
3838 }
3839 ITextHost_TxInvalidateRect(editor->texthost, NULL, TRUE);
3840 return lColor;
3841 }
3842 case EM_GETMODIFY:
3843 return editor->nModifyStep == 0 ? 0 : -1;
3844 case EM_SETMODIFY:
3845 {
3846 if (wParam)
3847 editor->nModifyStep = 1;
3848 else
3849 editor->nModifyStep = 0;
3850
3851 return 0;
3852 }
3853 case EM_SETREADONLY:
3854 {
3855 if (wParam)
3856 editor->styleFlags |= ES_READONLY;
3857 else
3858 editor->styleFlags &= ~ES_READONLY;
3859 return 1;
3860 }
3861 case EM_SETEVENTMASK:
3862 {
3863 DWORD nOldMask = editor->nEventMask;
3864
3865 editor->nEventMask = lParam;
3866 return nOldMask;
3867 }
3868 case EM_GETEVENTMASK:
3869 return editor->nEventMask;
3870 case EM_SETCHARFORMAT:
3871 {
3872 CHARFORMAT2W buf, *p;
3873 BOOL bRepaint = TRUE;
3874 p = ME_ToCF2W(&buf, (CHARFORMAT2W *)lParam);
3875 if (p == NULL) return 0;
3876 if (wParam & SCF_ALL) {
3877 if (editor->mode & TM_PLAINTEXT) {
3878 ME_SetDefaultCharFormat(editor, p);
3879 } else {
3880 ME_Cursor start;
3881 ME_SetCursorToStart(editor, &start);
3882 ME_SetCharFormat(editor, &start, NULL, p);
3883 editor->nModifyStep = 1;
3884 }
3885 } else if (wParam & SCF_SELECTION) {
3886 if (editor->mode & TM_PLAINTEXT)
3887 return 0;
3888 if (wParam & SCF_WORD) {
3889 ME_Cursor start;
3890 ME_Cursor end = editor->pCursors[0];
3891 ME_MoveCursorWords(editor, &end, +1);
3892 start = end;
3893 ME_MoveCursorWords(editor, &start, -1);
3894 ME_SetCharFormat(editor, &start, &end, p);
3895 }
3896 bRepaint = ME_IsSelection(editor);
3897 ME_SetSelectionCharFormat(editor, p);
3898 if (bRepaint) editor->nModifyStep = 1;
3899 } else { /* SCF_DEFAULT */
3900 ME_SetDefaultCharFormat(editor, p);
3901 }
3902 ME_CommitUndo(editor);
3903 if (bRepaint)
3904 {
3905 ME_WrapMarkedParagraphs(editor);
3906 ME_UpdateScrollBar(editor);
3907 ME_Repaint(editor);
3908 }
3909 return 1;
3910 }
3911 case EM_GETCHARFORMAT:
3912 {
3913 CHARFORMAT2W tmp, *dst = (CHARFORMAT2W *)lParam;
3914 if (dst->cbSize != sizeof(CHARFORMATA) &&
3915 dst->cbSize != sizeof(CHARFORMATW) &&
3916 dst->cbSize != sizeof(CHARFORMAT2A) &&
3917 dst->cbSize != sizeof(CHARFORMAT2W))
3918 return 0;
3919 tmp.cbSize = sizeof(tmp);
3920 if (!wParam)
3921 ME_GetDefaultCharFormat(editor, &tmp);
3922 else
3923 ME_GetSelectionCharFormat(editor, &tmp);
3924 ME_CopyToCFAny(dst, &tmp);
3925 return tmp.dwMask;
3926 }
3927 case EM_SETPARAFORMAT:
3928 {
3929 BOOL result = ME_SetSelectionParaFormat(editor, (PARAFORMAT2 *)lParam);
3930 ME_WrapMarkedParagraphs(editor);
3931 ME_UpdateScrollBar(editor);
3932 ME_Repaint(editor);
3933 ME_CommitUndo(editor);
3934 return result;
3935 }
3936 case EM_GETPARAFORMAT:
3937 ME_GetSelectionParaFormat(editor, (PARAFORMAT2 *)lParam);
3938 return ((PARAFORMAT2 *)lParam)->dwMask;
3939 case EM_GETFIRSTVISIBLELINE:
3940 {
3941 ME_DisplayItem *p = editor->pBuffer->pFirst;
3942 int y = editor->vert_si.nPos;
3943 int ypara = 0;
3944 int count = 0;
3945 int ystart, yend;
3946 while(p) {
3947 p = ME_FindItemFwd(p, diStartRowOrParagraphOrEnd);
3948 if (p->type == diTextEnd)
3949 break;
3950 if (p->type == diParagraph) {
3951 ypara = p->member.para.pt.y;
3952 continue;
3953 }
3954 ystart = ypara + p->member.row.pt.y;
3955 yend = ystart + p->member.row.nHeight;
3956 if (y < yend) {
3957 break;
3958 }
3959 count++;
3960 }
3961 return count;
3962 }
3963 case EM_HIDESELECTION:
3964 {
3965 editor->bHideSelection = (wParam != 0);
3966 ME_InvalidateSelection(editor);
3967 return 0;
3968 }
3969 case EM_LINESCROLL:
3970 {
3971 if (!(editor->styleFlags & ES_MULTILINE))
3972 return FALSE;
3973 ME_ScrollDown( editor, lParam * get_default_line_height( editor ) );
3974 return TRUE;
3975 }
3976 case WM_CLEAR:
3977 {
3978 int from, to;
3979 int nStartCursor = ME_GetSelectionOfs(editor, &from, &to);
3980 ME_InternalDeleteText(editor, &editor->pCursors[nStartCursor], to-from, FALSE);
3981 ME_CommitUndo(editor);
3982 ME_UpdateRepaint(editor, TRUE);
3983 return 0;
3984 }
3985 case EM_REPLACESEL:
3986 {
3987 int len = 0;
3988 LONG codepage = unicode ? CP_UNICODE : CP_ACP;
3989 LPWSTR wszText = ME_ToUnicode(codepage, (void *)lParam, &len);
3990
3991 TRACE("EM_REPLACESEL - %s\n", debugstr_w(wszText));
3992
3993 ME_ReplaceSel(editor, !!wParam, wszText, len);
3994 ME_EndToUnicode(codepage, wszText);
3995 return len;
3996 }
3997 case EM_SCROLLCARET:
3998 ME_EnsureVisible(editor, &editor->pCursors[0]);
3999 return 0;
4000 case WM_SETFONT:
4001 {
4002 LOGFONTW lf;
4003 CHARFORMAT2W fmt;
4004 HDC hDC;
4005 BOOL bRepaint = LOWORD(lParam);
4006
4007 if (!wParam)
4008 wParam = (WPARAM)GetStockObject(SYSTEM_FONT);
4009
4010 if (!GetObjectW((HGDIOBJ)wParam, sizeof(LOGFONTW), &lf))
4011 return 0;
4012
4013 hDC = ITextHost_TxGetDC(editor->texthost);
4014 ME_CharFormatFromLogFont(hDC, &lf, &fmt);
4015 ITextHost_TxReleaseDC(editor->texthost, hDC);
4016 if (editor->mode & TM_RICHTEXT) {
4017 ME_Cursor start;
4018 ME_SetCursorToStart(editor, &start);
4019 ME_SetCharFormat(editor, &start, NULL, &fmt);
4020 }
4021 ME_SetDefaultCharFormat(editor, &fmt);
4022
4023 ME_CommitUndo(editor);
4024 ME_MarkAllForWrapping(editor);
4025 ME_WrapMarkedParagraphs(editor);
4026 ME_UpdateScrollBar(editor);
4027 if (bRepaint)
4028 ME_Repaint(editor);
4029 return 0;
4030 }
4031 case WM_SETTEXT:
4032 {
4033 ME_Cursor cursor;
4034 ME_SetCursorToStart(editor, &cursor);
4035 ME_InternalDeleteText(editor, &cursor, ME_GetTextLength(editor), FALSE);
4036 if (lParam)
4037 {
4038 TRACE("WM_SETTEXT lParam==%lx\n",lParam);
4039 if (!strncmp((char *)lParam, "{\\rtf", 5) ||
4040 !strncmp((char *)lParam, "{\\urtf", 6))
4041 {
4042 /* Undocumented: WM_SETTEXT supports RTF text */
4043 ME_StreamInRTFString(editor, 0, (char *)lParam);
4044 }
4045 else
4046 ME_SetText(editor, (void*)lParam, unicode);
4047 }
4048 else
4049 TRACE("WM_SETTEXT - NULL\n");
4050 ME_SetCursorToStart(editor, &cursor);
4051 ME_UpdateLinkAttribute(editor, &cursor, INT_MAX);
4052 ME_SetSelection(editor, 0, 0);
4053 editor->nModifyStep = 0;
4054 ME_CommitUndo(editor);
4055 ME_EmptyUndoStack(editor);
4056 ME_UpdateRepaint(editor, FALSE);
4057 return 1;
4058 }
4059 case EM_CANPASTE:
4060 return paste_special( editor, 0, NULL, TRUE );
4061 case WM_PASTE:
4062 case WM_MBUTTONDOWN:
4063 wParam = 0;
4064 lParam = 0;
4065 /* fall through */
4066 case EM_PASTESPECIAL:
4067 paste_special( editor, wParam, (REPASTESPECIAL *)lParam, FALSE );
4068 return 0;
4069 case WM_CUT:
4070 case WM_COPY:
4071 copy_or_cut(editor, msg == WM_CUT);
4072 return 0;
4073 case WM_GETTEXTLENGTH:
4074 {
4075 GETTEXTLENGTHEX how;
4076
4077 /* CR/LF conversion required in 2.0 mode, verbatim in 1.0 mode */
4078 how.flags = GTL_CLOSE | (editor->bEmulateVersion10 ? 0 : GTL_USECRLF) | GTL_NUMCHARS;
4079 how.codepage = unicode ? CP_UNICODE : CP_ACP;
4080 return ME_GetTextLengthEx(editor, &how);
4081 }
4082 case EM_GETTEXTLENGTHEX:
4083 return ME_GetTextLengthEx(editor, (GETTEXTLENGTHEX *)wParam);
4084 case WM_GETTEXT:
4085 {
4086 GETTEXTEX ex;
4087 ex.cb = wParam * (unicode ? sizeof(WCHAR) : sizeof(CHAR));
4088 ex.flags = GT_USECRLF;
4089 ex.codepage = unicode ? CP_UNICODE : CP_ACP;
4090 ex.lpDefaultChar = NULL;
4091 ex.lpUsedDefChar = NULL;
4092 return ME_GetTextEx(editor, &ex, lParam);
4093 }
4094 case EM_GETTEXTEX:
4095 return ME_GetTextEx(editor, (GETTEXTEX*)wParam, lParam);
4096 case EM_GETSELTEXT:
4097 {
4098 int nFrom, nTo, nStartCur = ME_GetSelectionOfs(editor, &nFrom, &nTo);
4099 ME_Cursor *from = &editor->pCursors[nStartCur];
4100 return ME_GetTextRange(editor, (WCHAR *)lParam, from,
4101 nTo - nFrom, unicode);
4102 }
4103 case EM_GETSCROLLPOS:
4104 {
4105 POINT *point = (POINT *)lParam;
4106 point->x = editor->horz_si.nPos;
4107 point->y = editor->vert_si.nPos;
4108 /* 16-bit scaled value is returned as stored in scrollinfo */
4109 if (editor->horz_si.nMax > 0xffff)
4110 point->x = MulDiv(point->x, 0xffff, editor->horz_si.nMax);
4111 if (editor->vert_si.nMax > 0xffff)
4112 point->y = MulDiv(point->y, 0xffff, editor->vert_si.nMax);
4113 return 1;
4114 }
4115 case EM_GETTEXTRANGE:
4116 {
4117 TEXTRANGEW *rng = (TEXTRANGEW *)lParam;
4118 ME_Cursor start;
4119 int nStart = rng->chrg.cpMin;
4120 int nEnd = rng->chrg.cpMax;
4121 int textlength = ME_GetTextLength(editor);
4122
4123 TRACE("EM_GETTEXTRANGE min=%d max=%d unicode=%d textlength=%d\n",
4124 rng->chrg.cpMin, rng->chrg.cpMax, unicode, textlength);
4125 if (nStart < 0) return 0;
4126 if ((nStart == 0 && nEnd == -1) || nEnd > textlength)
4127 nEnd = textlength;
4128 if (nStart >= nEnd) return 0;
4129
4130 ME_CursorFromCharOfs(editor, nStart, &start);
4131 return ME_GetTextRange(editor, rng->lpstrText, &start, nEnd - nStart, unicode);
4132 }
4133 case EM_GETLINE:
4134 {
4135 ME_DisplayItem *run;
4136 const unsigned int nMaxChars = *(WORD *) lParam;
4137 unsigned int nCharsLeft = nMaxChars;
4138 char *dest = (char *) lParam;
4139 BOOL wroteNull = FALSE;
4140
4141 TRACE("EM_GETLINE: row=%d, nMaxChars=%d (%s)\n", (int) wParam, nMaxChars,
4142 unicode ? "Unicode" : "Ansi");
4143
4144 run = ME_FindRowWithNumber(editor, wParam);
4145 if (run == NULL)
4146 return 0;
4147
4148 while (nCharsLeft && (run = ME_FindItemFwd(run, diRunOrStartRow))
4149 && run->type == diRun)
4150 {
4151 WCHAR *str = get_text( &run->member.run, 0 );
4152 unsigned int nCopy;
4153
4154 nCopy = min(nCharsLeft, run->member.run.len);
4155
4156 if (unicode)
4157 memcpy(dest, str, nCopy * sizeof(WCHAR));
4158 else
4159 nCopy = WideCharToMultiByte(CP_ACP, 0, str, nCopy, dest,
4160 nCharsLeft, NULL, NULL);
4161 dest += nCopy * (unicode ? sizeof(WCHAR) : 1);
4162 nCharsLeft -= nCopy;
4163 }
4164
4165 /* append line termination, space allowing */
4166 if (nCharsLeft > 0)
4167 {
4168 if (unicode)
4169 *((WCHAR *)dest) = '\0';
4170 else
4171 *dest = '\0';
4172 nCharsLeft--;
4173 wroteNull = TRUE;
4174 }
4175
4176 TRACE("EM_GETLINE: got %u characters\n", nMaxChars - nCharsLeft);
4177 return nMaxChars - nCharsLeft - (wroteNull ? 1 : 0);
4178 }
4179 case EM_GETLINECOUNT:
4180 {
4181 ME_DisplayItem *item = editor->pBuffer->pFirst->next;
4182 int nRows = 0;
4183
4184 ME_DisplayItem *prev_para = NULL, *last_para = NULL;
4185
4186 while (item != editor->pBuffer->pLast)
4187 {
4188 assert(item->type == diParagraph);
4189 prev_para = ME_FindItemBack(item, diRun);
4190 if (prev_para) {
4191 assert(prev_para->member.run.nFlags & MERF_ENDPARA);
4192 }
4193 nRows += item->member.para.nRows;
4194 item = item->member.para.next_para;
4195 }
4196 last_para = ME_FindItemBack(item, diRun);
4197 assert(last_para);
4198 assert(last_para->member.run.nFlags & MERF_ENDPARA);
4199 if (editor->bEmulateVersion10 && prev_para &&
4200 last_para->member.run.nCharOfs == 0 &&
4201 prev_para->member.run.len == 1 &&
4202 *get_text( &prev_para->member.run, 0 ) == '\r')
4203 {
4204 /* In 1.0 emulation, the last solitary \r at the very end of the text
4205 (if one exists) is NOT a line break.
4206 FIXME: this is an ugly hack. This should have a more regular model. */
4207 nRows--;
4208 }
4209
4210 TRACE("EM_GETLINECOUNT: nRows==%d\n", nRows);
4211 return max(1, nRows);
4212 }
4213 case EM_LINEFROMCHAR:
4214 {
4215 if (wParam == -1)
4216 return ME_RowNumberFromCharOfs(editor, ME_GetCursorOfs(&editor->pCursors[1]));
4217 else
4218 return ME_RowNumberFromCharOfs(editor, wParam);
4219 }
4220 case EM_EXLINEFROMCHAR:
4221 {
4222 if (lParam == -1)
4223 return ME_RowNumberFromCharOfs(editor, ME_GetCursorOfs(&editor->pCursors[1]));
4224 else
4225 return ME_RowNumberFromCharOfs(editor, lParam);
4226 }
4227 case EM_LINEINDEX:
4228 {
4229 ME_DisplayItem *item, *para;
4230 int nCharOfs;
4231
4232 if (wParam == -1)
4233 item = ME_FindItemBack(editor->pCursors[0].pRun, diStartRow);
4234 else
4235 item = ME_FindRowWithNumber(editor, wParam);
4236 if (!item)
4237 return -1;
4238 para = ME_GetParagraph(item);
4239 item = ME_FindItemFwd(item, diRun);
4240 nCharOfs = para->member.para.nCharOfs + item->member.run.nCharOfs;
4241 TRACE("EM_LINEINDEX: nCharOfs==%d\n", nCharOfs);
4242 return nCharOfs;
4243 }
4244 case EM_LINELENGTH:
4245 {
4246 ME_DisplayItem *item, *item_end;
4247 int nChars = 0, nThisLineOfs = 0, nNextLineOfs = 0;
4248 ME_DisplayItem *para, *run;
4249
4250 if (wParam > ME_GetTextLength(editor))
4251 return 0;
4252 if (wParam == -1)
4253 {
4254 FIXME("EM_LINELENGTH: returning number of unselected characters on lines with selection unsupported.\n");
4255 return 0;
4256 }
4257 ME_RunOfsFromCharOfs(editor, wParam, &para, &run, NULL);
4258 item = ME_RowStart(run);
4259 nThisLineOfs = ME_CharOfsFromRunOfs(editor, para, ME_FindItemFwd(item, diRun), 0);
4260 item_end = ME_FindItemFwd(item, diStartRowOrParagraphOrEnd);
4261 if (item_end->type == diStartRow) {
4262 nNextLineOfs = ME_CharOfsFromRunOfs(editor, para, ME_FindItemFwd(item_end, diRun), 0);
4263 } else {
4264 ME_DisplayItem *endRun = ME_FindItemBack(item_end, diRun);
4265 assert(endRun && endRun->member.run.nFlags & MERF_ENDPARA);
4266 nNextLineOfs = item_end->member.para.nCharOfs - endRun->member.run.len;
4267 }
4268 nChars = nNextLineOfs - nThisLineOfs;
4269 TRACE("EM_LINELENGTH(%ld)==%d\n",wParam, nChars);
4270 return nChars;
4271 }
4272 case EM_EXLIMITTEXT:
4273 {
4274 if ((int)lParam < 0)
4275 return 0;
4276 if (lParam == 0)
4277 editor->nTextLimit = 65536;
4278 else
4279 editor->nTextLimit = (int) lParam;
4280 return 0;
4281 }
4282 case EM_LIMITTEXT:
4283 {
4284 if (wParam == 0)
4285 editor->nTextLimit = 65536;
4286 else
4287 editor->nTextLimit = (int) wParam;
4288 return 0;
4289 }
4290 case EM_GETLIMITTEXT:
4291 {
4292 return editor->nTextLimit;
4293 }
4294 case EM_FINDTEXT:
4295 {
4296 LRESULT r;
4297 if(!unicode){
4298 FINDTEXTA *ft = (FINDTEXTA *)lParam;
4299 int nChars = MultiByteToWideChar(CP_ACP, 0, ft->lpstrText, -1, NULL, 0);
4300 WCHAR *tmp;
4301
4302 if ((tmp = heap_alloc(nChars * sizeof(*tmp))) != NULL)
4303 MultiByteToWideChar(CP_ACP, 0, ft->lpstrText, -1, tmp, nChars);
4304 r = ME_FindText(editor, wParam, &ft->chrg, tmp, NULL);
4305 heap_free(tmp);
4306 }else{
4307 FINDTEXTW *ft = (FINDTEXTW *)lParam;
4308 r = ME_FindText(editor, wParam, &ft->chrg, ft->lpstrText, NULL);
4309 }
4310 return r;
4311 }
4312 case EM_FINDTEXTEX:
4313 {
4314 LRESULT r;
4315 if(!unicode){
4316 FINDTEXTEXA *ex = (FINDTEXTEXA *)lParam;
4317 int nChars = MultiByteToWideChar(CP_ACP, 0, ex->lpstrText, -1, NULL, 0);
4318 WCHAR *tmp;
4319
4320 if ((tmp = heap_alloc(nChars * sizeof(*tmp))) != NULL)
4321 MultiByteToWideChar(CP_ACP, 0, ex->lpstrText, -1, tmp, nChars);
4322 r = ME_FindText(editor, wParam, &ex->chrg, tmp, &ex->chrgText);
4323 heap_free(tmp);
4324 }else{
4325 FINDTEXTEXW *ex = (FINDTEXTEXW *)lParam;
4326 r = ME_FindText(editor, wParam, &ex->chrg, ex->lpstrText, &ex->chrgText);
4327 }
4328 return r;
4329 }
4330 case EM_FINDTEXTW:
4331 {
4332 FINDTEXTW *ft = (FINDTEXTW *)lParam;
4333 return ME_FindText(editor, wParam, &ft->chrg, ft->lpstrText, NULL);
4334 }
4335 case EM_FINDTEXTEXW:
4336 {
4337 FINDTEXTEXW *ex = (FINDTEXTEXW *)lParam;
4338 return ME_FindText(editor, wParam, &ex->chrg, ex->lpstrText, &ex->chrgText);
4339 }
4340 case EM_GETZOOM:
4341 if (!wParam || !lParam)
4342 return FALSE;
4343 *(int *)wParam = editor->nZoomNumerator;
4344 *(int *)lParam = editor->nZoomDenominator;
4345 return TRUE;
4346 case EM_SETZOOM:
4347 return ME_SetZoom(editor, wParam, lParam);
4348 case EM_CHARFROMPOS:
4349 {
4350 ME_Cursor cursor;
4351 if (ME_CharFromPos(editor, ((POINTL *)lParam)->x, ((POINTL *)lParam)->y,
4352 &cursor, NULL))
4353 return ME_GetCursorOfs(&cursor);
4354 else
4355 return -1;
4356 }
4357 case EM_POSFROMCHAR:
4358 {
4359 ME_DisplayItem *pPara, *pRun;
4360 int nCharOfs, nOffset, nLength;
4361 POINTL pt = {0,0};
4362
4363 nCharOfs = wParam;
4364 /* detect which API version we're dealing with */
4365 if (wParam >= 0x40000)
4366 nCharOfs = lParam;
4367 nLength = ME_GetTextLength(editor);
4368 nCharOfs = min(nCharOfs, nLength);
4369 nCharOfs = max(nCharOfs, 0);
4370
4371 ME_RunOfsFromCharOfs(editor, nCharOfs, &pPara, &pRun, &nOffset);
4372 assert(pRun->type == diRun);
4373 pt.y = pRun->member.run.pt.y;
4374 pt.x = pRun->member.run.pt.x + ME_PointFromChar(editor, &pRun->member.run, nOffset, TRUE);
4375 pt.y += pPara->member.para.pt.y + editor->rcFormat.top;
4376 pt.x += editor->rcFormat.left;
4377
4378 pt.x -= editor->horz_si.nPos;
4379 pt.y -= editor->vert_si.nPos;
4380
4381 if (wParam >= 0x40000) {
4382 *(POINTL *)wParam = pt;
4383 }
4384 return (wParam >= 0x40000) ? 0 : MAKELONG( pt.x, pt.y );
4385 }
4386 case WM_CREATE:
4387 return ME_WmCreate(editor, lParam, unicode);
4388 case WM_DESTROY:
4389 ME_DestroyEditor(editor);
4390 return 0;
4391 case WM_SETCURSOR:
4392 {
4393 POINT cursor_pos;
4394 if (wParam == (WPARAM)editor->hWnd && GetCursorPos(&cursor_pos) &&
4395 ScreenToClient(editor->hWnd, &cursor_pos))
4396 ME_LinkNotify(editor, msg, 0, MAKELPARAM(cursor_pos.x, cursor_pos.y));
4397 return ME_SetCursor(editor);
4398 }
4399 case WM_LBUTTONDBLCLK:
4400 case WM_LBUTTONDOWN:
4401 {
4402 ME_CommitUndo(editor); /* End coalesced undos for typed characters */
4403 if ((editor->nEventMask & ENM_MOUSEEVENTS) &&
4404 !ME_FilterEvent(editor, msg, &wParam, &lParam))
4405 return 0;
4406 ITextHost_TxSetFocus(editor->texthost);
4407 ME_LButtonDown(editor, (short)LOWORD(lParam), (short)HIWORD(lParam),
4408 ME_CalculateClickCount(editor, msg, wParam, lParam));
4409 ITextHost_TxSetCapture(editor->texthost, TRUE);
4410 editor->bMouseCaptured = TRUE;
4411 ME_LinkNotify(editor, msg, wParam, lParam);
4412 if (!ME_SetCursor(editor)) goto do_default;
4413 break;
4414 }
4415 case WM_MOUSEMOVE:
4416 if ((editor->nEventMask & ENM_MOUSEEVENTS) &&
4417 !ME_FilterEvent(editor, msg, &wParam, &lParam))
4418 return 0;
4419 if (editor->bMouseCaptured)
4420 ME_MouseMove(editor, (short)LOWORD(lParam), (short)HIWORD(lParam));
4421 else
4422 ME_LinkNotify(editor, msg, wParam, lParam);
4423 /* Set cursor if mouse is captured, since WM_SETCURSOR won't be received. */
4424 if (editor->bMouseCaptured)
4425 ME_SetCursor(editor);
4426 break;
4427 case WM_LBUTTONUP:
4428 if (editor->bMouseCaptured) {
4429 ITextHost_TxSetCapture(editor->texthost, FALSE);
4430 editor->bMouseCaptured = FALSE;
4431 }
4432 if (editor->nSelectionType == stDocument)
4433 editor->nSelectionType = stPosition;
4434 if ((editor->nEventMask & ENM_MOUSEEVENTS) &&
4435 !ME_FilterEvent(editor, msg, &wParam, &lParam))
4436 return 0;
4437 else
4438 {
4439 ME_SetCursor(editor);
4440 ME_LinkNotify(editor, msg, wParam, lParam);
4441 }
4442 break;
4443 case WM_RBUTTONUP:
4444 case WM_RBUTTONDOWN:
4445 case WM_RBUTTONDBLCLK:
4446 ME_CommitUndo(editor); /* End coalesced undos for typed characters */
4447 if ((editor->nEventMask & ENM_MOUSEEVENTS) &&
4448 !ME_FilterEvent(editor, msg, &wParam, &lParam))
4449 return 0;
4450 ME_LinkNotify(editor, msg, wParam, lParam);
4451 goto do_default;
4452 case WM_CONTEXTMENU:
4453 if (!ME_ShowContextMenu(editor, (short)LOWORD(lParam), (short)HIWORD(lParam)))
4454 goto do_default;
4455 break;
4456 case WM_SETFOCUS:
4457 editor->bHaveFocus = TRUE;
4458 ME_ShowCaret(editor);
4459 ME_SendOldNotify(editor, EN_SETFOCUS);
4460 if (!editor->bHideSelection && !(editor->styleFlags & ES_NOHIDESEL))
4461 ME_InvalidateSelection( editor );
4462 return 0;
4463 case WM_KILLFOCUS:
4464 ME_CommitUndo(editor); /* End coalesced undos for typed characters */
4465 editor->bHaveFocus = FALSE;
4466 editor->wheel_remain = 0;
4467 ME_HideCaret(editor);
4468 ME_SendOldNotify(editor, EN_KILLFOCUS);
4469 if (!editor->bHideSelection && !(editor->styleFlags & ES_NOHIDESEL))
4470 ME_InvalidateSelection( editor );
4471 return 0;
4472 case WM_COMMAND:
4473 TRACE("editor wnd command = %d\n", LOWORD(wParam));
4474 return 0;
4475 case WM_KEYUP:
4476 if ((editor->nEventMask & ENM_KEYEVENTS) &&
4477 !ME_FilterEvent(editor, msg, &wParam, &lParam))
4478 return 0;
4479 goto do_default;
4480 case WM_KEYDOWN:
4481 if ((editor->nEventMask & ENM_KEYEVENTS) &&
4482 !ME_FilterEvent(editor, msg, &wParam, &lParam))
4483 return 0;
4484 if (ME_KeyDown(editor, LOWORD(wParam)))
4485 return 0;
4486 goto do_default;
4487 case WM_CHAR:
4488 if ((editor->nEventMask & ENM_KEYEVENTS) &&
4489 !ME_FilterEvent(editor, msg, &wParam, &lParam))
4490 return 0;
4491 return ME_Char(editor, wParam, lParam, unicode);
4492 case WM_UNICHAR:
4493 if (unicode)
4494 {
4495 if(wParam == UNICODE_NOCHAR) return TRUE;
4496 if(wParam <= 0x000fffff)
4497 {
4498 if(wParam > 0xffff) /* convert to surrogates */
4499 {
4500 wParam -= 0x10000;
4501 ME_Char(editor, (wParam >> 10) + 0xd800, 0, TRUE);
4502 ME_Char(editor, (wParam & 0x03ff) + 0xdc00, 0, TRUE);
4503 } else {
4504 ME_Char(editor, wParam, 0, TRUE);
4505 }
4506 }
4507 return 0;
4508 }
4509 break;
4510 case EM_STOPGROUPTYPING:
4511 ME_CommitUndo(editor); /* End coalesced undos for typed characters */
4512 return 0;
4513 case WM_HSCROLL:
4514 {
4515 const int scrollUnit = 7;
4516
4517 switch(LOWORD(wParam))
4518 {
4519 case SB_LEFT:
4520 ME_ScrollAbs(editor, 0, 0);
4521 break;
4522 case SB_RIGHT:
4523 ME_ScrollAbs(editor,
4524 editor->horz_si.nMax - (int)editor->horz_si.nPage,
4525 editor->vert_si.nMax - (int)editor->vert_si.nPage);
4526 break;
4527 case SB_LINELEFT:
4528 ME_ScrollLeft(editor, scrollUnit);
4529 break;
4530 case SB_LINERIGHT:
4531 ME_ScrollRight(editor, scrollUnit);
4532 break;
4533 case SB_PAGELEFT:
4534 ME_ScrollLeft(editor, editor->sizeWindow.cx);
4535 break;
4536 case SB_PAGERIGHT:
4537 ME_ScrollRight(editor, editor->sizeWindow.cx);
4538 break;
4539 case SB_THUMBTRACK:
4540 case SB_THUMBPOSITION:
4541 {
4542 int pos = HIWORD(wParam);
4543 if (editor->horz_si.nMax > 0xffff)
4544 pos = MulDiv(pos, editor->horz_si.nMax, 0xffff);
4545 ME_HScrollAbs(editor, pos);
4546 break;
4547 }
4548 }
4549 break;
4550 }
4551 case EM_SCROLL: /* fall through */
4552 case WM_VSCROLL:
4553 {
4554 int origNPos;
4555 int lineHeight = get_default_line_height( editor );
4556
4557 origNPos = editor->vert_si.nPos;
4558
4559 switch(LOWORD(wParam))
4560 {
4561 case SB_TOP:
4562 ME_ScrollAbs(editor, 0, 0);
4563 break;
4564 case SB_BOTTOM:
4565 ME_ScrollAbs(editor,
4566 editor->horz_si.nMax - (int)editor->horz_si.nPage,
4567 editor->vert_si.nMax - (int)editor->vert_si.nPage);
4568 break;
4569 case SB_LINEUP:
4570 ME_ScrollUp(editor,lineHeight);
4571 break;
4572 case SB_LINEDOWN:
4573 ME_ScrollDown(editor,lineHeight);
4574 break;
4575 case SB_PAGEUP:
4576 ME_ScrollUp(editor,editor->sizeWindow.cy);
4577 break;
4578 case SB_PAGEDOWN:
4579 ME_ScrollDown(editor,editor->sizeWindow.cy);
4580 break;
4581 case SB_THUMBTRACK:
4582 case SB_THUMBPOSITION:
4583 {
4584 int pos = HIWORD(wParam);
4585 if (editor->vert_si.nMax > 0xffff)
4586 pos = MulDiv(pos, editor->vert_si.nMax, 0xffff);
4587 ME_VScrollAbs(editor, pos);
4588 break;
4589 }
4590 }
4591 if (msg == EM_SCROLL)
4592 return 0x00010000 | (((editor->vert_si.nPos - origNPos)/lineHeight) & 0xffff);
4593 break;
4594 }
4595 case WM_MOUSEWHEEL:
4596 {
4597 int delta;
4598 BOOL ctrl_is_down;
4599
4600 if ((editor->nEventMask & ENM_MOUSEEVENTS) &&
4601 !ME_FilterEvent(editor, msg, &wParam, &lParam))
4602 return 0;
4603
4604 ctrl_is_down = GetKeyState(VK_CONTROL) & 0x8000;
4605
4606 delta = GET_WHEEL_DELTA_WPARAM(wParam);
4607
4608 /* if scrolling changes direction, ignore left overs */
4609 if ((delta < 0 && editor->wheel_remain < 0) ||
4610 (delta > 0 && editor->wheel_remain > 0))
4611 editor->wheel_remain += delta;
4612 else
4613 editor->wheel_remain = delta;
4614
4615 if (editor->wheel_remain)
4616 {
4617 if (ctrl_is_down) {
4618 int numerator;
4619 if (!editor->nZoomNumerator || !editor->nZoomDenominator)
4620 {
4621 numerator = 100;
4622 } else {
4623 numerator = editor->nZoomNumerator * 100 / editor->nZoomDenominator;
4624 }
4625 numerator += calc_wheel_change( &editor->wheel_remain, 10 );
4626 if (numerator >= 10 && numerator <= 500)
4627 ME_SetZoom(editor, numerator, 100);
4628 } else {
4629 UINT max_lines = 3;
4630 int lines = 0;
4631
4632 SystemParametersInfoW( SPI_GETWHEELSCROLLLINES, 0, &max_lines, 0 );
4633 if (max_lines)
4634 lines = calc_wheel_change( &editor->wheel_remain, (int)max_lines );
4635 if (lines)
4636 ME_ScrollDown( editor, -lines * get_default_line_height( editor ) );
4637 }
4638 }
4639 break;
4640 }
4641 case EM_GETRECT:
4642 {
4643 *((RECT *)lParam) = editor->rcFormat;
4644 if (editor->bDefaultFormatRect)
4645 ((RECT *)lParam)->left -= editor->selofs;
4646 return 0;
4647 }
4648 case EM_SETRECT:
4649 case EM_SETRECTNP:
4650 {
4651 if (lParam)
4652 {
4653 int border = 0;
4654 RECT clientRect;
4655 RECT *rc = (RECT *)lParam;
4656
4657 border = editor->exStyleFlags & WS_EX_CLIENTEDGE ? 1 : 0;
4658 ITextHost_TxGetClientRect(editor->texthost, &clientRect);
4659 if (wParam == 0)
4660 {
4661 editor->rcFormat.top = max(0, rc->top - border);
4662 editor->rcFormat.left = max(0, rc->left - border);
4663 editor->rcFormat.bottom = min(clientRect.bottom, rc->bottom);
4664 editor->rcFormat.right = min(clientRect.right, rc->right + border);
4665 } else if (wParam == 1) {
4666 /* MSDN incorrectly says a wParam value of 1 causes the
4667 * lParam rect to be used as a relative offset,
4668 * however, the tests show it just prevents min/max bound
4669 * checking. */
4670 editor->rcFormat.top = rc->top - border;
4671 editor->rcFormat.left = rc->left - border;
4672 editor->rcFormat.bottom = rc->bottom;
4673 editor->rcFormat.right = rc->right + border;
4674 } else {
4675 return 0;
4676 }
4677 editor->bDefaultFormatRect = FALSE;
4678 }
4679 else
4680 {
4681 ME_SetDefaultFormatRect(editor);
4682 editor->bDefaultFormatRect = TRUE;
4683 }
4684 ME_MarkAllForWrapping(editor);
4685 ME_WrapMarkedParagraphs(editor);
4686 ME_UpdateScrollBar(editor);
4687 if (msg != EM_SETRECTNP)
4688 ME_Repaint(editor);
4689 return 0;
4690 }
4691 case EM_REQUESTRESIZE:
4692 ME_SendRequestResize(editor, TRUE);
4693 return 0;
4694 case WM_SETREDRAW:
4695 goto do_default;
4696 case WM_WINDOWPOSCHANGED:
4697 {
4698 RECT clientRect;
4699 WINDOWPOS *winpos = (WINDOWPOS *)lParam;
4700
4701 if (winpos->flags & SWP_NOCLIENTSIZE) goto do_default;
4702 ITextHost_TxGetClientRect(editor->texthost, &clientRect);
4703 if (editor->bDefaultFormatRect) {
4704 ME_SetDefaultFormatRect(editor);
4705 } else {
4706 editor->rcFormat.right += clientRect.right - editor->prevClientRect.right;
4707 editor->rcFormat.bottom += clientRect.bottom - editor->prevClientRect.bottom;
4708 }
4709 editor->prevClientRect = clientRect;
4710 ME_RewrapRepaint(editor);
4711 goto do_default;
4712 }
4713 /* IME messages to make richedit controls IME aware */
4714 case WM_IME_SETCONTEXT:
4715 case WM_IME_CONTROL:
4716 case WM_IME_SELECT:
4717 case WM_IME_COMPOSITIONFULL:
4718 return 0;
4719 case WM_IME_STARTCOMPOSITION:
4720 {
4721 editor->imeStartIndex=ME_GetCursorOfs(&editor->pCursors[0]);
4722 ME_DeleteSelection(editor);
4723 ME_CommitUndo(editor);
4724 ME_UpdateRepaint(editor, FALSE);
4725 return 0;
4726 }
4727 case WM_IME_COMPOSITION:
4728 {
4729 HIMC hIMC;
4730
4731 ME_Style *style = ME_GetInsertStyle(editor, 0);
4732 hIMC = ITextHost_TxImmGetContext(editor->texthost);
4733 ME_DeleteSelection(editor);
4734 ME_SaveTempStyle(editor, style);
4735 if (lParam & (GCS_RESULTSTR|GCS_COMPSTR))
4736 {
4737 LPWSTR lpCompStr = NULL;
4738 DWORD dwBufLen;
4739 DWORD dwIndex = lParam & GCS_RESULTSTR;
4740 if (!dwIndex)
4741 dwIndex = GCS_COMPSTR;
4742
4743 dwBufLen = ImmGetCompositionStringW(hIMC, dwIndex, NULL, 0);
4744 lpCompStr = HeapAlloc(GetProcessHeap(),0,dwBufLen + sizeof(WCHAR));
4745 ImmGetCompositionStringW(hIMC, dwIndex, lpCompStr, dwBufLen);
4746 lpCompStr[dwBufLen/sizeof(WCHAR)] = 0;
4747 ME_InsertTextFromCursor(editor,0,lpCompStr,dwBufLen/sizeof(WCHAR),style);
4748 HeapFree(GetProcessHeap(), 0, lpCompStr);
4749
4750 if (dwIndex == GCS_COMPSTR)
4751 ME_SetSelection(editor,editor->imeStartIndex,
4752 editor->imeStartIndex + dwBufLen/sizeof(WCHAR));
4753 }
4754 ME_ReleaseStyle(style);
4755 ME_CommitUndo(editor);
4756 ME_UpdateRepaint(editor, FALSE);
4757 return 0;
4758 }
4759 case WM_IME_ENDCOMPOSITION:
4760 {
4761 ME_DeleteSelection(editor);
4762 editor->imeStartIndex=-1;
4763 return 0;
4764 }
4765 case EM_GETOLEINTERFACE:
4766 {
4767 if (!editor->reOle)
4768 if (!CreateIRichEditOle(NULL, editor, (LPVOID *)&editor->reOle))
4769 return 0;
4770 *(LPVOID *)lParam = editor->reOle;
4771 IRichEditOle_AddRef(editor->reOle);
4772 return 1;
4773 }
4774 case EM_GETPASSWORDCHAR:
4775 {
4776 return editor->cPasswordMask;
4777 }
4778 case EM_SETOLECALLBACK:
4779 if(editor->lpOleCallback)
4780 IRichEditOleCallback_Release(editor->lpOleCallback);
4781 editor->lpOleCallback = (IRichEditOleCallback*)lParam;
4782 if(editor->lpOleCallback)
4783 IRichEditOleCallback_AddRef(editor->lpOleCallback);
4784 return TRUE;
4785 case EM_GETWORDBREAKPROC:
4786 return (LRESULT)editor->pfnWordBreak;
4787 case EM_SETWORDBREAKPROC:
4788 {
4789 EDITWORDBREAKPROCW pfnOld = editor->pfnWordBreak;
4790
4791 editor->pfnWordBreak = (EDITWORDBREAKPROCW)lParam;
4792 return (LRESULT)pfnOld;
4793 }
4794 case EM_GETTEXTMODE:
4795 return editor->mode;
4796 case EM_SETTEXTMODE:
4797 {
4798 int mask = 0;
4799 int changes = 0;
4800
4801 if (ME_GetTextLength(editor) ||
4802 !list_empty( &editor->undo_stack ) || !list_empty( &editor->redo_stack ))
4803 return E_UNEXPECTED;
4804
4805 /* Check for mutually exclusive flags in adjacent bits of wParam */
4806 if ((wParam & (TM_RICHTEXT | TM_MULTILEVELUNDO | TM_MULTICODEPAGE)) &
4807 (wParam & (TM_PLAINTEXT | TM_SINGLELEVELUNDO | TM_SINGLECODEPAGE)) << 1)
4808 return E_INVALIDARG;
4809
4810 if (wParam & (TM_RICHTEXT | TM_PLAINTEXT))
4811 {
4812 mask |= TM_RICHTEXT | TM_PLAINTEXT;
4813 changes |= wParam & (TM_RICHTEXT | TM_PLAINTEXT);
4814 if (wParam & TM_PLAINTEXT) {
4815 /* Clear selection since it should be possible to select the
4816 * end of text run for rich text */
4817 ME_InvalidateSelection(editor);
4818 ME_SetCursorToStart(editor, &editor->pCursors[0]);
4819 editor->pCursors[1] = editor->pCursors[0];
4820 /* plain text can only have the default style. */
4821 ME_ClearTempStyle(editor);
4822 ME_AddRefStyle(editor->pBuffer->pDefaultStyle);
4823 ME_ReleaseStyle(editor->pCursors[0].pRun->member.run.style);
4824 editor->pCursors[0].pRun->member.run.style = editor->pBuffer->pDefaultStyle;
4825 }
4826 }
4827 /* FIXME: Currently no support for undo level and code page options */
4828 editor->mode = (editor->mode & ~mask) | changes;
4829 return 0;
4830 }
4831 case EM_SETPASSWORDCHAR:
4832 {
4833 editor->cPasswordMask = wParam;
4834 ME_RewrapRepaint(editor);
4835 return 0;
4836 }
4837 case EM_SETTARGETDEVICE:
4838 if (wParam == 0)
4839 {
4840 BOOL new = (lParam == 0 && (editor->styleFlags & ES_MULTILINE));
4841 if (editor->nAvailWidth || editor->bWordWrap != new)
4842 {
4843 editor->bWordWrap = new;
4844 editor->nAvailWidth = 0; /* wrap to client area */
4845 ME_RewrapRepaint(editor);
4846 }
4847 } else {
4848 int width = max(0, lParam);
4849 if ((editor->styleFlags & ES_MULTILINE) &&
4850 (!editor->bWordWrap || editor->nAvailWidth != width))
4851 {
4852 editor->nAvailWidth = width;
4853 editor->bWordWrap = TRUE;
4854 ME_RewrapRepaint(editor);
4855 }
4856 FIXME("EM_SETTARGETDEVICE doesn't use non-NULL target devices\n");
4857 }
4858 return TRUE;
4859 default:
4860 do_default:
4861 *phresult = S_FALSE;
4862 break;
4863 }
4864 return 0L;
4865 }
4866
4867 static BOOL create_windowed_editor(HWND hwnd, CREATESTRUCTW *create, BOOL emulate_10)
4868 {
4869 ITextHost *host = ME_CreateTextHost( hwnd, create, emulate_10 );
4870 ME_TextEditor *editor;
4871
4872 if (!host) return FALSE;
4873
4874 editor = ME_MakeEditor( host, emulate_10 );
4875 if (!editor)
4876 {
4877 ITextHost_Release( host );
4878 return FALSE;
4879 }
4880
4881 editor->exStyleFlags = GetWindowLongW( hwnd, GWL_EXSTYLE );
4882 editor->styleFlags |= GetWindowLongW( hwnd, GWL_STYLE ) & ES_WANTRETURN;
4883 editor->hWnd = hwnd; /* FIXME: Remove editor's dependence on hWnd */
4884 editor->hwndParent = create->hwndParent;
4885
4886 SetWindowLongPtrW( hwnd, 0, (LONG_PTR)editor );
4887
4888 return TRUE;
4889 }
4890
4891 static LRESULT RichEditWndProc_common(HWND hWnd, UINT msg, WPARAM wParam,
4892 LPARAM lParam, BOOL unicode)
4893 {
4894 ME_TextEditor *editor;
4895 HRESULT hresult;
4896 LRESULT lresult = 0;
4897
4898 TRACE("enter hwnd %p msg %04x (%s) %lx %lx, unicode %d\n",
4899 hWnd, msg, get_msg_name(msg), wParam, lParam, unicode);
4900
4901 editor = (ME_TextEditor *)GetWindowLongPtrW(hWnd, 0);
4902 if (!editor)
4903 {
4904 if (msg == WM_NCCREATE)
4905 {
4906 CREATESTRUCTW *pcs = (CREATESTRUCTW *)lParam;
4907
4908 TRACE("WM_NCCREATE: hWnd %p style 0x%08x\n", hWnd, pcs->style);
4909 return create_windowed_editor( hWnd, pcs, FALSE );
4910 }
4911 else
4912 {
4913 return DefWindowProcW(hWnd, msg, wParam, lParam);
4914 }
4915 }
4916
4917 switch (msg)
4918 {
4919 case WM_PAINT:
4920 {
4921 HDC hDC;
4922 RECT rc;
4923 PAINTSTRUCT ps;
4924
4925 hDC = BeginPaint(editor->hWnd, &ps);
4926 if (!editor->bEmulateVersion10 || (editor->nEventMask & ENM_UPDATE))
4927 ME_SendOldNotify(editor, EN_UPDATE);
4928 /* Erase area outside of the formatting rectangle */
4929 if (ps.rcPaint.top < editor->rcFormat.top)
4930 {
4931 rc = ps.rcPaint;
4932 rc.bottom = editor->rcFormat.top;
4933 FillRect(hDC, &rc, editor->hbrBackground);
4934 ps.rcPaint.top = editor->rcFormat.top;
4935 }
4936 if (ps.rcPaint.bottom > editor->rcFormat.bottom) {
4937 rc = ps.rcPaint;
4938 rc.top = editor->rcFormat.bottom;
4939 FillRect(hDC, &rc, editor->hbrBackground);
4940 ps.rcPaint.bottom = editor->rcFormat.bottom;
4941 }
4942 if (ps.rcPaint.left < editor->rcFormat.left) {
4943 rc = ps.rcPaint;
4944 rc.right = editor->rcFormat.left;
4945 FillRect(hDC, &rc, editor->hbrBackground);
4946 ps.rcPaint.left = editor->rcFormat.left;
4947 }
4948 if (ps.rcPaint.right > editor->rcFormat.right) {
4949 rc = ps.rcPaint;
4950 rc.left = editor->rcFormat.right;
4951 FillRect(hDC, &rc, editor->hbrBackground);
4952 ps.rcPaint.right = editor->rcFormat.right;
4953 }
4954
4955 ME_PaintContent(editor, hDC, &ps.rcPaint);
4956 EndPaint(editor->hWnd, &ps);
4957 return 0;
4958 }
4959 case WM_ERASEBKGND:
4960 {
4961 HDC hDC = (HDC)wParam;
4962 RECT rc;
4963
4964 if (GetUpdateRect(editor->hWnd, &rc, TRUE))
4965 FillRect(hDC, &rc, editor->hbrBackground);
4966 return 1;
4967 }
4968 case EM_SETOPTIONS:
4969 {
4970 DWORD dwStyle;
4971 const DWORD mask = ECO_VERTICAL | ECO_AUTOHSCROLL | ECO_AUTOVSCROLL |
4972 ECO_NOHIDESEL | ECO_READONLY | ECO_WANTRETURN |
4973 ECO_SELECTIONBAR;
4974 lresult = ME_HandleMessage(editor, msg, wParam, lParam, unicode, &hresult);
4975 dwStyle = GetWindowLongW(hWnd, GWL_STYLE);
4976 dwStyle = (dwStyle & ~mask) | (lresult & mask);
4977 SetWindowLongW(hWnd, GWL_STYLE, dwStyle);
4978 return lresult;
4979 }
4980 case EM_SETREADONLY:
4981 {
4982 DWORD dwStyle;
4983 lresult = ME_HandleMessage(editor, msg, wParam, lParam, unicode, &hresult);
4984 dwStyle = GetWindowLongW(hWnd, GWL_STYLE);
4985 dwStyle &= ~ES_READONLY;
4986 if (wParam)
4987 dwStyle |= ES_READONLY;
4988 SetWindowLongW(hWnd, GWL_STYLE, dwStyle);
4989 return lresult;
4990 }
4991 default:
4992 lresult = ME_HandleMessage(editor, msg, wParam, lParam, unicode, &hresult);
4993 }
4994
4995 if (hresult == S_FALSE)
4996 lresult = DefWindowProcW(hWnd, msg, wParam, lParam);
4997
4998 TRACE("exit hwnd %p msg %04x (%s) %lx %lx, unicode %d -> %lu\n",
4999 hWnd, msg, get_msg_name(msg), wParam, lParam, unicode, lresult);
5000
5001 return lresult;
5002 }
5003
5004 static LRESULT WINAPI RichEditWndProcW(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
5005 {
5006 BOOL unicode = TRUE;
5007
5008 /* Under Win9x RichEdit20W returns ANSI strings, see the tests. */
5009 if (msg == WM_GETTEXT && (GetVersion() & 0x80000000))
5010 unicode = FALSE;
5011
5012 return RichEditWndProc_common(hWnd, msg, wParam, lParam, unicode);
5013 }
5014
5015 static LRESULT WINAPI RichEditWndProcA(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
5016 {
5017 return RichEditWndProc_common(hWnd, msg, wParam, lParam, FALSE);
5018 }
5019
5020 /******************************************************************
5021 * RichEditANSIWndProc (RICHED20.10)
5022 */
5023 LRESULT WINAPI RichEditANSIWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
5024 {
5025 return RichEditWndProcA(hWnd, msg, wParam, lParam);
5026 }
5027
5028 /******************************************************************
5029 * RichEdit10ANSIWndProc (RICHED20.9)
5030 */
5031 LRESULT WINAPI RichEdit10ANSIWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
5032 {
5033 if (msg == WM_NCCREATE && !GetWindowLongPtrW(hWnd, 0))
5034 {
5035 CREATESTRUCTW *pcs = (CREATESTRUCTW *)lParam;
5036
5037 TRACE("WM_NCCREATE: hWnd %p style 0x%08x\n", hWnd, pcs->style);
5038 return create_windowed_editor( hWnd, pcs, TRUE );
5039 }
5040 return RichEditANSIWndProc(hWnd, msg, wParam, lParam);
5041 }
5042
5043 void ME_SendOldNotify(ME_TextEditor *editor, int nCode)
5044 {
5045 ITextHost_TxNotify(editor->texthost, nCode, NULL);
5046 }
5047
5048 /* Fill buffer with srcChars unicode characters from the start cursor.
5049 *
5050 * buffer: destination buffer
5051 * buflen: length of buffer in characters excluding the NULL terminator.
5052 * start: start of editor text to copy into buffer.
5053 * srcChars: Number of characters to use from the editor text.
5054 * bCRLF: if true, replaces all end of lines with \r\n pairs.
5055 *
5056 * returns the number of characters written excluding the NULL terminator.
5057 *
5058 * The written text is always NULL terminated.
5059 */
5060 int ME_GetTextW(ME_TextEditor *editor, WCHAR *buffer, int buflen,
5061 const ME_Cursor *start, int srcChars, BOOL bCRLF,
5062 BOOL bEOP)
5063 {
5064 ME_DisplayItem *pRun, *pNextRun;
5065 const WCHAR *pStart = buffer;
5066 const WCHAR cr_lf[] = {'\r', '\n', 0};
5067 const WCHAR *str;
5068 int nLen;
5069
5070 /* bCRLF flag is only honored in 2.0 and up. 1.0 must always return text verbatim */
5071 if (editor->bEmulateVersion10) bCRLF = FALSE;
5072
5073 pRun = start->pRun;
5074 assert(pRun);
5075 pNextRun = ME_FindItemFwd(pRun, diRun);
5076
5077 nLen = pRun->member.run.len - start->nOffset;
5078 str = get_text( &pRun->member.run, start->nOffset );
5079
5080 while (srcChars && buflen && pNextRun)
5081 {
5082 int nFlags = pRun->member.run.nFlags;
5083
5084 if (bCRLF && nFlags & MERF_ENDPARA && ~nFlags & MERF_ENDCELL)
5085 {
5086 if (buflen == 1) break;
5087 /* FIXME: native fails to reduce srcChars here for WM_GETTEXT or
5088 * EM_GETTEXTEX, however, this is done for copying text which
5089 * also uses this function. */
5090 srcChars -= min(nLen, srcChars);
5091 nLen = 2;
5092 str = cr_lf;
5093 } else {
5094 nLen = min(nLen, srcChars);
5095 srcChars -= nLen;
5096 }
5097
5098 nLen = min(nLen, buflen);
5099 buflen -= nLen;
5100
5101 CopyMemory(buffer, str, sizeof(WCHAR) * nLen);
5102
5103 buffer += nLen;
5104
5105 pRun = pNextRun;
5106 pNextRun = ME_FindItemFwd(pRun, diRun);
5107
5108 nLen = pRun->member.run.len;
5109 str = get_text( &pRun->member.run, 0 );
5110 }
5111 /* append '\r' to the last paragraph. */
5112 if (pRun->next->type == diTextEnd && bEOP)
5113 {
5114 *buffer = '\r';
5115 buffer ++;
5116 }
5117 *buffer = 0;
5118 return buffer - pStart;
5119 }
5120
5121 static BOOL ME_RegisterEditorClass(HINSTANCE hInstance)
5122 {
5123 WNDCLASSW wcW;
5124 WNDCLASSA wcA;
5125
5126 wcW.style = CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW | CS_GLOBALCLASS;
5127 wcW.lpfnWndProc = RichEditWndProcW;
5128 wcW.cbClsExtra = 0;
5129 wcW.cbWndExtra = sizeof(ME_TextEditor *);
5130 wcW.hInstance = NULL; /* hInstance would register DLL-local class */
5131 wcW.hIcon = NULL;
5132 wcW.hCursor = LoadCursorW(NULL, (LPWSTR)IDC_IBEAM);
5133 wcW.hbrBackground = GetStockObject(NULL_BRUSH);
5134 wcW.lpszMenuName = NULL;
5135
5136 if (is_version_nt())
5137 {
5138 wcW.lpszClassName = RICHEDIT_CLASS20W;
5139 if (!RegisterClassW(&wcW)) return FALSE;
5140 wcW.lpszClassName = MSFTEDIT_CLASS;
5141 if (!RegisterClassW(&wcW)) return FALSE;
5142 }
5143 else
5144 {
5145 /* WNDCLASSA/W have the same layout */
5146 wcW.lpszClassName = (LPCWSTR)"RichEdit20W";
5147 if (!RegisterClassA((WNDCLASSA *)&wcW)) return FALSE;
5148 wcW.lpszClassName = (LPCWSTR)"RichEdit50W";
5149 if (!RegisterClassA((WNDCLASSA *)&wcW)) return FALSE;
5150 }
5151
5152 wcA.style = CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW | CS_GLOBALCLASS;
5153 wcA.lpfnWndProc = RichEditWndProcA;
5154 wcA.cbClsExtra = 0;
5155 wcA.cbWndExtra = sizeof(ME_TextEditor *);
5156 wcA.hInstance = NULL; /* hInstance would register DLL-local class */
5157 wcA.hIcon = NULL;
5158 wcA.hCursor = LoadCursorW(NULL, (LPWSTR)IDC_IBEAM);
5159 wcA.hbrBackground = GetStockObject(NULL_BRUSH);
5160 wcA.lpszMenuName = NULL;
5161 wcA.lpszClassName = RICHEDIT_CLASS20A;
5162 if (!RegisterClassA(&wcA)) return FALSE;
5163 wcA.lpszClassName = "RichEdit50A";
5164 if (!RegisterClassA(&wcA)) return FALSE;
5165
5166 return TRUE;
5167 }
5168
5169 static LRESULT WINAPI REComboWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) {
5170 /* FIXME: Not implemented */
5171 TRACE("hWnd %p msg %04x (%s) %08lx %08lx\n",
5172 hWnd, msg, get_msg_name(msg), wParam, lParam);
5173 return DefWindowProcW(hWnd, msg, wParam, lParam);
5174 }
5175
5176 static LRESULT WINAPI REListWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) {
5177 /* FIXME: Not implemented */
5178 TRACE("hWnd %p msg %04x (%s) %08lx %08lx\n",
5179 hWnd, msg, get_msg_name(msg), wParam, lParam);
5180 return DefWindowProcW(hWnd, msg, wParam, lParam);
5181 }
5182
5183 /******************************************************************
5184 * REExtendedRegisterClass (RICHED20.8)
5185 *
5186 * FIXME undocumented
5187 * Need to check for errors and implement controls and callbacks
5188 */
5189 LRESULT WINAPI REExtendedRegisterClass(void)
5190 {
5191 WNDCLASSW wcW;
5192 UINT result;
5193
5194 FIXME("semi stub\n");
5195
5196 wcW.cbClsExtra = 0;
5197 wcW.cbWndExtra = 4;
5198 wcW.hInstance = NULL;
5199 wcW.hIcon = NULL;
5200 wcW.hCursor = NULL;
5201 wcW.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
5202 wcW.lpszMenuName = NULL;
5203
5204 if (!ME_ListBoxRegistered)
5205 {
5206 wcW.style = CS_PARENTDC | CS_DBLCLKS | CS_GLOBALCLASS;
5207 wcW.lpfnWndProc = REListWndProc;
5208 wcW.lpszClassName = REListBox20W;
5209 if (RegisterClassW(&wcW)) ME_ListBoxRegistered = TRUE;
5210 }
5211
5212 if (!ME_ComboBoxRegistered)
5213 {
5214 wcW.style = CS_PARENTDC | CS_DBLCLKS | CS_GLOBALCLASS | CS_VREDRAW | CS_HREDRAW;
5215 wcW.lpfnWndProc = REComboWndProc;
5216 wcW.lpszClassName = REComboBox20W;
5217 if (RegisterClassW(&wcW)) ME_ComboBoxRegistered = TRUE;
5218 }
5219
5220 result = 0;
5221 if (ME_ListBoxRegistered)
5222 result += 1;
5223 if (ME_ComboBoxRegistered)
5224 result += 2;
5225
5226 return result;
5227 }
5228
5229 static int wchar_comp( const void *key, const void *elem )
5230 {
5231 return *(const WCHAR *)key - *(const WCHAR *)elem;
5232 }
5233
5234 /* neutral characters end the url if the next non-neutral character is a space character,
5235 otherwise they are included in the url. */
5236 static BOOL isurlneutral( WCHAR c )
5237 {
5238 /* NB this list is sorted */
5239 static const WCHAR neutral_chars[] = {'!','\"','\'','(',')',',','-','.',':',';','<','>','?','[',']','{','}'};
5240
5241 /* Some shortcuts */
5242 if (isalnum( c )) return FALSE;
5243 if (c > neutral_chars[sizeof(neutral_chars) / sizeof(neutral_chars[0]) - 1]) return FALSE;
5244
5245 return !!bsearch( &c, neutral_chars, sizeof(neutral_chars) / sizeof(neutral_chars[0]),
5246 sizeof(c), wchar_comp );
5247 }
5248
5249 /**
5250 * This proc takes a selection, and scans it forward in order to select the span
5251 * of a possible URL candidate. A possible URL candidate must start with isalnum
5252 * or one of the following special characters: *|/\+%#@ and must consist entirely
5253 * of the characters allowed to start the URL, plus : (colon) which may occur
5254 * at most once, and not at either end.
5255 */
5256 static BOOL ME_FindNextURLCandidate(ME_TextEditor *editor,
5257 const ME_Cursor *start,
5258 int nChars,
5259 ME_Cursor *candidate_min,
5260 ME_Cursor *candidate_max)
5261 {
5262 ME_Cursor cursor = *start, neutral_end, space_end;
5263 BOOL candidateStarted = FALSE, quoted = FALSE;
5264 WCHAR c;
5265
5266 while (nChars > 0)
5267 {
5268 WCHAR *str = get_text( &cursor.pRun->member.run, 0 );
5269 int run_len = cursor.pRun->member.run.len;
5270
5271 nChars -= run_len - cursor.nOffset;
5272
5273 /* Find start of candidate */
5274 if (!candidateStarted)
5275 {
5276 while (cursor.nOffset < run_len)
5277 {
5278 c = str[cursor.nOffset];
5279 if (!isspaceW( c ) && !isurlneutral( c ))
5280 {
5281 *candidate_min = cursor;
5282 candidateStarted = TRUE;
5283 neutral_end.pPara = NULL;
5284 space_end.pPara = NULL;
5285 cursor.nOffset++;
5286 break;
5287 }
5288 quoted = (c == '<');
5289 cursor.nOffset++;
5290 }
5291 }
5292
5293 /* Find end of candidate */
5294 if (candidateStarted)
5295 {
5296 while (cursor.nOffset < run_len)
5297 {
5298 c = str[cursor.nOffset];
5299 if (isspaceW( c ))
5300 {
5301 if (quoted && c != '\r')
5302 {
5303 if (!space_end.pPara)
5304 {
5305 if (neutral_end.pPara)
5306 space_end = neutral_end;
5307 else
5308 space_end = cursor;
5309 }
5310 }
5311 else
5312 goto done;
5313 }
5314 else if (isurlneutral( c ))
5315 {
5316 if (quoted && c == '>')
5317 {
5318 neutral_end.pPara = NULL;
5319 space_end.pPara = NULL;
5320 goto done;
5321 }
5322 if (!neutral_end.pPara)
5323 neutral_end = cursor;
5324 }
5325 else
5326 neutral_end.pPara = NULL;
5327
5328 cursor.nOffset++;
5329 }
5330 }
5331
5332 cursor.nOffset = 0;
5333 if (!ME_NextRun(&cursor.pPara, &cursor.pRun, TRUE))
5334 goto done;
5335 }
5336
5337 done:
5338 if (candidateStarted)
5339 {
5340 if (space_end.pPara)
5341 *candidate_max = space_end;
5342 else if (neutral_end.pPara)
5343 *candidate_max = neutral_end;
5344 else
5345 *candidate_max = cursor;
5346 return TRUE;
5347 }
5348 *candidate_max = *candidate_min = cursor;
5349 return FALSE;
5350 }
5351
5352 /**
5353 * This proc evaluates the selection and returns TRUE if it can be considered an URL
5354 */
5355 static BOOL ME_IsCandidateAnURL(ME_TextEditor *editor, const ME_Cursor *start, int nChars)
5356 {
5357 #define MAX_PREFIX_LEN 9
5358 struct prefix_s {
5359 const WCHAR text[MAX_PREFIX_LEN];
5360 int length;
5361 }prefixes[] = {
5362 {{'p','r','o','s','p','e','r','o',':'}, 9},
5363 {{'t','e','l','n','e','t',':'}, 7},
5364 {{'g','o','p','h','e','r',':'}, 7},
5365 {{'m','a','i','l','t','o',':'}, 7},
5366 {{'h','t','t','p','s',':'}, 6},
5367 {{'f','i','l','e',':'}, 5},
5368 {{'n','e','w','s',':'}, 5},
5369 {{'w','a','i','s',':'}, 5},
5370 {{'n','n','t','p',':'}, 5},
5371 {{'h','t','t','p',':'}, 5},
5372 {{'w','w','w','.'}, 4},
5373 {{'f','t','p',':'}, 4},
5374 };
5375 WCHAR bufferW[MAX_PREFIX_LEN + 1];
5376 unsigned int i;
5377
5378 ME_GetTextW(editor, bufferW, MAX_PREFIX_LEN, start, nChars, FALSE, FALSE);
5379 for (i = 0; i < sizeof(prefixes) / sizeof(*prefixes); i++)
5380 {
5381 if (nChars < prefixes[i].length) continue;
5382 if (!memcmp(prefixes[i].text, bufferW, prefixes[i].length * sizeof(WCHAR)))
5383 return TRUE;
5384 }
5385 return FALSE;
5386 #undef MAX_PREFIX_LEN
5387 }
5388
5389 /**
5390 * This proc walks through the indicated selection and evaluates whether each
5391 * section identified by ME_FindNextURLCandidate and in-between sections have
5392 * their proper CFE_LINK attributes set or unset. If the CFE_LINK attribute is
5393 * not what it is supposed to be, this proc sets or unsets it as appropriate.
5394 *
5395 * Since this function can cause runs to be split, do not depend on the value
5396 * of the start cursor at the end of the function.
5397 *
5398 * nChars may be set to INT_MAX to update to the end of the text.
5399 *
5400 * Returns TRUE if at least one section was modified.
5401 */
5402 static BOOL ME_UpdateLinkAttribute(ME_TextEditor *editor, ME_Cursor *start, int nChars)
5403 {
5404 BOOL modified = FALSE;
5405 ME_Cursor startCur = *start;
5406
5407 if (!editor->AutoURLDetect_bEnable) return FALSE;
5408
5409 do
5410 {
5411 CHARFORMAT2W link;
5412 ME_Cursor candidateStart, candidateEnd;
5413
5414 if (ME_FindNextURLCandidate(editor, &startCur, nChars,
5415 &candidateStart, &candidateEnd))
5416 {
5417 /* Section before candidate is not an URL */
5418 int cMin = ME_GetCursorOfs(&candidateStart);
5419 int cMax = ME_GetCursorOfs(&candidateEnd);
5420
5421 if (!ME_IsCandidateAnURL(editor, &candidateStart, cMax - cMin))
5422 candidateStart = candidateEnd;
5423 nChars -= cMax - ME_GetCursorOfs(&startCur);
5424 }
5425 else
5426 {
5427 /* No more candidates until end of selection */
5428 nChars = 0;
5429 }
5430
5431 if (startCur.pRun != candidateStart.pRun ||
5432 startCur.nOffset != candidateStart.nOffset)
5433 {
5434 /* CFE_LINK effect should be consistently unset */
5435 link.cbSize = sizeof(link);
5436 ME_GetCharFormat(editor, &startCur, &candidateStart, &link);
5437 if (!(link.dwMask & CFM_LINK) || (link.dwEffects & CFE_LINK))
5438 {
5439 /* CFE_LINK must be unset from this range */
5440 memset(&link, 0, sizeof(CHARFORMAT2W));
5441 link.cbSize = sizeof(link);
5442 link.dwMask = CFM_LINK;
5443 link.dwEffects = 0;
5444 ME_SetCharFormat(editor, &startCur, &candidateStart, &link);
5445 /* Update candidateEnd since setting character formats may split
5446 * runs, which can cause a cursor to be at an invalid offset within
5447 * a split run. */
5448 while (candidateEnd.nOffset >= candidateEnd.pRun->member.run.len)
5449 {
5450 candidateEnd.nOffset -= candidateEnd.pRun->member.run.len;
5451 candidateEnd.pRun = ME_FindItemFwd(candidateEnd.pRun, diRun);
5452 }
5453 modified = TRUE;
5454 }
5455 }
5456 if (candidateStart.pRun != candidateEnd.pRun ||
5457 candidateStart.nOffset != candidateEnd.nOffset)
5458 {
5459 /* CFE_LINK effect should be consistently set */
5460 link.cbSize = sizeof(link);
5461 ME_GetCharFormat(editor, &candidateStart, &candidateEnd, &link);
5462 if (!(link.dwMask & CFM_LINK) || !(link.dwEffects & CFE_LINK))
5463 {
5464 /* CFE_LINK must be set on this range */
5465 memset(&link, 0, sizeof(CHARFORMAT2W));
5466 link.cbSize = sizeof(link);
5467 link.dwMask = CFM_LINK;
5468 link.dwEffects = CFE_LINK;
5469 ME_SetCharFormat(editor, &candidateStart, &candidateEnd, &link);
5470 modified = TRUE;
5471 }
5472 }
5473 startCur = candidateEnd;
5474 } while (nChars > 0);
5475 return modified;
5476 }