Commits

Anonymous committed ea759dc

CWS-TOOLING: integrate CWS calcmultiline
2009-05-13 18:05:10 +0200 nn r271868 : CWS-TOOLING: rebase CWS calcmultiline to trunk@271830 (milestone: DEV300:m48)
2009-03-15 00:59:24 +0100 erack r269502 : #i100205# SYLK import/export: treat embedded double quotes and semicolons correctly; changes ID;PSCALC3 to ID;PCALCOOO32 due to incompatibility
2009-03-10 17:54:33 +0100 er r269286 : #i35913# multiline (newlines) as formula results; contributed by William S Fulton <wsfulton>

  • Participants
  • Parent commits 256d80b

Comments (0)

Files changed (28)

 	inline BOOL		IsHyperLinkCell() const { return pCode && pCode->IsHyperLink(); }
 	EditTextObject*		CreateURLObject() ;
     void            GetURLResult( String& rURL, String& rCellText );
+
+    /** Determines whether or not the result string contains more than one paragraph */
+    bool            IsMultilineResult();
 };
 
 //			Iterator fuer Referenzen in einer Formelzelle

sc/inc/editutil.hxx

 
 public:
 	static String ModifyDelimiters( const String& rOld );
+
+    /// Retrieves string with paragraphs delimited by spaces
 	static String GetSpaceDelimitedString( const EditEngine& rEngine );
 
+    /// Retrieves string with paragraphs delimited by new lines ('\n').
+    static String GetMultilineString( const EditEngine& rEngine );
+
 public:
 				ScEditUtil( ScDocument* pDocument, SCCOL nX, SCROW nY, SCTAB nZ,
 							const Point& rScrPosPixel,

sc/inc/formularesult.hxx

     and memory consumption. */
 class ScFormulaResult
 {
+    typedef unsigned char Multiline;
+    static const Multiline MULTILINE_UNKNOWN = 0;
+    static const Multiline MULTILINE_FALSE   = 1;
+    static const Multiline MULTILINE_TRUE    = 2;
+
     union
     {
         double          mfValue;    // double result direct for performance and memory consumption
     bool                mbToken :1; // whether content of union is a token
     bool                mbEmpty :1; // empty cell result
     bool                mbEmptyDisplayedAsString :1;    // only if mbEmpty
+    Multiline           meMultiline :2; // result is multiline
 
     /** Reset mnError, mbEmpty and mbEmptyDisplayedAsString to their defaults
         prior to assigning other types */
                                 /** Effectively type svUnknown. */
                                 ScFormulaResult()
                                     : mpToken(NULL), mnError(0), mbToken(true),
-                                    mbEmpty(false), mbEmptyDisplayedAsString(false) {}
+                                    mbEmpty(false), mbEmptyDisplayedAsString(false),
+                                    meMultiline(MULTILINE_UNKNOWN) {}
 
                                 ScFormulaResult( const ScFormulaResult & r )
                                     : mnError( r.mnError), mbToken( r.mbToken),
                                     mbEmpty( r.mbEmpty),
-                                    mbEmptyDisplayedAsString( r.mbEmptyDisplayedAsString)
+                                    mbEmptyDisplayedAsString( r.mbEmptyDisplayedAsString),
+                                    meMultiline( r.meMultiline)
                                 {
                                     if (mbToken)
                                     {
     /** Same comments as for SetToken() apply! */
     explicit                    ScFormulaResult( const formula::FormulaToken* p )
                                     : mnError(0), mbToken(false),
-                                    mbEmpty(false), mbEmptyDisplayedAsString(false)
+                                    mbEmpty(false), mbEmptyDisplayedAsString(false),
+                                    meMultiline(MULTILINE_UNKNOWN)
                                 {
                                     SetToken( p);
                                 }
         details instead. */
     inline  bool                IsValue() const;
 
+    /** Determines whether or not the result is a string containing more than 
+        one paragraph */
+    inline  bool                IsMultiline() const;
+
     /** Get error code if set or GetCellResultType() is formula::svError or svUnknown,
         else 0. */
     inline  USHORT              GetResultError() const;
     mnError = 0;
     mbEmpty = false;
     mbEmptyDisplayedAsString = false;
+    meMultiline = MULTILINE_UNKNOWN;
 }
 
 
                 mbToken = false;
                 // set in case mnError is 0 now, which shouldn't happen but ...
                 mfValue = 0.0;
+                meMultiline = MULTILINE_FALSE;
                 break;
             case formula::svEmptyCell:
                 mbEmpty = true;
                 mbEmptyDisplayedAsString = static_cast<const ScEmptyCellToken*>(p)->IsDisplayedAsString();
                 p->DecRef();
                 mbToken = false;
+                meMultiline = MULTILINE_FALSE;
                 break;
             case formula::svDouble:
                 mfValue = p->GetDouble();
                 p->DecRef();
                 mbToken = false;
+                meMultiline = MULTILINE_FALSE;
                 break;
             default:
                 mpToken = p;
         mbToken = false;
         mbEmpty = true;
         mbEmptyDisplayedAsString = r.mbEmptyDisplayedAsString;
+        meMultiline = r.meMultiline;
     }
     else if (r.mbToken)
     {
             mpToken->DecRef();
         mfValue = f;
         mbToken = false;
+        meMultiline = MULTILINE_FALSE;
     }
 }
 
     return sv == formula::svDouble || sv == formula::svError || sv == formula::svEmptyCell;
 }
 
+inline bool ScFormulaResult::IsMultiline() const
+{
+    if (meMultiline == MULTILINE_UNKNOWN)
+    {
+        const String& rStr = GetString();
+        if (rStr.Len() && rStr.Search( _LF ) != STRING_NOTFOUND)
+            const_cast<ScFormulaResult*>(this)->meMultiline = MULTILINE_TRUE;
+        else
+            const_cast<ScFormulaResult*>(this)->meMultiline = MULTILINE_FALSE;
+    }
+    return meMultiline == MULTILINE_TRUE;
+}
+
 
 inline USHORT ScFormulaResult::GetResultError() const
 {
     {
         mfValue = f;
         mbToken = false;
+        meMultiline = MULTILINE_FALSE;
     }
 }
 

sc/source/core/data/cell.cxx

     }
 }
 
+bool ScFormulaCell::IsMultilineResult()
+{
+    if (!IsValue())
+        return aResult.IsMultiline();
+    return false;
+}
+
 EditTextObject* ScFormulaCell::CreateURLObject()
 {
     String aCellText;

sc/source/core/data/cell2.cxx

         // auch Text von URL-Feldern, Doc-Engine ist eine ScFieldEditEngine
         EditEngine& rEngine = pDoc->GetEditEngine();
         rEngine.SetText( *pData );
-        rString = ScEditUtil::GetSpaceDelimitedString(rEngine);     // space between paragraphs
+        rString = ScEditUtil::GetMultilineString(rEngine); // string with line separators between paragraphs
         // kurze Strings fuer Formeln merken
         if ( rString.Len() < MAXSTRLEN )
             ((ScEditCell*)this)->pString = new String( rString );   //! non-const

sc/source/core/data/column.cxx

 	while ( (nIndex < nCount) ? ((nRow=pItems[nIndex].nRow) <= nEndRow) : FALSE )
 	{
 		ScBaseCell* pCell = pItems[nIndex].pCell;
-		if ( pCell->GetCellType() == CELLTYPE_EDIT ||
-			 IsAmbiguousScriptNonZero( pDocument->GetScriptType(nCol, nRow, nTab, pCell) ) )
+        CellType eCellType = pCell->GetCellType();
+		if ( eCellType == CELLTYPE_EDIT ||
+			 IsAmbiguousScriptNonZero( pDocument->GetScriptType(nCol, nRow, nTab, pCell) ) ||
+             ((eCellType == CELLTYPE_FORMULA) && ((ScFormulaCell*)pCell)->IsMultilineResult()) )
 		{
 			rFirst = nRow;
 			return TRUE;

sc/source/core/data/column2.cxx

 		}
 
 		BOOL bAddMargin = TRUE;
-		BOOL bEditEngine = ( pCell->GetCellType() == CELLTYPE_EDIT ||
+        CellType eCellType = pCell->GetCellType();
+
+		BOOL bEditEngine = ( eCellType == CELLTYPE_EDIT ||
 								eOrient == SVX_ORIENTATION_STACKED ||
-								IsAmbiguousScript( nScript ) );
+								IsAmbiguousScript( nScript ) ||
+                                ((eCellType == CELLTYPE_FORMULA) && ((ScFormulaCell*)pCell)->IsMultilineResult()) );
 
 		if (!bEditEngine)									// direkte Ausgabe
 		{

sc/source/core/data/column3.cxx

                     rForm.GetString( aString );
                     // #33224# do not clone empty string
                     if (aString.Len() > 0)
-                        pNew = new ScStringCell( aString );
+                    {
+                        if ( rForm.IsMultilineResult() )
+                        {
+                            pNew = new ScEditCell( aString, &rDestDoc );
+                        }
+                        else
+                        {
+                            pNew = new ScStringCell( aString );
+                        }
+                    }
                 }
             }
         break;

sc/source/core/tool/editutil.cxx

 	return aRet;
 }
 
-String ScEditUtil::GetSpaceDelimitedString( const EditEngine& rEngine )
+static String lcl_GetDelimitedString( const EditEngine& rEngine, const sal_Char c )
 {
 	String aRet;
 	USHORT nParCount = rEngine.GetParagraphCount();
 	for (USHORT nPar=0; nPar<nParCount; nPar++)
 	{
 		if (nPar > 0)
-			aRet += ' ';
+			aRet += c;
 		aRet += rEngine.GetText( nPar );
 	}
 	return aRet;
 }
 
+String ScEditUtil::GetSpaceDelimitedString( const EditEngine& rEngine )
+{
+    return lcl_GetDelimitedString(rEngine, ' ');
+}
+
+String ScEditUtil::GetMultilineString( const EditEngine& rEngine )
+{
+    return lcl_GetDelimitedString(rEngine, '\n');
+}
+
 //------------------------------------------------------------------------
 
 Rectangle ScEditUtil::GetEditArea( const ScPatternAttr* pPattern, BOOL bForceToTop )

sc/source/filter/dif/difimp.cxx

 
 	while( eS != S_END )
 	{
-		if( !rIn.ReadUniOrByteStringLine( aLine ) )
+		if( !ReadNextLine( aLine ) )
 		{
 			eS = S_END;
 			eRet = T_END;
 				break;
 			case S_UNKNOWN:
 				// 2 Zeilen ueberlesen
-				rIn.ReadUniOrByteStringLine( aLine );
+				ReadNextLine( aLine );
 			case S_ERROR_L2:				// Fehler in Line 2 aufgetreten
 				// eine Zeile ueberlesen
-				rIn.ReadUniOrByteStringLine( aLine );
+				ReadNextLine( aLine );
 				eS = S_END;
 				break;
 			default:
 }
 
 
-void lcl_DeEscapeQuotesDif( String& rString )
+static void lcl_DeEscapeQuotesDif( String& rString )
 {
 	//	Special handling for DIF import: Escaped (duplicated) quotes are resolved.
 	//	Single quote characters are left in place because older versions didn't
 	}
 }
 
+// Determine if passed in string is numeric data and set fVal/nNumFormat if so
+DATASET DifParser::GetNumberDataset( const sal_Unicode* pPossibleNumericData )
+{
+    DATASET eRet = D_SYNT_ERROR;
+    if( bPlain )
+    {
+        if( ScanFloatVal( pPossibleNumericData ) )
+            eRet = D_NUMERIC;
+        else
+            eRet = D_SYNT_ERROR;
+    }
+    else
+    {   // ...und zur Strafe mit'm Numberformatter...
+        DBG_ASSERT( pNumFormatter, "-DifParser::GetNextDataset(): No Formatter, more fun!" );
+        String aTestVal( pPossibleNumericData );
+        sal_uInt32 nFormat = 0;
+        double fTmpVal;
+        if( pNumFormatter->IsNumberFormat( aTestVal, nFormat, fTmpVal ) )
+        {
+            fVal = fTmpVal;
+            nNumFormat = nFormat;
+            eRet = D_NUMERIC;
+        }
+        else
+            eRet = D_SYNT_ERROR;
+    }
+    return eRet;
+}
+
+bool DifParser::ReadNextLine( String& rStr )
+{
+    if( aLookAheadLine.Len() == 0 )
+    {
+        return rIn.ReadUniOrByteStringLine( rStr );
+    }
+    else
+    {
+        rStr = aLookAheadLine;
+        aLookAheadLine.Erase();
+        return true;
+    }
+}
+
+// Look ahead in the stream to determine if the next line is the first line of 
+// a valid data record structure
+bool DifParser::LookAhead()
+{
+    const sal_Unicode* pAktBuffer;
+    bool bValidStructure = false;
+
+    DBG_ASSERT( aLookAheadLine.Len() == 0, "*DifParser::LookAhead(): LookAhead called twice in a row" );
+    rIn.ReadUniOrByteStringLine( aLookAheadLine );
+
+    pAktBuffer = aLookAheadLine.GetBuffer();
+
+    switch( *pAktBuffer )
+    {
+        case '-':                   // Special Datatype
+            pAktBuffer++;
+
+            if( Is1_0( pAktBuffer ) )
+            {
+                bValidStructure = true;
+            }
+            break;
+        case '0':                   // Numeric Data
+            pAktBuffer++;
+            if( *pAktBuffer == ',' )
+            {
+                pAktBuffer++;
+                bValidStructure = ( GetNumberDataset(pAktBuffer) != D_SYNT_ERROR );
+            }
+            break;
+        case '1':                   // String Data
+            if( Is1_0( aLookAheadLine.GetBuffer() ) )
+            {
+                bValidStructure = true;
+            }
+            break;
+    }
+    return bValidStructure;
+}
 
 DATASET	DifParser::GetNextDataset( void )
 {
 	DATASET				eRet = D_UNKNOWN;
 	String			    aLine;
-	const sal_Unicode*		pAkt;
+	const sal_Unicode*		pAktBuffer;
 
-	rIn.ReadUniOrByteStringLine( aLine );
+	ReadNextLine( aLine );
 
-	pAkt = aLine.GetBuffer();
+	pAktBuffer = aLine.GetBuffer();
 
-	switch( *pAkt )
+	switch( *pAktBuffer )
 	{
 		case '-':					// Special Datatype
-			pAkt++;
+			pAktBuffer++;
 
-			if( Is1_0( pAkt ) )
+			if( Is1_0( pAktBuffer ) )
 			{
-				rIn.ReadUniOrByteStringLine( aLine );
+				ReadNextLine( aLine );
 				if( IsBOT( aLine.GetBuffer() ) )
 					eRet = D_BOT;
 				else if( IsEOD( aLine.GetBuffer() ) )
 			}
 			break;
 		case '0':					// Numeric Data
-			pAkt++;					// Wert in fVal, 2. Zeile in aData
-			if( *pAkt == ',' )
+			pAktBuffer++;			// Wert in fVal, 2. Zeile in aData
+			if( *pAktBuffer == ',' )
 			{
-				pAkt++;
-				if( bPlain )
-				{
-					if( ScanFloatVal( pAkt ) )
-						eRet = D_NUMERIC;
-					else
-						eRet = D_SYNT_ERROR;
-				}
-				else
-				{	// ...und zur Strafe mit'm Numberformatter...
-					DBG_ASSERT( pNumFormatter, "-DifParser::GetNextDataset(): No Formatter, more fun!" );
-					String			aTestVal( pAkt );
-					sal_uInt32		nFormat = 0;
-					double			fTmpVal;
-					if( pNumFormatter->IsNumberFormat( aTestVal, nFormat, fTmpVal ) )
-					{
-						fVal = fTmpVal;
-						nNumFormat = nFormat;
-						eRet = D_NUMERIC;
-					}
-					else
-						eRet = D_SYNT_ERROR;
-				}
-                rIn.ReadUniOrByteStringLine( aData );
+				pAktBuffer++;
+                eRet = GetNumberDataset(pAktBuffer);
+                ReadNextLine( aData );
                 if ( eRet == D_SYNT_ERROR )
                 {   // for broken records write "#ERR: data" to cell
                     String aTmp( RTL_CONSTASCII_USTRINGPARAM( "#ERR: " ));
-                    aTmp += pAkt;
+                    aTmp += pAktBuffer;
                     aTmp.AppendAscii( " (" );
                     aTmp += aData;
                     aTmp += sal_Unicode(')');
 		case '1':					// String Data
 			if( Is1_0( aLine.GetBuffer() ) )
 			{
-				rIn.ReadUniOrByteStringLine( aLine );
-				DBG_ASSERT( aLine.Len() >= 2,
-					"*DifParser::GetNextTopic(): Text ist zu kurz (mind. \"\")!" );
-				aData = aLine.Copy( 1, aLine.Len() - 2 );
-				lcl_DeEscapeQuotesDif( aData );
-				eRet = D_STRING;
+				ReadNextLine( aLine );
+                xub_StrLen nLineLength = aLine.Len();
+                const sal_Unicode* pLine = aLine.GetBuffer();
+
+                if( nLineLength >= 1 && *pLine == '"' )
+                {
+                    // Quotes are not always escaped (duplicated), see lcl_DeEscapeQuotesDif
+                    // A look ahead into the next line is needed in order to deal with 
+                    // multiline strings containing quotes
+                    if( LookAhead() )
+                    {
+                        // Single line string
+                        if( nLineLength >= 2 && pLine[nLineLength - 1] == '"' )
+                        {
+                            aData = aLine.Copy( 1, nLineLength - 2 );
+                            lcl_DeEscapeQuotesDif( aData );
+                            eRet = D_STRING;
+                        }
+                    }
+                    else
+                    {
+                        // Multiline string
+                        aData = aLine.Copy( 1 );
+                        bool bContinue = true;
+                        while ( bContinue )
+                        {
+                            aData.Append( '\n' );
+                            bContinue = !rIn.IsEof() && ReadNextLine( aLine );
+                            if( bContinue )
+                            {
+                                nLineLength = aLine.Len();
+                                if( nLineLength >= 1 )
+                                {
+                                    pLine = aLine.GetBuffer();
+                                    bContinue = !LookAhead();
+                                    if( bContinue )
+                                    {
+                                        aData.Append( aLine );
+                                    }
+                                    else if( pLine[nLineLength - 1] == '"' )
+                                    {
+                                        aData.Append( pLine, nLineLength - 1 );
+                                        lcl_DeEscapeQuotesDif( aData );
+                                        eRet = D_STRING;
+                                    }
+                                }
+                            }
+                        };
+                    }
+                }
 			}
 			break;
 	}
 
 	if( eRet == D_UNKNOWN )
-		rIn.ReadUniOrByteStringLine( aLine );
+		ReadNextLine( aLine );
 
 	if( rIn.IsEof() )
 		eRet = D_EOD;

sc/source/filter/excel/xestyle.cxx

     return InsertCellXF( pPattern, nScript, NUMBERFORMAT_ENTRY_NOT_FOUND, nForceXclFont, bForceLineBreak );
 }
 
-sal_uInt32 XclExpXFBuffer::InsertWithNumFmt( const ScPatternAttr* pPattern, sal_Int16 nScript, ULONG nForceScNumFmt )
+sal_uInt32 XclExpXFBuffer::InsertWithNumFmt( const ScPatternAttr* pPattern, sal_Int16 nScript, ULONG nForceScNumFmt, bool bForceLineBreak )
 {
-    return InsertCellXF( pPattern, nScript, nForceScNumFmt, EXC_FONT_NOTFOUND, false );
+    return InsertCellXF( pPattern, nScript, nForceScNumFmt, EXC_FONT_NOTFOUND, bForceLineBreak );
 }
 
 sal_uInt32 XclExpXFBuffer::InsertStyle( const SfxStyleSheetBase* pStyleSheet )

sc/source/filter/excel/xetable.cxx

 
         // #i41420# find script type according to result type (always latin for numeric results)
         sal_Int16 nScript = ApiScriptType::LATIN;
+        bool bForceLineBreak = false;
         if( nFormatType == NUMBERFORMAT_TEXT )
         {
             String aResult;
             mrScFmlaCell.GetString( aResult );
+            bForceLineBreak = mrScFmlaCell.IsMultilineResult();
             nScript = XclExpStringHelper::GetLeadingScriptType( rRoot, aResult );
         }
-        SetXFId( rRoot.GetXFBuffer().InsertWithNumFmt( pPattern, nScript, nAltScNumFmt ) );
+        SetXFId( rRoot.GetXFBuffer().InsertWithNumFmt( pPattern, nScript, nAltScNumFmt, bForceLineBreak ) );
     }
 
     // *** Convert the formula token array *** --------------------------------

sc/source/filter/html/htmlexp.cxx

 	if ( !bFieldText )
 	{
 		if ( !aStrOut.Len() )
+        {
 			TAG_ON( OOO_STRING_SVTOOLS_HTML_linebreak );		// #42573# keine komplett leere Zelle
+        }
 		else
-			OUT_STR( aStrOut );
+        {
+            xub_StrLen nPos = aStrOut.Search( _LF );
+            if ( nPos == STRING_NOTFOUND )
+            {
+                OUT_STR( aStrOut );
+            }
+            else
+            {
+                xub_StrLen nStartPos = 0;
+                do
+                {
+                    String aSingleLine( aStrOut, nStartPos, nPos - nStartPos );
+                    OUT_STR( aSingleLine );
+                    TAG_ON( OOO_STRING_SVTOOLS_HTML_linebreak );
+                    nStartPos = nPos + 1;
+                } 
+                while( ( nPos = aStrOut.Search( _LF, nStartPos ) ) != STRING_NOTFOUND );
+                String aSingleLine( aStrOut, nStartPos, aStrOut.Len() - nStartPos );
+                OUT_STR( aSingleLine );
+            }
+        }
 	}
     if ( pGraphEntry )
 		WriteGraphEntry( pGraphEntry );
 		for ( USHORT nPar=0; nPar < nParas; nPar++ )
 		{
 			if ( nPar > 0 )
-				rStrm << ' ';		// blank between paragraphs
+                TAG_ON( OOO_STRING_SVTOOLS_HTML_linebreak );
 			SvUShorts aPortions;
 			rEngine.GetPortions( nPar, aPortions );
 			USHORT nCnt = aPortions.Count();

sc/source/filter/inc/dif.hxx

 	SvNumberFormatter*	pNumFormatter;
 	SvStream&			rIn;
 	BOOL				bPlain;
+    String              aLookAheadLine;
 
+    bool                ReadNextLine( String& rStr );
+    bool                LookAhead();
+    DATASET             GetNumberDataset( const sal_Unicode* pPossibleNumericData );
 	static inline BOOL	IsBOT( const sal_Unicode* pRef );
 	static inline BOOL	IsEOD( const sal_Unicode* pRef );
 	static inline BOOL	Is1_0( const sal_Unicode* pRef );

sc/source/filter/inc/xestyle.hxx

         @param nXFFlags  Additional flags allowing to control the creation of an XF.
         @param nForceScNumFmt  The number format to be exported, e.g. formula
             result type. This format will always overwrite the cell's number format.
+        @param bForceLineBreak  true = Set line break flag unconditionally.
+            This is required for cells that contain multi-line text.
         @return  A unique XF record ID. */
     sal_uInt32          InsertWithNumFmt(
                             const ScPatternAttr* pPattern, sal_Int16 nScript,
-                            ULONG nForceScNumFmt );
+                            ULONG nForceScNumFmt,
+                            bool bForceLineBreak );
     /** Inserts the passed cell style. Creates a style XF record and a STYLE record.
         @return  A unique XF record ID. */
     sal_uInt32          InsertStyle( const SfxStyleSheetBase* pStyleSheet );

sc/source/filter/qpro/qpro.cxx

                 readString( aLabel, getLength() - 7 );
                 nStyle = nStyle >> 3;
                 pStyle->SetFormat( pDoc, nCol, nRow, nTab, nStyle );
-                pDoc->PutCell( nCol, nRow, nTab, new ScStringCell( aLabel ), (BOOL) TRUE );
+                pDoc->PutCell( nCol, nRow, nTab, ScBaseCell::CreateTextCell( aLabel, pDoc ), (BOOL) TRUE );
                 }
                 break;
 

sc/source/filter/xml/XMLExportIterator.cxx

 	aShapeList(),
 	aDetectiveObjVec(),
     nValidationIndex(-1),
+    pBaseCell(NULL),
 	bIsAutoStyle( sal_False ),
 	bHasShape( sal_False ),
 	bIsMergedBase( sal_False ),

sc/source/filter/xml/XMLExportIterator.hxx

 struct	ScMyCell;
 class	ScXMLExport;
 class	ScFormatRangeStyles;
+class   ScBaseCell;
 
 //==============================================================================
 
 	sal_Int32					nNumberFormat;
 	com::sun::star::table::CellContentType	nType;
 
+    ScBaseCell*                 pBaseCell;
+
 	sal_Bool					bIsAutoStyle;
 
 	sal_Bool					bHasShape;

sc/source/filter/xml/xmlexprt.cxx

 
 	if (!bIsEmpty)
 	{
-		if ((aCell.nType == table::CellContentType_TEXT) && IsEditCell(aCell))
+        if ((aCell.nType == table::CellContentType_TEXT && IsEditCell(aCell)) || 
+            (aCell.nType == table::CellContentType_FORMULA && IsMultiLineFormulaCell(aCell)))
 		{
             bEditCell = sal_True;
             uno::Reference<text::XText> xText(xCurrentTableCellRange->getCellByPosition(aCell.aCellAddress.Column, aCell.aCellAddress.Row), uno::UNO_QUERY);
 	return (aCell1.nType == aCell2.nType);
 }
 
-sal_Bool ScXMLExport::IsEditCell(const com::sun::star::table::CellAddress& aAddress) const
+sal_Bool ScXMLExport::IsEditCell(const com::sun::star::table::CellAddress& aAddress, ScMyCell* pMyCell) const
 {
 	ScAddress aCoreAddress(static_cast<SCCOL>(aAddress.Column),
 						static_cast<SCROW>(aAddress.Row),
 						static_cast<SCTAB>(aAddress.Sheet));
 	ScBaseCell* pBaseCell = GetDocument() ? GetDocument()->GetCell(aCoreAddress) : NULL;
+    if (pMyCell)
+        pMyCell->pBaseCell = pBaseCell;
+
 	if (pBaseCell)
 		return (pBaseCell->GetCellType() == CELLTYPE_EDIT);
 	return sal_False;
 		return rCell.bIsEditCell;
 	else
 	{
-		rCell.bIsEditCell = IsEditCell(rCell.aCellAddress);
+ 		rCell.bIsEditCell = IsEditCell(rCell.aCellAddress, &rCell);
 		rCell.bKnowWhetherIsEditCell = sal_True;
 		return rCell.bIsEditCell;
 	}
 }
 
+sal_Bool ScXMLExport::IsMultiLineFormulaCell(ScMyCell& rCell) const
+{
+    if (rCell.pBaseCell)
+    {
+        if (rCell.pBaseCell->GetCellType() != CELLTYPE_FORMULA)
+            return false;
+
+        return static_cast<ScFormulaCell*>(rCell.pBaseCell)->IsMultilineResult();
+    }
+
+    ScAddress aAddr(static_cast<SCCOL>(rCell.aCellAddress.Column), 
+                    static_cast<SCROW>(rCell.aCellAddress.Row),
+                    static_cast<SCTAB>(rCell.aCellAddress.Sheet));
+    ScBaseCell* pBaseCell = pDoc ? pDoc->GetCell(aAddr) : NULL;
+    if (!pBaseCell)
+        return false;
+
+    rCell.pBaseCell = pBaseCell;
+    if (rCell.pBaseCell->GetCellType() != CELLTYPE_FORMULA)
+        return false;
+
+    return static_cast<ScFormulaCell*>(rCell.pBaseCell)->IsMultilineResult();
+}
+
 //UNUSED2008-05  sal_Bool ScXMLExport::IsAnnotationEqual(const uno::Reference<table::XCell>& /* xCell1 */,
 //UNUSED2008-05                                          const uno::Reference<table::XCell>& /* xCell2 */)
 //UNUSED2008-05  {

sc/source/filter/xml/xmlexprt.hxx

 class ScChartListener;
 class SfxItemPool;
 class ScAddress;
+class ScBaseCell;
 
 typedef std::vector< com::sun::star::uno::Reference < com::sun::star::drawing::XShapes > > ScMyXShapesVec;
 
 	void SetRepeatAttribute (const sal_Int32 nEqualCellCount);
 
 	sal_Bool IsCellTypeEqual (const ScMyCell& aCell1, const ScMyCell& aCell2) const;
-	sal_Bool IsEditCell(const com::sun::star::table::CellAddress& aAddress) const;
+ 	sal_Bool IsEditCell(const com::sun::star::table::CellAddress& aAddress, ScMyCell* pMyCell = NULL) const;
 //UNUSED2008-05  sal_Bool IsEditCell(const com::sun::star::uno::Reference <com::sun::star::table::XCell>& xCell) const;
 	sal_Bool IsEditCell(ScMyCell& rCell) const;
+    sal_Bool IsMultiLineFormulaCell(ScMyCell& rCell) const;
 //UNUSED2008-05  sal_Bool IsAnnotationEqual(const com::sun::star::uno::Reference<com::sun::star::table::XCell>& xCell1,
 //UNUSED2008-05                             const com::sun::star::uno::Reference<com::sun::star::table::XCell>& xCell2);
 	sal_Bool IsCellEqual (ScMyCell& aCell1, ScMyCell& aCell2);

sc/source/ui/app/transobj.cxx

 			BOOL bIncludeFiltered = pDoc->IsCutMode() || bUsedForLink;
 
 			ScImportExport aObj( pDoc, aBlock );
+            if ( bUsedForLink )
+                aObj.SetExportTextOptions( ScExportTextOptions( ScExportTextOptions::ToSpace, ' ', false ) );
 			aObj.SetFormulas( pDoc->GetViewOptions().GetOption( VOPT_FORMULAS ) );
 			aObj.SetIncludeFiltered( bIncludeFiltered );
 
 				{
 					String aStr;
 					pFCell->GetString(aStr);
-					pNew = new ScStringCell( aStr );
+                    if ( pFCell->IsMultilineResult() )
+                        pNew = new ScEditCell( aStr, pDestDoc );
+                    else
+                        pNew = new ScStringCell( aStr );
 				}
 				pDestDoc->PutCell( nCol,nRow,nDestTab, pNew );
 

sc/source/ui/docshell/docsh.cxx

 			}
 			bSetColWidths = TRUE;
 			bSetSimpleTextColWidths = TRUE;
