gecko-dev/lib/layout/edtcmd.cpp

1534 lines
39 KiB
C++

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
* Reserved.
*/
//
//
// Editor save stuff. LWT 06/01/95
// this should be on the branch
//
#ifdef EDITOR
#include "editor.h"
#ifdef DEBUG
const char* kCommandNames[kCommandIDMax+1] = {
"Null",
"Typing",
"AddText",
"DeleteText",
"Cut",
"PasteText",
"PasteHTML",
"PasteHREF",
"ChangeAttributes",
"Indent",
"ParagraphAlign",
"MorphContainer",
"InsertHorizRule",
"SetHorizRuleData",
"InsertImage",
"SetImageData",
"InsertBreak",
"ChangePageData",
"SetMetaData",
"DeleteMetaData",
"InsertTarget",
"SetTargetData",
"InsertUnknownTag",
"SetUnknownTagData",
"GroupOfChanges",
"SetListData",
"InsertTable",
"DeleteTable",
"SetTableData",
"InsertTableCaption",
"SetTableCaptionData",
"DeleteTableCaption",
"InsertTableRow",
"SetTableRowData",
"DeleteTableRow",
"InsertTableColumn",
"SetTableCellData",
"DeleteTableColumn",
"InsertTableCell",
"DeleteTableCell",
"InsertMultiColumn",
"DeleteMultiColumn",
"SetMultiColumnData",
"SetSelection"
};
#endif
CEditText::CEditText() { m_pChars = NULL; m_iLength = 0; m_iCount = 0; }
CEditText::~CEditText() { Clear(); }
void CEditText::Clear() {
if ( m_pChars )
XP_HUGE_FREE(m_pChars); // Tied to implementation of CStreamOutMemory
m_pChars = 0;
m_iCount = 0;
m_iLength = 0;
}
char* CEditText::GetChars() { return m_pChars; }
int32 CEditText::Length() { return m_iLength; }
char** CEditText::GetPChars() { return &m_pChars; }
int32* CEditText::GetPLength() { return &m_iLength; }
// CEditCommand
CEditCommand::CEditCommand(CEditBuffer* editBuffer, intn id)
{
m_id = id;
m_editBuffer = editBuffer;
}
CEditCommand::~CEditCommand()
{
}
void CEditCommand::Do()
{
}
void CEditCommand::Undo()
{
}
void CEditCommand::Redo()
{
Do();
}
intn CEditCommand::GetID()
{
return m_id;
}
#ifdef DEBUG
void CEditCommand::Print(IStreamOut& stream) {
const char* name = "Unknown";
if ( m_id >= 0 && m_id <= kCommandIDMax ){
name = kCommandNames[m_id];
}
stream.Printf("%s(%ld) ", name, (long)m_id);
}
#endif
// CEditCommandLog
// We were having problems where we were entering a command while processing the undo of another command.
// This would cause the command log to be trimmed, which would delete the undo log, including the
// command that was being undone. This helps detect that situation.
#ifdef DEBUG
class CEditCommandLogRecursionCheckEntry {
private:
XP_Bool m_bOldBusy;
CEditCommandLog* m_log;
public:
CEditCommandLogRecursionCheckEntry(CEditCommandLog* log) {
m_log = log;
m_bOldBusy = m_log->m_bBusy;
XP_ASSERT(m_log->m_bBusy == FALSE);
m_log->m_bBusy = TRUE;
}
~CEditCommandLogRecursionCheckEntry() {
m_log->m_bBusy = m_bOldBusy;
}
};
#define DEBUG_RECURSION_CHECK CEditCommandLogRecursionCheckEntry debugRecursionCheckEntry(this);
#else
#define DEBUG_RECURSION_CHECK
#endif
CGlobalHistoryGroup* CGlobalHistoryGroup::g_pGlobalHistoryGroup;
CGlobalHistoryGroup* CGlobalHistoryGroup::GetGlobalHistoryGroup(){
if ( g_pGlobalHistoryGroup == NULL ) {
g_pGlobalHistoryGroup = new CGlobalHistoryGroup();
}
return g_pGlobalHistoryGroup;
}
CGlobalHistoryGroup::CGlobalHistoryGroup(){
m_pHead = NULL;
}
CGlobalHistoryGroup::~CGlobalHistoryGroup(){
CEditCommandLog* pLog = m_pHead;
while(pLog){
CEditCommandLog* pCurrent = pLog;
pLog = pLog->m_pNext;
delete pCurrent;
}
m_pHead = NULL;
}
XP_Bool CGlobalHistoryGroup::IsReload(CEditBuffer* pEditBuffer){
CEditCommandLog* pLog = m_pHead;
while(pLog){
if ( pEditBuffer->m_pContext == pLog->m_pContext ) {
return TRUE;
}
pLog = pLog->m_pNext;
}
return FALSE;
}
CEditCommandLog* CGlobalHistoryGroup::CreateLog(CEditBuffer* pEditBuffer){
CEditCommandLog* pLog = m_pHead;
while(pLog){
if ( pEditBuffer->m_pContext == pLog->m_pContext ) {
pLog->m_bIgnoreDelete = FALSE;
pLog->m_pBuffer = pEditBuffer;
return pLog;
}
pLog = pLog->m_pNext;
}
pLog = new CEditCommandLog();
pLog->m_pContext = pEditBuffer->m_pContext;
pLog->m_pNext = m_pHead;
pLog->m_pBuffer = pEditBuffer;
m_pHead = pLog;
return pLog;
}
CEditCommandLog* CGlobalHistoryGroup::GetLog(CEditBuffer* pEditBuffer){
CEditCommandLog* pLog = m_pHead;
while(pLog){
if ( pEditBuffer->m_pContext == pLog->m_pContext ) {
return pLog;
}
pLog = pLog->m_pNext;
}
XP_ASSERT(FALSE);
return NULL;
}
void CGlobalHistoryGroup::DeleteLog(CEditBuffer* pEditBuffer){
CEditCommandLog* pLog = m_pHead;
CEditCommandLog* pPrev = NULL;
while(pLog){
if ( pEditBuffer->m_pContext == pLog->m_pContext ) {
pLog->m_pBuffer = NULL;
if ( pLog->m_bIgnoreDelete ) {
pLog->m_bIgnoreDelete = FALSE;
}
else {
CEditCommandLog* pNext = pLog->m_pNext;
delete pLog;
if ( pPrev ) {
pPrev->m_pNext = pNext;
}
else {
m_pHead = pNext;
if ( pNext == NULL ) {
// Why yes, that is our this pointer....
delete g_pGlobalHistoryGroup;
g_pGlobalHistoryGroup = NULL;
}
}
}
return;
}
pPrev = pLog;
pLog = pLog->m_pNext;
}
XP_ASSERT(FALSE);
}
void CGlobalHistoryGroup::IgnoreNextDeleteOf(CEditBuffer* pEditBuffer){
CEditCommandLog* pLog = GetLog(pEditBuffer);
if ( pLog ) {
XP_ASSERT(pLog->m_bIgnoreDelete == FALSE);
pLog->m_bIgnoreDelete = TRUE;
}
}
CCommandState::CCommandState(){
m_commandID = kNullCommandID;
m_pState = NULL;
}
CCommandState::~CCommandState(){
Flush();
}
void CCommandState::SetID(intn commandID){
m_commandID = commandID;
}
intn CCommandState::GetID(){
return m_commandID;
}
void CCommandState::Record(CEditBuffer* pBufferToRecord){
Flush();
m_pState = pBufferToRecord->RecordState();
}
void CCommandState::Restore(CEditBuffer* pBufferToRestore){
if (m_pState) {
pBufferToRestore->RestoreState(m_pState);
}
}
void CCommandState::Flush(){
delete m_pState;
m_pState = NULL;
}
#ifdef DEBUG
void CCommandState::Print(IStreamOut& stream) {
if (m_pState) {
stream.Printf("id: %d, ", m_commandID);
m_pState->Print(stream);
}
}
#endif
CEditCommandLog::CEditCommandLog()
{
m_pBuffer = NULL;
m_pUndo = NULL;
m_pRedo = NULL;
m_iBatchLevel = 0;
m_pNext = NULL;
m_pContext = NULL;
m_bIgnoreDelete = FALSE;
#ifdef DEBUG
m_bBusy = FALSE;
#endif
m_state = 0;
m_version = 0;
m_storedVersion = 0;
m_highestVersion = 0;
#ifdef EDITOR_JAVA
m_pPlugins = 0;
#endif
m_pDocTempDir = NULL;
m_iDocTempFilenameNonce = 1;
// If this is the first CEditBuffer created, delete any
// temporary document directories from the last time
// Communicator 4.0 was run.
#if defined(XP_WIN) || defined(XP_MAC) || defined(XP_OS2)
// Don't do this for UNIX since multiple instances of Communicator
// may be running.
// First instance of CEditCommandLog.
if (m_iDocTempDirNonce == 0) {
m_iDocTempDirNonce++; // First temp dir will start with "1".
char *pxpURL = GetAppTempDir();
if (pxpURL) {
// recursive delete.
edt_RemoveDirectoryR(pxpURL);
XP_FREE(pxpURL);
}
else {
XP_ASSERT(0);
}
}
#endif
}
CEditCommandLog::~CEditCommandLog()
{
Trim();
#ifdef EDITOR_JAVA
EditorPluginManager_delete(m_pPlugins);
m_pPlugins = 0;
#endif
// Clean up the temp files associated with this doc.
if (m_pDocTempDir) {
edt_RemoveDirectoryR(m_pDocTempDir);
XP_FREE(m_pDocTempDir);
}
}
void CEditCommandLog::StartTyping(intn typing){
InternalDo(typing);
}
void CEditCommandLog::EndTyping(){
}
void CEditCommandLog::AdoptAndDo(CEditCommand* pCommand)
{
DEBUG_RECURSION_CHECK
if ( m_iBatchLevel == 0 ){
InternalAdoptAndDo(pCommand);
}
else
{
pCommand->Do();
delete pCommand;
}
}
void CEditCommandLog::InternalAdoptAndDo(CEditCommand* command)
{
InternalDo(command->GetID());
command->Do();
delete command;
}
void CEditCommandLog::InternalDo(intn id)
{
if ( m_state == 1 ) {
// recovering from an undo/redo
return;
}
delete m_pUndo;
m_pUndo = new CCommandState();
m_pUndo->SetID(id);
m_pUndo->Record(m_pBuffer);
if ( m_pRedo ) {
delete m_pRedo;
m_pRedo = NULL;
}
m_state = 0;
// Set m_version to a version that has never been seen before.
m_version = ++m_highestVersion;
}
void CEditCommandLog::Undo()
{
DEBUG_RECURSION_CHECK
if ( !m_pUndo ) {
XP_TRACE(("Nothing to undo."));
return;
}
if ( m_pUndo )
{
FinishBatchCommands();
if ( ! m_pRedo ) {
m_pRedo = new CCommandState();
m_pRedo->SetID(m_pUndo->GetID());
m_pRedo->Record(m_pBuffer);
}
m_pUndo->Restore(m_pBuffer);
delete m_pUndo;
m_pUndo = NULL;
}
m_state = 1;
// CCommandState::Restore() will set m_version.
}
void CEditCommandLog::Redo()
{
DEBUG_RECURSION_CHECK
if ( !m_pRedo ) {
XP_TRACE(("Nothing to redo."));
return;
}
if ( m_pRedo )
{
FinishBatchCommands();
if ( ! m_pUndo ) {
m_pUndo = new CCommandState();
m_pUndo->SetID(m_pRedo->GetID());
m_pUndo->Record(m_pBuffer);
}
m_pRedo->Restore(m_pBuffer);
delete m_pRedo;
m_pRedo = NULL;
}
m_state = 1;
// CCommandState::Restore() will set m_version.
}
void CEditCommandLog::FinishBatchCommands()
{
if ( m_iBatchLevel > 0 ){
XP_ASSERT(FALSE);
m_iBatchLevel = 0;
}
}
void CEditCommandLog::Trim()
{
delete m_pRedo;
m_pRedo = NULL;
delete m_pUndo;
m_pUndo = NULL;
}
XP_Bool CEditCommandLog::InReload(){
return m_state != 0;
}
void CEditCommandLog::SetInReload(XP_Bool bInReload){
m_state = bInReload;
}
XP_Bool CEditCommandLog::IsDirty(){
return m_version != m_storedVersion;
}
DocumentVersion CEditCommandLog::GetVersion(){
return m_version;
}
void CEditCommandLog::SetVersion(DocumentVersion version) {
m_version = version;
}
void CEditCommandLog::DocumentStored(){
m_storedVersion = m_version;
}
DocumentVersion CEditCommandLog::GetStoredVersion(){
return m_storedVersion;
}
intn CEditCommandLog::GetCommandHistoryLimit()
{
return 1;
}
void CEditCommandLog::SetCommandHistoryLimit(intn newLimit) {
if ( newLimit >= 0 ) {
Trim();
}
}
intn CEditCommandLog::GetNumberOfCommandsToUndo()
{
return m_pUndo ? 1 : 0;
}
intn CEditCommandLog::GetNumberOfCommandsToRedo()
{
return m_pRedo ? 1 : 0;
}
// Returns NULL if out of range
intn CEditCommandLog::GetUndoCommand(intn index)
{
if ( m_pUndo == NULL || index < 0 || index >= GetNumberOfCommandsToUndo())
return kNullCommandID;
return m_pUndo->GetID();
}
intn CEditCommandLog::GetRedoCommand(intn index)
{
if ( m_pRedo == NULL || index < 0 || index >= GetNumberOfCommandsToRedo() )
return kNullCommandID;
return m_pRedo->GetID();
}
#ifdef DEBUG
void CEditCommandLog::Print(IStreamOut& stream) {
stream.Printf("state: %d\n", m_state);
stream.Printf("Undo list: %d commands\n", GetNumberOfCommandsToUndo());
if ( m_pUndo ) {
m_pUndo->Print(stream);
}
stream.Printf("Redo list: %d commands\n", GetNumberOfCommandsToRedo());
if ( m_pRedo ) {
m_pRedo->Print(stream);
}
}
#endif
void CEditCommandLog::BeginBatchChanges(intn id) {
if ( m_iBatchLevel < 0 ) {
XP_ASSERT(FALSE);
m_iBatchLevel = 0;
}
if ( m_iBatchLevel++ == 0 ) {
InternalDo(id);
}
}
void CEditCommandLog::EndBatchChanges() {
if(m_iBatchLevel <= 0) {
XP_ASSERT(FALSE);
m_iBatchLevel = 0;
}
else {
m_iBatchLevel--;
}
}
#ifdef EDITOR_JAVA
EditorPluginManager CEditCommandLog::GetPlugins(){
if ( m_pPlugins == NULL ) {
m_pPlugins = EditorPluginManager_new(m_pContext);
}
return m_pPlugins;
}
#endif
// Returns xpURL, ends in slash.
char *CEditCommandLog::GetAppTempDir() {
char *pTempRoot = XP_TempDirName();
char* pTempURL = XP_PlatformFileToURL(pTempRoot);
XP_FREEIF(pTempRoot);
if (!(pTempURL && *pTempURL)) {
XP_ASSERT(0);
return NULL;
}
// Make sure ends in slash.
if (pTempURL[XP_STRLEN(pTempURL)-1] != '/') {
StrAllocCat(pTempURL,"/");
}
// Will still end in a slash.
#ifdef XP_UNIX
// NOTE: on UNIX we need to provide support for multiple users...
//
char *pUserName = getenv("USER");
if (pUserName != NULL) {
StrAllocCat(pTempURL,"nscomm40-");
StrAllocCat(pTempURL,pUserName);
StrAllocCat(pTempURL,"/");
}
else {
StrAllocCat(pTempURL,"nscomm40/");
}
#else
StrAllocCat(pTempURL,"nscomm40/");
#endif
// pTempURL is now the application temp dir root.
// EXTREME DANGER! CANNOT USE THIS IF "#" IS IN THE TEMP DIRECTORY NAME!
// Bug 83166 -- entire C drive deleted if TEMP=C:\#TEMP
//char *pTemp_xpURL = NET_ParseURL(pTempURL,GET_PATH_PART);
// This is (mostly) lifted from NET_ParseURL - just skip over host part
char *pTemp_xpURL = NULL;
char * colon = XP_STRCHR(pTempURL, ':'); /* returns a const char */
if(colon)
{
char * slash;
if(*(colon+1) == '/' && *(colon+2) == '/')
{
/* skip host part */
slash = XP_STRCHR(colon+3, '/');
}
else
{
/* path is right after the colon
*/
slash = colon+1;
}
// Copy everything starting with "/" to result string
pTemp_xpURL = XP_STRDUP(slash);
XP_FREEIF(pTempURL);
} else {
// Should never get here, but if no colon,
// just return what we started with
pTemp_xpURL = pTempURL;
}
return pTemp_xpURL;
}
char *CEditCommandLog::GetDocTempDir() {
// Already created.
if (m_pDocTempDir) {
return XP_STRDUP(m_pDocTempDir);
}
char *pTemp_xpURL = GetAppTempDir();
if (!pTemp_xpURL) {
XP_ASSERT(0);
return NULL;
}
// Create application temporary directory if necessary
XP_Dir pDir = edt_OpenDir(pTemp_xpURL,xpURL);
if (pDir) {
// Already exists, so ok.
XP_CloseDir(pDir);
}
else {
edt_MakeDirectory(pTemp_xpURL,xpURL);
pDir = edt_OpenDir(pTemp_xpURL,xpURL);
if (pDir) {
// made successfully, ok
XP_CloseDir(pDir);
}
else {
// can't make it, so fail.
XP_FREEIF(pTemp_xpURL);
return NULL;
}
}
// Under application temporary directory, create process-specific
// temporary directory. This is only really process-specific for
// UNIX. WIN and MAC has dummy directory name.
char *pProcessDir = NULL;
#ifdef XP_UNIX
pProcessDir = PR_smprintf("%u/",(unsigned)getpid());
#else
pProcessDir = XP_STRDUP("tmp/");
#endif
StrAllocCat(pTemp_xpURL,pProcessDir);
if (!pTemp_xpURL || !pProcessDir) {
XP_ASSERT(0);
XP_FREEIF(pProcessDir);
XP_FREEIF(pTemp_xpURL);
return NULL;
}
// pTemp_xpURL now has process-specific name appended.
pDir = edt_OpenDir(pTemp_xpURL,xpURL);
if (pDir) {
// Already exists, so ok.
XP_CloseDir(pDir);
}
else {
edt_MakeDirectory(pTemp_xpURL,xpURL);
pDir = edt_OpenDir(pTemp_xpURL,xpURL);
if (pDir) {
// made successfully, ok
XP_CloseDir(pDir);
}
else {
// can't make it, so fail.
XP_FREEIF(pTemp_xpURL);
return NULL;
}
}
// Create document-specific temp dir.
char *pFilename = PR_smprintf("tmp%ld",m_iDocTempDirNonce);
if (!pFilename) {
XP_ASSERT(0);
XP_FREE(pTemp_xpURL);
return NULL;
}
if (XP_STRLEN(pFilename) > 8) {
// truncate at 8 chars.
pFilename[8] = '\0';
}
StrAllocCat(pTemp_xpURL,pFilename);
XP_FREE(pFilename);
// end in slash.
StrAllocCat(pTemp_xpURL,"/");
pDir = edt_OpenDir(pTemp_xpURL,xpURL);
if (pDir) {
// Already exists. Strange, but not really an error.
XP_CloseDir(pDir);
}
else {
edt_MakeDirectory(pTemp_xpURL,xpURL);
pDir = edt_OpenDir(pTemp_xpURL,xpURL);
if (pDir) {
// made successfully, ok
XP_CloseDir(pDir);
}
else {
// failure, clear pTemp_xpURL
XP_FREEIF(pTemp_xpURL);
}
}
if (pTemp_xpURL) {
// success.
m_iDocTempDirNonce++;
m_pDocTempDir = pTemp_xpURL;
return XP_STRDUP(m_pDocTempDir);
}
else {
return NULL;
}
}
int32 CEditCommandLog::m_iDocTempDirNonce = 0;
// Always return a new filename in the temporary directory.
char *CEditCommandLog::CreateDocTempFilename(char *pPrefix,char *pExtension) {
// Add directory.
char *pTempF = GetDocTempDir();
if (!pTempF) {
return NULL;
}
// Add file prefix.
if (pPrefix) {
char *pPreCopy = XP_STRDUP(pPrefix);
if (!pPreCopy) {
XP_ASSERT(0);
return NULL;
}
// truncate to 3 chars.
if (XP_STRLEN(pPreCopy) > 3) {
pPreCopy[3] = '\0';
}
StrAllocCat(pTempF,pPreCopy);
XP_FREE(pPreCopy);
}
else {
// default to "edt" for prefix.
StrAllocCat(pTempF,"edt");
}
if (!pTempF) {
return NULL;
}
char *pTempFilename = PR_smprintf("%s%d.%s",
pTempF,
(int)m_iDocTempFilenameNonce,
(pExtension ? pExtension : "tmp"));
XP_FREE(pTempF);
m_iDocTempFilenameNonce++;
return pTempFilename;
}
// CEditDataSaver
CEditDataSaver::CEditDataSaver(CEditBuffer* pBuffer){
m_pEditBuffer = pBuffer;
m_bModifiedTextHasBeenSaved = FALSE;
#ifdef DEBUG
m_bDoState = 0;
#endif
}
CEditDataSaver::~CEditDataSaver(){
}
void CEditDataSaver::DoBegin(CPersistentEditSelection& original){
#ifdef DEBUG
XP_ASSERT(m_bDoState == 0);
m_bDoState++;
#endif
m_original = original;
m_pEditBuffer->GetSelection(m_originalDocument);
CEditSelection selection =
m_pEditBuffer->PersistentToEphemeral(m_original);
selection.ExpandToNotCrossStructures();
m_expandedOriginal = m_pEditBuffer->EphemeralToPersistent(selection);
m_pEditBuffer->CopyEditText(m_expandedOriginal, m_originalText);
}
void CEditDataSaver::DoEnd(CPersistentEditSelection& modified){
#ifdef DEBUG
XP_ASSERT(m_bDoState == 1);
m_bDoState++;
#endif
m_pEditBuffer->GetSelection(m_modifiedDocument);
m_expandedModified = m_expandedOriginal;
m_expandedModified.Map(m_original, modified);
}
void CEditDataSaver::Undo(){
#ifdef DEBUG
XP_ASSERT(m_bDoState == 2);
m_bDoState++;
#endif
m_pEditBuffer->SetSelection(m_expandedModified);
if ( ! m_bModifiedTextHasBeenSaved ) {
m_pEditBuffer->CutEditText(m_modifiedText);
m_bModifiedTextHasBeenSaved = TRUE;
}
else {
m_pEditBuffer->DeleteSelection();
}
m_pEditBuffer->PasteEditText(m_originalText);
m_pEditBuffer->SetSelection(m_originalDocument);
}
void CEditDataSaver::Redo(){
#ifdef DEBUG
XP_ASSERT(m_bDoState == 3);
m_bDoState--;
#endif
m_pEditBuffer->SetSelection(m_expandedOriginal);
m_pEditBuffer->DeleteSelection();
m_pEditBuffer->PasteEditText(m_modifiedText);
m_pEditBuffer->SetSelection(m_modifiedDocument);
}
// CDeleteTableCommand
CDeleteTableCommand::CDeleteTableCommand(CEditBuffer* buffer, intn id)
: CEditCommand(buffer, id)
{
m_pTable = NULL;
GetEditBuffer()->GetSelection(m_originalSelection);
CEditInsertPoint ip;
GetEditBuffer()->GetTableInsertPoint(ip);
GetEditBuffer()->ClearTableAndCellSelection();
m_pTable = ip.m_pElement->GetTableIgnoreSubdoc();
if ( m_pTable )
m_pTable->Delete();
}
CDeleteTableCommand::~CDeleteTableCommand()
{
// Now done in CEditTableElement::Delete()
// delete m_pTable;
}
void CDeleteTableCommand::Do() {
}
// CInsertTableCaptionCommand
CInsertTableCaptionCommand::CInsertTableCaptionCommand(CEditBuffer* buffer,
EDT_TableCaptionData* pData, intn id)
: CEditCommand(buffer, id)
{
m_pOldCaption = NULL;
GetEditBuffer()->GetSelection(m_originalSelection);
CEditInsertPoint ip;
GetEditBuffer()->GetTableInsertPoint(ip);
CEditTableElement* pTable = ip.m_pElement->GetTableIgnoreSubdoc();
if ( pTable )
{
CEditCaptionElement* pCaption = new CEditCaptionElement();
pCaption->SetData(pData);
pTable->SetCaption(pCaption);
pTable->FinishedLoad(GetEditBuffer());
// CLM: Don't move insert point if we have a selected cell
// (We use selection to indicate current cell in property dialogs)
if( !GetEditBuffer()->IsSelected() &&
ip.m_pElement->GetTableCellIgnoreSubdoc() != NULL )
{
// Put cursor at end of caption
ip.m_pElement = pTable->GetCaption()->GetLastMostChild()->Leaf();
ip.m_iPos = ip.m_pElement->GetLen();
GetEditBuffer()->SetInsertPoint(ip);
}
GetEditBuffer()->Relayout(pTable, 0);
}
}
CInsertTableCaptionCommand::~CInsertTableCaptionCommand()
{
delete m_pOldCaption;
}
void CInsertTableCaptionCommand::Do() {
// All done in constructor
}
// CDeleteTableCaptionCommand
CDeleteTableCaptionCommand::CDeleteTableCaptionCommand(CEditBuffer* buffer, intn id)
: CEditCommand(buffer, id),
m_pOldCaption(NULL)
{
GetEditBuffer()->GetSelection(m_originalSelection);
CEditInsertPoint ip;
GetEditBuffer()->GetTableInsertPoint(ip);
CEditTableElement* pTable = ip.m_pElement->GetTableIgnoreSubdoc();
if ( pTable )
{
m_pOldCaption = pTable->GetCaption();
if ( m_pOldCaption )
{
CEditTableCellElement* pTableCell = ip.m_pElement->GetTableCellIgnoreSubdoc();
//cmanske - Set cursor only if we are not in the caption being deleted
CEditCaptionElement *pCaption = ip.m_pElement->GetCaption();
m_pOldCaption->Unlink();
pTable->FinishedLoad(GetEditBuffer());
if( pCaption /*!pTableCell*/ )
{
// Set cursor to someplace that still exists
// TODO: NEED TO FIX THIS TO PLACE CURSOR CLOSER TO WHERE CAPTION WAS
CEditTableCellElement * pCell = pTable->GetFirstCell();
int32 X = 0;
int32 Y = 0;
if( pCell )
{
X = pCell->GetX();
Y = pCell->GetY();
}
GetEditBuffer()->StartSelection(X,Y);
}
GetEditBuffer()->Relayout(pTable, 0);
}
}
}
CDeleteTableCaptionCommand::~CDeleteTableCaptionCommand()
{
delete m_pOldCaption;
}
void CDeleteTableCaptionCommand::Do()
{
}
// CInsertTableRowCommand
CInsertTableRowCommand::CInsertTableRowCommand(CEditBuffer* buffer,
EDT_TableRowData* /* pData */, XP_Bool bAfterCurrentRow, intn number, intn id)
: CEditCommand(buffer, id)
{
m_number = number;
GetEditBuffer()->GetSelection(m_originalSelection);
if( GetEditBuffer()->IsSelected() )
{
GetEditBuffer()->ClearSelection();
}
CEditInsertPoint ip;
GetEditBuffer()->GetTableInsertPoint(ip);
CEditTableCellElement* pTableCell = ip.m_pElement->GetTableCellIgnoreSubdoc();
if ( pTableCell)
{
CEditTableElement* pTable = pTableCell->GetTable();
if ( pTable )
{
int32 X = pTableCell->GetX();
int32 Y = pTableCell->GetY();
int32 iNewY = Y + (bAfterCurrentRow ? pTableCell->GetHeight() : 0);
// Try to locate cursor in new cell
int32 iCaretY = iNewY + (bAfterCurrentRow ? pTable->GetCellSpacing() : 0);
pTable->InsertRows(Y, iNewY, number);
pTable->FinishedLoad(GetEditBuffer());
GetEditBuffer()->Relayout(pTable, 0);
GetEditBuffer()->MoveToExistingCell(pTable, X+2, iCaretY+2);
}
}
}
CInsertTableRowCommand::~CInsertTableRowCommand()
{
}
void CInsertTableRowCommand::Do() {
// All done in constructor
}
// CDeleteTableRowCommand
CDeleteTableRowCommand::CDeleteTableRowCommand(CEditBuffer* buffer, intn rows, intn id)
: CEditCommand(buffer, id),
m_table(0,0)
{
GetEditBuffer()->GetSelection(m_originalSelection);
//The code from Redo moved here:
CEditInsertPoint ip;
GetEditBuffer()->GetTableInsertPoint(ip);
CEditTableCellElement* pTableCell = ip.m_pElement->GetTableCellIgnoreSubdoc();
if ( pTableCell )
{
CEditTableElement* pTable = pTableCell->GetTable();
if ( pTable )
{
int32 X = pTableCell->GetX();
int32 Y = pTableCell->GetY();
//TODO: FIGURE THIS OUT
m_bDeletedWholeTable = FALSE; //m_row == 0 && m_rows >= pTable->GetRows();
pTable->DeleteRows(Y, rows);
pTable->FinishedLoad(GetEditBuffer());
// Move to a safe location so Relayout() doesn't assert
GetEditBuffer()->Relayout(pTable, 0, NULL, RELAYOUT_NOCARET);
// Try to move to whereever we deleted
GetEditBuffer()->MoveToExistingCell(pTable, X, Y);
}
}
}
CDeleteTableRowCommand::~CDeleteTableRowCommand()
{
}
void CDeleteTableRowCommand::Do()
{
// All done in constructor
}
// CInsertTableColumnCommand
CInsertTableColumnCommand::CInsertTableColumnCommand(CEditBuffer* buffer,
EDT_TableCellData* /* pData */, XP_Bool bAfterCurrentCell, intn number, intn id)
: CEditCommand(buffer, id)
{
// m_number = number;
GetEditBuffer()->GetSelection(m_originalSelection);
if( GetEditBuffer()->IsSelected() ){
GetEditBuffer()->ClearSelection();
}
CEditInsertPoint ip;
GetEditBuffer()->GetTableInsertPoint(ip);
CEditTableCellElement* pTableCell = ip.m_pElement->GetTableCellIgnoreSubdoc();
if ( pTableCell)
{
CEditTableElement* pTable = pTableCell->GetTable();
if ( pTable )
{
int32 X = pTableCell->GetX();
int32 Y = pTableCell->GetY();
int32 iNewX = X + (bAfterCurrentCell ? pTableCell->GetFullWidth() : 0); // WAS GetWidth()
// Try to place cursor in inserted cell
//int32 iCaretX = iNewX + (bAfterCurrentCell ? pTable->GetCellSpacing() : 0);
pTable->InsertColumns(X, iNewX, number);
pTable->FinishedLoad(GetEditBuffer());
GetEditBuffer()->Relayout(pTable, 0);
GetEditBuffer()->MoveToExistingCell(pTable, iNewX /*iCaretX*/, Y);
}
}
}
CInsertTableColumnCommand::~CInsertTableColumnCommand()
{
}
void CInsertTableColumnCommand::Do() {
// All done in constructor
}
// CDeleteTableColumnCommand
CDeleteTableColumnCommand::CDeleteTableColumnCommand(CEditBuffer* buffer, intn columns, intn id)
: CEditCommand(buffer, id),
m_table(0,0)
{
// m_columns = columns;
GetEditBuffer()->GetSelection(m_originalSelection);
CEditInsertPoint ip;
GetEditBuffer()->GetTableInsertPoint(ip);
CEditTableCellElement* pTableCell = ip.m_pElement->GetTableCellIgnoreSubdoc();
if ( pTableCell )
{
CEditTableElement* pTable = pTableCell->GetTable();
if ( pTable )
{
//TODO: FIGURE THIS OUT
m_bDeletedWholeTable = FALSE; //m_column == 0 && m_columns >= pTable->GetColumns();
int32 X = pTableCell->GetX();
int32 Y = pTableCell->GetY();
// We don't save the table to undo any more
pTable->DeleteColumns(X, columns /*, &m_table*/);
pTable->FinishedLoad(GetEditBuffer());
// Move to a safe location so Relayout() doesn't assert
GetEditBuffer()->Relayout(pTable, 0, NULL, RELAYOUT_NOCARET);
// Try to move to whereever we deleted
GetEditBuffer()->MoveToExistingCell(pTable, X, Y);
}
}
}
CDeleteTableColumnCommand::~CDeleteTableColumnCommand()
{
}
void CDeleteTableColumnCommand::Do()
{
//#endif
}
// CInsertTableCellCommand
CInsertTableCellCommand::CInsertTableCellCommand(CEditBuffer* buffer,
XP_Bool bAfterCurrentCell, intn number, intn id)
: CEditCommand(buffer, id)
{
m_number = number;
GetEditBuffer()->GetSelection(m_originalSelection);
if( GetEditBuffer()->IsSelected() ){
GetEditBuffer()->ClearSelection();
}
CEditInsertPoint ip;
GetEditBuffer()->GetTableInsertPoint(ip);
CEditTableCellElement* pTableCell = ip.m_pElement->GetTableCellIgnoreSubdoc();
if ( pTableCell)
{
CEditTableRowElement* pTableRow = pTableCell->GetTableRow();
CEditTableElement* pTable = pTableCell->GetTable();
if ( pTable && pTableRow )
{
int32 X = pTableCell->GetX();
int32 Y = pTableCell->GetY();
int32 iNewX = X + (bAfterCurrentCell ? pTableCell->GetFullWidth() : 0); // WAS GetWidth()
// Try to move cursor to new column
// int32 iCaretX = bAfterCurrentCell ? (iNewX+pTable->GetCellSpacing()) : X;
pTableRow->InsertCells(X, iNewX, number);
pTable->FinishedLoad(GetEditBuffer());
GetEditBuffer()->Relayout(pTable, 0);
GetEditBuffer()->MoveToExistingCell(pTable, iNewX /*iCaretX*/, Y);
}
}
}
CInsertTableCellCommand::~CInsertTableCellCommand()
{
}
void CInsertTableCellCommand::Do() {
// All done in constructor
}
// CDeleteTableCellCommand
CDeleteTableCellCommand::CDeleteTableCellCommand(CEditBuffer* buffer, intn columns, intn id)
: CEditCommand(buffer, id)
{
// m_columns = columns;
GetEditBuffer()->GetSelection(m_originalSelection);
CEditInsertPoint ip;
GetEditBuffer()->GetTableInsertPoint(ip);
CEditTableCellElement* pTableCell = ip.m_pElement->GetTableCellIgnoreSubdoc();
if ( pTableCell )
{
CEditTableRowElement* pTableRow = pTableCell->GetTableRowIgnoreSubdoc();
CEditTableElement* pTable = pTableCell->GetTableIgnoreSubdoc();
if ( pTable && pTableRow )
{
// TODO: FIGURE THIS OUT
m_bDeletedWholeTable = FALSE; // m_column == 0 && m_columns >= pTableRow->GetCells();
int32 X = pTableCell->GetX();
int32 Y = pTableCell->GetY();
pTableRow->DeleteCells(X, columns /*, &m_tableRow*/);
pTable->FinishedLoad(GetEditBuffer());
// Move to a safe location so Relayout() doesn't assert
GetEditBuffer()->Relayout(pTable, 0, NULL, RELAYOUT_NOCARET);
// Try to move to whereever we deleted
GetEditBuffer()->MoveToExistingCell(pTable, X, Y);
}
}
}
CDeleteTableCellCommand::~CDeleteTableCellCommand()
{
}
void CDeleteTableCellCommand::Do()
{
}
// CEditCommandGroup
CEditCommandGroup::CEditCommandGroup(CEditBuffer* pEditBuffer, int id)
: CEditCommand( pEditBuffer, id){
}
CEditCommandGroup::~CEditCommandGroup(){
for ( int i = 0; i < m_commands.Size(); i++ ) {
CEditCommand* item = m_commands[i];
delete item;
}
}
void CEditCommandGroup::AdoptAndDo(CEditCommand* pCommand){
pCommand->Do();
//TODO: IS THIS EVER FREED???
m_commands.Add(pCommand);
}
void CEditCommandGroup::Undo(){
for ( int i = m_commands.Size() - 1; i >= 0 ; i-- ) {
CEditCommand* item = m_commands[i];
item->Undo();
}
}
void CEditCommandGroup::Redo(){
for ( int i = 0; i < m_commands.Size(); i++ ) {
CEditCommand* item = m_commands[i];
item->Redo();
}
}
intn CEditCommandGroup::GetNumberOfCommands(){
return m_commands.Size();
}
#ifdef DEBUG
void CEditCommandGroup::Print(IStreamOut& stream){
CEditCommand::Print(stream);
stream.Printf(" %d commands\n", m_commands.Size());
for ( int i = 0; i < m_commands.Size(); i++ ) {
CEditCommand* item = m_commands[i];
stream.Printf(" [%d] 0x%08x ", i, item);
item->Print(stream);
stream.Printf("\n");
}
}
#endif
// CSetListDataCommand
CSetListDataCommand::CSetListDataCommand(CEditBuffer* pBuffer, EDT_ListData& listData, intn id)
: CEditCommand(pBuffer, id)
{
m_newData = listData;
m_pOldData = NULL;
}
CSetListDataCommand::~CSetListDataCommand(){
if ( m_pOldData ){
CEditListElement::FreeData( m_pOldData );
}
}
void CSetListDataCommand::Do(){
m_pOldData = GetEditBuffer()->GetListData();
XP_ASSERT(m_pOldData);
GetEditBuffer()->SetListData(&m_newData);
}
void CSetListDataCommand::Undo(){
if ( m_pOldData ){
GetEditBuffer()->SetListData(m_pOldData);
}
}
void CSetListDataCommand::Redo(){
GetEditBuffer()->SetListData(&m_newData);
}
// CChangePageDataCommand
CChangePageDataCommand::CChangePageDataCommand(CEditBuffer* buffer, intn id)
: CEditCommand(buffer, id)
{
m_oldData = GetEditBuffer()->GetPageData();
m_newData = NULL;
}
CChangePageDataCommand::~CChangePageDataCommand(){
if ( m_oldData ) {
GetEditBuffer()->FreePageData(m_oldData);
}
if ( m_newData ) {
GetEditBuffer()->FreePageData(m_newData);
}
}
void CChangePageDataCommand::Undo(){
if ( ! m_newData ) {
m_newData = GetEditBuffer()->GetPageData();
}
GetEditBuffer()->SetPageData(m_oldData);
}
void CChangePageDataCommand::Redo(){
GetEditBuffer()->SetPageData(m_newData);
}
// CSetMetaDataCommand
CSetMetaDataCommand::CSetMetaDataCommand(CEditBuffer* buffer, EDT_MetaData *pMetaData, XP_Bool bDelete, intn id)
: CEditCommand(buffer, id){
m_bDelete = bDelete;
int existingIndex = GetEditBuffer()->FindMetaData( pMetaData);
m_bNewItem = existingIndex < 0;
if ( m_bNewItem ) {
m_pOldData = 0;
}
else {
m_pOldData = GetEditBuffer()->GetMetaData(existingIndex);
}
if ( m_bDelete ) {
GetEditBuffer()->DeleteMetaData(pMetaData);
m_pNewData = 0;
}
else {
GetEditBuffer()->SetMetaData(pMetaData);
m_pNewData = GetEditBuffer()->GetMetaData(GetEditBuffer()->FindMetaData(pMetaData));
}
}
CSetMetaDataCommand::~CSetMetaDataCommand(){
if ( m_pOldData ) {
GetEditBuffer()->FreeMetaData(m_pOldData);
}
if ( m_pNewData ) {
GetEditBuffer()->FreeMetaData(m_pNewData);
}
}
void CSetMetaDataCommand::Undo(){
if ( m_bNewItem ) {
if ( m_pNewData ) {
GetEditBuffer()->DeleteMetaData(m_pNewData);
}
}
else {
if ( m_pOldData ) {
GetEditBuffer()->SetMetaData(m_pOldData);
}
}
}
void CSetMetaDataCommand::Redo(){
if ( m_bDelete ) {
if ( m_pOldData ) {
GetEditBuffer()->DeleteMetaData(m_pOldData);
}
}
else {
if ( m_pNewData ) {
GetEditBuffer()->SetMetaData(m_pNewData);
}
}
}
// CSetTableDataCommand
CSetTableDataCommand::CSetTableDataCommand(CEditBuffer* buffer, EDT_TableData* pTableData, intn id)
: CEditCommand(buffer, id){
m_pOldData = GetEditBuffer()->GetTableData();
GetEditBuffer()->SetTableData(pTableData);
m_pNewData = GetEditBuffer()->GetTableData();
}
CSetTableDataCommand::~CSetTableDataCommand(){
CEditTableElement::FreeData(m_pOldData);
CEditTableElement::FreeData(m_pNewData);
}
void CSetTableDataCommand::Undo(){
GetEditBuffer()->SetTableData(m_pOldData);
}
void CSetTableDataCommand::Redo(){
GetEditBuffer()->SetTableData(m_pNewData);
}
CSetTableCaptionDataCommand::CSetTableCaptionDataCommand(CEditBuffer* buffer, EDT_TableCaptionData* pTableData, intn id)
: CEditCommand(buffer, id){
m_pOldData = GetEditBuffer()->GetTableCaptionData();
GetEditBuffer()->SetTableCaptionData(pTableData);
m_pNewData = GetEditBuffer()->GetTableCaptionData();
}
CSetTableCaptionDataCommand::~CSetTableCaptionDataCommand(){
CEditCaptionElement::FreeData(m_pOldData);
CEditCaptionElement::FreeData(m_pNewData);
}
void CSetTableCaptionDataCommand::Undo(){
GetEditBuffer()->SetTableCaptionData(m_pOldData);
}
void CSetTableCaptionDataCommand::Redo(){
GetEditBuffer()->SetTableCaptionData(m_pNewData);
}
CSetTableRowDataCommand::CSetTableRowDataCommand(CEditBuffer* buffer, EDT_TableRowData* pTableData, intn id)
: CEditCommand(buffer, id){
m_pOldData = GetEditBuffer()->GetTableRowData();
GetEditBuffer()->SetTableRowData(pTableData);
m_pNewData = GetEditBuffer()->GetTableRowData();
}
CSetTableRowDataCommand::~CSetTableRowDataCommand(){
CEditTableRowElement::FreeData(m_pOldData);
CEditTableRowElement::FreeData(m_pNewData);
}
void CSetTableRowDataCommand::Undo(){
GetEditBuffer()->SetTableRowData(m_pOldData);
}
void CSetTableRowDataCommand::Redo(){
GetEditBuffer()->SetTableRowData(m_pNewData);
}
CSetTableCellDataCommand::CSetTableCellDataCommand(CEditBuffer* buffer, EDT_TableCellData* pTableData, intn id)
: CEditCommand(buffer, id){
m_pOldData = GetEditBuffer()->GetTableCellData();
GetEditBuffer()->SetTableCellData(pTableData);
m_pNewData = GetEditBuffer()->GetTableCellData();
}
CSetTableCellDataCommand::~CSetTableCellDataCommand(){
CEditTableCellElement::FreeData(m_pOldData);
CEditTableCellElement::FreeData(m_pNewData);
}
void CSetTableCellDataCommand::Undo(){
GetEditBuffer()->SetTableCellData(m_pOldData);
}
void CSetTableCellDataCommand::Redo(){
GetEditBuffer()->SetTableCellData(m_pNewData);
}
#ifdef SUPPORT_MULTICOLUMN
CSetMultiColumnDataCommand::CSetMultiColumnDataCommand(CEditBuffer* buffer, EDT_MultiColumnData* pMultiColumnData, intn id)
: CEditCommand(buffer, id){
m_pOldData = GetEditBuffer()->GetMultiColumnData();
GetEditBuffer()->SetMultiColumnData(pMultiColumnData);
m_pNewData = GetEditBuffer()->GetMultiColumnData();
}
CSetMultiColumnDataCommand::~CSetMultiColumnDataCommand(){
CEditMultiColumnElement::FreeData(m_pOldData);
CEditMultiColumnElement::FreeData(m_pNewData);
}
void CSetMultiColumnDataCommand::Undo(){
GetEditBuffer()->SetMultiColumnData(m_pOldData);
}
void CSetMultiColumnDataCommand::Redo(){
GetEditBuffer()->SetMultiColumnData(m_pNewData);
}
#endif
// CSetSelectionCommand
CSetSelectionCommand::CSetSelectionCommand(CEditBuffer* buffer, CEditSelection& selection, intn id)
: CEditCommand(buffer, id){
m_NewSelection = GetEditBuffer()->EphemeralToPersistent(selection);
}
CSetSelectionCommand::~CSetSelectionCommand(){
}
void CSetSelectionCommand::Do(){
GetEditBuffer()->GetSelection(m_OldSelection);
GetEditBuffer()->SetSelection(m_NewSelection);
}
void CSetSelectionCommand::Undo(){
GetEditBuffer()->SetSelection(m_OldSelection);
}
void CSetSelectionCommand::Redo(){
GetEditBuffer()->SetSelection(m_NewSelection);
}
#endif