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 aBuf.insert(0, rTree.get_text(*xEntry, 0));
53 if (isAttribute(rTree, *xEntry))
54 aBuf.insert(0, "/@");
55 else
56 aBuf.insert(0, '/');
57 }
58 while (rTree.iter_parent(*xEntry));
59
60 return aBuf.makeStringAndClear();
61}
62
63}
64
66 SfxBindings* pB, SfxChildWindow* pCW, weld::Window* pParent, ScDocument* pDoc)
67 : ScAnyRefDlgController(pB, pCW, pParent, "modules/scalc/ui/xmlsourcedialog.ui", "XMLSourceDialog")
68 , mpDoc(pDoc)
69 , mbDlgLostFocus(false)
70 , mxBtnSelectSource(m_xBuilder->weld_button("selectsource"))
71 , mxFtSourceFile(m_xBuilder->weld_label("sourcefile"))
72 , mxMapGrid(m_xBuilder->weld_container("mapgrid"))
73 , mxLbTree(m_xBuilder->weld_tree_view("tree"))
74 , mxRefEdit(new formula::RefEdit(m_xBuilder->weld_entry("edit")))
75 , mxRefBtn(new formula::RefButton(m_xBuilder->weld_button("ref")))
76 , mxBtnOk(m_xBuilder->weld_button("ok"))
77 , mxBtnCancel(m_xBuilder->weld_button("cancel"))
78 , maCustomCompare(*mxLbTree)
79 , maCellLinks(maCustomCompare)
80 , maRangeLinks(maCustomCompare)
81{
82 mxLbTree->set_size_request(mxLbTree->get_approximate_digit_width() * 40,
83 mxLbTree->get_height_rows(15));
84 mxLbTree->set_selection_mode(SelectionMode::Multiple);
85 mxRefEdit->SetReferences(this, nullptr);
86 mxRefBtn->SetReferences(this, mxRefEdit.get());
87
88 mpActiveEdit = mxRefEdit.get();
89
90 maXMLParam.maImgElementDefault = RID_BMP_ELEMENT_DEFAULT;
91 maXMLParam.maImgElementRepeat = RID_BMP_ELEMENT_REPEAT;
92 maXMLParam.maImgAttribute = RID_BMP_ELEMENT_ATTRIBUTE;
93
94 Link<weld::Button&,void> aBtnHdl = LINK(this, ScXMLSourceDlg, BtnPressedHdl);
95 mxBtnSelectSource->connect_clicked(aBtnHdl);
96 mxBtnOk->connect_clicked(aBtnHdl);
97 mxBtnCancel->connect_clicked(aBtnHdl);
98
99 mxLbTree->connect_changed(LINK(this, ScXMLSourceDlg, TreeItemSelectHdl));
100
101 Link<formula::RefEdit&,void> aLink = LINK(this, ScXMLSourceDlg, RefModifiedHdl);
102 mxRefEdit->SetModifyHdl(aLink);
103
104 mxBtnOk->set_sensitive(false);
105
107 mxBtnSelectSource->grab_focus(); // Initial focus is on the select source button.
108}
109
111{
112}
113
115{
116 return mpActiveEdit != nullptr && mpActiveEdit->GetWidget()->get_sensitive();
117}
118
120{
121 if (!mpActiveEdit)
122 return;
123
124 if (rRange.aStart != rRange.aEnd)
126
127 OUString aStr(rRange.aStart.Format(ScRefFlags::ADDR_ABS_3D, &rDoc, rDoc.GetAddressConvention()));
129
131}
132
134{
135 mbDlgLostFocus = true;
136}
137
139{
140 if (mbDlgLostFocus)
141 {
142 mbDlgLostFocus = false;
143 if (mpActiveEdit)
144 {
146 }
147 }
148 else
149 {
150 m_xDialog->grab_focus();
151 }
152
153 RefInputDone();
154}
155
157{
158 DoClose(ScXMLSourceDlgWrapper::GetChildWindowId());
159}
160
162{
163 sfx2::FileDialogHelper aDlgHelper(ui::dialogs::TemplateDescription::FILEOPEN_SIMPLE,
164 FileDialogFlags::NONE, m_xDialog.get());
166
167 uno::Reference<ui::dialogs::XFilePicker3> xFilePicker = aDlgHelper.GetFilePicker();
168
169 // Use the directory of current source file.
171 aURL.removeSegment();
172 aURL.removeFinalSlash();
173 OUString aPath = aURL.GetMainURL(INetURLObject::DecodeMechanism::NONE);
174 xFilePicker->setDisplayDirectory(aPath);
175
176 if (xFilePicker->execute() != ui::dialogs::ExecutableDialogResults::OK)
177 // File picker dialog cancelled.
178 return;
179
180 uno::Sequence<OUString> aFiles = xFilePicker->getSelectedFiles();
181 if (!aFiles.hasElements())
182 return;
183
184 // There should only be one file returned from the file picker.
185 maSrcPath = aFiles[0];
186 mxFtSourceFile->set_label(maSrcPath);
188}
189
191{
193 if (!pOrcus)
194 return;
195
196 mpXMLContext = pOrcus->createXMLContext(*mpDoc, rPath);
197 if (!mpXMLContext)
198 return;
199
200 mpXMLContext->loadXMLStructure(*mxLbTree, maXMLParam);
201}
202
203namespace {
204
211std::unique_ptr<weld::TreeIter> getReferenceEntry(const weld::TreeView& rTree, const weld::TreeIter& rCurEntry)
212{
213 std::unique_ptr<weld::TreeIter> xParent(rTree.make_iterator(&rCurEntry));
214 bool bParent = rTree.iter_parent(*xParent);
215 std::unique_ptr<weld::TreeIter> xRefEntry;
216 while (bParent)
217 {
219 OSL_ASSERT(pUserData);
221 {
222 // This is a repeat element - a potential reference entry.
223 xRefEntry = rTree.make_iterator(xParent.get());
224 }
225 bParent = rTree.iter_parent(*xParent);
226 }
227
228 if (xRefEntry)
229 return xRefEntry;
230
231 std::unique_ptr<weld::TreeIter> xCurEntry(rTree.make_iterator(&rCurEntry));
232 return xCurEntry;
233}
234
235}
236
238{
239 std::unique_ptr<weld::TreeIter> xEntry(mxLbTree->make_iterator());
240 if (!mxLbTree->get_cursor(xEntry.get()))
241 return;
242
243 mxLbTree->unselect_all();
244 mxLbTree->select(*xEntry);
245
246 mxCurRefEntry = getReferenceEntry(*mxLbTree, *xEntry);
247
249 OSL_ASSERT(pUserData);
250
251 const ScAddress& rPos = pUserData->maLinkedPos;
252 if (rPos.IsValid())
253 {
255 mxRefEdit->SetRefString(aStr);
256 }
257 else
258 mxRefEdit->SetRefString(OUString());
259
260 switch (pUserData->meType)
261 {
264 break;
267 break;
270 break;
271 default:
272 ;
273 }
274}
275
277{
278 if (mxLbTree->iter_has_child(rEntry))
279 {
280 // Only an element with no child elements (leaf element) can be linked.
281 bool bHasChild = false;
282 std::unique_ptr<weld::TreeIter> xChild(mxLbTree->make_iterator(&rEntry));
283 (void)mxLbTree->iter_children(*xChild);
284 do
285 {
287 OSL_ASSERT(pUserData);
288 if (pUserData->meType != ScOrcusXMLTreeParam::Attribute)
289 {
290 // This child is not an attribute. Bail out.
291 bHasChild = true;
292 break;
293 }
294 }
295 while (mxLbTree->iter_next_sibling(*xChild));
296
297 if (bHasChild)
298 {
300 return;
301 }
302 }
303
304 // Check all its parents and make sure non of them are range-linked nor
305 // repeat elements.
306 if (IsParentDirty(&rEntry))
307 {
309 return;
310 }
311
313}
314
316{
317 // Check all its parents first.
318
319 if (IsParentDirty(&rEntry))
320 {
322 return;
323 }
324
325 // Check all its child elements / attributes and make sure non of them are
326 // linked.
327
328 if (IsChildrenDirty(&rEntry))
329 {
331 return;
332 }
333
334 if (!mxLbTree->is_selected(rEntry))
335 {
336 // Highlight the entry if not highlighted already. This can happen
337 // when the current entry is a child entry of a repeat element entry.
338 mxLbTree->select(rEntry);
339 }
340
341 SelectAllChildEntries(rEntry);
343}
344
346{
347 // Check all its parent elements and make sure non of them are linked nor
348 // repeat elements. In attribute's case, it's okay to have the immediate
349 // parent element linked (but not range-linked).
350 std::unique_ptr<weld::TreeIter> xParent(mxLbTree->make_iterator(&rEntry));
351 mxLbTree->iter_parent(*xParent);
352
354 OSL_ASSERT(pUserData);
355 if (pUserData->maLinkedPos.IsValid() && pUserData->mbRangeParent)
356 {
357 // Parent element is range-linked. Bail out.
359 return;
360 }
361
362 if (IsParentDirty(&rEntry))
363 {
365 return;
366 }
367
369}
370
372{
373 mxMapGrid->set_sensitive(false);
374}
375
377{
378 mxMapGrid->set_sensitive(true);
379}
380
382{
383 mxMapGrid->set_sensitive(true);
384}
385
387{
388 std::unique_ptr<weld::TreeIter> xChild(mxLbTree->make_iterator(&rEntry));
389 if (!mxLbTree->iter_children(*xChild))
390 return;
391 do
392 {
393 SelectAllChildEntries(*xChild); // select recursively.
394 mxLbTree->select(*xChild);
395 } while (mxLbTree->iter_next_sibling(*xChild));
396}
397
399{
400 std::unique_ptr<weld::TreeIter> xParent(mxLbTree->make_iterator(pEntry));
401 if (!mxLbTree->iter_parent(*xParent))
402 return false;
403 do
404 {
406 assert(pUserData);
407 if (pUserData->maLinkedPos.IsValid())
408 {
409 // This parent is already linked.
410 return true;
411 }
412 }
413 while (mxLbTree->iter_parent(*xParent));
414 return false;
415}
416
418{
419 std::unique_ptr<weld::TreeIter> xChild(mxLbTree->make_iterator(pEntry));
420 if (!mxLbTree->iter_children(*xChild))
421 return false;
422
423 do
424 {
426 OSL_ASSERT(pUserData);
427 if (pUserData->maLinkedPos.IsValid())
428 // Already linked.
429 return true;
430
432 {
433 // Check recursively.
434 if (IsChildrenDirty(xChild.get()))
435 return true;
436 }
437 } while (mxLbTree->iter_next_sibling(*xChild));
438
439 return false;
440}
441
442namespace {
443
447void getFieldLinks(
448 ScOrcusImportXMLParam::RangeLink& rRangeLink, std::vector<size_t>& rNamespaces,
449 const weld::TreeView& rTree, const weld::TreeIter& rEntry)
450{
451 std::unique_ptr<weld::TreeIter> xChild(rTree.make_iterator(&rEntry));
452 if (!rTree.iter_children(*xChild))
453 // No more children. We're done.
454 return;
455
456 do
457 {
458 OUString aPath = getXPath(rTree, *xChild, rNamespaces);
459 const ScOrcusXMLTreeParam::EntryData* pUserData = ScOrcusXMLTreeParam::getUserData(rTree, *xChild);
460
461 if (pUserData)
462 {
464 // nested repeat element automatically becomes a row-group node.
465 rRangeLink.maRowGroups.push_back(
466 OUStringToOString(aPath, RTL_TEXTENCODING_UTF8));
467
468 if (pUserData->mbLeafNode && !aPath.isEmpty())
469 // XPath should never be empty anyway, but it won't hurt to check...
470 rRangeLink.maFieldPaths.push_back(OUStringToOString(aPath, RTL_TEXTENCODING_UTF8));
471 }
472
473 // Walk recursively.
474 getFieldLinks(rRangeLink, rNamespaces, rTree, *xChild);
475 } while (rTree.iter_next_sibling(*xChild));
476}
477
478void removeDuplicates(std::vector<size_t>& rArray)
479{
480 std::sort(rArray.begin(), rArray.end());
481 std::vector<size_t>::iterator it = std::unique(rArray.begin(), rArray.end());
482 rArray.erase(it, rArray.end());
483}
484
485}
486
488{
489 if (!mpXMLContext)
490 return;
491
492 // Begin import.
493
495
496 // Convert single cell links.
497 for (const auto& rEntry : maCellLinks)
498 {
499 OUString aPath = getXPath(*mxLbTree, *rEntry, aParam.maNamespaces);
501
502 aParam.maCellLinks.emplace_back(
503 pUserData->maLinkedPos, OUStringToOString(aPath, RTL_TEXTENCODING_UTF8));
504 }
505
506 // Convert range links. For now, an element with range link takes all its
507 // child elements as its fields.
508 for (const auto& rEntry: maRangeLinks)
509 {
511
513 aRangeLink.maPos = pUserData->maLinkedPos;
514
515 // Go through all its child elements.
516 getFieldLinks(aRangeLink, aParam.maNamespaces, *mxLbTree, *rEntry);
517
518 // Add the reference entry as a row-group node, which will be used
519 // as a row position increment point.
520 OUString aThisEntry = getXPath(*mxLbTree, *rEntry, aParam.maNamespaces);
521 aRangeLink.maRowGroups.push_back(
522 OUStringToOString(aThisEntry, RTL_TEXTENCODING_UTF8));
523
524 aParam.maRangeLinks.push_back(aRangeLink);
525 }
526
527 // Remove duplicate namespace IDs.
528 removeDuplicates(aParam.maNamespaces);
529
530 // Now do the import.
531 mpXMLContext->importXML(aParam);
532
533 // Don't forget to broadcast the change.
535 pShell->Broadcast(SfxHint(SfxHintId::ScDataChanged));
536
537 // Repaint the grid to force repaint the cell values.
539 if (pViewShell)
540 pViewShell->PaintGrid();
541
542 m_xDialog->response(RET_OK);
543}
544
546{
547 m_xDialog->response(RET_CANCEL);
548}
549
551{
552 OUString aRefStr = mxRefEdit->GetText();
553
554 // Check if the address is valid.
555 // Preset current sheet in case only address was entered.
556 ScAddress aLinkedPos;
557 aLinkedPos.SetTab( ScDocShell::GetCurTab());
558 ScRefFlags nRes = aLinkedPos.Parse(aRefStr, *mpDoc, mpDoc->GetAddressConvention());
559 bool bValid = ( (nRes & ScRefFlags::VALID) == ScRefFlags::VALID );
560
561 // TODO: For some unknown reason, setting the ref invalid will hide the text altogether.
562 // Find out how to make this work.
563// mxRefEdit->SetRefValid(bValid);
564
565 if (!bValid)
566 aLinkedPos.SetInvalid();
567
568 // Set this address to the current reference entry.
569 if (!mxCurRefEntry)
570 // This should never happen.
571 return;
572
574 if (!pUserData)
575 // This should never happen either.
576 return;
577
578 bool bRepeatElem = pUserData->meType == ScOrcusXMLTreeParam::ElementRepeat;
579 pUserData->maLinkedPos = aLinkedPos;
580 pUserData->mbRangeParent = aLinkedPos.IsValid() && bRepeatElem;
581
582 if (bRepeatElem)
583 {
584 if (bValid)
585 maRangeLinks.insert(mxLbTree->make_iterator(mxCurRefEntry.get()));
586 else
588 }
589 else
590 {
591 if (bValid)
592 maCellLinks.insert(mxLbTree->make_iterator(mxCurRefEntry.get()));
593 else
595 }
596
597 // Enable the import button only when at least one link exists.
598 bool bHasLink = !maCellLinks.empty() || !maRangeLinks.empty();
599 mxBtnOk->set_sensitive(bHasLink);
600}
601
602IMPL_LINK(ScXMLSourceDlg, BtnPressedHdl, weld::Button&, rBtn, void)
603{
604 if (&rBtn == mxBtnSelectSource.get())
605 SelectSourceFile();
606 else if (&rBtn == mxBtnOk.get())
607 OkPressed();
608 else if (&rBtn == mxBtnCancel.get())
609 CancelPressed();
610}
611
613{
614 TreeItemSelected();
615}
616
618{
619 RefEditModified();
620}
621
622/* 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:2599
SC_DLLPUBLIC formula::FormulaGrammar::AddressConvention GetAddressConvention() const
Definition: documen3.cxx:500
SfxObjectShell * GetDocumentShell() const
Definition: document.hxx:1081
virtual ScOrcusFilters * GetOrcusFilters()=0
static SC_DLLPUBLIC ScFormatFilterPlugin & Get()
Definition: impex.cxx:2617
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:749
virtual void RefInputDone(bool bForced=false) override
Definition: anyrefdg.cxx:779
bool DoClose(sal_uInt16 nId)
Definition: anyrefdg.cxx:718
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
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)