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/.
10 #include "xmlsourcedlg.hxx"
12 #include "scresid.hxx"
13 #include "document.hxx"
14 #include "orcusfilters.hxx"
16 #include "reffact.hxx"
17 #include "tabvwsh.hxx"
19 #include <unotools/pathoptions.hxx>
20 #include <tools/urlobj.hxx>
21 #include <svtools/svlbitm.hxx>
22 #include <svtools/treelistentry.hxx>
23 #include <svtools/viewdataentry.hxx>
24 #include <sfx2/objsh.hxx>
26 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
27 #include <com/sun/star/ui/dialogs/FilePicker.hpp>
28 #include <com/sun/star/ui/dialogs/ExecutableDialogResults.hpp>
29 #include <com/sun/star/ui/dialogs/TemplateDescription.hpp>
31 using namespace com::sun::star
;
35 bool isAttribute(const SvTreeListEntry
& rEntry
)
37 const ScOrcusXMLTreeParam::EntryData
* pUserData
= ScOrcusXMLTreeParam::getUserData(rEntry
);
41 return pUserData
->meType
== ScOrcusXMLTreeParam::Attribute
;
45 const SvTreeListBox
& rTree
, const SvTreeListEntry
& rEntry
, std::vector
<size_t>& rNamespaces
)
48 for (const SvTreeListEntry
* p
= &rEntry
; p
; p
= rTree
.GetParent(p
))
50 const SvLBoxItem
* pItem
= p
->GetFirstItem(SV_ITEM_ID_LBOXSTRING
);
54 // Collect used namespace.
55 const ScOrcusXMLTreeParam::EntryData
* pData
= ScOrcusXMLTreeParam::getUserData(*p
);
57 rNamespaces
.push_back(pData
->mnNamespaceID
);
59 const SvLBoxString
* pStr
= static_cast<const SvLBoxString
*>(pItem
);
60 aBuf
.insert(0, pStr
->GetText());
61 aBuf
.insert(0, isAttribute(*p
) ? '@' : '/');
64 return aBuf
.makeStringAndClear();
69 ScXMLSourceDlg::ScXMLSourceDlg(
70 SfxBindings
* pB
, SfxChildWindow
* pCW
, vcl::Window
* pParent
, ScDocument
* pDoc
)
71 : ScAnyRefDlg(pB
, pCW
, pParent
, "XMLSourceDialog",
72 "modules/scalc/ui/xmlsourcedialog.ui")
75 , mbDlgLostFocus(false)
77 get(mpBtnSelectSource
, "selectsource");
78 get(mpFtSourceFile
, "sourcefile");
79 get(mpMapGrid
, "mapgrid");
80 get(mpLbTree
, "tree");
81 Size
aTreeSize(mpLbTree
->LogicToPixel(Size(130, 120), MAP_APPFONT
));
82 mpLbTree
->set_width_request(aTreeSize
.Width());
83 mpLbTree
->set_height_request(aTreeSize
.Height());
84 get(mpRefEdit
, "edit");
85 mpRefEdit
->SetReferences(this, NULL
);
87 mpRefBtn
->SetReferences(this, mpRefEdit
);
88 get(mpBtnCancel
, "cancel");
91 mpActiveEdit
= mpRefEdit
;
93 maXMLParam
.maImgElementDefault
= Image(ScResId(IMG_ELEMENT_DEFAULT
));
94 maXMLParam
.maImgElementRepeat
= Image(ScResId(IMG_ELEMENT_REPEAT
));
95 maXMLParam
.maImgAttribute
= Image(ScResId(IMG_ELEMENT_ATTRIBUTE
));
97 Link
<> aBtnHdl
= LINK(this, ScXMLSourceDlg
, BtnPressedHdl
);
98 mpBtnSelectSource
->SetClickHdl(aBtnHdl
);
99 mpBtnOk
->SetClickHdl(aBtnHdl
);
100 mpBtnCancel
->SetClickHdl(aBtnHdl
);
102 Link
<> aLink
= LINK(this, ScXMLSourceDlg
, GetFocusHdl
);
103 mpRefEdit
->SetGetFocusHdl(aLink
);
104 mpRefBtn
->SetGetFocusHdl(aLink
);
106 aLink
= LINK(this, ScXMLSourceDlg
, TreeItemSelectHdl
);
107 mpLbTree
->SetSelectHdl(aLink
);
109 aLink
= LINK(this, ScXMLSourceDlg
, RefModifiedHdl
);
110 mpRefEdit
->SetModifyHdl(aLink
);
115 mpBtnSelectSource
->GrabFocus(); // Initial focus is on the select source button.
118 ScXMLSourceDlg::~ScXMLSourceDlg()
123 void ScXMLSourceDlg::dispose()
125 mpBtnSelectSource
.clear();
126 mpFtSourceFile
.clear();
133 mpActiveEdit
.clear();
134 ScAnyRefDlg::dispose();
137 bool ScXMLSourceDlg::IsRefInputMode() const
139 return mpActiveEdit
!= nullptr && mpActiveEdit
->IsEnabled();
142 void ScXMLSourceDlg::SetReference(const ScRange
& rRange
, ScDocument
* pDoc
)
147 if (rRange
.aStart
!= rRange
.aEnd
)
148 RefInputStart(mpActiveEdit
);
150 OUString
aStr(rRange
.aStart
.Format(SCA_ABS_3D
, pDoc
, pDoc
->GetAddressConvention()));
151 mpActiveEdit
->SetRefString(aStr
);
156 void ScXMLSourceDlg::Deactivate()
158 mbDlgLostFocus
= true;
161 void ScXMLSourceDlg::SetActive()
165 mbDlgLostFocus
= false;
168 mpActiveEdit
->GrabFocus();
179 bool ScXMLSourceDlg::Close()
181 return DoClose(ScXMLSourceDlgWrapper::GetChildWindowId());
184 void ScXMLSourceDlg::SelectSourceFile()
186 uno::Reference
<ui::dialogs::XFilePicker3
> xFilePicker
= ui::dialogs::FilePicker::createWithMode( comphelper::getProcessComponentContext(), ui::dialogs::TemplateDescription::FILEOPEN_SIMPLE
);
188 if (maSrcPath
.isEmpty())
190 xFilePicker
->setDisplayDirectory(SvtPathOptions().GetWorkPath());
193 // Use the directory of current source file.
194 INetURLObject
aURL(maSrcPath
);
195 aURL
.removeSegment();
196 aURL
.removeFinalSlash();
197 OUString aPath
= aURL
.GetMainURL(INetURLObject::NO_DECODE
);
198 xFilePicker
->setDisplayDirectory(aPath
);
201 if (xFilePicker
->execute() != ui::dialogs::ExecutableDialogResults::OK
)
202 // File picker dialog cancelled.
205 uno::Sequence
<OUString
> aFiles
= xFilePicker
->getFiles();
206 if (!aFiles
.getLength())
209 // There should only be one file returned from the file picker.
210 maSrcPath
= aFiles
[0];
211 mpFtSourceFile
->SetText(maSrcPath
);
212 maHighlightedEntries
.clear();
213 LoadSourceFileStructure(maSrcPath
);
216 void ScXMLSourceDlg::LoadSourceFileStructure(const OUString
& rPath
)
218 ScOrcusFilters
* pOrcus
= ScFormatFilter::Get().GetOrcusFilters();
222 mpXMLContext
.reset(pOrcus
->createXMLContext(*mpDoc
, rPath
));
226 mpXMLContext
->loadXMLStructure(*mpLbTree
, maXMLParam
);
229 void ScXMLSourceDlg::HandleGetFocus(Control
* pCtrl
)
232 if (pCtrl
== mpRefEdit
|| pCtrl
== mpRefBtn
)
233 mpActiveEdit
= mpRefEdit
;
236 mpActiveEdit
->SetSelection(Selection(0, SELECTION_MAX
));
241 class UnhighlightEntry
: std::unary_function
<SvTreeListEntry
*, void>
243 SvTreeListBox
& mrTree
;
245 UnhighlightEntry(SvTreeListBox
& rTree
) : mrTree(rTree
) {}
247 void operator() (SvTreeListEntry
* p
)
249 SvViewDataEntry
* pView
= mrTree
.GetViewDataEntry(p
);
253 pView
->SetHighlighted(false);
259 * When the current entry is a direct or indirect child of a mappable
260 * repeat element entry, that entry becomes the reference entry.
261 * Otherwise the reference entry equals the current entry. A reference
262 * entry is the entry that stores mapped cell position.
264 SvTreeListEntry
* getReferenceEntry(SvTreeListBox
& rTree
, SvTreeListEntry
* pCurEntry
)
266 SvTreeListEntry
* pParent
= rTree
.GetParent(pCurEntry
);
267 SvTreeListEntry
* pRefEntry
= NULL
;
270 ScOrcusXMLTreeParam::EntryData
* pUserData
= ScOrcusXMLTreeParam::getUserData(*pParent
);
271 OSL_ASSERT(pUserData
);
272 if (pUserData
->meType
== ScOrcusXMLTreeParam::ElementRepeat
)
274 // This is a repeat element.
277 // Second repeat element encountered. Not good.
283 pParent
= rTree
.GetParent(pParent
);
286 return pRefEntry
? pRefEntry
: pCurEntry
;
291 void ScXMLSourceDlg::TreeItemSelected()
293 SvTreeListEntry
* pEntry
= mpLbTree
->GetCurEntry();
297 if (!maHighlightedEntries
.empty())
299 // Remove highlights from all previously highlighted entries (if any).
300 std::for_each(maHighlightedEntries
.begin(), maHighlightedEntries
.end(), UnhighlightEntry(*mpLbTree
));
301 maHighlightedEntries
.clear();
304 mpCurRefEntry
= getReferenceEntry(*mpLbTree
, pEntry
);
306 ScOrcusXMLTreeParam::EntryData
* pUserData
= ScOrcusXMLTreeParam::getUserData(*mpCurRefEntry
);
307 OSL_ASSERT(pUserData
);
309 const ScAddress
& rPos
= pUserData
->maLinkedPos
;
312 OUString
aStr(rPos
.Format(SCA_ABS_3D
, mpDoc
, mpDoc
->GetAddressConvention()));
313 mpRefEdit
->SetRefString(aStr
);
316 mpRefEdit
->SetRefString(OUString());
318 switch (pUserData
->meType
)
320 case ScOrcusXMLTreeParam::Attribute
:
321 AttributeSelected(*mpCurRefEntry
);
323 case ScOrcusXMLTreeParam::ElementDefault
:
324 DefaultElementSelected(*mpCurRefEntry
);
326 case ScOrcusXMLTreeParam::ElementRepeat
:
327 RepeatElementSelected(*mpCurRefEntry
);
334 void ScXMLSourceDlg::DefaultElementSelected(SvTreeListEntry
& rEntry
)
337 if (mpLbTree
->GetChildCount(&rEntry
) > 0)
339 // Only an element with no child elements (leaf element) can be linked.
340 bool bHasChild
= false;
341 for (SvTreeListEntry
* pChild
= mpLbTree
->FirstChild(&rEntry
); pChild
; pChild
= SvTreeListBox::NextSibling(pChild
))
343 ScOrcusXMLTreeParam::EntryData
* pUserData
= ScOrcusXMLTreeParam::getUserData(*pChild
);
344 OSL_ASSERT(pUserData
);
345 if (pUserData
->meType
!= ScOrcusXMLTreeParam::Attribute
)
347 // This child is not an attribute. Bail out.
360 // Check all its parents and make sure non of them are range-linked nor
362 if (IsParentDirty(&rEntry
))
371 void ScXMLSourceDlg::RepeatElementSelected(SvTreeListEntry
& rEntry
)
373 // Check all its parents first.
375 if (IsParentDirty(&rEntry
))
381 // Check all its child elements / attributes and make sure non of them are
382 // linked or repeat elements. In the future we will support range linking
383 // of repeat element who has another repeat elements. But first I need to
384 // support that scenario in orcus.
386 if (IsChildrenDirty(&rEntry
))
392 SvViewDataEntry
* p
= mpLbTree
->GetViewDataEntry(&rEntry
);
393 if (!p
->IsHighlighted())
395 // Highlight the entry if not highlighted already. This can happen
396 // when the current entry is a child entry of a repeat element entry.
397 p
->SetHighlighted(true);
398 mpLbTree
->Invalidate();
399 maHighlightedEntries
.push_back(&rEntry
);
402 SelectAllChildEntries(rEntry
);
406 void ScXMLSourceDlg::AttributeSelected(SvTreeListEntry
& rEntry
)
408 // Check all its parent elements and make sure non of them are linked nor
409 // repeat elements. In attribute's case, it's okay to have the immediate
410 // parent element linked (but not range-linked).
412 SvTreeListEntry
* pParent
= mpLbTree
->GetParent(&rEntry
);
413 OSL_ASSERT(pParent
); // attribute should have a parent element.
415 ScOrcusXMLTreeParam::EntryData
* pUserData
= ScOrcusXMLTreeParam::getUserData(*pParent
);
416 OSL_ASSERT(pUserData
);
417 if (pUserData
->maLinkedPos
.IsValid() && pUserData
->mbRangeParent
)
419 // Parent element is range-linked. Bail out.
424 if (IsParentDirty(&rEntry
))
433 void ScXMLSourceDlg::SetNonLinkable()
435 mpMapGrid
->Disable();
438 void ScXMLSourceDlg::SetSingleLinkable()
443 void ScXMLSourceDlg::SetRangeLinkable()
448 void ScXMLSourceDlg::SelectAllChildEntries(SvTreeListEntry
& rEntry
)
450 SvTreeListEntries
& rChildren
= rEntry
.GetChildEntries();
451 SvTreeListEntries::iterator it
= rChildren
.begin(), itEnd
= rChildren
.end();
452 for (; it
!= itEnd
; ++it
)
454 SvTreeListEntry
& r
= *it
;
455 SelectAllChildEntries(r
); // select recursively.
456 SvViewDataEntry
* p
= mpLbTree
->GetViewDataEntry(&r
);
457 p
->SetHighlighted(true);
458 mpLbTree
->Invalidate();
459 maHighlightedEntries
.push_back(&r
);
463 bool ScXMLSourceDlg::IsParentDirty(SvTreeListEntry
* pEntry
) const
465 SvTreeListEntry
* pParent
= mpLbTree
->GetParent(pEntry
);
468 ScOrcusXMLTreeParam::EntryData
* pUserData
= ScOrcusXMLTreeParam::getUserData(*pParent
);
470 if (pUserData
->maLinkedPos
.IsValid())
472 // This parent is already linked.
475 if (pUserData
->meType
== ScOrcusXMLTreeParam::ElementRepeat
)
477 // This is a repeat element.
480 pParent
= mpLbTree
->GetParent(pParent
);
485 bool ScXMLSourceDlg::IsChildrenDirty(SvTreeListEntry
* pEntry
) const
487 for (SvTreeListEntry
* pChild
= mpLbTree
->FirstChild(pEntry
); pChild
; pChild
= SvTreeListBox::NextSibling(pChild
))
489 ScOrcusXMLTreeParam::EntryData
* pUserData
= ScOrcusXMLTreeParam::getUserData(*pChild
);
490 OSL_ASSERT(pUserData
);
491 if (pUserData
->maLinkedPos
.IsValid())
495 if (pUserData
->meType
== ScOrcusXMLTreeParam::ElementRepeat
)
496 // We don't support linking of nested repeat elements (yet).
499 if (pUserData
->meType
== ScOrcusXMLTreeParam::ElementDefault
)
501 // Check recursively.
502 if (IsChildrenDirty(pChild
))
513 * Pick only the leaf elements.
516 ScOrcusImportXMLParam::RangeLink
& rRangeLink
, std::vector
<size_t>& rNamespaces
,
517 const SvTreeListBox
& rTree
, const SvTreeListEntry
& rEntry
)
519 const SvTreeListEntries
& rChildren
= rEntry
.GetChildEntries();
520 if (rChildren
.empty())
521 // No more children. We're done.
524 SvTreeListEntries::const_iterator it
= rChildren
.begin(), itEnd
= rChildren
.end();
525 for (; it
!= itEnd
; ++it
)
527 const SvTreeListEntry
& rChild
= *it
;
528 OUString aPath
= getXPath(rTree
, rChild
, rNamespaces
);
529 const ScOrcusXMLTreeParam::EntryData
* pUserData
= ScOrcusXMLTreeParam::getUserData(rChild
);
531 if (pUserData
&& pUserData
->mbLeafNode
)
533 if (!aPath
.isEmpty())
534 // XPath should never be empty anyway, but it won't hurt to check...
535 rRangeLink
.maFieldPaths
.push_back(OUStringToOString(aPath
, RTL_TEXTENCODING_UTF8
));
539 getFieldLinks(rRangeLink
, rNamespaces
, rTree
, rChild
);
543 void removeDuplicates(std::vector
<size_t>& rArray
)
545 std::sort(rArray
.begin(), rArray
.end());
546 std::vector
<size_t>::iterator it
= std::unique(rArray
.begin(), rArray
.end());
547 rArray
.erase(it
, rArray
.end());
552 void ScXMLSourceDlg::OkPressed()
559 ScOrcusImportXMLParam aParam
;
561 // Convert single cell links.
563 std::set
<const SvTreeListEntry
*>::const_iterator it
= maCellLinks
.begin(), itEnd
= maCellLinks
.end();
564 for (; it
!= itEnd
; ++it
)
566 const SvTreeListEntry
& rEntry
= **it
;
567 OUString aPath
= getXPath(*mpLbTree
, rEntry
, aParam
.maNamespaces
);
568 const ScOrcusXMLTreeParam::EntryData
* pUserData
= ScOrcusXMLTreeParam::getUserData(rEntry
);
570 aParam
.maCellLinks
.push_back(
571 ScOrcusImportXMLParam::CellLink(
572 pUserData
->maLinkedPos
, OUStringToOString(aPath
, RTL_TEXTENCODING_UTF8
)));
576 // Convert range links. For now, an element with range link takes all its
577 // child elements as its fields.
579 std::set
<const SvTreeListEntry
*>::const_iterator it
= maRangeLinks
.begin(), itEnd
= maRangeLinks
.end();
580 for (; it
!= itEnd
; ++it
)
582 const SvTreeListEntry
& rEntry
= **it
;
583 const ScOrcusXMLTreeParam::EntryData
* pUserData
= ScOrcusXMLTreeParam::getUserData(rEntry
);
585 ScOrcusImportXMLParam::RangeLink aRangeLink
;
586 aRangeLink
.maPos
= pUserData
->maLinkedPos
;
588 // Go through all its child elements.
589 getFieldLinks(aRangeLink
, aParam
.maNamespaces
, *mpLbTree
, rEntry
);
591 aParam
.maRangeLinks
.push_back(aRangeLink
);
595 // Remove duplicate namespace IDs.
596 removeDuplicates(aParam
.maNamespaces
);
598 // Now do the import.
599 mpXMLContext
->importXML(aParam
);
601 // Don't forget to broadcast the change.
602 SfxObjectShell
* pShell
= mpDoc
->GetDocumentShell();
603 pShell
->Broadcast(SfxSimpleHint(FID_DATACHANGED
));
605 // Repaint the grid to force repaint the cell values.
606 ScTabViewShell
* pViewShell
= ScTabViewShell::GetActiveViewShell();
608 pViewShell
->PaintGrid();
613 void ScXMLSourceDlg::CancelPressed()
618 void ScXMLSourceDlg::RefEditModified()
620 OUString aRefStr
= mpRefEdit
->GetText();
622 // Check if the address is valid.
623 ScAddress aLinkedPos
;
624 sal_uInt16 nRes
= aLinkedPos
.Parse(aRefStr
, mpDoc
, mpDoc
->GetAddressConvention());
625 bool bValid
= (nRes
& SCA_VALID
) == SCA_VALID
;
627 // TODO: For some unknown reason, setting the ref invalid will hide the text altogether.
628 // Find out how to make this work.
629 // mpRefEdit->SetRefValid(bValid);
632 aLinkedPos
.SetInvalid();
634 // Set this address to the current reference entry.
636 // This should never happen.
639 ScOrcusXMLTreeParam::EntryData
* pUserData
= ScOrcusXMLTreeParam::getUserData(*mpCurRefEntry
);
641 // This should never happen either.
644 bool bRepeatElem
= pUserData
->meType
== ScOrcusXMLTreeParam::ElementRepeat
;
645 pUserData
->maLinkedPos
= aLinkedPos
;
646 pUserData
->mbRangeParent
= aLinkedPos
.IsValid() && bRepeatElem
;
651 maRangeLinks
.insert(mpCurRefEntry
);
653 maRangeLinks
.erase(mpCurRefEntry
);
658 maCellLinks
.insert(mpCurRefEntry
);
660 maCellLinks
.erase(mpCurRefEntry
);
663 // Enable the import button only when at least one link exists.
664 bool bHasLink
= !maCellLinks
.empty() || !maRangeLinks
.empty();
665 mpBtnOk
->Enable(bHasLink
);
668 IMPL_LINK(ScXMLSourceDlg
, GetFocusHdl
, Control
*, pCtrl
)
670 HandleGetFocus(pCtrl
);
674 IMPL_LINK(ScXMLSourceDlg
, BtnPressedHdl
, Button
*, pBtn
)
676 if (pBtn
== mpBtnSelectSource
)
678 else if (pBtn
== mpBtnOk
)
680 else if (pBtn
== mpBtnCancel
)
685 IMPL_LINK_NOARG(ScXMLSourceDlg
, TreeItemSelectHdl
)
691 IMPL_LINK_NOARG(ScXMLSourceDlg
, RefModifiedHdl
)
697 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */