LibreOffice Module sc (master) 1
xmlsourcedlg.cxx
Go to the documentation of this file.
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 <xmlsourcedlg.hxx>
11#include <bitmaps.hlst>
12#include <document.hxx>
13#include <orcusfilters.hxx>
14#include <filter.hxx>
15#include <reffact.hxx>
16#include <tabvwsh.hxx>
17
18#include <tools/urlobj.hxx>
20#include <sfx2/objsh.hxx>
21
22#include <com/sun/star/ui/dialogs/XFilePicker3.hpp>
23#include <com/sun/star/ui/dialogs/ExecutableDialogResults.hpp>
24#include <com/sun/star/ui/dialogs/TemplateDescription.hpp>
25
26using namespace com::sun::star;
27
28namespace {
29
30bool isAttribute(const weld::TreeView& rControl, const weld::TreeIter& rEntry)
31{
32 const ScOrcusXMLTreeParam::EntryData* pUserData = ScOrcusXMLTreeParam::getUserData(rControl, rEntry);
33 if (!pUserData)
34 return false;
35
36 return pUserData->meType == ScOrcusXMLTreeParam::Attribute;
37}
38
39OUString getXPath(
40 const weld::TreeView& rTree, const weld::TreeIter& rEntry, std::vector<size_t>& rNamespaces)
41{
42 OUStringBuffer aBuf;
43 std::unique_ptr<weld::TreeIter> xEntry(rTree.make_iterator(&rEntry));
44 do
45 {
46 // Collect used namespace.
48 if (pData)
49 rNamespaces.push_back(pData->mnNamespaceID);
50
51 // element separator is '/' whereas attribute separator is '/@' in xpath.
52 std::u16string_view sSeparator;
53 if (isAttribute(rTree, *xEntry))
54 sSeparator = u"/@";
55 else
56 sSeparator = u"/";
57 aBuf.insert(0, sSeparator + rTree.get_text(*xEntry, 0));
58 }
59 while (rTree.iter_parent(*xEntry));
60
61 return aBuf.makeStringAndClear();
62}
63
64}
65
67 SfxBindings* pB, SfxChildWindow* pCW, weld::Window* pParent, ScDocument* pDoc)
68 : ScAnyRefDlgController(pB, pCW, pParent, "modules/scalc/ui/xmlsourcedialog.ui", "XMLSourceDialog")
69 , mpDoc(pDoc)
70 , mbDlgLostFocus(false)
71 , mxBtnSelectSource(m_xBuilder->weld_button("selectsource"))
72 , mxFtSourceFile(m_xBuilder->weld_label("sourcefile"))
73 , mxMapGrid(m_xBuilder->weld_container("mapgrid"))
74 , mxLbTree(m_xBuilder->weld_tree_view("tree"))
75 , mxRefEdit(new formula::RefEdit(m_xBuilder->weld_entry("edit")))
76 , mxRefBtn(new formula::RefButton(m_xBuilder->weld_button("ref")))
77 , mxBtnOk(m_xBuilder->weld_button("ok"))
78 , mxBtnCancel(m_xBuilder->weld_button("cancel"))
79 , maCustomCompare(*mxLbTree)
80 , maCellLinks(maCustomCompare)
81 , maRangeLinks(maCustomCompare)
82{
83 mxLbTree->set_size_request(mxLbTree->get_approximate_digit_width() * 40,
84 mxLbTree->get_height_rows(15));
85 mxLbTree->set_selection_mode(SelectionMode::Multiple);
86 mxRefEdit->SetReferences(this, nullptr);
87 mxRefBtn->SetReferences(this, mxRefEdit.get());
88
89 mpActiveEdit = mxRefEdit.get();
90
91 maXMLParam.maImgElementDefault = RID_BMP_ELEMENT_DEFAULT;
92 maXMLParam.maImgElementRepeat = RID_BMP_ELEMENT_REPEAT;
93 maXMLParam.maImgAttribute = RID_BMP_ELEMENT_ATTRIBUTE;
94
95 Link<weld::Button&,void> aBtnHdl = LINK(this, ScXMLSourceDlg, BtnPressedHdl);
96 mxBtnSelectSource->connect_clicked(aBtnHdl);
97 mxBtnOk->connect_clicked(aBtnHdl);
98 mxBtnCancel->connect_clicked(aBtnHdl);
99
100 mxLbTree->connect_changed(LINK(this, ScXMLSourceDlg, TreeItemSelectHdl));
101
102 Link<formula::RefEdit&,void> aLink = LINK(this, ScXMLSourceDlg, RefModifiedHdl);
103 mxRefEdit->SetModifyHdl(aLink);
104
105 mxBtnOk->set_sensitive(false);
106
108 mxBtnSelectSource->grab_focus(); // Initial focus is on the select source button.
109}
110
112{
113}
114
116{
117 return mpActiveEdit != nullptr && mpActiveEdit->GetWidget()->get_sensitive();
118}
119
121{
122 if (!mpActiveEdit)
123 return;
124
125 if (rRange.aStart != rRange.aEnd)
127
128 OUString aStr(rRange.aStart.Format(ScRefFlags::ADDR_ABS_3D, &rDoc, rDoc.GetAddressConvention()));
130
132}
133
135{
136 mbDlgLostFocus = true;
137}
138
140{
141 if (mbDlgLostFocus)
142 {
143 mbDlgLostFocus = false;
144 if (mpActiveEdit)
145 {
147 }
148 }
149 else
150 {
151 m_xDialog->grab_focus();
152 }
153
154 RefInputDone();
155}
156
158{
159 DoClose(ScXMLSourceDlgWrapper::GetChildWindowId());
160}
161
163{
164 sfx2::FileDialogHelper aDlgHelper(ui::dialogs::TemplateDescription::FILEOPEN_SIMPLE,
165 FileDialogFlags::NONE, m_xDialog.get());
167
168 uno::Reference<ui::dialogs::XFilePicker3> xFilePicker = aDlgHelper.GetFilePicker();
169
170 // Use the directory of current source file.
172 aURL.removeSegment();
173 aURL.removeFinalSlash();
174 OUString aPath = aURL.GetMainURL(INetURLObject::DecodeMechanism::NONE);
175 xFilePicker->setDisplayDirectory(aPath);
176
177 if (xFilePicker->execute() != ui::dialogs::ExecutableDialogResults::OK)
178 // File picker dialog cancelled.
179 return;
180
181 uno::Sequence<OUString> aFiles = xFilePicker->getSelectedFiles();
182 if (!aFiles.hasElements())
183 return;
184
185 // There should only be one file returned from the file picker.
186 maSrcPath = aFiles[0];
187 mxFtSourceFile->set_label(maSrcPath);
189}
190
192{
194 if (!pOrcus)
195 return;
196
197 mpXMLContext = pOrcus->createXMLContext(*mpDoc, rPath);
198 if (!mpXMLContext)
199 return;
200
201 mpXMLContext->loadXMLStructure(*mxLbTree, maXMLParam);
202}
203
204namespace {
205
212std::unique_ptr<weld::TreeIter> getReferenceEntry(const weld::TreeView& rTree, const weld::TreeIter& rCurEntry)
213{
214 std::unique_ptr<weld::TreeIter> xParent(rTree.make_iterator(&rCurEntry));
215 bool bParent = rTree.iter_parent(*xParent);
216 std::unique_ptr<weld::TreeIter> xRefEntry;
217 while (bParent)
218 {
220 OSL_ASSERT(pUserData);
222 {
223 // This is a repeat element - a potential reference entry.
224 xRefEntry = rTree.make_iterator(xParent.get());
225 }
226 bParent = rTree.iter_parent(*xParent);
227 }
228
229 if (xRefEntry)
230 return xRefEntry;
231
232 std::unique_ptr<weld::TreeIter> xCurEntry(rTree.make_iterator(&rCurEntry));
233 return xCurEntry;
234}
235
236}
237
239{
240 std::unique_ptr<weld::TreeIter> xEntry(mxLbTree->make_iterator());
241 if (!mxLbTree->get_cursor(xEntry.get()))
242 return;
243
244 mxLbTree->unselect_all();
245 mxLbTree->select(*xEntry);
246
247 mxCurRefEntry = getReferenceEntry(*mxLbTree, *xEntry);
248
250 OSL_ASSERT(pUserData);
251
252 const ScAddress& rPos = pUserData->maLinkedPos;
253 if (rPos.IsValid())
254 {
256 mxRefEdit->SetRefString(aStr);
257 }
258 else
259 mxRefEdit->SetRefString(OUString());
260
261 switch (pUserData->meType)
262 {
265 break;
268 break;
271 break;
272 default:
273 ;
274 }
275}
276
278{
279 if (mxLbTree->iter_has_child(rEntry))
280 {
281 // Only an element with no child elements (leaf element) can be linked.
282 bool bHasChild = false;
283 std::unique_ptr<weld::TreeIter> xChild(mxLbTree->make_iterator(&rEntry));
284 (void)mxLbTree->iter_children(*xChild);
285 do
286 {
288 OSL_ASSERT(pUserData);
289 if (pUserData->meType != ScOrcusXMLTreeParam::Attribute)
290 {
291 // This child is not an attribute. Bail out.
292 bHasChild = true;
293 break;
294 }
295 }
296 while (mxLbTree->iter_next_sibling(*xChild));
297
298 if (bHasChild)
299 {
301 return;
302 }
303 }
304
305 // Check all its parents and make sure non of them are range-linked nor
306 // repeat elements.
307 if (IsParentDirty(&rEntry))
308 {
310 return;
311 }
312
314}
315
317{
318 // Check all its parents first.
319
320 if (IsParentDirty(&rEntry))
321 {
323 return;
324 }
325
326 // Check all its child elements / attributes and make sure non of them are
327 // linked.
328
329 if (IsChildrenDirty(&rEntry))
330 {
332 return;
333 }
334
335 if (!mxLbTree->is_selected(rEntry))
336 {
337 // Highlight the entry if not highlighted already. This can happen
338 // when the current entry is a child entry of a repeat element entry.
339 mxLbTree->select(rEntry);
340 }
341
342 SelectAllChildEntries(rEntry);
344}
345
347{
348 // Check all its parent elements and make sure non of them are linked nor
349 // repeat elements. In attribute's case, it's okay to have the immediate
350 // parent element linked (but not range-linked).
351 std::unique_ptr<weld::TreeIter> xParent(mxLbTree->make_iterator(&rEntry));
352 mxLbTree->iter_parent(*xParent);
353
355 OSL_ASSERT(pUserData);
356 if (pUserData->maLinkedPos.IsValid() && pUserData->mbRangeParent)
357 {
358 // Parent element is range-linked. Bail out.
360 return;
361 }
362
363 if (IsParentDirty(&rEntry))
364 {
366 return;
367 }
368
370}
371
373{
374 mxMapGrid->set_sensitive(false);
375}
376
378{
379 mxMapGrid->set_sensitive(true);
380}
381
383{
384 mxMapGrid->set_sensitive(true);
385}
386
388{
389 std::unique_ptr<weld::TreeIter> xChild(mxLbTree->make_iterator(&rEntry));
390 if (!mxLbTree->iter_children(*xChild))
391 return;
392 do
393 {
394 SelectAllChildEntries(*xChild); // select recursively.
395 mxLbTree->select(*xChild);
396 } while (mxLbTree->iter_next_sibling(*xChild));
397}
398
400{
401 std::unique_ptr<weld::TreeIter> xParent(mxLbTree->make_iterator(pEntry));
402 if (!mxLbTree->iter_parent(*xParent))
403 return false;
404 do
405 {
407 assert(pUserData);
408 if (pUserData->maLinkedPos.IsValid())
409 {
410 // This parent is already linked.
411 return true;
412 }
413 }
414 while (mxLbTree->iter_parent(*xParent));
415 return false;
416}
417
419{
420 std::unique_ptr<weld::TreeIter> xChild(mxLbTree->make_iterator(pEntry));
421 if (!mxLbTree->iter_children(*xChild))
422 return false;
423
424 do
425 {
427 OSL_ASSERT(pUserData);
428 if (pUserData->maLinkedPos.IsValid())
429 // Already linked.
430 return true;
431
433 {
434 // Check recursively.
435 if (IsChildrenDirty(xChild.get()))
436 return true;
437 }
438 } while (mxLbTree->iter_next_sibling(*xChild));
439
440 return false;
441}
442
443namespace {
444
448void getFieldLinks(
449 ScOrcusImportXMLParam::RangeLink& rRangeLink, std::vector<size_t>& rNamespaces,
450 const weld::TreeView& rTree, const weld::TreeIter& rEntry)
451{
452 OUString aPath = getXPath(rTree, rEntry, rNamespaces);
454
455 if (pUserData)
456 {
458 // nested repeat element automatically becomes a row-group node.
459 rRangeLink.maRowGroups.push_back(
460 OUStringToOString(aPath, RTL_TEXTENCODING_UTF8));
461
462 if (pUserData->mbLeafNode && !aPath.isEmpty())
463 // XPath should never be empty anyway, but it won't hurt to check...
464 rRangeLink.maFieldPaths.push_back(OUStringToOString(aPath, RTL_TEXTENCODING_UTF8));
465 }
466
467 std::unique_ptr<weld::TreeIter> xChild(rTree.make_iterator(&rEntry));
468
469 if (!rTree.iter_children(*xChild))
470 // No more children. We're done.
471 return;
472
473 do
474 {
475 // Walk recursively.
476 getFieldLinks(rRangeLink, rNamespaces, rTree, *xChild);
477 }
478 while (rTree.iter_next_sibling(*xChild));
479}
480
481void removeDuplicates(std::vector<size_t>& rArray)
482{
483 std::sort(rArray.begin(), rArray.end());
484 std::vector<size_t>::iterator it = std::unique(rArray.begin(), rArray.end());
485 rArray.erase(it, rArray.end());
486}
487
488}
489
491{
492 if (!mpXMLContext)
493 return;
494
495 // Begin import.
496
498
499 // Convert single cell links.
500 for (const auto& rEntry : maCellLinks)
501 {
502 OUString aPath = getXPath(*mxLbTree, *rEntry, aParam.maNamespaces);
504
505 aParam.maCellLinks.emplace_back(
506 pUserData->maLinkedPos, OUStringToOString(aPath, RTL_TEXTENCODING_UTF8));
507 }
508
509 // Convert range links. For now, an element with range link takes all its
510 // child elements as its fields.
511 for (const auto& rEntry: maRangeLinks)
512 {
514
516 aRangeLink.maPos = pUserData->maLinkedPos;
517
518 // Go through all its child elements.
519 getFieldLinks(aRangeLink, aParam.maNamespaces, *mxLbTree, *rEntry);
520
521 // Add the reference entry as a row-group node, which will be used
522 // as a row position increment point.
523 OUString aThisEntry = getXPath(*mxLbTree, *rEntry, aParam.maNamespaces);
524 aRangeLink.maRowGroups.push_back(
525 OUStringToOString(aThisEntry, RTL_TEXTENCODING_UTF8));
526
527 aParam.maRangeLinks.push_back(aRangeLink);
528 }
529
530 // Remove duplicate namespace IDs.
531 removeDuplicates(aParam.maNamespaces);
532
533 // Now do the import.
534 mpXMLContext->importXML(aParam);
535
536 // Don't forget to broadcast the change.
538 pShell->Broadcast(SfxHint(SfxHintId::ScDataChanged));
539
540 // Repaint the grid to force repaint the cell values.
542 if (pViewShell)
543 pViewShell->PaintGrid();
544
545 m_xDialog->response(RET_OK);
546}
547
549{
550 m_xDialog->response(RET_CANCEL);
551}
552
554{
555 OUString aRefStr = mxRefEdit->GetText();
556
557 // Check if the address is valid.
558 // Preset current sheet in case only address was entered.
559 ScAddress aLinkedPos;
560 aLinkedPos.SetTab( ScDocShell::GetCurTab());
561 ScRefFlags nRes = aLinkedPos.Parse(aRefStr, *mpDoc, mpDoc->GetAddressConvention());
562 bool bValid = ( (nRes & ScRefFlags::VALID) == ScRefFlags::VALID );
563
564 // TODO: For some unknown reason, setting the ref invalid will hide the text altogether.
565 // Find out how to make this work.
566// mxRefEdit->SetRefValid(bValid);
567
568 if (!bValid)
569 aLinkedPos.SetInvalid();
570
571 // Set this address to the current reference entry.
572 if (!mxCurRefEntry)
573 // This should never happen.
574 return;
575
577 if (!pUserData)
578 // This should never happen either.
579 return;
580
581 bool bRepeatElem = pUserData->meType == ScOrcusXMLTreeParam::ElementRepeat;
582 pUserData->maLinkedPos = aLinkedPos;
583 pUserData->mbRangeParent = aLinkedPos.IsValid() && bRepeatElem;
584
585 if (bRepeatElem)
586 {
587 if (bValid)
588 maRangeLinks.insert(mxLbTree->make_iterator(mxCurRefEntry.get()));
589 else
591 }
592 else
593 {
594 if (bValid)
595 maCellLinks.insert(mxLbTree->make_iterator(mxCurRefEntry.get()));
596 else
598 }
599
600 // Enable the import button only when at least one link exists.
601 bool bHasLink = !maCellLinks.empty() || !maRangeLinks.empty();
602 mxBtnOk->set_sensitive(bHasLink);
603}
604
605IMPL_LINK(ScXMLSourceDlg, BtnPressedHdl, weld::Button&, rBtn, void)
606{
607 if (&rBtn == mxBtnSelectSource.get())
608 SelectSourceFile();
609 else if (&rBtn == mxBtnOk.get())
610 OkPressed();
611 else if (&rBtn == mxBtnCancel.get())
612 CancelPressed();
613}
614
616{
617 TreeItemSelected();
618}
619
621{
622 RefEditModified();
623}
624
625/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
ScRefFlags
Definition: address.hxx:158
Reference< XExecutableDialog > m_xDialog
SC_DLLPUBLIC void Format(OStringBuffer &r, ScRefFlags nFlags, const ScDocument *pDocument=nullptr, const Details &rDetails=detailsOOOa1) const
Definition: address.cxx:2074
bool IsValid() const
Definition: address.hxx:305
SC_DLLPUBLIC ScRefFlags Parse(const OUString &, const ScDocument &, const Details &rDetails=detailsOOOa1, ExternalInfo *pExtInfo=nullptr, const css::uno::Sequence< css::sheet::ExternalLinkInfo > *pExternalLinks=nullptr, sal_Int32 *pSheetEndPos=nullptr, const OUString *pErrRef=nullptr)
Definition: address.cxx:1537
void SetInvalid()
Definition: address.hxx:299
void SetTab(SCTAB nTabP)
Definition: address.hxx:295
static SCTAB GetCurTab()
Definition: docsh4.cxx:2614
SC_DLLPUBLIC formula::FormulaGrammar::AddressConvention GetAddressConvention() const
Definition: documen3.cxx:492
SfxObjectShell * GetDocumentShell() const
Definition: document.hxx:1083
virtual ScOrcusFilters * GetOrcusFilters()=0
static SC_DLLPUBLIC ScFormatFilterPlugin & Get()
Definition: impex.cxx:2684
Collection of orcus filter wrappers.
virtual std::unique_ptr< ScOrcusXMLContext > createXMLContext(ScDocument &rDoc, const OUString &rPath) const =0
Create a context for XML file.
ScAddress aEnd
Definition: address.hxx:498
ScAddress aStart
Definition: address.hxx:497
virtual void RefInputStart(formula::RefEdit *pEdit, formula::RefButton *pButton=nullptr) override
Definition: anyrefdg.cxx:745
virtual void RefInputDone(bool bForced=false) override
Definition: anyrefdg.cxx:775
bool DoClose(sal_uInt16 nId)
Definition: anyrefdg.cxx:714
static ScTabViewShell * GetActiveViewShell()
Definition: tabvwsh4.cxx:1076
void PaintGrid()
Definition: tabview3.cxx:2656
std::set< std::unique_ptr< weld::TreeIter >, CustomCompare > maRangeLinks
void SetRangeLinkable()
std::unique_ptr< formula::RefButton > mxRefBtn
std::unique_ptr< weld::Button > mxBtnCancel
ScDocument * mpDoc
bool IsChildrenDirty(const weld::TreeIter *pEntry) const
std::set< std::unique_ptr< weld::TreeIter >, CustomCompare > maCellLinks
ScXMLSourceDlg(SfxBindings *pB, SfxChildWindow *pCW, weld::Window *pParent, ScDocument *pDoc)
formula::RefEdit * mpActiveEdit
void RefEditModified()
virtual void Close() override
std::unique_ptr< weld::TreeView > mxLbTree
virtual bool IsRefInputMode() const override
virtual void Deactivate() override
void SelectSourceFile()
void SetSingleLinkable()
void SelectAllChildEntries(const weld::TreeIter &rEntry)
std::unique_ptr< weld::Button > mxBtnSelectSource
virtual ~ScXMLSourceDlg() override
void RepeatElementSelected(const weld::TreeIter &rEntry)
void LoadSourceFileStructure(const OUString &rPath)
std::unique_ptr< weld::Button > mxBtnOk
virtual void SetReference(const ScRange &rRange, ScDocument &rDoc) override
std::unique_ptr< formula::RefEdit > mxRefEdit
bool IsParentDirty(const weld::TreeIter *pEntry) const
Check if any of its parents is linked or repeated.
std::unique_ptr< ScOrcusXMLContext > mpXMLContext
ScOrcusXMLTreeParam maXMLParam
virtual void SetActive() override
std::unique_ptr< weld::Label > mxFtSourceFile
void DefaultElementSelected(const weld::TreeIter &rEntry)
void AttributeSelected(const weld::TreeIter &rEntry)
std::unique_ptr< weld::Container > mxMapGrid
void TreeItemSelected()
OUString maSrcPath
std::unique_ptr< weld::TreeIter > mxCurRefEntry
weld::Entry * GetWidget() const
void SetRefString(const OUString &rStr)
const css::uno::Reference< css::ui::dialogs::XFilePicker3 > & GetFilePicker() const
void SetContext(Context _eNewContext)
virtual std::unique_ptr< TreeIter > make_iterator(const TreeIter *pOrig=nullptr) const=0
virtual OUString get_text(int row, int col=-1) const=0
virtual bool iter_next_sibling(TreeIter &rIter) const=0
virtual bool iter_parent(TreeIter &rIter) const=0
virtual bool iter_children(TreeIter &rIter) const=0
virtual bool get_sensitive() const=0
URL aURL
float u
aStr
aBuf
std::unique_ptr< sal_Int32[]> pData
OString OUStringToOString(std::u16string_view str, ConnectionSettings const *settings)
std::vector< RangeLink > maRangeLinks
Definition: orcusxml.hxx:76
std::vector< CellLink > maCellLinks
Definition: orcusxml.hxx:75
std::vector< size_t > maNamespaces
Definition: orcusxml.hxx:74
Custom data stored with each tree item.
Definition: orcusxml.hxx:32
bool mbRangeParent
linked cell position (invalid if unlinked)
Definition: orcusxml.hxx:36
EntryType meType
numerical ID for xml namespace
Definition: orcusxml.hxx:34
static EntryData * getUserData(const weld::TreeView &rControl, const weld::TreeIter &rEntry)
Definition: orcusxml.cxx:23
OUString maImgElementRepeat
Definition: orcusxml.hxx:45
OUString maImgAttribute
Definition: orcusxml.hxx:46
OUString maImgElementDefault
Definition: orcusxml.hxx:44
RET_OK
RET_CANCEL
IMPL_LINK(ScXMLSourceDlg, BtnPressedHdl, weld::Button &, rBtn, void)
IMPL_LINK_NOARG(ScXMLSourceDlg, TreeItemSelectHdl, weld::TreeView &, void)