/**************************************************************
 * 
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 * 
 *************************************************************/



// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_sc.hxx"



// INCLUDE ---------------------------------------------------------------

#include <com/sun/star/document/XDocumentPropertiesSupplier.hpp>
#include <com/sun/star/document/XDocumentProperties.hpp>

#include "scitems.hxx"
#include "rangelst.hxx"
#include <editeng/flstitem.hxx>
#include <svx/pageitem.hxx>
#include <editeng/paperinf.hxx>
#include <svx/postattr.hxx>
#include <editeng/sizeitem.hxx>
#include <unotools/misccfg.hxx>
#include <sfx2/viewfrm.hxx>
#include <sfx2/app.hxx>
#include <sfx2/docfile.hxx>
#include <sfx2/printer.hxx>
#include <svtools/ctrltool.hxx>
#include <vcl/virdev.hxx>
#include <vcl/svapp.hxx>
#include <vcl/msgbox.hxx>
#include <unotools/localedatawrapper.hxx>

#include "docsh.hxx"
#include "docshimp.hxx"
#include "scmod.hxx"
#include "tabvwsh.hxx"
#include "viewdata.hxx"
#include "docpool.hxx"
#include "stlpool.hxx"
#include "patattr.hxx"
#include "uiitems.hxx"
#include "hints.hxx"
#include "docoptio.hxx"
#include "viewopti.hxx"
#include "pntlock.hxx"
#include "chgtrack.hxx"
#include "docfunc.hxx"
#include "cell.hxx"
#include "chgviset.hxx"
#include "progress.hxx"
#include "redcom.hxx"
#include "sc.hrc"
#include "inputopt.hxx"
#include "drwlayer.hxx"
#include "inputhdl.hxx"
#include "conflictsdlg.hxx"
#include "globstr.hrc"

#if DEBUG_CHANGETRACK
#include <stdio.h>
#endif // DEBUG_CHANGETRACK


//------------------------------------------------------------------

//
//			Redraw - Benachrichtigungen
//


void ScDocShell::PostEditView( ScEditEngineDefaulter* pEditEngine, const ScAddress& rCursorPos )
{
//	Broadcast( ScEditViewHint( pEditEngine, rCursorPos ) );

		//	Test: nur aktive ViewShell

	ScTabViewShell* pViewSh = ScTabViewShell::GetActiveViewShell();
	if (pViewSh && pViewSh->GetViewData()->GetDocShell() == this)
	{
		ScEditViewHint aHint( pEditEngine, rCursorPos );
		pViewSh->Notify( *this, aHint );
	}
}

void ScDocShell::PostDataChanged()
{
	Broadcast( SfxSimpleHint( FID_DATACHANGED ) );
	aDocument.ResetChanged( ScRange(0,0,0,MAXCOL,MAXROW,MAXTAB) );

	SFX_APP()->Broadcast(SfxSimpleHint( FID_ANYDATACHANGED ));		// Navigator
	//!	Navigator direkt benachrichtigen!
}

void ScDocShell::PostPaint( SCCOL nStartCol, SCROW nStartRow, SCTAB nStartTab,
							SCCOL nEndCol, SCROW nEndRow, SCTAB nEndTab, sal_uInt16 nPart,
							sal_uInt16 nExtFlags )
{
	if (!ValidCol(nStartCol)) nStartCol = MAXCOL;
	if (!ValidRow(nStartRow)) nStartRow = MAXROW;
	if (!ValidCol(nEndCol)) nEndCol = MAXCOL;
	if (!ValidRow(nEndRow)) nEndRow = MAXROW;

	if ( pPaintLockData )
	{
        // #i54081# PAINT_EXTRAS still has to be brodcast because it changes the
        // current sheet if it's invalid. All other flags added to pPaintLockData.
        sal_uInt16 nLockPart = nPart & ~PAINT_EXTRAS;
        if ( nLockPart )
        {
            //! nExtFlags ???
            pPaintLockData->AddRange( ScRange( nStartCol, nStartRow, nStartTab,
                                               nEndCol, nEndRow, nEndTab ), nLockPart );
        }

        nPart &= PAINT_EXTRAS;  // for broadcasting
        if ( !nPart )
            return;
	}


    if (nExtFlags & SC_PF_LINES)            // Platz fuer Linien beruecksichtigen
	{
											//! Abfrage auf versteckte Spalten/Zeilen!
		if (nStartCol>0) --nStartCol;
		if (nEndCol<MAXCOL) ++nEndCol;
		if (nStartRow>0) --nStartRow;
		if (nEndRow<MAXROW) ++nEndRow;
	}

											// um zusammengefasste erweitern
	if (nExtFlags & SC_PF_TESTMERGE)
		aDocument.ExtendMerge( nStartCol, nStartRow, nEndCol, nEndRow, nStartTab );

	if ( nStartCol != 0 || nEndCol != MAXCOL )
	{
		//	Extend to whole rows if SC_PF_WHOLEROWS is set, or rotated or non-left
		//	aligned cells are contained (see UpdatePaintExt).
		//	Special handling for RTL text (#i9731#) is unnecessary now with full
		//	support of right-aligned text.

		if ( ( nExtFlags & SC_PF_WHOLEROWS ) ||
			 aDocument.HasAttrib( nStartCol,nStartRow,nStartTab,
								  MAXCOL,nEndRow,nEndTab, HASATTR_ROTATE | HASATTR_RIGHTORCENTER ) )
		{
			nStartCol = 0;
			nEndCol = MAXCOL;
		}
	}

	Broadcast( ScPaintHint( ScRange( nStartCol, nStartRow, nStartTab,
									 nEndCol, nEndRow, nEndTab ), nPart ) );

	if ( nPart & PAINT_GRID )
		aDocument.ResetChanged( ScRange(nStartCol,nStartRow,nStartTab,nEndCol,nEndRow,nEndTab) );
}

void ScDocShell::PostPaint( const ScRange& rRange, sal_uInt16 nPart, sal_uInt16 nExtFlags )
{
	PostPaint( rRange.aStart.Col(), rRange.aStart.Row(), rRange.aStart.Tab(),
			   rRange.aEnd.Col(),   rRange.aEnd.Row(),   rRange.aEnd.Tab(),
			   nPart, nExtFlags );
}

void ScDocShell::PostPaintGridAll()
{
	PostPaint( 0,0,0, MAXCOL,MAXROW,MAXTAB, PAINT_GRID );
}

void ScDocShell::PostPaintCell( SCCOL nCol, SCROW nRow, SCTAB nTab )
{
	PostPaint( nCol,nRow,nTab, nCol,nRow,nTab, PAINT_GRID, SC_PF_TESTMERGE );
}

void ScDocShell::PostPaintCell( const ScAddress& rPos )
{
	PostPaintCell( rPos.Col(), rPos.Row(), rPos.Tab() );
}

void ScDocShell::PostPaintExtras()
{
	PostPaint( 0,0,0, MAXCOL,MAXROW,MAXTAB, PAINT_EXTRAS );
}

void ScDocShell::UpdatePaintExt( sal_uInt16& rExtFlags, const ScRange& rRange )
{
	if ( ( rExtFlags & SC_PF_LINES ) == 0 && aDocument.HasAttrib( rRange, HASATTR_PAINTEXT ) )
	{
		//	If the range contains lines, shadow or conditional formats,
		//	set SC_PF_LINES to include one extra cell in all directions.

		rExtFlags |= SC_PF_LINES;
	}

	if ( ( rExtFlags & SC_PF_WHOLEROWS ) == 0 &&
		 ( rRange.aStart.Col() != 0 || rRange.aEnd.Col() != MAXCOL ) &&
		 aDocument.HasAttrib( rRange, HASATTR_ROTATE | HASATTR_RIGHTORCENTER ) )
	{
		//	If the range contains (logically) right- or center-aligned cells,
		//	or rotated cells, set SC_PF_WHOLEROWS to paint the whole rows.
		//	This test isn't needed after the cell changes, because it's also
		//	tested in PostPaint. UpdatePaintExt may later be changed to do this
		//	only if called before the changes.

		rExtFlags |= SC_PF_WHOLEROWS;
	}
}

void ScDocShell::UpdatePaintExt( sal_uInt16& rExtFlags, SCCOL nStartCol, SCROW nStartRow, SCTAB nStartTab,
												   SCCOL nEndCol, SCROW nEndRow, SCTAB nEndTab )
{
	UpdatePaintExt( rExtFlags, ScRange( nStartCol, nStartRow, nStartTab, nEndCol, nEndRow, nEndTab ) );
}

//------------------------------------------------------------------

void ScDocShell::LockPaint_Impl(sal_Bool bDoc)
{
	if ( !pPaintLockData )
		pPaintLockData = new ScPaintLockData(0);	//! Modus...
    pPaintLockData->IncLevel(bDoc);
}

void ScDocShell::UnlockPaint_Impl(sal_Bool bDoc)
{
	if ( pPaintLockData )
	{
		if ( pPaintLockData->GetLevel(bDoc) )
			pPaintLockData->DecLevel(bDoc);
		if (!pPaintLockData->GetLevel(!bDoc) && !pPaintLockData->GetLevel(bDoc))
		{
			//		Paint jetzt ausfuehren

			ScPaintLockData* pPaint = pPaintLockData;
			pPaintLockData = NULL;						// nicht weitersammeln

			ScRangeListRef xRangeList = pPaint->GetRangeList();
			if (xRangeList)
			{
				sal_uInt16 nParts = pPaint->GetParts();
				sal_uLong nCount = xRangeList->Count();
				for ( sal_uLong i=0; i<nCount; i++ )
				{
					//!	nExtFlags ???
					ScRange aRange = *xRangeList->GetObject(i);
					PostPaint( aRange.aStart.Col(), aRange.aStart.Row(), aRange.aStart.Tab(),
								aRange.aEnd.Col(), aRange.aEnd.Row(), aRange.aEnd.Tab(),
								nParts );
				}
			}

			if ( pPaint->GetModified() )
				SetDocumentModified();

			delete pPaint;
		}
	}
	else
	{
		DBG_ERROR("UnlockPaint ohne LockPaint");
	}
}

void ScDocShell::LockDocument_Impl(sal_uInt16 nNew)
{
	if (!nDocumentLock)
	{
		ScDrawLayer* pDrawLayer = aDocument.GetDrawLayer();
		if (pDrawLayer)
			pDrawLayer->setLock(sal_True);
	}
	nDocumentLock = nNew;
}

void ScDocShell::UnlockDocument_Impl(sal_uInt16 nNew)
{
	nDocumentLock = nNew;
	if (!nDocumentLock)
	{
		ScDrawLayer* pDrawLayer = aDocument.GetDrawLayer();
		if (pDrawLayer)
			pDrawLayer->setLock(sal_False);
	}
}

sal_uInt16 ScDocShell::GetLockCount() const
{
	return nDocumentLock;
}

void ScDocShell::SetLockCount(sal_uInt16 nNew)
{
	if (nNew)					// setzen
	{
		if ( !pPaintLockData )
			pPaintLockData = new ScPaintLockData(0);	//! Modus...
		pPaintLockData->SetLevel(nNew-1, sal_True);
		LockDocument_Impl(nNew);
	}
	else if (pPaintLockData)	// loeschen
	{
		pPaintLockData->SetLevel(0, sal_True);	// bei Unlock sofort ausfuehren
		UnlockPaint_Impl(sal_True);					// jetzt
		UnlockDocument_Impl(0);
	}
}

void ScDocShell::LockPaint()
{
	LockPaint_Impl(sal_False);
}

void ScDocShell::UnlockPaint()
{
	UnlockPaint_Impl(sal_False);
}

void ScDocShell::LockDocument()
{
	LockPaint_Impl(sal_True);
	LockDocument_Impl(nDocumentLock + 1);
}

void ScDocShell::UnlockDocument()
{
	if (nDocumentLock)
	{
		UnlockPaint_Impl(sal_True);
		UnlockDocument_Impl(nDocumentLock - 1);
	}
	else
	{
		DBG_ERROR("UnlockDocument without LockDocument");
	}
}

//------------------------------------------------------------------

void ScDocShell::SetInplace( sal_Bool bInplace )
{
	if (bIsInplace != bInplace)
	{
		bIsInplace = bInplace;
		CalcOutputFactor();
	}
}

void ScDocShell::CalcOutputFactor()
{
	if (bIsInplace)
	{
		nPrtToScreenFactor = 1.0;			// passt sonst nicht zur inaktiven Darstellung
		return;
	}

	sal_Bool bTextWysiwyg = SC_MOD()->GetInputOptions().GetTextWysiwyg();
	if (bTextWysiwyg)
	{
		nPrtToScreenFactor = 1.0;
		return;
	}

	String aTestString = String::CreateFromAscii(RTL_CONSTASCII_STRINGPARAM(
			"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz01234567890123456789" ));
	long nPrinterWidth = 0;
	long nWindowWidth = 0;
	const ScPatternAttr* pPattern = (const ScPatternAttr*)&aDocument.GetPool()->
											GetDefaultItem(ATTR_PATTERN);

	Font aDefFont;
	OutputDevice* pRefDev = GetRefDevice();
	MapMode aOldMode = pRefDev->GetMapMode();
	Font	aOldFont = pRefDev->GetFont();

	pRefDev->SetMapMode(MAP_PIXEL);
	pPattern->GetFont(aDefFont, SC_AUTOCOL_BLACK, pRefDev);	// font color doesn't matter here
	pRefDev->SetFont(aDefFont);
	nPrinterWidth = pRefDev->PixelToLogic( Size( pRefDev->GetTextWidth(aTestString), 0 ), MAP_100TH_MM ).Width();
	pRefDev->SetFont(aOldFont);
	pRefDev->SetMapMode(aOldMode);

	VirtualDevice aVirtWindow( *Application::GetDefaultDevice() );
	aVirtWindow.SetMapMode(MAP_PIXEL);
	pPattern->GetFont(aDefFont, SC_AUTOCOL_BLACK, &aVirtWindow);	// font color doesn't matter here
	aVirtWindow.SetFont(aDefFont);
	nWindowWidth = aVirtWindow.GetTextWidth(aTestString);
	nWindowWidth = (long) ( nWindowWidth / ScGlobal::nScreenPPTX * HMM_PER_TWIPS );

	if (nPrinterWidth && nWindowWidth)
		nPrtToScreenFactor = nPrinterWidth / (double) nWindowWidth;
	else
	{
		DBG_ERROR("GetTextSize gibt 0 ??");
		nPrtToScreenFactor = 1.0;
	}
}

double ScDocShell::GetOutputFactor() const
{
	return nPrtToScreenFactor;
}

//---------------------------------------------------------------------

void ScDocShell::InitOptions(bool bForLoading)      // called from InitNew and Load
{
	//	Einstellungen aus dem SpellCheckCfg kommen in Doc- und ViewOptions

	sal_uInt16 nDefLang, nCjkLang, nCtlLang;
    sal_Bool bAutoSpell;
    ScModule::GetSpellSettings( nDefLang, nCjkLang, nCtlLang, bAutoSpell );
	ScModule* pScMod = SC_MOD();

	ScDocOptions  aDocOpt  = pScMod->GetDocOptions();
	ScViewOptions aViewOpt = pScMod->GetViewOptions();
	aDocOpt.SetAutoSpell( bAutoSpell );

	// zweistellige Jahreszahleneingabe aus Extras->Optionen->Allgemein->Sonstiges
    aDocOpt.SetYear2000( sal::static_int_cast<sal_uInt16>( ::utl::MiscCfg().GetYear2000() ) );

    if (bForLoading)
    {
        // #i112123# No style:decimal-places attribute means automatic decimals, not the configured default,
        // so it must not be taken from the global options.
        // Calculation settings are handled separately in ScXMLBodyContext::EndElement.
        aDocOpt.SetStdPrecision( SvNumberFormatter::UNLIMITED_PRECISION );
    }

	aDocument.SetDocOptions( aDocOpt );
	aDocument.SetViewOptions( aViewOpt );

	//	Druck-Optionen werden jetzt direkt vor dem Drucken gesetzt

	aDocument.SetLanguage( (LanguageType) nDefLang, (LanguageType) nCjkLang, (LanguageType) nCtlLang );
}

//---------------------------------------------------------------------

Printer* ScDocShell::GetDocumentPrinter()		// fuer OLE
{
	return aDocument.GetPrinter();
}

SfxPrinter* ScDocShell::GetPrinter(sal_Bool bCreateIfNotExist)
{
	return aDocument.GetPrinter(bCreateIfNotExist);
}

void ScDocShell::UpdateFontList()
{
    delete pImpl->pFontList;
    // pImpl->pFontList = new FontList( GetPrinter(), Application::GetDefaultDevice() );
    pImpl->pFontList = new FontList( GetRefDevice(), NULL, sal_False ); // sal_False or sal_True???
    SvxFontListItem aFontListItem( pImpl->pFontList, SID_ATTR_CHAR_FONTLIST );
	PutItem( aFontListItem );

	CalcOutputFactor();
}

OutputDevice* ScDocShell::GetRefDevice()
{
	return aDocument.GetRefDevice();
}

sal_uInt16 ScDocShell::SetPrinter( SfxPrinter* pNewPrinter, sal_uInt16 nDiffFlags )
{
    SfxPrinter *pOld = aDocument.GetPrinter( sal_False );
    if ( pOld && pOld->IsPrinting() )
        return SFX_PRINTERROR_BUSY;

	if (nDiffFlags & SFX_PRINTER_PRINTER)
	{
		if ( aDocument.GetPrinter() != pNewPrinter )
		{
			aDocument.SetPrinter( pNewPrinter );
			aDocument.SetPrintOptions();

			// MT: Use UpdateFontList: Will use Printer fonts only if needed!
			/*
            delete pImpl->pFontList;
            pImpl->pFontList = new FontList( pNewPrinter, Application::GetDefaultDevice() );
            SvxFontListItem aFontListItem( pImpl->pFontList, SID_ATTR_CHAR_FONTLIST );
			PutItem( aFontListItem );

			CalcOutputFactor();
			*/
			if ( SC_MOD()->GetInputOptions().GetTextWysiwyg() )
				UpdateFontList();

			ScModule* pScMod = SC_MOD();
			SfxViewFrame *pFrame = SfxViewFrame::GetFirst( this );
			while (pFrame)
			{
				SfxViewShell* pSh = pFrame->GetViewShell();
				if (pSh && pSh->ISA(ScTabViewShell))
				{
					ScTabViewShell* pViewSh	= (ScTabViewShell*)pSh;
					ScInputHandler* pInputHdl = pScMod->GetInputHdl(pViewSh);
					if (pInputHdl)
						pInputHdl->UpdateRefDevice();
				}
				pFrame = SfxViewFrame::GetNext( *pFrame, this );
			}
		}
	}
	else if (nDiffFlags & SFX_PRINTER_JOBSETUP)
	{
		SfxPrinter* pOldPrinter = aDocument.GetPrinter();
		if (pOldPrinter)
		{
			pOldPrinter->SetJobSetup( pNewPrinter->GetJobSetup() );

			//	#i6706# Call SetPrinter with the old printer again, so the drawing layer
			//	RefDevice is set (calling ReformatAllTextObjects and rebuilding charts),
			//	because the JobSetup (printer device settings) may affect text layout.
			aDocument.SetPrinter( pOldPrinter );
			CalcOutputFactor();							// also with the new settings
		}
	}

	if (nDiffFlags & SFX_PRINTER_OPTIONS)
	{
		aDocument.SetPrintOptions();		//! aus neuem Printer ???
	}

	if (nDiffFlags & (SFX_PRINTER_CHG_ORIENTATION | SFX_PRINTER_CHG_SIZE))
	{
		String aStyle = aDocument.GetPageStyle( GetCurTab() );
		ScStyleSheetPool* pStPl = aDocument.GetStyleSheetPool();
		SfxStyleSheet* pStyleSheet = (SfxStyleSheet*)pStPl->Find(aStyle, SFX_STYLE_FAMILY_PAGE);
		if (pStyleSheet)
		{
			SfxItemSet& rSet = pStyleSheet->GetItemSet();

			if (nDiffFlags & SFX_PRINTER_CHG_ORIENTATION)
			{
				const SvxPageItem& rOldItem = (const SvxPageItem&)rSet.Get(ATTR_PAGE);
				sal_Bool bWasLand = rOldItem.IsLandscape();
				sal_Bool bNewLand = ( pNewPrinter->GetOrientation() == ORIENTATION_LANDSCAPE );
				if (bNewLand != bWasLand)
				{
					SvxPageItem aNewItem( rOldItem );
					aNewItem.SetLandscape( bNewLand );
					rSet.Put( aNewItem );

					//	Groesse umdrehen
					Size aOldSize = ((const SvxSizeItem&)rSet.Get(ATTR_PAGE_SIZE)).GetSize();
					Size aNewSize(aOldSize.Height(),aOldSize.Width());
					SvxSizeItem aNewSItem(ATTR_PAGE_SIZE,aNewSize);
					rSet.Put( aNewSItem );
				}
			}
			if (nDiffFlags & SFX_PRINTER_CHG_SIZE)
			{
				SvxSizeItem	aPaperSizeItem( ATTR_PAGE_SIZE, SvxPaperInfo::GetPaperSize(pNewPrinter) );
				rSet.Put( aPaperSizeItem );
			}
		}
	}

	PostPaint(0,0,0,MAXCOL,MAXROW,MAXTAB,PAINT_ALL);

	return 0;
}

//---------------------------------------------------------------------

ScChangeAction* ScDocShell::GetChangeAction( const ScAddress& rPos )
{
	ScChangeTrack* pTrack = GetDocument()->GetChangeTrack();
	if (!pTrack)
		return NULL;

	SCTAB nTab = rPos.Tab();

	const ScChangeAction* pFound = NULL;
	const ScChangeAction* pFoundContent = NULL;
	const ScChangeAction* pFoundMove = NULL;
	long nModified = 0;
	const ScChangeAction* pAction = pTrack->GetFirst();
	while (pAction)
	{
		ScChangeActionType eType = pAction->GetType();
		//!	ScViewUtil::IsActionShown( *pAction, *pSettings, *pDoc )...
		if ( pAction->IsVisible() && eType != SC_CAT_DELETE_TABS )
		{
			const ScBigRange& rBig = pAction->GetBigRange();
			if ( rBig.aStart.Tab() == nTab )
			{
				ScRange aRange = rBig.MakeRange();

				if ( eType == SC_CAT_DELETE_ROWS )
					aRange.aEnd.SetRow( aRange.aStart.Row() );
				else if ( eType == SC_CAT_DELETE_COLS )
					aRange.aEnd.SetCol( aRange.aStart.Col() );

				if ( aRange.In( rPos ) )
				{
					pFound = pAction;		// der letzte gewinnt
					switch ( pAction->GetType() )
					{
						case SC_CAT_CONTENT :
							pFoundContent = pAction;
						break;
						case SC_CAT_MOVE :
							pFoundMove = pAction;
						break;
                        default:
                        {
                            // added to avoid warnings
                        }
					}
					++nModified;
				}
			}
			if ( pAction->GetType() == SC_CAT_MOVE )
			{
				ScRange aRange =
					((const ScChangeActionMove*)pAction)->
					GetFromRange().MakeRange();
				if ( aRange.In( rPos ) )
				{
					pFound = pAction;
					++nModified;
				}
			}
		}
		pAction = pAction->GetNext();
	}

	return (ScChangeAction*)pFound;
}

void ScDocShell::SetChangeComment( ScChangeAction* pAction, const String& rComment )
{
	if (pAction)
	{
		pAction->SetComment( rComment );
		//!	Undo ???
		SetDocumentModified();

		//	Dialog-Notify
		ScChangeTrack* pTrack = GetDocument()->GetChangeTrack();
		if (pTrack)
		{
			sal_uLong nNumber = pAction->GetActionNumber();
			pTrack->NotifyModified( SC_CTM_CHANGE, nNumber, nNumber );
		}
	}
}

void ScDocShell::ExecuteChangeCommentDialog( ScChangeAction* pAction, Window* pParent,sal_Bool bPrevNext)
{
	if (!pAction) return;			// ohne Aktion ist nichts..

	String aComment = pAction->GetComment();
	String aAuthor = pAction->GetUser();

	DateTime aDT = pAction->GetDateTime();
    String aDate = ScGlobal::pLocaleData->getDate( aDT );
	aDate += ' ';
    aDate += ScGlobal::pLocaleData->getTime( aDT, sal_False, sal_False );

	SfxItemSet aSet( GetPool(),
					  SID_ATTR_POSTIT_AUTHOR, SID_ATTR_POSTIT_AUTHOR,
					  SID_ATTR_POSTIT_DATE,   SID_ATTR_POSTIT_DATE,
					  SID_ATTR_POSTIT_TEXT,   SID_ATTR_POSTIT_TEXT,
					  0 );

	aSet.Put( SvxPostItTextItem  ( aComment, SID_ATTR_POSTIT_TEXT ) );
	aSet.Put( SvxPostItAuthorItem( aAuthor,  SID_ATTR_POSTIT_AUTHOR ) );
	aSet.Put( SvxPostItDateItem  ( aDate,    SID_ATTR_POSTIT_DATE ) );

	ScRedComDialog* pDlg = new ScRedComDialog( pParent, aSet,this,pAction,bPrevNext);

	pDlg->Execute();

	delete pDlg;
}

//---------------------------------------------------------------------

void ScDocShell::CompareDocument( ScDocument& rOtherDoc )
{
	ScChangeTrack* pTrack = aDocument.GetChangeTrack();
	if ( pTrack && pTrack->GetFirst() )
	{
		//!	Changes vorhanden -> Nachfrage ob geloescht werden soll
	}

	aDocument.EndChangeTracking();
	aDocument.StartChangeTracking();

	String aOldUser;
	pTrack = aDocument.GetChangeTrack();
	if ( pTrack )
	{
		aOldUser = pTrack->GetUser();

		//	check if comparing to same document

		String aThisFile;
		const SfxMedium* pThisMed = GetMedium();
		if (pThisMed)
			aThisFile = pThisMed->GetName();
		String aOtherFile;
		SfxObjectShell* pOtherSh = rOtherDoc.GetDocumentShell();
		if (pOtherSh)
		{
			const SfxMedium* pOtherMed = pOtherSh->GetMedium();
			if (pOtherMed)
				aOtherFile = pOtherMed->GetName();
		}
		sal_Bool bSameDoc = ( aThisFile == aOtherFile && aThisFile.Len() );
		if ( !bSameDoc )
		{
			//	create change actions from comparing with the name of the user
			//	who last saved the document
			//	(only if comparing different documents)

            using namespace ::com::sun::star;
            uno::Reference<document::XDocumentPropertiesSupplier> xDPS(
                GetModel(), uno::UNO_QUERY_THROW);
            uno::Reference<document::XDocumentProperties> xDocProps(
                xDPS->getDocumentProperties());
            DBG_ASSERT(xDocProps.is(), "no DocumentProperties");
            String aDocUser = xDocProps->getModifiedBy();

			if ( aDocUser.Len() )
				pTrack->SetUser( aDocUser );
		}
	}

	aDocument.CompareDocument( rOtherDoc );

	pTrack = aDocument.GetChangeTrack();
	if ( pTrack )
		pTrack->SetUser( aOldUser );

	PostPaintGridAll();
	SetDocumentModified();
}

