/*************************************************************************
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 * 
 * Copyright 2000, 2010 Oracle and/or its affiliates.
 *
 * OpenOffice.org - a multi-platform office productivity suite
 *
 * This file is part of OpenOffice.org.
 *
 * OpenOffice.org is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * OpenOffice.org is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License version 3 for more details
 * (a copy is included in the LICENSE file that accompanied this code).
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with OpenOffice.org.  If not, see
 * <http://www.openoffice.org/license.html>
 * for a copy of the LGPLv3 License.
 *
 ************************************************************************/

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

// System - Includes -----------------------------------------------------

class StarBASIC;



#ifndef PCH
#include "sc.hrc"
#define GLOBALOVERFLOW
#endif

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

#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <osl/endian.h>
#include <i18npool/mslangid.hxx>
#include <tools/list.hxx>
#include <tools/string.hxx>
#include <rtl/math.hxx>
#include <svtools/htmlout.hxx>
#include <svl/zforlist.hxx>
#define _SVSTDARR_ULONGS
#include <svl/svstdarr.hxx>
#include <sot/formats.hxx>
#include <sfx2/mieclip.hxx>
#include <unotools/charclass.hxx>
#include <unotools/collatorwrapper.hxx>
#include <unotools/calendarwrapper.hxx>
#include <com/sun/star/i18n/CalendarFieldIndex.hpp>
#include <unotools/transliterationwrapper.hxx>

#include "global.hxx"
#include "scerrors.hxx"
#include "docsh.hxx"
#include "undoblk.hxx"
#include "rangenam.hxx"
#include "viewdata.hxx"
#include "tabvwsh.hxx"
#include "filter.hxx"
#include "asciiopt.hxx"
#include "cell.hxx"
#include "docoptio.hxx"
#include "progress.hxx"
#include "scitems.hxx"
#include "editable.hxx"
#include "compiler.hxx"
#include "warnbox.hxx"

#include "impex.hxx"

// ause
#include "editutil.hxx"

#include "globstr.hrc"
#include <vcl/msgbox.hxx>
#include <vcl/svapp.hxx>
#include <osl/module.hxx>

//========================================================================

namespace
{
    const String SYLK_LF = String::CreateFromAscii("\x1b :");
    const String DOUBLE_SEMICOLON = String::CreateFromAscii(";;");
    const String DOUBLE_DOUBLEQUOTE = String::CreateFromAscii("\"\"");
}

enum SylkVersion
{
    SYLK_SCALC3,    // Wrote wrongly quoted strings and unescaped semicolons.
    SYLK_OOO32,     // Correct strings, plus multiline content.
    SYLK_OWN,       // Place our new versions, if any, before this value.
    SYLK_OTHER      // Assume that aliens wrote correct strings.
};


// Gesamtdokument ohne Undo


ScImportExport::ScImportExport( ScDocument* p )
    : pDocSh( PTR_CAST(ScDocShell,p->GetDocumentShell()) ), pDoc( p ),
      nSizeLimit( 0 ), cSep( '\t' ), cStr( '"' ), 
      bFormulas( sal_False ), bIncludeFiltered( sal_True ),
      bAll( sal_True ), bSingle( sal_True ), bUndo( sal_False ),
      bOverflow( sal_False ), mbApi( true ), mExportTextOptions()
{
	pUndoDoc = NULL;
	pExtOptions = NULL;
}

// Insert am Punkt ohne Bereichschecks


ScImportExport::ScImportExport( ScDocument* p, const ScAddress& rPt )
    : pDocSh( PTR_CAST(ScDocShell,p->GetDocumentShell()) ), pDoc( p ),
	  aRange( rPt ),
      nSizeLimit( 0 ), cSep( '\t' ), cStr( '"' ), 
      bFormulas( sal_False ), bIncludeFiltered( sal_True ),
      bAll( sal_False ), bSingle( sal_True ), bUndo( sal_Bool( pDocSh != NULL ) ),
      bOverflow( sal_False ), mbApi( true ), mExportTextOptions()
{
	pUndoDoc = NULL;
	pExtOptions = NULL;
}


//	ctor with a range is only used for export
//!	ctor with a string (and bSingle=sal_True) is also used for DdeSetData

ScImportExport::ScImportExport( ScDocument* p, const ScRange& r )
    : pDocSh( PTR_CAST(ScDocShell,p->GetDocumentShell()) ), pDoc( p ),
	  aRange( r ),
      nSizeLimit( 0 ), cSep( '\t' ), cStr( '"' ),
      bFormulas( sal_False ), bIncludeFiltered( sal_True ),
      bAll( sal_False ), bSingle( sal_False ), bUndo( sal_Bool( pDocSh != NULL ) ),
      bOverflow( sal_False ), mbApi( true ), mExportTextOptions()
{
	pUndoDoc = NULL;
	pExtOptions = NULL;
	// Zur Zeit nur in einer Tabelle!
	aRange.aEnd.SetTab( aRange.aStart.Tab() );
}

// String auswerten: Entweder Bereich, Punkt oder Gesamtdoc (bei Fehler)
// Falls eine View existiert, wird die TabNo der View entnommen!


ScImportExport::ScImportExport( ScDocument* p, const String& rPos )
    : pDocSh( PTR_CAST(ScDocShell,p->GetDocumentShell()) ), pDoc( p ),
      nSizeLimit( 0 ), cSep( '\t' ), cStr( '"' ),
      bFormulas( sal_False ), bIncludeFiltered( sal_True ),
      bAll( sal_False ), bSingle( sal_True ), bUndo( sal_Bool( pDocSh != NULL ) ),
      bOverflow( sal_False ), mbApi( true ), mExportTextOptions()
{
	pUndoDoc = NULL;
	pExtOptions = NULL;

	SCTAB nTab = ScDocShell::GetCurTab();
	aRange.aStart.SetTab( nTab );
	String aPos( rPos );
	//  Benannter Bereich?
	ScRangeName* pRange = pDoc->GetRangeName();
	if( pRange )
	{
		sal_uInt16 nPos;
		if( pRange->SearchName( aPos, nPos ) )
		{
			ScRangeData* pData = (*pRange)[ nPos ];
			if( pData->HasType( RT_REFAREA )
				|| pData->HasType( RT_ABSAREA )
				|| pData->HasType( RT_ABSPOS ) )
				pData->GetSymbol( aPos );					// mit dem Inhalt weitertesten
		}
	}
    formula::FormulaGrammar::AddressConvention eConv = pDoc->GetAddressConvention();
	// Bereich?
	if( aRange.Parse( aPos, pDoc, eConv ) & SCA_VALID )
		bSingle = sal_False;
	// Zelle?
	else if( aRange.aStart.Parse( aPos, pDoc, eConv ) & SCA_VALID )
		aRange.aEnd = aRange.aStart;
	else
		bAll = sal_True;
}


ScImportExport::~ScImportExport()
{
	delete pUndoDoc;
	delete pExtOptions;
}


void ScImportExport::SetExtOptions( const ScAsciiOptions& rOpt )
{
	if ( pExtOptions )
		*pExtOptions = rOpt;
	else
		pExtOptions = new ScAsciiOptions( rOpt );

	//	"normale" Optionen uebernehmen

	cSep = rOpt.GetFieldSeps().GetChar(0);
	cStr = rOpt.GetTextSep();
}


sal_Bool ScImportExport::IsFormatSupported( sal_uLong nFormat )
{
	return sal_Bool( nFormat == FORMAT_STRING
			  || nFormat == SOT_FORMATSTR_ID_SYLK
			  || nFormat == SOT_FORMATSTR_ID_LINK
			  || nFormat == SOT_FORMATSTR_ID_HTML
			  || nFormat == SOT_FORMATSTR_ID_HTML_SIMPLE
			  || nFormat == SOT_FORMATSTR_ID_DIF );
}


//////////////////////////////////////////////////////////////////////////////

// Vorbereitung fuer Undo: Undo-Dokument erzeugen


sal_Bool ScImportExport::StartPaste()
{
	if ( !bAll )
	{
		ScEditableTester aTester( pDoc, aRange );
		if ( !aTester.IsEditable() )
		{
			InfoBox aInfoBox(Application::GetDefDialogParent(),
								ScGlobal::GetRscString( aTester.GetMessageId() ) );
			aInfoBox.Execute();
			return sal_False;
		}
	}
	if( bUndo && pDocSh && pDoc->IsUndoEnabled())
	{
		pUndoDoc = new ScDocument( SCDOCMODE_UNDO );
		pUndoDoc->InitUndo( pDoc, aRange.aStart.Tab(), aRange.aEnd.Tab() );
        pDoc->CopyToDocument( aRange, IDF_ALL | IDF_NOCAPTIONS, sal_False, pUndoDoc );
	}
	return sal_True;
}

// Nachbereitung Insert: Undo/Redo-Aktionen erzeugen, Invalidate/Repaint


void ScImportExport::EndPaste()
{
	sal_Bool bHeight = pDocSh && pDocSh->AdjustRowHeight(
					aRange.aStart.Row(), aRange.aEnd.Row(), aRange.aStart.Tab() );

	if( pUndoDoc && pDoc->IsUndoEnabled() )
	{
		ScDocument* pRedoDoc = new ScDocument( SCDOCMODE_UNDO );
		pRedoDoc->InitUndo( pDoc, aRange.aStart.Tab(), aRange.aEnd.Tab() );
        pDoc->CopyToDocument( aRange, IDF_ALL | IDF_NOCAPTIONS, sal_False, pRedoDoc );
		ScMarkData aDestMark;
		aDestMark.SelectOneTable( aRange.aStart.Tab() );
		pDocSh->GetUndoManager()->AddUndoAction(
			new ScUndoPaste( pDocSh,
				aRange.aStart.Col(), aRange.aStart.Row(), aRange.aStart.Tab(),
				aRange.aEnd.Col(), aRange.aEnd.Row(), aRange.aEnd.Tab(), aDestMark,
                pUndoDoc, pRedoDoc, IDF_ALL, NULL,NULL,NULL,NULL ) );
	}
	pUndoDoc = NULL;
	if( pDocSh )
	{
		if (!bHeight)
			pDocSh->PostPaint( aRange, PAINT_GRID );	// AdjustRowHeight paintet evtl. selber
		pDocSh->SetDocumentModified();
	}
	ScTabViewShell* pViewSh = ScTabViewShell::GetActiveViewShell();
	if ( pViewSh )
		pViewSh->UpdateInputHandler();

}

/////////////////////////////////////////////////////////////////////////////


#if 0
sal_Bool ScImportExport::ImportData( SvData& rData )
{
	sal_uLong nFmt = rData.GetFormat();
	if ( nFmt == SOT_FORMATSTR_ID_HTML_SIMPLE )
	{
		MSE40HTMLClipFormatObj aMSE40ClpObj;
		if ( aMSE40ClpObj.GetData( rData ) )
		{
			SvStream* pStream = aMSE40ClpObj.GetStream();
			return ImportStream( *pStream, nFmt );
		}
		return sal_False;
	}
	else
	{
		void* pMem;
		sal_uLong nSize = rData.GetMinMemorySize();
		rData.GetData( &pMem, TRANSFER_REFERENCE );
		if( nFmt == FORMAT_STRING
	 			|| nFmt == FORMAT_RTF
	 			|| nFmt == SOT_FORMATSTR_ID_SYLK
	 			|| nFmt == SOT_FORMATSTR_ID_HTML
	 			|| nFmt == SOT_FORMATSTR_ID_DIF )
		{
			//!	String? Unicode??

			// Stringende ermitteln!
			sal_Char* pBegin = (sal_Char*) pMem;
			sal_Char* pEnd   = (sal_Char*) pMem + nSize;

			nSize = 0;
			while( pBegin != pEnd && *pBegin != '\0' )
				pBegin++, nSize++;
			// #72909# MT says only STRING has to be zero-terminated
			DBG_ASSERT( pBegin != pEnd || nFmt != FORMAT_STRING, "non zero-terminated String" )
		}
		SvMemoryStream aStrm( pMem, nSize, STREAM_READ );
		return ImportStream( aStrm, nFmt );
	}
}

#endif

sal_Bool ScImportExport::ImportData( const String& /* rMimeType */,
                     const ::com::sun::star::uno::Any & /* rValue */ )
{
	DBG_ASSERT( !this, "Implementation is missing" );
	return sal_False;
}

sal_Bool ScImportExport::ExportData( const String& rMimeType,
								 ::com::sun::star::uno::Any & rValue )
{
	SvMemoryStream aStrm;
    // mba: no BaseURL for data exchange
    if( ExportStream( aStrm, String(),
				SotExchange::GetFormatIdFromMimeType( rMimeType ) ))
	{
		aStrm << (sal_uInt8) 0;
		rValue <<= ::com::sun::star::uno::Sequence< sal_Int8 >(
										(sal_Int8*)aStrm.GetData(),
										aStrm.Seek( STREAM_SEEK_TO_END ) );
		return sal_True;
	}
	return sal_False;
}


sal_Bool ScImportExport::ImportString( const ::rtl::OUString& rText, sal_uLong nFmt )
{
	switch ( nFmt )
	{
		// formats supporting unicode
		case FORMAT_STRING :
		{
            ScImportStringStream aStrm( rText);
            return ImportStream( aStrm, String(), nFmt );
			// ImportStream must handle RTL_TEXTENCODING_UNICODE
		}
        //break;
		default:
		{
			rtl_TextEncoding eEnc = gsl_getSystemTextEncoding();
            ::rtl::OString aTmp( rText.getStr(), rText.getLength(), eEnc );
            SvMemoryStream aStrm( (void*)aTmp.getStr(), aTmp.getLength() * sizeof(sal_Char), STREAM_READ );
			aStrm.SetStreamCharSet( eEnc );
			SetNoEndianSwap( aStrm );		//! no swapping in memory
            return ImportStream( aStrm, String(), nFmt );
		}
	}
}


sal_Bool ScImportExport::ExportString( ::rtl::OUString& rText, sal_uLong nFmt )
{
	DBG_ASSERT( nFmt == FORMAT_STRING, "ScImportExport::ExportString: Unicode not supported for other formats than FORMAT_STRING" );
	if ( nFmt != FORMAT_STRING )
	{
		rtl_TextEncoding eEnc = gsl_getSystemTextEncoding();
		ByteString aTmp;
		sal_Bool bOk = ExportByteString( aTmp, eEnc, nFmt );
		rText = UniString( aTmp, eEnc );
		return bOk;
	}
	//	nSizeLimit not needed for OUString

	SvMemoryStream aStrm;
	aStrm.SetStreamCharSet( RTL_TEXTENCODING_UNICODE );
	SetNoEndianSwap( aStrm );		//! no swapping in memory
    // mba: no BaseURL for data exc
    if( ExportStream( aStrm, String(), nFmt ) )
	{
		aStrm << (sal_Unicode) 0;
		aStrm.Seek( STREAM_SEEK_TO_END );

		rText = rtl::OUString( (const sal_Unicode*) aStrm.GetData() );
		return sal_True;
	}
	rText = rtl::OUString();
	return sal_False;

	// ExportStream must handle RTL_TEXTENCODING_UNICODE
}


sal_Bool ScImportExport::ExportByteString( ByteString& rText, rtl_TextEncoding eEnc, sal_uLong nFmt )
{
	DBG_ASSERT( eEnc != RTL_TEXTENCODING_UNICODE, "ScImportExport::ExportByteString: Unicode not supported" );
	if ( eEnc == RTL_TEXTENCODING_UNICODE )
		eEnc = gsl_getSystemTextEncoding();

	if (!nSizeLimit)
		nSizeLimit = STRING_MAXLEN;

	SvMemoryStream aStrm;
	aStrm.SetStreamCharSet( eEnc );
	SetNoEndianSwap( aStrm );		//! no swapping in memory
    // mba: no BaseURL for data exchange
    if( ExportStream( aStrm, String(), nFmt ) )
	{
		aStrm << (sal_Char) 0;
		aStrm.Seek( STREAM_SEEK_TO_END );
		// Sicherheits-Check:
		if( aStrm.Tell() <= (sal_uLong) STRING_MAXLEN )
		{
			rText = (const sal_Char*) aStrm.GetData();
			return sal_True;
		}
	}
	rText.Erase();
	return sal_False;
}


sal_Bool ScImportExport::ImportStream( SvStream& rStrm, const String& rBaseURL, sal_uLong nFmt )
{
	if( nFmt == FORMAT_STRING )
	{
		if( ExtText2Doc( rStrm ) )		// pExtOptions auswerten
			return sal_True;
	}
	if( nFmt == SOT_FORMATSTR_ID_SYLK )
	{
		if( Sylk2Doc( rStrm ) )
			return sal_True;
	}
	if( nFmt == SOT_FORMATSTR_ID_DIF )
	{
		if( Dif2Doc( rStrm ) )
			return sal_True;
	}
	if( nFmt == FORMAT_RTF )
	{
        if( RTF2Doc( rStrm, rBaseURL ) )
			return sal_True;
	}
	if( nFmt == SOT_FORMATSTR_ID_LINK )
		return sal_True;			// Link-Import?
	if ( nFmt == SOT_FORMATSTR_ID_HTML )
	{
        if( HTML2Doc( rStrm, rBaseURL ) )
			return sal_True;
	}
	if ( nFmt == SOT_FORMATSTR_ID_HTML_SIMPLE )
	{
		MSE40HTMLClipFormatObj aMSE40ClpObj;				// needed to skip the header data
		SvStream* pHTML = aMSE40ClpObj.IsValid( rStrm );
        if ( pHTML && HTML2Doc( *pHTML, rBaseURL ) )
			return sal_True;
	}

	return sal_False;
}


sal_Bool ScImportExport::ExportStream( SvStream& rStrm, const String& rBaseURL, sal_uLong nFmt )
{
	if( nFmt == FORMAT_STRING )
	{
		if( Doc2Text( rStrm ) )
			return sal_True;
	}
	if( nFmt == SOT_FORMATSTR_ID_SYLK )
	{
		if( Doc2Sylk( rStrm ) )
			return sal_True;
	}
	if( nFmt == SOT_FORMATSTR_ID_DIF )
	{
		if( Doc2Dif( rStrm ) )
			return sal_True;
	}
	if( nFmt == SOT_FORMATSTR_ID_LINK && !bAll )
	{
		String aDocName;
		if ( pDoc->IsClipboard() )
			aDocName = ScGlobal::GetClipDocName();
		else
		{
			SfxObjectShell* pShell = pDoc->GetDocumentShell();
			if (pShell)
				aDocName = pShell->GetTitle( SFX_TITLE_FULLNAME );
		}

		DBG_ASSERT( aDocName.Len(), "ClipBoard document has no name! :-/" );
		if( aDocName.Len() )
		{
			String aRefName;
			sal_uInt16 nFlags = SCA_VALID | SCA_TAB_3D;
			if( bSingle )
				aRange.aStart.Format( aRefName, nFlags, pDoc, pDoc->GetAddressConvention() );
			else
			{
				if( aRange.aStart.Tab() != aRange.aEnd.Tab() )
					nFlags |= SCA_TAB2_3D;
				aRange.Format( aRefName, nFlags, pDoc );
			}
			String aAppName = Application::GetAppName();

			WriteUnicodeOrByteString( rStrm, aAppName, sal_True );
			WriteUnicodeOrByteString( rStrm, aDocName, sal_True );
			WriteUnicodeOrByteString( rStrm, aRefName, sal_True );
			if ( rStrm.GetStreamCharSet() == RTL_TEXTENCODING_UNICODE )
				rStrm << sal_Unicode(0);
			else
				rStrm << sal_Char(0);
			return sal_Bool( rStrm.GetError() == SVSTREAM_OK );
		}
	}
	if( nFmt == SOT_FORMATSTR_ID_HTML )
	{
        if( Doc2HTML( rStrm, rBaseURL ) )
			return sal_True;
	}
	if( nFmt == FORMAT_RTF )
	{
		if( Doc2RTF( rStrm ) )
			return sal_True;
	}

	return sal_False;
}


//static
void ScImportExport::WriteUnicodeOrByteString( SvStream& rStrm, const String& rString, sal_Bool bZero )
{
	rtl_TextEncoding eEnc = rStrm.GetStreamCharSet();
	if ( eEnc == RTL_TEXTENCODING_UNICODE )
	{
		if ( !IsEndianSwap( rStrm ) )
			rStrm.Write( rString.GetBuffer(), rString.Len() * sizeof(sal_Unicode) );
		else
		{
			const sal_Unicode* p = rString.GetBuffer();
			const sal_Unicode* const pStop = p + rString.Len();
			while ( p < pStop )
			{
				rStrm << *p;
			}
		}
		if ( bZero )
			rStrm << sal_Unicode(0);
	}
	else
	{
		ByteString aByteStr( rString, eEnc );
		rStrm << aByteStr.GetBuffer();
		if ( bZero )
			rStrm << sal_Char(0);
	}
}


// This function could be replaced by endlub()
// static
void ScImportExport::WriteUnicodeOrByteEndl( SvStream& rStrm )
{
	if ( rStrm.GetStreamCharSet() == RTL_TEXTENCODING_UNICODE )
	{	// same as endl() but unicode
		switch ( rStrm.GetLineDelimiter() )
		{
			case LINEEND_CR :
				rStrm << sal_Unicode(_CR);
			break;
			case LINEEND_LF :
				rStrm << sal_Unicode(_LF);
			break;
			default:
				rStrm << sal_Unicode(_CR) << sal_Unicode(_LF);
		}
	}
	else
		endl( rStrm );
}


enum DoubledQuoteMode
{
	DQM_KEEP,		// both are taken
	DQM_ESCAPE,		// escaped quote, one is taken, one ignored
	DQM_CONCAT,		// first is end, next is start, both ignored => strings combined
	DQM_SEPARATE	// end one string and begin next
};

static const sal_Unicode* lcl_ScanString( const sal_Unicode* p, String& rString,
			sal_Unicode cStr, DoubledQuoteMode eMode )
{
	p++;	//! jump over opening quote
	sal_Bool bCont;
	do
	{
		bCont = sal_False;
		const sal_Unicode* p0 = p;
		for( ;; )
		{
			if( !*p )
				break;
			if( *p == cStr )
			{
				if ( *++p != cStr )
					break;
				// doubled quote char
				switch ( eMode )
				{
					case DQM_KEEP :
						p++;			// both for us (not breaking for-loop)
					break;
					case DQM_ESCAPE :
						p++;			// one for us (breaking for-loop)
						bCont = sal_True;	// and more
					break;
					case DQM_CONCAT :
						if ( p0+1 < p )
                            rString.Append( p0, sal::static_int_cast<xub_StrLen>( (p-1) - p0 ) );  // first part
						p0 = ++p;		// text of next part starts here
					break;
					case DQM_SEPARATE :
										// positioned on next opening quote
					break;
				}
				if ( eMode == DQM_ESCAPE || eMode == DQM_SEPARATE )
					break;
			}
			else
				p++;
		}
		if ( p0 < p )
            rString.Append( p0, sal::static_int_cast<xub_StrLen>( ((*p || *(p-1) == cStr) ? p-1 : p) - p0 ) );
	} while ( bCont );
	return p;
}

void lcl_UnescapeSylk( String & rString, SylkVersion eVersion )
{
    // Older versions didn't escape the semicolon.
    // Older versions quoted the string and doubled embedded quotes, but not 
    // the semicolons, which was plain wrong.
    if (eVersion >= SYLK_OOO32)
        rString.SearchAndReplaceAll( DOUBLE_SEMICOLON, ';' );
    else
        rString.SearchAndReplaceAll( DOUBLE_DOUBLEQUOTE, '"' );

    rString.SearchAndReplaceAll( SYLK_LF, _LF );
}

static const sal_Unicode* lcl_ScanSylkString( const sal_Unicode* p,
        String& rString, SylkVersion eVersion )
{
    const sal_Unicode* pStartQuote = p;
    const sal_Unicode* pEndQuote = 0;
    while( *(++p) )
    {
        if( *p == '"' )
        {
            pEndQuote = p;
            if (eVersion >= SYLK_OOO32)
            {
                if (*(p+1) == ';')
                {
                    if (*(p+2) == ';')
                    {
                        p += 2;     // escaped ';'
                        pEndQuote = 0;
                    }
                    else
                        break;      // end field
                }
            }
            else
            {
                if (*(p+1) == '"')
                {
                    ++p;            // escaped '"'
                    pEndQuote = 0;
                }
                else if (*(p+1) == ';')
                    break;          // end field
            }
        }
    }
    if (!pEndQuote)
        pEndQuote = p;  // Take all data as string.
    rString.Append( pStartQuote + 1, sal::static_int_cast<xub_StrLen>( pEndQuote - pStartQuote - 1 ) );
    lcl_UnescapeSylk( rString, eVersion);
    return p;
}

static const sal_Unicode* lcl_ScanSylkFormula( const sal_Unicode* p,
        String& rString, SylkVersion eVersion )
{
    const sal_Unicode* pStart = p;
    if (eVersion >= SYLK_OOO32)
    {
        while (*p)
        {
            if (*p == ';')
            {
                if (*(p+1) == ';')
                    ++p;        // escaped ';'
                else
                    break;      // end field
            }
            ++p;
        }
        rString.Append( pStart, sal::static_int_cast<xub_StrLen>( p - pStart));
        lcl_UnescapeSylk( rString, eVersion);
    }
    else
    {
        // Nasty. If in old versions the formula contained a semicolon, it was 
        // quoted and embedded quotes were doubled, but semicolons were not. If 
        // there was no semicolon, it could still contain quotes and doubled 
        // embedded quotes if it was something like ="a""b", which was saved as 
        // E"a""b" as is and has to be preserved, even if older versions 
        // couldn't even load it correctly. However, theoretically another 
        // field might follow and thus the line contain a semicolon again, such 
        // as ...;E"a""b";...
        bool bQuoted = false;
        if (*p == '"')
        {
            // May be a quoted expression or just a string constant expression 
            // with quotes.
            while (*(++p))
            {
                if (*p == '"')
                {
                    if (*(p+1) == '"')
                        ++p;            // escaped '"'
                    else
                        break;          // closing '"', had no ';' yet
                }
                else if (*p == ';')
                {
                    bQuoted = true;     // ';' within quoted expression
                    break;
                }
            }
            p = pStart;
        }
        if (bQuoted)
            p = lcl_ScanSylkString( p, rString, eVersion);
        else
        {
            while (*p && *p != ';')
                ++p;
            rString.Append( pStart, sal::static_int_cast<xub_StrLen>( p - pStart));
        }
    }
    return p;
}

static void lcl_DoubleEscapeChar( String& rString, sal_Unicode cStr )
{
	xub_StrLen n = 0;
	while( ( n = rString.Search( cStr, n ) ) != STRING_NOTFOUND )
	{
		rString.Insert( cStr, n );
		n += 2;
	}
}

static void lcl_WriteString( SvStream& rStrm, String& rString, sal_Unicode cQuote, sal_Unicode cEsc )
{
    if (cEsc)
        lcl_DoubleEscapeChar( rString, cEsc );

    if (cQuote)
    {
        rString.Insert( cQuote, 0 );
        rString.Append( cQuote );
    }

	ScImportExport::WriteUnicodeOrByteString( rStrm, rString );
}

inline void lcl_WriteSimpleString( SvStream& rStrm, const String& rString )
{
	ScImportExport::WriteUnicodeOrByteString( rStrm, rString );
}

//////////////////////////////////////////////////////////////////////////////


sal_Bool ScImportExport::Text2Doc( SvStream& rStrm )
{
	sal_Bool bOk = sal_True;

	SCCOL nStartCol = aRange.aStart.Col();
	SCROW nStartRow = aRange.aStart.Row();
	SCCOL nEndCol = aRange.aEnd.Col();
	SCROW nEndRow = aRange.aEnd.Row();
	sal_uLong  nOldPos = rStrm.Tell();
    rStrm.StartReadingUnicodeText( rStrm.GetStreamCharSet() );
	sal_Bool   bData = sal_Bool( !bSingle );
	if( !bSingle)
		bOk = StartPaste();

	while( bOk )
	{
		ByteString aByteLine;
		String aLine, aCell;
		SCROW nRow = nStartRow;
		rStrm.Seek( nOldPos );
		for( ;; )
		{
			rStrm.ReadUniOrByteStringLine( aLine );
			if( rStrm.IsEof() )
				break;
			SCCOL nCol = nStartCol;
			const sal_Unicode* p = aLine.GetBuffer();
			while( *p )
			{
				aCell.Erase();
				if( *p == cStr )
				{
					p = lcl_ScanString( p, aCell, cStr, DQM_KEEP );
					while( *p && *p != cSep )
						p++;
					if( *p )
						p++;
				}
				else
				{
					const sal_Unicode* q = p;
					while( *p && *p != cSep )
						p++;
                    aCell.Assign( q, sal::static_int_cast<xub_StrLen>( p - q ) );
					if( *p )
						p++;
				}
				if (ValidCol(nCol) && ValidRow(nRow) )
				{
					if( bSingle )
					{
						if (nCol>nEndCol) nEndCol = nCol;
						if (nRow>nEndRow) nEndRow = nRow;
					}
					if( bData && nCol <= nEndCol && nRow <= nEndRow )
						pDoc->SetString( nCol, nRow, aRange.aStart.Tab(), aCell );
				}
				else							// zuviele Spalten/Zeilen
					bOverflow = sal_True;			// beim Import Warnung ausgeben
				++nCol;
			}
			++nRow;
		}

		if( !bData )
		{
			aRange.aEnd.SetCol( nEndCol );
			aRange.aEnd.SetRow( nEndRow );
			bOk = StartPaste();
			bData = sal_True;
		}
		else
			break;
	}

	EndPaste();
	return bOk;
}

		//
		//	erweiterter Ascii-Import
		//


static bool lcl_PutString( 
    ScDocument* pDoc, SCCOL nCol, SCROW nRow, SCTAB nTab, const String& rStr, sal_uInt8 nColFormat,
    SvNumberFormatter* pFormatter, bool bDetectNumFormat,
    ::utl::TransliterationWrapper& rTransliteration, CalendarWrapper& rCalendar,
    ::utl::TransliterationWrapper* pSecondTransliteration, CalendarWrapper* pSecondCalendar )
{
    bool bMultiLine = false;
	if ( nColFormat == SC_COL_SKIP || !rStr.Len() || !ValidCol(nCol) || !ValidRow(nRow) )
		return bMultiLine;

	if ( nColFormat == SC_COL_TEXT )
	{
		pDoc->PutCell( nCol, nRow, nTab, ScBaseCell::CreateTextCell( rStr, pDoc ) );
		return bMultiLine;
	}

	if ( nColFormat == SC_COL_ENGLISH )
	{
		//!	SetString mit Extra-Flag ???

        SvNumberFormatter* pDocFormatter = pDoc->GetFormatTable();
        sal_uInt32 nEnglish = pDocFormatter->GetStandardIndex(LANGUAGE_ENGLISH_US);
		double fVal;
        if ( pDocFormatter->IsNumberFormat( rStr, nEnglish, fVal ) )
		{
			//	Zahlformat wird nicht auf englisch gesetzt
			pDoc->SetValue( nCol, nRow, nTab, fVal );
			return bMultiLine;
		}
		//	sonst weiter mit SetString
	}
	else if ( nColFormat != SC_COL_STANDARD )					// Datumsformate
	{
        const sal_uInt16 nMaxNumberParts = 7;   // Y-M-D h:m:s.t
		xub_StrLen nLen = rStr.Len();
		xub_StrLen nStart[nMaxNumberParts];
		xub_StrLen nEnd[nMaxNumberParts];

        sal_uInt16 nDP, nMP, nYP;
        switch ( nColFormat )
        {
            case SC_COL_YMD: nDP = 2; nMP = 1; nYP = 0; break;
            case SC_COL_MDY: nDP = 1; nMP = 0; nYP = 2; break;
            case SC_COL_DMY:
            default:         nDP = 0; nMP = 1; nYP = 2; break;
        }

		sal_uInt16 nFound = 0;
		sal_Bool bInNum = sal_False;
        for ( xub_StrLen nPos=0; nPos<nLen && (bInNum ||
                    nFound<nMaxNumberParts); nPos++ )
		{
            if (bInNum && nFound == 3 && nColFormat == SC_COL_YMD &&
                    nPos <= nStart[nFound]+2 && rStr.GetChar(nPos) == 'T')
                bInNum = sal_False;     // ISO-8601: YYYY-MM-DDThh:mm...
            else if ((((!bInNum && nFound==nMP) || (bInNum && nFound==nMP+1))
                        && ScGlobal::pCharClass->isLetterNumeric( rStr, nPos))
                    || ScGlobal::pCharClass->isDigit( rStr, nPos))
			{
                if (!bInNum)
                {
                    bInNum = sal_True;
                    nStart[nFound] = nPos;
                    ++nFound;
                }
                nEnd[nFound-1] = nPos;
			}
			else
				bInNum = sal_False;
		}

		if ( nFound == 1 )
		{
			//	try to break one number (without separators) into date fields

			xub_StrLen nDateStart = nStart[0];
			xub_StrLen nDateLen = nEnd[0] + 1 - nDateStart;

			if ( nDateLen >= 5 && nDateLen <= 8 &&
					ScGlobal::pCharClass->isNumeric( rStr.Copy( nDateStart, nDateLen ) ) )
			{
				//	6 digits: 2 each for day, month, year
				//	8 digits: 4 for year, 2 each for day and month
				//	5 or 7 digits: first field is shortened by 1

				sal_Bool bLongYear = ( nDateLen >= 7 );
				sal_Bool bShortFirst = ( nDateLen == 5 || nDateLen == 7 );

				sal_uInt16 nFieldStart = nDateStart;
				for (sal_uInt16 nPos=0; nPos<3; nPos++)
				{
					sal_uInt16 nFieldEnd = nFieldStart + 1;		// default: 2 digits
					if ( bLongYear && nPos == nYP )
						nFieldEnd += 2;						// 2 extra digits for long year
					if ( bShortFirst && nPos == 0 )
						--nFieldEnd;						// first field shortened?

					nStart[nPos] = nFieldStart;
					nEnd[nPos]   = nFieldEnd;
					nFieldStart  = nFieldEnd + 1;
				}
				nFound = 3;
			}
		}

		if ( nFound >= 3 )
		{
			using namespace ::com::sun::star;
			sal_Bool bSecondCal = sal_False;
			sal_uInt16 nDay  = (sal_uInt16) rStr.Copy( nStart[nDP], nEnd[nDP]+1-nStart[nDP] ).ToInt32();
			sal_uInt16 nYear = (sal_uInt16) rStr.Copy( nStart[nYP], nEnd[nYP]+1-nStart[nYP] ).ToInt32();
			String aMStr = rStr.Copy( nStart[nMP], nEnd[nMP]+1-nStart[nMP] );
			sal_Int16 nMonth = (sal_Int16) aMStr.ToInt32();
			if (!nMonth)
			{
                static const String aSeptCorrect( RTL_CONSTASCII_USTRINGPARAM( "SEPT" ) );
                static const String aSepShortened( RTL_CONSTASCII_USTRINGPARAM( "SEP" ) );
				uno::Sequence< i18n::CalendarItem > xMonths;
                sal_Int32 i, nMonthCount;
				//	first test all month names from local international
				xMonths = rCalendar.getMonths();
                nMonthCount = xMonths.getLength();
                for (i=0; i<nMonthCount && !nMonth; i++)
				{
                    if ( rTransliteration.isEqual( aMStr, xMonths[i].FullName ) ||
                         rTransliteration.isEqual( aMStr, xMonths[i].AbbrevName ) )
                        nMonth = sal::static_int_cast<sal_Int16>( i+1 );
                    else if ( i == 8 && rTransliteration.isEqual( aSeptCorrect,
                                xMonths[i].AbbrevName ) &&
                            rTransliteration.isEqual( aMStr, aSepShortened ) )
                    {   // #102136# correct English abbreviation is SEPT,
                        // but data mostly contains SEP only
                        nMonth = sal::static_int_cast<sal_Int16>( i+1 );
                    }
				}
				//	if none found, then test english month names
                if ( !nMonth && pSecondCalendar && pSecondTransliteration )
				{
					xMonths = pSecondCalendar->getMonths();
                    nMonthCount = xMonths.getLength();
                    for (i=0; i<nMonthCount && !nMonth; i++)
					{
                        if ( pSecondTransliteration->isEqual( aMStr, xMonths[i].FullName ) ||
                             pSecondTransliteration->isEqual( aMStr, xMonths[i].AbbrevName ) )
						{
                            nMonth = sal::static_int_cast<sal_Int16>( i+1 );
							bSecondCal = sal_True;
						}
                        else if ( i == 8 && pSecondTransliteration->isEqual(
                                    aMStr, aSepShortened ) )
                        {   // #102136# correct English abbreviation is SEPT,
                            // but data mostly contains SEP only
                            nMonth = sal::static_int_cast<sal_Int16>( i+1 );
							bSecondCal = sal_True;
                        }
					}
				}
			}

            SvNumberFormatter* pDocFormatter = pDoc->GetFormatTable();
			if ( nYear < 100 )
                nYear = pDocFormatter->ExpandTwoDigitYear( nYear );

			CalendarWrapper* pCalendar = (bSecondCal ? pSecondCalendar : &rCalendar);
			sal_Int16 nNumMonths = pCalendar->getNumberOfMonthsInYear();
			if ( nDay && nMonth && nDay<=31 && nMonth<=nNumMonths )
			{
				--nMonth;
				pCalendar->setValue( i18n::CalendarFieldIndex::DAY_OF_MONTH, nDay );
				pCalendar->setValue( i18n::CalendarFieldIndex::MONTH, nMonth );
				pCalendar->setValue( i18n::CalendarFieldIndex::YEAR, nYear );
                sal_Int16 nHour, nMinute, nSecond, nMilli;
                // #i14974# The imported value should have no fractional value, so set the
                // time fields to zero (ICU calendar instance defaults to current date/time)
                nHour = nMinute = nSecond = nMilli = 0;
                if (nFound > 3)
                    nHour = (sal_Int16) rStr.Copy( nStart[3], nEnd[3]+1-nStart[3]).ToInt32();
                if (nFound > 4)
                    nMinute = (sal_Int16) rStr.Copy( nStart[4], nEnd[4]+1-nStart[4]).ToInt32();
                if (nFound > 5)
                    nSecond = (sal_Int16) rStr.Copy( nStart[5], nEnd[5]+1-nStart[5]).ToInt32();
                if (nFound > 6)
                {
                    sal_Unicode cDec = '.';
                    rtl::OUString aT( &cDec, 1);
                    aT += rStr.Copy( nStart[6], nEnd[6]+1-nStart[6]);
                    rtl_math_ConversionStatus eStatus;
                    double fV = rtl::math::stringToDouble( aT, cDec, 0, &eStatus, 0);
                    if (eStatus == rtl_math_ConversionStatus_Ok)
                        nMilli = (sal_Int16) (1000.0 * fV + 0.5);
                }
                pCalendar->setValue( i18n::CalendarFieldIndex::HOUR, nHour );
                pCalendar->setValue( i18n::CalendarFieldIndex::MINUTE, nMinute );
                pCalendar->setValue( i18n::CalendarFieldIndex::SECOND, nSecond );
                pCalendar->setValue( i18n::CalendarFieldIndex::MILLISECOND, nMilli );
                if ( pCalendar->isValid() )
                {
                    double fDiff = DateTime(*pDocFormatter->GetNullDate()) -
                        pCalendar->getEpochStart();
                    // #i14974# must use getLocalDateTime to get the same
                    // date values as set above
                    double fDays = pCalendar->getLocalDateTime();
                    fDays -= fDiff;

                    LanguageType eLatin, eCjk, eCtl;
                    pDoc->GetLanguage( eLatin, eCjk, eCtl );
                    LanguageType eDocLang = eLatin;     //! which language for date formats?

                    short nType = (nFound > 3 ? NUMBERFORMAT_DATETIME : NUMBERFORMAT_DATE);
                    sal_uLong nFormat = pDocFormatter->GetStandardFormat( nType, eDocLang );
                    // maybe there is a special format including seconds or milliseconds
                    if (nFound > 5)
                        nFormat = pDocFormatter->GetStandardFormat( fDays, nFormat, nType, eDocLang);

                    pDoc->PutCell( nCol, nRow, nTab, new ScValueCell(fDays), nFormat, sal_False );

                    return bMultiLine;     // success
                }
			}
		}
	}

    // Standard or date not determined -> SetString / EditCell
    if( rStr.Search( _LF ) == STRING_NOTFOUND )
        pDoc->SetString( nCol, nRow, nTab, rStr, pFormatter, bDetectNumFormat );
    else 
    {
        bMultiLine = true;
        pDoc->PutCell( nCol, nRow, nTab, new ScEditCell( rStr, pDoc ) );
    }
    return bMultiLine;
}


