bump product version to 4.1.6.2
[LibreOffice.git] / sc / source / ui / xmlsource / xmlsourcedlg.cxx
blob952ac3c57d81d0093d5f138deecb9ab4d08408d0
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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/.
8 */
10 #include "xmlsourcedlg.hxx"
11 #include "xmlsourcedlg.hrc"
13 #include "scresid.hxx"
14 #include "document.hxx"
15 #include "orcusfilters.hxx"
16 #include "filter.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;
34 namespace {
36 bool isAttribute(const SvTreeListEntry& rEntry)
38 const ScOrcusXMLTreeParam::EntryData* pUserData = ScOrcusXMLTreeParam::getUserData(rEntry);
39 if (!pUserData)
40 return false;
42 return pUserData->meType == ScOrcusXMLTreeParam::Attribute;
45 OUString getXPath(
46 const SvTreeListBox& rTree, const SvTreeListEntry& rEntry, std::vector<size_t>& rNamespaces)
48 OUStringBuffer aBuf;
49 for (const SvTreeListEntry* p = &rEntry; p; p = rTree.GetParent(p))
51 const SvLBoxItem* pItem = p->GetFirstItem(SV_ITEM_ID_LBOXSTRING);
52 if (!pItem)
53 continue;
55 // Collect used namespace.
56 const ScOrcusXMLTreeParam::EntryData* pData = ScOrcusXMLTreeParam::getUserData(*p);
57 if (pData)
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)),
87 mpCurRefEntry(NULL),
88 mpDoc(pDoc),
89 mpActiveEdit(&maRefEdit),
90 mbDlgLostFocus(false)
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);
97 FreeResource();
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);
117 maBtnOk.Disable();
119 SetNonLinkable();
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)
134 if (!mpActiveEdit)
135 return;
137 if (rRange.aStart != rRange.aEnd)
138 RefInputStart(mpActiveEdit);
140 OUString aStr;
141 rRange.aStart.Format(aStr, SCA_ABS_3D, pDoc, pDoc->GetAddressConvention());
142 mpActiveEdit->SetRefString(aStr);
144 RefEditModified();
147 void ScXMLSourceDlg::Deactivate()
149 mbDlgLostFocus = true;
152 void ScXMLSourceDlg::SetActive()
154 if (mbDlgLostFocus)
156 mbDlgLostFocus = false;
157 if (mpActiveEdit)
159 mpActiveEdit->GrabFocus();
162 else
164 GrabFocus();
167 RefInputDone();
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())
179 return;
181 uno::Reference<ui::dialogs::XFilePicker3> xFilePicker = ui::dialogs::FilePicker::createWithMode( comphelper::getComponentContext(xServiceMgr), ui::dialogs::TemplateDescription::FILEOPEN_SIMPLE );
183 if (maSrcPath.isEmpty())
184 // Use default path.
185 xFilePicker->setDisplayDirectory(SvtPathOptions().GetWorkPath());
186 else
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.
198 return;
200 uno::Sequence<OUString> aFiles = xFilePicker->getFiles();
201 if (!aFiles.getLength())
202 return;
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();
214 if (!pOrcus)
215 return;
217 mpXMLContext.reset(pOrcus->createXMLContext(*mpDoc, rPath));
218 if (!mpXMLContext)
219 return;
221 mpXMLContext->loadXMLStructure(maLbTree, maXMLParam);
224 void ScXMLSourceDlg::HandleGetFocus(Control* pCtrl)
226 mpActiveEdit = NULL;
227 if (pCtrl == &maRefEdit || pCtrl == &maRefBtn)
228 mpActiveEdit = &maRefEdit;
230 if (mpActiveEdit)
231 mpActiveEdit->SetSelection(Selection(0, SELECTION_MAX));
234 void ScXMLSourceDlg::HandleLoseFocus(Control* /*pCtrl*/)
238 namespace {
240 class UnhighlightEntry : std::unary_function<SvTreeListEntry*, void>
242 SvTreeListBox& mrTree;
243 public:
244 UnhighlightEntry(SvTreeListBox& rTree) : mrTree(rTree) {}
246 void operator() (SvTreeListEntry* p)
248 SvViewDataEntry* pView = mrTree.GetViewDataEntry(p);
249 if (!pView)
250 return;
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;
267 while (pParent)
269 ScOrcusXMLTreeParam::EntryData* pUserData = ScOrcusXMLTreeParam::getUserData(*pParent);
270 OSL_ASSERT(pUserData);
271 if (pUserData->meType == ScOrcusXMLTreeParam::ElementRepeat)
273 // This is a repeat element.
274 if (pRefEntry)
276 // Second repeat element encountered. Not good.
277 return pCurEntry;
280 pRefEntry = pParent;
282 pParent = rTree.GetParent(pParent);
285 return pRefEntry ? pRefEntry : pCurEntry;
290 void ScXMLSourceDlg::TreeItemSelected()
292 SvTreeListEntry* pEntry = maLbTree.GetCurEntry();
293 if (!pEntry)
294 return;
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;
309 if (rPos.IsValid())
311 OUString aStr;
312 rPos.Format(aStr, SCA_ABS_3D, mpDoc, mpDoc->GetAddressConvention());
313 maRefEdit.SetRefString(aStr);
315 else
316 maRefEdit.SetRefString(OUString());
318 switch (pUserData->meType)
320 case ScOrcusXMLTreeParam::Attribute:
321 AttributeSelected(*mpCurRefEntry);
322 break;
323 case ScOrcusXMLTreeParam::ElementDefault:
324 DefaultElementSelected(*mpCurRefEntry);
325 break;
326 case ScOrcusXMLTreeParam::ElementRepeat:
327 RepeatElementSelected(*mpCurRefEntry);
328 break;
329 default:
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.
349 bHasChild = true;
350 break;
354 if (bHasChild)
356 SetNonLinkable();
357 return;
361 // Check all its parents and make sure non of them are range-linked nor
362 // repeat elements.
363 if (IsParentDirty(&rEntry))
365 SetNonLinkable();
366 return;
369 SetSingleLinkable();
372 void ScXMLSourceDlg::RepeatElementSelected(SvTreeListEntry& rEntry)
374 // Check all its parents first.
376 if (IsParentDirty(&rEntry))
378 SetNonLinkable();
379 return;
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))
389 SetNonLinkable();
390 return;
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);
404 SetRangeLinkable();
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.
421 SetNonLinkable();
422 return;
425 if (IsParentDirty(&rEntry))
427 SetNonLinkable();
428 return;
431 SetSingleLinkable();
434 void ScXMLSourceDlg::SetNonLinkable()
436 maFtMappedCellTitle.Disable();
437 maRefEdit.Disable();
438 maRefBtn.Disable();
441 void ScXMLSourceDlg::SetSingleLinkable()
443 maFtMappedCellTitle.Enable();
444 maRefEdit.Enable();
445 maRefBtn.Enable();
448 void ScXMLSourceDlg::SetRangeLinkable()
450 maFtMappedCellTitle.Enable();
451 maRefEdit.Enable();
452 maRefBtn.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);
474 while (pParent)
476 pUserData = ScOrcusXMLTreeParam::getUserData(*pParent);
477 OSL_ASSERT(pUserData);
478 if (pUserData->maLinkedPos.IsValid())
480 // This parent is already linked.
481 return true;
483 if (pUserData->meType == ScOrcusXMLTreeParam::ElementRepeat)
485 // This is a repeat element.
486 return true;
488 pParent = maLbTree.GetParent(pParent);
490 return false;
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())
501 // Already linked.
502 return true;
504 if (pUserData->meType == ScOrcusXMLTreeParam::ElementRepeat)
505 // We don't support linking of nested repeat elements (yet).
506 return true;
508 if (pUserData->meType == ScOrcusXMLTreeParam::ElementDefault)
510 // Check recursively.
511 if (IsChildrenDirty(pChild))
512 return true;
516 return false;
519 namespace {
522 * Pick only the leaf elements.
524 void getFieldLinks(
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.
531 return;
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));
547 // Walk recursively.
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()
563 if (!mpXMLContext)
564 return;
566 // Begin import.
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();
616 if (pViewShell)
617 pViewShell->PaintGrid();
619 Close();
622 void ScXMLSourceDlg::CancelPressed()
624 Close();
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);
640 if (!bValid)
641 aLinkedPos.SetInvalid();
643 // Set this address to the current reference entry.
644 if (!mpCurRefEntry)
645 // This should never happen.
646 return;
648 ScOrcusXMLTreeParam::EntryData* pUserData = ScOrcusXMLTreeParam::getUserData(*mpCurRefEntry);
649 if (!pUserData)
650 // This should never happen either.
651 return;
653 bool bRepeatElem = pUserData->meType == ScOrcusXMLTreeParam::ElementRepeat;
654 pUserData->maLinkedPos = aLinkedPos;
655 pUserData->mbRangeParent = aLinkedPos.IsValid() && bRepeatElem;
657 if (bRepeatElem)
659 if (bValid)
660 maRangeLinks.insert(mpCurRefEntry);
661 else
662 maRangeLinks.erase(mpCurRefEntry);
664 else
666 if (bValid)
667 maCellLinks.insert(mpCurRefEntry);
668 else
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);
680 return 0;
683 IMPL_LINK(ScXMLSourceDlg, LoseFocusHdl, Control*, pCtrl)
685 HandleLoseFocus(pCtrl);
686 return 0;
689 IMPL_LINK(ScXMLSourceDlg, BtnPressedHdl, Button*, pBtn)
691 if (pBtn == &maBtnSelectSource)
692 SelectSourceFile();
693 else if (pBtn == &maBtnOk)
694 OkPressed();
695 else if (pBtn == &maBtnCancel)
696 CancelPressed();
697 return 0;
700 IMPL_LINK_NOARG(ScXMLSourceDlg, TreeItemSelectHdl)
702 TreeItemSelected();
703 return 0;
706 IMPL_LINK_NOARG(ScXMLSourceDlg, RefModifiedHdl)
708 RefEditModified();
709 return 0;
712 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */