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 <rangelst.hxx>
21 #include <comphelper/string.hxx>
22 #include <sfx2/app.hxx>
23 #include <sfx2/viewsh.hxx>
24 #include <sfx2/bindings.hxx>
25 #include <sfx2/dispatch.hxx>
26 #include <sfx2/viewfrm.hxx>
27 #include <osl/diagnose.h>
28 #include <o3tl/string_view.hxx>
30 #include <anyrefdg.hxx>
32 #include <inputhdl.hxx>
34 #include <inputwin.hxx>
35 #include <tabvwsh.hxx>
37 #include <rfindlst.hxx>
38 #include <compiler.hxx>
39 #include <inputopt.hxx>
40 #include <rangeutl.hxx>
41 #include <tokenarray.hxx>
42 #include <comphelper/lok.hxx>
47 ScFormulaReferenceHelper::ScFormulaReferenceHelper(IAnyRefDialog
* _pDlg
,SfxBindings
* _pBindings
)
49 , m_pRefEdit (nullptr)
52 , m_pBindings(_pBindings
)
54 , m_bHighlightRef(false)
56 ScInputOptions aInputOption
= ScModule::get()->GetInputOptions();
57 m_bEnableColorRef
=aInputOption
.GetRangeFinder();
60 ScFormulaReferenceHelper::~ScFormulaReferenceHelper() COVERITY_NOEXCEPT_FALSE
65 void ScFormulaReferenceHelper::dispose()
67 // common cleanup for ScAnyRefDlg and ScFormulaDlg is done here
71 ScInputHandler
* pInputHdl
= ScModule::get()->GetInputHdl();
73 pInputHdl
->ResetDelayTimer(); // stop the timer for disabling the input line
78 void ScFormulaReferenceHelper::enableInput( bool bEnable
)
80 ScDocShell
* pDocShell
= static_cast<ScDocShell
*>(SfxObjectShell::GetFirst(checkSfxObjectShell
<ScDocShell
>));
83 SfxViewFrame
* pFrame
= SfxViewFrame::GetFirst( pDocShell
);
86 // enable everything except InPlace, including bean frames
87 if ( !pFrame
->GetFrame().IsInPlace() )
89 SfxViewShell
* p
= pFrame
->GetViewShell();
90 ScTabViewShell
* pViewSh
= dynamic_cast< ScTabViewShell
*>( p
);
93 vcl::Window
*pWin
=pViewSh
->GetWindow();
96 vcl::Window
*pParent
=pWin
->GetParent();
99 pParent
->EnableInput(bEnable
);
100 pViewSh
->EnableRefInput(bEnable
);
105 pFrame
= SfxViewFrame::GetNext( *pFrame
, pDocShell
);
108 pDocShell
= static_cast<ScDocShell
*>(SfxObjectShell::GetNext(*pDocShell
, checkSfxObjectShell
<ScDocShell
>));
112 void ScFormulaReferenceHelper::ShowSimpleReference(std::u16string_view rStr
)
114 if (!m_bEnableColorRef
)
117 m_bHighlightRef
= true;
118 ScViewData
* pViewData
=ScDocShell::GetViewData();
122 ScDocument
& rDoc
= pViewData
->GetDocument();
123 ScTabViewShell
* pTabViewShell
=pViewData
->GetViewShell();
125 ScRangeList aRangeList
;
127 pTabViewShell
->DoneRefMode();
128 pTabViewShell
->ClearHighlightRanges();
130 if( ParseWithNames( aRangeList
, rStr
, rDoc
) )
132 for ( size_t i
= 0, nRanges
= aRangeList
.size(); i
< nRanges
; ++i
)
134 ScRange
const & rRangeEntry
= aRangeList
[ i
];
135 Color aColName
= ScRangeFindList::GetColorName( i
);
136 pTabViewShell
->AddHighlightRange( rRangeEntry
, aColName
);
141 bool ScFormulaReferenceHelper::ParseWithNames( ScRangeList
& rRanges
, std::u16string_view rStr
, const ScDocument
& rDoc
)
148 ScAddress::Details
aDetails(rDoc
.GetAddressConvention(), 0, 0);
155 OUString
aRangeStr( o3tl::getToken(rStr
, 0, ';', nIdx
) );
157 ScRefFlags nFlags
= aRange
.ParseAny( aRangeStr
, rDoc
, aDetails
);
158 if ( nFlags
& ScRefFlags::VALID
)
160 if ( (nFlags
& ScRefFlags::TAB_3D
) == ScRefFlags::ZERO
)
161 aRange
.aStart
.SetTab( m_nRefTab
);
162 if ( (nFlags
& ScRefFlags::TAB2_3D
) == ScRefFlags::ZERO
)
163 aRange
.aEnd
.SetTab( aRange
.aStart
.Tab() );
164 rRanges
.push_back( aRange
);
166 else if ( ScRangeUtil::MakeRangeFromName( aRangeStr
, rDoc
, m_nRefTab
, aRange
, RUTL_NAMES
, aDetails
) )
167 rRanges
.push_back( aRange
);
176 void ScFormulaReferenceHelper::ShowFormulaReference(const OUString
& rStr
)
178 if( !m_bEnableColorRef
)
181 m_bHighlightRef
=true;
182 ScViewData
* pViewData
=ScDocShell::GetViewData();
183 if ( !(pViewData
&& m_pRefComp
) )
186 ScTabViewShell
* pTabViewShell
=pViewData
->GetViewShell();
187 SCCOL nCol
= pViewData
->GetCurX();
188 SCROW nRow
= pViewData
->GetCurY();
189 SCTAB nTab
= pViewData
->GetTabNo();
190 ScAddress
aPos( nCol
, nRow
, nTab
);
192 std::unique_ptr
<ScTokenArray
> pScTokA(m_pRefComp
->CompileString(rStr
));
194 if (!(pTabViewShell
&& pScTokA
))
197 const ScViewData
& rViewData
= pTabViewShell
->GetViewData();
198 ScDocument
& rDoc
= rViewData
.GetDocument();
199 pTabViewShell
->DoneRefMode();
200 pTabViewShell
->ClearHighlightRanges();
202 formula::FormulaTokenArrayPlainIterator
aIter(*pScTokA
);
203 const formula::FormulaToken
* pToken
= aIter
.GetNextReference();
207 while(pToken
!=nullptr)
209 bool bDoubleRef
=(pToken
->GetType()==formula::svDoubleRef
);
211 if(pToken
->GetType()==formula::svSingleRef
|| bDoubleRef
)
216 ScComplexRefData
aRef( *pToken
->GetDoubleRef() );
217 aRange
= aRef
.toAbs(rDoc
, aPos
);
221 ScSingleRefData
aRef( *pToken
->GetSingleRef() );
222 aRange
.aStart
= aRef
.toAbs(rDoc
, aPos
);
223 aRange
.aEnd
= aRange
.aStart
;
225 Color aColName
=ScRangeFindList::GetColorName(nIndex
++);
226 pTabViewShell
->AddHighlightRange(aRange
, aColName
);
229 pToken
= aIter
.GetNextReference();
233 void ScFormulaReferenceHelper::HideReference( bool bDoneRefMode
)
235 ScViewData
* pViewData
=ScDocShell::GetViewData();
237 if( !(pViewData
&& m_bHighlightRef
&& m_bEnableColorRef
))
240 ScTabViewShell
* pTabViewShell
=pViewData
->GetViewShell();
242 if(pTabViewShell
!=nullptr)
244 // bDoneRefMode is sal_False when called from before SetReference.
245 // In that case, RefMode was just started and must not be ended now.
248 pTabViewShell
->DoneRefMode();
249 pTabViewShell
->ClearHighlightRanges();
251 if( comphelper::LibreOfficeKit::isActive() )
254 std::vector
<ReferenceMark
> aReferenceMarks
;
255 ScInputHandler::SendReferenceMarks( pTabViewShell
, aReferenceMarks
);
258 m_bHighlightRef
=false;
261 void ScFormulaReferenceHelper::ShowReference(const OUString
& rStr
)
263 if( !m_bEnableColorRef
)
266 // Exclude ';' semicolon as it is the separator for ParseWithNames() used
267 // in ShowSimpleReference(). Also sheet separator '.' dot is part of simple
268 // reference (could be array col/row separator as well but then in '{' '}'
269 // braces). Prefer '!' exclamation mark to be intersection operator rather
270 // than Excel sheet separator.
271 if (comphelper::string::indexOfAny( rStr
, u
"()+-*/^%&=<>~! #[]{},|\\@", 0) != -1)
273 ShowFormulaReference(rStr
);
277 ShowSimpleReference(rStr
);
281 void ScFormulaReferenceHelper::ReleaseFocus( formula::RefEdit
* pEdit
)
283 if( !m_pRefEdit
&& pEdit
)
285 m_pDlg
->RefInputStart( pEdit
);
288 ScTabViewShell
* pViewShell
= ScTabViewShell::GetActiveViewShell();
292 pViewShell
->ActiveGrabFocus();
296 const ScViewData
& rViewData
= pViewShell
->GetViewData();
297 ScDocument
& rDoc
= rViewData
.GetDocument();
298 ScRangeList aRangeList
;
299 if( !ParseWithNames( aRangeList
, m_pRefEdit
->GetText(), rDoc
) )
302 if ( !aRangeList
.empty() )
304 const ScRange
& rRange
= aRangeList
.front();
305 pViewShell
->SetTabNo( rRange
.aStart
.Tab() );
306 pViewShell
->MoveCursorAbs( rRange
.aStart
.Col(),
307 rRange
.aStart
.Row(), SC_FOLLOW_JUMP
, false, false );
308 pViewShell
->MoveCursorAbs( rRange
.aEnd
.Col(),
309 rRange
.aEnd
.Row(), SC_FOLLOW_JUMP
, true, false );
310 m_pDlg
->SetReference( rRange
, rDoc
);
314 void ScFormulaReferenceHelper::Init()
316 ScViewData
* pViewData
=ScDocShell::GetViewData(); //! use pScViewShell?
320 ScDocument
& rDoc
= pViewData
->GetDocument();
321 SCCOL nCol
= pViewData
->GetCurX();
322 SCROW nRow
= pViewData
->GetCurY();
323 SCTAB nTab
= pViewData
->GetTabNo();
324 ScAddress
aCursorPos( nCol
, nRow
, nTab
);
326 m_pRefComp
.reset( new ScCompiler( rDoc
, aCursorPos
, rDoc
.GetGrammar()) );
327 m_pRefComp
->EnableJumpCommandReorder(false);
328 m_pRefComp
->EnableStopOnError(false);
333 IMPL_LINK_NOARG(ScFormulaReferenceHelper
, ActivateHdl
, weld::Widget
&, bool)
336 m_pRefEdit
->GrabFocus();
337 m_pDlg
->RefInputDone(true);
341 void ScFormulaReferenceHelper::RefInputDone( bool bForced
)
343 if ( !CanInputDone( bForced
) )
349 // Adjust window title
350 m_pDialog
->set_title(m_sOldDialogText
);
353 m_pRefEdit
->SetActivateHdl(Link
<weld::Widget
&, bool>());
358 m_pRefBtn
->SetActivateHdl(Link
<weld::Widget
&, bool>());
359 m_pRefBtn
->SetStartImage();
362 m_pDialog
->undo_collapse();
364 m_pRefEdit
= nullptr;
368 void ScFormulaReferenceHelper::RefInputStart( formula::RefEdit
* pEdit
, formula::RefButton
* pButton
)
376 // Save and adjust window title
377 m_sOldDialogText
= m_pDialog
->get_title();
378 if (weld::Label
*pLabel
= m_pRefEdit
->GetLabelWidgetForShrinkMode())
380 const OUString sLabel
= pLabel
->get_label();
381 if (!sLabel
.isEmpty())
383 const OUString sNewDialogText
= m_sOldDialogText
+ ": " + comphelper::string::stripEnd(sLabel
, ':');
384 m_pDialog
->set_title(pLabel
->strip_mnemonic(sNewDialogText
));
388 m_pDialog
->collapse(pEdit
->GetWidget(), pButton
? pButton
->GetWidget() : nullptr);
392 pButton
->SetEndImage();
394 m_pRefEdit
->SetActivateHdl(LINK(this, ScFormulaReferenceHelper
, ActivateHdl
));
396 m_pRefBtn
->SetActivateHdl(LINK(this, ScFormulaReferenceHelper
, ActivateHdl
));
399 void ScFormulaReferenceHelper::ToggleCollapsed( formula::RefEdit
* pEdit
, formula::RefButton
* pButton
)
404 if( m_pRefEdit
== pEdit
) // is this the active ref edit field?
406 m_pRefEdit
->GrabFocus(); // before RefInputDone()
407 m_pDlg
->RefInputDone( true ); // finish ref input
411 m_pDlg
->RefInputDone( true ); // another active ref edit?
412 m_pDlg
->RefInputStart( pEdit
, pButton
); // start ref input
413 // pRefEdit might differ from pEdit after RefInputStart() (i.e. ScFormulaDlg)
415 m_pRefEdit
->GrabFocus();
419 void ScFormulaReferenceHelper::DoClose( sal_uInt16 nId
)
421 SfxApplication
* pSfxApp
= SfxGetpApp();
423 SetDispatcherLock( false ); //! here and in dtor ?
425 SfxViewFrame
* pViewFrm
= SfxViewFrame::Current();
426 if ( pViewFrm
&& pViewFrm
->HasChildWindow(FID_INPUTLINE_STATUS
) )
428 // The input row is disabled with ToolBox::Disable disabled, thus it must be
429 // reenabled with ToolBox::Enable (before the AppWindow is enabled)
430 // for the buttons to be drawn as enabled.
431 SfxChildWindow
* pChild
= pViewFrm
->GetChildWindow(FID_INPUTLINE_STATUS
);
434 ScInputWindow
* pWin
= static_cast<ScInputWindow
*>(pChild
->GetWindow());
439 // find parent view frame to close dialog
440 SfxViewFrame
* pMyViewFrm
= nullptr;
443 SfxDispatcher
* pMyDisp
= m_pBindings
->GetDispatcher();
445 pMyViewFrm
= pMyDisp
->GetFrame();
447 ScModule::get()->SetRefDialog(nId
, false, pMyViewFrm
);
449 pSfxApp
->Broadcast( SfxHint( SfxHintId::ScKillEditView
) );
451 ScTabViewShell
* pScViewShell
= ScTabViewShell::GetActiveViewShell();
453 pScViewShell
->UpdateInputHandler(true);
456 void ScFormulaReferenceHelper::SetDispatcherLock( bool bLock
)
458 if (!comphelper::LibreOfficeKit::isActive())
460 // lock / unlock only the dispatchers of Calc documents
461 ScDocShell
* pDocShell
= static_cast<ScDocShell
*>(SfxObjectShell::GetFirst(checkSfxObjectShell
<ScDocShell
>));
464 SfxViewFrame
* pFrame
= SfxViewFrame::GetFirst(pDocShell
);
467 SfxDispatcher
* pDisp
= pFrame
->GetDispatcher();
470 pFrame
= SfxViewFrame::GetNext(*pFrame
, pDocShell
);
472 pDocShell
= static_cast<ScDocShell
*>(SfxObjectShell::GetNext(*pDocShell
, checkSfxObjectShell
<ScDocShell
>));
475 // if a new view is created while the dialog is open,
476 // that view's dispatcher is locked when trying to create the dialog
477 // for that view (ScTabViewShell::CreateRefDialog)
480 // lock / unlock only the dispatcher of Calc document
481 SfxDispatcher
* pDisp
= nullptr;
484 pDisp
= m_pBindings
->GetDispatcher();
486 else if(SfxViewFrame
* pViewFrame
= SfxViewFrame::Current())
488 if (dynamic_cast< ScTabViewShell
* >(pViewFrame
->GetViewShell()))
489 pDisp
= pViewFrame
->GetDispatcher();
496 void ScFormulaReferenceHelper::ViewShellChanged()
498 enableInput( false );
500 EnableSpreadsheets();
502 void ScFormulaReferenceHelper::EnableSpreadsheets(bool bFlag
)
504 ScDocShell
* pDocShell
= static_cast<ScDocShell
*>(SfxObjectShell::GetFirst(checkSfxObjectShell
<ScDocShell
>));
507 SfxViewFrame
* pFrame
= SfxViewFrame::GetFirst( pDocShell
);
510 // enable everything except InPlace, including bean frames
511 if ( !pFrame
->GetFrame().IsInPlace() )
513 SfxViewShell
* p
= pFrame
->GetViewShell();
514 ScTabViewShell
* pViewSh
= dynamic_cast< ScTabViewShell
*>( p
);
517 vcl::Window
*pWin
=pViewSh
->GetWindow();
520 vcl::Window
*pParent
=pWin
->GetParent();
523 pParent
->EnableInput(bFlag
,false);
524 pViewSh
->EnableRefInput(bFlag
);
529 pFrame
= SfxViewFrame::GetNext( *pFrame
, pDocShell
);
532 pDocShell
= static_cast<ScDocShell
*>(SfxObjectShell::GetNext(*pDocShell
, checkSfxObjectShell
<ScDocShell
>));
536 static void lcl_InvalidateWindows()
538 ScDocShell
* pDocShell
= static_cast<ScDocShell
*>(SfxObjectShell::GetFirst(checkSfxObjectShell
<ScDocShell
>));
541 SfxViewFrame
* pFrame
= SfxViewFrame::GetFirst( pDocShell
);
544 // enable everything except InPlace, including bean frames
545 if ( !pFrame
->GetFrame().IsInPlace() )
547 SfxViewShell
* p
= pFrame
->GetViewShell();
548 ScTabViewShell
* pViewSh
= dynamic_cast< ScTabViewShell
*>( p
);
551 vcl::Window
*pWin
=pViewSh
->GetWindow();
554 vcl::Window
*pParent
=pWin
->GetParent();
556 pParent
->Invalidate();
560 pFrame
= SfxViewFrame::GetNext( *pFrame
, pDocShell
);
563 pDocShell
= static_cast<ScDocShell
*>(SfxObjectShell::GetNext(*pDocShell
, checkSfxObjectShell
<ScDocShell
>));
567 static void lcl_HideAllReferences()
569 SfxViewShell
* pSh
= SfxViewShell::GetFirst( true, checkSfxViewShell
<ScTabViewShell
> );
572 static_cast<ScTabViewShell
*>(pSh
)->ClearHighlightRanges();
573 pSh
= SfxViewShell::GetNext( *pSh
, true, checkSfxViewShell
<ScTabViewShell
> );
577 ScRefHandler::ScRefHandler(SfxDialogController
& rController
, SfxBindings
* pB
, bool bBindRef
)
578 : m_pController(&rController
)
579 , m_bInRefMode(false)
580 , m_aHelper(this, pB
)
583 m_aHelper
.SetDialog(rController
.getDialog());
585 if( bBindRef
) EnterRefMode();
588 bool ScRefHandler::EnterRefMode()
590 if( m_bInRefMode
) return false;
592 ScModule
* mod
= ScModule::get();
593 mod
->InputEnterHandler();
595 ScTabViewShell
* pScViewShell
= nullptr;
597 // title has to be from the view that opened the dialog,
598 // even if it's not the current view
600 SfxObjectShell
* pParentDoc
= nullptr;
603 SfxDispatcher
* pMyDisp
= m_pMyBindings
->GetDispatcher();
606 SfxViewFrame
* pMyViewFrm
= pMyDisp
->GetFrame();
609 pScViewShell
= dynamic_cast<ScTabViewShell
*>( pMyViewFrm
->GetViewShell() );
611 pScViewShell
->UpdateInputHandler(true);
612 pParentDoc
= pMyViewFrm
->GetObjectShell();
616 if ( !pParentDoc
&& pScViewShell
) // use current only if above fails
617 pParentDoc
= pScViewShell
->GetObjectShell();
619 m_aDocName
= pParentDoc
->GetTitle();
621 ScInputHandler
* pInputHdl
= mod
->GetInputHdl(pScViewShell
);
623 OSL_ENSURE( pInputHdl
, "Missing input handler :-/" );
626 pInputHdl
->NotifyChange( nullptr );
628 ScFormulaReferenceHelper::enableInput( false );
630 ScFormulaReferenceHelper::EnableSpreadsheets();
634 m_aHelper
.SetDispatcherLock( true );
640 ScRefHandler::~ScRefHandler() COVERITY_NOEXCEPT_FALSE
645 void ScRefHandler::disposeRefHandler()
647 m_pController
= nullptr;
652 bool ScRefHandler::LeaveRefMode()
654 if( !m_bInRefMode
) return false;
656 lcl_HideAllReferences();
658 SetDispatcherLock( false ); //! here and in DoClose ?
660 ScTabViewShell
* pScViewShell
= ScTabViewShell::GetActiveViewShell();
662 pScViewShell
->UpdateInputHandler(true);
664 lcl_InvalidateWindows();
666 m_bInRefMode
= false;
670 void ScRefHandler::SwitchToDocument()
672 ScTabViewShell
* pCurrent
= ScTabViewShell::GetActiveViewShell();
675 SfxObjectShell
* pObjSh
= pCurrent
->GetObjectShell();
676 if ( pObjSh
&& pObjSh
->GetTitle() == m_aDocName
)
678 // right document already visible -> nothing to do
683 SfxViewShell
* pSh
= SfxViewShell::GetFirst( true, checkSfxViewShell
<ScTabViewShell
> );
686 SfxObjectShell
* pObjSh
= pSh
->GetObjectShell();
687 if ( pObjSh
&& pObjSh
->GetTitle() == m_aDocName
)
689 // switch to first TabViewShell for document
690 static_cast<ScTabViewShell
*>(pSh
)->SetActive();
693 pSh
= SfxViewShell::GetNext( *pSh
, true, checkSfxViewShell
<ScTabViewShell
> );
697 bool ScRefHandler::IsDocAllowed(SfxObjectShell
* pDocSh
) const // pDocSh may be 0
699 // if aDocName isn't initialized, allow
700 if ( m_aDocName
.isEmpty() )
706 // default: allow only same document (overridden in function dialog)
707 return m_aDocName
==pDocSh
->GetTitle();
710 bool ScRefHandler::IsRefInputMode() const
712 return m_pController
->getDialog()->get_visible();
715 bool ScRefHandler::DoClose( sal_uInt16 nId
)
717 m_aHelper
.DoClose(nId
);
721 void ScRefHandler::SetDispatcherLock( bool bLock
)
723 m_aHelper
.SetDispatcherLock( bLock
);
726 void ScRefHandler::ViewShellChanged()
728 ScFormulaReferenceHelper::ViewShellChanged();
731 void ScRefHandler::AddRefEntry()
733 // override this for multi-references
736 bool ScRefHandler::IsTableLocked() const
738 // the default is that the sheet can be switched during while the reference is edited
743 // RefInputStart/Done: Zoom-In (AutoHide) on single field
744 // (using button or movement)
746 void ScRefHandler::RefInputStart( formula::RefEdit
* pEdit
, formula::RefButton
* pButton
)
748 m_aHelper
.RefInputStart( pEdit
, pButton
);
751 void ScRefHandler::ToggleCollapsed( formula::RefEdit
* pEdit
, formula::RefButton
* pButton
)
753 m_aHelper
.ToggleCollapsed( pEdit
, pButton
);
756 bool ScRefHandler::ParseWithNames( ScRangeList
& rRanges
, std::u16string_view rStr
, const ScDocument
& rDoc
)
758 return m_aHelper
.ParseWithNames( rRanges
, rStr
, rDoc
);
761 void ScRefHandler::HideReference( bool bDoneRefMode
)
763 m_aHelper
.HideReference( bDoneRefMode
);
766 void ScRefHandler::ShowReference(const OUString
& rStr
)
768 m_aHelper
.ShowReference(rStr
);
771 void ScRefHandler::ReleaseFocus( formula::RefEdit
* pEdit
)
773 m_aHelper
.ReleaseFocus( pEdit
);
776 void ScRefHandler::RefInputDone( bool bForced
)
778 m_aHelper
.RefInputDone( bForced
);
781 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */