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 .
22 #include <reffact.hxx>
23 #include <compiler.hxx>
24 #include <document.hxx>
25 #include <docfunc.hxx>
26 #include <globstr.hrc>
27 #include <scresid.hxx>
28 #include <namedlg.hxx>
29 #include <viewdata.hxx>
30 #include <tabvwsh.hxx>
32 #include <globalnames.hxx>
33 #include <tokenarray.hxx>
35 #include <vcl/settings.hxx>
36 #include <vcl/svapp.hxx>
37 #include <formula/errorcodes.hxx>
38 #include <unotools/charclass.hxx>
44 ScNameDlg::ScNameDlg( SfxBindings
* pB
, SfxChildWindow
* pCW
, weld::Window
* pParent
,
45 ScViewData
& rViewData
,
46 const ScAddress
& aCursorPos
,
47 std::map
<OUString
, std::unique_ptr
<ScRangeName
>> *const pRangeMap
)
48 : ScAnyRefDlgController(pB
, pCW
, pParent
, "modules/scalc/ui/managenamesdialog.ui",
51 , maGlobalNameStr(ScResId(STR_GLOBAL_SCOPE
))
52 , maErrInvalidNameStr(ScResId(STR_ERR_NAME_INVALID
))
53 , maErrNameInUse(ScResId(STR_ERR_NAME_EXISTS
))
54 , maStrMultiSelect(ScResId(STR_MULTI_SELECT
))
56 , mrViewData(rViewData
)
57 , mrDoc(rViewData
.GetDocument())
58 , maCursorPos(aCursorPos
)
59 , mbDataChanged(false)
60 , mbCloseWithoutUndo(false)
62 , m_xEdName(m_xBuilder
->weld_entry("name"))
63 , m_xFtAssign(m_xBuilder
->weld_label("label3"))
64 , m_xEdAssign(new formula::RefEdit(m_xBuilder
->weld_entry("range")))
65 , m_xRbAssign(new formula::RefButton(m_xBuilder
->weld_button("assign")))
66 , m_xLbScope(m_xBuilder
->weld_combo_box("scope"))
67 , m_xBtnPrintArea(m_xBuilder
->weld_check_button("printrange"))
68 , m_xBtnColHeader(m_xBuilder
->weld_check_button("colheader"))
69 , m_xBtnCriteria(m_xBuilder
->weld_check_button("filter"))
70 , m_xBtnRowHeader(m_xBuilder
->weld_check_button("rowheader"))
71 , m_xBtnAdd(m_xBuilder
->weld_button("add"))
72 , m_xBtnDelete(m_xBuilder
->weld_button("delete"))
73 , m_xBtnOk(m_xBuilder
->weld_button("ok"))
74 , m_xBtnCancel(m_xBuilder
->weld_button("cancel"))
75 , m_xFtInfo(m_xBuilder
->weld_label("info"))
76 , m_xExpander(m_xBuilder
->weld_expander("more"))
78 m_xEdAssign
->SetReferences(this, m_xFtAssign
.get());
79 m_xRbAssign
->SetReferences(this, m_xEdAssign
.get());
80 maStrInfoDefault
= m_xFtInfo
->get_label();
81 m_xFtInfo
->set_label(OUString());
85 std::map
<OUString
, ScRangeName
*> aRangeMap
;
86 mrDoc
.GetRangeNameMap(aRangeMap
);
87 for (const auto& [aTemp
, pRangeName
] : aRangeMap
)
89 m_RangeMap
.insert(std::make_pair(aTemp
, std::make_unique
<ScRangeName
>(*pRangeName
)));
94 m_RangeMap
.swap(*pRangeMap
);
99 ScNameDlg::~ScNameDlg()
103 void ScNameDlg::Init()
107 std::unique_ptr
<weld::TreeView
> xTreeView(m_xBuilder
->weld_tree_view("names"));
108 xTreeView
->set_size_request(xTreeView
->get_approximate_digit_width() * 75,
109 xTreeView
->get_height_rows(10));
110 m_xRangeManagerTable
.reset(new ScRangeManagerTable(std::move(xTreeView
), m_RangeMap
, maCursorPos
));
112 m_xRangeManagerTable
->connect_changed( LINK( this, ScNameDlg
, SelectionChangedHdl_Impl
) );
114 m_xBtnOk
->connect_clicked( LINK( this, ScNameDlg
, OkBtnHdl
) );
115 m_xBtnCancel
->connect_clicked( LINK( this, ScNameDlg
, CancelBtnHdl
) );
116 m_xBtnAdd
->connect_clicked( LINK( this, ScNameDlg
, AddBtnHdl
) );
117 m_xEdAssign
->SetGetFocusHdl( LINK( this, ScNameDlg
, AssignGetFocusHdl
) );
118 m_xEdAssign
->SetModifyHdl ( LINK( this, ScNameDlg
, RefEdModifyHdl
) );
119 m_xEdName
->connect_changed( LINK( this, ScNameDlg
, EdModifyHdl
) );
120 m_xLbScope
->connect_changed( LINK(this, ScNameDlg
, ScopeChangedHdl
) );
121 m_xBtnDelete
->connect_clicked( LINK( this, ScNameDlg
, RemoveBtnHdl
) );
122 m_xBtnPrintArea
->connect_toggled( LINK(this, ScNameDlg
, EdModifyCheckBoxHdl
) );
123 m_xBtnCriteria
->connect_toggled( LINK(this, ScNameDlg
, EdModifyCheckBoxHdl
) );
124 m_xBtnRowHeader
->connect_toggled( LINK(this, ScNameDlg
, EdModifyCheckBoxHdl
) );
125 m_xBtnColHeader
->connect_toggled( LINK(this, ScNameDlg
, EdModifyCheckBoxHdl
) );
127 // Initialize scope list.
128 m_xLbScope
->append_text(maGlobalNameStr
);
129 m_xLbScope
->set_active(0);
130 SCTAB n
= mrDoc
.GetTableCount();
131 for (SCTAB i
= 0; i
< n
; ++i
)
134 mrDoc
.GetName(i
, aTabName
);
135 m_xLbScope
->append_text(aTabName
);
138 CheckForEmptyTable();
140 if (m_xRangeManagerTable
->n_children())
142 m_xRangeManagerTable
->set_cursor(0);
143 m_xRangeManagerTable
->CheckForFormulaString();
149 bool ScNameDlg::IsRefInputMode() const
151 return m_xEdAssign
->GetWidget()->get_sensitive();
154 void ScNameDlg::RefInputDone( bool bForced
)
156 ScAnyRefDlgController::RefInputDone(bForced
);
157 RefEdModifyHdl(*m_xEdAssign
);
160 void ScNameDlg::SetReference( const ScRange
& rRef
, ScDocument
& rDocP
)
162 if (m_xEdAssign
->GetWidget()->get_sensitive())
164 if ( rRef
.aStart
!= rRef
.aEnd
)
165 RefInputStart(m_xEdAssign
.get());
166 OUString
aRefStr(rRef
.Format(rDocP
, ScRefFlags::RANGE_ABS_3D
,
167 ScAddress::Details(rDocP
.GetAddressConvention(), 0, 0)));
168 m_xEdAssign
->SetRefString( aRefStr
);
172 void ScNameDlg::Close()
174 if (mbDataChanged
&& !mbCloseWithoutUndo
)
175 mrViewData
.GetDocFunc().ModifyAllRangeNames(m_RangeMap
);
176 DoClose(ScNameDlgWrapper::GetChildWindowId());
179 void ScNameDlg::CheckForEmptyTable()
181 if (!m_xRangeManagerTable
->n_children())
183 m_xBtnDelete
->set_sensitive(false);
184 m_xEdAssign
->GetWidget()->set_sensitive(false);
185 m_xRbAssign
->GetWidget()->set_sensitive(false);
186 m_xEdName
->set_sensitive(false);
187 m_xLbScope
->set_sensitive(false);
189 m_xBtnCriteria
->set_sensitive(false);
190 m_xBtnPrintArea
->set_sensitive(false);
191 m_xBtnColHeader
->set_sensitive(false);
192 m_xBtnRowHeader
->set_sensitive(false);
196 m_xBtnDelete
->set_sensitive(true);
197 m_xEdAssign
->GetWidget()->set_sensitive(true);
198 m_xRbAssign
->GetWidget()->set_sensitive(true);
199 m_xEdName
->set_sensitive(true);
200 m_xLbScope
->set_sensitive(true);
202 m_xBtnCriteria
->set_sensitive(true);
203 m_xBtnPrintArea
->set_sensitive(true);
204 m_xBtnColHeader
->set_sensitive(true);
205 m_xBtnRowHeader
->set_sensitive(true);
209 void ScNameDlg::SetActive()
211 m_xEdAssign
->GrabFocus();
215 void ScNameDlg::UpdateChecks(const ScRangeData
* pData
)
217 // remove handlers, we only want the handlers to process
218 // user input and not when we are syncing the controls with our internal
219 // model ( also UpdateChecks is called already from some other event
220 // handlers, triggering handlers while already processing a handler can
221 // ( and does in this case ) corrupt the internal data
223 m_xBtnCriteria
->connect_toggled( Link
<weld::ToggleButton
&,void>() );
224 m_xBtnPrintArea
->connect_toggled( Link
<weld::ToggleButton
&,void>() );
225 m_xBtnColHeader
->connect_toggled( Link
<weld::ToggleButton
&,void>() );
226 m_xBtnRowHeader
->connect_toggled( Link
<weld::ToggleButton
&,void>() );
228 m_xBtnCriteria
->set_active( pData
->HasType( ScRangeData::Type::Criteria
) );
229 m_xBtnPrintArea
->set_active( pData
->HasType( ScRangeData::Type::PrintArea
) );
230 m_xBtnColHeader
->set_active( pData
->HasType( ScRangeData::Type::ColHeader
) );
231 m_xBtnRowHeader
->set_active( pData
->HasType( ScRangeData::Type::RowHeader
) );
233 // Restore handlers so user input is processed again
234 Link
<weld::ToggleButton
&,void> aToggleHandler
= LINK( this, ScNameDlg
, EdModifyCheckBoxHdl
);
235 m_xBtnCriteria
->connect_toggled( aToggleHandler
);
236 m_xBtnPrintArea
->connect_toggled( aToggleHandler
);
237 m_xBtnColHeader
->connect_toggled( aToggleHandler
);
238 m_xBtnRowHeader
->connect_toggled( aToggleHandler
);
241 bool ScNameDlg::IsNameValid()
243 OUString aScope
= m_xLbScope
->get_active_text();
244 OUString aName
= m_xEdName
->get_text();
245 aName
= aName
.trim();
250 ScRangeName
* pRangeName
= GetRangeName( aScope
);
252 if (ScRangeData::IsNameValid( aName
, mrDoc
) != ScRangeData::NAME_VALID
)
254 m_xFtInfo
->set_label_type(weld::LabelType::Error
);
255 m_xFtInfo
->set_label(maErrInvalidNameStr
);
258 else if (pRangeName
&& pRangeName
->findByUpperName(ScGlobal::getCharClassPtr()->uppercase(aName
)))
260 m_xFtInfo
->set_label_type(weld::LabelType::Error
);
261 m_xFtInfo
->set_label(maErrNameInUse
);
264 m_xFtInfo
->set_label( maStrInfoDefault
);
268 bool ScNameDlg::IsFormulaValid()
270 ScCompiler
aComp(mrDoc
, maCursorPos
, mrDoc
.GetGrammar());
271 std::unique_ptr
<ScTokenArray
> pCode
= aComp
.CompileString(m_xEdAssign
->GetText());
272 if (pCode
->GetCodeError() != FormulaError::NONE
)
274 m_xFtInfo
->set_label_type(weld::LabelType::Error
);
283 ScRangeName
* ScNameDlg::GetRangeName(const OUString
& rScope
)
285 if (rScope
== maGlobalNameStr
)
286 return m_RangeMap
.find(OUString(STR_GLOBAL_RANGE_NAME
))->second
.get();
288 return m_RangeMap
.find(rScope
)->second
.get();
291 void ScNameDlg::ShowOptions(const ScRangeNameLine
& rLine
)
293 ScRangeName
* pRangeName
= GetRangeName(rLine
.aScope
);
294 ScRangeData
* pData
= pRangeName
->findByUpperName(ScGlobal::getCharClassPtr()->uppercase(rLine
.aName
));
301 void ScNameDlg::AddPushed()
303 mbCloseWithoutUndo
= true;
304 ScTabViewShell
* pViewSh
= ScTabViewShell::GetActiveViewShell();
305 pViewSh
->SwitchBetweenRefDialogs(this);
308 void ScNameDlg::SetEntry(const OUString
& rName
, const OUString
& rScope
)
310 if (!rName
.isEmpty())
312 mbDataChanged
= true;
313 ScRangeNameLine aLine
;
315 aLine
.aScope
= rScope
;
316 m_xRangeManagerTable
->SetEntry(aLine
);
320 void ScNameDlg::RemovePushed()
322 std::vector
<ScRangeNameLine
> aEntries
= m_xRangeManagerTable
->GetSelectedEntries();
323 m_xRangeManagerTable
->DeleteSelectedEntries();
324 for (const auto& rEntry
: aEntries
)
326 ScRangeName
* pRangeName
= GetRangeName(rEntry
.aScope
);
327 ScRangeData
* pData
= pRangeName
->findByUpperName(ScGlobal::getCharClassPtr()->uppercase(rEntry
.aName
));
328 OSL_ENSURE(pData
, "table and model should be in sync");
329 // be safe and check for possible problems
331 pRangeName
->erase(*pData
);
333 mbDataChanged
= true;
335 CheckForEmptyTable();
338 void ScNameDlg::NameModified()
340 ScRangeNameLine aLine
;
341 m_xRangeManagerTable
->GetCurrentLine(aLine
);
342 OUString aOldName
= aLine
.aName
;
343 OUString aNewName
= m_xEdName
->get_text();
344 aNewName
= aNewName
.trim();
345 m_xFtInfo
->set_label_type(weld::LabelType::Normal
);
346 if (aNewName
!= aOldName
)
353 m_xFtInfo
->set_label( maStrInfoDefault
);
356 if (!IsFormulaValid())
358 //TODO: implement an info text
362 OUString aOldScope
= aLine
.aScope
;
364 if (aOldScope
.isEmpty())
366 OUString aExpr
= m_xEdAssign
->GetText();
367 OUString aNewScope
= m_xLbScope
->get_active_text();
369 ScRangeName
* pOldRangeName
= GetRangeName( aOldScope
);
370 ScRangeData
* pData
= pOldRangeName
->findByUpperName( ScGlobal::getCharClassPtr()->uppercase(aOldName
) );
371 ScRangeName
* pNewRangeName
= GetRangeName( aNewScope
);
372 OSL_ENSURE(pData
, "model and table should be in sync");
373 // be safe and check for range data
377 // Assign new index (0) only if the scope is changed, else keep the
379 sal_uInt16 nIndex
= (aNewScope
!= aOldScope
? 0 : pData
->GetIndex());
381 pOldRangeName
->erase(*pData
);
382 m_xRangeManagerTable
->BlockUpdate();
383 m_xRangeManagerTable
->DeleteSelectedEntries();
384 ScRangeData::Type nType
= ScRangeData::Type::Name
;
385 if ( m_xBtnRowHeader
->get_active() ) nType
|= ScRangeData::Type::RowHeader
;
386 if ( m_xBtnColHeader
->get_active() ) nType
|= ScRangeData::Type::ColHeader
;
387 if ( m_xBtnPrintArea
->get_active() ) nType
|= ScRangeData::Type::PrintArea
;
388 if ( m_xBtnCriteria
->get_active() ) nType
|= ScRangeData::Type::Criteria
;
390 ScRangeData
* pNewEntry
= new ScRangeData( mrDoc
, aNewName
, aExpr
,
392 pNewEntry
->SetIndex( nIndex
);
393 pNewRangeName
->insert(pNewEntry
, false /*bReuseFreeIndex*/);
394 aLine
.aName
= aNewName
;
395 aLine
.aExpression
= aExpr
;
396 aLine
.aScope
= aNewScope
;
397 m_xRangeManagerTable
->addEntry(aLine
, true);
398 // tdf#128137 process pending async row change events while UpdatesBlocked in place
399 Application::Reschedule(true);
400 m_xRangeManagerTable
->UnblockUpdate();
401 mbDataChanged
= true;
404 void ScNameDlg::SelectionChanged()
406 //don't update if we have just modified due to user input
407 if (m_xRangeManagerTable
->UpdatesBlocked())
412 if (m_xRangeManagerTable
->IsMultiSelection())
414 m_xEdName
->set_text(maStrMultiSelect
);
415 m_xEdAssign
->SetText(maStrMultiSelect
);
417 m_xEdName
->set_sensitive(false);
418 m_xEdAssign
->GetWidget()->set_sensitive(false);
419 m_xRbAssign
->GetWidget()->set_sensitive(false);
420 m_xLbScope
->set_sensitive(false);
421 m_xBtnRowHeader
->set_sensitive(false);
422 m_xBtnColHeader
->set_sensitive(false);
423 m_xBtnPrintArea
->set_sensitive(false);
424 m_xBtnCriteria
->set_sensitive(false);
428 ScRangeNameLine aLine
;
429 m_xRangeManagerTable
->GetCurrentLine(aLine
);
430 m_xEdAssign
->SetText(aLine
.aExpression
);
431 m_xEdName
->set_text(aLine
.aName
);
432 m_xLbScope
->set_active_text(aLine
.aScope
);
434 m_xBtnDelete
->set_sensitive(true);
435 m_xEdName
->set_sensitive(true);
436 m_xEdAssign
->GetWidget()->set_sensitive(true);
437 m_xRbAssign
->GetWidget()->set_sensitive(true);
438 m_xLbScope
->set_sensitive(true);
439 m_xBtnRowHeader
->set_sensitive(true);
440 m_xBtnColHeader
->set_sensitive(true);
441 m_xBtnPrintArea
->set_sensitive(true);
442 m_xBtnCriteria
->set_sensitive(true);
446 void ScNameDlg::ScopeChanged()
451 void ScNameDlg::GetRangeNames(std::map
<OUString
, std::unique_ptr
<ScRangeName
>>& rRangeMap
)
453 m_RangeMap
.swap(rRangeMap
);
456 IMPL_LINK_NOARG(ScNameDlg
, OkBtnHdl
, weld::Button
&, void)
461 IMPL_LINK_NOARG(ScNameDlg
, CancelBtnHdl
, weld::Button
&, void)
463 mbCloseWithoutUndo
= true;
464 response(RET_CANCEL
);
467 IMPL_LINK_NOARG(ScNameDlg
, AddBtnHdl
, weld::Button
&, void)
472 IMPL_LINK_NOARG(ScNameDlg
, RemoveBtnHdl
, weld::Button
&, void)
477 IMPL_LINK_NOARG(ScNameDlg
, EdModifyCheckBoxHdl
, weld::ToggleButton
&, void)
482 IMPL_LINK_NOARG(ScNameDlg
, EdModifyHdl
, weld::Entry
&, void)
487 IMPL_LINK_NOARG(ScNameDlg
, RefEdModifyHdl
, formula::RefEdit
&, void)
492 IMPL_LINK_NOARG(ScNameDlg
, AssignGetFocusHdl
, formula::RefEdit
&, void)
494 RefEdModifyHdl(*m_xEdAssign
);
497 IMPL_LINK_NOARG(ScNameDlg
, SelectionChangedHdl_Impl
, weld::TreeView
&, void)
502 IMPL_LINK_NOARG(ScNameDlg
, ScopeChangedHdl
, weld::ComboBox
&, void)
507 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */