2 * RichEdit - functions dealing with editor object
4 * Copyright 2004 by Krzysztof Foltman
5 * Copyright 2005 by Cihan Altinay
6 * Copyright 2005 by Phil Krylov
7 * Copyright 2008 Eric Pouech
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.
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.
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
25 API implementation status:
27 Messages (ANSI versions not done yet)
28 + EM_AUTOURLDETECT 2.0
39 + EM_FINDTEXT (only FR_DOWN flag implemented)
40 + EM_FINDTEXTEX (only FR_DOWN flag implemented)
44 + EM_GETAUTOURLDETECT 2.0
45 - EM_GETBIDIOPTIONS 3.0
46 - EM_GETCHARFORMAT (partly done)
49 + EM_GETFIRSTVISIBLELINE (can be optimized if needed)
50 - EM_GETIMECOLOR 1.0asian
51 - EM_GETIMECOMPMODE 2.0
52 - EM_GETIMEOPTIONS 1.0asian
54 - EM_GETLANGOPTIONS 2.0
57 + EM_GETLINECOUNT returns number of rows, not of paragraphs
62 + EM_GETPASSWORDCHAR 2.0
63 - EM_GETPUNCTUATION 1.0asian
67 + EM_GETSELTEXT (ANSI&Unicode)
71 + EM_GETTEXTLENGTHEX (GTL_PRECISE unimplemented)
73 ? + EM_GETTEXTRANGE (ANSI&Unicode)
74 - EM_GETTYPOGRAPHYOPTIONS 3.0
77 - EM_GETWORDBREAKPROCEX
78 - EM_GETWORDWRAPMODE 1.0asian
81 + EM_LIMITTEXT (Also called EM_SETLIMITTEXT)
90 + EM_REPLACESEL (proper style?) ANSI&Unicode
94 - EM_SETBIDIOPTIONS 3.0
96 + EM_SETCHARFORMAT (partly done, no ANSI)
98 + EM_SETEVENTMASK (few notifications supported)
100 - EM_SETIMECOLOR 1.0asian
101 - EM_SETIMEOPTIONS 1.0asian
103 - EM_SETLANGOPTIONS 2.0
106 + EM_SETMODIFY (not sure if implementation is correct)
108 + EM_SETOPTIONS (partially implemented)
111 + EM_SETPASSWORDCHAR 2.0
112 - EM_SETPUNCTUATION 1.0asian
113 + EM_SETREADONLY no beep on modification attempt
115 + EM_SETRECTNP (EM_SETRECT without repainting)
117 + EM_SETSCROLLPOS 3.0
119 - EM_SETTARGETDEVICE (partial)
120 + EM_SETTEXTEX 3.0 (proper style?)
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
128 + EM_SHOWSCROLLBAR 2.0
129 + EM_STOPGROUPTYPING 2.0
137 + WM_GETDLGCODE (the current implementation is incomplete)
138 + WM_GETTEXT (ANSI&Unicode)
139 + WM_GETTEXTLENGTH (ANSI version sucks)
143 + WM_SETTEXT (resets undo stack !) (proper style?) ANSI&Unicode
144 + WM_STYLECHANGING (seems to do nothing)
145 + WM_STYLECHANGED (seems to do nothing)
151 * EN_CHANGE (sent from the wrong place)
168 * EN_UPDATE (sent from the wrong place)
176 + ES_DISABLENOSCROLL (scrollbar is always visible)
177 - ES_EX_NOCALLOLEINIT
179 - ES_MULTILINE (currently single line controls aren't supported)
181 - ES_READONLY (I'm not sure if beeping is the proper behaviour)
187 - ES_WANTRETURN (don't know how to do WM_GETDLGCODE part)
194 * RICHED20 TODO (incomplete):
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)
204 * - how to implement EM_FORMATRANGE and EM_DISPLAYBAND ? (Mission Impossible)
205 * - italic caret with italic fonts
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
214 * - ListBox & ComboBox not implemented
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?
227 #define NONAMELESSUNION
232 #define NO_SHLWAPI_STREAM
238 #define STACK_SIZE_DEFAULT 100
239 #define STACK_SIZE_MAX 1000
241 #define TEXT_LIMIT_DEFAULT 32767
243 WINE_DEFAULT_DEBUG_CHANNEL(richedit
);
245 static BOOL
ME_RegisterEditorClass(HINSTANCE
);
246 static BOOL
ME_UpdateLinkAttribute(ME_TextEditor
*editor
, ME_Cursor
*start
, int nChars
);
248 static const WCHAR REListBox20W
[] = {'R','E','L','i','s','t','B','o','x','2','0','W', 0};
249 static const WCHAR REComboBox20W
[] = {'R','E','C','o','m','b','o','B','o','x','2','0','W', 0};
250 static HCURSOR hLeft
;
252 BOOL me_debug
= FALSE
;
253 HANDLE me_heap
= NULL
;
255 static BOOL ME_ListBoxRegistered
= FALSE
;
256 static BOOL ME_ComboBoxRegistered
= FALSE
;
258 static inline BOOL
is_version_nt(void)
260 return !(GetVersion() & 0x80000000);
263 static ME_TextBuffer
*ME_MakeText(void) {
265 ME_TextBuffer
*buf
= ALLOC_OBJ(ME_TextBuffer
);
267 ME_DisplayItem
*p1
= ME_MakeDI(diTextStart
);
268 ME_DisplayItem
*p2
= ME_MakeDI(diTextEnd
);
274 p1
->member
.para
.next_para
= p2
;
275 p2
->member
.para
.prev_para
= p1
;
276 p2
->member
.para
.nCharOfs
= 0;
280 buf
->pCharStyle
= NULL
;
286 static LRESULT
ME_StreamInText(ME_TextEditor
*editor
, DWORD dwFormat
, ME_InStream
*stream
, ME_Style
*style
)
289 LRESULT total_bytes_read
= 0;
290 BOOL is_read
= FALSE
;
291 DWORD cp
= CP_ACP
, copy
= 0;
292 char conv_buf
[4 + STREAMIN_BUFFER_SIZE
]; /* up to 4 additional UTF-8 bytes */
294 static const char bom_utf8
[] = {0xEF, 0xBB, 0xBF};
296 TRACE("%08x %p\n", dwFormat
, stream
);
300 WCHAR wszText
[STREAMIN_BUFFER_SIZE
+1];
304 ME_StreamInFill(stream
);
305 if (stream
->editstream
->dwError
)
309 total_bytes_read
+= stream
->dwSize
;
312 if (!(dwFormat
& SF_UNICODE
))
314 char * buf
= stream
->buffer
;
315 DWORD size
= stream
->dwSize
, end
;
320 if (stream
->dwSize
>= 3 && !memcmp(stream
->buffer
, bom_utf8
, 3))
332 memcpy(conv_buf
+ copy
, buf
, size
);
337 while ((buf
[end
-1] & 0xC0) == 0x80)
340 --total_bytes_read
; /* strange, but seems to match windows */
342 if (buf
[end
-1] & 0x80)
345 if ((buf
[end
-1] & 0xE0) == 0xC0)
347 if ((buf
[end
-1] & 0xF0) == 0xE0)
349 if ((buf
[end
-1] & 0xF8) == 0xF0)
352 if (size
- end
>= need
)
354 /* we have enough bytes for this sequence */
359 /* need more bytes, so don't transcode this sequence */
367 nWideChars
= MultiByteToWideChar(cp
, 0, buf
, end
, wszText
, STREAMIN_BUFFER_SIZE
);
374 memcpy(conv_buf
, buf
+ end
, size
- end
);
381 nWideChars
= stream
->dwSize
>> 1;
382 pText
= (WCHAR
*)stream
->buffer
;
385 ME_InsertTextFromCursor(editor
, 0, pText
, nWideChars
, style
);
386 if (stream
->dwSize
== 0)
390 return total_bytes_read
;
393 static void ME_ApplyBorderProperties(RTF_Info
*info
,
394 ME_BorderRect
*borderRect
,
395 RTFBorder
*borderDef
)
398 ME_Border
*pBorders
[] = {&borderRect
->top
,
402 for (i
= 0; i
< 4; i
++)
404 RTFColor
*colorDef
= info
->colorList
;
405 pBorders
[i
]->width
= borderDef
[i
].width
;
406 colorNum
= borderDef
[i
].color
;
407 while (colorDef
&& colorDef
->rtfCNum
!= colorNum
)
408 colorDef
= colorDef
->rtfNextColor
;
410 pBorders
[i
]->colorRef
= RGB(
411 colorDef
->rtfCRed
>= 0 ? colorDef
->rtfCRed
: 0,
412 colorDef
->rtfCGreen
>= 0 ? colorDef
->rtfCGreen
: 0,
413 colorDef
->rtfCBlue
>= 0 ? colorDef
->rtfCBlue
: 0);
415 pBorders
[i
]->colorRef
= RGB(0, 0, 0);
419 void ME_RTFCharAttrHook(RTF_Info
*info
)
422 fmt
.cbSize
= sizeof(fmt
);
426 switch(info
->rtfMinor
)
429 /* FIXME add more flags once they're implemented */
430 fmt
.dwMask
= CFM_BOLD
| CFM_ITALIC
| CFM_UNDERLINE
| CFM_UNDERLINETYPE
| CFM_STRIKEOUT
|
431 CFM_COLOR
| CFM_BACKCOLOR
| CFM_SIZE
| CFM_WEIGHT
;
432 fmt
.dwEffects
= CFE_AUTOCOLOR
| CFE_AUTOBACKCOLOR
;
433 fmt
.yHeight
= 12*20; /* 12pt */
434 fmt
.wWeight
= FW_NORMAL
;
435 fmt
.bUnderlineType
= CFU_UNDERLINE
;
438 fmt
.dwMask
= CFM_BOLD
| CFM_WEIGHT
;
439 fmt
.dwEffects
= info
->rtfParam
? CFE_BOLD
: 0;
440 fmt
.wWeight
= info
->rtfParam
? FW_BOLD
: FW_NORMAL
;
443 fmt
.dwMask
= CFM_ITALIC
;
444 fmt
.dwEffects
= info
->rtfParam
? fmt
.dwMask
: 0;
447 fmt
.dwMask
= CFM_UNDERLINETYPE
| CFM_UNDERLINE
;
448 fmt
.bUnderlineType
= CFU_UNDERLINE
;
449 fmt
.dwEffects
= info
->rtfParam
? CFE_UNDERLINE
: 0;
451 case rtfDotUnderline
:
452 fmt
.dwMask
= CFM_UNDERLINETYPE
| CFM_UNDERLINE
;
453 fmt
.bUnderlineType
= CFU_UNDERLINEDOTTED
;
454 fmt
.dwEffects
= info
->rtfParam
? CFE_UNDERLINE
: 0;
457 fmt
.dwMask
= CFM_UNDERLINETYPE
| CFM_UNDERLINE
;
458 fmt
.bUnderlineType
= CFU_UNDERLINEDOUBLE
;
459 fmt
.dwEffects
= info
->rtfParam
? CFE_UNDERLINE
: 0;
461 case rtfWordUnderline
:
462 fmt
.dwMask
= CFM_UNDERLINETYPE
| CFM_UNDERLINE
;
463 fmt
.bUnderlineType
= CFU_UNDERLINEWORD
;
464 fmt
.dwEffects
= info
->rtfParam
? CFE_UNDERLINE
: 0;
467 fmt
.dwMask
= CFM_UNDERLINE
;
471 fmt
.dwMask
= CFM_STRIKEOUT
;
472 fmt
.dwEffects
= info
->rtfParam
? fmt
.dwMask
: 0;
476 case rtfSubScrShrink
:
477 case rtfSuperScrShrink
:
479 fmt
.dwMask
= CFM_SUBSCRIPT
|CFM_SUPERSCRIPT
;
480 if (info
->rtfMinor
== rtfSubScrShrink
) fmt
.dwEffects
= CFE_SUBSCRIPT
;
481 if (info
->rtfMinor
== rtfSuperScrShrink
) fmt
.dwEffects
= CFE_SUPERSCRIPT
;
482 if (info
->rtfMinor
== rtfNoSuperSub
) fmt
.dwEffects
= 0;
485 fmt
.dwMask
= CFM_HIDDEN
;
486 fmt
.dwEffects
= info
->rtfParam
? fmt
.dwMask
: 0;
489 fmt
.dwMask
= CFM_BACKCOLOR
;
491 if (info
->rtfParam
== 0)
492 fmt
.dwEffects
= CFE_AUTOBACKCOLOR
;
493 else if (info
->rtfParam
!= rtfNoParam
)
495 RTFColor
*c
= RTFGetColor(info
, info
->rtfParam
);
496 if (c
&& c
->rtfCBlue
>= 0)
497 fmt
.crBackColor
= (c
->rtfCBlue
<<16)|(c
->rtfCGreen
<<8)|(c
->rtfCRed
);
499 fmt
.dwEffects
= CFE_AUTOBACKCOLOR
;
503 fmt
.dwMask
= CFM_COLOR
;
505 if (info
->rtfParam
== 0)
506 fmt
.dwEffects
= CFE_AUTOCOLOR
;
507 else if (info
->rtfParam
!= rtfNoParam
)
509 RTFColor
*c
= RTFGetColor(info
, info
->rtfParam
);
510 if (c
&& c
->rtfCBlue
>= 0)
511 fmt
.crTextColor
= (c
->rtfCBlue
<<16)|(c
->rtfCGreen
<<8)|(c
->rtfCRed
);
513 fmt
.dwEffects
= CFE_AUTOCOLOR
;
518 if (info
->rtfParam
!= rtfNoParam
)
520 RTFFont
*f
= RTFGetFont(info
, info
->rtfParam
);
523 MultiByteToWideChar(CP_ACP
, 0, f
->rtfFName
, -1, fmt
.szFaceName
, sizeof(fmt
.szFaceName
)/sizeof(WCHAR
));
524 fmt
.szFaceName
[sizeof(fmt
.szFaceName
)/sizeof(WCHAR
)-1] = '\0';
525 fmt
.bCharSet
= f
->rtfFCharSet
;
526 fmt
.dwMask
= CFM_FACE
| CFM_CHARSET
;
527 fmt
.bPitchAndFamily
= f
->rtfFPitch
| (f
->rtfFFamily
<< 4);
532 fmt
.dwMask
= CFM_SIZE
;
533 if (info
->rtfParam
!= rtfNoParam
)
534 fmt
.yHeight
= info
->rtfParam
*10;
539 RTFFlushOutputBuffer(info
);
540 /* FIXME too slow ? how come ? */
541 style2
= ME_ApplyStyle(info
->editor
, info
->style
, &fmt
);
542 ME_ReleaseStyle(info
->style
);
543 info
->style
= style2
;
544 info
->styleChanged
= TRUE
;
548 /* FIXME this function doesn't get any information about context of the RTF tag, which is very bad,
549 the same tags mean different things in different contexts */
550 void ME_RTFParAttrHook(RTF_Info
*info
)
552 switch(info
->rtfMinor
)
554 case rtfParDef
: /* restores default paragraph attributes */
555 if (!info
->editor
->bEmulateVersion10
) /* v4.1 */
556 info
->borderType
= RTFBorderParaLeft
;
557 else /* v1.0 - 3.0 */
558 info
->borderType
= RTFBorderParaTop
;
559 info
->fmt
.dwMask
= PFM_ALIGNMENT
| PFM_BORDER
| PFM_LINESPACING
| PFM_TABSTOPS
|
560 PFM_OFFSET
| PFM_RIGHTINDENT
| PFM_SPACEAFTER
| PFM_SPACEBEFORE
|
561 PFM_STARTINDENT
| PFM_RTLPARA
| PFM_NUMBERING
| PFM_NUMBERINGSTART
|
562 PFM_NUMBERINGSTYLE
| PFM_NUMBERINGTAB
;
564 info
->fmt
.wAlignment
= PFA_LEFT
;
565 info
->fmt
.cTabCount
= 0;
566 info
->fmt
.dxOffset
= info
->fmt
.dxStartIndent
= info
->fmt
.dxRightIndent
= 0;
567 info
->fmt
.wBorderWidth
= info
->fmt
.wBorders
= 0;
568 info
->fmt
.wBorderSpace
= 0;
569 info
->fmt
.bLineSpacingRule
= 0;
570 info
->fmt
.dySpaceBefore
= info
->fmt
.dySpaceAfter
= 0;
571 info
->fmt
.dyLineSpacing
= 0;
572 info
->fmt
.wEffects
&= ~PFE_RTLPARA
;
573 info
->fmt
.wNumbering
= 0;
574 info
->fmt
.wNumberingStart
= 0;
575 info
->fmt
.wNumberingStyle
= 0;
576 info
->fmt
.wNumberingTab
= 0;
578 if (!info
->editor
->bEmulateVersion10
) /* v4.1 */
580 if (info
->tableDef
&& info
->tableDef
->tableRowStart
&&
581 info
->tableDef
->tableRowStart
->member
.para
.nFlags
& MEPF_ROWEND
)
584 ME_DisplayItem
*para
;
585 /* We are just after a table row. */
586 RTFFlushOutputBuffer(info
);
587 cursor
= info
->editor
->pCursors
[0];
589 if (para
== info
->tableDef
->tableRowStart
->member
.para
.next_para
590 && !cursor
.nOffset
&& !cursor
.pRun
->member
.run
.nCharOfs
)
592 /* Since the table row end, no text has been inserted, and the \intbl
593 * control word has not be used. We can confirm that we are not in a
596 info
->tableDef
->tableRowStart
= NULL
;
597 info
->canInheritInTbl
= FALSE
;
600 } else { /* v1.0 - v3.0 */
601 info
->fmt
.dwMask
|= PFM_TABLE
;
602 info
->fmt
.wEffects
&= ~PFE_TABLE
;
606 if (!info
->editor
->bEmulateVersion10
) /* v4.1 */
608 while (info
->rtfParam
> info
->nestingLevel
) {
609 RTFTable
*tableDef
= ALLOC_OBJ(RTFTable
);
610 ZeroMemory(tableDef
, sizeof(RTFTable
));
611 tableDef
->parent
= info
->tableDef
;
612 info
->tableDef
= tableDef
;
614 RTFFlushOutputBuffer(info
);
615 if (tableDef
->tableRowStart
&&
616 tableDef
->tableRowStart
->member
.para
.nFlags
& MEPF_ROWEND
)
618 ME_DisplayItem
*para
= tableDef
->tableRowStart
;
619 para
= para
->member
.para
.next_para
;
620 para
= ME_InsertTableRowStartAtParagraph(info
->editor
, para
);
621 tableDef
->tableRowStart
= para
;
625 cursor
= info
->editor
->pCursors
[0];
626 if (cursor
.nOffset
|| cursor
.pRun
->member
.run
.nCharOfs
)
627 ME_InsertTextFromCursor(info
->editor
, 0, &endl
, 1, info
->style
);
628 tableDef
->tableRowStart
= ME_InsertTableRowStartFromCursor(info
->editor
);
631 info
->nestingLevel
++;
633 info
->canInheritInTbl
= FALSE
;
638 if (!info
->editor
->bEmulateVersion10
) /* v4.1 */
640 if (info
->nestingLevel
< 1)
645 info
->tableDef
= ALLOC_OBJ(RTFTable
);
646 ZeroMemory(info
->tableDef
, sizeof(RTFTable
));
648 tableDef
= info
->tableDef
;
649 RTFFlushOutputBuffer(info
);
650 if (tableDef
->tableRowStart
&&
651 tableDef
->tableRowStart
->member
.para
.nFlags
& MEPF_ROWEND
)
653 ME_DisplayItem
*para
= tableDef
->tableRowStart
;
654 para
= para
->member
.para
.next_para
;
655 para
= ME_InsertTableRowStartAtParagraph(info
->editor
, para
);
656 tableDef
->tableRowStart
= para
;
660 cursor
= info
->editor
->pCursors
[0];
661 if (cursor
.nOffset
|| cursor
.pRun
->member
.run
.nCharOfs
)
662 ME_InsertTextFromCursor(info
->editor
, 0, &endl
, 1, info
->style
);
663 tableDef
->tableRowStart
= ME_InsertTableRowStartFromCursor(info
->editor
);
665 info
->nestingLevel
= 1;
666 info
->canInheritInTbl
= TRUE
;
669 } else { /* v1.0 - v3.0 */
670 info
->fmt
.dwMask
|= PFM_TABLE
;
671 info
->fmt
.wEffects
|= PFE_TABLE
;
677 if ((info
->fmt
.dwMask
& (PFM_STARTINDENT
| PFM_OFFSET
)) != (PFM_STARTINDENT
| PFM_OFFSET
))
680 fmt
.cbSize
= sizeof(fmt
);
681 ME_GetSelectionParaFormat(info
->editor
, &fmt
);
682 info
->fmt
.dwMask
|= PFM_STARTINDENT
| PFM_OFFSET
;
683 info
->fmt
.dxStartIndent
= fmt
.dxStartIndent
;
684 info
->fmt
.dxOffset
= fmt
.dxOffset
;
686 if (info
->rtfMinor
== rtfFirstIndent
)
688 info
->fmt
.dxStartIndent
+= info
->fmt
.dxOffset
+ info
->rtfParam
;
689 info
->fmt
.dxOffset
= -info
->rtfParam
;
692 info
->fmt
.dxStartIndent
= info
->rtfParam
- info
->fmt
.dxOffset
;
695 info
->fmt
.dwMask
|= PFM_RIGHTINDENT
;
696 info
->fmt
.dxRightIndent
= info
->rtfParam
;
700 info
->fmt
.dwMask
|= PFM_ALIGNMENT
;
701 info
->fmt
.wAlignment
= PFA_LEFT
;
704 info
->fmt
.dwMask
|= PFM_ALIGNMENT
;
705 info
->fmt
.wAlignment
= PFA_RIGHT
;
708 info
->fmt
.dwMask
|= PFM_ALIGNMENT
;
709 info
->fmt
.wAlignment
= PFA_CENTER
;
712 if (!(info
->fmt
.dwMask
& PFM_TABSTOPS
))
715 fmt
.cbSize
= sizeof(fmt
);
716 ME_GetSelectionParaFormat(info
->editor
, &fmt
);
717 memcpy(info
->fmt
.rgxTabs
, fmt
.rgxTabs
,
718 fmt
.cTabCount
* sizeof(fmt
.rgxTabs
[0]));
719 info
->fmt
.cTabCount
= fmt
.cTabCount
;
720 info
->fmt
.dwMask
|= PFM_TABSTOPS
;
722 if (info
->fmt
.cTabCount
< MAX_TAB_STOPS
&& info
->rtfParam
< 0x1000000)
723 info
->fmt
.rgxTabs
[info
->fmt
.cTabCount
++] = info
->rtfParam
;
726 info
->fmt
.dwMask
|= PFM_KEEP
;
727 info
->fmt
.wEffects
|= PFE_KEEP
;
729 case rtfNoWidowControl
:
730 info
->fmt
.dwMask
|= PFM_NOWIDOWCONTROL
;
731 info
->fmt
.wEffects
|= PFE_NOWIDOWCONTROL
;
734 info
->fmt
.dwMask
|= PFM_KEEPNEXT
;
735 info
->fmt
.wEffects
|= PFE_KEEPNEXT
;
738 info
->fmt
.dwMask
|= PFM_SPACEAFTER
;
739 info
->fmt
.dySpaceAfter
= info
->rtfParam
;
742 info
->fmt
.dwMask
|= PFM_SPACEBEFORE
;
743 info
->fmt
.dySpaceBefore
= info
->rtfParam
;
745 case rtfSpaceBetween
:
746 info
->fmt
.dwMask
|= PFM_LINESPACING
;
747 if ((int)info
->rtfParam
> 0)
749 info
->fmt
.dyLineSpacing
= info
->rtfParam
;
750 info
->fmt
.bLineSpacingRule
= 3;
754 info
->fmt
.dyLineSpacing
= info
->rtfParam
;
755 info
->fmt
.bLineSpacingRule
= 4;
758 case rtfSpaceMultiply
:
759 info
->fmt
.dwMask
|= PFM_LINESPACING
;
760 info
->fmt
.dyLineSpacing
= info
->rtfParam
* 20;
761 info
->fmt
.bLineSpacingRule
= 5;
764 info
->fmt
.dwMask
|= PFM_NUMBERING
;
765 info
->fmt
.wNumbering
= PFN_BULLET
;
768 info
->fmt
.dwMask
|= PFM_NUMBERING
;
769 info
->fmt
.wNumbering
= 2; /* FIXME: MSDN says it's not used ?? */
772 info
->borderType
= RTFBorderParaLeft
;
773 info
->fmt
.wBorders
|= 1;
774 info
->fmt
.dwMask
|= PFM_BORDER
;
777 info
->borderType
= RTFBorderParaRight
;
778 info
->fmt
.wBorders
|= 2;
779 info
->fmt
.dwMask
|= PFM_BORDER
;
782 info
->borderType
= RTFBorderParaTop
;
783 info
->fmt
.wBorders
|= 4;
784 info
->fmt
.dwMask
|= PFM_BORDER
;
786 case rtfBorderBottom
:
787 info
->borderType
= RTFBorderParaBottom
;
788 info
->fmt
.wBorders
|= 8;
789 info
->fmt
.dwMask
|= PFM_BORDER
;
791 case rtfBorderSingle
:
792 info
->fmt
.wBorders
&= ~0x700;
793 info
->fmt
.wBorders
|= 1 << 8;
794 info
->fmt
.dwMask
|= PFM_BORDER
;
797 info
->fmt
.wBorders
&= ~0x700;
798 info
->fmt
.wBorders
|= 2 << 8;
799 info
->fmt
.dwMask
|= PFM_BORDER
;
801 case rtfBorderShadow
:
802 info
->fmt
.wBorders
&= ~0x700;
803 info
->fmt
.wBorders
|= 10 << 8;
804 info
->fmt
.dwMask
|= PFM_BORDER
;
806 case rtfBorderDouble
:
807 info
->fmt
.wBorders
&= ~0x700;
808 info
->fmt
.wBorders
|= 7 << 8;
809 info
->fmt
.dwMask
|= PFM_BORDER
;
812 info
->fmt
.wBorders
&= ~0x700;
813 info
->fmt
.wBorders
|= 11 << 8;
814 info
->fmt
.dwMask
|= PFM_BORDER
;
818 int borderSide
= info
->borderType
& RTFBorderSideMask
;
819 RTFTable
*tableDef
= info
->tableDef
;
820 if ((info
->borderType
& RTFBorderTypeMask
) == RTFBorderTypeCell
)
823 if (!tableDef
|| tableDef
->numCellsDefined
>= MAX_TABLE_CELLS
)
825 border
= &tableDef
->cells
[tableDef
->numCellsDefined
].border
[borderSide
];
826 border
->width
= info
->rtfParam
;
829 info
->fmt
.wBorderWidth
= info
->rtfParam
;
830 info
->fmt
.dwMask
|= PFM_BORDER
;
834 info
->fmt
.wBorderSpace
= info
->rtfParam
;
835 info
->fmt
.dwMask
|= PFM_BORDER
;
839 RTFTable
*tableDef
= info
->tableDef
;
840 int borderSide
= info
->borderType
& RTFBorderSideMask
;
841 int borderType
= info
->borderType
& RTFBorderTypeMask
;
844 case RTFBorderTypePara
:
845 if (!info
->editor
->bEmulateVersion10
) /* v4.1 */
847 /* v1.0 - 3.0 treat paragraph and row borders the same. */
848 case RTFBorderTypeRow
:
850 tableDef
->border
[borderSide
].color
= info
->rtfParam
;
853 case RTFBorderTypeCell
:
854 if (tableDef
&& tableDef
->numCellsDefined
< MAX_TABLE_CELLS
) {
855 tableDef
->cells
[tableDef
->numCellsDefined
].border
[borderSide
].color
= info
->rtfParam
;
862 info
->fmt
.dwMask
|= PFM_RTLPARA
;
863 info
->fmt
.wEffects
|= PFE_RTLPARA
;
866 info
->fmt
.dwMask
|= PFM_RTLPARA
;
867 info
->fmt
.wEffects
&= ~PFE_RTLPARA
;
872 void ME_RTFTblAttrHook(RTF_Info
*info
)
874 switch (info
->rtfMinor
)
878 if (!info
->editor
->bEmulateVersion10
) /* v4.1 */
879 info
->borderType
= 0; /* Not sure */
880 else /* v1.0 - 3.0 */
881 info
->borderType
= RTFBorderRowTop
;
882 if (!info
->tableDef
) {
883 info
->tableDef
= ME_MakeTableDef(info
->editor
);
885 ME_InitTableDef(info
->editor
, info
->tableDef
);
894 info
->tableDef
= ME_MakeTableDef(info
->editor
);
896 cellNum
= info
->tableDef
->numCellsDefined
;
897 if (cellNum
>= MAX_TABLE_CELLS
)
899 info
->tableDef
->cells
[cellNum
].rightBoundary
= info
->rtfParam
;
900 if (cellNum
< MAX_TAB_STOPS
) {
901 /* Tab stops were used to store cell positions before v4.1 but v4.1
902 * still seems to set the tabstops without using them. */
903 ME_DisplayItem
*para
= info
->editor
->pCursors
[0].pPara
;
904 PARAFORMAT2
*pFmt
= ¶
->member
.para
.fmt
;
905 pFmt
->rgxTabs
[cellNum
] &= ~0x00FFFFFF;
906 pFmt
->rgxTabs
[cellNum
] |= 0x00FFFFFF & info
->rtfParam
;
908 info
->tableDef
->numCellsDefined
++;
912 info
->borderType
= RTFBorderRowTop
;
915 info
->borderType
= RTFBorderRowLeft
;
917 case rtfRowBordBottom
:
918 info
->borderType
= RTFBorderRowBottom
;
920 case rtfRowBordRight
:
921 info
->borderType
= RTFBorderRowRight
;
924 info
->borderType
= RTFBorderCellTop
;
926 case rtfCellBordLeft
:
927 info
->borderType
= RTFBorderCellLeft
;
929 case rtfCellBordBottom
:
930 info
->borderType
= RTFBorderCellBottom
;
932 case rtfCellBordRight
:
933 info
->borderType
= RTFBorderCellRight
;
937 info
->tableDef
->gapH
= info
->rtfParam
;
941 info
->tableDef
->leftEdge
= info
->rtfParam
;
946 void ME_RTFSpecialCharHook(RTF_Info
*info
)
948 RTFTable
*tableDef
= info
->tableDef
;
949 switch (info
->rtfMinor
)
952 if (info
->editor
->bEmulateVersion10
) /* v1.0 - v3.0 */
954 /* else fall through since v4.1 treats rtfNestCell and rtfCell the same */
958 RTFFlushOutputBuffer(info
);
959 if (!info
->editor
->bEmulateVersion10
) { /* v4.1 */
960 if (tableDef
->tableRowStart
)
962 if (!info
->nestingLevel
&&
963 tableDef
->tableRowStart
->member
.para
.nFlags
& MEPF_ROWEND
)
965 ME_DisplayItem
*para
= tableDef
->tableRowStart
;
966 para
= para
->member
.para
.next_para
;
967 para
= ME_InsertTableRowStartAtParagraph(info
->editor
, para
);
968 tableDef
->tableRowStart
= para
;
969 info
->nestingLevel
= 1;
971 ME_InsertTableCellFromCursor(info
->editor
);
973 } else { /* v1.0 - v3.0 */
974 ME_DisplayItem
*para
= info
->editor
->pCursors
[0].pPara
;
975 PARAFORMAT2
*pFmt
= ¶
->member
.para
.fmt
;
976 if (pFmt
->dwMask
& PFM_TABLE
&& pFmt
->wEffects
& PFE_TABLE
&&
977 tableDef
->numCellsInserted
< tableDef
->numCellsDefined
)
980 ME_InsertTextFromCursor(info
->editor
, 0, &tab
, 1, info
->style
);
981 tableDef
->numCellsInserted
++;
986 if (info
->editor
->bEmulateVersion10
) /* v1.0 - v3.0 */
988 /* else fall through since v4.1 treats rtfNestRow and rtfRow the same */
991 ME_DisplayItem
*para
, *cell
, *run
;
996 RTFFlushOutputBuffer(info
);
997 if (!info
->editor
->bEmulateVersion10
) { /* v4.1 */
998 if (!tableDef
->tableRowStart
)
1000 if (!info
->nestingLevel
&&
1001 tableDef
->tableRowStart
->member
.para
.nFlags
& MEPF_ROWEND
)
1003 para
= tableDef
->tableRowStart
;
1004 para
= para
->member
.para
.next_para
;
1005 para
= ME_InsertTableRowStartAtParagraph(info
->editor
, para
);
1006 tableDef
->tableRowStart
= para
;
1007 info
->nestingLevel
++;
1009 para
= tableDef
->tableRowStart
;
1010 cell
= ME_FindItemFwd(para
, diCell
);
1011 assert(cell
&& !cell
->member
.cell
.prev_cell
);
1012 if (tableDef
->numCellsDefined
< 1)
1014 /* 2000 twips appears to be the cell size that native richedit uses
1015 * when no cell sizes are specified. */
1016 const int defaultCellSize
= 2000;
1017 int nRightBoundary
= defaultCellSize
;
1018 cell
->member
.cell
.nRightBoundary
= nRightBoundary
;
1019 while (cell
->member
.cell
.next_cell
) {
1020 cell
= cell
->member
.cell
.next_cell
;
1021 nRightBoundary
+= defaultCellSize
;
1022 cell
->member
.cell
.nRightBoundary
= nRightBoundary
;
1024 para
= ME_InsertTableCellFromCursor(info
->editor
);
1025 cell
= para
->member
.para
.pCell
;
1026 cell
->member
.cell
.nRightBoundary
= nRightBoundary
;
1028 for (i
= 0; i
< tableDef
->numCellsDefined
; i
++)
1030 RTFCell
*cellDef
= &tableDef
->cells
[i
];
1031 cell
->member
.cell
.nRightBoundary
= cellDef
->rightBoundary
;
1032 ME_ApplyBorderProperties(info
, &cell
->member
.cell
.border
,
1034 cell
= cell
->member
.cell
.next_cell
;
1037 para
= ME_InsertTableCellFromCursor(info
->editor
);
1038 cell
= para
->member
.para
.pCell
;
1041 /* Cell for table row delimiter is empty */
1042 cell
->member
.cell
.nRightBoundary
= tableDef
->cells
[i
-1].rightBoundary
;
1045 run
= ME_FindItemFwd(cell
, diRun
);
1046 if (info
->editor
->pCursors
[0].pRun
!= run
||
1047 info
->editor
->pCursors
[0].nOffset
)
1050 /* Delete inserted cells that aren't defined. */
1051 info
->editor
->pCursors
[1].pRun
= run
;
1052 info
->editor
->pCursors
[1].pPara
= ME_GetParagraph(run
);
1053 info
->editor
->pCursors
[1].nOffset
= 0;
1054 nOfs
= ME_GetCursorOfs(&info
->editor
->pCursors
[1]);
1055 nChars
= ME_GetCursorOfs(&info
->editor
->pCursors
[0]) - nOfs
;
1056 ME_InternalDeleteText(info
->editor
, &info
->editor
->pCursors
[1],
1060 para
= ME_InsertTableRowEndFromCursor(info
->editor
);
1061 para
->member
.para
.fmt
.dxOffset
= abs(info
->tableDef
->gapH
);
1062 para
->member
.para
.fmt
.dxStartIndent
= info
->tableDef
->leftEdge
;
1063 ME_ApplyBorderProperties(info
, ¶
->member
.para
.border
,
1065 info
->nestingLevel
--;
1066 if (!info
->nestingLevel
)
1068 if (info
->canInheritInTbl
) {
1069 tableDef
->tableRowStart
= para
;
1071 while (info
->tableDef
) {
1072 tableDef
= info
->tableDef
;
1073 info
->tableDef
= tableDef
->parent
;
1074 heap_free(tableDef
);
1078 info
->tableDef
= tableDef
->parent
;
1079 heap_free(tableDef
);
1081 } else { /* v1.0 - v3.0 */
1083 ME_DisplayItem
*para
= info
->editor
->pCursors
[0].pPara
;
1084 PARAFORMAT2
*pFmt
= ¶
->member
.para
.fmt
;
1085 pFmt
->dxOffset
= info
->tableDef
->gapH
;
1086 pFmt
->dxStartIndent
= info
->tableDef
->leftEdge
;
1088 ME_ApplyBorderProperties(info
, ¶
->member
.para
.border
,
1090 while (tableDef
->numCellsInserted
< tableDef
->numCellsDefined
)
1093 ME_InsertTextFromCursor(info
->editor
, 0, &tab
, 1, info
->style
);
1094 tableDef
->numCellsInserted
++;
1096 pFmt
->cTabCount
= min(tableDef
->numCellsDefined
, MAX_TAB_STOPS
);
1097 if (!tableDef
->numCellsDefined
)
1098 pFmt
->wEffects
&= ~PFE_TABLE
;
1099 ME_InsertTextFromCursor(info
->editor
, 0, &endl
, 1, info
->style
);
1100 tableDef
->numCellsInserted
= 0;
1106 if (info
->editor
->bEmulateVersion10
) { /* v1.0 - 3.0 */
1107 ME_DisplayItem
*para
;
1109 RTFFlushOutputBuffer(info
);
1110 para
= info
->editor
->pCursors
[0].pPara
;
1111 pFmt
= ¶
->member
.para
.fmt
;
1112 if (pFmt
->dwMask
& PFM_TABLE
&& pFmt
->wEffects
& PFE_TABLE
)
1114 /* rtfPar is treated like a space within a table. */
1115 info
->rtfClass
= rtfText
;
1116 info
->rtfMajor
= ' ';
1118 else if (info
->rtfMinor
== rtfPar
&& tableDef
)
1119 tableDef
->numCellsInserted
= 0;
1125 static BOOL
ME_RTFInsertOleObject(RTF_Info
*info
, HENHMETAFILE hemf
, HBITMAP hbmp
,
1128 LPOLEOBJECT lpObject
= NULL
;
1129 LPSTORAGE lpStorage
= NULL
;
1130 LPOLECLIENTSITE lpClientSite
= NULL
;
1131 LPDATAOBJECT lpDataObject
= NULL
;
1132 LPOLECACHE lpOleCache
= NULL
;
1141 stgm
.tymed
= TYMED_ENHMF
;
1142 stgm
.u
.hEnhMetaFile
= hemf
;
1143 fm
.cfFormat
= CF_ENHMETAFILE
;
1147 stgm
.tymed
= TYMED_GDI
;
1148 stgm
.u
.hBitmap
= hbmp
;
1149 fm
.cfFormat
= CF_BITMAP
;
1151 stgm
.pUnkForRelease
= NULL
;
1154 fm
.dwAspect
= DVASPECT_CONTENT
;
1156 fm
.tymed
= stgm
.tymed
;
1158 if (!info
->lpRichEditOle
)
1160 CreateIRichEditOle(NULL
, info
->editor
, (VOID
**)&info
->lpRichEditOle
);
1163 if (OleCreateDefaultHandler(&CLSID_NULL
, NULL
, &IID_IOleObject
, (void**)&lpObject
) == S_OK
&&
1164 IRichEditOle_GetClientSite(info
->lpRichEditOle
, &lpClientSite
) == S_OK
&&
1165 IOleObject_SetClientSite(lpObject
, lpClientSite
) == S_OK
&&
1166 IOleObject_GetUserClassID(lpObject
, &clsid
) == S_OK
&&
1167 IOleObject_QueryInterface(lpObject
, &IID_IOleCache
, (void**)&lpOleCache
) == S_OK
&&
1168 IOleCache_Cache(lpOleCache
, &fm
, 0, &conn
) == S_OK
&&
1169 IOleObject_QueryInterface(lpObject
, &IID_IDataObject
, (void**)&lpDataObject
) == S_OK
&&
1170 IDataObject_SetData(lpDataObject
, &fm
, &stgm
, TRUE
) == S_OK
)
1174 reobject
.cbStruct
= sizeof(reobject
);
1175 reobject
.cp
= REO_CP_SELECTION
;
1176 reobject
.clsid
= clsid
;
1177 reobject
.poleobj
= lpObject
;
1178 reobject
.pstg
= lpStorage
;
1179 reobject
.polesite
= lpClientSite
;
1180 /* convert from twips to .01 mm */
1181 reobject
.sizel
.cx
= MulDiv(sz
->cx
, 254, 144);
1182 reobject
.sizel
.cy
= MulDiv(sz
->cy
, 254, 144);
1183 reobject
.dvaspect
= DVASPECT_CONTENT
;
1184 reobject
.dwFlags
= 0; /* FIXME */
1185 reobject
.dwUser
= 0;
1187 ME_InsertOLEFromCursor(info
->editor
, &reobject
, 0);
1191 if (lpObject
) IOleObject_Release(lpObject
);
1192 if (lpClientSite
) IOleClientSite_Release(lpClientSite
);
1193 if (lpStorage
) IStorage_Release(lpStorage
);
1194 if (lpDataObject
) IDataObject_Release(lpDataObject
);
1195 if (lpOleCache
) IOleCache_Release(lpOleCache
);
1200 static void ME_RTFReadShpPictGroup( RTF_Info
*info
)
1208 if (info
->rtfClass
== rtfEOF
) return;
1209 if (RTFCheckCM( info
, rtfGroup
, rtfEndGroup
))
1211 if (--level
== 0) break;
1213 else if (RTFCheckCM( info
, rtfGroup
, rtfBeginGroup
))
1219 RTFRouteToken( info
);
1220 if (RTFCheckCM( info
, rtfGroup
, rtfEndGroup
))
1225 RTFRouteToken( info
); /* feed "}" back to router */
1229 static DWORD
read_hex_data( RTF_Info
*info
, BYTE
**out
)
1231 DWORD read
= 0, size
= 1024;
1237 if (info
->rtfClass
!= rtfText
)
1239 ERR("Called with incorrect token\n");
1243 buf
= HeapAlloc( GetProcessHeap(), 0, size
);
1246 val
= info
->rtfMajor
;
1247 for (flip
= TRUE
;; flip
= !flip
)
1249 RTFGetToken( info
);
1250 if (info
->rtfClass
== rtfEOF
)
1252 HeapFree( GetProcessHeap(), 0, buf
);
1255 if (info
->rtfClass
!= rtfText
) break;
1261 buf
= HeapReAlloc( GetProcessHeap(), 0, buf
, size
);
1264 buf
[read
++] = RTFCharToHex(val
) * 16 + RTFCharToHex(info
->rtfMajor
);
1267 val
= info
->rtfMajor
;
1269 if (flip
) FIXME("wrong hex string\n");
1275 static void ME_RTFReadPictGroup(RTF_Info
*info
)
1278 BYTE
*buffer
= NULL
;
1283 enum gfxkind
{gfx_unknown
= 0, gfx_enhmetafile
, gfx_metafile
, gfx_dib
} gfx
= gfx_unknown
;
1291 RTFGetToken( info
);
1293 if (info
->rtfClass
== rtfText
)
1298 size
= read_hex_data( info
, &buffer
);
1302 RTFSkipGroup( info
);
1304 } /* We potentially have a new token so fall through. */
1306 if (info
->rtfClass
== rtfEOF
) return;
1308 if (RTFCheckCM( info
, rtfGroup
, rtfEndGroup
))
1310 if (--level
== 0) break;
1313 if (RTFCheckCM( info
, rtfGroup
, rtfBeginGroup
))
1318 if (!RTFCheckCM( info
, rtfControl
, rtfPictAttr
))
1320 RTFRouteToken( info
);
1321 if (RTFCheckCM( info
, rtfGroup
, rtfEndGroup
))
1326 if (RTFCheckMM( info
, rtfPictAttr
, rtfWinMetafile
))
1328 mfp
.mm
= info
->rtfParam
;
1331 else if (RTFCheckMM( info
, rtfPictAttr
, rtfDevIndBitmap
))
1333 if (info
->rtfParam
!= 0) FIXME("dibitmap should be 0 (%d)\n", info
->rtfParam
);
1336 else if (RTFCheckMM( info
, rtfPictAttr
, rtfEmfBlip
))
1337 gfx
= gfx_enhmetafile
;
1338 else if (RTFCheckMM( info
, rtfPictAttr
, rtfPicWid
))
1339 mfp
.xExt
= info
->rtfParam
;
1340 else if (RTFCheckMM( info
, rtfPictAttr
, rtfPicHt
))
1341 mfp
.yExt
= info
->rtfParam
;
1342 else if (RTFCheckMM( info
, rtfPictAttr
, rtfPicGoalWid
))
1343 sz
.cx
= info
->rtfParam
;
1344 else if (RTFCheckMM( info
, rtfPictAttr
, rtfPicGoalHt
))
1345 sz
.cy
= info
->rtfParam
;
1347 FIXME("Non supported attribute: %d %d %d\n", info
->rtfClass
, info
->rtfMajor
, info
->rtfMinor
);
1354 case gfx_enhmetafile
:
1355 if ((hemf
= SetEnhMetaFileBits( size
, buffer
)))
1356 ME_RTFInsertOleObject( info
, hemf
, NULL
, &sz
);
1359 if ((hemf
= SetWinMetaFileBits( size
, buffer
, NULL
, &mfp
)))
1360 ME_RTFInsertOleObject( info
, hemf
, NULL
, &sz
);
1364 BITMAPINFO
*bi
= (BITMAPINFO
*)buffer
;
1366 unsigned nc
= bi
->bmiHeader
.biClrUsed
;
1368 /* not quite right, especially for bitfields type of compression */
1369 if (!nc
&& bi
->bmiHeader
.biBitCount
<= 8)
1370 nc
= 1 << bi
->bmiHeader
.biBitCount
;
1371 if ((hbmp
= CreateDIBitmap( hdc
, &bi
->bmiHeader
,
1372 CBM_INIT
, (char*)(bi
+ 1) + nc
* sizeof(RGBQUAD
),
1373 bi
, DIB_RGB_COLORS
)) )
1374 ME_RTFInsertOleObject( info
, NULL
, hbmp
, &sz
);
1375 ReleaseDC( 0, hdc
);
1382 HeapFree( GetProcessHeap(), 0, buffer
);
1383 RTFRouteToken( info
); /* feed "}" back to router */
1387 /* for now, lookup the \result part and use it, whatever the object */
1388 static void ME_RTFReadObjectGroup(RTF_Info
*info
)
1393 if (info
->rtfClass
== rtfEOF
)
1395 if (RTFCheckCM(info
, rtfGroup
, rtfEndGroup
))
1397 if (RTFCheckCM(info
, rtfGroup
, rtfBeginGroup
))
1400 if (info
->rtfClass
== rtfEOF
)
1402 if (RTFCheckCMM(info
, rtfControl
, rtfDestination
, rtfObjResult
))
1406 while (RTFGetToken (info
) != rtfEOF
)
1408 if (info
->rtfClass
== rtfGroup
)
1410 if (info
->rtfMajor
== rtfBeginGroup
) level
++;
1411 else if (info
->rtfMajor
== rtfEndGroup
&& --level
< 0) break;
1413 RTFRouteToken(info
);
1416 else RTFSkipGroup(info
);
1419 if (!RTFCheckCM (info
, rtfControl
, rtfObjAttr
))
1421 FIXME("Non supported attribute: %d %d %d\n", info
->rtfClass
, info
->rtfMajor
, info
->rtfMinor
);
1425 RTFRouteToken(info
); /* feed "}" back to router */
1428 static void ME_RTFReadParnumGroup( RTF_Info
*info
)
1430 int level
= 1, type
= -1;
1431 WORD indent
= 0, start
= 1;
1432 WCHAR txt_before
= 0, txt_after
= 0;
1436 RTFGetToken( info
);
1438 if (RTFCheckCMM( info
, rtfControl
, rtfDestination
, rtfParNumTextBefore
) ||
1439 RTFCheckCMM( info
, rtfControl
, rtfDestination
, rtfParNumTextAfter
))
1441 int loc
= info
->rtfMinor
;
1443 RTFGetToken( info
);
1444 if (info
->rtfClass
== rtfText
)
1446 if (loc
== rtfParNumTextBefore
)
1447 txt_before
= info
->rtfMajor
;
1449 txt_after
= info
->rtfMajor
;
1452 /* falling through to catch EOFs and group level changes */
1455 if (info
->rtfClass
== rtfEOF
)
1458 if (RTFCheckCM( info
, rtfGroup
, rtfEndGroup
))
1460 if (--level
== 0) break;
1464 if (RTFCheckCM( info
, rtfGroup
, rtfBeginGroup
))
1470 /* Ignore non para-attr */
1471 if (!RTFCheckCM( info
, rtfControl
, rtfParAttr
))
1474 switch (info
->rtfMinor
)
1476 case rtfParLevel
: /* Para level is ignored */
1483 case rtfParNumDecimal
:
1486 case rtfParNumULetter
:
1487 type
= PFN_UCLETTER
;
1489 case rtfParNumURoman
:
1492 case rtfParNumLLetter
:
1493 type
= PFN_LCLETTER
;
1495 case rtfParNumLRoman
:
1499 case rtfParNumIndent
:
1500 indent
= info
->rtfParam
;
1502 case rtfParNumStartAt
:
1503 start
= info
->rtfParam
;
1510 info
->fmt
.dwMask
|= (PFM_NUMBERING
| PFM_NUMBERINGSTART
| PFM_NUMBERINGSTYLE
| PFM_NUMBERINGTAB
);
1511 info
->fmt
.wNumbering
= type
;
1512 info
->fmt
.wNumberingStart
= start
;
1513 info
->fmt
.wNumberingStyle
= PFNS_PAREN
;
1514 if (type
!= PFN_BULLET
)
1516 if (txt_before
== 0 && txt_after
== 0)
1517 info
->fmt
.wNumberingStyle
= PFNS_PLAIN
;
1518 else if (txt_after
== '.')
1519 info
->fmt
.wNumberingStyle
= PFNS_PERIOD
;
1520 else if (txt_before
== '(' && txt_after
== ')')
1521 info
->fmt
.wNumberingStyle
= PFNS_PARENS
;
1523 info
->fmt
.wNumberingTab
= indent
;
1526 TRACE("type %d indent %d start %d txt before %04x txt after %04x\n",
1527 type
, indent
, start
, txt_before
, txt_after
);
1529 RTFRouteToken( info
); /* feed "}" back to router */
1532 static void ME_RTFReadHook(RTF_Info
*info
)
1534 switch(info
->rtfClass
)
1537 switch(info
->rtfMajor
)
1540 if (info
->stackTop
< maxStack
) {
1541 info
->stack
[info
->stackTop
].style
= info
->style
;
1542 ME_AddRefStyle(info
->style
);
1543 info
->stack
[info
->stackTop
].codePage
= info
->codePage
;
1544 info
->stack
[info
->stackTop
].unicodeLength
= info
->unicodeLength
;
1547 info
->styleChanged
= FALSE
;
1551 RTFFlushOutputBuffer(info
);
1553 if (info
->stackTop
<= 0)
1554 info
->rtfClass
= rtfEOF
;
1555 if (info
->stackTop
< 0)
1558 ME_ReleaseStyle(info
->style
);
1559 info
->style
= info
->stack
[info
->stackTop
].style
;
1560 info
->codePage
= info
->stack
[info
->stackTop
].codePage
;
1561 info
->unicodeLength
= info
->stack
[info
->stackTop
].unicodeLength
;
1570 ME_StreamInFill(ME_InStream
*stream
)
1572 stream
->editstream
->dwError
= stream
->editstream
->pfnCallback(stream
->editstream
->dwCookie
,
1573 (BYTE
*)stream
->buffer
,
1574 sizeof(stream
->buffer
),
1575 (LONG
*)&stream
->dwSize
);
1579 static LRESULT
ME_StreamIn(ME_TextEditor
*editor
, DWORD format
, EDITSTREAM
*stream
, BOOL stripLastCR
)
1583 int from
, to
, nUndoMode
;
1584 int nEventMask
= editor
->nEventMask
;
1585 ME_InStream inStream
;
1586 BOOL invalidRTF
= FALSE
;
1587 ME_Cursor
*selStart
, *selEnd
;
1588 LRESULT num_read
= 0; /* bytes read for SF_TEXT, non-control chars inserted for SF_RTF */
1590 TRACE("stream==%p editor==%p format==0x%X\n", stream
, editor
, format
);
1591 editor
->nEventMask
= 0;
1593 ME_GetSelectionOfs(editor
, &from
, &to
);
1594 if (format
& SFF_SELECTION
&& editor
->mode
& TM_RICHTEXT
)
1596 ME_GetSelection(editor
, &selStart
, &selEnd
);
1597 style
= ME_GetSelectionInsertStyle(editor
);
1599 ME_InternalDeleteText(editor
, selStart
, to
- from
, FALSE
);
1601 /* Don't insert text at the end of the table row */
1602 if (!editor
->bEmulateVersion10
) { /* v4.1 */
1603 ME_DisplayItem
*para
= editor
->pCursors
->pPara
;
1604 if (para
->member
.para
.nFlags
& MEPF_ROWEND
)
1606 para
= para
->member
.para
.next_para
;
1607 editor
->pCursors
[0].pPara
= para
;
1608 editor
->pCursors
[0].pRun
= ME_FindItemFwd(para
, diRun
);
1609 editor
->pCursors
[0].nOffset
= 0;
1611 if (para
->member
.para
.nFlags
& MEPF_ROWSTART
)
1613 para
= para
->member
.para
.next_para
;
1614 editor
->pCursors
[0].pPara
= para
;
1615 editor
->pCursors
[0].pRun
= ME_FindItemFwd(para
, diRun
);
1616 editor
->pCursors
[0].nOffset
= 0;
1618 editor
->pCursors
[1] = editor
->pCursors
[0];
1619 } else { /* v1.0 - 3.0 */
1620 if (editor
->pCursors
[0].pRun
->member
.run
.nFlags
& MERF_ENDPARA
&&
1621 ME_IsInTable(editor
->pCursors
[0].pRun
))
1625 style
= editor
->pBuffer
->pDefaultStyle
;
1626 ME_AddRefStyle(style
);
1627 ME_SetSelection(editor
, 0, 0);
1628 ME_InternalDeleteText(editor
, &editor
->pCursors
[1],
1629 ME_GetTextLength(editor
), FALSE
);
1631 ME_ClearTempStyle(editor
);
1632 ME_SetDefaultParaFormat(editor
, &editor
->pCursors
[0].pPara
->member
.para
.fmt
);
1636 /* Back up undo mode to a local variable */
1637 nUndoMode
= editor
->nUndoMode
;
1639 /* Only create an undo if SFF_SELECTION is set */
1640 if (!(format
& SFF_SELECTION
))
1641 editor
->nUndoMode
= umIgnore
;
1643 inStream
.editstream
= stream
;
1644 inStream
.editstream
->dwError
= 0;
1645 inStream
.dwSize
= 0;
1646 inStream
.dwUsed
= 0;
1648 if (format
& SF_RTF
)
1650 /* Check if it's really RTF, and if it is not, use plain text */
1651 ME_StreamInFill(&inStream
);
1652 if (!inStream
.editstream
->dwError
)
1654 if ((!editor
->bEmulateVersion10
&& strncmp(inStream
.buffer
, "{\\rtf", 5) && strncmp(inStream
.buffer
, "{\\urtf", 6))
1655 || (editor
->bEmulateVersion10
&& *inStream
.buffer
!= '{'))
1658 inStream
.editstream
->dwError
= -16;
1663 if (!invalidRTF
&& !inStream
.editstream
->dwError
)
1666 from
= ME_GetCursorOfs(&editor
->pCursors
[0]);
1667 if (format
& SF_RTF
) {
1669 /* setup the RTF parser */
1670 memset(&parser
, 0, sizeof parser
);
1671 RTFSetEditStream(&parser
, &inStream
);
1672 parser
.rtfFormat
= format
&(SF_TEXT
|SF_RTF
);
1673 parser
.editor
= editor
;
1674 parser
.style
= style
;
1675 WriterInit(&parser
);
1677 RTFSetReadHook(&parser
, ME_RTFReadHook
);
1678 RTFSetDestinationCallback(&parser
, rtfShpPict
, ME_RTFReadShpPictGroup
);
1679 RTFSetDestinationCallback(&parser
, rtfPict
, ME_RTFReadPictGroup
);
1680 RTFSetDestinationCallback(&parser
, rtfObject
, ME_RTFReadObjectGroup
);
1681 RTFSetDestinationCallback(&parser
, rtfParNumbering
, ME_RTFReadParnumGroup
);
1682 if (!parser
.editor
->bEmulateVersion10
) /* v4.1 */
1684 RTFSetDestinationCallback(&parser
, rtfNoNestTables
, RTFSkipGroup
);
1685 RTFSetDestinationCallback(&parser
, rtfNestTableProps
, RTFReadGroup
);
1689 /* do the parsing */
1691 RTFFlushOutputBuffer(&parser
);
1692 if (!editor
->bEmulateVersion10
) { /* v4.1 */
1693 if (parser
.tableDef
&& parser
.tableDef
->tableRowStart
&&
1694 (parser
.nestingLevel
> 0 || parser
.canInheritInTbl
))
1696 /* Delete any incomplete table row at the end of the rich text. */
1698 ME_DisplayItem
*para
;
1700 parser
.rtfMinor
= rtfRow
;
1701 /* Complete the table row before deleting it.
1702 * By doing it this way we will have the current paragraph format set
1703 * properly to reflect that is not in the complete table, and undo items
1704 * will be added for this change to the current paragraph format. */
1705 if (parser
.nestingLevel
> 0)
1707 while (parser
.nestingLevel
> 1)
1708 ME_RTFSpecialCharHook(&parser
); /* Decrements nestingLevel */
1709 para
= parser
.tableDef
->tableRowStart
;
1710 ME_RTFSpecialCharHook(&parser
);
1712 para
= parser
.tableDef
->tableRowStart
;
1713 ME_RTFSpecialCharHook(&parser
);
1714 assert(para
->member
.para
.nFlags
& MEPF_ROWEND
);
1715 para
= para
->member
.para
.next_para
;
1718 editor
->pCursors
[1].pPara
= para
;
1719 editor
->pCursors
[1].pRun
= ME_FindItemFwd(para
, diRun
);
1720 editor
->pCursors
[1].nOffset
= 0;
1721 nOfs
= ME_GetCursorOfs(&editor
->pCursors
[1]);
1722 nChars
= ME_GetCursorOfs(&editor
->pCursors
[0]) - nOfs
;
1723 ME_InternalDeleteText(editor
, &editor
->pCursors
[1], nChars
, TRUE
);
1724 if (parser
.tableDef
)
1725 parser
.tableDef
->tableRowStart
= NULL
;
1728 ME_CheckTablesForCorruption(editor
);
1729 RTFDestroy(&parser
);
1730 if (parser
.lpRichEditOle
)
1731 IRichEditOle_Release(parser
.lpRichEditOle
);
1733 if (parser
.stackTop
> 0)
1735 while (--parser
.stackTop
>= 0)
1737 ME_ReleaseStyle(parser
.style
);
1738 parser
.style
= parser
.stack
[parser
.stackTop
].style
;
1740 if (!inStream
.editstream
->dwError
)
1741 inStream
.editstream
->dwError
= HRESULT_FROM_WIN32(ERROR_HANDLE_EOF
);
1744 /* Remove last line break, as mandated by tests. This is not affected by
1745 CR/LF counters, since RTF streaming presents only \para tokens, which
1746 are converted according to the standard rules: \r for 2.0, \r\n for 1.0
1748 if (stripLastCR
&& !(format
& SFF_SELECTION
)) {
1750 ME_GetSelection(editor
, &selStart
, &selEnd
);
1751 newto
= ME_GetCursorOfs(selEnd
);
1752 if (newto
> to
+ (editor
->bEmulateVersion10
? 1 : 0)) {
1753 WCHAR lastchar
[3] = {'\0', '\0'};
1754 int linebreakSize
= editor
->bEmulateVersion10
? 2 : 1;
1755 ME_Cursor linebreakCursor
= *selEnd
, lastcharCursor
= *selEnd
;
1758 /* Set the final eop to the char fmt of the last char */
1759 cf
.cbSize
= sizeof(cf
);
1760 cf
.dwMask
= CFM_ALL2
;
1761 ME_MoveCursorChars(editor
, &lastcharCursor
, -1, FALSE
);
1762 ME_GetCharFormat(editor
, &lastcharCursor
, &linebreakCursor
, &cf
);
1763 ME_SetSelection(editor
, newto
, -1);
1764 ME_SetSelectionCharFormat(editor
, &cf
);
1765 ME_SetSelection(editor
, newto
, newto
);
1767 ME_MoveCursorChars(editor
, &linebreakCursor
, -linebreakSize
, FALSE
);
1768 ME_GetTextW(editor
, lastchar
, 2, &linebreakCursor
, linebreakSize
, FALSE
, FALSE
);
1769 if (lastchar
[0] == '\r' && (lastchar
[1] == '\n' || lastchar
[1] == '\0')) {
1770 ME_InternalDeleteText(editor
, &linebreakCursor
, linebreakSize
, FALSE
);
1774 to
= ME_GetCursorOfs(&editor
->pCursors
[0]);
1775 num_read
= to
- from
;
1777 style
= parser
.style
;
1779 else if (format
& SF_TEXT
)
1781 num_read
= ME_StreamInText(editor
, format
, &inStream
, style
);
1782 to
= ME_GetCursorOfs(&editor
->pCursors
[0]);
1785 ERR("EM_STREAMIN without SF_TEXT or SF_RTF\n");
1786 /* put the cursor at the top */
1787 if (!(format
& SFF_SELECTION
))
1788 ME_SetSelection(editor
, 0, 0);
1789 ME_CursorFromCharOfs(editor
, from
, &start
);
1790 ME_UpdateLinkAttribute(editor
, &start
, to
- from
);
1793 /* Restore saved undo mode */
1794 editor
->nUndoMode
= nUndoMode
;
1796 /* even if we didn't add an undo, we need to commit anything on the stack */
1797 ME_CommitUndo(editor
);
1799 /* If SFF_SELECTION isn't set, delete any undos from before we started too */
1800 if (!(format
& SFF_SELECTION
))
1801 ME_EmptyUndoStack(editor
);
1803 ME_ReleaseStyle(style
);
1804 editor
->nEventMask
= nEventMask
;
1805 ME_UpdateRepaint(editor
, FALSE
);
1806 if (!(format
& SFF_SELECTION
)) {
1807 ME_ClearTempStyle(editor
);
1809 ITextHost_TxShowCaret(editor
->texthost
, FALSE
);
1810 ME_MoveCaret(editor
);
1811 ITextHost_TxShowCaret(editor
->texthost
, TRUE
);
1812 ME_SendSelChange(editor
);
1813 ME_SendRequestResize(editor
, FALSE
);
1819 typedef struct tagME_RTFStringStreamStruct
1824 } ME_RTFStringStreamStruct
;
1826 static DWORD CALLBACK
ME_ReadFromRTFString(DWORD_PTR dwCookie
, LPBYTE lpBuff
, LONG cb
, LONG
*pcb
)
1828 ME_RTFStringStreamStruct
*pStruct
= (ME_RTFStringStreamStruct
*)dwCookie
;
1831 count
= min(cb
, pStruct
->length
- pStruct
->pos
);
1832 memmove(lpBuff
, pStruct
->string
+ pStruct
->pos
, count
);
1833 pStruct
->pos
+= count
;
1839 ME_StreamInRTFString(ME_TextEditor
*editor
, BOOL selection
, char *string
)
1842 ME_RTFStringStreamStruct data
;
1844 data
.string
= string
;
1845 data
.length
= strlen(string
);
1847 es
.dwCookie
= (DWORD_PTR
)&data
;
1848 es
.pfnCallback
= ME_ReadFromRTFString
;
1849 ME_StreamIn(editor
, SF_RTF
| (selection
? SFF_SELECTION
: 0), &es
, TRUE
);
1854 ME_FindText(ME_TextEditor
*editor
, DWORD flags
, const CHARRANGE
*chrg
, const WCHAR
*text
, CHARRANGE
*chrgText
)
1856 const int nLen
= lstrlenW(text
);
1857 const int nTextLen
= ME_GetTextLength(editor
);
1860 WCHAR wLastChar
= ' ';
1862 TRACE("flags==0x%08x, chrg->cpMin==%d, chrg->cpMax==%d text==%s\n",
1863 flags
, chrg
->cpMin
, chrg
->cpMax
, debugstr_w(text
));
1865 if (flags
& ~(FR_DOWN
| FR_MATCHCASE
| FR_WHOLEWORD
))
1866 FIXME("Flags 0x%08x not implemented\n",
1867 flags
& ~(FR_DOWN
| FR_MATCHCASE
| FR_WHOLEWORD
));
1870 if (chrg
->cpMax
== -1)
1873 nMax
= chrg
->cpMax
> nTextLen
? nTextLen
: chrg
->cpMax
;
1875 /* In 1.0 emulation, if cpMax reaches end of text, add the FR_DOWN flag */
1876 if (editor
->bEmulateVersion10
&& nMax
== nTextLen
)
1881 /* In 1.0 emulation, cpMin must always be no greater than cpMax */
1882 if (editor
->bEmulateVersion10
&& nMax
< nMin
)
1886 chrgText
->cpMin
= -1;
1887 chrgText
->cpMax
= -1;
1892 /* when searching up, if cpMin < cpMax, then instead of searching
1893 * on [cpMin,cpMax], we search on [0,cpMin], otherwise, search on
1894 * [cpMax, cpMin]. The exception is when cpMax is -1, in which
1895 * case, it is always bigger than cpMin.
1897 if (!editor
->bEmulateVersion10
&& !(flags
& FR_DOWN
))
1901 nMax
= nMin
> nTextLen
? nTextLen
: nMin
;
1902 if (nMin
< nSwap
|| chrg
->cpMax
== -1)
1908 if (!nLen
|| nMin
< 0 || nMax
< 0 || nMax
< nMin
)
1911 chrgText
->cpMin
= chrgText
->cpMax
= -1;
1915 if (flags
& FR_DOWN
) /* Forward search */
1917 /* If possible, find the character before where the search starts */
1918 if ((flags
& FR_WHOLEWORD
) && nMin
)
1920 ME_CursorFromCharOfs(editor
, nMin
- 1, &cursor
);
1921 wLastChar
= *get_text( &cursor
.pRun
->member
.run
, cursor
.nOffset
);
1922 ME_MoveCursorChars(editor
, &cursor
, 1, FALSE
);
1924 ME_CursorFromCharOfs(editor
, nMin
, &cursor
);
1927 while (cursor
.pRun
&& ME_GetCursorOfs(&cursor
) + nLen
<= nMax
)
1929 ME_DisplayItem
*pCurItem
= cursor
.pRun
;
1930 int nCurStart
= cursor
.nOffset
;
1933 while (pCurItem
&& ME_CharCompare( *get_text( &pCurItem
->member
.run
, nCurStart
+ nMatched
), text
[nMatched
], (flags
& FR_MATCHCASE
)))
1935 if ((flags
& FR_WHOLEWORD
) && isalnumW(wLastChar
))
1939 if (nMatched
== nLen
)
1941 ME_DisplayItem
*pNextItem
= pCurItem
;
1942 int nNextStart
= nCurStart
;
1945 /* Check to see if next character is a whitespace */
1946 if (flags
& FR_WHOLEWORD
)
1948 if (nCurStart
+ nMatched
== pCurItem
->member
.run
.len
)
1950 pNextItem
= ME_FindItemFwd(pCurItem
, diRun
);
1951 nNextStart
= -nMatched
;
1955 wNextChar
= *get_text( &pNextItem
->member
.run
, nNextStart
+ nMatched
);
1959 if (isalnumW(wNextChar
))
1963 cursor
.nOffset
+= cursor
.pPara
->member
.para
.nCharOfs
+ cursor
.pRun
->member
.run
.nCharOfs
;
1966 chrgText
->cpMin
= cursor
.nOffset
;
1967 chrgText
->cpMax
= cursor
.nOffset
+ nLen
;
1969 TRACE("found at %d-%d\n", cursor
.nOffset
, cursor
.nOffset
+ nLen
);
1970 return cursor
.nOffset
;
1972 if (nCurStart
+ nMatched
== pCurItem
->member
.run
.len
)
1974 pCurItem
= ME_FindItemFwd(pCurItem
, diRun
);
1975 nCurStart
= -nMatched
;
1979 wLastChar
= *get_text( &pCurItem
->member
.run
, nCurStart
+ nMatched
);
1984 if (cursor
.nOffset
== cursor
.pRun
->member
.run
.len
)
1986 ME_NextRun(&cursor
.pPara
, &cursor
.pRun
, TRUE
);
1991 else /* Backward search */
1993 /* If possible, find the character after where the search ends */
1994 if ((flags
& FR_WHOLEWORD
) && nMax
< nTextLen
- 1)
1996 ME_CursorFromCharOfs(editor
, nMax
+ 1, &cursor
);
1997 wLastChar
= *get_text( &cursor
.pRun
->member
.run
, cursor
.nOffset
);
1998 ME_MoveCursorChars(editor
, &cursor
, -1, FALSE
);
2000 ME_CursorFromCharOfs(editor
, nMax
, &cursor
);
2003 while (cursor
.pRun
&& ME_GetCursorOfs(&cursor
) - nLen
>= nMin
)
2005 ME_DisplayItem
*pCurItem
= cursor
.pRun
;
2006 ME_DisplayItem
*pCurPara
= cursor
.pPara
;
2007 int nCurEnd
= cursor
.nOffset
;
2012 ME_PrevRun(&pCurPara
, &pCurItem
, TRUE
);
2013 nCurEnd
= pCurItem
->member
.run
.len
;
2016 while (pCurItem
&& ME_CharCompare( *get_text( &pCurItem
->member
.run
, nCurEnd
- nMatched
- 1 ),
2017 text
[nLen
- nMatched
- 1], (flags
& FR_MATCHCASE
) ))
2019 if ((flags
& FR_WHOLEWORD
) && isalnumW(wLastChar
))
2023 if (nMatched
== nLen
)
2025 ME_DisplayItem
*pPrevItem
= pCurItem
;
2026 int nPrevEnd
= nCurEnd
;
2030 /* Check to see if previous character is a whitespace */
2031 if (flags
& FR_WHOLEWORD
)
2033 if (nPrevEnd
- nMatched
== 0)
2035 pPrevItem
= ME_FindItemBack(pCurItem
, diRun
);
2037 nPrevEnd
= pPrevItem
->member
.run
.len
+ nMatched
;
2041 wPrevChar
= *get_text( &pPrevItem
->member
.run
, nPrevEnd
- nMatched
- 1 );
2045 if (isalnumW(wPrevChar
))
2049 nStart
= pCurPara
->member
.para
.nCharOfs
2050 + pCurItem
->member
.run
.nCharOfs
+ nCurEnd
- nMatched
;
2053 chrgText
->cpMin
= nStart
;
2054 chrgText
->cpMax
= nStart
+ nLen
;
2056 TRACE("found at %d-%d\n", nStart
, nStart
+ nLen
);
2059 if (nCurEnd
- nMatched
== 0)
2061 ME_PrevRun(&pCurPara
, &pCurItem
, TRUE
);
2062 /* Don't care about pCurItem becoming NULL here; it's already taken
2063 * care of in the exterior loop condition */
2064 nCurEnd
= pCurItem
->member
.run
.len
+ nMatched
;
2068 wLastChar
= *get_text( &pCurItem
->member
.run
, nCurEnd
- nMatched
- 1 );
2073 if (cursor
.nOffset
< 0)
2075 ME_PrevRun(&cursor
.pPara
, &cursor
.pRun
, TRUE
);
2076 cursor
.nOffset
= cursor
.pRun
->member
.run
.len
;
2080 TRACE("not found\n");
2082 chrgText
->cpMin
= chrgText
->cpMax
= -1;
2086 static int ME_GetTextEx(ME_TextEditor
*editor
, GETTEXTEX
*ex
, LPARAM pText
)
2091 if (!ex
->cb
|| !pText
) return 0;
2093 if (ex
->flags
& ~(GT_SELECTION
| GT_USECRLF
))
2094 FIXME("GETTEXTEX flags 0x%08x not supported\n", ex
->flags
& ~(GT_SELECTION
| GT_USECRLF
));
2096 if (ex
->flags
& GT_SELECTION
)
2099 int nStartCur
= ME_GetSelectionOfs(editor
, &from
, &to
);
2100 start
= editor
->pCursors
[nStartCur
];
2105 ME_SetCursorToStart(editor
, &start
);
2108 if (ex
->codepage
== CP_UNICODE
)
2110 return ME_GetTextW(editor
, (LPWSTR
)pText
, ex
->cb
/ sizeof(WCHAR
) - 1,
2111 &start
, nChars
, ex
->flags
& GT_USECRLF
, FALSE
);
2115 /* potentially each char may be a CR, why calculate the exact value with O(N) when
2116 we can just take a bigger buffer? :)
2117 The above assumption still holds with CR/LF counters, since CR->CRLF expansion
2118 occurs only in richedit 2.0 mode, in which line breaks have only one CR
2120 int crlfmul
= (ex
->flags
& GT_USECRLF
) ? 2 : 1;
2125 buflen
= min(crlfmul
* nChars
, ex
->cb
- 1);
2126 buffer
= heap_alloc((buflen
+ 1) * sizeof(WCHAR
));
2128 nChars
= ME_GetTextW(editor
, buffer
, buflen
, &start
, nChars
, ex
->flags
& GT_USECRLF
, FALSE
);
2129 rc
= WideCharToMultiByte(ex
->codepage
, 0, buffer
, nChars
+ 1,
2130 (LPSTR
)pText
, ex
->cb
, ex
->lpDefaultChar
, ex
->lpUsedDefChar
);
2131 if (rc
) rc
--; /* do not count 0 terminator */
2138 static int ME_GetTextRange(ME_TextEditor
*editor
, WCHAR
*strText
,
2139 const ME_Cursor
*start
, int nLen
, BOOL unicode
)
2141 if (!strText
) return 0;
2143 return ME_GetTextW(editor
, strText
, INT_MAX
, start
, nLen
, FALSE
, FALSE
);
2146 WCHAR
*p
= ALLOC_N_OBJ(WCHAR
, nLen
+1);
2148 nChars
= ME_GetTextW(editor
, p
, nLen
, start
, nLen
, FALSE
, FALSE
);
2149 WideCharToMultiByte(CP_ACP
, 0, p
, nChars
+1, (char *)strText
,
2150 nLen
+1, NULL
, NULL
);
2156 static int handle_EM_EXSETSEL( ME_TextEditor
*editor
, int to
, int from
)
2160 TRACE("%d - %d\n", to
, from
);
2162 ME_InvalidateSelection( editor
);
2163 end
= ME_SetSelection( editor
, to
, from
);
2164 ME_InvalidateSelection( editor
);
2165 ITextHost_TxShowCaret( editor
->texthost
, FALSE
);
2166 ME_ShowCaret( editor
);
2167 ME_SendSelChange( editor
);
2172 typedef struct tagME_GlobalDestStruct
2176 } ME_GlobalDestStruct
;
2178 static DWORD CALLBACK
ME_ReadFromHGLOBALUnicode(DWORD_PTR dwCookie
, LPBYTE lpBuff
, LONG cb
, LONG
*pcb
)
2180 ME_GlobalDestStruct
*pData
= (ME_GlobalDestStruct
*)dwCookie
;
2185 pDest
= (WORD
*)lpBuff
;
2186 pSrc
= GlobalLock(pData
->hData
);
2187 for (i
= 0; i
<cb
&& pSrc
[pData
->nLength
+i
]; i
++) {
2188 pDest
[i
] = pSrc
[pData
->nLength
+i
];
2190 pData
->nLength
+= i
;
2192 GlobalUnlock(pData
->hData
);
2196 static DWORD CALLBACK
ME_ReadFromHGLOBALRTF(DWORD_PTR dwCookie
, LPBYTE lpBuff
, LONG cb
, LONG
*pcb
)
2198 ME_GlobalDestStruct
*pData
= (ME_GlobalDestStruct
*)dwCookie
;
2203 pSrc
= GlobalLock(pData
->hData
);
2204 for (i
= 0; i
<cb
&& pSrc
[pData
->nLength
+i
]; i
++) {
2205 pDest
[i
] = pSrc
[pData
->nLength
+i
];
2207 pData
->nLength
+= i
;
2209 GlobalUnlock(pData
->hData
);
2213 static BOOL
ME_Paste(ME_TextEditor
*editor
)
2217 ME_GlobalDestStruct gds
;
2218 UINT nRTFFormat
= RegisterClipboardFormatA("Rich Text Format");
2221 if (IsClipboardFormatAvailable(nRTFFormat
))
2222 cf
= nRTFFormat
, dwFormat
= SF_RTF
;
2223 else if (IsClipboardFormatAvailable(CF_UNICODETEXT
))
2224 cf
= CF_UNICODETEXT
, dwFormat
= SF_TEXT
|SF_UNICODE
;
2228 if (!OpenClipboard(editor
->hWnd
))
2230 gds
.hData
= GetClipboardData(cf
);
2232 es
.dwCookie
= (DWORD_PTR
)&gds
;
2233 es
.pfnCallback
= dwFormat
== SF_RTF
? ME_ReadFromHGLOBALRTF
: ME_ReadFromHGLOBALUnicode
;
2234 ME_StreamIn(editor
, dwFormat
|SFF_SELECTION
, &es
, FALSE
);
2240 static BOOL
ME_Copy(ME_TextEditor
*editor
, const ME_Cursor
*start
, int nChars
)
2242 LPDATAOBJECT dataObj
= NULL
;
2245 if (editor
->cPasswordMask
)
2246 return FALSE
; /* Copying or Cutting masked text isn't allowed */
2248 if(editor
->lpOleCallback
)
2251 range
.cpMin
= ME_GetCursorOfs(start
);
2252 range
.cpMax
= range
.cpMin
+ nChars
;
2253 hr
= IRichEditOleCallback_GetClipboardData(editor
->lpOleCallback
, &range
, RECO_COPY
, &dataObj
);
2255 if(FAILED(hr
) || !dataObj
)
2256 hr
= ME_GetDataObject(editor
, start
, nChars
, &dataObj
);
2258 hr
= OleSetClipboard(dataObj
);
2259 IDataObject_Release(dataObj
);
2261 return SUCCEEDED(hr
);
2264 /* helper to send a msg filter notification */
2266 ME_FilterEvent(ME_TextEditor
*editor
, UINT msg
, WPARAM
* wParam
, LPARAM
* lParam
)
2270 if (!editor
->hWnd
|| !editor
->hwndParent
) return FALSE
;
2271 msgf
.nmhdr
.hwndFrom
= editor
->hWnd
;
2272 msgf
.nmhdr
.idFrom
= GetWindowLongW(editor
->hWnd
, GWLP_ID
);
2273 msgf
.nmhdr
.code
= EN_MSGFILTER
;
2275 msgf
.wParam
= *wParam
;
2276 msgf
.lParam
= *lParam
;
2277 if (SendMessageW(editor
->hwndParent
, WM_NOTIFY
, msgf
.nmhdr
.idFrom
, (LPARAM
)&msgf
))
2279 *wParam
= msgf
.wParam
;
2280 *lParam
= msgf
.lParam
;
2281 msgf
.wParam
= *wParam
;
2286 static void ME_UpdateSelectionLinkAttribute(ME_TextEditor
*editor
)
2288 ME_DisplayItem
*startPara
, *endPara
;
2289 ME_DisplayItem
*prev_para
;
2290 ME_Cursor
*from
, *to
;
2294 if (!editor
->AutoURLDetect_bEnable
) return;
2296 ME_GetSelection(editor
, &from
, &to
);
2298 /* Find paragraph previous to the one that contains start cursor */
2299 startPara
= from
->pPara
;
2300 prev_para
= startPara
->member
.para
.prev_para
;
2301 if (prev_para
->type
== diParagraph
) startPara
= prev_para
;
2303 /* Find paragraph that contains end cursor */
2304 endPara
= to
->pPara
->member
.para
.next_para
;
2306 start
.pPara
= startPara
;
2307 start
.pRun
= ME_FindItemFwd(startPara
, diRun
);
2309 nChars
= endPara
->member
.para
.nCharOfs
- startPara
->member
.para
.nCharOfs
;
2311 ME_UpdateLinkAttribute(editor
, &start
, nChars
);
2315 ME_KeyDown(ME_TextEditor
*editor
, WORD nKey
)
2317 BOOL ctrl_is_down
= GetKeyState(VK_CONTROL
) & 0x8000;
2318 BOOL shift_is_down
= GetKeyState(VK_SHIFT
) & 0x8000;
2320 if (editor
->bMouseCaptured
)
2322 if (nKey
!= VK_SHIFT
&& nKey
!= VK_CONTROL
&& nKey
!= VK_MENU
)
2323 editor
->nSelectionType
= stPosition
;
2331 editor
->nUDArrowX
= -1;
2337 ME_CommitUndo(editor
); /* End coalesced undos for typed characters */
2338 ME_ArrowKey(editor
, nKey
, shift_is_down
, ctrl_is_down
);
2342 editor
->nUDArrowX
= -1;
2343 /* FIXME backspace and delete aren't the same, they act different wrt paragraph style of the merged paragraph */
2344 if (editor
->styleFlags
& ES_READONLY
)
2346 if (ME_IsSelection(editor
))
2348 ME_DeleteSelection(editor
);
2349 ME_CommitUndo(editor
);
2351 else if (nKey
== VK_DELETE
)
2353 /* Delete stops group typing.
2354 * (See MSDN remarks on EM_STOPGROUPTYPING message) */
2355 ME_DeleteTextAtCursor(editor
, 1, 1);
2356 ME_CommitUndo(editor
);
2358 else if (ME_ArrowKey(editor
, VK_LEFT
, FALSE
, FALSE
))
2360 BOOL bDeletionSucceeded
;
2361 /* Backspace can be grouped for a single undo */
2362 ME_ContinueCoalescingTransaction(editor
);
2363 bDeletionSucceeded
= ME_DeleteTextAtCursor(editor
, 1, 1);
2364 if (!bDeletionSucceeded
&& !editor
->bEmulateVersion10
) { /* v4.1 */
2365 /* Deletion was prevented so the cursor is moved back to where it was.
2366 * (e.g. this happens when trying to delete cell boundaries)
2368 ME_ArrowKey(editor
, VK_RIGHT
, FALSE
, FALSE
);
2370 ME_CommitCoalescingUndo(editor
);
2374 ME_MoveCursorFromTableRowStartParagraph(editor
);
2375 ME_UpdateSelectionLinkAttribute(editor
);
2376 ME_UpdateRepaint(editor
, FALSE
);
2377 ME_SendRequestResize(editor
, FALSE
);
2380 if (editor
->bDialogMode
)
2385 if (!(editor
->styleFlags
& ES_WANTRETURN
))
2387 if (editor
->hwndParent
)
2390 dw
= SendMessageW(editor
->hwndParent
, DM_GETDEFID
, 0, 0);
2391 if (HIWORD(dw
) == DC_HASDEFID
)
2393 HWND hwDefCtrl
= GetDlgItem(editor
->hwndParent
, LOWORD(dw
));
2396 SendMessageW(editor
->hwndParent
, WM_NEXTDLGCTL
, (WPARAM
)hwDefCtrl
, TRUE
);
2397 PostMessageW(hwDefCtrl
, WM_KEYDOWN
, VK_RETURN
, 0);
2405 if (editor
->styleFlags
& ES_MULTILINE
)
2407 ME_Cursor cursor
= editor
->pCursors
[0];
2408 ME_DisplayItem
*para
= cursor
.pPara
;
2410 const WCHAR endl
= '\r';
2411 const WCHAR endlv10
[] = {'\r','\n'};
2412 ME_Style
*style
, *eop_style
;
2414 if (editor
->styleFlags
& ES_READONLY
) {
2415 MessageBeep(MB_ICONERROR
);
2419 ME_GetSelectionOfs(editor
, &from
, &to
);
2420 if (editor
->nTextLimit
> ME_GetTextLength(editor
) - (to
-from
))
2422 if (!editor
->bEmulateVersion10
) { /* v4.1 */
2423 if (para
->member
.para
.nFlags
& MEPF_ROWEND
) {
2424 /* Add a new table row after this row. */
2425 para
= ME_AppendTableRow(editor
, para
);
2426 para
= para
->member
.para
.next_para
;
2427 editor
->pCursors
[0].pPara
= para
;
2428 editor
->pCursors
[0].pRun
= ME_FindItemFwd(para
, diRun
);
2429 editor
->pCursors
[0].nOffset
= 0;
2430 editor
->pCursors
[1] = editor
->pCursors
[0];
2431 ME_CommitUndo(editor
);
2432 ME_CheckTablesForCorruption(editor
);
2433 ME_UpdateRepaint(editor
, FALSE
);
2436 else if (para
== editor
->pCursors
[1].pPara
&&
2437 cursor
.nOffset
+ cursor
.pRun
->member
.run
.nCharOfs
== 0 &&
2438 para
->member
.para
.prev_para
->member
.para
.nFlags
& MEPF_ROWSTART
&&
2439 !para
->member
.para
.prev_para
->member
.para
.nCharOfs
)
2441 /* Insert a newline before the table. */
2442 para
= para
->member
.para
.prev_para
;
2443 para
->member
.para
.nFlags
&= ~MEPF_ROWSTART
;
2444 editor
->pCursors
[0].pPara
= para
;
2445 editor
->pCursors
[0].pRun
= ME_FindItemFwd(para
, diRun
);
2446 editor
->pCursors
[1] = editor
->pCursors
[0];
2447 ME_InsertTextFromCursor(editor
, 0, &endl
, 1,
2448 editor
->pCursors
[0].pRun
->member
.run
.style
);
2449 para
= editor
->pBuffer
->pFirst
->member
.para
.next_para
;
2450 ME_SetDefaultParaFormat(editor
, ¶
->member
.para
.fmt
);
2451 para
->member
.para
.nFlags
= MEPF_REWRAP
;
2452 editor
->pCursors
[0].pPara
= para
;
2453 editor
->pCursors
[0].pRun
= ME_FindItemFwd(para
, diRun
);
2454 editor
->pCursors
[1] = editor
->pCursors
[0];
2455 para
->member
.para
.next_para
->member
.para
.nFlags
|= MEPF_ROWSTART
;
2456 ME_CommitCoalescingUndo(editor
);
2457 ME_CheckTablesForCorruption(editor
);
2458 ME_UpdateRepaint(editor
, FALSE
);
2461 } else { /* v1.0 - 3.0 */
2462 ME_DisplayItem
*para
= cursor
.pPara
;
2463 if (ME_IsInTable(para
))
2465 if (cursor
.pRun
->member
.run
.nFlags
& MERF_ENDPARA
)
2468 ME_ContinueCoalescingTransaction(editor
);
2469 para
= ME_AppendTableRow(editor
, para
);
2470 editor
->pCursors
[0].pPara
= para
;
2471 editor
->pCursors
[0].pRun
= ME_FindItemFwd(para
, diRun
);
2472 editor
->pCursors
[0].nOffset
= 0;
2473 editor
->pCursors
[1] = editor
->pCursors
[0];
2474 ME_CommitCoalescingUndo(editor
);
2475 ME_UpdateRepaint(editor
, FALSE
);
2479 ME_ContinueCoalescingTransaction(editor
);
2480 if (cursor
.pRun
->member
.run
.nCharOfs
+ cursor
.nOffset
== 0 &&
2481 !ME_IsInTable(para
->member
.para
.prev_para
))
2483 /* Insert newline before table */
2484 cursor
.pRun
= ME_FindItemBack(para
, diRun
);
2486 editor
->pCursors
[0].pRun
= cursor
.pRun
;
2487 editor
->pCursors
[0].pPara
= para
->member
.para
.prev_para
;
2489 editor
->pCursors
[0].nOffset
= 0;
2490 editor
->pCursors
[1] = editor
->pCursors
[0];
2491 ME_InsertTextFromCursor(editor
, 0, &endl
, 1,
2492 editor
->pCursors
[0].pRun
->member
.run
.style
);
2494 editor
->pCursors
[1] = editor
->pCursors
[0];
2495 para
= ME_AppendTableRow(editor
, para
);
2496 editor
->pCursors
[0].pPara
= para
;
2497 editor
->pCursors
[0].pRun
= ME_FindItemFwd(para
, diRun
);
2498 editor
->pCursors
[0].nOffset
= 0;
2499 editor
->pCursors
[1] = editor
->pCursors
[0];
2501 ME_CommitCoalescingUndo(editor
);
2502 ME_UpdateRepaint(editor
, FALSE
);
2508 style
= ME_GetInsertStyle(editor
, 0);
2510 /* Normally the new eop style is the insert style, however in a list it is copied from the existing
2511 eop style (this prevents the list label style changing when the new eop is inserted).
2512 No extra ref is taken here on eop_style. */
2513 if (para
->member
.para
.fmt
.wNumbering
)
2514 eop_style
= para
->member
.para
.eop_run
->style
;
2517 ME_ContinueCoalescingTransaction(editor
);
2519 ME_InsertEndRowFromCursor(editor
, 0);
2521 if (!editor
->bEmulateVersion10
)
2522 ME_InsertTextFromCursor(editor
, 0, &endl
, 1, eop_style
);
2524 ME_InsertTextFromCursor(editor
, 0, endlv10
, 2, eop_style
);
2525 ME_CommitCoalescingUndo(editor
);
2528 ME_UpdateSelectionLinkAttribute(editor
);
2529 ME_UpdateRepaint(editor
, FALSE
);
2530 ME_SaveTempStyle(editor
, style
); /* set the temp insert style for the new para */
2531 ME_ReleaseStyle(style
);
2537 if (editor
->bDialogMode
&& editor
->hwndParent
)
2538 PostMessageW(editor
->hwndParent
, WM_CLOSE
, 0, 0);
2541 if (editor
->bDialogMode
&& editor
->hwndParent
)
2542 SendMessageW(editor
->hwndParent
, WM_NEXTDLGCTL
, shift_is_down
, 0);
2547 handle_EM_EXSETSEL( editor
, 0, -1 );
2553 return ME_Paste(editor
);
2561 int nStartCur
= ME_GetSelectionOfs(editor
, &nOfs
, &nChars
);
2562 ME_Cursor
*selStart
= &editor
->pCursors
[nStartCur
];
2565 result
= ME_Copy(editor
, selStart
, nChars
);
2566 if (result
&& nKey
== 'X')
2568 ME_InternalDeleteText(editor
, selStart
, nChars
, FALSE
);
2569 ME_CommitUndo(editor
);
2570 ME_UpdateRepaint(editor
, TRUE
);
2591 if (nKey
!= VK_SHIFT
&& nKey
!= VK_CONTROL
&& nKey
&& nKey
!= VK_MENU
)
2592 editor
->nUDArrowX
= -1;
2599 chf
.cbSize
= sizeof(chf
);
2601 ME_GetSelectionCharFormat(editor
, &chf
);
2602 ME_DumpStyleToBuf(&chf
, buf
);
2603 MessageBoxA(NULL
, buf
, "Style dump", MB_OK
);
2607 ME_CheckCharOffsets(editor
);
2614 static LRESULT
ME_Char(ME_TextEditor
*editor
, WPARAM charCode
,
2615 LPARAM flags
, BOOL unicode
)
2619 if (editor
->bMouseCaptured
)
2623 wstr
= (WCHAR
)charCode
;
2626 CHAR charA
= charCode
;
2627 MultiByteToWideChar(CP_ACP
, 0, &charA
, 1, &wstr
, 1);
2630 if (editor
->styleFlags
& ES_READONLY
) {
2631 MessageBeep(MB_ICONERROR
);
2632 return 0; /* FIXME really 0 ? */
2635 if ((unsigned)wstr
>= ' ' || wstr
== '\t')
2637 ME_Cursor cursor
= editor
->pCursors
[0];
2638 ME_DisplayItem
*para
= cursor
.pPara
;
2640 BOOL ctrl_is_down
= GetKeyState(VK_CONTROL
) & 0x8000;
2641 ME_GetSelectionOfs(editor
, &from
, &to
);
2643 /* v4.1 allows tabs to be inserted with ctrl key down */
2644 !(ctrl_is_down
&& !editor
->bEmulateVersion10
))
2646 ME_DisplayItem
*para
;
2647 BOOL bSelectedRow
= FALSE
;
2649 para
= cursor
.pPara
;
2650 if (ME_IsSelection(editor
) &&
2651 cursor
.pRun
->member
.run
.nCharOfs
+ cursor
.nOffset
== 0 &&
2652 to
== ME_GetCursorOfs(&editor
->pCursors
[0]) &&
2653 para
->member
.para
.prev_para
->type
== diParagraph
)
2655 para
= para
->member
.para
.prev_para
;
2656 bSelectedRow
= TRUE
;
2658 if (ME_IsInTable(para
))
2660 ME_TabPressedInTable(editor
, bSelectedRow
);
2661 ME_CommitUndo(editor
);
2664 } else if (!editor
->bEmulateVersion10
) { /* v4.1 */
2665 if (para
->member
.para
.nFlags
& MEPF_ROWEND
) {
2667 para
= para
->member
.para
.next_para
;
2668 if (para
->member
.para
.nFlags
& MEPF_ROWSTART
)
2669 para
= para
->member
.para
.next_para
;
2670 editor
->pCursors
[0].pPara
= para
;
2671 editor
->pCursors
[0].pRun
= ME_FindItemFwd(para
, diRun
);
2672 editor
->pCursors
[0].nOffset
= 0;
2673 editor
->pCursors
[1] = editor
->pCursors
[0];
2676 } else { /* v1.0 - 3.0 */
2677 if (ME_IsInTable(cursor
.pRun
) &&
2678 cursor
.pRun
->member
.run
.nFlags
& MERF_ENDPARA
&&
2681 /* Text should not be inserted at the end of the table. */
2686 /* FIXME maybe it would make sense to call EM_REPLACESEL instead ? */
2687 /* WM_CHAR is restricted to nTextLimit */
2688 if(editor
->nTextLimit
> ME_GetTextLength(editor
) - (to
-from
))
2690 ME_Style
*style
= ME_GetInsertStyle(editor
, 0);
2691 ME_ContinueCoalescingTransaction(editor
);
2692 ME_InsertTextFromCursor(editor
, 0, &wstr
, 1, style
);
2693 ME_ReleaseStyle(style
);
2694 ME_CommitCoalescingUndo(editor
);
2695 ITextHost_TxSetCursor(editor
->texthost
, NULL
, FALSE
);
2698 ME_UpdateSelectionLinkAttribute(editor
);
2699 ME_UpdateRepaint(editor
, FALSE
);
2704 /* Process the message and calculate the new click count.
2706 * returns: The click count if it is mouse down event, else returns 0. */
2707 static int ME_CalculateClickCount(ME_TextEditor
*editor
, UINT msg
, WPARAM wParam
,
2710 static int clickNum
= 0;
2711 if (msg
< WM_MOUSEFIRST
|| msg
> WM_MOUSELAST
)
2714 if ((msg
== WM_LBUTTONDBLCLK
) ||
2715 (msg
== WM_RBUTTONDBLCLK
) ||
2716 (msg
== WM_MBUTTONDBLCLK
) ||
2717 (msg
== WM_XBUTTONDBLCLK
))
2719 msg
-= (WM_LBUTTONDBLCLK
- WM_LBUTTONDOWN
);
2722 if ((msg
== WM_LBUTTONDOWN
) ||
2723 (msg
== WM_RBUTTONDOWN
) ||
2724 (msg
== WM_MBUTTONDOWN
) ||
2725 (msg
== WM_XBUTTONDOWN
))
2727 static MSG prevClickMsg
;
2729 /* Compare the editor instead of the hwnd so that the this
2730 * can still be done for windowless richedit controls. */
2731 clickMsg
.hwnd
= (HWND
)editor
;
2732 clickMsg
.message
= msg
;
2733 clickMsg
.wParam
= wParam
;
2734 clickMsg
.lParam
= lParam
;
2735 clickMsg
.time
= GetMessageTime();
2736 clickMsg
.pt
.x
= (short)LOWORD(lParam
);
2737 clickMsg
.pt
.y
= (short)HIWORD(lParam
);
2738 if ((clickNum
!= 0) &&
2739 (clickMsg
.message
== prevClickMsg
.message
) &&
2740 (clickMsg
.hwnd
== prevClickMsg
.hwnd
) &&
2741 (clickMsg
.wParam
== prevClickMsg
.wParam
) &&
2742 (clickMsg
.time
- prevClickMsg
.time
< GetDoubleClickTime()) &&
2743 (abs(clickMsg
.pt
.x
- prevClickMsg
.pt
.x
) < GetSystemMetrics(SM_CXDOUBLECLK
)/2) &&
2744 (abs(clickMsg
.pt
.y
- prevClickMsg
.pt
.y
) < GetSystemMetrics(SM_CYDOUBLECLK
)/2))
2750 prevClickMsg
= clickMsg
;
2757 static BOOL
is_link( ME_Run
*run
)
2759 return (run
->style
->fmt
.dwMask
& CFM_LINK
) && (run
->style
->fmt
.dwEffects
& CFE_LINK
);
2762 static BOOL
ME_SetCursor(ME_TextEditor
*editor
)
2768 DWORD messagePos
= GetMessagePos();
2769 pt
.x
= (short)LOWORD(messagePos
);
2770 pt
.y
= (short)HIWORD(messagePos
);
2774 sbi
.cbSize
= sizeof(sbi
);
2775 GetScrollBarInfo(editor
->hWnd
, OBJID_HSCROLL
, &sbi
);
2776 if (!(sbi
.rgstate
[0] & (STATE_SYSTEM_INVISIBLE
|STATE_SYSTEM_OFFSCREEN
)) &&
2777 PtInRect(&sbi
.rcScrollBar
, pt
))
2779 ITextHost_TxSetCursor(editor
->texthost
,
2780 LoadCursorW(NULL
, (WCHAR
*)IDC_ARROW
), FALSE
);
2783 sbi
.cbSize
= sizeof(sbi
);
2784 GetScrollBarInfo(editor
->hWnd
, OBJID_VSCROLL
, &sbi
);
2785 if (!(sbi
.rgstate
[0] & (STATE_SYSTEM_INVISIBLE
|STATE_SYSTEM_OFFSCREEN
)) &&
2786 PtInRect(&sbi
.rcScrollBar
, pt
))
2788 ITextHost_TxSetCursor(editor
->texthost
,
2789 LoadCursorW(NULL
, (WCHAR
*)IDC_ARROW
), FALSE
);
2793 ITextHost_TxScreenToClient(editor
->texthost
, &pt
);
2795 if (editor
->nSelectionType
== stLine
&& editor
->bMouseCaptured
) {
2796 ITextHost_TxSetCursor(editor
->texthost
, hLeft
, FALSE
);
2799 if (!editor
->bEmulateVersion10
/* v4.1 */ &&
2800 pt
.y
< editor
->rcFormat
.top
&&
2801 pt
.x
< editor
->rcFormat
.left
)
2803 ITextHost_TxSetCursor(editor
->texthost
, hLeft
, FALSE
);
2806 if (pt
.y
< editor
->rcFormat
.top
|| pt
.y
> editor
->rcFormat
.bottom
)
2808 if (editor
->bEmulateVersion10
) /* v1.0 - 3.0 */
2809 ITextHost_TxSetCursor(editor
->texthost
,
2810 LoadCursorW(NULL
, (WCHAR
*)IDC_ARROW
), FALSE
);
2812 ITextHost_TxSetCursor(editor
->texthost
,
2813 LoadCursorW(NULL
, (WCHAR
*)IDC_IBEAM
), TRUE
);
2816 if (pt
.x
< editor
->rcFormat
.left
)
2818 ITextHost_TxSetCursor(editor
->texthost
, hLeft
, FALSE
);
2821 ME_CharFromPos(editor
, pt
.x
, pt
.y
, &cursor
, &isExact
);
2826 run
= &cursor
.pRun
->member
.run
;
2829 ITextHost_TxSetCursor(editor
->texthost
,
2830 LoadCursorW(NULL
, (WCHAR
*)IDC_HAND
),
2835 if (ME_IsSelection(editor
))
2837 int selStart
, selEnd
;
2838 int offset
= ME_GetCursorOfs(&cursor
);
2840 ME_GetSelectionOfs(editor
, &selStart
, &selEnd
);
2841 if (selStart
<= offset
&& selEnd
>= offset
) {
2842 ITextHost_TxSetCursor(editor
->texthost
,
2843 LoadCursorW(NULL
, (WCHAR
*)IDC_ARROW
),
2849 ITextHost_TxSetCursor(editor
->texthost
,
2850 LoadCursorW(NULL
, (WCHAR
*)IDC_IBEAM
), TRUE
);
2854 static void ME_SetDefaultFormatRect(ME_TextEditor
*editor
)
2856 ITextHost_TxGetClientRect(editor
->texthost
, &editor
->rcFormat
);
2857 editor
->rcFormat
.top
+= editor
->exStyleFlags
& WS_EX_CLIENTEDGE
? 1 : 0;
2858 editor
->rcFormat
.left
+= 1 + editor
->selofs
;
2859 editor
->rcFormat
.right
-= 1;
2862 static BOOL
ME_ShowContextMenu(ME_TextEditor
*editor
, int x
, int y
)
2867 if(!editor
->lpOleCallback
|| !editor
->hWnd
)
2869 ME_GetSelectionOfs(editor
, &selrange
.cpMin
, &selrange
.cpMax
);
2870 if(selrange
.cpMin
== selrange
.cpMax
)
2871 seltype
|= SEL_EMPTY
;
2874 /* FIXME: Handle objects */
2875 seltype
|= SEL_TEXT
;
2876 if(selrange
.cpMax
-selrange
.cpMin
> 1)
2877 seltype
|= SEL_MULTICHAR
;
2879 if(SUCCEEDED(IRichEditOleCallback_GetContextMenu(editor
->lpOleCallback
, seltype
, NULL
, &selrange
, &menu
)))
2881 TrackPopupMenu(menu
, TPM_LEFTALIGN
| TPM_RIGHTBUTTON
, x
, y
, 0, editor
->hwndParent
, NULL
);
2887 ME_TextEditor
*ME_MakeEditor(ITextHost
*texthost
, BOOL bEmulateVersion10
, DWORD csStyle
)
2889 ME_TextEditor
*ed
= ALLOC_OBJ(ME_TextEditor
);
2895 ed
->hwndParent
= NULL
;
2896 ed
->sizeWindow
.cx
= ed
->sizeWindow
.cy
= 0;
2897 ed
->texthost
= texthost
;
2899 ed
->bEmulateVersion10
= bEmulateVersion10
;
2901 ed
->alignStyle
= PFA_LEFT
;
2902 if (csStyle
& ES_RIGHT
)
2903 ed
->alignStyle
= PFA_RIGHT
;
2904 if (csStyle
& ES_CENTER
)
2905 ed
->alignStyle
= PFA_CENTER
;
2906 ITextHost_TxGetPropertyBits(texthost
,
2907 (TXTBIT_RICHTEXT
|TXTBIT_MULTILINE
|
2908 TXTBIT_READONLY
|TXTBIT_USEPASSWORD
|
2909 TXTBIT_HIDESELECTION
|TXTBIT_SAVESELECTION
|
2910 TXTBIT_AUTOWORDSEL
|TXTBIT_VERTICAL
|
2911 TXTBIT_WORDWRAP
|TXTBIT_DISABLEDRAG
),
2913 ITextHost_TxGetScrollBars(texthost
, &ed
->styleFlags
);
2914 ed
->styleFlags
&= (WS_VSCROLL
|WS_HSCROLL
|ES_AUTOVSCROLL
|
2915 ES_AUTOHSCROLL
|ES_DISABLENOSCROLL
);
2916 ed
->pBuffer
= ME_MakeText();
2917 ed
->nZoomNumerator
= ed
->nZoomDenominator
= 0;
2918 ed
->nAvailWidth
= 0; /* wrap to client area */
2919 ME_MakeFirstParagraph(ed
);
2920 /* The four cursors are for:
2921 * 0 - The position where the caret is shown
2922 * 1 - The anchored end of the selection (for normal selection)
2923 * 2 & 3 - The anchored start and end respectively for word, line,
2924 * or paragraph selection.
2927 ed
->pCursors
= ALLOC_N_OBJ(ME_Cursor
, ed
->nCursors
);
2928 ME_SetCursorToStart(ed
, &ed
->pCursors
[0]);
2929 ed
->pCursors
[1] = ed
->pCursors
[0];
2930 ed
->pCursors
[2] = ed
->pCursors
[0];
2931 ed
->pCursors
[3] = ed
->pCursors
[1];
2932 ed
->nLastTotalLength
= ed
->nTotalLength
= 0;
2933 ed
->nLastTotalWidth
= ed
->nTotalWidth
= 0;
2935 ed
->rgbBackColor
= -1;
2936 ed
->hbrBackground
= GetSysColorBrush(COLOR_WINDOW
);
2937 ed
->bCaretAtEnd
= FALSE
;
2939 ed
->nModifyStep
= 0;
2940 ed
->nTextLimit
= TEXT_LIMIT_DEFAULT
;
2941 list_init( &ed
->undo_stack
);
2942 list_init( &ed
->redo_stack
);
2943 ed
->nUndoStackSize
= 0;
2944 ed
->nUndoLimit
= STACK_SIZE_DEFAULT
;
2945 ed
->nUndoMode
= umAddToUndo
;
2946 ed
->nParagraphs
= 1;
2947 ed
->nLastSelStart
= ed
->nLastSelEnd
= 0;
2948 ed
->pLastSelStartPara
= ed
->pLastSelEndPara
= ed
->pCursors
[0].pPara
;
2949 ed
->bHideSelection
= FALSE
;
2950 ed
->pfnWordBreak
= NULL
;
2951 ed
->lpOleCallback
= NULL
;
2952 ed
->mode
= TM_MULTILEVELUNDO
| TM_MULTICODEPAGE
;
2953 ed
->mode
|= (props
& TXTBIT_RICHTEXT
) ? TM_RICHTEXT
: TM_PLAINTEXT
;
2954 ed
->AutoURLDetect_bEnable
= FALSE
;
2955 ed
->bHaveFocus
= FALSE
;
2956 ed
->bDialogMode
= FALSE
;
2957 ed
->bMouseCaptured
= FALSE
;
2958 for (i
=0; i
<HFONT_CACHE_SIZE
; i
++)
2960 ed
->pFontCache
[i
].nRefs
= 0;
2961 ed
->pFontCache
[i
].nAge
= 0;
2962 ed
->pFontCache
[i
].hFont
= NULL
;
2965 ME_CheckCharOffsets(ed
);
2966 ed
->bDefaultFormatRect
= TRUE
;
2967 ITextHost_TxGetSelectionBarWidth(ed
->texthost
, &selbarwidth
);
2969 /* FIXME: Convert selbarwidth from HIMETRIC to pixels */
2970 ed
->selofs
= SELECTIONBAR_WIDTH
;
2971 ed
->styleFlags
|= ES_SELECTIONBAR
;
2975 ed
->nSelectionType
= stPosition
;
2977 ed
->cPasswordMask
= 0;
2978 if (props
& TXTBIT_USEPASSWORD
)
2979 ITextHost_TxGetPasswordChar(texthost
, &ed
->cPasswordMask
);
2981 if (props
& TXTBIT_AUTOWORDSEL
)
2982 ed
->styleFlags
|= ECO_AUTOWORDSELECTION
;
2983 if (props
& TXTBIT_MULTILINE
) {
2984 ed
->styleFlags
|= ES_MULTILINE
;
2985 ed
->bWordWrap
= (props
& TXTBIT_WORDWRAP
) != 0;
2987 ed
->bWordWrap
= FALSE
;
2989 if (props
& TXTBIT_READONLY
)
2990 ed
->styleFlags
|= ES_READONLY
;
2991 if (!(props
& TXTBIT_HIDESELECTION
))
2992 ed
->styleFlags
|= ES_NOHIDESEL
;
2993 if (props
& TXTBIT_SAVESELECTION
)
2994 ed
->styleFlags
|= ES_SAVESEL
;
2995 if (props
& TXTBIT_VERTICAL
)
2996 ed
->styleFlags
|= ES_VERTICAL
;
2997 if (props
& TXTBIT_DISABLEDRAG
)
2998 ed
->styleFlags
|= ES_NOOLEDRAGDROP
;
3000 ed
->notified_cr
.cpMin
= ed
->notified_cr
.cpMax
= 0;
3002 /* Default scrollbar information */
3003 ed
->vert_si
.cbSize
= sizeof(SCROLLINFO
);
3004 ed
->vert_si
.nMin
= 0;
3005 ed
->vert_si
.nMax
= 0;
3006 ed
->vert_si
.nPage
= 0;
3007 ed
->vert_si
.nPos
= 0;
3009 ed
->horz_si
.cbSize
= sizeof(SCROLLINFO
);
3010 ed
->horz_si
.nMin
= 0;
3011 ed
->horz_si
.nMax
= 0;
3012 ed
->horz_si
.nPage
= 0;
3013 ed
->horz_si
.nPos
= 0;
3015 ed
->wheel_remain
= 0;
3017 list_init( &ed
->style_list
);
3018 OleInitialize(NULL
);
3023 void ME_DestroyEditor(ME_TextEditor
*editor
)
3025 ME_DisplayItem
*pFirst
= editor
->pBuffer
->pFirst
;
3026 ME_DisplayItem
*p
= pFirst
, *pNext
= NULL
;
3027 ME_Style
*s
, *cursor2
;
3030 ME_ClearTempStyle(editor
);
3031 ME_EmptyUndoStack(editor
);
3034 ME_DestroyDisplayItem(p
);
3038 LIST_FOR_EACH_ENTRY_SAFE( s
, cursor2
, &editor
->style_list
, ME_Style
, entry
)
3039 ME_DestroyStyle( s
);
3041 ME_ReleaseStyle(editor
->pBuffer
->pDefaultStyle
);
3042 for (i
=0; i
<HFONT_CACHE_SIZE
; i
++)
3044 if (editor
->pFontCache
[i
].hFont
)
3045 DeleteObject(editor
->pFontCache
[i
].hFont
);
3047 if (editor
->rgbBackColor
!= -1)
3048 DeleteObject(editor
->hbrBackground
);
3049 if(editor
->lpOleCallback
)
3050 IRichEditOleCallback_Release(editor
->lpOleCallback
);
3051 ITextHost_Release(editor
->texthost
);
3054 IRichEditOle_Release(editor
->reOle
);
3055 editor
->reOle
= NULL
;
3059 FREE_OBJ(editor
->pBuffer
);
3060 FREE_OBJ(editor
->pCursors
);
3065 BOOL WINAPI
DllMain(HINSTANCE hinstDLL
, DWORD fdwReason
, LPVOID lpvReserved
)
3070 case DLL_PROCESS_ATTACH
:
3071 DisableThreadLibraryCalls(hinstDLL
);
3072 me_heap
= HeapCreate (0, 0x10000, 0);
3073 if (!ME_RegisterEditorClass(hinstDLL
)) return FALSE
;
3074 hLeft
= LoadCursorW(hinstDLL
, MAKEINTRESOURCEW(OCR_REVERSE
));
3078 case DLL_PROCESS_DETACH
:
3079 if (lpvReserved
) break;
3080 UnregisterClassW(RICHEDIT_CLASS20W
, 0);
3081 UnregisterClassW(MSFTEDIT_CLASS
, 0);
3082 UnregisterClassA(RICHEDIT_CLASS20A
, 0);
3083 UnregisterClassA("RichEdit50A", 0);
3084 if (ME_ListBoxRegistered
)
3085 UnregisterClassW(REListBox20W
, 0);
3086 if (ME_ComboBoxRegistered
)
3087 UnregisterClassW(REComboBox20W
, 0);
3089 HeapDestroy (me_heap
);
3096 static inline int get_default_line_height( ME_TextEditor
*editor
)
3100 if (editor
->pBuffer
&& editor
->pBuffer
->pDefaultStyle
)
3101 height
= editor
->pBuffer
->pDefaultStyle
->tm
.tmHeight
;
3102 if (height
<= 0) height
= 24;
3107 static inline int calc_wheel_change( int *remain
, int amount_per_click
)
3109 int change
= amount_per_click
* (float)*remain
/ WHEEL_DELTA
;
3110 *remain
-= WHEEL_DELTA
* change
/ amount_per_click
;
3114 static const char * const edit_messages
[] = {
3143 "EM_SETPASSWORDCHAR",
3144 "EM_EMPTYUNDOBUFFER",
3145 "EM_GETFIRSTVISIBLELINE",
3147 "EM_SETWORDBREAKPROC",
3148 "EM_GETWORDBREAKPROC",
3149 "EM_GETPASSWORDCHAR",
3159 static const char * const richedit_messages
[] = {
3164 "EM_EXLINEFROMCHAR",
3170 "EM_GETOLEINTERFACE",
3180 "EM_SETOLECALLBACK",
3182 "EM_SETTARGETDEVICE",
3190 "EM_GETWORDBREAKPROCEX",
3191 "EM_SETWORDBREAKPROCEX",
3193 "EM_UNKNOWN_USER_83",
3198 "EM_STOPGROUPTYPING",
3202 "EM_GETAUTOURLDETECT",
3205 "EM_GETTEXTLENGTHEX",
3208 "EM_UNKNOWN_USER_98",
3209 "EM_UNKNOWN_USER_99",
3210 "EM_SETPUNCTUATION",
3211 "EM_GETPUNCTUATION",
3212 "EM_SETWORDWRAPMODE",
3213 "EM_GETWORDWRAPMODE",
3219 "EM_UNKNOWN_USER_109",
3220 "EM_UNKNOWN_USER_110",
3221 "EM_UNKNOWN_USER_111",
3222 "EM_UNKNOWN_USER_112",
3223 "EM_UNKNOWN_USER_113",
3224 "EM_UNKNOWN_USER_114",
3225 "EM_UNKNOWN_USER_115",
3226 "EM_UNKNOWN_USER_116",
3227 "EM_UNKNOWN_USER_117",
3228 "EM_UNKNOWN_USER_118",
3229 "EM_UNKNOWN_USER_119",
3230 "EM_SETLANGOPTIONS",
3231 "EM_GETLANGOPTIONS",
3232 "EM_GETIMECOMPMODE",
3236 "EM_SETIMEMODEBIAS",
3241 get_msg_name(UINT msg
)
3243 if (msg
>= EM_GETSEL
&& msg
<= EM_CHARFROMPOS
)
3244 return edit_messages
[msg
- EM_GETSEL
];
3245 if (msg
>= EM_CANPASTE
&& msg
<= EM_GETIMEMODEBIAS
)
3246 return richedit_messages
[msg
- EM_CANPASTE
];
3250 static void ME_LinkNotify(ME_TextEditor
*editor
, UINT msg
, WPARAM wParam
, LPARAM lParam
)
3254 ME_Cursor cursor
; /* The start of the clicked text. */
3257 x
= (short)LOWORD(lParam
);
3258 y
= (short)HIWORD(lParam
);
3259 ME_CharFromPos(editor
, x
, y
, &cursor
, &isExact
);
3260 if (!isExact
) return;
3262 if (is_link( &cursor
.pRun
->member
.run
))
3263 { /* The clicked run has CFE_LINK set */
3266 info
.nmhdr
.hwndFrom
= NULL
;
3267 info
.nmhdr
.idFrom
= 0;
3268 info
.nmhdr
.code
= EN_LINK
;
3270 info
.wParam
= wParam
;
3271 info
.lParam
= lParam
;
3274 /* find the first contiguous run with CFE_LINK set */
3275 info
.chrg
.cpMin
= ME_GetCursorOfs(&cursor
);
3277 while (ME_PrevRun( NULL
, &di
, FALSE
) && is_link( &di
->member
.run
))
3278 info
.chrg
.cpMin
-= di
->member
.run
.len
;
3280 /* find the last contiguous run with CFE_LINK set */
3281 info
.chrg
.cpMax
= ME_GetCursorOfs(&cursor
) + cursor
.pRun
->member
.run
.len
;
3283 while (ME_NextRun( NULL
, &di
, FALSE
) && is_link( &di
->member
.run
))
3284 info
.chrg
.cpMax
+= di
->member
.run
.len
;
3286 ITextHost_TxNotify(editor
->texthost
, info
.nmhdr
.code
, &info
);
3290 void ME_ReplaceSel(ME_TextEditor
*editor
, BOOL can_undo
, const WCHAR
*str
, int len
)
3292 int from
, to
, nStartCursor
;
3295 nStartCursor
= ME_GetSelectionOfs(editor
, &from
, &to
);
3296 style
= ME_GetSelectionInsertStyle(editor
);
3297 ME_InternalDeleteText(editor
, &editor
->pCursors
[nStartCursor
], to
-from
, FALSE
);
3298 ME_InsertTextFromCursor(editor
, 0, str
, len
, style
);
3299 ME_ReleaseStyle(style
);
3300 /* drop temporary style if line end */
3302 * FIXME question: does abc\n mean: put abc,
3303 * clear temp style, put \n? (would require a change)
3305 if (len
>0 && str
[len
-1] == '\n')
3306 ME_ClearTempStyle(editor
);
3307 ME_CommitUndo(editor
);
3308 ME_UpdateSelectionLinkAttribute(editor
);
3310 ME_EmptyUndoStack(editor
);
3311 ME_UpdateRepaint(editor
, FALSE
);
3314 static void ME_SetText(ME_TextEditor
*editor
, void *text
, BOOL unicode
)
3316 LONG codepage
= unicode
? CP_UNICODE
: CP_ACP
;
3319 LPWSTR wszText
= ME_ToUnicode(codepage
, text
, &textLen
);
3325 /* uses default style! */
3326 if (!(editor
->styleFlags
& ES_MULTILINE
))
3330 while (*p
!= '\0' && *p
!= '\r' && *p
!= '\n') p
++;
3333 ME_InsertTextFromCursor(editor
, 0, wszText
, len
, editor
->pBuffer
->pDefaultStyle
);
3335 ME_EndToUnicode(codepage
, wszText
);
3338 static LRESULT
ME_WmCreate(ME_TextEditor
*editor
, LPARAM lParam
, BOOL unicode
)
3340 CREATESTRUCTW
*createW
= (CREATESTRUCTW
*)lParam
;
3341 CREATESTRUCTA
*createA
= (CREATESTRUCTA
*)lParam
;
3346 text
= unicode
? (void*)createW
->lpszName
: (void*)createA
->lpszName
;
3348 ME_SetDefaultFormatRect(editor
);
3350 max
= (editor
->styleFlags
& ES_DISABLENOSCROLL
) ? 1 : 0;
3351 if (~editor
->styleFlags
& ES_DISABLENOSCROLL
|| editor
->styleFlags
& WS_VSCROLL
)
3352 ITextHost_TxSetScrollRange(editor
->texthost
, SB_VERT
, 0, max
, TRUE
);
3354 if (~editor
->styleFlags
& ES_DISABLENOSCROLL
|| editor
->styleFlags
& WS_HSCROLL
)
3355 ITextHost_TxSetScrollRange(editor
->texthost
, SB_HORZ
, 0, max
, TRUE
);
3357 if (editor
->styleFlags
& ES_DISABLENOSCROLL
)
3359 if (editor
->styleFlags
& WS_VSCROLL
)
3361 ITextHost_TxEnableScrollBar(editor
->texthost
, SB_VERT
, ESB_DISABLE_BOTH
);
3362 ITextHost_TxShowScrollBar(editor
->texthost
, SB_VERT
, TRUE
);
3364 if (editor
->styleFlags
& WS_HSCROLL
)
3366 ITextHost_TxEnableScrollBar(editor
->texthost
, SB_HORZ
, ESB_DISABLE_BOTH
);
3367 ITextHost_TxShowScrollBar(editor
->texthost
, SB_HORZ
, TRUE
);
3373 ME_SetText(editor
, text
, unicode
);
3374 ME_SetCursorToStart(editor
, &editor
->pCursors
[0]);
3375 ME_SetCursorToStart(editor
, &editor
->pCursors
[1]);
3378 ME_CommitUndo(editor
);
3379 ME_WrapMarkedParagraphs(editor
);
3380 ME_MoveCaret(editor
);
3385 #define UNSUPPORTED_MSG(e) \
3387 FIXME(#e ": stub\n"); \
3388 *phresult = S_FALSE; \
3391 /* Handle messages for windowless and windowed richedit controls.
3393 * The LRESULT that is returned is a return value for window procs,
3394 * and the phresult parameter is the COM return code needed by the
3395 * text services interface. */
3396 LRESULT
ME_HandleMessage(ME_TextEditor
*editor
, UINT msg
, WPARAM wParam
,
3397 LPARAM lParam
, BOOL unicode
, HRESULT
* phresult
)
3403 UNSUPPORTED_MSG(EM_DISPLAYBAND
)
3404 UNSUPPORTED_MSG(EM_FINDWORDBREAK
)
3405 UNSUPPORTED_MSG(EM_FMTLINES
)
3406 UNSUPPORTED_MSG(EM_FORMATRANGE
)
3407 UNSUPPORTED_MSG(EM_GETBIDIOPTIONS
)
3408 UNSUPPORTED_MSG(EM_GETEDITSTYLE
)
3409 UNSUPPORTED_MSG(EM_GETIMECOMPMODE
)
3410 UNSUPPORTED_MSG(EM_GETIMESTATUS
)
3411 UNSUPPORTED_MSG(EM_SETIMESTATUS
)
3412 UNSUPPORTED_MSG(EM_GETLANGOPTIONS
)
3413 UNSUPPORTED_MSG(EM_GETREDONAME
)
3414 UNSUPPORTED_MSG(EM_GETTYPOGRAPHYOPTIONS
)
3415 UNSUPPORTED_MSG(EM_GETUNDONAME
)
3416 UNSUPPORTED_MSG(EM_GETWORDBREAKPROCEX
)
3417 UNSUPPORTED_MSG(EM_PASTESPECIAL
)
3418 UNSUPPORTED_MSG(EM_SELECTIONTYPE
)
3419 UNSUPPORTED_MSG(EM_SETBIDIOPTIONS
)
3420 UNSUPPORTED_MSG(EM_SETEDITSTYLE
)
3421 UNSUPPORTED_MSG(EM_SETLANGOPTIONS
)
3422 UNSUPPORTED_MSG(EM_SETMARGINS
)
3423 UNSUPPORTED_MSG(EM_SETPALETTE
)
3424 UNSUPPORTED_MSG(EM_SETTABSTOPS
)
3425 UNSUPPORTED_MSG(EM_SETTYPOGRAPHYOPTIONS
)
3426 UNSUPPORTED_MSG(EM_SETWORDBREAKPROCEX
)
3428 /* Messages specific to Richedit controls */
3431 return ME_StreamIn(editor
, wParam
, (EDITSTREAM
*)lParam
, TRUE
);
3433 return ME_StreamOut(editor
, wParam
, (EDITSTREAM
*)lParam
);
3436 UINT code
= DLGC_WANTCHARS
|DLGC_WANTTAB
|DLGC_WANTARROWS
;
3439 editor
->bDialogMode
= TRUE
;
3440 if (editor
->styleFlags
& ES_MULTILINE
)
3441 code
|= DLGC_WANTMESSAGE
;
3442 if (!(editor
->styleFlags
& ES_SAVESEL
))
3443 code
|= DLGC_HASSETSEL
;
3446 case EM_EMPTYUNDOBUFFER
:
3447 ME_EmptyUndoStack(editor
);
3451 /* Note: wParam/lParam can be NULL */
3453 PUINT pfrom
= wParam
? (PUINT
)wParam
: &from
;
3454 PUINT pto
= lParam
? (PUINT
)lParam
: &to
;
3455 ME_GetSelectionOfs(editor
, (int *)pfrom
, (int *)pto
);
3456 if ((*pfrom
|*pto
) & 0xFFFF0000)
3458 return MAKELONG(*pfrom
,*pto
);
3462 CHARRANGE
*pRange
= (CHARRANGE
*)lParam
;
3463 ME_GetSelectionOfs(editor
, &pRange
->cpMin
, &pRange
->cpMax
);
3464 TRACE("EM_EXGETSEL = (%d,%d)\n", pRange
->cpMin
, pRange
->cpMax
);
3467 case EM_SETUNDOLIMIT
:
3469 if ((int)wParam
< 0)
3470 editor
->nUndoLimit
= STACK_SIZE_DEFAULT
;
3472 editor
->nUndoLimit
= min(wParam
, STACK_SIZE_MAX
);
3473 /* Setting a max stack size keeps wine from getting killed
3474 for hogging memory. Windows allocates all this memory at once, so
3475 no program would realistically set a value above our maximum. */
3476 return editor
->nUndoLimit
;
3479 return !list_empty( &editor
->undo_stack
);
3481 return !list_empty( &editor
->redo_stack
);
3482 case WM_UNDO
: /* FIXME: actually not the same */
3484 return ME_Undo(editor
);
3486 return ME_Redo(editor
);
3489 /* these flags are equivalent to the ES_* counterparts */
3490 DWORD mask
= ECO_VERTICAL
| ECO_AUTOHSCROLL
| ECO_AUTOVSCROLL
|
3491 ECO_NOHIDESEL
| ECO_READONLY
| ECO_WANTRETURN
| ECO_SELECTIONBAR
;
3492 DWORD settings
= editor
->styleFlags
& mask
;
3496 case EM_SETFONTSIZE
:
3499 LONG tmp_size
, size
;
3500 BOOL is_increase
= ((LONG
)wParam
> 0);
3502 if (editor
->mode
& TM_PLAINTEXT
)
3505 cf
.cbSize
= sizeof(cf
);
3506 cf
.dwMask
= CFM_SIZE
;
3507 ME_GetSelectionCharFormat(editor
, &cf
);
3508 tmp_size
= (cf
.yHeight
/ 20) + wParam
;
3512 else if (tmp_size
> 12 && tmp_size
< 28 && tmp_size
% 2)
3513 size
= tmp_size
+ (is_increase
? 1 : -1);
3514 else if (tmp_size
> 28 && tmp_size
< 36)
3515 size
= is_increase
? 36 : 28;
3516 else if (tmp_size
> 36 && tmp_size
< 48)
3517 size
= is_increase
? 48 : 36;
3518 else if (tmp_size
> 48 && tmp_size
< 72)
3519 size
= is_increase
? 72 : 48;
3520 else if (tmp_size
> 72 && tmp_size
< 80)
3521 size
= is_increase
? 80 : 72;
3522 else if (tmp_size
> 80 && tmp_size
< 1638)
3523 size
= 10 * (is_increase
? (tmp_size
/ 10 + 1) : (tmp_size
/ 10));
3524 else if (tmp_size
>= 1638)
3529 cf
.yHeight
= size
* 20; /* convert twips to points */
3530 ME_SetSelectionCharFormat(editor
, &cf
);
3531 ME_CommitUndo(editor
);
3532 ME_WrapMarkedParagraphs(editor
);
3533 ME_UpdateScrollBar(editor
);
3540 /* these flags are equivalent to ES_* counterparts, except for
3541 * ECO_AUTOWORDSELECTION that doesn't have an ES_* counterpart,
3542 * but is still stored in editor->styleFlags. */
3543 const DWORD mask
= ECO_VERTICAL
| ECO_AUTOHSCROLL
| ECO_AUTOVSCROLL
|
3544 ECO_NOHIDESEL
| ECO_READONLY
| ECO_WANTRETURN
|
3545 ECO_SELECTIONBAR
| ECO_AUTOWORDSELECTION
;
3546 DWORD settings
= mask
& editor
->styleFlags
;
3547 DWORD oldSettings
= settings
;
3548 DWORD changedSettings
;
3564 changedSettings
= oldSettings
^ settings
;
3566 if (changedSettings
) {
3567 editor
->styleFlags
= (editor
->styleFlags
& ~mask
) | (settings
& mask
);
3569 if (changedSettings
& ECO_SELECTIONBAR
)
3571 ITextHost_TxInvalidateRect(editor
->texthost
, &editor
->rcFormat
, TRUE
);
3572 if (settings
& ECO_SELECTIONBAR
) {
3573 assert(!editor
->selofs
);
3574 editor
->selofs
= SELECTIONBAR_WIDTH
;
3575 editor
->rcFormat
.left
+= editor
->selofs
;
3577 editor
->rcFormat
.left
-= editor
->selofs
;
3580 ME_RewrapRepaint(editor
);
3583 if ((changedSettings
& settings
& ES_NOHIDESEL
) && !editor
->bHaveFocus
)
3584 ME_InvalidateSelection( editor
);
3586 if (changedSettings
& settings
& ECO_VERTICAL
)
3587 FIXME("ECO_VERTICAL not implemented yet!\n");
3588 if (changedSettings
& settings
& ECO_AUTOHSCROLL
)
3589 FIXME("ECO_AUTOHSCROLL not implemented yet!\n");
3590 if (changedSettings
& settings
& ECO_AUTOVSCROLL
)
3591 FIXME("ECO_AUTOVSCROLL not implemented yet!\n");
3592 if (changedSettings
& settings
& ECO_WANTRETURN
)
3593 FIXME("ECO_WANTRETURN not implemented yet!\n");
3594 if (changedSettings
& settings
& ECO_AUTOWORDSELECTION
)
3595 FIXME("ECO_AUTOWORDSELECTION not implemented yet!\n");
3602 return handle_EM_EXSETSEL( editor
, wParam
, lParam
);
3604 case EM_SETSCROLLPOS
:
3606 POINT
*point
= (POINT
*)lParam
;
3607 ME_ScrollAbs(editor
, point
->x
, point
->y
);
3610 case EM_AUTOURLDETECT
:
3612 if (wParam
==1 || wParam
==0)
3614 editor
->AutoURLDetect_bEnable
= (BOOL
)wParam
;
3617 return E_INVALIDARG
;
3619 case EM_GETAUTOURLDETECT
:
3621 return editor
->AutoURLDetect_bEnable
;
3625 CHARRANGE range
= *(CHARRANGE
*)lParam
;
3627 return handle_EM_EXSETSEL( editor
, range
.cpMin
, range
.cpMax
);
3629 case EM_SHOWSCROLLBAR
:
3642 flags
= WS_HSCROLL
|WS_VSCROLL
;
3649 editor
->styleFlags
|= flags
;
3650 if (flags
& WS_HSCROLL
)
3651 ITextHost_TxShowScrollBar(editor
->texthost
, SB_HORZ
,
3652 editor
->nTotalWidth
> editor
->sizeWindow
.cx
);
3653 if (flags
& WS_VSCROLL
)
3654 ITextHost_TxShowScrollBar(editor
->texthost
, SB_VERT
,
3655 editor
->nTotalLength
> editor
->sizeWindow
.cy
);
3657 editor
->styleFlags
&= ~flags
;
3658 ITextHost_TxShowScrollBar(editor
->texthost
, wParam
, FALSE
);
3665 SETTEXTEX
*pStruct
= (SETTEXTEX
*)wParam
;
3668 BOOL bRtf
, bUnicode
, bSelection
, bUTF8
;
3669 int oldModify
= editor
->nModifyStep
;
3670 static const char utf8_bom
[] = {0xef, 0xbb, 0xbf};
3672 if (!pStruct
) return 0;
3674 /* If we detect ascii rtf at the start of the string,
3675 * we know it isn't unicode. */
3676 bRtf
= (lParam
&& (!strncmp((char *)lParam
, "{\\rtf", 5) ||
3677 !strncmp((char *)lParam
, "{\\urtf", 6)));
3678 bUnicode
= !bRtf
&& pStruct
->codepage
== CP_UNICODE
;
3679 bUTF8
= (lParam
&& (!strncmp((char *)lParam
, utf8_bom
, 3)));
3681 TRACE("EM_SETTEXTEX - %s, flags %d, cp %d\n",
3682 bUnicode
? debugstr_w((LPCWSTR
)lParam
) : debugstr_a((LPCSTR
)lParam
),
3683 pStruct
->flags
, pStruct
->codepage
);
3685 bSelection
= (pStruct
->flags
& ST_SELECTION
) != 0;
3687 int nStartCursor
= ME_GetSelectionOfs(editor
, &from
, &to
);
3688 style
= ME_GetSelectionInsertStyle(editor
);
3689 ME_InternalDeleteText(editor
, &editor
->pCursors
[nStartCursor
], to
- from
, FALSE
);
3692 ME_SetCursorToStart(editor
, &start
);
3693 ME_InternalDeleteText(editor
, &start
, ME_GetTextLength(editor
), FALSE
);
3694 style
= editor
->pBuffer
->pDefaultStyle
;
3698 ME_StreamInRTFString(editor
, bSelection
, (char *)lParam
);
3700 /* FIXME: The length returned doesn't include the rtf control
3701 * characters, only the actual text. */
3702 len
= lParam
? strlen((char *)lParam
) : 0;
3705 if (bUTF8
&& !bUnicode
) {
3706 wszText
= ME_ToUnicode(CP_UTF8
, (void *)(lParam
+3), &len
);
3707 ME_InsertTextFromCursor(editor
, 0, wszText
, len
, style
);
3708 ME_EndToUnicode(CP_UTF8
, wszText
);
3710 wszText
= ME_ToUnicode(pStruct
->codepage
, (void *)lParam
, &len
);
3711 ME_InsertTextFromCursor(editor
, 0, wszText
, len
, style
);
3712 ME_EndToUnicode(pStruct
->codepage
, wszText
);
3717 ME_ReleaseStyle(style
);
3718 ME_UpdateSelectionLinkAttribute(editor
);
3722 ME_SetCursorToStart(editor
, &cursor
);
3723 ME_UpdateLinkAttribute(editor
, &cursor
, INT_MAX
);
3725 ME_CommitUndo(editor
);
3726 if (!(pStruct
->flags
& ST_KEEPUNDO
))
3728 editor
->nModifyStep
= oldModify
;
3729 ME_EmptyUndoStack(editor
);
3731 ME_UpdateRepaint(editor
, FALSE
);
3734 case EM_SETBKGNDCOLOR
:
3737 if (editor
->rgbBackColor
!= -1) {
3738 DeleteObject(editor
->hbrBackground
);
3739 lColor
= editor
->rgbBackColor
;
3741 else lColor
= ITextHost_TxGetSysColor(editor
->texthost
, COLOR_WINDOW
);
3745 editor
->rgbBackColor
= -1;
3746 editor
->hbrBackground
= GetSysColorBrush(COLOR_WINDOW
);
3750 editor
->rgbBackColor
= lParam
;
3751 editor
->hbrBackground
= CreateSolidBrush(editor
->rgbBackColor
);
3753 ITextHost_TxInvalidateRect(editor
->texthost
, NULL
, TRUE
);
3757 return editor
->nModifyStep
== 0 ? 0 : -1;
3761 editor
->nModifyStep
= 1;
3763 editor
->nModifyStep
= 0;
3767 case EM_SETREADONLY
:
3770 editor
->styleFlags
|= ES_READONLY
;
3772 editor
->styleFlags
&= ~ES_READONLY
;
3775 case EM_SETEVENTMASK
:
3777 DWORD nOldMask
= editor
->nEventMask
;
3779 editor
->nEventMask
= lParam
;
3782 case EM_GETEVENTMASK
:
3783 return editor
->nEventMask
;
3784 case EM_SETCHARFORMAT
:
3786 CHARFORMAT2W buf
, *p
;
3787 BOOL bRepaint
= TRUE
;
3788 p
= ME_ToCF2W(&buf
, (CHARFORMAT2W
*)lParam
);
3789 if (p
== NULL
) return 0;
3790 if (wParam
& SCF_ALL
) {
3791 if (editor
->mode
& TM_PLAINTEXT
) {
3792 ME_SetDefaultCharFormat(editor
, p
);
3795 ME_SetCursorToStart(editor
, &start
);
3796 ME_SetCharFormat(editor
, &start
, NULL
, p
);
3797 editor
->nModifyStep
= 1;
3799 } else if (wParam
& SCF_SELECTION
) {
3800 if (editor
->mode
& TM_PLAINTEXT
)
3802 if (wParam
& SCF_WORD
) {
3804 ME_Cursor end
= editor
->pCursors
[0];
3805 ME_MoveCursorWords(editor
, &end
, +1);
3807 ME_MoveCursorWords(editor
, &start
, -1);
3808 ME_SetCharFormat(editor
, &start
, &end
, p
);
3810 bRepaint
= ME_IsSelection(editor
);
3811 ME_SetSelectionCharFormat(editor
, p
);
3812 if (bRepaint
) editor
->nModifyStep
= 1;
3813 } else { /* SCF_DEFAULT */
3814 ME_SetDefaultCharFormat(editor
, p
);
3816 ME_CommitUndo(editor
);
3819 ME_WrapMarkedParagraphs(editor
);
3820 ME_UpdateScrollBar(editor
);
3825 case EM_GETCHARFORMAT
:
3827 CHARFORMAT2W tmp
, *dst
= (CHARFORMAT2W
*)lParam
;
3828 if (dst
->cbSize
!= sizeof(CHARFORMATA
) &&
3829 dst
->cbSize
!= sizeof(CHARFORMATW
) &&
3830 dst
->cbSize
!= sizeof(CHARFORMAT2A
) &&
3831 dst
->cbSize
!= sizeof(CHARFORMAT2W
))
3833 tmp
.cbSize
= sizeof(tmp
);
3835 ME_GetDefaultCharFormat(editor
, &tmp
);
3837 ME_GetSelectionCharFormat(editor
, &tmp
);
3838 ME_CopyToCFAny(dst
, &tmp
);
3841 case EM_SETPARAFORMAT
:
3843 BOOL result
= ME_SetSelectionParaFormat(editor
, (PARAFORMAT2
*)lParam
);
3844 ME_WrapMarkedParagraphs(editor
);
3845 ME_UpdateScrollBar(editor
);
3847 ME_CommitUndo(editor
);
3850 case EM_GETPARAFORMAT
:
3851 ME_GetSelectionParaFormat(editor
, (PARAFORMAT2
*)lParam
);
3852 return ((PARAFORMAT2
*)lParam
)->dwMask
;
3853 case EM_GETFIRSTVISIBLELINE
:
3855 ME_DisplayItem
*p
= editor
->pBuffer
->pFirst
;
3856 int y
= editor
->vert_si
.nPos
;
3861 p
= ME_FindItemFwd(p
, diStartRowOrParagraphOrEnd
);
3862 if (p
->type
== diTextEnd
)
3864 if (p
->type
== diParagraph
) {
3865 ypara
= p
->member
.para
.pt
.y
;
3868 ystart
= ypara
+ p
->member
.row
.pt
.y
;
3869 yend
= ystart
+ p
->member
.row
.nHeight
;
3877 case EM_HIDESELECTION
:
3879 editor
->bHideSelection
= (wParam
!= 0);
3880 ME_InvalidateSelection(editor
);
3885 if (!(editor
->styleFlags
& ES_MULTILINE
))
3887 ME_ScrollDown( editor
, lParam
* get_default_line_height( editor
) );
3893 int nStartCursor
= ME_GetSelectionOfs(editor
, &from
, &to
);
3894 ME_InternalDeleteText(editor
, &editor
->pCursors
[nStartCursor
], to
-from
, FALSE
);
3895 ME_CommitUndo(editor
);
3896 ME_UpdateRepaint(editor
, TRUE
);
3902 LONG codepage
= unicode
? CP_UNICODE
: CP_ACP
;
3903 LPWSTR wszText
= ME_ToUnicode(codepage
, (void *)lParam
, &len
);
3905 TRACE("EM_REPLACESEL - %s\n", debugstr_w(wszText
));
3907 ME_ReplaceSel(editor
, !!wParam
, wszText
, len
);
3908 ME_EndToUnicode(codepage
, wszText
);
3911 case EM_SCROLLCARET
:
3912 ME_EnsureVisible(editor
, &editor
->pCursors
[0]);
3919 BOOL bRepaint
= LOWORD(lParam
);
3922 wParam
= (WPARAM
)GetStockObject(SYSTEM_FONT
);
3924 if (!GetObjectW((HGDIOBJ
)wParam
, sizeof(LOGFONTW
), &lf
))
3927 hDC
= ITextHost_TxGetDC(editor
->texthost
);
3928 ME_CharFormatFromLogFont(hDC
, &lf
, &fmt
);
3929 ITextHost_TxReleaseDC(editor
->texthost
, hDC
);
3930 if (editor
->mode
& TM_RICHTEXT
) {
3932 ME_SetCursorToStart(editor
, &start
);
3933 ME_SetCharFormat(editor
, &start
, NULL
, &fmt
);
3935 ME_SetDefaultCharFormat(editor
, &fmt
);
3937 ME_CommitUndo(editor
);
3938 ME_MarkAllForWrapping(editor
);
3939 ME_WrapMarkedParagraphs(editor
);
3940 ME_UpdateScrollBar(editor
);
3948 ME_SetCursorToStart(editor
, &cursor
);
3949 ME_InternalDeleteText(editor
, &cursor
, ME_GetTextLength(editor
), FALSE
);
3952 TRACE("WM_SETTEXT lParam==%lx\n",lParam
);
3953 if (!strncmp((char *)lParam
, "{\\rtf", 5) ||
3954 !strncmp((char *)lParam
, "{\\urtf", 6))
3956 /* Undocumented: WM_SETTEXT supports RTF text */
3957 ME_StreamInRTFString(editor
, 0, (char *)lParam
);
3960 ME_SetText(editor
, (void*)lParam
, unicode
);
3963 TRACE("WM_SETTEXT - NULL\n");
3964 ME_SetCursorToStart(editor
, &cursor
);
3965 ME_UpdateLinkAttribute(editor
, &cursor
, INT_MAX
);
3966 ME_SetSelection(editor
, 0, 0);
3967 editor
->nModifyStep
= 0;
3968 ME_CommitUndo(editor
);
3969 ME_EmptyUndoStack(editor
);
3970 ME_UpdateRepaint(editor
, FALSE
);
3975 UINT nRTFFormat
= RegisterClipboardFormatA("Rich Text Format");
3976 if (IsClipboardFormatAvailable(nRTFFormat
))
3978 if (IsClipboardFormatAvailable(CF_UNICODETEXT
))
3983 case WM_MBUTTONDOWN
:
3989 int nFrom
, nTo
, nStartCur
= ME_GetSelectionOfs(editor
, &nFrom
, &nTo
);
3990 int nChars
= nTo
- nFrom
;
3991 ME_Cursor
*selStart
= &editor
->pCursors
[nStartCur
];
3993 if (ME_Copy(editor
, selStart
, nChars
) && msg
== WM_CUT
)
3995 ME_InternalDeleteText(editor
, selStart
, nChars
, FALSE
);
3996 ME_CommitUndo(editor
);
3997 ME_UpdateRepaint(editor
, TRUE
);
4001 case WM_GETTEXTLENGTH
:
4003 GETTEXTLENGTHEX how
;
4005 /* CR/LF conversion required in 2.0 mode, verbatim in 1.0 mode */
4006 how
.flags
= GTL_CLOSE
| (editor
->bEmulateVersion10
? 0 : GTL_USECRLF
) | GTL_NUMCHARS
;
4007 how
.codepage
= unicode
? CP_UNICODE
: CP_ACP
;
4008 return ME_GetTextLengthEx(editor
, &how
);
4010 case EM_GETTEXTLENGTHEX
:
4011 return ME_GetTextLengthEx(editor
, (GETTEXTLENGTHEX
*)wParam
);
4015 ex
.cb
= wParam
* (unicode
? sizeof(WCHAR
) : sizeof(CHAR
));
4016 ex
.flags
= GT_USECRLF
;
4017 ex
.codepage
= unicode
? CP_UNICODE
: CP_ACP
;
4018 ex
.lpDefaultChar
= NULL
;
4019 ex
.lpUsedDefChar
= NULL
;
4020 return ME_GetTextEx(editor
, &ex
, lParam
);
4023 return ME_GetTextEx(editor
, (GETTEXTEX
*)wParam
, lParam
);
4026 int nFrom
, nTo
, nStartCur
= ME_GetSelectionOfs(editor
, &nFrom
, &nTo
);
4027 ME_Cursor
*from
= &editor
->pCursors
[nStartCur
];
4028 return ME_GetTextRange(editor
, (WCHAR
*)lParam
, from
,
4029 nTo
- nFrom
, unicode
);
4031 case EM_GETSCROLLPOS
:
4033 POINT
*point
= (POINT
*)lParam
;
4034 point
->x
= editor
->horz_si
.nPos
;
4035 point
->y
= editor
->vert_si
.nPos
;
4036 /* 16-bit scaled value is returned as stored in scrollinfo */
4037 if (editor
->horz_si
.nMax
> 0xffff)
4038 point
->x
= MulDiv(point
->x
, 0xffff, editor
->horz_si
.nMax
);
4039 if (editor
->vert_si
.nMax
> 0xffff)
4040 point
->y
= MulDiv(point
->y
, 0xffff, editor
->vert_si
.nMax
);
4043 case EM_GETTEXTRANGE
:
4045 TEXTRANGEW
*rng
= (TEXTRANGEW
*)lParam
;
4047 int nStart
= rng
->chrg
.cpMin
;
4048 int nEnd
= rng
->chrg
.cpMax
;
4049 int textlength
= ME_GetTextLength(editor
);
4051 TRACE("EM_GETTEXTRANGE min=%d max=%d unicode=%d textlength=%d\n",
4052 rng
->chrg
.cpMin
, rng
->chrg
.cpMax
, unicode
, textlength
);
4053 if (nStart
< 0) return 0;
4054 if ((nStart
== 0 && nEnd
== -1) || nEnd
> textlength
)
4056 if (nStart
>= nEnd
) return 0;
4058 ME_CursorFromCharOfs(editor
, nStart
, &start
);
4059 return ME_GetTextRange(editor
, rng
->lpstrText
, &start
, nEnd
- nStart
, unicode
);
4063 ME_DisplayItem
*run
;
4064 const unsigned int nMaxChars
= *(WORD
*) lParam
;
4065 unsigned int nCharsLeft
= nMaxChars
;
4066 char *dest
= (char *) lParam
;
4067 BOOL wroteNull
= FALSE
;
4069 TRACE("EM_GETLINE: row=%d, nMaxChars=%d (%s)\n", (int) wParam
, nMaxChars
,
4070 unicode
? "Unicode" : "Ansi");
4072 run
= ME_FindRowWithNumber(editor
, wParam
);
4076 while (nCharsLeft
&& (run
= ME_FindItemFwd(run
, diRunOrStartRow
))
4077 && run
->type
== diRun
)
4079 WCHAR
*str
= get_text( &run
->member
.run
, 0 );
4082 nCopy
= min(nCharsLeft
, run
->member
.run
.len
);
4085 memcpy(dest
, str
, nCopy
* sizeof(WCHAR
));
4087 nCopy
= WideCharToMultiByte(CP_ACP
, 0, str
, nCopy
, dest
,
4088 nCharsLeft
, NULL
, NULL
);
4089 dest
+= nCopy
* (unicode
? sizeof(WCHAR
) : 1);
4090 nCharsLeft
-= nCopy
;
4093 /* append line termination, space allowing */
4097 *((WCHAR
*)dest
) = '\0';
4104 TRACE("EM_GETLINE: got %u characters\n", nMaxChars
- nCharsLeft
);
4105 return nMaxChars
- nCharsLeft
- (wroteNull
? 1 : 0);
4107 case EM_GETLINECOUNT
:
4109 ME_DisplayItem
*item
= editor
->pBuffer
->pFirst
->next
;
4112 ME_DisplayItem
*prev_para
= NULL
, *last_para
= NULL
;
4114 while (item
!= editor
->pBuffer
->pLast
)
4116 assert(item
->type
== diParagraph
);
4117 prev_para
= ME_FindItemBack(item
, diRun
);
4119 assert(prev_para
->member
.run
.nFlags
& MERF_ENDPARA
);
4121 nRows
+= item
->member
.para
.nRows
;
4122 item
= item
->member
.para
.next_para
;
4124 last_para
= ME_FindItemBack(item
, diRun
);
4126 assert(last_para
->member
.run
.nFlags
& MERF_ENDPARA
);
4127 if (editor
->bEmulateVersion10
&& prev_para
&&
4128 last_para
->member
.run
.nCharOfs
== 0 &&
4129 prev_para
->member
.run
.len
== 1 &&
4130 *get_text( &prev_para
->member
.run
, 0 ) == '\r')
4132 /* In 1.0 emulation, the last solitary \r at the very end of the text
4133 (if one exists) is NOT a line break.
4134 FIXME: this is an ugly hack. This should have a more regular model. */
4138 TRACE("EM_GETLINECOUNT: nRows==%d\n", nRows
);
4139 return max(1, nRows
);
4141 case EM_LINEFROMCHAR
:
4144 return ME_RowNumberFromCharOfs(editor
, ME_GetCursorOfs(&editor
->pCursors
[1]));
4146 return ME_RowNumberFromCharOfs(editor
, wParam
);
4148 case EM_EXLINEFROMCHAR
:
4151 return ME_RowNumberFromCharOfs(editor
, ME_GetCursorOfs(&editor
->pCursors
[1]));
4153 return ME_RowNumberFromCharOfs(editor
, lParam
);
4157 ME_DisplayItem
*item
, *para
;
4161 item
= ME_FindItemBack(editor
->pCursors
[0].pRun
, diStartRow
);
4163 item
= ME_FindRowWithNumber(editor
, wParam
);
4166 para
= ME_GetParagraph(item
);
4167 item
= ME_FindItemFwd(item
, diRun
);
4168 nCharOfs
= para
->member
.para
.nCharOfs
+ item
->member
.run
.nCharOfs
;
4169 TRACE("EM_LINEINDEX: nCharOfs==%d\n", nCharOfs
);
4174 ME_DisplayItem
*item
, *item_end
;
4175 int nChars
= 0, nThisLineOfs
= 0, nNextLineOfs
= 0;
4176 ME_DisplayItem
*para
, *run
;
4178 if (wParam
> ME_GetTextLength(editor
))
4182 FIXME("EM_LINELENGTH: returning number of unselected characters on lines with selection unsupported.\n");
4185 ME_RunOfsFromCharOfs(editor
, wParam
, ¶
, &run
, NULL
);
4186 item
= ME_RowStart(run
);
4187 nThisLineOfs
= ME_CharOfsFromRunOfs(editor
, para
, ME_FindItemFwd(item
, diRun
), 0);
4188 item_end
= ME_FindItemFwd(item
, diStartRowOrParagraphOrEnd
);
4189 if (item_end
->type
== diStartRow
) {
4190 nNextLineOfs
= ME_CharOfsFromRunOfs(editor
, para
, ME_FindItemFwd(item_end
, diRun
), 0);
4192 ME_DisplayItem
*endRun
= ME_FindItemBack(item_end
, diRun
);
4193 assert(endRun
&& endRun
->member
.run
.nFlags
& MERF_ENDPARA
);
4194 nNextLineOfs
= item_end
->member
.para
.nCharOfs
- endRun
->member
.run
.len
;
4196 nChars
= nNextLineOfs
- nThisLineOfs
;
4197 TRACE("EM_LINELENGTH(%ld)==%d\n",wParam
, nChars
);
4200 case EM_EXLIMITTEXT
:
4202 if ((int)lParam
< 0)
4205 editor
->nTextLimit
= 65536;
4207 editor
->nTextLimit
= (int) lParam
;
4213 editor
->nTextLimit
= 65536;
4215 editor
->nTextLimit
= (int) wParam
;
4218 case EM_GETLIMITTEXT
:
4220 return editor
->nTextLimit
;
4226 FINDTEXTA
*ft
= (FINDTEXTA
*)lParam
;
4227 int nChars
= MultiByteToWideChar(CP_ACP
, 0, ft
->lpstrText
, -1, NULL
, 0);
4230 if ((tmp
= ALLOC_N_OBJ(WCHAR
, nChars
)) != NULL
)
4231 MultiByteToWideChar(CP_ACP
, 0, ft
->lpstrText
, -1, tmp
, nChars
);
4232 r
= ME_FindText(editor
, wParam
, &ft
->chrg
, tmp
, NULL
);
4235 FINDTEXTW
*ft
= (FINDTEXTW
*)lParam
;
4236 r
= ME_FindText(editor
, wParam
, &ft
->chrg
, ft
->lpstrText
, NULL
);
4244 FINDTEXTEXA
*ex
= (FINDTEXTEXA
*)lParam
;
4245 int nChars
= MultiByteToWideChar(CP_ACP
, 0, ex
->lpstrText
, -1, NULL
, 0);
4248 if ((tmp
= ALLOC_N_OBJ(WCHAR
, nChars
)) != NULL
)
4249 MultiByteToWideChar(CP_ACP
, 0, ex
->lpstrText
, -1, tmp
, nChars
);
4250 r
= ME_FindText(editor
, wParam
, &ex
->chrg
, tmp
, &ex
->chrgText
);
4253 FINDTEXTEXW
*ex
= (FINDTEXTEXW
*)lParam
;
4254 r
= ME_FindText(editor
, wParam
, &ex
->chrg
, ex
->lpstrText
, &ex
->chrgText
);
4260 FINDTEXTW
*ft
= (FINDTEXTW
*)lParam
;
4261 return ME_FindText(editor
, wParam
, &ft
->chrg
, ft
->lpstrText
, NULL
);
4263 case EM_FINDTEXTEXW
:
4265 FINDTEXTEXW
*ex
= (FINDTEXTEXW
*)lParam
;
4266 return ME_FindText(editor
, wParam
, &ex
->chrg
, ex
->lpstrText
, &ex
->chrgText
);
4269 if (!wParam
|| !lParam
)
4271 *(int *)wParam
= editor
->nZoomNumerator
;
4272 *(int *)lParam
= editor
->nZoomDenominator
;
4275 return ME_SetZoom(editor
, wParam
, lParam
);
4276 case EM_CHARFROMPOS
:
4279 if (ME_CharFromPos(editor
, ((POINTL
*)lParam
)->x
, ((POINTL
*)lParam
)->y
,
4281 return ME_GetCursorOfs(&cursor
);
4285 case EM_POSFROMCHAR
:
4287 ME_DisplayItem
*pPara
, *pRun
;
4288 int nCharOfs
, nOffset
, nLength
;
4292 /* detect which API version we're dealing with */
4293 if (wParam
>= 0x40000)
4295 nLength
= ME_GetTextLength(editor
);
4296 nCharOfs
= min(nCharOfs
, nLength
);
4297 nCharOfs
= max(nCharOfs
, 0);
4299 ME_RunOfsFromCharOfs(editor
, nCharOfs
, &pPara
, &pRun
, &nOffset
);
4300 assert(pRun
->type
== diRun
);
4301 pt
.y
= pRun
->member
.run
.pt
.y
;
4302 pt
.x
= pRun
->member
.run
.pt
.x
+ ME_PointFromChar(editor
, &pRun
->member
.run
, nOffset
, TRUE
);
4303 pt
.y
+= pPara
->member
.para
.pt
.y
+ editor
->rcFormat
.top
;
4304 pt
.x
+= editor
->rcFormat
.left
;
4306 pt
.x
-= editor
->horz_si
.nPos
;
4307 pt
.y
-= editor
->vert_si
.nPos
;
4309 if (wParam
>= 0x40000) {
4310 *(POINTL
*)wParam
= pt
;
4312 return (wParam
>= 0x40000) ? 0 : MAKELONG( pt
.x
, pt
.y
);
4315 return ME_WmCreate(editor
, lParam
, unicode
);
4317 ME_DestroyEditor(editor
);
4322 if (wParam
== (WPARAM
)editor
->hWnd
&& GetCursorPos(&cursor_pos
) &&
4323 ScreenToClient(editor
->hWnd
, &cursor_pos
))
4324 ME_LinkNotify(editor
, msg
, 0, MAKELPARAM(cursor_pos
.x
, cursor_pos
.y
));
4325 return ME_SetCursor(editor
);
4327 case WM_LBUTTONDBLCLK
:
4328 case WM_LBUTTONDOWN
:
4330 ME_CommitUndo(editor
); /* End coalesced undos for typed characters */
4331 if ((editor
->nEventMask
& ENM_MOUSEEVENTS
) &&
4332 !ME_FilterEvent(editor
, msg
, &wParam
, &lParam
))
4334 ITextHost_TxSetFocus(editor
->texthost
);
4335 ME_LButtonDown(editor
, (short)LOWORD(lParam
), (short)HIWORD(lParam
),
4336 ME_CalculateClickCount(editor
, msg
, wParam
, lParam
));
4337 ITextHost_TxSetCapture(editor
->texthost
, TRUE
);
4338 editor
->bMouseCaptured
= TRUE
;
4339 ME_LinkNotify(editor
, msg
, wParam
, lParam
);
4340 if (!ME_SetCursor(editor
)) goto do_default
;
4344 if ((editor
->nEventMask
& ENM_MOUSEEVENTS
) &&
4345 !ME_FilterEvent(editor
, msg
, &wParam
, &lParam
))
4347 if (editor
->bMouseCaptured
)
4348 ME_MouseMove(editor
, (short)LOWORD(lParam
), (short)HIWORD(lParam
));
4350 ME_LinkNotify(editor
, msg
, wParam
, lParam
);
4351 /* Set cursor if mouse is captured, since WM_SETCURSOR won't be received. */
4352 if (editor
->bMouseCaptured
)
4353 ME_SetCursor(editor
);
4356 if (editor
->bMouseCaptured
) {
4357 ITextHost_TxSetCapture(editor
->texthost
, FALSE
);
4358 editor
->bMouseCaptured
= FALSE
;
4360 if (editor
->nSelectionType
== stDocument
)
4361 editor
->nSelectionType
= stPosition
;
4362 if ((editor
->nEventMask
& ENM_MOUSEEVENTS
) &&
4363 !ME_FilterEvent(editor
, msg
, &wParam
, &lParam
))
4367 ME_SetCursor(editor
);
4368 ME_LinkNotify(editor
, msg
, wParam
, lParam
);
4372 case WM_RBUTTONDOWN
:
4373 case WM_RBUTTONDBLCLK
:
4374 ME_CommitUndo(editor
); /* End coalesced undos for typed characters */
4375 if ((editor
->nEventMask
& ENM_MOUSEEVENTS
) &&
4376 !ME_FilterEvent(editor
, msg
, &wParam
, &lParam
))
4378 ME_LinkNotify(editor
, msg
, wParam
, lParam
);
4380 case WM_CONTEXTMENU
:
4381 if (!ME_ShowContextMenu(editor
, (short)LOWORD(lParam
), (short)HIWORD(lParam
)))
4385 editor
->bHaveFocus
= TRUE
;
4386 ME_ShowCaret(editor
);
4387 ME_SendOldNotify(editor
, EN_SETFOCUS
);
4388 if (!editor
->bHideSelection
&& !(editor
->styleFlags
& ES_NOHIDESEL
))
4389 ME_InvalidateSelection( editor
);
4392 ME_CommitUndo(editor
); /* End coalesced undos for typed characters */
4393 editor
->bHaveFocus
= FALSE
;
4394 editor
->wheel_remain
= 0;
4395 ME_HideCaret(editor
);
4396 ME_SendOldNotify(editor
, EN_KILLFOCUS
);
4397 if (!editor
->bHideSelection
&& !(editor
->styleFlags
& ES_NOHIDESEL
))
4398 ME_InvalidateSelection( editor
);
4401 TRACE("editor wnd command = %d\n", LOWORD(wParam
));
4404 if ((editor
->nEventMask
& ENM_KEYEVENTS
) &&
4405 !ME_FilterEvent(editor
, msg
, &wParam
, &lParam
))
4409 if ((editor
->nEventMask
& ENM_KEYEVENTS
) &&
4410 !ME_FilterEvent(editor
, msg
, &wParam
, &lParam
))
4412 if (ME_KeyDown(editor
, LOWORD(wParam
)))
4416 if ((editor
->nEventMask
& ENM_KEYEVENTS
) &&
4417 !ME_FilterEvent(editor
, msg
, &wParam
, &lParam
))
4419 return ME_Char(editor
, wParam
, lParam
, unicode
);
4423 if(wParam
== UNICODE_NOCHAR
) return TRUE
;
4424 if(wParam
<= 0x000fffff)
4426 if(wParam
> 0xffff) /* convert to surrogates */
4429 ME_Char(editor
, (wParam
>> 10) + 0xd800, 0, TRUE
);
4430 ME_Char(editor
, (wParam
& 0x03ff) + 0xdc00, 0, TRUE
);
4432 ME_Char(editor
, wParam
, 0, TRUE
);
4438 case EM_STOPGROUPTYPING
:
4439 ME_CommitUndo(editor
); /* End coalesced undos for typed characters */
4443 const int scrollUnit
= 7;
4445 switch(LOWORD(wParam
))
4448 ME_ScrollAbs(editor
, 0, 0);
4451 ME_ScrollAbs(editor
,
4452 editor
->horz_si
.nMax
- (int)editor
->horz_si
.nPage
,
4453 editor
->vert_si
.nMax
- (int)editor
->vert_si
.nPage
);
4456 ME_ScrollLeft(editor
, scrollUnit
);
4459 ME_ScrollRight(editor
, scrollUnit
);
4462 ME_ScrollLeft(editor
, editor
->sizeWindow
.cx
);
4465 ME_ScrollRight(editor
, editor
->sizeWindow
.cx
);
4468 case SB_THUMBPOSITION
:
4470 int pos
= HIWORD(wParam
);
4471 if (editor
->horz_si
.nMax
> 0xffff)
4472 pos
= MulDiv(pos
, editor
->horz_si
.nMax
, 0xffff);
4473 ME_HScrollAbs(editor
, pos
);
4479 case EM_SCROLL
: /* fall through */
4483 int lineHeight
= get_default_line_height( editor
);
4485 origNPos
= editor
->vert_si
.nPos
;
4487 switch(LOWORD(wParam
))
4490 ME_ScrollAbs(editor
, 0, 0);
4493 ME_ScrollAbs(editor
,
4494 editor
->horz_si
.nMax
- (int)editor
->horz_si
.nPage
,
4495 editor
->vert_si
.nMax
- (int)editor
->vert_si
.nPage
);
4498 ME_ScrollUp(editor
,lineHeight
);
4501 ME_ScrollDown(editor
,lineHeight
);
4504 ME_ScrollUp(editor
,editor
->sizeWindow
.cy
);
4507 ME_ScrollDown(editor
,editor
->sizeWindow
.cy
);
4510 case SB_THUMBPOSITION
:
4512 int pos
= HIWORD(wParam
);
4513 if (editor
->vert_si
.nMax
> 0xffff)
4514 pos
= MulDiv(pos
, editor
->vert_si
.nMax
, 0xffff);
4515 ME_VScrollAbs(editor
, pos
);
4519 if (msg
== EM_SCROLL
)
4520 return 0x00010000 | (((editor
->vert_si
.nPos
- origNPos
)/lineHeight
) & 0xffff);
4528 if ((editor
->nEventMask
& ENM_MOUSEEVENTS
) &&
4529 !ME_FilterEvent(editor
, msg
, &wParam
, &lParam
))
4532 ctrl_is_down
= GetKeyState(VK_CONTROL
) & 0x8000;
4534 delta
= GET_WHEEL_DELTA_WPARAM(wParam
);
4536 /* if scrolling changes direction, ignore left overs */
4537 if ((delta
< 0 && editor
->wheel_remain
< 0) ||
4538 (delta
> 0 && editor
->wheel_remain
> 0))
4539 editor
->wheel_remain
+= delta
;
4541 editor
->wheel_remain
= delta
;
4543 if (editor
->wheel_remain
)
4547 if (!editor
->nZoomNumerator
|| !editor
->nZoomDenominator
)
4551 numerator
= editor
->nZoomNumerator
* 100 / editor
->nZoomDenominator
;
4553 numerator
+= calc_wheel_change( &editor
->wheel_remain
, 10 );
4554 if (numerator
>= 10 && numerator
<= 500)
4555 ME_SetZoom(editor
, numerator
, 100);
4560 SystemParametersInfoW( SPI_GETWHEELSCROLLLINES
, 0, &max_lines
, 0 );
4562 lines
= calc_wheel_change( &editor
->wheel_remain
, (int)max_lines
);
4564 ME_ScrollDown( editor
, -lines
* get_default_line_height( editor
) );
4571 *((RECT
*)lParam
) = editor
->rcFormat
;
4572 if (editor
->bDefaultFormatRect
)
4573 ((RECT
*)lParam
)->left
-= editor
->selofs
;
4583 RECT
*rc
= (RECT
*)lParam
;
4585 border
= editor
->exStyleFlags
& WS_EX_CLIENTEDGE
? 1 : 0;
4586 ITextHost_TxGetClientRect(editor
->texthost
, &clientRect
);
4589 editor
->rcFormat
.top
= max(0, rc
->top
- border
);
4590 editor
->rcFormat
.left
= max(0, rc
->left
- border
);
4591 editor
->rcFormat
.bottom
= min(clientRect
.bottom
, rc
->bottom
);
4592 editor
->rcFormat
.right
= min(clientRect
.right
, rc
->right
+ border
);
4593 } else if (wParam
== 1) {
4594 /* MSDN incorrectly says a wParam value of 1 causes the
4595 * lParam rect to be used as a relative offset,
4596 * however, the tests show it just prevents min/max bound
4598 editor
->rcFormat
.top
= rc
->top
- border
;
4599 editor
->rcFormat
.left
= rc
->left
- border
;
4600 editor
->rcFormat
.bottom
= rc
->bottom
;
4601 editor
->rcFormat
.right
= rc
->right
+ border
;
4605 editor
->bDefaultFormatRect
= FALSE
;
4609 ME_SetDefaultFormatRect(editor
);
4610 editor
->bDefaultFormatRect
= TRUE
;
4612 ME_MarkAllForWrapping(editor
);
4613 ME_WrapMarkedParagraphs(editor
);
4614 ME_UpdateScrollBar(editor
);
4615 if (msg
!= EM_SETRECTNP
)
4619 case EM_REQUESTRESIZE
:
4620 ME_SendRequestResize(editor
, TRUE
);
4624 case WM_WINDOWPOSCHANGED
:
4627 WINDOWPOS
*winpos
= (WINDOWPOS
*)lParam
;
4629 if (winpos
->flags
& SWP_NOCLIENTSIZE
) goto do_default
;
4630 ITextHost_TxGetClientRect(editor
->texthost
, &clientRect
);
4631 if (editor
->bDefaultFormatRect
) {
4632 ME_SetDefaultFormatRect(editor
);
4634 editor
->rcFormat
.right
+= clientRect
.right
- editor
->prevClientRect
.right
;
4635 editor
->rcFormat
.bottom
+= clientRect
.bottom
- editor
->prevClientRect
.bottom
;
4637 editor
->prevClientRect
= clientRect
;
4638 ME_RewrapRepaint(editor
);
4641 /* IME messages to make richedit controls IME aware */
4642 case WM_IME_SETCONTEXT
:
4643 case WM_IME_CONTROL
:
4645 case WM_IME_COMPOSITIONFULL
:
4647 case WM_IME_STARTCOMPOSITION
:
4649 editor
->imeStartIndex
=ME_GetCursorOfs(&editor
->pCursors
[0]);
4650 ME_DeleteSelection(editor
);
4651 ME_CommitUndo(editor
);
4652 ME_UpdateRepaint(editor
, FALSE
);
4655 case WM_IME_COMPOSITION
:
4659 ME_Style
*style
= ME_GetInsertStyle(editor
, 0);
4660 hIMC
= ITextHost_TxImmGetContext(editor
->texthost
);
4661 ME_DeleteSelection(editor
);
4662 ME_SaveTempStyle(editor
, style
);
4663 if (lParam
& (GCS_RESULTSTR
|GCS_COMPSTR
))
4665 LPWSTR lpCompStr
= NULL
;
4667 DWORD dwIndex
= lParam
& GCS_RESULTSTR
;
4669 dwIndex
= GCS_COMPSTR
;
4671 dwBufLen
= ImmGetCompositionStringW(hIMC
, dwIndex
, NULL
, 0);
4672 lpCompStr
= HeapAlloc(GetProcessHeap(),0,dwBufLen
+ sizeof(WCHAR
));
4673 ImmGetCompositionStringW(hIMC
, dwIndex
, lpCompStr
, dwBufLen
);
4674 lpCompStr
[dwBufLen
/sizeof(WCHAR
)] = 0;
4675 ME_InsertTextFromCursor(editor
,0,lpCompStr
,dwBufLen
/sizeof(WCHAR
),style
);
4676 HeapFree(GetProcessHeap(), 0, lpCompStr
);
4678 if (dwIndex
== GCS_COMPSTR
)
4679 ME_SetSelection(editor
,editor
->imeStartIndex
,
4680 editor
->imeStartIndex
+ dwBufLen
/sizeof(WCHAR
));
4682 ME_ReleaseStyle(style
);
4683 ME_CommitUndo(editor
);
4684 ME_UpdateRepaint(editor
, FALSE
);
4687 case WM_IME_ENDCOMPOSITION
:
4689 ME_DeleteSelection(editor
);
4690 editor
->imeStartIndex
=-1;
4693 case EM_GETOLEINTERFACE
:
4696 if (!CreateIRichEditOle(NULL
, editor
, (LPVOID
*)&editor
->reOle
))
4698 *(LPVOID
*)lParam
= editor
->reOle
;
4699 IRichEditOle_AddRef(editor
->reOle
);
4702 case EM_GETPASSWORDCHAR
:
4704 return editor
->cPasswordMask
;
4706 case EM_SETOLECALLBACK
:
4707 if(editor
->lpOleCallback
)
4708 IRichEditOleCallback_Release(editor
->lpOleCallback
);
4709 editor
->lpOleCallback
= (IRichEditOleCallback
*)lParam
;
4710 if(editor
->lpOleCallback
)
4711 IRichEditOleCallback_AddRef(editor
->lpOleCallback
);
4713 case EM_GETWORDBREAKPROC
:
4714 return (LRESULT
)editor
->pfnWordBreak
;
4715 case EM_SETWORDBREAKPROC
:
4717 EDITWORDBREAKPROCW pfnOld
= editor
->pfnWordBreak
;
4719 editor
->pfnWordBreak
= (EDITWORDBREAKPROCW
)lParam
;
4720 return (LRESULT
)pfnOld
;
4722 case EM_GETTEXTMODE
:
4723 return editor
->mode
;
4724 case EM_SETTEXTMODE
:
4729 if (ME_GetTextLength(editor
) ||
4730 !list_empty( &editor
->undo_stack
) || !list_empty( &editor
->redo_stack
))
4731 return E_UNEXPECTED
;
4733 /* Check for mutually exclusive flags in adjacent bits of wParam */
4734 if ((wParam
& (TM_RICHTEXT
| TM_MULTILEVELUNDO
| TM_MULTICODEPAGE
)) &
4735 (wParam
& (TM_PLAINTEXT
| TM_SINGLELEVELUNDO
| TM_SINGLECODEPAGE
)) << 1)
4736 return E_INVALIDARG
;
4738 if (wParam
& (TM_RICHTEXT
| TM_PLAINTEXT
))
4740 mask
|= TM_RICHTEXT
| TM_PLAINTEXT
;
4741 changes
|= wParam
& (TM_RICHTEXT
| TM_PLAINTEXT
);
4742 if (wParam
& TM_PLAINTEXT
) {
4743 /* Clear selection since it should be possible to select the
4744 * end of text run for rich text */
4745 ME_InvalidateSelection(editor
);
4746 ME_SetCursorToStart(editor
, &editor
->pCursors
[0]);
4747 editor
->pCursors
[1] = editor
->pCursors
[0];
4748 /* plain text can only have the default style. */
4749 ME_ClearTempStyle(editor
);
4750 ME_AddRefStyle(editor
->pBuffer
->pDefaultStyle
);
4751 ME_ReleaseStyle(editor
->pCursors
[0].pRun
->member
.run
.style
);
4752 editor
->pCursors
[0].pRun
->member
.run
.style
= editor
->pBuffer
->pDefaultStyle
;
4755 /* FIXME: Currently no support for undo level and code page options */
4756 editor
->mode
= (editor
->mode
& ~mask
) | changes
;
4759 case EM_SETPASSWORDCHAR
:
4761 editor
->cPasswordMask
= wParam
;
4762 ME_RewrapRepaint(editor
);
4765 case EM_SETTARGETDEVICE
:
4768 BOOL
new = (lParam
== 0 && (editor
->styleFlags
& ES_MULTILINE
));
4769 if (editor
->nAvailWidth
|| editor
->bWordWrap
!= new)
4771 editor
->bWordWrap
= new;
4772 editor
->nAvailWidth
= 0; /* wrap to client area */
4773 ME_RewrapRepaint(editor
);
4776 int width
= max(0, lParam
);
4777 if ((editor
->styleFlags
& ES_MULTILINE
) &&
4778 (!editor
->bWordWrap
|| editor
->nAvailWidth
!= width
))
4780 editor
->nAvailWidth
= width
;
4781 editor
->bWordWrap
= TRUE
;
4782 ME_RewrapRepaint(editor
);
4784 FIXME("EM_SETTARGETDEVICE doesn't use non-NULL target devices\n");
4789 *phresult
= S_FALSE
;
4795 static LRESULT
RichEditWndProc_common(HWND hWnd
, UINT msg
, WPARAM wParam
,
4796 LPARAM lParam
, BOOL unicode
)
4798 ME_TextEditor
*editor
;
4800 LRESULT lresult
= 0;
4802 TRACE("enter hwnd %p msg %04x (%s) %lx %lx, unicode %d\n",
4803 hWnd
, msg
, get_msg_name(msg
), wParam
, lParam
, unicode
);
4805 editor
= (ME_TextEditor
*)GetWindowLongPtrW(hWnd
, 0);
4808 if (msg
== WM_NCCREATE
)
4810 CREATESTRUCTW
*pcs
= (CREATESTRUCTW
*)lParam
;
4811 ITextHost
*texthost
;
4813 TRACE("WM_NCCREATE: hWnd %p style 0x%08x\n", hWnd
, pcs
->style
);
4814 texthost
= ME_CreateTextHost(hWnd
, pcs
, FALSE
);
4815 return texthost
!= NULL
;
4819 return DefWindowProcW(hWnd
, msg
, wParam
, lParam
);
4831 hDC
= BeginPaint(editor
->hWnd
, &ps
);
4832 if (!editor
->bEmulateVersion10
|| (editor
->nEventMask
& ENM_UPDATE
))
4833 ME_SendOldNotify(editor
, EN_UPDATE
);
4834 /* Erase area outside of the formatting rectangle */
4835 if (ps
.rcPaint
.top
< editor
->rcFormat
.top
)
4838 rc
.bottom
= editor
->rcFormat
.top
;
4839 FillRect(hDC
, &rc
, editor
->hbrBackground
);
4840 ps
.rcPaint
.top
= editor
->rcFormat
.top
;
4842 if (ps
.rcPaint
.bottom
> editor
->rcFormat
.bottom
) {
4844 rc
.top
= editor
->rcFormat
.bottom
;
4845 FillRect(hDC
, &rc
, editor
->hbrBackground
);
4846 ps
.rcPaint
.bottom
= editor
->rcFormat
.bottom
;
4848 if (ps
.rcPaint
.left
< editor
->rcFormat
.left
) {
4850 rc
.right
= editor
->rcFormat
.left
;
4851 FillRect(hDC
, &rc
, editor
->hbrBackground
);
4852 ps
.rcPaint
.left
= editor
->rcFormat
.left
;
4854 if (ps
.rcPaint
.right
> editor
->rcFormat
.right
) {
4856 rc
.left
= editor
->rcFormat
.right
;
4857 FillRect(hDC
, &rc
, editor
->hbrBackground
);
4858 ps
.rcPaint
.right
= editor
->rcFormat
.right
;
4861 ME_PaintContent(editor
, hDC
, &ps
.rcPaint
);
4862 EndPaint(editor
->hWnd
, &ps
);
4867 HDC hDC
= (HDC
)wParam
;
4870 if (GetUpdateRect(editor
->hWnd
, &rc
, TRUE
))
4871 FillRect(hDC
, &rc
, editor
->hbrBackground
);
4877 const DWORD mask
= ECO_VERTICAL
| ECO_AUTOHSCROLL
| ECO_AUTOVSCROLL
|
4878 ECO_NOHIDESEL
| ECO_READONLY
| ECO_WANTRETURN
|
4880 lresult
= ME_HandleMessage(editor
, msg
, wParam
, lParam
, unicode
, &hresult
);
4881 dwStyle
= GetWindowLongW(hWnd
, GWL_STYLE
);
4882 dwStyle
= (dwStyle
& ~mask
) | (lresult
& mask
);
4883 SetWindowLongW(hWnd
, GWL_STYLE
, dwStyle
);
4886 case EM_SETREADONLY
:
4889 lresult
= ME_HandleMessage(editor
, msg
, wParam
, lParam
, unicode
, &hresult
);
4890 dwStyle
= GetWindowLongW(hWnd
, GWL_STYLE
);
4891 dwStyle
&= ~ES_READONLY
;
4893 dwStyle
|= ES_READONLY
;
4894 SetWindowLongW(hWnd
, GWL_STYLE
, dwStyle
);
4898 lresult
= ME_HandleMessage(editor
, msg
, wParam
, lParam
, unicode
, &hresult
);
4901 if (hresult
== S_FALSE
)
4902 lresult
= DefWindowProcW(hWnd
, msg
, wParam
, lParam
);
4904 TRACE("exit hwnd %p msg %04x (%s) %lx %lx, unicode %d -> %lu\n",
4905 hWnd
, msg
, get_msg_name(msg
), wParam
, lParam
, unicode
, lresult
);
4910 static LRESULT WINAPI
RichEditWndProcW(HWND hWnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
)
4912 BOOL unicode
= TRUE
;
4914 /* Under Win9x RichEdit20W returns ANSI strings, see the tests. */
4915 if (msg
== WM_GETTEXT
&& (GetVersion() & 0x80000000))
4918 return RichEditWndProc_common(hWnd
, msg
, wParam
, lParam
, unicode
);
4921 static LRESULT WINAPI
RichEditWndProcA(HWND hWnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
)
4923 return RichEditWndProc_common(hWnd
, msg
, wParam
, lParam
, FALSE
);
4926 /******************************************************************
4927 * RichEditANSIWndProc (RICHED20.10)
4929 LRESULT WINAPI
RichEditANSIWndProc(HWND hWnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
)
4931 return RichEditWndProcA(hWnd
, msg
, wParam
, lParam
);
4934 /******************************************************************
4935 * RichEdit10ANSIWndProc (RICHED20.9)
4937 LRESULT WINAPI
RichEdit10ANSIWndProc(HWND hWnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
)
4939 if (msg
== WM_NCCREATE
&& !GetWindowLongPtrW(hWnd
, 0))
4941 ITextHost
*texthost
;
4942 CREATESTRUCTW
*pcs
= (CREATESTRUCTW
*)lParam
;
4944 TRACE("WM_NCCREATE: hWnd %p style 0x%08x\n", hWnd
, pcs
->style
);
4945 texthost
= ME_CreateTextHost(hWnd
, pcs
, TRUE
);
4946 return texthost
!= NULL
;
4948 return RichEditANSIWndProc(hWnd
, msg
, wParam
, lParam
);
4951 void ME_SendOldNotify(ME_TextEditor
*editor
, int nCode
)
4953 ITextHost_TxNotify(editor
->texthost
, nCode
, NULL
);
4956 /* Fill buffer with srcChars unicode characters from the start cursor.
4958 * buffer: destination buffer
4959 * buflen: length of buffer in characters excluding the NULL terminator.
4960 * start: start of editor text to copy into buffer.
4961 * srcChars: Number of characters to use from the editor text.
4962 * bCRLF: if true, replaces all end of lines with \r\n pairs.
4964 * returns the number of characters written excluding the NULL terminator.
4966 * The written text is always NULL terminated.
4968 int ME_GetTextW(ME_TextEditor
*editor
, WCHAR
*buffer
, int buflen
,
4969 const ME_Cursor
*start
, int srcChars
, BOOL bCRLF
,
4972 ME_DisplayItem
*pRun
, *pNextRun
;
4973 const WCHAR
*pStart
= buffer
;
4974 const WCHAR cr_lf
[] = {'\r', '\n', 0};
4978 /* bCRLF flag is only honored in 2.0 and up. 1.0 must always return text verbatim */
4979 if (editor
->bEmulateVersion10
) bCRLF
= FALSE
;
4983 pNextRun
= ME_FindItemFwd(pRun
, diRun
);
4985 nLen
= pRun
->member
.run
.len
- start
->nOffset
;
4986 str
= get_text( &pRun
->member
.run
, start
->nOffset
);
4988 while (srcChars
&& buflen
&& pNextRun
)
4990 int nFlags
= pRun
->member
.run
.nFlags
;
4992 if (bCRLF
&& nFlags
& MERF_ENDPARA
&& ~nFlags
& MERF_ENDCELL
)
4994 if (buflen
== 1) break;
4995 /* FIXME: native fails to reduce srcChars here for WM_GETTEXT or
4996 * EM_GETTEXTEX, however, this is done for copying text which
4997 * also uses this function. */
4998 srcChars
-= min(nLen
, srcChars
);
5002 nLen
= min(nLen
, srcChars
);
5006 nLen
= min(nLen
, buflen
);
5009 CopyMemory(buffer
, str
, sizeof(WCHAR
) * nLen
);
5014 pNextRun
= ME_FindItemFwd(pRun
, diRun
);
5016 nLen
= pRun
->member
.run
.len
;
5017 str
= get_text( &pRun
->member
.run
, 0 );
5019 /* append '\r' to the last paragraph. */
5020 if (pRun
->next
->type
== diTextEnd
&& bEOP
)
5026 return buffer
- pStart
;
5029 static BOOL
ME_RegisterEditorClass(HINSTANCE hInstance
)
5034 wcW
.style
= CS_DBLCLKS
| CS_HREDRAW
| CS_VREDRAW
| CS_GLOBALCLASS
;
5035 wcW
.lpfnWndProc
= RichEditWndProcW
;
5037 wcW
.cbWndExtra
= sizeof(ME_TextEditor
*);
5038 wcW
.hInstance
= NULL
; /* hInstance would register DLL-local class */
5040 wcW
.hCursor
= LoadCursorW(NULL
, (LPWSTR
)IDC_IBEAM
);
5041 wcW
.hbrBackground
= GetStockObject(NULL_BRUSH
);
5042 wcW
.lpszMenuName
= NULL
;
5044 if (is_version_nt())
5046 wcW
.lpszClassName
= RICHEDIT_CLASS20W
;
5047 if (!RegisterClassW(&wcW
)) return FALSE
;
5048 wcW
.lpszClassName
= MSFTEDIT_CLASS
;
5049 if (!RegisterClassW(&wcW
)) return FALSE
;
5053 /* WNDCLASSA/W have the same layout */
5054 wcW
.lpszClassName
= (LPCWSTR
)"RichEdit20W";
5055 if (!RegisterClassA((WNDCLASSA
*)&wcW
)) return FALSE
;
5056 wcW
.lpszClassName
= (LPCWSTR
)"RichEdit50W";
5057 if (!RegisterClassA((WNDCLASSA
*)&wcW
)) return FALSE
;
5060 wcA
.style
= CS_DBLCLKS
| CS_HREDRAW
| CS_VREDRAW
| CS_GLOBALCLASS
;
5061 wcA
.lpfnWndProc
= RichEditWndProcA
;
5063 wcA
.cbWndExtra
= sizeof(ME_TextEditor
*);
5064 wcA
.hInstance
= NULL
; /* hInstance would register DLL-local class */
5066 wcA
.hCursor
= LoadCursorW(NULL
, (LPWSTR
)IDC_IBEAM
);
5067 wcA
.hbrBackground
= GetStockObject(NULL_BRUSH
);
5068 wcA
.lpszMenuName
= NULL
;
5069 wcA
.lpszClassName
= RICHEDIT_CLASS20A
;
5070 if (!RegisterClassA(&wcA
)) return FALSE
;
5071 wcA
.lpszClassName
= "RichEdit50A";
5072 if (!RegisterClassA(&wcA
)) return FALSE
;
5077 static LRESULT WINAPI
REComboWndProc(HWND hWnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
) {
5078 /* FIXME: Not implemented */
5079 TRACE("hWnd %p msg %04x (%s) %08lx %08lx\n",
5080 hWnd
, msg
, get_msg_name(msg
), wParam
, lParam
);
5081 return DefWindowProcW(hWnd
, msg
, wParam
, lParam
);
5084 static LRESULT WINAPI
REListWndProc(HWND hWnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
) {
5085 /* FIXME: Not implemented */
5086 TRACE("hWnd %p msg %04x (%s) %08lx %08lx\n",
5087 hWnd
, msg
, get_msg_name(msg
), wParam
, lParam
);
5088 return DefWindowProcW(hWnd
, msg
, wParam
, lParam
);
5091 /******************************************************************
5092 * REExtendedRegisterClass (RICHED20.8)
5094 * FIXME undocumented
5095 * Need to check for errors and implement controls and callbacks
5097 LRESULT WINAPI
REExtendedRegisterClass(void)
5102 FIXME("semi stub\n");
5106 wcW
.hInstance
= NULL
;
5109 wcW
.hbrBackground
= (HBRUSH
)(COLOR_WINDOW
+1);
5110 wcW
.lpszMenuName
= NULL
;
5112 if (!ME_ListBoxRegistered
)
5114 wcW
.style
= CS_PARENTDC
| CS_DBLCLKS
| CS_GLOBALCLASS
;
5115 wcW
.lpfnWndProc
= REListWndProc
;
5116 wcW
.lpszClassName
= REListBox20W
;
5117 if (RegisterClassW(&wcW
)) ME_ListBoxRegistered
= TRUE
;
5120 if (!ME_ComboBoxRegistered
)
5122 wcW
.style
= CS_PARENTDC
| CS_DBLCLKS
| CS_GLOBALCLASS
| CS_VREDRAW
| CS_HREDRAW
;
5123 wcW
.lpfnWndProc
= REComboWndProc
;
5124 wcW
.lpszClassName
= REComboBox20W
;
5125 if (RegisterClassW(&wcW
)) ME_ComboBoxRegistered
= TRUE
;
5129 if (ME_ListBoxRegistered
)
5131 if (ME_ComboBoxRegistered
)
5137 static int wchar_comp( const void *key
, const void *elem
)
5139 return *(const WCHAR
*)key
- *(const WCHAR
*)elem
;
5142 /* neutral characters end the url if the next non-neutral character is a space character,
5143 otherwise they are included in the url. */
5144 static BOOL
isurlneutral( WCHAR c
)
5146 /* NB this list is sorted */
5147 static const WCHAR neutral_chars
[] = {'!','\"','\'','(',')',',','-','.',':',';','<','>','?','[',']','{','}'};
5149 /* Some shortcuts */
5150 if (isalnum( c
)) return FALSE
;
5151 if (c
> neutral_chars
[sizeof(neutral_chars
) / sizeof(neutral_chars
[0]) - 1]) return FALSE
;
5153 return !!bsearch( &c
, neutral_chars
, sizeof(neutral_chars
) / sizeof(neutral_chars
[0]),
5154 sizeof(c
), wchar_comp
);
5158 * This proc takes a selection, and scans it forward in order to select the span
5159 * of a possible URL candidate. A possible URL candidate must start with isalnum
5160 * or one of the following special characters: *|/\+%#@ and must consist entirely
5161 * of the characters allowed to start the URL, plus : (colon) which may occur
5162 * at most once, and not at either end.
5164 static BOOL
ME_FindNextURLCandidate(ME_TextEditor
*editor
,
5165 const ME_Cursor
*start
,
5167 ME_Cursor
*candidate_min
,
5168 ME_Cursor
*candidate_max
)
5170 ME_Cursor cursor
= *start
, neutral_end
, space_end
;
5171 BOOL candidateStarted
= FALSE
, quoted
= FALSE
;
5176 WCHAR
*str
= get_text( &cursor
.pRun
->member
.run
, 0 );
5177 int run_len
= cursor
.pRun
->member
.run
.len
;
5179 nChars
-= run_len
- cursor
.nOffset
;
5181 /* Find start of candidate */
5182 if (!candidateStarted
)
5184 while (cursor
.nOffset
< run_len
)
5186 c
= str
[cursor
.nOffset
];
5187 if (!isspaceW( c
) && !isurlneutral( c
))
5189 *candidate_min
= cursor
;
5190 candidateStarted
= TRUE
;
5191 neutral_end
.pPara
= NULL
;
5192 space_end
.pPara
= NULL
;
5196 quoted
= (c
== '<');
5201 /* Find end of candidate */
5202 if (candidateStarted
)
5204 while (cursor
.nOffset
< run_len
)
5206 c
= str
[cursor
.nOffset
];
5209 if (quoted
&& c
!= '\r')
5211 if (!space_end
.pPara
)
5213 if (neutral_end
.pPara
)
5214 space_end
= neutral_end
;
5222 else if (isurlneutral( c
))
5224 if (quoted
&& c
== '>')
5226 neutral_end
.pPara
= NULL
;
5227 space_end
.pPara
= NULL
;
5230 if (!neutral_end
.pPara
)
5231 neutral_end
= cursor
;
5234 neutral_end
.pPara
= NULL
;
5241 if (!ME_NextRun(&cursor
.pPara
, &cursor
.pRun
, TRUE
))
5246 if (candidateStarted
)
5248 if (space_end
.pPara
)
5249 *candidate_max
= space_end
;
5250 else if (neutral_end
.pPara
)
5251 *candidate_max
= neutral_end
;
5253 *candidate_max
= cursor
;
5256 *candidate_max
= *candidate_min
= cursor
;
5261 * This proc evaluates the selection and returns TRUE if it can be considered an URL
5263 static BOOL
ME_IsCandidateAnURL(ME_TextEditor
*editor
, const ME_Cursor
*start
, int nChars
)
5265 #define MAX_PREFIX_LEN 9
5267 const WCHAR text
[MAX_PREFIX_LEN
];
5270 {{'p','r','o','s','p','e','r','o',':'}, 9},
5271 {{'t','e','l','n','e','t',':'}, 7},
5272 {{'g','o','p','h','e','r',':'}, 7},
5273 {{'m','a','i','l','t','o',':'}, 7},
5274 {{'h','t','t','p','s',':'}, 6},
5275 {{'f','i','l','e',':'}, 5},
5276 {{'n','e','w','s',':'}, 5},
5277 {{'w','a','i','s',':'}, 5},
5278 {{'n','n','t','p',':'}, 5},
5279 {{'h','t','t','p',':'}, 5},
5280 {{'w','w','w','.'}, 4},
5281 {{'f','t','p',':'}, 4},
5283 WCHAR bufferW
[MAX_PREFIX_LEN
+ 1];
5286 ME_GetTextW(editor
, bufferW
, MAX_PREFIX_LEN
, start
, nChars
, FALSE
, FALSE
);
5287 for (i
= 0; i
< sizeof(prefixes
) / sizeof(*prefixes
); i
++)
5289 if (nChars
< prefixes
[i
].length
) continue;
5290 if (!memcmp(prefixes
[i
].text
, bufferW
, prefixes
[i
].length
* sizeof(WCHAR
)))
5294 #undef MAX_PREFIX_LEN
5298 * This proc walks through the indicated selection and evaluates whether each
5299 * section identified by ME_FindNextURLCandidate and in-between sections have
5300 * their proper CFE_LINK attributes set or unset. If the CFE_LINK attribute is
5301 * not what it is supposed to be, this proc sets or unsets it as appropriate.
5303 * Since this function can cause runs to be split, do not depend on the value
5304 * of the start cursor at the end of the function.
5306 * nChars may be set to INT_MAX to update to the end of the text.
5308 * Returns TRUE if at least one section was modified.
5310 static BOOL
ME_UpdateLinkAttribute(ME_TextEditor
*editor
, ME_Cursor
*start
, int nChars
)
5312 BOOL modified
= FALSE
;
5313 ME_Cursor startCur
= *start
;
5315 if (!editor
->AutoURLDetect_bEnable
) return FALSE
;
5320 ME_Cursor candidateStart
, candidateEnd
;
5322 if (ME_FindNextURLCandidate(editor
, &startCur
, nChars
,
5323 &candidateStart
, &candidateEnd
))
5325 /* Section before candidate is not an URL */
5326 int cMin
= ME_GetCursorOfs(&candidateStart
);
5327 int cMax
= ME_GetCursorOfs(&candidateEnd
);
5329 if (!ME_IsCandidateAnURL(editor
, &candidateStart
, cMax
- cMin
))
5330 candidateStart
= candidateEnd
;
5331 nChars
-= cMax
- ME_GetCursorOfs(&startCur
);
5335 /* No more candidates until end of selection */
5339 if (startCur
.pRun
!= candidateStart
.pRun
||
5340 startCur
.nOffset
!= candidateStart
.nOffset
)
5342 /* CFE_LINK effect should be consistently unset */
5343 link
.cbSize
= sizeof(link
);
5344 ME_GetCharFormat(editor
, &startCur
, &candidateStart
, &link
);
5345 if (!(link
.dwMask
& CFM_LINK
) || (link
.dwEffects
& CFE_LINK
))
5347 /* CFE_LINK must be unset from this range */
5348 memset(&link
, 0, sizeof(CHARFORMAT2W
));
5349 link
.cbSize
= sizeof(link
);
5350 link
.dwMask
= CFM_LINK
;
5352 ME_SetCharFormat(editor
, &startCur
, &candidateStart
, &link
);
5353 /* Update candidateEnd since setting character formats may split
5354 * runs, which can cause a cursor to be at an invalid offset within
5356 while (candidateEnd
.nOffset
>= candidateEnd
.pRun
->member
.run
.len
)
5358 candidateEnd
.nOffset
-= candidateEnd
.pRun
->member
.run
.len
;
5359 candidateEnd
.pRun
= ME_FindItemFwd(candidateEnd
.pRun
, diRun
);
5364 if (candidateStart
.pRun
!= candidateEnd
.pRun
||
5365 candidateStart
.nOffset
!= candidateEnd
.nOffset
)
5367 /* CFE_LINK effect should be consistently set */
5368 link
.cbSize
= sizeof(link
);
5369 ME_GetCharFormat(editor
, &candidateStart
, &candidateEnd
, &link
);
5370 if (!(link
.dwMask
& CFM_LINK
) || !(link
.dwEffects
& CFE_LINK
))
5372 /* CFE_LINK must be set on this range */
5373 memset(&link
, 0, sizeof(CHARFORMAT2W
));
5374 link
.cbSize
= sizeof(link
);
5375 link
.dwMask
= CFM_LINK
;
5376 link
.dwEffects
= CFE_LINK
;
5377 ME_SetCharFormat(editor
, &candidateStart
, &candidateEnd
, &link
);
5381 startCur
= candidateEnd
;
5382 } while (nChars
> 0);