String lcl_GetFixed( const String& rLine, xub_StrLen nStart, xub_StrLen nNext, bool& rbIsQuoted )
{
	xub_StrLen nLen = rLine.Len();
	if (nNext > nLen)
		nNext = nLen;
	if ( nNext <= nStart )
		return EMPTY_STRING;

	const sal_Unicode* pStr = rLine.GetBuffer();

	xub_StrLen nSpace = nNext;
	while ( nSpace > nStart && pStr[nSpace-1] == ' ' )
		--nSpace;

    rbIsQuoted = (pStr[nStart] == sal_Unicode('"') && pStr[nSpace-1] == sal_Unicode('"'));
    if (rbIsQuoted)
        return rLine.Copy(nStart+1, nSpace-nStart-2);
    else
        return rLine.Copy(nStart, nSpace-nStart);
}

sal_Bool ScImportExport::ExtText2Doc( SvStream& rStrm )
{
	if (!pExtOptions)
		return Text2Doc( rStrm );

	sal_uLong nOldPos = rStrm.Tell();
	rStrm.Seek( STREAM_SEEK_TO_END );
    ::std::auto_ptr<ScProgress> xProgress( new ScProgress( pDocSh,
            ScGlobal::GetRscString( STR_LOAD_DOC ), rStrm.Tell() - nOldPos ));
	rStrm.Seek( nOldPos );
    rStrm.StartReadingUnicodeText( rStrm.GetStreamCharSet() );

	sal_Bool bOld = ScColumn::bDoubleAlloc;
	ScColumn::bDoubleAlloc = sal_True;

	SCCOL nStartCol = aRange.aStart.Col();
    SCCOL nEndCol = aRange.aEnd.Col();
	SCROW nStartRow = aRange.aStart.Row();
	SCTAB nTab = aRange.aStart.Tab();

	sal_Bool	bFixed			= pExtOptions->IsFixedLen();
	const String& rSeps     = pExtOptions->GetFieldSeps();
	const sal_Unicode* pSeps = rSeps.GetBuffer();
	sal_Bool	bMerge			= pExtOptions->IsMergeSeps();
	sal_uInt16	nInfoCount		= pExtOptions->GetInfoCount();
	const xub_StrLen* pColStart = pExtOptions->GetColStart();
	const sal_uInt8* pColFormat  = pExtOptions->GetColFormat();
	long nSkipLines	= pExtOptions->GetStartRow();

    LanguageType eDocLang = pExtOptions->GetLanguage();
    SvNumberFormatter aNumFormatter(pDoc->GetServiceManager(), eDocLang);
    bool bDetectNumFormat = pExtOptions->IsDetectSpecialNumber();

	// For date recognition
    ::utl::TransliterationWrapper aTransliteration(
        pDoc->GetServiceManager(), SC_TRANSLITERATION_IGNORECASE );
    aTransliteration.loadModuleIfNeeded( eDocLang );
	CalendarWrapper aCalendar( pDoc->GetServiceManager() );
	aCalendar.loadDefaultCalendar(
		MsLangId::convertLanguageToLocale( eDocLang ) );
    ::utl::TransliterationWrapper* pEnglishTransliteration = NULL;
	CalendarWrapper* pEnglishCalendar = NULL;
	if ( eDocLang != LANGUAGE_ENGLISH_US )
	{
        pEnglishTransliteration = new ::utl::TransliterationWrapper (
            pDoc->GetServiceManager(), SC_TRANSLITERATION_IGNORECASE );
        aTransliteration.loadModuleIfNeeded( LANGUAGE_ENGLISH_US );
		pEnglishCalendar = new CalendarWrapper ( pDoc->GetServiceManager() );
		pEnglishCalendar->loadDefaultCalendar(
			MsLangId::convertLanguageToLocale( LANGUAGE_ENGLISH_US ) );
	}

	String aLine, aCell;
	sal_uInt16 i;
	SCROW nRow = nStartRow;

	while(--nSkipLines>0)
	{
		rStrm.ReadCsvLine( aLine, !bFixed, rSeps, cStr); // content is ignored
		if ( rStrm.IsEof() )
			break;
	}

    // Determine range for Undo.
    // TODO: we don't need this during import of a file to a new sheet or
    // document, could set bDetermineRange=false then.
    bool bDetermineRange = true;   

    // Row heights don't need to be adjusted on the fly if EndPaste() is called
    // afterwards, which happens only if bDetermineRange. This variable also
    // survives the toggle of bDetermineRange down at the end of the do{} loop.
    bool bRangeIsDetermined = bDetermineRange;

    bool bQuotedAsText = pExtOptions && pExtOptions->IsQuotedAsText();

    sal_uLong nOriginalStreamPos = rStrm.Tell();
 
    do
    {
        for( ;; )
        {
            rStrm.ReadCsvLine( aLine, !bFixed, rSeps, cStr);
            if ( rStrm.IsEof() )
                break;

            xub_StrLen nLineLen = aLine.Len();
            SCCOL nCol = nStartCol;
            bool bMultiLine = false;
            if ( bFixed )				//	Feste Satzlaenge
            {
                // Yes, the check is nCol<=MAXCOL+1, +1 because it is only an
                // overflow if there is really data following to be put behind
                // the last column, which doesn't happen if info is
                // SC_COL_SKIP.
                for ( i=0; i<nInfoCount && nCol <= MAXCOL+1; i++ )
                {
                    sal_uInt8 nFmt = pColFormat[i];
                    if (nFmt != SC_COL_SKIP)        // sonst auch nCol nicht hochzaehlen
                    {
                        if (nCol > MAXCOL)
                            bOverflow = sal_True;       // display warning on import
                        else if (!bDetermineRange)
                        {
                            xub_StrLen nStart = pColStart[i];
                            xub_StrLen nNext = ( i+1 < nInfoCount ) ? pColStart[i+1] : nLineLen;
                            bool bIsQuoted = false;
                            aCell = lcl_GetFixed( aLine, nStart, nNext, bIsQuoted );
                            if (bIsQuoted && bQuotedAsText)
                                nFmt = SC_COL_TEXT;

                            bMultiLine |= lcl_PutString( 
                                pDoc, nCol, nRow, nTab, aCell, nFmt,
                                &aNumFormatter, bDetectNumFormat, aTransliteration, aCalendar,
                                pEnglishTransliteration, pEnglishCalendar);
                        }
                        ++nCol;
                    }
                }
            }
            else						//	Nach Trennzeichen suchen
            {
                SCCOL nSourceCol = 0;
                sal_uInt16 nInfoStart = 0;
                const sal_Unicode* p = aLine.GetBuffer();
                // Yes, the check is nCol<=MAXCOL+1, +1 because it is only an
                // overflow if there is really data following to be put behind
                // the last column, which doesn't happen if info is
                // SC_COL_SKIP.
                while (*p && nCol <= MAXCOL+1)
                {
                    bool bIsQuoted = false;
                    p = ScImportExport::ScanNextFieldFromString( p, aCell, cStr, pSeps, bMerge, bIsQuoted );

                    sal_uInt8 nFmt = SC_COL_STANDARD;
                    for ( i=nInfoStart; i<nInfoCount; i++ )
                    {
                        if ( pColStart[i] == nSourceCol + 1 )		// pColStart ist 1-basiert
                        {
                            nFmt = pColFormat[i];
                            nInfoStart = i + 1;		// ColInfos sind in Reihenfolge
                            break;	// for
                        }
                    }
                    if ( nFmt != SC_COL_SKIP )
                    {
                        if (nCol > MAXCOL)
                            bOverflow = sal_True;       // display warning on import
                        else if (!bDetermineRange)
                        {
                            if (bIsQuoted && bQuotedAsText)
                                nFmt = SC_COL_TEXT;

                            bMultiLine |= lcl_PutString( 
                                pDoc, nCol, nRow, nTab, aCell, nFmt, 
                                &aNumFormatter, bDetectNumFormat, aTransliteration,
                                aCalendar, pEnglishTransliteration, pEnglishCalendar);
                        }
                        ++nCol;
                    }

                    ++nSourceCol;
                }
            }
            if (nEndCol < nCol)
                nEndCol = nCol;     //! points to the next free or even MAXCOL+2

            if (!bDetermineRange)
            {
                if (bMultiLine && !bRangeIsDetermined && pDocSh)
                    pDocSh->AdjustRowHeight( nRow, nRow, nTab);
                xProgress->SetStateOnPercent( rStrm.Tell() - nOldPos );
            }
            ++nRow;
            if ( nRow > MAXROW )
            {
                bOverflow = sal_True;       // display warning on import
                break;  // for
            }
        }
        // so far nRow/nEndCol pointed to the next free
        if (nRow > nStartRow)
            --nRow;      
        if (nEndCol > nStartCol)
            nEndCol = ::std::min( static_cast<SCCOL>(nEndCol - 1), MAXCOL);      

        if (bDetermineRange)
        {
            aRange.aEnd.SetCol( nEndCol );
            aRange.aEnd.SetRow( nRow );

            if ( !mbApi && nStartCol != nEndCol &&
                 !pDoc->IsBlockEmpty( nTab, nStartCol + 1, nStartRow, nEndCol, nRow ) )
            {
                ScReplaceWarnBox aBox( pDocSh->GetActiveDialogParent() );
                if ( aBox.Execute() != RET_YES )
                {
                    delete pEnglishTransliteration;
                    delete pEnglishCalendar;
                    return sal_False;
                }
            }

            rStrm.Seek( nOriginalStreamPos );
            nRow = nStartRow;
            if (!StartPaste())
            {
                EndPaste();
                return sal_False;
            }
        }

        bDetermineRange = !bDetermineRange;     // toggle
    } while (!bDetermineRange);

	ScColumn::bDoubleAlloc = bOld;
	pDoc->DoColResize( nTab, nStartCol, nEndCol, 0 );

    delete pEnglishTransliteration;
	delete pEnglishCalendar;

    xProgress.reset();    // make room for AdjustRowHeight progress
    if (bRangeIsDetermined)
        EndPaste();

	return sal_True;
}


