Commit d488f3f1 authored by Krzysztof Foltman's avatar Krzysztof Foltman Committed by Alexandre Julliard

Initial implementation of riched20.

parent aa35787d
......@@ -1634,6 +1634,7 @@ dlls/qcap/Makefile
dlls/quartz/Makefile
dlls/quartz/tests/Makefile
dlls/rasapi32/Makefile
dlls/riched20/Makefile
dlls/richedit/Makefile
dlls/rpcrt4/Makefile
dlls/rpcrt4/tests/Makefile
......
......@@ -108,6 +108,7 @@ BASEDIRS = \
qcap \
quartz \
rasapi32 \
riched20 \
richedit \
rpcrt4 \
rsabase \
......@@ -351,6 +352,7 @@ SYMLINKS_SO = \
qcap.dll.so \
quartz.dll.so \
rasapi32.dll.so \
riched20.dll.so \
riched32.dll.so \
rpcrt4.dll.so \
rsabase.dll.so \
......@@ -781,6 +783,9 @@ rasapi32.dll.so: rasapi32/rasapi32.dll.so
rasapi16.dll.so : rasapi32.dll.so
$(RM) $@ && $(LN_S) rasapi32.dll.so $@
riched20.dll.so: riched20/riched20.dll.so
$(RM) $@ && $(LN_S) riched20/riched20.dll.so $@
riched32.dll.so: richedit/riched32.dll.so
$(RM) $@ && $(LN_S) richedit/riched32.dll.so $@
......@@ -1092,6 +1097,7 @@ IMPORT_LIBS = \
libqcap.$(IMPLIBEXT) \
libquartz.$(IMPLIBEXT) \
librasapi32.$(IMPLIBEXT) \
libriched20.$(IMPLIBEXT) \
libriched32.$(IMPLIBEXT) \
librpcrt4.$(IMPLIBEXT) \
librsabase.$(IMPLIBEXT) \
......@@ -1590,6 +1596,11 @@ librasapi32.def: rasapi32/rasapi32.spec.def
librasapi32.a: rasapi32/rasapi32.spec.def
$(DLLTOOL) -k -l $@ -d rasapi32/rasapi32.spec.def
libriched20.def: riched20/riched20.spec.def
$(RM) $@ && $(LN_S) riched20/riched20.spec.def $@
libriched20.a: riched20/riched20.spec.def
$(DLLTOOL) -k -l $@ -d riched20/riched20.spec.def
libriched32.def: richedit/riched32.spec.def
$(RM) $@ && $(LN_S) richedit/riched32.spec.def $@
libriched32.a: richedit/riched32.spec.def
......@@ -1880,6 +1891,7 @@ psapi/psapi.spec.def: $(WINEBUILD)
qcap/qcap.spec.def: $(WINEBUILD)
quartz/quartz.spec.def: $(WINEBUILD)
rasapi32/rasapi32.spec.def: $(WINEBUILD)
riched20/riched20.spec.def: $(WINEBUILD)
richedit/riched32.spec.def: $(WINEBUILD)
rpcrt4/rpcrt4.spec.def: $(WINEBUILD)
rsabase/rsabase.spec.def: $(WINEBUILD)
......@@ -2032,6 +2044,7 @@ psapi/psapi.dll.so: psapi
qcap/qcap.dll.so: qcap
quartz/quartz.dll.so: quartz
rasapi32/rasapi32.dll.so: rasapi32
riched20/riched20.dll.so: riched20
richedit/riched32.dll.so: richedit
rpcrt4/rpcrt4.dll.so: rpcrt4
rsabase/rsabase.dll.so: rsabase
......
Makefile
riched20.dll.dbg.c
riched20.spec.def
TOPSRCDIR = @top_srcdir@
TOPOBJDIR = ../..
SRCDIR = @srcdir@
VPATH = @srcdir@
MODULE = riched20.dll
IMPORTS = user32 gdi32 kernel32
C_SRCS = \
caret.c \
context.c \
editor.c \
list.c \
paint.c \
para.c \
richole.c \
row.c \
run.c \
string.c \
style.c \
undo.c \
wrap.c
@MAKE_DLL_RULES@
### Dependencies:
/*
* 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);
}
/*
* RichEdit - structures and constant
*
* Copyright 2004 by Krzysztof Foltman
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef __EDITSTR_H
#define __EDITSTR_H
#ifndef _WIN32_IE
#define _WIN32_IE 0x0400
#endif
#include <assert.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <windef.h>
#include <winbase.h>
#include <winnls.h>
#include <winnt.h>
#include <wingdi.h>
#include <winuser.h>
#include <richedit.h>
#include <commctrl.h>
#include "wine/debug.h"
typedef struct tagME_String
{
WCHAR *szData;
int nLen, nBuffer;
} ME_String;
typedef struct tagME_Style
{
CHARFORMAT2W fmt;
HFONT hFont; /* cached font for the style */
TEXTMETRICW tm; /* cached font metrics for the style */
int nRefs; /* reference count */
int nSequence; /* incremented when cache needs to be rebuilt, ie. every screen redraw */
} ME_Style;
typedef enum {
diTextStart, /* start of the text buffer */
diParagraph, /* paragraph start */
diRun, /* run (sequence of chars with the same character format) */
diStartRow, /* start of the row (line of text on the screen) */
diTextEnd, /* end of the text buffer */
/********************* these below are meant for finding only *********************/
diStartRowOrParagraph, /* 5 */
diStartRowOrParagraphOrEnd,
diRunOrParagraph,
diRunOrStartRow,
diParagraphOrEnd,
diRunOrParagraphOrEnd, /* 10 */
diUndoInsertRun, /* 11 */
diUndoDeleteRun, /* 12 */
diUndoJoinParagraphs, /* 13 */
diUndoSplitParagraph, /* 14 */
diUndoSetParagraphFormat, /* 15 */
diUndoSetCharFormat, /* 16 */
diUndoEndTransaction, /* 17 */
diUndoSetDefaultCharFormat, /* 18 */
} ME_DIType;
/******************************** run flags *************************/
#define MERF_STYLEFLAGS 0x0FFF
/* run contains non-text content, which has its own rules for wrapping, sizing etc */
#define MERF_GRAPHICS 1
/* run is splittable (contains white spaces in the middle or end) */
#define MERF_SPLITTABLE 0x001000
/* run starts with whitespaces */
#define MERF_STARTWHITE 0x002000
/* run ends with whitespaces */
#define MERF_ENDWHITE 0x004000
/* run is completely made of whitespaces */
#define MERF_WHITESPACE 0x008000
/* run is a last (dummy) run in the paragraph */
#define MERF_SKIPPED 0x010000
/* flags that are calculated during text wrapping */
#define MERF_CALCBYWRAP 0x0F0000
/* the "end of paragraph" run, contains 1 character */
#define MERF_ENDPARA 0x100000
/* those flags are kept when the row is split */
#define MERF_SPLITMASK (~(0))
/******************************** para flags *************************/
/* this paragraph was already wrapped and hasn't changed, every change resets that flag */
#define MEPF_WRAPPED 1
#define MEPF_REDRAW 2
/******************************** structures *************************/
struct tagME_DisplayItem;
typedef struct tagME_Run
{
ME_String *strText;
ME_Style *style;
int nCharOfs; /* relative to para's offset */
int nWidth; /* width of full run, width of leading&trailing ws */
int nFlags;
int nAscent, nDescent; /* pixels above/below baseline */
POINT pt; /* relative to para's position */
} ME_Run;
typedef struct tagME_Document {
struct tagME_DisplayItem *def_char_style;
struct tagME_DisplayItem *def_para_style;
int last_wrapped_line;
} ME_Document;
typedef struct tagME_Paragraph
{
PARAFORMAT2 *pFmt;
int nLeftMargin, nRightMargin, nFirstMargin;
int nCharOfs;
int nFlags;
int nYPos, nHeight;
struct tagME_DisplayItem *prev_para, *next_para, *document;
} ME_Paragraph;
typedef struct tagME_Row
{
int nHeight;
int nBaseline;
int nWidth;
int nLMargin;
int nRMargin;
int nYPos;
} ME_Row;
typedef struct tagME_DisplayItem
{
ME_DIType type;
struct tagME_DisplayItem *prev, *next;
union {
ME_Run run;
ME_Row row;
ME_Paragraph para;
ME_Document doc; /* not used */
ME_Style *ustyle; /* used by diUndoSetCharFormat */
} member;
} ME_DisplayItem;
typedef struct tagME_UndoItem
{
ME_DisplayItem di;
int nStart, nLen;
} ME_UndoItem;
typedef struct tagME_TextBuffer
{
ME_DisplayItem *pFirst, *pLast;
ME_Style *pCharStyle;
ME_Style *pDefaultStyle;
} ME_TextBuffer;
typedef struct tagME_Cursor
{
ME_DisplayItem *pRun;
int nOffset;
} ME_Cursor;
typedef enum {
umAddToUndo,
umAddToRedo,
umIgnore,
umAddBackToUndo
} ME_UndoMode;
typedef struct tagME_TextEditor
{
HWND hWnd;
BOOL bCaretShown;
ME_TextBuffer *pBuffer;
ME_Cursor *pCursors;
int nCursors;
SIZE sizeWindow;
int nScrollPos;
int nTotalLength;
int nUDArrowX;
int nSequence;
int nOldSelFrom, nOldSelTo;
COLORREF rgbBackColor;
BOOL bCaretAtEnd;
int nEventMask;
int nModifyStep;
ME_DisplayItem *pUndoStack, *pRedoStack;
ME_UndoMode nUndoMode;
int nParagraphs;
} ME_TextEditor;
typedef struct tagME_Context
{
HDC hDC;
POINT pt;
POINT ptRowOffset;
RECT rcView;
HBRUSH hbrMargin;
/* those are valid inside ME_WrapTextParagraph and related */
POINT ptFirstRun;
ME_TextEditor *editor;
int nSequence;
} ME_Context;
typedef struct tagME_WrapContext
{
ME_Style *style;
ME_Context *context;
int nLeftMargin, nRightMargin, nFirstMargin;
int nTotalWidth, nAvailWidth;
int nRow;
POINT pt;
BOOL bOverflown;
ME_DisplayItem *pRowStart;
ME_DisplayItem *pLastSplittableRun;
POINT ptLastSplittableRun;
} ME_WrapContext;
#endif
/*
* RichEdit - Basic operations on double linked lists.
*
* Copyright 2004 by Krzysztof Foltman
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "editor.h"
WINE_DEFAULT_DEBUG_CHANNEL(riched20);
void ME_InsertBefore(ME_DisplayItem *diWhere, ME_DisplayItem *diWhat)
{
diWhat->next = diWhere;
diWhat->prev = diWhere->prev;
diWhere->prev->next = diWhat;
diWhat->next->prev = diWhat;
}
void ME_Remove(ME_DisplayItem *diWhere)
{
ME_DisplayItem *diNext = diWhere->next;
ME_DisplayItem *diPrev = diWhere->prev;
assert(diNext);
assert(diPrev);
diPrev->next = diNext;
diNext->prev = diPrev;
}
ME_DisplayItem *ME_FindItemBack(ME_DisplayItem *di, ME_DIType nTypeOrClass)
{
if (!di)
return NULL;
di = di->prev;
while(di!=NULL) {
if (ME_DITypesEqual(di->type, nTypeOrClass))
return di;
di = di->prev;
}
return NULL;
}
ME_DisplayItem *ME_FindItemBackOrHere(ME_DisplayItem *di, ME_DIType nTypeOrClass)
{
while(di!=NULL) {
if (ME_DITypesEqual(di->type, nTypeOrClass))
return di;
di = di->prev;
}
return NULL;
}
ME_DisplayItem *ME_FindItemFwd(ME_DisplayItem *di, ME_DIType nTypeOrClass)
{
if (!di) return NULL;
di = di->next;
while(di!=NULL) {
if (ME_DITypesEqual(di->type, nTypeOrClass))
return di;
di = di->next;
}
return NULL;
}
ME_DisplayItem *ME_FindItemFwdOrHere(ME_DisplayItem *di, ME_DIType nTypeOrClass)
{
while(di!=NULL) {
if (ME_DITypesEqual(di->type, nTypeOrClass))
return di;
di = di->next;
}
return NULL;
}
BOOL ME_DITypesEqual(ME_DIType type, ME_DIType nTypeOrClass)
{
if (type==nTypeOrClass)
return TRUE;
if (nTypeOrClass==diRunOrParagraph && (type==diRun || type==diParagraph))
return TRUE;
if (nTypeOrClass==diRunOrStartRow && (type==diRun || type==diStartRow))
return TRUE;
if (nTypeOrClass==diParagraphOrEnd && (type==diTextEnd || type==diParagraph))
return TRUE;
if (nTypeOrClass==diStartRowOrParagraph && (type==diStartRow || type==diParagraph))
return TRUE;
if (nTypeOrClass==diStartRowOrParagraphOrEnd
&& (type==diStartRow || type==diParagraph || type==diTextEnd))
return TRUE;
if (nTypeOrClass==diRunOrParagraphOrEnd
&& (type==diRun || type==diParagraph || type==diTextEnd))
return TRUE;
return FALSE;
}
void ME_DestroyDisplayItem(ME_DisplayItem *item) {
/* TRACE("type=%s\n", ME_GetDITypeName(item->type)); */
if (item->type==diParagraph || item->type == diUndoSetParagraphFormat) {
FREE_OBJ(item->member.para.pFmt);
}
if (item->type==diRun || item->type == diUndoInsertRun) {
ME_ReleaseStyle(item->member.run.style);
ME_DestroyString(item->member.run.strText);
}
if (item->type==diUndoSetCharFormat || item->type==diUndoSetDefaultCharFormat) {
ME_ReleaseStyle(item->member.ustyle);
}
FREE_OBJ(item);
}
ME_DisplayItem *ME_MakeDI(ME_DIType type) {
ME_DisplayItem *item = ALLOC_OBJ(ME_DisplayItem);
ZeroMemory(item, sizeof(ME_DisplayItem));
item->type = type;
item->prev = item->next = NULL;
if (type == diParagraph || type == diUndoSplitParagraph) {
item->member.para.pFmt = ALLOC_OBJ(PARAFORMAT2);
item->member.para.pFmt->cbSize = sizeof(PARAFORMAT2);
item->member.para.pFmt->dwMask = 0;
}
return item;
}
const char *ME_GetDITypeName(ME_DIType type)
{
switch(type)
{
case diParagraph: return "diParagraph";
case diRun: return "diRun";
case diTextStart: return "diTextStart";
case diTextEnd: return "diTextEnd";
case diStartRow: return "diStartRow";
case diUndoEndTransaction: return "diUndoEndTransaction";
case diUndoSetParagraphFormat: return "diUndoSetParagraphFormat";
case diUndoSetCharFormat: return "diUndoSetCharFormat";
case diUndoInsertRun: return "diUndoInsertRun";
case diUndoDeleteRun: return "diUndoDeleteRun";
case diUndoJoinParagraphs: return "diJoinParagraphs";
case diUndoSplitParagraph: return "diSplitParagraph";
case diUndoSetDefaultCharFormat: return "diUndoSetDefaultCharFormat";
default: return "?";
}
}
void ME_DumpDocument(ME_TextBuffer *buffer)
{
/* FIXME this is useless, */
ME_DisplayItem *pItem = buffer->pFirst;
TRACE("DOCUMENT DUMP START\n");
while(pItem) {
switch(pItem->type)
{
case diTextStart:
TRACE("Start");
break;
case diParagraph:
TRACE("\nParagraph(ofs=%d)", pItem->member.para.nCharOfs);
break;
case diStartRow:
TRACE(" - StartRow");
break;
case diRun:
TRACE(" - Run(\"%s\", %d)", debugstr_w(pItem->member.run.strText->szData),
pItem->member.run.nCharOfs);
break;
case diTextEnd:
TRACE("\nEnd\n");
break;
default:
break;
}
pItem = pItem->next;
}
TRACE("DOCUMENT DUMP END\n");
}
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)
/*
* RichEdit GUIDs
*
* Copyright 2004 by Krzysztof Foltman
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <windef.h>
/* there is no way to be consistent across different sets of headers - mingw, Wine, Win32 SDK*/
#define RICHEDIT_GUID(name, l, w1, w2) \
GUID name = { l, w1, w2, {0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46}}
RICHEDIT_GUID(IID_IRichEditOle, 0x00020d00, 0, 0);
RICHEDIT_GUID(IID_IRichEditOleCallback, 0x00020d03, 0, 0);
#define TEXTSERV_GUID(name, l, w1, w2, b1, b2) \
GUID name = { l, w1, w2, {b1, b2, 0x00, 0xaa, 0x00, 0x6c, 0xad, 0xc5}}
TEXTSERV_GUID(IID_ITextServices, 0x8d33f740, 0xcf58, 0x11ce, 0xa8, 0x9d);
TEXTSERV_GUID(IID_ITextHost, 0xc5bdd8d0, 0xd26e, 0x11ce, 0xa8, 0x9e);
TEXTSERV_GUID(IID_ITextHost2, 0xc5bdd8d0, 0xd26e, 0x11ce, 0xa8, 0x9e);
/*
* 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);
}
/*
* 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);
}
/*
* 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);
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment