Stop leaking all ScPostIt instances.
[LibreOffice.git] / sc / source / ui / xmlsource / xmlsourcedlg.cxx
blob5ae5c861da2cf3f1636de901a26ae89e4190e1a1
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(rRange.aStart.Format(SCA_ABS_3D, pDoc, pDoc->GetAddressConvention()));
141 mpActiveEdit->SetRefString(aStr);
143 RefEditModified();
146 void ScXMLSourceDlg::Deactivate()
148 mbDlgLostFocus = true;
151 void ScXMLSourceDlg::SetActive()
153 if (mbDlgLostFocus)
155 mbDlgLostFocus = false;
156 if (mpActiveEdit)
158 mpActiveEdit->GrabFocus();
161 else
163 GrabFocus();
166 RefInputDone();
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())
179 // Use default path.
180 xFilePicker->setDisplayDirectory(SvtPathOptions().GetWorkPath());
181 else
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.
193 return;
195 uno::Sequence<OUString> aFiles = xFilePicker->getFiles();
196 if (!aFiles.getLength())
197 return;
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();
209 if (!pOrcus)
210 return;
212 mpXMLContext.reset(pOrcus->createXMLContext(*mpDoc, rPath));
213 if (!mpXMLContext)
214 return;
216 mpXMLContext->loadXMLStructure(maLbTree, maXMLParam);
219 void ScXMLSourceDlg::HandleGetFocus(Control* pCtrl)
221 mpActiveEdit = NULL;
222 if (pCtrl == &maRefEdit || pCtrl == &maRefBtn)
223 mpActiveEdit = &maRefEdit;
225 if (mpActiveEdit)
226 mpActiveEdit->SetSelection(Selection(0, SELECTION_MAX));
229 void ScXMLSourceDlg::HandleLoseFocus(Control* /*pCtrl*/)
233 namespace {
235 class UnhighlightEntry : std::unary_function<SvTreeListEntry*, void>
237 SvTreeListBox& mrTree;
238 public:
239 UnhighlightEntry(SvTreeListBox& rTree) : mrTree(rTree) {}
241 void operator() (SvTreeListEntry* p)
243 SvViewDataEntry* pView = mrTree.GetViewDataEntry(p);
244 if (!pView)
245 return;
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;
262 while (pParent)
264 ScOrcusXMLTreeParam::EntryData* pUserData = ScOrcusXMLTreeParam::getUserData(*pParent);
265 OSL_ASSERT(pUserData);
266 if (pUserData->meType == ScOrcusXMLTreeParam::ElementRepeat)
268 // This is a repeat element.
269 if (pRefEntry)
271 // Second repeat element encountered. Not good.
272 return pCurEntry;
275 pRefEntry = pParent;
277 pParent = rTree.GetParent(pParent);
280 return pRefEntry ? pRefEntry : pCurEntry;
285 void ScXMLSourceDlg::TreeItemSelected()
287 SvTreeListEntry* pEntry = maLbTree.GetCurEntry();
288 if (!pEntry)
289 return;
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;
304 if (rPos.IsValid())
306 OUString aStr(rPos.Format(SCA_ABS_3D, mpDoc, mpDoc->GetAddressConvention()));
307 maRefEdit.SetRefString(aStr);
309 else
310 maRefEdit.SetRefString(OUString());
312 switch (pUserData->meType)
314 case ScOrcusXMLTreeParam::Attribute:
315 AttributeSelected(*mpCurRefEntry);
316 break;
317 case ScOrcusXMLTreeParam::ElementDefault:
318 DefaultElementSelected(*mpCurRefEntry);
319 break;
320 case ScOrcusXMLTreeParam::ElementRepeat:
321 RepeatElementSelected(*mpCurRefEntry);
322 break;
323 default:
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.
343 bHasChild = true;
344 break;
348 if (bHasChild)
350 SetNonLinkable();
351 return;
355 // Check all its parents and make sure non of them are range-linked nor
356 // repeat elements.
357 if (IsParentDirty(&rEntry))
359 SetNonLinkable();
360 return;
363 SetSingleLinkable();
366 void ScXMLSourceDlg::RepeatElementSelected(SvTreeListEntry& rEntry)
368 // Check all its parents first.
370 if (IsParentDirty(&rEntry))
372 SetNonLinkable();
373 return;
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))
383 SetNonLinkable();
384 return;
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);
398 SetRangeLinkable();
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.
415 SetNonLinkable();
416 return;
419 if (IsParentDirty(&rEntry))
421 SetNonLinkable();
422 return;
425 SetSingleLinkable();
428 void ScXMLSourceDlg::SetNonLinkable()
430 maFtMappedCellTitle.Disable();
431 maRefEdit.Disable();
432 maRefBtn.Disable();
435 void ScXMLSourceDlg::SetSingleLinkable()
437 maFtMappedCellTitle.Enable();
438 maRefEdit.Enable();
439 maRefBtn.Enable();
442 void ScXMLSourceDlg::SetRangeLinkable()
444 maFtMappedCellTitle.Enable();
445 maRefEdit.Enable();
446 maRefBtn.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);
468 while (pParent)
470 pUserData = ScOrcusXMLTreeParam::getUserData(*pParent);
471 OSL_ASSERT(pUserData);
472 if (pUserData->maLinkedPos.IsValid())
474 // This parent is already linked.
475 return true;
477 if (pUserData->meType == ScOrcusXMLTreeParam::ElementRepeat)
479 // This is a repeat element.
480 return true;
482 pParent = maLbTree.GetParent(pParent);
484 return false;
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())
495 // Already linked.
496 return true;
498 if (pUserData->meType == ScOrcusXMLTreeParam::ElementRepeat)
499 // We don't support linking of nested repeat elements (yet).
500 return true;
502 if (pUserData->meType == ScOrcusXMLTreeParam::ElementDefault)
504 // Check recursively.
505 if (IsChildrenDirty(pChild))
506 return true;
510 return false;
513 namespace {
516 * Pick only the leaf elements.
518 void getFieldLinks(
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.
525 return;
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));
541 // Walk recursively.
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()
557 if (!mpXMLContext)
558 return;
560 // Begin import.
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();
610 if (pViewShell)
611 pViewShell->PaintGrid();
613 Close();
616 void ScXMLSourceDlg::CancelPressed()
618 Close();
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);
634 if (!bValid)
635 aLinkedPos.SetInvalid();
637 // Set this address to the current reference entry.
638 if (!mpCurRefEntry)
639 // This should never happen.
640 return;
642 ScOrcusXMLTreeParam::EntryData* pUserData = ScOrcusXMLTreeParam::getUserData(*mpCurRefEntry);
643 if (!pUserData)
644 // This should never happen either.
645 return;
647 bool bRepeatElem = pUserData->meType == ScOrcusXMLTreeParam::ElementRepeat;
648 pUserData->maLinkedPos = aLinkedPos;
649 pUserData->mbRangeParent = aLinkedPos.IsValid() && bRepeatElem;
651 if (bRepeatElem)
653 if (bValid)
654 maRangeLinks.insert(mpCurRefEntry);
655 else
656 maRangeLinks.erase(mpCurRefEntry);
658 else
660 if (bValid)
661 maCellLinks.insert(mpCurRefEntry);
662 else
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);
674 return 0;
677 IMPL_LINK(ScXMLSourceDlg, LoseFocusHdl, Control*, pCtrl)
679 HandleLoseFocus(pCtrl);
680 return 0;
683 IMPL_LINK(ScXMLSourceDlg, BtnPressedHdl, Button*, pBtn)
685 if (pBtn == &maBtnSelectSource)
686 SelectSourceFile();
687 else if (pBtn == &maBtnOk)
688 OkPressed();
689 else if (pBtn == &maBtnCancel)
690 CancelPressed();
691 return 0;
694 IMPL_LINK_NOARG(ScXMLSourceDlg, TreeItemSelectHdl)
696 TreeItemSelected();
697 return 0;
700 IMPL_LINK_NOARG(ScXMLSourceDlg, RefModifiedHdl)
702 RefEditModified();
703 return 0;
706 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */