diff options
author | Marco Cecchetti <marco.cecchetti@collabora.com> | 2023-03-23 08:33:16 +0100 |
---|---|---|
committer | Miklos Vajna <vmiklos@collabora.com> | 2023-06-02 08:04:38 +0200 |
commit | 14b3ad0b4e5a540f436db3467f8a4051392f8479 (patch) | |
tree | e25797ee8638f1d5a0136cd2ee23ab4d9c6e67b2 | |
parent | 8731701481de649a65d59e018d0ba0c381f670c3 (diff) |
lok: accessibility event listener for focused paragraph
LOKDocumentFocusListener keeps track of the currently focused
paragraph.
Also includes:
Author: Andras Timar <andras.timar@collabora.com>
Date: Mon May 15 22:06:10 2023 +0200
fix unused exception parameter 'e'
Change-Id: Ibfee099e4e9b724648d7500b9ebb4e8ab84989b8
and:
Author: Marco Cecchetti <marco.cecchetti@collabora.com>
Date: Thu May 4 12:11:53 2023 +0200
lok: a11y: focused paragraph info sent to client
For the currently focused paragraph the following data is notified to
client:
paragraph content, caret position, text selection start/end
These data is kept as an instance state so the client can request such
info at any time.
Change-Id: Ic1a3be0d93472300b1b6a91fb0de5bad87c031aa
and:
Author: Marco Cecchetti <marco.cecchetti@collabora.com>
Date: Sun May 7 11:52:14 2023 +0200
fixup! lok: accessibility event listener for focused paragraph
It seems it was not a good idea using a unique_ptr as smart pointer
for an instance of LOKDocumentFocusListener
Change-Id: I8e6b0f48fee3c5db3c9b074a663f7f3fb96a601e
Change-Id: I0fa400694f3129608228ade0b96e0b4e0aee87e2
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/152488
Tested-by: Jenkins
Reviewed-by: Miklos Vajna <vmiklos@collabora.com>
-rw-r--r-- | desktop/qa/desktop_lib/test_desktop_lib.cxx | 8 | ||||
-rw-r--r-- | desktop/source/lib/init.cxx | 144 | ||||
-rw-r--r-- | include/LibreOfficeKit/LibreOfficeKit.h | 9 | ||||
-rw-r--r-- | include/LibreOfficeKit/LibreOfficeKit.hxx | 30 | ||||
-rw-r--r-- | include/LibreOfficeKit/LibreOfficeKitEnums.h | 41 | ||||
-rw-r--r-- | include/sal/log-areas.dox | 1 | ||||
-rw-r--r-- | include/sfx2/lokhelper.hxx | 2 | ||||
-rw-r--r-- | include/sfx2/viewsh.hxx | 11 | ||||
-rw-r--r-- | libreofficekit/source/gtk/lokdocview.cxx | 3 | ||||
-rw-r--r-- | sfx2/source/view/lokhelper.cxx | 17 | ||||
-rw-r--r-- | sfx2/source/view/viewsh.cxx | 741 |
11 files changed, 968 insertions, 39 deletions
diff --git a/desktop/qa/desktop_lib/test_desktop_lib.cxx b/desktop/qa/desktop_lib/test_desktop_lib.cxx index 675ebfe82e8d..74db478c09a3 100644 --- a/desktop/qa/desktop_lib/test_desktop_lib.cxx +++ b/desktop/qa/desktop_lib/test_desktop_lib.cxx @@ -3641,9 +3641,15 @@ void DesktopLOKTest::testABI() CPPUNIT_ASSERT_EQUAL(documentClassOffset(67), offsetof(struct _LibreOfficeKitDocumentClass, getEditMode)); CPPUNIT_ASSERT_EQUAL(documentClassOffset(68), offsetof(struct _LibreOfficeKitDocumentClass, setViewTimezone)); + CPPUNIT_ASSERT_EQUAL(documentClassOffset(69), + offsetof(struct _LibreOfficeKitDocumentClass, setAccessibilityState)); + CPPUNIT_ASSERT_EQUAL(documentClassOffset(70), + offsetof(struct _LibreOfficeKitDocumentClass, getA11yFocusedParagraph)); + CPPUNIT_ASSERT_EQUAL(documentClassOffset(71), + offsetof(struct _LibreOfficeKitDocumentClass, getA11yCaretPosition)); // As above - CPPUNIT_ASSERT_EQUAL(documentClassOffset(69), sizeof(struct _LibreOfficeKitDocumentClass)); + CPPUNIT_ASSERT_EQUAL(documentClassOffset(72), sizeof(struct _LibreOfficeKitDocumentClass)); } CPPUNIT_TEST_SUITE_REGISTRATION(DesktopLOKTest); diff --git a/desktop/source/lib/init.cxx b/desktop/source/lib/init.cxx index 8561760febce..13922571d9ce 100644 --- a/desktop/source/lib/init.cxx +++ b/desktop/source/lib/init.cxx @@ -1309,6 +1309,11 @@ static void doc_sendContentControlEvent(LibreOfficeKitDocument* pThis, const cha static void doc_setViewTimezone(LibreOfficeKitDocument* pThis, int nId, const char* timezone); +static void doc_setAccessibilityState(LibreOfficeKitDocument* pThis, int nId, bool bEnabled); + +static char* doc_getA11yFocusedParagraph(LibreOfficeKitDocument* pThis); + +static int doc_getA11yCaretPosition(LibreOfficeKitDocument* pThis); } // extern "C" namespace { @@ -1361,6 +1366,45 @@ vcl::Font FindFont_FallbackToDefault(std::u16string_view rFontName) return OutputDevice::GetDefaultFont(DefaultFontType::SANS_UNICODE, LANGUAGE_NONE, GetDefaultFontFlags::NONE); } + +int getDocumentType (LibreOfficeKitDocument* pThis) +{ + SetLastExceptionMsg(); + + LibLODocument_Impl* pDocument = static_cast<LibLODocument_Impl*>(pThis); + + try + { + uno::Reference<lang::XServiceInfo> xDocument(pDocument->mxComponent, uno::UNO_QUERY_THROW); + + if (xDocument->supportsService("com.sun.star.sheet.SpreadsheetDocument")) + { + return LOK_DOCTYPE_SPREADSHEET; + } + else if (xDocument->supportsService("com.sun.star.presentation.PresentationDocument")) + { + return LOK_DOCTYPE_PRESENTATION; + } + else if (xDocument->supportsService("com.sun.star.drawing.DrawingDocument")) + { + return LOK_DOCTYPE_DRAWING; + } + else if (xDocument->supportsService("com.sun.star.text.TextDocument") || xDocument->supportsService("com.sun.star.text.WebDocument")) + { + return LOK_DOCTYPE_TEXT; + } + else + { + SetLastExceptionMsg("unknown document type"); + } + } + catch (const uno::Exception& exception) + { + SetLastExceptionMsg("exception: " + exception.Message); + } + return LOK_DOCTYPE_OTHER; +} + } // anonymous namespace LibLODocument_Impl::LibLODocument_Impl(uno::Reference <css::lang::XComponent> xComponent, int nDocumentId) @@ -1458,6 +1502,11 @@ LibLODocument_Impl::LibLODocument_Impl(uno::Reference <css::lang::XComponent> xC m_pDocumentClass->setViewTimezone = doc_setViewTimezone; + m_pDocumentClass->setAccessibilityState = doc_setAccessibilityState; + + m_pDocumentClass->getA11yFocusedParagraph = doc_getA11yFocusedParagraph; + m_pDocumentClass->getA11yCaretPosition = doc_getA11yCaretPosition; + gDocumentClass = m_pDocumentClass; } pClass = m_pDocumentClass.get(); @@ -1764,6 +1813,9 @@ void CallbackFlushHandler::queue(const int type, CallbackData& aCallbackData) case LOK_CALLBACK_INVALIDATE_SHEET_GEOMETRY: case LOK_CALLBACK_REFERENCE_MARKS: case LOK_CALLBACK_CELL_AUTO_FILL_AREA: + case LOK_CALLBACK_A11Y_FOCUS_CHANGED: + case LOK_CALLBACK_A11Y_CARET_CHANGED: + case LOK_CALLBACK_A11Y_TEXT_SELECTION_CHANGED: { const auto& pos = std::find(m_queue1.rbegin(), m_queue1.rend(), type); auto pos2 = toQueue2(pos); @@ -1822,6 +1874,9 @@ void CallbackFlushHandler::queue(const int type, CallbackData& aCallbackData) case LOK_CALLBACK_SET_PART: case LOK_CALLBACK_STATUS_INDICATOR_SET_VALUE: case LOK_CALLBACK_RULER_UPDATE: + case LOK_CALLBACK_A11Y_FOCUS_CHANGED: + case LOK_CALLBACK_A11Y_CARET_CHANGED: + case LOK_CALLBACK_A11Y_TEXT_SELECTION_CHANGED: { if (removeAll(type)) SAL_INFO("lok", "Removed dups of [" << type << "]: [" << aCallbackData.getPayload() << "]."); @@ -3615,40 +3670,7 @@ static int doc_getDocumentType (LibreOfficeKitDocument* pThis) comphelper::ProfileZone aZone("doc_getDocumentType"); SolarMutexGuard aGuard; - SetLastExceptionMsg(); - - LibLODocument_Impl* pDocument = static_cast<LibLODocument_Impl*>(pThis); - - try - { - uno::Reference<lang::XServiceInfo> xDocument(pDocument->mxComponent, uno::UNO_QUERY_THROW); - - if (xDocument->supportsService("com.sun.star.sheet.SpreadsheetDocument")) - { - return LOK_DOCTYPE_SPREADSHEET; - } - else if (xDocument->supportsService("com.sun.star.presentation.PresentationDocument")) - { - return LOK_DOCTYPE_PRESENTATION; - } - else if (xDocument->supportsService("com.sun.star.drawing.DrawingDocument")) - { - return LOK_DOCTYPE_DRAWING; - } - else if (xDocument->supportsService("com.sun.star.text.TextDocument") || xDocument->supportsService("com.sun.star.text.WebDocument")) - { - return LOK_DOCTYPE_TEXT; - } - else - { - SetLastExceptionMsg("unknown document type"); - } - } - catch (const uno::Exception& exception) - { - SetLastExceptionMsg("exception: " + exception.Message); - } - return LOK_DOCTYPE_OTHER; + return getDocumentType(pThis); } static int doc_getParts (LibreOfficeKitDocument* pThis) @@ -3770,6 +3792,46 @@ static char* doc_getPartPageRectangles(LibreOfficeKitDocument* pThis) return convertOUString(pDoc->getPartPageRectangles()); } +static char* doc_getA11yFocusedParagraph(LibreOfficeKitDocument* pThis) +{ + SolarMutexGuard aGuard; + SetLastExceptionMsg(); + + ITiledRenderable* pDoc = getTiledRenderable(pThis); + if (!pDoc) + { + SetLastExceptionMsg("Document doesn't support tiled rendering"); + return nullptr; + } + + if (SfxViewShell* pViewShell = SfxViewShell::Current()) + { + return convertOUString(pViewShell->getA11yFocusedParagraph()); + + } + return nullptr; +} + +static int doc_getA11yCaretPosition(LibreOfficeKitDocument* pThis) +{ + SolarMutexGuard aGuard; + SetLastExceptionMsg(); + + ITiledRenderable* pDoc = getTiledRenderable(pThis); + if (!pDoc) + { + SetLastExceptionMsg("Document doesn't support tiled rendering"); + return -1; + } + if (SfxViewShell* pViewShell = SfxViewShell::Current()) + { + return pViewShell->getA11yCaretPosition(); + + } + return -1; + +} + static char* doc_getPartName(LibreOfficeKitDocument* pThis, int nPart) { comphelper::ProfileZone aZone("doc_getPartName"); @@ -6395,8 +6457,6 @@ static void doc_setViewLanguage(SAL_UNUSED_PARAMETER LibreOfficeKitDocument* /*p SfxLokHelper::setViewLocale(nId, sLanguage); } - - unsigned char* doc_renderFont(LibreOfficeKitDocument* pThis, const char* pFontName, const char* pChar, @@ -6938,6 +6998,18 @@ static void doc_setViewTimezone(SAL_UNUSED_PARAMETER LibreOfficeKitDocument* /*p } } +static void doc_setAccessibilityState(SAL_UNUSED_PARAMETER LibreOfficeKitDocument* pThis, int nId, bool nEnabled) +{ + SolarMutexGuard aGuard; + SetLastExceptionMsg(); + + int nDocType = getDocumentType(pThis); + if (nDocType != LOK_DOCTYPE_TEXT) + return; + + SfxLokHelper::setAccessibilityState(nId, nEnabled); +} + static char* lo_getError (LibreOfficeKit *pThis) { comphelper::ProfileZone aZone("lo_getError"); diff --git a/include/LibreOfficeKit/LibreOfficeKit.h b/include/LibreOfficeKit/LibreOfficeKit.h index 767469666fef..ba51c5afeea9 100644 --- a/include/LibreOfficeKit/LibreOfficeKit.h +++ b/include/LibreOfficeKit/LibreOfficeKit.h @@ -493,6 +493,15 @@ struct _LibreOfficeKitDocumentClass /// @see lok::Document::setViewTimezone(). void (*setViewTimezone) (LibreOfficeKitDocument* pThis, int nId, const char* timezone); + /// @see lok::Document::setAccessibilityState(). + void (*setAccessibilityState) (LibreOfficeKitDocument* pThis, int nId, bool nEnabled); + + /// @see lok::Document::getA11yFocusedParagraph. + char* (*getA11yFocusedParagraph) (LibreOfficeKitDocument* pThis); + + /// @see lok::Document::getA11yCaretPosition. + int (*getA11yCaretPosition) (LibreOfficeKitDocument* pThis); + #endif // defined LOK_USE_UNSTABLE_API || defined LIBO_INTERNAL_ONLY }; diff --git a/include/LibreOfficeKit/LibreOfficeKit.hxx b/include/LibreOfficeKit/LibreOfficeKit.hxx index 2946d0baaf7d..65f263c368a6 100644 --- a/include/LibreOfficeKit/LibreOfficeKit.hxx +++ b/include/LibreOfficeKit/LibreOfficeKit.hxx @@ -859,6 +859,36 @@ public: mpDoc->pClass->setViewTimezone(mpDoc, nId, timezone); } + /** + * Enable/Disable accessibility support for the window with the specified nId. + * + * @param nId a view ID, returned by createView(). + * @param nEnabled true/false + */ + void setAccessibilityState(int nId, bool nEnabled) + { + mpDoc->pClass->setAccessibilityState(mpDoc, nId, nEnabled); + } + + /** + * Get the current focused paragraph info: + * { + * "content": paragraph content + * "start": selection start + * "end": selection end + * } + */ + char* getA11yFocusedParagraph() + { + return mpDoc->pClass->getA11yFocusedParagraph(mpDoc); + } + + /// Get the current text cursor position. + int getA11yCaretPosition() + { + return mpDoc->pClass->getA11yCaretPosition(mpDoc); + } + #endif // defined LOK_USE_UNSTABLE_API || defined LIBO_INTERNAL_ONLY }; diff --git a/include/LibreOfficeKit/LibreOfficeKitEnums.h b/include/LibreOfficeKit/LibreOfficeKitEnums.h index f57e00afa185..47b2d790738f 100644 --- a/include/LibreOfficeKit/LibreOfficeKitEnums.h +++ b/include/LibreOfficeKit/LibreOfficeKitEnums.h @@ -922,7 +922,40 @@ typedef enum * Informs the LibreOfficeKit client that the background color surrounding * the document has changed. */ - LOK_CALLBACK_APPLICATION_BACKGROUND_COLOR = 61 + LOK_CALLBACK_APPLICATION_BACKGROUND_COLOR = 61, + + /** + * Accessibility event: a paragraph get focus. + * The payload is a json with the following structure. + * + * { + * "content": "<paragraph text>" + * "position": N + * } + * where N is the position of the text cursor inside the focused paragraph. + */ + LOK_CALLBACK_A11Y_FOCUS_CHANGED = 62, + + /** + * Accessibility event: text cursor position has changed. + * + * { + * "position": N + * } + * where N is the position of the text cursor inside the focused paragraph. + */ + LOK_CALLBACK_A11Y_CARET_CHANGED = 63, + + /** + * Accessibility event: text cursor position has changed. + * + * { + * "start": N1 + * "end": N2 + * } + * where [N1,N2] is the range of the text selection inside the focused paragraph. + */ + LOK_CALLBACK_A11Y_TEXT_SELECTION_CHANGED = 64 } LibreOfficeKitCallbackType; @@ -1075,6 +1108,12 @@ static inline const char* lokCallbackTypeToString(int nType) return "LOK_CALLBACK_VIEW_RENDER_STATE"; case LOK_CALLBACK_APPLICATION_BACKGROUND_COLOR: return "LOK_CALLBACK_APPLICATION_BACKGROUND_COLOR"; + case LOK_CALLBACK_A11Y_FOCUS_CHANGED: + return "LOK_CALLBACK_A11Y_FOCUS_CHANGED"; + case LOK_CALLBACK_A11Y_CARET_CHANGED: + return "LOK_CALLBACK_A11Y_CARET_CHANGED"; + case LOK_CALLBACK_A11Y_TEXT_SELECTION_CHANGED: + return "LOK_CALLBACK_A11Y_TEXT_SELECTION_CHANGED"; } assert(!"Unknown LibreOfficeKitCallbackType type."); diff --git a/include/sal/log-areas.dox b/include/sal/log-areas.dox index 51b005e1bb51..f209b6244f5f 100644 --- a/include/sal/log-areas.dox +++ b/include/sal/log-areas.dox @@ -325,6 +325,7 @@ certain functionality. @li @c lok.fontsubst @li @c lok.tiledrendering @li @c lok.dialog +@li @c lok.a11y - LOK accessibility @section l10ntools diff --git a/include/sfx2/lokhelper.hxx b/include/sfx2/lokhelper.hxx index d975b4ade2bf..08d66fd538cc 100644 --- a/include/sfx2/lokhelper.hxx +++ b/include/sfx2/lokhelper.hxx @@ -83,6 +83,8 @@ public: static void setViewLanguage(int nId, const OUString& rBcp47LanguageTag); /// Set the default language for views. static void setDefaultLanguage(const OUString& rBcp47LanguageTag); + /// Enable/Disable AT support for the given view. + static void setAccessibilityState(int nId, bool nEnabled); /// Get the language used by the loading view (used for all save operations). static const LanguageTag & getLoadLanguage(); /// Set the language used by the loading view (used for all save operations). diff --git a/include/sfx2/viewsh.hxx b/include/sfx2/viewsh.hxx index a9713eb2375f..b805f1cf99d4 100644 --- a/include/sfx2/viewsh.hxx +++ b/include/sfx2/viewsh.hxx @@ -57,6 +57,7 @@ class SfxPrinter; class NotifyEvent; class SfxInPlaceClient; class SfxLokCallbackInterface; +class LOKDocumentFocusListener; class SfxStoringHelper; class VCLXPopupMenu; namespace rtl { class OStringBuffer; } @@ -173,6 +174,8 @@ friend class SfxPrinterController; LanguageTag maLOKLanguageTag; LanguageTag maLOKLocale; LOKDeviceFormFactor maLOKDeviceFormFactor; + bool mbLOKAccessibilityEnabled; + rtl::Reference<LOKDocumentFocusListener> mpLOKDocumentFocusListener; std::unordered_set<OUString> mvLOKBlockedCommandList; OUString maLOKTimezone; bool maLOKIsTimezoneSet; @@ -215,6 +218,9 @@ private: /// SfxInterface initializer. static void InitInterface_Impl(); + LOKDocumentFocusListener& GetLOKDocumentFocusListener(); + const LOKDocumentFocusListener& GetLOKDocumentFocusListener() const; + public: SfxViewShell( SfxViewFrame& rFrame, SfxViewShellFlags nFlags ); @@ -427,6 +433,8 @@ public: void SetLOKLanguageTag(const OUString& rBcp47LanguageTag); /// Get the LibreOfficeKit language of this view. const LanguageTag& GetLOKLanguageTag() const { return maLOKLanguageTag; } + /// Enable/Disable LibreOfficeKit AT support for this view. + void SetLOKAccessibilityState(bool bEnabled); /// Get the LibreOfficeKit timezone of this view. See @SetLOKTimezone. std::pair<bool, OUString> GetLOKTimezone() const @@ -466,6 +474,9 @@ public: StylesHighlighterColorMap& GetStylesHighlighterParaColorMap() { return ParaStylesColorMap; } StylesHighlighterColorMap& GetStylesHighlighterCharColorMap() { return CharStylesColorMap; } + + OUString getA11yFocusedParagraph() const; + int getA11yCaretPosition() const; }; #endif // INCLUDED_SFX2_VIEWSH_HXX diff --git a/libreofficekit/source/gtk/lokdocview.cxx b/libreofficekit/source/gtk/lokdocview.cxx index 76f94293956b..6d810f2d1e3f 100644 --- a/libreofficekit/source/gtk/lokdocview.cxx +++ b/libreofficekit/source/gtk/lokdocview.cxx @@ -1488,6 +1488,9 @@ callback (gpointer pData) case LOK_CALLBACK_EXPORT_FILE: case LOK_CALLBACK_VIEW_RENDER_STATE: case LOK_CALLBACK_APPLICATION_BACKGROUND_COLOR: + case LOK_CALLBACK_A11Y_FOCUS_CHANGED: + case LOK_CALLBACK_A11Y_CARET_CHANGED: + case LOK_CALLBACK_A11Y_TEXT_SELECTION_CHANGED: { // TODO: Implement me break; diff --git a/sfx2/source/view/lokhelper.cxx b/sfx2/source/view/lokhelper.cxx index ad78f01e7398..356925a6a341 100644 --- a/sfx2/source/view/lokhelper.cxx +++ b/sfx2/source/view/lokhelper.cxx @@ -145,10 +145,11 @@ void SfxLokHelper::destroyView(int nId) const ViewShellId nViewShellId(nId); std::vector<SfxViewShell*>& rViewArr = pApp->GetViewShells_Impl(); - for (const SfxViewShell* pViewShell : rViewArr) + for (SfxViewShell* pViewShell : rViewArr) { if (pViewShell->GetViewShellId() == nViewShellId) { + pViewShell->SetLOKAccessibilityState(false); SfxViewFrame& rViewFrame = pViewShell->GetViewFrame(); SfxRequest aRequest(rViewFrame, SID_CLOSEWIN); rViewFrame.Exec_Impl(aRequest); @@ -312,6 +313,20 @@ void SfxLokHelper::setViewLanguage(int nId, const OUString& rBcp47LanguageTag) } } +void SfxLokHelper::setAccessibilityState(int nId, bool nEnabled) +{ + std::vector<SfxViewShell*>& rViewArr = SfxGetpApp()->GetViewShells_Impl(); + + for (SfxViewShell* pViewShell : rViewArr) + { + if (pViewShell->GetViewShellId() == ViewShellId(nId)) + { + pViewShell->SetLOKAccessibilityState(nEnabled); + return; + } + } +} + void SfxLokHelper::setViewLocale(int nId, const OUString& rBcp47LanguageTag) { std::vector<SfxViewShell*>& rViewArr = SfxGetpApp()->GetViewShells_Impl(); diff --git a/sfx2/source/view/viewsh.cxx b/sfx2/source/view/viewsh.cxx index 4e414d9c65bf..a818a98380bb 100644 --- a/sfx2/source/view/viewsh.cxx +++ b/sfx2/source/view/viewsh.cxx @@ -19,6 +19,8 @@ #include <config_features.h> +#include <boost/property_tree/json_parser.hpp> + #include <sal/log.hxx> #include <svl/stritem.hxx> #include <svl/eitem.hxx> @@ -46,9 +48,22 @@ #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/XAccessibleText.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 <tools/urlobj.hxx> #include <unotools/tempfile.hxx> @@ -221,6 +236,664 @@ void SAL_CALL SfxClipboardChangeListener::changedContents( const datatransfer::c delete pInfo; } +class LOKDocumentFocusListener : + public ::cppu::WeakImplHelper< accessibility::XAccessibleEventListener > +{ + static constexpr sal_Int64 MAX_ATTACHABLE_CHILDREN = 30; + + const SfxViewShell* m_pViewShell; + std::set< uno::Reference< uno::XInterface > > m_aRefList; + OUString m_sFocusedParagraph; + bool m_bFocusedParagraphNotified; + sal_Int32 m_nCaretPosition; + sal_Int32 m_nSelectionStart; + sal_Int32 m_nSelectionEnd; + OUString m_sSelectedText; + bool m_bIsEditingCell; + OUString m_sSelectedCellAddress; + +public: + 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 + ); + + /// @throws lang::IndexOutOfBoundsException + /// @throws uno::RuntimeException + void detachRecursive( + const uno::Reference< accessibility::XAccessibleContext >& xContext + ); + + /// @throws lang::IndexOutOfBoundsException + /// @throws uno::RuntimeException + void detachRecursive( + const uno::Reference< accessibility::XAccessibleContext >& xContext, + const sal_Int64 nStateSet + ); + + /// @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 notifyFocusedParagraphChanged(); + void notifyCaretChanged(); + void notifyTextSelectionChanged(); + + OUString getFocusedParagraph() const; + int getCaretPosition() const; +}; + +LOKDocumentFocusListener::LOKDocumentFocusListener(const SfxViewShell* pViewShell) + : m_pViewShell(pViewShell) + , m_bFocusedParagraphNotified(false) + , m_nCaretPosition(0) + , m_nSelectionStart(0) + , m_nSelectionEnd(0) + , m_bIsEditingCell(false) +{ +} + +OUString LOKDocumentFocusListener::getFocusedParagraph() const +{ + SAL_INFO("lok.a11y", "LOKDocumentFocusListener::getFocusedParagraph: " << m_sFocusedParagraph); + const_cast<LOKDocumentFocusListener*>(this)->m_bFocusedParagraphNotified = true; + + sal_Int32 nSelectionStart = m_nSelectionStart; + sal_Int32 nSelectionEnd = m_nSelectionEnd; + if (nSelectionStart < 0 || nSelectionEnd < 0) + nSelectionStart = nSelectionEnd = m_nCaretPosition; + + boost::property_tree::ptree aPayloadTree; + aPayloadTree.put("content", m_sFocusedParagraph.toUtf8().getStr()); + aPayloadTree.put("start", nSelectionStart); + aPayloadTree.put("end", nSelectionEnd); + std::stringstream aStream; + boost::property_tree::write_json(aStream, aPayloadTree); + std::string aPayload = aStream.str(); + OUString sRet = OUString::fromUtf8(aPayload); + return sRet; +} + +int LOKDocumentFocusListener::getCaretPosition() const +{ + SAL_INFO("lok.a11y", "LOKDocumentFocusListener::getCaretPosition: " << m_nCaretPosition); + return m_nCaretPosition; +} + +void LOKDocumentFocusListener::notifyFocusedParagraphChanged() +{ + boost::property_tree::ptree aPayloadTree; + aPayloadTree.put("content", m_sFocusedParagraph.toUtf8().getStr()); + 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::notifyFocusedParagraphChanged: " << m_sFocusedParagraph); + m_bFocusedParagraphNotified = true; + const char* pPayload = aPayload.c_str(); + m_pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_A11Y_FOCUS_CHANGED, pPayload); + } +} + +void LOKDocumentFocusListener::notifyCaretChanged() +{ + 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); + const char* pPayload = aPayload.c_str(); + m_pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_A11Y_CARET_CHANGED, pPayload); + } +} + +void LOKDocumentFocusListener::notifyTextSelectionChanged() +{ + boost::property_tree::ptree aPayloadTree; + aPayloadTree.put("start", m_nSelectionStart); + aPayloadTree.put("end", m_nSelectionEnd); + 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); + const char* pPayload = aPayload.c_str(); + m_pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_A11Y_TEXT_SELECTION_CHANGED, pPayload); + } +} + +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); + +} + +namespace +{ +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); +} +} // anonymous namespace + +void LOKDocumentFocusListener::notifyEvent( const accessibility::AccessibleEventObject& aEvent ) +{ + try + { + switch( aEvent.EventId ) + { + case accessibility::AccessibleEventId::STATE_CHANGED: + { + uno::Reference< accessibility::XAccessible > xAccStateChanged = getAccessible(aEvent); + sal_Int64 nState = accessibility::AccessibleStateType::INVALID; + aEvent.NewValue >>= nState; + SAL_INFO("lok.a11y", "LOKDocumentFocusListener::notifyEvent: STATE_CHANGED: XAccessible: " << xAccStateChanged.get() << ", nState: " << nState); + + if( accessibility::AccessibleStateType::FOCUSED == nState ) + { + SAL_INFO("lok.a11y", "LOKDocumentFocusListener::notifyEvent: FOCUSED"); + + if (m_bIsEditingCell) + { + if (!hasState(aEvent, accessibility::AccessibleStateType::ACTIVE)) + { + SAL_WARN("lok.a11y", + "LOKDocumentFocusListener::notifyEvent: FOCUSED: Cell not ACTIVE for editing yet"); + return; + } + } + uno::Reference<css::accessibility::XAccessibleText> xAccText(xAccStateChanged, uno::UNO_QUERY); + if( xAccText.is() ) + { + OUString sText = xAccText->getText(); + sal_Int32 nLength = sText.getLength(); + sal_Int32 nCaretPosition = xAccText->getCaretPosition(); + SAL_INFO("lok.a11y", "LOKDocumentFocusListener::notifyEvent: xAccText: " << xAccText.get() + << ", text: >" << sText << "<, caret pos: " << nCaretPosition); + + if (nLength) + { + 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", "LOKDocumentFocusListener::notifyEvent: " + "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", + "LOKDocumentFocusListener::notifyEvent: 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; + attribute.Value >>= fValue; + sValue = OUString::number(fValue); + } + else if (attribute.Name == "CharPosture") + { + awt::FontSlant nValue; + attribute.Value >>= nValue; + sValue = OUString::number(static_cast<unsigned int>(nValue)); + } + else if (attribute.Name == "CharUnderline") + { + sal_Int16 nValue; + attribute.Value >>= nValue; + sValue = OUString::number(nValue); + } + else if (attribute.Name == "CharFontName") + { + attribute.Value >>= sValue; + } + else if (attribute.Name == "Rsid") + { + sal_uInt32 nValue; + attribute.Value >>= nValue; + sValue = OUString::number(nValue); + } + + if (!sValue.isEmpty()) + { + if (sAttributes != "{ ") + sAttributes += ", "; + sAttributes += attribute.Name + ": " + sValue; + sValue = ""; + } + } + sAttributes += " }"; + SAL_INFO("lok.a11y", + "LOKDocumentFocusListener::notifyEvent: attributes: " << sAttributes); + } + nPos = aTextSegment.SegmentEnd + 1; + } + } + if (!m_bFocusedParagraphNotified || m_sFocusedParagraph != sText) + { + m_sFocusedParagraph = sText; + m_nCaretPosition = nCaretPosition; + notifyFocusedParagraphChanged(); + } + } + } + + break; + } + + case accessibility::AccessibleEventId::CARET_CHANGED: + { + if (!isFocused(aEvent)) + { + SAL_WARN("lok.a11y", + "LOKDocumentFocusListener::notifyEvent: CARET_CHANGED: skip non focused paragraph"); + return; + } + + 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<css::accessibility::XAccessibleText> + xAccText(getAccessible(aEvent), uno::UNO_QUERY); + if( xAccText.is() ) + { + OUString sText = xAccText->getText(); + SAL_INFO("lok.a11y", "LOKDocumentFocusListener::notifyEvent: CARET_CHANGED: xAccText: " << xAccText.get() << ", text: >" << sText << "<"); + + m_nCaretPosition = nNewPos; + m_nSelectionStart = m_nSelectionEnd = m_nCaretPosition; + notifyCaretChanged(); + } + } + + break; + } + + case accessibility::AccessibleEventId::TEXT_CHANGED: + { + if (!isFocused(aEvent)) + { + SAL_WARN("lok.a11y", + "LOKDocumentFocusListener::notifyEvent: TEXT_CHANGED: skip non focused paragraph"); + return; + } + + accessibility::TextSegment aDeletedText; + accessibility::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<css::accessibility::XAccessibleText> xAccText(getAccessible(aEvent), uno::UNO_QUERY); + if (xAccText.is()) + { + OUString sText = xAccText->getText(); + SAL_INFO("lok.a11y", "LOKDocumentFocusListener::notifyEvent: TEXT_CHANGED: " + "xAccText: " << xAccText.get() << ", text: >" << sText << "<"); + m_sFocusedParagraph = sText; + m_bFocusedParagraphNotified = false; + } + + break; + } + case accessibility::AccessibleEventId::TEXT_SELECTION_CHANGED: + { + if (!isFocused(aEvent)) + { + SAL_WARN("lok.a11y", + "LOKDocumentFocusListener::notifyEvent: TEXT_SELECTION_CHANGED: skip non focused paragraph"); + return; + } + + uno::Reference<css::accessibility::XAccessibleText> xAccText(getAccessible(aEvent), uno::UNO_QUERY); + if (xAccText.is()) + { + OUString sText = xAccText->getText(); + sal_Int32 nSelectionStart = xAccText->getSelectionStart(); + sal_Int32 nSelectionEnd = xAccText->getSelectionEnd(); + m_sSelectedText = xAccText->getSelectedText(); + + SAL_INFO("lok.a11y", "LOKDocumentFocusListener::notifyEvent: TEXT_SELECTION_CHANGED: " + "\n xAccText: " << xAccText.get() << ", text: >" << sText << "<" + "\n start: " << nSelectionStart << ", end: " << nSelectionEnd + << "\n selected text: >" << m_sSelectedText << "<"); + + // This should not be risky since selection start/end are set also on CARET_CHANGED event + if (nSelectionStart == m_nSelectionStart && nSelectionEnd == m_nSelectionEnd) + return; + + // 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. + m_nSelectionStart = nSelectionStart; + m_nSelectionEnd = nSelectionEnd; + + // Calc: when editing a formula send the update content + if (m_bIsEditingCell && !m_sSelectedCellAddress.isEmpty() + && !m_sSelectedText.isEmpty() && sText.startsWith("=")) + { + notifyFocusedParagraphChanged(); + } + notifyTextSelectionChanged(); + } + + break; + } + case accessibility::AccessibleEventId::SELECTION_CHANGED: + { + uno::Reference< accessibility::XAccessible > xNewValue; + aEvent.NewValue >>= xNewValue; + if (xNewValue.is()) + { + uno::Reference< accessibility::XAccessibleContext > xContext = + xNewValue->getAccessibleContext(); + + if (xContext.is()) + { + OUString sName = xContext->getAccessibleName(); + SAL_INFO("lok.a11y", "LOKDocumentFocusListener::notifyEvent: SELECTION_CHANGED: this: " << this + << ", selected cell address: >" << sName << "<" + ", m_bIsEditingCell: " << m_bIsEditingCell); + if (m_bIsEditingCell && !sName.isEmpty()) + { + m_sSelectedCellAddress = sName; + // 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; + } + case accessibility::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 accessibility::AccessibleEventId::INVALIDATE_ALL_CHILDREN: + SAL_INFO("lok.a11y", "Invalidate all children called"); + break; + + default: + break; + } + } + catch( const lang::IndexOutOfBoundsException& ) + { + SAL_WARN("lok.a11y", "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; + + 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() ); + } + } + } + + return uno::Reference< accessibility::XAccessible >(); +} + +void LOKDocumentFocusListener::attachRecursive( + const uno::Reference< accessibility::XAccessible >& xAccessible +) +{ + SAL_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 +) +{ + SAL_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); +} + +void LOKDocumentFocusListener::attachRecursive( + const uno::Reference< accessibility::XAccessible >& xAccessible, + const uno::Reference< accessibility::XAccessibleContext >& xContext, + const sal_Int64 nStateSet +) +{ + SAL_INFO("lok.a11y", "LOKDocumentFocusListener::attachRecursive(3) #1: this: " << this + << ", xAccessible: " << xAccessible.get() + << ", role: " << xContext->getAccessibleRole() + << ", name: " << xContext->getAccessibleName() + << ", 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( !(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() ) + attachRecursive(xChild); + } + } + } +} + +void LOKDocumentFocusListener::detachRecursive( + const uno::Reference< accessibility::XAccessible >& xAccessible +) +{ + uno::Reference< accessibility::XAccessibleContext > xContext = + xAccessible->getAccessibleContext(); + + if( xContext.is() ) + detachRecursive(xContext); +} + +void LOKDocumentFocusListener::detachRecursive( + const uno::Reference< accessibility::XAccessibleContext >& xContext +) +{ + 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); +} + +void LOKDocumentFocusListener::detachRecursive( + const uno::Reference< accessibility::XAccessibleContext >& xContext, + const sal_Int64 nStateSet +) +{ + 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::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) @@ -1074,6 +1747,7 @@ SfxViewShell::SfxViewShell , maLOKLanguageTag(LANGUAGE_NONE) , maLOKLocale(LANGUAGE_NONE) , maLOKDeviceFormFactor(LOKDeviceFormFactor::UNKNOWN) +, mbLOKAccessibilityEnabled(false) { SetMargin( rViewFrame.GetMargin_Impl() ); @@ -1126,6 +1800,18 @@ SfxViewShell::~SfxViewShell() 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 @@ -1645,6 +2331,61 @@ void SfxViewShell::SetLOKLanguageTag(const OUString& rBcp47LanguageTag) 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&) + { + SAL_WARN("lok.a11y", "Exception caught processing LOKDocumentFocusListener::attachRecursive"); + } + } + else + { + try + { + rDocumentFocusListener.detachRecursive(xAccessible); + } + catch (const uno::Exception&) + { + SAL_WARN("lok.a11y", "Exception caught processing LOKDocumentFocusListener::detachRecursive"); + } + } +} + void SfxViewShell::SetLOKLocale(const OUString& rBcp47LanguageTag) { maLOKLocale = LanguageTag(rBcp47LanguageTag, true).makeFallback(); |