1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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/.
8  */
9 
10 #include <namedefdlg.hxx>
11 
12 #include <vcl/settings.hxx>
13 #include <formula/errorcodes.hxx>
14 #include <sfx2/app.hxx>
15 #include <unotools/charclass.hxx>
16 
17 #include <compiler.hxx>
18 #include <document.hxx>
19 #include <globstr.hrc>
20 #include <scresid.hxx>
21 #include <globalnames.hxx>
22 #include <rangenam.hxx>
23 #include <reffact.hxx>
24 #include <undorangename.hxx>
25 #include <tabvwsh.hxx>
26 #include <tokenarray.hxx>
27 
ScNameDefDlg(SfxBindings * pB,SfxChildWindow * pCW,weld::Window * pParent,const ScViewData * pViewData,const std::map<OUString,ScRangeName * > & aRangeMap,const ScAddress & aCursorPos,const bool bUndo)28 ScNameDefDlg::ScNameDefDlg( SfxBindings* pB, SfxChildWindow* pCW, weld::Window* pParent,
29         const ScViewData* pViewData, const std::map<OUString, ScRangeName*>& aRangeMap,
30         const ScAddress& aCursorPos, const bool bUndo )
31     : ScAnyRefDlgController( pB, pCW, pParent, "modules/scalc/ui/definename.ui", "DefineNameDialog")
32     , mbUndo( bUndo )
33     , mpDoc( pViewData->GetDocument() )
34     , mpDocShell ( pViewData->GetDocShell() )
35     , maCursorPos( aCursorPos )
36     , maGlobalNameStr  ( ScResId(STR_GLOBAL_SCOPE) )
37     , maErrInvalidNameStr( ScResId(STR_ERR_NAME_INVALID))
38     , maErrInvalidNameCellRefStr( ScResId(STR_ERR_NAME_INVALID_CELL_REF))
39     , maErrNameInUse   ( ScResId(STR_ERR_NAME_EXISTS))
40     , maRangeMap( aRangeMap )
41     , m_xEdName(m_xBuilder->weld_entry("edit"))
42     , m_xEdRange(new formula::RefEdit(m_xBuilder->weld_entry("range")))
43     , m_xRbRange(new formula::RefButton(m_xBuilder->weld_button("refbutton")))
44     , m_xLbScope(m_xBuilder->weld_combo_box("scope"))
45     , m_xBtnRowHeader(m_xBuilder->weld_check_button("rowheader"))
46     , m_xBtnColHeader(m_xBuilder->weld_check_button("colheader"))
47     , m_xBtnPrintArea(m_xBuilder->weld_check_button("printarea"))
48     , m_xBtnCriteria(m_xBuilder->weld_check_button("filter"))
49     , m_xBtnAdd(m_xBuilder->weld_button("add"))
50     , m_xBtnCancel(m_xBuilder->weld_button("cancel"))
51     , m_xFtInfo(m_xBuilder->weld_label("label"))
52     , m_xExpander(m_xBuilder->weld_expander("more"))
53     , m_xFtRange(m_xBuilder->weld_label("label3"))
54 {
55     m_xEdRange->SetReferences(this, m_xFtRange.get());
56     m_xRbRange->SetReferences(this, m_xEdRange.get());
57     maStrInfoDefault = m_xFtInfo->get_label();
58 
59     // Initialize scope list.
60     m_xLbScope->append_text(maGlobalNameStr);
61     m_xLbScope->set_active(0);
62     SCTAB n = mpDoc->GetTableCount();
63     for (SCTAB i = 0; i < n; ++i)
64     {
65         OUString aTabName;
66         mpDoc->GetName(i, aTabName);
67         m_xLbScope->append_text(aTabName);
68     }
69 
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 
75     m_xBtnAdd->set_sensitive(false); // empty name is invalid
76 
77     ScRange aRange;
78 
79     pViewData->GetSimpleArea( aRange );
80     OUString aAreaStr(aRange.Format(ScRefFlags::RANGE_ABS_3D, mpDoc,
81             ScAddress::Details(mpDoc->GetAddressConvention(), 0, 0)));
82 
83     m_xEdRange->SetText( aAreaStr );
84 
85     m_xEdName->grab_focus();
86     m_xEdName->select_region(0, -1);
87 }
88 
~ScNameDefDlg()89 ScNameDefDlg::~ScNameDefDlg()
90 {
91 }
92 
CancelPushed()93 void ScNameDefDlg::CancelPushed()
94 {
95     if (mbUndo)
96         response(RET_CANCEL);
97     else
98     {
99         ScTabViewShell* pViewSh = ScTabViewShell::GetActiveViewShell();
100         pViewSh->SwitchBetweenRefDialogs(this);
101     }
102 }
103 
IsFormulaValid()104 bool ScNameDefDlg::IsFormulaValid()
105 {
106     ScCompiler aComp( mpDoc, maCursorPos, mpDoc->GetGrammar());
107     std::unique_ptr<ScTokenArray> pCode = aComp.CompileString(m_xEdRange->GetText());
108     if (pCode->GetCodeError() != FormulaError::NONE)
109     {
110         //TODO: info message
111         return false;
112     }
113     else
114     {
115         return true;
116     }
117 }
118 
IsNameValid()119 bool ScNameDefDlg::IsNameValid()
120 {
121     OUString aScope = m_xLbScope->get_active_text();
122     OUString aName = m_xEdName->get_text();
123 
124     ScRangeName* pRangeName = nullptr;
125     if(aScope == maGlobalNameStr)
126     {
127         pRangeName = maRangeMap.find(OUString(STR_GLOBAL_RANGE_NAME))->second;
128     }
129     else
130     {
131         pRangeName = maRangeMap.find(aScope)->second;
132     }
133 
134     ScRangeData::IsNameValidType eType;
135     m_xFtInfo->set_message_type(weld::EntryMessageType::Normal);
136     if ( aName.isEmpty() )
137     {
138         m_xBtnAdd->set_sensitive(false);
139         m_xFtInfo->set_label(maStrInfoDefault);
140         return false;
141     }
142     else if ((eType = ScRangeData::IsNameValid( aName, mpDoc )) != ScRangeData::NAME_VALID)
143     {
144         m_xFtInfo->set_message_type(weld::EntryMessageType::Error);
145         if (eType == ScRangeData::NAME_INVALID_BAD_STRING)
146         {
147             m_xFtInfo->set_label(maErrInvalidNameStr);
148         }
149         else if (eType == ScRangeData::NAME_INVALID_CELL_REF)
150         {
151             m_xFtInfo->set_label(maErrInvalidNameCellRefStr);
152         }
153         m_xBtnAdd->set_sensitive(false);
154         return false;
155     }
156     else if (pRangeName->findByUpperName(ScGlobal::pCharClass->uppercase(aName)))
157     {
158         m_xFtInfo->set_message_type(weld::EntryMessageType::Error);
159         m_xFtInfo->set_label(maErrNameInUse);
160         m_xBtnAdd->set_sensitive(false);
161         return false;
162     }
163 
164     if (!IsFormulaValid())
165     {
166         m_xFtInfo->set_message_type(weld::EntryMessageType::Error);
167         m_xBtnAdd->set_sensitive(false);
168         return false;
169     }
170 
171     m_xFtInfo->set_label(maStrInfoDefault);
172     m_xBtnAdd->set_sensitive(true);
173     return true;
174 }
175 
AddPushed()176 void ScNameDefDlg::AddPushed()
177 {
178     OUString aScope = m_xLbScope->get_active_text();
179     OUString aName = m_xEdName->get_text();
180     OUString aExpression = m_xEdRange->GetText();
181 
182     if (aName.isEmpty())
183     {
184         return;
185     }
186     if (aScope.isEmpty())
187     {
188         return;
189     }
190 
191     ScRangeName* pRangeName = nullptr;
192     if(aScope == maGlobalNameStr)
193     {
194         pRangeName = maRangeMap.find(OUString(STR_GLOBAL_RANGE_NAME))->second;
195     }
196     else
197     {
198         pRangeName = maRangeMap.find(aScope)->second;
199     }
200     if (!pRangeName)
201         return;
202 
203     if (!IsNameValid()) //should not happen, but make sure we don't break anything
204         return;
205     else
206     {
207         if ( mpDoc )
208         {
209             ScRangeData::Type nType = ScRangeData::Type::Name;
210 
211             ScRangeData* pNewEntry = new ScRangeData( mpDoc,
212                     aName,
213                     aExpression,
214                     maCursorPos,
215                     nType );
216 
217             if ( m_xBtnRowHeader->get_active() ) nType |= ScRangeData::Type::RowHeader;
218             if ( m_xBtnColHeader->get_active() ) nType |= ScRangeData::Type::ColHeader;
219             if ( m_xBtnPrintArea->get_active() ) nType |= ScRangeData::Type::PrintArea;
220             if ( m_xBtnCriteria->get_active()  ) nType |= ScRangeData::Type::Criteria;
221 
222             pNewEntry->AddType(nType);
223 
224             // aExpression valid?
225             if ( FormulaError::NONE == pNewEntry->GetErrCode() )
226             {
227                 if ( !pRangeName->insert( pNewEntry, false /*bReuseFreeIndex*/ ) )
228                     pNewEntry = nullptr;
229 
230                 if (mbUndo)
231                 {
232                     // this means we called directly through the menu
233 
234                     SCTAB nTab;
235                     // if no table with that name is found, assume global range name
236                     if (!mpDoc->GetTable(aScope, nTab))
237                         nTab = -1;
238 
239                     assert( pNewEntry);     // undo of no insertion smells fishy
240                     if (pNewEntry)
241                         mpDocShell->GetUndoManager()->AddUndoAction(
242                                 std::make_unique<ScUndoAddRangeData>( mpDocShell, pNewEntry, nTab) );
243 
244                     // set table stream invalid, otherwise RangeName won't be saved if no other
245                     // call invalidates the stream
246                     if (nTab != -1)
247                         mpDoc->SetStreamValid(nTab, false);
248                     SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScAreasChanged ) );
249                     mpDocShell->SetDocumentModified();
250                     Close();
251                 }
252                 else
253                 {
254                     maName = aName;
255                     maScope = aScope;
256                     ScTabViewShell* pViewSh = ScTabViewShell::GetActiveViewShell();
257                     pViewSh->SwitchBetweenRefDialogs(this);
258                 }
259             }
260             else
261             {
262                 delete pNewEntry;
263                 m_xEdRange->GrabFocus();
264                 m_xEdRange->SelectAll();
265             }
266         }
267     }
268 }
269 
GetNewData(OUString & rName,OUString & rScope)270 void ScNameDefDlg::GetNewData(OUString& rName, OUString& rScope)
271 {
272     rName = maName;
273     rScope = maScope;
274 }
275 
IsRefInputMode() const276 bool ScNameDefDlg::IsRefInputMode() const
277 {
278     return m_xEdRange->GetWidget()->get_sensitive();
279 }
280 
RefInputDone(bool bForced)281 void ScNameDefDlg::RefInputDone( bool bForced)
282 {
283     ScAnyRefDlgController::RefInputDone(bForced);
284     IsNameValid();
285 }
286 
SetReference(const ScRange & rRef,ScDocument * pDocP)287 void ScNameDefDlg::SetReference( const ScRange& rRef, ScDocument* pDocP )
288 {
289     if (m_xEdRange->GetWidget()->get_sensitive())
290     {
291         if ( rRef.aStart != rRef.aEnd )
292             RefInputStart(m_xEdRange.get());
293         OUString aRefStr(rRef.Format(ScRefFlags::RANGE_ABS_3D, pDocP,
294                 ScAddress::Details(pDocP->GetAddressConvention(), 0, 0)));
295         m_xEdRange->SetRefString( aRefStr );
296     }
297 }
298 
Close()299 void ScNameDefDlg::Close()
300 {
301     DoClose( ScNameDefDlgWrapper::GetChildWindowId() );
302 }
303 
SetActive()304 void ScNameDefDlg::SetActive()
305 {
306     m_xEdRange->GrabFocus();
307     RefInputDone();
308 }
309 
IMPL_LINK_NOARG(ScNameDefDlg,CancelBtnHdl,weld::Button &,void)310 IMPL_LINK_NOARG(ScNameDefDlg, CancelBtnHdl, weld::Button&, void)
311 {
312     CancelPushed();
313 }
314 
IMPL_LINK_NOARG(ScNameDefDlg,AddBtnHdl,weld::Button &,void)315 IMPL_LINK_NOARG(ScNameDefDlg, AddBtnHdl, weld::Button&, void)
316 {
317     AddPushed();
318 };
319 
IMPL_LINK_NOARG(ScNameDefDlg,NameModifyHdl,weld::Entry &,void)320 IMPL_LINK_NOARG(ScNameDefDlg, NameModifyHdl, weld::Entry&, void)
321 {
322     IsNameValid();
323 }
324 
IMPL_LINK_NOARG(ScNameDefDlg,AssignGetFocusHdl,formula::RefEdit &,void)325 IMPL_LINK_NOARG(ScNameDefDlg, AssignGetFocusHdl, formula::RefEdit&, void)
326 {
327     IsNameValid();
328 }
329 
330 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
331