/* -*- 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 #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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define ShellClass_ScModule #include #include #include #include #define SC_IDLE_MIN 150 #define SC_IDLE_MAX 3000 #define SC_IDLE_STEP 75 #define SC_IDLE_COUNT 50 static sal_uInt16 nIdleCount = 0; SFX_IMPL_INTERFACE(ScModule, SfxShell) void ScModule::InitInterface_Impl() { GetStaticInterface()->RegisterObjectBar(SFX_OBJECTBAR_APPLICATION, SfxVisibilityFlags::Standard | SfxVisibilityFlags::Client | SfxVisibilityFlags::Viewer, ToolbarId::Objectbar_App); GetStaticInterface()->RegisterStatusBar(StatusBarId::CalcStatusBar); } ScModule::ScModule( SfxObjectFactory* pFact ) : SfxModule("sc", {pFact}), m_aIdleTimer("sc ScModule IdleTimer"), m_pDragData(new ScDragData), m_pSelTransfer( nullptr ), m_pRefInputHandler( nullptr ), m_nCurRefDlgId( 0 ), m_bIsWaterCan( false ), m_bIsInEditCommand( false ), m_bIsInExecuteDrop( false ), m_bIsInSharedDocLoading( false ), m_bIsInSharedDocSaving( false ) { // The ResManager (DLL data) is not yet initialized in the ctor! SetName("StarCalc"); // for Basic ResetDragObject(); // InputHandler does not need to be created // Create ErrorHandler - was in Init() // Between OfficeApplication::Init and ScGlobal::Init SvxErrorHandler::ensure(); m_pErrorHdl.reset( new SfxErrorHandler(RID_ERRHDLSC, ErrCodeArea::Sc, ErrCodeArea::Sc, GetResLocale()) ); m_aIdleTimer.SetTimeout(SC_IDLE_MIN); m_aIdleTimer.SetInvokeHandler( LINK( this, ScModule, IdleHandler ) ); m_aIdleTimer.Start(); m_pMessagePool = new ScMessagePool; m_pMessagePool->FreezeIdRanges(); SetPool( m_pMessagePool.get() ); ScGlobal::InitTextHeight( m_pMessagePool.get() ); StartListening( *SfxGetpApp() ); // for SfxHintId::Deinitializing } ScModule::~ScModule() { OSL_ENSURE( !m_pSelTransfer, "Selection Transfer object not deleted" ); // InputHandler does not need to be deleted (there's none in the App anymore) m_pMessagePool.clear(); m_pDragData.reset(); m_pErrorHdl.reset(); ScGlobal::Clear(); // Also calls ScDocumentPool::DeleteVersionMaps(); DeleteCfg(); // Called from Exit() } void ScModule::ConfigurationChanged( utl::ConfigurationBroadcaster* p, ConfigurationHints ) { if ( p == m_pColorConfig.get() || p == m_pAccessOptions.get() ) { // Test if detective objects have to be updated with new colors // (if the detective colors haven't been used yet, there's nothing to update) if ( ScDetectiveFunc::IsColorsInitialized() ) { const svtools::ColorConfig& rColors = GetColorConfig(); bool bArrows = ( ScDetectiveFunc::GetArrowColor() != rColors.GetColorValue(svtools::CALCDETECTIVE).nColor || ScDetectiveFunc::GetErrorColor() != rColors.GetColorValue(svtools::CALCDETECTIVEERROR).nColor ); bool bComments = ( ScDetectiveFunc::GetCommentColor() != rColors.GetColorValue(svtools::CALCNOTESBACKGROUND).nColor ); if ( bArrows || bComments ) { ScDetectiveFunc::InitializeColors(); // get the new colors // update detective objects in all open documents SfxObjectShell* pObjSh = SfxObjectShell::GetFirst(); while ( pObjSh ) { if ( auto pDocSh = dynamic_cast(pObjSh) ) { if ( bArrows ) ScDetectiveFunc( pDocSh->GetDocument(), 0 ).UpdateAllArrowColors(); if ( bComments ) ScDetectiveFunc::UpdateAllComments( pDocSh->GetDocument() ); } pObjSh = SfxObjectShell::GetNext( *pObjSh ); } } } // force all views to repaint, using the new options SfxViewShell* pViewShell = SfxViewShell::GetFirst(); while(pViewShell) { if (ScTabViewShell* pViewSh = dynamic_cast(pViewShell)) { pViewSh->PaintGrid(); pViewSh->PaintTop(); pViewSh->PaintLeft(); pViewSh->PaintExtras(); ScInputHandler* pHdl = pViewSh->GetInputHandler(); if ( pHdl ) pHdl->ForgetLastPattern(); // EditEngine BackgroundColor may change } else if ( dynamic_cast( pViewShell) != nullptr ) { vcl::Window* pWin = pViewShell->GetWindow(); if (pWin) pWin->Invalidate(); } pViewShell = SfxViewShell::GetNext( *pViewShell ); } } else if ( p == m_pCTLOptions.get() ) { // for all documents: set digit language for printer, recalc output factor, update row heights SfxObjectShell* pObjSh = SfxObjectShell::GetFirst(); while ( pObjSh ) { if ( auto pDocSh = dynamic_cast(pObjSh) ) { OutputDevice* pPrinter = pDocSh->GetPrinter(); if ( pPrinter ) pPrinter->SetDigitLanguage( GetOptDigitLanguage() ); pDocSh->CalcOutputFactor(); SCTAB nTabCount = pDocSh->GetDocument().GetTableCount(); for (SCTAB nTab=0; nTabAdjustRowHeight( 0, pDocSh->GetDocument().MaxRow(), nTab ); } pObjSh = SfxObjectShell::GetNext( *pObjSh ); } // for all views (table and preview): update digit language SfxViewShell* pSh = SfxViewShell::GetFirst(); while ( pSh ) { if (ScTabViewShell* pViewSh = dynamic_cast(pSh)) { // set ref-device for EditEngine (re-evaluates digit settings) ScInputHandler* pHdl = GetInputHdl(pViewSh); if (pHdl) pHdl->UpdateRefDevice(); pViewSh->DigitLanguageChanged(); pViewSh->PaintGrid(); } else if (ScPreviewShell* pPreviewSh = dynamic_cast(pSh)) { ScPreview* pPreview = pPreviewSh->GetPreview(); pPreview->GetOutDev()->SetDigitLanguage( GetOptDigitLanguage() ); pPreview->Invalidate(); } pSh = SfxViewShell::GetNext( *pSh ); } } } void ScModule::Notify( SfxBroadcaster&, const SfxHint& rHint ) { if ( rHint.GetId() == SfxHintId::Deinitializing ) { // ConfigItems must be removed before ConfigManager DeleteCfg(); } } void ScModule::DeleteCfg() { m_pViewCfg.reset(); // Saving happens automatically before Exit() m_pDocCfg.reset(); m_pAppCfg.reset(); m_pDefaultsCfg.reset(); m_pFormulaCfg.reset(); m_pInputCfg.reset(); m_pPrintCfg.reset(); m_pNavipiCfg.reset(); m_pAddInCfg.reset(); if ( m_pColorConfig ) { m_pColorConfig->RemoveListener(this); m_pColorConfig.reset(); } if ( m_pAccessOptions ) { m_pAccessOptions->RemoveListener(this); m_pAccessOptions.reset(); } if ( m_pCTLOptions ) { m_pCTLOptions->RemoveListener(this); m_pCTLOptions.reset(); } m_pUserOptions.reset(); } // Moved here from the App void ScModule::Execute( SfxRequest& rReq ) { SfxViewFrame* pViewFrm = SfxViewFrame::Current(); SfxBindings* pBindings = pViewFrm ? &pViewFrm->GetBindings() : nullptr; const SfxItemSet* pReqArgs = rReq.GetArgs(); sal_uInt16 nSlot = rReq.GetSlot(); switch ( nSlot ) { case SID_CHOOSE_DESIGN: SfxApplication::CallAppBasic( "Template.Samples.ShowStyles" ); break; case SID_EURO_CONVERTER: SfxApplication::CallAppBasic( "Euro.ConvertRun.Main" ); break; case SID_AUTOSPELL_CHECK: { bool bSet; const SfxPoolItem* pItem; if (pReqArgs && SfxItemState::SET == pReqArgs->GetItemState( FN_PARAM_1, true, &pItem )) bSet = static_cast(pItem)->GetValue(); else if ( pReqArgs && SfxItemState::SET == pReqArgs->GetItemState( nSlot, true, &pItem ) ) bSet = static_cast(pItem)->GetValue(); else { // Toggle ScDocShell* pDocSh = dynamic_cast( SfxObjectShell::Current() ); if ( pDocSh ) bSet = !pDocSh->GetDocument().GetDocOptions().IsAutoSpell(); else bSet = !GetDocOptions().IsAutoSpell(); } SfxItemSetFixed aSet( GetPool() ); aSet.Put( SfxBoolItem( SID_AUTOSPELL_CHECK, bSet ) ); ModifyOptions( aSet ); rReq.Done(); } break; case SID_ATTR_METRIC: { const SfxPoolItem* pItem; if ( pReqArgs && SfxItemState::SET == pReqArgs->GetItemState( nSlot, true, &pItem ) ) { FieldUnit eUnit = static_cast(static_cast(pItem)->GetValue()); switch( eUnit ) { case FieldUnit::MM: // Just the units that are also in the dialog case FieldUnit::CM: case FieldUnit::INCH: case FieldUnit::PICA: case FieldUnit::POINT: { PutItem( *pItem ); ScAppOptions aNewOpts( GetAppOptions() ); aNewOpts.SetAppMetric( eUnit ); SetAppOptions( aNewOpts ); rReq.Done(); } break; default: { // added to avoid warnings } } } } break; case FID_AUTOCOMPLETE: { ScAppOptions aNewOpts( GetAppOptions() ); bool bNew = !aNewOpts.GetAutoComplete(); aNewOpts.SetAutoComplete( bNew ); SetAppOptions( aNewOpts ); if (pBindings) pBindings->Invalidate( FID_AUTOCOMPLETE ); rReq.Done(); } break; case SID_DETECTIVE_AUTO: { ScAppOptions aNewOpts( GetAppOptions() ); bool bNew = !aNewOpts.GetDetectiveAuto(); const SfxBoolItem* pAuto = rReq.GetArg(SID_DETECTIVE_AUTO); if ( pAuto ) bNew = pAuto->GetValue(); aNewOpts.SetDetectiveAuto( bNew ); SetAppOptions( aNewOpts ); if (pBindings) pBindings->Invalidate( SID_DETECTIVE_AUTO ); rReq.AppendItem( SfxBoolItem( SID_DETECTIVE_AUTO, bNew ) ); rReq.Done(); } break; case SID_PSZ_FUNCTION: if (pReqArgs) { auto const & p = pReqArgs->Get(SID_PSZ_FUNCTION); assert(dynamic_cast(&p) && "wrong Parameter"); const SfxUInt32Item& rItem = static_cast(p); ScAppOptions aNewOpts( GetAppOptions() ); aNewOpts.SetStatusFunc( rItem.GetValue() ); SetAppOptions( aNewOpts ); if (pBindings) { pBindings->Invalidate( SID_TABLE_CELL ); pBindings->Update( SID_TABLE_CELL ); // Immediately pBindings->Invalidate( SID_PSZ_FUNCTION ); pBindings->Update( SID_PSZ_FUNCTION ); // If the menu is opened again immediately } } break; case SID_ATTR_LANGUAGE: case SID_ATTR_CHAR_CJK_LANGUAGE: case SID_ATTR_CHAR_CTL_LANGUAGE: { const SfxPoolItem* pItem; if ( pReqArgs && SfxItemState::SET == pReqArgs->GetItemState( GetPool().GetWhich(nSlot), true, &pItem ) ) { ScDocShell* pDocSh = dynamic_cast( SfxObjectShell::Current() ); if ( pDocSh ) { ScDocument& rDoc = pDocSh->GetDocument(); LanguageType eNewLang = static_cast(pItem)->GetLanguage(); LanguageType eLatin, eCjk, eCtl; rDoc.GetLanguage( eLatin, eCjk, eCtl ); LanguageType eOld = ( nSlot == SID_ATTR_CHAR_CJK_LANGUAGE ) ? eCjk : ( ( nSlot == SID_ATTR_CHAR_CTL_LANGUAGE ) ? eCtl : eLatin ); if ( eNewLang != eOld ) { if ( nSlot == SID_ATTR_CHAR_CJK_LANGUAGE ) eCjk = eNewLang; else if ( nSlot == SID_ATTR_CHAR_CTL_LANGUAGE ) eCtl = eNewLang; else eLatin = eNewLang; rDoc.SetLanguage( eLatin, eCjk, eCtl ); ScInputHandler* pInputHandler = GetInputHdl(); if ( pInputHandler ) pInputHandler->UpdateSpellSettings(); // EditEngine flags ScTabViewShell* pViewSh = dynamic_cast( SfxViewShell::Current() ); if ( pViewSh ) pViewSh->UpdateDrawTextOutliner(); // EditEngine flags pDocSh->SetDocumentModified(); } } } } break; case FID_FOCUS_POSWND: { ScInputHandler* pHdl = GetInputHdl(); if (pHdl) { ScInputWindow* pWin = pHdl->GetInputWindow(); if (pWin) pWin->PosGrabFocus(); } rReq.Done(); } break; case SID_OPEN_XML_FILTERSETTINGS: { try { css::uno::Reference < css::ui::dialogs::XExecutableDialog > xDialog = css::ui::dialogs::XSLTFilterDialog::create( ::comphelper::getProcessComponentContext()); xDialog->execute(); } catch( css::uno::RuntimeException& ) { DBG_UNHANDLED_EXCEPTION("sc.ui"); } } break; default: OSL_FAIL( "ScApplication: Unknown Message." ); break; } } void ScModule::GetState( SfxItemSet& rSet ) { ScDocShell* pDocSh = dynamic_cast( SfxObjectShell::Current() ); bool bTabView = pDocSh && (pDocSh->GetBestViewShell() != nullptr); SfxWhichIter aIter(rSet); for (sal_uInt16 nWhich = aIter.FirstWhich(); nWhich; nWhich = aIter.NextWhich()) { if (!bTabView) { // Not in the normal calc view shell (most likely in preview shell). Disable all actions. rSet.DisableItem(nWhich); continue; } switch ( nWhich ) { case FID_AUTOCOMPLETE: rSet.Put( SfxBoolItem( nWhich, GetAppOptions().GetAutoComplete() ) ); break; case SID_DETECTIVE_AUTO: rSet.Put( SfxBoolItem( nWhich, GetAppOptions().GetDetectiveAuto() ) ); break; case SID_PSZ_FUNCTION: rSet.Put( SfxUInt32Item( nWhich, GetAppOptions().GetStatusFunc() ) ); break; case SID_ATTR_METRIC: rSet.Put( SfxUInt16Item( nWhich, sal::static_int_cast(GetAppOptions().GetAppMetric()) ) ); break; case SID_AUTOSPELL_CHECK: rSet.Put( SfxBoolItem( nWhich, pDocSh->GetDocument().GetDocOptions().IsAutoSpell()) ); break; case SID_ATTR_LANGUAGE: case ATTR_CJK_FONT_LANGUAGE: // WID for SID_ATTR_CHAR_CJK_LANGUAGE case ATTR_CTL_FONT_LANGUAGE: // WID for SID_ATTR_CHAR_CTL_LANGUAGE { LanguageType eLatin, eCjk, eCtl; pDocSh->GetDocument().GetLanguage( eLatin, eCjk, eCtl ); LanguageType eLang = ( nWhich == ATTR_CJK_FONT_LANGUAGE ) ? eCjk : ( ( nWhich == ATTR_CTL_FONT_LANGUAGE ) ? eCtl : eLatin ); rSet.Put( SvxLanguageItem( eLang, nWhich ) ); } break; } } } void ScModule::HideDisabledSlots( SfxItemSet& rSet ) { if( SfxViewFrame* pViewFrm = SfxViewFrame::Current() ) { SfxBindings& rBindings = pViewFrm->GetBindings(); SfxWhichIter aIter( rSet ); for( sal_uInt16 nWhich = aIter.FirstWhich(); nWhich != 0; nWhich = aIter.NextWhich() ) { ScViewUtil::HideDisabledSlot( rSet, rBindings, nWhich ); // always disable the slots rSet.DisableItem( nWhich ); } } } void ScModule::ResetDragObject() { if (comphelper::LibreOfficeKit::isActive()) { ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell(); if (pViewShell) pViewShell->ResetDragObject(); } else { m_pDragData->pCellTransfer = nullptr; m_pDragData->pDrawTransfer = nullptr; m_pDragData->pJumpLocalDoc = nullptr; m_pDragData->aLinkDoc.clear(); m_pDragData->aLinkTable.clear(); m_pDragData->aLinkArea.clear(); m_pDragData->aJumpTarget.clear(); m_pDragData->aJumpText.clear(); } } const ScDragData& ScModule::GetDragData() const { if (comphelper::LibreOfficeKit::isActive()) { ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell(); assert(pViewShell); return pViewShell->GetDragData(); } else return *m_pDragData; } void ScModule::SetDragObject( ScTransferObj* pCellObj, ScDrawTransferObj* pDrawObj ) { if (comphelper::LibreOfficeKit::isActive()) { ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell(); if (pViewShell) pViewShell->SetDragObject(pCellObj, pDrawObj); } else { ResetDragObject(); m_pDragData->pCellTransfer = pCellObj; m_pDragData->pDrawTransfer = pDrawObj; } } void ScModule::SetDragLink( const OUString& rDoc, const OUString& rTab, const OUString& rArea ) { if (comphelper::LibreOfficeKit::isActive()) { ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell(); if (pViewShell) pViewShell->SetDragLink(rDoc, rTab, rArea); } else { ResetDragObject(); m_pDragData->aLinkDoc = rDoc; m_pDragData->aLinkTable = rTab; m_pDragData->aLinkArea = rArea; } } void ScModule::SetDragJump( ScDocument* pLocalDoc, const OUString& rTarget, const OUString& rText ) { if (comphelper::LibreOfficeKit::isActive()) { ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell(); if (pViewShell) pViewShell->SetDragJump(pLocalDoc, rTarget, rText); } else { ResetDragObject(); m_pDragData->pJumpLocalDoc = pLocalDoc; m_pDragData->aJumpTarget = rTarget; m_pDragData->aJumpText = rText; } } ScDocument* ScModule::GetClipDoc() { // called from document SfxViewFrame* pViewFrame = nullptr; ScTabViewShell* pViewShell = nullptr; css::uno::Reference xTransferable; if ((pViewShell = dynamic_cast(SfxViewShell::Current()))) xTransferable.set(ScTabViewShell::GetClipData(pViewShell->GetViewData().GetActiveWin())); else if ((pViewShell = dynamic_cast(SfxViewShell::GetFirst()))) xTransferable.set(ScTabViewShell::GetClipData(pViewShell->GetViewData().GetActiveWin())); else if ((pViewFrame = SfxViewFrame::GetFirst())) { css::uno::Reference xClipboard = pViewFrame->GetWindow().GetClipboard(); xTransferable.set(xClipboard.is() ? xClipboard->getContents() : nullptr, css::uno::UNO_QUERY); } const ScTransferObj* pObj = ScTransferObj::GetOwnClipboard(xTransferable); if (pObj) { ScDocument* pDoc = pObj->GetDocument(); assert((!pDoc || pDoc->IsClipboard()) && "Document is not clipboard, how can that be?"); return pDoc; } return nullptr; } void ScModule::SetSelectionTransfer( ScSelectionTransferObj* pNew ) { m_pSelTransfer = pNew; } void ScModule::SetViewOptions( const ScViewOptions& rOpt ) { if ( !m_pViewCfg ) m_pViewCfg.reset(new ScViewCfg); m_pViewCfg->SetOptions( rOpt ); } const ScViewOptions& ScModule::GetViewOptions() { if ( !m_pViewCfg ) m_pViewCfg.reset( new ScViewCfg ); return *m_pViewCfg; } void ScModule::SetDocOptions( const ScDocOptions& rOpt ) { if ( !m_pDocCfg ) m_pDocCfg.reset( new ScDocCfg ); m_pDocCfg->SetOptions( rOpt ); } const ScDocOptions& ScModule::GetDocOptions() { if ( !m_pDocCfg ) m_pDocCfg.reset( new ScDocCfg ); return *m_pDocCfg; } void ScModule::InsertEntryToLRUList(sal_uInt16 nFIndex) { if(nFIndex == 0) return; const ScAppOptions& rAppOpt = GetAppOptions(); sal_uInt16 nLRUFuncCount = std::min( rAppOpt.GetLRUFuncListCount(), sal_uInt16(LRU_MAX) ); sal_uInt16* pLRUListIds = rAppOpt.GetLRUFuncList(); sal_uInt16 aIdxList[LRU_MAX]; sal_uInt16 n = 0; bool bFound = false; while ((n < LRU_MAX) && nSetOptions( rOpt ); } void global_InitAppOptions() { SC_MOD()->GetAppOptions(); } const ScAppOptions& ScModule::GetAppOptions() { if ( !m_pAppCfg ) m_pAppCfg.reset( new ScAppCfg ); return m_pAppCfg->GetOptions(); } void ScModule::SetDefaultsOptions( const ScDefaultsOptions& rOpt ) { if ( !m_pDefaultsCfg ) m_pDefaultsCfg.reset( new ScDefaultsCfg ); m_pDefaultsCfg->SetOptions( rOpt ); } const ScDefaultsOptions& ScModule::GetDefaultsOptions() { if ( !m_pDefaultsCfg ) m_pDefaultsCfg.reset( new ScDefaultsCfg ); return *m_pDefaultsCfg; } void ScModule::SetFormulaOptions( const ScFormulaOptions& rOpt ) { if ( !m_pFormulaCfg ) m_pFormulaCfg.reset( new ScFormulaCfg ); m_pFormulaCfg->SetOptions( rOpt ); } const ScFormulaOptions& ScModule::GetFormulaOptions() { if ( !m_pFormulaCfg ) m_pFormulaCfg.reset( new ScFormulaCfg ); return *m_pFormulaCfg; } void ScModule::SetInputOptions( const ScInputOptions& rOpt ) { if ( !m_pInputCfg ) m_pInputCfg.reset( new ScInputCfg ); m_pInputCfg->SetOptions( rOpt ); } const ScInputOptions& ScModule::GetInputOptions() { if ( !m_pInputCfg ) m_pInputCfg.reset( new ScInputCfg ); return m_pInputCfg->GetOptions(); } void ScModule::SetPrintOptions( const ScPrintOptions& rOpt ) { if ( !m_pPrintCfg ) m_pPrintCfg.reset( new ScPrintCfg ); m_pPrintCfg->SetOptions( rOpt ); } const ScPrintOptions& ScModule::GetPrintOptions() { if ( !m_pPrintCfg ) m_pPrintCfg.reset( new ScPrintCfg ); return m_pPrintCfg->GetOptions(); } ScNavipiCfg& ScModule::GetNavipiCfg() { if ( !m_pNavipiCfg ) m_pNavipiCfg.reset( new ScNavipiCfg ); return *m_pNavipiCfg; } ScAddInCfg& ScModule::GetAddInCfg() { if ( !m_pAddInCfg ) m_pAddInCfg.reset( new ScAddInCfg ); return *m_pAddInCfg; } svtools::ColorConfig& ScModule::GetColorConfig() { if ( !m_pColorConfig ) { m_pColorConfig.reset( new svtools::ColorConfig ); m_pColorConfig->AddListener(this); } return *m_pColorConfig; } SvtAccessibilityOptions& ScModule::GetAccessOptions() { if ( !m_pAccessOptions ) { m_pAccessOptions.reset( new SvtAccessibilityOptions ); m_pAccessOptions->AddListener(this); } return *m_pAccessOptions; } SvtCTLOptions& ScModule::GetCTLOptions() { if ( !m_pCTLOptions ) { m_pCTLOptions.reset( new SvtCTLOptions ); m_pCTLOptions->AddListener(this); } return *m_pCTLOptions; } SvtUserOptions& ScModule::GetUserOptions() { if( !m_pUserOptions ) { m_pUserOptions.reset( new SvtUserOptions ); } return *m_pUserOptions; } LanguageType ScModule::GetOptDigitLanguage() { SvtCTLOptions::TextNumerals eNumerals = GetCTLOptions().GetCTLTextNumerals(); return ( eNumerals == SvtCTLOptions::NUMERALS_ARABIC ) ? LANGUAGE_ENGLISH_US : ( eNumerals == SvtCTLOptions::NUMERALS_HINDI) ? LANGUAGE_ARABIC_SAUDI_ARABIA : LANGUAGE_SYSTEM; } /** * Options * * Items from Calc options dialog and SID_AUTOSPELL_CHECK */ void ScModule::ModifyOptions( const SfxItemSet& rOptSet ) { LanguageType nOldSpellLang, nOldCjkLang, nOldCtlLang; bool bOldAutoSpell; GetSpellSettings( nOldSpellLang, nOldCjkLang, nOldCtlLang, bOldAutoSpell ); if (!m_pAppCfg) GetAppOptions(); OSL_ENSURE( m_pAppCfg, "AppOptions not initialised :-(" ); if (!m_pInputCfg) GetInputOptions(); OSL_ENSURE( m_pInputCfg, "InputOptions not initialised :-(" ); SfxViewFrame* pViewFrm = SfxViewFrame::Current(); SfxBindings* pBindings = pViewFrm ? &pViewFrm->GetBindings() : nullptr; ScTabViewShell* pViewSh = dynamic_cast( SfxViewShell::Current() ); ScDocShell* pDocSh = dynamic_cast( SfxObjectShell::Current() ); ScDocument* pDoc = pDocSh ? &pDocSh->GetDocument() : nullptr; bool bRepaint = false; bool bUpdateMarks = false; bool bUpdateRefDev = false; bool bCalcAll = false; bool bSaveAppOptions = false; bool bSaveInputOptions = false; bool bCompileErrorCells = false; // SfxGetpApp()->SetOptions( rOptSet ); ScAppOptions aAppOptions = m_pAppCfg->GetOptions(); // No more linguistics if (const SfxUInt16Item* pItem = rOptSet.GetItemIfSet(SID_ATTR_METRIC)) { PutItem( *pItem ); aAppOptions.SetAppMetric( static_cast(pItem->GetValue()) ); bSaveAppOptions = true; } if (const ScUserListItem* pItem = rOptSet.GetItemIfSet(SCITEM_USERLIST)) { ScGlobal::SetUserList( pItem->GetUserList() ); bSaveAppOptions = true; } if (const SfxBoolItem* pItem = rOptSet.GetItemIfSet(SID_SC_OPT_SYNCZOOM)) { aAppOptions.SetSynchronizeZoom( pItem->GetValue() ); bSaveAppOptions = true; } if (const SfxUInt16Item* pItem = rOptSet.GetItemIfSet(SID_SC_OPT_KEY_BINDING_COMPAT)) { sal_uInt16 nVal = pItem->GetValue(); ScOptionsUtil::KeyBindingType eOld = aAppOptions.GetKeyBindingType(); ScOptionsUtil::KeyBindingType eNew = static_cast(nVal); if (eOld != eNew) { aAppOptions.SetKeyBindingType(eNew); bSaveAppOptions = true; ScDocShell::ResetKeyBindings(eNew); } } // DefaultsOptions if (const ScTpDefaultsItem* pItem = rOptSet.GetItemIfSet(SID_SCDEFAULTSOPTIONS)) { const ScDefaultsOptions& rOpt = pItem->GetDefaultsOptions(); SetDefaultsOptions( rOpt ); } // FormulaOptions if (const ScTpFormulaItem* pItem = rOptSet.GetItemIfSet(SID_SCFORMULAOPTIONS)) { const ScFormulaOptions& rOpt = pItem->GetFormulaOptions(); if (!m_pFormulaCfg || (*m_pFormulaCfg != rOpt)) // Formula options have changed. Repaint the column headers. bRepaint = true; if (m_pFormulaCfg && m_pFormulaCfg->GetUseEnglishFuncName() != rOpt.GetUseEnglishFuncName()) { // Re-compile formula cells with error as the error may have been // caused by unresolved function names. bCompileErrorCells = true; } // Recalc for interpreter options changes. if (m_pFormulaCfg && m_pFormulaCfg->GetCalcConfig() != rOpt.GetCalcConfig()) bCalcAll = true; if ( pDocSh ) { pDocSh->SetFormulaOptions( rOpt ); pDocSh->SetDocumentModified(); } // ScDocShell::SetFormulaOptions() may check for changed settings, so // set the new options here after that has been called. if (!bCalcAll || rOpt.GetWriteCalcConfig()) { // CalcConfig is new, didn't change or is global, simply set all. SetFormulaOptions( rOpt ); } else { // If "only for current document" was checked, reset those affected // by that setting to previous values. ScFormulaOptions aNewOpt( rOpt); aNewOpt.GetCalcConfig().MergeDocumentSpecific( m_pFormulaCfg->GetCalcConfig()); SetFormulaOptions( aNewOpt); } } // ViewOptions if (const ScTpViewItem* pItem = rOptSet.GetItemIfSet(SID_SCVIEWOPTIONS)) { const ScViewOptions& rNewOpt = pItem->GetViewOptions(); if ( pViewSh ) { ScViewData& rViewData = pViewSh->GetViewData(); const ScViewOptions& rOldOpt = rViewData.GetOptions(); bool bAnchorList = ( rOldOpt.GetOption( VOPT_ANCHOR ) != rNewOpt.GetOption( VOPT_ANCHOR ) ); if ( rOldOpt != rNewOpt ) { rViewData.SetOptions( rNewOpt ); // Changes rOldOpt rViewData.GetDocument().SetViewOptions( rNewOpt ); if (pDocSh) pDocSh->SetDocumentModified(); bRepaint = true; } if ( bAnchorList ) pViewSh->UpdateAnchorHandles(); } SetViewOptions( rNewOpt ); if (pBindings) { pBindings->Invalidate(SID_HELPLINES_MOVE); } } // GridOptions // Evaluate after ViewOptions, as GridOptions is a member of ViewOptions if ( const SvxGridItem* pItem = rOptSet.GetItemIfSet(SID_ATTR_GRID_OPTIONS) ) { ScGridOptions aNewGridOpt( *pItem ); if ( pViewSh ) { ScViewData& rViewData = pViewSh->GetViewData(); ScViewOptions aNewViewOpt( rViewData.GetOptions() ); const ScGridOptions& rOldGridOpt = aNewViewOpt.GetGridOptions(); if ( rOldGridOpt != aNewGridOpt ) { aNewViewOpt.SetGridOptions( aNewGridOpt ); rViewData.SetOptions( aNewViewOpt ); rViewData.GetDocument().SetViewOptions( aNewViewOpt ); if (pDocSh) pDocSh->SetDocumentModified(); bRepaint = true; } } ScViewOptions aNewViewOpt ( GetViewOptions() ); aNewViewOpt.SetGridOptions( aNewGridOpt ); SetViewOptions( aNewViewOpt ); if (pBindings) { pBindings->Invalidate(SID_GRID_VISIBLE); pBindings->Invalidate(SID_GRID_USE); } } // DocOptions if ( const ScTpCalcItem* pItem = rOptSet.GetItemIfSet(SID_SCDOCOPTIONS) ) { const ScDocOptions& rNewOpt = pItem->GetDocOptions(); if ( pDoc ) { const ScDocOptions& rOldOpt = pDoc->GetDocOptions(); bRepaint = ( bRepaint || ( rOldOpt != rNewOpt ) ); bCalcAll = bRepaint && ( rOldOpt.IsIter() != rNewOpt.IsIter() || rOldOpt.GetIterCount() != rNewOpt.GetIterCount() || rOldOpt.GetIterEps() != rNewOpt.GetIterEps() || rOldOpt.IsIgnoreCase() != rNewOpt.IsIgnoreCase() || rOldOpt.IsCalcAsShown() != rNewOpt.IsCalcAsShown() || (rNewOpt.IsCalcAsShown() && rOldOpt.GetStdPrecision() != rNewOpt.GetStdPrecision()) || rOldOpt.IsMatchWholeCell() != rNewOpt.IsMatchWholeCell() || rOldOpt.GetYear2000() != rNewOpt.GetYear2000() || rOldOpt.IsFormulaRegexEnabled() != rNewOpt.IsFormulaRegexEnabled() || rOldOpt.IsFormulaWildcardsEnabled() != rNewOpt.IsFormulaWildcardsEnabled() ); pDoc->SetDocOptions( rNewOpt ); pDocSh->SetDocumentModified(); } SetDocOptions( rNewOpt ); } // Set TabDistance after the actual DocOptions if ( const SfxUInt16Item* pItem = rOptSet.GetItemIfSet(SID_ATTR_DEFTABSTOP) ) { sal_uInt16 nTabDist = pItem->GetValue(); ScDocOptions aOpt(GetDocOptions()); aOpt.SetTabDistance(nTabDist); SetDocOptions( aOpt ); if ( pDoc ) { ScDocOptions aDocOpt(pDoc->GetDocOptions()); aDocOpt.SetTabDistance(nTabDist); pDoc->SetDocOptions( aDocOpt ); pDocSh->SetDocumentModified(); if(pDoc->GetDrawLayer()) pDoc->GetDrawLayer()->SetDefaultTabulator(nTabDist); } } // AutoSpell after the DocOptions (due to being a member) if ( const SfxBoolItem* pItem = rOptSet.GetItemIfSet(SID_AUTOSPELL_CHECK) ) // At DocOptions { bool bDoAutoSpell = pItem->GetValue(); if (pDoc) { ScDocOptions aNewOpt = pDoc->GetDocOptions(); if ( aNewOpt.IsAutoSpell() != bDoAutoSpell ) { aNewOpt.SetAutoSpell( bDoAutoSpell ); pDoc->SetDocOptions( aNewOpt ); if (pViewSh) pViewSh->EnableAutoSpell(bDoAutoSpell); bRepaint = true; // Because HideAutoSpell might be invalid //TODO: Paint all Views? } } if ( bOldAutoSpell != bDoAutoSpell ) SetAutoSpellProperty( bDoAutoSpell ); if ( pDocSh ) pDocSh->PostPaintGridAll(); // Due to marks ScInputHandler* pInputHandler = GetInputHdl(); if ( pInputHandler ) pInputHandler->UpdateSpellSettings(); // EditEngine flags if ( pViewSh ) pViewSh->UpdateDrawTextOutliner(); // EditEngine flags if (pBindings) pBindings->Invalidate( SID_AUTOSPELL_CHECK ); } // InputOptions ScInputOptions aInputOptions = m_pInputCfg->GetOptions(); if ( const SfxUInt16Item* pItem = rOptSet.GetItemIfSet(SID_SC_INPUT_SELECTIONPOS) ) { aInputOptions.SetMoveDir( pItem->GetValue() ); bSaveInputOptions = true; } if ( const SfxBoolItem* pItem = rOptSet.GetItemIfSet(SID_SC_INPUT_SELECTION) ) { aInputOptions.SetMoveSelection( pItem->GetValue() ); bSaveInputOptions = true; } if ( const SfxBoolItem* pItem = rOptSet.GetItemIfSet(SID_SC_INPUT_EDITMODE) ) { aInputOptions.SetEnterEdit( pItem->GetValue() ); bSaveInputOptions = true; } if ( const SfxBoolItem* pItem = rOptSet.GetItemIfSet(SID_SC_INPUT_FMT_EXPAND) ) { aInputOptions.SetExtendFormat( pItem->GetValue() ); bSaveInputOptions = true; } if ( const SfxBoolItem* pItem = rOptSet.GetItemIfSet(SID_SC_INPUT_RANGEFINDER) ) { aInputOptions.SetRangeFinder( pItem->GetValue() ); bSaveInputOptions = true; } if ( const SfxBoolItem* pItem = rOptSet.GetItemIfSet(SID_SC_INPUT_REF_EXPAND) ) { aInputOptions.SetExpandRefs( pItem->GetValue() ); bSaveInputOptions = true; } if (const SfxBoolItem* pItem = rOptSet.GetItemIfSet(SID_SC_OPT_SORT_REF_UPDATE)) { aInputOptions.SetSortRefUpdate( pItem->GetValue()); bSaveInputOptions = true; } if ( const SfxBoolItem* pItem = rOptSet.GetItemIfSet(SID_SC_INPUT_MARK_HEADER) ) { aInputOptions.SetMarkHeader( pItem->GetValue() ); bSaveInputOptions = true; bUpdateMarks = true; } if ( const SfxBoolItem* pItem = rOptSet.GetItemIfSet(SID_SC_INPUT_TEXTWYSIWYG) ) { bool bNew = pItem->GetValue(); if ( bNew != aInputOptions.GetTextWysiwyg() ) { aInputOptions.SetTextWysiwyg( bNew ); bSaveInputOptions = true; bUpdateRefDev = true; } } if( const SfxBoolItem* pItem = rOptSet.GetItemIfSet( SID_SC_INPUT_REPLCELLSWARN ) ) { aInputOptions.SetReplaceCellsWarn( pItem->GetValue() ); bSaveInputOptions = true; } if( const SfxBoolItem* pItem = rOptSet.GetItemIfSet( SID_SC_INPUT_LEGACY_CELL_SELECTION ) ) { aInputOptions.SetLegacyCellSelection( pItem->GetValue() ); bSaveInputOptions = true; } if( const SfxBoolItem* pItem = rOptSet.GetItemIfSet( SID_SC_INPUT_ENTER_PASTE_MODE ) ) { aInputOptions.SetEnterPasteMode( pItem->GetValue() ); bSaveInputOptions = true; } // PrintOptions if ( const ScTpPrintItem* pItem = rOptSet.GetItemIfSet(SID_SCPRINTOPTIONS) ) { const ScPrintOptions& rNewOpt = pItem->GetPrintOptions(); SetPrintOptions( rNewOpt ); // broadcast causes all previews to recalc page numbers SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScPrintOptions ) ); } if ( bSaveAppOptions ) m_pAppCfg->SetOptions(aAppOptions); if ( bSaveInputOptions ) m_pInputCfg->SetOptions(aInputOptions); // Kick off recalculation? if (pDoc && bCompileErrorCells) { // Re-compile cells with name error, and recalc if at least one cell // has been re-compiled. In the future we may want to find a way to // recalc only those that are affected. if (pDoc->CompileErrorCells(FormulaError::NoName)) bCalcAll = true; } if ( pDoc && bCalcAll ) { weld::WaitObject aWait( ScDocShell::GetActiveDialogParent() ); pDoc->CalcAll(); if ( pViewSh ) pViewSh->UpdateCharts( true ); else ScDBFunc::DoUpdateCharts( ScAddress(), *pDoc, true ); if (pBindings) pBindings->Invalidate( SID_ATTR_SIZE ); //SvxPosSize StatusControl Update } if ( pViewSh && bUpdateMarks ) pViewSh->UpdateAutoFillMark(); // Repaint View? if ( pViewSh && bRepaint ) { pViewSh->UpdateFixPos(); pViewSh->PaintGrid(); pViewSh->PaintTop(); pViewSh->PaintLeft(); pViewSh->PaintExtras(); pViewSh->InvalidateBorder(); if (pBindings) { pBindings->Invalidate( FID_TOGGLEHEADERS ); // -> Checks in menu pBindings->Invalidate( FID_TOGGLESYNTAX ); } } // update ref device (for all documents) if ( !bUpdateRefDev ) return; // for all documents: recalc output factor, update row heights SfxObjectShell* pObjSh = SfxObjectShell::GetFirst(); while ( pObjSh ) { if ( auto pOneDocSh = dynamic_cast(pObjSh) ) { pOneDocSh->CalcOutputFactor(); SCTAB nTabCount = pOneDocSh->GetDocument().GetTableCount(); for (SCTAB nTab=0; nTabAdjustRowHeight( 0, pDocSh->GetDocument().MaxRow(), nTab ); } pObjSh = SfxObjectShell::GetNext( *pObjSh ); } // for all (tab-) views: SfxViewShell* pSh = SfxViewShell::GetFirst( true, checkSfxViewShell ); while ( pSh ) { ScTabViewShell* pOneViewSh = static_cast(pSh); // set ref-device for EditEngine ScInputHandler* pHdl = GetInputHdl(pOneViewSh); if (pHdl) pHdl->UpdateRefDevice(); // update view scale ScViewData& rViewData = pOneViewSh->GetViewData(); pOneViewSh->SetZoom( rViewData.GetZoomX(), rViewData.GetZoomY(), false ); // repaint pOneViewSh->PaintGrid(); pOneViewSh->PaintTop(); pOneViewSh->PaintLeft(); pSh = SfxViewShell::GetNext( *pSh, true, checkSfxViewShell ); } } /** * Input-Handler */ ScInputHandler* ScModule::GetInputHdl( ScTabViewShell* pViewSh, bool bUseRef ) { if ( !comphelper::LibreOfficeKit::isActive() && m_pRefInputHandler && bUseRef ) return m_pRefInputHandler; ScInputHandler* pHdl = nullptr; if ( !pViewSh ) { // in case a UIActive embedded object has no ViewShell (UNO component) // the own calc view shell will be set as current, but no handling should happen ScTabViewShell* pCurViewSh = dynamic_cast( SfxViewShell::Current() ); if ( pCurViewSh && !pCurViewSh->GetUIActiveClient() ) pViewSh = pCurViewSh; } if ( pViewSh ) pHdl = pViewSh->GetInputHandler(); // Viewshell always has one, from now on // If no ViewShell passed or active, we can get NULL OSL_ENSURE( pHdl || !pViewSh, "GetInputHdl: no InputHandler found!" ); return pHdl; } void ScModule::ViewShellChanged(bool bStopEditing /*=true*/) { ScInputHandler* pHdl = GetInputHdl(); ScTabViewShell* pShell = ScTabViewShell::GetActiveViewShell(); if ( pShell && pHdl ) pShell->UpdateInputHandler(false, bStopEditing); } void ScModule::SetInputMode( ScInputMode eMode, const OUString* pInitText ) { ScInputHandler* pHdl = GetInputHdl(); if (pHdl) pHdl->SetMode(eMode, pInitText); } bool ScModule::IsEditMode() { ScInputHandler* pHdl = GetInputHdl(); return pHdl && pHdl->IsEditMode(); } bool ScModule::IsInputMode() { ScInputHandler* pHdl = GetInputHdl(); return pHdl && pHdl->IsInputMode(); } bool ScModule::InputKeyEvent( const KeyEvent& rKEvt, bool bStartEdit ) { ScInputHandler* pHdl = GetInputHdl(); return pHdl && pHdl->KeyInput( rKEvt, bStartEdit ); } void ScModule::InputEnterHandler( ScEnterMode nBlockMode, bool bBeforeSavingInLOK ) { if ( !SfxGetpApp()->IsDowning() ) // Not when quitting the program { ScInputHandler* pHdl = GetInputHdl(); if (pHdl) pHdl->EnterHandler( nBlockMode, bBeforeSavingInLOK ); } } void ScModule::InputCancelHandler() { ScInputHandler* pHdl = GetInputHdl(); if (pHdl) pHdl->CancelHandler(); } void ScModule::InputSelection( const EditView* pView ) { ScInputHandler* pHdl = GetInputHdl(); if (pHdl) pHdl->InputSelection( pView ); } void ScModule::InputChanged( const EditView* pView ) { ScInputHandler* pHdl = GetInputHdl(); if (pHdl) pHdl->InputChanged( pView, false ); } void ScModule::ViewShellGone( const ScTabViewShell* pViewSh ) { ScInputHandler* pHdl = GetInputHdl(); if (pHdl) pHdl->ViewShellGone( pViewSh ); } void ScModule::SetRefInputHdl( ScInputHandler* pNew ) { m_pRefInputHandler = pNew; } void ScModule::InputGetSelection( sal_Int32& rStart, sal_Int32& rEnd ) { ScInputHandler* pHdl = GetInputHdl(); if (pHdl) pHdl->InputGetSelection( rStart, rEnd ); } void ScModule::InputSetSelection( sal_Int32 nStart, sal_Int32 nEnd ) { ScInputHandler* pHdl = GetInputHdl(); if (pHdl) pHdl->InputSetSelection( nStart, nEnd ); } void ScModule::InputReplaceSelection( const OUString& rStr ) { ScInputHandler* pHdl = GetInputHdl(); if (pHdl) pHdl->InputReplaceSelection( rStr ); } void ScModule::InputTurnOffWinEngine() { ScInputHandler* pHdl = GetInputHdl(); if (pHdl) pHdl->InputTurnOffWinEngine(); } void ScModule::ActivateInputWindow( const OUString* pStrFormula, bool bMatrix ) { ScInputHandler* pHdl = GetInputHdl(); if ( !pHdl ) return; ScInputWindow* pWin = pHdl->GetInputWindow(); if ( pStrFormula ) { // Take over formula if ( pWin ) { pWin->SetFuncString( *pStrFormula, false ); // SetSumAssignMode due to sal_False not necessary } ScEnterMode nMode = bMatrix ? ScEnterMode::MATRIX : ScEnterMode::NORMAL; pHdl->EnterHandler( nMode ); // Without Invalidate the selection remains active, if the formula has not changed if (pWin) pWin->TextInvalidate(); } else { // Cancel if ( pWin ) { pWin->SetFuncString( OUString(), false ); // SetSumAssignMode due to sal_False no necessary } pHdl->CancelHandler(); } } /** * Reference dialogs */ void ScModule::SetRefDialog( sal_uInt16 nId, bool bVis, SfxViewFrame* pViewFrm ) { //TODO: Move reference dialog handling to view // Just keep function autopilot here for references to other documents if ( !(m_nCurRefDlgId == 0 || ( nId == m_nCurRefDlgId && !bVis ) || ( comphelper::LibreOfficeKit::isActive() )) ) return; if ( !pViewFrm ) pViewFrm = SfxViewFrame::Current(); // bindings update causes problems with update of stylist if // current style family has changed //if ( pViewFrm ) // pViewFrm->GetBindings().Update(); // to avoid trouble in LockDispatcher // before SetChildWindow if ( comphelper::LibreOfficeKit::isActive() ) { if ( bVis ) m_nCurRefDlgId = nId; } else { m_nCurRefDlgId = bVis ? nId : 0; } if ( pViewFrm ) { // store the dialog id also in the view shell SfxViewShell* pViewSh = pViewFrm->GetViewShell(); if (ScTabViewShell* pTabViewSh = dynamic_cast(pViewSh)) pTabViewSh->SetCurRefDlgId(m_nCurRefDlgId); else { // no ScTabViewShell - possible for example from a Basic macro bVis = false; m_nCurRefDlgId = 0; // don't set nCurRefDlgId if no dialog is created } pViewFrm->SetChildWindow( nId, bVis ); } SfxApplication* pSfxApp = SfxGetpApp(); pSfxApp->Broadcast( SfxHint( SfxHintId::ScRefModeChanged ) ); } static SfxChildWindow* lcl_GetChildWinFromCurrentView( sal_uInt16 nId ) { SfxViewFrame* pViewFrm = SfxViewFrame::Current(); // #i46999# current view frame can be null (for example, when closing help) return pViewFrm ? pViewFrm->GetChildWindow( nId ) : nullptr; } static SfxChildWindow* lcl_GetChildWinFromAnyView( sal_uInt16 nId ) { // First, try the current view SfxChildWindow* pChildWnd = lcl_GetChildWinFromCurrentView( nId ); if ( pChildWnd ) return pChildWnd; // found in the current view // if not found there, get the child window from any open view // it can be open only in one view because nCurRefDlgId is global SfxViewFrame* pViewFrm = SfxViewFrame::GetFirst(); while ( pViewFrm ) { pChildWnd = pViewFrm->GetChildWindow( nId ); if ( pChildWnd ) return pChildWnd; // found in any view pViewFrm = SfxViewFrame::GetNext( *pViewFrm ); } return nullptr; // none found } bool ScModule::IsModalMode(SfxObjectShell* pDocSh) { //TODO: Move reference dialog handling to view // Just keep function autopilot here for references to other documents bool bIsModal = false; if ( m_nCurRefDlgId ) { SfxChildWindow* pChildWnd = lcl_GetChildWinFromCurrentView( m_nCurRefDlgId ); if ( pChildWnd ) { if (pChildWnd->GetController()) { IAnyRefDialog* pRefDlg = dynamic_cast(pChildWnd->GetController().get()); assert(pRefDlg); bIsModal = pChildWnd->IsVisible() && pRefDlg && !( pRefDlg->IsRefInputMode() && pRefDlg->IsDocAllowed(pDocSh) ); } } else if ( pDocSh && comphelper::LibreOfficeKit::isActive() ) { // m_nCurRefDlgId is not deglobalized so it can be set by other view // in LOK case when no ChildWindow for this view was detected -> fallback ScInputHandler* pHdl = GetInputHdl(); if ( pHdl ) bIsModal = pHdl->IsModalMode(pDocSh); } } else if (pDocSh) { ScInputHandler* pHdl = GetInputHdl(); if ( pHdl ) bIsModal = pHdl->IsModalMode(pDocSh); } return bIsModal; } bool ScModule::IsTableLocked() { //TODO: Move reference dialog handling to view // Just keep function autopilot here for references to other documents bool bLocked = false; // Up until now just for ScAnyRefDlg if ( m_nCurRefDlgId ) { SfxChildWindow* pChildWnd = lcl_GetChildWinFromAnyView( m_nCurRefDlgId ); if ( pChildWnd ) { if (pChildWnd->GetController()) { IAnyRefDialog* pRefDlg = dynamic_cast(pChildWnd->GetController().get()); assert(pRefDlg); if (pRefDlg) bLocked = pRefDlg->IsTableLocked(); } } else if (!comphelper::LibreOfficeKit::isActive()) bLocked = true; // for other views, see IsModalMode } // We can't stop LOK clients from switching part, and getting out of sync. assert(!bLocked || !comphelper::LibreOfficeKit::isActive()); return bLocked; } bool ScModule::IsRefDialogOpen() { //TODO: Move reference dialog handling to view // Just keep function autopilot here for references to other documents bool bIsOpen = false; if ( m_nCurRefDlgId ) { SfxChildWindow* pChildWnd = lcl_GetChildWinFromCurrentView( m_nCurRefDlgId ); if ( pChildWnd ) bIsOpen = pChildWnd->IsVisible(); } return bIsOpen; } bool ScModule::IsFormulaMode() { //TODO: Move reference dialog handling to view // Just keep function autopilot here for references to other documents bool bIsFormula = false; if ( m_nCurRefDlgId ) { SfxChildWindow* pChildWnd = nullptr; if ( comphelper::LibreOfficeKit::isActive() ) pChildWnd = lcl_GetChildWinFromCurrentView( m_nCurRefDlgId ); else pChildWnd = lcl_GetChildWinFromAnyView( m_nCurRefDlgId ); if ( pChildWnd ) { if (pChildWnd->GetController()) { IAnyRefDialog* pRefDlg = dynamic_cast(pChildWnd->GetController().get()); assert(pRefDlg); bIsFormula = pChildWnd->IsVisible() && pRefDlg && pRefDlg->IsRefInputMode(); } } else if ( comphelper::LibreOfficeKit::isActive() ) { // m_nCurRefDlgId is not deglobalized so it can be set by other view // in LOK case when no ChildWindow for this view was detected -> fallback ScInputHandler* pHdl = GetInputHdl(); if ( pHdl ) bIsFormula = pHdl->IsFormulaMode(); } } else { ScInputHandler* pHdl = GetInputHdl(); if ( pHdl ) bIsFormula = pHdl->IsFormulaMode(); } if (m_bIsInEditCommand) bIsFormula = true; return bIsFormula; } static void lcl_MarkedTabs( const ScMarkData& rMark, SCTAB& rStartTab, SCTAB& rEndTab ) { if (rMark.GetSelectCount() > 1) { rEndTab = rMark.GetLastSelected(); rStartTab = rMark.GetFirstSelected(); } } void ScModule::SetReference( const ScRange& rRef, ScDocument& rDoc, const ScMarkData* pMarkData ) { //TODO: Move reference dialog handling to view // Just keep function autopilot here for references to other documents // In RefDialogs we also trigger the ZoomIn, if the Ref's Start and End are different ScRange aNew = rRef; aNew.PutInOrder(); // Always in the right direction if( m_nCurRefDlgId ) { SfxChildWindow* pChildWnd = nullptr; if ( comphelper::LibreOfficeKit::isActive() ) pChildWnd = lcl_GetChildWinFromCurrentView( m_nCurRefDlgId ); else pChildWnd = lcl_GetChildWinFromAnyView( m_nCurRefDlgId ); OSL_ENSURE( pChildWnd, "NoChildWin" ); if ( pChildWnd ) { if ( m_nCurRefDlgId == SID_OPENDLG_CONSOLIDATE && pMarkData ) { SCTAB nStartTab = aNew.aStart.Tab(); SCTAB nEndTab = aNew.aEnd.Tab(); lcl_MarkedTabs( *pMarkData, nStartTab, nEndTab ); aNew.aStart.SetTab(nStartTab); aNew.aEnd.SetTab(nEndTab); } if (pChildWnd->GetController()) { IAnyRefDialog* pRefDlg = dynamic_cast(pChildWnd->GetController().get()); assert(pRefDlg); if(pRefDlg) { // hide the (color) selection now instead of later from LoseFocus, // don't abort the ref input that causes this call (bDoneRefMode = sal_False) pRefDlg->HideReference( false ); pRefDlg->SetReference( aNew, rDoc ); } } } else if ( comphelper::LibreOfficeKit::isActive() ) { // m_nCurRefDlgId is not deglobalized so it can be set by other view // in LOK case when no ChildWindow for this view was detected -> fallback ScInputHandler* pHdl = GetInputHdl(); if (pHdl) pHdl->SetReference( aNew, rDoc ); } } else { ScInputHandler* pHdl = GetInputHdl(); if (pHdl) pHdl->SetReference( aNew, rDoc ); else { OSL_FAIL("SetReference without receiver"); } } } /** * Multiple selection */ void ScModule::AddRefEntry() { //TODO: Move reference dialog handling to view // Just keep function autopilot here for references to other documents if ( m_nCurRefDlgId ) { SfxChildWindow* pChildWnd = lcl_GetChildWinFromAnyView( m_nCurRefDlgId ); OSL_ENSURE( pChildWnd, "NoChildWin" ); if ( pChildWnd ) { if (pChildWnd->GetController()) { IAnyRefDialog* pRefDlg = dynamic_cast(pChildWnd->GetController().get()); assert(pRefDlg); if (pRefDlg) { pRefDlg->AddRefEntry(); } } } } else { ScInputHandler* pHdl = GetInputHdl(); if (pHdl) pHdl->AddRefEntry(); } } void ScModule::EndReference() { //TODO: Move reference dialog handling to view // Just keep function autopilot here for references to other documents // We also annul the ZoomIn again in RefDialogs //FIXME: ShowRefFrame at InputHdl, if the Function AutoPilot is open? if ( !m_nCurRefDlgId ) return; SfxChildWindow* pChildWnd = nullptr; if ( comphelper::LibreOfficeKit::isActive() ) pChildWnd = lcl_GetChildWinFromCurrentView( m_nCurRefDlgId ); else pChildWnd = lcl_GetChildWinFromAnyView( m_nCurRefDlgId ); OSL_ENSURE( pChildWnd, "NoChildWin" ); if ( pChildWnd ) { if (pChildWnd->GetController()) { IAnyRefDialog* pRefDlg = dynamic_cast(pChildWnd->GetController().get()); assert(pRefDlg); if(pRefDlg) { pRefDlg->SetActive(); } } } } /** * Idle/OnlineSpelling */ void ScModule::AnythingChanged() { sal_uInt64 nOldTime = m_aIdleTimer.GetTimeout(); if ( nOldTime != SC_IDLE_MIN ) m_aIdleTimer.SetTimeout( SC_IDLE_MIN ); nIdleCount = 0; } static void lcl_CheckNeedsRepaint( const ScDocShell* pDocShell ) { SfxViewFrame* pFrame = SfxViewFrame::GetFirst( pDocShell ); while ( pFrame ) { SfxViewShell* p = pFrame->GetViewShell(); ScTabViewShell* pViewSh = dynamic_cast< ScTabViewShell *>( p ); if ( pViewSh ) pViewSh->CheckNeedsRepaint(); pFrame = SfxViewFrame::GetNext( *pFrame, pDocShell ); } } IMPL_LINK_NOARG(ScModule, IdleHandler, Timer *, void) { if ( Application::AnyInput( VclInputFlags::MOUSE | VclInputFlags::KEYBOARD ) ) { m_aIdleTimer.Start(); // Timeout unchanged return; } bool bMore = false; ScDocShell* pDocSh = dynamic_cast(SfxObjectShell::Current()); if ( pDocSh ) { ScDocument& rDoc = pDocSh->GetDocument(); sc::DocumentLinkManager& rLinkMgr = rDoc.GetDocLinkManager(); bool bLinks = rLinkMgr.idleCheckLinks(); bool bWidth = rDoc.IdleCalcTextWidth(); bMore = bLinks || bWidth; // Still something at all? // While calculating a Basic formula, a paint event may have occurred, // so check the bNeedsRepaint flags for this document's views if (bWidth) lcl_CheckNeedsRepaint( pDocSh ); } sal_uInt64 nOldTime = m_aIdleTimer.GetTimeout(); sal_uInt64 nNewTime = nOldTime; if ( bMore ) { nNewTime = SC_IDLE_MIN; nIdleCount = 0; } else { // Set SC_IDLE_COUNT to initial Timeout - increase afterwards if ( nIdleCount < SC_IDLE_COUNT ) ++nIdleCount; else { nNewTime += SC_IDLE_STEP; if ( nNewTime > SC_IDLE_MAX ) nNewTime = SC_IDLE_MAX; } } if ( nNewTime != nOldTime ) m_aIdleTimer.SetTimeout( nNewTime ); m_aIdleTimer.Start(); } /** * Virtual methods for the OptionsDialog */ std::optional ScModule::CreateItemSet( sal_uInt16 nId ) { std::optional pRet; if(SID_SC_EDITOPTIONS == nId) { pRet.emplace( GetPool(), svl::Items< // TP_USERLISTS: SCITEM_USERLIST, SCITEM_USERLIST, // TP_GRID: SID_ATTR_GRID_OPTIONS, SID_ATTR_GRID_OPTIONS, SID_ATTR_METRIC, SID_ATTR_METRIC, SID_ATTR_DEFTABSTOP, SID_ATTR_DEFTABSTOP, // TP_INPUT: SID_SC_INPUT_LEGACY_CELL_SELECTION, SID_SC_OPT_SORT_REF_UPDATE, // TP_FORMULA, TP_DEFAULTS: SID_SCFORMULAOPTIONS, SID_SCDEFAULTSOPTIONS, // TP_VIEW, TP_CALC: SID_SCVIEWOPTIONS, SID_SCDOCOPTIONS, // TP_INPUT: SID_SC_INPUT_ENTER_PASTE_MODE, SID_SC_INPUT_ENTER_PASTE_MODE, // TP_PRINT: SID_SCPRINTOPTIONS, SID_SCPRINTOPTIONS, // TP_INPUT: SID_SC_INPUT_SELECTION, SID_SC_INPUT_MARK_HEADER, SID_SC_INPUT_TEXTWYSIWYG, SID_SC_INPUT_TEXTWYSIWYG, SID_SC_INPUT_REPLCELLSWARN, SID_SC_INPUT_REPLCELLSWARN, // TP_VIEW: SID_SC_OPT_SYNCZOOM, SID_SC_OPT_KEY_BINDING_COMPAT>); const ScAppOptions& rAppOpt = GetAppOptions(); ScDocShell* pDocSh = dynamic_cast< ScDocShell *>( SfxObjectShell::Current() ); ScDocOptions aCalcOpt = pDocSh ? pDocSh->GetDocument().GetDocOptions() : GetDocOptions(); ScTabViewShell* pViewSh = dynamic_cast< ScTabViewShell *>( SfxViewShell::Current() ); ScViewOptions aViewOpt = pViewSh ? pViewSh->GetViewData().GetOptions() : GetViewOptions(); ScUserListItem aULItem( SCITEM_USERLIST ); ScUserList* pUL = ScGlobal::GetUserList(); // SfxGetpApp()->GetOptions( aSet ); pRet->Put( SfxUInt16Item( SID_ATTR_METRIC, sal::static_int_cast(rAppOpt.GetAppMetric()) ) ); // TP_CALC pRet->Put( SfxUInt16Item( SID_ATTR_DEFTABSTOP, aCalcOpt.GetTabDistance())); pRet->Put( ScTpCalcItem( SID_SCDOCOPTIONS, aCalcOpt ) ); // TP_VIEW pRet->Put( ScTpViewItem( aViewOpt ) ); pRet->Put( SfxBoolItem( SID_SC_OPT_SYNCZOOM, rAppOpt.GetSynchronizeZoom() ) ); // TP_INPUT const ScInputOptions& rInpOpt = GetInputOptions(); pRet->Put( SfxUInt16Item( SID_SC_INPUT_SELECTIONPOS, rInpOpt.GetMoveDir() ) ); pRet->Put( SfxBoolItem( SID_SC_INPUT_SELECTION, rInpOpt.GetMoveSelection() ) ); pRet->Put( SfxBoolItem( SID_SC_INPUT_EDITMODE, rInpOpt.GetEnterEdit() ) ); pRet->Put( SfxBoolItem( SID_SC_INPUT_FMT_EXPAND, rInpOpt.GetExtendFormat() ) ); pRet->Put( SfxBoolItem( SID_SC_INPUT_RANGEFINDER, rInpOpt.GetRangeFinder() ) ); pRet->Put( SfxBoolItem( SID_SC_INPUT_REF_EXPAND, rInpOpt.GetExpandRefs() ) ); pRet->Put( SfxBoolItem(SID_SC_OPT_SORT_REF_UPDATE, rInpOpt.GetSortRefUpdate())); pRet->Put( SfxBoolItem( SID_SC_INPUT_MARK_HEADER, rInpOpt.GetMarkHeader() ) ); pRet->Put( SfxBoolItem( SID_SC_INPUT_TEXTWYSIWYG, rInpOpt.GetTextWysiwyg() ) ); pRet->Put( SfxBoolItem( SID_SC_INPUT_REPLCELLSWARN, rInpOpt.GetReplaceCellsWarn() ) ); pRet->Put( SfxBoolItem( SID_SC_INPUT_LEGACY_CELL_SELECTION, rInpOpt.GetLegacyCellSelection() ) ); pRet->Put( SfxBoolItem( SID_SC_INPUT_ENTER_PASTE_MODE, rInpOpt.GetEnterPasteMode() ) ); // RID_SC_TP_PRINT pRet->Put( ScTpPrintItem( GetPrintOptions() ) ); // TP_GRID pRet->Put( aViewOpt.CreateGridItem() ); // TP_USERLISTS if ( pUL ) { aULItem.SetUserList( *pUL ); pRet->Put(aULItem); } // TP_COMPATIBILITY pRet->Put( SfxUInt16Item( SID_SC_OPT_KEY_BINDING_COMPAT, rAppOpt.GetKeyBindingType() ) ); // TP_DEFAULTS pRet->Put( ScTpDefaultsItem( GetDefaultsOptions() ) ); // TP_FORMULA ScFormulaOptions aOptions = GetFormulaOptions(); if (pDocSh) { ScCalcConfig aConfig( aOptions.GetCalcConfig()); aConfig.MergeDocumentSpecific( pDocSh->GetDocument().GetCalcConfig()); aOptions.SetCalcConfig( aConfig); } pRet->Put( ScTpFormulaItem( aOptions ) ); } return pRet; } void ScModule::ApplyItemSet( sal_uInt16 nId, const SfxItemSet& rSet ) { if(SID_SC_EDITOPTIONS == nId) { ModifyOptions( rSet ); } } std::unique_ptr ScModule::CreateTabPage( sal_uInt16 nId, weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rSet ) { std::unique_ptr xRet; ScAbstractDialogFactory* pFact = ScAbstractDialogFactory::Create(); switch(nId) { case SID_SC_TP_LAYOUT: { ::CreateTabPage ScTpLayoutOptionsCreate = pFact->GetTabPageCreatorFunc(SID_SC_TP_LAYOUT); if (ScTpLayoutOptionsCreate) xRet = (*ScTpLayoutOptionsCreate)(pPage, pController, &rSet); break; } case SID_SC_TP_CONTENT: { ::CreateTabPage ScTpContentOptionsCreate = pFact->GetTabPageCreatorFunc(SID_SC_TP_CONTENT); if (ScTpContentOptionsCreate) xRet = (*ScTpContentOptionsCreate)(pPage, pController, &rSet); break; } case SID_SC_TP_GRID: xRet = SvxGridTabPage::Create(pPage, pController, rSet); break; case SID_SC_TP_USERLISTS: { ::CreateTabPage ScTpUserListsCreate = pFact->GetTabPageCreatorFunc(SID_SC_TP_USERLISTS); if (ScTpUserListsCreate) xRet = (*ScTpUserListsCreate)(pPage, pController, &rSet); break; } case SID_SC_TP_CALC: { ::CreateTabPage ScTpCalcOptionsCreate = pFact->GetTabPageCreatorFunc(SID_SC_TP_CALC); if (ScTpCalcOptionsCreate) xRet = (*ScTpCalcOptionsCreate)(pPage, pController, &rSet); break; } case SID_SC_TP_FORMULA: { ::CreateTabPage ScTpFormulaOptionsCreate = pFact->GetTabPageCreatorFunc(SID_SC_TP_FORMULA); if (ScTpFormulaOptionsCreate) xRet = (*ScTpFormulaOptionsCreate)(pPage, pController, &rSet); break; } case SID_SC_TP_COMPATIBILITY: { ::CreateTabPage ScTpCompatOptionsCreate = pFact->GetTabPageCreatorFunc(SID_SC_TP_COMPATIBILITY); if (ScTpCompatOptionsCreate) xRet = (*ScTpCompatOptionsCreate)(pPage, pController, &rSet); break; } case SID_SC_TP_CHANGES: { ::CreateTabPage ScRedlineOptionsTabPageCreate = pFact->GetTabPageCreatorFunc(SID_SC_TP_CHANGES); if (ScRedlineOptionsTabPageCreate) xRet =(*ScRedlineOptionsTabPageCreate)(pPage, pController, &rSet); break; } case RID_SC_TP_PRINT: { ::CreateTabPage ScTpPrintOptionsCreate = pFact->GetTabPageCreatorFunc(RID_SC_TP_PRINT); if (ScTpPrintOptionsCreate) xRet = (*ScTpPrintOptionsCreate)(pPage, pController, &rSet); break; } case RID_SC_TP_DEFAULTS: { ::CreateTabPage ScTpDefaultsOptionsCreate = pFact->GetTabPageCreatorFunc(RID_SC_TP_DEFAULTS); if (ScTpDefaultsOptionsCreate) xRet = (*ScTpDefaultsOptionsCreate)(pPage, pController, &rSet); break; } } OSL_ENSURE( xRet, "ScModule::CreateTabPage(): no valid ID for TabPage!" ); return xRet; } IMPL_LINK( ScModule, CalcFieldValueHdl, EditFieldInfo*, pInfo, void ) { //TODO: Merge with ScFieldEditEngine! if (!pInfo) return; const SvxFieldItem& rField = pInfo->GetField(); const SvxFieldData* pField = rField.GetField(); if (const SvxURLField* pURLField = dynamic_cast(pField)) { // URLField const OUString& aURL = pURLField->GetURL(); switch ( pURLField->GetFormat() ) { case SvxURLFormat::AppDefault: //TODO: Settable in the App? case SvxURLFormat::Repr: { pInfo->SetRepresentation( pURLField->GetRepresentation() ); } break; case SvxURLFormat::Url: { pInfo->SetRepresentation( aURL ); } break; } svtools::ColorConfigEntry eEntry = INetURLHistory::GetOrCreate()->QueryUrl( aURL ) ? svtools::LINKSVISITED : svtools::LINKS; pInfo->SetTextColor( GetColorConfig().GetColorValue(eEntry).nColor ); } else { OSL_FAIL("Unknown Field"); pInfo->SetRepresentation(OUString('?')); } } void ScModule::RegisterRefController(sal_uInt16 nSlotId, std::shared_ptr& rWnd, weld::Window* pWndAncestor) { std::vector, weld::Window*>> & rlRefWindow = m_mapRefController[nSlotId]; if (std::find_if(rlRefWindow.begin(), rlRefWindow.end(), [rWnd](const std::pair, weld::Window*>& rCandidate) { return rCandidate.first.get() == rWnd.get(); }) == rlRefWindow.end()) { rlRefWindow.emplace_back(rWnd, pWndAncestor); } } void ScModule::UnregisterRefController(sal_uInt16 nSlotId, const std::shared_ptr& rWnd) { auto iSlot = m_mapRefController.find( nSlotId ); if( iSlot == m_mapRefController.end() ) return; std::vector, weld::Window*>> & rlRefWindow = iSlot->second; auto i = std::find_if(rlRefWindow.begin(), rlRefWindow.end(), [rWnd](const std::pair, weld::Window*>& rCandidate) { return rCandidate.first.get() == rWnd.get(); }); if( i == rlRefWindow.end() ) return; rlRefWindow.erase( i ); if( rlRefWindow.empty() ) m_mapRefController.erase( nSlotId ); } std::shared_ptr ScModule::Find1RefWindow(sal_uInt16 nSlotId, const weld::Window *pWndAncestor) { if (!pWndAncestor) return nullptr; auto iSlot = m_mapRefController.find( nSlotId ); if( iSlot == m_mapRefController.end() ) return nullptr; std::vector, weld::Window*>> & rlRefWindow = iSlot->second; for (auto const& refWindow : rlRefWindow) if ( refWindow.second == pWndAncestor ) return refWindow.first; return nullptr; } using namespace com::sun::star; constexpr OUStringLiteral LINGUPROP_AUTOSPELL = u"IsSpellAuto"; void ScModule::GetSpellSettings( LanguageType& rDefLang, LanguageType& rCjkLang, LanguageType& rCtlLang, bool& rAutoSpell ) { // use SvtLinguConfig instead of service LinguProperties to avoid // loading the linguistic component SvtLinguConfig aConfig; SvtLinguOptions aOptions; aConfig.GetOptions( aOptions ); rDefLang = MsLangId::resolveSystemLanguageByScriptType(aOptions.nDefaultLanguage, css::i18n::ScriptType::LATIN); rCjkLang = MsLangId::resolveSystemLanguageByScriptType(aOptions.nDefaultLanguage_CJK, css::i18n::ScriptType::ASIAN); rCtlLang = MsLangId::resolveSystemLanguageByScriptType(aOptions.nDefaultLanguage_CTL, css::i18n::ScriptType::COMPLEX); rAutoSpell = aOptions.bIsSpellAuto; } void ScModule::SetAutoSpellProperty( bool bSet ) { // use SvtLinguConfig instead of service LinguProperties to avoid // loading the linguistic component SvtLinguConfig aConfig; aConfig.SetProperty( LINGUPROP_AUTOSPELL, uno::Any(bSet) ); } bool ScModule::HasThesaurusLanguage( LanguageType nLang ) { if ( nLang == LANGUAGE_NONE ) return false; bool bHasLang = false; try { uno::Reference< linguistic2::XThesaurus > xThes(LinguMgr::GetThesaurus()); if ( xThes.is() ) bHasLang = xThes->hasLocale( LanguageTag::convertToLocale( nLang ) ); } catch( uno::Exception& ) { OSL_FAIL("Error in Thesaurus"); } return bHasLang; } std::optional ScModule::CreateStyleFamilies() { SfxStyleFamilies aStyleFamilies; aStyleFamilies.emplace_back(SfxStyleFamilyItem(SfxStyleFamily::Para, ScResId(STR_STYLE_FAMILY_CELL), BMP_STYLES_FAMILY_CELL, RID_CELLSTYLEFAMILY, SC_MOD()->GetResLocale())); aStyleFamilies.emplace_back(SfxStyleFamilyItem(SfxStyleFamily::Page, ScResId(STR_STYLE_FAMILY_PAGE), BMP_STYLES_FAMILY_PAGE, RID_PAGESTYLEFAMILY, SC_MOD()->GetResLocale())); return aStyleFamilies; } void ScModule::RegisterAutomationApplicationEventsCaller(css::uno::Reference< ooo::vba::XSinkCaller > const& xCaller) { mxAutomationApplicationEventsCaller = xCaller; } void ScModule::CallAutomationApplicationEventSinks(const OUString& Method, css::uno::Sequence< css::uno::Any >& Arguments) { if (mxAutomationApplicationEventsCaller.is()) mxAutomationApplicationEventsCaller->CallSinks(Method, Arguments); } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */