xref: /core/sc/source/ui/docshell/docsh.cxx (revision ab9b67bb)
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 <docsh.hxx>
21 
22 #include <scitems.hxx>
23 #include <sc.hrc>
24 #include <vcl/errinf.hxx>
25 #include <editeng/justifyitem.hxx>
26 #include <comphelper/fileformat.h>
27 #include <comphelper/classids.hxx>
28 #include <formula/errorcodes.hxx>
29 #include <vcl/weld.hxx>
30 #include <vcl/virdev.hxx>
31 #include <vcl/waitobj.hxx>
32 #include <rtl/bootstrap.hxx>
33 #include <rtl/tencinfo.h>
34 #include <sal/log.hxx>
35 #include <svl/PasswordHelper.hxx>
36 #include <sfx2/app.hxx>
37 #include <sfx2/bindings.hxx>
38 #include <sfx2/dinfdlg.hxx>
39 #include <sfx2/docfile.hxx>
40 #include <sfx2/event.hxx>
41 #include <sfx2/fcontnr.hxx>
42 #include <sfx2/objface.hxx>
43 #include <sfx2/viewfrm.hxx>
44 #include <svl/documentlockfile.hxx>
45 #include <svl/sharecontrolfile.hxx>
46 #include <svl/urihelper.hxx>
47 #include <osl/file.hxx>
48 #include <chgtrack.hxx>
49 #include <chgviset.hxx>
50 #include <com/sun/star/awt/Key.hpp>
51 #include <com/sun/star/awt/KeyModifier.hpp>
52 #include <com/sun/star/container/XContentEnumerationAccess.hpp>
53 #include <com/sun/star/document/UpdateDocMode.hpp>
54 #include <com/sun/star/script/vba/VBAEventId.hpp>
55 #include <com/sun/star/script/vba/VBAScriptEventId.hpp>
56 #include <com/sun/star/script/vba/XVBAEventProcessor.hpp>
57 #include <com/sun/star/script/vba/XVBAScriptListener.hpp>
58 #include <com/sun/star/script/vba/XVBACompatibility.hpp>
59 #include <com/sun/star/sheet/XSpreadsheetView.hpp>
60 #include <com/sun/star/task/XJob.hpp>
61 #include <com/sun/star/ui/theModuleUIConfigurationManagerSupplier.hpp>
62 #include <com/sun/star/ui/XAcceleratorConfiguration.hpp>
63 #include <com/sun/star/util/VetoException.hpp>
64 #include <com/sun/star/sheet/XSpreadsheetDocument.hpp>
65 #include <com/sun/star/sheet/XSpreadsheet.hpp>
66 #include <com/sun/star/container/XIndexAccess.hpp>
67 #include <com/sun/star/table/XTableChartsSupplier.hpp>
68 #include <com/sun/star/table/XTableCharts.hpp>
69 #include <com/sun/star/table/XTableChart.hpp>
70 #include <com/sun/star/chart2/XChartDocument.hpp>
71 #include <com/sun/star/document/XEmbeddedObjectSupplier.hpp>
72 #include <com/sun/star/frame/XStorable2.hpp>
73 #include <com/sun/star/frame/Desktop.hpp>
74 #include <com/sun/star/lang/XSingleComponentFactory.hpp>
75 #include <ooo/vba/excel/XWorkbook.hpp>
76 
77 #include <config_folders.h>
78 
79 #include <scabstdlg.hxx>
80 #include <sot/formats.hxx>
81 #include <svx/dialogs.hrc>
82 
83 #include <formulacell.hxx>
84 #include <postit.hxx>
85 #include <global.hxx>
86 #include <filter.hxx>
87 #include <scmod.hxx>
88 #include <tabvwsh.hxx>
89 #include <docfunc.hxx>
90 #include <imoptdlg.hxx>
91 #include <impex.hxx>
92 #include <scresid.hxx>
93 #include <strings.hrc>
94 #include <globstr.hrc>
95 #include <scerrors.hxx>
96 #include <brdcst.hxx>
97 #include <stlpool.hxx>
98 #include <autostyl.hxx>
99 #include <attrib.hxx>
100 #include <asciiopt.hxx>
101 #include <waitoff.hxx>
102 #include <docpool.hxx>
103 #include <progress.hxx>
104 #include <pntlock.hxx>
105 #include <docuno.hxx>
106 #include <appoptio.hxx>
107 #include <formulaopt.hxx>
108 #include <scdll.hxx>
109 #include <detdata.hxx>
110 #include <printfun.hxx>
111 #include <dociter.hxx>
112 #include <cellform.hxx>
113 #include <chartlis.hxx>
114 #include <hints.hxx>
115 #include <xmlwrap.hxx>
116 #include <drwlayer.hxx>
117 #include <refreshtimer.hxx>
118 #include <dbdata.hxx>
119 #include <scextopt.hxx>
120 #include <compiler.hxx>
121 #include <warnpassword.hxx>
122 #include <optsolver.hxx>
123 #include <sheetdata.hxx>
124 #include <tabprotection.hxx>
125 #include <transobj.hxx>
126 #include <docparam.hxx>
127 #include "docshimp.hxx"
128 #include <sizedev.hxx>
129 #include <refreshtimerprotector.hxx>
130 #include <orcus/orcus_import_ods.hpp>
131 #include <orcusfiltersimpl.hxx>
132 
133 #include <officecfg/Office/Calc.hxx>
134 #include <comphelper/processfactory.hxx>
135 #include <comphelper/string.hxx>
136 #include <unotools/configmgr.hxx>
137 #include <uiitems.hxx>
138 #include <cellsuno.hxx>
139 #include <dpobject.hxx>
140 #include <markdata.hxx>
141 #include <optuno.hxx>
142 #include <orcusfilters.hxx>
143 #include <datastream.hxx>
144 #include <documentlinkmgr.hxx>
145 #include <refupdatecontext.hxx>
146 #include <o3tl/make_unique.hxx>
147 
148 #include <memory>
149 #include <vector>
150 
151 using namespace com::sun::star;
152 using ::com::sun::star::uno::Reference;
153 using ::com::sun::star::lang::XMultiServiceFactory;
154 using std::shared_ptr;
155 using ::std::vector;
156 
157 //  Filter names (like in sclib.cxx)
158 
159 static const sal_Char pFilterSc50[]     = "StarCalc 5.0";
160 static const sal_Char pFilterXML[]      = "StarOffice XML (Calc)";
161 static const sal_Char pFilterAscii[]    = SC_TEXT_CSV_FILTER_NAME;
162 static const sal_Char pFilterLotus[]    = "Lotus";
163 static const sal_Char pFilterQPro6[]    = "Quattro Pro 6.0";
164 static const sal_Char pFilterExcel4[]   = "MS Excel 4.0";
165 static const sal_Char pFilterEx4Temp[]  = "MS Excel 4.0 Vorlage/Template";
166 static const sal_Char pFilterExcel5[]   = "MS Excel 5.0/95";
167 static const sal_Char pFilterEx5Temp[]  = "MS Excel 5.0/95 Vorlage/Template";
168 static const sal_Char pFilterExcel95[]  = "MS Excel 95";
169 static const sal_Char pFilterEx95Temp[] = "MS Excel 95 Vorlage/Template";
170 static const sal_Char pFilterExcel97[]  = "MS Excel 97";
171 static const sal_Char pFilterEx97Temp[] = "MS Excel 97 Vorlage/Template";
172 static const sal_Char pFilterDBase[]    = "dBase";
173 static const sal_Char pFilterDif[]      = "DIF";
174 static const sal_Char pFilterSylk[]     = "SYLK";
175 static const sal_Char pFilterHtml[]     = "HTML (StarCalc)";
176 static const sal_Char pFilterHtmlWebQ[] = "calc_HTML_WebQuery";
177 static const sal_Char pFilterRtf[]      = "Rich Text Format (StarCalc)";
178 
179 #define ShellClass_ScDocShell
180 #include <scslots.hxx>
181 
182 SFX_IMPL_INTERFACE(ScDocShell,SfxObjectShell)
183 
184 void ScDocShell::InitInterface_Impl()
185 {
186 }
187 
188 //  GlobalName of the current version:
189 SFX_IMPL_OBJECTFACTORY( ScDocShell, SvGlobalName(SO3_SC_CLASSID), "scalc" )
190 
191 
192 void ScDocShell::FillClass( SvGlobalName* pClassName,
193                                         SotClipboardFormatId* pFormat,
194                                         OUString* /* pAppName */,
195                                         OUString* pFullTypeName,
196                                         OUString* pShortTypeName,
197                                         sal_Int32 nFileFormat,
198                                         bool bTemplate /* = false */) const
199 {
200     if ( nFileFormat == SOFFICE_FILEFORMAT_60 )
201     {
202         *pClassName     = SvGlobalName( SO3_SC_CLASSID_60 );
203         *pFormat        = SotClipboardFormatId::STARCALC_60;
204         *pFullTypeName  = ScResId( SCSTR_LONG_SCDOC_NAME_60 );
205         *pShortTypeName = ScResId( SCSTR_SHORT_SCDOC_NAME );
206     }
207     else if ( nFileFormat == SOFFICE_FILEFORMAT_8 )
208     {
209         *pClassName     = SvGlobalName( SO3_SC_CLASSID_60 );
210         *pFormat        = bTemplate ? SotClipboardFormatId::STARCALC_8_TEMPLATE : SotClipboardFormatId::STARCALC_8;
211         *pFullTypeName  = ScResId( SCSTR_LONG_SCDOC_NAME_80 );
212         *pShortTypeName = ScResId(SCSTR_SHORT_SCDOC_NAME);
213     }
214     else
215     {
216         OSL_FAIL("Which version?");
217     }
218 }
219 
220 std::set<Color> ScDocShell::GetDocColors()
221 {
222     return m_aDocument.GetDocColors();
223 }
224 
225 void ScDocShell::DoEnterHandler()
226 {
227     ScTabViewShell* pViewSh = ScTabViewShell::GetActiveViewShell();
228     if (pViewSh && pViewSh->GetViewData().GetDocShell() == this)
229         SC_MOD()->InputEnterHandler();
230 }
231 
232 SCTAB ScDocShell::GetSaveTab()
233 {
234     SCTAB nTab = 0;
235     ScTabViewShell* pSh = GetBestViewShell();
236     if (pSh)
237     {
238         const ScMarkData& rMark = pSh->GetViewData().GetMarkData();
239         nTab = rMark.GetFirstSelected();
240     }
241     return nTab;
242 }
243 
244 HiddenInformation ScDocShell::GetHiddenInformationState( HiddenInformation nStates )
245 {
246     // get global state like HiddenInformation::DOCUMENTVERSIONS
247     HiddenInformation nState = SfxObjectShell::GetHiddenInformationState( nStates );
248 
249     if ( nStates & HiddenInformation::RECORDEDCHANGES )
250     {
251         if ( m_aDocument.GetChangeTrack() && m_aDocument.GetChangeTrack()->GetFirst() )
252           nState |= HiddenInformation::RECORDEDCHANGES;
253     }
254     if ( nStates & HiddenInformation::NOTES )
255     {
256         SCTAB nTableCount = m_aDocument.GetTableCount();
257         bool bFound = false;
258         for (SCTAB nTab = 0; nTab < nTableCount && !bFound; ++nTab)
259         {
260             if (m_aDocument.HasTabNotes(nTab)) //TODO:
261                 bFound = true;
262         }
263 
264         if (bFound)
265             nState |= HiddenInformation::NOTES;
266     }
267 
268     return nState;
269 }
270 
271 void ScDocShell::BeforeXMLLoading()
272 {
273     m_aDocument.EnableIdle(false);
274 
275     // prevent unnecessary broadcasts and updates
276     OSL_ENSURE(m_pModificator == nullptr, "The Modificator should not exist");
277     m_pModificator.reset( new ScDocShellModificator( *this ) );
278 
279     m_aDocument.SetImportingXML( true );
280     m_aDocument.EnableExecuteLink( false );   // #i101304# to be safe, prevent nested loading from external references
281     m_aDocument.EnableUndo( false );
282     // prevent unnecessary broadcasts and "half way listeners"
283     m_aDocument.SetInsertingFromOtherDoc( true );
284 }
285 
286 void ScDocShell::AfterXMLLoading(bool bRet)
287 {
288     if (GetCreateMode() != SfxObjectCreateMode::ORGANIZER)
289     {
290         UpdateLinks();
291         // don't prevent establishing of listeners anymore
292         m_aDocument.SetInsertingFromOtherDoc( false );
293         if ( bRet )
294         {
295             ScChartListenerCollection* pChartListener = m_aDocument.GetChartListenerCollection();
296             if (pChartListener)
297                 pChartListener->UpdateDirtyCharts();
298 
299             // #95582#; set the table names of linked tables to the new path
300             SCTAB nTabCount = m_aDocument.GetTableCount();
301             for (SCTAB i = 0; i < nTabCount; ++i)
302             {
303                 if (m_aDocument.IsLinked( i ))
304                 {
305                     OUString aName;
306                     m_aDocument.GetName(i, aName);
307                     OUString aLinkTabName = m_aDocument.GetLinkTab(i);
308                     sal_Int32 nLinkTabNameLength = aLinkTabName.getLength();
309                     sal_Int32 nNameLength = aName.getLength();
310                     if (nLinkTabNameLength < nNameLength)
311                     {
312 
313                         // remove the quotes on begin and end of the docname and restore the escaped quotes
314                         const sal_Unicode* pNameBuffer = aName.getStr();
315                         if ( *pNameBuffer == '\'' && // all docnames have to have a ' character on the first pos
316                             ScGlobal::UnicodeStrChr( pNameBuffer, SC_COMPILER_FILE_TAB_SEP ) )
317                         {
318                             OUStringBuffer aDocURLBuffer;
319                             bool bQuote = true; // Document name is always quoted
320                             ++pNameBuffer;
321                             while ( bQuote && *pNameBuffer )
322                             {
323                                 if ( *pNameBuffer == '\'' && *(pNameBuffer-1) != '\\' )
324                                     bQuote = false;
325                                 else if( !(*pNameBuffer == '\\' && *(pNameBuffer+1) == '\'') )
326                                     aDocURLBuffer.append(*pNameBuffer); // If escaped quote: only quote in the name
327                                 ++pNameBuffer;
328                             }
329 
330                             if( *pNameBuffer == SC_COMPILER_FILE_TAB_SEP )  // after the last quote of the docname should be the # char
331                             {
332                                 sal_Int32 nIndex = nNameLength - nLinkTabNameLength;
333                                 INetURLObject aINetURLObject(aDocURLBuffer.makeStringAndClear());
334                                 if(aName.match( aLinkTabName, nIndex) &&
335                                     (aName[nIndex - 1] == '#') && // before the table name should be the # char
336                                     !aINetURLObject.HasError()) // the docname should be a valid URL
337                                 {
338                                     aName = ScGlobal::GetDocTabName( m_aDocument.GetLinkDoc( i ), m_aDocument.GetLinkTab( i ) );
339                                     m_aDocument.RenameTab(i, aName, true/*bExternalDocument*/);
340                                 }
341                                 // else;  nothing has to happen, because it is a user given name
342                             }
343                             // else;  nothing has to happen, because it is a user given name
344                         }
345                         // else;  nothing has to happen, because it is a user given name
346                     }
347                     // else;  nothing has to happen, because it is a user given name
348                 }
349             }
350 
351             // #i94570# DataPilot table names have to be unique, or the tables can't be accessed by API.
352             // If no name (or an invalid name, skipped in ScXMLDataPilotTableContext::EndElement) was set, create a new name.
353             ScDPCollection* pDPCollection = m_aDocument.GetDPCollection();
354             if ( pDPCollection )
355             {
356                 size_t nDPCount = pDPCollection->GetCount();
357                 for (size_t nDP=0; nDP<nDPCount; ++nDP)
358                 {
359                     ScDPObject& rDPObj = (*pDPCollection)[nDP];
360                     if (rDPObj.GetName().isEmpty())
361                         rDPObj.SetName( pDPCollection->CreateNewName() );
362                 }
363             }
364         }
365     }
366     else
367         m_aDocument.SetInsertingFromOtherDoc( false );
368 
369     m_aDocument.SetImportingXML( false );
370     m_aDocument.EnableExecuteLink( true );
371     m_aDocument.EnableUndo( true );
372     m_bIsEmpty = false;
373 
374     if (m_pModificator)
375     {
376         ScDocument::HardRecalcState eRecalcState = m_aDocument.GetHardRecalcState();
377         // Temporarily set hard-recalc to prevent calling
378         // ScFormulaCell::Notify() during destruction of the Modificator which
379         // will set the cells dirty.
380         if (eRecalcState == ScDocument::HardRecalcState::OFF)
381             m_aDocument.SetHardRecalcState(ScDocument::HardRecalcState::TEMPORARY);
382         m_pModificator.reset();
383         m_aDocument.SetHardRecalcState(eRecalcState);
384     }
385     else
386     {
387         OSL_FAIL("The Modificator should exist");
388     }
389 
390     m_aDocument.EnableIdle(true);
391 }
392 
393 namespace {
394 
395 class LoadMediumGuard
396 {
397 public:
398     explicit LoadMediumGuard(ScDocument* pDoc) :
399         mpDoc(pDoc)
400     {
401         mpDoc->SetLoadingMedium(true);
402     }
403 
404     ~LoadMediumGuard()
405     {
406         mpDoc->SetLoadingMedium(false);
407     }
408 private:
409     ScDocument* mpDoc;
410 };
411 
412 void processDataStream( ScDocShell& rShell, const sc::ImportPostProcessData& rData )
413 {
414     if (!rData.mpDataStream)
415         return;
416 
417     const sc::ImportPostProcessData::DataStream& r = *rData.mpDataStream;
418     if (!r.maRange.IsValid())
419         return;
420 
421     // Break the streamed range into the top range and the height limit.  A
422     // height limit of 0 means unlimited i.e. the streamed data will go all
423     // the way to the last row.
424 
425     ScRange aTopRange = r.maRange;
426     aTopRange.aEnd.SetRow(aTopRange.aStart.Row());
427     sal_Int32 nLimit = r.maRange.aEnd.Row() - r.maRange.aStart.Row() + 1;
428     if (r.maRange.aEnd.Row() == MAXROW)
429         // Unlimited range.
430         nLimit = 0;
431 
432     sc::DataStream::MoveType eMove =
433         r.meInsertPos == sc::ImportPostProcessData::DataStream::InsertTop ?
434         sc::DataStream::MOVE_DOWN : sc::DataStream::RANGE_DOWN;
435 
436     sc::DataStream* pStrm = new sc::DataStream(&rShell, r.maURL, aTopRange, nLimit, eMove, 0);
437     pStrm->SetRefreshOnEmptyLine(r.mbRefreshOnEmpty);
438     sc::DocumentLinkManager& rMgr = rShell.GetDocument().GetDocLinkManager();
439     rMgr.setDataStream(pStrm);
440 }
441 
442 class MessageWithCheck : public weld::MessageDialogController
443 {
444 private:
445     std::unique_ptr<weld::CheckButton> m_xWarningOnBox;
446 public:
447     MessageWithCheck(weld::Window *pParent, const OUString& rUIFile, const OString& rDialogId)
448         : MessageDialogController(pParent, rUIFile, rDialogId, "ask")
449         , m_xWarningOnBox(m_xBuilder->weld_check_button("ask"))
450     {
451     }
452     bool get_active() const { return m_xWarningOnBox->get_active(); }
453 };
454 
455 
456 class VBAScriptListener : public ::cppu::WeakImplHelper< css::script::vba::XVBAScriptListener >
457 {
458 private:
459     ScDocShell* m_pDocSh;
460 public:
461     VBAScriptListener(ScDocShell* pDocSh) : m_pDocSh(pDocSh)
462     {
463     }
464 
465     // XVBAScriptListener
466     virtual void SAL_CALL notifyVBAScriptEvent( const ::css::script::vba::VBAScriptEvent& aEvent ) override
467     {
468         if (aEvent.Identifier == script::vba::VBAScriptEventId::SCRIPT_STOPPED &&
469             m_pDocSh->GetClipData().is())
470         {
471             m_pDocSh->SetClipData(uno::Reference<datatransfer::XTransferable2>());
472         }
473     }
474 
475     // XEventListener
476     virtual void SAL_CALL disposing( const ::css::lang::EventObject& /*Source*/ ) override
477     {
478     }
479 };
480 
481 }
482 
483 bool ScDocShell::LoadXML( SfxMedium* pLoadMedium, const css::uno::Reference< css::embed::XStorage >& xStor )
484 {
485     LoadMediumGuard aLoadGuard(&m_aDocument);
486 
487     //  MacroCallMode is no longer needed, state is kept in SfxObjectShell now
488 
489     // no Seek(0) here - always loading from storage, GetInStream must not be called
490 
491     BeforeXMLLoading();
492 
493     ScXMLImportWrapper aImport(*this, pLoadMedium, xStor);
494 
495     bool bRet = false;
496     ErrCode nError = ERRCODE_NONE;
497     m_aDocument.LockAdjustHeight();
498     if (GetCreateMode() == SfxObjectCreateMode::ORGANIZER)
499         bRet = aImport.Import(ImportFlags::Styles, nError);
500     else
501         bRet = aImport.Import(ImportFlags::All, nError);
502 
503     if ( nError )
504         pLoadMedium->SetError(nError);
505 
506     processDataStream(*this, aImport.GetImportPostProcessData());
507 
508     //if the document was not generated by LibreOffice, do hard recalc in case some other document
509     //generator saved cached formula results that differ from LibreOffice's calculated results or
510     //did not use cached formula results.
511     uno::Reference<document::XDocumentPropertiesSupplier> xDPS(GetModel(), uno::UNO_QUERY_THROW);
512     uno::Reference<document::XDocumentProperties> xDocProps = xDPS->getDocumentProperties();
513 
514     Reference<uno::XComponentContext> xContext = comphelper::getProcessComponentContext();
515     ScRecalcOptions nRecalcMode =
516         static_cast<ScRecalcOptions>(officecfg::Office::Calc::Formula::Load::ODFRecalcMode::get(xContext));
517 
518     bool bHardRecalc = false;
519     if (nRecalcMode == RECALC_ASK)
520     {
521         OUString sProductName(utl::ConfigManager::getProductName());
522         if (m_aDocument.IsUserInteractionEnabled() && xDocProps->getGenerator().indexOf(sProductName) == -1)
523         {
524             // Generator is not LibreOffice.  Ask if the user wants to perform
525             // full re-calculation.
526             vcl::Window* pWin = GetActiveDialogParent();
527 
528             MessageWithCheck aQueryBox(pWin ? pWin->GetFrameWeld() : nullptr,
529                     "modules/scalc/ui/recalcquerydialog.ui", "RecalcQueryDialog");
530             aQueryBox.set_primary_text(ScResId(STR_QUERY_FORMULA_RECALC_ONLOAD_ODS));
531             aQueryBox.set_default_response(RET_YES);
532 
533             bHardRecalc = aQueryBox.run() == RET_YES;
534 
535             if (aQueryBox.get_active())
536             {
537                 // Always perform selected action in the future.
538                 std::shared_ptr<comphelper::ConfigurationChanges> batch(comphelper::ConfigurationChanges::create());
539                 officecfg::Office::Calc::Formula::Load::ODFRecalcMode::set(sal_Int32(0), batch);
540                 ScFormulaOptions aOpt = SC_MOD()->GetFormulaOptions();
541                 aOpt.SetODFRecalcOptions(bHardRecalc ? RECALC_ALWAYS : RECALC_NEVER);
542                 /* XXX  is this really supposed to set the ScModule options?
543                  *      Not the ScDocShell options? */
544                 SC_MOD()->SetFormulaOptions(aOpt);
545 
546                 batch->commit();
547             }
548         }
549     }
550     else if (nRecalcMode == RECALC_ALWAYS)
551         bHardRecalc = true;
552 
553     if (bHardRecalc)
554         DoHardRecalc();
555     else
556     {
557         // still need to recalc volatile formula cells.
558         m_aDocument.Broadcast(ScHint(SfxHintId::ScDataChanged, BCA_BRDCST_ALWAYS));
559     }
560 
561     AfterXMLLoading(bRet);
562 
563     m_aDocument.UnlockAdjustHeight();
564     return bRet;
565 }
566 
567 bool ScDocShell::SaveXML( SfxMedium* pSaveMedium, const css::uno::Reference< css::embed::XStorage >& xStor )
568 {
569     m_aDocument.EnableIdle(false);
570 
571     ScXMLImportWrapper aImport(*this, pSaveMedium, xStor);
572     bool bRet(false);
573     if (GetCreateMode() != SfxObjectCreateMode::ORGANIZER)
574         bRet = aImport.Export(false);
575     else
576         bRet = aImport.Export(true);
577 
578     m_aDocument.EnableIdle(true);
579 
580     return bRet;
581 }
582 
583 bool ScDocShell::Load( SfxMedium& rMedium )
584 {
585     LoadMediumGuard aLoadGuard(&m_aDocument);
586     ScRefreshTimerProtector aProt( m_aDocument.GetRefreshTimerControlAddress() );
587 
588     //  only the latin script language is loaded
589     //  -> initialize the others from options (before loading)
590     InitOptions(true);
591 
592     // If this is an ODF file being loaded, then by default, use legacy processing
593     // for tdf#99729 (if required, it will be overridden in *::ReadUserDataSequence())
594     if (IsOwnStorageFormat(rMedium))
595     {
596         if (m_aDocument.GetDrawLayer())
597             m_aDocument.GetDrawLayer()->SetAnchoredTextOverflowLegacy(true);
598     }
599 
600     GetUndoManager()->Clear();
601 
602     bool bRet = SfxObjectShell::Load(rMedium);
603     if (bRet)
604     {
605         comphelper::EmbeddedObjectContainer& rEmbeddedObjectContainer = getEmbeddedObjectContainer();
606         rEmbeddedObjectContainer.setUserAllowsLinkUpdate(false);
607 
608         if (GetMedium())
609         {
610             const SfxUInt16Item* pUpdateDocItem = SfxItemSet::GetItem<SfxUInt16Item>(rMedium.GetItemSet(), SID_UPDATEDOCMODE, false);
611             m_nCanUpdate = pUpdateDocItem ? pUpdateDocItem->GetValue() : css::document::UpdateDocMode::NO_UPDATE;
612         }
613 
614         {
615             //  prepare a valid document for XML filter
616             //  (for ConvertFrom, InitNew is called before)
617             m_aDocument.MakeTable(0);
618             m_aDocument.GetStyleSheetPool()->CreateStandardStyles();
619             m_aDocument.UpdStlShtPtrsFrmNms();
620 
621             if (!m_bUcalcTest)
622             {
623                 /* Create styles that are imported through Orcus */
624 
625                 OUString aURL("$BRAND_BASE_DIR" LIBO_SHARE_FOLDER "/calc/styles.xml");
626                 rtl::Bootstrap::expandMacros(aURL);
627 
628                 OUString aPath;
629                 osl::FileBase::getSystemPathFromFileURL(aURL, aPath);
630 
631                 ScOrcusFilters* pOrcus = ScFormatFilter::Get().GetOrcusFilters();
632 
633                 if (pOrcus)
634                 {
635                     pOrcus->importODS_Styles(m_aDocument, aPath);
636                     m_aDocument.GetStyleSheetPool()->setAllStandard();
637                 }
638             }
639 
640             bRet = LoadXML( &rMedium, nullptr );
641         }
642     }
643 
644     if (!bRet && !rMedium.GetError())
645         rMedium.SetError(SVSTREAM_FILEFORMAT_ERROR);
646 
647     if (rMedium.GetError())
648         SetError(rMedium.GetError());
649 
650     InitItems();
651     CalcOutputFactor();
652 
653     // invalidate eventually temporary table areas
654     if ( bRet )
655         m_aDocument.InvalidateTableArea();
656 
657     m_bIsEmpty = false;
658     FinishedLoading();
659     return bRet;
660 }
661 
662 void ScDocShell::Notify( SfxBroadcaster&, const SfxHint& rHint )
663 {
664     const ScTablesHint* pScHint = dynamic_cast< const ScTablesHint* >( &rHint );
665     if (pScHint)
666     {
667         if (pScHint->GetTablesHintId() == SC_TAB_INSERTED)
668         {
669             uno::Reference< script::vba::XVBAEventProcessor > xVbaEvents = m_aDocument.GetVbaEventProcessor();
670             if ( xVbaEvents.is() ) try
671             {
672                 uno::Sequence< uno::Any > aArgs( 1 );
673                 aArgs[0] <<= pScHint->GetTab1();
674                 xVbaEvents->processVbaEvent( script::vba::VBAEventId::WORKBOOK_NEWSHEET, aArgs );
675             }
676             catch( uno::Exception& )
677             {
678             }
679         }
680     }
681 
682     if ( dynamic_cast<const SfxStyleSheetHint*>(&rHint) ) // Template changed
683         NotifyStyle( static_cast<const SfxStyleSheetHint&>(rHint) );
684     else if ( dynamic_cast<const ScAutoStyleHint*>(&rHint) )
685     {
686         //! direct call for AutoStyles
687 
688         //  this is called synchronously from ScInterpreter::ScStyle,
689         //  modifying the document must be asynchronous
690         //  (handled by AddInitial)
691 
692         const ScAutoStyleHint& rStlHint = static_cast<const ScAutoStyleHint&>(rHint);
693         const ScRange& aRange = rStlHint.GetRange();
694         const OUString& aName1 = rStlHint.GetStyle1();
695         const OUString& aName2 = rStlHint.GetStyle2();
696         sal_uInt32 nTimeout = rStlHint.GetTimeout();
697 
698         if (!m_pAutoStyleList)
699             m_pAutoStyleList.reset( new ScAutoStyleList(this) );
700         m_pAutoStyleList->AddInitial( aRange, aName1, nTimeout, aName2 );
701     }
702     else if ( dynamic_cast<const SfxEventHint*>(&rHint) )
703     {
704         SfxEventHintId nEventId = static_cast<const SfxEventHint*>(&rHint)->GetEventId();
705 
706         switch ( nEventId )
707         {
708             case SfxEventHintId::LoadFinished:
709                 {
710 #if HAVE_FEATURE_MULTIUSER_ENVIRONMENT
711                     // the readonly documents should not be opened in shared mode
712                     if ( HasSharedXMLFlagSet() && !SC_MOD()->IsInSharedDocLoading() && !IsReadOnly() )
713                     {
714                         if ( SwitchToShared( true, false ) )
715                         {
716                             ScViewData* pViewData = GetViewData();
717                             ScTabView* pTabView = ( pViewData ? dynamic_cast< ScTabView* >( pViewData->GetView() ) : nullptr );
718                             if ( pTabView )
719                             {
720                                 pTabView->UpdateLayerLocks();
721                             }
722                         }
723                         else
724                         {
725                             // switching to shared mode has failed, the document should be opened readonly
726                             // TODO/LATER: And error message should be shown here probably
727                             SetReadOnlyUI();
728                         }
729                     }
730 #endif
731                 }
732                 break;
733             case SfxEventHintId::ViewCreated:
734                 {
735  #if HAVE_FEATURE_SCRIPTING
736                     uno::Reference<script::vba::XVBACompatibility> xVBACompat(GetBasicContainer(), uno::UNO_QUERY);
737                     if ( !m_xVBAListener.is() && xVBACompat.is() )
738                     {
739                         m_xVBAListener.set(new VBAScriptListener(this));
740                         xVBACompat->addVBAScriptListener(m_xVBAListener);
741                     }
742 #endif
743 
744 #if HAVE_FEATURE_MULTIUSER_ENVIRONMENT
745                     if ( IsDocShared() && !SC_MOD()->IsInSharedDocLoading() )
746                     {
747                         ScAppOptions aAppOptions = SC_MOD()->GetAppOptions();
748                         if ( aAppOptions.GetShowSharedDocumentWarning() )
749                         {
750                             vcl::Window* pWin = ScDocShell::GetActiveDialogParent();
751 
752                             MessageWithCheck aWarningBox(pWin ? pWin->GetFrameWeld() : nullptr,
753                                     "modules/scalc/ui/sharedwarningdialog.ui", "SharedWarningDialog");
754                             aWarningBox.run();
755 
756                             bool bChecked = aWarningBox.get_active();
757                             if (bChecked)
758                             {
759                                 aAppOptions.SetShowSharedDocumentWarning(false);
760                                 SC_MOD()->SetAppOptions( aAppOptions );
761                             }
762                         }
763                     }
764 #endif
765                     try
766                     {
767                         uno::Reference< uno::XComponentContext > xContext(
768                             comphelper::getProcessComponentContext() );
769                         uno::Reference< lang::XMultiServiceFactory > xServiceManager(
770                             xContext->getServiceManager(),
771                             uno::UNO_QUERY_THROW );
772                         uno::Reference< container::XContentEnumerationAccess > xEnumAccess( xServiceManager, uno::UNO_QUERY_THROW );
773                         uno::Reference< container::XEnumeration> xEnum = xEnumAccess->createContentEnumeration(
774                             "com.sun.star.sheet.SpreadsheetDocumentJob" );
775                         if ( xEnum.is() )
776                         {
777                             while ( xEnum->hasMoreElements() )
778                             {
779                                 uno::Any aAny = xEnum->nextElement();
780                                 uno::Reference< lang::XSingleComponentFactory > xFactory;
781                                 aAny >>= xFactory;
782                                 if ( xFactory.is() )
783                                 {
784                                     uno::Reference< task::XJob > xJob( xFactory->createInstanceWithContext( xContext ), uno::UNO_QUERY_THROW );
785                                     ScViewData* pViewData = GetViewData();
786                                     SfxViewShell* pViewShell = ( pViewData ? pViewData->GetViewShell() : nullptr );
787                                     SfxViewFrame* pViewFrame = ( pViewShell ? pViewShell->GetViewFrame() : nullptr );
788                                     SfxFrame* pFrame = ( pViewFrame ? &pViewFrame->GetFrame() : nullptr );
789                                     uno::Reference< frame::XController > xController = ( pFrame ? pFrame->GetController() : nullptr );
790                                     uno::Reference< sheet::XSpreadsheetView > xSpreadsheetView( xController, uno::UNO_QUERY_THROW );
791                                     uno::Sequence< beans::NamedValue > aArgsForJob { { "SpreadsheetView", uno::makeAny( xSpreadsheetView ) } };
792                                     xJob->execute( aArgsForJob );
793                                 }
794                             }
795                         }
796                     }
797                     catch ( uno::Exception & )
798                     {
799                     }
800                 }
801                 break;
802             case SfxEventHintId::SaveDoc:
803                 {
804 #if HAVE_FEATURE_MULTIUSER_ENVIRONMENT
805                     if ( IsDocShared() && !SC_MOD()->IsInSharedDocSaving() )
806                     {
807                         bool bSuccess = false;
808                         bool bRetry = true;
809                         while ( bRetry )
810                         {
811                             bRetry = false;
812                             uno::Reference< frame::XModel > xModel;
813                             try
814                             {
815                                 // load shared file
816                                 xModel.set( LoadSharedDocument(), uno::UNO_QUERY_THROW );
817                                 uno::Reference< util::XCloseable > xCloseable( xModel, uno::UNO_QUERY_THROW );
818 
819                                 // check if shared flag is set in shared file
820                                 bool bShared = false;
821                                 ScModelObj* pDocObj = ScModelObj::getImplementation( xModel );
822                                 ScDocShell* pSharedDocShell = ( pDocObj ? dynamic_cast< ScDocShell* >( pDocObj->GetObjectShell() ) : nullptr );
823                                 if ( pSharedDocShell )
824                                 {
825                                     bShared = pSharedDocShell->HasSharedXMLFlagSet();
826                                 }
827 
828                                 // #i87870# check if shared status was disabled and enabled again
829                                 bool bOwnEntry = false;
830                                 bool bEntriesNotAccessible = false;
831                                 try
832                                 {
833                                     ::svt::ShareControlFile aControlFile( GetSharedFileURL() );
834                                     bOwnEntry = aControlFile.HasOwnEntry();
835                                 }
836                                 catch ( uno::Exception& )
837                                 {
838                                     bEntriesNotAccessible = true;
839                                 }
840 
841                                 if ( bShared && bOwnEntry )
842                                 {
843                                     uno::Reference< frame::XStorable > xStorable( xModel, uno::UNO_QUERY_THROW );
844 
845                                     if ( xStorable->isReadonly() )
846                                     {
847                                         xCloseable->close( true );
848 
849                                         OUString aUserName( ScResId( STR_UNKNOWN_USER ) );
850                                         bool bNoLockAccess = false;
851                                         try
852                                         {
853                                             ::svt::DocumentLockFile aLockFile( GetSharedFileURL() );
854                                             LockFileEntry aData = aLockFile.GetLockData();
855                                             if ( !aData[LockFileComponent::OOOUSERNAME].isEmpty() )
856                                             {
857                                                 aUserName = aData[LockFileComponent::OOOUSERNAME];
858                                             }
859                                             else if ( !aData[LockFileComponent::SYSUSERNAME].isEmpty() )
860                                             {
861                                                 aUserName = aData[LockFileComponent::SYSUSERNAME];
862                                             }
863                                         }
864                                         catch ( uno::Exception& )
865                                         {
866                                             bNoLockAccess = true;
867                                         }
868 
869                                         if ( bNoLockAccess )
870                                         {
871                                             // TODO/LATER: in future an error regarding impossibility to open file for writing could be shown
872                                             ErrorHandler::HandleError( ERRCODE_IO_GENERAL );
873                                         }
874                                         else
875                                         {
876                                             OUString aMessage( ScResId( STR_FILE_LOCKED_SAVE_LATER ) );
877                                             aMessage = aMessage.replaceFirst( "%1", aUserName );
878 
879                                             vcl::Window* pWin = GetActiveDialogParent();
880                                             std::unique_ptr<weld::MessageDialog> xWarn(Application::CreateMessageDialog(pWin ? pWin->GetFrameWeld() : nullptr,
881                                                                                        VclMessageType::Warning, VclButtonsType::NONE,
882                                                                                        aMessage));
883                                             xWarn->add_button(Button::GetStandardText(StandardButtonType::Retry), RET_RETRY);
884                                             xWarn->add_button(Button::GetStandardText(StandardButtonType::Cancel), RET_CANCEL);
885                                             xWarn->set_default_response(RET_RETRY);
886                                             if (xWarn->run() == RET_RETRY)
887                                             {
888                                                 bRetry = true;
889                                             }
890                                         }
891                                     }
892                                     else
893                                     {
894                                         // merge changes from shared file into temp file
895                                         bool bSaveToShared = false;
896                                         if ( pSharedDocShell )
897                                         {
898                                             bSaveToShared = MergeSharedDocument( pSharedDocShell );
899                                         }
900 
901                                         // close shared file
902                                         xCloseable->close( true );
903 
904                                         // TODO: keep file lock on shared file
905 
906                                         // store to shared file
907                                         if ( bSaveToShared )
908                                         {
909                                             bool bChangedViewSettings = false;
910                                             ScChangeViewSettings* pChangeViewSet = m_aDocument.GetChangeViewSettings();
911                                             if ( pChangeViewSet && pChangeViewSet->ShowChanges() )
912                                             {
913                                                 pChangeViewSet->SetShowChanges( false );
914                                                 pChangeViewSet->SetShowAccepted( false );
915                                                 m_aDocument.SetChangeViewSettings( *pChangeViewSet );
916                                                 bChangedViewSettings = true;
917                                             }
918 
919                                             uno::Reference< frame::XStorable > xStor( GetModel(), uno::UNO_QUERY_THROW );
920                                             // TODO/LATER: More entries from the MediaDescriptor might be interesting for the merge
921                                             uno::Sequence< beans::PropertyValue > aValues(1);
922                                             aValues[0].Name = "FilterName";
923                                             aValues[0].Value <<= GetMedium()->GetFilter()->GetFilterName();
924 
925                                             const SfxStringItem* pPasswordItem = SfxItemSet::GetItem<SfxStringItem>(GetMedium()->GetItemSet(), SID_PASSWORD, false);
926                                             if ( pPasswordItem && !pPasswordItem->GetValue().isEmpty() )
927                                             {
928                                                 aValues.realloc( 2 );
929                                                 aValues[1].Name = "Password";
930                                                 aValues[1].Value <<= pPasswordItem->GetValue();
931                                             }
932 
933                                             SC_MOD()->SetInSharedDocSaving( true );
934                                             xStor->storeToURL( GetSharedFileURL(), aValues );
935                                             SC_MOD()->SetInSharedDocSaving( false );
936 
937                                             if ( bChangedViewSettings )
938                                             {
939                                                 pChangeViewSet->SetShowChanges( true );
940                                                 pChangeViewSet->SetShowAccepted( true );
941                                                 m_aDocument.SetChangeViewSettings( *pChangeViewSet );
942                                             }
943                                         }
944 
945                                         bSuccess = true;
946                                         GetUndoManager()->Clear();
947                                     }
948                                 }
949                                 else
950                                 {
951                                     xCloseable->close( true );
952 
953                                     if ( bEntriesNotAccessible )
954                                     {
955                                         // TODO/LATER: in future an error regarding impossibility to write to share control file could be shown
956                                         ErrorHandler::HandleError( ERRCODE_IO_GENERAL );
957                                     }
958                                     else
959                                     {
960                                         vcl::Window* pWin = GetActiveDialogParent();
961                                         std::unique_ptr<weld::MessageDialog> xWarn(Application::CreateMessageDialog(pWin ? pWin->GetFrameWeld() : nullptr,
962                                                                                    VclMessageType::Warning, VclButtonsType::Ok,
963                                                                                    ScResId(STR_DOC_NOLONGERSHARED)));
964                                         xWarn->run();
965 
966                                         SfxBindings* pBindings = GetViewBindings();
967                                         if ( pBindings )
968                                         {
969                                             pBindings->ExecuteSynchron( SID_SAVEASDOC );
970                                         }
971                                     }
972                                 }
973                             }
974                             catch ( uno::Exception& )
975                             {
976                                 OSL_FAIL( "SfxEventHintId::SaveDoc: caught exception" );
977                                 SC_MOD()->SetInSharedDocSaving( false );
978 
979                                 try
980                                 {
981                                     uno::Reference< util::XCloseable > xClose( xModel, uno::UNO_QUERY_THROW );
982                                     xClose->close( true );
983                                 }
984                                 catch ( uno::Exception& )
985                                 {
986                                 }
987                             }
988                         }
989 
990                         if ( !bSuccess )
991                             SetError(ERRCODE_IO_ABORT); // this error code will produce no error message, but will break the further saving process
992                     }
993 #endif
994 
995                     if (m_pSheetSaveData)
996                         m_pSheetSaveData->SetInSupportedSave(true);
997                 }
998                 break;
999             case SfxEventHintId::SaveAsDoc:
1000                 {
1001                     if ( GetDocument().GetExternalRefManager()->containsUnsavedReferences() )
1002                     {
1003                         vcl::Window* pWin = GetActiveDialogParent();
1004                         std::unique_ptr<weld::MessageDialog> xWarn(Application::CreateMessageDialog(pWin ? pWin->GetFrameWeld() : nullptr,
1005                                                                    VclMessageType::Warning, VclButtonsType::YesNo,
1006                                                                    ScResId(STR_UNSAVED_EXT_REF)));
1007                         if (RET_NO == xWarn->run())
1008                         {
1009                             SetError(ERRCODE_IO_ABORT); // this error code will produce no error message, but will break the further saving process
1010                         }
1011                     }
1012                     [[fallthrough]];
1013                 }
1014             case SfxEventHintId::SaveToDoc:
1015                 // #i108978# If no event is sent before saving, there will also be no "...DONE" event,
1016                 // and SAVE/SAVEAS can't be distinguished from SAVETO. So stream copying is only enabled
1017                 // if there is a SAVE/SAVEAS/SAVETO event first.
1018                 if (m_pSheetSaveData)
1019                     m_pSheetSaveData->SetInSupportedSave(true);
1020                 break;
1021             case SfxEventHintId::SaveDocDone:
1022             case SfxEventHintId::SaveAsDocDone:
1023                 {
1024                     // new positions are used after "save" and "save as", but not "save to"
1025                     UseSheetSaveEntries();      // use positions from saved file for next saving
1026                     [[fallthrough]];
1027                 }
1028             case SfxEventHintId::SaveToDocDone:
1029                 // only reset the flag, don't use the new positions
1030                 if (m_pSheetSaveData)
1031                     m_pSheetSaveData->SetInSupportedSave(false);
1032                 break;
1033             default:
1034                 {
1035                 }
1036                 break;
1037         }
1038     }
1039     else if (rHint.GetId() == SfxHintId::TitleChanged) // Without parameter
1040     {
1041         m_aDocument.SetName( SfxShell::GetName() );
1042         //  RegisterNewTargetNames doesn't exist any longer
1043         SfxGetpApp()->Broadcast(SfxHint( SfxHintId::ScDocNameChanged )); // Navigator
1044     }
1045     else if (rHint.GetId() == SfxHintId::Deinitializing)
1046     {
1047 
1048 #if HAVE_FEATURE_SCRIPTING
1049         uno::Reference<script::vba::XVBACompatibility> xVBACompat(GetBasicContainer(), uno::UNO_QUERY);
1050         if (m_xVBAListener.is() && xVBACompat.is())
1051         {
1052             xVBACompat->removeVBAScriptListener(m_xVBAListener);
1053         }
1054 #endif
1055 
1056         if (m_aDocument.IsClipboardSource())
1057         {
1058             // Notes copied to the clipboard have a raw SdrCaptionObj pointer
1059             // copied from this document, forget it as it references this
1060             // document's drawing layer pages and what not, which otherwise when
1061             // pasting to another document after this document was destructed would
1062             // attempt to access non-existing data. Preserve the text data though.
1063             ScDocument* pClipDoc = GetClipDoc();
1064             if (pClipDoc)
1065                 pClipDoc->ClosingClipboardSource();
1066         }
1067     }
1068 
1069     if ( const SfxEventHint* pSfxEventHint = dynamic_cast<const SfxEventHint*>(&rHint) )
1070     {
1071         switch( pSfxEventHint->GetEventId() )
1072         {
1073            case SfxEventHintId::CreateDoc:
1074                 {
1075                     uno::Any aWorkbook;
1076                     aWorkbook <<= mxAutomationWorkbookObject;
1077                     uno::Sequence< uno::Any > aArgs(1);
1078                     aArgs[0] = aWorkbook;
1079                     SC_MOD()->CallAutomationApplicationEventSinks( "NewWorkbook", aArgs );
1080                 }
1081                 break;
1082             case SfxEventHintId::OpenDoc:
1083                 {
1084                     uno::Any aWorkbook;
1085                     aWorkbook <<= mxAutomationWorkbookObject;
1086                     uno::Sequence< uno::Any > aArgs(1);
1087                     aArgs[0] = aWorkbook;
1088                     SC_MOD()->CallAutomationApplicationEventSinks( "WorkbookOpen", aArgs );
1089                 }
1090                 break;
1091             default:
1092                 break;
1093         }
1094     }
1095 }
1096 
1097 // Load contents for organizer
1098 bool ScDocShell::LoadFrom( SfxMedium& rMedium )
1099 {
1100     LoadMediumGuard aLoadGuard(&m_aDocument);
1101     ScRefreshTimerProtector aProt( m_aDocument.GetRefreshTimerControlAddress() );
1102 
1103     WaitObject aWait( GetActiveDialogParent() );
1104 
1105     bool bRet = false;
1106 
1107     if (GetMedium())
1108     {
1109         const SfxUInt16Item* pUpdateDocItem = SfxItemSet::GetItem<SfxUInt16Item>(rMedium.GetItemSet(), SID_UPDATEDOCMODE, false);
1110         m_nCanUpdate = pUpdateDocItem ? pUpdateDocItem->GetValue() : css::document::UpdateDocMode::NO_UPDATE;
1111     }
1112 
1113     //  until loading/saving only the styles in XML is implemented,
1114     //  load the whole file
1115     bRet = LoadXML( &rMedium, nullptr );
1116     InitItems();
1117 
1118     SfxObjectShell::LoadFrom( rMedium );
1119 
1120     return bRet;
1121 }
1122 
1123 static void lcl_parseHtmlFilterOption(const OUString& rOption, LanguageType& rLang, bool& rDateConvert)
1124 {
1125     OUStringBuffer aBuf;
1126     std::vector< OUString > aTokens;
1127     sal_Int32 n = rOption.getLength();
1128     const sal_Unicode* p = rOption.getStr();
1129     for (sal_Int32 i = 0; i < n; ++i)
1130     {
1131         const sal_Unicode c = p[i];
1132         if (c == ' ')
1133         {
1134             if (!aBuf.isEmpty())
1135                 aTokens.push_back( aBuf.makeStringAndClear() );
1136         }
1137         else
1138             aBuf.append(c);
1139     }
1140 
1141     if (!aBuf.isEmpty())
1142         aTokens.push_back( aBuf.makeStringAndClear() );
1143 
1144     rLang = LanguageType( 0 );
1145     rDateConvert = false;
1146 
1147     if (!aTokens.empty())
1148         rLang = static_cast<LanguageType>(aTokens[0].toInt32());
1149     if (aTokens.size() > 1)
1150         rDateConvert = static_cast<bool>(aTokens[1].toInt32());
1151 }
1152 
1153 bool ScDocShell::ConvertFrom( SfxMedium& rMedium )
1154 {
1155     LoadMediumGuard aLoadGuard(&m_aDocument);
1156 
1157     bool bRet = false; // sal_False means user quit!
1158                            // On error: Set error at stream
1159 
1160     ScRefreshTimerProtector aProt( m_aDocument.GetRefreshTimerControlAddress() );
1161 
1162     GetUndoManager()->Clear();
1163 
1164     // Set optimal col width after import?
1165     bool bSetColWidths = false;
1166     bool bSetSimpleTextColWidths = false;
1167     std::map<SCCOL, ScColWidthParam> aColWidthParam;
1168     ScRange aColWidthRange;
1169     // Set optimal row height after import?
1170     bool bSetRowHeights = false;
1171 
1172     vector<ScDocRowHeightUpdater::TabRanges> aRecalcRowRangesArray;
1173 
1174     //  All filters need the complete file in one piece (not asynchronously)
1175     //  So make sure that we transfer the whole file with CreateFileStream
1176     rMedium.GetPhysicalName();  //! Call CreateFileStream directly, if available
1177 
1178     const SfxUInt16Item* pUpdateDocItem = SfxItemSet::GetItem<SfxUInt16Item>(rMedium.GetItemSet(), SID_UPDATEDOCMODE, false);
1179     m_nCanUpdate = pUpdateDocItem ? pUpdateDocItem->GetValue() : css::document::UpdateDocMode::NO_UPDATE;
1180 
1181     std::shared_ptr<const SfxFilter> pFilter = rMedium.GetFilter();
1182     if (pFilter)
1183     {
1184         OUString aFltName = pFilter->GetFilterName();
1185 
1186         bool bCalc3 = aFltName == "StarCalc 3.0";
1187         bool bCalc4 = aFltName == "StarCalc 4.0";
1188         if (!bCalc3 && !bCalc4)
1189             m_aDocument.SetInsertingFromOtherDoc( true );
1190 
1191         if (aFltName == pFilterXML)
1192             bRet = LoadXML( &rMedium, nullptr );
1193         else if (aFltName == pFilterLotus)
1194         {
1195             OUString sItStr;
1196             SfxItemSet*  pSet = rMedium.GetItemSet();
1197             const SfxPoolItem* pItem;
1198             if ( pSet && SfxItemState::SET ==
1199                  pSet->GetItemState( SID_FILE_FILTEROPTIONS, true, &pItem ) )
1200             {
1201                 sItStr = static_cast<const SfxStringItem*>(pItem)->GetValue();
1202             }
1203 
1204             if (sItStr.isEmpty())
1205             {
1206                 //  default for lotus import (from API without options):
1207                 //  IBM_437 encoding
1208                 sItStr = ScGlobal::GetCharsetString( RTL_TEXTENCODING_IBM_437 );
1209             }
1210 
1211             ErrCode eError = ScFormatFilter::Get().ScImportLotus123( rMedium, &m_aDocument,
1212                                                 ScGlobal::GetCharsetValue(sItStr));
1213             if (eError != ERRCODE_NONE)
1214             {
1215                 if (!GetError())
1216                     SetError(eError);
1217 
1218                 if( eError.IsWarning() )
1219                     bRet = true;
1220             }
1221             else
1222                 bRet = true;
1223             bSetColWidths = true;
1224             bSetRowHeights = true;
1225         }
1226         else if ( aFltName == pFilterExcel4 || aFltName == pFilterExcel5 ||
1227                    aFltName == pFilterExcel95 || aFltName == pFilterExcel97 ||
1228                    aFltName == pFilterEx4Temp || aFltName == pFilterEx5Temp ||
1229                    aFltName == pFilterEx95Temp || aFltName == pFilterEx97Temp )
1230         {
1231             EXCIMPFORMAT eFormat = EIF_AUTO;
1232             if ( aFltName == pFilterExcel4 || aFltName == pFilterEx4Temp )
1233                 eFormat = EIF_BIFF_LE4;
1234             else if ( aFltName == pFilterExcel5 || aFltName == pFilterExcel95 ||
1235                       aFltName == pFilterEx5Temp || aFltName == pFilterEx95Temp )
1236                 eFormat = EIF_BIFF5;
1237             else if ( aFltName == pFilterExcel97 || aFltName == pFilterEx97Temp )
1238                 eFormat = EIF_BIFF8;
1239 
1240             MakeDrawLayer(); //! In the filter
1241             CalcOutputFactor(); // prepare update of row height
1242             ErrCode eError = ScFormatFilter::Get().ScImportExcel( rMedium, &m_aDocument, eFormat );
1243             m_aDocument.UpdateFontCharSet();
1244             if ( m_aDocument.IsChartListenerCollectionNeedsUpdate() )
1245                 m_aDocument.UpdateChartListenerCollection(); //! For all imports?
1246 
1247             // all graphics objects must have names
1248             m_aDocument.EnsureGraphicNames();
1249 
1250             if (eError == SCWARN_IMPORT_RANGE_OVERFLOW)
1251             {
1252                 if (!GetError())
1253                     SetError(eError);
1254                 bRet = true;
1255             }
1256             else if (eError != ERRCODE_NONE)
1257             {
1258                 if (!GetError())
1259                     SetError(eError);
1260             }
1261             else
1262                 bRet = true;
1263         }
1264         else if (aFltName == "Gnumeric Spreadsheet")
1265         {
1266             ScOrcusFilters* pOrcus = ScFormatFilter::Get().GetOrcusFilters();
1267             if (!pOrcus)
1268                 return false;
1269 
1270             bRet = pOrcus->importGnumeric(m_aDocument, rMedium);
1271         }
1272         else if (aFltName == "MS Excel 2003 XML Orcus")
1273         {
1274             ScOrcusFilters* pOrcus = ScFormatFilter::Get().GetOrcusFilters();
1275             if (!pOrcus)
1276                 return false;
1277 
1278             bRet = pOrcus->importExcel2003XML(m_aDocument, rMedium);
1279         }
1280         else if (aFltName == pFilterAscii)
1281         {
1282             SfxItemSet*  pSet = rMedium.GetItemSet();
1283             const SfxPoolItem* pItem;
1284             ScAsciiOptions aOptions;
1285             bool bOptInit = false;
1286 
1287             if ( pSet && SfxItemState::SET ==
1288                  pSet->GetItemState( SID_FILE_FILTEROPTIONS, true, &pItem ) )
1289             {
1290                 aOptions.ReadFromString( static_cast<const SfxStringItem*>(pItem)->GetValue() );
1291                 bOptInit = true;
1292             }
1293 
1294             if ( !bOptInit )
1295             {
1296                 //  default for ascii import (from API without options):
1297                 //  ISO8859-1/MS_1252 encoding, comma, double quotes
1298 
1299                 aOptions.SetCharSet( RTL_TEXTENCODING_MS_1252 );
1300                 aOptions.SetFieldSeps( OUString(',') );
1301                 aOptions.SetTextSep( '"' );
1302             }
1303 
1304             ErrCode eError = ERRCODE_NONE;
1305             bool bOverflowRow, bOverflowCol, bOverflowCell;
1306             bOverflowRow = bOverflowCol = bOverflowCell = false;
1307 
1308             if( ! rMedium.IsStorage() )
1309             {
1310                 ScImportExport  aImpEx( &m_aDocument );
1311                 aImpEx.SetExtOptions( aOptions );
1312 
1313                 SvStream* pInStream = rMedium.GetInStream();
1314                 if (pInStream)
1315                 {
1316                     pInStream->SetStreamCharSet( aOptions.GetCharSet() );
1317                     pInStream->Seek( 0 );
1318                     bRet = aImpEx.ImportStream( *pInStream, rMedium.GetBaseURL(), SotClipboardFormatId::STRING );
1319                     eError = bRet ? ERRCODE_NONE : SCERR_IMPORT_CONNECT;
1320                     m_aDocument.StartAllListeners();
1321                     sc::SetFormulaDirtyContext aCxt;
1322                     m_aDocument.SetAllFormulasDirty(aCxt);
1323 
1324                     // The same resulting name has to be handled in
1325                     // ScExternalRefCache::initializeDoc() and related, hence
1326                     // pass 'true' for RenameTab()'s bExternalDocument for a
1327                     // composed name so ValidTabName() will not be checked,
1328                     // which could veto the rename in case it contained
1329                     // characters that Excel does not handle. If we wanted to
1330                     // change that then it needed to be handled in all
1331                     // corresponding places of the external references
1332                     // manager/cache. Likely then we'd also need a method to
1333                     // compose a name excluding such characters.
1334                     m_aDocument.RenameTab( 0, INetURLObject( rMedium.GetName()).GetBase(), true/*bExternalDocument*/);
1335 
1336                     bOverflowRow = aImpEx.IsOverflowRow();
1337                     bOverflowCol = aImpEx.IsOverflowCol();
1338                     bOverflowCell = aImpEx.IsOverflowCell();
1339                 }
1340                 else
1341                 {
1342                     OSL_FAIL( "No Stream" );
1343                 }
1344             }
1345 
1346             if (eError != ERRCODE_NONE)
1347             {
1348                 if (!GetError())
1349                     SetError(eError);
1350             }
1351             else if (!GetError() && (bOverflowRow || bOverflowCol || bOverflowCell))
1352             {
1353                 // precedence: row, column, cell
1354                 ErrCode nWarn = (bOverflowRow ? SCWARN_IMPORT_ROW_OVERFLOW :
1355                         (bOverflowCol ? SCWARN_IMPORT_COLUMN_OVERFLOW :
1356                          SCWARN_IMPORT_CELL_OVERFLOW));
1357                 SetError(nWarn);
1358             }
1359             bSetColWidths = true;
1360             bSetSimpleTextColWidths = true;
1361         }
1362         else if (aFltName == pFilterDBase)
1363         {
1364             OUString sItStr;
1365             SfxItemSet*  pSet = rMedium.GetItemSet();
1366             const SfxPoolItem* pItem;
1367             if ( pSet && SfxItemState::SET ==
1368                  pSet->GetItemState( SID_FILE_FILTEROPTIONS, true, &pItem ) )
1369             {
1370                 sItStr = static_cast<const SfxStringItem*>(pItem)->GetValue();
1371             }
1372 
1373             if (sItStr.isEmpty())
1374             {
1375                 //  default for dBase import (from API without options):
1376                 //  IBM_850 encoding
1377 
1378                 sItStr = ScGlobal::GetCharsetString( RTL_TEXTENCODING_IBM_850 );
1379             }
1380 
1381             ScDocRowHeightUpdater::TabRanges aRecalcRanges(0);
1382             ErrCode eError = DBaseImport( rMedium.GetPhysicalName(),
1383                     ScGlobal::GetCharsetValue(sItStr), aColWidthParam, *aRecalcRanges.mpRanges );
1384             aRecalcRowRangesArray.push_back(aRecalcRanges);
1385 
1386             if (eError != ERRCODE_NONE)
1387             {
1388                 if (!GetError())
1389                     SetError(eError);
1390                 bRet = ( eError == SCWARN_IMPORT_RANGE_OVERFLOW );
1391             }
1392             else
1393                 bRet = true;
1394 
1395             aColWidthRange.aStart.SetRow( 1 );  // Except for the column header
1396             bSetColWidths = true;
1397             bSetSimpleTextColWidths = true;
1398         }
1399         else if (aFltName == pFilterDif)
1400         {
1401             SvStream* pStream = rMedium.GetInStream();
1402             if (pStream)
1403             {
1404                 ErrCode eError;
1405                 OUString sItStr;
1406                 SfxItemSet*  pSet = rMedium.GetItemSet();
1407                 const SfxPoolItem* pItem;
1408                 if ( pSet && SfxItemState::SET ==
1409                      pSet->GetItemState( SID_FILE_FILTEROPTIONS, true, &pItem ) )
1410                 {
1411                     sItStr = static_cast<const SfxStringItem*>(pItem)->GetValue();
1412                 }
1413 
1414                 if (sItStr.isEmpty())
1415                 {
1416                     //  default for DIF import (from API without options):
1417                     //  ISO8859-1/MS_1252 encoding
1418 
1419                     sItStr = ScGlobal::GetCharsetString( RTL_TEXTENCODING_MS_1252 );
1420                 }
1421 
1422                 eError = ScFormatFilter::Get().ScImportDif( *pStream, &m_aDocument, ScAddress(0,0,0),
1423                                     ScGlobal::GetCharsetValue(sItStr));
1424                 if (eError != ERRCODE_NONE)
1425                 {
1426                     if (!GetError())
1427                         SetError(eError);
1428 
1429                     if( eError.IsWarning() )
1430                         bRet = true;
1431                 }
1432                 else
1433                     bRet = true;
1434             }
1435             bSetColWidths = true;
1436             bSetSimpleTextColWidths = true;
1437             bSetRowHeights = true;
1438         }
1439         else if (aFltName == pFilterSylk)
1440         {
1441             ErrCode eError = SCERR_IMPORT_UNKNOWN;
1442             bool bOverflowRow, bOverflowCol, bOverflowCell;
1443             bOverflowRow = bOverflowCol = bOverflowCell = false;
1444             if( !rMedium.IsStorage() )
1445             {
1446                 ScImportExport aImpEx( &m_aDocument );
1447 
1448                 SvStream* pInStream = rMedium.GetInStream();
1449                 if (pInStream)
1450                 {
1451                     pInStream->Seek( 0 );
1452                     bRet = aImpEx.ImportStream( *pInStream, rMedium.GetBaseURL(), SotClipboardFormatId::SYLK );
1453                     eError = bRet ? ERRCODE_NONE : SCERR_IMPORT_UNKNOWN;
1454                     m_aDocument.StartAllListeners();
1455                     sc::SetFormulaDirtyContext aCxt;
1456                     m_aDocument.SetAllFormulasDirty(aCxt);
1457 
1458                     bOverflowRow = aImpEx.IsOverflowRow();
1459                     bOverflowCol = aImpEx.IsOverflowCol();
1460                     bOverflowCell = aImpEx.IsOverflowCell();
1461                 }
1462                 else
1463                 {
1464                     OSL_FAIL( "No Stream" );
1465                 }
1466             }
1467 
1468             if ( eError != ERRCODE_NONE && !GetError() )
1469                 SetError(eError);
1470             else if (!GetError() && (bOverflowRow || bOverflowCol || bOverflowCell))
1471             {
1472                 // precedence: row, column, cell
1473                 ErrCode nWarn = (bOverflowRow ? SCWARN_IMPORT_ROW_OVERFLOW :
1474                         (bOverflowCol ? SCWARN_IMPORT_COLUMN_OVERFLOW :
1475                          SCWARN_IMPORT_CELL_OVERFLOW));
1476                 SetError(nWarn);
1477             }
1478             bSetColWidths = true;
1479             bSetSimpleTextColWidths = true;
1480             bSetRowHeights = true;
1481         }
1482         else if (aFltName == pFilterQPro6)
1483         {
1484             ErrCode eError = ScFormatFilter::Get().ScImportQuattroPro(rMedium.GetInStream(), &m_aDocument);
1485             if (eError != ERRCODE_NONE)
1486             {
1487                 if (!GetError())
1488                     SetError(eError);
1489                 if( eError.IsWarning() )
1490                     bRet = true;
1491             }
1492             else
1493                 bRet = true;
1494             // TODO: Filter should set column widths. Not doing it here, it may
1495             // result in very narrow or wide columns, depending on content.
1496             // Setting row heights makes cells with font size attribution or
1497             // wrapping enabled look nicer..
1498             bSetRowHeights = true;
1499         }
1500         else if (aFltName == pFilterRtf)
1501         {
1502             ErrCode eError = SCERR_IMPORT_UNKNOWN;
1503             if( !rMedium.IsStorage() )
1504             {
1505                 SvStream* pInStream = rMedium.GetInStream();
1506                 if (pInStream)
1507                 {
1508                     pInStream->Seek( 0 );
1509                     ScRange aRange;
1510                     eError = ScFormatFilter::Get().ScImportRTF( *pInStream, rMedium.GetBaseURL(), &m_aDocument, aRange );
1511                     if (eError != ERRCODE_NONE)
1512                     {
1513                         if (!GetError())
1514                             SetError(eError);
1515 
1516                         if( eError.IsWarning() )
1517                             bRet = true;
1518                     }
1519                     else
1520                         bRet = true;
1521                     m_aDocument.StartAllListeners();
1522                     sc::SetFormulaDirtyContext aCxt;
1523                     m_aDocument.SetAllFormulasDirty(aCxt);
1524                     bSetColWidths = true;
1525                     bSetRowHeights = true;
1526                 }
1527                 else
1528                 {
1529                     OSL_FAIL( "No Stream" );
1530                 }
1531             }
1532 
1533             if ( eError != ERRCODE_NONE && !GetError() )
1534                 SetError(eError);
1535         }
1536         else if (aFltName == pFilterHtml || aFltName == pFilterHtmlWebQ)
1537         {
1538             ErrCode eError = SCERR_IMPORT_UNKNOWN;
1539             bool bWebQuery = aFltName == pFilterHtmlWebQ;
1540             if( !rMedium.IsStorage() )
1541             {
1542                 SvStream* pInStream = rMedium.GetInStream();
1543                 if (pInStream)
1544                 {
1545                     LanguageType eLang = LANGUAGE_SYSTEM;
1546                     bool bDateConvert = false;
1547                     SfxItemSet*  pSet = rMedium.GetItemSet();
1548                     const SfxPoolItem* pItem;
1549                     if ( pSet && SfxItemState::SET ==
1550                          pSet->GetItemState( SID_FILE_FILTEROPTIONS, true, &pItem ) )
1551                     {
1552                         OUString aFilterOption = static_cast<const SfxStringItem*>(pItem)->GetValue();
1553                         lcl_parseHtmlFilterOption(aFilterOption, eLang, bDateConvert);
1554                     }
1555 
1556                     pInStream->Seek( 0 );
1557                     ScRange aRange;
1558                     // HTML does its own ColWidth/RowHeight
1559                     CalcOutputFactor();
1560                     SvNumberFormatter aNumFormatter( comphelper::getProcessComponentContext(), eLang);
1561                     eError = ScFormatFilter::Get().ScImportHTML( *pInStream, rMedium.GetBaseURL(), &m_aDocument, aRange,
1562                                             GetOutputFactor(), !bWebQuery, &aNumFormatter, bDateConvert );
1563                     if (eError != ERRCODE_NONE)
1564                     {
1565                         if (!GetError())
1566                             SetError(eError);
1567 
1568                         if( eError.IsWarning() )
1569                             bRet = true;
1570                     }
1571                     else
1572                         bRet = true;
1573                     m_aDocument.StartAllListeners();
1574 
1575                     sc::SetFormulaDirtyContext aCxt;
1576                     m_aDocument.SetAllFormulasDirty(aCxt);
1577                 }
1578                 else
1579                 {
1580                     OSL_FAIL( "No Stream" );
1581                 }
1582             }
1583 
1584             if ( eError != ERRCODE_NONE && !GetError() )
1585                 SetError(eError);
1586         }
1587         else
1588         {
1589             if (!GetError())
1590             {
1591                 SAL_WARN("sc.filter", "No match for filter '" << aFltName << "' in ConvertFrom");
1592                 SetError(SCERR_IMPORT_NI);
1593             }
1594         }
1595 
1596         if (!bCalc3)
1597             m_aDocument.SetInsertingFromOtherDoc( false );
1598     }
1599     else
1600     {
1601         OSL_FAIL("No Filter in ConvertFrom");
1602     }
1603 
1604     InitItems();
1605     CalcOutputFactor();
1606     if ( bRet && (bSetColWidths || bSetRowHeights) )
1607     {   // Adjust column width/row height; base 100% zoom
1608         Fraction aZoom( 1, 1 );
1609         double nPPTX = ScGlobal::nScreenPPTX * static_cast<double>(aZoom) / GetOutputFactor(); // Factor is printer display ratio
1610         double nPPTY = ScGlobal::nScreenPPTY * static_cast<double>(aZoom);
1611         ScopedVclPtrInstance< VirtualDevice > pVirtDev;
1612         //  all sheets (for Excel import)
1613         SCTAB nTabCount = m_aDocument.GetTableCount();
1614         for (SCTAB nTab=0; nTab<nTabCount; nTab++)
1615         {
1616             SCCOL nEndCol;
1617             SCROW nEndRow;
1618             m_aDocument.GetCellArea( nTab, nEndCol, nEndRow );
1619             aColWidthRange.aEnd.SetCol( nEndCol );
1620             aColWidthRange.aEnd.SetRow( nEndRow );
1621             ScMarkData aMark;
1622             aMark.SetMarkArea( aColWidthRange );
1623             aMark.MarkToMulti();
1624 
1625             // Order is important: First width, then height
1626             if ( bSetColWidths )
1627             {
1628                 for ( SCCOL nCol=0; nCol <= nEndCol; nCol++ )
1629                 {
1630                     if (!bSetSimpleTextColWidths)
1631                         aColWidthParam[nCol].mbSimpleText = false;
1632 
1633                     sal_uInt16 nWidth = m_aDocument.GetOptimalColWidth(
1634                         nCol, nTab, pVirtDev, nPPTX, nPPTY, aZoom, aZoom, false, &aMark,
1635                         &aColWidthParam[nCol] );
1636                     m_aDocument.SetColWidth( nCol, nTab,
1637                         nWidth + static_cast<sal_uInt16>(ScGlobal::nLastColWidthExtra) );
1638                 }
1639             }
1640         }
1641 
1642         if (bSetRowHeights)
1643         {
1644             // Update all rows in all tables.
1645             ScSizeDeviceProvider aProv(this);
1646             ScDocRowHeightUpdater aUpdater(m_aDocument, aProv.GetDevice(), aProv.GetPPTX(), aProv.GetPPTY(), nullptr);
1647             aUpdater.update();
1648         }
1649         else if (!aRecalcRowRangesArray.empty())
1650         {
1651             // Update only specified row ranges for better performance.
1652             ScSizeDeviceProvider aProv(this);
1653             ScDocRowHeightUpdater aUpdater(m_aDocument, aProv.GetDevice(), aProv.GetPPTX(), aProv.GetPPTY(), &aRecalcRowRangesArray);
1654             aUpdater.update();
1655         }
1656     }
1657     FinishedLoading();
1658 
1659     // invalidate eventually temporary table areas
1660     if ( bRet )
1661         m_aDocument.InvalidateTableArea();
1662 
1663     m_bIsEmpty = false;
1664 
1665     return bRet;
1666 }
1667 
1668 bool ScDocShell::LoadExternal( SfxMedium& rMed )
1669 {
1670     std::shared_ptr<const SfxFilter> pFilter = rMed.GetFilter();
1671     if (!pFilter)
1672         return false;
1673 
1674     if (pFilter->GetProviderName() == "orcus")
1675     {
1676         ScOrcusFilters* pOrcus = ScFormatFilter::Get().GetOrcusFilters();
1677         if (!pOrcus)
1678             return false;
1679 
1680         const OUString& rFilterName = pFilter->GetName();
1681         if (rFilterName == "gnumeric")
1682         {
1683             if (!pOrcus->importGnumeric(m_aDocument, rMed))
1684                 return false;
1685         }
1686         else if (rFilterName == "csv")
1687         {
1688             if (!pOrcus->importCSV(m_aDocument, rMed))
1689                 return false;
1690         }
1691         else if (rFilterName == "xlsx")
1692         {
1693             if (!pOrcus->importXLSX(m_aDocument, rMed))
1694                 return false;
1695         }
1696         else if (rFilterName == "ods")
1697         {
1698             if (!pOrcus->importODS(m_aDocument, rMed))
1699                 return false;
1700         }
1701 
1702         FinishedLoading();
1703         return true;
1704     }
1705 
1706     return false;
1707 }
1708 
1709 ScDocShell::PrepareSaveGuard::PrepareSaveGuard( ScDocShell& rDocShell )
1710     : mrDocShell( rDocShell)
1711 {
1712     // DoEnterHandler not here (because of AutoSave), is in ExecuteSave.
1713 
1714     ScChartListenerCollection* pCharts = mrDocShell.m_aDocument.GetChartListenerCollection();
1715     if (pCharts)
1716         pCharts->UpdateDirtyCharts();                           // Charts to be updated.
1717     mrDocShell.m_aDocument.StopTemporaryChartLock();
1718     if (mrDocShell.m_pAutoStyleList)
1719         mrDocShell.m_pAutoStyleList->ExecuteAllNow();             // Execute template timeouts now.
1720     if (mrDocShell.m_aDocument.HasExternalRefManager())
1721     {
1722         ScExternalRefManager* pRefMgr = mrDocShell.m_aDocument.GetExternalRefManager();
1723         if (pRefMgr && pRefMgr->hasExternalData())
1724         {
1725             pRefMgr->setAllCacheTableReferencedStati( false);
1726             mrDocShell.m_aDocument.MarkUsedExternalReferences();  // Mark tables of external references to be written.
1727         }
1728     }
1729     if (mrDocShell.GetCreateMode()== SfxObjectCreateMode::STANDARD)
1730         mrDocShell.SfxObjectShell::SetVisArea( tools::Rectangle() );   // "Normally" worked on => no VisArea.
1731 }
1732 
1733 ScDocShell::PrepareSaveGuard::~PrepareSaveGuard()
1734 {
1735     if (mrDocShell.m_aDocument.HasExternalRefManager())
1736     {
1737         ScExternalRefManager* pRefMgr = mrDocShell.m_aDocument.GetExternalRefManager();
1738         if (pRefMgr && pRefMgr->hasExternalData())
1739         {
1740             // Prevent accidental data loss due to lack of knowledge.
1741             pRefMgr->setAllCacheTableReferencedStati( true);
1742         }
1743     }
1744 }
1745 
1746 bool ScDocShell::Save()
1747 {
1748     ScRefreshTimerProtector aProt( m_aDocument.GetRefreshTimerControlAddress() );
1749 
1750     PrepareSaveGuard aPrepareGuard( *this);
1751 
1752     if (const auto pFrame1 = SfxViewFrame::GetFirst(this))
1753     {
1754         if (auto pSysWin = pFrame1->GetWindow().GetSystemWindow())
1755         {
1756             pSysWin->SetAccessibleName(OUString());
1757         }
1758     }
1759     //  wait cursor is handled with progress bar
1760     bool bRet = SfxObjectShell::Save();
1761     if( bRet )
1762         bRet = SaveXML( GetMedium(), nullptr );
1763     return bRet;
1764 }
1765 
1766 namespace {
1767 
1768 /**
1769  * Remove the file name from the full path, to keep only the directory path.
1770  */
1771 void popFileName(OUString& rPath)
1772 {
1773     if (!rPath.isEmpty())
1774     {
1775         INetURLObject aURLObj(rPath);
1776         aURLObj.removeSegment();
1777         rPath = aURLObj.GetMainURL(INetURLObject::DecodeMechanism::NONE);
1778     }
1779 }
1780 
1781 }
1782 
1783 bool ScDocShell::SaveAs( SfxMedium& rMedium )
1784 {
1785     OUString aCurPath; // empty for new document that hasn't been saved.
1786     const SfxMedium* pCurMedium = GetMedium();
1787     if (pCurMedium)
1788     {
1789         aCurPath = pCurMedium->GetName();
1790         popFileName(aCurPath);
1791     }
1792 
1793     if (!aCurPath.isEmpty())
1794     {
1795         // current document has a path -> not a brand-new document.
1796         OUString aNewPath = rMedium.GetName();
1797         popFileName(aNewPath);
1798         OUString aRel = URIHelper::simpleNormalizedMakeRelative(aCurPath, aNewPath);
1799         if (!aRel.isEmpty())
1800         {
1801             // Directory path will change before and after the save.
1802             m_aDocument.InvalidateStreamOnSave();
1803         }
1804     }
1805 
1806     ScTabViewShell* pViewShell = GetBestViewShell();
1807     bool bNeedsRehash = ScPassHashHelper::needsPassHashRegen(m_aDocument, PASSHASH_SHA1);
1808     if (bNeedsRehash)
1809         // legacy xls hash double-hashed by SHA1 is also supported.
1810         bNeedsRehash = ScPassHashHelper::needsPassHashRegen(m_aDocument, PASSHASH_XL, PASSHASH_SHA1);
1811     if (bNeedsRehash)
1812     {   // SHA256 explicitly supported in ODF 1.2, implicitly in ODF 1.1
1813         bNeedsRehash = ScPassHashHelper::needsPassHashRegen(m_aDocument, PASSHASH_SHA256);
1814     }
1815 
1816     if (pViewShell && bNeedsRehash)
1817     {
1818         if (!pViewShell->ExecuteRetypePassDlg(PASSHASH_SHA1))
1819             // password re-type cancelled.  Don't save the document.
1820             return false;
1821     }
1822 
1823     ScRefreshTimerProtector aProt( m_aDocument.GetRefreshTimerControlAddress() );
1824 
1825     PrepareSaveGuard aPrepareGuard( *this);
1826 
1827     //  wait cursor is handled with progress bar
1828     bool bRet = SfxObjectShell::SaveAs( rMedium );
1829     if (bRet)
1830         bRet = SaveXML( &rMedium, nullptr );
1831 
1832     return bRet;
1833 }
1834 
1835 namespace {
1836 
1837 // Xcl-like column width measured in characters of standard font.
1838 sal_Int32 lcl_ScDocShell_GetColWidthInChars( sal_uInt16 nWidth )
1839 {
1840     double f = nWidth;
1841     f *= 1328.0 / 25.0;
1842     f += 90.0;
1843     f *= 1.0 / 23.0;
1844     f /= 256.0;
1845 
1846     return sal_Int32( f );
1847 }
1848 
1849 void lcl_ScDocShell_GetFixedWidthString( OUString& rStr, const ScDocument& rDoc,
1850         SCTAB nTab, SCCOL nCol, bool bValue, SvxCellHorJustify eHorJust )
1851 {
1852     OUString aString = rStr;
1853     sal_Int32 nLen = lcl_ScDocShell_GetColWidthInChars(
1854             rDoc.GetColWidth( nCol, nTab ) );
1855     //If the text won't fit in the column
1856     if ( nLen < aString.getLength() )
1857     {
1858         OUStringBuffer aReplacement;
1859         if (bValue)
1860             aReplacement.append("###");
1861         else
1862             aReplacement.append(aString);
1863         //truncate to the number of characters that should fit, even in the
1864         //bValue case nLen might be < len ###
1865         aString = comphelper::string::truncateToLength(aReplacement, nLen).makeStringAndClear();
1866     }
1867     if ( nLen > aString.getLength() )
1868     {
1869         if ( bValue && eHorJust == SvxCellHorJustify::Standard )
1870             eHorJust = SvxCellHorJustify::Right;
1871         sal_Int32 nBlanks = nLen - aString.getLength();
1872         switch ( eHorJust )
1873         {
1874             case SvxCellHorJustify::Right:
1875             {
1876                 OUStringBuffer aTmp;
1877                 aTmp = comphelper::string::padToLength( aTmp, nBlanks, ' ' );
1878                 aString = aTmp.append(aString).makeStringAndClear();
1879             }
1880             break;
1881             case SvxCellHorJustify::Center:
1882             {
1883                 sal_Int32 nLeftPad = nBlanks / 2;
1884                 OUStringBuffer aTmp;
1885                 comphelper::string::padToLength( aTmp, nLeftPad, ' ' );
1886                 aTmp.append(aString);
1887                 comphelper::string::padToLength( aTmp, nLen, ' ' );
1888                 aString = aTmp.makeStringAndClear();
1889             }
1890             break;
1891             default:
1892             {
1893                 OUStringBuffer aTmp(aString);
1894                 comphelper::string::padToLength( aTmp, nLen, ' ' );
1895                 aString = aTmp.makeStringAndClear();
1896             }
1897         }
1898     }
1899     rStr = aString;
1900 }
1901 
1902 void lcl_ScDocShell_WriteEmptyFixedWidthString( SvStream& rStream,
1903         const ScDocument& rDoc, SCTAB nTab, SCCOL nCol )
1904 {
1905     OUString aString;
1906     lcl_ScDocShell_GetFixedWidthString( aString, rDoc, nTab, nCol, false,
1907             SvxCellHorJustify::Standard );
1908     rStream.WriteUnicodeOrByteText( aString );
1909 }
1910 
1911 template<typename StrT, typename SepCharT>
1912 sal_Int32 getTextSepPos(
1913     const StrT& rStr, const ScImportOptions& rAsciiOpt, const SepCharT& rTextSep, const SepCharT& rFieldSep, bool& rNeedQuotes)
1914 {
1915     // #i116636# quotes are needed if text delimiter (quote), field delimiter,
1916     // or LF or CR is in the cell text.
1917     sal_Int32 nPos = rStr.indexOf(rTextSep);
1918     rNeedQuotes = rAsciiOpt.bQuoteAllText || (nPos >= 0) ||
1919         (rStr.indexOf(rFieldSep) >= 0) ||
1920         (rStr.indexOf('\n') >= 0) ||
1921         (rStr.indexOf('\r') >= 0);
1922     return nPos;
1923 }
1924 
1925 template<typename StrT, typename StrBufT>
1926 void escapeTextSep(sal_Int32 nPos, const StrT& rStrDelim, StrT& rStr)
1927 {
1928     while (nPos >= 0)
1929     {
1930         StrBufT aBuf(rStr);
1931         aBuf.insert(nPos, rStrDelim);
1932         rStr = aBuf.makeStringAndClear();
1933         nPos = rStr.indexOf(rStrDelim, nPos+1+rStrDelim.getLength());
1934     }
1935 }
1936 
1937 }
1938 
1939 void ScDocShell::AsciiSave( SvStream& rStream, const ScImportOptions& rAsciiOpt )
1940 {
1941     sal_Unicode cDelim    = rAsciiOpt.nFieldSepCode;
1942     sal_Unicode cStrDelim = rAsciiOpt.nTextSepCode;
1943     rtl_TextEncoding eCharSet      = rAsciiOpt.eCharSet;
1944     bool bFixedWidth      = rAsciiOpt.bFixedWidth;
1945     bool bSaveAsShown     = rAsciiOpt.bSaveAsShown;
1946     bool bShowFormulas    = rAsciiOpt.bSaveFormulas;
1947 
1948     rtl_TextEncoding eOldCharSet = rStream.GetStreamCharSet();
1949     rStream.SetStreamCharSet( eCharSet );
1950     SvStreamEndian nOldNumberFormatInt = rStream.GetEndian();
1951     OString aStrDelimEncoded;    // only used if not Unicode
1952     OUString aStrDelimDecoded;     // only used if context encoding
1953     OString aDelimEncoded;
1954     OUString aDelimDecoded;
1955     bool bContextOrNotAsciiEncoding;
1956     if ( eCharSet == RTL_TEXTENCODING_UNICODE )
1957     {
1958         rStream.StartWritingUnicodeText();
1959         bContextOrNotAsciiEncoding = false;
1960     }
1961     else
1962     {
1963         aStrDelimEncoded = OString(&cStrDelim, 1, eCharSet);
1964         aDelimEncoded = OString(&cDelim, 1, eCharSet);
1965         rtl_TextEncodingInfo aInfo;
1966         aInfo.StructSize = sizeof(aInfo);
1967         if ( rtl_getTextEncodingInfo( eCharSet, &aInfo ) )
1968         {
1969             bContextOrNotAsciiEncoding =
1970                 (((aInfo.Flags & RTL_TEXTENCODING_INFO_CONTEXT) != 0) ||
1971                  ((aInfo.Flags & RTL_TEXTENCODING_INFO_ASCII) == 0));
1972             if ( bContextOrNotAsciiEncoding )
1973             {
1974                 aStrDelimDecoded = OStringToOUString(aStrDelimEncoded, eCharSet);
1975                 aDelimDecoded = OStringToOUString(aDelimEncoded, eCharSet);
1976             }
1977         }
1978         else
1979             bContextOrNotAsciiEncoding = false;
1980     }
1981 
1982     SCCOL nStartCol = 0;
1983     SCROW nStartRow = 0;
1984     SCTAB nTab = GetSaveTab();
1985     SCCOL nEndCol;
1986     SCROW nEndRow;
1987     m_aDocument.GetCellArea( nTab, nEndCol, nEndRow );
1988 
1989     ScProgress aProgress( this, ScResId( STR_SAVE_DOC ), nEndRow, true );
1990 
1991     OUString aString;
1992 
1993     bool bTabProtect = m_aDocument.IsTabProtected( nTab );
1994 
1995     SCCOL nCol;
1996     SCROW nRow;
1997     SCCOL nNextCol = nStartCol;
1998     SCROW nNextRow = nStartRow;
1999     SCCOL nEmptyCol;
2000     SCROW nEmptyRow;
2001     SvNumberFormatter& rFormatter = *m_aDocument.GetFormatTable();
2002 
2003     ScHorizontalCellIterator aIter( &m_aDocument, nTab, nStartCol, nStartRow,
2004         nEndCol, nEndRow );
2005     ScRefCellValue* pCell;
2006     while ( ( pCell = aIter.GetNext( nCol, nRow ) ) != nullptr )
2007     {
2008         bool bProgress = false;     // only upon line change
2009         if ( nNextRow < nRow )
2010         {   // empty rows or/and empty columns up to end of row
2011             bProgress = true;
2012             for ( nEmptyCol = nNextCol; nEmptyCol < nEndCol; nEmptyCol++ )
2013             {   // remaining columns of last row
2014                 if ( bFixedWidth )
2015                     lcl_ScDocShell_WriteEmptyFixedWidthString( rStream,
2016                             m_aDocument, nTab, nEmptyCol );
2017                 else if ( cDelim != 0 )
2018                     rStream.WriteUniOrByteChar( cDelim );
2019             }
2020             endlub( rStream );
2021             nNextRow++;
2022             for ( nEmptyRow = nNextRow; nEmptyRow < nRow; nEmptyRow++ )
2023             {   // completely empty rows
2024                 for ( nEmptyCol = nStartCol; nEmptyCol < nEndCol; nEmptyCol++ )
2025                 {
2026                     if ( bFixedWidth )
2027                         lcl_ScDocShell_WriteEmptyFixedWidthString( rStream,
2028                                 m_aDocument, nTab, nEmptyCol );
2029                     else if ( cDelim != 0 )
2030                         rStream.WriteUniOrByteChar( cDelim );
2031                 }
2032                 endlub( rStream );
2033             }
2034             for ( nEmptyCol = nStartCol; nEmptyCol < nCol; nEmptyCol++ )
2035             {   // empty columns at beginning of row
2036                 if ( bFixedWidth )
2037                     lcl_ScDocShell_WriteEmptyFixedWidthString( rStream,
2038                             m_aDocument, nTab, nEmptyCol );
2039                 else if ( cDelim != 0 )
2040                     rStream.WriteUniOrByteChar( cDelim );
2041             }
2042             nNextRow = nRow;
2043         }
2044         else if ( nNextCol < nCol )
2045         {   // empty columns in same row
2046             for ( nEmptyCol = nNextCol; nEmptyCol < nCol; nEmptyCol++ )
2047             {   // columns in between
2048                 if ( bFixedWidth )
2049                     lcl_ScDocShell_WriteEmptyFixedWidthString( rStream,
2050                             m_aDocument, nTab, nEmptyCol );
2051                 else if ( cDelim != 0 )
2052                     rStream.WriteUniOrByteChar( cDelim );
2053             }
2054         }
2055         if ( nCol == nEndCol )
2056         {
2057             bProgress = true;
2058             nNextCol = nStartCol;
2059             nNextRow = nRow + 1;
2060         }
2061         else
2062             nNextCol = nCol + 1;
2063 
2064         CellType eType = pCell->meType;
2065         ScAddress aPos(nCol, nRow, nTab);
2066         if ( bTabProtect )
2067         {
2068             const ScProtectionAttr* pProtAttr =
2069                 m_aDocument.GetAttr( nCol, nRow, nTab, ATTR_PROTECTION );
2070             if ( pProtAttr->GetHideCell() ||
2071                     ( eType == CELLTYPE_FORMULA && bShowFormulas &&
2072                       pProtAttr->GetHideFormula() ) )
2073                 eType = CELLTYPE_NONE;  // hide
2074         }
2075         bool bString;
2076         switch ( eType )
2077         {
2078             case CELLTYPE_NONE:
2079                 aString.clear();
2080                 bString = false;
2081                 break;
2082             case CELLTYPE_FORMULA :
2083                 {
2084                     FormulaError nErrCode;
2085                     if ( bShowFormulas )
2086                     {
2087                         pCell->mpFormula->GetFormula(aString);
2088                         bString = true;
2089                     }
2090                     else if ((nErrCode = pCell->mpFormula->GetErrCode()) != FormulaError::NONE)
2091                     {
2092                         aString = ScGlobal::GetErrorString( nErrCode );
2093                         bString = true;
2094                     }
2095                     else if (pCell->mpFormula->IsValue())
2096                     {
2097                         sal_uInt32 nFormat = m_aDocument.GetNumberFormat(aPos);
2098                         if ( bFixedWidth || bSaveAsShown )
2099                         {
2100                             Color* pDummy;
2101                             ScCellFormat::GetString(*pCell, nFormat, aString, &pDummy, rFormatter, &m_aDocument);
2102                             bString = bSaveAsShown && rFormatter.IsTextFormat( nFormat);
2103                         }
2104                         else
2105                         {
2106                             ScCellFormat::GetInputString(*pCell, nFormat, aString, rFormatter, &m_aDocument);
2107                             bString = false;
2108                         }
2109                     }
2110                     else
2111                     {
2112                         if ( bSaveAsShown )
2113                         {
2114                             sal_uInt32 nFormat = m_aDocument.GetNumberFormat(aPos);
2115                             Color* pDummy;
2116                             ScCellFormat::GetString(*pCell, nFormat, aString, &pDummy, rFormatter, &m_aDocument);
2117                         }
2118                         else
2119                             aString = pCell->mpFormula->GetString().getString();
2120                         bString = true;
2121                     }
2122                 }
2123                 break;
2124             case CELLTYPE_STRING :
2125                 if ( bSaveAsShown )
2126                 {
2127                     sal_uInt32 nFormat = m_aDocument.GetNumberFormat(aPos);
2128                     Color* pDummy;
2129                     ScCellFormat::GetString(*pCell, nFormat, aString, &pDummy, rFormatter, &m_aDocument);
2130                 }
2131                 else
2132                     aString = pCell->mpString->getString();
2133                 bString = true;
2134                 break;
2135             case CELLTYPE_EDIT :
2136                 {
2137                     const EditTextObject* pObj = pCell->mpEditText;
2138                     EditEngine& rEngine = m_aDocument.GetEditEngine();
2139                     rEngine.SetText( *pObj);
2140                     aString = rEngine.GetText();  // including LF
2141                     bString = true;
2142                 }
2143                 break;
2144             case CELLTYPE_VALUE :
2145                 {
2146                     sal_uInt32 nFormat;
2147                     m_aDocument.GetNumberFormat( nCol, nRow, nTab, nFormat );
2148                     if ( bFixedWidth || bSaveAsShown )
2149                     {
2150                         Color* pDummy;
2151                         ScCellFormat::GetString(*pCell, nFormat, aString, &pDummy, rFormatter, &m_aDocument);
2152                         bString = bSaveAsShown && rFormatter.IsTextFormat( nFormat);
2153                     }
2154                     else
2155                     {
2156                         ScCellFormat::GetInputString(*pCell, nFormat, aString, rFormatter, &m_aDocument);
2157                         bString = false;
2158                     }
2159                 }
2160                 break;
2161             default:
2162                 OSL_FAIL( "ScDocShell::AsciiSave: unknown CellType" );
2163                 aString.clear();
2164                 bString = false;
2165         }
2166 
2167         if ( bFixedWidth )
2168         {
2169             SvxCellHorJustify eHorJust =
2170                 m_aDocument.GetAttr( nCol, nRow, nTab, ATTR_HOR_JUSTIFY )->GetValue();
2171             lcl_ScDocShell_GetFixedWidthString( aString, m_aDocument, nTab, nCol,
2172                     !bString, eHorJust );
2173             rStream.WriteUnicodeOrByteText( aString );
2174         }
2175         else
2176         {
2177             OUString aUniString = aString;// TODO: remove that later
2178             if (!bString && cStrDelim != 0 && !aUniString.isEmpty())
2179             {
2180                 sal_Unicode c = aUniString[0];
2181                 bString = (c == cStrDelim || c == ' ' ||
2182                         aUniString.endsWith(" ") ||
2183                         aUniString.indexOf(cStrDelim) >= 0);
2184                 if (!bString && cDelim != 0)
2185                     bString = (aUniString.indexOf(cDelim) >= 0);
2186             }
2187             if ( bString )
2188             {
2189                 if ( cStrDelim != 0 ) //@ BugId 55355
2190                 {
2191                     if ( eCharSet == RTL_TEXTENCODING_UNICODE )
2192                     {
2193                         bool bNeedQuotes = false;
2194                         sal_Int32 nPos = getTextSepPos(
2195                             aUniString, rAsciiOpt, cStrDelim, cDelim, bNeedQuotes);
2196 
2197                         escapeTextSep<OUString, OUStringBuffer>(
2198                             nPos, OUString(cStrDelim), aUniString);
2199 
2200                         if ( bNeedQuotes )
2201                             rStream.WriteUniOrByteChar( cStrDelim, eCharSet );
2202                         write_uInt16s_FromOUString(rStream, aUniString);
2203                         if ( bNeedQuotes )
2204                             rStream.WriteUniOrByteChar( cStrDelim, eCharSet );
2205                     }
2206                     else
2207                     {
2208                         // This is nasty. The Unicode to byte encoding
2209                         // may convert typographical quotation marks to ASCII
2210                         // quotation marks, which may interfere with the delimiter,
2211                         // so we have to escape delimiters after the string has
2212                         // been encoded. Since this may happen also with UTF-8
2213                         // encoded typographical quotation marks if such was
2214                         // specified as a delimiter we have to check for the full
2215                         // encoded delimiter string, not just one character.
2216                         // Now for RTL_TEXTENCODING_ISO_2022_... and similar brain
2217                         // dead encodings where one code point (and especially a
2218                         // low ASCII value) may represent different characters, we
2219                         // have to convert forth and back and forth again. Same for
2220                         // UTF-7 since it is a context sensitive encoding too.
2221 
2222                         if ( bContextOrNotAsciiEncoding )
2223                         {
2224                             // to byte encoding
2225                             OString aStrEnc = OUStringToOString(aUniString, eCharSet);
2226                             // back to Unicode
2227                             OUString aStrDec = OStringToOUString(aStrEnc, eCharSet);
2228 
2229                             // search on re-decoded string
2230                             bool bNeedQuotes = false;
2231                             sal_Int32 nPos = getTextSepPos(
2232                                 aStrDec, rAsciiOpt, aStrDelimDecoded, aDelimDecoded, bNeedQuotes);
2233 
2234                             escapeTextSep<OUString, OUStringBuffer>(
2235                                 nPos, aStrDelimDecoded, aStrDec);
2236 
2237                             // write byte re-encoded
2238                             if ( bNeedQuotes )
2239                                 rStream.WriteUniOrByteChar( cStrDelim, eCharSet );
2240                             rStream.WriteUnicodeOrByteText( aStrDec, eCharSet );
2241                             if ( bNeedQuotes )
2242                                 rStream.WriteUniOrByteChar( cStrDelim, eCharSet );
2243                         }
2244                         else
2245                         {
2246                             OString aStrEnc = OUStringToOString(aUniString, eCharSet);
2247 
2248                             // search on encoded string
2249                             bool bNeedQuotes = false;
2250                             sal_Int32 nPos = getTextSepPos(
2251                                 aStrEnc, rAsciiOpt, aStrDelimEncoded, aDelimEncoded, bNeedQuotes);
2252 
2253                             escapeTextSep<OString, OStringBuffer>(
2254                                 nPos, aStrDelimEncoded, aStrEnc);
2255 
2256                             // write byte encoded
2257                             if ( bNeedQuotes )
2258                                 rStream.WriteBytes(
2259                                     aStrDelimEncoded.getStr(), aStrDelimEncoded.getLength());
2260                             rStream.WriteBytes(aStrEnc.getStr(), aStrEnc.getLength());
2261                             if ( bNeedQuotes )
2262                                 rStream.WriteBytes(
2263                                     aStrDelimEncoded.getStr(), aStrDelimEncoded.getLength());
2264                         }
2265                     }
2266                 }
2267                 else
2268                     rStream.WriteUnicodeOrByteText( aUniString );
2269             }
2270             else
2271                 rStream.WriteUnicodeOrByteText( aUniString );
2272         }
2273 
2274         if( nCol < nEndCol )
2275         {
2276             if(cDelim!=0) //@ BugId 55355
2277                 rStream.WriteUniOrByteChar( cDelim );
2278         }
2279         else
2280             endlub( rStream );
2281 
2282         if ( bProgress )
2283             aProgress.SetStateOnPercent( nRow );
2284     }
2285 
2286     // write out empty if requested
2287     if ( nNextRow <= nEndRow )
2288     {
2289         for ( nEmptyCol = nNextCol; nEmptyCol < nEndCol; nEmptyCol++ )
2290         {   // remaining empty columns of last row
2291             if ( bFixedWidth )
2292                 lcl_ScDocShell_WriteEmptyFixedWidthString( rStream,
2293                         m_aDocument, nTab, nEmptyCol );
2294             else if ( cDelim != 0 )
2295                 rStream.WriteUniOrByteChar( cDelim );
2296         }
2297         endlub( rStream );
2298         nNextRow++;
2299     }
2300     for ( nEmptyRow = nNextRow; nEmptyRow <= nEndRow; nEmptyRow++ )
2301     {   // entire empty rows
2302         for ( nEmptyCol = nStartCol; nEmptyCol < nEndCol; nEmptyCol++ )
2303         {
2304             if ( bFixedWidth )
2305                 lcl_ScDocShell_WriteEmptyFixedWidthString( rStream,
2306                         m_aDocument, nTab, nEmptyCol );
2307             else if ( cDelim != 0 )
2308                 rStream.WriteUniOrByteChar( cDelim );
2309         }
2310         endlub( rStream );
2311     }
2312 
2313     rStream.SetStreamCharSet( eOldCharSet );
2314     rStream.SetEndian( nOldNumberFormatInt );
2315 }
2316 
2317 bool ScDocShell::ConvertTo( SfxMedium &rMed )
2318 {
2319     ScRefreshTimerProtector aProt( m_aDocument.GetRefreshTimerControlAddress() );
2320 
2321     //  #i6500# don't call DoEnterHandler here (doesn't work with AutoSave),
2322     //  it's already in ExecuteSave (as for Save and SaveAs)
2323 
2324     if (m_pAutoStyleList)
2325         m_pAutoStyleList->ExecuteAllNow(); // Execute template timeouts now
2326     if (GetCreateMode()== SfxObjectCreateMode::STANDARD)
2327         SfxObjectShell::SetVisArea( tools::Rectangle() ); // Edited normally -> no VisArea
2328 
2329     OSL_ENSURE( rMed.GetFilter(), "Filter == 0" );
2330 
2331     bool bRet = false;
2332     OUString aFltName = rMed.GetFilter()->GetFilterName();
2333 
2334     if (aFltName == pFilterXML)
2335     {
2336         //TODO/LATER: this shouldn't happen!
2337         OSL_FAIL("XML filter in ConvertFrom?!");
2338         bRet = SaveXML( &rMed, nullptr );
2339     }
2340     else if (aFltName == pFilterExcel5 || aFltName == pFilterExcel95 ||
2341              aFltName == pFilterExcel97 || aFltName == pFilterEx5Temp ||
2342              aFltName == pFilterEx95Temp || aFltName == pFilterEx97Temp)
2343     {
2344         WaitObject aWait( GetActiveDialogParent() );
2345 
2346         bool bDoSave = true;
2347         if( ScTabViewShell* pViewShell = GetBestViewShell() )
2348         {
2349             ScExtDocOptions* pExtDocOpt = m_aDocument.GetExtDocOptions();
2350             if( !pExtDocOpt )
2351             {
2352                 m_aDocument.SetExtDocOptions( o3tl::make_unique<ScExtDocOptions>() );
2353                 pExtDocOpt = m_aDocument.GetExtDocOptions();
2354             }
2355             pViewShell->GetViewData().WriteExtOptions( *pExtDocOpt );
2356 
2357             /*  #i104990# If the imported document contains a medium
2358                 password, determine if we can save it, otherwise ask the users
2359                 whether they want to save without it. */
2360             if( (rMed.GetFilter()->GetFilterFlags() & SfxFilterFlags::ENCRYPTION) == SfxFilterFlags::NONE )
2361             {
2362                 SfxItemSet* pItemSet = rMed.GetItemSet();
2363                 const SfxPoolItem* pItem = nullptr;
2364                 if( pItemSet && pItemSet->GetItemState( SID_PASSWORD, true, &pItem ) == SfxItemState::SET )
2365                 {
2366                     bDoSave = ScWarnPassword::WarningOnPassword( rMed );
2367                     // #i42858# remove password from medium (warn only one time)
2368                     if( bDoSave )
2369                         pItemSet->ClearItem( SID_PASSWORD );
2370                 }
2371             }
2372 
2373             if( bDoSave )
2374             {
2375                 bool bNeedRetypePassDlg = ScPassHashHelper::needsPassHashRegen( m_aDocument, PASSHASH_XL );
2376                 bDoSave = !bNeedRetypePassDlg || pViewShell->ExecuteRetypePassDlg( PASSHASH_XL );
2377             }
2378         }
2379 
2380         if( bDoSave )
2381         {
2382             ExportFormatExcel eFormat = ExpBiff5;
2383             if( aFltName == pFilterExcel97 || aFltName == pFilterEx97Temp )
2384                 eFormat = ExpBiff8;
2385             ErrCode eError = ScFormatFilter::Get().ScExportExcel5( rMed, &m_aDocument, eFormat, RTL_TEXTENCODING_MS_1252 );
2386 
2387             if( eError && !GetError() )
2388                 SetError(eError);
2389 
2390             // don't return false for warnings
2391             bRet = eError.IsWarning() || (eError == ERRCODE_NONE);
2392         }
2393         else
2394         {
2395             // export aborted, i.e. "Save without password" warning
2396             SetError(ERRCODE_ABORT);
2397         }
2398     }
2399     else if (aFltName == pFilterAscii)
2400     {
2401         SvStream* pStream = rMed.GetOutStream();
2402         if (pStream)
2403         {
2404             OUString sItStr;
2405             SfxItemSet*  pSet = rMed.GetItemSet();
2406             const SfxPoolItem* pItem;
2407             if ( pSet && SfxItemState::SET ==
2408                  pSet->GetItemState( SID_FILE_FILTEROPTIONS, true, &pItem ) )
2409             {
2410                 sItStr = static_cast<const SfxStringItem*>(pItem)->GetValue();
2411             }
2412 
2413             if ( sItStr.isEmpty() )
2414             {
2415                 //  default for ascii export (from API without options):
2416                 //  ISO8859-1/MS_1252 encoding, comma, double quotes
2417 
2418                 ScImportOptions aDefOptions( ',', '"', RTL_TEXTENCODING_MS_1252 );
2419                 sItStr = aDefOptions.BuildString();
2420             }
2421 
2422             WaitObject aWait( GetActiveDialogParent() );
2423             ScImportOptions aOptions( sItStr );
2424             AsciiSave( *pStream, aOptions );
2425             bRet = true;
2426 
2427             if (m_aDocument.GetTableCount() > 1)
2428                 if (!rMed.GetError())
2429                     rMed.SetError(SCWARN_EXPORT_ASCII);
2430         }
2431     }
2432     else if (aFltName == pFilterDBase)
2433     {
2434         OUString sCharSet;
2435         SfxItemSet* pSet = rMed.GetItemSet();
2436         const SfxPoolItem* pItem;
2437         if ( pSet && SfxItemState::SET ==
2438              pSet->GetItemState( SID_FILE_FILTEROPTIONS, true, &pItem ) )
2439         {
2440             sCharSet = static_cast<const SfxStringItem*>(pItem)->GetValue();
2441         }
2442 
2443         if (sCharSet.isEmpty())
2444         {
2445             //  default for dBase export (from API without options):
2446             //  IBM_850 encoding
2447 
2448             sCharSet = ScGlobal::GetCharsetString( RTL_TEXTENCODING_IBM_850 );
2449         }
2450 
2451         WaitObject aWait( GetActiveDialogParent() );
2452         // FIXME:  Hack so that the Sba opened TempFile can be overwritten
2453         rMed.CloseOutStream();
2454         bool bHasMemo = false;
2455 
2456         ErrCode eError = DBaseExport(
2457             rMed.GetPhysicalName(), ScGlobal::GetCharsetValue(sCharSet), bHasMemo);
2458 
2459         if ( eError != ERRCODE_NONE && eError.IsWarning() )
2460         {
2461             eError = ERRCODE_NONE;
2462         }
2463 
2464         INetURLObject aTmpFile( rMed.GetPhysicalName(), INetProtocol::File );
2465         if ( bHasMemo )
2466             aTmpFile.setExtension("dbt");
2467         if ( eError != ERRCODE_NONE )
2468         {
2469             if (!GetError())
2470                 SetError(eError);
2471             if ( bHasMemo && IsDocument( aTmpFile ) )
2472                 KillFile( aTmpFile );
2473         }
2474         else
2475         {
2476             bRet = true;
2477             if ( bHasMemo )
2478             {
2479                 const SfxStringItem* pNameItem = rMed.GetItemSet()->GetItem<SfxStringItem>( SID_FILE_NAME );
2480                 INetURLObject aDbtFile( pNameItem->GetValue(), INetProtocol::File );
2481                 aDbtFile.setExtension("dbt");
2482 
2483                 // tdf#40713: don't lose dbt file
2484                 // if aDbtFile corresponds exactly to aTmpFile, we just have to return
2485                 if (aDbtFile.GetMainURL( INetURLObject::DecodeMechanism::Unambiguous ) == aTmpFile.GetMainURL( INetURLObject::DecodeMechanism::Unambiguous ))
2486                     return bRet;
2487 
2488                 if ( IsDocument( aDbtFile ) && !KillFile( aDbtFile ) )
2489                     bRet = false;
2490                 if ( bRet && !MoveFile( aTmpFile, aDbtFile ) )
2491                     bRet = false;
2492                 if ( !bRet )
2493                 {
2494                     KillFile( aTmpFile );
2495                     if ( !GetError() )
2496                         SetError(SCERR_EXPORT_DATA);
2497                 }
2498             }
2499         }
2500     }
2501     else if (aFltName == pFilterDif)
2502     {
2503         SvStream* pStream = rMed.GetOutStream();
2504         if (pStream)
2505         {
2506             OUString sItStr;
2507             SfxItemSet*  pSet = rMed.GetItemSet();
2508             const SfxPoolItem* pItem;
2509             if ( pSet && SfxItemState::SET ==
2510                  pSet->GetItemState( SID_FILE_FILTEROPTIONS, true, &pItem ) )
2511             {
2512                 sItStr = static_cast<const SfxStringItem*>(pItem)->GetValue();
2513             }
2514 
2515             if (sItStr.isEmpty())
2516             {
2517                 //  default for DIF export (from API without options):
2518                 //  ISO8859-1/MS_1252 encoding
2519 
2520                 sItStr = ScGlobal::GetCharsetString( RTL_TEXTENCODING_MS_1252 );
2521             }
2522 
2523             WaitObject aWait( GetActiveDialogParent() );
2524             ScFormatFilter::Get().ScExportDif( *pStream, &m_aDocument, ScAddress(0,0,0),
2525                 ScGlobal::GetCharsetValue(sItStr) );
2526             bRet = true;
2527 
2528             if (m_aDocument.GetTableCount() > 1)
2529                 if (!rMed.GetError())
2530                     rMed.SetError(SCWARN_EXPORT_ASCII);
2531         }
2532     }
2533     else if (aFltName == pFilterSylk)
2534     {
2535         SvStream* pStream = rMed.GetOutStream();
2536         if ( pStream )
2537         {
2538             WaitObject aWait( GetActiveDialogParent() );
2539 
2540             SCCOL nEndCol;
2541             SCROW nEndRow;
2542             m_aDocument.GetCellArea( 0, nEndCol, nEndRow );
2543             ScRange aRange( 0,0,0, nEndCol,nEndRow,0 );
2544 
2545             ScImportExport aImExport( &m_aDocument, aRange );
2546             aImExport.SetFormulas( true );
2547             bRet = aImExport.ExportStream( *pStream, rMed.GetBaseURL( true ), SotClipboardFormatId::SYLK );
2548         }
2549     }
2550     else if (aFltName == pFilterHtml)
2551     {
2552         SvStream* pStream = rMed.GetOutStream();
2553         if ( pStream )
2554         {
2555             SfxItemSet* pSet = rMed.GetItemSet();
2556             const SfxPoolItem* pItem;
2557             OUString sFilterOptions;
2558 
2559             if (pSet->GetItemState(SID_FILE_FILTEROPTIONS, true, &pItem) == SfxItemState::SET)
2560                 sFilterOptions = static_cast<const SfxStringItem*>(pItem)->GetValue();
2561 
2562             WaitObject aWait(GetActiveDialogParent());
2563             ScImportExport aImExport(&m_aDocument);
2564             aImExport.SetStreamPath(rMed.GetName());
2565             aImExport.SetFilterOptions(sFilterOptions);
2566             bRet = aImExport.ExportStream(*pStream, rMed.GetBaseURL(true), SotClipboardFormatId::HTML);
2567             if (bRet && !aImExport.GetNonConvertibleChars().isEmpty())
2568             {
2569                 SetError(*new StringErrorInfo(
2570                     SCWARN_EXPORT_NONCONVERTIBLE_CHARS,
2571                     aImExport.GetNonConvertibleChars(),
2572                     DialogMask::ButtonsOk | DialogMask::MessageInfo));
2573             }
2574         }
2575     }
2576     else
2577     {
2578         if (GetError())
2579             SetError(SCERR_IMPORT_NI);
2580     }
2581     return bRet;
2582 }
2583 
2584 bool ScDocShell::DoSaveCompleted( SfxMedium * pNewStor, bool bRegisterRecent )
2585 {
2586     bool bRet = SfxObjectShell::DoSaveCompleted( pNewStor, bRegisterRecent );
2587 
2588     //  SfxHintId::ScDocSaved for change ReadOnly -> Read/Write
2589     Broadcast( SfxHint( SfxHintId::ScDocSaved ) );
2590     return bRet;
2591 }
2592 
2593 bool ScDocShell::QuerySlotExecutable( sal_uInt16 nSlotId )
2594 {
2595     // #i112634# ask VBA event handlers whether to save or print the document
2596 
2597     using namespace ::com::sun::star::script::vba;
2598 
2599     sal_Int32 nVbaEventId = VBAEventId::NO_EVENT;
2600     uno::Sequence< uno::Any > aArgs;
2601     switch( nSlotId )
2602     {
2603         case SID_SAVEDOC:
2604         case SID_SAVEASDOC:
2605             nVbaEventId = VBAEventId::WORKBOOK_BEFORESAVE;
2606             aArgs.realloc( 1 );
2607             aArgs[ 0 ] <<= (nSlotId == SID_SAVEASDOC);
2608         break;
2609         case SID_PRINTDOC:
2610         case SID_PRINTDOCDIRECT:
2611             nVbaEventId = VBAEventId::WORKBOOK_BEFOREPRINT;
2612         break;
2613     }
2614 
2615     bool bSlotExecutable = true;
2616     if( nVbaEventId != VBAEventId::NO_EVENT ) try
2617     {
2618         uno::Reference< XVBAEventProcessor > xEventProcessor( m_aDocument.GetVbaEventProcessor(), uno::UNO_QUERY_THROW );
2619         xEventProcessor->processVbaEvent( nVbaEventId, aArgs );
2620     }
2621     catch( util::VetoException& )
2622     {
2623         bSlotExecutable = false;
2624     }
2625     catch( uno::Exception& )
2626     {
2627     }
2628     return bSlotExecutable;
2629 }
2630 
2631 bool ScDocShell::PrepareClose( bool bUI )
2632 {
2633     if(SC_MOD()->GetCurRefDlgId()>0)
2634     {
2635         SfxViewFrame* pFrame = SfxViewFrame::GetFirst( this );
2636         if( pFrame )
2637         {
2638             SfxViewShell* p = pFrame->GetViewShell();
2639             ScTabViewShell* pViewSh = dynamic_cast< ScTabViewShell *>( p );
2640             if(pViewSh!=nullptr)
2641             {
2642                 vcl::Window *pWin=pViewSh->GetWindow();
2643                 if(pWin!=nullptr) pWin->GrabFocus();
2644             }
2645         }
2646 
2647         return false;
2648     }
2649     if ( m_aDocument.IsInLinkUpdate() || m_aDocument.IsInInterpreter() )
2650     {
2651         ErrorMessage(STR_CLOSE_ERROR_LINK);
2652         return false;
2653     }
2654 
2655     DoEnterHandler();
2656 
2657     // start 'Workbook_BeforeClose' VBA event handler for possible veto
2658     if( !IsInPrepareClose() )
2659     {
2660         try
2661         {
2662             uno::Reference< script::vba::XVBAEventProcessor > xVbaEvents( m_aDocument.GetVbaEventProcessor(), uno::UNO_SET_THROW );
2663             uno::Sequence< uno::Any > aArgs;
2664             xVbaEvents->processVbaEvent( script::vba::VBAEventId::WORKBOOK_BEFORECLOSE, aArgs );
2665         }
2666         catch( util::VetoException& )
2667         {
2668             // if event processor throws VetoException, macro has vetoed close
2669             return false;
2670         }
2671         catch( uno::Exception& )
2672         {
2673         }
2674     }
2675     // end handler code
2676 
2677     bool bRet = SfxObjectShell::PrepareClose( bUI );
2678     if (bRet) // true == close
2679         m_aDocument.EnableIdle(false); // Do not mess around with it anymore!
2680 
2681     return bRet;
2682 }
2683 
2684 OUString ScDocShell::GetOwnFilterName()
2685 {
2686     return OUString(pFilterSc50);
2687 }
2688 
2689 OUString ScDocShell::GetHtmlFilterName()
2690 {
2691     return OUString(pFilterHtml);
2692 }
2693 
2694 OUString ScDocShell::GetWebQueryFilterName()
2695 {
2696     return OUString(pFilterHtmlWebQ);
2697 }
2698 
2699 OUString ScDocShell::GetAsciiFilterName()
2700 {
2701     return OUString(pFilterAscii);
2702 }
2703 
2704 OUString ScDocShell::GetLotusFilterName()
2705 {
2706     return OUString(pFilterLotus);
2707 }
2708 
2709 OUString ScDocShell::GetDBaseFilterName()
2710 {
2711     return OUString(pFilterDBase);
2712 }
2713 
2714 OUString ScDocShell::GetDifFilterName()
2715 {
2716     return OUString(pFilterDif);
2717 }
2718 
2719 bool ScDocShell::HasAutomaticTableName( const OUString& rFilter )
2720 {
2721     //  sal_True for those filters that keep the default table name
2722     //  (which is language specific)
2723 
2724     return rFilter == pFilterAscii
2725         || rFilter == pFilterLotus
2726         || rFilter == pFilterExcel4
2727         || rFilter == pFilterEx4Temp
2728         || rFilter == pFilterDBase
2729         || rFilter == pFilterDif
2730         || rFilter == pFilterSylk
2731         || rFilter == pFilterHtml
2732         || rFilter == pFilterRtf;
2733 }
2734 
2735 std::unique_ptr<ScDocFunc> ScDocShell::CreateDocFunc()
2736 {
2737     return o3tl::make_unique<ScDocFuncDirect>( *this );
2738 }
2739 
2740 ScDocument* ScDocShell::GetClipDoc()
2741 {
2742     vcl::Window* pWin = nullptr;
2743     if (ScTabViewShell* pViewShell = GetBestViewShell())
2744         pWin = pViewShell->GetViewData().GetActiveWin();
2745 
2746     const ScTransferObj* pObj = ScTransferObj::GetOwnClipboard(ScTabViewShell::GetClipData(pWin));
2747     if (pObj)
2748     {
2749         ScDocument* pDoc = pObj->GetDocument();
2750         assert((!pDoc || pDoc->IsClipboard()) && "Document is not clipboard, how can that be?");
2751         return pDoc;
2752     }
2753 
2754     return nullptr;
2755 }
2756 
2757 ScDocShell::ScDocShell( const SfxModelFlags i_nSfxCreationFlags ) :
2758     SfxObjectShell( i_nSfxCreationFlags ),
2759     m_aDocument       ( SCDOCMODE_DOCUMENT, this ),
2760     m_aDdeTextFmt(OUString("TEXT")),
2761     m_nPrtToScreenFactor( 1.0 ),
2762     m_pImpl           ( new DocShell_Impl ),
2763     m_bHeaderOn       ( true ),
2764     m_bFooterOn       ( true ),
2765     m_bIsEmpty        ( true ),
2766     m_bIsInUndo       ( false ),
2767     m_bDocumentModifiedPending( false ),
2768     m_bUpdateEnabled  ( true ),
2769     m_bUcalcTest     ( false ),
2770     m_nDocumentLock   ( 0 ),
2771     m_nCanUpdate (css::document::UpdateDocMode::ACCORDING_TO_CONFIG)
2772 {
2773     SetPool( &SC_MOD()->GetPool() );
2774 
2775     m_bIsInplace = (GetCreateMode() == SfxObjectCreateMode::EMBEDDED);
2776     //  Will be reset if not in place
2777 
2778     m_pDocFunc = CreateDocFunc();
2779 
2780     //  SetBaseModel needs exception handling
2781     ScModelObj::CreateAndSet( this );
2782 
2783     StartListening(*this);
2784     SfxStyleSheetPool* pStlPool = m_aDocument.GetStyleSheetPool();
2785     if (pStlPool)
2786         StartListening(*pStlPool);
2787 
2788     m_aDocument.GetDBCollection()->SetRefreshHandler(
2789         LINK( this, ScDocShell, RefreshDBDataHdl ) );
2790 
2791     // InitItems and CalcOutputFactor are called now in Load/ConvertFrom/InitNew
2792 }
2793 
2794 ScDocShell::~ScDocShell()
2795 {
2796     ResetDrawObjectShell(); // If the Drawing Layer still tries to access it, access it
2797 
2798     SfxStyleSheetPool* pStlPool = m_aDocument.GetStyleSheetPool();
2799     if (pStlPool)
2800         EndListening(*pStlPool);
2801     EndListening(*this);
2802 
2803     m_pAutoStyleList.reset();
2804 
2805     SfxApplication *pSfxApp = SfxGetpApp();
2806     if ( pSfxApp->GetDdeService() ) // Delete DDE for Document
2807         pSfxApp->RemoveDdeTopic( this );
2808 
2809     m_pDocFunc.reset();
2810     delete m_aDocument.mpUndoManager;
2811     m_aDocument.mpUndoManager = nullptr;
2812     m_pImpl.reset();
2813 
2814     m_pPaintLockData.reset();
2815 
2816     m_pSolverSaveData.reset();
2817     m_pSheetSaveData.reset();
2818     m_pFormatSaveData.reset();
2819     m_pOldAutoDBRange.reset();
2820 
2821     if (m_pModificator)
2822     {
2823         OSL_FAIL("The Modificator should not exist");
2824         m_pModificator.reset();
2825     }
2826 }
2827 
2828 SfxUndoManager* ScDocShell::GetUndoManager()
2829 {
2830     return m_aDocument.GetUndoManager();
2831 }
2832 
2833 void ScDocShell::SetModified( bool bModified )
2834 {
2835     if ( SfxObjectShell::IsEnableSetModified() )
2836     {
2837         SfxObjectShell::SetModified( bModified );
2838         Broadcast( SfxHint( SfxHintId::DocChanged ) );
2839     }
2840 }
2841 
2842 void ScDocShell::SetDocumentModified()
2843 {
2844     //  BroadcastUno must also happen right away with pPaintLockData
2845     //  FIXME: Also for SetDrawModified, if Drawing is connected
2846     //  FIXME: Then own Hint?
2847 
2848     if ( m_pPaintLockData )
2849     {
2850         // #i115009# broadcast BCA_BRDCST_ALWAYS, so a component can read recalculated results
2851         // of RecalcModeAlways formulas (like OFFSET) after modifying cells
2852         m_aDocument.Broadcast(ScHint(SfxHintId::ScDataChanged, BCA_BRDCST_ALWAYS));
2853         m_aDocument.InvalidateTableArea();    // #i105279# needed here
2854         m_aDocument.BroadcastUno( SfxHint( SfxHintId::DataChanged ) );
2855 
2856         m_pPaintLockData->SetModified(); // Later on ...
2857         return;
2858     }
2859 
2860     SetDrawModified();
2861 
2862     if ( m_aDocument.IsAutoCalcShellDisabled() )
2863         SetDocumentModifiedPending( true );
2864     else
2865     {
2866         SetDocumentModifiedPending( false );
2867         m_aDocument.InvalidateStyleSheetUsage();
2868         m_aDocument.InvalidateTableArea();
2869         m_aDocument.InvalidateLastTableOpParams();
2870         m_aDocument.Broadcast(ScHint(SfxHintId::ScDataChanged, BCA_BRDCST_ALWAYS));
2871         if ( m_aDocument.IsForcedFormulaPending() && m_aDocument.GetAutoCalc() )
2872             m_aDocument.CalcFormulaTree( true );
2873         m_aDocument.RefreshDirtyTableColumnNames();
2874         PostDataChanged();
2875 
2876         //  Detective AutoUpdate:
2877         //  Update if formulas were modified (DetectiveDirty) or the list contains
2878         //  "Trace Error" entries (Trace Error can look completely different
2879         //  after changes to non-formula cells).
2880 
2881         ScDetOpList* pList = m_aDocument.GetDetOpList();
2882         if ( pList && ( m_aDocument.IsDetectiveDirty() || pList->HasAddError() ) &&
2883              pList->Count() && !IsInUndo() && SC_MOD()->GetAppOptions().GetDetectiveAuto() )
2884         {
2885             GetDocFunc().DetectiveRefresh(true);    // sal_True = caused by automatic update
2886         }
2887         m_aDocument.SetDetectiveDirty(false);         // always reset, also if not refreshed
2888     }
2889 
2890     // notify UNO objects after BCA_BRDCST_ALWAYS etc.
2891     m_aDocument.BroadcastUno( SfxHint( SfxHintId::DataChanged ) );
2892 }
2893 
2894 /**
2895  * SetDrawModified - without Formula update
2896  *
2897  * Drawing also needs to be updated for the normal SetDocumentModified
2898  * e.g.: when deleting tables etc.
2899  */
2900 void ScDocShell::SetDrawModified()
2901 {
2902     bool bUpdate = !IsModified();
2903 
2904     SetModified();
2905 
2906     SfxBindings* pBindings = GetViewBindings();
2907     if (bUpdate && pBindings)
2908     {
2909         pBindings->Invalidate( SID_SAVEDOC );
2910         pBindings->Invalidate( SID_DOC_MODIFIED );
2911     }
2912 
2913     if (pBindings)
2914     {
2915         // #i105960# Undo etc used to be volatile.
2916         // They always have to be invalidated, including drawing layer or row height changes
2917         // (but not while pPaintLockData is set).
2918         pBindings->Invalidate( SID_UNDO );
2919         pBindings->Invalidate( SID_REDO );
2920         pBindings->Invalidate( SID_REPEAT );
2921     }
2922 
2923     if ( m_aDocument.IsChartListenerCollectionNeedsUpdate() )
2924     {
2925         m_aDocument.UpdateChartListenerCollection();
2926         SfxGetpApp()->Broadcast(SfxHint( SfxHintId::ScDrawChanged ));    // Navigator
2927     }
2928     SC_MOD()->AnythingChanged();
2929 }
2930 
2931 void ScDocShell::SetInUndo(bool bSet)
2932 {
2933     m_bIsInUndo = bSet;
2934 }
2935 
2936 void ScDocShell::GetDocStat( ScDocStat& rDocStat )
2937 {
2938     SfxPrinter* pPrinter = GetPrinter();
2939 
2940     m_aDocument.GetDocStat( rDocStat );
2941     rDocStat.nPageCount = 0;
2942 
2943     if ( pPrinter )
2944         for ( SCTAB i=0; i<rDocStat.nTableCount; i++ )
2945             rDocStat.nPageCount = sal::static_int_cast<sal_uInt16>( rDocStat.nPageCount +
2946                 static_cast<sal_uInt16>(ScPrintFunc( this, pPrinter, i ).GetTotalPages()) );
2947 }
2948 
2949 VclPtr<SfxDocumentInfoDialog> ScDocShell::CreateDocumentInfoDialog( const SfxItemSet &rSet )
2950 {
2951     VclPtr<SfxDocumentInfoDialog> pDlg   = VclPtr<SfxDocumentInfoDialog>::Create( nullptr, rSet );
2952     ScDocShell*            pDocSh = dynamic_cast< ScDocShell *>( SfxObjectShell::Current() );
2953 
2954     // Only for statistics, if this Doc is shown; not from the Doc Manager
2955     if( pDocSh == this )
2956     {
2957         ScAbstractDialogFactory* pFact = ScAbstractDialogFactory::Create();
2958         ::CreateTabPage ScDocStatPageCreate = pFact->GetTabPageCreatorFunc(SID_SC_TP_STAT);
2959         OSL_ENSURE(ScDocStatPageCreate, "Tabpage create fail!");
2960         pDlg->AddFontTabPage();
2961         pDlg->AddTabPage( 42,
2962             ScResId( STR_DOC_STAT ),
2963             ScDocStatPageCreate);
2964     }
2965     return pDlg;
2966 }
2967 
2968 vcl::Window* ScDocShell::GetActiveDialogParent()
2969 {
2970     ScTabViewShell* pViewSh = ScTabViewShell::GetActiveViewShell();
2971     if ( pViewSh )
2972         return pViewSh->GetDialogParent();
2973     else
2974         return Application::GetDefDialogParent();
2975 }
2976 
2977 void ScDocShell::SetSolverSaveData( std::unique_ptr<ScOptSolverSave> pData )
2978 {
2979     m_pSolverSaveData = std::move(pData);
2980 }
2981 
2982 ScSheetSaveData* ScDocShell::GetSheetSaveData()
2983 {
2984     if (!m_pSheetSaveData)
2985         m_pSheetSaveData.reset( new ScSheetSaveData );
2986 
2987     return m_pSheetSaveData.get();
2988 }
2989 
2990 ScFormatSaveData* ScDocShell::GetFormatSaveData()
2991 {
2992     if (!m_pFormatSaveData)
2993         m_pFormatSaveData.reset( new ScFormatSaveData );
2994 
2995     return m_pFormatSaveData.get();
2996 }
2997 
2998 namespace {
2999 
3000 void removeKeysIfExists(const Reference<ui::XAcceleratorConfiguration>& xScAccel, const vector<const awt::KeyEvent*>& rKeys)
3001 {
3002     vector<const awt::KeyEvent*>::const_iterator itr = rKeys.begin(), itrEnd = rKeys.end();
3003     for (; itr != itrEnd; ++itr)
3004     {
3005         const awt::KeyEvent* p = *itr;
3006         if (!p)
3007             continue;
3008 
3009         try
3010         {
3011             xScAccel->removeKeyEvent(*p);
3012         }
3013         catch (const container::NoSuchElementException&) {}
3014     }
3015 }
3016 
3017 }
3018 
3019 void ScDocShell::ResetKeyBindings( ScOptionsUtil::KeyBindingType eType )
3020 {
3021     using namespace ::com::sun::star::ui;
3022 
3023     Reference<uno::XComponentContext> xContext = ::comphelper::getProcessComponentContext();
3024     if (!xContext.is())
3025         return;
3026 
3027     Reference<XModuleUIConfigurationManagerSupplier> xModuleCfgSupplier(
3028         theModuleUIConfigurationManagerSupplier::get(xContext) );
3029 
3030     // Grab the Calc configuration.
3031     Reference<XUIConfigurationManager> xConfigMgr =
3032         xModuleCfgSupplier->getUIConfigurationManager(
3033             "com.sun.star.sheet.SpreadsheetDocument");
3034 
3035     if (!xConfigMgr.is())
3036         return;
3037 
3038     // shortcut manager
3039     Reference<XAcceleratorConfiguration> xScAccel = xConfigMgr->getShortCutManager();
3040 
3041     if (!xScAccel.is())
3042         return;
3043 
3044     vector<const awt::KeyEvent*> aKeys;
3045     aKeys.reserve(9);
3046 
3047     // Backspace key
3048     awt::KeyEvent aBackspace;
3049     aBackspace.KeyCode = awt::Key::BACKSPACE;
3050     aBackspace.Modifiers = 0;
3051     aKeys.push_back(&aBackspace);
3052 
3053     // Delete key
3054     awt::KeyEvent aDelete;
3055     aDelete.KeyCode = awt::Key::DELETE;
3056     aDelete.Modifiers = 0;
3057     aKeys.push_back(&aDelete);
3058 
3059     // Ctrl-D
3060     awt::KeyEvent aCtrlD;
3061     aCtrlD.KeyCode = awt::Key::D;
3062     aCtrlD.Modifiers = awt::KeyModifier::MOD1;
3063     aKeys.push_back(&aCtrlD);
3064 
3065     // Alt-Down
3066     awt::KeyEvent aAltDown;
3067     aAltDown.KeyCode = awt::Key::DOWN;
3068     aAltDown.Modifiers = awt::KeyModifier::MOD2;
3069     aKeys.push_back(&aAltDown);
3070 
3071     // Ctrl-Space
3072     awt::KeyEvent aCtrlSpace;
3073     aCtrlSpace.KeyCode = awt::Key::SPACE;
3074     aCtrlSpace.Modifiers = awt::KeyModifier::MOD1;
3075     aKeys.push_back(&aCtrlSpace);
3076 
3077     // Ctrl-Shift-Space
3078     awt::KeyEvent aCtrlShiftSpace;
3079     aCtrlShiftSpace.KeyCode = awt::Key::SPACE;
3080     aCtrlShiftSpace.Modifiers = awt::KeyModifier::MOD1 | awt::KeyModifier::SHIFT;
3081     aKeys.push_back(&aCtrlShiftSpace);
3082 
3083     // F4
3084     awt::KeyEvent aF4;
3085     aF4.KeyCode = awt::Key::F4;
3086     aF4.Modifiers = 0;
3087     aKeys.push_back(&aF4);
3088 
3089     // CTRL+SHIFT+F4
3090     awt::KeyEvent aCtrlShiftF4;
3091     aCtrlShiftF4.KeyCode = awt::Key::F4;
3092     aCtrlShiftF4.Modifiers = awt::KeyModifier::MOD1 | awt::KeyModifier::SHIFT;
3093     aKeys.push_back(&aCtrlShiftF4);
3094 
3095     // SHIFT+F4
3096     awt::KeyEvent aShiftF4;
3097     aShiftF4.KeyCode = awt::Key::F4;
3098     aShiftF4.Modifiers = awt::KeyModifier::SHIFT;
3099     aKeys.push_back(&aShiftF4);
3100 
3101     // Remove all involved keys first, because swapping commands don't work
3102     // well without doing this.
3103     removeKeysIfExists(xScAccel, aKeys);
3104     xScAccel->store();
3105 
3106     switch (eType)
3107     {
3108         case ScOptionsUtil::KEY_DEFAULT:
3109             xScAccel->setKeyEvent(aDelete, ".uno:ClearContents");
3110             xScAccel->setKeyEvent(aBackspace, ".uno:Delete");
3111             xScAccel->setKeyEvent(aCtrlD, ".uno:FillDown");
3112             xScAccel->setKeyEvent(aAltDown, ".uno:DataSelect");
3113             xScAccel->setKeyEvent(aCtrlSpace, ".uno:SelectColumn");
3114             xScAccel->setKeyEvent(aCtrlShiftSpace, ".uno:SelectAll");
3115             xScAccel->setKeyEvent(aF4, ".uno:ToggleRelative");
3116             xScAccel->setKeyEvent(aCtrlShiftF4, ".uno:ViewDataSourceBrowser");
3117         break;
3118         case ScOptionsUtil::KEY_OOO_LEGACY:
3119             xScAccel->setKeyEvent(aDelete, ".uno:Delete");
3120             xScAccel->setKeyEvent(aBackspace, ".uno:ClearContents");
3121             xScAccel->setKeyEvent(aCtrlD, ".uno:DataSelect");
3122             xScAccel->setKeyEvent(aCtrlShiftSpace, ".uno:SelectColumn");
3123             xScAccel->setKeyEvent(aF4, ".uno:ViewDataSourceBrowser");
3124             xScAccel->setKeyEvent(aShiftF4, ".uno:ToggleRelative");
3125         break;
3126         default:
3127             ;
3128     }
3129 
3130     xScAccel->store();
3131 }
3132 
3133 void ScDocShell::UseSheetSaveEntries()
3134 {
3135     if (m_pSheetSaveData)
3136     {
3137         m_pSheetSaveData->UseSaveEntries();   // use positions from saved file for next saving
3138 
3139         bool bHasEntries = false;
3140         SCTAB nTabCount = m_aDocument.GetTableCount();
3141         SCTAB nTab;
3142         for (nTab = 0; nTab < nTabCount; ++nTab)
3143             if (m_pSheetSaveData->HasStreamPos(nTab))
3144                 bHasEntries = true;
3145 
3146         if (!bHasEntries)
3147         {
3148             // if no positions were set (for example, export to other format),
3149             // reset all "valid" flags
3150             for (nTab = 0; nTab < nTabCount; ++nTab)
3151                 m_aDocument.SetStreamValid(nTab, false);
3152         }
3153     }
3154 }
3155 
3156 // --- ScDocShellModificator ------------------------------------------
3157 
3158 ScDocShellModificator::ScDocShellModificator( ScDocShell& rDS )
3159         :
3160         rDocShell( rDS ),
3161         mpProtector(new ScRefreshTimerProtector(rDS.GetDocument().GetRefreshTimerControlAddress()))
3162 {
3163     ScDocument& rDoc = rDocShell.GetDocument();
3164     bAutoCalcShellDisabled = rDoc.IsAutoCalcShellDisabled();
3165     bIdleEnabled = rDoc.IsIdleEnabled();
3166     rDoc.SetAutoCalcShellDisabled( true );
3167     rDoc.EnableIdle(false);
3168 }
3169 
3170 ScDocShellModificator::~ScDocShellModificator() COVERITY_NOEXCEPT_FALSE
3171 {
3172     ScDocument& rDoc = rDocShell.GetDocument();
3173     rDoc.SetAutoCalcShellDisabled( bAutoCalcShellDisabled );
3174     if ( !bAutoCalcShellDisabled && rDocShell.IsDocumentModifiedPending() )
3175         rDocShell.SetDocumentModified();    // last one shuts off the lights
3176     rDoc.EnableIdle(bIdleEnabled);
3177 }
3178 
3179 void ScDocShellModificator::SetDocumentModified()
3180 {
3181     ScDocument& rDoc = rDocShell.GetDocument();
3182     rDoc.PrepareFormulaCalc();
3183     if ( !rDoc.IsImportingXML() )
3184     {
3185         // temporarily restore AutoCalcShellDisabled
3186         bool bDisabled = rDoc.IsAutoCalcShellDisabled();
3187         rDoc.SetAutoCalcShellDisabled( bAutoCalcShellDisabled );
3188         rDocShell.SetDocumentModified();
3189         rDoc.SetAutoCalcShellDisabled( bDisabled );
3190     }
3191     else
3192     {
3193         // uno broadcast is necessary for api to work
3194         // -> must also be done during xml import
3195         rDoc.BroadcastUno( SfxHint( SfxHintId::DataChanged ) );
3196     }
3197 }
3198 
3199 bool ScDocShell::IsChangeRecording() const
3200 {
3201     ScChangeTrack* pChangeTrack = m_aDocument.GetChangeTrack();
3202     return pChangeTrack != nullptr;
3203 }
3204 
3205 bool ScDocShell::HasChangeRecordProtection() const
3206 {
3207     bool bRes = false;
3208     ScChangeTrack* pChangeTrack = m_aDocument.GetChangeTrack();
3209     if (pChangeTrack)
3210         bRes = pChangeTrack->IsProtected();
3211     return bRes;
3212 }
3213 
3214 void ScDocShell::SetChangeRecording( bool bActivate )
3215 {
3216     bool bOldChangeRecording = IsChangeRecording();
3217 
3218     if (bActivate)
3219     {
3220         m_aDocument.StartChangeTracking();
3221         ScChangeViewSettings aChangeViewSet;
3222         aChangeViewSet.SetShowChanges(true);
3223         m_aDocument.SetChangeViewSettings(aChangeViewSet);
3224     }
3225     else
3226     {
3227         m_aDocument.EndChangeTracking();
3228         PostPaintGridAll();
3229     }
3230 
3231     if (bOldChangeRecording != IsChangeRecording())
3232     {
3233         UpdateAcceptChangesDialog();
3234         // invalidate slots
3235         SfxBindings* pBindings = GetViewBindings();
3236         if (pBindings)
3237             pBindings->InvalidateAll(false);
3238     }
3239 }
3240 
3241 void ScDocShell::SetProtectionPassword( const OUString &rNewPassword )
3242 {
3243     ScChangeTrack* pChangeTrack = m_aDocument.GetChangeTrack();
3244     if (pChangeTrack)
3245     {
3246         bool bProtected = pChangeTrack->IsProtected();
3247 
3248         if (!rNewPassword.isEmpty())
3249         {
3250             // when password protection is applied change tracking must always be active
3251             SetChangeRecording( true );
3252 
3253             css::uno::Sequence< sal_Int8 > aProtectionHash;
3254             SvPasswordHelper::GetHashPassword( aProtectionHash, rNewPassword );
3255             pChangeTrack->SetProtection( aProtectionHash );
3256         }
3257         else
3258         {
3259             pChangeTrack->SetProtection( css::uno::Sequence< sal_Int8 >() );
3260         }
3261 
3262         if ( bProtected != pChangeTrack->IsProtected() )
3263         {
3264             UpdateAcceptChangesDialog();
3265             SetDocumentModified();
3266         }
3267     }
3268 }
3269 
3270 bool ScDocShell::GetProtectionHash( /*out*/ css::uno::Sequence< sal_Int8 > &rPasswordHash )
3271 {
3272     bool bRes = false;
3273     ScChangeTrack* pChangeTrack = m_aDocument.GetChangeTrack();
3274     if (pChangeTrack && pChangeTrack->IsProtected())
3275     {
3276         rPasswordHash = pChangeTrack->GetProtection();
3277         bRes = true;
3278     }
3279     return bRes;
3280 }
3281 
3282 void ScDocShell::SetIsInUcalc()
3283 {
3284     m_bUcalcTest = true;
3285 }
3286 
3287 void ScDocShell::RegisterAutomationWorkbookObject(css::uno::Reference< ooo::vba::excel::XWorkbook > const& xWorkbook)
3288 {
3289     mxAutomationWorkbookObject = xWorkbook;
3290 }
3291 
3292 extern "C" SAL_DLLPUBLIC_EXPORT bool TestImportSLK(SvStream &rStream)
3293 {
3294     ScDLL::Init();
3295     ScDocument aDocument;
3296     ScDocOptions aDocOpt = aDocument.GetDocOptions();
3297     aDocOpt.SetLookUpColRowNames(false);
3298     aDocument.SetDocOptions(aDocOpt);
3299     aDocument.MakeTable(0);
3300     aDocument.EnableExecuteLink(false);
3301     aDocument.SetInsertingFromOtherDoc(true);
3302     aDocument.SetImportingXML(true);
3303 
3304     ScImportExport aImpEx(&aDocument);
3305     return aImpEx.ImportStream(rStream, OUString(), SotClipboardFormatId::SYLK);
3306 }
3307 
3308 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
3309