//---------------------------------------------------------------------
//
//				Merge (Aenderungen zusammenfuehren)
//
//---------------------------------------------------------------------

inline sal_Bool lcl_Equal( const ScChangeAction* pA, const ScChangeAction* pB, sal_Bool bIgnore100Sec )
{
	return pA && pB &&
        pA->GetActionNumber() == pB->GetActionNumber() &&
        pA->GetType()		  == pB->GetType() &&
        pA->GetUser()		  == pB->GetUser() &&
        (bIgnore100Sec ?
         pA->GetDateTimeUTC().IsEqualIgnore100Sec( pB->GetDateTimeUTC() ) :
         pA->GetDateTimeUTC() == pB->GetDateTimeUTC());
	//	State nicht vergleichen, falls eine alte Aenderung akzeptiert wurde
}

bool lcl_FindAction( ScDocument* pDoc, const ScChangeAction* pAction, ScDocument* pSearchDoc, const ScChangeAction* pFirstSearchAction, const ScChangeAction* pLastSearchAction, sal_Bool bIgnore100Sec )
{
    if ( !pDoc || !pAction || !pSearchDoc || !pFirstSearchAction || !pLastSearchAction )
    {
        return false;
    }

    sal_uLong nLastSearchAction = pLastSearchAction->GetActionNumber();
    const ScChangeAction* pA = pFirstSearchAction;
    while ( pA && pA->GetActionNumber() <= nLastSearchAction )
    {
	    if ( pAction->GetType() == pA->GetType() &&
             pAction->GetUser() == pA->GetUser() &&
             (bIgnore100Sec ?
                pAction->GetDateTimeUTC().IsEqualIgnore100Sec( pA->GetDateTimeUTC() ) :
                pAction->GetDateTimeUTC() == pA->GetDateTimeUTC() ) &&
             pAction->GetBigRange() == pA->GetBigRange() )
        {
            String aActionDesc;
            pAction->GetDescription( aActionDesc, pDoc, sal_True );
            String aADesc;
            pA->GetDescription( aADesc, pSearchDoc, sal_True );
            if ( aActionDesc.Equals( aADesc ) )
            {
                DBG_ERROR( "lcl_FindAction(): found equal action!" );
                return true;
            }
        }
        pA = pA->GetNext();
    }

    return false;
}

