Update git submodules
[LibreOffice.git] / formula / source / ui / dlg / formula.cxx
blob1d1b3c6c624e900249608fec522b0d1ff5e6a01e
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <memory>
21 #include <sfx2/viewfrm.hxx>
22 #include <vcl/svapp.hxx>
23 #include <vcl/weld.hxx>
25 #include <sal/log.hxx>
27 #include <unotools/charclass.hxx>
28 #include <comphelper/diagnose_ex.hxx>
30 #include "funcpage.hxx"
31 #include <formula/formula.hxx>
32 #include <formula/IFunctionDescription.hxx>
33 #include <formula/FormulaCompiler.hxx>
34 #include <formula/token.hxx>
35 #include <formula/tokenarray.hxx>
36 #include <formula/formdata.hxx>
37 #include <formula/formulahelper.hxx>
38 #include "structpg.hxx"
39 #include "parawin.hxx"
40 #include <strings.hrc>
41 #include <core_resource.hxx>
42 #include <com/sun/star/sheet/FormulaToken.hpp>
43 #include <com/sun/star/sheet/FormulaLanguage.hpp>
44 #include <com/sun/star/sheet/FormulaMapGroup.hpp>
45 #include <com/sun/star/sheet/FormulaMapGroupSpecialOffset.hpp>
46 #include <com/sun/star/sheet/XFormulaOpCodeMapper.hpp>
47 #include <com/sun/star/sheet/XFormulaParser.hpp>
48 #include <map>
50 // For tab page
51 #define TOKEN_OPEN 0
52 #define TOKEN_CLOSE 1
53 namespace formula
56 using namespace ::com::sun::star;
58 class FormulaDlg_Impl
60 public:
61 ::std::pair<RefButton*, RefEdit*>
62 RefInputStartBefore( RefEdit* pEdit, RefButton* pButton );
63 void RefInputStartAfter();
64 void RefInputDoneAfter( bool bForced );
65 bool CalcValue( const OUString& rStrExp, OUString& rStrResult, bool bForceMatrixFormula = false );
66 void CalcStruct( const OUString& rStrExp, bool bForceRecalcStruct = false );
67 void UpdateValues( bool bForceRecalcStruct = false );
68 void DeleteArgs();
69 sal_Int32 GetFunctionPos(sal_Int32 nPos);
70 void ClearAllParas();
72 void MakeTree(StructPage* _pTree, weld::TreeIter* pParent, const FormulaToken* pFuncToken,
73 const FormulaToken* _pToken, tools::Long Count);
74 void fillTree(StructPage* _pTree);
75 void UpdateTokenArray( const OUString& rStrExp);
76 OUString RepairFormula(const OUString& aFormula);
77 void FillDialog(bool bFlag = true);
78 bool EditNextFunc( bool bForward, sal_Int32 nFStart = NOT_FOUND );
79 void EditThisFunc(sal_Int32 nFStart);
81 OUString GetPrevFuncExpression( bool bStartFromEnd );
83 void StoreFormEditData(FormEditData* pEditData);
85 void Update();
86 void Update(const OUString& _sExp);
88 void SaveArg( sal_uInt16 nEd );
89 void UpdateSelection();
90 void DoEnter( bool bOk );
91 void FillListboxes();
92 void FillControls( bool &rbNext, bool &rbPrev);
94 FormulaDlgMode SetMeText( const OUString& _sText, sal_Int32 PrivStart, sal_Int32 PrivEnd, bool bMatrix, bool _bSelect, bool _bUpdate);
95 void SetMeText(const OUString& _sText);
96 bool CheckMatrix(OUString& aFormula /*IN/OUT*/);
98 void SetEdSelection();
100 bool UpdateParaWin(Selection& _rSelection);
101 void UpdateParaWin( const Selection& _rSelection, const OUString& _sRefStr);
103 void SetData( sal_Int32 nFStart, sal_Int32 nNextFStart, sal_Int32 nNextFEnd, sal_Int32& PrivStart, sal_Int32& PrivEnd);
105 RefEdit* GetCurrRefEdit();
107 const FormulaHelper& GetFormulaHelper() const { return m_aFormulaHelper;}
108 void InitFormulaOpCodeMapper();
110 void UpdateOldSel();
111 void FormulaCursor();
113 DECL_LINK( ModifyHdl, ParaWin&, void );
114 DECL_LINK( FxHdl, ParaWin&, void );
116 DECL_LINK( MatrixHdl, weld::Toggleable&, void );
117 DECL_LINK( FormulaHdl, weld::TextView&, void);
118 DECL_LINK( FormulaCursorHdl, weld::TextView&, void );
119 DECL_LINK( BtnHdl, weld::Button&, void );
120 DECL_LINK( DblClkHdl, FuncPage&, void );
121 DECL_LINK( FuncSelHdl, FuncPage&, void );
122 DECL_LINK( StructSelHdl, StructPage&, void );
123 public:
124 mutable uno::Reference< sheet::XFormulaOpCodeMapper> m_xOpCodeMapper;
125 uno::Sequence< sheet::FormulaToken > m_aTokenList;
126 ::std::unique_ptr<FormulaTokenArray> m_pTokenArray;
127 ::std::optional<FormulaTokenArrayPlainIterator> m_oTokenArrayIterator;
128 mutable uno::Sequence< sheet::FormulaOpCodeMapEntry > m_aSpecialOpCodes;
129 mutable uno::Sequence< sheet::FormulaToken > m_aSeparatorsOpCodes;
130 mutable uno::Sequence< sheet::FormulaOpCodeMapEntry > m_aFunctionOpCodes;
131 mutable const sheet::FormulaOpCodeMapEntry* m_pFunctionOpCodesEnd;
132 ::std::map<const FormulaToken*, sheet::FormulaToken> m_aTokenMap;
133 IFormulaEditorHelper* m_pHelper;
134 weld::Dialog& m_rDialog;
136 OUString m_aOldFormula;
137 bool m_bStructUpdate;
138 bool m_bUserMatrixFlag;
140 const OUString m_aTitle1;
141 const OUString m_aTitle2;
142 FormulaHelper m_aFormulaHelper;
144 OUString m_aEditHelpId;
146 OUString m_aOldHelp;
147 bool m_bMakingTree; // in method of constructing tree
149 bool m_bEditFlag;
150 const IFunctionDescription* m_pFuncDesc;
151 sal_Int32 m_nArgs;
152 ::std::vector< OUString > m_aArguments;
153 Selection m_aFuncSel;
155 sal_Int32 m_nFuncExpStart; ///< current formula position for treeview results
157 int m_nSelectionStart;
158 int m_nSelectionEnd;
160 RefEdit* m_pTheRefEdit;
161 RefButton* m_pTheRefButton;
163 std::unique_ptr<weld::Notebook> m_xTabCtrl;
164 std::unique_ptr<weld::Container> m_xParaWinBox;
165 std::unique_ptr<ParaWin> m_xParaWin;
166 std::unique_ptr<weld::Label> m_xFtHeadLine;
167 std::unique_ptr<weld::Label> m_xFtFuncName;
168 std::unique_ptr<weld::Label> m_xFtFuncDesc;
170 std::unique_ptr<weld::Label> m_xFtEditName;
172 std::unique_ptr<weld::Label> m_xFtResult;
173 std::unique_ptr<weld::Entry> m_xWndResult;
175 std::unique_ptr<weld::Label> m_xFtFormula;
176 std::unique_ptr<weld::TextView> m_xMEdit;
178 std::unique_ptr<weld::CheckButton> m_xBtnMatrix;
179 std::unique_ptr<weld::Button> m_xBtnCancel;
181 std::unique_ptr<weld::Button> m_xBtnBackward;
182 std::unique_ptr<weld::Button> m_xBtnForward;
183 std::unique_ptr<weld::Button> m_xBtnEnd;
185 std::unique_ptr<weld::Label> m_xFtFormResult;
186 std::unique_ptr<weld::Entry> m_xWndFormResult;
188 std::unique_ptr<RefEdit> m_xEdRef;
189 std::unique_ptr<RefButton> m_xRefBtn;
191 std::unique_ptr<FuncPage> m_xFuncPage;
192 std::unique_ptr<StructPage> m_xStructPage;
194 FormulaDlg_Impl(weld::Dialog& rDialog,
195 weld::Builder& rBuilder,
196 bool _bSupportFunctionResult,
197 bool _bSupportResult,
198 bool _bSupportMatrix,
199 IFormulaEditorHelper* _pHelper,
200 const IFunctionManager* _pFunctionMgr,
201 IControlReferenceHandler* _pDlg);
202 ~FormulaDlg_Impl();
205 FormulaDlg_Impl::FormulaDlg_Impl(weld::Dialog& rDialog,
206 weld::Builder& rBuilder,
207 bool _bSupportFunctionResult,
208 bool _bSupportResult,
209 bool _bSupportMatrix,
210 IFormulaEditorHelper* _pHelper,
211 const IFunctionManager* _pFunctionMgr,
212 IControlReferenceHandler* _pDlg)
213 : m_pFunctionOpCodesEnd(nullptr)
214 , m_pHelper(_pHelper)
215 , m_rDialog(rDialog)
216 , m_bUserMatrixFlag(false)
217 , m_aTitle1( ForResId( STR_TITLE1 ) )
218 , m_aTitle2( ForResId( STR_TITLE2 ) )
219 , m_aFormulaHelper(_pFunctionMgr)
220 , m_bMakingTree(false)
221 , m_pFuncDesc(nullptr)
222 , m_nArgs(0)
223 , m_nFuncExpStart(0)
224 , m_nSelectionStart(-1)
225 , m_nSelectionEnd(-1)
226 , m_pTheRefEdit(nullptr)
227 , m_pTheRefButton(nullptr)
228 , m_xTabCtrl(rBuilder.weld_notebook(u"tabcontrol"_ustr))
229 , m_xParaWinBox(rBuilder.weld_container(u"BOX"_ustr))
230 , m_xFtHeadLine(rBuilder.weld_label(u"headline"_ustr))
231 , m_xFtFuncName(rBuilder.weld_label(u"funcname"_ustr))
232 , m_xFtFuncDesc(rBuilder.weld_label(u"funcdesc"_ustr))
233 , m_xFtEditName(rBuilder.weld_label(u"editname"_ustr))
234 , m_xFtResult(rBuilder.weld_label(u"label2"_ustr))
235 , m_xWndResult(rBuilder.weld_entry(u"result"_ustr))
236 , m_xFtFormula(rBuilder.weld_label(u"formula"_ustr))
237 , m_xMEdit(rBuilder.weld_text_view(u"ed_formula"_ustr))
238 , m_xBtnMatrix(rBuilder.weld_check_button(u"array"_ustr))
239 , m_xBtnCancel(rBuilder.weld_button(u"cancel"_ustr))
240 , m_xBtnBackward(rBuilder.weld_button(u"back"_ustr))
241 , m_xBtnForward(rBuilder.weld_button(u"next"_ustr))
242 , m_xBtnEnd(rBuilder.weld_button(u"ok"_ustr))
243 , m_xFtFormResult(rBuilder.weld_label(u"label1"_ustr))
244 , m_xWndFormResult(rBuilder.weld_entry(u"formula_result"_ustr))
245 , m_xEdRef(new RefEdit(rBuilder.weld_entry(u"ED_REF"_ustr)))
246 , m_xRefBtn(new RefButton(rBuilder.weld_button(u"RB_REF"_ustr)))
248 auto nWidth = m_xMEdit->get_approximate_digit_width() * 62;
250 //Space for two lines of text
251 m_xFtHeadLine->set_label(u"X\nX\n"_ustr);
252 auto nHeight = m_xFtHeadLine->get_preferred_size().Height();
253 m_xFtHeadLine->set_size_request(nWidth, nHeight);
254 m_xFtHeadLine->set_label(u""_ustr);
256 m_xFtFuncName->set_label(u"X\nX\n"_ustr);
257 nHeight = m_xFtFuncName->get_preferred_size().Height();
258 m_xFtFuncName->set_size_request(nWidth, nHeight);
259 m_xFtFuncDesc->set_size_request(nWidth, nHeight);
260 m_xFtFuncName->set_label(u""_ustr);
262 m_xMEdit->set_size_request(nWidth,
263 m_xMEdit->get_height_rows(5));
265 m_xEdRef->SetReferences(_pDlg, m_xFtEditName.get());
266 m_xRefBtn->SetReferences(_pDlg, m_xEdRef.get());
268 m_xParaWin.reset(new ParaWin(m_xParaWinBox.get(), _pDlg));
269 m_xParaWin->Show();
270 m_xParaWinBox->hide();
271 m_xFtEditName->hide();
272 m_xEdRef->GetWidget()->hide();
273 m_xRefBtn->GetWidget()->hide();
275 m_xMEdit->set_accessible_name(m_xFtFormula->get_label());
277 m_aEditHelpId = m_xMEdit->get_help_id();
279 m_bEditFlag =false;
280 m_bStructUpdate =true;
281 m_xParaWin->SetArgModifiedHdl( LINK( this, FormulaDlg_Impl, ModifyHdl ) );
282 m_xParaWin->SetFxHdl( LINK( this, FormulaDlg_Impl, FxHdl ) );
284 m_xFuncPage.reset(new FuncPage(m_xTabCtrl->get_page(u"functiontab"_ustr), _pFunctionMgr));
285 m_xStructPage.reset(new StructPage(m_xTabCtrl->get_page(u"structtab"_ustr)));
286 m_xTabCtrl->set_current_page(u"functiontab"_ustr);
288 m_aOldHelp = m_rDialog.get_help_id(); // HelpId from resource always for "Page 1"
290 m_xFtResult->set_visible( _bSupportResult );
291 m_xWndResult->set_visible( _bSupportResult );
293 m_xFtFormResult->set_visible( _bSupportFunctionResult );
294 m_xWndFormResult->set_visible( _bSupportFunctionResult );
296 if ( _bSupportMatrix )
297 m_xBtnMatrix->connect_toggled( LINK( this, FormulaDlg_Impl, MatrixHdl ) );
298 else
299 m_xBtnMatrix->hide();
301 m_xBtnCancel->connect_clicked( LINK( this, FormulaDlg_Impl, BtnHdl ) );
302 m_xBtnEnd->connect_clicked( LINK( this, FormulaDlg_Impl, BtnHdl ) );
303 m_xBtnForward->connect_clicked( LINK( this, FormulaDlg_Impl, BtnHdl ) );
304 m_xBtnBackward->connect_clicked( LINK( this, FormulaDlg_Impl, BtnHdl ) );
306 m_xFuncPage->SetDoubleClickHdl( LINK( this, FormulaDlg_Impl, DblClkHdl ) );
307 m_xFuncPage->SetSelectHdl( LINK( this, FormulaDlg_Impl, FuncSelHdl) );
308 m_xStructPage->SetSelectionHdl( LINK( this, FormulaDlg_Impl, StructSelHdl ) );
309 m_xMEdit->connect_changed( LINK( this, FormulaDlg_Impl, FormulaHdl ) );
310 m_xMEdit->connect_cursor_position( LINK( this, FormulaDlg_Impl, FormulaCursorHdl ) );
312 vcl::Font aFntLight = m_xFtFormula->get_font();
313 vcl::Font aFntBold = aFntLight;
314 aFntBold.SetWeight( WEIGHT_BOLD );
316 m_xParaWin->SetArgumentFonts( aFntBold, aFntLight);
319 FormulaDlg_Impl::~FormulaDlg_Impl()
321 m_xTabCtrl->remove_page(u"functiontab"_ustr);
322 m_xTabCtrl->remove_page(u"structtab"_ustr);
324 DeleteArgs();
327 void FormulaDlg_Impl::StoreFormEditData(FormEditData* pData)
329 if (!pData) // it won't be destroyed via Close
330 return;
332 int nStartPos, nEndPos;
333 m_xMEdit->get_selection_bounds(nStartPos, nEndPos);
334 if (nStartPos > nEndPos)
335 std::swap(nStartPos, nEndPos);
337 pData->SetFStart(nStartPos);
338 pData->SetSelection(Selection(nStartPos, nEndPos));
340 if (m_xTabCtrl->get_current_page_ident() == "functiontab")
341 pData->SetMode( FormulaDlgMode::Formula );
342 else
343 pData->SetMode( FormulaDlgMode::Edit );
344 pData->SetUndoStr(m_xMEdit->get_text());
345 pData->SetMatrixFlag(m_xBtnMatrix->get_active());
348 void FormulaDlg_Impl::InitFormulaOpCodeMapper()
350 if ( m_xOpCodeMapper.is() )
351 return;
353 m_xOpCodeMapper = m_pHelper->getFormulaOpCodeMapper();
354 m_aFunctionOpCodes = m_xOpCodeMapper->getAvailableMappings( sheet::FormulaLanguage::ODFF, sheet::FormulaMapGroup::FUNCTIONS);
355 m_pFunctionOpCodesEnd = m_aFunctionOpCodes.getConstArray() + m_aFunctionOpCodes.getLength();
357 // 0:TOKEN_OPEN, 1:TOKEN_CLOSE, 2:TOKEN_SEP
358 uno::Sequence< OUString > aArgs { u"("_ustr, u")"_ustr, u";"_ustr };
359 m_aSeparatorsOpCodes = m_xOpCodeMapper->getMappings( aArgs, sheet::FormulaLanguage::ODFF);
361 m_aSpecialOpCodes = m_xOpCodeMapper->getAvailableMappings( sheet::FormulaLanguage::ODFF, sheet::FormulaMapGroup::SPECIAL);
364 void FormulaDlg_Impl::DeleteArgs()
366 ::std::vector< OUString>().swap(m_aArguments);
367 m_nArgs = 0;
370 sal_Int32 FormulaDlg_Impl::GetFunctionPos(sal_Int32 nPos)
372 if ( !m_aTokenList.hasElements() )
373 return SAL_MAX_INT32;
375 const sal_Unicode sep = m_pHelper->getFunctionManager()->getSingleToken(IFunctionManager::eSep);
377 sal_Int32 nFuncPos = SAL_MAX_INT32;
378 OUString aFormString = m_aFormulaHelper.GetCharClass().uppercase(m_xMEdit->get_text());
380 const uno::Reference< sheet::XFormulaParser > xParser(m_pHelper->getFormulaParser());
381 const table::CellAddress aRefPos(m_pHelper->getReferencePosition());
383 const sheet::FormulaToken* pIter = m_aTokenList.getConstArray();
384 const sheet::FormulaToken* pEnd = pIter + m_aTokenList.getLength();
387 bool bFlag = false;
388 sal_Int32 nTokPos = 1;
389 sal_Int32 nOldTokPos = 1;
390 sal_Int32 nPrevFuncPos = 1;
391 short nBracketCount = 0;
392 const sal_Int32 nOpPush = m_aSpecialOpCodes[sheet::FormulaMapGroupSpecialOffset::PUSH].Token.OpCode;
393 const sal_Int32 nOpSpaces = m_aSpecialOpCodes[sheet::FormulaMapGroupSpecialOffset::SPACES].Token.OpCode;
394 const sal_Int32 nOpWhitespace = m_aSpecialOpCodes[sheet::FormulaMapGroupSpecialOffset::WHITESPACE].Token.OpCode;
395 while ( pIter != pEnd )
397 const sal_Int32 eOp = pIter->OpCode;
398 uno::Sequence<sheet::FormulaToken> aArgs { *pIter };
399 const OUString aString = xParser->printFormula( aArgs, aRefPos);
400 const sheet::FormulaToken* pNextToken = pIter + 1;
402 if ( !m_bUserMatrixFlag && FormulaCompiler::IsMatrixFunction(static_cast<OpCode>(eOp)) )
404 m_xBtnMatrix->set_active(true);
407 if (eOp == nOpPush || eOp == nOpSpaces || eOp == nOpWhitespace)
409 const sal_Int32 n1 = nTokPos < 0 ? -1 : aFormString.indexOf( sep, nTokPos);
410 const sal_Int32 n2 = nTokPos < 0 ? -1 : aFormString.indexOf( ')', nTokPos);
411 sal_Int32 nXXX = nTokPos;
412 if ( n1 < n2 && n1 != -1 )
414 nTokPos = n1;
416 else
418 nTokPos = n2;
420 if ( pNextToken != pEnd )
422 aArgs.getArray()[0] = *pNextToken;
423 const OUString a2String = xParser->printFormula( aArgs, aRefPos);
424 const sal_Int32 n3 = nXXX < 0 ? -1 : aFormString.indexOf( a2String, nXXX);
425 if (n3 < nTokPos && n3 != -1)
426 nTokPos = n3;
429 else
431 nTokPos = nTokPos + aString.getLength();
434 if ( eOp == m_aSeparatorsOpCodes[TOKEN_OPEN].OpCode )
436 nBracketCount++;
437 bFlag = true;
439 else if ( eOp == m_aSeparatorsOpCodes[TOKEN_CLOSE].OpCode )
441 nBracketCount--;
442 bFlag = false;
443 nFuncPos = nPrevFuncPos;
445 bool bIsFunction = std::any_of( m_aFunctionOpCodes.getConstArray(),
446 m_pFunctionOpCodesEnd,
447 [&eOp](const sheet::FormulaOpCodeMapEntry& aEntry) { return aEntry.Token.OpCode == eOp; });
449 if ( bIsFunction && nOpSpaces != eOp && nOpWhitespace != eOp )
451 nPrevFuncPos = nFuncPos;
452 nFuncPos = nOldTokPos;
455 if ( nOldTokPos <= nPos && nPos < nTokPos )
457 if ( !bIsFunction )
459 if ( nBracketCount < 1 )
461 nFuncPos = m_xMEdit->get_text().getLength();
463 else if ( !bFlag )
465 nFuncPos = nPrevFuncPos;
468 break;
471 pIter = pNextToken;
472 nOldTokPos = nTokPos;
473 } // while ( pIter != pEnd )
475 catch ( const uno::Exception& )
477 TOOLS_WARN_EXCEPTION("formula.ui", "FormulaDlg_Impl::GetFunctionPos");
480 return nFuncPos;
483 bool FormulaDlg_Impl::CalcValue( const OUString& rStrExp, OUString& rStrResult, bool bForceMatrixFormula )
485 bool bResult = true;
487 if ( !rStrExp.isEmpty() )
489 // Only calculate the value when there isn't any more keyboard input:
491 // Make this debuggable by assigning to a variable that can be changed
492 // from within the debugger.
493 bool bInput = Application::AnyInput( VclInputFlags::KEYBOARD );
494 if ( !bInput )
496 bResult = m_pHelper->calculateValue( rStrExp, rStrResult, bForceMatrixFormula || m_xBtnMatrix->get_active());
498 else
499 bResult = false;
502 return bResult;
505 void FormulaDlg_Impl::UpdateValues( bool bForceRecalcStruct )
507 // Take a force-array context into account. RPN creation propagated those
508 // to tokens that are ref-counted so also available in the token array.
509 bool bForceArray = false;
510 // Only necessary if it's not a matrix formula anyway and matrix evaluation
511 // is supported, i.e. the button is visible.
512 if (m_xBtnMatrix->get_visible() && !m_xBtnMatrix->get_active())
514 std::unique_ptr<FormulaCompiler> pCompiler(m_pHelper->createCompiler(*m_pTokenArray));
515 // In the case of the reportdesign dialog there is no currently active
516 // OpCode symbol mapping that could be used to create strings from
517 // tokens, it's all dreaded API mapping. However, in that case there's
518 // no array/matrix support anyway, but ensure checking.
519 if (pCompiler->GetCurrentOpCodeMap())
521 const sal_Int32 nPos = m_aFuncSel.Min();
522 assert( 0 <= nPos && nPos < m_pHelper->getCurrentFormula().getLength());
523 OUStringBuffer aBuf;
524 const FormulaToken* pToken = nullptr;
525 for (pToken = m_oTokenArrayIterator->First(); pToken; pToken = m_oTokenArrayIterator->Next())
527 pCompiler->CreateStringFromToken( aBuf, pToken);
528 if (nPos < aBuf.getLength())
529 break;
531 if (pToken && nPos < aBuf.getLength())
532 bForceArray = pToken->IsInForceArray();
536 OUString aStrResult;
537 if (m_pFuncDesc && CalcValue( m_pFuncDesc->getFormula( m_aArguments), aStrResult, bForceArray))
538 m_xWndResult->set_text( aStrResult );
540 if (m_bMakingTree)
541 return;
543 aStrResult.clear();
544 if ( CalcValue( m_pHelper->getCurrentFormula(), aStrResult ) )
545 m_xWndFormResult->set_text( aStrResult );
546 else
548 aStrResult.clear();
549 m_xWndFormResult->set_text( aStrResult );
551 CalcStruct( m_xMEdit->get_text(), bForceRecalcStruct);
554 void FormulaDlg_Impl::CalcStruct( const OUString& rStrExp, bool bForceRecalcStruct )
556 sal_Int32 nLength = rStrExp.getLength();
558 if ( !(!rStrExp.isEmpty() && (bForceRecalcStruct || m_aOldFormula != rStrExp) && m_bStructUpdate))
559 return;
561 m_xStructPage->ClearStruct();
563 OUString aString = rStrExp;
564 if (rStrExp[nLength-1] == '(')
566 aString = aString.copy( 0, nLength-1);
569 aString = aString.replaceAll( "\n", "");
570 OUString aStrResult;
572 if ( CalcValue( aString, aStrResult ) )
573 m_xWndFormResult->set_text(aStrResult);
575 UpdateTokenArray(aString);
576 fillTree(m_xStructPage.get());
578 m_aOldFormula = rStrExp;
579 if (rStrExp[nLength-1] == '(')
580 UpdateTokenArray(rStrExp);
583 void FormulaDlg_Impl::MakeTree(StructPage* _pTree, weld::TreeIter* pParent, const FormulaToken* pFuncToken,
584 const FormulaToken* _pToken, tools::Long Count)
586 if ( _pToken == nullptr || Count <= 0 )
587 return;
589 tools::Long nParas = _pToken->GetParamCount();
590 OpCode eOp = _pToken->GetOpCode();
592 // #i101512# for output, the original token is needed
593 const FormulaToken* pOrigToken = (_pToken->GetType() == svFAP) ? _pToken->GetFAPOrigToken() : _pToken;
594 ::std::map<const FormulaToken*, sheet::FormulaToken>::const_iterator itr = m_aTokenMap.find(pOrigToken);
595 if (itr == m_aTokenMap.end())
596 return;
598 uno::Sequence<sheet::FormulaToken> aArgs { itr->second };
601 const table::CellAddress aRefPos(m_pHelper->getReferencePosition());
602 const OUString aResult = m_pHelper->getFormulaParser()->printFormula( aArgs, aRefPos);
604 if ( nParas > 0 || (nParas == 0 && _pToken->IsFunction()) )
606 std::unique_ptr<weld::TreeIter> xEntry;
607 weld::TreeIter* pEntry;
609 bool bCalcSubformula = false;
610 OUString aTest = _pTree->GetEntryText(pParent);
612 if (aTest == aResult && (eOp == ocAdd || eOp == ocMul || eOp == ocAmpersand))
614 pEntry = pParent;
616 else
618 xEntry = m_xStructPage->GetTlbStruct().make_iterator();
620 if (eOp == ocBad)
622 _pTree->InsertEntry(aResult, pParent, STRUCT_ERROR, 0, _pToken, *xEntry);
624 else if (!((SC_OPCODE_START_BIN_OP <= eOp && eOp < SC_OPCODE_STOP_BIN_OP) ||
625 (SC_OPCODE_START_UN_OP <= eOp && eOp < SC_OPCODE_STOP_UN_OP)))
627 // Not a binary or unary operator.
628 bCalcSubformula = true;
629 _pTree->InsertEntry(aResult, pParent, STRUCT_FOLDER, 0, _pToken, *xEntry);
631 else
633 /* TODO: question remains, why not sub calculate operators? */
634 _pTree->InsertEntry(aResult, pParent, STRUCT_FOLDER, 0, _pToken, *xEntry);
637 pEntry = xEntry.get();
640 MakeTree(_pTree, pEntry, _pToken, m_oTokenArrayIterator->PrevRPN(), nParas);
642 if (bCalcSubformula)
644 OUString aFormula;
646 if (!m_bMakingTree)
648 // gets the last subformula result
649 m_bMakingTree = true;
650 aFormula = GetPrevFuncExpression( true);
652 else
654 // gets subsequent subformula results (from the back)
655 aFormula = GetPrevFuncExpression( false);
658 OUString aStr;
659 if (CalcValue( aFormula, aStr, _pToken->IsInForceArray()))
660 m_xWndResult->set_text( aStr );
661 aStr = m_xWndResult->get_text();
662 m_xStructPage->GetTlbStruct().set_text(*pEntry, aResult + " = " + aStr);
665 --Count;
666 m_oTokenArrayIterator->NextRPN(); /* TODO: what's this to be? ThisRPN()? */
667 MakeTree( _pTree, pParent, _pToken, m_oTokenArrayIterator->PrevRPN(), Count);
669 else
671 std::unique_ptr<weld::TreeIter> xEntry(m_xStructPage->GetTlbStruct().make_iterator());
672 if (eOp == ocBad)
674 _pTree->InsertEntry( aResult, pParent, STRUCT_ERROR, 0, _pToken, *xEntry);
676 else if (eOp == ocPush)
678 // Interpret range reference in matrix context to resolve
679 // as array elements. Depending on parameter classification
680 // a scalar value (non-array context) is calculated first.
681 OUString aUnforcedResult;
682 bool bForceMatrix = (!m_xBtnMatrix->get_active() &&
683 (_pToken->GetType() == svDoubleRef || _pToken->GetType() == svExternalDoubleRef));
684 if (bForceMatrix && pFuncToken)
686 formula::ParamClass eParamClass = ParamClass::Reference;
687 if (pFuncToken->IsInForceArray())
688 eParamClass = ParamClass::ForceArray;
689 else
691 std::shared_ptr<FormulaCompiler> pCompiler = m_pHelper->getCompiler();
692 if (pCompiler)
693 eParamClass = pCompiler->GetForceArrayParameter( pFuncToken, Count - 1);
695 switch (eParamClass)
697 case ParamClass::Unknown:
698 case ParamClass::Bounds:
699 case ParamClass::Value:
700 if (CalcValue( "=" + aResult, aUnforcedResult, false) && aUnforcedResult != aResult)
701 aUnforcedResult += " ";
702 else
703 aUnforcedResult.clear();
704 break;
705 case ParamClass::Reference:
706 case ParamClass::ReferenceOrRefArray:
707 case ParamClass::Array:
708 case ParamClass::ForceArray:
709 case ParamClass::ReferenceOrForceArray:
710 case ParamClass::SuppressedReferenceOrForceArray:
711 case ParamClass::ForceArrayReturn:
712 ; // nothing, only as array/matrix
713 // no default to get compiler warning
716 OUString aCellResult;
717 if (CalcValue( "=" + aResult, aCellResult, bForceMatrix) && aCellResult != aResult)
719 // Cell is a formula, print subformula.
720 // With scalar values prints "A1:A3 = 2 {1;2;3}"
721 _pTree->InsertEntry( aResult + " = " + aUnforcedResult + aCellResult,
722 pParent, STRUCT_END, 0, _pToken, *xEntry);
724 else
725 _pTree->InsertEntry(aResult, pParent, STRUCT_END, 0, _pToken, *xEntry);
727 else
729 _pTree->InsertEntry(aResult, pParent, STRUCT_END, 0, _pToken, *xEntry);
731 --Count;
732 MakeTree( _pTree, pParent, _pToken, m_oTokenArrayIterator->PrevRPN(), Count);
735 catch (const uno::Exception&)
737 DBG_UNHANDLED_EXCEPTION("formula.ui");
741 void FormulaDlg_Impl::fillTree(StructPage* _pTree)
743 InitFormulaOpCodeMapper();
744 FormulaToken* pToken = m_oTokenArrayIterator->LastRPN();
746 if ( pToken != nullptr)
748 MakeTree( _pTree, nullptr, nullptr, pToken, 1);
749 m_bMakingTree = false;
753 void FormulaDlg_Impl::UpdateTokenArray( const OUString& rStrExp)
755 m_aTokenMap.clear();
756 m_aTokenList.realloc(0);
759 const table::CellAddress aRefPos(m_pHelper->getReferencePosition());
760 m_aTokenList = m_pHelper->getFormulaParser()->parseFormula( rStrExp, aRefPos);
762 catch (const uno::Exception&)
764 DBG_UNHANDLED_EXCEPTION("formula.ui");
766 InitFormulaOpCodeMapper();
767 m_pTokenArray = m_pHelper->convertToTokenArray(m_aTokenList);
768 m_oTokenArrayIterator.emplace(*m_pTokenArray);
769 const sal_Int32 nLen = static_cast<sal_Int32>(m_pTokenArray->GetLen());
770 FormulaToken** pTokens = m_pTokenArray->GetArray();
771 if ( pTokens && nLen == m_aTokenList.getLength() )
773 for (sal_Int32 nPos = 0; nPos < nLen; nPos++)
775 m_aTokenMap.emplace( pTokens[nPos], m_aTokenList[nPos] );
777 } // if ( pTokens && nLen == m_aTokenList.getLength() )
779 std::unique_ptr<FormulaCompiler> pCompiler(m_pHelper->createCompiler(*m_pTokenArray));
780 // #i101512# Disable special handling of jump commands.
781 pCompiler->EnableJumpCommandReorder(false);
782 pCompiler->EnableStopOnError(false);
783 pCompiler->SetComputeIIFlag(true);
784 pCompiler->SetMatrixFlag(m_bUserMatrixFlag);
785 pCompiler->CompileTokenArray();
788 void FormulaDlg_Impl::FillDialog(bool bFlag)
790 bool bNext = true, bPrev = true;
791 if (bFlag)
792 FillControls( bNext, bPrev);
793 FillListboxes();
794 if (bFlag)
796 m_xBtnBackward->set_sensitive(bPrev);
797 m_xBtnForward->set_sensitive(bNext);
800 OUString aStrResult;
802 if ( CalcValue( m_pHelper->getCurrentFormula(), aStrResult ) )
803 m_xWndFormResult->set_text( aStrResult );
804 else
806 aStrResult.clear();
807 m_xWndFormResult->set_text( aStrResult );
811 void FormulaDlg_Impl::FillListboxes()
813 // Switch between the "Pages"
814 FormEditData* pData = m_pHelper->getFormEditData();
815 // 1. Page: select function
816 if ( m_pFuncDesc && m_pFuncDesc->getCategory() )
818 // We'll never have more than int32 max categories so this is safe ...
819 // Category listbox holds additional entries for Last Used and All, so
820 // the offset should be two but hard coded numbers are ugly...
821 const sal_Int32 nCategoryOffset = m_xFuncPage->GetCategoryEntryCount() - m_aFormulaHelper.GetCategoryCount();
822 if ( m_xFuncPage->GetCategory() != static_cast<sal_Int32>(m_pFuncDesc->getCategory()->getNumber() + nCategoryOffset) )
823 m_xFuncPage->SetCategory(m_pFuncDesc->getCategory()->getNumber() + nCategoryOffset);
825 sal_Int32 nPos = m_xFuncPage->GetFuncPos(m_pFuncDesc);
827 m_xFuncPage->SetFunction(nPos);
829 else if ( pData )
831 // tdf#104487 - remember last used function category
832 m_xFuncPage->SetCategory(FuncPage::GetRememeberdFunctionCategory());
833 m_xFuncPage->SetFunction( -1 );
835 FuncSelHdl(*m_xFuncPage);
837 m_pHelper->setDispatcherLock( true ); // Activate Modal-Mode
839 // HelpId for 1. page is the one from the resource
840 m_rDialog.set_help_id( m_aOldHelp );
843 void FormulaDlg_Impl::FillControls( bool &rbNext, bool &rbPrev)
845 // Switch between the "Pages"
846 FormEditData* pData = m_pHelper->getFormEditData();
847 if (!pData )
848 return;
850 // 2. Page or Edit: show selected function
852 sal_Int32 nFStart = pData->GetFStart();
853 OUString aFormula = m_pHelper->getCurrentFormula() + " )";
854 sal_Int32 nNextFStart = nFStart;
855 sal_Int32 nNextFEnd = 0;
857 DeleteArgs();
858 const IFunctionDescription* pOldFuncDesc = m_pFuncDesc;
860 if ( m_aFormulaHelper.GetNextFunc( aFormula, false,
861 nNextFStart, &nNextFEnd, &m_pFuncDesc, &m_aArguments ) )
863 const bool bTestFlag = (pOldFuncDesc != m_pFuncDesc);
864 if (bTestFlag)
866 m_xFtHeadLine->hide();
867 m_xFtFuncName->hide();
868 m_xFtFuncDesc->hide();
869 m_xParaWin->SetFunctionDesc(m_pFuncDesc);
870 m_xFtEditName->set_label( m_pFuncDesc->getFunctionName() );
871 m_xFtEditName->show();
872 m_xParaWinBox->show();
873 const OUString aHelpId = m_pFuncDesc->getHelpId();
874 if ( !aHelpId.isEmpty() )
875 m_xMEdit->set_help_id(aHelpId);
878 sal_Int32 nOldStart, nOldEnd;
879 m_pHelper->getSelection( nOldStart, nOldEnd );
880 if ( nOldStart != nNextFStart || nOldEnd != nNextFEnd )
882 m_pHelper->setSelection( nNextFStart, nNextFEnd );
884 m_aFuncSel.Min() = nNextFStart;
885 m_aFuncSel.Max() = nNextFEnd;
887 if (!m_bEditFlag)
888 m_xMEdit->set_text(m_pHelper->getCurrentFormula());
889 sal_Int32 PrivStart, PrivEnd;
890 m_pHelper->getSelection( PrivStart, PrivEnd);
891 if (!m_bEditFlag)
892 m_xMEdit->select_region(PrivStart, PrivEnd);
894 m_nArgs = m_pFuncDesc->getSuppressedArgumentCount();
895 sal_uInt16 nOffset = pData->GetOffset();
897 // Concatenate the Edit's for Focus-Control
899 if (bTestFlag)
900 m_xParaWin->SetArgumentOffset(nOffset);
901 sal_uInt16 nActiv = 0;
902 sal_Int32 nArgPos = m_aFormulaHelper.GetArgStart( aFormula, nFStart, 0 );
904 int nStartPos, nEndPos;
905 m_xMEdit->get_selection_bounds(nStartPos, nEndPos);
906 if (nStartPos > nEndPos)
907 std::swap(nStartPos, nEndPos);
909 sal_Int32 nEditPos = nStartPos;
910 bool bFlag = false;
912 for (sal_Int32 i = 0; i < m_nArgs; i++)
914 sal_Int32 nLength = m_aArguments[i].getLength()+1;
915 m_xParaWin->SetArgument( i, m_aArguments[i]);
916 if (nArgPos <= nEditPos && nEditPos < nArgPos+nLength)
918 nActiv = i;
919 bFlag = true;
921 nArgPos = nArgPos + nLength;
923 m_xParaWin->UpdateParas();
925 if (bFlag)
927 m_xParaWin->SetActiveLine(nActiv);
930 UpdateValues();
932 else
934 m_xFtEditName->set_label(u""_ustr);
935 m_xMEdit->set_help_id(m_aEditHelpId);
937 // test if before/after are anymore functions
939 sal_Int32 nTempStart = m_aFormulaHelper.GetArgStart( aFormula, nFStart, 0 );
940 rbNext = m_aFormulaHelper.GetNextFunc( aFormula, false, nTempStart );
942 int nStartPos, nEndPos;
943 m_xMEdit->get_selection_bounds(nStartPos, nEndPos);
944 if (nStartPos > nEndPos)
945 std::swap(nStartPos, nEndPos);
947 nTempStart = nStartPos;
948 pData->SetFStart(nTempStart);
949 rbPrev = m_aFormulaHelper.GetNextFunc( aFormula, true, nTempStart );
953 void FormulaDlg_Impl::ClearAllParas()
955 DeleteArgs();
956 m_pFuncDesc = nullptr;
957 m_xParaWin->ClearAll();
958 m_xWndResult->set_text(OUString());
959 m_xFtFuncName->set_label(OUString());
960 FuncSelHdl(*m_xFuncPage);
962 if (m_xFuncPage->IsVisible())
964 m_xFtEditName->hide();
965 m_xParaWinBox->hide();
967 m_xBtnForward->set_sensitive(true); //@new
968 m_xFtHeadLine->show();
969 m_xFtFuncName->show();
970 m_xFtFuncDesc->show();
974 OUString FormulaDlg_Impl::RepairFormula(const OUString& aFormula)
976 OUString aResult('=');
979 UpdateTokenArray(aFormula);
981 if ( m_aTokenList.hasElements() )
983 const table::CellAddress aRefPos(m_pHelper->getReferencePosition());
984 const OUString sFormula( m_pHelper->getFormulaParser()->printFormula( m_aTokenList, aRefPos));
985 if ( sFormula.isEmpty() || sFormula[0] != '=' )
986 aResult += sFormula;
987 else
988 aResult = sFormula;
992 catch ( const uno::Exception& )
994 TOOLS_WARN_EXCEPTION("formula.ui", "FormulaDlg_Impl::RepairFormula");
996 return aResult;
999 void FormulaDlg_Impl::DoEnter(bool bOk)
1001 // Accept input to the document or cancel
1002 if ( bOk)
1004 // remove dummy arguments
1005 OUString aInputFormula = m_pHelper->getCurrentFormula();
1006 OUString aString = RepairFormula(m_xMEdit->get_text());
1007 m_pHelper->setSelection( 0, aInputFormula.getLength());
1008 m_pHelper->setCurrentFormula(aString);
1011 m_pHelper->switchBack();
1013 m_pHelper->dispatch( bOk, m_xBtnMatrix->get_active());
1014 // Clear data
1015 m_pHelper->deleteFormData();
1017 // Close dialog
1018 m_pHelper->doClose(bOk);
1022 IMPL_LINK(FormulaDlg_Impl, BtnHdl, weld::Button&, rBtn, void)
1024 if (&rBtn == m_xBtnCancel.get())
1026 DoEnter(false); // closes the Dialog
1028 else if (&rBtn == m_xBtnEnd.get())
1030 DoEnter(true); // closes the Dialog
1032 else if (&rBtn == m_xBtnForward.get())
1034 const IFunctionDescription* pDesc;
1035 sal_Int32 nSelFunc = m_xFuncPage->GetFunction();
1036 if (nSelFunc != -1)
1037 pDesc = m_xFuncPage->GetFuncDesc();
1038 else
1040 // Do not overwrite the selected formula expression, just edit the
1041 // unlisted function.
1042 m_pFuncDesc = pDesc = nullptr;
1045 if (pDesc == m_pFuncDesc || !m_xFuncPage->IsVisible())
1046 EditNextFunc( true );
1047 else
1049 DblClkHdl(*m_xFuncPage); //new
1050 m_xBtnForward->set_sensitive(false); //new
1053 else if (&rBtn == m_xBtnBackward.get())
1055 m_bEditFlag = false;
1056 m_xBtnForward->set_sensitive(true);
1057 EditNextFunc( false );
1061 // Functions for 1. Page
1063 // Handler for Listboxes
1065 IMPL_LINK_NOARG( FormulaDlg_Impl, DblClkHdl, FuncPage&, void)
1067 // ex-UpdateLRUList
1068 const IFunctionDescription* pDesc = m_xFuncPage->GetFuncDesc();
1069 m_pHelper->insertEntryToLRUList(pDesc);
1071 OUString aFuncName = m_xFuncPage->GetSelFunctionName() + "()";
1072 m_pHelper->setCurrentFormula(aFuncName);
1073 m_xMEdit->replace_selection(aFuncName);
1075 int nStartPos, nEndPos;
1076 m_xMEdit->get_selection_bounds(nStartPos, nEndPos);
1077 if (nStartPos > nEndPos)
1078 std::swap(nStartPos, nEndPos);
1080 nEndPos = nEndPos - 1;
1081 m_xMEdit->select_region(nStartPos, nEndPos);
1083 FormulaHdl(*m_xMEdit);
1085 nStartPos = nEndPos;
1086 m_xMEdit->select_region(nStartPos, nEndPos);
1088 if (m_nArgs == 0)
1090 BtnHdl(*m_xBtnBackward);
1093 m_xParaWin->SetEdFocus();
1094 m_xBtnForward->set_sensitive(false); //@New
1097 // Functions for right Page
1099 void FormulaDlg_Impl::SetData( sal_Int32 nFStart, sal_Int32 nNextFStart, sal_Int32 nNextFEnd, sal_Int32& PrivStart, sal_Int32& PrivEnd)
1101 sal_Int32 nFEnd;
1103 // Notice and set new selection
1104 m_pHelper->getSelection( nFStart, nFEnd );
1105 m_pHelper->setSelection( nNextFStart, nNextFEnd );
1106 if (!m_bEditFlag)
1107 m_xMEdit->set_text(m_pHelper->getCurrentFormula());
1110 m_pHelper->getSelection( PrivStart, PrivEnd);
1111 if (!m_bEditFlag)
1113 m_xMEdit->select_region(PrivStart, PrivEnd);
1114 UpdateOldSel();
1117 FormEditData* pData = m_pHelper->getFormEditData();
1118 pData->SetFStart( nNextFStart );
1119 pData->SetOffset( 0 );
1121 FillDialog();
1124 void FormulaDlg_Impl::EditThisFunc(sal_Int32 nFStart)
1126 FormEditData* pData = m_pHelper->getFormEditData();
1127 if (!pData)
1128 return;
1130 OUString aFormula = m_pHelper->getCurrentFormula();
1132 if (nFStart == NOT_FOUND)
1134 nFStart = pData->GetFStart();
1136 else
1138 pData->SetFStart(nFStart);
1141 sal_Int32 nNextFStart = nFStart;
1142 sal_Int32 nNextFEnd = 0;
1144 bool bFound;
1146 bFound = m_aFormulaHelper.GetNextFunc( aFormula, false, nNextFStart, &nNextFEnd);
1147 if ( bFound )
1149 sal_Int32 PrivStart, PrivEnd;
1150 SetData( nFStart, nNextFStart, nNextFEnd, PrivStart, PrivEnd);
1151 m_pHelper->showReference( aFormula.copy( PrivStart, PrivEnd-PrivStart));
1153 else
1155 ClearAllParas();
1159 bool FormulaDlg_Impl::EditNextFunc( bool bForward, sal_Int32 nFStart )
1161 FormEditData* pData = m_pHelper->getFormEditData();
1162 if (!pData)
1163 return false;
1165 OUString aFormula = m_pHelper->getCurrentFormula();
1167 if (nFStart == NOT_FOUND)
1169 nFStart = pData->GetFStart();
1171 else
1173 pData->SetFStart(nFStart);
1176 sal_Int32 nNextFStart = 0;
1177 sal_Int32 nNextFEnd = 0;
1179 bool bFound;
1180 if ( bForward )
1182 nNextFStart = m_aFormulaHelper.GetArgStart( aFormula, nFStart, 0 );
1183 bFound = m_aFormulaHelper.GetNextFunc( aFormula, false, nNextFStart, &nNextFEnd);
1185 else
1187 nNextFStart = nFStart;
1188 bFound = m_aFormulaHelper.GetNextFunc( aFormula, true, nNextFStart, &nNextFEnd);
1191 if ( bFound )
1193 sal_Int32 PrivStart, PrivEnd;
1194 SetData( nFStart, nNextFStart, nNextFEnd, PrivStart, PrivEnd);
1197 return bFound;
1200 OUString FormulaDlg_Impl::GetPrevFuncExpression( bool bStartFromEnd )
1202 OUString aExpression;
1204 OUString aFormula( m_pHelper->getCurrentFormula());
1205 if (aFormula.isEmpty())
1206 return aExpression;
1208 if (bStartFromEnd || m_nFuncExpStart >= aFormula.getLength())
1209 m_nFuncExpStart = aFormula.getLength() - 1;
1211 sal_Int32 nFStart = m_nFuncExpStart;
1212 sal_Int32 nFEnd = 0;
1213 if (m_aFormulaHelper.GetNextFunc( aFormula, true, nFStart, &nFEnd))
1215 aExpression = aFormula.copy( nFStart, nFEnd - nFStart); // nFEnd is exclusive
1216 m_nFuncExpStart = nFStart;
1219 return aExpression;
1222 void FormulaDlg_Impl::SaveArg( sal_uInt16 nEd )
1224 if (nEd >= m_nArgs)
1225 return;
1227 for (sal_uInt16 i = 0; i <= nEd; i++)
1229 if ( m_aArguments[i].isEmpty() )
1230 m_aArguments[i] = " ";
1232 if (!m_xParaWin->GetArgument(nEd).isEmpty())
1233 m_aArguments[nEd] = m_xParaWin->GetArgument(nEd);
1235 sal_uInt16 nClearPos = nEd+1;
1236 for (sal_Int32 i = nEd+1; i < m_nArgs; i++)
1238 if ( !m_xParaWin->GetArgument(i).isEmpty() )
1240 nClearPos = i+1;
1244 for (sal_Int32 i = nClearPos; i < m_nArgs; i++)
1246 m_aArguments[i].clear();
1250 IMPL_LINK( FormulaDlg_Impl, FxHdl, ParaWin&, rPtr, void )
1252 if (&rPtr != m_xParaWin.get())
1253 return;
1255 m_xBtnForward->set_sensitive(true); //@ In order to be able to input another function.
1256 m_xTabCtrl->set_current_page(u"functiontab"_ustr);
1258 OUString aUndoStr = m_pHelper->getCurrentFormula(); // it will be added before a ";"
1259 FormEditData* pData = m_pHelper->getFormEditData();
1260 if (!pData)
1261 return;
1263 sal_uInt16 nArgNo = m_xParaWin->GetActiveLine();
1264 sal_uInt16 nEdFocus = nArgNo;
1266 SaveArg(nArgNo);
1267 UpdateSelection();
1269 sal_Int32 nFormulaStrPos = pData->GetFStart();
1271 OUString aFormula = m_pHelper->getCurrentFormula();
1272 sal_Int32 n1 = m_aFormulaHelper.GetArgStart( aFormula, nFormulaStrPos, nEdFocus + pData->GetOffset() );
1274 pData->SaveValues();
1275 pData->SetMode( FormulaDlgMode::Formula );
1276 pData->SetFStart( n1 );
1277 pData->SetUndoStr( aUndoStr );
1278 ClearAllParas();
1280 FillDialog(false);
1281 m_xFuncPage->SetFocus(); //There Parawin is not visible anymore
1284 IMPL_LINK( FormulaDlg_Impl, ModifyHdl, ParaWin&, rPtr, void )
1286 if (&rPtr == m_xParaWin.get())
1288 SaveArg(m_xParaWin->GetActiveLine());
1289 UpdateValues();
1291 UpdateSelection();
1292 CalcStruct(m_xMEdit->get_text());
1296 IMPL_LINK_NOARG( FormulaDlg_Impl, FormulaHdl, weld::TextView&, void)
1299 FormEditData* pData = m_pHelper->getFormEditData();
1300 if (!pData)
1301 return;
1303 m_bEditFlag = true;
1304 OUString aInputFormula = m_pHelper->getCurrentFormula();
1305 OUString aString = m_xMEdit->get_text();
1307 int nStartPos, nEndPos;
1308 m_xMEdit->get_selection_bounds(nStartPos, nEndPos);
1309 if (nStartPos > nEndPos)
1310 std::swap(nStartPos, nEndPos);
1312 if (aString.isEmpty()) // in case everything was cleared
1314 aString += "=";
1315 m_xMEdit->set_text(aString);
1316 nStartPos = 1;
1317 nEndPos = 1;
1318 m_xMEdit->select_region(nStartPos, nEndPos);
1320 else if (aString[0]!='=') // in case it's replaced
1322 aString = "=" + aString;
1323 m_xMEdit->set_text(aString);
1324 nStartPos += 1;
1325 nEndPos += 1;
1326 m_xMEdit->select_region(nStartPos, nEndPos);
1329 m_pHelper->setSelection( 0, aInputFormula.getLength());
1330 m_pHelper->setCurrentFormula(aString);
1331 m_pHelper->setSelection(nStartPos, nEndPos);
1333 sal_Int32 nPos = nStartPos - 1;
1335 OUString aStrResult;
1337 if ( CalcValue( m_pHelper->getCurrentFormula(), aStrResult ) )
1338 m_xWndFormResult->set_text( aStrResult );
1339 else
1341 aStrResult.clear();
1342 m_xWndFormResult->set_text( aStrResult );
1344 CalcStruct(aString);
1346 nPos = GetFunctionPos(nPos);
1348 if (nPos < nStartPos - 1)
1350 sal_Int32 nPos1 = aString.indexOf( '(', nPos);
1351 EditNextFunc( false, nPos1);
1353 else
1355 ClearAllParas();
1358 m_pHelper->setSelection(nStartPos, nEndPos);
1359 m_bEditFlag = false;
1362 void FormulaDlg_Impl::FormulaCursor()
1364 FormEditData* pData = m_pHelper->getFormEditData();
1365 if (!pData)
1366 return;
1368 m_bEditFlag = true;
1370 OUString aString = m_xMEdit->get_text();
1372 int nStartPos, nEndPos;
1373 m_xMEdit->get_selection_bounds(nStartPos, nEndPos);
1374 if (nStartPos > nEndPos)
1375 std::swap(nStartPos, nEndPos);
1377 m_pHelper->setSelection(nStartPos, nEndPos);
1379 if (nStartPos == 0)
1381 nStartPos = 1;
1382 m_xMEdit->select_region(nStartPos, nEndPos);
1384 if (nStartPos != aString.getLength())
1386 sal_Int32 nPos = nStartPos;
1388 sal_Int32 nFStart = GetFunctionPos(nPos - 1);
1390 if (nFStart < nPos)
1392 sal_Int32 nPos1 = m_aFormulaHelper.GetFunctionEnd( aString, nFStart);
1394 if (nPos1 > nPos)
1396 EditThisFunc(nFStart);
1398 else
1400 sal_Int32 n = nPos;
1401 short nCount = 1;
1402 while(n > 0)
1404 if (aString[n]==')')
1405 nCount++;
1406 else if (aString[n]=='(')
1407 nCount--;
1408 if (nCount == 0)
1409 break;
1410 n--;
1412 if (nCount == 0)
1414 nFStart = m_aFormulaHelper.GetFunctionStart( aString, n, true);
1415 EditThisFunc(nFStart);
1417 else
1419 ClearAllParas();
1423 else
1425 ClearAllParas();
1428 m_pHelper->setSelection(nStartPos, nEndPos);
1430 m_bEditFlag = false;
1433 void FormulaDlg_Impl::UpdateOldSel()
1435 m_xMEdit->get_selection_bounds(m_nSelectionStart, m_nSelectionEnd);
1436 if (m_nSelectionStart > m_nSelectionEnd)
1437 std::swap(m_nSelectionStart, m_nSelectionEnd);
1440 IMPL_LINK_NOARG( FormulaDlg_Impl, FormulaCursorHdl, weld::TextView&, void)
1442 int nStartPos, nEndPos;
1443 m_xMEdit->get_selection_bounds(nStartPos, nEndPos);
1444 if (nStartPos > nEndPos)
1445 std::swap(nStartPos, nEndPos);
1447 if (nStartPos != m_nSelectionStart || nEndPos != m_nSelectionEnd)
1449 m_nSelectionStart = nStartPos;
1450 m_nSelectionEnd = nEndPos;
1451 FormulaCursor();
1455 void FormulaDlg_Impl::UpdateSelection()
1457 m_pHelper->setSelection( m_aFuncSel.Min(), m_aFuncSel.Max());
1458 if (m_pFuncDesc)
1460 m_pHelper->setCurrentFormula( m_pFuncDesc->getFormula( m_aArguments ) );
1461 m_nArgs = m_pFuncDesc->getSuppressedArgumentCount();
1463 else
1465 m_pHelper->setCurrentFormula(u""_ustr);
1466 m_nArgs = 0;
1469 m_xMEdit->set_text(m_pHelper->getCurrentFormula());
1470 sal_Int32 PrivStart, PrivEnd;
1471 m_pHelper->getSelection( PrivStart, PrivEnd);
1472 m_aFuncSel.Min() = PrivStart;
1473 m_aFuncSel.Max() = PrivEnd;
1475 OUString aFormula = m_xMEdit->get_text();
1476 sal_Int32 nArgPos = m_aFormulaHelper.GetArgStart( aFormula, PrivStart, 0);
1478 sal_uInt16 nPos = m_xParaWin->GetActiveLine();
1479 if (nPos >= m_aArguments.size())
1481 SAL_WARN("formula.ui","FormulaDlg_Impl::UpdateSelection - shot in foot: nPos " <<
1482 nPos << " >= m_aArguments.size() " << m_aArguments.size() <<
1483 " for aFormula '" << aFormula << "'");
1484 nPos = m_aArguments.size();
1485 if (nPos)
1486 --nPos;
1489 for (sal_uInt16 i = 0; i < nPos; i++)
1491 nArgPos += (m_aArguments[i].getLength() + 1);
1493 sal_Int32 nLength = (nPos < m_aArguments.size()) ? m_aArguments[nPos].getLength() : 0;
1495 m_pHelper->setSelection(nArgPos, nArgPos + nLength);
1496 m_xMEdit->select_region(nArgPos, nArgPos + nLength);
1497 UpdateOldSel();
1500 ::std::pair<RefButton*, RefEdit*> FormulaDlg_Impl::RefInputStartBefore(RefEdit* pEdit, RefButton* pButton)
1502 m_pTheRefEdit = pEdit;
1503 m_pTheRefButton = pButton;
1505 Selection aOrigSelection;
1506 if (m_pTheRefEdit)
1508 // grab selection before showing next widget in case the selection is blown away
1509 // by it appearing
1510 aOrigSelection = m_pTheRefEdit->GetSelection();
1513 // because its initially hidden, give it its optimal size so clicking the
1514 // refbutton has an initial size to work when retro-fitting this to .ui
1515 m_xEdRef->GetWidget()->set_size_request(m_xEdRef->GetWidget()->get_preferred_size().Width(), -1);
1516 m_xEdRef->GetWidget()->show();
1518 if ( m_pTheRefEdit )
1520 m_xEdRef->SetRefString(m_pTheRefEdit->GetText());
1521 m_xEdRef->SetSelection(aOrigSelection);
1522 m_xEdRef->GetWidget()->set_help_id(m_pTheRefEdit->GetWidget()->get_help_id());
1525 m_xRefBtn->GetWidget()->set_visible(pButton != nullptr);
1527 ::std::pair<RefButton*, RefEdit*> aPair;
1528 aPair.first = pButton ? m_xRefBtn.get() : nullptr;
1529 aPair.second = m_xEdRef.get();
1530 return aPair;
1533 void FormulaDlg_Impl::RefInputStartAfter()
1535 m_xRefBtn->SetEndImage();
1537 if (!m_pTheRefEdit)
1538 return;
1540 OUString aStr = m_aTitle2 + " " + m_xFtEditName->get_label() + "( ";
1542 if ( m_xParaWin->GetActiveLine() > 0 )
1543 aStr += "...; ";
1544 aStr += m_xParaWin->GetActiveArgName();
1545 if ( m_xParaWin->GetActiveLine() + 1 < m_nArgs )
1546 aStr += "; ...";
1547 aStr += " )";
1549 m_rDialog.set_title(m_rDialog.strip_mnemonic(aStr));
1552 void FormulaDlg_Impl::RefInputDoneAfter( bool bForced )
1554 m_xRefBtn->SetStartImage();
1555 if (!bForced && m_xRefBtn->GetWidget()->get_visible())
1556 return;
1558 m_xEdRef->GetWidget()->hide();
1559 m_xRefBtn->GetWidget()->hide();
1560 if ( m_pTheRefEdit )
1562 m_pTheRefEdit->SetRefString( m_xEdRef->GetText() );
1563 m_pTheRefEdit->GrabFocus();
1565 if ( m_pTheRefButton )
1566 m_pTheRefButton->SetStartImage();
1568 sal_uInt16 nPrivActiv = m_xParaWin->GetActiveLine();
1569 m_xParaWin->SetArgument( nPrivActiv, m_xEdRef->GetText() );
1570 ModifyHdl( *m_xParaWin );
1571 m_pTheRefEdit = nullptr;
1573 m_rDialog.set_title(m_aTitle1);
1576 RefEdit* FormulaDlg_Impl::GetCurrRefEdit()
1578 return m_xEdRef->GetWidget()->get_visible() ? m_xEdRef.get() : m_xParaWin->GetActiveEdit();
1581 void FormulaDlg_Impl::Update()
1583 FormEditData* pData = m_pHelper->getFormEditData();
1584 const OUString sExpression = m_xMEdit->get_text();
1585 m_aOldFormula.clear();
1586 UpdateTokenArray(sExpression);
1587 FormulaCursor();
1588 CalcStruct(sExpression);
1589 if (pData->GetMode() == FormulaDlgMode::Formula)
1590 m_xTabCtrl->set_current_page(u"functiontab"_ustr);
1591 else
1592 m_xTabCtrl->set_current_page(u"structtab"_ustr);
1593 m_xBtnMatrix->set_active(pData->GetMatrixFlag());
1596 void FormulaDlg_Impl::Update(const OUString& _sExp)
1598 CalcStruct(_sExp);
1599 FillDialog();
1600 FuncSelHdl(*m_xFuncPage);
1603 void FormulaDlg_Impl::SetMeText(const OUString& _sText)
1605 FormEditData* pData = m_pHelper->getFormEditData();
1606 m_xMEdit->set_text(_sText);
1607 auto aSelection = pData->GetSelection();
1608 m_xMEdit->select_region(aSelection.Min(), aSelection.Max());
1609 UpdateOldSel();
1612 FormulaDlgMode FormulaDlg_Impl::SetMeText( const OUString& _sText, sal_Int32 PrivStart, sal_Int32 PrivEnd, bool bMatrix, bool _bSelect, bool _bUpdate)
1614 FormulaDlgMode eMode = FormulaDlgMode::Formula;
1615 if (!m_bEditFlag)
1616 m_xMEdit->set_text(_sText);
1618 if ( _bSelect || !m_bEditFlag )
1619 m_xMEdit->select_region(PrivStart, PrivEnd);
1620 if ( _bUpdate )
1622 UpdateOldSel();
1623 int nStartPos, nEndPos;
1624 m_xMEdit->get_selection_bounds(nStartPos, nEndPos);
1625 if (nStartPos > nEndPos)
1626 std::swap(nStartPos, nEndPos);
1627 m_pHelper->showReference(m_xMEdit->get_text().copy(nStartPos, nEndPos - nStartPos));
1628 eMode = FormulaDlgMode::Edit;
1630 m_xBtnMatrix->set_active( bMatrix );
1631 } // if ( _bUpdate )
1632 return eMode;
1635 bool FormulaDlg_Impl::CheckMatrix(OUString& aFormula)
1637 m_xMEdit->grab_focus();
1638 sal_Int32 nLen = aFormula.getLength();
1639 bool bMatrix = nLen > 3 // Matrix-Formula
1640 && aFormula[0] == '{'
1641 && aFormula[1] == '='
1642 && aFormula[nLen-1] == '}';
1643 if ( bMatrix )
1645 aFormula = aFormula.copy( 1, aFormula.getLength()-2 );
1646 m_xBtnMatrix->set_active( bMatrix );
1647 m_xBtnMatrix->set_sensitive(false);
1648 } // if ( bMatrix )
1650 m_xTabCtrl->set_current_page(u"structtab"_ustr);
1651 return bMatrix;
1654 IMPL_LINK_NOARG( FormulaDlg_Impl, StructSelHdl, StructPage&, void)
1656 m_bStructUpdate = false;
1657 if (m_xStructPage->IsVisible())
1658 m_xBtnForward->set_sensitive(false); //@New
1659 m_bStructUpdate = true;
1662 IMPL_LINK_NOARG( FormulaDlg_Impl, MatrixHdl, weld::Toggleable&, void)
1664 m_bUserMatrixFlag = true;
1665 UpdateValues(true);
1668 IMPL_LINK_NOARG( FormulaDlg_Impl, FuncSelHdl, FuncPage&, void)
1670 if ( (m_xFuncPage->GetFunctionEntryCount() > 0)
1671 && (m_xFuncPage->GetFunction() != -1) )
1673 const IFunctionDescription* pDesc = m_xFuncPage->GetFuncDesc();
1675 if (pDesc != m_pFuncDesc)
1676 m_xBtnForward->set_sensitive(true); //new
1678 if (pDesc)
1680 pDesc->initArgumentInfo(); // full argument info is needed
1682 OUString aSig = pDesc->getSignature();
1683 m_xFtHeadLine->set_label( pDesc->getFunctionName() );
1684 m_xFtFuncName->set_label( aSig );
1685 m_xFtFuncDesc->set_label( pDesc->getDescription() );
1688 else
1690 m_xFtHeadLine->set_label( OUString() );
1691 m_xFtFuncName->set_label( OUString() );
1692 m_xFtFuncDesc->set_label( OUString() );
1696 void FormulaDlg_Impl::UpdateParaWin( const Selection& _rSelection, const OUString& _sRefStr)
1698 Selection theSel = _rSelection;
1699 m_xEdRef->GetWidget()->replace_selection(_sRefStr);
1700 theSel.Max() = theSel.Min() + _sRefStr.getLength();
1701 m_xEdRef->SetSelection( theSel );
1704 // Manual Update of the results' fields:
1706 sal_uInt16 nPrivActiv = m_xParaWin->GetActiveLine();
1707 m_xParaWin->SetArgument( nPrivActiv, m_xEdRef->GetText());
1708 m_xParaWin->UpdateParas();
1710 RefEdit* pEd = GetCurrRefEdit();
1711 if (pEd)
1712 pEd->SetSelection( theSel );
1715 bool FormulaDlg_Impl::UpdateParaWin(Selection& _rSelection)
1717 OUString aStrEd;
1718 RefEdit* pEd = GetCurrRefEdit();
1719 if (pEd && !m_pTheRefEdit)
1721 _rSelection = pEd->GetSelection();
1722 _rSelection.Normalize();
1723 aStrEd = pEd->GetText();
1724 m_xEdRef->SetRefString(aStrEd);
1725 m_xEdRef->SetSelection( _rSelection );
1727 else
1729 _rSelection = m_xEdRef->GetSelection();
1730 _rSelection.Normalize();
1731 aStrEd = m_xEdRef->GetText();
1733 return m_pTheRefEdit == nullptr;
1736 void FormulaDlg_Impl::SetEdSelection()
1738 RefEdit* pEd = GetCurrRefEdit()/*aScParaWin.GetActiveEdit()*/;
1739 if (pEd)
1741 Selection theSel = m_xEdRef->GetSelection();
1742 // Edit may have the focus -> call ModifyHdl in addition
1743 // to what's happening in GetFocus
1744 pEd->GetModifyHdl().Call(*pEd);
1745 pEd->GrabFocus();
1746 pEd->SetSelection(theSel);
1747 } // if ( pEd )
1750 FormulaModalDialog::FormulaModalDialog(weld::Window* pParent,
1751 IFunctionManager const * _pFunctionMgr,
1752 IControlReferenceHandler* _pDlg)
1753 : GenericDialogController(pParent, u"formula/ui/formuladialog.ui"_ustr, u"FormulaDialog"_ustr)
1754 , m_pImpl(new FormulaDlg_Impl(*m_xDialog, *m_xBuilder, false/*_bSupportFunctionResult*/,
1755 false/*_bSupportResult*/, false/*_bSupportMatrix*/,
1756 this, _pFunctionMgr, _pDlg))
1758 m_xDialog->set_title(m_pImpl->m_aTitle1);
1761 FormulaModalDialog::~FormulaModalDialog() { }
1763 void FormulaModalDialog::Update(const OUString& _sExp)
1765 m_pImpl->Update(_sExp);
1768 void FormulaModalDialog::SetMeText(const OUString& _sText)
1770 m_pImpl->SetMeText(_sText);
1773 void FormulaModalDialog::CheckMatrix(OUString& aFormula)
1775 m_pImpl->CheckMatrix(aFormula);
1778 void FormulaModalDialog::Update()
1780 m_pImpl->Update();
1783 ::std::pair<RefButton*, RefEdit*> FormulaModalDialog::RefInputStartBefore( RefEdit* pEdit, RefButton* pButton )
1785 return m_pImpl->RefInputStartBefore( pEdit, pButton );
1788 void FormulaModalDialog::RefInputStartAfter()
1790 m_pImpl->RefInputStartAfter();
1793 void FormulaModalDialog::RefInputDoneAfter()
1795 m_pImpl->RefInputDoneAfter( true/*bForced*/ );
1798 void FormulaModalDialog::StoreFormEditData(FormEditData* pData)
1800 m_pImpl->StoreFormEditData(pData);
1803 // Initialisation / General functions for Dialog
1804 FormulaDlg::FormulaDlg(SfxBindings* pB, SfxChildWindow* pCW,
1805 weld::Window* pParent,
1806 IFunctionManager const * _pFunctionMgr, IControlReferenceHandler* _pDlg)
1807 : SfxModelessDialogController( pB, pCW, pParent, u"formula/ui/formuladialog.ui"_ustr, u"FormulaDialog"_ustr)
1808 , m_pImpl(new FormulaDlg_Impl(*m_xDialog, *m_xBuilder, true/*_bSupportFunctionResult*/
1809 , true/*_bSupportResult*/
1810 , true/*_bSupportMatrix*/
1811 , this, _pFunctionMgr, _pDlg))
1813 m_xDialog->set_title(m_pImpl->m_aTitle1);
1816 FormulaDlg::~FormulaDlg()
1820 void FormulaDlg::Update(const OUString& _sExp)
1822 m_pImpl->Update(_sExp);
1825 void FormulaDlg::SetMeText(const OUString& _sText)
1827 m_pImpl->SetMeText(_sText);
1830 FormulaDlgMode FormulaDlg::SetMeText( const OUString& _sText, sal_Int32 PrivStart, sal_Int32 PrivEnd, bool bMatrix, bool _bSelect, bool _bUpdate)
1832 return m_pImpl->SetMeText( _sText, PrivStart, PrivEnd, bMatrix, _bSelect, _bUpdate);
1835 bool FormulaDlg::CheckMatrix(OUString& aFormula)
1837 return m_pImpl->CheckMatrix(aFormula);
1840 OUString FormulaDlg::GetMeText() const
1842 return m_pImpl->m_xMEdit->get_text();
1845 void FormulaDlg::Update()
1847 m_pImpl->Update();
1850 void FormulaDlg::DoEnter()
1852 m_pImpl->DoEnter(false);
1855 ::std::pair<RefButton*, RefEdit*> FormulaDlg::RefInputStartBefore( RefEdit* pEdit, RefButton* pButton )
1857 return m_pImpl->RefInputStartBefore( pEdit, pButton );
1860 void FormulaDlg::RefInputStartAfter()
1862 m_pImpl->RefInputStartAfter();
1865 void FormulaDlg::RefInputDoneAfter( bool bForced )
1867 m_pImpl->RefInputDoneAfter( bForced );
1870 void FormulaDlg::disableOk()
1872 m_pImpl->m_xBtnEnd->set_sensitive(false);
1875 void FormulaDlg::StoreFormEditData(FormEditData* pData)
1877 m_pImpl->StoreFormEditData(pData);
1880 const IFunctionDescription* FormulaDlg::getCurrentFunctionDescription() const
1882 SAL_WARN_IF( (m_pImpl->m_pFuncDesc && m_pImpl->m_pFuncDesc->getSuppressedArgumentCount() != m_pImpl->m_nArgs),
1883 "formula.ui", "FormulaDlg::getCurrentFunctionDescription: getSuppressedArgumentCount " <<
1884 m_pImpl->m_pFuncDesc->getSuppressedArgumentCount() << " != m_nArgs " << m_pImpl->m_nArgs << " for " <<
1885 m_pImpl->m_pFuncDesc->getFunctionName());
1886 return m_pImpl->m_pFuncDesc;
1889 void FormulaDlg::UpdateParaWin( const Selection& _rSelection, const OUString& _sRefStr)
1891 m_pImpl->UpdateParaWin( _rSelection, _sRefStr);
1894 bool FormulaDlg::UpdateParaWin(Selection& _rSelection)
1896 return m_pImpl->UpdateParaWin(_rSelection);
1899 RefEdit* FormulaDlg::GetActiveEdit()
1901 return m_pImpl->m_xParaWin->GetActiveEdit();
1904 const FormulaHelper& FormulaDlg::GetFormulaHelper() const
1906 return m_pImpl->GetFormulaHelper();
1909 void FormulaDlg::SetEdSelection()
1911 m_pImpl->SetEdSelection();
1914 void FormEditData::SaveValues()
1916 Reset();
1919 void FormEditData::Reset()
1921 nMode = FormulaDlgMode::Formula;
1922 nFStart = 0;
1923 nOffset = 0;
1924 bMatrix = false;
1925 aSelection.Min() = 0;
1926 aSelection.Max() = 0;
1927 aUndoStr.clear();
1930 FormEditData& FormEditData::operator=( const FormEditData& r )
1932 nMode = r.nMode;
1933 nFStart = r.nFStart;
1934 nOffset = r.nOffset;
1935 aUndoStr = r.aUndoStr;
1936 bMatrix = r.bMatrix ;
1937 aSelection = r.aSelection;
1938 return *this;
1941 FormEditData::FormEditData()
1943 Reset();
1946 FormEditData::~FormEditData()
1950 FormEditData::FormEditData( const FormEditData& r )
1952 *this = r;
1956 } // formula
1959 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */