summaryrefslogtreecommitdiffstats
path: root/sfx2/source/view/viewsh.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'sfx2/source/view/viewsh.cxx')
-rw-r--r--sfx2/source/view/viewsh.cxx3912
1 files changed, 3912 insertions, 0 deletions
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: */