fix baseline build (old cairo) - 'cairo_rectangle_int_t' does not name a type
[LibreOffice.git] / sc / source / ui / xmlsource / xmlsourcedlg.cxx
blob3ba29ee9f48d0497f7656b1556500bb51370a9c6
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 "sc.hrc"
12 #include "scresid.hxx"
13 #include "document.hxx"
14 #include "orcusfilters.hxx"
15 #include "filter.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;
33 namespace {
35 bool isAttribute(const SvTreeListEntry& rEntry)
37 const ScOrcusXMLTreeParam::EntryData* pUserData = ScOrcusXMLTreeParam::getUserData(rEntry);
38 if (!pUserData)
39 return false;
41 return pUserData->meType == ScOrcusXMLTreeParam::Attribute;
44 OUString getXPath(
45 const SvTreeListBox& rTree, const SvTreeListEntry& rEntry, std::vector<size_t>& rNamespaces)
47 OUStringBuffer aBuf;
48 for (const SvTreeListEntry* p = &rEntry; p; p = rTree.GetParent(p))
50 const SvLBoxItem* pItem = p->GetFirstItem(SV_ITEM_ID_LBOXSTRING);
51 if (!pItem)
52 continue;
54 // Collect used namespace.
55 const ScOrcusXMLTreeParam::EntryData* pData = ScOrcusXMLTreeParam::getUserData(*p);
56 if (pData)
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")
73 , mpCurRefEntry(NULL)
74 , mpDoc(pDoc)
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);
86 get(mpRefBtn, "ref");
87 mpRefBtn->SetReferences(this, mpRefEdit);
88 get(mpBtnCancel, "cancel");
89 get(mpBtnOk, "ok");
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);
112 mpBtnOk->Disable();
114 SetNonLinkable();
115 mpBtnSelectSource->GrabFocus(); // Initial focus is on the select source button.
118 ScXMLSourceDlg::~ScXMLSourceDlg()
120 disposeOnce();
123 void ScXMLSourceDlg::dispose()
125 mpBtnSelectSource.clear();
126 mpFtSourceFile.clear();
127 mpMapGrid.clear();
128 mpLbTree.clear();
129 mpRefEdit.clear();
130 mpRefBtn.clear();
131 mpBtnOk.clear();
132 mpBtnCancel.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)
144 if (!mpActiveEdit)
145 return;
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);
153 RefEditModified();
156 void ScXMLSourceDlg::Deactivate()
158 mbDlgLostFocus = true;
161 void ScXMLSourceDlg::SetActive()
163 if (mbDlgLostFocus)
165 mbDlgLostFocus = false;
166 if (mpActiveEdit)
168 mpActiveEdit->GrabFocus();
171 else
173 GrabFocus();
176 RefInputDone();
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())
189 // Use default path.
190 xFilePicker->setDisplayDirectory(SvtPathOptions().GetWorkPath());
191 else
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.
203 return;
205 uno::Sequence<OUString> aFiles = xFilePicker->getFiles();
206 if (!aFiles.getLength())
207 return;
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();
219 if (!pOrcus)
220 return;
222 mpXMLContext.reset(pOrcus->createXMLContext(*mpDoc, rPath));
223 if (!mpXMLContext)
224 return;
226 mpXMLContext->loadXMLStructure(*mpLbTree, maXMLParam);
229 void ScXMLSourceDlg::HandleGetFocus(Control* pCtrl)
231 mpActiveEdit = NULL;
232 if (pCtrl == mpRefEdit || pCtrl == mpRefBtn)
233 mpActiveEdit = mpRefEdit;
235 if (mpActiveEdit)
236 mpActiveEdit->SetSelection(Selection(0, SELECTION_MAX));
239 namespace {
241 class UnhighlightEntry : std::unary_function<SvTreeListEntry*, void>
243 SvTreeListBox& mrTree;
244 public:
245 UnhighlightEntry(SvTreeListBox& rTree) : mrTree(rTree) {}
247 void operator() (SvTreeListEntry* p)
249 SvViewDataEntry* pView = mrTree.GetViewDataEntry(p);
250 if (!pView)
251 return;
253 pView->SetHighlighted(false);
254 mrTree.Invalidate();
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;
268 while (pParent)
270 ScOrcusXMLTreeParam::EntryData* pUserData = ScOrcusXMLTreeParam::getUserData(*pParent);
271 OSL_ASSERT(pUserData);
272 if (pUserData->meType == ScOrcusXMLTreeParam::ElementRepeat)
274 // This is a repeat element.
275 if (pRefEntry)
277 // Second repeat element encountered. Not good.
278 return pCurEntry;
281 pRefEntry = pParent;
283 pParent = rTree.GetParent(pParent);
286 return pRefEntry ? pRefEntry : pCurEntry;
291 void ScXMLSourceDlg::TreeItemSelected()
293 SvTreeListEntry* pEntry = mpLbTree->GetCurEntry();
294 if (!pEntry)
295 return;
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;
310 if (rPos.IsValid())
312 OUString aStr(rPos.Format(SCA_ABS_3D, mpDoc, mpDoc->GetAddressConvention()));
313 mpRefEdit->SetRefString(aStr);
315 else
316 mpRefEdit->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 (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.
348 bHasChild = true;
349 break;
353 if (bHasChild)
355 SetNonLinkable();
356 return;
360 // Check all its parents and make sure non of them are range-linked nor
361 // repeat elements.
362 if (IsParentDirty(&rEntry))
364 SetNonLinkable();
365 return;
368 SetSingleLinkable();
371 void ScXMLSourceDlg::RepeatElementSelected(SvTreeListEntry& rEntry)
373 // Check all its parents first.
375 if (IsParentDirty(&rEntry))
377 SetNonLinkable();
378 return;
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))
388 SetNonLinkable();
389 return;
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);
403 SetRangeLinkable();
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.
420 SetNonLinkable();
421 return;
424 if (IsParentDirty(&rEntry))
426 SetNonLinkable();
427 return;
430 SetSingleLinkable();
433 void ScXMLSourceDlg::SetNonLinkable()
435 mpMapGrid->Disable();
438 void ScXMLSourceDlg::SetSingleLinkable()
440 mpMapGrid->Enable();
443 void ScXMLSourceDlg::SetRangeLinkable()
445 mpMapGrid->Enable();
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);
466 while (pParent)
468 ScOrcusXMLTreeParam::EntryData* pUserData = ScOrcusXMLTreeParam::getUserData(*pParent);
469 assert(pUserData);
470 if (pUserData->maLinkedPos.IsValid())
472 // This parent is already linked.
473 return true;
475 if (pUserData->meType == ScOrcusXMLTreeParam::ElementRepeat)
477 // This is a repeat element.
478 return true;
480 pParent = mpLbTree->GetParent(pParent);
482 return false;
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())
492 // Already linked.
493 return true;
495 if (pUserData->meType == ScOrcusXMLTreeParam::ElementRepeat)
496 // We don't support linking of nested repeat elements (yet).
497 return true;
499 if (pUserData->meType == ScOrcusXMLTreeParam::ElementDefault)
501 // Check recursively.
502 if (IsChildrenDirty(pChild))
503 return true;
507 return false;
510 namespace {
513 * Pick only the leaf elements.
515 void getFieldLinks(
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.
522 return;
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));
538 // Walk recursively.
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()
554 if (!mpXMLContext)
555 return;
557 // Begin import.
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();
607 if (pViewShell)
608 pViewShell->PaintGrid();
610 Close();
613 void ScXMLSourceDlg::CancelPressed()
615 Close();
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);
631 if (!bValid)
632 aLinkedPos.SetInvalid();
634 // Set this address to the current reference entry.
635 if (!mpCurRefEntry)
636 // This should never happen.
637 return;
639 ScOrcusXMLTreeParam::EntryData* pUserData = ScOrcusXMLTreeParam::getUserData(*mpCurRefEntry);
640 if (!pUserData)
641 // This should never happen either.
642 return;
644 bool bRepeatElem = pUserData->meType == ScOrcusXMLTreeParam::ElementRepeat;
645 pUserData->maLinkedPos = aLinkedPos;
646 pUserData->mbRangeParent = aLinkedPos.IsValid() && bRepeatElem;
648 if (bRepeatElem)
650 if (bValid)
651 maRangeLinks.insert(mpCurRefEntry);
652 else
653 maRangeLinks.erase(mpCurRefEntry);
655 else
657 if (bValid)
658 maCellLinks.insert(mpCurRefEntry);
659 else
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);
671 return 0;
674 IMPL_LINK(ScXMLSourceDlg, BtnPressedHdl, Button*, pBtn)
676 if (pBtn == mpBtnSelectSource)
677 SelectSourceFile();
678 else if (pBtn == mpBtnOk)
679 OkPressed();
680 else if (pBtn == mpBtnCancel)
681 CancelPressed();
682 return 0;
685 IMPL_LINK_NOARG(ScXMLSourceDlg, TreeItemSelectHdl)
687 TreeItemSelected();
688 return 0;
691 IMPL_LINK_NOARG(ScXMLSourceDlg, RefModifiedHdl)
693 RefEditModified();
694 return 0;
697 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */