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 ::com::sun::star::util
;
125 using namespace ::cppu
;
127 class SfxClipboardChangeListener
: public ::cppu::WeakImplHelper
<
128 datatransfer::clipboard::XClipboardListener
>
131 SfxClipboardChangeListener( SfxViewShell
* pView
, uno::Reference
< datatransfer::clipboard::XClipboardNotifier
> xClpbrdNtfr
);
134 virtual void SAL_CALL
disposing( const lang::EventObject
& rEventObject
) override
;
136 // XClipboardListener
137 virtual void SAL_CALL
changedContents( const datatransfer::clipboard::ClipboardEvent
& rEventObject
) override
;
139 void DisconnectViewShell() { m_pViewShell
= nullptr; }
140 void ChangedContents();
144 ASYNCEXECUTE_CMD_DISPOSING
,
145 ASYNCEXECUTE_CMD_CHANGEDCONTENTS
148 struct AsyncExecuteInfo
150 AsyncExecuteInfo( AsyncExecuteCmd eCmd
, SfxClipboardChangeListener
* pListener
) :
151 m_eCmd( eCmd
), m_xListener( pListener
) {}
153 AsyncExecuteCmd m_eCmd
;
154 rtl::Reference
<SfxClipboardChangeListener
> m_xListener
;
158 SfxViewShell
* m_pViewShell
;
159 uno::Reference
< datatransfer::clipboard::XClipboardNotifier
> m_xClpbrdNtfr
;
160 uno::Reference
< lang::XComponent
> m_xCtrl
;
162 DECL_STATIC_LINK( SfxClipboardChangeListener
, AsyncExecuteHdl_Impl
, void*, void );
165 SfxClipboardChangeListener::SfxClipboardChangeListener( SfxViewShell
* pView
, uno::Reference
< datatransfer::clipboard::XClipboardNotifier
> xClpbrdNtfr
)
166 : m_pViewShell( nullptr ), m_xClpbrdNtfr(std::move( xClpbrdNtfr
)), m_xCtrl(pView
->GetController())
170 m_xCtrl
->addEventListener( uno::Reference
< lang::XEventListener
> ( static_cast < lang::XEventListener
* >( this ) ) );
171 m_pViewShell
= pView
;
173 if ( m_xClpbrdNtfr
.is() )
175 m_xClpbrdNtfr
->addClipboardListener( uno::Reference
< datatransfer::clipboard::XClipboardListener
>(
176 static_cast< datatransfer::clipboard::XClipboardListener
* >( this )));
180 void SfxClipboardChangeListener::ChangedContents()
182 const SolarMutexGuard aGuard
;
186 SfxBindings
& rBind
= m_pViewShell
->GetViewFrame().GetBindings();
187 rBind
.Invalidate(SID_PASTE
);
188 rBind
.Invalidate(SID_PASTE_SPECIAL
);
189 rBind
.Invalidate(SID_CLIPBOARD_FORMAT_ITEMS
);
191 if (comphelper::LibreOfficeKit::isActive())
193 // In the future we might send the payload as well.
194 SfxLokHelper::notifyAllViews(LOK_CALLBACK_CLIPBOARD_CHANGED
, ""_ostr
);
198 IMPL_STATIC_LINK( SfxClipboardChangeListener
, AsyncExecuteHdl_Impl
, void*, p
, void )
200 AsyncExecuteInfo
* pAsyncExecuteInfo
= static_cast<AsyncExecuteInfo
*>(p
);
201 if ( pAsyncExecuteInfo
)
203 if ( pAsyncExecuteInfo
->m_xListener
.is() )
205 if ( pAsyncExecuteInfo
->m_eCmd
== ASYNCEXECUTE_CMD_DISPOSING
)
206 pAsyncExecuteInfo
->m_xListener
->DisconnectViewShell();
207 else if ( pAsyncExecuteInfo
->m_eCmd
== ASYNCEXECUTE_CMD_CHANGEDCONTENTS
)
208 pAsyncExecuteInfo
->m_xListener
->ChangedContents();
211 delete pAsyncExecuteInfo
;
214 void SAL_CALL
SfxClipboardChangeListener::disposing( const lang::EventObject
& /*rEventObject*/ )
216 // Either clipboard or ViewShell is going to be destroyed -> no interest in listening anymore
217 uno::Reference
< lang::XComponent
> xCtrl( m_xCtrl
);
218 uno::Reference
< datatransfer::clipboard::XClipboardNotifier
> xNotify( m_xClpbrdNtfr
);
220 uno::Reference
< datatransfer::clipboard::XClipboardListener
> xThis( static_cast< datatransfer::clipboard::XClipboardListener
* >( this ));
222 xCtrl
->removeEventListener( uno::Reference
< lang::XEventListener
> ( static_cast < lang::XEventListener
* >( this )));
224 xNotify
->removeClipboardListener( xThis
);
226 // Make asynchronous call to avoid locking SolarMutex which is the
227 // root for many deadlocks, especially in conjunction with the "Windows"
228 // based single thread apartment clipboard code!
229 AsyncExecuteInfo
* pInfo
= new AsyncExecuteInfo( ASYNCEXECUTE_CMD_DISPOSING
, this );
230 if (!Application::PostUserEvent( LINK( nullptr, SfxClipboardChangeListener
, AsyncExecuteHdl_Impl
), pInfo
))
234 void SAL_CALL
SfxClipboardChangeListener::changedContents( const datatransfer::clipboard::ClipboardEvent
& )
236 // Make asynchronous call to avoid locking SolarMutex which is the
237 // root for many deadlocks, especially in conjunction with the "Windows"
238 // based single thread apartment clipboard code!
239 AsyncExecuteInfo
* pInfo
= new AsyncExecuteInfo( ASYNCEXECUTE_CMD_CHANGEDCONTENTS
, this );
240 if (!Application::PostUserEvent( LINK( nullptr, SfxClipboardChangeListener
, AsyncExecuteHdl_Impl
), pInfo
))
253 typedef std::list
<uno::Reference
<accessibility::XAccessibleTable
>> XAccessibleTableList
;
258 bool isText(sal_Int16 nRole
)
260 return nRole
== accessibility::AccessibleRole::DOCUMENT_TEXT
;
264 bool isSpreadsheet(sal_Int16 nRole
)
266 return nRole
== accessibility::AccessibleRole::DOCUMENT_SPREADSHEET
;
270 bool isPresentation(sal_Int16 nRole
)
272 return nRole
== accessibility::AccessibleRole::DOCUMENT_PRESENTATION
;
276 bool isDocument(sal_Int16 nRole
)
278 return isText(nRole
) || isSpreadsheet(nRole
) || isPresentation(nRole
);
281 bool hasState(const accessibility::AccessibleEventObject
& aEvent
, ::sal_Int64 nState
)
284 uno::Reference
< accessibility::XAccessibleContext
> xContext(aEvent
.Source
, uno::UNO_QUERY
);
287 ::sal_Int64 nStateSet
= xContext
->getAccessibleStateSet();
288 res
= (nStateSet
& nState
) != 0;
293 bool isFocused(const accessibility::AccessibleEventObject
& aEvent
)
295 return hasState(aEvent
, accessibility::AccessibleStateType::FOCUSED
);
298 uno::Reference
<accessibility::XAccessibleContext
>
299 getParentContext(const uno::Reference
<accessibility::XAccessibleContext
>& xContext
)
301 uno::Reference
<accessibility::XAccessibleContext
> xParentContext
;
302 uno::Reference
<accessibility::XAccessible
> xParent
= xContext
->getAccessibleParent();
304 xParentContext
= uno::Reference
<accessibility::XAccessibleContext
>(xParent
, uno::UNO_QUERY
);
305 return xParentContext
;
308 OUString
selectionEventTypeToString(sal_Int16 nEventId
)
310 using namespace accessibility
;
313 case AccessibleEventId::SELECTION_CHANGED
:
315 case AccessibleEventId::SELECTION_CHANGED_ADD
:
317 case AccessibleEventId::SELECTION_CHANGED_REMOVE
:
324 bool selectionHasToBeNotified(const uno::Reference
<accessibility::XAccessibleContext
>& xContext
)
326 sal_Int16 nRole
= xContext
->getAccessibleRole();
328 nRole
== accessibility::AccessibleRole::GRAPHIC
||
329 nRole
== accessibility::AccessibleRole::EMBEDDED_OBJECT
||
330 nRole
== accessibility::AccessibleRole::SHAPE
;
333 bool hasToBeActiveForEditing(sal_Int16 nRole
)
336 nRole
== accessibility::AccessibleRole::SHAPE
;
339 sal_Int16
getParentRole(const uno::Reference
<accessibility::XAccessibleContext
>& xContext
)
344 uno::Reference
<accessibility::XAccessibleContext
> xParentContext
= getParentContext(xContext
);
345 if (xParentContext
.is())
346 nRole
= xParentContext
->getAccessibleRole();
351 sal_Int64
getAccessibleSiblingCount(const Reference
<accessibility::XAccessibleContext
>& xContext
)
356 sal_Int64 nChildCount
= 0;
357 Reference
<accessibility::XAccessible
> xParent
= xContext
->getAccessibleParent();
360 Reference
<accessibility::XAccessibleContext
> xParentContext
= xParent
->getAccessibleContext();
361 if (xParentContext
.is())
363 nChildCount
= xParentContext
->getAccessibleChildCount();
366 return nChildCount
- 1;
369 // Put in rAncestorList all ancestors of xTable up to xAncestorTable or
370 // up to the first not-a-table ancestor if xAncestorTable is not an ancestor.
371 // xTable is included in the list, xAncestorTable is not included.
372 // The list is ordered from the ancient ancestor to xTable.
373 // Return true if xAncestorTable is an ancestor of xTable.
374 bool getAncestorList(XAccessibleTableList
& rAncestorList
,
375 const uno::Reference
<accessibility::XAccessibleTable
>& xTable
,
376 const uno::Reference
<accessibility::XAccessibleTable
>& xAncestorTable
= uno::Reference
<accessibility::XAccessibleTable
>())
378 uno::Reference
<accessibility::XAccessibleTable
> xCurrentTable
= xTable
;
379 while (xCurrentTable
.is() && xCurrentTable
!= xAncestorTable
)
381 rAncestorList
.push_front(xCurrentTable
);
383 uno::Reference
<accessibility::XAccessibleContext
> xContext(xCurrentTable
, uno::UNO_QUERY
);
384 xCurrentTable
.clear();
387 uno::Reference
<accessibility::XAccessible
> xParent
= xContext
->getAccessibleParent();
388 uno::Reference
<accessibility::XAccessibleContext
> xParentContext(xParent
, uno::UNO_QUERY
);
389 if (xParentContext
.is()
390 && xParentContext
->getAccessibleRole() == accessibility::AccessibleRole::TABLE_CELL
)
392 uno::Reference
<accessibility::XAccessible
> xCellParent
= xParentContext
->getAccessibleParent();
393 if (xCellParent
.is())
395 xCurrentTable
= uno::Reference
<accessibility::XAccessibleTable
>(xCellParent
, uno::UNO_QUERY
);
401 return xCurrentTable
.is() && xCurrentTable
== xAncestorTable
;
404 void lookForParentTable(const uno::Reference
<accessibility::XAccessibleContext
>& xContext
,
405 uno::Reference
<accessibility::XAccessibleTable
>& xTable
,
406 sal_Int64
& nChildIndex
)
408 using namespace accessibility
;
409 uno::Reference
<XAccessibleContext
> xParentContext
= getParentContext(xContext
);
410 if (xParentContext
.is() && xParentContext
->getAccessibleRole() == AccessibleRole::TABLE_CELL
)
412 uno::Reference
<XAccessible
> xCellParent
= xParentContext
->getAccessibleParent();
413 if (xCellParent
.is())
415 xTable
= uno::Reference
<XAccessibleTable
>(xCellParent
, uno::UNO_QUERY
);
418 nChildIndex
= xParentContext
->getAccessibleIndexInParent();
424 OUString
truncateText(OUString
& sText
, sal_Int32 nNewLength
)
426 // truncate test to given length
427 OUString sNewText
= sText
.copy(0, nNewLength
);
428 // try to truncate at a word
429 nNewLength
= sNewText
.lastIndexOf(" ");
431 sNewText
= sNewText
.copy(0, nNewLength
);
435 std::string
stateSetToString(::sal_Int64 stateSet
)
437 static const std::string states
[35] = {
438 "ACTIVE", "ARMED", "BUSY", "CHECKED", "DEFUNC",
439 "EDITABLE", "ENABLED", "EXPANDABLE", "EXPANDED", "FOCUSABLE",
440 "FOCUSED", "HORIZONTAL", "ICONIFIED", "INDETERMINATE", "MANAGES_DESCENDANTS",
441 "MODAL", "MULTI_LINE", "MULTI_SELECTABLE", "OPAQUE", "PRESSED",
442 "RESIZABLE", "SELECTABLE", "SELECTED", "SENSITIVE", "SHOWING",
443 "SINGLE_LINE", "STALE", "TRANSIENT", "VERTICAL", "VISIBLE",
444 "MOVEABLE", "DEFAULT", "OFFSCREEN", "COLLAPSE", "CHECKABLE"
449 ::sal_Int64 state
= 1;
451 for (int i
= 0; i
< 35; ++i
)
453 if (stateSet
& state
)
463 void aboutView(std::string msg
, const void* pInstance
, const SfxViewShell
* pViewShell
)
468 SAL_INFO("lok.a11y", ">>> " << msg
<< ": instance: " << pInstance
469 << ", VIED ID: " << pViewShell
->GetViewShellId().get() << " <<<");
472 void aboutEvent(std::string msg
, const accessibility::AccessibleEventObject
& aEvent
)
476 uno::Reference
< accessibility::XAccessible
> xSource(aEvent
.Source
, uno::UNO_QUERY
);
479 uno::Reference
< accessibility::XAccessibleContext
> xContext
=
480 xSource
->getAccessibleContext();
484 SAL_INFO("lok.a11y", msg
<< ": event id: " << aEvent
.EventId
485 << "\n xSource: " << xSource
.get()
486 << "\n role: " << xContext
->getAccessibleRole()
487 << "\n name: " << xContext
->getAccessibleName()
488 << "\n index in parent: " << xContext
->getAccessibleIndexInParent()
489 << "\n state set: " << stateSetToString(xContext
->getAccessibleStateSet())
490 << "\n parent: " << xContext
->getAccessibleParent().get()
491 << "\n child count: " << xContext
->getAccessibleChildCount());
495 SAL_INFO("lok.a11y", msg
<< ": event id: " << aEvent
.EventId
496 << ", no accessible context!");
501 SAL_INFO("lok.a11y", msg
<< ": event id: " << aEvent
.EventId
502 << ", no accessible source!");
504 uno::Reference
< accessibility::XAccessible
> xOldValue
;
505 aEvent
.OldValue
>>= xOldValue
;
508 uno::Reference
< accessibility::XAccessibleContext
> xContext
=
509 xOldValue
->getAccessibleContext();
513 SAL_INFO("lok.a11y", msg
<< ": "
514 "\n xOldValue: " << xOldValue
.get()
515 << "\n role: " << xContext
->getAccessibleRole()
516 << "\n name: " << xContext
->getAccessibleName()
517 << "\n index in parent: " << xContext
->getAccessibleIndexInParent()
518 << "\n state set: " << stateSetToString(xContext
->getAccessibleStateSet())
519 << "\n parent: " << xContext
->getAccessibleParent().get()
520 << "\n child count: " << xContext
->getAccessibleChildCount());
523 uno::Reference
< accessibility::XAccessible
> xNewValue
;
524 aEvent
.NewValue
>>= xNewValue
;
527 uno::Reference
< accessibility::XAccessibleContext
> xContext
=
528 xNewValue
->getAccessibleContext();
532 SAL_INFO("lok.a11y", msg
<< ": "
533 "\n xNewValue: " << xNewValue
.get()
534 << "\n role: " << xContext
->getAccessibleRole()
535 << "\n name: " << xContext
->getAccessibleName()
536 << "\n index in parent: " << xContext
->getAccessibleIndexInParent()
537 << "\n state set: " << stateSetToString(xContext
->getAccessibleStateSet())
538 << "\n parent: " << xContext
->getAccessibleParent().get()
539 << "\n child count: " << xContext
->getAccessibleChildCount());
543 catch( const lang::IndexOutOfBoundsException
& /*e*/ )
545 LOK_WARN("lok.a11y", "Focused object has invalid index in parent");
549 sal_Int32
getListPrefixSize(const uno::Reference
<css::accessibility::XAccessibleText
>& xAccText
)
554 OUString sText
= xAccText
->getText();
555 sal_Int32 nLength
= sText
.getLength();
559 css::uno::Sequence
< css::beans::PropertyValue
> aRunAttributeList
;
560 css::uno::Sequence
< OUString
> aRequestedAttributes
= {UNO_NAME_NUMBERING_LEVEL
, UNO_NAME_NUMBERING
};
561 aRunAttributeList
= xAccText
->getCharacterAttributes(0, aRequestedAttributes
);
563 sal_Int16 nLevel
= -1;
564 bool bIsCounted
= false;
565 for (const auto& attribute
: aRunAttributeList
)
567 if (attribute
.Name
.isEmpty())
569 if (attribute
.Name
== UNO_NAME_NUMBERING_LEVEL
)
570 attribute
.Value
>>= nLevel
;
571 else if (attribute
.Name
== UNO_NAME_NUMBERING
)
572 attribute
.Value
>>= bIsCounted
;
574 if (nLevel
< 0 || !bIsCounted
)
577 css::accessibility::TextSegment aTextSegment
=
578 xAccText
->getTextAtIndex(0, css::accessibility::AccessibleTextType::ATTRIBUTE_RUN
);
580 SAL_INFO("lok.a11y", "getListPrefixSize: prefix: " << aTextSegment
.SegmentText
<< ", level: " << nLevel
);
582 return aTextSegment
.SegmentEnd
;
585 void aboutTextFormatting(std::string msg
, const uno::Reference
<css::accessibility::XAccessibleText
>& xAccText
)
590 OUString sText
= xAccText
->getText();
591 sal_Int32 nLength
= sText
.getLength();
595 css::uno::Reference
<css::accessibility::XAccessibleTextAttributes
>
596 xAccTextAttr(xAccText
, uno::UNO_QUERY
);
597 css::uno::Sequence
< OUString
> aRequestedAttributes
;
600 while (nPos
< nLength
)
602 css::accessibility::TextSegment aTextSegment
=
603 xAccText
->getTextAtIndex(nPos
, css::accessibility::AccessibleTextType::ATTRIBUTE_RUN
);
604 SAL_INFO("lok.a11y", msg
<< ": "
605 "text segment: '" << aTextSegment
.SegmentText
606 << "', start: " << aTextSegment
.SegmentStart
607 << ", end: " << aTextSegment
.SegmentEnd
);
609 css::uno::Sequence
< css::beans::PropertyValue
> aRunAttributeList
;
610 if (xAccTextAttr
.is())
612 aRunAttributeList
= xAccTextAttr
->getRunAttributes(nPos
, aRequestedAttributes
);
616 aRunAttributeList
= xAccText
->getCharacterAttributes(nPos
, aRequestedAttributes
);
619 sal_Int32 nSize
= aRunAttributeList
.getLength();
621 msg
<< ": attribute list size: " << nSize
);
625 OUString sAttributes
= "{ ";
626 for (const auto& attribute
: aRunAttributeList
)
628 if (attribute
.Name
.isEmpty())
631 if (attribute
.Name
== "CharHeight" || attribute
.Name
== "CharWeight")
634 attribute
.Value
>>= fValue
;
635 sValue
= OUString::number(fValue
);
637 else if (attribute
.Name
== "CharPosture")
639 awt::FontSlant nValue
= awt::FontSlant_NONE
;
640 attribute
.Value
>>= nValue
;
641 sValue
= OUString::number(static_cast<unsigned int>(nValue
));
643 else if (attribute
.Name
== "CharUnderline")
645 sal_Int16 nValue
= 0;
646 attribute
.Value
>>= nValue
;
647 sValue
= OUString::number(nValue
);
649 else if (attribute
.Name
== "CharFontName")
651 attribute
.Value
>>= sValue
;
653 else if (attribute
.Name
== "Rsid")
655 sal_uInt32 nValue
= 0;
656 attribute
.Value
>>= nValue
;
657 sValue
= OUString::number(nValue
);
659 else if (attribute
.Name
== UNO_NAME_NUMBERING_LEVEL
)
661 sal_Int16
nValue(-1);
662 attribute
.Value
>>= nValue
;
663 sValue
= OUString::number(nValue
);
665 else if (attribute
.Name
== UNO_NAME_NUMBERING
)
668 attribute
.Value
>>= bValue
;
669 sValue
= OUString::boolean(bValue
);
671 else if (attribute
.Name
== UNO_NAME_NUMBERING_RULES
)
673 attribute
.Value
>>= sValue
;
676 if (!sValue
.isEmpty())
678 if (sAttributes
!= "{ ")
680 sAttributes
+= attribute
.Name
+ ": " + sValue
;
686 msg
<< ": " << sAttributes
);
688 nPos
= aTextSegment
.SegmentEnd
+ 1;
692 void aboutParagraph(std::string msg
, const OUString
& rsParagraphContent
, sal_Int32 nCaretPosition
,
693 sal_Int32 nSelectionStart
, sal_Int32 nSelectionEnd
, sal_Int32 nListPrefixLength
,
696 SAL_INFO("lok.a11y", msg
<< ": "
697 "\n text content: \"" << rsParagraphContent
<< "\""
698 "\n caret pos: " << nCaretPosition
699 << "\n selection: start: " << nSelectionStart
<< ", end: " << nSelectionEnd
700 << "\n list prefix length: " << nListPrefixLength
701 << "\n force: " << force
705 void aboutParagraph(std::string msg
, const uno::Reference
<css::accessibility::XAccessibleText
>& xAccText
,
711 OUString sText
= xAccText
->getText();
712 sal_Int32 nCaretPosition
= xAccText
->getCaretPosition();
713 sal_Int32 nSelectionStart
= xAccText
->getSelectionStart();
714 sal_Int32 nSelectionEnd
= xAccText
->getSelectionEnd();
715 sal_Int32 nListPrefixLength
= getListPrefixSize(xAccText
);
716 aboutParagraph(msg
, sText
, nCaretPosition
, nSelectionStart
, nSelectionEnd
, nListPrefixLength
, force
);
719 void aboutFocusedCellChanged(sal_Int32 nOutCount
, const std::vector
<TableSizeType
>& aInList
,
720 sal_Int32 nRow
, sal_Int32 nCol
, sal_Int32 nRowSpan
, sal_Int32 nColSpan
)
722 std::stringstream inListStream
;
723 inListStream
<< "[ ";
724 for (const auto& rTableSize
: aInList
)
726 inListStream
<< "{ rowCount: " << rTableSize
.nRowCount
<< " colCount: " << rTableSize
.nColCount
<< " } ";
730 SAL_INFO("lok.a11y", "LOKDocumentFocusListener::notifyFocusedCellChanged: "
731 "\n outCount: " << nOutCount
732 << "\n inList: " << inListStream
.str()
733 << "\n row: " << nRow
734 << "\n column: " << nCol
735 << "\n rowSpan: " << nRowSpan
736 << "\n colSpan: " << nColSpan
739 } // anonymous namespace
741 class LOKDocumentFocusListener
:
742 public ::cppu::WeakImplHelper
< accessibility::XAccessibleEventListener
>
744 static constexpr sal_Int64 MAX_ATTACHABLE_CHILDREN
= 100;
746 const SfxViewShell
* m_pViewShell
;
747 sal_Int16 m_nDocumentType
;
748 std::unordered_set
<uno::Reference
<uno::XInterface
>> m_aRefList
;
749 OUString m_sFocusedParagraph
;
750 sal_Int32 m_nCaretPosition
;
751 sal_Int32 m_nSelectionStart
;
752 sal_Int32 m_nSelectionEnd
;
753 sal_Int32 m_nListPrefixLength
;
754 uno::Reference
<accessibility::XAccessibleTable
> m_xLastTable
;
755 OUString m_sSelectedText
;
756 bool m_bIsEditingCell
;
757 // used for text content of a shape
758 bool m_bIsEditingInSelection
;
759 OUString m_sSelectedCellAddress
;
760 uno::Reference
<accessibility::XAccessible
> m_xSelectedObject
;
763 explicit LOKDocumentFocusListener(const SfxViewShell
* pViewShell
);
765 /// @throws lang::IndexOutOfBoundsException
766 /// @throws uno::RuntimeException
767 void attachRecursive(
768 const uno::Reference
< accessibility::XAccessible
>& xAccessible
771 /// @throws lang::IndexOutOfBoundsException
772 /// @throws uno::RuntimeException
773 void attachRecursive(
774 const uno::Reference
< accessibility::XAccessible
>& xAccessible
,
775 const uno::Reference
< accessibility::XAccessibleContext
>& xContext
778 /// @throws lang::IndexOutOfBoundsException
779 /// @throws uno::RuntimeException
780 void attachRecursive(
781 const uno::Reference
< accessibility::XAccessible
>& xAccessible
,
782 const uno::Reference
< accessibility::XAccessibleContext
>& xContext
,
783 const sal_Int64 nStateSet
786 /// @throws lang::IndexOutOfBoundsException
787 /// @throws uno::RuntimeException
788 void detachRecursive(
789 const uno::Reference
< accessibility::XAccessible
>& xAccessible
,
793 /// @throws lang::IndexOutOfBoundsException
794 /// @throws uno::RuntimeException
795 void detachRecursive(
796 const uno::Reference
< accessibility::XAccessibleContext
>& xContext
,
800 /// @throws lang::IndexOutOfBoundsException
801 /// @throws uno::RuntimeException
802 void detachRecursive(
803 const uno::Reference
< accessibility::XAccessibleContext
>& xContext
,
804 const sal_Int64 nStateSet
,
808 /// @throws lang::IndexOutOfBoundsException
809 /// @throws uno::RuntimeException
810 static uno::Reference
< accessibility::XAccessible
> getAccessible(const lang::EventObject
& aEvent
);
813 virtual void SAL_CALL
disposing( const lang::EventObject
& Source
) override
;
815 // XAccessibleEventListener
816 virtual void SAL_CALL
notifyEvent( const accessibility::AccessibleEventObject
& aEvent
) override
;
818 void notifyEditingInSelectionState(bool bParagraph
= true);
819 void notifyFocusedParagraphChanged(bool force
= false);
820 void notifyCaretChanged();
821 void notifyTextSelectionChanged();
822 void notifyFocusedCellChanged(sal_Int32 nOutCount
, const std::vector
<TableSizeType
>& aInList
, sal_Int32 nRow
, sal_Int32 nCol
, sal_Int32 nRowSpan
, sal_Int32 nColSpan
);
823 void notifySelectionChanged(const uno::Reference
<accessibility::XAccessible
>& xAccObj
, const OUString
& sAction
);
825 OUString
getFocusedParagraph() const;
826 int getCaretPosition() const;
829 void paragraphPropertiesToTree(boost::property_tree::ptree
& aPayloadTree
, bool force
= false) const;
830 void paragraphPropertiesToJson(std::string
& aPayload
, bool force
= false) const;
831 bool updateParagraphInfo(const uno::Reference
<css::accessibility::XAccessibleText
>& xAccText
,
832 bool force
, std::string msg
= "");
833 void updateAndNotifyParagraph(const uno::Reference
<css::accessibility::XAccessibleText
>& xAccText
,
834 bool force
, std::string msg
= "");
835 void resetParagraphInfo();
836 void onFocusedParagraphInWriterTable(const uno::Reference
<accessibility::XAccessibleTable
>& xTable
,
837 sal_Int64
& nChildIndex
,
838 const uno::Reference
<accessibility::XAccessibleText
>& xAccText
);
839 uno::Reference
< accessibility::XAccessible
>
840 getSelectedObject(const accessibility::AccessibleEventObject
& aEvent
) const;
841 void onShapeSelectionChanged(const Reference
<accessibility::XAccessible
>& xSelectedObject
,
842 const OUString
& sAction
);
845 LOKDocumentFocusListener::LOKDocumentFocusListener(const SfxViewShell
* pViewShell
)
846 : m_pViewShell(pViewShell
)
848 , m_nCaretPosition(0)
849 , m_nSelectionStart(0)
851 , m_nListPrefixLength(0)
852 , m_bIsEditingCell(false)
853 , m_bIsEditingInSelection(false)
857 void LOKDocumentFocusListener::paragraphPropertiesToTree(boost::property_tree::ptree
& aPayloadTree
, bool force
) const
859 bool bLeftToRight
= m_nCaretPosition
== m_nSelectionEnd
;
860 aPayloadTree
.put("content", m_sFocusedParagraph
.toUtf8().getStr());
861 aPayloadTree
.put("position", m_nCaretPosition
);
862 aPayloadTree
.put("start", bLeftToRight
? m_nSelectionStart
: m_nSelectionEnd
);
863 aPayloadTree
.put("end", bLeftToRight
? m_nSelectionEnd
: m_nSelectionStart
);
864 if (m_nListPrefixLength
> 0)
865 aPayloadTree
.put("listPrefixLength", m_nListPrefixLength
);
867 aPayloadTree
.put("force", 1);
870 void LOKDocumentFocusListener::paragraphPropertiesToJson(std::string
& aPayload
, bool force
) const
872 boost::property_tree::ptree aPayloadTree
;
873 paragraphPropertiesToTree(aPayloadTree
, force
);
874 std::stringstream aStream
;
875 boost::property_tree::write_json(aStream
, aPayloadTree
);
876 aPayload
= aStream
.str();
879 OUString
LOKDocumentFocusListener::getFocusedParagraph() const
881 aboutView("LOKDocumentFocusListener::getFocusedParagraph", this, m_pViewShell
);
882 aboutParagraph("LOKDocumentFocusListener::getFocusedParagraph",
883 m_sFocusedParagraph
, m_nCaretPosition
,
884 m_nSelectionStart
, m_nSelectionEnd
, m_nListPrefixLength
);
886 std::string aPayload
;
887 paragraphPropertiesToJson(aPayload
);
888 OUString sRet
= OUString::fromUtf8(aPayload
);
892 int LOKDocumentFocusListener::getCaretPosition() const
894 aboutView("LOKDocumentFocusListener::getCaretPosition", this, m_pViewShell
);
895 SAL_INFO("lok.a11y", "LOKDocumentFocusListener::getCaretPosition: " << m_nCaretPosition
);
896 return m_nCaretPosition
;
899 // notifyEditingInSelectionState
900 // Used for notifying when editing becomes active/disabled for a shape
901 // bParagraph: should we append currently focused paragraph ?
902 // The problem is that the initially focused paragraph could not be the one user has clicked on,
903 // when there are more than a single paragraph.
904 // So in some case sending the focused paragraph could be misleading.
905 void LOKDocumentFocusListener::notifyEditingInSelectionState(bool bParagraph
)
907 aboutView("LOKDocumentFocusListener::notifyEditingInSelectionState", this, m_pViewShell
);
909 boost::property_tree::ptree aPayloadTree
;
910 bool bIsCell
= !m_sSelectedCellAddress
.isEmpty();
911 aPayloadTree
.put("cell", bIsCell
? 1 : 0);
914 aPayloadTree
.put("enabled", m_bIsEditingCell
? 1 : 0);
915 if (m_bIsEditingCell
)
917 aPayloadTree
.put("selection", m_sSelectedCellAddress
);
919 aPayloadTree
.put("paragraph", m_sFocusedParagraph
);
924 aPayloadTree
.put("enabled", m_bIsEditingInSelection
? 1 : 0);
925 if (m_bIsEditingInSelection
&& m_xSelectedObject
.is())
927 uno::Reference
<accessibility::XAccessibleContext
> xContext
= m_xSelectedObject
->getAccessibleContext();
930 OUString sSelectionDescr
= xContext
->getAccessibleName();
931 sSelectionDescr
= sSelectionDescr
.trim();
932 aPayloadTree
.put("selection", sSelectionDescr
);
934 aPayloadTree
.put("paragraph", m_sFocusedParagraph
);
939 std::stringstream aStream
;
940 boost::property_tree::write_json(aStream
, aPayloadTree
);
941 std::string aPayload
= aStream
.str();
944 SAL_INFO("lok.a11y", "LOKDocumentFocusListener::notifyEditingInSelectionState: payload: \n" << aPayload
);
945 m_pViewShell
->libreOfficeKitViewCallback(LOK_CALLBACK_A11Y_EDITING_IN_SELECTION_STATE
, aPayload
.c_str());
949 /// notifyFocusedParagraphChanged
951 // Notify content, caret position and text selection start/end for the focused paragraph
953 // For focused we don't mean to be necessarily the currently focused accessibility node.
954 // It's enough that the caret is present in the paragraph (position != -1).
955 // In fact each view has its own accessibility node per each text paragraph.
956 // Anyway there can be only one focused accessibility node at time.
957 // So when text changes are performed in one view, both accessibility nodes emit
958 // a text changed event, anyway only the accessibility node belonging to the view
959 // where the text change has occurred is the focused one.
961 // force: when true update the clipboard content even if client is composing.
963 // Usually when editing on the client involves composing the clipboard area updating
964 // is skipped until the composition is over.
965 // On the contrary the composition would be aborted, making dictation not possible.
966 // Anyway when the text change has been performed by another view we are in due
967 // to update the clipboard content even if the user is in the middle of a composition.
968 void LOKDocumentFocusListener::notifyFocusedParagraphChanged(bool force
)
970 aboutView("LOKDocumentFocusListener::notifyFocusedParagraphChanged", this, m_pViewShell
);
971 std::string aPayload
;
972 paragraphPropertiesToJson(aPayload
, force
);
975 aboutParagraph("LOKDocumentFocusListener::notifyFocusedParagraphChanged",
976 m_sFocusedParagraph
, m_nCaretPosition
,
977 m_nSelectionStart
, m_nSelectionEnd
, m_nListPrefixLength
, force
);
979 m_pViewShell
->libreOfficeKitViewCallback(LOK_CALLBACK_A11Y_FOCUS_CHANGED
, aPayload
.c_str());
983 void LOKDocumentFocusListener::notifyCaretChanged()
985 aboutView("LOKDocumentFocusListener::notifyCaretChanged", this, m_pViewShell
);
986 boost::property_tree::ptree aPayloadTree
;
987 aPayloadTree
.put("position", m_nCaretPosition
);
988 std::stringstream aStream
;
989 boost::property_tree::write_json(aStream
, aPayloadTree
);
990 std::string aPayload
= aStream
.str();
993 SAL_INFO("lok.a11y", "LOKDocumentFocusListener::notifyCaretChanged: " << m_nCaretPosition
);
994 m_pViewShell
->libreOfficeKitViewCallback(LOK_CALLBACK_A11Y_CARET_CHANGED
, aPayload
.c_str());
998 void LOKDocumentFocusListener::notifyTextSelectionChanged()
1000 aboutView("LOKDocumentFocusListener::notifyTextSelectionChanged", this, m_pViewShell
);
1001 bool bLeftToRight
= m_nCaretPosition
== m_nSelectionEnd
;
1002 boost::property_tree::ptree aPayloadTree
;
1003 aPayloadTree
.put("start", bLeftToRight
? m_nSelectionStart
: m_nSelectionEnd
);
1004 aPayloadTree
.put("end", bLeftToRight
? m_nSelectionEnd
: m_nSelectionStart
);
1005 std::stringstream aStream
;
1006 boost::property_tree::write_json(aStream
, aPayloadTree
);
1007 std::string aPayload
= aStream
.str();
1010 SAL_INFO("lok.a11y", "LOKDocumentFocusListener::notifyTextSelectionChanged: "
1011 "start: " << m_nSelectionStart
<< ", end: " << m_nSelectionEnd
);
1012 m_pViewShell
->libreOfficeKitViewCallback(LOK_CALLBACK_A11Y_TEXT_SELECTION_CHANGED
, aPayload
.c_str());
1016 void LOKDocumentFocusListener::notifyFocusedCellChanged(
1017 sal_Int32 nOutCount
, const std::vector
<TableSizeType
>& aInList
,
1018 sal_Int32 nRow
, sal_Int32 nCol
, sal_Int32 nRowSpan
, sal_Int32 nColSpan
)
1020 aboutView("LOKDocumentFocusListener::notifyTablePositionChanged", this, m_pViewShell
);
1021 boost::property_tree::ptree aPayloadTree
;
1024 aPayloadTree
.put("outCount", nOutCount
);
1026 if (!aInList
.empty())
1028 boost::property_tree::ptree aInListNode
;
1029 for (const auto& rTableSize
: aInList
)
1031 boost::property_tree::ptree aTableSizeNode
;
1032 aTableSizeNode
.put("rowCount", rTableSize
.nRowCount
);
1033 aTableSizeNode
.put("colCount", rTableSize
.nColCount
);
1035 aInListNode
.push_back(std::make_pair(std::string(), aTableSizeNode
));
1037 aPayloadTree
.add_child("inList", aInListNode
);
1040 aPayloadTree
.put("row", nRow
);
1041 aPayloadTree
.put("col", nCol
);
1045 aPayloadTree
.put("rowSpan", nRowSpan
);
1049 aPayloadTree
.put("colSpan", nColSpan
);
1052 boost::property_tree::ptree aContentNode
;
1053 paragraphPropertiesToTree(aContentNode
);
1054 aPayloadTree
.add_child("paragraph", aContentNode
);
1056 std::stringstream aStream
;
1057 boost::property_tree::write_json(aStream
, aPayloadTree
);
1058 std::string aPayload
= aStream
.str();
1061 aboutFocusedCellChanged(nOutCount
, aInList
, nRow
, nCol
, nRowSpan
, nColSpan
);
1062 aboutParagraph("LOKDocumentFocusListener::notifyFocusedCellChanged: paragraph: ",
1063 m_sFocusedParagraph
, m_nCaretPosition
, m_nSelectionStart
, m_nSelectionEnd
,
1064 m_nListPrefixLength
, false);
1066 m_pViewShell
->libreOfficeKitViewCallback(LOK_CALLBACK_A11Y_FOCUSED_CELL_CHANGED
, aPayload
.c_str());
1070 void LOKDocumentFocusListener::notifySelectionChanged(const uno::Reference
<accessibility::XAccessible
>& xAccObj
,
1071 const OUString
& sAction
)
1073 using namespace accessibility
;
1077 aboutView("LOKDocumentFocusListener::notifySelectionChanged", this, m_pViewShell
);
1078 uno::Reference
<XAccessibleContext
> xContext
= xAccObj
->getAccessibleContext();
1081 OUString sName
= xContext
->getAccessibleName();
1082 sName
= sName
.trim();
1083 if (sName
== "GraphicObjectShape")
1086 // check for text content and send it with some limitations:
1087 // no more than 10 paragraphs, no more than 1000 chars
1088 bool bIsCell
= xContext
->getAccessibleRole() == AccessibleRole::TABLE_CELL
;
1089 OUString sTextContent
;
1090 if (sAction
== "create" || sAction
== "add")
1092 const sal_Int64 nMaxJoinedParagraphs
= 10;
1093 const sal_Int32 nMaxTextContentLength
= 1000;
1096 uno::Reference
<XAccessibleText
> xAccText(xAccObj
, uno::UNO_QUERY
);
1099 sTextContent
= xAccText
->getText();
1100 sal_Int32 nTextLength
= sTextContent
.getLength();
1101 if (nTextLength
> nMaxTextContentLength
)
1103 sTextContent
= truncateText(sTextContent
, nMaxTextContentLength
);
1109 sal_Int32 nTotalTextLength
= 0;
1110 sal_Int64 nChildCount
= xContext
->getAccessibleChildCount();
1111 if (nChildCount
> nMaxJoinedParagraphs
)
1112 nChildCount
= nMaxJoinedParagraphs
;
1113 for (sal_Int64 i
= 0; i
< nChildCount
; ++i
)
1115 uno::Reference
<XAccessible
> xChild
= xContext
->getAccessibleChild(i
);
1116 uno::Reference
<XAccessibleText
> xAccText(xChild
, uno::UNO_QUERY
);
1119 OUString sText
= xAccText
->getText();
1120 sal_Int32 nTextLength
= sText
.getLength();
1121 if (nTextLength
< 1)
1123 if (nTotalTextLength
+ nTextLength
< nMaxTextContentLength
)
1125 nTotalTextLength
+= nTextLength
;
1126 sTextContent
+= sText
+ " \n";
1130 // truncate paragraph
1131 sal_Int32 nNewLength
= nMaxTextContentLength
- nTotalTextLength
;
1132 sTextContent
+= truncateText(sText
, nNewLength
);
1139 boost::property_tree::ptree aPayloadTree
;
1140 aPayloadTree
.put("cell", bIsCell
? 1 : 0);
1141 aPayloadTree
.put("action", sAction
);
1142 aPayloadTree
.put("name", sName
);
1143 if (!sTextContent
.isEmpty())
1144 aPayloadTree
.put("text", sTextContent
);
1145 std::stringstream aStream
;
1146 boost::property_tree::write_json(aStream
, aPayloadTree
);
1147 std::string aPayload
= aStream
.str();
1150 SAL_INFO("lok.a11y", "LOKDocumentFocusListener::notifySelectionChanged: "
1151 "action: " << sAction
<< ", name: " << sName
);
1152 m_pViewShell
->libreOfficeKitViewCallback(LOK_CALLBACK_A11Y_SELECTION_CHANGED
, aPayload
.c_str());
1157 void LOKDocumentFocusListener::disposing( const lang::EventObject
& aEvent
)
1159 // Unref the object here, but do not remove as listener since the object
1160 // might no longer be in a state that safely allows this.
1161 if( aEvent
.Source
.is() )
1162 m_aRefList
.erase(aEvent
.Source
);
1166 bool LOKDocumentFocusListener::updateParagraphInfo(const uno::Reference
<css::accessibility::XAccessibleText
>& xAccText
,
1167 bool force
, std::string msg
)
1172 bool bNotify
= false;
1173 // If caret is present inside the paragraph (pos != -1), it means that paragraph has focus in the current view.
1174 sal_Int32 nCaretPosition
= xAccText
->getCaretPosition();
1175 if (nCaretPosition
>= 0)
1177 OUString sText
= xAccText
->getText();
1178 m_nCaretPosition
= nCaretPosition
;
1179 m_nSelectionStart
= xAccText
->getSelectionStart();
1180 m_nSelectionEnd
= xAccText
->getSelectionEnd();
1181 m_nListPrefixLength
= getListPrefixSize(xAccText
);
1183 // Inside a text shape when there is no selection, selection-start and selection-end are
1184 // set to current caret position instead of -1. Moreover, inside a text shape pressing
1185 // delete or backspace with an empty selection really deletes text and not only the empty
1186 // selection as it occurs in a text paragraph in Writer.
1187 // So whenever selection-start == selection-end, and we are inside a shape we need
1188 // to set these parameters to -1 in order to have the client to handle delete and
1189 // backspace properly.
1190 if (m_nSelectionStart
== m_nSelectionEnd
&& m_nSelectionStart
!= -1)
1192 uno::Reference
<accessibility::XAccessibleContext
> xContext(xAccText
, uno::UNO_QUERY
);
1193 sal_Int16 nParentRole
= getParentRole(xContext
);
1194 if (nParentRole
== accessibility::AccessibleRole::SHAPE
||
1195 nParentRole
== accessibility::AccessibleRole::TEXT_FRAME
) // spreadsheet cell editing
1196 m_nSelectionStart
= m_nSelectionEnd
= -1;
1199 // In case only caret position or text selection are different we can rely on specific events.
1200 if (m_sFocusedParagraph
!= sText
)
1202 m_sFocusedParagraph
= sText
;
1208 SAL_WARN("lok.a11y",
1209 "LOKDocumentFocusListener::updateParagraphInfo: skipped since no caret is present");
1212 std::string header
= "LOKDocumentFocusListener::updateParagraphInfo";
1214 header
+= ": " + msg
;
1215 aboutParagraph(header
, xAccText
, force
);
1220 void LOKDocumentFocusListener::updateAndNotifyParagraph(
1221 const uno::Reference
<css::accessibility::XAccessibleText
>& xAccText
,
1222 bool force
, std::string msg
)
1224 if (updateParagraphInfo(xAccText
, force
, msg
))
1225 notifyFocusedParagraphChanged(force
);
1228 void LOKDocumentFocusListener::resetParagraphInfo()
1230 m_sFocusedParagraph
= "";
1231 m_nCaretPosition
= 0;
1232 m_nSelectionStart
= -1;
1233 m_nSelectionEnd
= -1;
1234 m_nListPrefixLength
= 0;
1237 // For a presentation document when an accessible event of type SELECTION_CHANGED_XXX occurs
1238 // the selected (or unselected) object is put in NewValue, instead for a text document
1239 // the selected object is put in Source.
1240 // The following function helps to retrieve the selected object independently on where it has been put.
1241 uno::Reference
< accessibility::XAccessible
>
1242 LOKDocumentFocusListener::getSelectedObject(const accessibility::AccessibleEventObject
& aEvent
) const
1244 uno::Reference
< accessibility::XAccessible
> xSelectedObject
;
1245 if (isText(m_nDocumentType
))
1247 xSelectedObject
.set(aEvent
.Source
, uno::UNO_QUERY
);
1251 aEvent
.NewValue
>>= xSelectedObject
;
1253 return xSelectedObject
;
1256 void LOKDocumentFocusListener::onShapeSelectionChanged(
1257 const uno::Reference
<accessibility::XAccessible
>& xSelectedObject
,
1258 const OUString
& sAction
)
1260 // when a shape is selected or unselected we could need to notify that text content editing
1261 // is no more active, that allows on the client side to prevent default input.
1262 resetParagraphInfo();
1263 if (m_bIsEditingInSelection
)
1265 m_bIsEditingInSelection
= false;
1266 notifyEditingInSelectionState();
1268 notifySelectionChanged(xSelectedObject
, sAction
);
1271 void LOKDocumentFocusListener::onFocusedParagraphInWriterTable(
1272 const uno::Reference
<accessibility::XAccessibleTable
>& xTable
,
1273 sal_Int64
& nChildIndex
,
1274 const uno::Reference
<accessibility::XAccessibleText
>& xAccText
1277 std::vector
<TableSizeType
> aInList
;
1278 sal_Int32 nOutCount
= 0;
1280 if (m_xLastTable
.is())
1282 if (xTable
!= m_xLastTable
)
1284 // do we get in one or more nested tables ?
1285 // check if xTable is a descendant of m_xLastTable
1286 XAccessibleTableList newTableAncestorList
;
1287 bool isLastAncestorOfNew
= getAncestorList(newTableAncestorList
, xTable
, m_xLastTable
);
1288 bool isNewAncestorOfLast
= false;
1289 if (!isLastAncestorOfNew
)
1291 // do we get out of one or more nested tables ?
1292 // check if m_xLastTable is a descendant of xTable
1293 XAccessibleTableList lastTableAncestorList
;
1294 isNewAncestorOfLast
= getAncestorList(lastTableAncestorList
, m_xLastTable
, xTable
);
1295 // we have to notify "out of table" for all m_xLastTable ancestors up to xTable
1296 // or the first not-a-table ancestor
1297 nOutCount
= lastTableAncestorList
.size();
1299 if (isLastAncestorOfNew
|| !isNewAncestorOfLast
)
1301 // we have to notify row/col count for all xTable ancestors starting from the ancestor
1302 // which is a child of m_xLastTable (isLastAncestorOfNew) or the first not-a-table ancestor
1303 for (const auto& ancestor
: newTableAncestorList
)
1305 TableSizeType aTableSize
{ancestor
->getAccessibleRowCount(),
1306 ancestor
->getAccessibleColumnCount()};
1307 aInList
.push_back(aTableSize
);
1314 // cursor was not inside any table and gets inside one or more tables
1315 // we have to notify row/col count for all xTable ancestors starting from first not-a-table ancestor
1316 XAccessibleTableList newTableAncestorList
;
1317 getAncestorList(newTableAncestorList
, xTable
);
1318 for (const auto& ancestor
: newTableAncestorList
)
1320 TableSizeType aTableSize
{ancestor
->getAccessibleRowCount(),
1321 ancestor
->getAccessibleColumnCount()};
1322 aInList
.push_back(aTableSize
);
1326 // we have to notify current row/col of xTable and related row/col span
1327 sal_Int32 nRow
= xTable
->getAccessibleRow(nChildIndex
);
1328 sal_Int32 nCol
= xTable
->getAccessibleColumn(nChildIndex
);
1329 sal_Int32 nRowSpan
= xTable
->getAccessibleRowExtentAt(nRow
, nCol
);
1330 sal_Int32 nColSpan
= xTable
->getAccessibleColumnExtentAt(nRow
, nCol
);
1332 m_xLastTable
= xTable
;
1333 updateParagraphInfo(xAccText
, false, "STATE_CHANGED: FOCUSED");
1334 notifyFocusedCellChanged(nOutCount
, aInList
, nRow
, nCol
, nRowSpan
, nColSpan
);
1337 void LOKDocumentFocusListener::notifyEvent(const accessibility::AccessibleEventObject
& aEvent
)
1339 using namespace accessibility
;
1340 aboutView("LOKDocumentFocusListener::notifyEvent", this, m_pViewShell
);
1344 aboutEvent("LOKDocumentFocusListener::notifyEvent", aEvent
);
1346 switch (aEvent
.EventId
)
1348 case AccessibleEventId::STATE_CHANGED
:
1351 sal_Int64 nState
= accessibility::AccessibleStateType::INVALID
;
1352 aEvent
.NewValue
>>= nState
;
1353 sal_Int64 nOldState
= accessibility::AccessibleStateType::INVALID
;
1354 aEvent
.OldValue
>>= nOldState
;
1355 SAL_INFO("lok.a11y", "LOKDocumentFocusListener::notifyEvent: STATE_CHANGED: "
1356 " New State: " << stateSetToString(nState
)
1357 << ", Old State: " << stateSetToString(nOldState
));
1360 uno::Reference
< XAccessible
> xAccessibleObject
= getAccessible(aEvent
);
1361 if (!xAccessibleObject
.is())
1363 uno::Reference
<XAccessibleContext
> xContext(aEvent
.Source
, uno::UNO_QUERY
);
1367 sal_Int16 nRole
= xContext
->getAccessibleRole();
1369 if (nRole
== AccessibleRole::PARAGRAPH
)
1371 uno::Reference
<XAccessibleText
> xAccText(xAccessibleObject
, uno::UNO_QUERY
);
1377 case AccessibleStateType::ACTIVE
:
1379 if (!m_bIsEditingInSelection
&& hasToBeActiveForEditing(getParentRole(xContext
)))
1381 m_bIsEditingInSelection
= true;
1385 case AccessibleStateType::FOCUSED
:
1387 if (m_bIsEditingInSelection
&& m_xSelectedObject
.is())
1389 updateParagraphInfo(xAccText
, true, "STATE_CHANGED: FOCUSED");
1390 notifyEditingInSelectionState(getAccessibleSiblingCount(xContext
) == 0);
1391 notifyFocusedParagraphChanged(true);
1392 // we clear selected object so when editing is over but shape is
1393 // still selected, the selection event is notified the same to the client
1394 m_xSelectedObject
.clear();
1397 if (isText(m_nDocumentType
))
1399 // check if we are inside a table: in case notify table and current cell info
1400 bool isInsideTable
= false;
1401 uno::Reference
<XAccessibleTable
> xTable
;
1402 sal_Int64 nChildIndex
;
1403 lookForParentTable(xContext
, xTable
, nChildIndex
);
1406 onFocusedParagraphInWriterTable(xTable
, nChildIndex
, xAccText
);
1407 isInsideTable
= true;
1409 // paragraph is not inside any table
1412 if (m_xLastTable
.is())
1414 // we get out one or more tables
1415 // we have to notify "out of table" for all m_xLastTable ancestors
1416 // up to the first not-a-table ancestor
1417 XAccessibleTableList lastTableAncestorList
;
1418 getAncestorList(lastTableAncestorList
, m_xLastTable
);
1419 sal_Int32 nOutCount
= lastTableAncestorList
.size();
1420 // no more inside a table
1421 m_xLastTable
.clear();
1423 std::vector
<TableSizeType
> aInList
;
1424 updateParagraphInfo(xAccText
, false, "STATE_CHANGED: FOCUSED");
1425 notifyFocusedCellChanged(nOutCount
, aInList
, -1, -1, 1, 1);
1429 updateAndNotifyParagraph(xAccText
, false, "STATE_CHANGED: FOCUSED");
1433 else if (isSpreadsheet(m_nDocumentType
))
1435 if (m_bIsEditingCell
)
1437 if (!hasState(aEvent
, AccessibleStateType::ACTIVE
))
1439 SAL_WARN("lok.a11y",
1440 "LOKDocumentFocusListener::notifyEvent: FOCUSED: "
1441 "cell not ACTIVE for editing yet");
1444 else if (m_xSelectedObject
.is())
1446 updateParagraphInfo(xAccText
, true, "STATE_CHANGED: ACTIVE");
1447 notifyEditingInSelectionState(getAccessibleSiblingCount(xContext
) == 0);
1448 notifyFocusedParagraphChanged(true);
1449 m_xSelectedObject
.clear();
1453 updateAndNotifyParagraph(xAccText
, false, "STATE_CHANGED: FOCUSED");
1456 else if (isPresentation(m_nDocumentType
))
1458 updateAndNotifyParagraph(xAccText
, false, "STATE_CHANGED: FOCUSED");
1460 aboutTextFormatting("LOKDocumentFocusListener::notifyEvent: STATE_CHANGED: FOCUSED", xAccText
);
1470 case AccessibleEventId::CARET_CHANGED
:
1472 sal_Int32 nNewPos
= -1;
1473 aEvent
.NewValue
>>= nNewPos
;
1474 sal_Int32 nOldPos
= -1;
1475 aEvent
.OldValue
>>= nOldPos
;
1479 SAL_INFO("lok.a11y", "LOKDocumentFocusListener::notifyEvent: CARET_CHANGED: "
1480 "new pos: " << nNewPos
<< ", nOldPos: " << nOldPos
);
1482 uno::Reference
<XAccessibleText
> xAccText(getAccessible(aEvent
), uno::UNO_QUERY
);
1485 m_nCaretPosition
= nNewPos
;
1486 // Let's say we are in the following case: 'Hello wor|ld',
1487 // where '|' is the cursor position for the current view.
1488 // Suppose that in another view it's typed <enter> soon before 'world'.
1489 // Now the new paragraph content and caret position is: 'wor|ld'.
1490 // Anyway no new paragraph focused event is emitted for current view.
1491 // Only a new caret position event is emitted.
1492 // So we could need to notify a new focused paragraph changed message.
1493 if (!isFocused(aEvent
))
1495 if (updateParagraphInfo(xAccText
, false, "CARET_CHANGED"))
1496 notifyFocusedParagraphChanged(true);
1500 notifyCaretChanged();
1502 aboutParagraph("LOKDocumentFocusListener::notifyEvent: CARET_CHANGED", xAccText
);
1507 case AccessibleEventId::TEXT_CHANGED
:
1509 TextSegment aDeletedText
;
1510 TextSegment aInsertedText
;
1512 if (aEvent
.OldValue
>>= aDeletedText
)
1514 SAL_INFO("lok.a11y", "LOKDocumentFocusListener::notifyEvent: TEXT_CHANGED: "
1515 "deleted text: >" << aDeletedText
.SegmentText
<< "<");
1517 if (aEvent
.NewValue
>>= aInsertedText
)
1519 SAL_INFO("lok.a11y", "LOKDocumentFocusListener::notifyEvent: TEXT_CHANGED: "
1520 "inserted text: >" << aInsertedText
.SegmentText
<< "<");
1522 uno::Reference
<XAccessibleText
> xAccText(getAccessible(aEvent
), uno::UNO_QUERY
);
1524 // When the change has been performed in another view we need to force
1525 // paragraph content updating on the client, even if current editing involves composing.
1526 // We make a guess that if the paragraph accessibility node is not focused,
1527 // it means that the text change has been performed in another view.
1528 updateAndNotifyParagraph(xAccText
, !isFocused(aEvent
), "TEXT_CHANGED");
1532 case AccessibleEventId::TEXT_SELECTION_CHANGED
:
1534 if (!isFocused(aEvent
))
1536 SAL_WARN("lok.a11y",
1537 "LOKDocumentFocusListener::notifyEvent: TEXT_SELECTION_CHANGED: "
1538 "skip non focused paragraph");
1542 uno::Reference
<XAccessibleText
> xAccText(getAccessible(aEvent
), uno::UNO_QUERY
);
1545 // We send a message to client also when start/end are -1, in this way the client knows
1546 // if a text selection object exists or not. That's needed because of the odd behavior
1547 // occurring when <backspace>/<delete> are hit and a text selection is empty,
1548 // but it still exists.
1549 // Such keys delete the empty selection instead of the previous/next char.
1550 updateParagraphInfo(xAccText
, false, "TEXT_SELECTION_CHANGED");
1552 m_sSelectedText
= xAccText
->getSelectedText();
1553 SAL_INFO("lok.a11y",
1554 "LOKDocumentFocusListener::notifyEvent: TEXT_SELECTION_CHANGED: selected text: >"
1555 << m_sSelectedText
<< "<");
1557 // Calc: when editing a formula send the update content
1558 if (m_bIsEditingCell
)
1560 OUString sText
= xAccText
->getText();
1561 if (!m_sSelectedCellAddress
.isEmpty() &&
1562 !m_sSelectedText
.isEmpty() && sText
.startsWith("="))
1564 notifyFocusedParagraphChanged();
1567 notifyTextSelectionChanged();
1571 case AccessibleEventId::SELECTION_CHANGED
:
1572 case AccessibleEventId::SELECTION_CHANGED_REMOVE
:
1574 uno::Reference
< XAccessible
> xSelectedObject
= getSelectedObject(aEvent
);
1575 if (!xSelectedObject
.is())
1577 uno::Reference
< XAccessibleContext
> xContext
= xSelectedObject
->getAccessibleContext();
1581 if (aEvent
.EventId
== AccessibleEventId::SELECTION_CHANGED_REMOVE
)
1582 m_xSelectedObject
.clear();
1583 else if (m_xSelectedObject
.is() && m_xSelectedObject
== xSelectedObject
)
1584 return; // selecting the same object; note: on editing selected object is cleared
1586 m_xSelectedObject
= xSelectedObject
;
1587 SAL_INFO("lok.a11y", "LOKDocumentFocusListener::notifyEvent: SELECTION_CHANGED: "
1588 "m_xSelectedObject.is(): " << m_xSelectedObject
.is());
1590 OUString sAction
= selectionEventTypeToString(aEvent
.EventId
);
1591 sal_Int16 nRole
= xContext
->getAccessibleRole();
1594 case AccessibleRole::GRAPHIC
:
1595 case AccessibleRole::EMBEDDED_OBJECT
:
1596 case AccessibleRole::SHAPE
:
1598 onShapeSelectionChanged(xSelectedObject
, sAction
);
1601 case AccessibleRole::TABLE_CELL
:
1603 notifySelectionChanged(xSelectedObject
, sAction
);
1605 if (aEvent
.EventId
== AccessibleEventId::SELECTION_CHANGED
)
1607 m_sSelectedCellAddress
= xContext
->getAccessibleName();
1608 if (m_bIsEditingCell
&& !m_sSelectedCellAddress
.isEmpty())
1610 // Check cell address: "$Sheet1.A10".
1611 // On cell editing SELECTION_CHANGED is not emitted when selection is expanded.
1612 // So selection can't be a cell range.
1613 sal_Int32 nDotIndex
= m_sSelectedText
.indexOf('.');
1614 OUString sCellAddress
= m_sSelectedText
.copy(nDotIndex
+ 1);
1615 SAL_INFO("lok.a11y", "LOKDocumentFocusListener::notifyEvent: SELECTION_CHANGED: "
1616 "cell address: >" << sCellAddress
<< "<");
1617 if (m_sSelectedCellAddress
== sCellAddress
)
1619 notifyFocusedParagraphChanged();
1620 notifyTextSelectionChanged();
1631 case AccessibleEventId::CHILD
:
1633 uno::Reference
< accessibility::XAccessible
> xChild
;
1634 if( (aEvent
.OldValue
>>= xChild
) && xChild
.is() )
1635 detachRecursive(xChild
);
1637 if( (aEvent
.NewValue
>>= xChild
) && xChild
.is() )
1638 attachRecursive(xChild
);
1642 case AccessibleEventId::INVALIDATE_ALL_CHILDREN
:
1644 SAL_INFO("lok.a11y", "Invalidate all children called");
1651 catch( const lang::IndexOutOfBoundsException
& )
1653 LOK_WARN("lok.a11y",
1654 "LOKDocumentFocusListener::notifyEvent:Focused object has invalid index in parent");
1658 uno::Reference
< accessibility::XAccessible
> LOKDocumentFocusListener::getAccessible(const lang::EventObject
& aEvent
)
1660 uno::Reference
< accessibility::XAccessible
> xAccessible(aEvent
.Source
, uno::UNO_QUERY
);
1662 if( xAccessible
.is() )
1665 SAL_WARN("lok.a11y",
1666 "LOKDocumentFocusListener::getAccessible: Event source doesn't implement XAccessible.");
1668 uno::Reference
< accessibility::XAccessibleContext
> xContext(aEvent
.Source
, uno::UNO_QUERY
);
1672 uno::Reference
< accessibility::XAccessible
> xParent( xContext
->getAccessibleParent() );
1675 uno::Reference
< accessibility::XAccessibleContext
> xParentContext( xParent
->getAccessibleContext() );
1676 if( xParentContext
.is() )
1678 return xParentContext
->getAccessibleChild( xContext
->getAccessibleIndexInParent() );
1683 LOK_WARN("lok.a11y",
1684 "LOKDocumentFocusListener::getAccessible: Can't get any accessible object from event source.");
1686 return uno::Reference
< accessibility::XAccessible
>();
1689 void LOKDocumentFocusListener::attachRecursive(
1690 const uno::Reference
< accessibility::XAccessible
>& xAccessible
1693 LOK_INFO("lok.a11y", "LOKDocumentFocusListener::attachRecursive(1): xAccessible: " << xAccessible
.get());
1695 uno::Reference
< accessibility::XAccessibleContext
> xContext
=
1696 xAccessible
->getAccessibleContext();
1699 attachRecursive(xAccessible
, xContext
);
1702 void LOKDocumentFocusListener::attachRecursive(
1703 const uno::Reference
< accessibility::XAccessible
>& xAccessible
,
1704 const uno::Reference
< accessibility::XAccessibleContext
>& xContext
1709 LOK_INFO("lok.a11y", "LOKDocumentFocusListener::attachRecursive(2): xAccessible: "
1710 << xAccessible
.get() << ", role: " << xContext
->getAccessibleRole()
1711 << ", name: " << xContext
->getAccessibleName()
1712 << ", parent: " << xContext
->getAccessibleParent().get()
1713 << ", child count: " << xContext
->getAccessibleChildCount());
1715 sal_Int64 nStateSet
= xContext
->getAccessibleStateSet();
1717 if (!m_bIsEditingCell
)
1719 ::rtl::OUString sName
= xContext
->getAccessibleName();
1720 m_bIsEditingCell
= sName
.startsWith("Cell");
1723 attachRecursive(xAccessible
, xContext
, nStateSet
);
1725 catch (const uno::Exception
& e
)
1727 LOK_WARN("lok.a11y", "LOKDocumentFocusListener::attachRecursive(2): raised exception: " << e
.Message
);
1731 void LOKDocumentFocusListener::attachRecursive(
1732 const uno::Reference
< accessibility::XAccessible
>& xAccessible
,
1733 const uno::Reference
< accessibility::XAccessibleContext
>& xContext
,
1734 const sal_Int64 nStateSet
1737 aboutView("LOKDocumentFocusListener::attachRecursive (3)", this, m_pViewShell
);
1738 SAL_INFO("lok.a11y", "LOKDocumentFocusListener::attachRecursive(3) #1: this: " << this
1739 << ", xAccessible: " << xAccessible
.get()
1740 << ", role: " << xContext
->getAccessibleRole()
1741 << ", name: " << xContext
->getAccessibleName()
1742 << ", index in parent: " << xContext
->getAccessibleIndexInParent()
1743 << ", state: " << stateSetToString(nStateSet
)
1744 << ", parent: " << xContext
->getAccessibleParent().get()
1745 << ", child count: " << xContext
->getAccessibleChildCount());
1747 uno::Reference
< accessibility::XAccessibleEventBroadcaster
> xBroadcaster(xContext
, uno::UNO_QUERY
);
1749 if (!xBroadcaster
.is())
1751 SAL_INFO("lok.a11y", "LOKDocumentFocusListener::attachRecursive(3) #2: xBroadcaster.is()");
1752 // If not already done, add the broadcaster to the list and attach as listener.
1753 const uno::Reference
< uno::XInterface
>& xInterface
= xBroadcaster
;
1754 if( m_aRefList
.insert(xInterface
).second
)
1756 SAL_INFO("lok.a11y", "LOKDocumentFocusListener::attachRecursive(3) #3: m_aRefList.insert(xInterface).second");
1757 xBroadcaster
->addAccessibleEventListener(static_cast< accessibility::XAccessibleEventListener
*>(this));
1759 if (isDocument(xContext
->getAccessibleRole()))
1761 m_nDocumentType
= xContext
->getAccessibleRole();
1764 if (!(nStateSet
& accessibility::AccessibleStateType::MANAGES_DESCENDANTS
))
1766 if ((nStateSet
& accessibility::AccessibleStateType::SELECTED
) &&
1767 selectionHasToBeNotified(xContext
))
1769 uno::Reference
< accessibility::XAccessible
> xAccObj(xContext
, uno::UNO_QUERY
);
1770 onShapeSelectionChanged(xAccObj
, "create");
1773 sal_Int64 nmax
= xContext
->getAccessibleChildCount();
1774 if( nmax
> MAX_ATTACHABLE_CHILDREN
)
1775 nmax
= MAX_ATTACHABLE_CHILDREN
;
1777 for( sal_Int64 n
= 0; n
< nmax
; n
++ )
1779 uno::Reference
< accessibility::XAccessible
> xChild( xContext
->getAccessibleChild( n
) );
1782 attachRecursive(xChild
);
1787 // Usually, when the document is loaded, a CARET_CHANGED accessibility event is automatically emitted
1788 // for the first paragraph. That allows to notify the paragraph content to the client, even if no input
1789 // event occurred yet. However, when switching to a11y enabled in the client and in Cypress tests
1790 // no accessibility event is automatically emitted until some input event occurs.
1791 // So we use the following workaround to notify the content of the focused paragraph,
1792 // without waiting for an input event.
1793 // Here we update the paragraph info related to the focused paragraph,
1794 // later when afterCallbackRegistered is executed we notify the paragraph content.
1795 sal_Int64 nChildCount
= xContext
->getAccessibleChildCount();
1796 if (nChildCount
> 0 && nChildCount
< 10)
1798 for (sal_Int64 n
= 0; n
< nChildCount
; ++n
)
1800 uno::Reference
< accessibility::XAccessible
> xChild(xContext
->getAccessibleChild(n
));
1803 uno::Reference
<css::accessibility::XAccessibleText
> xAccText(xChild
, uno::UNO_QUERY
);
1806 sal_Int32 nPos
= xAccText
->getCaretPosition();
1809 attachRecursive(xChild
);
1810 updateParagraphInfo(xAccText
, false, "LOKDocumentFocusListener::attachRecursive(3)");
1821 void LOKDocumentFocusListener::detachRecursive(
1822 const uno::Reference
< accessibility::XAccessible
>& xAccessible
,
1826 uno::Reference
< accessibility::XAccessibleContext
> xContext
=
1827 xAccessible
->getAccessibleContext();
1830 detachRecursive(xContext
, bForce
);
1833 void LOKDocumentFocusListener::detachRecursive(
1834 const uno::Reference
< accessibility::XAccessibleContext
>& xContext
,
1838 aboutView("LOKDocumentFocusListener::detachRecursive (2)", this, m_pViewShell
);
1839 sal_Int64 nStateSet
= xContext
->getAccessibleStateSet();
1841 SAL_INFO("lok.a11y", "LOKDocumentFocusListener::detachRecursive(2): this: " << this
1842 << ", name: " << xContext
->getAccessibleName()
1843 << ", parent: " << xContext
->getAccessibleParent().get()
1844 << ", child count: " << xContext
->getAccessibleChildCount());
1846 if (m_bIsEditingCell
)
1848 ::rtl::OUString sName
= xContext
->getAccessibleName();
1849 m_bIsEditingCell
= !sName
.startsWith("Cell");
1850 if (!m_bIsEditingCell
)
1852 m_sFocusedParagraph
= "";
1853 m_nCaretPosition
= 0;
1854 notifyFocusedParagraphChanged();
1858 detachRecursive(xContext
, nStateSet
, bForce
);
1861 void LOKDocumentFocusListener::detachRecursive(
1862 const uno::Reference
< accessibility::XAccessibleContext
>& xContext
,
1863 const sal_Int64 nStateSet
,
1867 uno::Reference
< accessibility::XAccessibleEventBroadcaster
> xBroadcaster(xContext
, uno::UNO_QUERY
);
1869 if (xBroadcaster
.is() && 0 < m_aRefList
.erase(xBroadcaster
))
1871 xBroadcaster
->removeAccessibleEventListener(static_cast< accessibility::XAccessibleEventListener
*>(this));
1873 if ((nStateSet
& accessibility::AccessibleStateType::SELECTED
) &&
1874 selectionHasToBeNotified(xContext
))
1876 uno::Reference
< accessibility::XAccessible
> xAccObj(xContext
, uno::UNO_QUERY
);
1877 onShapeSelectionChanged(xAccObj
, "delete");
1880 if (bForce
|| !(nStateSet
& accessibility::AccessibleStateType::MANAGES_DESCENDANTS
))
1882 sal_Int64 nmax
= xContext
->getAccessibleChildCount();
1883 if (nmax
> MAX_ATTACHABLE_CHILDREN
)
1884 nmax
= MAX_ATTACHABLE_CHILDREN
;
1885 for (sal_Int64 n
= 0; n
< nmax
; n
++)
1887 uno::Reference
< accessibility::XAccessible
> xChild(xContext
->getAccessibleChild(n
));
1890 detachRecursive(xChild
);
1896 sal_uInt32
SfxViewShell_Impl::m_nLastViewShellId
= 0;
1898 SfxViewShell_Impl::SfxViewShell_Impl(SfxViewShellFlags
const nFlags
, ViewShellDocId nDocId
)
1899 : m_bHasPrintOptions(nFlags
& SfxViewShellFlags::HAS_PRINTOPTIONS
)
1900 , m_nFamily(0xFFFF) // undefined, default set by TemplateDialog
1901 , m_pLibreOfficeKitViewCallback(nullptr)
1902 , m_bTiledSearching(false)
1903 , m_nViewShellId(SfxViewShell_Impl::m_nLastViewShellId
++)
1908 SfxViewShell_Impl::~SfxViewShell_Impl()
1912 std::vector
< SfxInPlaceClient
* >& SfxViewShell_Impl::GetIPClients_Impl()
1917 SFX_IMPL_SUPERCLASS_INTERFACE(SfxViewShell
,SfxShell
)
1919 void SfxViewShell::InitInterface_Impl()
1924 /** search for a filter name dependent on type and module
1926 static OUString
impl_retrieveFilterNameFromTypeAndModule(
1927 const css::uno::Reference
< css::container::XContainerQuery
>& rContainerQuery
,
1928 const OUString
& rType
,
1929 const OUString
& rModuleIdentifier
,
1930 const sal_Int32 nFlags
)
1932 // Retrieve filter from type
1933 css::uno::Sequence
< css::beans::NamedValue
> aQuery
{
1934 { "Type", css::uno::Any( rType
) },
1935 { "DocumentService", css::uno::Any( rModuleIdentifier
) }
1938 css::uno::Reference
< css::container::XEnumeration
> xEnumeration
=
1939 rContainerQuery
->createSubSetEnumerationByProperties( aQuery
);
1941 OUString aFoundFilterName
;
1942 while ( xEnumeration
->hasMoreElements() )
1944 ::comphelper::SequenceAsHashMap
aFilterPropsHM( xEnumeration
->nextElement() );
1945 OUString aFilterName
= aFilterPropsHM
.getUnpackedValueOrDefault(
1949 sal_Int32 nFilterFlags
= aFilterPropsHM
.getUnpackedValueOrDefault(
1953 if ( nFilterFlags
& nFlags
)
1955 aFoundFilterName
= aFilterName
;
1960 return aFoundFilterName
;
1965 /** search for an internal typename, which map to the current app module
1966 and map also to a "family" of file formats as e.g. PDF/MS Doc/OOo Doc.
1976 static OUString
impl_searchFormatTypeForApp(const css::uno::Reference
< css::frame::XFrame
>& xFrame
,
1977 ETypeFamily eTypeFamily
)
1981 css::uno::Reference
< css::uno::XComponentContext
> xContext (::comphelper::getProcessComponentContext());
1982 css::uno::Reference
< css::frame::XModuleManager2
> xModuleManager(css::frame::ModuleManager::create(xContext
));
1984 OUString sModule
= xModuleManager
->identify(xFrame
);
1991 if ( sModule
== "com.sun.star.text.TextDocument" )
1992 sType
= "writer_MS_Word_2007";
1994 if ( sModule
== "com.sun.star.sheet.SpreadsheetDocument" )
1995 sType
= "MS Excel 2007 XML";
1997 if ( sModule
== "com.sun.star.presentation.PresentationDocument" )
1998 sType
= "MS PowerPoint 2007 XML";
2004 if ( sModule
== "com.sun.star.text.TextDocument" )
2007 if ( sModule
== "com.sun.star.sheet.SpreadsheetDocument" )
2010 if ( sModule
== "com.sun.star.drawing.DrawingDocument" )
2013 if ( sModule
== "com.sun.star.presentation.PresentationDocument" )
2021 catch (const css::uno::RuntimeException
&)
2025 catch (const css::uno::Exception
&)
2032 void SfxViewShell::NewIPClient_Impl( SfxInPlaceClient
*pIPClient
)
2034 pImpl
->GetIPClients_Impl().push_back(pIPClient
);
2037 void SfxViewShell::IPClientGone_Impl( SfxInPlaceClient
const *pIPClient
)
2039 std::vector
< SfxInPlaceClient
* >& pClients
= pImpl
->GetIPClients_Impl();
2041 auto it
= std::find(pClients
.begin(), pClients
.end(), pIPClient
);
2042 if (it
!= pClients
.end())
2043 pClients
.erase( it
);
2047 void SfxViewShell::ExecMisc_Impl( SfxRequest
&rReq
)
2049 const sal_uInt16 nId
= rReq
.GetSlot();
2052 case SID_STYLE_FAMILY
:
2054 const SfxUInt16Item
* pItem
= rReq
.GetArg
<SfxUInt16Item
>(nId
);
2057 pImpl
->m_nFamily
= pItem
->GetValue();
2061 case SID_ACTIVATE_STYLE_APPLY
:
2063 uno::Reference
< frame::XFrame
> xFrame
=
2064 GetViewFrame().GetFrame().GetFrameInterface();
2066 Reference
< beans::XPropertySet
> xPropSet( xFrame
, UNO_QUERY
);
2067 Reference
< frame::XLayoutManager
> xLayoutManager
;
2068 if ( xPropSet
.is() )
2072 Any aValue
= xPropSet
->getPropertyValue("LayoutManager");
2073 aValue
>>= xLayoutManager
;
2074 if ( xLayoutManager
.is() )
2076 uno::Reference
< ui::XUIElement
> xElement
= xLayoutManager
->getElement( "private:resource/toolbar/textobjectbar" );
2079 xElement
= xLayoutManager
->getElement( "private:resource/toolbar/frameobjectbar" );
2083 xElement
= xLayoutManager
->getElement( "private:resource/toolbar/oleobjectbar" );
2087 uno::Reference
< awt::XWindow
> xWin( xElement
->getRealInterface(), uno::UNO_QUERY_THROW
);
2088 VclPtr
<vcl::Window
> pWin
= VCLUnoHelper::GetWindow( xWin
);
2089 ToolBox
* pTextToolbox
= dynamic_cast< ToolBox
* >( pWin
.get() );
2092 ToolBox::ImplToolItems::size_type nItemCount
= pTextToolbox
->GetItemCount();
2093 for( ToolBox::ImplToolItems::size_type nItem
= 0; nItem
< nItemCount
; ++nItem
)
2095 ToolBoxItemId nItemId
= pTextToolbox
->GetItemId( nItem
);
2096 const OUString
& rCommand
= pTextToolbox
->GetItemCommand( nItemId
);
2097 if (rCommand
== ".uno:StyleApply")
2099 vcl::Window
* pItemWin
= pTextToolbox
->GetItemWindow( nItemId
);
2101 pItemWin
->GrabFocus();
2109 catch (const Exception
&)
2117 case SID_MAIL_SENDDOCASMS
:
2118 case SID_MAIL_SENDDOCASOOO
:
2119 case SID_MAIL_SENDDOCASPDF
:
2120 case SID_MAIL_SENDDOC
:
2121 case SID_MAIL_SENDDOCASFORMAT
:
2123 SfxObjectShell
* pDoc
= GetObjectShell();
2126 pDoc
->QueryHiddenInformation(HiddenWarningFact::WhenSaving
);
2127 SfxMailModel aModel
;
2130 const SfxStringItem
* pMailRecipient
= rReq
.GetArg
<SfxStringItem
>(SID_MAIL_RECIPIENT
);
2131 if ( pMailRecipient
)
2133 OUString
aRecipient( pMailRecipient
->GetValue() );
2134 OUString
aMailToStr("mailto:");
2136 if ( aRecipient
.startsWith( aMailToStr
) )
2137 aRecipient
= aRecipient
.copy( aMailToStr
.getLength() );
2138 aModel
.AddToAddress( aRecipient
);
2140 const SfxStringItem
* pMailDocType
= rReq
.GetArg
<SfxStringItem
>(SID_TYPE_NAME
);
2142 aDocType
= pMailDocType
->GetValue();
2144 uno::Reference
< frame::XFrame
> xFrame( rFrame
.GetFrame().GetFrameInterface() );
2145 SfxMailModel::SendMailResult eResult
= SfxMailModel::SEND_MAIL_ERROR
;
2147 if ( nId
== SID_MAIL_SENDDOC
)
2148 eResult
= aModel
.SaveAndSend( xFrame
, OUString() );
2149 else if ( nId
== SID_MAIL_SENDDOCASPDF
)
2150 eResult
= aModel
.SaveAndSend( xFrame
, "pdf_Portable_Document_Format");
2151 else if ( nId
== SID_MAIL_SENDDOCASMS
)
2153 aDocType
= impl_searchFormatTypeForApp(xFrame
, E_MS_DOC
);
2154 if (!aDocType
.isEmpty())
2155 eResult
= aModel
.SaveAndSend( xFrame
, aDocType
);
2157 else if ( nId
== SID_MAIL_SENDDOCASOOO
)
2159 aDocType
= impl_searchFormatTypeForApp(xFrame
, E_OOO_DOC
);
2160 if (!aDocType
.isEmpty())
2161 eResult
= aModel
.SaveAndSend( xFrame
, aDocType
);
2164 if ( eResult
== SfxMailModel::SEND_MAIL_ERROR
)
2166 weld::Window
* pWin
= SfxGetpApp()->GetTopWindow();
2167 std::unique_ptr
<weld::MessageDialog
> xBox(Application::CreateMessageDialog(pWin
,
2168 VclMessageType::Info
, VclButtonsType::Ok
,
2169 SfxResId(STR_ERROR_SEND_MAIL
)));
2178 case SID_BLUETOOTH_SENDDOC
:
2180 SfxBluetoothModel aModel
;
2181 SfxObjectShell
* pDoc
= GetObjectShell();
2184 pDoc
->QueryHiddenInformation(HiddenWarningFact::WhenSaving
);
2185 uno::Reference
< frame::XFrame
> xFrame( rFrame
.GetFrame().GetFrameInterface() );
2186 SfxMailModel::SendMailResult eResult
= aModel
.SaveAndSend( xFrame
);
2187 if( eResult
== SfxMailModel::SEND_MAIL_ERROR
)
2189 weld::Window
* pWin
= SfxGetpApp()->GetTopWindow();
2190 std::unique_ptr
<weld::MessageDialog
> xBox(Application::CreateMessageDialog(pWin
,
2191 VclMessageType::Info
, VclButtonsType::Ok
,
2192 SfxResId(STR_ERROR_SEND_MAIL
)));
2201 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2204 css::uno::Reference
< lang::XMultiServiceFactory
> xSMGR(::comphelper::getProcessServiceFactory(), css::uno::UNO_SET_THROW
);
2205 css::uno::Reference
< uno::XComponentContext
> xContext(::comphelper::getProcessComponentContext(), css::uno::UNO_SET_THROW
);
2206 css::uno::Reference
< css::frame::XFrame
> xFrame( rFrame
.GetFrame().GetFrameInterface() );
2207 css::uno::Reference
< css::frame::XModel
> xModel
;
2209 css::uno::Reference
< css::frame::XModuleManager2
> xModuleManager( css::frame::ModuleManager::create(xContext
) );
2214 aModule
= xModuleManager
->identify( xFrame
);
2216 catch (const css::uno::RuntimeException
&)
2220 catch (const css::uno::Exception
&)
2226 css::uno::Reference
< css::frame::XController
> xController
= xFrame
->getController();
2227 if ( xController
.is() )
2228 xModel
= xController
->getModel();
2231 // We need at least a valid module name and model reference
2232 css::uno::Reference
< css::frame::XStorable
> xStorable( xModel
, css::uno::UNO_QUERY
);
2233 if ( xModel
.is() && xStorable
.is() )
2235 OUString aFilterName
;
2236 OUString
aTypeName( "generic_HTML" );
2239 OUString aLocation
= xStorable
->getLocation();
2240 INetURLObject
aFileObj( aLocation
);
2242 bool bPrivateProtocol
= ( aFileObj
.GetProtocol() == INetProtocol::PrivSoffice
);
2243 bool bHasLocation
= !aLocation
.isEmpty() && !bPrivateProtocol
;
2245 css::uno::Reference
< css::container::XContainerQuery
> xContainerQuery(
2246 xSMGR
->createInstance( "com.sun.star.document.FilterFactory" ),
2247 css::uno::UNO_QUERY_THROW
);
2249 // Retrieve filter from type
2251 sal_Int32 nFilterFlags
= 0x00000002; // export
2252 aFilterName
= impl_retrieveFilterNameFromTypeAndModule( xContainerQuery
, aTypeName
, aModule
, nFilterFlags
);
2253 if ( aFilterName
.isEmpty() )
2255 // Draw/Impress uses a different type. 2nd chance try to use alternative type name
2256 aFilterName
= impl_retrieveFilterNameFromTypeAndModule(
2257 xContainerQuery
, "graphic_HTML", aModule
, nFilterFlags
);
2260 // No filter found => error
2261 // No type and no location => error
2262 if ( aFilterName
.isEmpty() || aTypeName
.isEmpty())
2268 // Use provided save file name. If empty determine file name
2269 if ( !bHasLocation
)
2271 // Create a default file name with the correct extension
2272 aFileName
= "webpreview";
2276 // Determine file name from model
2277 INetURLObject
aFObj( xStorable
->getLocation() );
2278 aFileName
= aFObj
.getName( INetURLObject::LAST_SEGMENT
, true, INetURLObject::DecodeMechanism::NONE
);
2281 OSL_ASSERT( !aFilterName
.isEmpty() );
2282 OSL_ASSERT( !aFileName
.isEmpty() );
2284 // Creates a temporary directory to store our predefined file into it (for the
2285 // flatpak case, create it in XDG_CACHE_HOME instead of /tmp for technical reasons,
2286 // so that it can be accessed by the browser running outside the sandbox):
2287 OUString
* parent
= nullptr;
2288 if (flatpak::isFlatpak() && !flatpak::createTemporaryHtmlDirectory(&parent
))
2290 SAL_WARN("sfx.view", "cannot create Flatpak html temp dir");
2293 INetURLObject
aFilePathObj( ::utl::CreateTempURL(parent
, true) );
2294 aFilePathObj
.insertName( aFileName
);
2295 aFilePathObj
.setExtension( u
"htm" );
2297 OUString aFileURL
= aFilePathObj
.GetMainURL( INetURLObject::DecodeMechanism::NONE
);
2299 css::uno::Sequence
< css::beans::PropertyValue
> aArgs
{
2300 comphelper::makePropertyValue("FilterName", aFilterName
)
2303 // Store document in the html format
2306 xStorable
->storeToURL( aFileURL
, aArgs
);
2308 catch (const io::IOException
&)
2314 sfx2::openUriExternally(aFileURL
, true, rReq
.GetFrameWeld());
2328 void SfxViewShell::GetState_Impl( SfxItemSet
&rSet
)
2331 SfxWhichIter
aIter( rSet
);
2332 SfxObjectShell
*pSh
= GetViewFrame().GetObjectShell();
2333 for ( sal_uInt16 nSID
= aIter
.FirstWhich(); nSID
; nSID
= aIter
.NextWhich() )
2338 case SID_BLUETOOTH_SENDDOC
:
2339 case SID_MAIL_SENDDOC
:
2340 case SID_MAIL_SENDDOCASFORMAT
:
2341 case SID_MAIL_SENDDOCASMS
:
2342 case SID_MAIL_SENDDOCASOOO
:
2343 case SID_MAIL_SENDDOCASPDF
:
2345 #if HAVE_FEATURE_MACOSX_SANDBOX
2346 rSet
.DisableItem(nSID
);
2348 if (pSh
&& pSh
->isExportLocked())
2349 rSet
.DisableItem(nSID
);
2354 if (pSh
&& pSh
->isExportLocked())
2355 rSet
.DisableItem(nSID
);
2358 // Printer functions
2360 case SID_PRINTDOCDIRECT
:
2361 case SID_SETUPPRINTER
:
2362 case SID_PRINTER_NAME
:
2364 if (Application::GetSettings().GetMiscSettings().GetDisablePrinting()
2365 || (pSh
&& pSh
->isPrintLocked()))
2367 rSet
.DisableItem(nSID
);
2371 SfxPrinter
*pPrinter
= GetPrinter();
2373 if ( SID_PRINTDOCDIRECT
== nSID
)
2375 OUString aPrinterName
;
2376 if ( pPrinter
!= nullptr )
2377 aPrinterName
= pPrinter
->GetName();
2380 // tdf#109149 don't poll the Default Printer Name on every query.
2381 // We are queried on every change, so on every
2382 // keystroke, and we are only using this to fill in the
2383 // printername inside the label of "Print Directly (printer-name)"
2384 // On Printer::GetDefaultPrinterName() is implemented with
2385 // GetDefaultPrinter so don't call this excessively. 5 mins
2386 // seems a reasonable refresh time.
2387 std::chrono::steady_clock::time_point now
= std::chrono::steady_clock::now();
2388 std::chrono::minutes
five_mins(5);
2389 if (now
> pImpl
->m_nDefaultPrinterNameFetchTime
+ five_mins
)
2391 pImpl
->m_sDefaultPrinterName
= Printer::GetDefaultPrinterName();
2392 pImpl
->m_nDefaultPrinterNameFetchTime
= now
;
2394 aPrinterName
= pImpl
->m_sDefaultPrinterName
;
2396 if ( !aPrinterName
.isEmpty() )
2398 uno::Reference
< frame::XFrame
> xFrame( rFrame
.GetFrame().GetFrameInterface() );
2400 auto aProperties
= vcl::CommandInfoProvider::GetCommandProperties(".uno:PrintDefault",
2401 vcl::CommandInfoProvider::GetModuleIdentifier(xFrame
));
2402 OUString val
= vcl::CommandInfoProvider::GetLabelForCommand(aProperties
) +
2403 " (" + aPrinterName
+ ")";
2405 rSet
.Put( SfxStringItem( SID_PRINTDOCDIRECT
, val
) );
2410 case SID_STYLE_FAMILY
:
2412 rSet
.Put( SfxUInt16Item( SID_STYLE_FAMILY
, pImpl
->m_nFamily
) );
2419 void SfxViewShell::SetZoomFactor( const Fraction
&rZoomX
,
2420 const Fraction
&rZoomY
)
2422 DBG_ASSERT( GetWindow(), "no window" );
2423 MapMode
aMap( GetWindow()->GetMapMode() );
2424 aMap
.SetScaleX( rZoomX
);
2425 aMap
.SetScaleY( rZoomY
);
2426 GetWindow()->SetMapMode( aMap
);
2429 ErrCode
SfxViewShell::DoVerb(sal_Int32
/*nVerb*/)
2433 Virtual Method used to perform a Verb on a selected Object.
2434 Since this Object is only known by the derived classes, they must override
2439 return ERRCODE_SO_NOVERBS
;
2442 void SfxViewShell::OutplaceActivated( bool bActive
)
2445 GetFrame()->GetFrame().Appear();
2448 void SfxViewShell::UIActivating( SfxInPlaceClient
* /*pClient*/ )
2450 uno::Reference
< frame::XFrame
> xOwnFrame( rFrame
.GetFrame().GetFrameInterface() );
2451 uno::Reference
< frame::XFramesSupplier
> xParentFrame
= xOwnFrame
->getCreator();
2452 if ( xParentFrame
.is() )
2453 xParentFrame
->setActiveFrame( xOwnFrame
);
2455 rFrame
.GetBindings().HidePopups();
2456 rFrame
.GetDispatcher()->Update_Impl( true );
2459 void SfxViewShell::UIDeactivated( SfxInPlaceClient
* /*pClient*/ )
2461 if ( !rFrame
.GetFrame().IsClosing_Impl() || SfxViewFrame::Current() != &rFrame
)
2462 rFrame
.GetDispatcher()->Update_Impl( true );
2463 rFrame
.GetBindings().HidePopups(false);
2465 rFrame
.GetBindings().InvalidateAll(true);
2468 SfxInPlaceClient
* SfxViewShell::FindIPClient
2470 const uno::Reference
< embed::XEmbeddedObject
>& xObj
,
2471 vcl::Window
* pObjParentWin
2474 std::vector
< SfxInPlaceClient
* >& rClients
= pImpl
->GetIPClients_Impl();
2475 if ( rClients
.empty() )
2478 if( !pObjParentWin
)
2479 pObjParentWin
= GetWindow();
2480 for (SfxInPlaceClient
* pIPClient
: rClients
)
2482 if ( pIPClient
->GetObject() == xObj
&& pIPClient
->GetEditWin() == pObjParentWin
)
2490 SfxInPlaceClient
* SfxViewShell::GetIPClient() const
2492 return GetUIActiveClient();
2496 SfxInPlaceClient
* SfxViewShell::GetUIActiveIPClient_Impl() const
2498 // this method is needed as long as SFX still manages the border space for ChildWindows (see SfxFrame::Resize)
2499 std::vector
< SfxInPlaceClient
* >& rClients
= pImpl
->GetIPClients_Impl();
2500 if ( rClients
.empty() )
2503 for (SfxInPlaceClient
* pIPClient
: rClients
)
2505 if ( pIPClient
->IsUIActive() )
2512 SfxInPlaceClient
* SfxViewShell::GetUIActiveClient() const
2514 std::vector
< SfxInPlaceClient
* >& rClients
= pImpl
->GetIPClients_Impl();
2515 if ( rClients
.empty() )
2518 const bool bIsTiledRendering
= comphelper::LibreOfficeKit::isActive();
2520 for (SfxInPlaceClient
* pIPClient
: rClients
)
2522 if ( pIPClient
->IsObjectUIActive() || ( bIsTiledRendering
&& pIPClient
->IsObjectInPlaceActive() ) )
2530 void SfxViewShell::Activate( bool bMDI
)
2534 SfxObjectShell
*pSh
= GetViewFrame().GetObjectShell();
2535 if (const auto xModel
= pSh
->GetModel())
2536 xModel
->setCurrentController(GetController());
2538 SetCurrentDocument();
2543 void SfxViewShell::Deactivate(bool /*bMDI*/)
2548 void SfxViewShell::Move()
2552 This virtual Method is called when the window displayed in the
2553 SfxViewShell gets a StarView-Move() notification.
2555 This base implementation does not have to be called. .
2559 This Method can be used to cancel a selection, in order to catch the
2560 mouse movement which is due to moving a window.
2562 For now the notification does not work In-Place.
2569 void SfxViewShell::OuterResizePixel
2571 const Point
& /*rToolOffset*/,// Upper left corner Tools in Frame-Window
2572 const Size
& /*rSize*/ // All available sizes.
2577 Override this Method to be able to react to the size-change of
2578 the View. Thus the View is defined as the Edit window and also the
2579 attached Tools are defined (for example the ruler).
2581 The Edit window must not be changed either in size or position.
2583 The Vis-Area of SfxObjectShell, its scale and position can be changed
2584 here. The main use is to change the size of the Vis-Area.
2586 If the Border is changed due to the new calculation then this has to be set
2587 by <SfxViewShell::SetBorderPixel(const SvBorder&)>. The Positioning of Tools
2588 is only allowed after the calling of 'SetBorderPixel'.
2592 void AppViewSh::OuterViewResizePixel( const Point &rOfs, const Size &rSz )
2594 // Calculate Tool position and size externally, do not set!
2595 // (due to the following Border calculation)
2596 Point aHLinPos...; Size aHLinSz...;
2599 // Calculate and Set a Border of Tools which matches rSize.
2601 SetBorderPixel( aBorder ); // Allow Positioning from here on.
2604 pHLin->SetPosSizePixel( aHLinPos, aHLinSz );
2610 <SfxViewShell::InnerResizePixel(const Point&,const Size& rSize)>
2614 SetBorderPixel( SvBorder() );
2618 void SfxViewShell::InnerResizePixel
2620 const Point
& /*rToolOffset*/,// Upper left corner Tools in Frame-Window
2621 const Size
& /*rSize*/, // All available sizes.
2627 Override this Method to be able to react to the size-change of
2630 The Edit window must not be changed either in size or position.
2631 Neither the Vis-Area of SfxObjectShell nor its scale or position are
2632 allowed to be changed
2634 If the Border is changed due to the new calculation then is has to be set
2635 by <SfxViewShell::SetBorderPixel(const SvBorder&)>.
2636 The Positioning of Tools is only allowed after the calling of
2642 void AppViewSh::InnerViewResizePixel( const Point &rOfs, const Size &rSz )
2644 // Calculate Tool position and size internally, do not set!
2645 // (due to the following Border calculation)
2646 Point aHLinPos...; Size aHLinSz...;
2649 // Calculate and Set a Border of Tools which matches rSize.
2651 SetBorderPixel( aBorder ); // Allow Positioning from here on.
2654 pHLin->SetPosSizePixel( aHLinPos, aHLinSz );
2660 <SfxViewShell::OuterResizePixel(const Point&,const Size& rSize)>
2664 SetBorderPixel( SvBorder() );
2667 void SfxViewShell::InvalidateBorder()
2669 GetViewFrame().InvalidateBorderImpl( this );
2670 if (pImpl
->m_pController
.is())
2672 pImpl
->m_pController
->BorderWidthsChanged_Impl();
2676 void SfxViewShell::SetBorderPixel( const SvBorder
&rBorder
)
2678 GetViewFrame().SetBorderPixelImpl( this, rBorder
);
2680 // notify related controller that border size is changed
2681 if (pImpl
->m_pController
.is())
2683 pImpl
->m_pController
->BorderWidthsChanged_Impl();
2687 const SvBorder
& SfxViewShell::GetBorderPixel() const
2689 return GetViewFrame().GetBorderPixelImpl();
2692 void SfxViewShell::SetWindow
2694 vcl::Window
* pViewPort
// For example Null pointer in the Destructor.
2699 With this method the SfxViewShell is set in the data window. This is
2700 needed for the in-place container and for restoring the proper focus.
2702 Even in-place-active the conversion of the ViewPort Windows is forbidden.
2706 if( pWindow
== pViewPort
)
2709 // Disconnect existing IP-Clients if possible
2710 DisconnectAllClients();
2713 bool bHadFocus
= pWindow
&& pWindow
->HasChildPathFocus( true );
2714 pWindow
= pViewPort
;
2718 // Disable automatic GUI mirroring (right-to-left) for document windows
2719 pWindow
->EnableRTL( false );
2722 if ( bHadFocus
&& pWindow
)
2723 pWindow
->GrabFocus();
2725 //Do we still need this Method?!
2726 //SfxGetpApp()->GrabFocus( pWindow );
2729 ViewShellDocId
SfxViewShell::mnCurrentDocId(0);
2731 SfxViewShell::SfxViewShell
2733 SfxViewFrame
& rViewFrame
, /* <SfxViewFrame>, which will be
2734 displayed in this View */
2735 SfxViewShellFlags nFlags
/* See <SfxViewShell-Flags> */
2739 , pImpl( new SfxViewShell_Impl(nFlags
, SfxViewShell::mnCurrentDocId
) )
2740 , rFrame(rViewFrame
)
2742 , bNoNewWindow( nFlags
& SfxViewShellFlags::NO_NEWWINDOW
)
2743 , mbPrinterSettingsModified(false)
2744 , maLOKLanguageTag(LANGUAGE_NONE
)
2745 , maLOKLocale(LANGUAGE_NONE
)
2746 , maLOKDeviceFormFactor(LOKDeviceFormFactor::UNKNOWN
)
2747 , mbLOKAccessibilityEnabled(false)
2749 SetMargin( rViewFrame
.GetMargin_Impl() );
2751 SetPool( &rViewFrame
.GetObjectShell()->GetPool() );
2752 StartListening(*rViewFrame
.GetObjectShell());
2755 std::vector
<SfxViewShell
*> &rViewArr
= SfxGetpApp()->GetViewShells_Impl();
2756 rViewArr
.push_back(this);
2758 if (comphelper::LibreOfficeKit::isActive())
2760 maLOKLanguageTag
= SfxLokHelper::getDefaultLanguage();
2761 maLOKLocale
= SfxLokHelper::getDefaultLanguage();
2763 const auto [isTimezoneSet
, aTimezone
] = SfxLokHelper::getDefaultTimezone();
2764 maLOKIsTimezoneSet
= isTimezoneSet
;
2765 maLOKTimezone
= aTimezone
;
2767 maLOKDeviceFormFactor
= SfxLokHelper::getDeviceFormFactor();
2769 vcl::Window
* pFrameWin
= rViewFrame
.GetWindow().GetFrameWindow();
2770 if (pFrameWin
&& !pFrameWin
->GetLOKNotifier())
2771 pFrameWin
->SetLOKNotifier(this, true);
2775 SfxViewShell::~SfxViewShell()
2778 const SfxViewShell
*pThis
= this;
2779 std::vector
<SfxViewShell
*> &rViewArr
= SfxGetpApp()->GetViewShells_Impl();
2780 auto it
= std::find( rViewArr
.begin(), rViewArr
.end(), pThis
);
2781 rViewArr
.erase( it
);
2783 if ( pImpl
->xClipboardListener
.is() )
2785 pImpl
->xClipboardListener
->DisconnectViewShell();
2786 pImpl
->xClipboardListener
= nullptr;
2789 if (pImpl
->m_pController
.is())
2791 pImpl
->m_pController
->ReleaseShell_Impl();
2792 pImpl
->m_pController
.clear();
2795 vcl::Window
* pFrameWin
= GetViewFrame().GetWindow().GetFrameWindow();
2796 if (pFrameWin
&& pFrameWin
->GetLOKNotifier() == this)
2797 pFrameWin
->ReleaseLOKNotifier();
2800 OUString
SfxViewShell::getA11yFocusedParagraph() const
2802 const LOKDocumentFocusListener
& rDocFocusListener
= GetLOKDocumentFocusListener();
2803 return rDocFocusListener
.getFocusedParagraph();
2806 int SfxViewShell::getA11yCaretPosition() const
2808 const LOKDocumentFocusListener
& rDocFocusListener
= GetLOKDocumentFocusListener();
2809 return rDocFocusListener
.getCaretPosition();
2812 bool SfxViewShell::PrepareClose
2814 bool bUI
// TRUE: Allow Dialog and so on, FALSE: silent-mode
2817 if (GetViewFrame().GetWindow().GetLOKNotifier() == this)
2818 GetViewFrame().GetWindow().ReleaseLOKNotifier();
2820 SfxPrinter
*pPrinter
= GetPrinter();
2821 if ( pPrinter
&& pPrinter
->IsPrinting() )
2825 std::unique_ptr
<weld::MessageDialog
> xBox(Application::CreateMessageDialog(GetViewFrame().GetFrameWeld(),
2826 VclMessageType::Info
, VclButtonsType::Ok
,
2827 SfxResId(STR_CANT_CLOSE
)));
2834 if( GetViewFrame().IsInModalMode() )
2837 if( bUI
&& GetViewFrame().GetDispatcher()->IsLocked() )
2844 SfxViewShell
* SfxViewShell::Current()
2846 SfxViewFrame
*pCurrent
= SfxViewFrame::Current();
2847 return pCurrent
? pCurrent
->GetViewShell() : nullptr;
2850 bool SfxViewShell::IsCurrentLokViewReadOnly()
2852 if (!comphelper::LibreOfficeKit::isActive() || Current() == nullptr || !Current()->IsLokReadOnlyView())
2858 SfxViewShell
* SfxViewShell::Get( const Reference
< XController
>& i_rController
)
2860 if ( !i_rController
.is() )
2863 for ( SfxViewShell
* pViewShell
= SfxViewShell::GetFirst( false );
2865 pViewShell
= SfxViewShell::GetNext( *pViewShell
, false )
2868 if ( pViewShell
->GetController() == i_rController
)
2875 SdrView
* SfxViewShell::GetDrawView() const
2879 This virtual Method has to be overloaded by the sub classes, to be able
2880 make the Property-Editor available.
2882 The default implementation does always return zero.
2890 OUString
SfxViewShell::GetSelectionText
2892 bool /*bCompleteWords*/, /* FALSE (default)
2893 Only the actual selected text is returned.
2896 The selected text is expanded so that only
2897 whole words are returned. As word separators
2898 these are used: white spaces and punctuation
2899 ".,;" and single and double quotes.
2901 bool /*bOnlyASample*/ /* used by some dialogs to avoid constructing monster strings e.g. in calc */
2906 Override this Method to return a text that
2907 is included in the current selection. This is for example used when
2910 When called with "CompleteWords == TRUE", it is for example sufficient
2911 with having the Cursor positioned somewhere within a URL in-order
2912 to have the entire URL returned.
2920 bool SfxViewShell::HasSelection( bool ) const
2924 With this virtual Method can a for example a Dialog be queried, to
2925 check if something is selected in the current view. If the Parameter
2926 is <BOOL> TRUE then it is checked whether some text is selected.
2933 void SfxViewShell::AddSubShell( SfxShell
& rShell
)
2935 pImpl
->aArr
.push_back(&rShell
);
2936 SfxDispatcher
*pDisp
= rFrame
.GetDispatcher();
2937 if ( pDisp
->IsActive(*this) )
2939 pDisp
->Push(rShell
);
2944 void SfxViewShell::RemoveSubShell( SfxShell
* pShell
)
2946 SfxDispatcher
*pDisp
= rFrame
.GetDispatcher();
2949 size_t nCount
= pImpl
->aArr
.size();
2950 if ( pDisp
->IsActive(*this) )
2952 for(size_t n
= nCount
; n
> 0; --n
)
2953 pDisp
->Pop(*pImpl
->aArr
[n
- 1]);
2956 pImpl
->aArr
.clear();
2960 SfxShellArr_Impl::iterator i
= std::find(pImpl
->aArr
.begin(), pImpl
->aArr
.end(), pShell
);
2961 if(i
!= pImpl
->aArr
.end())
2963 pImpl
->aArr
.erase(i
);
2964 if(pDisp
->IsActive(*this))
2966 pDisp
->RemoveShell_Impl(*pShell
);
2973 SfxShell
* SfxViewShell::GetSubShell( sal_uInt16 nNo
)
2975 sal_uInt16 nCount
= pImpl
->aArr
.size();
2977 return pImpl
->aArr
[nCount
- nNo
- 1];
2981 void SfxViewShell::PushSubShells_Impl( bool bPush
)
2983 SfxDispatcher
*pDisp
= rFrame
.GetDispatcher();
2986 for (auto const& elem
: pImpl
->aArr
)
2989 else if(!pImpl
->aArr
.empty())
2991 SfxShell
& rPopUntil
= *pImpl
->aArr
[0];
2992 if ( pDisp
->GetShellLevel( rPopUntil
) != USHRT_MAX
)
2993 pDisp
->Pop( rPopUntil
, SfxDispatcherPopFlags::POP_UNTIL
);
3000 void SfxViewShell::WriteUserData( OUString
&, bool )
3005 void SfxViewShell::ReadUserData(const OUString
&, bool )
3009 void SfxViewShell::ReadUserDataSequence ( const uno::Sequence
< beans::PropertyValue
>& )
3013 void SfxViewShell::WriteUserDataSequence ( uno::Sequence
< beans::PropertyValue
>& )
3018 // returns the first shell of spec. type viewing the specified doc.
3019 SfxViewShell
* SfxViewShell::GetFirst
3022 const std::function
< bool ( const SfxViewShell
* ) >& isViewShell
3025 // search for a SfxViewShell of the specified type
3026 std::vector
<SfxViewShell
*> &rShells
= SfxGetpApp()->GetViewShells_Impl();
3027 for (SfxViewShell
* pShell
: rShells
)
3031 // This code used to check that the frame exists in the other list,
3032 // because of https://bz.apache.org/ooo/show_bug.cgi?id=62084, with the explanation:
3033 // sometimes dangling SfxViewShells exist that point to a dead SfxViewFrame
3034 // these ViewShells shouldn't be accessible anymore
3035 // a destroyed ViewFrame is not in the ViewFrame array anymore, so checking this array helps
3036 // That doesn't seem to be needed anymore, but keep an assert, just in case.
3037 assert(std::find(SfxGetpApp()->GetViewFrames_Impl().begin(), SfxGetpApp()->GetViewFrames_Impl().end(),
3038 &pShell
->GetViewFrame()) != SfxGetpApp()->GetViewFrames_Impl().end());
3039 if ( ( !bOnlyVisible
|| pShell
->GetViewFrame().IsVisible() ) && (!isViewShell
|| isViewShell(pShell
)))
3047 // returns the next shell of spec. type viewing the specified doc.
3048 SfxViewShell
* SfxViewShell::GetNext
3050 const SfxViewShell
& rPrev
,
3052 const std::function
<bool ( const SfxViewShell
* )>& isViewShell
3055 std::vector
<SfxViewShell
*> &rShells
= SfxGetpApp()->GetViewShells_Impl();
3057 for ( nPos
= 0; nPos
< rShells
.size(); ++nPos
)
3058 if ( rShells
[nPos
] == &rPrev
)
3061 for ( ++nPos
; nPos
< rShells
.size(); ++nPos
)
3063 SfxViewShell
*pShell
= rShells
[nPos
];
3066 assert(std::find(SfxGetpApp()->GetViewFrames_Impl().begin(), SfxGetpApp()->GetViewFrames_Impl().end(),
3067 &pShell
->GetViewFrame()) != SfxGetpApp()->GetViewFrames_Impl().end());
3068 if ( ( !bOnlyVisible
|| pShell
->GetViewFrame().IsVisible() ) && (!isViewShell
|| isViewShell(pShell
)) )
3077 void SfxViewShell::Notify( SfxBroadcaster
& rBC
,
3078 const SfxHint
& rHint
)
3080 if (rHint
.GetId() != SfxHintId::ThisIsAnSfxEventHint
||
3081 static_cast<const SfxEventHint
&>(rHint
).GetEventId() != SfxEventHintId::LoadFinished
)
3086 if ( !GetController().is() )
3089 // avoid access to dangling ViewShells
3090 auto &rFrames
= SfxGetpApp()->GetViewFrames_Impl();
3091 for (SfxViewFrame
* frame
: rFrames
)
3093 if ( frame
== &GetViewFrame() && &rBC
== GetObjectShell() )
3095 SfxItemSet
& rSet
= GetObjectShell()->GetMedium()->GetItemSet();
3096 const SfxUnoAnyItem
* pItem
= rSet
.GetItem(SID_VIEW_DATA
, false);
3099 pImpl
->m_pController
->restoreViewData( pItem
->GetValue() );
3100 rSet
.ClearItem( SID_VIEW_DATA
);
3107 bool SfxViewShell::ExecKey_Impl(const KeyEvent
& aKey
)
3109 bool setModuleConfig
= false; // In case libreofficekit is active, we will re-set the module config class.
3110 if (!pImpl
->m_xAccExec
)
3112 pImpl
->m_xAccExec
= ::svt::AcceleratorExecute::createAcceleratorHelper();
3113 pImpl
->m_xAccExec
->init(::comphelper::getProcessComponentContext(),
3114 rFrame
.GetFrame().GetFrameInterface());
3115 setModuleConfig
= true;
3118 if (comphelper::LibreOfficeKit::isActive())
3120 // Get the module name.
3121 css::uno::Reference
< css::uno::XComponentContext
> xContext (::comphelper::getProcessComponentContext());
3122 css::uno::Reference
< css::frame::XModuleManager2
> xModuleManager(css::frame::ModuleManager::create(xContext
));
3123 OUString sModule
= xModuleManager
->identify(rFrame
.GetFrame().GetFrameInterface());
3125 // Get the language name.
3126 OUString viewLang
= GetLOKLanguageTag().getBcp47();
3128 // Merge them & have a key.
3129 OUString key
= sModule
+ viewLang
;
3131 // Check it in configurations map. Create a configuration manager if there isn't one for the key.
3132 std::unordered_map
<OUString
, css::uno::Reference
<com::sun::star::ui::XAcceleratorConfiguration
>>& acceleratorConfs
= SfxApplication::Get()->GetAcceleratorConfs_Impl();
3133 if (acceleratorConfs
.find(key
) == acceleratorConfs
.end())
3135 // Create a new configuration manager for the module.
3137 OUString actualLang
= officecfg::Setup::L10N::ooLocale::get();
3139 std::shared_ptr
<comphelper::ConfigurationChanges
> batch(comphelper::ConfigurationChanges::create());
3140 officecfg::Setup::L10N::ooLocale::set(viewLang
, batch
);
3143 // We have set the language. Time to create the config manager.
3144 acceleratorConfs
[key
] = svt::AcceleratorExecute::lok_createNewAcceleratorConfiguration(::comphelper::getProcessComponentContext(), sModule
);
3146 std::shared_ptr
<comphelper::ConfigurationChanges
> batch2(comphelper::ConfigurationChanges::create());
3147 officecfg::Setup::L10N::ooLocale::set(actualLang
, batch2
);
3151 if (setModuleConfig
)
3152 pImpl
->m_xAccExec
->lok_setModuleConfig(acceleratorConfs
[key
]);
3155 return pImpl
->m_xAccExec
->execute(aKey
.GetKeyCode());
3158 void SfxViewShell::setLibreOfficeKitViewCallback(SfxLokCallbackInterface
* pCallback
)
3160 pImpl
->m_pLibreOfficeKitViewCallback
= pCallback
;
3162 afterCallbackRegistered();
3164 if (!pImpl
->m_pLibreOfficeKitViewCallback
)
3167 // Ask other views to tell us about their cursors.
3168 SfxViewShell
* pViewShell
= SfxViewShell::GetFirst();
3171 if (pViewShell
->GetDocId() == GetDocId())
3172 pViewShell
->NotifyCursor(this);
3173 pViewShell
= SfxViewShell::GetNext(*pViewShell
);
3177 SfxLokCallbackInterface
* SfxViewShell::getLibreOfficeKitViewCallback() const
3179 return pImpl
->m_pLibreOfficeKitViewCallback
;
3182 void SfxViewShell::dumpLibreOfficeKitViewState(rtl::OStringBuffer
&rState
)
3184 rState
.append("\n SfxViewShell: ");
3185 rState
.append(OString::number(reinterpret_cast<sal_uInt64
>(this), 16));
3186 rState
.append("\n\tDocId:\t");
3187 auto nDocId
= static_cast<int>(GetDocId());
3188 rState
.append(static_cast<sal_Int32
>(nDocId
));
3189 rState
.append("\n\tViewId:\t");
3190 rState
.append(static_cast<sal_Int32
>(GetViewShellId()));
3191 rState
.append("\n\tPart:\t");
3192 rState
.append(static_cast<sal_Int32
>(getPart()));
3193 rState
.append("\n\tLang:\t");
3194 rState
.append(OUStringToOString(GetLOKLanguageTag().getBcp47(), RTL_TEXTENCODING_UTF8
));
3195 rState
.append("\n\tA11y:\t");
3196 rState
.append(GetLOKAccessibilityState() ? "enabled" : "disabled");
3198 if (pImpl
->m_pLibreOfficeKitViewCallback
)
3199 pImpl
->m_pLibreOfficeKitViewCallback
->dumpState(rState
);
3202 static bool ignoreLibreOfficeKitViewCallback(int nType
, const SfxViewShell_Impl
* pImpl
)
3204 if (!comphelper::LibreOfficeKit::isActive())
3207 if (comphelper::LibreOfficeKit::isTiledPainting())
3211 case LOK_CALLBACK_FORM_FIELD_BUTTON
:
3212 case LOK_CALLBACK_TEXT_SELECTION
:
3213 case LOK_CALLBACK_COMMENT
:
3216 // Reject e.g. invalidate during paint.
3221 if (pImpl
->m_bTiledSearching
)
3225 case LOK_CALLBACK_TEXT_SELECTION
:
3226 case LOK_CALLBACK_TEXT_VIEW_SELECTION
:
3227 case LOK_CALLBACK_TEXT_SELECTION_START
:
3228 case LOK_CALLBACK_TEXT_SELECTION_END
:
3229 case LOK_CALLBACK_GRAPHIC_SELECTION
:
3230 case LOK_CALLBACK_GRAPHIC_VIEW_SELECTION
:
3238 void SfxViewShell::libreOfficeKitViewInvalidateTilesCallback(const tools::Rectangle
* pRect
, int nPart
, int nMode
) const
3240 if (ignoreLibreOfficeKitViewCallback(LOK_CALLBACK_INVALIDATE_TILES
, pImpl
.get()))
3242 if (pImpl
->m_pLibreOfficeKitViewCallback
)
3243 pImpl
->m_pLibreOfficeKitViewCallback
->libreOfficeKitViewInvalidateTilesCallback(pRect
, nPart
, nMode
);
3247 "SfxViewShell::libreOfficeKitViewInvalidateTilesCallback no callback set!");
3250 void SfxViewShell::libreOfficeKitViewCallbackWithViewId(int nType
, const OString
& pPayload
, int nViewId
) const
3252 if (ignoreLibreOfficeKitViewCallback(nType
, pImpl
.get()))
3254 if (pImpl
->m_pLibreOfficeKitViewCallback
)
3255 pImpl
->m_pLibreOfficeKitViewCallback
->libreOfficeKitViewCallbackWithViewId(nType
, pPayload
, nViewId
);
3259 "SfxViewShell::libreOfficeKitViewCallbackWithViewId no callback set! Dropped payload of type "
3260 << lokCallbackTypeToString(nType
) << ": [" << pPayload
<< ']');
3263 void SfxViewShell::libreOfficeKitViewCallback(int nType
, const OString
& pPayload
) const
3265 if (ignoreLibreOfficeKitViewCallback(nType
, pImpl
.get()))
3267 if (pImpl
->m_pLibreOfficeKitViewCallback
)
3268 pImpl
->m_pLibreOfficeKitViewCallback
->libreOfficeKitViewCallback(nType
, pPayload
);
3272 "SfxViewShell::libreOfficeKitViewCallback no callback set! Dropped payload of type "
3273 << lokCallbackTypeToString(nType
) << ": [" << pPayload
<< ']');
3276 void SfxViewShell::libreOfficeKitViewUpdatedCallback(int nType
) const
3278 if (ignoreLibreOfficeKitViewCallback(nType
, pImpl
.get()))
3280 if (pImpl
->m_pLibreOfficeKitViewCallback
)
3281 pImpl
->m_pLibreOfficeKitViewCallback
->libreOfficeKitViewUpdatedCallback(nType
);
3285 "SfxViewShell::libreOfficeKitViewUpdatedCallback no callback set! Dropped payload of type "
3286 << lokCallbackTypeToString(nType
));
3289 void SfxViewShell::libreOfficeKitViewUpdatedCallbackPerViewId(int nType
, int nViewId
, int nSourceViewId
) const
3291 if (ignoreLibreOfficeKitViewCallback(nType
, pImpl
.get()))
3293 if (pImpl
->m_pLibreOfficeKitViewCallback
)
3294 pImpl
->m_pLibreOfficeKitViewCallback
->libreOfficeKitViewUpdatedCallbackPerViewId(nType
, nViewId
, nSourceViewId
);
3298 "SfxViewShell::libreOfficeKitViewUpdatedCallbackPerViewId no callback set! Dropped payload of type "
3299 << lokCallbackTypeToString(nType
));
3302 void SfxViewShell::libreOfficeKitViewAddPendingInvalidateTiles()
3304 if (pImpl
->m_pLibreOfficeKitViewCallback
)
3305 pImpl
->m_pLibreOfficeKitViewCallback
->libreOfficeKitViewAddPendingInvalidateTiles();
3309 "SfxViewShell::libreOfficeKitViewAddPendingInvalidateTiles no callback set!");
3312 void SfxViewShell::afterCallbackRegistered()
3314 LOK_INFO("sfx.view", "SfxViewShell::afterCallbackRegistered invoked");
3315 if (GetLOKAccessibilityState())
3317 LOKDocumentFocusListener
& rDocFocusListener
= GetLOKDocumentFocusListener();
3318 rDocFocusListener
.notifyFocusedParagraphChanged();
3322 void SfxViewShell::flushPendingLOKInvalidateTiles()
3324 // SfxViewShell itself does not delay any tile invalidations.
3327 std::optional
<OString
> SfxViewShell::getLOKPayload(int nType
, int /*nViewId*/) const
3329 // SfxViewShell itself currently doesn't handle any updated-payload types.
3330 SAL_WARN("sfx.view", "SfxViewShell::getLOKPayload unhandled type " << lokCallbackTypeToString(nType
));
3334 vcl::Window
* SfxViewShell::GetEditWindowForActiveOLEObj() const
3336 vcl::Window
* pEditWin
= nullptr;
3337 SfxInPlaceClient
* pIPClient
= GetIPClient();
3340 pEditWin
= pIPClient
->GetEditWin();
3345 ::Color
SfxViewShell::GetColorConfigColor(svtools::ColorConfigEntry eEntry
) const
3347 SAL_WARN("sfx.view", "SfxViewShell::GetColorConfigColor not overridden!");
3348 svtools::ColorConfig aColorConfig
;
3349 return aColorConfig
.GetColorValue(eEntry
).nColor
;
3352 void SfxViewShell::SetLOKLanguageTag(const OUString
& rBcp47LanguageTag
)
3354 LanguageTag
aTag(rBcp47LanguageTag
, true);
3356 css::uno::Sequence
<OUString
> inst(officecfg::Setup::Office::InstalledLocales::get()->getElementNames());
3357 LanguageTag aFallbackTag
= LanguageTag(getInstalledLocaleForSystemUILanguage(inst
, /* bRequestInstallIfMissing */ false, rBcp47LanguageTag
), true).makeFallback();
3359 // If we want de-CH, and the de localisation is available, we don't want to use de-DE as then
3360 // the magic in Translate::get() won't turn ess-zet into double s. Possibly other similar cases?
3361 if (comphelper::LibreOfficeKit::isActive() && aTag
.getLanguage() == aFallbackTag
.getLanguage())
3362 maLOKLanguageTag
= aTag
;
3364 maLOKLanguageTag
= aFallbackTag
;
3367 LOKDocumentFocusListener
& SfxViewShell::GetLOKDocumentFocusListener()
3369 if (mpLOKDocumentFocusListener
)
3370 return *mpLOKDocumentFocusListener
;
3372 mpLOKDocumentFocusListener
= new LOKDocumentFocusListener(this);
3373 return *mpLOKDocumentFocusListener
;
3376 const LOKDocumentFocusListener
& SfxViewShell::GetLOKDocumentFocusListener() const
3378 return const_cast<SfxViewShell
*>(this)->GetLOKDocumentFocusListener();
3381 void SfxViewShell::SetLOKAccessibilityState(bool bEnabled
)
3383 if (bEnabled
== mbLOKAccessibilityEnabled
)
3385 mbLOKAccessibilityEnabled
= bEnabled
;
3387 LOKDocumentFocusListener
& rDocumentFocusListener
= GetLOKDocumentFocusListener();
3392 uno::Reference
< accessibility::XAccessible
> xAccessible
=
3393 pWindow
->GetAccessible();
3395 if (!xAccessible
.is())
3398 if (mbLOKAccessibilityEnabled
)
3402 rDocumentFocusListener
.attachRecursive(xAccessible
);
3404 catch (const uno::Exception
&)
3406 LOK_WARN("SetLOKAccessibilityState", "Exception caught processing LOKDocumentFocusListener::attachRecursive");
3413 rDocumentFocusListener
.detachRecursive(xAccessible
, /*bForce*/ true);
3415 catch (const uno::Exception
&)
3417 LOK_WARN("SetLOKAccessibilityState", "Exception caught processing LOKDocumentFocusListener::detachRecursive");
3422 void SfxViewShell::SetLOKLocale(const OUString
& rBcp47LanguageTag
)
3424 maLOKLocale
= LanguageTag(rBcp47LanguageTag
, true).makeFallback();
3427 void SfxViewShell::NotifyCursor(SfxViewShell
* /*pViewShell*/) const
3431 void SfxViewShell::setTiledSearching(bool bTiledSearching
)
3433 pImpl
->m_bTiledSearching
= bTiledSearching
;
3436 int SfxViewShell::getPart() const
3441 int SfxViewShell::getEditMode() const
3446 ViewShellId
SfxViewShell::GetViewShellId() const
3448 return pImpl
->m_nViewShellId
;
3451 void SfxViewShell::SetCurrentDocId(ViewShellDocId nId
)
3453 mnCurrentDocId
= nId
;
3456 ViewShellDocId
SfxViewShell::GetDocId() const
3458 assert(pImpl
->m_nDocId
>= ViewShellDocId(0) && "m_nDocId should have been initialized, but it is invalid.");
3459 return pImpl
->m_nDocId
;
3462 void SfxViewShell::notifyInvalidation(tools::Rectangle
const* pRect
) const
3464 SfxLokHelper::notifyInvalidation(this, pRect
);
3467 void SfxViewShell::NotifyOtherViews(int nType
, const OString
& rKey
, const OString
& rPayload
)
3469 SfxLokHelper::notifyOtherViews(this, nType
, rKey
, rPayload
);
3472 void SfxViewShell::NotifyOtherView(OutlinerViewShell
* pOther
, int nType
, const OString
& rKey
, const OString
& rPayload
)
3474 auto pOtherShell
= dynamic_cast<SfxViewShell
*>(pOther
);
3478 SfxLokHelper::notifyOtherView(this, pOtherShell
, nType
, rKey
, rPayload
);
3481 void SfxViewShell::dumpAsXml(xmlTextWriterPtr pWriter
) const
3483 (void)xmlTextWriterStartElement(pWriter
, BAD_CAST("SfxViewShell"));
3484 (void)xmlTextWriterWriteFormatAttribute(pWriter
, BAD_CAST("ptr"), "%p", this);
3485 (void)xmlTextWriterWriteAttribute(pWriter
, BAD_CAST("id"), BAD_CAST(OString::number(static_cast<sal_Int32
>(GetViewShellId())).getStr()));
3486 (void)xmlTextWriterEndElement(pWriter
);
3489 bool SfxViewShell::KeyInput( const KeyEvent
&rKeyEvent
)
3493 This Method executes the KeyEvent 'rKeyEvent' of the Keys (Accelerator)
3494 configured either direct or indirect (for example by the Application)
3495 in the SfxViewShell.
3500 The Key (Accelerator) is configured and the
3501 associated Handler was called
3504 The Key (Accelerator) is not configured and
3505 subsequently no Handler was called
3509 <SfxApplication::KeyInput(const KeyEvent&)>
3512 return ExecKey_Impl(rKeyEvent
);
3515 bool SfxViewShell::GlobalKeyInput_Impl( const KeyEvent
&rKeyEvent
)
3517 return ExecKey_Impl(rKeyEvent
);
3521 void SfxViewShell::ShowCursor( bool /*bOn*/ )
3525 Subclasses must override this Method so that SFx can switch the
3526 Cursor on and off, for example while a <SfxProgress> is running.
3533 void SfxViewShell::ResetAllClients_Impl( SfxInPlaceClient
const *pIP
)
3536 std::vector
< SfxInPlaceClient
* >& rClients
= pImpl
->GetIPClients_Impl();
3537 if ( rClients
.empty() )
3540 for (SfxInPlaceClient
* pIPClient
: rClients
)
3542 if( pIPClient
!= pIP
)
3543 pIPClient
->ResetObject();
3548 void SfxViewShell::DisconnectAllClients()
3550 std::vector
< SfxInPlaceClient
* >& rClients
= pImpl
->GetIPClients_Impl();
3551 if ( rClients
.empty() )
3554 for ( size_t n
= 0; n
< rClients
.size(); )
3555 // clients will remove themselves from the list
3556 delete rClients
.at( n
);
3560 void SfxViewShell::QueryObjAreaPixel( tools::Rectangle
& ) const
3565 void SfxViewShell::VisAreaChanged()
3567 std::vector
< SfxInPlaceClient
* >& rClients
= pImpl
->GetIPClients_Impl();
3568 if ( rClients
.empty() )
3571 for (SfxInPlaceClient
* pIPClient
: rClients
)
3573 if ( pIPClient
->IsObjectInPlaceActive() )
3574 // client is active, notify client that the VisArea might have changed
3575 pIPClient
->VisAreaChanged();
3580 void SfxViewShell::CheckIPClient_Impl(
3581 SfxInPlaceClient
const *const pIPClient
, const tools::Rectangle
& rVisArea
)
3583 if ( GetObjectShell()->IsInClose() )
3586 bool bAlwaysActive
=
3587 ( ( pIPClient
->GetObjectMiscStatus() & embed::EmbedMisc::EMBED_ACTIVATEIMMEDIATELY
) != 0 );
3588 bool bActiveWhenVisible
=
3589 ( pIPClient
->GetObjectMiscStatus() & embed::EmbedMisc::MS_EMBED_ACTIVATEWHENVISIBLE
) != 0;
3591 // this method is called when a client is created
3592 if (pIPClient
->IsObjectInPlaceActive())
3595 // object in client is currently not active
3596 // check if the object wants to be activated always or when it becomes at least partially visible
3597 // TODO/LATER: maybe we should use the scaled area instead of the ObjArea?!
3598 if (bAlwaysActive
|| (bActiveWhenVisible
&& rVisArea
.Overlaps(pIPClient
->GetObjArea())))
3602 pIPClient
->GetObject()->changeState( embed::EmbedStates::INPLACE_ACTIVE
);
3604 catch (const uno::Exception
&)
3606 TOOLS_WARN_EXCEPTION("sfx.view", "SfxViewShell::CheckIPClient_Impl");
3611 SfxObjectShell
* SfxViewShell::GetObjectShell()
3613 return rFrame
.GetObjectShell();
3616 Reference
< XModel
> SfxViewShell::GetCurrentDocument() const
3618 Reference
< XModel
> xDocument
;
3620 const SfxObjectShell
* pDocShell( const_cast< SfxViewShell
* >( this )->GetObjectShell() );
3621 OSL_ENSURE( pDocShell
, "SfxViewFrame::GetCurrentDocument: no DocShell!?" );
3623 xDocument
= pDocShell
->GetModel();
3628 void SfxViewShell::SetCurrentDocument() const
3630 uno::Reference
< frame::XModel
> xDocument( GetCurrentDocument() );
3631 if ( xDocument
.is() )
3632 SfxObjectShell::SetCurrentComponent( xDocument
);
3636 const Size
& SfxViewShell::GetMargin() const
3638 return pImpl
->aMargin
;
3642 void SfxViewShell::SetMargin( const Size
& rSize
)
3644 // the default margin was verified using www.apple.com !!
3645 Size aMargin
= rSize
;
3646 if ( aMargin
.Width() == -1 )
3647 aMargin
.setWidth( DEFAULT_MARGIN_WIDTH
);
3648 if ( aMargin
.Height() == -1 )
3649 aMargin
.setHeight( DEFAULT_MARGIN_HEIGHT
);
3651 if ( aMargin
!= pImpl
->aMargin
)
3653 pImpl
->aMargin
= aMargin
;
3658 void SfxViewShell::MarginChanged()
3662 void SfxViewShell::JumpToMark( const OUString
& rMark
)
3664 SfxStringItem
aMarkItem( SID_JUMPTOMARK
, rMark
);
3665 GetViewFrame().GetDispatcher()->ExecuteList(
3667 SfxCallMode::SYNCHRON
|SfxCallMode::RECORD
,
3671 void SfxViewShell::SetController( SfxBaseController
* pController
)
3673 pImpl
->m_pController
= pController
;
3675 // there should be no old listener, but if there is one, it should be disconnected
3676 if ( pImpl
->xClipboardListener
.is() )
3677 pImpl
->xClipboardListener
->DisconnectViewShell();
3679 pImpl
->xClipboardListener
= new SfxClipboardChangeListener( this, GetClipboardNotifier() );
3682 Reference
< XController
> SfxViewShell::GetController() const
3684 return pImpl
->m_pController
;
3687 SfxBaseController
* SfxViewShell::GetBaseController_Impl() const
3689 return pImpl
->m_pController
.get();
3692 void SfxViewShell::AddContextMenuInterceptor_Impl( const uno::Reference
< ui::XContextMenuInterceptor
>& xInterceptor
)
3694 std::unique_lock
g(pImpl
->aMutex
);
3695 pImpl
->aInterceptorContainer
.addInterface( g
, xInterceptor
);
3698 void SfxViewShell::RemoveContextMenuInterceptor_Impl( const uno::Reference
< ui::XContextMenuInterceptor
>& xInterceptor
)
3700 std::unique_lock
g(pImpl
->aMutex
);
3701 pImpl
->aInterceptorContainer
.removeInterface( g
, xInterceptor
);
3704 bool SfxViewShell::TryContextMenuInterception(const rtl::Reference
<VCLXPopupMenu
>& rIn
,
3705 const OUString
& rMenuIdentifier
,
3706 rtl::Reference
<VCLXPopupMenu
>& rOut
,
3707 ui::ContextMenuExecuteEvent aEvent
)
3710 bool bModified
= false;
3712 // create container from menu
3713 aEvent
.ActionTriggerContainer
= ::framework::ActionTriggerHelper::CreateActionTriggerContainerFromMenu(
3714 rIn
, &rMenuIdentifier
);
3716 // get selection from controller
3717 aEvent
.Selection
.set( GetController(), uno::UNO_QUERY
);
3719 // call interceptors
3720 std::unique_lock
g(pImpl
->aMutex
);
3721 std::vector
<uno::Reference
< ui::XContextMenuInterceptor
>> aInterceptors
=
3722 pImpl
->aInterceptorContainer
.getElements(g
);
3724 for (const auto & rListener
: aInterceptors
)
3728 ui::ContextMenuInterceptorAction eAction
;
3730 SolarMutexReleaser rel
;
3731 eAction
= rListener
->notifyContextMenuExecute( aEvent
);
3735 case ui::ContextMenuInterceptorAction_CANCELLED
:
3736 // interceptor does not want execution
3738 case ui::ContextMenuInterceptorAction_EXECUTE_MODIFIED
:
3739 // interceptor wants his modified menu to be executed
3742 case ui::ContextMenuInterceptorAction_CONTINUE_MODIFIED
:
3743 // interceptor has modified menu, but allows for calling other interceptors
3746 case ui::ContextMenuInterceptorAction_IGNORED
:
3747 // interceptor is indifferent
3750 OSL_FAIL("Wrong return value of ContextMenuInterceptor!");
3757 pImpl
->aInterceptorContainer
.removeInterface(g
, rListener
);
3766 // container was modified, create a new menu out of it
3767 rOut
= new VCLXPopupMenu();
3768 ::framework::ActionTriggerHelper::CreateMenuFromActionTriggerContainer(rOut
, aEvent
.ActionTriggerContainer
);
3774 bool SfxViewShell::TryContextMenuInterception(const rtl::Reference
<VCLXPopupMenu
>& rPopupMenu
,
3775 const OUString
& rMenuIdentifier
, css::ui::ContextMenuExecuteEvent aEvent
)
3777 bool bModified
= false;
3779 // create container from menu
3780 aEvent
.ActionTriggerContainer
= ::framework::ActionTriggerHelper::CreateActionTriggerContainerFromMenu(
3781 rPopupMenu
, &rMenuIdentifier
);
3783 // get selection from controller
3784 aEvent
.Selection
= css::uno::Reference
< css::view::XSelectionSupplier
>( GetController(), css::uno::UNO_QUERY
);
3786 // call interceptors
3787 std::unique_lock
g(pImpl
->aMutex
);
3788 std::vector
<uno::Reference
< ui::XContextMenuInterceptor
>> aInterceptors
=
3789 pImpl
->aInterceptorContainer
.getElements(g
);
3791 for (const auto & rListener
: aInterceptors
)
3795 css::ui::ContextMenuInterceptorAction eAction
;
3797 SolarMutexReleaser rel
;
3798 eAction
= rListener
->notifyContextMenuExecute( aEvent
);
3802 case css::ui::ContextMenuInterceptorAction_CANCELLED
:
3803 // interceptor does not want execution
3805 case css::ui::ContextMenuInterceptorAction_EXECUTE_MODIFIED
:
3806 // interceptor wants his modified menu to be executed
3809 case css::ui::ContextMenuInterceptorAction_CONTINUE_MODIFIED
:
3810 // interceptor has modified menu, but allows for calling other interceptors
3813 case css::ui::ContextMenuInterceptorAction_IGNORED
:
3814 // interceptor is indifferent
3817 SAL_WARN( "sfx.view", "Wrong return value of ContextMenuInterceptor!" );
3824 pImpl
->aInterceptorContainer
.removeInterface(g
, rListener
);
3833 rPopupMenu
->clear();
3834 ::framework::ActionTriggerHelper::CreateMenuFromActionTriggerContainer(rPopupMenu
, aEvent
.ActionTriggerContainer
);
3840 bool SfxViewShell::HandleNotifyEvent_Impl( NotifyEvent
const & rEvent
)
3842 if (pImpl
->m_pController
.is())
3843 return pImpl
->m_pController
->HandleEvent_Impl( rEvent
);
3847 bool SfxViewShell::HasKeyListeners_Impl() const
3849 return (pImpl
->m_pController
.is())
3850 && pImpl
->m_pController
->HasKeyListeners_Impl();
3853 bool SfxViewShell::HasMouseClickListeners_Impl() const
3855 return (pImpl
->m_pController
.is())
3856 && pImpl
->m_pController
->HasMouseClickListeners_Impl();
3859 bool SfxViewShell::Escape()
3861 return GetViewFrame().GetBindings().Execute(SID_TERMINATE_INPLACEACTIVATION
);
3864 Reference
< view::XRenderable
> SfxViewShell::GetRenderable()
3866 Reference
< view::XRenderable
>xRender
;
3867 SfxObjectShell
* pObj
= GetObjectShell();
3870 Reference
< frame::XModel
> xModel( pObj
->GetModel() );
3872 xRender
.set( xModel
, UNO_QUERY
);
3877 void SfxViewShell::notifyWindow(vcl::LOKWindowId nDialogId
, const OUString
& rAction
, const std::vector
<vcl::LOKPayloadItem
>& rPayload
) const
3879 SfxLokHelper::notifyWindow(this, nDialogId
, rAction
, rPayload
);
3882 OString
SfxViewShell::dumpNotifyState() const
3884 return OString("sfxviewsh: " +
3885 OString::number(reinterpret_cast<sal_uInt64
>(this), 16) +
3886 " doc: " + OString::number(static_cast<sal_Int32
>(static_cast<int>(GetDocId()))) +
3888 OString::number(static_cast<sal_Int32
>(GetViewShellId())));
3891 uno::Reference
< datatransfer::clipboard::XClipboardNotifier
> SfxViewShell::GetClipboardNotifier() const
3893 uno::Reference
< datatransfer::clipboard::XClipboardNotifier
> xClipboardNotifier
;
3894 xClipboardNotifier
.set(GetViewFrame().GetWindow().GetClipboard(), uno::UNO_QUERY
);
3895 return xClipboardNotifier
;
3898 void SfxViewShell::AddRemoveClipboardListener( const uno::Reference
< datatransfer::clipboard::XClipboardListener
>& rClp
, bool bAdd
)
3902 uno::Reference
< datatransfer::clipboard::XClipboard
> xClipboard(GetViewFrame().GetWindow().GetClipboard());
3903 if( xClipboard
.is() )
3905 uno::Reference
< datatransfer::clipboard::XClipboardNotifier
> xClpbrdNtfr( xClipboard
, uno::UNO_QUERY
);
3906 if( xClpbrdNtfr
.is() )
3909 xClpbrdNtfr
->addClipboardListener( rClp
);
3911 xClpbrdNtfr
->removeClipboardListener( rClp
);
3915 catch (const uno::Exception
&)
3920 weld::Window
* SfxViewShell::GetFrameWeld() const
3922 return pWindow
? pWindow
->GetFrameWeld() : nullptr;
3925 void SfxViewShell::setBlockedCommandList(const char* blockedCommandList
)
3927 if(!mvLOKBlockedCommandList
.empty())
3930 OUString
BlockedListString(blockedCommandList
, strlen(blockedCommandList
), RTL_TEXTENCODING_UTF8
);
3931 OUString command
= BlockedListString
.getToken(0, ' ');
3932 for (size_t i
= 1; !command
.isEmpty(); i
++)
3934 mvLOKBlockedCommandList
.emplace(command
);
3935 command
= BlockedListString
.getToken(i
, ' ');
3939 bool SfxViewShell::isBlockedCommand(OUString command
) const
3941 return mvLOKBlockedCommandList
.find(command
) != mvLOKBlockedCommandList
.end();
3944 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */