1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
4 *
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 */
19
20 #include <utility>
21 #include <workbookfragment.hxx>
22
23 #include <oox/core/filterbase.hxx>
24 #include <oox/core/xmlfilterbase.hxx>
25 #include <oox/drawingml/themefragmenthandler.hxx>
26 #include <oox/helper/attributelist.hxx>
27 #include <oox/helper/binaryinputstream.hxx>
28 #include <oox/helper/progressbar.hxx>
29 #include <oox/ole/olestorage.hxx>
30 #include <oox/token/namespaces.hxx>
31 #include <oox/token/tokens.hxx>
32
33 #include <chartsheetfragment.hxx>
34 #include <connectionsfragment.hxx>
35 #include <externallinkbuffer.hxx>
36 #include <externallinkfragment.hxx>
37 #include <formulabuffer.hxx>
38 #include <pivotcachebuffer.hxx>
39 #include <sharedstringsfragment.hxx>
40 #include <revisionfragment.hxx>
41 #include <stylesfragment.hxx>
42 #include <tablebuffer.hxx>
43 #include <themebuffer.hxx>
44 #include <viewsettings.hxx>
45 #include <workbooksettings.hxx>
46 #include <worksheetbuffer.hxx>
47 #include <worksheethelper.hxx>
48 #include <worksheetfragment.hxx>
49 #include <extlstcontext.hxx>
50 #include <documentimport.hxx>
51 #include <biffhelper.hxx>
52
53 #include <document.hxx>
54 #include <drwlayer.hxx>
55 #include <docsh.hxx>
56 #include <calcconfig.hxx>
57 #include <globstr.hrc>
58 #include <scresid.hxx>
59 #include <scmod.hxx>
60 #include <formulaopt.hxx>
61
62 #include <vcl/svapp.hxx>
63 #include <vcl/timer.hxx>
64 #include <vcl/weld.hxx>
65
66 #include <oox/core/fastparser.hxx>
67 #include <svx/svdpage.hxx>
68 #include <comphelper/threadpool.hxx>
69 #include <sal/log.hxx>
70 #include <osl/diagnose.h>
71
72 #include <memory>
73
74 #include <oox/ole/vbaproject.hxx>
75
76 #include <comphelper/processfactory.hxx>
77 #include <officecfg/Office/Calc.hxx>
78
79 namespace oox::xls {
80
81 using namespace ::com::sun::star::io;
82 using namespace ::com::sun::star::table;
83 using namespace ::com::sun::star::uno;
84 using namespace ::oox::core;
85
86 using ::oox::drawingml::ThemeFragmentHandler;
87
88 namespace {
89
90 const double PROGRESS_LENGTH_GLOBALS = 0.1; /// 10% of progress bar for globals import.
91
92 } // namespace
93
WorkbookFragment(const WorkbookHelper & rHelper,const OUString & rFragmentPath)94 WorkbookFragment::WorkbookFragment( const WorkbookHelper& rHelper, const OUString& rFragmentPath ) :
95 WorkbookFragmentBase( rHelper, rFragmentPath )
96 {
97 }
98
onCreateContext(sal_Int32 nElement,const AttributeList & rAttribs)99 ContextHandlerRef WorkbookFragment::onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs )
100 {
101 switch( getCurrentElement() )
102 {
103 case XML_ROOT_CONTEXT:
104 if( nElement == XLS_TOKEN( workbook ) ) return this;
105 break;
106
107 case XLS_TOKEN( workbook ):
108 switch( nElement )
109 {
110 case XLS_TOKEN( sheets ):
111 case XLS_TOKEN( bookViews ):
112 case XLS_TOKEN( externalReferences ):
113 case XLS_TOKEN( definedNames ):
114 case XLS_TOKEN( pivotCaches ): return this;
115
116 case XLS_TOKEN( fileSharing ): getWorkbookSettings().importFileSharing( rAttribs ); break;
117 case XLS_TOKEN( workbookPr ): getWorkbookSettings().importWorkbookPr( rAttribs ); break;
118 case XLS_TOKEN( calcPr ): getWorkbookSettings().importCalcPr( rAttribs ); break;
119 case XLS_TOKEN( oleSize ): getViewSettings().importOleSize( rAttribs ); break;
120
121 case XLS_TOKEN( extLst ): return new ExtLstGlobalWorkbookContext( *this );
122 }
123 break;
124
125 case XLS_TOKEN( sheets ):
126 if( nElement == XLS_TOKEN( sheet ) ) getWorksheets().importSheet( rAttribs );
127 break;
128 case XLS_TOKEN( bookViews ):
129 if( nElement == XLS_TOKEN( workbookView ) ) getViewSettings().importWorkbookView( rAttribs );
130 break;
131 case XLS_TOKEN( externalReferences ):
132 if( nElement == XLS_TOKEN( externalReference ) ) importExternalReference( rAttribs );
133 break;
134 case XLS_TOKEN( definedNames ):
135 if( nElement == XLS_TOKEN( definedName ) ) { importDefinedName( rAttribs ); return this; } // collect formula
136 break;
137 case XLS_TOKEN( pivotCaches ):
138 if( nElement == XLS_TOKEN( pivotCache ) ) importPivotCache( rAttribs );
139 break;
140 }
141 return nullptr;
142 }
143
onCharacters(const OUString & rChars)144 void WorkbookFragment::onCharacters( const OUString& rChars )
145 {
146 if( isCurrentElement( XLS_TOKEN( definedName ) ) && mxCurrName )
147 mxCurrName->setFormula( rChars );
148 }
149
onCreateRecordContext(sal_Int32 nRecId,SequenceInputStream & rStrm)150 ContextHandlerRef WorkbookFragment::onCreateRecordContext( sal_Int32 nRecId, SequenceInputStream& rStrm )
151 {
152 switch( getCurrentElement() )
153 {
154 case XML_ROOT_CONTEXT:
155 if( nRecId == BIFF12_ID_WORKBOOK ) return this;
156 break;
157
158 case BIFF12_ID_WORKBOOK:
159 switch( nRecId )
160 {
161 case BIFF12_ID_SHEETS:
162 case BIFF12_ID_BOOKVIEWS:
163 case BIFF12_ID_EXTERNALREFS:
164 case BIFF12_ID_PIVOTCACHES: return this;
165
166 case BIFF12_ID_FILESHARING: getWorkbookSettings().importFileSharing( rStrm ); break;
167 case BIFF12_ID_WORKBOOKPR: getWorkbookSettings().importWorkbookPr( rStrm ); break;
168 case BIFF12_ID_CALCPR: getWorkbookSettings().importCalcPr( rStrm ); break;
169 case BIFF12_ID_OLESIZE: getViewSettings().importOleSize( rStrm ); break;
170 case BIFF12_ID_DEFINEDNAME: getDefinedNames().importDefinedName( rStrm ); break;
171 }
172 break;
173
174 case BIFF12_ID_SHEETS:
175 if( nRecId == BIFF12_ID_SHEET ) getWorksheets().importSheet( rStrm );
176 break;
177 case BIFF12_ID_BOOKVIEWS:
178 if( nRecId == BIFF12_ID_WORKBOOKVIEW ) getViewSettings().importWorkbookView( rStrm );
179 break;
180
181 case BIFF12_ID_EXTERNALREFS:
182 switch( nRecId )
183 {
184 case BIFF12_ID_EXTERNALREF: importExternalRef( rStrm ); break;
185 case BIFF12_ID_EXTERNALSELF: getExternalLinks().importExternalSelf( rStrm ); break;
186 case BIFF12_ID_EXTERNALSAME: getExternalLinks().importExternalSame( rStrm ); break;
187 case BIFF12_ID_EXTERNALADDIN: getExternalLinks().importExternalAddin( rStrm ); break;
188 case BIFF12_ID_EXTERNALSHEETS: getExternalLinks().importExternalSheets( rStrm ); break;
189 }
190 break;
191
192 case BIFF12_ID_PIVOTCACHES:
193 if( nRecId == BIFF12_ID_PIVOTCACHE ) importPivotCache( rStrm );
194 }
195 return nullptr;
196 }
197
getRecordInfos() const198 const RecordInfo* WorkbookFragment::getRecordInfos() const
199 {
200 static const RecordInfo spRecInfos[] =
201 {
202 { BIFF12_ID_BOOKVIEWS, BIFF12_ID_BOOKVIEWS + 1 },
203 { BIFF12_ID_EXTERNALREFS, BIFF12_ID_EXTERNALREFS + 1 },
204 { BIFF12_ID_FUNCTIONGROUPS, BIFF12_ID_FUNCTIONGROUPS + 2 },
205 { BIFF12_ID_PIVOTCACHE, BIFF12_ID_PIVOTCACHE + 1 },
206 { BIFF12_ID_PIVOTCACHES, BIFF12_ID_PIVOTCACHES + 1 },
207 { BIFF12_ID_SHEETS, BIFF12_ID_SHEETS + 1 },
208 { BIFF12_ID_WORKBOOK, BIFF12_ID_WORKBOOK + 1 },
209 { -1, -1 }
210 };
211 return spRecInfos;
212 }
213
214 namespace {
215
216 typedef std::pair<WorksheetGlobalsRef, FragmentHandlerRef> SheetFragmentHandler;
217 typedef std::vector<SheetFragmentHandler> SheetFragmentVector;
218
219 class WorkerThread : public comphelper::ThreadTask
220 {
221 sal_Int32 &mrSheetsLeft;
222 WorkbookFragment& mrWorkbookHandler;
223 rtl::Reference<FragmentHandler> mxHandler;
224
225 public:
WorkerThread(const std::shared_ptr<comphelper::ThreadTaskTag> & pTag,WorkbookFragment & rWorkbookHandler,rtl::Reference<FragmentHandler> xHandler,sal_Int32 & rSheetsLeft)226 WorkerThread( const std::shared_ptr<comphelper::ThreadTaskTag> & pTag,
227 WorkbookFragment& rWorkbookHandler,
228 rtl::Reference<FragmentHandler> xHandler,
229 sal_Int32 &rSheetsLeft ) :
230 comphelper::ThreadTask( pTag ),
231 mrSheetsLeft( rSheetsLeft ),
232 mrWorkbookHandler( rWorkbookHandler ),
233 mxHandler(std::move( xHandler ))
234 {
235 }
236
doWork()237 virtual void doWork() override
238 {
239 // We hold the solar mutex in all threads except for
240 // the small safe section of the inner loop in
241 // sheetdatacontext.cxx
242 SAL_INFO( "sc.filter", "start wait on solar" );
243 SolarMutexGuard aGuard;
244 SAL_INFO( "sc.filter", "got solar" );
245
246 std::unique_ptr<oox::core::FastParser> xParser(
247 oox::core::XmlFilterBase::createParser() );
248
249 SAL_INFO( "sc.filter", "start import" );
250 mrWorkbookHandler.importOoxFragment( mxHandler, *xParser );
251 SAL_INFO( "sc.filter", "end import, release solar" );
252 mrSheetsLeft--;
253 assert( mrSheetsLeft >= 0 );
254 if( mrSheetsLeft == 0 )
255 Application::EndYield();
256 }
257 };
258
259 class ProgressBarTimer : private Timer
260 {
261 // FIXME: really we should unify all sheet loading
262 // progress reporting into something pleasant.
263 class ProgressWrapper : public ISegmentProgressBar
264 {
265 double mfPosition;
266 ISegmentProgressBarRef mxWrapped;
267 public:
ProgressWrapper(ISegmentProgressBarRef xRef)268 explicit ProgressWrapper(ISegmentProgressBarRef xRef)
269 : mfPosition(0.0)
270 , mxWrapped(std::move(xRef))
271 {
272 }
273
274 // IProgressBar
getPosition() const275 virtual double getPosition() const override { return mfPosition; }
setPosition(double fPosition)276 virtual void setPosition( double fPosition ) override { mfPosition = fPosition; }
277 // ISegmentProgressBar
getFreeLength() const278 virtual double getFreeLength() const override { return 0.0; }
createSegment(double)279 virtual ISegmentProgressBarRef createSegment( double /* fLength */ ) override
280 {
281 return ISegmentProgressBarRef();
282 }
UpdateBar()283 void UpdateBar()
284 {
285 mxWrapped->setPosition( mfPosition );
286 }
287 };
288 std::vector< ISegmentProgressBarRef > aSegments;
289 public:
ProgressBarTimer()290 ProgressBarTimer() : Timer("sc ProgressBarTimer")
291 {
292 SetTimeout( 500 );
293 }
~ProgressBarTimer()294 virtual ~ProgressBarTimer() override
295 {
296 aSegments.clear();
297 }
wrapProgress(const ISegmentProgressBarRef & xProgress)298 const ISegmentProgressBarRef& wrapProgress( const ISegmentProgressBarRef &xProgress )
299 {
300 aSegments.push_back( std::make_shared<ProgressWrapper>( xProgress ) );
301 return aSegments.back();
302 }
Invoke()303 virtual void Invoke() override
304 {
305 for(std::shared_ptr<ISegmentProgressBar> & pSegment : aSegments)
306 static_cast< ProgressWrapper *>( pSegment.get() )->UpdateBar();
307 }
308 };
309
importSheetFragments(WorkbookFragment & rWorkbookHandler,SheetFragmentVector & rSheets)310 void importSheetFragments( WorkbookFragment& rWorkbookHandler, SheetFragmentVector& rSheets )
311 {
312 rWorkbookHandler.getDocImport().initForSheets();
313
314 // test sequential read in this mode
315 comphelper::ThreadPool &rSharedPool = comphelper::ThreadPool::getSharedOptimalPool();
316 std::shared_ptr<comphelper::ThreadTaskTag> pTag = comphelper::ThreadPool::createThreadTaskTag();
317
318 sal_Int32 nSheetsLeft = 0;
319 ProgressBarTimer aProgressUpdater;
320 for( auto& [rxSheetGlob, rxFragment] : rSheets )
321 {
322 // getting at the WorksheetGlobals is rather unpleasant
323 IWorksheetProgress *pProgress = WorksheetHelper::getWorksheetInterface( rxSheetGlob );
324 pProgress->setCustomRowProgress(
325 aProgressUpdater.wrapProgress(
326 pProgress->getRowProgress() ) );
327 rSharedPool.pushTask( std::make_unique<WorkerThread>( pTag, rWorkbookHandler, rxFragment,
328 /* ref */ nSheetsLeft ) );
329 nSheetsLeft++;
330 }
331
332 // coverity[loop_top] - this isn't an infinite loop where nSheetsLeft gets decremented by the above threads
333 while( nSheetsLeft > 0 && !Application::IsQuit())
334 {
335 // This is a much more controlled re-enterancy hazard than
336 // allowing a yield deeper inside the filter code for progress
337 // bar updating.
338 Application::Yield();
339 }
340 rSharedPool.waitUntilDone(pTag);
341
342 // threads joined in ThreadPool destructor
343 }
344
345 }
346
finalizeImport()347 void WorkbookFragment::finalizeImport()
348 {
349 ISegmentProgressBarRef xGlobalSegment = getProgressBar().createSegment( PROGRESS_LENGTH_GLOBALS );
350
351 // read the theme substream
352 OUString aThemeFragmentPath = getFragmentPathFromFirstTypeFromOfficeDoc( u"theme" );
353 auto& rOoxTheme = getTheme();
354
355 auto pTheme = rOoxTheme.oox::drawingml::Theme::getTheme(); // needed full name here because a conflict with WorkbookHelper and Theme in ThemeBuffer
356 if (!pTheme)
357 {
358 pTheme = std::make_shared<model::Theme>();
359 rOoxTheme.setTheme(pTheme);
360 }
361
362 if( !aThemeFragmentPath.isEmpty() )
363 importOoxFragment(new ThemeFragmentHandler(getFilter(), aThemeFragmentPath, rOoxTheme, *pTheme));
364 xGlobalSegment->setPosition( 0.25 );
365
366 // read the styles substream (requires finalized theme buffer)
367 OUString aStylesFragmentPath = getFragmentPathFromFirstTypeFromOfficeDoc( u"styles" );
368 if( !aStylesFragmentPath.isEmpty() )
369 importOoxFragment( new StylesFragment( *this, aStylesFragmentPath ) );
370 xGlobalSegment->setPosition( 0.5 );
371
372 // read the shared string table substream (requires finalized styles buffer)
373 OUString aSstFragmentPath = getFragmentPathFromFirstTypeFromOfficeDoc( u"sharedStrings" );
374 if( !aSstFragmentPath.isEmpty() )
375 if (!importOoxFragment( new SharedStringsFragment( *this, aSstFragmentPath ) ))
376 importOoxFragment(new SharedStringsFragment(*this, aSstFragmentPath.replaceFirst("sharedStrings","SharedStrings")));
377 xGlobalSegment->setPosition( 0.75 );
378
379 // read the connections substream
380 OUString aConnFragmentPath = getFragmentPathFromFirstTypeFromOfficeDoc( u"connections" );
381 if( !aConnFragmentPath.isEmpty() )
382 importOoxFragment( new ConnectionsFragment( *this, aConnFragmentPath ) );
383 xGlobalSegment->setPosition( 1.0 );
384
385 /* Create fragments for all sheets, before importing them. Needed to do
386 some preprocessing in the fragment constructors, e.g. loading the table
387 fragments for all sheets that are needed before the cell formulas are
388 loaded. Additionally, the instances of the WorkbookGlobals structures
389 have to be stored for every sheet. */
390 SheetFragmentVector aSheetFragments;
391 std::vector<WorksheetHelper*> aHelpers;
392 WorksheetBuffer& rWorksheets = getWorksheets();
393 sal_Int32 nWorksheetCount = rWorksheets.getWorksheetCount();
394 for( sal_Int32 nWorksheet = 0; nWorksheet < nWorksheetCount; ++nWorksheet )
395 {
396 sal_Int16 nCalcSheet = rWorksheets.getCalcSheetIndex( nWorksheet );
397 const Relation* pRelation = getRelations().getRelationFromRelId( rWorksheets.getWorksheetRelId( nWorksheet ) );
398 if( (nCalcSheet >= 0) && pRelation )
399 {
400 // get fragment path of the sheet
401 OUString aFragmentPath = getFragmentPathFromRelation( *pRelation );
402 OSL_ENSURE( !aFragmentPath.isEmpty(), "WorkbookFragment::finalizeImport - cannot access sheet fragment" );
403 if( !aFragmentPath.isEmpty() )
404 {
405 // leave space for formula processing ( calculate the segments as
406 // if there is an extra sheet )
407 double fSegmentLength = getProgressBar().getFreeLength() / (nWorksheetCount - ( nWorksheet - 1) );
408 ISegmentProgressBarRef xSheetSegment = getProgressBar().createSegment( fSegmentLength );
409
410 // get the sheet type according to the relations type
411 WorksheetType eSheetType = WorksheetType::Empty;
412 if( pRelation->maType == CREATE_OFFICEDOC_RELATION_TYPE( "worksheet" ) ||
413 pRelation->maType == CREATE_OFFICEDOC_RELATION_TYPE_STRICT( "worksheet" ))
414 eSheetType = WorksheetType::Work;
415 else if( pRelation->maType == CREATE_OFFICEDOC_RELATION_TYPE( "chartsheet" ) ||
416 pRelation->maType == CREATE_OFFICEDOC_RELATION_TYPE_STRICT( "chartsheet" ))
417 eSheetType = WorksheetType::Chart;
418 else if( (pRelation->maType == CREATE_MSOFFICE_RELATION_TYPE( "xlMacrosheet" )) ||
419 (pRelation->maType == CREATE_MSOFFICE_RELATION_TYPE( "xlIntlMacrosheet" )) )
420 eSheetType = WorksheetType::Macro;
421 else if( pRelation->maType == CREATE_OFFICEDOC_RELATION_TYPE( "dialogsheet" ) ||
422 pRelation->maType == CREATE_OFFICEDOC_RELATION_TYPE_STRICT(" dialogsheet" ))
423 eSheetType = WorksheetType::Dialog;
424 OSL_ENSURE( eSheetType != WorksheetType::Empty, "WorkbookFragment::finalizeImport - unknown sheet type" );
425 if( eSheetType != WorksheetType::Empty )
426 {
427 // create the WorksheetGlobals object
428 WorksheetGlobalsRef xSheetGlob = WorksheetHelper::constructGlobals( *this, xSheetSegment, eSheetType, nCalcSheet );
429 OSL_ENSURE( xSheetGlob, "WorkbookFragment::finalizeImport - missing sheet in document" );
430 if( xSheetGlob )
431 {
432 // create the sheet fragment handler
433 ::rtl::Reference< WorksheetFragmentBase > xFragment;
434 switch( eSheetType )
435 {
436 case WorksheetType::Work:
437 case WorksheetType::Macro:
438 case WorksheetType::Dialog:
439 xFragment.set( new WorksheetFragment( *xSheetGlob, aFragmentPath ) );
440 break;
441 case WorksheetType::Chart:
442 xFragment.set( new ChartsheetFragment( *xSheetGlob, aFragmentPath ) );
443 break;
444 // coverity[dead_error_begin] - following conditions exist to avoid compiler warning
445 case WorksheetType::Empty:
446 break;
447 }
448
449 // insert the fragment into the map
450 if( xFragment.is() )
451 {
452 aSheetFragments.emplace_back( xSheetGlob, xFragment.get() );
453 aHelpers.push_back(xFragment.get());
454 }
455 }
456 }
457 }
458 }
459 }
460
461 // setup structure sizes for the number of sheets
462 getFormulaBuffer().SetSheetCount( nWorksheetCount );
463
464 // create all database ranges and defined names, in that order
465 getTables().finalizeImport();
466 getTables().applyTableColumns();
467 getDefinedNames().finalizeImport();
468 // open the VBA project storage
469 OUString aVbaFragmentPath = getFragmentPathFromFirstType( CREATE_MSOFFICE_RELATION_TYPE( "vbaProject" ) );
470 if( !aVbaFragmentPath.isEmpty() )
471 {
472 Reference< XInputStream > xInStrm = getBaseFilter().openInputStream( aVbaFragmentPath );
473 if( xInStrm.is() )
474 {
475 StorageRef xPrjStrg = std::make_shared<::oox::ole::OleStorage>( getBaseFilter().getComponentContext(), xInStrm, false );
476 setVbaProjectStorage( xPrjStrg );
477 getBaseFilter().getVbaProject().readVbaModules( *xPrjStrg );
478 }
479 }
480
481 // lock the model to prevent broadcasting, speeds up load a lot
482 getScDocument().InitDrawLayer();
483 auto pModel = getScDocument().GetDrawLayer();
484 bool bWasLocked = pModel->isLocked();
485 pModel->setLock(true);
486
487 // load all worksheets
488 importSheetFragments(*this, aSheetFragments);
489
490 if (pTheme && !pTheme->GetName().isEmpty())
491 {
492 pModel->setTheme(pTheme);
493 }
494
495 // assumes getTables().finalizeImport ( which creates the DatabaseRanges )
496 // has been called already
497 getTables().applyAutoFilters();
498
499 sal_Int16 nActiveSheet = getViewSettings().getActiveCalcSheet();
500 getWorksheets().finalizeImport( nActiveSheet );
501
502 // final conversions, e.g. calculation settings and view settings
503 finalizeWorkbookImport();
504
505 //stop preventing establishment of listeners as is done in
506 //ScDocShell::AfterXMLLoading() for ods
507 getScDocument().SetInsertingFromOtherDoc(false);
508
509 for( WorksheetHelper* pHelper : aHelpers )
510 {
511 pHelper->finalizeDrawingImport();
512 }
513
514 for( auto& [rxSheetGlob, rxFragment] : aSheetFragments )
515 {
516 // delete fragment object and WorkbookGlobals object, will free all allocated sheet buffers
517 rxFragment.clear();
518 rxSheetGlob.reset();
519 }
520
521
522 getDocImport().finalize();
523
524 recalcFormulaCells();
525
526 OUString aRevHeadersPath = getFragmentPathFromFirstType(CREATE_OFFICEDOC_RELATION_TYPE("revisionHeaders"));
527 if (!aRevHeadersPath.isEmpty())
528 {
529 std::unique_ptr<oox::core::FastParser> xParser(oox::core::XmlFilterBase::createParser());
530 rtl::Reference<oox::core::FragmentHandler> xFragment(new RevisionHeadersFragment(*this, aRevHeadersPath));
531 importOoxFragment(xFragment, *xParser);
532 }
533
534 // attach macros to registered objects now that all objects have been created.
535 getBaseFilter().getVbaProject().attachMacros();
536
537 pModel->setLock(bWasLocked);
538 }
539
540 namespace {
541
getDocShell(const ScDocument & rDoc)542 ScDocShell& getDocShell(const ScDocument& rDoc)
543 {
544 return static_cast<ScDocShell&>(*rDoc.GetDocumentShell());
545 }
546
547 class MessageWithCheck : public weld::MessageDialogController
548 {
549 private:
550 std::unique_ptr<weld::CheckButton> m_xWarningOnBox;
551 public:
MessageWithCheck(weld::Window * pParent,const OUString & rUIFile,const OUString & rDialogId)552 MessageWithCheck(weld::Window *pParent, const OUString& rUIFile, const OUString& rDialogId)
553 : MessageDialogController(pParent, rUIFile, rDialogId, u"ask"_ustr)
554 , m_xWarningOnBox(m_xBuilder->weld_check_button(u"ask"_ustr))
555 {
556 }
get_active() const557 bool get_active() const { return m_xWarningOnBox->get_active(); }
hide_ask() const558 void hide_ask() const { m_xWarningOnBox->set_visible(false); };
559 };
560
561 }
562
recalcFormulaCells()563 void WorkbookFragment::recalcFormulaCells()
564 {
565 // Recalculate formula cells.
566 ScDocument& rDoc = getScDocument();
567 ScDocShell& rDocSh = getDocShell(rDoc);
568 ScRecalcOptions nRecalcMode =
569 static_cast<ScRecalcOptions>(officecfg::Office::Calc::Formula::Load::OOXMLRecalcMode::get());
570 bool bHardRecalc = false;
571 if (nRecalcMode == RECALC_ASK)
572 {
573 if (rDoc.IsUserInteractionEnabled())
574 {
575 // Ask the user if full re-calculation is desired.
576 MessageWithCheck aQueryBox(ScDocShell::GetActiveDialogParent(), u"modules/scalc/ui/recalcquerydialog.ui"_ustr, u"RecalcQueryDialog"_ustr);
577 aQueryBox.set_primary_text(ScResId(STR_QUERY_FORMULA_RECALC_ONLOAD_XLS));
578 aQueryBox.set_default_response(RET_YES);
579
580 if ( officecfg::Office::Calc::Formula::Load::OOXMLRecalcMode::isReadOnly() )
581 aQueryBox.hide_ask();
582
583 bHardRecalc = aQueryBox.run() == RET_YES;
584
585 if (aQueryBox.get_active())
586 {
587 // Always perform selected action in the future.
588 std::shared_ptr<comphelper::ConfigurationChanges> batch(comphelper::ConfigurationChanges::create());
589 officecfg::Office::Calc::Formula::Load::OOXMLRecalcMode::set(sal_Int32(0), batch);
590 ScFormulaOptions aOpt = SC_MOD()->GetFormulaOptions();
591 aOpt.SetOOXMLRecalcOptions(bHardRecalc ? RECALC_ALWAYS : RECALC_NEVER);
592 /* XXX is this really supposed to set the ScModule options?
593 * Not the ScDocShell options? */
594 SC_MOD()->SetFormulaOptions(aOpt);
595
596 batch->commit();
597 }
598 }
599 }
600 else if (nRecalcMode == RECALC_ALWAYS)
601 bHardRecalc = true;
602 else if (!hasCalculatedFormulaCells())
603 {
604 // Did not encounter any other formula result than 0.0 (or none / no
605 // formula cells at all, in which case recalculation is almost a no-op)
606 // in a non-known-good-generator document.
607 bHardRecalc = true;
608 }
609
610 if (bHardRecalc)
611 rDocSh.DoHardRecalc();
612 else
613 {
614 getDocImport().broadcastRecalcAfterImport();
615 // Full ScDocument::CalcFormulaTree() of all dirty cells is not
616 // necessary here, the View will recalculate them in the visible area,
617 // or any other method accessing formula cell results.
618 }
619 }
620
621 // private --------------------------------------------------------------------
622
importExternalReference(const AttributeList & rAttribs)623 void WorkbookFragment::importExternalReference( const AttributeList& rAttribs )
624 {
625 if( ExternalLink* pExtLink = getExternalLinks().importExternalReference( rAttribs ).get() )
626 importExternalLinkFragment( *pExtLink );
627 }
628
importDefinedName(const AttributeList & rAttribs)629 void WorkbookFragment::importDefinedName( const AttributeList& rAttribs )
630 {
631 mxCurrName = getDefinedNames().importDefinedName( rAttribs );
632 }
633
importPivotCache(const AttributeList & rAttribs)634 void WorkbookFragment::importPivotCache( const AttributeList& rAttribs )
635 {
636 sal_Int32 nCacheId = rAttribs.getInteger( XML_cacheId, -1 );
637 OUString aRelId = rAttribs.getString( R_TOKEN( id ), OUString() );
638 importPivotCacheDefFragment( aRelId, nCacheId );
639 }
640
importExternalRef(SequenceInputStream & rStrm)641 void WorkbookFragment::importExternalRef( SequenceInputStream& rStrm )
642 {
643 if( ExternalLink* pExtLink = getExternalLinks().importExternalRef( rStrm ).get() )
644 importExternalLinkFragment( *pExtLink );
645 }
646
importPivotCache(SequenceInputStream & rStrm)647 void WorkbookFragment::importPivotCache( SequenceInputStream& rStrm )
648 {
649 sal_Int32 nCacheId = rStrm.readInt32();
650 OUString aRelId = BiffHelper::readString( rStrm );
651 importPivotCacheDefFragment( aRelId, nCacheId );
652 }
653
importExternalLinkFragment(ExternalLink & rExtLink)654 void WorkbookFragment::importExternalLinkFragment( ExternalLink& rExtLink )
655 {
656 OUString aFragmentPath = getFragmentPathFromRelId( rExtLink.getRelId() );
657 if( !aFragmentPath.isEmpty() )
658 importOoxFragment( new ExternalLinkFragment( *this, aFragmentPath, rExtLink ) );
659 }
660
importPivotCacheDefFragment(const OUString & rRelId,sal_Int32 nCacheId)661 void WorkbookFragment::importPivotCacheDefFragment( const OUString& rRelId, sal_Int32 nCacheId )
662 {
663 // pivot caches will be imported on demand, here we just store the fragment path in the buffer
664 getPivotCaches().registerPivotCacheFragment( nCacheId, getFragmentPathFromRelId( rRelId ) );
665 }
666
667 } // namespace oox
668
669 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
670