/* -*- 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 ScFormulaReferenceHelper::ScFormulaReferenceHelper(IAnyRefDialog* _pDlg,SfxBindings* _pBindings) : m_pDlg(_pDlg) , m_pRefEdit (nullptr) , m_pRefBtn (nullptr) , m_pDialog(nullptr) , m_pBindings(_pBindings) , m_nRefTab(0) , m_bHighlightRef(false) { ScInputOptions aInputOption=SC_MOD()->GetInputOptions(); m_bEnableColorRef=aInputOption.GetRangeFinder(); } ScFormulaReferenceHelper::~ScFormulaReferenceHelper() COVERITY_NOEXCEPT_FALSE { dispose(); } void ScFormulaReferenceHelper::dispose() { // common cleanup for ScAnyRefDlg and ScFormulaDlg is done here HideReference(); enableInput( true ); ScInputHandler* pInputHdl = SC_MOD()->GetInputHdl(); if ( pInputHdl ) pInputHdl->ResetDelayTimer(); // stop the timer for disabling the input line m_pDialog = nullptr; } void ScFormulaReferenceHelper::enableInput( bool bEnable ) { ScDocShell* pDocShell = static_cast(SfxObjectShell::GetFirst(checkSfxObjectShell)); while( pDocShell ) { SfxViewFrame* pFrame = SfxViewFrame::GetFirst( pDocShell ); while( pFrame ) { // enable everything except InPlace, including bean frames if ( !pFrame->GetFrame().IsInPlace() ) { SfxViewShell* p = pFrame->GetViewShell(); ScTabViewShell* pViewSh = dynamic_cast< ScTabViewShell *>( p ); if(pViewSh!=nullptr) { vcl::Window *pWin=pViewSh->GetWindow(); if(pWin) { vcl::Window *pParent=pWin->GetParent(); if(pParent) { pParent->EnableInput(bEnable); pViewSh->EnableRefInput(bEnable); } } } } pFrame = SfxViewFrame::GetNext( *pFrame, pDocShell ); } pDocShell = static_cast(SfxObjectShell::GetNext(*pDocShell, checkSfxObjectShell)); } } void ScFormulaReferenceHelper::ShowSimpleReference(std::u16string_view rStr) { if (!m_bEnableColorRef) return; m_bHighlightRef = true; ScViewData* pViewData=ScDocShell::GetViewData(); if ( !pViewData ) return; ScDocument& rDoc = pViewData->GetDocument(); ScTabViewShell* pTabViewShell=pViewData->GetViewShell(); ScRangeList aRangeList; pTabViewShell->DoneRefMode(); pTabViewShell->ClearHighlightRanges(); if( ParseWithNames( aRangeList, rStr, rDoc ) ) { for ( size_t i = 0, nRanges = aRangeList.size(); i < nRanges; ++i ) { ScRange const & rRangeEntry = aRangeList[ i ]; Color aColName = ScRangeFindList::GetColorName( i ); pTabViewShell->AddHighlightRange( rRangeEntry, aColName ); } } } bool ScFormulaReferenceHelper::ParseWithNames( ScRangeList& rRanges, std::u16string_view rStr, const ScDocument& rDoc ) { rRanges.RemoveAll(); if (rStr.empty()) return true; ScAddress::Details aDetails(rDoc.GetAddressConvention(), 0, 0); bool bError = false; sal_Int32 nIdx {0}; do { ScRange aRange; OUString aRangeStr( o3tl::getToken(rStr, 0, ';', nIdx ) ); ScRefFlags nFlags = aRange.ParseAny( aRangeStr, rDoc, aDetails ); if ( nFlags & ScRefFlags::VALID ) { if ( (nFlags & ScRefFlags::TAB_3D) == ScRefFlags::ZERO ) aRange.aStart.SetTab( m_nRefTab ); if ( (nFlags & ScRefFlags::TAB2_3D) == ScRefFlags::ZERO ) aRange.aEnd.SetTab( aRange.aStart.Tab() ); rRanges.push_back( aRange ); } else if ( ScRangeUtil::MakeRangeFromName( aRangeStr, rDoc, m_nRefTab, aRange, RUTL_NAMES, aDetails ) ) rRanges.push_back( aRange ); else bError = true; } while (nIdx>0); return !bError; } void ScFormulaReferenceHelper::ShowFormulaReference(const OUString& rStr) { if( !m_bEnableColorRef) return; m_bHighlightRef=true; ScViewData* pViewData=ScDocShell::GetViewData(); if ( !(pViewData && m_pRefComp) ) return; ScTabViewShell* pTabViewShell=pViewData->GetViewShell(); SCCOL nCol = pViewData->GetCurX(); SCROW nRow = pViewData->GetCurY(); SCTAB nTab = pViewData->GetTabNo(); ScAddress aPos( nCol, nRow, nTab ); std::unique_ptr pScTokA(m_pRefComp->CompileString(rStr)); if (!(pTabViewShell && pScTokA)) return; const ScViewData& rViewData = pTabViewShell->GetViewData(); ScDocument& rDoc = rViewData.GetDocument(); pTabViewShell->DoneRefMode(); pTabViewShell->ClearHighlightRanges(); formula::FormulaTokenArrayPlainIterator aIter(*pScTokA); const formula::FormulaToken* pToken = aIter.GetNextReference(); sal_uInt16 nIndex=0; while(pToken!=nullptr) { bool bDoubleRef=(pToken->GetType()==formula::svDoubleRef); if(pToken->GetType()==formula::svSingleRef || bDoubleRef) { ScRange aRange; if(bDoubleRef) { ScComplexRefData aRef( *pToken->GetDoubleRef() ); aRange = aRef.toAbs(rDoc, aPos); } else { ScSingleRefData aRef( *pToken->GetSingleRef() ); aRange.aStart = aRef.toAbs(rDoc, aPos); aRange.aEnd = aRange.aStart; } Color aColName=ScRangeFindList::GetColorName(nIndex++); pTabViewShell->AddHighlightRange(aRange, aColName); } pToken = aIter.GetNextReference(); } } void ScFormulaReferenceHelper::HideReference( bool bDoneRefMode ) { ScViewData* pViewData=ScDocShell::GetViewData(); if( !(pViewData && m_bHighlightRef && m_bEnableColorRef)) return; ScTabViewShell* pTabViewShell=pViewData->GetViewShell(); if(pTabViewShell!=nullptr) { // bDoneRefMode is sal_False when called from before SetReference. // In that case, RefMode was just started and must not be ended now. if ( bDoneRefMode ) pTabViewShell->DoneRefMode(); pTabViewShell->ClearHighlightRanges(); if( comphelper::LibreOfficeKit::isActive() ) { // Clear std::vector aReferenceMarks; ScInputHandler::SendReferenceMarks( pTabViewShell, aReferenceMarks ); } } m_bHighlightRef=false; } void ScFormulaReferenceHelper::ShowReference(const OUString& rStr) { if( !m_bEnableColorRef ) return; // Exclude ';' semicolon as it is the separator for ParseWithNames() used // in ShowSimpleReference(). Also sheet separator '.' dot is part of simple // reference (could be array col/row separator as well but then in '{' '}' // braces). Prefer '!' exclamation mark to be intersection operator rather // than Excel sheet separator. if (comphelper::string::indexOfAny( rStr, u"()+-*/^%&=<>~! #[]{},|\\@", 0) != -1) { ShowFormulaReference(rStr); } else { ShowSimpleReference(rStr); } } void ScFormulaReferenceHelper::ReleaseFocus( formula::RefEdit* pEdit ) { if( !m_pRefEdit && pEdit ) { m_pDlg->RefInputStart( pEdit ); } ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell(); if( !pViewShell ) return; pViewShell->ActiveGrabFocus(); if( !m_pRefEdit ) return; const ScViewData& rViewData = pViewShell->GetViewData(); ScDocument& rDoc = rViewData.GetDocument(); ScRangeList aRangeList; if( !ParseWithNames( aRangeList, m_pRefEdit->GetText(), rDoc ) ) return; if ( !aRangeList.empty() ) { const ScRange & rRange = aRangeList.front(); pViewShell->SetTabNo( rRange.aStart.Tab() ); pViewShell->MoveCursorAbs( rRange.aStart.Col(), rRange.aStart.Row(), SC_FOLLOW_JUMP, false, false ); pViewShell->MoveCursorAbs( rRange.aEnd.Col(), rRange.aEnd.Row(), SC_FOLLOW_JUMP, true, false ); m_pDlg->SetReference( rRange, rDoc ); } } void ScFormulaReferenceHelper::Init() { ScViewData* pViewData=ScDocShell::GetViewData(); //! use pScViewShell? if ( !pViewData ) return; ScDocument& rDoc = pViewData->GetDocument(); SCCOL nCol = pViewData->GetCurX(); SCROW nRow = pViewData->GetCurY(); SCTAB nTab = pViewData->GetTabNo(); ScAddress aCursorPos( nCol, nRow, nTab ); m_pRefComp.reset( new ScCompiler( rDoc, aCursorPos, rDoc.GetGrammar()) ); m_pRefComp->EnableJumpCommandReorder(false); m_pRefComp->EnableStopOnError(false); m_nRefTab = nTab; } IMPL_LINK_NOARG(ScFormulaReferenceHelper, ActivateHdl, weld::Widget&, bool) { if (m_pRefEdit) m_pRefEdit->GrabFocus(); m_pDlg->RefInputDone(true); return true; } void ScFormulaReferenceHelper::RefInputDone( bool bForced ) { if ( !CanInputDone( bForced ) ) return; if (!m_pDialog) return; // Adjust window title m_pDialog->set_title(m_sOldDialogText); if (m_pRefEdit) m_pRefEdit->SetActivateHdl(Link()); // set button image if (m_pRefBtn) { m_pRefBtn->SetActivateHdl(Link()); m_pRefBtn->SetStartImage(); } m_pDialog->undo_collapse(); m_pRefEdit = nullptr; m_pRefBtn = nullptr; } void ScFormulaReferenceHelper::RefInputStart( formula::RefEdit* pEdit, formula::RefButton* pButton ) { if (m_pRefEdit) return; m_pRefEdit = pEdit; m_pRefBtn = pButton; // Save and adjust window title m_sOldDialogText = m_pDialog->get_title(); if (weld::Label *pLabel = m_pRefEdit->GetLabelWidgetForShrinkMode()) { const OUString sLabel = pLabel->get_label(); if (!sLabel.isEmpty()) { const OUString sNewDialogText = m_sOldDialogText + ": " + comphelper::string::stripEnd(sLabel, ':'); m_pDialog->set_title(pLabel->strip_mnemonic(sNewDialogText)); } } m_pDialog->collapse(pEdit->GetWidget(), pButton ? pButton->GetWidget() : nullptr); // set button image if (pButton) pButton->SetEndImage(); m_pRefEdit->SetActivateHdl(LINK(this, ScFormulaReferenceHelper, ActivateHdl)); if (m_pRefBtn) m_pRefBtn->SetActivateHdl(LINK(this, ScFormulaReferenceHelper, ActivateHdl)); } void ScFormulaReferenceHelper::ToggleCollapsed( formula::RefEdit* pEdit, formula::RefButton* pButton ) { if( !pEdit ) return; if( m_pRefEdit == pEdit ) // is this the active ref edit field? { m_pRefEdit->GrabFocus(); // before RefInputDone() m_pDlg->RefInputDone( true ); // finish ref input } else { m_pDlg->RefInputDone( true ); // another active ref edit? m_pDlg->RefInputStart( pEdit, pButton ); // start ref input // pRefEdit might differ from pEdit after RefInputStart() (i.e. ScFormulaDlg) if( m_pRefEdit ) m_pRefEdit->GrabFocus(); } } void ScFormulaReferenceHelper::DoClose( sal_uInt16 nId ) { SfxApplication* pSfxApp = SfxGetpApp(); SetDispatcherLock( false ); //! here and in dtor ? SfxViewFrame* pViewFrm = SfxViewFrame::Current(); if ( pViewFrm && pViewFrm->HasChildWindow(FID_INPUTLINE_STATUS) ) { // The input row is disabled with ToolBox::Disable disabled, thus it must be // reenabled with ToolBox::Enable (before the AppWindow is enabled) // for the buttons to be drawn as enabled. SfxChildWindow* pChild = pViewFrm->GetChildWindow(FID_INPUTLINE_STATUS); if (pChild) { ScInputWindow* pWin = static_cast(pChild->GetWindow()); pWin->Enable(); } } // find parent view frame to close dialog SfxViewFrame* pMyViewFrm = nullptr; if ( m_pBindings ) { SfxDispatcher* pMyDisp = m_pBindings->GetDispatcher(); if (pMyDisp) pMyViewFrm = pMyDisp->GetFrame(); } SC_MOD()->SetRefDialog( nId, false, pMyViewFrm ); pSfxApp->Broadcast( SfxHint( SfxHintId::ScKillEditView ) ); ScTabViewShell* pScViewShell = ScTabViewShell::GetActiveViewShell(); if ( pScViewShell ) pScViewShell->UpdateInputHandler(true); } void ScFormulaReferenceHelper::SetDispatcherLock( bool bLock ) { if (!comphelper::LibreOfficeKit::isActive()) { // lock / unlock only the dispatchers of Calc documents ScDocShell* pDocShell = static_cast(SfxObjectShell::GetFirst(checkSfxObjectShell)); while (pDocShell) { SfxViewFrame* pFrame = SfxViewFrame::GetFirst(pDocShell); while (pFrame) { SfxDispatcher* pDisp = pFrame->GetDispatcher(); if (pDisp) pDisp->Lock(bLock); pFrame = SfxViewFrame::GetNext(*pFrame, pDocShell); } pDocShell = static_cast(SfxObjectShell::GetNext(*pDocShell, checkSfxObjectShell)); } return; // if a new view is created while the dialog is open, // that view's dispatcher is locked when trying to create the dialog // for that view (ScTabViewShell::CreateRefDialog) } // lock / unlock only the dispatcher of Calc document SfxDispatcher* pDisp = nullptr; if ( m_pBindings ) { pDisp = m_pBindings->GetDispatcher(); } else if(SfxViewFrame* pViewFrame = SfxViewFrame::Current()) { if (dynamic_cast< ScTabViewShell* >(pViewFrame->GetViewShell())) pDisp = pViewFrame->GetDispatcher(); } if (pDisp) pDisp->Lock(bLock); } void ScFormulaReferenceHelper::ViewShellChanged() { enableInput( false ); EnableSpreadsheets(); } void ScFormulaReferenceHelper::EnableSpreadsheets(bool bFlag) { ScDocShell* pDocShell = static_cast(SfxObjectShell::GetFirst(checkSfxObjectShell)); while( pDocShell ) { SfxViewFrame* pFrame = SfxViewFrame::GetFirst( pDocShell ); while( pFrame ) { // enable everything except InPlace, including bean frames if ( !pFrame->GetFrame().IsInPlace() ) { SfxViewShell* p = pFrame->GetViewShell(); ScTabViewShell* pViewSh = dynamic_cast< ScTabViewShell *>( p ); if(pViewSh!=nullptr) { vcl::Window *pWin=pViewSh->GetWindow(); if(pWin) { vcl::Window *pParent=pWin->GetParent(); if(pParent) { pParent->EnableInput(bFlag,false); pViewSh->EnableRefInput(bFlag); } } } } pFrame = SfxViewFrame::GetNext( *pFrame, pDocShell ); } pDocShell = static_cast(SfxObjectShell::GetNext(*pDocShell, checkSfxObjectShell)); } } static void lcl_InvalidateWindows() { ScDocShell* pDocShell = static_cast(SfxObjectShell::GetFirst(checkSfxObjectShell)); while( pDocShell ) { SfxViewFrame* pFrame = SfxViewFrame::GetFirst( pDocShell ); while( pFrame ) { // enable everything except InPlace, including bean frames if ( !pFrame->GetFrame().IsInPlace() ) { SfxViewShell* p = pFrame->GetViewShell(); ScTabViewShell* pViewSh = dynamic_cast< ScTabViewShell *>( p ); if(pViewSh!=nullptr) { vcl::Window *pWin=pViewSh->GetWindow(); if(pWin) { vcl::Window *pParent=pWin->GetParent(); if(pParent) pParent->Invalidate(); } } } pFrame = SfxViewFrame::GetNext( *pFrame, pDocShell ); } pDocShell = static_cast(SfxObjectShell::GetNext(*pDocShell, checkSfxObjectShell)); } } static void lcl_HideAllReferences() { SfxViewShell* pSh = SfxViewShell::GetFirst( true, checkSfxViewShell ); while ( pSh ) { static_cast(pSh)->ClearHighlightRanges(); pSh = SfxViewShell::GetNext( *pSh, true, checkSfxViewShell ); } } ScRefHandler::ScRefHandler(SfxDialogController& rController, SfxBindings* pB, bool bBindRef) : m_pController(&rController) , m_bInRefMode(false) , m_aHelper(this, pB) , m_pMyBindings(pB) { m_aHelper.SetDialog(rController.getDialog()); if( bBindRef ) EnterRefMode(); } bool ScRefHandler::EnterRefMode() { if( m_bInRefMode ) return false; SC_MOD()->InputEnterHandler(); ScTabViewShell* pScViewShell = nullptr; // title has to be from the view that opened the dialog, // even if it's not the current view SfxObjectShell* pParentDoc = nullptr; if ( m_pMyBindings ) { SfxDispatcher* pMyDisp = m_pMyBindings->GetDispatcher(); if (pMyDisp) { SfxViewFrame* pMyViewFrm = pMyDisp->GetFrame(); if (pMyViewFrm) { pScViewShell = dynamic_cast( pMyViewFrm->GetViewShell() ); if( pScViewShell ) pScViewShell->UpdateInputHandler(true); pParentDoc = pMyViewFrm->GetObjectShell(); } } } if ( !pParentDoc && pScViewShell ) // use current only if above fails pParentDoc = pScViewShell->GetObjectShell(); if ( pParentDoc ) m_aDocName = pParentDoc->GetTitle(); ScInputHandler* pInputHdl = SC_MOD()->GetInputHdl(pScViewShell); OSL_ENSURE( pInputHdl, "Missing input handler :-/" ); if ( pInputHdl ) pInputHdl->NotifyChange( nullptr ); ScFormulaReferenceHelper::enableInput( false ); ScFormulaReferenceHelper::EnableSpreadsheets(); m_aHelper.Init(); m_aHelper.SetDispatcherLock( true ); m_bInRefMode = true; return m_bInRefMode; } ScRefHandler::~ScRefHandler() COVERITY_NOEXCEPT_FALSE { disposeRefHandler(); } void ScRefHandler::disposeRefHandler() { m_pController = nullptr; LeaveRefMode(); m_aHelper.dispose(); } bool ScRefHandler::LeaveRefMode() { if( !m_bInRefMode ) return false; lcl_HideAllReferences(); SetDispatcherLock( false ); //! here and in DoClose ? ScTabViewShell* pScViewShell = ScTabViewShell::GetActiveViewShell(); if( pScViewShell ) pScViewShell->UpdateInputHandler(true); lcl_InvalidateWindows(); m_bInRefMode = false; return true; } void ScRefHandler::SwitchToDocument() { ScTabViewShell* pCurrent = ScTabViewShell::GetActiveViewShell(); if (pCurrent) { SfxObjectShell* pObjSh = pCurrent->GetObjectShell(); if ( pObjSh && pObjSh->GetTitle() == m_aDocName ) { // right document already visible -> nothing to do return; } } SfxViewShell* pSh = SfxViewShell::GetFirst( true, checkSfxViewShell ); while ( pSh ) { SfxObjectShell* pObjSh = pSh->GetObjectShell(); if ( pObjSh && pObjSh->GetTitle() == m_aDocName ) { // switch to first TabViewShell for document static_cast(pSh)->SetActive(); return; } pSh = SfxViewShell::GetNext( *pSh, true, checkSfxViewShell ); } } bool ScRefHandler::IsDocAllowed(SfxObjectShell* pDocSh) const // pDocSh may be 0 { // if aDocName isn't initialized, allow if ( m_aDocName.isEmpty() ) return true; if ( !pDocSh ) return false; // default: allow only same document (overridden in function dialog) return m_aDocName==pDocSh->GetTitle(); } bool ScRefHandler::IsRefInputMode() const { return m_pController->getDialog()->get_visible(); } bool ScRefHandler::DoClose( sal_uInt16 nId ) { m_aHelper.DoClose(nId); return true; } void ScRefHandler::SetDispatcherLock( bool bLock ) { m_aHelper.SetDispatcherLock( bLock ); } void ScRefHandler::ViewShellChanged() { ScFormulaReferenceHelper::ViewShellChanged(); } void ScRefHandler::AddRefEntry() { // override this for multi-references } bool ScRefHandler::IsTableLocked() const { // the default is that the sheet can be switched during while the reference is edited return false; } // RefInputStart/Done: Zoom-In (AutoHide) on single field // (using button or movement) void ScRefHandler::RefInputStart( formula::RefEdit* pEdit, formula::RefButton* pButton ) { m_aHelper.RefInputStart( pEdit, pButton ); } void ScRefHandler::ToggleCollapsed( formula::RefEdit* pEdit, formula::RefButton* pButton ) { m_aHelper.ToggleCollapsed( pEdit, pButton ); } bool ScRefHandler::ParseWithNames( ScRangeList& rRanges, std::u16string_view rStr, const ScDocument& rDoc ) { return m_aHelper.ParseWithNames( rRanges, rStr, rDoc ); } void ScRefHandler::HideReference( bool bDoneRefMode ) { m_aHelper.HideReference( bDoneRefMode ); } void ScRefHandler::ShowReference(const OUString& rStr) { m_aHelper.ShowReference(rStr); } void ScRefHandler::ReleaseFocus( formula::RefEdit* pEdit ) { m_aHelper.ReleaseFocus( pEdit ); } void ScRefHandler::RefInputDone( bool bForced ) { m_aHelper.RefInputDone( bForced ); } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */