diff options
Diffstat (limited to 'formula/source/ui/dlg/formula.cxx')
-rw-r--r-- | formula/source/ui/dlg/formula.cxx | 1955 |
1 files changed, 1955 insertions, 0 deletions
diff --git a/formula/source/ui/dlg/formula.cxx b/formula/source/ui/dlg/formula.cxx new file mode 100644 index 000000000..330217c80 --- /dev/null +++ b/formula/source/ui/dlg/formula.cxx @@ -0,0 +1,1955 @@ +/* -*- 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 <memory> +#include <sfx2/viewfrm.hxx> +#include <vcl/svapp.hxx> +#include <vcl/weld.hxx> + +#include <sal/log.hxx> + +#include <unotools/charclass.hxx> +#include <tools/diagnose_ex.h> + +#include "funcpage.hxx" +#include <formula/formula.hxx> +#include <formula/IFunctionDescription.hxx> +#include <formula/FormulaCompiler.hxx> +#include <formula/token.hxx> +#include <formula/tokenarray.hxx> +#include <formula/formdata.hxx> +#include <formula/formulahelper.hxx> +#include "structpg.hxx" +#include "parawin.hxx" +#include <strings.hrc> +#include <core_resource.hxx> +#include <com/sun/star/sheet/FormulaToken.hpp> +#include <com/sun/star/sheet/FormulaLanguage.hpp> +#include <com/sun/star/sheet/FormulaMapGroup.hpp> +#include <com/sun/star/sheet/FormulaMapGroupSpecialOffset.hpp> +#include <com/sun/star/sheet/XFormulaOpCodeMapper.hpp> +#include <com/sun/star/sheet/XFormulaParser.hpp> +#include <map> + +// For tab page +#define TOKEN_OPEN 0 +#define TOKEN_CLOSE 1 +#define TOKEN_SEP 2 +namespace formula +{ + +using namespace ::com::sun::star; + +class FormulaDlg_Impl +{ +public: + ::std::pair<RefButton*, RefEdit*> + RefInputStartBefore( RefEdit* pEdit, RefButton* pButton ); + void RefInputStartAfter(); + void RefInputDoneAfter( bool bForced ); + bool CalcValue( const OUString& rStrExp, OUString& rStrResult, bool bForceMatrixFormula = false ); + void CalcStruct( const OUString& rStrExp, bool bForceRecalcStruct = false ); + void UpdateValues( bool bForceRecalcStruct = false ); + void DeleteArgs(); + sal_Int32 GetFunctionPos(sal_Int32 nPos); + void ClearAllParas(); + + void MakeTree(StructPage* _pTree, weld::TreeIter* pParent, const FormulaToken* pFuncToken, + const FormulaToken* _pToken, long Count); + void fillTree(StructPage* _pTree); + void UpdateTokenArray( const OUString& rStrExp); + OUString RepairFormula(const OUString& aFormula); + void FillDialog(bool bFlag = true); + bool EditNextFunc( bool bForward, sal_Int32 nFStart = NOT_FOUND ); + void EditThisFunc(sal_Int32 nFStart); + + OUString GetPrevFuncExpression( bool bStartFromEnd ); + + void StoreFormEditData(FormEditData* pEditData); + + void Update(); + void Update(const OUString& _sExp); + + void SaveArg( sal_uInt16 nEd ); + void UpdateSelection(); + void DoEnter( bool bOk ); + void FillListboxes(); + void FillControls( bool &rbNext, bool &rbPrev); + + FormulaDlgMode SetMeText( const OUString& _sText, sal_Int32 PrivStart, sal_Int32 PrivEnd, bool bMatrix, bool _bSelect, bool _bUpdate); + void SetMeText(const OUString& _sText); + bool CheckMatrix(OUString& aFormula /*IN/OUT*/); + + void SetEdSelection(); + + bool UpdateParaWin(Selection& _rSelection); + void UpdateParaWin( const Selection& _rSelection, const OUString& _sRefStr); + + void SetData( sal_Int32 nFStart, sal_Int32 nNextFStart, sal_Int32 nNextFEnd, sal_Int32& PrivStart, sal_Int32& PrivEnd); + + RefEdit* GetCurrRefEdit(); + + const FormulaHelper& GetFormulaHelper() const { return m_aFormulaHelper;} + void InitFormulaOpCodeMapper(); + + void UpdateOldSel(); + void FormulaCursor(); + + DECL_LINK( ModifyHdl, ParaWin&, void ); + DECL_LINK( FxHdl, ParaWin&, void ); + + DECL_LINK( MatrixHdl, weld::Button&, void ); + DECL_LINK( FormulaHdl, weld::TextView&, void); + DECL_LINK( FormulaCursorHdl, weld::TextView&, void ); + DECL_LINK( BtnHdl, weld::Button&, void ); + DECL_LINK( DblClkHdl, FuncPage&, void ); + DECL_LINK( FuncSelHdl, FuncPage&, void ); + DECL_LINK( StructSelHdl, StructPage&, void ); +public: + mutable uno::Reference< sheet::XFormulaOpCodeMapper> m_xOpCodeMapper; + uno::Sequence< sheet::FormulaToken > m_aTokenList; + ::std::unique_ptr<FormulaTokenArray> m_pTokenArray; + ::std::unique_ptr<FormulaTokenArrayPlainIterator> m_pTokenArrayIterator; + mutable uno::Sequence< sheet::FormulaOpCodeMapEntry > m_aSpecialOpCodes; + mutable uno::Sequence< sheet::FormulaToken > m_aSeparatorsOpCodes; + mutable uno::Sequence< sheet::FormulaOpCodeMapEntry > m_aFunctionOpCodes; + mutable const sheet::FormulaOpCodeMapEntry* m_pFunctionOpCodesEnd; + ::std::map<const FormulaToken*, sheet::FormulaToken> m_aTokenMap; + IFormulaEditorHelper* m_pHelper; + weld::Dialog& m_rDialog; + + OUString m_aOldFormula; + bool m_bStructUpdate; + bool m_bUserMatrixFlag; + + const OUString m_aTitle1; + const OUString m_aTitle2; + FormulaHelper m_aFormulaHelper; + + OString m_aEditHelpId; + + OString m_aOldHelp; + bool m_bMakingTree; // in method of constructing tree + + bool m_bEditFlag; + const IFunctionDescription* m_pFuncDesc; + sal_Int32 m_nArgs; + ::std::vector< OUString > m_aArguments; + Selection m_aFuncSel; + + sal_Int32 m_nFuncExpStart; ///< current formula position for treeview results + + int m_nSelectionStart; + int m_nSelectionEnd; + + RefEdit* m_pTheRefEdit; + RefButton* m_pTheRefButton; + + std::unique_ptr<weld::Notebook> m_xTabCtrl; + std::unique_ptr<weld::Container> m_xParaWinBox; + std::unique_ptr<ParaWin> m_xParaWin; + std::unique_ptr<weld::Label> m_xFtHeadLine; + std::unique_ptr<weld::Label> m_xFtFuncName; + std::unique_ptr<weld::Label> m_xFtFuncDesc; + + std::unique_ptr<weld::Label> m_xFtEditName; + + std::unique_ptr<weld::Label> m_xFtResult; + std::unique_ptr<weld::Entry> m_xWndResult; + + std::unique_ptr<weld::Label> m_xFtFormula; + std::unique_ptr<weld::TextView> m_xMEdit; + + std::unique_ptr<weld::CheckButton> m_xBtnMatrix; + std::unique_ptr<weld::Button> m_xBtnCancel; + + std::unique_ptr<weld::Button> m_xBtnBackward; + std::unique_ptr<weld::Button> m_xBtnForward; + std::unique_ptr<weld::Button> m_xBtnEnd; + + std::unique_ptr<weld::Label> m_xFtFormResult; + std::unique_ptr<weld::Entry> m_xWndFormResult; + + std::unique_ptr<RefEdit> m_xEdRef; + std::unique_ptr<RefButton> m_xRefBtn; + + std::unique_ptr<FuncPage> m_xFuncPage; + std::unique_ptr<StructPage> m_xStructPage; + + FormulaDlg_Impl(weld::Dialog& rDialog, + weld::Builder& rBuilder, + bool _bSupportFunctionResult, + bool _bSupportResult, + bool _bSupportMatrix, + IFormulaEditorHelper* _pHelper, + const IFunctionManager* _pFunctionMgr, + IControlReferenceHandler* _pDlg); + ~FormulaDlg_Impl(); +}; + +FormulaDlg_Impl::FormulaDlg_Impl(weld::Dialog& rDialog, + weld::Builder& rBuilder, + bool _bSupportFunctionResult, + bool _bSupportResult, + bool _bSupportMatrix, + IFormulaEditorHelper* _pHelper, + const IFunctionManager* _pFunctionMgr, + IControlReferenceHandler* _pDlg) + : m_pFunctionOpCodesEnd(nullptr) + , m_pHelper(_pHelper) + , m_rDialog(rDialog) + , m_bUserMatrixFlag(false) + , m_aTitle1( ForResId( STR_TITLE1 ) ) + , m_aTitle2( ForResId( STR_TITLE2 ) ) + , m_aFormulaHelper(_pFunctionMgr) + , m_bMakingTree(false) + , m_pFuncDesc(nullptr) + , m_nArgs(0) + , m_nFuncExpStart(0) + , m_nSelectionStart(-1) + , m_nSelectionEnd(-1) + , m_pTheRefEdit(nullptr) + , m_pTheRefButton(nullptr) + , m_xTabCtrl(rBuilder.weld_notebook("tabcontrol")) + , m_xParaWinBox(rBuilder.weld_container("BOX")) + , m_xFtHeadLine(rBuilder.weld_label("headline")) + , m_xFtFuncName(rBuilder.weld_label("funcname")) + , m_xFtFuncDesc(rBuilder.weld_label("funcdesc")) + , m_xFtEditName(rBuilder.weld_label("editname")) + , m_xFtResult(rBuilder.weld_label("label2")) + , m_xWndResult(rBuilder.weld_entry("result")) + , m_xFtFormula(rBuilder.weld_label("formula")) + , m_xMEdit(rBuilder.weld_text_view("ed_formula")) + , m_xBtnMatrix(rBuilder.weld_check_button("array")) + , m_xBtnCancel(rBuilder.weld_button("cancel")) + , m_xBtnBackward(rBuilder.weld_button("back")) + , m_xBtnForward(rBuilder.weld_button("next")) + , m_xBtnEnd(rBuilder.weld_button("ok")) + , m_xFtFormResult(rBuilder.weld_label("label1")) + , m_xWndFormResult(rBuilder.weld_entry("formula_result")) + , m_xEdRef(new RefEdit(rBuilder.weld_entry("ED_REF"))) + , m_xRefBtn(new RefButton(rBuilder.weld_button("RB_REF"))) +{ + auto nWidth = m_xMEdit->get_approximate_digit_width() * 62; + + //Space for two lines of text + m_xFtHeadLine->set_label("X\nX\n"); + auto nHeight = m_xFtHeadLine->get_preferred_size().Height(); + m_xFtHeadLine->set_size_request(nWidth, nHeight); + m_xFtHeadLine->set_label(""); + + m_xFtFuncName->set_label("X\nX\n"); + nHeight = m_xFtFuncName->get_preferred_size().Height(); + m_xFtFuncName->set_size_request(nWidth, nHeight); + m_xFtFuncDesc->set_size_request(nWidth, nHeight); + m_xFtFuncName->set_label(""); + + m_xMEdit->set_size_request(nWidth, + m_xMEdit->get_height_rows(5)); + + m_xEdRef->SetReferences(_pDlg, m_xFtEditName.get()); + m_xRefBtn->SetReferences(_pDlg, m_xEdRef.get()); + + m_xParaWin.reset(new ParaWin(m_xParaWinBox.get(), _pDlg)); + m_xParaWin->Show(); + m_xParaWinBox->hide(); + m_xFtEditName->hide(); + m_xEdRef->GetWidget()->hide(); + m_xRefBtn->GetWidget()->hide(); + + m_xMEdit->set_accessible_name(m_xFtFormula->get_label()); + + m_aEditHelpId = m_xMEdit->get_help_id(); + + m_bEditFlag =false; + m_bStructUpdate =true; + m_xParaWin->SetArgModifiedHdl( LINK( this, FormulaDlg_Impl, ModifyHdl ) ); + m_xParaWin->SetFxHdl( LINK( this, FormulaDlg_Impl, FxHdl ) ); + + m_xFuncPage.reset(new FuncPage(m_xTabCtrl->get_page("function"), _pFunctionMgr)); + m_xStructPage.reset(new StructPage(m_xTabCtrl->get_page("struct"))); + m_xTabCtrl->set_current_page("function"); + + m_aOldHelp = m_rDialog.get_help_id(); // HelpId from resource always for "Page 1" + + m_xFtResult->set_visible( _bSupportResult ); + m_xWndResult->set_visible( _bSupportResult ); + + m_xFtFormResult->set_visible( _bSupportFunctionResult ); + m_xWndFormResult->set_visible( _bSupportFunctionResult ); + + if ( _bSupportMatrix ) + m_xBtnMatrix->connect_clicked( LINK( this, FormulaDlg_Impl, MatrixHdl ) ); + else + m_xBtnMatrix->hide(); + + m_xBtnCancel->connect_clicked( LINK( this, FormulaDlg_Impl, BtnHdl ) ); + m_xBtnEnd->connect_clicked( LINK( this, FormulaDlg_Impl, BtnHdl ) ); + m_xBtnForward->connect_clicked( LINK( this, FormulaDlg_Impl, BtnHdl ) ); + m_xBtnBackward->connect_clicked( LINK( this, FormulaDlg_Impl, BtnHdl ) ); + + m_xFuncPage->SetDoubleClickHdl( LINK( this, FormulaDlg_Impl, DblClkHdl ) ); + m_xFuncPage->SetSelectHdl( LINK( this, FormulaDlg_Impl, FuncSelHdl) ); + m_xStructPage->SetSelectionHdl( LINK( this, FormulaDlg_Impl, StructSelHdl ) ); + m_xMEdit->connect_changed( LINK( this, FormulaDlg_Impl, FormulaHdl ) ); + m_xMEdit->connect_cursor_position( LINK( this, FormulaDlg_Impl, FormulaCursorHdl ) ); + + vcl::Font aFntLight = m_xFtFormula->get_font(); + vcl::Font aFntBold = aFntLight; + aFntBold.SetWeight( WEIGHT_BOLD ); + + m_xParaWin->SetArgumentFonts( aFntBold, aFntLight); +} + +FormulaDlg_Impl::~FormulaDlg_Impl() +{ + m_xTabCtrl->remove_page("function"); + m_xTabCtrl->remove_page("struct"); + + DeleteArgs(); +} + +void FormulaDlg_Impl::StoreFormEditData(FormEditData* pData) +{ + if (!pData) // it won't be destroyed via Close + return; + + int nStartPos, nEndPos; + m_xMEdit->get_selection_bounds(nStartPos, nEndPos); + if (nStartPos > nEndPos) + std::swap(nStartPos, nEndPos); + + pData->SetFStart(nStartPos); + pData->SetSelection(Selection(nStartPos, nEndPos)); + + if (m_xTabCtrl->get_current_page_ident() == "function") + pData->SetMode( FormulaDlgMode::Formula ); + else + pData->SetMode( FormulaDlgMode::Edit ); + pData->SetUndoStr(m_xMEdit->get_text()); + pData->SetMatrixFlag(m_xBtnMatrix->get_active()); +} + +void FormulaDlg_Impl::InitFormulaOpCodeMapper() +{ + if ( m_xOpCodeMapper.is() ) + return; + + m_xOpCodeMapper = m_pHelper->getFormulaOpCodeMapper(); + m_aFunctionOpCodes = m_xOpCodeMapper->getAvailableMappings( sheet::FormulaLanguage::ODFF, sheet::FormulaMapGroup::FUNCTIONS); + m_pFunctionOpCodesEnd = m_aFunctionOpCodes.getConstArray() + m_aFunctionOpCodes.getLength(); + + uno::Sequence< OUString > aArgs(3); + aArgs[TOKEN_OPEN] = "("; + aArgs[TOKEN_CLOSE] = ")"; + aArgs[TOKEN_SEP] = ";"; + m_aSeparatorsOpCodes = m_xOpCodeMapper->getMappings( aArgs, sheet::FormulaLanguage::ODFF); + + m_aSpecialOpCodes = m_xOpCodeMapper->getAvailableMappings( sheet::FormulaLanguage::ODFF, sheet::FormulaMapGroup::SPECIAL); +} + +void FormulaDlg_Impl::DeleteArgs() +{ + ::std::vector< OUString>().swap(m_aArguments); + m_nArgs = 0; +} + +sal_Int32 FormulaDlg_Impl::GetFunctionPos(sal_Int32 nPos) +{ + if ( !m_aTokenList.hasElements() ) + return SAL_MAX_INT32; + + const sal_Unicode sep = m_pHelper->getFunctionManager()->getSingleToken(IFunctionManager::eSep); + + sal_Int32 nFuncPos = SAL_MAX_INT32; + OUString aFormString = m_aFormulaHelper.GetCharClass()->uppercase(m_xMEdit->get_text()); + + const uno::Reference< sheet::XFormulaParser > xParser(m_pHelper->getFormulaParser()); + const table::CellAddress aRefPos(m_pHelper->getReferencePosition()); + + const sheet::FormulaToken* pIter = m_aTokenList.getConstArray(); + const sheet::FormulaToken* pEnd = pIter + m_aTokenList.getLength(); + try + { + bool bFlag = false; + sal_Int32 nTokPos = 1; + sal_Int32 nOldTokPos = 1; + sal_Int32 nPrevFuncPos = 1; + short nBracketCount = 0; + while ( pIter != pEnd ) + { + const sal_Int32 eOp = pIter->OpCode; + uno::Sequence<sheet::FormulaToken> aArgs(1); + aArgs[0] = *pIter; + const OUString aString = xParser->printFormula( aArgs, aRefPos); + const sheet::FormulaToken* pNextToken = pIter + 1; + + if ( !m_bUserMatrixFlag && FormulaCompiler::IsMatrixFunction(static_cast<OpCode>(eOp)) ) + { + m_xBtnMatrix->set_active(true); + } + + if (eOp == m_aSpecialOpCodes[sheet::FormulaMapGroupSpecialOffset::PUSH].Token.OpCode || + eOp == m_aSpecialOpCodes[sheet::FormulaMapGroupSpecialOffset::SPACES].Token.OpCode) + { + const sal_Int32 n1 = nTokPos < 0 ? -1 : aFormString.indexOf( sep, nTokPos); + const sal_Int32 n2 = nTokPos < 0 ? -1 : aFormString.indexOf( ')', nTokPos); + sal_Int32 nXXX = nTokPos; + if ( n1 < n2 && n1 != -1 ) + { + nTokPos = n1; + } + else + { + nTokPos = n2; + } + if ( pNextToken != pEnd ) + { + aArgs[0] = *pNextToken; + const OUString a2String = xParser->printFormula( aArgs, aRefPos); + const sal_Int32 n3 = nXXX < 0 ? -1 : aFormString.indexOf( a2String, nXXX); + if (n3 < nTokPos && n3 != -1) + nTokPos = n3; + } + } + else + { + nTokPos = nTokPos + aString.getLength(); + } + + if ( eOp == m_aSeparatorsOpCodes[TOKEN_OPEN].OpCode ) + { + nBracketCount++; + bFlag = true; + } + else if ( eOp == m_aSeparatorsOpCodes[TOKEN_CLOSE].OpCode ) + { + nBracketCount--; + bFlag = false; + nFuncPos = nPrevFuncPos; + } + bool bIsFunction = std::any_of( m_aFunctionOpCodes.getConstArray(), + m_pFunctionOpCodesEnd, + [&eOp](const sheet::FormulaOpCodeMapEntry& aEntry) { return aEntry.Token.OpCode == eOp; }); + + if ( bIsFunction && m_aSpecialOpCodes[sheet::FormulaMapGroupSpecialOffset::SPACES].Token.OpCode != eOp ) + { + nPrevFuncPos = nFuncPos; + nFuncPos = nOldTokPos; + } + + if ( nOldTokPos <= nPos && nPos < nTokPos ) + { + if ( !bIsFunction ) + { + if ( nBracketCount < 1 ) + { + nFuncPos = m_xMEdit->get_text().getLength(); + } + else if ( !bFlag ) + { + nFuncPos = nPrevFuncPos; + } + } + break; + } + + pIter = pNextToken; + nOldTokPos = nTokPos; + } // while ( pIter != pEnd ) + } + catch ( const uno::Exception& ) + { + TOOLS_WARN_EXCEPTION("formula.ui", "FormulaDlg_Impl::GetFunctionPos"); + } + + return nFuncPos; +} + +bool FormulaDlg_Impl::CalcValue( const OUString& rStrExp, OUString& rStrResult, bool bForceMatrixFormula ) +{ + bool bResult = true; + + if ( !rStrExp.isEmpty() ) + { + // Only calculate the value when there isn't any more keyboard input: + + // Make this debuggable by assigning to a variable that can be changed + // from within the debugger. + bool bInput = Application::AnyInput( VclInputFlags::KEYBOARD ); + if ( !bInput ) + { + bResult = m_pHelper->calculateValue( rStrExp, rStrResult, bForceMatrixFormula || m_xBtnMatrix->get_active()); + } + else + bResult = false; + } + + return bResult; +} + +void FormulaDlg_Impl::UpdateValues( bool bForceRecalcStruct ) +{ + // Take a force-array context into account. RPN creation propagated those + // to tokens that are ref-counted so also available in the token array. + bool bForceArray = false; + // Only necessary if it's not a matrix formula anyway and matrix evaluation + // is supported, i.e. the button is visible. + if (m_xBtnMatrix->get_visible() && !m_xBtnMatrix->get_active()) + { + std::unique_ptr<FormulaCompiler> pCompiler(m_pHelper->createCompiler(*m_pTokenArray)); + // In the case of the reportdesign dialog there is no currently active + // OpCode symbol mapping that could be used to create strings from + // tokens, it's all dreaded API mapping. However, in that case there's + // no array/matrix support anyway, but ensure checking. + if (pCompiler->GetCurrentOpCodeMap()) + { + const sal_Int32 nPos = m_aFuncSel.Min(); + assert( 0 <= nPos && nPos < m_pHelper->getCurrentFormula().getLength()); + OUStringBuffer aBuf; + const FormulaToken* pToken = nullptr; + for (pToken = m_pTokenArrayIterator->First(); pToken; pToken = m_pTokenArrayIterator->Next()) + { + pCompiler->CreateStringFromToken( aBuf, pToken); + if (nPos < aBuf.getLength()) + break; + } + if (pToken && nPos < aBuf.getLength()) + bForceArray = pToken->IsInForceArray(); + } + } + + OUString aStrResult; + if (m_pFuncDesc && CalcValue( m_pFuncDesc->getFormula( m_aArguments), aStrResult, bForceArray)) + m_xWndResult->set_text( aStrResult ); + + if (m_bMakingTree) + return; + + aStrResult.clear(); + if ( CalcValue( m_pHelper->getCurrentFormula(), aStrResult ) ) + m_xWndFormResult->set_text( aStrResult ); + else + { + aStrResult.clear(); + m_xWndFormResult->set_text( aStrResult ); + } + CalcStruct( m_xMEdit->get_text(), bForceRecalcStruct); +} + +void FormulaDlg_Impl::CalcStruct( const OUString& rStrExp, bool bForceRecalcStruct ) +{ + sal_Int32 nLength = rStrExp.getLength(); + + if ( !(!rStrExp.isEmpty() && (bForceRecalcStruct || m_aOldFormula != rStrExp) && m_bStructUpdate)) + return; + + m_xStructPage->ClearStruct(); + + OUString aString = rStrExp; + if (rStrExp[nLength-1] == '(') + { + aString = aString.copy( 0, nLength-1); + } + + aString = aString.replaceAll( "\n", ""); + OUString aStrResult; + + if ( CalcValue( aString, aStrResult ) ) + m_xWndFormResult->set_text(aStrResult); + + UpdateTokenArray(aString); + fillTree(m_xStructPage.get()); + + m_aOldFormula = rStrExp; + if (rStrExp[nLength-1] == '(') + UpdateTokenArray(rStrExp); +} + +void FormulaDlg_Impl::MakeTree(StructPage* _pTree, weld::TreeIter* pParent, const FormulaToken* pFuncToken, + const FormulaToken* _pToken, long Count) +{ + if ( !(_pToken != nullptr && Count > 0) ) + return; + + long nParas = _pToken->GetParamCount(); + OpCode eOp = _pToken->GetOpCode(); + + // #i101512# for output, the original token is needed + const FormulaToken* pOrigToken = (_pToken->GetType() == svFAP) ? _pToken->GetFAPOrigToken() : _pToken; + uno::Sequence<sheet::FormulaToken> aArgs(1); + ::std::map<const FormulaToken*, sheet::FormulaToken>::const_iterator itr = m_aTokenMap.find(pOrigToken); + if (itr == m_aTokenMap.end()) + return; + + aArgs[0] = itr->second; + try + { + const table::CellAddress aRefPos(m_pHelper->getReferencePosition()); + const OUString aResult = m_pHelper->getFormulaParser()->printFormula( aArgs, aRefPos); + + if ( nParas > 0 || (nParas == 0 && _pToken->IsFunction()) ) + { + std::unique_ptr<weld::TreeIter> xEntry; + weld::TreeIter* pEntry; + + bool bCalcSubformula = false; + OUString aTest = _pTree->GetEntryText(pParent); + + if (aTest == aResult && (eOp == ocAdd || eOp == ocMul || eOp == ocAmpersand)) + { + pEntry = pParent; + } + else + { + xEntry = m_xStructPage->GetTlbStruct().make_iterator(); + + if (eOp == ocBad) + { + _pTree->InsertEntry(aResult, pParent, STRUCT_ERROR, 0, _pToken, *xEntry); + } + else if (!((SC_OPCODE_START_BIN_OP <= eOp && eOp < SC_OPCODE_STOP_BIN_OP) || + (SC_OPCODE_START_UN_OP <= eOp && eOp < SC_OPCODE_STOP_UN_OP))) + { + // Not a binary or unary operator. + bCalcSubformula = true; + _pTree->InsertEntry(aResult, pParent, STRUCT_FOLDER, 0, _pToken, *xEntry); + } + else + { + /* TODO: question remains, why not sub calculate operators? */ + _pTree->InsertEntry(aResult, pParent, STRUCT_FOLDER, 0, _pToken, *xEntry); + } + + pEntry = xEntry.get(); + } + + MakeTree(_pTree, pEntry, _pToken, m_pTokenArrayIterator->PrevRPN(), nParas); + + if (bCalcSubformula) + { + OUString aFormula; + + if (!m_bMakingTree) + { + // gets the last subformula result + m_bMakingTree = true; + aFormula = GetPrevFuncExpression( true); + } + else + { + // gets subsequent subformula results (from the back) + aFormula = GetPrevFuncExpression( false); + } + + OUString aStr; + if (CalcValue( aFormula, aStr, _pToken->IsInForceArray())) + m_xWndResult->set_text( aStr ); + aStr = m_xWndResult->get_text(); + m_xStructPage->GetTlbStruct().set_text(*pEntry, aResult + " = " + aStr); + } + + --Count; + m_pTokenArrayIterator->NextRPN(); /* TODO: what's this to be? ThisRPN()? */ + MakeTree( _pTree, pParent, _pToken, m_pTokenArrayIterator->PrevRPN(), Count); + } + else + { + std::unique_ptr<weld::TreeIter> xEntry(m_xStructPage->GetTlbStruct().make_iterator()); + if (eOp == ocBad) + { + _pTree->InsertEntry( aResult, pParent, STRUCT_ERROR, 0, _pToken, *xEntry); + } + else if (eOp == ocPush) + { + // Interpret range reference in matrix context to resolve + // as array elements. Depending on parameter classification + // a scalar value (non-array context) is calculated first. + OUString aUnforcedResult; + bool bForceMatrix = (!m_xBtnMatrix->get_active() && + (_pToken->GetType() == svDoubleRef || _pToken->GetType() == svExternalDoubleRef)); + if (bForceMatrix && pFuncToken) + { + formula::ParamClass eParamClass = ParamClass::Reference; + if (pFuncToken->IsInForceArray()) + eParamClass = ParamClass::ForceArray; + else + { + std::shared_ptr<FormulaCompiler> pCompiler = m_pHelper->getCompiler(); + if (pCompiler) + eParamClass = pCompiler->GetForceArrayParameter( pFuncToken, Count - 1); + } + switch (eParamClass) + { + case ParamClass::Unknown: + case ParamClass::Bounds: + case ParamClass::Value: + if (CalcValue( "=" + aResult, aUnforcedResult, false) && aUnforcedResult != aResult) + aUnforcedResult += " "; + else + aUnforcedResult.clear(); + break; + case ParamClass::Reference: + case ParamClass::ReferenceOrRefArray: + case ParamClass::Array: + case ParamClass::ForceArray: + case ParamClass::ReferenceOrForceArray: + case ParamClass::SuppressedReferenceOrForceArray: + case ParamClass::ForceArrayReturn: + ; // nothing, only as array/matrix + // no default to get compiler warning + } + } + OUString aCellResult; + if (CalcValue( "=" + aResult, aCellResult, bForceMatrix) && aCellResult != aResult) + { + // Cell is a formula, print subformula. + // With scalar values prints "A1:A3 = 2 {1;2;3}" + _pTree->InsertEntry( aResult + " = " + aUnforcedResult + aCellResult, + pParent, STRUCT_END, 0, _pToken, *xEntry); + } + else + _pTree->InsertEntry(aResult, pParent, STRUCT_END, 0, _pToken, *xEntry); + } + else + { + _pTree->InsertEntry(aResult, pParent, STRUCT_END, 0, _pToken, *xEntry); + } + --Count; + MakeTree( _pTree, pParent, _pToken, m_pTokenArrayIterator->PrevRPN(), Count); + } + } + catch (const uno::Exception&) + { + DBG_UNHANDLED_EXCEPTION("formula.ui"); + } +} + +void FormulaDlg_Impl::fillTree(StructPage* _pTree) +{ + InitFormulaOpCodeMapper(); + FormulaToken* pToken = m_pTokenArrayIterator->LastRPN(); + + if ( pToken != nullptr) + { + MakeTree( _pTree, nullptr, nullptr, pToken, 1); + m_bMakingTree = false; + } +} + +void FormulaDlg_Impl::UpdateTokenArray( const OUString& rStrExp) +{ + m_aTokenMap.clear(); + m_aTokenList.realloc(0); + try + { + const table::CellAddress aRefPos(m_pHelper->getReferencePosition()); + m_aTokenList = m_pHelper->getFormulaParser()->parseFormula( rStrExp, aRefPos); + } + catch (const uno::Exception&) + { + DBG_UNHANDLED_EXCEPTION("formula.ui"); + } + InitFormulaOpCodeMapper(); + m_pTokenArray = m_pHelper->convertToTokenArray(m_aTokenList); + m_pTokenArrayIterator.reset(new FormulaTokenArrayPlainIterator(*m_pTokenArray)); + const sal_Int32 nLen = static_cast<sal_Int32>(m_pTokenArray->GetLen()); + FormulaToken** pTokens = m_pTokenArray->GetArray(); + if ( pTokens && nLen == m_aTokenList.getLength() ) + { + for (sal_Int32 nPos = 0; nPos < nLen; nPos++) + { + m_aTokenMap.emplace( pTokens[nPos], m_aTokenList[nPos] ); + } + } // if ( pTokens && nLen == m_aTokenList.getLength() ) + + std::unique_ptr<FormulaCompiler> pCompiler(m_pHelper->createCompiler(*m_pTokenArray)); + // #i101512# Disable special handling of jump commands. + pCompiler->EnableJumpCommandReorder(false); + pCompiler->EnableStopOnError(false); + pCompiler->SetComputeIIFlag(true); + pCompiler->SetMatrixFlag(m_bUserMatrixFlag); + pCompiler->CompileTokenArray(); +} + +void FormulaDlg_Impl::FillDialog(bool bFlag) +{ + bool bNext = true, bPrev = true; + if (bFlag) + FillControls( bNext, bPrev); + FillListboxes(); + if (bFlag) + { + m_xBtnBackward->set_sensitive(bPrev); + m_xBtnForward->set_sensitive(bNext); + } + + OUString aStrResult; + + if ( CalcValue( m_pHelper->getCurrentFormula(), aStrResult ) ) + m_xWndFormResult->set_text( aStrResult ); + else + { + aStrResult.clear(); + m_xWndFormResult->set_text( aStrResult ); + } +} + +void FormulaDlg_Impl::FillListboxes() +{ + // Switch between the "Pages" + FormEditData* pData = m_pHelper->getFormEditData(); + // 1. Page: select function + if ( m_pFuncDesc && m_pFuncDesc->getCategory() ) + { + // We'll never have more than int32 max categories so this is safe ... + // Category listbox holds additional entries for Last Used and All, so + // the offset should be two but hard coded numbers are ugly... + const sal_Int32 nCategoryOffset = m_xFuncPage->GetCategoryEntryCount() - m_aFormulaHelper.GetCategoryCount(); + if ( m_xFuncPage->GetCategory() != static_cast<sal_Int32>(m_pFuncDesc->getCategory()->getNumber() + nCategoryOffset) ) + m_xFuncPage->SetCategory(m_pFuncDesc->getCategory()->getNumber() + nCategoryOffset); + + sal_Int32 nPos = m_xFuncPage->GetFuncPos(m_pFuncDesc); + + m_xFuncPage->SetFunction(nPos); + } + else if ( pData ) + { + m_xFuncPage->SetCategory( 1 ); + m_xFuncPage->SetFunction( -1 ); + } + FuncSelHdl(*m_xFuncPage); + + m_pHelper->setDispatcherLock( true ); // Activate Modal-Mode + + // HelpId for 1. page is the one from the resource + m_rDialog.set_help_id( m_aOldHelp ); +} + +void FormulaDlg_Impl::FillControls( bool &rbNext, bool &rbPrev) +{ + // Switch between the "Pages" + FormEditData* pData = m_pHelper->getFormEditData(); + if (!pData ) + return; + + // 2. Page or Edit: show selected function + + sal_Int32 nFStart = pData->GetFStart(); + OUString aFormula = m_pHelper->getCurrentFormula() + " )"; + sal_Int32 nNextFStart = nFStart; + sal_Int32 nNextFEnd = 0; + + DeleteArgs(); + const IFunctionDescription* pOldFuncDesc = m_pFuncDesc; + + if ( m_aFormulaHelper.GetNextFunc( aFormula, false, + nNextFStart, &nNextFEnd, &m_pFuncDesc, &m_aArguments ) ) + { + const bool bTestFlag = (pOldFuncDesc != m_pFuncDesc); + if (bTestFlag) + { + m_xFtHeadLine->hide(); + m_xFtFuncName->hide(); + m_xFtFuncDesc->hide(); + m_xParaWin->SetFunctionDesc(m_pFuncDesc); + m_xFtEditName->set_label( m_pFuncDesc->getFunctionName() ); + m_xFtEditName->show(); + m_xParaWinBox->show(); + const OString aHelpId = m_pFuncDesc->getHelpId(); + if ( !aHelpId.isEmpty() ) + m_xMEdit->set_help_id(aHelpId); + } + + sal_Int32 nOldStart, nOldEnd; + m_pHelper->getSelection( nOldStart, nOldEnd ); + if ( nOldStart != nNextFStart || nOldEnd != nNextFEnd ) + { + m_pHelper->setSelection( nNextFStart, nNextFEnd ); + } + m_aFuncSel.Min() = nNextFStart; + m_aFuncSel.Max() = nNextFEnd; + + if (!m_bEditFlag) + m_xMEdit->set_text(m_pHelper->getCurrentFormula()); + sal_Int32 PrivStart, PrivEnd; + m_pHelper->getSelection( PrivStart, PrivEnd); + if (!m_bEditFlag) + m_xMEdit->select_region(PrivStart, PrivEnd); + + m_nArgs = m_pFuncDesc->getSuppressedArgumentCount(); + sal_uInt16 nOffset = pData->GetOffset(); + + // Concatenate the Edit's for Focus-Control + + if (bTestFlag) + m_xParaWin->SetArgumentOffset(nOffset); + sal_uInt16 nActiv = 0; + sal_Int32 nArgPos = m_aFormulaHelper.GetArgStart( aFormula, nFStart, 0 ); + + int nStartPos, nEndPos; + m_xMEdit->get_selection_bounds(nStartPos, nEndPos); + if (nStartPos > nEndPos) + std::swap(nStartPos, nEndPos); + + sal_Int32 nEditPos = nStartPos; + bool bFlag = false; + + for (sal_Int32 i = 0; i < m_nArgs; i++) + { + sal_Int32 nLength = m_aArguments[i].getLength()+1; + m_xParaWin->SetArgument( i, m_aArguments[i]); + if (nArgPos <= nEditPos && nEditPos < nArgPos+nLength) + { + nActiv = i; + bFlag = true; + } + nArgPos = nArgPos + nLength; + } + m_xParaWin->UpdateParas(); + + if (bFlag) + { + m_xParaWin->SetActiveLine(nActiv); + } + + UpdateValues(); + } + else + { + m_xFtEditName->set_label(""); + m_xMEdit->set_help_id(m_aEditHelpId); + } + // test if before/after are anymore functions + + sal_Int32 nTempStart = m_aFormulaHelper.GetArgStart( aFormula, nFStart, 0 ); + rbNext = m_aFormulaHelper.GetNextFunc( aFormula, false, nTempStart ); + + int nStartPos, nEndPos; + m_xMEdit->get_selection_bounds(nStartPos, nEndPos); + if (nStartPos > nEndPos) + std::swap(nStartPos, nEndPos); + + nTempStart = nStartPos; + pData->SetFStart(nTempStart); + rbPrev = m_aFormulaHelper.GetNextFunc( aFormula, true, nTempStart ); +} + + +void FormulaDlg_Impl::ClearAllParas() +{ + DeleteArgs(); + m_pFuncDesc = nullptr; + m_xParaWin->ClearAll(); + m_xWndResult->set_text(OUString()); + m_xFtFuncName->set_label(OUString()); + FuncSelHdl(*m_xFuncPage); + + if (m_xFuncPage->IsVisible()) + { + m_xFtEditName->hide(); + m_xParaWinBox->hide(); + + m_xBtnForward->set_sensitive(true); //@new + m_xFtHeadLine->show(); + m_xFtFuncName->show(); + m_xFtFuncDesc->show(); + } +} + +OUString FormulaDlg_Impl::RepairFormula(const OUString& aFormula) +{ + OUString aResult('='); + try + { + UpdateTokenArray(aFormula); + + if ( m_aTokenList.hasElements() ) + { + const table::CellAddress aRefPos(m_pHelper->getReferencePosition()); + const OUString sFormula( m_pHelper->getFormulaParser()->printFormula( m_aTokenList, aRefPos)); + if ( sFormula.isEmpty() || sFormula[0] != '=' ) + aResult += sFormula; + else + aResult = sFormula; + + } + } + catch ( const uno::Exception& ) + { + TOOLS_WARN_EXCEPTION("formula.ui", "FormulaDlg_Impl::RepairFormula"); + } + return aResult; +} + +void FormulaDlg_Impl::DoEnter(bool bOk) +{ + // Accept input to the document or cancel + if ( bOk) + { + // remove dummy arguments + OUString aInputFormula = m_pHelper->getCurrentFormula(); + OUString aString = RepairFormula(m_xMEdit->get_text()); + m_pHelper->setSelection( 0, aInputFormula.getLength()); + m_pHelper->setCurrentFormula(aString); + } + + m_pHelper->switchBack(); + + m_pHelper->dispatch( bOk, m_xBtnMatrix->get_active()); + // Clear data + m_pHelper->deleteFormData(); + + // Close dialog + m_pHelper->doClose(bOk); +} + + +IMPL_LINK(FormulaDlg_Impl, BtnHdl, weld::Button&, rBtn, void) +{ + if (&rBtn == m_xBtnCancel.get()) + { + DoEnter(false); // closes the Dialog + } + else if (&rBtn == m_xBtnEnd.get()) + { + DoEnter(true); // closes the Dialog + } + else if (&rBtn == m_xBtnForward.get()) + { + const IFunctionDescription* pDesc; + sal_Int32 nSelFunc = m_xFuncPage->GetFunction(); + if (nSelFunc != -1) + pDesc = m_xFuncPage->GetFuncDesc( nSelFunc ); + else + { + // Do not overwrite the selected formula expression, just edit the + // unlisted function. + m_pFuncDesc = pDesc = nullptr; + } + + if (pDesc == m_pFuncDesc || !m_xFuncPage->IsVisible()) + EditNextFunc( true ); + else + { + DblClkHdl(*m_xFuncPage); //new + m_xBtnForward->set_sensitive(false); //new + } + } + else if (&rBtn == m_xBtnBackward.get()) + { + m_bEditFlag = false; + m_xBtnForward->set_sensitive(true); + EditNextFunc( false ); + } +} + +// Functions for 1. Page + +// Handler for Listboxes + +IMPL_LINK_NOARG( FormulaDlg_Impl, DblClkHdl, FuncPage&, void) +{ + sal_Int32 nFunc = m_xFuncPage->GetFunction(); + + // ex-UpdateLRUList + const IFunctionDescription* pDesc = m_xFuncPage->GetFuncDesc(nFunc); + m_pHelper->insertEntryToLRUList(pDesc); + + OUString aFuncName = m_xFuncPage->GetSelFunctionName() + "()"; + m_pHelper->setCurrentFormula(aFuncName); + m_xMEdit->replace_selection(aFuncName); + + int nStartPos, nEndPos; + m_xMEdit->get_selection_bounds(nStartPos, nEndPos); + if (nStartPos > nEndPos) + std::swap(nStartPos, nEndPos); + + nEndPos = nEndPos - 1; + m_xMEdit->select_region(nStartPos, nEndPos); + + FormulaHdl(*m_xMEdit); + + nStartPos = nEndPos; + m_xMEdit->select_region(nStartPos, nEndPos); + + if (m_nArgs == 0) + { + BtnHdl(*m_xBtnBackward); + } + + m_xParaWin->SetEdFocus(); + m_xBtnForward->set_sensitive(false); //@New +} + +// Functions for right Page + +void FormulaDlg_Impl::SetData( sal_Int32 nFStart, sal_Int32 nNextFStart, sal_Int32 nNextFEnd, sal_Int32& PrivStart, sal_Int32& PrivEnd) +{ + sal_Int32 nFEnd; + + // Notice and set new selection + m_pHelper->getSelection( nFStart, nFEnd ); + m_pHelper->setSelection( nNextFStart, nNextFEnd ); + if (!m_bEditFlag) + m_xMEdit->set_text(m_pHelper->getCurrentFormula()); + + + m_pHelper->getSelection( PrivStart, PrivEnd); + if (!m_bEditFlag) + { + m_xMEdit->select_region(PrivStart, PrivEnd); + UpdateOldSel(); + } + + FormEditData* pData = m_pHelper->getFormEditData(); + pData->SetFStart( nNextFStart ); + pData->SetOffset( 0 ); + + FillDialog(); +} + +void FormulaDlg_Impl::EditThisFunc(sal_Int32 nFStart) +{ + FormEditData* pData = m_pHelper->getFormEditData(); + if (!pData) + return; + + OUString aFormula = m_pHelper->getCurrentFormula(); + + if (nFStart == NOT_FOUND) + { + nFStart = pData->GetFStart(); + } + else + { + pData->SetFStart(nFStart); + } + + sal_Int32 nNextFStart = nFStart; + sal_Int32 nNextFEnd = 0; + + bool bFound; + + bFound = m_aFormulaHelper.GetNextFunc( aFormula, false, nNextFStart, &nNextFEnd); + if ( bFound ) + { + sal_Int32 PrivStart, PrivEnd; + SetData( nFStart, nNextFStart, nNextFEnd, PrivStart, PrivEnd); + m_pHelper->showReference( aFormula.copy( PrivStart, PrivEnd-PrivStart)); + } + else + { + ClearAllParas(); + } +} + +bool FormulaDlg_Impl::EditNextFunc( bool bForward, sal_Int32 nFStart ) +{ + FormEditData* pData = m_pHelper->getFormEditData(); + if (!pData) + return false; + + OUString aFormula = m_pHelper->getCurrentFormula(); + + if (nFStart == NOT_FOUND) + { + nFStart = pData->GetFStart(); + } + else + { + pData->SetFStart(nFStart); + } + + sal_Int32 nNextFStart = 0; + sal_Int32 nNextFEnd = 0; + + bool bFound; + if ( bForward ) + { + nNextFStart = m_aFormulaHelper.GetArgStart( aFormula, nFStart, 0 ); + bFound = m_aFormulaHelper.GetNextFunc( aFormula, false, nNextFStart, &nNextFEnd); + } + else + { + nNextFStart = nFStart; + bFound = m_aFormulaHelper.GetNextFunc( aFormula, true, nNextFStart, &nNextFEnd); + } + + if ( bFound ) + { + sal_Int32 PrivStart, PrivEnd; + SetData( nFStart, nNextFStart, nNextFEnd, PrivStart, PrivEnd); + } + + return bFound; +} + +OUString FormulaDlg_Impl::GetPrevFuncExpression( bool bStartFromEnd ) +{ + OUString aExpression; + + OUString aFormula( m_pHelper->getCurrentFormula()); + if (aFormula.isEmpty()) + return aExpression; + + if (bStartFromEnd || m_nFuncExpStart >= aFormula.getLength()) + m_nFuncExpStart = aFormula.getLength() - 1; + + sal_Int32 nFStart = m_nFuncExpStart; + sal_Int32 nFEnd = 0; + if (m_aFormulaHelper.GetNextFunc( aFormula, true, nFStart, &nFEnd)) + { + aExpression = aFormula.copy( nFStart, nFEnd - nFStart); // nFEnd is exclusive + m_nFuncExpStart = nFStart; + } + + return aExpression; +} + +void FormulaDlg_Impl::SaveArg( sal_uInt16 nEd ) +{ + if (nEd >= m_nArgs) + return; + + for (sal_uInt16 i = 0; i <= nEd; i++) + { + if ( m_aArguments[i].isEmpty() ) + m_aArguments[i] = " "; + } + if (!m_xParaWin->GetArgument(nEd).isEmpty()) + m_aArguments[nEd] = m_xParaWin->GetArgument(nEd); + + sal_uInt16 nClearPos = nEd+1; + for (sal_Int32 i = nEd+1; i < m_nArgs; i++) + { + if ( !m_xParaWin->GetArgument(i).isEmpty() ) + { + nClearPos = i+1; + } + } + + for (sal_Int32 i = nClearPos; i < m_nArgs; i++) + { + m_aArguments[i].clear(); + } +} + +IMPL_LINK( FormulaDlg_Impl, FxHdl, ParaWin&, rPtr, void ) +{ + if (&rPtr != m_xParaWin.get()) + return; + + m_xBtnForward->set_sensitive(true); //@ In order to be able to input another function. + m_xTabCtrl->set_current_page("function"); + + OUString aUndoStr = m_pHelper->getCurrentFormula(); // it will be added before a ";" + FormEditData* pData = m_pHelper->getFormEditData(); + if (!pData) + return; + + sal_uInt16 nArgNo = m_xParaWin->GetActiveLine(); + sal_uInt16 nEdFocus = nArgNo; + + SaveArg(nArgNo); + UpdateSelection(); + + sal_Int32 nFormulaStrPos = pData->GetFStart(); + + OUString aFormula = m_pHelper->getCurrentFormula(); + sal_Int32 n1 = m_aFormulaHelper.GetArgStart( aFormula, nFormulaStrPos, nEdFocus + pData->GetOffset() ); + + pData->SaveValues(); + pData->SetMode( FormulaDlgMode::Formula ); + pData->SetFStart( n1 ); + pData->SetUndoStr( aUndoStr ); + ClearAllParas(); + + FillDialog(false); + m_xFuncPage->SetFocus(); //There Parawin is not visible anymore +} + +IMPL_LINK( FormulaDlg_Impl, ModifyHdl, ParaWin&, rPtr, void ) +{ + if (&rPtr == m_xParaWin.get()) + { + SaveArg(m_xParaWin->GetActiveLine()); + UpdateValues(); + + UpdateSelection(); + CalcStruct(m_xMEdit->get_text()); + } +} + +IMPL_LINK_NOARG( FormulaDlg_Impl, FormulaHdl, weld::TextView&, void) +{ + + FormEditData* pData = m_pHelper->getFormEditData(); + if (!pData) + return; + + m_bEditFlag = true; + OUString aInputFormula = m_pHelper->getCurrentFormula(); + OUString aString = m_xMEdit->get_text(); + + int nStartPos, nEndPos; + m_xMEdit->get_selection_bounds(nStartPos, nEndPos); + if (nStartPos > nEndPos) + std::swap(nStartPos, nEndPos); + + if (aString.isEmpty()) // in case everything was cleared + { + aString += "="; + m_xMEdit->set_text(aString); + nStartPos = 1; + nEndPos = 1; + m_xMEdit->select_region(nStartPos, nEndPos); + } + else if (aString[0]!='=') // in case it's replaced + { + aString = "=" + aString; + m_xMEdit->set_text(aString); + nStartPos += 1; + nEndPos += 1; + m_xMEdit->select_region(nStartPos, nEndPos); + } + + m_pHelper->setSelection( 0, aInputFormula.getLength()); + m_pHelper->setCurrentFormula(aString); + m_pHelper->setSelection(nStartPos, nEndPos); + + sal_Int32 nPos = nStartPos - 1; + + OUString aStrResult; + + if ( CalcValue( m_pHelper->getCurrentFormula(), aStrResult ) ) + m_xWndFormResult->set_text( aStrResult ); + else + { + aStrResult.clear(); + m_xWndFormResult->set_text( aStrResult ); + } + CalcStruct(aString); + + nPos = GetFunctionPos(nPos); + + if (nPos < nStartPos - 1) + { + sal_Int32 nPos1 = aString.indexOf( '(', nPos); + EditNextFunc( false, nPos1); + } + else + { + ClearAllParas(); + } + + m_pHelper->setSelection(nStartPos, nEndPos); + m_bEditFlag = false; +} + +void FormulaDlg_Impl::FormulaCursor() +{ + FormEditData* pData = m_pHelper->getFormEditData(); + if (!pData) + return; + + m_bEditFlag = true; + + OUString aString = m_xMEdit->get_text(); + + int nStartPos, nEndPos; + m_xMEdit->get_selection_bounds(nStartPos, nEndPos); + if (nStartPos > nEndPos) + std::swap(nStartPos, nEndPos); + + m_pHelper->setSelection(nStartPos, nEndPos); + + if (nStartPos == 0) + { + nStartPos = 1; + m_xMEdit->select_region(nStartPos, nEndPos); + } + if (nStartPos != aString.getLength()) + { + sal_Int32 nPos = nStartPos; + + sal_Int32 nFStart = GetFunctionPos(nPos - 1); + + if (nFStart < nPos) + { + sal_Int32 nPos1 = m_aFormulaHelper.GetFunctionEnd( aString, nFStart); + + if (nPos1 > nPos) + { + EditThisFunc(nFStart); + } + else + { + sal_Int32 n = nPos; + short nCount = 1; + while(n > 0) + { + if (aString[n]==')') + nCount++; + else if (aString[n]=='(') + nCount--; + if (nCount == 0) + break; + n--; + } + if (nCount == 0) + { + nFStart = m_aFormulaHelper.GetFunctionStart( aString, n, true); + EditThisFunc(nFStart); + } + else + { + ClearAllParas(); + } + } + } + else + { + ClearAllParas(); + } + } + m_pHelper->setSelection(nStartPos, nEndPos); + + m_bEditFlag = false; +} + +void FormulaDlg_Impl::UpdateOldSel() +{ + m_xMEdit->get_selection_bounds(m_nSelectionStart, m_nSelectionEnd); + if (m_nSelectionStart > m_nSelectionEnd) + std::swap(m_nSelectionStart, m_nSelectionEnd); +} + +IMPL_LINK_NOARG( FormulaDlg_Impl, FormulaCursorHdl, weld::TextView&, void) +{ + int nStartPos, nEndPos; + m_xMEdit->get_selection_bounds(nStartPos, nEndPos); + if (nStartPos > nEndPos) + std::swap(nStartPos, nEndPos); + + if (nStartPos != m_nSelectionStart || nEndPos != m_nSelectionEnd) + { + m_nSelectionStart = nStartPos; + m_nSelectionEnd = nEndPos; + FormulaCursor(); + } +} + +void FormulaDlg_Impl::UpdateSelection() +{ + m_pHelper->setSelection( m_aFuncSel.Min(), m_aFuncSel.Max()); + m_pHelper->setCurrentFormula( m_pFuncDesc->getFormula( m_aArguments ) ); + m_xMEdit->set_text(m_pHelper->getCurrentFormula()); + sal_Int32 PrivStart, PrivEnd; + m_pHelper->getSelection( PrivStart, PrivEnd); + m_aFuncSel.Min() = PrivStart; + m_aFuncSel.Max() = PrivEnd; + + m_nArgs = m_pFuncDesc->getSuppressedArgumentCount(); + + OUString aFormula = m_xMEdit->get_text(); + sal_Int32 nArgPos = m_aFormulaHelper.GetArgStart( aFormula, PrivStart, 0); + + sal_uInt16 nPos = m_xParaWin->GetActiveLine(); + if (nPos >= m_aArguments.size()) + { + SAL_WARN("formula.ui","FormulaDlg_Impl::UpdateSelection - shot in foot: nPos " << + nPos << " >= m_aArguments.size() " << m_aArguments.size() << + " for aFormula '" << aFormula << "'"); + nPos = m_aArguments.size(); + if (nPos) + --nPos; + } + + for (sal_uInt16 i = 0; i < nPos; i++) + { + nArgPos += (m_aArguments[i].getLength() + 1); + } + sal_Int32 nLength = (nPos < m_aArguments.size()) ? m_aArguments[nPos].getLength() : 0; + + m_pHelper->setSelection(nArgPos, nArgPos + nLength); + m_xMEdit->select_region(nArgPos, nArgPos + nLength); + UpdateOldSel(); +} + +::std::pair<RefButton*, RefEdit*> FormulaDlg_Impl::RefInputStartBefore(RefEdit* pEdit, RefButton* pButton) +{ + m_pTheRefEdit = pEdit; + m_pTheRefButton = pButton; + + Selection aOrigSelection; + if (m_pTheRefEdit) + { + // grab selection before showing next widget in case the selection is blown away + // by it appearing + aOrigSelection = m_pTheRefEdit->GetSelection(); + } + + // because its initially hidden, give it its optimal size so clicking the + // refbutton has an initial size to work when retro-fitting this to .ui + m_xEdRef->GetWidget()->set_size_request(m_xEdRef->GetWidget()->get_preferred_size().Width(), -1); + m_xEdRef->GetWidget()->show(); + + if ( m_pTheRefEdit ) + { + m_xEdRef->SetRefString(m_pTheRefEdit->GetText()); + m_xEdRef->SetSelection(aOrigSelection); + m_xEdRef->GetWidget()->set_help_id(m_pTheRefEdit->GetWidget()->get_help_id()); + } + + m_xRefBtn->GetWidget()->set_visible(pButton != nullptr); + + ::std::pair<RefButton*, RefEdit*> aPair; + aPair.first = pButton ? m_xRefBtn.get() : nullptr; + aPair.second = m_xEdRef.get(); + return aPair; +} + +void FormulaDlg_Impl::RefInputStartAfter() +{ + m_xRefBtn->SetEndImage(); + + if (!m_pTheRefEdit) + return; + + OUString aStr = m_aTitle2 + " " + m_xFtEditName->get_label() + "( "; + + if ( m_xParaWin->GetActiveLine() > 0 ) + aStr += "...; "; + aStr += m_xParaWin->GetActiveArgName(); + if ( m_xParaWin->GetActiveLine() + 1 < m_nArgs ) + aStr += "; ..."; + aStr += " )"; + + m_rDialog.set_title(m_rDialog.strip_mnemonic(aStr)); +} + +void FormulaDlg_Impl::RefInputDoneAfter( bool bForced ) +{ + m_xRefBtn->SetStartImage(); + if (!(bForced || !m_xRefBtn->GetWidget()->get_visible())) + return; + + m_xEdRef->GetWidget()->hide(); + m_xRefBtn->GetWidget()->hide(); + if ( m_pTheRefEdit ) + { + m_pTheRefEdit->SetRefString( m_xEdRef->GetText() ); + m_pTheRefEdit->GrabFocus(); + + if ( m_pTheRefButton ) + m_pTheRefButton->SetStartImage(); + + sal_uInt16 nPrivActiv = m_xParaWin->GetActiveLine(); + m_xParaWin->SetArgument( nPrivActiv, m_xEdRef->GetText() ); + ModifyHdl( *m_xParaWin ); + m_pTheRefEdit = nullptr; + } + m_rDialog.set_title(m_aTitle1); +} + +RefEdit* FormulaDlg_Impl::GetCurrRefEdit() +{ + return m_xEdRef->GetWidget()->get_visible() ? m_xEdRef.get() : m_xParaWin->GetActiveEdit(); +} + +void FormulaDlg_Impl::Update() +{ + FormEditData* pData = m_pHelper->getFormEditData(); + const OUString sExpression = m_xMEdit->get_text(); + m_aOldFormula.clear(); + UpdateTokenArray(sExpression); + FormulaCursor(); + CalcStruct(sExpression); + if (pData->GetMode() == FormulaDlgMode::Formula) + m_xTabCtrl->set_current_page("function"); + else + m_xTabCtrl->set_current_page("struct"); + m_xBtnMatrix->set_active(pData->GetMatrixFlag()); +} + +void FormulaDlg_Impl::Update(const OUString& _sExp) +{ + CalcStruct(_sExp); + FillDialog(); + FuncSelHdl(*m_xFuncPage); +} + +void FormulaDlg_Impl::SetMeText(const OUString& _sText) +{ + FormEditData* pData = m_pHelper->getFormEditData(); + m_xMEdit->set_text(_sText); + auto aSelection = pData->GetSelection(); + m_xMEdit->select_region(aSelection.Min(), aSelection.Max()); + UpdateOldSel(); +} + +FormulaDlgMode FormulaDlg_Impl::SetMeText( const OUString& _sText, sal_Int32 PrivStart, sal_Int32 PrivEnd, bool bMatrix, bool _bSelect, bool _bUpdate) +{ + FormulaDlgMode eMode = FormulaDlgMode::Formula; + if (!m_bEditFlag) + m_xMEdit->set_text(_sText); + + if ( _bSelect || !m_bEditFlag ) + m_xMEdit->select_region(PrivStart, PrivEnd); + if ( _bUpdate ) + { + UpdateOldSel(); + int nStartPos, nEndPos; + m_xMEdit->get_selection_bounds(nStartPos, nEndPos); + if (nStartPos > nEndPos) + std::swap(nStartPos, nEndPos); + m_pHelper->showReference(m_xMEdit->get_text().copy(nStartPos, nEndPos - nStartPos)); + eMode = FormulaDlgMode::Edit; + + m_xBtnMatrix->set_active( bMatrix ); + } // if ( _bUpdate ) + return eMode; +} + +bool FormulaDlg_Impl::CheckMatrix(OUString& aFormula) +{ + m_xMEdit->grab_focus(); + sal_Int32 nLen = aFormula.getLength(); + bool bMatrix = nLen > 3 // Matrix-Formula + && aFormula[0] == '{' + && aFormula[1] == '=' + && aFormula[nLen-1] == '}'; + if ( bMatrix ) + { + aFormula = aFormula.copy( 1, aFormula.getLength()-2 ); + m_xBtnMatrix->set_active( bMatrix ); + m_xBtnMatrix->set_sensitive(false); + } // if ( bMatrix ) + + m_xTabCtrl->set_current_page("struct"); + return bMatrix; +} + +IMPL_LINK_NOARG( FormulaDlg_Impl, StructSelHdl, StructPage&, void) +{ + m_bStructUpdate = false; + if (m_xStructPage->IsVisible()) + m_xBtnForward->set_sensitive(false); //@New + m_bStructUpdate = true; +} + +IMPL_LINK_NOARG( FormulaDlg_Impl, MatrixHdl, weld::Button&, void) +{ + m_bUserMatrixFlag = true; + UpdateValues(true); +} + +IMPL_LINK_NOARG( FormulaDlg_Impl, FuncSelHdl, FuncPage&, void) +{ + if ( (m_xFuncPage->GetFunctionEntryCount() > 0) + && (m_xFuncPage->GetFunction() != -1) ) + { + const IFunctionDescription* pDesc = m_xFuncPage->GetFuncDesc( m_xFuncPage->GetFunction() ); + + if (pDesc != m_pFuncDesc) + m_xBtnForward->set_sensitive(true); //new + + if (pDesc) + { + pDesc->initArgumentInfo(); // full argument info is needed + + OUString aSig = pDesc->getSignature(); + m_xFtHeadLine->set_label( pDesc->getFunctionName() ); + m_xFtFuncName->set_label( aSig ); + m_xFtFuncDesc->set_label( pDesc->getDescription() ); + } + } + else + { + m_xFtHeadLine->set_label( OUString() ); + m_xFtFuncName->set_label( OUString() ); + m_xFtFuncDesc->set_label( OUString() ); + } +} + +void FormulaDlg_Impl::UpdateParaWin( const Selection& _rSelection, const OUString& _sRefStr) +{ + Selection theSel = _rSelection; + m_xEdRef->GetWidget()->replace_selection(_sRefStr); + theSel.Max() = theSel.Min() + _sRefStr.getLength(); + m_xEdRef->SetSelection( theSel ); + + + // Manual Update of the results' fields: + + sal_uInt16 nPrivActiv = m_xParaWin->GetActiveLine(); + m_xParaWin->SetArgument( nPrivActiv, m_xEdRef->GetText()); + m_xParaWin->UpdateParas(); + + RefEdit* pEd = GetCurrRefEdit(); + if (pEd) + pEd->SetSelection( theSel ); +} + +bool FormulaDlg_Impl::UpdateParaWin(Selection& _rSelection) +{ + OUString aStrEd; + RefEdit* pEd = GetCurrRefEdit(); + if (pEd && !m_pTheRefEdit) + { + _rSelection = pEd->GetSelection(); + _rSelection.Justify(); + aStrEd = pEd->GetText(); + m_xEdRef->SetRefString(aStrEd); + m_xEdRef->SetSelection( _rSelection ); + } + else + { + _rSelection = m_xEdRef->GetSelection(); + _rSelection.Justify(); + aStrEd = m_xEdRef->GetText(); + } + return m_pTheRefEdit == nullptr; +} + +void FormulaDlg_Impl::SetEdSelection() +{ + RefEdit* pEd = GetCurrRefEdit()/*aScParaWin.GetActiveEdit()*/; + if (pEd) + { + Selection theSel = m_xEdRef->GetSelection(); + // Edit may have the focus -> call ModifyHdl in addition + // to what's happening in GetFocus + pEd->GetModifyHdl().Call(*pEd); + pEd->GrabFocus(); + pEd->SetSelection(theSel); + } // if ( pEd ) +} + +FormulaModalDialog::FormulaModalDialog(weld::Window* pParent, + IFunctionManager const * _pFunctionMgr, + IControlReferenceHandler* _pDlg) + : GenericDialogController(pParent, "formula/ui/formuladialog.ui", "FormulaDialog") + , m_pImpl(new FormulaDlg_Impl(*m_xDialog, *m_xBuilder, false/*_bSupportFunctionResult*/, + false/*_bSupportResult*/, false/*_bSupportMatrix*/, + this, _pFunctionMgr, _pDlg)) +{ + m_xDialog->set_title(m_pImpl->m_aTitle1); +} + +FormulaModalDialog::~FormulaModalDialog() { } + +void FormulaModalDialog::Update(const OUString& _sExp) +{ + m_pImpl->Update(_sExp); +} + +void FormulaModalDialog::SetMeText(const OUString& _sText) +{ + m_pImpl->SetMeText(_sText); +} + +void FormulaModalDialog::CheckMatrix(OUString& aFormula) +{ + m_pImpl->CheckMatrix(aFormula); +} + +void FormulaModalDialog::Update() +{ + m_pImpl->Update(); +} + +::std::pair<RefButton*, RefEdit*> FormulaModalDialog::RefInputStartBefore( RefEdit* pEdit, RefButton* pButton ) +{ + return m_pImpl->RefInputStartBefore( pEdit, pButton ); +} + +void FormulaModalDialog::RefInputStartAfter() +{ + m_pImpl->RefInputStartAfter(); +} + +void FormulaModalDialog::RefInputDoneAfter() +{ + m_pImpl->RefInputDoneAfter( true/*bForced*/ ); +} + +void FormulaModalDialog::StoreFormEditData(FormEditData* pData) +{ + m_pImpl->StoreFormEditData(pData); +} + +// Initialisation / General functions for Dialog +FormulaDlg::FormulaDlg(SfxBindings* pB, SfxChildWindow* pCW, + weld::Window* pParent, + IFunctionManager const * _pFunctionMgr, IControlReferenceHandler* _pDlg) + : SfxModelessDialogController( pB, pCW, pParent, "formula/ui/formuladialog.ui", "FormulaDialog") + , m_pImpl(new FormulaDlg_Impl(*m_xDialog, *m_xBuilder, true/*_bSupportFunctionResult*/ + , true/*_bSupportResult*/ + , true/*_bSupportMatrix*/ + , this, _pFunctionMgr, _pDlg)) +{ + m_xDialog->set_title(m_pImpl->m_aTitle1); +} + +FormulaDlg::~FormulaDlg() +{ +} + +void FormulaDlg::Update(const OUString& _sExp) +{ + m_pImpl->Update(_sExp); +} + +void FormulaDlg::SetMeText(const OUString& _sText) +{ + m_pImpl->SetMeText(_sText); +} + +FormulaDlgMode FormulaDlg::SetMeText( const OUString& _sText, sal_Int32 PrivStart, sal_Int32 PrivEnd, bool bMatrix, bool _bSelect, bool _bUpdate) +{ + return m_pImpl->SetMeText( _sText, PrivStart, PrivEnd, bMatrix, _bSelect, _bUpdate); +} + +bool FormulaDlg::CheckMatrix(OUString& aFormula) +{ + return m_pImpl->CheckMatrix(aFormula); +} + +OUString FormulaDlg::GetMeText() const +{ + return m_pImpl->m_xMEdit->get_text(); +} + +void FormulaDlg::Update() +{ + m_pImpl->Update(); +} + +void FormulaDlg::DoEnter() +{ + m_pImpl->DoEnter(false); +} + +::std::pair<RefButton*, RefEdit*> FormulaDlg::RefInputStartBefore( RefEdit* pEdit, RefButton* pButton ) +{ + return m_pImpl->RefInputStartBefore( pEdit, pButton ); +} + +void FormulaDlg::RefInputStartAfter() +{ + m_pImpl->RefInputStartAfter(); +} + +void FormulaDlg::RefInputDoneAfter( bool bForced ) +{ + m_pImpl->RefInputDoneAfter( bForced ); +} + +void FormulaDlg::disableOk() +{ + m_pImpl->m_xBtnEnd->set_sensitive(false); +} + +void FormulaDlg::StoreFormEditData(FormEditData* pData) +{ + m_pImpl->StoreFormEditData(pData); +} + +const IFunctionDescription* FormulaDlg::getCurrentFunctionDescription() const +{ + SAL_WARN_IF( (m_pImpl->m_pFuncDesc && m_pImpl->m_pFuncDesc->getSuppressedArgumentCount() != m_pImpl->m_nArgs), + "formula.ui", "FormulaDlg::getCurrentFunctionDescription: getSuppressedArgumentCount " << + m_pImpl->m_pFuncDesc->getSuppressedArgumentCount() << " != m_nArgs " << m_pImpl->m_nArgs << " for " << + m_pImpl->m_pFuncDesc->getFunctionName()); + return m_pImpl->m_pFuncDesc; +} + +void FormulaDlg::UpdateParaWin( const Selection& _rSelection, const OUString& _sRefStr) +{ + m_pImpl->UpdateParaWin( _rSelection, _sRefStr); +} + +bool FormulaDlg::UpdateParaWin(Selection& _rSelection) +{ + return m_pImpl->UpdateParaWin(_rSelection); +} + +RefEdit* FormulaDlg::GetActiveEdit() +{ + return m_pImpl->m_xParaWin->GetActiveEdit(); +} + +const FormulaHelper& FormulaDlg::GetFormulaHelper() const +{ + return m_pImpl->GetFormulaHelper(); +} + +void FormulaDlg::SetEdSelection() +{ + m_pImpl->SetEdSelection(); +} + +void FormEditData::SaveValues() +{ + Reset(); +} + +void FormEditData::Reset() +{ + nMode = FormulaDlgMode::Formula; + nFStart = 0; + nOffset = 0; + bMatrix = false; + aSelection.Min() = 0; + aSelection.Max() = 0; + aUndoStr.clear(); +} + +FormEditData& FormEditData::operator=( const FormEditData& r ) +{ + nMode = r.nMode; + nFStart = r.nFStart; + nOffset = r.nOffset; + aUndoStr = r.aUndoStr; + bMatrix = r.bMatrix ; + aSelection = r.aSelection; + return *this; +} + +FormEditData::FormEditData() +{ + Reset(); +} + +FormEditData::~FormEditData() +{ +} + +FormEditData::FormEditData( const FormEditData& r ) +{ + *this = r; +} + + +} // formula + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |