Initial implementation of riched20.
This commit is contained in:
parent
aa35787dc7
commit
d488f3f1de
|
@ -1634,6 +1634,7 @@ dlls/qcap/Makefile
|
|||
dlls/quartz/Makefile
|
||||
dlls/quartz/tests/Makefile
|
||||
dlls/rasapi32/Makefile
|
||||
dlls/riched20/Makefile
|
||||
dlls/richedit/Makefile
|
||||
dlls/rpcrt4/Makefile
|
||||
dlls/rpcrt4/tests/Makefile
|
||||
|
|
|
@ -108,6 +108,7 @@ BASEDIRS = \
|
|||
qcap \
|
||||
quartz \
|
||||
rasapi32 \
|
||||
riched20 \
|
||||
richedit \
|
||||
rpcrt4 \
|
||||
rsabase \
|
||||
|
@ -351,6 +352,7 @@ SYMLINKS_SO = \
|
|||
qcap.dll.so \
|
||||
quartz.dll.so \
|
||||
rasapi32.dll.so \
|
||||
riched20.dll.so \
|
||||
riched32.dll.so \
|
||||
rpcrt4.dll.so \
|
||||
rsabase.dll.so \
|
||||
|
@ -781,6 +783,9 @@ rasapi32.dll.so: rasapi32/rasapi32.dll.so
|
|||
rasapi16.dll.so : rasapi32.dll.so
|
||||
$(RM) $@ && $(LN_S) rasapi32.dll.so $@
|
||||
|
||||
riched20.dll.so: riched20/riched20.dll.so
|
||||
$(RM) $@ && $(LN_S) riched20/riched20.dll.so $@
|
||||
|
||||
riched32.dll.so: richedit/riched32.dll.so
|
||||
$(RM) $@ && $(LN_S) richedit/riched32.dll.so $@
|
||||
|
||||
|
@ -1092,6 +1097,7 @@ IMPORT_LIBS = \
|
|||
libqcap.$(IMPLIBEXT) \
|
||||
libquartz.$(IMPLIBEXT) \
|
||||
librasapi32.$(IMPLIBEXT) \
|
||||
libriched20.$(IMPLIBEXT) \
|
||||
libriched32.$(IMPLIBEXT) \
|
||||
librpcrt4.$(IMPLIBEXT) \
|
||||
librsabase.$(IMPLIBEXT) \
|
||||
|
@ -1590,6 +1596,11 @@ librasapi32.def: rasapi32/rasapi32.spec.def
|
|||
librasapi32.a: rasapi32/rasapi32.spec.def
|
||||
$(DLLTOOL) -k -l $@ -d rasapi32/rasapi32.spec.def
|
||||
|
||||
libriched20.def: riched20/riched20.spec.def
|
||||
$(RM) $@ && $(LN_S) riched20/riched20.spec.def $@
|
||||
libriched20.a: riched20/riched20.spec.def
|
||||
$(DLLTOOL) -k -l $@ -d riched20/riched20.spec.def
|
||||
|
||||
libriched32.def: richedit/riched32.spec.def
|
||||
$(RM) $@ && $(LN_S) richedit/riched32.spec.def $@
|
||||
libriched32.a: richedit/riched32.spec.def
|
||||
|
@ -1880,6 +1891,7 @@ psapi/psapi.spec.def: $(WINEBUILD)
|
|||
qcap/qcap.spec.def: $(WINEBUILD)
|
||||
quartz/quartz.spec.def: $(WINEBUILD)
|
||||
rasapi32/rasapi32.spec.def: $(WINEBUILD)
|
||||
riched20/riched20.spec.def: $(WINEBUILD)
|
||||
richedit/riched32.spec.def: $(WINEBUILD)
|
||||
rpcrt4/rpcrt4.spec.def: $(WINEBUILD)
|
||||
rsabase/rsabase.spec.def: $(WINEBUILD)
|
||||
|
@ -2032,6 +2044,7 @@ psapi/psapi.dll.so: psapi
|
|||
qcap/qcap.dll.so: qcap
|
||||
quartz/quartz.dll.so: quartz
|
||||
rasapi32/rasapi32.dll.so: rasapi32
|
||||
riched20/riched20.dll.so: riched20
|
||||
richedit/riched32.dll.so: richedit
|
||||
rpcrt4/rpcrt4.dll.so: rpcrt4
|
||||
rsabase/rsabase.dll.so: rsabase
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
Makefile
|
||||
riched20.dll.dbg.c
|
||||
riched20.spec.def
|
|
@ -0,0 +1,25 @@
|
|||
TOPSRCDIR = @top_srcdir@
|
||||
TOPOBJDIR = ../..
|
||||
SRCDIR = @srcdir@
|
||||
VPATH = @srcdir@
|
||||
MODULE = riched20.dll
|
||||
IMPORTS = user32 gdi32 kernel32
|
||||
|
||||
C_SRCS = \
|
||||
caret.c \
|
||||
context.c \
|
||||
editor.c \
|
||||
list.c \
|
||||
paint.c \
|
||||
para.c \
|
||||
richole.c \
|
||||
row.c \
|
||||
run.c \
|
||||
string.c \
|
||||
style.c \
|
||||
undo.c \
|
||||
wrap.c
|
||||
|
||||
@MAKE_DLL_RULES@
|
||||
|
||||
### Dependencies:
|
|
@ -0,0 +1,959 @@
|
|||
/*
|
||||
* RichEdit - Caret and selection functions.
|
||||
*
|
||||
* Copyright 2004 by Krzysztof Foltman
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
|
||||
#include "editor.h"
|
||||
|
||||
WINE_DEFAULT_DEBUG_CHANNEL(richedit);
|
||||
|
||||
void ME_GetSelection(ME_TextEditor *editor, int *from, int *to)
|
||||
{
|
||||
*from = ME_GetCursorOfs(editor, 0);
|
||||
*to = ME_GetCursorOfs(editor, 1);
|
||||
|
||||
if (*from > *to)
|
||||
{
|
||||
int tmp = *from;
|
||||
*from = *to;
|
||||
*to = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
int ME_GetTextLength(ME_TextEditor *editor)
|
||||
{
|
||||
return ME_CharOfsFromRunOfs(editor, ME_FindItemBack(editor->pBuffer->pLast, diRun), 0);
|
||||
}
|
||||
|
||||
void ME_SetSelection(ME_TextEditor *editor, int from, int to)
|
||||
{
|
||||
if (from == 0 && to == -1)
|
||||
{
|
||||
editor->pCursors[1].pRun = ME_FindItemFwd(editor->pBuffer->pFirst, diRun);
|
||||
editor->pCursors[1].nOffset = 0;
|
||||
editor->pCursors[0].pRun = ME_FindItemBack(editor->pBuffer->pLast, diRun);
|
||||
editor->pCursors[0].nOffset = 0;
|
||||
ME_Repaint(editor);
|
||||
ME_ClearTempStyle(editor);
|
||||
return;
|
||||
}
|
||||
if (from == -1)
|
||||
{
|
||||
editor->pCursors[1] = editor->pCursors[0];
|
||||
ME_Repaint(editor);
|
||||
ME_ClearTempStyle(editor);
|
||||
return;
|
||||
}
|
||||
if (from>to)
|
||||
{
|
||||
int tmp = from;
|
||||
from = to;
|
||||
to = tmp;
|
||||
}
|
||||
ME_RunOfsFromCharOfs(editor, from, &editor->pCursors[1].pRun, &editor->pCursors[1].nOffset);
|
||||
ME_RunOfsFromCharOfs(editor, to, &editor->pCursors[0].pRun, &editor->pCursors[0].nOffset);
|
||||
}
|
||||
|
||||
void ME_MoveCaret(ME_TextEditor *editor)
|
||||
{
|
||||
HDC hDC = GetDC(editor->hWnd);
|
||||
ME_Context c;
|
||||
|
||||
ME_Cursor *pCursor = &editor->pCursors[0];
|
||||
ME_DisplayItem *pCursorRun = pCursor->pRun;
|
||||
ME_DisplayItem *pSizeRun = pCursor->pRun;
|
||||
|
||||
ME_InitContext(&c, editor, hDC);
|
||||
assert(!pCursor->nOffset || !editor->bCaretAtEnd);
|
||||
|
||||
if (pCursorRun->type == diRun) {
|
||||
ME_DisplayItem *row = ME_FindItemBack(pCursorRun, diStartRowOrParagraph);
|
||||
if (row) {
|
||||
ME_DisplayItem *run = pCursorRun;
|
||||
ME_DisplayItem *para;
|
||||
SIZE sz = {0, 0};
|
||||
if (!pCursor->nOffset && !editor->bCaretAtEnd)
|
||||
{
|
||||
ME_DisplayItem *prev = ME_FindItemBack(pCursorRun, diRunOrStartRow);
|
||||
if (prev->type == diRun)
|
||||
pSizeRun = prev;
|
||||
}
|
||||
assert(row->type == diStartRow); /* paragraph -> run without start row ?*/
|
||||
para = ME_FindItemBack(row, diParagraph);
|
||||
if (editor->bCaretAtEnd && !pCursor->nOffset &&
|
||||
run == ME_FindItemFwd(row, diRun))
|
||||
{
|
||||
ME_DisplayItem *tmp = ME_FindItemBack(row, diRunOrParagraph);
|
||||
if (tmp->type == diRun)
|
||||
{
|
||||
row = ME_FindItemBack(tmp, diStartRow);
|
||||
pSizeRun = run = tmp;
|
||||
sz = ME_GetRunSize(&c, &run->member.run, ME_StrLen(run->member.run.strText));
|
||||
}
|
||||
}
|
||||
if (pCursor->nOffset && !(run->member.run.nFlags & MERF_SKIPPED)) {
|
||||
sz = ME_GetRunSize(&c, &run->member.run, pCursor->nOffset);
|
||||
}
|
||||
CreateCaret(editor->hWnd, NULL, 0, pSizeRun->member.run.nAscent+pSizeRun->member.run.nDescent);
|
||||
SetCaretPos(run->member.run.pt.x+sz.cx,
|
||||
para->member.para.nYPos+row->member.row.nBaseline+pSizeRun->member.run.pt.y-pSizeRun->member.run.nAscent-GetScrollPos(editor->hWnd, SB_VERT));
|
||||
} else {
|
||||
assert(0 == "Wrapped paragraph run without a row?");
|
||||
CreateCaret(editor->hWnd, NULL, 0, 10);
|
||||
SetCaretPos(0,0);
|
||||
}
|
||||
}
|
||||
else {
|
||||
assert(0 == "Cursor not on a run");
|
||||
CreateCaret(editor->hWnd, NULL, 0, 10); /* FIXME use global font */
|
||||
SetCaretPos(0,0);
|
||||
}
|
||||
ME_DestroyContext(&c);
|
||||
ReleaseDC(editor->hWnd, hDC);
|
||||
}
|
||||
|
||||
void ME_ShowCaret(ME_TextEditor *ed)
|
||||
{
|
||||
ME_MoveCaret(ed);
|
||||
ShowCaret(ed->hWnd);
|
||||
}
|
||||
|
||||
void ME_HideCaret(ME_TextEditor *ed)
|
||||
{
|
||||
HideCaret(ed->hWnd);
|
||||
DestroyCaret();
|
||||
}
|
||||
|
||||
void ME_InternalDeleteText(ME_TextEditor *editor, int nOfs,
|
||||
int nChars)
|
||||
{
|
||||
ME_Cursor c;
|
||||
int shift = 0;
|
||||
|
||||
while(nChars > 0)
|
||||
{
|
||||
ME_Run *run;
|
||||
ME_CursorFromCharOfs(editor, nOfs, &c);
|
||||
run = &c.pRun->member.run;
|
||||
if (run->nFlags & MERF_ENDPARA) {
|
||||
if (!ME_FindItemFwd(c.pRun, diParagraph))
|
||||
{
|
||||
return;
|
||||
}
|
||||
ME_JoinParagraphs(editor, ME_GetParagraph(c.pRun));
|
||||
/* ME_SkipAndPropagateCharOffset(p->pRun, shift); */
|
||||
ME_CheckCharOffsets(editor);
|
||||
nChars--;
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
ME_Cursor cursor;
|
||||
int nIntendedChars = nChars;
|
||||
int nCharsToDelete = nChars;
|
||||
int i;
|
||||
int loc = c.nOffset;
|
||||
|
||||
ME_FindItemBack(c.pRun, diParagraph)->member.para.nFlags &= ~MEPF_WRAPPED;
|
||||
|
||||
cursor = c;
|
||||
ME_StrRelPos(run->strText, loc, &nChars);
|
||||
/* nChars is the number of characters that should be deleted from the
|
||||
FOLLOWING runs (these AFTER cursor.pRun)
|
||||
nCharsToDelete is a number of chars to delete from THIS run */
|
||||
nCharsToDelete -= nChars;
|
||||
shift -= nCharsToDelete;
|
||||
TRACE("Deleting %d (intended %d-remaning %d) chars at %d in '%s' (%d)\n",
|
||||
nCharsToDelete, nIntendedChars, nChars, c.nOffset,
|
||||
debugstr_w(run->strText->szData), run->strText->nLen);
|
||||
|
||||
if (!c.nOffset && ME_StrVLen(run->strText) == nCharsToDelete)
|
||||
{
|
||||
/* undo = reinsert whole run */
|
||||
/* nOfs is a character offset (from the start of the document
|
||||
to the current (deleted) run */
|
||||
ME_UndoItem *pUndo = ME_AddUndoItem(editor, diUndoInsertRun, c.pRun);
|
||||
if (pUndo)
|
||||
pUndo->di.member.run.nCharOfs = nOfs;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* undo = reinsert partial run */
|
||||
ME_UndoItem *pUndo = ME_AddUndoItem(editor, diUndoInsertRun, c.pRun);
|
||||
if (pUndo) {
|
||||
ME_DestroyString(pUndo->di.member.run.strText);
|
||||
pUndo->di.member.run.nCharOfs = nOfs;
|
||||
pUndo->di.member.run.strText = ME_MakeStringN(run->strText->szData+c.nOffset, nCharsToDelete);
|
||||
}
|
||||
}
|
||||
TRACE("Post deletion string: %s (%d)\n", debugstr_w(run->strText->szData), run->strText->nLen);
|
||||
TRACE("Shift value: %d\n", shift);
|
||||
ME_StrDeleteV(run->strText, c.nOffset, nCharsToDelete);
|
||||
|
||||
/* update cursors (including c) */
|
||||
for (i=-1; i<editor->nCursors; i++) {
|
||||
ME_Cursor *pThisCur = editor->pCursors + i;
|
||||
if (i == -1) pThisCur = &c;
|
||||
if (pThisCur->pRun == cursor.pRun) {
|
||||
if (pThisCur->nOffset > cursor.nOffset) {
|
||||
if (pThisCur->nOffset-cursor.nOffset < nCharsToDelete)
|
||||
pThisCur->nOffset = cursor.nOffset;
|
||||
else
|
||||
pThisCur->nOffset -= nCharsToDelete;
|
||||
assert(pThisCur->nOffset >= 0);
|
||||
assert(pThisCur->nOffset <= ME_StrVLen(run->strText));
|
||||
}
|
||||
if (pThisCur->nOffset == ME_StrVLen(run->strText))
|
||||
{
|
||||
pThisCur->pRun = ME_FindItemFwd(pThisCur->pRun, diRunOrParagraphOrEnd);
|
||||
assert(pThisCur->pRun->type == diRun);
|
||||
pThisCur->nOffset = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* c = updated data now */
|
||||
|
||||
if (c.pRun == cursor.pRun)
|
||||
ME_SkipAndPropagateCharOffset(c.pRun, shift);
|
||||
else
|
||||
ME_PropagateCharOffset(c.pRun, shift);
|
||||
|
||||
if (!ME_StrVLen(cursor.pRun->member.run.strText))
|
||||
{
|
||||
TRACE("Removing useless run\n");
|
||||
ME_Remove(cursor.pRun);
|
||||
ME_DestroyDisplayItem(cursor.pRun);
|
||||
}
|
||||
|
||||
shift = 0;
|
||||
/*
|
||||
ME_CheckCharOffsets(editor);
|
||||
*/
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ME_DeleteTextAtCursor(ME_TextEditor *editor, int nCursor,
|
||||
int nChars)
|
||||
{
|
||||
assert(nCursor>=0 && nCursor<editor->nCursors);
|
||||
ME_InternalDeleteText(editor, ME_GetCursorOfs(editor, nCursor), nChars);
|
||||
}
|
||||
|
||||
static WCHAR wszSpace[] = {' ', 0};
|
||||
|
||||
/* FIXME this is temporary, just to have something to test how bad graphics handler is */
|
||||
void ME_InsertGraphicsFromCursor(ME_TextEditor *editor, int nCursor)
|
||||
{
|
||||
ME_Cursor *pCursor = &editor->pCursors[nCursor];
|
||||
ME_DisplayItem *pItem = NULL;
|
||||
ME_DisplayItem *pNewRun = NULL;
|
||||
ME_Style *pStyle = ME_GetInsertStyle(editor, nCursor);
|
||||
ME_UndoItem *pUndo;
|
||||
|
||||
/* FIXME no no no */
|
||||
if (ME_IsSelection(editor))
|
||||
ME_DeleteSelection(editor);
|
||||
|
||||
pUndo = ME_AddUndoItem(editor, diUndoDeleteRun, NULL);
|
||||
if (pUndo) {
|
||||
pUndo->nStart = pCursor->nOffset + pCursor->pRun->member.run.nCharOfs + ME_GetParagraph(pCursor->pRun)->member.para.nCharOfs;
|
||||
pUndo->nLen = 1;
|
||||
}
|
||||
if (pCursor->nOffset)
|
||||
{
|
||||
ME_SplitRunSimple(editor, pCursor->pRun, pCursor->nOffset);
|
||||
}
|
||||
pItem = pCursor->pRun;
|
||||
pNewRun = ME_MakeRun(pStyle, ME_MakeStringN(wszSpace, 1), MERF_GRAPHICS);
|
||||
pNewRun->member.run.nCharOfs = pCursor->pRun->member.run.nCharOfs;
|
||||
ME_InsertBefore(pCursor->pRun, pNewRun);
|
||||
ME_PropagateCharOffset(pItem, 1);
|
||||
ME_CheckCharOffsets(editor);
|
||||
ME_SendSelChange(editor);
|
||||
}
|
||||
|
||||
void ME_InsertTextFromCursor(ME_TextEditor *editor, int nCursor,
|
||||
const WCHAR *str, int len, ME_Style *style)
|
||||
{
|
||||
const WCHAR *pos;
|
||||
ME_Cursor *p = NULL;
|
||||
|
||||
assert(style);
|
||||
editor->bCaretAtEnd = FALSE;
|
||||
|
||||
/*
|
||||
if (!style)
|
||||
style = ME_GetInsertStyle(editor, nCursor);
|
||||
else
|
||||
ME_AddRefStyle(style);
|
||||
*/
|
||||
ME_AddRefStyle(style);
|
||||
|
||||
/* FIXME really HERE ? */
|
||||
if (ME_IsSelection(editor))
|
||||
ME_DeleteSelection(editor);
|
||||
|
||||
assert(nCursor>=0 && nCursor<editor->nCursors);
|
||||
if (len == -1)
|
||||
len = lstrlenW(str);
|
||||
pos = str;
|
||||
/* FIXME this sucks - no respect for unicode (what else can be a line separator in unicode?) */
|
||||
while(pos-str < len && *pos != '\r' && *pos != '\n')
|
||||
pos++;
|
||||
/* handle EOLs */
|
||||
if (pos-str < len) {
|
||||
ME_DisplayItem *tp, *end_run;
|
||||
ME_Paragraph *para;
|
||||
ME_Style *tmp_style;
|
||||
if (pos!=str)
|
||||
ME_InsertTextFromCursor(editor, nCursor, str, pos-str, style);
|
||||
p = &editor->pCursors[nCursor];
|
||||
tp = ME_FindItemBack(p->pRun, diParagraph);
|
||||
para = &tp->member.para;
|
||||
assert(tp);
|
||||
if (p->nOffset) {
|
||||
ME_SplitRunSimple(editor, p->pRun, p->nOffset);
|
||||
p = &editor->pCursors[nCursor];
|
||||
}
|
||||
tmp_style = ME_GetInsertStyle(editor, nCursor);
|
||||
/* ME_SplitParagraph increases style refcount */
|
||||
tp = ME_SplitParagraph(editor, p->pRun, p->pRun->member.run.style);
|
||||
p->pRun = ME_FindItemFwd(tp, diRun);
|
||||
end_run = ME_FindItemBack(tp, diRun);
|
||||
ME_ReleaseStyle(end_run->member.run.style);
|
||||
end_run->member.run.style = tmp_style;
|
||||
p->nOffset = 0;
|
||||
if(pos-str < len && *pos =='\r')
|
||||
pos++;
|
||||
if(pos-str < len && *pos =='\n')
|
||||
pos++;
|
||||
if(pos-str < len) {
|
||||
ME_InsertTextFromCursor(editor, nCursor, pos, len-(pos-str), style);
|
||||
}
|
||||
ME_ReleaseStyle(style);
|
||||
return;
|
||||
}
|
||||
p = &editor->pCursors[nCursor];
|
||||
if (style) {
|
||||
ME_DisplayItem *pNewRun = NULL;
|
||||
|
||||
assert(p->pRun->type == diRun);
|
||||
pNewRun = ME_MakeRun(style, ME_MakeStringN(str, len), 0); /* addrefs style */
|
||||
ME_InsertRun(editor, ME_CharOfsFromRunOfs(editor, p->pRun, p->nOffset), pNewRun);
|
||||
ME_DestroyDisplayItem(pNewRun);
|
||||
ME_ReleaseStyle(style);
|
||||
return;
|
||||
} else {
|
||||
assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
BOOL ME_ArrowLeft(ME_TextEditor *editor, ME_Cursor *p)
|
||||
{
|
||||
if (p->nOffset) {
|
||||
p->nOffset = ME_StrRelPos2(p->pRun->member.run.strText, p->nOffset, -1);
|
||||
return TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
ME_DisplayItem *pRun = ME_FindItemBack(p->pRun, diRunOrParagraph);
|
||||
assert(pRun);
|
||||
if (pRun->type == diRun) {
|
||||
p->pRun = pRun;
|
||||
assert(p->pRun->type == diRun);
|
||||
p->nOffset = pRun->member.run.strText->nLen;
|
||||
if (p->nOffset) {
|
||||
p->nOffset = ME_StrRelPos2(pRun->member.run.strText, p->nOffset, -1);
|
||||
return TRUE;
|
||||
}
|
||||
else
|
||||
assert(0);
|
||||
}
|
||||
if (pRun->type == diParagraph)
|
||||
{
|
||||
if (pRun->member.para.prev_para->type == diTextStart)
|
||||
return FALSE;
|
||||
assert(pRun->member.para.prev_para->type == diParagraph);
|
||||
pRun = ME_FindItemBack(pRun, diRunOrParagraph);
|
||||
/* every paragraph ought to have at least one run */
|
||||
assert(pRun && pRun->type == diRun);
|
||||
assert(pRun->member.run.nFlags & MERF_ENDPARA);
|
||||
p->pRun = pRun;
|
||||
p->nOffset = 0;
|
||||
return TRUE;
|
||||
}
|
||||
assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
BOOL ME_ArrowRight(ME_TextEditor *editor, ME_Cursor *p)
|
||||
{
|
||||
int new_ofs = ME_StrRelPos2(p->pRun->member.run.strText, p->nOffset, 1);
|
||||
if (new_ofs<p->pRun->member.run.strText->nLen) {
|
||||
p->nOffset = new_ofs;
|
||||
}
|
||||
else
|
||||
{
|
||||
ME_DisplayItem *pRun = ME_FindItemFwd(p->pRun, diRun);
|
||||
if (pRun) {
|
||||
p->pRun = pRun;
|
||||
assert(p->pRun->type == diRun);
|
||||
p->nOffset = 0;
|
||||
}
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
int ME_GetCursorOfs(ME_TextEditor *editor, int nCursor)
|
||||
{
|
||||
ME_Cursor *pCursor = &editor->pCursors[nCursor];
|
||||
|
||||
return ME_GetParagraph(pCursor->pRun)->member.para.nCharOfs
|
||||
+ pCursor->pRun->member.run.nCharOfs + pCursor->nOffset;
|
||||
}
|
||||
|
||||
int ME_FindPixelPos(ME_TextEditor *editor, int x, int y, ME_Cursor *result, BOOL *is_eol)
|
||||
{
|
||||
ME_DisplayItem *p = editor->pBuffer->pFirst->member.para.next_para;
|
||||
int rx = 0;
|
||||
|
||||
if (is_eol)
|
||||
*is_eol = 0;
|
||||
|
||||
while(p != editor->pBuffer->pLast)
|
||||
{
|
||||
if (p->type == diParagraph)
|
||||
{
|
||||
int ry = y - p->member.para.nYPos;
|
||||
if (ry < 0)
|
||||
{
|
||||
result->pRun = ME_FindItemFwd(p, diRun);
|
||||
result->nOffset = 0;
|
||||
return 0;
|
||||
}
|
||||
if (ry >= p->member.para.nHeight)
|
||||
{
|
||||
p = p->member.para.next_para;
|
||||
continue;
|
||||
}
|
||||
p = ME_FindItemFwd(p, diStartRow);
|
||||
y = ry;
|
||||
continue;
|
||||
}
|
||||
if (p->type == diStartRow)
|
||||
{
|
||||
int ry = y - p->member.row.nYPos;
|
||||
if (ry < 0)
|
||||
return 0;
|
||||
if (ry >= p->member.row.nHeight)
|
||||
{
|
||||
p = ME_FindItemFwd(p, diStartRowOrParagraphOrEnd);
|
||||
if (p->type != diStartRow)
|
||||
return 0;
|
||||
continue;
|
||||
}
|
||||
p = ME_FindItemFwd(p, diRun);
|
||||
continue;
|
||||
}
|
||||
if (p->type == diRun)
|
||||
{
|
||||
ME_DisplayItem *pp;
|
||||
rx = x - p->member.run.pt.x;
|
||||
if (rx < 0)
|
||||
rx = 0;
|
||||
if (rx >= p->member.run.nWidth) /* not this run yet... find next item */
|
||||
{
|
||||
pp = p;
|
||||
do {
|
||||
p = p->next;
|
||||
if (p->type == diRun)
|
||||
{
|
||||
rx = x - p->member.run.pt.x;
|
||||
goto continue_search;
|
||||
}
|
||||
if (p->type == diStartRow)
|
||||
{
|
||||
p = ME_FindItemFwd(p, diRun);
|
||||
if (is_eol)
|
||||
*is_eol = 1;
|
||||
rx = 0; /* FIXME not sure */
|
||||
goto found_here;
|
||||
}
|
||||
if (p->type == diParagraph || p->type == diTextEnd)
|
||||
{
|
||||
rx = 0; /* FIXME not sure */
|
||||
p = pp;
|
||||
goto found_here;
|
||||
}
|
||||
} while(1);
|
||||
continue;
|
||||
}
|
||||
found_here:
|
||||
if (p->member.run.nFlags & MERF_ENDPARA)
|
||||
rx = 0;
|
||||
result->pRun = p;
|
||||
result->nOffset = ME_CharFromPointCursor(editor, rx, &p->member.run);
|
||||
if (editor->pCursors[0].nOffset == p->member.run.strText->nLen && rx)
|
||||
{
|
||||
result->pRun = ME_FindItemFwd(editor->pCursors[0].pRun, diRun);
|
||||
result->nOffset = 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
assert(0);
|
||||
continue_search:
|
||||
;
|
||||
}
|
||||
result->pRun = ME_FindItemBack(p, diRun);
|
||||
result->nOffset = 0;
|
||||
assert(result->pRun->member.run.nFlags & MERF_ENDPARA);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ME_LButtonDown(ME_TextEditor *editor, int x, int y)
|
||||
{
|
||||
ME_Cursor tmp_cursor;
|
||||
int is_selection = 0;
|
||||
|
||||
editor->nUDArrowX = -1;
|
||||
|
||||
y += GetScrollPos(editor->hWnd, SB_VERT);
|
||||
|
||||
tmp_cursor = editor->pCursors[0];
|
||||
is_selection = ME_IsSelection(editor);
|
||||
|
||||
ME_FindPixelPos(editor, x, y, &editor->pCursors[0], &editor->bCaretAtEnd);
|
||||
|
||||
if (GetKeyState(VK_SHIFT)>=0)
|
||||
{
|
||||
editor->pCursors[1] = editor->pCursors[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!is_selection) {
|
||||
editor->pCursors[1] = tmp_cursor;
|
||||
is_selection = 1;
|
||||
}
|
||||
}
|
||||
HideCaret(editor->hWnd);
|
||||
ME_MoveCaret(editor);
|
||||
if (is_selection)
|
||||
ME_Repaint(editor);
|
||||
ShowCaret(editor->hWnd);
|
||||
ME_ClearTempStyle(editor);
|
||||
ME_SendSelChange(editor);
|
||||
}
|
||||
|
||||
void ME_MouseMove(ME_TextEditor *editor, int x, int y)
|
||||
{
|
||||
ME_Cursor tmp_cursor;
|
||||
|
||||
y += GetScrollPos(editor->hWnd, SB_VERT);
|
||||
|
||||
tmp_cursor = editor->pCursors[0];
|
||||
if (!ME_FindPixelPos(editor, x, y, &editor->pCursors[0], &editor->bCaretAtEnd))
|
||||
/* return */;
|
||||
|
||||
if (tmp_cursor.pRun == editor->pCursors[0].pRun &&
|
||||
tmp_cursor.nOffset == editor->pCursors[0].nOffset)
|
||||
return;
|
||||
|
||||
HideCaret(editor->hWnd);
|
||||
ME_MoveCaret(editor);
|
||||
ME_Repaint(editor);
|
||||
ShowCaret(editor->hWnd);
|
||||
ME_SendSelChange(editor);
|
||||
}
|
||||
|
||||
static ME_DisplayItem *ME_FindRunInRow(ME_TextEditor *editor, ME_DisplayItem *pRow,
|
||||
int x, int *pOffset, int *pbCaretAtEnd)
|
||||
{
|
||||
ME_DisplayItem *pNext, *pLastRun;
|
||||
pNext = ME_FindItemFwd(pRow, diRunOrStartRow);
|
||||
assert(pNext->type == diRun);
|
||||
pLastRun = pNext;
|
||||
*pbCaretAtEnd = FALSE;
|
||||
do {
|
||||
int run_x = pNext->member.run.pt.x;
|
||||
int width = pNext->member.run.nWidth;
|
||||
if (x < run_x)
|
||||
{
|
||||
if (pOffset) *pOffset = 0;
|
||||
return pNext;
|
||||
}
|
||||
if (x >= run_x && x < run_x+width)
|
||||
{
|
||||
int ch = ME_CharFromPointCursor(editor, x-run_x, &pNext->member.run);
|
||||
ME_String *s = pNext->member.run.strText;
|
||||
if (ch < s->nLen) {
|
||||
if (pOffset)
|
||||
*pOffset = ch;
|
||||
return pNext;
|
||||
}
|
||||
}
|
||||
pLastRun = pNext;
|
||||
pNext = ME_FindItemFwd(pNext, diRunOrStartRow);
|
||||
} while(pNext && pNext->type == diRun);
|
||||
|
||||
if ((pLastRun->member.run.nFlags & MERF_ENDPARA) == 0)
|
||||
{
|
||||
pNext = ME_FindItemFwd(pNext, diRun);
|
||||
if (pbCaretAtEnd) *pbCaretAtEnd = 1;
|
||||
if (pOffset) *pOffset = 0;
|
||||
return pNext;
|
||||
} else {
|
||||
if (pbCaretAtEnd) *pbCaretAtEnd = 0;
|
||||
if (pOffset) *pOffset = 0;
|
||||
return pLastRun;
|
||||
}
|
||||
}
|
||||
|
||||
static int ME_GetXForArrow(ME_TextEditor *editor, ME_Cursor *pCursor)
|
||||
{
|
||||
ME_DisplayItem *pRun = pCursor->pRun;
|
||||
int x;
|
||||
|
||||
if (editor->nUDArrowX != -1)
|
||||
x = editor->nUDArrowX;
|
||||
else {
|
||||
if (editor->bCaretAtEnd)
|
||||
{
|
||||
pRun = ME_FindItemBack(pRun, diRun);
|
||||
assert(pRun);
|
||||
x = pRun->member.run.pt.x + pRun->member.run.nWidth;
|
||||
}
|
||||
else {
|
||||
x = pRun->member.run.pt.x;
|
||||
x += ME_PointFromChar(editor, &pRun->member.run, pCursor->nOffset);
|
||||
}
|
||||
editor->nUDArrowX = x;
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
void ME_ArrowUp(ME_TextEditor *editor, ME_Cursor *pCursor)
|
||||
{
|
||||
ME_DisplayItem *pRun = pCursor->pRun;
|
||||
ME_DisplayItem *pItem, *pItem2;
|
||||
int x = ME_GetXForArrow(editor, pCursor);
|
||||
|
||||
if (editor->bCaretAtEnd && !pCursor->nOffset)
|
||||
{
|
||||
pRun = ME_FindItemBack(pRun, diRun);
|
||||
if (!pRun)
|
||||
return;
|
||||
}
|
||||
|
||||
/* start of this row */
|
||||
pItem = ME_FindItemBack(pRun, diStartRow);
|
||||
assert(pItem);
|
||||
/* start of the previous row */
|
||||
pItem2 = ME_FindItemBack(pItem, diStartRow);
|
||||
/* no previous row = the first line of the first paragraph */
|
||||
if (!pItem2) /* can't go up - don't go BOL (as in MS richedit) */
|
||||
return;
|
||||
/* FIXME
|
||||
ME_WrapTextParagraph(editor, ME_FindItemBack(pItem2, diParagraph));
|
||||
*/
|
||||
pCursor->pRun = ME_FindRunInRow(editor, pItem2, x, &pCursor->nOffset, &editor->bCaretAtEnd);
|
||||
}
|
||||
|
||||
void ME_ArrowDown(ME_TextEditor *editor, ME_Cursor *pCursor)
|
||||
{
|
||||
ME_DisplayItem *pRun = pCursor->pRun;
|
||||
ME_DisplayItem *pItem;
|
||||
int x = ME_GetXForArrow(editor, pCursor);
|
||||
if (!pCursor->nOffset && editor->bCaretAtEnd)
|
||||
{
|
||||
pRun = ME_FindItemBack(pRun, diRun);
|
||||
/* x = pRun->member.run.pt.x + pRun->member.run.nWidth; */
|
||||
}
|
||||
/* start of the next row */
|
||||
pItem = ME_FindItemFwd(pRun, diStartRow);
|
||||
/* FIXME If diParagraph is before diStartRow, wrap the next paragraph?
|
||||
*/
|
||||
if (!pItem)
|
||||
{
|
||||
/* next row not found - ignore */
|
||||
return;
|
||||
}
|
||||
pCursor->pRun = ME_FindRunInRow(editor, pItem, x, &pCursor->nOffset, &editor->bCaretAtEnd);
|
||||
assert(pCursor->pRun);
|
||||
assert(pCursor->pRun->type == diRun);
|
||||
}
|
||||
|
||||
void ME_ArrowHome(ME_TextEditor *editor, ME_Cursor *pCursor)
|
||||
{
|
||||
ME_DisplayItem *pRow = ME_FindItemBack(pCursor->pRun, diStartRow);
|
||||
if (pRow) {
|
||||
ME_DisplayItem *pRun;
|
||||
if (editor->bCaretAtEnd && !pCursor->nOffset) {
|
||||
pRow = ME_FindItemBack(pRow, diStartRow);
|
||||
if (!pRow)
|
||||
return;
|
||||
}
|
||||
pRun = ME_FindItemFwd(pRow, diRun);
|
||||
if (pRun) {
|
||||
pCursor->pRun = pRun;
|
||||
pCursor->nOffset = 0;
|
||||
}
|
||||
}
|
||||
editor->bCaretAtEnd = FALSE;
|
||||
}
|
||||
|
||||
void ME_ArrowCtrlHome(ME_TextEditor *editor, ME_Cursor *pCursor)
|
||||
{
|
||||
ME_DisplayItem *pRow = ME_FindItemBack(pCursor->pRun, diTextStart);
|
||||
if (pRow) {
|
||||
ME_DisplayItem *pRun = ME_FindItemFwd(pRow, diRun);
|
||||
if (pRun) {
|
||||
pCursor->pRun = pRun;
|
||||
pCursor->nOffset = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ME_ArrowEnd(ME_TextEditor *editor, ME_Cursor *pCursor)
|
||||
{
|
||||
ME_DisplayItem *pRow;
|
||||
|
||||
if (editor->bCaretAtEnd && !pCursor->nOffset)
|
||||
return;
|
||||
|
||||
pRow = ME_FindItemFwd(pCursor->pRun, diStartRowOrParagraphOrEnd);
|
||||
assert(pRow);
|
||||
if (pRow->type == diStartRow) {
|
||||
/* FIXME WTF was I thinking about here ? */
|
||||
ME_DisplayItem *pRun = ME_FindItemFwd(pRow, diRun);
|
||||
assert(pRun);
|
||||
pCursor->pRun = pRun;
|
||||
pCursor->nOffset = 0;
|
||||
editor->bCaretAtEnd = 1;
|
||||
return;
|
||||
}
|
||||
pCursor->pRun = ME_FindItemBack(pRow, diRun);
|
||||
assert(pCursor->pRun && pCursor->pRun->member.run.nFlags & MERF_ENDPARA);
|
||||
pCursor->nOffset = 0;
|
||||
editor->bCaretAtEnd = FALSE;
|
||||
}
|
||||
|
||||
void ME_ArrowCtrlEnd(ME_TextEditor *editor, ME_Cursor *pCursor)
|
||||
{
|
||||
ME_DisplayItem *p = ME_FindItemFwd(pCursor->pRun, diTextEnd);
|
||||
assert(p);
|
||||
p = ME_FindItemBack(p, diRun);
|
||||
assert(p);
|
||||
assert(p->member.run.nFlags & MERF_ENDPARA);
|
||||
pCursor->pRun = p;
|
||||
pCursor->nOffset = 0;
|
||||
editor->bCaretAtEnd = FALSE;
|
||||
}
|
||||
|
||||
BOOL ME_IsSelection(ME_TextEditor *editor)
|
||||
{
|
||||
return memcmp(&editor->pCursors[0], &editor->pCursors[1], sizeof(ME_Cursor))!=0;
|
||||
}
|
||||
|
||||
int ME_GetSelCursor(ME_TextEditor *editor, int dir)
|
||||
{
|
||||
int cdir = ME_GetCursorOfs(editor, 0) - ME_GetCursorOfs(editor, 1);
|
||||
|
||||
if (cdir*dir>0)
|
||||
return 0;
|
||||
else
|
||||
return 1;
|
||||
}
|
||||
|
||||
BOOL ME_CancelSelection(ME_TextEditor *editor, int dir)
|
||||
{
|
||||
int cdir;
|
||||
|
||||
if (GetKeyState(VK_SHIFT)<0)
|
||||
return FALSE;
|
||||
if (!memcmp(&editor->pCursors[0], &editor->pCursors[1], sizeof(ME_Cursor)))
|
||||
return FALSE;
|
||||
|
||||
cdir = ME_GetCursorOfs(editor, 0) - ME_GetCursorOfs(editor, 1);
|
||||
|
||||
if (cdir*dir>0)
|
||||
editor->pCursors[1] = editor->pCursors[0];
|
||||
else
|
||||
editor->pCursors[0] = editor->pCursors[1];
|
||||
/* FIXME optimize */
|
||||
ME_MarkAllForWrapping(editor);
|
||||
ME_Repaint(editor);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void ME_RepaintSelection(ME_TextEditor *editor, ME_Cursor *pTempCursor)
|
||||
{
|
||||
ME_Cursor old_anchor = editor->pCursors[1];
|
||||
BOOL bRedraw = FALSE;
|
||||
bRedraw = memcmp(&editor->pCursors[0], &editor->pCursors[1], sizeof(ME_Cursor));
|
||||
|
||||
if (bRedraw)
|
||||
{
|
||||
/* FIXME optimize */
|
||||
ME_MarkAllForWrapping(editor);
|
||||
}
|
||||
|
||||
if (GetKeyState(VK_SHIFT)>=0) /* cancelling selection */
|
||||
{
|
||||
/* any selection was present ? if so, it's no more, repaint ! */
|
||||
editor->pCursors[1] = editor->pCursors[0];
|
||||
if (memcmp(pTempCursor, &old_anchor, sizeof(ME_Cursor))) {
|
||||
ME_Repaint(editor);
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!memcmp(pTempCursor, &editor->pCursors[1], sizeof(ME_Cursor))) /* starting selection */
|
||||
{
|
||||
editor->pCursors[1] = *pTempCursor;
|
||||
}
|
||||
}
|
||||
|
||||
ME_Repaint(editor);
|
||||
}
|
||||
|
||||
void ME_DeleteSelection(ME_TextEditor *editor)
|
||||
{
|
||||
int from, to;
|
||||
ME_GetSelection(editor, &from, &to);
|
||||
ME_DeleteTextAtCursor(editor, ME_GetSelCursor(editor,-1), to-from);
|
||||
}
|
||||
|
||||
void ME_SendSelChange(ME_TextEditor *editor)
|
||||
{
|
||||
SELCHANGE sc;
|
||||
if (!(editor->nEventMask & ENM_SELCHANGE))
|
||||
return;
|
||||
sc.nmhdr.hwndFrom = editor->hWnd;
|
||||
sc.nmhdr.idFrom = GetWindowLongW(editor->hWnd, GWLP_ID);
|
||||
sc.nmhdr.code = EN_SELCHANGE;
|
||||
SendMessageW(editor->hWnd, EM_EXGETSEL, 0, (LPARAM)&sc.chrg);
|
||||
sc.seltyp = SEL_EMPTY;
|
||||
if (sc.chrg.cpMin != sc.chrg.cpMax)
|
||||
sc.seltyp |= SEL_TEXT;
|
||||
if (sc.chrg.cpMin < sc.chrg.cpMax+1) /* wth were RICHEDIT authors thinking ? */
|
||||
sc.seltyp |= SEL_MULTICHAR;
|
||||
SendMessageW(GetParent(editor->hWnd), WM_NOTIFY, sc.nmhdr.idFrom, (LPARAM)&sc);
|
||||
}
|
||||
|
||||
BOOL ME_ArrowKey(ME_TextEditor *editor, int nVKey, int nCtrl)
|
||||
{
|
||||
int nCursor = 0;
|
||||
ME_Cursor *p = &editor->pCursors[nCursor];
|
||||
ME_Cursor tmp_curs = *p;
|
||||
|
||||
switch(nVKey) {
|
||||
case VK_UP:
|
||||
ME_ArrowUp(editor, p);
|
||||
ME_ClearTempStyle(editor);
|
||||
ME_RepaintSelection(editor, &tmp_curs);
|
||||
ME_SendSelChange(editor);
|
||||
return TRUE;
|
||||
case VK_DOWN:
|
||||
ME_ArrowDown(editor, p);
|
||||
ME_ClearTempStyle(editor);
|
||||
ME_RepaintSelection(editor, &tmp_curs);
|
||||
ME_SendSelChange(editor);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
editor->nUDArrowX = -1;
|
||||
switch(nVKey) {
|
||||
case VK_BACK: { /* FIXME backspace and delete aren't the same, they act different wrt paragraph style of the merged paragraph */
|
||||
if (GetWindowLongW(editor->hWnd, GWL_STYLE) & ES_READONLY)
|
||||
return FALSE;
|
||||
if (ME_IsSelection(editor))
|
||||
{
|
||||
editor->bCaretAtEnd = FALSE; /* FIXME or maybe not */
|
||||
ME_DeleteSelection(editor);
|
||||
ME_UpdateRepaint(editor);
|
||||
return TRUE;
|
||||
}
|
||||
if (ME_ArrowLeft(editor, p)) {
|
||||
editor->bCaretAtEnd = FALSE; /* FIXME or maybe not */
|
||||
ME_MoveCaret(editor);
|
||||
ME_DeleteTextAtCursor(editor, nCursor, 1);
|
||||
ME_UpdateRepaint(editor);
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
case VK_DELETE: {
|
||||
if (GetWindowLongW(editor->hWnd, GWL_STYLE) & ES_READONLY)
|
||||
return FALSE;
|
||||
/* editor->bCaretAtEnd = 0; FIXME or maybe not */
|
||||
if (ME_IsSelection(editor))
|
||||
{
|
||||
ME_DeleteSelection(editor);
|
||||
ME_UpdateRepaint(editor);
|
||||
return TRUE;
|
||||
}
|
||||
ME_DeleteTextAtCursor(editor, nCursor, 1);
|
||||
ME_UpdateRepaint(editor);
|
||||
return TRUE;
|
||||
}
|
||||
case VK_HOME: {
|
||||
if (GetKeyState(VK_CONTROL)<0)
|
||||
ME_ArrowCtrlHome(editor, p);
|
||||
else
|
||||
ME_ArrowHome(editor, p);
|
||||
editor->bCaretAtEnd = 0;
|
||||
ME_ClearTempStyle(editor);
|
||||
ME_RepaintSelection(editor, &tmp_curs);
|
||||
ME_SendSelChange(editor);
|
||||
return TRUE;
|
||||
}
|
||||
case VK_END:
|
||||
if (GetKeyState(VK_CONTROL)<0)
|
||||
ME_ArrowCtrlEnd(editor, p);
|
||||
else
|
||||
ME_ArrowEnd(editor, p);
|
||||
ME_ClearTempStyle(editor);
|
||||
ME_RepaintSelection(editor, &tmp_curs);
|
||||
ME_SendSelChange(editor);
|
||||
return TRUE;
|
||||
case VK_LEFT:
|
||||
editor->bCaretAtEnd = 0;
|
||||
if (ME_CancelSelection(editor, -1))
|
||||
return TRUE;
|
||||
ME_ArrowLeft(editor, p);
|
||||
ME_RepaintSelection(editor, &tmp_curs);
|
||||
ME_ClearTempStyle(editor);
|
||||
ME_SendSelChange(editor);
|
||||
return TRUE;
|
||||
case VK_RIGHT:
|
||||
editor->bCaretAtEnd = 0;
|
||||
if (ME_CancelSelection(editor, +1))
|
||||
return TRUE;
|
||||
ME_ArrowRight(editor, p);
|
||||
ME_RepaintSelection(editor, &tmp_curs);
|
||||
ME_ClearTempStyle(editor);
|
||||
ME_SendSelChange(editor);
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* RichEdit - Operation context functions
|
||||
*
|
||||
* Copyright 2004 by Krzysztof Foltman
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "editor.h"
|
||||
|
||||
void ME_InitContext(ME_Context *c, ME_TextEditor *editor, HDC hDC)
|
||||
{
|
||||
c->nSequence = editor->nSequence++;
|
||||
c->hDC = hDC;
|
||||
c->editor = editor;
|
||||
c->pt.x = 0;
|
||||
c->pt.y = 0;
|
||||
c->hbrMargin = CreateSolidBrush(RGB(224,224,224));
|
||||
GetClientRect(editor->hWnd, &c->rcView);
|
||||
}
|
||||
|
||||
void ME_DestroyContext(ME_Context *c)
|
||||
{
|
||||
DeleteObject(c->hbrMargin);
|
||||
}
|
|
@ -0,0 +1,998 @@
|
|||
/*
|
||||
* RichEdit - functions dealing with editor object
|
||||
*
|
||||
* Copyright 2004 by Krzysztof Foltman
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
/*
|
||||
API implementation status:
|
||||
|
||||
Messages (ANSI versions not done yet)
|
||||
- EM_AUTOURLDETECT 2.0
|
||||
- EM_CANPASTE
|
||||
+ EM_CANREDO 2.0
|
||||
+ EM_CANUNDO
|
||||
- EM_CHARFROMPOS
|
||||
- EM_DISPLAYBAND
|
||||
+ EM_EMPTYUNDOBUFFER
|
||||
+ EM_EXGETSEL
|
||||
- EM_EXLIMITTEXT
|
||||
- EM_EXLINEFROMCHAR
|
||||
+ EM_EXSETSEL
|
||||
- EM_FINDTEXT
|
||||
- EM_FINDTEXTEX
|
||||
- EM_FINDWORDBREAK
|
||||
- EM_FMTLINES
|
||||
- EM_FORMATRANGE
|
||||
- EM_GETCHARFORMAT (partly done)
|
||||
+ EM_GETEVENTMASK
|
||||
- EM_GETFIRSTVISIBLELINE
|
||||
- EM_GETIMECOLOR 1.0asian
|
||||
- EM_GETIMECOMPMODE 2.0
|
||||
- EM_GETIMEOPTIONS 1.0asian
|
||||
- EM_GETIMESTATUS
|
||||
- EM_GETLANGOPTIONS 2.0
|
||||
- EM_GETLIMITTEXT
|
||||
- EM_GETLINE
|
||||
- EM_GETLINECOUNT returns number of rows, not of paragraphs
|
||||
+ EM_GETMODIFY
|
||||
- EM_GETOLEINTERFACE
|
||||
- EM_GETOPTIONS
|
||||
+ EM_GETPARAFORMAT
|
||||
- EM_GETPUNCTUATION 1.0asian
|
||||
- EM_GETRECT
|
||||
- EM_GETREDONAME 2.0
|
||||
+ EM_GETSEL
|
||||
+ EM_GETSELTEXT (ANSI&Unicode)
|
||||
! - EM_GETTHUMB
|
||||
- EM_GETTEXTMODE 2.0
|
||||
? + EM_GETTEXTRANGE (ANSI&Unicode)
|
||||
- EM_GETUNDONAME
|
||||
- EM_GETWORDBREAKPROC
|
||||
- EM_GETWORDBREAKPROCEX
|
||||
- EM_GETWORDWRAPMODE 1.0asian
|
||||
- EM_HIDESELECTION
|
||||
- EM_LIMITTEXT
|
||||
- EM_LINEFROMCHAR
|
||||
- EM_LINEINDEX
|
||||
- EM_LINELENGTH
|
||||
- EM_LINESCROLL
|
||||
- EM_PASTESPECIAL
|
||||
- EM_POSFROMCHARS
|
||||
- EM_REDO 2.0
|
||||
- EM_REQUESTRESIZE
|
||||
+ EM_REPLACESEL (proper style?) ANSI&Unicode
|
||||
- EM_SCROLL
|
||||
- EM_SCROLLCARET
|
||||
- EM_SELECTIONTYPE
|
||||
+ EM_SETBKGNDCOLOR
|
||||
- EM_SETCHARFORMAT (partly done, no ANSI)
|
||||
+ EM_SETEVENTMASK (few notifications supported)
|
||||
- EM_SETIMECOLOR 1.0asian
|
||||
- EM_SETIMEOPTIONS 1.0asian
|
||||
- EM_SETLANGOPTIONS 2.0
|
||||
- EM_SETLIMITTEXT
|
||||
+ EM_SETMODIFY (not sure if implementation is correct)
|
||||
- EM_SETOLECALLBACK
|
||||
- EM_SETOPTIONS
|
||||
+ EM_SETPARAFORMAT
|
||||
- EM_SETPUNCTUATION 1.0asian
|
||||
+ EM_SETREADONLY no beep on modification attempt
|
||||
- EM_SETRECT
|
||||
- EM_SETRECTNP (EM_SETRECT without repainting) - not supported in RICHEDIT
|
||||
+ EM_SETSEL
|
||||
- EM_SETTARGETDEVICE
|
||||
- EM_SETTEXTMODE 2.0
|
||||
- EM_SETUNDOLIMIT 2.0
|
||||
- EM_SETWORDBREAKPROC
|
||||
- EM_SETWORDBREAKPROCEX
|
||||
- EM_SETWORDWRAPMODE 1.0asian
|
||||
- EM_STOPGROUPTYPING 2.0
|
||||
- EM_STREAMIN
|
||||
- EM_STREAMOUT
|
||||
- EM_UNDO
|
||||
+ WM_CHAR
|
||||
+ WM_CLEAR
|
||||
- WM_COPY (lame implementation, no RTF support)
|
||||
- WM_CUT (lame implementation, no RTF support)
|
||||
+ WM_GETDLGCODE (the current implementation is incomplete)
|
||||
+ WM_GETTEXT (ANSI&Unicode)
|
||||
+ WM_GETTEXTLENGTH (ANSI version sucks)
|
||||
- WM_PASTE
|
||||
- WM_SETFONT
|
||||
+ WM_SETTEXT (resets undo stack !) (proper style?) ANSI&Unicode
|
||||
- WM_STYLECHANGING
|
||||
- WM_STYLECHANGED (things like read-only flag)
|
||||
- WM_UNICHAR
|
||||
|
||||
Notifications
|
||||
|
||||
* EN_CHANGE (sent from the wrong place)
|
||||
- EN_CORRECTTEXT
|
||||
- EN_DROPFILES
|
||||
- EN_ERRSPACE
|
||||
- EN_HSCROLL
|
||||
- EN_IMECHANGE
|
||||
+ EN_KILLFOCUS
|
||||
- EN_LINK
|
||||
- EN_MAXTEXT
|
||||
- EN_MSGFILTER
|
||||
- EN_OLEOPFAILED
|
||||
- EN_PROTECTED
|
||||
- EN_REQUESTRESIZE
|
||||
- EN_SAVECLIPBOARD
|
||||
+ EN_SELCHANGE
|
||||
+ EN_SETFOCUS
|
||||
- EN_STOPNOUNDO
|
||||
* EN_UPDATE (sent from the wrong place)
|
||||
- EN_VSCROLL
|
||||
|
||||
Styles
|
||||
|
||||
- ES_AUTOHSCROLL
|
||||
- ES_AUTOVSCROLL
|
||||
- ES_CENTER
|
||||
- ES_DISABLENOSCROLL (scrollbar is always visible)
|
||||
- ES_EX_NOCALLOLEINIT
|
||||
- ES_LEFT
|
||||
- ES_MULTILINE (currently single line controls aren't supported)
|
||||
- ES_NOIME
|
||||
- ES_READONLY (I'm not sure if beeping is the proper behaviour)
|
||||
- ES_RIGHT
|
||||
- ES_SAVESEL
|
||||
- ES_SELFIME
|
||||
- ES_SUNKEN
|
||||
- ES_VERTICAL
|
||||
- ES_WANTRETURN (don't know how to do WM_GETDLGCODE part)
|
||||
- WS_SETFONT
|
||||
- WS_HSCROLL
|
||||
- WS_VSCROLL
|
||||
*/
|
||||
|
||||
/*
|
||||
* RICHED20 TODO (incomplete):
|
||||
*
|
||||
* - font caching
|
||||
* - add remaining CHARFORMAT/PARAFORMAT fields
|
||||
* - right/center align should strip spaces from the beginning
|
||||
* - more advanced navigation (Ctrl-arrows, PageUp/PageDn)
|
||||
* - tabs
|
||||
* - pictures (not just smiling faces that lack API support ;-) )
|
||||
* - OLE objects
|
||||
* - calculate heights of pictures (half-done)
|
||||
* - EM_STREAMIN/EM_STREAMOUT
|
||||
* - horizontal scrolling (not even started)
|
||||
* - fix scrollbars and refresh (it sucks bigtime)
|
||||
* - hysteresis during wrapping (related to scrollbars appearing/disappearing)
|
||||
* - should remember maximum row width for wrap hysteresis
|
||||
* - find/replace
|
||||
* - how to implement EM_FORMATRANGE and EM_DISPLAYBAND ? (Mission Impossible)
|
||||
* - italic cursor with italic fonts
|
||||
* - IME
|
||||
* - most notifications aren't sent at all (the most important ones are)
|
||||
* - when should EN_SELCHANGE be sent after text change ? (before/after EN_UPDATE?)
|
||||
* - WM_SETTEXT may use wrong style (but I'm 80% sure it's OK)
|
||||
* - EM_GETCHARFORMAT with SCF_SELECTION may not behave 100% like in original (but very close)
|
||||
* - bugs in end-of-text handling (the gray bar) could get me in jail ;-)
|
||||
* - determination of row size
|
||||
* - end-of-paragraph marks should be of reasonable size
|
||||
*
|
||||
* Bugs that are probably fixed, but not so easy to verify:
|
||||
* - EN_UPDATE/EN_CHANGE are handled very incorrectly (should be OK now)
|
||||
* - undo for ME_JoinParagraphs doesn't store paragraph format ? (it does)
|
||||
* - check/fix artificial EOL logic (bCursorAtEnd, hardly logical)
|
||||
* - caret shouldn't be displayed when selection isn't empty
|
||||
* - check refcounting in style management functions (looks perfect now, but no bugs is suspicious)
|
||||
* - undo for setting default format (done, might be buggy)
|
||||
* - styles might be not released properly (looks like they work like charm, but who knows?
|
||||
*
|
||||
*/
|
||||
|
||||
#include "editor.h"
|
||||
#include <ole2.h>
|
||||
#include <richole.h>
|
||||
#include <winreg.h>
|
||||
#define NO_SHLWAPI_STREAM
|
||||
#include <shlwapi.h>
|
||||
|
||||
WINE_DEFAULT_DEBUG_CHANNEL(richedit);
|
||||
|
||||
int me_debug = 0;
|
||||
HANDLE me_heap = NULL;
|
||||
|
||||
void DoWrap(ME_TextEditor *editor) {
|
||||
HDC hDC = GetDC(editor->hWnd);
|
||||
ME_DisplayItem *item;
|
||||
ME_Context c;
|
||||
HWND hWnd = editor->hWnd;
|
||||
int yLength = editor->nTotalLength;
|
||||
int nSelFrom, nSelTo;
|
||||
int nMinSel, nMaxSel;
|
||||
|
||||
ME_GetSelection(editor, &nSelFrom, &nSelTo);
|
||||
|
||||
nMinSel = nSelFrom < editor->nOldSelFrom ? nSelFrom : editor->nOldSelFrom;
|
||||
nMaxSel = nSelTo > editor->nOldSelTo ? nSelTo : editor->nOldSelTo;
|
||||
|
||||
ME_InitContext(&c, editor, hDC);
|
||||
c.pt.x = 0;
|
||||
c.pt.y = 0;
|
||||
item = editor->pBuffer->pFirst->next;
|
||||
while(item != editor->pBuffer->pLast) {
|
||||
int para_from, para_to;
|
||||
BOOL bRedraw = FALSE;
|
||||
|
||||
para_from = item->member.para.nCharOfs;
|
||||
para_to = item->member.para.next_para->member.para.nCharOfs;
|
||||
|
||||
if (para_from <= nMaxSel && para_to >= nMinSel && nMinSel != nMaxSel)
|
||||
bRedraw = TRUE;
|
||||
|
||||
assert(item->type == diParagraph);
|
||||
if (!(item->member.para.nFlags & MEPF_WRAPPED)
|
||||
|| (item->member.para.nYPos != c.pt.y))
|
||||
bRedraw = TRUE;
|
||||
item->member.para.nYPos = c.pt.y;
|
||||
|
||||
ME_WrapTextParagraph(&c, item);
|
||||
|
||||
if (bRedraw) {
|
||||
item->member.para.nFlags |= MEPF_REDRAW;
|
||||
}
|
||||
c.pt.y = item->member.para.nYPos + item->member.para.nHeight;
|
||||
item = item->member.para.next_para;
|
||||
}
|
||||
editor->sizeWindow.cx = c.rcView.right-c.rcView.left;
|
||||
editor->sizeWindow.cy = c.rcView.bottom-c.rcView.top;
|
||||
editor->nTotalLength = c.pt.y-c.rcView.top;
|
||||
|
||||
ME_UpdateScrollBar(editor, -1);
|
||||
ME_EnsureVisible(editor, editor->pCursors[0].pRun);
|
||||
|
||||
/* FIXME this should be marked for update too somehow, so that painting happens in ME_PaintContent */
|
||||
if (yLength != c.pt.y-c.rcView.top) {
|
||||
RECT rc;
|
||||
rc.left = c.rcView.left;
|
||||
rc.right = c.rcView.right;
|
||||
rc.top = c.pt.y;
|
||||
rc.bottom = c.rcView.bottom;
|
||||
InvalidateRect(editor->hWnd, &rc, FALSE);
|
||||
UpdateWindow(editor->hWnd);
|
||||
}
|
||||
|
||||
editor->nOldSelFrom = nSelFrom;
|
||||
editor->nOldSelTo = nSelTo;
|
||||
/* PatBlt(hDC, 0, c.pt.y, c.rcView.right, c.rcView.bottom, BLACKNESS);*/
|
||||
|
||||
ME_DestroyContext(&c);
|
||||
ReleaseDC(hWnd, hDC);
|
||||
}
|
||||
|
||||
ME_TextBuffer *ME_MakeText() {
|
||||
|
||||
ME_TextBuffer *buf = ALLOC_OBJ(ME_TextBuffer);
|
||||
|
||||
ME_DisplayItem *p1 = ME_MakeDI(diTextStart);
|
||||
ME_DisplayItem *p2 = ME_MakeDI(diTextEnd);
|
||||
|
||||
p1->prev = NULL;
|
||||
p1->next = p2;
|
||||
p2->prev = p1;
|
||||
p2->next = NULL;
|
||||
p1->member.para.next_para = p2;
|
||||
p2->member.para.prev_para = p1;
|
||||
p2->member.para.nCharOfs = 0;
|
||||
|
||||
buf->pFirst = p1;
|
||||
buf->pLast = p2;
|
||||
buf->pCharStyle = NULL;
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
ME_TextEditor *ME_MakeEditor(HWND hWnd) {
|
||||
ME_TextEditor *ed = ALLOC_OBJ(ME_TextEditor);
|
||||
HDC hDC;
|
||||
ed->hWnd = hWnd;
|
||||
ed->pBuffer = ME_MakeText();
|
||||
hDC = GetDC(hWnd);
|
||||
ME_MakeFirstParagraph(hDC, ed->pBuffer);
|
||||
ReleaseDC(hWnd, hDC);
|
||||
ed->bCaretShown = FALSE;
|
||||
ed->nCursors = 3;
|
||||
ed->pCursors = ALLOC_N_OBJ(ME_Cursor, ed->nCursors);
|
||||
ed->pCursors[0].pRun = ME_FindItemFwd(ed->pBuffer->pFirst, diRun);
|
||||
ed->pCursors[0].nOffset = 0;
|
||||
ed->pCursors[1].pRun = ME_FindItemFwd(ed->pBuffer->pFirst, diRun);
|
||||
ed->pCursors[1].nOffset = 0;
|
||||
ed->nTotalLength = 0;
|
||||
ed->nScrollPos = 0;
|
||||
ed->nUDArrowX = -1;
|
||||
ed->nSequence = 0;
|
||||
ed->rgbBackColor = -1;
|
||||
ed->bCaretAtEnd = FALSE;
|
||||
ed->nEventMask = 0;
|
||||
ed->nModifyStep = 0;
|
||||
ed->pUndoStack = ed->pRedoStack = NULL;
|
||||
ed->nUndoMode = umAddToUndo;
|
||||
ed->nParagraphs = 1;
|
||||
ME_CheckCharOffsets(ed);
|
||||
return ed;
|
||||
}
|
||||
|
||||
void ME_DestroyEditor(ME_TextEditor *editor)
|
||||
{
|
||||
ME_DisplayItem *pFirst = editor->pBuffer->pFirst;
|
||||
ME_DisplayItem *p = pFirst, *pNext = NULL;
|
||||
|
||||
ME_ClearTempStyle(editor);
|
||||
ME_EmptyUndoStack(editor);
|
||||
while(p) {
|
||||
pNext = p->next;
|
||||
ME_DestroyDisplayItem(p);
|
||||
p = pNext;
|
||||
}
|
||||
ME_ReleaseStyle(editor->pBuffer->pDefaultStyle);
|
||||
|
||||
FREE_OBJ(editor);
|
||||
}
|
||||
|
||||
#define UNSUPPORTED_MSG(e) \
|
||||
case e: \
|
||||
FIXME(#e ": stub\n"); \
|
||||
return DefWindowProcW(hWnd, msg, wParam, lParam);
|
||||
|
||||
/******************************************************************
|
||||
* RichEditANSIWndProc (RICHED20.10)
|
||||
*/
|
||||
LRESULT WINAPI RichEditANSIWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) {
|
||||
HDC hDC;
|
||||
PAINTSTRUCT ps;
|
||||
SCROLLINFO si;
|
||||
ME_TextEditor *editor = (ME_TextEditor *)GetWindowLongW(hWnd, 0);
|
||||
switch(msg) {
|
||||
|
||||
UNSUPPORTED_MSG(EM_AUTOURLDETECT)
|
||||
UNSUPPORTED_MSG(EM_CANPASTE)
|
||||
UNSUPPORTED_MSG(EM_CHARFROMPOS)
|
||||
UNSUPPORTED_MSG(EM_DISPLAYBAND)
|
||||
UNSUPPORTED_MSG(EM_EXLIMITTEXT)
|
||||
UNSUPPORTED_MSG(EM_EXLINEFROMCHAR)
|
||||
UNSUPPORTED_MSG(EM_FINDTEXT)
|
||||
UNSUPPORTED_MSG(EM_FINDTEXTEX)
|
||||
UNSUPPORTED_MSG(EM_FINDWORDBREAK)
|
||||
UNSUPPORTED_MSG(EM_FMTLINES)
|
||||
UNSUPPORTED_MSG(EM_FORMATRANGE)
|
||||
UNSUPPORTED_MSG(EM_GETFIRSTVISIBLELINE)
|
||||
UNSUPPORTED_MSG(EM_GETIMECOMPMODE)
|
||||
/* UNSUPPORTED_MSG(EM_GETIMESTATUS) missing in Wine headers */
|
||||
UNSUPPORTED_MSG(EM_GETLANGOPTIONS)
|
||||
UNSUPPORTED_MSG(EM_GETLIMITTEXT)
|
||||
UNSUPPORTED_MSG(EM_GETLINE)
|
||||
UNSUPPORTED_MSG(EM_GETLINECOUNT)
|
||||
/* UNSUPPORTED_MSG(EM_GETOLEINTERFACE) separate stub */
|
||||
UNSUPPORTED_MSG(EM_GETOPTIONS)
|
||||
UNSUPPORTED_MSG(EM_GETRECT)
|
||||
UNSUPPORTED_MSG(EM_GETREDONAME)
|
||||
UNSUPPORTED_MSG(EM_GETTEXTMODE)
|
||||
UNSUPPORTED_MSG(EM_GETUNDONAME)
|
||||
UNSUPPORTED_MSG(EM_GETWORDBREAKPROC)
|
||||
UNSUPPORTED_MSG(EM_GETWORDBREAKPROCEX)
|
||||
UNSUPPORTED_MSG(EM_HIDESELECTION)
|
||||
UNSUPPORTED_MSG(EM_LIMITTEXT) /* also known as EM_SETLIMITTEXT */
|
||||
UNSUPPORTED_MSG(EM_LINEFROMCHAR)
|
||||
UNSUPPORTED_MSG(EM_LINEINDEX)
|
||||
UNSUPPORTED_MSG(EM_LINELENGTH)
|
||||
UNSUPPORTED_MSG(EM_LINESCROLL)
|
||||
UNSUPPORTED_MSG(EM_PASTESPECIAL)
|
||||
/* UNSUPPORTED_MSG(EM_POSFROMCHARS) missing in Wine headers */
|
||||
UNSUPPORTED_MSG(EM_REQUESTRESIZE)
|
||||
UNSUPPORTED_MSG(EM_SCROLL)
|
||||
UNSUPPORTED_MSG(EM_SCROLLCARET)
|
||||
UNSUPPORTED_MSG(EM_SELECTIONTYPE)
|
||||
UNSUPPORTED_MSG(EM_SETLANGOPTIONS)
|
||||
UNSUPPORTED_MSG(EM_SETOLECALLBACK)
|
||||
UNSUPPORTED_MSG(EM_SETOPTIONS)
|
||||
UNSUPPORTED_MSG(EM_SETRECT)
|
||||
UNSUPPORTED_MSG(EM_SETRECTNP)
|
||||
UNSUPPORTED_MSG(EM_SETTARGETDEVICE)
|
||||
UNSUPPORTED_MSG(EM_SETTEXTMODE)
|
||||
UNSUPPORTED_MSG(EM_SETUNDOLIMIT)
|
||||
UNSUPPORTED_MSG(EM_SETWORDBREAKPROC)
|
||||
UNSUPPORTED_MSG(EM_SETWORDBREAKPROCEX)
|
||||
UNSUPPORTED_MSG(EM_STREAMIN)
|
||||
UNSUPPORTED_MSG(EM_STREAMOUT)
|
||||
UNSUPPORTED_MSG(WM_SETFONT)
|
||||
UNSUPPORTED_MSG(WM_PASTE)
|
||||
UNSUPPORTED_MSG(WM_STYLECHANGING)
|
||||
UNSUPPORTED_MSG(WM_STYLECHANGED)
|
||||
/* UNSUPPORTED_MSG(WM_UNICHAR) FIXME missing in Wine headers */
|
||||
|
||||
|
||||
case WM_GETDLGCODE:
|
||||
{
|
||||
UINT code = DLGC_WANTCHARS|DLGC_WANTARROWS;
|
||||
if (GetWindowLongW(hWnd, GWL_STYLE)&ES_WANTRETURN)
|
||||
code |= 0; /* FIXME what can we do here ? ask for messages and censor them ? */
|
||||
return code;
|
||||
}
|
||||
case WM_NCCREATE:
|
||||
{
|
||||
CREATESTRUCTW *pcs = (CREATESTRUCTW *)lParam;
|
||||
editor = ME_MakeEditor(hWnd);
|
||||
SetWindowLongW(hWnd, 0, (long)editor);
|
||||
pcs = 0; /* ignore */
|
||||
return TRUE;
|
||||
}
|
||||
case EM_EMPTYUNDOBUFFER:
|
||||
ME_EmptyUndoStack(editor);
|
||||
return 0;
|
||||
case EM_GETSEL:
|
||||
{
|
||||
ME_GetSelection(editor, (int *)wParam, (int *)lParam);
|
||||
if (!((wParam|lParam) & 0xFFFF0000))
|
||||
return (lParam<<16)|wParam;
|
||||
return -1;
|
||||
}
|
||||
case EM_EXGETSEL:
|
||||
{
|
||||
CHARRANGE *pRange = (CHARRANGE *)lParam;
|
||||
ME_GetSelection(editor, (int *)&pRange->cpMin, (int *)&pRange->cpMax);
|
||||
return 0;
|
||||
}
|
||||
case EM_CANUNDO:
|
||||
return editor->pUndoStack != NULL;
|
||||
case EM_CANREDO:
|
||||
return editor->pRedoStack != NULL;
|
||||
case EM_UNDO:
|
||||
ME_Undo(editor);
|
||||
return 0;
|
||||
case EM_REDO:
|
||||
ME_Redo(editor);
|
||||
return 0;
|
||||
case EM_SETSEL:
|
||||
{
|
||||
ME_SetSelection(editor, wParam, lParam);
|
||||
ME_Repaint(editor);
|
||||
ME_SendSelChange(editor);
|
||||
return 0;
|
||||
}
|
||||
case EM_EXSETSEL:
|
||||
{
|
||||
CHARRANGE *pRange = (CHARRANGE *)lParam;
|
||||
ME_SetSelection(editor, pRange->cpMin, pRange->cpMax);
|
||||
/* FIXME optimize */
|
||||
ME_Repaint(editor);
|
||||
ME_SendSelChange(editor);
|
||||
return 0;
|
||||
}
|
||||
case EM_SETBKGNDCOLOR:
|
||||
{
|
||||
LRESULT lColor = ME_GetBackColor(editor);
|
||||
if (wParam)
|
||||
editor->rgbBackColor = -1;
|
||||
else
|
||||
editor->rgbBackColor = lParam;
|
||||
InvalidateRect(hWnd, NULL, TRUE);
|
||||
UpdateWindow(hWnd);
|
||||
return lColor;
|
||||
}
|
||||
case EM_GETMODIFY:
|
||||
return editor->nModifyStep == 0 ? 0 : 1;
|
||||
case EM_SETMODIFY:
|
||||
{
|
||||
if (wParam)
|
||||
editor->nModifyStep = 0x80000000;
|
||||
else
|
||||
editor->nModifyStep = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
case EM_SETREADONLY:
|
||||
{
|
||||
long nStyle = GetWindowLongW(hWnd, GWL_STYLE);
|
||||
if (wParam)
|
||||
nStyle |= ES_READONLY;
|
||||
else
|
||||
nStyle &= ~ES_READONLY;
|
||||
SetWindowLongW(hWnd, GWL_STYLE, nStyle);
|
||||
ME_Repaint(editor);
|
||||
return 0;
|
||||
}
|
||||
case EM_SETEVENTMASK:
|
||||
editor->nEventMask = lParam;
|
||||
return 0;
|
||||
case EM_GETEVENTMASK:
|
||||
return editor->nEventMask;
|
||||
case EM_SETCHARFORMAT:
|
||||
{
|
||||
CHARFORMAT2W buf, *p;
|
||||
p = ME_ToCF2W(&buf, (CHARFORMAT2W *)lParam);
|
||||
if (!wParam)
|
||||
ME_SetDefaultCharFormat(editor, p);
|
||||
else if (wParam == (SCF_WORD | SCF_SELECTION))
|
||||
FIXME("word selection not supported\n");
|
||||
else if (wParam == SCF_ALL)
|
||||
ME_SetCharFormat(editor, 0, ME_GetTextLength(editor), p);
|
||||
else
|
||||
ME_SetSelectionCharFormat(editor, p);
|
||||
ME_CommitUndo(editor);
|
||||
ME_UpdateRepaint(editor);
|
||||
return 0;
|
||||
}
|
||||
case EM_GETCHARFORMAT:
|
||||
{
|
||||
CHARFORMAT2W tmp;
|
||||
tmp.cbSize = sizeof(tmp);
|
||||
if (!wParam)
|
||||
ME_GetDefaultCharFormat(editor, &tmp);
|
||||
else
|
||||
ME_GetSelectionCharFormat(editor, &tmp);
|
||||
ME_CopyToCFAny((CHARFORMAT2W *)lParam, &tmp);
|
||||
return 0;
|
||||
}
|
||||
case EM_SETPARAFORMAT:
|
||||
ME_SetSelectionParaFormat(editor, (PARAFORMAT2 *)lParam);
|
||||
ME_CommitUndo(editor);
|
||||
return 0;
|
||||
case EM_GETPARAFORMAT:
|
||||
ME_GetSelectionParaFormat(editor, (PARAFORMAT2 *)lParam);
|
||||
return 0;
|
||||
case WM_CLEAR:
|
||||
{
|
||||
int from, to;
|
||||
ME_GetSelection(editor, &from, &to);
|
||||
ME_InternalDeleteText(editor, from, to-from);
|
||||
ME_CommitUndo(editor);
|
||||
ME_UpdateRepaint(editor);
|
||||
return 0;
|
||||
}
|
||||
case EM_REPLACESEL:
|
||||
{
|
||||
int from, to;
|
||||
ME_Cursor c;
|
||||
ME_Style *style;
|
||||
LPWSTR wszText = ME_ToUnicode(hWnd, (void *)lParam);
|
||||
size_t len = lstrlenW(wszText);
|
||||
|
||||
ME_GetSelection(editor, &from, &to);
|
||||
ME_CursorFromCharOfs(editor, from, &c);
|
||||
if (from != to) {
|
||||
style = c.pRun->member.run.style;
|
||||
ME_AddRefStyle(style); /* ME_GetInsertStyle has already done that */
|
||||
}
|
||||
else
|
||||
style = ME_GetInsertStyle(editor, 0);
|
||||
ME_InternalDeleteText(editor, from, to-from);
|
||||
ME_InsertTextFromCursor(editor, 0, wszText, len, style);
|
||||
ME_ReleaseStyle(style);
|
||||
ME_EndToUnicode(hWnd, wszText);
|
||||
/* drop temporary style if line end */
|
||||
/* FIXME question: does abc\n mean: put abc, clear temp style, put \n? (would require a change) */
|
||||
if (len>0 && wszText[len-1] == '\n')
|
||||
ME_ClearTempStyle(editor);
|
||||
|
||||
ME_CommitUndo(editor);
|
||||
if (!wParam)
|
||||
ME_EmptyUndoStack(editor);
|
||||
ME_UpdateRepaint(editor);
|
||||
return 0;
|
||||
}
|
||||
case WM_SETTEXT:
|
||||
{
|
||||
LPWSTR wszText = ME_ToUnicode(hWnd, (void *)lParam);
|
||||
ME_InternalDeleteText(editor, 0, ME_GetTextLength(editor));
|
||||
/* uses default style! */
|
||||
ME_InsertTextFromCursor(editor, 0, wszText, -1, editor->pBuffer->pDefaultStyle);
|
||||
ME_EndToUnicode(hWnd, wszText);
|
||||
ME_CommitUndo(editor);
|
||||
ME_EmptyUndoStack(editor);
|
||||
ME_UpdateRepaint(editor);
|
||||
return 0;
|
||||
}
|
||||
case WM_CUT:
|
||||
case WM_COPY:
|
||||
{
|
||||
int from, to, pars;
|
||||
WCHAR *data;
|
||||
HANDLE hData;
|
||||
|
||||
if (!OpenClipboard(hWnd))
|
||||
return 0;
|
||||
|
||||
EmptyClipboard();
|
||||
ME_GetSelection(editor, &from, &to);
|
||||
pars = ME_CountParagraphsBetween(editor, from, to);
|
||||
hData = GlobalAlloc(GMEM_MOVEABLE, sizeof(WCHAR)*(to-from+pars+1));
|
||||
data = (WCHAR *)GlobalLock(hData);
|
||||
ME_GetTextW(editor, data, from, to-from, TRUE);
|
||||
GlobalUnlock(hData);
|
||||
SetClipboardData(CF_UNICODETEXT, hData);
|
||||
CloseClipboard();
|
||||
if (msg == WM_CUT)
|
||||
{
|
||||
ME_InternalDeleteText(editor, from, to-from);
|
||||
ME_CommitUndo(editor);
|
||||
ME_UpdateRepaint(editor);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
case WM_GETTEXTLENGTH:
|
||||
return ME_GetTextLength(editor);
|
||||
case WM_GETTEXT:
|
||||
{
|
||||
TEXTRANGEW tr; /* W and A differ only by rng->lpstrText */
|
||||
tr.chrg.cpMin = 0;
|
||||
tr.chrg.cpMax = wParam-1;
|
||||
tr.lpstrText = (WCHAR *)lParam;
|
||||
return RichEditANSIWndProc(hWnd, EM_GETTEXTRANGE, 0, (LPARAM)&tr);
|
||||
}
|
||||
case EM_GETSELTEXT:
|
||||
{
|
||||
int from, to;
|
||||
TEXTRANGEW tr; /* W and A differ only by rng->lpstrText */
|
||||
ME_GetSelection(editor, &from, &to);
|
||||
tr.chrg.cpMin = from;
|
||||
tr.chrg.cpMax = to;
|
||||
tr.lpstrText = (WCHAR *)lParam;
|
||||
return RichEditANSIWndProc(hWnd, EM_GETTEXTRANGE, 0, (LPARAM)&tr);
|
||||
}
|
||||
case EM_GETTEXTRANGE:
|
||||
{
|
||||
TEXTRANGEW *rng = (TEXTRANGEW *)lParam;
|
||||
if (IsWindowUnicode(hWnd))
|
||||
return ME_GetTextW(editor, rng->lpstrText, rng->chrg.cpMin, rng->chrg.cpMax-rng->chrg.cpMin, FALSE);
|
||||
else
|
||||
{
|
||||
int nLen = rng->chrg.cpMax-rng->chrg.cpMin;
|
||||
WCHAR *p = ALLOC_N_OBJ(WCHAR, nLen+1);
|
||||
int nChars = ME_GetTextW(editor, p, rng->chrg.cpMin, nLen, FALSE);
|
||||
/* FIXME this is a potential security hole (buffer overrun)
|
||||
if you know more about wchar->mbyte conversion please explain
|
||||
*/
|
||||
WideCharToMultiByte(CP_ACP, 0, p, nChars+1, (char *)rng->lpstrText, nLen+1, NULL, NULL);
|
||||
FREE_OBJ(p);
|
||||
return nChars;
|
||||
}
|
||||
return ME_GetTextW(editor, rng->lpstrText, rng->chrg.cpMin, rng->chrg.cpMax-rng->chrg.cpMin, FALSE);
|
||||
}
|
||||
case WM_CREATE:
|
||||
ME_CommitUndo(editor);
|
||||
/* ME_InsertTextFromCursor(editor, 0, (WCHAR *)L"x", 1, editor->pBuffer->pDefaultStyle); */
|
||||
DoWrap(editor);
|
||||
ME_MoveCaret(editor);
|
||||
return 0;
|
||||
case WM_DESTROY:
|
||||
ME_DestroyEditor(editor);
|
||||
SetWindowLongW(hWnd, 0, 0);
|
||||
PostQuitMessage(0);
|
||||
break;
|
||||
case WM_LBUTTONDOWN:
|
||||
SetFocus(hWnd);
|
||||
ME_LButtonDown(editor, (short)LOWORD(lParam), (short)HIWORD(lParam));
|
||||
SetCapture(hWnd);
|
||||
break;
|
||||
case WM_MOUSEMOVE:
|
||||
if (GetCapture() == hWnd)
|
||||
ME_MouseMove(editor, (short)LOWORD(lParam), (short)HIWORD(lParam));
|
||||
break;
|
||||
case WM_LBUTTONUP:
|
||||
if (GetCapture() == hWnd)
|
||||
ReleaseCapture();
|
||||
break;
|
||||
case WM_PAINT:
|
||||
hDC = BeginPaint(hWnd, &ps);
|
||||
ME_PaintContent(editor, hDC, FALSE);
|
||||
EndPaint(hWnd, &ps);
|
||||
break;
|
||||
case WM_SETFOCUS:
|
||||
ME_ShowCaret(editor);
|
||||
ME_SendOldNotify(editor, EN_SETFOCUS);
|
||||
return 0;
|
||||
case WM_KILLFOCUS:
|
||||
ME_HideCaret(editor);
|
||||
ME_SendOldNotify(editor, EN_KILLFOCUS);
|
||||
return 0;
|
||||
case WM_ERASEBKGND:
|
||||
{
|
||||
HDC hDC = (HDC)wParam;
|
||||
RECT rc;
|
||||
COLORREF rgbBG = ME_GetBackColor(editor);
|
||||
if (GetUpdateRect(hWnd,&rc,TRUE))
|
||||
{
|
||||
HBRUSH hbr = CreateSolidBrush(rgbBG);
|
||||
FillRect(hDC, &rc, hbr);
|
||||
DeleteObject(hbr);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
case WM_COMMAND:
|
||||
TRACE("editor wnd command = %d\n", LOWORD(wParam));
|
||||
return 0;
|
||||
case WM_KEYDOWN:
|
||||
if (ME_ArrowKey(editor, LOWORD(wParam), GetKeyState(VK_CONTROL)<0)) {
|
||||
ME_CommitUndo(editor);
|
||||
ME_EnsureVisible(editor, editor->pCursors[0].pRun);
|
||||
HideCaret(hWnd);
|
||||
ME_MoveCaret(editor);
|
||||
ShowCaret(hWnd);
|
||||
return 0;
|
||||
}
|
||||
if (GetKeyState(VK_CONTROL)<0)
|
||||
{
|
||||
if (LOWORD(wParam)=='W')
|
||||
{
|
||||
CHARFORMAT2W chf;
|
||||
char buf[2048];
|
||||
ME_GetSelectionCharFormat(editor, &chf);
|
||||
ME_DumpStyleToBuf(&chf, buf);
|
||||
MessageBoxA(NULL, buf, "Style dump", MB_OK);
|
||||
}
|
||||
if (LOWORD(wParam)=='Q')
|
||||
{
|
||||
ME_CheckCharOffsets(editor);
|
||||
}
|
||||
}
|
||||
goto do_default;
|
||||
case WM_CHAR:
|
||||
{
|
||||
WCHAR wstr;
|
||||
if (GetWindowLongW(editor->hWnd, GWL_STYLE) & ES_READONLY) {
|
||||
MessageBeep(MB_ICONERROR);
|
||||
return 0; /* FIXME really 0 ? */
|
||||
}
|
||||
wstr = LOWORD(wParam);
|
||||
if (((unsigned)wstr)>=' ' || wstr=='\r') {
|
||||
/* FIXME maybe it would make sense to call EM_REPLACESEL instead ? */
|
||||
ME_Style *style = ME_GetInsertStyle(editor, 0);
|
||||
ME_SaveTempStyle(editor);
|
||||
ME_InsertTextFromCursor(editor, 0, &wstr, 1, style);
|
||||
ME_ReleaseStyle(style);
|
||||
ME_CommitUndo(editor);
|
||||
ME_UpdateRepaint(editor);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
case WM_VSCROLL:
|
||||
{
|
||||
si.cbSize = sizeof(SCROLLINFO);
|
||||
si.fMask = SIF_PAGE|SIF_POS|SIF_RANGE|SIF_TRACKPOS;
|
||||
GetScrollInfo(hWnd, SB_VERT, &si);
|
||||
switch(LOWORD(wParam)) {
|
||||
case SB_THUMBTRACK:
|
||||
SetScrollPos(hWnd, SB_VERT, si.nTrackPos, FALSE);
|
||||
ScrollWindow(hWnd, 0, si.nPos-si.nTrackPos, NULL, NULL);
|
||||
/* InvalidateRect(hWnd, NULL, TRUE); */
|
||||
UpdateWindow(hWnd);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case WM_SIZE:
|
||||
{
|
||||
ME_DisplayItem *tp = editor->pBuffer->pFirst;
|
||||
while(tp)
|
||||
{
|
||||
if (tp->type == diParagraph)
|
||||
{
|
||||
tp->member.para.nFlags &= ~MEPF_WRAPPED;
|
||||
tp = tp->member.para.next_para;
|
||||
}
|
||||
else
|
||||
tp = tp->next;
|
||||
}
|
||||
ME_Repaint(editor);
|
||||
return DefWindowProcW(hWnd, msg, wParam, lParam);
|
||||
}
|
||||
case EM_GETOLEINTERFACE:
|
||||
FIXME("EM_GETOLEINTERFACE: stub\n");
|
||||
/* return 0x12345678; to find out if the missing interface is
|
||||
related to app crash */
|
||||
return 0;
|
||||
default:
|
||||
do_default:
|
||||
return DefWindowProcW(hWnd, msg, wParam, lParam);
|
||||
}
|
||||
return 0L;
|
||||
}
|
||||
|
||||
/******************************************************************
|
||||
* RichEdit10ANSIWndProc (RICHED20.9)
|
||||
*/
|
||||
LRESULT WINAPI RichEdit10ANSIWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
/* FIXME: this is NOT the same as 2.0 version */
|
||||
return RichEditANSIWndProc(hWnd, msg, wParam, lParam);
|
||||
}
|
||||
|
||||
void ME_SendOldNotify(ME_TextEditor *editor, int nCode)
|
||||
{
|
||||
HWND hWnd = editor->hWnd;
|
||||
SendMessageA(GetParent(hWnd), WM_COMMAND, (nCode<<16)|GetWindowLongW(hWnd, GWLP_ID), (LPARAM)hWnd);
|
||||
}
|
||||
|
||||
int ME_CountParagraphsBetween(ME_TextEditor *editor, int from, int to)
|
||||
{
|
||||
ME_DisplayItem *item = ME_FindItemFwd(editor->pBuffer->pFirst, diParagraph);
|
||||
int i = 0;
|
||||
|
||||
while(item && item->member.para.next_para->member.para.nCharOfs <= from)
|
||||
item = item->member.para.next_para;
|
||||
if (!item)
|
||||
return 0;
|
||||
while(item && item->member.para.next_para->member.para.nCharOfs <= to) {
|
||||
item = item->member.para.next_para;
|
||||
i++;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
int ME_GetTextW(ME_TextEditor *editor, WCHAR *buffer, int nStart, int nChars, int bCRLF)
|
||||
{
|
||||
ME_DisplayItem *item = ME_FindItemFwd(editor->pBuffer->pFirst, diParagraph);
|
||||
int nWritten = 0;
|
||||
|
||||
while(item && item->member.para.next_para->member.para.nCharOfs <= nStart)
|
||||
item = ME_FindItemFwd(item, diParagraph);
|
||||
if (!item) {
|
||||
*buffer = L'\0';
|
||||
return 0;
|
||||
}
|
||||
nStart -= item->member.para.nCharOfs;
|
||||
|
||||
do {
|
||||
item = ME_FindItemFwd(item, diRun);
|
||||
} while(item && (item->member.run.nCharOfs + ME_StrLen(item->member.run.strText) <= nStart));
|
||||
assert(item);
|
||||
|
||||
nStart -= item->member.run.nCharOfs;
|
||||
|
||||
if (nStart)
|
||||
{
|
||||
int nLen = ME_StrLen(item->member.run.strText) - nStart;
|
||||
if (nLen > nChars)
|
||||
nLen = nChars;
|
||||
CopyMemory(buffer, item->member.run.strText->szData + nStart, sizeof(WCHAR)*nLen);
|
||||
nChars -= nLen;
|
||||
nWritten += nLen;
|
||||
if (!nChars)
|
||||
return nWritten;
|
||||
buffer += nLen;
|
||||
nStart = 0;
|
||||
item = ME_FindItemFwd(item, diRun);
|
||||
}
|
||||
|
||||
while(nChars && item)
|
||||
{
|
||||
int nLen = ME_StrLen(item->member.run.strText);
|
||||
if (nLen > nChars)
|
||||
nLen = nChars;
|
||||
|
||||
if (item->member.run.nFlags & MERF_ENDPARA)
|
||||
{
|
||||
if (bCRLF) {
|
||||
*buffer++ = '\r';
|
||||
nWritten++;
|
||||
}
|
||||
*buffer = '\n';
|
||||
assert(nLen == 1);
|
||||
}
|
||||
else
|
||||
CopyMemory(buffer, item->member.run.strText->szData, sizeof(WCHAR)*nLen);
|
||||
nChars -= nLen;
|
||||
nWritten += nLen;
|
||||
buffer += nLen;
|
||||
|
||||
if (!nChars)
|
||||
{
|
||||
*buffer = L'\0';
|
||||
return nWritten;
|
||||
}
|
||||
item = ME_FindItemFwd(item, diRun);
|
||||
}
|
||||
*buffer = L'\0';
|
||||
return nWritten;
|
||||
}
|
||||
|
||||
static WCHAR wszClassName[] = {'R', 'i', 'c', 'h', 'E', 'd', 'i', 't', '2', '0', 'W', 0};
|
||||
static WCHAR wszClassName50[] = {'R', 'i', 'c', 'h', 'E', 'd', 'i', 't', '5', '0', 'W', 0};
|
||||
|
||||
void ME_RegisterEditorClass(HINSTANCE hInstance)
|
||||
{
|
||||
BOOL bResult;
|
||||
WNDCLASSW wcW;
|
||||
WNDCLASSA wcA;
|
||||
|
||||
wcW.style = CS_HREDRAW | CS_VREDRAW;
|
||||
wcW.lpfnWndProc = RichEditANSIWndProc;
|
||||
wcW.cbClsExtra = 0;
|
||||
wcW.cbWndExtra = 4;
|
||||
wcW.hInstance = NULL; /* hInstance would register DLL-local class */
|
||||
wcW.hIcon = NULL;
|
||||
wcW.hCursor = LoadCursorW(NULL, MAKEINTRESOURCEW(IDC_IBEAM));
|
||||
wcW.hbrBackground = (HBRUSH)GetStockObject(NULL_BRUSH);
|
||||
wcW.lpszMenuName = NULL;
|
||||
wcW.lpszClassName = wszClassName;
|
||||
bResult = RegisterClassW(&wcW);
|
||||
assert(bResult);
|
||||
wcW.lpszClassName = wszClassName50;
|
||||
bResult = RegisterClassW(&wcW);
|
||||
assert(bResult);
|
||||
|
||||
wcA.style = CS_HREDRAW | CS_VREDRAW;
|
||||
wcA.lpfnWndProc = RichEditANSIWndProc;
|
||||
wcA.cbClsExtra = 0;
|
||||
wcA.cbWndExtra = 4;
|
||||
wcA.hInstance = NULL; /* hInstance would register DLL-local class */
|
||||
wcA.hIcon = NULL;
|
||||
wcA.hCursor = LoadCursorW(NULL, MAKEINTRESOURCEW(IDC_IBEAM));
|
||||
wcA.hbrBackground = (HBRUSH)GetStockObject(NULL_BRUSH);
|
||||
wcA.lpszMenuName = NULL;
|
||||
wcA.lpszClassName = "RichEdit20A";
|
||||
bResult = RegisterClassA(&wcA);
|
||||
assert(bResult);
|
||||
wcA.lpszClassName = "RichEdit50A";
|
||||
bResult = RegisterClassA(&wcA);
|
||||
assert(bResult);
|
||||
}
|
||||
|
||||
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
|
||||
{
|
||||
TRACE("\n");
|
||||
switch (fdwReason)
|
||||
{
|
||||
case DLL_PROCESS_ATTACH:
|
||||
DisableThreadLibraryCalls(hinstDLL);
|
||||
me_heap = HeapCreate (0, 0x10000, 0);
|
||||
ME_RegisterEditorClass(hinstDLL);
|
||||
break;
|
||||
|
||||
case DLL_PROCESS_DETACH:
|
||||
UnregisterClassW(wszClassName, hinstDLL);
|
||||
UnregisterClassA("RichEdit20A", hinstDLL);
|
||||
HeapDestroy (me_heap);
|
||||
me_heap = NULL;
|
||||
break;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/******************************************************************
|
||||
* CreateTextServices (RICHED20.4)
|
||||
*
|
||||
* FIXME should be ITextHost instead of void*
|
||||
*/
|
||||
HRESULT WINAPI CreateTextServices(IUnknown *punkOuter, void *pITextHost,
|
||||
IUnknown **ppUnk)
|
||||
{
|
||||
FIXME("stub\n");
|
||||
/* FIXME should support aggregation */
|
||||
if (punkOuter)
|
||||
return CLASS_E_NOAGGREGATION;
|
||||
|
||||
return E_FAIL; /* E_NOTIMPL isn't allowed by MSDN */
|
||||
}
|
||||
|
||||
/******************************************************************
|
||||
* REExtendedRegisterClass (RICHED20.8)
|
||||
*
|
||||
* FIXME undocumented
|
||||
*/
|
||||
void WINAPI REExtendedRegisterClass(void)
|
||||
{
|
||||
FIXME("stub\n");
|
||||
}
|
|
@ -0,0 +1,209 @@
|
|||
/*
|
||||
* RichEdit - prototypes for functions and macro definitions
|
||||
*
|
||||
* Copyright 2004 by Krzysztof Foltman
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "editstr.h"
|
||||
|
||||
#define ALLOC_OBJ(type) (type *)HeapAlloc(me_heap, 0, sizeof(type))
|
||||
#define ALLOC_N_OBJ(type, count) (type *)HeapAlloc(me_heap, 0, count*sizeof(type))
|
||||
#define FREE_OBJ(ptr) HeapFree(me_heap, 0, ptr)
|
||||
|
||||
/* style.c */
|
||||
ME_Style *ME_MakeStyle(CHARFORMAT2W *style);
|
||||
void ME_AddRefStyle(ME_Style *item);
|
||||
void ME_ReleaseStyle(ME_Style *item);
|
||||
ME_Style *ME_GetInsertStyle(ME_TextEditor *editor, int nCursor);
|
||||
ME_Style *ME_ApplyStyle(ME_Style *sSrc, CHARFORMAT2W *style);
|
||||
void ME_PrepareStyle(ME_Context *c, ME_Style *s);
|
||||
void ME_PrepareStyleFromDC(ME_Style *s, HDC hDC, int nSequence);
|
||||
void ME_UnprepareStyle(ME_Style *s);
|
||||
HFONT ME_SelectStyleFont(HDC hDC, ME_Style *s);
|
||||
void ME_InitCharFormat2W(CHARFORMAT2W *pFmt);
|
||||
void ME_SaveTempStyle(ME_TextEditor *editor);
|
||||
void ME_ClearTempStyle(ME_TextEditor *editor);
|
||||
void ME_DumpStyleToBuf(CHARFORMAT2W *pFmt, char buf[2048]);
|
||||
void ME_DumpStyle(ME_Style *s);
|
||||
CHARFORMAT2W *ME_ToCF2W(CHARFORMAT2W *to, CHARFORMAT2W *from);
|
||||
void ME_CopyToCF2W(CHARFORMAT2W *to, CHARFORMAT2W *from);
|
||||
CHARFORMAT2W *ME_ToCFAny(CHARFORMAT2W *to, CHARFORMAT2W *from);
|
||||
void ME_CopyToCFAny(CHARFORMAT2W *to, CHARFORMAT2W *from);
|
||||
void ME_CopyCharFormat(CHARFORMAT2W *pDest, CHARFORMAT2W *pSrc); /* only works with 2W structs */
|
||||
|
||||
/* list.c */
|
||||
void ME_InsertBefore(ME_DisplayItem *diWhere, ME_DisplayItem *diWhat);
|
||||
void ME_Remove(ME_DisplayItem *diWhere);
|
||||
ME_DisplayItem *ME_FindItemBack(ME_DisplayItem *di, ME_DIType nTypeOrClass);
|
||||
ME_DisplayItem *ME_FindItemFwd(ME_DisplayItem *di, ME_DIType nTypeOrClass);
|
||||
ME_DisplayItem *ME_FindItemBackOrHere(ME_DisplayItem *di, ME_DIType nTypeOrClass);
|
||||
ME_DisplayItem *ME_FindItemFwdOrHere(ME_DisplayItem *di, ME_DIType nTypeOrClass);
|
||||
BOOL ME_DITypesEqual(ME_DIType type, ME_DIType nTypeOrClass);
|
||||
ME_DisplayItem *ME_MakeDI(ME_DIType type);
|
||||
void ME_DestroyDisplayItem(ME_DisplayItem *item);
|
||||
void ME_DumpDocument(ME_TextBuffer *buffer);
|
||||
const char *ME_GetDITypeName(ME_DIType type);
|
||||
|
||||
/* string.c */
|
||||
int ME_GetOptimalBuffer(int nLen);
|
||||
ME_String *ME_MakeString(LPCWSTR szText);
|
||||
ME_String *ME_MakeStringN(LPCWSTR szText, int nMaxChars);
|
||||
ME_String *ME_StrDup(ME_String *s);
|
||||
void ME_DestroyString(ME_String *s);
|
||||
void ME_AppendString(ME_String *s1, ME_String *s2);
|
||||
ME_String *ME_ConcatString(ME_String *s1, ME_String *s2);
|
||||
ME_String *ME_VSplitString(ME_String *orig, int nVPos);
|
||||
int ME_IsWhitespaces(ME_String *s);
|
||||
int ME_IsSplitable(ME_String *s);
|
||||
/* int ME_CalcSkipChars(ME_String *s); */
|
||||
int ME_StrLen(ME_String *s);
|
||||
int ME_StrVLen(ME_String *s);
|
||||
int ME_FindNonWhitespaceV(ME_String *s, int nVChar);
|
||||
int ME_FindWhitespaceV(ME_String *s, int nVChar);
|
||||
int ME_GetCharFwd(ME_String *s, int nPos); /* get char starting from start */
|
||||
int ME_GetCharBack(ME_String *s, int nPos); /* get char starting from \0 */
|
||||
int ME_StrRelPos(ME_String *s, int nVChar, int *pRelChars);
|
||||
int ME_StrRelPos2(ME_String *s, int nVChar, int nRelChars);
|
||||
int ME_VPosToPos(ME_String *s, int nVPos);
|
||||
int ME_PosToVPos(ME_String *s, int nPos);
|
||||
void ME_StrDeleteV(ME_String *s, int nVChar, int nChars);
|
||||
/* smart helpers for A<->W conversions, they reserve/free memory and call MultiByte<->WideChar functions */
|
||||
LPWSTR ME_ToUnicode(HWND hWnd, LPVOID psz);
|
||||
void ME_EndToUnicode(HWND hWnd, LPVOID psz);
|
||||
LPSTR ME_ToAnsi(HWND hWnd, LPVOID psz);
|
||||
void ME_EndToAnsi(HWND hWnd, LPVOID psz);
|
||||
|
||||
|
||||
/* note: those two really return the first matching offset (starting from EOS)+1
|
||||
* in other words, an offset of the first trailing white/black */
|
||||
int ME_ReverseFindNonWhitespaceV(ME_String *s, int nVChar);
|
||||
int ME_ReverseFindWhitespaceV(ME_String *s, int nVChar);
|
||||
|
||||
/* row.c */
|
||||
ME_DisplayItem *ME_FindRowStart(ME_Context *c, ME_DisplayItem *run, int nRelPos);
|
||||
ME_DisplayItem *ME_RowStart(ME_DisplayItem *item);
|
||||
ME_DisplayItem *ME_RowEnd(ME_DisplayItem *item);
|
||||
void ME_RenumberParagraphs(ME_DisplayItem *item); /* TODO */
|
||||
|
||||
/* run.c */
|
||||
ME_DisplayItem *ME_MakeRun(ME_Style *s, ME_String *strData, int nFlags);
|
||||
/* note: ME_InsertRun inserts a copy of the specified run - so you need to destroy the original */
|
||||
ME_DisplayItem *ME_InsertRun(ME_TextEditor *editor, int nCharOfs, ME_DisplayItem *pItem);
|
||||
void ME_CheckCharOffsets(ME_TextEditor *editor);
|
||||
void ME_PropagateCharOffset(ME_DisplayItem *p, int shift);
|
||||
void ME_GetGraphicsSize(ME_TextEditor *editor, ME_Run *run, SIZE *pSize);
|
||||
int ME_CharFromPoint(ME_TextEditor *editor, int cx, ME_Run *run);
|
||||
/* this one accounts for 1/2 char tolerance */
|
||||
int ME_CharFromPointCursor(ME_TextEditor *editor, int cx, ME_Run *run);
|
||||
int ME_PointFromChar(ME_TextEditor *editor, ME_Run *pRun, int nOffset);
|
||||
int ME_GetLastSplittablePlace(ME_Context *c, ME_Run *run);
|
||||
int ME_CanJoinRuns(ME_Run *run1, ME_Run *run2);
|
||||
void ME_JoinRuns(ME_TextEditor *editor, ME_DisplayItem *p);
|
||||
ME_DisplayItem *ME_SplitRun(ME_Context *c, ME_DisplayItem *item, int nChar);
|
||||
ME_DisplayItem *ME_SplitRunSimple(ME_TextEditor *editor, ME_DisplayItem *item, int nChar);
|
||||
int ME_FindSplitPoint(ME_Context *c, POINT *pt, ME_Run *run, int desperate);
|
||||
void ME_UpdateRunFlags(ME_TextEditor *editor, ME_Run *run);
|
||||
ME_DisplayItem *ME_SplitFurther(ME_TextEditor *editor, ME_DisplayItem *run);
|
||||
void ME_CalcRunExtent(ME_Context *c, ME_Run *run);
|
||||
SIZE ME_GetRunSize(ME_Context *c, ME_Run *run, int nLen);
|
||||
void ME_CursorFromCharOfs(ME_TextEditor *editor, int nCharOfs, ME_Cursor *pCursor);
|
||||
void ME_RunOfsFromCharOfs(ME_TextEditor *editor, int nCharOfs, ME_DisplayItem **ppRun, int *pOfs);
|
||||
int ME_CharOfsFromRunOfs(ME_TextEditor *editor, ME_DisplayItem *pRun, int nOfs);
|
||||
void ME_SkipAndPropagateCharOffset(ME_DisplayItem *p, int shift);
|
||||
void ME_SetCharFormat(ME_TextEditor *editor, int nFrom, int nLen, CHARFORMAT2W *pFmt);
|
||||
void ME_SetSelectionCharFormat(ME_TextEditor *editor, CHARFORMAT2W *pFmt);
|
||||
void ME_GetCharFormat(ME_TextEditor *editor, int nFrom, int nLen, CHARFORMAT2W *pFmt);
|
||||
void ME_GetSelectionCharFormat(ME_TextEditor *editor, CHARFORMAT2W *pFmt);
|
||||
void ME_GetDefaultCharFormat(ME_TextEditor *editor, CHARFORMAT2W *pFmt);
|
||||
void ME_SetDefaultCharFormat(ME_TextEditor *editor, CHARFORMAT2W *mod);
|
||||
|
||||
/* caret.c */
|
||||
void ME_SetSelection(ME_TextEditor *editor, int from, int to);
|
||||
void ME_HideCaret(ME_TextEditor *ed);
|
||||
void ME_ShowCaret(ME_TextEditor *ed);
|
||||
void ME_MoveCaret(ME_TextEditor *ed);
|
||||
int ME_FindPixelPos(ME_TextEditor *editor, int x, int y, ME_Cursor *result, BOOL *is_eol);
|
||||
void ME_LButtonDown(ME_TextEditor *editor, int x, int y);
|
||||
void ME_MouseMove(ME_TextEditor *editor, int x, int y);
|
||||
void ME_DeleteTextAtCursor(ME_TextEditor *editor, int nCursor, int nChars);
|
||||
void ME_InsertTextFromCursor(ME_TextEditor *editor, int nCursor,
|
||||
const WCHAR *str, int len, ME_Style *style);
|
||||
void ME_SetCharFormat(ME_TextEditor *editor, int nOfs, int nChars, CHARFORMAT2W *pFmt);
|
||||
BOOL ME_ArrowKey(ME_TextEditor *ed, int nVKey, int nCtrl);
|
||||
|
||||
void ME_InitContext(ME_Context *c, ME_TextEditor *editor, HDC hDC);
|
||||
void ME_DestroyContext(ME_Context *c);
|
||||
ME_Style *GetInsertStyle(ME_TextEditor *editor, int nCursor);
|
||||
void ME_MustBeWrapped(ME_Context *c, ME_DisplayItem *para);
|
||||
int ME_GetCursorOfs(ME_TextEditor *editor, int nCursor);
|
||||
void ME_GetSelection(ME_TextEditor *editor, int *from, int *to);
|
||||
int ME_CountParagraphsBetween(ME_TextEditor *editor, int from, int to);
|
||||
BOOL ME_IsSelection(ME_TextEditor *editor);
|
||||
void ME_DeleteSelection(ME_TextEditor *editor);
|
||||
void ME_SendSelChange(ME_TextEditor *editor);
|
||||
void ME_InsertGraphicsFromCursor(ME_TextEditor *editor, int nCursor);
|
||||
void ME_InternalDeleteText(ME_TextEditor *editor, int nOfs, int nChars);
|
||||
int ME_GetTextLength(ME_TextEditor *editor);
|
||||
|
||||
/* wrap.c */
|
||||
void ME_PrepareParagraphForWrapping(ME_Context *c, ME_DisplayItem *tp);
|
||||
ME_DisplayItem *ME_MakeRow(int height, int baseline, int width);
|
||||
void ME_InsertRowStart(ME_WrapContext *wc, ME_DisplayItem *pEnd);
|
||||
void ME_WrapTextParagraph(ME_Context *c, ME_DisplayItem *tp);
|
||||
|
||||
/* para.c */
|
||||
ME_DisplayItem *ME_GetParagraph(ME_DisplayItem *run);
|
||||
void ME_MakeFirstParagraph(HDC hDC, ME_TextBuffer *editor);
|
||||
ME_DisplayItem *ME_SplitParagraph(ME_TextEditor *editor, ME_DisplayItem *rp, ME_Style *style);
|
||||
ME_DisplayItem *ME_JoinParagraphs(ME_TextEditor *editor, ME_DisplayItem *tp);
|
||||
void ME_DumpParaStyle(ME_Paragraph *s);
|
||||
void ME_DumpParaStyleToBuf(PARAFORMAT2 *pFmt, char buf[2048]);
|
||||
void ME_SetParaFormat(ME_TextEditor *editor, ME_DisplayItem *para, PARAFORMAT2 *pFmt);
|
||||
void ME_SetSelectionParaFormat(ME_TextEditor *editor, PARAFORMAT2 *pFmt);
|
||||
void ME_GetParaFormat(ME_TextEditor *editor, ME_DisplayItem *para, PARAFORMAT2 *pFmt);
|
||||
void ME_GetSelectionParaFormat(ME_TextEditor *editor, PARAFORMAT2 *pFmt);
|
||||
/* marks from first up to (but not including) last */
|
||||
void ME_MarkForWrapping(ME_TextEditor *editor, ME_DisplayItem *first, ME_DisplayItem *last);
|
||||
void ME_MarkAllForWrapping(ME_TextEditor *editor);
|
||||
|
||||
/* paint.c */
|
||||
void ME_PaintContent(ME_TextEditor *editor, HDC hDC, BOOL bOnlyNew);
|
||||
void ME_Repaint(ME_TextEditor *editor);
|
||||
void ME_UpdateRepaint(ME_TextEditor *editor);
|
||||
void ME_DrawParagraph(ME_Context *c, ME_DisplayItem *paragraph);
|
||||
void ME_UpdateScrollBar(ME_TextEditor *editor, int ypos);
|
||||
int ME_GetScrollPos(ME_TextEditor *editor);
|
||||
void ME_EnsureVisible(ME_TextEditor *editor, ME_DisplayItem *pRun);
|
||||
COLORREF ME_GetBackColor(ME_TextEditor *editor);
|
||||
|
||||
/* wintest.c */
|
||||
|
||||
/* editor.c */
|
||||
void ME_RegisterEditorClass();
|
||||
ME_TextEditor *ME_MakeEditor(HWND hWnd);
|
||||
void ME_DestroyEditor(ME_TextEditor *editor);
|
||||
void ME_SendOldNotify(ME_TextEditor *editor, int nCode);
|
||||
ME_UndoItem *ME_AddUndoItem(ME_TextEditor *editor, ME_DIType type, ME_DisplayItem *di);
|
||||
void ME_CommitUndo(ME_TextEditor *editor);
|
||||
void ME_Undo(ME_TextEditor *editor);
|
||||
void ME_Redo(ME_TextEditor *editor);
|
||||
void ME_EmptyUndoStack(ME_TextEditor *editor);
|
||||
int ME_GetTextW(ME_TextEditor *editor, WCHAR *buffer, int nStart, int nChars, BOOL bCRLF);
|
||||
|
||||
extern int me_debug;
|
||||
extern HANDLE me_heap;
|
||||
extern void DoWrap(ME_TextEditor *editor);
|
|
@ -0,0 +1,245 @@
|
|||
/*
|
||||
* RichEdit - structures and constant
|
||||
*
|
||||
* Copyright 2004 by Krzysztof Foltman
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef __EDITSTR_H
|
||||
#define __EDITSTR_H
|
||||
|
||||
#ifndef _WIN32_IE
|
||||
#define _WIN32_IE 0x0400
|
||||
#endif
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <windef.h>
|
||||
#include <winbase.h>
|
||||
#include <winnls.h>
|
||||
#include <winnt.h>
|
||||
#include <wingdi.h>
|
||||
#include <winuser.h>
|
||||
#include <richedit.h>
|
||||
#include <commctrl.h>
|
||||
|
||||
#include "wine/debug.h"
|
||||
|
||||
typedef struct tagME_String
|
||||
{
|
||||
WCHAR *szData;
|
||||
int nLen, nBuffer;
|
||||
} ME_String;
|
||||
|
||||
typedef struct tagME_Style
|
||||
{
|
||||
CHARFORMAT2W fmt;
|
||||
|
||||
HFONT hFont; /* cached font for the style */
|
||||
TEXTMETRICW tm; /* cached font metrics for the style */
|
||||
int nRefs; /* reference count */
|
||||
int nSequence; /* incremented when cache needs to be rebuilt, ie. every screen redraw */
|
||||
} ME_Style;
|
||||
|
||||
typedef enum {
|
||||
diTextStart, /* start of the text buffer */
|
||||
diParagraph, /* paragraph start */
|
||||
diRun, /* run (sequence of chars with the same character format) */
|
||||
diStartRow, /* start of the row (line of text on the screen) */
|
||||
diTextEnd, /* end of the text buffer */
|
||||
|
||||
/********************* these below are meant for finding only *********************/
|
||||
diStartRowOrParagraph, /* 5 */
|
||||
diStartRowOrParagraphOrEnd,
|
||||
diRunOrParagraph,
|
||||
diRunOrStartRow,
|
||||
diParagraphOrEnd,
|
||||
diRunOrParagraphOrEnd, /* 10 */
|
||||
|
||||
diUndoInsertRun, /* 11 */
|
||||
diUndoDeleteRun, /* 12 */
|
||||
diUndoJoinParagraphs, /* 13 */
|
||||
diUndoSplitParagraph, /* 14 */
|
||||
diUndoSetParagraphFormat, /* 15 */
|
||||
diUndoSetCharFormat, /* 16 */
|
||||
diUndoEndTransaction, /* 17 */
|
||||
diUndoSetDefaultCharFormat, /* 18 */
|
||||
} ME_DIType;
|
||||
|
||||
/******************************** run flags *************************/
|
||||
#define MERF_STYLEFLAGS 0x0FFF
|
||||
/* run contains non-text content, which has its own rules for wrapping, sizing etc */
|
||||
#define MERF_GRAPHICS 1
|
||||
|
||||
/* run is splittable (contains white spaces in the middle or end) */
|
||||
#define MERF_SPLITTABLE 0x001000
|
||||
/* run starts with whitespaces */
|
||||
#define MERF_STARTWHITE 0x002000
|
||||
/* run ends with whitespaces */
|
||||
#define MERF_ENDWHITE 0x004000
|
||||
/* run is completely made of whitespaces */
|
||||
#define MERF_WHITESPACE 0x008000
|
||||
/* run is a last (dummy) run in the paragraph */
|
||||
#define MERF_SKIPPED 0x010000
|
||||
/* flags that are calculated during text wrapping */
|
||||
#define MERF_CALCBYWRAP 0x0F0000
|
||||
/* the "end of paragraph" run, contains 1 character */
|
||||
#define MERF_ENDPARA 0x100000
|
||||
|
||||
/* those flags are kept when the row is split */
|
||||
#define MERF_SPLITMASK (~(0))
|
||||
|
||||
/******************************** para flags *************************/
|
||||
|
||||
/* this paragraph was already wrapped and hasn't changed, every change resets that flag */
|
||||
#define MEPF_WRAPPED 1
|
||||
#define MEPF_REDRAW 2
|
||||
|
||||
/******************************** structures *************************/
|
||||
|
||||
struct tagME_DisplayItem;
|
||||
|
||||
typedef struct tagME_Run
|
||||
{
|
||||
ME_String *strText;
|
||||
ME_Style *style;
|
||||
int nCharOfs; /* relative to para's offset */
|
||||
int nWidth; /* width of full run, width of leading&trailing ws */
|
||||
int nFlags;
|
||||
int nAscent, nDescent; /* pixels above/below baseline */
|
||||
POINT pt; /* relative to para's position */
|
||||
} ME_Run;
|
||||
|
||||
typedef struct tagME_Document {
|
||||
struct tagME_DisplayItem *def_char_style;
|
||||
struct tagME_DisplayItem *def_para_style;
|
||||
int last_wrapped_line;
|
||||
} ME_Document;
|
||||
|
||||
typedef struct tagME_Paragraph
|
||||
{
|
||||
PARAFORMAT2 *pFmt;
|
||||
int nLeftMargin, nRightMargin, nFirstMargin;
|
||||
int nCharOfs;
|
||||
int nFlags;
|
||||
int nYPos, nHeight;
|
||||
struct tagME_DisplayItem *prev_para, *next_para, *document;
|
||||
} ME_Paragraph;
|
||||
|
||||
typedef struct tagME_Row
|
||||
{
|
||||
int nHeight;
|
||||
int nBaseline;
|
||||
int nWidth;
|
||||
int nLMargin;
|
||||
int nRMargin;
|
||||
int nYPos;
|
||||
} ME_Row;
|
||||
|
||||
typedef struct tagME_DisplayItem
|
||||
{
|
||||
ME_DIType type;
|
||||
struct tagME_DisplayItem *prev, *next;
|
||||
union {
|
||||
ME_Run run;
|
||||
ME_Row row;
|
||||
ME_Paragraph para;
|
||||
ME_Document doc; /* not used */
|
||||
ME_Style *ustyle; /* used by diUndoSetCharFormat */
|
||||
} member;
|
||||
} ME_DisplayItem;
|
||||
|
||||
typedef struct tagME_UndoItem
|
||||
{
|
||||
ME_DisplayItem di;
|
||||
int nStart, nLen;
|
||||
} ME_UndoItem;
|
||||
|
||||
typedef struct tagME_TextBuffer
|
||||
{
|
||||
ME_DisplayItem *pFirst, *pLast;
|
||||
ME_Style *pCharStyle;
|
||||
ME_Style *pDefaultStyle;
|
||||
} ME_TextBuffer;
|
||||
|
||||
typedef struct tagME_Cursor
|
||||
{
|
||||
ME_DisplayItem *pRun;
|
||||
int nOffset;
|
||||
} ME_Cursor;
|
||||
|
||||
typedef enum {
|
||||
umAddToUndo,
|
||||
umAddToRedo,
|
||||
umIgnore,
|
||||
umAddBackToUndo
|
||||
} ME_UndoMode;
|
||||
|
||||
typedef struct tagME_TextEditor
|
||||
{
|
||||
HWND hWnd;
|
||||
BOOL bCaretShown;
|
||||
ME_TextBuffer *pBuffer;
|
||||
ME_Cursor *pCursors;
|
||||
int nCursors;
|
||||
SIZE sizeWindow;
|
||||
int nScrollPos;
|
||||
int nTotalLength;
|
||||
int nUDArrowX;
|
||||
int nSequence;
|
||||
int nOldSelFrom, nOldSelTo;
|
||||
COLORREF rgbBackColor;
|
||||
BOOL bCaretAtEnd;
|
||||
int nEventMask;
|
||||
int nModifyStep;
|
||||
ME_DisplayItem *pUndoStack, *pRedoStack;
|
||||
ME_UndoMode nUndoMode;
|
||||
int nParagraphs;
|
||||
} ME_TextEditor;
|
||||
|
||||
typedef struct tagME_Context
|
||||
{
|
||||
HDC hDC;
|
||||
POINT pt;
|
||||
POINT ptRowOffset;
|
||||
RECT rcView;
|
||||
HBRUSH hbrMargin;
|
||||
|
||||
/* those are valid inside ME_WrapTextParagraph and related */
|
||||
POINT ptFirstRun;
|
||||
ME_TextEditor *editor;
|
||||
int nSequence;
|
||||
} ME_Context;
|
||||
|
||||
typedef struct tagME_WrapContext
|
||||
{
|
||||
ME_Style *style;
|
||||
ME_Context *context;
|
||||
int nLeftMargin, nRightMargin, nFirstMargin;
|
||||
int nTotalWidth, nAvailWidth;
|
||||
int nRow;
|
||||
POINT pt;
|
||||
BOOL bOverflown;
|
||||
ME_DisplayItem *pRowStart;
|
||||
|
||||
ME_DisplayItem *pLastSplittableRun;
|
||||
POINT ptLastSplittableRun;
|
||||
} ME_WrapContext;
|
||||
|
||||
#endif
|
|
@ -0,0 +1,191 @@
|
|||
/*
|
||||
* RichEdit - Basic operations on double linked lists.
|
||||
*
|
||||
* Copyright 2004 by Krzysztof Foltman
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
|
||||
#include "editor.h"
|
||||
|
||||
WINE_DEFAULT_DEBUG_CHANNEL(riched20);
|
||||
|
||||
void ME_InsertBefore(ME_DisplayItem *diWhere, ME_DisplayItem *diWhat)
|
||||
{
|
||||
diWhat->next = diWhere;
|
||||
diWhat->prev = diWhere->prev;
|
||||
|
||||
diWhere->prev->next = diWhat;
|
||||
diWhat->next->prev = diWhat;
|
||||
}
|
||||
|
||||
void ME_Remove(ME_DisplayItem *diWhere)
|
||||
{
|
||||
ME_DisplayItem *diNext = diWhere->next;
|
||||
ME_DisplayItem *diPrev = diWhere->prev;
|
||||
assert(diNext);
|
||||
assert(diPrev);
|
||||
diPrev->next = diNext;
|
||||
diNext->prev = diPrev;
|
||||
}
|
||||
|
||||
ME_DisplayItem *ME_FindItemBack(ME_DisplayItem *di, ME_DIType nTypeOrClass)
|
||||
{
|
||||
if (!di)
|
||||
return NULL;
|
||||
di = di->prev;
|
||||
while(di!=NULL) {
|
||||
if (ME_DITypesEqual(di->type, nTypeOrClass))
|
||||
return di;
|
||||
di = di->prev;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ME_DisplayItem *ME_FindItemBackOrHere(ME_DisplayItem *di, ME_DIType nTypeOrClass)
|
||||
{
|
||||
while(di!=NULL) {
|
||||
if (ME_DITypesEqual(di->type, nTypeOrClass))
|
||||
return di;
|
||||
di = di->prev;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ME_DisplayItem *ME_FindItemFwd(ME_DisplayItem *di, ME_DIType nTypeOrClass)
|
||||
{
|
||||
if (!di) return NULL;
|
||||
di = di->next;
|
||||
while(di!=NULL) {
|
||||
if (ME_DITypesEqual(di->type, nTypeOrClass))
|
||||
return di;
|
||||
di = di->next;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ME_DisplayItem *ME_FindItemFwdOrHere(ME_DisplayItem *di, ME_DIType nTypeOrClass)
|
||||
{
|
||||
while(di!=NULL) {
|
||||
if (ME_DITypesEqual(di->type, nTypeOrClass))
|
||||
return di;
|
||||
di = di->next;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
BOOL ME_DITypesEqual(ME_DIType type, ME_DIType nTypeOrClass)
|
||||
{
|
||||
if (type==nTypeOrClass)
|
||||
return TRUE;
|
||||
if (nTypeOrClass==diRunOrParagraph && (type==diRun || type==diParagraph))
|
||||
return TRUE;
|
||||
if (nTypeOrClass==diRunOrStartRow && (type==diRun || type==diStartRow))
|
||||
return TRUE;
|
||||
if (nTypeOrClass==diParagraphOrEnd && (type==diTextEnd || type==diParagraph))
|
||||
return TRUE;
|
||||
if (nTypeOrClass==diStartRowOrParagraph && (type==diStartRow || type==diParagraph))
|
||||
return TRUE;
|
||||
if (nTypeOrClass==diStartRowOrParagraphOrEnd
|
||||
&& (type==diStartRow || type==diParagraph || type==diTextEnd))
|
||||
return TRUE;
|
||||
if (nTypeOrClass==diRunOrParagraphOrEnd
|
||||
&& (type==diRun || type==diParagraph || type==diTextEnd))
|
||||
return TRUE;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
void ME_DestroyDisplayItem(ME_DisplayItem *item) {
|
||||
/* TRACE("type=%s\n", ME_GetDITypeName(item->type)); */
|
||||
if (item->type==diParagraph || item->type == diUndoSetParagraphFormat) {
|
||||
FREE_OBJ(item->member.para.pFmt);
|
||||
}
|
||||
if (item->type==diRun || item->type == diUndoInsertRun) {
|
||||
ME_ReleaseStyle(item->member.run.style);
|
||||
ME_DestroyString(item->member.run.strText);
|
||||
}
|
||||
if (item->type==diUndoSetCharFormat || item->type==diUndoSetDefaultCharFormat) {
|
||||
ME_ReleaseStyle(item->member.ustyle);
|
||||
}
|
||||
FREE_OBJ(item);
|
||||
}
|
||||
|
||||
ME_DisplayItem *ME_MakeDI(ME_DIType type) {
|
||||
ME_DisplayItem *item = ALLOC_OBJ(ME_DisplayItem);
|
||||
ZeroMemory(item, sizeof(ME_DisplayItem));
|
||||
item->type = type;
|
||||
item->prev = item->next = NULL;
|
||||
if (type == diParagraph || type == diUndoSplitParagraph) {
|
||||
item->member.para.pFmt = ALLOC_OBJ(PARAFORMAT2);
|
||||
item->member.para.pFmt->cbSize = sizeof(PARAFORMAT2);
|
||||
item->member.para.pFmt->dwMask = 0;
|
||||
}
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
const char *ME_GetDITypeName(ME_DIType type)
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
case diParagraph: return "diParagraph";
|
||||
case diRun: return "diRun";
|
||||
case diTextStart: return "diTextStart";
|
||||
case diTextEnd: return "diTextEnd";
|
||||
case diStartRow: return "diStartRow";
|
||||
case diUndoEndTransaction: return "diUndoEndTransaction";
|
||||
case diUndoSetParagraphFormat: return "diUndoSetParagraphFormat";
|
||||
case diUndoSetCharFormat: return "diUndoSetCharFormat";
|
||||
case diUndoInsertRun: return "diUndoInsertRun";
|
||||
case diUndoDeleteRun: return "diUndoDeleteRun";
|
||||
case diUndoJoinParagraphs: return "diJoinParagraphs";
|
||||
case diUndoSplitParagraph: return "diSplitParagraph";
|
||||
case diUndoSetDefaultCharFormat: return "diUndoSetDefaultCharFormat";
|
||||
default: return "?";
|
||||
}
|
||||
}
|
||||
|
||||
void ME_DumpDocument(ME_TextBuffer *buffer)
|
||||
{
|
||||
/* FIXME this is useless, */
|
||||
ME_DisplayItem *pItem = buffer->pFirst;
|
||||
TRACE("DOCUMENT DUMP START\n");
|
||||
while(pItem) {
|
||||
switch(pItem->type)
|
||||
{
|
||||
case diTextStart:
|
||||
TRACE("Start");
|
||||
break;
|
||||
case diParagraph:
|
||||
TRACE("\nParagraph(ofs=%d)", pItem->member.para.nCharOfs);
|
||||
break;
|
||||
case diStartRow:
|
||||
TRACE(" - StartRow");
|
||||
break;
|
||||
case diRun:
|
||||
TRACE(" - Run(\"%s\", %d)", debugstr_w(pItem->member.run.strText->szData),
|
||||
pItem->member.run.nCharOfs);
|
||||
break;
|
||||
case diTextEnd:
|
||||
TRACE("\nEnd\n");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
pItem = pItem->next;
|
||||
}
|
||||
TRACE("DOCUMENT DUMP END\n");
|
||||
}
|
|
@ -0,0 +1,359 @@
|
|||
/*
|
||||
* RichEdit - painting functions
|
||||
*
|
||||
* Copyright 2004 by Krzysztof Foltman
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "editor.h"
|
||||
|
||||
WINE_DEFAULT_DEBUG_CHANNEL(richedit);
|
||||
|
||||
void ME_PaintContent(ME_TextEditor *editor, HDC hDC, BOOL bOnlyNew) {
|
||||
ME_DisplayItem *item;
|
||||
ME_Context c;
|
||||
|
||||
editor->nSequence++;
|
||||
ME_InitContext(&c, editor, hDC);
|
||||
SetBkMode(hDC, TRANSPARENT);
|
||||
ME_MoveCaret(editor);
|
||||
item = editor->pBuffer->pFirst->next;
|
||||
c.pt.y=-GetScrollPos(editor->hWnd, SB_VERT);
|
||||
while(item != editor->pBuffer->pLast) {
|
||||
assert(item->type == diParagraph);
|
||||
if (!bOnlyNew || (item->member.para.nFlags & MEPF_REDRAW))
|
||||
{
|
||||
ME_DrawParagraph(&c, item);
|
||||
item->member.para.nFlags &= ~MEPF_REDRAW;
|
||||
}
|
||||
c.pt.y += item->member.para.nHeight;
|
||||
item = item->member.para.next_para;
|
||||
}
|
||||
/* FIXME this code just sucks, it should try to redraw incrementally */
|
||||
if (c.pt.y<c.rcView.bottom) {
|
||||
RECT rc;
|
||||
rc.left = c.rcView.left;
|
||||
rc.top = c.pt.y;
|
||||
rc.right = c.rcView.right;
|
||||
rc.bottom = c.pt.y+1;
|
||||
FillRect(hDC, &rc, (HBRUSH)GetStockObject(BLACK_BRUSH));
|
||||
|
||||
rc.left = c.rcView.left;
|
||||
rc.top = c.pt.y+1;
|
||||
rc.right = c.rcView.right;
|
||||
rc.bottom = c.rcView.bottom;
|
||||
FillRect(hDC, &rc, (HBRUSH)GetStockObject(LTGRAY_BRUSH));
|
||||
}
|
||||
ME_DestroyContext(&c);
|
||||
|
||||
item = editor->pBuffer->pFirst->next;
|
||||
while(item != editor->pBuffer->pLast) {
|
||||
if (item->type == diRun)
|
||||
ME_UnprepareStyle(item->member.run.style);
|
||||
item = item->next;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void ME_Repaint(ME_TextEditor *editor)
|
||||
{
|
||||
ME_Cursor *pCursor = &editor->pCursors[0];
|
||||
ME_DisplayItem *pRun = NULL;
|
||||
int nOffset = -1;
|
||||
HDC hDC;
|
||||
|
||||
int nCharOfs = ME_CharOfsFromRunOfs(editor, pCursor->pRun, pCursor->nOffset);
|
||||
ME_RunOfsFromCharOfs(editor, nCharOfs, &pRun, &nOffset);
|
||||
assert(pRun == pCursor->pRun);
|
||||
assert(nOffset == pCursor->nOffset);
|
||||
DoWrap(editor);
|
||||
hDC = GetDC(editor->hWnd);
|
||||
ME_HideCaret(editor);
|
||||
ME_PaintContent(editor, hDC, TRUE);
|
||||
ReleaseDC(editor->hWnd, hDC);
|
||||
ME_ShowCaret(editor);
|
||||
}
|
||||
|
||||
void ME_UpdateRepaint(ME_TextEditor *editor)
|
||||
{
|
||||
InvalidateRect(editor->hWnd, NULL, TRUE);
|
||||
ME_SendOldNotify(editor, EN_CHANGE);
|
||||
ME_Repaint(editor);
|
||||
ME_SendOldNotify(editor, EN_UPDATE);
|
||||
ME_SendSelChange(editor);
|
||||
}
|
||||
|
||||
void ME_DrawTextWithStyle(ME_Context *c, int x, int y, LPCWSTR szText, int nChars,
|
||||
ME_Style *s, int *width, int nSelFrom, int nSelTo, int ymin, int cy) {
|
||||
HDC hDC = c->hDC;
|
||||
HGDIOBJ hOldFont;
|
||||
COLORREF rgbOld, rgbBack;
|
||||
ME_PrepareStyle(c, s);
|
||||
hOldFont = SelectObject(hDC, s->hFont);
|
||||
rgbBack = ME_GetBackColor(c->editor);
|
||||
if ((s->fmt.dwMask & CFM_COLOR) && (s->fmt.dwEffects & CFE_AUTOCOLOR))
|
||||
rgbOld = SetTextColor(hDC, GetSysColor(COLOR_WINDOWTEXT));
|
||||
else
|
||||
rgbOld = SetTextColor(hDC, s->fmt.crTextColor);
|
||||
ExtTextOutW(hDC, x, y, 0, NULL, szText, nChars, NULL);
|
||||
if (width) {
|
||||
SIZE sz;
|
||||
GetTextExtentPoint32W(hDC, szText, nChars, &sz);
|
||||
*width = sz.cx;
|
||||
}
|
||||
if (nSelFrom < nChars && nSelTo >= 0 && nSelFrom<nSelTo)
|
||||
{
|
||||
SIZE sz;
|
||||
if (nSelFrom < 0) nSelFrom = 0;
|
||||
if (nSelTo > nChars) nSelTo = nChars;
|
||||
GetTextExtentPoint32W(hDC, szText, nSelFrom, &sz);
|
||||
x += sz.cx;
|
||||
GetTextExtentPoint32W(hDC, szText+nSelFrom, nSelTo-nSelFrom, &sz);
|
||||
PatBlt(hDC, x, ymin, sz.cx, cy, DSTINVERT);
|
||||
}
|
||||
SetTextColor(hDC, rgbOld);
|
||||
SelectObject(hDC, hOldFont);
|
||||
}
|
||||
|
||||
void ME_DrawSelection(ME_Context *c)
|
||||
{
|
||||
}
|
||||
|
||||
void ME_DebugWrite(HDC hDC, POINT *pt, WCHAR *szText) {
|
||||
int align = SetTextAlign(hDC, TA_LEFT|TA_TOP);
|
||||
HGDIOBJ hFont = SelectObject(hDC, GetStockObject(DEFAULT_GUI_FONT));
|
||||
COLORREF color = SetTextColor(hDC, RGB(128,128,128));
|
||||
TextOutW(hDC, pt->x, pt->y, szText, lstrlenW(szText));
|
||||
SelectObject(hDC, hFont);
|
||||
SetTextAlign(hDC, align);
|
||||
SetTextColor(hDC, color);
|
||||
}
|
||||
|
||||
void ME_DrawGraphics(ME_Context *c, int x, int y, ME_Run *run,
|
||||
ME_Paragraph *para, BOOL selected) {
|
||||
SIZE sz;
|
||||
int xs, ys, xe, ye, h, ym, width, eyes;
|
||||
ME_GetGraphicsSize(c->editor, run, &sz);
|
||||
xs = run->pt.x;
|
||||
ys = y-sz.cy;
|
||||
xe = xs+sz.cx;
|
||||
ye = y;
|
||||
h = ye-ys;
|
||||
ym = ys+h/4;
|
||||
width = sz.cx;
|
||||
eyes = width/8;
|
||||
/* draw a smiling face :) */
|
||||
Ellipse(c->hDC, xs, ys, xe, ye);
|
||||
Ellipse(c->hDC, xs+width/8, ym, x+width/8+eyes, ym+eyes);
|
||||
Ellipse(c->hDC, xs+7*width/8-eyes, ym, xs+7*width/8, ym+eyes);
|
||||
MoveToEx(c->hDC, xs+width/8, ys+3*h/4-eyes, NULL);
|
||||
LineTo(c->hDC, xs+width/8, ys+3*h/4);
|
||||
LineTo(c->hDC, xs+7*width/8, ys+3*h/4);
|
||||
LineTo(c->hDC, xs+7*width/8, ys+3*h/4-eyes);
|
||||
if (selected)
|
||||
{
|
||||
/* descent is usually (always?) 0 for graphics */
|
||||
PatBlt(c->hDC, x, y-run->nAscent, sz.cx, run->nAscent+run->nDescent, DSTINVERT);
|
||||
}
|
||||
}
|
||||
|
||||
void ME_DrawRun(ME_Context *c, int x, int y, ME_DisplayItem *rundi, ME_Paragraph *para) {
|
||||
ME_Run *run = &rundi->member.run;
|
||||
int runofs = run->nCharOfs+para->nCharOfs;
|
||||
|
||||
if (run->nFlags & MERF_GRAPHICS) {
|
||||
int blfrom, blto;
|
||||
ME_GetSelection(c->editor, &blfrom, &blto);
|
||||
ME_DrawGraphics(c, x, y, run, para, (runofs >= blfrom) && (runofs < blto));
|
||||
} else
|
||||
{
|
||||
int blfrom, blto;
|
||||
ME_DisplayItem *start = ME_FindItemBack(rundi, diStartRow);
|
||||
ME_GetSelection(c->editor, &blfrom, &blto);
|
||||
|
||||
ME_DrawTextWithStyle(c, x, y,
|
||||
run->strText->szData, ME_StrVLen(run->strText), run->style, NULL,
|
||||
blfrom-runofs, blto-runofs, c->pt.y+start->member.row.nYPos, start->member.row.nHeight);
|
||||
}
|
||||
}
|
||||
|
||||
COLORREF ME_GetBackColor(ME_TextEditor *editor)
|
||||
{
|
||||
/* Looks like I was seriously confused
|
||||
return GetSysColor((GetWindowLong(editor->hWnd, GWL_STYLE) & ES_READONLY) ? COLOR_3DFACE: COLOR_WINDOW);
|
||||
*/
|
||||
if (editor->rgbBackColor == -1)
|
||||
return GetSysColor(COLOR_WINDOW);
|
||||
else
|
||||
return editor->rgbBackColor;
|
||||
}
|
||||
|
||||
void ME_DrawParagraph(ME_Context *c, ME_DisplayItem *paragraph) {
|
||||
int align = SetTextAlign(c->hDC, TA_BASELINE);
|
||||
ME_DisplayItem *p;
|
||||
ME_Run *run;
|
||||
ME_Paragraph *para = NULL;
|
||||
RECT rc, rcPara;
|
||||
int y = c->pt.y;
|
||||
int height = 0, baseline = 0, no=0, pno = 0;
|
||||
int xs, xe;
|
||||
int visible = 0;
|
||||
int nMargWidth = 0;
|
||||
|
||||
c->pt.x = c->rcView.left;
|
||||
rcPara.left = c->rcView.left;
|
||||
rcPara.right = c->rcView.right;
|
||||
for (p = paragraph; p!=paragraph->member.para.next_para; p = p->next) {
|
||||
switch(p->type) {
|
||||
case diParagraph:
|
||||
para = &p->member.para;
|
||||
break;
|
||||
case diStartRow:
|
||||
assert(para);
|
||||
nMargWidth = (pno==0?para->nFirstMargin:para->nLeftMargin);
|
||||
xs = c->rcView.left+nMargWidth;
|
||||
xe = c->rcView.right-para->nRightMargin;
|
||||
y += height;
|
||||
rcPara.top = y;
|
||||
rcPara.bottom = y+p->member.row.nHeight;
|
||||
visible = RectVisible(c->hDC, &rcPara);
|
||||
if (visible) {
|
||||
HBRUSH hbr;
|
||||
/* left margin */
|
||||
rc.left = c->rcView.left;
|
||||
rc.right = c->rcView.left+nMargWidth;
|
||||
rc.top = y;
|
||||
rc.bottom = y+p->member.row.nHeight;
|
||||
FillRect(c->hDC, &rc, c->hbrMargin);
|
||||
/* right margin */
|
||||
rc.left = xe;
|
||||
rc.right = c->rcView.right;
|
||||
FillRect(c->hDC, &rc, c->hbrMargin);
|
||||
rc.left = c->rcView.left+para->nLeftMargin;
|
||||
rc.right = xe;
|
||||
hbr = CreateSolidBrush(ME_GetBackColor(c->editor));
|
||||
FillRect(c->hDC, &rc, hbr);
|
||||
DeleteObject(hbr);
|
||||
}
|
||||
if (me_debug)
|
||||
{
|
||||
const WCHAR wszRowDebug[] = {'r','o','w','[','%','d',']',0};
|
||||
WCHAR buf[128];
|
||||
POINT pt = c->pt;
|
||||
wsprintfW(buf, wszRowDebug, no);
|
||||
pt.y = 12+y;
|
||||
ME_DebugWrite(c->hDC, &pt, buf);
|
||||
}
|
||||
|
||||
height = p->member.row.nHeight;
|
||||
baseline = p->member.row.nBaseline;
|
||||
pno++;
|
||||
break;
|
||||
case diRun:
|
||||
assert(para);
|
||||
run = &p->member.run;
|
||||
if (visible && me_debug) {
|
||||
rc.left = c->rcView.left+run->pt.x;
|
||||
rc.right = c->rcView.left+run->pt.x+run->nWidth;
|
||||
rc.top = c->pt.y+run->pt.y;
|
||||
rc.bottom = c->pt.y+run->pt.y+height;
|
||||
TRACE("rc = (%ld, %ld, %ld, %ld)\n", rc.left, rc.top, rc.right, rc.bottom);
|
||||
if (run->nFlags & MERF_SKIPPED)
|
||||
DrawFocusRect(c->hDC, &rc);
|
||||
else
|
||||
FrameRect(c->hDC, &rc, GetSysColorBrush(COLOR_GRAYTEXT));
|
||||
}
|
||||
if (visible)
|
||||
ME_DrawRun(c, run->pt.x, c->pt.y+run->pt.y+baseline, p, ¶graph->member.para);
|
||||
if (me_debug)
|
||||
{
|
||||
/* I'm using %ls, hope wsprintfW is not going to use wrong (4-byte) WCHAR version */
|
||||
const WCHAR wszRunDebug[] = {'[','%','d',':','%','x',']',' ','%','l','s',0};
|
||||
WCHAR buf[2560];
|
||||
POINT pt;
|
||||
pt.x = run->pt.x;
|
||||
pt.y = c->pt.y + run->pt.y;
|
||||
wsprintfW(buf, wszRunDebug, no, p->member.run.nFlags, p->member.run.strText->szData);
|
||||
ME_DebugWrite(c->hDC, &pt, buf);
|
||||
}
|
||||
/* c->pt.x += p->member.run.nWidth; */
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
no++;
|
||||
}
|
||||
SetTextAlign(c->hDC, align);
|
||||
}
|
||||
|
||||
void ME_UpdateScrollBar(ME_TextEditor *editor, int ypos)
|
||||
{
|
||||
float perc = 0.0;
|
||||
SCROLLINFO si;
|
||||
HWND hWnd = editor->hWnd;
|
||||
int overflow = editor->nTotalLength - editor->sizeWindow.cy;
|
||||
si.cbSize = sizeof(SCROLLINFO);
|
||||
si.fMask = SIF_PAGE|SIF_POS|SIF_RANGE|SIF_TRACKPOS;
|
||||
GetScrollInfo(hWnd, SB_VERT, &si);
|
||||
|
||||
if (ypos < 0) {
|
||||
perc = 1.0*si.nPos/si.nMax;
|
||||
ypos = perc*overflow;
|
||||
}
|
||||
if (ypos >= overflow && overflow > 0)
|
||||
ypos = overflow - 1;
|
||||
|
||||
if (overflow > 0) {
|
||||
EnableScrollBar(hWnd, SB_VERT, ESB_ENABLE_BOTH);
|
||||
SetScrollRange(hWnd, SB_VERT, 0, overflow, FALSE);
|
||||
SetScrollPos(hWnd, SB_VERT, ypos, TRUE);
|
||||
} else {
|
||||
EnableScrollBar(hWnd, SB_VERT, ESB_DISABLE_BOTH);
|
||||
SetScrollRange(hWnd, SB_VERT, 0, 0, FALSE);
|
||||
SetScrollPos(hWnd, SB_VERT, 0, TRUE);
|
||||
}
|
||||
if (ypos != si.nPos)
|
||||
{
|
||||
TRACE("ScrollWindow(%d, %d, %d, %0.4f)\n", si.nPos, si.nMax, ypos, perc);
|
||||
ScrollWindow(hWnd, 0, si.nPos - ypos, NULL, NULL);
|
||||
UpdateWindow(hWnd);
|
||||
}
|
||||
}
|
||||
|
||||
int ME_GetScrollPos(ME_TextEditor *editor)
|
||||
{
|
||||
return GetScrollPos(editor->hWnd, SB_VERT);
|
||||
}
|
||||
|
||||
void ME_EnsureVisible(ME_TextEditor *editor, ME_DisplayItem *pRun)
|
||||
{
|
||||
ME_DisplayItem *pRow = ME_FindItemBack(pRun, diStartRow);
|
||||
ME_DisplayItem *pPara = ME_FindItemBack(pRun, diParagraph);
|
||||
int y, yrel, yheight;
|
||||
|
||||
assert(pRow);
|
||||
assert(pPara);
|
||||
|
||||
y = pPara->member.para.nYPos+pRow->member.row.nYPos;
|
||||
yheight = pRow->member.row.nHeight;
|
||||
yrel = y - ME_GetScrollPos(editor);
|
||||
if (yrel < 0)
|
||||
ME_UpdateScrollBar(editor, y);
|
||||
else if (yrel + yheight > editor->sizeWindow.cy)
|
||||
{
|
||||
ME_UpdateScrollBar(editor, y + yheight - editor->sizeWindow.cy);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,358 @@
|
|||
/*
|
||||
* RichEdit - functions working on paragraphs of text (diParagraph).
|
||||
*
|
||||
* Copyright 2004 by Krzysztof Foltman
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "editor.h"
|
||||
|
||||
WINE_DEFAULT_DEBUG_CHANNEL(richedit);
|
||||
|
||||
static WCHAR wszParagraphSign[] = {0xB6, 0};
|
||||
|
||||
void ME_MakeFirstParagraph(HDC hDC, ME_TextBuffer *text)
|
||||
{
|
||||
PARAFORMAT2 fmt;
|
||||
CHARFORMAT2W cf;
|
||||
LOGFONTW lf;
|
||||
HFONT hf;
|
||||
ME_DisplayItem *para = ME_MakeDI(diParagraph);
|
||||
ME_DisplayItem *run;
|
||||
ME_Style *style;
|
||||
|
||||
hf = (HFONT)GetStockObject(SYSTEM_FONT);
|
||||
assert(hf);
|
||||
GetObjectW(hf, sizeof(LOGFONTW), &lf);
|
||||
ZeroMemory(&cf, sizeof(cf));
|
||||
cf.cbSize = sizeof(cf);
|
||||
cf.dwMask = CFM_BACKCOLOR|CFM_COLOR|CFM_FACE|CFM_SIZE|CFM_CHARSET;
|
||||
cf.dwMask |= CFM_ALLCAPS|CFM_BOLD|CFM_DISABLED|CFM_EMBOSS|CFM_HIDDEN;
|
||||
cf.dwMask |= CFM_IMPRINT|CFM_ITALIC|CFM_LINK|CFM_OUTLINE|CFM_PROTECTED;
|
||||
cf.dwMask |= CFM_REVISED|CFM_SHADOW|CFM_SMALLCAPS|CFM_STRIKEOUT;
|
||||
cf.dwMask |= CFM_SUBSCRIPT|CFM_UNDERLINE;
|
||||
|
||||
cf.dwEffects = CFE_AUTOCOLOR | CFE_AUTOBACKCOLOR;
|
||||
lstrcpyW(cf.szFaceName, lf.lfFaceName);
|
||||
cf.yHeight=lf.lfHeight*1440/GetDeviceCaps(hDC, LOGPIXELSY);
|
||||
if (lf.lfWeight>=700) /* FIXME correct weight ? */
|
||||
cf.dwEffects |= CFE_BOLD;
|
||||
cf.wWeight = lf.lfWeight;
|
||||
if (lf.lfItalic) cf.dwEffects |= CFE_ITALIC;
|
||||
if (lf.lfUnderline) cf.dwEffects |= CFE_UNDERLINE;
|
||||
if (lf.lfStrikeOut) cf.dwEffects |= CFE_STRIKEOUT;
|
||||
|
||||
ZeroMemory(&fmt, sizeof(fmt));
|
||||
fmt.cbSize = sizeof(fmt);
|
||||
fmt.dwMask = PFM_ALIGNMENT | PFM_OFFSET | PFM_STARTINDENT | PFM_RIGHTINDENT;
|
||||
|
||||
CopyMemory(para->member.para.pFmt, &fmt, sizeof(PARAFORMAT2));
|
||||
|
||||
style = ME_MakeStyle(&cf);
|
||||
text->pDefaultStyle = style;
|
||||
|
||||
run = ME_MakeRun(style, ME_MakeString(wszParagraphSign), MERF_ENDPARA);
|
||||
run->member.run.nCharOfs = 0;
|
||||
|
||||
ME_InsertBefore(text->pLast, para);
|
||||
ME_InsertBefore(text->pLast, run);
|
||||
para->member.para.prev_para = text->pFirst;
|
||||
para->member.para.next_para = text->pLast;
|
||||
text->pFirst->member.para.next_para = para;
|
||||
text->pLast->member.para.prev_para = para;
|
||||
|
||||
text->pLast->member.para.nCharOfs = 1;
|
||||
}
|
||||
|
||||
void ME_MarkAllForWrapping(ME_TextEditor *editor)
|
||||
{
|
||||
ME_MarkForWrapping(editor, editor->pBuffer->pFirst->member.para.next_para, editor->pBuffer->pLast);
|
||||
}
|
||||
|
||||
void ME_MarkForWrapping(ME_TextEditor *editor, ME_DisplayItem *first, ME_DisplayItem *last)
|
||||
{
|
||||
while(first != last)
|
||||
{
|
||||
first->member.para.nFlags &= ~MEPF_WRAPPED;
|
||||
first = first->member.para.next_para;
|
||||
}
|
||||
}
|
||||
|
||||
/* split paragraph at the beginning of the run */
|
||||
ME_DisplayItem *ME_SplitParagraph(ME_TextEditor *editor, ME_DisplayItem *run, ME_Style *style)
|
||||
{
|
||||
ME_DisplayItem *next_para = NULL;
|
||||
ME_DisplayItem *run_para = NULL;
|
||||
ME_DisplayItem *new_para = ME_MakeDI(diParagraph);
|
||||
ME_DisplayItem *end_run = ME_MakeRun(style,ME_MakeString(wszParagraphSign), MERF_ENDPARA);
|
||||
ME_UndoItem *undo = NULL;
|
||||
int ofs;
|
||||
ME_DisplayItem *pp;
|
||||
|
||||
assert(run->type == diRun);
|
||||
|
||||
run_para = ME_GetParagraph(run);
|
||||
assert(run_para->member.para.pFmt->cbSize == sizeof(PARAFORMAT2));
|
||||
|
||||
ofs = end_run->member.run.nCharOfs = run->member.run.nCharOfs;
|
||||
next_para = run_para->member.para.next_para;
|
||||
assert(next_para == ME_FindItemFwd(run_para, diParagraphOrEnd));
|
||||
|
||||
undo = ME_AddUndoItem(editor, diUndoJoinParagraphs, NULL);
|
||||
if (undo)
|
||||
undo->nStart = run_para->member.para.nCharOfs + ofs;
|
||||
|
||||
/* the new paragraph will have a different starting offset, so let's update its runs */
|
||||
pp = run;
|
||||
while(pp->type == diRun) {
|
||||
pp->member.run.nCharOfs -= ofs;
|
||||
pp = ME_FindItemFwd(pp, diRunOrParagraphOrEnd);
|
||||
}
|
||||
new_para->member.para.nCharOfs = ME_GetParagraph(run)->member.para.nCharOfs+ofs;
|
||||
new_para->member.para.nCharOfs += 1;
|
||||
|
||||
new_para->member.para.nFlags = 0; /* FIXME copy flags (if applicable) */
|
||||
/* FIXME initialize format style and call ME_SetParaFormat blah blah */
|
||||
CopyMemory(new_para->member.para.pFmt, run_para->member.para.pFmt, sizeof(PARAFORMAT2));
|
||||
|
||||
/* FIXME remove this as soon as nLeftMargin etc are replaced with proper fields of PARAFORMAT2 */
|
||||
new_para->member.para.nLeftMargin = run_para->member.para.nLeftMargin;
|
||||
new_para->member.para.nRightMargin = run_para->member.para.nRightMargin;
|
||||
new_para->member.para.nFirstMargin = run_para->member.para.nFirstMargin;
|
||||
|
||||
/* insert paragraph into paragraph double linked list */
|
||||
new_para->member.para.prev_para = run_para;
|
||||
new_para->member.para.next_para = next_para;
|
||||
run_para->member.para.next_para = new_para;
|
||||
next_para->member.para.prev_para = new_para;
|
||||
|
||||
/* insert end run of the old paragraph, and new paragraph, into DI double linked list */
|
||||
ME_InsertBefore(run, new_para);
|
||||
ME_InsertBefore(new_para, end_run);
|
||||
|
||||
/* force rewrap of the */
|
||||
run_para->member.para.prev_para->member.para.nFlags &= ~MEPF_WRAPPED;
|
||||
new_para->member.para.prev_para->member.para.nFlags &= ~MEPF_WRAPPED;
|
||||
|
||||
/* we've added the end run, so we need to modify nCharOfs in the next paragraphs */
|
||||
ME_PropagateCharOffset(next_para, 1);
|
||||
editor->nParagraphs++;
|
||||
|
||||
return new_para;
|
||||
}
|
||||
|
||||
/* join tp with tp->member.para.next_para, keeping tp's style; this
|
||||
* is consistent with the original */
|
||||
ME_DisplayItem *ME_JoinParagraphs(ME_TextEditor *editor, ME_DisplayItem *tp)
|
||||
{
|
||||
ME_DisplayItem *pNext, *pFirstRunInNext, *pRun, *pTmp;
|
||||
int i, shift;
|
||||
ME_UndoItem *undo = NULL;
|
||||
|
||||
assert(tp->type == diParagraph);
|
||||
assert(tp->member.para.next_para);
|
||||
assert(tp->member.para.next_para->type == diParagraph);
|
||||
|
||||
pNext = tp->member.para.next_para;
|
||||
|
||||
{
|
||||
/* null char format operation to store the original char format for the ENDPARA run */
|
||||
CHARFORMAT2W fmt;
|
||||
ME_InitCharFormat2W(&fmt);
|
||||
ME_SetCharFormat(editor, pNext->member.para.nCharOfs-1, 1, &fmt);
|
||||
}
|
||||
undo = ME_AddUndoItem(editor, diUndoSplitParagraph, NULL);
|
||||
if (undo)
|
||||
{
|
||||
undo->nStart = pNext->member.para.nCharOfs-1;
|
||||
assert(pNext->member.para.pFmt->cbSize == sizeof(PARAFORMAT2));
|
||||
CopyMemory(undo->di.member.para.pFmt, pNext->member.para.pFmt, sizeof(PARAFORMAT2));
|
||||
}
|
||||
|
||||
shift = pNext->member.para.nCharOfs - tp->member.para.nCharOfs - 1;
|
||||
|
||||
pRun = ME_FindItemBack(pNext, diRunOrParagraph);
|
||||
pFirstRunInNext = ME_FindItemFwd(pNext, diRunOrParagraph);
|
||||
|
||||
assert(pRun);
|
||||
assert(pRun->type == diRun);
|
||||
assert(pRun->member.run.nFlags & MERF_ENDPARA);
|
||||
assert(pFirstRunInNext->type == diRun);
|
||||
|
||||
/* if some cursor points at end of paragraph, make it point to the first
|
||||
run of the next joined paragraph */
|
||||
for (i=0; i<editor->nCursors; i++) {
|
||||
if (editor->pCursors[i].pRun == pRun) {
|
||||
editor->pCursors[i].pRun = pFirstRunInNext;
|
||||
editor->pCursors[i].nOffset = 0;
|
||||
}
|
||||
}
|
||||
|
||||
pTmp = pNext;
|
||||
do {
|
||||
pTmp = ME_FindItemFwd(pTmp, diRunOrParagraphOrEnd);
|
||||
if (pTmp->type != diRun)
|
||||
break;
|
||||
TRACE("shifting \"%s\" by %d (previous %d)\n", debugstr_w(pTmp->member.run.strText->szData), shift, pTmp->member.run.nCharOfs);
|
||||
pTmp->member.run.nCharOfs += shift;
|
||||
} while(1);
|
||||
|
||||
ME_Remove(pRun);
|
||||
ME_DestroyDisplayItem(pRun);
|
||||
|
||||
tp->member.para.next_para = pNext->member.para.next_para;
|
||||
pNext->member.para.next_para->member.para.prev_para = tp;
|
||||
ME_Remove(pNext);
|
||||
ME_DestroyDisplayItem(pNext);
|
||||
|
||||
ME_PropagateCharOffset(tp->member.para.next_para, -1);
|
||||
|
||||
ME_CheckCharOffsets(editor);
|
||||
|
||||
editor->nParagraphs--;
|
||||
tp->member.para.nFlags &= ~MEPF_WRAPPED;
|
||||
return tp;
|
||||
}
|
||||
|
||||
ME_DisplayItem *ME_GetParagraph(ME_DisplayItem *item) {
|
||||
return ME_FindItemBackOrHere(item, diParagraph);
|
||||
}
|
||||
|
||||
static void ME_DumpStyleEffect(char **p, const char *name, PARAFORMAT2 *fmt, int mask)
|
||||
{
|
||||
*p += sprintf(*p, "%-22s%s\n", name, (fmt->dwMask & mask) ? ((fmt->wEffects & mask) ? "yes" : "no") : "N/A");
|
||||
}
|
||||
|
||||
void ME_DumpParaStyleToBuf(PARAFORMAT2 *pFmt, char buf[2048])
|
||||
{
|
||||
/* FIXME only PARAFORMAT styles implemented */
|
||||
char *p;
|
||||
p = buf;
|
||||
p += sprintf(p, "Alignment: %s\n",
|
||||
!(pFmt->dwMask & PFM_ALIGNMENT) ? "N/A" :
|
||||
((pFmt->wAlignment == PFA_LEFT) ? "left" :
|
||||
((pFmt->wAlignment == PFA_RIGHT) ? "right" :
|
||||
((pFmt->wAlignment == PFA_CENTER) ? "center" :
|
||||
/*((pFmt->wAlignment == PFA_JUSTIFY) ? "justify" : "incorrect")*/
|
||||
"incorrect"))));
|
||||
|
||||
if (pFmt->dwMask & PFM_OFFSET)
|
||||
p += sprintf(p, "Offset: %d\n", (int)pFmt->dxOffset);
|
||||
else
|
||||
p += sprintf(p, "Offset: N/A\n");
|
||||
|
||||
if (pFmt->dwMask & PFM_OFFSETINDENT)
|
||||
p += sprintf(p, "Offset indent: %d\n", (int)pFmt->dxStartIndent);
|
||||
else
|
||||
p += sprintf(p, "Offset indent: N/A\n");
|
||||
|
||||
if (pFmt->dwMask & PFM_STARTINDENT)
|
||||
p += sprintf(p, "Start indent: %d\n", (int)pFmt->dxStartIndent);
|
||||
else
|
||||
p += sprintf(p, "Start indent: N/A\n");
|
||||
|
||||
if (pFmt->dwMask & PFM_RIGHTINDENT)
|
||||
p += sprintf(p, "Right indent: %d\n", (int)pFmt->dxRightIndent);
|
||||
else
|
||||
p += sprintf(p, "Right indent: N/A\n");
|
||||
|
||||
ME_DumpStyleEffect(&p, "Page break before:", pFmt, PFM_PAGEBREAKBEFORE);
|
||||
}
|
||||
|
||||
void ME_SetParaFormat(ME_TextEditor *editor, ME_DisplayItem *para, PARAFORMAT2 *pFmt)
|
||||
{
|
||||
PARAFORMAT2 copy;
|
||||
assert(sizeof(*para->member.para.pFmt) == sizeof(PARAFORMAT2));
|
||||
ME_AddUndoItem(editor, diUndoSetParagraphFormat, para);
|
||||
|
||||
CopyMemory(©, para->member.para.pFmt, sizeof(PARAFORMAT2));
|
||||
|
||||
if (pFmt->dwMask & PFM_ALIGNMENT)
|
||||
para->member.para.pFmt->wAlignment = pFmt->wAlignment;
|
||||
|
||||
/* FIXME to be continued (indents, bulleting and such) */
|
||||
|
||||
if (memcmp(©, para->member.para.pFmt, sizeof(PARAFORMAT2)))
|
||||
para->member.para.nFlags &= ~MEPF_WRAPPED;
|
||||
}
|
||||
|
||||
void ME_SetSelectionParaFormat(ME_TextEditor *editor, PARAFORMAT2 *pFmt)
|
||||
{
|
||||
int nFrom, nTo;
|
||||
ME_DisplayItem *para, *para_end, *run;
|
||||
int nOffset;
|
||||
|
||||
ME_GetSelection(editor, &nFrom, &nTo);
|
||||
if (nTo>nFrom) /* selection consists of chars from nFrom up to nTo-1 */
|
||||
nTo--;
|
||||
|
||||
ME_RunOfsFromCharOfs(editor, nFrom, &run, &nOffset);
|
||||
para = ME_GetParagraph(run);
|
||||
ME_RunOfsFromCharOfs(editor, nTo, &run, &nOffset);
|
||||
para_end = ME_GetParagraph(run);
|
||||
|
||||
do {
|
||||
ME_SetParaFormat(editor, para, pFmt);
|
||||
if (para == para_end)
|
||||
break;
|
||||
para = para->member.para.next_para;
|
||||
} while(1);
|
||||
ME_Repaint(editor);
|
||||
}
|
||||
|
||||
void ME_GetParaFormat(ME_TextEditor *editor, ME_DisplayItem *para, PARAFORMAT2 *pFmt)
|
||||
{
|
||||
if (pFmt->cbSize >= sizeof(PARAFORMAT2))
|
||||
{
|
||||
CopyMemory(pFmt, para->member.para.pFmt, sizeof(PARAFORMAT2));
|
||||
return;
|
||||
}
|
||||
CopyMemory(pFmt, para->member.para.pFmt, pFmt->cbSize);
|
||||
}
|
||||
|
||||
void ME_GetSelectionParaFormat(ME_TextEditor *editor, PARAFORMAT2 *pFmt)
|
||||
{
|
||||
int nFrom, nTo;
|
||||
ME_DisplayItem *para, *para_end, *run;
|
||||
int nOffset;
|
||||
PARAFORMAT2 tmp;
|
||||
|
||||
ME_GetSelection(editor, &nFrom, &nTo);
|
||||
if (nTo>nFrom) /* selection consists of chars from nFrom up to nTo-1 */
|
||||
nTo--;
|
||||
|
||||
ME_RunOfsFromCharOfs(editor, nFrom, &run, &nOffset);
|
||||
para = ME_GetParagraph(run);
|
||||
ME_RunOfsFromCharOfs(editor, nTo, &run, &nOffset);
|
||||
para_end = ME_GetParagraph(run);
|
||||
|
||||
ME_GetParaFormat(editor, para, pFmt);
|
||||
if (para == para_end) return;
|
||||
|
||||
do {
|
||||
ZeroMemory(&tmp, sizeof(tmp));
|
||||
tmp.cbSize = sizeof(tmp);
|
||||
ME_GetParaFormat(editor, para, &tmp);
|
||||
assert(tmp.dwMask & PFM_ALIGNMENT);
|
||||
|
||||
if (pFmt->wAlignment != tmp.wAlignment)
|
||||
pFmt->dwMask &= ~PFM_ALIGNMENT;
|
||||
|
||||
if (para == para_end)
|
||||
return;
|
||||
para = para->member.para.next_para;
|
||||
} while(1);
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
2 extern IID_IRichEditOle
|
||||
3 extern IID_IRichEditOleCallback
|
||||
4 stdcall CreateTextServices(ptr ptr ptr)
|
||||
5 extern IID_ITextServices
|
||||
6 extern IID_ITextHost
|
||||
7 extern IID_ITextHost2
|
||||
8 stdcall REExtendedRegisterClass()
|
||||
9 stdcall RichEdit10ANSIWndProc(ptr long long long)
|
||||
10 stdcall RichEditANSIWndProc(ptr long long long)
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* RichEdit GUIDs
|
||||
*
|
||||
* Copyright 2004 by Krzysztof Foltman
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <windef.h>
|
||||
|
||||
/* there is no way to be consistent across different sets of headers - mingw, Wine, Win32 SDK*/
|
||||
|
||||
#define RICHEDIT_GUID(name, l, w1, w2) \
|
||||
GUID name = { l, w1, w2, {0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46}}
|
||||
|
||||
RICHEDIT_GUID(IID_IRichEditOle, 0x00020d00, 0, 0);
|
||||
RICHEDIT_GUID(IID_IRichEditOleCallback, 0x00020d03, 0, 0);
|
||||
|
||||
#define TEXTSERV_GUID(name, l, w1, w2, b1, b2) \
|
||||
GUID name = { l, w1, w2, {b1, b2, 0x00, 0xaa, 0x00, 0x6c, 0xad, 0xc5}}
|
||||
|
||||
TEXTSERV_GUID(IID_ITextServices, 0x8d33f740, 0xcf58, 0x11ce, 0xa8, 0x9d);
|
||||
TEXTSERV_GUID(IID_ITextHost, 0xc5bdd8d0, 0xd26e, 0x11ce, 0xa8, 0x9e);
|
||||
TEXTSERV_GUID(IID_ITextHost2, 0xc5bdd8d0, 0xd26e, 0x11ce, 0xa8, 0x9e);
|
|
@ -0,0 +1,84 @@
|
|||
/*
|
||||
* RichEdit - Operations on rows of text (rows are recreated during
|
||||
* wrapping and are used for displaying the document, they don't keep any
|
||||
* true document content; delete all rows, rewrap all paragraphs and
|
||||
* you get them back).
|
||||
*
|
||||
* Copyright 2004 by Krzysztof Foltman
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
|
||||
#include "editor.h"
|
||||
|
||||
ME_DisplayItem *ME_FindRowStart(ME_Context *c, ME_DisplayItem *item,
|
||||
int nRelPos) {
|
||||
ME_DisplayItem *para = ME_GetParagraph(item);
|
||||
ME_MustBeWrapped(c, para);
|
||||
if(nRelPos>=0) { /* if this or preceding row */
|
||||
while(nRelPos<=0) {
|
||||
ME_DisplayItem *item2 = ME_FindItemBack(item, diStartRowOrParagraph);
|
||||
if (item2->type == diParagraph)
|
||||
{
|
||||
if (item2->member.para.prev_para == NULL)
|
||||
return item;
|
||||
/* if skipping to the preceding paragraph, ensure it's wrapped */
|
||||
ME_MustBeWrapped(c, item2->member.para.prev_para);
|
||||
item = item2;
|
||||
continue;
|
||||
}
|
||||
else if (item2->type == diStartRow)
|
||||
{
|
||||
nRelPos++;
|
||||
if (nRelPos>0)
|
||||
return item;
|
||||
item = item2;
|
||||
continue;
|
||||
}
|
||||
assert(0 == "bug in FindItemBack(item, diStartRowOrParagraph)");
|
||||
item = item2;
|
||||
}
|
||||
return item;
|
||||
}
|
||||
while(nRelPos>0) { /* if one of the next rows */
|
||||
ME_DisplayItem *item2 = ME_FindItemFwd(item, diStartRowOrParagraph);
|
||||
if (!item2)
|
||||
return item;
|
||||
if (item2->type == diParagraph)
|
||||
{
|
||||
if (item2->member.para.next_para == NULL)
|
||||
return item;
|
||||
continue;
|
||||
}
|
||||
item = item2;
|
||||
nRelPos--;
|
||||
}
|
||||
return item;
|
||||
}
|
||||
|
||||
/* I'm sure these functions would simplify some code in caret ops etc,
|
||||
* I just didn't remember them when I wrote that code
|
||||
*/
|
||||
|
||||
ME_DisplayItem *ME_RowStart(ME_DisplayItem *item) {
|
||||
return ME_FindItemBackOrHere(item, diStartRow);
|
||||
}
|
||||
|
||||
ME_DisplayItem *ME_RowEnd(ME_DisplayItem *item) {
|
||||
ME_DisplayItem *item2 = ME_FindItemFwd(item, diStartRowOrParagraphOrEnd);
|
||||
if (!item2) return NULL;
|
||||
return ME_FindItemBack(item, diRun);
|
||||
}
|
|
@ -0,0 +1,672 @@
|
|||
/*
|
||||
* RichEdit - operations on runs (diRun, rectangular pieces of paragraphs).
|
||||
* Splitting/joining runs. Adjusting offsets after deleting/adding content.
|
||||
* Character/pixel conversions.
|
||||
*
|
||||
* Copyright 2004 by Krzysztof Foltman
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "editor.h"
|
||||
|
||||
WINE_DEFAULT_DEBUG_CHANNEL(richedit);
|
||||
|
||||
int ME_CanJoinRuns(ME_Run *run1, ME_Run *run2)
|
||||
{
|
||||
if ((run1->nFlags | run2->nFlags) & (MERF_ENDPARA | MERF_GRAPHICS))
|
||||
return 0;
|
||||
if (run1->style != run2->style)
|
||||
return 0;
|
||||
if ((run1->nFlags & MERF_STYLEFLAGS) != (run2->nFlags & MERF_STYLEFLAGS))
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
void ME_SkipAndPropagateCharOffset(ME_DisplayItem *p, int shift)
|
||||
{
|
||||
p = ME_FindItemFwd(p, diRunOrParagraphOrEnd);
|
||||
assert(p);
|
||||
ME_PropagateCharOffset(p, shift);
|
||||
}
|
||||
|
||||
void ME_PropagateCharOffset(ME_DisplayItem *p, int shift)
|
||||
{
|
||||
if (p->type == diRun) /* propagate in all runs in this para */
|
||||
{
|
||||
TRACE("PropagateCharOffset(%s, %d)\n", debugstr_w(p->member.run.strText->szData), shift);
|
||||
do {
|
||||
p->member.run.nCharOfs += shift;
|
||||
assert(p->member.run.nCharOfs >= 0);
|
||||
p = ME_FindItemFwd(p, diRunOrParagraphOrEnd);
|
||||
} while(p->type == diRun);
|
||||
}
|
||||
if (p->type == diParagraph) /* propagate in all next paras */
|
||||
{
|
||||
do {
|
||||
p->member.para.nCharOfs += shift;
|
||||
assert(p->member.para.nCharOfs >= 0);
|
||||
p = p->member.para.next_para;
|
||||
} while(p->type == diParagraph);
|
||||
}
|
||||
if (p->type == diTextEnd)
|
||||
{
|
||||
p->member.para.nCharOfs += shift;
|
||||
assert(p->member.para.nCharOfs >= 0);
|
||||
}
|
||||
}
|
||||
|
||||
void ME_CheckCharOffsets(ME_TextEditor *editor)
|
||||
{
|
||||
ME_DisplayItem *p = editor->pBuffer->pFirst;
|
||||
int ofs = 0, ofsp = 0;
|
||||
if(TRACE_ON(richedit))
|
||||
{
|
||||
TRACE("---\n");
|
||||
ME_DumpDocument(editor->pBuffer);
|
||||
}
|
||||
do {
|
||||
p = ME_FindItemFwd(p, diRunOrParagraphOrEnd);
|
||||
switch(p->type) {
|
||||
case diTextEnd:
|
||||
TRACE("tend, real ofsp = %d, counted = %d\n", p->member.para.nCharOfs, ofsp+ofs);
|
||||
assert(ofsp+ofs == p->member.para.nCharOfs);
|
||||
return;
|
||||
case diParagraph:
|
||||
TRACE("para, real ofsp = %d, counted = %d\n", p->member.para.nCharOfs, ofsp+ofs);
|
||||
assert(ofsp+ofs == p->member.para.nCharOfs);
|
||||
ofsp = p->member.para.nCharOfs;
|
||||
ofs = 0;
|
||||
break;
|
||||
case diRun:
|
||||
TRACE("run, real ofs = %d (+ofsp = %d), counted = %d, len = %d, txt = \"%s\", flags=%08x, fx&mask = %08lx\n",
|
||||
p->member.run.nCharOfs, p->member.run.nCharOfs+ofsp, ofsp+ofs,
|
||||
p->member.run.strText->nLen, debugstr_w(p->member.run.strText->szData),
|
||||
p->member.run.nFlags,
|
||||
p->member.run.style->fmt.dwMask & p->member.run.style->fmt.dwEffects);
|
||||
assert(ofs == p->member.run.nCharOfs);
|
||||
ofs += ME_StrLen(p->member.run.strText);
|
||||
break;
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
} while(1);
|
||||
}
|
||||
|
||||
int ME_CharOfsFromRunOfs(ME_TextEditor *editor, ME_DisplayItem *pRun, int nOfs)
|
||||
{
|
||||
ME_DisplayItem *pPara;
|
||||
|
||||
assert(pRun->type == diRun);
|
||||
assert(pRun->member.run.nCharOfs != -1);
|
||||
|
||||
pPara = ME_FindItemBack(pRun, diParagraph);
|
||||
assert(pPara);
|
||||
assert(pPara->type==diParagraph);
|
||||
return pPara->member.para.nCharOfs + pRun->member.run.nCharOfs
|
||||
+ ME_VPosToPos(pRun->member.run.strText, nOfs);
|
||||
}
|
||||
|
||||
void ME_CursorFromCharOfs(ME_TextEditor *editor, int nCharOfs, ME_Cursor *pCursor)
|
||||
{
|
||||
ME_RunOfsFromCharOfs(editor, nCharOfs, &pCursor->pRun, &pCursor->nOffset);
|
||||
}
|
||||
|
||||
void ME_RunOfsFromCharOfs(ME_TextEditor *editor, int nCharOfs, ME_DisplayItem **ppRun, int *pOfs)
|
||||
{
|
||||
ME_DisplayItem *pPara;
|
||||
int nParaOfs;
|
||||
|
||||
pPara = editor->pBuffer->pFirst->member.para.next_para;
|
||||
assert(pPara);
|
||||
assert(ppRun);
|
||||
assert(pOfs);
|
||||
while (pPara->type == diParagraph)
|
||||
{
|
||||
nParaOfs = pPara->member.para.nCharOfs;
|
||||
assert(nCharOfs >= nParaOfs);
|
||||
|
||||
if (nCharOfs < pPara->member.para.next_para->member.para.nCharOfs)
|
||||
{
|
||||
*ppRun = ME_FindItemFwd(pPara, diRun);
|
||||
assert(*ppRun);
|
||||
while (!((*ppRun)->member.run.nFlags & MERF_ENDPARA))
|
||||
{
|
||||
ME_DisplayItem *pNext = ME_FindItemFwd(*ppRun, diRun);
|
||||
assert(pNext);
|
||||
assert(pNext->type == diRun);
|
||||
if (nCharOfs < nParaOfs + pNext->member.run.nCharOfs) {
|
||||
*pOfs = ME_PosToVPos((*ppRun)->member.run.strText,
|
||||
nCharOfs - nParaOfs - (*ppRun)->member.run.nCharOfs);
|
||||
return;
|
||||
}
|
||||
*ppRun = pNext;
|
||||
}
|
||||
if (nCharOfs == nParaOfs + (*ppRun)->member.run.nCharOfs) {
|
||||
*pOfs = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
pPara = pPara->member.para.next_para;
|
||||
}
|
||||
*ppRun = ME_FindItemBack(editor->pBuffer->pLast, diRun);
|
||||
*pOfs = 0;
|
||||
assert((*ppRun)->member.run.nFlags & MERF_ENDPARA);
|
||||
}
|
||||
|
||||
void ME_JoinRuns(ME_TextEditor *editor, ME_DisplayItem *p)
|
||||
{
|
||||
ME_DisplayItem *pNext = p->next;
|
||||
int i;
|
||||
assert(p->type == diRun && pNext->type == diRun);
|
||||
assert(p->member.run.nCharOfs != -1);
|
||||
|
||||
for (i=0; i<editor->nCursors; i++) {
|
||||
if (editor->pCursors[i].pRun == pNext) {
|
||||
editor->pCursors[i].pRun = p;
|
||||
editor->pCursors[i].nOffset += ME_StrVLen(p->member.run.strText);
|
||||
}
|
||||
}
|
||||
|
||||
ME_AppendString(p->member.run.strText, pNext->member.run.strText);
|
||||
ME_Remove(pNext);
|
||||
ME_DestroyDisplayItem(pNext);
|
||||
ME_UpdateRunFlags(editor, &p->member.run);
|
||||
if(TRACE_ON(richedit))
|
||||
{
|
||||
TRACE("Before check after join\n");
|
||||
ME_CheckCharOffsets(editor);
|
||||
TRACE("After check after join\n");
|
||||
}
|
||||
}
|
||||
|
||||
ME_DisplayItem *ME_SplitRun(ME_Context *c, ME_DisplayItem *item, int nVChar)
|
||||
{
|
||||
ME_TextEditor *editor = c->editor;
|
||||
ME_DisplayItem *item2 = NULL;
|
||||
ME_Run *run, *run2;
|
||||
|
||||
assert(item->member.run.nCharOfs != -1);
|
||||
if(TRACE_ON(richedit))
|
||||
{
|
||||
TRACE("Before check before split\n");
|
||||
ME_CheckCharOffsets(editor);
|
||||
TRACE("After check before split\n");
|
||||
}
|
||||
|
||||
run = &item->member.run;
|
||||
|
||||
TRACE("Before split: %s(%ld, %ld)\n", debugstr_w(run->strText->szData),
|
||||
run->pt.x, run->pt.y);
|
||||
|
||||
item2 = ME_SplitRunSimple(editor, item, nVChar);
|
||||
|
||||
run2 = &item2->member.run;
|
||||
|
||||
ME_CalcRunExtent(c, run);
|
||||
ME_CalcRunExtent(c, run2);
|
||||
|
||||
run2->pt.x = run->pt.x+run->nWidth;
|
||||
run2->pt.y = run->pt.y;
|
||||
|
||||
if(TRACE_ON(richedit))
|
||||
{
|
||||
TRACE("Before check after split\n");
|
||||
ME_CheckCharOffsets(editor);
|
||||
TRACE("After check after split\n");
|
||||
TRACE("After split: %s(%ld, %ld), %s(%ld, %ld)\n",
|
||||
debugstr_w(run->strText->szData), run->pt.x, run->pt.y,
|
||||
debugstr_w(run2->strText->szData), run2->pt.x, run2->pt.y);
|
||||
}
|
||||
|
||||
return item2;
|
||||
}
|
||||
|
||||
/* split a run starting from voffset */
|
||||
ME_DisplayItem *ME_SplitRunSimple(ME_TextEditor *editor, ME_DisplayItem *item, int nVChar)
|
||||
{
|
||||
ME_Run *run = &item->member.run;
|
||||
ME_DisplayItem *item2;
|
||||
ME_Run *run2;
|
||||
int i;
|
||||
assert(nVChar > 0 && nVChar < ME_StrVLen(run->strText));
|
||||
assert(item->type == diRun);
|
||||
assert(!(item->member.run.nFlags & MERF_GRAPHICS));
|
||||
assert(item->member.run.nCharOfs != -1);
|
||||
|
||||
item2 = ME_MakeRun(run->style,
|
||||
ME_VSplitString(run->strText, nVChar), run->nFlags&MERF_SPLITMASK);
|
||||
|
||||
item2->member.run.nCharOfs = item->member.run.nCharOfs+
|
||||
ME_VPosToPos(item->member.run.strText, nVChar);
|
||||
|
||||
run2 = &item2->member.run;
|
||||
ME_InsertBefore(item->next, item2);
|
||||
|
||||
ME_UpdateRunFlags(editor, run);
|
||||
ME_UpdateRunFlags(editor, run2);
|
||||
for (i=0; i<editor->nCursors; i++) {
|
||||
if (editor->pCursors[i].pRun == item &&
|
||||
editor->pCursors[i].nOffset >= nVChar) {
|
||||
assert(item2->type == diRun);
|
||||
editor->pCursors[i].pRun = item2;
|
||||
editor->pCursors[i].nOffset -= nVChar;
|
||||
}
|
||||
}
|
||||
ME_GetParagraph(item)->member.para.nFlags &= ~MEPF_WRAPPED;
|
||||
return item2;
|
||||
}
|
||||
|
||||
/* split the start and final whitespace into separate runs */
|
||||
/* returns the last run added */
|
||||
/*
|
||||
ME_DisplayItem *ME_SplitFurther(ME_TextEditor *editor, ME_DisplayItem *item)
|
||||
{
|
||||
int i, nVLen, nChanged;
|
||||
assert(item->type == diRun);
|
||||
assert(!(item->member.run.nFlags & MERF_GRAPHICS));
|
||||
return item;
|
||||
}
|
||||
*/
|
||||
|
||||
ME_DisplayItem *ME_MakeRun(ME_Style *s, ME_String *strData, int nFlags)
|
||||
{
|
||||
ME_DisplayItem *item = ME_MakeDI(diRun);
|
||||
item->member.run.style = s;
|
||||
item->member.run.strText = strData;
|
||||
item->member.run.nFlags = nFlags;
|
||||
item->member.run.nCharOfs = -1;
|
||||
ME_AddRefStyle(s);
|
||||
return item;
|
||||
}
|
||||
|
||||
ME_DisplayItem *ME_InsertRun(ME_TextEditor *editor, int nCharOfs, ME_DisplayItem *pItem)
|
||||
{
|
||||
ME_Cursor tmp;
|
||||
ME_DisplayItem *pDI;
|
||||
ME_UndoItem *pUI;
|
||||
|
||||
assert(pItem->type == diRun || pItem->type == diUndoInsertRun);
|
||||
|
||||
pUI = ME_AddUndoItem(editor, diUndoDeleteRun, NULL);
|
||||
pUI->nStart = nCharOfs;
|
||||
pUI->nLen = pItem->member.run.strText->nLen;
|
||||
ME_CursorFromCharOfs(editor, nCharOfs, &tmp);
|
||||
if (tmp.nOffset) {
|
||||
tmp.pRun = ME_SplitRunSimple(editor, tmp.pRun, tmp.nOffset);
|
||||
tmp.nOffset = 0;
|
||||
}
|
||||
pDI = ME_MakeRun(pItem->member.run.style, ME_StrDup(pItem->member.run.strText), pItem->member.run.nFlags);
|
||||
pDI->member.run.nCharOfs = tmp.pRun->member.run.nCharOfs;
|
||||
ME_InsertBefore(tmp.pRun, pDI);
|
||||
TRACE("Shift length:%d\n", pDI->member.run.strText->nLen);
|
||||
ME_PropagateCharOffset(tmp.pRun, pDI->member.run.strText->nLen);
|
||||
ME_GetParagraph(tmp.pRun)->member.para.nFlags &= ~MEPF_WRAPPED;
|
||||
|
||||
return pDI;
|
||||
}
|
||||
|
||||
static inline int ME_IsWSpace(WCHAR ch)
|
||||
{
|
||||
return ch <= ' ';
|
||||
}
|
||||
|
||||
void ME_UpdateRunFlags(ME_TextEditor *editor, ME_Run *run)
|
||||
{
|
||||
assert(run->nCharOfs != -1);
|
||||
if (ME_IsSplitable(run->strText))
|
||||
run->nFlags |= MERF_SPLITTABLE;
|
||||
else
|
||||
run->nFlags &= ~MERF_SPLITTABLE;
|
||||
|
||||
if (!(run->nFlags & MERF_GRAPHICS)) {
|
||||
if (ME_IsWhitespaces(run->strText))
|
||||
run->nFlags |= MERF_WHITESPACE | MERF_STARTWHITE | MERF_ENDWHITE;
|
||||
else
|
||||
{
|
||||
run->nFlags &= ~MERF_WHITESPACE;
|
||||
|
||||
if (ME_IsWSpace(ME_GetCharFwd(run->strText,0)))
|
||||
run->nFlags |= MERF_STARTWHITE;
|
||||
else
|
||||
run->nFlags &= ~MERF_STARTWHITE;
|
||||
|
||||
if (ME_IsWSpace(ME_GetCharBack(run->strText,0)))
|
||||
run->nFlags |= MERF_ENDWHITE;
|
||||
else
|
||||
run->nFlags &= ~MERF_ENDWHITE;
|
||||
}
|
||||
}
|
||||
else
|
||||
run->nFlags &= ~(MERF_WHITESPACE | MERF_STARTWHITE | MERF_ENDWHITE);
|
||||
}
|
||||
|
||||
void ME_GetGraphicsSize(ME_TextEditor *editor, ME_Run *run, SIZE *pSize)
|
||||
{
|
||||
assert(run->nFlags & MERF_GRAPHICS);
|
||||
pSize->cx = 64;
|
||||
pSize->cy = 64;
|
||||
}
|
||||
|
||||
int ME_CharFromPoint(ME_TextEditor *editor, int cx, ME_Run *run)
|
||||
{
|
||||
int fit = 0;
|
||||
HGDIOBJ hOldFont;
|
||||
HDC hDC;
|
||||
SIZE sz;
|
||||
if (!run->strText->nLen)
|
||||
return 0;
|
||||
|
||||
if (run->nFlags & MERF_GRAPHICS)
|
||||
{
|
||||
SIZE sz;
|
||||
ME_GetGraphicsSize(editor, run, &sz);
|
||||
if (cx < sz.cx)
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
hDC = GetDC(editor->hWnd);
|
||||
hOldFont = ME_SelectStyleFont(hDC, run->style);
|
||||
GetTextExtentExPointW(hDC, run->strText->szData, run->strText->nLen,
|
||||
cx, &fit, NULL, &sz);
|
||||
assert(run->style->hFont);
|
||||
SelectObject(hDC, hOldFont);
|
||||
ReleaseDC(editor->hWnd, hDC);
|
||||
return fit;
|
||||
}
|
||||
|
||||
int ME_CharFromPointCursor(ME_TextEditor *editor, int cx, ME_Run *run)
|
||||
{
|
||||
int fit = 0, fit1 = 0;
|
||||
HGDIOBJ hOldFont;
|
||||
HDC hDC;
|
||||
SIZE sz, sz2, sz3;
|
||||
if (!run->strText->nLen)
|
||||
return 0;
|
||||
|
||||
if (run->nFlags & MERF_GRAPHICS)
|
||||
{
|
||||
SIZE sz;
|
||||
ME_GetGraphicsSize(editor, run, &sz);
|
||||
if (cx < sz.cx/2)
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
hDC = GetDC(editor->hWnd);
|
||||
hOldFont = ME_SelectStyleFont(hDC, run->style);
|
||||
GetTextExtentExPointW(hDC, run->strText->szData, run->strText->nLen,
|
||||
cx, &fit, NULL, &sz);
|
||||
if (fit != run->strText->nLen)
|
||||
{
|
||||
int chars = 1;
|
||||
|
||||
GetTextExtentPoint32W(hDC, run->strText->szData, fit, &sz2);
|
||||
fit1 = ME_StrRelPos(run->strText, fit, &chars);
|
||||
GetTextExtentPoint32W(hDC, run->strText->szData, fit1, &sz3);
|
||||
if (cx >= (sz2.cx+sz3.cx)/2)
|
||||
fit = fit1;
|
||||
}
|
||||
SelectObject(hDC, hOldFont);
|
||||
ReleaseDC(editor->hWnd, hDC);
|
||||
return fit;
|
||||
}
|
||||
|
||||
int ME_PointFromChar(ME_TextEditor *editor, ME_Run *pRun, int nOffset)
|
||||
{
|
||||
SIZE size;
|
||||
HDC hDC = GetDC(editor->hWnd);
|
||||
HGDIOBJ hOldFont;
|
||||
|
||||
if (pRun->nFlags & MERF_GRAPHICS)
|
||||
{
|
||||
if (!nOffset) return 0;
|
||||
ME_GetGraphicsSize(editor, pRun, &size);
|
||||
return 1;
|
||||
}
|
||||
hOldFont = ME_SelectStyleFont(hDC, pRun->style);
|
||||
GetTextExtentPoint32W(hDC, pRun->strText->szData, nOffset, &size);
|
||||
SelectObject(hDC, hOldFont);
|
||||
ReleaseDC(editor->hWnd, hDC);
|
||||
return size.cx;
|
||||
}
|
||||
|
||||
void ME_GetTextExtent(ME_Context *c, LPCWSTR szText, int nChars, ME_Style *s,
|
||||
SIZE *size)
|
||||
{
|
||||
HDC hDC = c->hDC;
|
||||
HGDIOBJ hOldFont;
|
||||
hOldFont = ME_SelectStyleFont(hDC, s);
|
||||
GetTextExtentPoint32W(hDC, szText, nChars, size);
|
||||
SelectObject(hDC, hOldFont);
|
||||
}
|
||||
|
||||
SIZE ME_GetRunSize(ME_Context *c, ME_Run *run, int nLen)
|
||||
{
|
||||
SIZE size;
|
||||
int nMaxLen = ME_StrVLen(run->strText);
|
||||
|
||||
if (nLen>nMaxLen)
|
||||
nLen = nMaxLen;
|
||||
|
||||
if (run->nFlags & MERF_GRAPHICS)
|
||||
{
|
||||
ME_GetGraphicsSize(c->editor, run, &size);
|
||||
return size;
|
||||
}
|
||||
|
||||
ME_GetTextExtent(c, run->strText->szData, nLen, run->style, &size);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
void ME_CalcRunExtent(ME_Context *c, ME_Run *run)
|
||||
{
|
||||
SIZE size;
|
||||
int nEnd = ME_StrVLen(run->strText);
|
||||
|
||||
if (run->nFlags & MERF_GRAPHICS) {
|
||||
ME_GetGraphicsSize(c->editor, run, &size);
|
||||
run->nWidth = size.cx;
|
||||
run->nAscent = size.cy;
|
||||
run->nDescent = 0;
|
||||
return;
|
||||
}
|
||||
ME_GetTextExtent(c, run->strText->szData, nEnd, run->style, &size);
|
||||
run->nWidth = size.cx;
|
||||
run->nAscent = run->style->tm.tmAscent;
|
||||
run->nDescent = run->style->tm.tmDescent;
|
||||
}
|
||||
|
||||
void ME_MustBeWrapped(ME_Context *c, ME_DisplayItem *para)
|
||||
{
|
||||
assert(para->type == diParagraph);
|
||||
/* FIXME */
|
||||
}
|
||||
|
||||
void ME_SetSelectionCharFormat(ME_TextEditor *editor, CHARFORMAT2W *pFmt)
|
||||
{
|
||||
int nFrom, nTo;
|
||||
ME_GetSelection(editor, &nFrom, &nTo);
|
||||
if (nFrom == nTo)
|
||||
{
|
||||
ME_Style *s;
|
||||
if (!editor->pBuffer->pCharStyle)
|
||||
editor->pBuffer->pCharStyle = ME_GetInsertStyle(editor, 0);
|
||||
s = ME_ApplyStyle(editor->pBuffer->pCharStyle, pFmt);
|
||||
ME_ReleaseStyle(editor->pBuffer->pCharStyle);
|
||||
editor->pBuffer->pCharStyle = s;
|
||||
}
|
||||
else
|
||||
ME_SetCharFormat(editor, nFrom, nTo-nFrom, pFmt);
|
||||
}
|
||||
|
||||
void ME_SetCharFormat(ME_TextEditor *editor, int nOfs, int nChars, CHARFORMAT2W *pFmt)
|
||||
{
|
||||
ME_Cursor tmp, tmp2;
|
||||
ME_DisplayItem *para;
|
||||
|
||||
ME_CursorFromCharOfs(editor, nOfs, &tmp);
|
||||
if (tmp.nOffset)
|
||||
tmp.pRun = ME_SplitRunSimple(editor, tmp.pRun, tmp.nOffset);
|
||||
|
||||
ME_CursorFromCharOfs(editor, nOfs+nChars, &tmp2);
|
||||
if (tmp2.nOffset)
|
||||
tmp2.pRun = ME_SplitRunSimple(editor, tmp2.pRun, tmp2.nOffset);
|
||||
|
||||
para = ME_GetParagraph(tmp.pRun);
|
||||
para->member.para.nFlags &= ~MEPF_WRAPPED;
|
||||
|
||||
while(tmp.pRun != tmp2.pRun)
|
||||
{
|
||||
ME_UndoItem *undo = NULL;
|
||||
ME_Style *new_style = ME_ApplyStyle(tmp.pRun->member.run.style, pFmt);
|
||||
/* ME_DumpStyle(new_style); */
|
||||
undo = ME_AddUndoItem(editor, diUndoSetCharFormat, NULL);
|
||||
if (undo) {
|
||||
undo->nStart = tmp.pRun->member.run.nCharOfs+para->member.para.nCharOfs;
|
||||
undo->nLen = tmp.pRun->member.run.strText->nLen;
|
||||
undo->di.member.ustyle = tmp.pRun->member.run.style;
|
||||
/* we'd have to addref undo..ustyle and release tmp...style
|
||||
but they'd cancel each other out so we can do nothing instead */
|
||||
}
|
||||
else
|
||||
ME_ReleaseStyle(tmp.pRun->member.run.style);
|
||||
tmp.pRun->member.run.style = new_style;
|
||||
tmp.pRun = ME_FindItemFwd(tmp.pRun, diRunOrParagraph);
|
||||
if (tmp.pRun->type == diParagraph)
|
||||
{
|
||||
para = tmp.pRun;
|
||||
tmp.pRun = ME_FindItemFwd(tmp.pRun, diRun);
|
||||
if (tmp.pRun != tmp2.pRun)
|
||||
para->member.para.nFlags &= ~MEPF_WRAPPED;
|
||||
}
|
||||
assert(tmp.pRun);
|
||||
}
|
||||
}
|
||||
|
||||
void ME_SetDefaultCharFormat(ME_TextEditor *editor, CHARFORMAT2W *mod)
|
||||
{
|
||||
ME_Style *style;
|
||||
ME_UndoItem *undo;
|
||||
|
||||
assert(mod->cbSize == sizeof(CHARFORMAT2W));
|
||||
undo = ME_AddUndoItem(editor, diUndoSetDefaultCharFormat, NULL);
|
||||
if (undo) {
|
||||
undo->nStart = -1;
|
||||
undo->nLen = -1;
|
||||
undo->di.member.ustyle = editor->pBuffer->pDefaultStyle;
|
||||
ME_AddRefStyle(undo->di.member.ustyle);
|
||||
}
|
||||
style = ME_ApplyStyle(editor->pBuffer->pDefaultStyle, mod);
|
||||
editor->pBuffer->pDefaultStyle->fmt = style->fmt;
|
||||
editor->pBuffer->pDefaultStyle->tm = style->tm;
|
||||
ME_ReleaseStyle(style);
|
||||
ME_MarkAllForWrapping(editor);
|
||||
/* pcf = editor->pBuffer->pDefaultStyle->fmt; */
|
||||
}
|
||||
|
||||
void ME_GetRunCharFormat(ME_TextEditor *editor, ME_DisplayItem *run, CHARFORMAT2W *pFmt)
|
||||
{
|
||||
ME_CopyCharFormat(pFmt, &run->member.run.style->fmt);
|
||||
}
|
||||
|
||||
void ME_GetDefaultCharFormat(ME_TextEditor *editor, CHARFORMAT2W *pFmt)
|
||||
{
|
||||
int nFrom, nTo;
|
||||
ME_GetSelection(editor, &nFrom, &nTo);
|
||||
ME_CopyCharFormat(pFmt, &editor->pBuffer->pDefaultStyle->fmt);
|
||||
}
|
||||
|
||||
void ME_GetSelectionCharFormat(ME_TextEditor *editor, CHARFORMAT2W *pFmt)
|
||||
{
|
||||
int nFrom, nTo;
|
||||
ME_GetSelection(editor, &nFrom, &nTo);
|
||||
if (nFrom == nTo && editor->pBuffer->pCharStyle)
|
||||
{
|
||||
ME_CopyCharFormat(pFmt, &editor->pBuffer->pCharStyle->fmt);
|
||||
return;
|
||||
}
|
||||
ME_GetCharFormat(editor, nFrom, nTo, pFmt);
|
||||
}
|
||||
|
||||
void ME_GetCharFormat(ME_TextEditor *editor, int nFrom, int nTo, CHARFORMAT2W *pFmt)
|
||||
{
|
||||
ME_DisplayItem *run, *run_end;
|
||||
int nOffset, nOffset2;
|
||||
CHARFORMAT2W tmp;
|
||||
|
||||
if (nTo>nFrom) /* selection consists of chars from nFrom up to nTo-1 */
|
||||
nTo--;
|
||||
|
||||
ME_RunOfsFromCharOfs(editor, nFrom, &run, &nOffset);
|
||||
if (nFrom == nTo) /* special case - if selection is empty, take previous char's formatting */
|
||||
{
|
||||
if (!nOffset)
|
||||
{
|
||||
ME_DisplayItem *tmp_run = ME_FindItemBack(run, diRunOrParagraph);
|
||||
if (tmp_run->type == diRun) {
|
||||
ME_GetRunCharFormat(editor, tmp_run, pFmt);
|
||||
return;
|
||||
}
|
||||
}
|
||||
ME_GetRunCharFormat(editor, run, pFmt);
|
||||
return;
|
||||
}
|
||||
ME_RunOfsFromCharOfs(editor, nTo, &run_end, &nOffset2);
|
||||
|
||||
ME_GetRunCharFormat(editor, run, pFmt);
|
||||
|
||||
if (run == run_end) return;
|
||||
|
||||
do {
|
||||
/* FIXME add more style feature comparisons */
|
||||
int nAttribs = CFM_SIZE | CFM_FACE | CFM_COLOR;
|
||||
int nEffects = CFM_BOLD | CFM_ITALIC | CFM_UNDERLINE;
|
||||
|
||||
run = ME_FindItemFwd(run, diRun);
|
||||
|
||||
ZeroMemory(&tmp, sizeof(tmp));
|
||||
tmp.cbSize = sizeof(tmp);
|
||||
ME_GetRunCharFormat(editor, run, &tmp);
|
||||
|
||||
assert((tmp.dwMask & nAttribs) == nAttribs);
|
||||
assert((tmp.dwMask & nEffects) == nEffects);
|
||||
/* reset flags that differ */
|
||||
|
||||
if (pFmt->yHeight != tmp.yHeight)
|
||||
pFmt->dwMask &= ~CFM_SIZE;
|
||||
if (pFmt->dwMask & CFM_FACE)
|
||||
{
|
||||
if (!(tmp.dwMask & CFM_FACE))
|
||||
pFmt->dwMask &= ~CFM_FACE;
|
||||
else if (lstrcmpW(pFmt->szFaceName, tmp.szFaceName))
|
||||
pFmt->dwMask &= ~CFM_FACE;
|
||||
}
|
||||
if (pFmt->yHeight != tmp.yHeight)
|
||||
pFmt->dwMask &= ~CFM_SIZE;
|
||||
if (pFmt->dwMask & CFM_COLOR)
|
||||
{
|
||||
if (!((pFmt->dwEffects&CFE_AUTOCOLOR) & (tmp.dwEffects&CFE_AUTOCOLOR)))
|
||||
{
|
||||
if (pFmt->crTextColor != tmp.crTextColor)
|
||||
pFmt->dwMask &= ~CFM_COLOR;
|
||||
}
|
||||
}
|
||||
|
||||
pFmt->dwMask &= ~((pFmt->dwEffects ^ tmp.dwEffects) & nEffects);
|
||||
|
||||
} while(run != run_end);
|
||||
}
|
|
@ -0,0 +1,318 @@
|
|||
/*
|
||||
* RichEdit - string operations
|
||||
*
|
||||
* Copyright 2004 by Krzysztof Foltman
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "editor.h"
|
||||
|
||||
WINE_DEFAULT_DEBUG_CHANNEL(richedit);
|
||||
|
||||
int ME_GetOptimalBuffer(int nLen)
|
||||
{
|
||||
return ((2*nLen+1)+128)&~63;
|
||||
}
|
||||
|
||||
ME_String *ME_MakeString(LPCWSTR szText)
|
||||
{
|
||||
ME_String *s = ALLOC_OBJ(ME_String);
|
||||
s->nLen = lstrlenW(szText);
|
||||
s->nBuffer = ME_GetOptimalBuffer(s->nLen+1);
|
||||
s->szData = ALLOC_N_OBJ(WCHAR, s->nBuffer);
|
||||
lstrcpyW(s->szData, szText);
|
||||
return s;
|
||||
}
|
||||
|
||||
ME_String *ME_MakeStringN(LPCWSTR szText, int nMaxChars)
|
||||
{
|
||||
ME_String *s = ALLOC_OBJ(ME_String);
|
||||
int i;
|
||||
for (i=0; i<nMaxChars && szText[i]; i++)
|
||||
;
|
||||
s->nLen = i;
|
||||
s->nBuffer = ME_GetOptimalBuffer(s->nLen+1);
|
||||
s->szData = ALLOC_N_OBJ(WCHAR, s->nBuffer);
|
||||
lstrcpynW(s->szData, szText, s->nLen+1);
|
||||
return s;
|
||||
}
|
||||
|
||||
ME_String *ME_StrDup(ME_String *s)
|
||||
{
|
||||
return ME_MakeStringN(s->szData, s->nLen);
|
||||
}
|
||||
|
||||
void ME_DestroyString(ME_String *s)
|
||||
{
|
||||
FREE_OBJ(s->szData);
|
||||
FREE_OBJ(s);
|
||||
}
|
||||
|
||||
void ME_AppendString(ME_String *s1, ME_String *s2)
|
||||
{
|
||||
if (s1->nLen+s2->nLen+1 <= s1->nBuffer) {
|
||||
lstrcpyW(s1->szData+s1->nLen, s2->szData);
|
||||
s1->nLen += s2->nLen;
|
||||
}
|
||||
else
|
||||
{
|
||||
WCHAR *buf;
|
||||
s1->nBuffer = ME_GetOptimalBuffer(s1->nLen+s2->nLen+1);
|
||||
|
||||
buf = ALLOC_N_OBJ(WCHAR, s1->nBuffer);
|
||||
lstrcpyW(buf, s1->szData);
|
||||
lstrcpyW(buf+s1->nLen, s2->szData);
|
||||
FREE_OBJ(s1->szData);
|
||||
s1->szData = buf;
|
||||
s1->nLen += s2->nLen;
|
||||
}
|
||||
}
|
||||
|
||||
ME_String *ME_ConcatString(ME_String *s1, ME_String *s2)
|
||||
{
|
||||
ME_String *s = ALLOC_OBJ(ME_String);
|
||||
s->nLen = s1->nLen+s2->nLen;
|
||||
s->nBuffer = ME_GetOptimalBuffer(s1->nLen+s2->nLen+1);
|
||||
s->szData = ALLOC_N_OBJ(WCHAR, s->nBuffer);
|
||||
lstrcpyW(s->szData, s1->szData);
|
||||
lstrcpyW(s->szData+s1->nLen, s2->szData);
|
||||
return s;
|
||||
}
|
||||
|
||||
ME_String *ME_VSplitString(ME_String *orig, int charidx)
|
||||
{
|
||||
ME_String *s;
|
||||
|
||||
/*if (charidx<0) charidx = 0;
|
||||
if (charidx>orig->nLen) charidx = orig->nLen;
|
||||
*/
|
||||
assert(charidx>=0);
|
||||
assert(charidx<=orig->nLen);
|
||||
|
||||
s = ME_MakeString(orig->szData+charidx);
|
||||
orig->nLen = charidx;
|
||||
orig->szData[charidx] = L'\0';
|
||||
return s;
|
||||
}
|
||||
|
||||
int ME_IsWhitespaces(ME_String *s)
|
||||
{
|
||||
/* FIXME multibyte */
|
||||
WCHAR *pos = s->szData;
|
||||
while(*pos++ == ' ')
|
||||
;
|
||||
pos--;
|
||||
if (*pos)
|
||||
return 0;
|
||||
else
|
||||
return 1;
|
||||
}
|
||||
|
||||
int ME_IsSplitable(ME_String *s)
|
||||
{
|
||||
/* FIXME multibyte */
|
||||
WCHAR *pos = s->szData;
|
||||
WCHAR ch;
|
||||
while(*pos++ == L' ')
|
||||
;
|
||||
pos--;
|
||||
while((ch = *pos++) != 0)
|
||||
{
|
||||
if (ch == L' ')
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* FIXME multibyte */
|
||||
/*
|
||||
int ME_CalcSkipChars(ME_String *s)
|
||||
{
|
||||
int cnt = 0;
|
||||
while(cnt < s->nLen && s->szData[s->nLen-1-cnt]==' ')
|
||||
cnt++;
|
||||
return cnt;
|
||||
}
|
||||
*/
|
||||
|
||||
int ME_StrLen(ME_String *s) {
|
||||
return s->nLen;
|
||||
}
|
||||
|
||||
int ME_StrVLen(ME_String *s) {
|
||||
return s->nLen;
|
||||
}
|
||||
|
||||
/* FIXME we use widechars, not multibytes, inside, no need for complex logic anymore */
|
||||
int ME_StrRelPos(ME_String *s, int nVChar, int *pRelChars)
|
||||
{
|
||||
TRACE("%s,%d,&%d\n", debugstr_w(s->szData), nVChar, *pRelChars);
|
||||
|
||||
assert(*pRelChars);
|
||||
if (!*pRelChars) return nVChar;
|
||||
|
||||
if (*pRelChars>0)
|
||||
{
|
||||
while(nVChar<s->nLen && *pRelChars>0)
|
||||
{
|
||||
nVChar++;
|
||||
(*pRelChars)--;
|
||||
}
|
||||
return nVChar;
|
||||
}
|
||||
|
||||
while(nVChar>0 && *pRelChars<0)
|
||||
{
|
||||
nVChar--;
|
||||
(*pRelChars)++;
|
||||
}
|
||||
return nVChar;
|
||||
}
|
||||
|
||||
int ME_StrRelPos2(ME_String *s, int nVChar, int nRelChars)
|
||||
{
|
||||
return ME_StrRelPos(s, nVChar, &nRelChars);
|
||||
}
|
||||
|
||||
int ME_VPosToPos(ME_String *s, int nVPos)
|
||||
{
|
||||
return nVPos;
|
||||
/*
|
||||
int i = 0, len = 0;
|
||||
if (!nVPos)
|
||||
return 0;
|
||||
while (i < s->nLen)
|
||||
{
|
||||
if (i == nVPos)
|
||||
return len;
|
||||
if (s->szData[i]=='\\') i++;
|
||||
i++;
|
||||
len++;
|
||||
}
|
||||
return len;
|
||||
*/
|
||||
}
|
||||
|
||||
int ME_PosToVPos(ME_String *s, int nPos)
|
||||
{
|
||||
if (!nPos)
|
||||
return 0;
|
||||
return ME_StrRelPos2(s, 0, nPos);
|
||||
}
|
||||
|
||||
void ME_StrDeleteV(ME_String *s, int nVChar, int nChars)
|
||||
{
|
||||
int end_ofs;
|
||||
|
||||
assert(nVChar >=0 && nVChar <= s->nLen);
|
||||
assert(nChars >= 0);
|
||||
assert(nVChar+nChars <= s->nLen);
|
||||
|
||||
end_ofs = ME_StrRelPos2(s, nVChar, nChars);
|
||||
assert(end_ofs <= s->nLen);
|
||||
memmove(s->szData+nVChar, s->szData+end_ofs, 2*(s->nLen+1-end_ofs));
|
||||
s->nLen -= (end_ofs - nVChar);
|
||||
}
|
||||
|
||||
int ME_GetCharFwd(ME_String *s, int nPos)
|
||||
{
|
||||
int nVPos = 0;
|
||||
|
||||
assert(nPos < ME_StrLen(s));
|
||||
if (nPos)
|
||||
nVPos = ME_StrRelPos2(s, nVPos, nPos);
|
||||
|
||||
if (nVPos < s->nLen)
|
||||
return s->szData[nVPos];
|
||||
return -1;
|
||||
}
|
||||
|
||||
int ME_GetCharBack(ME_String *s, int nPos)
|
||||
{
|
||||
int nVPos = ME_StrVLen(s);
|
||||
|
||||
assert(nPos < ME_StrLen(s));
|
||||
if (nPos)
|
||||
nVPos = ME_StrRelPos2(s, nVPos, -nPos);
|
||||
|
||||
if (nVPos < s->nLen)
|
||||
return s->szData[nVPos];
|
||||
return -1;
|
||||
}
|
||||
|
||||
int ME_FindNonWhitespaceV(ME_String *s, int nVChar) {
|
||||
int i;
|
||||
for (i = nVChar; isspace(s->szData[i]) && i<s->nLen; i++)
|
||||
;
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
/* note: returns offset of the first trailing whitespace */
|
||||
int ME_ReverseFindNonWhitespaceV(ME_String *s, int nVChar) {
|
||||
int i;
|
||||
for (i = nVChar; i>0 && isspace(s->szData[i-1]); i--)
|
||||
;
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
/* note: returns offset of the first trailing nonwhitespace */
|
||||
int ME_ReverseFindWhitespaceV(ME_String *s, int nVChar) {
|
||||
int i;
|
||||
for (i = nVChar; i>0 && !isspace(s->szData[i-1]); i--)
|
||||
;
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
LPWSTR ME_ToUnicode(HWND hWnd, LPVOID psz)
|
||||
{
|
||||
if (IsWindowUnicode(hWnd))
|
||||
return (LPWSTR)psz;
|
||||
else {
|
||||
WCHAR *tmp;
|
||||
int nChars = MultiByteToWideChar(CP_ACP, 0, (char *)psz, -1, NULL, 0);
|
||||
if((tmp = ALLOC_N_OBJ(WCHAR, nChars)) != NULL)
|
||||
MultiByteToWideChar(CP_ACP, 0, (char *)psz, -1, tmp, nChars);
|
||||
return tmp;
|
||||
}
|
||||
}
|
||||
|
||||
void ME_EndToUnicode(HWND hWnd, LPVOID psz)
|
||||
{
|
||||
if (IsWindowUnicode(hWnd))
|
||||
FREE_OBJ(psz);
|
||||
}
|
||||
|
||||
LPSTR ME_ToAnsi(HWND hWnd, LPVOID psz)
|
||||
{
|
||||
if (!IsWindowUnicode(hWnd))
|
||||
return (LPSTR)psz;
|
||||
else {
|
||||
char *tmp;
|
||||
int nChars = WideCharToMultiByte(CP_ACP, 0, (WCHAR *)psz, -1, NULL, 0, NULL, NULL);
|
||||
if((tmp = ALLOC_N_OBJ(char, nChars)) != NULL)
|
||||
WideCharToMultiByte(CP_ACP, 0, (WCHAR *)psz, -1, tmp, nChars, NULL, NULL);
|
||||
return tmp;
|
||||
}
|
||||
}
|
||||
|
||||
void ME_EndToAnsi(HWND hWnd, LPVOID psz)
|
||||
{
|
||||
if (!IsWindowUnicode(hWnd))
|
||||
FREE_OBJ(psz);
|
||||
}
|
|
@ -0,0 +1,397 @@
|
|||
/*
|
||||
* RichEdit style management functions
|
||||
*
|
||||
* Copyright 2004 by Krzysztof Foltman
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "editor.h"
|
||||
|
||||
WINE_DEFAULT_DEBUG_CHANNEL(richedit);
|
||||
|
||||
static int all_refs = 0;
|
||||
|
||||
CHARFORMAT2W *ME_ToCF2W(CHARFORMAT2W *to, CHARFORMAT2W *from)
|
||||
{
|
||||
if (from->cbSize == sizeof(CHARFORMATA))
|
||||
{
|
||||
CHARFORMATA *f = (CHARFORMATA *)from;
|
||||
CopyMemory(to, f, sizeof(*f)-sizeof(f->szFaceName));
|
||||
to->cbSize = sizeof(CHARFORMAT2W);
|
||||
if (f->dwMask & CFM_FACE) {
|
||||
MultiByteToWideChar(0, 0, f->szFaceName, -1, to->szFaceName, sizeof(to->szFaceName)/sizeof(WCHAR));
|
||||
}
|
||||
return to;
|
||||
}
|
||||
if (from->cbSize == sizeof(CHARFORMATW))
|
||||
{
|
||||
CHARFORMATW *f = (CHARFORMATW *)from;
|
||||
CopyMemory(to, f, sizeof(*f));
|
||||
/* theoretically, we don't need to zero the remaining memory */
|
||||
ZeroMemory(((CHARFORMATW *)to)+1, sizeof(CHARFORMAT2W)-sizeof(CHARFORMATW));
|
||||
to->cbSize = sizeof(CHARFORMAT2W);
|
||||
return to;
|
||||
}
|
||||
if (from->cbSize == sizeof(CHARFORMAT2A))
|
||||
{
|
||||
CHARFORMATA *f = (CHARFORMATA *)from;
|
||||
/* copy the A structure without face name */
|
||||
CopyMemory(to, f, sizeof(CHARFORMATA)-sizeof(f->szFaceName));
|
||||
/* convert face name */
|
||||
if (f->dwMask & CFM_FACE)
|
||||
MultiByteToWideChar(0, 0, f->szFaceName, -1, to->szFaceName, sizeof(to->szFaceName));
|
||||
/* copy the rest of the 2A structure to 2W */
|
||||
CopyMemory(1+((CHARFORMATW *)from), f+1, sizeof(CHARFORMAT2A)-sizeof(CHARFORMATA));
|
||||
to->cbSize = sizeof(CHARFORMAT2W);
|
||||
return to;
|
||||
}
|
||||
|
||||
assert(from->cbSize >= sizeof(CHARFORMAT2W));
|
||||
return from;
|
||||
}
|
||||
|
||||
void ME_CopyToCF2W(CHARFORMAT2W *to, CHARFORMAT2W *from)
|
||||
{
|
||||
if (ME_ToCF2W(to, from) == from)
|
||||
CopyMemory(to, from, sizeof(*from));
|
||||
}
|
||||
|
||||
CHARFORMAT2W *ME_ToCFAny(CHARFORMAT2W *to, CHARFORMAT2W *from)
|
||||
{
|
||||
assert(from->cbSize == sizeof(CHARFORMAT2W));
|
||||
if (to->cbSize == sizeof(CHARFORMATA))
|
||||
{
|
||||
CHARFORMATA *t = (CHARFORMATA *)to;
|
||||
CopyMemory(t, from, sizeof(*t)-sizeof(t->szFaceName));
|
||||
WideCharToMultiByte(0, 0, from->szFaceName, -1, t->szFaceName, sizeof(t->szFaceName), 0, 0);
|
||||
t->cbSize = sizeof(*t); /* it was overwritten by CopyMemory */
|
||||
return to;
|
||||
}
|
||||
if (to->cbSize == sizeof(CHARFORMATW))
|
||||
{
|
||||
CHARFORMATW *t = (CHARFORMATW *)to;
|
||||
CopyMemory(t, from, sizeof(*t));
|
||||
t->cbSize = sizeof(*t); /* it was overwritten by CopyMemory */
|
||||
return to;
|
||||
}
|
||||
if (to->cbSize == sizeof(CHARFORMAT2A))
|
||||
{
|
||||
CHARFORMAT2A *t = (CHARFORMAT2A *)to;
|
||||
/* copy the A structure without face name */
|
||||
CopyMemory(t, from, sizeof(CHARFORMATA)-sizeof(t->szFaceName));
|
||||
/* convert face name */
|
||||
WideCharToMultiByte(0, 0, from->szFaceName, -1, t->szFaceName, sizeof(t->szFaceName), 0, 0);
|
||||
/* copy the rest of the 2A structure to 2W */
|
||||
CopyMemory(&t->wWeight, &from->wWeight, sizeof(CHARFORMAT2A)-sizeof(CHARFORMATA));
|
||||
t->cbSize = sizeof(*t); /* it was overwritten by CopyMemory */
|
||||
return to;
|
||||
}
|
||||
assert(to->cbSize >= sizeof(CHARFORMAT2W));
|
||||
return from;
|
||||
}
|
||||
|
||||
void ME_CopyToCFAny(CHARFORMAT2W *to, CHARFORMAT2W *from)
|
||||
{
|
||||
if (ME_ToCFAny(to, from) == from)
|
||||
CopyMemory(to, from, to->cbSize);
|
||||
}
|
||||
|
||||
ME_Style *ME_MakeStyle(CHARFORMAT2W *style) {
|
||||
CHARFORMAT2W styledata;
|
||||
ME_Style *s = ALLOC_OBJ(ME_Style);
|
||||
|
||||
style = ME_ToCF2W(&styledata, style);
|
||||
memset(s, 0, sizeof(ME_Style));
|
||||
if (style->cbSize <= sizeof(CHARFORMAT2W))
|
||||
CopyMemory(&s->fmt, style, style->cbSize);
|
||||
else
|
||||
CopyMemory(&s->fmt, style, sizeof(CHARFORMAT2W));
|
||||
s->fmt.cbSize = sizeof(CHARFORMAT2W);
|
||||
|
||||
s->nSequence = -2;
|
||||
s->nRefs = 1;
|
||||
s->hFont = NULL;
|
||||
all_refs++;
|
||||
return s;
|
||||
}
|
||||
|
||||
#define COPY_STYLE_ITEM(mask, member) \
|
||||
if (style->dwMask & mask) { \
|
||||
s->fmt.dwMask |= mask;\
|
||||
s->fmt.member = style->member;\
|
||||
}
|
||||
|
||||
#define COPY_STYLE_ITEM_MEMCPY(mask, member) \
|
||||
if (style->dwMask & mask) { \
|
||||
s->fmt.dwMask |= mask;\
|
||||
CopyMemory(s->fmt.member, style->member, sizeof(style->member));\
|
||||
}
|
||||
|
||||
void ME_InitCharFormat2W(CHARFORMAT2W *pFmt)
|
||||
{
|
||||
ZeroMemory(pFmt, sizeof(CHARFORMAT2W));
|
||||
pFmt->cbSize = sizeof(CHARFORMAT2W);
|
||||
}
|
||||
|
||||
ME_Style *ME_ApplyStyle(ME_Style *sSrc, CHARFORMAT2W *style)
|
||||
{
|
||||
CHARFORMAT2W styledata;
|
||||
ME_Style *s = ME_MakeStyle(&sSrc->fmt);
|
||||
style = ME_ToCF2W(&styledata, style);
|
||||
COPY_STYLE_ITEM(CFM_ANIMATION, bAnimation);
|
||||
COPY_STYLE_ITEM(CFM_BACKCOLOR, crBackColor);
|
||||
COPY_STYLE_ITEM(CFM_CHARSET, bCharSet);
|
||||
COPY_STYLE_ITEM(CFM_COLOR, crTextColor);
|
||||
COPY_STYLE_ITEM_MEMCPY(CFM_FACE, szFaceName);
|
||||
COPY_STYLE_ITEM(CFM_KERNING, wKerning);
|
||||
COPY_STYLE_ITEM(CFM_LCID, lcid);
|
||||
COPY_STYLE_ITEM(CFM_OFFSET, yOffset);
|
||||
COPY_STYLE_ITEM(CFM_REVAUTHOR, bRevAuthor);
|
||||
COPY_STYLE_ITEM(CFM_SIZE, yHeight);
|
||||
COPY_STYLE_ITEM(CFM_SPACING, sSpacing);
|
||||
COPY_STYLE_ITEM(CFM_STYLE, sStyle);
|
||||
COPY_STYLE_ITEM(CFM_UNDERLINETYPE, bUnderlineType);
|
||||
COPY_STYLE_ITEM(CFM_WEIGHT, wWeight);
|
||||
|
||||
s->fmt.dwEffects &= ~(style->dwMask);
|
||||
s->fmt.dwEffects |= style->dwEffects & style->dwMask;
|
||||
s->fmt.dwMask |= style->dwMask;
|
||||
if (style->dwMask & CFM_COLOR)
|
||||
{
|
||||
if (style->dwEffects & CFE_AUTOCOLOR)
|
||||
s->fmt.dwEffects |= CFE_AUTOCOLOR;
|
||||
else
|
||||
s->fmt.dwEffects &= ~CFE_AUTOCOLOR;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
void ME_CopyCharFormat(CHARFORMAT2W *pDest, CHARFORMAT2W *pSrc)
|
||||
{
|
||||
/* using this with non-2W structs is forbidden */
|
||||
assert(pSrc->cbSize == sizeof(CHARFORMAT2W));
|
||||
assert(pDest->cbSize == sizeof(CHARFORMAT2W));
|
||||
CopyMemory(pDest, pSrc, sizeof(CHARFORMAT2W));
|
||||
}
|
||||
|
||||
static void ME_DumpStyleEffect(char **p, const char *name, CHARFORMAT2W *fmt, int mask)
|
||||
{
|
||||
*p += sprintf(*p, "%-22s%s\n", name, (fmt->dwMask & mask) ? ((fmt->dwEffects & mask) ? "YES" : "no") : "N/A");
|
||||
}
|
||||
|
||||
void ME_DumpStyle(ME_Style *s)
|
||||
{
|
||||
char buf[2048];
|
||||
ME_DumpStyleToBuf(&s->fmt, buf);
|
||||
TRACE("%s\n", buf);
|
||||
}
|
||||
|
||||
void ME_DumpStyleToBuf(CHARFORMAT2W *pFmt, char buf[2048])
|
||||
{
|
||||
/* FIXME only CHARFORMAT styles implemented */
|
||||
/* this function sucks, doesn't check for buffer overruns but it's "good enough" as for debug code */
|
||||
char *p;
|
||||
p = buf;
|
||||
p += sprintf(p, "Font face: ");
|
||||
if (pFmt->dwMask & CFM_FACE) {
|
||||
WCHAR *q = pFmt->szFaceName;
|
||||
while(*q) {
|
||||
*p++ = (*q > 255) ? '?' : *q;
|
||||
q++;
|
||||
}
|
||||
} else
|
||||
p += sprintf(p, "N/A");
|
||||
|
||||
if (pFmt->dwMask & CFM_SIZE)
|
||||
p += sprintf(p, "\nFont size: %d\n", (int)pFmt->yHeight);
|
||||
else
|
||||
p += sprintf(p, "\nFont size: N/A\n");
|
||||
|
||||
if (pFmt->dwMask & CFM_OFFSET)
|
||||
p += sprintf(p, "Char offset: %d\n", (int)pFmt->yOffset);
|
||||
else
|
||||
p += sprintf(p, "Char offset: N/A\n");
|
||||
|
||||
if (pFmt->dwMask & CFM_CHARSET)
|
||||
p += sprintf(p, "Font charset: %d\n", (int)pFmt->bCharSet);
|
||||
else
|
||||
p += sprintf(p, "Font charset: N/A\n");
|
||||
|
||||
/* I'm assuming CFM_xxx and CFE_xxx are the same values, fortunately it IS true wrt used flags*/
|
||||
ME_DumpStyleEffect(&p, "Font bold:", pFmt, CFM_BOLD);
|
||||
ME_DumpStyleEffect(&p, "Font italic:", pFmt, CFM_ITALIC);
|
||||
ME_DumpStyleEffect(&p, "Font underline:", pFmt, CFM_UNDERLINE);
|
||||
ME_DumpStyleEffect(&p, "Font strikeout:", pFmt, CFM_STRIKEOUT);
|
||||
p += sprintf(p, "Text color: ");
|
||||
if (pFmt->dwMask & CFM_COLOR)
|
||||
{
|
||||
if (pFmt->dwEffects & CFE_AUTOCOLOR)
|
||||
p += sprintf(p, "auto\n");
|
||||
else
|
||||
p += sprintf(p, "%06x\n", (int)pFmt->crTextColor);
|
||||
}
|
||||
else
|
||||
p += sprintf(p, "N/A\n");
|
||||
ME_DumpStyleEffect(&p, "Text protected:", pFmt, CFM_PROTECTED);
|
||||
}
|
||||
|
||||
void ME_UnprepareStyle(ME_Style *s)
|
||||
{
|
||||
if (s->hFont) {
|
||||
DeleteObject(s->hFont);
|
||||
s->hFont = NULL;
|
||||
s->nSequence = -2;
|
||||
}
|
||||
}
|
||||
|
||||
void ME_PrepareStyleFromDC(ME_Style *s, HDC hDC, int nSequence)
|
||||
{
|
||||
HGDIOBJ hOldFont;
|
||||
LOGFONTW lf;
|
||||
int rx, ry;
|
||||
|
||||
if (nSequence == s->nSequence && s->hFont)
|
||||
return;
|
||||
/* assert(s); */
|
||||
rx = GetDeviceCaps(hDC, LOGPIXELSX);
|
||||
ry = GetDeviceCaps(hDC, LOGPIXELSY);
|
||||
if (s->hFont) {
|
||||
DeleteObject(s->hFont);
|
||||
s->hFont = NULL;
|
||||
}
|
||||
ZeroMemory(&lf, sizeof(lf));
|
||||
lstrcpyW(lf.lfFaceName, s->fmt.szFaceName);
|
||||
lf.lfHeight = -s->fmt.yHeight*ry/1440;
|
||||
lf.lfWeight = 400;
|
||||
if (s->fmt.dwEffects & s->fmt.dwMask & CFM_BOLD)
|
||||
lf.lfWeight = 700;
|
||||
if (s->fmt.dwEffects & s->fmt.dwMask & CFM_WEIGHT)
|
||||
lf.lfWeight = s->fmt.wWeight;
|
||||
if (s->fmt.dwEffects & s->fmt.dwMask & CFM_ITALIC)
|
||||
lf.lfItalic = 1;
|
||||
if (s->fmt.dwEffects & s->fmt.dwMask & CFM_UNDERLINE)
|
||||
lf.lfUnderline = 1;
|
||||
if (s->fmt.dwEffects & s->fmt.dwMask & CFM_STRIKEOUT)
|
||||
lf.lfStrikeOut = 1;
|
||||
/*lf.lfQuality = PROOF_QUALITY; */
|
||||
lf.lfPitchAndFamily = s->fmt.bPitchAndFamily;
|
||||
lf.lfCharSet = s->fmt.bCharSet;
|
||||
s->hFont = CreateFontIndirectW(&lf);
|
||||
assert(s->hFont);
|
||||
GetObjectW(s->hFont, sizeof(LOGFONTW), &lf);
|
||||
hOldFont = SelectObject(hDC, s->hFont);
|
||||
GetTextMetricsW(hDC, &s->tm);
|
||||
SelectObject(hDC, hOldFont);
|
||||
s->nSequence = nSequence;
|
||||
}
|
||||
|
||||
HFONT ME_SelectStyleFont(HDC hDC, ME_Style *s)
|
||||
{
|
||||
HFONT hOldFont;
|
||||
ME_PrepareStyleFromDC(s, hDC, -1);
|
||||
assert(s->hFont);
|
||||
hOldFont = SelectObject(hDC, s->hFont);
|
||||
return hOldFont;
|
||||
}
|
||||
|
||||
void ME_PrepareStyle(ME_Context *c, ME_Style *s)
|
||||
{
|
||||
ME_PrepareStyleFromDC(s, c->hDC, c->nSequence);
|
||||
}
|
||||
|
||||
void ME_DestroyStyle(ME_Style *s) {
|
||||
if (s->hFont)
|
||||
{
|
||||
DeleteObject(s->hFont);
|
||||
s->hFont = NULL;
|
||||
}
|
||||
FREE_OBJ(s);
|
||||
}
|
||||
|
||||
void ME_AddRefStyle(ME_Style *s)
|
||||
{
|
||||
assert(s->nRefs>0); /* style with 0 references isn't supposed to exist */
|
||||
s->nRefs++;
|
||||
all_refs++;
|
||||
}
|
||||
|
||||
void ME_ReleaseStyle(ME_Style *s)
|
||||
{
|
||||
s->nRefs--;
|
||||
all_refs--;
|
||||
if (s->nRefs==0)
|
||||
TRACE("destroy style %p, total refs=%d\n", s, all_refs);
|
||||
else
|
||||
TRACE("release style %p, new refs=%d, total refs=%d\n", s, s->nRefs, all_refs);
|
||||
if (!all_refs) FIXME("all style references freed (good!)\n");
|
||||
assert(s->nRefs>=0);
|
||||
if (!s->nRefs)
|
||||
ME_DestroyStyle(s);
|
||||
}
|
||||
|
||||
ME_Style *ME_GetInsertStyle(ME_TextEditor *editor, int nCursor) {
|
||||
if (ME_IsSelection(editor))
|
||||
{
|
||||
ME_Cursor c;
|
||||
int from, to;
|
||||
|
||||
ME_GetSelection(editor, &from, &to);
|
||||
ME_CursorFromCharOfs(editor, from, &c);
|
||||
ME_AddRefStyle(c.pRun->member.run.style);
|
||||
return c.pRun->member.run.style;
|
||||
}
|
||||
if (editor->pBuffer->pCharStyle) {
|
||||
ME_AddRefStyle(editor->pBuffer->pCharStyle);
|
||||
return editor->pBuffer->pCharStyle;
|
||||
}
|
||||
else
|
||||
{
|
||||
ME_Cursor *pCursor = &editor->pCursors[nCursor];
|
||||
ME_DisplayItem *pRunItem = pCursor->pRun;
|
||||
ME_DisplayItem *pPrevItem = NULL;
|
||||
if (pCursor->nOffset) {
|
||||
ME_Run *pRun = &pRunItem->member.run;
|
||||
ME_AddRefStyle(pRun->style);
|
||||
return pRun->style;
|
||||
}
|
||||
pPrevItem = ME_FindItemBack(pRunItem, diRunOrParagraph);
|
||||
if (pPrevItem->type == diRun)
|
||||
{
|
||||
ME_AddRefStyle(pPrevItem->member.run.style);
|
||||
return pPrevItem->member.run.style;
|
||||
}
|
||||
else
|
||||
{
|
||||
ME_AddRefStyle(pRunItem->member.run.style);
|
||||
return pRunItem->member.run.style;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ME_SaveTempStyle(ME_TextEditor *editor)
|
||||
{
|
||||
ME_Style *old_style = editor->pBuffer->pCharStyle;
|
||||
editor->pBuffer->pCharStyle = ME_GetInsertStyle(editor, 0);
|
||||
if (old_style)
|
||||
ME_ReleaseStyle(old_style);
|
||||
}
|
||||
|
||||
void ME_ClearTempStyle(ME_TextEditor *editor)
|
||||
{
|
||||
if (!editor->pBuffer->pCharStyle) return;
|
||||
ME_ReleaseStyle(editor->pBuffer->pCharStyle);
|
||||
editor->pBuffer->pCharStyle = NULL;
|
||||
}
|
|
@ -0,0 +1,261 @@
|
|||
/*
|
||||
* RichEdit - functions dealing with editor object
|
||||
*
|
||||
* Copyright 2004 by Krzysztof Foltman
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "editor.h"
|
||||
|
||||
WINE_DEFAULT_DEBUG_CHANNEL(richedit);
|
||||
|
||||
void ME_EmptyUndoStack(ME_TextEditor *editor)
|
||||
{
|
||||
ME_DisplayItem *p, *pNext;
|
||||
|
||||
TRACE("Emptying undo stack\n");
|
||||
|
||||
p = editor->pUndoStack;
|
||||
editor->pUndoStack = NULL;
|
||||
while(p) {
|
||||
pNext = p->next;
|
||||
ME_DestroyDisplayItem(p);
|
||||
p = pNext;
|
||||
}
|
||||
p = editor->pRedoStack;
|
||||
editor->pRedoStack = NULL;
|
||||
while(p) {
|
||||
pNext = p->next;
|
||||
ME_DestroyDisplayItem(p);
|
||||
p = pNext;
|
||||
}
|
||||
}
|
||||
|
||||
ME_UndoItem *ME_AddUndoItem(ME_TextEditor *editor, ME_DIType type, ME_DisplayItem *pdi) {
|
||||
if (editor->nUndoMode == umIgnore)
|
||||
return NULL;
|
||||
else
|
||||
{
|
||||
ME_DisplayItem *pItem = (ME_DisplayItem *)ALLOC_OBJ(ME_UndoItem);
|
||||
switch(type)
|
||||
{
|
||||
case diUndoEndTransaction:
|
||||
break;
|
||||
case diUndoSetParagraphFormat:
|
||||
assert(pdi);
|
||||
CopyMemory(&pItem->member.para, &pdi->member.para, sizeof(ME_Paragraph));
|
||||
pItem->member.para.pFmt = ALLOC_OBJ(PARAFORMAT2);
|
||||
CopyMemory(pItem->member.para.pFmt, pdi->member.para.pFmt, sizeof(PARAFORMAT2));
|
||||
break;
|
||||
case diUndoInsertRun:
|
||||
assert(pdi);
|
||||
CopyMemory(&pItem->member.run, &pdi->member.run, sizeof(ME_Run));
|
||||
pItem->member.run.strText = ME_StrDup(pItem->member.run.strText);
|
||||
ME_AddRefStyle(pItem->member.run.style);
|
||||
break;
|
||||
case diUndoSetCharFormat:
|
||||
case diUndoSetDefaultCharFormat:
|
||||
break;
|
||||
case diUndoDeleteRun:
|
||||
case diUndoJoinParagraphs:
|
||||
break;
|
||||
case diUndoSplitParagraph:
|
||||
pItem->member.para.pFmt = ALLOC_OBJ(PARAFORMAT2);
|
||||
pItem->member.para.pFmt->cbSize = sizeof(PARAFORMAT2);
|
||||
pItem->member.para.pFmt->dwMask = 0;
|
||||
|
||||
break;
|
||||
default:
|
||||
assert(0 == "AddUndoItem, unsupported item type");
|
||||
return NULL;
|
||||
}
|
||||
pItem->type = type;
|
||||
pItem->prev = NULL;
|
||||
if (editor->nUndoMode == umAddToUndo || editor->nUndoMode == umAddBackToUndo)
|
||||
{
|
||||
if (editor->nUndoMode == umAddToUndo)
|
||||
TRACE("Pushing id=%s to undo stack, deleting redo stack\n", ME_GetDITypeName(type));
|
||||
else
|
||||
TRACE("Pushing id=%s to undo stack\n", ME_GetDITypeName(type));
|
||||
pItem->next = editor->pUndoStack;
|
||||
if (editor->pUndoStack)
|
||||
editor->pUndoStack->prev = pItem;
|
||||
editor->pUndoStack = pItem;
|
||||
/* any new operation (not redo) clears the redo stack */
|
||||
if (editor->nUndoMode == umAddToUndo) {
|
||||
ME_DisplayItem *p = editor->pRedoStack;
|
||||
while(p)
|
||||
{
|
||||
ME_DisplayItem *pp = p->next;
|
||||
ME_DestroyDisplayItem(p);
|
||||
p = pp;
|
||||
}
|
||||
editor->pRedoStack = NULL;
|
||||
}
|
||||
}
|
||||
else if (editor->nUndoMode == umAddToRedo)
|
||||
{
|
||||
TRACE("Pushing id=%s to redo stack\n", ME_GetDITypeName(type));
|
||||
pItem->next = editor->pRedoStack;
|
||||
if (editor->pRedoStack)
|
||||
editor->pRedoStack->prev = pItem;
|
||||
editor->pRedoStack = pItem;
|
||||
}
|
||||
else
|
||||
assert(0);
|
||||
return (ME_UndoItem *)pItem;
|
||||
}
|
||||
}
|
||||
|
||||
void ME_CommitUndo(ME_TextEditor *editor) {
|
||||
assert(editor->nUndoMode == umAddToUndo);
|
||||
|
||||
/* no transactions, no need to commit */
|
||||
if (!editor->pUndoStack)
|
||||
return;
|
||||
|
||||
/* no need to commit empty transactions */
|
||||
if (editor->pUndoStack->type == diUndoEndTransaction)
|
||||
return;
|
||||
|
||||
ME_AddUndoItem(editor, diUndoEndTransaction, NULL);
|
||||
ME_SendSelChange(editor);
|
||||
editor->nModifyStep++;
|
||||
}
|
||||
|
||||
void ME_PlayUndoItem(ME_TextEditor *editor, ME_DisplayItem *pItem)
|
||||
{
|
||||
ME_UndoItem *pUItem = (ME_UndoItem *)pItem;
|
||||
|
||||
TRACE("Playing undo/redo item, id=%s\n", ME_GetDITypeName(pItem->type));
|
||||
|
||||
switch(pItem->type)
|
||||
{
|
||||
case diUndoEndTransaction:
|
||||
assert(0);
|
||||
case diUndoSetParagraphFormat:
|
||||
{
|
||||
ME_Cursor tmp;
|
||||
ME_CursorFromCharOfs(editor, pItem->member.para.nCharOfs, &tmp);
|
||||
ME_SetParaFormat(editor, ME_FindItemBack(tmp.pRun, diParagraph), pItem->member.para.pFmt);
|
||||
break;
|
||||
}
|
||||
case diUndoSetCharFormat:
|
||||
{
|
||||
ME_SetCharFormat(editor, pUItem->nStart, pUItem->nLen, &pItem->member.ustyle->fmt);
|
||||
break;
|
||||
}
|
||||
case diUndoSetDefaultCharFormat:
|
||||
{
|
||||
ME_SetDefaultCharFormat(editor, &pItem->member.ustyle->fmt);
|
||||
break;
|
||||
}
|
||||
case diUndoInsertRun:
|
||||
{
|
||||
ME_InsertRun(editor, pItem->member.run.nCharOfs, pItem);
|
||||
break;
|
||||
}
|
||||
case diUndoDeleteRun:
|
||||
{
|
||||
ME_InternalDeleteText(editor, pUItem->nStart, pUItem->nLen);
|
||||
break;
|
||||
}
|
||||
case diUndoJoinParagraphs:
|
||||
{
|
||||
ME_Cursor tmp;
|
||||
ME_CursorFromCharOfs(editor, pUItem->nStart, &tmp);
|
||||
/* the only thing that's needed is paragraph offset, so no need to split runs */
|
||||
ME_JoinParagraphs(editor, ME_GetParagraph(tmp.pRun));
|
||||
break;
|
||||
}
|
||||
case diUndoSplitParagraph:
|
||||
{
|
||||
ME_Cursor tmp;
|
||||
ME_DisplayItem *new_para;
|
||||
ME_CursorFromCharOfs(editor, pUItem->nStart, &tmp);
|
||||
if (tmp.nOffset)
|
||||
tmp.pRun = ME_SplitRunSimple(editor, tmp.pRun, tmp.nOffset);
|
||||
new_para = ME_SplitParagraph(editor, tmp.pRun, tmp.pRun->member.run.style);
|
||||
assert(pItem->member.para.pFmt->cbSize == sizeof(PARAFORMAT2));
|
||||
CopyMemory(new_para->member.para.pFmt, pItem->member.para.pFmt, sizeof(PARAFORMAT2));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
assert(0 == "PlayUndoItem, unexpected type");
|
||||
}
|
||||
}
|
||||
|
||||
void ME_Undo(ME_TextEditor *editor) {
|
||||
ME_DisplayItem *p;
|
||||
ME_UndoMode nMode = editor->nUndoMode;
|
||||
|
||||
assert(nMode == umAddToUndo || nMode == umIgnore);
|
||||
|
||||
/* no undo items ? */
|
||||
if (!editor->pUndoStack)
|
||||
return;
|
||||
|
||||
/* watch out for uncommited transactions ! */
|
||||
assert(editor->pUndoStack->type == diUndoEndTransaction);
|
||||
|
||||
editor->nUndoMode = umAddToRedo;
|
||||
p = editor->pUndoStack->next;
|
||||
ME_DestroyDisplayItem(editor->pUndoStack);
|
||||
do {
|
||||
ME_DisplayItem *pp = p;
|
||||
ME_PlayUndoItem(editor, p);
|
||||
p = p->next;
|
||||
ME_DestroyDisplayItem(pp);
|
||||
} while(p && p->type != diUndoEndTransaction);
|
||||
ME_AddUndoItem(editor, diUndoEndTransaction, NULL);
|
||||
editor->pUndoStack = p;
|
||||
if (p)
|
||||
p->prev = NULL;
|
||||
editor->nUndoMode = nMode;
|
||||
editor->nModifyStep--;
|
||||
ME_UpdateRepaint(editor);
|
||||
}
|
||||
|
||||
void ME_Redo(ME_TextEditor *editor) {
|
||||
ME_DisplayItem *p;
|
||||
ME_UndoMode nMode = editor->nUndoMode;
|
||||
|
||||
assert(nMode == umAddToUndo || nMode == umIgnore);
|
||||
|
||||
/* no redo items ? */
|
||||
if (!editor->pRedoStack)
|
||||
return;
|
||||
|
||||
/* watch out for uncommited transactions ! */
|
||||
assert(editor->pRedoStack->type == diUndoEndTransaction);
|
||||
|
||||
editor->nUndoMode = umAddBackToUndo;
|
||||
p = editor->pRedoStack->next;
|
||||
ME_DestroyDisplayItem(editor->pRedoStack);
|
||||
do {
|
||||
ME_DisplayItem *pp = p;
|
||||
ME_PlayUndoItem(editor, p);
|
||||
p = p->next;
|
||||
ME_DestroyDisplayItem(pp);
|
||||
} while(p && p->type != diUndoEndTransaction);
|
||||
ME_AddUndoItem(editor, diUndoEndTransaction, NULL);
|
||||
editor->pRedoStack = p;
|
||||
if (p)
|
||||
p->prev = NULL;
|
||||
editor->nUndoMode = nMode;
|
||||
editor->nModifyStep++;
|
||||
ME_UpdateRepaint(editor);
|
||||
}
|
|
@ -0,0 +1,415 @@
|
|||
/*
|
||||
* RichEdit - Paragraph wrapping. Don't try to understand it. You've been
|
||||
* warned !
|
||||
*
|
||||
* Copyright 2004 by Krzysztof Foltman
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
|
||||
#include "editor.h"
|
||||
|
||||
WINE_DEFAULT_DEBUG_CHANNEL(richedit);
|
||||
|
||||
/*
|
||||
* Unsolved problems:
|
||||
*
|
||||
* - center and right align in WordPad omits all spaces at the start, we don't
|
||||
* - objects/images are not handled yet
|
||||
* - no tabs
|
||||
*/
|
||||
|
||||
ME_DisplayItem *ME_MakeRow(int height, int baseline, int width)
|
||||
{
|
||||
ME_DisplayItem *item = ME_MakeDI(diStartRow);
|
||||
|
||||
item->member.row.nHeight = height;
|
||||
item->member.row.nBaseline = baseline;
|
||||
item->member.row.nWidth = width;
|
||||
return item;
|
||||
}
|
||||
|
||||
void ME_BeginRow(ME_WrapContext *wc)
|
||||
{
|
||||
wc->pRowStart = NULL;
|
||||
wc->bOverflown = FALSE;
|
||||
wc->pLastSplittableRun = NULL;
|
||||
wc->nAvailWidth = wc->nTotalWidth - (wc->nRow ? wc->nLeftMargin : wc->nFirstMargin) - wc->nRightMargin;
|
||||
wc->pt.x = 0;
|
||||
}
|
||||
|
||||
void ME_InsertRowStart(ME_WrapContext *wc, ME_DisplayItem *pEnd)
|
||||
{
|
||||
ME_DisplayItem *p, *row, *para;
|
||||
int ascent = 0, descent = 0, width=0, shift = 0, align = 0;
|
||||
/* wrap text */
|
||||
para = ME_GetParagraph(wc->pRowStart);
|
||||
for (p = wc->pRowStart; p!=pEnd; p = p->next)
|
||||
{
|
||||
/* ENDPARA run shouldn't affect row height, except if it's the only run in the paragraph */
|
||||
if (p->type==diRun && ((p==wc->pRowStart) || !(p->member.run.nFlags & MERF_ENDPARA))) { /* FIXME add more run types */
|
||||
if (p->member.run.nAscent>ascent)
|
||||
ascent = p->member.run.nAscent;
|
||||
if (p->member.run.nDescent>descent)
|
||||
descent = p->member.run.nDescent;
|
||||
if (!(p->member.run.nFlags & (MERF_ENDPARA|MERF_SKIPPED)))
|
||||
width += p->member.run.nWidth;
|
||||
}
|
||||
}
|
||||
row = ME_MakeRow(ascent+descent, ascent, width);
|
||||
row->member.row.nYPos = wc->pt.y;
|
||||
row->member.row.nLMargin = (!wc->nRow ? wc->nFirstMargin : wc->nLeftMargin);
|
||||
row->member.row.nRMargin = wc->nRightMargin;
|
||||
assert(para->member.para.pFmt->dwMask & PFM_ALIGNMENT);
|
||||
align = para->member.para.pFmt->wAlignment;
|
||||
if (align == PFA_CENTER)
|
||||
shift = (wc->nAvailWidth-width)/2;
|
||||
if (align == PFA_RIGHT)
|
||||
shift = wc->nAvailWidth-width;
|
||||
for (p = wc->pRowStart; p!=pEnd; p = p->next)
|
||||
{
|
||||
if (p->type==diRun) { /* FIXME add more run types */
|
||||
p->member.run.pt.x += row->member.row.nLMargin+shift;
|
||||
}
|
||||
}
|
||||
ME_InsertBefore(wc->pRowStart, row);
|
||||
wc->nRow++;
|
||||
wc->pt.y += ascent+descent;
|
||||
ME_BeginRow(wc);
|
||||
}
|
||||
|
||||
void ME_WrapEndParagraph(ME_WrapContext *wc, ME_DisplayItem *p)
|
||||
{
|
||||
if (wc->pRowStart)
|
||||
ME_InsertRowStart(wc, p->next);
|
||||
|
||||
/*
|
||||
p = p->member.para.prev_para->next;
|
||||
while(p) {
|
||||
if (p->type == diParagraph || p->type == diTextEnd)
|
||||
return;
|
||||
if (p->type == diRun)
|
||||
{
|
||||
ME_Run *run = &p->member.run;
|
||||
TRACE("%s - (%d, %d)\n", debugstr_w(run->strText->szData), run->pt.x, run->pt.y);
|
||||
}
|
||||
p = p->next;
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
void ME_WrapSizeRun(ME_WrapContext *wc, ME_DisplayItem *p)
|
||||
{
|
||||
/* FIXME compose style (out of character and paragraph styles) here */
|
||||
|
||||
ME_UpdateRunFlags(wc->context->editor, &p->member.run);
|
||||
|
||||
ME_CalcRunExtent(wc->context, &p->member.run);
|
||||
}
|
||||
|
||||
ME_DisplayItem *ME_MaximizeSplit(ME_WrapContext *wc, ME_DisplayItem *p, int i)
|
||||
{
|
||||
ME_DisplayItem *pp, *piter = p;
|
||||
int j;
|
||||
if (!i)
|
||||
return NULL;
|
||||
j = ME_ReverseFindNonWhitespaceV(p->member.run.strText, i);
|
||||
if (j>0) {
|
||||
pp = ME_SplitRun(wc->context, piter, j);
|
||||
wc->pt.x += piter->member.run.nWidth;
|
||||
return pp;
|
||||
}
|
||||
else
|
||||
{
|
||||
pp = piter;
|
||||
/* omit all spaces before split point */
|
||||
while(piter != wc->pRowStart)
|
||||
{
|
||||
piter = ME_FindItemBack(piter, diRun);
|
||||
if (piter->member.run.nFlags & MERF_WHITESPACE)
|
||||
{
|
||||
pp = piter;
|
||||
continue;
|
||||
}
|
||||
if (piter->member.run.nFlags & MERF_ENDWHITE)
|
||||
{
|
||||
j = ME_ReverseFindNonWhitespaceV(piter->member.run.strText, i);
|
||||
pp = ME_SplitRun(wc->context, piter, i);
|
||||
wc->pt = pp->member.run.pt;
|
||||
return pp;
|
||||
}
|
||||
/* this run is the end of spaces, so the run edge is a good point to split */
|
||||
wc->pt = pp->member.run.pt;
|
||||
wc->bOverflown = TRUE;
|
||||
TRACE("Split point is: %s|%s\n", debugstr_w(piter->member.run.strText->szData), debugstr_w(pp->member.run.strText->szData));
|
||||
return pp;
|
||||
}
|
||||
wc->pt = piter->member.run.pt;
|
||||
return piter;
|
||||
}
|
||||
}
|
||||
|
||||
ME_DisplayItem *ME_SplitByBacktracking(ME_WrapContext *wc, ME_DisplayItem *p, int loc)
|
||||
{
|
||||
ME_DisplayItem *piter = p, *pp;
|
||||
int i, idesp, len;
|
||||
ME_Run *run = &p->member.run;
|
||||
|
||||
idesp = i = ME_CharFromPoint(wc->context->editor, loc, run);
|
||||
len = ME_StrVLen(run->strText);
|
||||
assert(len>0);
|
||||
assert(i<len);
|
||||
if (i) {
|
||||
/* don't split words */
|
||||
i = ME_ReverseFindWhitespaceV(run->strText, i);
|
||||
pp = ME_MaximizeSplit(wc, p, i);
|
||||
if (pp)
|
||||
return pp;
|
||||
}
|
||||
TRACE("Must backtrack to split at: %s\n", debugstr_w(p->member.run.strText->szData));
|
||||
if (wc->pLastSplittableRun)
|
||||
{
|
||||
if (wc->pLastSplittableRun->member.run.nFlags & MERF_GRAPHICS)
|
||||
{
|
||||
wc->pt = wc->ptLastSplittableRun;
|
||||
return wc->pLastSplittableRun;
|
||||
}
|
||||
else if (wc->pLastSplittableRun->member.run.nFlags & MERF_SPLITTABLE)
|
||||
{
|
||||
/* the following two lines are just to check if we forgot to call UpdateRunFlags earlier,
|
||||
they serve no other purpose */
|
||||
ME_UpdateRunFlags(wc->context->editor, run);
|
||||
assert((wc->pLastSplittableRun->member.run.nFlags & MERF_SPLITTABLE));
|
||||
|
||||
piter = wc->pLastSplittableRun;
|
||||
run = &piter->member.run;
|
||||
len = ME_StrVLen(run->strText);
|
||||
/* don't split words */
|
||||
i = ME_ReverseFindWhitespaceV(run->strText, len);
|
||||
if (i == len)
|
||||
i = ME_ReverseFindNonWhitespaceV(run->strText, len);
|
||||
if (i) {
|
||||
ME_DisplayItem *piter2 = ME_SplitRun(wc->context, piter, i);
|
||||
wc->pt = piter2->member.run.pt;
|
||||
return piter2;
|
||||
}
|
||||
/* splittable = must have whitespaces */
|
||||
assert(0 == "Splittable, but no whitespaces");
|
||||
}
|
||||
else
|
||||
{
|
||||
/* restart from the first run beginning with spaces */
|
||||
wc->pt = wc->ptLastSplittableRun;
|
||||
return wc->pLastSplittableRun;
|
||||
}
|
||||
}
|
||||
TRACE("Backtracking failed, trying desperate: %s\n", debugstr_w(p->member.run.strText->szData));
|
||||
/* OK, no better idea, so assume we MAY split words if we can split at all*/
|
||||
if (idesp)
|
||||
return ME_SplitRun(wc->context, piter, idesp);
|
||||
else
|
||||
if (wc->pRowStart && piter != wc->pRowStart)
|
||||
{
|
||||
/* don't need to break current run, because it's possible to split
|
||||
before this run */
|
||||
wc->bOverflown = TRUE;
|
||||
return piter;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* split point inside first character - no choice but split after that char */
|
||||
int chars = 1;
|
||||
int pos2 = ME_StrRelPos(run->strText, 0, &chars);
|
||||
if (pos2 != len) {
|
||||
/* the run is more than 1 char, so we may split */
|
||||
return ME_SplitRun(wc->context, piter, pos2);
|
||||
}
|
||||
/* the run is one char, can't split it */
|
||||
return piter;
|
||||
}
|
||||
}
|
||||
|
||||
ME_DisplayItem *ME_WrapHandleRun(ME_WrapContext *wc, ME_DisplayItem *p)
|
||||
{
|
||||
ME_DisplayItem *pp;
|
||||
ME_Run *run;
|
||||
int len;
|
||||
|
||||
assert(p->type == diRun);
|
||||
if (!wc->pRowStart)
|
||||
wc->pRowStart = p;
|
||||
ME_WrapSizeRun(wc, p);
|
||||
run = &p->member.run;
|
||||
run->pt.x = wc->pt.x;
|
||||
run->pt.y = wc->pt.y;
|
||||
len = ME_StrVLen(run->strText);
|
||||
|
||||
if (wc->bOverflown) /* just skipping final whitespaces */
|
||||
{
|
||||
if (run->nFlags & MERF_WHITESPACE) {
|
||||
p->member.run.nFlags |= MERF_SKIPPED;
|
||||
/* wc->pt.x += run->nWidth; */
|
||||
/* skip runs consisting of only whitespaces */
|
||||
return p->next;
|
||||
}
|
||||
|
||||
if (run->nFlags & MERF_STARTWHITE) {
|
||||
/* try to split the run at the first non-white char */
|
||||
int black;
|
||||
black = ME_FindNonWhitespaceV(run->strText, 0);
|
||||
if (black) {
|
||||
wc->bOverflown = FALSE;
|
||||
pp = ME_SplitRun(wc->context, p, black);
|
||||
p->member.run.nFlags |= MERF_SKIPPED;
|
||||
/*
|
||||
run->pt = wc->pt;
|
||||
wc->pt.x += run->nWidth;
|
||||
*/
|
||||
ME_InsertRowStart(wc, pp);
|
||||
return pp;
|
||||
}
|
||||
}
|
||||
/* black run: the row goes from pRowStart to the previous run */
|
||||
ME_InsertRowStart(wc, p);
|
||||
return p;
|
||||
}
|
||||
/* we're not at the end of the row */
|
||||
/* will current run fit? */
|
||||
if (wc->pt.x + run->nWidth > wc->nAvailWidth)
|
||||
{
|
||||
int loc = wc->nAvailWidth - wc->pt.x;
|
||||
/* total white run ? */
|
||||
if (run->nFlags & MERF_WHITESPACE) {
|
||||
/* let the overflow logic handle it */
|
||||
wc->bOverflown = TRUE;
|
||||
return p;
|
||||
}
|
||||
/* graphics - we can split before */
|
||||
if (run->nFlags & MERF_GRAPHICS) {
|
||||
wc->bOverflown = TRUE;
|
||||
return p;
|
||||
}
|
||||
/* can we separate out the last spaces ? (to use overflow logic later) */
|
||||
if (run->nFlags & MERF_ENDWHITE)
|
||||
{
|
||||
/* we aren't sure if it's *really* necessary, it's a good start however */
|
||||
int black = ME_ReverseFindNonWhitespaceV(run->strText, len);
|
||||
ME_SplitRun(wc->context, p, black);
|
||||
/* handle both parts again */
|
||||
return p;
|
||||
}
|
||||
/* determine the split point by backtracking */
|
||||
pp = ME_SplitByBacktracking(wc, p, loc);
|
||||
if (pp == wc->pRowStart)
|
||||
{
|
||||
/* we had only spaces so far, entire content can be omitted */
|
||||
wc->pt.x = 0;
|
||||
return p->next;
|
||||
}
|
||||
if (p != pp) /* found a suitable split point */
|
||||
{
|
||||
wc->bOverflown = TRUE;
|
||||
return pp;
|
||||
}
|
||||
/* we detected that it's best to split on start of this run */
|
||||
if (wc->bOverflown)
|
||||
return pp;
|
||||
ERR("failure!\n");
|
||||
/* not found anything - writing over margins is the only option left */
|
||||
}
|
||||
if ((run->nFlags & (MERF_SPLITTABLE | MERF_STARTWHITE))
|
||||
|| ((run->nFlags & MERF_GRAPHICS) && (p != wc->pRowStart)))
|
||||
{
|
||||
wc->pLastSplittableRun = p;
|
||||
wc->ptLastSplittableRun = wc->pt;
|
||||
}
|
||||
wc->pt.x += run->nWidth;
|
||||
return p->next;
|
||||
}
|
||||
|
||||
void ME_WrapTextParagraph(ME_Context *c, ME_DisplayItem *tp) {
|
||||
ME_DisplayItem *p;
|
||||
ME_WrapContext wc;
|
||||
|
||||
assert(tp->type == diParagraph);
|
||||
if (tp->member.para.nFlags & MEPF_WRAPPED) {
|
||||
return;
|
||||
}
|
||||
ME_PrepareParagraphForWrapping(c, tp);
|
||||
|
||||
wc.context = c;
|
||||
/* wc.para_style = tp->member.para.style; */
|
||||
wc.style = NULL;
|
||||
wc.nFirstMargin = tp->member.para.nFirstMargin;
|
||||
wc.nLeftMargin = tp->member.para.nLeftMargin;
|
||||
wc.nRightMargin = tp->member.para.nRightMargin;
|
||||
wc.nRow = 0;
|
||||
wc.pt.x = 0;
|
||||
wc.pt.y = 0;
|
||||
wc.nTotalWidth = c->rcView.right - c->rcView.left;
|
||||
wc.nAvailWidth = wc.nTotalWidth - wc.nFirstMargin - wc.nRightMargin;
|
||||
wc.pRowStart = NULL;
|
||||
|
||||
ME_BeginRow(&wc);
|
||||
for (p = tp->next; p!=tp->member.para.next_para; ) {
|
||||
assert(p->type != diStartRow);
|
||||
if (p->type == diRun) {
|
||||
ME_PrepareStyle(c, p->member.run.style);
|
||||
p = ME_WrapHandleRun(&wc, p);
|
||||
continue;
|
||||
}
|
||||
p = p->next;
|
||||
}
|
||||
ME_WrapEndParagraph(&wc, p);
|
||||
tp->member.para.nFlags |= MEPF_WRAPPED;
|
||||
tp->member.para.nHeight = wc.pt.y;
|
||||
}
|
||||
|
||||
|
||||
void ME_PrepareParagraphForWrapping(ME_Context *c, ME_DisplayItem *tp) {
|
||||
ME_DisplayItem *p;
|
||||
/* remove all items that will be reinserted by paragraph wrapper anyway */
|
||||
for (p = tp->next; p!=tp->member.para.next_para; p = p->next) {
|
||||
switch(p->type) {
|
||||
case diStartRow:
|
||||
p = p->prev;
|
||||
ME_Remove(p->next);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* join runs that can be joined, set up flags */
|
||||
for (p = tp->next; p!=tp->member.para.next_para; p = p->next) {
|
||||
int changed = 0;
|
||||
switch(p->type) {
|
||||
case diStartRow: assert(0); break; /* should have deleted it */
|
||||
case diRun:
|
||||
while (p->next->type == diRun) { /* FIXME */
|
||||
if (ME_CanJoinRuns(&p->member.run, &p->next->member.run)) {
|
||||
ME_JoinRuns(c->editor, p);
|
||||
changed = 1;
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
p->member.run.nFlags &= ~MERF_CALCBYWRAP;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue