648c3ef435d49e08d78e2d42af2714b1292c4698
[reactos.git] / reactos / 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 (currently single line controls aren't supported)
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 #include "editor.h"
228
229 #include <commdlg.h>
230 #include <undocuser.h>
231
232 #include "rtf.h"
233 #include "res.h"
234
235 #define STACK_SIZE_DEFAULT 100
236 #define STACK_SIZE_MAX 1000
237
238 #define TEXT_LIMIT_DEFAULT 32767
239
240 WINE_DEFAULT_DEBUG_CHANNEL(richedit);
241
242 static BOOL ME_RegisterEditorClass(HINSTANCE);
243 static BOOL ME_UpdateLinkAttribute(ME_TextEditor *editor, ME_Cursor *start, int nChars);
244
245 static const WCHAR REListBox20W[] = {'R','E','L','i','s','t','B','o','x','2','0','W', 0};
246 static const WCHAR REComboBox20W[] = {'R','E','C','o','m','b','o','B','o','x','2','0','W', 0};
247 static HCURSOR hLeft;
248
249 BOOL me_debug = FALSE;
250 HANDLE me_heap = NULL;
251
252 static BOOL ME_ListBoxRegistered = FALSE;
253 static BOOL ME_ComboBoxRegistered = FALSE;
254
255 static inline BOOL is_version_nt(void)
256 {
257 return !(GetVersion() & 0x80000000);
258 }
259
260 static ME_TextBuffer *ME_MakeText(void) {
261
262 ME_TextBuffer *buf = ALLOC_OBJ(ME_TextBuffer);
263
264 ME_DisplayItem *p1 = ME_MakeDI(diTextStart);
265 ME_DisplayItem *p2 = ME_MakeDI(diTextEnd);
266
267 p1->prev = NULL;
268 p1->next = p2;
269 p2->prev = p1;
270 p2->next = NULL;
271 p1->member.para.next_para = p2;
272 p2->member.para.prev_para = p1;
273 p2->member.para.nCharOfs = 0;
274
275 buf->pFirst = p1;
276 buf->pLast = p2;
277 buf->pCharStyle = NULL;
278
279 return buf;
280 }
281
282
283 static LRESULT ME_StreamInText(ME_TextEditor *editor, DWORD dwFormat, ME_InStream *stream, ME_Style *style)
284 {
285 WCHAR *pText;
286 LRESULT total_bytes_read = 0;
287 BOOL is_read = FALSE;
288 DWORD cp = CP_ACP, copy = 0;
289 char conv_buf[4 + STREAMIN_BUFFER_SIZE]; /* up to 4 additional UTF-8 bytes */
290
291 static const char bom_utf8[] = {0xEF, 0xBB, 0xBF};
292
293 TRACE("%08x %p\n", dwFormat, stream);
294
295 do {
296 LONG nWideChars = 0;
297 WCHAR wszText[STREAMIN_BUFFER_SIZE+1];
298
299 if (!stream->dwSize)
300 {
301 ME_StreamInFill(stream);
302 if (stream->editstream->dwError)
303 break;
304 if (!stream->dwSize)
305 break;
306 total_bytes_read += stream->dwSize;
307 }
308
309 if (!(dwFormat & SF_UNICODE))
310 {
311 char * buf = stream->buffer;
312 DWORD size = stream->dwSize, end;
313
314 if (!is_read)
315 {
316 is_read = TRUE;
317 if (stream->dwSize >= 3 && !memcmp(stream->buffer, bom_utf8, 3))
318 {
319 cp = CP_UTF8;
320 buf += 3;
321 size -= 3;
322 }
323 }
324
325 if (cp == CP_UTF8)
326 {
327 if (copy)
328 {
329 memcpy(conv_buf + copy, buf, size);
330 buf = conv_buf;
331 size += copy;
332 }
333 end = size;
334 while ((buf[end-1] & 0xC0) == 0x80)
335 {
336 --end;
337 --total_bytes_read; /* strange, but seems to match windows */
338 }
339 if (buf[end-1] & 0x80)
340 {
341 DWORD need = 0;
342 if ((buf[end-1] & 0xE0) == 0xC0)
343 need = 1;
344 if ((buf[end-1] & 0xF0) == 0xE0)
345 need = 2;
346 if ((buf[end-1] & 0xF8) == 0xF0)
347 need = 3;
348
349 if (size - end >= need)
350 {
351 /* we have enough bytes for this sequence */
352 end = size;
353 }
354 else
355 {
356 /* need more bytes, so don't transcode this sequence */
357 --end;
358 }
359 }
360 }
361 else
362 end = size;
363
364 nWideChars = MultiByteToWideChar(cp, 0, buf, end, wszText, STREAMIN_BUFFER_SIZE);
365 pText = wszText;
366
367 if (cp == CP_UTF8)
368 {
369 if (end != size)
370 {
371 memcpy(conv_buf, buf + end, size - end);
372 copy = size - end;
373 }
374 }
375 }
376 else
377 {
378 nWideChars = stream->dwSize >> 1;
379 pText = (WCHAR *)stream->buffer;
380 }
381
382 ME_InsertTextFromCursor(editor, 0, pText, nWideChars, style);
383 if (stream->dwSize == 0)
384 break;
385 stream->dwSize = 0;
386 } while(1);
387 return total_bytes_read;
388 }
389
390 static void ME_ApplyBorderProperties(RTF_Info *info,
391 ME_BorderRect *borderRect,
392 RTFBorder *borderDef)
393 {
394 int i, colorNum;
395 ME_Border *pBorders[] = {&borderRect->top,
396 &borderRect->left,
397 &borderRect->bottom,
398 &borderRect->right};
399 for (i = 0; i < 4; i++)
400 {
401 RTFColor *colorDef = info->colorList;
402 pBorders[i]->width = borderDef[i].width;
403 colorNum = borderDef[i].color;
404 while (colorDef && colorDef->rtfCNum != colorNum)
405 colorDef = colorDef->rtfNextColor;
406 if (colorDef)
407 pBorders[i]->colorRef = RGB(
408 colorDef->rtfCRed >= 0 ? colorDef->rtfCRed : 0,
409 colorDef->rtfCGreen >= 0 ? colorDef->rtfCGreen : 0,
410 colorDef->rtfCBlue >= 0 ? colorDef->rtfCBlue : 0);
411 else
412 pBorders[i]->colorRef = RGB(0, 0, 0);
413 }
414 }
415
416 void ME_RTFCharAttrHook(RTF_Info *info)
417 {
418 CHARFORMAT2W fmt;
419 fmt.cbSize = sizeof(fmt);
420 fmt.dwMask = 0;
421 fmt.dwEffects = 0;
422
423 switch(info->rtfMinor)
424 {
425 case rtfPlain:
426 /* FIXME add more flags once they're implemented */
427 fmt.dwMask = CFM_BOLD | CFM_ITALIC | CFM_UNDERLINETYPE | CFM_STRIKEOUT | CFM_COLOR | CFM_BACKCOLOR | CFM_SIZE | CFM_WEIGHT;
428 fmt.dwEffects = CFE_AUTOCOLOR | CFE_AUTOBACKCOLOR;
429 fmt.yHeight = 12*20; /* 12pt */
430 fmt.wWeight = FW_NORMAL;
431 fmt.bUnderlineType = CFU_UNDERLINENONE;
432 break;
433 case rtfBold:
434 fmt.dwMask = CFM_BOLD | CFM_WEIGHT;
435 fmt.dwEffects = info->rtfParam ? CFE_BOLD : 0;
436 fmt.wWeight = info->rtfParam ? FW_BOLD : FW_NORMAL;
437 break;
438 case rtfItalic:
439 fmt.dwMask = CFM_ITALIC;
440 fmt.dwEffects = info->rtfParam ? fmt.dwMask : 0;
441 break;
442 case rtfUnderline:
443 fmt.dwMask = CFM_UNDERLINETYPE;
444 fmt.bUnderlineType = info->rtfParam ? CFU_CF1UNDERLINE : CFU_UNDERLINENONE;
445 break;
446 case rtfDotUnderline:
447 fmt.dwMask = CFM_UNDERLINETYPE;
448 fmt.bUnderlineType = info->rtfParam ? CFU_UNDERLINEDOTTED : CFU_UNDERLINENONE;
449 break;
450 case rtfDbUnderline:
451 fmt.dwMask = CFM_UNDERLINETYPE;
452 fmt.bUnderlineType = info->rtfParam ? CFU_UNDERLINEDOUBLE : CFU_UNDERLINENONE;
453 break;
454 case rtfWordUnderline:
455 fmt.dwMask = CFM_UNDERLINETYPE;
456 fmt.bUnderlineType = info->rtfParam ? CFU_UNDERLINEWORD : CFU_UNDERLINENONE;
457 break;
458 case rtfNoUnderline:
459 fmt.dwMask = CFM_UNDERLINETYPE;
460 fmt.bUnderlineType = CFU_UNDERLINENONE;
461 break;
462 case rtfStrikeThru:
463 fmt.dwMask = CFM_STRIKEOUT;
464 fmt.dwEffects = info->rtfParam ? fmt.dwMask : 0;
465 break;
466 case rtfSubScript:
467 case rtfSuperScript:
468 case rtfSubScrShrink:
469 case rtfSuperScrShrink:
470 case rtfNoSuperSub:
471 fmt.dwMask = CFM_SUBSCRIPT|CFM_SUPERSCRIPT;
472 if (info->rtfMinor == rtfSubScrShrink) fmt.dwEffects = CFE_SUBSCRIPT;
473 if (info->rtfMinor == rtfSuperScrShrink) fmt.dwEffects = CFE_SUPERSCRIPT;
474 if (info->rtfMinor == rtfNoSuperSub) fmt.dwEffects = 0;
475 break;
476 case rtfInvisible:
477 fmt.dwMask = CFM_HIDDEN;
478 fmt.dwEffects = info->rtfParam ? fmt.dwMask : 0;
479 break;
480 case rtfBackColor:
481 fmt.dwMask = CFM_BACKCOLOR;
482 fmt.dwEffects = 0;
483 if (info->rtfParam == 0)
484 fmt.dwEffects = CFE_AUTOBACKCOLOR;
485 else if (info->rtfParam != rtfNoParam)
486 {
487 RTFColor *c = RTFGetColor(info, info->rtfParam);
488 if (c && c->rtfCBlue >= 0)
489 fmt.crTextColor = (c->rtfCBlue<<16)|(c->rtfCGreen<<8)|(c->rtfCRed);
490 else
491 fmt.dwEffects = CFE_AUTOBACKCOLOR;
492 }
493 break;
494 case rtfForeColor:
495 fmt.dwMask = CFM_COLOR;
496 fmt.dwEffects = 0;
497 if (info->rtfParam == 0)
498 fmt.dwEffects = CFE_AUTOCOLOR;
499 else if (info->rtfParam != rtfNoParam)
500 {
501 RTFColor *c = RTFGetColor(info, info->rtfParam);
502 if (c && c->rtfCBlue >= 0)
503 fmt.crTextColor = (c->rtfCBlue<<16)|(c->rtfCGreen<<8)|(c->rtfCRed);
504 else {
505 fmt.dwEffects = CFE_AUTOCOLOR;
506 }
507 }
508 break;
509 case rtfFontNum:
510 if (info->rtfParam != rtfNoParam)
511 {
512 RTFFont *f = RTFGetFont(info, info->rtfParam);
513 if (f)
514 {
515 MultiByteToWideChar(CP_ACP, 0, f->rtfFName, -1, fmt.szFaceName, sizeof(fmt.szFaceName)/sizeof(WCHAR));
516 fmt.szFaceName[sizeof(fmt.szFaceName)/sizeof(WCHAR)-1] = '\0';
517 fmt.bCharSet = f->rtfFCharSet;
518 fmt.dwMask = CFM_FACE | CFM_CHARSET;
519 fmt.bPitchAndFamily = f->rtfFPitch | (f->rtfFFamily << 4);
520 }
521 }
522 break;
523 case rtfFontSize:
524 fmt.dwMask = CFM_SIZE;
525 if (info->rtfParam != rtfNoParam)
526 fmt.yHeight = info->rtfParam*10;
527 break;
528 }
529 if (fmt.dwMask) {
530 ME_Style *style2;
531 RTFFlushOutputBuffer(info);
532 /* FIXME too slow ? how come ? */
533 style2 = ME_ApplyStyle(info->editor, info->style, &fmt);
534 ME_ReleaseStyle(info->style);
535 info->style = style2;
536 info->styleChanged = TRUE;
537 }
538 }
539
540 /* FIXME this function doesn't get any information about context of the RTF tag, which is very bad,
541 the same tags mean different things in different contexts */
542 void ME_RTFParAttrHook(RTF_Info *info)
543 {
544 switch(info->rtfMinor)
545 {
546 case rtfParDef: /* restores default paragraph attributes */
547 if (!info->editor->bEmulateVersion10) /* v4.1 */
548 info->borderType = RTFBorderParaLeft;
549 else /* v1.0 - 3.0 */
550 info->borderType = RTFBorderParaTop;
551 info->fmt.dwMask = PFM_ALIGNMENT | PFM_BORDER | PFM_LINESPACING | PFM_TABSTOPS |
552 PFM_OFFSET | PFM_RIGHTINDENT | PFM_SPACEAFTER | PFM_SPACEBEFORE |
553 PFM_STARTINDENT | PFM_RTLPARA;
554 /* TODO: numbering, shading */
555 info->fmt.wAlignment = PFA_LEFT;
556 info->fmt.cTabCount = 0;
557 info->fmt.dxOffset = info->fmt.dxStartIndent = info->fmt.dxRightIndent = 0;
558 info->fmt.wBorderWidth = info->fmt.wBorders = 0;
559 info->fmt.wBorderSpace = 0;
560 info->fmt.bLineSpacingRule = 0;
561 info->fmt.dySpaceBefore = info->fmt.dySpaceAfter = 0;
562 info->fmt.dyLineSpacing = 0;
563 info->fmt.wEffects &= ~PFE_RTLPARA;
564 if (!info->editor->bEmulateVersion10) /* v4.1 */
565 {
566 if (info->tableDef && info->tableDef->tableRowStart &&
567 info->tableDef->tableRowStart->member.para.nFlags & MEPF_ROWEND)
568 {
569 ME_Cursor cursor;
570 ME_DisplayItem *para;
571 /* We are just after a table row. */
572 RTFFlushOutputBuffer(info);
573 cursor = info->editor->pCursors[0];
574 para = cursor.pPara;
575 if (para == info->tableDef->tableRowStart->member.para.next_para
576 && !cursor.nOffset && !cursor.pRun->member.run.nCharOfs)
577 {
578 /* Since the table row end, no text has been inserted, and the \intbl
579 * control word has not be used. We can confirm that we are not in a
580 * table anymore.
581 */
582 info->tableDef->tableRowStart = NULL;
583 info->canInheritInTbl = FALSE;
584 }
585 }
586 } else { /* v1.0 - v3.0 */
587 info->fmt.dwMask |= PFM_TABLE;
588 info->fmt.wEffects &= ~PFE_TABLE;
589 }
590 break;
591 case rtfNestLevel:
592 if (!info->editor->bEmulateVersion10) /* v4.1 */
593 {
594 while (info->rtfParam > info->nestingLevel) {
595 RTFTable *tableDef = ALLOC_OBJ(RTFTable);
596 ZeroMemory(tableDef, sizeof(RTFTable));
597 tableDef->parent = info->tableDef;
598 info->tableDef = tableDef;
599
600 RTFFlushOutputBuffer(info);
601 if (tableDef->tableRowStart &&
602 tableDef->tableRowStart->member.para.nFlags & MEPF_ROWEND)
603 {
604 ME_DisplayItem *para = tableDef->tableRowStart;
605 para = para->member.para.next_para;
606 para = ME_InsertTableRowStartAtParagraph(info->editor, para);
607 tableDef->tableRowStart = para;
608 } else {
609 ME_Cursor cursor;
610 WCHAR endl = '\r';
611 cursor = info->editor->pCursors[0];
612 if (cursor.nOffset || cursor.pRun->member.run.nCharOfs)
613 ME_InsertTextFromCursor(info->editor, 0, &endl, 1, info->style);
614 tableDef->tableRowStart = ME_InsertTableRowStartFromCursor(info->editor);
615 }
616
617 info->nestingLevel++;
618 }
619 info->canInheritInTbl = FALSE;
620 }
621 break;
622 case rtfInTable:
623 {
624 if (!info->editor->bEmulateVersion10) /* v4.1 */
625 {
626 if (info->nestingLevel < 1)
627 {
628 RTFTable *tableDef;
629 if (!info->tableDef)
630 {
631 info->tableDef = ALLOC_OBJ(RTFTable);
632 ZeroMemory(info->tableDef, sizeof(RTFTable));
633 }
634 tableDef = info->tableDef;
635 RTFFlushOutputBuffer(info);
636 if (tableDef->tableRowStart &&
637 tableDef->tableRowStart->member.para.nFlags & MEPF_ROWEND)
638 {
639 ME_DisplayItem *para = tableDef->tableRowStart;
640 para = para->member.para.next_para;
641 para = ME_InsertTableRowStartAtParagraph(info->editor, para);
642 tableDef->tableRowStart = para;
643 } else {
644 ME_Cursor cursor;
645 WCHAR endl = '\r';
646 cursor = info->editor->pCursors[0];
647 if (cursor.nOffset || cursor.pRun->member.run.nCharOfs)
648 ME_InsertTextFromCursor(info->editor, 0, &endl, 1, info->style);
649 tableDef->tableRowStart = ME_InsertTableRowStartFromCursor(info->editor);
650 }
651 info->nestingLevel = 1;
652 info->canInheritInTbl = TRUE;
653 }
654 return;
655 } else { /* v1.0 - v3.0 */
656 info->fmt.dwMask |= PFM_TABLE;
657 info->fmt.wEffects |= PFE_TABLE;
658 }
659 break;
660 }
661 case rtfFirstIndent:
662 case rtfLeftIndent:
663 if ((info->fmt.dwMask & (PFM_STARTINDENT | PFM_OFFSET)) != (PFM_STARTINDENT | PFM_OFFSET))
664 {
665 PARAFORMAT2 fmt;
666 fmt.cbSize = sizeof(fmt);
667 ME_GetSelectionParaFormat(info->editor, &fmt);
668 info->fmt.dwMask |= PFM_STARTINDENT | PFM_OFFSET;
669 info->fmt.dxStartIndent = fmt.dxStartIndent;
670 info->fmt.dxOffset = fmt.dxOffset;
671 }
672 if (info->rtfMinor == rtfFirstIndent)
673 {
674 info->fmt.dxStartIndent += info->fmt.dxOffset + info->rtfParam;
675 info->fmt.dxOffset = -info->rtfParam;
676 }
677 else
678 info->fmt.dxStartIndent = info->rtfParam - info->fmt.dxOffset;
679 break;
680 case rtfRightIndent:
681 info->fmt.dwMask |= PFM_RIGHTINDENT;
682 info->fmt.dxRightIndent = info->rtfParam;
683 break;
684 case rtfQuadLeft:
685 case rtfQuadJust:
686 info->fmt.dwMask |= PFM_ALIGNMENT;
687 info->fmt.wAlignment = PFA_LEFT;
688 break;
689 case rtfQuadRight:
690 info->fmt.dwMask |= PFM_ALIGNMENT;
691 info->fmt.wAlignment = PFA_RIGHT;
692 break;
693 case rtfQuadCenter:
694 info->fmt.dwMask |= PFM_ALIGNMENT;
695 info->fmt.wAlignment = PFA_CENTER;
696 break;
697 case rtfTabPos:
698 if (!(info->fmt.dwMask & PFM_TABSTOPS))
699 {
700 PARAFORMAT2 fmt;
701 fmt.cbSize = sizeof(fmt);
702 ME_GetSelectionParaFormat(info->editor, &fmt);
703 memcpy(info->fmt.rgxTabs, fmt.rgxTabs,
704 fmt.cTabCount * sizeof(fmt.rgxTabs[0]));
705 info->fmt.cTabCount = fmt.cTabCount;
706 info->fmt.dwMask |= PFM_TABSTOPS;
707 }
708 if (info->fmt.cTabCount < MAX_TAB_STOPS && info->rtfParam < 0x1000000)
709 info->fmt.rgxTabs[info->fmt.cTabCount++] = info->rtfParam;
710 break;
711 case rtfKeep:
712 info->fmt.dwMask |= PFM_KEEP;
713 info->fmt.wEffects |= PFE_KEEP;
714 break;
715 case rtfNoWidowControl:
716 info->fmt.dwMask |= PFM_NOWIDOWCONTROL;
717 info->fmt.wEffects |= PFE_NOWIDOWCONTROL;
718 break;
719 case rtfKeepNext:
720 info->fmt.dwMask |= PFM_KEEPNEXT;
721 info->fmt.wEffects |= PFE_KEEPNEXT;
722 break;
723 case rtfSpaceAfter:
724 info->fmt.dwMask |= PFM_SPACEAFTER;
725 info->fmt.dySpaceAfter = info->rtfParam;
726 break;
727 case rtfSpaceBefore:
728 info->fmt.dwMask |= PFM_SPACEBEFORE;
729 info->fmt.dySpaceBefore = info->rtfParam;
730 break;
731 case rtfSpaceBetween:
732 info->fmt.dwMask |= PFM_LINESPACING;
733 if ((int)info->rtfParam > 0)
734 {
735 info->fmt.dyLineSpacing = info->rtfParam;
736 info->fmt.bLineSpacingRule = 3;
737 }
738 else
739 {
740 info->fmt.dyLineSpacing = info->rtfParam;
741 info->fmt.bLineSpacingRule = 4;
742 }
743 break;
744 case rtfSpaceMultiply:
745 info->fmt.dwMask |= PFM_LINESPACING;
746 info->fmt.dyLineSpacing = info->rtfParam * 20;
747 info->fmt.bLineSpacingRule = 5;
748 break;
749 case rtfParBullet:
750 info->fmt.dwMask |= PFM_NUMBERING;
751 info->fmt.wNumbering = PFN_BULLET;
752 break;
753 case rtfParSimple:
754 info->fmt.dwMask |= PFM_NUMBERING;
755 info->fmt.wNumbering = 2; /* FIXME: MSDN says it's not used ?? */
756 break;
757 case rtfParNumDecimal:
758 info->fmt.dwMask |= PFM_NUMBERING;
759 info->fmt.wNumbering = 2; /* FIXME: MSDN says it's not used ?? */
760 break;
761 case rtfParNumIndent:
762 info->fmt.dwMask |= PFM_NUMBERINGTAB;
763 info->fmt.wNumberingTab = info->rtfParam;
764 break;
765 case rtfParNumStartAt:
766 info->fmt.dwMask |= PFM_NUMBERINGSTART;
767 info->fmt.wNumberingStart = info->rtfParam;
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.pFmt;
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.pFmt;
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.pFmt->dxOffset = abs(info->tableDef->gapH);
1060 para->member.para.pFmt->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.pFmt;
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.pFmt;
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 BOOL ME_RTFInsertOleObject(RTF_Info *info, 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 BOOL ret = FALSE;
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 (!info->lpRichEditOle)
1157 {
1158 CreateIRichEditOle(NULL, info->editor, (VOID**)&info->lpRichEditOle);
1159 }
1160
1161 if (OleCreateDefaultHandler(&CLSID_NULL, NULL, &IID_IOleObject, (void**)&lpObject) == S_OK &&
1162 IRichEditOle_GetClientSite(info->lpRichEditOle, &lpClientSite) == S_OK &&
1163 IOleObject_SetClientSite(lpObject, lpClientSite) == S_OK &&
1164 IOleObject_GetUserClassID(lpObject, &clsid) == S_OK &&
1165 IOleObject_QueryInterface(lpObject, &IID_IOleCache, (void**)&lpOleCache) == S_OK &&
1166 IOleCache_Cache(lpOleCache, &fm, 0, &conn) == S_OK &&
1167 IOleObject_QueryInterface(lpObject, &IID_IDataObject, (void**)&lpDataObject) == S_OK &&
1168 IDataObject_SetData(lpDataObject, &fm, &stgm, TRUE) == S_OK)
1169 {
1170 REOBJECT reobject;
1171
1172 reobject.cbStruct = sizeof(reobject);
1173 reobject.cp = REO_CP_SELECTION;
1174 reobject.clsid = clsid;
1175 reobject.poleobj = lpObject;
1176 reobject.pstg = lpStorage;
1177 reobject.polesite = lpClientSite;
1178 /* convert from twips to .01 mm */
1179 reobject.sizel.cx = MulDiv(sz->cx, 254, 144);
1180 reobject.sizel.cy = MulDiv(sz->cy, 254, 144);
1181 reobject.dvaspect = DVASPECT_CONTENT;
1182 reobject.dwFlags = 0; /* FIXME */
1183 reobject.dwUser = 0;
1184
1185 ME_InsertOLEFromCursor(info->editor, &reobject, 0);
1186 ret = TRUE;
1187 }
1188
1189 if (lpObject) IOleObject_Release(lpObject);
1190 if (lpClientSite) IOleClientSite_Release(lpClientSite);
1191 if (lpStorage) IStorage_Release(lpStorage);
1192 if (lpDataObject) IDataObject_Release(lpDataObject);
1193 if (lpOleCache) IOleCache_Release(lpOleCache);
1194
1195 return ret;
1196 }
1197
1198 static void ME_RTFReadShpPictGroup( RTF_Info *info )
1199 {
1200 int level = 1;
1201
1202 for (;;)
1203 {
1204 RTFGetToken (info);
1205
1206 if (info->rtfClass == rtfEOF) return;
1207 if (RTFCheckCM( info, rtfGroup, rtfEndGroup ))
1208 {
1209 if (--level == 0) break;
1210 }
1211 else if (RTFCheckCM( info, rtfGroup, rtfBeginGroup ))
1212 {
1213 level++;
1214 }
1215 else
1216 {
1217 RTFRouteToken( info );
1218 if (RTFCheckCM( info, rtfGroup, rtfEndGroup ))
1219 level--;
1220 }
1221 }
1222
1223 RTFRouteToken( info ); /* feed "}" back to router */
1224 return;
1225 }
1226
1227 static DWORD read_hex_data( RTF_Info *info, BYTE **out )
1228 {
1229 DWORD read = 0, size = 1024;
1230 BYTE *buf, val;
1231 BOOL flip;
1232
1233 *out = NULL;
1234
1235 if (info->rtfClass != rtfText)
1236 {
1237 ERR("Called with incorrect token\n");
1238 return 0;
1239 }
1240
1241 buf = HeapAlloc( GetProcessHeap(), 0, size );
1242 if (!buf) return 0;
1243
1244 val = info->rtfMajor;
1245 for (flip = TRUE;; flip = !flip)
1246 {
1247 RTFGetToken( info );
1248 if (info->rtfClass == rtfEOF)
1249 {
1250 HeapFree( GetProcessHeap(), 0, buf );
1251 return 0;
1252 }
1253 if (info->rtfClass != rtfText) break;
1254 if (flip)
1255 {
1256 if (read >= size)
1257 {
1258 size *= 2;
1259 buf = HeapReAlloc( GetProcessHeap(), 0, buf, size );
1260 if (!buf) return 0;
1261 }
1262 buf[read++] = RTFCharToHex(val) * 16 + RTFCharToHex(info->rtfMajor);
1263 }
1264 else
1265 val = info->rtfMajor;
1266 }
1267 if (flip) FIXME("wrong hex string\n");
1268
1269 *out = buf;
1270 return read;
1271 }
1272
1273 static void ME_RTFReadPictGroup(RTF_Info *info)
1274 {
1275 SIZEL sz;
1276 BYTE *buffer = NULL;
1277 DWORD size = 0;
1278 METAFILEPICT mfp;
1279 HENHMETAFILE hemf;
1280 HBITMAP hbmp;
1281 enum gfxkind {gfx_unknown = 0, gfx_enhmetafile, gfx_metafile, gfx_dib} gfx = gfx_unknown;
1282 int level = 1;
1283
1284 mfp.mm = MM_TEXT;
1285 sz.cx = sz.cy = 0;
1286
1287 for (;;)
1288 {
1289 RTFGetToken( info );
1290
1291 if (info->rtfClass == rtfText)
1292 {
1293 if (level == 1)
1294 {
1295 if (!buffer)
1296 size = read_hex_data( info, &buffer );
1297 }
1298 else
1299 {
1300 RTFSkipGroup( info );
1301 }
1302 } /* We potentially have a new token so fall through. */
1303
1304 if (info->rtfClass == rtfEOF) return;
1305
1306 if (RTFCheckCM( info, rtfGroup, rtfEndGroup ))
1307 {
1308 if (--level == 0) break;
1309 continue;
1310 }
1311 if (RTFCheckCM( info, rtfGroup, rtfBeginGroup ))
1312 {
1313 level++;
1314 continue;
1315 }
1316 if (!RTFCheckCM( info, rtfControl, rtfPictAttr ))
1317 {
1318 RTFRouteToken( info );
1319 if (RTFCheckCM( info, rtfGroup, rtfEndGroup ))
1320 level--;
1321 continue;
1322 }
1323
1324 if (RTFCheckMM( info, rtfPictAttr, rtfWinMetafile ))
1325 {
1326 mfp.mm = info->rtfParam;
1327 gfx = gfx_metafile;
1328 }
1329 else if (RTFCheckMM( info, rtfPictAttr, rtfDevIndBitmap ))
1330 {
1331 if (info->rtfParam != 0) FIXME("dibitmap should be 0 (%d)\n", info->rtfParam);
1332 gfx = gfx_dib;
1333 }
1334 else if (RTFCheckMM( info, rtfPictAttr, rtfEmfBlip ))
1335 gfx = gfx_enhmetafile;
1336 else if (RTFCheckMM( info, rtfPictAttr, rtfPicWid ))
1337 mfp.xExt = info->rtfParam;
1338 else if (RTFCheckMM( info, rtfPictAttr, rtfPicHt ))
1339 mfp.yExt = info->rtfParam;
1340 else if (RTFCheckMM( info, rtfPictAttr, rtfPicGoalWid ))
1341 sz.cx = info->rtfParam;
1342 else if (RTFCheckMM( info, rtfPictAttr, rtfPicGoalHt ))
1343 sz.cy = info->rtfParam;
1344 else
1345 FIXME("Non supported attribute: %d %d %d\n", info->rtfClass, info->rtfMajor, info->rtfMinor);
1346 }
1347
1348 if (buffer)
1349 {
1350 switch (gfx)
1351 {
1352 case gfx_enhmetafile:
1353 if ((hemf = SetEnhMetaFileBits( size, buffer )))
1354 ME_RTFInsertOleObject( info, hemf, NULL, &sz );
1355 break;
1356 case gfx_metafile:
1357 if ((hemf = SetWinMetaFileBits( size, buffer, NULL, &mfp )))
1358 ME_RTFInsertOleObject( info, hemf, NULL, &sz );
1359 break;
1360 case gfx_dib:
1361 {
1362 BITMAPINFO *bi = (BITMAPINFO*)buffer;
1363 HDC hdc = GetDC(0);
1364 unsigned nc = bi->bmiHeader.biClrUsed;
1365
1366 /* not quite right, especially for bitfields type of compression */
1367 if (!nc && bi->bmiHeader.biBitCount <= 8)
1368 nc = 1 << bi->bmiHeader.biBitCount;
1369 if ((hbmp = CreateDIBitmap( hdc, &bi->bmiHeader,
1370 CBM_INIT, (char*)(bi + 1) + nc * sizeof(RGBQUAD),
1371 bi, DIB_RGB_COLORS)) )
1372 ME_RTFInsertOleObject( info, NULL, hbmp, &sz );
1373 ReleaseDC( 0, hdc );
1374 break;
1375 }
1376 default:
1377 break;
1378 }
1379 }
1380 HeapFree( GetProcessHeap(), 0, buffer );
1381 RTFRouteToken( info ); /* feed "}" back to router */
1382 return;
1383 }
1384
1385 /* for now, lookup the \result part and use it, whatever the object */
1386 static void ME_RTFReadObjectGroup(RTF_Info *info)
1387 {
1388 for (;;)
1389 {
1390 RTFGetToken (info);
1391 if (info->rtfClass == rtfEOF)
1392 return;
1393 if (RTFCheckCM(info, rtfGroup, rtfEndGroup))
1394 break;
1395 if (RTFCheckCM(info, rtfGroup, rtfBeginGroup))
1396 {
1397 RTFGetToken (info);
1398 if (info->rtfClass == rtfEOF)
1399 return;
1400 if (RTFCheckCMM(info, rtfControl, rtfDestination, rtfObjResult))
1401 {
1402 int level = 1;
1403
1404 while (RTFGetToken (info) != rtfEOF)
1405 {
1406 if (info->rtfClass == rtfGroup)
1407 {
1408 if (info->rtfMajor == rtfBeginGroup) level++;
1409 else if (info->rtfMajor == rtfEndGroup && --level < 0) break;
1410 }
1411 RTFRouteToken(info);
1412 }
1413 }
1414 else RTFSkipGroup(info);
1415 continue;
1416 }
1417 if (!RTFCheckCM (info, rtfControl, rtfObjAttr))
1418 {
1419 FIXME("Non supported attribute: %d %d %d\n", info->rtfClass, info->rtfMajor, info->rtfMinor);
1420 return;
1421 }
1422 }
1423 RTFRouteToken(info); /* feed "}" back to router */
1424 }
1425
1426 static void ME_RTFReadHook(RTF_Info *info)
1427 {
1428 switch(info->rtfClass)
1429 {
1430 case rtfGroup:
1431 switch(info->rtfMajor)
1432 {
1433 case rtfBeginGroup:
1434 if (info->stackTop < maxStack) {
1435 info->stack[info->stackTop].style = info->style;
1436 ME_AddRefStyle(info->style);
1437 info->stack[info->stackTop].codePage = info->codePage;
1438 info->stack[info->stackTop].unicodeLength = info->unicodeLength;
1439 }
1440 info->stackTop++;
1441 info->styleChanged = FALSE;
1442 break;
1443 case rtfEndGroup:
1444 {
1445 RTFFlushOutputBuffer(info);
1446 info->stackTop--;
1447 if (info->stackTop <= 0)
1448 info->rtfClass = rtfEOF;
1449 if (info->stackTop < 0)
1450 return;
1451
1452 ME_ReleaseStyle(info->style);
1453 info->style = info->stack[info->stackTop].style;
1454 info->codePage = info->stack[info->stackTop].codePage;
1455 info->unicodeLength = info->stack[info->stackTop].unicodeLength;
1456 break;
1457 }
1458 }
1459 break;
1460 }
1461 }
1462
1463 void
1464 ME_StreamInFill(ME_InStream *stream)
1465 {
1466 stream->editstream->dwError = stream->editstream->pfnCallback(stream->editstream->dwCookie,
1467 (BYTE *)stream->buffer,
1468 sizeof(stream->buffer),
1469 (LONG *)&stream->dwSize);
1470 stream->dwUsed = 0;
1471 }
1472
1473 static LRESULT ME_StreamIn(ME_TextEditor *editor, DWORD format, EDITSTREAM *stream, BOOL stripLastCR)
1474 {
1475 RTF_Info parser;
1476 ME_Style *style;
1477 int from, to, nUndoMode;
1478 int nEventMask = editor->nEventMask;
1479 ME_InStream inStream;
1480 BOOL invalidRTF = FALSE;
1481 ME_Cursor *selStart, *selEnd;
1482 LRESULT num_read = 0; /* bytes read for SF_TEXT, non-control chars inserted for SF_RTF */
1483
1484 TRACE("stream==%p editor==%p format==0x%X\n", stream, editor, format);
1485 editor->nEventMask = 0;
1486
1487 ME_GetSelectionOfs(editor, &from, &to);
1488 if (format & SFF_SELECTION && editor->mode & TM_RICHTEXT)
1489 {
1490 ME_GetSelection(editor, &selStart, &selEnd);
1491 style = ME_GetSelectionInsertStyle(editor);
1492
1493 ME_InternalDeleteText(editor, selStart, to - from, FALSE);
1494
1495 /* Don't insert text at the end of the table row */
1496 if (!editor->bEmulateVersion10) { /* v4.1 */
1497 ME_DisplayItem *para = editor->pCursors->pPara;
1498 if (para->member.para.nFlags & MEPF_ROWEND)
1499 {
1500 para = para->member.para.next_para;
1501 editor->pCursors[0].pPara = para;
1502 editor->pCursors[0].pRun = ME_FindItemFwd(para, diRun);
1503 editor->pCursors[0].nOffset = 0;
1504 }
1505 if (para->member.para.nFlags & MEPF_ROWSTART)
1506 {
1507 para = para->member.para.next_para;
1508 editor->pCursors[0].pPara = para;
1509 editor->pCursors[0].pRun = ME_FindItemFwd(para, diRun);
1510 editor->pCursors[0].nOffset = 0;
1511 }
1512 editor->pCursors[1] = editor->pCursors[0];
1513 } else { /* v1.0 - 3.0 */
1514 if (editor->pCursors[0].pRun->member.run.nFlags & MERF_ENDPARA &&
1515 ME_IsInTable(editor->pCursors[0].pRun))
1516 return 0;
1517 }
1518 } else {
1519 style = editor->pBuffer->pDefaultStyle;
1520 ME_AddRefStyle(style);
1521 ME_SetSelection(editor, 0, 0);
1522 ME_InternalDeleteText(editor, &editor->pCursors[1],
1523 ME_GetTextLength(editor), FALSE);
1524 from = to = 0;
1525 ME_ClearTempStyle(editor);
1526 ME_SetDefaultParaFormat(editor, editor->pCursors[0].pPara->member.para.pFmt);
1527 }
1528
1529
1530 /* Back up undo mode to a local variable */
1531 nUndoMode = editor->nUndoMode;
1532
1533 /* Only create an undo if SFF_SELECTION is set */
1534 if (!(format & SFF_SELECTION))
1535 editor->nUndoMode = umIgnore;
1536
1537 inStream.editstream = stream;
1538 inStream.editstream->dwError = 0;
1539 inStream.dwSize = 0;
1540 inStream.dwUsed = 0;
1541
1542 if (format & SF_RTF)
1543 {
1544 /* Check if it's really RTF, and if it is not, use plain text */
1545 ME_StreamInFill(&inStream);
1546 if (!inStream.editstream->dwError)
1547 {
1548 if ((!editor->bEmulateVersion10 && strncmp(inStream.buffer, "{\\rtf", 5) && strncmp(inStream.buffer, "{\\urtf", 6))
1549 || (editor->bEmulateVersion10 && *inStream.buffer != '{'))
1550 {
1551 invalidRTF = TRUE;
1552 inStream.editstream->dwError = -16;
1553 }
1554 }
1555 }
1556
1557 if (!invalidRTF && !inStream.editstream->dwError)
1558 {
1559 if (format & SF_RTF) {
1560 from = ME_GetCursorOfs(&editor->pCursors[0]);
1561
1562 /* setup the RTF parser */
1563 memset(&parser, 0, sizeof parser);
1564 RTFSetEditStream(&parser, &inStream);
1565 parser.rtfFormat = format&(SF_TEXT|SF_RTF);
1566 parser.editor = editor;
1567 parser.style = style;
1568 WriterInit(&parser);
1569 RTFInit(&parser);
1570 RTFSetReadHook(&parser, ME_RTFReadHook);
1571 RTFSetDestinationCallback(&parser, rtfShpPict, ME_RTFReadShpPictGroup);
1572 RTFSetDestinationCallback(&parser, rtfPict, ME_RTFReadPictGroup);
1573 RTFSetDestinationCallback(&parser, rtfObject, ME_RTFReadObjectGroup);
1574 if (!parser.editor->bEmulateVersion10) /* v4.1 */
1575 {
1576 RTFSetDestinationCallback(&parser, rtfNoNestTables, RTFSkipGroup);
1577 RTFSetDestinationCallback(&parser, rtfNestTableProps, RTFReadGroup);
1578 }
1579 BeginFile(&parser);
1580
1581 /* do the parsing */
1582 RTFRead(&parser);
1583 RTFFlushOutputBuffer(&parser);
1584 if (!editor->bEmulateVersion10) { /* v4.1 */
1585 if (parser.tableDef && parser.tableDef->tableRowStart &&
1586 (parser.nestingLevel > 0 || parser.canInheritInTbl))
1587 {
1588 /* Delete any incomplete table row at the end of the rich text. */
1589 int nOfs, nChars;
1590 ME_DisplayItem *para;
1591
1592 parser.rtfMinor = rtfRow;
1593 /* Complete the table row before deleting it.
1594 * By doing it this way we will have the current paragraph format set
1595 * properly to reflect that is not in the complete table, and undo items
1596 * will be added for this change to the current paragraph format. */
1597 if (parser.nestingLevel > 0)
1598 {
1599 while (parser.nestingLevel > 1)
1600 ME_RTFSpecialCharHook(&parser); /* Decrements nestingLevel */
1601 para = parser.tableDef->tableRowStart;
1602 ME_RTFSpecialCharHook(&parser);
1603 } else {
1604 para = parser.tableDef->tableRowStart;
1605 ME_RTFSpecialCharHook(&parser);
1606 assert(para->member.para.nFlags & MEPF_ROWEND);
1607 para = para->member.para.next_para;
1608 }
1609
1610 editor->pCursors[1].pPara = para;
1611 editor->pCursors[1].pRun = ME_FindItemFwd(para, diRun);
1612 editor->pCursors[1].nOffset = 0;
1613 nOfs = ME_GetCursorOfs(&editor->pCursors[1]);
1614 nChars = ME_GetCursorOfs(&editor->pCursors[0]) - nOfs;
1615 ME_InternalDeleteText(editor, &editor->pCursors[1], nChars, TRUE);
1616 if (parser.tableDef)
1617 parser.tableDef->tableRowStart = NULL;
1618 }
1619 }
1620 ME_CheckTablesForCorruption(editor);
1621 RTFDestroy(&parser);
1622 if (parser.lpRichEditOle)
1623 IRichEditOle_Release(parser.lpRichEditOle);
1624
1625 if (parser.stackTop > 0)
1626 {
1627 while (--parser.stackTop >= 0)
1628 {
1629 ME_ReleaseStyle(parser.style);
1630 parser.style = parser.stack[parser.stackTop].style;
1631 }
1632 if (!inStream.editstream->dwError)
1633 inStream.editstream->dwError = HRESULT_FROM_WIN32(ERROR_HANDLE_EOF);
1634 }
1635
1636 /* Remove last line break, as mandated by tests. This is not affected by
1637 CR/LF counters, since RTF streaming presents only \para tokens, which
1638 are converted according to the standard rules: \r for 2.0, \r\n for 1.0
1639 */
1640 if (stripLastCR && !(format & SFF_SELECTION)) {
1641 int newto;
1642 ME_GetSelection(editor, &selStart, &selEnd);
1643 newto = ME_GetCursorOfs(selEnd);
1644 if (newto > to + (editor->bEmulateVersion10 ? 1 : 0)) {
1645 WCHAR lastchar[3] = {'\0', '\0'};
1646 int linebreakSize = editor->bEmulateVersion10 ? 2 : 1;
1647 ME_Cursor linebreakCursor = *selEnd;
1648
1649 ME_MoveCursorChars(editor, &linebreakCursor, -linebreakSize);
1650 ME_GetTextW(editor, lastchar, 2, &linebreakCursor, linebreakSize, FALSE, FALSE);
1651 if (lastchar[0] == '\r' && (lastchar[1] == '\n' || lastchar[1] == '\0')) {
1652 ME_InternalDeleteText(editor, &linebreakCursor, linebreakSize, FALSE);
1653 }
1654 }
1655 }
1656 to = ME_GetCursorOfs(&editor->pCursors[0]);
1657 num_read = to - from;
1658
1659 style = parser.style;
1660 }
1661 else if (format & SF_TEXT)
1662 num_read = ME_StreamInText(editor, format, &inStream, style);
1663 else
1664 ERR("EM_STREAMIN without SF_TEXT or SF_RTF\n");
1665 /* put the cursor at the top */
1666 if (!(format & SFF_SELECTION))
1667 ME_SetSelection(editor, 0, 0);
1668 }
1669
1670 /* Restore saved undo mode */
1671 editor->nUndoMode = nUndoMode;
1672
1673 /* even if we didn't add an undo, we need to commit anything on the stack */
1674 ME_CommitUndo(editor);
1675
1676 /* If SFF_SELECTION isn't set, delete any undos from before we started too */
1677 if (!(format & SFF_SELECTION))
1678 ME_EmptyUndoStack(editor);
1679
1680 ME_ReleaseStyle(style);
1681 editor->nEventMask = nEventMask;
1682 ME_UpdateRepaint(editor, FALSE);
1683 if (!(format & SFF_SELECTION)) {
1684 ME_ClearTempStyle(editor);
1685 }
1686 ITextHost_TxShowCaret(editor->texthost, FALSE);
1687 ME_MoveCaret(editor);
1688 ITextHost_TxShowCaret(editor->texthost, TRUE);
1689 ME_SendSelChange(editor);
1690 ME_SendRequestResize(editor, FALSE);
1691
1692 return num_read;
1693 }
1694
1695
1696 typedef struct tagME_RTFStringStreamStruct
1697 {
1698 char *string;
1699 int pos;
1700 int length;
1701 } ME_RTFStringStreamStruct;
1702
1703 static DWORD CALLBACK ME_ReadFromRTFString(DWORD_PTR dwCookie, LPBYTE lpBuff, LONG cb, LONG *pcb)
1704 {
1705 ME_RTFStringStreamStruct *pStruct = (ME_RTFStringStreamStruct *)dwCookie;
1706 int count;
1707
1708 count = min(cb, pStruct->length - pStruct->pos);
1709 memmove(lpBuff, pStruct->string + pStruct->pos, count);
1710 pStruct->pos += count;
1711 *pcb = count;
1712 return 0;
1713 }
1714
1715 static void
1716 ME_StreamInRTFString(ME_TextEditor *editor, BOOL selection, char *string)
1717 {
1718 EDITSTREAM es;
1719 ME_RTFStringStreamStruct data;
1720
1721 data.string = string;
1722 data.length = strlen(string);
1723 data.pos = 0;
1724 es.dwCookie = (DWORD_PTR)&data;
1725 es.pfnCallback = ME_ReadFromRTFString;
1726 ME_StreamIn(editor, SF_RTF | (selection ? SFF_SELECTION : 0), &es, TRUE);
1727 }
1728
1729
1730 static int
1731 ME_FindText(ME_TextEditor *editor, DWORD flags, const CHARRANGE *chrg, const WCHAR *text, CHARRANGE *chrgText)
1732 {
1733 const int nLen = lstrlenW(text);
1734 const int nTextLen = ME_GetTextLength(editor);
1735 int nMin, nMax;
1736 ME_Cursor cursor;
1737 WCHAR wLastChar = ' ';
1738
1739 TRACE("flags==0x%08x, chrg->cpMin==%d, chrg->cpMax==%d text==%s\n",
1740 flags, chrg->cpMin, chrg->cpMax, debugstr_w(text));
1741
1742 if (flags & ~(FR_DOWN | FR_MATCHCASE | FR_WHOLEWORD))
1743 FIXME("Flags 0x%08x not implemented\n",
1744 flags & ~(FR_DOWN | FR_MATCHCASE | FR_WHOLEWORD));
1745
1746 nMin = chrg->cpMin;
1747 if (chrg->cpMax == -1)
1748 nMax = nTextLen;
1749 else
1750 nMax = chrg->cpMax > nTextLen ? nTextLen : chrg->cpMax;
1751
1752 /* In 1.0 emulation, if cpMax reaches end of text, add the FR_DOWN flag */
1753 if (editor->bEmulateVersion10 && nMax == nTextLen)
1754 {
1755 flags |= FR_DOWN;
1756 }
1757
1758 /* In 1.0 emulation, cpMin must always be no greater than cpMax */
1759 if (editor->bEmulateVersion10 && nMax < nMin)
1760 {
1761 if (chrgText)
1762 {
1763 chrgText->cpMin = -1;
1764 chrgText->cpMax = -1;
1765 }
1766 return -1;
1767 }
1768
1769 /* when searching up, if cpMin < cpMax, then instead of searching
1770 * on [cpMin,cpMax], we search on [0,cpMin], otherwise, search on
1771 * [cpMax, cpMin]. The exception is when cpMax is -1, in which
1772 * case, it is always bigger than cpMin.
1773 */
1774 if (!editor->bEmulateVersion10 && !(flags & FR_DOWN))
1775 {
1776 int nSwap = nMax;
1777
1778 nMax = nMin > nTextLen ? nTextLen : nMin;
1779 if (nMin < nSwap || chrg->cpMax == -1)
1780 nMin = 0;
1781 else
1782 nMin = nSwap;
1783 }
1784
1785 if (!nLen || nMin < 0 || nMax < 0 || nMax < nMin)
1786 {
1787 if (chrgText)
1788 chrgText->cpMin = chrgText->cpMax = -1;
1789 return -1;
1790 }
1791
1792 if (flags & FR_DOWN) /* Forward search */
1793 {
1794 /* If possible, find the character before where the search starts */
1795 if ((flags & FR_WHOLEWORD) && nMin)
1796 {
1797 ME_CursorFromCharOfs(editor, nMin - 1, &cursor);
1798 wLastChar = *get_text( &cursor.pRun->member.run, cursor.nOffset );
1799 ME_MoveCursorChars(editor, &cursor, 1);
1800 } else {
1801 ME_CursorFromCharOfs(editor, nMin, &cursor);
1802 }
1803
1804 while (cursor.pRun && ME_GetCursorOfs(&cursor) + nLen <= nMax)
1805 {
1806 ME_DisplayItem *pCurItem = cursor.pRun;
1807 int nCurStart = cursor.nOffset;
1808 int nMatched = 0;
1809
1810 while (pCurItem && ME_CharCompare( *get_text( &pCurItem->member.run, nCurStart + nMatched ), text[nMatched], (flags & FR_MATCHCASE)))
1811 {
1812 if ((flags & FR_WHOLEWORD) && isalnumW(wLastChar))
1813 break;
1814
1815 nMatched++;
1816 if (nMatched == nLen)
1817 {
1818 ME_DisplayItem *pNextItem = pCurItem;
1819 int nNextStart = nCurStart;
1820 WCHAR wNextChar;
1821
1822 /* Check to see if next character is a whitespace */
1823 if (flags & FR_WHOLEWORD)
1824 {
1825 if (nCurStart + nMatched == pCurItem->member.run.len)
1826 {
1827 pNextItem = ME_FindItemFwd(pCurItem, diRun);
1828 nNextStart = -nMatched;
1829 }
1830
1831 if (pNextItem)
1832 wNextChar = *get_text( &pNextItem->member.run, nNextStart + nMatched );
1833 else
1834 wNextChar = ' ';
1835
1836 if (isalnumW(wNextChar))
1837 break;
1838 }
1839
1840 cursor.nOffset += cursor.pPara->member.para.nCharOfs + cursor.pRun->member.run.nCharOfs;
1841 if (chrgText)
1842 {
1843 chrgText->cpMin = cursor.nOffset;
1844 chrgText->cpMax = cursor.nOffset + nLen;
1845 }
1846 TRACE("found at %d-%d\n", cursor.nOffset, cursor.nOffset + nLen);
1847 return cursor.nOffset;
1848 }
1849 if (nCurStart + nMatched == pCurItem->member.run.len)
1850 {
1851 pCurItem = ME_FindItemFwd(pCurItem, diRun);
1852 nCurStart = -nMatched;
1853 }
1854 }
1855 if (pCurItem)
1856 wLastChar = *get_text( &pCurItem->member.run, nCurStart + nMatched );
1857 else
1858 wLastChar = ' ';
1859
1860 cursor.nOffset++;
1861 if (cursor.nOffset == cursor.pRun->member.run.len)
1862 {
1863 ME_NextRun(&cursor.pPara, &cursor.pRun);
1864 cursor.nOffset = 0;
1865 }
1866 }
1867 }
1868 else /* Backward search */
1869 {
1870 /* If possible, find the character after where the search ends */
1871 if ((flags & FR_WHOLEWORD) && nMax < nTextLen - 1)
1872 {
1873 ME_CursorFromCharOfs(editor, nMax + 1, &cursor);
1874 wLastChar = *get_text( &cursor.pRun->member.run, cursor.nOffset );
1875 ME_MoveCursorChars(editor, &cursor, -1);
1876 } else {
1877 ME_CursorFromCharOfs(editor, nMax, &cursor);
1878 }
1879
1880 while (cursor.pRun && ME_GetCursorOfs(&cursor) - nLen >= nMin)
1881 {
1882 ME_DisplayItem *pCurItem = cursor.pRun;
1883 ME_DisplayItem *pCurPara = cursor.pPara;
1884 int nCurEnd = cursor.nOffset;
1885 int nMatched = 0;
1886
1887 if (nCurEnd == 0)
1888 {
1889 ME_PrevRun(&pCurPara, &pCurItem);
1890 nCurEnd = pCurItem->member.run.len;
1891 }
1892
1893 while (pCurItem && ME_CharCompare( *get_text( &pCurItem->member.run, nCurEnd - nMatched - 1 ),
1894 text[nLen - nMatched - 1], (flags & FR_MATCHCASE) ))
1895 {
1896 if ((flags & FR_WHOLEWORD) && isalnumW(wLastChar))
1897 break;
1898
1899 nMatched++;
1900 if (nMatched == nLen)
1901 {
1902 ME_DisplayItem *pPrevItem = pCurItem;
1903 int nPrevEnd = nCurEnd;
1904 WCHAR wPrevChar;
1905 int nStart;
1906
1907 /* Check to see if previous character is a whitespace */
1908 if (flags & FR_WHOLEWORD)
1909 {
1910 if (nPrevEnd - nMatched == 0)
1911 {
1912 pPrevItem = ME_FindItemBack(pCurItem, diRun);
1913 if (pPrevItem)
1914 nPrevEnd = pPrevItem->member.run.len + nMatched;
1915 }
1916
1917 if (pPrevItem)
1918 wPrevChar = *get_text( &pPrevItem->member.run, nPrevEnd - nMatched - 1 );
1919 else
1920 wPrevChar = ' ';
1921
1922 if (isalnumW(wPrevChar))
1923 break;
1924 }
1925
1926 nStart = pCurPara->member.para.nCharOfs
1927 + pCurItem->member.run.nCharOfs + nCurEnd - nMatched;
1928 if (chrgText)
1929 {
1930 chrgText->cpMin = nStart;
1931 chrgText->cpMax = nStart + nLen;
1932 }
1933 TRACE("found at %d-%d\n", nStart, nStart + nLen);
1934 return nStart;
1935 }
1936 if (nCurEnd - nMatched == 0)
1937 {
1938 ME_PrevRun(&pCurPara, &pCurItem);
1939 /* Don't care about pCurItem becoming NULL here; it's already taken
1940 * care of in the exterior loop condition */
1941 nCurEnd = pCurItem->member.run.len + nMatched;
1942 }
1943 }
1944 if (pCurItem)
1945 wLastChar = *get_text( &pCurItem->member.run, nCurEnd - nMatched - 1 );
1946 else
1947 wLastChar = ' ';
1948
1949 cursor.nOffset--;
1950 if (cursor.nOffset < 0)
1951 {
1952 ME_PrevRun(&cursor.pPara, &cursor.pRun);
1953 cursor.nOffset = cursor.pRun->member.run.len;
1954 }
1955 }
1956 }
1957 TRACE("not found\n");
1958 if (chrgText)
1959 chrgText->cpMin = chrgText->cpMax = -1;
1960 return -1;
1961 }
1962
1963 static int ME_GetTextEx(ME_TextEditor *editor, GETTEXTEX *ex, LPARAM pText)
1964 {
1965 int nChars;
1966 ME_Cursor start;
1967
1968 if (!ex->cb || !pText) return 0;
1969
1970 if (ex->flags & ~(GT_SELECTION | GT_USECRLF))
1971 FIXME("GETTEXTEX flags 0x%08x not supported\n", ex->flags & ~(GT_SELECTION | GT_USECRLF));
1972
1973 if (ex->flags & GT_SELECTION)
1974 {
1975 int from, to;
1976 int nStartCur = ME_GetSelectionOfs(editor, &from, &to);
1977 start = editor->pCursors[nStartCur];
1978 nChars = to - from;
1979 }
1980 else
1981 {
1982 ME_SetCursorToStart(editor, &start);
1983 nChars = INT_MAX;
1984 }
1985 if (ex->codepage == CP_UNICODE)
1986 {
1987 return ME_GetTextW(editor, (LPWSTR)pText, ex->cb / sizeof(WCHAR) - 1,
1988 &start, nChars, ex->flags & GT_USECRLF, FALSE);
1989 }
1990 else
1991 {
1992 /* potentially each char may be a CR, why calculate the exact value with O(N) when
1993 we can just take a bigger buffer? :)
1994 The above assumption still holds with CR/LF counters, since CR->CRLF expansion
1995 occurs only in richedit 2.0 mode, in which line breaks have only one CR
1996 */
1997 int crlfmul = (ex->flags & GT_USECRLF) ? 2 : 1;
1998 DWORD buflen;
1999 LPWSTR buffer;
2000 LRESULT rc;
2001
2002 buflen = min(crlfmul * nChars, ex->cb - 1);
2003 buffer = heap_alloc((buflen + 1) * sizeof(WCHAR));
2004
2005 nChars = ME_GetTextW(editor, buffer, buflen, &start, nChars, ex->flags & GT_USECRLF, FALSE);
2006 rc = WideCharToMultiByte(ex->codepage, 0, buffer, nChars + 1,
2007 (LPSTR)pText, ex->cb, ex->lpDefaultChar, ex->lpUsedDefChar);
2008 if (rc) rc--; /* do not count 0 terminator */
2009
2010 heap_free(buffer);
2011 return rc;
2012 }
2013 }
2014
2015 static int ME_GetTextRange(ME_TextEditor *editor, WCHAR *strText,
2016 const ME_Cursor *start, int nLen, BOOL unicode)
2017 {
2018 if (!strText) return 0;
2019 if (unicode) {
2020 return ME_GetTextW(editor, strText, INT_MAX, start, nLen, FALSE, FALSE);
2021 } else {
2022 int nChars;
2023 WCHAR *p = ALLOC_N_OBJ(WCHAR, nLen+1);
2024 if (!p) return 0;
2025 nChars = ME_GetTextW(editor, p, nLen, start, nLen, FALSE, FALSE);
2026 WideCharToMultiByte(CP_ACP, 0, p, nChars+1, (char *)strText,
2027 nLen+1, NULL, NULL);
2028 FREE_OBJ(p);
2029 return nChars;
2030 }
2031 }
2032
2033 static int handle_EM_EXSETSEL( ME_TextEditor *editor, int to, int from )
2034 {
2035 int end;
2036
2037 TRACE("%d - %d\n", to, from );
2038
2039 ME_InvalidateSelection( editor );
2040 end = ME_SetSelection( editor, to, from );
2041 ME_InvalidateSelection( editor );
2042 ITextHost_TxShowCaret( editor->texthost, FALSE );
2043 ME_ShowCaret( editor );
2044 ME_SendSelChange( editor );
2045
2046 return end;
2047 }
2048
2049 typedef struct tagME_GlobalDestStruct
2050 {
2051 HGLOBAL hData;
2052 int nLength;
2053 } ME_GlobalDestStruct;
2054
2055 static DWORD CALLBACK ME_ReadFromHGLOBALUnicode(DWORD_PTR dwCookie, LPBYTE lpBuff, LONG cb, LONG *pcb)
2056 {
2057 ME_GlobalDestStruct *pData = (ME_GlobalDestStruct *)dwCookie;
2058 int i;
2059 WORD *pSrc, *pDest;
2060
2061 cb = cb >> 1;
2062 pDest = (WORD *)lpBuff;
2063 pSrc = GlobalLock(pData->hData);
2064 for (i = 0; i<cb && pSrc[pData->nLength+i]; i++) {
2065 pDest[i] = pSrc[pData->nLength+i];
2066 }
2067 pData->nLength += i;
2068 *pcb = 2*i;
2069 GlobalUnlock(pData->hData);
2070 return 0;
2071 }
2072
2073 static DWORD CALLBACK ME_ReadFromHGLOBALRTF(DWORD_PTR dwCookie, LPBYTE lpBuff, LONG cb, LONG *pcb)
2074 {
2075 ME_GlobalDestStruct *pData = (ME_GlobalDestStruct *)dwCookie;
2076 int i;
2077 BYTE *pSrc, *pDest;
2078
2079 pDest = lpBuff;
2080 pSrc = GlobalLock(pData->hData);
2081 for (i = 0; i<cb && pSrc[pData->nLength+i]; i++) {
2082 pDest[i] = pSrc[pData->nLength+i];
2083 }
2084 pData->nLength += i;
2085 *pcb = i;
2086 GlobalUnlock(pData->hData);
2087 return 0;
2088 }
2089
2090 static BOOL ME_Paste(ME_TextEditor *editor)
2091 {
2092 DWORD dwFormat = 0;
2093 EDITSTREAM es;
2094 ME_GlobalDestStruct gds;
2095 UINT nRTFFormat = RegisterClipboardFormatA("Rich Text Format");
2096 UINT cf = 0;
2097
2098 if (IsClipboardFormatAvailable(nRTFFormat))
2099 cf = nRTFFormat, dwFormat = SF_RTF;
2100 else if (IsClipboardFormatAvailable(CF_UNICODETEXT))
2101 cf = CF_UNICODETEXT, dwFormat = SF_TEXT|SF_UNICODE;
2102 else
2103 return FALSE;
2104
2105 if (!OpenClipboard(editor->hWnd))
2106 return FALSE;
2107 gds.hData = GetClipboardData(cf);
2108 gds.nLength = 0;
2109 es.dwCookie = (DWORD_PTR)&gds;
2110 es.pfnCallback = dwFormat == SF_RTF ? ME_ReadFromHGLOBALRTF : ME_ReadFromHGLOBALUnicode;
2111 ME_StreamIn(editor, dwFormat|SFF_SELECTION, &es, FALSE);
2112
2113 CloseClipboard();
2114 return TRUE;
2115 }
2116
2117 static BOOL ME_Copy(ME_TextEditor *editor, const ME_Cursor *start, int nChars)
2118 {
2119 LPDATAOBJECT dataObj = NULL;
2120 HRESULT hr = S_OK;
2121
2122 if (editor->cPasswordMask)
2123 return FALSE; /* Copying or Cutting masked text isn't allowed */
2124
2125 if(editor->lpOleCallback)
2126 {
2127 CHARRANGE range;
2128 range.cpMin = ME_GetCursorOfs(start);
2129 range.cpMax = range.cpMin + nChars;
2130 hr = IRichEditOleCallback_GetClipboardData(editor->lpOleCallback, &range, RECO_COPY, &dataObj);
2131 }
2132 if(FAILED(hr) || !dataObj)
2133 hr = ME_GetDataObject(editor, start, nChars, &dataObj);
2134 if(SUCCEEDED(hr)) {
2135 hr = OleSetClipboard(dataObj);
2136 IDataObject_Release(dataObj);
2137 }
2138 return SUCCEEDED(hr);
2139 }
2140
2141 /* helper to send a msg filter notification */
2142 static BOOL
2143 ME_FilterEvent(ME_TextEditor *editor, UINT msg, WPARAM* wParam, LPARAM* lParam)
2144 {
2145 MSGFILTER msgf;
2146
2147 if (!editor->hWnd || !editor->hwndParent) return FALSE;
2148 msgf.nmhdr.hwndFrom = editor->hWnd;
2149 msgf.nmhdr.idFrom = GetWindowLongW(editor->hWnd, GWLP_ID);
2150 msgf.nmhdr.code = EN_MSGFILTER;
2151 msgf.msg = msg;
2152 msgf.wParam = *wParam;
2153 msgf.lParam = *lParam;
2154 if (SendMessageW(editor->hwndParent, WM_NOTIFY, msgf.nmhdr.idFrom, (LPARAM)&msgf))
2155 return FALSE;
2156 *wParam = msgf.wParam;
2157 *lParam = msgf.lParam;
2158 msgf.wParam = *wParam;
2159
2160 return TRUE;
2161 }
2162
2163 static void ME_UpdateSelectionLinkAttribute(ME_TextEditor *editor)
2164 {
2165 ME_DisplayItem *startPara, *endPara;
2166 ME_DisplayItem *prev_para;
2167 ME_Cursor *from, *to;
2168 ME_Cursor start;
2169 int nChars;
2170
2171 if (!editor->AutoURLDetect_bEnable) return;
2172
2173 ME_GetSelection(editor, &from, &to);
2174
2175 /* Find paragraph previous to the one that contains start cursor */
2176 startPara = from->pPara;
2177 prev_para = startPara->member.para.prev_para;
2178 if (prev_para->type == diParagraph) startPara = prev_para;
2179
2180 /* Find paragraph that contains end cursor */
2181 endPara = to->pPara->member.para.next_para;
2182
2183 start.pPara = startPara;
2184 start.pRun = ME_FindItemFwd(startPara, diRun);
2185 start.nOffset = 0;
2186 nChars = endPara->member.para.nCharOfs - startPara->member.para.nCharOfs;
2187
2188 ME_UpdateLinkAttribute(editor, &start, nChars);
2189 }
2190
2191 static BOOL
2192 ME_KeyDown(ME_TextEditor *editor, WORD nKey)
2193 {
2194 BOOL ctrl_is_down = GetKeyState(VK_CONTROL) & 0x8000;
2195 BOOL shift_is_down = GetKeyState(VK_SHIFT) & 0x8000;
2196
2197 if (editor->bMouseCaptured)
2198 return FALSE;
2199 if (nKey != VK_SHIFT && nKey != VK_CONTROL && nKey != VK_MENU)
2200 editor->nSelectionType = stPosition;
2201
2202 switch (nKey)
2203 {
2204 case VK_LEFT:
2205 case VK_RIGHT:
2206 case VK_HOME:
2207 case VK_END:
2208 editor->nUDArrowX = -1;
2209 /* fall through */
2210 case VK_UP:
2211 case VK_DOWN:
2212 case VK_PRIOR:
2213 case VK_NEXT:
2214 ME_CommitUndo(editor); /* End coalesced undos for typed characters */
2215 ME_ArrowKey(editor, nKey, shift_is_down, ctrl_is_down);
2216 return TRUE;
2217 case VK_BACK:
2218 case VK_DELETE:
2219 editor->nUDArrowX = -1;
2220 /* FIXME backspace and delete aren't the same, they act different wrt paragraph style of the merged paragraph */
2221 if (editor->styleFlags & ES_READONLY)
2222 return FALSE;
2223 if (ME_IsSelection(editor))
2224 {
2225 ME_DeleteSelection(editor);
2226 ME_CommitUndo(editor);
2227 }
2228 else if (nKey == VK_DELETE)
2229 {
2230 /* Delete stops group typing.
2231 * (See MSDN remarks on EM_STOPGROUPTYPING message) */
2232 ME_DeleteTextAtCursor(editor, 1, 1);
2233 ME_CommitUndo(editor);
2234 }
2235 else if (ME_ArrowKey(editor, VK_LEFT, FALSE, FALSE))
2236 {
2237 BOOL bDeletionSucceeded;
2238 /* Backspace can be grouped for a single undo */
2239 ME_ContinueCoalescingTransaction(editor);
2240 bDeletionSucceeded = ME_DeleteTextAtCursor(editor, 1, 1);
2241 if (!bDeletionSucceeded && !editor->bEmulateVersion10) { /* v4.1 */
2242 /* Deletion was prevented so the cursor is moved back to where it was.
2243 * (e.g. this happens when trying to delete cell boundaries)
2244 */
2245 ME_ArrowKey(editor, VK_RIGHT, FALSE, FALSE);
2246 }
2247 ME_CommitCoalescingUndo(editor);
2248 }
2249 else
2250 return TRUE;
2251 ME_MoveCursorFromTableRowStartParagraph(editor);
2252 ME_UpdateSelectionLinkAttribute(editor);
2253 ME_UpdateRepaint(editor, FALSE);
2254 ME_SendRequestResize(editor, FALSE);
2255 return TRUE;
2256 case VK_RETURN:
2257 if (editor->bDialogMode)
2258 {
2259 if (ctrl_is_down)
2260 return TRUE;
2261
2262 if (!(editor->styleFlags & ES_WANTRETURN))
2263 {
2264 if (editor->hwndParent)
2265 {
2266 DWORD dw;
2267 dw = SendMessageW(editor->hwndParent, DM_GETDEFID, 0, 0);
2268 if (HIWORD(dw) == DC_HASDEFID)
2269 {
2270 HWND hwDefCtrl = GetDlgItem(editor->hwndParent, LOWORD(dw));
2271 if (hwDefCtrl)
2272 {
2273 SendMessageW(editor->hwndParent, WM_NEXTDLGCTL, (WPARAM)hwDefCtrl, TRUE);
2274 PostMessageW(hwDefCtrl, WM_KEYDOWN, VK_RETURN, 0);
2275 }
2276 }
2277 }
2278 return TRUE;
2279 }
2280 }
2281
2282 if (editor->styleFlags & ES_MULTILINE)
2283 {
2284 ME_Cursor cursor = editor->pCursors[0];
2285 ME_DisplayItem *para = cursor.pPara;
2286 int from, to;
2287 const WCHAR endl = '\r';
2288 const WCHAR endlv10[] = {'\r','\n'};
2289 ME_Style *style;
2290
2291 if (editor->styleFlags & ES_READONLY) {
2292 MessageBeep(MB_ICONERROR);
2293 return TRUE;
2294 }
2295
2296 ME_GetSelectionOfs(editor, &from, &to);
2297 if (editor->nTextLimit > ME_GetTextLength(editor) - (to-from))
2298 {
2299 if (!editor->bEmulateVersion10) { /* v4.1 */
2300 if (para->member.para.nFlags & MEPF_ROWEND) {
2301 /* Add a new table row after this row. */
2302 para = ME_AppendTableRow(editor, para);
2303 para = para->member.para.next_para;
2304 editor->pCursors[0].pPara = para;
2305 editor->pCursors[0].pRun = ME_FindItemFwd(para, diRun);
2306 editor->pCursors[0].nOffset = 0;
2307 editor->pCursors[1] = editor->pCursors[0];
2308 ME_CommitUndo(editor);
2309 ME_CheckTablesForCorruption(editor);
2310 ME_UpdateRepaint(editor, FALSE);
2311 return TRUE;
2312 }
2313 else if (para == editor->pCursors[1].pPara &&
2314 cursor.nOffset + cursor.pRun->member.run.nCharOfs == 0 &&
2315 para->member.para.prev_para->member.para.nFlags & MEPF_ROWSTART &&
2316 !para->member.para.prev_para->member.para.nCharOfs)
2317 {
2318 /* Insert a newline before the table. */
2319 para = para->member.para.prev_para;
2320 para->member.para.nFlags &= ~MEPF_ROWSTART;
2321 editor->pCursors[0].pPara = para;
2322 editor->pCursors[0].pRun = ME_FindItemFwd(para, diRun);
2323 editor->pCursors[1] = editor->pCursors[0];
2324 ME_InsertTextFromCursor(editor, 0, &endl, 1,
2325 editor->pCursors[0].pRun->member.run.style);
2326 para = editor->pBuffer->pFirst->member.para.next_para;
2327 ME_SetDefaultParaFormat(editor, para->member.para.pFmt);
2328 para->member.para.nFlags = MEPF_REWRAP;
2329 editor->pCursors[0].pPara = para;
2330 editor->pCursors[0].pRun = ME_FindItemFwd(para, diRun);
2331 editor->pCursors[1] = editor->pCursors[0];
2332 para->member.para.next_para->member.para.nFlags |= MEPF_ROWSTART;
2333 ME_CommitCoalescingUndo(editor);
2334 ME_CheckTablesForCorruption(editor);
2335 ME_UpdateRepaint(editor, FALSE);
2336 return TRUE;
2337 }
2338 } else { /* v1.0 - 3.0 */
2339 ME_DisplayItem *para = cursor.pPara;
2340 if (ME_IsInTable(para))
2341 {
2342 if (cursor.pRun->member.run.nFlags & MERF_ENDPARA)
2343 {
2344 if (from == to) {
2345 ME_ContinueCoalescingTransaction(editor);
2346 para = ME_AppendTableRow(editor, para);
2347 editor->pCursors[0].pPara = para;
2348 editor->pCursors[0].pRun = ME_FindItemFwd(para, diRun);
2349 editor->pCursors[0].nOffset = 0;
2350 editor->pCursors[1] = editor->pCursors[0];
2351 ME_CommitCoalescingUndo(editor);
2352 ME_UpdateRepaint(editor, FALSE);
2353 return TRUE;
2354 }
2355 } else {
2356 ME_ContinueCoalescingTransaction(editor);
2357 if (cursor.pRun->member.run.nCharOfs + cursor.nOffset == 0 &&
2358 !ME_IsInTable(para->member.para.prev_para))
2359 {
2360 /* Insert newline before table */
2361 cursor.pRun = ME_FindItemBack(para, diRun);
2362 if (cursor.pRun) {
2363 editor->pCursors[0].pRun = cursor.pRun;
2364 editor->pCursors[0].pPara = para->member.para.prev_para;
2365 }
2366 editor->pCursors[0].nOffset = 0;
2367 editor->pCursors[1] = editor->pCursors[0];
2368 ME_InsertTextFromCursor(editor, 0, &endl, 1,
2369 editor->pCursors[0].pRun->member.run.style);
2370 } else {
2371 editor->pCursors[1] = editor->pCursors[0];
2372 para = ME_AppendTableRow(editor, para);
2373 editor->pCursors[0].pPara = para;
2374 editor->pCursors[0].pRun = ME_FindItemFwd(para, diRun);
2375 editor->pCursors[0].nOffset = 0;
2376 editor->pCursors[1] = editor->pCursors[0];
2377 }
2378 ME_CommitCoalescingUndo(editor);
2379 ME_UpdateRepaint(editor, FALSE);
2380 return TRUE;
2381 }
2382 }
2383 }
2384
2385 style = ME_GetInsertStyle(editor, 0);
2386 ME_SaveTempStyle(editor);
2387 ME_ContinueCoalescingTransaction(editor);
2388 if (shift_is_down)
2389 ME_InsertEndRowFromCursor(editor, 0);
2390 else
2391 if (!editor->bEmulateVersion10)
2392 ME_InsertTextFromCursor(editor, 0, &endl, 1, style);
2393 else
2394 ME_InsertTextFromCursor(editor, 0, endlv10, 2, style);
2395 ME_ReleaseStyle(style);
2396 ME_CommitCoalescingUndo(editor);
2397 SetCursor(NULL);
2398
2399 ME_UpdateSelectionLinkAttribute(editor);
2400 ME_UpdateRepaint(editor, FALSE);
2401 }
2402 return TRUE;
2403 }
2404 break;
2405 case VK_ESCAPE:
2406 if (editor->bDialogMode && editor->hwndParent)
2407 PostMessageW(editor->hwndParent, WM_CLOSE, 0, 0);
2408 return TRUE;
2409 case VK_TAB:
2410 if (editor->bDialogMode && editor->hwndParent)
2411 SendMessageW(editor->hwndParent, WM_NEXTDLGCTL, shift_is_down, 0);
2412 return TRUE;
2413 case 'A':
2414 if (ctrl_is_down)
2415 {
2416 handle_EM_EXSETSEL( editor, 0, -1 );
2417 return TRUE;
2418 }
2419 break;
2420 case 'V':
2421 if (ctrl_is_down)
2422 return ME_Paste(editor);
2423 break;
2424 case 'C':
2425 case 'X':
2426 if (ctrl_is_down)
2427 {
2428 BOOL result;
2429 int nOfs, nChars;
2430 int nStartCur = ME_GetSelectionOfs(editor, &nOfs, &nChars);
2431 ME_Cursor *selStart = &editor->pCursors[nStartCur];
2432
2433 nChars -= nOfs;
2434 result = ME_Copy(editor, selStart, nChars);
2435 if (result && nKey == 'X')
2436 {
2437 ME_InternalDeleteText(editor, selStart, nChars, FALSE);
2438 ME_CommitUndo(editor);
2439 ME_UpdateRepaint(editor, TRUE);
2440 }
2441 return result;
2442 }
2443 break;
2444 case 'Z':
2445 if (ctrl_is_down)
2446 {
2447 ME_Undo(editor);
2448 return TRUE;
2449 }
2450 break;
2451 case 'Y':
2452 if (ctrl_is_down)
2453 {
2454 ME_Redo(editor);
2455 return TRUE;
2456 }
2457 break;
2458
2459 default:
2460 if (nKey != VK_SHIFT && nKey != VK_CONTROL && nKey && nKey != VK_MENU)
2461 editor->nUDArrowX = -1;
2462 if (ctrl_is_down)
2463 {
2464 if (nKey == 'W')
2465 {
2466 CHARFORMAT2W chf;
2467 char buf[2048];
2468 chf.cbSize = sizeof(chf);
2469
2470 ME_GetSelectionCharFormat(editor, &chf);
2471 ME_DumpStyleToBuf(&chf, buf);
2472 MessageBoxA(NULL, buf, "Style dump", MB_OK);
2473 }
2474 if (nKey == 'Q')
2475 {
2476 ME_CheckCharOffsets(editor);
2477 }
2478 }
2479 }
2480 return FALSE;
2481 }
2482
2483 static LRESULT ME_Char(ME_TextEditor *editor, WPARAM charCode,
2484 LPARAM flags, BOOL unicode)
2485 {
2486 WCHAR wstr;
2487
2488 if (editor->bMouseCaptured)
2489 return 0;
2490
2491 if (unicode)
2492 wstr = (WCHAR)charCode;
2493 else
2494 {
2495 CHAR charA = charCode;
2496 MultiByteToWideChar(CP_ACP, 0, &charA, 1, &wstr, 1);
2497 }
2498
2499 if (editor->styleFlags & ES_READONLY) {
2500 MessageBeep(MB_ICONERROR);
2501 return 0; /* FIXME really 0 ? */
2502 }
2503
2504 if ((unsigned)wstr >= ' ' || wstr == '\t')
2505 {
2506 ME_Cursor cursor = editor->pCursors[0];
2507 ME_DisplayItem *para = cursor.pPara;
2508 int from, to;
2509 BOOL ctrl_is_down = GetKeyState(VK_CONTROL) & 0x8000;
2510 ME_GetSelectionOfs(editor, &from, &to);
2511 if (wstr == '\t' &&
2512 /* v4.1 allows tabs to be inserted with ctrl key down */
2513 !(ctrl_is_down && !editor->bEmulateVersion10))
2514 {
2515 ME_DisplayItem *para;
2516 BOOL bSelectedRow = FALSE;
2517
2518 para = cursor.pPara;
2519 if (ME_IsSelection(editor) &&
2520 cursor.pRun->member.run.nCharOfs + cursor.nOffset == 0 &&
2521 to == ME_GetCursorOfs(&editor->pCursors[0]) &&
2522 para->member.para.prev_para->type == diParagraph)
2523 {
2524 para = para->member.para.prev_para;
2525 bSelectedRow = TRUE;
2526 }
2527 if (ME_IsInTable(para))
2528 {
2529 ME_TabPressedInTable(editor, bSelectedRow);
2530 ME_CommitUndo(editor);
2531 return 0;
2532 }
2533 } else if (!editor->bEmulateVersion10) { /* v4.1 */
2534 if (para->member.para.nFlags & MEPF_ROWEND) {
2535 if (from == to) {
2536 para = para->member.para.next_para;
2537 if (para->member.para.nFlags & MEPF_ROWSTART)
2538 para = para->member.para.next_para;
2539 editor->pCursors[0].pPara = para;
2540 editor->pCursors[0].pRun = ME_FindItemFwd(para, diRun);
2541 editor->pCursors[0].nOffset = 0;
2542 editor->pCursors[1] = editor->pCursors[0];
2543 }
2544 }
2545 } else { /* v1.0 - 3.0 */
2546 if (ME_IsInTable(cursor.pRun) &&
2547 cursor.pRun->member.run.nFlags & MERF_ENDPARA &&
2548 from == to)
2549 {
2550 /* Text should not be inserted at the end of the table. */
2551 MessageBeep(-1);
2552 return 0;
2553 }
2554 }
2555 /* FIXME maybe it would make sense to call EM_REPLACESEL instead ? */
2556 /* WM_CHAR is restricted to nTextLimit */
2557 if(editor->nTextLimit > ME_GetTextLength(editor) - (to-from))
2558 {
2559 ME_Style *style = ME_GetInsertStyle(editor, 0);
2560 ME_SaveTempStyle(editor);
2561 ME_ContinueCoalescingTransaction(editor);
2562 ME_InsertTextFromCursor(editor, 0, &wstr, 1, style);
2563 ME_ReleaseStyle(style);
2564 ME_CommitCoalescingUndo(editor);
2565 ITextHost_TxSetCursor(editor->texthost, NULL, FALSE);
2566 }
2567
2568 ME_UpdateSelectionLinkAttribute(editor);
2569 ME_UpdateRepaint(editor, FALSE);
2570 }
2571 return 0;
2572 }
2573
2574 /* Process the message and calculate the new click count.
2575 *
2576 * returns: The click count if it is mouse down event, else returns 0. */
2577 static int ME_CalculateClickCount(ME_TextEditor *editor, UINT msg, WPARAM wParam,
2578 LPARAM lParam)
2579 {
2580 static int clickNum = 0;
2581 if (msg < WM_MOUSEFIRST || msg > WM_MOUSELAST)
2582 return 0;
2583
2584 if ((msg == WM_LBUTTONDBLCLK) ||
2585 (msg == WM_RBUTTONDBLCLK) ||
2586 (msg == WM_MBUTTONDBLCLK) ||
2587 (msg == WM_XBUTTONDBLCLK))
2588 {
2589 msg -= (WM_LBUTTONDBLCLK - WM_LBUTTONDOWN);
2590 }
2591
2592 if ((msg == WM_LBUTTONDOWN) ||
2593 (msg == WM_RBUTTONDOWN) ||
2594 (msg == WM_MBUTTONDOWN) ||
2595 (msg == WM_XBUTTONDOWN))
2596 {
2597 static MSG prevClickMsg;
2598 MSG clickMsg;
2599 /* Compare the editor instead of the hwnd so that the this
2600 * can still be done for windowless richedit controls. */
2601 clickMsg.hwnd = (HWND)editor;
2602 clickMsg.message = msg;
2603 clickMsg.wParam = wParam;
2604 clickMsg.lParam = lParam;
2605 clickMsg.time = GetMessageTime();
2606 clickMsg.pt.x = (short)LOWORD(lParam);
2607 clickMsg.pt.y = (short)HIWORD(lParam);
2608 if ((clickNum != 0) &&
2609 (clickMsg.message == prevClickMsg.message) &&
2610 (clickMsg.hwnd == prevClickMsg.hwnd) &&
2611 (clickMsg.wParam == prevClickMsg.wParam) &&
2612 (clickMsg.time - prevClickMsg.time < GetDoubleClickTime()) &&
2613 (abs(clickMsg.pt.x - prevClickMsg.pt.x) < GetSystemMetrics(SM_CXDOUBLECLK)/2) &&
2614 (abs(clickMsg.pt.y - prevClickMsg.pt.y) < GetSystemMetrics(SM_CYDOUBLECLK)/2))
2615 {
2616 clickNum++;
2617 } else {
2618 clickNum = 1;
2619 }
2620 prevClickMsg = clickMsg;
2621 } else {
2622 return 0;
2623 }
2624 return clickNum;
2625 }
2626
2627 static BOOL ME_SetCursor(ME_TextEditor *editor)
2628 {
2629 ME_Cursor cursor;
2630 POINT pt;
2631 BOOL isExact;
2632 SCROLLBARINFO sbi;
2633 DWORD messagePos = GetMessagePos();
2634 pt.x = (short)LOWORD(messagePos);
2635 pt.y = (short)HIWORD(messagePos);
2636
2637 if (editor->hWnd)
2638 {
2639 sbi.cbSize = sizeof(sbi);
2640 GetScrollBarInfo(editor->hWnd, OBJID_HSCROLL, &sbi);
2641 if (!(sbi.rgstate[0] & (STATE_SYSTEM_INVISIBLE|STATE_SYSTEM_OFFSCREEN)) &&
2642 PtInRect(&sbi.rcScrollBar, pt))
2643 {
2644 ITextHost_TxSetCursor(editor->texthost,
2645 LoadCursorW(NULL, (WCHAR*)IDC_ARROW), FALSE);
2646 return TRUE;
2647 }
2648 sbi.cbSize = sizeof(sbi);
2649 GetScrollBarInfo(editor->hWnd, OBJID_VSCROLL, &sbi);
2650 if (!(sbi.rgstate[0] & (STATE_SYSTEM_INVISIBLE|STATE_SYSTEM_OFFSCREEN)) &&
2651 PtInRect(&sbi.rcScrollBar, pt))
2652 {
2653 ITextHost_TxSetCursor(editor->texthost,
2654 LoadCursorW(NULL, (WCHAR*)IDC_ARROW), FALSE);
2655 return TRUE;
2656 }
2657 }
2658 ITextHost_TxScreenToClient(editor->texthost, &pt);
2659
2660 if (editor->nSelectionType == stLine && editor->bMouseCaptured) {
2661 ITextHost_TxSetCursor(editor->texthost, hLeft, FALSE);
2662 return TRUE;
2663 }
2664 if (!editor->bEmulateVersion10 /* v4.1 */ &&
2665 pt.y < editor->rcFormat.top &&
2666 pt.x < editor->rcFormat.left)
2667 {
2668 ITextHost_TxSetCursor(editor->texthost, hLeft, FALSE);
2669 return TRUE;
2670 }
2671 if (pt.y < editor->rcFormat.top || pt.y > editor->rcFormat.bottom)
2672 {
2673 if (editor->bEmulateVersion10) /* v1.0 - 3.0 */
2674 ITextHost_TxSetCursor(editor->texthost,
2675 LoadCursorW(NULL, (WCHAR*)IDC_ARROW), FALSE);
2676 else /* v4.1 */
2677 ITextHost_TxSetCursor(editor->texthost,
2678 LoadCursorW(NULL, (WCHAR*)IDC_IBEAM), TRUE);
2679 return TRUE;
2680 }
2681 if (pt.x < editor->rcFormat.left)
2682 {
2683 ITextHost_TxSetCursor(editor->texthost, hLeft, FALSE);
2684 return TRUE;
2685 }
2686 ME_CharFromPos(editor, pt.x, pt.y, &cursor, &isExact);
2687 if (isExact)
2688 {
2689 ME_Run *run;
2690
2691 run = &cursor.pRun->member.run;
2692 if (run->style->fmt.dwMask & CFM_LINK &&
2693 run->style->fmt.dwEffects & CFE_LINK)
2694 {
2695 ITextHost_TxSetCursor(editor->texthost,
2696 LoadCursorW(NULL, (WCHAR*)IDC_HAND),
2697 FALSE);
2698 return TRUE;
2699 }
2700
2701 if (ME_IsSelection(editor))
2702 {
2703 int selStart, selEnd;
2704 int offset = ME_GetCursorOfs(&cursor);
2705
2706 ME_GetSelectionOfs(editor, &selStart, &selEnd);
2707 if (selStart <= offset && selEnd >= offset) {
2708 ITextHost_TxSetCursor(editor->texthost,
2709 LoadCursorW(NULL, (WCHAR*)IDC_ARROW),
2710 FALSE);
2711 return TRUE;
2712 }
2713 }
2714 }
2715 ITextHost_TxSetCursor(editor->texthost,
2716 LoadCursorW(NULL, (WCHAR*)IDC_IBEAM), TRUE);
2717 return TRUE;
2718 }
2719
2720 static void ME_SetDefaultFormatRect(ME_TextEditor *editor)
2721 {
2722 ITextHost_TxGetClientRect(editor->texthost, &editor->rcFormat);
2723 editor->rcFormat.top += editor->exStyleFlags & WS_EX_CLIENTEDGE ? 1 : 0;
2724 editor->rcFormat.left += 1 + editor->selofs;
2725 editor->rcFormat.right -= 1;
2726 }
2727
2728 static BOOL ME_ShowContextMenu(ME_TextEditor *editor, int x, int y)
2729 {
2730 CHARRANGE selrange;
2731 HMENU menu;
2732 int seltype = 0;
2733 if(!editor->lpOleCallback || !editor->hWnd)
2734 return FALSE;
2735 ME_GetSelectionOfs(editor, &selrange.cpMin, &selrange.cpMax);
2736 if(selrange.cpMin == selrange.cpMax)
2737 seltype |= SEL_EMPTY;
2738 else
2739 {
2740 /* FIXME: Handle objects */
2741 seltype |= SEL_TEXT;
2742 if(selrange.cpMax-selrange.cpMin > 1)
2743 seltype |= SEL_MULTICHAR;
2744 }
2745 if(SUCCEEDED(IRichEditOleCallback_GetContextMenu(editor->lpOleCallback, seltype, NULL, &selrange, &menu)))
2746 {
2747 TrackPopupMenu(menu, TPM_LEFTALIGN | TPM_RIGHTBUTTON, x, y, 0, editor->hwndParent, NULL);
2748 DestroyMenu(menu);
2749 }
2750 return TRUE;
2751 }
2752
2753 ME_TextEditor *ME_MakeEditor(ITextHost *texthost, BOOL bEmulateVersion10, DWORD csStyle)
2754 {
2755 ME_TextEditor *ed = ALLOC_OBJ(ME_TextEditor);
2756 int i;
2757 DWORD props;
2758 LONG selbarwidth;
2759
2760 ed->hWnd = NULL;
2761 ed->hwndParent = NULL;
2762 ed->sizeWindow.cx = ed->sizeWindow.cy = 0;
2763 ed->texthost = texthost;
2764 ed->reOle = NULL;
2765 ed->bEmulateVersion10 = bEmulateVersion10;
2766 ed->styleFlags = 0;
2767 ed->alignStyle = PFA_LEFT;
2768 if (csStyle & ES_RIGHT)
2769 ed->alignStyle = PFA_RIGHT;
2770 if (csStyle & ES_CENTER)
2771 ed->alignStyle = PFA_CENTER;
2772 ITextHost_TxGetPropertyBits(texthost,
2773 (TXTBIT_RICHTEXT|TXTBIT_MULTILINE|
2774 TXTBIT_READONLY|TXTBIT_USEPASSWORD|
2775 TXTBIT_HIDESELECTION|TXTBIT_SAVESELECTION|
2776 TXTBIT_AUTOWORDSEL|TXTBIT_VERTICAL|
2777 TXTBIT_WORDWRAP|TXTBIT_DISABLEDRAG),
2778 &props);
2779 ITextHost_TxGetScrollBars(texthost, &ed->styleFlags);
2780 ed->styleFlags &= (WS_VSCROLL|WS_HSCROLL|ES_AUTOVSCROLL|
2781 ES_AUTOHSCROLL|ES_DISABLENOSCROLL);
2782 ed->pBuffer = ME_MakeText();
2783 ed->nZoomNumerator = ed->nZoomDenominator = 0;
2784 ed->nAvailWidth = 0; /* wrap to client area */
2785 ME_MakeFirstParagraph(ed);
2786 /* The four cursors are for:
2787 * 0 - The position where the caret is shown
2788 * 1 - The anchored end of the selection (for normal selection)
2789 * 2 & 3 - The anchored start and end respectively for word, line,
2790 * or paragraph selection.
2791 */
2792 ed->nCursors = 4;
2793 ed->pCursors = ALLOC_N_OBJ(ME_Cursor, ed->nCursors);
2794 ME_SetCursorToStart(ed, &ed->pCursors[0]);
2795 ed->pCursors[1] = ed->pCursors[0];
2796 ed->pCursors[2] = ed->pCursors[0];
2797 ed->pCursors[3] = ed->pCursors[1];
2798 ed->nLastTotalLength = ed->nTotalLength = 0;
2799 ed->nLastTotalWidth = ed->nTotalWidth = 0;
2800 ed->nUDArrowX = -1;
2801 ed->rgbBackColor = -1;
2802 ed->hbrBackground = GetSysColorBrush(COLOR_WINDOW);
2803 ed->bCaretAtEnd = FALSE;
2804 ed->nEventMask = 0;
2805 ed->nModifyStep = 0;
2806 ed->nTextLimit = TEXT_LIMIT_DEFAULT;
2807 list_init( &ed->undo_stack );
2808 list_init( &ed->redo_stack );
2809 ed->nUndoStackSize = 0;
2810 ed->nUndoLimit = STACK_SIZE_DEFAULT;
2811 ed->nUndoMode = umAddToUndo;
2812 ed->nParagraphs = 1;
2813 ed->nLastSelStart = ed->nLastSelEnd = 0;
2814 ed->pLastSelStartPara = ed->pLastSelEndPara = ed->pCursors[0].pPara;
2815 ed->bHideSelection = FALSE;
2816 ed->pfnWordBreak = NULL;
2817 ed->lpOleCallback = NULL;
2818 ed->mode = TM_MULTILEVELUNDO | TM_MULTICODEPAGE;
2819 ed->mode |= (props & TXTBIT_RICHTEXT) ? TM_RICHTEXT : TM_PLAINTEXT;
2820 ed->AutoURLDetect_bEnable = FALSE;
2821 ed->bHaveFocus = FALSE;
2822 ed->bDialogMode = FALSE;
2823 ed->bMouseCaptured = FALSE;
2824 for (i=0; i<HFONT_CACHE_SIZE; i++)
2825 {
2826 ed->pFontCache[i].nRefs = 0;
2827 ed->pFontCache[i].nAge = 0;
2828 ed->pFontCache[i].hFont = NULL;
2829 }
2830
2831 ME_CheckCharOffsets(ed);
2832 ed->bDefaultFormatRect = TRUE;
2833 ITextHost_TxGetSelectionBarWidth(ed->texthost, &selbarwidth);
2834 if (selbarwidth) {
2835 /* FIXME: Convert selbarwidth from HIMETRIC to pixels */
2836 ed->selofs = SELECTIONBAR_WIDTH;
2837 ed->styleFlags |= ES_SELECTIONBAR;
2838 } else {
2839 ed->selofs = 0;
2840 }
2841 ed->nSelectionType = stPosition;
2842
2843 ed->cPasswordMask = 0;
2844 if (props & TXTBIT_USEPASSWORD)
2845 ITextHost_TxGetPasswordChar(texthost, &ed->cPasswordMask);
2846
2847 if (props & TXTBIT_AUTOWORDSEL)
2848 ed->styleFlags |= ECO_AUTOWORDSELECTION;
2849 if (props & TXTBIT_MULTILINE) {
2850 ed->styleFlags |= ES_MULTILINE;
2851 ed->bWordWrap = (props & TXTBIT_WORDWRAP) != 0;
2852 } else {
2853 ed->bWordWrap = FALSE;
2854 }
2855 if (props & TXTBIT_READONLY)
2856 ed->styleFlags |= ES_READONLY;
2857 if (!(props & TXTBIT_HIDESELECTION))
2858 ed->styleFlags |= ES_NOHIDESEL;
2859 if (props & TXTBIT_SAVESELECTION)
2860 ed->styleFlags |= ES_SAVESEL;
2861 if (props & TXTBIT_VERTICAL)
2862 ed->styleFlags |= ES_VERTICAL;
2863 if (props & TXTBIT_DISABLEDRAG)
2864 ed->styleFlags |= ES_NOOLEDRAGDROP;
2865
2866 ed->notified_cr.cpMin = ed->notified_cr.cpMax = 0;
2867
2868 /* Default scrollbar information */
2869 ed->vert_si.cbSize = sizeof(SCROLLINFO);
2870 ed->vert_si.nMin = 0;
2871 ed->vert_si.nMax = 0;
2872 ed->vert_si.nPage = 0;
2873 ed->vert_si.nPos = 0;
2874
2875 ed->horz_si.cbSize = sizeof(SCROLLINFO);
2876 ed->horz_si.nMin = 0;
2877 ed->horz_si.nMax = 0;
2878 ed->horz_si.nPage = 0;
2879 ed->horz_si.nPos = 0;
2880
2881 ed->wheel_remain = 0;
2882
2883 list_init( &ed->style_list );
2884 OleInitialize(NULL);
2885
2886 return ed;
2887 }
2888
2889 void ME_DestroyEditor(ME_TextEditor *editor)
2890 {
2891 ME_DisplayItem *pFirst = editor->pBuffer->pFirst;
2892 ME_DisplayItem *p = pFirst, *pNext = NULL;
2893 ME_Style *s, *cursor2;
2894 int i;
2895
2896 ME_ClearTempStyle(editor);
2897 ME_EmptyUndoStack(editor);
2898 while(p) {
2899 pNext = p->next;
2900 ME_DestroyDisplayItem(p);
2901 p = pNext;
2902 }
2903
2904 LIST_FOR_EACH_ENTRY_SAFE( s, cursor2, &editor->style_list, ME_Style, entry )
2905 ME_DestroyStyle( s );
2906
2907 ME_ReleaseStyle(editor->pBuffer->pDefaultStyle);
2908 for (i=0; i<HFONT_CACHE_SIZE; i++)
2909 {
2910 if (editor->pFontCache[i].hFont)
2911 DeleteObject(editor->pFontCache[i].hFont);
2912 }
2913 if (editor->rgbBackColor != -1)
2914 DeleteObject(editor->hbrBackground);
2915 if(editor->lpOleCallback)
2916 IRichEditOleCallback_Release(editor->lpOleCallback);
2917 ITextHost_Release(editor->texthost);
2918 if (editor->reOle)
2919 {
2920 IRichEditOle_Release(editor->reOle);
2921 editor->reOle = NULL;
2922 }
2923 OleUninitialize();
2924
2925 FREE_OBJ(editor->pBuffer);
2926 FREE_OBJ(editor->pCursors);
2927
2928 FREE_OBJ(editor);
2929 }
2930
2931 BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
2932 {
2933 TRACE("\n");
2934 switch (fdwReason)
2935 {
2936 case DLL_PROCESS_ATTACH:
2937 DisableThreadLibraryCalls(hinstDLL);
2938 me_heap = HeapCreate (0, 0x10000, 0);
2939 if (!ME_RegisterEditorClass(hinstDLL)) return FALSE;
2940 hLeft = LoadCursorW(hinstDLL, MAKEINTRESOURCEW(OCR_REVERSE));
2941 LookupInit();
2942 break;
2943
2944 case DLL_PROCESS_DETACH:
2945 if (lpvReserved) break;
2946 UnregisterClassW(RICHEDIT_CLASS20W, 0);
2947 UnregisterClassW(MSFTEDIT_CLASS, 0);
2948 UnregisterClassA(RICHEDIT_CLASS20A, 0);
2949 UnregisterClassA("RichEdit50A", 0);
2950 if (ME_ListBoxRegistered)
2951 UnregisterClassW(REListBox20W, 0);
2952 if (ME_ComboBoxRegistered)
2953 UnregisterClassW(REComboBox20W, 0);
2954 LookupCleanup();
2955 HeapDestroy (me_heap);
2956 release_typelib();
2957 break;
2958 }
2959 return TRUE;
2960 }
2961
2962 static inline int get_default_line_height( ME_TextEditor *editor )
2963 {
2964 int height = 0;
2965
2966 if (editor->pBuffer && editor->pBuffer->pDefaultStyle)
2967 height = editor->pBuffer->pDefaultStyle->tm.tmHeight;
2968 if (height <= 0) height = 24;
2969
2970 return height;
2971 }
2972
2973 static inline int calc_wheel_change( int *remain, int amount_per_click )
2974 {
2975 int change = amount_per_click * (float)*remain / WHEEL_DELTA;
2976 *remain -= WHEEL_DELTA * change / amount_per_click;
2977 return change;
2978 }
2979
2980 static const char * const edit_messages[] = {
2981 "EM_GETSEL",
2982 "EM_SETSEL",
2983 "EM_GETRECT",
2984 "EM_SETRECT",
2985 "EM_SETRECTNP",
2986 "EM_SCROLL",
2987 "EM_LINESCROLL",
2988 "EM_SCROLLCARET",
2989 "EM_GETMODIFY",
2990 "EM_SETMODIFY",
2991 "EM_GETLINECOUNT",
2992 "EM_LINEINDEX",
2993 "EM_SETHANDLE",
2994 "EM_GETHANDLE",
2995 "EM_GETTHUMB",
2996 "EM_UNKNOWN_BF",
2997 "EM_UNKNOWN_C0",
2998 "EM_LINELENGTH",
2999 "EM_REPLACESEL",
3000 "EM_UNKNOWN_C3",
3001 "EM_GETLINE",
3002 "EM_LIMITTEXT",
3003 "EM_CANUNDO",
3004 "EM_UNDO",
3005 "EM_FMTLINES",
3006 "EM_LINEFROMCHAR",
3007 "EM_UNKNOWN_CA",
3008 "EM_SETTABSTOPS",
3009 "EM_SETPASSWORDCHAR",
3010 "EM_EMPTYUNDOBUFFER",
3011 "EM_GETFIRSTVISIBLELINE",
3012 "EM_SETREADONLY",
3013 "EM_SETWORDBREAKPROC",
3014 "EM_GETWORDBREAKPROC",
3015 "EM_GETPASSWORDCHAR",
3016 "EM_SETMARGINS",
3017 "EM_GETMARGINS",
3018 "EM_GETLIMITTEXT",
3019 "EM_POSFROMCHAR",
3020 "EM_CHARFROMPOS",
3021 "EM_SETIMESTATUS",
3022 "EM_GETIMESTATUS"
3023 };
3024
3025 static const char * const richedit_messages[] = {
3026 "EM_CANPASTE",
3027 "EM_DISPLAYBAND",
3028 "EM_EXGETSEL",
3029 "EM_EXLIMITTEXT",
3030 "EM_EXLINEFROMCHAR",
3031 "EM_EXSETSEL",
3032 "EM_FINDTEXT",
3033 "EM_FORMATRANGE",
3034 "EM_GETCHARFORMAT",
3035 "EM_GETEVENTMASK",
3036 "EM_GETOLEINTERFACE",
3037 "EM_GETPARAFORMAT",
3038 "EM_GETSELTEXT",
3039 "EM_HIDESELECTION",
3040 "EM_PASTESPECIAL",
3041 "EM_REQUESTRESIZE",
3042 "EM_SELECTIONTYPE",
3043 "EM_SETBKGNDCOLOR",
3044 "EM_SETCHARFORMAT",
3045 "EM_SETEVENTMASK",
3046 "EM_SETOLECALLBACK",
3047 "EM_SETPARAFORMAT",
3048 "EM_SETTARGETDEVICE",
3049 "EM_STREAMIN",
3050 "EM_STREAMOUT",
3051 "EM_GETTEXTRANGE",
3052 "EM_FINDWORDBREAK",
3053 "EM_SETOPTIONS",
3054 "EM_GETOPTIONS",
3055 "EM_FINDTEXTEX",
3056 "EM_GETWORDBREAKPROCEX",
3057 "EM_SETWORDBREAKPROCEX",
3058 "EM_SETUNDOLIMIT",
3059 "EM_UNKNOWN_USER_83",
3060 "EM_REDO",
3061 "EM_CANREDO",
3062 "EM_GETUNDONAME",
3063 "EM_GETREDONAME",
3064 "EM_STOPGROUPTYPING",
3065 "EM_SETTEXTMODE",
3066 "EM_GETTEXTMODE",
3067 "EM_AUTOURLDETECT",
3068 "EM_GETAUTOURLDETECT",
3069 "EM_SETPALETTE",
3070 "EM_GETTEXTEX",
3071 "EM_GETTEXTLENGTHEX",
3072 "EM_SHOWSCROLLBAR",
3073 "EM_SETTEXTEX",
3074 "EM_UNKNOWN_USER_98",
3075 "EM_UNKNOWN_USER_99",
3076 "EM_SETPUNCTUATION",
3077 "EM_GETPUNCTUATION",
3078 "EM_SETWORDWRAPMODE",
3079 "EM_GETWORDWRAPMODE",
3080 "EM_SETIMECOLOR",
3081 "EM_GETIMECOLOR",
3082 "EM_SETIMEOPTIONS",
3083 "EM_GETIMEOPTIONS",
3084 "EM_CONVPOSITION",
3085 "EM_UNKNOWN_USER_109",
3086 "EM_UNKNOWN_USER_110",
3087 "EM_UNKNOWN_USER_111",
3088 "EM_UNKNOWN_USER_112",
3089 "EM_UNKNOWN_USER_113",
3090 "EM_UNKNOWN_USER_114",
3091 "EM_UNKNOWN_USER_115",
3092 "EM_UNKNOWN_USER_116",
3093 "EM_UNKNOWN_USER_117",
3094 "EM_UNKNOWN_USER_118",
3095 "EM_UNKNOWN_USER_119",
3096 "EM_SETLANGOPTIONS",
3097 "EM_GETLANGOPTIONS",
3098 "EM_GETIMECOMPMODE",
3099 "EM_FINDTEXTW",
3100 "EM_FINDTEXTEXW",
3101 "EM_RECONVERSION",
3102 "EM_SETIMEMODEBIAS",
3103 "EM_GETIMEMODEBIAS"
3104 };
3105
3106 static const char *
3107 get_msg_name(UINT msg)
3108 {
3109 if (msg >= EM_GETSEL && msg <= EM_CHARFROMPOS)
3110 return edit_messages[msg - EM_GETSEL];
3111 if (msg >= EM_CANPASTE && msg <= EM_GETIMEMODEBIAS)
3112 return richedit_messages[msg - EM_CANPASTE];
3113 return "";
3114 }
3115
3116 static void ME_LinkNotify(ME_TextEditor *editor, UINT msg, WPARAM wParam, LPARAM lParam)
3117 {
3118 int x,y;
3119 BOOL isExact;
3120 ME_Cursor cursor; /* The start of the clicked text. */
3121
3122 ENLINK info;
3123 x = (short)LOWORD(lParam);
3124 y = (short)HIWORD(lParam);
3125 ME_CharFromPos(editor, x, y, &cursor, &isExact);
3126 if (!isExact) return;
3127
3128 if (cursor.pRun->member.run.style->fmt.dwMask & CFM_LINK &&
3129 cursor.pRun->member.run.style->fmt.dwEffects & CFE_LINK)
3130 { /* The clicked run has CFE_LINK set */
3131 ME_DisplayItem *di;
3132
3133 info.nmhdr.hwndFrom = NULL;
3134 info.nmhdr.idFrom = 0;
3135 info.nmhdr.code = EN_LINK;
3136 info.msg = msg;
3137 info.wParam = wParam;
3138 info.lParam = lParam;
3139 cursor.nOffset = 0;
3140
3141 /* find the first contiguous run with CFE_LINK set */
3142 info.chrg.cpMin = ME_GetCursorOfs(&cursor);
3143 for (di = cursor.pRun->prev;
3144 di && di->type == diRun && (di->member.run.style->fmt.dwMask & CFM_LINK) && (di->member.run.style->fmt.dwEffects & CFE_LINK);
3145 di = di->prev)
3146 {
3147 info.chrg.cpMin -= di->member.run.len;
3148 }
3149
3150 /* find the last contiguous run with CFE_LINK set */
3151 info.chrg.cpMax = ME_GetCursorOfs(&cursor) + cursor.pRun->member.run.len;
3152 for (di = cursor.pRun->next;
3153 di && di->type == diRun && (di->member.run.style->fmt.dwMask & CFM_LINK) && (di->member.run.style->fmt.dwEffects & CFE_LINK);
3154 di = di->next)
3155 {
3156 info.chrg.cpMax += di->member.run.len;
3157 }
3158
3159 ITextHost_TxNotify(editor->texthost, info.nmhdr.code, &info);
3160 }
3161 }
3162
3163 void ME_ReplaceSel(ME_TextEditor *editor, BOOL can_undo, const WCHAR *str, int len)
3164 {
3165 int from, to, nStartCursor;
3166 ME_Style *style;
3167
3168 nStartCursor = ME_GetSelectionOfs(editor, &from, &to);
3169 style = ME_GetSelectionInsertStyle(editor);
3170 ME_InternalDeleteText(editor, &editor->pCursors[nStartCursor], to-from, FALSE);
3171 ME_InsertTextFromCursor(editor, 0, str, len, style);
3172 ME_ReleaseStyle(style);
3173 /* drop temporary style if line end */
3174 /*
3175 * FIXME question: does abc\n mean: put abc,
3176 * clear temp style, put \n? (would require a change)
3177 */
3178 if (len>0 && str[len-1] == '\n')
3179 ME_ClearTempStyle(editor);
3180 ME_CommitUndo(editor);
3181 ME_UpdateSelectionLinkAttribute(editor);
3182 if (!can_undo)
3183 ME_EmptyUndoStack(editor);
3184 ME_UpdateRepaint(editor, FALSE);
3185 }
3186
3187 static void ME_SetText(ME_TextEditor *editor, void *text, BOOL unicode)
3188 {
3189 LONG codepage = unicode ? CP_UNICODE : CP_ACP;
3190 int textLen;
3191
3192 LPWSTR wszText = ME_ToUnicode(codepage, text, &textLen);
3193
3194 if (textLen > 0)
3195 {
3196 int len = -1;
3197
3198 /* uses default style! */
3199 if (!(editor->styleFlags & ES_MULTILINE))
3200 {
3201 WCHAR *p = wszText;
3202
3203 while (*p != '\0' && *p != '\r' && *p != '\n') p++;
3204 len = p - wszText;
3205 }
3206 ME_InsertTextFromCursor(editor, 0, wszText, len, editor->pBuffer->pDefaultStyle);
3207 }
3208 ME_EndToUnicode(codepage, wszText);
3209 }
3210
3211 static LRESULT ME_WmCreate(ME_TextEditor *editor, LPARAM lParam, BOOL unicode)
3212 {
3213 CREATESTRUCTW *createW = (CREATESTRUCTW*)lParam;
3214 CREATESTRUCTA *createA = (CREATESTRUCTA*)lParam;
3215 void *text = NULL;
3216 INT max;
3217
3218 if (lParam)
3219 text = unicode ? (void*)createW->lpszName : (void*)createA->lpszName;
3220
3221 ME_SetDefaultFormatRect(editor);
3222
3223 max = (editor->styleFlags & ES_DISABLENOSCROLL) ? 1 : 0;
3224 if (~editor->styleFlags & ES_DISABLENOSCROLL || editor->styleFlags & WS_VSCROLL)
3225 ITextHost_TxSetScrollRange(editor->texthost, SB_VERT, 0, max, TRUE);
3226
3227 if (~editor->styleFlags & ES_DISABLENOSCROLL || editor->styleFlags & WS_HSCROLL)
3228 ITextHost_TxSetScrollRange(editor->texthost, SB_HORZ, 0, max, TRUE);
3229
3230 if (editor->styleFlags & ES_DISABLENOSCROLL)
3231 {
3232 if (editor->styleFlags & WS_VSCROLL)
3233 {
3234 ITextHost_TxEnableScrollBar(editor->texthost, SB_VERT, ESB_DISABLE_BOTH);
3235 ITextHost_TxShowScrollBar(editor->texthost, SB_VERT, TRUE);
3236 }
3237 if (editor->styleFlags & WS_HSCROLL)
3238 {
3239 ITextHost_TxEnableScrollBar(editor->texthost, SB_HORZ, ESB_DISABLE_BOTH);
3240 ITextHost_TxShowScrollBar(editor->texthost, SB_HORZ, TRUE);
3241 }
3242 }
3243
3244 if (text)
3245 {
3246 ME_SetText(editor, text, unicode);
3247 ME_SetCursorToStart(editor, &editor->pCursors[0]);
3248 ME_SetCursorToStart(editor, &editor->pCursors[1]);
3249 }
3250
3251 ME_CommitUndo(editor);
3252 ME_WrapMarkedParagraphs(editor);
3253 ME_MoveCaret(editor);
3254 return 0;
3255 }
3256
3257
3258 #define UNSUPPORTED_MSG(e) \
3259 case e: \
3260 FIXME(#e ": stub\n"); \
3261 *phresult = S_FALSE; \
3262 return 0;
3263
3264 /* Handle messages for windowless and windowed richedit controls.
3265 *
3266 * The LRESULT that is returned is a return value for window procs,
3267 * and the phresult parameter is the COM return code needed by the
3268 * text services interface. */
3269 LRESULT ME_HandleMessage(ME_TextEditor *editor, UINT msg, WPARAM wParam,
3270 LPARAM lParam, BOOL unicode, HRESULT* phresult)
3271 {
3272 *phresult = S_OK;
3273
3274 switch(msg) {
3275
3276 UNSUPPORTED_MSG(EM_DISPLAYBAND)
3277 UNSUPPORTED_MSG(EM_FINDWORDBREAK)
3278 UNSUPPORTED_MSG(EM_FMTLINES)
3279 UNSUPPORTED_MSG(EM_FORMATRANGE)
3280 UNSUPPORTED_MSG(EM_GETBIDIOPTIONS)
3281 UNSUPPORTED_MSG(EM_GETEDITSTYLE)
3282 UNSUPPORTED_MSG(EM_GETIMECOMPMODE)
3283 UNSUPPORTED_MSG(EM_GETIMESTATUS)
3284 UNSUPPORTED_MSG(EM_SETIMESTATUS)
3285 UNSUPPORTED_MSG(EM_GETLANGOPTIONS)
3286 UNSUPPORTED_MSG(EM_GETREDONAME)
3287 UNSUPPORTED_MSG(EM_GETTYPOGRAPHYOPTIONS)
3288 UNSUPPORTED_MSG(EM_GETUNDONAME)
3289 UNSUPPORTED_MSG(EM_GETWORDBREAKPROCEX)
3290 UNSUPPORTED_MSG(EM_PASTESPECIAL)
3291 UNSUPPORTED_MSG(EM_SELECTIONTYPE)
3292 UNSUPPORTED_MSG(EM_SETBIDIOPTIONS)
3293 UNSUPPORTED_MSG(EM_SETEDITSTYLE)
3294 UNSUPPORTED_MSG(EM_SETLANGOPTIONS)
3295 UNSUPPORTED_MSG(EM_SETMARGINS)
3296 UNSUPPORTED_MSG(EM_SETPALETTE)
3297 UNSUPPORTED_MSG(EM_SETTABSTOPS)
3298 UNSUPPORTED_MSG(EM_SETTYPOGRAPHYOPTIONS)
3299 UNSUPPORTED_MSG(EM_SETWORDBREAKPROCEX)
3300
3301 /* Messages specific to Richedit controls */
3302
3303 case EM_STREAMIN:
3304 return ME_StreamIn(editor, wParam, (EDITSTREAM*)lParam, TRUE);
3305 case EM_STREAMOUT:
3306 return ME_StreamOut(editor, wParam, (EDITSTREAM *)lParam);
3307 case WM_GETDLGCODE:
3308 {
3309 UINT code = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS;
3310
3311 if (lParam)
3312 editor->bDialogMode = TRUE;
3313 if (editor->styleFlags & ES_MULTILINE)
3314 code |= DLGC_WANTMESSAGE;
3315 if (!(editor->styleFlags & ES_SAVESEL))
3316 code |= DLGC_HASSETSEL;
3317 return code;
3318 }
3319 case EM_EMPTYUNDOBUFFER:
3320 ME_EmptyUndoStack(editor);
3321 return 0;
3322 case EM_GETSEL:
3323 {
3324 /* Note: wParam/lParam can be NULL */
3325 UINT from, to;
3326 PUINT pfrom = wParam ? (PUINT)wParam : &from;
3327 PUINT pto = lParam ? (PUINT)lParam : &to;
3328 ME_GetSelectionOfs(editor, (int *)pfrom, (int *)pto);
3329 if ((*pfrom|*pto) & 0xFFFF0000)
3330 return -1;
3331 return MAKELONG(*pfrom,*pto);
3332 }
3333 case EM_EXGETSEL:
3334 {
3335 CHARRANGE *pRange = (CHARRANGE *)lParam;
3336 ME_GetSelectionOfs(editor, &pRange->cpMin, &pRange->cpMax);
3337 TRACE("EM_EXGETSEL = (%d,%d)\n", pRange->cpMin, pRange->cpMax);
3338 return 0;
3339 }
3340 case EM_SETUNDOLIMIT:
3341 {
3342 if ((int)wParam < 0)
3343 editor->nUndoLimit = STACK_SIZE_DEFAULT;
3344 else
3345 editor->nUndoLimit = min(wParam, STACK_SIZE_MAX);
3346 /* Setting a max stack size keeps wine from getting killed
3347 for hogging memory. Windows allocates all this memory at once, so
3348 no program would realistically set a value above our maximum. */
3349 return editor->nUndoLimit;
3350 }
3351 case EM_CANUNDO:
3352 return !list_empty( &editor->undo_stack );
3353 case EM_CANREDO:
3354 return !list_empty( &editor->redo_stack );
3355 case WM_UNDO: /* FIXME: actually not the same */
3356 case EM_UNDO:
3357 return ME_Undo(editor);
3358 case EM_REDO:
3359 return ME_Redo(editor);
3360 case EM_GETOPTIONS:
3361 {
3362 /* these flags are equivalent to the ES_* counterparts */
3363 DWORD mask = ECO_VERTICAL | ECO_AUTOHSCROLL | ECO_AUTOVSCROLL |
3364 ECO_NOHIDESEL | ECO_READONLY | ECO_WANTRETURN | ECO_SELECTIONBAR;
3365 DWORD settings = editor->styleFlags & mask;
3366
3367 return settings;
3368 }
3369 case EM_SETFONTSIZE:
3370 {
3371 CHARFORMAT2W cf;
3372 LONG tmp_size, size;
3373 BOOL is_increase = ((LONG)wParam > 0);
3374
3375 if (editor->mode & TM_PLAINTEXT)
3376 return FALSE;
3377
3378 cf.cbSize = sizeof(cf);
3379 cf.dwMask = CFM_SIZE;
3380 ME_GetSelectionCharFormat(editor, &cf);
3381 tmp_size = (cf.yHeight / 20) + wParam;
3382
3383 if (tmp_size <= 1)
3384 size = 1;
3385 else if (tmp_size > 12 && tmp_size < 28 && tmp_size % 2)
3386 size = tmp_size + (is_increase ? 1 : -1);
3387 else if (tmp_size > 28 && tmp_size < 36)
3388 size = is_increase ? 36 : 28;
3389 else if (tmp_size > 36 && tmp_size < 48)
3390 size = is_increase ? 48 : 36;
3391 else if (tmp_size > 48 && tmp_size < 72)
3392 size = is_increase ? 72 : 48;
3393 else if (tmp_size > 72 && tmp_size < 80)
3394 size = is_increase ? 80 : 72;
3395 else if (tmp_size > 80 && tmp_size < 1638)
3396 size = 10 * (is_increase ? (tmp_size / 10 + 1) : (tmp_size / 10));
3397 else if (tmp_size >= 1638)
3398 size = 1638;
3399 else
3400 size = tmp_size;
3401
3402 cf.yHeight = size * 20; /* convert twips to points */
3403 ME_SetSelectionCharFormat(editor, &cf);
3404 ME_CommitUndo(editor);
3405 ME_WrapMarkedParagraphs(editor);
3406 ME_UpdateScrollBar(editor);
3407 ME_Repaint(editor);
3408
3409 return TRUE;
3410 }
3411 case EM_SETOPTIONS:
3412 {
3413 /* these flags are equivalent to ES_* counterparts, except for
3414 * ECO_AUTOWORDSELECTION that doesn't have an ES_* counterpart,
3415 * but is still stored in editor->styleFlags. */
3416 const DWORD mask = ECO_VERTICAL | ECO_AUTOHSCROLL | ECO_AUTOVSCROLL |
3417 ECO_NOHIDESEL | ECO_READONLY | ECO_WANTRETURN |
3418 ECO_SELECTIONBAR | ECO_AUTOWORDSELECTION;
3419 DWORD settings = mask & editor->styleFlags;
3420 DWORD oldSettings = settings;
3421 DWORD changedSettings;
3422
3423 switch(wParam)
3424 {
3425 case ECOOP_SET:
3426 settings = lParam;
3427 break;
3428 case ECOOP_OR:
3429 settings |= lParam;
3430 break;
3431 case ECOOP_AND:
3432 settings &= lParam;
3433 break;
3434 case ECOOP_XOR:
3435 settings ^= lParam;
3436 }
3437 changedSettings = oldSettings ^ settings;
3438
3439 if (changedSettings) {
3440 editor->styleFlags = (editor->styleFlags & ~mask) | (settings & mask);
3441
3442 if (changedSettings & ECO_SELECTIONBAR)
3443 {
3444 ITextHost_TxInvalidateRect(editor->texthost, &editor->rcFormat, TRUE);
3445 if (settings & ECO_SELECTIONBAR) {
3446 assert(!editor->selofs);
3447 editor->selofs = SELECTIONBAR_WIDTH;
3448 editor->rcFormat.left += editor->selofs;
3449 } else {
3450 editor->rcFormat.left -= editor->selofs;
3451 editor->selofs = 0;
3452 }
3453 ME_RewrapRepaint(editor);
3454 }
3455
3456 if (changedSettings & settings & ECO_VERTICAL)
3457 FIXME("ECO_VERTICAL not implemented yet!\n");
3458 if (changedSettings & settings & ECO_AUTOHSCROLL)
3459 FIXME("ECO_AUTOHSCROLL not implemented yet!\n");
3460 if (changedSettings & settings & ECO_AUTOVSCROLL)
3461 FIXME("ECO_AUTOVSCROLL not implemented yet!\n");
3462 if (changedSettings & settings & ECO_NOHIDESEL)
3463 FIXME("ECO_NOHIDESEL not implemented yet!\n");
3464 if (changedSettings & settings & ECO_WANTRETURN)
3465 FIXME("ECO_WANTRETURN not implemented yet!\n");
3466 if (changedSettings & settings & ECO_AUTOWORDSELECTION)
3467 FIXME("ECO_AUTOWORDSELECTION not implemented yet!\n");
3468 }
3469
3470 return settings;
3471 }
3472 case EM_SETSEL:
3473 {
3474 return handle_EM_EXSETSEL( editor, wParam, lParam );
3475 }
3476 case EM_SETSCROLLPOS:
3477 {
3478 POINT *point = (POINT *)lParam;
3479 ME_ScrollAbs(editor, point->x, point->y);
3480 return 0;
3481 }
3482 case EM_AUTOURLDETECT:
3483 {
3484 if (wParam==1 || wParam ==0)
3485 {
3486 editor->AutoURLDetect_bEnable = (BOOL)wParam;
3487 return 0;
3488 }
3489 return E_INVALIDARG;
3490 }
3491 case EM_GETAUTOURLDETECT:
3492 {
3493 return editor->AutoURLDetect_bEnable;
3494 }
3495 case EM_EXSETSEL:
3496 {
3497 CHARRANGE range = *(CHARRANGE *)lParam;
3498
3499 return handle_EM_EXSETSEL( editor, range.cpMin, range.cpMax );
3500 }
3501 case EM_SHOWSCROLLBAR:
3502 {
3503 DWORD flags;
3504
3505 switch (wParam)
3506 {
3507 case SB_HORZ:
3508 flags = WS_HSCROLL;
3509 break;
3510 case SB_VERT:
3511 flags = WS_VSCROLL;
3512 break;
3513 case SB_BOTH:
3514 flags = WS_HSCROLL|WS_VSCROLL;
3515 break;
3516 default:
3517 return 0;
3518 }
3519
3520 if (lParam) {
3521 editor->styleFlags |= flags;
3522 if (flags & WS_HSCROLL)
3523 ITextHost_TxShowScrollBar(editor->texthost, SB_HORZ,
3524 editor->nTotalWidth > editor->sizeWindow.cx);
3525 if (flags & WS_VSCROLL)
3526 ITextHost_TxShowScrollBar(editor->texthost, SB_VERT,
3527 editor->nTotalLength > editor->sizeWindow.cy);
3528 } else {
3529 editor->styleFlags &= ~flags;
3530 ITextHost_TxShowScrollBar(editor->texthost, wParam, FALSE);
3531 }
3532 return 0;
3533 }
3534 case EM_SETTEXTEX:
3535 {
3536 LPWSTR wszText;
3537 SETTEXTEX *pStruct = (SETTEXTEX *)wParam;
3538 int from, to, len;
3539 ME_Style *style;
3540 BOOL bRtf, bUnicode, bSelection, bUTF8;
3541 int oldModify = editor->nModifyStep;
3542 static const char utf8_bom[] = {0xef, 0xbb, 0xbf};
3543
3544 if (!pStruct) return 0;
3545
3546 /* If we detect ascii rtf at the start of the string,
3547 * we know it isn't unicode. */
3548 bRtf = (lParam && (!strncmp((char *)lParam, "{\\rtf", 5) ||
3549 !strncmp((char *)lParam, "{\\urtf", 6)));
3550 bUnicode = !bRtf && pStruct->codepage == CP_UNICODE;
3551 bUTF8 = (lParam && (!strncmp((char *)lParam, utf8_bom, 3)));
3552
3553 TRACE("EM_SETTEXTEX - %s, flags %d, cp %d\n",
3554 bUnicode ? debugstr_w((LPCWSTR)lParam) : debugstr_a((LPCSTR)lParam),
3555 pStruct->flags, pStruct->codepage);
3556
3557 bSelection = (pStruct->flags & ST_SELECTION) != 0;
3558 if (bSelection) {
3559 int nStartCursor = ME_GetSelectionOfs(editor, &from, &to);
3560 style = ME_GetSelectionInsertStyle(editor);
3561 ME_InternalDeleteText(editor, &editor->pCursors[nStartCursor], to - from, FALSE);
3562 } else {
3563 ME_Cursor start;
3564 ME_SetCursorToStart(editor, &start);
3565 ME_InternalDeleteText(editor, &start, ME_GetTextLength(editor), FALSE);
3566 style = editor->pBuffer->pDefaultStyle;
3567 }
3568
3569 if (bRtf) {
3570 ME_StreamInRTFString(editor, bSelection, (char *)lParam);
3571 if (bSelection) {
3572 /* FIXME: The length returned doesn't include the rtf control
3573 * characters, only the actual text. */
3574 len = lParam ? strlen((char *)lParam) : 0;
3575 }
3576 } else {
3577 if (bUTF8 && !bUnicode) {
3578 wszText = ME_ToUnicode(CP_UTF8, (void *)(lParam+3), &len);
3579 ME_InsertTextFromCursor(editor, 0, wszText, len, style);
3580 ME_EndToUnicode(CP_UTF8, wszText);
3581 } else {
3582 wszText = ME_ToUnicode(pStruct->codepage, (void *)lParam, &len);
3583 ME_InsertTextFromCursor(editor, 0, wszText, len, style);
3584 ME_EndToUnicode(pStruct->codepage, wszText);
3585 }
3586 }
3587
3588 if (bSelection) {
3589 ME_ReleaseStyle(style);
3590 ME_UpdateSelectionLinkAttribute(editor);
3591 } else {
3592 ME_Cursor cursor;
3593 len = 1;
3594 ME_SetCursorToStart(editor, &cursor);
3595 ME_UpdateLinkAttribute(editor, &cursor, INT_MAX);
3596 }
3597 ME_CommitUndo(editor);
3598 if (!(pStruct->flags & ST_KEEPUNDO))
3599 {
3600 editor->nModifyStep = oldModify;
3601 ME_EmptyUndoStack(editor);
3602 }
3603 ME_UpdateRepaint(editor, FALSE);
3604 return len;
3605 }
3606 case EM_SETBKGNDCOLOR:
3607 {
3608 LRESULT lColor;
3609 if (editor->rgbBackColor != -1) {
3610 DeleteObject(editor->hbrBackground);
3611 lColor = editor->rgbBackColor;
3612 }
3613 else lColor = ITextHost_TxGetSysColor(editor->texthost, COLOR_WINDOW);
3614
3615 if (wParam)
3616 {
3617 editor->rgbBackColor = -1;
3618 editor->hbrBackground = GetSysColorBrush(COLOR_WINDOW);
3619 }
3620 else
3621 {
3622 editor->rgbBackColor = lParam;
3623 editor->hbrBackground = CreateSolidBrush(editor->rgbBackColor);
3624 }
3625 ITextHost_TxInvalidateRect(editor->texthost, NULL, TRUE);
3626 return lColor;
3627 }
3628 case EM_GETMODIFY:
3629 return editor->nModifyStep == 0 ? 0 : -1;
3630 case EM_SETMODIFY:
3631 {
3632 if (wParam)
3633 editor->nModifyStep = 1;
3634 else
3635 editor->nModifyStep = 0;
3636
3637 return 0;
3638 }
3639 case EM_SETREADONLY:
3640 {
3641 if (wParam)
3642 editor->styleFlags |= ES_READONLY;
3643 else
3644 editor->styleFlags &= ~ES_READONLY;
3645 return 1;
3646 }
3647 case EM_SETEVENTMASK:
3648 {
3649 DWORD nOldMask = editor->nEventMask;
3650
3651 editor->nEventMask = lParam;
3652 return nOldMask;
3653 }
3654 case EM_GETEVENTMASK:
3655 return editor->nEventMask;
3656 case EM_SETCHARFORMAT:
3657 {
3658 CHARFORMAT2W buf, *p;
3659 BOOL bRepaint = TRUE;
3660 p = ME_ToCF2W(&buf, (CHARFORMAT2W *)lParam);
3661 if (p == NULL) return 0;
3662 if (wParam & SCF_ALL) {
3663 if (editor->mode & TM_PLAINTEXT) {
3664 ME_SetDefaultCharFormat(editor, p);
3665 } else {
3666 ME_Cursor start;
3667 ME_SetCursorToStart(editor, &start);
3668 ME_SetCharFormat(editor, &start, NULL, p);
3669 editor->nModifyStep = 1;
3670 }
3671 } else if (wParam & SCF_SELECTION) {
3672 if (editor->mode & TM_PLAINTEXT)
3673 return 0;
3674 if (wParam & SCF_WORD) {
3675 ME_Cursor start;
3676 ME_Cursor end = editor->pCursors[0];
3677 ME_MoveCursorWords(editor, &end, +1);
3678 start = end;
3679 ME_MoveCursorWords(editor, &start, -1);
3680 ME_SetCharFormat(editor, &start, &end, p);
3681 }
3682 bRepaint = ME_IsSelection(editor);
3683 ME_SetSelectionCharFormat(editor, p);
3684 if (bRepaint) editor->nModifyStep = 1;
3685 } else { /* SCF_DEFAULT */
3686 ME_SetDefaultCharFormat(editor, p);
3687 }
3688 ME_CommitUndo(editor);
3689 if (bRepaint)
3690 {
3691 ME_WrapMarkedParagraphs(editor);
3692 ME_UpdateScrollBar(editor);
3693 ME_Repaint(editor);
3694 }
3695 return 1;
3696 }
3697 case EM_GETCHARFORMAT:
3698 {
3699 CHARFORMAT2W tmp, *dst = (CHARFORMAT2W *)lParam;
3700 if (dst->cbSize != sizeof(CHARFORMATA) &&
3701 dst->cbSize != sizeof(CHARFORMATW) &&
3702 dst->cbSize != sizeof(CHARFORMAT2A) &&
3703 dst->cbSize != sizeof(CHARFORMAT2W))
3704 return 0;
3705 tmp.cbSize = sizeof(tmp);
3706 if (!wParam)
3707 ME_GetDefaultCharFormat(editor, &tmp);
3708 else
3709 ME_GetSelectionCharFormat(editor, &tmp);
3710 ME_CopyToCFAny(dst, &tmp);
3711 return tmp.dwMask;
3712 }
3713 case EM_SETPARAFORMAT:
3714 {
3715 BOOL result = ME_SetSelectionParaFormat(editor, (PARAFORMAT2 *)lParam);
3716 ME_WrapMarkedParagraphs(editor);
3717 ME_UpdateScrollBar(editor);
3718 ME_Repaint(editor);
3719 ME_CommitUndo(editor);
3720 return result;
3721 }
3722 case EM_GETPARAFORMAT:
3723 ME_GetSelectionParaFormat(editor, (PARAFORMAT2 *)lParam);
3724 return ((PARAFORMAT2 *)lParam)->dwMask;
3725 case EM_GETFIRSTVISIBLELINE:
3726 {
3727 ME_DisplayItem *p = editor->pBuffer->pFirst;
3728 int y = editor->vert_si.nPos;
3729 int ypara = 0;
3730 int count = 0;
3731 int ystart, yend;
3732 while(p) {
3733 p = ME_FindItemFwd(p, diStartRowOrParagraphOrEnd);
3734 if (p->type == diTextEnd)
3735 break;
3736 if (p->type == diParagraph) {
3737 ypara = p->member.para.pt.y;
3738 continue;
3739 }
3740 ystart = ypara + p->member.row.pt.y;
3741 yend = ystart + p->member.row.nHeight;
3742 if (y < yend) {
3743 break;
3744 }
3745 count++;
3746 }
3747 return count;
3748 }
3749 case EM_HIDESELECTION:
3750 {
3751 editor->bHideSelection = (wParam != 0);
3752 ME_InvalidateSelection(editor);
3753 return 0;
3754 }
3755 case EM_LINESCROLL:
3756 {
3757 if (!(editor->styleFlags & ES_MULTILINE))
3758 return FALSE;
3759 ME_ScrollDown( editor, lParam * get_default_line_height( editor ) );
3760 return TRUE;
3761 }
3762 case WM_CLEAR:
3763 {
3764 int from, to;
3765 int nStartCursor = ME_GetSelectionOfs(editor, &from, &to);
3766 ME_InternalDeleteText(editor, &editor->pCursors[nStartCursor], to-from, FALSE);
3767 ME_CommitUndo(editor);
3768 ME_UpdateRepaint(editor, TRUE);
3769 return 0;
3770 }
3771 case EM_REPLACESEL:
3772 {
3773 int len = 0;
3774 LONG codepage = unicode ? CP_UNICODE : CP_ACP;
3775 LPWSTR wszText = ME_ToUnicode(codepage, (void *)lParam, &len);
3776
3777 TRACE("EM_REPLACESEL - %s\n", debugstr_w(wszText));
3778
3779 ME_ReplaceSel(editor, !!wParam, wszText, len);
3780 ME_EndToUnicode(codepage, wszText);
3781 return len;
3782 }
3783 case EM_SCROLLCARET:
3784 ME_EnsureVisible(editor, &editor->pCursors[0]);
3785 return 0;
3786 case WM_SETFONT:
3787 {
3788 LOGFONTW lf;
3789 CHARFORMAT2W fmt;
3790 HDC hDC;
3791 BOOL bRepaint = LOWORD(lParam);
3792
3793 if (!wParam)
3794 wParam = (WPARAM)GetStockObject(SYSTEM_FONT);
3795
3796 if (!GetObjectW((HGDIOBJ)wParam, sizeof(LOGFONTW), &lf))
3797 return 0;
3798
3799 hDC = ITextHost_TxGetDC(editor->texthost);
3800 ME_CharFormatFromLogFont(hDC, &lf, &fmt);
3801 ITextHost_TxReleaseDC(editor->texthost, hDC);
3802 if (editor->mode & TM_RICHTEXT) {
3803 ME_Cursor start;
3804 ME_SetCursorToStart(editor, &start);
3805 ME_SetCharFormat(editor, &start, NULL, &fmt);
3806 }
3807 ME_SetDefaultCharFormat(editor, &fmt);
3808
3809 ME_CommitUndo(editor);
3810 ME_MarkAllForWrapping(editor);
3811 ME_WrapMarkedParagraphs(editor);
3812 ME_UpdateScrollBar(editor);
3813 if (bRepaint)
3814 ME_Repaint(editor);
3815 return 0;
3816 }
3817 case WM_SETTEXT:
3818 {
3819 ME_Cursor cursor;
3820 ME_SetCursorToStart(editor, &cursor);
3821 ME_InternalDeleteText(editor, &cursor, ME_GetTextLength(editor), FALSE);
3822 if (lParam)
3823 {
3824 TRACE("WM_SETTEXT lParam==%lx\n",lParam);
3825 if (!strncmp((char *)lParam, "{\\rtf", 5) ||
3826 !strncmp((char *)lParam, "{\\urtf", 6))
3827 {
3828 /* Undocumented: WM_SETTEXT supports RTF text */
3829 ME_StreamInRTFString(editor, 0, (char *)lParam);
3830 }
3831 else
3832 ME_SetText(editor, (void*)lParam, unicode);
3833 }
3834 else
3835 TRACE("WM_SETTEXT - NULL\n");
3836 ME_SetCursorToStart(editor, &cursor);
3837 ME_UpdateLinkAttribute(editor, &cursor, INT_MAX);
3838 ME_SetSelection(editor, 0, 0);
3839 editor->nModifyStep = 0;
3840 ME_CommitUndo(editor);
3841 ME_EmptyUndoStack(editor);
3842 ME_UpdateRepaint(editor, FALSE);
3843 return 1;
3844 }
3845 case EM_CANPASTE:
3846 {
3847 UINT nRTFFormat = RegisterClipboardFormatA("Rich Text Format");
3848 if (IsClipboardFormatAvailable(nRTFFormat))
3849 return TRUE;
3850 if (IsClipboardFormatAvailable(CF_UNICODETEXT))
3851 return TRUE;
3852 return FALSE;
3853 }
3854 case WM_PASTE:
3855 case WM_MBUTTONDOWN:
3856 ME_Paste(editor);
3857 return 0;
3858 case WM_CUT:
3859 case WM_COPY:
3860 {
3861 int nFrom, nTo, nStartCur = ME_GetSelectionOfs(editor, &nFrom, &nTo);
3862 int nChars = nTo - nFrom;
3863 ME_Cursor *selStart = &editor->pCursors[nStartCur];
3864
3865 if (ME_Copy(editor, selStart, nChars) && msg == WM_CUT)
3866 {
3867 ME_InternalDeleteText(editor, selStart, nChars, FALSE);
3868 ME_CommitUndo(editor);
3869 ME_UpdateRepaint(editor, TRUE);
3870 }
3871 return 0;
3872 }
3873 case WM_GETTEXTLENGTH:
3874 {
3875 GETTEXTLENGTHEX how;
3876
3877 /* CR/LF conversion required in 2.0 mode, verbatim in 1.0 mode */
3878 how.flags = GTL_CLOSE | (editor->bEmulateVersion10 ? 0 : GTL_USECRLF) | GTL_NUMCHARS;
3879 how.codepage = unicode ? CP_UNICODE : CP_ACP;
3880 return ME_GetTextLengthEx(editor, &how);
3881 }
3882 case EM_GETTEXTLENGTHEX:
3883 return ME_GetTextLengthEx(editor, (GETTEXTLENGTHEX *)wParam);
3884 case WM_GETTEXT:
3885 {
3886 GETTEXTEX ex;
3887 ex.cb = wParam * (unicode ? sizeof(WCHAR) : sizeof(CHAR));
3888 ex.flags = GT_USECRLF;
3889 ex.codepage = unicode ? CP_UNICODE : CP_ACP;
3890 ex.lpDefaultChar = NULL;
3891 ex.lpUsedDefChar = NULL;
3892 return ME_GetTextEx(editor, &ex, lParam);
3893 }
3894 case EM_GETTEXTEX:
3895 return ME_GetTextEx(editor, (GETTEXTEX*)wParam, lParam);
3896 case EM_GETSELTEXT:
3897 {
3898 int nFrom, nTo, nStartCur = ME_GetSelectionOfs(editor, &nFrom, &nTo);
3899 ME_Cursor *from = &editor->pCursors[nStartCur];
3900 return ME_GetTextRange(editor, (WCHAR *)lParam, from,
3901 nTo - nFrom, unicode);
3902 }
3903 case EM_GETSCROLLPOS:
3904 {
3905 POINT *point = (POINT *)lParam;
3906 point->x = editor->horz_si.nPos;
3907 point->y = editor->vert_si.nPos;
3908 /* 16-bit scaled value is returned as stored in scrollinfo */
3909 if (editor->horz_si.nMax > 0xffff)
3910 point->x = MulDiv(point->x, 0xffff, editor->horz_si.nMax);
3911 if (editor->vert_si.nMax > 0xffff)
3912 point->y = MulDiv(point->y, 0xffff, editor->vert_si.nMax);
3913 return 1;
3914 }
3915 case EM_GETTEXTRANGE:
3916 {
3917 TEXTRANGEW *rng = (TEXTRANGEW *)lParam;
3918 ME_Cursor start;
3919 int nStart = rng->chrg.cpMin;
3920 int nEnd = rng->chrg.cpMax;
3921 int textlength = ME_GetTextLength(editor);
3922
3923 TRACE("EM_GETTEXTRANGE min=%d max=%d unicode=%d textlength=%d\n",
3924 rng->chrg.cpMin, rng->chrg.cpMax, unicode, textlength);
3925 if (nStart < 0) return 0;
3926 if ((nStart == 0 && nEnd == -1) || nEnd > textlength)
3927 nEnd = textlength;
3928 if (nStart >= nEnd) return 0;
3929
3930 ME_CursorFromCharOfs(editor, nStart, &start);
3931 return ME_GetTextRange(editor, rng->lpstrText, &start, nEnd - nStart, unicode);
3932 }
3933 case EM_GETLINE:
3934 {
3935 ME_DisplayItem *run;
3936 const unsigned int nMaxChars = *(WORD *) lParam;
3937 unsigned int nCharsLeft = nMaxChars;
3938 char *dest = (char *) lParam;
3939 BOOL wroteNull = FALSE;
3940
3941 TRACE("EM_GETLINE: row=%d, nMaxChars=%d (%s)\n", (int) wParam, nMaxChars,
3942 unicode ? "Unicode" : "Ansi");
3943
3944 run = ME_FindRowWithNumber(editor, wParam);
3945 if (run == NULL)
3946 return 0;
3947
3948 while (nCharsLeft && (run = ME_FindItemFwd(run, diRunOrStartRow))
3949 && run->type == diRun)
3950 {
3951 WCHAR *str = get_text( &run->member.run, 0 );
3952 unsigned int nCopy;
3953
3954 nCopy = min(nCharsLeft, run->member.run.len);
3955
3956 if (unicode)
3957 memcpy(dest, str, nCopy * sizeof(WCHAR));
3958 else
3959 nCopy = WideCharToMultiByte(CP_ACP, 0, str, nCopy, dest,
3960 nCharsLeft, NULL, NULL);
3961 dest += nCopy * (unicode ? sizeof(WCHAR) : 1);
3962 nCharsLeft -= nCopy;
3963 }
3964
3965 /* append line termination, space allowing */
3966 if (nCharsLeft > 0)
3967 {
3968 if (unicode)
3969 *((WCHAR *)dest) = '\0';
3970 else
3971 *dest = '\0';
3972 nCharsLeft--;
3973 wroteNull = TRUE;
3974 }
3975
3976 TRACE("EM_GETLINE: got %u characters\n", nMaxChars - nCharsLeft);
3977 return nMaxChars - nCharsLeft - (wroteNull ? 1 : 0);
3978 }
3979 case EM_GETLINECOUNT:
3980 {
3981 ME_DisplayItem *item = editor->pBuffer->pFirst->next;
3982 int nRows = 0;
3983
3984 ME_DisplayItem *prev_para = NULL, *last_para = NULL;
3985
3986 while (item != editor->pBuffer->pLast)
3987 {
3988 assert(item->type == diParagraph);
3989 prev_para = ME_FindItemBack(item, diRun);
3990 if (prev_para) {
3991 assert(prev_para->member.run.nFlags & MERF_ENDPARA);
3992 }
3993 nRows += item->member.para.nRows;
3994 item = item->member.para.next_para;
3995 }
3996 last_para = ME_FindItemBack(item, diRun);
3997 assert(last_para);
3998 assert(last_para->member.run.nFlags & MERF_ENDPARA);
3999 if (editor->bEmulateVersion10 && prev_para &&
4000 last_para->member.run.nCharOfs == 0 &&
4001 prev_para->member.run.len == 1 &&
4002 *get_text( &prev_para->member.run, 0 ) == '\r')
4003 {
4004 /* In 1.0 emulation, the last solitary \r at the very end of the text
4005 (if one exists) is NOT a line break.
4006 FIXME: this is an ugly hack. This should have a more regular model. */
4007 nRows--;
4008 }
4009
4010 TRACE("EM_GETLINECOUNT: nRows==%d\n", nRows);
4011 return max(1, nRows);
4012 }
4013 case EM_LINEFROMCHAR:
4014 {
4015 if (wParam == -1)
4016 return ME_RowNumberFromCharOfs(editor, ME_GetCursorOfs(&editor->pCursors[1]));
4017 else
4018 return ME_RowNumberFromCharOfs(editor, wParam);
4019 }
4020 case EM_EXLINEFROMCHAR:
4021 {
4022 if (lParam == -1)
4023 return ME_RowNumberFromCharOfs(editor, ME_GetCursorOfs(&editor->pCursors[1]));
4024 else
4025 return ME_RowNumberFromCharOfs(editor, lParam);
4026 }
4027 case EM_LINEINDEX:
4028 {
4029 ME_DisplayItem *item, *para;
4030 int nCharOfs;
4031
4032 if (wParam == -1)
4033 item = ME_FindItemBack(editor->pCursors[0].pRun, diStartRow);
4034 else
4035 item = ME_FindRowWithNumber(editor, wParam);
4036 if (!item)
4037 return -1;
4038 para = ME_GetParagraph(item);
4039 item = ME_FindItemFwd(item, diRun);
4040 nCharOfs = para->member.para.nCharOfs + item->member.run.nCharOfs;
4041 TRACE("EM_LINEINDEX: nCharOfs==%d\n", nCharOfs);
4042 return nCharOfs;
4043 }
4044 case EM_LINELENGTH:
4045 {
4046 ME_DisplayItem *item, *item_end;
4047 int nChars = 0, nThisLineOfs = 0, nNextLineOfs = 0;
4048 ME_DisplayItem *para, *run;
4049
4050 if (wParam > ME_GetTextLength(editor))
4051 return 0;
4052 if (wParam == -1)
4053 {
4054 FIXME("EM_LINELENGTH: returning number of unselected characters on lines with selection unsupported.\n");
4055 return 0;
4056 }
4057 ME_RunOfsFromCharOfs(editor, wParam, &para, &run, NULL);
4058 item = ME_RowStart(run);
4059 nThisLineOfs = ME_CharOfsFromRunOfs(editor, para, ME_FindItemFwd(item, diRun), 0);
4060 item_end = ME_FindItemFwd(item, diStartRowOrParagraphOrEnd);
4061 if (item_end->type == diStartRow) {
4062 nNextLineOfs = ME_CharOfsFromRunOfs(editor, para, ME_FindItemFwd(item_end, diRun), 0);
4063 } else {
4064 ME_DisplayItem *endRun = ME_FindItemBack(item_end, diRun);
4065 assert(endRun && endRun->member.run.nFlags & MERF_ENDPARA);
4066 nNextLineOfs = item_end->member.para.nCharOfs - endRun->member.run.len;
4067 }
4068 nChars = nNextLineOfs - nThisLineOfs;
4069 TRACE("EM_LINELENGTH(%ld)==%d\n",wParam, nChars);
4070 return nChars;
4071 }
4072 case EM_EXLIMITTEXT:
4073 {
4074 if ((int)lParam < 0)
4075 return 0;
4076 if (lParam == 0)
4077 editor->nTextLimit = 65536;
4078 else
4079 editor->nTextLimit = (int) lParam;
4080 return 0;
4081 }
4082 case EM_LIMITTEXT:
4083 {
4084 if (wParam == 0)
4085 editor->nTextLimit = 65536;
4086 else
4087 editor->nTextLimit = (int) wParam;
4088 return 0;
4089 }
4090 case EM_GETLIMITTEXT:
4091 {
4092 return editor->nTextLimit;
4093 }
4094 case EM_FINDTEXT:
4095 {
4096 LRESULT r;
4097 if(!unicode){
4098 FINDTEXTA *ft = (FINDTEXTA *)lParam;
4099 int nChars = MultiByteToWideChar(CP_ACP, 0, ft->lpstrText, -1, NULL, 0);
4100 WCHAR *tmp;
4101
4102 if ((tmp = ALLOC_N_OBJ(WCHAR, nChars)) != NULL)
4103 MultiByteToWideChar(CP_ACP, 0, ft->lpstrText, -1, tmp, nChars);
4104 r = ME_FindText(editor, wParam, &ft->chrg, tmp, NULL);
4105 FREE_OBJ( tmp );
4106 }else{
4107 FINDTEXTW *ft = (FINDTEXTW *)lParam;
4108 r = ME_FindText(editor, wParam, &ft->chrg, ft->lpstrText, NULL);
4109 }
4110 return r;
4111 }
4112 case EM_FINDTEXTEX:
4113 {
4114 LRESULT r;
4115 if(!unicode){
4116 FINDTEXTEXA *ex = (FINDTEXTEXA *)lParam;
4117 int nChars = MultiByteToWideChar(CP_ACP, 0, ex->lpstrText, -1, NULL, 0);
4118 WCHAR *tmp;
4119
4120 if ((tmp = ALLOC_N_OBJ(WCHAR, nChars)) != NULL)
4121 MultiByteToWideChar(CP_ACP, 0, ex->lpstrText, -1, tmp, nChars);
4122 r = ME_FindText(editor, wParam, &ex->chrg, tmp, &ex->chrgText);
4123 FREE_OBJ( tmp );
4124 }else{
4125 FINDTEXTEXW *ex = (FINDTEXTEXW *)lParam;
4126 r = ME_FindText(editor, wParam, &ex->chrg, ex->lpstrText, &ex->chrgText);
4127 }
4128 return r;
4129 }
4130 case EM_FINDTEXTW:
4131 {
4132 FINDTEXTW *ft = (FINDTEXTW *)lParam;
4133 return ME_FindText(editor, wParam, &ft->chrg, ft->lpstrText, NULL);
4134 }
4135 case EM_FINDTEXTEXW:
4136 {
4137 FINDTEXTEXW *ex = (FINDTEXTEXW *)lParam;
4138 return ME_FindText(editor, wParam, &ex->chrg, ex->lpstrText, &ex->chrgText);
4139 }
4140 case EM_GETZOOM:
4141 if (!wParam || !lParam)
4142 return FALSE;
4143 *(int *)wParam = editor->nZoomNumerator;
4144 *(int *)lParam = editor->nZoomDenominator;
4145 return TRUE;
4146 case EM_SETZOOM:
4147 return ME_SetZoom(editor, wParam, lParam);
4148 case EM_CHARFROMPOS:
4149 {
4150 ME_Cursor cursor;
4151 if (ME_CharFromPos(editor, ((POINTL *)lParam)->x, ((POINTL *)lParam)->y,
4152 &cursor, NULL))
4153 return ME_GetCursorOfs(&cursor);
4154 else
4155 return -1;
4156 }
4157 case EM_POSFROMCHAR:
4158 {
4159 ME_DisplayItem *pPara, *pRun;
4160 int nCharOfs, nOffset, nLength;
4161 POINTL pt = {0,0};
4162
4163 nCharOfs = wParam;
4164 /* detect which API version we're dealing with */
4165 if (wParam >= 0x40000)
4166 nCharOfs = lParam;
4167 nLength = ME_GetTextLength(editor);
4168 nCharOfs = min(nCharOfs, nLength);
4169 nCharOfs = max(nCharOfs, 0);
4170
4171 ME_RunOfsFromCharOfs(editor, nCharOfs, &pPara, &pRun, &nOffset);
4172 assert(pRun->type == diRun);
4173 pt.y = pRun->member.run.pt.y;
4174 pt.x = pRun->member.run.pt.x + ME_PointFromChar(editor, &pRun->member.run, nOffset, TRUE);
4175 pt.y += pPara->member.para.pt.y + editor->rcFormat.top;
4176 pt.x += editor->rcFormat.left;
4177
4178 pt.x -= editor->horz_si.nPos;
4179 pt.y -= editor->vert_si.nPos;
4180
4181 if (wParam >= 0x40000) {
4182 *(POINTL *)wParam = pt;
4183 }
4184 return (wParam >= 0x40000) ? 0 : MAKELONG( pt.x, pt.y );
4185 }
4186 case WM_CREATE:
4187 return ME_WmCreate(editor, lParam, unicode);
4188 case WM_DESTROY:
4189 ME_DestroyEditor(editor);
4190 return 0;
4191 case WM_SETCURSOR:
4192 {
4193 POINT cursor_pos;
4194 if (wParam == (WPARAM)editor->hWnd && GetCursorPos(&cursor_pos) &&
4195 ScreenToClient(editor->hWnd, &cursor_pos))
4196 ME_LinkNotify(editor, msg, 0, MAKELPARAM(cursor_pos.x, cursor_pos.y));
4197 return ME_SetCursor(editor);
4198 }
4199 case WM_LBUTTONDBLCLK:
4200 case WM_LBUTTONDOWN:
4201 {
4202 ME_CommitUndo(editor); /* End coalesced undos for typed characters */
4203 if ((editor->nEventMask & ENM_MOUSEEVENTS) &&
4204 !ME_FilterEvent(editor, msg, &wParam, &lParam))
4205 return 0;
4206 ITextHost_TxSetFocus(editor->texthost);
4207 ME_LButtonDown(editor, (short)LOWORD(lParam), (short)HIWORD(lParam),
4208 ME_CalculateClickCount(editor, msg, wParam, lParam));
4209 ITextHost_TxSetCapture(editor->texthost, TRUE);
4210 editor->bMouseCaptured = TRUE;
4211 ME_LinkNotify(editor, msg, wParam, lParam);
4212 if (!ME_SetCursor(editor)) goto do_default;
4213 break;
4214 }
4215 case WM_MOUSEMOVE:
4216 if ((editor->nEventMask & ENM_MOUSEEVENTS) &&
4217 !ME_FilterEvent(editor, msg, &wParam, &lParam))
4218 return 0;
4219 if (editor->bMouseCaptured)
4220 ME_MouseMove(editor, (short)LOWORD(lParam), (short)HIWORD(lParam));
4221 else
4222 ME_LinkNotify(editor, msg, wParam, lParam);
4223 /* Set cursor if mouse is captured, since WM_SETCURSOR won't be received. */
4224 if (editor->bMouseCaptured)
4225 ME_SetCursor(editor);
4226 break;
4227 case WM_LBUTTONUP:
4228 if (editor->bMouseCaptured) {
4229 ITextHost_TxSetCapture(editor->texthost, FALSE);
4230 editor->bMouseCaptured = FALSE;
4231 }
4232 if (editor->nSelectionType == stDocument)
4233 editor->nSelectionType = stPosition;
4234 if ((editor->nEventMask & ENM_MOUSEEVENTS) &&
4235 !ME_FilterEvent(editor, msg, &wParam, &lParam))
4236 return 0;
4237 else
4238 {
4239 ME_SetCursor(editor);
4240 ME_LinkNotify(editor, msg, wParam, lParam);
4241 }
4242 break;
4243 case WM_RBUTTONUP:
4244 case WM_RBUTTONDOWN:
4245 case WM_RBUTTONDBLCLK:
4246 ME_CommitUndo(editor); /* End coalesced undos for typed characters */
4247 if ((editor->nEventMask & ENM_MOUSEEVENTS) &&
4248 !ME_FilterEvent(editor, msg, &wParam, &lParam))
4249 return 0;
4250 ME_LinkNotify(editor, msg, wParam, lParam);
4251 goto do_default;
4252 case WM_CONTEXTMENU:
4253 if (!ME_ShowContextMenu(editor, (short)LOWORD(lParam), (short)HIWORD(lParam)))
4254 goto do_default;
4255 break;
4256 case WM_SETFOCUS:
4257 editor->bHaveFocus = TRUE;
4258 ME_ShowCaret(editor);
4259 ME_SendOldNotify(editor, EN_SETFOCUS);
4260 return 0;
4261 case WM_KILLFOCUS:
4262 ME_CommitUndo(editor); /* End coalesced undos for typed characters */
4263 editor->bHaveFocus = FALSE;
4264 editor->wheel_remain = 0;
4265 ME_HideCaret(editor);
4266 ME_SendOldNotify(editor, EN_KILLFOCUS);
4267 return 0;
4268 case WM_COMMAND:
4269 TRACE("editor wnd command = %d\n", LOWORD(wParam));
4270 return 0;
4271 case WM_KEYUP:
4272 if ((editor->nEventMask & ENM_KEYEVENTS) &&
4273 !ME_FilterEvent(editor, msg, &wParam, &lParam))
4274 return 0;
4275 goto do_default;
4276 case WM_KEYDOWN:
4277 if ((editor->nEventMask & ENM_KEYEVENTS) &&
4278 !ME_FilterEvent(editor, msg, &wParam, &lParam))
4279 return 0;
4280 if (ME_KeyDown(editor, LOWORD(wParam)))
4281 return 0;
4282 goto do_default;
4283 case WM_CHAR:
4284 if ((editor->nEventMask & ENM_KEYEVENTS) &&
4285 !ME_FilterEvent(editor, msg, &wParam, &lParam))
4286 return 0;
4287 return ME_Char(editor, wParam, lParam, unicode);
4288 case WM_UNICHAR:
4289 if (unicode)
4290 {
4291 if(wParam == UNICODE_NOCHAR) return TRUE;
4292 if(wParam <= 0x000fffff)
4293 {
4294 if(wParam > 0xffff) /* convert to surrogates */
4295 {
4296 wParam -= 0x10000;
4297 ME_Char(editor, (wParam >> 10) + 0xd800, 0, TRUE);
4298 ME_Char(editor, (wParam & 0x03ff) + 0xdc00, 0, TRUE);
4299 } else {
4300 ME_Char(editor, wParam, 0, TRUE);
4301 }
4302 }
4303 return 0;
4304 }
4305 break;
4306 case EM_STOPGROUPTYPING:
4307 ME_CommitUndo(editor); /* End coalesced undos for typed characters */
4308 return 0;
4309 case WM_HSCROLL:
4310 {
4311 const int scrollUnit = 7;
4312
4313 switch(LOWORD(wParam))
4314 {
4315 case SB_LEFT:
4316 ME_ScrollAbs(editor, 0, 0);
4317 break;
4318 case SB_RIGHT:
4319 ME_ScrollAbs(editor,
4320 editor->horz_si.nMax - (int)editor->horz_si.nPage,
4321 editor->vert_si.nMax - (int)editor->vert_si.nPage);
4322 break;
4323 case SB_LINELEFT:
4324 ME_ScrollLeft(editor, scrollUnit);
4325 break;
4326 case SB_LINERIGHT:
4327 ME_ScrollRight(editor, scrollUnit);
4328 break;
4329 case SB_PAGELEFT:
4330 ME_ScrollLeft(editor, editor->sizeWindow.cx);
4331 break;
4332 case SB_PAGERIGHT:
4333 ME_ScrollRight(editor, editor->sizeWindow.cx);
4334 break;
4335 case SB_THUMBTRACK:
4336 case SB_THUMBPOSITION:
4337 {
4338 int pos = HIWORD(wParam);
4339 if (editor->horz_si.nMax > 0xffff)
4340 pos = MulDiv(pos, editor->horz_si.nMax, 0xffff);
4341 ME_HScrollAbs(editor, pos);
4342 break;
4343 }
4344 }
4345 break;
4346 }
4347 case EM_SCROLL: /* fall through */
4348 case WM_VSCROLL:
4349 {
4350 int origNPos;
4351 int lineHeight = get_default_line_height( editor );
4352
4353 origNPos = editor->vert_si.nPos;
4354
4355 switch(LOWORD(wParam))
4356 {
4357 case SB_TOP:
4358 ME_ScrollAbs(editor, 0, 0);
4359 break;
4360 case SB_BOTTOM:
4361 ME_ScrollAbs(editor,
4362 editor->horz_si.nMax - (int)editor->horz_si.nPage,
4363 editor->vert_si.nMax - (int)editor->vert_si.nPage);
4364 break;
4365 case SB_LINEUP:
4366 ME_ScrollUp(editor,lineHeight);
4367 break;
4368 case SB_LINEDOWN:
4369 ME_ScrollDown(editor,lineHeight);
4370 break;
4371 case SB_PAGEUP:
4372 ME_ScrollUp(editor,editor->sizeWindow.cy);
4373 break;
4374 case SB_PAGEDOWN:
4375 ME_ScrollDown(editor,editor->sizeWindow.cy);
4376 break;
4377 case SB_THUMBTRACK:
4378 case SB_THUMBPOSITION:
4379 {
4380 int pos = HIWORD(wParam);
4381 if (editor->vert_si.nMax > 0xffff)
4382 pos = MulDiv(pos, editor->vert_si.nMax, 0xffff);
4383 ME_VScrollAbs(editor, pos);
4384 break;
4385 }
4386 }
4387 if (msg == EM_SCROLL)
4388 return 0x00010000 | (((editor->vert_si.nPos - origNPos)/lineHeight) & 0xffff);
4389 break;
4390 }
4391 case WM_MOUSEWHEEL:
4392 {
4393 int delta;
4394 BOOL ctrl_is_down;
4395
4396 if ((editor->nEventMask & ENM_MOUSEEVENTS) &&
4397 !ME_FilterEvent(editor, msg, &wParam, &lParam))
4398 return 0;
4399
4400 ctrl_is_down = GetKeyState(VK_CONTROL) & 0x8000;
4401
4402 delta = GET_WHEEL_DELTA_WPARAM(wParam);
4403
4404 /* if scrolling changes direction, ignore left overs */
4405 if ((delta < 0 && editor->wheel_remain < 0) ||
4406 (delta > 0 && editor->wheel_remain > 0))
4407 editor->wheel_remain += delta;
4408 else
4409 editor->wheel_remain = delta;
4410
4411 if (editor->wheel_remain)
4412 {
4413 if (ctrl_is_down) {
4414 int numerator;
4415 if (!editor->nZoomNumerator || !editor->nZoomDenominator)
4416 {
4417 numerator = 100;
4418 } else {
4419 numerator = editor->nZoomNumerator * 100 / editor->nZoomDenominator;
4420 }
4421 numerator += calc_wheel_change( &editor->wheel_remain, 10 );
4422 if (numerator >= 10 && numerator <= 500)
4423 ME_SetZoom(editor, numerator, 100);
4424 } else {
4425 UINT max_lines = 3;
4426 int lines = 0;
4427
4428 SystemParametersInfoW( SPI_GETWHEELSCROLLLINES, 0, &max_lines, 0 );
4429 if (max_lines)
4430 lines = calc_wheel_change( &editor->wheel_remain, (int)max_lines );
4431 if (lines)
4432 ME_ScrollDown( editor, -lines * get_default_line_height( editor ) );
4433 }
4434 }
4435 break;
4436 }
4437 case EM_GETRECT:
4438 {
4439 *((RECT *)lParam) = editor->rcFormat;
4440 if (editor->bDefaultFormatRect)
4441 ((RECT *)lParam)->left -= editor->selofs;
4442 return 0;
4443 }
4444 case EM_SETRECT:
4445 case EM_SETRECTNP:
4446 {
4447 if (lParam)
4448 {
4449 int border = 0;
4450 RECT clientRect;
4451 RECT *rc = (RECT *)lParam;
4452
4453 border = editor->exStyleFlags & WS_EX_CLIENTEDGE ? 1 : 0;
4454 ITextHost_TxGetClientRect(editor->texthost, &clientRect);
4455 if (wParam == 0)
4456 {
4457 editor->rcFormat.top = max(0, rc->top - border);
4458 editor->rcFormat.left = max(0, rc->left - border);
4459 editor->rcFormat.bottom = min(clientRect.bottom, rc->bottom);
4460 editor->rcFormat.right = min(clientRect.right, rc->right + border);
4461 } else if (wParam == 1) {
4462 /* MSDN incorrectly says a wParam value of 1 causes the
4463 * lParam rect to be used as a relative offset,
4464 * however, the tests show it just prevents min/max bound
4465 * checking. */
4466 editor->rcFormat.top = rc->top - border;
4467 editor->rcFormat.left = rc->left - border;
4468 editor->rcFormat.bottom = rc->bottom;
4469 editor->rcFormat.right = rc->right + border;
4470 } else {
4471 return 0;
4472 }
4473 editor->bDefaultFormatRect = FALSE;
4474 }
4475 else
4476 {
4477 ME_SetDefaultFormatRect(editor);
4478 editor->bDefaultFormatRect = TRUE;
4479 }
4480 ME_MarkAllForWrapping(editor);
4481 ME_WrapMarkedParagraphs(editor);
4482 ME_UpdateScrollBar(editor);
4483 if (msg != EM_SETRECTNP)
4484 ME_Repaint(editor);
4485 return 0;
4486 }
4487 case EM_REQUESTRESIZE:
4488 ME_SendRequestResize(editor, TRUE);
4489 return 0;
4490 case WM_SETREDRAW:
4491 goto do_default;
4492 case WM_WINDOWPOSCHANGED:
4493 {
4494 RECT clientRect;
4495 WINDOWPOS *winpos = (WINDOWPOS *)lParam;
4496
4497 if (winpos->flags & SWP_NOCLIENTSIZE) goto do_default;
4498 ITextHost_TxGetClientRect(editor->texthost, &clientRect);
4499 if (editor->bDefaultFormatRect) {
4500 ME_SetDefaultFormatRect(editor);
4501 } else {
4502 editor->rcFormat.right += clientRect.right - editor->prevClientRect.right;
4503 editor->rcFormat.bottom += clientRect.bottom - editor->prevClientRect.bottom;
4504 }
4505 editor->prevClientRect = clientRect;
4506 ME_RewrapRepaint(editor);
4507 goto do_default;
4508 }
4509 /* IME messages to make richedit controls IME aware */
4510 case WM_IME_SETCONTEXT:
4511 case WM_IME_CONTROL:
4512 case WM_IME_SELECT:
4513 case WM_IME_COMPOSITIONFULL:
4514 return 0;
4515 case WM_IME_STARTCOMPOSITION:
4516 {
4517 editor->imeStartIndex=ME_GetCursorOfs(&editor->pCursors[0]);
4518 ME_DeleteSelection(editor);
4519 ME_CommitUndo(editor);
4520 ME_UpdateRepaint(editor, FALSE);
4521 return 0;
4522 }
4523 case WM_IME_COMPOSITION:
4524 {
4525 HIMC hIMC;
4526
4527 ME_Style *style = ME_GetInsertStyle(editor, 0);
4528 hIMC = ITextHost_TxImmGetContext(editor->texthost);
4529 ME_DeleteSelection(editor);
4530 ME_SaveTempStyle(editor);
4531 if (lParam & (GCS_RESULTSTR|GCS_COMPSTR))
4532 {
4533 LPWSTR lpCompStr = NULL;
4534 DWORD dwBufLen;
4535 DWORD dwIndex = lParam & GCS_RESULTSTR;
4536 if (!dwIndex)
4537 dwIndex = GCS_COMPSTR;
4538
4539 dwBufLen = ImmGetCompositionStringW(hIMC, dwIndex, NULL, 0);
4540 lpCompStr = HeapAlloc(GetProcessHeap(),0,dwBufLen + sizeof(WCHAR));
4541 ImmGetCompositionStringW(hIMC, dwIndex, lpCompStr, dwBufLen);
4542 lpCompStr[dwBufLen/sizeof(WCHAR)] = 0;
4543 ME_InsertTextFromCursor(editor,0,lpCompStr,dwBufLen/sizeof(WCHAR),style);
4544 HeapFree(GetProcessHeap(), 0, lpCompStr);
4545
4546 if (dwIndex == GCS_COMPSTR)
4547 ME_SetSelection(editor,editor->imeStartIndex,
4548 editor->imeStartIndex + dwBufLen/sizeof(WCHAR));
4549 }
4550 ME_ReleaseStyle(style);
4551 ME_CommitUndo(editor);
4552 ME_UpdateRepaint(editor, FALSE);
4553 return 0;
4554 }
4555 case WM_IME_ENDCOMPOSITION:
4556 {
4557 ME_DeleteSelection(editor);
4558 editor->imeStartIndex=-1;
4559 return 0;
4560 }
4561 case EM_GETOLEINTERFACE:
4562 {
4563 if (!editor->reOle)
4564 if (!CreateIRichEditOle(NULL, editor, (LPVOID *)&editor->reOle))
4565 return 0;
4566 *(LPVOID *)lParam = editor->reOle;
4567 IRichEditOle_AddRef(editor->reOle);
4568 return 1;
4569 }
4570 case EM_GETPASSWORDCHAR:
4571 {
4572 return editor->cPasswordMask;
4573 }
4574 case EM_SETOLECALLBACK:
4575 if(editor->lpOleCallback)
4576 IRichEditOleCallback_Release(editor->lpOleCallback);
4577 editor->lpOleCallback = (IRichEditOleCallback*)lParam;
4578 if(editor->lpOleCallback)
4579 IRichEditOleCallback_AddRef(editor->lpOleCallback);
4580 return TRUE;
4581 case EM_GETWORDBREAKPROC:
4582 return (LRESULT)editor->pfnWordBreak;
4583 case EM_SETWORDBREAKPROC:
4584 {
4585 EDITWORDBREAKPROCW pfnOld = editor->pfnWordBreak;
4586
4587 editor->pfnWordBreak = (EDITWORDBREAKPROCW)lParam;
4588 return (LRESULT)pfnOld;
4589 }
4590 case EM_GETTEXTMODE:
4591 return editor->mode;
4592 case EM_SETTEXTMODE:
4593 {
4594 int mask = 0;
4595 int changes = 0;
4596
4597 if (ME_GetTextLength(editor) ||
4598 !list_empty( &editor->undo_stack ) || !list_empty( &editor->redo_stack ))
4599 return E_UNEXPECTED;
4600
4601 /* Check for mutually exclusive flags in adjacent bits of wParam */
4602 if ((wParam & (TM_RICHTEXT | TM_MULTILEVELUNDO | TM_MULTICODEPAGE)) &
4603 (wParam & (TM_PLAINTEXT | TM_SINGLELEVELUNDO | TM_SINGLECODEPAGE)) << 1)
4604 return E_INVALIDARG;
4605
4606 if (wParam & (TM_RICHTEXT | TM_PLAINTEXT))
4607 {
4608 mask |= TM_RICHTEXT | TM_PLAINTEXT;
4609 changes |= wParam & (TM_RICHTEXT | TM_PLAINTEXT);
4610 if (wParam & TM_PLAINTEXT) {
4611 /* Clear selection since it should be possible to select the
4612 * end of text run for rich text */
4613 ME_InvalidateSelection(editor);
4614 ME_SetCursorToStart(editor, &editor->pCursors[0]);
4615 editor->pCursors[1] = editor->pCursors[0];
4616 /* plain text can only have the default style. */
4617 ME_ClearTempStyle(editor);
4618 ME_AddRefStyle(editor->pBuffer->pDefaultStyle);
4619 ME_ReleaseStyle(editor->pCursors[0].pRun->member.run.style);
4620 editor->pCursors[0].pRun->member.run.style = editor->pBuffer->pDefaultStyle;
4621 }
4622 }
4623 /* FIXME: Currently no support for undo level and code page options */
4624 editor->mode = (editor->mode & ~mask) | changes;
4625 return 0;
4626 }
4627 case EM_SETPASSWORDCHAR:
4628 {
4629 editor->cPasswordMask = wParam;
4630 ME_RewrapRepaint(editor);
4631 return 0;
4632 }
4633 case EM_SETTARGETDEVICE:
4634 if (wParam == 0)
4635 {
4636 BOOL new = (lParam == 0 && (editor->styleFlags & ES_MULTILINE));
4637 if (editor->nAvailWidth || editor->bWordWrap != new)
4638 {
4639 editor->bWordWrap = new;
4640 editor->nAvailWidth = 0; /* wrap to client area */
4641 ME_RewrapRepaint(editor);
4642 }
4643 } else {
4644 int width = max(0, lParam);
4645 if ((editor->styleFlags & ES_MULTILINE) &&
4646 (!editor->bWordWrap || editor->nAvailWidth != width))
4647 {
4648 editor->nAvailWidth = width;
4649 editor->bWordWrap = TRUE;
4650 ME_RewrapRepaint(editor);
4651 }
4652 FIXME("EM_SETTARGETDEVICE doesn't use non-NULL target devices\n");
4653 }
4654 return TRUE;
4655 default:
4656 do_default:
4657 *phresult = S_FALSE;
4658 break;
4659 }
4660 return 0L;
4661 }
4662
4663 static LRESULT RichEditWndProc_common(HWND hWnd, UINT msg, WPARAM wParam,
4664 LPARAM lParam, BOOL unicode)
4665 {
4666 ME_TextEditor *editor;
4667 HRESULT hresult;
4668 LRESULT lresult = 0;
4669
4670 TRACE("enter hwnd %p msg %04x (%s) %lx %lx, unicode %d\n",
4671 hWnd, msg, get_msg_name(msg), wParam, lParam, unicode);
4672
4673 editor = (ME_TextEditor *)GetWindowLongPtrW(hWnd, 0);
4674 if (!editor)
4675 {
4676 if (msg == WM_NCCREATE)
4677 {
4678 CREATESTRUCTW *pcs = (CREATESTRUCTW *)lParam;
4679 ITextHost *texthost;
4680
4681 TRACE("WM_NCCREATE: hWnd %p style 0x%08x\n", hWnd, pcs->style);
4682 texthost = ME_CreateTextHost(hWnd, pcs, FALSE);
4683 return texthost != NULL;
4684 }
4685 else
4686 {
4687 return DefWindowProcW(hWnd, msg, wParam, lParam);
4688 }
4689 }
4690
4691 switch (msg)
4692 {
4693 case WM_PAINT:
4694 {
4695 HDC hDC;
4696 RECT rc;
4697 PAINTSTRUCT ps;
4698
4699 hDC = BeginPaint(editor->hWnd, &ps);
4700 if (!editor->bEmulateVersion10 || (editor->nEventMask & ENM_UPDATE))
4701 ME_SendOldNotify(editor, EN_UPDATE);
4702 /* Erase area outside of the formatting rectangle */
4703 if (ps.rcPaint.top < editor->rcFormat.top)
4704 {
4705 rc = ps.rcPaint;
4706 rc.bottom = editor->rcFormat.top;
4707 FillRect(hDC, &rc, editor->hbrBackground);
4708 ps.rcPaint.top = editor->rcFormat.top;
4709 }
4710 if (ps.rcPaint.bottom > editor->rcFormat.bottom) {
4711 rc = ps.rcPaint;
4712 rc.top = editor->rcFormat.bottom;
4713 FillRect(hDC, &rc, editor->hbrBackground);
4714 ps.rcPaint.bottom = editor->rcFormat.bottom;
4715 }
4716 if (ps.rcPaint.left < editor->rcFormat.left) {
4717 rc = ps.rcPaint;
4718 rc.right = editor->rcFormat.left;
4719 FillRect(hDC, &rc, editor->hbrBackground);
4720 ps.rcPaint.left = editor->rcFormat.left;
4721 }
4722 if (ps.rcPaint.right > editor->rcFormat.right) {
4723 rc = ps.rcPaint;
4724 rc.left = editor->rcFormat.right;
4725 FillRect(hDC, &rc, editor->hbrBackground);
4726 ps.rcPaint.right = editor->rcFormat.right;
4727 }
4728
4729 ME_PaintContent(editor, hDC, &ps.rcPaint);
4730 EndPaint(editor->hWnd, &ps);
4731 return 0;
4732 }
4733 case WM_ERASEBKGND:
4734 {
4735 HDC hDC = (HDC)wParam;
4736 RECT rc;
4737
4738 if (GetUpdateRect(editor->hWnd, &rc, TRUE))
4739 FillRect(hDC, &rc, editor->hbrBackground);
4740 return 1;
4741 }
4742 case EM_SETOPTIONS:
4743 {
4744 DWORD dwStyle;
4745 const DWORD mask = ECO_VERTICAL | ECO_AUTOHSCROLL | ECO_AUTOVSCROLL |
4746 ECO_NOHIDESEL | ECO_READONLY | ECO_WANTRETURN |
4747 ECO_SELECTIONBAR;
4748 lresult = ME_HandleMessage(editor, msg, wParam, lParam, unicode, &hresult);
4749 dwStyle = GetWindowLongW(hWnd, GWL_STYLE);
4750 dwStyle = (dwStyle & ~mask) | (lresult & mask);
4751 SetWindowLongW(hWnd, GWL_STYLE, dwStyle);
4752 return lresult;
4753 }
4754 case EM_SETREADONLY:
4755 {
4756 DWORD dwStyle;
4757 lresult = ME_HandleMessage(editor, msg, wParam, lParam, unicode, &hresult);
4758 dwStyle = GetWindowLongW(hWnd, GWL_STYLE);
4759 dwStyle &= ~ES_READONLY;
4760 if (wParam)
4761 dwStyle |= ES_READONLY;
4762 SetWindowLongW(hWnd, GWL_STYLE, dwStyle);
4763 return lresult;
4764 }
4765 default:
4766 lresult = ME_HandleMessage(editor, msg, wParam, lParam, unicode, &hresult);
4767 }
4768
4769 if (hresult == S_FALSE)
4770 lresult = DefWindowProcW(hWnd, msg, wParam, lParam);
4771
4772 TRACE("exit hwnd %p msg %04x (%s) %lx %lx, unicode %d -> %lu\n",
4773 hWnd, msg, get_msg_name(msg), wParam, lParam, unicode, lresult);
4774
4775 return lresult;
4776 }
4777
4778 static LRESULT WINAPI RichEditWndProcW(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
4779 {
4780 BOOL unicode = TRUE;
4781
4782 /* Under Win9x RichEdit20W returns ANSI strings, see the tests. */
4783 if (msg == WM_GETTEXT && (GetVersion() & 0x80000000))
4784 unicode = FALSE;
4785
4786 return RichEditWndProc_common(hWnd, msg, wParam, lParam, unicode);
4787 }
4788
4789 static LRESULT WINAPI RichEditWndProcA(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
4790 {
4791 return RichEditWndProc_common(hWnd, msg, wParam, lParam, FALSE);
4792 }
4793
4794 /******************************************************************
4795 * RichEditANSIWndProc (RICHED20.10)
4796 */
4797 LRESULT WINAPI RichEditANSIWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
4798 {
4799 return RichEditWndProcA(hWnd, msg, wParam, lParam);
4800 }
4801
4802 /******************************************************************
4803 * RichEdit10ANSIWndProc (RICHED20.9)
4804 */
4805 LRESULT WINAPI RichEdit10ANSIWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
4806 {
4807 if (msg == WM_NCCREATE && !GetWindowLongPtrW(hWnd, 0))
4808 {
4809 ITextHost *texthost;
4810 CREATESTRUCTW *pcs = (CREATESTRUCTW *)lParam;
4811
4812 TRACE("WM_NCCREATE: hWnd %p style 0x%08x\n", hWnd, pcs->style);
4813 texthost = ME_CreateTextHost(hWnd, pcs, TRUE);
4814 return texthost != NULL;
4815 }
4816 return RichEditANSIWndProc(hWnd, msg, wParam, lParam);
4817 }
4818
4819 void ME_SendOldNotify(ME_TextEditor *editor, int nCode)
4820 {
4821 ITextHost_TxNotify(editor->texthost, nCode, NULL);
4822 }
4823
4824 /* Fill buffer with srcChars unicode characters from the start cursor.
4825 *
4826 * buffer: destination buffer
4827 * buflen: length of buffer in characters excluding the NULL terminator.
4828 * start: start of editor text to copy into buffer.
4829 * srcChars: Number of characters to use from the editor text.
4830 * bCRLF: if true, replaces all end of lines with \r\n pairs.
4831 *
4832 * returns the number of characters written excluding the NULL terminator.
4833 *
4834 * The written text is always NULL terminated.
4835 */
4836 int ME_GetTextW(ME_TextEditor *editor, WCHAR *buffer, int buflen,
4837 const ME_Cursor *start, int srcChars, BOOL bCRLF,
4838 BOOL bEOP)
4839 {
4840 ME_DisplayItem *pRun, *pNextRun;
4841 const WCHAR *pStart = buffer;
4842 const WCHAR cr_lf[] = {'\r', '\n', 0};
4843 const WCHAR *str;
4844 int nLen;
4845
4846 /* bCRLF flag is only honored in 2.0 and up. 1.0 must always return text verbatim */
4847 if (editor->bEmulateVersion10) bCRLF = FALSE;
4848
4849 pRun = start->pRun;
4850 assert(pRun);
4851 pNextRun = ME_FindItemFwd(pRun, diRun);
4852
4853 nLen = pRun->member.run.len - start->nOffset;
4854 str = get_text( &pRun->member.run, start->nOffset );
4855
4856 while (srcChars && buflen && pNextRun)
4857 {
4858 int nFlags = pRun->member.run.nFlags;
4859
4860 if (bCRLF && nFlags & MERF_ENDPARA && ~nFlags & MERF_ENDCELL)
4861 {
4862 if (buflen == 1) break;
4863 /* FIXME: native fails to reduce srcChars here for WM_GETTEXT or
4864 * EM_GETTEXTEX, however, this is done for copying text which
4865 * also uses this function. */
4866 srcChars -= min(nLen, srcChars);
4867 nLen = 2;
4868 str = cr_lf;
4869 } else {
4870 nLen = min(nLen, srcChars);
4871 srcChars -= nLen;
4872 }
4873
4874 nLen = min(nLen, buflen);
4875 buflen -= nLen;
4876
4877 CopyMemory(buffer, str, sizeof(WCHAR) * nLen);
4878
4879 buffer += nLen;
4880
4881 pRun = pNextRun;
4882 pNextRun = ME_FindItemFwd(pRun, diRun);
4883
4884 nLen = pRun->member.run.len;
4885 str = get_text( &pRun->member.run, 0 );
4886 }
4887 /* append '\r' to the last paragraph. */
4888 if (pRun->next->type == diTextEnd && bEOP)
4889 {
4890 *buffer = '\r';
4891 buffer ++;
4892 }
4893 *buffer = 0;
4894 return buffer - pStart;
4895 }
4896
4897 static BOOL ME_RegisterEditorClass(HINSTANCE hInstance)
4898 {
4899 WNDCLASSW wcW;
4900 WNDCLASSA wcA;
4901
4902 wcW.style = CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW | CS_GLOBALCLASS;
4903 wcW.lpfnWndProc = RichEditWndProcW;
4904 wcW.cbClsExtra = 0;
4905 wcW.cbWndExtra = sizeof(ME_TextEditor *);
4906 wcW.hInstance = NULL; /* hInstance would register DLL-local class */
4907 wcW.hIcon = NULL;
4908 wcW.hCursor = LoadCursorW(NULL, (LPWSTR)IDC_IBEAM);
4909 wcW.hbrBackground = GetStockObject(NULL_BRUSH);
4910 wcW.lpszMenuName = NULL;
4911
4912 if (is_version_nt())
4913 {
4914 wcW.lpszClassName = RICHEDIT_CLASS20W;
4915 if (!RegisterClassW(&wcW)) return FALSE;
4916 wcW.lpszClassName = MSFTEDIT_CLASS;
4917 if (!RegisterClassW(&wcW)) return FALSE;
4918 }
4919 else
4920 {
4921 /* WNDCLASSA/W have the same layout */
4922 wcW.lpszClassName = (LPCWSTR)"RichEdit20W";
4923 if (!RegisterClassA((WNDCLASSA *)&wcW)) return FALSE;
4924 wcW.lpszClassName = (LPCWSTR)"RichEdit50W";
4925 if (!RegisterClassA((WNDCLASSA *)&wcW)) return FALSE;
4926 }
4927
4928 wcA.style = CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW | CS_GLOBALCLASS;
4929 wcA.lpfnWndProc = RichEditWndProcA;
4930 wcA.cbClsExtra = 0;
4931 wcA.cbWndExtra = sizeof(ME_TextEditor *);
4932 wcA.hInstance = NULL; /* hInstance would register DLL-local class */
4933 wcA.hIcon = NULL;
4934 wcA.hCursor = LoadCursorW(NULL, (LPWSTR)IDC_IBEAM);
4935 wcA.hbrBackground = GetStockObject(NULL_BRUSH);
4936 wcA.lpszMenuName = NULL;
4937 wcA.lpszClassName = RICHEDIT_CLASS20A;
4938 if (!RegisterClassA(&wcA)) return FALSE;
4939 wcA.lpszClassName = "RichEdit50A";
4940 if (!RegisterClassA(&wcA)) return FALSE;
4941
4942 return TRUE;
4943 }
4944
4945 static LRESULT WINAPI REComboWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) {
4946 /* FIXME: Not implemented */
4947 TRACE("hWnd %p msg %04x (%s) %08lx %08lx\n",
4948 hWnd, msg, get_msg_name(msg), wParam, lParam);
4949 return DefWindowProcW(hWnd, msg, wParam, lParam);
4950 }
4951
4952 static LRESULT WINAPI REListWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) {
4953 /* FIXME: Not implemented */
4954 TRACE("hWnd %p msg %04x (%s) %08lx %08lx\n",
4955 hWnd, msg, get_msg_name(msg), wParam, lParam);
4956 return DefWindowProcW(hWnd, msg, wParam, lParam);
4957 }
4958
4959 /******************************************************************
4960 * REExtendedRegisterClass (RICHED20.8)
4961 *
4962 * FIXME undocumented
4963 * Need to check for errors and implement controls and callbacks
4964 */
4965 LRESULT WINAPI REExtendedRegisterClass(void)
4966 {
4967 WNDCLASSW wcW;
4968 UINT result;
4969
4970 FIXME("semi stub\n");
4971
4972 wcW.cbClsExtra = 0;
4973 wcW.cbWndExtra = 4;
4974 wcW.hInstance = NULL;
4975 wcW.hIcon = NULL;
4976 wcW.hCursor = NULL;
4977 wcW.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
4978 wcW.lpszMenuName = NULL;
4979
4980 if (!ME_ListBoxRegistered)
4981 {
4982 wcW.style = CS_PARENTDC | CS_DBLCLKS | CS_GLOBALCLASS;
4983 wcW.lpfnWndProc = REListWndProc;
4984 wcW.lpszClassName = REListBox20W;
4985 if (RegisterClassW(&wcW)) ME_ListBoxRegistered = TRUE;
4986 }
4987
4988 if (!ME_ComboBoxRegistered)
4989 {
4990 wcW.style = CS_PARENTDC | CS_DBLCLKS | CS_GLOBALCLASS | CS_VREDRAW | CS_HREDRAW;
4991 wcW.lpfnWndProc = REComboWndProc;
4992 wcW.lpszClassName = REComboBox20W;
4993 if (RegisterClassW(&wcW)) ME_ComboBoxRegistered = TRUE;
4994 }
4995
4996 result = 0;
4997 if (ME_ListBoxRegistered)
4998 result += 1;
4999 if (ME_ComboBoxRegistered)
5000 result += 2;
5001
5002 return result;
5003 }
5004
5005 static BOOL isurlspecial(WCHAR c)
5006 {
5007 static const WCHAR special_chars[] = {'.','/','%','@','*','|','\\','+','#',0};
5008 return strchrW( special_chars, c ) != NULL;
5009 }
5010
5011 /**
5012 * This proc takes a selection, and scans it forward in order to select the span
5013 * of a possible URL candidate. A possible URL candidate must start with isalnum
5014 * or one of the following special characters: *|/\+%#@ and must consist entirely
5015 * of the characters allowed to start the URL, plus : (colon) which may occur
5016 * at most once, and not at either end.
5017 */
5018 static BOOL ME_FindNextURLCandidate(ME_TextEditor *editor,
5019 const ME_Cursor *start,
5020 int nChars,
5021 ME_Cursor *candidate_min,
5022 ME_Cursor *candidate_max)
5023 {
5024 ME_Cursor cursor = *start;
5025 BOOL foundColon = FALSE;
5026 BOOL candidateStarted = FALSE;
5027 WCHAR lastAcceptedChar = '\0';
5028
5029 while (nChars > 0)
5030 {
5031 WCHAR *strStart = get_text( &cursor.pRun->member.run, 0 );
5032 WCHAR *str = strStart + cursor.nOffset;
5033 int nLen = cursor.pRun->member.run.len - cursor.nOffset;
5034 nChars -= nLen;
5035
5036 if (~cursor.pRun->member.run.nFlags & MERF_ENDPARA)
5037 {
5038 /* Find start of candidate */
5039 if (!candidateStarted)
5040 {
5041 while (nLen)
5042 {
5043 nLen--;
5044 if (isalnumW(*str) || isurlspecial(*str))
5045 {
5046 cursor.nOffset = str - strStart;
5047 *candidate_min = cursor;
5048 candidateStarted = TRUE;
5049 lastAcceptedChar = *str++;
5050 break;
5051 }
5052 str++;
5053 }
5054 }
5055
5056 /* Find end of candidate */
5057 if (candidateStarted) {
5058 while (nLen)
5059 {
5060 nLen--;
5061 if (*str == ':' && !foundColon) {
5062 foundColon = TRUE;
5063 } else if (!isalnumW(*str) && !isurlspecial(*str)) {
5064 cursor.nOffset = str - strStart;
5065 if (lastAcceptedChar == ':')
5066 ME_MoveCursorChars(editor, &cursor, -1);
5067 *candidate_max = cursor;
5068 return TRUE;
5069 }
5070 lastAcceptedChar = *str++;
5071 }
5072 }
5073 } else {
5074 /* End of paragraph: skip it if before candidate span, or terminates
5075 current active span */
5076 if (candidateStarted) {
5077 if (lastAcceptedChar == ':')
5078 ME_MoveCursorChars(editor, &cursor, -1);
5079 *candidate_max = cursor;
5080 return TRUE;
5081 }
5082 }
5083
5084 /* Reaching this point means no span was found, so get next span */
5085 if (!ME_NextRun(&cursor.pPara, &cursor.pRun)) {
5086 if (candidateStarted) {
5087 /* There are no further runs, so take end of text as end of candidate */
5088 cursor.nOffset = str - strStart;
5089 if (lastAcceptedChar == ':')
5090 ME_MoveCursorChars(editor, &cursor, -1);
5091 *candidate_max = cursor;
5092 return TRUE;
5093 }
5094 *candidate_max = *candidate_min = cursor;
5095 return FALSE;
5096 }
5097 cursor.nOffset = 0;
5098 }
5099
5100 if (candidateStarted) {
5101 /* There are no further runs, so take end of text as end of candidate */
5102 if (lastAcceptedChar == ':')
5103 ME_MoveCursorChars(editor, &cursor, -1);
5104 *candidate_max = cursor;
5105 return TRUE;
5106 }
5107 *candidate_max = *candidate_min = cursor;
5108 return FALSE;
5109 }
5110
5111 /**
5112 * This proc evaluates the selection and returns TRUE if it can be considered an URL
5113 */
5114 static BOOL ME_IsCandidateAnURL(ME_TextEditor *editor, const ME_Cursor *start, int nChars)
5115 {
5116 #define MAX_PREFIX_LEN 9
5117 struct prefix_s {
5118 const WCHAR text[MAX_PREFIX_LEN];
5119 int length;
5120 }prefixes[] = {
5121 {{'p','r','o','s','p','e','r','o',':'}, 9},
5122 {{'t','e','l','n','e','t',':'}, 7},
5123 {{'g','o','p','h','e','r',':'}, 7},
5124 {{'m','a','i','l','t','o',':'}, 7},
5125 {{'h','t','t','p','s',':'}, 6},
5126 {{'f','i','l','e',':'}, 5},
5127 {{'n','e','w','s',':'}, 5},
5128 {{'w','a','i','s',':'}, 5},
5129 {{'n','n','t','p',':'}, 5},
5130 {{'h','t','t','p',':'}, 5},
5131 {{'w','w','w','.'}, 4},
5132 {{'f','t','p',':'}, 4},
5133 };
5134 WCHAR bufferW[MAX_PREFIX_LEN + 1];
5135 unsigned int i;
5136
5137 ME_GetTextW(editor, bufferW, MAX_PREFIX_LEN, start, nChars, FALSE, FALSE);
5138 for (i = 0; i < sizeof(prefixes) / sizeof(*prefixes); i++)
5139 {
5140 if (nChars < prefixes[i].length) continue;
5141 if (!memcmp(prefixes[i].text, bufferW, prefixes[i].length * sizeof(WCHAR)))
5142 return TRUE;
5143 }
5144 return FALSE;
5145 #undef MAX_PREFIX_LEN
5146 }
5147
5148 /**
5149 * This proc walks through the indicated selection and evaluates whether each
5150 * section identified by ME_FindNextURLCandidate and in-between sections have
5151 * their proper CFE_LINK attributes set or unset. If the CFE_LINK attribute is
5152 * not what it is supposed to be, this proc sets or unsets it as appropriate.
5153 *
5154 * Since this function can cause runs to be split, do not depend on the value
5155 * of the start cursor at the end of the function.
5156 *
5157 * nChars may be set to INT_MAX to update to the end of the text.
5158 *
5159 * Returns TRUE if at least one section was modified.
5160 */
5161 static BOOL ME_UpdateLinkAttribute(ME_TextEditor *editor, ME_Cursor *start, int nChars)
5162 {
5163 BOOL modified = FALSE;
5164 ME_Cursor startCur = *start;
5165
5166 if (!editor->AutoURLDetect_bEnable) return FALSE;
5167
5168 do
5169 {
5170 CHARFORMAT2W link;
5171 ME_Cursor candidateStart, candidateEnd;
5172
5173 if (ME_FindNextURLCandidate(editor, &startCur, nChars,
5174 &candidateStart, &candidateEnd))
5175 {
5176 /* Section before candidate is not an URL */
5177 int cMin = ME_GetCursorOfs(&candidateStart);
5178 int cMax = ME_GetCursorOfs(&candidateEnd);
5179
5180 if (!ME_IsCandidateAnURL(editor, &candidateStart, cMax - cMin))
5181 candidateStart = candidateEnd;
5182 nChars -= cMax - ME_GetCursorOfs(&startCur);
5183 }
5184 else
5185 {
5186 /* No more candidates until end of selection */
5187 nChars = 0;
5188 }
5189
5190 if (startCur.pRun != candidateStart.pRun ||
5191 startCur.nOffset != candidateStart.nOffset)
5192 {
5193 /* CFE_LINK effect should be consistently unset */
5194 link.cbSize = sizeof(link);
5195 ME_GetCharFormat(editor, &startCur, &candidateStart, &link);
5196 if (!(link.dwMask & CFM_LINK) || (link.dwEffects & CFE_LINK))
5197 {
5198 /* CFE_LINK must be unset from this range */
5199 memset(&link, 0, sizeof(CHARFORMAT2W));
5200 link.cbSize = sizeof(link);
5201 link.dwMask = CFM_LINK;
5202 link.dwEffects = 0;
5203 ME_SetCharFormat(editor, &startCur, &candidateStart, &link);
5204 /* Update candidateEnd since setting character formats may split
5205 * runs, which can cause a cursor to be at an invalid offset within
5206 * a split run. */
5207 while (candidateEnd.nOffset >= candidateEnd.pRun->member.run.len)
5208 {
5209 candidateEnd.nOffset -= candidateEnd.pRun->member.run.len;
5210 candidateEnd.pRun = ME_FindItemFwd(candidateEnd.pRun, diRun);
5211 }
5212 modified = TRUE;
5213 }
5214 }
5215 if (candidateStart.pRun != candidateEnd.pRun ||
5216 candidateStart.nOffset != candidateEnd.nOffset)
5217 {
5218 /* CFE_LINK effect should be consistently set */
5219 link.cbSize = sizeof(link);
5220 ME_GetCharFormat(editor, &candidateStart, &candidateEnd, &link);
5221 if (!(link.dwMask & CFM_LINK) || !(link.dwEffects & CFE_LINK))
5222 {
5223 /* CFE_LINK must be set on this range */
5224 memset(&link, 0, sizeof(CHARFORMAT2W));
5225 link.cbSize = sizeof(link);
5226 link.dwMask = CFM_LINK;
5227 link.dwEffects = CFE_LINK;
5228 ME_SetCharFormat(editor, &candidateStart, &candidateEnd, &link);
5229 modified = TRUE;
5230 }
5231 }
5232 startCur = candidateEnd;
5233 } while (nChars > 0);
5234 return modified;
5235 }