1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <config_features.h>
22 #include <boost/property_tree/json_parser.hpp>
24 #include <sal/log.hxx>
25 #include <svl/stritem.hxx>
26 #include <svl/eitem.hxx>
27 #include <svl/whiter.hxx>
29 #include <vcl/svapp.hxx>
30 #include <vcl/toolbox.hxx>
31 #include <vcl/weld.hxx>
32 #include <svl/intitem.hxx>
33 #include <svtools/colorcfg.hxx>
34 #include <svtools/langhelp.hxx>
35 #include <com/sun/star/awt/XPopupMenu.hpp>
36 #include <com/sun/star/frame/XLayoutManager.hpp>
37 #include <com/sun/star/frame/ModuleManager.hpp>
38 #include <com/sun/star/io/IOException.hpp>
39 #include <com/sun/star/beans/XPropertySet.hpp>
40 #include <com/sun/star/embed/EmbedStates.hpp>
41 #include <com/sun/star/embed/EmbedMisc.hpp>
42 #include <com/sun/star/embed/XEmbeddedObject.hpp>
43 #include <com/sun/star/container/XContainerQuery.hpp>
44 #include <com/sun/star/frame/XStorable.hpp>
45 #include <com/sun/star/frame/XModel.hpp>
46 #include <com/sun/star/datatransfer/clipboard/XClipboard.hpp>
47 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
48 #include <com/sun/star/datatransfer/clipboard/XClipboardListener.hpp>
49 #include <com/sun/star/datatransfer/clipboard/XClipboardNotifier.hpp>
50 #include <com/sun/star/view/XRenderable.hpp>
51 #include <com/sun/star/uno/Reference.hxx>
52 #include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
53 #include <com/sun/star/accessibility/XAccessibleContext.hpp>
54 #include <com/sun/star/accessibility/XAccessibleEventBroadcaster.hpp>
55 #include <com/sun/star/accessibility/XAccessibleSelection.hpp>
56 #include <com/sun/star/accessibility/AccessibleEventId.hpp>
57 #include <com/sun/star/accessibility/AccessibleStateType.hpp>
58 #include <com/sun/star/accessibility/AccessibleRole.hpp>
59 #include <com/sun/star/accessibility/XAccessibleText.hpp>
60 #include <com/sun/star/accessibility/XAccessibleTable.hpp>
61 #include <cppuhelper/implbase.hxx>
62 #include <com/sun/star/ui/XAcceleratorConfiguration.hpp>
64 #include <cppuhelper/weakref.hxx>
66 #include <com/sun/star/accessibility/XAccessibleTextAttributes.hpp>
67 #include <com/sun/star/accessibility/AccessibleTextType.hpp>
68 #include <com/sun/star/awt/FontSlant.hpp>
70 #include <comphelper/diagnose_ex.hxx>
71 #include <editeng/unoprnms.hxx>
72 #include <tools/urlobj.hxx>
73 #include <unotools/tempfile.hxx>
74 #include <svtools/soerr.hxx>
75 #include <tools/svborder.hxx>
77 #include <framework/actiontriggerhelper.hxx>
78 #include <comphelper/lok.hxx>
79 #include <comphelper/processfactory.hxx>
80 #include <comphelper/propertyvalue.hxx>
81 #include <comphelper/sequenceashashmap.hxx>
82 #include <toolkit/helper/vclunohelper.hxx>
83 #include <vcl/settings.hxx>
84 #include <vcl/commandinfoprovider.hxx>
85 #include <LibreOfficeKit/LibreOfficeKitEnums.h>
87 #include <officecfg/Setup.hxx>
88 #include <sfx2/app.hxx>
89 #include <sfx2/flatpak.hxx>
90 #include <sfx2/viewsh.hxx>
91 #include "viewimp.hxx"
92 #include <sfx2/sfxresid.hxx>
93 #include <sfx2/request.hxx>
94 #include <sfx2/printer.hxx>
95 #include <sfx2/docfile.hxx>
96 #include <sfx2/dispatch.hxx>
97 #include <sfx2/strings.hrc>
98 #include <sfx2/sfxbasecontroller.hxx>
99 #include <sfx2/mailmodelapi.hxx>
100 #include <bluthsndapi.hxx>
101 #include <sfx2/viewfrm.hxx>
102 #include <sfx2/event.hxx>
103 #include <sfx2/ipclient.hxx>
104 #include <sfx2/sfxsids.hrc>
105 #include <sfx2/objface.hxx>
106 #include <sfx2/lokhelper.hxx>
107 #include <sfx2/lokcallback.hxx>
108 #include <openuriexternally.hxx>
112 #include <libxml/xmlwriter.h>
113 #include <toolkit/awt/vclxmenu.hxx>
114 #include <unordered_map>
115 #include <unordered_set>
117 #define ShellClass_SfxViewShell
118 #include <sfxslots.hxx>
120 using namespace ::com::sun::star
;
121 using namespace ::com::sun::star::uno
;
122 using namespace ::com::sun::star::frame
;
123 using namespace ::com::sun::star::beans
;
124 using namespace ::cppu
;
126 class SfxClipboardChangeListener
: public ::cppu::WeakImplHelper
<
127 datatransfer::clipboard::XClipboardListener
>
130 SfxClipboardChangeListener( SfxViewShell
* pView
, uno::Reference
< datatransfer::clipboard::XClipboardNotifier
> xClpbrdNtfr
);
133 virtual void SAL_CALL
disposing( const lang::EventObject
& rEventObject
) override
;
135 // XClipboardListener
136 virtual void SAL_CALL
changedContents( const datatransfer::clipboard::ClipboardEvent
& rEventObject
) override
;
138 void DisconnectViewShell() { m_pViewShell
= nullptr; }
139 void ChangedContents();
143 ASYNCEXECUTE_CMD_DISPOSING
,
144 ASYNCEXECUTE_CMD_CHANGEDCONTENTS
147 struct AsyncExecuteInfo
149 AsyncExecuteInfo( AsyncExecuteCmd eCmd
, SfxClipboardChangeListener
* pListener
) :
150 m_eCmd( eCmd
), m_xListener( pListener
) {}
152 AsyncExecuteCmd m_eCmd
;
153 rtl::Reference
<SfxClipboardChangeListener
> m_xListener
;
157 SfxViewShell
* m_pViewShell
;
158 uno::Reference
< datatransfer::clipboard::XClipboardNotifier
> m_xClpbrdNtfr
;
159 uno::Reference
< lang::XComponent
> m_xCtrl
;
161 DECL_STATIC_LINK( SfxClipboardChangeListener
, AsyncExecuteHdl_Impl
, void*, void );
164 SfxClipboardChangeListener::SfxClipboardChangeListener( SfxViewShell
* pView
, uno::Reference
< datatransfer::clipboard::XClipboardNotifier
> xClpbrdNtfr
)
165 : m_pViewShell( nullptr ), m_xClpbrdNtfr(std::move( xClpbrdNtfr
)), m_xCtrl(pView
->GetController())
169 m_xCtrl
->addEventListener( uno::Reference
< lang::XEventListener
> ( static_cast < lang::XEventListener
* >( this ) ) );
170 m_pViewShell
= pView
;
172 if ( m_xClpbrdNtfr
.is() )
174 m_xClpbrdNtfr
->addClipboardListener( uno::Reference
< datatransfer::clipboard::XClipboardListener
>(
175 static_cast< datatransfer::clipboard::XClipboardListener
* >( this )));
179 void SfxClipboardChangeListener::ChangedContents()
181 const SolarMutexGuard aGuard
;
185 SfxBindings
& rBind
= m_pViewShell
->GetViewFrame().GetBindings();
186 rBind
.Invalidate(SID_PASTE
);
187 rBind
.Invalidate(SID_PASTE_SPECIAL
);
188 rBind
.Invalidate(SID_CLIPBOARD_FORMAT_ITEMS
);
190 if (comphelper::LibreOfficeKit::isActive())
192 // In the future we might send the payload as well.
193 SfxLokHelper::notifyAllViews(LOK_CALLBACK_CLIPBOARD_CHANGED
, ""_ostr
);
197 IMPL_STATIC_LINK( SfxClipboardChangeListener
, AsyncExecuteHdl_Impl
, void*, p
, void )
199 AsyncExecuteInfo
* pAsyncExecuteInfo
= static_cast<AsyncExecuteInfo
*>(p
);
200 if ( pAsyncExecuteInfo
)
202 if ( pAsyncExecuteInfo
->m_xListener
.is() )
204 if ( pAsyncExecuteInfo
->m_eCmd
== ASYNCEXECUTE_CMD_DISPOSING
)
205 pAsyncExecuteInfo
->m_xListener
->DisconnectViewShell();
206 else if ( pAsyncExecuteInfo
->m_eCmd
== ASYNCEXECUTE_CMD_CHANGEDCONTENTS
)
207 pAsyncExecuteInfo
->m_xListener
->ChangedContents();
210 delete pAsyncExecuteInfo
;
213 void SAL_CALL
SfxClipboardChangeListener::disposing( const lang::EventObject
& /*rEventObject*/ )
215 // Either clipboard or ViewShell is going to be destroyed -> no interest in listening anymore
216 uno::Reference
< lang::XComponent
> xCtrl( m_xCtrl
);
217 uno::Reference
< datatransfer::clipboard::XClipboardNotifier
> xNotify( m_xClpbrdNtfr
);
219 uno::Reference
< datatransfer::clipboard::XClipboardListener
> xThis( static_cast< datatransfer::clipboard::XClipboardListener
* >( this ));
221 xCtrl
->removeEventListener( uno::Reference
< lang::XEventListener
> ( static_cast < lang::XEventListener
* >( this )));
223 xNotify
->removeClipboardListener( xThis
);
225 // Make asynchronous call to avoid locking SolarMutex which is the
226 // root for many deadlocks, especially in conjunction with the "Windows"
227 // based single thread apartment clipboard code!
228 AsyncExecuteInfo
* pInfo
= new AsyncExecuteInfo( ASYNCEXECUTE_CMD_DISPOSING
, this );
229 if (!Application::PostUserEvent( LINK( nullptr, SfxClipboardChangeListener
, AsyncExecuteHdl_Impl
), pInfo
))
233 void SAL_CALL
SfxClipboardChangeListener::changedContents( const datatransfer::clipboard::ClipboardEvent
& )
235 // Make asynchronous call to avoid locking SolarMutex which is the
236 // root for many deadlocks, especially in conjunction with the "Windows"
237 // based single thread apartment clipboard code!
238 AsyncExecuteInfo
* pInfo
= new AsyncExecuteInfo( ASYNCEXECUTE_CMD_CHANGEDCONTENTS
, this );
239 if (!Application::PostUserEvent( LINK( nullptr, SfxClipboardChangeListener
, AsyncExecuteHdl_Impl
), pInfo
))
252 typedef std::list
<uno::Reference
<accessibility::XAccessibleTable
>> XAccessibleTableList
;
257 bool isText(sal_Int16 nRole
)
259 return nRole
== accessibility::AccessibleRole::DOCUMENT_TEXT
;
263 bool isSpreadsheet(sal_Int16 nRole
)
265 return nRole
== accessibility::AccessibleRole::DOCUMENT_SPREADSHEET
;
269 bool isPresentation(sal_Int16 nRole
)
271 return nRole
== accessibility::AccessibleRole::DOCUMENT_PRESENTATION
;
275 bool isDocument(sal_Int16 nRole
)
277 return isText(nRole
) || isSpreadsheet(nRole
) || isPresentation(nRole
);
280 bool hasState(const accessibility::AccessibleEventObject
& aEvent
, ::sal_Int64 nState
)
283 uno::Reference
< accessibility::XAccessibleContext
> xContext(aEvent
.Source
, uno::UNO_QUERY
);
286 ::sal_Int64 nStateSet
= xContext
->getAccessibleStateSet();
287 res
= (nStateSet
& nState
) != 0;
292 bool isFocused(const accessibility::AccessibleEventObject
& aEvent
)
294 return hasState(aEvent
, accessibility::AccessibleStateType::FOCUSED
);
297 uno::Reference
<accessibility::XAccessibleContext
>
298 getParentContext(const uno::Reference
<accessibility::XAccessibleContext
>& xContext
)
300 uno::Reference
<accessibility::XAccessibleContext
> xParentContext
;
301 uno::Reference
<accessibility::XAccessible
> xParent
= xContext
->getAccessibleParent();
303 xParentContext
= uno::Reference
<accessibility::XAccessibleContext
>(xParent
, uno::UNO_QUERY
);
304 return xParentContext
;
307 OUString
selectionEventTypeToString(sal_Int16 nEventId
)
309 using namespace accessibility
;
312 case AccessibleEventId::SELECTION_CHANGED
:
313 return u
"create"_ustr
;
314 case AccessibleEventId::SELECTION_CHANGED_ADD
:
316 case AccessibleEventId::SELECTION_CHANGED_REMOVE
:
317 return u
"remove"_ustr
;
323 bool selectionHasToBeNotified(const uno::Reference
<accessibility::XAccessibleContext
>& xContext
)
325 sal_Int16 nRole
= xContext
->getAccessibleRole();
327 nRole
== accessibility::AccessibleRole::GRAPHIC
||
328 nRole
== accessibility::AccessibleRole::EMBEDDED_OBJECT
||
329 nRole
== accessibility::AccessibleRole::SHAPE
;
332 bool hasToBeActiveForEditing(sal_Int16 nRole
)
335 nRole
== accessibility::AccessibleRole::SHAPE
;
338 sal_Int16
getParentRole(const uno::Reference
<accessibility::XAccessibleContext
>& xContext
)
343 uno::Reference
<accessibility::XAccessibleContext
> xParentContext
= getParentContext(xContext
);
344 if (xParentContext
.is())
345 nRole
= xParentContext
->getAccessibleRole();
350 sal_Int64
getAccessibleSiblingCount(const Reference
<accessibility::XAccessibleContext
>& xContext
)
355 sal_Int64 nChildCount
= 0;
356 Reference
<accessibility::XAccessible
> xParent
= xContext
->getAccessibleParent();
359 Reference
<accessibility::XAccessibleContext
> xParentContext
= xParent
->getAccessibleContext();
360 if (xParentContext
.is())
362 nChildCount
= xParentContext
->getAccessibleChildCount();
365 return nChildCount
- 1;
368 // Put in rAncestorList all ancestors of xTable up to xAncestorTable or
369 // up to the first not-a-table ancestor if xAncestorTable is not an ancestor.
370 // xTable is included in the list, xAncestorTable is not included.
371 // The list is ordered from the ancient ancestor to xTable.
372 // Return true if xAncestorTable is an ancestor of xTable.
373 bool getAncestorList(XAccessibleTableList
& rAncestorList
,
374 const uno::Reference
<accessibility::XAccessibleTable
>& xTable
,
375 const uno::Reference
<accessibility::XAccessibleTable
>& xAncestorTable
= uno::Reference
<accessibility::XAccessibleTable
>())
377 uno::Reference
<accessibility::XAccessibleTable
> xCurrentTable
= xTable
;
378 while (xCurrentTable
.is() && xCurrentTable
!= xAncestorTable
)
380 rAncestorList
.push_front(xCurrentTable
);
382 uno::Reference
<accessibility::XAccessibleContext
> xContext(xCurrentTable
, uno::UNO_QUERY
);
383 xCurrentTable
.clear();
386 uno::Reference
<accessibility::XAccessible
> xParent
= xContext
->getAccessibleParent();
387 uno::Reference
<accessibility::XAccessibleContext
> xParentContext(xParent
, uno::UNO_QUERY
);
388 if (xParentContext
.is()
389 && xParentContext
->getAccessibleRole() == accessibility::AccessibleRole::TABLE_CELL
)
391 uno::Reference
<accessibility::XAccessible
> xCellParent
= xParentContext
->getAccessibleParent();
392 if (xCellParent
.is())
394 xCurrentTable
= uno::Reference
<accessibility::XAccessibleTable
>(xCellParent
, uno::UNO_QUERY
);
400 return xCurrentTable
.is() && xCurrentTable
== xAncestorTable
;
403 void lookForParentTable(const uno::Reference
<accessibility::XAccessibleContext
>& xContext
,
404 uno::Reference
<accessibility::XAccessibleTable
>& xTable
,
405 sal_Int64
& nChildIndex
)
407 using namespace accessibility
;
408 uno::Reference
<XAccessibleContext
> xParentContext
= getParentContext(xContext
);
409 if (xParentContext
.is() && xParentContext
->getAccessibleRole() == AccessibleRole::TABLE_CELL
)
411 uno::Reference
<XAccessible
> xCellParent
= xParentContext
->getAccessibleParent();
412 if (xCellParent
.is())
414 xTable
= uno::Reference
<XAccessibleTable
>(xCellParent
, uno::UNO_QUERY
);
417 nChildIndex
= xParentContext
->getAccessibleIndexInParent();
423 OUString
truncateText(OUString
& sText
, sal_Int32 nNewLength
)
425 // truncate test to given length
426 OUString sNewText
= sText
.copy(0, nNewLength
);
427 // try to truncate at a word
428 nNewLength
= sNewText
.lastIndexOf(" ");
430 sNewText
= sNewText
.copy(0, nNewLength
);
434 std::string
stateSetToString(::sal_Int64 stateSet
)
436 static const std::string states
[35] = {
437 "ACTIVE", "ARMED", "BUSY", "CHECKED", "DEFUNC",
438 "EDITABLE", "ENABLED", "EXPANDABLE", "EXPANDED", "FOCUSABLE",
439 "FOCUSED", "HORIZONTAL", "ICONIFIED", "INDETERMINATE", "MANAGES_DESCENDANTS",
440 "MODAL", "MULTI_LINE", "MULTI_SELECTABLE", "OPAQUE", "PRESSED",
441 "RESIZABLE", "SELECTABLE", "SELECTED", "SENSITIVE", "SHOWING",
442 "SINGLE_LINE", "STALE", "TRANSIENT", "VERTICAL", "VISIBLE",
443 "MOVEABLE", "DEFAULT", "OFFSCREEN", "COLLAPSE", "CHECKABLE"
448 ::sal_Int64 state
= 1;
450 for (int i
= 0; i
< 35; ++i
)
452 if (stateSet
& state
)
462 void aboutView(std::string msg
, const void* pInstance
, const SfxViewShell
* pViewShell
)
467 SAL_INFO("lok.a11y", ">>> " << msg
<< ": instance: " << pInstance
468 << ", VIED ID: " << pViewShell
->GetViewShellId().get() << " <<<");
471 void aboutEvent(std::string msg
, const accessibility::AccessibleEventObject
& aEvent
)
475 uno::Reference
< accessibility::XAccessible
> xSource(aEvent
.Source
, uno::UNO_QUERY
);
478 uno::Reference
< accessibility::XAccessibleContext
> xContext
=
479 xSource
->getAccessibleContext();
483 SAL_INFO("lok.a11y", msg
<< ": event id: " << aEvent
.EventId
484 << "\n xSource: " << xSource
.get()
485 << "\n role: " << xContext
->getAccessibleRole()
486 << "\n name: " << xContext
->getAccessibleName()
487 << "\n index in parent: " << xContext
->getAccessibleIndexInParent()
488 << "\n state set: " << stateSetToString(xContext
->getAccessibleStateSet())
489 << "\n parent: " << xContext
->getAccessibleParent().get()
490 << "\n child count: " << xContext
->getAccessibleChildCount());
494 SAL_INFO("lok.a11y", msg
<< ": event id: " << aEvent
.EventId
495 << ", no accessible context!");
500 SAL_INFO("lok.a11y", msg
<< ": event id: " << aEvent
.EventId
501 << ", no accessible source!");
503 uno::Reference
< accessibility::XAccessible
> xOldValue
;
504 aEvent
.OldValue
>>= xOldValue
;
507 uno::Reference
< accessibility::XAccessibleContext
> xContext
=
508 xOldValue
->getAccessibleContext();
512 SAL_INFO("lok.a11y", msg
<< ": "
513 "\n xOldValue: " << xOldValue
.get()
514 << "\n role: " << xContext
->getAccessibleRole()
515 << "\n name: " << xContext
->getAccessibleName()
516 << "\n index in parent: " << xContext
->getAccessibleIndexInParent()
517 << "\n state set: " << stateSetToString(xContext
->getAccessibleStateSet())
518 << "\n parent: " << xContext
->getAccessibleParent().get()
519 << "\n child count: " << xContext
->getAccessibleChildCount());
522 uno::Reference
< accessibility::XAccessible
> xNewValue
;
523 aEvent
.NewValue
>>= xNewValue
;
526 uno::Reference
< accessibility::XAccessibleContext
> xContext
=
527 xNewValue
->getAccessibleContext();
531 SAL_INFO("lok.a11y", msg
<< ": "
532 "\n xNewValue: " << xNewValue
.get()
533 << "\n role: " << xContext
->getAccessibleRole()
534 << "\n name: " << xContext
->getAccessibleName()
535 << "\n index in parent: " << xContext
->getAccessibleIndexInParent()
536 << "\n state set: " << stateSetToString(xContext
->getAccessibleStateSet())
537 << "\n parent: " << xContext
->getAccessibleParent().get()
538 << "\n child count: " << xContext
->getAccessibleChildCount());
542 catch( const lang::IndexOutOfBoundsException
& /*e*/ )
544 LOK_WARN("lok.a11y", "Focused object has invalid index in parent");
548 sal_Int32
getListPrefixSize(const uno::Reference
<css::accessibility::XAccessibleText
>& xAccText
)
553 OUString sText
= xAccText
->getText();
554 sal_Int32 nLength
= sText
.getLength();
558 css::uno::Sequence
< css::beans::PropertyValue
> aRunAttributeList
;
559 css::uno::Sequence
< OUString
> aRequestedAttributes
= {UNO_NAME_NUMBERING_LEVEL
, UNO_NAME_NUMBERING
};
560 aRunAttributeList
= xAccText
->getCharacterAttributes(0, aRequestedAttributes
);
562 sal_Int16 nLevel
= -1;
563 bool bIsCounted
= false;
564 for (const auto& attribute
: aRunAttributeList
)
566 if (attribute
.Name
.isEmpty())
568 if (attribute
.Name
== UNO_NAME_NUMBERING_LEVEL
)
569 attribute
.Value
>>= nLevel
;
570 else if (attribute
.Name
== UNO_NAME_NUMBERING
)
571 attribute
.Value
>>= bIsCounted
;
573 if (nLevel
< 0 || !bIsCounted
)
576 css::accessibility::TextSegment aTextSegment
=
577 xAccText
->getTextAtIndex(0, css::accessibility::AccessibleTextType::ATTRIBUTE_RUN
);
579 SAL_INFO("lok.a11y", "getListPrefixSize: prefix: " << aTextSegment
.SegmentText
<< ", level: " << nLevel
);
581 return aTextSegment
.SegmentEnd
;
584 void aboutTextFormatting(std::string msg
, const uno::Reference
<css::accessibility::XAccessibleText
>& xAccText
)
589 OUString sText
= xAccText
->getText();
590 sal_Int32 nLength
= sText
.getLength();
594 css::uno::Reference
<css::accessibility::XAccessibleTextAttributes
>
595 xAccTextAttr(xAccText
, uno::UNO_QUERY
);
596 css::uno::Sequence
< OUString
> aRequestedAttributes
;
599 while (nPos
< nLength
)
601 css::accessibility::TextSegment aTextSegment
=
602 xAccText
->getTextAtIndex(nPos
, css::accessibility::AccessibleTextType::ATTRIBUTE_RUN
);
603 SAL_INFO("lok.a11y", msg
<< ": "
604 "text segment: '" << aTextSegment
.SegmentText
605 << "', start: " << aTextSegment
.SegmentStart
606 << ", end: " << aTextSegment
.SegmentEnd
);
608 css::uno::Sequence
< css::beans::PropertyValue
> aRunAttributeList
;
609 if (xAccTextAttr
.is())
611 aRunAttributeList
= xAccTextAttr
->getRunAttributes(nPos
, aRequestedAttributes
);
615 aRunAttributeList
= xAccText
->getCharacterAttributes(nPos
, aRequestedAttributes
);
618 sal_Int32 nSize
= aRunAttributeList
.getLength();
620 msg
<< ": attribute list size: " << nSize
);
624 OUString sAttributes
= u
"{ "_ustr
;
625 for (const auto& attribute
: aRunAttributeList
)
627 if (attribute
.Name
.isEmpty())
630 if (attribute
.Name
== "CharHeight" || attribute
.Name
== "CharWeight")
633 attribute
.Value
>>= fValue
;
634 sValue
= OUString::number(fValue
);
636 else if (attribute
.Name
== "CharPosture")
638 awt::FontSlant nValue
= awt::FontSlant_NONE
;
639 attribute
.Value
>>= nValue
;
640 sValue
= OUString::number(static_cast<unsigned int>(nValue
));
642 else if (attribute
.Name
== "CharUnderline")
644 sal_Int16 nValue
= 0;
645 attribute
.Value
>>= nValue
;
646 sValue
= OUString::number(nValue
);
648 else if (attribute
.Name
== "CharFontName")
650 attribute
.Value
>>= sValue
;
652 else if (attribute
.Name
== "Rsid")
654 sal_uInt32 nValue
= 0;
655 attribute
.Value
>>= nValue
;
656 sValue
= OUString::number(nValue
);
658 else if (attribute
.Name
== UNO_NAME_NUMBERING_LEVEL
)
660 sal_Int16
nValue(-1);
661 attribute
.Value
>>= nValue
;
662 sValue
= OUString::number(nValue
);
664 else if (attribute
.Name
== UNO_NAME_NUMBERING
)
667 attribute
.Value
>>= bValue
;
668 sValue
= OUString::boolean(bValue
);
670 else if (attribute
.Name
== UNO_NAME_NUMBERING_RULES
)
672 attribute
.Value
>>= sValue
;
675 if (!sValue
.isEmpty())
677 if (sAttributes
!= "{ ")
679 sAttributes
+= attribute
.Name
+ ": " + sValue
;
685 msg
<< ": " << sAttributes
);
687 nPos
= aTextSegment
.SegmentEnd
+ 1;
691 void aboutParagraph(const std::string
& msg
, const OUString
& rsParagraphContent
, sal_Int32 nCaretPosition
,
692 sal_Int32 nSelectionStart
, sal_Int32 nSelectionEnd
, sal_Int32 nListPrefixLength
,
695 SAL_INFO("lok.a11y", msg
<< ": "
696 "\n text content: \"" << rsParagraphContent
<< "\""
697 "\n caret pos: " << nCaretPosition
698 << "\n selection: start: " << nSelectionStart
<< ", end: " << nSelectionEnd
699 << "\n list prefix length: " << nListPrefixLength
700 << "\n force: " << force
704 void aboutParagraph(const std::string
& msg
, const uno::Reference
<css::accessibility::XAccessibleText
>& xAccText
,
710 OUString sText
= xAccText
->getText();
711 sal_Int32 nCaretPosition
= xAccText
->getCaretPosition();
712 sal_Int32 nSelectionStart
= xAccText
->getSelectionStart();
713 sal_Int32 nSelectionEnd
= xAccText
->getSelectionEnd();
714 sal_Int32 nListPrefixLength
= getListPrefixSize(xAccText
);
715 aboutParagraph(msg
, sText
, nCaretPosition
, nSelectionStart
, nSelectionEnd
, nListPrefixLength
, force
);
718 void aboutFocusedCellChanged(sal_Int32 nOutCount
, const std::vector
<TableSizeType
>& aInList
,
719 sal_Int32 nRow
, sal_Int32 nCol
, sal_Int32 nRowSpan
, sal_Int32 nColSpan
)
721 std::stringstream inListStream
;
722 inListStream
<< "[ ";
723 for (const auto& rTableSize
: aInList
)
725 inListStream
<< "{ rowCount: " << rTableSize
.nRowCount
<< " colCount: " << rTableSize
.nColCount
<< " } ";
729 SAL_INFO("lok.a11y", "LOKDocumentFocusListener::notifyFocusedCellChanged: "
730 "\n outCount: " << nOutCount
731 << "\n inList: " << inListStream
.str()
732 << "\n row: " << nRow
733 << "\n column: " << nCol
734 << "\n rowSpan: " << nRowSpan
735 << "\n colSpan: " << nColSpan
738 } // anonymous namespace
740 class LOKDocumentFocusListener
:
741 public ::cppu::WeakImplHelper
< accessibility::XAccessibleEventListener
>
743 static constexpr sal_Int64 MAX_ATTACHABLE_CHILDREN
= 100;
745 const SfxViewShell
* m_pViewShell
;
746 sal_Int16 m_nDocumentType
;
747 std::unordered_set
<uno::Reference
<uno::XInterface
>> m_aRefList
;
748 OUString m_sFocusedParagraph
;
749 sal_Int32 m_nCaretPosition
;
750 sal_Int32 m_nSelectionStart
;
751 sal_Int32 m_nSelectionEnd
;
752 sal_Int32 m_nListPrefixLength
;
753 uno::Reference
<accessibility::XAccessibleTable
> m_xLastTable
;
754 OUString m_sSelectedText
;
755 bool m_bIsEditingCell
;
756 // used for text content of a shape
757 bool m_bIsEditingInSelection
;
758 OUString m_sSelectedCellAddress
;
759 uno::Reference
<accessibility::XAccessible
> m_xSelectedObject
;
762 explicit LOKDocumentFocusListener(const SfxViewShell
* pViewShell
);
764 /// @throws lang::IndexOutOfBoundsException
765 /// @throws uno::RuntimeException
766 void attachRecursive(
767 const uno::Reference
< accessibility::XAccessible
>& xAccessible
770 /// @throws lang::IndexOutOfBoundsException
771 /// @throws uno::RuntimeException
772 void attachRecursive(
773 const uno::Reference
< accessibility::XAccessible
>& xAccessible
,
774 const uno::Reference
< accessibility::XAccessibleContext
>& xContext
777 /// @throws lang::IndexOutOfBoundsException
778 /// @throws uno::RuntimeException
779 void attachRecursive(
780 const uno::Reference
< accessibility::XAccessible
>& xAccessible
,
781 const uno::Reference
< accessibility::XAccessibleContext
>& xContext
,
782 const sal_Int64 nStateSet
785 /// @throws lang::IndexOutOfBoundsException
786 /// @throws uno::RuntimeException
787 void detachRecursive(
788 const uno::Reference
< accessibility::XAccessible
>& xAccessible
,
792 /// @throws lang::IndexOutOfBoundsException
793 /// @throws uno::RuntimeException
794 void detachRecursive(
795 const uno::Reference
< accessibility::XAccessibleContext
>& xContext
,
799 /// @throws lang::IndexOutOfBoundsException
800 /// @throws uno::RuntimeException
801 void detachRecursive(
802 const uno::Reference
< accessibility::XAccessibleContext
>& xContext
,
803 const sal_Int64 nStateSet
,
807 /// @throws lang::IndexOutOfBoundsException
808 /// @throws uno::RuntimeException
809 static uno::Reference
< accessibility::XAccessible
> getAccessible(const lang::EventObject
& aEvent
);
812 virtual void SAL_CALL
disposing( const lang::EventObject
& Source
) override
;
814 // XAccessibleEventListener
815 virtual void SAL_CALL
notifyEvent( const accessibility::AccessibleEventObject
& aEvent
) override
;
817 void notifyEditingInSelectionState(bool bParagraph
= true);
818 void notifyFocusedParagraphChanged(bool force
= false);
819 void notifyCaretChanged();
820 void notifyTextSelectionChanged();
821 void notifyFocusedCellChanged(sal_Int32 nOutCount
, const std::vector
<TableSizeType
>& aInList
, sal_Int32 nRow
, sal_Int32 nCol
, sal_Int32 nRowSpan
, sal_Int32 nColSpan
);
822 void notifySelectionChanged(const uno::Reference
<accessibility::XAccessible
>& xAccObj
, const OUString
& sAction
);
824 OUString
getFocusedParagraph() const;
825 int getCaretPosition() const;
828 void paragraphPropertiesToTree(boost::property_tree::ptree
& aPayloadTree
, bool force
= false) const;
829 void paragraphPropertiesToJson(std::string
& aPayload
, bool force
= false) const;
830 bool updateParagraphInfo(const uno::Reference
<css::accessibility::XAccessibleText
>& xAccText
,
831 bool force
, const std::string
& msg
= "");
832 void updateAndNotifyParagraph(const uno::Reference
<css::accessibility::XAccessibleText
>& xAccText
,
833 bool force
, const std::string
& msg
= "");
834 void resetParagraphInfo();
835 void onFocusedParagraphInWriterTable(const uno::Reference
<accessibility::XAccessibleTable
>& xTable
,
836 sal_Int64
& nChildIndex
,
837 const uno::Reference
<accessibility::XAccessibleText
>& xAccText
);
838 uno::Reference
< accessibility::XAccessible
>
839 getSelectedObject(const accessibility::AccessibleEventObject
& aEvent
) const;
840 void onShapeSelectionChanged(const Reference
<accessibility::XAccessible
>& xSelectedObject
,
841 const OUString
& sAction
);
844 LOKDocumentFocusListener::LOKDocumentFocusListener(const SfxViewShell
* pViewShell
)
845 : m_pViewShell(pViewShell
)
847 , m_nCaretPosition(0)
848 , m_nSelectionStart(0)
850 , m_nListPrefixLength(0)
851 , m_bIsEditingCell(false)
852 , m_bIsEditingInSelection(false)
856 void LOKDocumentFocusListener::paragraphPropertiesToTree(boost::property_tree::ptree
& aPayloadTree
, bool force
) const
858 bool bLeftToRight
= m_nCaretPosition
== m_nSelectionEnd
;
859 aPayloadTree
.put("content", m_sFocusedParagraph
.toUtf8().getStr());
860 aPayloadTree
.put("position", m_nCaretPosition
);
861 aPayloadTree
.put("start", bLeftToRight
? m_nSelectionStart
: m_nSelectionEnd
);
862 aPayloadTree
.put("end", bLeftToRight
? m_nSelectionEnd
: m_nSelectionStart
);
863 if (m_nListPrefixLength
> 0)
864 aPayloadTree
.put("listPrefixLength", m_nListPrefixLength
);
866 aPayloadTree
.put("force", 1);
869 void LOKDocumentFocusListener::paragraphPropertiesToJson(std::string
& aPayload
, bool force
) const
871 boost::property_tree::ptree aPayloadTree
;
872 paragraphPropertiesToTree(aPayloadTree
, force
);
873 std::stringstream aStream
;
874 boost::property_tree::write_json(aStream
, aPayloadTree
);
875 aPayload
= aStream
.str();
878 OUString
LOKDocumentFocusListener::getFocusedParagraph() const
880 aboutView("LOKDocumentFocusListener::getFocusedParagraph", this, m_pViewShell
);
881 aboutParagraph("LOKDocumentFocusListener::getFocusedParagraph",
882 m_sFocusedParagraph
, m_nCaretPosition
,
883 m_nSelectionStart
, m_nSelectionEnd
, m_nListPrefixLength
);
885 std::string aPayload
;
886 paragraphPropertiesToJson(aPayload
);
887 OUString sRet
= OUString::fromUtf8(aPayload
);
891 int LOKDocumentFocusListener::getCaretPosition() const
893 aboutView("LOKDocumentFocusListener::getCaretPosition", this, m_pViewShell
);
894 SAL_INFO("lok.a11y", "LOKDocumentFocusListener::getCaretPosition: " << m_nCaretPosition
);
895 return m_nCaretPosition
;
898 // notifyEditingInSelectionState
899 // Used for notifying when editing becomes active/disabled for a shape
900 // bParagraph: should we append currently focused paragraph ?
901 // The problem is that the initially focused paragraph could not be the one user has clicked on,
902 // when there are more than a single paragraph.
903 // So in some case sending the focused paragraph could be misleading.
904 void LOKDocumentFocusListener::notifyEditingInSelectionState(bool bParagraph
)
906 aboutView("LOKDocumentFocusListener::notifyEditingInSelectionState", this, m_pViewShell
);
908 boost::property_tree::ptree aPayloadTree
;
909 bool bIsCell
= !m_sSelectedCellAddress
.isEmpty();
910 aPayloadTree
.put("cell", bIsCell
? 1 : 0);
913 aPayloadTree
.put("enabled", m_bIsEditingCell
? 1 : 0);
914 if (m_bIsEditingCell
)
916 aPayloadTree
.put("selection", m_sSelectedCellAddress
);
918 aPayloadTree
.put("paragraph", m_sFocusedParagraph
);
923 aPayloadTree
.put("enabled", m_bIsEditingInSelection
? 1 : 0);
924 if (m_bIsEditingInSelection
&& m_xSelectedObject
.is())
926 uno::Reference
<accessibility::XAccessibleContext
> xContext
= m_xSelectedObject
->getAccessibleContext();
929 OUString sSelectionDescr
= xContext
->getAccessibleName();
930 sSelectionDescr
= sSelectionDescr
.trim();
931 aPayloadTree
.put("selection", sSelectionDescr
);
933 aPayloadTree
.put("paragraph", m_sFocusedParagraph
);
938 std::stringstream aStream
;
939 boost::property_tree::write_json(aStream
, aPayloadTree
);
940 std::string aPayload
= aStream
.str();
943 SAL_INFO("lok.a11y", "LOKDocumentFocusListener::notifyEditingInSelectionState: payload: \n" << aPayload
);
944 m_pViewShell
->libreOfficeKitViewCallback(LOK_CALLBACK_A11Y_EDITING_IN_SELECTION_STATE
, aPayload
.c_str());
948 /// notifyFocusedParagraphChanged
950 // Notify content, caret position and text selection start/end for the focused paragraph
952 // For focused we don't mean to be necessarily the currently focused accessibility node.
953 // It's enough that the caret is present in the paragraph (position != -1).
954 // In fact each view has its own accessibility node per each text paragraph.
955 // Anyway there can be only one focused accessibility node at time.
956 // So when text changes are performed in one view, both accessibility nodes emit
957 // a text changed event, anyway only the accessibility node belonging to the view
958 // where the text change has occurred is the focused one.
960 // force: when true update the clipboard content even if client is composing.
962 // Usually when editing on the client involves composing the clipboard area updating
963 // is skipped until the composition is over.
964 // On the contrary the composition would be aborted, making dictation not possible.
965 // Anyway when the text change has been performed by another view we are in due
966 // to update the clipboard content even if the user is in the middle of a composition.
967 void LOKDocumentFocusListener::notifyFocusedParagraphChanged(bool force
)
969 aboutView("LOKDocumentFocusListener::notifyFocusedParagraphChanged", this, m_pViewShell
);
970 std::string aPayload
;
971 paragraphPropertiesToJson(aPayload
, force
);
974 aboutParagraph("LOKDocumentFocusListener::notifyFocusedParagraphChanged",
975 m_sFocusedParagraph
, m_nCaretPosition
,
976 m_nSelectionStart
, m_nSelectionEnd
, m_nListPrefixLength
, force
);
978 m_pViewShell
->libreOfficeKitViewCallback(LOK_CALLBACK_A11Y_FOCUS_CHANGED
, aPayload
.c_str());
982 void LOKDocumentFocusListener::notifyCaretChanged()
984 aboutView("LOKDocumentFocusListener::notifyCaretChanged", this, m_pViewShell
);
985 boost::property_tree::ptree aPayloadTree
;
986 aPayloadTree
.put("position", m_nCaretPosition
);
987 std::stringstream aStream
;
988 boost::property_tree::write_json(aStream
, aPayloadTree
);
989 std::string aPayload
= aStream
.str();
992 SAL_INFO("lok.a11y", "LOKDocumentFocusListener::notifyCaretChanged: " << m_nCaretPosition
);
993 m_pViewShell
->libreOfficeKitViewCallback(LOK_CALLBACK_A11Y_CARET_CHANGED
, aPayload
.c_str());
997 void LOKDocumentFocusListener::notifyTextSelectionChanged()
999 aboutView("LOKDocumentFocusListener::notifyTextSelectionChanged", this, m_pViewShell
);
1000 bool bLeftToRight
= m_nCaretPosition
== m_nSelectionEnd
;
1001 boost::property_tree::ptree aPayloadTree
;
1002 aPayloadTree
.put("start", bLeftToRight
? m_nSelectionStart
: m_nSelectionEnd
);
1003 aPayloadTree
.put("end", bLeftToRight
? m_nSelectionEnd
: m_nSelectionStart
);
1004 std::stringstream aStream
;
1005 boost::property_tree::write_json(aStream
, aPayloadTree
);
1006 std::string aPayload
= aStream
.str();
1009 SAL_INFO("lok.a11y", "LOKDocumentFocusListener::notifyTextSelectionChanged: "
1010 "start: " << m_nSelectionStart
<< ", end: " << m_nSelectionEnd
);
1011 m_pViewShell
->libreOfficeKitViewCallback(LOK_CALLBACK_A11Y_TEXT_SELECTION_CHANGED
, aPayload
.c_str());
1015 void LOKDocumentFocusListener::notifyFocusedCellChanged(
1016 sal_Int32 nOutCount
, const std::vector
<TableSizeType
>& aInList
,
1017 sal_Int32 nRow
, sal_Int32 nCol
, sal_Int32 nRowSpan
, sal_Int32 nColSpan
)
1019 aboutView("LOKDocumentFocusListener::notifyTablePositionChanged", this, m_pViewShell
);
1020 boost::property_tree::ptree aPayloadTree
;
1023 aPayloadTree
.put("outCount", nOutCount
);
1025 if (!aInList
.empty())
1027 boost::property_tree::ptree aInListNode
;
1028 for (const auto& rTableSize
: aInList
)
1030 boost::property_tree::ptree aTableSizeNode
;
1031 aTableSizeNode
.put("rowCount", rTableSize
.nRowCount
);
1032 aTableSizeNode
.put("colCount", rTableSize
.nColCount
);
1034 aInListNode
.push_back(std::make_pair(std::string(), aTableSizeNode
));
1036 aPayloadTree
.add_child("inList", aInListNode
);
1039 aPayloadTree
.put("row", nRow
);
1040 aPayloadTree
.put("col", nCol
);
1044 aPayloadTree
.put("rowSpan", nRowSpan
);
1048 aPayloadTree
.put("colSpan", nColSpan
);
1051 boost::property_tree::ptree aContentNode
;
1052 paragraphPropertiesToTree(aContentNode
);
1053 aPayloadTree
.add_child("paragraph", aContentNode
);
1055 std::stringstream aStream
;
1056 boost::property_tree::write_json(aStream
, aPayloadTree
);
1057 std::string aPayload
= aStream
.str();
1060 aboutFocusedCellChanged(nOutCount
, aInList
, nRow
, nCol
, nRowSpan
, nColSpan
);
1061 aboutParagraph("LOKDocumentFocusListener::notifyFocusedCellChanged: paragraph: ",
1062 m_sFocusedParagraph
, m_nCaretPosition
, m_nSelectionStart
, m_nSelectionEnd
,
1063 m_nListPrefixLength
, false);
1065 m_pViewShell
->libreOfficeKitViewCallback(LOK_CALLBACK_A11Y_FOCUSED_CELL_CHANGED
, aPayload
.c_str());
1069 void LOKDocumentFocusListener::notifySelectionChanged(const uno::Reference
<accessibility::XAccessible
>& xAccObj
,
1070 const OUString
& sAction
)
1072 using namespace accessibility
;
1076 aboutView("LOKDocumentFocusListener::notifySelectionChanged", this, m_pViewShell
);
1077 uno::Reference
<XAccessibleContext
> xContext
= xAccObj
->getAccessibleContext();
1080 OUString sName
= xContext
->getAccessibleName();
1081 sName
= sName
.trim();
1082 if (sName
== "GraphicObjectShape")
1085 // check for text content and send it with some limitations:
1086 // no more than 10 paragraphs, no more than 1000 chars
1087 bool bIsCell
= xContext
->getAccessibleRole() == AccessibleRole::TABLE_CELL
;
1088 OUString sTextContent
;
1089 if (sAction
== "create" || sAction
== "add")
1091 const sal_Int64 nMaxJoinedParagraphs
= 10;
1092 const sal_Int32 nMaxTextContentLength
= 1000;
1095 uno::Reference
<XAccessibleText
> xAccText(xAccObj
, uno::UNO_QUERY
);
1098 sTextContent
= xAccText
->getText();
1099 sal_Int32 nTextLength
= sTextContent
.getLength();
1100 if (nTextLength
> nMaxTextContentLength
)
1102 sTextContent
= truncateText(sTextContent
, nMaxTextContentLength
);
1108 sal_Int32 nTotalTextLength
= 0;
1109 sal_Int64 nChildCount
= xContext
->getAccessibleChildCount();
1110 if (nChildCount
> nMaxJoinedParagraphs
)
1111 nChildCount
= nMaxJoinedParagraphs
;
1112 for (sal_Int64 i
= 0; i
< nChildCount
; ++i
)
1114 uno::Reference
<XAccessible
> xChild
= xContext
->getAccessibleChild(i
);
1115 uno::Reference
<XAccessibleText
> xAccText(xChild
, uno::UNO_QUERY
);
1118 OUString sText
= xAccText
->getText();
1119 sal_Int32 nTextLength
= sText
.getLength();
1120 if (nTextLength
< 1)
1122 if (nTotalTextLength
+ nTextLength
< nMaxTextContentLength
)
1124 nTotalTextLength
+= nTextLength
;
1125 sTextContent
+= sText
+ " \n";
1129 // truncate paragraph
1130 sal_Int32 nNewLength
= nMaxTextContentLength
- nTotalTextLength
;
1131 sTextContent
+= truncateText(sText
, nNewLength
);
1138 boost::property_tree::ptree aPayloadTree
;
1139 aPayloadTree
.put("cell", bIsCell
? 1 : 0);
1140 aPayloadTree
.put("action", sAction
);
1141 aPayloadTree
.put("name", sName
);
1142 if (!sTextContent
.isEmpty())
1143 aPayloadTree
.put("text", sTextContent
);
1144 std::stringstream aStream
;
1145 boost::property_tree::write_json(aStream
, aPayloadTree
);
1146 std::string aPayload
= aStream
.str();
1149 SAL_INFO("lok.a11y", "LOKDocumentFocusListener::notifySelectionChanged: "
1150 "action: " << sAction
<< ", name: " << sName
);
1151 m_pViewShell
->libreOfficeKitViewCallback(LOK_CALLBACK_A11Y_SELECTION_CHANGED
, aPayload
.c_str());
1156 void LOKDocumentFocusListener::disposing( const lang::EventObject
& aEvent
)
1158 // Unref the object here, but do not remove as listener since the object
1159 // might no longer be in a state that safely allows this.
1160 if( aEvent
.Source
.is() )
1161 m_aRefList
.erase(aEvent
.Source
);
1165 bool LOKDocumentFocusListener::updateParagraphInfo(const uno::Reference
<css::accessibility::XAccessibleText
>& xAccText
,
1166 bool force
, const std::string
& msg
)
1171 bool bNotify
= false;
1172 // If caret is present inside the paragraph (pos != -1), it means that paragraph has focus in the current view.
1173 sal_Int32 nCaretPosition
= xAccText
->getCaretPosition();
1174 if (nCaretPosition
>= 0)
1176 OUString sText
= xAccText
->getText();
1177 m_nCaretPosition
= nCaretPosition
;
1178 m_nSelectionStart
= xAccText
->getSelectionStart();
1179 m_nSelectionEnd
= xAccText
->getSelectionEnd();
1180 m_nListPrefixLength
= getListPrefixSize(xAccText
);
1182 // Inside a text shape when there is no selection, selection-start and selection-end are
1183 // set to current caret position instead of -1. Moreover, inside a text shape pressing
1184 // delete or backspace with an empty selection really deletes text and not only the empty
1185 // selection as it occurs in a text paragraph in Writer.
1186 // So whenever selection-start == selection-end, and we are inside a shape we need
1187 // to set these parameters to -1 in order to have the client to handle delete and
1188 // backspace properly.
1189 if (m_nSelectionStart
== m_nSelectionEnd
&& m_nSelectionStart
!= -1)
1191 uno::Reference
<accessibility::XAccessibleContext
> xContext(xAccText
, uno::UNO_QUERY
);
1192 sal_Int16 nParentRole
= getParentRole(xContext
);
1193 if (nParentRole
== accessibility::AccessibleRole::SHAPE
||
1194 nParentRole
== accessibility::AccessibleRole::TEXT_FRAME
) // spreadsheet cell editing
1195 m_nSelectionStart
= m_nSelectionEnd
= -1;
1198 // In case only caret position or text selection are different we can rely on specific events.
1199 if (m_sFocusedParagraph
!= sText
)
1201 m_sFocusedParagraph
= sText
;
1207 SAL_WARN("lok.a11y",
1208 "LOKDocumentFocusListener::updateParagraphInfo: skipped since no caret is present");
1211 std::string header
= "LOKDocumentFocusListener::updateParagraphInfo";
1213 header
+= ": " + msg
;
1214 aboutParagraph(header
, xAccText
, force
);
1219 void LOKDocumentFocusListener::updateAndNotifyParagraph(
1220 const uno::Reference
<css::accessibility::XAccessibleText
>& xAccText
,
1221 bool force
, const std::string
& msg
)
1223 if (updateParagraphInfo(xAccText
, force
, msg
))
1224 notifyFocusedParagraphChanged(force
);
1227 void LOKDocumentFocusListener::resetParagraphInfo()
1229 m_sFocusedParagraph
= "";
1230 m_nCaretPosition
= 0;
1231 m_nSelectionStart
= -1;
1232 m_nSelectionEnd
= -1;
1233 m_nListPrefixLength
= 0;
1236 // For a presentation document when an accessible event of type SELECTION_CHANGED_XXX occurs
1237 // the selected (or unselected) object is put in NewValue, instead for a text document
1238 // the selected object is put in Source.
1239 // The following function helps to retrieve the selected object independently on where it has been put.
1240 uno::Reference
< accessibility::XAccessible
>
1241 LOKDocumentFocusListener::getSelectedObject(const accessibility::AccessibleEventObject
& aEvent
) const
1243 uno::Reference
< accessibility::XAccessible
> xSelectedObject
;
1244 if (isText(m_nDocumentType
))
1246 xSelectedObject
.set(aEvent
.Source
, uno::UNO_QUERY
);
1250 aEvent
.NewValue
>>= xSelectedObject
;
1252 return xSelectedObject
;
1255 void LOKDocumentFocusListener::onShapeSelectionChanged(
1256 const uno::Reference
<accessibility::XAccessible
>& xSelectedObject
,
1257 const OUString
& sAction
)
1259 // when a shape is selected or unselected we could need to notify that text content editing
1260 // is no more active, that allows on the client side to prevent default input.
1261 resetParagraphInfo();
1262 if (m_bIsEditingInSelection
)
1264 m_bIsEditingInSelection
= false;
1265 notifyEditingInSelectionState();
1267 notifySelectionChanged(xSelectedObject
, sAction
);
1270 void LOKDocumentFocusListener::onFocusedParagraphInWriterTable(
1271 const uno::Reference
<accessibility::XAccessibleTable
>& xTable
,
1272 sal_Int64
& nChildIndex
,
1273 const uno::Reference
<accessibility::XAccessibleText
>& xAccText
1276 std::vector
<TableSizeType
> aInList
;
1277 sal_Int32 nOutCount
= 0;
1279 if (m_xLastTable
.is())
1281 if (xTable
!= m_xLastTable
)
1283 // do we get in one or more nested tables ?
1284 // check if xTable is a descendant of m_xLastTable
1285 XAccessibleTableList newTableAncestorList
;
1286 bool isLastAncestorOfNew
= getAncestorList(newTableAncestorList
, xTable
, m_xLastTable
);
1287 bool isNewAncestorOfLast
= false;
1288 if (!isLastAncestorOfNew
)
1290 // do we get out of one or more nested tables ?
1291 // check if m_xLastTable is a descendant of xTable
1292 XAccessibleTableList lastTableAncestorList
;
1293 isNewAncestorOfLast
= getAncestorList(lastTableAncestorList
, m_xLastTable
, xTable
);
1294 // we have to notify "out of table" for all m_xLastTable ancestors up to xTable
1295 // or the first not-a-table ancestor
1296 nOutCount
= lastTableAncestorList
.size();
1298 if (isLastAncestorOfNew
|| !isNewAncestorOfLast
)
1300 // we have to notify row/col count for all xTable ancestors starting from the ancestor
1301 // which is a child of m_xLastTable (isLastAncestorOfNew) or the first not-a-table ancestor
1302 for (const auto& ancestor
: newTableAncestorList
)
1304 TableSizeType aTableSize
{ancestor
->getAccessibleRowCount(),
1305 ancestor
->getAccessibleColumnCount()};
1306 aInList
.push_back(aTableSize
);
1313 // cursor was not inside any table and gets inside one or more tables
1314 // we have to notify row/col count for all xTable ancestors starting from first not-a-table ancestor
1315 XAccessibleTableList newTableAncestorList
;
1316 getAncestorList(newTableAncestorList
, xTable
);
1317 for (const auto& ancestor
: newTableAncestorList
)
1319 TableSizeType aTableSize
{ancestor
->getAccessibleRowCount(),
1320 ancestor
->getAccessibleColumnCount()};
1321 aInList
.push_back(aTableSize
);
1325 // we have to notify current row/col of xTable and related row/col span
1326 sal_Int32 nRow
= xTable
->getAccessibleRow(nChildIndex
);
1327 sal_Int32 nCol
= xTable
->getAccessibleColumn(nChildIndex
);
1328 sal_Int32 nRowSpan
= xTable
->getAccessibleRowExtentAt(nRow
, nCol
);
1329 sal_Int32 nColSpan
= xTable
->getAccessibleColumnExtentAt(nRow
, nCol
);
1331 m_xLastTable
= xTable
;
1332 updateParagraphInfo(xAccText
, false, "STATE_CHANGED: FOCUSED");
1333 notifyFocusedCellChanged(nOutCount
, aInList
, nRow
, nCol
, nRowSpan
, nColSpan
);
1336 void LOKDocumentFocusListener::notifyEvent(const accessibility::AccessibleEventObject
& aEvent
)
1338 using namespace accessibility
;
1339 aboutView("LOKDocumentFocusListener::notifyEvent", this, m_pViewShell
);
1343 aboutEvent("LOKDocumentFocusListener::notifyEvent", aEvent
);
1345 switch (aEvent
.EventId
)
1347 case AccessibleEventId::STATE_CHANGED
:
1350 sal_Int64 nState
= accessibility::AccessibleStateType::INVALID
;
1351 aEvent
.NewValue
>>= nState
;
1352 sal_Int64 nOldState
= accessibility::AccessibleStateType::INVALID
;
1353 aEvent
.OldValue
>>= nOldState
;
1354 SAL_INFO("lok.a11y", "LOKDocumentFocusListener::notifyEvent: STATE_CHANGED: "
1355 " New State: " << stateSetToString(nState
)
1356 << ", Old State: " << stateSetToString(nOldState
));
1359 uno::Reference
< XAccessible
> xAccessibleObject
= getAccessible(aEvent
);
1360 if (!xAccessibleObject
.is())
1362 uno::Reference
<XAccessibleContext
> xContext(aEvent
.Source
, uno::UNO_QUERY
);
1366 sal_Int16 nRole
= xContext
->getAccessibleRole();
1368 if (nRole
== AccessibleRole::PARAGRAPH
)
1370 uno::Reference
<XAccessibleText
> xAccText(xAccessibleObject
, uno::UNO_QUERY
);
1376 case AccessibleStateType::ACTIVE
:
1378 if (!m_bIsEditingInSelection
&& hasToBeActiveForEditing(getParentRole(xContext
)))
1380 m_bIsEditingInSelection
= true;
1384 case AccessibleStateType::FOCUSED
:
1386 if (m_bIsEditingInSelection
&& m_xSelectedObject
.is())
1388 updateParagraphInfo(xAccText
, true, "STATE_CHANGED: FOCUSED");
1389 notifyEditingInSelectionState(getAccessibleSiblingCount(xContext
) == 0);
1390 notifyFocusedParagraphChanged(true);
1391 // we clear selected object so when editing is over but shape is
1392 // still selected, the selection event is notified the same to the client
1393 m_xSelectedObject
.clear();
1396 if (isText(m_nDocumentType
))
1398 // check if we are inside a table: in case notify table and current cell info
1399 bool isInsideTable
= false;
1400 uno::Reference
<XAccessibleTable
> xTable
;
1401 sal_Int64 nChildIndex
;
1402 lookForParentTable(xContext
, xTable
, nChildIndex
);
1405 onFocusedParagraphInWriterTable(xTable
, nChildIndex
, xAccText
);
1406 isInsideTable
= true;
1408 // paragraph is not inside any table
1411 if (m_xLastTable
.is())
1413 // we get out one or more tables
1414 // we have to notify "out of table" for all m_xLastTable ancestors
1415 // up to the first not-a-table ancestor
1416 XAccessibleTableList lastTableAncestorList
;
1417 getAncestorList(lastTableAncestorList
, m_xLastTable
);
1418 sal_Int32 nOutCount
= lastTableAncestorList
.size();
1419 // no more inside a table
1420 m_xLastTable
.clear();
1422 std::vector
<TableSizeType
> aInList
;
1423 updateParagraphInfo(xAccText
, false, "STATE_CHANGED: FOCUSED");
1424 notifyFocusedCellChanged(nOutCount
, aInList
, -1, -1, 1, 1);
1428 updateAndNotifyParagraph(xAccText
, false, "STATE_CHANGED: FOCUSED");
1432 else if (isSpreadsheet(m_nDocumentType
))
1434 if (m_bIsEditingCell
)
1436 if (!hasState(aEvent
, AccessibleStateType::ACTIVE
))
1438 SAL_WARN("lok.a11y",
1439 "LOKDocumentFocusListener::notifyEvent: FOCUSED: "
1440 "cell not ACTIVE for editing yet");
1443 else if (m_xSelectedObject
.is())
1445 updateParagraphInfo(xAccText
, true, "STATE_CHANGED: ACTIVE");
1446 notifyEditingInSelectionState(getAccessibleSiblingCount(xContext
) == 0);
1447 notifyFocusedParagraphChanged(true);
1448 m_xSelectedObject
.clear();
1452 updateAndNotifyParagraph(xAccText
, false, "STATE_CHANGED: FOCUSED");
1455 else if (isPresentation(m_nDocumentType
))
1457 updateAndNotifyParagraph(xAccText
, false, "STATE_CHANGED: FOCUSED");
1459 aboutTextFormatting("LOKDocumentFocusListener::notifyEvent: STATE_CHANGED: FOCUSED", xAccText
);
1469 case AccessibleEventId::CARET_CHANGED
:
1471 sal_Int32 nNewPos
= -1;
1472 aEvent
.NewValue
>>= nNewPos
;
1473 sal_Int32 nOldPos
= -1;
1474 aEvent
.OldValue
>>= nOldPos
;
1478 SAL_INFO("lok.a11y", "LOKDocumentFocusListener::notifyEvent: CARET_CHANGED: "
1479 "new pos: " << nNewPos
<< ", nOldPos: " << nOldPos
);
1481 uno::Reference
<XAccessibleText
> xAccText(getAccessible(aEvent
), uno::UNO_QUERY
);
1484 m_nCaretPosition
= nNewPos
;
1485 // Let's say we are in the following case: 'Hello wor|ld',
1486 // where '|' is the cursor position for the current view.
1487 // Suppose that in another view it's typed <enter> soon before 'world'.
1488 // Now the new paragraph content and caret position is: 'wor|ld'.
1489 // Anyway no new paragraph focused event is emitted for current view.
1490 // Only a new caret position event is emitted.
1491 // So we could need to notify a new focused paragraph changed message.
1492 if (!isFocused(aEvent
))
1494 if (updateParagraphInfo(xAccText
, false, "CARET_CHANGED"))
1495 notifyFocusedParagraphChanged(true);
1499 notifyCaretChanged();
1501 aboutParagraph("LOKDocumentFocusListener::notifyEvent: CARET_CHANGED", xAccText
);
1506 case AccessibleEventId::TEXT_CHANGED
:
1508 TextSegment aDeletedText
;
1509 TextSegment aInsertedText
;
1511 if (aEvent
.OldValue
>>= aDeletedText
)
1513 SAL_INFO("lok.a11y", "LOKDocumentFocusListener::notifyEvent: TEXT_CHANGED: "
1514 "deleted text: >" << aDeletedText
.SegmentText
<< "<");
1516 if (aEvent
.NewValue
>>= aInsertedText
)
1518 SAL_INFO("lok.a11y", "LOKDocumentFocusListener::notifyEvent: TEXT_CHANGED: "
1519 "inserted text: >" << aInsertedText
.SegmentText
<< "<");
1521 uno::Reference
<XAccessibleText
> xAccText(getAccessible(aEvent
), uno::UNO_QUERY
);
1523 // When the change has been performed in another view we need to force
1524 // paragraph content updating on the client, even if current editing involves composing.
1525 // We make a guess that if the paragraph accessibility node is not focused,
1526 // it means that the text change has been performed in another view.
1527 updateAndNotifyParagraph(xAccText
, !isFocused(aEvent
), "TEXT_CHANGED");
1531 case AccessibleEventId::TEXT_SELECTION_CHANGED
:
1533 if (!isFocused(aEvent
))
1535 SAL_WARN("lok.a11y",
1536 "LOKDocumentFocusListener::notifyEvent: TEXT_SELECTION_CHANGED: "
1537 "skip non focused paragraph");
1541 uno::Reference
<XAccessibleText
> xAccText(getAccessible(aEvent
), uno::UNO_QUERY
);
1544 // We send a message to client also when start/end are -1, in this way the client knows
1545 // if a text selection object exists or not. That's needed because of the odd behavior
1546 // occurring when <backspace>/<delete> are hit and a text selection is empty,
1547 // but it still exists.
1548 // Such keys delete the empty selection instead of the previous/next char.
1549 updateParagraphInfo(xAccText
, false, "TEXT_SELECTION_CHANGED");
1551 m_sSelectedText
= xAccText
->getSelectedText();
1552 SAL_INFO("lok.a11y",
1553 "LOKDocumentFocusListener::notifyEvent: TEXT_SELECTION_CHANGED: selected text: >"
1554 << m_sSelectedText
<< "<");
1556 // Calc: when editing a formula send the update content
1557 if (m_bIsEditingCell
)
1559 OUString sText
= xAccText
->getText();
1560 if (!m_sSelectedCellAddress
.isEmpty() &&
1561 !m_sSelectedText
.isEmpty() && sText
.startsWith("="))
1563 notifyFocusedParagraphChanged();
1566 notifyTextSelectionChanged();
1570 case AccessibleEventId::SELECTION_CHANGED
:
1571 case AccessibleEventId::SELECTION_CHANGED_REMOVE
:
1573 uno::Reference
< XAccessible
> xSelectedObject
= getSelectedObject(aEvent
);
1574 if (!xSelectedObject
.is())
1576 uno::Reference
< XAccessibleContext
> xContext
= xSelectedObject
->getAccessibleContext();
1580 if (aEvent
.EventId
== AccessibleEventId::SELECTION_CHANGED_REMOVE
)
1581 m_xSelectedObject
.clear();
1582 else if (m_xSelectedObject
.is() && m_xSelectedObject
== xSelectedObject
)
1583 return; // selecting the same object; note: on editing selected object is cleared
1585 m_xSelectedObject
= xSelectedObject
;
1586 SAL_INFO("lok.a11y", "LOKDocumentFocusListener::notifyEvent: SELECTION_CHANGED: "
1587 "m_xSelectedObject.is(): " << m_xSelectedObject
.is());
1589 OUString sAction
= selectionEventTypeToString(aEvent
.EventId
);
1590 sal_Int16 nRole
= xContext
->getAccessibleRole();
1593 case AccessibleRole::GRAPHIC
:
1594 case AccessibleRole::EMBEDDED_OBJECT
:
1595 case AccessibleRole::SHAPE
:
1597 onShapeSelectionChanged(xSelectedObject
, sAction
);
1600 case AccessibleRole::TABLE_CELL
:
1602 notifySelectionChanged(xSelectedObject
, sAction
);
1604 if (aEvent
.EventId
== AccessibleEventId::SELECTION_CHANGED
)
1606 m_sSelectedCellAddress
= xContext
->getAccessibleName();
1607 if (m_bIsEditingCell
&& !m_sSelectedCellAddress
.isEmpty())
1609 // Check cell address: "$Sheet1.A10".
1610 // On cell editing SELECTION_CHANGED is not emitted when selection is expanded.
1611 // So selection can't be a cell range.
1612 sal_Int32 nDotIndex
= m_sSelectedText
.indexOf('.');
1613 OUString sCellAddress
= m_sSelectedText
.copy(nDotIndex
+ 1);
1614 SAL_INFO("lok.a11y", "LOKDocumentFocusListener::notifyEvent: SELECTION_CHANGED: "
1615 "cell address: >" << sCellAddress
<< "<");
1616 if (m_sSelectedCellAddress
== sCellAddress
)
1618 notifyFocusedParagraphChanged();
1619 notifyTextSelectionChanged();
1630 case AccessibleEventId::CHILD
:
1632 uno::Reference
< accessibility::XAccessible
> xChild
;
1633 if( (aEvent
.OldValue
>>= xChild
) && xChild
.is() )
1634 detachRecursive(xChild
);
1636 if( (aEvent
.NewValue
>>= xChild
) && xChild
.is() )
1637 attachRecursive(xChild
);
1641 case AccessibleEventId::INVALIDATE_ALL_CHILDREN
:
1643 SAL_INFO("lok.a11y", "Invalidate all children called");
1650 catch( const lang::IndexOutOfBoundsException
& )
1652 LOK_WARN("lok.a11y",
1653 "LOKDocumentFocusListener::notifyEvent:Focused object has invalid index in parent");
1657 uno::Reference
< accessibility::XAccessible
> LOKDocumentFocusListener::getAccessible(const lang::EventObject
& aEvent
)
1659 uno::Reference
< accessibility::XAccessible
> xAccessible(aEvent
.Source
, uno::UNO_QUERY
);
1661 if( xAccessible
.is() )
1664 SAL_WARN("lok.a11y",
1665 "LOKDocumentFocusListener::getAccessible: Event source doesn't implement XAccessible.");
1667 uno::Reference
< accessibility::XAccessibleContext
> xContext(aEvent
.Source
, uno::UNO_QUERY
);
1671 uno::Reference
< accessibility::XAccessible
> xParent( xContext
->getAccessibleParent() );
1674 uno::Reference
< accessibility::XAccessibleContext
> xParentContext( xParent
->getAccessibleContext() );
1675 if( xParentContext
.is() )
1677 return xParentContext
->getAccessibleChild( xContext
->getAccessibleIndexInParent() );
1682 LOK_WARN("lok.a11y",
1683 "LOKDocumentFocusListener::getAccessible: Can't get any accessible object from event source.");
1685 return uno::Reference
< accessibility::XAccessible
>();
1688 void LOKDocumentFocusListener::attachRecursive(
1689 const uno::Reference
< accessibility::XAccessible
>& xAccessible
1692 LOK_INFO("lok.a11y", "LOKDocumentFocusListener::attachRecursive(1): xAccessible: " << xAccessible
.get());
1694 uno::Reference
< accessibility::XAccessibleContext
> xContext
=
1695 xAccessible
->getAccessibleContext();
1698 attachRecursive(xAccessible
, xContext
);
1701 void LOKDocumentFocusListener::attachRecursive(
1702 const uno::Reference
< accessibility::XAccessible
>& xAccessible
,
1703 const uno::Reference
< accessibility::XAccessibleContext
>& xContext
1708 LOK_INFO("lok.a11y", "LOKDocumentFocusListener::attachRecursive(2): xAccessible: "
1709 << xAccessible
.get() << ", role: " << xContext
->getAccessibleRole()
1710 << ", name: " << xContext
->getAccessibleName()
1711 << ", parent: " << xContext
->getAccessibleParent().get()
1712 << ", child count: " << xContext
->getAccessibleChildCount());
1714 sal_Int64 nStateSet
= xContext
->getAccessibleStateSet();
1716 if (!m_bIsEditingCell
)
1718 ::rtl::OUString sName
= xContext
->getAccessibleName();
1719 m_bIsEditingCell
= sName
.startsWith("Cell");
1722 attachRecursive(xAccessible
, xContext
, nStateSet
);
1724 catch (const uno::Exception
& e
)
1726 LOK_WARN("lok.a11y", "LOKDocumentFocusListener::attachRecursive(2): raised exception: " << e
.Message
);
1730 void LOKDocumentFocusListener::attachRecursive(
1731 const uno::Reference
< accessibility::XAccessible
>& xAccessible
,
1732 const uno::Reference
< accessibility::XAccessibleContext
>& xContext
,
1733 const sal_Int64 nStateSet
1736 aboutView("LOKDocumentFocusListener::attachRecursive (3)", this, m_pViewShell
);
1737 SAL_INFO("lok.a11y", "LOKDocumentFocusListener::attachRecursive(3) #1: this: " << this
1738 << ", xAccessible: " << xAccessible
.get()
1739 << ", role: " << xContext
->getAccessibleRole()
1740 << ", name: " << xContext
->getAccessibleName()
1741 << ", index in parent: " << xContext
->getAccessibleIndexInParent()
1742 << ", state: " << stateSetToString(nStateSet
)
1743 << ", parent: " << xContext
->getAccessibleParent().get()
1744 << ", child count: " << xContext
->getAccessibleChildCount());
1746 uno::Reference
< accessibility::XAccessibleEventBroadcaster
> xBroadcaster(xContext
, uno::UNO_QUERY
);
1748 if (!xBroadcaster
.is())
1750 SAL_INFO("lok.a11y", "LOKDocumentFocusListener::attachRecursive(3) #2: xBroadcaster.is()");
1751 // If not already done, add the broadcaster to the list and attach as listener.
1752 const uno::Reference
< uno::XInterface
>& xInterface
= xBroadcaster
;
1753 if( m_aRefList
.insert(xInterface
).second
)
1755 SAL_INFO("lok.a11y", "LOKDocumentFocusListener::attachRecursive(3) #3: m_aRefList.insert(xInterface).second");
1756 xBroadcaster
->addAccessibleEventListener(static_cast< accessibility::XAccessibleEventListener
*>(this));
1758 if (isDocument(xContext
->getAccessibleRole()))
1760 m_nDocumentType
= xContext
->getAccessibleRole();
1763 if (!(nStateSet
& accessibility::AccessibleStateType::MANAGES_DESCENDANTS
))
1765 if ((nStateSet
& accessibility::AccessibleStateType::SELECTED
) &&
1766 selectionHasToBeNotified(xContext
))
1768 uno::Reference
< accessibility::XAccessible
> xAccObj(xContext
, uno::UNO_QUERY
);
1769 onShapeSelectionChanged(xAccObj
, u
"create"_ustr
);
1772 sal_Int64 nmax
= xContext
->getAccessibleChildCount();
1773 if( nmax
> MAX_ATTACHABLE_CHILDREN
)
1774 nmax
= MAX_ATTACHABLE_CHILDREN
;
1776 for( sal_Int64 n
= 0; n
< nmax
; n
++ )
1778 uno::Reference
< accessibility::XAccessible
> xChild( xContext
->getAccessibleChild( n
) );
1781 attachRecursive(xChild
);
1786 // Usually, when the document is loaded, a CARET_CHANGED accessibility event is automatically emitted
1787 // for the first paragraph. That allows to notify the paragraph content to the client, even if no input
1788 // event occurred yet. However, when switching to a11y enabled in the client and in Cypress tests
1789 // no accessibility event is automatically emitted until some input event occurs.
1790 // So we use the following workaround to notify the content of the focused paragraph,
1791 // without waiting for an input event.
1792 // Here we update the paragraph info related to the focused paragraph,
1793 // later when afterCallbackRegistered is executed we notify the paragraph content.
1794 sal_Int64 nChildCount
= xContext
->getAccessibleChildCount();
1795 if (nChildCount
> 0 && nChildCount
< 10)
1797 for (sal_Int64 n
= 0; n
< nChildCount
; ++n
)
1799 uno::Reference
< accessibility::XAccessible
> xChild(xContext
->getAccessibleChild(n
));
1802 uno::Reference
<css::accessibility::XAccessibleText
> xAccText(xChild
, uno::UNO_QUERY
);
1805 sal_Int32 nPos
= xAccText
->getCaretPosition();
1808 attachRecursive(xChild
);
1809 updateParagraphInfo(xAccText
, false, "LOKDocumentFocusListener::attachRecursive(3)");
1820 void LOKDocumentFocusListener::detachRecursive(
1821 const uno::Reference
< accessibility::XAccessible
>& xAccessible
,
1825 uno::Reference
< accessibility::XAccessibleContext
> xContext
=
1826 xAccessible
->getAccessibleContext();
1829 detachRecursive(xContext
, bForce
);
1832 void LOKDocumentFocusListener::detachRecursive(
1833 const uno::Reference
< accessibility::XAccessibleContext
>& xContext
,
1837 aboutView("LOKDocumentFocusListener::detachRecursive (2)", this, m_pViewShell
);
1838 sal_Int64 nStateSet
= xContext
->getAccessibleStateSet();
1840 SAL_INFO("lok.a11y", "LOKDocumentFocusListener::detachRecursive(2): this: " << this
1841 << ", name: " << xContext
->getAccessibleName()
1842 << ", parent: " << xContext
->getAccessibleParent().get()
1843 << ", child count: " << xContext
->getAccessibleChildCount());
1845 if (m_bIsEditingCell
)
1847 ::rtl::OUString sName
= xContext
->getAccessibleName();
1848 m_bIsEditingCell
= !sName
.startsWith("Cell");
1849 if (!m_bIsEditingCell
)
1851 m_sFocusedParagraph
= "";
1852 m_nCaretPosition
= 0;
1853 notifyFocusedParagraphChanged();
1857 detachRecursive(xContext
, nStateSet
, bForce
);
1860 void LOKDocumentFocusListener::detachRecursive(
1861 const uno::Reference
< accessibility::XAccessibleContext
>& xContext
,
1862 const sal_Int64 nStateSet
,
1866 uno::Reference
< accessibility::XAccessibleEventBroadcaster
> xBroadcaster(xContext
, uno::UNO_QUERY
);
1868 if (xBroadcaster
.is() && 0 < m_aRefList
.erase(xBroadcaster
))
1870 xBroadcaster
->removeAccessibleEventListener(static_cast< accessibility::XAccessibleEventListener
*>(this));
1872 if ((nStateSet
& accessibility::AccessibleStateType::SELECTED
) &&
1873 selectionHasToBeNotified(xContext
))
1875 uno::Reference
< accessibility::XAccessible
> xAccObj(xContext
, uno::UNO_QUERY
);
1876 onShapeSelectionChanged(xAccObj
, u
"delete"_ustr
);
1879 if (bForce
|| !(nStateSet
& accessibility::AccessibleStateType::MANAGES_DESCENDANTS
))
1881 sal_Int64 nmax
= xContext
->getAccessibleChildCount();
1882 if (nmax
> MAX_ATTACHABLE_CHILDREN
)
1883 nmax
= MAX_ATTACHABLE_CHILDREN
;
1884 for (sal_Int64 n
= 0; n
< nmax
; n
++)
1886 uno::Reference
< accessibility::XAccessible
> xChild(xContext
->getAccessibleChild(n
));
1889 detachRecursive(xChild
);
1895 sal_uInt32
SfxViewShell_Impl::m_nLastViewShellId
= 0;
1897 SfxViewShell_Impl::SfxViewShell_Impl(SfxViewShellFlags
const nFlags
, ViewShellDocId nDocId
)
1898 : m_bHasPrintOptions(nFlags
& SfxViewShellFlags::HAS_PRINTOPTIONS
)
1899 , m_nFamily(0xFFFF) // undefined, default set by TemplateDialog
1900 , m_pLibreOfficeKitViewCallback(nullptr)
1901 , m_bTiledSearching(false)
1902 , m_nViewShellId(SfxViewShell_Impl::m_nLastViewShellId
++)
1907 SfxViewShell_Impl::~SfxViewShell_Impl()
1911 std::vector
< SfxInPlaceClient
* >& SfxViewShell_Impl::GetIPClients_Impl()
1916 SFX_IMPL_SUPERCLASS_INTERFACE(SfxViewShell
,SfxShell
)
1918 void SfxViewShell::InitInterface_Impl()
1923 /** search for a filter name dependent on type and module
1925 static OUString
impl_retrieveFilterNameFromTypeAndModule(
1926 const css::uno::Reference
< css::container::XContainerQuery
>& rContainerQuery
,
1927 const OUString
& rType
,
1928 const OUString
& rModuleIdentifier
,
1929 const sal_Int32 nFlags
)
1931 // Retrieve filter from type
1932 css::uno::Sequence
< css::beans::NamedValue
> aQuery
{
1933 { u
"Type"_ustr
, css::uno::Any( rType
) },
1934 { u
"DocumentService"_ustr
, css::uno::Any( rModuleIdentifier
) }
1937 css::uno::Reference
< css::container::XEnumeration
> xEnumeration
=
1938 rContainerQuery
->createSubSetEnumerationByProperties( aQuery
);
1940 OUString aFoundFilterName
;
1941 while ( xEnumeration
->hasMoreElements() )
1943 ::comphelper::SequenceAsHashMap
aFilterPropsHM( xEnumeration
->nextElement() );
1944 sal_Int32 nFilterFlags
= aFilterPropsHM
.getUnpackedValueOrDefault(
1948 if ( nFilterFlags
& nFlags
)
1950 aFoundFilterName
= aFilterPropsHM
.getUnpackedValueOrDefault(u
"Name"_ustr
, OUString());
1955 return aFoundFilterName
;
1960 /** search for an internal typename, which map to the current app module
1961 and map also to a "family" of file formats as e.g. PDF/MS Doc/OOo Doc.
1971 static OUString
impl_searchFormatTypeForApp(const css::uno::Reference
< css::frame::XFrame
>& xFrame
,
1972 ETypeFamily eTypeFamily
)
1976 const css::uno::Reference
< css::uno::XComponentContext
>& xContext (::comphelper::getProcessComponentContext());
1977 css::uno::Reference
< css::frame::XModuleManager2
> xModuleManager(css::frame::ModuleManager::create(xContext
));
1979 OUString sModule
= xModuleManager
->identify(xFrame
);
1986 if ( sModule
== "com.sun.star.text.TextDocument" )
1987 sType
= "writer_MS_Word_2007";
1989 if ( sModule
== "com.sun.star.sheet.SpreadsheetDocument" )
1990 sType
= "MS Excel 2007 XML";
1992 if ( sModule
== "com.sun.star.presentation.PresentationDocument" )
1993 sType
= "MS PowerPoint 2007 XML";
1999 if ( sModule
== "com.sun.star.text.TextDocument" )
2002 if ( sModule
== "com.sun.star.sheet.SpreadsheetDocument" )
2005 if ( sModule
== "com.sun.star.drawing.DrawingDocument" )
2008 if ( sModule
== "com.sun.star.presentation.PresentationDocument" )
2016 catch (const css::uno::RuntimeException
&)
2020 catch (const css::uno::Exception
&)
2027 void SfxViewShell::NewIPClient_Impl( SfxInPlaceClient
*pIPClient
)
2029 pImpl
->GetIPClients_Impl().push_back(pIPClient
);
2032 void SfxViewShell::IPClientGone_Impl( SfxInPlaceClient
const *pIPClient
)
2034 std::vector
< SfxInPlaceClient
* >& pClients
= pImpl
->GetIPClients_Impl();
2036 auto it
= std::find(pClients
.begin(), pClients
.end(), pIPClient
);
2037 if (it
!= pClients
.end())
2038 pClients
.erase( it
);
2042 void SfxViewShell::ExecMisc_Impl( SfxRequest
&rReq
)
2044 const sal_uInt16 nId
= rReq
.GetSlot();
2047 case SID_STYLE_FAMILY
:
2049 const SfxUInt16Item
* pItem
= rReq
.GetArg
<SfxUInt16Item
>(nId
);
2052 pImpl
->m_nFamily
= pItem
->GetValue();
2056 case SID_ACTIVATE_STYLE_APPLY
:
2058 uno::Reference
< frame::XFrame
> xFrame
=
2059 GetViewFrame().GetFrame().GetFrameInterface();
2061 Reference
< beans::XPropertySet
> xPropSet( xFrame
, UNO_QUERY
);
2062 Reference
< frame::XLayoutManager
> xLayoutManager
;
2063 if ( xPropSet
.is() )
2067 Any aValue
= xPropSet
->getPropertyValue(u
"LayoutManager"_ustr
);
2068 aValue
>>= xLayoutManager
;
2069 if ( xLayoutManager
.is() )
2071 uno::Reference
< ui::XUIElement
> xElement
= xLayoutManager
->getElement( u
"private:resource/toolbar/textobjectbar"_ustr
);
2074 xElement
= xLayoutManager
->getElement( u
"private:resource/toolbar/frameobjectbar"_ustr
);
2078 xElement
= xLayoutManager
->getElement( u
"private:resource/toolbar/oleobjectbar"_ustr
);
2082 uno::Reference
< awt::XWindow
> xWin( xElement
->getRealInterface(), uno::UNO_QUERY_THROW
);
2083 VclPtr
<vcl::Window
> pWin
= VCLUnoHelper::GetWindow( xWin
);
2084 ToolBox
* pTextToolbox
= dynamic_cast< ToolBox
* >( pWin
.get() );
2087 ToolBox::ImplToolItems::size_type nItemCount
= pTextToolbox
->GetItemCount();
2088 for( ToolBox::ImplToolItems::size_type nItem
= 0; nItem
< nItemCount
; ++nItem
)
2090 ToolBoxItemId nItemId
= pTextToolbox
->GetItemId( nItem
);
2091 const OUString aCommand
= pTextToolbox
->GetItemCommand( nItemId
);
2092 if (aCommand
== ".uno:StyleApply")
2094 vcl::Window
* pItemWin
= pTextToolbox
->GetItemWindow( nItemId
);
2096 pItemWin
->GrabFocus();
2104 catch (const Exception
&)
2112 case SID_MAIL_SENDDOCASMS
:
2113 case SID_MAIL_SENDDOCASOOO
:
2114 case SID_MAIL_SENDDOCASPDF
:
2115 case SID_MAIL_SENDDOC
:
2116 case SID_MAIL_SENDDOCASFORMAT
:
2118 SfxObjectShell
* pDoc
= GetObjectShell();
2121 pDoc
->QueryHiddenInformation(HiddenWarningFact::WhenSaving
);
2122 SfxMailModel aModel
;
2125 const SfxStringItem
* pMailRecipient
= rReq
.GetArg
<SfxStringItem
>(SID_MAIL_RECIPIENT
);
2126 if ( pMailRecipient
)
2128 OUString
aRecipient( pMailRecipient
->GetValue() );
2129 OUString
aMailToStr(u
"mailto:"_ustr
);
2131 if ( aRecipient
.startsWith( aMailToStr
) )
2132 aRecipient
= aRecipient
.copy( aMailToStr
.getLength() );
2133 aModel
.AddToAddress( aRecipient
);
2135 const SfxStringItem
* pMailDocType
= rReq
.GetArg
<SfxStringItem
>(SID_TYPE_NAME
);
2137 aDocType
= pMailDocType
->GetValue();
2139 uno::Reference
< frame::XFrame
> xFrame( rFrame
.GetFrame().GetFrameInterface() );
2140 SfxMailModel::SendMailResult eResult
= SfxMailModel::SEND_MAIL_ERROR
;
2142 if ( nId
== SID_MAIL_SENDDOC
)
2143 eResult
= aModel
.SaveAndSend( xFrame
, OUString() );
2144 else if ( nId
== SID_MAIL_SENDDOCASPDF
)
2145 eResult
= aModel
.SaveAndSend( xFrame
, u
"pdf_Portable_Document_Format"_ustr
);
2146 else if ( nId
== SID_MAIL_SENDDOCASMS
)
2148 aDocType
= impl_searchFormatTypeForApp(xFrame
, E_MS_DOC
);
2149 if (!aDocType
.isEmpty())
2150 eResult
= aModel
.SaveAndSend( xFrame
, aDocType
);
2152 else if ( nId
== SID_MAIL_SENDDOCASOOO
)
2154 aDocType
= impl_searchFormatTypeForApp(xFrame
, E_OOO_DOC
);
2155 if (!aDocType
.isEmpty())
2156 eResult
= aModel
.SaveAndSend( xFrame
, aDocType
);
2159 if ( eResult
== SfxMailModel::SEND_MAIL_ERROR
)
2161 weld::Window
* pWin
= SfxGetpApp()->GetTopWindow();
2162 std::unique_ptr
<weld::MessageDialog
> xBox(Application::CreateMessageDialog(pWin
,
2163 VclMessageType::Info
, VclButtonsType::Ok
,
2164 SfxResId(STR_ERROR_SEND_MAIL
)));
2173 case SID_BLUETOOTH_SENDDOC
:
2175 SfxBluetoothModel aModel
;
2176 SfxObjectShell
* pDoc
= GetObjectShell();
2179 pDoc
->QueryHiddenInformation(HiddenWarningFact::WhenSaving
);
2180 uno::Reference
< frame::XFrame
> xFrame( rFrame
.GetFrame().GetFrameInterface() );
2181 SfxMailModel::SendMailResult eResult
= aModel
.SaveAndSend( xFrame
);
2182 if( eResult
== SfxMailModel::SEND_MAIL_ERROR
)
2184 weld::Window
* pWin
= SfxGetpApp()->GetTopWindow();
2185 std::unique_ptr
<weld::MessageDialog
> xBox(Application::CreateMessageDialog(pWin
,
2186 VclMessageType::Info
, VclButtonsType::Ok
,
2187 SfxResId(STR_ERROR_SEND_MAIL
)));
2196 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2199 css::uno::Reference
< lang::XMultiServiceFactory
> xSMGR(::comphelper::getProcessServiceFactory(), css::uno::UNO_SET_THROW
);
2200 css::uno::Reference
< uno::XComponentContext
> xContext(::comphelper::getProcessComponentContext(), css::uno::UNO_SET_THROW
);
2201 css::uno::Reference
< css::frame::XFrame
> xFrame( rFrame
.GetFrame().GetFrameInterface() );
2202 css::uno::Reference
< css::frame::XModel
> xModel
;
2204 css::uno::Reference
< css::frame::XModuleManager2
> xModuleManager( css::frame::ModuleManager::create(xContext
) );
2209 aModule
= xModuleManager
->identify( xFrame
);
2211 catch (const css::uno::RuntimeException
&)
2215 catch (const css::uno::Exception
&)
2221 css::uno::Reference
< css::frame::XController
> xController
= xFrame
->getController();
2222 if ( xController
.is() )
2223 xModel
= xController
->getModel();
2226 // We need at least a valid module name and model reference
2227 css::uno::Reference
< css::frame::XStorable
> xStorable( xModel
, css::uno::UNO_QUERY
);
2228 if ( xModel
.is() && xStorable
.is() )
2230 OUString aFilterName
;
2231 OUString
aTypeName( u
"generic_HTML"_ustr
);
2234 OUString aLocation
= xStorable
->getLocation();
2235 INetURLObject
aFileObj( aLocation
);
2237 bool bPrivateProtocol
= ( aFileObj
.GetProtocol() == INetProtocol::PrivSoffice
);
2238 bool bHasLocation
= !aLocation
.isEmpty() && !bPrivateProtocol
;
2240 css::uno::Reference
< css::container::XContainerQuery
> xContainerQuery(
2241 xSMGR
->createInstance( u
"com.sun.star.document.FilterFactory"_ustr
),
2242 css::uno::UNO_QUERY_THROW
);
2244 // Retrieve filter from type
2246 sal_Int32 nFilterFlags
= 0x00000002; // export
2247 aFilterName
= impl_retrieveFilterNameFromTypeAndModule( xContainerQuery
, aTypeName
, aModule
, nFilterFlags
);
2248 if ( aFilterName
.isEmpty() )
2250 // Draw/Impress uses a different type. 2nd chance try to use alternative type name
2251 aFilterName
= impl_retrieveFilterNameFromTypeAndModule(
2252 xContainerQuery
, u
"graphic_HTML"_ustr
, aModule
, nFilterFlags
);
2255 // No filter found => error
2256 // No type and no location => error
2257 if ( aFilterName
.isEmpty() || aTypeName
.isEmpty())
2263 // Use provided save file name. If empty determine file name
2264 if ( !bHasLocation
)
2266 // Create a default file name with the correct extension
2267 aFileName
= "webpreview";
2271 // Determine file name from model
2272 INetURLObject
aFObj( xStorable
->getLocation() );
2273 aFileName
= aFObj
.getName( INetURLObject::LAST_SEGMENT
, true, INetURLObject::DecodeMechanism::NONE
);
2276 OSL_ASSERT( !aFilterName
.isEmpty() );
2277 OSL_ASSERT( !aFileName
.isEmpty() );
2279 // Creates a temporary directory to store our predefined file into it (for the
2280 // flatpak case, create it in XDG_CACHE_HOME instead of /tmp for technical reasons,
2281 // so that it can be accessed by the browser running outside the sandbox):
2282 OUString
* parent
= nullptr;
2283 if (flatpak::isFlatpak() && !flatpak::createTemporaryHtmlDirectory(&parent
))
2285 SAL_WARN("sfx.view", "cannot create Flatpak html temp dir");
2288 INetURLObject
aFilePathObj( ::utl::CreateTempURL(parent
, true) );
2289 aFilePathObj
.insertName( aFileName
);
2290 aFilePathObj
.setExtension( u
"htm" );
2292 OUString aFileURL
= aFilePathObj
.GetMainURL( INetURLObject::DecodeMechanism::NONE
);
2294 css::uno::Sequence
< css::beans::PropertyValue
> aArgs
{
2295 comphelper::makePropertyValue(u
"FilterName"_ustr
, aFilterName
)
2298 // Store document in the html format
2301 xStorable
->storeToURL( aFileURL
, aArgs
);
2303 catch (const io::IOException
&)
2309 sfx2::openUriExternally(aFileURL
, true, rReq
.GetFrameWeld());
2323 void SfxViewShell::GetState_Impl( SfxItemSet
&rSet
)
2326 SfxWhichIter
aIter( rSet
);
2327 SfxObjectShell
*pSh
= GetViewFrame().GetObjectShell();
2328 for ( sal_uInt16 nSID
= aIter
.FirstWhich(); nSID
; nSID
= aIter
.NextWhich() )
2333 case SID_BLUETOOTH_SENDDOC
:
2334 case SID_MAIL_SENDDOC
:
2335 case SID_MAIL_SENDDOCASFORMAT
:
2336 case SID_MAIL_SENDDOCASMS
:
2337 case SID_MAIL_SENDDOCASOOO
:
2338 case SID_MAIL_SENDDOCASPDF
:
2340 #if HAVE_FEATURE_MACOSX_SANDBOX
2341 rSet
.DisableItem(nSID
);
2343 if (pSh
&& pSh
->isExportLocked())
2344 rSet
.DisableItem(nSID
);
2349 if (pSh
&& pSh
->isExportLocked())
2350 rSet
.DisableItem(nSID
);
2353 // Printer functions
2355 case SID_PRINTDOCDIRECT
:
2356 case SID_SETUPPRINTER
:
2357 case SID_PRINTER_NAME
:
2359 if (Application::GetSettings().GetMiscSettings().GetDisablePrinting()
2360 || (pSh
&& pSh
->isPrintLocked()))
2362 rSet
.DisableItem(nSID
);
2366 SfxPrinter
*pPrinter
= GetPrinter();
2368 if ( SID_PRINTDOCDIRECT
== nSID
)
2370 OUString aPrinterName
;
2371 if ( pPrinter
!= nullptr )
2372 aPrinterName
= pPrinter
->GetName();
2375 // tdf#109149 don't poll the Default Printer Name on every query.
2376 // We are queried on every change, so on every
2377 // keystroke, and we are only using this to fill in the
2378 // printername inside the label of "Print Directly (printer-name)"
2379 // On Printer::GetDefaultPrinterName() is implemented with
2380 // GetDefaultPrinter so don't call this excessively. 5 mins
2381 // seems a reasonable refresh time.
2382 std::chrono::steady_clock::time_point now
= std::chrono::steady_clock::now();
2383 std::chrono::minutes
five_mins(5);
2384 if (now
> pImpl
->m_nDefaultPrinterNameFetchTime
+ five_mins
)
2386 pImpl
->m_sDefaultPrinterName
= Printer::GetDefaultPrinterName();
2387 pImpl
->m_nDefaultPrinterNameFetchTime
= now
;
2389 aPrinterName
= pImpl
->m_sDefaultPrinterName
;
2391 if ( !aPrinterName
.isEmpty() )
2393 uno::Reference
< frame::XFrame
> xFrame( rFrame
.GetFrame().GetFrameInterface() );
2395 auto aProperties
= vcl::CommandInfoProvider::GetCommandProperties(u
".uno:PrintDefault"_ustr
,
2396 vcl::CommandInfoProvider::GetModuleIdentifier(xFrame
));
2397 OUString val
= vcl::CommandInfoProvider::GetLabelForCommand(aProperties
) +
2398 " (" + aPrinterName
+ ")";
2400 rSet
.Put( SfxStringItem( SID_PRINTDOCDIRECT
, val
) );
2405 case SID_STYLE_FAMILY
:
2407 rSet
.Put( SfxUInt16Item( SID_STYLE_FAMILY
, pImpl
->m_nFamily
) );
2414 void SfxViewShell::SetZoomFactor( const Fraction
&rZoomX
,
2415 const Fraction
&rZoomY
)
2417 DBG_ASSERT( GetWindow(), "no window" );
2418 MapMode
aMap( GetWindow()->GetMapMode() );
2419 aMap
.SetScaleX( rZoomX
);
2420 aMap
.SetScaleY( rZoomY
);
2421 GetWindow()->SetMapMode( aMap
);
2424 ErrCode
SfxViewShell::DoVerb(sal_Int32
/*nVerb*/)
2428 Virtual Method used to perform a Verb on a selected Object.
2429 Since this Object is only known by the derived classes, they must override
2434 return ERRCODE_SO_NOVERBS
;
2437 void SfxViewShell::OutplaceActivated( bool bActive
)
2441 if (SfxViewFrame
* pFrame
= GetFrame())
2442 pFrame
->GetFrame().Appear();
2446 void SfxViewShell::UIActivating( SfxInPlaceClient
* /*pClient*/ )
2448 uno::Reference
< frame::XFrame
> xOwnFrame( rFrame
.GetFrame().GetFrameInterface() );
2449 uno::Reference
< frame::XFramesSupplier
> xParentFrame
= xOwnFrame
->getCreator();
2450 if ( xParentFrame
.is() )
2451 xParentFrame
->setActiveFrame( xOwnFrame
);
2453 rFrame
.GetBindings().HidePopups();
2454 rFrame
.GetDispatcher()->Update_Impl( true );
2457 void SfxViewShell::UIDeactivated( SfxInPlaceClient
* /*pClient*/ )
2459 if ( !rFrame
.GetFrame().IsClosing_Impl() || SfxViewFrame::Current() != &rFrame
)
2460 rFrame
.GetDispatcher()->Update_Impl( true );
2461 rFrame
.GetBindings().HidePopups(false);
2463 rFrame
.GetBindings().InvalidateAll(true);
2466 SfxInPlaceClient
* SfxViewShell::FindIPClient
2468 const uno::Reference
< embed::XEmbeddedObject
>& xObj
,
2469 vcl::Window
* pObjParentWin
2472 std::vector
< SfxInPlaceClient
* >& rClients
= pImpl
->GetIPClients_Impl();
2473 if ( rClients
.empty() )
2476 if( !pObjParentWin
)
2477 pObjParentWin
= GetWindow();
2478 for (SfxInPlaceClient
* pIPClient
: rClients
)
2480 if ( pIPClient
->GetObject() == xObj
&& pIPClient
->GetEditWin() == pObjParentWin
)
2488 SfxInPlaceClient
* SfxViewShell::GetIPClient() const
2490 return GetUIActiveClient();
2494 SfxInPlaceClient
* SfxViewShell::GetUIActiveIPClient_Impl() const
2496 // this method is needed as long as SFX still manages the border space for ChildWindows (see SfxFrame::Resize)
2497 std::vector
< SfxInPlaceClient
* >& rClients
= pImpl
->GetIPClients_Impl();
2498 if ( rClients
.empty() )
2501 for (SfxInPlaceClient
* pIPClient
: rClients
)
2503 if ( pIPClient
->IsUIActive() )
2510 SfxInPlaceClient
* SfxViewShell::GetUIActiveClient() const
2512 std::vector
< SfxInPlaceClient
* >& rClients
= pImpl
->GetIPClients_Impl();
2513 if ( rClients
.empty() )
2516 const bool bIsTiledRendering
= comphelper::LibreOfficeKit::isActive();
2518 for (SfxInPlaceClient
* pIPClient
: rClients
)
2520 if ( pIPClient
->IsObjectUIActive() || ( bIsTiledRendering
&& pIPClient
->IsObjectInPlaceActive() ) )
2528 void SfxViewShell::Activate( bool bMDI
)
2532 SfxObjectShell
*pSh
= GetViewFrame().GetObjectShell();
2533 if (const auto xModel
= pSh
->GetModel())
2534 xModel
->setCurrentController(GetController());
2536 SetCurrentDocument();
2541 void SfxViewShell::Deactivate(bool /*bMDI*/)
2546 void SfxViewShell::Move()
2550 This virtual Method is called when the window displayed in the
2551 SfxViewShell gets a StarView-Move() notification.
2553 This base implementation does not have to be called. .
2557 This Method can be used to cancel a selection, in order to catch the
2558 mouse movement which is due to moving a window.
2560 For now the notification does not work In-Place.
2567 void SfxViewShell::OuterResizePixel
2569 const Point
& /*rToolOffset*/,// Upper left corner Tools in Frame-Window
2570 const Size
& /*rSize*/ // All available sizes.
2575 Override this Method to be able to react to the size-change of
2576 the View. Thus the View is defined as the Edit window and also the
2577 attached Tools are defined (for example the ruler).
2579 The Edit window must not be changed either in size or position.
2581 The Vis-Area of SfxObjectShell, its scale and position can be changed
2582 here. The main use is to change the size of the Vis-Area.
2584 If the Border is changed due to the new calculation then this has to be set
2585 by <SfxViewShell::SetBorderPixel(const SvBorder&)>. The Positioning of Tools
2586 is only allowed after the calling of 'SetBorderPixel'.
2590 void AppViewSh::OuterViewResizePixel( const Point &rOfs, const Size &rSz )
2592 // Calculate Tool position and size externally, do not set!
2593 // (due to the following Border calculation)
2594 Point aHLinPos...; Size aHLinSz...;
2597 // Calculate and Set a Border of Tools which matches rSize.
2599 SetBorderPixel( aBorder ); // Allow Positioning from here on.
2602 pHLin->SetPosSizePixel( aHLinPos, aHLinSz );
2608 <SfxViewShell::InnerResizePixel(const Point&,const Size& rSize)>
2612 SetBorderPixel( SvBorder() );
2616 void SfxViewShell::InnerResizePixel
2618 const Point
& /*rToolOffset*/,// Upper left corner Tools in Frame-Window
2619 const Size
& /*rSize*/, // All available sizes.
2625 Override this Method to be able to react to the size-change of
2628 The Edit window must not be changed either in size or position.
2629 Neither the Vis-Area of SfxObjectShell nor its scale or position are
2630 allowed to be changed
2632 If the Border is changed due to the new calculation then is has to be set
2633 by <SfxViewShell::SetBorderPixel(const SvBorder&)>.
2634 The Positioning of Tools is only allowed after the calling of
2640 void AppViewSh::InnerViewResizePixel( const Point &rOfs, const Size &rSz )
2642 // Calculate Tool position and size internally, do not set!
2643 // (due to the following Border calculation)
2644 Point aHLinPos...; Size aHLinSz...;
2647 // Calculate and Set a Border of Tools which matches rSize.
2649 SetBorderPixel( aBorder ); // Allow Positioning from here on.
2652 pHLin->SetPosSizePixel( aHLinPos, aHLinSz );
2658 <SfxViewShell::OuterResizePixel(const Point&,const Size& rSize)>
2662 SetBorderPixel( SvBorder() );
2665 void SfxViewShell::InvalidateBorder()
2667 GetViewFrame().InvalidateBorderImpl( this );
2668 if (pImpl
->m_pController
.is())
2670 pImpl
->m_pController
->BorderWidthsChanged_Impl();
2674 void SfxViewShell::SetBorderPixel( const SvBorder
&rBorder
)
2676 GetViewFrame().SetBorderPixelImpl( this, rBorder
);
2678 // notify related controller that border size is changed
2679 if (pImpl
->m_pController
.is())
2681 pImpl
->m_pController
->BorderWidthsChanged_Impl();
2685 const SvBorder
& SfxViewShell::GetBorderPixel() const
2687 return GetViewFrame().GetBorderPixelImpl();
2690 void SfxViewShell::SetWindow
2692 vcl::Window
* pViewPort
// For example Null pointer in the Destructor.
2697 With this method the SfxViewShell is set in the data window. This is
2698 needed for the in-place container and for restoring the proper focus.
2700 Even in-place-active the conversion of the ViewPort Windows is forbidden.
2704 if( pWindow
== pViewPort
)
2707 // Disconnect existing IP-Clients if possible
2708 DisconnectAllClients();
2711 bool bHadFocus
= pWindow
&& pWindow
->HasChildPathFocus( true );
2712 pWindow
= pViewPort
;
2716 // Disable automatic GUI mirroring (right-to-left) for document windows
2717 pWindow
->EnableRTL( false );
2720 if ( bHadFocus
&& pWindow
)
2721 pWindow
->GrabFocus();
2723 //Do we still need this Method?!
2724 //SfxGetpApp()->GrabFocus( pWindow );
2727 ViewShellDocId
SfxViewShell::mnCurrentDocId(0);
2729 SfxViewShell::SfxViewShell
2731 SfxViewFrame
& rViewFrame
, /* <SfxViewFrame>, which will be
2732 displayed in this View */
2733 SfxViewShellFlags nFlags
/* See <SfxViewShell-Flags> */
2737 , pImpl( new SfxViewShell_Impl(nFlags
, SfxViewShell::mnCurrentDocId
) )
2738 , rFrame(rViewFrame
)
2740 , bNoNewWindow( nFlags
& SfxViewShellFlags::NO_NEWWINDOW
)
2741 , mbPrinterSettingsModified(false)
2742 , maLOKLanguageTag(LANGUAGE_NONE
)
2743 , maLOKLocale(LANGUAGE_NONE
)
2744 , maLOKDeviceFormFactor(LOKDeviceFormFactor::UNKNOWN
)
2745 , mbLOKAccessibilityEnabled(false)
2747 SetMargin( rViewFrame
.GetMargin_Impl() );
2749 SetPool( &rViewFrame
.GetObjectShell()->GetPool() );
2750 StartListening(*rViewFrame
.GetObjectShell());
2753 std::vector
<SfxViewShell
*> &rViewArr
= SfxGetpApp()->GetViewShells_Impl();
2754 rViewArr
.push_back(this);
2756 if (comphelper::LibreOfficeKit::isActive())
2758 maLOKLanguageTag
= SfxLokHelper::getDefaultLanguage();
2759 maLOKLocale
= SfxLokHelper::getDefaultLanguage();
2761 const auto [isTimezoneSet
, aTimezone
] = SfxLokHelper::getDefaultTimezone();
2762 maLOKIsTimezoneSet
= isTimezoneSet
;
2763 maLOKTimezone
= aTimezone
;
2765 maLOKDeviceFormFactor
= SfxLokHelper::getDeviceFormFactor();
2767 vcl::Window
* pFrameWin
= rViewFrame
.GetWindow().GetFrameWindow();
2768 if (pFrameWin
&& !pFrameWin
->GetLOKNotifier())
2769 pFrameWin
->SetLOKNotifier(this, true);
2773 SfxViewShell::~SfxViewShell()
2776 const SfxViewShell
*pThis
= this;
2777 std::vector
<SfxViewShell
*> &rViewArr
= SfxGetpApp()->GetViewShells_Impl();
2778 auto it
= std::find( rViewArr
.begin(), rViewArr
.end(), pThis
);
2779 rViewArr
.erase( it
);
2781 if ( pImpl
->xClipboardListener
.is() )
2783 pImpl
->xClipboardListener
->DisconnectViewShell();
2784 pImpl
->xClipboardListener
= nullptr;
2787 if (pImpl
->m_pController
.is())
2789 pImpl
->m_pController
->ReleaseShell_Impl();
2790 pImpl
->m_pController
.clear();
2793 vcl::Window
* pFrameWin
= GetViewFrame().GetWindow().GetFrameWindow();
2794 if (pFrameWin
&& pFrameWin
->GetLOKNotifier() == this)
2795 pFrameWin
->ReleaseLOKNotifier();
2798 OUString
SfxViewShell::getA11yFocusedParagraph() const
2800 const LOKDocumentFocusListener
& rDocFocusListener
= GetLOKDocumentFocusListener();
2801 return rDocFocusListener
.getFocusedParagraph();
2804 int SfxViewShell::getA11yCaretPosition() const
2806 const LOKDocumentFocusListener
& rDocFocusListener
= GetLOKDocumentFocusListener();
2807 return rDocFocusListener
.getCaretPosition();
2810 void SfxViewShell::SetSigningCertificate(const uno::Reference
<security::XCertificate
>& xCertificate
)
2812 pImpl
->m_xSigningCertificate
= xCertificate
;
2815 const uno::Reference
<security::XCertificate
> & SfxViewShell::GetSigningCertificate() const
2817 return pImpl
->m_xSigningCertificate
;
2820 bool SfxViewShell::PrepareClose
2822 bool bUI
// TRUE: Allow Dialog and so on, FALSE: silent-mode
2825 if (GetViewFrame().GetWindow().GetLOKNotifier() == this)
2826 GetViewFrame().GetWindow().ReleaseLOKNotifier();
2828 SfxPrinter
*pPrinter
= GetPrinter();
2829 if ( pPrinter
&& pPrinter
->IsPrinting() )
2833 std::unique_ptr
<weld::MessageDialog
> xBox(Application::CreateMessageDialog(GetViewFrame().GetFrameWeld(),
2834 VclMessageType::Info
, VclButtonsType::Ok
,
2835 SfxResId(STR_CANT_CLOSE
)));
2842 if( GetViewFrame().IsInModalMode() )
2845 if( bUI
&& GetViewFrame().GetDispatcher()->IsLocked() )
2851 SfxViewShell
* SfxViewShell::Current()
2853 SfxViewFrame
*pCurrent
= SfxViewFrame::Current();
2854 return pCurrent
? pCurrent
->GetViewShell() : nullptr;
2857 bool SfxViewShell::IsCurrentLokViewReadOnly()
2859 if (!comphelper::LibreOfficeKit::isActive())
2861 SfxViewShell
* pCurrent
= Current();
2862 return pCurrent
&& pCurrent
->IsLokReadOnlyView();
2865 SfxViewShell
* SfxViewShell::Get( const Reference
< XController
>& i_rController
)
2867 if ( !i_rController
.is() )
2870 for ( SfxViewShell
* pViewShell
= SfxViewShell::GetFirst( false );
2872 pViewShell
= SfxViewShell::GetNext( *pViewShell
, false )
2875 if ( pViewShell
->GetController() == i_rController
)
2881 SdrView
* SfxViewShell::GetDrawView() const
2885 This virtual Method has to be overloaded by the sub classes, to be able
2886 make the Property-Editor available.
2888 The default implementation does always return zero.
2896 OUString
SfxViewShell::GetSelectionText
2898 bool /*bCompleteWords*/, /* FALSE (default)
2899 Only the actual selected text is returned.
2902 The selected text is expanded so that only
2903 whole words are returned. As word separators
2904 these are used: white spaces and punctuation
2905 ".,;" and single and double quotes.
2907 bool /*bOnlyASample*/ /* used by some dialogs to avoid constructing monster strings e.g. in calc */
2912 Override this Method to return a text that
2913 is included in the current selection. This is for example used when
2916 When called with "CompleteWords == TRUE", it is for example sufficient
2917 with having the Cursor positioned somewhere within a URL in-order
2918 to have the entire URL returned.
2926 bool SfxViewShell::HasSelection( bool ) const
2930 With this virtual Method can a for example a Dialog be queried, to
2931 check if something is selected in the current view. If the Parameter
2932 is <BOOL> TRUE then it is checked whether some text is selected.
2939 void SfxViewShell::AddSubShell( SfxShell
& rShell
)
2941 pImpl
->aArr
.push_back(&rShell
);
2942 SfxDispatcher
*pDisp
= rFrame
.GetDispatcher();
2943 if ( pDisp
->IsActive(*this) )
2945 pDisp
->Push(rShell
);
2950 void SfxViewShell::RemoveSubShell( SfxShell
* pShell
)
2952 SfxDispatcher
*pDisp
= rFrame
.GetDispatcher();
2955 size_t nCount
= pImpl
->aArr
.size();
2956 if ( pDisp
->IsActive(*this) )
2958 for(size_t n
= nCount
; n
> 0; --n
)
2959 pDisp
->Pop(*pImpl
->aArr
[n
- 1]);
2962 pImpl
->aArr
.clear();
2966 SfxShellArr_Impl::iterator i
= std::find(pImpl
->aArr
.begin(), pImpl
->aArr
.end(), pShell
);
2967 if(i
!= pImpl
->aArr
.end())
2969 pImpl
->aArr
.erase(i
);
2970 if(pDisp
->IsActive(*this))
2972 pDisp
->RemoveShell_Impl(*pShell
);
2979 SfxShell
* SfxViewShell::GetSubShell( sal_uInt16 nNo
)
2981 sal_uInt16 nCount
= pImpl
->aArr
.size();
2983 return pImpl
->aArr
[nCount
- nNo
- 1];
2987 void SfxViewShell::PushSubShells_Impl( bool bPush
)
2989 SfxDispatcher
*pDisp
= rFrame
.GetDispatcher();
2992 for (auto const& elem
: pImpl
->aArr
)
2995 else if(!pImpl
->aArr
.empty())
2997 SfxShell
& rPopUntil
= *pImpl
->aArr
[0];
2998 if ( pDisp
->GetShellLevel( rPopUntil
) != USHRT_MAX
)
2999 pDisp
->Pop( rPopUntil
, SfxDispatcherPopFlags::POP_UNTIL
);
3006 void SfxViewShell::WriteUserData( OUString
&, bool )
3011 void SfxViewShell::ReadUserData(const OUString
&, bool )
3015 void SfxViewShell::ReadUserDataSequence ( const uno::Sequence
< beans::PropertyValue
>& )
3019 void SfxViewShell::WriteUserDataSequence ( uno::Sequence
< beans::PropertyValue
>& )
3024 // returns the first shell of spec. type viewing the specified doc.
3025 SfxViewShell
* SfxViewShell::GetFirst
3028 const std::function
< bool ( const SfxViewShell
& ) >& isViewShell
3031 // search for a SfxViewShell of the specified type
3032 std::vector
<SfxViewShell
*> &rShells
= SfxGetpApp()->GetViewShells_Impl();
3033 for (SfxViewShell
* pShell
: rShells
)
3037 // This code used to check that the frame exists in the other list,
3038 // because of https://bz.apache.org/ooo/show_bug.cgi?id=62084, with the explanation:
3039 // sometimes dangling SfxViewShells exist that point to a dead SfxViewFrame
3040 // these ViewShells shouldn't be accessible anymore
3041 // a destroyed ViewFrame is not in the ViewFrame array anymore, so checking this array helps
3042 // That doesn't seem to be needed anymore, but keep an assert, just in case.
3043 assert(std::find(SfxGetpApp()->GetViewFrames_Impl().begin(), SfxGetpApp()->GetViewFrames_Impl().end(),
3044 &pShell
->GetViewFrame()) != SfxGetpApp()->GetViewFrames_Impl().end());
3045 if ( ( !bOnlyVisible
|| pShell
->GetViewFrame().IsVisible() ) && (!isViewShell
|| isViewShell(*pShell
)))
3053 // returns the next shell of spec. type viewing the specified doc.
3054 SfxViewShell
* SfxViewShell::GetNext
3056 const SfxViewShell
& rPrev
,
3058 const std::function
<bool ( const SfxViewShell
& )>& isViewShell
3061 std::vector
<SfxViewShell
*> &rShells
= SfxGetpApp()->GetViewShells_Impl();
3063 for ( nPos
= 0; nPos
< rShells
.size(); ++nPos
)
3064 if ( rShells
[nPos
] == &rPrev
)
3067 for ( ++nPos
; nPos
< rShells
.size(); ++nPos
)
3069 SfxViewShell
*pShell
= rShells
[nPos
];
3072 assert(std::find(SfxGetpApp()->GetViewFrames_Impl().begin(), SfxGetpApp()->GetViewFrames_Impl().end(),
3073 &pShell
->GetViewFrame()) != SfxGetpApp()->GetViewFrames_Impl().end());
3074 if ( ( !bOnlyVisible
|| pShell
->GetViewFrame().IsVisible() ) && (!isViewShell
|| isViewShell(*pShell
)) )
3082 void SfxViewShell::Notify( SfxBroadcaster
& rBC
,
3083 const SfxHint
& rHint
)
3085 if (rHint
.GetId() != SfxHintId::ThisIsAnSfxEventHint
||
3086 static_cast<const SfxEventHint
&>(rHint
).GetEventId() != SfxEventHintId::LoadFinished
)
3091 if ( !GetController().is() )
3094 // avoid access to dangling ViewShells
3095 auto &rFrames
= SfxGetpApp()->GetViewFrames_Impl();
3096 for (SfxViewFrame
* frame
: rFrames
)
3098 if ( frame
== &GetViewFrame() && &rBC
== GetObjectShell() )
3100 SfxItemSet
& rSet
= GetObjectShell()->GetMedium()->GetItemSet();
3101 const SfxUnoAnyItem
* pItem
= rSet
.GetItem(SID_VIEW_DATA
, false);
3104 pImpl
->m_pController
->restoreViewData( pItem
->GetValue() );
3105 rSet
.ClearItem( SID_VIEW_DATA
);
3112 bool SfxViewShell::ExecKey_Impl(const KeyEvent
& aKey
)
3114 bool setModuleConfig
= false; // In case libreofficekit is active, we will re-set the module config class.
3115 if (!pImpl
->m_xAccExec
)
3117 pImpl
->m_xAccExec
= ::svt::AcceleratorExecute::createAcceleratorHelper();
3118 pImpl
->m_xAccExec
->init(::comphelper::getProcessComponentContext(),
3119 rFrame
.GetFrame().GetFrameInterface());
3120 setModuleConfig
= true;
3123 if (comphelper::LibreOfficeKit::isActive())
3125 // Get the module name.
3126 const css::uno::Reference
< css::uno::XComponentContext
>& xContext (::comphelper::getProcessComponentContext());
3127 css::uno::Reference
< css::frame::XModuleManager2
> xModuleManager(css::frame::ModuleManager::create(xContext
));
3128 OUString sModule
= xModuleManager
->identify(rFrame
.GetFrame().GetFrameInterface());
3130 // Get the language name.
3131 OUString viewLang
= GetLOKLanguageTag().getBcp47();
3133 // Merge them & have a key.
3134 OUString key
= sModule
+ viewLang
;
3136 // Check it in configurations map. Create a configuration manager if there isn't one for the key.
3137 std::unordered_map
<OUString
, css::uno::Reference
<css::ui::XAcceleratorConfiguration
>>& acceleratorConfs
= SfxApplication::Get()->GetAcceleratorConfs_Impl();
3138 if (acceleratorConfs
.find(key
) == acceleratorConfs
.end())
3140 // Create a new configuration manager for the module.
3142 OUString actualLang
= officecfg::Setup::L10N::ooLocale::get();
3144 std::shared_ptr
<comphelper::ConfigurationChanges
> batch(comphelper::ConfigurationChanges::create());
3145 officecfg::Setup::L10N::ooLocale::set(viewLang
, batch
);
3148 // We have set the language. Time to create the config manager.
3149 acceleratorConfs
[key
] = svt::AcceleratorExecute::lok_createNewAcceleratorConfiguration(::comphelper::getProcessComponentContext(), sModule
);
3151 std::shared_ptr
<comphelper::ConfigurationChanges
> batch2(comphelper::ConfigurationChanges::create());
3152 officecfg::Setup::L10N::ooLocale::set(actualLang
, batch2
);
3156 if (setModuleConfig
)
3157 pImpl
->m_xAccExec
->lok_setModuleConfig(acceleratorConfs
[key
]);
3160 return pImpl
->m_xAccExec
->execute(aKey
.GetKeyCode());
3163 void SfxViewShell::setLibreOfficeKitViewCallback(SfxLokCallbackInterface
* pCallback
)
3165 pImpl
->m_pLibreOfficeKitViewCallback
= pCallback
;
3167 afterCallbackRegistered();
3169 if (!pImpl
->m_pLibreOfficeKitViewCallback
)
3172 // Ask other views to tell us about their cursors.
3173 SfxViewShell
* pViewShell
= SfxViewShell::GetFirst();
3176 if (pViewShell
->GetDocId() == GetDocId())
3177 pViewShell
->NotifyCursor(this);
3178 pViewShell
= SfxViewShell::GetNext(*pViewShell
);
3182 SfxLokCallbackInterface
* SfxViewShell::getLibreOfficeKitViewCallback() const
3184 return pImpl
->m_pLibreOfficeKitViewCallback
;
3187 void SfxViewShell::dumpLibreOfficeKitViewState(rtl::OStringBuffer
&rState
)
3189 rState
.append("\n SfxViewShell: ");
3190 rState
.append(OString::number(reinterpret_cast<sal_uInt64
>(this), 16));
3191 rState
.append("\n\tDocId:\t");
3192 auto nDocId
= static_cast<int>(GetDocId());
3193 rState
.append(static_cast<sal_Int32
>(nDocId
));
3194 rState
.append("\n\tViewId:\t");
3195 rState
.append(static_cast<sal_Int32
>(GetViewShellId()));
3196 rState
.append("\n\tPart:\t");
3197 rState
.append(static_cast<sal_Int32
>(getPart()));
3198 rState
.append("\n\tLang:\t");
3199 rState
.append(OUStringToOString(GetLOKLanguageTag().getBcp47(), RTL_TEXTENCODING_UTF8
));
3200 rState
.append("\n\tA11y:\t");
3201 rState
.append(GetLOKAccessibilityState() ? "enabled" : "disabled");
3203 if (pImpl
->m_pLibreOfficeKitViewCallback
)
3204 pImpl
->m_pLibreOfficeKitViewCallback
->dumpState(rState
);
3207 static bool ignoreLibreOfficeKitViewCallback(int nType
, const SfxViewShell_Impl
* pImpl
)
3209 if (!comphelper::LibreOfficeKit::isActive())
3212 if (comphelper::LibreOfficeKit::isTiledPainting())
3216 case LOK_CALLBACK_FORM_FIELD_BUTTON
:
3217 case LOK_CALLBACK_TEXT_SELECTION
:
3218 case LOK_CALLBACK_COMMENT
:
3219 case LOK_CALLBACK_DOCUMENT_SIZE_CHANGED
:
3222 // Reject e.g. invalidate during paint.
3227 if (pImpl
->m_bTiledSearching
)
3231 case LOK_CALLBACK_TEXT_SELECTION
:
3232 case LOK_CALLBACK_TEXT_VIEW_SELECTION
:
3233 case LOK_CALLBACK_TEXT_SELECTION_START
:
3234 case LOK_CALLBACK_TEXT_SELECTION_END
:
3235 case LOK_CALLBACK_GRAPHIC_SELECTION
:
3236 case LOK_CALLBACK_GRAPHIC_VIEW_SELECTION
:
3244 void SfxViewShell::libreOfficeKitViewInvalidateTilesCallback(const tools::Rectangle
* pRect
, int nPart
, int nMode
) const
3246 if (ignoreLibreOfficeKitViewCallback(LOK_CALLBACK_INVALIDATE_TILES
, pImpl
.get()))
3248 if (pImpl
->m_pLibreOfficeKitViewCallback
)
3249 pImpl
->m_pLibreOfficeKitViewCallback
->libreOfficeKitViewInvalidateTilesCallback(pRect
, nPart
, nMode
);
3253 "SfxViewShell::libreOfficeKitViewInvalidateTilesCallback no callback set!");
3256 void SfxViewShell::libreOfficeKitViewCallbackWithViewId(int nType
, const OString
& pPayload
, int nViewId
) const
3258 if (ignoreLibreOfficeKitViewCallback(nType
, pImpl
.get()))
3260 if (pImpl
->m_pLibreOfficeKitViewCallback
)
3261 pImpl
->m_pLibreOfficeKitViewCallback
->libreOfficeKitViewCallbackWithViewId(nType
, pPayload
, nViewId
);
3265 "SfxViewShell::libreOfficeKitViewCallbackWithViewId no callback set! Dropped payload of type "
3266 << lokCallbackTypeToString(nType
) << ": [" << pPayload
<< ']');
3269 void SfxViewShell::libreOfficeKitViewCallback(int nType
, const OString
& pPayload
) const
3271 if (ignoreLibreOfficeKitViewCallback(nType
, pImpl
.get()))
3273 if (pImpl
->m_pLibreOfficeKitViewCallback
)
3274 pImpl
->m_pLibreOfficeKitViewCallback
->libreOfficeKitViewCallback(nType
, pPayload
);
3278 "SfxViewShell::libreOfficeKitViewCallback no callback set! Dropped payload of type "
3279 << lokCallbackTypeToString(nType
) << ": [" << pPayload
<< ']');
3282 void SfxViewShell::libreOfficeKitViewUpdatedCallback(int nType
) const
3284 if (ignoreLibreOfficeKitViewCallback(nType
, pImpl
.get()))
3286 if (pImpl
->m_pLibreOfficeKitViewCallback
)
3287 pImpl
->m_pLibreOfficeKitViewCallback
->libreOfficeKitViewUpdatedCallback(nType
);
3291 "SfxViewShell::libreOfficeKitViewUpdatedCallback no callback set! Dropped payload of type "
3292 << lokCallbackTypeToString(nType
));
3295 void SfxViewShell::libreOfficeKitViewUpdatedCallbackPerViewId(int nType
, int nViewId
, int nSourceViewId
) const
3297 if (ignoreLibreOfficeKitViewCallback(nType
, pImpl
.get()))
3299 if (pImpl
->m_pLibreOfficeKitViewCallback
)
3300 pImpl
->m_pLibreOfficeKitViewCallback
->libreOfficeKitViewUpdatedCallbackPerViewId(nType
, nViewId
, nSourceViewId
);
3304 "SfxViewShell::libreOfficeKitViewUpdatedCallbackPerViewId no callback set! Dropped payload of type "
3305 << lokCallbackTypeToString(nType
));
3308 void SfxViewShell::libreOfficeKitViewAddPendingInvalidateTiles()
3310 if (pImpl
->m_pLibreOfficeKitViewCallback
)
3311 pImpl
->m_pLibreOfficeKitViewCallback
->libreOfficeKitViewAddPendingInvalidateTiles();
3315 "SfxViewShell::libreOfficeKitViewAddPendingInvalidateTiles no callback set!");
3318 void SfxViewShell::afterCallbackRegistered()
3320 LOK_INFO("sfx.view", "SfxViewShell::afterCallbackRegistered invoked");
3321 if (GetLOKAccessibilityState())
3323 LOKDocumentFocusListener
& rDocFocusListener
= GetLOKDocumentFocusListener();
3324 rDocFocusListener
.notifyFocusedParagraphChanged();
3328 void SfxViewShell::flushPendingLOKInvalidateTiles()
3330 // SfxViewShell itself does not delay any tile invalidations.
3333 std::optional
<OString
> SfxViewShell::getLOKPayload(int nType
, int /*nViewId*/) const
3335 // SfxViewShell itself currently doesn't handle any updated-payload types.
3336 SAL_WARN("sfx.view", "SfxViewShell::getLOKPayload unhandled type " << lokCallbackTypeToString(nType
));
3340 vcl::Window
* SfxViewShell::GetEditWindowForActiveOLEObj() const
3342 vcl::Window
* pEditWin
= nullptr;
3343 SfxInPlaceClient
* pIPClient
= GetIPClient();
3346 pEditWin
= pIPClient
->GetEditWin();
3351 ::Color
SfxViewShell::GetColorConfigColor(svtools::ColorConfigEntry eEntry
) const
3353 SAL_WARN("sfx.view", "SfxViewShell::GetColorConfigColor not overridden!");
3354 svtools::ColorConfig aColorConfig
;
3355 return aColorConfig
.GetColorValue(eEntry
).nColor
;
3358 void SfxViewShell::SetLOKLanguageTag(const OUString
& rBcp47LanguageTag
)
3360 LanguageTag
aTag(rBcp47LanguageTag
, true);
3362 css::uno::Sequence
<OUString
> inst(officecfg::Setup::Office::InstalledLocales::get()->getElementNames());
3363 LanguageTag aFallbackTag
= LanguageTag(getInstalledLocaleForSystemUILanguage(inst
, /* bRequestInstallIfMissing */ false, rBcp47LanguageTag
), true).makeFallback();
3365 // If we want de-CH, and the de localisation is available, we don't want to use de-DE as then
3366 // the magic in Translate::get() won't turn ess-zet into double s.
3367 if (rBcp47LanguageTag
== "de-CH")
3368 maLOKLanguageTag
= std::move(aTag
);
3370 maLOKLanguageTag
= std::move(aFallbackTag
);
3373 LOKDocumentFocusListener
& SfxViewShell::GetLOKDocumentFocusListener()
3375 if (mpLOKDocumentFocusListener
)
3376 return *mpLOKDocumentFocusListener
;
3378 mpLOKDocumentFocusListener
= new LOKDocumentFocusListener(this);
3379 return *mpLOKDocumentFocusListener
;
3382 const LOKDocumentFocusListener
& SfxViewShell::GetLOKDocumentFocusListener() const
3384 return const_cast<SfxViewShell
*>(this)->GetLOKDocumentFocusListener();
3387 void SfxViewShell::SetLOKAccessibilityState(bool bEnabled
)
3389 if (bEnabled
== mbLOKAccessibilityEnabled
)
3391 mbLOKAccessibilityEnabled
= bEnabled
;
3393 LOKDocumentFocusListener
& rDocumentFocusListener
= GetLOKDocumentFocusListener();
3398 uno::Reference
< accessibility::XAccessible
> xAccessible
=
3399 pWindow
->GetAccessible();
3401 if (!xAccessible
.is())
3404 if (mbLOKAccessibilityEnabled
)
3408 rDocumentFocusListener
.attachRecursive(xAccessible
);
3410 catch (const uno::Exception
&)
3412 LOK_WARN("SetLOKAccessibilityState", "Exception caught processing LOKDocumentFocusListener::attachRecursive");
3419 rDocumentFocusListener
.detachRecursive(xAccessible
, /*bForce*/ true);
3421 catch (const uno::Exception
&)
3423 LOK_WARN("SetLOKAccessibilityState", "Exception caught processing LOKDocumentFocusListener::detachRecursive");
3428 void SfxViewShell::SetLOKLocale(const OUString
& rBcp47LanguageTag
)
3430 maLOKLocale
= LanguageTag(rBcp47LanguageTag
, true).makeFallback();
3433 void SfxViewShell::NotifyCursor(SfxViewShell
* /*pViewShell*/) const
3437 void SfxViewShell::setTiledSearching(bool bTiledSearching
)
3439 pImpl
->m_bTiledSearching
= bTiledSearching
;
3442 int SfxViewShell::getPart() const
3447 int SfxViewShell::getEditMode() const
3452 ViewShellId
SfxViewShell::GetViewShellId() const
3454 return pImpl
->m_nViewShellId
;
3457 void SfxViewShell::SetCurrentDocId(ViewShellDocId nId
)
3459 mnCurrentDocId
= nId
;
3462 ViewShellDocId
SfxViewShell::GetDocId() const
3464 assert(pImpl
->m_nDocId
>= ViewShellDocId(0) && "m_nDocId should have been initialized, but it is invalid.");
3465 return pImpl
->m_nDocId
;
3468 void SfxViewShell::notifyInvalidation(tools::Rectangle
const* pRect
) const
3470 SfxLokHelper::notifyInvalidation(this, pRect
);
3473 void SfxViewShell::NotifyOtherViews(int nType
, const OString
& rKey
, const OString
& rPayload
)
3475 SfxLokHelper::notifyOtherViews(this, nType
, rKey
, rPayload
);
3478 void SfxViewShell::NotifyOtherView(OutlinerViewShell
* pOther
, int nType
, const OString
& rKey
, const OString
& rPayload
)
3480 auto pOtherShell
= dynamic_cast<SfxViewShell
*>(pOther
);
3484 SfxLokHelper::notifyOtherView(this, pOtherShell
, nType
, rKey
, rPayload
);
3487 void SfxViewShell::dumpAsXml(xmlTextWriterPtr pWriter
) const
3489 (void)xmlTextWriterStartElement(pWriter
, BAD_CAST("SfxViewShell"));
3490 (void)xmlTextWriterWriteFormatAttribute(pWriter
, BAD_CAST("ptr"), "%p", this);
3491 (void)xmlTextWriterWriteAttribute(pWriter
, BAD_CAST("id"), BAD_CAST(OString::number(static_cast<sal_Int32
>(GetViewShellId())).getStr()));
3492 (void)xmlTextWriterEndElement(pWriter
);
3495 bool SfxViewShell::KeyInput( const KeyEvent
&rKeyEvent
)
3499 This Method executes the KeyEvent 'rKeyEvent' of the Keys (Accelerator)
3500 configured either direct or indirect (for example by the Application)
3501 in the SfxViewShell.
3506 The Key (Accelerator) is configured and the
3507 associated Handler was called
3510 The Key (Accelerator) is not configured and
3511 subsequently no Handler was called
3515 <SfxApplication::KeyInput(const KeyEvent&)>
3518 return ExecKey_Impl(rKeyEvent
);
3521 bool SfxViewShell::GlobalKeyInput_Impl( const KeyEvent
&rKeyEvent
)
3523 return ExecKey_Impl(rKeyEvent
);
3527 void SfxViewShell::ShowCursor( bool /*bOn*/ )
3531 Subclasses must override this Method so that SFx can switch the
3532 Cursor on and off, for example while a <SfxProgress> is running.
3539 void SfxViewShell::ResetAllClients_Impl( SfxInPlaceClient
const *pIP
)
3542 std::vector
< SfxInPlaceClient
* >& rClients
= pImpl
->GetIPClients_Impl();
3543 if ( rClients
.empty() )
3546 for (SfxInPlaceClient
* pIPClient
: rClients
)
3548 if( pIPClient
!= pIP
)
3549 pIPClient
->ResetObject();
3554 void SfxViewShell::DisconnectAllClients()
3556 std::vector
< SfxInPlaceClient
* >& rClients
= pImpl
->GetIPClients_Impl();
3557 if ( rClients
.empty() )
3560 for ( size_t n
= 0; n
< rClients
.size(); )
3561 // clients will remove themselves from the list
3562 delete rClients
.at( n
);
3566 void SfxViewShell::QueryObjAreaPixel( tools::Rectangle
& ) const
3571 void SfxViewShell::VisAreaChanged()
3573 std::vector
< SfxInPlaceClient
* >& rClients
= pImpl
->GetIPClients_Impl();
3574 if ( rClients
.empty() )
3577 for (SfxInPlaceClient
* pIPClient
: rClients
)
3579 if ( pIPClient
->IsObjectInPlaceActive() )
3580 // client is active, notify client that the VisArea might have changed
3581 pIPClient
->VisAreaChanged();
3586 void SfxViewShell::CheckIPClient_Impl(
3587 SfxInPlaceClient
const *const pIPClient
, const tools::Rectangle
& rVisArea
)
3589 if ( GetObjectShell()->IsInClose() )
3592 bool bAlwaysActive
=
3593 ( ( pIPClient
->GetObjectMiscStatus() & embed::EmbedMisc::EMBED_ACTIVATEIMMEDIATELY
) != 0 );
3594 bool bActiveWhenVisible
=
3595 ( pIPClient
->GetObjectMiscStatus() & embed::EmbedMisc::MS_EMBED_ACTIVATEWHENVISIBLE
) != 0;
3597 // this method is called when a client is created
3598 if (pIPClient
->IsObjectInPlaceActive())
3601 // object in client is currently not active
3602 // check if the object wants to be activated always or when it becomes at least partially visible
3603 // TODO/LATER: maybe we should use the scaled area instead of the ObjArea?!
3604 if (bAlwaysActive
|| (bActiveWhenVisible
&& rVisArea
.Overlaps(pIPClient
->GetObjArea())))
3608 pIPClient
->GetObject()->changeState( embed::EmbedStates::INPLACE_ACTIVE
);
3610 catch (const uno::Exception
&)
3612 TOOLS_WARN_EXCEPTION("sfx.view", "SfxViewShell::CheckIPClient_Impl");
3617 SfxObjectShell
* SfxViewShell::GetObjectShell()
3619 return rFrame
.GetObjectShell();
3622 Reference
< XModel
> SfxViewShell::GetCurrentDocument() const
3624 Reference
< XModel
> xDocument
;
3626 const SfxObjectShell
* pDocShell( const_cast< SfxViewShell
* >( this )->GetObjectShell() );
3627 OSL_ENSURE( pDocShell
, "SfxViewFrame::GetCurrentDocument: no DocShell!?" );
3629 xDocument
= pDocShell
->GetModel();
3634 void SfxViewShell::SetCurrentDocument() const
3636 uno::Reference
< frame::XModel
> xDocument( GetCurrentDocument() );
3637 if ( xDocument
.is() )
3638 SfxObjectShell::SetCurrentComponent( xDocument
);
3642 const Size
& SfxViewShell::GetMargin() const
3644 return pImpl
->aMargin
;
3648 void SfxViewShell::SetMargin( const Size
& rSize
)
3650 // the default margin was verified using www.apple.com !!
3651 Size aMargin
= rSize
;
3652 if ( aMargin
.Width() == -1 )
3653 aMargin
.setWidth( DEFAULT_MARGIN_WIDTH
);
3654 if ( aMargin
.Height() == -1 )
3655 aMargin
.setHeight( DEFAULT_MARGIN_HEIGHT
);
3657 if ( aMargin
!= pImpl
->aMargin
)
3659 pImpl
->aMargin
= aMargin
;
3664 void SfxViewShell::MarginChanged()
3668 void SfxViewShell::JumpToMark( const OUString
& rMark
)
3670 SfxStringItem
aMarkItem( SID_JUMPTOMARK
, rMark
);
3671 GetViewFrame().GetDispatcher()->ExecuteList(
3673 SfxCallMode::SYNCHRON
|SfxCallMode::RECORD
,
3677 void SfxViewShell::SetController( SfxBaseController
* pController
)
3679 pImpl
->m_pController
= pController
;
3681 // there should be no old listener, but if there is one, it should be disconnected
3682 if ( pImpl
->xClipboardListener
.is() )
3683 pImpl
->xClipboardListener
->DisconnectViewShell();
3685 pImpl
->xClipboardListener
= new SfxClipboardChangeListener( this, GetClipboardNotifier() );
3688 Reference
< XController
> SfxViewShell::GetController() const
3690 return pImpl
->m_pController
;
3693 SfxBaseController
* SfxViewShell::GetBaseController_Impl() const
3695 return pImpl
->m_pController
.get();
3698 void SfxViewShell::AddContextMenuInterceptor_Impl( const uno::Reference
< ui::XContextMenuInterceptor
>& xInterceptor
)
3700 std::unique_lock
g(pImpl
->aMutex
);
3701 pImpl
->aInterceptorContainer
.addInterface( g
, xInterceptor
);
3704 void SfxViewShell::RemoveContextMenuInterceptor_Impl( const uno::Reference
< ui::XContextMenuInterceptor
>& xInterceptor
)
3706 std::unique_lock
g(pImpl
->aMutex
);
3707 pImpl
->aInterceptorContainer
.removeInterface( g
, xInterceptor
);
3710 bool SfxViewShell::TryContextMenuInterception(const rtl::Reference
<VCLXPopupMenu
>& rIn
,
3711 const OUString
& rMenuIdentifier
,
3712 rtl::Reference
<VCLXPopupMenu
>& rOut
,
3713 ui::ContextMenuExecuteEvent aEvent
)
3716 bool bModified
= false;
3718 // create container from menu
3719 aEvent
.ActionTriggerContainer
= ::framework::ActionTriggerHelper::CreateActionTriggerContainerFromMenu(
3720 rIn
, &rMenuIdentifier
);
3722 // get selection from controller
3723 aEvent
.Selection
.set( GetController(), uno::UNO_QUERY
);
3725 // call interceptors
3726 std::unique_lock
g(pImpl
->aMutex
);
3727 std::vector
<uno::Reference
< ui::XContextMenuInterceptor
>> aInterceptors
=
3728 pImpl
->aInterceptorContainer
.getElements(g
);
3730 for (const auto & rListener
: aInterceptors
)
3734 ui::ContextMenuInterceptorAction eAction
;
3736 SolarMutexReleaser rel
;
3737 eAction
= rListener
->notifyContextMenuExecute( aEvent
);
3741 case ui::ContextMenuInterceptorAction_CANCELLED
:
3742 // interceptor does not want execution
3744 case ui::ContextMenuInterceptorAction_EXECUTE_MODIFIED
:
3745 // interceptor wants his modified menu to be executed
3748 case ui::ContextMenuInterceptorAction_CONTINUE_MODIFIED
:
3749 // interceptor has modified menu, but allows for calling other interceptors
3752 case ui::ContextMenuInterceptorAction_IGNORED
:
3753 // interceptor is indifferent
3756 OSL_FAIL("Wrong return value of ContextMenuInterceptor!");
3763 pImpl
->aInterceptorContainer
.removeInterface(g
, rListener
);
3772 // container was modified, create a new menu out of it
3773 rOut
= new VCLXPopupMenu();
3774 ::framework::ActionTriggerHelper::CreateMenuFromActionTriggerContainer(rOut
, aEvent
.ActionTriggerContainer
);
3780 bool SfxViewShell::TryContextMenuInterception(const rtl::Reference
<VCLXPopupMenu
>& rPopupMenu
,
3781 const OUString
& rMenuIdentifier
, css::ui::ContextMenuExecuteEvent aEvent
)
3783 bool bModified
= false;
3785 // create container from menu
3786 aEvent
.ActionTriggerContainer
= ::framework::ActionTriggerHelper::CreateActionTriggerContainerFromMenu(
3787 rPopupMenu
, &rMenuIdentifier
);
3789 // get selection from controller
3790 aEvent
.Selection
= css::uno::Reference
< css::view::XSelectionSupplier
>( GetController(), css::uno::UNO_QUERY
);
3792 // call interceptors
3793 std::unique_lock
g(pImpl
->aMutex
);
3794 std::vector
<uno::Reference
< ui::XContextMenuInterceptor
>> aInterceptors
=
3795 pImpl
->aInterceptorContainer
.getElements(g
);
3797 for (const auto & rListener
: aInterceptors
)
3801 css::ui::ContextMenuInterceptorAction eAction
;
3803 SolarMutexReleaser rel
;
3804 eAction
= rListener
->notifyContextMenuExecute( aEvent
);
3808 case css::ui::ContextMenuInterceptorAction_CANCELLED
:
3809 // interceptor does not want execution
3811 case css::ui::ContextMenuInterceptorAction_EXECUTE_MODIFIED
:
3812 // interceptor wants his modified menu to be executed
3815 case css::ui::ContextMenuInterceptorAction_CONTINUE_MODIFIED
:
3816 // interceptor has modified menu, but allows for calling other interceptors
3819 case css::ui::ContextMenuInterceptorAction_IGNORED
:
3820 // interceptor is indifferent
3823 SAL_WARN( "sfx.view", "Wrong return value of ContextMenuInterceptor!" );
3830 pImpl
->aInterceptorContainer
.removeInterface(g
, rListener
);
3839 rPopupMenu
->clear();
3840 ::framework::ActionTriggerHelper::CreateMenuFromActionTriggerContainer(rPopupMenu
, aEvent
.ActionTriggerContainer
);
3846 bool SfxViewShell::HandleNotifyEvent_Impl( NotifyEvent
const & rEvent
)
3848 if (pImpl
->m_pController
.is())
3849 return pImpl
->m_pController
->HandleEvent_Impl( rEvent
);
3853 bool SfxViewShell::HasKeyListeners_Impl() const
3855 return (pImpl
->m_pController
.is())
3856 && pImpl
->m_pController
->HasKeyListeners_Impl();
3859 bool SfxViewShell::HasMouseClickListeners_Impl() const
3861 return (pImpl
->m_pController
.is())
3862 && pImpl
->m_pController
->HasMouseClickListeners_Impl();
3865 bool SfxViewShell::Escape()
3867 return GetViewFrame().GetBindings().Execute(SID_TERMINATE_INPLACEACTIVATION
);
3870 Reference
< view::XRenderable
> SfxViewShell::GetRenderable()
3872 Reference
< view::XRenderable
>xRender
;
3873 SfxObjectShell
* pObj
= GetObjectShell();
3876 Reference
< frame::XModel
> xModel( pObj
->GetModel() );
3878 xRender
.set( xModel
, UNO_QUERY
);
3883 void SfxViewShell::notifyWindow(vcl::LOKWindowId nDialogId
, const OUString
& rAction
, const std::vector
<vcl::LOKPayloadItem
>& rPayload
) const
3885 SfxLokHelper::notifyWindow(this, nDialogId
, rAction
, rPayload
);
3888 OString
SfxViewShell::dumpNotifyState() const
3890 return OString("sfxviewsh: " +
3891 OString::number(reinterpret_cast<sal_uInt64
>(this), 16) +
3892 " doc: " + OString::number(static_cast<sal_Int32
>(static_cast<int>(GetDocId()))) +
3894 OString::number(static_cast<sal_Int32
>(GetViewShellId())));
3897 uno::Reference
< datatransfer::clipboard::XClipboardNotifier
> SfxViewShell::GetClipboardNotifier() const
3899 uno::Reference
< datatransfer::clipboard::XClipboardNotifier
> xClipboardNotifier
;
3900 xClipboardNotifier
.set(GetViewFrame().GetWindow().GetClipboard(), uno::UNO_QUERY
);
3901 return xClipboardNotifier
;
3904 void SfxViewShell::AddRemoveClipboardListener( const uno::Reference
< datatransfer::clipboard::XClipboardListener
>& rClp
, bool bAdd
)
3908 uno::Reference
< datatransfer::clipboard::XClipboard
> xClipboard(GetViewFrame().GetWindow().GetClipboard());
3909 if( xClipboard
.is() )
3911 uno::Reference
< datatransfer::clipboard::XClipboardNotifier
> xClpbrdNtfr( xClipboard
, uno::UNO_QUERY
);
3912 if( xClpbrdNtfr
.is() )
3915 xClpbrdNtfr
->addClipboardListener( rClp
);
3917 xClpbrdNtfr
->removeClipboardListener( rClp
);
3921 catch (const uno::Exception
&)
3926 weld::Window
* SfxViewShell::GetFrameWeld() const
3928 return pWindow
? pWindow
->GetFrameWeld() : nullptr;
3931 void SfxViewShell::setBlockedCommandList(const char* blockedCommandList
)
3933 if(!mvLOKBlockedCommandList
.empty())
3936 OUString
BlockedListString(blockedCommandList
, strlen(blockedCommandList
), RTL_TEXTENCODING_UTF8
);
3937 OUString command
= BlockedListString
.getToken(0, ' ');
3938 for (size_t i
= 1; !command
.isEmpty(); i
++)
3940 mvLOKBlockedCommandList
.emplace(command
);
3941 command
= BlockedListString
.getToken(i
, ' ');
3945 bool SfxViewShell::isBlockedCommand(const OUString
& command
) const
3947 return mvLOKBlockedCommandList
.find(command
) != mvLOKBlockedCommandList
.end();
3950 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */