mirror of
https://github.com/reactos/reactos.git
synced 2025-07-24 13:43:42 +00:00
Sync to Wine-20050310:
import svn path=/trunk/; revision=14091
This commit is contained in:
parent
0eb4051a93
commit
67347846ec
24 changed files with 11459 additions and 0 deletions
40
reactos/include/wine/richole.h
Normal file
40
reactos/include/wine/richole.h
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
/*
|
||||||
|
* Compatibility header
|
||||||
|
*
|
||||||
|
* This header is wrapper to allow compilation of Wine DLLs under ReactOS
|
||||||
|
* build system. It contains definitions commonly refered to as Wineisms
|
||||||
|
* and definitions that are missing in w32api.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <richedit.h>
|
||||||
|
|
||||||
|
#include_next <richole.h>
|
||||||
|
|
||||||
|
#ifndef WINE_RICHOLE_H_INCLUDED
|
||||||
|
#define WINE_RICHOLE_H_INCLUDED
|
||||||
|
|
||||||
|
#ifdef COBJMACROS
|
||||||
|
/*** IUnknown methods ***/
|
||||||
|
#define IRichEditOle_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b)
|
||||||
|
#define IRichEditOle_AddRef(p) (p)->lpVtbl->AddRef(p)
|
||||||
|
#define IRichEditOle_Release(p) (p)->lpVtbl->Release(p)
|
||||||
|
/*** IRichEditOle methods ***/
|
||||||
|
#define IRichEditOle_GetClientSite(p,a) (p)->lpVtbl->GetClientSite(p,a)
|
||||||
|
#define IRichEditOle_GetObjectCount(p) (p)->lpVtbl->GetObjectCount(p)
|
||||||
|
#define IRichEditOle_GetLinkCount(p) (p)->lpVtbl->GetLinkCount(p)
|
||||||
|
#define IRichEditOle_GetObject(p,a,b,c) (p)->lpVtbl->GetObject(p,a,b,c)
|
||||||
|
#define IRichEditOle_InsertObject(p,a) (p)->lpVtbl->InsertObject(p,a)
|
||||||
|
#define IRichEditOle_ConvertObject(p,a,b,c) (p)->lpVtbl->ConvertObject(p,a,b,c)
|
||||||
|
#define IRichEditOle_ActivateAs(p,a,b) (p)->lpVtbl->ActivateAs(p,a,b)
|
||||||
|
#define IRichEditOle_SetHostNames(p,a,b) (p)->lpVtbl->SetHostNames(p,a,b)
|
||||||
|
#define IRichEditOle_SetLinkAvailable(p,a,b) (p)->lpVtbl->SetLinkAvailable(p,a,b)
|
||||||
|
#define IRichEditOle_SetDvaspect(p,a,b) (p)->lpVtbl->SetDvaspect(p,a,b)
|
||||||
|
#define IRichEditOle_HandsOffStorage(p,a) (p)->lpVtbl->HandsOffStorage(p,a)
|
||||||
|
#define IRichEditOle_SaveCompleted(p,a,b) (p)->lpVtbl->SaveCompleted(p,a,b)
|
||||||
|
#define IRichEditOle_InPlaceDeactivate(p) (p)->lpVtbl->InPlaceDeactivate(p)
|
||||||
|
#define IRichEditOle_ContextSensitiveHelp(p,a) (p)->lpVtbl->ContextSensitiveHelp(p,a)
|
||||||
|
#define IRichEditOle_GetClipboardData(p,a,b,c) (p)->lpVtbl->GetClipboardData(p,a,b,c)
|
||||||
|
#define IRichEditOle_ImportDataObject(p,a,b,c) (p)->lpVtbl->ImportDataObject(p,a,b,c)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* WINE_RICHOLE_H_INCLUDED */
|
27
reactos/lib/riched20/Makefile.in
Normal file
27
reactos/lib/riched20/Makefile.in
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
TOPSRCDIR = @top_srcdir@
|
||||||
|
TOPOBJDIR = ../..
|
||||||
|
SRCDIR = @srcdir@
|
||||||
|
VPATH = @srcdir@
|
||||||
|
MODULE = riched20.dll
|
||||||
|
IMPORTS = user32 gdi32 kernel32
|
||||||
|
EXTRALIBS = -luuid
|
||||||
|
|
||||||
|
C_SRCS = \
|
||||||
|
caret.c \
|
||||||
|
context.c \
|
||||||
|
editor.c \
|
||||||
|
list.c \
|
||||||
|
paint.c \
|
||||||
|
para.c \
|
||||||
|
reader.c \
|
||||||
|
richole.c \
|
||||||
|
row.c \
|
||||||
|
run.c \
|
||||||
|
string.c \
|
||||||
|
style.c \
|
||||||
|
undo.c \
|
||||||
|
wrap.c
|
||||||
|
|
||||||
|
@MAKE_DLL_RULES@
|
||||||
|
|
||||||
|
### Dependencies:
|
19
reactos/lib/riched20/Makefile.ros-template
Normal file
19
reactos/lib/riched20/Makefile.ros-template
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
# $Id$
|
||||||
|
|
||||||
|
TARGET_NAME = riched20
|
||||||
|
|
||||||
|
TARGET_OBJECTS = @C_SRCS@
|
||||||
|
|
||||||
|
TARGET_CFLAGS = @EXTRADEFS@ -D__REACTOS__ -D__WINESRC__
|
||||||
|
|
||||||
|
TARGET_SDKLIBS = @IMPORTS@ wine.a wine_uuid.a ntdll.a
|
||||||
|
|
||||||
|
TARGET_BASE = $(TARGET_BASE_LIB_SETUPAPI)
|
||||||
|
|
||||||
|
TARGET_NORC = yes
|
||||||
|
|
||||||
|
default: all
|
||||||
|
|
||||||
|
DEP_OBJECTS = $(TARGET_OBJECTS)
|
||||||
|
|
||||||
|
include $(TOOLS_PATH)/depend.mk
|
977
reactos/lib/riched20/caret.c
Normal file
977
reactos/lib/riched20/caret.c
Normal file
|
@ -0,0 +1,977 @@
|
||||||
|
/*
|
||||||
|
* 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_REWRAP;
|
||||||
|
|
||||||
|
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);
|
||||||
|
assert(pRun->member.run.strText->nLen);
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
ME_Style *ME_GetSelectionInsertStyle(ME_TextEditor *editor)
|
||||||
|
{
|
||||||
|
ME_Style *style;
|
||||||
|
int from, to;
|
||||||
|
ME_Cursor c;
|
||||||
|
|
||||||
|
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);
|
||||||
|
return style;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
37
reactos/lib/riched20/context.c
Normal file
37
reactos/lib/riched20/context.c
Normal file
|
@ -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);
|
||||||
|
}
|
1020
reactos/lib/riched20/editor.c
Normal file
1020
reactos/lib/riched20/editor.c
Normal file
File diff suppressed because it is too large
Load diff
212
reactos/lib/riched20/editor.h
Normal file
212
reactos/lib/riched20/editor.h
Normal file
|
@ -0,0 +1,212 @@
|
||||||
|
/*
|
||||||
|
* 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);
|
||||||
|
HFONT ME_SelectStyleFont(ME_TextEditor *editor, HDC hDC, ME_Style *s);
|
||||||
|
void ME_UnselectStyleFont(ME_TextEditor *editor, HDC hDC, ME_Style *s, HFONT hOldFont);
|
||||||
|
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);
|
||||||
|
ME_Style *ME_GetSelectionInsertStyle(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);
|
||||||
|
void ME_WrapMarkedParagraphs(ME_TextEditor *editor);
|
||||||
|
|
||||||
|
/* 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, RECT *rcUpdate);
|
||||||
|
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);
|
||||||
|
|
||||||
|
/* richole.c */
|
||||||
|
extern LRESULT CreateIRichEditOle(LPVOID *);
|
||||||
|
|
||||||
|
/* 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);
|
258
reactos/lib/riched20/editstr.h
Normal file
258
reactos/lib/riched20/editstr.h
Normal file
|
@ -0,0 +1,258 @@
|
||||||
|
/*
|
||||||
|
* 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_REWRAP 1
|
||||||
|
#define MEPF_REPAINT 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;
|
||||||
|
int nLastPaintYPos, nLastPaintHeight;
|
||||||
|
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_FontCacheItem
|
||||||
|
{
|
||||||
|
LOGFONTW lfSpecs;
|
||||||
|
HFONT hFont;
|
||||||
|
int nRefs;
|
||||||
|
int nAge;
|
||||||
|
} ME_FontCacheItem;
|
||||||
|
|
||||||
|
#define HFONT_CACHE_SIZE 10
|
||||||
|
|
||||||
|
typedef struct tagME_TextEditor
|
||||||
|
{
|
||||||
|
HWND hWnd;
|
||||||
|
BOOL bCaretShown;
|
||||||
|
ME_TextBuffer *pBuffer;
|
||||||
|
ME_Cursor *pCursors;
|
||||||
|
int nCursors;
|
||||||
|
SIZE sizeWindow;
|
||||||
|
int nScrollPos;
|
||||||
|
int nTotalLength, nLastTotalLength;
|
||||||
|
int nUDArrowX;
|
||||||
|
int nSequence;
|
||||||
|
int nOldSelFrom, nOldSelTo;
|
||||||
|
COLORREF rgbBackColor;
|
||||||
|
BOOL bCaretAtEnd;
|
||||||
|
int nEventMask;
|
||||||
|
int nModifyStep;
|
||||||
|
ME_DisplayItem *pUndoStack, *pRedoStack;
|
||||||
|
ME_UndoMode nUndoMode;
|
||||||
|
int nParagraphs;
|
||||||
|
int nLastSelStart, nLastSelEnd;
|
||||||
|
ME_FontCacheItem pFontCache[HFONT_CACHE_SIZE];
|
||||||
|
} 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
|
192
reactos/lib/riched20/list.c
Normal file
192
reactos/lib/riched20/list.c
Normal file
|
@ -0,0 +1,192 @@
|
||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
item->member.para.nFlags = MEPF_REWRAP;
|
||||||
|
}
|
||||||
|
|
||||||
|
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");
|
||||||
|
}
|
9
reactos/lib/riched20/makefile
Normal file
9
reactos/lib/riched20/makefile
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
# $Id: makefile 12852 2005-01-06 13:58:04Z mf $
|
||||||
|
|
||||||
|
PATH_TO_TOP = ../..
|
||||||
|
|
||||||
|
TARGET_TYPE = winedll
|
||||||
|
|
||||||
|
include $(PATH_TO_TOP)/rules.mak
|
||||||
|
|
||||||
|
include $(TOOLS_PATH)/helper.mk
|
430
reactos/lib/riched20/paint.c
Normal file
430
reactos/lib/riched20/paint.c
Normal file
|
@ -0,0 +1,430 @@
|
||||||
|
/*
|
||||||
|
* 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, RECT *rcUpdate) {
|
||||||
|
ME_DisplayItem *item;
|
||||||
|
ME_Context c;
|
||||||
|
int yoffset;
|
||||||
|
|
||||||
|
editor->nSequence++;
|
||||||
|
yoffset = GetScrollPos(editor->hWnd, SB_VERT);
|
||||||
|
ME_InitContext(&c, editor, hDC);
|
||||||
|
SetBkMode(hDC, TRANSPARENT);
|
||||||
|
ME_MoveCaret(editor);
|
||||||
|
item = editor->pBuffer->pFirst->next;
|
||||||
|
c.pt.y -= yoffset;
|
||||||
|
while(item != editor->pBuffer->pLast) {
|
||||||
|
assert(item->type == diParagraph);
|
||||||
|
if (!bOnlyNew || (item->member.para.nFlags & MEPF_REPAINT))
|
||||||
|
{
|
||||||
|
BOOL bPaint = (rcUpdate == NULL);
|
||||||
|
if (rcUpdate)
|
||||||
|
bPaint = c.pt.y<rcUpdate->bottom &&
|
||||||
|
c.pt.y+item->member.para.nHeight>rcUpdate->top;
|
||||||
|
if (bPaint)
|
||||||
|
{
|
||||||
|
ME_DrawParagraph(&c, item);
|
||||||
|
item->member.para.nFlags &= ~MEPF_REPAINT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
c.pt.y += item->member.para.nHeight;
|
||||||
|
item = item->member.para.next_para;
|
||||||
|
}
|
||||||
|
if (c.pt.y<c.rcView.bottom) {
|
||||||
|
RECT rc;
|
||||||
|
int xs = c.rcView.left, xe = c.rcView.right;
|
||||||
|
int ys = c.pt.y, ye = c.rcView.bottom;
|
||||||
|
|
||||||
|
if (bOnlyNew)
|
||||||
|
{
|
||||||
|
int y1 = editor->nTotalLength-yoffset, y2 = editor->nLastTotalLength-yoffset;
|
||||||
|
if (y1<y2)
|
||||||
|
ys = y1, ye = y2+1;
|
||||||
|
else
|
||||||
|
ys = ye;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rcUpdate && ys!=ye)
|
||||||
|
{
|
||||||
|
xs = rcUpdate->left, xe = rcUpdate->right;
|
||||||
|
if (rcUpdate->top > ys)
|
||||||
|
ys = rcUpdate->top;
|
||||||
|
if (rcUpdate->bottom < ye)
|
||||||
|
ye = rcUpdate->bottom;
|
||||||
|
}
|
||||||
|
|
||||||
|
rc.left = xs; /* FIXME remove if it's not necessary anymore */
|
||||||
|
rc.top = c.pt.y;
|
||||||
|
rc.right = xe;
|
||||||
|
rc.bottom = c.pt.y+1;
|
||||||
|
FillRect(hDC, &rc, (HBRUSH)GetStockObject(BLACK_BRUSH));
|
||||||
|
|
||||||
|
if (ys == c.pt.y) /* don't overwrite the top bar */
|
||||||
|
ys++;
|
||||||
|
if (ye>ys) {
|
||||||
|
rc.left = xs;
|
||||||
|
rc.top = ys;
|
||||||
|
rc.right = xe;
|
||||||
|
rc.bottom = ye;
|
||||||
|
/* this is not supposed to be gray, I know, but lets keep it gray for now for debugging purposes */
|
||||||
|
FillRect(hDC, &rc, (HBRUSH)GetStockObject(LTGRAY_BRUSH));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
editor->nLastTotalLength = editor->nTotalLength;
|
||||||
|
ME_DestroyContext(&c);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ME_MarkParagraphRange(ME_TextEditor *editor, ME_DisplayItem *p1,
|
||||||
|
ME_DisplayItem *p2, int nFlags)
|
||||||
|
{
|
||||||
|
ME_DisplayItem *p3;
|
||||||
|
if (p1 == p2)
|
||||||
|
{
|
||||||
|
p1->member.para.nFlags |= nFlags;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (p1->member.para.nCharOfs > p2->member.para.nCharOfs)
|
||||||
|
p3 = p1, p1 = p2, p2 = p3;
|
||||||
|
|
||||||
|
p1->member.para.nFlags |= nFlags;
|
||||||
|
do {
|
||||||
|
p1 = p1->member.para.next_para;
|
||||||
|
p1->member.para.nFlags |= nFlags;
|
||||||
|
} while (p1 != p2);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ME_MarkOffsetRange(ME_TextEditor *editor, int from, int to, int nFlags)
|
||||||
|
{
|
||||||
|
ME_Cursor c1, c2;
|
||||||
|
ME_CursorFromCharOfs(editor, from, &c1);
|
||||||
|
ME_CursorFromCharOfs(editor, to, &c2);
|
||||||
|
|
||||||
|
ME_MarkParagraphRange(editor, ME_GetParagraph(c1.pRun), ME_GetParagraph(c2.pRun), nFlags);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ME_MarkSelectionForRepaint(ME_TextEditor *editor)
|
||||||
|
{
|
||||||
|
int from, to, from2, to2, end;
|
||||||
|
|
||||||
|
end = ME_GetTextLength(editor);
|
||||||
|
ME_GetSelection(editor, &from, &to);
|
||||||
|
from2 = editor->nLastSelStart;
|
||||||
|
to2 = editor->nLastSelEnd;
|
||||||
|
if (from<from2) ME_MarkOffsetRange(editor, from, from2, MEPF_REPAINT);
|
||||||
|
if (from>from2) ME_MarkOffsetRange(editor, from2, from, MEPF_REPAINT);
|
||||||
|
if (to<to2) ME_MarkOffsetRange(editor, to, to2, MEPF_REPAINT);
|
||||||
|
if (to>to2) ME_MarkOffsetRange(editor, to2, to, MEPF_REPAINT);
|
||||||
|
|
||||||
|
editor->nLastSelStart = from;
|
||||||
|
editor->nLastSelEnd = to;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
ME_MarkSelectionForRepaint(editor);
|
||||||
|
ME_WrapMarkedParagraphs(editor);
|
||||||
|
hDC = GetDC(editor->hWnd);
|
||||||
|
ME_HideCaret(editor);
|
||||||
|
ME_PaintContent(editor, hDC, TRUE, NULL);
|
||||||
|
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;
|
||||||
|
hOldFont = ME_SelectStyleFont(c->editor, hDC, s);
|
||||||
|
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);
|
||||||
|
ME_UnselectStyleFont(c->editor, hDC, s, hOldFont);
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
if (si.nMax<1) si.nMax = 1;
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
358
reactos/lib/riched20/para.c
Normal file
358
reactos/lib/riched20/para.c
Normal file
|
@ -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_REWRAP;
|
||||||
|
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 = MEPF_REWRAP; /* 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_REWRAP;
|
||||||
|
new_para->member.para.prev_para->member.para.nFlags |= MEPF_REWRAP;
|
||||||
|
|
||||||
|
/* 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_REWRAP;
|
||||||
|
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_REWRAP;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
3786
reactos/lib/riched20/reader.c
Normal file
3786
reactos/lib/riched20/reader.c
Normal file
File diff suppressed because it is too large
Load diff
9
reactos/lib/riched20/riched20.spec
Normal file
9
reactos/lib/riched20/riched20.spec
Normal file
|
@ -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)
|
266
reactos/lib/riched20/richole.c
Normal file
266
reactos/lib/riched20/richole.c
Normal file
|
@ -0,0 +1,266 @@
|
||||||
|
/*
|
||||||
|
* RichEdit GUIDs and OLE interface
|
||||||
|
*
|
||||||
|
* Copyright 2004 by Krzysztof Foltman
|
||||||
|
* Copyright 2004 Aric Stewart
|
||||||
|
*
|
||||||
|
* 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 <stdarg.h>
|
||||||
|
|
||||||
|
#define NONAMELESSUNION
|
||||||
|
#define NONAMELESSSTRUCT
|
||||||
|
#define COBJMACROS
|
||||||
|
|
||||||
|
#include "windef.h"
|
||||||
|
#include "winbase.h"
|
||||||
|
#include "wingdi.h"
|
||||||
|
#include "winuser.h"
|
||||||
|
#include "ole2.h"
|
||||||
|
#include "richole.h"
|
||||||
|
#include "wine/debug.h"
|
||||||
|
|
||||||
|
WINE_DEFAULT_DEBUG_CHANNEL(richedit);
|
||||||
|
|
||||||
|
typedef struct IRichEditOleImpl {
|
||||||
|
IRichEditOleVtbl *lpVtbl;
|
||||||
|
DWORD ref;
|
||||||
|
} IRichEditOleImpl;
|
||||||
|
|
||||||
|
/* there is no way to be consistent across different sets of headers - mingw, Wine, Win32 SDK*/
|
||||||
|
|
||||||
|
/* FIXME: the next 6 lines should be in textserv.h */
|
||||||
|
#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);
|
||||||
|
|
||||||
|
static HRESULT WINAPI
|
||||||
|
IRichEditOle_fnQueryInterface(IRichEditOle *me, REFIID riid, LPVOID *ppvObj)
|
||||||
|
{
|
||||||
|
IRichEditOleImpl *This = (IRichEditOleImpl *)me;
|
||||||
|
|
||||||
|
TRACE("%p %s\n", This, debugstr_guid(riid) );
|
||||||
|
|
||||||
|
if (IsEqualGUID(riid, &IID_IUnknown) ||
|
||||||
|
IsEqualGUID(riid, &IID_IRichEditOle))
|
||||||
|
{
|
||||||
|
IRichEditOle_AddRef(me);
|
||||||
|
*ppvObj = (LPVOID) This;
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
return E_NOINTERFACE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ULONG WINAPI
|
||||||
|
IRichEditOle_fnAddRef(IRichEditOle *me)
|
||||||
|
{
|
||||||
|
IRichEditOleImpl *This = (IRichEditOleImpl *)me;
|
||||||
|
ULONG ref = InterlockedIncrement( &This->ref );
|
||||||
|
|
||||||
|
TRACE("%p ref = %lu\n", This, ref);
|
||||||
|
|
||||||
|
return ref;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ULONG WINAPI
|
||||||
|
IRichEditOle_fnRelease(IRichEditOle *me)
|
||||||
|
{
|
||||||
|
IRichEditOleImpl *This = (IRichEditOleImpl *)me;
|
||||||
|
ULONG ref = InterlockedDecrement(&This->ref);
|
||||||
|
|
||||||
|
TRACE ("%p ref=%lu\n", This, ref);
|
||||||
|
|
||||||
|
if (!ref)
|
||||||
|
{
|
||||||
|
TRACE ("Destroying %p\n", This);
|
||||||
|
HeapFree(GetProcessHeap(),0,This);
|
||||||
|
}
|
||||||
|
return ref;
|
||||||
|
}
|
||||||
|
|
||||||
|
static HRESULT WINAPI
|
||||||
|
IRichEditOle_fnActivateAs(IRichEditOle *me, REFCLSID rclsid, REFCLSID rclsidAs)
|
||||||
|
{
|
||||||
|
IRichEditOleImpl *This = (IRichEditOleImpl *)me;
|
||||||
|
FIXME("stub %p\n",This);
|
||||||
|
return E_NOTIMPL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static HRESULT WINAPI
|
||||||
|
IRichEditOle_fnContextSensitiveHelp(IRichEditOle *me, BOOL fEnterMode)
|
||||||
|
{
|
||||||
|
IRichEditOleImpl *This = (IRichEditOleImpl *)me;
|
||||||
|
FIXME("stub %p\n",This);
|
||||||
|
return E_NOTIMPL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static HRESULT WINAPI
|
||||||
|
IRichEditOle_fnConvertObject(IRichEditOle *me, LONG iob,
|
||||||
|
REFCLSID rclsidNew, LPCSTR lpstrUserTypeNew)
|
||||||
|
{
|
||||||
|
IRichEditOleImpl *This = (IRichEditOleImpl *)me;
|
||||||
|
FIXME("stub %p\n",This);
|
||||||
|
return E_NOTIMPL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static HRESULT WINAPI
|
||||||
|
IRichEditOle_fnGetClientSite(IRichEditOle *me,
|
||||||
|
LPOLECLIENTSITE *lplpolesite)
|
||||||
|
{
|
||||||
|
IRichEditOleImpl *This = (IRichEditOleImpl *)me;
|
||||||
|
FIXME("stub %p\n",This);
|
||||||
|
return E_NOTIMPL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static HRESULT WINAPI
|
||||||
|
IRichEditOle_fnGetClipboardData(IRichEditOle *me, CHARRANGE *lpchrg,
|
||||||
|
DWORD reco, LPDATAOBJECT *lplpdataobj)
|
||||||
|
{
|
||||||
|
IRichEditOleImpl *This = (IRichEditOleImpl *)me;
|
||||||
|
FIXME("stub %p\n",This);
|
||||||
|
return E_NOTIMPL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static LONG WINAPI IRichEditOle_fnGetLinkCount(IRichEditOle *me)
|
||||||
|
{
|
||||||
|
IRichEditOleImpl *This = (IRichEditOleImpl *)me;
|
||||||
|
FIXME("stub %p\n",This);
|
||||||
|
return E_NOTIMPL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static HRESULT WINAPI
|
||||||
|
IRichEditOle_fnGetObject(IRichEditOle *me, LONG iob,
|
||||||
|
REOBJECT *lpreobject, DWORD dwFlags)
|
||||||
|
{
|
||||||
|
IRichEditOleImpl *This = (IRichEditOleImpl *)me;
|
||||||
|
FIXME("stub %p\n",This);
|
||||||
|
return E_NOTIMPL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static LONG WINAPI
|
||||||
|
IRichEditOle_fnGetObjectCount(IRichEditOle *me)
|
||||||
|
{
|
||||||
|
IRichEditOleImpl *This = (IRichEditOleImpl *)me;
|
||||||
|
FIXME("stub %p\n",This);
|
||||||
|
return E_NOTIMPL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static HRESULT WINAPI
|
||||||
|
IRichEditOle_fnHandsOffStorage(IRichEditOle *me, LONG iob)
|
||||||
|
{
|
||||||
|
IRichEditOleImpl *This = (IRichEditOleImpl *)me;
|
||||||
|
FIXME("stub %p\n",This);
|
||||||
|
return E_NOTIMPL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static HRESULT WINAPI
|
||||||
|
IRichEditOle_fnImportDataObject(IRichEditOle *me, LPDATAOBJECT lpdataobj,
|
||||||
|
CLIPFORMAT cf, HGLOBAL hMetaPict)
|
||||||
|
{
|
||||||
|
IRichEditOleImpl *This = (IRichEditOleImpl *)me;
|
||||||
|
FIXME("stub %p\n",This);
|
||||||
|
return E_NOTIMPL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static HRESULT WINAPI
|
||||||
|
IRichEditOle_fnInPlaceDeactivate(IRichEditOle *me)
|
||||||
|
{
|
||||||
|
IRichEditOleImpl *This = (IRichEditOleImpl *)me;
|
||||||
|
FIXME("stub %p\n",This);
|
||||||
|
return E_NOTIMPL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static HRESULT WINAPI
|
||||||
|
IRichEditOle_fnInsertObject(IRichEditOle *me, REOBJECT *lpreobject)
|
||||||
|
{
|
||||||
|
IRichEditOleImpl *This = (IRichEditOleImpl *)me;
|
||||||
|
FIXME("stub %p\n",This);
|
||||||
|
return E_NOTIMPL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static HRESULT WINAPI IRichEditOle_fnSaveCompleted(IRichEditOle *me, LONG iob,
|
||||||
|
LPSTORAGE lpstg)
|
||||||
|
{
|
||||||
|
IRichEditOleImpl *This = (IRichEditOleImpl *)me;
|
||||||
|
FIXME("stub %p\n",This);
|
||||||
|
return E_NOTIMPL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static HRESULT WINAPI
|
||||||
|
IRichEditOle_fnSetDvaspect(IRichEditOle *me, LONG iob, DWORD dvaspect)
|
||||||
|
{
|
||||||
|
IRichEditOleImpl *This = (IRichEditOleImpl *)me;
|
||||||
|
FIXME("stub %p\n",This);
|
||||||
|
return E_NOTIMPL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static HRESULT WINAPI IRichEditOle_fnSetHostNames(IRichEditOle *me,
|
||||||
|
LPCSTR lpstrContainerApp, LPCSTR lpstrContainerObj)
|
||||||
|
{
|
||||||
|
IRichEditOleImpl *This = (IRichEditOleImpl *)me;
|
||||||
|
FIXME("stub %p\n",This);
|
||||||
|
return E_NOTIMPL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static HRESULT WINAPI
|
||||||
|
IRichEditOle_fnSetLinkAvailable(IRichEditOle *me, LONG iob, BOOL fAvailable)
|
||||||
|
{
|
||||||
|
IRichEditOleImpl *This = (IRichEditOleImpl *)me;
|
||||||
|
FIXME("stub %p\n",This);
|
||||||
|
return E_NOTIMPL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static IRichEditOleVtbl revt = {
|
||||||
|
IRichEditOle_fnQueryInterface,
|
||||||
|
IRichEditOle_fnAddRef,
|
||||||
|
IRichEditOle_fnRelease,
|
||||||
|
IRichEditOle_fnGetClientSite,
|
||||||
|
IRichEditOle_fnGetObjectCount,
|
||||||
|
IRichEditOle_fnGetLinkCount,
|
||||||
|
IRichEditOle_fnGetObject,
|
||||||
|
IRichEditOle_fnInsertObject,
|
||||||
|
IRichEditOle_fnConvertObject,
|
||||||
|
IRichEditOle_fnActivateAs,
|
||||||
|
IRichEditOle_fnSetHostNames,
|
||||||
|
IRichEditOle_fnSetLinkAvailable,
|
||||||
|
IRichEditOle_fnSetDvaspect,
|
||||||
|
IRichEditOle_fnHandsOffStorage,
|
||||||
|
IRichEditOle_fnSaveCompleted,
|
||||||
|
IRichEditOle_fnInPlaceDeactivate,
|
||||||
|
IRichEditOle_fnContextSensitiveHelp,
|
||||||
|
IRichEditOle_fnGetClipboardData,
|
||||||
|
IRichEditOle_fnImportDataObject
|
||||||
|
};
|
||||||
|
|
||||||
|
LRESULT CreateIRichEditOle(LPVOID *ppObj)
|
||||||
|
{
|
||||||
|
IRichEditOleImpl *reo;
|
||||||
|
|
||||||
|
reo = HeapAlloc(GetProcessHeap(), 0, sizeof(IRichEditOleImpl));
|
||||||
|
if (!reo)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
reo->lpVtbl = &revt;
|
||||||
|
reo->ref = 1;
|
||||||
|
TRACE("Created %p\n",reo);
|
||||||
|
*ppObj = (LPVOID) reo;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
84
reactos/lib/riched20/row.c
Normal file
84
reactos/lib/riched20/row.c
Normal file
|
@ -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);
|
||||||
|
}
|
1570
reactos/lib/riched20/rtf.h
Normal file
1570
reactos/lib/riched20/rtf.h
Normal file
File diff suppressed because it is too large
Load diff
672
reactos/lib/riched20/run.c
Normal file
672
reactos/lib/riched20/run.c
Normal file
|
@ -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);
|
||||||
|
ME_GetParagraph(p)->member.para.nFlags |= MEPF_REWRAP;
|
||||||
|
|
||||||
|
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_REWRAP;
|
||||||
|
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_REWRAP;
|
||||||
|
|
||||||
|
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(editor, hDC, run->style);
|
||||||
|
GetTextExtentExPointW(hDC, run->strText->szData, run->strText->nLen,
|
||||||
|
cx, &fit, NULL, &sz);
|
||||||
|
ME_UnselectStyleFont(editor, hDC, run->style, 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(editor, 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;
|
||||||
|
}
|
||||||
|
ME_UnselectStyleFont(editor, hDC, run->style, 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(editor, hDC, pRun->style);
|
||||||
|
GetTextExtentPoint32W(hDC, pRun->strText->szData, nOffset, &size);
|
||||||
|
ME_UnselectStyleFont(editor, hDC, pRun->style, 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(c->editor, hDC, s);
|
||||||
|
GetTextExtentPoint32W(hDC, szText, nChars, size);
|
||||||
|
ME_UnselectStyleFont(c->editor, hDC, s, 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_REWRAP;
|
||||||
|
|
||||||
|
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_REWRAP;
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
}
|
318
reactos/lib/riched20/string.c
Normal file
318
reactos/lib/riched20/string.c
Normal file
|
@ -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);
|
||||||
|
}
|
443
reactos/lib/riched20/style.c
Normal file
443
reactos/lib/riched20/style.c
Normal file
|
@ -0,0 +1,443 @@
|
||||||
|
/*
|
||||||
|
* 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_LogFontFromStyle(HDC hDC, LOGFONTW *lf, ME_Style *s)
|
||||||
|
{
|
||||||
|
int rx, ry;
|
||||||
|
rx = GetDeviceCaps(hDC, LOGPIXELSX);
|
||||||
|
ry = GetDeviceCaps(hDC, LOGPIXELSY);
|
||||||
|
ZeroMemory(lf, sizeof(LOGFONTW));
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
BOOL ME_IsFontEqual(LOGFONTW *p1, LOGFONTW *p2)
|
||||||
|
{
|
||||||
|
if (memcmp(p1, p2, sizeof(LOGFONTW)-sizeof(p1->lfFaceName)))
|
||||||
|
return FALSE;
|
||||||
|
if (lstrcmpW(p1->lfFaceName, p2->lfFaceName))
|
||||||
|
return FALSE;
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
HFONT ME_SelectStyleFont(ME_TextEditor *editor, HDC hDC, ME_Style *s)
|
||||||
|
{
|
||||||
|
HFONT hOldFont;
|
||||||
|
LOGFONTW lf;
|
||||||
|
int i, nEmpty, nAge = 0x7FFFFFFF;
|
||||||
|
ME_FontCacheItem *item;
|
||||||
|
assert(hDC);
|
||||||
|
assert(s);
|
||||||
|
|
||||||
|
ME_LogFontFromStyle(hDC, &lf, s);
|
||||||
|
|
||||||
|
for (i=0; i<HFONT_CACHE_SIZE; i++)
|
||||||
|
editor->pFontCache[i].nAge++;
|
||||||
|
for (i=0, nEmpty=-1, nAge=0; i<HFONT_CACHE_SIZE; i++)
|
||||||
|
{
|
||||||
|
item = &editor->pFontCache[i];
|
||||||
|
if (!item->nRefs)
|
||||||
|
{
|
||||||
|
if (item->nAge > nAge)
|
||||||
|
nEmpty = i, nAge = item->nAge;
|
||||||
|
}
|
||||||
|
if (ME_IsFontEqual(&item->lfSpecs, &lf))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (i < HFONT_CACHE_SIZE) /* found */
|
||||||
|
{
|
||||||
|
item = &editor->pFontCache[i];
|
||||||
|
TRACE("font reused %d\n", i);
|
||||||
|
|
||||||
|
s->hFont = item->hFont;
|
||||||
|
item->nRefs++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
item = &editor->pFontCache[nEmpty]; /* this legal even when nEmpty == -1, as we don't dereference it */
|
||||||
|
|
||||||
|
assert(nEmpty != -1); /* otherwise we leak cache entries or get too many fonts at once*/
|
||||||
|
if (item->hFont) {
|
||||||
|
TRACE("font deleted %d\n", nEmpty);
|
||||||
|
DeleteObject(item->hFont);
|
||||||
|
item->hFont = NULL;
|
||||||
|
}
|
||||||
|
s->hFont = CreateFontIndirectW(&lf);
|
||||||
|
assert(s->hFont);
|
||||||
|
TRACE("font created %d\n", nEmpty);
|
||||||
|
item->hFont = s->hFont;
|
||||||
|
item->nRefs = 1;
|
||||||
|
memcpy(&item->lfSpecs, &lf, sizeof(LOGFONTW));
|
||||||
|
}
|
||||||
|
hOldFont = SelectObject(hDC, s->hFont);
|
||||||
|
/* should be cached too, maybe ? */
|
||||||
|
GetTextMetricsW(hDC, &s->tm);
|
||||||
|
return hOldFont;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ME_UnselectStyleFont(ME_TextEditor *editor, HDC hDC, ME_Style *s, HFONT hOldFont)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
assert(hDC);
|
||||||
|
assert(s);
|
||||||
|
SelectObject(hDC, hOldFont);
|
||||||
|
for (i=0; i<HFONT_CACHE_SIZE; i++)
|
||||||
|
{
|
||||||
|
ME_FontCacheItem *pItem = &editor->pFontCache[i];
|
||||||
|
if (pItem->hFont == s->hFont && pItem->nRefs > 0)
|
||||||
|
{
|
||||||
|
pItem->nRefs--;
|
||||||
|
pItem->nAge = 0;
|
||||||
|
s->hFont = NULL;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert(0 == "UnselectStyleFont without SelectStyleFont");
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
261
reactos/lib/riched20/undo.c
Normal file
261
reactos/lib/riched20/undo.c
Normal file
|
@ -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);
|
||||||
|
}
|
449
reactos/lib/riched20/wrap.c
Normal file
449
reactos/lib/riched20/wrap.c
Normal file
|
@ -0,0 +1,449 @@
|
||||||
|
/*
|
||||||
|
* 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_REWRAP)) {
|
||||||
|
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) {
|
||||||
|
p = ME_WrapHandleRun(&wc, p);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
p = p->next;
|
||||||
|
}
|
||||||
|
ME_WrapEndParagraph(&wc, p);
|
||||||
|
tp->member.para.nFlags &= ~MEPF_REWRAP;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ME_WrapMarkedParagraphs(ME_TextEditor *editor) {
|
||||||
|
HWND hWnd = editor->hWnd;
|
||||||
|
HDC hDC = GetDC(hWnd);
|
||||||
|
ME_DisplayItem *item;
|
||||||
|
ME_Context c;
|
||||||
|
|
||||||
|
ME_InitContext(&c, editor, hDC);
|
||||||
|
c.pt.x = 0;
|
||||||
|
c.pt.y = 0;
|
||||||
|
item = editor->pBuffer->pFirst->next;
|
||||||
|
while(item != editor->pBuffer->pLast) {
|
||||||
|
BOOL bRedraw = FALSE;
|
||||||
|
|
||||||
|
assert(item->type == diParagraph);
|
||||||
|
if ((item->member.para.nFlags & MEPF_REWRAP)
|
||||||
|
|| (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_REPAINT;
|
||||||
|
|
||||||
|
c.pt.y += 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;
|
||||||
|
|
||||||
|
ME_DestroyContext(&c);
|
||||||
|
ReleaseDC(hWnd, hDC);
|
||||||
|
}
|
|
@ -81,6 +81,9 @@ DEFINE_GUID(FMTID_SummaryInformation,0xF29F85E0,0x4FF9,0x1068,0xAB,0x91,0x08,0x0
|
||||||
DEFINE_GUID(FMTID_DocSummaryInformation,0xD5CDD502,0x2E9C,0x101B,0x93,0x97,0x08,0x00,0x2B,0x2C,0xF9,0xAE);
|
DEFINE_GUID(FMTID_DocSummaryInformation,0xD5CDD502,0x2E9C,0x101B,0x93,0x97,0x08,0x00,0x2B,0x2C,0xF9,0xAE);
|
||||||
DEFINE_GUID(FMTID_UserDefinedProperties,0xD5CDD505,0x2E9C,0x101B,0x93,0x97,0x08,0x00,0x2B,0x2C,0xF9,0xAE);
|
DEFINE_GUID(FMTID_UserDefinedProperties,0xD5CDD505,0x2E9C,0x101B,0x93,0x97,0x08,0x00,0x2B,0x2C,0xF9,0xAE);
|
||||||
|
|
||||||
|
DEFINE_GUID(IID_IRichEditOle,0x00020D00,0,0,0xC0,0,0,0,0,0,0,0x46);
|
||||||
|
DEFINE_GUID(IID_IRichEditOleCallback,0x00020D03,0,0,0xC0,0,0,0,0,0,0,0x46);
|
||||||
|
|
||||||
DEFINE_OLEGUID(IID_StdOle,0x00020430,0,0);
|
DEFINE_OLEGUID(IID_StdOle,0x00020430,0,0);
|
||||||
|
|
||||||
DEFINE_GUID(CLSID_StdFont,0x0be35203,0x8f91,0x11ce,0x9d,0xe3,0x00,0xaa,0x00,0x4b,0xb8,0x51);
|
DEFINE_GUID(CLSID_StdFont,0x0be35203,0x8f91,0x11ce,0x9d,0xe3,0x00,0xaa,0x00,0x4b,0xb8,0x51);
|
||||||
|
|
|
@ -29,8 +29,26 @@ extern "C" {
|
||||||
#define CFM_FACE 0x20000000
|
#define CFM_FACE 0x20000000
|
||||||
#define CFM_OFFSET 0x10000000
|
#define CFM_OFFSET 0x10000000
|
||||||
#define CFM_CHARSET 0x08000000
|
#define CFM_CHARSET 0x08000000
|
||||||
|
#define CFM_BACKCOLOR 0x04000000
|
||||||
|
#define CFM_LCID 0x02000000
|
||||||
|
#define CFM_UNDERLINETYPE 0x00800000
|
||||||
|
#define CFM_WEIGHT 0x00400000
|
||||||
|
#define CFM_SPACING 0x00200000
|
||||||
|
#define CFM_KERNING 0x00100000
|
||||||
|
#define CFM_STYLE 0x00080000
|
||||||
|
#define CFM_ANIMATION 0x00040000
|
||||||
#define CFM_SUBSCRIPT 0x00030000
|
#define CFM_SUBSCRIPT 0x00030000
|
||||||
#define CFM_SUPERSCRIPT 0x00030000
|
#define CFM_SUPERSCRIPT 0x00030000
|
||||||
|
#define CFM_REVAUTHOR 0x00008000
|
||||||
|
#define CFM_REVISED 0x00004000
|
||||||
|
#define CFM_DISABLED 0x00002000
|
||||||
|
#define CFM_IMPRINT 0x00001000
|
||||||
|
#define CFM_EMBOSS 0x00000800
|
||||||
|
#define CFM_SHADOW 0x00000400
|
||||||
|
#define CFM_OUTLINE 0x00000200
|
||||||
|
#define CFM_HIDDEN 0x00000100
|
||||||
|
#define CFM_ALLCAPS 0x00000080
|
||||||
|
#define CFM_SMALLCAPS 0x00000040
|
||||||
#define CFM_EFFECTS (CFM_BOLD | CFM_ITALIC | CFM_UNDERLINE | CFM_COLOR | CFM_STRIKEOUT | CFE_PROTECTED | CFM_LINK)
|
#define CFM_EFFECTS (CFM_BOLD | CFM_ITALIC | CFM_UNDERLINE | CFM_COLOR | CFM_STRIKEOUT | CFE_PROTECTED | CFM_LINK)
|
||||||
#define CFE_BOLD 1
|
#define CFE_BOLD 1
|
||||||
#define CFE_ITALIC 2
|
#define CFE_ITALIC 2
|
||||||
|
@ -38,6 +56,7 @@ extern "C" {
|
||||||
#define CFE_STRIKEOUT 8
|
#define CFE_STRIKEOUT 8
|
||||||
#define CFE_PROTECTED 16
|
#define CFE_PROTECTED 16
|
||||||
#define CFE_AUTOCOLOR 0x40000000
|
#define CFE_AUTOCOLOR 0x40000000
|
||||||
|
#define CFE_AUTOBACKCOLOR 0x04000000
|
||||||
#define CFE_SUBSCRIPT 0x00010000
|
#define CFE_SUBSCRIPT 0x00010000
|
||||||
#define CFE_SUPERSCRIPT 0x00020000
|
#define CFE_SUPERSCRIPT 0x00020000
|
||||||
#define IMF_FORCENONE 1
|
#define IMF_FORCENONE 1
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue