blob: 0a687c4297b7a156cacc5b842db9812d19f57761 [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.
*
*************************************************************/
#include "oox/xls/worksheethelper.hxx"
#include <algorithm>
#include <list>
#include <utility>
#include <com/sun/star/awt/Point.hpp>
#include <com/sun/star/awt/Size.hpp>
#include <com/sun/star/drawing/XDrawPageSupplier.hpp>
#include <com/sun/star/lang/XMultiServiceFactory.hpp>
#include <com/sun/star/sheet/TableValidationVisibility.hpp>
#include <com/sun/star/sheet/ValidationType.hpp>
#include <com/sun/star/sheet/ValidationAlertStyle.hpp>
#include <com/sun/star/sheet/XCellAddressable.hpp>
#include <com/sun/star/sheet/XCellRangeAddressable.hpp>
#include <com/sun/star/sheet/XFormulaTokens.hpp>
#include <com/sun/star/sheet/XLabelRanges.hpp>
#include <com/sun/star/sheet/XMultiFormulaTokens.hpp>
#include <com/sun/star/sheet/XSheetCellRangeContainer.hpp>
#include <com/sun/star/sheet/XSheetCondition.hpp>
#include <com/sun/star/sheet/XSheetOutline.hpp>
#include <com/sun/star/sheet/XSpreadsheet.hpp>
#include <com/sun/star/table/XColumnRowRange.hpp>
#include <com/sun/star/text/WritingMode2.hpp>
#include <com/sun/star/text/XText.hpp>
#include <rtl/ustrbuf.hxx>
#include "oox/core/filterbase.hxx"
#include "oox/helper/propertyset.hxx"
#include "oox/xls/addressconverter.hxx"
#include "oox/xls/autofilterbuffer.hxx"
#include "oox/xls/commentsbuffer.hxx"
#include "oox/xls/condformatbuffer.hxx"
#include "oox/xls/drawingfragment.hxx"
#include "oox/xls/drawingmanager.hxx"
#include "oox/xls/formulaparser.hxx"
#include "oox/xls/pagesettings.hxx"
#include "oox/xls/querytablebuffer.hxx"
#include "oox/xls/sharedstringsbuffer.hxx"
#include "oox/xls/sheetdatabuffer.hxx"
#include "oox/xls/stylesbuffer.hxx"
#include "oox/xls/unitconverter.hxx"
#include "oox/xls/viewsettings.hxx"
#include "oox/xls/workbooksettings.hxx"
#include "oox/xls/worksheetbuffer.hxx"
#include "oox/xls/worksheetsettings.hxx"
namespace oox {
namespace xls {
// ============================================================================
using namespace ::com::sun::star::awt;
using namespace ::com::sun::star::beans;
using namespace ::com::sun::star::drawing;
using namespace ::com::sun::star::lang;
using namespace ::com::sun::star::sheet;
using namespace ::com::sun::star::table;
using namespace ::com::sun::star::text;
using namespace ::com::sun::star::uno;
using namespace ::com::sun::star::util;
using ::rtl::OUString;
using ::rtl::OUStringBuffer;
// ============================================================================
namespace {
void lclUpdateProgressBar( const ISegmentProgressBarRef& rxProgressBar, const CellRangeAddress& rUsedArea, sal_Int32 nRow )
{
if( rxProgressBar.get() && (rUsedArea.StartRow <= nRow) && (nRow <= rUsedArea.EndRow) )
{
double fPosition = static_cast< double >( nRow - rUsedArea.StartRow + 1 ) / (rUsedArea.EndRow - rUsedArea.StartRow + 1);
if( rxProgressBar->getPosition() < fPosition )
rxProgressBar->setPosition( fPosition );
}
}
void lclUpdateProgressBar( const ISegmentProgressBarRef& rxProgressBar, double fPosition )
{
if( rxProgressBar.get() )
rxProgressBar->setPosition( fPosition );
}
} // namespace
// ============================================================================
// ============================================================================
ColumnModel::ColumnModel() :
maRange( -1 ),
mfWidth( 0.0 ),
mnXfId( -1 ),
mnLevel( 0 ),
mbShowPhonetic( false ),
mbHidden( false ),
mbCollapsed( false )
{
}
bool ColumnModel::isMergeable( const ColumnModel& rModel ) const
{
return
(maRange.mnFirst <= rModel.maRange.mnFirst) &&
(rModel.maRange.mnFirst <= maRange.mnLast + 1) &&
(mfWidth == rModel.mfWidth) &&
// ignore mnXfId, cell formatting is always set directly
(mnLevel == rModel.mnLevel) &&
(mbHidden == rModel.mbHidden) &&
(mbCollapsed == rModel.mbCollapsed);
}
// ----------------------------------------------------------------------------
RowModel::RowModel() :
mnRow( -1 ),
mfHeight( 0.0 ),
mnXfId( -1 ),
mnLevel( 0 ),
mbCustomHeight( false ),
mbCustomFormat( false ),
mbShowPhonetic( false ),
mbHidden( false ),
mbCollapsed( false ),
mbThickTop( false ),
mbThickBottom( false )
{
}
void RowModel::insertColSpan( const ValueRange& rColSpan )
{
if( (0 <= rColSpan.mnFirst) && (rColSpan.mnFirst <= rColSpan.mnLast) )
maColSpans.insert( rColSpan );
}
bool RowModel::isMergeable( const RowModel& rModel ) const
{
return
// ignore maColSpans - is handled separately in SheetDataBuffer class
(mfHeight == rModel.mfHeight) &&
// ignore mnXfId, mbCustomFormat, mbShowPhonetic - cell formatting is always set directly
(mnLevel == rModel.mnLevel) &&
(mbCustomHeight == rModel.mbCustomHeight) &&
(mbHidden == rModel.mbHidden) &&
(mbCollapsed == rModel.mbCollapsed);
}
// ----------------------------------------------------------------------------
PageBreakModel::PageBreakModel() :
mnColRow( 0 ),
mbManual( false )
{
}
// ----------------------------------------------------------------------------
HyperlinkModel::HyperlinkModel()
{
}
// ----------------------------------------------------------------------------
ValidationModel::ValidationModel() :
mnType( XML_none ),
mnOperator( XML_between ),
mnErrorStyle( XML_stop ),
mbShowInputMsg( false ),
mbShowErrorMsg( false ),
mbNoDropDown( false ),
mbAllowBlank( false )
{
}
void ValidationModel::setBiffType( sal_uInt8 nType )
{
static const sal_Int32 spnTypeIds[] = {
XML_none, XML_whole, XML_decimal, XML_list, XML_date, XML_time, XML_textLength, XML_custom };
mnType = STATIC_ARRAY_SELECT( spnTypeIds, nType, XML_none );
}
void ValidationModel::setBiffOperator( sal_uInt8 nOperator )
{
static const sal_Int32 spnOperators[] = {
XML_between, XML_notBetween, XML_equal, XML_notEqual,
XML_greaterThan, XML_lessThan, XML_greaterThanOrEqual, XML_lessThanOrEqual };
mnOperator = STATIC_ARRAY_SELECT( spnOperators, nOperator, XML_TOKEN_INVALID );
}
void ValidationModel::setBiffErrorStyle( sal_uInt8 nErrorStyle )
{
static const sal_Int32 spnErrorStyles[] = { XML_stop, XML_warning, XML_information };
mnErrorStyle = STATIC_ARRAY_SELECT( spnErrorStyles, nErrorStyle, XML_stop );
}
// ============================================================================
// ============================================================================
class WorksheetGlobals : public WorkbookHelper
{
public:
explicit WorksheetGlobals(
const WorkbookHelper& rHelper,
const ISegmentProgressBarRef& rxProgressBar,
WorksheetType eSheetType,
sal_Int16 nSheet );
/** Returns true, if this helper refers to an existing Calc sheet. */
inline bool isValidSheet() const { return mxSheet.is(); }
/** Returns the type of this sheet. */
inline WorksheetType getSheetType() const { return meSheetType; }
/** Returns the index of the current sheet. */
inline sal_Int16 getSheetIndex() const { return maUsedArea.Sheet; }
/** Returns the XSpreadsheet interface of the current sheet. */
inline const Reference< XSpreadsheet >& getSheet() const { return mxSheet; }
/** Returns the XCell interface for the passed cell address. */
Reference< XCell > getCell( const CellAddress& rAddress ) const;
/** Returns the XCellRange interface for the passed cell range address. */
Reference< XCellRange > getCellRange( const CellRangeAddress& rRange ) const;
/** Returns the XSheetCellRanges interface for the passed cell range addresses. */
Reference< XSheetCellRanges > getCellRangeList( const ApiCellRangeList& rRanges ) const;
/** Returns the XCellRange interface for a column. */
Reference< XCellRange > getColumn( sal_Int32 nCol ) const;
/** Returns the XCellRange interface for a row. */
Reference< XCellRange > getRow( sal_Int32 nRow ) const;
/** Returns the XTableColumns interface for a range of columns. */
Reference< XTableColumns > getColumns( const ValueRange& rColRange ) const;
/** Returns the XTableRows interface for a range of rows. */
Reference< XTableRows > getRows( const ValueRange& rRowRange ) const;
/** Returns the XDrawPage interface of the draw page of the current sheet. */
Reference< XDrawPage > getDrawPage() const;
/** Returns the size of the entire drawing page in 1/100 mm. */
const Size& getDrawPageSize() const;
/** Returns the absolute position of the top-left corner of the cell in 1/100 mm. */
Point getCellPosition( sal_Int32 nCol, sal_Int32 nRow ) const;
/** Returns the size of the cell in 1/100 mm. */
Size getCellSize( sal_Int32 nCol, sal_Int32 nRow ) const;
/** Returns the address of the cell that contains the passed point in 1/100 mm. */
CellAddress getCellAddressFromPosition( const Point& rPosition ) const;
/** Returns the cell range address that contains the passed rectangle in 1/100 mm. */
CellRangeAddress getCellRangeFromRectangle( const Rectangle& rRect ) const;
/** Returns the buffer for cell contents and cell formatting. */
inline SheetDataBuffer& getSheetData() { return maSheetData; }
/** Returns the conditional formattings in this sheet. */
inline CondFormatBuffer& getCondFormats() { return maCondFormats; }
/** Returns the buffer for all cell comments in this sheet. */
inline CommentsBuffer& getComments() { return maComments; }
/** Returns the auto filters for the sheet. */
inline AutoFilterBuffer& getAutoFilters() { return maAutoFilters; }
/** Returns the buffer for all web query tables in this sheet. */
inline QueryTableBuffer& getQueryTables() { return maQueryTables; }
/** Returns the worksheet settings object. */
inline WorksheetSettings& getWorksheetSettings() { return maSheetSett; }
/** Returns the page/print settings for this sheet. */
inline PageSettings& getPageSettings() { return maPageSett; }
/** Returns the view settings for this sheet. */
inline SheetViewSettings& getSheetViewSettings() { return maSheetViewSett; }
/** Returns the VML drawing page for this sheet (OOXML/BIFF12 only). */
inline VmlDrawing& getVmlDrawing() { return *mxVmlDrawing; }
/** Returns the BIFF drawing page for this sheet (BIFF2-BIFF8 only). */
inline BiffSheetDrawing& getBiffDrawing() const { return *mxBiffDrawing; }
/** Changes the current sheet type. */
inline void setSheetType( WorksheetType eSheetType ) { meSheetType = eSheetType; }
/** Sets a column or row page break described in the passed struct. */
void setPageBreak( const PageBreakModel& rModel, bool bRowBreak );
/** Inserts the hyperlink URL into the spreadsheet. */
void setHyperlink( const HyperlinkModel& rModel );
/** Inserts the data validation settings into the spreadsheet. */
void setValidation( const ValidationModel& rModel );
/** Sets the path to the DrawingML fragment of this sheet. */
void setDrawingPath( const OUString& rDrawingPath );
/** Sets the path to the legacy VML drawing fragment of this sheet. */
void setVmlDrawingPath( const OUString& rVmlDrawingPath );
/** Extends the used area of this sheet by the passed cell position. */
void extendUsedArea( const CellAddress& rAddress );
/** Extends the used area of this sheet by the passed cell range. */
void extendUsedArea( const CellRangeAddress& rRange );
/** Extends the shape bounding box by the position and size of the passed rectangle. */
void extendShapeBoundingBox( const Rectangle& rShapeRect );
/** Sets base width for all columns (without padding pixels). This value
is only used, if base width has not been set with setDefaultColumnWidth(). */
void setBaseColumnWidth( sal_Int32 nWidth );
/** Sets default width for all columns. This function overrides the base
width set with the setBaseColumnWidth() function. */
void setDefaultColumnWidth( double fWidth );
/** Sets column settings for a specific column range.
@descr Column default formatting is converted directly, other settings
are cached and converted in the finalizeImport() call. */
void setColumnModel( const ColumnModel& rModel );
/** Converts column default cell formatting. */
void convertColumnFormat( sal_Int32 nFirstCol, sal_Int32 nLastCol, sal_Int32 nXfId ) const;
/** Sets default height and hidden state for all unused rows in the sheet. */
void setDefaultRowSettings( double fHeight, bool bCustomHeight, bool bHidden, bool bThickTop, bool bThickBottom );
/** Sets row settings for a specific row.
@descr Row default formatting is converted directly, other settings
are cached and converted in the finalizeImport() call. */
void setRowModel( const RowModel& rModel );
/** Specifies that the passed row needs to set its height manually. */
void setManualRowHeight( sal_Int32 nRow );
/** Initial conversion before importing the worksheet. */
void initializeWorksheetImport();
/** Final conversion after importing the worksheet. */
void finalizeWorksheetImport();
private:
typedef ::std::vector< sal_Int32 > OutlineLevelVec;
typedef ::std::pair< ColumnModel, sal_Int32 > ColumnModelRange;
typedef ::std::map< sal_Int32, ColumnModelRange > ColumnModelRangeMap;
typedef ::std::pair< RowModel, sal_Int32 > RowModelRange;
typedef ::std::map< sal_Int32, RowModelRange > RowModelRangeMap;
typedef ::std::list< HyperlinkModel > HyperlinkModelList;
typedef ::std::list< ValidationModel > ValidationModelList;
/** Inserts all imported hyperlinks into their cell ranges. */
void finalizeHyperlinkRanges() const;
/** Generates the final URL for the passed hyperlink. */
OUString getHyperlinkUrl( const HyperlinkModel& rHyperlink ) const;
/** Inserts a hyperlinks into the specified cell. */
void insertHyperlink( const CellAddress& rAddress, const OUString& rUrl ) const;
/** Inserts all imported data validations into their cell ranges. */
void finalizeValidationRanges() const;
/** Converts column properties for all columns in the sheet. */
void convertColumns();
/** Converts column properties. */
void convertColumns( OutlineLevelVec& orColLevels, const ValueRange& rColRange, const ColumnModel& rModel );
/** Converts row properties for all rows in the sheet. */
void convertRows();
/** Converts row properties. */
void convertRows( OutlineLevelVec& orRowLevels, const ValueRange& rRowRange, const RowModel& rModel, double fDefHeight = -1.0 );
/** Converts outline grouping for the passed column or row. */
void convertOutlines( OutlineLevelVec& orLevels, sal_Int32 nColRow, sal_Int32 nLevel, bool bCollapsed, bool bRows );
/** Groups columns or rows for the given range. */
void groupColumnsOrRows( sal_Int32 nFirstColRow, sal_Int32 nLastColRow, bool bCollapsed, bool bRows );
/** Imports the drawings of the sheet (DML, VML, DFF) and updates the used area. */
void finalizeDrawings();
private:
typedef ::std::auto_ptr< VmlDrawing > VmlDrawingPtr;
typedef ::std::auto_ptr< BiffSheetDrawing > BiffSheetDrawingPtr;
const OUString maSheetCellRanges; /// Service name for a SheetCellRanges object.
const OUString maUrlTextField; /// Service name for a URL text field.
const CellAddress& mrMaxApiPos; /// Reference to maximum Calc cell address from address converter.
CellRangeAddress maUsedArea; /// Used area of the sheet, and sheet index of the sheet.
ColumnModel maDefColModel; /// Default column formatting.
ColumnModelRangeMap maColModels; /// Ranges of columns sorted by first column index.
RowModel maDefRowModel; /// Default row formatting.
RowModelRangeMap maRowModels; /// Ranges of rows sorted by first row index.
HyperlinkModelList maHyperlinks; /// Cell ranges containing hyperlinks.
ValidationModelList maValidations; /// Cell ranges containing data validation settings.
ValueRangeSet maManualRowHeights; /// Rows that need manual height independent from own settings.
SheetDataBuffer maSheetData; /// Buffer for cell contents and cell formatting.
CondFormatBuffer maCondFormats; /// Buffer for conditional formattings.
CommentsBuffer maComments; /// Buffer for all cell comments in this sheet.
AutoFilterBuffer maAutoFilters; /// Sheet auto filters (not associated to a table).
QueryTableBuffer maQueryTables; /// Buffer for all web query tables in this sheet.
WorksheetSettings maSheetSett; /// Global settings for this sheet.
PageSettings maPageSett; /// Page/print settings for this sheet.
SheetViewSettings maSheetViewSett; /// View settings for this sheet.
VmlDrawingPtr mxVmlDrawing; /// Collection of all VML shapes.
BiffSheetDrawingPtr mxBiffDrawing; /// Collection of all BIFF/DFF shapes.
OUString maDrawingPath; /// Path to DrawingML fragment.
OUString maVmlDrawingPath; /// Path to legacy VML drawing fragment.
Size maDrawPageSize; /// Current size of the drawing page in 1/100 mm.
Rectangle maShapeBoundingBox; /// Bounding box for all shapes from all drawings.
ISegmentProgressBarRef mxProgressBar; /// Sheet progress bar.
ISegmentProgressBarRef mxRowProgress; /// Progress bar for row/cell processing.
ISegmentProgressBarRef mxFinalProgress; /// Progress bar for finalization.
WorksheetType meSheetType; /// Type of this sheet.
Reference< XSpreadsheet > mxSheet; /// Reference to the current sheet.
bool mbHasDefWidth; /// True = default column width is set from defaultColWidth attribute.
};
// ----------------------------------------------------------------------------
WorksheetGlobals::WorksheetGlobals( const WorkbookHelper& rHelper, const ISegmentProgressBarRef& rxProgressBar, WorksheetType eSheetType, sal_Int16 nSheet ) :
WorkbookHelper( rHelper ),
maSheetCellRanges( CREATE_OUSTRING( "com.sun.star.sheet.SheetCellRanges" ) ),
maUrlTextField( CREATE_OUSTRING( "com.sun.star.text.TextField.URL" ) ),
mrMaxApiPos( rHelper.getAddressConverter().getMaxApiAddress() ),
maUsedArea( nSheet, SAL_MAX_INT32, SAL_MAX_INT32, -1, -1 ),
maSheetData( *this ),
maCondFormats( *this ),
maComments( *this ),
maAutoFilters( *this ),
maQueryTables( *this ),
maSheetSett( *this ),
maPageSett( *this ),
maSheetViewSett( *this ),
mxProgressBar( rxProgressBar ),
meSheetType( eSheetType ),
mbHasDefWidth( false )
{
mxSheet = getSheetFromDoc( nSheet );
if( !mxSheet.is() )
maUsedArea.Sheet = -1;
// default column settings (width and hidden state may be updated later)
maDefColModel.mfWidth = 8.5;
maDefColModel.mnXfId = -1;
maDefColModel.mnLevel = 0;
maDefColModel.mbHidden = false;
maDefColModel.mbCollapsed = false;
// default row settings (height and hidden state may be updated later)
maDefRowModel.mfHeight = 0.0;
maDefRowModel.mnXfId = -1;
maDefRowModel.mnLevel = 0;
maDefRowModel.mbCustomHeight = false;
maDefRowModel.mbCustomFormat = false;
maDefRowModel.mbShowPhonetic = false;
maDefRowModel.mbHidden = false;
maDefRowModel.mbCollapsed = false;
// buffers
switch( getFilterType() )
{
case FILTER_OOXML:
mxVmlDrawing.reset( new VmlDrawing( *this ) );
break;
case FILTER_BIFF:
mxBiffDrawing.reset( new BiffSheetDrawing( *this ) );
break;
case FILTER_UNKNOWN:
break;
}
// prepare progress bars
if( mxProgressBar.get() )
{
mxRowProgress = mxProgressBar->createSegment( 0.5 );
mxFinalProgress = mxProgressBar->createSegment( 0.5 );
}
}
Reference< XCell > WorksheetGlobals::getCell( const CellAddress& rAddress ) const
{
Reference< XCell > xCell;
if( mxSheet.is() ) try
{
xCell = mxSheet->getCellByPosition( rAddress.Column, rAddress.Row );
}
catch( Exception& )
{
}
return xCell;
}
Reference< XCellRange > WorksheetGlobals::getCellRange( const CellRangeAddress& rRange ) const
{
Reference< XCellRange > xRange;
if( mxSheet.is() ) try
{
xRange = mxSheet->getCellRangeByPosition( rRange.StartColumn, rRange.StartRow, rRange.EndColumn, rRange.EndRow );
}
catch( Exception& )
{
}
return xRange;
}
Reference< XSheetCellRanges > WorksheetGlobals::getCellRangeList( const ApiCellRangeList& rRanges ) const
{
Reference< XSheetCellRanges > xRanges;
if( mxSheet.is() && !rRanges.empty() ) try
{
xRanges.set( getBaseFilter().getModelFactory()->createInstance( maSheetCellRanges ), UNO_QUERY_THROW );
Reference< XSheetCellRangeContainer > xRangeCont( xRanges, UNO_QUERY_THROW );
xRangeCont->addRangeAddresses( ContainerHelper::vectorToSequence( rRanges ), sal_False );
}
catch( Exception& )
{
}
return xRanges;
}
Reference< XCellRange > WorksheetGlobals::getColumn( sal_Int32 nCol ) const
{
Reference< XCellRange > xColumn;
try
{
Reference< XColumnRowRange > xColRowRange( mxSheet, UNO_QUERY_THROW );
Reference< XTableColumns > xColumns( xColRowRange->getColumns(), UNO_SET_THROW );
xColumn.set( xColumns->getByIndex( nCol ), UNO_QUERY );
}
catch( Exception& )
{
}
return xColumn;
}
Reference< XCellRange > WorksheetGlobals::getRow( sal_Int32 nRow ) const
{
Reference< XCellRange > xRow;
try
{
Reference< XColumnRowRange > xColRowRange( mxSheet, UNO_QUERY_THROW );
Reference< XTableRows > xRows( xColRowRange->getRows(), UNO_SET_THROW );
xRow.set( xRows->getByIndex( nRow ), UNO_QUERY );
}
catch( Exception& )
{
}
return xRow;
}
Reference< XTableColumns > WorksheetGlobals::getColumns( const ValueRange& rColRange ) const
{
Reference< XTableColumns > xColumns;
sal_Int32 nLastCol = ::std::min( rColRange.mnLast, mrMaxApiPos.Column );
if( (0 <= rColRange.mnFirst) && (rColRange.mnFirst <= nLastCol) )
{
Reference< XColumnRowRange > xRange( getCellRange( CellRangeAddress( getSheetIndex(), rColRange.mnFirst, 0, nLastCol, 0 ) ), UNO_QUERY );
if( xRange.is() )
xColumns = xRange->getColumns();
}
return xColumns;
}
Reference< XTableRows > WorksheetGlobals::getRows( const ValueRange& rRowRange ) const
{
Reference< XTableRows > xRows;
sal_Int32 nLastRow = ::std::min( rRowRange.mnLast, mrMaxApiPos.Row );
if( (0 <= rRowRange.mnFirst) && (rRowRange.mnFirst <= nLastRow) )
{
Reference< XColumnRowRange > xRange( getCellRange( CellRangeAddress( getSheetIndex(), 0, rRowRange.mnFirst, 0, nLastRow ) ), UNO_QUERY );
if( xRange.is() )
xRows = xRange->getRows();
}
return xRows;
}
Reference< XDrawPage > WorksheetGlobals::getDrawPage() const
{
Reference< XDrawPage > xDrawPage;
try
{
xDrawPage = Reference< XDrawPageSupplier >( mxSheet, UNO_QUERY_THROW )->getDrawPage();
}
catch( Exception& )
{
}
return xDrawPage;
}
const Size& WorksheetGlobals::getDrawPageSize() const
{
OSL_ENSURE( (maDrawPageSize.Width > 0) && (maDrawPageSize.Height > 0), "WorksheetGlobals::getDrawPageSize - called too early, size invalid" );
return maDrawPageSize;
}
Point WorksheetGlobals::getCellPosition( sal_Int32 nCol, sal_Int32 nRow ) const
{
Point aPoint;
PropertySet aCellProp( getCell( CellAddress( getSheetIndex(), nCol, nRow ) ) );
aCellProp.getProperty( aPoint, PROP_Position );
return aPoint;
}
Size WorksheetGlobals::getCellSize( sal_Int32 nCol, sal_Int32 nRow ) const
{
Size aSize;
PropertySet aCellProp( getCell( CellAddress( getSheetIndex(), nCol, nRow ) ) );
aCellProp.getProperty( aSize, PROP_Size );
return aSize;
}
namespace {
inline sal_Int32 lclGetMidAddr( sal_Int32 nBegAddr, sal_Int32 nEndAddr, sal_Int32 nBegPos, sal_Int32 nEndPos, sal_Int32 nSearchPos )
{
// use sal_Int64 to prevent integer overflow
return nBegAddr + 1 + static_cast< sal_Int32 >( static_cast< sal_Int64 >( nEndAddr - nBegAddr - 2 ) * (nSearchPos - nBegPos) / (nEndPos - nBegPos) );
}
bool lclPrepareInterval( sal_Int32 nBegAddr, sal_Int32& rnMidAddr, sal_Int32 nEndAddr,
sal_Int32 nBegPos, sal_Int32 nEndPos, sal_Int32 nSearchPos )
{
// searched position before nBegPos -> use nBegAddr
if( nSearchPos <= nBegPos )
{
rnMidAddr = nBegAddr;
return false;
}
// searched position after nEndPos, or begin next to end -> use nEndAddr
if( (nSearchPos >= nEndPos) || (nBegAddr + 1 >= nEndAddr) )
{
rnMidAddr = nEndAddr;
return false;
}
/* Otherwise find mid address according to position. lclGetMidAddr() will
return an address between nBegAddr and nEndAddr. */
rnMidAddr = lclGetMidAddr( nBegAddr, nEndAddr, nBegPos, nEndPos, nSearchPos );
return true;
}
bool lclUpdateInterval( sal_Int32& rnBegAddr, sal_Int32& rnMidAddr, sal_Int32& rnEndAddr,
sal_Int32& rnBegPos, sal_Int32 nMidPos, sal_Int32& rnEndPos, sal_Int32 nSearchPos )
{
// nSearchPos < nMidPos: use the interval [begin,mid] in the next iteration
if( nSearchPos < nMidPos )
{
// if rnBegAddr is next to rnMidAddr, the latter is the column/row in question
if( rnBegAddr + 1 >= rnMidAddr )
return false;
// otherwise, set interval end to mid
rnEndPos = nMidPos;
rnEndAddr = rnMidAddr;
rnMidAddr = lclGetMidAddr( rnBegAddr, rnEndAddr, rnBegPos, rnEndPos, nSearchPos );
return true;
}
// nSearchPos > nMidPos: use the interval [mid,end] in the next iteration
if( nSearchPos > nMidPos )
{
// if rnMidAddr is next to rnEndAddr, the latter is the column/row in question
if( rnMidAddr + 1 >= rnEndAddr )
{
rnMidAddr = rnEndAddr;
return false;
}
// otherwise, set interval start to mid
rnBegPos = nMidPos;
rnBegAddr = rnMidAddr;
rnMidAddr = lclGetMidAddr( rnBegAddr, rnEndAddr, rnBegPos, rnEndPos, nSearchPos );
return true;
}
// nSearchPos == nMidPos: rnMidAddr is the column/row in question, do not loop anymore
return false;
}
} // namespace
CellAddress WorksheetGlobals::getCellAddressFromPosition( const Point& rPosition ) const
{
// starting cell address and its position in drawing layer (top-left edge)
sal_Int32 nBegCol = 0;
sal_Int32 nBegRow = 0;
Point aBegPos( 0, 0 );
// end cell address and its position in drawing layer (bottom-right edge)
sal_Int32 nEndCol = mrMaxApiPos.Column + 1;
sal_Int32 nEndRow = mrMaxApiPos.Row + 1;
Point aEndPos( maDrawPageSize.Width, maDrawPageSize.Height );
// starting point for interval search
sal_Int32 nMidCol, nMidRow;
bool bLoopCols = lclPrepareInterval( nBegCol, nMidCol, nEndCol, aBegPos.X, aEndPos.X, rPosition.X );
bool bLoopRows = lclPrepareInterval( nBegRow, nMidRow, nEndRow, aBegPos.Y, aEndPos.Y, rPosition.Y );
Point aMidPos = getCellPosition( nMidCol, nMidRow );
/* The loop will find the column/row index of the cell right of/below
the cell containing the passed point, unless the point is located at
the top or left border of the containing cell. */
while( bLoopCols || bLoopRows )
{
bLoopCols = bLoopCols && lclUpdateInterval( nBegCol, nMidCol, nEndCol, aBegPos.X, aMidPos.X, aEndPos.X, rPosition.X );
bLoopRows = bLoopRows && lclUpdateInterval( nBegRow, nMidRow, nEndRow, aBegPos.Y, aMidPos.Y, aEndPos.Y, rPosition.Y );
aMidPos = getCellPosition( nMidCol, nMidRow );
}
/* The cell left of/above the current search position contains the passed
point, unless the point is located on the top/left border of the cell,
or the last column/row of the sheet has been reached. */
if( aMidPos.X > rPosition.X ) --nMidCol;
if( aMidPos.Y > rPosition.Y ) --nMidRow;
return CellAddress( getSheetIndex(), nMidCol, nMidRow );
}
CellRangeAddress WorksheetGlobals::getCellRangeFromRectangle( const Rectangle& rRect ) const
{
CellAddress aStartAddr = getCellAddressFromPosition( Point( rRect.X, rRect.Y ) );
Point aBotRight( rRect.X + rRect.Width, rRect.Y + rRect.Height );
CellAddress aEndAddr = getCellAddressFromPosition( aBotRight );
bool bMultiCols = aStartAddr.Column < aEndAddr.Column;
bool bMultiRows = aStartAddr.Row < aEndAddr.Row;
if( bMultiCols || bMultiRows )
{
/* Reduce end position of the cell range to previous column or row, if
the rectangle ends exactly between two columns or rows. */
Point aEndPos = getCellPosition( aEndAddr.Column, aEndAddr.Row );
if( bMultiCols && (aBotRight.X <= aEndPos.X) )
--aEndAddr.Column;
if( bMultiRows && (aBotRight.Y <= aEndPos.Y) )
--aEndAddr.Row;
}
return CellRangeAddress( getSheetIndex(), aStartAddr.Column, aStartAddr.Row, aEndAddr.Column, aEndAddr.Row );
}
void WorksheetGlobals::setPageBreak( const PageBreakModel& rModel, bool bRowBreak )
{
if( rModel.mbManual && (rModel.mnColRow > 0) )
{
PropertySet aPropSet( bRowBreak ? getRow( rModel.mnColRow ) : getColumn( rModel.mnColRow ) );
aPropSet.setProperty( PROP_IsStartOfNewPage, true );
}
}
void WorksheetGlobals::setHyperlink( const HyperlinkModel& rModel )
{
maHyperlinks.push_back( rModel );
}
void WorksheetGlobals::setValidation( const ValidationModel& rModel )
{
maValidations.push_back( rModel );
}
void WorksheetGlobals::setDrawingPath( const OUString& rDrawingPath )
{
maDrawingPath = rDrawingPath;
}
void WorksheetGlobals::setVmlDrawingPath( const OUString& rVmlDrawingPath )
{
maVmlDrawingPath = rVmlDrawingPath;
}
void WorksheetGlobals::extendUsedArea( const CellAddress& rAddress )
{
maUsedArea.StartColumn = ::std::min( maUsedArea.StartColumn, rAddress.Column );
maUsedArea.StartRow = ::std::min( maUsedArea.StartRow, rAddress.Row );
maUsedArea.EndColumn = ::std::max( maUsedArea.EndColumn, rAddress.Column );
maUsedArea.EndRow = ::std::max( maUsedArea.EndRow, rAddress.Row );
}
void WorksheetGlobals::extendUsedArea( const CellRangeAddress& rRange )
{
extendUsedArea( CellAddress( rRange.Sheet, rRange.StartColumn, rRange.StartRow ) );
extendUsedArea( CellAddress( rRange.Sheet, rRange.EndColumn, rRange.EndRow ) );
}
void WorksheetGlobals::extendShapeBoundingBox( const Rectangle& rShapeRect )
{
if( (maShapeBoundingBox.Width == 0) && (maShapeBoundingBox.Height == 0) )
{
// width and height of maShapeBoundingBox are assumed to be zero on first cell
maShapeBoundingBox = rShapeRect;
}
else
{
sal_Int32 nEndX = ::std::max( maShapeBoundingBox.X + maShapeBoundingBox.Width, rShapeRect.X + rShapeRect.Width );
sal_Int32 nEndY = ::std::max( maShapeBoundingBox.Y + maShapeBoundingBox.Height, rShapeRect.Y + rShapeRect.Height );
maShapeBoundingBox.X = ::std::min( maShapeBoundingBox.X, rShapeRect.X );
maShapeBoundingBox.Y = ::std::min( maShapeBoundingBox.Y, rShapeRect.Y );
maShapeBoundingBox.Width = nEndX - maShapeBoundingBox.X;
maShapeBoundingBox.Height = nEndY - maShapeBoundingBox.Y;
}
}
void WorksheetGlobals::setBaseColumnWidth( sal_Int32 nWidth )
{
// do not modify width, if setDefaultColumnWidth() has been used
if( !mbHasDefWidth && (nWidth > 0) )
{
// #i3006# add 5 pixels padding to the width
const UnitConverter& rUnitConv = getUnitConverter();
maDefColModel.mfWidth = rUnitConv.scaleFromMm100(
rUnitConv.scaleToMm100( nWidth, UNIT_DIGIT ) + rUnitConv.scaleToMm100( 5, UNIT_SCREENX ), UNIT_DIGIT );
}
}
void WorksheetGlobals::setDefaultColumnWidth( double fWidth )
{
// overrides a width set with setBaseColumnWidth()
if( fWidth > 0.0 )
{
maDefColModel.mfWidth = fWidth;
mbHasDefWidth = true;
}
}
void WorksheetGlobals::setColumnModel( const ColumnModel& rModel )
{
// convert 1-based OOXML column indexes to 0-based API column indexes
sal_Int32 nFirstCol = rModel.maRange.mnFirst - 1;
sal_Int32 nLastCol = rModel.maRange.mnLast - 1;
if( getAddressConverter().checkCol( nFirstCol, true ) && (nFirstCol <= nLastCol) )
{
// validate last column index
if( !getAddressConverter().checkCol( nLastCol, true ) )
nLastCol = mrMaxApiPos.Column;
// try to find entry in column model map that is able to merge with the passed model
bool bInsertModel = true;
if( !maColModels.empty() )
{
// find first column model range following nFirstCol (nFirstCol < aIt->first), or end of map
ColumnModelRangeMap::iterator aIt = maColModels.upper_bound( nFirstCol );
OSL_ENSURE( aIt == maColModels.end(), "WorksheetGlobals::setColModel - columns are unsorted" );
// if inserting before another column model, get last free column
OSL_ENSURE( (aIt == maColModels.end()) || (nLastCol < aIt->first), "WorksheetGlobals::setColModel - multiple models of the same column" );
if( aIt != maColModels.end() )
nLastCol = ::std::min( nLastCol, aIt->first - 1 );
if( aIt != maColModels.begin() )
{
// go to previous map element (which may be able to merge with the passed model)
--aIt;
// the usage of upper_bound() above ensures that aIt->first is less than or equal to nFirstCol now
sal_Int32& rnLastMapCol = aIt->second.second;
OSL_ENSURE( rnLastMapCol < nFirstCol, "WorksheetGlobals::setColModel - multiple models of the same column" );
nFirstCol = ::std::max( rnLastMapCol + 1, nFirstCol );
if( (rnLastMapCol + 1 == nFirstCol) && (nFirstCol <= nLastCol) && aIt->second.first.isMergeable( rModel ) )
{
// can merge with existing model, update last column index
rnLastMapCol = nLastCol;
bInsertModel = false;
}
}
}
if( nFirstCol <= nLastCol )
{
// insert the column model, if it has not been merged with another
if( bInsertModel )
maColModels[ nFirstCol ] = ColumnModelRange( rModel, nLastCol );
// set column formatting directly
convertColumnFormat( nFirstCol, nLastCol, rModel.mnXfId );
}
}
}
void WorksheetGlobals::convertColumnFormat( sal_Int32 nFirstCol, sal_Int32 nLastCol, sal_Int32 nXfId ) const
{
CellRangeAddress aRange( getSheetIndex(), nFirstCol, 0, nLastCol, mrMaxApiPos.Row );
if( getAddressConverter().validateCellRange( aRange, true, false ) )
{
PropertySet aPropSet( getCellRange( aRange ) );
getStyles().writeCellXfToPropertySet( aPropSet, nXfId );
}
}
void WorksheetGlobals::setDefaultRowSettings( double fHeight, bool bCustomHeight, bool bHidden, bool bThickTop, bool bThickBottom )
{
maDefRowModel.mfHeight = fHeight;
maDefRowModel.mbCustomHeight = bCustomHeight;
maDefRowModel.mbHidden = bHidden;
maDefRowModel.mbThickTop = bThickTop;
maDefRowModel.mbThickBottom = bThickBottom;
}
void WorksheetGlobals::setRowModel( const RowModel& rModel )
{
// convert 1-based OOXML row index to 0-based API row index
sal_Int32 nRow = rModel.mnRow - 1;
if( getAddressConverter().checkRow( nRow, true ) )
{
// try to find entry in row model map that is able to merge with the passed model
bool bInsertModel = true;
bool bUnusedRow = true;
if( !maRowModels.empty() )
{
// find first row model range following nRow (nRow < aIt->first), or end of map
RowModelRangeMap::iterator aIt = maRowModels.upper_bound( nRow );
OSL_ENSURE( aIt == maRowModels.end(), "WorksheetGlobals::setRowModel - rows are unsorted" );
if( aIt != maRowModels.begin() )
{
// go to previous map element (which may be able to merge with the passed model)
--aIt;
// the usage of upper_bound() above ensures that aIt->first is less than or equal to nRow now
sal_Int32& rnLastMapRow = aIt->second.second;
bUnusedRow = rnLastMapRow < nRow;
OSL_ENSURE( bUnusedRow, "WorksheetGlobals::setRowModel - multiple models of the same row" );
if( (rnLastMapRow + 1 == nRow) && aIt->second.first.isMergeable( rModel ) )
{
// can merge with existing model, update last row index
++rnLastMapRow;
bInsertModel = false;
}
}
}
if( bUnusedRow )
{
// insert the row model, if it has not been merged with another
if( bInsertModel )
maRowModels[ nRow ] = RowModelRange( rModel, nRow );
// set row formatting
maSheetData.setRowFormat( nRow, rModel.mnXfId, rModel.mbCustomFormat );
// set column spans
maSheetData.setColSpans( nRow, rModel.maColSpans );
}
}
lclUpdateProgressBar( mxRowProgress, maUsedArea, nRow );
}
void WorksheetGlobals::setManualRowHeight( sal_Int32 nRow )
{
maManualRowHeights.insert( nRow );
}
void WorksheetGlobals::initializeWorksheetImport()
{
// set default cell style for unused cells
PropertySet aPropSet( mxSheet );
aPropSet.setProperty( PROP_CellStyle, getStyles().getDefaultStyleName() );
/* Remember the current sheet index in global data, needed by global
objects, e.g. the chart converter. */
setCurrentSheetIndex( getSheetIndex() );
}
void WorksheetGlobals::finalizeWorksheetImport()
{
lclUpdateProgressBar( mxRowProgress, 1.0 );
maSheetData.finalizeImport();
lclUpdateProgressBar( mxFinalProgress, 0.25 );
finalizeHyperlinkRanges();
finalizeValidationRanges();
maAutoFilters.finalizeImport( getSheetIndex() );
maCondFormats.finalizeImport();
maQueryTables.finalizeImport();
maSheetSett.finalizeImport();
maPageSett.finalizeImport();
maSheetViewSett.finalizeImport();
lclUpdateProgressBar( mxFinalProgress, 0.5 );
convertColumns();
convertRows();
lclUpdateProgressBar( mxFinalProgress, 0.75 );
finalizeDrawings();
lclUpdateProgressBar( mxFinalProgress, 1.0 );
// forget current sheet index in global data
setCurrentSheetIndex( -1 );
}
// private --------------------------------------------------------------------
void WorksheetGlobals::finalizeHyperlinkRanges() const
{
for( HyperlinkModelList::const_iterator aIt = maHyperlinks.begin(), aEnd = maHyperlinks.end(); aIt != aEnd; ++aIt )
{
OUString aUrl = getHyperlinkUrl( *aIt );
// try to insert URL into each cell of the range
if( aUrl.getLength() > 0 )
for( CellAddress aAddress( getSheetIndex(), aIt->maRange.StartColumn, aIt->maRange.StartRow ); aAddress.Row <= aIt->maRange.EndRow; ++aAddress.Row )
for( aAddress.Column = aIt->maRange.StartColumn; aAddress.Column <= aIt->maRange.EndColumn; ++aAddress.Column )
insertHyperlink( aAddress, aUrl );
}
}
OUString WorksheetGlobals::getHyperlinkUrl( const HyperlinkModel& rHyperlink ) const
{
OUStringBuffer aUrlBuffer;
if( rHyperlink.maTarget.getLength() > 0 )
aUrlBuffer.append( getBaseFilter().getAbsoluteUrl( rHyperlink.maTarget ) );
if( rHyperlink.maLocation.getLength() > 0 )
aUrlBuffer.append( sal_Unicode( '#' ) ).append( rHyperlink.maLocation );
OUString aUrl = aUrlBuffer.makeStringAndClear();
// convert '#SheetName!A1' to '#SheetName.A1'
if( (aUrl.getLength() > 0) && (aUrl[ 0 ] == '#') )
{
sal_Int32 nSepPos = aUrl.lastIndexOf( '!' );
if( nSepPos > 0 )
{
// replace the exclamation mark with a period
aUrl = aUrl.replaceAt( nSepPos, 1, OUString( sal_Unicode( '.' ) ) );
// #i66592# convert sheet names that have been renamed on import
OUString aSheetName = aUrl.copy( 1, nSepPos - 1 );
OUString aCalcName = getWorksheets().getCalcSheetName( aSheetName );
if( aCalcName.getLength() > 0 )
aUrl = aUrl.replaceAt( 1, nSepPos - 1, aCalcName );
}
}
return aUrl;
}
void WorksheetGlobals::insertHyperlink( const CellAddress& rAddress, const OUString& rUrl ) const
{
Reference< XCell > xCell = getCell( rAddress );
if( xCell.is() ) switch( xCell->getType() )
{
// #i54261# restrict creation of URL field to text cells
case CellContentType_TEXT:
{
Reference< XText > xText( xCell, UNO_QUERY );
if( xText.is() )
{
// create a URL field object and set its properties
Reference< XTextContent > xUrlField( getBaseFilter().getModelFactory()->createInstance( maUrlTextField ), UNO_QUERY );
OSL_ENSURE( xUrlField.is(), "WorksheetGlobals::insertHyperlink - cannot create text field" );
if( xUrlField.is() )
{
// properties of the URL field
PropertySet aPropSet( xUrlField );
aPropSet.setProperty( PROP_URL, rUrl );
aPropSet.setProperty( PROP_Representation, xText->getString() );
try
{
// insert the field into the cell
xText->setString( OUString() );
Reference< XTextRange > xRange( xText->createTextCursor(), UNO_QUERY_THROW );
xText->insertTextContent( xRange, xUrlField, sal_False );
}
catch( const Exception& )
{
OSL_ENSURE( false, "WorksheetGlobals::insertHyperlink - cannot insert text field" );
}
}
}
}
break;
// fix for #i31050# disabled, HYPERLINK is not able to return numeric value (#i91351#)
#if 0
// #i31050# replace number with HYPERLINK function
case CellContentType_VALUE:
{
Reference< XFormulaTokens > xTokens( xCell, UNO_QUERY );
ApiTokenSequence aTokens = getFormulaParser().convertNumberToHyperlink( rUrl, xCell->getValue() );
OSL_ENSURE( xTokens.is(), "WorksheetHelper::insertHyperlink - missing formula token interface" );
if( xTokens.is() && aTokens.hasElements() )
xTokens->setTokens( aTokens );
}
break;
#endif
default:;
}
}
void WorksheetGlobals::finalizeValidationRanges() const
{
for( ValidationModelList::const_iterator aIt = maValidations.begin(), aEnd = maValidations.end(); aIt != aEnd; ++aIt )
{
PropertySet aPropSet( getCellRangeList( aIt->maRanges ) );
Reference< XPropertySet > xValidation( aPropSet.getAnyProperty( PROP_Validation ), UNO_QUERY );
if( xValidation.is() )
{
PropertySet aValProps( xValidation );
try
{
sal_Int32 nIndex = 0;
OUString aToken = aIt->msRef.getToken( 0, ' ', nIndex );
Reference<XSpreadsheet> xSheet = getSheetFromDoc( getCurrentSheetIndex() );
Reference<XCellRange> xDBCellRange;
Reference<XCell> xCell;
xDBCellRange = xSheet->getCellRangeByName( aToken );
xCell = xDBCellRange->getCellByPosition( 0, 0 );
Reference<XCellAddressable> xCellAddressable( xCell, UNO_QUERY_THROW );
CellAddress aFirstCell = xCellAddressable->getCellAddress();
Reference<XSheetCondition> xCondition( xValidation, UNO_QUERY_THROW );
xCondition->setSourcePosition( aFirstCell );
}
catch( Exception& )
{
}
// convert validation type to API enum
ValidationType eType = ValidationType_ANY;
switch( aIt->mnType )
{
case XML_custom: eType = ValidationType_CUSTOM; break;
case XML_date: eType = ValidationType_DATE; break;
case XML_decimal: eType = ValidationType_DECIMAL; break;
case XML_list: eType = ValidationType_LIST; break;
case XML_none: eType = ValidationType_ANY; break;
case XML_textLength: eType = ValidationType_TEXT_LEN; break;
case XML_time: eType = ValidationType_TIME; break;
case XML_whole: eType = ValidationType_WHOLE; break;
default: OSL_ENSURE( false, "WorksheetGlobals::finalizeValidationRanges - unknown validation type" );
}
aValProps.setProperty( PROP_Type, eType );
// convert error alert style to API enum
ValidationAlertStyle eAlertStyle = ValidationAlertStyle_STOP;
switch( aIt->mnErrorStyle )
{
case XML_information: eAlertStyle = ValidationAlertStyle_INFO; break;
case XML_stop: eAlertStyle = ValidationAlertStyle_STOP; break;
case XML_warning: eAlertStyle = ValidationAlertStyle_WARNING; break;
default: OSL_ENSURE( false, "WorksheetGlobals::finalizeValidationRanges - unknown error style" );
}
aValProps.setProperty( PROP_ErrorAlertStyle, eAlertStyle );
// convert dropdown style to API visibility constants
sal_Int16 nVisibility = aIt->mbNoDropDown ? TableValidationVisibility::INVISIBLE : TableValidationVisibility::UNSORTED;
aValProps.setProperty( PROP_ShowList, nVisibility );
// messages
aValProps.setProperty( PROP_ShowInputMessage, aIt->mbShowInputMsg );
aValProps.setProperty( PROP_InputTitle, aIt->maInputTitle );
aValProps.setProperty( PROP_InputMessage, aIt->maInputMessage );
aValProps.setProperty( PROP_ShowErrorMessage, aIt->mbShowErrorMsg );
aValProps.setProperty( PROP_ErrorTitle, aIt->maErrorTitle );
aValProps.setProperty( PROP_ErrorMessage, aIt->maErrorMessage );
// allow blank cells
aValProps.setProperty( PROP_IgnoreBlankCells, aIt->mbAllowBlank );
try
{
// condition operator
Reference< XSheetCondition > xSheetCond( xValidation, UNO_QUERY_THROW );
xSheetCond->setOperator( CondFormatBuffer::convertToApiOperator( aIt->mnOperator ) );
// condition formulas
Reference< XMultiFormulaTokens > xTokens( xValidation, UNO_QUERY_THROW );
xTokens->setTokens( 0, aIt->maTokens1 );
xTokens->setTokens( 1, aIt->maTokens2 );
}
catch( Exception& )
{
}
// write back validation settings to cell range(s)
aPropSet.setProperty( PROP_Validation, xValidation );
}
}
}
void WorksheetGlobals::convertColumns()
{
sal_Int32 nNextCol = 0;
sal_Int32 nMaxCol = mrMaxApiPos.Column;
// stores first grouped column index for each level
OutlineLevelVec aColLevels;
for( ColumnModelRangeMap::iterator aIt = maColModels.begin(), aEnd = maColModels.end(); aIt != aEnd; ++aIt )
{
// column indexes are stored 0-based in maColModels
ValueRange aColRange( ::std::max( aIt->first, nNextCol ), ::std::min( aIt->second.second, nMaxCol ) );
// process gap between two column models, use default column model
if( nNextCol < aColRange.mnFirst )
convertColumns( aColLevels, ValueRange( nNextCol, aColRange.mnFirst - 1 ), maDefColModel );
// process the column model
convertColumns( aColLevels, aColRange, aIt->second.first );
// cache next column to be processed
nNextCol = aColRange.mnLast + 1;
}
// remaining default columns to end of sheet
convertColumns( aColLevels, ValueRange( nNextCol, nMaxCol ), maDefColModel );
// close remaining column outlines spanning to end of sheet
convertOutlines( aColLevels, nMaxCol + 1, 0, false, false );
}
void WorksheetGlobals::convertColumns( OutlineLevelVec& orColLevels,
const ValueRange& rColRange, const ColumnModel& rModel )
{
PropertySet aPropSet( getColumns( rColRange ) );
// column width: convert 'number of characters' to column width in 1/100 mm
sal_Int32 nWidth = getUnitConverter().scaleToMm100( rModel.mfWidth, UNIT_DIGIT );
// macro sheets have double width
if( meSheetType == SHEETTYPE_MACROSHEET )
nWidth *= 2;
if( nWidth > 0 )
aPropSet.setProperty( PROP_Width, nWidth );
// hidden columns: TODO: #108683# hide columns later?
if( rModel.mbHidden )
aPropSet.setProperty( PROP_IsVisible, false );
// outline settings for this column range
convertOutlines( orColLevels, rColRange.mnFirst, rModel.mnLevel, rModel.mbCollapsed, false );
}
void WorksheetGlobals::convertRows()
{
sal_Int32 nNextRow = 0;
sal_Int32 nMaxRow = mrMaxApiPos.Row;
// stores first grouped row index for each level
OutlineLevelVec aRowLevels;
for( RowModelRangeMap::iterator aIt = maRowModels.begin(), aEnd = maRowModels.end(); aIt != aEnd; ++aIt )
{
// row indexes are stored 0-based in maRowModels
ValueRange aRowRange( ::std::max( aIt->first, nNextRow ), ::std::min( aIt->second.second, nMaxRow ) );
// process gap between two row models, use default row model
if( nNextRow < aRowRange.mnFirst )
convertRows( aRowLevels, ValueRange( nNextRow, aRowRange.mnFirst - 1 ), maDefRowModel );
// process the row model
convertRows( aRowLevels, aRowRange, aIt->second.first, maDefRowModel.mfHeight );
// cache next row to be processed
nNextRow = aRowRange.mnLast + 1;
}
// remaining default rows to end of sheet
convertRows( aRowLevels, ValueRange( nNextRow, nMaxRow ), maDefRowModel );
// close remaining row outlines spanning to end of sheet
convertOutlines( aRowLevels, nMaxRow + 1, 0, false, true );
}
void WorksheetGlobals::convertRows( OutlineLevelVec& orRowLevels,
const ValueRange& rRowRange, const RowModel& rModel, double fDefHeight )
{
// row height: convert points to row height in 1/100 mm
double fHeight = (rModel.mfHeight >= 0.0) ? rModel.mfHeight : fDefHeight;
sal_Int32 nHeight = getUnitConverter().scaleToMm100( fHeight, UNIT_POINT );
if( nHeight > 0 )
{
/* Get all rows that have custom height inside the passed row model.
If the model has the custom height flag set, all its rows have
custom height, otherwise get all rows specified in the class member
maManualRowHeights that are inside the passed row model. */
ValueRangeVector aManualRows;
if( rModel.mbCustomHeight )
aManualRows.push_back( rRowRange );
else
aManualRows = maManualRowHeights.getIntersection( rRowRange );
for( ValueRangeVector::const_iterator aIt = aManualRows.begin(), aEnd = aManualRows.end(); aIt != aEnd; ++aIt )
{
PropertySet aPropSet( getRows( *aIt ) );
aPropSet.setProperty( PROP_Height, nHeight );
}
}
// hidden rows: TODO: #108683# hide rows later?
if( rModel.mbHidden )
{
PropertySet aPropSet( getRows( rRowRange ) );
/* #i116460# Use VisibleFlag instead of IsVisible: directly set the
flag, without drawing layer update etc. (only possible before
shapes are inserted). */
aPropSet.setProperty( PROP_VisibleFlag, false );
}
// outline settings for this row range
convertOutlines( orRowLevels, rRowRange.mnFirst, rModel.mnLevel, rModel.mbCollapsed, true );
}
void WorksheetGlobals::convertOutlines( OutlineLevelVec& orLevels,
sal_Int32 nColRow, sal_Int32 nLevel, bool bCollapsed, bool bRows )
{
/* It is ensured from caller functions, that this function is called
without any gaps between the processed column or row ranges. */
OSL_ENSURE( nLevel >= 0, "WorksheetGlobals::convertOutlines - negative outline level" );
nLevel = ::std::max< sal_Int32 >( nLevel, 0 );
sal_Int32 nSize = orLevels.size();
if( nSize < nLevel )
{
// Outline level increased. Push the begin column position.
for( sal_Int32 nIndex = nSize; nIndex < nLevel; ++nIndex )
orLevels.push_back( nColRow );
}
else if( nLevel < nSize )
{
// Outline level decreased. Pop them all out.
for( sal_Int32 nIndex = nLevel; nIndex < nSize; ++nIndex )
{
sal_Int32 nFirstInLevel = orLevels.back();
orLevels.pop_back();
groupColumnsOrRows( nFirstInLevel, nColRow - 1, bCollapsed, bRows );
bCollapsed = false; // collapse only once
}
}
}
void WorksheetGlobals::groupColumnsOrRows( sal_Int32 nFirstColRow, sal_Int32 nLastColRow, bool bCollapse, bool bRows )
{
try
{
Reference< XSheetOutline > xOutline( mxSheet, UNO_QUERY_THROW );
if( bRows )
{
CellRangeAddress aRange( getSheetIndex(), 0, nFirstColRow, 0, nLastColRow );
xOutline->group( aRange, TableOrientation_ROWS );
if( bCollapse )
xOutline->hideDetail( aRange );
}
else
{
CellRangeAddress aRange( getSheetIndex(), nFirstColRow, 0, nLastColRow, 0 );
xOutline->group( aRange, TableOrientation_COLUMNS );
if( bCollapse )
xOutline->hideDetail( aRange );
}
}
catch( Exception& )
{
}
}
void WorksheetGlobals::finalizeDrawings()
{
// calculate the current drawing page size (after rows/columns are imported)
PropertySet aRangeProp( getCellRange( CellRangeAddress( getSheetIndex(), 0, 0, mrMaxApiPos.Column, mrMaxApiPos.Row ) ) );
aRangeProp.getProperty( maDrawPageSize, PROP_Size );
switch( getFilterType() )
{
case FILTER_OOXML:
// import DML and VML
if( maDrawingPath.getLength() > 0 )
importOoxFragment( new DrawingFragment( *this, maDrawingPath ) );
if( maVmlDrawingPath.getLength() > 0 )
importOoxFragment( new VmlDrawingFragment( *this, maVmlDrawingPath ) );
break;
case FILTER_BIFF:
// convert BIFF3-BIFF5 drawing objects, or import and convert DFF stream
getBiffDrawing().finalizeImport();
break;
case FILTER_UNKNOWN:
break;
}
// comments (after callout shapes have been imported from VML/DFF)
maComments.finalizeImport();
/* Extend used area of the sheet by cells covered with drawing objects.
Needed if the imported document is inserted as "OLE object from file"
and thus does not provide an OLE size property by itself. */
if( (maShapeBoundingBox.Width > 0) || (maShapeBoundingBox.Height > 0) )
extendUsedArea( getCellRangeFromRectangle( maShapeBoundingBox ) );
// if no used area is set, default to A1
if( maUsedArea.StartColumn > maUsedArea.EndColumn )
maUsedArea.StartColumn = maUsedArea.EndColumn = 0;
if( maUsedArea.StartRow > maUsedArea.EndRow )
maUsedArea.StartRow = maUsedArea.EndRow = 0;
/* Register the used area of this sheet in global view settings. The
global view settings will set the visible area if this document is an
embedded OLE object. */
getViewSettings().setSheetUsedArea( maUsedArea );
/* #i103686# Set right-to-left sheet layout. Must be done after all
drawing shapes to simplify calculation of shape coordinates. */
if( maSheetViewSett.isSheetRightToLeft() )
{
PropertySet aPropSet( mxSheet );
aPropSet.setProperty( PROP_TableLayout, WritingMode2::RL_TB );
}
}
// ============================================================================
// ============================================================================
WorksheetHelper::WorksheetHelper( WorksheetGlobals& rSheetGlob ) :
WorkbookHelper( rSheetGlob ),
mrSheetGlob( rSheetGlob )
{
}
/*static*/ WorksheetGlobalsRef WorksheetHelper::constructGlobals( const WorkbookHelper& rHelper,
const ISegmentProgressBarRef& rxProgressBar, WorksheetType eSheetType, sal_Int16 nSheet )
{
WorksheetGlobalsRef xSheetGlob( new WorksheetGlobals( rHelper, rxProgressBar, eSheetType, nSheet ) );
if( !xSheetGlob->isValidSheet() )
xSheetGlob.reset();
return xSheetGlob;
}
WorksheetType WorksheetHelper::getSheetType() const
{
return mrSheetGlob.getSheetType();
}
sal_Int16 WorksheetHelper::getSheetIndex() const
{
return mrSheetGlob.getSheetIndex();
}
const Reference< XSpreadsheet >& WorksheetHelper::getSheet() const
{
return mrSheetGlob.getSheet();
}
Reference< XCell > WorksheetHelper::getCell( const CellAddress& rAddress ) const
{
return mrSheetGlob.getCell( rAddress );
}
Reference< XCellRange > WorksheetHelper::getCellRange( const CellRangeAddress& rRange ) const
{
return mrSheetGlob.getCellRange( rRange );
}
Reference< XSheetCellRanges > WorksheetHelper::getCellRangeList( const ApiCellRangeList& rRanges ) const
{
return mrSheetGlob.getCellRangeList( rRanges );
}
CellAddress WorksheetHelper::getCellAddress( const Reference< XCell >& rxCell )
{
CellAddress aAddress;
Reference< XCellAddressable > xAddressable( rxCell, UNO_QUERY );
OSL_ENSURE( xAddressable.is(), "WorksheetHelper::getCellAddress - cell reference not addressable" );
if( xAddressable.is() )
aAddress = xAddressable->getCellAddress();
return aAddress;
}
CellRangeAddress WorksheetHelper::getRangeAddress( const Reference< XCellRange >& rxRange )
{
CellRangeAddress aRange;
Reference< XCellRangeAddressable > xAddressable( rxRange, UNO_QUERY );
OSL_ENSURE( xAddressable.is(), "WorksheetHelper::getRangeAddress - cell range reference not addressable" );
if( xAddressable.is() )
aRange = xAddressable->getRangeAddress();
return aRange;
}
Reference< XCellRange > WorksheetHelper::getColumn( sal_Int32 nCol ) const
{
return mrSheetGlob.getColumn( nCol );
}
Reference< XCellRange > WorksheetHelper::getRow( sal_Int32 nRow ) const
{
return mrSheetGlob.getRow( nRow );
}
Reference< XTableColumns > WorksheetHelper::getColumns( const ValueRange& rColRange ) const
{
return mrSheetGlob.getColumns( rColRange );
}
Reference< XTableRows > WorksheetHelper::getRows( const ValueRange& rRowRange ) const
{
return mrSheetGlob.getRows( rRowRange );
}
Reference< XDrawPage > WorksheetHelper::getDrawPage() const
{
return mrSheetGlob.getDrawPage();
}
Point WorksheetHelper::getCellPosition( sal_Int32 nCol, sal_Int32 nRow ) const
{
return mrSheetGlob.getCellPosition( nCol, nRow );
}
Size WorksheetHelper::getCellSize( sal_Int32 nCol, sal_Int32 nRow ) const
{
return mrSheetGlob.getCellSize( nCol, nRow );
}
Size WorksheetHelper::getDrawPageSize() const
{
return mrSheetGlob.getDrawPageSize();
}
SheetDataBuffer& WorksheetHelper::getSheetData() const
{
return mrSheetGlob.getSheetData();
}
CondFormatBuffer& WorksheetHelper::getCondFormats() const
{
return mrSheetGlob.getCondFormats();
}
CommentsBuffer& WorksheetHelper::getComments() const
{
return mrSheetGlob.getComments();
}
AutoFilterBuffer& WorksheetHelper::getAutoFilters() const
{
return mrSheetGlob.getAutoFilters();
}
QueryTableBuffer& WorksheetHelper::getQueryTables() const
{
return mrSheetGlob.getQueryTables();
}
WorksheetSettings& WorksheetHelper::getWorksheetSettings() const
{
return mrSheetGlob.getWorksheetSettings();
}
PageSettings& WorksheetHelper::getPageSettings() const
{
return mrSheetGlob.getPageSettings();
}
SheetViewSettings& WorksheetHelper::getSheetViewSettings() const
{
return mrSheetGlob.getSheetViewSettings();
}
VmlDrawing& WorksheetHelper::getVmlDrawing() const
{
return mrSheetGlob.getVmlDrawing();
}
BiffSheetDrawing& WorksheetHelper::getBiffDrawing() const
{
return mrSheetGlob.getBiffDrawing();
}
void WorksheetHelper::setSheetType( WorksheetType eSheetType )
{
mrSheetGlob.setSheetType( eSheetType );
}
void WorksheetHelper::setPageBreak( const PageBreakModel& rModel, bool bRowBreak )
{
mrSheetGlob.setPageBreak( rModel, bRowBreak );
}
void WorksheetHelper::setHyperlink( const HyperlinkModel& rModel )
{
mrSheetGlob.setHyperlink( rModel );
}
void WorksheetHelper::setValidation( const ValidationModel& rModel )
{
mrSheetGlob.setValidation( rModel );
}
void WorksheetHelper::setLabelRanges( const ApiCellRangeList& rColRanges, const ApiCellRangeList& rRowRanges )
{
const CellAddress& rMaxPos = getAddressConverter().getMaxApiAddress();
PropertySet aPropSet( getSheet() );
if( !rColRanges.empty() )
{
Reference< XLabelRanges > xLabelRanges( aPropSet.getAnyProperty( PROP_ColumnLabelRanges ), UNO_QUERY );
if( xLabelRanges.is() )
{
for( ApiCellRangeList::const_iterator aIt = rColRanges.begin(), aEnd = rColRanges.end(); aIt != aEnd; ++aIt )
{
CellRangeAddress aDataRange = *aIt;
if( aDataRange.EndRow < rMaxPos.Row )
{
aDataRange.StartRow = aDataRange.EndRow + 1;
aDataRange.EndRow = rMaxPos.Row;
}
else if( aDataRange.StartRow > 0 )
{
aDataRange.EndRow = aDataRange.StartRow - 1;
aDataRange.StartRow = 0;
}
xLabelRanges->addNew( *aIt, aDataRange );
}
}
}
if( !rRowRanges.empty() )
{
Reference< XLabelRanges > xLabelRanges( aPropSet.getAnyProperty( PROP_RowLabelRanges ), UNO_QUERY );
if( xLabelRanges.is() )
{
for( ApiCellRangeList::const_iterator aIt = rRowRanges.begin(), aEnd = rRowRanges.end(); aIt != aEnd; ++aIt )
{
CellRangeAddress aDataRange = *aIt;
if( aDataRange.EndColumn < rMaxPos.Column )
{
aDataRange.StartColumn = aDataRange.EndColumn + 1;
aDataRange.EndColumn = rMaxPos.Column;
}
else if( aDataRange.StartColumn > 0 )
{
aDataRange.EndColumn = aDataRange.StartColumn - 1;
aDataRange.StartColumn = 0;
}
xLabelRanges->addNew( *aIt, aDataRange );
}
}
}
}
void WorksheetHelper::setDrawingPath( const OUString& rDrawingPath )
{
mrSheetGlob.setDrawingPath( rDrawingPath );
}
void WorksheetHelper::setVmlDrawingPath( const OUString& rVmlDrawingPath )
{
mrSheetGlob.setVmlDrawingPath( rVmlDrawingPath );
}
void WorksheetHelper::extendUsedArea( const CellAddress& rAddress )
{
mrSheetGlob.extendUsedArea( rAddress );
}
void WorksheetHelper::extendUsedArea( const CellRangeAddress& rRange )
{
mrSheetGlob.extendUsedArea( rRange );
}
void WorksheetHelper::extendShapeBoundingBox( const Rectangle& rShapeRect )
{
mrSheetGlob.extendShapeBoundingBox( rShapeRect );
}
void WorksheetHelper::setBaseColumnWidth( sal_Int32 nWidth )
{
mrSheetGlob.setBaseColumnWidth( nWidth );
}
void WorksheetHelper::setDefaultColumnWidth( double fWidth )
{
mrSheetGlob.setDefaultColumnWidth( fWidth );
}
void WorksheetHelper::setDefaultColumnFormat( sal_Int32 nFirstCol, sal_Int32 nLastCol, sal_Int32 nXfId )
{
mrSheetGlob.convertColumnFormat( nFirstCol, nLastCol, nXfId );
}
void WorksheetHelper::setColumnModel( const ColumnModel& rModel )
{
mrSheetGlob.setColumnModel( rModel );
}
void WorksheetHelper::setDefaultRowSettings( double fHeight, bool bCustomHeight, bool bHidden, bool bThickTop, bool bThickBottom )
{
mrSheetGlob.setDefaultRowSettings( fHeight, bCustomHeight, bHidden, bThickTop, bThickBottom );
}
void WorksheetHelper::setRowModel( const RowModel& rModel )
{
mrSheetGlob.setRowModel( rModel );
}
void WorksheetHelper::setManualRowHeight( sal_Int32 nRow )
{
mrSheetGlob.setManualRowHeight( nRow );
}
void WorksheetHelper::putValue( const CellAddress& rAddress, double fValue ) const
{
Reference< XCell > xCell = getCell( rAddress );
OSL_ENSURE( xCell.is(), "WorksheetHelper::putValue - missing cell interface" );
if( xCell.is() ) xCell->setValue( fValue );
}
void WorksheetHelper::putString( const CellAddress& rAddress, const OUString& rText ) const
{
Reference< XText > xText( getCell( rAddress ), UNO_QUERY );
OSL_ENSURE( xText.is(), "WorksheetHelper::putString - missing text interface" );
if( xText.is() ) xText->setString( rText );
}
void WorksheetHelper::putRichString( const CellAddress& rAddress, const RichString& rString, const Font* pFirstPortionFont ) const
{
Reference< XText > xText( getCell( rAddress ), UNO_QUERY );
OSL_ENSURE( xText.is(), "WorksheetHelper::putRichString - missing text interface" );
/* Passing false will always append the portions to the XText. This is
essential for special rich formatting attributes at the leading text
portion supported by edit cells only, e.g. font escapement. */
rString.convert( xText, false, pFirstPortionFont );
}
void WorksheetHelper::putFormulaTokens( const CellAddress& rAddress, const ApiTokenSequence& rTokens ) const
{
Reference< XFormulaTokens > xTokens( getCell( rAddress ), UNO_QUERY );
OSL_ENSURE( xTokens.is(), "WorksheetHelper::putFormulaTokens - missing token interface" );
if( xTokens.is() ) xTokens->setTokens( rTokens );
}
void WorksheetHelper::initializeWorksheetImport()
{
mrSheetGlob.initializeWorksheetImport();
}
void WorksheetHelper::finalizeWorksheetImport()
{
mrSheetGlob.finalizeWorksheetImport();
}
// ============================================================================
// ============================================================================
} // namespace xls
} // namespace oox