- EM_CANPASTE implemented
[wine/multimedia.git] / dlls / riched20 / editor.c
blob7b7f8653e43a54cc0b165dc7f66b0890e600d457
1 /*
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
21 /*
22 API implementation status:
24 Messages (ANSI versions not done yet)
25 - EM_AUTOURLDETECT 2.0
26 + EM_CANPASTE
27 + EM_CANREDO 2.0
28 + EM_CANUNDO
29 - EM_CHARFROMPOS
30 - EM_DISPLAYBAND
31 + EM_EMPTYUNDOBUFFER
32 + EM_EXGETSEL
33 - EM_EXLIMITTEXT
34 - EM_EXLINEFROMCHAR
35 + EM_EXSETSEL
36 - EM_FINDTEXT
37 - EM_FINDTEXTEX
38 - EM_FINDWORDBREAK
39 - EM_FMTLINES
40 - EM_FORMATRANGE
41 - EM_GETCHARFORMAT (partly done)
42 + EM_GETEVENTMASK
43 - EM_GETFIRSTVISIBLELINE
44 - EM_GETIMECOLOR 1.0asian
45 - EM_GETIMECOMPMODE 2.0
46 - EM_GETIMEOPTIONS 1.0asian
47 - EM_GETIMESTATUS
48 - EM_GETLANGOPTIONS 2.0
49 - EM_GETLIMITTEXT
50 - EM_GETLINE
51 - EM_GETLINECOUNT returns number of rows, not of paragraphs
52 + EM_GETMODIFY
53 - EM_GETOLEINTERFACE
54 - EM_GETOPTIONS
55 + EM_GETPARAFORMAT
56 - EM_GETPUNCTUATION 1.0asian
57 - EM_GETRECT
58 - EM_GETREDONAME 2.0
59 + EM_GETSEL
60 + EM_GETSELTEXT (ANSI&Unicode)
61 ! - EM_GETTHUMB
62 - EM_GETTEXTMODE 2.0
63 ? + EM_GETTEXTRANGE (ANSI&Unicode)
64 - EM_GETUNDONAME
65 - EM_GETWORDBREAKPROC
66 - EM_GETWORDBREAKPROCEX
67 - EM_GETWORDWRAPMODE 1.0asian
68 - EM_HIDESELECTION
69 - EM_LIMITTEXT
70 - EM_LINEFROMCHAR
71 - EM_LINEINDEX
72 - EM_LINELENGTH
73 - EM_LINESCROLL
74 - EM_PASTESPECIAL
75 - EM_POSFROMCHARS
76 + EM_REDO 2.0
77 - EM_REQUESTRESIZE
78 + EM_REPLACESEL (proper style?) ANSI&Unicode
79 - EM_SCROLL
80 - EM_SCROLLCARET
81 - EM_SELECTIONTYPE
82 + EM_SETBKGNDCOLOR
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
88 - EM_SETLIMITTEXT
89 + EM_SETMODIFY (not sure if implementation is correct)
90 - EM_SETOLECALLBACK
91 - EM_SETOPTIONS
92 + EM_SETPARAFORMAT
93 - EM_SETPUNCTUATION 1.0asian
94 + EM_SETREADONLY no beep on modification attempt
95 - EM_SETRECT
96 - EM_SETRECTNP (EM_SETRECT without repainting) - not supported in RICHEDIT
97 + EM_SETSEL
98 - EM_SETTARGETDEVICE
99 - EM_SETTEXTMODE 2.0
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)
106 + EM_STREAMOUT
107 + EM_UNDO
108 + WM_CHAR
109 + WM_CLEAR
110 + WM_COPY
111 + WM_CUT
112 + WM_GETDLGCODE (the current implementation is incomplete)
113 + WM_GETTEXT (ANSI&Unicode)
114 + WM_GETTEXTLENGTH (ANSI version sucks)
115 + WM_PASTE
116 - WM_SETFONT
117 + WM_SETTEXT (resets undo stack !) (proper style?) ANSI&Unicode
118 - WM_STYLECHANGING
119 - WM_STYLECHANGED (things like read-only flag)
120 - WM_UNICHAR
122 Notifications
124 * EN_CHANGE (sent from the wrong place)
125 - EN_CORRECTTEXT
126 - EN_DROPFILES
127 - EN_ERRSPACE
128 - EN_HSCROLL
129 - EN_IMECHANGE
130 + EN_KILLFOCUS
131 - EN_LINK
132 - EN_MAXTEXT
133 - EN_MSGFILTER
134 - EN_OLEOPFAILED
135 - EN_PROTECTED
136 - EN_REQUESTRESIZE
137 - EN_SAVECLIPBOARD
138 + EN_SELCHANGE
139 + EN_SETFOCUS
140 - EN_STOPNOUNDO
141 * EN_UPDATE (sent from the wrong place)
142 - EN_VSCROLL
144 Styles
146 - ES_AUTOHSCROLL
147 - ES_AUTOVSCROLL
148 - ES_CENTER
149 - ES_DISABLENOSCROLL (scrollbar is always visible)
150 - ES_EX_NOCALLOLEINIT
151 - ES_LEFT
152 - ES_MULTILINE (currently single line controls aren't supported)
153 - ES_NOIME
154 - ES_READONLY (I'm not sure if beeping is the proper behaviour)
155 - ES_RIGHT
156 - ES_SAVESEL
157 - ES_SELFIME
158 - ES_SUNKEN
159 - ES_VERTICAL
160 - ES_WANTRETURN (don't know how to do WM_GETDLGCODE part)
161 - WS_SETFONT
162 - WS_HSCROLL
163 - WS_VSCROLL
167 * RICHED20 TODO (incomplete):
169 * - messages/styles/notifications listed above
170 * - Undo coalescing
171 * - add remaining CHARFORMAT/PARAFORMAT fields
172 * - right/center align should strip spaces from the beginning
173 * - more advanced navigation (Ctrl-arrows)
174 * - tabs
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)
180 * - find/replace
181 * - how to implement EM_FORMATRANGE and EM_DISPLAYBAND ? (Mission Impossible)
182 * - italic caret with italic fonts
183 * - IME
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?
200 #include "editor.h"
201 #include "ole2.h"
202 #include "richole.h"
203 #include "winreg.h"
204 #define NO_SHLWAPI_STREAM
205 #include "shlwapi.h"
207 #include "rtf.h"
209 WINE_DEFAULT_DEBUG_CHANNEL(richedit);
211 int me_debug = 0;
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);
221 p1->prev = NULL;
222 p1->next = p2;
223 p2->prev = p1;
224 p2->next = NULL;
225 p1->member.para.next_para = p2;
226 p2->member.para.prev_para = p1;
227 p2->member.para.nCharOfs = 0;
229 buf->pFirst = p1;
230 buf->pLast = p2;
231 buf->pCharStyle = NULL;
233 return buf;
236 #define STREAMIN_BUFFER_SIZE 4096 /* M$ compatibility */
238 static LRESULT ME_StreamInText(ME_TextEditor *editor, DWORD dwFormat, EDITSTREAM *stream, ME_Style *style)
240 BYTE buffer[STREAMIN_BUFFER_SIZE+1];
241 WCHAR wszText[STREAMIN_BUFFER_SIZE+1];
243 TRACE("%08lx %p\n", dwFormat, stream);
244 stream->dwError = 0;
246 do {
247 long nDataSize = 0, nWideChars = 0;
248 stream->dwError = stream->pfnCallback(stream->dwCookie,
249 (dwFormat & SF_UNICODE ? (BYTE *)wszText : buffer),
250 STREAMIN_BUFFER_SIZE, &nDataSize);
252 if (stream->dwError)
253 break;
254 if (!nDataSize)
255 break;
257 if (!(dwFormat & SF_UNICODE))
259 /* FIXME? this is doomed to fail on true MBCS like UTF-8, luckily they're unlikely to be used as CP_ACP */
260 nWideChars = MultiByteToWideChar(CP_ACP, 0, buffer, nDataSize, wszText, STREAMIN_BUFFER_SIZE);
262 else
263 nWideChars = nDataSize>>1;
264 ME_InsertTextFromCursor(editor, 0, wszText, nWideChars, style);
265 if (nDataSize<STREAMIN_BUFFER_SIZE)
266 break;
267 } while(1);
268 ME_CommitUndo(editor);
269 ME_Repaint(editor);
270 return 0;
273 void ME_RTFCharAttrHook(RTF_Info *info)
275 CHARFORMAT2W fmt;
276 fmt.cbSize = sizeof(fmt);
277 fmt.dwMask = 0;
279 switch(info->rtfMinor)
281 case rtfPlain:
282 /* FIXME add more flags once they're implemented */
283 fmt.dwMask = CFM_BOLD | CFM_ITALIC | CFM_UNDERLINE | CFM_STRIKEOUT | CFM_COLOR | CFM_BACKCOLOR | CFM_SIZE | CFM_WEIGHT;
284 fmt.dwEffects = CFE_AUTOCOLOR | CFE_AUTOBACKCOLOR;
285 fmt.yHeight = 12*20; /* 12pt */
286 fmt.wWeight = 400;
287 break;
288 case rtfBold:
289 fmt.dwMask = CFM_BOLD;
290 fmt.dwEffects = info->rtfParam ? fmt.dwMask : 0;
291 break;
292 case rtfItalic:
293 fmt.dwMask = CFM_ITALIC;
294 fmt.dwEffects = info->rtfParam ? fmt.dwMask : 0;
295 break;
296 case rtfUnderline:
297 fmt.dwMask = CFM_UNDERLINE;
298 fmt.dwEffects = info->rtfParam ? fmt.dwMask : 0;
299 break;
300 case rtfStrikeThru:
301 fmt.dwMask = CFM_STRIKEOUT;
302 fmt.dwEffects = info->rtfParam ? fmt.dwMask : 0;
303 break;
304 case rtfBackColor:
305 fmt.dwMask = CFM_BACKCOLOR;
306 fmt.dwEffects = 0;
307 if (info->rtfParam == 0)
308 fmt.dwEffects = CFE_AUTOBACKCOLOR;
309 else if (info->rtfParam != rtfNoParam)
311 RTFColor *c = RTFGetColor(info, info->rtfParam);
312 fmt.crTextColor = (c->rtfCBlue<<16)|(c->rtfCGreen<<8)|(c->rtfCRed);
314 break;
315 case rtfForeColor:
316 fmt.dwMask = CFM_COLOR;
317 fmt.dwEffects = 0;
318 if (info->rtfParam == 0)
319 fmt.dwEffects = CFE_AUTOCOLOR;
320 else if (info->rtfParam != rtfNoParam)
322 RTFColor *c = RTFGetColor(info, info->rtfParam);
323 fmt.crTextColor = (c->rtfCBlue<<16)|(c->rtfCGreen<<8)|(c->rtfCRed);
325 break;
326 case rtfFontNum:
327 if (info->rtfParam != rtfNoParam)
329 RTFFont *f = RTFGetFont(info, info->rtfParam);
330 if (f)
332 MultiByteToWideChar(CP_ACP, 0, f->rtfFName, -1, fmt.szFaceName, sizeof(fmt.szFaceName)/sizeof(WCHAR));
333 fmt.szFaceName[sizeof(fmt.szFaceName)/sizeof(WCHAR)-1] = '\0';
334 fmt.bCharSet = f->rtfFCharSet;
335 fmt.dwMask = CFM_FACE | CFM_CHARSET;
338 break;
339 case rtfFontSize:
340 fmt.dwMask = CFM_SIZE;
341 if (info->rtfParam != rtfNoParam)
342 fmt.yHeight = info->rtfParam*10;
343 break;
345 if (fmt.dwMask) {
346 ME_Style *style2;
347 RTFFlushOutputBuffer(info);
348 /* FIXME too slow ? how come ? */
349 style2 = ME_ApplyStyle(info->style, &fmt);
350 ME_ReleaseStyle(info->style);
351 info->style = style2;
355 void ME_RTFParAttrHook(RTF_Info *info)
357 PARAFORMAT2 fmt;
358 fmt.cbSize = sizeof(fmt);
359 fmt.dwMask = 0;
361 switch(info->rtfMinor)
363 case rtfParDef: /* I'm not 100% sure what does it do, but I guess it restores default paragraph attributes */
364 fmt.dwMask = PFM_ALIGNMENT;
365 fmt.wAlignment = PFA_LEFT;
366 break;
367 case rtfQuadLeft:
368 case rtfQuadJust:
369 fmt.dwMask = PFM_ALIGNMENT;
370 fmt.wAlignment = PFA_LEFT;
371 break;
372 case rtfQuadRight:
373 fmt.dwMask = PFM_ALIGNMENT;
374 fmt.wAlignment = PFA_RIGHT;
375 break;
376 case rtfQuadCenter:
377 fmt.dwMask = PFM_ALIGNMENT;
378 fmt.wAlignment = PFA_CENTER;
379 break;
381 if (fmt.dwMask) {
382 RTFFlushOutputBuffer(info);
383 /* FIXME too slow ? how come ?*/
384 ME_SetSelectionParaFormat(info->editor, &fmt);
388 void ME_RTFReadHook(RTF_Info *info) {
389 switch(info->rtfClass)
391 case rtfGroup:
392 switch(info->rtfMajor)
394 case rtfBeginGroup:
395 if (info->stackTop < maxStack) {
396 memcpy(&info->stack[info->stackTop].fmt, &info->style->fmt, sizeof(CHARFORMAT2W));
397 info->stack[info->stackTop].codePage = info->codePage;
398 info->stack[info->stackTop].unicodeLength = info->unicodeLength;
400 info->stackTop++;
401 break;
402 case rtfEndGroup:
404 ME_Style *s;
405 RTFFlushOutputBuffer(info);
406 info->stackTop--;
407 /* FIXME too slow ? how come ? */
408 s = ME_ApplyStyle(info->style, &info->stack[info->stackTop].fmt);
409 ME_ReleaseStyle(info->style);
410 info->style = s;
411 info->codePage = info->stack[info->stackTop].codePage;
412 info->unicodeLength = info->stack[info->stackTop].unicodeLength;
413 break;
416 break;
417 case rtfControl:
418 switch(info->rtfMajor)
420 case rtfCharAttr:
421 ME_RTFCharAttrHook(info);
422 break;
423 case rtfParAttr:
424 ME_RTFParAttrHook(info);
425 break;
427 break;
431 static LRESULT ME_StreamIn(ME_TextEditor *editor, DWORD format, EDITSTREAM *stream)
433 RTF_Info parser;
434 ME_Style *style;
435 int from, to, to2, nUndoMode;
436 ME_UndoItem *pUI;
437 int nEventMask = editor->nEventMask;
439 TRACE("%p %p\n", stream, editor->hWnd);
440 editor->nEventMask = 0;
442 ME_GetSelection(editor, &from, &to);
443 if (format & SFF_SELECTION) {
444 style = ME_GetSelectionInsertStyle(editor);
446 ME_InternalDeleteText(editor, from, to-from);
448 else {
449 style = editor->pBuffer->pDefaultStyle;
450 ME_AddRefStyle(style);
451 SendMessageA(editor->hWnd, EM_SETSEL, 0, 0);
452 ME_InternalDeleteText(editor, 0, ME_GetTextLength(editor));
453 from = to = 0;
454 ME_ClearTempStyle(editor);
455 /* FIXME restore default paragraph formatting ! */
458 nUndoMode = editor->nUndoMode;
459 editor->nUndoMode = umIgnore;
460 if (format & SF_RTF) {
461 /* setup the RTF parser */
462 memset(&parser, 0, sizeof parser);
463 RTFSetEditStream(&parser, stream);
464 parser.rtfFormat = format&(SF_TEXT|SF_RTF);
465 parser.hwndEdit = editor->hWnd;
466 parser.editor = editor;
467 parser.style = style;
468 WriterInit(&parser);
469 RTFInit(&parser);
470 RTFSetReadHook(&parser, ME_RTFReadHook);
471 BeginFile(&parser);
473 /* do the parsing */
474 RTFRead(&parser);
475 RTFFlushOutputBuffer(&parser);
476 RTFDestroy(&parser);
478 style = parser.style;
480 else if (format & SF_TEXT)
481 ME_StreamInText(editor, format, stream, style);
482 else
483 ERR("EM_STREAMIN without SF_TEXT or SF_RTF\n");
484 ME_GetSelection(editor, &to, &to2);
485 /* put the cursor at the top */
486 if (!(format & SFF_SELECTION))
487 SendMessageA(editor->hWnd, EM_SETSEL, 0, 0);
488 else
490 /* FIXME where to put cursor now ? */
493 editor->nUndoMode = nUndoMode;
494 pUI = ME_AddUndoItem(editor, diUndoDeleteRun, NULL);
495 TRACE("from %d to %d\n", from, to);
496 if (pUI && from < to)
498 pUI->nStart = from;
499 pUI->nLen = to-from;
501 ME_CommitUndo(editor);
502 ME_ReleaseStyle(style);
503 editor->nEventMask = nEventMask;
504 InvalidateRect(editor->hWnd, NULL, TRUE);
505 ME_UpdateRepaint(editor);
506 if (!(format & SFF_SELECTION)) {
507 ME_ClearTempStyle(editor);
509 ME_MoveCaret(editor);
510 ME_SendSelChange(editor);
512 return 0;
516 ME_DisplayItem *
517 ME_FindItemAtOffset(ME_TextEditor *editor, ME_DIType nItemType, int nOffset, int *nItemOffset)
519 ME_DisplayItem *item = ME_FindItemFwd(editor->pBuffer->pFirst, diParagraph);
521 while (item && item->member.para.next_para->member.para.nCharOfs <= nOffset)
522 item = ME_FindItemFwd(item, diParagraph);
524 if (!item)
525 return item;
527 nOffset -= item->member.para.nCharOfs;
528 if (nItemType == diParagraph) {
529 if (nItemOffset)
530 *nItemOffset = nOffset;
531 return item;
534 do {
535 item = ME_FindItemFwd(item, diRun);
536 } while (item && (item->member.run.nCharOfs + ME_StrLen(item->member.run.strText) <= nOffset));
537 if (item) {
538 nOffset -= item->member.run.nCharOfs;
539 if (nItemOffset)
540 *nItemOffset = nOffset;
542 return item;
546 ME_TextEditor *ME_MakeEditor(HWND hWnd) {
547 ME_TextEditor *ed = ALLOC_OBJ(ME_TextEditor);
548 HDC hDC;
549 int i;
550 ed->hWnd = hWnd;
551 ed->pBuffer = ME_MakeText();
552 hDC = GetDC(hWnd);
553 ME_MakeFirstParagraph(hDC, ed->pBuffer);
554 ReleaseDC(hWnd, hDC);
555 ed->bCaretShown = FALSE;
556 ed->nCursors = 3;
557 ed->pCursors = ALLOC_N_OBJ(ME_Cursor, ed->nCursors);
558 ed->pCursors[0].pRun = ME_FindItemFwd(ed->pBuffer->pFirst, diRun);
559 ed->pCursors[0].nOffset = 0;
560 ed->pCursors[1].pRun = ME_FindItemFwd(ed->pBuffer->pFirst, diRun);
561 ed->pCursors[1].nOffset = 0;
562 ed->nLastTotalLength = ed->nTotalLength = 0;
563 ed->nUDArrowX = -1;
564 ed->nSequence = 0;
565 ed->rgbBackColor = -1;
566 ed->bCaretAtEnd = FALSE;
567 ed->nEventMask = 0;
568 ed->nModifyStep = 0;
569 ed->pUndoStack = ed->pRedoStack = NULL;
570 ed->nUndoMode = umAddToUndo;
571 ed->nParagraphs = 1;
572 ed->nLastSelStart = ed->nLastSelEnd = 0;
573 ed->nScrollPosY = 0;
574 for (i=0; i<HFONT_CACHE_SIZE; i++)
576 ed->pFontCache[i].nRefs = 0;
577 ed->pFontCache[i].nAge = 0;
578 ed->pFontCache[i].hFont = NULL;
580 ME_CheckCharOffsets(ed);
581 return ed;
584 typedef struct tagME_GlobalDestStruct
586 HGLOBAL hData;
587 int nLength;
588 } ME_GlobalDestStruct;
590 static DWORD CALLBACK ME_AppendToHGLOBAL(DWORD_PTR dwCookie, LPBYTE lpBuff, LONG cb, LONG *pcb)
592 ME_GlobalDestStruct *pData = (ME_GlobalDestStruct *)dwCookie;
593 int nMaxSize;
594 BYTE *pDest;
596 nMaxSize = GlobalSize(pData->hData);
597 if (pData->nLength+cb+1 >= cb)
599 /* round up to 2^17 */
600 int nNewSize = (((nMaxSize+cb+1)|0x1FFFF)+1) & 0xFFFE0000;
601 pData->hData = GlobalReAlloc(pData->hData, nNewSize, 0);
603 pDest = (BYTE *)GlobalLock(pData->hData);
604 memcpy(pDest + pData->nLength, lpBuff, cb);
605 pData->nLength += cb;
606 pDest[pData->nLength] = '\0';
607 GlobalUnlock(pData->hData);
608 *pcb = cb;
610 return 0;
613 static DWORD CALLBACK ME_ReadFromHGLOBALUnicode(DWORD_PTR dwCookie, LPBYTE lpBuff, LONG cb, LONG *pcb)
615 ME_GlobalDestStruct *pData = (ME_GlobalDestStruct *)dwCookie;
616 int i;
617 WORD *pSrc, *pDest;
619 cb = cb >> 1;
620 pDest = (WORD *)lpBuff;
621 pSrc = (WORD *)GlobalLock(pData->hData);
622 for (i = 0; i<cb && pSrc[pData->nLength+i]; i++) {
623 pDest[i] = pSrc[pData->nLength+i];
625 pData->nLength += i;
626 *pcb = 2*i;
627 GlobalUnlock(pData->hData);
628 return 0;
631 static DWORD CALLBACK ME_ReadFromHGLOBALRTF(DWORD_PTR dwCookie, LPBYTE lpBuff, LONG cb, LONG *pcb)
633 ME_GlobalDestStruct *pData = (ME_GlobalDestStruct *)dwCookie;
634 int i;
635 BYTE *pSrc, *pDest;
637 pDest = lpBuff;
638 pSrc = (BYTE *)GlobalLock(pData->hData);
639 for (i = 0; i<cb && pSrc[pData->nLength+i]; i++) {
640 pDest[i] = pSrc[pData->nLength+i];
642 pData->nLength += i;
643 *pcb = i;
644 GlobalUnlock(pData->hData);
645 return 0;
648 void ME_DestroyEditor(ME_TextEditor *editor)
650 ME_DisplayItem *pFirst = editor->pBuffer->pFirst;
651 ME_DisplayItem *p = pFirst, *pNext = NULL;
652 int i;
654 ME_ClearTempStyle(editor);
655 ME_EmptyUndoStack(editor);
656 while(p) {
657 pNext = p->next;
658 ME_DestroyDisplayItem(p);
659 p = pNext;
661 ME_ReleaseStyle(editor->pBuffer->pDefaultStyle);
662 for (i=0; i<HFONT_CACHE_SIZE; i++)
664 if (editor->pFontCache[i].hFont)
665 DeleteObject(editor->pFontCache[i].hFont);
668 FREE_OBJ(editor);
671 #define UNSUPPORTED_MSG(e) \
672 case e: \
673 FIXME(#e ": stub\n"); \
674 return DefWindowProcW(hWnd, msg, wParam, lParam);
676 /******************************************************************
677 * RichEditANSIWndProc (RICHED20.10)
679 LRESULT WINAPI RichEditANSIWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) {
680 HDC hDC;
681 PAINTSTRUCT ps;
682 SCROLLINFO si;
683 ME_TextEditor *editor = (ME_TextEditor *)GetWindowLongW(hWnd, 0);
684 TRACE("msg %d %08x %08lx\n", msg, wParam, lParam);
685 switch(msg) {
687 UNSUPPORTED_MSG(EM_AUTOURLDETECT)
688 UNSUPPORTED_MSG(EM_CHARFROMPOS)
689 UNSUPPORTED_MSG(EM_DISPLAYBAND)
690 UNSUPPORTED_MSG(EM_EXLIMITTEXT)
691 UNSUPPORTED_MSG(EM_EXLINEFROMCHAR)
692 UNSUPPORTED_MSG(EM_FINDTEXT)
693 UNSUPPORTED_MSG(EM_FINDTEXTEX)
694 UNSUPPORTED_MSG(EM_FINDWORDBREAK)
695 UNSUPPORTED_MSG(EM_FMTLINES)
696 UNSUPPORTED_MSG(EM_FORMATRANGE)
697 UNSUPPORTED_MSG(EM_GETFIRSTVISIBLELINE)
698 UNSUPPORTED_MSG(EM_GETIMECOMPMODE)
699 /* UNSUPPORTED_MSG(EM_GETIMESTATUS) missing in Wine headers */
700 UNSUPPORTED_MSG(EM_GETLANGOPTIONS)
701 UNSUPPORTED_MSG(EM_GETLIMITTEXT)
702 UNSUPPORTED_MSG(EM_GETLINE)
703 UNSUPPORTED_MSG(EM_GETLINECOUNT)
704 /* UNSUPPORTED_MSG(EM_GETOLEINTERFACE) separate stub */
705 UNSUPPORTED_MSG(EM_GETOPTIONS)
706 UNSUPPORTED_MSG(EM_GETRECT)
707 UNSUPPORTED_MSG(EM_GETREDONAME)
708 UNSUPPORTED_MSG(EM_GETTEXTMODE)
709 UNSUPPORTED_MSG(EM_GETUNDONAME)
710 UNSUPPORTED_MSG(EM_GETWORDBREAKPROC)
711 UNSUPPORTED_MSG(EM_GETWORDBREAKPROCEX)
712 UNSUPPORTED_MSG(EM_HIDESELECTION)
713 UNSUPPORTED_MSG(EM_LIMITTEXT) /* also known as EM_SETLIMITTEXT */
714 UNSUPPORTED_MSG(EM_LINEFROMCHAR)
715 UNSUPPORTED_MSG(EM_LINEINDEX)
716 UNSUPPORTED_MSG(EM_LINELENGTH)
717 UNSUPPORTED_MSG(EM_LINESCROLL)
718 UNSUPPORTED_MSG(EM_PASTESPECIAL)
719 /* UNSUPPORTED_MSG(EM_POSFROMCHARS) missing in Wine headers */
720 UNSUPPORTED_MSG(EM_REQUESTRESIZE)
721 UNSUPPORTED_MSG(EM_SCROLL)
722 UNSUPPORTED_MSG(EM_SCROLLCARET)
723 UNSUPPORTED_MSG(EM_SELECTIONTYPE)
724 UNSUPPORTED_MSG(EM_SETLANGOPTIONS)
725 UNSUPPORTED_MSG(EM_SETOLECALLBACK)
726 UNSUPPORTED_MSG(EM_SETOPTIONS)
727 UNSUPPORTED_MSG(EM_SETRECT)
728 UNSUPPORTED_MSG(EM_SETRECTNP)
729 UNSUPPORTED_MSG(EM_SETTARGETDEVICE)
730 UNSUPPORTED_MSG(EM_SETTEXTMODE)
731 UNSUPPORTED_MSG(EM_SETUNDOLIMIT)
732 UNSUPPORTED_MSG(EM_SETWORDBREAKPROC)
733 UNSUPPORTED_MSG(EM_SETWORDBREAKPROCEX)
734 UNSUPPORTED_MSG(WM_SETFONT)
735 UNSUPPORTED_MSG(WM_STYLECHANGING)
736 UNSUPPORTED_MSG(WM_STYLECHANGED)
737 /* UNSUPPORTED_MSG(WM_UNICHAR) FIXME missing in Wine headers */
739 /* Messages specific to Richedit controls */
741 case EM_STREAMIN:
742 return ME_StreamIn(editor, wParam, (EDITSTREAM*)lParam);
743 case EM_STREAMOUT:
744 return ME_StreamOut(editor, wParam, (EDITSTREAM *)lParam);
745 case WM_GETDLGCODE:
747 UINT code = DLGC_WANTCHARS|DLGC_WANTARROWS;
748 if (GetWindowLongW(hWnd, GWL_STYLE)&ES_WANTRETURN)
749 code |= 0; /* FIXME what can we do here ? ask for messages and censor them ? */
750 return code;
752 case WM_NCCREATE:
754 CREATESTRUCTW *pcs = (CREATESTRUCTW *)lParam;
755 editor = ME_MakeEditor(hWnd);
756 SetWindowLongW(hWnd, 0, (long)editor);
757 pcs = 0; /* ignore */
758 return TRUE;
760 case EM_EMPTYUNDOBUFFER:
761 ME_EmptyUndoStack(editor);
762 return 0;
763 case EM_GETSEL:
765 ME_GetSelection(editor, (int *)wParam, (int *)lParam);
766 if (!((wParam|lParam) & 0xFFFF0000))
767 return (lParam<<16)|wParam;
768 return -1;
770 case EM_EXGETSEL:
772 CHARRANGE *pRange = (CHARRANGE *)lParam;
773 ME_GetSelection(editor, (int *)&pRange->cpMin, (int *)&pRange->cpMax);
774 return 0;
776 case EM_CANUNDO:
777 return editor->pUndoStack != NULL;
778 case EM_CANREDO:
779 return editor->pRedoStack != NULL;
780 case EM_UNDO:
781 ME_Undo(editor);
782 return 0;
783 case EM_REDO:
784 ME_Redo(editor);
785 return 0;
786 case EM_SETSEL:
788 ME_SetSelection(editor, wParam, lParam);
789 ME_Repaint(editor);
790 ME_SendSelChange(editor);
791 return 0;
793 case EM_EXSETSEL:
795 CHARRANGE *pRange = (CHARRANGE *)lParam;
796 ME_SetSelection(editor, pRange->cpMin, pRange->cpMax);
797 /* FIXME optimize */
798 ME_Repaint(editor);
799 ME_SendSelChange(editor);
800 return 0;
802 case EM_SETBKGNDCOLOR:
804 LRESULT lColor = ME_GetBackColor(editor);
805 if (wParam)
806 editor->rgbBackColor = -1;
807 else
808 editor->rgbBackColor = lParam;
809 InvalidateRect(hWnd, NULL, TRUE);
810 UpdateWindow(hWnd);
811 return lColor;
813 case EM_GETMODIFY:
814 return editor->nModifyStep == 0 ? 0 : 1;
815 case EM_SETMODIFY:
817 if (wParam)
818 editor->nModifyStep = 0x80000000;
819 else
820 editor->nModifyStep = 0;
822 return 0;
824 case EM_SETREADONLY:
826 long nStyle = GetWindowLongW(hWnd, GWL_STYLE);
827 if (wParam)
828 nStyle |= ES_READONLY;
829 else
830 nStyle &= ~ES_READONLY;
831 SetWindowLongW(hWnd, GWL_STYLE, nStyle);
832 ME_Repaint(editor);
833 return 0;
835 case EM_SETEVENTMASK:
836 editor->nEventMask = lParam;
837 return 0;
838 case EM_GETEVENTMASK:
839 return editor->nEventMask;
840 case EM_SETCHARFORMAT:
842 CHARFORMAT2W buf, *p;
843 BOOL bRepaint = TRUE;
844 p = ME_ToCF2W(&buf, (CHARFORMAT2W *)lParam);
845 if (!wParam)
846 ME_SetDefaultCharFormat(editor, p);
847 else if (wParam == (SCF_WORD | SCF_SELECTION))
848 FIXME("word selection not supported\n");
849 else if (wParam == SCF_ALL)
850 ME_SetCharFormat(editor, 0, ME_GetTextLength(editor), p);
851 else {
852 int from, to;
853 ME_GetSelection(editor, &from, &to);
854 bRepaint = (from != to);
855 ME_SetSelectionCharFormat(editor, p);
857 ME_CommitUndo(editor);
858 if (bRepaint)
859 ME_UpdateRepaint(editor);
860 return 0;
862 case EM_GETCHARFORMAT:
864 CHARFORMAT2W tmp;
865 tmp.cbSize = sizeof(tmp);
866 if (!wParam)
867 ME_GetDefaultCharFormat(editor, &tmp);
868 else
869 ME_GetSelectionCharFormat(editor, &tmp);
870 ME_CopyToCFAny((CHARFORMAT2W *)lParam, &tmp);
871 return 0;
873 case EM_SETPARAFORMAT:
874 ME_SetSelectionParaFormat(editor, (PARAFORMAT2 *)lParam);
875 ME_UpdateRepaint(editor);
876 ME_CommitUndo(editor);
877 return 0;
878 case EM_GETPARAFORMAT:
879 ME_GetSelectionParaFormat(editor, (PARAFORMAT2 *)lParam);
880 return 0;
881 case WM_CLEAR:
883 int from, to;
884 ME_GetSelection(editor, &from, &to);
885 ME_InternalDeleteText(editor, from, to-from);
886 ME_CommitUndo(editor);
887 ME_UpdateRepaint(editor);
888 return 0;
890 case EM_REPLACESEL:
892 int from, to;
893 ME_Style *style;
894 LPWSTR wszText = ME_ToUnicode(hWnd, (void *)lParam);
895 size_t len = lstrlenW(wszText);
896 TRACE("EM_REPLACESEL - %s\n", debugstr_w(wszText));
898 ME_GetSelection(editor, &from, &to);
899 style = ME_GetSelectionInsertStyle(editor);
900 ME_InternalDeleteText(editor, from, to-from);
901 ME_InsertTextFromCursor(editor, 0, wszText, len, style);
902 ME_ReleaseStyle(style);
903 ME_EndToUnicode(hWnd, wszText);
904 /* drop temporary style if line end */
905 /* FIXME question: does abc\n mean: put abc, clear temp style, put \n? (would require a change) */
906 if (len>0 && wszText[len-1] == '\n')
907 ME_ClearTempStyle(editor);
909 ME_CommitUndo(editor);
910 if (!wParam)
911 ME_EmptyUndoStack(editor);
912 ME_UpdateRepaint(editor);
913 return 0;
915 case WM_SETTEXT:
917 LPWSTR wszText = ME_ToUnicode(hWnd, (void *)lParam);
918 TRACE("WM_SETTEXT - %s\n", (char *)(wszText)); /* debugstr_w() */
919 ME_InternalDeleteText(editor, 0, ME_GetTextLength(editor));
920 /* uses default style! */
921 ME_InsertTextFromCursor(editor, 0, wszText, -1, editor->pBuffer->pDefaultStyle);
922 ME_EndToUnicode(hWnd, wszText);
923 ME_CommitUndo(editor);
924 ME_EmptyUndoStack(editor);
925 ME_UpdateRepaint(editor);
926 return 0;
928 case EM_CANPASTE:
930 UINT nRTFFormat = RegisterClipboardFormatA("Rich Text Format");
931 if (IsClipboardFormatAvailable(nRTFFormat))
932 return TRUE;
933 if (IsClipboardFormatAvailable(CF_UNICODETEXT))
934 return TRUE;
935 return FALSE;
937 case WM_PASTE:
939 DWORD dwFormat = 0;
940 EDITSTREAM es;
941 ME_GlobalDestStruct gds;
942 UINT nRTFFormat = RegisterClipboardFormatA("Rich Text Format");
943 UINT cf = 0;
945 if (IsClipboardFormatAvailable(nRTFFormat))
946 cf = nRTFFormat, dwFormat = SF_RTF;
947 else if (IsClipboardFormatAvailable(CF_UNICODETEXT))
948 cf = CF_UNICODETEXT, dwFormat = SF_TEXT|SF_UNICODE;
949 else
950 return 0;
952 if (!OpenClipboard(hWnd))
953 return 0;
954 gds.hData = GetClipboardData(cf);
955 gds.nLength = 0;
956 es.dwCookie = (DWORD)&gds;
957 es.pfnCallback = dwFormat == SF_RTF ? ME_ReadFromHGLOBALRTF : ME_ReadFromHGLOBALUnicode;
958 SendMessageW(hWnd, EM_STREAMIN, dwFormat|SFF_SELECTION, (LPARAM)&es);
960 CloseClipboard();
961 return 0;
963 case WM_CUT:
964 case WM_COPY:
966 int from, to, pars;
967 WCHAR *data;
968 HANDLE hData;
969 EDITSTREAM es;
970 ME_GlobalDestStruct gds;
972 if (!OpenClipboard(hWnd))
973 return 0;
975 EmptyClipboard();
976 ME_GetSelection(editor, &from, &to);
977 pars = ME_CountParagraphsBetween(editor, from, to);
978 hData = GlobalAlloc(GMEM_MOVEABLE, sizeof(WCHAR)*(to-from+pars+1));
979 data = (WCHAR *)GlobalLock(hData);
980 ME_GetTextW(editor, data, from, to-from, TRUE);
981 GlobalUnlock(hData);
983 gds.hData = GlobalAlloc(GMEM_MOVEABLE, 0);
984 gds.nLength = 0;
985 es.dwCookie = (DWORD)&gds;
986 es.pfnCallback = ME_AppendToHGLOBAL;
987 SendMessageW(hWnd, EM_STREAMOUT, SFF_SELECTION|SF_RTF, (LPARAM)&es);
988 GlobalReAlloc(gds.hData, gds.nLength+1, 0);
990 SetClipboardData(CF_UNICODETEXT, hData);
991 SetClipboardData(RegisterClipboardFormatA("Rich Text Format"), gds.hData);
993 CloseClipboard();
994 if (msg == WM_CUT)
996 ME_InternalDeleteText(editor, from, to-from);
997 ME_CommitUndo(editor);
998 ME_UpdateRepaint(editor);
1000 return 0;
1002 case WM_GETTEXTLENGTH:
1003 return ME_GetTextLength(editor);
1004 case WM_GETTEXT:
1006 TEXTRANGEW tr; /* W and A differ only by rng->lpstrText */
1007 tr.chrg.cpMin = 0;
1008 tr.chrg.cpMax = wParam-1;
1009 tr.lpstrText = (WCHAR *)lParam;
1010 return RichEditANSIWndProc(hWnd, EM_GETTEXTRANGE, 0, (LPARAM)&tr);
1012 case EM_GETSELTEXT:
1014 int from, to;
1015 TEXTRANGEW tr; /* W and A differ only by rng->lpstrText */
1016 ME_GetSelection(editor, &from, &to);
1017 tr.chrg.cpMin = from;
1018 tr.chrg.cpMax = to;
1019 tr.lpstrText = (WCHAR *)lParam;
1020 return RichEditANSIWndProc(hWnd, EM_GETTEXTRANGE, 0, (LPARAM)&tr);
1022 case EM_GETTEXTRANGE:
1024 TEXTRANGEW *rng = (TEXTRANGEW *)lParam;
1025 if (IsWindowUnicode(hWnd))
1026 return ME_GetTextW(editor, rng->lpstrText, rng->chrg.cpMin, rng->chrg.cpMax-rng->chrg.cpMin, FALSE);
1027 else
1029 int nLen = rng->chrg.cpMax-rng->chrg.cpMin;
1030 WCHAR *p = ALLOC_N_OBJ(WCHAR, nLen+1);
1031 int nChars = ME_GetTextW(editor, p, rng->chrg.cpMin, nLen, FALSE);
1032 /* FIXME this is a potential security hole (buffer overrun)
1033 if you know more about wchar->mbyte conversion please explain
1035 WideCharToMultiByte(CP_ACP, 0, p, nChars+1, (char *)rng->lpstrText, nLen+1, NULL, NULL);
1036 FREE_OBJ(p);
1037 return nChars;
1039 return ME_GetTextW(editor, rng->lpstrText, rng->chrg.cpMin, rng->chrg.cpMax-rng->chrg.cpMin, FALSE);
1041 case WM_CREATE:
1042 ME_CommitUndo(editor);
1043 ME_WrapMarkedParagraphs(editor);
1044 ME_MoveCaret(editor);
1045 return 0;
1046 case WM_DESTROY:
1047 ME_DestroyEditor(editor);
1048 SetWindowLongW(hWnd, 0, 0);
1049 return 0;
1050 case WM_LBUTTONDOWN:
1051 SetFocus(hWnd);
1052 ME_LButtonDown(editor, (short)LOWORD(lParam), (short)HIWORD(lParam));
1053 SetCapture(hWnd);
1054 break;
1055 case WM_MOUSEMOVE:
1056 if (GetCapture() == hWnd)
1057 ME_MouseMove(editor, (short)LOWORD(lParam), (short)HIWORD(lParam));
1058 break;
1059 case WM_LBUTTONUP:
1060 if (GetCapture() == hWnd)
1061 ReleaseCapture();
1062 break;
1063 case WM_PAINT:
1064 hDC = BeginPaint(hWnd, &ps);
1065 ME_PaintContent(editor, hDC, FALSE, &ps.rcPaint);
1066 EndPaint(hWnd, &ps);
1067 break;
1068 case WM_SETFOCUS:
1069 ME_ShowCaret(editor);
1070 ME_SendOldNotify(editor, EN_SETFOCUS);
1071 return 0;
1072 case WM_KILLFOCUS:
1073 ME_HideCaret(editor);
1074 ME_SendOldNotify(editor, EN_KILLFOCUS);
1075 return 0;
1076 case WM_ERASEBKGND:
1078 HDC hDC = (HDC)wParam;
1079 RECT rc;
1080 COLORREF rgbBG = ME_GetBackColor(editor);
1081 if (GetUpdateRect(hWnd,&rc,TRUE))
1083 HBRUSH hbr = CreateSolidBrush(rgbBG);
1084 FillRect(hDC, &rc, hbr);
1085 DeleteObject(hbr);
1087 return 1;
1089 case WM_COMMAND:
1090 TRACE("editor wnd command = %d\n", LOWORD(wParam));
1091 return 0;
1092 case WM_KEYDOWN:
1093 if (ME_ArrowKey(editor, LOWORD(wParam), GetKeyState(VK_CONTROL)<0)) {
1094 ME_CommitUndo(editor);
1095 ME_EnsureVisible(editor, editor->pCursors[0].pRun);
1096 HideCaret(hWnd);
1097 ME_MoveCaret(editor);
1098 ShowCaret(hWnd);
1099 return 0;
1101 if (GetKeyState(VK_CONTROL)<0)
1103 if (LOWORD(wParam)=='W')
1105 CHARFORMAT2W chf;
1106 char buf[2048];
1107 ME_GetSelectionCharFormat(editor, &chf);
1108 ME_DumpStyleToBuf(&chf, buf);
1109 MessageBoxA(NULL, buf, "Style dump", MB_OK);
1111 if (LOWORD(wParam)=='Q')
1113 ME_CheckCharOffsets(editor);
1116 goto do_default;
1117 case WM_CHAR:
1119 WCHAR wstr;
1120 if (GetWindowLongW(editor->hWnd, GWL_STYLE) & ES_READONLY) {
1121 MessageBeep(MB_ICONERROR);
1122 return 0; /* FIXME really 0 ? */
1124 wstr = LOWORD(wParam);
1125 if (((unsigned)wstr)>=' ' || wstr=='\r') {
1126 /* FIXME maybe it would make sense to call EM_REPLACESEL instead ? */
1127 ME_Style *style = ME_GetInsertStyle(editor, 0);
1128 ME_SaveTempStyle(editor);
1129 ME_InsertTextFromCursor(editor, 0, &wstr, 1, style);
1130 ME_ReleaseStyle(style);
1131 ME_CommitUndo(editor);
1132 ME_UpdateRepaint(editor);
1134 return 0;
1136 case WM_VSCROLL:
1138 int nPos = editor->nScrollPosY;
1139 si.cbSize = sizeof(SCROLLINFO);
1140 si.fMask = SIF_PAGE|SIF_POS|SIF_RANGE|SIF_TRACKPOS;
1141 GetScrollInfo(hWnd, SB_VERT, &si);
1142 switch(LOWORD(wParam)) {
1143 case SB_LINEUP:
1144 nPos -= 24; /* FIXME follow the original */
1145 if (nPos<0) nPos = 0;
1146 break;
1147 case SB_LINEDOWN:
1149 int nEnd = editor->nTotalLength - editor->sizeWindow.cy;
1150 nPos += 24; /* FIXME follow the original */
1151 if (nPos>=nEnd) nPos = nEnd;
1152 break;
1154 case SB_PAGEUP:
1155 nPos -= editor->sizeWindow.cy;
1156 if (nPos<0) nPos = 0;
1157 break;
1158 case SB_PAGEDOWN:
1159 nPos += editor->sizeWindow.cy;
1160 if (nPos>=editor->nTotalLength) nPos = editor->nTotalLength-1;
1161 break;
1162 case SB_THUMBTRACK:
1163 case SB_THUMBPOSITION:
1164 nPos = si.nTrackPos;
1165 break;
1167 if (nPos != editor->nScrollPosY) {
1168 ScrollWindow(hWnd, 0, editor->nScrollPosY-nPos, NULL, NULL);
1169 editor->nScrollPosY = nPos;
1170 SetScrollPos(hWnd, SB_VERT, nPos, TRUE);
1171 UpdateWindow(hWnd);
1173 break;
1175 case WM_SIZE:
1177 ME_MarkAllForWrapping(editor);
1178 ME_WrapMarkedParagraphs(editor);
1179 ME_UpdateScrollBar(editor);
1180 ME_Repaint(editor);
1181 return DefWindowProcW(hWnd, msg, wParam, lParam);
1183 case EM_GETOLEINTERFACE:
1185 LPVOID *ppvObj = (LPVOID*) lParam;
1186 FIXME("EM_GETOLEINTERFACE %p: stub\n", ppvObj);
1187 return CreateIRichEditOle(ppvObj);
1189 default:
1190 do_default:
1191 return DefWindowProcW(hWnd, msg, wParam, lParam);
1193 return 0L;
1196 /******************************************************************
1197 * RichEdit10ANSIWndProc (RICHED20.9)
1199 LRESULT WINAPI RichEdit10ANSIWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
1201 /* FIXME: this is NOT the same as 2.0 version */
1202 return RichEditANSIWndProc(hWnd, msg, wParam, lParam);
1205 void ME_SendOldNotify(ME_TextEditor *editor, int nCode)
1207 HWND hWnd = editor->hWnd;
1208 SendMessageA(GetParent(hWnd), WM_COMMAND, (nCode<<16)|GetWindowLongW(hWnd, GWLP_ID), (LPARAM)hWnd);
1211 int ME_CountParagraphsBetween(ME_TextEditor *editor, int from, int to)
1213 ME_DisplayItem *item = ME_FindItemFwd(editor->pBuffer->pFirst, diParagraph);
1214 int i = 0;
1216 while(item && item->member.para.next_para->member.para.nCharOfs <= from)
1217 item = item->member.para.next_para;
1218 if (!item)
1219 return 0;
1220 while(item && item->member.para.next_para->member.para.nCharOfs <= to) {
1221 item = item->member.para.next_para;
1222 i++;
1224 return i;
1228 int ME_GetTextW(ME_TextEditor *editor, WCHAR *buffer, int nStart, int nChars, int bCRLF)
1230 ME_DisplayItem *item = ME_FindItemAtOffset(editor, diRun, nStart, &nStart);
1231 int nWritten = 0;
1233 if (!item) {
1234 *buffer = L'\0';
1235 return 0;
1237 assert(item);
1239 if (nStart)
1241 int nLen = ME_StrLen(item->member.run.strText) - nStart;
1242 if (nLen > nChars)
1243 nLen = nChars;
1244 CopyMemory(buffer, item->member.run.strText->szData + nStart, sizeof(WCHAR)*nLen);
1245 nChars -= nLen;
1246 nWritten += nLen;
1247 if (!nChars)
1248 return nWritten;
1249 buffer += nLen;
1250 nStart = 0;
1251 item = ME_FindItemFwd(item, diRun);
1254 while(nChars && item)
1256 int nLen = ME_StrLen(item->member.run.strText);
1257 if (nLen > nChars)
1258 nLen = nChars;
1260 if (item->member.run.nFlags & MERF_ENDPARA)
1262 if (bCRLF) {
1263 *buffer++ = '\r';
1264 nWritten++;
1266 *buffer = '\n';
1267 assert(nLen == 1);
1269 else
1270 CopyMemory(buffer, item->member.run.strText->szData, sizeof(WCHAR)*nLen);
1271 nChars -= nLen;
1272 nWritten += nLen;
1273 buffer += nLen;
1275 if (!nChars)
1277 *buffer = L'\0';
1278 return nWritten;
1280 item = ME_FindItemFwd(item, diRun);
1282 *buffer = L'\0';
1283 return nWritten;
1286 static WCHAR wszClassName[] = {'R', 'i', 'c', 'h', 'E', 'd', 'i', 't', '2', '0', 'W', 0};
1287 static WCHAR wszClassName50[] = {'R', 'i', 'c', 'h', 'E', 'd', 'i', 't', '5', '0', 'W', 0};
1289 void ME_RegisterEditorClass(HINSTANCE hInstance)
1291 BOOL bResult;
1292 WNDCLASSW wcW;
1293 WNDCLASSA wcA;
1295 wcW.style = CS_HREDRAW | CS_VREDRAW;
1296 wcW.lpfnWndProc = RichEditANSIWndProc;
1297 wcW.cbClsExtra = 0;
1298 wcW.cbWndExtra = 4;
1299 wcW.hInstance = NULL; /* hInstance would register DLL-local class */
1300 wcW.hIcon = NULL;
1301 wcW.hCursor = LoadCursorW(NULL, MAKEINTRESOURCEW(IDC_IBEAM));
1302 wcW.hbrBackground = (HBRUSH)GetStockObject(NULL_BRUSH);
1303 wcW.lpszMenuName = NULL;
1304 wcW.lpszClassName = wszClassName;
1305 bResult = RegisterClassW(&wcW);
1306 assert(bResult);
1307 wcW.lpszClassName = wszClassName50;
1308 bResult = RegisterClassW(&wcW);
1309 assert(bResult);
1311 wcA.style = CS_HREDRAW | CS_VREDRAW;
1312 wcA.lpfnWndProc = RichEditANSIWndProc;
1313 wcA.cbClsExtra = 0;
1314 wcA.cbWndExtra = 4;
1315 wcA.hInstance = NULL; /* hInstance would register DLL-local class */
1316 wcA.hIcon = NULL;
1317 wcA.hCursor = LoadCursorW(NULL, MAKEINTRESOURCEW(IDC_IBEAM));
1318 wcA.hbrBackground = (HBRUSH)GetStockObject(NULL_BRUSH);
1319 wcA.lpszMenuName = NULL;
1320 wcA.lpszClassName = "RichEdit20A";
1321 bResult = RegisterClassA(&wcA);
1322 assert(bResult);
1323 wcA.lpszClassName = "RichEdit50A";
1324 bResult = RegisterClassA(&wcA);
1325 assert(bResult);
1328 BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
1330 TRACE("\n");
1331 switch (fdwReason)
1333 case DLL_PROCESS_ATTACH:
1334 DisableThreadLibraryCalls(hinstDLL);
1335 me_heap = HeapCreate (0, 0x10000, 0);
1336 ME_RegisterEditorClass(hinstDLL);
1337 break;
1339 case DLL_PROCESS_DETACH:
1340 UnregisterClassW(wszClassName, 0);
1341 UnregisterClassW(wszClassName50, 0);
1342 UnregisterClassA("RichEdit20A", 0);
1343 UnregisterClassA("RichEdit50A", 0);
1344 HeapDestroy (me_heap);
1345 me_heap = NULL;
1346 break;
1348 return TRUE;
1351 /******************************************************************
1352 * CreateTextServices (RICHED20.4)
1354 * FIXME should be ITextHost instead of void*
1356 HRESULT WINAPI CreateTextServices(IUnknown *punkOuter, void *pITextHost,
1357 IUnknown **ppUnk)
1359 FIXME("stub\n");
1360 /* FIXME should support aggregation */
1361 if (punkOuter)
1362 return CLASS_E_NOAGGREGATION;
1364 return E_FAIL; /* E_NOTIMPL isn't allowed by MSDN */
1367 /******************************************************************
1368 * REExtendedRegisterClass (RICHED20.8)
1370 * FIXME undocumented
1372 void WINAPI REExtendedRegisterClass(void)
1374 FIXME("stub\n");