// static
const sal_Unicode* ScImportExport::ScanNextFieldFromString( const sal_Unicode* p,
		String& rField, sal_Unicode cStr, const sal_Unicode* pSeps, bool bMergeSeps, bool& rbIsQuoted )
{
    rbIsQuoted = false;
	rField.Erase();
	if ( *p == cStr )			// String in Anfuehrungszeichen
	{
        rbIsQuoted = true;
        const sal_Unicode* p1;
		p1 = p = lcl_ScanString( p, rField, cStr, DQM_ESCAPE );
		while ( *p && !ScGlobal::UnicodeStrChr( pSeps, *p ) )
			p++;
        // Append remaining unquoted and undelimited data (dirty, dirty) to
        // this field.
        if (p > p1)
            rField.Append( p1, sal::static_int_cast<xub_StrLen>( p - p1 ) );
		if( *p )
			p++;
	}
	else						// bis zum Trennzeichen
	{
		const sal_Unicode* p0 = p;
		while ( *p && !ScGlobal::UnicodeStrChr( pSeps, *p ) )
			p++;
        rField.Append( p0, sal::static_int_cast<xub_StrLen>( p - p0 ) );
		if( *p )
			p++;
	}
	if ( bMergeSeps ) 			// folgende Trennzeichen ueberspringen
	{
		while ( *p && ScGlobal::UnicodeStrChr( pSeps, *p ) )
			p++;
	}
	return p;
}

		//
		//
		//


sal_Bool ScImportExport::Doc2Text( SvStream& rStrm )
{
	SCCOL nCol;
	SCROW nRow;
	SCCOL nStartCol = aRange.aStart.Col();
	SCROW nStartRow = aRange.aStart.Row();
	SCCOL nEndCol = aRange.aEnd.Col();
	SCROW nEndRow = aRange.aEnd.Row();
	String aCell;
    bool bConvertLF = (GetSystemLineEnd() != LINEEND_LF);

	for (nRow = nStartRow; nRow <= nEndRow; nRow++)
	{
		if (bIncludeFiltered || !pDoc->RowFiltered( nRow, aRange.aStart.Tab() ))
		{
			for (nCol = nStartCol; nCol <= nEndCol; nCol++)
			{
				CellType eType;
				pDoc->GetCellType( nCol, nRow, aRange.aStart.Tab(), eType );
				switch (eType)
				{
					case CELLTYPE_FORMULA:
					{
						if (bFormulas)
						{
							pDoc->GetFormula( nCol, nRow, aRange.aStart.Tab(), aCell, sal_True );
							if( aCell.Search( cSep ) != STRING_NOTFOUND )
								lcl_WriteString( rStrm, aCell, cStr, cStr );
							else
								lcl_WriteSimpleString( rStrm, aCell );
						}
						else
						{
                            pDoc->GetString( nCol, nRow, aRange.aStart.Tab(), aCell );

                            bool bMultiLineText = ( aCell.Search( _LF ) != STRING_NOTFOUND );
                            if( bMultiLineText )
                            {
                                if( mExportTextOptions.meNewlineConversion == ScExportTextOptions::ToSpace )
                                    aCell.SearchAndReplaceAll( _LF, ' ' );
                                else if ( mExportTextOptions.meNewlineConversion == ScExportTextOptions::ToSystem && bConvertLF )
                                    aCell.ConvertLineEnd();
                            }

                            if( mExportTextOptions.mcSeparatorConvertTo && cSep )
                                aCell.SearchAndReplaceAll( cSep, mExportTextOptions.mcSeparatorConvertTo );

                            if( mExportTextOptions.mbAddQuotes && ( aCell.Search( cSep ) != STRING_NOTFOUND ) )
                                lcl_WriteString( rStrm, aCell, cStr, cStr );
                            else
                                lcl_WriteSimpleString( rStrm, aCell );
						}
					}
					break;
					case CELLTYPE_VALUE:
					{
						pDoc->GetString( nCol, nRow, aRange.aStart.Tab(), aCell );
						lcl_WriteSimpleString( rStrm, aCell );
					}
					break;
					case CELLTYPE_NOTE:
					case CELLTYPE_NONE:
					break;
					default:
					{
						pDoc->GetString( nCol, nRow, aRange.aStart.Tab(), aCell );

                        bool bMultiLineText = ( aCell.Search( _LF ) != STRING_NOTFOUND );
                        if( bMultiLineText )
                        {
                            if( mExportTextOptions.meNewlineConversion == ScExportTextOptions::ToSpace )
                                aCell.SearchAndReplaceAll( _LF, ' ' );
                            else if ( mExportTextOptions.meNewlineConversion == ScExportTextOptions::ToSystem && bConvertLF )
                                aCell.ConvertLineEnd();
                        }

                        if( mExportTextOptions.mcSeparatorConvertTo && cSep )
                            aCell.SearchAndReplaceAll( cSep, mExportTextOptions.mcSeparatorConvertTo );

                        if( mExportTextOptions.mbAddQuotes && ( aCell.Search( cSep ) != STRING_NOTFOUND ) )
                            lcl_WriteString( rStrm, aCell, cStr, cStr );
                        else
                            lcl_WriteSimpleString( rStrm, aCell );
					}
				}
				if( nCol < nEndCol )
					lcl_WriteSimpleString( rStrm, String(cSep) );
			}
//			if( nRow < nEndRow )
				WriteUnicodeOrByteEndl( rStrm );
			if( rStrm.GetError() != SVSTREAM_OK )
				break;
			if( nSizeLimit && rStrm.Tell() > nSizeLimit )
				break;
		}
	}

	return sal_Bool( rStrm.GetError() == SVSTREAM_OK );
}


