riched20: Move undo handling to a different set of structs as the display item structs are not a good fit.
This commit is contained in:
parent
cc796b9569
commit
dee40e90ce
|
@ -361,7 +361,6 @@ BOOL ME_InternalDeleteText(ME_TextEditor *editor, ME_Cursor *start,
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ME_Cursor cursor;
|
ME_Cursor cursor;
|
||||||
ME_UndoItem *undo;
|
|
||||||
int nCharsToDelete = min(nChars, c.nOffset);
|
int nCharsToDelete = min(nChars, c.nOffset);
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
|
@ -379,14 +378,9 @@ BOOL ME_InternalDeleteText(ME_TextEditor *editor, ME_Cursor *start,
|
||||||
nCharsToDelete, nChars, c.nOffset,
|
nCharsToDelete, nChars, c.nOffset,
|
||||||
debugstr_w(run->strText->szData), run->strText->nLen);
|
debugstr_w(run->strText->szData), run->strText->nLen);
|
||||||
|
|
||||||
undo = ME_AddUndoItem(editor, diUndoInsertRun, c.pRun);
|
/* nOfs is a character offset (from the start of the document
|
||||||
if (undo)
|
to the current (deleted) run */
|
||||||
{
|
add_undo_insert_run( editor, nOfs + nChars, run->strText->szData + c.nOffset, nCharsToDelete, run->nFlags, run->style );
|
||||||
/* nOfs is a character offset (from the start of the document
|
|
||||||
to the current (deleted) run */
|
|
||||||
undo->di.member.run.nCharOfs = nOfs + nChars;
|
|
||||||
undo->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("Post deletion string: %s (%d)\n", debugstr_w(run->strText->szData), run->strText->nLen);
|
||||||
TRACE("Shift value: %d\n", shift);
|
TRACE("Shift value: %d\n", shift);
|
||||||
|
|
|
@ -2709,7 +2709,8 @@ ME_TextEditor *ME_MakeEditor(ITextHost *texthost, BOOL bEmulateVersion10)
|
||||||
ed->nEventMask = 0;
|
ed->nEventMask = 0;
|
||||||
ed->nModifyStep = 0;
|
ed->nModifyStep = 0;
|
||||||
ed->nTextLimit = TEXT_LIMIT_DEFAULT;
|
ed->nTextLimit = TEXT_LIMIT_DEFAULT;
|
||||||
ed->pUndoStack = ed->pRedoStack = ed->pUndoStackBottom = NULL;
|
list_init( &ed->undo_stack );
|
||||||
|
list_init( &ed->redo_stack );
|
||||||
ed->nUndoStackSize = 0;
|
ed->nUndoStackSize = 0;
|
||||||
ed->nUndoLimit = STACK_SIZE_DEFAULT;
|
ed->nUndoLimit = STACK_SIZE_DEFAULT;
|
||||||
ed->nUndoMode = umAddToUndo;
|
ed->nUndoMode = umAddToUndo;
|
||||||
|
@ -3106,9 +3107,9 @@ LRESULT ME_HandleMessage(ME_TextEditor *editor, UINT msg, WPARAM wParam,
|
||||||
return editor->nUndoLimit;
|
return editor->nUndoLimit;
|
||||||
}
|
}
|
||||||
case EM_CANUNDO:
|
case EM_CANUNDO:
|
||||||
return editor->pUndoStack != NULL;
|
return !list_empty( &editor->undo_stack );
|
||||||
case EM_CANREDO:
|
case EM_CANREDO:
|
||||||
return editor->pRedoStack != NULL;
|
return !list_empty( &editor->redo_stack );
|
||||||
case WM_UNDO: /* FIXME: actually not the same */
|
case WM_UNDO: /* FIXME: actually not the same */
|
||||||
case EM_UNDO:
|
case EM_UNDO:
|
||||||
return ME_Undo(editor);
|
return ME_Undo(editor);
|
||||||
|
@ -4348,7 +4349,8 @@ LRESULT ME_HandleMessage(ME_TextEditor *editor, UINT msg, WPARAM wParam,
|
||||||
int mask = 0;
|
int mask = 0;
|
||||||
int changes = 0;
|
int changes = 0;
|
||||||
|
|
||||||
if (ME_GetTextLength(editor) || editor->pUndoStack || editor->pRedoStack)
|
if (ME_GetTextLength(editor) ||
|
||||||
|
!list_empty( &editor->undo_stack ) || !list_empty( &editor->redo_stack ))
|
||||||
return E_UNEXPECTED;
|
return E_UNEXPECTED;
|
||||||
|
|
||||||
/* Check for mutually exclusive flags in adjacent bits of wParam */
|
/* Check for mutually exclusive flags in adjacent bits of wParam */
|
||||||
|
|
|
@ -317,7 +317,12 @@ ITextHost *ME_CreateTextHost(HWND hwnd, CREATESTRUCTW *cs, BOOL bEmulateVersion1
|
||||||
#define ITextHost_TxGetSelectionBarWidth(This,a) TXTHOST_VTABLE(This)->TxGetSelectionBarWidth(This,a)
|
#define ITextHost_TxGetSelectionBarWidth(This,a) TXTHOST_VTABLE(This)->TxGetSelectionBarWidth(This,a)
|
||||||
|
|
||||||
/* undo.c */
|
/* undo.c */
|
||||||
ME_UndoItem *ME_AddUndoItem(ME_TextEditor *editor, ME_DIType type, const ME_DisplayItem *pdi) DECLSPEC_HIDDEN;
|
BOOL add_undo_insert_run( ME_TextEditor *, int pos, const WCHAR *str, int len, int flags, ME_Style *style ) DECLSPEC_HIDDEN;
|
||||||
|
BOOL add_undo_delete_run( ME_TextEditor *, int pos, int len ) DECLSPEC_HIDDEN;
|
||||||
|
BOOL add_undo_set_para_fmt( ME_TextEditor *, const ME_Paragraph *para ) DECLSPEC_HIDDEN;
|
||||||
|
BOOL add_undo_set_char_fmt( ME_TextEditor *, int pos, int len, const CHARFORMAT2W *fmt ) DECLSPEC_HIDDEN;
|
||||||
|
BOOL add_undo_join_paras( ME_TextEditor *, int pos ) DECLSPEC_HIDDEN;
|
||||||
|
BOOL add_undo_split_para( ME_TextEditor *, const ME_Paragraph *para, const ME_Run *run, const ME_Cell *cell) DECLSPEC_HIDDEN;
|
||||||
void ME_CommitUndo(ME_TextEditor *editor) DECLSPEC_HIDDEN;
|
void ME_CommitUndo(ME_TextEditor *editor) DECLSPEC_HIDDEN;
|
||||||
void ME_ContinueCoalescingTransaction(ME_TextEditor *editor) DECLSPEC_HIDDEN;
|
void ME_ContinueCoalescingTransaction(ME_TextEditor *editor) DECLSPEC_HIDDEN;
|
||||||
void ME_CommitCoalescingUndo(ME_TextEditor *editor) DECLSPEC_HIDDEN;
|
void ME_CommitCoalescingUndo(ME_TextEditor *editor) DECLSPEC_HIDDEN;
|
||||||
|
|
|
@ -49,6 +49,7 @@
|
||||||
#include <textserv.h>
|
#include <textserv.h>
|
||||||
|
|
||||||
#include "wine/debug.h"
|
#include "wine/debug.h"
|
||||||
|
#include "wine/list.h"
|
||||||
|
|
||||||
#ifdef __i386__
|
#ifdef __i386__
|
||||||
extern const struct ITextHostVtbl itextHostStdcallVtbl;
|
extern const struct ITextHostVtbl itextHostStdcallVtbl;
|
||||||
|
@ -85,15 +86,6 @@ typedef enum {
|
||||||
diRunOrStartRow,
|
diRunOrStartRow,
|
||||||
diParagraphOrEnd,
|
diParagraphOrEnd,
|
||||||
diRunOrParagraphOrEnd, /* 12 */
|
diRunOrParagraphOrEnd, /* 12 */
|
||||||
|
|
||||||
diUndoInsertRun, /* 13 */
|
|
||||||
diUndoDeleteRun, /* 14 */
|
|
||||||
diUndoJoinParagraphs, /* 15 */
|
|
||||||
diUndoSplitParagraph, /* 16 */
|
|
||||||
diUndoSetParagraphFormat, /* 17 */
|
|
||||||
diUndoSetCharFormat, /* 18 */
|
|
||||||
diUndoEndTransaction, /* 19 - marks the end of a group of changes for undo */
|
|
||||||
diUndoPotentialEndTransaction, /* 20 - allows grouping typed chars for undo */
|
|
||||||
} ME_DIType;
|
} ME_DIType;
|
||||||
|
|
||||||
#define SELECTIONBAR_WIDTH 8
|
#define SELECTIONBAR_WIDTH 8
|
||||||
|
@ -230,17 +222,9 @@ typedef struct tagME_DisplayItem
|
||||||
ME_Row row;
|
ME_Row row;
|
||||||
ME_Cell cell;
|
ME_Cell cell;
|
||||||
ME_Paragraph para;
|
ME_Paragraph para;
|
||||||
ME_Style *ustyle; /* used by diUndoSetCharFormat */
|
|
||||||
} member;
|
} member;
|
||||||
} ME_DisplayItem;
|
} ME_DisplayItem;
|
||||||
|
|
||||||
typedef struct tagME_UndoItem
|
|
||||||
{
|
|
||||||
ME_DisplayItem di;
|
|
||||||
int nStart, nLen;
|
|
||||||
ME_String *eol_str; /* used by diUndoSplitParagraph */
|
|
||||||
} ME_UndoItem;
|
|
||||||
|
|
||||||
typedef struct tagME_TextBuffer
|
typedef struct tagME_TextBuffer
|
||||||
{
|
{
|
||||||
ME_DisplayItem *pFirst, *pLast;
|
ME_DisplayItem *pFirst, *pLast;
|
||||||
|
@ -262,6 +246,75 @@ typedef enum {
|
||||||
umAddBackToUndo
|
umAddBackToUndo
|
||||||
} ME_UndoMode;
|
} ME_UndoMode;
|
||||||
|
|
||||||
|
enum undo_type
|
||||||
|
{
|
||||||
|
undo_insert_run,
|
||||||
|
undo_delete_run,
|
||||||
|
undo_join_paras,
|
||||||
|
undo_split_para,
|
||||||
|
undo_set_para_fmt,
|
||||||
|
undo_set_char_fmt,
|
||||||
|
undo_end_transaction, /* marks the end of a group of changes for undo */
|
||||||
|
undo_potential_end_transaction /* allows grouping typed chars for undo */
|
||||||
|
};
|
||||||
|
|
||||||
|
struct insert_run_item
|
||||||
|
{
|
||||||
|
int pos, len;
|
||||||
|
WCHAR *str;
|
||||||
|
ME_Style *style;
|
||||||
|
DWORD flags;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct delete_run_item
|
||||||
|
{
|
||||||
|
int pos, len;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct join_paras_item
|
||||||
|
{
|
||||||
|
int pos;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct split_para_item
|
||||||
|
{
|
||||||
|
int pos;
|
||||||
|
PARAFORMAT2 fmt;
|
||||||
|
ME_BorderRect border;
|
||||||
|
ME_String *eol_str;
|
||||||
|
DWORD flags;
|
||||||
|
ME_BorderRect cell_border;
|
||||||
|
int cell_right_boundary;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct set_para_fmt_item
|
||||||
|
{
|
||||||
|
int pos;
|
||||||
|
PARAFORMAT2 fmt;
|
||||||
|
ME_BorderRect border;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct set_char_fmt_item
|
||||||
|
{
|
||||||
|
int pos, len;
|
||||||
|
CHARFORMAT2W fmt;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct undo_item
|
||||||
|
{
|
||||||
|
struct list entry;
|
||||||
|
enum undo_type type;
|
||||||
|
union
|
||||||
|
{
|
||||||
|
struct insert_run_item insert_run;
|
||||||
|
struct delete_run_item delete_run;
|
||||||
|
struct join_paras_item join_paras;
|
||||||
|
struct split_para_item split_para;
|
||||||
|
struct set_para_fmt_item set_para_fmt;
|
||||||
|
struct set_char_fmt_item set_char_fmt;
|
||||||
|
} u;
|
||||||
|
};
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
stPosition = 0,
|
stPosition = 0,
|
||||||
stWord,
|
stWord,
|
||||||
|
@ -337,7 +390,8 @@ typedef struct tagME_TextEditor
|
||||||
BOOL bCaretAtEnd;
|
BOOL bCaretAtEnd;
|
||||||
int nEventMask;
|
int nEventMask;
|
||||||
int nModifyStep;
|
int nModifyStep;
|
||||||
ME_DisplayItem *pUndoStack, *pRedoStack, *pUndoStackBottom;
|
struct list undo_stack;
|
||||||
|
struct list redo_stack;
|
||||||
int nUndoStackSize;
|
int nUndoStackSize;
|
||||||
int nUndoLimit;
|
int nUndoLimit;
|
||||||
ME_UndoMode nUndoMode;
|
ME_UndoMode nUndoMode;
|
||||||
|
|
|
@ -139,32 +139,29 @@ ME_DisplayItem *ME_FindItemFwd(ME_DisplayItem *di, ME_DIType nTypeOrClass)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ME_DestroyDisplayItem(ME_DisplayItem *item) {
|
void ME_DestroyDisplayItem(ME_DisplayItem *item)
|
||||||
|
{
|
||||||
/* TRACE("type=%s\n", ME_GetDITypeName(item->type)); */
|
/* TRACE("type=%s\n", ME_GetDITypeName(item->type)); */
|
||||||
if (item->type==diParagraph || item->type == diUndoSetParagraphFormat) {
|
if (item->type==diParagraph)
|
||||||
FREE_OBJ(item->member.para.pFmt);
|
FREE_OBJ(item->member.para.pFmt);
|
||||||
}
|
|
||||||
if (item->type==diRun || item->type == diUndoInsertRun) {
|
if (item->type==diRun)
|
||||||
|
{
|
||||||
if (item->member.run.ole_obj) ME_DeleteReObject(item->member.run.ole_obj);
|
if (item->member.run.ole_obj) ME_DeleteReObject(item->member.run.ole_obj);
|
||||||
ME_ReleaseStyle(item->member.run.style);
|
ME_ReleaseStyle(item->member.run.style);
|
||||||
ME_DestroyString(item->member.run.strText);
|
ME_DestroyString(item->member.run.strText);
|
||||||
}
|
}
|
||||||
if (item->type==diUndoSetCharFormat) {
|
|
||||||
ME_ReleaseStyle(item->member.ustyle);
|
|
||||||
}
|
|
||||||
if (item->type==diUndoSplitParagraph) {
|
|
||||||
FREE_OBJ(item->member.para.pFmt);
|
|
||||||
FREE_OBJ(item->member.para.pCell);
|
|
||||||
}
|
|
||||||
FREE_OBJ(item);
|
FREE_OBJ(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
ME_DisplayItem *ME_MakeDI(ME_DIType type) {
|
ME_DisplayItem *ME_MakeDI(ME_DIType type)
|
||||||
|
{
|
||||||
ME_DisplayItem *item = ALLOC_OBJ(ME_DisplayItem);
|
ME_DisplayItem *item = ALLOC_OBJ(ME_DisplayItem);
|
||||||
ZeroMemory(item, sizeof(ME_DisplayItem));
|
ZeroMemory(item, sizeof(ME_DisplayItem));
|
||||||
item->type = type;
|
item->type = type;
|
||||||
item->prev = item->next = NULL;
|
item->prev = item->next = NULL;
|
||||||
if (type == diParagraph || type == diUndoSplitParagraph) {
|
if (type == diParagraph)
|
||||||
|
{
|
||||||
item->member.para.pFmt = ALLOC_OBJ(PARAFORMAT2);
|
item->member.para.pFmt = ALLOC_OBJ(PARAFORMAT2);
|
||||||
ME_SetDefaultParaFormat(item->member.para.pFmt);
|
ME_SetDefaultParaFormat(item->member.para.pFmt);
|
||||||
item->member.para.nFlags = MEPF_REWRAP;
|
item->member.para.nFlags = MEPF_REWRAP;
|
||||||
|
@ -183,14 +180,6 @@ const char *ME_GetDITypeName(ME_DIType type)
|
||||||
case diTextStart: return "diTextStart";
|
case diTextStart: return "diTextStart";
|
||||||
case diTextEnd: return "diTextEnd";
|
case diTextEnd: return "diTextEnd";
|
||||||
case diStartRow: return "diStartRow";
|
case diStartRow: return "diStartRow";
|
||||||
case diUndoEndTransaction: return "diUndoEndTransaction";
|
|
||||||
case diUndoPotentialEndTransaction: return "diUndoPotentialEndTransaction";
|
|
||||||
case diUndoSetParagraphFormat: return "diUndoSetParagraphFormat";
|
|
||||||
case diUndoSetCharFormat: return "diUndoSetCharFormat";
|
|
||||||
case diUndoInsertRun: return "diUndoInsertRun";
|
|
||||||
case diUndoDeleteRun: return "diUndoDeleteRun";
|
|
||||||
case diUndoJoinParagraphs: return "diJoinParagraphs";
|
|
||||||
case diUndoSplitParagraph: return "diSplitParagraph";
|
|
||||||
default: return "?";
|
default: return "?";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -127,7 +127,7 @@ static BOOL ME_SetParaFormat(ME_TextEditor *editor, ME_DisplayItem *para, const
|
||||||
else
|
else
|
||||||
dwMask &= PFM_ALL2;
|
dwMask &= PFM_ALL2;
|
||||||
|
|
||||||
ME_AddUndoItem(editor, diUndoSetParagraphFormat, para);
|
add_undo_set_para_fmt( editor, ¶->member.para );
|
||||||
|
|
||||||
copy = *para->member.para.pFmt;
|
copy = *para->member.para.pFmt;
|
||||||
|
|
||||||
|
@ -198,7 +198,6 @@ ME_DisplayItem *ME_SplitParagraph(ME_TextEditor *editor, ME_DisplayItem *run,
|
||||||
ME_DisplayItem *run_para = NULL;
|
ME_DisplayItem *run_para = NULL;
|
||||||
ME_DisplayItem *new_para = ME_MakeDI(diParagraph);
|
ME_DisplayItem *new_para = ME_MakeDI(diParagraph);
|
||||||
ME_DisplayItem *end_run;
|
ME_DisplayItem *end_run;
|
||||||
ME_UndoItem *undo = NULL;
|
|
||||||
int ofs, i;
|
int ofs, i;
|
||||||
ME_DisplayItem *pp;
|
ME_DisplayItem *pp;
|
||||||
int run_flags = MERF_ENDPARA;
|
int run_flags = MERF_ENDPARA;
|
||||||
|
@ -224,9 +223,7 @@ ME_DisplayItem *ME_SplitParagraph(ME_TextEditor *editor, ME_DisplayItem *run,
|
||||||
next_para = run_para->member.para.next_para;
|
next_para = run_para->member.para.next_para;
|
||||||
assert(next_para == ME_FindItemFwd(run_para, diParagraphOrEnd));
|
assert(next_para == ME_FindItemFwd(run_para, diParagraphOrEnd));
|
||||||
|
|
||||||
undo = ME_AddUndoItem(editor, diUndoJoinParagraphs, NULL);
|
add_undo_join_paras( editor, run_para->member.para.nCharOfs + ofs );
|
||||||
if (undo)
|
|
||||||
undo->nStart = run_para->member.para.nCharOfs + ofs;
|
|
||||||
|
|
||||||
/* Update selection cursors to point to the correct paragraph. */
|
/* Update selection cursors to point to the correct paragraph. */
|
||||||
for (i = 0; i < editor->nCursors; i++) {
|
for (i = 0; i < editor->nCursors; i++) {
|
||||||
|
@ -324,7 +321,6 @@ ME_DisplayItem *ME_JoinParagraphs(ME_TextEditor *editor, ME_DisplayItem *tp,
|
||||||
{
|
{
|
||||||
ME_DisplayItem *pNext, *pFirstRunInNext, *pRun, *pTmp, *pCell = NULL;
|
ME_DisplayItem *pNext, *pFirstRunInNext, *pRun, *pTmp, *pCell = NULL;
|
||||||
int i, shift;
|
int i, shift;
|
||||||
ME_UndoItem *undo = NULL;
|
|
||||||
int end_len;
|
int end_len;
|
||||||
CHARFORMAT2W fmt;
|
CHARFORMAT2W fmt;
|
||||||
ME_Cursor startCur, endCur;
|
ME_Cursor startCur, endCur;
|
||||||
|
@ -370,26 +366,7 @@ ME_DisplayItem *ME_JoinParagraphs(ME_TextEditor *editor, ME_DisplayItem *tp,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
undo = ME_AddUndoItem(editor, diUndoSplitParagraph, pNext);
|
add_undo_split_para( editor, &pNext->member.para, &pRun->member.run, pCell ? &pCell->member.cell : NULL );
|
||||||
if (undo)
|
|
||||||
{
|
|
||||||
undo->nStart = pNext->member.para.nCharOfs - end_len;
|
|
||||||
undo->eol_str = pRun->member.run.strText;
|
|
||||||
pRun->member.run.strText = NULL; /* Avoid freeing the string */
|
|
||||||
|
|
||||||
if (pCell)
|
|
||||||
{
|
|
||||||
assert(!(undo->di.member.para.nFlags & MEPF_ROWEND));
|
|
||||||
if (!(undo->di.member.para.nFlags & MEPF_ROWSTART))
|
|
||||||
undo->di.member.para.nFlags |= MEPF_CELL;
|
|
||||||
undo->di.member.para.pCell = ALLOC_OBJ(ME_DisplayItem);
|
|
||||||
*undo->di.member.para.pCell = *pCell;
|
|
||||||
undo->di.member.para.pCell->next = NULL;
|
|
||||||
undo->di.member.para.pCell->prev = NULL;
|
|
||||||
undo->di.member.para.pCell->member.cell.next_cell = NULL;
|
|
||||||
undo->di.member.para.pCell->member.cell.prev_cell = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pCell)
|
if (pCell)
|
||||||
{
|
{
|
||||||
|
@ -403,7 +380,7 @@ ME_DisplayItem *ME_JoinParagraphs(ME_TextEditor *editor, ME_DisplayItem *tp,
|
||||||
|
|
||||||
if (!keepFirstParaFormat)
|
if (!keepFirstParaFormat)
|
||||||
{
|
{
|
||||||
ME_AddUndoItem(editor, diUndoSetParagraphFormat, tp);
|
add_undo_set_para_fmt( editor, &tp->member.para );
|
||||||
*tp->member.para.pFmt = *pNext->member.para.pFmt;
|
*tp->member.para.pFmt = *pNext->member.para.pFmt;
|
||||||
tp->member.para.border = pNext->member.para.border;
|
tp->member.para.border = pNext->member.para.border;
|
||||||
}
|
}
|
||||||
|
|
|
@ -357,17 +357,12 @@ ME_InsertRunAtCursor(ME_TextEditor *editor, ME_Cursor *cursor, ME_Style *style,
|
||||||
const WCHAR *str, int len, int flags)
|
const WCHAR *str, int len, int flags)
|
||||||
{
|
{
|
||||||
ME_DisplayItem *pDI;
|
ME_DisplayItem *pDI;
|
||||||
ME_UndoItem *pUI;
|
|
||||||
|
|
||||||
if (cursor->nOffset)
|
if (cursor->nOffset)
|
||||||
ME_SplitRunSimple(editor, cursor);
|
ME_SplitRunSimple(editor, cursor);
|
||||||
|
|
||||||
pUI = ME_AddUndoItem(editor, diUndoDeleteRun, NULL);
|
add_undo_delete_run( editor, cursor->pPara->member.para.nCharOfs +
|
||||||
if (pUI) {
|
cursor->pRun->member.run.nCharOfs, len );
|
||||||
pUI->nStart = cursor->pPara->member.para.nCharOfs
|
|
||||||
+ cursor->pRun->member.run.nCharOfs;
|
|
||||||
pUI->nLen = len;
|
|
||||||
}
|
|
||||||
|
|
||||||
pDI = ME_MakeRun(style, ME_MakeStringN(str, len), flags);
|
pDI = ME_MakeRun(style, ME_MakeStringN(str, len), flags);
|
||||||
pDI->member.run.nCharOfs = cursor->pRun->member.run.nCharOfs;
|
pDI->member.run.nCharOfs = cursor->pRun->member.run.nCharOfs;
|
||||||
|
@ -769,19 +764,12 @@ void ME_SetCharFormat(ME_TextEditor *editor, ME_Cursor *start, ME_Cursor *end, C
|
||||||
|
|
||||||
while(run != end_run)
|
while(run != end_run)
|
||||||
{
|
{
|
||||||
ME_UndoItem *undo = NULL;
|
|
||||||
ME_Style *new_style = ME_ApplyStyle(run->member.run.style, pFmt);
|
ME_Style *new_style = ME_ApplyStyle(run->member.run.style, pFmt);
|
||||||
/* ME_DumpStyle(new_style); */
|
/* ME_DumpStyle(new_style); */
|
||||||
undo = ME_AddUndoItem(editor, diUndoSetCharFormat, NULL);
|
|
||||||
if (undo) {
|
add_undo_set_char_fmt( editor, para->member.para.nCharOfs + run->member.run.nCharOfs,
|
||||||
undo->nStart = run->member.run.nCharOfs+para->member.para.nCharOfs;
|
run->member.run.strText->nLen, &run->member.run.style->fmt );
|
||||||
undo->nLen = run->member.run.strText->nLen;
|
ME_ReleaseStyle(run->member.run.style);
|
||||||
undo->di.member.ustyle = run->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(run->member.run.style);
|
|
||||||
run->member.run.style = new_style;
|
run->member.run.style = new_style;
|
||||||
run = ME_FindItemFwd(run, diRunOrParagraph);
|
run = ME_FindItemFwd(run, diRunOrParagraph);
|
||||||
if (run && run->type == diParagraph)
|
if (run && run->type == diParagraph)
|
||||||
|
|
|
@ -22,150 +22,194 @@
|
||||||
|
|
||||||
WINE_DEFAULT_DEBUG_CHANNEL(richedit);
|
WINE_DEFAULT_DEBUG_CHANNEL(richedit);
|
||||||
|
|
||||||
|
static void destroy_undo_item( struct undo_item *undo )
|
||||||
|
{
|
||||||
|
switch( undo->type )
|
||||||
|
{
|
||||||
|
case undo_insert_run:
|
||||||
|
heap_free( undo->u.insert_run.str );
|
||||||
|
ME_ReleaseStyle( undo->u.insert_run.style );
|
||||||
|
break;
|
||||||
|
case undo_split_para:
|
||||||
|
ME_DestroyString( undo->u.split_para.eol_str );
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
heap_free( undo );
|
||||||
|
}
|
||||||
|
|
||||||
|
static void empty_redo_stack(ME_TextEditor *editor)
|
||||||
|
{
|
||||||
|
struct undo_item *cursor, *cursor2;
|
||||||
|
LIST_FOR_EACH_ENTRY_SAFE( cursor, cursor2, &editor->redo_stack, struct undo_item, entry )
|
||||||
|
{
|
||||||
|
list_remove( &cursor->entry );
|
||||||
|
destroy_undo_item( cursor );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void ME_EmptyUndoStack(ME_TextEditor *editor)
|
void ME_EmptyUndoStack(ME_TextEditor *editor)
|
||||||
{
|
{
|
||||||
ME_DisplayItem *p, *pNext;
|
struct undo_item *cursor, *cursor2;
|
||||||
|
|
||||||
if (editor->nUndoMode == umIgnore)
|
if (editor->nUndoMode == umIgnore)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
TRACE("Emptying undo stack\n");
|
TRACE("Emptying undo stack\n");
|
||||||
|
|
||||||
p = editor->pUndoStack;
|
|
||||||
editor->pUndoStack = editor->pUndoStackBottom = NULL;
|
|
||||||
editor->nUndoStackSize = 0;
|
editor->nUndoStackSize = 0;
|
||||||
while(p) {
|
|
||||||
pNext = p->next;
|
LIST_FOR_EACH_ENTRY_SAFE( cursor, cursor2, &editor->undo_stack, struct undo_item, entry )
|
||||||
ME_DestroyDisplayItem(p);
|
{
|
||||||
p = pNext;
|
list_remove( &cursor->entry );
|
||||||
}
|
destroy_undo_item( cursor );
|
||||||
p = editor->pRedoStack;
|
}
|
||||||
editor->pRedoStack = NULL;
|
|
||||||
while(p) {
|
empty_redo_stack( editor );
|
||||||
pNext = p->next;
|
|
||||||
ME_DestroyDisplayItem(p);
|
|
||||||
p = pNext;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ME_UndoItem *ME_AddUndoItem(ME_TextEditor *editor, ME_DIType type, const ME_DisplayItem *pdi) {
|
static struct undo_item *add_undo( ME_TextEditor *editor, enum undo_type type )
|
||||||
if (editor->nUndoMode == umIgnore)
|
{
|
||||||
return NULL;
|
struct undo_item *undo, *item;
|
||||||
else if (editor->nUndoLimit == 0)
|
struct list *head;
|
||||||
return NULL;
|
|
||||||
else
|
if (editor->nUndoMode == umIgnore) return NULL;
|
||||||
{
|
if (editor->nUndoLimit == 0) return NULL;
|
||||||
ME_DisplayItem *pItem = ALLOC_OBJ(ME_UndoItem);
|
|
||||||
switch(type)
|
undo = heap_alloc( sizeof(*undo) );
|
||||||
{
|
if (!undo) return NULL;
|
||||||
case diUndoPotentialEndTransaction:
|
undo->type = type;
|
||||||
/* only should be added for manually typed chars, not undos or redos */
|
|
||||||
assert(editor->nUndoMode == umAddToUndo);
|
|
||||||
/* intentional fall-through to next case */
|
|
||||||
case diUndoEndTransaction:
|
|
||||||
break;
|
|
||||||
case diUndoSetParagraphFormat:
|
|
||||||
assert(pdi);
|
|
||||||
pItem->member.para = pdi->member.para;
|
|
||||||
pItem->member.para.pFmt = ALLOC_OBJ(PARAFORMAT2);
|
|
||||||
*pItem->member.para.pFmt = *pdi->member.para.pFmt;
|
|
||||||
break;
|
|
||||||
case diUndoInsertRun:
|
|
||||||
assert(pdi);
|
|
||||||
pItem->member.run = pdi->member.run;
|
|
||||||
pItem->member.run.strText = NULL;
|
|
||||||
ME_AddRefStyle(pItem->member.run.style);
|
|
||||||
if (pdi->member.run.ole_obj)
|
|
||||||
{
|
|
||||||
pItem->member.run.ole_obj = ALLOC_OBJ(*pItem->member.run.ole_obj);
|
|
||||||
ME_CopyReObject(pItem->member.run.ole_obj, pdi->member.run.ole_obj);
|
|
||||||
}
|
|
||||||
else pItem->member.run.ole_obj = NULL;
|
|
||||||
break;
|
|
||||||
case diUndoSetCharFormat:
|
|
||||||
break;
|
|
||||||
case diUndoDeleteRun:
|
|
||||||
case diUndoJoinParagraphs:
|
|
||||||
break;
|
|
||||||
case diUndoSplitParagraph:
|
|
||||||
{
|
|
||||||
ME_DisplayItem *prev_para = pdi->member.para.prev_para;
|
|
||||||
assert(pdi->member.para.pFmt->cbSize == sizeof(PARAFORMAT2));
|
|
||||||
pItem->member.para.pFmt = ALLOC_OBJ(PARAFORMAT2);
|
|
||||||
pItem->member.para.pFmt->cbSize = sizeof(PARAFORMAT2);
|
|
||||||
pItem->member.para.pFmt->dwMask = 0;
|
|
||||||
*pItem->member.para.pFmt = *pdi->member.para.pFmt;
|
|
||||||
pItem->member.para.border = pdi->member.para.border;
|
|
||||||
pItem->member.para.nFlags = prev_para->member.para.nFlags & ~MEPF_CELL;
|
|
||||||
pItem->member.para.pCell = NULL;
|
|
||||||
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 || editor->nUndoMode == umAddBackToUndo)
|
||||||
{
|
{
|
||||||
if (editor->pUndoStack
|
|
||||||
&& editor->pUndoStack->type == diUndoPotentialEndTransaction)
|
|
||||||
{
|
|
||||||
editor->pUndoStack->type = diUndoEndTransaction;
|
|
||||||
}
|
|
||||||
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;
|
head = list_head( &editor->undo_stack );
|
||||||
if (type == diUndoEndTransaction || type == diUndoPotentialEndTransaction)
|
if (head)
|
||||||
editor->nUndoStackSize++;
|
|
||||||
if (editor->pUndoStack)
|
|
||||||
editor->pUndoStack->prev = pItem;
|
|
||||||
else
|
|
||||||
editor->pUndoStackBottom = pItem;
|
|
||||||
editor->pUndoStack = pItem;
|
|
||||||
|
|
||||||
if (editor->nUndoStackSize > editor->nUndoLimit)
|
|
||||||
{ /* remove oldest undo from stack */
|
|
||||||
ME_DisplayItem *p = editor->pUndoStackBottom;
|
|
||||||
while (p->type !=diUndoEndTransaction)
|
|
||||||
p = p->prev; /*find new stack bottom */
|
|
||||||
editor->pUndoStackBottom = p->prev;
|
|
||||||
editor->pUndoStackBottom->next = NULL;
|
|
||||||
do
|
|
||||||
{
|
{
|
||||||
ME_DisplayItem *pp = p->next;
|
item = LIST_ENTRY( head, struct undo_item, entry );
|
||||||
ME_DestroyDisplayItem(p);
|
if (item->type == undo_potential_end_transaction)
|
||||||
p = pp;
|
item->type = undo_end_transaction;
|
||||||
} while (p);
|
|
||||||
editor->nUndoStackSize--;
|
|
||||||
}
|
|
||||||
/* 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;
|
|
||||||
}
|
if (editor->nUndoMode == umAddToUndo)
|
||||||
|
TRACE("Pushing id=%d to undo stack, deleting redo stack\n", type);
|
||||||
|
else
|
||||||
|
TRACE("Pushing id=%d to undo stack\n", type);
|
||||||
|
|
||||||
|
list_add_head( &editor->undo_stack, &undo->entry );
|
||||||
|
|
||||||
|
if (type == undo_end_transaction || type == undo_potential_end_transaction)
|
||||||
|
editor->nUndoStackSize++;
|
||||||
|
|
||||||
|
if (editor->nUndoStackSize > editor->nUndoLimit)
|
||||||
|
{
|
||||||
|
struct undo_item *cursor2;
|
||||||
|
/* remove oldest undo from stack */
|
||||||
|
LIST_FOR_EACH_ENTRY_SAFE_REV( item, cursor2, &editor->undo_stack, struct undo_item, entry )
|
||||||
|
{
|
||||||
|
BOOL done = (item->type == undo_end_transaction);
|
||||||
|
list_remove( &item->entry );
|
||||||
|
destroy_undo_item( item );
|
||||||
|
if (done) break;
|
||||||
|
}
|
||||||
|
editor->nUndoStackSize--;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* any new operation (not redo) clears the redo stack */
|
||||||
|
if (editor->nUndoMode == umAddToUndo) empty_redo_stack( editor );
|
||||||
}
|
}
|
||||||
else if (editor->nUndoMode == umAddToRedo)
|
else if (editor->nUndoMode == umAddToRedo)
|
||||||
{
|
{
|
||||||
TRACE("Pushing id=%s to redo stack\n", ME_GetDITypeName(type));
|
TRACE("Pushing id=%d to redo stack\n", type);
|
||||||
pItem->next = editor->pRedoStack;
|
list_add_head( &editor->redo_stack, &undo->entry );
|
||||||
if (editor->pRedoStack)
|
|
||||||
editor->pRedoStack->prev = pItem;
|
|
||||||
editor->pRedoStack = pItem;
|
|
||||||
}
|
}
|
||||||
else
|
|
||||||
assert(0);
|
return undo;
|
||||||
return (ME_UndoItem *)pItem;
|
}
|
||||||
}
|
|
||||||
|
BOOL add_undo_insert_run( ME_TextEditor *editor, int pos, const WCHAR *str, int len, int flags, ME_Style *style )
|
||||||
|
{
|
||||||
|
struct undo_item *undo = add_undo( editor, undo_insert_run );
|
||||||
|
if (!undo) return FALSE;
|
||||||
|
|
||||||
|
undo->u.insert_run.str = heap_alloc( (len + 1) * sizeof(WCHAR) );
|
||||||
|
if (!undo->u.insert_run.str)
|
||||||
|
{
|
||||||
|
ME_EmptyUndoStack( editor );
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
memcpy( undo->u.insert_run.str, str, len * sizeof(WCHAR) );
|
||||||
|
undo->u.insert_run.str[len] = 0;
|
||||||
|
undo->u.insert_run.pos = pos;
|
||||||
|
undo->u.insert_run.len = len;
|
||||||
|
undo->u.insert_run.flags = flags;
|
||||||
|
undo->u.insert_run.style = style;
|
||||||
|
ME_AddRefStyle( style );
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOL add_undo_set_para_fmt( ME_TextEditor *editor, const ME_Paragraph *para )
|
||||||
|
{
|
||||||
|
struct undo_item *undo = add_undo( editor, undo_set_para_fmt );
|
||||||
|
if (!undo) return FALSE;
|
||||||
|
|
||||||
|
undo->u.set_para_fmt.pos = para->nCharOfs;
|
||||||
|
undo->u.set_para_fmt.fmt = *para->pFmt;
|
||||||
|
undo->u.set_para_fmt.border = para->border;
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOL add_undo_set_char_fmt( ME_TextEditor *editor, int pos, int len, const CHARFORMAT2W *fmt )
|
||||||
|
{
|
||||||
|
struct undo_item *undo = add_undo( editor, undo_set_char_fmt );
|
||||||
|
if (!undo) return FALSE;
|
||||||
|
|
||||||
|
undo->u.set_char_fmt.pos = pos;
|
||||||
|
undo->u.set_char_fmt.len = len;
|
||||||
|
undo->u.set_char_fmt.fmt = *fmt;
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOL add_undo_join_paras( ME_TextEditor *editor, int pos )
|
||||||
|
{
|
||||||
|
struct undo_item *undo = add_undo( editor, undo_join_paras );
|
||||||
|
if (!undo) return FALSE;
|
||||||
|
|
||||||
|
undo->u.join_paras.pos = pos;
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOL add_undo_split_para( ME_TextEditor *editor, const ME_Paragraph *para, const ME_Run *run, const ME_Cell *cell )
|
||||||
|
{
|
||||||
|
struct undo_item *undo = add_undo( editor, undo_split_para );
|
||||||
|
if (!undo) return FALSE;
|
||||||
|
|
||||||
|
undo->u.split_para.pos = para->nCharOfs - run->strText->nLen;
|
||||||
|
undo->u.split_para.eol_str = ME_StrDup( run->strText );
|
||||||
|
undo->u.split_para.fmt = *para->pFmt;
|
||||||
|
undo->u.split_para.border = para->border;
|
||||||
|
undo->u.split_para.flags = para->prev_para->member.para.nFlags & ~MEPF_CELL;
|
||||||
|
|
||||||
|
if (cell)
|
||||||
|
{
|
||||||
|
undo->u.split_para.cell_border = cell->border;
|
||||||
|
undo->u.split_para.cell_right_boundary = cell->nRightBoundary;
|
||||||
|
}
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOL add_undo_delete_run( ME_TextEditor *editor, int pos, int len )
|
||||||
|
{
|
||||||
|
struct undo_item *undo = add_undo( editor, undo_delete_run );
|
||||||
|
if (!undo) return FALSE;
|
||||||
|
|
||||||
|
undo->u.delete_run.pos = pos;
|
||||||
|
undo->u.delete_run.len = len;
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -180,29 +224,31 @@ ME_UndoItem *ME_AddUndoItem(ME_TextEditor *editor, ME_DIType type, const ME_Disp
|
||||||
* This can also be used to conclude a coalescing transaction (used for grouping
|
* This can also be used to conclude a coalescing transaction (used for grouping
|
||||||
* typed characters).
|
* typed characters).
|
||||||
*/
|
*/
|
||||||
void ME_CommitUndo(ME_TextEditor *editor) {
|
void ME_CommitUndo(ME_TextEditor *editor)
|
||||||
|
{
|
||||||
|
struct undo_item *item;
|
||||||
|
struct list *head;
|
||||||
|
|
||||||
if (editor->nUndoMode == umIgnore)
|
if (editor->nUndoMode == umIgnore)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
assert(editor->nUndoMode == umAddToUndo);
|
assert(editor->nUndoMode == umAddToUndo);
|
||||||
|
|
||||||
/* no transactions, no need to commit */
|
/* no transactions, no need to commit */
|
||||||
if (!editor->pUndoStack)
|
head = list_head( &editor->undo_stack );
|
||||||
return;
|
if (!head) return;
|
||||||
|
|
||||||
/* no need to commit empty transactions */
|
/* no need to commit empty transactions */
|
||||||
if (editor->pUndoStack->type == diUndoEndTransaction)
|
item = LIST_ENTRY( head, struct undo_item, entry );
|
||||||
return;
|
if (item->type == undo_end_transaction) return;
|
||||||
|
|
||||||
if (editor->pUndoStack->type == diUndoPotentialEndTransaction)
|
if (item->type == undo_potential_end_transaction)
|
||||||
{
|
{
|
||||||
/* Previous transaction was as a result of characters typed,
|
item->type = undo_end_transaction;
|
||||||
* so the end of this transaction is confirmed. */
|
|
||||||
editor->pUndoStack->type = diUndoEndTransaction;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ME_AddUndoItem(editor, diUndoEndTransaction, NULL);
|
add_undo( editor, undo_end_transaction );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -218,21 +264,23 @@ void ME_CommitUndo(ME_TextEditor *editor) {
|
||||||
*/
|
*/
|
||||||
void ME_ContinueCoalescingTransaction(ME_TextEditor *editor)
|
void ME_ContinueCoalescingTransaction(ME_TextEditor *editor)
|
||||||
{
|
{
|
||||||
ME_DisplayItem* p;
|
struct undo_item *item;
|
||||||
|
struct list *head;
|
||||||
|
|
||||||
if (editor->nUndoMode == umIgnore)
|
if (editor->nUndoMode == umIgnore)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
assert(editor->nUndoMode == umAddToUndo);
|
assert(editor->nUndoMode == umAddToUndo);
|
||||||
|
|
||||||
p = editor->pUndoStack;
|
head = list_head( &editor->undo_stack );
|
||||||
|
if (!head) return;
|
||||||
|
|
||||||
if (p && p->type == diUndoPotentialEndTransaction) {
|
item = LIST_ENTRY( head, struct undo_item, entry );
|
||||||
assert(p->next); /* EndTransactions shouldn't be at bottom of undo stack */
|
if (item->type == undo_potential_end_transaction)
|
||||||
editor->pUndoStack = p->next;
|
{
|
||||||
editor->pUndoStack->prev = NULL;
|
list_remove( &item->entry );
|
||||||
editor->nUndoStackSize--;
|
editor->nUndoStackSize--;
|
||||||
ME_DestroyDisplayItem(p);
|
destroy_undo_item( item );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -252,92 +300,91 @@ void ME_ContinueCoalescingTransaction(ME_TextEditor *editor)
|
||||||
*/
|
*/
|
||||||
void ME_CommitCoalescingUndo(ME_TextEditor *editor)
|
void ME_CommitCoalescingUndo(ME_TextEditor *editor)
|
||||||
{
|
{
|
||||||
|
struct undo_item *item;
|
||||||
|
struct list *head;
|
||||||
|
|
||||||
if (editor->nUndoMode == umIgnore)
|
if (editor->nUndoMode == umIgnore)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
assert(editor->nUndoMode == umAddToUndo);
|
assert(editor->nUndoMode == umAddToUndo);
|
||||||
|
|
||||||
/* no transactions, no need to commit */
|
head = list_head( &editor->undo_stack );
|
||||||
if (!editor->pUndoStack)
|
if (!head) return;
|
||||||
return;
|
|
||||||
|
|
||||||
/* no need to commit empty transactions */
|
/* no need to commit empty transactions */
|
||||||
if (editor->pUndoStack->type == diUndoEndTransaction)
|
item = LIST_ENTRY( head, struct undo_item, entry );
|
||||||
return;
|
if (item->type == undo_end_transaction ||
|
||||||
if (editor->pUndoStack->type == diUndoPotentialEndTransaction)
|
item->type == undo_potential_end_transaction)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
ME_AddUndoItem(editor, diUndoPotentialEndTransaction, NULL);
|
add_undo( editor, undo_potential_end_transaction );
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ME_PlayUndoItem(ME_TextEditor *editor, ME_DisplayItem *pItem)
|
static void ME_PlayUndoItem(ME_TextEditor *editor, struct undo_item *undo)
|
||||||
{
|
{
|
||||||
ME_UndoItem *pUItem = (ME_UndoItem *)pItem;
|
|
||||||
|
|
||||||
if (editor->nUndoMode == umIgnore)
|
if (editor->nUndoMode == umIgnore)
|
||||||
return;
|
return;
|
||||||
TRACE("Playing undo/redo item, id=%s\n", ME_GetDITypeName(pItem->type));
|
TRACE("Playing undo/redo item, id=%d\n", undo->type);
|
||||||
|
|
||||||
switch(pItem->type)
|
switch(undo->type)
|
||||||
{
|
{
|
||||||
case diUndoPotentialEndTransaction:
|
case undo_potential_end_transaction:
|
||||||
case diUndoEndTransaction:
|
case undo_end_transaction:
|
||||||
assert(0);
|
assert(0);
|
||||||
case diUndoSetParagraphFormat:
|
case undo_set_para_fmt:
|
||||||
{
|
{
|
||||||
ME_Cursor tmp;
|
ME_Cursor tmp;
|
||||||
ME_DisplayItem *para;
|
ME_DisplayItem *para;
|
||||||
ME_CursorFromCharOfs(editor, pItem->member.para.nCharOfs, &tmp);
|
ME_CursorFromCharOfs(editor, undo->u.set_para_fmt.pos, &tmp);
|
||||||
para = ME_FindItemBack(tmp.pRun, diParagraph);
|
para = ME_FindItemBack(tmp.pRun, diParagraph);
|
||||||
ME_AddUndoItem(editor, diUndoSetParagraphFormat, para);
|
add_undo_set_para_fmt( editor, ¶->member.para );
|
||||||
*para->member.para.pFmt = *pItem->member.para.pFmt;
|
*para->member.para.pFmt = undo->u.set_para_fmt.fmt;
|
||||||
para->member.para.border = pItem->member.para.border;
|
para->member.para.border = undo->u.set_para_fmt.border;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case diUndoSetCharFormat:
|
case undo_set_char_fmt:
|
||||||
{
|
{
|
||||||
ME_Cursor start, end;
|
ME_Cursor start, end;
|
||||||
ME_CursorFromCharOfs(editor, pUItem->nStart, &start);
|
ME_CursorFromCharOfs(editor, undo->u.set_char_fmt.pos, &start);
|
||||||
end = start;
|
end = start;
|
||||||
ME_MoveCursorChars(editor, &end, pUItem->nLen);
|
ME_MoveCursorChars(editor, &end, undo->u.set_char_fmt.len);
|
||||||
ME_SetCharFormat(editor, &start, &end, &pItem->member.ustyle->fmt);
|
ME_SetCharFormat(editor, &start, &end, &undo->u.set_char_fmt.fmt);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case diUndoInsertRun:
|
case undo_insert_run:
|
||||||
{
|
{
|
||||||
ME_Cursor tmp;
|
ME_Cursor tmp;
|
||||||
ME_CursorFromCharOfs(editor, pItem->member.run.nCharOfs, &tmp);
|
ME_CursorFromCharOfs(editor, undo->u.insert_run.pos, &tmp);
|
||||||
ME_InsertRunAtCursor(editor, &tmp, pItem->member.run.style,
|
ME_InsertRunAtCursor(editor, &tmp, undo->u.insert_run.style,
|
||||||
pItem->member.run.strText->szData,
|
undo->u.insert_run.str,
|
||||||
pItem->member.run.strText->nLen,
|
undo->u.insert_run.len,
|
||||||
pItem->member.run.nFlags);
|
undo->u.insert_run.flags);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case diUndoDeleteRun:
|
case undo_delete_run:
|
||||||
{
|
{
|
||||||
ME_Cursor tmp;
|
ME_Cursor tmp;
|
||||||
ME_CursorFromCharOfs(editor, pUItem->nStart, &tmp);
|
ME_CursorFromCharOfs(editor, undo->u.delete_run.pos, &tmp);
|
||||||
ME_InternalDeleteText(editor, &tmp, pUItem->nLen, TRUE);
|
ME_InternalDeleteText(editor, &tmp, undo->u.delete_run.len, TRUE);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case diUndoJoinParagraphs:
|
case undo_join_paras:
|
||||||
{
|
{
|
||||||
ME_Cursor tmp;
|
ME_Cursor tmp;
|
||||||
ME_CursorFromCharOfs(editor, pUItem->nStart, &tmp);
|
ME_CursorFromCharOfs(editor, undo->u.join_paras.pos, &tmp);
|
||||||
/* the only thing that's needed is paragraph offset, so no need to split runs */
|
|
||||||
ME_JoinParagraphs(editor, tmp.pPara, TRUE);
|
ME_JoinParagraphs(editor, tmp.pPara, TRUE);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case diUndoSplitParagraph:
|
case undo_split_para:
|
||||||
{
|
{
|
||||||
ME_Cursor tmp;
|
ME_Cursor tmp;
|
||||||
ME_DisplayItem *this_para, *new_para;
|
ME_DisplayItem *this_para, *new_para;
|
||||||
BOOL bFixRowStart;
|
BOOL bFixRowStart;
|
||||||
int paraFlags = pItem->member.para.nFlags & (MEPF_ROWSTART|MEPF_CELL|MEPF_ROWEND);
|
int paraFlags = undo->u.split_para.flags & (MEPF_ROWSTART|MEPF_CELL|MEPF_ROWEND);
|
||||||
ME_CursorFromCharOfs(editor, pUItem->nStart, &tmp);
|
ME_CursorFromCharOfs(editor, undo->u.split_para.pos, &tmp);
|
||||||
if (tmp.nOffset)
|
if (tmp.nOffset)
|
||||||
ME_SplitRunSimple(editor, &tmp);
|
ME_SplitRunSimple(editor, &tmp);
|
||||||
assert(pUItem->eol_str);
|
|
||||||
this_para = tmp.pPara;
|
this_para = tmp.pPara;
|
||||||
bFixRowStart = this_para->member.para.nFlags & MEPF_ROWSTART;
|
bFixRowStart = this_para->member.para.nFlags & MEPF_ROWSTART;
|
||||||
if (bFixRowStart)
|
if (bFixRowStart)
|
||||||
|
@ -347,58 +394,56 @@ static void ME_PlayUndoItem(ME_TextEditor *editor, ME_DisplayItem *pItem)
|
||||||
this_para->member.para.nFlags &= ~MEPF_ROWSTART;
|
this_para->member.para.nFlags &= ~MEPF_ROWSTART;
|
||||||
}
|
}
|
||||||
new_para = ME_SplitParagraph(editor, tmp.pRun, tmp.pRun->member.run.style,
|
new_para = ME_SplitParagraph(editor, tmp.pRun, tmp.pRun->member.run.style,
|
||||||
pUItem->eol_str, paraFlags);
|
undo->u.split_para.eol_str, paraFlags);
|
||||||
|
/* ME_SplitParagraph owns eol_str */
|
||||||
|
undo->u.split_para.eol_str = NULL;
|
||||||
if (bFixRowStart)
|
if (bFixRowStart)
|
||||||
new_para->member.para.nFlags |= MEPF_ROWSTART;
|
new_para->member.para.nFlags |= MEPF_ROWSTART;
|
||||||
assert(pItem->member.para.pFmt->cbSize == sizeof(PARAFORMAT2));
|
*new_para->member.para.pFmt = undo->u.split_para.fmt;
|
||||||
*new_para->member.para.pFmt = *pItem->member.para.pFmt;
|
new_para->member.para.border = undo->u.split_para.border;
|
||||||
new_para->member.para.border = pItem->member.para.border;
|
if (paraFlags)
|
||||||
if (pItem->member.para.pCell)
|
|
||||||
{
|
{
|
||||||
ME_DisplayItem *pItemCell, *pCell;
|
ME_DisplayItem *pCell = new_para->member.para.pCell;
|
||||||
pItemCell = pItem->member.para.pCell;
|
pCell->member.cell.nRightBoundary = undo->u.split_para.cell_right_boundary;
|
||||||
pCell = new_para->member.para.pCell;
|
pCell->member.cell.border = undo->u.split_para.cell_border;
|
||||||
pCell->member.cell.nRightBoundary = pItemCell->member.cell.nRightBoundary;
|
|
||||||
pCell->member.cell.border = pItemCell->member.cell.border;
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
|
||||||
assert(0 == "PlayUndoItem, unexpected type");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOL ME_Undo(ME_TextEditor *editor) {
|
BOOL ME_Undo(ME_TextEditor *editor)
|
||||||
ME_DisplayItem *p;
|
{
|
||||||
ME_UndoMode nMode = editor->nUndoMode;
|
ME_UndoMode nMode = editor->nUndoMode;
|
||||||
|
struct list *head;
|
||||||
if (editor->nUndoMode == umIgnore)
|
struct undo_item *undo, *cursor2;
|
||||||
return FALSE;
|
|
||||||
|
if (editor->nUndoMode == umIgnore) return FALSE;
|
||||||
assert(nMode == umAddToUndo || nMode == umIgnore);
|
assert(nMode == umAddToUndo || nMode == umIgnore);
|
||||||
|
|
||||||
/* no undo items ? */
|
head = list_head( &editor->undo_stack );
|
||||||
if (!editor->pUndoStack)
|
if (!head) return FALSE;
|
||||||
return FALSE;
|
|
||||||
|
|
||||||
/* watch out for uncommitted transactions ! */
|
/* watch out for uncommitted transactions ! */
|
||||||
assert(editor->pUndoStack->type == diUndoEndTransaction
|
undo = LIST_ENTRY( head, struct undo_item, entry );
|
||||||
|| editor->pUndoStack->type == diUndoPotentialEndTransaction);
|
assert(undo->type == undo_end_transaction
|
||||||
|
|| undo->type == undo_potential_end_transaction);
|
||||||
|
|
||||||
editor->nUndoMode = umAddToRedo;
|
editor->nUndoMode = umAddToRedo;
|
||||||
p = editor->pUndoStack->next;
|
|
||||||
ME_DestroyDisplayItem(editor->pUndoStack);
|
list_remove( &undo->entry );
|
||||||
editor->pUndoStack = p;
|
destroy_undo_item( undo );
|
||||||
do {
|
|
||||||
p->prev = NULL;
|
LIST_FOR_EACH_ENTRY_SAFE( undo, cursor2, &editor->undo_stack, struct undo_item, entry )
|
||||||
ME_PlayUndoItem(editor, p);
|
{
|
||||||
editor->pUndoStack = p->next;
|
if (undo->type == undo_end_transaction) break;
|
||||||
ME_DestroyDisplayItem(p);
|
ME_PlayUndoItem( editor, undo );
|
||||||
p = editor->pUndoStack;
|
list_remove( &undo->entry );
|
||||||
} while(p && p->type != diUndoEndTransaction);
|
destroy_undo_item( undo );
|
||||||
if (p)
|
}
|
||||||
p->prev = NULL;
|
|
||||||
ME_MoveCursorFromTableRowStartParagraph(editor);
|
ME_MoveCursorFromTableRowStartParagraph(editor);
|
||||||
ME_AddUndoItem(editor, diUndoEndTransaction, NULL);
|
add_undo( editor, undo_end_transaction );
|
||||||
ME_CheckTablesForCorruption(editor);
|
ME_CheckTablesForCorruption(editor);
|
||||||
editor->nUndoStackSize--;
|
editor->nUndoStackSize--;
|
||||||
editor->nUndoMode = nMode;
|
editor->nUndoMode = nMode;
|
||||||
|
@ -406,36 +451,36 @@ BOOL ME_Undo(ME_TextEditor *editor) {
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOL ME_Redo(ME_TextEditor *editor) {
|
BOOL ME_Redo(ME_TextEditor *editor)
|
||||||
ME_DisplayItem *p;
|
{
|
||||||
ME_UndoMode nMode = editor->nUndoMode;
|
ME_UndoMode nMode = editor->nUndoMode;
|
||||||
|
struct list *head;
|
||||||
|
struct undo_item *undo, *cursor2;
|
||||||
|
|
||||||
assert(nMode == umAddToUndo || nMode == umIgnore);
|
assert(nMode == umAddToUndo || nMode == umIgnore);
|
||||||
|
|
||||||
if (editor->nUndoMode == umIgnore)
|
if (editor->nUndoMode == umIgnore) return FALSE;
|
||||||
return FALSE;
|
|
||||||
/* no redo items ? */
|
head = list_head( &editor->redo_stack );
|
||||||
if (!editor->pRedoStack)
|
if (!head) return FALSE;
|
||||||
return FALSE;
|
|
||||||
|
|
||||||
/* watch out for uncommitted transactions ! */
|
/* watch out for uncommitted transactions ! */
|
||||||
assert(editor->pRedoStack->type == diUndoEndTransaction);
|
undo = LIST_ENTRY( head, struct undo_item, entry );
|
||||||
|
assert( undo->type == undo_end_transaction );
|
||||||
|
|
||||||
editor->nUndoMode = umAddBackToUndo;
|
editor->nUndoMode = umAddBackToUndo;
|
||||||
p = editor->pRedoStack->next;
|
list_remove( &undo->entry );
|
||||||
ME_DestroyDisplayItem(editor->pRedoStack);
|
destroy_undo_item( undo );
|
||||||
editor->pRedoStack = p;
|
|
||||||
do {
|
LIST_FOR_EACH_ENTRY_SAFE( undo, cursor2, &editor->redo_stack, struct undo_item, entry )
|
||||||
p->prev = NULL;
|
{
|
||||||
ME_PlayUndoItem(editor, p);
|
if (undo->type == undo_end_transaction) break;
|
||||||
editor->pRedoStack = p->next;
|
ME_PlayUndoItem( editor, undo );
|
||||||
ME_DestroyDisplayItem(p);
|
list_remove( &undo->entry );
|
||||||
p = editor->pRedoStack;
|
destroy_undo_item( undo );
|
||||||
} while(p && p->type != diUndoEndTransaction);
|
}
|
||||||
if (p)
|
|
||||||
p->prev = NULL;
|
|
||||||
ME_MoveCursorFromTableRowStartParagraph(editor);
|
ME_MoveCursorFromTableRowStartParagraph(editor);
|
||||||
ME_AddUndoItem(editor, diUndoEndTransaction, NULL);
|
add_undo( editor, undo_end_transaction );
|
||||||
ME_CheckTablesForCorruption(editor);
|
ME_CheckTablesForCorruption(editor);
|
||||||
editor->nUndoMode = nMode;
|
editor->nUndoMode = nMode;
|
||||||
ME_UpdateRepaint(editor, FALSE);
|
ME_UpdateRepaint(editor, FALSE);
|
||||||
|
|
Loading…
Reference in New Issue