2 * RichEdit - functions dealing with editor object
4 * Copyright 2004 by Krzysztof Foltman
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 API implementation status:
24 Messages (ANSI versions not done yet)
25 - EM_AUTOURLDETECT 2.0
41 - EM_GETCHARFORMAT (partly done)
43 - EM_GETFIRSTVISIBLELINE
44 - EM_GETIMECOLOR 1.0asian
45 - EM_GETIMECOMPMODE 2.0
46 - EM_GETIMEOPTIONS 1.0asian
48 - EM_GETLANGOPTIONS 2.0
51 - EM_GETLINECOUNT returns number of rows, not of paragraphs
56 - EM_GETPUNCTUATION 1.0asian
60 + EM_GETSELTEXT (ANSI&Unicode)
63 ? + EM_GETTEXTRANGE (ANSI&Unicode)
66 - EM_GETWORDBREAKPROCEX
67 - EM_GETWORDWRAPMODE 1.0asian
78 + EM_REPLACESEL (proper style?) ANSI&Unicode
83 - EM_SETCHARFORMAT (partly done, no ANSI)
84 + EM_SETEVENTMASK (few notifications supported)
85 - EM_SETIMECOLOR 1.0asian
86 - EM_SETIMEOPTIONS 1.0asian
87 - EM_SETLANGOPTIONS 2.0
89 + EM_SETMODIFY (not sure if implementation is correct)
93 - EM_SETPUNCTUATION 1.0asian
94 + EM_SETREADONLY no beep on modification attempt
96 - EM_SETRECTNP (EM_SETRECT without repainting) - not supported in RICHEDIT
100 - EM_SETUNDOLIMIT 2.0
101 - EM_SETWORDBREAKPROC
102 - EM_SETWORDBREAKPROCEX
103 - EM_SETWORDWRAPMODE 1.0asian
104 - EM_STOPGROUPTYPING 2.0
105 + EM_STREAMIN (can't fall back to text when the RTF isn't really RTF)
112 + WM_GETDLGCODE (the current implementation is incomplete)
113 + WM_GETTEXT (ANSI&Unicode)
114 + WM_GETTEXTLENGTH (ANSI version sucks)
117 + WM_SETTEXT (resets undo stack !) (proper style?) ANSI&Unicode
119 - WM_STYLECHANGED (things like read-only flag)
124 * EN_CHANGE (sent from the wrong place)
141 * EN_UPDATE (sent from the wrong place)
149 - ES_DISABLENOSCROLL (scrollbar is always visible)
150 - ES_EX_NOCALLOLEINIT
152 - ES_MULTILINE (currently single line controls aren't supported)
154 - ES_READONLY (I'm not sure if beeping is the proper behaviour)
160 - ES_WANTRETURN (don't know how to do WM_GETDLGCODE part)
167 * RICHED20 TODO (incomplete):
169 * - messages/styles/notifications listed above
171 * - add remaining CHARFORMAT/PARAFORMAT fields
172 * - right/center align should strip spaces from the beginning
173 * - more advanced navigation (Ctrl-arrows)
175 * - pictures/OLE objects (not just smiling faces that lack API support ;-) )
176 * - COM interface (looks like a major pain in the TODO list)
177 * - calculate heights of pictures (half-done)
178 * - horizontal scrolling (not even started)
179 * - hysteresis during wrapping (related to scrollbars appearing/disappearing)
181 * - how to implement EM_FORMATRANGE and EM_DISPLAYBAND ? (Mission Impossible)
182 * - italic caret with italic fonts
184 * - most notifications aren't sent at all (the most important ones are)
185 * - when should EN_SELCHANGE be sent after text change ? (before/after EN_UPDATE?)
186 * - WM_SETTEXT may use wrong style (but I'm 80% sure it's OK)
187 * - EM_GETCHARFORMAT with SCF_SELECTION may not behave 100% like in original (but very close)
189 * Bugs that are probably fixed, but not so easy to verify:
190 * - EN_UPDATE/EN_CHANGE are handled very incorrectly (should be OK now)
191 * - undo for ME_JoinParagraphs doesn't store paragraph format ? (it does)
192 * - check/fix artificial EOL logic (bCursorAtEnd, hardly logical)
193 * - caret shouldn't be displayed when selection isn't empty
194 * - check refcounting in style management functions (looks perfect now, but no bugs is suspicious)
195 * - undo for setting default format (done, might be buggy)
196 * - styles might be not released properly (looks like they work like charm, but who knows?
204 #define NO_SHLWAPI_STREAM
209 WINE_DEFAULT_DEBUG_CHANNEL(richedit);
212 HANDLE me_heap = NULL;
214 ME_TextBuffer *ME_MakeText() {
216 ME_TextBuffer *buf = ALLOC_OBJ(ME_TextBuffer);
218 ME_DisplayItem *p1 = ME_MakeDI(diTextStart);
219 ME_DisplayItem *p2 = ME_MakeDI(diTextEnd);
225 p1->member.para.next_para = p2;
226 p2->member.para.prev_para = p1;
227 p2->member.para.nCharOfs = 0;
231 buf->pCharStyle = NULL;
237 static LRESULT ME_StreamInText(ME_TextEditor *editor, DWORD dwFormat, ME_InStream *stream, ME_Style *style)
239 WCHAR wszText[STREAMIN_BUFFER_SIZE+1];
242 TRACE("%08lx %p\n", dwFormat, stream);
249 ME_StreamInFill(stream);
250 if (stream->editstream->dwError)
256 if (!(dwFormat & SF_UNICODE))
258 /* FIXME? this is doomed to fail on true MBCS like UTF-8, luckily they're unlikely to be used as CP_ACP */
259 nWideChars = MultiByteToWideChar(CP_ACP, 0, stream->buffer, stream->dwSize, wszText, STREAMIN_BUFFER_SIZE);
264 nWideChars = stream->dwSize >> 1;
265 pText = (WCHAR *)stream->buffer;
268 ME_InsertTextFromCursor(editor, 0, pText, nWideChars, style);
269 if (stream->dwSize < STREAMIN_BUFFER_SIZE)
273 ME_CommitUndo(editor);
278 void ME_RTFCharAttrHook(RTF_Info *info)
281 fmt.cbSize = sizeof(fmt);
284 switch(info->rtfMinor)
287 /* FIXME add more flags once they're implemented */
288 fmt.dwMask = CFM_BOLD | CFM_ITALIC | CFM_UNDERLINE | CFM_STRIKEOUT | CFM_COLOR | CFM_BACKCOLOR | CFM_SIZE | CFM_WEIGHT;
289 fmt.dwEffects = CFE_AUTOCOLOR | CFE_AUTOBACKCOLOR;
290 fmt.yHeight = 12*20; /* 12pt */
294 fmt.dwMask = CFM_BOLD;
295 fmt.dwEffects = info->rtfParam ? fmt.dwMask : 0;
298 fmt.dwMask = CFM_ITALIC;
299 fmt.dwEffects = info->rtfParam ? fmt.dwMask : 0;
302 fmt.dwMask = CFM_UNDERLINE;
303 fmt.dwEffects = info->rtfParam ? fmt.dwMask : 0;
306 fmt.dwMask = CFM_STRIKEOUT;
307 fmt.dwEffects = info->rtfParam ? fmt.dwMask : 0;
310 fmt.dwMask = CFM_BACKCOLOR;
312 if (info->rtfParam == 0)
313 fmt.dwEffects = CFE_AUTOBACKCOLOR;
314 else if (info->rtfParam != rtfNoParam)
316 RTFColor *c = RTFGetColor(info, info->rtfParam);
317 fmt.crTextColor = (c->rtfCBlue<<16)|(c->rtfCGreen<<8)|(c->rtfCRed);
321 fmt.dwMask = CFM_COLOR;
323 if (info->rtfParam == 0)
324 fmt.dwEffects = CFE_AUTOCOLOR;
325 else if (info->rtfParam != rtfNoParam)
327 RTFColor *c = RTFGetColor(info, info->rtfParam);
328 fmt.crTextColor = (c->rtfCBlue<<16)|(c->rtfCGreen<<8)|(c->rtfCRed);
332 if (info->rtfParam != rtfNoParam)
334 RTFFont *f = RTFGetFont(info, info->rtfParam);
337 MultiByteToWideChar(CP_ACP, 0, f->rtfFName, -1, fmt.szFaceName, sizeof(fmt.szFaceName)/sizeof(WCHAR));
338 fmt.szFaceName[sizeof(fmt.szFaceName)/sizeof(WCHAR)-1] = '\0';
339 fmt.bCharSet = f->rtfFCharSet;
340 fmt.dwMask = CFM_FACE | CFM_CHARSET;
345 fmt.dwMask = CFM_SIZE;
346 if (info->rtfParam != rtfNoParam)
347 fmt.yHeight = info->rtfParam*10;
352 RTFFlushOutputBuffer(info);
353 /* FIXME too slow ? how come ? */
354 style2 = ME_ApplyStyle(info->style, &fmt);
355 ME_ReleaseStyle(info->style);
356 info->style = style2;
360 /* FIXME this function doesn't get any information about context of the RTF tag, which is very bad,
361 the same tags mean different things in different contexts */
362 void ME_RTFParAttrHook(RTF_Info *info)
365 fmt.cbSize = sizeof(fmt);
368 switch(info->rtfMinor)
370 case rtfParDef: /* I'm not 100% sure what does it do, but I guess it restores default paragraph attributes */
371 fmt.dwMask = PFM_ALIGNMENT | PFM_TABSTOPS | PFM_OFFSET | PFM_STARTINDENT;
372 fmt.wAlignment = PFA_LEFT;
374 fmt.dxOffset = fmt.dxStartIndent = 0;
377 ME_GetSelectionParaFormat(info->editor, &fmt);
378 fmt.dwMask = PFM_STARTINDENT;
379 fmt.dxStartIndent = info->rtfParam + fmt.dxOffset;
384 ME_GetSelectionParaFormat(info->editor, &fmt);
385 first = fmt.dxStartIndent;
386 left = info->rtfParam;
387 fmt.dwMask = PFM_STARTINDENT|PFM_OFFSET;
388 fmt.dxStartIndent = first + left;
389 fmt.dxOffset = -first;
393 fmt.dwMask = PFM_RIGHTINDENT;
394 fmt.dxRightIndent = info->rtfParam;
398 fmt.dwMask = PFM_ALIGNMENT;
399 fmt.wAlignment = PFA_LEFT;
402 fmt.dwMask = PFM_ALIGNMENT;
403 fmt.wAlignment = PFA_RIGHT;
406 fmt.dwMask = PFM_ALIGNMENT;
407 fmt.wAlignment = PFA_CENTER;
410 ME_GetSelectionParaFormat(info->editor, &fmt);
411 if (!(fmt.dwMask & PFM_TABSTOPS))
413 fmt.dwMask |= PFM_TABSTOPS;
416 if (fmt.cTabCount < MAX_TAB_STOPS)
417 fmt.rgxTabs[fmt.cTabCount++] = info->rtfParam;
421 RTFFlushOutputBuffer(info);
422 /* FIXME too slow ? how come ?*/
423 ME_SetSelectionParaFormat(info->editor, &fmt);
427 void ME_RTFReadHook(RTF_Info *info) {
428 switch(info->rtfClass)
431 switch(info->rtfMajor)
434 if (info->stackTop < maxStack) {
435 memcpy(&info->stack[info->stackTop].fmt, &info->style->fmt, sizeof(CHARFORMAT2W));
436 info->stack[info->stackTop].codePage = info->codePage;
437 info->stack[info->stackTop].unicodeLength = info->unicodeLength;
444 RTFFlushOutputBuffer(info);
446 /* FIXME too slow ? how come ? */
447 s = ME_ApplyStyle(info->style, &info->stack[info->stackTop].fmt);
448 ME_ReleaseStyle(info->style);
450 info->codePage = info->stack[info->stackTop].codePage;
451 info->unicodeLength = info->stack[info->stackTop].unicodeLength;
457 switch(info->rtfMajor)
460 ME_RTFCharAttrHook(info);
463 ME_RTFParAttrHook(info);
471 ME_StreamInFill(ME_InStream *stream)
473 stream->editstream->dwError = stream->editstream->pfnCallback(stream->editstream->dwCookie,
475 sizeof(stream->buffer),
480 static LRESULT ME_StreamIn(ME_TextEditor *editor, DWORD format, EDITSTREAM *stream)
484 int from, to, to2, nUndoMode;
486 int nEventMask = editor->nEventMask;
487 ME_InStream inStream;
489 TRACE("stream==%p hWnd==%p format==0x%X\n", stream, editor->hWnd, (UINT)format);
490 editor->nEventMask = 0;
492 ME_GetSelection(editor, &from, &to);
493 if (format & SFF_SELECTION) {
494 style = ME_GetSelectionInsertStyle(editor);
496 ME_InternalDeleteText(editor, from, to-from);
499 style = editor->pBuffer->pDefaultStyle;
500 ME_AddRefStyle(style);
501 SendMessageA(editor->hWnd, EM_SETSEL, 0, 0);
502 ME_InternalDeleteText(editor, 0, ME_GetTextLength(editor));
504 ME_ClearTempStyle(editor);
505 /* FIXME restore default paragraph formatting ! */
508 nUndoMode = editor->nUndoMode;
509 editor->nUndoMode = umIgnore;
511 inStream.editstream = stream;
512 inStream.editstream->dwError = 0;
518 /* Check if it's really RTF, and if it is not, use plain text */
519 ME_StreamInFill(&inStream);
520 if (!inStream.editstream->dwError)
522 if (strncmp(inStream.buffer, "{\\rtf1", 6) && strncmp(inStream.buffer, "{\\urtf", 6))
530 if (!inStream.editstream->dwError)
532 if (format & SF_RTF) {
533 /* setup the RTF parser */
534 memset(&parser, 0, sizeof parser);
535 RTFSetEditStream(&parser, &inStream);
536 parser.rtfFormat = format&(SF_TEXT|SF_RTF);
537 parser.hwndEdit = editor->hWnd;
538 parser.editor = editor;
539 parser.style = style;
542 RTFSetReadHook(&parser, ME_RTFReadHook);
547 RTFFlushOutputBuffer(&parser);
550 style = parser.style;
552 else if (format & SF_TEXT)
553 ME_StreamInText(editor, format, &inStream, style);
555 ERR("EM_STREAMIN without SF_TEXT or SF_RTF\n");
556 ME_GetSelection(editor, &to, &to2);
557 /* put the cursor at the top */
558 if (!(format & SFF_SELECTION))
559 SendMessageA(editor->hWnd, EM_SETSEL, 0, 0);
562 /* FIXME where to put cursor now ? */
566 editor->nUndoMode = nUndoMode;
567 pUI = ME_AddUndoItem(editor, diUndoDeleteRun, NULL);
568 TRACE("from %d to %d\n", from, to);
569 if (pUI && from < to)
574 ME_CommitUndo(editor);
575 ME_ReleaseStyle(style);
576 editor->nEventMask = nEventMask;
577 InvalidateRect(editor->hWnd, NULL, TRUE);
578 ME_UpdateRepaint(editor);
579 if (!(format & SFF_SELECTION)) {
580 ME_ClearTempStyle(editor);
582 ME_MoveCaret(editor);
583 ME_SendSelChange(editor);
590 ME_FindItemAtOffset(ME_TextEditor *editor, ME_DIType nItemType, int nOffset, int *nItemOffset)
592 ME_DisplayItem *item = ME_FindItemFwd(editor->pBuffer->pFirst, diParagraph);
594 while (item && item->member.para.next_para->member.para.nCharOfs <= nOffset)
595 item = ME_FindItemFwd(item, diParagraph);
600 nOffset -= item->member.para.nCharOfs;
601 if (nItemType == diParagraph) {
603 *nItemOffset = nOffset;
608 item = ME_FindItemFwd(item, diRun);
609 } while (item && (item->member.run.nCharOfs + ME_StrLen(item->member.run.strText) <= nOffset));
611 nOffset -= item->member.run.nCharOfs;
613 *nItemOffset = nOffset;
619 ME_TextEditor *ME_MakeEditor(HWND hWnd) {
620 ME_TextEditor *ed = ALLOC_OBJ(ME_TextEditor);
624 ed->pBuffer = ME_MakeText();
626 ME_MakeFirstParagraph(hDC, ed->pBuffer);
627 ReleaseDC(hWnd, hDC);
628 ed->bCaretShown = FALSE;
630 ed->pCursors = ALLOC_N_OBJ(ME_Cursor, ed->nCursors);
631 ed->pCursors[0].pRun = ME_FindItemFwd(ed->pBuffer->pFirst, diRun);
632 ed->pCursors[0].nOffset = 0;
633 ed->pCursors[1].pRun = ME_FindItemFwd(ed->pBuffer->pFirst, diRun);
634 ed->pCursors[1].nOffset = 0;
635 ed->nLastTotalLength = ed->nTotalLength = 0;
638 ed->rgbBackColor = -1;
639 ed->bCaretAtEnd = FALSE;
642 ed->pUndoStack = ed->pRedoStack = NULL;
643 ed->nUndoMode = umAddToUndo;
645 ed->nLastSelStart = ed->nLastSelEnd = 0;
647 for (i=0; i<HFONT_CACHE_SIZE; i++)
649 ed->pFontCache[i].nRefs = 0;
650 ed->pFontCache[i].nAge = 0;
651 ed->pFontCache[i].hFont = NULL;
653 ME_CheckCharOffsets(ed);
657 typedef struct tagME_GlobalDestStruct
661 } ME_GlobalDestStruct;
663 static DWORD CALLBACK ME_AppendToHGLOBAL(DWORD_PTR dwCookie, LPBYTE lpBuff, LONG cb, LONG *pcb)
665 ME_GlobalDestStruct *pData = (ME_GlobalDestStruct *)dwCookie;
669 nMaxSize = GlobalSize(pData->hData);
670 if (pData->nLength+cb+1 >= cb)
672 /* round up to 2^17 */
673 int nNewSize = (((nMaxSize+cb+1)|0x1FFFF)+1) & 0xFFFE0000;
674 pData->hData = GlobalReAlloc(pData->hData, nNewSize, 0);
676 pDest = (BYTE *)GlobalLock(pData->hData);
677 memcpy(pDest + pData->nLength, lpBuff, cb);
678 pData->nLength += cb;
679 pDest[pData->nLength] = '\0';
680 GlobalUnlock(pData->hData);
686 static DWORD CALLBACK ME_ReadFromHGLOBALUnicode(DWORD_PTR dwCookie, LPBYTE lpBuff, LONG cb, LONG *pcb)
688 ME_GlobalDestStruct *pData = (ME_GlobalDestStruct *)dwCookie;
693 pDest = (WORD *)lpBuff;
694 pSrc = (WORD *)GlobalLock(pData->hData);
695 for (i = 0; i<cb && pSrc[pData->nLength+i]; i++) {
696 pDest[i] = pSrc[pData->nLength+i];
700 GlobalUnlock(pData->hData);
704 static DWORD CALLBACK ME_ReadFromHGLOBALRTF(DWORD_PTR dwCookie, LPBYTE lpBuff, LONG cb, LONG *pcb)
706 ME_GlobalDestStruct *pData = (ME_GlobalDestStruct *)dwCookie;
711 pSrc = (BYTE *)GlobalLock(pData->hData);
712 for (i = 0; i<cb && pSrc[pData->nLength+i]; i++) {
713 pDest[i] = pSrc[pData->nLength+i];
717 GlobalUnlock(pData->hData);
721 void ME_DestroyEditor(ME_TextEditor *editor)
723 ME_DisplayItem *pFirst = editor->pBuffer->pFirst;
724 ME_DisplayItem *p = pFirst, *pNext = NULL;
727 ME_ClearTempStyle(editor);
728 ME_EmptyUndoStack(editor);
731 ME_DestroyDisplayItem(p);
734 ME_ReleaseStyle(editor->pBuffer->pDefaultStyle);
735 for (i=0; i<HFONT_CACHE_SIZE; i++)
737 if (editor->pFontCache[i].hFont)
738 DeleteObject(editor->pFontCache[i].hFont);
744 #define UNSUPPORTED_MSG(e) \
746 FIXME(#e ": stub\n"); \
747 return DefWindowProcW(hWnd, msg, wParam, lParam);
749 /******************************************************************
750 * RichEditANSIWndProc (RICHED20.10)
752 LRESULT WINAPI RichEditANSIWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) {
756 ME_TextEditor *editor = (ME_TextEditor *)GetWindowLongW(hWnd, 0);
757 TRACE("msg %d %08x %08lx\n", msg, wParam, lParam);
760 UNSUPPORTED_MSG(EM_AUTOURLDETECT)
761 UNSUPPORTED_MSG(EM_CHARFROMPOS)
762 UNSUPPORTED_MSG(EM_DISPLAYBAND)
763 UNSUPPORTED_MSG(EM_EXLIMITTEXT)
764 UNSUPPORTED_MSG(EM_EXLINEFROMCHAR)
765 UNSUPPORTED_MSG(EM_FINDTEXT)
766 UNSUPPORTED_MSG(EM_FINDTEXTEX)
767 UNSUPPORTED_MSG(EM_FINDWORDBREAK)
768 UNSUPPORTED_MSG(EM_FMTLINES)
769 UNSUPPORTED_MSG(EM_FORMATRANGE)
770 UNSUPPORTED_MSG(EM_GETFIRSTVISIBLELINE)
771 UNSUPPORTED_MSG(EM_GETIMECOMPMODE)
772 /* UNSUPPORTED_MSG(EM_GETIMESTATUS) missing in Wine headers */
773 UNSUPPORTED_MSG(EM_GETLANGOPTIONS)
774 UNSUPPORTED_MSG(EM_GETLIMITTEXT)
775 UNSUPPORTED_MSG(EM_GETLINE)
776 UNSUPPORTED_MSG(EM_GETLINECOUNT)
777 /* UNSUPPORTED_MSG(EM_GETOLEINTERFACE) separate stub */
778 UNSUPPORTED_MSG(EM_GETOPTIONS)
779 UNSUPPORTED_MSG(EM_GETRECT)
780 UNSUPPORTED_MSG(EM_GETREDONAME)
781 UNSUPPORTED_MSG(EM_GETTEXTMODE)
782 UNSUPPORTED_MSG(EM_GETUNDONAME)
783 UNSUPPORTED_MSG(EM_GETWORDBREAKPROC)
784 UNSUPPORTED_MSG(EM_GETWORDBREAKPROCEX)
785 UNSUPPORTED_MSG(EM_HIDESELECTION)
786 UNSUPPORTED_MSG(EM_LIMITTEXT) /* also known as EM_SETLIMITTEXT */
787 UNSUPPORTED_MSG(EM_LINEFROMCHAR)
788 UNSUPPORTED_MSG(EM_LINEINDEX)
789 UNSUPPORTED_MSG(EM_LINELENGTH)
790 UNSUPPORTED_MSG(EM_LINESCROLL)
791 UNSUPPORTED_MSG(EM_PASTESPECIAL)
792 /* UNSUPPORTED_MSG(EM_POSFROMCHARS) missing in Wine headers */
793 UNSUPPORTED_MSG(EM_REQUESTRESIZE)
794 UNSUPPORTED_MSG(EM_SCROLL)
795 UNSUPPORTED_MSG(EM_SCROLLCARET)
796 UNSUPPORTED_MSG(EM_SELECTIONTYPE)
797 UNSUPPORTED_MSG(EM_SETLANGOPTIONS)
798 UNSUPPORTED_MSG(EM_SETOLECALLBACK)
799 UNSUPPORTED_MSG(EM_SETOPTIONS)
800 UNSUPPORTED_MSG(EM_SETRECT)
801 UNSUPPORTED_MSG(EM_SETRECTNP)
802 UNSUPPORTED_MSG(EM_SETTARGETDEVICE)
803 UNSUPPORTED_MSG(EM_SETTEXTMODE)
804 UNSUPPORTED_MSG(EM_SETUNDOLIMIT)
805 UNSUPPORTED_MSG(EM_SETWORDBREAKPROC)
806 UNSUPPORTED_MSG(EM_SETWORDBREAKPROCEX)
807 UNSUPPORTED_MSG(WM_SETFONT)
808 UNSUPPORTED_MSG(WM_STYLECHANGING)
809 UNSUPPORTED_MSG(WM_STYLECHANGED)
810 /* UNSUPPORTED_MSG(WM_UNICHAR) FIXME missing in Wine headers */
812 /* Messages specific to Richedit controls */
815 return ME_StreamIn(editor, wParam, (EDITSTREAM*)lParam);
817 return ME_StreamOut(editor, wParam, (EDITSTREAM *)lParam);
820 UINT code = DLGC_WANTCHARS|DLGC_WANTARROWS;
821 if (GetWindowLongW(hWnd, GWL_STYLE)&ES_WANTRETURN)
822 code |= 0; /* FIXME what can we do here ? ask for messages and censor them ? */
827 CREATESTRUCTW *pcs = (CREATESTRUCTW *)lParam;
828 editor = ME_MakeEditor(hWnd);
829 SetWindowLongW(hWnd, 0, (long)editor);
830 pcs = 0; /* ignore */
833 case EM_EMPTYUNDOBUFFER:
834 ME_EmptyUndoStack(editor);
838 ME_GetSelection(editor, (int *)wParam, (int *)lParam);
839 if (!((wParam|lParam) & 0xFFFF0000))
840 return (lParam<<16)|wParam;
845 CHARRANGE *pRange = (CHARRANGE *)lParam;
846 ME_GetSelection(editor, (int *)&pRange->cpMin, (int *)&pRange->cpMax);
850 return editor->pUndoStack != NULL;
852 return editor->pRedoStack != NULL;
861 ME_SetSelection(editor, wParam, lParam);
863 ME_SendSelChange(editor);
868 CHARRANGE *pRange = (CHARRANGE *)lParam;
869 ME_SetSelection(editor, pRange->cpMin, pRange->cpMax);
872 ME_SendSelChange(editor);
875 case EM_SETBKGNDCOLOR:
877 LRESULT lColor = ME_GetBackColor(editor);
879 editor->rgbBackColor = -1;
881 editor->rgbBackColor = lParam;
882 InvalidateRect(hWnd, NULL, TRUE);
887 return editor->nModifyStep == 0 ? 0 : 1;
891 editor->nModifyStep = 0x80000000;
893 editor->nModifyStep = 0;
899 long nStyle = GetWindowLongW(hWnd, GWL_STYLE);
901 nStyle |= ES_READONLY;
903 nStyle &= ~ES_READONLY;
904 SetWindowLongW(hWnd, GWL_STYLE, nStyle);
908 case EM_SETEVENTMASK:
909 editor->nEventMask = lParam;
911 case EM_GETEVENTMASK:
912 return editor->nEventMask;
913 case EM_SETCHARFORMAT:
915 CHARFORMAT2W buf, *p;
916 BOOL bRepaint = TRUE;
917 p = ME_ToCF2W(&buf, (CHARFORMAT2W *)lParam);
919 ME_SetDefaultCharFormat(editor, p);
920 else if (wParam == (SCF_WORD | SCF_SELECTION))
921 FIXME("word selection not supported\n");
922 else if (wParam == SCF_ALL)
923 ME_SetCharFormat(editor, 0, ME_GetTextLength(editor), p);
926 ME_GetSelection(editor, &from, &to);
927 bRepaint = (from != to);
928 ME_SetSelectionCharFormat(editor, p);
930 ME_CommitUndo(editor);
932 ME_UpdateRepaint(editor);
935 case EM_GETCHARFORMAT:
938 tmp.cbSize = sizeof(tmp);
940 ME_GetDefaultCharFormat(editor, &tmp);
942 ME_GetSelectionCharFormat(editor, &tmp);
943 ME_CopyToCFAny((CHARFORMAT2W *)lParam, &tmp);
946 case EM_SETPARAFORMAT:
947 ME_SetSelectionParaFormat(editor, (PARAFORMAT2 *)lParam);
948 ME_UpdateRepaint(editor);
949 ME_CommitUndo(editor);
951 case EM_GETPARAFORMAT:
952 ME_GetSelectionParaFormat(editor, (PARAFORMAT2 *)lParam);
957 ME_GetSelection(editor, &from, &to);
958 ME_InternalDeleteText(editor, from, to-from);
959 ME_CommitUndo(editor);
960 ME_UpdateRepaint(editor);
967 LPWSTR wszText = ME_ToUnicode(hWnd, (void *)lParam);
968 size_t len = lstrlenW(wszText);
969 TRACE("EM_REPLACESEL - %s\n", debugstr_w(wszText));
971 ME_GetSelection(editor, &from, &to);
972 style = ME_GetSelectionInsertStyle(editor);
973 ME_InternalDeleteText(editor, from, to-from);
974 ME_InsertTextFromCursor(editor, 0, wszText, len, style);
975 ME_ReleaseStyle(style);
976 ME_EndToUnicode(hWnd, wszText);
977 /* drop temporary style if line end */
978 /* FIXME question: does abc\n mean: put abc, clear temp style, put \n? (would require a change) */
979 if (len>0 && wszText[len-1] == '\n')
980 ME_ClearTempStyle(editor);
982 ME_CommitUndo(editor);
984 ME_EmptyUndoStack(editor);
985 ME_UpdateRepaint(editor);
990 LPWSTR wszText = ME_ToUnicode(hWnd, (void *)lParam);
991 TRACE("WM_SETTEXT - %s\n", (char *)(wszText)); /* debugstr_w() */
992 ME_InternalDeleteText(editor, 0, ME_GetTextLength(editor));
993 /* uses default style! */
994 ME_InsertTextFromCursor(editor, 0, wszText, -1, editor->pBuffer->pDefaultStyle);
995 ME_EndToUnicode(hWnd, wszText);
996 ME_CommitUndo(editor);
997 ME_EmptyUndoStack(editor);
998 ME_UpdateRepaint(editor);
1003 UINT nRTFFormat = RegisterClipboardFormatA("Rich Text Format");
1004 if (IsClipboardFormatAvailable(nRTFFormat))
1006 if (IsClipboardFormatAvailable(CF_UNICODETEXT))
1014 ME_GlobalDestStruct gds;
1015 UINT nRTFFormat = RegisterClipboardFormatA("Rich Text Format");
1018 if (IsClipboardFormatAvailable(nRTFFormat))
1019 cf = nRTFFormat, dwFormat = SF_RTF;
1020 else if (IsClipboardFormatAvailable(CF_UNICODETEXT))
1021 cf = CF_UNICODETEXT, dwFormat = SF_TEXT|SF_UNICODE;
1025 if (!OpenClipboard(hWnd))
1027 gds.hData = GetClipboardData(cf);
1029 es.dwCookie = (DWORD)&gds;
1030 es.pfnCallback = dwFormat == SF_RTF ? ME_ReadFromHGLOBALRTF : ME_ReadFromHGLOBALUnicode;
1031 SendMessageW(hWnd, EM_STREAMIN, dwFormat|SFF_SELECTION, (LPARAM)&es);
1043 ME_GlobalDestStruct gds;
1045 if (!OpenClipboard(hWnd))
1049 ME_GetSelection(editor, &from, &to);
1050 pars = ME_CountParagraphsBetween(editor, from, to);
1051 hData = GlobalAlloc(GMEM_MOVEABLE, sizeof(WCHAR)*(to-from+pars+1));
1052 data = (WCHAR *)GlobalLock(hData);
1053 ME_GetTextW(editor, data, from, to-from, TRUE);
1054 GlobalUnlock(hData);
1056 gds.hData = GlobalAlloc(GMEM_MOVEABLE, 0);
1058 es.dwCookie = (DWORD)&gds;
1059 es.pfnCallback = ME_AppendToHGLOBAL;
1060 SendMessageW(hWnd, EM_STREAMOUT, SFF_SELECTION|SF_RTF, (LPARAM)&es);
1061 GlobalReAlloc(gds.hData, gds.nLength+1, 0);
1063 SetClipboardData(CF_UNICODETEXT, hData);
1064 SetClipboardData(RegisterClipboardFormatA("Rich Text Format"), gds.hData);
1069 ME_InternalDeleteText(editor, from, to-from);
1070 ME_CommitUndo(editor);
1071 ME_UpdateRepaint(editor);
1075 case WM_GETTEXTLENGTH:
1076 return ME_GetTextLength(editor);
1079 TEXTRANGEW tr; /* W and A differ only by rng->lpstrText */
1081 tr.chrg.cpMax = wParam-1;
1082 tr.lpstrText = (WCHAR *)lParam;
1083 return RichEditANSIWndProc(hWnd, EM_GETTEXTRANGE, 0, (LPARAM)&tr);
1088 TEXTRANGEW tr; /* W and A differ only by rng->lpstrText */
1089 ME_GetSelection(editor, &from, &to);
1090 tr.chrg.cpMin = from;
1092 tr.lpstrText = (WCHAR *)lParam;
1093 return RichEditANSIWndProc(hWnd, EM_GETTEXTRANGE, 0, (LPARAM)&tr);
1095 case EM_GETTEXTRANGE:
1097 TEXTRANGEW *rng = (TEXTRANGEW *)lParam;
1098 if (IsWindowUnicode(hWnd))
1099 return ME_GetTextW(editor, rng->lpstrText, rng->chrg.cpMin, rng->chrg.cpMax-rng->chrg.cpMin, FALSE);
1102 int nLen = rng->chrg.cpMax-rng->chrg.cpMin;
1103 WCHAR *p = ALLOC_N_OBJ(WCHAR, nLen+1);
1104 int nChars = ME_GetTextW(editor, p, rng->chrg.cpMin, nLen, FALSE);
1105 /* FIXME this is a potential security hole (buffer overrun)
1106 if you know more about wchar->mbyte conversion please explain
1108 WideCharToMultiByte(CP_ACP, 0, p, nChars+1, (char *)rng->lpstrText, nLen+1, NULL, NULL);
1112 return ME_GetTextW(editor, rng->lpstrText, rng->chrg.cpMin, rng->chrg.cpMax-rng->chrg.cpMin, FALSE);
1115 ME_CommitUndo(editor);
1116 ME_WrapMarkedParagraphs(editor);
1117 ME_MoveCaret(editor);
1120 ME_DestroyEditor(editor);
1121 SetWindowLongW(hWnd, 0, 0);
1123 case WM_LBUTTONDOWN:
1125 ME_LButtonDown(editor, (short)LOWORD(lParam), (short)HIWORD(lParam));
1129 if (GetCapture() == hWnd)
1130 ME_MouseMove(editor, (short)LOWORD(lParam), (short)HIWORD(lParam));
1133 if (GetCapture() == hWnd)
1137 hDC = BeginPaint(hWnd, &ps);
1138 ME_PaintContent(editor, hDC, FALSE, &ps.rcPaint);
1139 EndPaint(hWnd, &ps);
1142 ME_ShowCaret(editor);
1143 ME_SendOldNotify(editor, EN_SETFOCUS);
1146 ME_HideCaret(editor);
1147 ME_SendOldNotify(editor, EN_KILLFOCUS);
1151 HDC hDC = (HDC)wParam;
1153 COLORREF rgbBG = ME_GetBackColor(editor);
1154 if (GetUpdateRect(hWnd,&rc,TRUE))
1156 HBRUSH hbr = CreateSolidBrush(rgbBG);
1157 FillRect(hDC, &rc, hbr);
1163 TRACE("editor wnd command = %d\n", LOWORD(wParam));
1166 if (ME_ArrowKey(editor, LOWORD(wParam), GetKeyState(VK_CONTROL)<0)) {
1167 ME_CommitUndo(editor);
1168 ME_EnsureVisible(editor, editor->pCursors[0].pRun);
1170 ME_MoveCaret(editor);
1174 if (GetKeyState(VK_CONTROL)<0)
1176 if (LOWORD(wParam)=='W')
1180 ME_GetSelectionCharFormat(editor, &chf);
1181 ME_DumpStyleToBuf(&chf, buf);
1182 MessageBoxA(NULL, buf, "Style dump", MB_OK);
1184 if (LOWORD(wParam)=='Q')
1186 ME_CheckCharOffsets(editor);
1193 if (GetWindowLongW(editor->hWnd, GWL_STYLE) & ES_READONLY) {
1194 MessageBeep(MB_ICONERROR);
1195 return 0; /* FIXME really 0 ? */
1197 wstr = LOWORD(wParam);
1198 if (((unsigned)wstr)>=' ' || wstr=='\r' || wstr=='\t') {
1199 /* FIXME maybe it would make sense to call EM_REPLACESEL instead ? */
1200 ME_Style *style = ME_GetInsertStyle(editor, 0);
1201 ME_SaveTempStyle(editor);
1202 ME_InsertTextFromCursor(editor, 0, &wstr, 1, style);
1203 ME_ReleaseStyle(style);
1204 ME_CommitUndo(editor);
1205 ME_UpdateRepaint(editor);
1211 int nPos = editor->nScrollPosY;
1212 si.cbSize = sizeof(SCROLLINFO);
1213 si.fMask = SIF_PAGE|SIF_POS|SIF_RANGE|SIF_TRACKPOS;
1214 GetScrollInfo(hWnd, SB_VERT, &si);
1215 switch(LOWORD(wParam)) {
1217 nPos -= 24; /* FIXME follow the original */
1218 if (nPos<0) nPos = 0;
1222 int nEnd = editor->nTotalLength - editor->sizeWindow.cy;
1223 nPos += 24; /* FIXME follow the original */
1224 if (nPos>=nEnd) nPos = nEnd;
1228 nPos -= editor->sizeWindow.cy;
1229 if (nPos<0) nPos = 0;
1232 nPos += editor->sizeWindow.cy;
1233 if (nPos>=editor->nTotalLength) nPos = editor->nTotalLength-1;
1236 case SB_THUMBPOSITION:
1237 nPos = si.nTrackPos;
1240 if (nPos != editor->nScrollPosY) {
1241 ScrollWindow(hWnd, 0, editor->nScrollPosY-nPos, NULL, NULL);
1242 editor->nScrollPosY = nPos;
1243 SetScrollPos(hWnd, SB_VERT, nPos, TRUE);
1250 int gcWheelDelta = 0, nPos = editor->nScrollPosY;
1251 UINT pulScrollLines;
1253 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
1254 gcWheelDelta -= GET_WHEEL_DELTA_WPARAM(wParam);
1255 if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
1256 nPos += pulScrollLines * (gcWheelDelta / WHEEL_DELTA) * 8;
1257 if(nPos>=editor->nTotalLength)
1258 nPos = editor->nTotalLength - 1;
1261 if (nPos != editor->nScrollPosY) {
1262 ScrollWindow(hWnd, 0, editor->nScrollPosY-nPos, NULL, NULL);
1263 editor->nScrollPosY = nPos;
1264 SetScrollPos(hWnd, SB_VERT, nPos, TRUE);
1271 ME_MarkAllForWrapping(editor);
1272 ME_WrapMarkedParagraphs(editor);
1273 ME_UpdateScrollBar(editor);
1275 return DefWindowProcW(hWnd, msg, wParam, lParam);
1277 case EM_GETOLEINTERFACE:
1279 LPVOID *ppvObj = (LPVOID*) lParam;
1280 FIXME("EM_GETOLEINTERFACE %p: stub\n", ppvObj);
1281 return CreateIRichEditOle(ppvObj);
1285 return DefWindowProcW(hWnd, msg, wParam, lParam);
1290 /******************************************************************
1291 * RichEdit10ANSIWndProc (RICHED20.9)
1293 LRESULT WINAPI RichEdit10ANSIWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
1295 /* FIXME: this is NOT the same as 2.0 version */
1296 return RichEditANSIWndProc(hWnd, msg, wParam, lParam);
1299 void ME_SendOldNotify(ME_TextEditor *editor, int nCode)
1301 HWND hWnd = editor->hWnd;
1302 SendMessageA(GetParent(hWnd), WM_COMMAND, (nCode<<16)|GetWindowLongW(hWnd, GWLP_ID), (LPARAM)hWnd);
1305 int ME_CountParagraphsBetween(ME_TextEditor *editor, int from, int to)
1307 ME_DisplayItem *item = ME_FindItemFwd(editor->pBuffer->pFirst, diParagraph);
1310 while(item && item->member.para.next_para->member.para.nCharOfs <= from)
1311 item = item->member.para.next_para;
1314 while(item && item->member.para.next_para->member.para.nCharOfs <= to) {
1315 item = item->member.para.next_para;
1322 int ME_GetTextW(ME_TextEditor *editor, WCHAR *buffer, int nStart, int nChars, int bCRLF)
1324 ME_DisplayItem *item = ME_FindItemAtOffset(editor, diRun, nStart, &nStart);
1335 int nLen = ME_StrLen(item->member.run.strText) - nStart;
1338 CopyMemory(buffer, item->member.run.strText->szData + nStart, sizeof(WCHAR)*nLen);
1345 item = ME_FindItemFwd(item, diRun);
1348 while(nChars && item)
1350 int nLen = ME_StrLen(item->member.run.strText);
1354 if (item->member.run.nFlags & MERF_ENDPARA)
1364 CopyMemory(buffer, item->member.run.strText->szData, sizeof(WCHAR)*nLen);
1374 item = ME_FindItemFwd(item, diRun);
1380 static WCHAR wszClassName[] = {'R', 'i', 'c', 'h', 'E', 'd', 'i', 't', '2', '0', 'W', 0};
1381 static WCHAR wszClassName50[] = {'R', 'i', 'c', 'h', 'E', 'd', 'i', 't', '5', '0', 'W', 0};
1383 void ME_RegisterEditorClass(HINSTANCE hInstance)
1389 wcW.style = CS_HREDRAW | CS_VREDRAW;
1390 wcW.lpfnWndProc = RichEditANSIWndProc;
1393 wcW.hInstance = NULL; /* hInstance would register DLL-local class */
1395 wcW.hCursor = LoadCursorW(NULL, MAKEINTRESOURCEW(IDC_IBEAM));
1396 wcW.hbrBackground = (HBRUSH)GetStockObject(NULL_BRUSH);
1397 wcW.lpszMenuName = NULL;
1398 wcW.lpszClassName = wszClassName;
1399 bResult = RegisterClassW(&wcW);
1401 wcW.lpszClassName = wszClassName50;
1402 bResult = RegisterClassW(&wcW);
1405 wcA.style = CS_HREDRAW | CS_VREDRAW;
1406 wcA.lpfnWndProc = RichEditANSIWndProc;
1409 wcA.hInstance = NULL; /* hInstance would register DLL-local class */
1411 wcA.hCursor = LoadCursorW(NULL, MAKEINTRESOURCEW(IDC_IBEAM));
1412 wcA.hbrBackground = (HBRUSH)GetStockObject(NULL_BRUSH);
1413 wcA.lpszMenuName = NULL;
1414 wcA.lpszClassName = "RichEdit20A";
1415 bResult = RegisterClassA(&wcA);
1417 wcA.lpszClassName = "RichEdit50A";
1418 bResult = RegisterClassA(&wcA);
1422 BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
1427 case DLL_PROCESS_ATTACH:
1428 DisableThreadLibraryCalls(hinstDLL);
1429 me_heap = HeapCreate (0, 0x10000, 0);
1430 ME_RegisterEditorClass(hinstDLL);
1433 case DLL_PROCESS_DETACH:
1434 UnregisterClassW(wszClassName, 0);
1435 UnregisterClassW(wszClassName50, 0);
1436 UnregisterClassA("RichEdit20A", 0);
1437 UnregisterClassA("RichEdit50A", 0);
1438 HeapDestroy (me_heap);
1445 /******************************************************************
1446 * CreateTextServices (RICHED20.4)
1448 * FIXME should be ITextHost instead of void*
1450 HRESULT WINAPI CreateTextServices(IUnknown *punkOuter, void *pITextHost,
1454 /* FIXME should support aggregation */
1456 return CLASS_E_NOAGGREGATION;
1458 return E_FAIL; /* E_NOTIMPL isn't allowed by MSDN */
1461 /******************************************************************
1462 * REExtendedRegisterClass (RICHED20.8)
1464 * FIXME undocumented
1466 void WINAPI REExtendedRegisterClass(void)