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"
11 #include "xmlsourcedlg.hrc"
13 #include "scresid.hxx"
14 #include "document.hxx"
15 #include "orcusfilters.hxx"
17 #include "reffact.hxx"
18 #include "tabvwsh.hxx"
20 #include "unotools/pathoptions.hxx"
21 #include "tools/urlobj.hxx"
22 #include "svtools/svlbitm.hxx"
23 #include "svtools/treelistentry.hxx"
24 #include "svtools/viewdataentry.hxx"
25 #include "sfx2/objsh.hxx"
27 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
28 #include <com/sun/star/ui/dialogs/FilePicker.hpp>
29 #include <com/sun/star/ui/dialogs/ExecutableDialogResults.hpp>
30 #include <com/sun/star/ui/dialogs/TemplateDescription.hpp>
32 using namespace com::sun::star
;
36 bool isAttribute(const SvTreeListEntry
& rEntry
)
38 const ScOrcusXMLTreeParam::EntryData
* pUserData
= ScOrcusXMLTreeParam::getUserData(rEntry
);
42 return pUserData
->meType
== ScOrcusXMLTreeParam::Attribute
;
46 const SvTreeListBox
& rTree
, const SvTreeListEntry
& rEntry
, std::vector
<size_t>& rNamespaces
)
49 for (const SvTreeListEntry
* p
= &rEntry
; p
; p
= rTree
.GetParent(p
))
51 const SvLBoxItem
* pItem
= p
->GetFirstItem(SV_ITEM_ID_LBOXSTRING
);
55 // Collect used namespace.
56 const ScOrcusXMLTreeParam::EntryData
* pData
= ScOrcusXMLTreeParam::getUserData(*p
);
58 rNamespaces
.push_back(pData
->mnNamespaceID
);
60 const SvLBoxString
* pStr
= static_cast<const SvLBoxString
*>(pItem
);
61 aBuf
.insert(0, pStr
->GetText());
62 aBuf
.insert(0, isAttribute(*p
) ? '@' : '/');
65 return aBuf
.makeStringAndClear();
70 ScXMLSourceTree::ScXMLSourceTree(Window
* pParent
, const ResId
& rResId
) :
71 SvTreeListBox(pParent
, rResId
) {}
73 ScXMLSourceDlg::ScXMLSourceDlg(
74 SfxBindings
* pB
, SfxChildWindow
* pCW
, Window
* pParent
, ScDocument
* pDoc
) :
75 ScAnyRefDlg(pB
, pCW
, pParent
, RID_SCDLG_XML_SOURCE
),
76 maFlSourceFile(this, ScResId(FL_SOURCE_FILE
)),
77 maBtnSelectSource(this, ScResId(BTN_SELECT_SOURCE_FILE
)),
78 maFtSourceFile(this, ScResId(FT_SOURCE_FILE
)),
79 maFtMapXmlDoc(this, ScResId(FL_MAP_XML_TO_DOCUMENT
)),
80 maFtMappedCellTitle(this, ScResId(FT_MAPPED_CELL_TITLE
)),
81 maLbTree(this, ScResId(LB_SOURCE_TREE
)),
82 maRefEdit(this, this, NULL
, ScResId(ED_MAPPED_CELL
)),
83 maRefBtn(this, ScResId(BTN_MAPPED_CELL
), &maRefEdit
, this),
84 maBtnOk(this, ScResId(BTN_OK
)),
85 maBtnCancel(this, ScResId(BTN_CANCEL
)),
86 maImgFileOpen(ScResId(IMG_FILE_OPEN
)),
89 mpActiveEdit(&maRefEdit
),
92 maXMLParam
.maImgElementDefault
= Image(ScResId(IMG_ELEMENT_DEFAULT
));
93 maXMLParam
.maImgElementRepeat
= Image(ScResId(IMG_ELEMENT_REPEAT
));
94 maXMLParam
.maImgAttribute
= Image(ScResId(IMG_ELEMENT_ATTRIBUTE
));
96 maBtnSelectSource
.SetModeImage(maImgFileOpen
);
99 Link aBtnHdl
= LINK(this, ScXMLSourceDlg
, BtnPressedHdl
);
100 maBtnSelectSource
.SetClickHdl(aBtnHdl
);
101 maBtnOk
.SetClickHdl(aBtnHdl
);
102 maBtnCancel
.SetClickHdl(aBtnHdl
);
104 Link aLink
= LINK(this, ScXMLSourceDlg
, GetFocusHdl
);
105 maRefEdit
.SetGetFocusHdl(aLink
);
106 maRefBtn
.SetGetFocusHdl(aLink
);
107 aLink
= LINK(this, ScXMLSourceDlg
, LoseFocusHdl
);
108 maRefEdit
.SetLoseFocusHdl(aLink
);
109 maRefBtn
.SetLoseFocusHdl(aLink
);
111 aLink
= LINK(this, ScXMLSourceDlg
, TreeItemSelectHdl
);
112 maLbTree
.SetSelectHdl(aLink
);
114 aLink
= LINK(this, ScXMLSourceDlg
, RefModifiedHdl
);
115 maRefEdit
.SetModifyHdl(aLink
);
120 maBtnSelectSource
.GrabFocus(); // Initial focus is on the select source button.
123 ScXMLSourceDlg::~ScXMLSourceDlg()
127 sal_Bool
ScXMLSourceDlg::IsRefInputMode() const
129 return mpActiveEdit
!= NULL
&& mpActiveEdit
->IsEnabled();
132 void ScXMLSourceDlg::SetReference(const ScRange
& rRange
, ScDocument
* pDoc
)
137 if (rRange
.aStart
!= rRange
.aEnd
)
138 RefInputStart(mpActiveEdit
);
140 OUString
aStr(rRange
.aStart
.Format(SCA_ABS_3D
, pDoc
, pDoc
->GetAddressConvention()));
141 mpActiveEdit
->SetRefString(aStr
);
146 void ScXMLSourceDlg::Deactivate()
148 mbDlgLostFocus
= true;
151 void ScXMLSourceDlg::SetActive()
155 mbDlgLostFocus
= false;
158 mpActiveEdit
->GrabFocus();
169 sal_Bool
ScXMLSourceDlg::Close()
171 return DoClose(ScXMLSourceDlgWrapper::GetChildWindowId());
174 void ScXMLSourceDlg::SelectSourceFile()
176 uno::Reference
<ui::dialogs::XFilePicker3
> xFilePicker
= ui::dialogs::FilePicker::createWithMode( comphelper::getProcessComponentContext(), ui::dialogs::TemplateDescription::FILEOPEN_SIMPLE
);
178 if (maSrcPath
.isEmpty())
180 xFilePicker
->setDisplayDirectory(SvtPathOptions().GetWorkPath());
183 // Use the directory of current source file.
184 INetURLObject
aURL(maSrcPath
);
185 aURL
.removeSegment();
186 aURL
.removeFinalSlash();
187 OUString aPath
= aURL
.GetMainURL(INetURLObject::NO_DECODE
);
188 xFilePicker
->setDisplayDirectory(aPath
);
191 if (xFilePicker
->execute() != ui::dialogs::ExecutableDialogResults::OK
)
192 // File picker dialog cancelled.
195 uno::Sequence
<OUString
> aFiles
= xFilePicker
->getFiles();
196 if (!aFiles
.getLength())
199 // There should only be one file returned from the file picker.
200 maSrcPath
= aFiles
[0];
201 maFtSourceFile
.SetText(maSrcPath
);
202 maHighlightedEntries
.clear();
203 LoadSourceFileStructure(maSrcPath
);
206 void ScXMLSourceDlg::LoadSourceFileStructure(const OUString
& rPath
)
208 ScOrcusFilters
* pOrcus
= ScFormatFilter::Get().GetOrcusFilters();
212 mpXMLContext
.reset(pOrcus
->createXMLContext(*mpDoc
, rPath
));
216 mpXMLContext
->loadXMLStructure(maLbTree
, maXMLParam
);
219 void ScXMLSourceDlg::HandleGetFocus(Control
* pCtrl
)
222 if (pCtrl
== &maRefEdit
|| pCtrl
== &maRefBtn
)
223 mpActiveEdit
= &maRefEdit
;
226 mpActiveEdit
->SetSelection(Selection(0, SELECTION_MAX
));
229 void ScXMLSourceDlg::HandleLoseFocus(Control
* /*pCtrl*/)
235 class UnhighlightEntry
: std::unary_function
<SvTreeListEntry
*, void>
237 SvTreeListBox
& mrTree
;
239 UnhighlightEntry(SvTreeListBox
& rTree
) : mrTree(rTree
) {}
241 void operator() (SvTreeListEntry
* p
)
243 SvViewDataEntry
* pView
= mrTree
.GetViewDataEntry(p
);
247 pView
->SetHighlighted(false);
248 mrTree
.PaintEntry(p
);
253 * When the current entry is a direct or indirect child of a mappable
254 * repeat element entry, that entry becomes the reference entry.
255 * Otherwise the reference entry equals the current entry. A reference
256 * entry is the entry that stores mapped cell position.
258 SvTreeListEntry
* getReferenceEntry(SvTreeListBox
& rTree
, SvTreeListEntry
* pCurEntry
)
260 SvTreeListEntry
* pParent
= rTree
.GetParent(pCurEntry
);
261 SvTreeListEntry
* pRefEntry
= NULL
;
264 ScOrcusXMLTreeParam::EntryData
* pUserData
= ScOrcusXMLTreeParam::getUserData(*pParent
);
265 OSL_ASSERT(pUserData
);
266 if (pUserData
->meType
== ScOrcusXMLTreeParam::ElementRepeat
)
268 // This is a repeat element.
271 // Second repeat element encountered. Not good.
277 pParent
= rTree
.GetParent(pParent
);
280 return pRefEntry
? pRefEntry
: pCurEntry
;
285 void ScXMLSourceDlg::TreeItemSelected()
287 SvTreeListEntry
* pEntry
= maLbTree
.GetCurEntry();
291 if (!maHighlightedEntries
.empty())
293 // Remove highlights from all previously highlighted entries (if any).
294 std::for_each(maHighlightedEntries
.begin(), maHighlightedEntries
.end(), UnhighlightEntry(maLbTree
));
295 maHighlightedEntries
.clear();
298 mpCurRefEntry
= getReferenceEntry(maLbTree
, pEntry
);
300 ScOrcusXMLTreeParam::EntryData
* pUserData
= ScOrcusXMLTreeParam::getUserData(*mpCurRefEntry
);
301 OSL_ASSERT(pUserData
);
303 const ScAddress
& rPos
= pUserData
->maLinkedPos
;
306 OUString
aStr(rPos
.Format(SCA_ABS_3D
, mpDoc
, mpDoc
->GetAddressConvention()));
307 maRefEdit
.SetRefString(aStr
);
310 maRefEdit
.SetRefString(OUString());
312 switch (pUserData
->meType
)
314 case ScOrcusXMLTreeParam::Attribute
:
315 AttributeSelected(*mpCurRefEntry
);
317 case ScOrcusXMLTreeParam::ElementDefault
:
318 DefaultElementSelected(*mpCurRefEntry
);
320 case ScOrcusXMLTreeParam::ElementRepeat
:
321 RepeatElementSelected(*mpCurRefEntry
);
328 void ScXMLSourceDlg::DefaultElementSelected(SvTreeListEntry
& rEntry
)
331 if (maLbTree
.GetChildCount(&rEntry
) > 0)
333 // Only an element with no child elements (leaf element) can be linked.
334 bool bHasChild
= false;
335 ScOrcusXMLTreeParam::EntryData
* pUserData
= NULL
;
336 for (SvTreeListEntry
* pChild
= maLbTree
.FirstChild(&rEntry
); pChild
; pChild
= maLbTree
.NextSibling(pChild
))
338 pUserData
= ScOrcusXMLTreeParam::getUserData(*pChild
);
339 OSL_ASSERT(pUserData
);
340 if (pUserData
->meType
!= ScOrcusXMLTreeParam::Attribute
)
342 // This child is not an attribute. Bail out.
355 // Check all its parents and make sure non of them are range-linked nor
357 if (IsParentDirty(&rEntry
))
366 void ScXMLSourceDlg::RepeatElementSelected(SvTreeListEntry
& rEntry
)
368 // Check all its parents first.
370 if (IsParentDirty(&rEntry
))
376 // Check all its child elements / attributes and make sure non of them are
377 // linked or repeat elements. In the future we will support range linking
378 // of repeat element who has another repeat elements. But first I need to
379 // support that scenario in orcus.
381 if (IsChildrenDirty(&rEntry
))
387 SvViewDataEntry
* p
= maLbTree
.GetViewDataEntry(&rEntry
);
388 if (!p
->IsHighlighted())
390 // Highlight the entry if not highlighted already. This can happen
391 // when the current entry is a child entry of a repeat element entry.
392 p
->SetHighlighted(true);
393 maLbTree
.PaintEntry(&rEntry
);
394 maHighlightedEntries
.push_back(&rEntry
);
397 SelectAllChildEntries(rEntry
);
401 void ScXMLSourceDlg::AttributeSelected(SvTreeListEntry
& rEntry
)
403 // Check all its parent elements and make sure non of them are linked nor
404 // repeat elements. In attribute's case, it's okay to have the immediate
405 // parent element linked (but not range-linked).
407 SvTreeListEntry
* pParent
= maLbTree
.GetParent(&rEntry
);
408 OSL_ASSERT(pParent
); // attribute should have a parent element.
410 ScOrcusXMLTreeParam::EntryData
* pUserData
= ScOrcusXMLTreeParam::getUserData(*pParent
);
411 OSL_ASSERT(pUserData
);
412 if (pUserData
->maLinkedPos
.IsValid() && pUserData
->mbRangeParent
)
414 // Parent element is range-linked. Bail out.
419 if (IsParentDirty(&rEntry
))
428 void ScXMLSourceDlg::SetNonLinkable()
430 maFtMappedCellTitle
.Disable();
435 void ScXMLSourceDlg::SetSingleLinkable()
437 maFtMappedCellTitle
.Enable();
442 void ScXMLSourceDlg::SetRangeLinkable()
444 maFtMappedCellTitle
.Enable();
449 void ScXMLSourceDlg::SelectAllChildEntries(SvTreeListEntry
& rEntry
)
451 SvTreeListEntries
& rChildren
= rEntry
.GetChildEntries();
452 SvTreeListEntries::iterator it
= rChildren
.begin(), itEnd
= rChildren
.end();
453 for (; it
!= itEnd
; ++it
)
455 SvTreeListEntry
& r
= *it
;
456 SelectAllChildEntries(r
); // select recursively.
457 SvViewDataEntry
* p
= maLbTree
.GetViewDataEntry(&r
);
458 p
->SetHighlighted(true);
459 maLbTree
.PaintEntry(&r
);
460 maHighlightedEntries
.push_back(&r
);
464 bool ScXMLSourceDlg::IsParentDirty(SvTreeListEntry
* pEntry
) const
466 ScOrcusXMLTreeParam::EntryData
* pUserData
= NULL
;
467 SvTreeListEntry
* pParent
= maLbTree
.GetParent(pEntry
);
470 pUserData
= ScOrcusXMLTreeParam::getUserData(*pParent
);
471 OSL_ASSERT(pUserData
);
472 if (pUserData
->maLinkedPos
.IsValid())
474 // This parent is already linked.
477 if (pUserData
->meType
== ScOrcusXMLTreeParam::ElementRepeat
)
479 // This is a repeat element.
482 pParent
= maLbTree
.GetParent(pParent
);
487 bool ScXMLSourceDlg::IsChildrenDirty(SvTreeListEntry
* pEntry
) const
489 ScOrcusXMLTreeParam::EntryData
* pUserData
= NULL
;
490 for (SvTreeListEntry
* pChild
= maLbTree
.FirstChild(pEntry
); pChild
; pChild
= maLbTree
.NextSibling(pChild
))
492 pUserData
= ScOrcusXMLTreeParam::getUserData(*pChild
);
493 OSL_ASSERT(pUserData
);
494 if (pUserData
->maLinkedPos
.IsValid())
498 if (pUserData
->meType
== ScOrcusXMLTreeParam::ElementRepeat
)
499 // We don't support linking of nested repeat elements (yet).
502 if (pUserData
->meType
== ScOrcusXMLTreeParam::ElementDefault
)
504 // Check recursively.
505 if (IsChildrenDirty(pChild
))
516 * Pick only the leaf elements.
519 ScOrcusImportXMLParam::RangeLink
& rRangeLink
, std::vector
<size_t>& rNamespaces
,
520 const SvTreeListBox
& rTree
, const SvTreeListEntry
& rEntry
)
522 const SvTreeListEntries
& rChildren
= rEntry
.GetChildEntries();
523 if (rChildren
.empty())
524 // No more children. We're done.
527 SvTreeListEntries::const_iterator it
= rChildren
.begin(), itEnd
= rChildren
.end();
528 for (; it
!= itEnd
; ++it
)
530 const SvTreeListEntry
& rChild
= *it
;
531 OUString aPath
= getXPath(rTree
, rChild
, rNamespaces
);
532 const ScOrcusXMLTreeParam::EntryData
* pUserData
= ScOrcusXMLTreeParam::getUserData(rChild
);
534 if (pUserData
&& pUserData
->mbLeafNode
)
536 if (!aPath
.isEmpty())
537 // XPath should never be empty anyway, but it won't hurt to check...
538 rRangeLink
.maFieldPaths
.push_back(OUStringToOString(aPath
, RTL_TEXTENCODING_UTF8
));
542 getFieldLinks(rRangeLink
, rNamespaces
, rTree
, rChild
);
546 void removeDuplicates(std::vector
<size_t>& rArray
)
548 std::sort(rArray
.begin(), rArray
.end());
549 std::vector
<size_t>::iterator it
= std::unique(rArray
.begin(), rArray
.end());
550 rArray
.erase(it
, rArray
.end());
555 void ScXMLSourceDlg::OkPressed()
562 ScOrcusImportXMLParam aParam
;
564 // Convert single cell links.
566 std::set
<const SvTreeListEntry
*>::const_iterator it
= maCellLinks
.begin(), itEnd
= maCellLinks
.end();
567 for (; it
!= itEnd
; ++it
)
569 const SvTreeListEntry
& rEntry
= **it
;
570 OUString aPath
= getXPath(maLbTree
, rEntry
, aParam
.maNamespaces
);
571 const ScOrcusXMLTreeParam::EntryData
* pUserData
= ScOrcusXMLTreeParam::getUserData(rEntry
);
573 aParam
.maCellLinks
.push_back(
574 ScOrcusImportXMLParam::CellLink(
575 pUserData
->maLinkedPos
, OUStringToOString(aPath
, RTL_TEXTENCODING_UTF8
)));
579 // Convert range links. For now, an element with range link takes all its
580 // child elements as its fields.
582 std::set
<const SvTreeListEntry
*>::const_iterator it
= maRangeLinks
.begin(), itEnd
= maRangeLinks
.end();
583 for (; it
!= itEnd
; ++it
)
585 const SvTreeListEntry
& rEntry
= **it
;
586 const ScOrcusXMLTreeParam::EntryData
* pUserData
= ScOrcusXMLTreeParam::getUserData(rEntry
);
588 ScOrcusImportXMLParam::RangeLink aRangeLink
;
589 aRangeLink
.maPos
= pUserData
->maLinkedPos
;
591 // Go through all its child elements.
592 getFieldLinks(aRangeLink
, aParam
.maNamespaces
, maLbTree
, rEntry
);
594 aParam
.maRangeLinks
.push_back(aRangeLink
);
598 // Remove duplicate namespace IDs.
599 removeDuplicates(aParam
.maNamespaces
);
601 // Now do the import.
602 mpXMLContext
->importXML(aParam
);
604 // Don't forget to broadcast the change.
605 SfxObjectShell
* pShell
= mpDoc
->GetDocumentShell();
606 pShell
->Broadcast(SfxSimpleHint(FID_DATACHANGED
));
608 // Repaint the grid to force repaint the cell values.
609 ScTabViewShell
* pViewShell
= ScTabViewShell::GetActiveViewShell();
611 pViewShell
->PaintGrid();
616 void ScXMLSourceDlg::CancelPressed()
621 void ScXMLSourceDlg::RefEditModified()
623 OUString aRefStr
= maRefEdit
.GetText();
625 // Check if the address is valid.
626 ScAddress aLinkedPos
;
627 sal_uInt16 nRes
= aLinkedPos
.Parse(aRefStr
, mpDoc
, mpDoc
->GetAddressConvention());
628 bool bValid
= (nRes
& SCA_VALID
) == SCA_VALID
;
630 // TODO: For some unknown reason, setting the ref invalid will hide the text altogether.
631 // Find out how to make this work.
632 // maRefEdit.SetRefValid(bValid);
635 aLinkedPos
.SetInvalid();
637 // Set this address to the current reference entry.
639 // This should never happen.
642 ScOrcusXMLTreeParam::EntryData
* pUserData
= ScOrcusXMLTreeParam::getUserData(*mpCurRefEntry
);
644 // This should never happen either.
647 bool bRepeatElem
= pUserData
->meType
== ScOrcusXMLTreeParam::ElementRepeat
;
648 pUserData
->maLinkedPos
= aLinkedPos
;
649 pUserData
->mbRangeParent
= aLinkedPos
.IsValid() && bRepeatElem
;
654 maRangeLinks
.insert(mpCurRefEntry
);
656 maRangeLinks
.erase(mpCurRefEntry
);
661 maCellLinks
.insert(mpCurRefEntry
);
663 maCellLinks
.erase(mpCurRefEntry
);
666 // Enable the import button only when at least one link exists.
667 bool bHasLink
= !maCellLinks
.empty() || !maRangeLinks
.empty();
668 maBtnOk
.Enable(bHasLink
);
671 IMPL_LINK(ScXMLSourceDlg
, GetFocusHdl
, Control
*, pCtrl
)
673 HandleGetFocus(pCtrl
);
677 IMPL_LINK(ScXMLSourceDlg
, LoseFocusHdl
, Control
*, pCtrl
)
679 HandleLoseFocus(pCtrl
);
683 IMPL_LINK(ScXMLSourceDlg
, BtnPressedHdl
, Button
*, pBtn
)
685 if (pBtn
== &maBtnSelectSource
)
687 else if (pBtn
== &maBtnOk
)
689 else if (pBtn
== &maBtnCancel
)
694 IMPL_LINK_NOARG(ScXMLSourceDlg
, TreeItemSelectHdl
)
700 IMPL_LINK_NOARG(ScXMLSourceDlg
, RefModifiedHdl
)
706 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */