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/svapp.hxx>
36 #include <formula/errorcodes.hxx>
37 #include <unotools/charclass.hxx>
43 ScNameDlg::ScNameDlg( SfxBindings
* pB
, SfxChildWindow
* pCW
, weld::Window
* pParent
,
44 ScViewData
& rViewData
,
45 const ScAddress
& aCursorPos
,
46 std::map
<OUString
, ScRangeName
> *const pRangeMap
)
47 : ScAnyRefDlgController(pB
, pCW
, pParent
, u
"modules/scalc/ui/managenamesdialog.ui"_ustr
,
48 u
"ManageNamesDialog"_ustr
)
50 , maGlobalNameStr(ScResId(STR_GLOBAL_SCOPE
))
51 , maErrInvalidNameStr(ScResId(STR_ERR_NAME_INVALID
))
52 , maErrNameInUse(ScResId(STR_ERR_NAME_EXISTS
))
53 , maErrInvalidSheetReference(ScResId(STR_INVALID_TABREF_PRINT_AREA
))
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(u
"name"_ustr
))
63 , m_xFtAssign(m_xBuilder
->weld_label(u
"label3"_ustr
))
64 , m_xEdAssign(new formula::RefEdit(m_xBuilder
->weld_entry(u
"range"_ustr
)))
65 , m_xRbAssign(new formula::RefButton(m_xBuilder
->weld_button(u
"assign"_ustr
)))
66 , m_xLbScope(m_xBuilder
->weld_combo_box(u
"scope"_ustr
))
67 , m_xBtnPrintArea(m_xBuilder
->weld_check_button(u
"printrange"_ustr
))
68 , m_xBtnColHeader(m_xBuilder
->weld_check_button(u
"colheader"_ustr
))
69 , m_xBtnCriteria(m_xBuilder
->weld_check_button(u
"filter"_ustr
))
70 , m_xBtnRowHeader(m_xBuilder
->weld_check_button(u
"rowheader"_ustr
))
71 , m_xBtnAdd(m_xBuilder
->weld_button(u
"add"_ustr
))
72 , m_xBtnDelete(m_xBuilder
->weld_button(u
"delete"_ustr
))
73 , m_xBtnOk(m_xBuilder
->weld_button(u
"ok"_ustr
))
74 , m_xBtnCancel(m_xBuilder
->weld_button(u
"cancel"_ustr
))
75 , m_xFtInfo(m_xBuilder
->weld_label(u
"info"_ustr
))
76 , m_xExpander(m_xBuilder
->weld_expander(u
"more"_ustr
))
78 m_xEdAssign
->SetReferences(this, m_xFtAssign
.get());
79 m_xRbAssign
->SetReferences(this, m_xEdAssign
.get());
80 maStrInfoDefault
= m_xFtInfo
->get_label();
84 std::map
<OUString
, ScRangeName
*> aRangeMap
;
85 mrDoc
.GetRangeNameMap(aRangeMap
);
86 for (const auto& [aTemp
, pRangeName
] : aRangeMap
)
88 m_RangeMap
.insert(std::make_pair(aTemp
, *pRangeName
));
93 m_RangeMap
.swap(*pRangeMap
);
98 ScNameDlg::~ScNameDlg()
102 void ScNameDlg::Init()
106 std::unique_ptr
<weld::TreeView
> xTreeView(m_xBuilder
->weld_tree_view(u
"names"_ustr
));
107 xTreeView
->set_size_request(xTreeView
->get_approximate_digit_width() * 75,
108 xTreeView
->get_height_rows(10));
109 m_xRangeManagerTable
.reset(new ScRangeManagerTable(std::move(xTreeView
), m_RangeMap
, maCursorPos
));
111 m_xRangeManagerTable
->connect_changed( LINK( this, ScNameDlg
, SelectionChangedHdl_Impl
) );
113 m_xBtnOk
->connect_clicked( LINK( this, ScNameDlg
, OkBtnHdl
) );
114 m_xBtnCancel
->connect_clicked( LINK( this, ScNameDlg
, CancelBtnHdl
) );
115 m_xBtnAdd
->connect_clicked( LINK( this, ScNameDlg
, AddBtnHdl
) );
116 m_xEdAssign
->SetGetFocusHdl( LINK( this, ScNameDlg
, AssignGetFocusHdl
) );
117 m_xEdAssign
->SetModifyHdl ( LINK( this, ScNameDlg
, RefEdModifyHdl
) );
118 m_xEdName
->connect_changed( LINK( this, ScNameDlg
, EdModifyHdl
) );
119 m_xLbScope
->connect_changed( LINK(this, ScNameDlg
, ScopeChangedHdl
) );
120 m_xBtnDelete
->connect_clicked( LINK( this, ScNameDlg
, RemoveBtnHdl
) );
121 m_xBtnPrintArea
->connect_toggled( LINK(this, ScNameDlg
, EdModifyCheckBoxHdl
) );
122 m_xBtnCriteria
->connect_toggled( LINK(this, ScNameDlg
, EdModifyCheckBoxHdl
) );
123 m_xBtnRowHeader
->connect_toggled( LINK(this, ScNameDlg
, EdModifyCheckBoxHdl
) );
124 m_xBtnColHeader
->connect_toggled( LINK(this, ScNameDlg
, EdModifyCheckBoxHdl
) );
126 // Initialize scope list.
127 m_xLbScope
->append_text(maGlobalNameStr
);
128 m_xLbScope
->set_active(0);
129 SCTAB n
= mrDoc
.GetTableCount();
130 for (SCTAB i
= 0; i
< n
; ++i
)
133 mrDoc
.GetName(i
, aTabName
);
134 m_xLbScope
->append_text(aTabName
);
137 CheckForEmptyTable();
139 if (m_xRangeManagerTable
->n_children())
141 m_xRangeManagerTable
->set_cursor(0);
142 m_xRangeManagerTable
->CheckForFormulaString();
148 bool ScNameDlg::IsRefInputMode() const
150 return m_xEdAssign
->GetWidget()->get_sensitive();
153 void ScNameDlg::RefInputDone( bool bForced
)
155 ScAnyRefDlgController::RefInputDone(bForced
);
156 RefEdModifyHdl(*m_xEdAssign
);
159 void ScNameDlg::SetReference( const ScRange
& rRef
, ScDocument
& rDocP
)
161 if (m_xEdAssign
->GetWidget()->get_sensitive())
163 if ( rRef
.aStart
!= rRef
.aEnd
)
164 RefInputStart(m_xEdAssign
.get());
165 OUString
aRefStr(rRef
.Format(rDocP
, ScRefFlags::RANGE_ABS_3D
,
166 ScAddress::Details(rDocP
.GetAddressConvention(), 0, 0)));
167 m_xEdAssign
->SetRefString( aRefStr
);
171 void ScNameDlg::Close()
173 if (mbDataChanged
&& !mbCloseWithoutUndo
)
174 mrViewData
.GetDocFunc().ModifyAllRangeNames(m_RangeMap
);
175 DoClose(ScNameDlgWrapper::GetChildWindowId());
178 void ScNameDlg::CheckForEmptyTable()
180 if (!m_xRangeManagerTable
->n_children())
182 m_xBtnDelete
->set_sensitive(false);
183 m_xEdAssign
->GetWidget()->set_sensitive(false);
184 m_xRbAssign
->GetWidget()->set_sensitive(false);
185 m_xEdName
->set_sensitive(false);
186 m_xLbScope
->set_sensitive(false);
188 m_xBtnCriteria
->set_sensitive(false);
189 m_xBtnPrintArea
->set_sensitive(false);
190 m_xBtnColHeader
->set_sensitive(false);
191 m_xBtnRowHeader
->set_sensitive(false);
195 m_xBtnDelete
->set_sensitive(true);
196 m_xEdAssign
->GetWidget()->set_sensitive(true);
197 m_xRbAssign
->GetWidget()->set_sensitive(true);
198 m_xEdName
->set_sensitive(true);
199 m_xLbScope
->set_sensitive(true);
201 m_xBtnCriteria
->set_sensitive(true);
202 m_xBtnPrintArea
->set_sensitive(true);
203 m_xBtnColHeader
->set_sensitive(true);
204 m_xBtnRowHeader
->set_sensitive(true);
208 void ScNameDlg::SetActive()
210 m_xEdAssign
->GrabFocus();
214 void ScNameDlg::UpdateChecks(const ScRangeData
* pData
)
216 // remove handlers, we only want the handlers to process
217 // user input and not when we are syncing the controls with our internal
218 // model ( also UpdateChecks is called already from some other event
219 // handlers, triggering handlers while already processing a handler can
220 // ( and does in this case ) corrupt the internal data
222 m_xBtnCriteria
->connect_toggled( Link
<weld::Toggleable
&,void>() );
223 m_xBtnPrintArea
->connect_toggled( Link
<weld::Toggleable
&,void>() );
224 m_xBtnColHeader
->connect_toggled( Link
<weld::Toggleable
&,void>() );
225 m_xBtnRowHeader
->connect_toggled( Link
<weld::Toggleable
&,void>() );
227 m_xBtnCriteria
->set_active( pData
->HasType( ScRangeData::Type::Criteria
) );
228 m_xBtnPrintArea
->set_active( pData
->HasType( ScRangeData::Type::PrintArea
) );
229 m_xBtnColHeader
->set_active( pData
->HasType( ScRangeData::Type::ColHeader
) );
230 m_xBtnRowHeader
->set_active( pData
->HasType( ScRangeData::Type::RowHeader
) );
232 // Restore handlers so user input is processed again
233 Link
<weld::Toggleable
&,void> aToggleHandler
= LINK( this, ScNameDlg
, EdModifyCheckBoxHdl
);
234 m_xBtnCriteria
->connect_toggled( aToggleHandler
);
235 m_xBtnPrintArea
->connect_toggled( aToggleHandler
);
236 m_xBtnColHeader
->connect_toggled( aToggleHandler
);
237 m_xBtnRowHeader
->connect_toggled( aToggleHandler
);
240 bool ScNameDlg::IsNameValid()
242 OUString aScope
= m_xLbScope
->get_active_text();
243 OUString aName
= m_xEdName
->get_text();
244 aName
= aName
.trim();
249 ScRangeName
* pRangeName
= GetRangeName( aScope
);
251 if (ScRangeData::IsNameValid(aName
, mrDoc
) != ScRangeData::IsNameValidType::NAME_VALID
)
253 m_xFtInfo
->set_label_type(weld::LabelType::Error
);
254 m_xFtInfo
->set_label(maErrInvalidNameStr
);
257 else if (pRangeName
&& pRangeName
->findByUpperName(ScGlobal::getCharClass().uppercase(aName
)))
259 m_xFtInfo
->set_label_type(weld::LabelType::Error
);
260 m_xFtInfo
->set_label(maErrNameInUse
);
266 bool ScNameDlg::IsFormulaValid()
268 const OUString aRangeOrFormulaExp
= m_xEdAssign
->GetText();
269 // tdf#140394 - check if formula is a valid print range
270 if (m_xBtnPrintArea
->get_active())
272 const ScRefFlags nValidAddr
= ScRefFlags::VALID
| ScRefFlags::ROW_VALID
| ScRefFlags::COL_VALID
;
273 const ScRefFlags nValidRange
= nValidAddr
| ScRefFlags::ROW2_VALID
| ScRefFlags::COL2_VALID
;
274 const formula::FormulaGrammar::AddressConvention eConv
= mrDoc
.GetAddressConvention();
275 const sal_Unicode sep
= ScCompiler::GetNativeSymbolChar(ocSep
);
279 for (sal_Int32 nIdx
= 0; nIdx
>= 0;)
281 const OUString aOne
= aRangeOrFormulaExp
.getToken(0, sep
, nIdx
);
282 ScRefFlags nResult
= aRange
.Parse(aOne
, mrDoc
, eConv
);
283 if ((nResult
& nValidRange
) != nValidRange
)
285 ScRefFlags nAddrResult
= aAddr
.Parse(aOne
, mrDoc
, eConv
);
286 if ((nAddrResult
& nValidAddr
) != nValidAddr
)
288 m_xFtInfo
->set_label_type(weld::LabelType::Error
);
289 m_xFtInfo
->set_label(maErrInvalidSheetReference
);
297 ScCompiler
aComp(mrDoc
, maCursorPos
, mrDoc
.GetGrammar());
298 std::unique_ptr
<ScTokenArray
> pCode
= aComp
.CompileString(aRangeOrFormulaExp
);
299 if (pCode
->GetCodeError() != FormulaError::NONE
)
301 m_xFtInfo
->set_label_type(weld::LabelType::Error
);
302 //TODO: implement an info text
310 ScRangeName
* ScNameDlg::GetRangeName(const OUString
& rScope
)
312 if (rScope
== maGlobalNameStr
)
314 const auto iter
= m_RangeMap
.find(STR_GLOBAL_RANGE_NAME
);
315 assert(iter
!= m_RangeMap
.end());
316 return &iter
->second
;
320 const auto iter
= m_RangeMap
.find(rScope
);
321 assert(iter
!= m_RangeMap
.end());
322 return &iter
->second
;
326 void ScNameDlg::ShowOptions(const ScRangeNameLine
& rLine
)
328 ScRangeName
* pRangeName
= GetRangeName(rLine
.aScope
);
329 ScRangeData
* pData
= pRangeName
->findByUpperName(ScGlobal::getCharClass().uppercase(rLine
.aName
));
336 void ScNameDlg::AddPushed()
338 mbCloseWithoutUndo
= true;
339 if (ScTabViewShell
* pViewSh
= ScTabViewShell::GetActiveViewShell())
340 pViewSh
->SwitchBetweenRefDialogs(this);
343 void ScNameDlg::SetEntry(const OUString
& rName
, const OUString
& rScope
)
345 if (!rName
.isEmpty())
347 mbDataChanged
= true;
348 ScRangeNameLine aLine
;
350 aLine
.aScope
= rScope
;
351 m_xRangeManagerTable
->SetEntry(aLine
);
355 void ScNameDlg::RemovePushed()
357 std::vector
<ScRangeNameLine
> aEntries
= m_xRangeManagerTable
->GetSelectedEntries();
358 m_xRangeManagerTable
->DeleteSelectedEntries();
359 for (const auto& rEntry
: aEntries
)
361 ScRangeName
* pRangeName
= GetRangeName(rEntry
.aScope
);
362 ScRangeData
* pData
= pRangeName
->findByUpperName(ScGlobal::getCharClass().uppercase(rEntry
.aName
));
363 OSL_ENSURE(pData
, "table and model should be in sync");
364 // be safe and check for possible problems
366 pRangeName
->erase(*pData
);
368 mbDataChanged
= true;
370 CheckForEmptyTable();
373 void ScNameDlg::NameModified()
375 ScRangeNameLine aLine
;
376 m_xRangeManagerTable
->GetCurrentLine(aLine
);
377 OUString aOldName
= aLine
.aName
;
378 OUString aNewName
= m_xEdName
->get_text();
379 aNewName
= aNewName
.trim();
380 m_xBtnOk
->set_sensitive(false);
381 if (aNewName
!= aOldName
)
387 if (!IsFormulaValid())
390 m_xFtInfo
->set_label_type(weld::LabelType::Normal
);
391 m_xFtInfo
->set_label(maStrInfoDefault
);
392 m_xBtnOk
->set_sensitive(true);
394 OUString aOldScope
= aLine
.aScope
;
396 if (aOldScope
.isEmpty())
398 OUString aExpr
= m_xEdAssign
->GetText();
399 OUString aNewScope
= m_xLbScope
->get_active_text();
401 ScRangeName
* pOldRangeName
= GetRangeName( aOldScope
);
402 ScRangeData
* pData
= pOldRangeName
->findByUpperName( ScGlobal::getCharClass().uppercase(aOldName
) );
403 ScRangeName
* pNewRangeName
= GetRangeName( aNewScope
);
404 OSL_ENSURE(pData
, "model and table should be in sync");
405 // be safe and check for range data
409 // Assign new index (0) only if the scope is changed, else keep the
411 sal_uInt16 nIndex
= (aNewScope
!= aOldScope
? 0 : pData
->GetIndex());
413 pOldRangeName
->erase(*pData
);
414 m_xRangeManagerTable
->BlockUpdate();
415 m_xRangeManagerTable
->DeleteSelectedEntries();
416 ScRangeData::Type nType
= ScRangeData::Type::Name
;
417 if ( m_xBtnRowHeader
->get_active() ) nType
|= ScRangeData::Type::RowHeader
;
418 if ( m_xBtnColHeader
->get_active() ) nType
|= ScRangeData::Type::ColHeader
;
419 if ( m_xBtnPrintArea
->get_active() ) nType
|= ScRangeData::Type::PrintArea
;
420 if ( m_xBtnCriteria
->get_active() ) nType
|= ScRangeData::Type::Criteria
;
422 ScRangeData
* pNewEntry
= new ScRangeData( mrDoc
, aNewName
, aExpr
,
424 pNewEntry
->SetIndex( nIndex
);
425 pNewRangeName
->insert(pNewEntry
, false /*bReuseFreeIndex*/);
426 aLine
.aName
= aNewName
;
427 aLine
.aExpression
= aExpr
;
428 aLine
.aScope
= aNewScope
;
429 m_xRangeManagerTable
->addEntry(aLine
, true);
430 // tdf#128137 process pending async row change events while UpdatesBlocked in place
431 Application::Reschedule(true);
432 m_xRangeManagerTable
->UnblockUpdate();
433 mbDataChanged
= true;
436 void ScNameDlg::SelectionChanged()
438 //don't update if we have just modified due to user input
439 if (m_xRangeManagerTable
->UpdatesBlocked())
444 if (m_xRangeManagerTable
->IsMultiSelection())
446 m_xEdName
->set_text(maStrMultiSelect
);
447 m_xEdAssign
->SetText(maStrMultiSelect
);
449 m_xEdName
->set_sensitive(false);
450 m_xEdAssign
->GetWidget()->set_sensitive(false);
451 m_xRbAssign
->GetWidget()->set_sensitive(false);
452 m_xLbScope
->set_sensitive(false);
453 m_xBtnRowHeader
->set_sensitive(false);
454 m_xBtnColHeader
->set_sensitive(false);
455 m_xBtnPrintArea
->set_sensitive(false);
456 m_xBtnCriteria
->set_sensitive(false);
460 ScRangeNameLine aLine
;
461 m_xRangeManagerTable
->GetCurrentLine(aLine
);
462 m_xEdAssign
->SetText(aLine
.aExpression
);
463 m_xEdName
->set_text(aLine
.aName
);
464 m_xLbScope
->set_active_text(aLine
.aScope
);
466 m_xBtnDelete
->set_sensitive(true);
467 m_xEdName
->set_sensitive(true);
468 m_xEdAssign
->GetWidget()->set_sensitive(true);
469 m_xRbAssign
->GetWidget()->set_sensitive(true);
470 m_xLbScope
->set_sensitive(true);
471 m_xBtnRowHeader
->set_sensitive(true);
472 m_xBtnColHeader
->set_sensitive(true);
473 m_xBtnPrintArea
->set_sensitive(true);
474 m_xBtnCriteria
->set_sensitive(true);
478 void ScNameDlg::ScopeChanged()
483 void ScNameDlg::GetRangeNames(std::map
<OUString
, ScRangeName
>& rRangeMap
)
485 m_RangeMap
.swap(rRangeMap
);
488 IMPL_LINK_NOARG(ScNameDlg
, OkBtnHdl
, weld::Button
&, void)
493 IMPL_LINK_NOARG(ScNameDlg
, CancelBtnHdl
, weld::Button
&, void)
495 mbCloseWithoutUndo
= true;
496 response(RET_CANCEL
);
499 IMPL_LINK_NOARG(ScNameDlg
, AddBtnHdl
, weld::Button
&, void)
504 IMPL_LINK_NOARG(ScNameDlg
, RemoveBtnHdl
, weld::Button
&, void)
509 IMPL_LINK_NOARG(ScNameDlg
, EdModifyCheckBoxHdl
, weld::Toggleable
&, void)
514 IMPL_LINK_NOARG(ScNameDlg
, EdModifyHdl
, weld::Entry
&, void)
519 IMPL_LINK_NOARG(ScNameDlg
, RefEdModifyHdl
, formula::RefEdit
&, void)
524 IMPL_LINK_NOARG(ScNameDlg
, AssignGetFocusHdl
, formula::RefEdit
&, void)
526 RefEdModifyHdl(*m_xEdAssign
);
529 IMPL_LINK_NOARG(ScNameDlg
, SelectionChangedHdl_Impl
, weld::TreeView
&, void)
534 IMPL_LINK_NOARG(ScNameDlg
, ScopeChangedHdl
, weld::ComboBox
&, void)
539 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */