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/.
10 #include <namedefdlg.hxx>
12 #include <formula/errorcodes.hxx>
13 #include <sfx2/app.hxx>
14 #include <unotools/charclass.hxx>
16 #include <compiler.hxx>
17 #include <document.hxx>
18 #include <globstr.hrc>
19 #include <scresid.hxx>
20 #include <globalnames.hxx>
21 #include <rangenam.hxx>
22 #include <reffact.hxx>
23 #include <undorangename.hxx>
24 #include <tabvwsh.hxx>
25 #include <tokenarray.hxx>
27 ScNameDefDlg::ScNameDefDlg( SfxBindings
* pB
, SfxChildWindow
* pCW
, weld::Window
* pParent
,
28 const ScViewData
& rViewData
, std::map
<OUString
, ScRangeName
*>&& aRangeMap
,
29 const ScAddress
& aCursorPos
, const bool bUndo
)
30 : ScAnyRefDlgController( pB
, pCW
, pParent
, u
"modules/scalc/ui/definename.ui"_ustr
, u
"DefineNameDialog"_ustr
)
32 , mrDoc(rViewData
.GetDocument())
33 , mpDocShell ( rViewData
.GetDocShell() )
34 , maCursorPos( aCursorPos
)
35 , maGlobalNameStr ( ScResId(STR_GLOBAL_SCOPE
) )
36 , maErrInvalidNameStr( ScResId(STR_ERR_NAME_INVALID
))
37 , maErrInvalidNameCellRefStr( ScResId(STR_ERR_NAME_INVALID_CELL_REF
))
38 , maErrInvalidSheetReference(ScResId(STR_INVALID_TABREF_PRINT_AREA
))
39 , maErrNameInUse ( ScResId(STR_ERR_NAME_EXISTS
))
40 , maRangeMap( std::move(aRangeMap
) )
41 , m_xEdName(m_xBuilder
->weld_entry(u
"edit"_ustr
))
42 , m_xEdRange(new formula::RefEdit(m_xBuilder
->weld_entry(u
"range"_ustr
)))
43 , m_xRbRange(new formula::RefButton(m_xBuilder
->weld_button(u
"refbutton"_ustr
)))
44 , m_xLbScope(m_xBuilder
->weld_combo_box(u
"scope"_ustr
))
45 , m_xBtnRowHeader(m_xBuilder
->weld_check_button(u
"rowheader"_ustr
))
46 , m_xBtnColHeader(m_xBuilder
->weld_check_button(u
"colheader"_ustr
))
47 , m_xBtnPrintArea(m_xBuilder
->weld_check_button(u
"printarea"_ustr
))
48 , m_xBtnCriteria(m_xBuilder
->weld_check_button(u
"filter"_ustr
))
49 , m_xBtnAdd(m_xBuilder
->weld_button(u
"add"_ustr
))
50 , m_xBtnCancel(m_xBuilder
->weld_button(u
"cancel"_ustr
))
51 , m_xFtInfo(m_xBuilder
->weld_label(u
"label"_ustr
))
52 , m_xExpander(m_xBuilder
->weld_expander(u
"more"_ustr
))
53 , m_xFtRange(m_xBuilder
->weld_label(u
"label3"_ustr
))
55 m_xEdRange
->SetReferences(this, m_xFtRange
.get());
56 m_xRbRange
->SetReferences(this, m_xEdRange
.get());
57 maStrInfoDefault
= m_xFtInfo
->get_label();
59 // Initialize scope list.
60 m_xLbScope
->append_text(maGlobalNameStr
);
61 m_xLbScope
->set_active(0);
62 SCTAB n
= mrDoc
.GetTableCount();
63 for (SCTAB i
= 0; i
< n
; ++i
)
66 mrDoc
.GetName(i
, aTabName
);
67 m_xLbScope
->append_text(aTabName
);
70 m_xBtnCancel
->connect_clicked( LINK( this, ScNameDefDlg
, CancelBtnHdl
));
71 m_xBtnAdd
->connect_clicked( LINK( this, ScNameDefDlg
, AddBtnHdl
));
72 m_xEdName
->connect_changed( LINK( this, ScNameDefDlg
, NameModifyHdl
));
73 m_xEdRange
->SetGetFocusHdl( LINK( this, ScNameDefDlg
, AssignGetFocusHdl
) );
74 m_xEdRange
->SetModifyHdl( LINK( this, ScNameDefDlg
, RefEdModifyHdl
) );
75 m_xBtnPrintArea
->connect_toggled(LINK(this, ScNameDefDlg
, EdModifyCheckBoxHdl
));
77 m_xBtnAdd
->set_sensitive(false); // empty name is invalid
81 rViewData
.GetSimpleArea( aRange
);
82 OUString
aAreaStr(aRange
.Format(mrDoc
, ScRefFlags::RANGE_ABS_3D
,
83 ScAddress::Details(mrDoc
.GetAddressConvention(), 0, 0)));
85 m_xEdRange
->SetText( aAreaStr
);
87 m_xEdName
->grab_focus();
88 m_xEdName
->select_region(0, -1);
91 ScNameDefDlg::~ScNameDefDlg()
95 void ScNameDefDlg::CancelPushed()
101 if (ScTabViewShell
* pViewSh
= ScTabViewShell::GetActiveViewShell())
102 pViewSh
->SwitchBetweenRefDialogs(this);
106 bool ScNameDefDlg::IsFormulaValid()
108 const OUString aRangeOrFormulaExp
= m_xEdRange
->GetText();
109 // tdf#140394 - check if formula is a valid print range
110 if (m_xBtnPrintArea
->get_active())
112 const ScRefFlags nValidAddr
= ScRefFlags::VALID
| ScRefFlags::ROW_VALID
| ScRefFlags::COL_VALID
;
113 const ScRefFlags nValidRange
= nValidAddr
| ScRefFlags::ROW2_VALID
| ScRefFlags::COL2_VALID
;
114 const formula::FormulaGrammar::AddressConvention eConv
= mrDoc
.GetAddressConvention();
115 const sal_Unicode sep
= ScCompiler::GetNativeSymbolChar(ocSep
);
119 for (sal_Int32 nIdx
= 0; nIdx
>= 0;)
121 const OUString aOne
= aRangeOrFormulaExp
.getToken(0, sep
, nIdx
);
122 ScRefFlags nResult
= aRange
.Parse(aOne
, mrDoc
, eConv
);
123 if ((nResult
& nValidRange
) != nValidRange
)
125 ScRefFlags nAddrResult
= aAddr
.Parse(aOne
, mrDoc
, eConv
);
126 if ((nAddrResult
& nValidAddr
) != nValidAddr
)
133 ScCompiler
aComp(mrDoc
, maCursorPos
, mrDoc
.GetGrammar());
134 std::unique_ptr
<ScTokenArray
> pCode
= aComp
.CompileString(m_xEdRange
->GetText());
135 if (pCode
->GetCodeError() != FormulaError::NONE
)
142 bool ScNameDefDlg::IsNameValid()
144 OUString aScope
= m_xLbScope
->get_active_text();
145 OUString aName
= m_xEdName
->get_text();
147 bool bIsNameValid
= true;
148 OUString aHelpText
= maStrInfoDefault
;
150 ScRangeName
* pRangeName
= nullptr;
151 if(aScope
== maGlobalNameStr
)
153 const auto iter
= maRangeMap
.find(STR_GLOBAL_RANGE_NAME
);
154 assert(iter
!= maRangeMap
.end());
155 pRangeName
= iter
->second
;
159 const auto iter
= maRangeMap
.find(aScope
);
160 assert(iter
!= maRangeMap
.end());
161 pRangeName
= iter
->second
;
164 ScRangeData::IsNameValidType eType
;
165 if ( aName
.isEmpty() )
167 bIsNameValid
= false;
169 else if ((eType
= ScRangeData::IsNameValid(aName
, mrDoc
))
170 != ScRangeData::IsNameValidType::NAME_VALID
)
172 if (eType
== ScRangeData::IsNameValidType::NAME_INVALID_BAD_STRING
)
174 aHelpText
= maErrInvalidNameStr
;
176 else if (eType
== ScRangeData::IsNameValidType::NAME_INVALID_CELL_REF
)
178 aHelpText
= maErrInvalidNameCellRefStr
;
180 bIsNameValid
= false;
182 else if (pRangeName
->findByUpperName(ScGlobal::getCharClass().uppercase(aName
)))
184 aHelpText
= maErrNameInUse
;
185 bIsNameValid
= false;
188 if (!IsFormulaValid())
190 bIsNameValid
= false;
191 if (m_xBtnPrintArea
->get_active())
192 aHelpText
= maErrInvalidSheetReference
;
193 //TODO: info message for a non valid formula (print range not checked)
196 m_xEdName
->set_tooltip_text(aHelpText
);
197 m_xEdName
->set_message_type(bIsNameValid
|| aName
.isEmpty() ? weld::EntryMessageType::Normal
198 : weld::EntryMessageType::Error
);
199 m_xBtnAdd
->set_sensitive(bIsNameValid
);
203 void ScNameDefDlg::AddPushed()
205 OUString aScope
= m_xLbScope
->get_active_text();
206 OUString aName
= m_xEdName
->get_text();
207 OUString aExpression
= m_xEdRange
->GetText();
213 if (aScope
.isEmpty())
218 ScRangeName
* pRangeName
= nullptr;
219 if(aScope
== maGlobalNameStr
)
221 const auto iter
= maRangeMap
.find(STR_GLOBAL_RANGE_NAME
);
222 assert(iter
!= maRangeMap
.end());
223 pRangeName
= iter
->second
;
227 const auto iter
= maRangeMap
.find(aScope
);
228 assert(iter
!= maRangeMap
.end());
229 pRangeName
= iter
->second
;
234 if (!IsNameValid()) //should not happen, but make sure we don't break anything
238 ScRangeData::Type nType
= ScRangeData::Type::Name
;
240 ScRangeData
* pNewEntry
= new ScRangeData( mrDoc
,
246 if ( m_xBtnRowHeader
->get_active() ) nType
|= ScRangeData::Type::RowHeader
;
247 if ( m_xBtnColHeader
->get_active() ) nType
|= ScRangeData::Type::ColHeader
;
248 if ( m_xBtnPrintArea
->get_active() ) nType
|= ScRangeData::Type::PrintArea
;
249 if ( m_xBtnCriteria
->get_active() ) nType
|= ScRangeData::Type::Criteria
;
251 pNewEntry
->AddType(nType
);
253 // aExpression valid?
254 if ( FormulaError::NONE
== pNewEntry
->GetErrCode() )
256 if ( !pRangeName
->insert( pNewEntry
, false /*bReuseFreeIndex*/ ) )
261 // this means we called directly through the menu
264 // if no table with that name is found, assume global range name
265 if (!mrDoc
.GetTable(aScope
, nTab
))
268 assert( pNewEntry
); // undo of no insertion smells fishy
270 mpDocShell
->GetUndoManager()->AddUndoAction(
271 std::make_unique
<ScUndoAddRangeData
>( mpDocShell
, pNewEntry
, nTab
) );
273 // set table stream invalid, otherwise RangeName won't be saved if no other
274 // call invalidates the stream
276 mrDoc
.SetStreamValid(nTab
, false);
277 SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScAreasChanged
) );
278 mpDocShell
->SetDocumentModified();
285 if (ScTabViewShell
* pViewSh
= ScTabViewShell::GetActiveViewShell())
286 pViewSh
->SwitchBetweenRefDialogs(this);
292 m_xEdRange
->GrabFocus();
293 m_xEdRange
->SelectAll();
298 void ScNameDefDlg::GetNewData(OUString
& rName
, OUString
& rScope
)
304 bool ScNameDefDlg::IsRefInputMode() const
306 return m_xEdRange
->GetWidget()->get_sensitive();
309 void ScNameDefDlg::RefInputDone( bool bForced
)
311 ScAnyRefDlgController::RefInputDone(bForced
);
315 void ScNameDefDlg::SetReference( const ScRange
& rRef
, ScDocument
& rDocP
)
317 if (m_xEdRange
->GetWidget()->get_sensitive())
319 if ( rRef
.aStart
!= rRef
.aEnd
)
320 RefInputStart(m_xEdRange
.get());
321 OUString
aRefStr(rRef
.Format(rDocP
, ScRefFlags::RANGE_ABS_3D
,
322 ScAddress::Details(rDocP
.GetAddressConvention(), 0, 0)));
323 m_xEdRange
->SetRefString( aRefStr
);
327 void ScNameDefDlg::Close()
329 DoClose( ScNameDefDlgWrapper::GetChildWindowId() );
332 void ScNameDefDlg::SetActive()
334 m_xEdRange
->GrabFocus();
338 IMPL_LINK_NOARG(ScNameDefDlg
, CancelBtnHdl
, weld::Button
&, void)
343 IMPL_LINK_NOARG(ScNameDefDlg
, AddBtnHdl
, weld::Button
&, void)
348 IMPL_LINK_NOARG(ScNameDefDlg
, NameModifyHdl
, weld::Entry
&, void)
353 IMPL_LINK_NOARG(ScNameDefDlg
, AssignGetFocusHdl
, formula::RefEdit
&, void)
358 IMPL_LINK_NOARG(ScNameDefDlg
, EdModifyCheckBoxHdl
, weld::Toggleable
&, void)
363 IMPL_LINK_NOARG(ScNameDefDlg
, RefEdModifyHdl
, formula::RefEdit
&, void)
368 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */