Update ooo320-m1
[ooovba.git] / sc / source / filter / excel / xehelper.cxx
blob54043cef00f0b9748d4cba93940d0e8147c58fa7
1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: xehelper.cxx,v $
10 * $Revision: 1.31.148.1 $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_sc.hxx"
33 #include <com/sun/star/i18n/XBreakIterator.hpp>
34 #include <com/sun/star/i18n/ScriptType.hpp>
35 #include <sfx2/objsh.hxx>
36 #include <vcl/font.hxx>
37 #include <tools/urlobj.hxx>
38 #include <svtools/itemset.hxx>
39 #include <svtools/ctrltool.hxx>
40 #include <svx/svdotext.hxx>
41 #include <svx/outlobj.hxx>
42 #include "scitems.hxx"
43 #include <svx/fhgtitem.hxx>
44 #include <svx/flstitem.hxx>
45 #include <svx/colritem.hxx>
46 #include <svx/eeitem.hxx>
47 #include <svx/flditem.hxx>
48 #include <svx/escpitem.hxx>
49 #include <svx/svxfont.hxx>
51 #define _SVSTDARR_USHORTS
52 #include <svtools/svstdarr.hxx>
53 #include "document.hxx"
54 #include "docpool.hxx"
55 #include "cell.hxx"
56 #include "editutil.hxx"
57 #include "patattr.hxx"
58 #include "xestyle.hxx"
59 #include "fprogressbar.hxx"
60 #include "xltracer.hxx"
61 #include "xecontent.hxx"
62 #include "xelink.hxx"
63 #include "xehelper.hxx"
65 using ::rtl::OUString;
66 using ::com::sun::star::uno::Reference;
67 using ::com::sun::star::i18n::XBreakIterator;
69 // Export progress bar ========================================================
71 XclExpProgressBar::XclExpProgressBar( const XclExpRoot& rRoot ) :
72 XclExpRoot( rRoot ),
73 mxProgress( new ScfProgressBar( rRoot.GetDocShell(), STR_SAVE_DOC ) ),
74 mpSubProgress( 0 ),
75 mpSubRowCreate( 0 ),
76 mpSubRowFinal( 0 ),
77 mnSegRowFinal( SCF_INV_SEGMENT ),
78 mnRowCount( 0 )
82 XclExpProgressBar::~XclExpProgressBar()
86 void XclExpProgressBar::Initialize()
88 const ScDocument& rDoc = GetDoc();
89 const XclExpTabInfo& rTabInfo = GetTabInfo();
90 SCTAB nScTabCount = rTabInfo.GetScTabCount();
92 // *** segment: creation of ROW records *** -------------------------------
94 sal_Int32 nSegRowCreate = mxProgress->AddSegment( 2000 );
95 mpSubRowCreate = &mxProgress->GetSegmentProgressBar( nSegRowCreate );
96 maSubSegRowCreate.resize( nScTabCount, SCF_INV_SEGMENT );
98 for( SCTAB nScTab = 0; nScTab < nScTabCount; ++nScTab )
100 if( rTabInfo.IsExportTab( nScTab ) )
102 SCCOL nLastUsedScCol;
103 SCROW nLastUsedScRow;
104 rDoc.GetTableArea( nScTab, nLastUsedScCol, nLastUsedScRow );
105 sal_Size nSegSize = static_cast< sal_Size >( nLastUsedScRow + 1 );
106 maSubSegRowCreate[ nScTab ] = mpSubRowCreate->AddSegment( nSegSize );
110 // *** segment: writing all ROW records *** -------------------------------
112 mnSegRowFinal = mxProgress->AddSegment( 1000 );
113 // sub progress bar and segment are created later in ActivateFinalRowsSegment()
116 void XclExpProgressBar::IncRowRecordCount()
118 ++mnRowCount;
121 void XclExpProgressBar::ActivateCreateRowsSegment()
123 DBG_ASSERT( (0 <= GetCurrScTab()) && (GetCurrScTab() < GetTabInfo().GetScTabCount()),
124 "XclExpProgressBar::ActivateCreateRowsSegment - invalid sheet" );
125 sal_Int32 nSeg = maSubSegRowCreate[ GetCurrScTab() ];
126 DBG_ASSERT( nSeg != SCF_INV_SEGMENT, "XclExpProgressBar::ActivateCreateRowsSegment - invalid segment" );
127 if( nSeg != SCF_INV_SEGMENT )
129 mpSubProgress = mpSubRowCreate;
130 mpSubProgress->ActivateSegment( nSeg );
132 else
133 mpSubProgress = 0;
136 void XclExpProgressBar::ActivateFinalRowsSegment()
138 if( !mpSubRowFinal && (mnRowCount > 0) )
140 mpSubRowFinal = &mxProgress->GetSegmentProgressBar( mnSegRowFinal );
141 mpSubRowFinal->AddSegment( mnRowCount );
143 mpSubProgress = mpSubRowFinal;
144 if( mpSubProgress )
145 mpSubProgress->Activate();
148 void XclExpProgressBar::Progress()
150 if( mpSubProgress && !mpSubProgress->IsFull() )
151 mpSubProgress->Progress();
154 // Calc->Excel cell address/range conversion ==================================
156 namespace {
158 /** Fills the passed Excel address with the passed Calc cell coordinates without checking any limits. */
159 inline void lclFillAddress( XclAddress& rXclPos, SCCOL nScCol, SCROW nScRow )
161 rXclPos.mnCol = static_cast< sal_uInt16 >( nScCol );
162 rXclPos.mnRow = static_cast< sal_uInt16 >( nScRow );
165 } // namespace
167 // ----------------------------------------------------------------------------
169 XclExpAddressConverter::XclExpAddressConverter( const XclExpRoot& rRoot ) :
170 XclAddressConverterBase( rRoot.GetTracer(), rRoot.GetXclMaxPos() )
174 // cell address ---------------------------------------------------------------
176 bool XclExpAddressConverter::CheckAddress( const ScAddress& rScPos, bool bWarn )
178 // ScAddress::operator<=() doesn't do what we want here
179 bool bValidCol = (0 <= rScPos.Col()) && (rScPos.Col() <= maMaxPos.Col());
180 bool bValidRow = (0 <= rScPos.Row()) && (rScPos.Row() <= maMaxPos.Row());
181 bool bValidTab = (0 <= rScPos.Tab()) && (rScPos.Tab() <= maMaxPos.Tab());
183 bool bValid = bValidCol && bValidRow && bValidTab;
184 if( !bValid && bWarn )
186 mbColTrunc |= !bValidCol;
187 mbRowTrunc |= !bValidRow;
188 mbTabTrunc |= (rScPos.Tab() > maMaxPos.Tab()); // do not warn for deleted refs
189 mrTracer.TraceInvalidAddress( rScPos, maMaxPos );
191 return bValid;
194 bool XclExpAddressConverter::ConvertAddress( XclAddress& rXclPos,
195 const ScAddress& rScPos, bool bWarn )
197 bool bValid = CheckAddress( rScPos, bWarn );
198 if( bValid )
199 lclFillAddress( rXclPos, rScPos.Col(), rScPos.Row() );
200 return bValid;
203 XclAddress XclExpAddressConverter::CreateValidAddress( const ScAddress& rScPos, bool bWarn )
205 XclAddress aXclPos( ScAddress::UNINITIALIZED );
206 if( !ConvertAddress( aXclPos, rScPos, bWarn ) )
207 lclFillAddress( aXclPos, ::std::min( rScPos.Col(), maMaxPos.Col() ), ::std::min( rScPos.Row(), maMaxPos.Row() ) );
208 return aXclPos;
211 // cell range -----------------------------------------------------------------
213 bool XclExpAddressConverter::CheckRange( const ScRange& rScRange, bool bWarn )
215 return CheckAddress( rScRange.aStart, bWarn ) && CheckAddress( rScRange.aEnd, bWarn );
218 bool XclExpAddressConverter::ValidateRange( ScRange& rScRange, bool bWarn )
220 rScRange.Justify();
222 // check start position
223 bool bValidStart = CheckAddress( rScRange.aStart, bWarn );
224 if( bValidStart )
226 // check & correct end position
227 ScAddress& rScEnd = rScRange.aEnd;
228 if( !CheckAddress( rScEnd, bWarn ) )
230 rScEnd.SetCol( ::std::min( rScEnd.Col(), maMaxPos.Col() ) );
231 rScEnd.SetRow( ::std::min( rScEnd.Row(), maMaxPos.Row() ) );
232 rScEnd.SetTab( ::std::min( rScEnd.Tab(), maMaxPos.Tab() ) );
236 return bValidStart;
239 bool XclExpAddressConverter::ConvertRange( XclRange& rXclRange,
240 const ScRange& rScRange, bool bWarn )
242 // check start position
243 bool bValidStart = CheckAddress( rScRange.aStart, bWarn );
244 if( bValidStart )
246 lclFillAddress( rXclRange.maFirst, rScRange.aStart.Col(), rScRange.aStart.Row() );
248 // check & correct end position
249 SCCOL nScCol2 = rScRange.aEnd.Col();
250 SCROW nScRow2 = rScRange.aEnd.Row();
251 if( !CheckAddress( rScRange.aEnd, bWarn ) )
253 nScCol2 = ::std::min( nScCol2, maMaxPos.Col() );
254 nScRow2 = ::std::min( nScRow2, maMaxPos.Row() );
256 lclFillAddress( rXclRange.maLast, nScCol2, nScRow2 );
258 return bValidStart;
261 //UNUSED2008-05 XclRange XclExpAddressConverter::CreateValidRange( const ScRange& rScRange, bool bWarn )
262 //UNUSED2008-05 {
263 //UNUSED2008-05 return XclRange(
264 //UNUSED2008-05 CreateValidAddress( rScRange.aStart, bWarn ),
265 //UNUSED2008-05 CreateValidAddress( rScRange.aEnd, bWarn ) );
266 //UNUSED2008-05 }
268 // cell range list ------------------------------------------------------------
270 //UNUSED2008-05 bool XclExpAddressConverter::CheckRangeList( const ScRangeList& rScRanges, bool bWarn )
271 //UNUSED2008-05 {
272 //UNUSED2008-05 for( ULONG nIdx = 0, nSize = rScRanges.Count(); nIdx < nSize; ++nIdx )
273 //UNUSED2008-05 if( const ScRange* pScRange = rScRanges.GetObject( nIdx ) )
274 //UNUSED2008-05 if( !CheckRange( *pScRange, bWarn ) )
275 //UNUSED2008-05 return false;
276 //UNUSED2008-05 return true;
277 //UNUSED2008-05 }
279 void XclExpAddressConverter::ValidateRangeList( ScRangeList& rScRanges, bool bWarn )
281 ULONG nIdx = rScRanges.Count();
282 while( nIdx )
284 --nIdx; // backwards to keep nIdx valid
285 ScRange* pScRange = rScRanges.GetObject( nIdx );
286 if( pScRange && !CheckRange( *pScRange, bWarn ) )
287 delete rScRanges.Remove( nIdx );
291 void XclExpAddressConverter::ConvertRangeList( XclRangeList& rXclRanges,
292 const ScRangeList& rScRanges, bool bWarn )
294 rXclRanges.clear();
295 for( ULONG nPos = 0, nCount = rScRanges.Count(); nPos < nCount; ++nPos )
297 if( const ScRange* pScRange = rScRanges.GetObject( nPos ) )
299 XclRange aXclRange( ScAddress::UNINITIALIZED );
300 if( ConvertRange( aXclRange, *pScRange, bWarn ) )
301 rXclRanges.push_back( aXclRange );
306 // EditEngine->String conversion ==============================================
308 namespace {
310 String lclGetUrlRepresentation( const SvxURLField& rUrlField )
312 String aRepr( rUrlField.GetRepresentation() );
313 // no representation -> use URL
314 return aRepr.Len() ? aRepr : rUrlField.GetURL();
317 } // namespace
319 // ----------------------------------------------------------------------------
321 XclExpHyperlinkHelper::XclExpHyperlinkHelper( const XclExpRoot& rRoot, const ScAddress& rScPos ) :
322 XclExpRoot( rRoot ),
323 maScPos( rScPos ),
324 mbMultipleUrls( false )
328 XclExpHyperlinkHelper::~XclExpHyperlinkHelper()
332 String XclExpHyperlinkHelper::ProcessUrlField( const SvxURLField& rUrlField )
334 String aUrlRepr;
336 if( GetBiff() == EXC_BIFF8 ) // no HLINK records in BIFF2-BIFF7
338 // there was/is already a HLINK record
339 mbMultipleUrls = mxLinkRec.is();
341 mxLinkRec.reset( new XclExpHyperlink( GetRoot(), rUrlField, maScPos ) );
343 if( const String* pRepr = mxLinkRec->GetRepr() )
344 aUrlRepr = *pRepr;
346 // add URL to note text
347 ScGlobal::AddToken( maUrlList, rUrlField.GetURL(), '\n' );
350 // no hyperlink representation from Excel HLINK record -> use it from text field
351 return aUrlRepr.Len() ? aUrlRepr : lclGetUrlRepresentation( rUrlField );
354 bool XclExpHyperlinkHelper::HasLinkRecord() const
356 return !mbMultipleUrls && mxLinkRec.is();
359 XclExpHyperlinkHelper::XclExpHyperlinkRef XclExpHyperlinkHelper::GetLinkRecord()
361 if( HasLinkRecord() )
362 return mxLinkRec;
363 return XclExpHyperlinkRef();
366 // ----------------------------------------------------------------------------
368 namespace {
370 /** Creates a new formatted string from the passed unformatted string.
372 Creates a Unicode string or a byte string, depending on the current BIFF
373 version contained in the passed XclExpRoot object. May create a formatted
374 string object, if the text contains different script types.
376 @param pCellAttr
377 Cell attributes used for font formatting.
378 @param nFlags
379 Modifiers for string export.
380 @param nMaxLen
381 The maximum number of characters to store in this string.
382 @return
383 The new string object.
385 XclExpStringRef lclCreateFormattedString(
386 const XclExpRoot& rRoot, const String& rText, const ScPatternAttr* pCellAttr,
387 XclStrFlags nFlags, sal_uInt16 nMaxLen )
389 /* Create an empty Excel string object with correctly initialized BIFF mode,
390 because this function only uses Append() functions that require this. */
391 XclExpStringRef xString = XclExpStringHelper::CreateString( rRoot, EMPTY_STRING, nFlags, nMaxLen );
393 // script type handling
394 Reference< XBreakIterator > xBreakIt = rRoot.GetDoc().GetBreakIterator();
395 namespace ApiScriptType = ::com::sun::star::i18n::ScriptType;
396 // #i63255# get script type for leading weak characters
397 sal_Int16 nLastScript = XclExpStringHelper::GetLeadingScriptType( rRoot, rText );
399 // font buffer and cell item set
400 XclExpFontBuffer& rFontBuffer = rRoot.GetFontBuffer();
401 const SfxItemSet& rItemSet = pCellAttr ? pCellAttr->GetItemSet() : rRoot.GetDoc().GetDefPattern()->GetItemSet();
403 // process all script portions
404 OUString aOUText( rText );
405 sal_Int32 nPortionPos = 0;
406 sal_Int32 nTextLen = aOUText.getLength();
407 while( nPortionPos < nTextLen )
409 // get script type and end position of next script portion
410 sal_Int16 nScript = xBreakIt->getScriptType( aOUText, nPortionPos );
411 sal_Int32 nPortionEnd = xBreakIt->endOfScript( aOUText, nPortionPos, nScript );
413 // reuse previous script for following weak portions
414 if( nScript == ApiScriptType::WEAK )
415 nScript = nLastScript;
417 // construct font from current text portion
418 SvxFont aFont( XclExpFontBuffer::GetFontFromItemSet( rItemSet, nScript ) );
420 // Excel start position of this portion
421 sal_uInt16 nXclPortionStart = xString->Len();
422 // add portion text to Excel string
423 XclExpStringHelper::AppendString( *xString, rRoot, aOUText.copy( nPortionPos, nPortionEnd - nPortionPos ) );
424 if( nXclPortionStart < xString->Len() )
426 // insert font into buffer
427 sal_uInt16 nFontIdx = rFontBuffer.Insert( aFont, EXC_COLOR_CELLTEXT );
428 // insert font index into format run vector
429 xString->AppendFormat( nXclPortionStart, nFontIdx );
432 // go to next script portion
433 nLastScript = nScript;
434 nPortionPos = nPortionEnd;
437 return xString;
440 /** Creates a new formatted string from an edit engine text object.
442 Creates a Unicode string or a byte string, depending on the current BIFF
443 version contained in the passed XclExpRoot object.
445 @param rEE
446 The edit engine in use. The text object must already be set.
447 @param nFlags
448 Modifiers for string export.
449 @param nMaxLen
450 The maximum number of characters to store in this string.
451 @return
452 The new string object.
454 XclExpStringRef lclCreateFormattedString(
455 const XclExpRoot& rRoot, EditEngine& rEE, XclExpHyperlinkHelper* pLinkHelper,
456 XclStrFlags nFlags, sal_uInt16 nMaxLen )
458 /* Create an empty Excel string object with correctly initialized BIFF mode,
459 because this function only uses Append() functions that require this. */
460 XclExpStringRef xString = XclExpStringHelper::CreateString( rRoot, EMPTY_STRING, nFlags, nMaxLen );
462 // font buffer and helper item set for edit engine -> Calc item conversion
463 XclExpFontBuffer& rFontBuffer = rRoot.GetFontBuffer();
464 SfxItemSet aItemSet( *rRoot.GetDoc().GetPool(), ATTR_PATTERN_START, ATTR_PATTERN_END );
466 // script type handling
467 Reference< XBreakIterator > xBreakIt = rRoot.GetDoc().GetBreakIterator();
468 namespace ApiScriptType = ::com::sun::star::i18n::ScriptType;
469 // #i63255# get script type for leading weak characters
470 sal_Int16 nLastScript = XclExpStringHelper::GetLeadingScriptType( rRoot, rEE.GetText() );
472 // process all paragraphs
473 sal_uInt16 nParaCount = rEE.GetParagraphCount();
474 for( sal_uInt16 nPara = 0; nPara < nParaCount; ++nPara )
476 ESelection aSel( nPara, 0 );
477 String aParaText( rEE.GetText( nPara ) );
479 SvUShorts aPosList;
480 rEE.GetPortions( nPara, aPosList );
482 // process all portions in the paragraph
483 sal_uInt16 nPosCount = aPosList.Count();
484 for( sal_uInt16 nPos = 0; nPos < nPosCount; ++nPos )
486 aSel.nEndPos = static_cast< xub_StrLen >( aPosList.GetObject( nPos ) );
487 String aXclPortionText( aParaText, aSel.nStartPos, aSel.nEndPos - aSel.nStartPos );
489 aItemSet.ClearItem();
490 SfxItemSet aEditSet( rEE.GetAttribs( aSel ) );
491 ScPatternAttr::GetFromEditItemSet( aItemSet, aEditSet );
493 // get escapement value
494 short nEsc = GETITEM( aEditSet, SvxEscapementItem, EE_CHAR_ESCAPEMENT ).GetEsc();
496 // process text fields
497 bool bIsHyperlink = false;
498 if( aSel.nStartPos + 1 == aSel.nEndPos )
500 // test if the character is a text field
501 const SfxPoolItem* pItem;
502 if( aEditSet.GetItemState( EE_FEATURE_FIELD, FALSE, &pItem ) == SFX_ITEM_SET )
504 const SvxFieldData* pField = static_cast< const SvxFieldItem* >( pItem )->GetField();
505 if( const SvxURLField* pUrlField = PTR_CAST( SvxURLField, pField ) )
507 // convert URL field to string representation
508 aXclPortionText = pLinkHelper ?
509 pLinkHelper->ProcessUrlField( *pUrlField ) :
510 lclGetUrlRepresentation( *pUrlField );
511 bIsHyperlink = true;
513 else
515 DBG_ERRORFILE( "lclCreateFormattedString - unknown text field" );
516 aXclPortionText.Erase();
521 // Excel start position of this portion
522 sal_uInt16 nXclPortionStart = xString->Len();
523 // add portion text to Excel string
524 XclExpStringHelper::AppendString( *xString, rRoot, aXclPortionText );
525 if( (nXclPortionStart < xString->Len()) || (aParaText.Len() == 0) )
527 /* Construct font from current edit engine text portion. Edit engine
528 creates different portions for different script types, no need to loop. */
529 sal_Int16 nScript = xBreakIt->getScriptType( aXclPortionText, 0 );
530 if( nScript == ApiScriptType::WEAK )
531 nScript = nLastScript;
532 SvxFont aFont( XclExpFontBuffer::GetFontFromItemSet( aItemSet, nScript ) );
533 nLastScript = nScript;
535 // add escapement
536 aFont.SetEscapement( nEsc );
537 // modify automatic font color for hyperlinks
538 if( bIsHyperlink && (GETITEM( aItemSet, SvxColorItem, ATTR_FONT_COLOR ).GetValue().GetColor() == COL_AUTO) )
539 aFont.SetColor( Color( COL_LIGHTBLUE ) );
541 // insert font into buffer
542 sal_uInt16 nFontIdx = rFontBuffer.Insert( aFont, EXC_COLOR_CELLTEXT );
543 // insert font index into format run vector
544 xString->AppendFormat( nXclPortionStart, nFontIdx );
547 aSel.nStartPos = aSel.nEndPos;
550 // add trailing newline (important for correct character index calculation)
551 if( nPara + 1 < nParaCount )
552 XclExpStringHelper::AppendChar( *xString, rRoot, '\n' );
555 return xString;
558 } // namespace
560 // ----------------------------------------------------------------------------
562 XclExpStringRef XclExpStringHelper::CreateString(
563 const XclExpRoot& rRoot, const String& rString, XclStrFlags nFlags, sal_uInt16 nMaxLen )
565 XclExpStringRef xString( new XclExpString );
566 if( rRoot.GetBiff() == EXC_BIFF8 )
567 xString->Assign( rString, nFlags, nMaxLen );
568 else
569 xString->AssignByte( rString, rRoot.GetTextEncoding(), nFlags, nMaxLen );
570 return xString;
573 XclExpStringRef XclExpStringHelper::CreateString(
574 const XclExpRoot& rRoot, sal_Unicode cChar, XclStrFlags nFlags, sal_uInt16 nMaxLen )
576 XclExpStringRef xString = CreateString( rRoot, EMPTY_STRING, nFlags, nMaxLen );
577 AppendChar( *xString, rRoot, cChar );
578 return xString;
581 void XclExpStringHelper::AppendString( XclExpString& rXclString, const XclExpRoot& rRoot, const String& rString )
583 if( rRoot.GetBiff() == EXC_BIFF8 )
584 rXclString.Append( rString );
585 else
586 rXclString.AppendByte( rString, rRoot.GetTextEncoding() );
589 void XclExpStringHelper::AppendChar( XclExpString& rXclString, const XclExpRoot& rRoot, sal_Unicode cChar )
591 if( rRoot.GetBiff() == EXC_BIFF8 )
592 rXclString.Append( cChar );
593 else
594 rXclString.AppendByte( cChar, rRoot.GetTextEncoding() );
597 XclExpStringRef XclExpStringHelper::CreateCellString(
598 const XclExpRoot& rRoot, const ScStringCell& rStringCell, const ScPatternAttr* pCellAttr,
599 XclStrFlags nFlags, sal_uInt16 nMaxLen )
601 String aCellText;
602 rStringCell.GetString( aCellText );
603 return lclCreateFormattedString( rRoot, aCellText, pCellAttr, nFlags, nMaxLen );
606 XclExpStringRef XclExpStringHelper::CreateCellString(
607 const XclExpRoot& rRoot, const ScEditCell& rEditCell, const ScPatternAttr* pCellAttr,
608 XclExpHyperlinkHelper& rLinkHelper, XclStrFlags nFlags, sal_uInt16 nMaxLen )
610 XclExpStringRef xString;
611 if( const EditTextObject* pEditObj = rEditCell.GetData() )
613 // formatted cell
614 ScEditEngineDefaulter& rEE = rRoot.GetEditEngine();
615 BOOL bOldUpdateMode = rEE.GetUpdateMode();
616 rEE.SetUpdateMode( TRUE );
617 // default items
618 const SfxItemSet& rItemSet = pCellAttr ? pCellAttr->GetItemSet() : rRoot.GetDoc().GetDefPattern()->GetItemSet();
619 SfxItemSet* pEEItemSet = new SfxItemSet( rEE.GetEmptyItemSet() );
620 ScPatternAttr::FillToEditItemSet( *pEEItemSet, rItemSet );
621 rEE.SetDefaults( pEEItemSet ); // edit engine takes ownership
622 // create the string
623 rEE.SetText( *pEditObj );
624 xString = lclCreateFormattedString( rRoot, rEE, &rLinkHelper, nFlags, nMaxLen );
625 rEE.SetUpdateMode( bOldUpdateMode );
627 else
629 // unformatted cell
630 String aCellText;
631 rEditCell.GetString( aCellText );
632 xString = lclCreateFormattedString( rRoot, aCellText, pCellAttr, nFlags, nMaxLen );
634 return xString;
637 XclExpStringRef XclExpStringHelper::CreateString(
638 const XclExpRoot& rRoot, const SdrTextObj& rTextObj,
639 XclStrFlags nFlags, sal_uInt16 nMaxLen )
641 XclExpStringRef xString;
642 if( const OutlinerParaObject* pParaObj = rTextObj.GetOutlinerParaObject() )
644 EditEngine& rEE = rRoot.GetDrawEditEngine();
645 BOOL bOldUpdateMode = rEE.GetUpdateMode();
646 rEE.SetUpdateMode( TRUE );
647 // create the string
648 rEE.SetText( pParaObj->GetTextObject() );
649 xString = lclCreateFormattedString( rRoot, rEE, 0, nFlags, nMaxLen );
650 rEE.SetUpdateMode( bOldUpdateMode );
651 // limit formats - TODO: BIFF dependent
652 if( !xString->IsEmpty() )
654 xString->LimitFormatCount( EXC_MAXRECSIZE_BIFF8 / 8 - 1 );
655 xString->AppendTrailingFormat( EXC_FONT_APP );
658 else
660 DBG_ERRORFILE( "XclExpStringHelper::CreateString - textbox without para object" );
661 // create BIFF dependent empty Excel string
662 xString = CreateString( rRoot, EMPTY_STRING, nFlags, nMaxLen );
664 return xString;
667 XclExpStringRef XclExpStringHelper::CreateString(
668 const XclExpRoot& rRoot, const EditTextObject& rEditObj,
669 XclStrFlags nFlags, sal_uInt16 nMaxLen )
671 XclExpStringRef xString;
672 EditEngine& rEE = rRoot.GetDrawEditEngine();
673 BOOL bOldUpdateMode = rEE.GetUpdateMode();
674 rEE.SetUpdateMode( TRUE );
675 rEE.SetText( rEditObj );
676 xString = lclCreateFormattedString( rRoot, rEE, 0, nFlags, nMaxLen );
677 rEE.SetUpdateMode( bOldUpdateMode );
678 // limit formats - TODO: BIFF dependent
679 if( !xString->IsEmpty() )
681 xString->LimitFormatCount( EXC_MAXRECSIZE_BIFF8 / 8 - 1 );
682 xString->AppendTrailingFormat( EXC_FONT_APP );
684 return xString;
687 sal_Int16 XclExpStringHelper::GetLeadingScriptType( const XclExpRoot& rRoot, const String& rString )
689 namespace ApiScriptType = ::com::sun::star::i18n::ScriptType;
690 Reference< XBreakIterator > xBreakIt = rRoot.GetDoc().GetBreakIterator();
691 OUString aOUString( rString );
692 sal_Int32 nStrPos = 0;
693 sal_Int32 nStrLen = aOUString.getLength();
694 sal_Int16 nScript = ApiScriptType::WEAK;
695 while( (nStrPos < nStrLen) && (nScript == ApiScriptType::WEAK) )
697 nScript = xBreakIt->getScriptType( aOUString, nStrPos );
698 nStrPos = xBreakIt->endOfScript( aOUString, nStrPos, nScript );
700 return (nScript == ApiScriptType::WEAK) ? rRoot.GetDefApiScript() : nScript;
703 // Header/footer conversion ===================================================
705 XclExpHFConverter::XclExpHFConverter( const XclExpRoot& rRoot ) :
706 XclExpRoot( rRoot ),
707 mrEE( rRoot.GetHFEditEngine() ),
708 mnTotalHeight( 0 )
712 void XclExpHFConverter::GenerateString(
713 const EditTextObject* pLeftObj,
714 const EditTextObject* pCenterObj,
715 const EditTextObject* pRightObj )
717 maHFString.Erase();
718 mnTotalHeight = 0;
719 AppendPortion( pLeftObj, 'L' );
720 AppendPortion( pCenterObj, 'C' );
721 AppendPortion( pRightObj, 'R' );
724 void XclExpHFConverter::AppendPortion( const EditTextObject* pTextObj, sal_Unicode cPortionCode )
726 if( !pTextObj ) return;
728 String aText;
729 sal_Int32 nHeight = 0;
730 SfxItemSet aItemSet( *GetDoc().GetPool(), ATTR_PATTERN_START, ATTR_PATTERN_END );
732 // edit engine
733 BOOL bOldUpdateMode = mrEE.GetUpdateMode();
734 mrEE.SetUpdateMode( TRUE );
735 mrEE.SetText( *pTextObj );
737 // font information
738 XclFontData aFontData, aNewData;
739 if( const XclExpFont* pFirstFont = GetFontBuffer().GetFont( EXC_FONT_APP ) )
741 aFontData = pFirstFont->GetFontData();
742 (aFontData.mnHeight += 10) /= 20; // using pt here, not twips
744 else
745 aFontData.mnHeight = 10;
747 const FontList* pFontList = 0;
748 if( SfxObjectShell* pDocShell = GetDocShell() )
750 if( const SvxFontListItem* pInfoItem = static_cast< const SvxFontListItem* >(
751 pDocShell->GetItem( SID_ATTR_CHAR_FONTLIST ) ) )
752 pFontList = pInfoItem->GetFontList();
755 sal_uInt16 nParaCount = mrEE.GetParagraphCount();
756 for( sal_uInt16 nPara = 0; nPara < nParaCount; ++nPara )
758 ESelection aSel( nPara, 0 );
759 String aParaText;
760 sal_Int32 nParaHeight = 0;
761 SvUShorts aPosList;
762 mrEE.GetPortions( nPara, aPosList );
764 sal_uInt16 nPosCount = aPosList.Count();
765 for( sal_uInt16 nPos = 0; nPos < nPosCount; ++nPos )
767 aSel.nEndPos = static_cast< xub_StrLen >( aPosList.GetObject( nPos ) );
768 if( aSel.nStartPos < aSel.nEndPos )
771 // --- font attributes ---
773 Font aFont;
774 aItemSet.ClearItem();
775 SfxItemSet aEditSet( mrEE.GetAttribs( aSel ) );
776 ScPatternAttr::GetFromEditItemSet( aItemSet, aEditSet );
777 ScPatternAttr::GetFont( aFont, aItemSet, SC_AUTOCOL_RAW );
779 // font name and style
780 aNewData.maName = XclTools::GetXclFontName( aFont.GetName() );
781 aNewData.mnWeight = (aFont.GetWeight() > WEIGHT_NORMAL) ? EXC_FONTWGHT_BOLD : EXC_FONTWGHT_NORMAL;
782 aNewData.mbItalic = (aFont.GetItalic() != ITALIC_NONE);
783 bool bNewFont = !(aFontData.maName == aNewData.maName);
784 bool bNewStyle = (aFontData.mnWeight != aNewData.mnWeight) ||
785 (aFontData.mbItalic != aNewData.mbItalic);
786 if( bNewFont || (bNewStyle && pFontList) )
788 aParaText.AppendAscii( "&\"" ).Append( aNewData.maName );
789 if( pFontList )
791 FontInfo aFontInfo( pFontList->Get(
792 aNewData.maName,
793 (aNewData.mnWeight > EXC_FONTWGHT_NORMAL) ? WEIGHT_BOLD : WEIGHT_NORMAL,
794 aNewData.mbItalic ? ITALIC_NORMAL : ITALIC_NONE ) );
795 aNewData.maStyle = pFontList->GetStyleName( aFontInfo );
796 if( aNewData.maStyle.Len() )
797 aParaText.Append( ',' ).Append( aNewData.maStyle );
799 aParaText.Append( '"' );
802 // height
803 // is calculated wrong in ScPatternAttr::GetFromEditItemSet, because already in twips and not 100thmm
804 // -> get it directly from edit engine item set
805 aNewData.mnHeight = ulimit_cast< sal_uInt16 >( GETITEM( aEditSet, SvxFontHeightItem, EE_CHAR_FONTHEIGHT ).GetHeight() );
806 (aNewData.mnHeight += 10) /= 20;
807 bool bFontHtChanged = (aFontData.mnHeight != aNewData.mnHeight);
808 if( bFontHtChanged )
809 aParaText.Append( '&' ).Append( String::CreateFromInt32( aNewData.mnHeight ) );
810 // update maximum paragraph height, convert to twips
811 nParaHeight = ::std::max< sal_Int32 >( nParaHeight, aNewData.mnHeight * 20 );
813 // underline
814 aNewData.mnUnderline = EXC_FONTUNDERL_NONE;
815 switch( aFont.GetUnderline() )
817 case UNDERLINE_NONE: aNewData.mnUnderline = EXC_FONTUNDERL_NONE; break;
818 case UNDERLINE_SINGLE: aNewData.mnUnderline = EXC_FONTUNDERL_SINGLE; break;
819 case UNDERLINE_DOUBLE: aNewData.mnUnderline = EXC_FONTUNDERL_DOUBLE; break;
820 default: aNewData.mnUnderline = EXC_FONTUNDERL_SINGLE;
822 if( aFontData.mnUnderline != aNewData.mnUnderline )
824 sal_uInt8 nTmpUnderl = (aNewData.mnUnderline == EXC_FONTUNDERL_NONE) ?
825 aFontData.mnUnderline : aNewData.mnUnderline;
826 aParaText.AppendAscii( (nTmpUnderl == EXC_FONTUNDERL_SINGLE) ? "&U" : "&E" );
829 // strikeout
830 aNewData.mbStrikeout = (aFont.GetStrikeout() != STRIKEOUT_NONE);
831 if( aFontData.mbStrikeout != aNewData.mbStrikeout )
832 aParaText.AppendAscii( "&S" );
834 // super/sub script
835 const SvxEscapementItem& rEscapeItem = GETITEM( aEditSet, SvxEscapementItem, EE_CHAR_ESCAPEMENT );
836 aNewData.SetScEscapement( rEscapeItem.GetEsc() );
837 if( aFontData.mnEscapem != aNewData.mnEscapem )
839 switch(aNewData.mnEscapem)
841 // close the previous super/sub script.
842 case EXC_FONTESC_NONE: aParaText.AppendAscii( (aFontData.mnEscapem == EXC_FONTESC_SUPER) ? "&X" : "&Y" ); break;
843 case EXC_FONTESC_SUPER: aParaText.AppendAscii( "&X" ); break;
844 case EXC_FONTESC_SUB: aParaText.AppendAscii( "&Y" ); break;
845 default: break;
849 aFontData = aNewData;
851 // --- text content or text fields ---
853 const SfxPoolItem* pItem;
854 if( (aSel.nStartPos + 1 == aSel.nEndPos) && // fields are single characters
855 (aEditSet.GetItemState( EE_FEATURE_FIELD, sal_False, &pItem ) == SFX_ITEM_SET) )
857 if( const SvxFieldData* pFieldData = static_cast< const SvxFieldItem* >( pItem )->GetField() )
859 if( pFieldData->ISA( SvxPageField ) )
860 aParaText.AppendAscii( "&P" );
861 else if( pFieldData->ISA( SvxPagesField ) )
862 aParaText.AppendAscii( "&N" );
863 else if( pFieldData->ISA( SvxDateField ) )
864 aParaText.AppendAscii( "&D" );
865 else if( pFieldData->ISA( SvxTimeField ) || pFieldData->ISA( SvxExtTimeField ) )
866 aParaText.AppendAscii( "&T" );
867 else if( pFieldData->ISA( SvxTableField ) )
868 aParaText.AppendAscii( "&A" );
869 else if( pFieldData->ISA( SvxFileField ) ) // title -> file name
870 aParaText.AppendAscii( "&F" );
871 else if( const SvxExtFileField* pFileField = PTR_CAST( SvxExtFileField, pFieldData ) )
873 switch( pFileField->GetFormat() )
875 case SVXFILEFORMAT_NAME_EXT:
876 case SVXFILEFORMAT_NAME:
877 aParaText.AppendAscii( "&F" );
878 break;
879 case SVXFILEFORMAT_PATH:
880 aParaText.AppendAscii( "&Z" );
881 break;
882 case SVXFILEFORMAT_FULLPATH:
883 aParaText.AppendAscii( "&Z&F" );
884 break;
885 default:
886 DBG_ERRORFILE( "XclExpHFConverter::AppendPortion - unknown file field" );
891 else
893 String aPortionText( mrEE.GetText( aSel ) );
894 aPortionText.SearchAndReplaceAll( String( '&' ), String( RTL_CONSTASCII_USTRINGPARAM( "&&" ) ) );
895 // #i17440# space between font height and numbers in text
896 if( bFontHtChanged && aParaText.Len() && aPortionText.Len() )
898 sal_Unicode cLast = aParaText.GetChar( aParaText.Len() - 1 );
899 sal_Unicode cFirst = aPortionText.GetChar( 0 );
900 if( ('0' <= cLast) && (cLast <= '9') && ('0' <= cFirst) && (cFirst <= '9') )
901 aParaText.Append( ' ' );
903 aParaText.Append( aPortionText );
907 aSel.nStartPos = aSel.nEndPos;
910 ScGlobal::AddToken( aText, aParaText, '\n' );
911 if( nParaHeight == 0 )
912 nParaHeight = aFontData.mnHeight * 20; // points -> twips
913 nHeight += nParaHeight;
916 mrEE.SetUpdateMode( bOldUpdateMode );
918 if( aText.Len() )
920 maHFString.Append( '&' ).Append( cPortionCode ).Append( aText );
921 mnTotalHeight = ::std::max( mnTotalHeight, nHeight );
925 // URL conversion =============================================================
927 namespace {
929 /** Converts the file URL passed in rUrl to a URL in DOS notation (local or UNC).
930 @param rUrl (in/out-param) In: URL to convert; Out: Converted URL in DOS notation.
931 @param rBasePath Base path for relative URLs.
932 @param bSaveRelUrl Converts to a relative URL, using rBasePath.
933 @return True = Conversion successful, rUrl contains converted file URL. */
934 bool lclConvertToDos( String& rUrl, const String& rBasePath, bool bSaveRelUrl )
936 String aDosUrl( INetURLObject( rUrl ).getFSysPath( INetURLObject::FSYS_DOS ) );
937 bool bRet = (aDosUrl.Len() > 0);
938 if( bRet && bSaveRelUrl )
940 // try to convert to relative path
941 String aDosBase( INetURLObject( rBasePath ).getFSysPath( INetURLObject::FSYS_DOS ) );
942 if( aDosBase.Len() )
944 xub_StrLen nPos;
946 // --- 1st step: delete equal subdirectories ---
948 // special handling for UNC
949 xub_StrLen nStartSearch = aDosBase.EqualsAscii( "\\\\", 0, 2 ) ? 2 : 0;
950 bool bEqualBase = false;
951 bool bLoop = true;
952 while( bLoop && ((nPos = aDosBase.Search( '\\', nStartSearch )) != STRING_NOTFOUND) )
954 bLoop = (TRUE == aDosBase.Equals( aDosUrl, 0, nPos + 1 ));
955 if( bLoop )
957 aDosBase.Erase( 0, nPos + 1 );
958 aDosUrl.Erase( 0, nPos + 1 );
959 nStartSearch = 0;
960 bEqualBase = true;
964 // --- 2nd step: add parent directory levels ---
966 if( bEqualBase )
968 while( (nPos = aDosBase.Search( '\\' )) != STRING_NOTFOUND )
970 aDosBase.Erase( 0, nPos + 1 );
971 aDosUrl.InsertAscii( "..\\", 0 );
975 rUrl = aDosUrl;
977 return bRet;
980 /** Encodes special parts of the URL, i.e. directory separators and volume names.
981 @param pTableName Pointer to a table name to be encoded in this URL, or 0. */
982 void lclEncodeDosUrl( XclBiff eBiff, String& rUrl, const String* pTableName = 0 )
984 if( rUrl.Len() )
986 String aOldUrl( rUrl );
987 rUrl = EXC_URLSTART_ENCODED;
989 if( (aOldUrl.Len() > 2) && aOldUrl.EqualsAscii( "\\\\", 0, 2 ) )
991 // UNC
992 rUrl.Append( EXC_URL_DOSDRIVE ).Append( '@' );
993 aOldUrl.Erase( 0, 2 );
995 else if( (aOldUrl.Len() > 2) && aOldUrl.EqualsAscii( ":\\", 1, 2 ) )
997 // drive letter
998 rUrl.Append( EXC_URL_DOSDRIVE ).Append( aOldUrl.GetChar( 0 ) );
999 aOldUrl.Erase( 0, 3 );
1002 // directories
1003 xub_StrLen nPos;
1004 while( (nPos = aOldUrl.Search( '\\' )) != STRING_NOTFOUND )
1006 if( aOldUrl.EqualsAscii( "..", 0, 2 ) )
1007 rUrl.Append( EXC_URL_PARENTDIR ); // parent dir
1008 else
1009 rUrl.Append( aOldUrl.GetBuffer(), nPos ).Append( EXC_URL_SUBDIR );
1010 aOldUrl.Erase( 0, nPos + 1 );
1013 // file name
1014 if( pTableName ) // enclose file name in brackets if table name follows
1015 rUrl.Append( '[' ).Append( aOldUrl ).Append( ']' );
1016 else
1017 rUrl.Append( aOldUrl );
1019 else // empty URL -> self reference
1021 switch( eBiff )
1023 case EXC_BIFF5:
1024 rUrl = pTableName ? EXC_URLSTART_SELFENCODED : EXC_URLSTART_SELF;
1025 break;
1026 case EXC_BIFF8:
1027 DBG_ASSERT( pTableName, "lclEncodeDosUrl - sheet name required for BIFF8" );
1028 rUrl = EXC_URLSTART_SELF;
1029 break;
1030 default:
1031 DBG_ERROR_BIFF();
1035 // table name
1036 if( pTableName )
1037 rUrl.Append( *pTableName );
1040 } // namespace
1042 // ----------------------------------------------------------------------------
1044 String XclExpUrlHelper::EncodeUrl( const XclExpRoot& rRoot, const String& rAbsUrl, const String* pTableName )
1046 String aDosUrl( rAbsUrl );
1047 if( !aDosUrl.Len() || lclConvertToDos( aDosUrl, rRoot.GetBasePath(), rRoot.IsRelUrl() ) )
1048 lclEncodeDosUrl( rRoot.GetBiff(), aDosUrl, pTableName );
1049 return aDosUrl;
1052 String XclExpUrlHelper::EncodeDde( const String& rApplic, const String rTopic )
1054 String aDde( rApplic );
1055 aDde.Append( EXC_DDE_DELIM ).Append( rTopic );
1056 return aDde;
1059 // Cached Value Lists =========================================================
1061 XclExpCachedMatrix::XclExpCachedMatrix( const ScMatrix& rMatrix )
1062 : mrMatrix( rMatrix )
1064 mrMatrix.IncRef();
1066 XclExpCachedMatrix::~XclExpCachedMatrix()
1068 mrMatrix.DecRef();
1071 void XclExpCachedMatrix::GetDimensions( SCSIZE & nCols, SCSIZE & nRows ) const
1073 mrMatrix.GetDimensions( nCols, nRows );
1075 DBG_ASSERT( nCols && nRows, "XclExpCachedMatrix::GetDimensions - empty matrix" );
1076 DBG_ASSERT( nCols <= 256, "XclExpCachedMatrix::GetDimensions - too many columns" );
1079 sal_Size XclExpCachedMatrix::GetSize() const
1081 SCSIZE nCols, nRows;
1083 GetDimensions( nCols, nRows );
1085 /* The returned size may be wrong if the matrix contains strings. The only
1086 effect is that the export stream has to update a wrong record size which is
1087 faster than to iterate through all cached values and calculate their sizes. */
1088 return 3 + 9 * (nCols * nRows);
1091 void XclExpCachedMatrix::Save( XclExpStream& rStrm ) const
1093 SCSIZE nCols, nRows;
1095 GetDimensions( nCols, nRows );
1097 if( rStrm.GetRoot().GetBiff() <= EXC_BIFF5 )
1098 // in BIFF2-BIFF7: 256 columns represented by 0 columns
1099 rStrm << static_cast< sal_uInt8 >( nCols ) << static_cast< sal_uInt16 >( nRows );
1100 else
1101 // in BIFF8: columns and rows decreaed by 1
1102 rStrm << static_cast< sal_uInt8 >( nCols - 1 ) << static_cast< sal_uInt16 >( nRows - 1 );
1104 for( SCSIZE nRow = 0; nRow < nRows; ++nRow )
1106 for( SCSIZE nCol = 0; nCol < nCols; ++nCol )
1108 ScMatValType nMatValType = SC_MATVAL_VALUE;
1109 const ScMatrixValue* pMatVal = mrMatrix.Get( nCol, nRow, nMatValType );
1111 if( !pMatVal || SC_MATVAL_EMPTY == nMatValType )
1113 rStrm.SetSliceSize( 9 );
1114 rStrm << EXC_CACHEDVAL_EMPTY;
1115 rStrm.WriteZeroBytes( 8 );
1117 else if( ScMatrix::IsNonValueType( nMatValType ) )
1119 XclExpString aStr( pMatVal->GetString(), EXC_STR_DEFAULT );
1120 rStrm.SetSliceSize( 6 );
1121 rStrm << EXC_CACHEDVAL_STRING << aStr;
1123 else if( SC_MATVAL_BOOLEAN == nMatValType )
1125 sal_Int8 nBool = pMatVal->GetBoolean();
1126 rStrm.SetSliceSize( 9 );
1127 rStrm << EXC_CACHEDVAL_BOOL << nBool;
1128 rStrm.WriteZeroBytes( 7 );
1130 else if( USHORT nScError = pMatVal->GetError() )
1132 sal_Int8 nError ( XclTools::GetXclErrorCode( nScError ) );
1133 rStrm.SetSliceSize( 9 );
1134 rStrm << EXC_CACHEDVAL_ERROR << nError;
1135 rStrm.WriteZeroBytes( 7 );
1137 else
1139 rStrm.SetSliceSize( 9 );
1140 rStrm << EXC_CACHEDVAL_DOUBLE << pMatVal->fVal;
1146 // ============================================================================