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
);
141 rRange
.aStart
.Format(aStr
, SCA_ABS_3D
, pDoc
, pDoc
->GetAddressConvention());
142 mpActiveEdit
->SetRefString(aStr
);
147 void ScXMLSourceDlg::Deactivate()
149 mbDlgLostFocus
= true;
152 void ScXMLSourceDlg::SetActive()
156 mbDlgLostFocus
= false;
159 mpActiveEdit
->GrabFocus();
170 sal_Bool
ScXMLSourceDlg::Close()
172 return DoClose(ScXMLSourceDlgWrapper::GetChildWindowId());
175 void ScXMLSourceDlg::SelectSourceFile()
177 uno::Reference
<lang::XMultiServiceFactory
> xServiceMgr
= mpDoc
->GetServiceManager();
178 if (!xServiceMgr
.is())
181 uno::Reference
<ui::dialogs::XFilePicker3
> xFilePicker
= ui::dialogs::FilePicker::createWithMode( comphelper::getComponentContext(xServiceMgr
), ui::dialogs::TemplateDescription::FILEOPEN_SIMPLE
);
183 if (maSrcPath
.isEmpty())
185 xFilePicker
->setDisplayDirectory(SvtPathOptions().GetWorkPath());
188 // Use the directory of current source file.
189 INetURLObject
aURL(maSrcPath
);
190 aURL
.removeSegment();
191 aURL
.removeFinalSlash();
192 OUString aPath
= aURL
.GetMainURL(INetURLObject::NO_DECODE
);
193 xFilePicker
->setDisplayDirectory(aPath
);
196 if (xFilePicker
->execute() != ui::dialogs::ExecutableDialogResults::OK
)
197 // File picker dialog cancelled.
200 uno::Sequence
<OUString
> aFiles
= xFilePicker
->getFiles();
201 if (!aFiles
.getLength())
204 // There should only be one file returned from the file picker.
205 maSrcPath
= aFiles
[0];
206 maFtSourceFile
.SetText(maSrcPath
);
207 maHighlightedEntries
.clear();
208 LoadSourceFileStructure(maSrcPath
);
211 void ScXMLSourceDlg::LoadSourceFileStructure(const OUString
& rPath
)
213 ScOrcusFilters
* pOrcus
= ScFormatFilter::Get().GetOrcusFilters();
217 mpXMLContext
.reset(pOrcus
->createXMLContext(*mpDoc
, rPath
));
221 mpXMLContext
->loadXMLStructure(maLbTree
, maXMLParam
);
224 void ScXMLSourceDlg::HandleGetFocus(Control
* pCtrl
)
227 if (pCtrl
== &maRefEdit
|| pCtrl
== &maRefBtn
)
228 mpActiveEdit
= &maRefEdit
;
231 mpActiveEdit
->SetSelection(Selection(0, SELECTION_MAX
));
234 void ScXMLSourceDlg::HandleLoseFocus(Control
* /*pCtrl*/)
240 class UnhighlightEntry
: std::unary_function
<SvTreeListEntry
*, void>
242 SvTreeListBox
& mrTree
;
244 UnhighlightEntry(SvTreeListBox
& rTree
) : mrTree(rTree
) {}
246 void operator() (SvTreeListEntry
* p
)
248 SvViewDataEntry
* pView
= mrTree
.GetViewDataEntry(p
);
252 pView
->SetHighlighted(false);
253 mrTree
.PaintEntry(p
);
258 * When the current entry is a direct or indirect child of a mappable
259 * repeat element entry, that entry becomes the reference entry.
260 * Otherwise the reference entry equals the current entry. A reference
261 * entry is the entry that stores mapped cell position.
263 SvTreeListEntry
* getReferenceEntry(SvTreeListBox
& rTree
, SvTreeListEntry
* pCurEntry
)
265 SvTreeListEntry
* pParent
= rTree
.GetParent(pCurEntry
);
266 SvTreeListEntry
* pRefEntry
= NULL
;
269 ScOrcusXMLTreeParam::EntryData
* pUserData
= ScOrcusXMLTreeParam::getUserData(*pParent
);
270 OSL_ASSERT(pUserData
);
271 if (pUserData
->meType
== ScOrcusXMLTreeParam::ElementRepeat
)
273 // This is a repeat element.
276 // Second repeat element encountered. Not good.
282 pParent
= rTree
.GetParent(pParent
);
285 return pRefEntry
? pRefEntry
: pCurEntry
;
290 void ScXMLSourceDlg::TreeItemSelected()
292 SvTreeListEntry
* pEntry
= maLbTree
.GetCurEntry();
296 if (!maHighlightedEntries
.empty())
298 // Remove highlights from all previously highlighted entries (if any).
299 std::for_each(maHighlightedEntries
.begin(), maHighlightedEntries
.end(), UnhighlightEntry(maLbTree
));
300 maHighlightedEntries
.clear();
303 mpCurRefEntry
= getReferenceEntry(maLbTree
, pEntry
);
305 ScOrcusXMLTreeParam::EntryData
* pUserData
= ScOrcusXMLTreeParam::getUserData(*mpCurRefEntry
);
306 OSL_ASSERT(pUserData
);
308 const ScAddress
& rPos
= pUserData
->maLinkedPos
;
312 rPos
.Format(aStr
, SCA_ABS_3D
, mpDoc
, mpDoc
->GetAddressConvention());
313 maRefEdit
.SetRefString(aStr
);
316 maRefEdit
.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 (maLbTree
.GetChildCount(&rEntry
) > 0)
339 // Only an element with no child elements (leaf element) can be linked.
340 bool bHasChild
= false;
341 ScOrcusXMLTreeParam::EntryData
* pUserData
= NULL
;
342 for (SvTreeListEntry
* pChild
= maLbTree
.FirstChild(&rEntry
); pChild
; pChild
= maLbTree
.NextSibling(pChild
))
344 pUserData
= ScOrcusXMLTreeParam::getUserData(*pChild
);
345 OSL_ASSERT(pUserData
);
346 if (pUserData
->meType
!= ScOrcusXMLTreeParam::Attribute
)
348 // This child is not an attribute. Bail out.
361 // Check all its parents and make sure non of them are range-linked nor
363 if (IsParentDirty(&rEntry
))
372 void ScXMLSourceDlg::RepeatElementSelected(SvTreeListEntry
& rEntry
)
374 // Check all its parents first.
376 if (IsParentDirty(&rEntry
))
382 // Check all its child elements / attributes and make sure non of them are
383 // linked or repeat elements. In the future we will support range linking
384 // of repeat element who has another repeat elements. But first I need to
385 // support that scenario in orcus.
387 if (IsChildrenDirty(&rEntry
))
393 SvViewDataEntry
* p
= maLbTree
.GetViewDataEntry(&rEntry
);
394 if (!p
->IsHighlighted())
396 // Highlight the entry if not highlighted already. This can happen
397 // when the current entry is a child entry of a repeat element entry.
398 p
->SetHighlighted(true);
399 maLbTree
.PaintEntry(&rEntry
);
400 maHighlightedEntries
.push_back(&rEntry
);
403 SelectAllChildEntries(rEntry
);
407 void ScXMLSourceDlg::AttributeSelected(SvTreeListEntry
& rEntry
)
409 // Check all its parent elements and make sure non of them are linked nor
410 // repeat elements. In attribute's case, it's okay to have the immediate
411 // parent element linked (but not range-linked).
413 SvTreeListEntry
* pParent
= maLbTree
.GetParent(&rEntry
);
414 OSL_ASSERT(pParent
); // attribute should have a parent element.
416 ScOrcusXMLTreeParam::EntryData
* pUserData
= ScOrcusXMLTreeParam::getUserData(*pParent
);
417 OSL_ASSERT(pUserData
);
418 if (pUserData
->maLinkedPos
.IsValid() && pUserData
->mbRangeParent
)
420 // Parent element is range-linked. Bail out.
425 if (IsParentDirty(&rEntry
))
434 void ScXMLSourceDlg::SetNonLinkable()
436 maFtMappedCellTitle
.Disable();
441 void ScXMLSourceDlg::SetSingleLinkable()
443 maFtMappedCellTitle
.Enable();
448 void ScXMLSourceDlg::SetRangeLinkable()
450 maFtMappedCellTitle
.Enable();
455 void ScXMLSourceDlg::SelectAllChildEntries(SvTreeListEntry
& rEntry
)
457 SvTreeListEntries
& rChildren
= rEntry
.GetChildEntries();
458 SvTreeListEntries::iterator it
= rChildren
.begin(), itEnd
= rChildren
.end();
459 for (; it
!= itEnd
; ++it
)
461 SvTreeListEntry
& r
= *it
;
462 SelectAllChildEntries(r
); // select recursively.
463 SvViewDataEntry
* p
= maLbTree
.GetViewDataEntry(&r
);
464 p
->SetHighlighted(true);
465 maLbTree
.PaintEntry(&r
);
466 maHighlightedEntries
.push_back(&r
);
470 bool ScXMLSourceDlg::IsParentDirty(SvTreeListEntry
* pEntry
) const
472 ScOrcusXMLTreeParam::EntryData
* pUserData
= NULL
;
473 SvTreeListEntry
* pParent
= maLbTree
.GetParent(pEntry
);
476 pUserData
= ScOrcusXMLTreeParam::getUserData(*pParent
);
477 OSL_ASSERT(pUserData
);
478 if (pUserData
->maLinkedPos
.IsValid())
480 // This parent is already linked.
483 if (pUserData
->meType
== ScOrcusXMLTreeParam::ElementRepeat
)
485 // This is a repeat element.
488 pParent
= maLbTree
.GetParent(pParent
);
493 bool ScXMLSourceDlg::IsChildrenDirty(SvTreeListEntry
* pEntry
) const
495 ScOrcusXMLTreeParam::EntryData
* pUserData
= NULL
;
496 for (SvTreeListEntry
* pChild
= maLbTree
.FirstChild(pEntry
); pChild
; pChild
= maLbTree
.NextSibling(pChild
))
498 pUserData
= ScOrcusXMLTreeParam::getUserData(*pChild
);
499 OSL_ASSERT(pUserData
);
500 if (pUserData
->maLinkedPos
.IsValid())
504 if (pUserData
->meType
== ScOrcusXMLTreeParam::ElementRepeat
)
505 // We don't support linking of nested repeat elements (yet).
508 if (pUserData
->meType
== ScOrcusXMLTreeParam::ElementDefault
)
510 // Check recursively.
511 if (IsChildrenDirty(pChild
))
522 * Pick only the leaf elements.
525 ScOrcusImportXMLParam::RangeLink
& rRangeLink
, std::vector
<size_t>& rNamespaces
,
526 const SvTreeListBox
& rTree
, const SvTreeListEntry
& rEntry
)
528 const SvTreeListEntries
& rChildren
= rEntry
.GetChildEntries();
529 if (rChildren
.empty())
530 // No more children. We're done.
533 SvTreeListEntries::const_iterator it
= rChildren
.begin(), itEnd
= rChildren
.end();
534 for (; it
!= itEnd
; ++it
)
536 const SvTreeListEntry
& rChild
= *it
;
537 OUString aPath
= getXPath(rTree
, rChild
, rNamespaces
);
538 const ScOrcusXMLTreeParam::EntryData
* pUserData
= ScOrcusXMLTreeParam::getUserData(rChild
);
540 if (pUserData
&& pUserData
->mbLeafNode
)
542 if (!aPath
.isEmpty())
543 // XPath should never be empty anyway, but it won't hurt to check...
544 rRangeLink
.maFieldPaths
.push_back(OUStringToOString(aPath
, RTL_TEXTENCODING_UTF8
));
548 getFieldLinks(rRangeLink
, rNamespaces
, rTree
, rChild
);
552 void removeDuplicates(std::vector
<size_t>& rArray
)
554 std::sort(rArray
.begin(), rArray
.end());
555 std::vector
<size_t>::iterator it
= std::unique(rArray
.begin(), rArray
.end());
556 rArray
.erase(it
, rArray
.end());
561 void ScXMLSourceDlg::OkPressed()
568 ScOrcusImportXMLParam aParam
;
570 // Convert single cell links.
572 std::set
<const SvTreeListEntry
*>::const_iterator it
= maCellLinks
.begin(), itEnd
= maCellLinks
.end();
573 for (; it
!= itEnd
; ++it
)
575 const SvTreeListEntry
& rEntry
= **it
;
576 OUString aPath
= getXPath(maLbTree
, rEntry
, aParam
.maNamespaces
);
577 const ScOrcusXMLTreeParam::EntryData
* pUserData
= ScOrcusXMLTreeParam::getUserData(rEntry
);
579 aParam
.maCellLinks
.push_back(
580 ScOrcusImportXMLParam::CellLink(
581 pUserData
->maLinkedPos
, OUStringToOString(aPath
, RTL_TEXTENCODING_UTF8
)));
585 // Convert range links. For now, an element with range link takes all its
586 // child elements as its fields.
588 std::set
<const SvTreeListEntry
*>::const_iterator it
= maRangeLinks
.begin(), itEnd
= maRangeLinks
.end();
589 for (; it
!= itEnd
; ++it
)
591 const SvTreeListEntry
& rEntry
= **it
;
592 const ScOrcusXMLTreeParam::EntryData
* pUserData
= ScOrcusXMLTreeParam::getUserData(rEntry
);
594 ScOrcusImportXMLParam::RangeLink aRangeLink
;
595 aRangeLink
.maPos
= pUserData
->maLinkedPos
;
597 // Go through all its child elements.
598 getFieldLinks(aRangeLink
, aParam
.maNamespaces
, maLbTree
, rEntry
);
600 aParam
.maRangeLinks
.push_back(aRangeLink
);
604 // Remove duplicate namespace IDs.
605 removeDuplicates(aParam
.maNamespaces
);
607 // Now do the import.
608 mpXMLContext
->importXML(aParam
);
610 // Don't forget to broadcast the change.
611 SfxObjectShell
* pShell
= mpDoc
->GetDocumentShell();
612 pShell
->Broadcast(SfxSimpleHint(FID_DATACHANGED
));
614 // Repaint the grid to force repaint the cell values.
615 ScTabViewShell
* pViewShell
= ScTabViewShell::GetActiveViewShell();
617 pViewShell
->PaintGrid();
622 void ScXMLSourceDlg::CancelPressed()
627 void ScXMLSourceDlg::RefEditModified()
629 OUString aRefStr
= maRefEdit
.GetText();
631 // Check if the address is valid.
632 ScAddress aLinkedPos
;
633 sal_uInt16 nRes
= aLinkedPos
.Parse(aRefStr
, mpDoc
, mpDoc
->GetAddressConvention());
634 bool bValid
= (nRes
& SCA_VALID
) == SCA_VALID
;
636 // TODO: For some unknown reason, setting the ref invalid will hide the text altogether.
637 // Find out how to make this work.
638 // maRefEdit.SetRefValid(bValid);
641 aLinkedPos
.SetInvalid();
643 // Set this address to the current reference entry.
645 // This should never happen.
648 ScOrcusXMLTreeParam::EntryData
* pUserData
= ScOrcusXMLTreeParam::getUserData(*mpCurRefEntry
);
650 // This should never happen either.
653 bool bRepeatElem
= pUserData
->meType
== ScOrcusXMLTreeParam::ElementRepeat
;
654 pUserData
->maLinkedPos
= aLinkedPos
;
655 pUserData
->mbRangeParent
= aLinkedPos
.IsValid() && bRepeatElem
;
660 maRangeLinks
.insert(mpCurRefEntry
);
662 maRangeLinks
.erase(mpCurRefEntry
);
667 maCellLinks
.insert(mpCurRefEntry
);
669 maCellLinks
.erase(mpCurRefEntry
);
672 // Enable the import button only when at least one link exists.
673 bool bHasLink
= !maCellLinks
.empty() || !maRangeLinks
.empty();
674 maBtnOk
.Enable(bHasLink
);
677 IMPL_LINK(ScXMLSourceDlg
, GetFocusHdl
, Control
*, pCtrl
)
679 HandleGetFocus(pCtrl
);
683 IMPL_LINK(ScXMLSourceDlg
, LoseFocusHdl
, Control
*, pCtrl
)
685 HandleLoseFocus(pCtrl
);
689 IMPL_LINK(ScXMLSourceDlg
, BtnPressedHdl
, Button
*, pBtn
)
691 if (pBtn
== &maBtnSelectSource
)
693 else if (pBtn
== &maBtnOk
)
695 else if (pBtn
== &maBtnCancel
)
700 IMPL_LINK_NOARG(ScXMLSourceDlg
, TreeItemSelectHdl
)
706 IMPL_LINK_NOARG(ScXMLSourceDlg
, RefModifiedHdl
)
712 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */