- EM_STREAMIN can now deal with undo in a reasonable manner (no
multiple undo actions in one EM_STREAMIN). - Related changes to undo code (umIgnore mode is now handled correctly). - Numerous improvements in the RTF reader: it reads some character attributes now (you will have proper small print in license agreements now). - Fixed a memory overwrite bug in conversion from CHARFORMAT2A to CHARFORMAT2W.
This commit is contained in:
parent
8971f06225
commit
898068f3ce
|
@ -274,25 +274,151 @@ static LRESULT ME_StreamInText(ME_TextEditor *editor, DWORD dwFormat, EDITSTREAM
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ME_RTFCharAttrHook(RTF_Info *info)
|
||||||
|
{
|
||||||
|
CHARFORMAT2A fmt;
|
||||||
|
fmt.cbSize = sizeof(fmt);
|
||||||
|
fmt.dwMask = 0;
|
||||||
|
|
||||||
|
switch(info->rtfMinor)
|
||||||
|
{
|
||||||
|
case rtfBold:
|
||||||
|
fmt.dwMask = CFM_BOLD;
|
||||||
|
fmt.dwEffects = info->rtfParam ? fmt.dwMask : 0;
|
||||||
|
break;
|
||||||
|
case rtfItalic:
|
||||||
|
fmt.dwMask = CFM_ITALIC;
|
||||||
|
fmt.dwEffects = info->rtfParam ? fmt.dwMask : 0;
|
||||||
|
break;
|
||||||
|
case rtfUnderline:
|
||||||
|
fmt.dwMask = CFM_UNDERLINE;
|
||||||
|
fmt.dwEffects = info->rtfParam ? fmt.dwMask : 0;
|
||||||
|
break;
|
||||||
|
case rtfStrikeThru:
|
||||||
|
fmt.dwMask = CFM_STRIKEOUT;
|
||||||
|
fmt.dwEffects = info->rtfParam ? fmt.dwMask : 0;
|
||||||
|
break;
|
||||||
|
case rtfBackColor:
|
||||||
|
fmt.dwMask = CFM_BACKCOLOR;
|
||||||
|
fmt.dwEffects = 0;
|
||||||
|
if (info->rtfParam == 0)
|
||||||
|
fmt.dwEffects = CFE_AUTOBACKCOLOR;
|
||||||
|
else if (info->rtfParam != rtfNoParam)
|
||||||
|
{
|
||||||
|
RTFColor *c = RTFGetColor(info, info->rtfParam);
|
||||||
|
fmt.crTextColor = (c->rtfCBlue<<16)|(c->rtfCGreen<<8)|(c->rtfCRed);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case rtfForeColor:
|
||||||
|
fmt.dwMask = CFM_COLOR;
|
||||||
|
fmt.dwEffects = 0;
|
||||||
|
if (info->rtfParam == 0)
|
||||||
|
fmt.dwEffects = CFE_AUTOCOLOR;
|
||||||
|
else if (info->rtfParam != rtfNoParam)
|
||||||
|
{
|
||||||
|
RTFColor *c = RTFGetColor(info, info->rtfParam);
|
||||||
|
fmt.crTextColor = (c->rtfCBlue<<16)|(c->rtfCGreen<<8)|(c->rtfCRed);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case rtfFontNum:
|
||||||
|
if (info->rtfParam != rtfNoParam)
|
||||||
|
{
|
||||||
|
RTFFont *f = RTFGetFont(info, info->rtfParam);
|
||||||
|
if (f)
|
||||||
|
{
|
||||||
|
strncpy(fmt.szFaceName, f->rtfFName, sizeof(fmt.szFaceName)-1);
|
||||||
|
fmt.szFaceName[sizeof(fmt.szFaceName)-1] = '\0';
|
||||||
|
fmt.dwMask = CFM_FACE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case rtfFontSize:
|
||||||
|
fmt.dwMask = CFM_SIZE;
|
||||||
|
if (info->rtfParam != rtfNoParam)
|
||||||
|
fmt.yHeight = info->rtfParam*10;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (fmt.dwMask) {
|
||||||
|
RTFFlushOutputBuffer(info);
|
||||||
|
SendMessageW(info->hwndEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&fmt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ME_RTFParAttrHook(RTF_Info *info)
|
||||||
|
{
|
||||||
|
PARAFORMAT2 fmt;
|
||||||
|
fmt.cbSize = sizeof(fmt);
|
||||||
|
fmt.dwMask = 0;
|
||||||
|
|
||||||
|
switch(info->rtfMinor)
|
||||||
|
{
|
||||||
|
case rtfParDef: /* I'm not 100% sure what does it do, but I guess it restores default paragraph attributes */
|
||||||
|
fmt.dwMask = PFM_ALIGNMENT;
|
||||||
|
fmt.wAlignment = PFA_LEFT;
|
||||||
|
break;
|
||||||
|
case rtfQuadLeft:
|
||||||
|
case rtfQuadJust:
|
||||||
|
fmt.dwMask = PFM_ALIGNMENT;
|
||||||
|
fmt.wAlignment = PFA_LEFT;
|
||||||
|
break;
|
||||||
|
case rtfQuadRight:
|
||||||
|
fmt.dwMask = PFM_ALIGNMENT;
|
||||||
|
fmt.wAlignment = PFA_RIGHT;
|
||||||
|
break;
|
||||||
|
case rtfQuadCenter:
|
||||||
|
fmt.dwMask = PFM_ALIGNMENT;
|
||||||
|
fmt.wAlignment = PFA_CENTER;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (fmt.dwMask) {
|
||||||
|
RTFFlushOutputBuffer(info);
|
||||||
|
SendMessageW(info->hwndEdit, EM_SETPARAFORMAT, 0, (LPARAM)&fmt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ME_RTFReadHook(RTF_Info *info) {
|
||||||
|
switch(info->rtfClass)
|
||||||
|
{
|
||||||
|
case rtfControl:
|
||||||
|
switch(info->rtfMajor)
|
||||||
|
{
|
||||||
|
case rtfCharAttr:
|
||||||
|
ME_RTFCharAttrHook(info);
|
||||||
|
break;
|
||||||
|
case rtfParAttr:
|
||||||
|
ME_RTFParAttrHook(info);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static LRESULT ME_StreamIn(ME_TextEditor *editor, DWORD format, EDITSTREAM *stream)
|
static LRESULT ME_StreamIn(ME_TextEditor *editor, DWORD format, EDITSTREAM *stream)
|
||||||
{
|
{
|
||||||
RTF_Info parser;
|
RTF_Info parser;
|
||||||
ME_Style *style;
|
ME_Style *style;
|
||||||
|
int from, to, to2, nUndoMode;
|
||||||
|
ME_UndoItem *pUI;
|
||||||
|
|
||||||
TRACE("%p %p\n", stream, editor->hWnd);
|
TRACE("%p %p\n", stream, editor->hWnd);
|
||||||
|
|
||||||
|
ME_GetSelection(editor, &from, &to);
|
||||||
if (format & SFF_SELECTION) {
|
if (format & SFF_SELECTION) {
|
||||||
style = ME_GetSelectionInsertStyle(editor);
|
style = ME_GetSelectionInsertStyle(editor);
|
||||||
SendMessageW(editor->hWnd, WM_CLEAR, 0, 0);
|
|
||||||
|
ME_InternalDeleteText(editor, from, to-from);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
style = editor->pBuffer->pDefaultStyle;
|
style = editor->pBuffer->pDefaultStyle;
|
||||||
ME_AddRefStyle(style);
|
ME_AddRefStyle(style);
|
||||||
SendMessageA(editor->hWnd, EM_SETSEL, 0, 0);
|
SendMessageA(editor->hWnd, EM_SETSEL, 0, 0);
|
||||||
SetWindowTextA(editor->hWnd, "");
|
ME_InternalDeleteText(editor, 0, ME_GetTextLength(editor));
|
||||||
|
from = to = 0;
|
||||||
ME_ClearTempStyle(editor);
|
ME_ClearTempStyle(editor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nUndoMode = editor->nUndoMode;
|
||||||
|
editor->nUndoMode = umIgnore;
|
||||||
if (format & SF_RTF) {
|
if (format & SF_RTF) {
|
||||||
/* setup the RTF parser */
|
/* setup the RTF parser */
|
||||||
memset(&parser, 0, sizeof parser);
|
memset(&parser, 0, sizeof parser);
|
||||||
|
@ -301,6 +427,7 @@ static LRESULT ME_StreamIn(ME_TextEditor *editor, DWORD format, EDITSTREAM *stre
|
||||||
parser.hwndEdit = editor->hWnd;
|
parser.hwndEdit = editor->hWnd;
|
||||||
WriterInit(&parser);
|
WriterInit(&parser);
|
||||||
RTFInit(&parser);
|
RTFInit(&parser);
|
||||||
|
RTFSetReadHook(&parser, ME_RTFReadHook);
|
||||||
BeginFile(&parser);
|
BeginFile(&parser);
|
||||||
|
|
||||||
/* do the parsing */
|
/* do the parsing */
|
||||||
|
@ -311,6 +438,7 @@ static LRESULT ME_StreamIn(ME_TextEditor *editor, DWORD format, EDITSTREAM *stre
|
||||||
ME_StreamInText(editor, format, stream, style);
|
ME_StreamInText(editor, format, stream, style);
|
||||||
else
|
else
|
||||||
ERR("EM_STREAMIN without SF_TEXT or SF_RTF\n");
|
ERR("EM_STREAMIN without SF_TEXT or SF_RTF\n");
|
||||||
|
ME_GetSelection(editor, &to, &to2);
|
||||||
/* put the cursor at the top */
|
/* put the cursor at the top */
|
||||||
if (!(format & SFF_SELECTION))
|
if (!(format & SFF_SELECTION))
|
||||||
SendMessageA(editor->hWnd, EM_SETSEL, 0, 0);
|
SendMessageA(editor->hWnd, EM_SETSEL, 0, 0);
|
||||||
|
@ -318,6 +446,16 @@ static LRESULT ME_StreamIn(ME_TextEditor *editor, DWORD format, EDITSTREAM *stre
|
||||||
{
|
{
|
||||||
/* FIXME where to put cursor now ? */
|
/* FIXME where to put cursor now ? */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
editor->nUndoMode = nUndoMode;
|
||||||
|
pUI = ME_AddUndoItem(editor, diUndoDeleteRun, NULL);
|
||||||
|
FIXME("from %d to %d\n", from, to);
|
||||||
|
if (pUI && from < to)
|
||||||
|
{
|
||||||
|
pUI->nStart = from;
|
||||||
|
pUI->nLen = to-from;
|
||||||
|
}
|
||||||
|
ME_CommitUndo(editor);
|
||||||
ME_ReleaseStyle(style);
|
ME_ReleaseStyle(style);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -40,6 +40,7 @@
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
#include "rtf.h"
|
#include "rtf.h"
|
||||||
|
|
||||||
|
@ -3590,6 +3591,16 @@ BeginFile (RTF_Info *info )
|
||||||
return (1);
|
return (1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Write out a character. Seems to work for the default ANSI codepage,
|
||||||
|
* contrary to TextClass_orig.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static void
|
||||||
|
TextClass (RTF_Info *info)
|
||||||
|
{
|
||||||
|
PutLitChar (info, info->rtfMajor);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Write out a character. rtfMajor contains the input character, rtfMinor
|
* Write out a character. rtfMajor contains the input character, rtfMinor
|
||||||
|
@ -3597,10 +3608,13 @@ BeginFile (RTF_Info *info )
|
||||||
*
|
*
|
||||||
* If the input character isn't in the charset map, try to print some
|
* If the input character isn't in the charset map, try to print some
|
||||||
* representation of it.
|
* representation of it.
|
||||||
|
*
|
||||||
|
* I'm not removing it, because it may be helpful if someone else decides
|
||||||
|
* to rewrite the character handler in a i18n-friendly way
|
||||||
*/
|
*/
|
||||||
|
#if 0
|
||||||
static void
|
static void
|
||||||
TextClass (RTF_Info *info)
|
TextClass_orig (RTF_Info *info)
|
||||||
{
|
{
|
||||||
char buf[rtfBufSiz];
|
char buf[rtfBufSiz];
|
||||||
|
|
||||||
|
@ -3612,13 +3626,15 @@ TextClass (RTF_Info *info)
|
||||||
PutStdChar (info, info->rtfMinor);
|
PutStdChar (info, info->rtfMinor);
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (info->rtfMajor < 128) /* in ASCII range */
|
if (info->rtfMajor < 256) /* in ASCII range */
|
||||||
sprintf (buf, "[[%c]]", info->rtfMajor);
|
PutLitChar(info, info->rtfMajor);
|
||||||
else
|
else {
|
||||||
sprintf (buf, "[[\\'%02x]]", info->rtfMajor);
|
sprintf (buf, "[[\\'%02x]]", info->rtfMajor);
|
||||||
PutLitStr (info, buf);
|
PutLitStr (info, buf);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -3763,10 +3779,16 @@ void PutLitChar (RTF_Info *info, int c)
|
||||||
info->OutputBuffer[info->dwOutputCount++] = c;
|
info->OutputBuffer[info->dwOutputCount++] = c;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RTFOutputANSIString( RTF_Info *info, char *str, int len )
|
||||||
|
{
|
||||||
|
assert(str[len] == '\0');
|
||||||
|
if (len) SendMessageA( info->hwndEdit, EM_REPLACESEL, FALSE, (LPARAM) str);
|
||||||
|
}
|
||||||
|
|
||||||
void RTFFlushOutputBuffer( RTF_Info *info )
|
void RTFFlushOutputBuffer( RTF_Info *info )
|
||||||
{
|
{
|
||||||
info->OutputBuffer[info->dwOutputCount] = 0;
|
info->OutputBuffer[info->dwOutputCount] = 0;
|
||||||
SendMessageA( info->hwndEdit, EM_REPLACESEL, FALSE, (LPARAM) info->OutputBuffer );
|
RTFOutputANSIString(info, info->OutputBuffer, info->dwOutputCount);
|
||||||
info->dwOutputCount = 0;
|
info->dwOutputCount = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3778,7 +3800,7 @@ static void PutLitStr (RTF_Info *info, char *str )
|
||||||
RTFFlushOutputBuffer( info );
|
RTFFlushOutputBuffer( info );
|
||||||
if( ( len + 1 ) >= sizeof info->OutputBuffer )
|
if( ( len + 1 ) >= sizeof info->OutputBuffer )
|
||||||
{
|
{
|
||||||
SendMessageA( info->hwndEdit, EM_REPLACESEL, FALSE, (LPARAM) str );
|
RTFOutputANSIString(info, str, len);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
strcpy( &info->OutputBuffer[info->dwOutputCount], str );
|
strcpy( &info->OutputBuffer[info->dwOutputCount], str );
|
||||||
|
|
|
@ -302,8 +302,10 @@ ME_DisplayItem *ME_InsertRun(ME_TextEditor *editor, int nCharOfs, ME_DisplayItem
|
||||||
assert(pItem->type == diRun || pItem->type == diUndoInsertRun);
|
assert(pItem->type == diRun || pItem->type == diUndoInsertRun);
|
||||||
|
|
||||||
pUI = ME_AddUndoItem(editor, diUndoDeleteRun, NULL);
|
pUI = ME_AddUndoItem(editor, diUndoDeleteRun, NULL);
|
||||||
pUI->nStart = nCharOfs;
|
if (pUI) {
|
||||||
pUI->nLen = pItem->member.run.strText->nLen;
|
pUI->nStart = nCharOfs;
|
||||||
|
pUI->nLen = pItem->member.run.strText->nLen;
|
||||||
|
}
|
||||||
ME_CursorFromCharOfs(editor, nCharOfs, &tmp);
|
ME_CursorFromCharOfs(editor, nCharOfs, &tmp);
|
||||||
if (tmp.nOffset) {
|
if (tmp.nOffset) {
|
||||||
tmp.pRun = ME_SplitRunSimple(editor, tmp.pRun, tmp.nOffset);
|
tmp.pRun = ME_SplitRunSimple(editor, tmp.pRun, tmp.nOffset);
|
||||||
|
|
|
@ -54,7 +54,7 @@ CHARFORMAT2W *ME_ToCF2W(CHARFORMAT2W *to, CHARFORMAT2W *from)
|
||||||
if (f->dwMask & CFM_FACE)
|
if (f->dwMask & CFM_FACE)
|
||||||
MultiByteToWideChar(0, 0, f->szFaceName, -1, to->szFaceName, sizeof(to->szFaceName));
|
MultiByteToWideChar(0, 0, f->szFaceName, -1, to->szFaceName, sizeof(to->szFaceName));
|
||||||
/* copy the rest of the 2A structure to 2W */
|
/* copy the rest of the 2A structure to 2W */
|
||||||
CopyMemory(1+((CHARFORMATW *)from), f+1, sizeof(CHARFORMAT2A)-sizeof(CHARFORMATA));
|
CopyMemory(1+((CHARFORMATW *)to), f+1, sizeof(CHARFORMAT2A)-sizeof(CHARFORMATA));
|
||||||
to->cbSize = sizeof(CHARFORMAT2W);
|
to->cbSize = sizeof(CHARFORMAT2W);
|
||||||
return to;
|
return to;
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,6 +26,9 @@ void ME_EmptyUndoStack(ME_TextEditor *editor)
|
||||||
{
|
{
|
||||||
ME_DisplayItem *p, *pNext;
|
ME_DisplayItem *p, *pNext;
|
||||||
|
|
||||||
|
if (editor->nUndoMode == umIgnore)
|
||||||
|
return;
|
||||||
|
|
||||||
TRACE("Emptying undo stack\n");
|
TRACE("Emptying undo stack\n");
|
||||||
|
|
||||||
p = editor->pUndoStack;
|
p = editor->pUndoStack;
|
||||||
|
@ -121,6 +124,10 @@ ME_UndoItem *ME_AddUndoItem(ME_TextEditor *editor, ME_DIType type, ME_DisplayIte
|
||||||
}
|
}
|
||||||
|
|
||||||
void ME_CommitUndo(ME_TextEditor *editor) {
|
void ME_CommitUndo(ME_TextEditor *editor) {
|
||||||
|
|
||||||
|
if (editor->nUndoMode == umIgnore)
|
||||||
|
return;
|
||||||
|
|
||||||
assert(editor->nUndoMode == umAddToUndo);
|
assert(editor->nUndoMode == umAddToUndo);
|
||||||
|
|
||||||
/* no transactions, no need to commit */
|
/* no transactions, no need to commit */
|
||||||
|
@ -140,6 +147,8 @@ void ME_PlayUndoItem(ME_TextEditor *editor, ME_DisplayItem *pItem)
|
||||||
{
|
{
|
||||||
ME_UndoItem *pUItem = (ME_UndoItem *)pItem;
|
ME_UndoItem *pUItem = (ME_UndoItem *)pItem;
|
||||||
|
|
||||||
|
if (editor->nUndoMode == umIgnore)
|
||||||
|
return;
|
||||||
TRACE("Playing undo/redo item, id=%s\n", ME_GetDITypeName(pItem->type));
|
TRACE("Playing undo/redo item, id=%s\n", ME_GetDITypeName(pItem->type));
|
||||||
|
|
||||||
switch(pItem->type)
|
switch(pItem->type)
|
||||||
|
@ -202,6 +211,8 @@ void ME_Undo(ME_TextEditor *editor) {
|
||||||
ME_DisplayItem *p;
|
ME_DisplayItem *p;
|
||||||
ME_UndoMode nMode = editor->nUndoMode;
|
ME_UndoMode nMode = editor->nUndoMode;
|
||||||
|
|
||||||
|
if (editor->nUndoMode == umIgnore)
|
||||||
|
return;
|
||||||
assert(nMode == umAddToUndo || nMode == umIgnore);
|
assert(nMode == umAddToUndo || nMode == umIgnore);
|
||||||
|
|
||||||
/* no undo items ? */
|
/* no undo items ? */
|
||||||
|
@ -235,6 +246,8 @@ void ME_Redo(ME_TextEditor *editor) {
|
||||||
|
|
||||||
assert(nMode == umAddToUndo || nMode == umIgnore);
|
assert(nMode == umAddToUndo || nMode == umIgnore);
|
||||||
|
|
||||||
|
if (editor->nUndoMode == umIgnore)
|
||||||
|
return;
|
||||||
/* no redo items ? */
|
/* no redo items ? */
|
||||||
if (!editor->pRedoStack)
|
if (!editor->pRedoStack)
|
||||||
return;
|
return;
|
||||||
|
|
Loading…
Reference in New Issue