xref: /core/sc/source/filter/oox/workbookfragment.cxx (revision 000aaf1f)
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