sal_Bool ScImportExport::Sylk2Doc( SvStream& rStrm )
{
	sal_Bool bOk = sal_True;
	sal_Bool bMyDoc = sal_False;
    SylkVersion eVersion = SYLK_OTHER;

	// US-English separators for StringToDouble
	sal_Unicode cDecSep = '.';
	sal_Unicode cGrpSep = ',';

	SCCOL nStartCol = aRange.aStart.Col();
	SCROW nStartRow = aRange.aStart.Row();
	SCCOL nEndCol = aRange.aEnd.Col();
	SCROW nEndRow = aRange.aEnd.Row();
	sal_uLong nOldPos = rStrm.Tell();
	sal_Bool bData = sal_Bool( !bSingle );
    SvULongs aFormats;

	if( !bSingle)
		bOk = StartPaste();

	while( bOk )
	{
		String aLine;
        String aText;
		ByteString aByteLine;
		SCCOL nCol = nStartCol;
		SCROW nRow = nStartRow;
        SCCOL nRefCol = 1;
        SCROW nRefRow = 1;
		rStrm.Seek( nOldPos );
		for( ;; )
		{
			//!	allow unicode
			rStrm.ReadLine( aByteLine );
			aLine = String( aByteLine, rStrm.GetStreamCharSet() );
			if( rStrm.IsEof() )
				break;
			const sal_Unicode* p = aLine.GetBuffer();
			sal_Unicode cTag = *p++;
            if( cTag == 'C' )       // Content
			{
				if( *p++ != ';' )
					return sal_False;
				while( *p )
				{
					sal_Unicode ch = *p++;
					ch = ScGlobal::ToUpperAlpha( ch );
					switch( ch )
					{
						case 'X':
							nCol = static_cast<SCCOL>(String( p ).ToInt32()) + nStartCol - 1;
							break;
						case 'Y':
							nRow = String( p ).ToInt32() + nStartRow - 1;
							break;
                        case 'C':
                            nRefCol = static_cast<SCCOL>(String( p ).ToInt32()) + nStartCol - 1;
							break;
                        case 'R':
                            nRefRow = String( p ).ToInt32() + nStartRow - 1;
							break;
						case 'K':
                        {
                            if( !bSingle &&
                                    ( nCol < nStartCol || nCol > nEndCol
                                      || nRow < nStartRow || nRow > nEndRow
                                      || nCol > MAXCOL || nRow > MAXROW ) )
								break;
							if( !bData )
							{
								if( nRow > nEndRow )
									nEndRow = nRow;
								if( nCol > nEndCol )
									nEndCol = nCol;
								break;
							}
                            sal_Bool bText;
                            if( *p == '"' )
                            {
                                bText = sal_True;
                                aText.Erase();
                                p = lcl_ScanSylkString( p, aText, eVersion);
                            }
                            else
                                bText = sal_False;
                            const sal_Unicode* q = p;
                            while( *q && *q != ';' )
                                q++;
                            if ( !(*q == ';' && *(q+1) == 'I') )
                            {   // don't ignore value
                                if( bText )
                                {
                                    pDoc->PutCell( nCol, nRow, aRange.aStart.Tab(), 
                                            ScBaseCell::CreateTextCell( aText, pDoc),
                                            (sal_Bool) sal_True);
                                }
                                else
                                {
                                    double fVal = rtl_math_uStringToDouble( p,
                                            aLine.GetBuffer() + aLine.Len(),
                                            cDecSep, cGrpSep, NULL, NULL );
                                    pDoc->SetValue( nCol, nRow, aRange.aStart.Tab(), fVal );
                                }
                            }
                        }
                        break;
						case 'E':
                        case 'M':
                        {
                            if ( ch == 'M' )
                            {
                                if ( nRefCol < nCol )
                                    nRefCol = nCol;
                                if ( nRefRow < nRow )
                                    nRefRow = nRow;
                                if ( !bData )
                                {
                                    if( nRefRow > nEndRow )
                                        nEndRow = nRefRow;
                                    if( nRefCol > nEndCol )
                                        nEndCol = nRefCol;
                                }
                            }
							if( !bMyDoc || !bData )
								break;
                            aText = '=';
                            p = lcl_ScanSylkFormula( p, aText, eVersion);
                            ScAddress aPos( nCol, nRow, aRange.aStart.Tab() );
                            /* FIXME: do we want GRAM_ODFF_A1 instead? At the
                             * end it probably should be GRAM_ODFF_R1C1, since
                             * R1C1 is what Excel writes in SYLK. */
                            const formula::FormulaGrammar::Grammar eGrammar = formula::FormulaGrammar::GRAM_PODF_A1;
                            ScCompiler aComp( pDoc, aPos);
                            aComp.SetGrammar(eGrammar);
                            ScTokenArray* pCode = aComp.CompileString( aText );
                            if ( ch == 'M' )
                            {
                                ScMarkData aMark;
                                aMark.SelectTable( aPos.Tab(), sal_True );
                                pDoc->InsertMatrixFormula( nCol, nRow, nRefCol,
                                    nRefRow, aMark, EMPTY_STRING, pCode );
                            }
                            else
                            {
                                ScFormulaCell* pFCell = new ScFormulaCell(
                                        pDoc, aPos, pCode, eGrammar, MM_NONE);
                                pDoc->PutCell( aPos, pFCell );
                            }
                            delete pCode;   // ctor/InsertMatrixFormula did copy TokenArray
                        }
                        break;
					}
					while( *p && *p != ';' )
						p++;
					if( *p )
						p++;
				}
			}
            else if( cTag == 'F' )      // Format
            {
                if( *p++ != ';' )
                    return sal_False;
                sal_Int32 nFormat = -1;
                while( *p )
                {
                    sal_Unicode ch = *p++;
                    ch = ScGlobal::ToUpperAlpha( ch );
                    switch( ch )
                    {
                        case 'X':
                            nCol = static_cast<SCCOL>(String( p ).ToInt32()) + nStartCol - 1;
                            break;
                        case 'Y':
                            nRow = String( p ).ToInt32() + nStartRow - 1;
                            break;
                        case 'P' :
                            if ( bData )
                            {
                                // F;P<n> sets format code of P;P<code> at
                                // current position, or at ;X;Y if specified.
                                // Note that ;X;Y may appear after ;P
                                const sal_Unicode* p0 = p;
                                while( *p && *p != ';' )
                                    p++;
                                String aNumber( p0, sal::static_int_cast<xub_StrLen>( p - p0 ) );
                                nFormat = aNumber.ToInt32();
                            }
                            break;
                    }
                    while( *p && *p != ';' )
                        p++;
                    if( *p )
                        p++;
                }
                if ( !bData )
                {
                    if( nRow > nEndRow )
                        nEndRow = nRow;
                    if( nCol > nEndCol )
                        nEndCol = nCol;
                }
                if ( 0 <= nFormat && nFormat < aFormats.Count() )
                {
                    sal_uLong nKey = aFormats[(sal_uInt16)nFormat];
                    pDoc->ApplyAttr( nCol, nRow, aRange.aStart.Tab(),
                            SfxUInt32Item( ATTR_VALUE_FORMAT, nKey ) );
                }
            }
            else if( cTag == 'P' )
            {
                if ( bData && *p == ';' && *(p+1) == 'P' )
                {
                    String aCode( p+2 );
                    // unescape doubled semicolons
                    xub_StrLen nPos = 0;
                    String aSemicolon( RTL_CONSTASCII_USTRINGPARAM(";;"));
                    while ( (nPos = aCode.Search( aSemicolon, nPos )) != STRING_NOTFOUND )
                        aCode.Erase( nPos++, 1 );
                    // get rid of Xcl escape characters
                    nPos = 0;
                    while ( (nPos = aCode.Search( sal_Unicode(0x1b), nPos )) != STRING_NOTFOUND )
                        aCode.Erase( nPos, 1 );
                    xub_StrLen nCheckPos;
                    short nType;
                    sal_uInt32 nKey;
                    pDoc->GetFormatTable()->PutandConvertEntry(
                        aCode, nCheckPos, nType, nKey, LANGUAGE_ENGLISH_US,
                        ScGlobal::eLnge );
                    if ( nCheckPos )
                        nKey = 0;
                    aFormats.Insert( nKey, aFormats.Count() );
                }
            }
            else if( cTag == 'I' && *p == 'D' )
			{
				aLine.Erase( 0, 4 );
				if (aLine.EqualsAscii( "CALCOOO32" ))
                    eVersion = SYLK_OOO32;
                else if (aLine.EqualsAscii( "SCALC3" ))
                    eVersion = SYLK_SCALC3;
                bMyDoc = (eVersion <= SYLK_OWN);
			}
			else if( cTag == 'E' )						// Ende
				break;
		}
		if( !bData )
		{
			aRange.aEnd.SetCol( nEndCol );
			aRange.aEnd.SetRow( nEndRow );
			bOk = StartPaste();
			bData = sal_True;
		}
		else
			break;
	}

	EndPaste();
	return bOk;
}


sal_Bool ScImportExport::Doc2Sylk( SvStream& rStrm )
{
	SCCOL nCol;
	SCROW nRow;
	SCCOL nStartCol = aRange.aStart.Col();
	SCROW nStartRow = aRange.aStart.Row();
	SCCOL nEndCol = aRange.aEnd.Col();
	SCROW nEndRow = aRange.aEnd.Row();
	String aCellStr;
	String aValStr;
	lcl_WriteSimpleString( rStrm,
            String( RTL_CONSTASCII_USTRINGPARAM( "ID;PCALCOOO32")));
	WriteUnicodeOrByteEndl( rStrm );

	for (nRow = nStartRow; nRow <= nEndRow; nRow++)
	{
		for (nCol = nStartCol; nCol <= nEndCol; nCol++)
		{
			String aBufStr;
			double nVal;
			sal_Bool bForm = sal_False;
			SCROW r = nRow - nStartRow + 1;
			SCCOL c = nCol - nStartCol + 1;
            ScBaseCell* pCell;
            pDoc->GetCell( nCol, nRow, aRange.aStart.Tab(), pCell );
            CellType eType = (pCell ? pCell->GetCellType() : CELLTYPE_NONE);
			switch( eType )
			{
				case CELLTYPE_FORMULA:
					bForm = bFormulas;
					if( pDoc->HasValueData( nCol, nRow, aRange.aStart.Tab()) )
						goto hasvalue;
					else
						goto hasstring;

				case CELLTYPE_VALUE:
				hasvalue:
					pDoc->GetValue( nCol, nRow, aRange.aStart.Tab(), nVal );

                    aValStr = ::rtl::math::doubleToUString( nVal,
                            rtl_math_StringFormat_Automatic,
                            rtl_math_DecimalPlaces_Max, '.', sal_True );

					aBufStr.AssignAscii(RTL_CONSTASCII_STRINGPARAM( "C;X" ));
					aBufStr += String::CreateFromInt32( c );
					aBufStr.AppendAscii(RTL_CONSTASCII_STRINGPARAM( ";Y" ));
					aBufStr += String::CreateFromInt32( r );
					aBufStr.AppendAscii(RTL_CONSTASCII_STRINGPARAM( ";K" ));
					aBufStr += aValStr;
					lcl_WriteSimpleString( rStrm, aBufStr );
					goto checkformula;

				case CELLTYPE_STRING:
				case CELLTYPE_EDIT:
				hasstring:
					pDoc->GetString( nCol, nRow, aRange.aStart.Tab(), aCellStr );
                    aCellStr.SearchAndReplaceAll( _LF, SYLK_LF );

					aBufStr.AssignAscii(RTL_CONSTASCII_STRINGPARAM( "C;X" ));
					aBufStr += String::CreateFromInt32( c );
					aBufStr.AppendAscii(RTL_CONSTASCII_STRINGPARAM( ";Y" ));
					aBufStr += String::CreateFromInt32( r );
					aBufStr.AppendAscii(RTL_CONSTASCII_STRINGPARAM( ";K" ));
					lcl_WriteSimpleString( rStrm, aBufStr );
					lcl_WriteString( rStrm, aCellStr, '"', ';' );

				checkformula:
					if( bForm )
					{
                        const ScFormulaCell* pFCell =
                            static_cast<const ScFormulaCell*>(pCell);
                        switch ( pFCell->GetMatrixFlag() )
                        {
                            case MM_REFERENCE :
                                aCellStr.Erase();
                            break;
                            default:
                                pFCell->GetFormula( aCellStr,formula::FormulaGrammar::GRAM_PODF_A1);
                                /* FIXME: do we want GRAM_ODFF_A1 instead? At
                                 * the end it probably should be
                                 * GRAM_ODFF_R1C1, since R1C1 is what Excel
                                 * writes in SYLK. */
                        }
                        if ( pFCell->GetMatrixFlag() != MM_NONE &&
                                aCellStr.Len() > 2 &&
                                aCellStr.GetChar(0) == '{' &&
                                aCellStr.GetChar(aCellStr.Len()-1) == '}' )
                        {   // cut off matrix {} characters
                            aCellStr.Erase(aCellStr.Len()-1,1);
                            aCellStr.Erase(0,1);
                        }
                        if ( aCellStr.GetChar(0) == '=' )
                            aCellStr.Erase(0,1);
                        String aPrefix;
                        switch ( pFCell->GetMatrixFlag() )
                        {
                            case MM_FORMULA :
                            {   // diff expression with 'M' M$-extension
                                SCCOL nC;
                                SCROW nR;
                                pFCell->GetMatColsRows( nC, nR );
                                nC += c - 1;
                                nR += r - 1;
                                aPrefix.AssignAscii( RTL_CONSTASCII_STRINGPARAM( ";R" ) );
                                aPrefix += String::CreateFromInt32( nR );
                                aPrefix.AppendAscii( RTL_CONSTASCII_STRINGPARAM( ";C" ) );
                                aPrefix += String::CreateFromInt32( nC );
                                aPrefix.AppendAscii( RTL_CONSTASCII_STRINGPARAM( ";M" ) );
                            }
                            break;
                            case MM_REFERENCE :
                            {   // diff expression with 'I' M$-extension
                                ScAddress aPos;
                                pFCell->GetMatrixOrigin( aPos );
                                aPrefix.AssignAscii( RTL_CONSTASCII_STRINGPARAM( ";I;R" ) );
                                aPrefix += String::CreateFromInt32( aPos.Row() - nStartRow + 1 );
                                aPrefix.AppendAscii( RTL_CONSTASCII_STRINGPARAM( ";C" ) );
                                aPrefix += String::CreateFromInt32( aPos.Col() - nStartCol + 1 );
                            }
                            break;
                            default:
                                // formula Expression
                                aPrefix.AssignAscii( RTL_CONSTASCII_STRINGPARAM( ";E" ) );
                        }
                        lcl_WriteSimpleString( rStrm, aPrefix );
                        if ( aCellStr.Len() )
                            lcl_WriteString( rStrm, aCellStr, 0, ';' );
					}
					WriteUnicodeOrByteEndl( rStrm );
					break;

                default:
                {
                    // added to avoid warnings
                }
			}
		}
	}
	lcl_WriteSimpleString( rStrm, String( 'E' ) );
	WriteUnicodeOrByteEndl( rStrm );
	return sal_Bool( rStrm.GetError() == SVSTREAM_OK );
}


sal_Bool ScImportExport::Doc2HTML( SvStream& rStrm, const String& rBaseURL )
{
    // CharSet is ignored in ScExportHTML, read from Load/Save HTML options
    ScFormatFilter::Get().ScExportHTML( rStrm, rBaseURL, pDoc, aRange, RTL_TEXTENCODING_DONTKNOW, bAll,
        aStreamPath, aNonConvertibleChars );
	return sal_Bool( rStrm.GetError() == SVSTREAM_OK );
}

sal_Bool ScImportExport::Doc2RTF( SvStream& rStrm )
{
	//	CharSet is ignored in ScExportRTF
    ScFormatFilter::Get().ScExportRTF( rStrm, pDoc, aRange, RTL_TEXTENCODING_DONTKNOW );
	return sal_Bool( rStrm.GetError() == SVSTREAM_OK );
}


sal_Bool ScImportExport::Doc2Dif( SvStream& rStrm )
{
	// for DIF in the clipboard, IBM_850 is always used
    ScFormatFilter::Get().ScExportDif( rStrm, pDoc, aRange, RTL_TEXTENCODING_IBM_850 );
	return sal_True;
}


sal_Bool ScImportExport::Dif2Doc( SvStream& rStrm )
{
	SCTAB nTab = aRange.aStart.Tab();
	ScDocument* pImportDoc = new ScDocument( SCDOCMODE_UNDO );
	pImportDoc->InitUndo( pDoc, nTab, nTab );

	// for DIF in the clipboard, IBM_850 is always used
	ScFormatFilter::Get().ScImportDif( rStrm, pImportDoc, aRange.aStart, RTL_TEXTENCODING_IBM_850 );

	SCCOL nEndCol;
	SCROW nEndRow;
	pImportDoc->GetCellArea( nTab, nEndCol, nEndRow );
    // #131247# if there are no cells in the imported content, nEndCol/nEndRow may be before the start
    if ( nEndCol < aRange.aStart.Col() )
        nEndCol = aRange.aStart.Col();
    if ( nEndRow < aRange.aStart.Row() )
        nEndRow = aRange.aStart.Row();
	aRange.aEnd = ScAddress( nEndCol, nEndRow, nTab );

	sal_Bool bOk = StartPaste();
	if (bOk)
	{
		sal_uInt16 nFlags = IDF_ALL & ~IDF_STYLES;
		pDoc->DeleteAreaTab( aRange, nFlags );
		pImportDoc->CopyToDocument( aRange, nFlags, sal_False, pDoc );
		EndPaste();
	}

	delete pImportDoc;

	return bOk;
}


sal_Bool ScImportExport::RTF2Doc( SvStream& rStrm, const String& rBaseURL )
{
    ScEEAbsImport *pImp = ScFormatFilter::Get().CreateRTFImport( pDoc, aRange );
    if (!pImp)
        return false;
    pImp->Read( rStrm, rBaseURL );
    aRange = pImp->GetRange();

    sal_Bool bOk = StartPaste();
    if (bOk)
    {
        sal_uInt16 nFlags = IDF_ALL & ~IDF_STYLES;
        pDoc->DeleteAreaTab( aRange, nFlags );
        pImp->WriteToDocument();
        EndPaste();
    }
    delete pImp;
    return bOk;
}


sal_Bool ScImportExport::HTML2Doc( SvStream& rStrm, const String& rBaseURL )
{
    ScEEAbsImport *pImp = ScFormatFilter::Get().CreateHTMLImport( pDoc, rBaseURL, aRange, sal_True);
    if (!pImp)
        return false;
    pImp->Read( rStrm, rBaseURL );
    aRange = pImp->GetRange();

    sal_Bool bOk = StartPaste();
    if (bOk)
    {
        // ScHTMLImport may call ScDocument::InitDrawLayer, resulting in
        // a Draw Layer but no Draw View -> create Draw Layer and View here
        if (pDocSh)
            pDocSh->MakeDrawLayer();

        sal_uInt16 nFlags = IDF_ALL & ~IDF_STYLES;
        pDoc->DeleteAreaTab( aRange, nFlags );
        pImp->WriteToDocument();
        EndPaste();
    }
    delete pImp;
    return bOk;
}

#define RETURN_ERROR { return eERR_INTERN; }
class ScFormatFilterMissing : public ScFormatFilterPlugin {
  public:
    ScFormatFilterMissing()
    {
      OSL_ASSERT ("Missing file filters");
    }
    virtual FltError ScImportLotus123( SfxMedium&, ScDocument*, CharSet ) RETURN_ERROR
    virtual FltError ScImportQuattroPro( SfxMedium &, ScDocument * ) RETURN_ERROR
    virtual FltError ScImportExcel( SfxMedium&, ScDocument*, const EXCIMPFORMAT ) RETURN_ERROR
    virtual FltError ScImportStarCalc10( SvStream&, ScDocument* ) RETURN_ERROR
    virtual FltError ScImportDif( SvStream&, ScDocument*, const ScAddress&,
				 const CharSet, sal_uInt32 ) RETURN_ERROR
    virtual FltError ScImportRTF( SvStream&, const String&, ScDocument*, ScRange& ) RETURN_ERROR
    virtual FltError ScImportHTML( SvStream&, const String&, ScDocument*, ScRange&, double, sal_Bool, SvNumberFormatter*, bool ) RETURN_ERROR

    virtual ScEEAbsImport *CreateRTFImport( ScDocument*, const ScRange& ) { return NULL; }
    virtual ScEEAbsImport *CreateHTMLImport( ScDocument*, const String&, const ScRange&, sal_Bool ) { return NULL; }
    virtual String         GetHTMLRangeNameList( ScDocument*, const String& ) { return String(); }

#if ENABLE_LOTUS123_EXPORT
    virtual FltError ScExportLotus123( SvStream&, ScDocument*, ExportFormatLotus, CharSet ) RETURN_ERROR
#endif
    virtual FltError ScExportExcel5( SfxMedium&, ScDocument*, ExportFormatExcel, CharSet ) RETURN_ERROR
    virtual FltError ScExportDif( SvStream&, ScDocument*, const ScAddress&, const CharSet, sal_uInt32 ) RETURN_ERROR
    virtual FltError ScExportDif( SvStream&, ScDocument*, const ScRange&, const CharSet, sal_uInt32 ) RETURN_ERROR
    virtual FltError ScExportHTML( SvStream&, const String&, ScDocument*, const ScRange&, const CharSet, sal_Bool,
				  const String&, String& ) RETURN_ERROR
    virtual FltError ScExportRTF( SvStream&, ScDocument*, const ScRange&, const CharSet ) RETURN_ERROR
};

extern "C" { static void SAL_CALL thisModule() {} }
typedef ScFormatFilterPlugin * (*FilterFn)(void);
ScFormatFilterPlugin &ScFormatFilter::Get()
{
    static ScFormatFilterPlugin *plugin;

    if (plugin != NULL)
        return *plugin;

    static ::osl::Module aModule;
    if ( aModule.loadRelative( &thisModule,
			       ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( SVLIBRARY( "scfilt" ) ) ) ) )
    {
	oslGenericFunction fn = aModule.getFunctionSymbol( ::rtl::OUString::createFromAscii( "ScFilterCreate" ) );
	if (fn != NULL)
	    plugin = reinterpret_cast<FilterFn>(fn)();
    }
    if (plugin == NULL)
        plugin = new ScFormatFilterMissing();

    return *plugin;
}