void ScDocShell::MergeDocument( ScDocument& rOtherDoc, bool bShared, bool bCheckDuplicates, sal_uLong nOffset, ScChangeActionMergeMap* pMergeMap, bool bInverseMap )
{
    ScTabViewShell* pViewSh = GetBestViewShell( sal_False );    //! Funktionen an die DocShell
	if (!pViewSh)
		return;

	ScChangeTrack* pSourceTrack = rOtherDoc.GetChangeTrack();
	if (!pSourceTrack)
		return;				//!	nichts zu tun - Fehlermeldung?

	ScChangeTrack* pThisTrack = aDocument.GetChangeTrack();
	if ( !pThisTrack )
	{	// anschalten
		aDocument.StartChangeTracking();
		pThisTrack = aDocument.GetChangeTrack();
		DBG_ASSERT(pThisTrack,"ChangeTracking nicht angeschaltet?");
        if ( !bShared )
        {
            // #51138# visuelles RedLining einschalten
            ScChangeViewSettings aChangeViewSet;
            aChangeViewSet.SetShowChanges(sal_True);
            aDocument.SetChangeViewSettings(aChangeViewSet);
        }
	}

    // #97286# include 100th seconds in compare?
    sal_Bool bIgnore100Sec = !pSourceTrack->IsTime100thSeconds() ||
            !pThisTrack->IsTime100thSeconds();

	//	gemeinsame Ausgangsposition suchen
	sal_uLong nFirstNewNumber = 0;
	const ScChangeAction* pSourceAction = pSourceTrack->GetFirst();
	const ScChangeAction* pThisAction = pThisTrack->GetFirst();
    // skip identical actions
    while ( lcl_Equal( pSourceAction, pThisAction, bIgnore100Sec ) )
	{
		nFirstNewNumber = pSourceAction->GetActionNumber() + 1;
		pSourceAction = pSourceAction->GetNext();
		pThisAction = pThisAction->GetNext();
	}
	//	pSourceAction und pThisAction zeigen jetzt auf die ersten "eigenen" Aktionen
	//	Die gemeinsamen Aktionen davor interessieren ueberhaupt nicht

	//!	Abfrage, ob die Dokumente vor dem Change-Tracking gleich waren !!!


	const ScChangeAction* pFirstMergeAction = pSourceAction;
    const ScChangeAction* pFirstSearchAction = pThisAction;

    // #i94841# [Collaboration] When deleting rows is rejected, the content is sometimes wrong
    const ScChangeAction* pLastSearchAction = pThisTrack->GetLast();

	//	MergeChangeData aus den folgenden Aktionen erzeugen
	sal_uLong nNewActionCount = 0;
	const ScChangeAction* pCount = pSourceAction;
	while ( pCount )
	{
        if ( bShared || !ScChangeTrack::MergeIgnore( *pCount, nFirstNewNumber ) )
			++nNewActionCount;
		pCount = pCount->GetNext();
	}
	if (!nNewActionCount)
		return;				//!	nichts zu tun - Fehlermeldung?
							//	ab hier kein return mehr

	ScProgress aProgress( this,
					String::CreateFromAscii(RTL_CONSTASCII_STRINGPARAM("...")),
					nNewActionCount );

	sal_uLong nLastMergeAction = pSourceTrack->GetLast()->GetActionNumber();
	// UpdateReference-Undo, gueltige Referenzen fuer den letzten gemeinsamen Zustand
    pSourceTrack->MergePrepare( (ScChangeAction*) pFirstMergeAction, bShared );

	//	MergeChangeData an alle noch folgenden Aktionen in diesem Dokument anpassen
	//	-> Referenzen gueltig fuer dieses Dokument
	while ( pThisAction )
	{
        // #i87049# [Collaboration] Conflict between delete row and insert content is not merged correctly
        if ( !bShared || !ScChangeTrack::MergeIgnore( *pThisAction, nFirstNewNumber ) )
        {
            ScChangeActionType eType = pThisAction->GetType();
		    switch ( eType )
		    {
			    case SC_CAT_INSERT_COLS :
			    case SC_CAT_INSERT_ROWS :
			    case SC_CAT_INSERT_TABS :
				    pSourceTrack->AppendInsert( pThisAction->GetBigRange().MakeRange() );
			    break;
			    case SC_CAT_DELETE_COLS :
			    case SC_CAT_DELETE_ROWS :
			    case SC_CAT_DELETE_TABS :
			    {
				    const ScChangeActionDel* pDel = (const ScChangeActionDel*) pThisAction;
				    if ( pDel->IsTopDelete() && !pDel->IsTabDeleteCol() )
				    {	// deleted Table enthaelt deleted Cols, die nicht
					    sal_uLong nStart, nEnd;
					    pSourceTrack->AppendDeleteRange(
						    pDel->GetOverAllRange().MakeRange(), NULL, nStart, nEnd );
				    }
			    }
			    break;
			    case SC_CAT_MOVE :
			    {
				    const ScChangeActionMove* pMove = (const ScChangeActionMove*) pThisAction;
				    pSourceTrack->AppendMove( pMove->GetFromRange().MakeRange(),
					    pMove->GetBigRange().MakeRange(), NULL );
			    }
			    break;
                default:
                {
                    // added to avoid warnings
                }
		    }
        }
		pThisAction = pThisAction->GetNext();
	}

    LockPaint();    // #i73877# no repainting after each action

	//	MergeChangeData in das aktuelle Dokument uebernehmen
	sal_Bool bHasRejected = sal_False;
	String aOldUser = pThisTrack->GetUser();
	pThisTrack->SetUseFixDateTime( sal_True );
	ScMarkData& rMarkData = pViewSh->GetViewData()->GetMarkData();
	ScMarkData aOldMarkData( rMarkData );
	pSourceAction = pFirstMergeAction;
	while ( pSourceAction && pSourceAction->GetActionNumber() <= nLastMergeAction )
	{
        bool bMergeAction = false;
        if ( bShared )
        {
            if ( !bCheckDuplicates || !lcl_FindAction( &rOtherDoc, pSourceAction, &aDocument, pFirstSearchAction, pLastSearchAction, bIgnore100Sec ) )
            {
                bMergeAction = true;
            }
        }
        else
        {
            if ( !ScChangeTrack::MergeIgnore( *pSourceAction, nFirstNewNumber ) )
            {
                bMergeAction = true;
            }
        }

        if ( bMergeAction )
		{
			ScChangeActionType eSourceType = pSourceAction->GetType();
            if ( !bShared && pSourceAction->IsDeletedIn() )
			{
				//! muss hier noch festgestellt werden, ob wirklich in
				//! _diesem_ Dokument geloescht?

				//	liegt in einem Bereich, der in diesem Dokument geloescht wurde
				//	-> wird weggelassen
				//!	??? Loesch-Aktion rueckgaengig machen ???
				//!	??? Aktion irgendwo anders speichern  ???
#ifdef DBG_UTIL
				String aValue;
				if ( eSourceType == SC_CAT_CONTENT )
					((const ScChangeActionContent*)pSourceAction)->GetNewString( aValue );
				ByteString aError( aValue, gsl_getSystemTextEncoding() );
				aError += " weggelassen";
				DBG_ERROR( aError.GetBuffer() );
#endif
			}
			else
			{
				//!	Datum/Autor/Kommentar der Source-Aktion uebernehmen!

				pThisTrack->SetUser( pSourceAction->GetUser() );
				pThisTrack->SetFixDateTimeUTC( pSourceAction->GetDateTimeUTC() );
                sal_uLong nOldActionMax = pThisTrack->GetActionMax();

                bool bExecute = true;
				sal_uLong nReject = pSourceAction->GetRejectAction();
                if ( nReject )
				{
                    if ( bShared )
                    {
                        if ( nReject >= nFirstNewNumber )
                        {
                            nReject += nOffset;
                        }
                        ScChangeAction* pOldAction = pThisTrack->GetAction( nReject );
                        if ( pOldAction && pOldAction->IsVirgin() )
                        {
                            pThisTrack->Reject( pOldAction );
                            bHasRejected = sal_True;
                            bExecute = false;
                        }
                    }
                    else
                    {
					    //	alte Aktion (aus den gemeinsamen) ablehnen
					    ScChangeAction* pOldAction = pThisTrack->GetAction( nReject );
					    if (pOldAction && pOldAction->GetState() == SC_CAS_VIRGIN)
					    {
						    //!	was passiert bei Aktionen, die in diesem Dokument accepted worden sind???
						    //!	Fehlermeldung oder was???
						    //!	oder Reject-Aenderung normal ausfuehren

						    pThisTrack->Reject(pOldAction);
						    bHasRejected = sal_True;				// fuer Paint
					    }
                        bExecute = false;
                    }
				}

                if ( bExecute )
				{
					//	normal ausfuehren
					ScRange aSourceRange = pSourceAction->GetBigRange().MakeRange();
					rMarkData.SelectOneTable( aSourceRange.aStart.Tab() );
					switch ( eSourceType )
					{
						case SC_CAT_CONTENT:
						{
							//!	Test, ob es ganz unten im Dokument war, dann automatisches
							//!	Zeilen-Einfuegen ???

							DBG_ASSERT( aSourceRange.aStart == aSourceRange.aEnd, "huch?" );
							ScAddress aPos = aSourceRange.aStart;
							String aValue;
							((const ScChangeActionContent*)pSourceAction)->GetNewString( aValue );
							sal_uInt8 eMatrix = MM_NONE;
							const ScBaseCell* pCell = ((const ScChangeActionContent*)pSourceAction)->GetNewCell();
                            if ( pCell && pCell->GetCellType() == CELLTYPE_FORMULA )
								eMatrix = ((const ScFormulaCell*)pCell)->GetMatrixFlag();
							switch ( eMatrix )
							{
								case MM_NONE :
									pViewSh->EnterData( aPos.Col(), aPos.Row(), aPos.Tab(), aValue );
								break;
								case MM_FORMULA :
								{
                                    SCCOL nCols;
                                    SCROW nRows;
                                    ((const ScFormulaCell*)pCell)->GetMatColsRows( nCols, nRows );
									aSourceRange.aEnd.SetCol( aPos.Col() + nCols - 1 );
									aSourceRange.aEnd.SetRow( aPos.Row() + nRows - 1 );
									aValue.Erase( 0, 1 );
									aValue.Erase( aValue.Len()-1, 1 );
                                    GetDocFunc().EnterMatrix( aSourceRange,
                                        NULL, NULL, aValue, sal_False, sal_False,
                                        EMPTY_STRING, formula::FormulaGrammar::GRAM_DEFAULT );
								}
								break;
								case MM_REFERENCE :		// do nothing
								break;
								case MM_FAKE :
									DBG_WARNING( "MergeDocument: MatrixFlag MM_FAKE" );
									pViewSh->EnterData( aPos.Col(), aPos.Row(), aPos.Tab(), aValue );
								break;
								default:
									DBG_ERROR( "MergeDocument: unknown MatrixFlag" );
							}
						}
						break;
						case SC_CAT_INSERT_TABS :
						{
							String aName;
							aDocument.CreateValidTabName( aName );
							GetDocFunc().InsertTable( aSourceRange.aStart.Tab(), aName, sal_True, sal_False );
						}
						break;
						case SC_CAT_INSERT_ROWS:
							GetDocFunc().InsertCells( aSourceRange, NULL, INS_INSROWS, sal_True, sal_False );
						break;
						case SC_CAT_INSERT_COLS:
							GetDocFunc().InsertCells( aSourceRange, NULL, INS_INSCOLS, sal_True, sal_False );
						break;
						case SC_CAT_DELETE_TABS :
							GetDocFunc().DeleteTable( aSourceRange.aStart.Tab(), sal_True, sal_False );
						break;
						case SC_CAT_DELETE_ROWS:
						{
							const ScChangeActionDel* pDel = (const ScChangeActionDel*) pSourceAction;
							if ( pDel->IsTopDelete() )
							{
								aSourceRange = pDel->GetOverAllRange().MakeRange();
								GetDocFunc().DeleteCells( aSourceRange, NULL, DEL_DELROWS, sal_True, sal_False );

                                // #i101099# [Collaboration] Changes are not correctly shown
                                if ( bShared )
                                {
                                    ScChangeAction* pAct = pThisTrack->GetLast();
                        			if ( pAct && pAct->GetType() == eSourceType && pAct->IsDeletedIn() && !pSourceAction->IsDeletedIn() )
                                    {
                                        pAct->RemoveAllDeletedIn();
                                    }
                                }
							}
						}
						break;
						case SC_CAT_DELETE_COLS:
						{
							const ScChangeActionDel* pDel = (const ScChangeActionDel*) pSourceAction;
							if ( pDel->IsTopDelete() && !pDel->IsTabDeleteCol() )
							{	// deleted Table enthaelt deleted Cols, die nicht
								aSourceRange = pDel->GetOverAllRange().MakeRange();
								GetDocFunc().DeleteCells( aSourceRange, NULL, DEL_DELCOLS, sal_True, sal_False );
							}
						}
						break;
						case SC_CAT_MOVE :
						{
							const ScChangeActionMove* pMove = (const ScChangeActionMove*) pSourceAction;
							ScRange aFromRange( pMove->GetFromRange().MakeRange() );
							GetDocFunc().MoveBlock( aFromRange,
								aSourceRange.aStart, sal_True, sal_True, sal_False, sal_False );
						}
						break;
                        default:
                        {
                            // added to avoid warnings
                        }
					}
				}
				const String& rComment = pSourceAction->GetComment();
				if ( rComment.Len() )
				{
					ScChangeAction* pAct = pThisTrack->GetLast();
                    if ( pAct && pAct->GetActionNumber() > nOldActionMax )
						pAct->SetComment( rComment );
#ifdef DBG_UTIL
					else
						DBG_ERROR( "MergeDocument: wohin mit dem Kommentar?!?" );
#endif
				}

				// Referenzen anpassen
				pSourceTrack->MergeOwn( (ScChangeAction*) pSourceAction, nFirstNewNumber, bShared );

                // merge action state
                if ( bShared && !pSourceAction->IsRejected() )
                {
                    ScChangeAction* pAct = pThisTrack->GetLast();
                    if ( pAct && pAct->GetActionNumber() > nOldActionMax )
                    {
                        pThisTrack->MergeActionState( pAct, pSourceAction );
                    }
                }

                // fill merge map
                if ( bShared && pMergeMap )
                {
                    ScChangeAction* pAct = pThisTrack->GetLast();
                    if ( pAct && pAct->GetActionNumber() > nOldActionMax )
                    {
                        sal_uLong nActionMax = pAct->GetActionNumber();
                        sal_uLong nActionCount = nActionMax - nOldActionMax;
                        sal_uLong nAction = nActionMax - nActionCount + 1;
                        sal_uLong nSourceAction = pSourceAction->GetActionNumber() - nActionCount + 1;
                        while ( nAction <= nActionMax )
                        {
                            if ( bInverseMap )
                            {
                                (*pMergeMap)[ nAction++ ] = nSourceAction++;
                            }
                            else
                            {
                                (*pMergeMap)[ nSourceAction++ ] = nAction++;
                            }
                        }
                    }
                }
			}
			aProgress.SetStateCountDown( --nNewActionCount );
		}
		pSourceAction = pSourceAction->GetNext();
	}

	rMarkData = aOldMarkData;
	pThisTrack->SetUser(aOldUser);
	pThisTrack->SetUseFixDateTime( sal_False );

	pSourceTrack->Clear();		//! der ist jetzt verhunzt

	if (bHasRejected)
		PostPaintGridAll();			// Reject() paintet nicht selber

    UnlockPaint();
}

bool ScDocShell::MergeSharedDocument( ScDocShell* pSharedDocShell )
{
    if ( !pSharedDocShell )
    {
        return false;
    }

    ScChangeTrack* pThisTrack = aDocument.GetChangeTrack();
    if ( !pThisTrack )
    {
        return false;
    }

    ScDocument& rSharedDoc = *( pSharedDocShell->GetDocument() );
    ScChangeTrack* pSharedTrack = rSharedDoc.GetChangeTrack();
    if ( !pSharedTrack )
    {
        return false;
    }

#if DEBUG_CHANGETRACK
    ::rtl::OUString aMessage = ::rtl::OUString::createFromAscii( "\nbefore merge:\n" );
    aMessage += pThisTrack->ToString();
    ::rtl::OString aMsg = ::rtl::OUStringToOString( aMessage, RTL_TEXTENCODING_UTF8 );
    OSL_ENSURE( false, aMsg.getStr() );
    //fprintf( stdout, "%s ", aMsg.getStr() );
    //fflush( stdout );
#endif // DEBUG_CHANGETRACK

    // reset show changes
    ScChangeViewSettings aChangeViewSet;
    aChangeViewSet.SetShowChanges( sal_False );
    aDocument.SetChangeViewSettings( aChangeViewSet );

    // find first merge action in this document
    sal_Bool bIgnore100Sec = !pThisTrack->IsTime100thSeconds() || !pSharedTrack->IsTime100thSeconds();
    ScChangeAction* pThisAction = pThisTrack->GetFirst();
    ScChangeAction* pSharedAction = pSharedTrack->GetFirst();
    while ( lcl_Equal( pThisAction, pSharedAction, bIgnore100Sec ) )
    {
        pThisAction = pThisAction->GetNext();
        pSharedAction = pSharedAction->GetNext();
    }

    if ( pSharedAction )
    {
        if ( pThisAction )
        {
            // merge own changes into shared document
            sal_uLong nActStartShared = pSharedAction->GetActionNumber();
            sal_uLong nActEndShared = pSharedTrack->GetActionMax();
            ScDocument* pTmpDoc = new ScDocument;
            for ( sal_Int32 nIndex = 0; nIndex < aDocument.GetTableCount(); ++nIndex )
            {
                String sTabName;
                pTmpDoc->CreateValidTabName( sTabName );
                pTmpDoc->InsertTab( SC_TAB_APPEND, sTabName );
            }
            aDocument.GetChangeTrack()->Clone( pTmpDoc );
            ScChangeActionMergeMap aOwnInverseMergeMap;
            pSharedDocShell->MergeDocument( *pTmpDoc, true, true, 0, &aOwnInverseMergeMap, true );
            delete pTmpDoc;
            sal_uLong nActStartOwn = nActEndShared + 1;
            sal_uLong nActEndOwn = pSharedTrack->GetActionMax();

            // find conflicts
            ScConflictsList aConflictsList;
            ScConflictsFinder aFinder( pSharedTrack, nActStartShared, nActEndShared, nActStartOwn, nActEndOwn, aConflictsList );
            if ( aFinder.Find() )
            {
                ScConflictsListHelper::TransformConflictsList( aConflictsList, NULL, &aOwnInverseMergeMap );
                bool bLoop = true;
                while ( bLoop )
                {
                    bLoop = false;
                    ScConflictsDlg aDlg( GetActiveDialogParent(), GetViewData(), &rSharedDoc, aConflictsList );
                    if ( aDlg.Execute() == RET_CANCEL )
                    {
                        QueryBox aBox( GetActiveDialogParent(), WinBits( WB_YES_NO | WB_DEF_YES ),
                            ScGlobal::GetRscString( STR_DOC_WILLNOTBESAVED ) );
                        if ( aBox.Execute() == RET_YES )
                        {
                            return false;
                        }
                        else
                        {
                            bLoop = true;
                        }
                    }
                }
            }

            // undo own changes in shared document
            pSharedTrack->Undo( nActStartOwn, nActEndOwn );

            // clone change track for merging into own document
            pTmpDoc = new ScDocument;
            for ( sal_Int32 nIndex = 0; nIndex < aDocument.GetTableCount(); ++nIndex )
            {
                String sTabName;
                pTmpDoc->CreateValidTabName( sTabName );
                pTmpDoc->InsertTab( SC_TAB_APPEND, sTabName );
            }
            pThisTrack->Clone( pTmpDoc );

            // undo own changes since last save in own document
            sal_uLong nStartShared = pThisAction->GetActionNumber();
            ScChangeAction* pAction = pThisTrack->GetLast();
            while ( pAction && pAction->GetActionNumber() >= nStartShared )
            {
                pThisTrack->Reject( pAction, true );
                pAction = pAction->GetPrev();
            }

            // #i94841# [Collaboration] When deleting rows is rejected, the content is sometimes wrong
            pThisTrack->Undo( nStartShared, pThisTrack->GetActionMax(), true );

            // merge shared changes into own document
            ScChangeActionMergeMap aSharedMergeMap;
            MergeDocument( rSharedDoc, true, true, 0, &aSharedMergeMap );
            sal_uLong nEndShared = pThisTrack->GetActionMax();

            // resolve conflicts for shared non-content actions
            if ( !aConflictsList.empty() )
            {
                ScConflictsListHelper::TransformConflictsList( aConflictsList, &aSharedMergeMap, NULL );
                ScConflictsResolver aResolver( pThisTrack, aConflictsList );
                pAction = pThisTrack->GetAction( nEndShared );
                while ( pAction && pAction->GetActionNumber() >= nStartShared )
                {
                    aResolver.HandleAction( pAction, true /*bIsSharedAction*/,
                        false /*bHandleContentAction*/, true /*bHandleNonContentAction*/ );
                    pAction = pAction->GetPrev();
                }
            }
            nEndShared = pThisTrack->GetActionMax();

            // only show changes from shared document
            aChangeViewSet.SetShowChanges( sal_True );
            aChangeViewSet.SetShowAccepted( sal_True );
            aChangeViewSet.SetHasActionRange( true );
            aChangeViewSet.SetTheActionRange( nStartShared, nEndShared );
            aDocument.SetChangeViewSettings( aChangeViewSet );

            // merge own changes back into own document
            sal_uLong nStartOwn = nEndShared + 1;
            ScChangeActionMergeMap aOwnMergeMap;
            MergeDocument( *pTmpDoc, true, true, nEndShared - nStartShared + 1, &aOwnMergeMap );
            delete pTmpDoc;
            sal_uLong nEndOwn = pThisTrack->GetActionMax();

            // resolve conflicts for shared content actions and own actions
            if ( !aConflictsList.empty() )
            {
                ScConflictsListHelper::TransformConflictsList( aConflictsList, NULL, &aOwnMergeMap );
                ScConflictsResolver aResolver( pThisTrack, aConflictsList );
                pAction = pThisTrack->GetAction( nEndShared );
                while ( pAction && pAction->GetActionNumber() >= nStartShared )
                {
                    aResolver.HandleAction( pAction, true /*bIsSharedAction*/,
                        true /*bHandleContentAction*/, false /*bHandleNonContentAction*/ );
                    pAction = pAction->GetPrev();
                }

                pAction = pThisTrack->GetAction( nEndOwn );
                while ( pAction && pAction->GetActionNumber() >= nStartOwn )
                {
                    aResolver.HandleAction( pAction, false /*bIsSharedAction*/,
                        true /*bHandleContentAction*/, true /*bHandleNonContentAction*/ );
                    pAction = pAction->GetPrev();
                }
            }
            nEndOwn = pThisTrack->GetActionMax();
        }
        else
        {
            // merge shared changes into own document
            sal_uLong nStartShared = pThisTrack->GetActionMax() + 1;
            MergeDocument( rSharedDoc, true, true );
            sal_uLong nEndShared = pThisTrack->GetActionMax();

            // only show changes from shared document
            aChangeViewSet.SetShowChanges( sal_True );
            aChangeViewSet.SetShowAccepted( sal_True );
            aChangeViewSet.SetHasActionRange( true );
            aChangeViewSet.SetTheActionRange( nStartShared, nEndShared );
            aDocument.SetChangeViewSettings( aChangeViewSet );
        }

        // update view
        PostPaintExtras();
        PostPaintGridAll();

        InfoBox aInfoBox( GetActiveDialogParent(), ScGlobal::GetRscString( STR_DOC_UPDATED ) );
        aInfoBox.Execute();
    }

#if DEBUG_CHANGETRACK
    aMessage = ::rtl::OUString::createFromAscii( "\nafter merge:\n" );
    aMessage += pThisTrack->ToString();
    aMsg = ::rtl::OUStringToOString( aMessage, RTL_TEXTENCODING_UTF8 );
    OSL_ENSURE( false, aMsg.getStr() );
    //fprintf( stdout, "%s ", aMsg.getStr() );
    //fflush( stdout );
#endif // DEBUG_CHANGETRACK

    return ( pThisAction != NULL );
}
