summaryrefslogtreecommitdiffstats
path: root/sfx2/source/view
diff options
context:
space:
mode:
Diffstat (limited to 'sfx2/source/view')
-rw-r--r--sfx2/source/view/classificationcontroller.cxx364
-rw-r--r--sfx2/source/view/classificationhelper.cxx988
-rw-r--r--sfx2/source/view/frame.cxx720
-rw-r--r--sfx2/source/view/frame2.cxx406
-rw-r--r--sfx2/source/view/frmload.cxx826
-rw-r--r--sfx2/source/view/impframe.hxx71
-rw-r--r--sfx2/source/view/impviewframe.hxx81
-rw-r--r--sfx2/source/view/ipclient.cxx1148
-rw-r--r--sfx2/source/view/lokcharthelper.cxx369
-rw-r--r--sfx2/source/view/lokhelper.cxx1066
-rw-r--r--sfx2/source/view/lokstarmathhelper.cxx247
-rw-r--r--sfx2/source/view/printer.cxx189
-rw-r--r--sfx2/source/view/prnmon.hxx54
-rw-r--r--sfx2/source/view/sfxbasecontroller.cxx1498
-rw-r--r--sfx2/source/view/userinputinterception.cxx263
-rw-r--r--sfx2/source/view/viewfac.cxx56
-rw-r--r--sfx2/source/view/viewfrm.cxx3706
-rw-r--r--sfx2/source/view/viewfrm2.cxx380
-rw-r--r--sfx2/source/view/viewimp.hxx71
-rw-r--r--sfx2/source/view/viewprn.cxx923
-rw-r--r--sfx2/source/view/viewsh.cxx3912
21 files changed, 17338 insertions, 0 deletions
diff --git a/sfx2/source/view/classificationcontroller.cxx b/sfx2/source/view/classificationcontroller.cxx
new file mode 100644
index 0000000000..1cda4a41ca
--- /dev/null
+++ b/sfx2/source/view/classificationcontroller.cxx
@@ -0,0 +1,364 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <cppuhelper/implbase.hxx>
+#include <svtools/toolboxcontroller.hxx>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/document/XDocumentProperties.hpp>
+
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/frame/XFrame.hpp>
+
+#include <toolkit/helper/vclunohelper.hxx>
+#include <vcl/InterimItemWindow.hxx>
+#include <sfx2/classificationhelper.hxx>
+#include <sfx2/objsh.hxx>
+#include <sfx2/strings.hrc>
+#include <sfx2/sfxresid.hxx>
+#include <vcl/event.hxx>
+#include <vcl/toolbox.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/vclptr.hxx>
+#include <vcl/weld.hxx>
+
+#include <cppuhelper/supportsservice.hxx>
+#include <comphelper/propertysequence.hxx>
+#include <comphelper/dispatchcommand.hxx>
+#include <comphelper/configurationlistener.hxx>
+
+using namespace com::sun::star;
+
+namespace sfx2
+{
+
+namespace {
+
+class ClassificationCategoriesController;
+
+}
+
+using ClassificationPropertyListenerBase = comphelper::ConfigurationListenerProperty<OUString>;
+
+namespace {
+
+/// Listens to configuration changes, so no restart is needed after setting the classification path.
+class ClassificationPropertyListener : public ClassificationPropertyListenerBase
+{
+ ClassificationCategoriesController& m_rController;
+
+public:
+ ClassificationPropertyListener(const rtl::Reference<comphelper::ConfigurationListener>& xListener, ClassificationCategoriesController& rController);
+ void setProperty(const uno::Any& rProperty) override;
+};
+
+}
+
+using ClassificationCategoriesControllerBase = cppu::ImplInheritanceHelper<svt::ToolboxController, lang::XServiceInfo>;
+
+namespace {
+
+class ClassificationControl;
+
+/// Controller for .uno:ClassificationApply.
+class ClassificationCategoriesController : public ClassificationCategoriesControllerBase
+{
+ VclPtr<ClassificationControl> m_pClassification;
+ rtl::Reference<comphelper::ConfigurationListener> m_xListener;
+ ClassificationPropertyListener m_aPropertyListener;
+
+ DECL_LINK(SelectHdl, weld::ComboBox&, void);
+
+public:
+ explicit ClassificationCategoriesController(const uno::Reference<uno::XComponentContext>& rContext);
+
+ // XServiceInfo
+ OUString SAL_CALL getImplementationName() override;
+ sal_Bool SAL_CALL supportsService(const OUString& rServiceName) override;
+ uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
+
+ // XComponent
+ void SAL_CALL dispose() override;
+
+ // XToolbarController
+ uno::Reference<awt::XWindow> SAL_CALL createItemWindow(const uno::Reference<awt::XWindow>& rParent) override;
+
+ // XStatusListener
+ void SAL_CALL statusChanged(const frame::FeatureStateEvent& rEvent) override;
+
+ void removeEntries();
+};
+
+/// Classification control is the parent of all widgets that belongs to ClassificationCategoriesController.
+class SAL_WARN_UNUSED ClassificationControl final : public InterimItemWindow
+{
+ std::unique_ptr<weld::Label> m_xLabel;
+ std::unique_ptr<weld::ComboBox> m_xCategory;
+
+ DECL_LINK(KeyInputHdl, const KeyEvent&, bool);
+
+ void SetOptimalSize();
+ void DataChanged(const DataChangedEvent& rEvent) override;
+
+public:
+ explicit ClassificationControl(vcl::Window* pParent);
+ ~ClassificationControl() override;
+ void dispose() override;
+ weld::ComboBox& getCategory()
+ {
+ return *m_xCategory;
+ }
+ void set_sensitive(bool bSensitive)
+ {
+ Enable(bSensitive);
+ m_xContainer->set_sensitive(bSensitive);
+ }
+ static sfx::ClassificationCreationOrigin getExistingClassificationOrigin();
+ void toggleInteractivityOnOrigin();
+ void setCategoryStateFromPolicy(const SfxClassificationHelper & rHelper);
+};
+
+OUString const & getCategoryType()
+{
+ return SfxClassificationHelper::policyTypeToString(SfxClassificationHelper::getPolicyType());
+}
+
+} // end anonymous namespace
+
+ClassificationPropertyListener::ClassificationPropertyListener(const rtl::Reference<comphelper::ConfigurationListener>& xListener, ClassificationCategoriesController& rController)
+ : ClassificationPropertyListenerBase(xListener, "WritePath")
+ , m_rController(rController)
+{
+}
+
+void ClassificationPropertyListener::setProperty(const uno::Any& /*rProperty*/)
+{
+ // So that its gets re-filled with entries from the new policy.
+ m_rController.removeEntries();
+}
+
+ClassificationCategoriesController::ClassificationCategoriesController(const uno::Reference<uno::XComponentContext>& rContext)
+ : ClassificationCategoriesControllerBase(rContext, uno::Reference<frame::XFrame>(), OUString(".uno:ClassificationApply"))
+ , m_pClassification(nullptr)
+ , m_xListener(new comphelper::ConfigurationListener("/org.openoffice.Office.Paths/Paths/Classification"))
+ , m_aPropertyListener(m_xListener, *this)
+{
+
+}
+
+OUString ClassificationCategoriesController::getImplementationName()
+{
+ return "com.sun.star.comp.sfx2.ClassificationCategoriesController";
+}
+
+sal_Bool ClassificationCategoriesController::supportsService(const OUString& rServiceName)
+{
+ return cppu::supportsService(this, rServiceName);
+}
+
+uno::Sequence<OUString> ClassificationCategoriesController::getSupportedServiceNames()
+{
+ return { "com.sun.star.frame.ToolbarController" };
+}
+
+void ClassificationCategoriesController::dispose()
+{
+ SolarMutexGuard aSolarMutexGuard;
+
+ svt::ToolboxController::dispose();
+ m_pClassification.disposeAndClear();
+ m_aPropertyListener.dispose();
+ m_xListener->dispose();
+}
+
+uno::Reference<awt::XWindow> ClassificationCategoriesController::createItemWindow(const uno::Reference<awt::XWindow>& rParent)
+{
+ VclPtr<vcl::Window> pParent = VCLUnoHelper::GetWindow(rParent);
+ auto pToolbar = dynamic_cast<ToolBox*>(pParent.get());
+ if (pToolbar)
+ {
+ m_pClassification = VclPtr<ClassificationControl>::Create(pToolbar);
+ m_pClassification->getCategory().connect_changed(LINK(this, ClassificationCategoriesController, SelectHdl));
+ m_pClassification->Show();
+ }
+
+ return VCLUnoHelper::GetInterface(m_pClassification);
+}
+
+IMPL_LINK(ClassificationCategoriesController, SelectHdl, weld::ComboBox&, rCategory, void)
+{
+ m_pClassification->toggleInteractivityOnOrigin();
+
+ if (ClassificationControl::getExistingClassificationOrigin() == sfx::ClassificationCreationOrigin::MANUAL)
+ {
+ SfxObjectShell* pObjectShell = SfxObjectShell::Current();
+ if (!pObjectShell)
+ return;
+ SfxClassificationHelper aHelper(pObjectShell->getDocProperties());
+ m_pClassification->setCategoryStateFromPolicy(aHelper);
+ }
+ else
+ {
+ OUString aEntry = rCategory.get_active_text();
+
+ const OUString& aType = getCategoryType();
+ uno::Sequence<beans::PropertyValue> aPropertyValues(comphelper::InitPropertySequence({
+ {"Name", uno::Any(aEntry)},
+ {"Type", uno::Any(aType)},
+ }));
+ comphelper::dispatchCommand(".uno:ClassificationApply", aPropertyValues);
+ }
+}
+
+void ClassificationCategoriesController::statusChanged(const frame::FeatureStateEvent& /*rEvent*/)
+{
+ if (!m_pClassification)
+ return;
+
+ SfxObjectShell* pObjectShell = SfxObjectShell::Current();
+ if (!pObjectShell)
+ return;
+
+ SfxClassificationHelper aHelper(pObjectShell->getDocProperties());
+
+ //toggle if the pop-up is enabled/disabled
+ m_pClassification->toggleInteractivityOnOrigin();
+
+ // check if classification was set via the advanced dialog
+ if (ClassificationControl::getExistingClassificationOrigin() != sfx::ClassificationCreationOrigin::MANUAL)
+ {
+ weld::ComboBox& rCategories = m_pClassification->getCategory();
+ if (rCategories.get_count() == 0)
+ {
+ std::vector<OUString> aNames = aHelper.GetBACNames();
+ for (const OUString& rName : aNames)
+ rCategories.append_text(rName);
+ }
+ }
+
+ // Restore state based on the doc. model.
+ m_pClassification->setCategoryStateFromPolicy(aHelper);
+
+}
+
+void ClassificationCategoriesController::removeEntries()
+{
+ m_pClassification->getCategory().clear();
+}
+
+ClassificationControl::ClassificationControl(vcl::Window* pParent)
+ : InterimItemWindow(pParent, "sfx/ui/classificationbox.ui", "ClassificationBox")
+ , m_xLabel(m_xBuilder->weld_label("label"))
+ , m_xCategory(m_xBuilder->weld_combo_box("combobox"))
+{
+ InitControlBase(m_xCategory.get());
+
+ m_xCategory->connect_key_press(LINK(this, ClassificationControl, KeyInputHdl));
+
+ // WB_NOLABEL means here that the control won't be replaced with a label
+ // when it wouldn't fit the available space.
+ SetStyle(GetStyle() | WB_DIALOGCONTROL | WB_NOLABEL);
+
+ OUString aText;
+ switch (SfxClassificationHelper::getPolicyType())
+ {
+ case SfxClassificationPolicyType::IntellectualProperty:
+ aText = SfxResId(STR_CLASSIFIED_INTELLECTUAL_PROPERTY);
+ break;
+ case SfxClassificationPolicyType::NationalSecurity:
+ aText = SfxResId(STR_CLASSIFIED_NATIONAL_SECURITY);
+ break;
+ case SfxClassificationPolicyType::ExportControl:
+ aText = SfxResId(STR_CLASSIFIED_EXPORT_CONTROL);
+ break;
+ }
+
+ m_xLabel->set_label(aText);
+
+ // Same as SvxColorDockingWindow.
+ const Size aLogicalAttrSize(150, 0);
+ Size aSize(LogicToPixel(aLogicalAttrSize, MapMode(MapUnit::MapAppFont)));
+ m_xCategory->set_size_request(aSize.Width() - m_xLabel->get_preferred_size().Width(), -1);
+
+ SetOptimalSize();
+}
+
+IMPL_LINK(ClassificationControl, KeyInputHdl, const KeyEvent&, rKEvt, bool)
+{
+ return ChildKeyInput(rKEvt);
+}
+
+ClassificationControl::~ClassificationControl()
+{
+ disposeOnce();
+}
+
+void ClassificationControl::dispose()
+{
+ m_xLabel.reset();
+ m_xCategory.reset();
+ InterimItemWindow::dispose();
+}
+
+void ClassificationControl::SetOptimalSize()
+{
+ SetSizePixel(get_preferred_size());
+}
+
+void ClassificationControl::DataChanged(const DataChangedEvent& rEvent)
+{
+ if ((rEvent.GetType() == DataChangedEventType::SETTINGS) && (rEvent.GetFlags() & AllSettingsFlags::STYLE))
+ SetOptimalSize();
+
+ toggleInteractivityOnOrigin();
+
+ InterimItemWindow::DataChanged(rEvent);
+}
+
+sfx::ClassificationCreationOrigin ClassificationControl::getExistingClassificationOrigin()
+{
+ SfxObjectShell* pObjectShell = SfxObjectShell::Current();
+ if (!pObjectShell)
+ return sfx::ClassificationCreationOrigin::NONE;
+
+ uno::Reference<document::XDocumentProperties> xDocumentProperties = pObjectShell->getDocProperties();
+ uno::Reference<beans::XPropertyContainer> xPropertyContainer = xDocumentProperties->getUserDefinedProperties();
+
+ sfx::ClassificationKeyCreator aKeyCreator(SfxClassificationHelper::getPolicyType());
+ return sfx::getCreationOriginProperty(xPropertyContainer, aKeyCreator);
+}
+
+void ClassificationControl::toggleInteractivityOnOrigin()
+{
+ if (getExistingClassificationOrigin() == sfx::ClassificationCreationOrigin::MANUAL)
+ {
+ set_sensitive(false);
+ }
+ else
+ {
+ set_sensitive(true);
+ }
+}
+
+void ClassificationControl::setCategoryStateFromPolicy(const SfxClassificationHelper & rHelper)
+{
+ const OUString& rCategoryName = rHelper.GetBACName(SfxClassificationHelper::getPolicyType());
+ if (!rCategoryName.isEmpty())
+ {
+ getCategory().set_active_text(rCategoryName);
+ }
+}
+
+} // namespace sfx2
+
+extern "C" SAL_DLLPUBLIC_EXPORT uno::XInterface* com_sun_star_sfx2_ClassificationCategoriesController_get_implementation(uno::XComponentContext* pContext, const uno::Sequence<uno::Any>&)
+{
+ return cppu::acquire(new sfx2::ClassificationCategoriesController(pContext));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/view/classificationhelper.cxx b/sfx2/source/view/classificationhelper.cxx
new file mode 100644
index 0000000000..e9556e88c1
--- /dev/null
+++ b/sfx2/source/view/classificationhelper.cxx
@@ -0,0 +1,988 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <sfx2/classificationhelper.hxx>
+
+#include <map>
+#include <algorithm>
+#include <iterator>
+
+#include <com/sun/star/beans/XPropertyContainer.hpp>
+#include <com/sun/star/beans/Property.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/document/XDocumentProperties.hpp>
+#include <com/sun/star/xml/sax/Parser.hpp>
+#include <com/sun/star/xml/sax/XDocumentHandler.hpp>
+#include <com/sun/star/xml/sax/SAXParseException.hpp>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+
+#include <sal/log.hxx>
+#include <i18nlangtag/languagetag.hxx>
+#include <sfx2/infobar.hxx>
+#include <comphelper/processfactory.hxx>
+#include <unotools/configmgr.hxx>
+#include <unotools/pathoptions.hxx>
+#include <unotools/ucbstreamhelper.hxx>
+#include <unotools/streamwrap.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <sfx2/strings.hrc>
+#include <sfx2/sfxresid.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <tools/datetime.hxx>
+#include <comphelper/diagnose_ex.hxx>
+#include <unotools/datetime.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/weld.hxx>
+#include <svl/fstathelper.hxx>
+
+#include <o3tl/string_view.hxx>
+#include <officecfg/Office/Common.hxx>
+
+using namespace com::sun::star;
+
+namespace
+{
+
+const OUString& PROP_BACNAME()
+{
+ static constexpr OUString sProp(u"BusinessAuthorizationCategory:Name"_ustr);
+ return sProp;
+}
+
+const OUString& PROP_STARTVALIDITY()
+{
+ static constexpr OUString sProp(u"Authorization:StartValidity"_ustr);
+ return sProp;
+}
+
+const OUString& PROP_NONE()
+{
+ static constexpr OUString sProp(u"None"_ustr);
+ return sProp;
+}
+
+const OUString& PROP_IMPACTSCALE()
+{
+ static constexpr OUString sProp(u"Impact:Scale"_ustr);
+ return sProp;
+}
+
+const OUString& PROP_IMPACTLEVEL()
+{
+ static constexpr OUString sProp(u"Impact:Level:Confidentiality"_ustr);
+ return sProp;
+}
+
+const OUString& PROP_PREFIX_EXPORTCONTROL()
+{
+ static constexpr OUString sProp(u"urn:bails:ExportControl:"_ustr);
+ return sProp;
+}
+
+const OUString& PROP_PREFIX_NATIONALSECURITY()
+{
+ static constexpr OUString sProp(u"urn:bails:NationalSecurity:"_ustr);
+ return sProp;
+}
+
+/// Represents one category of a classification policy.
+class SfxClassificationCategory
+{
+public:
+ /// PROP_BACNAME() is stored separately for easier lookup.
+ OUString m_aName;
+ OUString m_aAbbreviatedName; //< An abbreviation to display instead of m_aName.
+ OUString m_aIdentifier; //< The Identifier of this entry.
+ size_t m_nConfidentiality; //< 0 is the lowest (least-sensitive).
+ std::map<OUString, OUString> m_aLabels;
+};
+
+/// Parses a policy XML conforming to the TSCP BAF schema.
+class SfxClassificationParser : public cppu::WeakImplHelper<xml::sax::XDocumentHandler>
+{
+public:
+ std::vector<SfxClassificationCategory> m_aCategories;
+ std::vector<OUString> m_aMarkings;
+ std::vector<OUString> m_aIPParts;
+ std::vector<OUString> m_aIPPartNumbers;
+
+ OUString m_aPolicyAuthorityName;
+ bool m_bInPolicyAuthorityName = false;
+ OUString m_aPolicyName;
+ bool m_bInPolicyName = false;
+ OUString m_aProgramID;
+ bool m_bInProgramID = false;
+ OUString m_aScale;
+ bool m_bInScale = false;
+ OUString m_aConfidentalityValue;
+ bool m_bInConfidentalityValue = false;
+ OUString m_aIdentifier;
+ bool m_bInIdentifier = false;
+ OUString m_aValue;
+ bool m_bInValue = false;
+
+ /// Pointer to a value in m_aCategories, the currently parsed category.
+ SfxClassificationCategory* m_pCategory = nullptr;
+
+ SfxClassificationParser();
+
+ void SAL_CALL startDocument() override;
+
+ void SAL_CALL endDocument() override;
+
+ void SAL_CALL startElement(const OUString& rName, const uno::Reference<xml::sax::XAttributeList>& xAttribs) override;
+
+ void SAL_CALL endElement(const OUString& rName) override;
+
+ void SAL_CALL characters(const OUString& rChars) override;
+
+ void SAL_CALL ignorableWhitespace(const OUString& rWhitespaces) override;
+
+ void SAL_CALL processingInstruction(const OUString& rTarget, const OUString& rData) override;
+
+ void SAL_CALL setDocumentLocator(const uno::Reference<xml::sax::XLocator>& xLocator) override;
+};
+
+SfxClassificationParser::SfxClassificationParser() = default;
+
+void SAL_CALL SfxClassificationParser::startDocument()
+{
+}
+
+void SAL_CALL SfxClassificationParser::endDocument()
+{
+}
+
+void SAL_CALL SfxClassificationParser::startElement(const OUString& rName, const uno::Reference<xml::sax::XAttributeList>& xAttribs)
+{
+ if (rName == "baf:PolicyAuthorityName")
+ {
+ m_aPolicyAuthorityName.clear();
+ m_bInPolicyAuthorityName = true;
+ }
+ else if (rName == "baf:PolicyName")
+ {
+ m_aPolicyName.clear();
+ m_bInPolicyName = true;
+ }
+ else if (rName == "baf:ProgramID")
+ {
+ m_aProgramID.clear();
+ m_bInProgramID = true;
+ }
+ else if (rName == "baf:BusinessAuthorizationCategory")
+ {
+ const OUString aName = xAttribs->getValueByName("Name");
+ if (!m_pCategory && !aName.isEmpty())
+ {
+ OUString aIdentifier = xAttribs->getValueByName("Identifier");
+
+ // Create a new category and initialize it with the data that's true for all categories.
+ m_aCategories.emplace_back();
+ SfxClassificationCategory& rCategory = m_aCategories.back();
+
+ rCategory.m_aName = aName;
+ // Set the abbreviated name, if any, otherwise fallback on the full name.
+ const OUString aAbbreviatedName = xAttribs->getValueByName("loextAbbreviatedName");
+ rCategory.m_aAbbreviatedName = !aAbbreviatedName.isEmpty() ? aAbbreviatedName : aName;
+ rCategory.m_aIdentifier = aIdentifier;
+
+ rCategory.m_aLabels["PolicyAuthority:Name"] = m_aPolicyAuthorityName;
+ rCategory.m_aLabels["Policy:Name"] = m_aPolicyName;
+ rCategory.m_aLabels["BusinessAuthorization:Identifier"] = m_aProgramID;
+ rCategory.m_aLabels["BusinessAuthorizationCategory:Identifier"] = aIdentifier;
+
+ // Also initialize defaults.
+ rCategory.m_aLabels["PolicyAuthority:Identifier"] = PROP_NONE();
+ rCategory.m_aLabels["PolicyAuthority:Country"] = PROP_NONE();
+ rCategory.m_aLabels["Policy:Identifier"] = PROP_NONE();
+ rCategory.m_aLabels["BusinessAuthorization:Name"] = PROP_NONE();
+ rCategory.m_aLabels["BusinessAuthorization:Locator"] = PROP_NONE();
+ rCategory.m_aLabels["BusinessAuthorizationCategory:Identifier:OID"] = PROP_NONE();
+ rCategory.m_aLabels["BusinessAuthorizationCategory:Locator"] = PROP_NONE();
+ rCategory.m_aLabels["BusinessAuthorization:Locator"] = PROP_NONE();
+ rCategory.m_aLabels["MarkingPrecedence"] = PROP_NONE();
+ rCategory.m_aLabels["Marking:general-summary"].clear();
+ rCategory.m_aLabels["Marking:general-warning-statement"].clear();
+ rCategory.m_aLabels["Marking:general-warning-statement:ext:2"].clear();
+ rCategory.m_aLabels["Marking:general-warning-statement:ext:3"].clear();
+ rCategory.m_aLabels["Marking:general-warning-statement:ext:4"].clear();
+ rCategory.m_aLabels["Marking:general-distribution-statement"].clear();
+ rCategory.m_aLabels["Marking:general-distribution-statement:ext:2"].clear();
+ rCategory.m_aLabels["Marking:general-distribution-statement:ext:3"].clear();
+ rCategory.m_aLabels["Marking:general-distribution-statement:ext:4"].clear();
+ rCategory.m_aLabels[SfxClassificationHelper::PROP_DOCHEADER()].clear();
+ rCategory.m_aLabels[SfxClassificationHelper::PROP_DOCFOOTER()].clear();
+ rCategory.m_aLabels[SfxClassificationHelper::PROP_DOCWATERMARK()].clear();
+ rCategory.m_aLabels["Marking:email-first-line-of-text"].clear();
+ rCategory.m_aLabels["Marking:email-last-line-of-text"].clear();
+ rCategory.m_aLabels["Marking:email-subject-prefix"].clear();
+ rCategory.m_aLabels["Marking:email-subject-suffix"].clear();
+ rCategory.m_aLabels[PROP_STARTVALIDITY()] = PROP_NONE();
+ rCategory.m_aLabels["Authorization:StopValidity"] = PROP_NONE();
+ m_pCategory = &rCategory;
+ }
+ }
+ else if (rName == "loext:Marking")
+ {
+ OUString aName = xAttribs->getValueByName("Name");
+ m_aMarkings.push_back(aName);
+ }
+ else if (rName == "loext:IntellectualPropertyPart")
+ {
+ OUString aName = xAttribs->getValueByName("Name");
+ m_aIPParts.push_back(aName);
+ }
+ else if (rName == "loext:IntellectualPropertyPartNumber")
+ {
+ OUString aName = xAttribs->getValueByName("Name");
+ m_aIPPartNumbers.push_back(aName);
+ }
+ else if (rName == "baf:Scale")
+ {
+ m_aScale.clear();
+ m_bInScale = true;
+ }
+ else if (rName == "baf:ConfidentalityValue")
+ {
+ m_aConfidentalityValue.clear();
+ m_bInConfidentalityValue = true;
+ }
+ else if (rName == "baf:Identifier")
+ {
+ m_aIdentifier.clear();
+ m_bInIdentifier = true;
+ }
+ else if (rName == "baf:Value")
+ {
+ m_aValue.clear();
+ m_bInValue = true;
+ }
+}
+
+void SAL_CALL SfxClassificationParser::endElement(const OUString& rName)
+{
+ if (rName == "baf:PolicyAuthorityName")
+ m_bInPolicyAuthorityName = false;
+ else if (rName == "baf:PolicyName")
+ m_bInPolicyName = false;
+ else if (rName == "baf:ProgramID")
+ m_bInProgramID = false;
+ else if (rName == "baf:BusinessAuthorizationCategory")
+ m_pCategory = nullptr;
+ else if (rName == "baf:Scale")
+ {
+ m_bInScale = false;
+ if (m_pCategory)
+ m_pCategory->m_aLabels[PROP_IMPACTSCALE()] = m_aScale;
+ }
+ else if (rName == "baf:ConfidentalityValue")
+ {
+ m_bInConfidentalityValue = false;
+ if (m_pCategory)
+ {
+ std::map<OUString, OUString>& rLabels = m_pCategory->m_aLabels;
+ rLabels[PROP_IMPACTLEVEL()] = m_aConfidentalityValue;
+ m_pCategory->m_nConfidentiality = m_aConfidentalityValue.toInt32(); // 0-based class sensitivity; 0 is lowest.
+ // Set the two other type of levels as well, if they're not set
+ // yet: they're optional in BAF, but not in BAILS.
+ rLabels.try_emplace("Impact:Level:Integrity", m_aConfidentalityValue);
+ rLabels.try_emplace("Impact:Level:Availability", m_aConfidentalityValue);
+ }
+ }
+ else if (rName == "baf:Identifier")
+ m_bInIdentifier = false;
+ else if (rName == "baf:Value")
+ {
+ if (m_pCategory)
+ {
+ if (m_aIdentifier == "Document: Header")
+ m_pCategory->m_aLabels[SfxClassificationHelper::PROP_DOCHEADER()] = m_aValue;
+ else if (m_aIdentifier == "Document: Footer")
+ m_pCategory->m_aLabels[SfxClassificationHelper::PROP_DOCFOOTER()] = m_aValue;
+ else if (m_aIdentifier == "Document: Watermark")
+ m_pCategory->m_aLabels[SfxClassificationHelper::PROP_DOCWATERMARK()] = m_aValue;
+ }
+ }
+}
+
+void SAL_CALL SfxClassificationParser::characters(const OUString& rChars)
+{
+ if (m_bInPolicyAuthorityName)
+ m_aPolicyAuthorityName += rChars;
+ else if (m_bInPolicyName)
+ m_aPolicyName += rChars;
+ else if (m_bInProgramID)
+ m_aProgramID += rChars;
+ else if (m_bInScale)
+ m_aScale += rChars;
+ else if (m_bInConfidentalityValue)
+ m_aConfidentalityValue += rChars;
+ else if (m_bInIdentifier)
+ m_aIdentifier += rChars;
+ else if (m_bInValue)
+ m_aValue += rChars;
+}
+
+void SAL_CALL SfxClassificationParser::ignorableWhitespace(const OUString& /*rWhitespace*/)
+{
+}
+
+void SAL_CALL SfxClassificationParser::processingInstruction(const OUString& /*rTarget*/, const OUString& /*rData*/)
+{
+}
+
+void SAL_CALL SfxClassificationParser::setDocumentLocator(const uno::Reference<xml::sax::XLocator>& /*xLocator*/)
+{
+}
+
+} // anonymous namespace
+
+/// Implementation details of SfxClassificationHelper.
+class SfxClassificationHelper::Impl
+{
+public:
+ /// Selected categories, one category for each policy type.
+ std::map<SfxClassificationPolicyType, SfxClassificationCategory> m_aCategory;
+ /// Possible categories of a policy to choose from.
+ std::vector<SfxClassificationCategory> m_aCategories;
+ std::vector<OUString> m_aMarkings;
+ std::vector<OUString> m_aIPParts;
+ std::vector<OUString> m_aIPPartNumbers;
+
+ uno::Reference<document::XDocumentProperties> m_xDocumentProperties;
+
+ bool m_bUseLocalized;
+
+ explicit Impl(uno::Reference<document::XDocumentProperties> xDocumentProperties, bool bUseLocalized);
+ void parsePolicy();
+ /// Synchronize m_aLabels back to the document properties.
+ void pushToDocumentProperties();
+ /// Set the classification start date to the system time.
+ void setStartValidity(SfxClassificationPolicyType eType);
+};
+
+SfxClassificationHelper::Impl::Impl(uno::Reference<document::XDocumentProperties> xDocumentProperties, bool bUseLocalized)
+ : m_xDocumentProperties(std::move(xDocumentProperties))
+ , m_bUseLocalized(bUseLocalized)
+{
+ parsePolicy();
+}
+
+void SfxClassificationHelper::Impl::parsePolicy()
+{
+ uno::Reference<uno::XComponentContext> xComponentContext = comphelper::getProcessComponentContext();
+ SvtPathOptions aOptions;
+ OUString aPath = aOptions.GetClassificationPath();
+
+ // See if there is a localized variant next to the configured XML.
+ OUString aExtension(".xml");
+ if (aPath.endsWith(aExtension) && m_bUseLocalized)
+ {
+ std::u16string_view aBase = aPath.subView(0, aPath.getLength() - aExtension.getLength());
+ const LanguageTag& rLanguageTag = Application::GetSettings().GetLanguageTag();
+ // Expected format is "<original path>_xx-XX.xml".
+ OUString aLocalized = OUString::Concat(aBase) + "_" + rLanguageTag.getBcp47() + aExtension;
+ if (FStatHelper::IsDocument(aLocalized))
+ aPath = aLocalized;
+ }
+
+ std::unique_ptr<SvStream> pStream = utl::UcbStreamHelper::CreateStream(aPath, StreamMode::READ);
+ uno::Reference<io::XInputStream> xInputStream(new utl::OStreamWrapper(std::move(pStream)));
+ xml::sax::InputSource aParserInput;
+ aParserInput.aInputStream = xInputStream;
+
+ uno::Reference<xml::sax::XParser> xParser = xml::sax::Parser::create(xComponentContext);
+ rtl::Reference<SfxClassificationParser> xClassificationParser(new SfxClassificationParser());
+ xParser->setDocumentHandler(xClassificationParser);
+ try
+ {
+ xParser->parseStream(aParserInput);
+ }
+ catch (const xml::sax::SAXParseException&)
+ {
+ TOOLS_WARN_EXCEPTION("sfx.view", "parsePolicy() failed");
+ }
+ m_aCategories = xClassificationParser->m_aCategories;
+ m_aMarkings = xClassificationParser->m_aMarkings;
+ m_aIPParts = xClassificationParser->m_aIPParts;
+ m_aIPPartNumbers = xClassificationParser->m_aIPPartNumbers;
+}
+
+static bool lcl_containsProperty(const uno::Sequence<beans::Property>& rProperties, std::u16string_view rName)
+{
+ return std::any_of(rProperties.begin(), rProperties.end(), [&](const beans::Property& rProperty)
+ {
+ return rProperty.Name == rName;
+ });
+}
+
+void SfxClassificationHelper::Impl::setStartValidity(SfxClassificationPolicyType eType)
+{
+ auto itCategory = m_aCategory.find(eType);
+ if (itCategory == m_aCategory.end())
+ return;
+
+ SfxClassificationCategory& rCategory = itCategory->second;
+ auto it = rCategory.m_aLabels.find(policyTypeToString(eType) + PROP_STARTVALIDITY());
+ if (it != rCategory.m_aLabels.end())
+ {
+ if (it->second == PROP_NONE())
+ {
+ // The policy left the start date unchanged, replace it with the system time.
+ util::DateTime aDateTime = DateTime(DateTime::SYSTEM).GetUNODateTime();
+ it->second = utl::toISO8601(aDateTime);
+ }
+ }
+}
+
+void SfxClassificationHelper::Impl::pushToDocumentProperties()
+{
+ uno::Reference<beans::XPropertyContainer> xPropertyContainer = m_xDocumentProperties->getUserDefinedProperties();
+ uno::Reference<beans::XPropertySet> xPropertySet(xPropertyContainer, uno::UNO_QUERY);
+ uno::Sequence<beans::Property> aProperties = xPropertySet->getPropertySetInfo()->getProperties();
+ for (auto& rPair : m_aCategory)
+ {
+ SfxClassificationPolicyType eType = rPair.first;
+ SfxClassificationCategory& rCategory = rPair.second;
+ std::map<OUString, OUString> aLabels = rCategory.m_aLabels;
+ aLabels[policyTypeToString(eType) + PROP_BACNAME()] = rCategory.m_aName;
+ for (const auto& rLabel : aLabels)
+ {
+ try
+ {
+ if (lcl_containsProperty(aProperties, rLabel.first))
+ xPropertySet->setPropertyValue(rLabel.first, uno::Any(rLabel.second));
+ else
+ xPropertyContainer->addProperty(rLabel.first, beans::PropertyAttribute::REMOVABLE, uno::Any(rLabel.second));
+ }
+ catch (const uno::Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("sfx.view", "pushDocumentProperties() failed for property " << rLabel.first);
+ }
+ }
+ }
+}
+
+bool SfxClassificationHelper::IsClassified(const uno::Reference<document::XDocumentProperties>& xDocumentProperties)
+{
+ uno::Reference<beans::XPropertyContainer> xPropertyContainer = xDocumentProperties->getUserDefinedProperties();
+ if (!xPropertyContainer.is())
+ return false;
+
+ uno::Reference<beans::XPropertySet> xPropertySet(xPropertyContainer, uno::UNO_QUERY);
+ const uno::Sequence<beans::Property> aProperties = xPropertySet->getPropertySetInfo()->getProperties();
+ for (const beans::Property& rProperty : aProperties)
+ {
+ if (rProperty.Name.startsWith("urn:bails:"))
+ return true;
+ }
+
+ return false;
+}
+
+SfxClassificationCheckPasteResult SfxClassificationHelper::CheckPaste(const uno::Reference<document::XDocumentProperties>& xSource,
+ const uno::Reference<document::XDocumentProperties>& xDestination)
+{
+ if (!SfxClassificationHelper::IsClassified(xSource))
+ // No classification on the source side. Return early, regardless the
+ // state of the destination side.
+ return SfxClassificationCheckPasteResult::None;
+
+ if (!SfxClassificationHelper::IsClassified(xDestination))
+ {
+ // Paste from a classified document to a non-classified one -> deny.
+ return SfxClassificationCheckPasteResult::TargetDocNotClassified;
+ }
+
+ // Remaining case: paste between two classified documents.
+ SfxClassificationHelper aSource(xSource);
+ SfxClassificationHelper aDestination(xDestination);
+ if (aSource.GetImpactScale() != aDestination.GetImpactScale())
+ // It's possible to compare them if they have the same scale.
+ return SfxClassificationCheckPasteResult::None;
+
+ if (aSource.GetImpactLevel() > aDestination.GetImpactLevel())
+ // Paste from a doc that has higher classification -> deny.
+ return SfxClassificationCheckPasteResult::DocClassificationTooLow;
+
+ return SfxClassificationCheckPasteResult::None;
+}
+
+bool SfxClassificationHelper::ShowPasteInfo(SfxClassificationCheckPasteResult eResult)
+{
+ switch (eResult)
+ {
+ case SfxClassificationCheckPasteResult::None:
+ {
+ return true;
+ }
+ break;
+ case SfxClassificationCheckPasteResult::TargetDocNotClassified:
+ {
+ if (!Application::IsHeadlessModeEnabled())
+ {
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(nullptr,
+ VclMessageType::Info, VclButtonsType::Ok,
+ SfxResId(STR_TARGET_DOC_NOT_CLASSIFIED)));
+ xBox->run();
+ }
+ return false;
+ }
+ break;
+ case SfxClassificationCheckPasteResult::DocClassificationTooLow:
+ {
+ if (!Application::IsHeadlessModeEnabled())
+ {
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(nullptr,
+ VclMessageType::Info, VclButtonsType::Ok,
+ SfxResId(STR_DOC_CLASSIFICATION_TOO_LOW)));
+ xBox->run();
+ }
+ return false;
+ }
+ break;
+ }
+
+ return true;
+}
+
+SfxClassificationHelper::SfxClassificationHelper(const uno::Reference<document::XDocumentProperties>& xDocumentProperties, bool bUseLocalizedPolicy)
+ : m_pImpl(std::make_unique<Impl>(xDocumentProperties, bUseLocalizedPolicy))
+{
+ if (!xDocumentProperties.is())
+ return;
+
+ uno::Reference<beans::XPropertyContainer> xPropertyContainer = xDocumentProperties->getUserDefinedProperties();
+ if (!xPropertyContainer.is())
+ return;
+
+ uno::Reference<beans::XPropertySet> xPropertySet(xPropertyContainer, uno::UNO_QUERY);
+ const uno::Sequence<beans::Property> aProperties = xPropertySet->getPropertySetInfo()->getProperties();
+ for (const beans::Property& rProperty : aProperties)
+ {
+ if (!rProperty.Name.startsWith("urn:bails:"))
+ continue;
+
+ uno::Any aAny = xPropertySet->getPropertyValue(rProperty.Name);
+ OUString aValue;
+ if (aAny >>= aValue)
+ {
+ SfxClassificationPolicyType eType = stringToPolicyType(rProperty.Name);
+ OUString aPrefix = policyTypeToString(eType);
+ if (!rProperty.Name.startsWith(aPrefix))
+ // It's a prefix we did not recognize, ignore.
+ continue;
+
+ //TODO: Support abbreviated names(?)
+ if (rProperty.Name == Concat2View(aPrefix + PROP_BACNAME()))
+ m_pImpl->m_aCategory[eType].m_aName = aValue;
+ else
+ m_pImpl->m_aCategory[eType].m_aLabels[rProperty.Name] = aValue;
+ }
+ }
+}
+
+SfxClassificationHelper::~SfxClassificationHelper() = default;
+
+std::vector<OUString> const & SfxClassificationHelper::GetMarkings() const
+{
+ return m_pImpl->m_aMarkings;
+}
+
+std::vector<OUString> const & SfxClassificationHelper::GetIntellectualPropertyParts() const
+{
+ return m_pImpl->m_aIPParts;
+}
+
+std::vector<OUString> const & SfxClassificationHelper::GetIntellectualPropertyPartNumbers() const
+{
+ return m_pImpl->m_aIPPartNumbers;
+}
+
+const OUString& SfxClassificationHelper::GetBACName(SfxClassificationPolicyType eType) const
+{
+ return m_pImpl->m_aCategory[eType].m_aName;
+}
+
+const OUString& SfxClassificationHelper::GetAbbreviatedBACName(const OUString& sFullName)
+{
+ for (const auto& category : m_pImpl->m_aCategories)
+ {
+ if (category.m_aName == sFullName)
+ return category.m_aAbbreviatedName;
+ }
+
+ return sFullName;
+}
+
+OUString SfxClassificationHelper::GetBACNameForIdentifier(std::u16string_view sIdentifier)
+{
+ if (sIdentifier.empty())
+ return "";
+
+ for (const auto& category : m_pImpl->m_aCategories)
+ {
+ if (category.m_aIdentifier == sIdentifier)
+ return category.m_aName;
+ }
+
+ return "";
+}
+
+OUString SfxClassificationHelper::GetHigherClass(const OUString& first, const OUString& second)
+{
+ size_t nFirstConfidentiality = 0;
+ size_t nSecondConfidentiality = 0;
+ for (const auto& category : m_pImpl->m_aCategories)
+ {
+ if (category.m_aName == first)
+ nFirstConfidentiality = category.m_nConfidentiality;
+ if (category.m_aName == second)
+ nSecondConfidentiality = category.m_nConfidentiality;
+ }
+
+ return nFirstConfidentiality >= nSecondConfidentiality ? first : second;
+}
+
+bool SfxClassificationHelper::HasImpactLevel()
+{
+ auto itCategory = m_pImpl->m_aCategory.find(SfxClassificationPolicyType::IntellectualProperty);
+ if (itCategory == m_pImpl->m_aCategory.end())
+ return false;
+
+ SfxClassificationCategory& rCategory = itCategory->second;
+ auto it = rCategory.m_aLabels.find(PROP_PREFIX_INTELLECTUALPROPERTY() + PROP_IMPACTSCALE());
+ if (it == rCategory.m_aLabels.end())
+ return false;
+
+ it = rCategory.m_aLabels.find(PROP_PREFIX_INTELLECTUALPROPERTY() + PROP_IMPACTLEVEL());
+ return it != rCategory.m_aLabels.end();
+}
+
+bool SfxClassificationHelper::HasDocumentHeader()
+{
+ auto itCategory = m_pImpl->m_aCategory.find(SfxClassificationPolicyType::IntellectualProperty);
+ if (itCategory == m_pImpl->m_aCategory.end())
+ return false;
+
+ SfxClassificationCategory& rCategory = itCategory->second;
+ auto it = rCategory.m_aLabels.find(PROP_PREFIX_INTELLECTUALPROPERTY() + PROP_DOCHEADER());
+ return it != rCategory.m_aLabels.end() && !it->second.isEmpty();
+}
+
+bool SfxClassificationHelper::HasDocumentFooter()
+{
+ auto itCategory = m_pImpl->m_aCategory.find(SfxClassificationPolicyType::IntellectualProperty);
+ if (itCategory == m_pImpl->m_aCategory.end())
+ return false;
+
+ SfxClassificationCategory& rCategory = itCategory->second;
+ auto it = rCategory.m_aLabels.find(PROP_PREFIX_INTELLECTUALPROPERTY() + PROP_DOCFOOTER());
+ return it != rCategory.m_aLabels.end() && !it->second.isEmpty();
+}
+
+InfobarType SfxClassificationHelper::GetImpactLevelType()
+{
+ InfobarType aRet;
+
+ aRet = InfobarType::WARNING;
+
+ auto itCategory = m_pImpl->m_aCategory.find(SfxClassificationPolicyType::IntellectualProperty);
+ if (itCategory == m_pImpl->m_aCategory.end())
+ return aRet;
+
+ SfxClassificationCategory& rCategory = itCategory->second;
+ auto it = rCategory.m_aLabels.find(PROP_PREFIX_INTELLECTUALPROPERTY() + PROP_IMPACTSCALE());
+ if (it == rCategory.m_aLabels.end())
+ return aRet;
+ OUString aScale = it->second;
+
+ it = rCategory.m_aLabels.find(PROP_PREFIX_INTELLECTUALPROPERTY() + PROP_IMPACTLEVEL());
+ if (it == rCategory.m_aLabels.end())
+ return aRet;
+ OUString aLevel = it->second;
+
+ // The spec defines two valid scale values: FIPS-199 and UK-Cabinet.
+ if (aScale == "UK-Cabinet")
+ {
+ if (aLevel == "0")
+ aRet = InfobarType::SUCCESS;
+ else if (aLevel == "1")
+ aRet = InfobarType::WARNING;
+ else if (aLevel == "2")
+ aRet = InfobarType::WARNING;
+ else if (aLevel == "3")
+ aRet = InfobarType::DANGER;
+ }
+ else if (aScale == "FIPS-199")
+ {
+ if (aLevel == "Low")
+ aRet = InfobarType::SUCCESS;
+ else if (aLevel == "Moderate")
+ aRet = InfobarType::WARNING;
+ else if (aLevel == "High")
+ aRet = InfobarType::DANGER;
+ }
+ return aRet;
+}
+
+sal_Int32 SfxClassificationHelper::GetImpactLevel()
+{
+ sal_Int32 nRet = -1;
+
+ auto itCategory = m_pImpl->m_aCategory.find(SfxClassificationPolicyType::IntellectualProperty);
+ if (itCategory == m_pImpl->m_aCategory.end())
+ return nRet;
+
+ SfxClassificationCategory& rCategory = itCategory->second;
+ auto it = rCategory.m_aLabels.find(PROP_PREFIX_INTELLECTUALPROPERTY() + PROP_IMPACTSCALE());
+ if (it == rCategory.m_aLabels.end())
+ return nRet;
+ OUString aScale = it->second;
+
+ it = rCategory.m_aLabels.find(PROP_PREFIX_INTELLECTUALPROPERTY() + PROP_IMPACTLEVEL());
+ if (it == rCategory.m_aLabels.end())
+ return nRet;
+ OUString aLevel = it->second;
+
+ if (aScale == "UK-Cabinet")
+ {
+ sal_Int32 nValue = aLevel.toInt32();
+ if (nValue < 0 || nValue > 3)
+ return nRet;
+ nRet = nValue;
+ }
+ else if (aScale == "FIPS-199")
+ {
+ static std::map<OUString, sal_Int32> const aValues
+ {
+ { "Low", 0 },
+ { "Moderate", 1 },
+ { "High", 2 }
+ };
+ auto itValues = aValues.find(aLevel);
+ if (itValues == aValues.end())
+ return nRet;
+ nRet = itValues->second;
+ }
+
+ return nRet;
+}
+
+OUString SfxClassificationHelper::GetImpactScale()
+{
+ auto itCategory = m_pImpl->m_aCategory.find(SfxClassificationPolicyType::IntellectualProperty);
+ if (itCategory == m_pImpl->m_aCategory.end())
+ return OUString();
+
+ SfxClassificationCategory& rCategory = itCategory->second;
+ auto it = rCategory.m_aLabels.find(PROP_PREFIX_INTELLECTUALPROPERTY() + PROP_IMPACTSCALE());
+ if (it != rCategory.m_aLabels.end())
+ return it->second;
+
+ return OUString();
+}
+
+OUString SfxClassificationHelper::GetDocumentWatermark()
+{
+ auto itCategory = m_pImpl->m_aCategory.find(SfxClassificationPolicyType::IntellectualProperty);
+ if (itCategory == m_pImpl->m_aCategory.end())
+ return OUString();
+
+ SfxClassificationCategory& rCategory = itCategory->second;
+ auto it = rCategory.m_aLabels.find(PROP_PREFIX_INTELLECTUALPROPERTY() + PROP_DOCWATERMARK());
+ if (it != rCategory.m_aLabels.end())
+ return it->second;
+
+ return OUString();
+}
+
+std::vector<OUString> SfxClassificationHelper::GetBACNames()
+{
+ if (m_pImpl->m_aCategories.empty())
+ m_pImpl->parsePolicy();
+
+ std::vector<OUString> aRet;
+ std::transform(m_pImpl->m_aCategories.begin(), m_pImpl->m_aCategories.end(), std::back_inserter(aRet), [](const SfxClassificationCategory& rCategory)
+ {
+ return rCategory.m_aName;
+ });
+ return aRet;
+}
+
+std::vector<OUString> SfxClassificationHelper::GetBACIdentifiers()
+{
+ if (m_pImpl->m_aCategories.empty())
+ m_pImpl->parsePolicy();
+
+ std::vector<OUString> aRet;
+ std::transform(m_pImpl->m_aCategories.begin(), m_pImpl->m_aCategories.end(), std::back_inserter(aRet), [](const SfxClassificationCategory& rCategory)
+ {
+ return rCategory.m_aIdentifier;
+ });
+ return aRet;
+}
+
+std::vector<OUString> SfxClassificationHelper::GetAbbreviatedBACNames()
+{
+ if (m_pImpl->m_aCategories.empty())
+ m_pImpl->parsePolicy();
+
+ std::vector<OUString> aRet;
+ std::transform(m_pImpl->m_aCategories.begin(), m_pImpl->m_aCategories.end(), std::back_inserter(aRet), [](const SfxClassificationCategory& rCategory)
+ {
+ return rCategory.m_aAbbreviatedName;
+ });
+ return aRet;
+}
+
+void SfxClassificationHelper::SetBACName(const OUString& rName, SfxClassificationPolicyType eType)
+{
+ if (m_pImpl->m_aCategories.empty())
+ m_pImpl->parsePolicy();
+
+ auto it = std::find_if(m_pImpl->m_aCategories.begin(), m_pImpl->m_aCategories.end(), [&](const SfxClassificationCategory& rCategory)
+ {
+ return rCategory.m_aName == rName;
+ });
+ if (it == m_pImpl->m_aCategories.end())
+ {
+ SAL_WARN("sfx.view", "'" << rName << "' is not a recognized category name");
+ return;
+ }
+
+ m_pImpl->m_aCategory[eType].m_aName = it->m_aName;
+ m_pImpl->m_aCategory[eType].m_aAbbreviatedName = it->m_aAbbreviatedName;
+ m_pImpl->m_aCategory[eType].m_nConfidentiality = it->m_nConfidentiality;
+ m_pImpl->m_aCategory[eType].m_aLabels.clear();
+ const OUString& rPrefix = policyTypeToString(eType);
+ for (const auto& rLabel : it->m_aLabels)
+ m_pImpl->m_aCategory[eType].m_aLabels[rPrefix + rLabel.first] = rLabel.second;
+
+ m_pImpl->setStartValidity(eType);
+ m_pImpl->pushToDocumentProperties();
+ SfxViewFrame* pViewFrame = SfxViewFrame::Current();
+ if (!pViewFrame)
+ return;
+
+ UpdateInfobar(*pViewFrame);
+}
+
+void SfxClassificationHelper::UpdateInfobar(SfxViewFrame& rViewFrame)
+{
+ OUString aBACName = GetBACName(SfxClassificationPolicyType::IntellectualProperty);
+ bool bImpactLevel = HasImpactLevel();
+ if (!aBACName.isEmpty() && bImpactLevel)
+ {
+ OUString aMessage = SfxResId(STR_CLASSIFIED_DOCUMENT);
+ aMessage = aMessage.replaceFirst("%1", aBACName);
+
+ rViewFrame.RemoveInfoBar(u"classification");
+ rViewFrame.AppendInfoBar("classification", "", aMessage, GetImpactLevelType());
+ }
+}
+
+SfxClassificationPolicyType SfxClassificationHelper::stringToPolicyType(std::u16string_view rType)
+{
+ if (o3tl::starts_with(rType, PROP_PREFIX_EXPORTCONTROL()))
+ return SfxClassificationPolicyType::ExportControl;
+ else if (o3tl::starts_with(rType, PROP_PREFIX_NATIONALSECURITY()))
+ return SfxClassificationPolicyType::NationalSecurity;
+ else
+ return SfxClassificationPolicyType::IntellectualProperty;
+}
+
+const OUString& SfxClassificationHelper::policyTypeToString(SfxClassificationPolicyType eType)
+{
+ switch (eType)
+ {
+ case SfxClassificationPolicyType::ExportControl:
+ return PROP_PREFIX_EXPORTCONTROL();
+ case SfxClassificationPolicyType::NationalSecurity:
+ return PROP_PREFIX_NATIONALSECURITY();
+ case SfxClassificationPolicyType::IntellectualProperty:
+ break;
+ }
+
+ return PROP_PREFIX_INTELLECTUALPROPERTY();
+}
+
+const OUString& SfxClassificationHelper::PROP_DOCHEADER()
+{
+ static constexpr OUString sProp(u"Marking:document-header"_ustr);
+ return sProp;
+}
+
+const OUString& SfxClassificationHelper::PROP_DOCFOOTER()
+{
+ static constexpr OUString sProp(u"Marking:document-footer"_ustr);
+ return sProp;
+}
+
+const OUString& SfxClassificationHelper::PROP_DOCWATERMARK()
+{
+ static constexpr OUString sProp(u"Marking:document-watermark"_ustr);
+ return sProp;
+}
+
+const OUString& SfxClassificationHelper::PROP_PREFIX_INTELLECTUALPROPERTY()
+{
+ static constexpr OUString sProp(u"urn:bails:IntellectualProperty:"_ustr);
+ return sProp;
+}
+
+SfxClassificationPolicyType SfxClassificationHelper::getPolicyType()
+{
+ if (utl::ConfigManager::IsFuzzing())
+ return SfxClassificationPolicyType::IntellectualProperty;
+ sal_Int32 nPolicyTypeNumber = officecfg::Office::Common::Classification::Policy::get();
+ auto eType = static_cast<SfxClassificationPolicyType>(nPolicyTypeNumber);
+ return eType;
+}
+
+namespace sfx
+{
+
+namespace
+{
+
+OUString getProperty(uno::Reference<beans::XPropertyContainer> const& rxPropertyContainer,
+ OUString const& rName)
+{
+ try
+ {
+ uno::Reference<beans::XPropertySet> xPropertySet(rxPropertyContainer, uno::UNO_QUERY);
+ return xPropertySet->getPropertyValue(rName).get<OUString>();
+ }
+ catch (const css::uno::Exception&)
+ {
+ }
+
+ return OUString();
+}
+
+} // end anonymous namespace
+
+sfx::ClassificationCreationOrigin getCreationOriginProperty(uno::Reference<beans::XPropertyContainer> const & rxPropertyContainer,
+ sfx::ClassificationKeyCreator const & rKeyCreator)
+{
+ OUString sValue = getProperty(rxPropertyContainer, rKeyCreator.makeCreationOriginKey());
+ if (sValue.isEmpty())
+ return sfx::ClassificationCreationOrigin::NONE;
+
+ return (sValue == "BAF_POLICY")
+ ? sfx::ClassificationCreationOrigin::BAF_POLICY
+ : sfx::ClassificationCreationOrigin::MANUAL;
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/view/frame.cxx b/sfx2/source/view/frame.cxx
new file mode 100644
index 0000000000..ad04da3534
--- /dev/null
+++ b/sfx2/source/view/frame.cxx
@@ -0,0 +1,720 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <com/sun/star/uno/Reference.h>
+#include <com/sun/star/awt/XTopWindow.hpp>
+#include <com/sun/star/awt/XWindow.hpp>
+#include <com/sun/star/util/XCloseable.hpp>
+#include <com/sun/star/util/CloseVetoException.hpp>
+#include <com/sun/star/lang/DisposedException.hpp>
+#include <com/sun/star/container/XChild.hpp>
+#include <com/sun/star/embed/XEmbeddedObject.hpp>
+
+#include <svl/intitem.hxx>
+#include <svl/stritem.hxx>
+#include <tools/svborder.hxx>
+#include <comphelper/diagnose_ex.hxx>
+
+#include <appdata.hxx>
+#include <sfx2/app.hxx>
+#include <sfx2/event.hxx>
+#include <sfx2/frame.hxx>
+#include <sfx2/objsh.hxx>
+#include <sfx2/dispatch.hxx>
+#include <sfx2/docfile.hxx>
+#include <sfx2/docfilt.hxx>
+#include <sfx2/frmdescr.hxx>
+#include <sfx2/sfxsids.hrc>
+#include <sfx2/viewsh.hxx>
+#include <sfx2/viewfrm.hxx>
+#include "impframe.hxx"
+#include <utility>
+#include <workwin.hxx>
+#include <sfx2/ipclient.hxx>
+#include <vector>
+
+using namespace com::sun::star;
+
+static std::vector<SfxFrame*> gaFramesArr_Impl;
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::util;
+using namespace ::com::sun::star::frame;
+using namespace ::com::sun::star::container;
+
+SfxPoolItem* SfxUnoAnyItem::CreateDefault()
+{
+ return new SfxUnoAnyItem(0, uno::Any());
+}
+
+SfxPoolItem* SfxUnoFrameItem::CreateDefault()
+{
+ return new SfxUnoFrameItem();
+}
+void SfxFrame::Construct_Impl()
+{
+ m_pImpl.reset(new SfxFrame_Impl);
+ gaFramesArr_Impl.push_back( this );
+}
+
+
+SfxFrame::~SfxFrame()
+{
+ RemoveTopFrame_Impl( this );
+ m_pWindow.disposeAndClear();
+
+ auto it = std::find( gaFramesArr_Impl.begin(), gaFramesArr_Impl.end(), this );
+ if ( it != gaFramesArr_Impl.end() )
+ gaFramesArr_Impl.erase( it );
+
+ delete m_pImpl->pDescr;
+}
+
+bool SfxFrame::DoClose()
+{
+ // Actually, one more PrepareClose is still needed!
+ bool bRet = false;
+ if ( !m_pImpl->bClosing )
+ {
+ m_pImpl->bClosing = true;
+ CancelTransfers();
+
+ // now close frame; it will be deleted if this call is successful, so don't use any members after that!
+ bRet = true;
+ try
+ {
+ Reference< XCloseable > xCloseable ( m_pImpl->xFrame, UNO_QUERY );
+ if (xCloseable.is())
+ xCloseable->close(true);
+ else if ( m_pImpl->xFrame.is() )
+ {
+ Reference < XFrame > xFrame = m_pImpl->xFrame;
+ xFrame->setComponent( Reference < css::awt::XWindow >(), Reference < XController >() );
+ xFrame->dispose();
+ }
+ else
+ DoClose_Impl();
+ }
+ catch( css::util::CloseVetoException& )
+ {
+ m_pImpl->bClosing = false;
+ bRet = false;
+ }
+ catch( css::lang::DisposedException& )
+ {
+ }
+ }
+
+ return bRet;
+}
+
+void SfxFrame::DoClose_Impl()
+{
+ SfxBindings* pBindings = nullptr;
+ if ( m_pImpl->pCurrentViewFrame )
+ pBindings = &m_pImpl->pCurrentViewFrame->GetBindings();
+
+ // For internal tasks Controllers and Tools must be cleared
+ if ( m_pImpl->pWorkWin )
+ m_pImpl->pWorkWin->DeleteControllers_Impl();
+
+ if ( m_pImpl->pCurrentViewFrame )
+ m_pImpl->pCurrentViewFrame->Close();
+
+ if ( m_pImpl->bOwnsBindings )
+ {
+ delete pBindings;
+ pBindings = nullptr;
+ }
+
+ delete this;
+}
+
+bool SfxFrame::DocIsModified_Impl()
+{
+ return m_pImpl->pCurrentViewFrame && m_pImpl->pCurrentViewFrame->GetObjectShell() &&
+ m_pImpl->pCurrentViewFrame->GetObjectShell()->IsModified();
+}
+
+bool SfxFrame::PrepareClose_Impl( bool bUI )
+{
+ bool bRet = true;
+
+ // prevent recursive calls
+ if( !m_pImpl->bPrepClosing )
+ {
+ m_pImpl->bPrepClosing = true;
+
+ SfxObjectShell* pCur = GetCurrentDocument() ;
+ if( pCur )
+ {
+ // SFX components have a known behaviour
+ // First check if this frame is the only view to its current document
+ bool bOther = false;
+ for ( const SfxViewFrame *pFrame = SfxViewFrame::GetFirst( pCur );
+ !bOther && pFrame; pFrame = SfxViewFrame::GetNext( *pFrame, pCur ) )
+ {
+ bOther = ( &pFrame->GetFrame() != this );
+ }
+
+ SfxGetpApp()->NotifyEvent( SfxViewEventHint(SfxEventHintId::PrepareCloseView, GlobalEventConfig::GetEventName( GlobalEventId::PREPARECLOSEVIEW ), pCur, GetController() ) );
+
+ if ( bOther )
+ // if there are other views only the current view of this frame must be asked
+ bRet = GetCurrentViewFrame()->GetViewShell()->PrepareClose( bUI );
+ else
+ // otherwise ask the document
+ bRet = pCur->PrepareClose( bUI );
+ }
+
+ m_pImpl->bPrepClosing = false;
+ }
+
+ if ( bRet && m_pImpl->pWorkWin )
+ // if closing was accepted by the component the UI subframes must be asked also
+ bRet = m_pImpl->pWorkWin->PrepareClose_Impl();
+
+ return bRet;
+}
+
+
+bool SfxFrame::IsClosing_Impl() const
+{
+ return m_pImpl->bClosing;
+}
+
+void SfxFrame::SetIsClosing_Impl()
+{
+ m_pImpl->bClosing = true;
+}
+
+void SfxFrame::CancelTransfers()
+{
+ if( m_pImpl->bInCancelTransfers )
+ return;
+
+ m_pImpl->bInCancelTransfers = true;
+ SfxObjectShell* pObj = GetCurrentDocument();
+ if( pObj ) //&& !( pObj->Get_Impl()->nLoadedFlags & SfxLoadedFlags::ALL ))
+ {
+ SfxViewFrame* pFrm;
+ for( pFrm = SfxViewFrame::GetFirst( pObj );
+ pFrm && &pFrm->GetFrame() == this;
+ pFrm = SfxViewFrame::GetNext( *pFrm, pObj ) ) ;
+ // No more Frame in Document -> Cancel
+ if( !pFrm )
+ {
+ pObj->CancelTransfers();
+ GetCurrentDocument()->Broadcast( SfxHint(SfxHintId::TitleChanged) );
+ }
+ }
+
+ // Check if StarOne-Loader should be canceled
+ SfxFrameWeakRef wFrame( this );
+ if (wFrame.is())
+ m_pImpl->bInCancelTransfers = false;
+}
+
+SfxViewFrame* SfxFrame::GetCurrentViewFrame() const
+{
+ return m_pImpl->pCurrentViewFrame;
+}
+
+bool SfxFrame::IsAutoLoadLocked_Impl() const
+{
+ // Its own Document is locked?
+ const SfxObjectShell* pObjSh = GetCurrentDocument();
+ if ( !pObjSh || !pObjSh->IsAutoLoadLocked() )
+ return false;
+
+ // otherwise allow AutoLoad
+ return true;
+}
+
+SfxObjectShell* SfxFrame::GetCurrentDocument() const
+{
+ return m_pImpl->pCurrentViewFrame ?
+ m_pImpl->pCurrentViewFrame->GetObjectShell() :
+ nullptr;
+}
+
+void SfxFrame::SetCurrentViewFrame_Impl( SfxViewFrame *pFrame )
+{
+ m_pImpl->pCurrentViewFrame = pFrame;
+}
+
+bool SfxFrame::GetHasTitle() const
+{
+ return m_pImpl->mbHasTitle;
+}
+
+void SfxFrame::SetHasTitle( bool n )
+{
+ m_pImpl->mbHasTitle = n;
+}
+
+void SfxFrame::GetViewData_Impl()
+{
+ // Update all modifiable data between load and unload, the
+ // fixed data is only processed once (after PrepareForDoc_Impl in
+ // updateDescriptor) to save time.
+
+ SfxViewFrame* pViewFrame = GetCurrentViewFrame();
+ if( pViewFrame && pViewFrame->GetViewShell() )
+ {
+ SfxItemSet *pSet = GetDescriptor()->GetArgs();
+ if ( GetController().is() && pSet->GetItemState( SID_VIEW_DATA ) != SfxItemState::SET )
+ {
+ css::uno::Any aData = GetController()->getViewData();
+ pSet->Put( SfxUnoAnyItem( SID_VIEW_DATA, aData ) );
+ }
+
+ if ( pViewFrame->GetCurViewId() )
+ pSet->Put( SfxUInt16Item( SID_VIEW_ID, static_cast<sal_uInt16>(pViewFrame->GetCurViewId()) ) );
+ }
+}
+
+void SfxFrame::UpdateDescriptor( SfxObjectShell const *pDoc )
+{
+ // For PrepareForDoc_Impl frames, the descriptor of the updated
+ // and new itemset to be initialized. All data fir restoring the view
+ // are thus saved. If the document be replaced, GetViewData_Impl (so)
+ // the latest information hinzugef by "added. All together then the
+ // browser-history saved in. When you activate such frame pick entry
+ // is complete itemsets and the descriptor in the OpenDoc sent;.
+ // Here only the fixed properties identified "other adjustable, the
+ // retrieved by GetViewData (saves time).
+
+ assert(pDoc && "NULL-Document inserted ?!");
+
+ const SfxMedium *pMed = pDoc->GetMedium();
+ GetDescriptor()->SetActualURL();
+
+ // Mark FileOpen parameter
+ SfxItemSet& rItemSet = pMed->GetItemSet();
+
+ const std::shared_ptr<const SfxFilter>& pFilter = pMed->GetFilter();
+ OUString aFilter;
+ if ( pFilter )
+ aFilter = pFilter->GetFilterName();
+
+ const SfxStringItem* pRefererItem = rItemSet.GetItem<SfxStringItem>(SID_REFERER, false);
+ const SfxStringItem* pOptionsItem = rItemSet.GetItem<SfxStringItem>(SID_FILE_FILTEROPTIONS, false);
+ const SfxStringItem* pTitle1Item = rItemSet.GetItem<SfxStringItem>(SID_DOCINFO_TITLE, false);
+
+ SfxItemSet *pSet = GetDescriptor()->GetArgs();
+
+ // Delete all old Items
+ pSet->ClearItem();
+
+ if ( pRefererItem )
+ pSet->Put( *pRefererItem );
+ else
+ pSet->Put( SfxStringItem( SID_REFERER, OUString() ) );
+
+ if ( pOptionsItem )
+ pSet->Put( *pOptionsItem );
+
+ if ( pTitle1Item )
+ pSet->Put( *pTitle1Item );
+
+ pSet->Put( SfxStringItem( SID_FILTER_NAME, aFilter ));
+}
+
+
+SfxFrameDescriptor* SfxFrame::GetDescriptor() const
+{
+ // Create a FrameDescriptor On Demand; if there is no TopLevel-Frame
+ // will result in an error, as no valid link is created.
+
+ if ( !m_pImpl->pDescr )
+ {
+ DBG_ASSERT( true, "No TopLevel-Frame, but no Descriptor!" );
+ m_pImpl->pDescr = new SfxFrameDescriptor;
+ if ( GetCurrentDocument() )
+ m_pImpl->pDescr->SetURL( GetCurrentDocument()->GetMedium()->GetOrigURL() );
+ }
+ return m_pImpl->pDescr;
+}
+
+void SfxFrame::GetDefaultTargetList(TargetList& rList)
+{
+ // An empty string for 'No Target'
+ rList.emplace_back( );
+ rList.emplace_back( "_top" );
+ rList.emplace_back( "_parent" );
+ rList.emplace_back( "_blank" );
+ rList.emplace_back( "_self" );
+}
+
+void SfxFrame::InsertTopFrame_Impl( SfxFrame* pFrame )
+{
+ auto& rArr = SfxGetpApp()->Get_Impl()->vTopFrames;
+ rArr.push_back( pFrame );
+}
+
+void SfxFrame::RemoveTopFrame_Impl( SfxFrame* pFrame )
+{
+ auto& rArr = SfxGetpApp()->Get_Impl()->vTopFrames;
+ auto it = std::find( rArr.begin(), rArr.end(), pFrame );
+ if ( it != rArr.end() )
+ rArr.erase( it );
+}
+
+SfxFrameItem::SfxFrameItem( sal_uInt16 nWhichId, SfxViewFrame const *p )
+ : SfxPoolItem( nWhichId ), pFrame( p ? &p->GetFrame() : nullptr )
+{
+ wFrame = pFrame;
+}
+
+SfxFrameItem::SfxFrameItem( sal_uInt16 nWhichId, SfxFrame *p ):
+ SfxPoolItem( nWhichId ),
+ pFrame( p ), wFrame( p )
+{
+}
+
+SfxFrameItem::SfxFrameItem( SfxFrame *p ):
+ SfxPoolItem( 0 ),
+ pFrame( p ), wFrame( p )
+{
+}
+
+bool SfxFrameItem::operator==( const SfxPoolItem &rItem ) const
+{
+ return SfxPoolItem::operator==(rItem) &&
+ static_cast<const SfxFrameItem&>(rItem).pFrame == pFrame &&
+ static_cast<const SfxFrameItem&>(rItem).wFrame == wFrame;
+}
+
+SfxFrameItem* SfxFrameItem::Clone( SfxItemPool *) const
+{
+ SfxFrameItem* pNew = new SfxFrameItem( wFrame);
+ pNew->pFrame = pFrame;
+ return pNew;
+}
+
+bool SfxFrameItem::QueryValue( css::uno::Any& rVal, sal_uInt8 ) const
+{
+ if ( wFrame )
+ {
+ rVal <<= wFrame->GetFrameInterface();
+ return true;
+ }
+
+ return false;
+}
+
+bool SfxFrameItem::PutValue( const css::uno::Any& rVal, sal_uInt8 )
+{
+ Reference < XFrame > xFrame;
+ if ( (rVal >>= xFrame) && xFrame.is() )
+ {
+ SfxFrame* pFr = SfxFrame::GetFirst();
+ while ( pFr )
+ {
+ if ( pFr->GetFrameInterface() == xFrame )
+ {
+ wFrame = pFrame = pFr;
+ return true;
+ }
+
+ pFr = SfxFrame::GetNext( *pFr );
+ }
+ return true;
+ }
+
+ return false;
+}
+
+
+SfxUnoAnyItem::SfxUnoAnyItem( sal_uInt16 nWhichId, const css::uno::Any& rAny )
+ : SfxPoolItem( nWhichId )
+{
+ aValue = rAny;
+}
+
+bool SfxUnoAnyItem::operator==( const SfxPoolItem& rItem ) const
+{
+ assert(SfxPoolItem::operator==(rItem)); (void)rItem;
+ return false;
+}
+
+SfxUnoAnyItem* SfxUnoAnyItem::Clone( SfxItemPool *) const
+{
+ return new SfxUnoAnyItem( *this );
+}
+
+bool SfxUnoAnyItem::QueryValue( css::uno::Any& rVal, sal_uInt8 /*nMemberId*/ ) const
+{
+ rVal = aValue;
+ return true;
+}
+
+bool SfxUnoAnyItem::PutValue( const css::uno::Any& rVal, sal_uInt8 /*nMemberId*/ )
+{
+ aValue = rVal;
+ return true;
+}
+
+SfxUnoFrameItem::SfxUnoFrameItem()
+{
+}
+
+SfxUnoFrameItem::SfxUnoFrameItem( sal_uInt16 nWhichId, css::uno::Reference< css::frame::XFrame > i_xFrame )
+ : SfxPoolItem( nWhichId )
+ , m_xFrame(std::move( i_xFrame ))
+{
+}
+
+bool SfxUnoFrameItem::operator==( const SfxPoolItem& i_rItem ) const
+{
+ return SfxPoolItem::operator==(i_rItem) &&
+ static_cast< const SfxUnoFrameItem& >( i_rItem ).m_xFrame == m_xFrame;
+}
+
+SfxUnoFrameItem* SfxUnoFrameItem::Clone( SfxItemPool* ) const
+{
+ return new SfxUnoFrameItem( *this );
+}
+
+bool SfxUnoFrameItem::QueryValue( css::uno::Any& rVal, sal_uInt8 /*nMemberId*/ ) const
+{
+ rVal <<= m_xFrame;
+ return true;
+}
+
+bool SfxUnoFrameItem::PutValue( const css::uno::Any& rVal, sal_uInt8 /*nMemberId*/ )
+{
+ return ( rVal >>= m_xFrame );
+}
+
+css::uno::Reference< css::frame::XController > SfxFrame::GetController() const
+{
+ if ( m_pImpl->pCurrentViewFrame && m_pImpl->pCurrentViewFrame->GetViewShell() )
+ return m_pImpl->pCurrentViewFrame->GetViewShell()->GetController();
+ else
+ return css::uno::Reference< css::frame::XController > ();
+}
+
+const css::uno::Reference< css::frame::XFrame >& SfxFrame::GetFrameInterface() const
+{
+ return m_pImpl->xFrame;
+}
+
+void SfxFrame::SetFrameInterface_Impl( const css::uno::Reference< css::frame::XFrame >& rFrame )
+{
+ m_pImpl->xFrame = rFrame;
+ css::uno::Reference< css::frame::XDispatchRecorder > xRecorder;
+ if ( !rFrame.is() && GetCurrentViewFrame() )
+ GetCurrentViewFrame()->GetBindings().SetRecorder_Impl( xRecorder );
+}
+
+void SfxFrame::Appear()
+{
+ if ( GetCurrentViewFrame() )
+ {
+ GetCurrentViewFrame()->Show();
+ GetWindow().Show();
+ m_pImpl->xFrame->getContainerWindow()->setVisible( true );
+ Reference < css::awt::XTopWindow > xTopWindow( m_pImpl->xFrame->getContainerWindow(), UNO_QUERY );
+ if ( xTopWindow.is() )
+ xTopWindow->toFront();
+ }
+}
+
+void SfxFrame::AppearWithUpdate()
+{
+ Appear();
+ if ( GetCurrentViewFrame() )
+ GetCurrentViewFrame()->GetDispatcher()->Update_Impl( true );
+}
+
+void SfxFrame::SetOwnsBindings_Impl( bool bSet )
+{
+ m_pImpl->bOwnsBindings = bSet;
+}
+
+bool SfxFrame::OwnsBindings_Impl() const
+{
+ return m_pImpl->bOwnsBindings;
+}
+
+void SfxFrame::SetToolSpaceBorderPixel_Impl( const SvBorder& rBorder )
+{
+ m_pImpl->aBorder = rBorder;
+ SfxViewFrame *pF = GetCurrentViewFrame();
+ if ( !pF )
+ return;
+
+ Point aPos ( rBorder.Left(), rBorder.Top() );
+ Size aSize( GetWindow().GetOutputSizePixel() );
+ tools::Long nDeltaX = rBorder.Left() + rBorder.Right();
+ if ( aSize.Width() > nDeltaX )
+ aSize.AdjustWidth( -nDeltaX );
+ else
+ aSize.setWidth( 0 );
+
+ tools::Long nDeltaY = rBorder.Top() + rBorder.Bottom();
+ if ( aSize.Height() > nDeltaY )
+ aSize.AdjustHeight( -nDeltaY );
+ else
+ aSize.setHeight( 0 );
+
+ pF->GetWindow().SetPosSizePixel( aPos, aSize );
+}
+
+tools::Rectangle SfxFrame::GetTopOuterRectPixel_Impl() const
+{
+ Size aSize( GetWindow().GetOutputSizePixel() );
+ return tools::Rectangle( Point(), aSize );
+}
+
+SfxWorkWindow* SfxFrame::GetWorkWindow_Impl() const
+{
+ return m_pImpl->pWorkWin;
+}
+
+void SfxFrame::CreateWorkWindow_Impl()
+{
+ SfxFrame* pFrame = this;
+
+ if ( IsInPlace() )
+ {
+ // this makes sense only for inplace activated objects
+ try
+ {
+ Reference < XChild > xChild( GetCurrentDocument()->GetModel(), UNO_QUERY );
+ if ( xChild.is() )
+ {
+ Reference < XModel > xParent( xChild->getParent(), UNO_QUERY );
+ if ( xParent.is() )
+ {
+ Reference< XController > xParentCtrler = xParent->getCurrentController();
+ if ( xParentCtrler.is() )
+ {
+ Reference < XFrame > xFrame( xParentCtrler->getFrame() );
+ SfxFrame* pFr = SfxFrame::GetFirst();
+ while ( pFr )
+ {
+ if ( pFr->GetFrameInterface() == xFrame )
+ {
+ pFrame = pFr;
+ break;
+ }
+
+ pFr = SfxFrame::GetNext( *pFr );
+ }
+ }
+ }
+ }
+ }
+ catch(Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "sfx.view", "SfxFrame::CreateWorkWindow_Impl: Exception caught. Please try to submit a reproducible bug!");
+ }
+ }
+
+ m_pImpl->pWorkWin = new SfxWorkWindow( &pFrame->GetWindow(), this, pFrame );
+}
+
+void SfxFrame::GrabFocusOnComponent_Impl()
+{
+ if ( m_pImpl->bReleasingComponent )
+ {
+ GetWindow().GrabFocus();
+ return;
+ }
+
+ vcl::Window* pFocusWindow = &GetWindow();
+ if ( GetCurrentViewFrame() && GetCurrentViewFrame()->GetViewShell() && GetCurrentViewFrame()->GetViewShell()->GetWindow() )
+ pFocusWindow = GetCurrentViewFrame()->GetViewShell()->GetWindow();
+
+ if( !pFocusWindow->HasChildPathFocus() )
+ pFocusWindow->GrabFocus();
+}
+
+void SfxFrame::ReleasingComponent_Impl()
+{
+ m_pImpl->bReleasingComponent = true;
+}
+
+bool SfxFrame::IsInPlace() const
+{
+ return m_pImpl->bInPlace;
+}
+
+void SfxFrame::Resize()
+{
+ if ( IsClosing_Impl() )
+ return;
+
+ if ( OwnsBindings_Impl() )
+ {
+ if ( IsInPlace() )
+ {
+ SetToolSpaceBorderPixel_Impl( SvBorder() );
+ }
+ else
+ {
+ // check for IPClient that contains UIactive object or object that is currently UI activating
+ SfxWorkWindow *pWork = GetWorkWindow_Impl();
+ SfxInPlaceClient* pClient = GetCurrentViewFrame()->GetViewShell() ? GetCurrentViewFrame()->GetViewShell()->GetUIActiveIPClient_Impl() : nullptr;
+ if ( pClient )
+ {
+ SfxObjectShell* pDoc
+ = SfxObjectShell::GetShellFromComponent(pClient->GetObject()->getComponent());
+ SfxViewFrame* pFrame = SfxViewFrame::GetFirst(pDoc);
+ pWork = pFrame ? pFrame->GetFrame().GetWorkWindow_Impl() : nullptr;
+ }
+
+ if ( pWork )
+ {
+ pWork->ArrangeChildren_Impl();
+ pWork->ShowChildren_Impl();
+ }
+
+ // problem in presence of UIActive object: when the window is resized, but the toolspace border
+ // remains the same, setting the toolspace border at the ContainerEnvironment doesn't force a
+ // resize on the IPEnvironment; without that no resize is called for the SfxViewFrame. So always
+ // set the window size of the SfxViewFrame explicit.
+ SetToolSpaceBorderPixel_Impl( m_pImpl->aBorder );
+ }
+ }
+ else if ( m_pImpl->pCurrentViewFrame )
+ {
+ m_pImpl->pCurrentViewFrame->GetWindow().SetSizePixel( GetWindow().GetOutputSizePixel() );
+ }
+
+}
+
+SfxFrame* SfxFrame::GetFirst()
+{
+ return gaFramesArr_Impl.empty() ? nullptr : gaFramesArr_Impl.front();
+}
+
+SfxFrame* SfxFrame::GetNext( SfxFrame& rFrame )
+{
+ auto it = std::find( gaFramesArr_Impl.begin(), gaFramesArr_Impl.end(), &rFrame );
+ if ( it != gaFramesArr_Impl.end() && (++it) != gaFramesArr_Impl.end() )
+ return *it;
+ else
+ return nullptr;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/view/frame2.cxx b/sfx2/source/view/frame2.cxx
new file mode 100644
index 0000000000..1ae9ff4ad1
--- /dev/null
+++ b/sfx2/source/view/frame2.cxx
@@ -0,0 +1,406 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include "impframe.hxx"
+#include <workwin.hxx>
+
+#include <sfx2/bindings.hxx>
+#include <sfx2/dispatch.hxx>
+#include <sfx2/docfile.hxx>
+#include <sfx2/sfxsids.hrc>
+#include <sfx2/sfxuno.hxx>
+#include <sfx2/viewsh.hxx>
+
+#include <com/sun/star/awt/XWindow2.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/frame/Desktop.hpp>
+#include <com/sun/star/frame/XComponentLoader.hpp>
+#include <com/sun/star/frame/Frame.hpp>
+#include <com/sun/star/frame/XLayoutManager.hpp>
+
+#include <comphelper/namedvaluecollection.hxx>
+#include <comphelper/processfactory.hxx>
+#include <toolkit/helper/vclunohelper.hxx>
+#include <comphelper/diagnose_ex.hxx>
+#include <vcl/event.hxx>
+#include <vcl/syswin.hxx>
+#include <sal/log.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::frame;
+using namespace ::com::sun::star::util;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::beans;
+using ::com::sun::star::frame::XComponentLoader;
+
+class SfxFrameWindow_Impl : public vcl::Window
+{
+ DECL_LINK(ModalHierarchyHdl, bool, void);
+public:
+ SfxFrame* m_pFrame;
+
+ SfxFrameWindow_Impl( SfxFrame* pF, vcl::Window& i_rContainerWindow );
+
+ virtual void DataChanged( const DataChangedEvent& rDCEvt ) override;
+ virtual void StateChanged( StateChangedType nStateChange ) override;
+ virtual bool PreNotify( NotifyEvent& rNEvt ) override;
+ virtual bool EventNotify( NotifyEvent& rEvt ) override;
+ virtual void Resize() override;
+ virtual void GetFocus() override;
+ virtual void dispose() override;
+ void DoResize();
+};
+
+SfxFrameWindow_Impl::SfxFrameWindow_Impl(SfxFrame* pF, vcl::Window& i_rContainerWindow)
+ : Window(&i_rContainerWindow, WB_BORDER | WB_CLIPCHILDREN | WB_NODIALOGCONTROL | WB_3DLOOK)
+ , m_pFrame(pF)
+{
+ i_rContainerWindow.SetModalHierarchyHdl(LINK(this, SfxFrameWindow_Impl, ModalHierarchyHdl));
+}
+
+void SfxFrameWindow_Impl::dispose()
+{
+ GetParent()->SetModalHierarchyHdl(Link<bool, void>());
+ vcl::Window::dispose();
+}
+
+void SfxFrameWindow_Impl::DataChanged( const DataChangedEvent& rDCEvt )
+{
+ Window::DataChanged( rDCEvt );
+ // tdf#131613 the printers changing has no effect on window layout
+ if (rDCEvt.GetType() == DataChangedEventType::PRINTER)
+ return;
+ SfxWorkWindow *pWorkWin = m_pFrame->GetWorkWindow_Impl();
+ if ( pWorkWin )
+ pWorkWin->DataChanged_Impl();
+}
+
+bool SfxFrameWindow_Impl::EventNotify( NotifyEvent& rNEvt )
+{
+ if ( m_pFrame->IsClosing_Impl() || !m_pFrame->GetFrameInterface().is() )
+ return false;
+
+ SfxViewFrame* pView = m_pFrame->GetCurrentViewFrame();
+ if ( !pView || !pView->GetObjectShell() )
+ return Window::EventNotify( rNEvt );
+
+ if ( rNEvt.GetType() == NotifyEventType::GETFOCUS )
+ {
+ if ( pView->GetViewShell() && !pView->GetViewShell()->GetUIActiveIPClient_Impl() && !m_pFrame->IsInPlace() )
+ {
+ SAL_INFO("sfx", "SfxFrame: GotFocus");
+ pView->MakeActive_Impl( false );
+ }
+
+ // if focus was on an external window, the clipboard content might have been changed
+ pView->GetBindings().Invalidate( SID_PASTE );
+ pView->GetBindings().Invalidate( SID_PASTE_SPECIAL );
+ return true;
+ }
+ else if( rNEvt.GetType() == NotifyEventType::KEYINPUT )
+ {
+ if ( pView->GetViewShell()->KeyInput( *rNEvt.GetKeyEvent() ) )
+ return true;
+ }
+
+ return Window::EventNotify( rNEvt );
+}
+
+IMPL_LINK(SfxFrameWindow_Impl, ModalHierarchyHdl, bool, bSetModal, void)
+{
+ SfxViewFrame* pView = m_pFrame->GetCurrentViewFrame();
+ if (!pView || !pView->GetObjectShell())
+ return;
+ pView->SetModalMode(bSetModal);
+}
+
+bool SfxFrameWindow_Impl::PreNotify( NotifyEvent& rNEvt )
+{
+ NotifyEventType nType = rNEvt.GetType();
+ if ( nType == NotifyEventType::KEYINPUT || nType == NotifyEventType::KEYUP )
+ {
+ SfxViewFrame* pView = m_pFrame->GetCurrentViewFrame();
+ SfxViewShell* pShell = pView ? pView->GetViewShell() : nullptr;
+ if ( pShell && pShell->HasKeyListeners_Impl() && pShell->HandleNotifyEvent_Impl( rNEvt ) )
+ return true;
+ }
+ else if ( nType == NotifyEventType::MOUSEBUTTONUP || nType == NotifyEventType::MOUSEBUTTONDOWN )
+ {
+ vcl::Window* pWindow = rNEvt.GetWindow();
+ SfxViewFrame* pView = m_pFrame->GetCurrentViewFrame();
+ SfxViewShell* pShell = pView ? pView->GetViewShell() : nullptr;
+ if ( pShell )
+ if ( pWindow == pShell->GetWindow() || pShell->GetWindow()->IsChild( pWindow ) )
+ if ( pShell->HasMouseClickListeners_Impl() && pShell->HandleNotifyEvent_Impl( rNEvt ) )
+ return true;
+ }
+
+ if ( nType == NotifyEventType::MOUSEBUTTONDOWN )
+ {
+ vcl::Window* pWindow = rNEvt.GetWindow();
+ const MouseEvent* pMEvent = rNEvt.GetMouseEvent();
+ Point aPos = pWindow->OutputToScreenPixel( pMEvent->GetPosPixel() );
+ SfxWorkWindow *pWorkWin = m_pFrame->GetWorkWindow_Impl();
+ if ( pWorkWin )
+ pWorkWin->EndAutoShow_Impl( aPos );
+ }
+
+ return Window::PreNotify( rNEvt );
+}
+
+void SfxFrameWindow_Impl::GetFocus()
+{
+ if ( m_pFrame && !m_pFrame->IsClosing_Impl() &&
+ m_pFrame->GetCurrentViewFrame() &&
+ m_pFrame->GetFrameInterface().is() )
+ m_pFrame->GetCurrentViewFrame()->MakeActive_Impl( true );
+}
+
+void SfxFrameWindow_Impl::Resize()
+{
+ if ( IsReallyVisible() || IsReallyShown() || GetOutputSizePixel().Width() )
+ DoResize();
+}
+
+void SfxFrameWindow_Impl::StateChanged( StateChangedType nStateChange )
+{
+ if ( nStateChange == StateChangedType::InitShow )
+ {
+ m_pFrame->m_pImpl->bHidden = false;
+ if ( m_pFrame->IsInPlace() )
+ // TODO/MBA: workaround for bug in LayoutManager: the final resize does not get through because the
+ // LayoutManager works asynchronously and between resize and time execution the DockingAcceptor was exchanged so that
+ // the resize event never is sent to the component
+ SetSizePixel( GetParent()->GetOutputSizePixel() );
+
+ DoResize();
+ SfxViewFrame* pView = m_pFrame->GetCurrentViewFrame();
+ if ( pView )
+ pView->GetBindings().GetWorkWindow_Impl()->ShowChildren_Impl();
+ }
+
+ Window::StateChanged( nStateChange );
+}
+
+void SfxFrameWindow_Impl::DoResize()
+{
+ if ( !m_pFrame->m_pImpl->bLockResize )
+ m_pFrame->Resize();
+}
+
+Reference < XFrame > SfxFrame::CreateBlankFrame()
+{
+ Reference < XFrame > xFrame;
+ try
+ {
+ Reference < XDesktop2 > xDesktop = Desktop::create( ::comphelper::getProcessComponentContext() );
+ xFrame.set( xDesktop->findFrame( "_blank", 0 ), UNO_SET_THROW );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("sfx.view");
+ }
+ return xFrame;
+}
+
+SfxFrame* SfxFrame::CreateHidden( SfxObjectShell const & rDoc, vcl::Window& rWindow, SfxInterfaceId nViewId )
+{
+ SfxFrame* pFrame = nullptr;
+ try
+ {
+ // create and initialize new top level frame for this window
+ Reference < XComponentContext > xContext( ::comphelper::getProcessComponentContext() );
+ Reference < XDesktop2 > xDesktop = Desktop::create( xContext );
+ Reference < XFrame2 > xFrame = Frame::create( xContext );
+
+ Reference< awt::XWindow2 > xWin( VCLUnoHelper::GetInterface ( &rWindow ), uno::UNO_QUERY_THROW );
+ xFrame->initialize( xWin );
+ xDesktop->getFrames()->append( xFrame );
+
+ if ( xWin->isActive() )
+ xFrame->activate();
+
+ // create load arguments
+ Sequence< PropertyValue > aLoadArgs;
+ TransformItems( SID_OPENDOC, rDoc.GetMedium()->GetItemSet(), aLoadArgs );
+
+ ::comphelper::NamedValueCollection aArgs( aLoadArgs );
+ aArgs.put( "Model", rDoc.GetModel() );
+ aArgs.put( "Hidden", true );
+ if ( nViewId != SFX_INTERFACE_NONE )
+ aArgs.put( "ViewId", static_cast<sal_uInt16>(nViewId) );
+
+ aLoadArgs = aArgs.getPropertyValues();
+
+ // load the doc into that frame
+ Reference< XComponentLoader > xLoader( xFrame, UNO_QUERY_THROW );
+ xLoader->loadComponentFromURL(
+ "private:object",
+ "_self",
+ 0,
+ aLoadArgs
+ );
+
+ for ( pFrame = SfxFrame::GetFirst();
+ pFrame;
+ pFrame = SfxFrame::GetNext( *pFrame )
+ )
+ {
+ if ( pFrame->GetFrameInterface() == xFrame )
+ break;
+ }
+
+ OSL_ENSURE( pFrame, "SfxFrame::Create: load succeeded, but no SfxFrame was created during this!" );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("sfx.view");
+ }
+
+ return pFrame;
+}
+
+SfxFrame* SfxFrame::Create( const Reference < XFrame >& i_rFrame )
+{
+ // create a new TopFrame to an external XFrame object ( wrap controller )
+ ENSURE_OR_THROW( i_rFrame.is(), "NULL frame not allowed" );
+ VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow( i_rFrame->getContainerWindow() );
+ ENSURE_OR_THROW( pWindow, "frame without container window not allowed" );
+
+ SfxFrame* pFrame = new SfxFrame( *pWindow );
+ pFrame->SetFrameInterface_Impl( i_rFrame );
+ return pFrame;
+}
+
+SfxFrame::SfxFrame( vcl::Window& i_rContainerWindow )
+ :SvCompatWeakBase<SfxFrame>( this )
+ ,m_pWindow( nullptr )
+{
+ Construct_Impl();
+
+ m_pImpl->bHidden = false;
+ InsertTopFrame_Impl( this );
+ m_pImpl->pExternalContainerWindow = &i_rContainerWindow;
+
+ m_pWindow = VclPtr<SfxFrameWindow_Impl>::Create( this, i_rContainerWindow );
+
+ // always show pWindow, which is the ComponentWindow of the XFrame we live in
+ // nowadays, since SfxFrames can be created with an XFrame only, hiding or showing the complete XFrame
+ // is not done at level of the container window, not at SFX level. Thus, the component window can
+ // always be visible.
+ m_pWindow->Show();
+}
+
+void SfxFrame::SetPresentationMode( bool bSet )
+{
+ if ( GetCurrentViewFrame() )
+ GetCurrentViewFrame()->GetWindow().SetBorderStyle( bSet ? WindowBorderStyle::NOBORDER : WindowBorderStyle::NORMAL );
+
+ Reference< css::beans::XPropertySet > xPropSet( GetFrameInterface(), UNO_QUERY );
+ Reference< css::frame::XLayoutManager > xLayoutManager;
+
+ if ( xPropSet.is() )
+ {
+ Any aValue = xPropSet->getPropertyValue("LayoutManager");
+ aValue >>= xLayoutManager;
+ }
+
+ if ( xLayoutManager.is() )
+ xLayoutManager->setVisible( !bSet ); // we don't want to have ui in presentation mode
+
+ SetMenuBarOn_Impl( !bSet );
+ if ( GetWorkWindow_Impl() )
+ GetWorkWindow_Impl()->SetDockingAllowed( !bSet );
+ if ( GetCurrentViewFrame() )
+ GetCurrentViewFrame()->GetDispatcher()->Update_Impl( true );
+}
+
+SystemWindow* SfxFrame::GetSystemWindow() const
+{
+ return GetTopWindow_Impl();
+}
+
+SystemWindow* SfxFrame::GetTopWindow_Impl() const
+{
+ if ( m_pImpl->pExternalContainerWindow->IsSystemWindow() )
+ return static_cast<SystemWindow*>( m_pImpl->pExternalContainerWindow.get() );
+ else
+ return nullptr;
+}
+
+
+void SfxFrame::LockResize_Impl( bool bLock )
+{
+ m_pImpl->bLockResize = bLock;
+}
+
+void SfxFrame::SetMenuBarOn_Impl( bool bOn )
+{
+ m_pImpl->bMenuBarOn = bOn;
+
+ Reference< css::beans::XPropertySet > xPropSet( GetFrameInterface(), UNO_QUERY );
+ Reference< css::frame::XLayoutManager > xLayoutManager;
+
+ if ( xPropSet.is() )
+ {
+ Any aValue = xPropSet->getPropertyValue("LayoutManager");
+ aValue >>= xLayoutManager;
+ }
+
+ if ( xLayoutManager.is() )
+ {
+ OUString aMenuBarURL( "private:resource/menubar/menubar" );
+
+ if ( bOn )
+ xLayoutManager->showElement( aMenuBarURL );
+ else
+ xLayoutManager->hideElement( aMenuBarURL );
+ }
+}
+
+bool SfxFrame::IsMenuBarOn_Impl() const
+{
+ return m_pImpl->bMenuBarOn;
+}
+
+void SfxFrame::PrepareForDoc_Impl( const SfxObjectShell& i_rDoc )
+{
+ const ::comphelper::NamedValueCollection aDocumentArgs( i_rDoc.GetModel()->getArgs2( { "Hidden", "PluginMode" } ) );
+
+ // hidden?
+ OSL_ENSURE( !m_pImpl->bHidden, "when does this happen?" );
+ m_pImpl->bHidden = aDocumentArgs.getOrDefault( "Hidden", m_pImpl->bHidden );
+
+ // update our descriptor
+ UpdateDescriptor( &i_rDoc );
+
+ // plugin mode
+ sal_Int16 nPluginMode = aDocumentArgs.getOrDefault( "PluginMode", sal_Int16( 0 ) );
+ if ( nPluginMode && ( nPluginMode != 2 ) )
+ m_pImpl->bInPlace = true;
+}
+
+bool SfxFrame::IsMarkedHidden_Impl() const
+{
+ return m_pImpl->bHidden;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/view/frmload.cxx b/sfx2/source/view/frmload.cxx
new file mode 100644
index 0000000000..7f58a397cc
--- /dev/null
+++ b/sfx2/source/view/frmload.cxx
@@ -0,0 +1,826 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <sfx2/app.hxx>
+#include <sfx2/bindings.hxx>
+#include <sfx2/docfac.hxx>
+#include <sfx2/docfile.hxx>
+#include <sfx2/docfilt.hxx>
+#include <sfx2/doctempl.hxx>
+#include <sfx2/fcontnr.hxx>
+#include <sfx2/frame.hxx>
+#include <sfx2/objsh.hxx>
+#include <sfx2/request.hxx>
+#include <sfx2/sfxsids.hrc>
+#include <sfx2/viewfac.hxx>
+
+#include <com/sun/star/container/XContainerQuery.hpp>
+#include <com/sun/star/document/XTypeDetection.hpp>
+#include <com/sun/star/frame/XFrame.hpp>
+#include <com/sun/star/frame/XLoadable.hpp>
+#include <com/sun/star/task/XInteractionHandler.hpp>
+#include <com/sun/star/task/XInteractionHandler2.hpp>
+#include <com/sun/star/document/XViewDataSupplier.hpp>
+#include <com/sun/star/container/XIndexAccess.hpp>
+#include <com/sun/star/frame/XSynchronousFrameLoader.hpp>
+#include <com/sun/star/frame/XController2.hpp>
+#include <com/sun/star/frame/XModel2.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/lang/XInitialization.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/util/XCloseable.hpp>
+
+#include <comphelper/interaction.hxx>
+#include <comphelper/namedvaluecollection.hxx>
+#include <cppuhelper/exc_hlp.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <framework/interaction.hxx>
+#include <rtl/ref.hxx>
+#include <sal/log.hxx>
+#include <svl/eitem.hxx>
+#include <svl/stritem.hxx>
+#include <unotools/fcm.hxx>
+#include <unotools/moduleoptions.hxx>
+#include <comphelper/diagnose_ex.hxx>
+#include <tools/stream.hxx>
+#include <tools/urlobj.hxx>
+#include <vcl/svapp.hxx>
+#include <o3tl/string_view.hxx>
+
+using namespace com::sun::star;
+using ::com::sun::star::beans::PropertyValue;
+using ::com::sun::star::container::XContainerQuery;
+using ::com::sun::star::container::XEnumeration;
+using ::com::sun::star::document::XTypeDetection;
+using ::com::sun::star::frame::XFrame;
+using ::com::sun::star::frame::XLoadable;
+using ::com::sun::star::task::XInteractionHandler;
+using ::com::sun::star::task::XInteractionHandler2;
+using ::com::sun::star::uno::Any;
+using ::com::sun::star::uno::Exception;
+using ::com::sun::star::uno::Reference;
+using ::com::sun::star::uno::RuntimeException;
+using ::com::sun::star::uno::Sequence;
+using ::com::sun::star::uno::UNO_QUERY;
+using ::com::sun::star::uno::UNO_QUERY_THROW;
+using ::com::sun::star::uno::UNO_SET_THROW;
+using ::com::sun::star::util::XCloseable;
+using ::com::sun::star::document::XViewDataSupplier;
+using ::com::sun::star::container::XIndexAccess;
+using ::com::sun::star::frame::XController2;
+using ::com::sun::star::frame::XModel2;
+
+namespace {
+
+class SfxFrameLoader_Impl : public ::cppu::WeakImplHelper< css::frame::XSynchronousFrameLoader, css::lang::XServiceInfo >
+{
+ css::uno::Reference < css::uno::XComponentContext > m_aContext;
+
+public:
+ explicit SfxFrameLoader_Impl( const css::uno::Reference < css::uno::XComponentContext >& _rxContext );
+
+ virtual OUString SAL_CALL getImplementationName() override;
+
+ virtual sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override;
+
+ virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
+
+
+ // XSynchronousFrameLoader
+
+ virtual sal_Bool SAL_CALL load( const css::uno::Sequence< css::beans::PropertyValue >& _rArgs, const css::uno::Reference< css::frame::XFrame >& _rxFrame ) override;
+ virtual void SAL_CALL cancel() override;
+
+protected:
+ virtual ~SfxFrameLoader_Impl() override;
+
+private:
+ std::shared_ptr<const SfxFilter> impl_getFilterFromServiceName_nothrow(
+ const OUString& i_rServiceName
+ ) const;
+
+ static OUString impl_askForFilter_nothrow(
+ const css::uno::Reference< css::task::XInteractionHandler >& i_rxHandler,
+ const OUString& i_rDocumentURL
+ );
+
+ std::shared_ptr<const SfxFilter> impl_detectFilterForURL(
+ const OUString& _rURL,
+ const ::comphelper::NamedValueCollection& i_rDescriptor,
+ const SfxFilterMatcher& rMatcher
+ ) const;
+
+ static bool impl_createNewDocWithSlotParam(
+ const sal_uInt16 _nSlotID,
+ const css::uno::Reference< css::frame::XFrame >& i_rxFrame,
+ const bool i_bHidden
+ );
+
+ void impl_determineFilter(
+ ::comphelper::NamedValueCollection& io_rDescriptor
+ ) const;
+
+ bool impl_determineTemplateDocument(
+ ::comphelper::NamedValueCollection& io_rDescriptor
+ ) const;
+
+ static sal_uInt16 impl_findSlotParam(
+ std::u16string_view i_rFactoryURL
+ );
+
+ static SfxObjectShellRef impl_findObjectShell(
+ const css::uno::Reference< css::frame::XModel2 >& i_rxDocument
+ );
+
+ static void impl_handleCaughtError_nothrow(
+ const css::uno::Any& i_rCaughtError,
+ const ::comphelper::NamedValueCollection& i_rDescriptor
+ );
+
+ static void impl_removeLoaderArguments(
+ ::comphelper::NamedValueCollection& io_rDescriptor
+ );
+
+ static SfxInterfaceId impl_determineEffectiveViewId_nothrow(
+ const SfxObjectShell& i_rDocument,
+ const ::comphelper::NamedValueCollection& i_rDescriptor
+ );
+
+ static ::comphelper::NamedValueCollection
+ impl_extractViewCreationArgs(
+ ::comphelper::NamedValueCollection& io_rDescriptor
+ );
+
+ static css::uno::Reference< css::frame::XController2 >
+ impl_createDocumentView(
+ const css::uno::Reference< css::frame::XModel2 >& i_rModel,
+ const css::uno::Reference< css::frame::XFrame >& i_rFrame,
+ const ::comphelper::NamedValueCollection& i_rViewFactoryArgs,
+ const OUString& i_rViewName
+ );
+};
+
+SfxFrameLoader_Impl::SfxFrameLoader_Impl( const Reference< css::uno::XComponentContext >& _rxContext )
+ :m_aContext( _rxContext )
+{
+}
+
+SfxFrameLoader_Impl::~SfxFrameLoader_Impl()
+{
+}
+
+
+std::shared_ptr<const SfxFilter> SfxFrameLoader_Impl::impl_detectFilterForURL( const OUString& sURL,
+ const ::comphelper::NamedValueCollection& i_rDescriptor, const SfxFilterMatcher& rMatcher ) const
+{
+ OUString sFilter;
+ try
+ {
+ if ( sURL.isEmpty() )
+ return nullptr;
+
+ Reference< XTypeDetection > xDetect(
+ m_aContext->getServiceManager()->createInstanceWithContext("com.sun.star.document.TypeDetection", m_aContext),
+ UNO_QUERY_THROW);
+
+ ::comphelper::NamedValueCollection aNewArgs;
+ aNewArgs.put( "URL", sURL );
+
+ if ( i_rDescriptor.has( "InteractionHandler" ) )
+ aNewArgs.put( "InteractionHandler", i_rDescriptor.get( "InteractionHandler" ) );
+ if ( i_rDescriptor.has( "StatusIndicator" ) )
+ aNewArgs.put( "StatusIndicator", i_rDescriptor.get( "StatusIndicator" ) );
+
+ Sequence< PropertyValue > aQueryArgs( aNewArgs.getPropertyValues() );
+ OUString sType = xDetect->queryTypeByDescriptor( aQueryArgs, true );
+ if ( !sType.isEmpty() )
+ {
+ std::shared_ptr<const SfxFilter> pFilter = rMatcher.GetFilter4EA( sType );
+ if ( pFilter )
+ sFilter = pFilter->GetName();
+ }
+ }
+ catch ( const RuntimeException& )
+ {
+ throw;
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("sfx.view");
+ sFilter.clear();
+ }
+
+ std::shared_ptr<const SfxFilter> pFilter;
+ if (!sFilter.isEmpty())
+ pFilter = rMatcher.GetFilter4FilterName(sFilter);
+ return pFilter;
+}
+
+
+std::shared_ptr<const SfxFilter> SfxFrameLoader_Impl::impl_getFilterFromServiceName_nothrow( const OUString& i_rServiceName ) const
+{
+ try
+ {
+ ::comphelper::NamedValueCollection aQuery;
+ aQuery.put( "DocumentService", i_rServiceName );
+
+ const Reference< XContainerQuery > xQuery(
+ m_aContext->getServiceManager()->createInstanceWithContext("com.sun.star.document.FilterFactory", m_aContext),
+ UNO_QUERY_THROW );
+
+ const SfxFilterMatcher& rMatcher = SfxGetpApp()->GetFilterMatcher();
+ const SfxFilterFlags nMust = SfxFilterFlags::IMPORT;
+ const SfxFilterFlags nDont = SFX_FILTER_NOTINSTALLED;
+
+ Reference < XEnumeration > xEnum( xQuery->createSubSetEnumerationByProperties(
+ aQuery.getNamedValues() ), UNO_SET_THROW );
+ while ( xEnum->hasMoreElements() )
+ {
+ ::comphelper::NamedValueCollection aType( xEnum->nextElement() );
+ OUString sFilterName = aType.getOrDefault( "Name", OUString() );
+ if ( sFilterName.isEmpty() )
+ continue;
+
+ std::shared_ptr<const SfxFilter> pFilter = rMatcher.GetFilter4FilterName( sFilterName );
+ if ( !pFilter )
+ continue;
+
+ SfxFilterFlags nFlags = pFilter->GetFilterFlags();
+ if ( ( ( nFlags & nMust ) == nMust )
+ && ( ( nFlags & nDont ) == SfxFilterFlags::NONE )
+ )
+ {
+ return pFilter;
+ }
+ }
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("sfx.view");
+ }
+ return nullptr;
+}
+
+
+OUString SfxFrameLoader_Impl::impl_askForFilter_nothrow( const Reference< XInteractionHandler >& i_rxHandler,
+ const OUString& i_rDocumentURL )
+{
+ ENSURE_OR_THROW( i_rxHandler.is(), "invalid interaction handler" );
+
+ OUString sFilterName;
+ try
+ {
+ ::framework::RequestFilterSelect aRequest( i_rDocumentURL );
+ i_rxHandler->handle( aRequest.GetRequest() );
+ if( !aRequest.isAbort() )
+ sFilterName = aRequest.getFilter();
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("sfx.view");
+ }
+
+ return sFilterName;
+}
+
+bool lcl_getDispatchResult(const SfxPoolItemHolder& rResult)
+{
+ if (nullptr == rResult.getItem())
+ return false;
+
+ // default must be set to true, because some return values
+ // can't be checked, but nonetheless indicate "success"!
+ bool bSuccess = true;
+
+ // On the other side some special slots return a boolean state,
+ // which can be set to FALSE.
+ const SfxBoolItem* pItem(dynamic_cast<const SfxBoolItem*>(rResult.getItem()));
+ if ( pItem )
+ bSuccess = pItem->GetValue();
+
+ return bSuccess;
+}
+
+bool SfxFrameLoader_Impl::impl_createNewDocWithSlotParam( const sal_uInt16 _nSlotID, const Reference< XFrame >& i_rxFrame,
+ const bool i_bHidden )
+{
+ SfxRequest aRequest( _nSlotID, SfxCallMode::SYNCHRON, SfxGetpApp()->GetPool() );
+ aRequest.AppendItem( SfxUnoFrameItem( SID_FILLFRAME, i_rxFrame ) );
+ if ( i_bHidden )
+ aRequest.AppendItem( SfxBoolItem( SID_HIDDEN, true ) );
+ return lcl_getDispatchResult(SfxGetpApp()->ExecuteSlot(aRequest));
+}
+
+
+void SfxFrameLoader_Impl::impl_determineFilter( ::comphelper::NamedValueCollection& io_rDescriptor ) const
+{
+ const OUString sURL = io_rDescriptor.getOrDefault( "URL", OUString() );
+ const OUString sTypeName = io_rDescriptor.getOrDefault( "TypeName", OUString() );
+ const OUString sFilterName = io_rDescriptor.getOrDefault( "FilterName", OUString() );
+ const OUString sServiceName = io_rDescriptor.getOrDefault( "DocumentService", OUString() );
+ const Reference< XInteractionHandler >
+ xInteraction = io_rDescriptor.getOrDefault( "InteractionHandler", Reference< XInteractionHandler >() );
+
+ const SfxFilterMatcher& rMatcher = SfxGetpApp()->GetFilterMatcher();
+ std::shared_ptr<const SfxFilter> pFilter;
+
+ // get filter by its name directly ...
+ if ( !sFilterName.isEmpty() )
+ pFilter = rMatcher.GetFilter4FilterName( sFilterName );
+
+ // or search the preferred filter for the detected type ...
+ if ( !pFilter && !sTypeName.isEmpty() )
+ pFilter = rMatcher.GetFilter4EA( sTypeName );
+
+ // or use given document service for detection, too
+ if ( !pFilter && !sServiceName.isEmpty() )
+ pFilter = impl_getFilterFromServiceName_nothrow( sServiceName );
+
+ // or use interaction to ask user for right filter.
+ if ( !pFilter && xInteraction.is() && !sURL.isEmpty() )
+ {
+ OUString sSelectedFilter = impl_askForFilter_nothrow( xInteraction, sURL );
+ if ( !sSelectedFilter.isEmpty() )
+ pFilter = rMatcher.GetFilter4FilterName( sSelectedFilter );
+ }
+
+ if ( !pFilter )
+ return;
+
+ io_rDescriptor.put( "FilterName", pFilter->GetFilterName() );
+
+ // If detected filter indicates using of an own template format
+ // add property "AsTemplate" to descriptor. But suppress this step
+ // if such property already exists.
+ if ( pFilter->IsOwnTemplateFormat() && !io_rDescriptor.has( "AsTemplate" ) )
+ io_rDescriptor.put( "AsTemplate", true );
+
+ // The DocumentService property will finally be used to determine the document type to create, so
+ // override it with the service name as indicated by the found filter.
+ io_rDescriptor.put( "DocumentService", pFilter->GetServiceName() );
+}
+
+
+SfxObjectShellRef SfxFrameLoader_Impl::impl_findObjectShell( const Reference< XModel2 >& i_rxDocument )
+{
+ for ( SfxObjectShell* pDoc = SfxObjectShell::GetFirst( nullptr, false ); pDoc;
+ pDoc = SfxObjectShell::GetNext( *pDoc, nullptr, false ) )
+ {
+ if ( i_rxDocument == pDoc->GetModel() )
+ {
+ return pDoc;
+ }
+ }
+
+ SAL_WARN( "sfx.view", "SfxFrameLoader_Impl::impl_findObjectShell: model is not based on SfxObjectShell - wrong frame loader usage!" );
+ return nullptr;
+}
+
+
+bool SfxFrameLoader_Impl::impl_determineTemplateDocument( ::comphelper::NamedValueCollection& io_rDescriptor ) const
+{
+ try
+ {
+ const OUString sTemplateRegioName = io_rDescriptor.getOrDefault( "TemplateRegionName", OUString() );
+ const OUString sTemplateName = io_rDescriptor.getOrDefault( "TemplateName", OUString() );
+ const OUString sServiceName = io_rDescriptor.getOrDefault( "DocumentService", OUString() );
+ const OUString sURL = io_rDescriptor.getOrDefault( "URL", OUString() );
+
+ // determine the full URL of the template to use, if any
+ OUString sTemplateURL;
+ if ( !sTemplateRegioName.isEmpty() && !sTemplateName.isEmpty() )
+ {
+ SfxDocumentTemplates aTmpFac;
+ aTmpFac.GetFull( sTemplateRegioName, sTemplateName, sTemplateURL );
+ }
+ else
+ {
+ if ( !sServiceName.isEmpty() )
+ sTemplateURL = SfxObjectFactory::GetStandardTemplate( sServiceName );
+ else
+ sTemplateURL = SfxObjectFactory::GetStandardTemplate( SfxObjectShell::GetServiceNameFromFactory( sURL ) );
+ }
+
+ if ( !sTemplateURL.isEmpty() )
+ {
+ // detect the filter for the template. Might still be NULL (if the template is broken, or does not
+ // exist, or some such), but this is handled by our caller the same way as if no template/URL was present.
+ std::shared_ptr<const SfxFilter> pTemplateFilter = impl_detectFilterForURL( sTemplateURL, io_rDescriptor, SfxGetpApp()->GetFilterMatcher() );
+ if ( pTemplateFilter )
+ {
+ // load the template document, but, well, "as template"
+ io_rDescriptor.put( "FilterName", pTemplateFilter->GetName() );
+ io_rDescriptor.put( "FileName", sTemplateURL );
+ io_rDescriptor.put( "AsTemplate", true );
+
+ // #i21583#
+ // the DocumentService property will finally be used to create the document. Thus, override any possibly
+ // present value with the document service of the template.
+ io_rDescriptor.put( "DocumentService", pTemplateFilter->GetServiceName() );
+ return true;
+ }
+ }
+ }
+ catch (...)
+ {
+ }
+ return false;
+}
+
+
+sal_uInt16 SfxFrameLoader_Impl::impl_findSlotParam( std::u16string_view i_rFactoryURL )
+{
+ std::u16string_view sSlotParam;
+ const size_t nParamPos = i_rFactoryURL.find( '?' );
+ if ( nParamPos != std::u16string_view::npos )
+ {
+ // currently only the "slot" parameter is supported
+ const size_t nSlotPos = i_rFactoryURL.find( u"slot=", nParamPos );
+ if ( nSlotPos > 0 && nSlotPos != std::u16string_view::npos )
+ sSlotParam = i_rFactoryURL.substr( nSlotPos + 5 );
+ }
+
+ if ( !sSlotParam.empty() )
+ return sal_uInt16( o3tl::toInt32(sSlotParam) );
+
+ return 0;
+}
+
+
+void SfxFrameLoader_Impl::impl_handleCaughtError_nothrow( const Any& i_rCaughtError, const ::comphelper::NamedValueCollection& i_rDescriptor )
+{
+ try
+ {
+ const Reference< XInteractionHandler > xInteraction =
+ i_rDescriptor.getOrDefault( "InteractionHandler", Reference< XInteractionHandler >() );
+ if ( !xInteraction.is() )
+ return;
+ ::rtl::Reference< ::comphelper::OInteractionRequest > pRequest( new ::comphelper::OInteractionRequest( i_rCaughtError ) );
+ ::rtl::Reference< ::comphelper::OInteractionApprove > pApprove( new ::comphelper::OInteractionApprove );
+ pRequest->addContinuation( pApprove );
+
+ const Reference< XInteractionHandler2 > xHandler( xInteraction, UNO_QUERY );
+ #if OSL_DEBUG_LEVEL > 0
+ const bool bHandled =
+ #endif
+ xHandler.is() && xHandler->handleInteractionRequest( pRequest );
+
+ #if OSL_DEBUG_LEVEL > 0
+ if ( !bHandled )
+ // the interaction handler couldn't deal with this error
+ // => report it as assertion, at least (done in the DBG_UNHANDLED_EXCEPTION below)
+ ::cppu::throwException( i_rCaughtError );
+ #endif
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("sfx.view");
+ }
+}
+
+
+void SfxFrameLoader_Impl::impl_removeLoaderArguments( ::comphelper::NamedValueCollection& io_rDescriptor )
+{
+ // remove the arguments which are for the loader only, and not for a call to attachResource
+ io_rDescriptor.remove( "StatusIndicator" );
+ io_rDescriptor.remove( "Model" );
+}
+
+
+::comphelper::NamedValueCollection SfxFrameLoader_Impl::impl_extractViewCreationArgs( ::comphelper::NamedValueCollection& io_rDescriptor )
+{
+ static const std::u16string_view sKnownViewArgs[] = { u"JumpMark", u"PickListEntry" };
+
+ ::comphelper::NamedValueCollection aViewArgs;
+ for (const auto& rKnownViewArg : sKnownViewArgs)
+ {
+ const OUString sKnownViewArg(rKnownViewArg);
+ if ( io_rDescriptor.has( sKnownViewArg ) )
+ {
+ aViewArgs.put( sKnownViewArg, io_rDescriptor.get( sKnownViewArg ) );
+ io_rDescriptor.remove( sKnownViewArg );
+ }
+ }
+ return aViewArgs;
+}
+
+
+SfxInterfaceId SfxFrameLoader_Impl::impl_determineEffectiveViewId_nothrow( const SfxObjectShell& i_rDocument, const ::comphelper::NamedValueCollection& i_rDescriptor )
+{
+ SfxInterfaceId nViewId(i_rDescriptor.getOrDefault( "ViewId", sal_Int16( 0 ) ));
+ try
+ {
+ if ( nViewId == SFX_INTERFACE_NONE )
+ do
+ {
+ Reference< XViewDataSupplier > xViewDataSupplier( i_rDocument.GetModel(), UNO_QUERY );
+ Reference< XIndexAccess > xViewData;
+ if ( xViewDataSupplier.is() )
+ xViewData.set( xViewDataSupplier->getViewData() );
+
+ if ( !xViewData.is() || ( xViewData->getCount() == 0 ) )
+ // no view data stored together with the model
+ break;
+
+ // obtain the ViewID from the view data
+ Sequence< PropertyValue > aViewData;
+ if ( !( xViewData->getByIndex( 0 ) >>= aViewData ) )
+ break;
+
+ OUString sViewId = ::comphelper::NamedValueCollection::getOrDefault( aViewData, u"ViewId", OUString() );
+ if ( sViewId.isEmpty() )
+ break;
+
+ // somewhat weird convention here ... in the view data, the ViewId is a string, effectively describing
+ // a view name. In the document load descriptor, the ViewId is in fact the numeric ID.
+
+ SfxViewFactory* pViewFactory = i_rDocument.GetFactory().GetViewFactoryByViewName( sViewId );
+ if ( pViewFactory )
+ nViewId = pViewFactory->GetOrdinal();
+ }
+ while ( false );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("sfx.view");
+ }
+
+ if ( nViewId == SFX_INTERFACE_NONE )
+ nViewId = i_rDocument.GetFactory().GetViewFactory().GetOrdinal();
+ return nViewId;
+}
+
+
+Reference< XController2 > SfxFrameLoader_Impl::impl_createDocumentView( const Reference< XModel2 >& i_rModel,
+ const Reference< XFrame >& i_rFrame, const ::comphelper::NamedValueCollection& i_rViewFactoryArgs,
+ const OUString& i_rViewName )
+{
+ // let the model create a new controller
+ const Reference< XController2 > xController( i_rModel->createViewController(
+ i_rViewName,
+ i_rViewFactoryArgs.getPropertyValues(),
+ i_rFrame
+ ), UNO_SET_THROW );
+
+ // introduce model/view/controller to each other
+ utl::ConnectFrameControllerModel(i_rFrame, xController, i_rModel);
+
+ return xController;
+}
+
+std::shared_ptr<const SfxFilter> getEmptyURLFilter(std::u16string_view sURL)
+{
+ INetURLObject aParser(sURL);
+ const OUString aExt = aParser.getExtension(INetURLObject::LAST_SEGMENT, true,
+ INetURLObject::DecodeMechanism::WithCharset);
+ const SfxFilterMatcher& rMatcher = SfxGetpApp()->GetFilterMatcher();
+
+ // Requiring the export+preferred flags helps to find the relevant filter, e.g. .doc -> WW8 (and
+ // not WW6 or Mac_Word).
+ std::shared_ptr<const SfxFilter> pFilter = rMatcher.GetFilter4Extension(
+ aExt, SfxFilterFlags::IMPORT | SfxFilterFlags::EXPORT | SfxFilterFlags::PREFERED);
+ if (!pFilter)
+ {
+ // retry without PREFERED so we can find at least something for 0-byte *.ods
+ pFilter
+ = rMatcher.GetFilter4Extension(aExt, SfxFilterFlags::IMPORT | SfxFilterFlags::EXPORT);
+ }
+ return pFilter;
+}
+
+sal_Bool SAL_CALL SfxFrameLoader_Impl::load( const Sequence< PropertyValue >& rArgs,
+ const Reference< XFrame >& _rTargetFrame )
+{
+ ENSURE_OR_THROW( _rTargetFrame.is(), "illegal NULL frame" );
+
+ SAL_INFO( "sfx.view", "SfxFrameLoader::load" );
+
+ ::comphelper::NamedValueCollection aDescriptor( rArgs );
+
+ // ensure the descriptor contains a referrer
+ if ( !aDescriptor.has( "Referer" ) )
+ aDescriptor.put( "Referer", OUString() );
+
+ // did the caller already pass a model?
+ Reference< XModel2 > xModel = aDescriptor.getOrDefault( "Model", Reference< XModel2 >() );
+ const bool bExternalModel = xModel.is();
+
+ // check for factory URLs to create a new doc, instead of loading one
+ const OUString sURL = aDescriptor.getOrDefault( "URL", OUString() );
+ const bool bIsFactoryURL = sURL.startsWith( "private:factory/" );
+ std::shared_ptr<const SfxFilter> pEmptyURLFilter;
+ bool bInitNewModel = bIsFactoryURL;
+ const bool bIsDefault = bIsFactoryURL && !bExternalModel;
+ if (!aDescriptor.has("Replaceable"))
+ aDescriptor.put("Replaceable", bIsDefault);
+ if (bIsDefault)
+ {
+ const OUString sFactory = sURL.copy( sizeof( "private:factory/" ) -1 );
+ // special handling for some weird factory URLs a la private:factory/swriter?slot=21053
+ const sal_uInt16 nSlotParam = impl_findSlotParam( sFactory );
+ if ( nSlotParam != 0 )
+ {
+ return impl_createNewDocWithSlotParam( nSlotParam, _rTargetFrame, aDescriptor.getOrDefault( "Hidden", false ) );
+ }
+
+ const bool bDescribesValidTemplate = impl_determineTemplateDocument( aDescriptor );
+ if ( bDescribesValidTemplate )
+ {
+ // if the media descriptor allowed us to determine a template document to create the new document
+ // from, then do not init a new document model from scratch (below), but instead load the
+ // template document
+ bInitNewModel = false;
+ }
+ else
+ {
+ const OUString sServiceName = SfxObjectShell::GetServiceNameFromFactory( sFactory );
+ aDescriptor.put( "DocumentService", sServiceName );
+ }
+ }
+ else
+ {
+ // compatibility
+ aDescriptor.put( "FileName", aDescriptor.get( "URL" ) );
+
+ if (!bIsFactoryURL && !bExternalModel && tools::isEmptyFileUrl(sURL))
+ {
+ pEmptyURLFilter = getEmptyURLFilter(sURL);
+ if (pEmptyURLFilter)
+ {
+ aDescriptor.put("DocumentService", pEmptyURLFilter->GetServiceName());
+ if (impl_determineTemplateDocument(aDescriptor))
+ {
+ // if the media descriptor allowed us to determine a template document
+ // to create the new document from, then do not init a new document model
+ // from scratch (below), but instead load the template document
+ bInitNewModel = false;
+ // Do not try to load from empty UCB content
+ aDescriptor.remove("UCBContent");
+ }
+ else
+ {
+ bInitNewModel = true;
+ }
+ }
+ }
+ }
+
+ bool bLoadSuccess = false;
+ try
+ {
+ // extract view relevant arguments from the loader args
+ ::comphelper::NamedValueCollection aViewCreationArgs( impl_extractViewCreationArgs( aDescriptor ) );
+
+ // no model passed from outside? => create one from scratch
+ if ( !bExternalModel )
+ {
+ bool bInternalFilter = aDescriptor.getOrDefault<OUString>("FilterProvider", OUString()).isEmpty();
+
+ if (bInternalFilter && !bInitNewModel)
+ {
+ // Ensure that the current SfxFilter instance is loaded before
+ // going further. We don't need to do this for external
+ // filter providers.
+ impl_determineFilter(aDescriptor);
+ }
+
+ // create the new doc
+ const OUString sServiceName = aDescriptor.getOrDefault( "DocumentService", OUString() );
+ xModel.set( m_aContext->getServiceManager()->createInstanceWithContext(sServiceName, m_aContext), UNO_QUERY_THROW );
+
+ // load resp. init it
+ const Reference< XLoadable > xLoadable( xModel, UNO_QUERY_THROW );
+ if ( bInitNewModel )
+ {
+ xLoadable->initNew();
+
+ impl_removeLoaderArguments( aDescriptor );
+ xModel->attachResource( OUString(), aDescriptor.getPropertyValues() );
+ }
+ else
+ {
+ xLoadable->load( aDescriptor.getPropertyValues() );
+ }
+ }
+ else
+ {
+ // tell the doc its (current) load args.
+ impl_removeLoaderArguments( aDescriptor );
+ xModel->attachResource( xModel->getURL(), aDescriptor.getPropertyValues() );
+ }
+
+ SolarMutexGuard aGuard;
+
+ // get the SfxObjectShell (still needed at the moment)
+ // SfxObjectShellRef is used here ( instead of ...Lock ) since the model is closed below if necessary
+ // SfxObjectShellLock would be even dangerous here, since the lifetime control should be done outside in case of success
+ const SfxObjectShellRef xDoc = impl_findObjectShell( xModel );
+ ENSURE_OR_THROW( xDoc.is(), "no SfxObjectShell for the given model" );
+
+ if (pEmptyURLFilter)
+ {
+ // Detach the medium from the template, and set proper document name and filter
+ auto pMedium = xDoc->GetMedium();
+ auto& rItemSet = pMedium->GetItemSet();
+ rItemSet.ClearItem(SID_TEMPLATE);
+ rItemSet.Put(SfxStringItem(SID_FILTER_NAME, pEmptyURLFilter->GetFilterName()));
+ pMedium->SetName(sURL, true);
+ pMedium->SetFilter(pEmptyURLFilter);
+ pMedium->GetInitFileDate(true);
+ xDoc->SetLoading(SfxLoadedFlags::NONE);
+ xDoc->FinishedLoading();
+ }
+
+ // ensure the ID of the to-be-created view is in the descriptor, if possible
+ const SfxInterfaceId nViewId = impl_determineEffectiveViewId_nothrow( *xDoc, aDescriptor );
+ const sal_Int16 nViewNo = xDoc->GetFactory().GetViewNo_Impl( nViewId, 0 );
+ const OUString sViewName( xDoc->GetFactory().GetViewFactory( nViewNo ).GetAPIViewName() );
+
+ // plug the document into the frame
+ Reference<XController2> xController =
+ impl_createDocumentView( xModel, _rTargetFrame, aViewCreationArgs, sViewName );
+
+ Reference<lang::XInitialization> xInit(xController, UNO_QUERY);
+ if (xInit.is())
+ {
+ uno::Sequence<uno::Any> aArgs; // empty for now.
+ xInit->initialize(aArgs);
+ }
+
+ bLoadSuccess = true;
+ }
+ catch ( Exception& )
+ {
+ const Any aError( ::cppu::getCaughtException() );
+ if ( !aDescriptor.getOrDefault( "Silent", false ) )
+ impl_handleCaughtError_nothrow( aError, aDescriptor );
+ }
+
+ // if loading was not successful, close the document
+ if ( !bLoadSuccess && !bExternalModel )
+ {
+ try
+ {
+ const Reference< XCloseable > xCloseable( xModel, UNO_QUERY_THROW );
+ xCloseable->close( true );
+ }
+ catch ( Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("sfx.view");
+ }
+ }
+
+ return bLoadSuccess;
+}
+
+void SfxFrameLoader_Impl::cancel()
+{
+}
+
+/* XServiceInfo */
+OUString SAL_CALL SfxFrameLoader_Impl::getImplementationName()
+{
+ return "com.sun.star.comp.office.FrameLoader";
+}
+
+/* XServiceInfo */
+sal_Bool SAL_CALL SfxFrameLoader_Impl::supportsService( const OUString& sServiceName )
+{
+ return cppu::supportsService(this, sServiceName);
+}
+
+/* XServiceInfo */
+Sequence< OUString > SAL_CALL SfxFrameLoader_Impl::getSupportedServiceNames()
+{
+ return { "com.sun.star.frame.SynchronousFrameLoader", "com.sun.star.frame.OfficeFrameLoader" };
+}
+
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+com_sun_star_comp_office_FrameLoader_get_implementation(
+ css::uno::XComponentContext *context,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new SfxFrameLoader_Impl(context));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/view/impframe.hxx b/sfx2/source/view/impframe.hxx
new file mode 100644
index 0000000000..b98d9170fc
--- /dev/null
+++ b/sfx2/source/view/impframe.hxx
@@ -0,0 +1,71 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_SFX2_SOURCE_VIEW_IMPFRAME_HXX
+#define INCLUDED_SFX2_SOURCE_VIEW_IMPFRAME_HXX
+
+#include <sfx2/frame.hxx>
+#include <sfx2/viewfrm.hxx>
+
+#include <tools/svborder.hxx>
+#include <vcl/window.hxx>
+
+class SfxFrame_Impl : public SfxBroadcaster
+{
+public:
+ css::uno::Reference< css::frame::XFrame > xFrame;
+ bool mbHasTitle;
+ SfxViewFrame* pCurrentViewFrame;
+ SfxFrameDescriptor* pDescr;
+ bool bClosing : 1;
+ bool bPrepClosing : 1;
+ bool bInCancelTransfers : 1;
+ bool bOwnsBindings : 1;
+ bool bReleasingComponent : 1;
+ bool bInPlace : 1;
+ SfxWorkWindow* pWorkWin;
+ SvBorder aBorder;
+ // formerly SfxTopFrame
+ VclPtr<vcl::Window> pExternalContainerWindow;
+ bool bHidden;
+ bool bLockResize;
+ bool bMenuBarOn;
+
+ explicit SfxFrame_Impl()
+ :mbHasTitle( false )
+ ,pCurrentViewFrame( nullptr )
+ ,pDescr( nullptr )
+ ,bClosing(false)
+ ,bPrepClosing(false)
+ ,bInCancelTransfers( false )
+ ,bOwnsBindings( false )
+ ,bReleasingComponent( false )
+ ,bInPlace( false )
+ ,pWorkWin( nullptr )
+ ,pExternalContainerWindow( nullptr )
+ ,bHidden( false )
+ ,bLockResize( false )
+ ,bMenuBarOn( true )
+ {
+ }
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/view/impviewframe.hxx b/sfx2/source/view/impviewframe.hxx
new file mode 100644
index 0000000000..18675d48e2
--- /dev/null
+++ b/sfx2/source/view/impviewframe.hxx
@@ -0,0 +1,81 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_SFX2_SOURCE_VIEW_IMPVIEWFRAME_HXX
+#define INCLUDED_SFX2_SOURCE_VIEW_IMPVIEWFRAME_HXX
+
+#include <sfx2/viewfrm.hxx>
+
+#include <tools/svborder.hxx>
+#include <vcl/window.hxx>
+
+struct SfxViewFrame_Impl
+{
+ SvBorder aBorder;
+ Size aMargin;
+ Size aSize;
+ OUString aActualURL;
+ SfxFrame& rFrame;
+ VclPtr<vcl::Window> pWindow;
+ sal_uInt16 nDocViewNo;
+ SfxInterfaceId nCurViewId;
+ bool bResizeInToOut:1;
+ bool bObjLocked:1;
+ bool bReloading:1;
+ bool bIsDowning:1;
+ bool bModal:1;
+ bool bEnabled:1;
+ bool bWindowWasEnabled:1;
+ OUString aFactoryName;
+
+ explicit SfxViewFrame_Impl(SfxFrame& i_rFrame)
+ : rFrame(i_rFrame)
+ , pWindow(nullptr)
+ , nDocViewNo(0)
+ , nCurViewId(0)
+ , bResizeInToOut(false)
+ , bObjLocked(false)
+ , bReloading(false)
+ , bIsDowning(false)
+ , bModal(false)
+ , bEnabled(false)
+ , bWindowWasEnabled(true)
+ {
+ }
+};
+
+class SfxFrameViewWindow_Impl : public vcl::Window
+{
+ SfxViewFrame* pFrame;
+
+public:
+ SfxFrameViewWindow_Impl( SfxViewFrame* p, vcl::Window& rParent ) :
+ Window( &rParent, WB_CLIPCHILDREN ),
+ pFrame( p )
+ {
+ p->GetFrame().GetWindow().SetBorderStyle( WindowBorderStyle::NOBORDER );
+ }
+
+ virtual void Resize() override;
+ virtual void StateChanged( StateChangedType nStateChange ) override;
+};
+
+#endif // INCLUDED_SFX2_SOURCE_VIEW_IMPVIEWFRAME_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/view/ipclient.cxx b/sfx2/source/view/ipclient.cxx
new file mode 100644
index 0000000000..e6b9beb364
--- /dev/null
+++ b/sfx2/source/view/ipclient.cxx
@@ -0,0 +1,1148 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <com/sun/star/awt/XVclWindowPeer.hpp>
+#include <com/sun/star/embed/Aspects.hpp>
+#include <com/sun/star/embed/EmbedStates.hpp>
+#include <com/sun/star/embed/UnreachableStateException.hpp>
+#include <com/sun/star/embed/XEmbeddedClient.hpp>
+#include <com/sun/star/embed/XInplaceClient.hpp>
+#include <com/sun/star/embed/XInplaceObject.hpp>
+#include <com/sun/star/embed/XWindowSupplier.hpp>
+#include <com/sun/star/embed/EmbedVerbs.hpp>
+#include <com/sun/star/embed/XEmbeddedOleObject.hpp>
+#include <com/sun/star/embed/XEmbeddedObject.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/embed/XStateChangeListener.hpp>
+#include <com/sun/star/embed/StateChangeInProgressException.hpp>
+#include <com/sun/star/embed/XLinkageSupport.hpp>
+#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
+#include <com/sun/star/task/ErrorCodeIOException.hpp>
+#include <com/sun/star/task/StatusIndicatorFactory.hpp>
+#include <com/sun/star/task/XStatusIndicator.hpp>
+
+#include <com/sun/star/embed/EmbedMisc.hpp>
+#include <svtools/embedhlp.hxx>
+#include <vcl/svapp.hxx>
+
+#include <sfx2/ipclient.hxx>
+#include <sfx2/viewsh.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <sfx2/objsh.hxx>
+#include <guisaveas.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <svtools/ehdl.hxx>
+
+#include <vcl/timer.hxx>
+#include <vcl/window.hxx>
+#include <toolkit/helper/vclunohelper.hxx>
+#include <toolkit/helper/convert.hxx>
+#include <tools/debug.hxx>
+#include <comphelper/diagnose_ex.hxx>
+#include <tools/fract.hxx>
+#include <tools/gen.hxx>
+#include <svtools/soerr.hxx>
+#include <comphelper/lok.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/propertyvalue.hxx>
+#include <cppuhelper/exc_hlp.hxx>
+
+#include <LibreOfficeKit/LibreOfficeKitEnums.h>
+
+#define SFX_CLIENTACTIVATE_TIMEOUT 100
+
+using namespace com::sun::star;
+
+namespace {
+
+// SfxEmbedResizeGuard
+class SfxBooleanFlagGuard
+{
+ bool& m_rFlag;
+public:
+ explicit SfxBooleanFlagGuard(bool& bFlag)
+ : m_rFlag( bFlag )
+ {
+ m_rFlag = true;
+ }
+
+ ~SfxBooleanFlagGuard()
+ {
+ m_rFlag = false;
+ }
+};
+
+tools::Rectangle lcl_negateRectX(const tools::Rectangle& rRect)
+{
+ return tools::Rectangle(
+ std::max(static_cast<tools::Long>(0l), -rRect.Right()),
+ rRect.Top(),
+ std::max(static_cast<tools::Long>(0l), -rRect.Left()),
+ rRect.Bottom());
+}
+
+}
+
+// SfxInPlaceClient_Impl
+
+
+class SfxInPlaceClient_Impl : public ::cppu::WeakImplHelper< embed::XEmbeddedClient,
+ embed::XInplaceClient,
+ document::XEventListener,
+ embed::XStateChangeListener,
+ embed::XWindowSupplier >
+{
+public:
+ Timer m_aTimer { "sfx::SfxInPlaceClient m_xImpl::m_aTimer" }; // activation timeout, starts after object connection
+ tools::Rectangle m_aObjArea; // area of object in coordinate system of the container (without scaling)
+ Fraction m_aScaleWidth; // scaling that was applied to the object when it was not active
+ Fraction m_aScaleHeight;
+ SfxInPlaceClient* m_pClient;
+ sal_Int64 m_nAspect; // ViewAspect that is assigned from the container
+ bool m_bStoreObject;
+ bool m_bUIActive; // set and cleared when notification for UI (de)activation is sent
+ bool m_bResizeNoScale;
+ bool m_bNegativeX;
+
+ uno::Reference < embed::XEmbeddedObject > m_xObject;
+
+
+ SfxInPlaceClient_Impl()
+ : m_pClient( nullptr )
+ , m_nAspect( 0 )
+ , m_bStoreObject( true )
+ , m_bUIActive( false )
+ , m_bResizeNoScale( false )
+ , m_bNegativeX( false )
+ {}
+
+ void SizeHasChanged();
+ DECL_LINK(TimerHdl, Timer *, void);
+ uno::Reference < frame::XFrame > const & GetFrame() const;
+
+ // XEmbeddedClient
+ virtual void SAL_CALL saveObject() override;
+ virtual void SAL_CALL visibilityChanged( sal_Bool bVisible ) override;
+
+ // XInplaceClient
+ virtual sal_Bool SAL_CALL canInplaceActivate() override;
+ virtual void SAL_CALL activatingInplace() override;
+ virtual void SAL_CALL activatingUI() override;
+ virtual void SAL_CALL deactivatedInplace() override;
+ virtual void SAL_CALL deactivatedUI() override;
+ virtual uno::Reference< css::frame::XLayoutManager > SAL_CALL getLayoutManager() override;
+ virtual uno::Reference< frame::XDispatchProvider > SAL_CALL getInplaceDispatchProvider() override;
+ virtual awt::Rectangle SAL_CALL getPlacement() override;
+ virtual awt::Rectangle SAL_CALL getClipRectangle() override;
+ virtual void SAL_CALL translateAccelerators( const uno::Sequence< awt::KeyEvent >& aKeys ) override;
+ virtual void SAL_CALL scrollObject( const awt::Size& aOffset ) override;
+ virtual void SAL_CALL changedPlacement( const awt::Rectangle& aPosRect ) override;
+
+ // XComponentSupplier
+ virtual uno::Reference< util::XCloseable > SAL_CALL getComponent() override;
+
+ // XWindowSupplier
+ virtual uno::Reference< awt::XWindow > SAL_CALL getWindow() override;
+
+ // document::XEventListener
+ virtual void SAL_CALL notifyEvent( const document::EventObject& aEvent ) override;
+
+ // XStateChangeListener
+ virtual void SAL_CALL changingState( const css::lang::EventObject& aEvent, ::sal_Int32 nOldState, ::sal_Int32 nNewState ) override;
+ virtual void SAL_CALL stateChanged( const css::lang::EventObject& aEvent, ::sal_Int32 nOldState, ::sal_Int32 nNewState ) override;
+ virtual void SAL_CALL disposing( const css::lang::EventObject& aEvent ) override;
+};
+
+void SAL_CALL SfxInPlaceClient_Impl::changingState(
+ const css::lang::EventObject& /*aEvent*/,
+ ::sal_Int32 /*nOldState*/,
+ ::sal_Int32 /*nNewState*/ )
+{
+}
+
+void SAL_CALL SfxInPlaceClient_Impl::stateChanged(
+ const css::lang::EventObject& /*aEvent*/,
+ ::sal_Int32 nOldState,
+ ::sal_Int32 nNewState )
+{
+ if ( m_pClient && nOldState != embed::EmbedStates::LOADED && nNewState == embed::EmbedStates::RUNNING )
+ {
+ // deactivation of object
+ uno::Reference< frame::XModel > xDocument;
+ if ( m_pClient->GetViewShell()->GetObjectShell() )
+ xDocument = m_pClient->GetViewShell()->GetObjectShell()->GetModel();
+ SfxObjectShell::SetCurrentComponent( xDocument );
+ }
+}
+
+void SAL_CALL SfxInPlaceClient_Impl::notifyEvent( const document::EventObject& aEvent )
+{
+ SolarMutexGuard aGuard;
+
+ if ( m_pClient && aEvent.EventName == "OnVisAreaChanged" && m_nAspect != embed::Aspects::MSOLE_ICON )
+ {
+ m_pClient->FormatChanged(); // for Writer when format of the object is changed with the area
+ m_pClient->ViewChanged();
+ m_pClient->Invalidate();
+ }
+}
+
+void SAL_CALL SfxInPlaceClient_Impl::disposing( const css::lang::EventObject& /*aEvent*/ )
+{
+ delete m_pClient;
+ m_pClient = nullptr;
+}
+
+// XEmbeddedClient
+
+uno::Reference < frame::XFrame > const & SfxInPlaceClient_Impl::GetFrame() const
+{
+ if ( !m_pClient )
+ throw uno::RuntimeException();
+ return m_pClient->GetViewShell()->GetViewFrame().GetFrame().GetFrameInterface();
+}
+
+void SAL_CALL SfxInPlaceClient_Impl::saveObject()
+{
+ if (!m_bStoreObject || (m_pClient && m_pClient->IsProtected()))
+ // client wants to discard the object (usually it means the container document is closed while an object is active
+ // and the user didn't request saving the changes
+ return;
+
+ // the common persistence is supported by objects and links
+ uno::Reference< embed::XCommonEmbedPersist > xPersist( m_xObject, uno::UNO_QUERY_THROW );
+
+ uno::Reference< frame::XFrame > xFrame;
+ uno::Reference< task::XStatusIndicator > xStatusIndicator;
+ uno::Reference< frame::XModel > xModel( m_xObject->getComponent(), uno::UNO_QUERY );
+ uno::Reference< uno::XComponentContext > xContext( ::comphelper::getProcessComponentContext() );
+
+ if ( xModel.is() )
+ {
+ uno::Reference< frame::XController > xController = xModel->getCurrentController();
+ if ( xController.is() )
+ xFrame = xController->getFrame();
+ }
+
+ if ( xFrame.is() )
+ {
+ // set non-reschedule progress to prevent problems when asynchronous calls are made
+ // during storing of the embedded object
+ uno::Reference< task::XStatusIndicatorFactory > xStatusIndicatorFactory =
+ task::StatusIndicatorFactory::createWithFrame( xContext, xFrame, true/*DisableReschedule*/, false/*AllowParentShow*/ );
+
+ uno::Reference< beans::XPropertySet > xPropSet( xFrame, uno::UNO_QUERY );
+ if ( xPropSet.is() )
+ {
+ try
+ {
+ xStatusIndicator = xStatusIndicatorFactory->createStatusIndicator();
+ xPropSet->setPropertyValue( "IndicatorInterception" , uno::Any( xStatusIndicator ));
+ }
+ catch ( const uno::RuntimeException& )
+ {
+ throw;
+ }
+ catch ( uno::Exception& )
+ {
+ }
+ }
+ }
+
+ try
+ {
+ xPersist->storeOwn();
+ m_xObject->update();
+ }
+ catch ( uno::Exception& )
+ {
+ //TODO/LATER: what should happen if object can't be saved?!
+ }
+
+ // reset status indicator interception after storing
+ try
+ {
+ uno::Reference< beans::XPropertySet > xPropSet( xFrame, uno::UNO_QUERY );
+ if ( xPropSet.is() )
+ {
+ xStatusIndicator.clear();
+ xPropSet->setPropertyValue( "IndicatorInterception" , uno::Any( xStatusIndicator ));
+ }
+ }
+ catch ( const uno::RuntimeException& )
+ {
+ throw;
+ }
+ catch ( uno::Exception& )
+ {
+ }
+
+ // the client can exist only in case there is a view shell
+ if ( !m_pClient || !m_pClient->GetViewShell() )
+ throw uno::RuntimeException();
+
+ SfxObjectShell* pDocShell = m_pClient->GetViewShell()->GetObjectShell();
+ if ( !pDocShell )
+ throw uno::RuntimeException();
+
+ pDocShell->SetModified();
+
+ //TODO/LATER: invalidation might be necessary when object was modified, but is not
+ //saved through this method
+ // m_pClient->Invalidate();
+}
+
+
+void SAL_CALL SfxInPlaceClient_Impl::visibilityChanged( sal_Bool bVisible )
+{
+ SolarMutexGuard aGuard;
+
+ if ( !m_pClient || !m_pClient->GetViewShell() )
+ throw uno::RuntimeException();
+
+ m_pClient->GetViewShell()->OutplaceActivated( bVisible );
+ if (m_pClient) // it can change in the above code
+ m_pClient->Invalidate();
+}
+
+
+// XInplaceClient
+
+sal_Bool SAL_CALL SfxInPlaceClient_Impl::canInplaceActivate()
+{
+ if ( !m_xObject.is() )
+ throw uno::RuntimeException();
+
+ // we don't want to switch directly from outplace to inplace mode
+ if ( m_xObject->getCurrentState() == embed::EmbedStates::ACTIVE || m_nAspect == embed::Aspects::MSOLE_ICON )
+ return false;
+
+ return true;
+}
+
+
+void SAL_CALL SfxInPlaceClient_Impl::activatingInplace()
+{
+ if ( !m_pClient || !m_pClient->GetViewShell() )
+ throw uno::RuntimeException();
+
+ if ( !comphelper::LibreOfficeKit::isActive() )
+ return;
+
+ if ( SfxViewShell* pViewShell = m_pClient->GetViewShell() )
+ {
+ tools::Rectangle aRect(m_pClient->GetObjArea());
+
+ if (m_pClient->GetEditWin())
+ {
+ if (m_pClient->GetEditWin()->GetMapMode().GetMapUnit() == MapUnit::Map100thMM)
+ aRect = o3tl::convert(aRect, o3tl::Length::mm100, o3tl::Length::twip);
+ }
+
+ OString str = (m_bNegativeX ? lcl_negateRectX(aRect) : aRect).toString() + ", \"INPLACE\"";
+ pViewShell->libreOfficeKitViewCallback( LOK_CALLBACK_GRAPHIC_SELECTION, str );
+ }
+
+}
+
+
+void SAL_CALL SfxInPlaceClient_Impl::activatingUI()
+{
+ if ( !m_pClient || !m_pClient->GetViewShell() )
+ throw uno::RuntimeException();
+
+ m_pClient->GetViewShell()->ResetAllClients_Impl(m_pClient);
+ m_bUIActive = true;
+ m_pClient->GetViewShell()->UIActivating( m_pClient );
+}
+
+
+void SAL_CALL SfxInPlaceClient_Impl::deactivatedInplace()
+{
+ if ( !m_pClient || !m_pClient->GetViewShell() )
+ throw uno::RuntimeException();
+
+ if ( comphelper::LibreOfficeKit::isActive() )
+ {
+ if ( SfxViewShell* pViewShell = m_pClient->GetViewShell() ) {
+ pViewShell->libreOfficeKitViewCallback( LOK_CALLBACK_GRAPHIC_SELECTION, "INPLACE EXIT"_ostr );
+ }
+ }
+}
+
+
+void SAL_CALL SfxInPlaceClient_Impl::deactivatedUI()
+{
+ if ( !m_pClient || !m_pClient->GetViewShell() )
+ throw uno::RuntimeException();
+
+ m_pClient->GetViewShell()->UIDeactivated( m_pClient );
+ m_bUIActive = false;
+}
+
+
+uno::Reference< css::frame::XLayoutManager > SAL_CALL SfxInPlaceClient_Impl::getLayoutManager()
+{
+ uno::Reference < beans::XPropertySet > xFrame( GetFrame(), uno::UNO_QUERY_THROW );
+
+ uno::Reference< css::frame::XLayoutManager > xMan;
+ try
+ {
+ uno::Any aAny = xFrame->getPropertyValue( "LayoutManager" );
+ aAny >>= xMan;
+ }
+ catch ( uno::Exception& ex )
+ {
+ css::uno::Any anyEx = cppu::getCaughtException();
+ throw css::lang::WrappedTargetRuntimeException( ex.Message,
+ nullptr, anyEx );
+ }
+
+ return xMan;
+}
+
+
+uno::Reference< frame::XDispatchProvider > SAL_CALL SfxInPlaceClient_Impl::getInplaceDispatchProvider()
+{
+ return uno::Reference < frame::XDispatchProvider >( GetFrame(), uno::UNO_QUERY_THROW );
+}
+
+
+awt::Rectangle SAL_CALL SfxInPlaceClient_Impl::getPlacement()
+{
+ if ( !m_pClient || !m_pClient->GetViewShell() )
+ throw uno::RuntimeException();
+
+ // apply scaling to object area and convert to pixels
+ tools::Rectangle aRealObjArea( m_aObjArea );
+ aRealObjArea.SetSize( Size( tools::Long( aRealObjArea.GetWidth() * m_aScaleWidth),
+ tools::Long( aRealObjArea.GetHeight() * m_aScaleHeight) ) );
+
+ vcl::Window* pEditWin = m_pClient->GetEditWin();
+ // In Writer and Impress the map mode is disabled. So when a chart is
+ // activated (for in place editing) we get the chart win size in 100th mm
+ // and any method that should return pixels returns 100th mm and the chart
+ // window map mode has a ~26.485 scale factor.
+ // All that does not fit with current implementation for handling chart
+ // editing in LOK.
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ bool bMapModeEnabled = pEditWin->IsMapModeEnabled();
+ if (!bMapModeEnabled)
+ pEditWin->EnableMapMode();
+ aRealObjArea = pEditWin->LogicToPixel(aRealObjArea);
+ if (!bMapModeEnabled && pEditWin->IsMapModeEnabled())
+ pEditWin->EnableMapMode(false);
+ }
+ else
+ {
+ aRealObjArea = pEditWin->LogicToPixel(aRealObjArea);
+ }
+
+ return AWTRectangle( aRealObjArea );
+}
+
+
+awt::Rectangle SAL_CALL SfxInPlaceClient_Impl::getClipRectangle()
+{
+ if ( !m_pClient || !m_pClient->GetViewShell() )
+ throw uno::RuntimeException();
+
+ // currently(?) same as placement
+ tools::Rectangle aRealObjArea( m_aObjArea );
+ aRealObjArea.SetSize( Size( tools::Long( aRealObjArea.GetWidth() * m_aScaleWidth),
+ tools::Long( aRealObjArea.GetHeight() * m_aScaleHeight) ) );
+
+ vcl::Window* pEditWin = m_pClient->GetEditWin();
+ // See comment for SfxInPlaceClient_Impl::getPlacement.
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ bool bMapModeEnabled = pEditWin->IsMapModeEnabled();
+ if (!bMapModeEnabled)
+ pEditWin->EnableMapMode();
+ aRealObjArea = pEditWin->LogicToPixel(aRealObjArea);
+ if (!bMapModeEnabled && pEditWin->IsMapModeEnabled())
+ pEditWin->EnableMapMode(false);
+ }
+ else
+ {
+ aRealObjArea = pEditWin->LogicToPixel(aRealObjArea);
+ }
+
+ return AWTRectangle( aRealObjArea );
+}
+
+
+void SAL_CALL SfxInPlaceClient_Impl::translateAccelerators( const uno::Sequence< awt::KeyEvent >& /*aKeys*/ )
+{
+ if ( !m_pClient || !m_pClient->GetViewShell() )
+ throw uno::RuntimeException();
+
+ // TODO/MBA: keyboard accelerators
+}
+
+
+void SAL_CALL SfxInPlaceClient_Impl::scrollObject( const awt::Size& /*aOffset*/ )
+{
+ if ( !m_pClient || !m_pClient->GetViewShell() )
+ throw uno::RuntimeException();
+}
+
+
+void SAL_CALL SfxInPlaceClient_Impl::changedPlacement( const awt::Rectangle& aPosRect )
+{
+ uno::Reference< embed::XInplaceObject > xInplace( m_xObject, uno::UNO_QUERY_THROW );
+ if ( !m_pClient || !m_pClient->GetEditWin() || !m_pClient->GetViewShell() )
+ throw uno::RuntimeException();
+
+ // check if the change is at least one pixel in size
+ awt::Rectangle aOldRect = getPlacement();
+ tools::Rectangle aNewPixelRect = VCLRectangle( aPosRect );
+ tools::Rectangle aOldPixelRect = VCLRectangle( aOldRect );
+ if ( aOldPixelRect == aNewPixelRect )
+ // nothing has changed
+ return;
+
+ // new scaled object area
+ tools::Rectangle aNewLogicRect = m_pClient->GetEditWin()->PixelToLogic( aNewPixelRect );
+
+ // all the size changes in this method should happen without scaling
+ // SfxBooleanFlagGuard aGuard( m_bResizeNoScale, sal_True );
+
+ // allow container to apply restrictions on the requested new area;
+ // the container might change the object view during size calculation;
+ // currently only writer does it
+ m_pClient->RequestNewObjectArea( aNewLogicRect);
+
+ if ( aNewLogicRect != m_pClient->GetScaledObjArea() )
+ {
+ // the calculation of the object area has not changed the object size
+ // it should be done here then
+ SfxBooleanFlagGuard aGuard( m_bResizeNoScale );
+
+ // new size of the object area without scaling
+ Size aNewObjSize( tools::Long( aNewLogicRect.GetWidth() / m_aScaleWidth ),
+ tools::Long( aNewLogicRect.GetHeight() / m_aScaleHeight ) );
+
+ // now remove scaling from new placement and keep this at the new object area
+ aNewLogicRect.SetSize( aNewObjSize );
+ m_aObjArea = aNewLogicRect;
+
+ // let the window size be recalculated
+ SizeHasChanged();
+ }
+
+ // notify container view about changes
+ m_pClient->ObjectAreaChanged();
+}
+
+// XComponentSupplier
+
+uno::Reference< util::XCloseable > SAL_CALL SfxInPlaceClient_Impl::getComponent()
+{
+ if ( !m_pClient || !m_pClient->GetViewShell() )
+ throw uno::RuntimeException();
+
+ SfxObjectShell* pDocShell = m_pClient->GetViewShell()->GetObjectShell();
+ if ( !pDocShell )
+ throw uno::RuntimeException();
+
+ // all the components must implement XCloseable
+ uno::Reference< util::XCloseable > xComp( pDocShell->GetModel(), uno::UNO_QUERY_THROW );
+ return xComp;
+}
+
+
+// XWindowSupplier
+
+uno::Reference< awt::XWindow > SAL_CALL SfxInPlaceClient_Impl::getWindow()
+{
+ if ( !m_pClient || !m_pClient->GetEditWin() )
+ throw uno::RuntimeException();
+
+ uno::Reference< awt::XWindow > xWin( m_pClient->GetEditWin()->GetComponentInterface(), uno::UNO_QUERY );
+ return xWin;
+}
+
+
+// notification to the client implementation that either the object area or the scaling has been changed
+// as a result the logical size of the window has changed also
+void SfxInPlaceClient_Impl::SizeHasChanged()
+{
+ if ( !m_pClient || !m_pClient->GetViewShell() )
+ throw uno::RuntimeException();
+
+ try {
+ if ( m_xObject.is()
+ && ( m_xObject->getCurrentState() == embed::EmbedStates::INPLACE_ACTIVE
+ || m_xObject->getCurrentState() == embed::EmbedStates::UI_ACTIVE ) )
+ {
+ // only possible in active states
+ uno::Reference< embed::XInplaceObject > xInplace( m_xObject, uno::UNO_QUERY_THROW );
+
+ if ( m_bResizeNoScale )
+ {
+ // the resizing should be done without scaling
+ // set the correct size to the object to avoid the scaling
+ MapMode aObjectMap( VCLUnoHelper::UnoEmbed2VCLMapUnit( m_xObject->getMapUnit( m_nAspect ) ) );
+ MapMode aClientMap( m_pClient->GetEditWin()->GetMapMode().GetMapUnit() );
+
+ // convert to logical coordinates of the embedded object
+ Size aNewSize = m_pClient->GetEditWin()->LogicToLogic( m_aObjArea.GetSize(), &aClientMap, &aObjectMap );
+ m_xObject->setVisualAreaSize( m_nAspect, awt::Size( aNewSize.Width(), aNewSize.Height() ) );
+ }
+
+ xInplace->setObjectRectangles( getPlacement(), getClipRectangle() );
+ }
+ }
+ catch( uno::Exception& )
+ {
+ // TODO/LATER: handle error
+ }
+}
+
+
+IMPL_LINK_NOARG(SfxInPlaceClient_Impl, TimerHdl, Timer *, void)
+{
+ if ( m_pClient && m_xObject.is() )
+ {
+ m_pClient->GetViewShell()->CheckIPClient_Impl(m_pClient,
+ m_pClient->GetViewShell()->GetObjectShell()->GetVisArea());
+ }
+}
+
+
+// SfxInPlaceClient
+
+
+SfxInPlaceClient::SfxInPlaceClient( SfxViewShell* pViewShell, vcl::Window *pDraw, sal_Int64 nAspect ) :
+ m_xImp( new SfxInPlaceClient_Impl ),
+ m_pViewSh( pViewShell ),
+ m_pEditWin( pDraw )
+{
+ m_xImp->m_pClient = this;
+ m_xImp->m_nAspect = nAspect;
+ m_xImp->m_aScaleWidth = m_xImp->m_aScaleHeight = Fraction(1,1);
+ pViewShell->NewIPClient_Impl(this);
+ m_xImp->m_aTimer.SetTimeout( SFX_CLIENTACTIVATE_TIMEOUT );
+ m_xImp->m_aTimer.SetInvokeHandler( LINK( m_xImp.get(), SfxInPlaceClient_Impl, TimerHdl ) );
+}
+
+
+SfxInPlaceClient::~SfxInPlaceClient()
+{
+ m_pViewSh->IPClientGone_Impl(this);
+
+ // deleting the client before storing the object means discarding all changes
+ m_xImp->m_bStoreObject = false;
+ SetObject(nullptr);
+
+ m_xImp->m_pClient = nullptr;
+
+ // the next call will destroy m_xImp if no other reference to it exists
+ m_xImp.clear();
+
+ // TODO/LATER:
+ // the class is not intended to be used in multithreaded environment;
+ // if it will this disconnection and all the parts that use the m_pClient
+ // must be guarded with mutex
+}
+
+
+void SfxInPlaceClient::SetObjectState( sal_Int32 nState )
+{
+ if ( !GetObject().is() )
+ return;
+
+ if ( m_xImp->m_nAspect == embed::Aspects::MSOLE_ICON
+ && ( nState == embed::EmbedStates::UI_ACTIVE || nState == embed::EmbedStates::INPLACE_ACTIVE ) )
+ {
+ OSL_FAIL( "Iconified object should not be activated inplace!" );
+ return;
+ }
+
+ try
+ {
+ GetObject()->changeState( nState );
+ }
+ catch ( uno::Exception& )
+ {}
+}
+
+
+sal_Int64 SfxInPlaceClient::GetObjectMiscStatus() const
+{
+ if ( GetObject().is() )
+ return GetObject()->getStatus( m_xImp->m_nAspect );
+ return 0;
+}
+
+
+const uno::Reference < embed::XEmbeddedObject >& SfxInPlaceClient::GetObject() const
+{
+ return m_xImp->m_xObject;
+}
+
+
+void SfxInPlaceClient::SetObject( const uno::Reference < embed::XEmbeddedObject >& rObject )
+{
+ if ( m_xImp->m_xObject.is() && rObject != m_xImp->m_xObject )
+ {
+ DBG_ASSERT( GetObject()->getClientSite() == getXWeak(m_xImp.get()), "Wrong ClientSite!" );
+ if ( GetObject()->getClientSite() == getXWeak(m_xImp.get()) )
+ {
+ if ( GetObject()->getCurrentState() != embed::EmbedStates::LOADED )
+ SetObjectState( embed::EmbedStates::RUNNING );
+ m_xImp->m_xObject->removeEventListener( m_xImp );
+ m_xImp->m_xObject->removeStateChangeListener( m_xImp );
+ try
+ {
+ m_xImp->m_xObject->setClientSite( nullptr );
+ }
+ catch( uno::Exception& )
+ {
+ OSL_FAIL( "Can not clean the client site!" );
+ }
+ }
+ }
+
+ if ( m_pViewSh->GetViewFrame().GetFrame().IsClosing_Impl() )
+ // sometimes applications reconnect clients on shutting down because it happens in their Paint methods
+ return;
+
+ m_xImp->m_xObject = rObject;
+
+ if ( rObject.is() )
+ {
+ // as soon as an object was connected to a client it has to be checked whether the object wants
+ // to be activated
+ rObject->addStateChangeListener( m_xImp );
+ rObject->addEventListener( m_xImp );
+
+ try
+ {
+ rObject->setClientSite( m_xImp );
+ }
+ catch( uno::Exception& )
+ {
+ OSL_FAIL( "Can not set the client site!" );
+ }
+
+ m_xImp->m_aTimer.Start();
+ }
+ else
+ m_xImp->m_aTimer.Stop();
+}
+
+
+bool SfxInPlaceClient::SetObjArea( const tools::Rectangle& rArea )
+{
+ if( rArea != m_xImp->m_aObjArea )
+ {
+ m_xImp->m_aObjArea = rArea;
+ m_xImp->SizeHasChanged();
+
+ Invalidate();
+ return true;
+ }
+
+ return false;
+}
+
+
+const tools::Rectangle& SfxInPlaceClient::GetObjArea() const
+{
+ return m_xImp->m_aObjArea;
+}
+
+tools::Rectangle SfxInPlaceClient::GetScaledObjArea() const
+{
+ tools::Rectangle aRealObjArea( m_xImp->m_aObjArea );
+ aRealObjArea.SetSize( Size( tools::Long( aRealObjArea.GetWidth() * m_xImp->m_aScaleWidth ),
+ tools::Long( aRealObjArea.GetHeight() * m_xImp->m_aScaleHeight ) ) );
+ return aRealObjArea;
+}
+
+
+void SfxInPlaceClient::SetSizeScale( const Fraction & rScaleWidth, const Fraction & rScaleHeight )
+{
+ if ( m_xImp->m_aScaleWidth != rScaleWidth || m_xImp->m_aScaleHeight != rScaleHeight )
+ {
+ m_xImp->m_aScaleWidth = rScaleWidth;
+ m_xImp->m_aScaleHeight = rScaleHeight;
+
+ m_xImp->SizeHasChanged();
+
+ // TODO/LATER: Invalidate seems to trigger (wrong) recalculations of the ObjArea, so it's better
+ // not to call it here, but maybe it sounds reasonable to do so.
+ //Invalidate();
+ }
+}
+
+
+void SfxInPlaceClient::SetObjAreaAndScale( const tools::Rectangle& rArea, const Fraction& rScaleWidth, const Fraction& rScaleHeight )
+{
+ if( rArea != m_xImp->m_aObjArea || m_xImp->m_aScaleWidth != rScaleWidth || m_xImp->m_aScaleHeight != rScaleHeight )
+ {
+ m_xImp->m_aObjArea = rArea;
+ m_xImp->m_aScaleWidth = rScaleWidth;
+ m_xImp->m_aScaleHeight = rScaleHeight;
+
+ m_xImp->SizeHasChanged();
+
+ Invalidate();
+ }
+}
+
+
+const Fraction& SfxInPlaceClient::GetScaleWidth() const
+{
+ return m_xImp->m_aScaleWidth;
+}
+
+
+const Fraction& SfxInPlaceClient::GetScaleHeight() const
+{
+ return m_xImp->m_aScaleHeight;
+}
+
+
+void SfxInPlaceClient::Invalidate()
+{
+ // TODO/LATER: do we need both?
+
+ // the object area is provided in logical coordinates of the window but without scaling applied
+ tools::Rectangle aRealObjArea( m_xImp->m_aObjArea );
+ aRealObjArea.SetSize( Size( tools::Long( aRealObjArea.GetWidth() * m_xImp->m_aScaleWidth ),
+ tools::Long( aRealObjArea.GetHeight() * m_xImp->m_aScaleHeight ) ) );
+
+ m_pEditWin->Invalidate( IsNegativeX() ? lcl_negateRectX(aRealObjArea) : aRealObjArea );
+
+ ViewChanged();
+}
+
+
+bool SfxInPlaceClient::IsObjectUIActive() const
+{
+ try {
+ return ( m_xImp->m_xObject.is() && ( m_xImp->m_xObject->getCurrentState() == embed::EmbedStates::UI_ACTIVE ) );
+ }
+ catch( uno::Exception& )
+ {}
+
+ return false;
+}
+
+
+bool SfxInPlaceClient::IsObjectInPlaceActive() const
+{
+ try {
+ return(
+ (
+ m_xImp->m_xObject.is() &&
+ (m_xImp->m_xObject->getCurrentState() == embed::EmbedStates::INPLACE_ACTIVE)
+ ) ||
+ (
+ m_xImp->m_xObject.is() &&
+ (m_xImp->m_xObject->getCurrentState() == embed::EmbedStates::UI_ACTIVE)
+ )
+ );
+ }
+ catch( uno::Exception& )
+ {}
+
+ return false;
+}
+
+
+SfxInPlaceClient* SfxInPlaceClient::GetClient( SfxObjectShell const * pDoc, const css::uno::Reference < css::embed::XEmbeddedObject >& xObject )
+{
+ for ( SfxViewFrame* pFrame = SfxViewFrame::GetFirst(pDoc); pFrame; pFrame=SfxViewFrame::GetNext(*pFrame,pDoc) )
+ {
+ if( pFrame->GetViewShell() )
+ {
+ SfxInPlaceClient* pClient = pFrame->GetViewShell()->FindIPClient( xObject, nullptr );
+ if ( pClient )
+ return pClient;
+ }
+ }
+
+ return nullptr;
+}
+
+sal_Int64 SfxInPlaceClient::GetAspect() const
+{
+ return m_xImp->m_nAspect;
+}
+
+ErrCodeMsg SfxInPlaceClient::DoVerb(sal_Int32 nVerb)
+{
+ SfxErrorContext aEc(ERRCTX_SO_DOVERB, m_pViewSh->GetFrameWeld(), RID_SO_ERRCTX);
+ ErrCodeMsg nError = ERRCODE_NONE;
+
+ if ( m_xImp->m_xObject.is() )
+ {
+ bool bSaveCopyAs = false;
+ if ( nVerb == -8 ) // "Save Copy as..."
+ {
+ svt::EmbeddedObjectRef::TryRunningState( m_xImp->m_xObject );
+ // TODO/LATER: this special verb should disappear when outplace activation is completely available
+ uno::Reference< frame::XModel > xEmbModel( m_xImp->m_xObject->getComponent(), uno::UNO_QUERY );
+ if ( xEmbModel.is() )
+ {
+ bSaveCopyAs = true;
+
+ try
+ {
+ SfxStoringHelper aHelper;
+ uno::Sequence< beans::PropertyValue > aDispatchArgs{
+ comphelper::makePropertyValue("SaveTo", true)
+ };
+
+ aHelper.GUIStoreModel( xEmbModel,
+ u"SaveAs",
+ aDispatchArgs,
+ false,
+ SignatureState::NOSIGNATURES,
+ false );
+ }
+ catch( const task::ErrorCodeIOException& aErrorEx )
+ {
+ nError = ErrCode(aErrorEx.ErrCode);
+ }
+ catch( uno::Exception& )
+ {
+ nError = ERRCODE_IO_GENERAL;
+ // TODO/LATER: better error handling
+ }
+ }
+ }
+
+ if ( !bSaveCopyAs )
+ {
+ if ( m_xImp->m_nAspect == embed::Aspects::MSOLE_ICON )
+ {
+ // the common persistence is supported by objects and links
+
+ uno::Reference< embed::XEmbeddedOleObject > xEmbeddedOleObject( m_xImp->m_xObject, uno::UNO_QUERY );
+
+ if ( xEmbeddedOleObject.is() && (nVerb == embed::EmbedVerbs::MS_OLEVERB_PRIMARY || nVerb == embed::EmbedVerbs::MS_OLEVERB_OPEN || nVerb == embed::EmbedVerbs::MS_OLEVERB_SHOW ))
+ nVerb = embed::EmbedVerbs::MS_OLEVERB_SHOW;
+ else if ( nVerb == embed::EmbedVerbs::MS_OLEVERB_PRIMARY || nVerb == embed::EmbedVerbs::MS_OLEVERB_SHOW )
+ nVerb = embed::EmbedVerbs::MS_OLEVERB_OPEN; // outplace activation
+ else if ( nVerb == embed::EmbedVerbs::MS_OLEVERB_UIACTIVATE
+ || nVerb == embed::EmbedVerbs::MS_OLEVERB_IPACTIVATE )
+ nError = ERRCODE_SO_GENERALERROR;
+ }
+
+ if ( !nError )
+ {
+ // See comment for SfxInPlaceClient_Impl::getPlacement.
+ vcl::Window* pEditWin = GetEditWin();
+ bool bMapModeEnabled = pEditWin->IsMapModeEnabled();
+ if (comphelper::LibreOfficeKit::isActive() && !bMapModeEnabled)
+ {
+ pEditWin->EnableMapMode();
+ }
+ m_pViewSh->GetViewFrame().GetFrame().LockResize_Impl(true);
+ try
+ {
+ m_xImp->m_xObject->setClientSite( m_xImp );
+
+ m_xImp->m_xObject->doVerb( nVerb );
+ }
+ catch ( embed::UnreachableStateException& e )
+ {
+ if (nVerb == embed::EmbedVerbs::MS_OLEVERB_PRIMARY || nVerb == embed::EmbedVerbs::MS_OLEVERB_OPEN || nVerb == embed::EmbedVerbs::MS_OLEVERB_SHOW)
+ {
+ // a workaround for the default verb, usually makes sense for alien objects
+ try
+ {
+ m_xImp->m_xObject->doVerb( -9 ); // open own view, a workaround verb that is not visible
+
+ if ( m_xImp->m_xObject->getCurrentState() == embed::EmbedStates::UI_ACTIVE )
+ {
+ // the object was converted to OOo object
+ awt::Size aSize = m_xImp->m_xObject->getVisualAreaSize( m_xImp->m_nAspect );
+ MapMode aObjectMap( VCLUnoHelper::UnoEmbed2VCLMapUnit( m_xImp->m_xObject->getMapUnit( m_xImp->m_nAspect ) ) );
+ MapMode aClientMap( GetEditWin()->GetMapMode().GetMapUnit() );
+ Size aNewSize = GetEditWin()->LogicToLogic( Size( aSize.Width, aSize.Height ), &aObjectMap, &aClientMap );
+
+ tools::Rectangle aScaledArea = GetScaledObjArea();
+ m_xImp->m_aObjArea.SetSize( aNewSize );
+ m_xImp->m_aScaleWidth = Fraction( aScaledArea.GetWidth(), aNewSize.Width() );
+ m_xImp->m_aScaleHeight = Fraction( aScaledArea.GetHeight(), aNewSize.Height() );
+ }
+ }
+ catch (uno::Exception const&)
+ {
+ TOOLS_WARN_EXCEPTION("embeddedobj", "SfxInPlaceClient::DoVerb: -9 fallback path");
+ nError = ErrCodeMsg(ERRCODE_SO_GENERALERROR, e.Message);
+ }
+ }
+ }
+ catch ( embed::StateChangeInProgressException& )
+ {
+ // TODO/LATER: it would be nice to be able to provide the current target state outside
+ nError = ERRCODE_SO_CANNOT_DOVERB_NOW;
+ }
+ catch (uno::Exception const&)
+ {
+ TOOLS_WARN_EXCEPTION("embeddedobj", "SfxInPlaceClient::DoVerb");
+ nError = ERRCODE_SO_GENERALERROR;
+ //TODO/LATER: better error handling
+
+ }
+ if (comphelper::LibreOfficeKit::isActive() && !bMapModeEnabled
+ && pEditWin->IsMapModeEnabled())
+ {
+ pEditWin->EnableMapMode(false);
+ }
+ SfxViewFrame& rFrame = m_pViewSh->GetViewFrame();
+ rFrame.GetFrame().LockResize_Impl(false);
+ rFrame.GetFrame().Resize();
+ }
+ }
+ }
+
+ if( nError )
+ ErrorHandler::HandleError( nError );
+
+ return nError;
+}
+
+void SfxInPlaceClient::VisAreaChanged()
+{
+ uno::Reference < embed::XInplaceObject > xObj( m_xImp->m_xObject, uno::UNO_QUERY );
+ if ( xObj.is() )
+ m_xImp->SizeHasChanged();
+}
+
+void SfxInPlaceClient::ObjectAreaChanged()
+{
+ // dummy implementation
+}
+
+void SfxInPlaceClient::RequestNewObjectArea( tools::Rectangle& )
+{
+ // dummy implementation
+}
+
+void SfxInPlaceClient::ViewChanged()
+{
+ // dummy implementation
+}
+
+void SfxInPlaceClient::FormatChanged()
+{
+ // dummy implementation
+}
+
+bool SfxInPlaceClient::IsProtected() const { return false; }
+
+void SfxInPlaceClient::DeactivateObject()
+{
+ if ( !GetObject().is() )
+ return;
+
+ try
+ {
+ m_xImp->m_bUIActive = false;
+ bool bHasFocus = false;
+ uno::Reference< frame::XModel > xModel( m_xImp->m_xObject->getComponent(), uno::UNO_QUERY );
+ if ( xModel.is() )
+ {
+ uno::Reference< frame::XController > xController = xModel->getCurrentController();
+ if ( xController.is() )
+ {
+ VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow( xController->getFrame()->getContainerWindow() );
+ bHasFocus = pWindow->HasChildPathFocus( true );
+ }
+ }
+
+ m_pViewSh->GetViewFrame().GetFrame().LockResize_Impl(true);
+
+ if ( m_xImp->m_xObject->getStatus( m_xImp->m_nAspect ) & embed::EmbedMisc::MS_EMBED_ACTIVATEWHENVISIBLE )
+ {
+ m_xImp->m_xObject->changeState( embed::EmbedStates::INPLACE_ACTIVE );
+ if (bHasFocus)
+ m_pViewSh->GetWindow()->GrabFocus();
+ }
+ else
+ {
+ // the links should not stay in running state for long time because of locking
+ uno::Reference< embed::XLinkageSupport > xLink( m_xImp->m_xObject, uno::UNO_QUERY );
+ if ( xLink.is() && xLink->isLink() )
+ m_xImp->m_xObject->changeState( embed::EmbedStates::LOADED );
+ else
+ m_xImp->m_xObject->changeState( embed::EmbedStates::RUNNING );
+ }
+
+ SfxViewFrame& rFrame = m_pViewSh->GetViewFrame();
+ SfxViewFrame::SetViewFrame( &rFrame );
+ rFrame.GetFrame().LockResize_Impl(false);
+ rFrame.GetFrame().Resize();
+ }
+ catch (css::uno::Exception& )
+ {}
+}
+
+void SfxInPlaceClient::ResetObject()
+{
+ if ( !GetObject().is() )
+ return;
+
+ try
+ {
+ m_xImp->m_bUIActive = false;
+ if ( m_xImp->m_xObject->getStatus( m_xImp->m_nAspect ) & embed::EmbedMisc::MS_EMBED_ACTIVATEWHENVISIBLE )
+ m_xImp->m_xObject->changeState( embed::EmbedStates::INPLACE_ACTIVE );
+ else
+ {
+ // the links should not stay in running state for long time because of locking
+ uno::Reference< embed::XLinkageSupport > xLink( m_xImp->m_xObject, uno::UNO_QUERY );
+ if ( xLink.is() && xLink->isLink() )
+ m_xImp->m_xObject->changeState( embed::EmbedStates::LOADED );
+ else
+ m_xImp->m_xObject->changeState( embed::EmbedStates::RUNNING );
+ }
+ }
+ catch (css::uno::Exception& )
+ {}
+}
+
+bool SfxInPlaceClient::IsUIActive() const
+{
+ return m_xImp->m_bUIActive;
+}
+
+void SfxInPlaceClient::SetNegativeX(bool bSet)
+{
+ m_xImp->m_bNegativeX = bSet;
+}
+
+bool SfxInPlaceClient::IsNegativeX() const
+{
+ return m_xImp->m_bNegativeX;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/view/lokcharthelper.cxx b/sfx2/source/view/lokcharthelper.cxx
new file mode 100644
index 0000000000..f8e8ec47ea
--- /dev/null
+++ b/sfx2/source/view/lokcharthelper.cxx
@@ -0,0 +1,369 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <sfx2/lokcomponenthelpers.hxx>
+
+#include <comphelper/lok.hxx>
+#include <comphelper/propertyvalue.hxx>
+#include <LibreOfficeKit/LibreOfficeKitEnums.h>
+#include <sfx2/ipclient.hxx>
+#include <sfx2/lokhelper.hxx>
+#include <toolkit/helper/vclunohelper.hxx>
+#include <tools/fract.hxx>
+#include <tools/UnitConversion.hxx>
+#include <vcl/virdev.hxx>
+#include <vcl/window.hxx>
+
+#include <com/sun/star/embed/XEmbeddedObject.hpp>
+#include <com/sun/star/frame/XDispatch.hpp>
+#include <com/sun/star/chart2/XChartDocument.hpp>
+
+using namespace com::sun::star;
+
+css::uno::Reference<css::frame::XController>& LokChartHelper::GetXController()
+{
+ if(!mxController.is() && mpViewShell)
+ {
+ SfxInPlaceClient* pIPClient = mpViewShell->GetIPClient();
+ if (pIPClient)
+ {
+ const css::uno::Reference< ::css::embed::XEmbeddedObject >& xEmbObj = pIPClient->GetObject();
+ if( xEmbObj.is() )
+ {
+ ::css::uno::Reference< ::css::chart2::XChartDocument > xChart( xEmbObj->getComponent(), uno::UNO_QUERY );
+ if( xChart.is() )
+ {
+ ::css::uno::Reference< ::css::frame::XController > xChartController = xChart->getCurrentController();
+ if( xChartController.is() )
+ {
+ mxController = xChartController;
+ }
+ }
+ }
+ }
+ }
+
+ return mxController;
+}
+
+css::uno::Reference<css::frame::XDispatch>& LokChartHelper::GetXDispatcher()
+{
+ if( !mxDispatcher.is() )
+ {
+ ::css::uno::Reference< ::css::frame::XController >& xChartController = GetXController();
+ if( xChartController.is() )
+ {
+ ::css::uno::Reference< ::css::frame::XDispatch > xDispatcher( xChartController, uno::UNO_QUERY );
+ if( xDispatcher.is() )
+ {
+ mxDispatcher = xDispatcher;
+ }
+ }
+ }
+
+ return mxDispatcher;
+}
+
+vcl::Window* LokChartHelper::GetWindow()
+{
+ if (!mpWindow)
+ {
+ ::css::uno::Reference< ::css::frame::XController >& xChartController = GetXController();
+ if( xChartController.is() )
+ {
+ ::css::uno::Reference< ::css::frame::XFrame > xFrame = xChartController->getFrame();
+ if (xFrame.is())
+ {
+ ::css::uno::Reference< ::css::awt::XWindow > xDockerWin = xFrame->getContainerWindow();
+ vcl::Window* pParent = VCLUnoHelper::GetWindow( xDockerWin );
+ if (pParent)
+ {
+ sal_uInt16 nTotChildren = pParent->GetChildCount();
+ while (nTotChildren--)
+ {
+ vcl::Window* pChildWin = pParent->GetChild(nTotChildren);
+ if (pChildWin && pChildWin->IsChart())
+ {
+ mpWindow = pChildWin;
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return mpWindow.get();
+}
+
+tools::Rectangle LokChartHelper::GetChartBoundingBox()
+{
+ tools::Rectangle aBBox;
+ if (mpViewShell)
+ {
+ SfxInPlaceClient* pIPClient = mpViewShell->GetIPClient();
+ if (pIPClient)
+ {
+ vcl::Window* pRootWin = pIPClient->GetEditWin();
+ if (pRootWin)
+ {
+ vcl::Window* pWindow = GetWindow();
+ if (pWindow)
+ {
+ // In all cases, the following code fragment
+ // returns the chart bounding box in twips.
+ const MapMode& aCWMapMode = pWindow->GetMapMode();
+ constexpr auto p = o3tl::getConversionMulDiv(o3tl::Length::px, o3tl::Length::twip);
+ const auto& scaleX = aCWMapMode.GetScaleX();
+ const auto& scaleY = aCWMapMode.GetScaleY();
+ const auto nXNum = p.first * scaleX.GetDenominator();
+ const auto nXDen = p.second * scaleX.GetNumerator();
+ const auto nYNum = p.first * scaleY.GetDenominator();
+ const auto nYDen = p.second * scaleY.GetNumerator();
+
+ Point aOffset = pWindow->GetOffsetPixelFrom(*pRootWin);
+ if (mbNegativeX && AllSettings::GetLayoutRTL())
+ {
+ // If global RTL flag is set, vcl-window X offset of chart window is
+ // mirrored w.r.t parent window rectangle. This needs to be reverted.
+ aOffset.setX(pRootWin->GetOutOffXPixel() + pRootWin->GetSizePixel().Width()
+ - pWindow->GetOutOffXPixel() - pWindow->GetSizePixel().Width());
+
+ }
+
+ aOffset = aOffset.scale(nXNum, nXDen, nYNum, nYDen);
+ Size aSize = pWindow->GetSizePixel().scale(nXNum, nXDen, nYNum, nYDen);
+ aBBox = tools::Rectangle(aOffset, aSize);
+ }
+ }
+ }
+ }
+ return aBBox;
+}
+
+void LokChartHelper::Invalidate()
+{
+ mpWindow = nullptr;
+ mxDispatcher.clear();
+ mxController.clear();
+}
+
+bool LokChartHelper::Hit(const Point& aPos)
+{
+ if (mpViewShell)
+ {
+ vcl::Window* pChartWindow = GetWindow();
+ if (pChartWindow)
+ {
+ tools::Rectangle rChartBBox = GetChartBoundingBox();
+ return rChartBBox.Contains(aPos);
+ }
+ }
+ return false;
+}
+
+bool LokChartHelper::HitAny(const Point& aPos, bool bNegativeX)
+{
+ SfxViewShell* pCurView = SfxViewShell::Current();
+ int nPartForCurView = pCurView ? pCurView->getPart() : -1;
+ SfxViewShell* pViewShell = SfxViewShell::GetFirst();
+ while (pViewShell)
+ {
+ if (pCurView && pViewShell->GetDocId() == pCurView->GetDocId() && pViewShell->getPart() == nPartForCurView)
+ {
+ LokChartHelper aChartHelper(pViewShell, bNegativeX);
+ if (aChartHelper.Hit(aPos))
+ return true;
+ }
+ pViewShell = SfxViewShell::GetNext(*pViewShell);
+ }
+ return false;
+}
+
+void LokChartHelper::PaintTile(VirtualDevice& rRenderContext, const tools::Rectangle& rTileRect)
+{
+ if (!mpViewShell)
+ return;
+
+ vcl::Window* pChartWindow = GetWindow();
+ if (!pChartWindow)
+ return;
+
+ tools::Rectangle aChartRect = GetChartBoundingBox();
+ tools::Rectangle aTestRect = rTileRect;
+ aTestRect.Intersection( aChartRect );
+ if (aTestRect.IsEmpty())
+ return;
+
+ Point aOffset( aChartRect.Left() - rTileRect.Left(), aChartRect.Top() - rTileRect.Top() );
+ Point aOffsetFromTile = convertTwipToMm100(aOffset);
+ Size aSize = convertTwipToMm100(aChartRect.GetSize());
+ tools::Rectangle aRectangle(Point(0,0), aSize);
+
+ bool bEnableMapMode = !pChartWindow->IsMapModeEnabled();
+ pChartWindow->EnableMapMode();
+ bool bRenderContextEnableMapMode = !rRenderContext.IsMapModeEnabled();
+ rRenderContext.EnableMapMode();
+
+ rRenderContext.Push(vcl::PushFlags::MAPMODE);
+
+ MapMode aCWMapMode = pChartWindow->GetMapMode();
+ aCWMapMode.SetScaleX(rRenderContext.GetMapMode().GetScaleX());
+ aCWMapMode.SetScaleY(rRenderContext.GetMapMode().GetScaleY());
+
+ aCWMapMode.SetOrigin(aOffsetFromTile);
+ rRenderContext.SetMapMode(aCWMapMode);
+
+ pChartWindow->Paint(rRenderContext, aRectangle);
+
+ rRenderContext.Pop();
+
+ if (bRenderContextEnableMapMode)
+ rRenderContext.EnableMapMode(false);
+ if (bEnableMapMode)
+ pChartWindow->EnableMapMode(false);
+}
+
+void LokChartHelper::PaintAllChartsOnTile(VirtualDevice& rDevice,
+ int nOutputWidth, int nOutputHeight,
+ int nTilePosX, int nTilePosY,
+ tools::Long nTileWidth, tools::Long nTileHeight,
+ bool bNegativeX)
+{
+ if (comphelper::LibreOfficeKit::isTiledAnnotations())
+ return;
+
+ // Resizes the virtual device so to contain the entries context
+ rDevice.SetOutputSizePixel(Size(nOutputWidth, nOutputHeight));
+
+ rDevice.Push(vcl::PushFlags::MAPMODE);
+ MapMode aMapMode(rDevice.GetMapMode());
+
+ // Scaling. Must convert from pixels to twips. We know
+ // that VirtualDevices use a DPI of 96.
+ const Fraction scale = conversionFract(o3tl::Length::px, o3tl::Length::twip);
+ Fraction scaleX = Fraction(nOutputWidth, nTileWidth) * scale;
+ Fraction scaleY = Fraction(nOutputHeight, nTileHeight) * scale;
+ aMapMode.SetScaleX(scaleX);
+ aMapMode.SetScaleY(scaleY);
+ rDevice.SetMapMode(aMapMode);
+
+ SfxViewShell* pCurView = SfxViewShell::Current();
+ int nPartForCurView = pCurView ? pCurView->getPart() : -1;
+ tools::Long nTileRectLeft = bNegativeX ? -nTilePosX - nTileWidth : nTilePosX;
+ tools::Rectangle aTileRect(Point(nTileRectLeft, nTilePosY), Size(nTileWidth, nTileHeight));
+ SfxViewShell* pViewShell = SfxViewShell::GetFirst();
+ while (pViewShell)
+ {
+ if (pCurView && pViewShell->GetDocId() == pCurView->GetDocId() && pViewShell->getPart() == nPartForCurView)
+ {
+ LokChartHelper aChartHelper(pViewShell, bNegativeX);
+ aChartHelper.PaintTile(rDevice, aTileRect);
+ }
+ pViewShell = SfxViewShell::GetNext(*pViewShell);
+ }
+ rDevice.Pop();
+}
+
+bool LokChartHelper::postMouseEvent(int nType, int nX, int nY,
+ int nCount, int nButtons, int nModifier,
+ double fScaleX, double fScaleY)
+{
+ Point aMousePos(nX, nY);
+ vcl::Window* pChartWindow = GetWindow();
+ if (pChartWindow)
+ {
+ tools::Rectangle rChartBBox = GetChartBoundingBox();
+ if (rChartBBox.Contains(aMousePos))
+ {
+ int nChartWinX = nX - rChartBBox.Left();
+ int nChartWinY = nY - rChartBBox.Top();
+
+ // chart window expects pixels, but the conversion factor
+ // can depend on the client zoom
+ Point aPos(nChartWinX * fScaleX, nChartWinY * fScaleY);
+
+ LokMouseEventData aMouseEventData(nType, aPos, nCount, MouseEventModifiers::SIMPLECLICK,
+ nButtons, nModifier);
+ SfxLokHelper::postMouseEventAsync(pChartWindow, aMouseEventData);
+
+ return true;
+ }
+ }
+ return false;
+}
+
+bool LokChartHelper::setTextSelection(int nType, int nX, int nY)
+{
+ tools::Rectangle rChartBBox = GetChartBoundingBox();
+ if (rChartBBox.Contains(Point(nX, nY)))
+ {
+ css::uno::Reference<css::frame::XDispatch> xDispatcher = GetXDispatcher();
+ if (xDispatcher.is())
+ {
+ int nChartWinX = nX - rChartBBox.Left();
+ int nChartWinY = nY - rChartBBox.Top();
+
+ // no scale here the chart controller expects twips
+ // that are converted to hmm
+ util::URL aURL;
+ aURL.Path = "LOKSetTextSelection";
+ uno::Sequence< beans::PropertyValue > aArgs{
+ comphelper::makePropertyValue({}, static_cast<sal_Int32>(nType)), // Why no name?
+ comphelper::makePropertyValue({}, static_cast<sal_Int32>(nChartWinX)),
+ comphelper::makePropertyValue({}, static_cast<sal_Int32>(nChartWinY))
+ };
+ xDispatcher->dispatch(aURL, aArgs);
+ }
+ return true;
+ }
+ return false;
+}
+
+bool LokChartHelper::setGraphicSelection(int nType, int nX, int nY,
+ double fScaleX, double fScaleY)
+{
+ tools::Rectangle rChartBBox = GetChartBoundingBox();
+ if (rChartBBox.Contains(Point(nX, nY)))
+ {
+ int nChartWinX = nX - rChartBBox.Left();
+ int nChartWinY = nY - rChartBBox.Top();
+
+ vcl::Window* pChartWindow = GetWindow();
+
+ Point aPos(nChartWinX * fScaleX, nChartWinY * fScaleY);
+ switch (nType)
+ {
+ case LOK_SETGRAPHICSELECTION_START:
+ {
+ MouseEvent aClickEvent(aPos, 1, MouseEventModifiers::SIMPLECLICK, MOUSE_LEFT);
+ pChartWindow->MouseButtonDown(aClickEvent);
+ MouseEvent aMoveEvent(aPos, 0, MouseEventModifiers::SIMPLEMOVE, MOUSE_LEFT);
+ pChartWindow->MouseMove(aMoveEvent);
+ }
+ break;
+ case LOK_SETGRAPHICSELECTION_END:
+ {
+ MouseEvent aMoveEvent(aPos, 0, MouseEventModifiers::SIMPLEMOVE, MOUSE_LEFT);
+ pChartWindow->MouseMove(aMoveEvent);
+ MouseEvent aClickEvent(aPos, 1, MouseEventModifiers::SIMPLECLICK, MOUSE_LEFT);
+ pChartWindow->MouseButtonUp(aClickEvent);
+ }
+ break;
+ default:
+ assert(false);
+ break;
+ }
+ return true;
+ }
+ return false;
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/view/lokhelper.cxx b/sfx2/source/view/lokhelper.cxx
new file mode 100644
index 0000000000..6ef68f2e42
--- /dev/null
+++ b/sfx2/source/view/lokhelper.cxx
@@ -0,0 +1,1066 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <sal/config.h>
+
+#include <string>
+#include <string_view>
+#include <list>
+
+#include <sfx2/lokcomponenthelpers.hxx>
+#include <sfx2/lokhelper.hxx>
+
+#include <com/sun/star/frame/Desktop.hpp>
+#include <com/sun/star/ui/ContextChangeEventObject.hpp>
+
+#include <comphelper/processfactory.hxx>
+#include <o3tl/string_view.hxx>
+#include <rtl/strbuf.hxx>
+#include <vcl/lok.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/commandevent.hxx>
+#include <vcl/window.hxx>
+#include <sal/log.hxx>
+#include <sfx2/app.hxx>
+#include <sfx2/msg.hxx>
+#include <sfx2/viewsh.hxx>
+#include <sfx2/request.hxx>
+#include <sfx2/sfxsids.hrc>
+#include <sfx2/viewfrm.hxx>
+#include <LibreOfficeKit/LibreOfficeKitEnums.h>
+#include <comphelper/lok.hxx>
+#include <sfx2/msgpool.hxx>
+
+#include <boost/property_tree/json_parser.hpp>
+
+using namespace com::sun::star;
+
+namespace {
+/// Used to disable callbacks.
+/// Needed to avoid recursion when switching views,
+/// which can cause clients to invoke LOKit API and
+/// implicitly set the view, which might cause an
+/// infinite recursion if not detected and prevented.
+class DisableCallbacks
+{
+public:
+ DisableCallbacks()
+ {
+ assert(m_nDisabled >= 0 && "Expected non-negative DisabledCallbacks state when disabling.");
+ ++m_nDisabled;
+ }
+
+ ~DisableCallbacks()
+ {
+ assert(m_nDisabled > 0 && "Expected positive DisabledCallbacks state when re-enabling.");
+ --m_nDisabled;
+ }
+
+ static inline bool disabled()
+ {
+ return !comphelper::LibreOfficeKit::isActive() || m_nDisabled != 0;
+ }
+
+private:
+ static int m_nDisabled;
+};
+
+int DisableCallbacks::m_nDisabled = 0;
+}
+
+namespace
+{
+LanguageTag g_defaultLanguageTag("en-US", true);
+LanguageTag g_loadLanguageTag("en-US", true); //< The language used to load.
+LOKDeviceFormFactor g_deviceFormFactor = LOKDeviceFormFactor::UNKNOWN;
+bool g_isDefaultTimezoneSet = false;
+OUString g_DefaultTimezone;
+const std::size_t g_logNotifierCacheMaxSize = 50;
+::std::list<::std::string> g_logNotifierCache;
+}
+
+int SfxLokHelper::createView(SfxViewFrame& rViewFrame, ViewShellDocId docId)
+{
+ assert(docId >= ViewShellDocId(0) && "Cannot createView for invalid (negative) DocId.");
+
+ SfxViewShell::SetCurrentDocId(docId);
+ SfxRequest aRequest(rViewFrame, SID_NEWWINDOW);
+ rViewFrame.ExecView_Impl(aRequest);
+ SfxViewShell* pViewShell = SfxViewShell::Current();
+ if (pViewShell == nullptr)
+ return -1;
+
+ assert(pViewShell->GetDocId() == docId && "DocId must be already set!");
+ return static_cast<sal_Int32>(pViewShell->GetViewShellId());
+}
+
+int SfxLokHelper::createView()
+{
+ // Assumes a single document, or at least that the
+ // current view belongs to the document on which the
+ // view will be created.
+ SfxViewShell* pViewShell = SfxViewShell::Current();
+ if (pViewShell == nullptr)
+ return -1;
+
+ return createView(pViewShell->GetViewFrame(), pViewShell->GetDocId());
+}
+
+std::unordered_map<OUString, css::uno::Reference<com::sun::star::ui::XAcceleratorConfiguration>>& SfxLokHelper::getAcceleratorConfs()
+{
+ return SfxApplication::GetOrCreate()->GetAcceleratorConfs_Impl();
+}
+
+int SfxLokHelper::createView(int nDocId)
+{
+ const SfxApplication* pApp = SfxApplication::Get();
+ if (pApp == nullptr)
+ return -1;
+
+ // Find a shell with the given DocId.
+ const ViewShellDocId docId(nDocId);
+ for (const SfxViewShell* pViewShell : pApp->GetViewShells_Impl())
+ {
+ if (pViewShell->GetDocId() == docId)
+ return createView(pViewShell->GetViewFrame(), docId);
+ }
+
+ // No frame with nDocId found.
+ return -1;
+}
+
+void SfxLokHelper::setEditMode(int nMode, vcl::ITiledRenderable* pDoc)
+{
+ DisableCallbacks dc;
+ pDoc->setEditMode(nMode);
+}
+
+void SfxLokHelper::destroyView(int nId)
+{
+ const SfxApplication* pApp = SfxApplication::Get();
+ if (pApp == nullptr)
+ return;
+
+ const ViewShellId nViewShellId(nId);
+ std::vector<SfxViewShell*>& rViewArr = pApp->GetViewShells_Impl();
+
+ for (SfxViewShell* pViewShell : rViewArr)
+ {
+ if (pViewShell->GetViewShellId() == nViewShellId)
+ {
+ pViewShell->SetLOKAccessibilityState(false);
+ SfxViewFrame& rViewFrame = pViewShell->GetViewFrame();
+ SfxRequest aRequest(rViewFrame, SID_CLOSEWIN);
+ rViewFrame.Exec_Impl(aRequest);
+ break;
+ }
+ }
+}
+
+void SfxLokHelper::setView(int nId)
+{
+ SfxApplication* pApp = SfxApplication::Get();
+ if (pApp == nullptr)
+ return;
+
+ const ViewShellId nViewShellId(nId);
+ std::vector<SfxViewShell*>& rViewArr = pApp->GetViewShells_Impl();
+
+ for (const SfxViewShell* pViewShell : rViewArr)
+ {
+ if (pViewShell->GetViewShellId() == nViewShellId)
+ {
+ DisableCallbacks dc;
+
+ if (pViewShell == SfxViewShell::Current())
+ return;
+
+ // update the current LOK language and locale for the dialog tunneling
+ comphelper::LibreOfficeKit::setLanguageTag(pViewShell->GetLOKLanguageTag());
+ comphelper::LibreOfficeKit::setLocale(pViewShell->GetLOKLocale());
+
+ SfxViewFrame& rViewFrame = pViewShell->GetViewFrame();
+ rViewFrame.MakeActive_Impl(false);
+
+ // Make comphelper::dispatchCommand() find the correct frame.
+ uno::Reference<frame::XFrame> xFrame = rViewFrame.GetFrame().GetFrameInterface();
+ uno::Reference<frame::XDesktop2> xDesktop = frame::Desktop::create(comphelper::getProcessComponentContext());
+ xDesktop->setActiveFrame(xFrame);
+ return;
+ }
+ }
+
+}
+
+SfxViewShell* SfxLokHelper::getViewOfId(int nId)
+{
+ SfxApplication* pApp = SfxApplication::Get();
+ if (pApp == nullptr)
+ return nullptr;
+
+ const ViewShellId nViewShellId(nId);
+ std::vector<SfxViewShell*>& rViewArr = pApp->GetViewShells_Impl();
+ for (SfxViewShell* pViewShell : rViewArr)
+ {
+ if (pViewShell->GetViewShellId() == nViewShellId)
+ return pViewShell;
+ }
+
+ return nullptr;
+}
+
+int SfxLokHelper::getView(const SfxViewShell* pViewShell)
+{
+ if (!pViewShell)
+ pViewShell = SfxViewShell::Current();
+ // Still no valid view shell? Then no idea.
+ if (!pViewShell)
+ return -1;
+
+ return static_cast<sal_Int32>(pViewShell->GetViewShellId());
+}
+
+std::size_t SfxLokHelper::getViewsCount(int nDocId)
+{
+ assert(nDocId != -1 && "Cannot getViewsCount for invalid DocId -1");
+
+ SfxApplication* pApp = SfxApplication::Get();
+ if (!pApp)
+ return 0;
+
+ const ViewShellDocId nCurrentDocId(nDocId);
+ std::size_t n = 0;
+ SfxViewShell* pViewShell = SfxViewShell::GetFirst();
+ while (pViewShell)
+ {
+ if (pViewShell->GetDocId() == nCurrentDocId)
+ n++;
+ pViewShell = SfxViewShell::GetNext(*pViewShell);
+ }
+
+ return n;
+}
+
+bool SfxLokHelper::getViewIds(int nDocId, int* pArray, size_t nSize)
+{
+ assert(nDocId != -1 && "Cannot getViewsIds for invalid DocId -1");
+
+ SfxApplication* pApp = SfxApplication::Get();
+ if (!pApp)
+ return false;
+
+ const ViewShellDocId nCurrentDocId(nDocId);
+ std::size_t n = 0;
+ SfxViewShell* pViewShell = SfxViewShell::GetFirst();
+ while (pViewShell)
+ {
+ if (pViewShell->GetDocId() == nCurrentDocId)
+ {
+ if (n == nSize)
+ return false;
+
+ pArray[n] = static_cast<sal_Int32>(pViewShell->GetViewShellId());
+ n++;
+ }
+
+ pViewShell = SfxViewShell::GetNext(*pViewShell);
+ }
+
+ return true;
+}
+
+int SfxLokHelper::getDocumentIdOfView(int nViewId)
+{
+ SfxViewShell* pViewShell = SfxViewShell::GetFirst();
+ while (pViewShell)
+ {
+ if (pViewShell->GetViewShellId() == ViewShellId(nViewId))
+ return static_cast<int>(pViewShell->GetDocId());
+ pViewShell = SfxViewShell::GetNext(*pViewShell);
+ }
+ return -1;
+}
+
+const LanguageTag & SfxLokHelper::getDefaultLanguage()
+{
+ return g_defaultLanguageTag;
+}
+
+void SfxLokHelper::setDefaultLanguage(const OUString& rBcp47LanguageTag)
+{
+ g_defaultLanguageTag = LanguageTag(rBcp47LanguageTag, true);
+}
+
+const LanguageTag& SfxLokHelper::getLoadLanguage() { return g_loadLanguageTag; }
+
+void SfxLokHelper::setLoadLanguage(const OUString& rBcp47LanguageTag)
+{
+ g_loadLanguageTag = LanguageTag(rBcp47LanguageTag, true);
+}
+
+void SfxLokHelper::setViewLanguage(int nId, const OUString& rBcp47LanguageTag)
+{
+ std::vector<SfxViewShell*>& rViewArr = SfxGetpApp()->GetViewShells_Impl();
+
+ for (SfxViewShell* pViewShell : rViewArr)
+ {
+ if (pViewShell->GetViewShellId() == ViewShellId(nId))
+ {
+ pViewShell->SetLOKLanguageTag(rBcp47LanguageTag);
+ return;
+ }
+ }
+}
+
+void SfxLokHelper::setAccessibilityState(int nId, bool nEnabled)
+{
+ std::vector<SfxViewShell*>& rViewArr = SfxGetpApp()->GetViewShells_Impl();
+
+ for (SfxViewShell* pViewShell : rViewArr)
+ {
+ if (pViewShell && pViewShell->GetViewShellId() == ViewShellId(nId))
+ {
+ LOK_INFO("lok.a11y", "SfxLokHelper::setAccessibilityState: view id: " << nId << ", nEnabled: " << nEnabled);
+ pViewShell->SetLOKAccessibilityState(nEnabled);
+ return;
+ }
+ }
+}
+
+void SfxLokHelper::setViewLocale(int nId, const OUString& rBcp47LanguageTag)
+{
+ std::vector<SfxViewShell*>& rViewArr = SfxGetpApp()->GetViewShells_Impl();
+
+ for (SfxViewShell* pViewShell : rViewArr)
+ {
+ if (pViewShell->GetViewShellId() == ViewShellId(nId))
+ {
+ pViewShell->SetLOKLocale(rBcp47LanguageTag);
+ return;
+ }
+ }
+}
+
+LOKDeviceFormFactor SfxLokHelper::getDeviceFormFactor()
+{
+ return g_deviceFormFactor;
+}
+
+void SfxLokHelper::setDeviceFormFactor(std::u16string_view rDeviceFormFactor)
+{
+ if (rDeviceFormFactor == u"desktop")
+ g_deviceFormFactor = LOKDeviceFormFactor::DESKTOP;
+ else if (rDeviceFormFactor == u"tablet")
+ g_deviceFormFactor = LOKDeviceFormFactor::TABLET;
+ else if (rDeviceFormFactor == u"mobile")
+ g_deviceFormFactor = LOKDeviceFormFactor::MOBILE;
+ else
+ g_deviceFormFactor = LOKDeviceFormFactor::UNKNOWN;
+}
+
+void SfxLokHelper::setDefaultTimezone(bool isSet, const OUString& rTimezone)
+{
+ g_isDefaultTimezoneSet = isSet;
+ g_DefaultTimezone = rTimezone;
+}
+
+std::pair<bool, OUString> SfxLokHelper::getDefaultTimezone()
+{
+ return { g_isDefaultTimezoneSet, g_DefaultTimezone };
+}
+
+void SfxLokHelper::setViewTimezone(int nId, bool isSet, const OUString& rTimezone)
+{
+ std::vector<SfxViewShell*>& rViewArr = SfxGetpApp()->GetViewShells_Impl();
+
+ for (SfxViewShell* pViewShell : rViewArr)
+ {
+ if (pViewShell->GetViewShellId() == ViewShellId(nId))
+ {
+ pViewShell->SetLOKTimezone(isSet, rTimezone);
+ return;
+ }
+ }
+}
+
+std::pair<bool, OUString> SfxLokHelper::getViewTimezone(int nId)
+{
+ std::vector<SfxViewShell*>& rViewArr = SfxGetpApp()->GetViewShells_Impl();
+
+ for (SfxViewShell* pViewShell : rViewArr)
+ {
+ if (pViewShell->GetViewShellId() == ViewShellId(nId))
+ {
+ return pViewShell->GetLOKTimezone();
+ }
+ }
+
+ return {};
+}
+
+/*
+* Used for putting a whole JSON string into a string value
+* e.g { key: "{JSON}" }
+*/
+static OString lcl_sanitizeJSONAsValue(const OString &rStr)
+{
+ if (rStr.getLength() < 1)
+ return rStr;
+ // FIXME: need an optimized 'escape' method for O[U]String.
+ OStringBuffer aBuf(rStr.getLength() + 8);
+ for (sal_Int32 i = 0; i < rStr.getLength(); ++i)
+ {
+ if (rStr[i] == '"' || rStr[i] == '\\')
+ aBuf.append('\\');
+
+ if (rStr[i] != '\n')
+ aBuf.append(rStr[i]);
+ }
+ return aBuf.makeStringAndClear();
+}
+
+static OString lcl_generateJSON(const SfxViewShell* pView, const boost::property_tree::ptree& rTree)
+{
+ assert(pView != nullptr && "pView must be valid");
+ boost::property_tree::ptree aMessageProps = rTree;
+ aMessageProps.put("viewId", SfxLokHelper::getView(pView));
+ aMessageProps.put("part", pView->getPart());
+ aMessageProps.put("mode", pView->getEditMode());
+ std::stringstream aStream;
+ boost::property_tree::write_json(aStream, aMessageProps, false /* pretty */);
+ return OString(o3tl::trim(aStream.str()));
+}
+
+static inline OString lcl_generateJSON(const SfxViewShell* pView, int nViewId, std::string_view rKey,
+ const OString& rPayload)
+{
+ assert(pView != nullptr && "pView must be valid");
+ return OString::Concat("{ \"viewId\": \"") + OString::number(nViewId)
+ + "\", \"part\": \"" + OString::number(pView->getPart()) + "\", \"mode\": \""
+ + OString::number(pView->getEditMode()) + "\", \"" + rKey + "\": \""
+ + lcl_sanitizeJSONAsValue(rPayload) + "\" }";
+}
+
+static inline OString lcl_generateJSON(const SfxViewShell* pView, std::string_view rKey,
+ const OString& rPayload)
+{
+ return lcl_generateJSON(pView, SfxLokHelper::getView(pView), rKey, rPayload);
+}
+
+void SfxLokHelper::notifyOtherView(const SfxViewShell* pThisView, SfxViewShell const* pOtherView,
+ int nType, std::string_view rKey, const OString& rPayload)
+{
+ assert(pThisView != nullptr && "pThisView must be valid");
+ if (DisableCallbacks::disabled())
+ return;
+
+ const OString aPayload = lcl_generateJSON(pThisView, rKey, rPayload);
+ const int viewId = SfxLokHelper::getView(pThisView);
+ pOtherView->libreOfficeKitViewCallbackWithViewId(nType, aPayload, viewId);
+}
+
+void SfxLokHelper::notifyOtherView(const SfxViewShell* pThisView, SfxViewShell const* pOtherView,
+ int nType, const boost::property_tree::ptree& rTree)
+{
+ assert(pThisView != nullptr && "pThisView must be valid");
+ if (DisableCallbacks::disabled() || !pOtherView)
+ return;
+
+ const int viewId = SfxLokHelper::getView(pThisView);
+ pOtherView->libreOfficeKitViewCallbackWithViewId(nType, lcl_generateJSON(pThisView, rTree), viewId);
+}
+
+void SfxLokHelper::notifyOtherViews(const SfxViewShell* pThisView, int nType, std::string_view rKey,
+ const OString& rPayload)
+{
+ assert(pThisView != nullptr && "pThisView must be valid");
+ if (DisableCallbacks::disabled())
+ return;
+
+ // Cache the payload so we only have to generate it once, at most.
+ OString aPayload;
+ int viewId = -1;
+
+ const ViewShellDocId nCurrentDocId = pThisView->GetDocId();
+ SfxViewShell* pViewShell = SfxViewShell::GetFirst();
+ while (pViewShell)
+ {
+ if (pViewShell != pThisView && nCurrentDocId == pViewShell->GetDocId())
+ {
+ // Payload is only dependent on pThisView.
+ if (aPayload.isEmpty())
+ {
+ aPayload = lcl_generateJSON(pThisView, rKey, rPayload);
+ viewId = SfxLokHelper::getView(pThisView);
+ }
+
+ pViewShell->libreOfficeKitViewCallbackWithViewId(nType, aPayload, viewId);
+ }
+
+ pViewShell = SfxViewShell::GetNext(*pViewShell);
+ }
+}
+
+void SfxLokHelper::notifyOtherViews(const SfxViewShell* pThisView, int nType,
+ const boost::property_tree::ptree& rTree)
+{
+ assert(pThisView != nullptr && "pThisView must be valid");
+ if (!pThisView || DisableCallbacks::disabled())
+ return;
+
+ // Cache the payload so we only have to generate it once, at most.
+ OString aPayload;
+ int viewId = -1;
+
+ const ViewShellDocId nCurrentDocId = pThisView->GetDocId();
+ SfxViewShell* pViewShell = SfxViewShell::GetFirst();
+ while (pViewShell)
+ {
+ if (pViewShell != pThisView && nCurrentDocId == pViewShell->GetDocId())
+ {
+ // Payload is only dependent on pThisView.
+ if (aPayload.isEmpty())
+ {
+ aPayload = lcl_generateJSON(pThisView, rTree);
+ viewId = SfxLokHelper::getView(pThisView);
+ }
+
+ pViewShell->libreOfficeKitViewCallbackWithViewId(nType, aPayload, viewId);
+ }
+
+ pViewShell = SfxViewShell::GetNext(*pViewShell);
+ }
+}
+
+OString SfxLokHelper::makePayloadJSON(const SfxViewShell* pThisView, int nViewId, std::string_view rKey, const OString& rPayload)
+{
+ return lcl_generateJSON(pThisView, nViewId, rKey, rPayload);
+}
+
+namespace {
+ OUString lcl_getNameForSlot(const SfxViewShell* pShell, sal_uInt16 nWhich)
+ {
+ if (pShell && pShell->GetFrame())
+ {
+ const SfxSlot* pSlot = SfxSlotPool::GetSlotPool(pShell->GetFrame()).GetSlot(nWhich);
+ if (pSlot)
+ {
+ if (!pSlot->GetUnoName().isEmpty())
+ {
+ return pSlot->GetCommand();
+ }
+ }
+ }
+
+ return "";
+ }
+}
+
+void SfxLokHelper::sendUnoStatus(const SfxViewShell* pShell, const SfxPoolItem* pItem)
+{
+ if (!pShell || !pItem || IsInvalidItem(pItem) || DisableCallbacks::disabled())
+ return;
+
+ boost::property_tree::ptree aItem = pItem->dumpAsJSON();
+
+ if (aItem.count("state"))
+ {
+ OUString sCommand = lcl_getNameForSlot(pShell, pItem->Which());
+ if (!sCommand.isEmpty())
+ aItem.put("commandName", sCommand);
+
+ std::stringstream aStream;
+ boost::property_tree::write_json(aStream, aItem);
+ pShell->libreOfficeKitViewCallback(LOK_CALLBACK_STATE_CHANGED, OString(aStream.str()));
+ }
+}
+
+void SfxLokHelper::notifyViewRenderState(const SfxViewShell* pShell, vcl::ITiledRenderable* pDoc)
+{
+ pShell->libreOfficeKitViewCallback(LOK_CALLBACK_VIEW_RENDER_STATE, pDoc->getViewRenderState());
+}
+
+void SfxLokHelper::notifyWindow(const SfxViewShell* pThisView,
+ vcl::LOKWindowId nLOKWindowId,
+ std::u16string_view rAction,
+ const std::vector<vcl::LOKPayloadItem>& rPayload)
+{
+ assert(pThisView != nullptr && "pThisView must be valid");
+
+ if (nLOKWindowId == 0 || DisableCallbacks::disabled())
+ return;
+
+ OStringBuffer aPayload =
+ "{ \"id\": \"" + OString::number(nLOKWindowId) + "\""
+ ", \"action\": \"" + OUStringToOString(rAction, RTL_TEXTENCODING_UTF8) + "\"";
+
+ for (const auto& rItem: rPayload)
+ {
+ if (!rItem.first.isEmpty() && !rItem.second.isEmpty())
+ {
+ auto aFirst = rItem.first.replaceAll("\""_ostr, "\\\""_ostr);
+ auto aSecond = rItem.second.replaceAll("\""_ostr, "\\\""_ostr);
+ aPayload.append(", \"" + aFirst + "\": \"" + aSecond + "\"");
+ }
+ }
+ aPayload.append('}');
+
+ const OString s = aPayload.makeStringAndClear();
+ pThisView->libreOfficeKitViewCallback(LOK_CALLBACK_WINDOW, s);
+}
+
+void SfxLokHelper::notifyInvalidation(SfxViewShell const* pThisView, tools::Rectangle const* pRect)
+{
+ // -1 means all parts
+ const int nPart = comphelper::LibreOfficeKit::isPartInInvalidation() ? pThisView->getPart() : INT_MIN;
+ SfxLokHelper::notifyInvalidation(pThisView, nPart, pRect);
+}
+
+void SfxLokHelper::notifyInvalidation(SfxViewShell const* pThisView, const int nInPart, tools::Rectangle const* pRect)
+{
+ if (DisableCallbacks::disabled())
+ return;
+
+ // -1 means all parts
+ const int nPart = comphelper::LibreOfficeKit::isPartInInvalidation() ? nInPart : INT_MIN;
+ const int nMode = pThisView->getEditMode();
+ pThisView->libreOfficeKitViewInvalidateTilesCallback(pRect, nPart, nMode);
+}
+
+void SfxLokHelper::notifyDocumentSizeChanged(SfxViewShell const* pThisView, const OString& rPayload, vcl::ITiledRenderable* pDoc, bool bInvalidateAll)
+{
+ if (!pDoc || pDoc->isDisposed() || DisableCallbacks::disabled())
+ return;
+
+ if (bInvalidateAll)
+ {
+ for (int i = 0; i < pDoc->getParts(); ++i)
+ {
+ tools::Rectangle aRectangle(0, 0, 1000000000, 1000000000);
+ const int nMode = pThisView->getEditMode();
+ pThisView->libreOfficeKitViewInvalidateTilesCallback(&aRectangle, i, nMode);
+ }
+ }
+ pThisView->libreOfficeKitViewCallback(LOK_CALLBACK_DOCUMENT_SIZE_CHANGED, rPayload);
+}
+
+void SfxLokHelper::notifyDocumentSizeChangedAllViews(vcl::ITiledRenderable* pDoc, bool bInvalidateAll)
+{
+ if (DisableCallbacks::disabled())
+ return;
+
+ // FIXME: Do we know whether it is the views for the document that is in the "current" view that has changed?
+ const SfxViewShell* const pCurrentViewShell = SfxViewShell::Current();
+ SfxViewShell* pViewShell = SfxViewShell::GetFirst();
+ while (pViewShell)
+ {
+ // FIXME: What if SfxViewShell::Current() returned null?
+ // Should we then do this for all views of all open documents
+ // or not?
+ if (pCurrentViewShell == nullptr || pViewShell->GetDocId() == pCurrentViewShell-> GetDocId())
+ {
+ SfxLokHelper::notifyDocumentSizeChanged(pViewShell, ""_ostr, pDoc, bInvalidateAll);
+ bInvalidateAll = false; // we direct invalidations to all views anyway.
+ }
+ pViewShell = SfxViewShell::GetNext(*pViewShell);
+ }
+}
+
+void SfxLokHelper::notifyPartSizeChangedAllViews(vcl::ITiledRenderable* pDoc, int nPart)
+{
+ if (DisableCallbacks::disabled())
+ return;
+
+ SfxViewShell* pViewShell = SfxViewShell::GetFirst();
+ while (pViewShell)
+ {
+ if (pViewShell->getPart() == nPart)
+ SfxLokHelper::notifyDocumentSizeChanged(pViewShell, ""_ostr, pDoc, false);
+ pViewShell = SfxViewShell::GetNext(*pViewShell);
+ }
+}
+
+OString SfxLokHelper::makeVisCursorInvalidation(int nViewId, const OString& rRectangle,
+ bool bMispelledWord, const OString& rHyperlink)
+{
+ if (comphelper::LibreOfficeKit::isViewIdForVisCursorInvalidation())
+ {
+ OString sHyperlink = rHyperlink.isEmpty() ? "{}"_ostr : rHyperlink;
+ return OString::Concat("{ \"viewId\": \"") + OString::number(nViewId) +
+ "\", \"rectangle\": \"" + rRectangle +
+ "\", \"mispelledWord\": \"" + OString::number(bMispelledWord ? 1 : 0) +
+ "\", \"hyperlink\": " + sHyperlink + " }";
+ }
+ else
+ {
+ return rRectangle;
+ }
+}
+
+void SfxLokHelper::notifyAllViews(int nType, const OString& rPayload)
+{
+ if (DisableCallbacks::disabled())
+ return;
+
+ const auto payload = rPayload.getStr();
+ const SfxViewShell* const pCurrentViewShell = SfxViewShell::Current();
+ if (!pCurrentViewShell)
+ return;
+ SfxViewShell* pViewShell = SfxViewShell::GetFirst();
+ while (pViewShell)
+ {
+ if (pViewShell->GetDocId() == pCurrentViewShell->GetDocId())
+ pViewShell->libreOfficeKitViewCallback(nType, payload);
+ pViewShell = SfxViewShell::GetNext(*pViewShell);
+ }
+}
+
+void SfxLokHelper::notifyContextChange(const css::ui::ContextChangeEventObject& rEvent)
+{
+ if (DisableCallbacks::disabled())
+ return;
+
+ SfxViewShell* pViewShell = SfxViewShell::Get({ rEvent.Source, css::uno::UNO_QUERY });
+ if (!pViewShell)
+ return;
+
+ OUString aBuffer =
+ rEvent.ApplicationName.replace(' ', '_') +
+ " " +
+ rEvent.ContextName.replace(' ', '_');
+ pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_CONTEXT_CHANGED, aBuffer.toUtf8());
+}
+
+void SfxLokHelper::notifyLog(const std::ostringstream& stream)
+{
+ if (DisableCallbacks::disabled())
+ return;
+
+ SfxViewShell* pViewShell = SfxViewShell::Current();
+ if (!pViewShell)
+ return;
+ if (pViewShell->getLibreOfficeKitViewCallback())
+ {
+ if (!g_logNotifierCache.empty())
+ {
+ for (const auto& msg : g_logNotifierCache)
+ {
+ pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_CORE_LOG, msg.c_str());
+ }
+ g_logNotifierCache.clear();
+ }
+ pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_CORE_LOG, stream.str().c_str());
+ }
+ else
+ {
+ while (g_logNotifierCache.size() >= g_logNotifierCacheMaxSize)
+ g_logNotifierCache.pop_front();
+ g_logNotifierCache.push_back(stream.str());
+ }
+}
+
+void SfxLokHelper::notifyUpdate(SfxViewShell const* pThisView, int nType)
+{
+ if (DisableCallbacks::disabled())
+ return;
+
+ pThisView->libreOfficeKitViewUpdatedCallback(nType);
+}
+
+void SfxLokHelper::notifyUpdatePerViewId(SfxViewShell const* pThisView, int nType)
+{
+ notifyUpdatePerViewId(pThisView, pThisView, pThisView, nType);
+}
+
+void SfxLokHelper::notifyUpdatePerViewId(SfxViewShell const* pTargetShell, SfxViewShell const* pViewShell,
+ SfxViewShell const* pSourceShell, int nType)
+{
+ if (DisableCallbacks::disabled())
+ return;
+
+ int viewId = SfxLokHelper::getView(pViewShell);
+ int sourceViewId = SfxLokHelper::getView(pSourceShell);
+ pTargetShell->libreOfficeKitViewUpdatedCallbackPerViewId(nType, viewId, sourceViewId);
+}
+
+void SfxLokHelper::notifyOtherViewsUpdatePerViewId(SfxViewShell const* pThisView, int nType)
+{
+ assert(pThisView != nullptr && "pThisView must be valid");
+ if (DisableCallbacks::disabled())
+ return;
+
+ int viewId = SfxLokHelper::getView(pThisView);
+ const ViewShellDocId nCurrentDocId = pThisView->GetDocId();
+ SfxViewShell* pViewShell = SfxViewShell::GetFirst();
+ while (pViewShell)
+ {
+ if (pViewShell != pThisView && nCurrentDocId == pViewShell->GetDocId())
+ pViewShell->libreOfficeKitViewUpdatedCallbackPerViewId(nType, viewId, viewId);
+
+ pViewShell = SfxViewShell::GetNext(*pViewShell);
+ }
+}
+
+namespace
+{
+ struct LOKAsyncEventData
+ {
+ int mnView; // Window is not enough.
+ VclPtr<vcl::Window> mpWindow;
+ VclEventId mnEvent;
+ MouseEvent maMouseEvent;
+ KeyEvent maKeyEvent;
+ OUString maText;
+ };
+
+ void LOKPostAsyncEvent(void* pEv, void*)
+ {
+ std::unique_ptr<LOKAsyncEventData> pLOKEv(static_cast<LOKAsyncEventData*>(pEv));
+ if (pLOKEv->mpWindow->isDisposed())
+ return;
+
+ int nView = SfxLokHelper::getView(nullptr);
+ if (nView != pLOKEv->mnView)
+ {
+ SAL_INFO("sfx.view", "LOK - view mismatch " << nView << " vs. " << pLOKEv->mnView);
+ SfxLokHelper::setView(pLOKEv->mnView);
+ }
+
+ if (!pLOKEv->mpWindow->HasChildPathFocus(true))
+ {
+ SAL_INFO("sfx.view", "LOK - focus mismatch, switching focus");
+ pLOKEv->mpWindow->GrabFocus();
+ }
+
+ VclPtr<vcl::Window> pFocusWindow = pLOKEv->mpWindow->GetFocusedWindow();
+ if (!pFocusWindow)
+ pFocusWindow = pLOKEv->mpWindow;
+
+ if (pLOKEv->mpWindow->isDisposed())
+ return;
+
+ switch (pLOKEv->mnEvent)
+ {
+ case VclEventId::WindowKeyInput:
+ {
+ sal_uInt16 nRepeat = pLOKEv->maKeyEvent.GetRepeat();
+ KeyEvent singlePress(pLOKEv->maKeyEvent.GetCharCode(),
+ pLOKEv->maKeyEvent.GetKeyCode());
+ for (sal_uInt16 i = 0; i <= nRepeat; ++i)
+ if (!pFocusWindow->isDisposed())
+ pFocusWindow->KeyInput(singlePress);
+ break;
+ }
+ case VclEventId::WindowKeyUp:
+ if (!pFocusWindow->isDisposed())
+ pFocusWindow->KeyUp(pLOKEv->maKeyEvent);
+ break;
+ case VclEventId::WindowMouseButtonDown:
+ pLOKEv->mpWindow->SetLastMousePos(pLOKEv->maMouseEvent.GetPosPixel());
+ pLOKEv->mpWindow->MouseButtonDown(pLOKEv->maMouseEvent);
+ // Invoke the context menu
+ if (pLOKEv->maMouseEvent.GetButtons() & MOUSE_RIGHT)
+ {
+ const CommandEvent aCEvt(pLOKEv->maMouseEvent.GetPosPixel(), CommandEventId::ContextMenu, true, nullptr);
+ pLOKEv->mpWindow->Command(aCEvt);
+ }
+ break;
+ case VclEventId::WindowMouseButtonUp:
+ pLOKEv->mpWindow->SetLastMousePos(pLOKEv->maMouseEvent.GetPosPixel());
+ pLOKEv->mpWindow->MouseButtonUp(pLOKEv->maMouseEvent);
+
+ // sometimes MouseButtonDown captures mouse and starts tracking, and VCL
+ // will not take care of releasing that with tiled rendering
+ if (pLOKEv->mpWindow->IsTracking())
+ pLOKEv->mpWindow->EndTracking();
+
+ break;
+ case VclEventId::WindowMouseMove:
+ pLOKEv->mpWindow->SetLastMousePos(pLOKEv->maMouseEvent.GetPosPixel());
+ pLOKEv->mpWindow->MouseMove(pLOKEv->maMouseEvent);
+ break;
+ case VclEventId::ExtTextInput:
+ case VclEventId::EndExtTextInput:
+ pLOKEv->mpWindow->PostExtTextInputEvent(pLOKEv->mnEvent, pLOKEv->maText);
+ break;
+ default:
+ assert(false);
+ break;
+ }
+ }
+
+ void postEventAsync(LOKAsyncEventData *pEvent)
+ {
+ if (!pEvent->mpWindow || pEvent->mpWindow->isDisposed())
+ {
+ SAL_WARN("vcl", "Async event post - but no valid window as destination " << pEvent->mpWindow.get());
+ delete pEvent;
+ return;
+ }
+
+ pEvent->mnView = SfxLokHelper::getView(nullptr);
+ if (vcl::lok::isUnipoll())
+ {
+ if (!Application::IsMainThread())
+ SAL_WARN("lok", "Posting event directly but not called from main thread!");
+ LOKPostAsyncEvent(pEvent, nullptr);
+ }
+ else
+ Application::PostUserEvent(Link<void*, void>(pEvent, LOKPostAsyncEvent));
+ }
+}
+
+void SfxLokHelper::postKeyEventAsync(const VclPtr<vcl::Window> &xWindow,
+ int nType, int nCharCode, int nKeyCode, int nRepeat)
+{
+ LOKAsyncEventData* pLOKEv = new LOKAsyncEventData;
+ switch (nType)
+ {
+ case LOK_KEYEVENT_KEYINPUT:
+ pLOKEv->mnEvent = VclEventId::WindowKeyInput;
+ break;
+ case LOK_KEYEVENT_KEYUP:
+ pLOKEv->mnEvent = VclEventId::WindowKeyUp;
+ break;
+ default:
+ assert(false);
+ }
+ pLOKEv->maKeyEvent = KeyEvent(nCharCode, nKeyCode, nRepeat);
+ pLOKEv->mpWindow = xWindow;
+ postEventAsync(pLOKEv);
+}
+
+void SfxLokHelper::setBlockedCommandList(int nViewId, const char* blockedCommandList)
+{
+ SfxViewShell* pViewShell = SfxLokHelper::getViewOfId(nViewId);
+
+ if(pViewShell)
+ {
+ pViewShell->setBlockedCommandList(blockedCommandList);
+ }
+}
+
+void SfxLokHelper::postExtTextEventAsync(const VclPtr<vcl::Window> &xWindow,
+ int nType, const OUString &rText)
+{
+ LOKAsyncEventData* pLOKEv = new LOKAsyncEventData;
+ switch (nType)
+ {
+ case LOK_EXT_TEXTINPUT:
+ pLOKEv->mnEvent = VclEventId::ExtTextInput;
+ pLOKEv->maText = rText;
+ break;
+ case LOK_EXT_TEXTINPUT_END:
+ pLOKEv->mnEvent = VclEventId::EndExtTextInput;
+ pLOKEv->maText = "";
+ break;
+ default:
+ assert(false);
+ }
+ pLOKEv->mpWindow = xWindow;
+ postEventAsync(pLOKEv);
+}
+
+void SfxLokHelper::postMouseEventAsync(const VclPtr<vcl::Window> &xWindow, LokMouseEventData const & rLokMouseEventData)
+{
+ LOKAsyncEventData* pLOKEv = new LOKAsyncEventData;
+ switch (rLokMouseEventData.mnType)
+ {
+ case LOK_MOUSEEVENT_MOUSEBUTTONDOWN:
+ pLOKEv->mnEvent = VclEventId::WindowMouseButtonDown;
+ break;
+ case LOK_MOUSEEVENT_MOUSEBUTTONUP:
+ pLOKEv->mnEvent = VclEventId::WindowMouseButtonUp;
+ break;
+ case LOK_MOUSEEVENT_MOUSEMOVE:
+ pLOKEv->mnEvent = VclEventId::WindowMouseMove;
+ break;
+ default:
+ assert(false);
+ }
+
+ // no reason - just always true so far.
+ assert (rLokMouseEventData.meModifiers == MouseEventModifiers::SIMPLECLICK);
+
+ pLOKEv->maMouseEvent = MouseEvent(rLokMouseEventData.maPosition, rLokMouseEventData.mnCount,
+ rLokMouseEventData.meModifiers, rLokMouseEventData.mnButtons,
+ rLokMouseEventData.mnModifier);
+ if (rLokMouseEventData.maLogicPosition)
+ {
+ pLOKEv->maMouseEvent.setLogicPosition(*rLokMouseEventData.maLogicPosition);
+ }
+ pLOKEv->mpWindow = xWindow;
+ postEventAsync(pLOKEv);
+}
+
+void SfxLokHelper::dumpState(rtl::OStringBuffer &rState)
+{
+ SfxViewShell* pShell = SfxViewShell::Current();
+ sal_Int32 nDocId = pShell ? static_cast<sal_Int32>(pShell->GetDocId().get()) : -1;
+
+ rState.append("\n\tDocId:\t");
+ rState.append(nDocId);
+
+ if (nDocId < 0)
+ return;
+
+ rState.append("\n\tViewCount:\t");
+ rState.append(static_cast<sal_Int32>(getViewsCount(nDocId)));
+
+ const SfxViewShell* const pCurrentViewShell = SfxViewShell::Current();
+ SfxViewShell* pViewShell = SfxViewShell::GetFirst();
+ while (pViewShell)
+ {
+ if (pCurrentViewShell == nullptr || pViewShell->GetDocId() == pCurrentViewShell-> GetDocId())
+ pViewShell->dumpLibreOfficeKitViewState(rState);
+
+ pViewShell = SfxViewShell::GetNext(*pViewShell);
+ }
+}
+
+bool SfxLokHelper::testInPlaceComponentMouseEventHit(SfxViewShell* pViewShell, int nType, int nX,
+ int nY, int nCount, int nButtons,
+ int nModifier, double fScaleX, double fScaleY,
+ bool bNegativeX)
+{
+ // In LOK RTL mode draw/svx operates in negative X coordinates
+ // But the coordinates from client is always positive, so negate nX.
+ if (bNegativeX)
+ nX = -nX;
+
+ // check if the user hit a chart/math object which is being edited by this view
+ if (LokChartHelper aChartHelper(pViewShell, bNegativeX);
+ aChartHelper.postMouseEvent(nType, nX, nY, nCount, nButtons, nModifier, fScaleX, fScaleY))
+ return true;
+
+ if (LokStarMathHelper aMathHelper(pViewShell);
+ aMathHelper.postMouseEvent(nType, nX, nY, nCount, nButtons, nModifier, fScaleX, fScaleY))
+ return true;
+
+ // check if the user hit a chart which is being edited by someone else
+ // and, if so, skip current mouse event
+ if (nType != LOK_MOUSEEVENT_MOUSEMOVE)
+ {
+ if (LokChartHelper::HitAny({nX, nY}, bNegativeX))
+ return true;
+ }
+
+ return false;
+}
+
+VclPtr<vcl::Window> SfxLokHelper::getInPlaceDocWindow(SfxViewShell* pViewShell)
+{
+ if (VclPtr<vcl::Window> pWindow = LokChartHelper(pViewShell).GetWindow())
+ return pWindow;
+ if (VclPtr<vcl::Window> pWindow = LokStarMathHelper(pViewShell).GetWidgetWindow())
+ return pWindow;
+ return {};
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/view/lokstarmathhelper.cxx b/sfx2/source/view/lokstarmathhelper.cxx
new file mode 100644
index 0000000000..9b2df19ecd
--- /dev/null
+++ b/sfx2/source/view/lokstarmathhelper.cxx
@@ -0,0 +1,247 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <sal/config.h>
+
+#include <sfx2/ipclient.hxx>
+#include <sfx2/lokcomponenthelpers.hxx>
+#include <sfx2/lokhelper.hxx>
+#include <sfx2/objsh.hxx>
+
+#include <comphelper/dispatchcommand.hxx>
+#include <comphelper/lok.hxx>
+#include <toolkit/helper/vclunohelper.hxx>
+#include <tools/fract.hxx>
+#include <tools/UnitConversion.hxx>
+#include <vcl/layout.hxx>
+#include <vcl/virdev.hxx>
+#include <vcl/window.hxx>
+
+#include <com/sun/star/embed/XEmbeddedObject.hpp>
+#include <com/sun/star/frame/XModel.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+
+LokStarMathHelper::LokStarMathHelper(const SfxViewShell* pViewShell)
+ : mpViewShell(pViewShell)
+{
+ if (mpViewShell)
+ {
+ if (const SfxInPlaceClient* pIPClient = mpViewShell->GetIPClient())
+ {
+ if (const auto& xEmbObj = pIPClient->GetObject())
+ {
+ css::uno::Reference<css::lang::XServiceInfo> xComp(xEmbObj->getComponent(),
+ css::uno::UNO_QUERY);
+ if (xComp && xComp->supportsService("com.sun.star.formula.FormulaProperties"))
+ {
+ if (css::uno::Reference<css::frame::XModel> xModel{ xComp,
+ css::uno::UNO_QUERY })
+ {
+ if (auto xController = xModel->getCurrentController())
+ {
+ mpIPClient = pIPClient;
+ mxFrame = xController->getFrame();
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+void LokStarMathHelper::Dispatch(
+ const OUString& cmd, const css::uno::Sequence<css::beans::PropertyValue>& rArguments) const
+{
+ if (mxFrame)
+ comphelper::dispatchCommand(cmd, mxFrame, rArguments);
+}
+
+namespace
+{
+// Find a child SmGraphicWindow*
+vcl::Window* FindSmGraphicWindow(vcl::Window* pWin)
+{
+ if (!pWin)
+ return nullptr;
+
+ if (pWin->IsStarMath())
+ return pWin;
+
+ pWin = pWin->GetWindow(GetWindowType::FirstChild);
+ while (pWin)
+ {
+ if (vcl::Window* pSmGraphicWindow = FindSmGraphicWindow(pWin))
+ return pSmGraphicWindow;
+ pWin = pWin->GetWindow(GetWindowType::Next);
+ }
+ return nullptr;
+}
+
+// Find a child window that corresponds to SmGraphicWidget
+vcl::Window* FindChildSmGraphicWidgetWindow(vcl::Window* pWin)
+{
+ if (!pWin)
+ return nullptr;
+
+ // The needed window is a VclDrawingArea
+ if (dynamic_cast<VclDrawingArea*>(pWin))
+ return pWin;
+
+ pWin = pWin->GetWindow(GetWindowType::FirstChild);
+ while (pWin)
+ {
+ if (vcl::Window* pSmGraphicWidgetWindow = FindChildSmGraphicWidgetWindow(pWin))
+ return pSmGraphicWidgetWindow;
+ pWin = pWin->GetWindow(GetWindowType::Next);
+ }
+ return nullptr;
+}
+}
+
+vcl::Window* LokStarMathHelper::GetGraphicWindow()
+{
+ if (!mpGraphicWindow)
+ {
+ if (mxFrame)
+ {
+ css::uno::Reference<css::awt::XWindow> xDockerWin = mxFrame->getContainerWindow();
+ mpGraphicWindow.set(FindSmGraphicWindow(VCLUnoHelper::GetWindow(xDockerWin)));
+ }
+ }
+
+ return mpGraphicWindow.get();
+}
+
+vcl::Window* LokStarMathHelper::GetWidgetWindow()
+{
+ if (!mpWidgetWindow)
+ mpWidgetWindow.set(FindChildSmGraphicWidgetWindow(GetGraphicWindow()));
+
+ return mpWidgetWindow.get();
+}
+
+const SfxViewShell* LokStarMathHelper::GetSmViewShell()
+{
+ if (vcl::Window* pGraphWindow = GetGraphicWindow())
+ {
+ return SfxViewShell::GetFirst(false, [pGraphWindow](const SfxViewShell* shell) {
+ return shell->GetWindow() && shell->GetWindow()->IsChild(pGraphWindow);
+ });
+ }
+ return nullptr;
+}
+
+tools::Rectangle LokStarMathHelper::GetBoundingBox() const
+{
+ if (mpIPClient)
+ {
+ tools::Rectangle r(mpIPClient->GetObjArea());
+ if (SfxObjectShell* pObjShell = const_cast<SfxViewShell*>(mpViewShell)->GetObjectShell())
+ {
+ const o3tl::Length unit = MapToO3tlLength(pObjShell->GetMapUnit());
+ if (unit != o3tl::Length::twip && unit != o3tl::Length::invalid)
+ r = o3tl::convert(r, unit, o3tl::Length::twip);
+ }
+ return r;
+ }
+ return {};
+}
+
+bool LokStarMathHelper::postMouseEvent(int nType, int nX, int nY, int nCount, int nButtons,
+ int nModifier, double fPPTScaleX, double fPPTScaleY)
+{
+ const tools::Rectangle rBBox = GetBoundingBox();
+ if (Point aMousePos(nX, nY); rBBox.Contains(aMousePos))
+ {
+ if (vcl::Window* pWindow = GetWidgetWindow())
+ {
+ aMousePos -= rBBox.TopLeft();
+
+ // In lok, Math does not convert coordinates (see SmGraphicWidget::SetDrawingArea,
+ // which disables MapMode), and uses twips internally (see SmDocShell ctor and
+ // SmMapUnit), but the conversion factor can depend on the client zoom.
+ // 1. Remove the twip->pixel factor in the passed scales
+ double fScaleX = o3tl::convert(fPPTScaleX, o3tl::Length::px, o3tl::Length::twip);
+ double fScaleY = o3tl::convert(fPPTScaleY, o3tl::Length::px, o3tl::Length::twip);
+ // 2. Adjust the position according to the scales
+ aMousePos
+ = Point(std::round(aMousePos.X() * fScaleX), std::round(aMousePos.Y() * fScaleY));
+ // 3. Take window own scaling into account (reverses the conversion done in
+ // SmGraphicWidget::MouseButtonDown, albeit incompletely - it does not handle
+ // GetFormulaDrawPos; hopefully, in lok/in-place case, it's always [ 0, 0 ]?)
+ aMousePos = pWindow->LogicToPixel(aMousePos);
+
+ LokMouseEventData aMouseEventData(
+ nType, aMousePos, nCount, MouseEventModifiers::SIMPLECLICK, nButtons, nModifier);
+ SfxLokHelper::postMouseEventAsync(pWindow, aMouseEventData);
+
+ return true;
+ }
+ }
+ return false;
+}
+
+void LokStarMathHelper::PaintTile(VirtualDevice& rDevice, const tools::Rectangle& rTileRect)
+{
+ const tools::Rectangle aMathRect = GetBoundingBox();
+ if (rTileRect.GetIntersection(aMathRect).IsEmpty())
+ return;
+
+ vcl::Window* pWidgetWindow = GetWidgetWindow();
+ if (!pWidgetWindow)
+ return;
+
+ Point aOffset(aMathRect.Left() - rTileRect.Left(), aMathRect.Top() - rTileRect.Top());
+
+ MapMode newMode = rDevice.GetMapMode();
+ newMode.SetOrigin(aOffset);
+ rDevice.SetMapMode(newMode); // Push/Pop is done in PaintAllInPlaceOnTile
+
+ pWidgetWindow->Paint(rDevice, {}); // SmGraphicWidget::Paint does not use the passed rectangle
+}
+
+void LokStarMathHelper::PaintAllInPlaceOnTile(VirtualDevice& rDevice, int nOutputWidth,
+ int nOutputHeight, int nTilePosX, int nTilePosY,
+ tools::Long nTileWidth, tools::Long nTileHeight)
+{
+ if (comphelper::LibreOfficeKit::isTiledAnnotations())
+ return;
+
+ SfxViewShell* pCurView = SfxViewShell::Current();
+ if (!pCurView)
+ return;
+ const ViewShellDocId nDocId = pCurView->GetDocId();
+ const int nPartForCurView = pCurView->getPart();
+
+ // Resizes the virtual device to contain the entries context
+ rDevice.SetOutputSizePixel({ nOutputWidth, nOutputHeight });
+
+ rDevice.Push(vcl::PushFlags::MAPMODE);
+ MapMode aMapMode(rDevice.GetMapMode());
+
+ // Scaling. Must convert from pixels to twips. We know that VirtualDevices use a DPI of 96.
+ const Fraction scale = conversionFract(o3tl::Length::px, o3tl::Length::twip);
+ const Fraction scaleX = Fraction(nOutputWidth, nTileWidth) * scale;
+ const Fraction scaleY = Fraction(nOutputHeight, nTileHeight) * scale;
+ aMapMode.SetScaleX(scaleX);
+ aMapMode.SetScaleY(scaleY);
+ aMapMode.SetMapUnit(MapUnit::MapTwip);
+ rDevice.SetMapMode(aMapMode);
+
+ const tools::Rectangle aTileRect(Point(nTilePosX, nTilePosY), Size(nTileWidth, nTileHeight));
+
+ for (SfxViewShell* pViewShell = SfxViewShell::GetFirst(); pViewShell;
+ pViewShell = SfxViewShell::GetNext(*pViewShell))
+ if (pViewShell->GetDocId() == nDocId && pViewShell->getPart() == nPartForCurView)
+ LokStarMathHelper(pViewShell).PaintTile(rDevice, aTileRect);
+
+ rDevice.Pop();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/sfx2/source/view/printer.cxx b/sfx2/source/view/printer.cxx
new file mode 100644
index 0000000000..7b77453495
--- /dev/null
+++ b/sfx2/source/view/printer.cxx
@@ -0,0 +1,189 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <tools/debug.hxx>
+
+#include <utility>
+
+#include <sfx2/printer.hxx>
+#include <sfx2/viewsh.hxx>
+#include <sfx2/tabdlg.hxx>
+#include "prnmon.hxx"
+
+// class SfxPrinter ------------------------------------------------------
+
+VclPtr<SfxPrinter> SfxPrinter::Create( SvStream& rStream, std::unique_ptr<SfxItemSet>&& pOptions )
+
+/* [Description]
+
+ Creates a <SfxPrinter> from the stream. Loading is really only a jobsetup.
+ If such a printer is not available on the system, then the original is
+ marked as the original Job-setup and a comparable printer is selected from
+ existing ones.
+
+ The 'pOptions' are taken over in the generated SfxPrinter, the return
+ value belongs to the caller.
+*/
+
+{
+ // Load JobSetup
+ JobSetup aFileJobSetup;
+ ReadJobSetup( rStream, aFileJobSetup );
+
+ // Get printers
+ VclPtr<SfxPrinter> pPrinter = VclPtr<SfxPrinter>::Create( std::move(pOptions), aFileJobSetup );
+ return pPrinter;
+}
+
+
+void SfxPrinter::Store( SvStream& rStream ) const
+
+/* [Description]
+
+ Saves the used JobSetup of <SfxPrinter>s.
+*/
+
+{
+ WriteJobSetup( rStream, GetJobSetup() );
+}
+
+
+SfxPrinter::SfxPrinter( std::unique_ptr<SfxItemSet>&& pTheOptions ) :
+
+/* [Description]
+
+ This constructor creates a default printer.
+*/
+ pOptions( std::move(pTheOptions) ),
+ bKnown( true )
+{
+ assert(pOptions);
+}
+
+
+SfxPrinter::SfxPrinter( std::unique_ptr<SfxItemSet>&& pTheOptions,
+ const JobSetup& rTheOrigJobSetup ) :
+ Printer( rTheOrigJobSetup.GetPrinterName() ),
+ pOptions( std::move(pTheOptions) )
+{
+ assert(pOptions);
+ bKnown = GetName() == rTheOrigJobSetup.GetPrinterName();
+
+ if ( bKnown )
+ SetJobSetup( rTheOrigJobSetup );
+}
+
+
+SfxPrinter::SfxPrinter( std::unique_ptr<SfxItemSet>&& pTheOptions,
+ const OUString& rPrinterName ) :
+ Printer( rPrinterName ),
+ pOptions( std::move(pTheOptions) ),
+ bKnown( GetName() == rPrinterName )
+{
+ assert(pOptions);
+}
+
+
+SfxPrinter::SfxPrinter( const SfxPrinter& rPrinter ) :
+ VclReferenceBase(),
+ Printer( rPrinter.GetName() ),
+ pOptions( rPrinter.GetOptions().Clone() ),
+ bKnown( rPrinter.IsKnown() )
+{
+ assert(pOptions);
+ SetJobSetup( rPrinter.GetJobSetup() );
+ SetPrinterProps( &rPrinter );
+ SetMapMode( rPrinter.GetMapMode() );
+}
+
+
+VclPtr<SfxPrinter> SfxPrinter::Clone() const
+{
+ if ( IsDefPrinter() )
+ {
+ VclPtr<SfxPrinter> pNewPrinter = VclPtr<SfxPrinter>::Create( GetOptions().Clone() );
+ pNewPrinter->SetJobSetup( GetJobSetup() );
+ pNewPrinter->SetPrinterProps( this );
+ pNewPrinter->SetMapMode( GetMapMode() );
+ return pNewPrinter;
+ }
+ else
+ return VclPtr<SfxPrinter>::Create( *this );
+}
+
+
+SfxPrinter::~SfxPrinter()
+{
+ disposeOnce();
+}
+
+void SfxPrinter::dispose()
+{
+ pOptions.reset();
+ Printer::dispose();
+}
+
+
+void SfxPrinter::SetOptions( const SfxItemSet &rNewOptions )
+{
+ pOptions->Set(rNewOptions);
+}
+
+
+SfxPrintOptionsDialog::SfxPrintOptionsDialog(weld::Window *pParent,
+ SfxViewShell *pViewShell,
+ const SfxItemSet *pSet)
+ : GenericDialogController(pParent, "sfx/ui/printeroptionsdialog.ui", "PrinterOptionsDialog")
+ , pOptions(pSet->Clone())
+ , m_xHelpBtn(m_xBuilder->weld_widget("help"))
+ , m_xContainer(m_xDialog->weld_content_area())
+ , m_xPage(pViewShell->CreatePrintOptionsPage(m_xContainer.get(), this, *pOptions)) // Insert TabPage
+{
+ DBG_ASSERT( m_xPage, "CreatePrintOptions != SFX_VIEW_HAS_PRINTOPTIONS" );
+ if (m_xPage)
+ {
+ m_xPage->Reset( pOptions.get() );
+ m_xDialog->set_help_id(m_xPage->GetHelpId());
+ }
+}
+
+SfxPrintOptionsDialog::~SfxPrintOptionsDialog()
+{
+}
+
+short SfxPrintOptionsDialog::run()
+{
+ if (!m_xPage)
+ return RET_CANCEL;
+
+ short nRet = GenericDialogController::run();
+
+ if (nRet == RET_OK)
+ m_xPage->FillItemSet( pOptions.get() );
+ else
+ m_xPage->Reset( pOptions.get() );
+ return nRet;
+}
+
+void SfxPrintOptionsDialog::DisableHelp()
+{
+ m_xHelpBtn->set_sensitive(false);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/view/prnmon.hxx b/sfx2/source/view/prnmon.hxx
new file mode 100644
index 0000000000..06b3217fa9
--- /dev/null
+++ b/sfx2/source/view/prnmon.hxx
@@ -0,0 +1,54 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#ifndef INCLUDED_SFX2_PRNMON_HXX
+#define INCLUDED_SFX2_PRNMON_HXX
+
+#include <memory>
+#include <sal/config.h>
+#include <vcl/weld.hxx>
+
+
+class SfxViewShell;
+class SfxTabPage;
+class SfxItemSet;
+
+
+class SfxPrintOptionsDialog final : public weld::GenericDialogController
+{
+private:
+ std::unique_ptr<SfxItemSet> pOptions;
+ std::unique_ptr<weld::Widget> m_xHelpBtn;
+ std::unique_ptr<weld::Container> m_xContainer;
+ std::unique_ptr<SfxTabPage> m_xPage;
+
+public:
+ SfxPrintOptionsDialog(weld::Window *pParent,
+ SfxViewShell *pViewShell,
+ const SfxItemSet *rOptions);
+ virtual ~SfxPrintOptionsDialog() override;
+
+ virtual short run() override;
+
+ const SfxItemSet& GetOptions() const { return *pOptions; }
+ void DisableHelp();
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/view/sfxbasecontroller.cxx b/sfx2/source/view/sfxbasecontroller.cxx
new file mode 100644
index 0000000000..e9cd1f4642
--- /dev/null
+++ b/sfx2/source/view/sfxbasecontroller.cxx
@@ -0,0 +1,1498 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <time.h>
+#include <sfx2/sfxbasecontroller.hxx>
+#include <com/sun/star/awt/XVclWindowPeer.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/util/XCloseable.hpp>
+#include <com/sun/star/util/XCloseBroadcaster.hpp>
+#include <com/sun/star/util/XCloseListener.hpp>
+#include <com/sun/star/util/CloseVetoException.hpp>
+#include <com/sun/star/document/XCmisDocument.hpp>
+#include <com/sun/star/document/XViewDataSupplier.hpp>
+#include <cppuhelper/implbase.hxx>
+#include <com/sun/star/frame/FrameActionEvent.hpp>
+#include <com/sun/star/frame/FrameAction.hpp>
+#include <com/sun/star/frame/FrameSearchFlag.hpp>
+#include <com/sun/star/frame/CommandGroup.hpp>
+#include <com/sun/star/frame/XFrame.hpp>
+#include <com/sun/star/frame/XBorderResizeListener.hpp>
+#include <com/sun/star/frame/XUntitledNumbers.hpp>
+#include <com/sun/star/lang/DisposedException.hpp>
+#include <com/sun/star/lang/EventObject.hpp>
+#include <com/sun/star/lang/XEventListener.hpp>
+#include <com/sun/star/lang/XComponent.hpp>
+#include <com/sun/star/container/XIndexAccess.hpp>
+#include <comphelper/interfacecontainer3.hxx>
+#include <comphelper/multicontainer2.hxx>
+#include <sfx2/viewsh.hxx>
+#include <sfx2/docfac.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <sfx2/objsh.hxx>
+#include <sfx2/app.hxx>
+#include <sfx2/msgpool.hxx>
+#include <sfx2/dispatch.hxx>
+#include <sfx2/userinputinterception.hxx>
+
+#include <unoctitm.hxx>
+#include <sfx2/childwin.hxx>
+#include <sfx2/sfxsids.hrc>
+#include <sfx2/sfxresid.hxx>
+#include <workwin.hxx>
+#include <sfx2/infobar.hxx>
+
+#include <osl/mutex.hxx>
+#include <comphelper/diagnose_ex.hxx>
+#include <comphelper/namedvaluecollection.hxx>
+#include <comphelper/sequence.hxx>
+#include <toolkit/helper/convert.hxx>
+#include <framework/titlehelper.hxx>
+#include <comphelper/processfactory.hxx>
+#include <vcl/svapp.hxx>
+#include <tools/svborder.hxx>
+
+#include <sfx2/event.hxx>
+#include <sfx2/viewfac.hxx>
+#include <sfx2/strings.hrc>
+#include <sfxbasecontroller_internal.hxx>
+
+#include <unordered_map>
+
+#include <com/sun/star/ui/XSidebarProvider.hpp>
+#include <sidebar/UnoSidebar.hxx>
+
+#define TIMEOUT_START_RESCHEDULE 10L /* 10th s */
+
+using namespace ::com::sun::star;
+using ::com::sun::star::uno::Reference;
+using ::com::sun::star::uno::RuntimeException;
+using ::com::sun::star::uno::UNO_QUERY_THROW;
+using ::com::sun::star::lang::DisposedException;
+using ::com::sun::star::awt::XWindow;
+using ::com::sun::star::frame::XController;
+using ::com::sun::star::frame::XDispatchProvider;
+using ::com::sun::star::document::XViewDataSupplier;
+using ::com::sun::star::container::XIndexAccess;
+using ::com::sun::star::beans::PropertyValue;
+using ::com::sun::star::beans::StringPair;
+using ::com::sun::star::uno::Sequence;
+using ::com::sun::star::uno::UNO_QUERY;
+using ::com::sun::star::uno::Exception;
+using ::com::sun::star::frame::XFrame;
+using ::com::sun::star::frame::XFrameActionListener;
+using ::com::sun::star::util::XCloseListener;
+using ::com::sun::star::task::XStatusIndicator;
+using ::com::sun::star::frame::XTitle;
+using ::com::sun::star::ui::XSidebarProvider;
+
+
+typedef std::unordered_map< SfxGroupId, sal_Int16 > GroupHashMap;
+
+sal_Int16 MapGroupIDToCommandGroup( SfxGroupId nGroupID )
+{
+ static GroupHashMap s_aHashMap
+ {
+ { SfxGroupId::Intern , frame::CommandGroup::INTERNAL },
+ { SfxGroupId::Application , frame::CommandGroup::APPLICATION },
+ { SfxGroupId::Document , frame::CommandGroup::DOCUMENT },
+ { SfxGroupId::View , frame::CommandGroup::VIEW },
+ { SfxGroupId::Edit , frame::CommandGroup::EDIT },
+ { SfxGroupId::Macro , frame::CommandGroup::MACRO },
+ { SfxGroupId::Options , frame::CommandGroup::OPTIONS },
+ { SfxGroupId::Math , frame::CommandGroup::MATH },
+ { SfxGroupId::Navigator , frame::CommandGroup::NAVIGATOR },
+ { SfxGroupId::Insert , frame::CommandGroup::INSERT },
+ { SfxGroupId::Format , frame::CommandGroup::FORMAT },
+ { SfxGroupId::Template , frame::CommandGroup::TEMPLATE },
+ { SfxGroupId::Text , frame::CommandGroup::TEXT },
+ { SfxGroupId::Frame , frame::CommandGroup::FRAME },
+ { SfxGroupId::Graphic , frame::CommandGroup::GRAPHIC },
+ { SfxGroupId::Table , frame::CommandGroup::TABLE },
+ { SfxGroupId::Enumeration , frame::CommandGroup::ENUMERATION },
+ { SfxGroupId::Data , frame::CommandGroup::DATA },
+ { SfxGroupId::Special , frame::CommandGroup::SPECIAL },
+ { SfxGroupId::Image , frame::CommandGroup::IMAGE },
+ { SfxGroupId::Chart , frame::CommandGroup::CHART },
+ { SfxGroupId::Explorer , frame::CommandGroup::EXPLORER },
+ { SfxGroupId::Connector , frame::CommandGroup::CONNECTOR },
+ { SfxGroupId::Modify , frame::CommandGroup::MODIFY },
+ { SfxGroupId::Drawing , frame::CommandGroup::DRAWING },
+ { SfxGroupId::Controls , frame::CommandGroup::CONTROLS },
+ };
+
+
+ GroupHashMap::const_iterator pIter = s_aHashMap.find( nGroupID );
+ if ( pIter != s_aHashMap.end() )
+ return pIter->second;
+ else
+ return frame::CommandGroup::INTERNAL;
+}
+
+sal_uInt32 Get10ThSec()
+{
+ sal_uInt32 n10Ticks = 10 * static_cast<sal_uInt32>(clock());
+ return n10Ticks / CLOCKS_PER_SEC;
+}
+
+static sal_Int32 m_nInReschedule = 0; /// static counter for rescheduling
+
+static void reschedule()
+{
+ if ( m_nInReschedule == 0 )
+ {
+ ++m_nInReschedule;
+ Application::Reschedule();
+ --m_nInReschedule;
+ }
+}
+
+namespace {
+
+class SfxStatusIndicator : public ::cppu::WeakImplHelper< task::XStatusIndicator, lang::XEventListener >
+{
+ Reference < XController > xOwner;
+ Reference < task::XStatusIndicator > xProgress;
+ SfxWorkWindow* pWorkWindow;
+ tools::Long _nStartTime;
+public:
+ SfxStatusIndicator(SfxBaseController* pController, SfxWorkWindow* pWork)
+ : xOwner( pController )
+ , pWorkWindow( pWork )
+ , _nStartTime(0)
+ {
+ osl_atomic_increment(&m_refCount);
+ Reference< lang::XComponent > xComponent = pController;
+ if (xComponent.is())
+ xComponent->addEventListener(this);
+ osl_atomic_decrement(&m_refCount);
+ }
+
+ virtual void SAL_CALL start(const OUString& aText, sal_Int32 nRange) override;
+ virtual void SAL_CALL end() override;
+ virtual void SAL_CALL setText(const OUString& aText) override;
+ virtual void SAL_CALL setValue(sal_Int32 nValue) override;
+ virtual void SAL_CALL reset() override;
+
+ virtual void SAL_CALL disposing( const lang::EventObject& Source ) override;
+};
+
+}
+
+void SAL_CALL SfxStatusIndicator::start(const OUString& aText, sal_Int32 nRange)
+{
+ SolarMutexGuard aGuard;
+ if ( xOwner.is() )
+ {
+ if ( !xProgress.is() )
+ xProgress = pWorkWindow->GetStatusIndicator();
+
+ if ( xProgress.is() )
+ xProgress->start( aText, nRange );
+
+ _nStartTime = Get10ThSec();
+ reschedule();
+ }
+}
+
+void SAL_CALL SfxStatusIndicator::end()
+{
+ SolarMutexGuard aGuard;
+ if ( xOwner.is() )
+ {
+ if ( !xProgress.is() )
+ xProgress = pWorkWindow->GetStatusIndicator();
+
+ if ( xProgress.is() )
+ xProgress->end();
+
+ reschedule();
+ }
+}
+
+void SAL_CALL SfxStatusIndicator::setText(const OUString& aText)
+{
+ SolarMutexGuard aGuard;
+ if ( xOwner.is() )
+ {
+ if ( !xProgress.is() )
+ xProgress = pWorkWindow->GetStatusIndicator();
+
+ if ( xProgress.is() )
+ xProgress->setText( aText );
+
+ reschedule();
+ }
+}
+
+void SAL_CALL SfxStatusIndicator::setValue( sal_Int32 nValue )
+{
+ SolarMutexGuard aGuard;
+ if ( xOwner.is() )
+ {
+ if ( !xProgress.is() )
+ xProgress = pWorkWindow->GetStatusIndicator();
+
+ if ( xProgress.is() )
+ xProgress->setValue( nValue );
+
+ bool bReschedule = (( Get10ThSec() - _nStartTime ) > TIMEOUT_START_RESCHEDULE );
+ if ( bReschedule )
+ reschedule();
+ }
+}
+
+void SAL_CALL SfxStatusIndicator::reset()
+{
+ SolarMutexGuard aGuard;
+ if ( xOwner.is() )
+ {
+ if ( !xProgress.is() )
+ xProgress = pWorkWindow->GetStatusIndicator();
+
+ if ( xProgress.is() )
+ xProgress->reset();
+
+ reschedule();
+ }
+}
+
+void SAL_CALL SfxStatusIndicator::disposing( const lang::EventObject& /*Source*/ )
+{
+ SolarMutexGuard aGuard;
+ xOwner = nullptr;
+ xProgress.clear();
+}
+
+
+// declaration IMPL_SfxBaseController_ListenerHelper
+
+namespace {
+
+class IMPL_SfxBaseController_ListenerHelper : public ::cppu::WeakImplHelper< frame::XFrameActionListener >
+{
+public:
+ explicit IMPL_SfxBaseController_ListenerHelper( SfxBaseController* pController ) ;
+
+ virtual void SAL_CALL frameAction( const frame::FrameActionEvent& aEvent ) override ;
+ virtual void SAL_CALL disposing( const lang::EventObject& aEvent ) override ;
+
+private:
+
+ SfxBaseController* m_pController ;
+
+} ; // class IMPL_SfxBaseController_ListenerContainer
+
+class IMPL_SfxBaseController_CloseListenerHelper : public ::cppu::WeakImplHelper< util::XCloseListener >
+{
+public:
+ explicit IMPL_SfxBaseController_CloseListenerHelper( SfxBaseController* pController ) ;
+
+ virtual void SAL_CALL queryClosing( const lang::EventObject& aEvent, sal_Bool bDeliverOwnership ) override ;
+ virtual void SAL_CALL notifyClosing( const lang::EventObject& aEvent ) override ;
+ virtual void SAL_CALL disposing( const lang::EventObject& aEvent ) override ;
+
+private:
+
+ SfxBaseController* m_pController;
+
+} ; // class IMPL_SfxBaseController_ListenerContainer
+
+}
+
+IMPL_SfxBaseController_CloseListenerHelper::IMPL_SfxBaseController_CloseListenerHelper( SfxBaseController* pController )
+ : m_pController ( pController )
+{
+}
+
+void SAL_CALL IMPL_SfxBaseController_CloseListenerHelper::disposing( const lang::EventObject& /*aEvent*/ )
+{
+}
+
+void SAL_CALL IMPL_SfxBaseController_CloseListenerHelper::queryClosing( const lang::EventObject& /*aEvent*/, sal_Bool /*bDeliverOwnership*/ )
+{
+ SolarMutexGuard aGuard;
+ SfxViewShell* pShell = m_pController->GetViewShell_Impl();
+ if (pShell)
+ {
+ bool bCanClose = pShell->PrepareClose( false );
+ if ( !bCanClose )
+ {
+ throw util::CloseVetoException("Controller disagree ...",getXWeak());
+ }
+ }
+}
+
+void SAL_CALL IMPL_SfxBaseController_CloseListenerHelper::notifyClosing( const lang::EventObject& /*aEvent*/ )
+{
+}
+
+
+// declaration IMPL_SfxBaseController_DataContainer
+
+
+struct IMPL_SfxBaseController_DataContainer
+{
+ Reference< XFrame > m_xFrame ;
+ Reference< XFrameActionListener > m_xListener ;
+ Reference< XCloseListener > m_xCloseListener ;
+ ::sfx2::UserInputInterception m_aUserInputInterception;
+ ::comphelper::OMultiTypeInterfaceContainerHelper2 m_aListenerContainer ;
+ ::comphelper::OInterfaceContainerHelper3<ui::XContextMenuInterceptor> m_aInterceptorContainer ;
+ Reference< XStatusIndicator > m_xIndicator ;
+ SfxViewShell* m_pViewShell ;
+ SfxBaseController* m_pController ;
+ bool m_bDisposing ;
+ bool m_bSuspendState ;
+ Reference< XTitle > m_xTitleHelper ;
+ Sequence< PropertyValue > m_aCreationArgs ;
+
+ IMPL_SfxBaseController_DataContainer( ::osl::Mutex& aMutex ,
+ SfxViewShell* pViewShell ,
+ SfxBaseController* pController )
+ : m_xListener ( new IMPL_SfxBaseController_ListenerHelper( pController ) )
+ , m_xCloseListener ( new IMPL_SfxBaseController_CloseListenerHelper( pController ) )
+ , m_aUserInputInterception ( *pController, aMutex )
+ , m_aListenerContainer ( aMutex )
+ , m_aInterceptorContainer ( aMutex )
+ , m_pViewShell ( pViewShell )
+ , m_pController ( pController )
+ , m_bDisposing ( false )
+ , m_bSuspendState ( false )
+ {
+ }
+
+} ; // struct IMPL_SfxBaseController_DataContainer
+
+
+// IMPL_SfxBaseController_ListenerHelper constructor
+
+
+IMPL_SfxBaseController_ListenerHelper::IMPL_SfxBaseController_ListenerHelper( SfxBaseController* pController )
+ : m_pController ( pController )
+{
+}
+
+void SAL_CALL IMPL_SfxBaseController_ListenerHelper::frameAction( const frame::FrameActionEvent& aEvent )
+{
+ SolarMutexGuard aGuard;
+ if (
+ ( m_pController != nullptr ) &&
+ ( aEvent.Frame == m_pController->getFrame() ) &&
+ ( m_pController->GetViewShell_Impl() && m_pController->GetViewShell_Impl()->GetWindow() != nullptr )
+ )
+ {
+ if ( aEvent.Action == frame::FrameAction_FRAME_UI_ACTIVATED )
+ {
+ if ( !m_pController->GetViewShell_Impl()->GetUIActiveIPClient_Impl() )
+ m_pController->GetViewShell_Impl()->GetViewFrame().MakeActive_Impl( false );
+ }
+ else if ( aEvent.Action == frame::FrameAction_CONTEXT_CHANGED )
+ {
+ m_pController->GetViewShell_Impl()->GetViewFrame().GetBindings().ContextChanged_Impl();
+ }
+ }
+}
+
+
+// IMPL_SfxBaseController_ListenerHelper -> XEventListener
+
+
+void SAL_CALL IMPL_SfxBaseController_ListenerHelper::disposing( const lang::EventObject& /*aEvent*/ )
+{
+ SolarMutexGuard aGuard;
+ if ( m_pController && m_pController->getFrame().is() )
+ m_pController->getFrame()->removeFrameActionListener( this ) ;
+}
+
+SfxBaseController::SfxBaseController( SfxViewShell* pViewShell )
+ : m_pData ( new IMPL_SfxBaseController_DataContainer( m_aMutex, pViewShell, this ))
+{
+ m_pData->m_pViewShell->SetController( this );
+}
+
+
+// SfxBaseController -> destructor
+
+
+SfxBaseController::~SfxBaseController()
+{
+}
+
+
+// SfxBaseController -> XController2
+
+
+Reference< XWindow > SAL_CALL SfxBaseController::getComponentWindow()
+{
+ SolarMutexGuard aGuard;
+ if ( !m_pData->m_pViewShell )
+ throw DisposedException();
+
+ return Reference< XWindow >( GetViewFrame_Impl().GetFrame().GetWindow().GetComponentInterface(), UNO_QUERY_THROW );
+}
+
+OUString SAL_CALL SfxBaseController::getViewControllerName()
+{
+ SolarMutexGuard aGuard;
+ if ( !m_pData->m_pViewShell || !m_pData->m_pViewShell->GetObjectShell() )
+ throw DisposedException();
+
+ const SfxObjectFactory& rDocFac( m_pData->m_pViewShell->GetObjectShell()->GetFactory() );
+ sal_uInt16 nViewNo = rDocFac.GetViewNo_Impl( GetViewFrame_Impl().GetCurViewId(), rDocFac.GetViewFactoryCount() );
+ OSL_ENSURE( nViewNo < rDocFac.GetViewFactoryCount(), "SfxBaseController::getViewControllerName: view ID not found in view factories!" );
+
+ OUString sViewName;
+ if ( nViewNo < rDocFac.GetViewFactoryCount() )
+ sViewName = rDocFac.GetViewFactory( nViewNo ).GetAPIViewName();
+
+ return sViewName;
+}
+
+Sequence< PropertyValue > SAL_CALL SfxBaseController::getCreationArguments()
+{
+ SolarMutexGuard aGuard;
+ if ( !m_pData->m_pViewShell || !m_pData->m_pViewShell->GetObjectShell() )
+ throw DisposedException();
+
+ return m_pData->m_aCreationArgs;
+}
+
+void SfxBaseController::SetCreationArguments_Impl( const Sequence< PropertyValue >& i_rCreationArgs )
+{
+ OSL_ENSURE( !m_pData->m_aCreationArgs.hasElements(), "SfxBaseController::SetCreationArguments_Impl: not intended to be called twice!" );
+ m_pData->m_aCreationArgs = i_rCreationArgs;
+}
+
+SfxViewFrame& SfxBaseController::GetViewFrame_Impl() const
+{
+ ENSURE_OR_THROW( m_pData->m_pViewShell, "not to be called without a view shell" );
+ SfxViewFrame* pActFrame = m_pData->m_pViewShell->GetFrame();
+ ENSURE_OR_THROW( pActFrame, "a view shell without a view frame is pretty pathological" );
+ return *pActFrame;
+}
+
+
+Reference<XSidebarProvider> SAL_CALL SfxBaseController::getSidebar()
+{
+ SfxViewFrame& rViewFrame = GetViewFrame_Impl();
+ SfxFrame& rFrame = rViewFrame.GetFrame();
+
+ Reference<XSidebarProvider> rSidebar = new SfxUnoSidebar(rFrame.GetFrameInterface());
+ return rSidebar;
+}
+
+
+// SfxBaseController -> XController2 -> XController
+
+
+void SAL_CALL SfxBaseController::attachFrame( const Reference< frame::XFrame >& xFrame )
+{
+ Reference< frame::XFrame > xTemp( getFrame() ) ;
+
+ SolarMutexGuard aGuard;
+ if ( xTemp.is() )
+ {
+ xTemp->removeFrameActionListener( m_pData->m_xListener ) ;
+ Reference < util::XCloseBroadcaster > xCloseable( xTemp, uno::UNO_QUERY );
+ if ( xCloseable.is() )
+ xCloseable->removeCloseListener( m_pData->m_xCloseListener );
+ }
+
+ m_pData->m_xFrame = xFrame;
+
+ if ( !xFrame.is() )
+ return;
+
+ xFrame->addFrameActionListener( m_pData->m_xListener ) ;
+ Reference < util::XCloseBroadcaster > xCloseable( xFrame, uno::UNO_QUERY );
+ if ( xCloseable.is() )
+ xCloseable->addCloseListener( m_pData->m_xCloseListener );
+
+ if ( m_pData->m_pViewShell )
+ {
+ ConnectSfxFrame_Impl( E_CONNECT );
+ ShowInfoBars( );
+
+ // attaching the frame to the controller is the last step in the creation of a new view, so notify this
+ SfxViewEventHint aHint( SfxEventHintId::ViewCreated, GlobalEventConfig::GetEventName( GlobalEventId::VIEWCREATED ), m_pData->m_pViewShell->GetObjectShell(), Reference< frame::XController2 >( this ) );
+ SfxGetpApp()->NotifyEvent( aHint );
+ }
+}
+
+
+// SfxBaseController -> XController
+
+
+sal_Bool SAL_CALL SfxBaseController::attachModel( const Reference< frame::XModel >& xModel )
+{
+ if ( m_pData->m_pViewShell && xModel.is() && xModel != m_pData->m_pViewShell->GetObjectShell()->GetModel() )
+ {
+ // don't allow to reattach a model!
+ OSL_FAIL("Can't reattach model!");
+ return false;
+ }
+
+ Reference < util::XCloseBroadcaster > xCloseable( xModel, uno::UNO_QUERY );
+ if ( xCloseable.is() )
+ xCloseable->addCloseListener( m_pData->m_xCloseListener );
+ return true;
+}
+
+
+// SfxBaseController -> XController
+
+
+sal_Bool SAL_CALL SfxBaseController::suspend( sal_Bool bSuspend )
+{
+ SolarMutexGuard aGuard;
+
+ // ignore duplicate calls, which doesn't change anything real
+ if (bool(bSuspend) == m_pData->m_bSuspendState)
+ return true;
+
+ if ( bSuspend )
+ {
+ if ( !m_pData->m_pViewShell )
+ {
+ m_pData->m_bSuspendState = true;
+ return true;
+ }
+
+ if ( !m_pData->m_pViewShell->PrepareClose() )
+ return false;
+
+ if ( getFrame().is() )
+ getFrame()->removeFrameActionListener( m_pData->m_xListener ) ;
+ SfxViewFrame* pActFrame = m_pData->m_pViewShell->GetFrame() ;
+
+ // More Views on the same document?
+ SfxObjectShell* pDocShell = m_pData->m_pViewShell->GetObjectShell() ;
+ bool bOther = false ;
+
+ for ( const SfxViewFrame* pFrame = SfxViewFrame::GetFirst( pDocShell ); !bOther && pFrame; pFrame = SfxViewFrame::GetNext( *pFrame, pDocShell ) )
+ bOther = (pFrame != pActFrame);
+
+ bool bRet = bOther || pDocShell->PrepareClose();
+ if ( bRet )
+ {
+ ConnectSfxFrame_Impl( E_DISCONNECT );
+ m_pData->m_bSuspendState = true;
+ }
+
+ return bRet;
+ }
+ else
+ {
+ if ( getFrame().is() )
+ getFrame()->addFrameActionListener( m_pData->m_xListener ) ;
+
+ if ( m_pData->m_pViewShell )
+ {
+ ConnectSfxFrame_Impl( E_RECONNECT );
+ }
+
+ m_pData->m_bSuspendState = false;
+ return true ;
+ }
+}
+
+
+// SfxBaseController -> XController
+
+
+uno::Any SfxBaseController::getViewData()
+{
+ uno::Any aAny;
+ SolarMutexGuard aGuard;
+ if ( m_pData->m_pViewShell )
+ {
+ OUString sData;
+ m_pData->m_pViewShell->WriteUserData( sData ) ;
+ aAny <<= sData ;
+ }
+
+ return aAny ;
+}
+
+
+// SfxBaseController -> XController
+
+
+void SAL_CALL SfxBaseController::restoreViewData( const uno::Any& aValue )
+{
+ SolarMutexGuard aGuard;
+ if ( m_pData->m_pViewShell )
+ {
+ OUString sData;
+ aValue >>= sData ;
+ m_pData->m_pViewShell->ReadUserData( sData ) ;
+ }
+}
+
+
+// SfxBaseController -> XController
+
+
+Reference< frame::XFrame > SAL_CALL SfxBaseController::getFrame()
+{
+ SolarMutexGuard aGuard;
+ return m_pData->m_xFrame;
+}
+
+
+// SfxBaseController -> XController
+
+
+Reference< frame::XModel > SAL_CALL SfxBaseController::getModel()
+{
+ SolarMutexGuard aGuard;
+ return m_pData->m_pViewShell ? m_pData->m_pViewShell->GetObjectShell()->GetModel() : Reference < frame::XModel > () ;
+}
+
+
+// SfxBaseController -> XDispatchProvider
+
+static css::uno::Reference<css::frame::XDispatch>
+GetSlotDispatchWithFallback(SfxViewFrame* pViewFrame, const css::util::URL& aURL,
+ const OUString& sActCommand, bool bMasterCommand, const SfxSlot* pSlot)
+{
+ assert(pViewFrame);
+
+ if (pSlot && (!pViewFrame->GetFrame().IsInPlace() || !pSlot->IsMode(SfxSlotMode::CONTAINER)))
+ return pViewFrame->GetBindings().GetDispatch(pSlot, aURL, bMasterCommand);
+
+ // try to find parent SfxViewFrame
+ if (const auto& xOwnFrame = pViewFrame->GetFrame().GetFrameInterface())
+ {
+ if (const auto& xParentFrame = xOwnFrame->getCreator())
+ {
+ // TODO/LATER: in future probably SfxViewFrame hierarchy should be the same as XFrame hierarchy
+ // SfxViewFrame* pParentFrame = pViewFrame->GetParentViewFrame();
+
+ // search the related SfxViewFrame
+ SfxViewFrame* pParentFrame = nullptr;
+ for (SfxViewFrame* pFrame = SfxViewFrame::GetFirst(); pFrame;
+ pFrame = SfxViewFrame::GetNext(*pFrame))
+ {
+ if (pFrame->GetFrame().GetFrameInterface() == xParentFrame)
+ {
+ pParentFrame = pFrame;
+ break;
+ }
+ }
+
+ if (pParentFrame)
+ {
+ const SfxSlotPool& rFrameSlotPool = SfxSlotPool::GetSlotPool(pParentFrame);
+ if (const SfxSlot* pSlot2 = rFrameSlotPool.GetUnoSlot(sActCommand))
+ return pParentFrame->GetBindings().GetDispatch(pSlot2, aURL, bMasterCommand);
+ }
+ }
+ }
+
+ return {};
+}
+
+Reference< frame::XDispatch > SAL_CALL SfxBaseController::queryDispatch( const util::URL& aURL ,
+ const OUString& sTargetFrameName,
+ sal_Int32 eSearchFlags )
+{
+ SolarMutexGuard aGuard;
+
+ if (!m_pData->m_bDisposing && m_pData->m_pViewShell)
+ {
+ SfxViewFrame& rAct = m_pData->m_pViewShell->GetViewFrame() ;
+ if ( sTargetFrameName == "_beamer" )
+ {
+ if ( eSearchFlags & frame::FrameSearchFlag::CREATE )
+ rAct.SetChildWindow( SID_BROWSER, true );
+ if (SfxChildWindow* pChildWin = rAct.GetChildWindow(SID_BROWSER))
+ {
+ if (Reference<frame::XFrame> xFrame{ pChildWin->GetFrame() })
+ {
+ xFrame->setName(sTargetFrameName);
+ if (Reference<XDispatchProvider> xProv{ xFrame, uno::UNO_QUERY })
+ return xProv->queryDispatch(aURL, sTargetFrameName, frame::FrameSearchFlag::SELF);
+ }
+ }
+ }
+
+ if ( aURL.Protocol == ".uno:" )
+ {
+ OUString aActCommand = SfxOfficeDispatch::GetMasterUnoCommand(aURL);
+ bool bMasterCommand(!aActCommand.isEmpty());
+ if (!bMasterCommand)
+ aActCommand = aURL.Path;
+ const SfxSlot* pSlot = SfxSlotPool::GetSlotPool(&rAct).GetUnoSlot(aActCommand);
+ return GetSlotDispatchWithFallback(&rAct, aURL, aActCommand, bMasterCommand, pSlot);
+ }
+ else if ( aURL.Protocol == "slot:" )
+ {
+ sal_uInt16 nId = static_cast<sal_uInt16>(aURL.Path.toInt32());
+
+ if (nId >= SID_VERB_START && nId <= SID_VERB_END)
+ {
+ const SfxSlot* pSlot = m_pData->m_pViewShell->GetVerbSlot_Impl(nId);
+ if ( pSlot )
+ return rAct.GetBindings().GetDispatch( pSlot, aURL, false );
+ }
+
+ const SfxSlot* pSlot = SfxSlotPool::GetSlotPool(&rAct).GetSlot(nId);
+ return GetSlotDispatchWithFallback(&rAct, aURL, aURL.Path, false, pSlot);
+ }
+ else if( sTargetFrameName == "_self" || sTargetFrameName.isEmpty() )
+ {
+ // check for already loaded URL ... but with additional jumpmark!
+ Reference< frame::XModel > xModel = getModel();
+ if( xModel.is() && !aURL.Mark.isEmpty() )
+ {
+ SfxSlotPool& rSlotPool = SfxSlotPool::GetSlotPool( &rAct );
+ const SfxSlot* pSlot = rSlotPool.GetSlot( SID_JUMPTOMARK );
+ if( !aURL.Main.isEmpty() && aURL.Main == xModel->getURL() && pSlot )
+ return Reference< frame::XDispatch >( new SfxOfficeDispatch( rAct.GetBindings(), rAct.GetDispatcher(), pSlot, aURL) );
+ }
+ }
+ }
+
+ return {};
+}
+
+
+// SfxBaseController -> XDispatchProvider
+
+
+uno::Sequence< Reference< frame::XDispatch > > SAL_CALL SfxBaseController::queryDispatches( const uno::Sequence< frame::DispatchDescriptor >& seqDescripts )
+{
+ // Create return list - which must have same size then the given descriptor
+ // It's not allowed to pack it!
+ sal_Int32 nCount = seqDescripts.getLength();
+ uno::Sequence< Reference< frame::XDispatch > > lDispatcher( nCount );
+
+ std::transform(seqDescripts.begin(), seqDescripts.end(), lDispatcher.getArray(),
+ [this](const frame::DispatchDescriptor& rDesc) -> Reference< frame::XDispatch > {
+ return queryDispatch(rDesc.FeatureURL, rDesc.FrameName, rDesc.SearchFlags); });
+
+ return lDispatcher;
+}
+
+
+// SfxBaseController -> XControllerBorder
+
+
+frame::BorderWidths SAL_CALL SfxBaseController::getBorder()
+{
+ frame::BorderWidths aResult;
+
+ SolarMutexGuard aGuard;
+ if ( m_pData->m_pViewShell )
+ {
+ SvBorder aBorder = m_pData->m_pViewShell->GetBorderPixel();
+ aResult.Left = aBorder.Left();
+ aResult.Top = aBorder.Top();
+ aResult.Right = aBorder.Right();
+ aResult.Bottom = aBorder.Bottom();
+ }
+
+ return aResult;
+}
+
+void SAL_CALL SfxBaseController::addBorderResizeListener( const Reference< frame::XBorderResizeListener >& xListener )
+{
+ m_pData->m_aListenerContainer.addInterface( cppu::UnoType<frame::XBorderResizeListener>::get(),
+ xListener );
+}
+
+void SAL_CALL SfxBaseController::removeBorderResizeListener( const Reference< frame::XBorderResizeListener >& xListener )
+{
+ m_pData->m_aListenerContainer.removeInterface( cppu::UnoType<frame::XBorderResizeListener>::get(),
+ xListener );
+}
+
+awt::Rectangle SAL_CALL SfxBaseController::queryBorderedArea( const awt::Rectangle& aPreliminaryRectangle )
+{
+ SolarMutexGuard aGuard;
+ if ( m_pData->m_pViewShell )
+ {
+ tools::Rectangle aTmpRect = VCLRectangle( aPreliminaryRectangle );
+ m_pData->m_pViewShell->QueryObjAreaPixel( aTmpRect );
+ return AWTRectangle( aTmpRect );
+ }
+
+ return aPreliminaryRectangle;
+}
+
+void SfxBaseController::BorderWidthsChanged_Impl()
+{
+ ::comphelper::OInterfaceContainerHelper2* pContainer = m_pData->m_aListenerContainer.getContainer(
+ cppu::UnoType<frame::XBorderResizeListener>::get());
+ if ( !pContainer )
+ return;
+
+ frame::BorderWidths aBWidths = getBorder();
+ Reference< uno::XInterface > xThis( getXWeak() );
+
+ ::comphelper::OInterfaceIteratorHelper2 pIterator(*pContainer);
+ while (pIterator.hasMoreElements())
+ {
+ try
+ {
+ static_cast<frame::XBorderResizeListener*>(pIterator.next())->borderWidthsChanged( xThis, aBWidths );
+ }
+ catch (const RuntimeException&)
+ {
+ pIterator.remove();
+ }
+ }
+}
+
+
+// SfxBaseController -> XComponent
+
+
+void SAL_CALL SfxBaseController::dispose()
+{
+ SolarMutexGuard aGuard;
+ Reference< XController > xKeepAlive( this );
+ m_pData->m_bDisposing = true ;
+
+ lang::EventObject aEventObject;
+ aEventObject.Source = *this ;
+ m_pData->m_aListenerContainer.disposeAndClear( aEventObject ) ;
+
+ if ( m_pData->m_pController && m_pData->m_pController->getFrame().is() )
+ m_pData->m_pController->getFrame()->removeFrameActionListener( m_pData->m_xListener ) ;
+
+ if ( !m_pData->m_pViewShell )
+ return;
+
+ SfxViewFrame& rFrame = m_pData->m_pViewShell->GetViewFrame() ;
+ if (rFrame.GetViewShell() == m_pData->m_pViewShell )
+ rFrame.GetFrame().SetIsClosing_Impl();
+ m_pData->m_pViewShell->DisconnectAllClients();
+
+ lang::EventObject aObject;
+ aObject.Source = *this ;
+
+ SfxObjectShell* pDoc = rFrame.GetObjectShell() ;
+ SfxViewFrame *pView = SfxViewFrame::GetFirst(pDoc);
+ while( pView )
+ {
+ // if there is another ViewFrame or currently the ViewShell in my ViewFrame is switched (PagePreview)
+ if ( pView != &rFrame || pView->GetViewShell() != m_pData->m_pViewShell )
+ break;
+ pView = SfxViewFrame::GetNext( *pView, pDoc );
+ }
+
+ SfxGetpApp()->NotifyEvent( SfxViewEventHint(SfxEventHintId::CloseView, GlobalEventConfig::GetEventName( GlobalEventId::CLOSEVIEW ), pDoc, Reference< frame::XController2 >( this ) ) );
+ if ( !pView )
+ SfxGetpApp()->NotifyEvent( SfxEventHint(SfxEventHintId::CloseDoc, GlobalEventConfig::GetEventName( GlobalEventId::CLOSEDOC ), pDoc) );
+
+ Reference< frame::XModel > xModel = pDoc->GetModel();
+ Reference < util::XCloseable > xCloseable( xModel, uno::UNO_QUERY );
+ if ( xModel.is() )
+ {
+ xModel->disconnectController( this );
+ if ( xCloseable.is() )
+ xCloseable->removeCloseListener( m_pData->m_xCloseListener );
+ }
+
+ Reference < frame::XFrame > aXFrame;
+ attachFrame( aXFrame );
+
+ m_pData->m_xListener->disposing( aObject );
+ SfxViewShell *pShell = m_pData->m_pViewShell;
+ m_pData->m_pViewShell = nullptr;
+ if (rFrame.GetViewShell() == pShell)
+ {
+ // Enter registrations only allowed if we are the owner!
+ if ( rFrame.GetFrame().OwnsBindings_Impl() )
+ rFrame.GetBindings().ENTERREGISTRATIONS();
+ rFrame.GetFrame().SetFrameInterface_Impl( aXFrame );
+ rFrame.GetFrame().DoClose_Impl();
+ }
+}
+
+
+// SfxBaseController -> XComponent
+
+
+void SAL_CALL SfxBaseController::addEventListener( const Reference< lang::XEventListener >& aListener )
+{
+ m_pData->m_aListenerContainer.addInterface( cppu::UnoType<lang::XEventListener>::get(), aListener );
+}
+
+
+// SfxBaseController -> XComponent
+
+
+void SAL_CALL SfxBaseController::removeEventListener( const Reference< lang::XEventListener >& aListener )
+{
+ m_pData->m_aListenerContainer.removeInterface( cppu::UnoType<lang::XEventListener>::get(), aListener );
+}
+
+void SfxBaseController::ReleaseShell_Impl()
+{
+ SolarMutexGuard aGuard;
+ if ( !m_pData->m_pViewShell )
+ return;
+
+ SfxObjectShell* pDoc = m_pData->m_pViewShell->GetObjectShell() ;
+ Reference< frame::XModel > xModel = pDoc->GetModel();
+ Reference < util::XCloseable > xCloseable( xModel, uno::UNO_QUERY );
+ if ( xModel.is() )
+ {
+ xModel->disconnectController( this );
+ if ( xCloseable.is() )
+ xCloseable->removeCloseListener( m_pData->m_xCloseListener );
+ }
+ m_pData->m_pViewShell = nullptr;
+
+ Reference < frame::XFrame > aXFrame;
+ attachFrame( aXFrame );
+}
+
+void SfxBaseController::CopyLokViewCallbackFromFrameCreator()
+{
+ if (!m_pData->m_pViewShell)
+ return;
+ SfxLokCallbackInterface* pCallback = nullptr;
+ if (m_pData->m_xFrame)
+ if (auto xCreator = m_pData->m_xFrame->getCreator())
+ if (auto parentVS = SfxViewShell::Get(xCreator->getController()))
+ pCallback = parentVS->getLibreOfficeKitViewCallback();
+ m_pData->m_pViewShell->setLibreOfficeKitViewCallback(pCallback);
+}
+
+SfxViewShell* SfxBaseController::GetViewShell_Impl() const
+{
+ return m_pData->m_pViewShell;
+}
+
+Reference< task::XStatusIndicator > SAL_CALL SfxBaseController::getStatusIndicator( )
+{
+ SolarMutexGuard aGuard;
+ if ( m_pData->m_pViewShell && !m_pData->m_xIndicator.is() )
+ m_pData->m_xIndicator = new SfxStatusIndicator( this, m_pData->m_pViewShell->GetViewFrame().GetFrame().GetWorkWindow_Impl() );
+ return m_pData->m_xIndicator;
+}
+
+void SAL_CALL SfxBaseController::registerContextMenuInterceptor( const Reference< ui::XContextMenuInterceptor >& xInterceptor )
+
+{
+ m_pData->m_aInterceptorContainer.addInterface( xInterceptor );
+
+ SolarMutexGuard aGuard;
+ if ( m_pData->m_pViewShell )
+ m_pData->m_pViewShell->AddContextMenuInterceptor_Impl( xInterceptor );
+}
+
+void SAL_CALL SfxBaseController::releaseContextMenuInterceptor( const Reference< ui::XContextMenuInterceptor >& xInterceptor )
+
+{
+ m_pData->m_aInterceptorContainer.removeInterface( xInterceptor );
+
+ SolarMutexGuard aGuard;
+ if ( m_pData->m_pViewShell )
+ m_pData->m_pViewShell->RemoveContextMenuInterceptor_Impl( xInterceptor );
+}
+
+void SAL_CALL SfxBaseController::addKeyHandler( const Reference< awt::XKeyHandler >& xHandler )
+{
+ SolarMutexGuard aGuard;
+ m_pData->m_aUserInputInterception.addKeyHandler( xHandler );
+}
+
+void SAL_CALL SfxBaseController::removeKeyHandler( const Reference< awt::XKeyHandler >& xHandler )
+{
+ SolarMutexGuard aGuard;
+ m_pData->m_aUserInputInterception.removeKeyHandler( xHandler );
+}
+
+void SAL_CALL SfxBaseController::addMouseClickHandler( const Reference< awt::XMouseClickHandler >& xHandler )
+{
+ SolarMutexGuard aGuard;
+ m_pData->m_aUserInputInterception.addMouseClickHandler( xHandler );
+}
+
+void SAL_CALL SfxBaseController::removeMouseClickHandler( const Reference< awt::XMouseClickHandler >& xHandler )
+{
+ SolarMutexGuard aGuard;
+ m_pData->m_aUserInputInterception.removeMouseClickHandler( xHandler );
+}
+
+uno::Sequence< sal_Int16 > SAL_CALL SfxBaseController::getSupportedCommandGroups()
+{
+ SolarMutexGuard aGuard;
+
+ std::vector< sal_Int16 > aGroupList;
+ SfxViewFrame* pViewFrame = m_pData->m_pViewShell ? m_pData->m_pViewShell->GetFrame() : nullptr;
+ SfxSlotPool* pSlotPool = pViewFrame ? &SfxSlotPool::GetSlotPool(pViewFrame) : &SFX_SLOTPOOL();
+ const SfxSlotMode nMode( SfxSlotMode::TOOLBOXCONFIG|SfxSlotMode::ACCELCONFIG|SfxSlotMode::MENUCONFIG );
+
+ // Select Group ( Group 0 is internal )
+ for ( sal_uInt16 i=0; i<pSlotPool->GetGroupCount(); i++ )
+ {
+ pSlotPool->SeekGroup( i );
+ const SfxSlot* pSfxSlot = pSlotPool->FirstSlot();
+ while ( pSfxSlot )
+ {
+ if ( pSfxSlot->GetMode() & nMode )
+ {
+ sal_Int16 nCommandGroup = MapGroupIDToCommandGroup( pSfxSlot->GetGroupId() );
+ aGroupList.push_back( nCommandGroup );
+ break;
+ }
+ pSfxSlot = pSlotPool->NextSlot();
+ }
+ }
+
+ return comphelper::containerToSequence( aGroupList );
+}
+
+uno::Sequence< frame::DispatchInformation > SAL_CALL SfxBaseController::getConfigurableDispatchInformation( sal_Int16 nCmdGroup )
+{
+ std::vector< frame::DispatchInformation > aCmdVector;
+
+ SolarMutexGuard aGuard;
+ if ( m_pData->m_pViewShell )
+ {
+ const SfxSlotMode nMode( SfxSlotMode::TOOLBOXCONFIG|SfxSlotMode::ACCELCONFIG|SfxSlotMode::MENUCONFIG );
+
+ SfxViewFrame* pViewFrame( m_pData->m_pViewShell->GetFrame() );
+ SfxSlotPool* pSlotPool
+ = pViewFrame ? &SfxSlotPool::GetSlotPool(pViewFrame) : &SFX_SLOTPOOL();
+ for ( sal_uInt16 i=0; i<pSlotPool->GetGroupCount(); i++ )
+ {
+ pSlotPool->SeekGroup( i );
+ const SfxSlot* pSfxSlot = pSlotPool->FirstSlot();
+ if ( pSfxSlot )
+ {
+ sal_Int16 nCommandGroup = MapGroupIDToCommandGroup( pSfxSlot->GetGroupId() );
+ if ( nCommandGroup == nCmdGroup )
+ {
+ while ( pSfxSlot )
+ {
+ if ( pSfxSlot->GetMode() & nMode )
+ {
+ frame::DispatchInformation aCmdInfo;
+ aCmdInfo.Command = pSfxSlot->GetCommand();
+ aCmdInfo.GroupId = nCommandGroup;
+ aCmdVector.push_back( aCmdInfo );
+ }
+ pSfxSlot = pSlotPool->NextSlot();
+ }
+ }
+ }
+ }
+ }
+
+ return comphelper::containerToSequence( aCmdVector );
+}
+
+bool SfxBaseController::HandleEvent_Impl( NotifyEvent const & rEvent )
+{
+ return m_pData->m_aUserInputInterception.handleNotifyEvent( rEvent );
+}
+
+bool SfxBaseController::HasKeyListeners_Impl() const
+{
+ return m_pData->m_aUserInputInterception.hasKeyHandlers();
+}
+
+bool SfxBaseController::HasMouseClickListeners_Impl() const
+{
+ return m_pData->m_aUserInputInterception.hasMouseClickListeners();
+}
+
+void SfxBaseController::ConnectSfxFrame_Impl( const ConnectSfxFrame i_eConnect )
+{
+ ENSURE_OR_THROW( m_pData->m_pViewShell, "not to be called without a view shell" );
+ SfxViewFrame* pViewFrame = m_pData->m_pViewShell->GetFrame();
+ ENSURE_OR_THROW( pViewFrame, "a view shell without a view frame is pretty pathological" );
+
+ const bool bConnect = ( i_eConnect != E_DISCONNECT );
+
+ // disable window and dispatcher
+ pViewFrame->Enable( bConnect );
+ pViewFrame->GetDispatcher()->Lock( !bConnect );
+
+ if ( bConnect )
+ {
+ if ( i_eConnect == E_CONNECT )
+ {
+ if ( ( m_pData->m_pViewShell->GetObjectShell() != nullptr )
+ && ( m_pData->m_pViewShell->GetObjectShell()->GetCreateMode() == SfxObjectCreateMode::EMBEDDED )
+ )
+ {
+ SfxViewFrame& rViewFrm = m_pData->m_pViewShell->GetViewFrame();
+ if ( !rViewFrm.GetFrame().IsInPlace() )
+ {
+ // for outplace embedded objects, we want the layout manager to keep the content window
+ // size constant, if possible
+ try
+ {
+ Reference< beans::XPropertySet > xFrameProps( m_pData->m_xFrame, uno::UNO_QUERY_THROW );
+ Reference< beans::XPropertySet > xLayouterProps(
+ xFrameProps->getPropertyValue("LayoutManager"), uno::UNO_QUERY_THROW );
+ xLayouterProps->setPropertyValue("PreserveContentSize", uno::Any( true ) );
+ }
+ catch (const uno::Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("sfx.view");
+ }
+ }
+ }
+ }
+
+ // upon DISCONNECT, we did *not* pop the shells from the stack (this is done elsewhere), so upon
+ // RECONNECT, we're not allowed to push them
+ if ( i_eConnect != E_RECONNECT )
+ {
+ pViewFrame->GetDispatcher()->Push( *m_pData->m_pViewShell );
+ m_pData->m_pViewShell->PushSubShells_Impl();
+ pViewFrame->GetDispatcher()->Flush();
+ }
+
+ vcl::Window* pEditWin = m_pData->m_pViewShell->GetWindow();
+ if ( pEditWin )
+ pEditWin->Show();
+
+ if ( SfxViewFrame::Current() == pViewFrame )
+ pViewFrame->GetDispatcher()->Update_Impl( true );
+
+ vcl::Window* pFrameWin = &pViewFrame->GetWindow();
+ if ( pFrameWin != &pViewFrame->GetFrame().GetWindow() )
+ pFrameWin->Show();
+
+ if ( i_eConnect == E_CONNECT )
+ {
+ css::uno::Reference<css::frame::XModel3> xModel(getModel(), css::uno::UNO_QUERY_THROW);
+ const sal_Int16 nPluginMode = ::comphelper::NamedValueCollection::getOrDefault( xModel->getArgs2( { "PluginMode" } ), u"PluginMode", sal_Int16( 0 ) );
+ const bool bHasPluginMode = ( nPluginMode != 0 );
+
+ SfxFrame& rFrame = pViewFrame->GetFrame();
+ SfxObjectShell& rDoc = *m_pData->m_pViewShell->GetObjectShell();
+ if ( !rFrame.IsMarkedHidden_Impl() )
+ {
+ if ( rDoc.IsHelpDocument() || ( nPluginMode == 2 ) )
+ pViewFrame->GetDispatcher()->HideUI();
+ else
+ pViewFrame->GetDispatcher()->HideUI( false );
+
+ if ( rFrame.IsInPlace() )
+ pViewFrame->LockAdjustPosSizePixel();
+
+ if ( nPluginMode == 3 )
+ rFrame.GetWorkWindow_Impl()->SetInternalDockingAllowed( false );
+
+ if ( !rFrame.IsInPlace() )
+ pViewFrame->GetDispatcher()->Update_Impl();
+ pViewFrame->Show();
+ rFrame.GetWindow().Show();
+ if ( !rFrame.IsInPlace() || ( nPluginMode == 3 ) )
+ pViewFrame->MakeActive_Impl( rFrame.GetFrameInterface()->isActive() );
+
+ if ( rFrame.IsInPlace() )
+ {
+ pViewFrame->UnlockAdjustPosSizePixel();
+ // force resize for OLE server to fix layout problems of writer and math
+ // see i53651
+ if ( nPluginMode == 3 )
+ pViewFrame->Resize( true );
+ }
+ }
+ else
+ {
+ DBG_ASSERT( !rFrame.IsInPlace() && !bHasPluginMode, "Special modes not compatible with hidden mode!" );
+ rFrame.GetWindow().Show();
+ }
+
+ // UpdateTitle now, hidden TopFrames have otherwise no Name!
+ pViewFrame->UpdateTitle();
+
+ if ( !rFrame.IsInPlace() )
+ pViewFrame->Resize( true );
+
+ ::comphelper::NamedValueCollection aViewArgs(getCreationArguments());
+
+ // sometimes we want to avoid adding to the recent documents
+ bool bAllowPickListEntry = aViewArgs.getOrDefault("PickListEntry", true);
+ m_pData->m_pViewShell->GetObjectShell()->AvoidRecentDocs(!bAllowPickListEntry);
+
+ // if there's a JumpMark given, then, well, jump to it
+ const OUString sJumpMark = aViewArgs.getOrDefault( "JumpMark", OUString() );
+ const bool bHasJumpMark = !sJumpMark.isEmpty();
+ OSL_ENSURE( ( !m_pData->m_pViewShell->GetObjectShell()->IsLoading() )
+ || ( sJumpMark.isEmpty() ),
+ "SfxBaseController::ConnectSfxFrame_Impl: so this code wasn't dead?" );
+ // Before CWS autorecovery, there was code which postponed jumping to the Mark to a later time
+ // (SfxObjectShell::PositionView_Impl), but it seems this branch was never used, since this method
+ // here is never called before the load process finished. At least not with a non-empty jump mark
+ if ( !sJumpMark.isEmpty() )
+ m_pData->m_pViewShell->JumpToMark( sJumpMark );
+
+ // if no plugin mode and no jump mark was supplied, check whether the document itself can provide view data, and
+ // if so, forward it to the view/shell.
+ if ( !bHasPluginMode && !bHasJumpMark )
+ {
+ // Note that this might not be the ideal place here. Restoring view data should, IMO, be the
+ // responsibility of the loader, not an implementation detail buried here deep within the controller's
+ // implementation.
+ // What I think should be done to replace the below code:
+ // - change SfxBaseController::restoreViewData to also accept a PropertyValue[] (it currently accepts
+ // a string only), and forward it to its ViewShell's ReadUserDataSequence
+ // - change the frame loader so that when a new document is loaded (as opposed to an existing
+ // document being loaded into a new frame), the model's view data is examine the very same
+ // way as below, and the proper view data is set via XController::restoreViewData
+ // - extend SfxViewFrame::SwitchToViewShell_Impl. Currently, it cares for the case where a non-PrintPreview
+ // view is exchanged, and sets the old view's data at the model. It should also care for the other
+ // way, were the PrintPreview view is left: in this case, the new view should also be initialized
+ // with the model's view data
+ try
+ {
+ Reference< XViewDataSupplier > xViewDataSupplier( getModel(), UNO_QUERY_THROW );
+ Reference< XIndexAccess > xViewData( xViewDataSupplier->getViewData() );
+
+ // find the view data item whose ViewId matches the ID of the view we're just connecting to
+ const SfxObjectFactory& rDocFactory( rDoc.GetFactory() );
+ const sal_Int32 nCount = xViewData.is() ? xViewData->getCount() : 0;
+ sal_Int32 nViewDataIndex = 0;
+ for ( sal_Int32 i=0; i<nCount; ++i )
+ {
+ const ::comphelper::NamedValueCollection aViewData( xViewData->getByIndex(i) );
+ OUString sViewId( aViewData.getOrDefault( "ViewId", OUString() ) );
+ if ( sViewId.isEmpty() )
+ continue;
+
+ const SfxViewFactory* pViewFactory = rDocFactory.GetViewFactoryByViewName( sViewId );
+ if ( pViewFactory == nullptr )
+ continue;
+
+ if ( pViewFactory->GetOrdinal() == pViewFrame->GetCurViewId() )
+ {
+ nViewDataIndex = i;
+ break;
+ }
+ }
+ if (nViewDataIndex < nCount || !xViewData.is())
+ {
+ Sequence< PropertyValue > aViewData;
+ if (xViewData.is())
+ {
+ OSL_VERIFY(xViewData->getByIndex(nViewDataIndex) >>= aViewData);
+ }
+ if (aViewData.hasElements() || !xViewData.is())
+ {
+ // Tolerate empty xViewData, ReadUserDataSequence() has side effects.
+ m_pData->m_pViewShell->ReadUserDataSequence( aViewData );
+ }
+ }
+ }
+ catch (const Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("sfx.view");
+ }
+ }
+ }
+ }
+
+ // invalidate slot corresponding to the view shell
+ const sal_uInt16 nViewNo = m_pData->m_pViewShell->GetObjectShell()->GetFactory().GetViewNo_Impl( pViewFrame->GetCurViewId(), USHRT_MAX );
+ DBG_ASSERT( nViewNo != USHRT_MAX, "view shell id not found" );
+ if ( nViewNo != USHRT_MAX )
+ pViewFrame->GetBindings().Invalidate( nViewNo + SID_VIEWSHELL0 );
+}
+
+void SfxBaseController::ShowInfoBars( )
+{
+ if ( !m_pData->m_pViewShell )
+ return;
+
+ // CMIS verifications
+ Reference< document::XCmisDocument > xCmisDoc( m_pData->m_pViewShell->GetObjectShell()->GetModel(), uno::UNO_QUERY );
+ if ( !xCmisDoc.is( ) || !xCmisDoc->canCheckOut( ) )
+ return;
+
+ const uno::Sequence< document::CmisProperty> aCmisProperties = xCmisDoc->getCmisProperties( );
+
+ if ( !(xCmisDoc->isVersionable( ) && aCmisProperties.hasElements( )) )
+ return;
+
+ // Loop over the CMIS Properties to find cmis:isVersionSeriesCheckedOut
+ // and find if it is a Google Drive file.
+ bool bIsGoogleFile = false;
+ bool bCheckedOut = false;
+ for ( const auto& rCmisProp : aCmisProperties )
+ {
+ if ( rCmisProp.Id == "cmis:isVersionSeriesCheckedOut" ) {
+ uno::Sequence< sal_Bool > bTmp;
+ rCmisProp.Value >>= bTmp;
+ bCheckedOut = bTmp[0];
+ }
+ // if it is a Google Drive file, we don't need the checkout bar,
+ // still need the checkout feature for the version dialog.
+ if ( rCmisProp.Name == "title" )
+ bIsGoogleFile = true;
+ }
+
+ if ( bCheckedOut || bIsGoogleFile )
+ return;
+
+ // Get the Frame and show the InfoBar if not checked out
+ SfxViewFrame* pViewFrame = m_pData->m_pViewShell->GetFrame();
+ auto pInfoBar = pViewFrame->AppendInfoBar("checkout", "", SfxResId(STR_NONCHECKEDOUT_DOCUMENT),
+ InfobarType::WARNING);
+ if (pInfoBar)
+ {
+ weld::Button &rBtn = pInfoBar->addButton();
+ rBtn.set_label(SfxResId(STR_CHECKOUT));
+ rBtn.connect_clicked(LINK(this, SfxBaseController, CheckOutHandler));
+ }
+}
+
+IMPL_LINK_NOARG ( SfxBaseController, CheckOutHandler, weld::Button&, void )
+{
+ if ( m_pData->m_pViewShell )
+ m_pData->m_pViewShell->GetObjectShell()->CheckOut( );
+}
+
+
+Reference< frame::XTitle > SfxBaseController::impl_getTitleHelper ()
+{
+ SolarMutexGuard aGuard;
+
+ if ( ! m_pData->m_xTitleHelper.is ())
+ {
+ Reference< frame::XModel > xModel = getModel ();
+ Reference< frame::XUntitledNumbers > xUntitledProvider(xModel , uno::UNO_QUERY );
+
+ m_pData->m_xTitleHelper = new ::framework::TitleHelper(::comphelper::getProcessComponentContext(),
+ Reference< frame::XController >(this), xUntitledProvider);
+ }
+
+ return m_pData->m_xTitleHelper;
+}
+
+
+// frame::XTitle
+OUString SAL_CALL SfxBaseController::getTitle()
+{
+ return impl_getTitleHelper()->getTitle ();
+}
+
+
+// frame::XTitle
+void SAL_CALL SfxBaseController::setTitle(const OUString& sTitle)
+{
+ impl_getTitleHelper()->setTitle (sTitle);
+}
+
+
+// frame::XTitleChangeBroadcaster
+void SAL_CALL SfxBaseController::addTitleChangeListener(const Reference< frame::XTitleChangeListener >& xListener)
+{
+ Reference< frame::XTitleChangeBroadcaster > xBroadcaster(impl_getTitleHelper(), uno::UNO_QUERY);
+ if (xBroadcaster.is ())
+ xBroadcaster->addTitleChangeListener (xListener);
+}
+
+
+// frame::XTitleChangeBroadcaster
+void SAL_CALL SfxBaseController::removeTitleChangeListener(const Reference< frame::XTitleChangeListener >& xListener)
+{
+ Reference< frame::XTitleChangeBroadcaster > xBroadcaster(impl_getTitleHelper(), uno::UNO_QUERY);
+ if (xBroadcaster.is ())
+ xBroadcaster->removeTitleChangeListener (xListener);
+}
+
+void SfxBaseController::initialize( const css::uno::Sequence< css::uno::Any >& /*aArguments*/ )
+{
+}
+
+void SAL_CALL SfxBaseController::appendInfobar(const OUString& sId, const OUString& sPrimaryMessage,
+ const OUString& sSecondaryMessage,
+ sal_Int32 aInfobarType,
+ const Sequence<StringPair>& actionButtons,
+ sal_Bool bShowCloseButton)
+{
+ SolarMutexGuard aGuard;
+
+ if (aInfobarType < static_cast<sal_Int32>(InfobarType::INFO)
+ || aInfobarType > static_cast<sal_Int32>(InfobarType::DANGER))
+ throw lang::IllegalArgumentException("Undefined InfobarType: "
+ + OUString::number(aInfobarType),
+ getXWeak(), 0);
+ SfxViewFrame* pViewFrame = m_pData->m_pViewShell->GetFrame();
+ if (pViewFrame->HasInfoBarWithID(sId))
+ throw lang::IllegalArgumentException("Infobar with ID '" + sId + "' already existing.",
+ getXWeak(), 0);
+
+ auto pInfoBar
+ = pViewFrame->AppendInfoBar(sId, sPrimaryMessage, sSecondaryMessage,
+ static_cast<InfobarType>(aInfobarType), bShowCloseButton);
+ if (!pInfoBar)
+ throw uno::RuntimeException("Could not create Infobar");
+
+ for (const StringPair & actionButton : std::as_const(actionButtons))
+ {
+ if (actionButton.First.isEmpty() || actionButton.Second.isEmpty())
+ continue;
+ weld::Button& rBtn = pInfoBar->addButton(&actionButton.Second);
+ rBtn.set_label(actionButton.First);
+ }
+}
+
+void SAL_CALL SfxBaseController::updateInfobar(const OUString& sId, const OUString& sPrimaryMessage,
+ const OUString& sSecondaryMessage,
+ sal_Int32 aInfobarType)
+{
+ SolarMutexGuard aGuard;
+
+ if (aInfobarType < static_cast<sal_Int32>(InfobarType::INFO)
+ || aInfobarType > static_cast<sal_Int32>(InfobarType::DANGER))
+ throw lang::IllegalArgumentException("Undefined InfobarType: "
+ + OUString::number(aInfobarType),
+ getXWeak(), 0);
+ SfxViewFrame* pViewFrame = m_pData->m_pViewShell->GetFrame();
+ if (!pViewFrame->HasInfoBarWithID(sId))
+ throw css::container::NoSuchElementException("Infobar with ID '" + sId + "' not found.");
+
+ pViewFrame->UpdateInfoBar(sId, sPrimaryMessage, sSecondaryMessage,
+ static_cast<InfobarType>(aInfobarType));
+}
+
+void SAL_CALL SfxBaseController::removeInfobar(const OUString& sId)
+{
+ SolarMutexGuard aGuard;
+
+ SfxViewFrame* pViewFrame = m_pData->m_pViewShell->GetFrame();
+ if (!pViewFrame->HasInfoBarWithID(sId))
+ throw css::container::NoSuchElementException("Infobar with ID '" + sId + "' not found.");
+ pViewFrame->RemoveInfoBar(sId);
+}
+
+sal_Bool SAL_CALL SfxBaseController::hasInfobar(const OUString& sId)
+{
+ SolarMutexGuard aGuard;
+ SfxViewFrame* pViewFrame = m_pData->m_pViewShell->GetFrame();
+ return pViewFrame->HasInfoBarWithID(sId);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/view/userinputinterception.cxx b/sfx2/source/view/userinputinterception.cxx
new file mode 100644
index 0000000000..f609652ea7
--- /dev/null
+++ b/sfx2/source/view/userinputinterception.cxx
@@ -0,0 +1,263 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <sfx2/userinputinterception.hxx>
+
+#include <com/sun/star/awt/MouseButton.hpp>
+#include <com/sun/star/awt/MouseEvent.hpp>
+#include <com/sun/star/awt/InputEvent.hpp>
+#include <com/sun/star/awt/KeyEvent.hpp>
+#include <com/sun/star/awt/KeyModifier.hpp>
+#include <com/sun/star/awt/XKeyHandler.hpp>
+#include <com/sun/star/awt/XMouseClickHandler.hpp>
+#include <com/sun/star/awt/XVclWindowPeer.hpp>
+#include <com/sun/star/uno/XInterface.hpp>
+
+#include <comphelper/interfacecontainer3.hxx>
+#include <cppuhelper/weak.hxx>
+#include <vcl/event.hxx>
+#include <vcl/window.hxx>
+#include <osl/diagnose.h>
+
+namespace sfx2
+{
+
+
+ using ::com::sun::star::uno::Reference;
+ using ::com::sun::star::uno::XInterface;
+ using ::com::sun::star::uno::Exception;
+ using ::com::sun::star::uno::RuntimeException;
+ using ::com::sun::star::awt::MouseEvent;
+ using ::com::sun::star::awt::KeyEvent;
+ using ::com::sun::star::awt::InputEvent;
+ using ::com::sun::star::awt::XKeyHandler;
+ using ::com::sun::star::awt::XMouseClickHandler;
+ using ::com::sun::star::lang::DisposedException;
+
+ namespace MouseButton = ::com::sun::star::awt::MouseButton;
+ namespace KeyModifier = ::com::sun::star::awt::KeyModifier;
+
+ struct UserInputInterception_Data
+ {
+ public:
+ ::cppu::OWeakObject& m_rControllerImpl;
+ ::comphelper::OInterfaceContainerHelper3<XKeyHandler> m_aKeyHandlers;
+ ::comphelper::OInterfaceContainerHelper3<XMouseClickHandler> m_aMouseClickHandlers;
+
+ public:
+ UserInputInterception_Data( ::cppu::OWeakObject& _rControllerImpl, ::osl::Mutex& _rMutex )
+ :m_rControllerImpl( _rControllerImpl )
+ ,m_aKeyHandlers( _rMutex )
+ ,m_aMouseClickHandlers( _rMutex )
+ {
+ }
+ };
+
+ namespace
+ {
+ template< class VCLEVENT >
+ void lcl_initModifiers( InputEvent& _rEvent, const VCLEVENT& _rVclEvent )
+ {
+ _rEvent.Modifiers = 0;
+
+ if ( _rVclEvent.IsShift() )
+ _rEvent.Modifiers |= KeyModifier::SHIFT;
+ if ( _rVclEvent.IsMod1() )
+ _rEvent.Modifiers |= KeyModifier::MOD1;
+ if ( _rVclEvent.IsMod2() )
+ _rEvent.Modifiers |= KeyModifier::MOD2;
+ if ( _rVclEvent.IsMod3() )
+ _rEvent.Modifiers |= KeyModifier::MOD3;
+ }
+
+ void lcl_initKeyEvent( KeyEvent& rEvent, const ::KeyEvent& rEvt )
+ {
+ lcl_initModifiers( rEvent, rEvt.GetKeyCode() );
+
+ rEvent.KeyCode = rEvt.GetKeyCode().GetCode();
+ rEvent.KeyChar = rEvt.GetCharCode();
+ rEvent.KeyFunc = sal::static_int_cast< sal_Int16 >( rEvt.GetKeyCode().GetFunction());
+ }
+
+ void lcl_initMouseEvent( MouseEvent& rEvent, const ::MouseEvent& rEvt )
+ {
+ lcl_initModifiers( rEvent, rEvt );
+
+ rEvent.Buttons = 0;
+ if ( rEvt.IsLeft() )
+ rEvent.Buttons |= MouseButton::LEFT;
+ if ( rEvt.IsRight() )
+ rEvent.Buttons |= MouseButton::RIGHT;
+ if ( rEvt.IsMiddle() )
+ rEvent.Buttons |= MouseButton::MIDDLE;
+
+ rEvent.X = rEvt.GetPosPixel().X();
+ rEvent.Y = rEvt.GetPosPixel().Y();
+ rEvent.ClickCount = rEvt.GetClicks();
+ rEvent.PopupTrigger = false;
+ }
+
+ }
+
+
+ //= UserInputInterception
+
+
+ UserInputInterception::UserInputInterception( ::cppu::OWeakObject& _rControllerImpl, ::osl::Mutex& _rMutex )
+ :m_pData( new UserInputInterception_Data( _rControllerImpl, _rMutex ) )
+ {
+ }
+
+
+ UserInputInterception::~UserInputInterception()
+ {
+ }
+
+
+ void UserInputInterception::addKeyHandler( const Reference< XKeyHandler >& _rxHandler )
+ {
+ if ( _rxHandler.is() )
+ m_pData->m_aKeyHandlers.addInterface( _rxHandler );
+ }
+
+
+ void UserInputInterception::removeKeyHandler( const Reference< XKeyHandler >& _rxHandler )
+ {
+ m_pData->m_aKeyHandlers.removeInterface( _rxHandler );
+ }
+
+
+ void UserInputInterception::addMouseClickHandler( const Reference< XMouseClickHandler >& _rxHandler )
+ {
+ if ( _rxHandler.is() )
+ m_pData->m_aMouseClickHandlers.addInterface( _rxHandler );
+ }
+
+
+ void UserInputInterception::removeMouseClickHandler( const Reference< XMouseClickHandler >& _rxHandler )
+ {
+ m_pData->m_aMouseClickHandlers.removeInterface( _rxHandler );
+ }
+
+
+ bool UserInputInterception::hasKeyHandlers() const
+ {
+ return m_pData->m_aKeyHandlers.getLength() > 0;
+ }
+
+
+ bool UserInputInterception::hasMouseClickListeners() const
+ {
+ return m_pData->m_aMouseClickHandlers.getLength() > 0;
+ }
+
+
+ bool UserInputInterception::handleNotifyEvent( const NotifyEvent& _rEvent )
+ {
+ Reference < XInterface > xHoldAlive( m_pData->m_rControllerImpl );
+
+ NotifyEventType nType = _rEvent.GetType();
+ bool bHandled = false;
+
+ switch ( nType )
+ {
+ case NotifyEventType::KEYINPUT:
+ case NotifyEventType::KEYUP:
+ {
+ KeyEvent aEvent;
+ lcl_initKeyEvent( aEvent, *_rEvent.GetKeyEvent() );
+ if ( _rEvent.GetWindow() )
+ aEvent.Source = _rEvent.GetWindow()->GetComponentInterface();
+
+ ::comphelper::OInterfaceIteratorHelper3 aIterator( m_pData->m_aKeyHandlers );
+ while ( aIterator.hasMoreElements() )
+ {
+ Reference< XKeyHandler > xHandler( aIterator.next() );
+ try
+ {
+ if ( nType == NotifyEventType::KEYINPUT )
+ bHandled = xHandler->keyPressed( aEvent );
+ else
+ bHandled = xHandler->keyReleased( aEvent );
+ }
+ catch( const DisposedException& e )
+ {
+ if ( e.Context == xHandler )
+ aIterator.remove();
+ }
+ catch( const RuntimeException& )
+ {
+ throw;
+ }
+ catch( const Exception& )
+ {
+ }
+ }
+ }
+ break;
+
+ case NotifyEventType::MOUSEBUTTONDOWN:
+ case NotifyEventType::MOUSEBUTTONUP:
+ {
+ MouseEvent aEvent;
+ lcl_initMouseEvent( aEvent, *_rEvent.GetMouseEvent() );
+ if ( _rEvent.GetWindow() )
+ aEvent.Source = _rEvent.GetWindow()->GetComponentInterface();
+
+ ::comphelper::OInterfaceIteratorHelper3 aIterator( m_pData->m_aMouseClickHandlers );
+ while ( aIterator.hasMoreElements() )
+ {
+ Reference< XMouseClickHandler > xHandler( aIterator.next() );
+ try
+ {
+ if ( nType == NotifyEventType::MOUSEBUTTONDOWN )
+ bHandled = xHandler->mousePressed( aEvent );
+ else
+ bHandled = xHandler->mouseReleased( aEvent );
+ }
+ catch( const DisposedException& e )
+ {
+ if ( e.Context == xHandler )
+ aIterator.remove();
+ }
+ catch( const RuntimeException& )
+ {
+ throw;
+ }
+ catch( const Exception& )
+ {
+ }
+ }
+ }
+ break;
+
+ default:
+ OSL_FAIL( "UserInputInterception::handleNotifyEvent: illegal event type!" );
+ break;
+ }
+
+ return bHandled;
+ }
+
+
+} // namespace sfx2
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/view/viewfac.cxx b/sfx2/source/view/viewfac.cxx
new file mode 100644
index 0000000000..fbcb8c2c14
--- /dev/null
+++ b/sfx2/source/view/viewfac.cxx
@@ -0,0 +1,56 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sfx2/viewfac.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <sfx2/viewsh.hxx>
+
+SfxViewShell *SfxViewFactory::CreateInstance(SfxViewFrame& rFrame, SfxViewShell *pOldSh)
+{
+ return (*fnCreate)(rFrame, pOldSh);
+}
+
+OUString SfxViewFactory::GetLegacyViewName() const
+{
+ return "view" + OUString::number( sal_uInt16( GetOrdinal() ) );
+}
+
+OUString SfxViewFactory::GetAPIViewName() const
+{
+ if ( !m_sViewName.isEmpty() )
+ return m_sViewName;
+
+ if ( GetOrdinal() == SFX_INTERFACE_NONE )
+ return "Default";
+
+ return GetLegacyViewName();
+}
+
+// CTOR / DTOR -----------------------------------------------------------
+
+SfxViewFactory::SfxViewFactory( SfxViewCtor fnC,
+ SfxInterfaceId nOrdinal, const char* asciiViewName ):
+ fnCreate(fnC),
+ nOrd(nOrdinal),
+ m_sViewName( OUString::createFromAscii( asciiViewName ) )
+{
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/view/viewfrm.cxx b/sfx2/source/view/viewfrm.cxx
new file mode 100644
index 0000000000..fe0dc0adc7
--- /dev/null
+++ b/sfx2/source/view/viewfrm.cxx
@@ -0,0 +1,3706 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <config_feature_desktop.h>
+#include <config_wasm_strip.h>
+
+#include <osl/file.hxx>
+#include <sfx2/docfilt.hxx>
+#include <sfx2/infobar.hxx>
+#include <sfx2/sfxsids.hrc>
+#include <sfx2/viewfrm.hxx>
+#include <sfx2/classificationhelper.hxx>
+#include <sfx2/notebookbar/SfxNotebookBar.hxx>
+#include <sfx2/pageids.hxx>
+#include <com/sun/star/document/MacroExecMode.hpp>
+#include <com/sun/star/frame/Desktop.hpp>
+#include <com/sun/star/frame/DispatchRecorder.hpp>
+#include <com/sun/star/frame/DispatchRecorderSupplier.hpp>
+#include <com/sun/star/frame/XLoadable.hpp>
+#include <com/sun/star/frame/XLayoutManager.hpp>
+#include <com/sun/star/frame/XComponentLoader.hpp>
+#include <com/sun/star/task/PasswordContainer.hpp>
+#include <officecfg/Office/Common.hxx>
+#include <officecfg/Setup.hxx>
+#include <toolkit/helper/vclunohelper.hxx>
+#include <vcl/wrkwin.hxx>
+#include <unotools/moduleoptions.hxx>
+#include <svl/intitem.hxx>
+#include <svl/visitem.hxx>
+#include <svl/stritem.hxx>
+#include <svl/eitem.hxx>
+#include <svl/whiter.hxx>
+#include <svl/undo.hxx>
+#include <vcl/help.hxx>
+#include <vcl/stdtext.hxx>
+#include <vcl/weld.hxx>
+#include <vcl/weldutils.hxx>
+#if !ENABLE_WASM_STRIP_PINGUSER
+#include <unotools/VersionConfig.hxx>
+#endif
+#include <unotools/securityoptions.hxx>
+#include <svtools/miscopt.hxx>
+#include <comphelper/diagnose_ex.hxx>
+#include <com/sun/star/container/XIndexAccess.hpp>
+#include <com/sun/star/frame/XFramesSupplier.hpp>
+#include <com/sun/star/frame/FrameSearchFlag.hpp>
+#include <com/sun/star/frame/XFrame.hpp>
+#include <com/sun/star/awt/XWindow.hpp>
+#include <com/sun/star/frame/XController.hpp>
+#include <com/sun/star/util/URLTransformer.hpp>
+#include <com/sun/star/util/XURLTransformer.hpp>
+#include <com/sun/star/util/XCloseable.hpp>
+#include <com/sun/star/frame/XDispatchRecorderSupplier.hpp>
+#include <com/sun/star/document/UpdateDocMode.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/uri/UriReferenceFactory.hpp>
+#include <com/sun/star/uri/XVndSunStarScriptUrl.hpp>
+#include <com/sun/star/document/XViewDataSupplier.hpp>
+#include <com/sun/star/container/XIndexContainer.hpp>
+#include <com/sun/star/task/InteractionHandler.hpp>
+#include <com/sun/star/drawing/XDrawView.hpp>
+#include <com/sun/star/drawing/XDrawPagesSupplier.hpp>
+#include <rtl/ustrbuf.hxx>
+#include <sal/log.hxx>
+
+#include <unotools/ucbhelper.hxx>
+#include <comphelper/lok.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/namedvaluecollection.hxx>
+#include <comphelper/docpasswordrequest.hxx>
+#include <comphelper/docpasswordhelper.hxx>
+
+#include <com/sun/star/uno/Reference.h>
+
+#include <basic/basmgr.hxx>
+#include <basic/sbmod.hxx>
+#include <basic/sbmeth.hxx>
+#include <svtools/strings.hrc>
+#include <svtools/svtresid.hxx>
+#include <framework/framelistanalyzer.hxx>
+
+#include <optional>
+
+#include <comphelper/sequenceashashmap.hxx>
+
+#include <commandpopup/CommandPopup.hxx>
+
+// Due to ViewFrame::Current
+#include <appdata.hxx>
+#include <sfx2/app.hxx>
+#include <sfx2/objface.hxx>
+#include <openflag.hxx>
+#include <objshimp.hxx>
+#include <sfx2/viewsh.hxx>
+#include <sfx2/objsh.hxx>
+#include <sfx2/bindings.hxx>
+#include <sfx2/dispatch.hxx>
+#include <sfx2/request.hxx>
+#include <sfx2/docfac.hxx>
+#include <sfx2/ipclient.hxx>
+#include <sfx2/sfxresid.hxx>
+#include <sfx2/viewfac.hxx>
+#include <sfx2/event.hxx>
+#include <sfx2/fcontnr.hxx>
+#include <sfx2/docfile.hxx>
+#include <sfx2/module.hxx>
+#include <sfx2/sfxuno.hxx>
+#include <sfx2/progress.hxx>
+#include <sfx2/sidebar/Sidebar.hxx>
+#include <workwin.hxx>
+#include <sfx2/minfitem.hxx>
+#include <sfx2/strings.hrc>
+#include "impviewframe.hxx"
+#include <vcl/commandinfoprovider.hxx>
+#include <vcl/svapp.hxx>
+
+#define ShellClass_SfxViewFrame
+#include <sfxslots.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::ucb;
+using namespace ::com::sun::star::frame;
+using namespace ::com::sun::star::lang;
+using ::com::sun::star::awt::XWindow;
+using ::com::sun::star::beans::PropertyValue;
+using ::com::sun::star::document::XViewDataSupplier;
+using ::com::sun::star::container::XIndexContainer;
+
+constexpr OUString CHANGES_STR = u"private:resource/toolbar/changes"_ustr;
+
+SFX_IMPL_SUPERCLASS_INTERFACE(SfxViewFrame,SfxShell)
+
+void SfxViewFrame::InitInterface_Impl()
+{
+ GetStaticInterface()->RegisterChildWindow(SID_BROWSER);
+ GetStaticInterface()->RegisterChildWindow(SID_RECORDING_FLOATWINDOW);
+#if HAVE_FEATURE_DESKTOP
+ GetStaticInterface()->RegisterObjectBar(SFX_OBJECTBAR_FULLSCREEN, SfxVisibilityFlags::FullScreen, ToolbarId::FullScreenToolbox);
+ GetStaticInterface()->RegisterObjectBar(SFX_OBJECTBAR_APPLICATION, SfxVisibilityFlags::Standard, ToolbarId::EnvToolbox);
+#endif
+}
+
+namespace {
+/// Asks the user if editing a read-only document is really wanted.
+class SfxEditDocumentDialog : public weld::MessageDialogController
+{
+public:
+ SfxEditDocumentDialog(weld::Widget* pParent);
+};
+
+SfxEditDocumentDialog::SfxEditDocumentDialog(weld::Widget* pParent)
+ : MessageDialogController(pParent, "sfx/ui/editdocumentdialog.ui",
+ "EditDocumentDialog")
+{
+}
+
+class SfxQueryOpenAsTemplate
+{
+private:
+ std::unique_ptr<weld::MessageDialog> m_xQueryBox;
+public:
+ SfxQueryOpenAsTemplate(weld::Window* pParent, bool bAllowIgnoreLock, LockFileEntry& rLockData)
+ : m_xQueryBox(Application::CreateMessageDialog(pParent, VclMessageType::Question,
+ VclButtonsType::NONE, ""))
+ {
+ m_xQueryBox->add_button(SfxResId(STR_QUERY_OPENASTEMPLATE_OPENCOPY_BTN), RET_YES);
+ bAllowIgnoreLock
+ = bAllowIgnoreLock && officecfg::Office::Common::Misc::AllowOverrideLocking::get();
+ if (bAllowIgnoreLock)
+ m_xQueryBox->add_button(SfxResId(STR_QUERY_OPENASTEMPLATE_OPEN_BTN), RET_IGNORE);
+ m_xQueryBox->add_button(GetStandardText( StandardButtonType::Cancel ), RET_CANCEL);
+ m_xQueryBox->set_primary_text(QueryString(bAllowIgnoreLock, rLockData));
+ m_xQueryBox->set_default_response(RET_YES);
+ }
+ short run() { return m_xQueryBox->run(); }
+
+private:
+ static OUString QueryString(bool bAllowIgnoreLock, LockFileEntry& rLockData)
+ {
+ OUString sLockUserData;
+ if (!rLockData[LockFileComponent::OOOUSERNAME].isEmpty())
+ sLockUserData = rLockData[LockFileComponent::OOOUSERNAME];
+ else
+ sLockUserData = rLockData[LockFileComponent::SYSUSERNAME];
+
+ if (!sLockUserData.isEmpty() && !rLockData[LockFileComponent::EDITTIME].isEmpty())
+ sLockUserData += " ( " + rLockData[LockFileComponent::EDITTIME] + " )";
+
+ if (!sLockUserData.isEmpty())
+ sLockUserData = "\n\n" + sLockUserData + "\n";
+
+ const bool bUseLockStr = bAllowIgnoreLock || !sLockUserData.isEmpty();
+
+ OUString sMsg(
+ SfxResId(bUseLockStr ? STR_QUERY_OPENASTEMPLATE_LOCKED : STR_QUERY_OPENASTEMPLATE));
+
+ if (bAllowIgnoreLock)
+ sMsg += "\n\n" + SfxResId(STR_QUERY_OPENASTEMPLATE_ALLOW_IGNORE);
+
+ return sMsg.replaceFirst("%LOCKINFO", sLockUserData);
+ }
+};
+
+bool AskPasswordToModify_Impl( const uno::Reference< task::XInteractionHandler >& xHandler, const OUString& aPath, const std::shared_ptr<const SfxFilter>& pFilter, sal_uInt32 nPasswordHash, const uno::Sequence< beans::PropertyValue >& aInfo )
+{
+ // TODO/LATER: In future the info should replace the direct hash completely
+ bool bResult = ( !nPasswordHash && !aInfo.hasElements() );
+
+ SAL_WARN_IF( !(pFilter && ( pFilter->GetFilterFlags() & SfxFilterFlags::PASSWORDTOMODIFY )), "sfx.view",
+ "PasswordToModify feature is active for a filter that does not support it!");
+
+ if ( pFilter && xHandler.is() )
+ {
+ bool bCancel = false;
+ bool bFirstTime = true;
+
+ while ( !bResult && !bCancel )
+ {
+ bool bMSType = !pFilter->IsOwnFormat();
+
+ ::rtl::Reference< ::comphelper::DocPasswordRequest > pPasswordRequest(
+ new ::comphelper::DocPasswordRequest(
+ bMSType ? ::comphelper::DocPasswordRequestType::MS : ::comphelper::DocPasswordRequestType::Standard,
+ bFirstTime ? css::task::PasswordRequestMode_PASSWORD_ENTER : css::task::PasswordRequestMode_PASSWORD_REENTER,
+ aPath,
+ true ) );
+
+ xHandler->handle( pPasswordRequest );
+
+ if ( pPasswordRequest->isPassword() )
+ {
+ if ( aInfo.hasElements() )
+ {
+ bResult = ::comphelper::DocPasswordHelper::IsModifyPasswordCorrect( pPasswordRequest->getPasswordToModify(), aInfo );
+ }
+ else
+ {
+ // the binary format
+ bResult = ( SfxMedium::CreatePasswordToModifyHash( pPasswordRequest->getPasswordToModify(), pFilter->GetServiceName()=="com.sun.star.text.TextDocument" ) == nPasswordHash );
+ }
+ }
+ else
+ bCancel = true;
+
+ bFirstTime = false;
+ }
+ }
+
+ return bResult;
+}
+
+bool physObjIsOlder(INetURLObject const & aMedObj, INetURLObject const & aPhysObj) {
+ return ::utl::UCBContentHelper::IsYounger(aMedObj.GetMainURL( INetURLObject::DecodeMechanism::NONE),
+ aPhysObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ) );
+}
+}
+
+void SfxViewFrame::ExecReload_Impl( SfxRequest& rReq )
+{
+ SfxObjectShell* pSh = GetObjectShell();
+ switch ( rReq.GetSlot() )
+ {
+ case SID_EDITDOC:
+ case SID_READONLYDOC:
+ {
+ // Due to Double occupancy in toolboxes (with or without Ctrl),
+ // it is also possible that the slot is enabled, but Ctrl-click
+ // despite this is not!
+ if( !pSh || !pSh->HasName() || !(pSh->Get_Impl()->nLoadedFlags & SfxLoadedFlags::MAINDOCUMENT ))
+ break;
+
+ if (pSh->isEditDocLocked())
+ break;
+
+ // Only change read-only UI and remove info bar when we succeed
+ struct ReadOnlyUIGuard
+ {
+ SfxViewFrame* m_pFrame;
+ SfxObjectShell* m_pSh;
+ SfxMedium* m_pMed = nullptr;
+ bool m_bSetRO;
+ ReadOnlyUIGuard(SfxViewFrame* pFrame, SfxObjectShell* p_Sh)
+ : m_pFrame(pFrame), m_pSh(p_Sh), m_bSetRO(p_Sh->IsReadOnlyUI())
+ {}
+ ~ReadOnlyUIGuard() COVERITY_NOEXCEPT_FALSE
+ {
+ if (m_bSetRO != m_pSh->IsReadOnlyUI())
+ {
+ m_pSh->SetReadOnlyUI(m_bSetRO);
+ if (!m_bSetRO)
+ m_pFrame->RemoveInfoBar(u"readonly");
+ if (m_pMed)
+ {
+ bool const isEnableSetModified(m_pSh->IsEnableSetModified());
+ m_pSh->EnableSetModified(false);
+ // tdf#116066: DoSaveCompleted should be called after SetReadOnlyUI
+ m_pSh->DoSaveCompleted(m_pMed);
+ m_pSh->Broadcast(SfxHint(SfxHintId::ModeChanged));
+ m_pSh->EnableSetModified(isEnableSetModified);
+ }
+ }
+ }
+ } aReadOnlyUIGuard(this, pSh);
+
+ SfxMedium* pMed = pSh->GetMedium();
+
+ std::shared_ptr<std::recursive_mutex> pChkEditMutex = pMed->GetCheckEditableMutex();
+ std::unique_lock<std::recursive_mutex> chkEditLock;
+ if (pChkEditMutex != nullptr)
+ chkEditLock = std::unique_lock<std::recursive_mutex>(*pChkEditMutex);
+ pMed->CancelCheckEditableEntry();
+
+ const SfxBoolItem* pItem = pMed->GetItemSet().GetItem(SID_VIEWONLY, false);
+ if ( pItem && pItem->GetValue() )
+ {
+ SfxApplication* pApp = SfxGetpApp();
+ SfxAllItemSet aSet( pApp->GetPool() );
+ aSet.Put( SfxStringItem( SID_FILE_NAME, pMed->GetURLObject().GetMainURL(INetURLObject::DecodeMechanism::NONE) ) );
+ aSet.Put( SfxBoolItem( SID_TEMPLATE, true ) );
+ aSet.Put( SfxStringItem( SID_TARGETNAME, "_blank" ) );
+ const SfxStringItem* pReferer = pMed->GetItemSet().GetItem(SID_REFERER, false);
+ if ( pReferer )
+ aSet.Put( *pReferer );
+ const SfxInt16Item* pVersionItem = pMed->GetItemSet().GetItem(SID_VERSION, false);
+ if ( pVersionItem )
+ aSet.Put( *pVersionItem );
+
+ if( pMed->GetFilter() )
+ {
+ aSet.Put( SfxStringItem( SID_FILTER_NAME, pMed->GetFilter()->GetFilterName() ) );
+ const SfxStringItem* pOptions = pMed->GetItemSet().GetItem(SID_FILE_FILTEROPTIONS, false);
+ if ( pOptions )
+ aSet.Put( *pOptions );
+ }
+
+ GetDispatcher()->Execute( SID_OPENDOC, SfxCallMode::ASYNCHRON, aSet );
+ return;
+ }
+
+ StreamMode nOpenMode;
+ bool bNeedsReload = false;
+ bool bPasswordEntered = false;
+ if ( !pSh->IsReadOnly() )
+ {
+ // Save and reload Readonly
+ if( pSh->IsModified() )
+ {
+ if ( pSh->PrepareClose() )
+ {
+ // the storing could let the medium be changed
+ pMed = pSh->GetMedium();
+ bNeedsReload = true;
+ }
+ else
+ {
+ rReq.SetReturnValue( SfxBoolItem( rReq.GetSlot(), false ) );
+ return;
+ }
+ }
+ nOpenMode = SFX_STREAM_READONLY;
+ aReadOnlyUIGuard.m_bSetRO = true;
+ }
+ else
+ {
+ if ( pSh->IsReadOnlyMedium()
+ && ( pSh->GetModifyPasswordHash() || pSh->GetModifyPasswordInfo().hasElements() )
+ && !pSh->IsModifyPasswordEntered() )
+ {
+ const OUString aDocumentName = INetURLObject( pMed->GetOrigURL() ).GetMainURL( INetURLObject::DecodeMechanism::WithCharset );
+ if( !AskPasswordToModify_Impl( pMed->GetInteractionHandler(), aDocumentName, pMed->GetFilter(), pSh->GetModifyPasswordHash(), pSh->GetModifyPasswordInfo() ) )
+ {
+ // this is a read-only document, if it has "Password to modify"
+ // the user should enter password before he can edit the document
+ rReq.SetReturnValue( SfxBoolItem( rReq.GetSlot(), false ) );
+ return;
+ }
+
+ pSh->SetModifyPasswordEntered();
+ bPasswordEntered = true;
+ }
+
+ nOpenMode = pSh->IsOriginallyReadOnlyMedium() ? SFX_STREAM_READONLY : SFX_STREAM_READWRITE;
+ aReadOnlyUIGuard.m_bSetRO = false;
+
+ // if only the view was in the readonly mode then there is no need to do the reload
+ if ( !pSh->IsReadOnlyMedium() )
+ {
+ // SetReadOnlyUI causes recomputation of window title, using
+ // open mode among other things, so call SetOpenMode before
+ // SetReadOnlyUI:
+ pMed->SetOpenMode( nOpenMode );
+ return;
+ }
+ }
+
+ if ( rReq.IsAPI() )
+ {
+ // Control through API if r/w or r/o
+ const SfxBoolItem* pEditItem = rReq.GetArg<SfxBoolItem>(SID_EDITDOC);
+ if ( pEditItem )
+ nOpenMode = pEditItem->GetValue() ? SFX_STREAM_READWRITE : SFX_STREAM_READONLY;
+ }
+
+ // doing
+
+ OUString sTemp;
+ osl::FileBase::getFileURLFromSystemPath( pMed->GetPhysicalName(), sTemp );
+ INetURLObject aPhysObj( sTemp );
+ const SfxInt16Item* pVersionItem = pMed->GetItemSet().GetItem(SID_VERSION, false);
+
+ INetURLObject aMedObj( pMed->GetName() );
+
+ // -> tdf#82744
+ // the logic below is following:
+ // if the document seems not to need to be reloaded
+ // and the physical name is different to the logical one,
+ // then on file system it can be checked that the copy is still newer than the original and no document reload is required.
+ // Did some semplification to enhance readability of the 'if' expression
+ //
+ // when the 'http/https' protocol is active, the bool bPhysObjIsYounger relies upon the getlastmodified Property of a WebDAV resource.
+ // Said property should be implemented, but sometimes it's not.
+ // implemented. On this case the reload activated here will not work properly.
+ // TODO: change the check age method for WebDAV to etag (entity-tag) property value, need some rethinking, since the
+ // etag tells that the cache representation (e.g. in LO) is different from the one on the server,
+ // but tells nothing about the age
+ // Details at this link: http://tools.ietf.org/html/rfc4918#section-15, section 15.7
+ bool bIsWebDAV = aMedObj.isAnyKnownWebDAVScheme();
+
+ // tdf#118938 Reload the document when the user enters the editing password,
+ // even if the physical name isn't different to the logical name.
+ if ( ( !bNeedsReload && ( ( aMedObj.GetProtocol() == INetProtocol::File &&
+ ( aMedObj.getFSysPath( FSysStyle::Detect ) != aPhysObj.getFSysPath( FSysStyle::Detect )
+ || bPasswordEntered ) &&
+ !physObjIsOlder(aMedObj, aPhysObj))
+ || (bIsWebDAV && !physObjIsOlder(aMedObj, aPhysObj))
+ || ( pMed->IsRemote() && !bIsWebDAV ) ) )
+ || pVersionItem )
+ // <- tdf#82744
+ {
+ bool bOK = false;
+ bool bRetryIgnoringLock = false;
+ bool bOpenTemplate = false;
+ std::optional<bool> aOrigROVal;
+ if (!pVersionItem)
+ {
+ auto pRO = pMed->GetItemSet().GetItem<SfxBoolItem>(SID_DOC_READONLY, false);
+ if (pRO)
+ aOrigROVal = pRO->GetValue();
+ }
+ do {
+ LockFileEntry aLockData;
+ if ( !pVersionItem )
+ {
+ if (bRetryIgnoringLock)
+ pMed->ResetError();
+
+ bool bHasStorage = pMed->HasStorage_Impl();
+ // switching edit mode could be possible without reload
+ if ( bHasStorage && pMed->GetStorage() == pSh->GetStorage() )
+ {
+ // TODO/LATER: faster creation of copy
+ if ( !pSh->ConnectTmpStorage_Impl( pMed->GetStorage(), pMed ) )
+ return;
+ }
+
+ pMed->CloseAndRelease();
+ pMed->SetOpenMode( nOpenMode );
+ // We need to clear the SID_DOC_READONLY item from the set, to allow
+ // MediaDescriptor::impl_openStreamWithURL (called indirectly by
+ // SfxMedium::CompleteReOpen) to properly fill input stream of the
+ // descriptor, even when the file can't be open in read-write mode.
+ // Only then can following call to SfxMedium::LockOrigFileOnDemand
+ // return proper information about who has locked the file, to show
+ // in the SfxQueryOpenAsTemplate box below; otherwise it exits right
+ // after call to SfxMedium::GetMedium_Impl. This mimics what happens
+ // when the file is opened initially, when filter detection code also
+ // calls MediaDescriptor::impl_openStreamWithURL without the item set.
+ pMed->GetItemSet().ClearItem(SID_DOC_READONLY);
+ pMed->CompleteReOpen();
+ pMed->GetItemSet().Put(
+ SfxBoolItem(SID_DOC_READONLY, !(nOpenMode & StreamMode::WRITE)));
+ if ( nOpenMode & StreamMode::WRITE )
+ {
+ auto eResult = pMed->LockOrigFileOnDemand(
+ true, true, bRetryIgnoringLock, &aLockData);
+ bRetryIgnoringLock
+ = eResult == SfxMedium::LockFileResult::FailedLockFile;
+ }
+
+ // LockOrigFileOnDemand might set the readonly flag itself, it should be set back
+ pMed->GetItemSet().Put( SfxBoolItem( SID_DOC_READONLY, !( nOpenMode & StreamMode::WRITE ) ) );
+
+ if ( !pMed->GetErrorCode() )
+ bOK = true;
+ }
+
+ if( !bOK )
+ {
+ if (nOpenMode == SFX_STREAM_READWRITE && !rReq.IsAPI())
+ {
+ // css::sdbcx::User offering to open it as a template
+ SfxQueryOpenAsTemplate aBox(GetWindow().GetFrameWeld(),
+ bRetryIgnoringLock, aLockData);
+
+ short nUserAnswer = aBox.run();
+ bOpenTemplate = RET_YES == nUserAnswer;
+ // Always reset this here to avoid infinite loop
+ bRetryIgnoringLock = RET_IGNORE == nUserAnswer;
+ if (RET_CANCEL == nUserAnswer)
+ pMed->AddToCheckEditableWorkerList();
+ }
+ else
+ bRetryIgnoringLock = false;
+ }
+ }
+ while ( !bOK && bRetryIgnoringLock );
+
+ if( !bOK )
+ {
+ ErrCodeMsg nErr = pMed->GetErrorCode();
+ if ( pVersionItem )
+ nErr = ERRCODE_IO_ACCESSDENIED;
+ else
+ {
+ pMed->ResetError();
+ pMed->SetOpenMode( SFX_STREAM_READONLY );
+ if (aOrigROVal)
+ pMed->GetItemSet().Put(SfxBoolItem(SID_DOC_READONLY, *aOrigROVal));
+ else
+ pMed->GetItemSet().ClearItem(SID_DOC_READONLY);
+ pMed->ReOpen();
+ pSh->DoSaveCompleted( pMed );
+ }
+
+ // Readonly document can not be switched to edit mode?
+ rReq.Done();
+
+ if ( nOpenMode == SFX_STREAM_READWRITE && !rReq.IsAPI() )
+ {
+ if ( bOpenTemplate )
+ {
+ SfxApplication* pApp = SfxGetpApp();
+ SfxAllItemSet aSet( pApp->GetPool() );
+ aSet.Put( SfxStringItem( SID_FILE_NAME, pMed->GetName() ) );
+ const SfxStringItem* pReferer = pMed->GetItemSet().GetItem(SID_REFERER, false);
+ if ( pReferer )
+ aSet.Put( *pReferer );
+ aSet.Put( SfxBoolItem( SID_TEMPLATE, true ) );
+ if ( pVersionItem )
+ aSet.Put( *pVersionItem );
+
+ if( pMed->GetFilter() )
+ {
+ aSet.Put( SfxStringItem( SID_FILTER_NAME, pMed->GetFilter()->GetFilterName() ) );
+ const SfxStringItem* pOptions = pMed->GetItemSet().GetItem(SID_FILE_FILTEROPTIONS, false);
+ if ( pOptions )
+ aSet.Put( *pOptions );
+ }
+
+ GetDispatcher()->Execute( SID_OPENDOC, SfxCallMode::ASYNCHRON, aSet );
+ return;
+ }
+
+ nErr = ERRCODE_NONE;
+ }
+
+ // Keep the read-only UI
+ aReadOnlyUIGuard.m_bSetRO = true;
+
+ ErrorHandler::HandleError( nErr );
+ rReq.SetReturnValue(
+ SfxBoolItem( rReq.GetSlot(), false ) );
+ return;
+ }
+ else
+ {
+ aReadOnlyUIGuard.m_pMed = pMed;
+ rReq.SetReturnValue( SfxBoolItem( rReq.GetSlot(), true ) );
+ rReq.Done( true );
+ return;
+ }
+ }
+
+ rReq.AppendItem( SfxBoolItem(SID_FORCERELOAD,
+ (rReq.GetSlot() == SID_EDITDOC
+ // tdf#151715 exclude files loaded from /tmp to avoid Notebookbar bugs
+ && (!pSh->IsOriginallyReadOnlyMedium() || pSh->IsOriginallyLoadedReadOnlyMedium()))
+ || bNeedsReload) );
+ rReq.AppendItem( SfxBoolItem( SID_SILENT, true ));
+
+ [[fallthrough]]; //TODO ???
+ }
+
+ case SID_RELOAD:
+ {
+ // Due to Double occupancy in toolboxes (with or without Ctrl),
+ // it is also possible that the slot is enabled, but Ctrl-click
+ // despite this is not!
+ if ( !pSh || !pSh->CanReload_Impl() )
+ break;
+ SfxApplication* pApp = SfxGetpApp();
+ const SfxBoolItem* pForceReloadItem = rReq.GetArg<SfxBoolItem>(SID_FORCERELOAD);
+ if( pForceReloadItem && !pForceReloadItem->GetValue() &&
+ !pSh->GetMedium()->IsExpired() )
+ return;
+ if( m_pImpl->bReloading || pSh->IsInModalMode() )
+ return;
+
+ // AutoLoad is prohibited if possible
+ const SfxBoolItem* pAutoLoadItem = rReq.GetArg<SfxBoolItem>(SID_AUTOLOAD);
+ if ( pAutoLoadItem && pAutoLoadItem->GetValue() &&
+ GetFrame().IsAutoLoadLocked_Impl() )
+ return;
+
+ SfxObjectShellLock xOldObj( pSh );
+ m_pImpl->bReloading = true;
+ const SfxStringItem* pURLItem = rReq.GetArg<SfxStringItem>(SID_FILE_NAME);
+ // Open as editable?
+ bool bForEdit = !pSh->IsReadOnly();
+
+ // If possible ask the User
+ bool bDo = GetViewShell()->PrepareClose();
+ const SfxBoolItem* pSilentItem = rReq.GetArg<SfxBoolItem>(SID_SILENT);
+ if (getenv("SAL_NO_QUERYSAVE"))
+ bDo = true;
+ else if (bDo && GetFrame().DocIsModified_Impl() && !rReq.IsAPI()
+ && (!pSilentItem || !pSilentItem->GetValue()))
+ {
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(
+ GetWindow().GetFrameWeld(), VclMessageType::Question, VclButtonsType::YesNo,
+ SfxResId(STR_QUERY_LASTVERSION)));
+ bDo = RET_YES == xBox->run();
+ }
+
+ if ( bDo )
+ {
+ SfxMedium *pMedium = xOldObj->GetMedium();
+ std::shared_ptr<std::recursive_mutex> pChkEditMutex
+ = pMedium->GetCheckEditableMutex();
+ std::unique_lock<std::recursive_mutex> chkEditLock;
+ if (pChkEditMutex != nullptr)
+ chkEditLock = std::unique_lock<std::recursive_mutex>(*pChkEditMutex);
+ pMedium->CancelCheckEditableEntry();
+
+ bool bHandsOff =
+ ( pMedium->GetURLObject().GetProtocol() == INetProtocol::File && !xOldObj->IsDocShared() );
+
+ // Empty existing SfxMDIFrames for this Document
+ // in native format or R/O, open it now for editing?
+ SfxObjectShellLock xNewObj;
+
+ // collect the views of the document
+ // TODO: when UNO ViewFactories are available for SFX-based documents, the below code should
+ // be UNOized, too
+ typedef ::std::pair< Reference< XFrame >, SfxInterfaceId > ViewDescriptor;
+ ::std::vector< ViewDescriptor > aViewFrames;
+ SfxViewFrame *pView = GetFirst( xOldObj );
+ while ( pView )
+ {
+ Reference< XFrame > xFrame( pView->GetFrame().GetFrameInterface() );
+ SAL_WARN_IF( !xFrame.is(), "sfx.view", "SfxViewFrame::ExecReload_Impl: no XFrame?!");
+ aViewFrames.emplace_back( xFrame, pView->GetCurViewId() );
+
+ pView = GetNext( *pView, xOldObj );
+ }
+
+ xOldObj->Get_Impl()->pReloadTimer.reset();
+
+ std::optional<SfxAllItemSet> pNewSet;
+ std::shared_ptr<const SfxFilter> pFilter = pMedium->GetFilter();
+ if( pURLItem )
+ {
+ pNewSet.emplace( pApp->GetPool() );
+ pNewSet->Put( *pURLItem );
+
+ // Filter Detection
+ OUString referer;
+ const SfxStringItem* refererItem = rReq.GetArg<SfxStringItem>(SID_REFERER);
+ if (refererItem != nullptr) {
+ referer = refererItem->GetValue();
+ }
+ SfxMedium aMedium( pURLItem->GetValue(), referer, SFX_STREAM_READWRITE );
+ SfxFilterMatcher().GuessFilter( aMedium, pFilter );
+ if ( pFilter )
+ pNewSet->Put( SfxStringItem( SID_FILTER_NAME, pFilter->GetName() ) );
+ pNewSet->Put( aMedium.GetItemSet() );
+ }
+ else
+ {
+ pNewSet.emplace( pMedium->GetItemSet() );
+ pNewSet->ClearItem( SID_VIEW_ID );
+ pNewSet->ClearItem( SID_STREAM );
+ pNewSet->ClearItem( SID_INPUTSTREAM );
+ pNewSet->Put( SfxStringItem( SID_FILTER_NAME, pMedium->GetFilter()->GetName() ) );
+
+ // let the current security settings be checked again
+ pNewSet->Put( SfxUInt16Item( SID_MACROEXECMODE, document::MacroExecMode::USE_CONFIG ) );
+
+ if ( pSh->IsOriginallyReadOnlyMedium()
+ || pSh->IsOriginallyLoadedReadOnlyMedium() )
+ // edit mode is switched or reload of readonly document
+ pNewSet->Put( SfxBoolItem( SID_DOC_READONLY, true ) );
+ else
+ // Reload of file opened for writing
+ pNewSet->ClearItem( SID_DOC_READONLY );
+ }
+
+ // If a salvaged file is present, do not enclose the OrigURL
+ // again, since the Template is invalid after reload.
+ const SfxStringItem* pSalvageItem = SfxItemSet::GetItem<SfxStringItem>(&*pNewSet, SID_DOC_SALVAGE, false);
+ if( pSalvageItem )
+ {
+ pNewSet->ClearItem( SID_DOC_SALVAGE );
+ }
+
+#if HAVE_FEATURE_MULTIUSER_ENVIRONMENT
+ // TODO/LATER: Temporary solution, the SfxMedium must know the original URL as aLogicName
+ // SfxMedium::Transfer_Impl() will be forbidden then.
+ if ( xOldObj->IsDocShared() )
+ pNewSet->Put( SfxStringItem( SID_FILE_NAME, xOldObj->GetSharedFileURL() ) );
+#endif
+ if ( pURLItem )
+ pNewSet->Put( SfxStringItem( SID_REFERER, pMedium->GetName() ) );
+ else
+ pNewSet->Put( SfxStringItem( SID_REFERER, OUString() ) );
+
+ xOldObj->CancelTransfers();
+
+
+ if ( pSilentItem && pSilentItem->GetValue() )
+ pNewSet->Put( SfxBoolItem( SID_SILENT, true ) );
+
+ const SfxUnoAnyItem* pInteractionItem = SfxItemSet::GetItem<SfxUnoAnyItem>(&*pNewSet, SID_INTERACTIONHANDLER, false);
+ const SfxUInt16Item* pMacroExecItem = SfxItemSet::GetItem<SfxUInt16Item>(&*pNewSet, SID_MACROEXECMODE, false);
+ const SfxUInt16Item* pDocTemplateItem = SfxItemSet::GetItem<SfxUInt16Item>(&*pNewSet, SID_UPDATEDOCMODE, false);
+
+ if (!pInteractionItem)
+ {
+ Reference < task::XInteractionHandler2 > xHdl = task::InteractionHandler::createWithParent( ::comphelper::getProcessComponentContext(), nullptr );
+ if (xHdl.is())
+ pNewSet->Put( SfxUnoAnyItem(SID_INTERACTIONHANDLER,css::uno::Any(xHdl)) );
+ }
+
+ if (!pMacroExecItem)
+ pNewSet->Put( SfxUInt16Item(SID_MACROEXECMODE,css::document::MacroExecMode::USE_CONFIG) );
+ if (!pDocTemplateItem)
+ pNewSet->Put( SfxUInt16Item(SID_UPDATEDOCMODE,css::document::UpdateDocMode::ACCORDING_TO_CONFIG) );
+
+ xOldObj->SetModified( false );
+ // Do not cache the old Document! Is invalid when loading
+ // another document.
+
+ bool bHasStorage = pMedium->HasStorage_Impl();
+ if( bHandsOff )
+ {
+ if ( bHasStorage && pMedium->GetStorage() == xOldObj->GetStorage() )
+ {
+ // TODO/LATER: faster creation of copy
+ if ( !xOldObj->ConnectTmpStorage_Impl( pMedium->GetStorage(), pMedium ) )
+ return;
+ }
+
+ pMedium->CloseAndRelease();
+ }
+
+ xNewObj = SfxObjectShell::CreateObject( pFilter->GetServiceName() );
+
+ if ( xOldObj->IsModifyPasswordEntered() )
+ xNewObj->SetModifyPasswordEntered();
+
+ uno::Sequence < beans::PropertyValue > aLoadArgs;
+ TransformItems( SID_OPENDOC, *pNewSet, aLoadArgs );
+ try
+ {
+ uno::Reference < frame::XLoadable > xLoad( xNewObj->GetModel(), uno::UNO_QUERY );
+ xLoad->load( aLoadArgs );
+ }
+ catch ( uno::Exception& )
+ {
+ xNewObj->DoClose();
+ xNewObj = nullptr;
+ pMedium->AddToCheckEditableWorkerList();
+ }
+
+ pNewSet.reset();
+
+ if( !xNewObj.Is() )
+ {
+ if( bHandsOff )
+ {
+ // back to old medium
+ pMedium->ReOpen();
+ pMedium->LockOrigFileOnDemand( false, true );
+
+ xOldObj->DoSaveCompleted( pMedium );
+ }
+ }
+ else
+ {
+ if ( xNewObj->GetModifyPasswordHash() && xNewObj->GetModifyPasswordHash() != xOldObj->GetModifyPasswordHash() )
+ {
+ xNewObj->SetModifyPasswordEntered( false );
+ xNewObj->SetReadOnly();
+ }
+ else if ( rReq.GetSlot() == SID_EDITDOC || rReq.GetSlot() == SID_READONLYDOC )
+ {
+ xNewObj->SetReadOnlyUI( !bForEdit );
+ }
+
+#if HAVE_FEATURE_MULTIUSER_ENVIRONMENT
+ if ( xNewObj->IsDocShared() )
+ {
+ // the file is shared but the closing can change the sharing control file
+ xOldObj->DoNotCleanShareControlFile();
+ }
+#endif
+ // the Reload and Silent items were only temporary, remove them
+ xNewObj->GetMedium()->GetItemSet().ClearItem( SID_RELOAD );
+ xNewObj->GetMedium()->GetItemSet().ClearItem( SID_SILENT );
+ TransformItems( SID_OPENDOC, xNewObj->GetMedium()->GetItemSet(), aLoadArgs );
+
+ UpdateDocument_Impl();
+
+ auto sModule = vcl::CommandInfoProvider::GetModuleIdentifier(GetFrame().GetFrameInterface());
+ OUString sReloadNotebookBar;
+ if (sModule == "com.sun.star.text.TextDocument")
+ sReloadNotebookBar = u"modules/swriter/ui/"_ustr;
+ else if (sModule == "com.sun.star.sheet.SpreadsheetDocument")
+ sReloadNotebookBar = u"modules/scalc/ui/"_ustr;
+ else if (sfx2::SfxNotebookBar::IsActive()
+ && sModule != "presentation.PresentationDocument"
+ && sModule != "com.sun.star.drawing.DrawingDocument")
+ {
+ assert(false && "SID_RELOAD Notebookbar active, but not refreshed here");
+ }
+
+ try
+ {
+ for (auto const& viewFrame : aViewFrames)
+ {
+ LoadViewIntoFrame_Impl( *xNewObj, viewFrame.first, aLoadArgs, viewFrame.second, false );
+ }
+ aViewFrames.clear();
+ }
+ catch( const Exception& )
+ {
+ // close the remaining frames
+ // Don't catch exceptions herein, if this fails, then we're left in an indetermined state, and
+ // crashing is better than trying to proceed
+ for (auto const& viewFrame : aViewFrames)
+ {
+ Reference< util::XCloseable > xClose( viewFrame.first, UNO_QUERY_THROW );
+ xClose->close( true );
+ }
+ aViewFrames.clear();
+ }
+
+ const SfxInt32Item* pPageNumber = rReq.GetArg<SfxInt32Item>(SID_PAGE_NUMBER);
+ if (pPageNumber && pPageNumber->GetValue() >= 0)
+ {
+ // Restore current page after reload.
+ uno::Reference<drawing::XDrawView> xController(
+ xNewObj->GetModel()->getCurrentController(), uno::UNO_QUERY);
+ uno::Reference<drawing::XDrawPagesSupplier> xSupplier(xNewObj->GetModel(),
+ uno::UNO_QUERY);
+ uno::Reference<drawing::XDrawPages> xDrawPages = xSupplier->getDrawPages();
+ uno::Reference<drawing::XDrawPage> xDrawPage(
+ xDrawPages->getByIndex(pPageNumber->GetValue()), uno::UNO_QUERY);
+ xController->setCurrentPage(xDrawPage);
+ }
+
+ // Propagate document closure.
+ SfxGetpApp()->NotifyEvent( SfxEventHint( SfxEventHintId::CloseDoc, GlobalEventConfig::GetEventName( GlobalEventId::CLOSEDOC ), xOldObj ) );
+
+ // tdf#126006 Calc needs to reload the notebookbar after closing the document
+ if (!sReloadNotebookBar.isEmpty())
+ sfx2::SfxNotebookBar::ReloadNotebookBar(sReloadNotebookBar);
+ }
+
+ // Record as done
+ rReq.Done( true );
+ rReq.SetReturnValue(SfxBoolItem(rReq.GetSlot(), true));
+ return;
+ }
+ else
+ {
+ // Record as not done
+ rReq.Done();
+ rReq.SetReturnValue(SfxBoolItem(rReq.GetSlot(), false));
+ m_pImpl->bReloading = false;
+ return;
+ }
+ }
+ }
+}
+
+void SfxViewFrame::StateReload_Impl( SfxItemSet& rSet )
+{
+ SfxObjectShell* pSh = GetObjectShell();
+ if ( !pSh )
+ {
+ // I'm just on reload and am yielding myself ...
+ return;
+ }
+
+ SfxWhichIter aIter( rSet );
+ for ( sal_uInt16 nWhich = aIter.FirstWhich(); nWhich; nWhich = aIter.NextWhich() )
+ {
+ switch ( nWhich )
+ {
+ case SID_EDITDOC:
+ case SID_READONLYDOC:
+ {
+ const SfxViewShell *pVSh;
+ const SfxShell *pFSh;
+ if ( !pSh->HasName() ||
+ !( pSh->Get_Impl()->nLoadedFlags & SfxLoadedFlags::MAINDOCUMENT ) ||
+ (pSh->isEditDocLocked()) ||
+ ( pSh->GetCreateMode() == SfxObjectCreateMode::EMBEDDED &&
+ ( !(pVSh = pSh->GetViewShell()) ||
+ !(pFSh = pVSh->GetFormShell()) ||
+ !pFSh->IsDesignMode())))
+ rSet.DisableItem( nWhich );
+ else
+ {
+ const SfxBoolItem* pItem = pSh->GetMedium()->GetItemSet().GetItem(SID_EDITDOC, false);
+ if ( pItem && !pItem->GetValue() )
+ rSet.DisableItem( nWhich );
+ else
+ {
+ if (nWhich==SID_EDITDOC)
+ rSet.Put( SfxBoolItem( nWhich, !pSh->IsReadOnly() ) );
+ else if (nWhich==SID_READONLYDOC)
+ rSet.Put( SfxBoolItem( nWhich, pSh->IsReadOnly() ) );
+ }
+ }
+ break;
+ }
+
+ case SID_RELOAD:
+ {
+ if ( !pSh->CanReload_Impl() || pSh->GetCreateMode() == SfxObjectCreateMode::EMBEDDED )
+ rSet.DisableItem(nWhich);
+ else
+ {
+ // If any ChildFrame is reloadable, the slot is enabled,
+ // so you can perform CTRL-Reload
+ rSet.Put( SfxBoolItem( nWhich, false));
+ }
+
+ break;
+ }
+ }
+ }
+}
+
+void SfxViewFrame::ExecHistory_Impl( SfxRequest &rReq )
+{
+ // Is there an Undo-Manager on the top Shell?
+ SfxShell *pSh = GetDispatcher()->GetShell(0);
+ if (!pSh)
+ return;
+
+ SfxUndoManager* pShUndoMgr = pSh->GetUndoManager();
+ bool bOK = false;
+ if ( pShUndoMgr )
+ {
+ switch ( rReq.GetSlot() )
+ {
+ case SID_CLEARHISTORY:
+ pShUndoMgr->Clear();
+ bOK = true;
+ break;
+
+ case SID_UNDO:
+ pShUndoMgr->Undo();
+ GetBindings().InvalidateAll(false);
+ bOK = true;
+ break;
+
+ case SID_REDO:
+ pShUndoMgr->Redo();
+ GetBindings().InvalidateAll(false);
+ bOK = true;
+ break;
+
+ case SID_REPEAT:
+ if ( pSh->GetRepeatTarget() )
+ pShUndoMgr->Repeat( *pSh->GetRepeatTarget() );
+ bOK = true;
+ break;
+ }
+ }
+ else if ( GetViewShell() )
+ {
+ // The SW has its own undo in the View
+ const SfxPoolItemHolder& rResult(GetViewShell()->ExecuteSlot(rReq));
+ if (nullptr != rResult.getItem())
+ bOK = static_cast<const SfxBoolItem*>(rResult.getItem())->GetValue();
+ }
+
+ rReq.SetReturnValue( SfxBoolItem( rReq.GetSlot(), bOK ) );
+ rReq.Done();
+}
+
+void SfxViewFrame::StateHistory_Impl( SfxItemSet &rSet )
+{
+ // Search for Undo-Manager
+ SfxShell *pSh = GetDispatcher()->GetShell(0);
+ if ( !pSh )
+ // I'm just on reload and am yielding myself ...
+ return;
+
+ SfxUndoManager *pShUndoMgr = pSh->GetUndoManager();
+ if ( !pShUndoMgr )
+ {
+ // The SW has its own undo in the View
+ SfxWhichIter aIter( rSet );
+ SfxViewShell *pViewSh = GetViewShell();
+ if( !pViewSh ) return;
+ for ( sal_uInt16 nSID = aIter.FirstWhich(); nSID; nSID = aIter.NextWhich() )
+ pViewSh->GetSlotState( nSID, nullptr, &rSet );
+ return;
+ }
+
+ if ( pShUndoMgr->GetUndoActionCount() == 0 &&
+ pShUndoMgr->GetRedoActionCount() == 0 &&
+ pShUndoMgr->GetRepeatActionCount() == 0 )
+ rSet.DisableItem( SID_CLEARHISTORY );
+
+ if (pShUndoMgr->GetUndoActionCount())
+ {
+ const SfxUndoAction* pAction = pShUndoMgr->GetUndoAction();
+ SfxViewShell *pViewSh = GetViewShell();
+ if (pViewSh && pAction->GetViewShellId() != pViewSh->GetViewShellId())
+ {
+ rSet.Put(SfxUInt32Item(SID_UNDO, static_cast<sal_uInt32>(SID_REPAIRPACKAGE)));
+ }
+ else
+ {
+ rSet.Put( SfxStringItem( SID_UNDO, SvtResId(STR_UNDO)+pShUndoMgr->GetUndoActionComment() ) );
+ }
+ }
+ else
+ rSet.DisableItem( SID_UNDO );
+
+ if (pShUndoMgr->GetRedoActionCount())
+ {
+ const SfxUndoAction* pAction = pShUndoMgr->GetRedoAction();
+ SfxViewShell *pViewSh = GetViewShell();
+ if (pViewSh && pAction->GetViewShellId() != pViewSh->GetViewShellId())
+ {
+ rSet.Put(SfxUInt32Item(SID_REDO, static_cast<sal_uInt32>(SID_REPAIRPACKAGE)));
+ }
+ else
+ {
+ rSet.Put(SfxStringItem(SID_REDO, SvtResId(STR_REDO) + pShUndoMgr->GetRedoActionComment()));
+ }
+ }
+ else
+ rSet.DisableItem( SID_REDO );
+
+ SfxRepeatTarget *pTarget = pSh->GetRepeatTarget();
+ if (pTarget && pShUndoMgr->GetRepeatActionCount() && pShUndoMgr->CanRepeat(*pTarget))
+ rSet.Put( SfxStringItem( SID_REPEAT, SvtResId(STR_REPEAT)+pShUndoMgr->GetRepeatActionComment(*pTarget) ) );
+ else
+ rSet.DisableItem( SID_REPEAT );
+}
+
+void SfxViewFrame::PopShellAndSubShells_Impl( SfxViewShell& i_rViewShell )
+{
+ i_rViewShell.PopSubShells_Impl();
+ sal_uInt16 nLevel = m_pDispatcher->GetShellLevel( i_rViewShell );
+ if ( nLevel != USHRT_MAX )
+ {
+ if ( nLevel )
+ {
+ // more sub shells on the stack, which were not affected by PopSubShells_Impl
+ SfxShell *pSubShell = m_pDispatcher->GetShell( nLevel-1 );
+ m_pDispatcher->Pop( *pSubShell, SfxDispatcherPopFlags::POP_UNTIL | SfxDispatcherPopFlags::POP_DELETE );
+ }
+ m_pDispatcher->Pop( i_rViewShell );
+ m_pDispatcher->Flush();
+ }
+
+}
+
+/* [Description]
+
+ This method empties the SfxViewFrame, i.e. takes the <SfxObjectShell>
+ from the dispatcher and ends its <SfxListener> Relationship to this
+ SfxObjectShell (by which they may even destroy themselves).
+
+ Thus, by invoking ReleaseObjectShell() and SetObjectShell() the
+ SfxObjectShell can be replaced.
+
+ Between ReleaseObjectShell() and SetObjectShell() the control cannot
+ be handed over to the system.
+
+ [Cross-reference]
+
+ <SfxViewFrame::SetObjectShell(SfxObjectShell&)>
+*/
+void SfxViewFrame::ReleaseObjectShell_Impl()
+{
+ DBG_ASSERT( m_xObjSh.is(), "no SfxObjectShell to release!" );
+
+ GetFrame().ReleasingComponent_Impl();
+ if ( GetWindow().HasChildPathFocus( true ) )
+ {
+ GetWindow().GrabFocus();
+ }
+
+ SfxViewShell *pDyingViewSh = GetViewShell();
+ if ( pDyingViewSh )
+ {
+ PopShellAndSubShells_Impl( *pDyingViewSh );
+ pDyingViewSh->DisconnectAllClients();
+ SetViewShell_Impl(nullptr);
+ delete pDyingViewSh;
+ }
+#ifdef DBG_UTIL
+ else
+ OSL_FAIL("No Shell");
+#endif
+
+ if ( m_xObjSh.is() )
+ {
+ m_pDispatcher->Pop( *m_xObjSh );
+ SfxModule* pModule = m_xObjSh->GetModule();
+ if( pModule )
+ m_pDispatcher->RemoveShell_Impl( *pModule );
+ m_pDispatcher->Flush();
+ EndListening( *m_xObjSh );
+
+ Notify( *m_xObjSh, SfxHint(SfxHintId::TitleChanged) );
+ Notify( *m_xObjSh, SfxHint(SfxHintId::DocChanged) );
+
+ if ( 1 == m_xObjSh->GetOwnerLockCount() && m_pImpl->bObjLocked && m_xObjSh->GetCreateMode() == SfxObjectCreateMode::EMBEDDED )
+ m_xObjSh->DoClose();
+ SfxObjectShellRef xDyingObjSh = m_xObjSh;
+ m_xObjSh.clear();
+ if( GetFrame().GetHasTitle() && m_pImpl->nDocViewNo )
+ xDyingObjSh->GetNoSet_Impl().ReleaseIndex(m_pImpl->nDocViewNo-1);
+ if ( m_pImpl->bObjLocked )
+ {
+ xDyingObjSh->OwnerLock( false );
+ m_pImpl->bObjLocked = false;
+ }
+ }
+
+ GetDispatcher()->SetDisableFlags( SfxDisableFlags::NONE );
+}
+
+void SfxViewFrame::Close()
+{
+
+ DBG_ASSERT( GetFrame().IsClosing_Impl() || !GetFrame().GetFrameInterface().is(), "ViewFrame closed too early!" );
+
+ // If no saving have been made up until now, then embedded Objects should
+ // not be saved automatically anymore.
+ if ( GetViewShell() )
+ GetViewShell()->DisconnectAllClients();
+ Broadcast( SfxHint( SfxHintId::Dying ) );
+
+ if (SfxViewFrame::Current() == this)
+ SfxViewFrame::SetViewFrame( nullptr );
+
+ // Since the Dispatcher is emptied, it can not be used in any reasonable
+ // manner, thus it is better to let the dispatcher be.
+ GetDispatcher()->Lock(true);
+ delete this;
+}
+
+void SfxViewFrame::DoActivate( bool bUI )
+{
+ m_pDispatcher->DoActivate_Impl( bUI );
+}
+
+void SfxViewFrame::DoDeactivate(bool bUI, SfxViewFrame const * pNewFrame )
+{
+ m_pDispatcher->DoDeactivate_Impl( bUI, pNewFrame );
+}
+
+void SfxViewFrame::InvalidateBorderImpl( const SfxViewShell* pSh )
+{
+ if( !pSh || m_nAdjustPosPixelLock )
+ return;
+
+ if ( GetViewShell() && GetWindow().IsVisible() )
+ {
+ if ( GetFrame().IsInPlace() )
+ {
+ return;
+ }
+
+ DoAdjustPosSizePixel( GetViewShell(), Point(),
+ GetWindow().GetOutputSizePixel(),
+ false );
+ }
+}
+
+void SfxViewFrame::SetBorderPixelImpl
+(
+ const SfxViewShell* pVSh,
+ const SvBorder& rBorder
+)
+
+{
+ m_pImpl->aBorder = rBorder;
+
+ if ( m_pImpl->bResizeInToOut && !GetFrame().IsInPlace() )
+ {
+ Size aSize = pVSh->GetWindow()->GetOutputSizePixel();
+ if ( aSize.Width() && aSize.Height() )
+ {
+ aSize.AdjustWidth(rBorder.Left() + rBorder.Right() );
+ aSize.AdjustHeight(rBorder.Top() + rBorder.Bottom() );
+
+ Size aOldSize = GetWindow().GetOutputSizePixel();
+ GetWindow().SetOutputSizePixel( aSize );
+ vcl::Window* pParent = &GetWindow();
+ while ( pParent->GetParent() )
+ pParent = pParent->GetParent();
+ Size aOuterSize = pParent->GetOutputSizePixel();
+ aOuterSize.AdjustWidth( aSize.Width() - aOldSize.Width() );
+ aOuterSize.AdjustHeight( aSize.Height() - aOldSize.Height() );
+ pParent->SetOutputSizePixel( aOuterSize );
+ }
+ }
+ else
+ {
+ tools::Rectangle aEditArea( Point(), GetWindow().GetOutputSizePixel() );
+ aEditArea.AdjustLeft(rBorder.Left() );
+ aEditArea.AdjustRight( -(rBorder.Right()) );
+ aEditArea.AdjustTop(rBorder.Top() );
+ aEditArea.AdjustBottom( -(rBorder.Bottom()) );
+ pVSh->GetWindow()->SetPosSizePixel( aEditArea.TopLeft(), aEditArea.GetSize() );
+ }
+}
+
+const SvBorder& SfxViewFrame::GetBorderPixelImpl() const
+{
+ return m_pImpl->aBorder;
+}
+
+void SfxViewFrame::AppendReadOnlyInfobar()
+{
+ bool bSignPDF = m_xObjSh->IsSignPDF();
+ bool bSignWithCert = false;
+ if (bSignPDF)
+ {
+ SfxObjectShell* pObjectShell = GetObjectShell();
+ uno::Reference<security::XCertificate> xCertificate = pObjectShell->GetSignPDFCertificate();
+ bSignWithCert = xCertificate.is();
+ }
+
+ auto pInfoBar = AppendInfoBar("readonly", "",
+ SfxResId(bSignPDF ? STR_READONLY_PDF : STR_READONLY_DOCUMENT),
+ InfobarType::INFO);
+ if (!pInfoBar)
+ return;
+
+ if (bSignPDF)
+ {
+ // SID_SIGNPDF opened a read-write PDF
+ // read-only for signing purposes.
+ weld::Button& rSignButton = pInfoBar->addButton();
+ if (bSignWithCert)
+ {
+ rSignButton.set_label(SfxResId(STR_READONLY_FINISH_SIGN));
+ }
+ else
+ {
+ rSignButton.set_label(SfxResId(STR_READONLY_SIGN));
+ }
+
+ rSignButton.connect_clicked(LINK(this, SfxViewFrame, SignDocumentHandler));
+ }
+
+ bool showEditDocumentButton = true;
+ if (m_xObjSh->isEditDocLocked())
+ showEditDocumentButton = false;
+
+ if (showEditDocumentButton)
+ {
+ weld::Button& rBtn = pInfoBar->addButton();
+ rBtn.set_label(SfxResId(STR_READONLY_EDIT));
+ rBtn.connect_clicked(LINK(this, SfxViewFrame, SwitchReadOnlyHandler));
+ }
+}
+
+void SfxViewFrame::HandleSecurityInfobar(const OUString& sSecondaryMessage)
+{
+ if (!HasInfoBarWithID(u"securitywarn"))
+ {
+ // new info bar
+ if (!sSecondaryMessage.isEmpty())
+ {
+ auto pInfoBar = AppendInfoBar("securitywarn", SfxResId(STR_HIDDENINFO_CONTAINS).replaceAll("\n\n", " "),
+ sSecondaryMessage, InfobarType::WARNING);
+ if (!pInfoBar)
+ return;
+
+ weld::Button& rGetInvolvedButton = pInfoBar->addButton();
+ rGetInvolvedButton.set_label(SfxResId(STR_SECURITY_OPTIONS));
+ rGetInvolvedButton.connect_clicked(LINK(this, SfxViewFrame, SecurityButtonHandler));
+ }
+ }
+ else
+ {
+ // info bar exists already
+ if (sSecondaryMessage.isEmpty())
+ {
+ RemoveInfoBar(u"securitywarn");
+ }
+ else
+ {
+ UpdateInfoBar(u"securitywarn", SfxResId(STR_HIDDENINFO_CONTAINS).replaceAll("\n\n", " "),
+ sSecondaryMessage, InfobarType::WARNING);
+ }
+ }
+}
+
+void SfxViewFrame::AppendContainsMacrosInfobar()
+{
+ SfxObjectShell_Impl* pObjImpl = m_xObjSh->Get_Impl();
+
+ // what's the difference between pObjImpl->documentStorageHasMacros() and pObjImpl->aMacroMode.hasMacroLibrary() ?
+ bool bHasDocumentMacros = pObjImpl->aMacroMode.hasMacroLibrary();
+
+ Reference<XModel> xModel = m_xObjSh->GetModel();
+ uno::Reference<document::XEventsSupplier> xSupplier(xModel, uno::UNO_QUERY);
+ bool bHasBoundConfigEvents(false);
+ if (xSupplier.is())
+ {
+ css::uno::Reference<css::container::XNameReplace> xDocumentEvents = xSupplier->getEvents();
+
+ Sequence<OUString> eventNames = xDocumentEvents->getElementNames();
+ sal_Int32 nEventCount = eventNames.getLength();
+ for (sal_Int32 nEvent = 0; nEvent < nEventCount; ++nEvent)
+ {
+ OUString url;
+ try
+ {
+ Any aAny(xDocumentEvents->getByName(eventNames[nEvent]));
+ Sequence<beans::PropertyValue> props;
+ if (aAny >>= props)
+ {
+ ::comphelper::NamedValueCollection aProps(props);
+ url = aProps.getOrDefault("Script", url);
+ }
+ }
+ catch (const Exception&)
+ {
+ }
+ if (!url.isEmpty())
+ {
+ bHasBoundConfigEvents = true;
+ break;
+ }
+ }
+ }
+
+ if (bHasDocumentMacros || bHasBoundConfigEvents)
+ {
+ auto aResId = STR_CONTAINS_MACROS;
+ if (SvtSecurityOptions::IsMacroDisabled())
+ aResId = STR_MACROS_DISABLED;
+ else if (pObjImpl->aMacroMode.hasUnsignedContentError())
+ aResId = STR_MACROS_DISABLED_CONTENT_UNSIGNED;
+ auto pInfoBar = AppendInfoBar("macro", SfxResId(STR_MACROS_DISABLED_TITLE),
+ SfxResId(aResId), InfobarType::WARNING);
+ if (!pInfoBar)
+ return;
+
+ // No access to macro dialog when macros are disabled globally.
+ if (SvtSecurityOptions::IsMacroDisabled())
+ return;
+
+ if (bHasDocumentMacros)
+ {
+ weld::Button& rMacroButton = pInfoBar->addButton();
+ rMacroButton.set_label(SfxResId(STR_MACROS));
+ rMacroButton.connect_clicked(LINK(this, SfxViewFrame, MacroButtonHandler));
+ }
+
+ if (bHasBoundConfigEvents)
+ {
+ weld::Button& rEventButton = pInfoBar->addButton();
+ rEventButton.set_label(SfxResId(STR_EVENTS));
+ rEventButton.connect_clicked(LINK(this, SfxViewFrame, EventButtonHandler));
+ }
+ }
+}
+
+namespace
+{
+css::uno::Reference<css::frame::XLayoutManager> getLayoutManager(const SfxFrame& rFrame)
+{
+ css::uno::Reference<css::frame::XLayoutManager> xLayoutManager;
+ css::uno::Reference<css::beans::XPropertySet> xPropSet(rFrame.GetFrameInterface(),
+ uno::UNO_QUERY);
+ if (xPropSet.is())
+ {
+ try
+ {
+ xLayoutManager.set(xPropSet->getPropertyValue("LayoutManager"), uno::UNO_QUERY);
+ }
+ catch (const Exception& e)
+ {
+ SAL_WARN("sfx.view", "Failure getting layout manager: " + e.Message);
+ }
+ }
+ return xLayoutManager;
+}
+}
+
+bool SfxApplication::IsHeadlessOrUITest()
+{
+ if (Application::IsHeadlessModeEnabled())
+ return true;
+
+ bool bIsUITest = false; //uitest.uicheck fails when the dialog is open
+ for (sal_uInt16 i = 0, nCount = Application::GetCommandLineParamCount(); i < nCount; ++i)
+ {
+ if (Application::GetCommandLineParam(i) == "--nologo")
+ {
+ bIsUITest = true;
+ break;
+ }
+ }
+ return bIsUITest;
+}
+
+bool SfxApplication::IsTipOfTheDayDue()
+{
+ const bool bShowTipOfTheDay = officecfg::Office::Common::Misc::ShowTipOfTheDay::get();
+ if (!bShowTipOfTheDay)
+ return false;
+
+ const auto t0 = std::chrono::system_clock::now().time_since_epoch();
+
+ // show tip-of-the-day dialog ?
+ const sal_Int32 nLastTipOfTheDay = officecfg::Office::Common::Misc::LastTipOfTheDayShown::get();
+ const sal_Int32 nDay = std::chrono::duration_cast<std::chrono::hours>(t0).count()/24; // days since 1970-01-01
+ return nDay - nLastTipOfTheDay > 0; //only once per day
+}
+
+void SfxViewFrame::Notify( SfxBroadcaster& /*rBC*/, const SfxHint& rHint )
+{
+ if(m_pImpl->bIsDowning)
+ return;
+
+ // we know only SfxEventHint or simple SfxHint
+ if (rHint.GetId() == SfxHintId::ThisIsAnSfxEventHint)
+ {
+ // When the Document is loaded asynchronously, was the Dispatcher
+ // set as ReadOnly, to what must be returned when the document itself
+ // is not read only, and the loading is finished.
+ switch (static_cast<const SfxEventHint&>(rHint).GetEventId())
+ {
+ case SfxEventHintId::ModifyChanged:
+ {
+ SfxBindings& rBind = GetBindings();
+ rBind.Invalidate( SID_DOC_MODIFIED );
+ rBind.Invalidate( SID_RELOAD );
+ rBind.Invalidate( SID_EDITDOC );
+ break;
+ }
+
+ case SfxEventHintId::OpenDoc:
+ case SfxEventHintId::CreateDoc:
+ {
+ if ( !m_xObjSh.is() )
+ break;
+
+ SfxBindings& rBind = GetBindings();
+ rBind.Invalidate( SID_RELOAD );
+ rBind.Invalidate( SID_EDITDOC );
+
+#if !ENABLE_WASM_STRIP_PINGUSER
+ bool bIsHeadlessOrUITest = SfxApplication::IsHeadlessOrUITest(); //uitest.uicheck fails when the dialog is open
+
+ //what's new infobar
+ if (utl::isProductVersionUpgraded(true) && !bIsHeadlessOrUITest)
+ {
+ VclPtr<SfxInfoBarWindow> pInfoBar = AppendInfoBar("whatsnew", "", SfxResId(STR_WHATSNEW_TEXT), InfobarType::INFO);
+ if (pInfoBar)
+ {
+ weld::Button& rWhatsNewButton = pInfoBar->addButton();
+ rWhatsNewButton.set_label(SfxResId(STR_WHATSNEW_BUTTON));
+ rWhatsNewButton.connect_clicked(LINK(this, SfxViewFrame, WhatsNewHandler));
+ }
+ }
+
+ // show tip-of-the-day dialog if it due, but not if there is the impress modal template dialog
+ // open where SdModule::ExecuteNewDocument will launch it instead when that dialog is dismissed
+ if (SfxApplication::IsTipOfTheDayDue() && !bIsHeadlessOrUITest && !IsInModalMode())
+ {
+ // tdf#127946 pass in argument for dialog parent
+ SfxUnoFrameItem aDocFrame(SID_FILLFRAME, GetFrame().GetFrameInterface());
+ GetDispatcher()->ExecuteList(SID_TIPOFTHEDAY, SfxCallMode::SLOT, {}, { &aDocFrame });
+ }
+
+ // inform about the community involvement
+ const auto t0 = std::chrono::system_clock::now().time_since_epoch();
+ const sal_Int64 nLastGetInvolvedShown = officecfg::Setup::Product::LastTimeGetInvolvedShown::get();
+ const sal_Int64 nNow = std::chrono::duration_cast<std::chrono::seconds>(t0).count();
+ const sal_Int64 nPeriodSec(60 * 60 * 24 * 180); // 180 days in seconds
+ bool bUpdateLastTimeGetInvolvedShown = false;
+
+ if (nLastGetInvolvedShown == 0)
+ bUpdateLastTimeGetInvolvedShown = true;
+ else if (nPeriodSec < nNow && nLastGetInvolvedShown < (nNow + nPeriodSec/2) - nPeriodSec) // 90d alternating with donation
+ {
+ bUpdateLastTimeGetInvolvedShown = true;
+
+ VclPtr<SfxInfoBarWindow> pInfoBar = AppendInfoBar("getinvolved", "", SfxResId(STR_GET_INVOLVED_TEXT), InfobarType::INFO);
+
+ if (pInfoBar)
+ {
+ weld::Button& rGetInvolvedButton = pInfoBar->addButton();
+ rGetInvolvedButton.set_label(SfxResId(STR_GET_INVOLVED_BUTTON));
+ rGetInvolvedButton.connect_clicked(LINK(this, SfxViewFrame, GetInvolvedHandler));
+ }
+ }
+
+ if (bUpdateLastTimeGetInvolvedShown
+ && !officecfg::Setup::Product::LastTimeGetInvolvedShown::isReadOnly())
+ {
+ std::shared_ptr<comphelper::ConfigurationChanges> batch(comphelper::ConfigurationChanges::create());
+ officecfg::Setup::Product::LastTimeGetInvolvedShown::set(nNow, batch);
+ batch->commit();
+ }
+
+ // inform about donations
+ const sal_Int64 nLastDonateShown = officecfg::Setup::Product::LastTimeDonateShown::get();
+ bool bUpdateLastTimeDonateShown = false;
+
+ if (nLastDonateShown == 0)
+ bUpdateLastTimeDonateShown = true;
+ else if (nPeriodSec < nNow && nLastDonateShown < nNow - nPeriodSec) // 90d alternating with getinvolved
+ {
+ bUpdateLastTimeDonateShown = true;
+
+ VclPtr<SfxInfoBarWindow> pInfoBar = AppendInfoBar("donate", "", SfxResId(STR_DONATE_TEXT), InfobarType::INFO);
+ if (pInfoBar)
+ {
+ weld::Button& rDonateButton = pInfoBar->addButton();
+ rDonateButton.set_label(SfxResId(STR_DONATE_BUTTON));
+ rDonateButton.connect_clicked(LINK(this, SfxViewFrame, DonationHandler));
+ }
+ }
+
+ if (bUpdateLastTimeDonateShown
+ && !officecfg::Setup::Product::LastTimeDonateShown::isReadOnly())
+ {
+ std::shared_ptr<comphelper::ConfigurationChanges> batch(comphelper::ConfigurationChanges::create());
+ officecfg::Setup::Product::LastTimeDonateShown::set(nNow, batch);
+ batch->commit();
+ }
+#endif
+ if (officecfg::Office::Common::Passwords::HasMaster::get() &&
+ officecfg::Office::Common::Passwords::StorageVersion::get() == 0)
+ {
+ // master password stored in deprecated format
+ VclPtr<SfxInfoBarWindow> pOldMasterPasswordInfoBar =
+ AppendInfoBar("oldmasterpassword", "",
+ SfxResId(STR_REFRESH_MASTER_PASSWORD), InfobarType::DANGER, false);
+ if (pOldMasterPasswordInfoBar)
+ {
+ weld::Button& rButton = pOldMasterPasswordInfoBar->addButton();
+ rButton.set_label(SfxResId(STR_REFRESH_PASSWORD));
+ rButton.connect_clicked(LINK(this,
+ SfxViewFrame, RefreshMasterPasswordHdl));
+ if (Application::GetHelp())
+ {
+ weld::Button& rHelp = pOldMasterPasswordInfoBar->addButton();
+ rHelp.set_label(SfxResId(RID_STR_HELP));
+ rHelp.connect_clicked(LINK(this, SfxViewFrame, HelpMasterPasswordHdl));
+ }
+ }
+ }
+
+ const bool bEmbedded = m_xObjSh->GetCreateMode() == SfxObjectCreateMode::EMBEDDED;
+
+ // read-only infobar if necessary
+ const SfxViewShell *pVSh;
+ const SfxShell *pFSh;
+ if ( m_xObjSh->IsReadOnly() &&
+ ! m_xObjSh->IsSecurityOptOpenReadOnly() &&
+ ( !bEmbedded ||
+ (( pVSh = m_xObjSh->GetViewShell()) && (pFSh = pVSh->GetFormShell()) && pFSh->IsDesignMode())))
+ {
+ AppendReadOnlyInfobar();
+ }
+
+ if (!bEmbedded && m_xObjSh->Get_Impl()->getCurrentMacroExecMode() == css::document::MacroExecMode::NEVER_EXECUTE)
+ AppendContainsMacrosInfobar();
+
+ if (vcl::CommandInfoProvider::GetModuleIdentifier(GetFrame().GetFrameInterface()) == "com.sun.star.text.TextDocument")
+ sfx2::SfxNotebookBar::ReloadNotebookBar(u"modules/swriter/ui/");
+
+ if (SfxClassificationHelper::IsClassified(m_xObjSh->getDocProperties()))
+ {
+ // Document has BAILS properties, display an infobar accordingly.
+ SfxClassificationHelper aHelper(m_xObjSh->getDocProperties());
+ aHelper.UpdateInfobar(*this);
+ }
+
+ // Add pending infobars
+ std::vector<InfobarData>& aPendingInfobars = m_xObjSh->getPendingInfobars();
+ while (!aPendingInfobars.empty())
+ {
+ InfobarData& aInfobarData = aPendingInfobars.back();
+
+ // don't show Track Changes infobar, if Track Changes toolbar is visible
+ if (aInfobarData.msId == "hiddentrackchanges")
+ {
+ if (auto xLayoutManager = getLayoutManager(GetFrame()))
+ {
+ if ( xLayoutManager->getElement(CHANGES_STR).is() )
+ {
+ aPendingInfobars.pop_back();
+ continue;
+ }
+ }
+ }
+
+ // Track Changes infobar: add a button to show/hide Track Changes functions
+ // Hyphenation infobar: add a button to get more information
+ // tdf#148913 limit VclPtr usage for these
+ bool bTrackChanges = aInfobarData.msId == "hiddentrackchanges";
+ if ( bTrackChanges || aInfobarData.msId == "hyphenationmissing" )
+ {
+ VclPtr<SfxInfoBarWindow> pInfoBar =
+ AppendInfoBar(aInfobarData.msId, aInfobarData.msPrimaryMessage,
+ aInfobarData.msSecondaryMessage, aInfobarData.maInfobarType,
+ aInfobarData.mbShowCloseButton);
+
+ // tdf#148913 don't extend this condition to keep it thread-safe
+ if (pInfoBar)
+ {
+ weld::Button& rButton = pInfoBar->addButton();
+ rButton.set_label(SfxResId(bTrackChanges
+ ? STR_TRACK_CHANGES_BUTTON
+ : STR_HYPHENATION_BUTTON));
+ if (bTrackChanges)
+ {
+ rButton.connect_clicked(LINK(this,
+ SfxViewFrame, HiddenTrackChangesHandler));
+ }
+ else
+ {
+ rButton.connect_clicked(LINK(this,
+ SfxViewFrame, HyphenationMissingHandler));
+ }
+ }
+ }
+ else
+ {
+ AppendInfoBar(aInfobarData.msId, aInfobarData.msPrimaryMessage,
+ aInfobarData.msSecondaryMessage, aInfobarData.maInfobarType,
+ aInfobarData.mbShowCloseButton);
+ }
+
+ aPendingInfobars.pop_back();
+ }
+
+ break;
+ }
+ default: break;
+ }
+ }
+ else
+ {
+ switch( rHint.GetId() )
+ {
+ case SfxHintId::ModeChanged:
+ {
+ UpdateTitle();
+
+ if ( !m_xObjSh.is() )
+ break;
+
+ // Switch r/o?
+ SfxBindings& rBind = GetBindings();
+ rBind.Invalidate( SID_RELOAD );
+ SfxDispatcher *pDispat = GetDispatcher();
+ bool bWasReadOnly = pDispat->GetReadOnly_Impl();
+ bool bIsReadOnly = m_xObjSh->IsReadOnly();
+ if ( bWasReadOnly != bIsReadOnly )
+ {
+ // Then also TITLE_CHANGED
+ UpdateTitle();
+ rBind.Invalidate( SID_FILE_NAME );
+ rBind.Invalidate( SID_DOCINFO_TITLE );
+ rBind.Invalidate( SID_EDITDOC );
+
+ pDispat->GetBindings()->InvalidateAll(true);
+ pDispat->SetReadOnly_Impl( bIsReadOnly );
+
+ // Only force and Dispatcher-Update, if it is done next
+ // anyway, otherwise flickering or GPF is possible since
+ // the Writer for example prefers in Resize perform some
+ // actions which has a SetReadOnlyUI in Dispatcher as a
+ // result!
+
+ if ( pDispat->IsUpdated_Impl() )
+ pDispat->Update_Impl(true);
+ }
+
+ Enable( !m_xObjSh->IsInModalMode() );
+ break;
+ }
+
+ case SfxHintId::TitleChanged:
+ {
+ UpdateTitle();
+ SfxBindings& rBind = GetBindings();
+ rBind.Invalidate( SID_FILE_NAME );
+ rBind.Invalidate( SID_DOCINFO_TITLE );
+ rBind.Invalidate( SID_EDITDOC );
+ rBind.Invalidate( SID_RELOAD );
+ break;
+ }
+
+ case SfxHintId::DocumentRepair:
+ {
+ GetBindings().Invalidate( SID_DOC_REPAIR );
+ break;
+ }
+
+ case SfxHintId::Deinitializing:
+ {
+ vcl::Window* pFrameWin = GetWindow().GetFrameWindow();
+ if (pFrameWin && pFrameWin->GetLOKNotifier())
+ pFrameWin->ReleaseLOKNotifier();
+
+ GetFrame().DoClose();
+ break;
+ }
+ case SfxHintId::Dying:
+ // when the Object is being deleted, destroy the view too
+ if ( m_xObjSh.is() )
+ ReleaseObjectShell_Impl();
+ else
+ GetFrame().DoClose();
+ break;
+ default: break;
+ }
+ }
+}
+
+#if !ENABLE_WASM_STRIP_PINGUSER
+IMPL_LINK_NOARG(SfxViewFrame, WhatsNewHandler, weld::Button&, void)
+{
+ GetDispatcher()->Execute(SID_WHATSNEW);
+}
+
+IMPL_LINK_NOARG(SfxViewFrame, GetInvolvedHandler, weld::Button&, void)
+{
+ GetDispatcher()->Execute(SID_GETINVOLVED);
+}
+
+IMPL_LINK_NOARG(SfxViewFrame, DonationHandler, weld::Button&, void)
+{
+ GetDispatcher()->Execute(SID_DONATION);
+}
+#endif
+
+IMPL_LINK(SfxViewFrame, SwitchReadOnlyHandler, weld::Button&, rButton, void)
+{
+ if (m_xObjSh.is() && m_xObjSh->IsSignPDF())
+ {
+ SfxEditDocumentDialog aDialog(&rButton);
+ if (aDialog.run() != RET_OK)
+ return;
+ }
+ GetDispatcher()->Execute(SID_EDITDOC);
+}
+
+IMPL_LINK_NOARG(SfxViewFrame, SignDocumentHandler, weld::Button&, void)
+{
+ GetDispatcher()->Execute(SID_SIGNATURE);
+}
+
+IMPL_LINK(SfxViewFrame, HiddenTrackChangesHandler, weld::Button&, rButton, void)
+{
+ // enable Track Changes toolbar, if it is disabled.
+ // Otherwise disable the toolbar, and close the infobar
+ auto xLayoutManager = getLayoutManager(GetFrame());
+ if (!xLayoutManager)
+ return;
+
+ if (!xLayoutManager->getElement(CHANGES_STR).is())
+ {
+ xLayoutManager->createElement(CHANGES_STR);
+ xLayoutManager->showElement(CHANGES_STR);
+ rButton.set_label(SfxResId(STR_TRACK_CHANGES_BUTTON_HIDE));
+ }
+ else
+ {
+ xLayoutManager->hideElement(CHANGES_STR);
+ xLayoutManager->destroyElement(CHANGES_STR);
+ RemoveInfoBar(u"hiddentrackchanges");
+ }
+}
+
+IMPL_LINK_NOARG(SfxViewFrame, HyphenationMissingHandler, weld::Button&, void)
+{
+ GetDispatcher()->Execute(SID_HYPHENATIONMISSING);
+ RemoveInfoBar(u"hyphenationmissing");
+}
+
+IMPL_LINK_NOARG(SfxViewFrame, MacroButtonHandler, weld::Button&, void)
+{
+ // start with tab 0 displayed
+ SfxUInt16Item aTabItem(SID_MACROORGANIZER, 0);
+ SfxBoolItem aCurrentDocItem(FN_PARAM_2, true);
+ SfxUnoFrameItem aDocFrame(SID_FILLFRAME, GetFrame().GetFrameInterface());
+ GetDispatcher()->ExecuteList(SID_MACROORGANIZER, SfxCallMode::ASYNCHRON,
+ { &aTabItem, &aCurrentDocItem }, { &aDocFrame });
+}
+
+IMPL_LINK_NOARG(SfxViewFrame, SecurityButtonHandler, weld::Button&, void)
+{
+ SfxUInt16Item aPageID(SID_OPTIONS_PAGEID, sal_uInt16(RID_SVXPAGE_INET_SECURITY));
+ GetDispatcher()->ExecuteList(SID_OPTIONS_TREEDIALOG, SfxCallMode::SYNCHRON, { &aPageID });
+ RemoveInfoBar(u"securitywarn");
+}
+
+IMPL_LINK_NOARG(SfxViewFrame, EventButtonHandler, weld::Button&, void)
+{
+ SfxUnoFrameItem aDocFrame(SID_FILLFRAME, GetFrame().GetFrameInterface());
+ GetDispatcher()->ExecuteList(SID_CONFIGEVENT, SfxCallMode::ASYNCHRON,
+ {}, { &aDocFrame });
+}
+
+IMPL_LINK_NOARG(SfxViewFrame, RefreshMasterPasswordHdl, weld::Button&, void)
+{
+ bool bChanged = false;
+ try
+ {
+ Reference< task::XPasswordContainer2 > xMasterPasswd(
+ task::PasswordContainer::create(comphelper::getProcessComponentContext()));
+
+ css::uno::Reference<css::frame::XFrame> xFrame = GetFrame().GetFrameInterface();
+ css::uno::Reference<css::awt::XWindow> xContainerWindow = xFrame->getContainerWindow();
+
+ uno::Reference<task::XInteractionHandler> xTmpHandler(task::InteractionHandler::createWithParent(comphelper::getProcessComponentContext(),
+ xContainerWindow));
+ bChanged = xMasterPasswd->changeMasterPassword(xTmpHandler);
+ }
+ catch (const Exception&)
+ {}
+ if (bChanged)
+ RemoveInfoBar(u"oldmasterpassword");
+}
+
+IMPL_STATIC_LINK_NOARG(SfxViewFrame, HelpMasterPasswordHdl, weld::Button&, void)
+{
+ if (Help* pHelp = Application::GetHelp())
+ pHelp->Start("cui/ui/optsecuritypage/savepassword");
+}
+
+void SfxViewFrame::Construct_Impl( SfxObjectShell *pObjSh )
+{
+ m_pImpl->bResizeInToOut = true;
+ m_pImpl->bObjLocked = false;
+ m_pImpl->nCurViewId = SFX_INTERFACE_NONE;
+ m_pImpl->bReloading = false;
+ m_pImpl->bIsDowning = false;
+ m_pImpl->bModal = false;
+ m_pImpl->bEnabled = true;
+ m_pImpl->nDocViewNo = 0;
+ m_pImpl->aMargin = Size( -1, -1 );
+ m_pImpl->pWindow = nullptr;
+
+ SetPool( &SfxGetpApp()->GetPool() );
+ m_pDispatcher.reset( new SfxDispatcher(this) );
+ if ( !GetBindings().GetDispatcher() )
+ GetBindings().SetDispatcher( m_pDispatcher.get() );
+
+ m_xObjSh = pObjSh;
+ if ( m_xObjSh.is() && m_xObjSh->IsPreview() )
+ GetDispatcher()->SetQuietMode_Impl( true );
+
+ if ( pObjSh )
+ {
+ m_pDispatcher->Push( *SfxGetpApp() );
+ SfxModule* pModule = m_xObjSh->GetModule();
+ if( pModule )
+ m_pDispatcher->Push( *pModule );
+ m_pDispatcher->Push( *this );
+ m_pDispatcher->Push( *pObjSh );
+ m_pDispatcher->Flush();
+ StartListening( *pObjSh );
+ Notify( *pObjSh, SfxHint(SfxHintId::TitleChanged) );
+ Notify( *pObjSh, SfxHint(SfxHintId::DocChanged) );
+ m_pDispatcher->SetReadOnly_Impl( pObjSh->IsReadOnly() );
+ }
+ else
+ {
+ m_pDispatcher->Push( *SfxGetpApp() );
+ m_pDispatcher->Push( *this );
+ m_pDispatcher->Flush();
+ }
+
+ SfxGetpApp()->GetViewFrames_Impl().push_back(this);
+}
+
+/* [Description]
+
+ Constructor of SfxViewFrame for a <SfxObjectShell> from the Resource.
+ The 'nViewId' to the created <SfxViewShell> can be returned.
+ (default is the SfxViewShell-Subclass that was registered first).
+*/
+SfxViewFrame::SfxViewFrame
+(
+ SfxFrame& rFrame,
+ SfxObjectShell* pObjShell
+)
+ : m_pImpl( new SfxViewFrame_Impl( rFrame ) )
+ , m_pBindings( new SfxBindings )
+ , m_pHelpData(CreateSVHelpData())
+ , m_pWinData(CreateSVWinData())
+ , m_nAdjustPosPixelLock( 0 )
+ , m_pCommandPopupHandler(new CommandPopupHandler)
+{
+
+ rFrame.SetCurrentViewFrame_Impl( this );
+ rFrame.SetHasTitle( true );
+ Construct_Impl( pObjShell );
+
+ m_pImpl->pWindow = VclPtr<SfxFrameViewWindow_Impl>::Create( this, rFrame.GetWindow() );
+ m_pImpl->pWindow->SetSizePixel( rFrame.GetWindow().GetOutputSizePixel() );
+ rFrame.SetOwnsBindings_Impl( true );
+ rFrame.CreateWorkWindow_Impl();
+}
+
+SfxViewFrame::~SfxViewFrame()
+{
+ m_pImpl->bIsDowning = true;
+
+ if ( SfxViewFrame::Current() == this )
+ SfxViewFrame::SetViewFrame( nullptr );
+
+ ReleaseObjectShell_Impl();
+
+ if ( GetFrame().OwnsBindings_Impl() )
+ // The Bindings delete the Frame!
+ KillDispatcher_Impl();
+
+ m_pImpl->pWindow.disposeAndClear();
+
+ if ( GetFrame().GetCurrentViewFrame() == this )
+ GetFrame().SetCurrentViewFrame_Impl( nullptr );
+
+ // Unregister from the Frame List.
+ SfxApplication *pSfxApp = SfxApplication::Get();
+ if (pSfxApp)
+ {
+ auto &rFrames = pSfxApp->GetViewFrames_Impl();
+ auto it = std::find( rFrames.begin(), rFrames.end(), this );
+ rFrames.erase( it );
+ }
+
+ // Delete Member
+ KillDispatcher_Impl();
+
+ DestroySVHelpData(m_pHelpData);
+ m_pHelpData = nullptr;
+
+ DestroySVWinData(m_pWinData);
+ m_pWinData = nullptr;
+}
+
+// Remove and delete the Dispatcher.
+void SfxViewFrame::KillDispatcher_Impl()
+{
+
+ SfxModule* pModule = m_xObjSh.is() ? m_xObjSh->GetModule() : nullptr;
+ if ( m_xObjSh.is() )
+ ReleaseObjectShell_Impl();
+ if ( m_pDispatcher )
+ {
+ if( pModule )
+ m_pDispatcher->Pop( *pModule, SfxDispatcherPopFlags::POP_UNTIL );
+ else
+ m_pDispatcher->Pop( *this );
+ m_pDispatcher.reset();
+ }
+}
+
+SfxViewFrame* SfxViewFrame::Current()
+{
+ SfxApplication* pApp = SfxApplication::Get();
+ return pApp ? pApp->Get_Impl()->pViewFrame : nullptr;
+}
+
+// returns the first window of spec. type viewing the specified doc.
+SfxViewFrame* SfxViewFrame::GetFirst
+(
+ const SfxObjectShell* pDoc,
+ bool bOnlyIfVisible
+)
+{
+ SfxApplication *pSfxApp = SfxApplication::Get();
+ if (!pSfxApp)
+ return nullptr;
+
+ // search for a SfxDocument of the specified type
+ for (SfxViewFrame* pFrame : pSfxApp->GetViewFrames_Impl())
+ {
+ if ( ( !pDoc || pDoc == pFrame->GetObjectShell() )
+ && ( !bOnlyIfVisible || pFrame->IsVisible() )
+ )
+ return pFrame;
+ }
+
+ return nullptr;
+}
+
+// returns the next window of spec. type viewing the specified doc.
+SfxViewFrame* SfxViewFrame::GetNext
+(
+ const SfxViewFrame& rPrev,
+ const SfxObjectShell* pDoc,
+ bool bOnlyIfVisible
+)
+{
+ SfxApplication *pSfxApp = SfxApplication::Get();
+ if (!pSfxApp)
+ return nullptr;
+
+ auto &rFrames = pSfxApp->GetViewFrames_Impl();
+
+ // refind the specified predecessor
+ size_t nPos;
+ for ( nPos = 0; nPos < rFrames.size(); ++nPos )
+ if ( rFrames[nPos] == &rPrev )
+ break;
+
+ // search for a Frame of the specified type
+ for ( ++nPos; nPos < rFrames.size(); ++nPos )
+ {
+ SfxViewFrame *pFrame = rFrames[nPos];
+ if ( ( !pDoc || pDoc == pFrame->GetObjectShell() )
+ && ( !bOnlyIfVisible || pFrame->IsVisible() )
+ )
+ return pFrame;
+ }
+ return nullptr;
+}
+
+SfxProgress* SfxViewFrame::GetProgress() const
+{
+ SfxObjectShell *pObjSh = m_xObjSh.get();
+ return pObjSh ? pObjSh->GetProgress() : nullptr;
+}
+
+void SfxViewFrame::DoAdjustPosSizePixel //! divide on Inner.../Outer...
+(
+ SfxViewShell* pSh,
+ const Point& rPos,
+ const Size& rSize,
+ bool inplaceEditModeChange
+)
+{
+
+ // Components do not use this Method!
+ if( pSh && pSh->GetWindow() && !m_nAdjustPosPixelLock )
+ {
+ m_nAdjustPosPixelLock++;
+ if ( m_pImpl->bResizeInToOut )
+ pSh->InnerResizePixel( rPos, rSize, inplaceEditModeChange );
+ else
+ pSh->OuterResizePixel( rPos, rSize );
+ m_nAdjustPosPixelLock--;
+ }
+}
+
+bool SfxViewFrameItem::operator==( const SfxPoolItem &rItem ) const
+{
+ return SfxPoolItem::operator==(rItem) &&
+ static_cast<const SfxViewFrameItem&>(rItem).pFrame == pFrame;
+}
+
+SfxViewFrameItem* SfxViewFrameItem::Clone( SfxItemPool *) const
+{
+ return new SfxViewFrameItem( *this );
+}
+
+void SfxViewFrame::SetViewShell_Impl( SfxViewShell *pVSh )
+/* [Description]
+
+ Internal Method to set the current <SfxViewShell> Instance,
+ that is active int this SfxViewFrame at the moment.
+*/
+{
+ SfxShell::SetViewShell_Impl( pVSh );
+
+ // Hack: InPlaceMode
+ if ( pVSh )
+ m_pImpl->bResizeInToOut = false;
+}
+
+void SfxViewFrame::ForceOuterResize_Impl()
+{
+ m_pImpl->bResizeInToOut = true;
+}
+
+void SfxViewFrame::GetDocNumber_Impl()
+{
+ DBG_ASSERT( GetObjectShell(), "No Document!" );
+ GetObjectShell()->SetNamedVisibility_Impl();
+ m_pImpl->nDocViewNo = GetObjectShell()->GetNoSet_Impl().GetFreeIndex()+1;
+}
+
+void SfxViewFrame::Enable( bool bEnable )
+{
+ if ( bEnable == m_pImpl->bEnabled )
+ return;
+
+ m_pImpl->bEnabled = bEnable;
+
+ vcl::Window *pWindow = &GetFrame().GetWindow();
+ if ( !bEnable )
+ m_pImpl->bWindowWasEnabled = pWindow->IsInputEnabled();
+ if ( !bEnable || m_pImpl->bWindowWasEnabled )
+ pWindow->EnableInput( bEnable );
+
+ // cursor and focus
+ SfxViewShell* pViewSh = GetViewShell();
+ if ( bEnable )
+ {
+ // show cursor
+ if ( pViewSh )
+ pViewSh->ShowCursor();
+ }
+ else
+ {
+ // hide cursor
+ if ( pViewSh )
+ pViewSh->ShowCursor(false);
+ }
+}
+
+/* [Description]
+
+ This method makes the Frame-Window visible and before transmits the
+ window name. In addition, the document is held. In general one can never
+ show the window directly!
+*/
+void SfxViewFrame::Show()
+{
+ // First lock the objectShell so that UpdateTitle() is valid:
+ // IsVisible() == true (:#)
+ if ( m_xObjSh.is() )
+ {
+ m_xObjSh->GetMedium()->GetItemSet().ClearItem( SID_HIDDEN );
+ if ( !m_pImpl->bObjLocked )
+ LockObjectShell_Impl();
+
+ // Adjust Doc-Shell title number, get unique view-no
+ if ( 0 == m_pImpl->nDocViewNo )
+ {
+ GetDocNumber_Impl();
+ UpdateTitle();
+ }
+ }
+ else
+ UpdateTitle();
+
+ // Display Frame-window, but only if the ViewFrame has no window of its
+ // own or if it does not contain a Component
+ GetWindow().Show();
+ GetFrame().GetWindow().Show();
+}
+
+
+bool SfxViewFrame::IsVisible() const
+{
+ return m_pImpl->bObjLocked;
+}
+
+
+void SfxViewFrame::LockObjectShell_Impl()
+{
+ DBG_ASSERT( !m_pImpl->bObjLocked, "Wrong Locked status!" );
+
+ DBG_ASSERT( GetObjectShell(), "No Document!" );
+ GetObjectShell()->OwnerLock(true);
+ m_pImpl->bObjLocked = true;
+}
+
+
+void SfxViewFrame::MakeActive_Impl( bool bGrabFocus )
+{
+ if ( !GetViewShell() || GetFrame().IsClosing_Impl() )
+ return;
+
+ if ( !IsVisible() )
+ return;
+
+ bool bPreview = false;
+ if (GetObjectShell()->IsPreview())
+ {
+ bPreview = true;
+ }
+
+ css::uno::Reference<css::frame::XFrame> xFrame = GetFrame().GetFrameInterface();
+ if (!bPreview)
+ {
+ SetViewFrame(this);
+ GetBindings().SetActiveFrame(css::uno::Reference<css::frame::XFrame>());
+ uno::Reference<frame::XFramesSupplier> xSupp(xFrame, uno::UNO_QUERY);
+ if (xSupp.is())
+ xSupp->setActiveFrame(uno::Reference<frame::XFrame>());
+
+ css::uno::Reference< css::awt::XWindow > xContainerWindow = xFrame->getContainerWindow();
+ VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow(xContainerWindow);
+ if (pWindow && pWindow->HasChildPathFocus() && bGrabFocus)
+ {
+ SfxInPlaceClient *pCli = GetViewShell()->GetUIActiveClient();
+ if (!pCli || !pCli->IsObjectUIActive())
+ GetFrame().GrabFocusOnComponent_Impl();
+ }
+ }
+ else
+ {
+ GetBindings().SetDispatcher(GetDispatcher());
+ GetBindings().SetActiveFrame(css::uno::Reference<css::frame::XFrame>());
+ GetDispatcher()->Update_Impl();
+ }
+}
+
+SfxObjectShell* SfxViewFrame::GetObjectShell()
+{
+ return m_xObjSh.get();
+}
+
+const Size& SfxViewFrame::GetMargin_Impl() const
+{
+ return m_pImpl->aMargin;
+}
+
+SfxViewFrame* SfxViewFrame::LoadViewIntoFrame_Impl_NoThrow( const SfxObjectShell& i_rDoc, const Reference< XFrame >& i_rFrame,
+ const SfxInterfaceId i_nViewId, const bool i_bHidden )
+{
+ Reference< XFrame > xFrame( i_rFrame );
+ bool bOwnFrame = false;
+ SfxViewShell* pSuccessView = nullptr;
+ try
+ {
+ if ( !xFrame.is() )
+ {
+ Reference < XDesktop2 > xDesktop = Desktop::create( ::comphelper::getProcessComponentContext() );
+
+ if ( !i_bHidden )
+ {
+ try
+ {
+ // if there is a backing component, use it
+ ::framework::FrameListAnalyzer aAnalyzer( xDesktop, Reference< XFrame >(), FrameAnalyzerFlags::BackingComponent );
+
+ if ( aAnalyzer.m_xBackingComponent.is() )
+ xFrame = aAnalyzer.m_xBackingComponent;
+ }
+ catch( uno::Exception& )
+ {}
+ }
+
+ if ( !xFrame.is() )
+ xFrame.set( xDesktop->findFrame( "_blank", 0 ), UNO_SET_THROW );
+
+ bOwnFrame = true;
+ }
+
+ pSuccessView = LoadViewIntoFrame_Impl(
+ i_rDoc,
+ xFrame,
+ Sequence< PropertyValue >(), // means "reuse existing model's args"
+ i_nViewId,
+ i_bHidden
+ );
+
+ if ( bOwnFrame && !i_bHidden )
+ {
+ // ensure the frame/window is visible
+ Reference< XWindow > xContainerWindow( xFrame->getContainerWindow(), UNO_SET_THROW );
+ xContainerWindow->setVisible( true );
+ }
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("sfx.view");
+ }
+
+ if ( pSuccessView )
+ return &pSuccessView->GetViewFrame();
+
+ if ( bOwnFrame )
+ {
+ try
+ {
+ xFrame->dispose();
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("sfx.view");
+ }
+ }
+
+ return nullptr;
+}
+
+SfxViewShell* SfxViewFrame::LoadViewIntoFrame_Impl( const SfxObjectShell& i_rDoc, const Reference< XFrame >& i_rFrame,
+ const Sequence< PropertyValue >& i_rLoadArgs, const SfxInterfaceId i_nViewId,
+ const bool i_bHidden )
+{
+ Reference< XModel > xDocument( i_rDoc.GetModel(), UNO_SET_THROW );
+
+ ::comphelper::NamedValueCollection aTransformLoadArgs( i_rLoadArgs.hasElements() ? i_rLoadArgs : xDocument->getArgs() );
+ aTransformLoadArgs.put( "Model", xDocument );
+ if ( i_nViewId )
+ aTransformLoadArgs.put( "ViewId", sal_uInt16( i_nViewId ) );
+ if ( i_bHidden )
+ aTransformLoadArgs.put( "Hidden", i_bHidden );
+ else
+ aTransformLoadArgs.remove( "Hidden" );
+
+ Reference< XComponentLoader > xLoader( i_rFrame, UNO_QUERY_THROW );
+ xLoader->loadComponentFromURL( "private:object", "_self", 0,
+ aTransformLoadArgs.getPropertyValues() );
+
+ SfxViewShell* pViewShell = SfxViewShell::Get( i_rFrame->getController() );
+ ENSURE_OR_THROW( pViewShell,
+ "SfxViewFrame::LoadViewIntoFrame_Impl: loading an SFX doc into a frame resulted in a non-SFX view - quite impossible" );
+ return pViewShell;
+}
+
+SfxViewFrame* SfxViewFrame::LoadHiddenDocument( SfxObjectShell const & i_rDoc, SfxInterfaceId i_nViewId )
+{
+ return LoadViewIntoFrame_Impl_NoThrow( i_rDoc, Reference< XFrame >(), i_nViewId, true );
+}
+
+SfxViewFrame* SfxViewFrame::LoadDocument( SfxObjectShell const & i_rDoc, SfxInterfaceId i_nViewId )
+{
+ return LoadViewIntoFrame_Impl_NoThrow( i_rDoc, Reference< XFrame >(), i_nViewId, false );
+}
+
+SfxViewFrame* SfxViewFrame::LoadDocumentIntoFrame( SfxObjectShell const & i_rDoc, const Reference< XFrame >& i_rTargetFrame )
+{
+ return LoadViewIntoFrame_Impl_NoThrow( i_rDoc, i_rTargetFrame, SFX_INTERFACE_NONE, false );
+}
+
+SfxViewFrame* SfxViewFrame::LoadDocumentIntoFrame( SfxObjectShell const & i_rDoc, const SfxFrameItem* i_pFrameItem, SfxInterfaceId i_nViewId )
+{
+ return LoadViewIntoFrame_Impl_NoThrow( i_rDoc, i_pFrameItem && i_pFrameItem->GetFrame() ? i_pFrameItem->GetFrame()->GetFrameInterface() : nullptr, i_nViewId, false );
+}
+
+SfxViewFrame* SfxViewFrame::DisplayNewDocument( SfxObjectShell const & i_rDoc, const SfxRequest& i_rCreateDocRequest )
+{
+ const SfxUnoFrameItem* pFrameItem = i_rCreateDocRequest.GetArg<SfxUnoFrameItem>(SID_FILLFRAME);
+ const SfxBoolItem* pHiddenItem = i_rCreateDocRequest.GetArg<SfxBoolItem>(SID_HIDDEN);
+
+ return LoadViewIntoFrame_Impl_NoThrow(
+ i_rDoc,
+ pFrameItem ? pFrameItem->GetFrame() : nullptr,
+ SFX_INTERFACE_NONE,
+ pHiddenItem && pHiddenItem->GetValue()
+ );
+}
+
+SfxViewFrame* SfxViewFrame::Get( const Reference< XController>& i_rController, const SfxObjectShell* i_pDoc )
+{
+ if ( !i_rController.is() )
+ return nullptr;
+
+ const SfxObjectShell* pDoc = i_pDoc;
+ if ( !pDoc )
+ {
+ Reference< XModel > xDocument( i_rController->getModel() );
+ for ( pDoc = SfxObjectShell::GetFirst( nullptr, false );
+ pDoc;
+ pDoc = SfxObjectShell::GetNext( *pDoc, nullptr, false )
+ )
+ {
+ if ( pDoc->GetModel() == xDocument )
+ break;
+ }
+ }
+
+ SfxViewFrame* pViewFrame = nullptr;
+ for ( pViewFrame = SfxViewFrame::GetFirst( pDoc, false );
+ pViewFrame;
+ pViewFrame = SfxViewFrame::GetNext( *pViewFrame, pDoc, false )
+ )
+ {
+ if ( pViewFrame->GetViewShell()->GetController() == i_rController )
+ break;
+ }
+
+ return pViewFrame;
+}
+
+void SfxViewFrame::SaveCurrentViewData_Impl( const SfxInterfaceId i_nNewViewId )
+{
+ SfxViewShell* pCurrentShell = GetViewShell();
+ ENSURE_OR_RETURN_VOID( pCurrentShell != nullptr, "SfxViewFrame::SaveCurrentViewData_Impl: no current view shell -> no current view data!" );
+
+ // determine the logical (API) view name
+ const SfxObjectFactory& rDocFactory( pCurrentShell->GetObjectShell()->GetFactory() );
+ const sal_uInt16 nCurViewNo = rDocFactory.GetViewNo_Impl( GetCurViewId(), 0 );
+ const OUString sCurrentViewName = rDocFactory.GetViewFactory( nCurViewNo ).GetAPIViewName();
+ const sal_uInt16 nNewViewNo = rDocFactory.GetViewNo_Impl( i_nNewViewId, 0 );
+ const OUString sNewViewName = rDocFactory.GetViewFactory( nNewViewNo ).GetAPIViewName();
+ if ( sCurrentViewName.isEmpty() || sNewViewName.isEmpty() )
+ {
+ // can't say anything about the view, the respective application did not yet migrate its code to
+ // named view factories => bail out
+ OSL_FAIL( "SfxViewFrame::SaveCurrentViewData_Impl: views without API names? Shouldn't happen anymore?" );
+ return;
+ }
+ SAL_WARN_IF(sNewViewName == sCurrentViewName, "sfx.view", "SfxViewFrame::SaveCurrentViewData_Impl: suspicious: new and old view name are identical!");
+
+ // save the view data only when we're moving from a non-print-preview to the print-preview view
+ if ( sNewViewName != "PrintPreview" )
+ return;
+
+ // retrieve the view data from the view
+ Sequence< PropertyValue > aViewData;
+ pCurrentShell->WriteUserDataSequence( aViewData );
+
+ try
+ {
+ // retrieve view data (for *all* views) from the model
+ const Reference< XController > xController( pCurrentShell->GetController(), UNO_SET_THROW );
+ const Reference< XViewDataSupplier > xViewDataSupplier( xController->getModel(), UNO_QUERY_THROW );
+ const Reference< XIndexContainer > xViewData( xViewDataSupplier->getViewData(), UNO_QUERY_THROW );
+
+ // look up the one view data item which corresponds to our current view, and remove it
+ const sal_Int32 nCount = xViewData->getCount();
+ for ( sal_Int32 i=0; i<nCount; ++i )
+ {
+ const ::comphelper::NamedValueCollection aCurViewData( xViewData->getByIndex(i) );
+ const OUString sViewId( aCurViewData.getOrDefault( "ViewId", OUString() ) );
+ if ( sViewId.isEmpty() )
+ continue;
+
+ const SfxViewFactory* pViewFactory = rDocFactory.GetViewFactoryByViewName( sViewId );
+ if ( pViewFactory == nullptr )
+ continue;
+
+ if ( pViewFactory->GetOrdinal() == GetCurViewId() )
+ {
+ xViewData->removeByIndex(i);
+ break;
+ }
+ }
+
+ // then replace it with the most recent view data we just obtained
+ xViewData->insertByIndex( 0, Any( aViewData ) );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("sfx.view");
+ }
+}
+
+/* [Description]
+
+ Internal Method for switching to another <SfxViewShell> subclass,
+ which should be created in this SfxMDIFrame. If no SfxViewShell exist
+ in this SfxMDIFrame, then one will first be created.
+
+
+ [Return Value]
+
+ bool true
+ requested SfxViewShell was created and a
+ possibly existing one deleted
+
+ false
+ SfxViewShell requested could not be created,
+ the existing SfxViewShell thus continue to exist
+*/
+bool SfxViewFrame::SwitchToViewShell_Impl
+(
+ sal_uInt16 nViewIdOrNo, /* > 0
+ Registration-Id of the View, to which the
+ method should switch, for example the one
+ that will be created.
+
+ == 0
+ First use the Default view. */
+
+ bool bIsIndex /* true
+ 'nViewIdOrNo' is no Registration-Id instead
+ an Index of <SfxViewFrame> in <SfxObjectShell>.
+ */
+)
+{
+ try
+ {
+ ENSURE_OR_THROW( GetObjectShell() != nullptr, "not possible without a document" );
+
+ // if we already have a view shell, remove it
+ SfxViewShell* pOldSh = GetViewShell();
+ OSL_PRECOND( pOldSh, "SfxViewFrame::SwitchToViewShell_Impl: that's called *switch* (not for *initial-load*) for a reason" );
+ if ( pOldSh )
+ {
+ // ask whether it can be closed
+ if ( !pOldSh->PrepareClose() )
+ return false;
+
+ // remove sub shells from Dispatcher before switching to new ViewShell
+ PopShellAndSubShells_Impl( *pOldSh );
+ }
+
+ GetBindings().ENTERREGISTRATIONS();
+ LockAdjustPosSizePixel();
+
+ // ID of the new view
+ SfxObjectFactory& rDocFact = GetObjectShell()->GetFactory();
+ const SfxInterfaceId nViewId = ( bIsIndex || !nViewIdOrNo ) ? rDocFact.GetViewFactory( nViewIdOrNo ).GetOrdinal() : SfxInterfaceId(nViewIdOrNo);
+
+ // save the view data of the old view, so it can be restored later on (when needed)
+ SaveCurrentViewData_Impl( nViewId );
+
+ if (pOldSh)
+ pOldSh->SetDying();
+
+ // create and load new ViewShell
+ SfxViewShell* pNewSh = LoadViewIntoFrame_Impl(
+ *GetObjectShell(),
+ GetFrame().GetFrameInterface(),
+ Sequence< PropertyValue >(), // means "reuse existing model's args"
+ nViewId,
+ false
+ );
+
+ // allow resize events to be processed
+ UnlockAdjustPosSizePixel();
+
+ if ( GetWindow().IsReallyVisible() )
+ DoAdjustPosSizePixel( pNewSh, Point(), GetWindow().GetOutputSizePixel(), false );
+
+ GetBindings().LEAVEREGISTRATIONS();
+ delete pOldSh;
+ }
+ catch ( const css::uno::Exception& )
+ {
+ // the SfxCode is not able to cope with exceptions thrown while creating views
+ // the code will crash in the stack unwinding procedure, so we shouldn't let exceptions go through here
+ DBG_UNHANDLED_EXCEPTION("sfx.view");
+ return false;
+ }
+
+ DBG_ASSERT( SfxGetpApp()->GetViewFrames_Impl().size() == SfxGetpApp()->GetViewShells_Impl().size(), "Inconsistent view arrays!" );
+ return true;
+}
+
+void SfxViewFrame::SetCurViewId_Impl( const SfxInterfaceId i_nID )
+{
+ m_pImpl->nCurViewId = i_nID;
+}
+
+SfxInterfaceId SfxViewFrame::GetCurViewId() const
+{
+ return m_pImpl->nCurViewId;
+}
+
+/* [Description]
+
+ Internal method to run the slot for the <SfxShell> Subclass in the
+ SfxViewFrame <SVIDL> described slots.
+*/
+void SfxViewFrame::ExecView_Impl
+(
+ SfxRequest& rReq // The executable <SfxRequest>
+)
+{
+
+ // If the Shells are just being replaced...
+ if ( !GetObjectShell() || !GetViewShell() )
+ return;
+
+ switch ( rReq.GetSlot() )
+ {
+ case SID_TERMINATE_INPLACEACTIVATION :
+ {
+ SfxInPlaceClient* pClient = GetViewShell()->GetUIActiveClient();
+ if ( pClient )
+ pClient->DeactivateObject();
+ break;
+ }
+
+ case SID_VIEWSHELL:
+ {
+ const SfxUInt16Item *pItem = nullptr;
+ if ( rReq.GetArgs()
+ && (pItem = rReq.GetArgs()->GetItemIfSet( SID_VIEWSHELL, false ))
+ )
+ {
+ const sal_uInt16 nViewId = pItem->GetValue();
+ bool bSuccess = SwitchToViewShell_Impl( nViewId );
+ rReq.SetReturnValue( SfxBoolItem( 0, bSuccess ) );
+ }
+ break;
+ }
+
+ case SID_VIEWSHELL0:
+ case SID_VIEWSHELL1:
+ case SID_VIEWSHELL2:
+ case SID_VIEWSHELL3:
+ case SID_VIEWSHELL4:
+ {
+ const sal_uInt16 nViewNo = rReq.GetSlot() - SID_VIEWSHELL0;
+ bool bSuccess = SwitchToViewShell_Impl( nViewNo, true );
+ rReq.SetReturnValue( SfxBoolItem( 0, bSuccess ) );
+ break;
+ }
+
+ case SID_NEWWINDOW:
+ {
+ // Hack. at the moment a virtual Function
+ if ( !GetViewShell()->NewWindowAllowed() )
+ {
+ OSL_FAIL( "You should have disabled the 'Window/New Window' slot!" );
+ return;
+ }
+
+ // Get ViewData of FrameSets recursively.
+ GetFrame().GetViewData_Impl();
+ SfxMedium* pMed = GetObjectShell()->GetMedium();
+
+ // do not open the new window hidden
+ pMed->GetItemSet().ClearItem( SID_HIDDEN );
+
+ // the view ID (optional arg. TODO: this is currently not supported in the slot definition ...)
+ const SfxUInt16Item* pViewIdItem = rReq.GetArg<SfxUInt16Item>(SID_VIEW_ID);
+ const SfxInterfaceId nViewId = pViewIdItem ? SfxInterfaceId(pViewIdItem->GetValue()) : GetCurViewId();
+
+ Reference < XFrame > xFrame;
+ // the frame (optional arg. TODO: this is currently not supported in the slot definition ...)
+ const SfxUnoFrameItem* pFrameItem = rReq.GetArg<SfxUnoFrameItem>(SID_FILLFRAME);
+ if ( pFrameItem )
+ xFrame = pFrameItem->GetFrame();
+
+ LoadViewIntoFrame_Impl_NoThrow( *GetObjectShell(), xFrame, nViewId, false );
+
+ rReq.Done();
+ break;
+ }
+
+ case SID_OBJECT:
+ {
+ const SfxInt16Item* pItem = rReq.GetArg<SfxInt16Item>(SID_OBJECT);
+
+ if (pItem)
+ {
+ GetViewShell()->DoVerb( pItem->GetValue() );
+ rReq.Done();
+ break;
+ }
+ }
+ }
+}
+
+/* TODO as96863:
+ This method try to collect information about the count of currently open documents.
+ But the algorithm is implemented very simple ...
+ E.g. hidden documents should be ignored here ... but they are counted.
+ TODO: export special helper "framework::FrameListAnalyzer" within the framework module
+ and use it here.
+*/
+static bool impl_maxOpenDocCountReached()
+{
+ css::uno::Reference< css::uno::XComponentContext > xContext = ::comphelper::getProcessComponentContext();
+ std::optional<sal_Int32> x(officecfg::Office::Common::Misc::MaxOpenDocuments::get());
+ // NIL means: count of allowed documents = infinite !
+ if (!x)
+ return false;
+ sal_Int32 nMaxDocs(*x);
+ sal_Int32 nOpenDocs = 0;
+
+ css::uno::Reference< css::frame::XDesktop2 > xDesktop = css::frame::Desktop::create(xContext);
+ css::uno::Reference< css::container::XIndexAccess > xCont(xDesktop->getFrames(), css::uno::UNO_QUERY_THROW);
+
+ sal_Int32 c = xCont->getCount();
+ sal_Int32 i = 0;
+
+ for (i=0; i<c; ++i)
+ {
+ try
+ {
+ css::uno::Reference< css::frame::XFrame > xFrame;
+ xCont->getByIndex(i) >>= xFrame;
+ if ( ! xFrame.is())
+ continue;
+
+ // a) do not count the help window
+ if ( xFrame->getName() == "OFFICE_HELP_TASK" )
+ continue;
+
+ // b) count all other frames
+ ++nOpenDocs;
+ }
+ catch(const css::uno::Exception&)
+ // An IndexOutOfBoundsException can happen in multithreaded
+ // environments, where any other thread can change this
+ // container !
+ { continue; }
+ }
+
+ return (nOpenDocs >= nMaxDocs);
+}
+
+/* [Description]
+
+ This internal method returns in 'rSet' the Status for the <SfxShell>
+ Subclass SfxViewFrame in the <SVIDL> described <Slots>.
+
+ Thus exactly those Slots-IDs that are recognized as being invalid by Sfx
+ are included as Which-ranges in 'rSet'. If there exists a mapping for
+ single slot-IDs of the <SfxItemPool> set in the shell, then the respective
+ Which-IDs are used so that items can be replaced directly with a working
+ Core::sun::com::star::script::Engine of the Which-IDs if possible. .
+*/
+void SfxViewFrame::StateView_Impl
+(
+ SfxItemSet& rSet /* empty <SfxItemSet> with <Which-Ranges>,
+ which describes the Slot Ids */
+)
+{
+
+ SfxObjectShell *pDocSh = GetObjectShell();
+
+ if ( !pDocSh )
+ // I'm just on reload and am yielding myself ...
+ return;
+
+ const WhichRangesContainer & pRanges = rSet.GetRanges();
+ assert(!pRanges.empty() && "Set with no Range");
+ for ( auto const & pRange : pRanges )
+ {
+ sal_uInt16 nStartWhich = pRange.first;
+ sal_uInt16 nEndWhich = pRange.second;
+ for ( sal_uInt16 nWhich = nStartWhich; nWhich <= nEndWhich; ++nWhich )
+ {
+ switch(nWhich)
+ {
+ case SID_VIEWSHELL:
+ {
+ rSet.Put( SfxUInt16Item( nWhich, sal_uInt16(m_pImpl->nCurViewId )) );
+ break;
+ }
+
+ case SID_VIEWSHELL0:
+ case SID_VIEWSHELL1:
+ case SID_VIEWSHELL2:
+ case SID_VIEWSHELL3:
+ case SID_VIEWSHELL4:
+ {
+ sal_uInt16 nViewNo = nWhich - SID_VIEWSHELL0;
+ if ( GetObjectShell()->GetFactory().GetViewFactoryCount() >
+ nViewNo && !GetObjectShell()->IsInPlaceActive() )
+ {
+ SfxViewFactory &rViewFactory =
+ GetObjectShell()->GetFactory().GetViewFactory(nViewNo);
+ rSet.Put( SfxBoolItem(
+ nWhich, m_pImpl->nCurViewId == rViewFactory.GetOrdinal() ) );
+ }
+ else
+ rSet.DisableItem( nWhich );
+ break;
+ }
+
+ case SID_NEWWINDOW:
+ {
+ if ( !GetViewShell()->NewWindowAllowed()
+ || impl_maxOpenDocCountReached()
+ )
+ rSet.DisableItem( nWhich );
+ break;
+ }
+ }
+ }
+ }
+}
+
+
+void SfxViewFrame::ToTop()
+{
+ GetFrame().Appear();
+}
+
+
+/* [Description]
+
+ GetFrame returns the Frame, in which the ViewFrame is located.
+*/
+SfxFrame& SfxViewFrame::GetFrame() const
+{
+ return m_pImpl->rFrame;
+}
+
+SfxViewFrame* SfxViewFrame::GetTopViewFrame() const
+{
+ return GetFrame().GetCurrentViewFrame();
+}
+
+vcl::Window& SfxViewFrame::GetWindow() const
+{
+ return m_pImpl->pWindow ? *m_pImpl->pWindow : GetFrame().GetWindow();
+}
+
+weld::Window* SfxViewFrame::GetFrameWeld() const
+{
+ return GetWindow().GetFrameWeld();
+}
+
+bool SfxViewFrame::DoClose()
+{
+ return GetFrame().DoClose();
+}
+
+OUString SfxViewFrame::GetActualPresentationURL_Impl() const
+{
+ if ( m_xObjSh.is() )
+ return m_xObjSh->GetMedium()->GetName();
+ return OUString();
+}
+
+void SfxViewFrame::SetModalMode( bool bModal )
+{
+ // no real modality for LOK
+ if (comphelper::LibreOfficeKit::isActive())
+ return;
+
+ m_pImpl->bModal = bModal;
+ if ( m_xObjSh.is() )
+ {
+ for ( SfxViewFrame* pFrame = SfxViewFrame::GetFirst( m_xObjSh.get() );
+ !bModal && pFrame; pFrame = SfxViewFrame::GetNext( *pFrame, m_xObjSh.get() ) )
+ bModal = pFrame->m_pImpl->bModal;
+ m_xObjSh->SetModalMode_Impl( bModal );
+ }
+}
+
+bool SfxViewFrame::IsInModalMode() const
+{
+ return m_pImpl->bModal || GetFrame().GetWindow().IsInModalMode();
+}
+
+void SfxViewFrame::Resize( bool bForce )
+{
+ Size aSize = GetWindow().GetOutputSizePixel();
+ if ( !bForce && aSize == m_pImpl->aSize )
+ return;
+
+ m_pImpl->aSize = aSize;
+ SfxViewShell *pShell = GetViewShell();
+ if ( pShell )
+ {
+ if ( GetFrame().IsInPlace() )
+ {
+ Point aPoint = GetWindow().GetPosPixel();
+ DoAdjustPosSizePixel( pShell, aPoint, aSize, true );
+ }
+ else
+ {
+ DoAdjustPosSizePixel( pShell, Point(), aSize, false );
+ }
+ }
+}
+
+#if HAVE_FEATURE_SCRIPTING
+
+#define LINE_SEP 0x0A
+
+static void CutLines( OUString& rStr, sal_Int32 nStartLine, sal_Int32 nLines )
+{
+ sal_Int32 nStartPos = 0;
+ sal_Int32 nLine = 0;
+ while ( nLine < nStartLine )
+ {
+ nStartPos = rStr.indexOf( LINE_SEP, nStartPos );
+ if( nStartPos == -1 )
+ break;
+ nStartPos++; // not the \n.
+ nLine++;
+ }
+
+ SAL_WARN_IF(nStartPos == -1, "sfx.view", "CutLines: Start row not found!");
+
+ if ( nStartPos != -1 )
+ {
+ sal_Int32 nEndPos = nStartPos;
+ for ( sal_Int32 i = 0; i < nLines; i++ )
+ nEndPos = rStr.indexOf( LINE_SEP, nEndPos+1 );
+
+ if ( nEndPos == -1 ) // Can happen at the last row.
+ nEndPos = rStr.getLength();
+ else
+ nEndPos++;
+
+ rStr = OUString::Concat(rStr.subView( 0, nStartPos )) + rStr.subView( nEndPos );
+ }
+ // erase trailing lines
+ if ( nStartPos != -1 )
+ {
+ sal_Int32 n = nStartPos;
+ sal_Int32 nLen = rStr.getLength();
+ while ( ( n < nLen ) && ( rStr[ n ] == LINE_SEP ) )
+ n++;
+
+ if ( n > nStartPos )
+ rStr = OUString::Concat(rStr.subView( 0, nStartPos )) + rStr.subView( n );
+ }
+}
+
+#endif
+
+/*
+ add new recorded dispatch macro script into the application global basic
+ lib container. It generates a new unique id for it and insert the macro
+ by using this number as name for the module
+ */
+void SfxViewFrame::AddDispatchMacroToBasic_Impl( const OUString& sMacro )
+{
+#if !HAVE_FEATURE_SCRIPTING
+ (void) sMacro;
+#else
+ if ( sMacro.isEmpty() )
+ return;
+
+ SfxApplication* pSfxApp = SfxGetpApp();
+ SfxItemPool& rPool = pSfxApp->GetPool();
+ SfxRequest aReq(SID_BASICCHOOSER, SfxCallMode::SYNCHRON, rPool);
+
+ //seen in tdf#122598, no parent for subsequent dialog
+ SfxAllItemSet aSet(rPool);
+ css::uno::Reference< css::frame::XFrame > xFrame =
+ GetFrame().GetFrameInterface();
+ aSet.Put(SfxUnoFrameItem(SID_FILLFRAME, xFrame));
+ aReq.SetInternalArgs_Impl(aSet);
+
+ aReq.AppendItem( SfxBoolItem(SID_RECORDMACRO,true) );
+ const SfxPoolItemHolder& rResult(SfxGetpApp()->ExecuteSlot(aReq));
+ OUString aScriptURL;
+ if (nullptr != rResult.getItem())
+ aScriptURL = static_cast<const SfxStringItem*>(rResult.getItem())->GetValue();
+ if ( !aScriptURL.isEmpty() )
+ {
+ // parse scriptURL
+ OUString aLibName;
+ OUString aModuleName;
+ OUString aMacroName;
+ OUString aLocation;
+ Reference< XComponentContext > xContext = ::comphelper::getProcessComponentContext();
+ Reference< css::uri::XUriReferenceFactory > xFactory =
+ css::uri::UriReferenceFactory::create( xContext );
+ Reference< css::uri::XVndSunStarScriptUrl > xUrl( xFactory->parse( aScriptURL ), UNO_QUERY );
+ if ( xUrl.is() )
+ {
+ // get name
+ const OUString aName = xUrl->getName();
+ const sal_Unicode cTok = '.';
+ sal_Int32 nIndex = 0;
+ aLibName = aName.getToken( 0, cTok, nIndex );
+ if ( nIndex != -1 )
+ aModuleName = aName.getToken( 0, cTok, nIndex );
+ if ( nIndex != -1 )
+ aMacroName = aName.getToken( 0, cTok, nIndex );
+
+ // get location
+ aLocation = xUrl->getParameter( "location" );
+ }
+
+ BasicManager* pBasMgr = nullptr;
+ if ( aLocation == "application" )
+ {
+ // application basic
+ pBasMgr = SfxApplication::GetBasicManager();
+ }
+ else if ( aLocation == "document" )
+ {
+ pBasMgr = GetObjectShell()->GetBasicManager();
+ }
+
+ OUString aOUSource;
+ if ( pBasMgr)
+ {
+ StarBASIC* pBasic = pBasMgr->GetLib( aLibName );
+ if ( pBasic )
+ {
+ SbModule* pModule = pBasic->FindModule( aModuleName );
+ SbMethod* pMethod = pModule ? pModule->FindMethod(aMacroName, SbxClassType::Method) : nullptr;
+ if (pMethod)
+ {
+ aOUSource = pModule->GetSource32();
+ sal_uInt16 nStart, nEnd;
+ pMethod->GetLineRange( nStart, nEnd );
+ sal_uInt16 nlStart = nStart;
+ sal_uInt16 nlEnd = nEnd;
+ CutLines( aOUSource, nlStart-1, nlEnd-nlStart+1 );
+ }
+ }
+ }
+
+ // open lib container and break operation if it couldn't be opened
+ css::uno::Reference< css::script::XLibraryContainer > xLibCont;
+ if ( aLocation == "application" )
+ {
+ xLibCont = SfxGetpApp()->GetBasicContainer();
+ }
+ else if ( aLocation == "document" )
+ {
+ xLibCont = GetObjectShell()->GetBasicContainer();
+ }
+
+ if(!xLibCont.is())
+ {
+ SAL_WARN("sfx.view", "couldn't get access to the basic lib container. Adding of macro isn't possible.");
+ return;
+ }
+
+ // get LibraryContainer
+ css::uno::Any aTemp;
+
+ css::uno::Reference< css::container::XNameAccess > xLib;
+ if(xLibCont->hasByName(aLibName))
+ {
+ // library must be loaded
+ aTemp = xLibCont->getByName(aLibName);
+ xLibCont->loadLibrary(aLibName);
+ aTemp >>= xLib;
+ }
+ else
+ {
+ xLib = xLibCont->createLibrary(aLibName);
+ }
+
+ // pack the macro as direct usable "sub" routine
+ OUStringBuffer sRoutine(10000);
+ bool bReplace = false;
+
+ // get module
+ if(xLib->hasByName(aModuleName))
+ {
+ if ( !aOUSource.isEmpty() )
+ {
+ sRoutine.append( aOUSource );
+ }
+ else
+ {
+ OUString sCode;
+ aTemp = xLib->getByName(aModuleName);
+ aTemp >>= sCode;
+ sRoutine.append( sCode );
+ }
+
+ bReplace = true;
+ }
+
+ // append new method
+ sRoutine.append( "\nsub "
+ + aMacroName
+ + "\n"
+ + sMacro
+ + "\nend sub\n" );
+
+ // create the module inside the library and insert the macro routine
+ aTemp <<= sRoutine.makeStringAndClear();
+ if ( bReplace )
+ {
+ css::uno::Reference< css::container::XNameContainer > xModulCont(
+ xLib,
+ css::uno::UNO_QUERY);
+ xModulCont->replaceByName(aModuleName,aTemp);
+ }
+ else
+ {
+ css::uno::Reference< css::container::XNameContainer > xModulCont(
+ xLib,
+ css::uno::UNO_QUERY);
+ xModulCont->insertByName(aModuleName,aTemp);
+ }
+
+ // #i17355# update the Basic IDE
+ for ( SfxViewShell* pViewShell = SfxViewShell::GetFirst(); pViewShell; pViewShell = SfxViewShell::GetNext( *pViewShell ) )
+ {
+ if ( pViewShell->GetName() == "BasicIDE" )
+ {
+ SfxViewFrame& rViewFrame = pViewShell->GetViewFrame();
+ SfxDispatcher* pDispat = rViewFrame.GetDispatcher();
+ if ( pDispat )
+ {
+ SfxMacroInfoItem aInfoItem( SID_BASICIDE_ARG_MACROINFO, pBasMgr, aLibName, aModuleName, OUString(), OUString() );
+ pDispat->ExecuteList(SID_BASICIDE_UPDATEMODULESOURCE,
+ SfxCallMode::SYNCHRON, { &aInfoItem });
+ }
+ }
+ }
+ }
+ else
+ {
+ // add code for "session only" macro
+ }
+#endif
+}
+
+void SfxViewFrame::MiscExec_Impl( SfxRequest& rReq )
+{
+ switch ( rReq.GetSlot() )
+ {
+ case SID_STOP_RECORDING :
+ case SID_RECORDMACRO :
+ {
+ // try to find any active recorder on this frame
+ static constexpr OUString sProperty(u"DispatchRecorderSupplier"_ustr);
+ css::uno::Reference< css::frame::XFrame > xFrame =
+ GetFrame().GetFrameInterface();
+
+ css::uno::Reference< css::beans::XPropertySet > xSet(xFrame,css::uno::UNO_QUERY);
+ css::uno::Any aProp = xSet->getPropertyValue(sProperty);
+ css::uno::Reference< css::frame::XDispatchRecorderSupplier > xSupplier;
+ aProp >>= xSupplier;
+ css::uno::Reference< css::frame::XDispatchRecorder > xRecorder;
+ if (xSupplier.is())
+ xRecorder = xSupplier->getDispatchRecorder();
+
+ bool bIsRecording = xRecorder.is();
+ const SfxBoolItem* pItem = rReq.GetArg<SfxBoolItem>(SID_RECORDMACRO);
+ if ( pItem && pItem->GetValue() == bIsRecording )
+ return;
+
+ if ( xRecorder.is() )
+ {
+ // disable active recording
+ aProp <<= css::uno::Reference< css::frame::XDispatchRecorderSupplier >();
+ xSet->setPropertyValue(sProperty,aProp);
+
+ const SfxBoolItem* pRecordItem = rReq.GetArg<SfxBoolItem>(FN_PARAM_1);
+ if ( !pRecordItem || !pRecordItem->GetValue() )
+ // insert script into basic library container of application
+ AddDispatchMacroToBasic_Impl(xRecorder->getRecordedMacro());
+
+ xRecorder->endRecording();
+ xRecorder = nullptr;
+ GetBindings().SetRecorder_Impl( xRecorder );
+
+ SetChildWindow( SID_RECORDING_FLOATWINDOW, false );
+ if ( rReq.GetSlot() != SID_RECORDMACRO )
+ GetBindings().Invalidate( SID_RECORDMACRO );
+ }
+ else if ( rReq.GetSlot() == SID_RECORDMACRO )
+ {
+ // enable recording
+ css::uno::Reference< css::uno::XComponentContext > xContext(
+ ::comphelper::getProcessComponentContext());
+
+ xRecorder = css::frame::DispatchRecorder::create( xContext );
+
+ xSupplier = css::frame::DispatchRecorderSupplier::create( xContext );
+
+ xSupplier->setDispatchRecorder(xRecorder);
+ xRecorder->startRecording(xFrame);
+ aProp <<= xSupplier;
+ xSet->setPropertyValue(sProperty,aProp);
+ GetBindings().SetRecorder_Impl( xRecorder );
+ SetChildWindow( SID_RECORDING_FLOATWINDOW, true );
+ }
+
+ rReq.Done();
+ break;
+ }
+
+ case SID_TOGGLESTATUSBAR:
+ {
+ if ( auto xLayoutManager = getLayoutManager(GetFrame()) )
+ {
+ static constexpr OUString aStatusbarResString( u"private:resource/statusbar/statusbar"_ustr );
+ // Evaluate parameter.
+ const SfxBoolItem* pShowItem = rReq.GetArg<SfxBoolItem>(rReq.GetSlot());
+ bool bShow( true );
+ if ( !pShowItem )
+ bShow = xLayoutManager->isElementVisible( aStatusbarResString );
+ else
+ bShow = pShowItem->GetValue();
+
+ if ( bShow )
+ {
+ xLayoutManager->createElement( aStatusbarResString );
+ xLayoutManager->showElement( aStatusbarResString );
+ }
+ else
+ xLayoutManager->hideElement( aStatusbarResString );
+
+ if ( !pShowItem )
+ rReq.AppendItem( SfxBoolItem( SID_TOGGLESTATUSBAR, bShow ) );
+ }
+ rReq.Done();
+ break;
+ }
+ case SID_COMMAND_POPUP:
+ {
+ tools::Rectangle aRectangle(Point(0,0), GetWindow().GetSizePixel());
+ weld::Window* pParent = weld::GetPopupParent(GetWindow(), aRectangle);
+ m_pCommandPopupHandler->showPopup(pParent, GetFrame().GetFrameInterface());
+
+ rReq.Done();
+ break;
+ }
+ case SID_WIN_FULLSCREEN:
+ {
+ const SfxBoolItem* pItem = rReq.GetArg<SfxBoolItem>(rReq.GetSlot());
+ SfxViewFrame *pTop = GetTopViewFrame();
+ if ( pTop )
+ {
+ WorkWindow* pWork = static_cast<WorkWindow*>( pTop->GetFrame().GetTopWindow_Impl() );
+ if ( pWork )
+ {
+ Reference< css::frame::XLayoutManager > xLayoutManager = getLayoutManager(GetFrame());
+ bool bNewFullScreenMode = pItem ? pItem->GetValue() : !pWork->IsFullScreenMode();
+ if ( bNewFullScreenMode != pWork->IsFullScreenMode() )
+ {
+ if ( bNewFullScreenMode )
+ sfx2::SfxNotebookBar::LockNotebookBar();
+ else
+ sfx2::SfxNotebookBar::UnlockNotebookBar();
+
+ Reference< css::beans::XPropertySet > xLMPropSet( xLayoutManager, UNO_QUERY );
+ if ( xLMPropSet.is() )
+ {
+ try
+ {
+ xLMPropSet->setPropertyValue(
+ "HideCurrentUI",
+ Any( bNewFullScreenMode ));
+ }
+ catch ( css::beans::UnknownPropertyException& )
+ {
+ }
+ }
+ pWork->ShowFullScreenMode( bNewFullScreenMode );
+ pWork->SetMenuBarMode( bNewFullScreenMode ? MenuBarMode::Hide : MenuBarMode::Normal );
+ GetFrame().GetWorkWindow_Impl()->SetFullScreen_Impl( bNewFullScreenMode );
+ if ( !pItem )
+ rReq.AppendItem( SfxBoolItem( SID_WIN_FULLSCREEN, bNewFullScreenMode ) );
+ rReq.Done();
+ }
+ else
+ rReq.Ignore();
+ }
+ }
+ else
+ rReq.Ignore();
+
+ GetDispatcher()->Update_Impl( true );
+ break;
+ }
+ }
+}
+
+void SfxViewFrame::MiscState_Impl(SfxItemSet &rSet)
+{
+ const WhichRangesContainer & pRanges = rSet.GetRanges();
+ DBG_ASSERT(!pRanges.empty(), "Set without range");
+ for ( auto const & pRange : pRanges )
+ {
+ for(sal_uInt16 nWhich = pRange.first; nWhich <= pRange.second; ++nWhich)
+ {
+ switch(nWhich)
+ {
+ case SID_CURRENT_URL:
+ {
+ rSet.Put( SfxStringItem( nWhich, GetActualPresentationURL_Impl() ) );
+ break;
+ }
+
+ case SID_RECORDMACRO :
+ {
+ const OUString& sName{GetObjectShell()->GetFactory().GetFactoryName()};
+ bool bMacrosDisabled = officecfg::Office::Common::Security::Scripting::DisableMacrosExecution::get();
+ if (bMacrosDisabled ||
+ !officecfg::Office::Common::Misc::MacroRecorderMode::get() ||
+ ( sName!="swriter" && sName!="scalc" ) )
+ {
+ rSet.DisableItem( nWhich );
+ rSet.Put(SfxVisibilityItem(nWhich, false));
+ break;
+ }
+
+ css::uno::Reference< css::beans::XPropertySet > xSet(
+ GetFrame().GetFrameInterface(),
+ css::uno::UNO_QUERY);
+
+ css::uno::Any aProp = xSet->getPropertyValue("DispatchRecorderSupplier");
+ css::uno::Reference< css::frame::XDispatchRecorderSupplier > xSupplier;
+ if ( aProp >>= xSupplier )
+ rSet.Put( SfxBoolItem( nWhich, xSupplier.is() ) );
+ else
+ rSet.DisableItem( nWhich );
+ break;
+ }
+
+ case SID_STOP_RECORDING :
+ {
+ const OUString& sName{GetObjectShell()->GetFactory().GetFactoryName()};
+ if ( !officecfg::Office::Common::Misc::MacroRecorderMode::get() ||
+ ( sName!="swriter" && sName!="scalc" ) )
+ {
+ rSet.DisableItem( nWhich );
+ break;
+ }
+
+ css::uno::Reference< css::beans::XPropertySet > xSet(
+ GetFrame().GetFrameInterface(),
+ css::uno::UNO_QUERY);
+
+ css::uno::Any aProp = xSet->getPropertyValue("DispatchRecorderSupplier");
+ css::uno::Reference< css::frame::XDispatchRecorderSupplier > xSupplier;
+ if ( !(aProp >>= xSupplier) || !xSupplier.is() )
+ rSet.DisableItem( nWhich );
+ break;
+ }
+
+ case SID_TOGGLESTATUSBAR:
+ {
+ css::uno::Reference< css::frame::XLayoutManager > xLayoutManager;
+ css::uno::Reference< css::beans::XPropertySet > xSet(
+ GetFrame().GetFrameInterface(),
+ css::uno::UNO_QUERY);
+ css::uno::Any aProp = xSet->getPropertyValue( "LayoutManager" );
+
+ if ( !( aProp >>= xLayoutManager ))
+ rSet.Put( SfxBoolItem( nWhich, false ));
+ else
+ {
+ bool bShow = xLayoutManager->isElementVisible( "private:resource/statusbar/statusbar" );
+ rSet.Put( SfxBoolItem( nWhich, bShow ));
+ }
+ break;
+ }
+
+ case SID_WIN_FULLSCREEN:
+ {
+ SfxViewFrame* pTop = GetTopViewFrame();
+ if ( pTop )
+ {
+ WorkWindow* pWork = static_cast<WorkWindow*>( pTop->GetFrame().GetTopWindow_Impl() );
+ if ( pWork )
+ {
+ rSet.Put( SfxBoolItem( nWhich, pWork->IsFullScreenMode() ) );
+ break;
+ }
+ }
+
+ rSet.DisableItem( nWhich );
+ break;
+ }
+
+ default:
+ break;
+ }
+ }
+ }
+}
+
+/* [Description]
+
+ This method can be included in the Execute method for the on- and off-
+ switching of ChildWindows, to implement this and API-bindings.
+
+ Simply include as 'ExecuteMethod' in the IDL.
+*/
+void SfxViewFrame::ChildWindowExecute( SfxRequest &rReq )
+{
+ // Evaluate Parameter
+ sal_uInt16 nSID = rReq.GetSlot();
+
+ if (nSID == SID_SIDEBAR_DECK)
+ {
+ const SfxStringItem* pDeckIdItem = rReq.GetArg<SfxStringItem>(SID_SIDEBAR_DECK);
+ if (pDeckIdItem)
+ {
+ const OUString aDeckId(pDeckIdItem->GetValue());
+ const SfxBoolItem* pToggleItem = rReq.GetArg<SfxBoolItem>(SID_SIDEBAR_DECK_TOGGLE);
+ bool bToggle = pToggleItem && pToggleItem->GetValue();
+ ::sfx2::sidebar::Sidebar::ShowDeck(aDeckId, this, bToggle);
+ }
+ rReq.Done();
+ return;
+ }
+
+ const SfxBoolItem* pShowItem = rReq.GetArg<SfxBoolItem>(nSID);
+ if ( nSID == SID_VIEW_DATA_SOURCE_BROWSER )
+ {
+ if (!SvtModuleOptions().IsModuleInstalled(SvtModuleOptions::EModule::DATABASE))
+ return;
+ Reference < XFrame > xFrame = GetFrame().GetFrameInterface();
+ Reference < XFrame > xBeamer( xFrame->findFrame( "_beamer", FrameSearchFlag::CHILDREN ) );
+ bool bHasChild = xBeamer.is();
+ bool bShow = pShowItem ? pShowItem->GetValue() : !bHasChild;
+ if ( pShowItem )
+ {
+ if( bShow == bHasChild )
+ return;
+ }
+ else
+ rReq.AppendItem( SfxBoolItem( nSID, bShow ) );
+
+ if ( !bShow )
+ {
+ SetChildWindow( SID_BROWSER, false );
+ }
+ else
+ {
+ css::util::URL aTargetURL;
+ aTargetURL.Complete = ".component:DB/DataSourceBrowser";
+ Reference < css::util::XURLTransformer > xTrans(
+ css::util::URLTransformer::create(
+ ::comphelper::getProcessComponentContext() ) );
+ xTrans->parseStrict( aTargetURL );
+
+ Reference < XDispatchProvider > xProv( xFrame, UNO_QUERY );
+ Reference < css::frame::XDispatch > xDisp;
+ if ( xProv.is() )
+ xDisp = xProv->queryDispatch( aTargetURL, "_beamer", 31 );
+ if ( xDisp.is() )
+ {
+ Sequence < css::beans::PropertyValue > aArgs(1);
+ css::beans::PropertyValue* pArg = aArgs.getArray();
+ pArg[0].Name = "Referer";
+ pArg[0].Value <<= OUString("private:user");
+ xDisp->dispatch( aTargetURL, aArgs );
+ }
+ }
+
+ rReq.Done();
+ return;
+ }
+ if (nSID == SID_STYLE_DESIGNER)
+ {
+ // First make sure that the sidebar is visible
+ ShowChildWindow(SID_SIDEBAR);
+
+ ::sfx2::sidebar::Sidebar::ShowPanel(u"StyleListPanel",
+ GetFrame().GetFrameInterface(), true);
+ rReq.Done();
+ return;
+ }
+ if (nSID == SID_NAVIGATOR)
+ {
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ ShowChildWindow(SID_SIDEBAR);
+ ::sfx2::sidebar::Sidebar::ShowDeck(u"NavigatorDeck", this, true);
+ rReq.Done();
+ return;
+ }
+ }
+
+ bool bHasChild = HasChildWindow(nSID);
+ bool bShow = pShowItem ? pShowItem->GetValue() : !bHasChild;
+ GetDispatcher()->Update_Impl( true );
+
+ // Perform action.
+ if ( !pShowItem || bShow != bHasChild )
+ ToggleChildWindow( nSID );
+
+ GetBindings().Invalidate( nSID );
+
+ // Record if possible.
+ if ( nSID == SID_HYPERLINK_DIALOG || nSID == SID_SEARCH_DLG )
+ {
+ rReq.Ignore();
+ }
+ else
+ {
+ rReq.AppendItem( SfxBoolItem( nSID, bShow ) );
+ rReq.Done();
+ }
+}
+
+/* [Description]
+
+ This method can be used in the state method for the on and off-state
+ of child-windows, in order to implement this.
+
+ Just register the IDL as 'StateMethod'.
+*/
+void SfxViewFrame::ChildWindowState( SfxItemSet& rState )
+{
+ SfxWhichIter aIter( rState );
+ for ( sal_uInt16 nSID = aIter.FirstWhich(); nSID; nSID = aIter.NextWhich() )
+ {
+ if ( nSID == SID_VIEW_DATA_SOURCE_BROWSER )
+ {
+ rState.Put( SfxBoolItem( nSID, HasChildWindow( SID_BROWSER ) ) );
+ }
+ else if ( nSID == SID_HYPERLINK_DIALOG )
+ {
+ SfxPoolItemHolder aDummy;
+ SfxItemState eState = GetDispatcher()->QueryState(SID_HYPERLINK_SETLINK, aDummy);
+ if ( SfxItemState::DISABLED == eState )
+ rState.DisableItem(nSID);
+ else
+ {
+ if ( KnowsChildWindow(nSID) )
+ rState.Put( SfxBoolItem( nSID, HasChildWindow(nSID)) );
+ else
+ rState.DisableItem(nSID);
+ }
+ }
+ else if ( nSID == SID_BROWSER )
+ {
+ Reference < XFrame > xFrame = GetFrame().GetFrameInterface()->
+ findFrame( "_beamer", FrameSearchFlag::CHILDREN );
+ if ( !xFrame.is() )
+ rState.DisableItem( nSID );
+ else if ( KnowsChildWindow(nSID) )
+ rState.Put( SfxBoolItem( nSID, HasChildWindow(nSID) ) );
+ }
+ else if ( nSID == SID_SIDEBAR )
+ {
+ if ( !KnowsChildWindow( nSID ) )
+ {
+ SAL_INFO("sfx.view", "SID_SIDEBAR state requested, but no task pane child window exists for this ID!");
+ rState.DisableItem( nSID );
+ }
+ else
+ {
+ rState.Put( SfxBoolItem( nSID, HasChildWindow( nSID ) ) );
+ }
+ }
+ else if ( KnowsChildWindow(nSID) )
+ rState.Put( SfxBoolItem( nSID, HasChildWindow(nSID) ) );
+ else
+ rState.DisableItem(nSID);
+ }
+}
+
+SfxWorkWindow* SfxViewFrame::GetWorkWindow_Impl()
+{
+ SfxWorkWindow* pWork = GetFrame().GetWorkWindow_Impl();
+ return pWork;
+}
+
+void SfxViewFrame::SetChildWindow(sal_uInt16 nId, bool bOn, bool bSetFocus )
+{
+ SfxWorkWindow* pWork = GetWorkWindow_Impl();
+ if ( pWork )
+ pWork->SetChildWindow_Impl( nId, bOn, bSetFocus );
+}
+
+void SfxViewFrame::ToggleChildWindow(sal_uInt16 nId)
+{
+ SfxWorkWindow* pWork = GetWorkWindow_Impl();
+ if ( pWork )
+ pWork->ToggleChildWindow_Impl( nId, true );
+}
+
+bool SfxViewFrame::HasChildWindow( sal_uInt16 nId )
+{
+ SfxWorkWindow* pWork = GetWorkWindow_Impl();
+ return pWork && pWork->HasChildWindow_Impl(nId);
+}
+
+bool SfxViewFrame::KnowsChildWindow( sal_uInt16 nId )
+{
+ SfxWorkWindow* pWork = GetWorkWindow_Impl();
+ return pWork && pWork->KnowsChildWindow_Impl(nId);
+}
+
+void SfxViewFrame::ShowChildWindow( sal_uInt16 nId, bool bVisible )
+{
+ SfxWorkWindow* pWork = GetWorkWindow_Impl();
+ if ( pWork )
+ {
+ GetDispatcher()->Update_Impl(true);
+ pWork->ShowChildWindow_Impl(nId, bVisible, true );
+ }
+}
+
+SfxChildWindow* SfxViewFrame::GetChildWindow(sal_uInt16 nId)
+{
+ SfxWorkWindow* pWork = GetWorkWindow_Impl();
+ return pWork ? pWork->GetChildWindow_Impl(nId) : nullptr;
+}
+
+void SfxViewFrame::UpdateDocument_Impl()
+{
+ SfxObjectShell* pDoc = GetObjectShell();
+ if ( pDoc->IsLoadingFinished() )
+ pDoc->CheckSecurityOnLoading_Impl();
+
+ // check if document depends on a template
+ pDoc->UpdateFromTemplate_Impl();
+}
+
+void SfxViewFrame::SetViewFrame( SfxViewFrame* pFrame )
+{
+ if(pFrame)
+ SetSVHelpData(pFrame->m_pHelpData);
+
+ SetSVWinData(pFrame ? pFrame->m_pWinData : nullptr);
+
+ SfxGetpApp()->SetViewFrame_Impl( pFrame );
+}
+
+VclPtr<SfxInfoBarWindow> SfxViewFrame::AppendInfoBar(const OUString& sId,
+ const OUString& sPrimaryMessage,
+ const OUString& sSecondaryMessage,
+ InfobarType aInfobarType, bool bShowCloseButton)
+{
+ SfxChildWindow* pChild = GetChildWindow(SfxInfoBarContainerChild::GetChildWindowId());
+ if (!pChild)
+ return nullptr;
+
+ if (HasInfoBarWithID(sId))
+ return nullptr;
+
+ SfxInfoBarContainerWindow* pInfoBarContainer = static_cast<SfxInfoBarContainerWindow*>(pChild->GetWindow());
+ auto pInfoBar = pInfoBarContainer->appendInfoBar(sId, sPrimaryMessage, sSecondaryMessage,
+ aInfobarType, bShowCloseButton);
+ ShowChildWindow(SfxInfoBarContainerChild::GetChildWindowId());
+ return pInfoBar;
+}
+
+void SfxViewFrame::UpdateInfoBar(std::u16string_view sId, const OUString& sPrimaryMessage,
+ const OUString& sSecondaryMessage, InfobarType eType)
+{
+ const sal_uInt16 nId = SfxInfoBarContainerChild::GetChildWindowId();
+
+ // Make sure the InfoBar container is visible
+ if (!HasChildWindow(nId))
+ ToggleChildWindow(nId);
+
+ SfxChildWindow* pChild = GetChildWindow(nId);
+ if (pChild)
+ {
+ SfxInfoBarContainerWindow* pInfoBarContainer = static_cast<SfxInfoBarContainerWindow*>(pChild->GetWindow());
+ auto pInfoBar = pInfoBarContainer->getInfoBar(sId);
+
+ if (pInfoBar)
+ pInfoBar->Update(sPrimaryMessage, sSecondaryMessage, eType);
+ }
+}
+
+void SfxViewFrame::RemoveInfoBar( std::u16string_view sId )
+{
+ const sal_uInt16 nId = SfxInfoBarContainerChild::GetChildWindowId();
+
+ // Make sure the InfoBar container is visible
+ if (!HasChildWindow(nId))
+ ToggleChildWindow(nId);
+
+ SfxChildWindow* pChild = GetChildWindow(nId);
+ if (pChild)
+ {
+ SfxInfoBarContainerWindow* pInfoBarContainer = static_cast<SfxInfoBarContainerWindow*>(pChild->GetWindow());
+ auto pInfoBar = pInfoBarContainer->getInfoBar(sId);
+ pInfoBarContainer->removeInfoBar(pInfoBar);
+ ShowChildWindow(nId);
+ }
+}
+
+bool SfxViewFrame::HasInfoBarWithID( std::u16string_view sId )
+{
+ const sal_uInt16 nId = SfxInfoBarContainerChild::GetChildWindowId();
+
+ SfxChildWindow* pChild = GetChildWindow(nId);
+ if (pChild)
+ {
+ SfxInfoBarContainerWindow* pInfoBarContainer = static_cast<SfxInfoBarContainerWindow*>(pChild->GetWindow());
+ return pInfoBarContainer->hasInfoBarWithID(sId);
+ }
+
+ return false;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/view/viewfrm2.cxx b/sfx2/source/view/viewfrm2.cxx
new file mode 100644
index 0000000000..0af0c2c5bc
--- /dev/null
+++ b/sfx2/source/view/viewfrm2.cxx
@@ -0,0 +1,380 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include "impviewframe.hxx"
+#include <statcach.hxx>
+#include <workwin.hxx>
+
+#include <sfx2/app.hxx>
+#include <sfx2/bindings.hxx>
+#include <sfx2/ctrlitem.hxx>
+#include <sfx2/dispatch.hxx>
+#include <sfx2/docfac.hxx>
+#include <sfx2/docfile.hxx>
+#include <sfx2/objitem.hxx>
+#include <sfx2/objsh.hxx>
+#include <sfx2/request.hxx>
+#include <sfx2/sfxsids.hrc>
+#include <sfx2/viewfrm.hxx>
+#include <sfx2/viewsh.hxx>
+#include <com/sun/star/lang/DisposedException.hpp>
+#include <com/sun/star/util/CloseVetoException.hpp>
+#include <com/sun/star/util/XCloseable.hpp>
+#include <com/sun/star/embed/VerbDescriptor.hpp>
+
+#include <osl/diagnose.h>
+#include <svl/eitem.hxx>
+#include <svl/stritem.hxx>
+#include <tools/urlobj.hxx>
+#include <sal/log.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::util;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::beans;
+
+
+void SfxFrameViewWindow_Impl::StateChanged( StateChangedType nStateChange )
+{
+ if ( nStateChange == StateChangedType::InitShow )
+ {
+ SfxObjectShell* pDoc = pFrame->GetObjectShell();
+ if ( pDoc && !pFrame->IsVisible() )
+ pFrame->Show();
+
+ pFrame->Resize();
+ }
+ else
+ Window::StateChanged( nStateChange );
+}
+
+void SfxFrameViewWindow_Impl::Resize()
+{
+ if ( IsReallyVisible() || IsReallyShown() || GetOutputSizePixel().Width() )
+ pFrame->Resize();
+}
+
+
+void SfxViewFrame::UpdateTitle()
+
+/* [Description]
+
+ With this method, can the SfxViewFrame be forced to immediately provide
+ the new title from the <SfxObjectShell>.
+
+ [Note]
+
+ This is for example necessary if one listens to the SfxObjectShell as
+ SfxListener and then react on the <SfxSimpleHint> SfxHintId::TitleChanged,
+ then query the title of his views. However these views (SfxTopViewFrames)
+ are also SfxListener and because the order of notifications might not be
+ fixed, the title update will be enforced in advance.
+
+ [Example]
+
+ void SwDocShell::Notify( SfxBroadcaster& rBC, const SfxHint& rHint )
+ {
+ if ( dynamic_cast<const SfxSimpleHint *>(&rHint) != nullptr )
+ {
+ switch( ( (SfxSimpleHint&) rHint ).GetId() )
+ {
+ case SfxHintId::TitleChanged:
+ for ( SfxViewFrame *pTop = SfxViewFrame::GetFirst( this );
+ pTop;
+ pTop = SfxViewFrame::GetNext( this );
+ {
+ pTop->UpdateTitle();
+ ... pTop->GetName() ...
+ }
+ break;
+ ...
+ }
+ }
+ }
+*/
+
+{
+
+ const SfxObjectFactory &rFact = GetObjectShell()->GetFactory();
+ m_pImpl->aFactoryName = rFact.GetFactoryName();
+
+ SfxObjectShell *pObjSh = GetObjectShell();
+ if ( !pObjSh )
+ return;
+
+
+ const SfxMedium *pMedium = pObjSh->GetMedium();
+ OUString aURL;
+ GetFrame(); // -Wall required??
+ if ( pObjSh->HasName() )
+ {
+ INetURLObject aTmp( pMedium->GetName() );
+ aURL = aTmp.getName( INetURLObject::LAST_SEGMENT, true, INetURLObject::DecodeMechanism::WithCharset );
+ }
+
+ if ( aURL != m_pImpl->aActualURL )
+ // URL has changed
+ m_pImpl->aActualURL = aURL;
+
+ // SbxObjects name
+ OUString aSbxName = pObjSh->SfxShell::GetName();
+ if ( IsVisible() )
+ {
+ aSbxName += ":" + OUString::number(m_pImpl->nDocViewNo);
+ }
+
+ SetName( aSbxName );
+ GetBindings().Invalidate( SID_CURRENT_URL );
+ GetBindings().Invalidate( SID_NEWDOCDIRECT );
+}
+
+void SfxViewFrame::Exec_Impl(SfxRequest &rReq )
+{
+ // If presently the shells are replaced...
+ if ( !GetObjectShell() || !GetViewShell() )
+ return;
+
+ switch ( rReq.GetSlot() )
+ {
+ case SID_SHOWPOPUPS :
+ {
+ const SfxBoolItem* pShowItem = rReq.GetArg<SfxBoolItem>(SID_SHOWPOPUPS);
+ bool bShow = pShowItem == nullptr || pShowItem->GetValue();
+
+ SfxWorkWindow *pWorkWin = GetFrame().GetWorkWindow_Impl();
+ if ( bShow )
+ {
+ // First, make the floats viewable
+ pWorkWin->MakeChildrenVisible_Impl(true);
+ GetDispatcher()->Update_Impl( true );
+
+ // Then view it
+ GetBindings().HidePopups(false);
+ }
+ else
+ {
+ pWorkWin->HidePopups_Impl(true);
+ pWorkWin->MakeChildrenVisible_Impl(false);
+ }
+
+ Invalidate( rReq.GetSlot() );
+ rReq.Done();
+ break;
+ }
+
+ case SID_ACTIVATE:
+ {
+ MakeActive_Impl( true );
+ rReq.SetReturnValue(SfxObjectItem(0, this));
+ break;
+ }
+
+ case SID_NEWDOCDIRECT :
+ {
+ const SfxStringItem* pFactoryItem = rReq.GetArg<SfxStringItem>(SID_NEWDOCDIRECT);
+ OUString aFactName;
+ if ( pFactoryItem )
+ aFactName = pFactoryItem->GetValue();
+ else if ( !m_pImpl->aFactoryName.isEmpty() )
+ aFactName = m_pImpl->aFactoryName;
+ else
+ {
+ SAL_WARN("sfx.view", "Missing argument!");
+ break;
+ }
+
+ SfxRequest aReq( SID_OPENDOC, SfxCallMode::SYNCHRON, GetPool() );
+ const OUString aFact("private:factory/" + aFactName);
+ aReq.AppendItem( SfxStringItem( SID_FILE_NAME, aFact ) );
+ aReq.AppendItem( SfxFrameItem( SID_DOCFRAME, &GetFrame() ) );
+ aReq.AppendItem( SfxStringItem( SID_TARGETNAME, "_blank" ) );
+ SfxGetpApp()->ExecuteSlot( aReq );
+
+ const SfxViewFrameItem* pItem(dynamic_cast<const SfxViewFrameItem*>(aReq.GetReturnValue().getItem()));
+ if (nullptr != pItem)
+ rReq.SetReturnValue(SfxFrameItem(0, pItem->GetFrame()));
+ break;
+ }
+
+ case SID_CLOSEWIN:
+ {
+ // disable CloseWin, if frame is not a task
+ Reference < XCloseable > xTask( GetFrame().GetFrameInterface(), UNO_QUERY );
+ if ( !xTask.is() )
+ break;
+
+ if ( GetViewShell()->PrepareClose() )
+ {
+ // More Views on the same Document?
+ SfxObjectShell *pDocSh = GetObjectShell();
+ bool bOther = false;
+ for ( const SfxViewFrame* pFrame = SfxViewFrame::GetFirst( pDocSh );
+ !bOther && pFrame;
+ pFrame = SfxViewFrame::GetNext( *pFrame, pDocSh ) )
+ bOther = (pFrame != this);
+
+ // Document only needs to be queried, if no other View present.
+ bool bClosed = false;
+ if ( bOther || pDocSh->PrepareClose( true/*bUI*/ ) )
+ {
+ if ( !bOther )
+ pDocSh->SetModified( false );
+ rReq.Done(); // Must call this before Close()!
+ bClosed = false;
+ try
+ {
+ xTask->close(true);
+ bClosed = true;
+ }
+ catch (css::lang::DisposedException &) {
+ // already closed; ignore
+ }
+ catch( CloseVetoException& )
+ {
+ bClosed = false;
+ }
+ }
+
+ rReq.SetReturnValue(SfxBoolItem(rReq.GetSlot(), bClosed));
+ }
+ return;
+ }
+ }
+
+ rReq.Done();
+}
+
+void SfxViewFrame::GetState_Impl( SfxItemSet &rSet )
+{
+ SfxObjectShell *pDocSh = GetObjectShell();
+
+ if ( !pDocSh )
+ return;
+
+ const WhichRangesContainer & pRanges = rSet.GetRanges();
+ DBG_ASSERT(!pRanges.empty(), "Set without Range");
+ for ( auto const & pRange : pRanges )
+ {
+ for ( sal_uInt16 nWhich = pRange.first; nWhich <= pRange.second; ++nWhich )
+ {
+ switch(nWhich)
+ {
+ case SID_NEWDOCDIRECT :
+ {
+ if ( !m_pImpl->aFactoryName.isEmpty() )
+ {
+ rSet.Put( SfxStringItem( nWhich, "private:factory/"+m_pImpl->aFactoryName ) );
+ }
+ break;
+ }
+
+ case SID_NEWWINDOW:
+ rSet.DisableItem(nWhich);
+ break;
+
+ case SID_CLOSEWIN:
+ {
+ // disable CloseWin, if frame is not a task
+ Reference < XCloseable > xTask( GetFrame().GetFrameInterface(), UNO_QUERY );
+ if ( !xTask.is() )
+ rSet.DisableItem(nWhich);
+ break;
+ }
+
+ case SID_SHOWPOPUPS :
+ break;
+
+ case SID_OBJECT:
+ if ( GetViewShell() && GetViewShell()->GetVerbs().hasElements() && !GetObjectShell()->IsInPlaceActive() )
+ {
+ uno::Any aAny(GetViewShell()->GetVerbs());
+ rSet.Put( SfxUnoAnyItem( sal_uInt16( SID_OBJECT ), aAny ) );
+ }
+ else
+ rSet.DisableItem( SID_OBJECT );
+ break;
+
+ default:
+ OSL_FAIL( "invalid message-id" );
+ }
+ }
+ }
+}
+
+void SfxViewFrame::INetExecute_Impl( SfxRequest &rRequest )
+{
+ sal_uInt16 nSlotId = rRequest.GetSlot();
+ switch( nSlotId )
+ {
+ case SID_BROWSE_FORWARD:
+ case SID_BROWSE_BACKWARD:
+ OSL_FAIL( "SfxViewFrame::INetExecute_Impl: SID_BROWSE_FORWARD/BACKWARD are dead!" );
+ break;
+ case SID_CREATELINK:
+ {
+/*! (pb) we need new implementation to create a link
+*/
+ break;
+ }
+ case SID_FOCUSURLBOX:
+ {
+ SfxStateCache *pCache = GetBindings().GetAnyStateCache_Impl( SID_OPENURL );
+ if( pCache )
+ {
+ SfxControllerItem* pCtrl = pCache->GetItemLink();
+ while( pCtrl )
+ {
+ pCtrl->StateChangedAtToolBoxControl( SID_FOCUSURLBOX, SfxItemState::UNKNOWN, nullptr );
+ pCtrl = pCtrl->GetItemLink();
+ }
+ }
+ }
+ }
+
+ // Recording
+ rRequest.Done();
+}
+
+void SfxViewFrame::INetState_Impl( SfxItemSet &rItemSet )
+{
+ rItemSet.DisableItem( SID_BROWSE_FORWARD );
+ rItemSet.DisableItem( SID_BROWSE_BACKWARD );
+
+ // Add/SaveToBookmark at BASIC-IDE, QUERY-EDITOR etc. disable
+ SfxObjectShell *pDocSh = GetObjectShell();
+ bool bEmbedded = pDocSh && pDocSh->GetCreateMode() == SfxObjectCreateMode::EMBEDDED;
+ if ( !pDocSh || bEmbedded || !pDocSh->HasName() )
+ rItemSet.DisableItem( SID_CREATELINK );
+}
+
+void SfxViewFrame::Activate( bool /*bMDI*/ )
+{
+ DBG_ASSERT(GetViewShell(), "No Shell");
+//(mba): here maybe as in Beanframe NotifyEvent ?!
+}
+
+void SfxViewFrame::Deactivate( bool /*bMDI*/ )
+{
+ DBG_ASSERT(GetViewShell(), "No Shell");
+//(mba): here maybe as in Beanframe NotifyEvent ?!
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/view/viewimp.hxx b/sfx2/source/view/viewimp.hxx
new file mode 100644
index 0000000000..aa03945910
--- /dev/null
+++ b/sfx2/source/view/viewimp.hxx
@@ -0,0 +1,71 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_SFX2_SOURCE_VIEW_VIEWIMP_HXX
+#define INCLUDED_SFX2_SOURCE_VIEW_VIEWIMP_HXX
+
+#include <com/sun/star/ui/XContextMenuInterceptor.hpp>
+#include <memory>
+#include <sfx2/viewsh.hxx>
+#include <mutex>
+#include <comphelper/interfacecontainer4.hxx>
+#include <svtools/acceleratorexecute.hxx>
+#include <rtl/ref.hxx>
+#include <vcl/print.hxx>
+#include <chrono>
+#include <vector>
+
+class SfxBaseController;
+typedef std::vector<SfxShell*> SfxShellArr_Impl;
+class SfxClipboardChangeListener;
+
+struct SfxViewShell_Impl
+{
+ std::mutex aMutex;
+ ::comphelper::OInterfaceContainerHelper4<css::ui::XContextMenuInterceptor>
+ aInterceptorContainer;
+ SfxShellArr_Impl aArr;
+ Size aMargin;
+ OUString m_sDefaultPrinterName;
+ std::chrono::steady_clock::time_point m_nDefaultPrinterNameFetchTime;
+ bool m_bHasPrintOptions;
+ sal_uInt16 m_nFamily;
+ ::rtl::Reference<SfxBaseController> m_pController;
+ std::unique_ptr<::svt::AcceleratorExecute> m_xAccExec;
+ ::rtl::Reference<SfxClipboardChangeListener> xClipboardListener;
+ std::shared_ptr<vcl::PrinterController> m_xPrinterController;
+
+ mutable std::vector<SfxInPlaceClient*> maIPClients;
+
+ SfxLokCallbackInterface* m_pLibreOfficeKitViewCallback;
+ /// Set if we are in the middle of a tiled search.
+ bool m_bTiledSearching;
+ static sal_uInt32 m_nLastViewShellId;
+ const ViewShellId m_nViewShellId;
+ const ViewShellDocId m_nDocId;
+
+ explicit SfxViewShell_Impl(SfxViewShellFlags const nFlags, ViewShellDocId nDocId);
+ ~SfxViewShell_Impl();
+
+ std::vector<SfxInPlaceClient*>& GetIPClients_Impl();
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/view/viewprn.cxx b/sfx2/source/view/viewprn.cxx
new file mode 100644
index 0000000000..19fddbffdf
--- /dev/null
+++ b/sfx2/source/view/viewprn.cxx
@@ -0,0 +1,923 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <memory>
+
+#include <com/sun/star/document/XDocumentProperties.hpp>
+#include <com/sun/star/view/XRenderable.hpp>
+#include <com/sun/star/view/XSelectionSupplier.hpp>
+
+#include <comphelper/propertyvalue.hxx>
+#include <officecfg/Office/Common.hxx>
+#include <sal/log.hxx>
+#include <utility>
+#include <vcl/svapp.hxx>
+#include <vcl/weld.hxx>
+#include <svtools/prnsetup.hxx>
+#include <svl/flagitem.hxx>
+#include <svl/stritem.hxx>
+#include <svl/eitem.hxx>
+#include <unotools/useroptions.hxx>
+#include <tools/datetime.hxx>
+#include <sfx2/bindings.hxx>
+#include <sfx2/objface.hxx>
+#include <sfx2/viewsh.hxx>
+#include "viewimp.hxx"
+#include <sfx2/viewfrm.hxx>
+#include <sfx2/printer.hxx>
+#include <sfx2/sfxresid.hxx>
+#include <sfx2/request.hxx>
+#include <sfx2/objsh.hxx>
+#include <sfx2/event.hxx>
+#include <sfx2/docfile.hxx>
+#include <sfx2/docfilt.hxx>
+#include <sfx2/sfxsids.hrc>
+#include <sfx2/strings.hrc>
+#include <sfx2/sfxuno.hxx>
+#include <sfx2/tabdlg.hxx>
+
+#include <toolkit/awt/vclxdevice.hxx>
+
+#include "prnmon.hxx"
+
+using namespace com::sun::star;
+using namespace com::sun::star::uno;
+
+class SfxPrinterController : public vcl::PrinterController, public SfxListener
+{
+ Any maCompleteSelection;
+ Any maSelection;
+ Reference< view::XRenderable > mxRenderable;
+ mutable VclPtr<Printer> mpLastPrinter;
+ mutable Reference<awt::XDevice> mxDevice;
+ SfxViewShell* mpViewShell;
+ SfxObjectShell* mpObjectShell;
+ bool m_bOrigStatus;
+ bool m_bNeedsChange;
+ bool m_bApi;
+ bool m_bTempPrinter;
+ util::DateTime m_aLastPrinted;
+ OUString m_aLastPrintedBy;
+
+ Sequence< beans::PropertyValue > getMergedOptions() const;
+ const Any& getSelectionObject() const;
+
+public:
+ SfxPrinterController( const VclPtr<Printer>& i_rPrinter,
+ Any i_Complete,
+ Any i_Selection,
+ const Any& i_rViewProp,
+ const Reference< view::XRenderable >& i_xRender,
+ bool i_bApi, bool i_bDirect,
+ SfxViewShell* pView,
+ const uno::Sequence< beans::PropertyValue >& rProps
+ );
+
+ virtual void Notify( SfxBroadcaster&, const SfxHint& ) override;
+
+ virtual int getPageCount() const override;
+ virtual Sequence< beans::PropertyValue > getPageParameters( int i_nPage ) const override;
+ virtual void printPage( int i_nPage ) const override;
+ virtual void jobStarted() override;
+ virtual void jobFinished( css::view::PrintableState ) override;
+};
+
+SfxPrinterController::SfxPrinterController( const VclPtr<Printer>& i_rPrinter,
+ Any i_Complete,
+ Any i_Selection,
+ const Any& i_rViewProp,
+ const Reference< view::XRenderable >& i_xRender,
+ bool i_bApi, bool i_bDirect,
+ SfxViewShell* pView,
+ const uno::Sequence< beans::PropertyValue >& rProps
+ )
+ : PrinterController(i_rPrinter, pView ? pView->GetFrameWeld() : nullptr)
+ , maCompleteSelection(std::move( i_Complete ))
+ , maSelection(std::move( i_Selection ))
+ , mxRenderable( i_xRender )
+ , mpLastPrinter( nullptr )
+ , mpViewShell( pView )
+ , mpObjectShell(nullptr)
+ , m_bOrigStatus( false )
+ , m_bNeedsChange( false )
+ , m_bApi(i_bApi)
+ , m_bTempPrinter( i_rPrinter )
+{
+ if ( mpViewShell )
+ {
+ StartListening( *mpViewShell );
+ mpObjectShell = mpViewShell->GetObjectShell();
+ StartListening( *mpObjectShell );
+ }
+
+ // initialize extra ui options
+ if( mxRenderable.is() )
+ {
+ for (const auto& rProp : rProps)
+ setValue( rProp.Name, rProp.Value );
+
+ Sequence< beans::PropertyValue > aRenderOptions{
+ comphelper::makePropertyValue("ExtraPrintUIOptions", Any{}),
+ comphelper::makePropertyValue("View", i_rViewProp),
+ comphelper::makePropertyValue("IsPrinter", true)
+ };
+ try
+ {
+ const Sequence< beans::PropertyValue > aRenderParms( mxRenderable->getRenderer( 0 , getSelectionObject(), aRenderOptions ) );
+ for( const auto& rRenderParm : aRenderParms )
+ {
+ if ( rRenderParm.Name == "ExtraPrintUIOptions" )
+ {
+ Sequence< beans::PropertyValue > aUIProps;
+ rRenderParm.Value >>= aUIProps;
+ setUIOptions( aUIProps );
+ }
+ else if( rRenderParm.Name == "NUp" )
+ {
+ setValue( rRenderParm.Name, rRenderParm.Value );
+ }
+ }
+ }
+ catch( lang::IllegalArgumentException& )
+ {
+ // the first renderer should always be available for the UI options,
+ // but catch the exception to be safe
+ }
+ }
+
+ // set some job parameters
+ setValue( "IsApi", Any( i_bApi ) );
+ setValue( "IsDirect", Any( i_bDirect ) );
+ setValue( "IsPrinter", Any( true ) );
+ setValue( "View", i_rViewProp );
+}
+
+void SfxPrinterController::Notify( SfxBroadcaster& , const SfxHint& rHint )
+{
+ if ( rHint.GetId() == SfxHintId::Dying )
+ {
+ EndListening(*mpViewShell);
+ EndListening(*mpObjectShell);
+ dialogsParentClosing();
+ mpViewShell = nullptr;
+ mpObjectShell = nullptr;
+ }
+}
+
+const Any& SfxPrinterController::getSelectionObject() const
+{
+ const beans::PropertyValue* pVal = getValue( OUString( "PrintSelectionOnly" ) );
+ if( pVal )
+ {
+ bool bSel = false;
+ pVal->Value >>= bSel;
+ return bSel ? maSelection : maCompleteSelection;
+ }
+
+ sal_Int32 nChoice = 0;
+ pVal = getValue( OUString( "PrintContent" ) );
+ if( pVal )
+ pVal->Value >>= nChoice;
+
+ return (nChoice > 1) ? maSelection : maCompleteSelection;
+}
+
+Sequence< beans::PropertyValue > SfxPrinterController::getMergedOptions() const
+{
+ VclPtr<Printer> xPrinter( getPrinter() );
+ if( xPrinter.get() != mpLastPrinter )
+ {
+ mpLastPrinter = xPrinter.get();
+ rtl::Reference<VCLXDevice> pXDevice = new VCLXDevice();
+ pXDevice->SetOutputDevice( mpLastPrinter );
+ mxDevice.set( pXDevice );
+ }
+
+ Sequence< beans::PropertyValue > aRenderOptions{ comphelper::makePropertyValue(
+ "RenderDevice", mxDevice) };
+
+ aRenderOptions = getJobProperties( aRenderOptions );
+ return aRenderOptions;
+}
+
+int SfxPrinterController::getPageCount() const
+{
+ int nPages = 0;
+ VclPtr<Printer> xPrinter( getPrinter() );
+ if( mxRenderable.is() && xPrinter )
+ {
+ Sequence< beans::PropertyValue > aJobOptions( getMergedOptions() );
+ try
+ {
+ nPages = mxRenderable->getRendererCount( getSelectionObject(), aJobOptions );
+ }
+ catch (lang::DisposedException &)
+ {
+ SAL_WARN("sfx", "SfxPrinterController: document disposed while printing");
+ const_cast<SfxPrinterController*>(this)->setJobState(
+ view::PrintableState_JOB_ABORTED);
+ }
+ }
+ return nPages;
+}
+
+Sequence< beans::PropertyValue > SfxPrinterController::getPageParameters( int i_nPage ) const
+{
+ VclPtr<Printer> xPrinter( getPrinter() );
+ Sequence< beans::PropertyValue > aResult;
+
+ if (mxRenderable.is() && xPrinter)
+ {
+ Sequence< beans::PropertyValue > aJobOptions( getMergedOptions() );
+ try
+ {
+ aResult = mxRenderable->getRenderer( i_nPage, getSelectionObject(), aJobOptions );
+ }
+ catch( lang::IllegalArgumentException& )
+ {
+ }
+ catch (lang::DisposedException &)
+ {
+ SAL_WARN("sfx", "SfxPrinterController: document disposed while printing");
+ const_cast<SfxPrinterController*>(this)->setJobState(
+ view::PrintableState_JOB_ABORTED);
+ }
+ }
+ return aResult;
+}
+
+void SfxPrinterController::printPage( int i_nPage ) const
+{
+ VclPtr<Printer> xPrinter( getPrinter() );
+ if( !mxRenderable.is() || !xPrinter )
+ return;
+
+ Sequence< beans::PropertyValue > aJobOptions( getMergedOptions() );
+ try
+ {
+ mxRenderable->render( i_nPage, getSelectionObject(), aJobOptions );
+ }
+ catch( lang::IllegalArgumentException& )
+ {
+ // don't care enough about nonexistent page here
+ // to provoke a crash
+ }
+ catch (lang::DisposedException &)
+ {
+ SAL_WARN("sfx", "SfxPrinterController: document disposed while printing");
+ const_cast<SfxPrinterController*>(this)->setJobState(
+ view::PrintableState_JOB_ABORTED);
+ }
+}
+
+void SfxPrinterController::jobStarted()
+{
+ if ( !mpObjectShell )
+ return;
+
+ m_bOrigStatus = mpObjectShell->IsEnableSetModified();
+
+ // check configuration: shall update of printing information in DocInfo set the document to "modified"?
+ if (m_bOrigStatus && !officecfg::Office::Common::Print::PrintingModifiesDocument::get())
+ {
+ mpObjectShell->EnableSetModified( false );
+ m_bNeedsChange = true;
+ }
+
+ // refresh document info
+ uno::Reference<document::XDocumentProperties> xDocProps(mpObjectShell->getDocProperties());
+ m_aLastPrintedBy = xDocProps->getPrintedBy();
+ m_aLastPrinted = xDocProps->getPrintDate();
+
+ xDocProps->setPrintedBy( mpObjectShell->IsUseUserData()
+ ? SvtUserOptions().GetFullName()
+ : OUString() );
+ ::DateTime now( ::DateTime::SYSTEM );
+
+ xDocProps->setPrintDate( now.GetUNODateTime() );
+
+ uno::Sequence < beans::PropertyValue > aOpts;
+ aOpts = getJobProperties( aOpts );
+
+ uno::Reference< frame::XController2 > xController;
+ if ( mpViewShell )
+ xController.set( mpViewShell->GetController(), uno::UNO_QUERY );
+
+ mpObjectShell->Broadcast( SfxPrintingHint(
+ view::PrintableState_JOB_STARTED, aOpts, mpObjectShell, xController ) );
+}
+
+void SfxPrinterController::jobFinished( css::view::PrintableState nState )
+{
+ if ( !mpObjectShell )
+ return;
+
+ bool bCopyJobSetup = false;
+ mpObjectShell->Broadcast( SfxPrintingHint( nState ) );
+ switch ( nState )
+ {
+ case view::PrintableState_JOB_SPOOLING_FAILED :
+ case view::PrintableState_JOB_FAILED :
+ {
+ // "real" problem (not simply printing cancelled by user)
+ OUString aMsg( SfxResId(STR_NOSTARTPRINTER) );
+ if ( !m_bApi )
+ {
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(mpViewShell->GetFrameWeld(),
+ VclMessageType::Warning, VclButtonsType::Ok,
+ aMsg));
+ xBox->run();
+ }
+ [[fallthrough]];
+ }
+ case view::PrintableState_JOB_ABORTED :
+ {
+ // printing not successful, reset DocInfo
+ uno::Reference<document::XDocumentProperties> xDocProps(mpObjectShell->getDocProperties());
+ xDocProps->setPrintedBy(m_aLastPrintedBy);
+ xDocProps->setPrintDate(m_aLastPrinted);
+ break;
+ }
+
+ case view::PrintableState_JOB_SPOOLED :
+ case view::PrintableState_JOB_COMPLETED :
+ {
+ SfxBindings& rBind = mpViewShell->GetViewFrame().GetBindings();
+ rBind.Invalidate( SID_PRINTDOC );
+ rBind.Invalidate( SID_PRINTDOCDIRECT );
+ rBind.Invalidate( SID_SETUPPRINTER );
+ bCopyJobSetup = ! m_bTempPrinter;
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ if( bCopyJobSetup && mpViewShell )
+ {
+ // #i114306#
+ // Note: this possibly creates a printer that gets immediately replaced
+ // by a new one. The reason for this is that otherwise we would not get
+ // the printer's SfxItemSet here to copy. Awkward, but at the moment there is no
+ // other way here to get the item set.
+ SfxPrinter* pDocPrt = mpViewShell->GetPrinter(true);
+ if( pDocPrt )
+ {
+ if( pDocPrt->GetName() == getPrinter()->GetName() )
+ pDocPrt->SetJobSetup( getPrinter()->GetJobSetup() );
+ else
+ {
+ VclPtr<SfxPrinter> pNewPrt = VclPtr<SfxPrinter>::Create( pDocPrt->GetOptions().Clone(), getPrinter()->GetName() );
+ pNewPrt->SetJobSetup( getPrinter()->GetJobSetup() );
+ mpViewShell->SetPrinter( pNewPrt, SfxPrinterChangeFlags::PRINTER | SfxPrinterChangeFlags::JOBSETUP );
+ }
+ }
+ }
+
+ if ( m_bNeedsChange )
+ mpObjectShell->EnableSetModified( m_bOrigStatus );
+
+ if ( mpViewShell )
+ {
+ mpViewShell->pImpl->m_xPrinterController.reset();
+ }
+}
+
+namespace {
+
+/**
+ An instance of this class is created for the life span of the
+ printer dialogue, to create in its click handler for the additions by the
+ virtual method of the derived SfxViewShell generated print options dialogue
+ and to cache the options set there as SfxItemSet.
+*/
+class SfxDialogExecutor_Impl
+{
+private:
+ SfxViewShell* _pViewSh;
+ PrinterSetupDialog& _rSetupParent;
+ std::unique_ptr<SfxItemSet> _pOptions;
+ bool _bHelpDisabled;
+
+ DECL_LINK( Execute, weld::Button&, void );
+
+public:
+ SfxDialogExecutor_Impl( SfxViewShell* pViewSh, PrinterSetupDialog& rParent );
+
+ Link<weld::Button&, void> GetLink() const { return LINK(const_cast<SfxDialogExecutor_Impl*>(this), SfxDialogExecutor_Impl, Execute); }
+ const SfxItemSet* GetOptions() const { return _pOptions.get(); }
+ void DisableHelp() { _bHelpDisabled = true; }
+};
+
+}
+
+SfxDialogExecutor_Impl::SfxDialogExecutor_Impl( SfxViewShell* pViewSh, PrinterSetupDialog& rParent ) :
+
+ _pViewSh ( pViewSh ),
+ _rSetupParent ( rParent ),
+ _bHelpDisabled ( false )
+
+{
+}
+
+IMPL_LINK_NOARG(SfxDialogExecutor_Impl, Execute, weld::Button&, void)
+{
+ // Options noted locally
+ if ( !_pOptions )
+ {
+ _pOptions = static_cast<SfxPrinter*>( _rSetupParent.GetPrinter() )->GetOptions().Clone();
+ }
+
+ assert(_pOptions);
+ if (!_pOptions)
+ return;
+
+ // Create Dialog
+ SfxPrintOptionsDialog aDlg(_rSetupParent.GetFrameWeld(), _pViewSh, _pOptions.get() );
+ if (_bHelpDisabled)
+ aDlg.DisableHelp();
+ if (aDlg.run() == RET_OK)
+ {
+ _pOptions = aDlg.GetOptions().Clone();
+ }
+}
+
+/**
+ Internal method for setting the differences between 'pNewPrinter' to the
+ current printer. pNewPrinter is either taken over or deleted.
+*/
+void SfxViewShell::SetPrinter_Impl( VclPtr<SfxPrinter>& pNewPrinter )
+{
+ // get current Printer
+ SfxPrinter *pDocPrinter = GetPrinter();
+
+ // Evaluate Printer Options
+ const SfxFlagItem *pFlagItem = pDocPrinter->GetOptions().GetItemIfSet( SID_PRINTER_CHANGESTODOC, false );
+ bool bOriToDoc = pFlagItem && (static_cast<SfxPrinterChangeFlags>(pFlagItem->GetValue()) & SfxPrinterChangeFlags::CHG_ORIENTATION);
+ bool bSizeToDoc = pFlagItem && (static_cast<SfxPrinterChangeFlags>(pFlagItem->GetValue()) & SfxPrinterChangeFlags::CHG_SIZE);
+
+ // Determine the previous format and size
+ Orientation eOldOri = pDocPrinter->GetOrientation();
+ Size aOldPgSz = pDocPrinter->GetPaperSizePixel();
+
+ // Determine the new format and size
+ Orientation eNewOri = pNewPrinter->GetOrientation();
+ Size aNewPgSz = pNewPrinter->GetPaperSizePixel();
+
+ // Determine the changes in page format
+ bool bOriChg = (eOldOri != eNewOri) && bOriToDoc;
+ bool bPgSzChg = ( aOldPgSz.Height() !=
+ ( bOriChg ? aNewPgSz.Width() : aNewPgSz.Height() ) ||
+ aOldPgSz.Width() !=
+ ( bOriChg ? aNewPgSz.Height() : aNewPgSz.Width() ) ) &&
+ bSizeToDoc;
+
+ // Message and Flags for page format changes
+ OUString aMsg;
+ SfxPrinterChangeFlags nNewOpt = SfxPrinterChangeFlags::NONE;
+ if( bOriChg && bPgSzChg )
+ {
+ aMsg = SfxResId(STR_PRINT_NEWORISIZE);
+ nNewOpt = SfxPrinterChangeFlags::CHG_ORIENTATION | SfxPrinterChangeFlags::CHG_SIZE;
+ }
+ else if (bOriChg )
+ {
+ aMsg = SfxResId(STR_PRINT_NEWORI);
+ nNewOpt = SfxPrinterChangeFlags::CHG_ORIENTATION;
+ }
+ else if (bPgSzChg)
+ {
+ aMsg = SfxResId(STR_PRINT_NEWSIZE);
+ nNewOpt = SfxPrinterChangeFlags::CHG_SIZE;
+ }
+
+ // Summarize in this variable what has been changed.
+ SfxPrinterChangeFlags nChangedFlags = SfxPrinterChangeFlags::NONE;
+
+ // Ask if possible, if page format should be taken over from printer.
+ if (bOriChg || bPgSzChg)
+ {
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(nullptr,
+ VclMessageType::Question, VclButtonsType::YesNo,
+ aMsg));
+ if (RET_YES == xBox->run())
+ {
+ // Flags with changes for <SetPrinter(SfxPrinter*)> are maintained
+ nChangedFlags |= nNewOpt;
+ }
+ }
+
+ // Was the printer selection changed from Default to Specific
+ // or the other way around?
+ if ( (pNewPrinter->GetName() != pDocPrinter->GetName())
+ || (pDocPrinter->IsDefPrinter() != pNewPrinter->IsDefPrinter()) )
+ {
+ nChangedFlags |= SfxPrinterChangeFlags::PRINTER|SfxPrinterChangeFlags::JOBSETUP;
+ if ( ! (pNewPrinter->GetOptions() == pDocPrinter->GetOptions()) )
+ {
+ nChangedFlags |= SfxPrinterChangeFlags::OPTIONS;
+ }
+
+ pDocPrinter = pNewPrinter;
+ }
+ else
+ {
+ // Compare extra options
+ if ( ! (pNewPrinter->GetOptions() == pDocPrinter->GetOptions()) )
+ {
+ // Option have changed
+ pDocPrinter->SetOptions( pNewPrinter->GetOptions() );
+ nChangedFlags |= SfxPrinterChangeFlags::OPTIONS;
+ }
+
+ // Compare JobSetups
+ JobSetup aNewJobSetup = pNewPrinter->GetJobSetup();
+ JobSetup aOldJobSetup = pDocPrinter->GetJobSetup();
+ if ( aNewJobSetup != aOldJobSetup )
+ {
+ nChangedFlags |= SfxPrinterChangeFlags::JOBSETUP;
+ }
+
+ // Keep old changed Printer.
+ pDocPrinter->SetPrinterProps( pNewPrinter );
+ pNewPrinter.disposeAndClear();
+ }
+
+ if ( SfxPrinterChangeFlags::NONE != nChangedFlags )
+ // SetPrinter will delete the old printer if it changes
+ SetPrinter( pDocPrinter, nChangedFlags );
+}
+
+void SfxViewShell::StartPrint( const uno::Sequence < beans::PropertyValue >& rProps, bool bIsAPI, bool bIsDirect )
+{
+ assert( !pImpl->m_xPrinterController );
+
+ // get the current selection; our controller should know it
+ Reference< frame::XController > xController( GetController() );
+ Reference< view::XSelectionSupplier > xSupplier( xController, UNO_QUERY );
+
+ Any aSelection;
+ if( xSupplier.is() )
+ aSelection = xSupplier->getSelection();
+ else
+ aSelection <<= GetObjectShell()->GetModel();
+ Any aComplete( Any( GetObjectShell()->GetModel() ) );
+ Any aViewProp( xController );
+ VclPtr<Printer> aPrt;
+
+ const beans::PropertyValue* pVal = std::find_if(rProps.begin(), rProps.end(),
+ [](const beans::PropertyValue& rVal) { return rVal.Name == "PrinterName"; });
+ if (pVal != rProps.end())
+ {
+ OUString aPrinterName;
+ pVal->Value >>= aPrinterName;
+ aPrt.reset( VclPtr<Printer>::Create( aPrinterName ) );
+ }
+
+ std::shared_ptr<vcl::PrinterController> xNewController(std::make_shared<SfxPrinterController>(
+ aPrt,
+ aComplete,
+ aSelection,
+ aViewProp,
+ GetRenderable(),
+ bIsAPI,
+ bIsDirect,
+ this,
+ rProps
+ ));
+ pImpl->m_xPrinterController = xNewController;
+
+ // When no JobName was specified via com::sun::star::view::PrintOptions::JobName ,
+ // use the document title as default job name
+ css::beans::PropertyValue* pJobNameVal = xNewController->getValue("JobName");
+ if (!pJobNameVal)
+ {
+ if (SfxObjectShell* pDoc = GetObjectShell())
+ {
+ xNewController->setValue("JobName", Any(pDoc->GetTitle(1)));
+ xNewController->setPrinterModified(mbPrinterSettingsModified);
+ }
+ }
+}
+
+void SfxViewShell::ExecPrint( const uno::Sequence < beans::PropertyValue >& rProps, bool bIsAPI, bool bIsDirect )
+{
+ StartPrint( rProps, bIsAPI, bIsDirect );
+ // FIXME: job setup
+ SfxPrinter* pDocPrt = GetPrinter();
+ JobSetup aJobSetup = pDocPrt ? pDocPrt->GetJobSetup() : JobSetup();
+ Printer::PrintJob( GetPrinterController(), aJobSetup );
+}
+
+const std::shared_ptr< vcl::PrinterController >& SfxViewShell::GetPrinterController() const
+{
+ return pImpl->m_xPrinterController;
+}
+
+Printer* SfxViewShell::GetActivePrinter() const
+{
+ return pImpl->m_xPrinterController
+ ? pImpl->m_xPrinterController->getPrinter().get() : nullptr;
+}
+
+void SfxViewShell::ExecPrint_Impl( SfxRequest &rReq )
+{
+ sal_uInt16 nDialogRet = RET_CANCEL;
+ VclPtr<SfxPrinter> pPrinter;
+ bool bSilent = false;
+
+ // does the function have been called by the user interface or by an API call
+ bool bIsAPI = rReq.GetArgs() && rReq.GetArgs()->Count();
+ if ( bIsAPI )
+ {
+ // the function have been called by the API
+
+ // Should it be visible on the user interface,
+ // should it launch popup dialogue ?
+ const SfxBoolItem* pSilentItem = rReq.GetArg<SfxBoolItem>(SID_SILENT);
+ bSilent = pSilentItem && pSilentItem->GetValue();
+ }
+
+ // no help button in dialogs if called from the help window
+ // (pressing help button would exchange the current page inside the help
+ // document that is going to be printed!)
+ SfxMedium* pMedium = GetViewFrame().GetObjectShell()->GetMedium();
+ std::shared_ptr<const SfxFilter> pFilter = pMedium ? pMedium->GetFilter() : nullptr;
+ bool bPrintOnHelp = ( pFilter && pFilter->GetFilterName() == "writer_web_HTML_help" );
+
+ const sal_uInt16 nId = rReq.GetSlot();
+ switch( nId )
+ {
+ case SID_PRINTDOC: // display the printer selection and properties dialogue : File > Print...
+ case SID_PRINTDOCDIRECT: // Print the document directly, without displaying the dialogue
+ {
+ SfxObjectShell* pDoc = GetObjectShell();
+
+ // derived class may decide to abort this
+ if( pDoc == nullptr || !pDoc->QuerySlotExecutable( nId ) )
+ {
+ rReq.SetReturnValue( SfxBoolItem( 0, false ) );
+ return;
+ }
+
+ pDoc->QueryHiddenInformation(HiddenWarningFact::WhenPrinting);
+
+ // should we print only the selection or the whole document
+ const SfxBoolItem* pSelectItem = rReq.GetArg<SfxBoolItem>(SID_SELECTION);
+ bool bSelection = ( pSelectItem != nullptr && pSelectItem->GetValue() );
+ // detect non api call from writer ( that adds SID_SELECTION ) and reset bIsAPI
+ if ( pSelectItem && rReq.GetArgs()->Count() == 1 )
+ bIsAPI = false;
+
+ uno::Sequence < beans::PropertyValue > aProps;
+ if ( bIsAPI )
+ {
+ // supported properties:
+ // String PrinterName
+ // String FileName
+ // Int16 From
+ // Int16 To
+ // In16 Copies
+ // String RangeText
+ // bool Selection
+ // bool Asynchron
+ // bool Collate
+ // bool Silent
+
+ // the TransformItems function overwrite aProps
+ TransformItems( nId, *rReq.GetArgs(), aProps, GetInterface()->GetSlot(nId) );
+
+ for ( auto& rProp : asNonConstRange(aProps) )
+ {
+ if ( rProp.Name == "Copies" )
+ {
+ rProp.Name = "CopyCount";
+ }
+ else if ( rProp.Name == "RangeText" )
+ {
+ rProp.Name = "Pages";
+ }
+ else if ( rProp.Name == "Asynchron" )
+ {
+ rProp.Name = "Wait";
+ bool bAsynchron = false;
+ rProp.Value >>= bAsynchron;
+ rProp.Value <<= !bAsynchron;
+ }
+ else if ( rProp.Name == "Silent" )
+ {
+ rProp.Name = "MonitorVisible";
+ bool bPrintSilent = false;
+ rProp.Value >>= bPrintSilent;
+ rProp.Value <<= !bPrintSilent;
+ }
+ }
+ }
+
+ // we will add the "PrintSelectionOnly" or "HideHelpButton" properties
+ // we have to increase the capacity of aProps
+ sal_Int32 nLen = aProps.getLength();
+ aProps.realloc( nLen + 1 );
+ auto pProps = aProps.getArray();
+
+ // HACK: writer sets the SID_SELECTION item when printing directly and expects
+ // to get only the selection document in that case (see getSelectionObject)
+ // however it also reacts to the PrintContent property. We need this distinction here, too,
+ // else one of the combinations print / print direct and selection / all will not work.
+ // it would be better if writer handled this internally
+ if( nId == SID_PRINTDOCDIRECT )
+ {
+ pProps[nLen].Name = "PrintSelectionOnly";
+ pProps[nLen].Value <<= bSelection;
+ }
+ else // if nId == SID_PRINTDOC ; nothing to do with the previous HACK
+ {
+ // should the printer selection and properties dialogue display an help button
+ pProps[nLen].Name = "HideHelpButton";
+ pProps[nLen].Value <<= bPrintOnHelp;
+ }
+
+ ExecPrint( aProps, bIsAPI, (nId == SID_PRINTDOCDIRECT) );
+
+ // FIXME: Recording
+ rReq.Done();
+ break;
+ }
+
+ case SID_PRINTER_NAME: // for recorded macros
+ {
+ // get printer and printer settings from the document
+ SfxPrinter* pDocPrinter = GetPrinter(true);
+ const SfxStringItem* pPrinterItem = rReq.GetArg<SfxStringItem>(SID_PRINTER_NAME);
+ if (!pPrinterItem)
+ {
+ rReq.Ignore();
+ break;
+ }
+ // use PrinterName parameter to create a printer
+ pPrinter = VclPtr<SfxPrinter>::Create(pDocPrinter->GetOptions().Clone(),
+ pPrinterItem->GetValue());
+
+ if (!pPrinter->IsKnown())
+ {
+ pPrinter.disposeAndClear();
+ rReq.Ignore();
+ break;
+ }
+ SetPrinter(pPrinter, SfxPrinterChangeFlags::PRINTER);
+ rReq.Done();
+ break;
+ }
+ case SID_SETUPPRINTER : // display the printer settings dialog : File > Printer Settings...
+ {
+ // get printer and printer settings from the document
+ SfxPrinter *pDocPrinter = GetPrinter(true);
+
+ // look for printer in parameters
+ const SfxStringItem* pPrinterItem = rReq.GetArg<SfxStringItem>(SID_PRINTER_NAME);
+ if ( pPrinterItem )
+ {
+ // use PrinterName parameter to create a printer
+ pPrinter = VclPtr<SfxPrinter>::Create( pDocPrinter->GetOptions().Clone(), pPrinterItem->GetValue() );
+
+ // if printer is unknown, it can't be used - now printer from document will be used
+ if ( !pPrinter->IsKnown() )
+ pPrinter.disposeAndClear();
+ }
+
+ // no PrinterName parameter in ItemSet or the PrinterName points to an unknown printer
+ if ( !pPrinter )
+ // use default printer from document
+ pPrinter = pDocPrinter;
+
+ if( !pPrinter || !pPrinter->IsValid() )
+ {
+ // no valid printer either in ItemSet or at the document
+ if ( !bSilent )
+ {
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(nullptr,
+ VclMessageType::Warning, VclButtonsType::Ok,
+ SfxResId(STR_NODEFPRINTER)));
+ xBox->run();
+ }
+
+ rReq.SetReturnValue(SfxBoolItem(0,false));
+
+ break;
+ }
+
+ // FIXME: printer isn't used for printing anymore!
+ if( pPrinter->IsPrinting() )
+ {
+ // if printer is busy, abort configuration
+ if ( !bSilent )
+ {
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(nullptr,
+ VclMessageType::Info, VclButtonsType::Ok,
+ SfxResId(STR_ERROR_PRINTER_BUSY)));
+ xBox->run();
+ }
+ rReq.SetReturnValue(SfxBoolItem(0,false));
+
+ return;
+ }
+
+ // Open Printer Setup dialog (needs a temporary printer)
+ VclPtr<SfxPrinter> pDlgPrinter = pPrinter->Clone();
+ PrinterSetupDialog aPrintSetupDlg(GetFrameWeld());
+ std::unique_ptr<SfxDialogExecutor_Impl> pExecutor;
+
+ if (pImpl->m_bHasPrintOptions && HasPrintOptionsPage())
+ {
+ // additional controls for dialog
+ pExecutor.reset(new SfxDialogExecutor_Impl(this, aPrintSetupDlg));
+ if (bPrintOnHelp)
+ pExecutor->DisableHelp();
+ aPrintSetupDlg.SetOptionsHdl(pExecutor->GetLink());
+ }
+
+ aPrintSetupDlg.SetPrinter(pDlgPrinter);
+ nDialogRet = aPrintSetupDlg.run();
+
+ if (pExecutor && pExecutor->GetOptions())
+ {
+ if (nDialogRet == RET_OK)
+ // remark: have to be recorded if possible!
+ pDlgPrinter->SetOptions(*pExecutor->GetOptions());
+ else
+ {
+ pPrinter->SetOptions(*pExecutor->GetOptions());
+ SetPrinter(pPrinter, SfxPrinterChangeFlags::OPTIONS);
+ }
+ }
+
+ // no recording of PrinterSetup except printer name (is printer dependent)
+ rReq.Ignore();
+
+ if (nDialogRet == RET_OK)
+ {
+ if (pPrinter->GetName() != pDlgPrinter->GetName())
+ {
+ // user has changed the printer -> macro recording
+ SfxRequest aReq(GetViewFrame(), SID_PRINTER_NAME);
+ aReq.AppendItem(SfxStringItem(SID_PRINTER_NAME, pDlgPrinter->GetName()));
+ aReq.Done();
+ }
+
+ // take the changes made in the dialog
+ SetPrinter_Impl(pDlgPrinter);
+
+ // forget new printer, it was taken over (as pPrinter) or deleted
+ pDlgPrinter = nullptr;
+ mbPrinterSettingsModified = true;
+ }
+ else
+ {
+ // PrinterDialog is used to transfer information on printing,
+ // so it will only be deleted here if dialog was cancelled
+ pDlgPrinter.disposeAndClear();
+ rReq.Ignore();
+ }
+ break;
+ }
+ }
+}
+
+SfxPrinter* SfxViewShell::GetPrinter( bool /*bCreate*/ )
+{
+ return nullptr;
+}
+
+sal_uInt16 SfxViewShell::SetPrinter( SfxPrinter* /*pNewPrinter*/, SfxPrinterChangeFlags /*nDiffFlags*/ )
+{
+ return 0;
+}
+
+std::unique_ptr<SfxTabPage> SfxViewShell::CreatePrintOptionsPage(weld::Container*, weld::DialogController*, const SfxItemSet&)
+{
+ return nullptr;
+}
+
+bool SfxViewShell::HasPrintOptionsPage() const
+{
+ return false;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/view/viewsh.cxx b/sfx2/source/view/viewsh.cxx
new file mode 100644
index 0000000000..93c18a16e2
--- /dev/null
+++ b/sfx2/source/view/viewsh.cxx
@@ -0,0 +1,3912 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <config_features.h>
+
+#include <boost/property_tree/json_parser.hpp>
+
+#include <sal/log.hxx>
+#include <svl/stritem.hxx>
+#include <svl/eitem.hxx>
+#include <svl/whiter.hxx>
+#include <utility>
+#include <vcl/svapp.hxx>
+#include <vcl/toolbox.hxx>
+#include <vcl/weld.hxx>
+#include <svl/intitem.hxx>
+#include <svtools/langhelp.hxx>
+#include <com/sun/star/awt/XPopupMenu.hpp>
+#include <com/sun/star/frame/XLayoutManager.hpp>
+#include <com/sun/star/frame/ModuleManager.hpp>
+#include <com/sun/star/io/IOException.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/embed/EmbedStates.hpp>
+#include <com/sun/star/embed/EmbedMisc.hpp>
+#include <com/sun/star/embed/XEmbeddedObject.hpp>
+#include <com/sun/star/container/XContainerQuery.hpp>
+#include <com/sun/star/frame/XStorable.hpp>
+#include <com/sun/star/frame/XModel.hpp>
+#include <com/sun/star/datatransfer/clipboard/XClipboard.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <com/sun/star/datatransfer/clipboard/XClipboardListener.hpp>
+#include <com/sun/star/datatransfer/clipboard/XClipboardNotifier.hpp>
+#include <com/sun/star/view/XRenderable.hpp>
+#include <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <com/sun/star/accessibility/XAccessibleContext.hpp>
+#include <com/sun/star/accessibility/XAccessibleEventBroadcaster.hpp>
+#include <com/sun/star/accessibility/XAccessibleSelection.hpp>
+#include <com/sun/star/accessibility/AccessibleEventId.hpp>
+#include <com/sun/star/accessibility/AccessibleStateType.hpp>
+#include <com/sun/star/accessibility/AccessibleRole.hpp>
+#include <com/sun/star/accessibility/XAccessibleText.hpp>
+#include <com/sun/star/accessibility/XAccessibleTable.hpp>
+#include <cppuhelper/implbase.hxx>
+#include <com/sun/star/ui/XAcceleratorConfiguration.hpp>
+
+#include <cppuhelper/weakref.hxx>
+
+#include <com/sun/star/accessibility/XAccessibleTextAttributes.hpp>
+#include <com/sun/star/accessibility/AccessibleTextType.hpp>
+#include <com/sun/star/awt/FontSlant.hpp>
+
+#include <comphelper/diagnose_ex.hxx>
+#include <editeng/unoprnms.hxx>
+#include <tools/urlobj.hxx>
+#include <unotools/tempfile.hxx>
+#include <svtools/soerr.hxx>
+#include <tools/svborder.hxx>
+
+#include <framework/actiontriggerhelper.hxx>
+#include <comphelper/lok.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/propertyvalue.hxx>
+#include <comphelper/sequenceashashmap.hxx>
+#include <toolkit/helper/vclunohelper.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/commandinfoprovider.hxx>
+#include <LibreOfficeKit/LibreOfficeKitEnums.h>
+
+#include <officecfg/Setup.hxx>
+#include <sfx2/app.hxx>
+#include <sfx2/flatpak.hxx>
+#include <sfx2/viewsh.hxx>
+#include "viewimp.hxx"
+#include <sfx2/sfxresid.hxx>
+#include <sfx2/request.hxx>
+#include <sfx2/printer.hxx>
+#include <sfx2/docfile.hxx>
+#include <sfx2/dispatch.hxx>
+#include <sfx2/strings.hrc>
+#include <sfx2/sfxbasecontroller.hxx>
+#include <sfx2/mailmodelapi.hxx>
+#include <bluthsndapi.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <sfx2/event.hxx>
+#include <sfx2/ipclient.hxx>
+#include <sfx2/sfxsids.hrc>
+#include <sfx2/objface.hxx>
+#include <sfx2/lokhelper.hxx>
+#include <sfx2/lokcallback.hxx>
+#include <openuriexternally.hxx>
+#include <iostream>
+#include <vector>
+#include <list>
+#include <libxml/xmlwriter.h>
+#include <toolkit/awt/vclxmenu.hxx>
+#include <unordered_map>
+#include <unordered_set>
+
+#define ShellClass_SfxViewShell
+#include <sfxslots.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::frame;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::util;
+using namespace ::cppu;
+
+class SfxClipboardChangeListener : public ::cppu::WeakImplHelper<
+ datatransfer::clipboard::XClipboardListener >
+{
+public:
+ SfxClipboardChangeListener( SfxViewShell* pView, uno::Reference< datatransfer::clipboard::XClipboardNotifier > xClpbrdNtfr );
+
+ // XEventListener
+ virtual void SAL_CALL disposing( const lang::EventObject& rEventObject ) override;
+
+ // XClipboardListener
+ virtual void SAL_CALL changedContents( const datatransfer::clipboard::ClipboardEvent& rEventObject ) override;
+
+ void DisconnectViewShell() { m_pViewShell = nullptr; }
+ void ChangedContents();
+
+ enum AsyncExecuteCmd
+ {
+ ASYNCEXECUTE_CMD_DISPOSING,
+ ASYNCEXECUTE_CMD_CHANGEDCONTENTS
+ };
+
+ struct AsyncExecuteInfo
+ {
+ AsyncExecuteInfo( AsyncExecuteCmd eCmd, SfxClipboardChangeListener* pListener ) :
+ m_eCmd( eCmd ), m_xListener( pListener ) {}
+
+ AsyncExecuteCmd m_eCmd;
+ rtl::Reference<SfxClipboardChangeListener> m_xListener;
+ };
+
+private:
+ SfxViewShell* m_pViewShell;
+ uno::Reference< datatransfer::clipboard::XClipboardNotifier > m_xClpbrdNtfr;
+ uno::Reference< lang::XComponent > m_xCtrl;
+
+ DECL_STATIC_LINK( SfxClipboardChangeListener, AsyncExecuteHdl_Impl, void*, void );
+};
+
+SfxClipboardChangeListener::SfxClipboardChangeListener( SfxViewShell* pView, uno::Reference< datatransfer::clipboard::XClipboardNotifier > xClpbrdNtfr )
+ : m_pViewShell( nullptr ), m_xClpbrdNtfr(std::move( xClpbrdNtfr )), m_xCtrl(pView->GetController())
+{
+ if ( m_xCtrl.is() )
+ {
+ m_xCtrl->addEventListener( uno::Reference < lang::XEventListener > ( static_cast < lang::XEventListener* >( this ) ) );
+ m_pViewShell = pView;
+ }
+ if ( m_xClpbrdNtfr.is() )
+ {
+ m_xClpbrdNtfr->addClipboardListener( uno::Reference< datatransfer::clipboard::XClipboardListener >(
+ static_cast< datatransfer::clipboard::XClipboardListener* >( this )));
+ }
+}
+
+void SfxClipboardChangeListener::ChangedContents()
+{
+ const SolarMutexGuard aGuard;
+ if (!m_pViewShell)
+ return;
+
+ SfxBindings& rBind = m_pViewShell->GetViewFrame().GetBindings();
+ rBind.Invalidate(SID_PASTE);
+ rBind.Invalidate(SID_PASTE_SPECIAL);
+ rBind.Invalidate(SID_CLIPBOARD_FORMAT_ITEMS);
+
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ // In the future we might send the payload as well.
+ SfxLokHelper::notifyAllViews(LOK_CALLBACK_CLIPBOARD_CHANGED, ""_ostr);
+ }
+}
+
+IMPL_STATIC_LINK( SfxClipboardChangeListener, AsyncExecuteHdl_Impl, void*, p, void )
+{
+ AsyncExecuteInfo* pAsyncExecuteInfo = static_cast<AsyncExecuteInfo*>(p);
+ if ( pAsyncExecuteInfo )
+ {
+ if ( pAsyncExecuteInfo->m_xListener.is() )
+ {
+ if ( pAsyncExecuteInfo->m_eCmd == ASYNCEXECUTE_CMD_DISPOSING )
+ pAsyncExecuteInfo->m_xListener->DisconnectViewShell();
+ else if ( pAsyncExecuteInfo->m_eCmd == ASYNCEXECUTE_CMD_CHANGEDCONTENTS )
+ pAsyncExecuteInfo->m_xListener->ChangedContents();
+ }
+ }
+ delete pAsyncExecuteInfo;
+}
+
+void SAL_CALL SfxClipboardChangeListener::disposing( const lang::EventObject& /*rEventObject*/ )
+{
+ // Either clipboard or ViewShell is going to be destroyed -> no interest in listening anymore
+ uno::Reference< lang::XComponent > xCtrl( m_xCtrl );
+ uno::Reference< datatransfer::clipboard::XClipboardNotifier > xNotify( m_xClpbrdNtfr );
+
+ uno::Reference< datatransfer::clipboard::XClipboardListener > xThis( static_cast< datatransfer::clipboard::XClipboardListener* >( this ));
+ if ( xCtrl.is() )
+ xCtrl->removeEventListener( uno::Reference < lang::XEventListener > ( static_cast < lang::XEventListener* >( this )));
+ if ( xNotify.is() )
+ xNotify->removeClipboardListener( xThis );
+
+ // Make asynchronous call to avoid locking SolarMutex which is the
+ // root for many deadlocks, especially in conjunction with the "Windows"
+ // based single thread apartment clipboard code!
+ AsyncExecuteInfo* pInfo = new AsyncExecuteInfo( ASYNCEXECUTE_CMD_DISPOSING, this );
+ if (!Application::PostUserEvent( LINK( nullptr, SfxClipboardChangeListener, AsyncExecuteHdl_Impl ), pInfo ))
+ delete pInfo;
+}
+
+void SAL_CALL SfxClipboardChangeListener::changedContents( const datatransfer::clipboard::ClipboardEvent& )
+{
+ // Make asynchronous call to avoid locking SolarMutex which is the
+ // root for many deadlocks, especially in conjunction with the "Windows"
+ // based single thread apartment clipboard code!
+ AsyncExecuteInfo* pInfo = new AsyncExecuteInfo( ASYNCEXECUTE_CMD_CHANGEDCONTENTS, this );
+ if (!Application::PostUserEvent( LINK( nullptr, SfxClipboardChangeListener, AsyncExecuteHdl_Impl ), pInfo ))
+ delete pInfo;
+}
+
+namespace
+{
+struct TableSizeType
+{
+ sal_Int32 nRowCount;
+ sal_Int32 nColCount;
+};
+}
+
+typedef std::list<uno::Reference<accessibility::XAccessibleTable>> XAccessibleTableList;
+
+namespace
+{
+constexpr
+bool isText(sal_Int16 nRole)
+{
+ return nRole == accessibility::AccessibleRole::DOCUMENT_TEXT;
+}
+
+constexpr
+bool isSpreadsheet(sal_Int16 nRole)
+{
+ return nRole == accessibility::AccessibleRole::DOCUMENT_SPREADSHEET;
+}
+
+constexpr
+bool isPresentation(sal_Int16 nRole)
+{
+ return nRole == accessibility::AccessibleRole::DOCUMENT_PRESENTATION;
+}
+
+constexpr
+bool isDocument(sal_Int16 nRole)
+{
+ return isText(nRole) || isSpreadsheet(nRole) || isPresentation(nRole);
+}
+
+bool hasState(const accessibility::AccessibleEventObject& aEvent, ::sal_Int64 nState)
+{
+ bool res = false;
+ uno::Reference< accessibility::XAccessibleContext > xContext(aEvent.Source, uno::UNO_QUERY);
+ if (xContext.is())
+ {
+ ::sal_Int64 nStateSet = xContext->getAccessibleStateSet();
+ res = (nStateSet & nState) != 0;
+ }
+ return res;
+}
+
+bool isFocused(const accessibility::AccessibleEventObject& aEvent)
+{
+ return hasState(aEvent, accessibility::AccessibleStateType::FOCUSED);
+}
+
+uno::Reference<accessibility::XAccessibleContext>
+getParentContext(const uno::Reference<accessibility::XAccessibleContext>& xContext)
+{
+ uno::Reference<accessibility::XAccessibleContext> xParentContext;
+ uno::Reference<accessibility::XAccessible> xParent = xContext->getAccessibleParent();
+ if (xParent.is())
+ xParentContext = uno::Reference<accessibility::XAccessibleContext>(xParent, uno::UNO_QUERY);
+ return xParentContext;
+}
+
+OUString selectionEventTypeToString(sal_Int16 nEventId)
+{
+ using namespace accessibility;
+ switch(nEventId)
+ {
+ case AccessibleEventId::SELECTION_CHANGED:
+ return "create";
+ case AccessibleEventId::SELECTION_CHANGED_ADD:
+ return "add";
+ case AccessibleEventId::SELECTION_CHANGED_REMOVE:
+ return "remove";
+ default:
+ return "";
+ }
+}
+
+bool selectionHasToBeNotified(const uno::Reference<accessibility::XAccessibleContext>& xContext)
+{
+ sal_Int16 nRole = xContext->getAccessibleRole();
+ return
+ nRole == accessibility::AccessibleRole::GRAPHIC ||
+ nRole == accessibility::AccessibleRole::EMBEDDED_OBJECT ||
+ nRole == accessibility::AccessibleRole::SHAPE;
+}
+
+bool hasToBeActiveForEditing(sal_Int16 nRole)
+{
+ return
+ nRole == accessibility::AccessibleRole::SHAPE;
+}
+
+sal_Int16 getParentRole(const uno::Reference<accessibility::XAccessibleContext>& xContext)
+{
+ sal_Int16 nRole = 0;
+ if (xContext.is())
+ {
+ uno::Reference<accessibility::XAccessibleContext> xParentContext = getParentContext(xContext);
+ if (xParentContext.is())
+ nRole = xParentContext->getAccessibleRole();
+ }
+ return nRole;
+}
+
+sal_Int64 getAccessibleSiblingCount(const Reference<accessibility::XAccessibleContext>& xContext)
+{
+ if (!xContext.is())
+ return -1;
+
+ sal_Int64 nChildCount = 0;
+ Reference<accessibility::XAccessible> xParent = xContext->getAccessibleParent();
+ if (xParent.is())
+ {
+ Reference<accessibility::XAccessibleContext> xParentContext = xParent->getAccessibleContext();
+ if (xParentContext.is())
+ {
+ nChildCount = xParentContext->getAccessibleChildCount();
+ }
+ }
+ return nChildCount - 1;
+}
+
+// Put in rAncestorList all ancestors of xTable up to xAncestorTable or
+// up to the first not-a-table ancestor if xAncestorTable is not an ancestor.
+// xTable is included in the list, xAncestorTable is not included.
+// The list is ordered from the ancient ancestor to xTable.
+// Return true if xAncestorTable is an ancestor of xTable.
+bool getAncestorList(XAccessibleTableList& rAncestorList,
+ const uno::Reference<accessibility::XAccessibleTable>& xTable,
+ const uno::Reference<accessibility::XAccessibleTable>& xAncestorTable = uno::Reference<accessibility::XAccessibleTable>())
+{
+ uno::Reference<accessibility::XAccessibleTable> xCurrentTable = xTable;
+ while (xCurrentTable.is() && xCurrentTable != xAncestorTable)
+ {
+ rAncestorList.push_front(xCurrentTable);
+
+ uno::Reference<accessibility::XAccessibleContext> xContext(xCurrentTable, uno::UNO_QUERY);
+ xCurrentTable.clear();
+ if (xContext.is())
+ {
+ uno::Reference<accessibility::XAccessible> xParent = xContext->getAccessibleParent();
+ uno::Reference<accessibility::XAccessibleContext> xParentContext(xParent, uno::UNO_QUERY);
+ if (xParentContext.is()
+ && xParentContext->getAccessibleRole() == accessibility::AccessibleRole::TABLE_CELL)
+ {
+ uno::Reference<accessibility::XAccessible> xCellParent = xParentContext->getAccessibleParent();
+ if (xCellParent.is())
+ {
+ xCurrentTable = uno::Reference<accessibility::XAccessibleTable>(xCellParent, uno::UNO_QUERY);
+ }
+ }
+ }
+ }
+
+ return xCurrentTable.is() && xCurrentTable == xAncestorTable;
+}
+
+void lookForParentTable(const uno::Reference<accessibility::XAccessibleContext>& xContext,
+ uno::Reference<accessibility::XAccessibleTable>& xTable,
+ sal_Int64& nChildIndex)
+{
+ using namespace accessibility;
+ uno::Reference<XAccessibleContext> xParentContext = getParentContext(xContext);
+ if (xParentContext.is() && xParentContext->getAccessibleRole() == AccessibleRole::TABLE_CELL)
+ {
+ uno::Reference<XAccessible> xCellParent = xParentContext->getAccessibleParent();
+ if (xCellParent.is())
+ {
+ xTable = uno::Reference<XAccessibleTable>(xCellParent, uno::UNO_QUERY);
+ if (xTable.is())
+ {
+ nChildIndex = xParentContext->getAccessibleIndexInParent();
+ }
+ }
+ }
+}
+
+OUString truncateText(OUString& sText, sal_Int32 nNewLength)
+{
+ // truncate test to given length
+ OUString sNewText = sText.copy(0, nNewLength);
+ // try to truncate at a word
+ nNewLength = sNewText.lastIndexOf(" ");
+ if (nNewLength > 0)
+ sNewText = sNewText.copy(0, nNewLength);
+ return sNewText;
+}
+
+std::string stateSetToString(::sal_Int64 stateSet)
+{
+ static const std::string states[35] = {
+ "ACTIVE", "ARMED", "BUSY", "CHECKED", "DEFUNC",
+ "EDITABLE", "ENABLED", "EXPANDABLE", "EXPANDED", "FOCUSABLE",
+ "FOCUSED", "HORIZONTAL", "ICONIFIED", "INDETERMINATE", "MANAGES_DESCENDANTS",
+ "MODAL", "MULTI_LINE", "MULTI_SELECTABLE", "OPAQUE", "PRESSED",
+ "RESIZABLE", "SELECTABLE", "SELECTED", "SENSITIVE", "SHOWING",
+ "SINGLE_LINE", "STALE", "TRANSIENT", "VERTICAL", "VISIBLE",
+ "MOVEABLE", "DEFAULT", "OFFSCREEN", "COLLAPSE", "CHECKABLE"
+ };
+
+ if (stateSet == 0)
+ return "INVALID";
+ ::sal_Int64 state = 1;
+ std::string s;
+ for (int i = 0; i < 35; ++i)
+ {
+ if (stateSet & state)
+ {
+ s += states[i];
+ s += "|";
+ }
+ state <<= 1;
+ }
+ return s;
+}
+
+void aboutView(std::string msg, const void* pInstance, const SfxViewShell* pViewShell)
+{
+ if (!pViewShell)
+ return;
+
+ SAL_INFO("lok.a11y", ">>> " << msg << ": instance: " << pInstance
+ << ", VIED ID: " << pViewShell->GetViewShellId().get() << " <<<");
+}
+
+void aboutEvent(std::string msg, const accessibility::AccessibleEventObject& aEvent)
+{
+ try
+ {
+ uno::Reference< accessibility::XAccessible > xSource(aEvent.Source, uno::UNO_QUERY);
+ if (xSource.is())
+ {
+ uno::Reference< accessibility::XAccessibleContext > xContext =
+ xSource->getAccessibleContext();
+
+ if (xContext.is())
+ {
+ SAL_INFO("lok.a11y", msg << ": event id: " << aEvent.EventId
+ << "\n xSource: " << xSource.get()
+ << "\n role: " << xContext->getAccessibleRole()
+ << "\n name: " << xContext->getAccessibleName()
+ << "\n index in parent: " << xContext->getAccessibleIndexInParent()
+ << "\n state set: " << stateSetToString(xContext->getAccessibleStateSet())
+ << "\n parent: " << xContext->getAccessibleParent().get()
+ << "\n child count: " << xContext->getAccessibleChildCount());
+ }
+ else
+ {
+ SAL_INFO("lok.a11y", msg << ": event id: " << aEvent.EventId
+ << ", no accessible context!");
+ }
+ }
+ else
+ {
+ SAL_INFO("lok.a11y", msg << ": event id: " << aEvent.EventId
+ << ", no accessible source!");
+ }
+ uno::Reference< accessibility::XAccessible > xOldValue;
+ aEvent.OldValue >>= xOldValue;
+ if (xOldValue.is())
+ {
+ uno::Reference< accessibility::XAccessibleContext > xContext =
+ xOldValue->getAccessibleContext();
+
+ if (xContext.is())
+ {
+ SAL_INFO("lok.a11y", msg << ": "
+ "\n xOldValue: " << xOldValue.get()
+ << "\n role: " << xContext->getAccessibleRole()
+ << "\n name: " << xContext->getAccessibleName()
+ << "\n index in parent: " << xContext->getAccessibleIndexInParent()
+ << "\n state set: " << stateSetToString(xContext->getAccessibleStateSet())
+ << "\n parent: " << xContext->getAccessibleParent().get()
+ << "\n child count: " << xContext->getAccessibleChildCount());
+ }
+ }
+ uno::Reference< accessibility::XAccessible > xNewValue;
+ aEvent.NewValue >>= xNewValue;
+ if (xNewValue.is())
+ {
+ uno::Reference< accessibility::XAccessibleContext > xContext =
+ xNewValue->getAccessibleContext();
+
+ if (xContext.is())
+ {
+ SAL_INFO("lok.a11y", msg << ": "
+ "\n xNewValue: " << xNewValue.get()
+ << "\n role: " << xContext->getAccessibleRole()
+ << "\n name: " << xContext->getAccessibleName()
+ << "\n index in parent: " << xContext->getAccessibleIndexInParent()
+ << "\n state set: " << stateSetToString(xContext->getAccessibleStateSet())
+ << "\n parent: " << xContext->getAccessibleParent().get()
+ << "\n child count: " << xContext->getAccessibleChildCount());
+ }
+ }
+ }
+ catch( const lang::IndexOutOfBoundsException& /*e*/ )
+ {
+ LOK_WARN("lok.a11y", "Focused object has invalid index in parent");
+ }
+}
+
+sal_Int32 getListPrefixSize(const uno::Reference<css::accessibility::XAccessibleText>& xAccText)
+{
+ if (!xAccText.is())
+ return 0;
+
+ OUString sText = xAccText->getText();
+ sal_Int32 nLength = sText.getLength();
+ if (nLength <= 0)
+ return 0;
+
+ css::uno::Sequence< css::beans::PropertyValue > aRunAttributeList;
+ css::uno::Sequence< OUString > aRequestedAttributes = {UNO_NAME_NUMBERING_LEVEL, UNO_NAME_NUMBERING};
+ aRunAttributeList = xAccText->getCharacterAttributes(0, aRequestedAttributes);
+
+ sal_Int16 nLevel = -1;
+ bool bIsCounted = false;
+ for (const auto& attribute: aRunAttributeList)
+ {
+ if (attribute.Name.isEmpty())
+ continue;
+ if (attribute.Name == UNO_NAME_NUMBERING_LEVEL)
+ attribute.Value >>= nLevel;
+ else if (attribute.Name == UNO_NAME_NUMBERING)
+ attribute.Value >>= bIsCounted;
+ }
+ if (nLevel < 0 || !bIsCounted)
+ return 0;
+
+ css::accessibility::TextSegment aTextSegment =
+ xAccText->getTextAtIndex(0, css::accessibility::AccessibleTextType::ATTRIBUTE_RUN);
+
+ SAL_INFO("lok.a11y", "getListPrefixSize: prefix: " << aTextSegment.SegmentText << ", level: " << nLevel);
+
+ return aTextSegment.SegmentEnd;
+}
+
+void aboutTextFormatting(std::string msg, const uno::Reference<css::accessibility::XAccessibleText>& xAccText)
+{
+ if (!xAccText.is())
+ return;
+
+ OUString sText = xAccText->getText();
+ sal_Int32 nLength = sText.getLength();
+ if (nLength <= 0)
+ return;
+
+ css::uno::Reference<css::accessibility::XAccessibleTextAttributes>
+ xAccTextAttr(xAccText, uno::UNO_QUERY);
+ css::uno::Sequence< OUString > aRequestedAttributes;
+
+ sal_Int32 nPos = 0;
+ while (nPos < nLength)
+ {
+ css::accessibility::TextSegment aTextSegment =
+ xAccText->getTextAtIndex(nPos, css::accessibility::AccessibleTextType::ATTRIBUTE_RUN);
+ SAL_INFO("lok.a11y", msg << ": "
+ "text segment: '" << aTextSegment.SegmentText
+ << "', start: " << aTextSegment.SegmentStart
+ << ", end: " << aTextSegment.SegmentEnd);
+
+ css::uno::Sequence< css::beans::PropertyValue > aRunAttributeList;
+ if (xAccTextAttr.is())
+ {
+ aRunAttributeList = xAccTextAttr->getRunAttributes(nPos, aRequestedAttributes);
+ }
+ else
+ {
+ aRunAttributeList = xAccText->getCharacterAttributes(nPos, aRequestedAttributes);
+ }
+
+ sal_Int32 nSize = aRunAttributeList.getLength();
+ SAL_INFO("lok.a11y",
+ msg << ": attribute list size: " << nSize);
+ if (nSize)
+ {
+ OUString sValue;
+ OUString sAttributes = "{ ";
+ for (const auto& attribute: aRunAttributeList)
+ {
+ if (attribute.Name.isEmpty())
+ continue;
+
+ if (attribute.Name == "CharHeight" || attribute.Name == "CharWeight")
+ {
+ float fValue = 0;
+ attribute.Value >>= fValue;
+ sValue = OUString::number(fValue);
+ }
+ else if (attribute.Name == "CharPosture")
+ {
+ awt::FontSlant nValue = awt::FontSlant_NONE;
+ attribute.Value >>= nValue;
+ sValue = OUString::number(static_cast<unsigned int>(nValue));
+ }
+ else if (attribute.Name == "CharUnderline")
+ {
+ sal_Int16 nValue = 0;
+ attribute.Value >>= nValue;
+ sValue = OUString::number(nValue);
+ }
+ else if (attribute.Name == "CharFontName")
+ {
+ attribute.Value >>= sValue;
+ }
+ else if (attribute.Name == "Rsid")
+ {
+ sal_uInt32 nValue = 0;
+ attribute.Value >>= nValue;
+ sValue = OUString::number(nValue);
+ }
+ else if (attribute.Name == UNO_NAME_NUMBERING_LEVEL)
+ {
+ sal_Int16 nValue(-1);
+ attribute.Value >>= nValue;
+ sValue = OUString::number(nValue);
+ }
+ else if (attribute.Name == UNO_NAME_NUMBERING)
+ {
+ bool bValue(false);
+ attribute.Value >>= bValue;
+ sValue = OUString::boolean(bValue);
+ }
+ else if (attribute.Name == UNO_NAME_NUMBERING_RULES)
+ {
+ attribute.Value >>= sValue;
+ }
+
+ if (!sValue.isEmpty())
+ {
+ if (sAttributes != "{ ")
+ sAttributes += ", ";
+ sAttributes += attribute.Name + ": " + sValue;
+ sValue = "";
+ }
+ }
+ sAttributes += " }";
+ SAL_INFO("lok.a11y",
+ msg << ": " << sAttributes);
+ }
+ nPos = aTextSegment.SegmentEnd + 1;
+ }
+}
+
+void aboutParagraph(std::string msg, const OUString& rsParagraphContent, sal_Int32 nCaretPosition,
+ sal_Int32 nSelectionStart, sal_Int32 nSelectionEnd, sal_Int32 nListPrefixLength,
+ bool force = false)
+{
+ SAL_INFO("lok.a11y", msg << ": "
+ "\n text content: \"" << rsParagraphContent << "\""
+ "\n caret pos: " << nCaretPosition
+ << "\n selection: start: " << nSelectionStart << ", end: " << nSelectionEnd
+ << "\n list prefix length: " << nListPrefixLength
+ << "\n force: " << force
+ );
+}
+
+void aboutParagraph(std::string msg, const uno::Reference<css::accessibility::XAccessibleText>& xAccText,
+ bool force = false)
+{
+ if (!xAccText.is())
+ return;
+
+ OUString sText = xAccText->getText();
+ sal_Int32 nCaretPosition = xAccText->getCaretPosition();
+ sal_Int32 nSelectionStart = xAccText->getSelectionStart();
+ sal_Int32 nSelectionEnd = xAccText->getSelectionEnd();
+ sal_Int32 nListPrefixLength = getListPrefixSize(xAccText);
+ aboutParagraph(msg, sText, nCaretPosition, nSelectionStart, nSelectionEnd, nListPrefixLength, force);
+}
+
+void aboutFocusedCellChanged(sal_Int32 nOutCount, const std::vector<TableSizeType>& aInList,
+ sal_Int32 nRow, sal_Int32 nCol, sal_Int32 nRowSpan, sal_Int32 nColSpan)
+{
+ std::stringstream inListStream;
+ inListStream << "[ ";
+ for (const auto& rTableSize: aInList)
+ {
+ inListStream << "{ rowCount: " << rTableSize.nRowCount << " colCount: " << rTableSize.nColCount << " } ";
+ }
+ inListStream << "]";
+
+ SAL_INFO("lok.a11y", "LOKDocumentFocusListener::notifyFocusedCellChanged: "
+ "\n outCount: " << nOutCount
+ << "\n inList: " << inListStream.str()
+ << "\n row: " << nRow
+ << "\n column: " << nCol
+ << "\n rowSpan: " << nRowSpan
+ << "\n colSpan: " << nColSpan
+ );
+}
+} // anonymous namespace
+
+class LOKDocumentFocusListener :
+ public ::cppu::WeakImplHelper< accessibility::XAccessibleEventListener >
+{
+ static constexpr sal_Int64 MAX_ATTACHABLE_CHILDREN = 100;
+
+ const SfxViewShell* m_pViewShell;
+ sal_Int16 m_nDocumentType;
+ std::unordered_set<uno::Reference<uno::XInterface>> m_aRefList;
+ OUString m_sFocusedParagraph;
+ sal_Int32 m_nCaretPosition;
+ sal_Int32 m_nSelectionStart;
+ sal_Int32 m_nSelectionEnd;
+ sal_Int32 m_nListPrefixLength;
+ uno::Reference<accessibility::XAccessibleTable> m_xLastTable;
+ OUString m_sSelectedText;
+ bool m_bIsEditingCell;
+ // used for text content of a shape
+ bool m_bIsEditingInSelection;
+ OUString m_sSelectedCellAddress;
+ uno::Reference<accessibility::XAccessible> m_xSelectedObject;
+
+public:
+ explicit LOKDocumentFocusListener(const SfxViewShell* pViewShell);
+
+ /// @throws lang::IndexOutOfBoundsException
+ /// @throws uno::RuntimeException
+ void attachRecursive(
+ const uno::Reference< accessibility::XAccessible >& xAccessible
+ );
+
+ /// @throws lang::IndexOutOfBoundsException
+ /// @throws uno::RuntimeException
+ void attachRecursive(
+ const uno::Reference< accessibility::XAccessible >& xAccessible,
+ const uno::Reference< accessibility::XAccessibleContext >& xContext
+ );
+
+ /// @throws lang::IndexOutOfBoundsException
+ /// @throws uno::RuntimeException
+ void attachRecursive(
+ const uno::Reference< accessibility::XAccessible >& xAccessible,
+ const uno::Reference< accessibility::XAccessibleContext >& xContext,
+ const sal_Int64 nStateSet
+ );
+
+ /// @throws lang::IndexOutOfBoundsException
+ /// @throws uno::RuntimeException
+ void detachRecursive(
+ const uno::Reference< accessibility::XAccessible >& xAccessible,
+ bool bForce = false
+ );
+
+ /// @throws lang::IndexOutOfBoundsException
+ /// @throws uno::RuntimeException
+ void detachRecursive(
+ const uno::Reference< accessibility::XAccessibleContext >& xContext,
+ bool bForce = false
+ );
+
+ /// @throws lang::IndexOutOfBoundsException
+ /// @throws uno::RuntimeException
+ void detachRecursive(
+ const uno::Reference< accessibility::XAccessibleContext >& xContext,
+ const sal_Int64 nStateSet,
+ bool bForce = false
+ );
+
+ /// @throws lang::IndexOutOfBoundsException
+ /// @throws uno::RuntimeException
+ static uno::Reference< accessibility::XAccessible > getAccessible(const lang::EventObject& aEvent );
+
+ // XEventListener
+ virtual void SAL_CALL disposing( const lang::EventObject& Source ) override;
+
+ // XAccessibleEventListener
+ virtual void SAL_CALL notifyEvent( const accessibility::AccessibleEventObject& aEvent ) override;
+
+ void notifyEditingInSelectionState(bool bParagraph = true);
+ void notifyFocusedParagraphChanged(bool force = false);
+ void notifyCaretChanged();
+ void notifyTextSelectionChanged();
+ void notifyFocusedCellChanged(sal_Int32 nOutCount, const std::vector<TableSizeType>& aInList, sal_Int32 nRow, sal_Int32 nCol, sal_Int32 nRowSpan, sal_Int32 nColSpan);
+ void notifySelectionChanged(const uno::Reference<accessibility::XAccessible>& xAccObj, const OUString& sAction);
+
+ OUString getFocusedParagraph() const;
+ int getCaretPosition() const;
+
+private:
+ void paragraphPropertiesToTree(boost::property_tree::ptree& aPayloadTree, bool force = false) const;
+ void paragraphPropertiesToJson(std::string& aPayload, bool force = false) const;
+ bool updateParagraphInfo(const uno::Reference<css::accessibility::XAccessibleText>& xAccText,
+ bool force, std::string msg = "");
+ void updateAndNotifyParagraph(const uno::Reference<css::accessibility::XAccessibleText>& xAccText,
+ bool force, std::string msg = "");
+ void resetParagraphInfo();
+ void onFocusedParagraphInWriterTable(const uno::Reference<accessibility::XAccessibleTable>& xTable,
+ sal_Int64& nChildIndex,
+ const uno::Reference<accessibility::XAccessibleText>& xAccText);
+ uno::Reference< accessibility::XAccessible >
+ getSelectedObject(const accessibility::AccessibleEventObject& aEvent) const;
+ void onShapeSelectionChanged(const Reference<accessibility::XAccessible>& xSelectedObject,
+ const OUString& sAction);
+};
+
+LOKDocumentFocusListener::LOKDocumentFocusListener(const SfxViewShell* pViewShell)
+ : m_pViewShell(pViewShell)
+ , m_nDocumentType(0)
+ , m_nCaretPosition(0)
+ , m_nSelectionStart(0)
+ , m_nSelectionEnd(0)
+ , m_nListPrefixLength(0)
+ , m_bIsEditingCell(false)
+ , m_bIsEditingInSelection(false)
+{
+}
+
+void LOKDocumentFocusListener::paragraphPropertiesToTree(boost::property_tree::ptree& aPayloadTree, bool force) const
+{
+ bool bLeftToRight = m_nCaretPosition == m_nSelectionEnd;
+ aPayloadTree.put("content", m_sFocusedParagraph.toUtf8().getStr());
+ aPayloadTree.put("position", m_nCaretPosition);
+ aPayloadTree.put("start", bLeftToRight ? m_nSelectionStart : m_nSelectionEnd);
+ aPayloadTree.put("end", bLeftToRight ? m_nSelectionEnd : m_nSelectionStart);
+ if (m_nListPrefixLength > 0)
+ aPayloadTree.put("listPrefixLength", m_nListPrefixLength);
+ if (force)
+ aPayloadTree.put("force", 1);
+}
+
+void LOKDocumentFocusListener::paragraphPropertiesToJson(std::string& aPayload, bool force) const
+{
+ boost::property_tree::ptree aPayloadTree;
+ paragraphPropertiesToTree(aPayloadTree, force);
+ std::stringstream aStream;
+ boost::property_tree::write_json(aStream, aPayloadTree);
+ aPayload = aStream.str();
+}
+
+OUString LOKDocumentFocusListener::getFocusedParagraph() const
+{
+ aboutView("LOKDocumentFocusListener::getFocusedParagraph", this, m_pViewShell);
+ aboutParagraph("LOKDocumentFocusListener::getFocusedParagraph",
+ m_sFocusedParagraph, m_nCaretPosition,
+ m_nSelectionStart, m_nSelectionEnd, m_nListPrefixLength);
+
+ std::string aPayload;
+ paragraphPropertiesToJson(aPayload);
+ OUString sRet = OUString::fromUtf8(aPayload);
+ return sRet;
+}
+
+int LOKDocumentFocusListener::getCaretPosition() const
+{
+ aboutView("LOKDocumentFocusListener::getCaretPosition", this, m_pViewShell);
+ SAL_INFO("lok.a11y", "LOKDocumentFocusListener::getCaretPosition: " << m_nCaretPosition);
+ return m_nCaretPosition;
+}
+
+// notifyEditingInSelectionState
+// Used for notifying when editing becomes active/disabled for a shape
+// bParagraph: should we append currently focused paragraph ?
+// The problem is that the initially focused paragraph could not be the one user has clicked on,
+// when there are more than a single paragraph.
+// So in some case sending the focused paragraph could be misleading.
+void LOKDocumentFocusListener::notifyEditingInSelectionState(bool bParagraph)
+{
+ aboutView("LOKDocumentFocusListener::notifyEditingInSelectionState", this, m_pViewShell);
+
+ boost::property_tree::ptree aPayloadTree;
+ bool bIsCell = !m_sSelectedCellAddress.isEmpty();
+ aPayloadTree.put("cell", bIsCell ? 1 : 0);
+ if (bIsCell)
+ {
+ aPayloadTree.put("enabled", m_bIsEditingCell ? 1 : 0);
+ if (m_bIsEditingCell)
+ {
+ aPayloadTree.put("selection", m_sSelectedCellAddress);
+ if (bParagraph)
+ aPayloadTree.put("paragraph", m_sFocusedParagraph);
+ }
+ }
+ else
+ {
+ aPayloadTree.put("enabled", m_bIsEditingInSelection ? 1 : 0);
+ if (m_bIsEditingInSelection && m_xSelectedObject.is())
+ {
+ uno::Reference<accessibility::XAccessibleContext> xContext = m_xSelectedObject->getAccessibleContext();
+ if (xContext.is())
+ {
+ OUString sSelectionDescr = xContext->getAccessibleName();
+ sSelectionDescr = sSelectionDescr.trim();
+ aPayloadTree.put("selection", sSelectionDescr);
+ if (bParagraph)
+ aPayloadTree.put("paragraph", m_sFocusedParagraph);
+ }
+ }
+ }
+
+ std::stringstream aStream;
+ boost::property_tree::write_json(aStream, aPayloadTree);
+ std::string aPayload = aStream.str();
+ if (m_pViewShell)
+ {
+ SAL_INFO("lok.a11y", "LOKDocumentFocusListener::notifyEditingInSelectionState: payload: \n" << aPayload);
+ m_pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_A11Y_EDITING_IN_SELECTION_STATE, aPayload.c_str());
+ }
+}
+
+/// notifyFocusedParagraphChanged
+//
+// Notify content, caret position and text selection start/end for the focused paragraph
+// in current view.
+// For focused we don't mean to be necessarily the currently focused accessibility node.
+// It's enough that the caret is present in the paragraph (position != -1).
+// In fact each view has its own accessibility node per each text paragraph.
+// Anyway there can be only one focused accessibility node at time.
+// So when text changes are performed in one view, both accessibility nodes emit
+// a text changed event, anyway only the accessibility node belonging to the view
+// where the text change has occurred is the focused one.
+//
+// force: when true update the clipboard content even if client is composing.
+//
+// Usually when editing on the client involves composing the clipboard area updating
+// is skipped until the composition is over.
+// On the contrary the composition would be aborted, making dictation not possible.
+// Anyway when the text change has been performed by another view we are in due
+// to update the clipboard content even if the user is in the middle of a composition.
+void LOKDocumentFocusListener::notifyFocusedParagraphChanged(bool force)
+{
+ aboutView("LOKDocumentFocusListener::notifyFocusedParagraphChanged", this, m_pViewShell);
+ std::string aPayload;
+ paragraphPropertiesToJson(aPayload, force);
+ if (m_pViewShell)
+ {
+ aboutParagraph("LOKDocumentFocusListener::notifyFocusedParagraphChanged",
+ m_sFocusedParagraph, m_nCaretPosition,
+ m_nSelectionStart, m_nSelectionEnd, m_nListPrefixLength, force);
+
+ m_pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_A11Y_FOCUS_CHANGED, aPayload.c_str());
+ }
+}
+
+void LOKDocumentFocusListener::notifyCaretChanged()
+{
+ aboutView("LOKDocumentFocusListener::notifyCaretChanged", this, m_pViewShell);
+ boost::property_tree::ptree aPayloadTree;
+ aPayloadTree.put("position", m_nCaretPosition);
+ std::stringstream aStream;
+ boost::property_tree::write_json(aStream, aPayloadTree);
+ std::string aPayload = aStream.str();
+ if (m_pViewShell)
+ {
+ SAL_INFO("lok.a11y", "LOKDocumentFocusListener::notifyCaretChanged: " << m_nCaretPosition);
+ m_pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_A11Y_CARET_CHANGED, aPayload.c_str());
+ }
+}
+
+void LOKDocumentFocusListener::notifyTextSelectionChanged()
+{
+ aboutView("LOKDocumentFocusListener::notifyTextSelectionChanged", this, m_pViewShell);
+ bool bLeftToRight = m_nCaretPosition == m_nSelectionEnd;
+ boost::property_tree::ptree aPayloadTree;
+ aPayloadTree.put("start", bLeftToRight ? m_nSelectionStart : m_nSelectionEnd);
+ aPayloadTree.put("end", bLeftToRight ? m_nSelectionEnd : m_nSelectionStart);
+ std::stringstream aStream;
+ boost::property_tree::write_json(aStream, aPayloadTree);
+ std::string aPayload = aStream.str();
+ if (m_pViewShell)
+ {
+ SAL_INFO("lok.a11y", "LOKDocumentFocusListener::notifyTextSelectionChanged: "
+ "start: " << m_nSelectionStart << ", end: " << m_nSelectionEnd);
+ m_pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_A11Y_TEXT_SELECTION_CHANGED, aPayload.c_str());
+ }
+}
+
+void LOKDocumentFocusListener::notifyFocusedCellChanged(
+ sal_Int32 nOutCount, const std::vector<TableSizeType>& aInList,
+ sal_Int32 nRow, sal_Int32 nCol, sal_Int32 nRowSpan, sal_Int32 nColSpan)
+{
+ aboutView("LOKDocumentFocusListener::notifyTablePositionChanged", this, m_pViewShell);
+ boost::property_tree::ptree aPayloadTree;
+ if (nOutCount > 0)
+ {
+ aPayloadTree.put("outCount", nOutCount);
+ }
+ if (!aInList.empty())
+ {
+ boost::property_tree::ptree aInListNode;
+ for (const auto& rTableSize: aInList)
+ {
+ boost::property_tree::ptree aTableSizeNode;
+ aTableSizeNode.put("rowCount", rTableSize.nRowCount);
+ aTableSizeNode.put("colCount", rTableSize.nColCount);
+
+ aInListNode.push_back(std::make_pair(std::string(), aTableSizeNode));
+ }
+ aPayloadTree.add_child("inList", aInListNode);
+ }
+
+ aPayloadTree.put("row", nRow);
+ aPayloadTree.put("col", nCol);
+
+ if (nRowSpan > 1)
+ {
+ aPayloadTree.put("rowSpan", nRowSpan);
+ }
+ if (nColSpan > 1)
+ {
+ aPayloadTree.put("colSpan", nColSpan);
+ }
+
+ boost::property_tree::ptree aContentNode;
+ paragraphPropertiesToTree(aContentNode);
+ aPayloadTree.add_child("paragraph", aContentNode);
+
+ std::stringstream aStream;
+ boost::property_tree::write_json(aStream, aPayloadTree);
+ std::string aPayload = aStream.str();
+ if (m_pViewShell)
+ {
+ aboutFocusedCellChanged(nOutCount, aInList, nRow, nCol, nRowSpan, nColSpan);
+ aboutParagraph("LOKDocumentFocusListener::notifyFocusedCellChanged: paragraph: ",
+ m_sFocusedParagraph, m_nCaretPosition, m_nSelectionStart, m_nSelectionEnd,
+ m_nListPrefixLength, false);
+
+ m_pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_A11Y_FOCUSED_CELL_CHANGED, aPayload.c_str());
+ }
+}
+
+void LOKDocumentFocusListener::notifySelectionChanged(const uno::Reference<accessibility::XAccessible>& xAccObj,
+ const OUString& sAction)
+{
+ using namespace accessibility;
+ if (!xAccObj.is())
+ return;
+
+ aboutView("LOKDocumentFocusListener::notifySelectionChanged", this, m_pViewShell);
+ uno::Reference<XAccessibleContext> xContext = xAccObj->getAccessibleContext();
+ if (xContext.is())
+ {
+ OUString sName = xContext->getAccessibleName();
+ sName = sName.trim();
+ if (sName == "GraphicObjectShape")
+ sName = "Graphic";
+
+ // check for text content and send it with some limitations:
+ // no more than 10 paragraphs, no more than 1000 chars
+ bool bIsCell = xContext->getAccessibleRole() == AccessibleRole::TABLE_CELL;
+ OUString sTextContent;
+ if (sAction == "create" || sAction == "add")
+ {
+ const sal_Int64 nMaxJoinedParagraphs = 10;
+ const sal_Int32 nMaxTextContentLength = 1000;
+ if (bIsCell)
+ {
+ uno::Reference<XAccessibleText> xAccText(xAccObj, uno::UNO_QUERY);
+ if (xAccText.is())
+ {
+ sTextContent = xAccText->getText();
+ sal_Int32 nTextLength = sTextContent.getLength();
+ if (nTextLength > nMaxTextContentLength)
+ {
+ sTextContent = truncateText(sTextContent, nMaxTextContentLength);
+ }
+ }
+ }
+ else
+ {
+ sal_Int32 nTotalTextLength = 0;
+ sal_Int64 nChildCount = xContext->getAccessibleChildCount();
+ if (nChildCount > nMaxJoinedParagraphs)
+ nChildCount = nMaxJoinedParagraphs;
+ for (sal_Int64 i = 0; i < nChildCount; ++i)
+ {
+ uno::Reference<XAccessible> xChild = xContext->getAccessibleChild(i);
+ uno::Reference<XAccessibleText> xAccText(xChild, uno::UNO_QUERY);
+ if (!xAccText.is())
+ continue;
+ OUString sText = xAccText->getText();
+ sal_Int32 nTextLength = sText.getLength();
+ if (nTextLength < 1)
+ continue;
+ if (nTotalTextLength + nTextLength < nMaxTextContentLength)
+ {
+ nTotalTextLength += nTextLength;
+ sTextContent += sText + " \n";
+ }
+ else
+ {
+ // truncate paragraph
+ sal_Int32 nNewLength = nMaxTextContentLength - nTotalTextLength;
+ sTextContent += truncateText(sText, nNewLength);
+ break;
+ }
+ }
+ }
+ }
+
+ boost::property_tree::ptree aPayloadTree;
+ aPayloadTree.put("cell", bIsCell ? 1 : 0);
+ aPayloadTree.put("action", sAction);
+ aPayloadTree.put("name", sName);
+ if (!sTextContent.isEmpty())
+ aPayloadTree.put("text", sTextContent);
+ std::stringstream aStream;
+ boost::property_tree::write_json(aStream, aPayloadTree);
+ std::string aPayload = aStream.str();
+ if (m_pViewShell)
+ {
+ SAL_INFO("lok.a11y", "LOKDocumentFocusListener::notifySelectionChanged: "
+ "action: " << sAction << ", name: " << sName);
+ m_pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_A11Y_SELECTION_CHANGED, aPayload.c_str());
+ }
+ }
+}
+
+void LOKDocumentFocusListener::disposing( const lang::EventObject& aEvent )
+{
+ // Unref the object here, but do not remove as listener since the object
+ // might no longer be in a state that safely allows this.
+ if( aEvent.Source.is() )
+ m_aRefList.erase(aEvent.Source);
+
+}
+
+bool LOKDocumentFocusListener::updateParagraphInfo(const uno::Reference<css::accessibility::XAccessibleText>& xAccText,
+ bool force, std::string msg)
+{
+ if (!xAccText.is())
+ return false;
+
+ bool bNotify = false;
+ // If caret is present inside the paragraph (pos != -1), it means that paragraph has focus in the current view.
+ sal_Int32 nCaretPosition = xAccText->getCaretPosition();
+ if (nCaretPosition >= 0)
+ {
+ OUString sText = xAccText->getText();
+ m_nCaretPosition = nCaretPosition;
+ m_nSelectionStart = xAccText->getSelectionStart();
+ m_nSelectionEnd = xAccText->getSelectionEnd();
+ m_nListPrefixLength = getListPrefixSize(xAccText);
+
+ // Inside a text shape when there is no selection, selection-start and selection-end are
+ // set to current caret position instead of -1. Moreover, inside a text shape pressing
+ // delete or backspace with an empty selection really deletes text and not only the empty
+ // selection as it occurs in a text paragraph in Writer.
+ // So whenever selection-start == selection-end, and we are inside a shape we need
+ // to set these parameters to -1 in order to have the client to handle delete and
+ // backspace properly.
+ if (m_nSelectionStart == m_nSelectionEnd && m_nSelectionStart != -1)
+ {
+ uno::Reference<accessibility::XAccessibleContext> xContext(xAccText, uno::UNO_QUERY);
+ sal_Int16 nParentRole = getParentRole(xContext);
+ if (nParentRole == accessibility::AccessibleRole::SHAPE ||
+ nParentRole == accessibility::AccessibleRole::TEXT_FRAME) // spreadsheet cell editing
+ m_nSelectionStart = m_nSelectionEnd = -1;
+ }
+
+ // In case only caret position or text selection are different we can rely on specific events.
+ if (m_sFocusedParagraph != sText)
+ {
+ m_sFocusedParagraph = sText;
+ bNotify = true;
+ }
+ }
+ else
+ {
+ SAL_WARN("lok.a11y",
+ "LOKDocumentFocusListener::updateParagraphInfo: skipped since no caret is present");
+ }
+
+ std::string header = "LOKDocumentFocusListener::updateParagraphInfo";
+ if (msg.size())
+ header += ": " + msg;
+ aboutParagraph(header, xAccText, force);
+ return bNotify;
+
+}
+
+void LOKDocumentFocusListener::updateAndNotifyParagraph(
+ const uno::Reference<css::accessibility::XAccessibleText>& xAccText,
+ bool force, std::string msg)
+{
+ if (updateParagraphInfo(xAccText, force, msg))
+ notifyFocusedParagraphChanged(force);
+}
+
+void LOKDocumentFocusListener::resetParagraphInfo()
+{
+ m_sFocusedParagraph = "";
+ m_nCaretPosition = 0;
+ m_nSelectionStart = -1;
+ m_nSelectionEnd = -1;
+ m_nListPrefixLength = 0;
+}
+
+// For a presentation document when an accessible event of type SELECTION_CHANGED_XXX occurs
+// the selected (or unselected) object is put in NewValue, instead for a text document
+// the selected object is put in Source.
+// The following function helps to retrieve the selected object independently on where it has been put.
+uno::Reference< accessibility::XAccessible >
+LOKDocumentFocusListener::getSelectedObject(const accessibility::AccessibleEventObject& aEvent) const
+{
+ uno::Reference< accessibility::XAccessible > xSelectedObject;
+ if (isText(m_nDocumentType))
+ {
+ xSelectedObject.set(aEvent.Source, uno::UNO_QUERY);
+ }
+ else
+ {
+ aEvent.NewValue >>= xSelectedObject;
+ }
+ return xSelectedObject;
+}
+
+void LOKDocumentFocusListener::onShapeSelectionChanged(
+ const uno::Reference<accessibility::XAccessible>& xSelectedObject,
+ const OUString& sAction)
+{
+ // when a shape is selected or unselected we could need to notify that text content editing
+ // is no more active, that allows on the client side to prevent default input.
+ resetParagraphInfo();
+ if (m_bIsEditingInSelection)
+ {
+ m_bIsEditingInSelection = false;
+ notifyEditingInSelectionState();
+ }
+ notifySelectionChanged(xSelectedObject, sAction);
+}
+
+void LOKDocumentFocusListener::onFocusedParagraphInWriterTable(
+ const uno::Reference<accessibility::XAccessibleTable>& xTable,
+ sal_Int64& nChildIndex,
+ const uno::Reference<accessibility::XAccessibleText>& xAccText
+)
+{
+ std::vector<TableSizeType> aInList;
+ sal_Int32 nOutCount = 0;
+
+ if (m_xLastTable.is())
+ {
+ if (xTable != m_xLastTable)
+ {
+ // do we get in one or more nested tables ?
+ // check if xTable is a descendant of m_xLastTable
+ XAccessibleTableList newTableAncestorList;
+ bool isLastAncestorOfNew = getAncestorList(newTableAncestorList, xTable, m_xLastTable);
+ bool isNewAncestorOfLast = false;
+ if (!isLastAncestorOfNew)
+ {
+ // do we get out of one or more nested tables ?
+ // check if m_xLastTable is a descendant of xTable
+ XAccessibleTableList lastTableAncestorList;
+ isNewAncestorOfLast = getAncestorList(lastTableAncestorList, m_xLastTable, xTable);
+ // we have to notify "out of table" for all m_xLastTable ancestors up to xTable
+ // or the first not-a-table ancestor
+ nOutCount = lastTableAncestorList.size();
+ }
+ if (isLastAncestorOfNew || !isNewAncestorOfLast)
+ {
+ // we have to notify row/col count for all xTable ancestors starting from the ancestor
+ // which is a child of m_xLastTable (isLastAncestorOfNew) or the first not-a-table ancestor
+ for (const auto& ancestor: newTableAncestorList)
+ {
+ TableSizeType aTableSize{ancestor->getAccessibleRowCount(),
+ ancestor->getAccessibleColumnCount()};
+ aInList.push_back(aTableSize);
+ }
+ }
+ }
+ }
+ else
+ {
+ // cursor was not inside any table and gets inside one or more tables
+ // we have to notify row/col count for all xTable ancestors starting from first not-a-table ancestor
+ XAccessibleTableList newTableAncestorList;
+ getAncestorList(newTableAncestorList, xTable);
+ for (const auto& ancestor: newTableAncestorList)
+ {
+ TableSizeType aTableSize{ancestor->getAccessibleRowCount(),
+ ancestor->getAccessibleColumnCount()};
+ aInList.push_back(aTableSize);
+ }
+ }
+
+ // we have to notify current row/col of xTable and related row/col span
+ sal_Int32 nRow = xTable->getAccessibleRow(nChildIndex);
+ sal_Int32 nCol = xTable->getAccessibleColumn(nChildIndex);
+ sal_Int32 nRowSpan = xTable->getAccessibleRowExtentAt(nRow, nCol);
+ sal_Int32 nColSpan = xTable->getAccessibleColumnExtentAt(nRow, nCol);
+
+ m_xLastTable = xTable;
+ updateParagraphInfo(xAccText, false, "STATE_CHANGED: FOCUSED");
+ notifyFocusedCellChanged(nOutCount, aInList, nRow, nCol, nRowSpan, nColSpan);
+}
+
+void LOKDocumentFocusListener::notifyEvent(const accessibility::AccessibleEventObject& aEvent)
+{
+ using namespace accessibility;
+ aboutView("LOKDocumentFocusListener::notifyEvent", this, m_pViewShell);
+
+ try
+ {
+ aboutEvent("LOKDocumentFocusListener::notifyEvent", aEvent);
+
+ switch (aEvent.EventId)
+ {
+ case AccessibleEventId::STATE_CHANGED:
+ {
+ // logging
+ sal_Int64 nState = accessibility::AccessibleStateType::INVALID;
+ aEvent.NewValue >>= nState;
+ sal_Int64 nOldState = accessibility::AccessibleStateType::INVALID;
+ aEvent.OldValue >>= nOldState;
+ SAL_INFO("lok.a11y", "LOKDocumentFocusListener::notifyEvent: STATE_CHANGED: "
+ " New State: " << stateSetToString(nState)
+ << ", Old State: " << stateSetToString(nOldState));
+
+ // check validity
+ uno::Reference< XAccessible > xAccessibleObject = getAccessible(aEvent);
+ if (!xAccessibleObject.is())
+ return;
+ uno::Reference<XAccessibleContext> xContext(aEvent.Source, uno::UNO_QUERY);
+ if (!xContext)
+ return;
+
+ sal_Int16 nRole = xContext->getAccessibleRole();
+
+ if (nRole == AccessibleRole::PARAGRAPH)
+ {
+ uno::Reference<XAccessibleText> xAccText(xAccessibleObject, uno::UNO_QUERY);
+ if (!xAccText.is())
+ return;
+
+ switch (nState)
+ {
+ case AccessibleStateType::ACTIVE:
+ {
+ if (!m_bIsEditingInSelection && hasToBeActiveForEditing(getParentRole(xContext)))
+ {
+ m_bIsEditingInSelection = true;
+ }
+ break;
+ }
+ case AccessibleStateType::FOCUSED:
+ {
+ if (m_bIsEditingInSelection && m_xSelectedObject.is())
+ {
+ updateParagraphInfo(xAccText, true, "STATE_CHANGED: FOCUSED");
+ notifyEditingInSelectionState(getAccessibleSiblingCount(xContext) == 0);
+ notifyFocusedParagraphChanged(true);
+ // we clear selected object so when editing is over but shape is
+ // still selected, the selection event is notified the same to the client
+ m_xSelectedObject.clear();
+ return;
+ }
+ if (isText(m_nDocumentType))
+ {
+ // check if we are inside a table: in case notify table and current cell info
+ bool isInsideTable = false;
+ uno::Reference<XAccessibleTable> xTable;
+ sal_Int64 nChildIndex;
+ lookForParentTable(xContext, xTable, nChildIndex);
+ if (xTable.is())
+ {
+ onFocusedParagraphInWriterTable(xTable, nChildIndex, xAccText);
+ isInsideTable = true;
+ }
+ // paragraph is not inside any table
+ if (!isInsideTable)
+ {
+ if (m_xLastTable.is())
+ {
+ // we get out one or more tables
+ // we have to notify "out of table" for all m_xLastTable ancestors
+ // up to the first not-a-table ancestor
+ XAccessibleTableList lastTableAncestorList;
+ getAncestorList(lastTableAncestorList, m_xLastTable);
+ sal_Int32 nOutCount = lastTableAncestorList.size();
+ // no more inside a table
+ m_xLastTable.clear();
+ // notify
+ std::vector<TableSizeType> aInList;
+ updateParagraphInfo(xAccText, false, "STATE_CHANGED: FOCUSED");
+ notifyFocusedCellChanged(nOutCount, aInList, -1, -1, 1, 1);
+ }
+ else
+ {
+ updateAndNotifyParagraph(xAccText, false, "STATE_CHANGED: FOCUSED");
+ }
+ }
+ }
+ else if (isSpreadsheet(m_nDocumentType))
+ {
+ if (m_bIsEditingCell)
+ {
+ if (!hasState(aEvent, AccessibleStateType::ACTIVE))
+ {
+ SAL_WARN("lok.a11y",
+ "LOKDocumentFocusListener::notifyEvent: FOCUSED: "
+ "cell not ACTIVE for editing yet");
+ return;
+ }
+ else if (m_xSelectedObject.is())
+ {
+ updateParagraphInfo(xAccText, true, "STATE_CHANGED: ACTIVE");
+ notifyEditingInSelectionState(getAccessibleSiblingCount(xContext) == 0);
+ notifyFocusedParagraphChanged(true);
+ m_xSelectedObject.clear();
+ return;
+ }
+
+ updateAndNotifyParagraph(xAccText, false, "STATE_CHANGED: FOCUSED");
+ }
+ }
+ else if (isPresentation(m_nDocumentType))
+ {
+ updateAndNotifyParagraph(xAccText, false, "STATE_CHANGED: FOCUSED");
+ }
+ aboutTextFormatting("LOKDocumentFocusListener::notifyEvent: STATE_CHANGED: FOCUSED", xAccText);
+
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ break;
+ }
+ case AccessibleEventId::CARET_CHANGED:
+ {
+ sal_Int32 nNewPos = -1;
+ aEvent.NewValue >>= nNewPos;
+ sal_Int32 nOldPos = -1;
+ aEvent.OldValue >>= nOldPos;
+
+ if (nNewPos >= 0)
+ {
+ SAL_INFO("lok.a11y", "LOKDocumentFocusListener::notifyEvent: CARET_CHANGED: "
+ "new pos: " << nNewPos << ", nOldPos: " << nOldPos);
+
+ uno::Reference<XAccessibleText> xAccText(getAccessible(aEvent), uno::UNO_QUERY);
+ if (xAccText.is())
+ {
+ m_nCaretPosition = nNewPos;
+ // Let's say we are in the following case: 'Hello wor|ld',
+ // where '|' is the cursor position for the current view.
+ // Suppose that in another view it's typed <enter> soon before 'world'.
+ // Now the new paragraph content and caret position is: 'wor|ld'.
+ // Anyway no new paragraph focused event is emitted for current view.
+ // Only a new caret position event is emitted.
+ // So we could need to notify a new focused paragraph changed message.
+ if (!isFocused(aEvent))
+ {
+ if (updateParagraphInfo(xAccText, false, "CARET_CHANGED"))
+ notifyFocusedParagraphChanged(true);
+ }
+ else
+ {
+ notifyCaretChanged();
+ }
+ aboutParagraph("LOKDocumentFocusListener::notifyEvent: CARET_CHANGED", xAccText);
+ }
+ }
+ break;
+ }
+ case AccessibleEventId::TEXT_CHANGED:
+ {
+ TextSegment aDeletedText;
+ TextSegment aInsertedText;
+
+ if (aEvent.OldValue >>= aDeletedText)
+ {
+ SAL_INFO("lok.a11y", "LOKDocumentFocusListener::notifyEvent: TEXT_CHANGED: "
+ "deleted text: >" << aDeletedText.SegmentText << "<");
+ }
+ if (aEvent.NewValue >>= aInsertedText)
+ {
+ SAL_INFO("lok.a11y", "LOKDocumentFocusListener::notifyEvent: TEXT_CHANGED: "
+ "inserted text: >" << aInsertedText.SegmentText << "<");
+ }
+ uno::Reference<XAccessibleText> xAccText(getAccessible(aEvent), uno::UNO_QUERY);
+
+ // When the change has been performed in another view we need to force
+ // paragraph content updating on the client, even if current editing involves composing.
+ // We make a guess that if the paragraph accessibility node is not focused,
+ // it means that the text change has been performed in another view.
+ updateAndNotifyParagraph(xAccText, !isFocused(aEvent), "TEXT_CHANGED");
+
+ break;
+ }
+ case AccessibleEventId::TEXT_SELECTION_CHANGED:
+ {
+ if (!isFocused(aEvent))
+ {
+ SAL_WARN("lok.a11y",
+ "LOKDocumentFocusListener::notifyEvent: TEXT_SELECTION_CHANGED: "
+ "skip non focused paragraph");
+ return;
+ }
+
+ uno::Reference<XAccessibleText> xAccText(getAccessible(aEvent), uno::UNO_QUERY);
+ if (xAccText.is())
+ {
+ // We send a message to client also when start/end are -1, in this way the client knows
+ // if a text selection object exists or not. That's needed because of the odd behavior
+ // occurring when <backspace>/<delete> are hit and a text selection is empty,
+ // but it still exists.
+ // Such keys delete the empty selection instead of the previous/next char.
+ updateParagraphInfo(xAccText, false, "TEXT_SELECTION_CHANGED");
+
+ m_sSelectedText = xAccText->getSelectedText();
+ SAL_INFO("lok.a11y",
+ "LOKDocumentFocusListener::notifyEvent: TEXT_SELECTION_CHANGED: selected text: >"
+ << m_sSelectedText << "<");
+
+ // Calc: when editing a formula send the update content
+ if (m_bIsEditingCell)
+ {
+ OUString sText = xAccText->getText();
+ if (!m_sSelectedCellAddress.isEmpty() &&
+ !m_sSelectedText.isEmpty() && sText.startsWith("="))
+ {
+ notifyFocusedParagraphChanged();
+ }
+ }
+ notifyTextSelectionChanged();
+ }
+ break;
+ }
+ case AccessibleEventId::SELECTION_CHANGED:
+ case AccessibleEventId::SELECTION_CHANGED_REMOVE:
+ {
+ uno::Reference< XAccessible > xSelectedObject = getSelectedObject(aEvent);
+ if (!xSelectedObject.is())
+ return;
+ uno::Reference< XAccessibleContext > xContext = xSelectedObject->getAccessibleContext();
+ if (!xContext.is())
+ return;
+
+ if (aEvent.EventId == AccessibleEventId::SELECTION_CHANGED_REMOVE)
+ m_xSelectedObject.clear();
+ else if (m_xSelectedObject.is() && m_xSelectedObject == xSelectedObject)
+ return; // selecting the same object; note: on editing selected object is cleared
+ else
+ m_xSelectedObject = xSelectedObject;
+ SAL_INFO("lok.a11y", "LOKDocumentFocusListener::notifyEvent: SELECTION_CHANGED: "
+ "m_xSelectedObject.is(): " << m_xSelectedObject.is());
+
+ OUString sAction = selectionEventTypeToString(aEvent.EventId);
+ sal_Int16 nRole = xContext->getAccessibleRole();
+ switch(nRole)
+ {
+ case AccessibleRole::GRAPHIC:
+ case AccessibleRole::EMBEDDED_OBJECT:
+ case AccessibleRole::SHAPE:
+ {
+ onShapeSelectionChanged(xSelectedObject, sAction);
+ break;
+ }
+ case AccessibleRole::TABLE_CELL:
+ {
+ notifySelectionChanged(xSelectedObject, sAction);
+
+ if (aEvent.EventId == AccessibleEventId::SELECTION_CHANGED)
+ {
+ m_sSelectedCellAddress = xContext->getAccessibleName();
+ if (m_bIsEditingCell && !m_sSelectedCellAddress.isEmpty())
+ {
+ // Check cell address: "$Sheet1.A10".
+ // On cell editing SELECTION_CHANGED is not emitted when selection is expanded.
+ // So selection can't be a cell range.
+ sal_Int32 nDotIndex = m_sSelectedText.indexOf('.');
+ OUString sCellAddress = m_sSelectedText.copy(nDotIndex + 1);
+ SAL_INFO("lok.a11y", "LOKDocumentFocusListener::notifyEvent: SELECTION_CHANGED: "
+ "cell address: >" << sCellAddress << "<");
+ if (m_sSelectedCellAddress == sCellAddress)
+ {
+ notifyFocusedParagraphChanged();
+ notifyTextSelectionChanged();
+ }
+ }
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ break;
+ }
+ case AccessibleEventId::CHILD:
+ {
+ uno::Reference< accessibility::XAccessible > xChild;
+ if( (aEvent.OldValue >>= xChild) && xChild.is() )
+ detachRecursive(xChild);
+
+ if( (aEvent.NewValue >>= xChild) && xChild.is() )
+ attachRecursive(xChild);
+
+ break;
+ }
+ case AccessibleEventId::INVALIDATE_ALL_CHILDREN:
+ {
+ SAL_INFO("lok.a11y", "Invalidate all children called");
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ catch( const lang::IndexOutOfBoundsException& )
+ {
+ LOK_WARN("lok.a11y",
+ "LOKDocumentFocusListener::notifyEvent:Focused object has invalid index in parent");
+ }
+}
+
+uno::Reference< accessibility::XAccessible > LOKDocumentFocusListener::getAccessible(const lang::EventObject& aEvent )
+{
+ uno::Reference< accessibility::XAccessible > xAccessible(aEvent.Source, uno::UNO_QUERY);
+
+ if( xAccessible.is() )
+ return xAccessible;
+
+ SAL_WARN("lok.a11y",
+ "LOKDocumentFocusListener::getAccessible: Event source doesn't implement XAccessible.");
+
+ uno::Reference< accessibility::XAccessibleContext > xContext(aEvent.Source, uno::UNO_QUERY);
+
+ if( xContext.is() )
+ {
+ uno::Reference< accessibility::XAccessible > xParent( xContext->getAccessibleParent() );
+ if( xParent.is() )
+ {
+ uno::Reference< accessibility::XAccessibleContext > xParentContext( xParent->getAccessibleContext() );
+ if( xParentContext.is() )
+ {
+ return xParentContext->getAccessibleChild( xContext->getAccessibleIndexInParent() );
+ }
+ }
+ }
+
+ LOK_WARN("lok.a11y",
+ "LOKDocumentFocusListener::getAccessible: Can't get any accessible object from event source.");
+
+ return uno::Reference< accessibility::XAccessible >();
+}
+
+void LOKDocumentFocusListener::attachRecursive(
+ const uno::Reference< accessibility::XAccessible >& xAccessible
+)
+{
+ LOK_INFO("lok.a11y", "LOKDocumentFocusListener::attachRecursive(1): xAccessible: " << xAccessible.get());
+
+ uno::Reference< accessibility::XAccessibleContext > xContext =
+ xAccessible->getAccessibleContext();
+
+ if( xContext.is() )
+ attachRecursive(xAccessible, xContext);
+}
+
+void LOKDocumentFocusListener::attachRecursive(
+ const uno::Reference< accessibility::XAccessible >& xAccessible,
+ const uno::Reference< accessibility::XAccessibleContext >& xContext
+)
+{
+ try
+ {
+ LOK_INFO("lok.a11y", "LOKDocumentFocusListener::attachRecursive(2): xAccessible: "
+ << xAccessible.get() << ", role: " << xContext->getAccessibleRole()
+ << ", name: " << xContext->getAccessibleName()
+ << ", parent: " << xContext->getAccessibleParent().get()
+ << ", child count: " << xContext->getAccessibleChildCount());
+
+ sal_Int64 nStateSet = xContext->getAccessibleStateSet();
+
+ if (!m_bIsEditingCell)
+ {
+ ::rtl::OUString sName = xContext->getAccessibleName();
+ m_bIsEditingCell = sName.startsWith("Cell");
+ }
+
+ attachRecursive(xAccessible, xContext, nStateSet);
+ }
+ catch (const uno::Exception& e)
+ {
+ LOK_WARN("lok.a11y", "LOKDocumentFocusListener::attachRecursive(2): raised exception: " << e.Message);
+ }
+}
+
+void LOKDocumentFocusListener::attachRecursive(
+ const uno::Reference< accessibility::XAccessible >& xAccessible,
+ const uno::Reference< accessibility::XAccessibleContext >& xContext,
+ const sal_Int64 nStateSet
+)
+{
+ aboutView("LOKDocumentFocusListener::attachRecursive (3)", this, m_pViewShell);
+ SAL_INFO("lok.a11y", "LOKDocumentFocusListener::attachRecursive(3) #1: this: " << this
+ << ", xAccessible: " << xAccessible.get()
+ << ", role: " << xContext->getAccessibleRole()
+ << ", name: " << xContext->getAccessibleName()
+ << ", index in parent: " << xContext->getAccessibleIndexInParent()
+ << ", state: " << stateSetToString(nStateSet)
+ << ", parent: " << xContext->getAccessibleParent().get()
+ << ", child count: " << xContext->getAccessibleChildCount());
+
+ uno::Reference< accessibility::XAccessibleEventBroadcaster > xBroadcaster(xContext, uno::UNO_QUERY);
+
+ if (!xBroadcaster.is())
+ return;
+ SAL_INFO("lok.a11y", "LOKDocumentFocusListener::attachRecursive(3) #2: xBroadcaster.is()");
+ // If not already done, add the broadcaster to the list and attach as listener.
+ const uno::Reference< uno::XInterface >& xInterface = xBroadcaster;
+ if( m_aRefList.insert(xInterface).second )
+ {
+ SAL_INFO("lok.a11y", "LOKDocumentFocusListener::attachRecursive(3) #3: m_aRefList.insert(xInterface).second");
+ xBroadcaster->addAccessibleEventListener(static_cast< accessibility::XAccessibleEventListener *>(this));
+
+ if (isDocument(xContext->getAccessibleRole()))
+ {
+ m_nDocumentType = xContext->getAccessibleRole();
+ }
+
+ if (!(nStateSet & accessibility::AccessibleStateType::MANAGES_DESCENDANTS))
+ {
+ if ((nStateSet & accessibility::AccessibleStateType::SELECTED) &&
+ selectionHasToBeNotified(xContext))
+ {
+ uno::Reference< accessibility::XAccessible > xAccObj(xContext, uno::UNO_QUERY);
+ onShapeSelectionChanged(xAccObj, "create");
+ }
+
+ sal_Int64 nmax = xContext->getAccessibleChildCount();
+ if( nmax > MAX_ATTACHABLE_CHILDREN )
+ nmax = MAX_ATTACHABLE_CHILDREN;
+
+ for( sal_Int64 n = 0; n < nmax; n++ )
+ {
+ uno::Reference< accessibility::XAccessible > xChild( xContext->getAccessibleChild( n ) );
+
+ if( xChild.is() )
+ attachRecursive(xChild);
+ }
+ }
+ else
+ {
+ // Usually, when the document is loaded, a CARET_CHANGED accessibility event is automatically emitted
+ // for the first paragraph. That allows to notify the paragraph content to the client, even if no input
+ // event occurred yet. However, when switching to a11y enabled in the client and in Cypress tests
+ // no accessibility event is automatically emitted until some input event occurs.
+ // So we use the following workaround to notify the content of the focused paragraph,
+ // without waiting for an input event.
+ // Here we update the paragraph info related to the focused paragraph,
+ // later when afterCallbackRegistered is executed we notify the paragraph content.
+ sal_Int64 nChildCount = xContext->getAccessibleChildCount();
+ if (nChildCount > 0 && nChildCount < 10)
+ {
+ for (sal_Int64 n = 0; n < nChildCount; ++n)
+ {
+ uno::Reference< accessibility::XAccessible > xChild(xContext->getAccessibleChild(n));
+ if (xChild.is())
+ {
+ uno::Reference<css::accessibility::XAccessibleText> xAccText(xChild, uno::UNO_QUERY);
+ if (xAccText.is())
+ {
+ sal_Int32 nPos = xAccText->getCaretPosition();
+ if (nPos >= 0)
+ {
+ attachRecursive(xChild);
+ updateParagraphInfo(xAccText, false, "LOKDocumentFocusListener::attachRecursive(3)");
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+void LOKDocumentFocusListener::detachRecursive(
+ const uno::Reference< accessibility::XAccessible >& xAccessible,
+ bool bForce
+)
+{
+ uno::Reference< accessibility::XAccessibleContext > xContext =
+ xAccessible->getAccessibleContext();
+
+ if( xContext.is() )
+ detachRecursive(xContext, bForce);
+}
+
+void LOKDocumentFocusListener::detachRecursive(
+ const uno::Reference< accessibility::XAccessibleContext >& xContext,
+ bool bForce
+)
+{
+ aboutView("LOKDocumentFocusListener::detachRecursive (2)", this, m_pViewShell);
+ sal_Int64 nStateSet = xContext->getAccessibleStateSet();
+
+ SAL_INFO("lok.a11y", "LOKDocumentFocusListener::detachRecursive(2): this: " << this
+ << ", name: " << xContext->getAccessibleName()
+ << ", parent: " << xContext->getAccessibleParent().get()
+ << ", child count: " << xContext->getAccessibleChildCount());
+
+ if (m_bIsEditingCell)
+ {
+ ::rtl::OUString sName = xContext->getAccessibleName();
+ m_bIsEditingCell = !sName.startsWith("Cell");
+ if (!m_bIsEditingCell)
+ {
+ m_sFocusedParagraph = "";
+ m_nCaretPosition = 0;
+ notifyFocusedParagraphChanged();
+ }
+ }
+
+ detachRecursive(xContext, nStateSet, bForce);
+}
+
+void LOKDocumentFocusListener::detachRecursive(
+ const uno::Reference< accessibility::XAccessibleContext >& xContext,
+ const sal_Int64 nStateSet,
+ bool bForce
+)
+{
+ uno::Reference< accessibility::XAccessibleEventBroadcaster > xBroadcaster(xContext, uno::UNO_QUERY);
+
+ if (xBroadcaster.is() && 0 < m_aRefList.erase(xBroadcaster))
+ {
+ xBroadcaster->removeAccessibleEventListener(static_cast< accessibility::XAccessibleEventListener *>(this));
+
+ if ((nStateSet & accessibility::AccessibleStateType::SELECTED) &&
+ selectionHasToBeNotified(xContext))
+ {
+ uno::Reference< accessibility::XAccessible > xAccObj(xContext, uno::UNO_QUERY);
+ onShapeSelectionChanged(xAccObj, "delete");
+ }
+
+ if (bForce || !(nStateSet & accessibility::AccessibleStateType::MANAGES_DESCENDANTS))
+ {
+ sal_Int64 nmax = xContext->getAccessibleChildCount();
+ if (nmax > MAX_ATTACHABLE_CHILDREN)
+ nmax = MAX_ATTACHABLE_CHILDREN;
+ for (sal_Int64 n = 0; n < nmax; n++)
+ {
+ uno::Reference< accessibility::XAccessible > xChild(xContext->getAccessibleChild(n));
+
+ if (xChild.is())
+ detachRecursive(xChild);
+ }
+ }
+ }
+}
+
+sal_uInt32 SfxViewShell_Impl::m_nLastViewShellId = 0;
+
+SfxViewShell_Impl::SfxViewShell_Impl(SfxViewShellFlags const nFlags, ViewShellDocId nDocId)
+: m_bHasPrintOptions(nFlags & SfxViewShellFlags::HAS_PRINTOPTIONS)
+, m_nFamily(0xFFFF) // undefined, default set by TemplateDialog
+, m_pLibreOfficeKitViewCallback(nullptr)
+, m_bTiledSearching(false)
+, m_nViewShellId(SfxViewShell_Impl::m_nLastViewShellId++)
+, m_nDocId(nDocId)
+{
+}
+
+SfxViewShell_Impl::~SfxViewShell_Impl()
+{
+}
+
+std::vector< SfxInPlaceClient* >& SfxViewShell_Impl::GetIPClients_Impl()
+{
+ return maIPClients;
+}
+
+SFX_IMPL_SUPERCLASS_INTERFACE(SfxViewShell,SfxShell)
+
+void SfxViewShell::InitInterface_Impl()
+{
+}
+
+
+/** search for a filter name dependent on type and module
+ */
+static OUString impl_retrieveFilterNameFromTypeAndModule(
+ const css::uno::Reference< css::container::XContainerQuery >& rContainerQuery,
+ const OUString& rType,
+ const OUString& rModuleIdentifier,
+ const sal_Int32 nFlags )
+{
+ // Retrieve filter from type
+ css::uno::Sequence< css::beans::NamedValue > aQuery {
+ { "Type", css::uno::Any( rType ) },
+ { "DocumentService", css::uno::Any( rModuleIdentifier ) }
+ };
+
+ css::uno::Reference< css::container::XEnumeration > xEnumeration =
+ rContainerQuery->createSubSetEnumerationByProperties( aQuery );
+
+ OUString aFoundFilterName;
+ while ( xEnumeration->hasMoreElements() )
+ {
+ ::comphelper::SequenceAsHashMap aFilterPropsHM( xEnumeration->nextElement() );
+ OUString aFilterName = aFilterPropsHM.getUnpackedValueOrDefault(
+ "Name",
+ OUString() );
+
+ sal_Int32 nFilterFlags = aFilterPropsHM.getUnpackedValueOrDefault(
+ "Flags",
+ sal_Int32( 0 ) );
+
+ if ( nFilterFlags & nFlags )
+ {
+ aFoundFilterName = aFilterName;
+ break;
+ }
+ }
+
+ return aFoundFilterName;
+}
+
+namespace {
+
+/** search for an internal typename, which map to the current app module
+ and map also to a "family" of file formats as e.g. PDF/MS Doc/OOo Doc.
+ */
+enum ETypeFamily
+{
+ E_MS_DOC,
+ E_OOO_DOC
+};
+
+}
+
+static OUString impl_searchFormatTypeForApp(const css::uno::Reference< css::frame::XFrame >& xFrame ,
+ ETypeFamily eTypeFamily)
+{
+ try
+ {
+ css::uno::Reference< css::uno::XComponentContext > xContext (::comphelper::getProcessComponentContext());
+ css::uno::Reference< css::frame::XModuleManager2 > xModuleManager(css::frame::ModuleManager::create(xContext));
+
+ OUString sModule = xModuleManager->identify(xFrame);
+ OUString sType ;
+
+ switch(eTypeFamily)
+ {
+ case E_MS_DOC:
+ {
+ if ( sModule == "com.sun.star.text.TextDocument" )
+ sType = "writer_MS_Word_2007";
+ else
+ if ( sModule == "com.sun.star.sheet.SpreadsheetDocument" )
+ sType = "MS Excel 2007 XML";
+ else
+ if ( sModule == "com.sun.star.presentation.PresentationDocument" )
+ sType = "MS PowerPoint 2007 XML";
+ }
+ break;
+
+ case E_OOO_DOC:
+ {
+ if ( sModule == "com.sun.star.text.TextDocument" )
+ sType = "writer8";
+ else
+ if ( sModule == "com.sun.star.sheet.SpreadsheetDocument" )
+ sType = "calc8";
+ else
+ if ( sModule == "com.sun.star.drawing.DrawingDocument" )
+ sType = "draw8";
+ else
+ if ( sModule == "com.sun.star.presentation.PresentationDocument" )
+ sType = "impress8";
+ }
+ break;
+ }
+
+ return sType;
+ }
+ catch (const css::uno::RuntimeException&)
+ {
+ throw;
+ }
+ catch (const css::uno::Exception&)
+ {
+ }
+
+ return OUString();
+}
+
+void SfxViewShell::NewIPClient_Impl( SfxInPlaceClient *pIPClient )
+{
+ pImpl->GetIPClients_Impl().push_back(pIPClient);
+}
+
+void SfxViewShell::IPClientGone_Impl( SfxInPlaceClient const *pIPClient )
+{
+ std::vector< SfxInPlaceClient* >& pClients = pImpl->GetIPClients_Impl();
+
+ auto it = std::find(pClients.begin(), pClients.end(), pIPClient);
+ if (it != pClients.end())
+ pClients.erase( it );
+}
+
+
+void SfxViewShell::ExecMisc_Impl( SfxRequest &rReq )
+{
+ const sal_uInt16 nId = rReq.GetSlot();
+ switch( nId )
+ {
+ case SID_STYLE_FAMILY :
+ {
+ const SfxUInt16Item* pItem = rReq.GetArg<SfxUInt16Item>(nId);
+ if (pItem)
+ {
+ pImpl->m_nFamily = pItem->GetValue();
+ }
+ break;
+ }
+ case SID_ACTIVATE_STYLE_APPLY:
+ {
+ uno::Reference< frame::XFrame > xFrame =
+ GetViewFrame().GetFrame().GetFrameInterface();
+
+ Reference< beans::XPropertySet > xPropSet( xFrame, UNO_QUERY );
+ Reference< frame::XLayoutManager > xLayoutManager;
+ if ( xPropSet.is() )
+ {
+ try
+ {
+ Any aValue = xPropSet->getPropertyValue("LayoutManager");
+ aValue >>= xLayoutManager;
+ if ( xLayoutManager.is() )
+ {
+ uno::Reference< ui::XUIElement > xElement = xLayoutManager->getElement( "private:resource/toolbar/textobjectbar" );
+ if(!xElement.is())
+ {
+ xElement = xLayoutManager->getElement( "private:resource/toolbar/frameobjectbar" );
+ }
+ if(!xElement.is())
+ {
+ xElement = xLayoutManager->getElement( "private:resource/toolbar/oleobjectbar" );
+ }
+ if(xElement.is())
+ {
+ uno::Reference< awt::XWindow > xWin( xElement->getRealInterface(), uno::UNO_QUERY_THROW );
+ VclPtr<vcl::Window> pWin = VCLUnoHelper::GetWindow( xWin );
+ ToolBox* pTextToolbox = dynamic_cast< ToolBox* >( pWin.get() );
+ if( pTextToolbox )
+ {
+ ToolBox::ImplToolItems::size_type nItemCount = pTextToolbox->GetItemCount();
+ for( ToolBox::ImplToolItems::size_type nItem = 0; nItem < nItemCount; ++nItem )
+ {
+ ToolBoxItemId nItemId = pTextToolbox->GetItemId( nItem );
+ const OUString& rCommand = pTextToolbox->GetItemCommand( nItemId );
+ if (rCommand == ".uno:StyleApply")
+ {
+ vcl::Window* pItemWin = pTextToolbox->GetItemWindow( nItemId );
+ if( pItemWin )
+ pItemWin->GrabFocus();
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ catch (const Exception&)
+ {
+ }
+ }
+ rReq.Done();
+ }
+ break;
+
+ case SID_MAIL_SENDDOCASMS:
+ case SID_MAIL_SENDDOCASOOO:
+ case SID_MAIL_SENDDOCASPDF:
+ case SID_MAIL_SENDDOC:
+ case SID_MAIL_SENDDOCASFORMAT:
+ {
+ SfxObjectShell* pDoc = GetObjectShell();
+ if (!pDoc)
+ break;
+ pDoc->QueryHiddenInformation(HiddenWarningFact::WhenSaving);
+ SfxMailModel aModel;
+ OUString aDocType;
+
+ const SfxStringItem* pMailRecipient = rReq.GetArg<SfxStringItem>(SID_MAIL_RECIPIENT);
+ if ( pMailRecipient )
+ {
+ OUString aRecipient( pMailRecipient->GetValue() );
+ OUString aMailToStr("mailto:");
+
+ if ( aRecipient.startsWith( aMailToStr ) )
+ aRecipient = aRecipient.copy( aMailToStr.getLength() );
+ aModel.AddToAddress( aRecipient );
+ }
+ const SfxStringItem* pMailDocType = rReq.GetArg<SfxStringItem>(SID_TYPE_NAME);
+ if ( pMailDocType )
+ aDocType = pMailDocType->GetValue();
+
+ uno::Reference < frame::XFrame > xFrame( rFrame.GetFrame().GetFrameInterface() );
+ SfxMailModel::SendMailResult eResult = SfxMailModel::SEND_MAIL_ERROR;
+
+ if ( nId == SID_MAIL_SENDDOC )
+ eResult = aModel.SaveAndSend( xFrame, OUString() );
+ else if ( nId == SID_MAIL_SENDDOCASPDF )
+ eResult = aModel.SaveAndSend( xFrame, "pdf_Portable_Document_Format");
+ else if ( nId == SID_MAIL_SENDDOCASMS )
+ {
+ aDocType = impl_searchFormatTypeForApp(xFrame, E_MS_DOC);
+ if (!aDocType.isEmpty())
+ eResult = aModel.SaveAndSend( xFrame, aDocType );
+ }
+ else if ( nId == SID_MAIL_SENDDOCASOOO )
+ {
+ aDocType = impl_searchFormatTypeForApp(xFrame, E_OOO_DOC);
+ if (!aDocType.isEmpty())
+ eResult = aModel.SaveAndSend( xFrame, aDocType );
+ }
+
+ if ( eResult == SfxMailModel::SEND_MAIL_ERROR )
+ {
+ weld::Window* pWin = SfxGetpApp()->GetTopWindow();
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(pWin,
+ VclMessageType::Info, VclButtonsType::Ok,
+ SfxResId(STR_ERROR_SEND_MAIL)));
+ xBox->run();
+ rReq.Ignore();
+ }
+ else
+ rReq.Done();
+ }
+ break;
+
+ case SID_BLUETOOTH_SENDDOC:
+ {
+ SfxBluetoothModel aModel;
+ SfxObjectShell* pDoc = GetObjectShell();
+ if (!pDoc)
+ break;
+ pDoc->QueryHiddenInformation(HiddenWarningFact::WhenSaving);
+ uno::Reference < frame::XFrame > xFrame( rFrame.GetFrame().GetFrameInterface() );
+ SfxMailModel::SendMailResult eResult = aModel.SaveAndSend( xFrame );
+ if( eResult == SfxMailModel::SEND_MAIL_ERROR )
+ {
+ weld::Window* pWin = SfxGetpApp()->GetTopWindow();
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(pWin,
+ VclMessageType::Info, VclButtonsType::Ok,
+ SfxResId(STR_ERROR_SEND_MAIL)));
+ xBox->run();
+ rReq.Ignore();
+ }
+ else
+ rReq.Done();
+ }
+ break;
+
+ // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ case SID_WEBHTML:
+ {
+ css::uno::Reference< lang::XMultiServiceFactory > xSMGR(::comphelper::getProcessServiceFactory(), css::uno::UNO_SET_THROW);
+ css::uno::Reference< uno::XComponentContext > xContext(::comphelper::getProcessComponentContext(), css::uno::UNO_SET_THROW);
+ css::uno::Reference< css::frame::XFrame > xFrame( rFrame.GetFrame().GetFrameInterface() );
+ css::uno::Reference< css::frame::XModel > xModel;
+
+ css::uno::Reference< css::frame::XModuleManager2 > xModuleManager( css::frame::ModuleManager::create(xContext) );
+
+ OUString aModule;
+ try
+ {
+ aModule = xModuleManager->identify( xFrame );
+ }
+ catch (const css::uno::RuntimeException&)
+ {
+ throw;
+ }
+ catch (const css::uno::Exception&)
+ {
+ }
+
+ if ( xFrame.is() )
+ {
+ css::uno::Reference< css::frame::XController > xController = xFrame->getController();
+ if ( xController.is() )
+ xModel = xController->getModel();
+ }
+
+ // We need at least a valid module name and model reference
+ css::uno::Reference< css::frame::XStorable > xStorable( xModel, css::uno::UNO_QUERY );
+ if ( xModel.is() && xStorable.is() )
+ {
+ OUString aFilterName;
+ OUString aTypeName( "generic_HTML" );
+ OUString aFileName;
+
+ OUString aLocation = xStorable->getLocation();
+ INetURLObject aFileObj( aLocation );
+
+ bool bPrivateProtocol = ( aFileObj.GetProtocol() == INetProtocol::PrivSoffice );
+ bool bHasLocation = !aLocation.isEmpty() && !bPrivateProtocol;
+
+ css::uno::Reference< css::container::XContainerQuery > xContainerQuery(
+ xSMGR->createInstance( "com.sun.star.document.FilterFactory" ),
+ css::uno::UNO_QUERY_THROW );
+
+ // Retrieve filter from type
+
+ sal_Int32 nFilterFlags = 0x00000002; // export
+ aFilterName = impl_retrieveFilterNameFromTypeAndModule( xContainerQuery, aTypeName, aModule, nFilterFlags );
+ if ( aFilterName.isEmpty() )
+ {
+ // Draw/Impress uses a different type. 2nd chance try to use alternative type name
+ aFilterName = impl_retrieveFilterNameFromTypeAndModule(
+ xContainerQuery, "graphic_HTML", aModule, nFilterFlags );
+ }
+
+ // No filter found => error
+ // No type and no location => error
+ if ( aFilterName.isEmpty() || aTypeName.isEmpty())
+ {
+ rReq.Done();
+ return;
+ }
+
+ // Use provided save file name. If empty determine file name
+ if ( !bHasLocation )
+ {
+ // Create a default file name with the correct extension
+ aFileName = "webpreview";
+ }
+ else
+ {
+ // Determine file name from model
+ INetURLObject aFObj( xStorable->getLocation() );
+ aFileName = aFObj.getName( INetURLObject::LAST_SEGMENT, true, INetURLObject::DecodeMechanism::NONE );
+ }
+
+ OSL_ASSERT( !aFilterName.isEmpty() );
+ OSL_ASSERT( !aFileName.isEmpty() );
+
+ // Creates a temporary directory to store our predefined file into it (for the
+ // flatpak case, create it in XDG_CACHE_HOME instead of /tmp for technical reasons,
+ // so that it can be accessed by the browser running outside the sandbox):
+ OUString * parent = nullptr;
+ if (flatpak::isFlatpak() && !flatpak::createTemporaryHtmlDirectory(&parent))
+ {
+ SAL_WARN("sfx.view", "cannot create Flatpak html temp dir");
+ }
+
+ INetURLObject aFilePathObj( ::utl::CreateTempURL(parent, true) );
+ aFilePathObj.insertName( aFileName );
+ aFilePathObj.setExtension( u"htm" );
+
+ OUString aFileURL = aFilePathObj.GetMainURL( INetURLObject::DecodeMechanism::NONE );
+
+ css::uno::Sequence< css::beans::PropertyValue > aArgs{
+ comphelper::makePropertyValue("FilterName", aFilterName)
+ };
+
+ // Store document in the html format
+ try
+ {
+ xStorable->storeToURL( aFileURL, aArgs );
+ }
+ catch (const io::IOException&)
+ {
+ rReq.Done();
+ return;
+ }
+
+ sfx2::openUriExternally(aFileURL, true, rReq.GetFrameWeld());
+ rReq.Done(true);
+ break;
+ }
+ else
+ {
+ rReq.Done();
+ return;
+ }
+ }
+ }
+}
+
+
+void SfxViewShell::GetState_Impl( SfxItemSet &rSet )
+{
+
+ SfxWhichIter aIter( rSet );
+ SfxObjectShell *pSh = GetViewFrame().GetObjectShell();
+ for ( sal_uInt16 nSID = aIter.FirstWhich(); nSID; nSID = aIter.NextWhich() )
+ {
+ switch ( nSID )
+ {
+
+ case SID_BLUETOOTH_SENDDOC:
+ case SID_MAIL_SENDDOC:
+ case SID_MAIL_SENDDOCASFORMAT:
+ case SID_MAIL_SENDDOCASMS:
+ case SID_MAIL_SENDDOCASOOO:
+ case SID_MAIL_SENDDOCASPDF:
+ {
+#if HAVE_FEATURE_MACOSX_SANDBOX
+ rSet.DisableItem(nSID);
+#endif
+ if (pSh && pSh->isExportLocked())
+ rSet.DisableItem(nSID);
+ break;
+ }
+ case SID_WEBHTML:
+ {
+ if (pSh && pSh->isExportLocked())
+ rSet.DisableItem(nSID);
+ break;
+ }
+ // Printer functions
+ case SID_PRINTDOC:
+ case SID_PRINTDOCDIRECT:
+ case SID_SETUPPRINTER:
+ case SID_PRINTER_NAME:
+ {
+ if (Application::GetSettings().GetMiscSettings().GetDisablePrinting()
+ || (pSh && pSh->isPrintLocked()))
+ {
+ rSet.DisableItem(nSID);
+ break;
+ }
+
+ SfxPrinter *pPrinter = GetPrinter();
+
+ if ( SID_PRINTDOCDIRECT == nSID )
+ {
+ OUString aPrinterName;
+ if ( pPrinter != nullptr )
+ aPrinterName = pPrinter->GetName();
+ else
+ {
+ // tdf#109149 don't poll the Default Printer Name on every query.
+ // We are queried on every change, so on every
+ // keystroke, and we are only using this to fill in the
+ // printername inside the label of "Print Directly (printer-name)"
+ // On Printer::GetDefaultPrinterName() is implemented with
+ // GetDefaultPrinter so don't call this excessively. 5 mins
+ // seems a reasonable refresh time.
+ std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now();
+ std::chrono::minutes five_mins(5);
+ if (now > pImpl->m_nDefaultPrinterNameFetchTime + five_mins)
+ {
+ pImpl->m_sDefaultPrinterName = Printer::GetDefaultPrinterName();
+ pImpl->m_nDefaultPrinterNameFetchTime = now;
+ }
+ aPrinterName = pImpl->m_sDefaultPrinterName;
+ }
+ if ( !aPrinterName.isEmpty() )
+ {
+ uno::Reference < frame::XFrame > xFrame( rFrame.GetFrame().GetFrameInterface() );
+
+ auto aProperties = vcl::CommandInfoProvider::GetCommandProperties(".uno:PrintDefault",
+ vcl::CommandInfoProvider::GetModuleIdentifier(xFrame));
+ OUString val = vcl::CommandInfoProvider::GetLabelForCommand(aProperties) +
+ " (" + aPrinterName + ")";
+
+ rSet.Put( SfxStringItem( SID_PRINTDOCDIRECT, val ) );
+ }
+ }
+ break;
+ }
+ case SID_STYLE_FAMILY :
+ {
+ rSet.Put( SfxUInt16Item( SID_STYLE_FAMILY, pImpl->m_nFamily ) );
+ break;
+ }
+ }
+ }
+}
+
+void SfxViewShell::SetZoomFactor( const Fraction &rZoomX,
+ const Fraction &rZoomY )
+{
+ DBG_ASSERT( GetWindow(), "no window" );
+ MapMode aMap( GetWindow()->GetMapMode() );
+ aMap.SetScaleX( rZoomX );
+ aMap.SetScaleY( rZoomY );
+ GetWindow()->SetMapMode( aMap );
+}
+
+ErrCode SfxViewShell::DoVerb(sal_Int32 /*nVerb*/)
+
+/* [Description]
+
+ Virtual Method used to perform a Verb on a selected Object.
+ Since this Object is only known by the derived classes, they must override
+ DoVerb.
+*/
+
+{
+ return ERRCODE_SO_NOVERBS;
+}
+
+void SfxViewShell::OutplaceActivated( bool bActive )
+{
+ if ( !bActive )
+ GetFrame()->GetFrame().Appear();
+}
+
+void SfxViewShell::UIActivating( SfxInPlaceClient* /*pClient*/ )
+{
+ uno::Reference < frame::XFrame > xOwnFrame( rFrame.GetFrame().GetFrameInterface() );
+ uno::Reference < frame::XFramesSupplier > xParentFrame = xOwnFrame->getCreator();
+ if ( xParentFrame.is() )
+ xParentFrame->setActiveFrame( xOwnFrame );
+
+ rFrame.GetBindings().HidePopups();
+ rFrame.GetDispatcher()->Update_Impl( true );
+}
+
+void SfxViewShell::UIDeactivated( SfxInPlaceClient* /*pClient*/ )
+{
+ if ( !rFrame.GetFrame().IsClosing_Impl() || SfxViewFrame::Current() != &rFrame )
+ rFrame.GetDispatcher()->Update_Impl( true );
+ rFrame.GetBindings().HidePopups(false);
+
+ rFrame.GetBindings().InvalidateAll(true);
+}
+
+SfxInPlaceClient* SfxViewShell::FindIPClient
+(
+ const uno::Reference < embed::XEmbeddedObject >& xObj,
+ vcl::Window* pObjParentWin
+) const
+{
+ std::vector< SfxInPlaceClient* >& rClients = pImpl->GetIPClients_Impl();
+ if ( rClients.empty() )
+ return nullptr;
+
+ if( !pObjParentWin )
+ pObjParentWin = GetWindow();
+ for (SfxInPlaceClient* pIPClient : rClients)
+ {
+ if ( pIPClient->GetObject() == xObj && pIPClient->GetEditWin() == pObjParentWin )
+ return pIPClient;
+ }
+
+ return nullptr;
+}
+
+
+SfxInPlaceClient* SfxViewShell::GetIPClient() const
+{
+ return GetUIActiveClient();
+}
+
+
+SfxInPlaceClient* SfxViewShell::GetUIActiveIPClient_Impl() const
+{
+ // this method is needed as long as SFX still manages the border space for ChildWindows (see SfxFrame::Resize)
+ std::vector< SfxInPlaceClient* >& rClients = pImpl->GetIPClients_Impl();
+ if ( rClients.empty() )
+ return nullptr;
+
+ for (SfxInPlaceClient* pIPClient : rClients)
+ {
+ if ( pIPClient->IsUIActive() )
+ return pIPClient;
+ }
+
+ return nullptr;
+}
+
+SfxInPlaceClient* SfxViewShell::GetUIActiveClient() const
+{
+ std::vector< SfxInPlaceClient* >& rClients = pImpl->GetIPClients_Impl();
+ if ( rClients.empty() )
+ return nullptr;
+
+ const bool bIsTiledRendering = comphelper::LibreOfficeKit::isActive();
+
+ for (SfxInPlaceClient* pIPClient : rClients)
+ {
+ if ( pIPClient->IsObjectUIActive() || ( bIsTiledRendering && pIPClient->IsObjectInPlaceActive() ) )
+ return pIPClient;
+ }
+
+ return nullptr;
+}
+
+
+void SfxViewShell::Activate( bool bMDI )
+{
+ if ( bMDI )
+ {
+ SfxObjectShell *pSh = GetViewFrame().GetObjectShell();
+ if (const auto xModel = pSh->GetModel())
+ xModel->setCurrentController(GetController());
+
+ SetCurrentDocument();
+ }
+}
+
+
+void SfxViewShell::Deactivate(bool /*bMDI*/)
+{
+}
+
+
+void SfxViewShell::Move()
+
+/* [Description]
+
+ This virtual Method is called when the window displayed in the
+ SfxViewShell gets a StarView-Move() notification.
+
+ This base implementation does not have to be called. .
+
+ [Note]
+
+ This Method can be used to cancel a selection, in order to catch the
+ mouse movement which is due to moving a window.
+
+ For now the notification does not work In-Place.
+*/
+
+{
+}
+
+
+void SfxViewShell::OuterResizePixel
+(
+ const Point& /*rToolOffset*/,// Upper left corner Tools in Frame-Window
+ const Size& /*rSize*/ // All available sizes.
+)
+
+/* [Description]
+
+ Override this Method to be able to react to the size-change of
+ the View. Thus the View is defined as the Edit window and also the
+ attached Tools are defined (for example the ruler).
+
+ The Edit window must not be changed either in size or position.
+
+ The Vis-Area of SfxObjectShell, its scale and position can be changed
+ here. The main use is to change the size of the Vis-Area.
+
+ If the Border is changed due to the new calculation then this has to be set
+ by <SfxViewShell::SetBorderPixel(const SvBorder&)>. The Positioning of Tools
+ is only allowed after the calling of 'SetBorderPixel'.
+
+ [Example]
+
+ void AppViewSh::OuterViewResizePixel( const Point &rOfs, const Size &rSz )
+ {
+ // Calculate Tool position and size externally, do not set!
+ // (due to the following Border calculation)
+ Point aHLinPos...; Size aHLinSz...;
+ ...
+
+ // Calculate and Set a Border of Tools which matches rSize.
+ SvBorder aBorder...
+ SetBorderPixel( aBorder ); // Allow Positioning from here on.
+
+ // Arrange Tools
+ pHLin->SetPosSizePixel( aHLinPos, aHLinSz );
+ ...
+ }
+
+ [Cross-reference]
+
+ <SfxViewShell::InnerResizePixel(const Point&,const Size& rSize)>
+*/
+
+{
+ SetBorderPixel( SvBorder() );
+}
+
+
+void SfxViewShell::InnerResizePixel
+(
+ const Point& /*rToolOffset*/,// Upper left corner Tools in Frame-Window
+ const Size& /*rSize*/, // All available sizes.
+ bool
+)
+
+/* [Description]
+
+ Override this Method to be able to react to the size-change of
+ the Edit window.
+
+ The Edit window must not be changed either in size or position.
+ Neither the Vis-Area of SfxObjectShell nor its scale or position are
+ allowed to be changed
+
+ If the Border is changed due to the new calculation then is has to be set
+ by <SfxViewShell::SetBorderPixel(const SvBorder&)>.
+ The Positioning of Tools is only allowed after the calling of
+ 'SetBorderPixel'.
+
+
+ [Note]
+
+ void AppViewSh::InnerViewResizePixel( const Point &rOfs, const Size &rSz )
+ {
+ // Calculate Tool position and size internally, do not set!
+ // (due to the following Border calculation)
+ Point aHLinPos...; Size aHLinSz...;
+ ...
+
+ // Calculate and Set a Border of Tools which matches rSize.
+ SvBorder aBorder...
+ SetBorderPixel( aBorder ); // Allow Positioning from here on.
+
+ // Arrange Tools
+ pHLin->SetPosSizePixel( aHLinPos, aHLinSz );
+ ...
+ }
+
+ [Cross-reference]
+
+ <SfxViewShell::OuterResizePixel(const Point&,const Size& rSize)>
+*/
+
+{
+ SetBorderPixel( SvBorder() );
+}
+
+void SfxViewShell::InvalidateBorder()
+{
+ GetViewFrame().InvalidateBorderImpl( this );
+ if (pImpl->m_pController.is())
+ {
+ pImpl->m_pController->BorderWidthsChanged_Impl();
+ }
+}
+
+void SfxViewShell::SetBorderPixel( const SvBorder &rBorder )
+{
+ GetViewFrame().SetBorderPixelImpl( this, rBorder );
+
+ // notify related controller that border size is changed
+ if (pImpl->m_pController.is())
+ {
+ pImpl->m_pController->BorderWidthsChanged_Impl();
+ }
+}
+
+const SvBorder& SfxViewShell::GetBorderPixel() const
+{
+ return GetViewFrame().GetBorderPixelImpl();
+}
+
+void SfxViewShell::SetWindow
+(
+ vcl::Window* pViewPort // For example Null pointer in the Destructor.
+)
+
+/* [Description]
+
+ With this method the SfxViewShell is set in the data window. This is
+ needed for the in-place container and for restoring the proper focus.
+
+ Even in-place-active the conversion of the ViewPort Windows is forbidden.
+*/
+
+{
+ if( pWindow == pViewPort )
+ return;
+
+ // Disconnect existing IP-Clients if possible
+ DisconnectAllClients();
+
+ // Switch View-Port
+ bool bHadFocus = pWindow && pWindow->HasChildPathFocus( true );
+ pWindow = pViewPort;
+
+ if( pWindow )
+ {
+ // Disable automatic GUI mirroring (right-to-left) for document windows
+ pWindow->EnableRTL( false );
+ }
+
+ if ( bHadFocus && pWindow )
+ pWindow->GrabFocus();
+ //TODO/CLEANUP
+ //Do we still need this Method?!
+ //SfxGetpApp()->GrabFocus( pWindow );
+}
+
+ViewShellDocId SfxViewShell::mnCurrentDocId(0);
+
+SfxViewShell::SfxViewShell
+(
+ SfxViewFrame& rViewFrame, /* <SfxViewFrame>, which will be
+ displayed in this View */
+ SfxViewShellFlags nFlags /* See <SfxViewShell-Flags> */
+)
+
+: SfxShell(this)
+, pImpl( new SfxViewShell_Impl(nFlags, SfxViewShell::mnCurrentDocId) )
+, rFrame(rViewFrame)
+, pWindow(nullptr)
+, bNoNewWindow( nFlags & SfxViewShellFlags::NO_NEWWINDOW )
+, mbPrinterSettingsModified(false)
+, maLOKLanguageTag(LANGUAGE_NONE)
+, maLOKLocale(LANGUAGE_NONE)
+, maLOKDeviceFormFactor(LOKDeviceFormFactor::UNKNOWN)
+, mbLOKAccessibilityEnabled(false)
+{
+ SetMargin( rViewFrame.GetMargin_Impl() );
+
+ SetPool( &rViewFrame.GetObjectShell()->GetPool() );
+ StartListening(*rViewFrame.GetObjectShell());
+
+ // Insert into list
+ std::vector<SfxViewShell*> &rViewArr = SfxGetpApp()->GetViewShells_Impl();
+ rViewArr.push_back(this);
+
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ maLOKLanguageTag = SfxLokHelper::getDefaultLanguage();
+ maLOKLocale = SfxLokHelper::getDefaultLanguage();
+
+ const auto [isTimezoneSet, aTimezone] = SfxLokHelper::getDefaultTimezone();
+ maLOKIsTimezoneSet = isTimezoneSet;
+ maLOKTimezone = aTimezone;
+
+ maLOKDeviceFormFactor = SfxLokHelper::getDeviceFormFactor();
+
+ vcl::Window* pFrameWin = rViewFrame.GetWindow().GetFrameWindow();
+ if (pFrameWin && !pFrameWin->GetLOKNotifier())
+ pFrameWin->SetLOKNotifier(this, true);
+ }
+}
+
+SfxViewShell::~SfxViewShell()
+{
+ // Remove from list
+ const SfxViewShell *pThis = this;
+ std::vector<SfxViewShell*> &rViewArr = SfxGetpApp()->GetViewShells_Impl();
+ auto it = std::find( rViewArr.begin(), rViewArr.end(), pThis );
+ rViewArr.erase( it );
+
+ if ( pImpl->xClipboardListener.is() )
+ {
+ pImpl->xClipboardListener->DisconnectViewShell();
+ pImpl->xClipboardListener = nullptr;
+ }
+
+ if (pImpl->m_pController.is())
+ {
+ pImpl->m_pController->ReleaseShell_Impl();
+ pImpl->m_pController.clear();
+ }
+
+ vcl::Window* pFrameWin = GetViewFrame().GetWindow().GetFrameWindow();
+ if (pFrameWin && pFrameWin->GetLOKNotifier() == this)
+ pFrameWin->ReleaseLOKNotifier();
+}
+
+OUString SfxViewShell::getA11yFocusedParagraph() const
+{
+ const LOKDocumentFocusListener& rDocFocusListener = GetLOKDocumentFocusListener();
+ return rDocFocusListener.getFocusedParagraph();
+}
+
+int SfxViewShell::getA11yCaretPosition() const
+{
+ const LOKDocumentFocusListener& rDocFocusListener = GetLOKDocumentFocusListener();
+ return rDocFocusListener.getCaretPosition();
+}
+
+bool SfxViewShell::PrepareClose
+(
+ bool bUI // TRUE: Allow Dialog and so on, FALSE: silent-mode
+)
+{
+ if (GetViewFrame().GetWindow().GetLOKNotifier() == this)
+ GetViewFrame().GetWindow().ReleaseLOKNotifier();
+
+ SfxPrinter *pPrinter = GetPrinter();
+ if ( pPrinter && pPrinter->IsPrinting() )
+ {
+ if ( bUI )
+ {
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(GetViewFrame().GetFrameWeld(),
+ VclMessageType::Info, VclButtonsType::Ok,
+ SfxResId(STR_CANT_CLOSE)));
+ xBox->run();
+ }
+
+ return false;
+ }
+
+ if( GetViewFrame().IsInModalMode() )
+ return false;
+
+ if( bUI && GetViewFrame().GetDispatcher()->IsLocked() )
+ return false;
+
+ return true;
+}
+
+
+SfxViewShell* SfxViewShell::Current()
+{
+ SfxViewFrame *pCurrent = SfxViewFrame::Current();
+ return pCurrent ? pCurrent->GetViewShell() : nullptr;
+}
+
+
+SfxViewShell* SfxViewShell::Get( const Reference< XController>& i_rController )
+{
+ if ( !i_rController.is() )
+ return nullptr;
+
+ for ( SfxViewShell* pViewShell = SfxViewShell::GetFirst( false );
+ pViewShell;
+ pViewShell = SfxViewShell::GetNext( *pViewShell, false )
+ )
+ {
+ if ( pViewShell->GetController() == i_rController )
+ return pViewShell;
+ }
+ return nullptr;
+}
+
+
+SdrView* SfxViewShell::GetDrawView() const
+
+/* [Description]
+
+ This virtual Method has to be overloaded by the sub classes, to be able
+ make the Property-Editor available.
+
+ The default implementation does always return zero.
+*/
+
+{
+ return nullptr;
+}
+
+
+OUString SfxViewShell::GetSelectionText
+(
+ bool /*bCompleteWords*/, /* FALSE (default)
+ Only the actual selected text is returned.
+
+ TRUE
+ The selected text is expanded so that only
+ whole words are returned. As word separators
+ these are used: white spaces and punctuation
+ ".,;" and single and double quotes.
+ */
+ bool /*bOnlyASample*/ /* used by some dialogs to avoid constructing monster strings e.g. in calc */
+)
+
+/* [Description]
+
+ Override this Method to return a text that
+ is included in the current selection. This is for example used when
+ sending emails.
+
+ When called with "CompleteWords == TRUE", it is for example sufficient
+ with having the Cursor positioned somewhere within a URL in-order
+ to have the entire URL returned.
+*/
+
+{
+ return OUString();
+}
+
+
+bool SfxViewShell::HasSelection( bool ) const
+
+/* [Description]
+
+ With this virtual Method can a for example a Dialog be queried, to
+ check if something is selected in the current view. If the Parameter
+ is <BOOL> TRUE then it is checked whether some text is selected.
+*/
+
+{
+ return false;
+}
+
+void SfxViewShell::AddSubShell( SfxShell& rShell )
+{
+ pImpl->aArr.push_back(&rShell);
+ SfxDispatcher *pDisp = rFrame.GetDispatcher();
+ if ( pDisp->IsActive(*this) )
+ {
+ pDisp->Push(rShell);
+ pDisp->Flush();
+ }
+}
+
+void SfxViewShell::RemoveSubShell( SfxShell* pShell )
+{
+ SfxDispatcher *pDisp = rFrame.GetDispatcher();
+ if ( !pShell )
+ {
+ size_t nCount = pImpl->aArr.size();
+ if ( pDisp->IsActive(*this) )
+ {
+ for(size_t n = nCount; n > 0; --n)
+ pDisp->Pop(*pImpl->aArr[n - 1]);
+ pDisp->Flush();
+ }
+ pImpl->aArr.clear();
+ }
+ else
+ {
+ SfxShellArr_Impl::iterator i = std::find(pImpl->aArr.begin(), pImpl->aArr.end(), pShell);
+ if(i != pImpl->aArr.end())
+ {
+ pImpl->aArr.erase(i);
+ if(pDisp->IsActive(*this))
+ {
+ pDisp->RemoveShell_Impl(*pShell);
+ pDisp->Flush();
+ }
+ }
+ }
+}
+
+SfxShell* SfxViewShell::GetSubShell( sal_uInt16 nNo )
+{
+ sal_uInt16 nCount = pImpl->aArr.size();
+ if(nNo < nCount)
+ return pImpl->aArr[nCount - nNo - 1];
+ return nullptr;
+}
+
+void SfxViewShell::PushSubShells_Impl( bool bPush )
+{
+ SfxDispatcher *pDisp = rFrame.GetDispatcher();
+ if ( bPush )
+ {
+ for (auto const& elem : pImpl->aArr)
+ pDisp->Push(*elem);
+ }
+ else if(!pImpl->aArr.empty())
+ {
+ SfxShell& rPopUntil = *pImpl->aArr[0];
+ if ( pDisp->GetShellLevel( rPopUntil ) != USHRT_MAX )
+ pDisp->Pop( rPopUntil, SfxDispatcherPopFlags::POP_UNTIL );
+ }
+
+ pDisp->Flush();
+}
+
+
+void SfxViewShell::WriteUserData( OUString&, bool )
+{
+}
+
+
+void SfxViewShell::ReadUserData(const OUString&, bool )
+{
+}
+
+void SfxViewShell::ReadUserDataSequence ( const uno::Sequence < beans::PropertyValue >& )
+{
+}
+
+void SfxViewShell::WriteUserDataSequence ( uno::Sequence < beans::PropertyValue >& )
+{
+}
+
+
+// returns the first shell of spec. type viewing the specified doc.
+SfxViewShell* SfxViewShell::GetFirst
+(
+ bool bOnlyVisible,
+ const std::function< bool ( const SfxViewShell* ) >& isViewShell
+)
+{
+ // search for a SfxViewShell of the specified type
+ std::vector<SfxViewShell*> &rShells = SfxGetpApp()->GetViewShells_Impl();
+ for (SfxViewShell* pShell : rShells)
+ {
+ if ( pShell )
+ {
+ // This code used to check that the frame exists in the other list,
+ // because of https://bz.apache.org/ooo/show_bug.cgi?id=62084, with the explanation:
+ // sometimes dangling SfxViewShells exist that point to a dead SfxViewFrame
+ // these ViewShells shouldn't be accessible anymore
+ // a destroyed ViewFrame is not in the ViewFrame array anymore, so checking this array helps
+ // That doesn't seem to be needed anymore, but keep an assert, just in case.
+ assert(std::find(SfxGetpApp()->GetViewFrames_Impl().begin(), SfxGetpApp()->GetViewFrames_Impl().end(),
+ &pShell->GetViewFrame()) != SfxGetpApp()->GetViewFrames_Impl().end());
+ if ( ( !bOnlyVisible || pShell->GetViewFrame().IsVisible() ) && (!isViewShell || isViewShell(pShell)))
+ return pShell;
+ }
+ }
+
+ return nullptr;
+}
+
+// returns the next shell of spec. type viewing the specified doc.
+SfxViewShell* SfxViewShell::GetNext
+(
+ const SfxViewShell& rPrev,
+ bool bOnlyVisible,
+ const std::function<bool ( const SfxViewShell* )>& isViewShell
+)
+{
+ std::vector<SfxViewShell*> &rShells = SfxGetpApp()->GetViewShells_Impl();
+ size_t nPos;
+ for ( nPos = 0; nPos < rShells.size(); ++nPos )
+ if ( rShells[nPos] == &rPrev )
+ break;
+
+ for ( ++nPos; nPos < rShells.size(); ++nPos )
+ {
+ SfxViewShell *pShell = rShells[nPos];
+ if ( pShell )
+ {
+ assert(std::find(SfxGetpApp()->GetViewFrames_Impl().begin(), SfxGetpApp()->GetViewFrames_Impl().end(),
+ &pShell->GetViewFrame()) != SfxGetpApp()->GetViewFrames_Impl().end());
+ if ( ( !bOnlyVisible || pShell->GetViewFrame().IsVisible() ) && (!isViewShell || isViewShell(pShell)) )
+ return pShell;
+ }
+ }
+
+ return nullptr;
+}
+
+
+void SfxViewShell::Notify( SfxBroadcaster& rBC,
+ const SfxHint& rHint )
+{
+ if (rHint.GetId() != SfxHintId::ThisIsAnSfxEventHint ||
+ static_cast<const SfxEventHint&>(rHint).GetEventId() != SfxEventHintId::LoadFinished)
+ {
+ return;
+ }
+
+ if ( !GetController().is() )
+ return;
+
+ // avoid access to dangling ViewShells
+ auto &rFrames = SfxGetpApp()->GetViewFrames_Impl();
+ for (SfxViewFrame* frame : rFrames)
+ {
+ if ( frame == &GetViewFrame() && &rBC == GetObjectShell() )
+ {
+ SfxItemSet& rSet = GetObjectShell()->GetMedium()->GetItemSet();
+ const SfxUnoAnyItem* pItem = rSet.GetItem(SID_VIEW_DATA, false);
+ if ( pItem )
+ {
+ pImpl->m_pController->restoreViewData( pItem->GetValue() );
+ rSet.ClearItem( SID_VIEW_DATA );
+ }
+ break;
+ }
+ }
+}
+
+bool SfxViewShell::ExecKey_Impl(const KeyEvent& aKey)
+{
+ bool setModuleConfig = false; // In case libreofficekit is active, we will re-set the module config class.
+ if (!pImpl->m_xAccExec)
+ {
+ pImpl->m_xAccExec = ::svt::AcceleratorExecute::createAcceleratorHelper();
+ pImpl->m_xAccExec->init(::comphelper::getProcessComponentContext(),
+ rFrame.GetFrame().GetFrameInterface());
+ setModuleConfig = true;
+ }
+
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ // Get the module name.
+ css::uno::Reference< css::uno::XComponentContext > xContext (::comphelper::getProcessComponentContext());
+ css::uno::Reference< css::frame::XModuleManager2 > xModuleManager(css::frame::ModuleManager::create(xContext));
+ OUString sModule = xModuleManager->identify(rFrame.GetFrame().GetFrameInterface());
+
+ // Get the language name.
+ OUString viewLang = GetLOKLanguageTag().getBcp47();
+
+ // Merge them & have a key.
+ OUString key = sModule + viewLang;
+
+ // Check it in configurations map. Create a configuration manager if there isn't one for the key.
+ std::unordered_map<OUString, css::uno::Reference<com::sun::star::ui::XAcceleratorConfiguration>>& acceleratorConfs = SfxApplication::Get()->GetAcceleratorConfs_Impl();
+ if (acceleratorConfs.find(key) == acceleratorConfs.end())
+ {
+ // Create a new configuration manager for the module.
+
+ OUString actualLang = officecfg::Setup::L10N::ooLocale::get();
+
+ std::shared_ptr<comphelper::ConfigurationChanges> batch(comphelper::ConfigurationChanges::create());
+ officecfg::Setup::L10N::ooLocale::set(viewLang, batch);
+ batch->commit();
+
+ // We have set the language. Time to create the config manager.
+ acceleratorConfs[key] = svt::AcceleratorExecute::lok_createNewAcceleratorConfiguration(::comphelper::getProcessComponentContext(), sModule);
+
+ std::shared_ptr<comphelper::ConfigurationChanges> batch2(comphelper::ConfigurationChanges::create());
+ officecfg::Setup::L10N::ooLocale::set(actualLang, batch2);
+ batch2->commit();
+ }
+
+ if (setModuleConfig)
+ pImpl->m_xAccExec->lok_setModuleConfig(acceleratorConfs[key]);
+ }
+
+ return pImpl->m_xAccExec->execute(aKey.GetKeyCode());
+}
+
+void SfxViewShell::setLibreOfficeKitViewCallback(SfxLokCallbackInterface* pCallback)
+{
+ pImpl->m_pLibreOfficeKitViewCallback = pCallback;
+
+ afterCallbackRegistered();
+
+ if (!pImpl->m_pLibreOfficeKitViewCallback)
+ return;
+
+ // Ask other views to tell us about their cursors.
+ SfxViewShell* pViewShell = SfxViewShell::GetFirst();
+ while (pViewShell)
+ {
+ if (pViewShell->GetDocId() == GetDocId())
+ pViewShell->NotifyCursor(this);
+ pViewShell = SfxViewShell::GetNext(*pViewShell);
+ }
+}
+
+SfxLokCallbackInterface* SfxViewShell::getLibreOfficeKitViewCallback() const
+{
+ return pImpl->m_pLibreOfficeKitViewCallback;
+}
+
+void SfxViewShell::dumpLibreOfficeKitViewState(rtl::OStringBuffer &rState)
+{
+ if (pImpl->m_pLibreOfficeKitViewCallback)
+ pImpl->m_pLibreOfficeKitViewCallback->dumpState(rState);
+}
+
+static bool ignoreLibreOfficeKitViewCallback(int nType, const SfxViewShell_Impl* pImpl)
+{
+ if (!comphelper::LibreOfficeKit::isActive())
+ return true;
+
+ if (comphelper::LibreOfficeKit::isTiledPainting())
+ {
+ switch (nType)
+ {
+ case LOK_CALLBACK_FORM_FIELD_BUTTON:
+ case LOK_CALLBACK_TEXT_SELECTION:
+ case LOK_CALLBACK_COMMENT:
+ break;
+ default:
+ // Reject e.g. invalidate during paint.
+ return true;
+ }
+ }
+
+ if (pImpl->m_bTiledSearching)
+ {
+ switch (nType)
+ {
+ case LOK_CALLBACK_TEXT_SELECTION:
+ case LOK_CALLBACK_TEXT_VIEW_SELECTION:
+ case LOK_CALLBACK_TEXT_SELECTION_START:
+ case LOK_CALLBACK_TEXT_SELECTION_END:
+ case LOK_CALLBACK_GRAPHIC_SELECTION:
+ case LOK_CALLBACK_GRAPHIC_VIEW_SELECTION:
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void SfxViewShell::libreOfficeKitViewInvalidateTilesCallback(const tools::Rectangle* pRect, int nPart, int nMode) const
+{
+ if (ignoreLibreOfficeKitViewCallback(LOK_CALLBACK_INVALIDATE_TILES, pImpl.get()))
+ return;
+ if (pImpl->m_pLibreOfficeKitViewCallback)
+ pImpl->m_pLibreOfficeKitViewCallback->libreOfficeKitViewInvalidateTilesCallback(pRect, nPart, nMode);
+ else
+ SAL_INFO(
+ "sfx.view",
+ "SfxViewShell::libreOfficeKitViewInvalidateTilesCallback no callback set!");
+}
+
+void SfxViewShell::libreOfficeKitViewCallbackWithViewId(int nType, const OString& pPayload, int nViewId) const
+{
+ if (ignoreLibreOfficeKitViewCallback(nType, pImpl.get()))
+ return;
+ if (pImpl->m_pLibreOfficeKitViewCallback)
+ pImpl->m_pLibreOfficeKitViewCallback->libreOfficeKitViewCallbackWithViewId(nType, pPayload, nViewId);
+ else
+ SAL_INFO(
+ "sfx.view",
+ "SfxViewShell::libreOfficeKitViewCallbackWithViewId no callback set! Dropped payload of type "
+ << lokCallbackTypeToString(nType) << ": [" << pPayload << ']');
+}
+
+void SfxViewShell::libreOfficeKitViewCallback(int nType, const OString& pPayload) const
+{
+ if (ignoreLibreOfficeKitViewCallback(nType, pImpl.get()))
+ return;
+ if (pImpl->m_pLibreOfficeKitViewCallback)
+ pImpl->m_pLibreOfficeKitViewCallback->libreOfficeKitViewCallback(nType, pPayload);
+ else
+ SAL_INFO(
+ "sfx.view",
+ "SfxViewShell::libreOfficeKitViewCallback no callback set! Dropped payload of type "
+ << lokCallbackTypeToString(nType) << ": [" << pPayload << ']');
+}
+
+void SfxViewShell::libreOfficeKitViewUpdatedCallback(int nType) const
+{
+ if (ignoreLibreOfficeKitViewCallback(nType, pImpl.get()))
+ return;
+ if (pImpl->m_pLibreOfficeKitViewCallback)
+ pImpl->m_pLibreOfficeKitViewCallback->libreOfficeKitViewUpdatedCallback(nType);
+ else
+ SAL_INFO(
+ "sfx.view",
+ "SfxViewShell::libreOfficeKitViewUpdatedCallback no callback set! Dropped payload of type "
+ << lokCallbackTypeToString(nType));
+}
+
+void SfxViewShell::libreOfficeKitViewUpdatedCallbackPerViewId(int nType, int nViewId, int nSourceViewId) const
+{
+ if (ignoreLibreOfficeKitViewCallback(nType, pImpl.get()))
+ return;
+ if (pImpl->m_pLibreOfficeKitViewCallback)
+ pImpl->m_pLibreOfficeKitViewCallback->libreOfficeKitViewUpdatedCallbackPerViewId(nType, nViewId, nSourceViewId);
+ else
+ SAL_INFO(
+ "sfx.view",
+ "SfxViewShell::libreOfficeKitViewUpdatedCallbackPerViewId no callback set! Dropped payload of type "
+ << lokCallbackTypeToString(nType));
+}
+
+void SfxViewShell::libreOfficeKitViewAddPendingInvalidateTiles()
+{
+ if (pImpl->m_pLibreOfficeKitViewCallback)
+ pImpl->m_pLibreOfficeKitViewCallback->libreOfficeKitViewAddPendingInvalidateTiles();
+ else
+ SAL_INFO(
+ "sfx.view",
+ "SfxViewShell::libreOfficeKitViewAddPendingInvalidateTiles no callback set!");
+}
+
+void SfxViewShell::afterCallbackRegistered()
+{
+ LOK_INFO("sfx.view", "SfxViewShell::afterCallbackRegistered invoked");
+ if (GetLOKAccessibilityState())
+ {
+ LOKDocumentFocusListener& rDocFocusListener = GetLOKDocumentFocusListener();
+ rDocFocusListener.notifyFocusedParagraphChanged();
+ }
+}
+
+void SfxViewShell::flushPendingLOKInvalidateTiles()
+{
+ // SfxViewShell itself does not delay any tile invalidations.
+}
+
+std::optional<OString> SfxViewShell::getLOKPayload(int nType, int /*nViewId*/) const
+{
+ // SfxViewShell itself currently doesn't handle any updated-payload types.
+ SAL_WARN("sfx.view", "SfxViewShell::getLOKPayload unhandled type " << lokCallbackTypeToString(nType));
+ abort();
+}
+
+vcl::Window* SfxViewShell::GetEditWindowForActiveOLEObj() const
+{
+ vcl::Window* pEditWin = nullptr;
+ SfxInPlaceClient* pIPClient = GetIPClient();
+ if (pIPClient)
+ {
+ pEditWin = pIPClient->GetEditWin();
+ }
+ return pEditWin;
+}
+
+::Color SfxViewShell::GetColorConfigColor(svtools::ColorConfigEntry) const
+{
+ SAL_WARN("sfx.view", "SfxViewShell::GetColorConfigColor not overridden!");
+ return {};
+}
+
+void SfxViewShell::SetLOKLanguageTag(const OUString& rBcp47LanguageTag)
+{
+ LanguageTag aTag(rBcp47LanguageTag, true);
+
+ css::uno::Sequence<OUString> inst(officecfg::Setup::Office::InstalledLocales::get()->getElementNames());
+ LanguageTag aFallbackTag = LanguageTag(getInstalledLocaleForSystemUILanguage(inst, /* bRequestInstallIfMissing */ false, rBcp47LanguageTag), true).makeFallback();
+
+ // If we want de-CH, and the de localisation is available, we don't want to use de-DE as then
+ // the magic in Translate::get() won't turn ess-zet into double s. Possibly other similar cases?
+ if (comphelper::LibreOfficeKit::isActive() && aTag.getLanguage() == aFallbackTag.getLanguage())
+ maLOKLanguageTag = aTag;
+ else
+ maLOKLanguageTag = aFallbackTag;
+}
+
+LOKDocumentFocusListener& SfxViewShell::GetLOKDocumentFocusListener()
+{
+ if (mpLOKDocumentFocusListener)
+ return *mpLOKDocumentFocusListener;
+
+ mpLOKDocumentFocusListener = new LOKDocumentFocusListener(this);
+ return *mpLOKDocumentFocusListener;
+}
+
+const LOKDocumentFocusListener& SfxViewShell::GetLOKDocumentFocusListener() const
+{
+ return const_cast<SfxViewShell*>(this)->GetLOKDocumentFocusListener();
+}
+
+void SfxViewShell::SetLOKAccessibilityState(bool bEnabled)
+{
+ if (bEnabled == mbLOKAccessibilityEnabled)
+ return;
+ mbLOKAccessibilityEnabled = bEnabled;
+
+ LOKDocumentFocusListener& rDocumentFocusListener = GetLOKDocumentFocusListener();
+
+ if (!pWindow)
+ return;
+
+ uno::Reference< accessibility::XAccessible > xAccessible =
+ pWindow->GetAccessible();
+
+ if (!xAccessible.is())
+ return;
+
+ if (mbLOKAccessibilityEnabled)
+ {
+ try
+ {
+ rDocumentFocusListener.attachRecursive(xAccessible);
+ }
+ catch (const uno::Exception&)
+ {
+ LOK_WARN("SetLOKAccessibilityState", "Exception caught processing LOKDocumentFocusListener::attachRecursive");
+ }
+ }
+ else
+ {
+ try
+ {
+ rDocumentFocusListener.detachRecursive(xAccessible, /*bForce*/ true);
+ }
+ catch (const uno::Exception&)
+ {
+ LOK_WARN("SetLOKAccessibilityState", "Exception caught processing LOKDocumentFocusListener::detachRecursive");
+ }
+ }
+}
+
+void SfxViewShell::SetLOKLocale(const OUString& rBcp47LanguageTag)
+{
+ maLOKLocale = LanguageTag(rBcp47LanguageTag, true).makeFallback();
+}
+
+void SfxViewShell::NotifyCursor(SfxViewShell* /*pViewShell*/) const
+{
+}
+
+void SfxViewShell::setTiledSearching(bool bTiledSearching)
+{
+ pImpl->m_bTiledSearching = bTiledSearching;
+}
+
+int SfxViewShell::getPart() const
+{
+ return 0;
+}
+
+int SfxViewShell::getEditMode() const
+{
+ return 0;
+}
+
+ViewShellId SfxViewShell::GetViewShellId() const
+{
+ return pImpl->m_nViewShellId;
+}
+
+void SfxViewShell::SetCurrentDocId(ViewShellDocId nId)
+{
+ mnCurrentDocId = nId;
+}
+
+ViewShellDocId SfxViewShell::GetDocId() const
+{
+ assert(pImpl->m_nDocId >= ViewShellDocId(0) && "m_nDocId should have been initialized, but it is invalid.");
+ return pImpl->m_nDocId;
+}
+
+void SfxViewShell::notifyInvalidation(tools::Rectangle const* pRect) const
+{
+ SfxLokHelper::notifyInvalidation(this, pRect);
+}
+
+void SfxViewShell::NotifyOtherViews(int nType, const OString& rKey, const OString& rPayload)
+{
+ SfxLokHelper::notifyOtherViews(this, nType, rKey, rPayload);
+}
+
+void SfxViewShell::NotifyOtherView(OutlinerViewShell* pOther, int nType, const OString& rKey, const OString& rPayload)
+{
+ auto pOtherShell = dynamic_cast<SfxViewShell*>(pOther);
+ if (!pOtherShell)
+ return;
+
+ SfxLokHelper::notifyOtherView(this, pOtherShell, nType, rKey, rPayload);
+}
+
+void SfxViewShell::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SfxViewShell"));
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this);
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("id"), BAD_CAST(OString::number(static_cast<sal_Int32>(GetViewShellId())).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+bool SfxViewShell::KeyInput( const KeyEvent &rKeyEvent )
+
+/* [Description]
+
+ This Method executes the KeyEvent 'rKeyEvent' of the Keys (Accelerator)
+ configured either direct or indirect (for example by the Application)
+ in the SfxViewShell.
+
+ [Return value]
+
+ bool TRUE
+ The Key (Accelerator) is configured and the
+ associated Handler was called
+
+ FALSE
+ The Key (Accelerator) is not configured and
+ subsequently no Handler was called
+
+ [Cross-reference]
+
+ <SfxApplication::KeyInput(const KeyEvent&)>
+*/
+{
+ return ExecKey_Impl(rKeyEvent);
+}
+
+bool SfxViewShell::GlobalKeyInput_Impl( const KeyEvent &rKeyEvent )
+{
+ return ExecKey_Impl(rKeyEvent);
+}
+
+
+void SfxViewShell::ShowCursor( bool /*bOn*/ )
+
+/* [Description]
+
+ Subclasses must override this Method so that SFx can switch the
+ Cursor on and off, for example while a <SfxProgress> is running.
+*/
+
+{
+}
+
+
+void SfxViewShell::ResetAllClients_Impl( SfxInPlaceClient const *pIP )
+{
+
+ std::vector< SfxInPlaceClient* >& rClients = pImpl->GetIPClients_Impl();
+ if ( rClients.empty() )
+ return;
+
+ for (SfxInPlaceClient* pIPClient : rClients)
+ {
+ if( pIPClient != pIP )
+ pIPClient->ResetObject();
+ }
+}
+
+
+void SfxViewShell::DisconnectAllClients()
+{
+ std::vector< SfxInPlaceClient* >& rClients = pImpl->GetIPClients_Impl();
+ if ( rClients.empty() )
+ return;
+
+ for ( size_t n = 0; n < rClients.size(); )
+ // clients will remove themselves from the list
+ delete rClients.at( n );
+}
+
+
+void SfxViewShell::QueryObjAreaPixel( tools::Rectangle& ) const
+{
+}
+
+
+void SfxViewShell::VisAreaChanged()
+{
+ std::vector< SfxInPlaceClient* >& rClients = pImpl->GetIPClients_Impl();
+ if ( rClients.empty() )
+ return;
+
+ for (SfxInPlaceClient* pIPClient : rClients)
+ {
+ if ( pIPClient->IsObjectInPlaceActive() )
+ // client is active, notify client that the VisArea might have changed
+ pIPClient->VisAreaChanged();
+ }
+}
+
+
+void SfxViewShell::CheckIPClient_Impl(
+ SfxInPlaceClient const *const pIPClient, const tools::Rectangle& rVisArea)
+{
+ if ( GetObjectShell()->IsInClose() )
+ return;
+
+ bool bAlwaysActive =
+ ( ( pIPClient->GetObjectMiscStatus() & embed::EmbedMisc::EMBED_ACTIVATEIMMEDIATELY ) != 0 );
+ bool bActiveWhenVisible =
+ ( pIPClient->GetObjectMiscStatus() & embed::EmbedMisc::MS_EMBED_ACTIVATEWHENVISIBLE ) != 0;
+
+ // this method is called when a client is created
+ if (pIPClient->IsObjectInPlaceActive())
+ return;
+
+ // object in client is currently not active
+ // check if the object wants to be activated always or when it becomes at least partially visible
+ // TODO/LATER: maybe we should use the scaled area instead of the ObjArea?!
+ if (bAlwaysActive || (bActiveWhenVisible && rVisArea.Overlaps(pIPClient->GetObjArea())))
+ {
+ try
+ {
+ pIPClient->GetObject()->changeState( embed::EmbedStates::INPLACE_ACTIVE );
+ }
+ catch (const uno::Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("sfx.view", "SfxViewShell::CheckIPClient_Impl");
+ }
+ }
+}
+
+SfxObjectShell* SfxViewShell::GetObjectShell()
+{
+ return rFrame.GetObjectShell();
+}
+
+Reference< XModel > SfxViewShell::GetCurrentDocument() const
+{
+ Reference< XModel > xDocument;
+
+ const SfxObjectShell* pDocShell( const_cast< SfxViewShell* >( this )->GetObjectShell() );
+ OSL_ENSURE( pDocShell, "SfxViewFrame::GetCurrentDocument: no DocShell!?" );
+ if ( pDocShell )
+ xDocument = pDocShell->GetModel();
+ return xDocument;
+}
+
+
+void SfxViewShell::SetCurrentDocument() const
+{
+ uno::Reference< frame::XModel > xDocument( GetCurrentDocument() );
+ if ( xDocument.is() )
+ SfxObjectShell::SetCurrentComponent( xDocument );
+}
+
+
+const Size& SfxViewShell::GetMargin() const
+{
+ return pImpl->aMargin;
+}
+
+
+void SfxViewShell::SetMargin( const Size& rSize )
+{
+ // the default margin was verified using www.apple.com !!
+ Size aMargin = rSize;
+ if ( aMargin.Width() == -1 )
+ aMargin.setWidth( DEFAULT_MARGIN_WIDTH );
+ if ( aMargin.Height() == -1 )
+ aMargin.setHeight( DEFAULT_MARGIN_HEIGHT );
+
+ if ( aMargin != pImpl->aMargin )
+ {
+ pImpl->aMargin = aMargin;
+ MarginChanged();
+ }
+}
+
+void SfxViewShell::MarginChanged()
+{
+}
+
+void SfxViewShell::JumpToMark( const OUString& rMark )
+{
+ SfxStringItem aMarkItem( SID_JUMPTOMARK, rMark );
+ GetViewFrame().GetDispatcher()->ExecuteList(
+ SID_JUMPTOMARK,
+ SfxCallMode::SYNCHRON|SfxCallMode::RECORD,
+ { &aMarkItem });
+}
+
+void SfxViewShell::SetController( SfxBaseController* pController )
+{
+ pImpl->m_pController = pController;
+
+ // there should be no old listener, but if there is one, it should be disconnected
+ if ( pImpl->xClipboardListener.is() )
+ pImpl->xClipboardListener->DisconnectViewShell();
+
+ pImpl->xClipboardListener = new SfxClipboardChangeListener( this, GetClipboardNotifier() );
+}
+
+Reference < XController > SfxViewShell::GetController() const
+{
+ return pImpl->m_pController;
+}
+
+SfxBaseController* SfxViewShell::GetBaseController_Impl() const
+{
+ return pImpl->m_pController.get();
+}
+
+void SfxViewShell::AddContextMenuInterceptor_Impl( const uno::Reference< ui::XContextMenuInterceptor >& xInterceptor )
+{
+ std::unique_lock g(pImpl->aMutex);
+ pImpl->aInterceptorContainer.addInterface( g, xInterceptor );
+}
+
+void SfxViewShell::RemoveContextMenuInterceptor_Impl( const uno::Reference< ui::XContextMenuInterceptor >& xInterceptor )
+{
+ std::unique_lock g(pImpl->aMutex);
+ pImpl->aInterceptorContainer.removeInterface( g, xInterceptor );
+}
+
+bool SfxViewShell::TryContextMenuInterception(const rtl::Reference<VCLXPopupMenu>& rIn,
+ const OUString& rMenuIdentifier,
+ rtl::Reference<VCLXPopupMenu>& rOut,
+ ui::ContextMenuExecuteEvent aEvent)
+{
+ rOut.clear();
+ bool bModified = false;
+
+ // create container from menu
+ aEvent.ActionTriggerContainer = ::framework::ActionTriggerHelper::CreateActionTriggerContainerFromMenu(
+ rIn, &rMenuIdentifier);
+
+ // get selection from controller
+ aEvent.Selection.set( GetController(), uno::UNO_QUERY );
+
+ // call interceptors
+ std::unique_lock g(pImpl->aMutex);
+ std::vector<uno::Reference< ui::XContextMenuInterceptor>> aInterceptors =
+ pImpl->aInterceptorContainer.getElements(g);
+ g.unlock();
+ for (const auto & rListener : aInterceptors )
+ {
+ try
+ {
+ ui::ContextMenuInterceptorAction eAction;
+ {
+ SolarMutexReleaser rel;
+ eAction = rListener->notifyContextMenuExecute( aEvent );
+ }
+ switch ( eAction )
+ {
+ case ui::ContextMenuInterceptorAction_CANCELLED :
+ // interceptor does not want execution
+ return false;
+ case ui::ContextMenuInterceptorAction_EXECUTE_MODIFIED :
+ // interceptor wants his modified menu to be executed
+ bModified = true;
+ break;
+ case ui::ContextMenuInterceptorAction_CONTINUE_MODIFIED :
+ // interceptor has modified menu, but allows for calling other interceptors
+ bModified = true;
+ continue;
+ case ui::ContextMenuInterceptorAction_IGNORED :
+ // interceptor is indifferent
+ continue;
+ default:
+ OSL_FAIL("Wrong return value of ContextMenuInterceptor!");
+ continue;
+ }
+ }
+ catch (...)
+ {
+ g.lock();
+ pImpl->aInterceptorContainer.removeInterface(g, rListener);
+ g.unlock();
+ }
+
+ break;
+ }
+
+ if (bModified)
+ {
+ // container was modified, create a new menu out of it
+ rOut = new VCLXPopupMenu();
+ ::framework::ActionTriggerHelper::CreateMenuFromActionTriggerContainer(rOut, aEvent.ActionTriggerContainer);
+ }
+
+ return true;
+}
+
+bool SfxViewShell::TryContextMenuInterception(const rtl::Reference<VCLXPopupMenu>& rPopupMenu,
+ const OUString& rMenuIdentifier, css::ui::ContextMenuExecuteEvent aEvent)
+{
+ bool bModified = false;
+
+ // create container from menu
+ aEvent.ActionTriggerContainer = ::framework::ActionTriggerHelper::CreateActionTriggerContainerFromMenu(
+ rPopupMenu, &rMenuIdentifier);
+
+ // get selection from controller
+ aEvent.Selection = css::uno::Reference< css::view::XSelectionSupplier >( GetController(), css::uno::UNO_QUERY );
+
+ // call interceptors
+ std::unique_lock g(pImpl->aMutex);
+ std::vector<uno::Reference< ui::XContextMenuInterceptor>> aInterceptors =
+ pImpl->aInterceptorContainer.getElements(g);
+ g.unlock();
+ for (const auto & rListener : aInterceptors )
+ {
+ try
+ {
+ css::ui::ContextMenuInterceptorAction eAction;
+ {
+ SolarMutexReleaser rel;
+ eAction = rListener->notifyContextMenuExecute( aEvent );
+ }
+ switch ( eAction )
+ {
+ case css::ui::ContextMenuInterceptorAction_CANCELLED:
+ // interceptor does not want execution
+ return false;
+ case css::ui::ContextMenuInterceptorAction_EXECUTE_MODIFIED:
+ // interceptor wants his modified menu to be executed
+ bModified = true;
+ break;
+ case css::ui::ContextMenuInterceptorAction_CONTINUE_MODIFIED:
+ // interceptor has modified menu, but allows for calling other interceptors
+ bModified = true;
+ continue;
+ case css::ui::ContextMenuInterceptorAction_IGNORED:
+ // interceptor is indifferent
+ continue;
+ default:
+ SAL_WARN( "sfx.view", "Wrong return value of ContextMenuInterceptor!" );
+ continue;
+ }
+ }
+ catch (...)
+ {
+ g.lock();
+ pImpl->aInterceptorContainer.removeInterface(g, rListener);
+ g.unlock();
+ }
+
+ break;
+ }
+
+ if ( bModified )
+ {
+ rPopupMenu->clear();
+ ::framework::ActionTriggerHelper::CreateMenuFromActionTriggerContainer(rPopupMenu, aEvent.ActionTriggerContainer);
+ }
+
+ return true;
+}
+
+bool SfxViewShell::HandleNotifyEvent_Impl( NotifyEvent const & rEvent )
+{
+ if (pImpl->m_pController.is())
+ return pImpl->m_pController->HandleEvent_Impl( rEvent );
+ return false;
+}
+
+bool SfxViewShell::HasKeyListeners_Impl() const
+{
+ return (pImpl->m_pController.is())
+ && pImpl->m_pController->HasKeyListeners_Impl();
+}
+
+bool SfxViewShell::HasMouseClickListeners_Impl() const
+{
+ return (pImpl->m_pController.is())
+ && pImpl->m_pController->HasMouseClickListeners_Impl();
+}
+
+bool SfxViewShell::Escape()
+{
+ return GetViewFrame().GetBindings().Execute(SID_TERMINATE_INPLACEACTIVATION);
+}
+
+Reference< view::XRenderable > SfxViewShell::GetRenderable()
+{
+ Reference< view::XRenderable >xRender;
+ SfxObjectShell* pObj = GetObjectShell();
+ if( pObj )
+ {
+ Reference< frame::XModel > xModel( pObj->GetModel() );
+ if( xModel.is() )
+ xRender.set( xModel, UNO_QUERY );
+ }
+ return xRender;
+}
+
+void SfxViewShell::notifyWindow(vcl::LOKWindowId nDialogId, const OUString& rAction, const std::vector<vcl::LOKPayloadItem>& rPayload) const
+{
+ SfxLokHelper::notifyWindow(this, nDialogId, rAction, rPayload);
+}
+
+uno::Reference< datatransfer::clipboard::XClipboardNotifier > SfxViewShell::GetClipboardNotifier() const
+{
+ uno::Reference< datatransfer::clipboard::XClipboardNotifier > xClipboardNotifier;
+ xClipboardNotifier.set(GetViewFrame().GetWindow().GetClipboard(), uno::UNO_QUERY);
+ return xClipboardNotifier;
+}
+
+void SfxViewShell::AddRemoveClipboardListener( const uno::Reference < datatransfer::clipboard::XClipboardListener >& rClp, bool bAdd )
+{
+ try
+ {
+ uno::Reference< datatransfer::clipboard::XClipboard > xClipboard(GetViewFrame().GetWindow().GetClipboard());
+ if( xClipboard.is() )
+ {
+ uno::Reference< datatransfer::clipboard::XClipboardNotifier > xClpbrdNtfr( xClipboard, uno::UNO_QUERY );
+ if( xClpbrdNtfr.is() )
+ {
+ if( bAdd )
+ xClpbrdNtfr->addClipboardListener( rClp );
+ else
+ xClpbrdNtfr->removeClipboardListener( rClp );
+ }
+ }
+ }
+ catch (const uno::Exception&)
+ {
+ }
+}
+
+weld::Window* SfxViewShell::GetFrameWeld() const
+{
+ return pWindow ? pWindow->GetFrameWeld() : nullptr;
+}
+
+void SfxViewShell::setBlockedCommandList(const char* blockedCommandList)
+{
+ if(!mvLOKBlockedCommandList.empty())
+ return;
+
+ OUString BlockedListString(blockedCommandList, strlen(blockedCommandList), RTL_TEXTENCODING_UTF8);
+ OUString command = BlockedListString.getToken(0, ' ');
+ for (size_t i = 1; !command.isEmpty(); i++)
+ {
+ mvLOKBlockedCommandList.emplace(command);
+ command = BlockedListString.getToken(i, ' ');
+ }
+}
+
+bool SfxViewShell::isBlockedCommand(OUString command)
+{
+ return mvLOKBlockedCommandList.find(command) != mvLOKBlockedCommandList.end();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */