1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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 "scitems.hxx"
21 #include <sfx2/dispatch.hxx>
22 #include <sfx2/docfile.hxx>
23 #include <sfx2/objsh.hxx>
24 #include <svl/zforlist.hxx>
25 #include <svl/stritem.hxx>
26 #include <svtools/treelistbox.hxx>
27 #include <sfx2/viewfrm.hxx>
28 #include <vcl/svapp.hxx>
29 #include <vcl/mnemonic.hxx>
30 #include <unotools/charclass.hxx>
31 #include <tools/urlobj.hxx>
32 #include <formula/formulahelper.hxx>
33 #include <formula/IFunctionDescription.hxx>
35 #include "tokenuno.hxx"
36 #include "formula.hxx"
37 #include "formdata.hxx"
38 #include "globstr.hrc"
39 #include "scresid.hxx"
40 #include "reffact.hxx"
41 #include "document.hxx"
42 #include "simpleformulacalc.hxx"
44 #include "inputhdl.hxx"
45 #include "tabvwsh.hxx"
46 #include "appoptio.hxx"
48 #include "funcdesc.hxx"
49 #include <formula/token.hxx>
50 #include "tokenarray.hxx"
52 #include "servuno.hxx"
53 #include "unonames.hxx"
54 #include "externalrefmgr.hxx"
56 #include <com/sun/star/table/CellAddress.hpp>
58 using namespace formula
;
59 using namespace com::sun::star
;
61 ScDocument
* ScFormulaDlg::pDoc
= NULL
;
62 ScAddress
ScFormulaDlg::aCursorPos
;
64 // init/ shared functions for dialog
66 ScFormulaDlg::ScFormulaDlg( SfxBindings
* pB
, SfxChildWindow
* pCW
,
67 vcl::Window
* pParent
, ScViewData
* pViewData
,formula::IFunctionManager
* _pFunctionMgr
)
68 : formula::FormulaDlg( pB
, pCW
, pParent
, true,true,true, _pFunctionMgr
,this)
71 m_aHelper
.SetWindow(this);
72 ScModule
* pScMod
= SC_MOD();
73 pScMod
->InputEnterHandler();
74 ScTabViewShell
* pScViewShell
= NULL
;
76 // title has to be from the view that opened the dialog,
77 // even if it's not the current view
81 SfxDispatcher
* pMyDisp
= pB
->GetDispatcher();
84 SfxViewFrame
* pMyViewFrm
= pMyDisp
->GetFrame();
87 pScViewShell
= PTR_CAST( ScTabViewShell
, pMyViewFrm
->GetViewShell() );
89 pScViewShell
->UpdateInputHandler(true);
95 pDoc
= pViewData
->GetDocument();
96 m_xParser
.set(ScServiceProvider::MakeInstance(SC_SERVICE_FORMULAPARS
, static_cast<ScDocShell
*>(pDoc
->GetDocumentShell())),uno::UNO_QUERY
);
97 uno::Reference
< beans::XPropertySet
> xSet(m_xParser
,uno::UNO_QUERY
);
98 xSet
->setPropertyValue(OUString(SC_UNO_COMPILEFAP
),uno::makeAny(sal_True
));
100 m_xOpCodeMapper
.set(ScServiceProvider::MakeInstance(SC_SERVICE_OPCODEMAPPER
, static_cast<ScDocShell
*>(pDoc
->GetDocumentShell())),uno::UNO_QUERY
);
102 ScInputHandler
* pInputHdl
= SC_MOD()->GetInputHdl(pScViewShell
);
104 OSL_ENSURE( pInputHdl
, "Missing input handler :-/" );
107 pInputHdl
->NotifyChange( NULL
);
109 ScFormulaReferenceHelper::enableInput( false );
110 ScFormulaReferenceHelper::EnableSpreadsheets();
112 ScFormulaReferenceHelper::SetDispatcherLock( true );
117 ScFormEditData
* pData
= pScMod
->GetFormEditData();
120 pScMod
->SetRefInputHdl(pScMod
->GetInputHdl());
122 pDoc
= pViewData
->GetDocument();
123 SCCOL nCol
= pViewData
->GetCurX();
124 SCROW nRow
= pViewData
->GetCurY();
125 SCTAB nTab
= pViewData
->GetTabNo();
126 aCursorPos
= ScAddress( nCol
, nRow
, nTab
);
128 pScMod
->InitFormEditData(); // create new
129 pData
= pScMod
->GetFormEditData();
130 pData
->SetInputHandler(pScMod
->GetInputHdl());
131 pData
->SetDocShell(pViewData
->GetDocShell());
133 OSL_ENSURE(pData
,"FormEditData ist nicht da");
135 formula::FormulaDlgMode eMode
= FORMULA_FORMDLG_FORMULA
; // default...
137 // edit if formula exists
140 pDoc
->GetFormula( nCol
, nRow
, nTab
, aFormula
);
141 bool bEdit
= ( aFormula
.getLength() > 1 );
142 bool bMatrix
= false;
145 bMatrix
= CheckMatrix(aFormula
);
147 sal_Int32 nFStart
= 0;
149 if ( GetFormulaHelper().GetNextFunc( aFormula
, false, nFStart
, &nFEnd
) )
151 pScMod
->InputReplaceSelection( aFormula
);
152 pScMod
->InputSetSelection( nFStart
, nFEnd
);
153 sal_Int32 PrivStart
, PrivEnd
;
154 pScMod
->InputGetSelection( PrivStart
, PrivEnd
);
156 eMode
= SetMeText(pScMod
->InputGetFormulaStr(),PrivStart
, PrivEnd
, bMatrix
, true, true);
157 pData
->SetFStart( nFStart
);
165 OUString
aNewFormula('=');
166 if ( aFormula
.startsWith("=") )
167 aNewFormula
= aFormula
;
169 pScMod
->InputReplaceSelection( aNewFormula
);
170 pScMod
->InputSetSelection( 1, aNewFormula
.getLength()+1 );
171 sal_Int32 PrivStart
, PrivEnd
;
172 pScMod
->InputGetSelection( PrivStart
, PrivEnd
);
173 SetMeText(pScMod
->InputGetFormulaStr(),PrivStart
, PrivEnd
,bMatrix
,false,false);
175 pData
->SetFStart( 1 ); // after "="
178 pData
->SetMode( (sal_uInt16
) eMode
);
179 OUString rStrExp
= GetMeText();
186 void ScFormulaDlg::notifyChange()
188 ScModule
* pScMod
= SC_MOD();
190 ScInputHandler
* pInputHdl
= pScMod
->GetInputHdl();
192 pInputHdl
->NotifyChange( NULL
);
195 void ScFormulaDlg::fill()
197 ScModule
* pScMod
= SC_MOD();
198 ScFormEditData
* pData
= pScMod
->GetFormEditData();
203 // data exists -> restore state (after switch)
204 // don't reinitialise pDoc and aCursorPos
205 //pDoc = pViewData->GetDocument();
206 if(IsInputHdl(pData
->GetInputHandler()))
208 pScMod
->SetRefInputHdl(pData
->GetInputHandler());
212 PtrTabViewShell pTabViewShell
;
213 ScInputHandler
* pInputHdl
= GetNextInputHandler(pData
->GetDocShell(),&pTabViewShell
);
215 if ( pInputHdl
== NULL
) //no more InputHandler for DocShell
218 pInputHdl
= pScMod
->GetInputHdl();
222 pInputHdl
->SetRefViewShell(pTabViewShell
);
224 pScMod
->SetRefInputHdl(pInputHdl
);
225 pData
->SetInputHandler(pInputHdl
);
228 OUString
aOldFormulaTmp(pScMod
->InputGetFormulaStr());
229 pScMod
->InputSetSelection( 0, aOldFormulaTmp
.getLength());
231 rStrExp
=pData
->GetUndoStr();
232 pScMod
->InputReplaceSelection(rStrExp
);
237 // switch back, maybe new Doc has been opened
238 pScMod
->SetRefInputHdl(NULL
);
242 ScFormulaDlg::~ScFormulaDlg()
247 void ScFormulaDlg::dispose()
249 ScModule
* pScMod
= SC_MOD();
250 ScFormEditData
* pData
= pScMod
->GetFormEditData();
253 if (pData
) // close dosen't destroy;
255 //set back reference input handler
256 pScMod
->SetRefInputHdl(NULL
);
257 StoreFormEditData(pData
);
259 formula::FormulaDlg::dispose();
262 bool ScFormulaDlg::IsInputHdl(ScInputHandler
* pHdl
)
266 // belongs InputHandler to a ViewShell?
268 TypeId aScType
= TYPE(ScTabViewShell
);
269 SfxViewShell
* pSh
= SfxViewShell::GetFirst( &aScType
);
270 while ( pSh
&& !bAlive
)
272 if (static_cast<ScTabViewShell
*>(pSh
)->GetInputHandler() == pHdl
)
274 pSh
= SfxViewShell::GetNext( *pSh
, &aScType
);
281 ScInputHandler
* ScFormulaDlg::GetNextInputHandler(ScDocShell
* pDocShell
,PtrTabViewShell
* ppViewSh
)
283 ScInputHandler
* pHdl
=NULL
;
285 SfxViewFrame
* pFrame
= SfxViewFrame::GetFirst( pDocShell
);
286 while( pFrame
&& pHdl
==NULL
)
288 SfxViewShell
* p
= pFrame
->GetViewShell();
289 ScTabViewShell
* pViewSh
= PTR_CAST(ScTabViewShell
,p
);
292 pHdl
=pViewSh
->GetInputHandler();
293 if(ppViewSh
!=NULL
) *ppViewSh
=pViewSh
;
295 pFrame
= SfxViewFrame::GetNext( *pFrame
, pDocShell
);
301 bool ScFormulaDlg::Close()
307 // functions for right side
309 bool ScFormulaDlg::calculateValue( const OUString
& rStrExp
, OUString
& rStrResult
)
311 boost::scoped_ptr
<ScSimpleFormulaCalculator
> pFCell( new ScSimpleFormulaCalculator( pDoc
, aCursorPos
, rStrExp
) );
313 // HACK! to avoid neither #REF! from ColRowNames
314 // if a name is added as actually range in the overall formula,
315 // but is interpreted at the individual representation as single-cell reference
316 bool bColRowName
= pFCell
->HasColRowName();
319 // ColRowName from RPN-Code?
320 if ( pFCell
->GetCode()->GetCodeLen() <= 1 )
322 // ==0: would be an area if...
323 OUStringBuffer aBraced
;
325 aBraced
.append(rStrExp
);
327 pFCell
.reset( new ScSimpleFormulaCalculator( pDoc
, aCursorPos
, aBraced
.makeStringAndClear() ) );
333 sal_uInt16 nErrCode
= pFCell
->GetErrCode();
336 SvNumberFormatter
& aFormatter
= *(pDoc
->GetFormatTable());
338 if ( pFCell
->IsValue() )
340 double n
= pFCell
->GetValue();
341 sal_uLong nFormat
= aFormatter
.GetStandardFormat( n
, 0,
342 pFCell
->GetFormatType(), ScGlobal::eLnge
);
343 aFormatter
.GetOutputString( n
, nFormat
,
344 rStrResult
, &pColor
);
348 sal_uLong nFormat
= aFormatter
.GetStandardFormat(
349 pFCell
->GetFormatType(), ScGlobal::eLnge
);
350 aFormatter
.GetOutputString( pFCell
->GetString().getString(), nFormat
,
351 rStrResult
, &pColor
);
355 if ( bColRowName
|| (aTestRange
.Parse(rStrExp
) & SCA_VALID
) )
356 rStrResult
+= " ...";
360 rStrResult
+= ScGlobal::GetErrorString(nErrCode
);
365 // virtual methods of ScAnyRefDlg:
366 void ScFormulaDlg::RefInputStart( formula::RefEdit
* pEdit
, formula::RefButton
* pButton
)
368 pEdit
->SetSelection(Selection(0, SELECTION_MAX
));
369 ::std::pair
<formula::RefButton
*,formula::RefEdit
*> aPair
= RefInputStartBefore( pEdit
, pButton
);
370 m_aHelper
.RefInputStart( aPair
.second
, aPair
.first
);
371 RefInputStartAfter( aPair
.second
, aPair
.first
);
374 void ScFormulaDlg::RefInputDone( bool bForced
)
376 m_aHelper
.RefInputDone( bForced
);
377 RefInputDoneAfter( bForced
);
380 void ScFormulaDlg::SetReference( const ScRange
& rRef
, ScDocument
* pRefDoc
)
382 const IFunctionDescription
* pFunc
= getCurrentFunctionDescription();
383 if ( pFunc
&& pFunc
->getSuppressedArgumentCount() > 0 )
386 bool bRefNull
= UpdateParaWin(theSel
);
388 if ( rRef
.aStart
!= rRef
.aEnd
&& bRefNull
)
390 RefInputStart(GetActiveEdit());
394 bool bOtherDoc
= ( pRefDoc
!= pDoc
&& pRefDoc
->GetDocumentShell()->HasName() );
397 // reference to other document - wie inputhdl.cxx
399 OSL_ENSURE(rRef
.aStart
.Tab()==rRef
.aEnd
.Tab(), "nStartTab!=nEndTab");
401 OUString
aTmp(rRef
.Format(SCA_VALID
|SCA_TAB_3D
, pRefDoc
)); // immer 3d
403 SfxObjectShell
* pObjSh
= pRefDoc
->GetDocumentShell();
405 // #i75893# convert escaped URL of the document to something user friendly
406 // OUString aFileName = pObjSh->GetMedium()->GetName();
407 OUString aFileName
= pObjSh
->GetMedium()->GetURLObject().GetMainURL( INetURLObject::DECODE_UNAMBIGUOUS
);
410 aRefStr
+= aFileName
;
416 // We can't use ScRange::Format here because in R1C1 mode we need
417 // to display the reference position relative to the cursor
420 ScComplexRefData aRefData
;
421 aRefData
.InitRangeRel(rRef
, aCursorPos
);
422 bool bSingle
= aRefData
.Ref1
== aRefData
.Ref2
;
423 if (aCursorPos
.Tab() != rRef
.aStart
.Tab())
424 aRefData
.Ref1
.SetFlag3D(true);
426 aArray
.AddSingleReference(aRefData
.Ref1
);
428 aArray
.AddDoubleReference(aRefData
);
429 ScCompiler
aComp(pDoc
, aCursorPos
, aArray
);
430 aComp
.SetGrammar(pDoc
->GetGrammar());
432 aComp
.CreateStringFromTokenArray(aBuf
);
433 aRefStr
= aBuf
.makeStringAndClear();
436 UpdateParaWin(theSel
,aRefStr
);
440 bool ScFormulaDlg::IsRefInputMode() const
442 const IFunctionDescription
* pDesc
= getCurrentFunctionDescription();
443 bool bRef
= (pDesc
&& (pDesc
->getSuppressedArgumentCount() > 0)) && (pDoc
!=NULL
);
447 bool ScFormulaDlg::IsDocAllowed(SfxObjectShell
* pDocSh
) const
449 // not allowed: different from this doc, and no name
450 // pDocSh is always a ScDocShell
451 if ( pDocSh
&& &static_cast<ScDocShell
*>(pDocSh
)->GetDocument() != pDoc
&& !pDocSh
->HasName() )
454 return true; // everything else is allowed
457 void ScFormulaDlg::SetActive()
459 const IFunctionDescription
* pFunc
= getCurrentFunctionDescription();
460 if ( pFunc
&& pFunc
->getSuppressedArgumentCount() > 0 )
467 void ScFormulaDlg::SaveLRUEntry(const ScFuncDesc
* pFuncDescP
)
469 if (pFuncDescP
&& pFuncDescP
->nFIndex
!=0)
471 ScModule
* pScMod
= SC_MOD();
472 pScMod
->InsertEntryToLRUList(pFuncDescP
->nFIndex
);
476 void ScFormulaDlg::doClose(bool /*_bOk*/)
478 m_aHelper
.DoClose( ScFormulaDlgWrapper::GetChildWindowId() );
480 void ScFormulaDlg::insertEntryToLRUList(const formula::IFunctionDescription
* _pDesc
)
482 const ScFuncDesc
* pDesc
= dynamic_cast<const ScFuncDesc
*>(_pDesc
);
485 void ScFormulaDlg::showReference(const OUString
& _sFormula
)
487 ShowReference(_sFormula
);
489 void ScFormulaDlg::ShowReference(const OUString
& _sFormula
)
491 m_aHelper
.ShowReference(_sFormula
);
493 void ScFormulaDlg::HideReference( bool bDoneRefMode
)
495 m_aHelper
.HideReference(bDoneRefMode
);
497 void ScFormulaDlg::ViewShellChanged()
499 ScFormulaReferenceHelper::ViewShellChanged();
501 void ScFormulaDlg::AddRefEntry( )
505 bool ScFormulaDlg::IsTableLocked( ) const
507 // default: reference input can also be used to switch the table
510 void ScFormulaDlg::ToggleCollapsed( formula::RefEdit
* pEdit
, formula::RefButton
* pButton
)
512 m_aHelper
.ToggleCollapsed(pEdit
,pButton
);
514 void ScFormulaDlg::ReleaseFocus( formula::RefEdit
* pEdit
, formula::RefButton
* pButton
)
516 m_aHelper
.ReleaseFocus(pEdit
,pButton
);
518 void ScFormulaDlg::dispatch(bool _bOK
, bool _bMatrixChecked
)
520 SfxBoolItem
aRetItem( SID_DLG_RETOK
, _bOK
);
521 SfxBoolItem
aMatItem( SID_DLG_MATRIX
, _bMatrixChecked
);
522 SfxStringItem
aStrItem( SCITEM_STRING
, getCurrentFormula() );
524 // if edit line is empty (caused by document switching) -> string is empty
525 // -> don't delete old formula
526 if ( aStrItem
.GetValue().isEmpty() )
527 aRetItem
.SetValue( false ); // sal_False = Cancel
529 ScFormulaReferenceHelper::SetDispatcherLock( false ); // turn off modal-mode
533 GetBindings().GetDispatcher()->Execute( SID_INS_FUNCTION
,
534 SfxCallMode::ASYNCHRON
| SfxCallMode::RECORD
,
535 &aRetItem
, &aStrItem
, &aMatItem
, 0L );
537 void ScFormulaDlg::setDispatcherLock( bool bLock
)
539 ScFormulaReferenceHelper::SetDispatcherLock( bLock
);
541 void ScFormulaDlg::setReferenceInput(const formula::FormEditData
* _pData
)
543 ScModule
* pScMod
= SC_MOD();
544 ScFormEditData
& rData
= const_cast<ScFormEditData
&>(dynamic_cast<const ScFormEditData
&>(*_pData
));
545 pScMod
->SetRefInputHdl(rData
.GetInputHandler());
547 void ScFormulaDlg::deleteFormData()
549 ScModule
* pScMod
= SC_MOD();
550 pScMod
->ClearFormEditData(); // pData is invalid!
552 void ScFormulaDlg::clear()
556 //restore reference inputhandler
557 ScModule
* pScMod
= SC_MOD();
558 pScMod
->SetRefInputHdl(NULL
);
560 // force Enable() of edit line
561 ScTabViewShell
* pScViewShell
= PTR_CAST(ScTabViewShell
, SfxViewShell::Current());
563 pScViewShell
->UpdateInputHandler();
565 void ScFormulaDlg::switchBack()
567 ScModule
* pScMod
= SC_MOD();
568 // back to the document
569 // (foreign doc could be above - #34222#)
570 ScInputHandler
* pHdl
= pScMod
->GetInputHdl();
573 pHdl
->ViewShellGone(NULL
); // -> get active view
574 pHdl
->ShowRefFrame();
577 // restore current chart (cause mouse-RefInput)
578 ScTabViewShell
* pScViewShell
= PTR_CAST(ScTabViewShell
, SfxViewShell::Current());
581 ScViewData
& rVD
=pScViewShell
->GetViewData();
582 SCTAB nExecTab
= aCursorPos
.Tab();
583 if ( nExecTab
!= rVD
.GetTabNo() )
584 pScViewShell
->SetTabNo( nExecTab
);
586 SCROW nRow
=aCursorPos
.Row();
587 SCCOL nCol
=aCursorPos
.Col();
589 if(rVD
.GetCurX()!=nCol
|| rVD
.GetCurY()!=nRow
)
590 pScViewShell
->SetCursor(nCol
,nRow
);
593 formula::FormEditData
* ScFormulaDlg::getFormEditData() const
595 ScModule
* pScMod
= SC_MOD();
596 return pScMod
->GetFormEditData();
598 void ScFormulaDlg::setCurrentFormula(const OUString
& _sReplacement
)
600 ScModule
* pScMod
= SC_MOD();
602 //fdo#69971 We need the EditEngine Modification handler of the inputbar that we
603 //are feeding to be disabled while this dialog is open. Otherwise we end up in
604 //a situation where...
605 //a) this ScFormulaDlg changes the editengine
606 //b) the modify callback gets called
607 //c) which also modifies the editengine
608 //d) on return from that modify handler the editengine attempts to use
609 // old node pointers which were replaced and removed by c
611 //We turn it off in the ctor and back on in the dtor, but if calc has
612 //to repaint, e.g. when switching to another window and back, then in
613 //ScMultiTextWnd::Paint a new editengine will have been created via
614 //GetEditView with its default Modification handler enabled. So ensure
615 //its off when we will access it via InputReplaceSelection
616 pScMod
->InputTurnOffWinEngine();
618 pScMod
->InputReplaceSelection(_sReplacement
);
620 void ScFormulaDlg::setSelection(sal_Int32 _nStart
, sal_Int32 _nEnd
)
622 ScModule
* pScMod
= SC_MOD();
623 pScMod
->InputSetSelection( _nStart
, _nEnd
);
625 void ScFormulaDlg::getSelection(sal_Int32
& _nStart
, sal_Int32
& _nEnd
) const
627 ScModule
* pScMod
= SC_MOD();
628 pScMod
->InputGetSelection( _nStart
, _nEnd
);
630 OUString
ScFormulaDlg::getCurrentFormula() const
632 ScModule
* pScMod
= SC_MOD();
633 return pScMod
->InputGetFormulaStr();
635 formula::IFunctionManager
* ScFormulaDlg::getFunctionManager()
637 return ScGlobal::GetStarCalcFunctionMgr();
639 uno::Reference
< sheet::XFormulaParser
> ScFormulaDlg::getFormulaParser() const
643 uno::Reference
< sheet::XFormulaOpCodeMapper
> ScFormulaDlg::getFormulaOpCodeMapper() const
645 return m_xOpCodeMapper
;
648 table::CellAddress
ScFormulaDlg::getReferencePosition() const
650 return table::CellAddress(aCursorPos
.Tab(),aCursorPos
.Col(),aCursorPos
.Row());
653 ::std::unique_ptr
<formula::FormulaTokenArray
> ScFormulaDlg::convertToTokenArray(const uno::Sequence
< sheet::FormulaToken
>& _aTokenList
)
655 ::std::unique_ptr
<formula::FormulaTokenArray
> pArray(new ScTokenArray());
656 pArray
->Fill(_aTokenList
, pDoc
->GetSharedStringPool(), pDoc
->GetExternalRefManager());
660 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */