blob: 4034ca8ee0c839f2b7c729b09595c166bfa67181 [file] [log] [blame]
/**************************************************************
*
* 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"
// 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 )//cStr = "
{
p = lcl_ScanString( p, aCell, cStr, DQM_KEEP );
}
const sal_Unicode* q = p;
while( *p && *p != cSep )// cSep = tab
p++;
aCell.Append( 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;
}