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