/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This file incorporates work covered by the following license notice: * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed * with this work for additional information regarding copyright * ownership. The ASF licenses this file to you under the Apache * License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.apache.org/licenses/LICENSE-2.0 . */ #include #include #include using namespace ::com::sun::star; #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "docshimp.hxx" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void ScDocShell::SetInitialLinkUpdate( const SfxMedium* pMed ) { if (pMed) { const SfxUInt16Item* pUpdateDocItem = SfxItemSet::GetItem( pMed->GetItemSet(), SID_UPDATEDOCMODE, false); m_nCanUpdate = pUpdateDocItem ? pUpdateDocItem->GetValue() : css::document::UpdateDocMode::NO_UPDATE; } // GetLinkUpdateModeState() evaluates m_nCanUpdate so that must have // been set first. Do not override an already forbidden LinkUpdate (the // default is allow). comphelper::EmbeddedObjectContainer& rEmbeddedObjectContainer = getEmbeddedObjectContainer(); if (rEmbeddedObjectContainer.getUserAllowsLinkUpdate()) { // For anything else than LM_ALWAYS we need user confirmation. rEmbeddedObjectContainer.setUserAllowsLinkUpdate( GetLinkUpdateModeState() == LM_ALWAYS); } } ScLkUpdMode ScDocShell::GetLinkUpdateModeState() const { ScLkUpdMode nSet; if (m_nCanUpdate == css::document::UpdateDocMode::NO_UPDATE) nSet = LM_NEVER; else if (m_nCanUpdate == css::document::UpdateDocMode::FULL_UPDATE) nSet = LM_ALWAYS; else { nSet = GetDocument().GetLinkMode(); if (nSet == LM_UNKNOWN) { ScAppOptions aAppOptions = SC_MOD()->GetAppOptions(); nSet = aAppOptions.GetLinkMode(); } } if (nSet == LM_ALWAYS && !(SvtSecurityOptions::isTrustedLocationUriForUpdatingLinks( GetMedium() == nullptr ? OUString() : GetMedium()->GetName()) || (IsDocShared() && SvtSecurityOptions::isTrustedLocationUriForUpdatingLinks( GetSharedFileURL())))) { nSet = LM_ON_DEMAND; } if (m_nCanUpdate == css::document::UpdateDocMode::QUIET_UPDATE && nSet == LM_ON_DEMAND) { nSet = LM_NEVER; } return nSet; } void ScDocShell::AllowLinkUpdate() { m_pDocument->SetLinkFormulaNeedingCheck(false); getEmbeddedObjectContainer().setUserAllowsLinkUpdate(true); } void ScDocShell::ReloadAllLinks() { AllowLinkUpdate(); ReloadTabLinks(); weld::Window *pDialogParent = GetActiveDialogParent(); m_pDocument->UpdateExternalRefLinks(pDialogParent); bool bAnyDde = m_pDocument->GetDocLinkManager().updateDdeOrOleOrWebServiceLinks(pDialogParent); if (bAnyDde) { // calculate formulas and paint like in the TrackTimeHdl m_pDocument->TrackFormulas(); Broadcast(SfxHint(SfxHintId::ScDataChanged)); // Should FID_DATACHANGED become asynchronous some time // (e.g., with Invalidate at Window), an update needs to be forced here. } m_pDocument->UpdateAreaLinks(); } IMPL_LINK_NOARG( ScDocShell, ReloadAllLinksHdl, weld::Button&, void ) { ReloadAllLinks(); ScTabViewShell* pViewSh = GetBestViewShell(); SfxViewFrame* pViewFrame = pViewSh ? pViewSh->GetFrame() : nullptr; if (pViewFrame) pViewFrame->RemoveInfoBar(u"enablecontent"); SAL_WARN_IF(!pViewFrame, "sc", "expected there to be a ViewFrame"); } namespace { class LinkHelp { public: DECL_STATIC_LINK(LinkHelp, DispatchHelpLinksHdl, weld::Button&, void); }; } IMPL_STATIC_LINK(LinkHelp, DispatchHelpLinksHdl, weld::Button&, rBtn, void) { if (Help* pHelp = Application::GetHelp()) pHelp->Start(HID_UPDATE_LINK_WARNING, &rBtn); } void ScDocShell::Execute( SfxRequest& rReq ) { const SfxItemSet* pReqArgs = rReq.GetArgs(); SfxBindings* pBindings = GetViewBindings(); bool bUndo (m_pDocument->IsUndoEnabled()); sal_uInt16 nSlot = rReq.GetSlot(); switch ( nSlot ) { case SID_SC_SETTEXT: { const SfxPoolItem* pColItem; const SfxPoolItem* pRowItem; const SfxPoolItem* pTabItem; const SfxPoolItem* pTextItem; if( pReqArgs && pReqArgs->HasItem( FN_PARAM_1, &pColItem ) && pReqArgs->HasItem( FN_PARAM_2, &pRowItem ) && pReqArgs->HasItem( FN_PARAM_3, &pTabItem ) && pReqArgs->HasItem( SID_SC_SETTEXT, &pTextItem ) ) { // parameters are 1-based !!! SCCOL nCol = static_cast(pColItem)->GetValue() - 1; SCROW nRow = static_cast(pRowItem)->GetValue() - 1; SCTAB nTab = static_cast(pTabItem)->GetValue() - 1; SCTAB nTabCount = m_pDocument->GetTableCount(); if ( m_pDocument->ValidCol(nCol) && m_pDocument->ValidRow(nRow) && ValidTab(nTab,nTabCount) ) { if ( m_pDocument->IsBlockEditable( nTab, nCol,nRow, nCol, nRow ) ) { OUString aVal = static_cast(pTextItem)->GetValue(); m_pDocument->SetString( nCol, nRow, nTab, aVal ); PostPaintCell( nCol, nRow, nTab ); SetDocumentModified(); rReq.Done(); break; } else // protected cell { #if HAVE_FEATURE_SCRIPTING SbxBase::SetError( ERRCODE_BASIC_BAD_PARAMETER ); //! which error ? #endif break; } } } #if HAVE_FEATURE_SCRIPTING SbxBase::SetError( ERRCODE_BASIC_NO_OBJECT ); #endif } break; case SID_SBA_IMPORT: { if (pReqArgs) { const SfxPoolItem* pItem; svx::ODataAccessDescriptor aDesc; if ( pReqArgs->GetItemState( nSlot, true, &pItem ) == SfxItemState::SET ) { uno::Any aAny = static_cast(pItem)->GetValue(); uno::Sequence aProperties; if ( aAny >>= aProperties ) aDesc.initializeFrom( aProperties ); } OUString sTarget; if ( pReqArgs->GetItemState( FN_PARAM_1, true, &pItem ) == SfxItemState::SET ) sTarget = static_cast(pItem)->GetValue(); bool bIsNewArea = true; // Default sal_True (no inquiry) if ( pReqArgs->GetItemState( FN_PARAM_2, true, &pItem ) == SfxItemState::SET ) bIsNewArea = static_cast(pItem)->GetValue(); // if necessary, create new database area bool bMakeArea = false; if (bIsNewArea) { ScDBCollection* pDBColl = m_pDocument->GetDBCollection(); if ( !pDBColl || !pDBColl->getNamedDBs().findByUpperName(ScGlobal::getCharClass().uppercase(sTarget)) ) { ScAddress aPos; if ( aPos.Parse( sTarget, *m_pDocument, m_pDocument->GetAddressConvention() ) & ScRefFlags::VALID ) { bMakeArea = true; if (bUndo) { OUString aStrImport = ScResId( STR_UNDO_IMPORTDATA ); ViewShellId nViewShellId(-1); if (ScTabViewShell* pViewSh = ScTabViewShell::GetActiveViewShell()) nViewShellId = pViewSh->GetViewShellId(); GetUndoManager()->EnterListAction( aStrImport, aStrImport, 0, nViewShellId ); } ScDBData* pDBData = GetDBData( ScRange(aPos), SC_DB_IMPORT, ScGetDBSelection::Keep ); OSL_ENSURE(pDBData, "Cannot create DB data"); sTarget = pDBData->GetName(); } } } // inquire, before old DB range gets overwritten bool bDo = true; if (!bIsNewArea) { OUString aTemplate = ScResId( STR_IMPORT_REPLACE ); OUString aMessage = o3tl::getToken(aTemplate, 0, '#' ) + sTarget + o3tl::getToken(aTemplate, 1, '#' ); std::unique_ptr xQueryBox(Application::CreateMessageDialog(nullptr, VclMessageType::Question, VclButtonsType::YesNo, aMessage)); xQueryBox->set_default_response(RET_YES); bDo = xQueryBox->run() == RET_YES; } if (bDo) { ScDBDocFunc(*this).UpdateImport( sTarget, aDesc ); rReq.Done(); // UpdateImport also updates the internal operations } else rReq.Ignore(); if ( bMakeArea && bUndo) GetUndoManager()->LeaveListAction(); } else { OSL_FAIL( "arguments expected" ); } } break; case SID_CHART_SOURCE: case SID_CHART_ADDSOURCE: if (pReqArgs) { ScDocument& rDoc = GetDocument(); const SfxPoolItem* pItem; OUString aChartName, aRangeName; ScRange aSingleRange; ScRangeListRef aRangeListRef; bool bMultiRange = false; bool bColHeaders = true; bool bRowHeaders = true; bool bColInit = false; bool bRowInit = false; bool bAddRange = (nSlot == SID_CHART_ADDSOURCE); if( const SfxStringItem* pChartItem = pReqArgs->GetItemIfSet( SID_CHART_NAME ) ) aChartName = pChartItem->GetValue(); if( const SfxStringItem* pChartItem = pReqArgs->GetItemIfSet( SID_CHART_SOURCE ) ) aRangeName = pChartItem->GetValue(); if( pReqArgs->HasItem( FN_PARAM_1, &pItem ) ) { bColHeaders = static_cast(pItem)->GetValue(); bColInit = true; } if( pReqArgs->HasItem( FN_PARAM_2, &pItem ) ) { bRowHeaders = static_cast(pItem)->GetValue(); bRowInit = true; } ScAddress::Details aDetails(rDoc.GetAddressConvention(), 0, 0); bool bValid = (aSingleRange.ParseAny(aRangeName, rDoc, aDetails) & ScRefFlags::VALID) != ScRefFlags::ZERO; if (!bValid) { aRangeListRef = new ScRangeList; aRangeListRef->Parse( aRangeName, rDoc, rDoc.GetAddressConvention()); if ( !aRangeListRef->empty() ) { bMultiRange = true; aSingleRange = aRangeListRef->front(); // for header bValid = true; } else aRangeListRef.clear(); } ScTabViewShell* pViewSh = ScTabViewShell::GetActiveViewShell(); if (pViewSh && bValid && !aChartName.isEmpty() ) { weld::Window* pParent = pViewSh->GetFrameWeld(); SCCOL nCol1 = aSingleRange.aStart.Col(); SCROW nRow1 = aSingleRange.aStart.Row(); SCCOL nCol2 = aSingleRange.aEnd.Col(); SCROW nRow2 = aSingleRange.aEnd.Row(); SCTAB nTab = aSingleRange.aStart.Tab(); //! limit always or not at all ??? if (!bMultiRange) m_pDocument->LimitChartArea( nTab, nCol1,nRow1, nCol2,nRow2 ); // Dialog for column/row headers bool bOk = true; if ( !bAddRange && ( !bColInit || !bRowInit ) ) { ScChartPositioner aChartPositioner( *m_pDocument, nTab, nCol1,nRow1, nCol2,nRow2 ); if (!bColInit) bColHeaders = aChartPositioner.HasColHeaders(); if (!bRowInit) bRowHeaders = aChartPositioner.HasRowHeaders(); ScAbstractDialogFactory* pFact = ScAbstractDialogFactory::Create(); ScopedVclPtr pDlg(pFact->CreateScColRowLabelDlg(pParent, bRowHeaders, bColHeaders)); if ( pDlg->Execute() == RET_OK ) { bColHeaders = pDlg->IsRow(); bRowHeaders = pDlg->IsCol(); rReq.AppendItem(SfxBoolItem(FN_PARAM_1, bColHeaders)); rReq.AppendItem(SfxBoolItem(FN_PARAM_2, bRowHeaders)); } else bOk = false; } if (bOk) // execute { if (bMultiRange) { if (bUndo) { GetUndoManager()->AddUndoAction( std::make_unique( this, aChartName, aRangeListRef, bColHeaders, bRowHeaders, bAddRange ) ); } m_pDocument->UpdateChartArea( aChartName, aRangeListRef, bColHeaders, bRowHeaders, bAddRange ); } else { ScRange aNewRange( nCol1,nRow1,nTab, nCol2,nRow2,nTab ); if (bUndo) { GetUndoManager()->AddUndoAction( std::make_unique( this, aChartName, aNewRange, bColHeaders, bRowHeaders, bAddRange ) ); } m_pDocument->UpdateChartArea( aChartName, aNewRange, bColHeaders, bRowHeaders, bAddRange ); } } } else { OSL_FAIL("UpdateChartArea: no ViewShell or wrong data"); } rReq.Done(); } else { OSL_FAIL("SID_CHART_SOURCE without arguments"); } break; case FID_AUTO_CALC: { bool bNewVal; const SfxPoolItem* pItem; if ( pReqArgs && SfxItemState::SET == pReqArgs->GetItemState( nSlot, true, &pItem ) ) bNewVal = static_cast(pItem)->GetValue(); else bNewVal = !m_pDocument->GetAutoCalc(); // Toggle for menu m_pDocument->SetAutoCalc( bNewVal ); SetDocumentModified(); if (pBindings) { pBindings->Invalidate( FID_AUTO_CALC ); } rReq.AppendItem( SfxBoolItem( FID_AUTO_CALC, bNewVal ) ); rReq.Done(); } break; case FID_RECALC: DoRecalc( rReq.IsAPI() ); rReq.Done(); break; case FID_HARD_RECALC: DoHardRecalc(); rReq.Done(); break; case SID_UPDATETABLINKS: { ScLkUpdMode nSet = GetLinkUpdateModeState(); if (nSet == LM_ALWAYS) { ReloadAllLinks(); rReq.Done(); } else if (nSet == LM_NEVER) { getEmbeddedObjectContainer().setUserAllowsLinkUpdate(false); rReq.Ignore(); } else if (nSet == LM_ON_DEMAND) { ScTabViewShell* pViewSh = GetBestViewShell(); SfxViewFrame* pViewFrame = pViewSh ? pViewSh->GetFrame() : nullptr; if (pViewFrame) { pViewFrame->RemoveInfoBar(u"enablecontent"); auto pInfoBar = pViewFrame->AppendInfoBar("enablecontent", "", ScResId(STR_RELOAD_TABLES), InfobarType::WARNING); if (pInfoBar) { weld::Button& rHelpBtn = pInfoBar->addButton(); rHelpBtn.set_label(GetStandardText(StandardButtonType::Help).replaceFirst("~", "")); rHelpBtn.connect_clicked(LINK(nullptr, LinkHelp, DispatchHelpLinksHdl)); weld::Button& rBtn = pInfoBar->addButton(); rBtn.set_label(ScResId(STR_ENABLE_CONTENT)); rBtn.set_tooltip_text(ScResId(STR_ENABLE_CONTENT_TOOLTIP)); rBtn.connect_clicked(LINK(this, ScDocShell, ReloadAllLinksHdl)); } } rReq.Done(); } } break; case SID_REIMPORT_AFTER_LOAD: { // Is called after loading if there are DB areas with omitted data bool bDone = false; ScDBCollection* pDBColl = m_pDocument->GetDBCollection(); if ((m_nCanUpdate != css::document::UpdateDocMode::NO_UPDATE) && (m_nCanUpdate != css::document::UpdateDocMode::QUIET_UPDATE)) { ScRange aRange; ScTabViewShell* pViewSh = GetBestViewShell(); OSL_ENSURE(pViewSh,"SID_REIMPORT_AFTER_LOAD: no View"); if (pViewSh && pDBColl) { std::unique_ptr xQueryBox(Application::CreateMessageDialog(GetActiveDialogParent(), VclMessageType::Question, VclButtonsType::YesNo, ScResId(STR_REIMPORT_AFTER_LOAD))); xQueryBox->set_default_response(RET_YES); if (xQueryBox->run() == RET_YES) { ScDBCollection::NamedDBs& rDBs = pDBColl->getNamedDBs(); for (const auto& rxDB : rDBs) { ScDBData& rDBData = *rxDB; if ( rDBData.IsStripData() && rDBData.HasImportParam() && !rDBData.HasImportSelection() ) { rDBData.GetArea(aRange); pViewSh->MarkRange(aRange); // Import and internal operations like SID_REFRESH_DBAREA // (inquiry for import not needed here) ScImportParam aImportParam; rDBData.GetImportParam( aImportParam ); bool bContinue = pViewSh->ImportData( aImportParam ); rDBData.SetImportParam( aImportParam ); // mark (size may have changed) rDBData.GetArea(aRange); pViewSh->MarkRange(aRange); if ( bContinue ) // error at import -> abort { // internal operations, if some where saved if ( rDBData.HasQueryParam() || rDBData.HasSortParam() || rDBData.HasSubTotalParam() ) pViewSh->RepeatDB(); // pivot tables, which have the range as source data RefreshPivotTables(aRange); } } } bDone = true; } } } if ( !bDone && pDBColl ) { // if not, but then update the dependent formulas //! also for individual ranges, which cannot be updated m_pDocument->CalcAll(); //! only for the dependent PostDataChanged(); } if (bDone) rReq.Done(); else rReq.Ignore(); } break; case SID_AUTO_STYLE: OSL_FAIL("use ScAutoStyleHint instead of SID_AUTO_STYLE"); break; case SID_GET_COLORLIST: { const SvxColorListItem* pColItem = GetItem(SID_COLOR_TABLE); const XColorListRef& pList = pColItem->GetColorList(); rReq.SetReturnValue(OfaXColorListItem(SID_GET_COLORLIST, pList)); } break; case FID_CHG_RECORD: { ScDocument& rDoc = GetDocument(); // get argument (recorded macro) const SfxBoolItem* pItem = rReq.GetArg(FID_CHG_RECORD); bool bDo = true; // desired state ScChangeTrack* pChangeTrack = rDoc.GetChangeTrack(); bool bActivateTracking = (pChangeTrack == nullptr); // toggle if ( pItem ) bActivateTracking = pItem->GetValue(); // from argument if ( !bActivateTracking ) { if ( !pItem ) { // no dialog on playing the macro std::unique_ptr xWarn(Application::CreateMessageDialog(GetActiveDialogParent(), VclMessageType::Warning, VclButtonsType::YesNo, ScResId(STR_END_REDLINING))); xWarn->set_default_response(RET_NO); bDo = (xWarn->run() == RET_YES ); } if ( bDo ) { if (pChangeTrack) { if ( pChangeTrack->IsProtected() ) bDo = ExecuteChangeProtectionDialog(); } if ( bDo ) { rDoc.EndChangeTracking(); PostPaintGridAll(); } } } else { rDoc.StartChangeTracking(); ScChangeViewSettings aChangeViewSet; aChangeViewSet.SetShowChanges(true); rDoc.SetChangeViewSettings(aChangeViewSet); } if ( bDo ) { UpdateAcceptChangesDialog(); // invalidate slots if (pBindings) pBindings->InvalidateAll(false); if ( !pItem ) rReq.AppendItem( SfxBoolItem( FID_CHG_RECORD, bActivateTracking ) ); rReq.Done(); } else rReq.Ignore(); } break; case SID_CHG_PROTECT : { if ( ExecuteChangeProtectionDialog() ) { rReq.Done(); SetDocumentModified(); } else rReq.Ignore(); } break; case SID_DOCUMENT_MERGE: case SID_DOCUMENT_COMPARE: { bool bDo = true; ScChangeTrack* pChangeTrack = m_pDocument->GetChangeTrack(); if ( pChangeTrack && !m_pImpl->bIgnoreLostRedliningWarning ) { if ( nSlot == SID_DOCUMENT_COMPARE ) { //! old changes trace will be lost std::unique_ptr xWarn(Application::CreateMessageDialog(GetActiveDialogParent(), VclMessageType::Warning, VclButtonsType::YesNo, ScResId(STR_END_REDLINING))); xWarn->set_default_response(RET_NO); if (xWarn->run() == RET_YES) bDo = ExecuteChangeProtectionDialog( true ); else bDo = false; } else // merge might reject some actions bDo = ExecuteChangeProtectionDialog( true ); } if ( !bDo ) { rReq.Ignore(); break; } SfxApplication* pApp = SfxGetpApp(); const SfxPoolItem* pItem; const SfxStringItem* pFileNameItem(nullptr); SfxMedium* pMed = nullptr; if (pReqArgs) pFileNameItem = pReqArgs->GetItemIfSet(SID_FILE_NAME); if (pFileNameItem) { OUString aFileName = pFileNameItem->GetValue(); OUString aFilterName; if (const SfxStringItem* pFilterItem = pReqArgs->GetItemIfSet(SID_FILTER_NAME)) { aFilterName = pFilterItem->GetValue(); } OUString aOptions; if (const SfxStringItem* pOptionsItem = pReqArgs->GetItemIfSet(SID_FILE_FILTEROPTIONS)) { aOptions = pOptionsItem->GetValue(); } short nVersion = 0; const SfxInt16Item* pInt16Item(nullptr); if (pReqArgs->GetItemState(SID_VERSION, true, &pItem) == SfxItemState::SET) pInt16Item = dynamic_cast(pItem); if (pInt16Item) { nVersion = pInt16Item->GetValue(); } // no filter specified -> detection if (aFilterName.isEmpty()) ScDocumentLoader::GetFilterName( aFileName, aFilterName, aOptions, true, false ); // filter name from dialog contains application prefix, // GetFilter needs name without the prefix. ScDocumentLoader::RemoveAppPrefix( aFilterName ); std::shared_ptr pFilter = ScDocShell::Factory().GetFilterContainer()->GetFilter4FilterName( aFilterName ); auto pSet = std::make_shared( pApp->GetPool() ); if (!aOptions.isEmpty()) pSet->Put( SfxStringItem( SID_FILE_FILTEROPTIONS, aOptions ) ); if ( nVersion != 0 ) pSet->Put( SfxInt16Item( SID_VERSION, nVersion ) ); pMed = new SfxMedium( aFileName, StreamMode::STD_READ, pFilter, std::move(pSet) ); } else { const sfx2::DocumentInserter::Mode mode { nSlot==SID_DOCUMENT_COMPARE ? sfx2::DocumentInserter::Mode::Compare : sfx2::DocumentInserter::Mode::Merge}; // start file dialog asynchronous m_pImpl->bIgnoreLostRedliningWarning = true; m_pImpl->pRequest.reset(new SfxRequest( rReq )); m_pImpl->pDocInserter.reset(); ScTabViewShell* pViewSh = ScTabViewShell::GetActiveViewShell(); weld::Window* pParent = pViewSh ? pViewSh->GetFrameWeld() : nullptr; m_pImpl->pDocInserter.reset( new ::sfx2::DocumentInserter(pParent, ScDocShell::Factory().GetFactoryName(), mode ) ); m_pImpl->pDocInserter->StartExecuteModal( LINK( this, ScDocShell, DialogClosedHdl ) ); return ; } // now execute in earnest... SfxErrorContext aEc( ERRCTX_SFX_OPENDOC, pMed->GetName() ); // pOtherDocSh->DoClose() will be called explicitly later, but it is still more safe to use SfxObjectShellLock here ScDocShell* pOtherDocSh = new ScDocShell; SfxObjectShellLock aDocShTablesRef = pOtherDocSh; pOtherDocSh->DoLoad( pMed ); ErrCode nErr = pOtherDocSh->GetErrorCode(); if (nErr) ErrorHandler::HandleError( nErr ); // also warnings if ( !pOtherDocSh->GetError() ) // only errors { bool bHadTrack = ( m_pDocument->GetChangeTrack() != nullptr ); #if HAVE_FEATURE_MULTIUSER_ENVIRONMENT sal_uLong nStart = 0; if ( nSlot == SID_DOCUMENT_MERGE && pChangeTrack ) { nStart = pChangeTrack->GetActionMax() + 1; } #endif if ( nSlot == SID_DOCUMENT_COMPARE ) CompareDocument( pOtherDocSh->GetDocument() ); else MergeDocument( pOtherDocSh->GetDocument() ); // show "accept changes" dialog //! get view for this document! if ( !IsDocShared() ) { SfxViewFrame* pViewFrm = SfxViewFrame::Current(); if ( pViewFrm ) { pViewFrm->ShowChildWindow( ScAcceptChgDlgWrapper::GetChildWindowId() ); //@51669 } if ( pBindings ) { pBindings->Invalidate( FID_CHG_ACCEPT ); } } rReq.SetReturnValue( SfxInt32Item( nSlot, 0 ) ); //! ??????? rReq.Done(); if (!bHadTrack) // newly turned on -> show as well { ScChangeViewSettings* pOldSet = m_pDocument->GetChangeViewSettings(); if ( !pOldSet || !pOldSet->ShowChanges() ) { ScChangeViewSettings aChangeViewSet; aChangeViewSet.SetShowChanges(true); m_pDocument->SetChangeViewSettings(aChangeViewSet); } } #if HAVE_FEATURE_MULTIUSER_ENVIRONMENT else if ( nSlot == SID_DOCUMENT_MERGE && IsDocShared() && pChangeTrack ) { sal_uLong nEnd = pChangeTrack->GetActionMax(); if ( nEnd >= nStart ) { // only show changes from merged document ScChangeViewSettings aChangeViewSet; aChangeViewSet.SetShowChanges( true ); aChangeViewSet.SetShowAccepted( true ); aChangeViewSet.SetHasActionRange(); aChangeViewSet.SetTheActionRange( nStart, nEnd ); m_pDocument->SetChangeViewSettings( aChangeViewSet ); // update view PostPaintExtras(); PostPaintGridAll(); } } #endif } pOtherDocSh->DoClose(); // delete happens with the Ref } break; case SID_DELETE_SCENARIO: if (pReqArgs) { const SfxPoolItem* pItem; if ( pReqArgs->GetItemState( nSlot, true, &pItem ) == SfxItemState::SET ) { if (const SfxStringItem* pStringItem = dynamic_cast(pItem)) { const OUString& aName = pStringItem->GetValue(); SCTAB nTab; if (m_pDocument->GetTable( aName, nTab )) { // move DeleteTable from viewfunc to docfunc! ScTabViewShell* pSh = GetBestViewShell(); if ( pSh ) { //! omit SetTabNo in DeleteTable? SCTAB nDispTab = pSh->GetViewData().GetTabNo(); pSh->DeleteTable( nTab ); pSh->SetTabNo(nDispTab); rReq.Done(); } } } } } break; case SID_EDIT_SCENARIO: { const SfxPoolItem* pItem; if ( pReqArgs->GetItemState( nSlot, true, &pItem ) == SfxItemState::SET ) { if (const SfxStringItem* pStringItem = dynamic_cast(pItem)) { OUString aName = pStringItem->GetValue(); SCTAB nTab; if (m_pDocument->GetTable( aName, nTab )) { if (m_pDocument->IsScenario(nTab)) { OUString aComment; Color aColor; ScScenarioFlags nFlags; m_pDocument->GetScenarioData( nTab, aComment, aColor, nFlags ); // Determine if the Sheet that the Scenario was created on // is protected. But first we need to find that Sheet. // Rewind back to the actual sheet. SCTAB nActualTab = nTab; do { nActualTab--; } while(m_pDocument->IsScenario(nActualTab)); bool bSheetProtected = m_pDocument->IsTabProtected(nActualTab); ScAbstractDialogFactory* pFact = ScAbstractDialogFactory::Create(); ScopedVclPtr pNewDlg(pFact->CreateScNewScenarioDlg(GetActiveDialogParent(), aName, true, bSheetProtected)); pNewDlg->SetScenarioData( aName, aComment, aColor, nFlags ); if ( pNewDlg->Execute() == RET_OK ) { pNewDlg->GetScenarioData( aName, aComment, aColor, nFlags ); ModifyScenario( nTab, aName, aComment, aColor, nFlags ); rReq.Done(); } } } } } } break; case SID_ATTR_YEAR2000 : { const SfxPoolItem* pItem; if ( pReqArgs->GetItemState( nSlot, true, &pItem ) == SfxItemState::SET ) { if (const SfxUInt16Item* pInt16Item = dynamic_cast(pItem)) { sal_uInt16 nY2k = pInt16Item->GetValue(); // set always to DocOptions, so that it is also saved for S050 // (and all inquiries run up until now on it as well). // SetDocOptions propagates that to the NumberFormatter ScDocOptions aDocOpt( m_pDocument->GetDocOptions() ); aDocOpt.SetYear2000( nY2k ); m_pDocument->SetDocOptions( aDocOpt ); // the FormShell shall notice it as well ScTabViewShell* pSh = GetBestViewShell(); if ( pSh ) { FmFormShell* pFSh = pSh->GetFormShell(); if ( pFSh ) pFSh->SetY2KState( nY2k ); } } } } break; #if HAVE_FEATURE_MULTIUSER_ENVIRONMENT case SID_SHARE_DOC: { ScViewData* pViewData = GetViewData(); if ( !pViewData ) { rReq.Ignore(); break; } weld::Window* pWin = GetActiveDialogParent(); ScShareDocumentDlg aDlg(pWin, pViewData); if (aDlg.run() == RET_OK) { bool bSetShared = aDlg.IsShareDocumentChecked(); if ( bSetShared != IsDocShared() ) { if ( bSetShared ) { bool bContinue = true; if ( HasName() ) { std::unique_ptr xQueryBox(Application::CreateMessageDialog(pWin, VclMessageType::Question, VclButtonsType::YesNo, ScResId(STR_DOC_WILLBESAVED))); xQueryBox->set_default_response(RET_YES); if (xQueryBox->run() == RET_NO) { bContinue = false; } } if ( bContinue ) { EnableSharedSettings( true ); SC_MOD()->SetInSharedDocSaving( true ); if ( !SwitchToShared( true, true ) ) { // TODO/LATER: what should be done in case the switch has failed? // for example in case the user has cancelled the saveAs operation } SC_MOD()->SetInSharedDocSaving( false ); InvalidateName(); GetUndoManager()->Clear(); ScTabView* pTabView = pViewData->GetView(); if ( pTabView ) { pTabView->UpdateLayerLocks(); } } } else { uno::Reference< frame::XModel > xModel; try { // load shared file xModel.set( LoadSharedDocument(), uno::UNO_SET_THROW ); uno::Reference< util::XCloseable > xCloseable( xModel, uno::UNO_QUERY_THROW ); // check if shared flag is set in shared file bool bShared = false; ScModelObj* pDocObj = comphelper::getFromUnoTunnel( xModel ); if ( pDocObj ) { ScDocShell* pDocShell = dynamic_cast< ScDocShell* >( pDocObj->GetEmbeddedObject() ); if ( pDocShell ) { bShared = pDocShell->HasSharedXMLFlagSet(); } } // #i87870# check if shared status was disabled and enabled again bool bOwnEntry = false; try { ::svt::ShareControlFile aControlFile( GetSharedFileURL() ); bOwnEntry = aControlFile.HasOwnEntry(); } catch ( uno::Exception& ) { } if ( bShared && bOwnEntry ) { uno::Reference< frame::XStorable > xStorable( xModel, uno::UNO_QUERY_THROW ); if ( xStorable->isReadonly() ) { xCloseable->close( true ); OUString aUserName( ScResId( STR_UNKNOWN_USER ) ); try { ::svt::DocumentLockFile aLockFile( GetSharedFileURL() ); LockFileEntry aData = aLockFile.GetLockData(); if ( !aData[LockFileComponent::OOOUSERNAME].isEmpty() ) { aUserName = aData[LockFileComponent::OOOUSERNAME]; } else if ( !aData[LockFileComponent::SYSUSERNAME].isEmpty() ) { aUserName = aData[LockFileComponent::SYSUSERNAME]; } } catch ( uno::Exception& ) { } OUString aMessage( ScResId( STR_FILE_LOCKED_TRY_LATER ) ); aMessage = aMessage.replaceFirst( "%1", aUserName ); std::unique_ptr xWarn(Application::CreateMessageDialog(pWin, VclMessageType::Warning, VclButtonsType::Ok, aMessage)); xWarn->run(); } else { std::unique_ptr xWarn(Application::CreateMessageDialog(pWin, VclMessageType::Warning, VclButtonsType::YesNo, ScResId(STR_DOC_DISABLESHARED))); xWarn->set_default_response(RET_YES); if (xWarn->run() == RET_YES) { xCloseable->close( true ); if ( !SwitchToShared( false, true ) ) { // TODO/LATER: what should be done in case the switch has failed? // for example in case the user has cancelled the saveAs operation } EnableSharedSettings( false ); // Do *not* use dispatch mechanism in this place - we don't want others (extensions etc.) to intercept this. uno::Reference xStorable2( GetModel(), uno::UNO_QUERY_THROW); xStorable2->store(); ScTabView* pTabView = pViewData->GetView(); if ( pTabView ) { pTabView->UpdateLayerLocks(); } } else { xCloseable->close( true ); } } } else { xCloseable->close( true ); std::unique_ptr xWarn(Application::CreateMessageDialog(pWin, VclMessageType::Warning, VclButtonsType::Ok, ScResId(STR_DOC_NOLONGERSHARED))); xWarn->run(); } } catch ( uno::Exception& ) { TOOLS_WARN_EXCEPTION( "sc", "SID_SHARE_DOC" ); SC_MOD()->SetInSharedDocSaving( false ); try { uno::Reference< util::XCloseable > xClose( xModel, uno::UNO_QUERY_THROW ); xClose->close( true ); } catch ( uno::Exception& ) { } } } } } rReq.Done(); } break; #endif case SID_OPEN_CALC: { ScViewData* pViewData = GetViewData(); if (pViewData) { SfxStringItem aApp(SID_DOC_SERVICE, "com.sun.star.sheet.SpreadsheetDocument"); SfxStringItem aTarget(SID_TARGETNAME, "_blank"); pViewData->GetDispatcher().ExecuteList( SID_OPENDOC, SfxCallMode::API|SfxCallMode::SYNCHRON, { &aApp, &aTarget }); } } break; case SID_NOTEBOOKBAR: { const SfxStringItem* pFile = rReq.GetArg( SID_NOTEBOOKBAR ); if ( pBindings && sfx2::SfxNotebookBar::IsActive() ) sfx2::SfxNotebookBar::ExecMethod(*pBindings, pFile ? pFile->GetValue() : ""); else if ( pBindings ) sfx2::SfxNotebookBar::CloseMethod(*pBindings); } break; case SID_LANGUAGE_STATUS: { OUString aLangText; const SfxStringItem* pItem = rReq.GetArg(nSlot); if ( pItem ) aLangText = pItem->GetValue(); if ( !aLangText.isEmpty() ) { LanguageType eLang, eLatin, eCjk, eCtl; static const OUStringLiteral aSelectionLangPrefix(u"Current_"); static const OUStringLiteral aParagraphLangPrefix(u"Paragraph_"); static const OUStringLiteral aDocLangPrefix(u"Default_"); bool bSelection = false; bool bParagraph = false; ScDocument& rDoc = GetDocument(); rDoc.GetLanguage( eLatin, eCjk, eCtl ); sal_Int32 nPos = 0; if ( aLangText == "*" ) { SfxAbstractDialogFactory* pFact = SfxAbstractDialogFactory::Create(); ScTabViewShell* pSh = GetBestViewShell(); ScopedVclPtr pDlg(pFact->CreateVclDialog(pSh ? pSh->GetDialogParent() : nullptr, SID_LANGUAGE_OPTIONS)); pDlg->Execute(); rDoc.GetLanguage( eLang, eCjk, eCtl ); } else if ( (nPos = aLangText.indexOf(aDocLangPrefix)) != -1 ) { aLangText = aLangText.replaceAt(nPos, aDocLangPrefix.getLength(), u""); if ( aLangText == "LANGUAGE_NONE" ) { eLang = LANGUAGE_NONE; rDoc.SetLanguage( eLang, eCjk, eCtl ); } else if ( aLangText == "RESET_LANGUAGES" ) { bool bAutoSpell; ScModule::GetSpellSettings(eLang, eCjk, eCtl, bAutoSpell); rDoc.SetLanguage(eLang, eCjk, eCtl); } else { eLang = SvtLanguageTable::GetLanguageType( aLangText ); if ( eLang != LANGUAGE_DONTKNOW && SvtLanguageOptions::GetScriptTypeOfLanguage(eLang) == SvtScriptType::LATIN ) { rDoc.SetLanguage( eLang, eCjk, eCtl ); } else { eLang = eLatin; } } } else if (-1 != (nPos = aLangText.indexOf( aSelectionLangPrefix ))) { bSelection = true; aLangText = aLangText.replaceAt( nPos, aSelectionLangPrefix.getLength(), u"" ); } else if (-1 != (nPos = aLangText.indexOf( aParagraphLangPrefix ))) { bParagraph = true; aLangText = aLangText.replaceAt( nPos, aParagraphLangPrefix.getLength(), u"" ); } if (bSelection || bParagraph) { ScViewData* pViewData = GetViewData(); if (!pViewData) return; EditView* pEditView = pViewData->GetEditView(pViewData->GetActivePart()); if (!pEditView) return; const LanguageType nLangToUse = SvtLanguageTable::GetLanguageType( aLangText ); SvtScriptType nScriptType = SvtLanguageOptions::GetScriptTypeOfLanguage( nLangToUse ); SfxItemSet aAttrs = pEditView->GetEditEngine()->GetEmptyItemSet(); if (nScriptType == SvtScriptType::LATIN) aAttrs.Put( SvxLanguageItem( nLangToUse, EE_CHAR_LANGUAGE ) ); if (nScriptType == SvtScriptType::COMPLEX) aAttrs.Put( SvxLanguageItem( nLangToUse, EE_CHAR_LANGUAGE_CTL ) ); if (nScriptType == SvtScriptType::ASIAN) aAttrs.Put( SvxLanguageItem( nLangToUse, EE_CHAR_LANGUAGE_CJK ) ); ESelection aOldSel; if (bParagraph) { ESelection aSel = pEditView->GetSelection(); aOldSel = aSel; aSel.nStartPos = 0; aSel.nEndPos = EE_TEXTPOS_ALL; pEditView->SetSelection( aSel ); } pEditView->SetAttribs( aAttrs ); if (bParagraph) pEditView->SetSelection( aOldSel ); } else if ( eLang != eLatin ) { if ( ScTabViewShell* pViewSh = ScTabViewShell::GetActiveViewShell() ) { ScInputHandler* pInputHandler = SC_MOD()->GetInputHdl(pViewSh); if ( pInputHandler ) pInputHandler->UpdateSpellSettings(); pViewSh->UpdateDrawTextOutliner(); } SetDocumentModified(); Broadcast(SfxHint(SfxHintId::LanguageChanged)); PostPaintGridAll(); } } } break; case SID_SPELLCHECK_IGNORE_ALL: { ScViewData* pViewData = GetViewData(); if (!pViewData) return; EditView* pEditView = pViewData->GetEditView(pViewData->GetActivePart()); if (!pEditView) return; OUString sIgnoreText; const SfxStringItem* pItem2 = rReq.GetArg(FN_PARAM_1); if (pItem2) sIgnoreText = pItem2->GetValue(); if(sIgnoreText == "Spelling") { ESelection aOldSel = pEditView->GetSelection(); pEditView->SpellIgnoreWord(); pEditView->SetSelection( aOldSel ); } } break; case SID_SPELLCHECK_APPLY_SUGGESTION: { ScViewData* pViewData = GetViewData(); if (!pViewData) return; EditView* pEditView = pViewData->GetEditView(pViewData->GetActivePart()); if (!pEditView) return; OUString sApplyText; const SfxStringItem* pItem2 = rReq.GetArg(FN_PARAM_1); if (pItem2) sApplyText = pItem2->GetValue(); static const OUStringLiteral sSpellingRule(u"Spelling_"); sal_Int32 nPos = 0; if(-1 != (nPos = sApplyText.indexOf( sSpellingRule ))) { sApplyText = sApplyText.replaceAt(nPos, sSpellingRule.getLength(), u""); pEditView->InsertText( sApplyText ); } } break; case SID_REFRESH_VIEW: { PostPaintGridAll(); } break; default: { // small (?) hack -> forwarding of the slots to TabViewShell ScTabViewShell* pSh = GetBestViewShell(); if ( pSh ) pSh->Execute( rReq ); #if HAVE_FEATURE_SCRIPTING else SbxBase::SetError( ERRCODE_BASIC_NO_ACTIVE_OBJECT ); #endif } } } void UpdateAcceptChangesDialog() { // update "accept changes" dialog //! notify all views SfxViewFrame* pViewFrm = SfxViewFrame::Current(); if ( pViewFrm && pViewFrm->HasChildWindow( FID_CHG_ACCEPT ) ) { SfxChildWindow* pChild = pViewFrm->GetChildWindow( FID_CHG_ACCEPT ); if ( pChild ) static_cast(pChild)->ReInitDlg(); } } bool ScDocShell::ExecuteChangeProtectionDialog( bool bJustQueryIfProtected ) { bool bDone = false; ScChangeTrack* pChangeTrack = m_pDocument->GetChangeTrack(); if ( pChangeTrack ) { bool bProtected = pChangeTrack->IsProtected(); if ( bJustQueryIfProtected && !bProtected ) return true; OUString aTitle( ScResId( bProtected ? SCSTR_CHG_UNPROTECT : SCSTR_CHG_PROTECT ) ); OUString aText( ScResId( SCSTR_PASSWORD ) ); OUString aPassword; weld::Window* pWin = ScDocShell::GetActiveDialogParent(); SfxPasswordDialog aDlg(pWin, &aText); aDlg.set_title(aTitle); aDlg.SetMinLen(1); aDlg.set_help_id(GetStaticInterface()->GetSlot(SID_CHG_PROTECT)->GetCommand()); aDlg.SetEditHelpId( HID_CHG_PROTECT ); if ( !bProtected ) aDlg.ShowExtras(SfxShowExtras::CONFIRM); if (aDlg.run() == RET_OK) aPassword = aDlg.GetPassword(); if (!aPassword.isEmpty()) { if ( bProtected ) { if ( SvPasswordHelper::CompareHashPassword(pChangeTrack->GetProtection(), aPassword) ) { if ( bJustQueryIfProtected ) bDone = true; else pChangeTrack->SetProtection( {} ); } else { std::unique_ptr xInfoBox(Application::CreateMessageDialog(pWin, VclMessageType::Info, VclButtonsType::Ok, ScResId(SCSTR_WRONGPASSWORD))); xInfoBox->run(); } } else { css::uno::Sequence< sal_Int8 > aPass; SvPasswordHelper::GetHashPassword( aPass, aPassword ); pChangeTrack->SetProtection( aPass ); } if ( bProtected != pChangeTrack->IsProtected() ) { UpdateAcceptChangesDialog(); bDone = true; } } } else if ( bJustQueryIfProtected ) bDone = true; return bDone; } void ScDocShell::DoRecalc( bool bApi ) { if (m_pDocument->IsInDocShellRecalc()) { SAL_WARN("sc","ScDocShell::DoRecalc tries re-entering while in Recalc; probably Forms->BASIC->Dispatcher."); return; } ScDocShellRecalcGuard aGuard(*m_pDocument); bool bDone = false; ScTabViewShell* pSh = GetBestViewShell(); ScInputHandler* pHdl = ( pSh ? SC_MOD()->GetInputHdl( pSh ) : nullptr ); if ( pSh ) { if ( pHdl && pHdl->IsInputMode() && pHdl->IsFormulaMode() && !bApi ) { pHdl->FormulaPreview(); // partial result as QuickHelp bDone = true; } else { ScTabView::UpdateInputLine(); // InputEnterHandler pSh->UpdateInputHandler(); } } if (bDone) // otherwise re-calculate document return; weld::WaitObject aWaitObj( GetActiveDialogParent() ); if ( pHdl ) { // tdf97897 set current cell to Dirty to force recalculation of cell ScFormulaCell* pFC = m_pDocument->GetFormulaCell( pHdl->GetCursorPos()); if (pFC) pFC->SetDirty(); } m_pDocument->CalcFormulaTree(); if ( pSh ) pSh->UpdateCharts(true); m_pDocument->BroadcastUno( SfxHint( SfxHintId::DataChanged ) ); // If there are charts, then paint everything, so that PostDataChanged // and the charts do not come one after the other and parts are painted twice. ScChartListenerCollection* pCharts = m_pDocument->GetChartListenerCollection(); if ( pCharts && pCharts->hasListeners() ) PostPaintGridAll(); else PostDataChanged(); } void ScDocShell::DoHardRecalc() { if (m_pDocument->IsInDocShellRecalc()) { SAL_WARN("sc","ScDocShell::DoHardRecalc tries re-entering while in Recalc; probably Forms->BASIC->Dispatcher."); return; } auto start = std::chrono::steady_clock::now(); ScDocShellRecalcGuard aGuard(*m_pDocument); weld::WaitObject aWaitObj( GetActiveDialogParent() ); ScTabViewShell* pSh = GetBestViewShell(); if ( pSh ) { ScTabView::UpdateInputLine(); // InputEnterHandler pSh->UpdateInputHandler(); } m_pDocument->CalcAll(); GetDocFunc().DetectiveRefresh(); // creates own Undo if ( pSh ) pSh->UpdateCharts(true); // set notification flags for "calculate" event (used in SfxHintId::DataChanged broadcast) // (might check for the presence of any formulas on each sheet) SCTAB nTabCount = m_pDocument->GetTableCount(); if (m_pDocument->HasAnySheetEventScript( ScSheetEventId::CALCULATE, true )) // search also for VBA handler for (SCTAB nTab=0; nTabSetCalcNotification(nTab); // CalcAll doesn't broadcast value changes, so SfxHintId::ScCalcAll is broadcasted globally // in addition to SfxHintId::DataChanged. m_pDocument->BroadcastUno( SfxHint( SfxHintId::ScCalcAll ) ); m_pDocument->BroadcastUno( SfxHint( SfxHintId::DataChanged ) ); // use hard recalc also to disable stream-copying of all sheets // (somewhat consistent with charts) for (SCTAB nTab=0; nTabSetStreamValid(nTab, false); PostPaintGridAll(); auto end = std::chrono::steady_clock::now(); SAL_INFO("sc.timing", "ScDocShell::DoHardRecalc(): took " << std::chrono::duration_cast(end - start).count() << "ms"); } void ScDocShell::DoAutoStyle( const ScRange& rRange, const OUString& rStyle ) { ScStyleSheetPool* pStylePool = m_pDocument->GetStyleSheetPool(); ScStyleSheet* pStyleSheet = pStylePool->FindAutoStyle(rStyle); if (!pStyleSheet) return; OSL_ENSURE(rRange.aStart.Tab() == rRange.aEnd.Tab(), "DoAutoStyle with several tables"); SCTAB nTab = rRange.aStart.Tab(); SCCOL nStartCol = rRange.aStart.Col(); SCROW nStartRow = rRange.aStart.Row(); SCCOL nEndCol = rRange.aEnd.Col(); SCROW nEndRow = rRange.aEnd.Row(); m_pDocument->ApplyStyleAreaTab( nStartCol, nStartRow, nEndCol, nEndRow, nTab, *pStyleSheet ); m_pDocument->ExtendMerge( nStartCol, nStartRow, nEndCol, nEndRow, nTab ); PostPaint( nStartCol, nStartRow, nTab, nEndCol, nEndRow, nTab, PaintPartFlags::Grid ); } void ScDocShell::NotifyStyle( const SfxStyleSheetHint& rHint ) { SfxHintId nId = rHint.GetId(); const SfxStyleSheetBase* pStyle = rHint.GetStyleSheet(); if (!pStyle) return; if ( pStyle->GetFamily() == SfxStyleFamily::Page ) { if ( nId == SfxHintId::StyleSheetModified ) { ScDocShellModificator aModificator( *this ); const OUString& aNewName = pStyle->GetName(); OUString aOldName = aNewName; const SfxStyleSheetModifiedHint* pExtendedHint = dynamic_cast(&rHint); // name changed? if (pExtendedHint) aOldName = pExtendedHint->GetOldName(); if ( aNewName != aOldName ) m_pDocument->RenamePageStyleInUse( aOldName, aNewName ); SCTAB nTabCount = m_pDocument->GetTableCount(); for (SCTAB nTab=0; nTabGetPageStyle(nTab) == aNewName) // already adjusted to new { m_pDocument->PageStyleModified( nTab, aNewName ); ScPrintFunc aPrintFunc( this, GetPrinter(), nTab ); aPrintFunc.UpdatePages(); } aModificator.SetDocumentModified(); if (pExtendedHint) { SfxBindings* pBindings = GetViewBindings(); if (pBindings) { pBindings->Invalidate( SID_STATUS_PAGESTYLE ); pBindings->Invalidate( SID_STYLE_FAMILY4 ); pBindings->Invalidate( FID_RESET_PRINTZOOM ); pBindings->Invalidate( SID_ATTR_PARA_LEFT_TO_RIGHT ); pBindings->Invalidate( SID_ATTR_PARA_RIGHT_TO_LEFT ); } } } } else if ( pStyle->GetFamily() == SfxStyleFamily::Para ) { if ( nId == SfxHintId::StyleSheetModified) { const OUString& aNewName = pStyle->GetName(); OUString aOldName = aNewName; const SfxStyleSheetModifiedHint* pExtendedHint = dynamic_cast(&rHint); if (pExtendedHint) aOldName = pExtendedHint->GetOldName(); if ( aNewName != aOldName ) { for(SCTAB i = 0; i < m_pDocument->GetTableCount(); ++i) { ScConditionalFormatList* pList = m_pDocument->GetCondFormList(i); if (pList) pList->RenameCellStyle( aOldName,aNewName ); } } } } // everything else goes via slots... } // like in printfun.cxx #define ZOOM_MIN 10 void ScDocShell::SetPrintZoom( SCTAB nTab, sal_uInt16 nScale, sal_uInt16 nPages ) { OUString aStyleName = m_pDocument->GetPageStyle( nTab ); ScStyleSheetPool* pStylePool = m_pDocument->GetStyleSheetPool(); SfxStyleSheetBase* pStyleSheet = pStylePool->Find( aStyleName, SfxStyleFamily::Page ); OSL_ENSURE( pStyleSheet, "PageStyle not found" ); if ( !pStyleSheet ) return; ScDocShellModificator aModificator( *this ); SfxItemSet& rSet = pStyleSheet->GetItemSet(); const bool bUndo(m_pDocument->IsUndoEnabled()); if (bUndo) { sal_uInt16 nOldScale = rSet.Get(ATTR_PAGE_SCALE).GetValue(); sal_uInt16 nOldPages = rSet.Get(ATTR_PAGE_SCALETOPAGES).GetValue(); GetUndoManager()->AddUndoAction( std::make_unique( this, nTab, nOldScale, nOldPages, nScale, nPages ) ); } rSet.Put( SfxUInt16Item( ATTR_PAGE_SCALE, nScale ) ); rSet.Put( SfxUInt16Item( ATTR_PAGE_SCALETOPAGES, nPages ) ); ScPrintFunc aPrintFunc( this, GetPrinter(), nTab ); aPrintFunc.UpdatePages(); aModificator.SetDocumentModified(); SfxBindings* pBindings = GetViewBindings(); if (pBindings) pBindings->Invalidate( FID_RESET_PRINTZOOM ); } bool ScDocShell::AdjustPrintZoom( const ScRange& rRange ) { bool bChange = false; SCTAB nTab = rRange.aStart.Tab(); OUString aStyleName = m_pDocument->GetPageStyle( nTab ); ScStyleSheetPool* pStylePool = m_pDocument->GetStyleSheetPool(); SfxStyleSheetBase* pStyleSheet = pStylePool->Find( aStyleName, SfxStyleFamily::Page ); OSL_ENSURE( pStyleSheet, "PageStyle not found" ); if ( pStyleSheet ) { SfxItemSet& rSet = pStyleSheet->GetItemSet(); bool bHeaders = rSet.Get(ATTR_PAGE_HEADERS).GetValue(); sal_uInt16 nOldScale = rSet.Get(ATTR_PAGE_SCALE).GetValue(); sal_uInt16 nOldPages = rSet.Get(ATTR_PAGE_SCALETOPAGES).GetValue(); std::optional oRepeatCol = m_pDocument->GetRepeatColRange( nTab ); std::optional oRepeatRow = m_pDocument->GetRepeatRowRange( nTab ); // calculate needed scaling for selection sal_uInt16 nNewScale = nOldScale; tools::Long nBlkTwipsX = 0; if (bHeaders) nBlkTwipsX += PRINT_HEADER_WIDTH; SCCOL nStartCol = rRange.aStart.Col(); SCCOL nEndCol = rRange.aEnd.Col(); if ( oRepeatCol && nStartCol >= oRepeatCol->aStart.Col() ) { for (SCCOL i=oRepeatCol->aStart.Col(); i<=oRepeatCol->aEnd.Col(); i++ ) nBlkTwipsX += m_pDocument->GetColWidth( i, nTab ); if ( nStartCol <= oRepeatCol->aEnd.Col() ) nStartCol = oRepeatCol->aEnd.Col() + 1; } // legacy compilers' own scope for i { for ( SCCOL i=nStartCol; i<=nEndCol; i++ ) nBlkTwipsX += m_pDocument->GetColWidth( i, nTab ); } tools::Long nBlkTwipsY = 0; if (bHeaders) nBlkTwipsY += PRINT_HEADER_HEIGHT; SCROW nStartRow = rRange.aStart.Row(); SCROW nEndRow = rRange.aEnd.Row(); if ( oRepeatRow && nStartRow >= oRepeatRow->aStart.Row() ) { nBlkTwipsY += m_pDocument->GetRowHeight( oRepeatRow->aStart.Row(), oRepeatRow->aEnd.Row(), nTab ); if ( nStartRow <= oRepeatRow->aEnd.Row() ) nStartRow = oRepeatRow->aEnd.Row() + 1; } nBlkTwipsY += m_pDocument->GetRowHeight( nStartRow, nEndRow, nTab ); Size aPhysPage; tools::Long nHdr, nFtr; ScPrintFunc aOldPrFunc( this, GetPrinter(), nTab ); aOldPrFunc.GetScaleData( aPhysPage, nHdr, nFtr ); nBlkTwipsY += nHdr + nFtr; if ( nBlkTwipsX == 0 ) // hidden columns/rows may lead to 0 nBlkTwipsX = 1; if ( nBlkTwipsY == 0 ) nBlkTwipsY = 1; tools::Long nNeeded = std::min( aPhysPage.Width() * 100 / nBlkTwipsX, aPhysPage.Height() * 100 / nBlkTwipsY ); if ( nNeeded < ZOOM_MIN ) nNeeded = ZOOM_MIN; // boundary if ( nNeeded < static_cast(nNewScale) ) nNewScale = static_cast(nNeeded); bChange = ( nNewScale != nOldScale || nOldPages != 0 ); if ( bChange ) SetPrintZoom( nTab, nNewScale, 0 ); } return bChange; } void ScDocShell::PageStyleModified( std::u16string_view rStyleName, bool bApi ) { ScDocShellModificator aModificator( *this ); SCTAB nTabCount = m_pDocument->GetTableCount(); SCTAB nUseTab = MAXTAB+1; for (SCTAB nTab=0; nTabMAXTAB; nTab++) if ( m_pDocument->GetPageStyle(nTab) == rStyleName && ( !bApi || m_pDocument->GetPageSize(nTab).Width() ) ) nUseTab = nTab; // at bApi only if breaks already shown if (ValidTab(nUseTab)) // not used -> nothing to do { bool bWarn = false; ScPrintFunc aPrintFunc( this, GetPrinter(), nUseTab ); //! cope without CountPages if (!aPrintFunc.UpdatePages()) // sets breaks on all tabs bWarn = true; if (bWarn && !bApi) { weld::Window* pWin = GetActiveDialogParent(); weld::WaitObject aWaitOff(pWin); std::unique_ptr xInfoBox(Application::CreateMessageDialog(pWin, VclMessageType::Info, VclButtonsType::Ok, ScResId(STR_PRINT_INVALID_AREA))); xInfoBox->run(); } } aModificator.SetDocumentModified(); SfxBindings* pBindings = GetViewBindings(); if (pBindings) { pBindings->Invalidate( FID_RESET_PRINTZOOM ); pBindings->Invalidate( SID_ATTR_PARA_LEFT_TO_RIGHT ); pBindings->Invalidate( SID_ATTR_PARA_RIGHT_TO_LEFT ); } } void ScDocShell::ExecutePageStyle( const SfxViewShell& rCaller, SfxRequest& rReq, SCTAB nCurTab ) { const SfxItemSet* pReqArgs = rReq.GetArgs(); switch ( rReq.GetSlot() ) { case SID_STATUS_PAGESTYLE: // click on StatusBar control case SID_FORMATPAGE: { if ( pReqArgs == nullptr ) { OUString aOldName = m_pDocument->GetPageStyle( nCurTab ); ScStyleSheetPool* pStylePool = m_pDocument->GetStyleSheetPool(); SfxStyleSheetBase* pStyleSheet = pStylePool->Find( aOldName, SfxStyleFamily::Page ); OSL_ENSURE( pStyleSheet, "PageStyle not found! :-/" ); if ( pStyleSheet ) { ScStyleSaveData aOldData; const bool bUndo(m_pDocument->IsUndoEnabled()); if (bUndo) aOldData.InitFromStyle( pStyleSheet ); SfxItemSet& rStyleSet = pStyleSheet->GetItemSet(); rStyleSet.MergeRange( XATTR_FILL_FIRST, XATTR_FILL_LAST ); ScAbstractDialogFactory* pFact = ScAbstractDialogFactory::Create(); VclPtr pDlg(pFact->CreateScStyleDlg(GetActiveDialogParent(), *pStyleSheet, true)); auto pRequest = std::make_shared(rReq); rReq.Ignore(); // the 'old' request is not relevant any more pDlg->StartExecuteAsync([this, pDlg, pRequest, pStyleSheet, aOldData, aOldName, &rStyleSet, nCurTab, &rCaller, bUndo](sal_Int32 nResult){ if ( nResult == RET_OK ) { const SfxItemSet* pOutSet = pDlg->GetOutputItemSet(); weld::WaitObject aWait( GetActiveDialogParent() ); OUString aNewName = pStyleSheet->GetName(); if ( aNewName != aOldName && m_pDocument->RenamePageStyleInUse( aOldName, aNewName ) ) { SfxBindings* pBindings = GetViewBindings(); if (pBindings) { pBindings->Invalidate( SID_STATUS_PAGESTYLE ); pBindings->Invalidate( FID_RESET_PRINTZOOM ); } } if ( pOutSet ) m_pDocument->ModifyStyleSheet( *pStyleSheet, *pOutSet ); // memorizing for GetState(): GetPageOnFromPageStyleSet( &rStyleSet, nCurTab, m_bHeaderOn, m_bFooterOn ); rCaller.GetViewFrame()->GetBindings().Invalidate( SID_HFEDIT ); ScStyleSaveData aNewData; aNewData.InitFromStyle( pStyleSheet ); if (bUndo) { GetUndoManager()->AddUndoAction( std::make_unique( this, SfxStyleFamily::Page, aOldData, aNewData ) ); } PageStyleModified( aNewName, false ); pRequest->Done(); } pDlg->disposeOnce(); }); } } } break; case SID_HFEDIT: { if ( pReqArgs == nullptr ) { OUString aStr( m_pDocument->GetPageStyle( nCurTab ) ); ScStyleSheetPool* pStylePool = m_pDocument->GetStyleSheetPool(); SfxStyleSheetBase* pStyleSheet = pStylePool->Find( aStr, SfxStyleFamily::Page ); OSL_ENSURE( pStyleSheet, "PageStyle not found! :-/" ); if ( pStyleSheet ) { SfxItemSet& rStyleSet = pStyleSheet->GetItemSet(); SvxPageUsage eUsage = rStyleSet.Get( ATTR_PAGE ).GetPageUsage(); bool bShareHeader = rStyleSet .Get(ATTR_PAGE_HEADERSET) .GetItemSet() .Get(ATTR_PAGE_SHARED) .GetValue(); bool bShareFooter = rStyleSet .Get(ATTR_PAGE_FOOTERSET) .GetItemSet() .Get(ATTR_PAGE_SHARED) .GetValue(); sal_uInt16 nResId = 0; switch ( eUsage ) { case SvxPageUsage::Left: case SvxPageUsage::Right: { if ( m_bHeaderOn && m_bFooterOn ) nResId = RID_SCDLG_HFEDIT; else if ( SvxPageUsage::Right == eUsage ) { if ( !m_bHeaderOn && m_bFooterOn ) nResId = RID_SCDLG_HFEDIT_RIGHTFOOTER; else if ( m_bHeaderOn && !m_bFooterOn ) nResId = RID_SCDLG_HFEDIT_RIGHTHEADER; } else { // #69193a# respect "shared" setting if ( !m_bHeaderOn && m_bFooterOn ) nResId = bShareFooter ? RID_SCDLG_HFEDIT_RIGHTFOOTER : RID_SCDLG_HFEDIT_LEFTFOOTER; else if ( m_bHeaderOn && !m_bFooterOn ) nResId = bShareHeader ? RID_SCDLG_HFEDIT_RIGHTHEADER : RID_SCDLG_HFEDIT_LEFTHEADER; } } break; case SvxPageUsage::Mirror: case SvxPageUsage::All: default: { if ( !bShareHeader && !bShareFooter ) { if ( m_bHeaderOn && m_bFooterOn ) nResId = RID_SCDLG_HFEDIT_ALL; else if ( !m_bHeaderOn && m_bFooterOn ) nResId = RID_SCDLG_HFEDIT_FOOTER; else if ( m_bHeaderOn && !m_bFooterOn ) nResId = RID_SCDLG_HFEDIT_HEADER; } else if ( bShareHeader && bShareFooter ) { if ( m_bHeaderOn && m_bFooterOn ) nResId = RID_SCDLG_HFEDIT; else { if ( !m_bHeaderOn && m_bFooterOn ) nResId = RID_SCDLG_HFEDIT_RIGHTFOOTER; else if ( m_bHeaderOn && !m_bFooterOn ) nResId = RID_SCDLG_HFEDIT_RIGHTHEADER; } } else if ( !bShareHeader && bShareFooter ) { if ( m_bHeaderOn && m_bFooterOn ) nResId = RID_SCDLG_HFEDIT_SFTR; else if ( !m_bHeaderOn && m_bFooterOn ) nResId = RID_SCDLG_HFEDIT_RIGHTFOOTER; else if ( m_bHeaderOn && !m_bFooterOn ) nResId = RID_SCDLG_HFEDIT_HEADER; } else if ( bShareHeader && !bShareFooter ) { if ( m_bHeaderOn && m_bFooterOn ) nResId = RID_SCDLG_HFEDIT_SHDR; else if ( !m_bHeaderOn && m_bFooterOn ) nResId = RID_SCDLG_HFEDIT_FOOTER; else if ( m_bHeaderOn && !m_bFooterOn ) nResId = RID_SCDLG_HFEDIT_RIGHTHEADER; } } } ScAbstractDialogFactory* pFact = ScAbstractDialogFactory::Create(); VclPtr pDlg(pFact->CreateScHFEditDlg( GetActiveDialogParent(), rStyleSet, aStr, nResId)); auto xRequest = std::make_shared(rReq); rReq.Ignore(); // the 'old' request is not relevant any more pDlg->StartExecuteAsync([this, pDlg, pStyleSheet, xRequest](sal_Int32 nResult){ if ( nResult == RET_OK ) { const SfxItemSet* pOutSet = pDlg->GetOutputItemSet(); if ( pOutSet ) m_pDocument->ModifyStyleSheet( *pStyleSheet, *pOutSet ); SetDocumentModified(); xRequest->Done(); } pDlg->disposeOnce(); }); } } } break; default: break; } } void ScDocShell::GetStatePageStyle( SfxItemSet& rSet, SCTAB nCurTab ) { SfxWhichIter aIter(rSet); sal_uInt16 nWhich = aIter.FirstWhich(); while ( nWhich ) { switch (nWhich) { case SID_STATUS_PAGESTYLE: rSet.Put( SfxStringItem( nWhich, m_pDocument->GetPageStyle( nCurTab ) ) ); break; case SID_HFEDIT: { OUString aStr = m_pDocument->GetPageStyle( nCurTab ); ScStyleSheetPool* pStylePool = m_pDocument->GetStyleSheetPool(); SfxStyleSheetBase* pStyleSheet = pStylePool->Find( aStr, SfxStyleFamily::Page ); OSL_ENSURE( pStyleSheet, "PageStyle not found! :-/" ); if ( pStyleSheet ) { SfxItemSet& rStyleSet = pStyleSheet->GetItemSet(); GetPageOnFromPageStyleSet( &rStyleSet, nCurTab, m_bHeaderOn, m_bFooterOn ); if ( !m_bHeaderOn && !m_bFooterOn ) rSet.DisableItem( nWhich ); } } break; } nWhich = aIter.NextWhich(); } } void ScDocShell::GetState( SfxItemSet &rSet ) { bool bTabView = GetBestViewShell() != nullptr; SfxWhichIter aIter(rSet); for (sal_uInt16 nWhich = aIter.FirstWhich(); nWhich; nWhich = aIter.NextWhich()) { if (!bTabView) { rSet.DisableItem(nWhich); continue; } switch (nWhich) { case FID_AUTO_CALC: if ( m_pDocument->GetHardRecalcState() != ScDocument::HardRecalcState::OFF ) rSet.DisableItem( nWhich ); else rSet.Put( SfxBoolItem( nWhich, m_pDocument->GetAutoCalc() ) ); break; case FID_CHG_RECORD: if ( IsDocShared() ) rSet.DisableItem( nWhich ); else rSet.Put( SfxBoolItem( nWhich, m_pDocument->GetChangeTrack() != nullptr ) ); break; case SID_CHG_PROTECT: { ScChangeTrack* pChangeTrack = m_pDocument->GetChangeTrack(); if ( pChangeTrack && !IsDocShared() ) rSet.Put( SfxBoolItem( nWhich, pChangeTrack->IsProtected() ) ); else rSet.DisableItem( nWhich ); } break; case SID_DOCUMENT_COMPARE: { if ( IsDocShared() ) { rSet.DisableItem( nWhich ); } } break; // When a formula is edited, FID_RECALC must be enabled in any case. Recalc for // the doc was disabled once because of a bug if AutoCalc was on, but is now // always enabled because of another bug. case SID_TABLES_COUNT: rSet.Put( SfxInt16Item( nWhich, m_pDocument->GetTableCount() ) ); break; case SID_ATTR_YEAR2000 : rSet.Put( SfxUInt16Item( nWhich, m_pDocument->GetDocOptions().GetYear2000() ) ); break; case SID_SHARE_DOC: { if ( IsReadOnly() || GetObjectShell()->isExportLocked() ) { rSet.DisableItem( nWhich ); } } break; case SID_ATTR_CHAR_FONTLIST: rSet.Put( SvxFontListItem( m_pImpl->pFontList.get(), nWhich ) ); break; case SID_NOTEBOOKBAR: { if (GetViewBindings()) { bool bVisible = sfx2::SfxNotebookBar::StateMethod(*GetViewBindings(), u"modules/scalc/ui/"); rSet.Put( SfxBoolItem( SID_NOTEBOOKBAR, bVisible ) ); } } break; case SID_LANGUAGE_STATUS: { LanguageType eLatin, eCjk, eCtl; GetDocument().GetLanguage( eLatin, eCjk, eCtl ); OUString sLanguage = SvtLanguageTable::GetLanguageString(eLatin); if (comphelper::LibreOfficeKit::isActive()) { if (eLatin == LANGUAGE_NONE) sLanguage += ";-"; else sLanguage += ";" + LanguageTag(eLatin).getBcp47(false); } rSet.Put(SfxStringItem(nWhich, sLanguage)); } break; default: { } break; } } } void ScDocShell::Draw( OutputDevice* pDev, const JobSetup & /* rSetup */, sal_uInt16 nAspect ) { SCTAB nVisTab = m_pDocument->GetVisibleTab(); if (!m_pDocument->HasTable(nVisTab)) return; vcl::text::ComplexTextLayoutFlags nOldLayoutMode = pDev->GetLayoutMode(); pDev->SetLayoutMode( vcl::text::ComplexTextLayoutFlags::Default ); // even if it's the same, to get the metafile action if ( nAspect == ASPECT_THUMBNAIL ) { tools::Rectangle aBoundRect = GetVisArea( ASPECT_THUMBNAIL ); ScViewData aTmpData( *this, nullptr ); aTmpData.SetTabNo(nVisTab); SnapVisArea( aBoundRect ); aTmpData.SetScreen( aBoundRect ); ScPrintFunc::DrawToDev( *m_pDocument, pDev, 1.0, aBoundRect, &aTmpData, true ); } else { tools::Rectangle aOldArea = SfxObjectShell::GetVisArea(); tools::Rectangle aNewArea = aOldArea; ScViewData aTmpData( *this, nullptr ); aTmpData.SetTabNo(nVisTab); SnapVisArea( aNewArea ); if ( aNewArea != aOldArea && (m_pDocument->GetPosLeft() > 0 || m_pDocument->GetPosTop() > 0) ) SfxObjectShell::SetVisArea( aNewArea ); aTmpData.SetScreen( aNewArea ); ScPrintFunc::DrawToDev( *m_pDocument, pDev, 1.0, aNewArea, &aTmpData, true ); } pDev->SetLayoutMode( nOldLayoutMode ); } tools::Rectangle ScDocShell::GetVisArea( sal_uInt16 nAspect ) const { SfxObjectCreateMode eShellMode = GetCreateMode(); if ( eShellMode == SfxObjectCreateMode::ORGANIZER ) { // without contents we also don't know how large are the contents; // return empty rectangle, it will then be calculated after the loading return tools::Rectangle(); } if( nAspect == ASPECT_THUMBNAIL ) { SCTAB nVisTab = m_pDocument->GetVisibleTab(); if (!m_pDocument->HasTable(nVisTab)) { nVisTab = 0; const_cast(this)->m_pDocument->SetVisibleTab(nVisTab); } Size aSize = m_pDocument->GetPageSize(nVisTab); const tools::Long SC_PREVIEW_SIZE_X = 10000; const tools::Long SC_PREVIEW_SIZE_Y = 12400; tools::Rectangle aArea( 0,0, SC_PREVIEW_SIZE_X, SC_PREVIEW_SIZE_Y); if (aSize.Width() > aSize.Height()) { aArea.SetRight( SC_PREVIEW_SIZE_Y ); aArea.SetBottom( SC_PREVIEW_SIZE_X ); } bool bNegativePage = m_pDocument->IsNegativePage( m_pDocument->GetVisibleTab() ); if ( bNegativePage ) ScDrawLayer::MirrorRectRTL( aArea ); SnapVisArea( aArea ); return aArea; } else if( nAspect == ASPECT_CONTENT && eShellMode != SfxObjectCreateMode::EMBEDDED ) { // fetch visarea like after loading SCTAB nVisTab = m_pDocument->GetVisibleTab(); if (!m_pDocument->HasTable(nVisTab)) { nVisTab = 0; const_cast(this)->m_pDocument->SetVisibleTab(nVisTab); } SCCOL nStartCol; SCROW nStartRow; m_pDocument->GetDataStart( nVisTab, nStartCol, nStartRow ); SCCOL nEndCol; SCROW nEndRow; m_pDocument->GetPrintArea( nVisTab, nEndCol, nEndRow ); if (nStartCol>nEndCol) nStartCol = nEndCol; if (nStartRow>nEndRow) nStartRow = nEndRow; tools::Rectangle aNewArea = m_pDocument ->GetMMRect( nStartCol,nStartRow, nEndCol,nEndRow, nVisTab ); return aNewArea; } else return SfxObjectShell::GetVisArea( nAspect ); } namespace { [[nodiscard]] tools::Long SnapHorizontal( const ScDocument& rDoc, SCTAB nTab, tools::Long nVal, SCCOL& rStartCol ) { SCCOL nCol = 0; tools::Long nTwips = o3tl::convert(nVal, o3tl::Length::mm100, o3tl::Length::twip); tools::Long nSnap = 0; while ( nCol