+			bSetRowHeights = TRUE;
 		}
 		else if (aFltName.EqualsAscii(pFilterSylk))
 		{
 				SetError(eError);
 			bSetColWidths = TRUE;
 			bSetSimpleTextColWidths = TRUE;
+			bSetRowHeights = TRUE;
 		}
 		else if (aFltName.EqualsAscii(pFilterQPro6))
         {

sc/source/ui/docshell/docsh4.cxx

 		if( aDdeTextFmt.EqualsAscii( "CSV" ) ||
 			aDdeTextFmt.EqualsAscii( "FCSV" ) )
 			aObj.SetSeparator( ',' );
+        aObj.SetExportTextOptions( ScExportTextOptions( ScExportTextOptions::ToSpace, 0, false ) );
 		return aObj.ExportData( rMimeType, rValue ) ? 1 : 0;
 	}
 
 	ScImportExport aObj( &aDocument, rItem );
+    aObj.SetExportTextOptions( ScExportTextOptions( ScExportTextOptions::ToSpace, 0, false ) );
 	if( aObj.IsRef() )
 		return aObj.ExportData( rMimeType, rValue ) ? 1 : 0;
 	return 0;

sc/source/ui/docshell/impex.cxx

 
 //========================================================================
 
+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
 
       nSizeLimit( 0 ), cSep( '\t' ), cStr( '"' ), 
       bFormulas( FALSE ), bIncludeFiltered( TRUE ),
       bAll( TRUE ), bSingle( TRUE ), bUndo( FALSE ),
-      bOverflow( FALSE ), mbApi( true )
+      bOverflow( FALSE ), mbApi( true ), mExportTextOptions()
 {
 	pUndoDoc = NULL;
 	pExtOptions = NULL;
       nSizeLimit( 0 ), cSep( '\t' ), cStr( '"' ), 
       bFormulas( FALSE ), bIncludeFiltered( TRUE ),
       bAll( FALSE ), bSingle( TRUE ), bUndo( BOOL( pDocSh != NULL ) ),
-      bOverflow( FALSE ), mbApi( true )
+      bOverflow( FALSE ), mbApi( true ), mExportTextOptions()
 {
 	pUndoDoc = NULL;
 	pExtOptions = NULL;
       nSizeLimit( 0 ), cSep( '\t' ), cStr( '"' ),
       bFormulas( FALSE ), bIncludeFiltered( TRUE ),
       bAll( FALSE ), bSingle( FALSE ), bUndo( BOOL( pDocSh != NULL ) ),
-      bOverflow( FALSE ), mbApi( true )
+      bOverflow( FALSE ), mbApi( true ), mExportTextOptions()
 {
 	pUndoDoc = NULL;
 	pExtOptions = NULL;
       nSizeLimit( 0 ), cSep( '\t' ), cStr( '"' ),
       bFormulas( FALSE ), bIncludeFiltered( TRUE ),
       bAll( FALSE ), bSingle( TRUE ), bUndo( BOOL( pDocSh != NULL ) ),
-      bOverflow( FALSE ), mbApi( true )
+      bOverflow( FALSE ), mbApi( true ), mExportTextOptions()
 {
 	pUndoDoc = NULL;
 	pExtOptions = NULL;
 }
 
 
+// This function could be replaced by endlub()
 // static
 void ScImportExport::WriteUnicodeOrByteEndl( SvStream& rStrm )
 {
 	DQM_SEPARATE	// end one string and begin next
 };
 
-const sal_Unicode* lcl_ScanString( const sal_Unicode* p, String& rString,
+static const sal_Unicode* lcl_ScanString( const sal_Unicode* p, String& rString,
 			sal_Unicode cStr, DoubledQuoteMode eMode )
 {
 	p++;	//! jump over opening quote
 	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, '"' );
 
-void lcl_WriteString( SvStream& rStrm, String& rString, sal_Unicode cStr )
+    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;
 	}
+}
 
-	rString.Insert( cStr, 0 );
-	rString.Append( cStr );
+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 );
 }
 	SCCOL nEndCol = aRange.aEnd.Col();
 	SCROW nEndRow = aRange.aEnd.Row();
 	String aCell;
+    bool bConvertLF = (GetSystemLineEnd() != LINEEND_LF);
 
 	for (nRow = nStartRow; nRow <= nEndRow; nRow++)
 	{
 						{
 							pDoc->GetFormula( nCol, nRow, aRange.aStart.Tab(), aCell, TRUE );
 							if( aCell.Search( cSep ) != STRING_NOTFOUND )
-								lcl_WriteString( rStrm, aCell, cStr );
+								lcl_WriteString( rStrm, aCell, cStr, cStr );
 							else
 								lcl_WriteSimpleString( rStrm, aCell );
 						}
 						else
 						{
-							pDoc->GetString( nCol, nRow, aRange.aStart.Tab(), aCell );
-							if( aCell.Search( cSep ) != STRING_NOTFOUND )
-								lcl_WriteString( rStrm, aCell, cStr );
-							else
-								lcl_WriteSimpleString( rStrm, aCell );
+                            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;
 					default:
 					{
 						pDoc->GetString( nCol, nRow, aRange.aStart.Tab(), aCell );
-						if( aCell.Search( cSep ) != STRING_NOTFOUND )
-							lcl_WriteString( rStrm, aCell, cStr );
-						else
-							lcl_WriteSimpleString( rStrm, 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 )
 {
 	BOOL bOk = TRUE;
 	BOOL bMyDoc = FALSE;
+    SylkVersion eVersion = SYLK_OTHER;
 
 	// US-English separators for StringToDouble
 	sal_Unicode cDecSep = '.';
                             if( *p == '"' )
                             {
                                 bText = TRUE;
-                                aText = '\'';       // force string cell
-                                p = lcl_ScanString( p, aText, '"', DQM_ESCAPE );
+                                aText.Erase();
+                                p = lcl_ScanSylkString( p, aText, eVersion);
                             }
                             else
                                 bText = FALSE;
                             if ( !(*q == ';' && *(q+1) == 'I') )
                             {   // don't ignore value
                                 if( bText )
-                                    pDoc->SetString( nCol, nRow, aRange.aStart.Tab(), aText );
+                                {
+                                    pDoc->PutCell( nCol, nRow, aRange.aStart.Tab(), 
+                                            ScBaseCell::CreateTextCell( aText, pDoc),
+                                            (BOOL) TRUE);
+                                }
                                 else
                                 {
                                     double fVal = rtl_math_uStringToDouble( p,
 							if( !bMyDoc || !bData )
 								break;
                             aText = '=';
-							if( *p == '"' )
-                                p = lcl_ScanString( p, aText, '"', DQM_ESCAPE );
-							else
-							{
-                                const sal_Unicode* q = p;
-                                while( *p && *p != ';' )
-                                    p++;
-                                aText.Append( q, sal::static_int_cast<xub_StrLen>( p-q ) );
-							}
+                            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
             else if( cTag == 'I' && *p == 'D' )
 			{
 				aLine.Erase( 0, 4 );
-				bMyDoc = aLine.EqualsAscii( "SCALC3" );
+				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;
 	String aCellStr;
 	String aValStr;
 	lcl_WriteSimpleString( rStrm,
-			String::CreateFromAscii(RTL_CONSTASCII_STRINGPARAM( "ID;PSCALC3" )) );
+            String( RTL_CONSTASCII_USTRINGPARAM( "ID;PCALCOOO32")));
 	WriteUnicodeOrByteEndl( rStrm );
 
 	for (nRow = nStartRow; nRow <= nEndRow; nRow++)
 				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 += String::CreateFromInt32( r );
 					aBufStr.AppendAscii(RTL_CONSTASCII_STRINGPARAM( ";K" ));
 					lcl_WriteSimpleString( rStrm, aBufStr );
-					lcl_WriteString( rStrm, aCellStr, '"' );
+					lcl_WriteString( rStrm, aCellStr, '"', ';' );
 
 				checkformula:
 					if( bForm )
                         }
                         lcl_WriteSimpleString( rStrm, aPrefix );
                         if ( aCellStr.Len() )
-                        {
-                            if( aCellStr.Search( ';' ) != STRING_NOTFOUND )
-                                lcl_WriteString( rStrm, aCellStr, '"' );
-                            else
-                                lcl_WriteSimpleString( rStrm, aCellStr );
-                        }
+                            lcl_WriteString( rStrm, aCellStr, 0, ';' );
 					}
 					WriteUnicodeOrByteEndl( rStrm );
 					break;

sc/source/ui/docshell/servobj.cxx

 		if( aDdeTextFmt.EqualsAscii( "CSV" ) ||
 			aDdeTextFmt.EqualsAscii( "FCSV" ) )
 			aObj.SetSeparator( ',' );
+        aObj.SetExportTextOptions( ScExportTextOptions( ScExportTextOptions::ToSpace, ' ', false ) );
 		return aObj.ExportData( rMimeType, rData ) ? 1 : 0;
 	}
 
 	ScImportExport aObj( pDoc, aRange );
+    aObj.SetExportTextOptions( ScExportTextOptions( ScExportTextOptions::ToSpace, ' ', false ) );
 	if( aObj.IsRef() )
 		return aObj.ExportData( rMimeType, rData ) ? 1 : 0;
 	return 0;

sc/source/ui/inc/impex.hxx

 class SfxMedium;
 class ScAsciiOptions;
 
+struct ScExportTextOptions
+{
+    enum NewlineConversion { ToSystem, ToSpace, None };
+    ScExportTextOptions( NewlineConversion eNewlineConversion = ToSystem, sal_Unicode cSeparatorConvertTo = 0, bool bAddQuotes = false ) : 
+        meNewlineConversion( eNewlineConversion ), mcSeparatorConvertTo( cSeparatorConvertTo ), mbAddQuotes( bAddQuotes ) {}
+
+    NewlineConversion meNewlineConversion;
+    sal_Unicode mcSeparatorConvertTo;   // Convert separator to this character
+    bool mbAddQuotes;
+};
+
 class ScImportExport
 {
 	ScDocShell* pDocSh;
 	BOOL		bUndo;					// Mit Undo?
 	BOOL		bOverflow;				// zuviele Zeilen/Spalten
     bool        mbApi;
+    ScExportTextOptions mExportTextOptions;
 
 	ScAsciiOptions*	pExtOptions;		// erweiterte Optionen
 
 
     bool IsApi() const { return mbApi; }
     void SetApi( bool bApi ) { mbApi = bApi; }
+    const ScExportTextOptions& GetExportTextOptions() { return mExportTextOptions; }
+    void SetExportTextOptions( const ScExportTextOptions& options ) { mExportTextOptions = options; }
 };
 
 

sc/source/ui/view/cellsh2.cxx

                     DBG_ASSERT( pDoc, "ScCellShell::ExecuteDB: SID_TEXT_TO_COLUMNS - pDoc is null!" );
 
                     ScImportExport aExport( pDoc, aRange );
+                    aExport.SetExportTextOptions( ScExportTextOptions( ScExportTextOptions::None, 0, false ) );
 
                     // #i87703# text to columns fails with tab separator
                     aExport.SetDelimiter( static_cast< sal_Unicode >( 0 ) );

sc/source/ui/view/output2.cxx

 				}
 				if (bDoCell && !bNeedEdit)
 				{
-					if ( pCell->GetCellType() == CELLTYPE_FORMULA )
+					BOOL bFormulaCell = (pCell->GetCellType() == CELLTYPE_FORMULA );
+					if ( bFormulaCell )
 						lcl_CreateInterpretProgress( bProgress, pDoc, (ScFormulaCell*)pCell );
 					if ( aVars.SetText(pCell) )
 						pOldPattern = NULL;
-                    bNeedEdit = aVars.HasEditCharacters();
+                    bNeedEdit = aVars.HasEditCharacters() ||
+					                (bFormulaCell && ((ScFormulaCell*)pCell)->IsMultilineResult());
                 }
                 if (bDoCell && !bNeedEdit)
                 {