xref: /core/sfx2/source/view/viewsh.cxx (revision 150525a38ef011b7bc1f84179ff643874eb3d1c7)
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.
4  *
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/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
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 .
18  */
19 
20 #include <config_features.h>
21 
22 #include <boost/property_tree/json_parser.hpp>
23 
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/drawing/XShapes.hpp>
51 #include <com/sun/star/view/XRenderable.hpp>
52 #include <com/sun/star/uno/Reference.hxx>
53 #include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
54 #include <com/sun/star/accessibility/XAccessibleContext.hpp>
55 #include <com/sun/star/accessibility/XAccessibleEventBroadcaster.hpp>
56 #include <com/sun/star/accessibility/XAccessibleSelection.hpp>
57 #include <com/sun/star/accessibility/AccessibleEventId.hpp>
58 #include <com/sun/star/accessibility/AccessibleStateType.hpp>
59 #include <com/sun/star/accessibility/AccessibleRole.hpp>
60 #include <com/sun/star/accessibility/XAccessibleText.hpp>
61 #include <com/sun/star/accessibility/XAccessibleTable.hpp>
62 #include <cppuhelper/implbase.hxx>
63 #include <com/sun/star/ui/XAcceleratorConfiguration.hpp>
64 
65 #include <cppuhelper/weakref.hxx>
66 
67 #include <com/sun/star/accessibility/XAccessibleTextAttributes.hpp>
68 #include <com/sun/star/accessibility/AccessibleTextType.hpp>
69 #include <com/sun/star/awt/FontSlant.hpp>
70 
71 #include <comphelper/OAccessible.hxx>
72 #include <comphelper/diagnose_ex.hxx>
73 #include <editeng/unoprnms.hxx>
74 #include <tools/urlobj.hxx>
75 #include <unotools/tempfile.hxx>
76 #include <svtools/soerr.hxx>
77 #include <tools/svborder.hxx>
78 
79 #include <framework/actiontriggerhelper.hxx>
80 #include <comphelper/lok.hxx>
81 #include <comphelper/processfactory.hxx>
82 #include <comphelper/propertyvalue.hxx>
83 #include <comphelper/sequenceashashmap.hxx>
84 #include <toolkit/helper/vclunohelper.hxx>
85 #include <vcl/settings.hxx>
86 #include <vcl/commandinfoprovider.hxx>
87 #include <LibreOfficeKit/LibreOfficeKitEnums.h>
88 
89 #include <officecfg/Setup.hxx>
90 #include <sfx2/app.hxx>
91 #include <sfx2/flatpak.hxx>
92 #include <sfx2/viewsh.hxx>
93 #include "viewimp.hxx"
94 #include <sfx2/sfxresid.hxx>
95 #include <sfx2/request.hxx>
96 #include <sfx2/printer.hxx>
97 #include <sfx2/docfile.hxx>
98 #include <sfx2/dispatch.hxx>
99 #include <sfx2/strings.hrc>
100 #include <sfx2/sfxbasecontroller.hxx>
101 #include <sfx2/mailmodelapi.hxx>
102 #include <bluthsndapi.hxx>
103 #include <sfx2/viewfrm.hxx>
104 #include <sfx2/event.hxx>
105 #include <sfx2/ipclient.hxx>
106 #include <sfx2/sfxsids.hrc>
107 #include <sfx2/objface.hxx>
108 #include <sfx2/lokhelper.hxx>
109 #include <sfx2/lokcallback.hxx>
110 #include <openuriexternally.hxx>
111 #include <iostream>
112 #include <vector>
113 #include <list>
114 #include <libxml/xmlwriter.h>
115 #include <toolkit/awt/vclxmenu.hxx>
116 #include <unordered_map>
117 #include <unordered_set>
118 
119 #define ShellClass_SfxViewShell
120 #include <sfxslots.hxx>
121 
122 using namespace ::com::sun::star;
123 using namespace ::com::sun::star::uno;
124 using namespace ::com::sun::star::frame;
125 using namespace ::com::sun::star::beans;
126 using namespace ::cppu;
127 
128 class SfxClipboardChangeListener : public ::cppu::WeakImplHelper<
129     datatransfer::clipboard::XClipboardListener >
130 {
131 public:
132     SfxClipboardChangeListener( SfxViewShell* pView, uno::Reference< datatransfer::clipboard::XClipboardNotifier > xClpbrdNtfr );
133 
134     // XEventListener
135     virtual void SAL_CALL disposing( const lang::EventObject& rEventObject ) override;
136 
137     // XClipboardListener
138     virtual void SAL_CALL changedContents( const datatransfer::clipboard::ClipboardEvent& rEventObject ) override;
139 
DisconnectViewShell()140     void DisconnectViewShell() { m_pViewShell = nullptr; }
141     void ChangedContents();
142 
143     enum AsyncExecuteCmd
144     {
145         ASYNCEXECUTE_CMD_DISPOSING,
146         ASYNCEXECUTE_CMD_CHANGEDCONTENTS
147     };
148 
149     struct AsyncExecuteInfo
150     {
AsyncExecuteInfoSfxClipboardChangeListener::AsyncExecuteInfo151         AsyncExecuteInfo( AsyncExecuteCmd eCmd, SfxClipboardChangeListener* pListener ) :
152             m_eCmd( eCmd ), m_xListener( pListener ) {}
153 
154         AsyncExecuteCmd m_eCmd;
155         rtl::Reference<SfxClipboardChangeListener> m_xListener;
156     };
157 
158 private:
159     SfxViewShell* m_pViewShell;
160     uno::Reference< datatransfer::clipboard::XClipboardNotifier > m_xClpbrdNtfr;
161     uno::Reference< lang::XComponent > m_xCtrl;
162 
163     DECL_STATIC_LINK( SfxClipboardChangeListener, AsyncExecuteHdl_Impl, void*, void );
164 };
165 
SfxClipboardChangeListener(SfxViewShell * pView,uno::Reference<datatransfer::clipboard::XClipboardNotifier> xClpbrdNtfr)166 SfxClipboardChangeListener::SfxClipboardChangeListener( SfxViewShell* pView, uno::Reference< datatransfer::clipboard::XClipboardNotifier > xClpbrdNtfr )
167   : m_pViewShell( nullptr ), m_xClpbrdNtfr(std::move( xClpbrdNtfr )), m_xCtrl(pView->GetController())
168 {
169     if ( m_xCtrl.is() )
170     {
171         m_xCtrl->addEventListener( uno::Reference < lang::XEventListener > ( static_cast < lang::XEventListener* >( this ) ) );
172         m_pViewShell = pView;
173     }
174     if ( m_xClpbrdNtfr.is() )
175     {
176         m_xClpbrdNtfr->addClipboardListener( uno::Reference< datatransfer::clipboard::XClipboardListener >(
177             static_cast< datatransfer::clipboard::XClipboardListener* >( this )));
178     }
179 }
180 
ChangedContents()181 void SfxClipboardChangeListener::ChangedContents()
182 {
183     const SolarMutexGuard aGuard;
184     if (!m_pViewShell)
185         return;
186 
187     SfxBindings& rBind = m_pViewShell->GetViewFrame().GetBindings();
188     rBind.Invalidate(SID_PASTE);
189     rBind.Invalidate(SID_PASTE_SPECIAL);
190     rBind.Invalidate(SID_CLIPBOARD_FORMAT_ITEMS);
191 
192     if (comphelper::LibreOfficeKit::isActive())
193     {
194         // In the future we might send the payload as well.
195         SfxLokHelper::notifyAllViews(LOK_CALLBACK_CLIPBOARD_CHANGED, ""_ostr);
196     }
197 }
198 
IMPL_STATIC_LINK(SfxClipboardChangeListener,AsyncExecuteHdl_Impl,void *,p,void)199 IMPL_STATIC_LINK( SfxClipboardChangeListener, AsyncExecuteHdl_Impl, void*, p, void )
200 {
201     AsyncExecuteInfo* pAsyncExecuteInfo = static_cast<AsyncExecuteInfo*>(p);
202     if ( pAsyncExecuteInfo )
203     {
204         if ( pAsyncExecuteInfo->m_xListener.is() )
205         {
206             if ( pAsyncExecuteInfo->m_eCmd == ASYNCEXECUTE_CMD_DISPOSING )
207                 pAsyncExecuteInfo->m_xListener->DisconnectViewShell();
208             else if ( pAsyncExecuteInfo->m_eCmd == ASYNCEXECUTE_CMD_CHANGEDCONTENTS )
209                 pAsyncExecuteInfo->m_xListener->ChangedContents();
210         }
211     }
212     delete pAsyncExecuteInfo;
213 }
214 
disposing(const lang::EventObject &)215 void SAL_CALL SfxClipboardChangeListener::disposing( const lang::EventObject& /*rEventObject*/ )
216 {
217     // Either clipboard or ViewShell is going to be destroyed -> no interest in listening anymore
218     uno::Reference< lang::XComponent > xCtrl( m_xCtrl );
219     uno::Reference< datatransfer::clipboard::XClipboardNotifier > xNotify( m_xClpbrdNtfr );
220 
221     uno::Reference< datatransfer::clipboard::XClipboardListener > xThis( static_cast< datatransfer::clipboard::XClipboardListener* >( this ));
222     if ( xCtrl.is() )
223         xCtrl->removeEventListener( uno::Reference < lang::XEventListener > ( static_cast < lang::XEventListener* >( this )));
224     if ( xNotify.is() )
225         xNotify->removeClipboardListener( xThis );
226 
227     // Make asynchronous call to avoid locking SolarMutex which is the
228     // root for many deadlocks, especially in conjunction with the "Windows"
229     // based single thread apartment clipboard code!
230     AsyncExecuteInfo* pInfo = new AsyncExecuteInfo( ASYNCEXECUTE_CMD_DISPOSING, this );
231     if (!Application::PostUserEvent( LINK( nullptr, SfxClipboardChangeListener, AsyncExecuteHdl_Impl ), pInfo ))
232         delete pInfo;
233 }
234 
changedContents(const datatransfer::clipboard::ClipboardEvent &)235 void SAL_CALL SfxClipboardChangeListener::changedContents( const datatransfer::clipboard::ClipboardEvent& )
236 {
237     // Make asynchronous call to avoid locking SolarMutex which is the
238     // root for many deadlocks, especially in conjunction with the "Windows"
239     // based single thread apartment clipboard code!
240     AsyncExecuteInfo* pInfo = new AsyncExecuteInfo( ASYNCEXECUTE_CMD_CHANGEDCONTENTS, this );
241     if (!Application::PostUserEvent( LINK( nullptr, SfxClipboardChangeListener, AsyncExecuteHdl_Impl ), pInfo ))
242         delete pInfo;
243 }
244 
245 namespace
246 {
247 struct TableSizeType
248 {
249     sal_Int32 nRowCount;
250     sal_Int32 nColCount;
251 };
252 }
253 
254 typedef std::list<uno::Reference<accessibility::XAccessibleTable>> XAccessibleTableList;
255 
256 namespace
257 {
258 constexpr
isText(sal_Int16 nRole)259 bool isText(sal_Int16 nRole)
260 {
261     return nRole == accessibility::AccessibleRole::DOCUMENT_TEXT;
262 }
263 
264 constexpr
isSpreadsheet(sal_Int16 nRole)265 bool isSpreadsheet(sal_Int16 nRole)
266 {
267     return nRole == accessibility::AccessibleRole::DOCUMENT_SPREADSHEET;
268 }
269 
270 constexpr
isPresentation(sal_Int16 nRole)271 bool isPresentation(sal_Int16 nRole)
272 {
273     return nRole == accessibility::AccessibleRole::DOCUMENT_PRESENTATION;
274 }
275 
276 constexpr
isDocument(sal_Int16 nRole)277 bool isDocument(sal_Int16 nRole)
278 {
279     return isText(nRole) || isSpreadsheet(nRole) || isPresentation(nRole);
280 }
281 
hasState(const accessibility::AccessibleEventObject & aEvent,::sal_Int64 nState)282 bool hasState(const accessibility::AccessibleEventObject& aEvent, ::sal_Int64 nState)
283 {
284     bool res = false;
285     uno::Reference< accessibility::XAccessibleContext > xContext(aEvent.Source, uno::UNO_QUERY);
286     if (xContext.is())
287     {
288         ::sal_Int64 nStateSet = xContext->getAccessibleStateSet();
289         res = (nStateSet & nState) != 0;
290     }
291     return res;
292 }
293 
isFocused(const accessibility::AccessibleEventObject & aEvent)294 bool isFocused(const accessibility::AccessibleEventObject& aEvent)
295 {
296     return hasState(aEvent, accessibility::AccessibleStateType::FOCUSED);
297 }
298 
299 uno::Reference<accessibility::XAccessibleContext>
getParentContext(const uno::Reference<accessibility::XAccessibleContext> & xContext)300 getParentContext(const uno::Reference<accessibility::XAccessibleContext>& xContext)
301 {
302     uno::Reference<accessibility::XAccessibleContext> xParentContext;
303     uno::Reference<accessibility::XAccessible> xParent = xContext->getAccessibleParent();
304     if (xParent.is())
305         xParentContext = uno::Reference<accessibility::XAccessibleContext>(xParent, uno::UNO_QUERY);
306     return xParentContext;
307 }
308 
selectionEventTypeToString(sal_Int16 nEventId)309 OUString selectionEventTypeToString(sal_Int16 nEventId)
310 {
311     using namespace accessibility;
312     switch(nEventId)
313     {
314         case AccessibleEventId::SELECTION_CHANGED:
315             return u"create"_ustr;
316         case AccessibleEventId::SELECTION_CHANGED_ADD:
317             return u"add"_ustr;
318         case AccessibleEventId::SELECTION_CHANGED_REMOVE:
319             return u"remove"_ustr;
320         default:
321             return u""_ustr;
322     }
323 }
324 
selectionHasToBeNotified(const uno::Reference<accessibility::XAccessibleContext> & xContext)325 bool selectionHasToBeNotified(const uno::Reference<accessibility::XAccessibleContext>& xContext)
326 {
327     sal_Int16 nRole = xContext->getAccessibleRole();
328     return
329         nRole == accessibility::AccessibleRole::GRAPHIC ||
330         nRole == accessibility::AccessibleRole::EMBEDDED_OBJECT ||
331         nRole == accessibility::AccessibleRole::SHAPE;
332 }
333 
hasToBeActiveForEditing(sal_Int16 nRole)334 bool hasToBeActiveForEditing(sal_Int16 nRole)
335 {
336     return
337         nRole == accessibility::AccessibleRole::SHAPE;
338 }
339 
getParentRole(const uno::Reference<accessibility::XAccessibleContext> & xContext)340 sal_Int16 getParentRole(const uno::Reference<accessibility::XAccessibleContext>& xContext)
341 {
342     sal_Int16 nRole = 0;
343     if (xContext.is())
344     {
345         uno::Reference<accessibility::XAccessibleContext> xParentContext = getParentContext(xContext);
346         if (xParentContext.is())
347             nRole = xParentContext->getAccessibleRole();
348     }
349     return nRole;
350 }
351 
getAccessibleSiblingCount(const Reference<accessibility::XAccessibleContext> & xContext)352 sal_Int64 getAccessibleSiblingCount(const Reference<accessibility::XAccessibleContext>& xContext)
353 {
354     if (!xContext.is())
355         return -1;
356 
357     sal_Int64 nChildCount = 0;
358     Reference<accessibility::XAccessible> xParent = xContext->getAccessibleParent();
359     if (xParent.is())
360     {
361         Reference<accessibility::XAccessibleContext> xParentContext = xParent->getAccessibleContext();
362         if (xParentContext.is())
363         {
364             nChildCount = xParentContext->getAccessibleChildCount();
365         }
366     }
367     return nChildCount - 1;
368 }
369 
370 // Put in rAncestorList all ancestors of xTable up to xAncestorTable or
371 // up to the first not-a-table ancestor if xAncestorTable is not an ancestor.
372 // xTable is included in the list, xAncestorTable is not included.
373 // The list is ordered from the ancient ancestor to xTable.
374 // Return true if xAncestorTable is an ancestor of xTable.
getAncestorList(XAccessibleTableList & rAncestorList,const uno::Reference<accessibility::XAccessibleTable> & xTable,const uno::Reference<accessibility::XAccessibleTable> & xAncestorTable=uno::Reference<accessibility::XAccessibleTable> ())375 bool getAncestorList(XAccessibleTableList& rAncestorList,
376                      const uno::Reference<accessibility::XAccessibleTable>& xTable,
377                      const uno::Reference<accessibility::XAccessibleTable>& xAncestorTable = uno::Reference<accessibility::XAccessibleTable>())
378 {
379     uno::Reference<accessibility::XAccessibleTable> xCurrentTable = xTable;
380     while (xCurrentTable.is() && xCurrentTable != xAncestorTable)
381     {
382         rAncestorList.push_front(xCurrentTable);
383 
384         uno::Reference<accessibility::XAccessibleContext> xContext(xCurrentTable, uno::UNO_QUERY);
385         xCurrentTable.clear();
386         if (xContext.is())
387         {
388             uno::Reference<accessibility::XAccessible> xParent = xContext->getAccessibleParent();
389             uno::Reference<accessibility::XAccessibleContext> xParentContext(xParent, uno::UNO_QUERY);
390             if (xParentContext.is()
391                     && xParentContext->getAccessibleRole() == accessibility::AccessibleRole::TABLE_CELL)
392             {
393                 uno::Reference<accessibility::XAccessible> xCellParent = xParentContext->getAccessibleParent();
394                 if (xCellParent.is())
395                 {
396                     xCurrentTable = uno::Reference<accessibility::XAccessibleTable>(xCellParent, uno::UNO_QUERY);
397                 }
398             }
399         }
400     }
401 
402     return xCurrentTable.is() && xCurrentTable == xAncestorTable;
403 }
404 
lookForParentTable(const uno::Reference<accessibility::XAccessibleContext> & xContext,uno::Reference<accessibility::XAccessibleTable> & xTable,sal_Int64 & nChildIndex)405 void lookForParentTable(const uno::Reference<accessibility::XAccessibleContext>& xContext,
406                         uno::Reference<accessibility::XAccessibleTable>& xTable,
407                         sal_Int64& nChildIndex)
408 {
409     using namespace accessibility;
410     uno::Reference<XAccessibleContext> xParentContext = getParentContext(xContext);
411     if (xParentContext.is() && xParentContext->getAccessibleRole() == AccessibleRole::TABLE_CELL)
412     {
413         uno::Reference<XAccessible> xCellParent = xParentContext->getAccessibleParent();
414         if (xCellParent.is())
415         {
416             xTable = uno::Reference<XAccessibleTable>(xCellParent, uno::UNO_QUERY);
417             if (xTable.is())
418             {
419                 nChildIndex = xParentContext->getAccessibleIndexInParent();
420             }
421         }
422     }
423 }
424 
truncateText(std::u16string_view sText,sal_Int32 nNewLength)425 OUString truncateText(std::u16string_view sText, sal_Int32 nNewLength)
426 {
427     // truncate test to given length
428     std::u16string_view sNewText = sText.substr(0, nNewLength);
429     // try to truncate at a word
430     size_t nLastPos = sNewText.rfind(u" ");
431     if (nLastPos != 0 && nLastPos != std::u16string_view::npos)
432         sNewText = sNewText.substr(0, nLastPos);
433     return OUString(sNewText);
434 }
435 
stateSetToString(::sal_Int64 stateSet)436 std::string stateSetToString(::sal_Int64 stateSet)
437 {
438     static const std::string states[35] = {
439             "ACTIVE", "ARMED", "BUSY", "CHECKED", "DEFUNC",
440             "EDITABLE", "ENABLED", "EXPANDABLE", "EXPANDED", "FOCUSABLE",
441             "FOCUSED", "HORIZONTAL", "ICONIFIED", "INDETERMINATE", "MANAGES_DESCENDANTS",
442             "MODAL", "MULTI_LINE", "MULTI_SELECTABLE", "OPAQUE", "PRESSED",
443             "RESIZABLE", "SELECTABLE", "SELECTED", "SENSITIVE", "SHOWING",
444             "SINGLE_LINE", "STALE", "TRANSIENT", "VERTICAL", "VISIBLE",
445             "MOVEABLE", "DEFAULT", "OFFSCREEN", "COLLAPSE", "CHECKABLE"
446     };
447 
448     if (stateSet == 0)
449         return "INVALID";
450     ::sal_Int64 state = 1;
451     std::string s;
452     for (int i = 0; i < 35; ++i)
453     {
454         if (stateSet & state)
455         {
456             s += states[i];
457             s += "|";
458         }
459         state <<= 1;
460     }
461     return s;
462 }
463 
aboutView(std::string msg,const void * pInstance,const SfxViewShell * pViewShell)464 void aboutView(std::string msg,  const void* pInstance, const SfxViewShell* pViewShell)
465 {
466     if (!pViewShell)
467         return;
468 
469     SAL_INFO("lok.a11y", ">>> " << msg << ": instance: " << pInstance
470             << ", VIED ID: " <<  pViewShell->GetViewShellId().get() << " <<<");
471 }
472 
aboutEvent(std::string msg,const accessibility::AccessibleEventObject & aEvent)473 void aboutEvent(std::string msg, const accessibility::AccessibleEventObject& aEvent)
474 {
475     try
476     {
477         uno::Reference< accessibility::XAccessible > xSource(aEvent.Source, uno::UNO_QUERY);
478         if (xSource.is())
479         {
480             uno::Reference< accessibility::XAccessibleContext > xContext =
481                     xSource->getAccessibleContext();
482 
483             if (xContext.is())
484             {
485                 SAL_INFO("lok.a11y", msg << ": event id: " << aEvent.EventId
486                         << "\n  xSource: " << xSource.get()
487                         << "\n  role: " << xContext->getAccessibleRole()
488                         << "\n  name: " << xContext->getAccessibleName()
489                         << "\n  index in parent: " << xContext->getAccessibleIndexInParent()
490                         << "\n  state set: " << stateSetToString(xContext->getAccessibleStateSet())
491                         << "\n  parent: " << xContext->getAccessibleParent().get()
492                         << "\n  child count: " << xContext->getAccessibleChildCount());
493             }
494             else
495             {
496                 SAL_INFO("lok.a11y", msg << ": event id: " << aEvent.EventId
497                                          << ", no accessible context!");
498             }
499         }
500         else
501         {
502             SAL_INFO("lok.a11y", msg << ": event id: " << aEvent.EventId
503                                      << ", no accessible source!");
504         }
505         uno::Reference< accessibility::XAccessible > xOldValue;
506         aEvent.OldValue >>= xOldValue;
507         if (xOldValue.is())
508         {
509             uno::Reference< accessibility::XAccessibleContext > xContext =
510                     xOldValue->getAccessibleContext();
511 
512             if (xContext.is())
513             {
514                 SAL_INFO("lok.a11y", msg << ": "
515                            "\n  xOldValue: " << xOldValue.get()
516                         << "\n  role: " << xContext->getAccessibleRole()
517                         << "\n  name: " << xContext->getAccessibleName()
518                         << "\n  index in parent: " << xContext->getAccessibleIndexInParent()
519                         << "\n  state set: " << stateSetToString(xContext->getAccessibleStateSet())
520                         << "\n  parent: " << xContext->getAccessibleParent().get()
521                         << "\n  child count: " << xContext->getAccessibleChildCount());
522             }
523         }
524         uno::Reference< accessibility::XAccessible > xNewValue;
525         aEvent.NewValue >>= xNewValue;
526         if (xNewValue.is())
527         {
528             uno::Reference< accessibility::XAccessibleContext > xContext =
529                     xNewValue->getAccessibleContext();
530 
531             if (xContext.is())
532             {
533                 SAL_INFO("lok.a11y", msg << ": "
534                            "\n  xNewValue: " << xNewValue.get()
535                         << "\n  role: " << xContext->getAccessibleRole()
536                         << "\n  name: " << xContext->getAccessibleName()
537                         << "\n  index in parent: " << xContext->getAccessibleIndexInParent()
538                         << "\n  state set: " << stateSetToString(xContext->getAccessibleStateSet())
539                         << "\n  parent: " << xContext->getAccessibleParent().get()
540                         << "\n  child count: " << xContext->getAccessibleChildCount());
541             }
542         }
543     }
544     catch( const lang::IndexOutOfBoundsException& /*e*/ )
545     {
546         LOK_WARN("lok.a11y", "Focused object has invalid index in parent");
547     }
548 }
549 
getListPrefixSize(const uno::Reference<css::accessibility::XAccessibleText> & xAccText)550 sal_Int32 getListPrefixSize(const uno::Reference<css::accessibility::XAccessibleText>& xAccText)
551 {
552     if (!xAccText.is())
553         return 0;
554 
555     OUString sText = xAccText->getText();
556     sal_Int32 nLength = sText.getLength();
557     if (nLength <= 0)
558         return 0;
559 
560     css::uno::Sequence< css::beans::PropertyValue > aRunAttributeList;
561     css::uno::Sequence< OUString > aRequestedAttributes = {UNO_NAME_NUMBERING_LEVEL, UNO_NAME_NUMBERING};
562     aRunAttributeList = xAccText->getCharacterAttributes(0, aRequestedAttributes);
563 
564     sal_Int16 nLevel = -1;
565     bool bIsCounted = false;
566     for (const auto& attribute: aRunAttributeList)
567     {
568         if (attribute.Name.isEmpty())
569             continue;
570         if (attribute.Name == UNO_NAME_NUMBERING_LEVEL)
571            attribute.Value >>= nLevel;
572         else if (attribute.Name == UNO_NAME_NUMBERING)
573            attribute.Value >>= bIsCounted;
574     }
575     if (nLevel < 0 || !bIsCounted)
576         return 0;
577 
578     css::accessibility::TextSegment aTextSegment =
579         xAccText->getTextAtIndex(0, css::accessibility::AccessibleTextType::ATTRIBUTE_RUN);
580 
581     SAL_INFO("lok.a11y", "getListPrefixSize: prefix: " << aTextSegment.SegmentText << ", level: " << nLevel);
582 
583     return aTextSegment.SegmentEnd;
584 }
585 
aboutTextFormatting(std::string msg,const uno::Reference<css::accessibility::XAccessibleText> & xAccText)586 void aboutTextFormatting(std::string msg, const uno::Reference<css::accessibility::XAccessibleText>& xAccText)
587 {
588     if (!xAccText.is())
589         return;
590 
591     OUString sText = xAccText->getText();
592     sal_Int32 nLength = sText.getLength();
593     if (nLength <= 0)
594         return;
595 
596     css::uno::Reference<css::accessibility::XAccessibleTextAttributes>
597         xAccTextAttr(xAccText, uno::UNO_QUERY);
598     css::uno::Sequence< OUString > aRequestedAttributes;
599 
600     sal_Int32 nPos = 0;
601     while (nPos < nLength)
602     {
603         css::accessibility::TextSegment aTextSegment =
604                 xAccText->getTextAtIndex(nPos, css::accessibility::AccessibleTextType::ATTRIBUTE_RUN);
605         SAL_INFO("lok.a11y", msg << ": "
606                 "text segment: '" << aTextSegment.SegmentText
607                 << "', start: " << aTextSegment.SegmentStart
608                 << ", end: " << aTextSegment.SegmentEnd);
609 
610         css::uno::Sequence< css::beans::PropertyValue > aRunAttributeList;
611         if (xAccTextAttr.is())
612         {
613             aRunAttributeList = xAccTextAttr->getRunAttributes(nPos, aRequestedAttributes);
614         }
615         else
616         {
617             aRunAttributeList = xAccText->getCharacterAttributes(nPos, aRequestedAttributes);
618         }
619 
620         sal_Int32 nSize = aRunAttributeList.getLength();
621         SAL_INFO("lok.a11y",
622                  msg << ": attribute list size: " << nSize);
623         if (nSize)
624         {
625             OUString sValue;
626             OUString sAttributes = u"{ "_ustr;
627             for (const auto& attribute: aRunAttributeList)
628             {
629                 if (attribute.Name.isEmpty())
630                     continue;
631 
632                 if (attribute.Name == "CharHeight" || attribute.Name == "CharWeight")
633                 {
634                     float fValue = 0;
635                     attribute.Value >>= fValue;
636                     sValue = OUString::number(fValue);
637                 }
638                 else if (attribute.Name == "CharPosture")
639                 {
640                     awt::FontSlant nValue = awt::FontSlant_NONE;
641                     attribute.Value >>= nValue;
642                     sValue = OUString::number(static_cast<unsigned int>(nValue));
643                 }
644                 else if (attribute.Name == "CharUnderline")
645                 {
646                     sal_Int16 nValue = 0;
647                     attribute.Value >>= nValue;
648                     sValue = OUString::number(nValue);
649                 }
650                 else if (attribute.Name == "CharFontName")
651                 {
652                     attribute.Value >>= sValue;
653                 }
654                 else if (attribute.Name == "Rsid")
655                 {
656                     sal_uInt32 nValue = 0;
657                     attribute.Value >>= nValue;
658                     sValue = OUString::number(nValue);
659                 }
660                 else if (attribute.Name == UNO_NAME_NUMBERING_LEVEL)
661                 {
662                     sal_Int16 nValue(-1);
663                     attribute.Value >>= nValue;
664                     sValue = OUString::number(nValue);
665                 }
666                 else if (attribute.Name == UNO_NAME_NUMBERING)
667                 {
668                     bool bValue(false);
669                     attribute.Value >>= bValue;
670                     sValue = OUString::boolean(bValue);
671                 }
672                 else if (attribute.Name == UNO_NAME_NUMBERING_RULES)
673                 {
674                     attribute.Value >>= sValue;
675                 }
676 
677                 if (!sValue.isEmpty())
678                 {
679                     if (sAttributes != "{ ")
680                         sAttributes += ", ";
681                     sAttributes += attribute.Name + ": " + sValue;
682                     sValue = "";
683                 }
684             }
685             sAttributes += " }";
686             SAL_INFO("lok.a11y",
687                      msg << ": " << sAttributes);
688         }
689         nPos = aTextSegment.SegmentEnd + 1;
690     }
691 }
692 
aboutParagraph(const std::string & msg,const OUString & rsParagraphContent,sal_Int32 nCaretPosition,sal_Int32 nSelectionStart,sal_Int32 nSelectionEnd,sal_Int32 nListPrefixLength,bool force=false)693 void aboutParagraph(const std::string& msg, const OUString& rsParagraphContent, sal_Int32 nCaretPosition,
694                     sal_Int32 nSelectionStart, sal_Int32 nSelectionEnd, sal_Int32 nListPrefixLength,
695                     bool force = false)
696 {
697     SAL_INFO("lok.a11y", msg << ": "
698             "\n text content: \"" << rsParagraphContent << "\""
699             "\n caret pos: " << nCaretPosition
700             << "\n selection: start: " << nSelectionStart << ", end: " << nSelectionEnd
701             << "\n list prefix length: " << nListPrefixLength
702             << "\n force: " << force
703             );
704 }
705 
aboutParagraph(const std::string & msg,const uno::Reference<css::accessibility::XAccessibleText> & xAccText,bool force=false)706 void aboutParagraph(const std::string& msg, const uno::Reference<css::accessibility::XAccessibleText>& xAccText,
707                     bool force = false)
708 {
709     if (!xAccText.is())
710         return;
711 
712     OUString sText = xAccText->getText();
713     sal_Int32 nCaretPosition = xAccText->getCaretPosition();
714     sal_Int32 nSelectionStart = xAccText->getSelectionStart();
715     sal_Int32 nSelectionEnd = xAccText->getSelectionEnd();
716     sal_Int32 nListPrefixLength = getListPrefixSize(xAccText);
717     aboutParagraph(msg, sText, nCaretPosition, nSelectionStart, nSelectionEnd, nListPrefixLength, force);
718 }
719 
aboutFocusedCellChanged(sal_Int32 nOutCount,const std::vector<TableSizeType> & aInList,sal_Int32 nRow,sal_Int32 nCol,sal_Int32 nRowSpan,sal_Int32 nColSpan)720 void aboutFocusedCellChanged(sal_Int32 nOutCount, const std::vector<TableSizeType>& aInList,
721                              sal_Int32 nRow, sal_Int32 nCol, sal_Int32 nRowSpan, sal_Int32 nColSpan)
722 {
723     std::stringstream inListStream;
724     inListStream << "[ ";
725     for (const auto& rTableSize: aInList)
726     {
727         inListStream << "{ rowCount: " << rTableSize.nRowCount << " colCount: " << rTableSize.nColCount << " } ";
728     }
729     inListStream << "]";
730 
731     SAL_INFO("lok.a11y", "LOKDocumentFocusListener::notifyFocusedCellChanged: "
732             "\n outCount: " << nOutCount
733             << "\n inList: " << inListStream.str()
734             << "\n row: " << nRow
735             << "\n column: " << nCol
736             << "\n rowSpan: " << nRowSpan
737             << "\n colSpan: " << nColSpan
738             );
739 }
740 } // anonymous namespace
741 
742 class LOKDocumentFocusListener :
743     public ::cppu::WeakImplHelper< accessibility::XAccessibleEventListener >
744 {
745     static constexpr sal_Int64 MAX_ATTACHABLE_CHILDREN = 100;
746 
747     const SfxViewShell* m_pViewShell;
748     sal_Int16 m_nDocumentType;
749     std::unordered_set<uno::Reference<uno::XInterface>> m_aRefList;
750     OUString m_sFocusedParagraph;
751     sal_Int32 m_nCaretPosition;
752     sal_Int32 m_nSelectionStart;
753     sal_Int32 m_nSelectionEnd;
754     sal_Int32 m_nListPrefixLength;
755     uno::Reference<accessibility::XAccessibleTable> m_xLastTable;
756     OUString m_sSelectedText;
757     bool m_bIsEditingCell;
758     // used for text content of a shape
759     bool m_bIsEditingInSelection;
760     OUString m_sSelectedCellAddress;
761     uno::Reference<accessibility::XAccessible> m_xSelectedObject;
762 
763 public:
764     explicit LOKDocumentFocusListener(const SfxViewShell* pViewShell);
765 
766     /// @throws lang::IndexOutOfBoundsException
767     /// @throws uno::RuntimeException
768     void attachRecursive(
769         const uno::Reference< accessibility::XAccessible >& xAccessible
770     );
771 
772     /// @throws lang::IndexOutOfBoundsException
773     /// @throws uno::RuntimeException
774     void attachRecursive(
775         const uno::Reference< accessibility::XAccessible >& xAccessible,
776         const uno::Reference< accessibility::XAccessibleContext >& xContext
777     );
778 
779     /// @throws lang::IndexOutOfBoundsException
780     /// @throws uno::RuntimeException
781     void attachRecursive(
782         const uno::Reference< accessibility::XAccessible >& xAccessible,
783         const uno::Reference< accessibility::XAccessibleContext >& xContext,
784         const sal_Int64 nStateSet
785     );
786 
787     /// @throws lang::IndexOutOfBoundsException
788     /// @throws uno::RuntimeException
789     void detachRecursive(
790         const uno::Reference< accessibility::XAccessible >& xAccessible,
791         bool bForce = false
792     );
793 
794     /// @throws lang::IndexOutOfBoundsException
795     /// @throws uno::RuntimeException
796     void detachRecursive(
797         const uno::Reference< accessibility::XAccessibleContext >& xContext,
798         bool bForce = false
799     );
800 
801     /// @throws lang::IndexOutOfBoundsException
802     /// @throws uno::RuntimeException
803     void detachRecursive(
804         const uno::Reference< accessibility::XAccessibleContext >& xContext,
805         const sal_Int64 nStateSet,
806         bool bForce = false
807     );
808 
809     /// @throws lang::IndexOutOfBoundsException
810     /// @throws uno::RuntimeException
811     static uno::Reference< accessibility::XAccessible > getAccessible(const lang::EventObject& aEvent );
812 
813     // XEventListener
814     virtual void SAL_CALL disposing( const lang::EventObject& Source ) override;
815 
816     // XAccessibleEventListener
817     virtual void SAL_CALL notifyEvent( const accessibility::AccessibleEventObject& aEvent ) override;
818 
819     void notifyEditingInSelectionState(bool bParagraph = true);
820     void notifyFocusedParagraphChanged(bool force = false);
821     void notifyCaretChanged();
822     void notifyTextSelectionChanged();
823     void notifyFocusedCellChanged(sal_Int32 nOutCount, const std::vector<TableSizeType>& aInList, sal_Int32 nRow, sal_Int32 nCol, sal_Int32 nRowSpan, sal_Int32 nColSpan);
824     void notifySelectionChanged(const uno::Reference<accessibility::XAccessible>& xAccObj, const OUString& sAction);
825 
826     OUString getFocusedParagraph() const;
827     int getCaretPosition() const;
828 
829 private:
830     void paragraphPropertiesToTree(boost::property_tree::ptree& aPayloadTree, bool force = false) const;
831     void paragraphPropertiesToJson(std::string& aPayload, bool force = false) const;
832     bool updateParagraphInfo(const uno::Reference<css::accessibility::XAccessibleText>& xAccText,
833                              bool force, const std::string& msg = "");
834     void updateAndNotifyParagraph(const uno::Reference<css::accessibility::XAccessibleText>& xAccText,
835                                   bool force, const std::string& msg = "");
836     void resetParagraphInfo();
837     void onFocusedParagraphInWriterTable(const uno::Reference<accessibility::XAccessibleTable>& xTable,
838                                          sal_Int64 nChildIndex,
839                                          const uno::Reference<accessibility::XAccessibleText>& xAccText);
840     uno::Reference< accessibility::XAccessible >
841     getSelectedObject(const accessibility::AccessibleEventObject& aEvent) const;
842     void onShapeSelectionChanged(const Reference<accessibility::XAccessible>& xSelectedObject,
843                                  const OUString& sAction);
844 };
845 
LOKDocumentFocusListener(const SfxViewShell * pViewShell)846 LOKDocumentFocusListener::LOKDocumentFocusListener(const SfxViewShell* pViewShell)
847     : m_pViewShell(pViewShell)
848     , m_nDocumentType(0)
849     , m_nCaretPosition(0)
850     , m_nSelectionStart(0)
851     , m_nSelectionEnd(0)
852     , m_nListPrefixLength(0)
853     , m_bIsEditingCell(false)
854     , m_bIsEditingInSelection(false)
855 {
856 }
857 
paragraphPropertiesToTree(boost::property_tree::ptree & aPayloadTree,bool force) const858 void LOKDocumentFocusListener::paragraphPropertiesToTree(boost::property_tree::ptree& aPayloadTree, bool force) const
859 {
860     bool bLeftToRight = m_nCaretPosition == m_nSelectionEnd;
861     aPayloadTree.put("content", m_sFocusedParagraph.toUtf8().getStr());
862     aPayloadTree.put("position", m_nCaretPosition);
863     aPayloadTree.put("start", bLeftToRight ? m_nSelectionStart : m_nSelectionEnd);
864     aPayloadTree.put("end", bLeftToRight ? m_nSelectionEnd : m_nSelectionStart);
865     if (m_nListPrefixLength > 0)
866         aPayloadTree.put("listPrefixLength", m_nListPrefixLength);
867     if (force)
868         aPayloadTree.put("force", 1);
869 }
870 
paragraphPropertiesToJson(std::string & aPayload,bool force) const871 void LOKDocumentFocusListener::paragraphPropertiesToJson(std::string& aPayload, bool force) const
872 {
873     boost::property_tree::ptree aPayloadTree;
874     paragraphPropertiesToTree(aPayloadTree, force);
875     std::stringstream aStream;
876     boost::property_tree::write_json(aStream, aPayloadTree);
877     aPayload = aStream.str();
878 }
879 
getFocusedParagraph() const880 OUString LOKDocumentFocusListener::getFocusedParagraph() const
881 {
882     aboutView("LOKDocumentFocusListener::getFocusedParagraph", this, m_pViewShell);
883     aboutParagraph("LOKDocumentFocusListener::getFocusedParagraph",
884                    m_sFocusedParagraph, m_nCaretPosition,
885                    m_nSelectionStart, m_nSelectionEnd, m_nListPrefixLength);
886 
887     std::string aPayload;
888     paragraphPropertiesToJson(aPayload);
889     OUString sRet = OUString::fromUtf8(aPayload);
890     return sRet;
891 }
892 
getCaretPosition() const893 int LOKDocumentFocusListener::getCaretPosition() const
894 {
895     aboutView("LOKDocumentFocusListener::getCaretPosition", this, m_pViewShell);
896     SAL_INFO("lok.a11y", "LOKDocumentFocusListener::getCaretPosition: " << m_nCaretPosition);
897     return m_nCaretPosition;
898 }
899 
900 // notifyEditingInSelectionState
901 // Used for notifying when editing becomes active/disabled for a shape
902 // bParagraph: should we append currently focused paragraph ?
903 // The problem is that the initially focused paragraph could not be the one user has clicked on,
904 // when there are more than a single paragraph.
905 // So in some case sending the focused paragraph could be misleading.
notifyEditingInSelectionState(bool bParagraph)906 void LOKDocumentFocusListener::notifyEditingInSelectionState(bool bParagraph)
907 {
908     aboutView("LOKDocumentFocusListener::notifyEditingInSelectionState", this, m_pViewShell);
909 
910     boost::property_tree::ptree aPayloadTree;
911     bool bIsCell = !m_sSelectedCellAddress.isEmpty();
912     aPayloadTree.put("cell", bIsCell ? 1 : 0);
913     if (bIsCell)
914     {
915         aPayloadTree.put("enabled", m_bIsEditingCell ? 1 : 0);
916         if (m_bIsEditingCell)
917         {
918             aPayloadTree.put("selection", m_sSelectedCellAddress);
919             if (bParagraph)
920                 aPayloadTree.put("paragraph", m_sFocusedParagraph);
921         }
922     }
923     else
924     {
925         aPayloadTree.put("enabled", m_bIsEditingInSelection ? 1 : 0);
926         if (m_bIsEditingInSelection && m_xSelectedObject.is())
927         {
928             uno::Reference<accessibility::XAccessibleContext> xContext = m_xSelectedObject->getAccessibleContext();
929             if (xContext.is())
930             {
931                 OUString sSelectionDescr = xContext->getAccessibleName();
932                 sSelectionDescr = sSelectionDescr.trim();
933                 aPayloadTree.put("selection", sSelectionDescr);
934                 if (bParagraph)
935                     aPayloadTree.put("paragraph", m_sFocusedParagraph);
936             }
937         }
938     }
939 
940     std::stringstream aStream;
941     boost::property_tree::write_json(aStream, aPayloadTree);
942     std::string aPayload = aStream.str();
943     if (m_pViewShell)
944     {
945         SAL_INFO("lok.a11y", "LOKDocumentFocusListener::notifyEditingInSelectionState: payload: \n" << aPayload);
946         m_pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_A11Y_EDITING_IN_SELECTION_STATE, aPayload.c_str());
947     }
948 }
949 
950 /// notifyFocusedParagraphChanged
951 //
952 //  Notify content, caret position and text selection start/end for the focused paragraph
953 //  in current view.
954 //  For focused we don't mean to be necessarily the currently focused accessibility node.
955 //  It's enough that the caret is present in the paragraph (position != -1).
956 //  In fact each view has its own accessibility node per each text paragraph.
957 //  Anyway there can be only one focused accessibility node at time.
958 //  So when text changes are performed in one view, both accessibility nodes emit
959 //  a text changed event, anyway only the accessibility node belonging to the view
960 //  where the text change has occurred is the focused one.
961 //
962 //  force: when true update the clipboard content even if client is composing.
963 //
964 //  Usually when editing on the client involves composing the clipboard area updating
965 //  is skipped until the composition is over.
966 //  On the contrary the composition would be aborted, making dictation not possible.
967 //  Anyway when the text change has been performed by another view we are in due
968 //  to update the clipboard content even if the user is in the middle of a composition.
notifyFocusedParagraphChanged(bool force)969 void LOKDocumentFocusListener::notifyFocusedParagraphChanged(bool force)
970 {
971     aboutView("LOKDocumentFocusListener::notifyFocusedParagraphChanged", this, m_pViewShell);
972     std::string aPayload;
973     paragraphPropertiesToJson(aPayload, force);
974     if (m_pViewShell)
975     {
976         aboutParagraph("LOKDocumentFocusListener::notifyFocusedParagraphChanged",
977                        m_sFocusedParagraph, m_nCaretPosition,
978                        m_nSelectionStart, m_nSelectionEnd, m_nListPrefixLength, force);
979 
980         m_pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_A11Y_FOCUS_CHANGED, aPayload.c_str());
981     }
982 }
983 
notifyCaretChanged()984 void LOKDocumentFocusListener::notifyCaretChanged()
985 {
986     aboutView("LOKDocumentFocusListener::notifyCaretChanged", this, m_pViewShell);
987     boost::property_tree::ptree aPayloadTree;
988     aPayloadTree.put("position", m_nCaretPosition);
989     std::stringstream aStream;
990     boost::property_tree::write_json(aStream, aPayloadTree);
991     std::string aPayload = aStream.str();
992     if (m_pViewShell)
993     {
994         SAL_INFO("lok.a11y", "LOKDocumentFocusListener::notifyCaretChanged: " << m_nCaretPosition);
995         m_pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_A11Y_CARET_CHANGED, aPayload.c_str());
996     }
997 }
998 
notifyTextSelectionChanged()999 void LOKDocumentFocusListener::notifyTextSelectionChanged()
1000 {
1001     aboutView("LOKDocumentFocusListener::notifyTextSelectionChanged", this, m_pViewShell);
1002     bool bLeftToRight = m_nCaretPosition == m_nSelectionEnd;
1003     boost::property_tree::ptree aPayloadTree;
1004     aPayloadTree.put("start", bLeftToRight ? m_nSelectionStart : m_nSelectionEnd);
1005     aPayloadTree.put("end", bLeftToRight ? m_nSelectionEnd : m_nSelectionStart);
1006     std::stringstream aStream;
1007     boost::property_tree::write_json(aStream, aPayloadTree);
1008     std::string aPayload = aStream.str();
1009     if (m_pViewShell)
1010     {
1011         SAL_INFO("lok.a11y",  "LOKDocumentFocusListener::notifyTextSelectionChanged: "
1012                 "start: " << m_nSelectionStart << ", end: " << m_nSelectionEnd);
1013         m_pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_A11Y_TEXT_SELECTION_CHANGED, aPayload.c_str());
1014     }
1015 }
1016 
notifyFocusedCellChanged(sal_Int32 nOutCount,const std::vector<TableSizeType> & aInList,sal_Int32 nRow,sal_Int32 nCol,sal_Int32 nRowSpan,sal_Int32 nColSpan)1017 void LOKDocumentFocusListener::notifyFocusedCellChanged(
1018         sal_Int32 nOutCount, const std::vector<TableSizeType>& aInList,
1019         sal_Int32 nRow, sal_Int32 nCol, sal_Int32 nRowSpan, sal_Int32 nColSpan)
1020 {
1021     aboutView("LOKDocumentFocusListener::notifyTablePositionChanged", this, m_pViewShell);
1022     boost::property_tree::ptree aPayloadTree;
1023     if (nOutCount > 0)
1024     {
1025         aPayloadTree.put("outCount", nOutCount);
1026     }
1027     if (!aInList.empty())
1028     {
1029         boost::property_tree::ptree aInListNode;
1030         for (const auto& rTableSize: aInList)
1031         {
1032             boost::property_tree::ptree aTableSizeNode;
1033             aTableSizeNode.put("rowCount", rTableSize.nRowCount);
1034             aTableSizeNode.put("colCount", rTableSize.nColCount);
1035 
1036             aInListNode.push_back(std::make_pair(std::string(), aTableSizeNode));
1037         }
1038         aPayloadTree.add_child("inList", aInListNode);
1039     }
1040 
1041     aPayloadTree.put("row", nRow);
1042     aPayloadTree.put("col", nCol);
1043 
1044     if (nRowSpan > 1)
1045     {
1046         aPayloadTree.put("rowSpan", nRowSpan);
1047     }
1048     if (nColSpan > 1)
1049     {
1050         aPayloadTree.put("colSpan", nColSpan);
1051     }
1052 
1053     boost::property_tree::ptree aContentNode;
1054     paragraphPropertiesToTree(aContentNode);
1055     aPayloadTree.add_child("paragraph", aContentNode);
1056 
1057     std::stringstream aStream;
1058     boost::property_tree::write_json(aStream, aPayloadTree);
1059     std::string aPayload = aStream.str();
1060     if (m_pViewShell)
1061     {
1062         aboutFocusedCellChanged(nOutCount, aInList, nRow, nCol, nRowSpan, nColSpan);
1063         aboutParagraph("LOKDocumentFocusListener::notifyFocusedCellChanged: paragraph: ",
1064                        m_sFocusedParagraph, m_nCaretPosition, m_nSelectionStart, m_nSelectionEnd,
1065                        m_nListPrefixLength, false);
1066 
1067         m_pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_A11Y_FOCUSED_CELL_CHANGED, aPayload.c_str());
1068     }
1069 }
1070 
notifySelectionChanged(const uno::Reference<accessibility::XAccessible> & xAccObj,const OUString & sAction)1071 void LOKDocumentFocusListener::notifySelectionChanged(const uno::Reference<accessibility::XAccessible>& xAccObj,
1072                                                       const OUString& sAction)
1073 {
1074     using namespace accessibility;
1075     if (!xAccObj.is())
1076         return;
1077 
1078     aboutView("LOKDocumentFocusListener::notifySelectionChanged", this, m_pViewShell);
1079     uno::Reference<XAccessibleContext> xContext = xAccObj->getAccessibleContext();
1080     if (xContext.is())
1081     {
1082         OUString sName = xContext->getAccessibleName();
1083         sName = sName.trim();
1084         if (sName == "GraphicObjectShape")
1085             sName = "Graphic";
1086 
1087         // check for text content and send it with some limitations:
1088         // no more than 10 paragraphs, no more than 1000 chars
1089         bool bIsCell = xContext->getAccessibleRole() == AccessibleRole::TABLE_CELL;
1090         OUString sTextContent;
1091         if (sAction == "create" || sAction == "add")
1092         {
1093             const sal_Int64 nMaxJoinedParagraphs = 10;
1094             const sal_Int32 nMaxTextContentLength = 1000;
1095             if (bIsCell)
1096             {
1097                 uno::Reference<XAccessibleText> xAccText(xAccObj, uno::UNO_QUERY);
1098                 if (xAccText.is())
1099                 {
1100                     sTextContent = xAccText->getText();
1101                     sal_Int32 nTextLength = sTextContent.getLength();
1102                     if (nTextLength > nMaxTextContentLength)
1103                     {
1104                         sTextContent = truncateText(sTextContent, nMaxTextContentLength);
1105                     }
1106                 }
1107             }
1108             else
1109             {
1110                 sal_Int32 nTotalTextLength = 0;
1111                 sal_Int64 nChildCount = xContext->getAccessibleChildCount();
1112                 if (nChildCount > nMaxJoinedParagraphs)
1113                     nChildCount = nMaxJoinedParagraphs;
1114                 for (sal_Int64 i = 0; i < nChildCount; ++i)
1115                 {
1116                     uno::Reference<XAccessible> xChild = xContext->getAccessibleChild(i);
1117                     uno::Reference<XAccessibleText> xAccText(xChild, uno::UNO_QUERY);
1118                     if (!xAccText.is())
1119                         continue;
1120                     OUString sText = xAccText->getText();
1121                     sal_Int32 nTextLength = sText.getLength();
1122                     if (nTextLength < 1)
1123                         continue;
1124                     if (nTotalTextLength + nTextLength < nMaxTextContentLength)
1125                     {
1126                         nTotalTextLength += nTextLength;
1127                         sTextContent += sText + " \n";
1128                     }
1129                     else
1130                     {
1131                         // truncate paragraph
1132                         sal_Int32 nNewLength = nMaxTextContentLength - nTotalTextLength;
1133                         sTextContent += truncateText(sText, nNewLength);
1134                         break;
1135                     }
1136                 }
1137             }
1138         }
1139 
1140         boost::property_tree::ptree aPayloadTree;
1141         aPayloadTree.put("cell", bIsCell ? 1 : 0);
1142         aPayloadTree.put("action", sAction);
1143         aPayloadTree.put("name", sName);
1144         if (!sTextContent.isEmpty())
1145             aPayloadTree.put("text", sTextContent);
1146         std::stringstream aStream;
1147         boost::property_tree::write_json(aStream, aPayloadTree);
1148         std::string aPayload = aStream.str();
1149         if (m_pViewShell)
1150         {
1151             SAL_INFO("lok.a11y",  "LOKDocumentFocusListener::notifySelectionChanged: "
1152                                      "action: " << sAction << ", name: " << sName);
1153             m_pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_A11Y_SELECTION_CHANGED, aPayload.c_str());
1154         }
1155     }
1156 }
1157 
disposing(const lang::EventObject & aEvent)1158 void LOKDocumentFocusListener::disposing( const lang::EventObject& aEvent )
1159 {
1160     // Unref the object here, but do not remove as listener since the object
1161     // might no longer be in a state that safely allows this.
1162     if( aEvent.Source.is() )
1163         m_aRefList.erase(aEvent.Source);
1164 
1165 }
1166 
updateParagraphInfo(const uno::Reference<css::accessibility::XAccessibleText> & xAccText,bool force,const std::string & msg)1167 bool LOKDocumentFocusListener::updateParagraphInfo(const uno::Reference<css::accessibility::XAccessibleText>& xAccText,
1168                                                    bool force, const std::string& msg)
1169 {
1170     if (!xAccText.is())
1171         return false;
1172 
1173     bool bNotify = false;
1174     // If caret is present inside the paragraph (pos != -1), it means that paragraph has focus in the current view.
1175     sal_Int32 nCaretPosition = xAccText->getCaretPosition();
1176     if (nCaretPosition >= 0)
1177     {
1178         OUString sText = xAccText->getText();
1179         m_nCaretPosition = nCaretPosition;
1180         m_nSelectionStart = xAccText->getSelectionStart();
1181         m_nSelectionEnd = xAccText->getSelectionEnd();
1182         m_nListPrefixLength = getListPrefixSize(xAccText);
1183 
1184         // Inside a text shape when there is no selection, selection-start and selection-end are
1185         // set to current caret position instead of -1. Moreover, inside a text shape pressing
1186         // delete or backspace with an empty selection really deletes text and not only the empty
1187         // selection as it occurs in a text paragraph in Writer.
1188         // So whenever selection-start == selection-end, and we are inside a shape we need
1189         // to set these parameters to -1 in order to have the client to handle delete and
1190         // backspace properly.
1191         if (m_nSelectionStart == m_nSelectionEnd && m_nSelectionStart != -1)
1192         {
1193             uno::Reference<accessibility::XAccessibleContext> xContext(xAccText, uno::UNO_QUERY);
1194             sal_Int16 nParentRole = getParentRole(xContext);
1195             if (nParentRole == accessibility::AccessibleRole::SHAPE ||
1196                 nParentRole == accessibility::AccessibleRole::TEXT_FRAME) // spreadsheet cell editing
1197                 m_nSelectionStart = m_nSelectionEnd = -1;
1198         }
1199 
1200         // In case only caret position or text selection are different we can rely on specific events.
1201         if (m_sFocusedParagraph != sText)
1202         {
1203             m_sFocusedParagraph = sText;
1204             bNotify = true;
1205         }
1206     }
1207     else
1208     {
1209         SAL_WARN("lok.a11y",
1210                  "LOKDocumentFocusListener::updateParagraphInfo: skipped since no caret is present");
1211     }
1212 
1213     std::string header = "LOKDocumentFocusListener::updateParagraphInfo";
1214     if (msg.size())
1215         header += ": " + msg;
1216     aboutParagraph(header, xAccText, force);
1217     return bNotify;
1218 
1219 }
1220 
updateAndNotifyParagraph(const uno::Reference<css::accessibility::XAccessibleText> & xAccText,bool force,const std::string & msg)1221 void LOKDocumentFocusListener::updateAndNotifyParagraph(
1222         const uno::Reference<css::accessibility::XAccessibleText>& xAccText,
1223         bool force, const std::string& msg)
1224 {
1225     if (updateParagraphInfo(xAccText, force, msg))
1226         notifyFocusedParagraphChanged(force);
1227 }
1228 
resetParagraphInfo()1229 void LOKDocumentFocusListener::resetParagraphInfo()
1230 {
1231     m_sFocusedParagraph = "";
1232     m_nCaretPosition = 0;
1233     m_nSelectionStart = -1;
1234     m_nSelectionEnd = -1;
1235     m_nListPrefixLength = 0;
1236 }
1237 
1238 // For a presentation document when an accessible event of type SELECTION_CHANGED_XXX occurs
1239 // the selected (or unselected) object is put in NewValue, instead for a text document
1240 // the selected object is put in Source.
1241 // The following function helps to retrieve the selected object independently on where it has been put.
1242 uno::Reference< accessibility::XAccessible >
getSelectedObject(const accessibility::AccessibleEventObject & aEvent) const1243 LOKDocumentFocusListener::getSelectedObject(const accessibility::AccessibleEventObject& aEvent) const
1244 {
1245     uno::Reference< accessibility::XAccessible > xSelectedObject;
1246     if (isText(m_nDocumentType))
1247     {
1248         xSelectedObject.set(aEvent.Source, uno::UNO_QUERY);
1249     }
1250     else
1251     {
1252         aEvent.NewValue >>= xSelectedObject;
1253     }
1254     return xSelectedObject;
1255 }
1256 
onShapeSelectionChanged(const uno::Reference<accessibility::XAccessible> & xSelectedObject,const OUString & sAction)1257 void LOKDocumentFocusListener::onShapeSelectionChanged(
1258     const uno::Reference<accessibility::XAccessible>& xSelectedObject,
1259     const OUString& sAction)
1260 {
1261     // when a shape is selected or unselected we could need to notify that text content editing
1262     // is no more active, that allows on the client side to prevent default input.
1263     resetParagraphInfo();
1264     if (m_bIsEditingInSelection)
1265     {
1266         m_bIsEditingInSelection = false;
1267         notifyEditingInSelectionState();
1268     }
1269     notifySelectionChanged(xSelectedObject, sAction);
1270 }
1271 
onFocusedParagraphInWriterTable(const uno::Reference<accessibility::XAccessibleTable> & xTable,sal_Int64 nChildIndex,const uno::Reference<accessibility::XAccessibleText> & xAccText)1272 void LOKDocumentFocusListener::onFocusedParagraphInWriterTable(
1273     const uno::Reference<accessibility::XAccessibleTable>& xTable,
1274     sal_Int64 nChildIndex,
1275     const uno::Reference<accessibility::XAccessibleText>& xAccText
1276 )
1277 {
1278     std::vector<TableSizeType> aInList;
1279     sal_Int32 nOutCount = 0;
1280 
1281     if (m_xLastTable.is())
1282     {
1283         if (xTable != m_xLastTable)
1284         {
1285             // do we get in one or more nested tables ?
1286             // check if xTable is a descendant of m_xLastTable
1287             XAccessibleTableList newTableAncestorList;
1288             bool isLastAncestorOfNew = getAncestorList(newTableAncestorList, xTable, m_xLastTable);
1289             bool isNewAncestorOfLast = false;
1290             if (!isLastAncestorOfNew)
1291             {
1292                 // do we get out of one or more nested tables ?
1293                 // check if m_xLastTable is a descendant of xTable
1294                 XAccessibleTableList lastTableAncestorList;
1295                 isNewAncestorOfLast = getAncestorList(lastTableAncestorList, m_xLastTable, xTable);
1296                 // we have to notify "out of table" for all  m_xLastTable ancestors up to xTable
1297                 // or the first not-a-table ancestor
1298                 nOutCount = lastTableAncestorList.size();
1299             }
1300             if (isLastAncestorOfNew || !isNewAncestorOfLast)
1301             {
1302                 // we have to notify row/col count for all xTable ancestors starting from the ancestor
1303                 // which is a child of m_xLastTable (isLastAncestorOfNew) or the first not-a-table ancestor
1304                 for (const auto& ancestor: newTableAncestorList)
1305                 {
1306                     TableSizeType aTableSize{ancestor->getAccessibleRowCount(),
1307                                               ancestor->getAccessibleColumnCount()};
1308                     aInList.push_back(aTableSize);
1309                 }
1310             }
1311         }
1312     }
1313     else
1314     {
1315         // cursor was not inside any table and gets inside one or more tables
1316         // we have to notify row/col count for all xTable ancestors starting from first not-a-table ancestor
1317         XAccessibleTableList newTableAncestorList;
1318         getAncestorList(newTableAncestorList, xTable);
1319         for (const auto& ancestor: newTableAncestorList)
1320         {
1321             TableSizeType aTableSize{ancestor->getAccessibleRowCount(),
1322                                       ancestor->getAccessibleColumnCount()};
1323             aInList.push_back(aTableSize);
1324         }
1325     }
1326 
1327     // we have to notify current row/col of xTable and related row/col span
1328     sal_Int32 nRow = xTable->getAccessibleRow(nChildIndex);
1329     sal_Int32 nCol = xTable->getAccessibleColumn(nChildIndex);
1330     sal_Int32 nRowSpan = xTable->getAccessibleRowExtentAt(nRow, nCol);
1331     sal_Int32 nColSpan = xTable->getAccessibleColumnExtentAt(nRow, nCol);
1332 
1333     m_xLastTable = xTable;
1334     updateParagraphInfo(xAccText, false, "STATE_CHANGED: FOCUSED");
1335     notifyFocusedCellChanged(nOutCount, aInList, nRow, nCol, nRowSpan, nColSpan);
1336 }
1337 
notifyEvent(const accessibility::AccessibleEventObject & aEvent)1338 void LOKDocumentFocusListener::notifyEvent(const accessibility::AccessibleEventObject& aEvent)
1339 {
1340     using namespace accessibility;
1341     aboutView("LOKDocumentFocusListener::notifyEvent", this, m_pViewShell);
1342 
1343     try
1344     {
1345         aboutEvent("LOKDocumentFocusListener::notifyEvent", aEvent);
1346 
1347         switch (aEvent.EventId)
1348         {
1349             case AccessibleEventId::STATE_CHANGED:
1350             {
1351                 // logging
1352                 sal_Int64 nState = accessibility::AccessibleStateType::INVALID;
1353                 aEvent.NewValue >>= nState;
1354                 sal_Int64 nOldState = accessibility::AccessibleStateType::INVALID;
1355                 aEvent.OldValue >>= nOldState;
1356                 SAL_INFO("lok.a11y", "LOKDocumentFocusListener::notifyEvent: STATE_CHANGED: "
1357                                         " New State: " << stateSetToString(nState)
1358                                      << ", Old State: " << stateSetToString(nOldState));
1359 
1360                 // check validity
1361                 uno::Reference< XAccessible > xAccessibleObject = getAccessible(aEvent);
1362                 if (!xAccessibleObject.is())
1363                     return;
1364                 uno::Reference<XAccessibleContext> xContext(aEvent.Source, uno::UNO_QUERY);
1365                 if (!xContext)
1366                     return;
1367 
1368                 sal_Int16 nRole = xContext->getAccessibleRole();
1369 
1370                 if (nRole == AccessibleRole::PARAGRAPH)
1371                 {
1372                     uno::Reference<XAccessibleText> xAccText(xAccessibleObject, uno::UNO_QUERY);
1373                     if (!xAccText.is())
1374                         return;
1375 
1376                     switch (nState)
1377                     {
1378                         case AccessibleStateType::ACTIVE:
1379                         {
1380                             if (!m_bIsEditingInSelection && hasToBeActiveForEditing(getParentRole(xContext)))
1381                             {
1382                                 m_bIsEditingInSelection = true;
1383                             }
1384                             break;
1385                         }
1386                         case AccessibleStateType::FOCUSED:
1387                         {
1388                             if (m_bIsEditingInSelection && m_xSelectedObject.is())
1389                             {
1390                                 updateParagraphInfo(xAccText, true, "STATE_CHANGED: FOCUSED");
1391                                 notifyEditingInSelectionState(getAccessibleSiblingCount(xContext) == 0);
1392                                 notifyFocusedParagraphChanged(true);
1393                                 // we clear selected object so when editing is over but shape is
1394                                 // still selected, the selection event is notified the same to the client
1395                                 m_xSelectedObject.clear();
1396                                 return;
1397                             }
1398                             if (isText(m_nDocumentType))
1399                             {
1400                                 // check if we are inside a table: in case notify table and current cell info
1401                                 bool isInsideTable = false;
1402                                 uno::Reference<XAccessibleTable> xTable;
1403                                 sal_Int64 nChildIndex = 0;
1404                                 lookForParentTable(xContext, xTable, nChildIndex);
1405                                 if (xTable.is())
1406                                 {
1407                                     onFocusedParagraphInWriterTable(xTable, nChildIndex, xAccText);
1408                                     isInsideTable = true;
1409                                 }
1410                                 // paragraph is not inside any table
1411                                 if (!isInsideTable)
1412                                 {
1413                                     if (m_xLastTable.is())
1414                                     {
1415                                         // we get out one or more tables
1416                                         // we have to notify "out of table" for all m_xLastTable ancestors
1417                                         // up to the first not-a-table ancestor
1418                                         XAccessibleTableList lastTableAncestorList;
1419                                         getAncestorList(lastTableAncestorList, m_xLastTable);
1420                                         sal_Int32 nOutCount = lastTableAncestorList.size();
1421                                         // no more inside a table
1422                                         m_xLastTable.clear();
1423                                         // notify
1424                                         std::vector<TableSizeType> aInList;
1425                                         updateParagraphInfo(xAccText, false, "STATE_CHANGED: FOCUSED");
1426                                         notifyFocusedCellChanged(nOutCount, aInList, -1, -1, 1, 1);
1427                                     }
1428                                     else
1429                                     {
1430                                         updateAndNotifyParagraph(xAccText, false, "STATE_CHANGED: FOCUSED");
1431                                     }
1432                                 }
1433                             }
1434                             else if (isSpreadsheet(m_nDocumentType))
1435                             {
1436                                 if (m_bIsEditingCell)
1437                                 {
1438                                     if (!hasState(aEvent, AccessibleStateType::ACTIVE))
1439                                     {
1440                                         SAL_WARN("lok.a11y",
1441                                                  "LOKDocumentFocusListener::notifyEvent: FOCUSED: "
1442                                                  "cell not ACTIVE for editing yet");
1443                                         return;
1444                                     }
1445                                     else if (m_xSelectedObject.is())
1446                                     {
1447                                         updateParagraphInfo(xAccText, true, "STATE_CHANGED: ACTIVE");
1448                                         notifyEditingInSelectionState(getAccessibleSiblingCount(xContext) == 0);
1449                                         notifyFocusedParagraphChanged(true);
1450                                         m_xSelectedObject.clear();
1451                                         return;
1452                                     }
1453 
1454                                     updateAndNotifyParagraph(xAccText, false, "STATE_CHANGED: FOCUSED");
1455                                 }
1456                             }
1457                             else if (isPresentation(m_nDocumentType))
1458                             {
1459                                 updateAndNotifyParagraph(xAccText, false, "STATE_CHANGED: FOCUSED");
1460                             }
1461                             aboutTextFormatting("LOKDocumentFocusListener::notifyEvent: STATE_CHANGED: FOCUSED", xAccText);
1462 
1463                             break;
1464                         }
1465                         default:
1466                             break;
1467                     }
1468                 }
1469                 break;
1470             }
1471             case AccessibleEventId::CARET_CHANGED:
1472             {
1473                 sal_Int32 nNewPos = -1;
1474                 aEvent.NewValue >>= nNewPos;
1475                 sal_Int32 nOldPos = -1;
1476                 aEvent.OldValue >>= nOldPos;
1477 
1478                 if (nNewPos >= 0)
1479                 {
1480                     SAL_INFO("lok.a11y", "LOKDocumentFocusListener::notifyEvent: CARET_CHANGED: "
1481                                          "new pos: " << nNewPos << ", nOldPos: " << nOldPos);
1482 
1483                     uno::Reference<XAccessibleText> xAccText(getAccessible(aEvent), uno::UNO_QUERY);
1484                     if (xAccText.is())
1485                     {
1486                         m_nCaretPosition = nNewPos;
1487                         // Let's say we are in the following case: 'Hello wor|ld',
1488                         // where '|' is the cursor position for the current view.
1489                         // Suppose that in another view it's typed <enter> soon before 'world'.
1490                         // Now the new paragraph content and caret position is: 'wor|ld'.
1491                         // Anyway no new paragraph focused event is emitted for current view.
1492                         // Only a new caret position event is emitted.
1493                         // So we could need to notify a new focused paragraph changed message.
1494                         if (!isFocused(aEvent))
1495                         {
1496                             if (updateParagraphInfo(xAccText, false, "CARET_CHANGED"))
1497                                 notifyFocusedParagraphChanged(true);
1498                         }
1499                         else
1500                         {
1501                             notifyCaretChanged();
1502                         }
1503                         aboutParagraph("LOKDocumentFocusListener::notifyEvent: CARET_CHANGED", xAccText);
1504                     }
1505                 }
1506                 break;
1507             }
1508             case AccessibleEventId::TEXT_CHANGED:
1509             {
1510                 TextSegment aDeletedText;
1511                 TextSegment aInsertedText;
1512 
1513                 if (aEvent.OldValue >>= aDeletedText)
1514                 {
1515                     SAL_INFO("lok.a11y", "LOKDocumentFocusListener::notifyEvent: TEXT_CHANGED: "
1516                                              "deleted text: >" << aDeletedText.SegmentText << "<");
1517                 }
1518                 if (aEvent.NewValue >>= aInsertedText)
1519                 {
1520                     SAL_INFO("lok.a11y", "LOKDocumentFocusListener::notifyEvent: TEXT_CHANGED: "
1521                                              "inserted text: >" << aInsertedText.SegmentText << "<");
1522                 }
1523                 uno::Reference<XAccessibleText> xAccText(getAccessible(aEvent), uno::UNO_QUERY);
1524 
1525                 // When the change has been performed in another view we need to force
1526                 // paragraph content updating on the client, even if current editing involves composing.
1527                 // We make a guess that if the paragraph accessibility node is not focused,
1528                 // it means that the text change has been performed in another view.
1529                 updateAndNotifyParagraph(xAccText, !isFocused(aEvent), "TEXT_CHANGED");
1530 
1531                 break;
1532             }
1533             case AccessibleEventId::TEXT_SELECTION_CHANGED:
1534             {
1535                 if (!isFocused(aEvent))
1536                 {
1537                     SAL_WARN("lok.a11y",
1538                              "LOKDocumentFocusListener::notifyEvent: TEXT_SELECTION_CHANGED: "
1539                              "skip non focused paragraph");
1540                     return;
1541                 }
1542 
1543                 uno::Reference<XAccessibleText> xAccText(getAccessible(aEvent), uno::UNO_QUERY);
1544                 if (xAccText.is())
1545                 {
1546                     // We send a message to client also when start/end are -1, in this way the client knows
1547                     // if a text selection object exists or not. That's needed because of the odd behavior
1548                     // occurring when <backspace>/<delete> are hit and a text selection is empty,
1549                     // but it still exists.
1550                     // Such keys delete the empty selection instead of the previous/next char.
1551                     updateParagraphInfo(xAccText, false, "TEXT_SELECTION_CHANGED");
1552 
1553                     m_sSelectedText = xAccText->getSelectedText();
1554                     SAL_INFO("lok.a11y",
1555                              "LOKDocumentFocusListener::notifyEvent: TEXT_SELECTION_CHANGED: selected text: >"
1556                               << m_sSelectedText << "<");
1557 
1558                     // Calc: when editing a formula send the update content
1559                     if (m_bIsEditingCell)
1560                     {
1561                         OUString sText = xAccText->getText();
1562                         if (!m_sSelectedCellAddress.isEmpty() &&
1563                             !m_sSelectedText.isEmpty() && sText.startsWith("="))
1564                         {
1565                             notifyFocusedParagraphChanged();
1566                         }
1567                     }
1568                     notifyTextSelectionChanged();
1569                 }
1570                 break;
1571             }
1572             case AccessibleEventId::SELECTION_CHANGED:
1573             case AccessibleEventId::SELECTION_CHANGED_REMOVE:
1574             {
1575                 uno::Reference< XAccessible > xSelectedObject = getSelectedObject(aEvent);
1576                 if (!xSelectedObject.is())
1577                     return;
1578                 uno::Reference< XAccessibleContext > xContext = xSelectedObject->getAccessibleContext();
1579                 if (!xContext.is())
1580                     return;
1581 
1582                 if (aEvent.EventId == AccessibleEventId::SELECTION_CHANGED_REMOVE)
1583                     m_xSelectedObject.clear();
1584                 else if (m_xSelectedObject.is() && m_xSelectedObject == xSelectedObject)
1585                         return; // selecting the same object; note: on editing selected object is cleared
1586                 else
1587                     m_xSelectedObject = xSelectedObject;
1588                 SAL_INFO("lok.a11y", "LOKDocumentFocusListener::notifyEvent: SELECTION_CHANGED: "
1589                                          "m_xSelectedObject.is(): " << m_xSelectedObject.is());
1590 
1591                 OUString sAction = selectionEventTypeToString(aEvent.EventId);
1592                 sal_Int16 nRole = xContext->getAccessibleRole();
1593                 switch(nRole)
1594                 {
1595                     case AccessibleRole::GRAPHIC:
1596                     case AccessibleRole::EMBEDDED_OBJECT:
1597                     case AccessibleRole::SHAPE:
1598                     {
1599                         onShapeSelectionChanged(xSelectedObject, sAction);
1600                         break;
1601                     }
1602                     case AccessibleRole::TABLE_CELL:
1603                     {
1604                         notifySelectionChanged(xSelectedObject, sAction);
1605 
1606                         if (aEvent.EventId == AccessibleEventId::SELECTION_CHANGED)
1607                         {
1608                             m_sSelectedCellAddress = xContext->getAccessibleName();
1609                             if (m_bIsEditingCell && !m_sSelectedCellAddress.isEmpty())
1610                             {
1611                                 // Check cell address: "$Sheet1.A10".
1612                                 // On cell editing SELECTION_CHANGED is not emitted when selection is expanded.
1613                                 // So selection can't be a cell range.
1614                                 sal_Int32 nDotIndex = m_sSelectedText.indexOf('.');
1615                                 OUString sCellAddress = m_sSelectedText.copy(nDotIndex + 1);
1616                                 SAL_INFO("lok.a11y", "LOKDocumentFocusListener::notifyEvent: SELECTION_CHANGED: "
1617                                                          "cell address: >" << sCellAddress << "<");
1618                                 if (m_sSelectedCellAddress == sCellAddress)
1619                                 {
1620                                     notifyFocusedParagraphChanged();
1621                                     notifyTextSelectionChanged();
1622                                 }
1623                             }
1624                         }
1625                         break;
1626                     }
1627                     default:
1628                         break;
1629                 }
1630                 break;
1631             }
1632             case AccessibleEventId::CHILD:
1633             {
1634                 uno::Reference< accessibility::XAccessible > xChild;
1635                 if( (aEvent.OldValue >>= xChild) && xChild.is() )
1636                     detachRecursive(xChild);
1637 
1638                 if( (aEvent.NewValue >>= xChild) && xChild.is() )
1639                     attachRecursive(xChild);
1640 
1641                 break;
1642             }
1643             case AccessibleEventId::INVALIDATE_ALL_CHILDREN:
1644             {
1645                 SAL_INFO("lok.a11y", "Invalidate all children called");
1646                 break;
1647             }
1648             default:
1649                 break;
1650         }
1651     }
1652     catch( const lang::IndexOutOfBoundsException& )
1653     {
1654         LOK_WARN("lok.a11y",
1655                  "LOKDocumentFocusListener::notifyEvent:Focused object has invalid index in parent");
1656     }
1657 }
1658 
getAccessible(const lang::EventObject & aEvent)1659 uno::Reference< accessibility::XAccessible > LOKDocumentFocusListener::getAccessible(const lang::EventObject& aEvent )
1660 {
1661     uno::Reference< accessibility::XAccessible > xAccessible(aEvent.Source, uno::UNO_QUERY);
1662 
1663     if( xAccessible.is() )
1664         return xAccessible;
1665 
1666     SAL_WARN("lok.a11y",
1667              "LOKDocumentFocusListener::getAccessible: Event source doesn't implement XAccessible.");
1668 
1669     uno::Reference< accessibility::XAccessibleContext > xContext(aEvent.Source, uno::UNO_QUERY);
1670 
1671     if( xContext.is() )
1672     {
1673         uno::Reference< accessibility::XAccessible > xParent( xContext->getAccessibleParent() );
1674         if( xParent.is() )
1675         {
1676             uno::Reference< accessibility::XAccessibleContext > xParentContext( xParent->getAccessibleContext() );
1677             if( xParentContext.is() )
1678             {
1679                 return xParentContext->getAccessibleChild( xContext->getAccessibleIndexInParent() );
1680             }
1681         }
1682     }
1683 
1684     LOK_WARN("lok.a11y",
1685              "LOKDocumentFocusListener::getAccessible: Can't get any accessible object from event source.");
1686 
1687     return uno::Reference< accessibility::XAccessible >();
1688 }
1689 
attachRecursive(const uno::Reference<accessibility::XAccessible> & xAccessible)1690 void LOKDocumentFocusListener::attachRecursive(
1691     const uno::Reference< accessibility::XAccessible >& xAccessible
1692 )
1693 {
1694     LOK_INFO("lok.a11y", "LOKDocumentFocusListener::attachRecursive(1): xAccessible: " << xAccessible.get());
1695 
1696     uno::Reference< accessibility::XAccessibleContext > xContext =
1697         xAccessible->getAccessibleContext();
1698 
1699     if( xContext.is() )
1700         attachRecursive(xAccessible, xContext);
1701 }
1702 
attachRecursive(const uno::Reference<accessibility::XAccessible> & xAccessible,const uno::Reference<accessibility::XAccessibleContext> & xContext)1703 void LOKDocumentFocusListener::attachRecursive(
1704     const uno::Reference< accessibility::XAccessible >& xAccessible,
1705     const uno::Reference< accessibility::XAccessibleContext >& xContext
1706 )
1707 {
1708     try
1709     {
1710         LOK_INFO("lok.a11y", "LOKDocumentFocusListener::attachRecursive(2): xAccessible: "
1711                                  << xAccessible.get() << ", role: " << xContext->getAccessibleRole()
1712                                  << ", name: " << xContext->getAccessibleName()
1713                                  << ", parent: " << xContext->getAccessibleParent().get()
1714                                  << ", child count: " << xContext->getAccessibleChildCount());
1715 
1716         sal_Int64 nStateSet = xContext->getAccessibleStateSet();
1717 
1718         if (!m_bIsEditingCell)
1719         {
1720             ::rtl::OUString sName = xContext->getAccessibleName();
1721             m_bIsEditingCell = sName.startsWith("Cell");
1722         }
1723 
1724         attachRecursive(xAccessible, xContext, nStateSet);
1725     }
1726     catch (const uno::Exception& e)
1727     {
1728         LOK_WARN("lok.a11y", "LOKDocumentFocusListener::attachRecursive(2): raised exception: " << e.Message);
1729     }
1730 }
1731 
attachRecursive(const uno::Reference<accessibility::XAccessible> & xAccessible,const uno::Reference<accessibility::XAccessibleContext> & xContext,const sal_Int64 nStateSet)1732 void LOKDocumentFocusListener::attachRecursive(
1733     const uno::Reference< accessibility::XAccessible >& xAccessible,
1734     const uno::Reference< accessibility::XAccessibleContext >& xContext,
1735     const sal_Int64 nStateSet
1736 )
1737 {
1738     aboutView("LOKDocumentFocusListener::attachRecursive (3)", this, m_pViewShell);
1739     SAL_INFO("lok.a11y", "LOKDocumentFocusListener::attachRecursive(3) #1: this: " << this
1740             << ", xAccessible: " << xAccessible.get()
1741             << ", role: " << xContext->getAccessibleRole()
1742             << ", name: " << xContext->getAccessibleName()
1743             << ", index in parent: " << xContext->getAccessibleIndexInParent()
1744             << ", state: " << stateSetToString(nStateSet)
1745             << ", parent: " << xContext->getAccessibleParent().get()
1746             << ", child count: " << xContext->getAccessibleChildCount());
1747 
1748     uno::Reference< accessibility::XAccessibleEventBroadcaster > xBroadcaster(xContext, uno::UNO_QUERY);
1749 
1750     if (!xBroadcaster.is())
1751         return;
1752     SAL_INFO("lok.a11y", "LOKDocumentFocusListener::attachRecursive(3) #2: xBroadcaster.is()");
1753     // If not already done, add the broadcaster to the list and attach as listener.
1754     const uno::Reference< uno::XInterface >& xInterface = xBroadcaster;
1755     if( m_aRefList.insert(xInterface).second )
1756     {
1757         SAL_INFO("lok.a11y", "LOKDocumentFocusListener::attachRecursive(3) #3: m_aRefList.insert(xInterface).second");
1758         xBroadcaster->addAccessibleEventListener(static_cast< accessibility::XAccessibleEventListener *>(this));
1759 
1760         if (isDocument(xContext->getAccessibleRole()))
1761         {
1762             m_nDocumentType = xContext->getAccessibleRole();
1763         }
1764 
1765         if (!(nStateSet & accessibility::AccessibleStateType::MANAGES_DESCENDANTS))
1766         {
1767             if ((nStateSet & accessibility::AccessibleStateType::SELECTED) &&
1768                 selectionHasToBeNotified(xContext))
1769             {
1770                 uno::Reference< accessibility::XAccessible > xAccObj(xContext, uno::UNO_QUERY);
1771                 onShapeSelectionChanged(xAccObj, u"create"_ustr);
1772             }
1773 
1774             sal_Int64 nmax = xContext->getAccessibleChildCount();
1775             if( nmax > MAX_ATTACHABLE_CHILDREN )
1776                 nmax = MAX_ATTACHABLE_CHILDREN;
1777 
1778             for( sal_Int64 n = 0; n < nmax; n++ )
1779             {
1780                 uno::Reference< accessibility::XAccessible > xChild( xContext->getAccessibleChild( n ) );
1781 
1782                 if( xChild.is() )
1783                     attachRecursive(xChild);
1784             }
1785         }
1786         else
1787         {
1788             // Usually, when the document is loaded, a CARET_CHANGED accessibility event is automatically emitted
1789             // for the first paragraph. That allows to notify the paragraph content to the client, even if no input
1790             // event occurred yet. However, when switching to a11y enabled in the client and in Cypress tests
1791             // no accessibility event is automatically emitted until some input event occurs.
1792             // So we use the following workaround to notify the content of the focused paragraph,
1793             // without waiting for an input event.
1794             // Here we update the paragraph info related to the focused paragraph,
1795             // later when afterCallbackRegistered is executed we notify the paragraph content.
1796             sal_Int64 nChildCount = xContext->getAccessibleChildCount();
1797             if (nChildCount > 0 && nChildCount < 10)
1798             {
1799                 for (sal_Int64 n = 0; n < nChildCount; ++n)
1800                 {
1801                     uno::Reference< accessibility::XAccessible > xChild(xContext->getAccessibleChild(n));
1802                     if (xChild.is())
1803                     {
1804                         uno::Reference<css::accessibility::XAccessibleText> xAccText(xChild, uno::UNO_QUERY);
1805                         if (xAccText.is())
1806                         {
1807                             sal_Int32 nPos = xAccText->getCaretPosition();
1808                             if (nPos >= 0)
1809                             {
1810                                 attachRecursive(xChild);
1811                                 updateParagraphInfo(xAccText, false, "LOKDocumentFocusListener::attachRecursive(3)");
1812                                 break;
1813                             }
1814                         }
1815                     }
1816                 }
1817             }
1818         }
1819     }
1820 }
1821 
detachRecursive(const uno::Reference<accessibility::XAccessible> & xAccessible,bool bForce)1822 void LOKDocumentFocusListener::detachRecursive(
1823     const uno::Reference< accessibility::XAccessible >& xAccessible,
1824     bool bForce
1825 )
1826 {
1827     uno::Reference< accessibility::XAccessibleContext > xContext =
1828         xAccessible->getAccessibleContext();
1829 
1830     if( xContext.is() )
1831         detachRecursive(xContext, bForce);
1832 }
1833 
detachRecursive(const uno::Reference<accessibility::XAccessibleContext> & xContext,bool bForce)1834 void LOKDocumentFocusListener::detachRecursive(
1835     const uno::Reference< accessibility::XAccessibleContext >& xContext,
1836     bool bForce
1837 )
1838 {
1839     aboutView("LOKDocumentFocusListener::detachRecursive (2)", this, m_pViewShell);
1840     sal_Int64 nStateSet = xContext->getAccessibleStateSet();
1841 
1842     SAL_INFO("lok.a11y", "LOKDocumentFocusListener::detachRecursive(2): this: " << this
1843             << ", name: " << xContext->getAccessibleName()
1844             << ", parent: " << xContext->getAccessibleParent().get()
1845             << ", child count: " << xContext->getAccessibleChildCount());
1846 
1847     if (m_bIsEditingCell)
1848     {
1849         ::rtl::OUString sName = xContext->getAccessibleName();
1850         m_bIsEditingCell = !sName.startsWith("Cell");
1851         if (!m_bIsEditingCell)
1852         {
1853             m_sFocusedParagraph = "";
1854             m_nCaretPosition = 0;
1855             notifyFocusedParagraphChanged();
1856         }
1857     }
1858 
1859     detachRecursive(xContext, nStateSet, bForce);
1860 }
1861 
detachRecursive(const uno::Reference<accessibility::XAccessibleContext> & xContext,const sal_Int64 nStateSet,bool bForce)1862 void LOKDocumentFocusListener::detachRecursive(
1863     const uno::Reference< accessibility::XAccessibleContext >& xContext,
1864     const sal_Int64 nStateSet,
1865     bool bForce
1866 )
1867 {
1868     uno::Reference< accessibility::XAccessibleEventBroadcaster > xBroadcaster(xContext, uno::UNO_QUERY);
1869 
1870     if (xBroadcaster.is() && 0 < m_aRefList.erase(xBroadcaster))
1871     {
1872         xBroadcaster->removeAccessibleEventListener(static_cast< accessibility::XAccessibleEventListener *>(this));
1873 
1874         if ((nStateSet & accessibility::AccessibleStateType::SELECTED) &&
1875             selectionHasToBeNotified(xContext))
1876         {
1877             uno::Reference< accessibility::XAccessible > xAccObj(xContext, uno::UNO_QUERY);
1878             onShapeSelectionChanged(xAccObj, u"delete"_ustr);
1879         }
1880 
1881         if (bForce || !(nStateSet & accessibility::AccessibleStateType::MANAGES_DESCENDANTS))
1882         {
1883             sal_Int64 nmax = xContext->getAccessibleChildCount();
1884             if (nmax > MAX_ATTACHABLE_CHILDREN)
1885                 nmax = MAX_ATTACHABLE_CHILDREN;
1886             for (sal_Int64 n = 0; n < nmax; n++)
1887             {
1888                 uno::Reference< accessibility::XAccessible > xChild(xContext->getAccessibleChild(n));
1889 
1890                 if (xChild.is())
1891                     detachRecursive(xChild);
1892             }
1893         }
1894     }
1895 }
1896 
1897 sal_uInt32 SfxViewShell_Impl::m_nLastViewShellId = 0;
1898 
SfxViewShell_Impl(SfxViewShellFlags const nFlags,ViewShellDocId nDocId)1899 SfxViewShell_Impl::SfxViewShell_Impl(SfxViewShellFlags const nFlags, ViewShellDocId nDocId)
1900 :   m_bHasPrintOptions(nFlags & SfxViewShellFlags::HAS_PRINTOPTIONS)
1901 ,   m_nFamily(0xFFFF)   // undefined, default set by TemplateDialog
1902 ,   m_pLibreOfficeKitViewCallback(nullptr)
1903 ,   m_bTiledSearching(false)
1904 ,   m_nViewShellId(SfxViewShell_Impl::m_nLastViewShellId++)
1905 ,   m_nDocId(nDocId)
1906 {
1907 }
1908 
~SfxViewShell_Impl()1909 SfxViewShell_Impl::~SfxViewShell_Impl()
1910 {
1911 }
1912 
GetIPClients_Impl()1913 std::vector< SfxInPlaceClient* >& SfxViewShell_Impl::GetIPClients_Impl()
1914 {
1915     return maIPClients;
1916 }
1917 
SFX_IMPL_SUPERCLASS_INTERFACE(SfxViewShell,SfxShell)1918 SFX_IMPL_SUPERCLASS_INTERFACE(SfxViewShell,SfxShell)
1919 
1920 void SfxViewShell::InitInterface_Impl()
1921 {
1922 }
1923 
1924 
1925 /** search for a filter name dependent on type and module
1926  */
impl_retrieveFilterNameFromTypeAndModule(const css::uno::Reference<css::container::XContainerQuery> & rContainerQuery,const OUString & rType,const OUString & rModuleIdentifier,const sal_Int32 nFlags)1927 static OUString impl_retrieveFilterNameFromTypeAndModule(
1928     const css::uno::Reference< css::container::XContainerQuery >& rContainerQuery,
1929     const OUString& rType,
1930     const OUString& rModuleIdentifier,
1931     const sal_Int32 nFlags )
1932 {
1933     // Retrieve filter from type
1934     css::uno::Sequence< css::beans::NamedValue > aQuery {
1935         { u"Type"_ustr, css::uno::Any( rType ) },
1936         { u"DocumentService"_ustr, css::uno::Any( rModuleIdentifier ) }
1937     };
1938 
1939     css::uno::Reference< css::container::XEnumeration > xEnumeration =
1940         rContainerQuery->createSubSetEnumerationByProperties( aQuery );
1941 
1942     OUString aFoundFilterName;
1943     while ( xEnumeration->hasMoreElements() )
1944     {
1945         ::comphelper::SequenceAsHashMap aFilterPropsHM( xEnumeration->nextElement() );
1946         sal_Int32 nFilterFlags = aFilterPropsHM.getUnpackedValueOrDefault(
1947             u"Flags"_ustr,
1948             sal_Int32( 0 ) );
1949 
1950         if ( nFilterFlags & nFlags )
1951         {
1952             aFoundFilterName = aFilterPropsHM.getUnpackedValueOrDefault(u"Name"_ustr, OUString());
1953             break;
1954         }
1955     }
1956 
1957     return aFoundFilterName;
1958 }
1959 
1960 namespace {
1961 
1962 /** search for an internal typename, which map to the current app module
1963     and map also to a "family" of file formats as e.g. PDF/MS Doc/OOo Doc.
1964  */
1965 enum ETypeFamily
1966 {
1967     E_MS_DOC,
1968     E_OOO_DOC
1969 };
1970 
1971 }
1972 
impl_searchFormatTypeForApp(const css::uno::Reference<css::frame::XFrame> & xFrame,ETypeFamily eTypeFamily)1973 static OUString impl_searchFormatTypeForApp(const css::uno::Reference< css::frame::XFrame >& xFrame     ,
1974                                                   ETypeFamily                                eTypeFamily)
1975 {
1976     try
1977     {
1978         const css::uno::Reference< css::uno::XComponentContext >&  xContext      (::comphelper::getProcessComponentContext());
1979         css::uno::Reference< css::frame::XModuleManager2 >  xModuleManager(css::frame::ModuleManager::create(xContext));
1980 
1981         OUString sModule = xModuleManager->identify(xFrame);
1982         OUString sType   ;
1983 
1984         switch(eTypeFamily)
1985         {
1986             case E_MS_DOC:
1987             {
1988                 if ( sModule == "com.sun.star.text.TextDocument" )
1989                     sType = "writer_MS_Word_2007";
1990                 else
1991                 if ( sModule == "com.sun.star.sheet.SpreadsheetDocument" )
1992                     sType = "MS Excel 2007 XML";
1993                 else
1994                 if ( sModule == "com.sun.star.presentation.PresentationDocument" )
1995                     sType = "MS PowerPoint 2007 XML";
1996             }
1997             break;
1998 
1999             case E_OOO_DOC:
2000             {
2001                 if ( sModule == "com.sun.star.text.TextDocument" )
2002                     sType = "writer8";
2003                 else
2004                 if ( sModule == "com.sun.star.sheet.SpreadsheetDocument" )
2005                     sType = "calc8";
2006                 else
2007                 if ( sModule == "com.sun.star.drawing.DrawingDocument" )
2008                     sType = "draw8";
2009                 else
2010                 if ( sModule == "com.sun.star.presentation.PresentationDocument" )
2011                     sType = "impress8";
2012             }
2013             break;
2014         }
2015 
2016         return sType;
2017     }
2018     catch (const css::uno::RuntimeException&)
2019     {
2020         throw;
2021     }
2022     catch (const css::uno::Exception&)
2023     {
2024     }
2025 
2026     return OUString();
2027 }
2028 
NewIPClient_Impl(SfxInPlaceClient * pIPClient)2029 void SfxViewShell::NewIPClient_Impl( SfxInPlaceClient *pIPClient )
2030 {
2031     pImpl->GetIPClients_Impl().push_back(pIPClient);
2032 }
2033 
IPClientGone_Impl(SfxInPlaceClient const * pIPClient)2034 void SfxViewShell::IPClientGone_Impl( SfxInPlaceClient const *pIPClient )
2035 {
2036     std::vector< SfxInPlaceClient* >& pClients = pImpl->GetIPClients_Impl();
2037 
2038     auto it = std::find(pClients.begin(), pClients.end(), pIPClient);
2039     if (it != pClients.end())
2040         pClients.erase( it );
2041 }
2042 
2043 
ExecMisc_Impl(SfxRequest & rReq)2044 void SfxViewShell::ExecMisc_Impl( SfxRequest &rReq )
2045 {
2046     const sal_uInt16 nId = rReq.GetSlot();
2047     switch( nId )
2048     {
2049         case SID_STYLE_FAMILY :
2050         {
2051             const SfxUInt16Item* pItem = rReq.GetArg<SfxUInt16Item>(nId);
2052             if (pItem)
2053             {
2054                 pImpl->m_nFamily = pItem->GetValue();
2055             }
2056             break;
2057         }
2058         case SID_ACTIVATE_STYLE_APPLY:
2059         {
2060             uno::Reference< frame::XFrame > xFrame =
2061                 GetViewFrame().GetFrame().GetFrameInterface();
2062 
2063             Reference< beans::XPropertySet > xPropSet( xFrame, UNO_QUERY );
2064             Reference< frame::XLayoutManager > xLayoutManager;
2065             if ( xPropSet.is() )
2066             {
2067                 try
2068                 {
2069                     Any aValue = xPropSet->getPropertyValue(u"LayoutManager"_ustr);
2070                     aValue >>= xLayoutManager;
2071                     if ( xLayoutManager.is() )
2072                     {
2073                         uno::Reference< ui::XUIElement > xElement = xLayoutManager->getElement( u"private:resource/toolbar/textobjectbar"_ustr );
2074                         if(!xElement.is())
2075                         {
2076                             xElement = xLayoutManager->getElement( u"private:resource/toolbar/frameobjectbar"_ustr );
2077                         }
2078                         if(!xElement.is())
2079                         {
2080                             xElement = xLayoutManager->getElement( u"private:resource/toolbar/oleobjectbar"_ustr );
2081                         }
2082                         if(xElement.is())
2083                         {
2084                             uno::Reference< awt::XWindow > xWin( xElement->getRealInterface(), uno::UNO_QUERY_THROW );
2085                             VclPtr<vcl::Window> pWin = VCLUnoHelper::GetWindow( xWin );
2086                             ToolBox* pTextToolbox = dynamic_cast< ToolBox* >( pWin.get() );
2087                             if( pTextToolbox )
2088                             {
2089                                 ToolBox::ImplToolItems::size_type nItemCount = pTextToolbox->GetItemCount();
2090                                 for( ToolBox::ImplToolItems::size_type nItem = 0; nItem < nItemCount; ++nItem )
2091                                 {
2092                                     ToolBoxItemId nItemId = pTextToolbox->GetItemId( nItem );
2093                                     const OUString aCommand = pTextToolbox->GetItemCommand( nItemId );
2094                                     if (aCommand == ".uno:StyleApply")
2095                                     {
2096                                         vcl::Window* pItemWin = pTextToolbox->GetItemWindow( nItemId );
2097                                         if( pItemWin )
2098                                             pItemWin->GrabFocus();
2099                                         break;
2100                                     }
2101                                 }
2102                             }
2103                         }
2104                     }
2105                 }
2106                 catch (const Exception&)
2107                 {
2108                 }
2109             }
2110             rReq.Done();
2111         }
2112         break;
2113 
2114         case SID_MAIL_SENDDOCASMS:
2115         case SID_MAIL_SENDDOCASOOO:
2116         case SID_MAIL_SENDDOCASPDF:
2117         case SID_MAIL_SENDDOC:
2118         case SID_MAIL_SENDDOCASFORMAT:
2119         {
2120             SfxObjectShell* pDoc = GetObjectShell();
2121             if (!pDoc)
2122                 break;
2123             pDoc->QueryHiddenInformation(HiddenWarningFact::WhenSaving);
2124             SfxMailModel  aModel;
2125             OUString aDocType;
2126 
2127             const SfxStringItem* pMailRecipient = rReq.GetArg<SfxStringItem>(SID_MAIL_RECIPIENT);
2128             if ( pMailRecipient )
2129             {
2130                 OUString aRecipient( pMailRecipient->GetValue() );
2131                 OUString aMailToStr(u"mailto:"_ustr);
2132 
2133                 if ( aRecipient.startsWith( aMailToStr ) )
2134                     aRecipient = aRecipient.copy( aMailToStr.getLength() );
2135                 aModel.AddToAddress( aRecipient );
2136             }
2137             const SfxStringItem* pMailDocType = rReq.GetArg<SfxStringItem>(SID_TYPE_NAME);
2138             if ( pMailDocType )
2139                 aDocType = pMailDocType->GetValue();
2140 
2141             uno::Reference < frame::XFrame > xFrame( rFrame.GetFrame().GetFrameInterface() );
2142             SfxMailModel::SendMailResult eResult = SfxMailModel::SEND_MAIL_ERROR;
2143 
2144             if ( nId == SID_MAIL_SENDDOC )
2145                 eResult = aModel.SaveAndSend( xFrame, OUString() );
2146             else if ( nId == SID_MAIL_SENDDOCASPDF )
2147                 eResult = aModel.SaveAndSend( xFrame, u"pdf_Portable_Document_Format"_ustr);
2148             else if ( nId == SID_MAIL_SENDDOCASMS )
2149             {
2150                 aDocType = impl_searchFormatTypeForApp(xFrame, E_MS_DOC);
2151                 if (!aDocType.isEmpty())
2152                     eResult = aModel.SaveAndSend( xFrame, aDocType );
2153             }
2154             else if ( nId == SID_MAIL_SENDDOCASOOO )
2155             {
2156                 aDocType = impl_searchFormatTypeForApp(xFrame, E_OOO_DOC);
2157                 if (!aDocType.isEmpty())
2158                     eResult = aModel.SaveAndSend( xFrame, aDocType );
2159             }
2160 
2161             if ( eResult == SfxMailModel::SEND_MAIL_ERROR )
2162             {
2163                 weld::Window* pWin = SfxGetpApp()->GetTopWindow();
2164                 std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(pWin,
2165                                                                          VclMessageType::Info, VclButtonsType::Ok,
2166                                                                          SfxResId(STR_ERROR_SEND_MAIL)));
2167                 xBox->run();
2168                 rReq.Ignore();
2169             }
2170             else
2171                 rReq.Done();
2172         }
2173         break;
2174 
2175         case SID_BLUETOOTH_SENDDOC:
2176         {
2177             SfxBluetoothModel aModel;
2178             SfxObjectShell* pDoc = GetObjectShell();
2179             if (!pDoc)
2180                 break;
2181             pDoc->QueryHiddenInformation(HiddenWarningFact::WhenSaving);
2182             uno::Reference < frame::XFrame > xFrame( rFrame.GetFrame().GetFrameInterface() );
2183             SfxMailModel::SendMailResult eResult = aModel.SaveAndSend( xFrame );
2184             if( eResult == SfxMailModel::SEND_MAIL_ERROR )
2185             {
2186                 weld::Window* pWin = SfxGetpApp()->GetTopWindow();
2187                 std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(pWin,
2188                                                                          VclMessageType::Info, VclButtonsType::Ok,
2189                                                                          SfxResId(STR_ERROR_SEND_MAIL)));
2190                 xBox->run();
2191                 rReq.Ignore();
2192             }
2193             else
2194                 rReq.Done();
2195         }
2196         break;
2197 
2198         // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2199         case SID_WEBHTML:
2200         {
2201             css::uno::Reference< lang::XMultiServiceFactory > xSMGR(::comphelper::getProcessServiceFactory(), css::uno::UNO_SET_THROW);
2202             css::uno::Reference< uno::XComponentContext >     xContext(::comphelper::getProcessComponentContext(), css::uno::UNO_SET_THROW);
2203             css::uno::Reference< css::frame::XFrame >         xFrame( rFrame.GetFrame().GetFrameInterface() );
2204             css::uno::Reference< css::frame::XModel >         xModel;
2205 
2206             css::uno::Reference< css::frame::XModuleManager2 > xModuleManager( css::frame::ModuleManager::create(xContext) );
2207 
2208             OUString aModule;
2209             try
2210             {
2211                 aModule = xModuleManager->identify( xFrame );
2212             }
2213             catch (const css::uno::RuntimeException&)
2214             {
2215                 throw;
2216             }
2217             catch (const css::uno::Exception&)
2218             {
2219             }
2220 
2221             if ( xFrame.is() )
2222             {
2223                 css::uno::Reference< css::frame::XController > xController = xFrame->getController();
2224                 if ( xController.is() )
2225                     xModel = xController->getModel();
2226             }
2227 
2228             // We need at least a valid module name and model reference
2229             css::uno::Reference< css::frame::XStorable > xStorable( xModel, css::uno::UNO_QUERY );
2230             if ( xModel.is() && xStorable.is() )
2231             {
2232                 OUString aFilterName;
2233                 OUString aTypeName( u"generic_HTML"_ustr );
2234                 OUString aFileName;
2235 
2236                 OUString aLocation = xStorable->getLocation();
2237                 INetURLObject aFileObj( aLocation );
2238 
2239                 bool bPrivateProtocol = ( aFileObj.GetProtocol() == INetProtocol::PrivSoffice );
2240                 bool bHasLocation = !aLocation.isEmpty() && !bPrivateProtocol;
2241 
2242                 css::uno::Reference< css::container::XContainerQuery > xContainerQuery(
2243                     xSMGR->createInstance( u"com.sun.star.document.FilterFactory"_ustr ),
2244                     css::uno::UNO_QUERY_THROW );
2245 
2246                 // Retrieve filter from type
2247 
2248                 sal_Int32 nFilterFlags = 0x00000002; // export
2249                 aFilterName = impl_retrieveFilterNameFromTypeAndModule( xContainerQuery, aTypeName, aModule, nFilterFlags );
2250                 if ( aFilterName.isEmpty() )
2251                 {
2252                     // Draw/Impress uses a different type. 2nd chance try to use alternative type name
2253                     aFilterName = impl_retrieveFilterNameFromTypeAndModule(
2254                         xContainerQuery, u"graphic_HTML"_ustr, aModule, nFilterFlags );
2255                 }
2256 
2257                 // No filter found => error
2258                 // No type and no location => error
2259                 if ( aFilterName.isEmpty() ||  aTypeName.isEmpty())
2260                 {
2261                     rReq.Done();
2262                     return;
2263                 }
2264 
2265                 // Use provided save file name. If empty determine file name
2266                 if ( !bHasLocation )
2267                 {
2268                     // Create a default file name with the correct extension
2269                     aFileName = "webpreview";
2270                 }
2271                 else
2272                 {
2273                     // Determine file name from model
2274                     INetURLObject aFObj( xStorable->getLocation() );
2275                     aFileName = aFObj.getName( INetURLObject::LAST_SEGMENT, true, INetURLObject::DecodeMechanism::NONE );
2276                 }
2277 
2278                 OSL_ASSERT( !aFilterName.isEmpty() );
2279                 OSL_ASSERT( !aFileName.isEmpty() );
2280 
2281                 // Creates a temporary directory to store our predefined file into it (for the
2282                 // flatpak case, create it in XDG_CACHE_HOME instead of /tmp for technical reasons,
2283                 // so that it can be accessed by the browser running outside the sandbox):
2284                 OUString * parent = nullptr;
2285                 if (flatpak::isFlatpak() && !flatpak::createTemporaryHtmlDirectory(&parent))
2286                 {
2287                     SAL_WARN("sfx.view", "cannot create Flatpak html temp dir");
2288                 }
2289 
2290                 INetURLObject aFilePathObj( ::utl::CreateTempURL(parent, true) );
2291                 aFilePathObj.insertName( aFileName );
2292                 aFilePathObj.setExtension( u"htm" );
2293 
2294                 OUString aFileURL = aFilePathObj.GetMainURL( INetURLObject::DecodeMechanism::NONE );
2295 
2296                 css::uno::Sequence< css::beans::PropertyValue > aArgs{
2297                     comphelper::makePropertyValue(u"FilterName"_ustr, aFilterName)
2298                 };
2299 
2300                 // Store document in the html format
2301                 try
2302                 {
2303                     xStorable->storeToURL( aFileURL, aArgs );
2304                 }
2305                 catch (const io::IOException&)
2306                 {
2307                     rReq.Done();
2308                     return;
2309                 }
2310 
2311                 sfx2::openUriExternally(aFileURL, true, rReq.GetFrameWeld());
2312                 rReq.Done(true);
2313                 break;
2314             }
2315             else
2316             {
2317                 rReq.Done();
2318                 return;
2319             }
2320         }
2321     }
2322 }
2323 
2324 
GetState_Impl(SfxItemSet & rSet)2325 void SfxViewShell::GetState_Impl( SfxItemSet &rSet )
2326 {
2327 
2328     SfxWhichIter aIter( rSet );
2329     SfxObjectShell *pSh = GetViewFrame().GetObjectShell();
2330     for ( sal_uInt16 nSID = aIter.FirstWhich(); nSID; nSID = aIter.NextWhich() )
2331     {
2332         switch ( nSID )
2333         {
2334 
2335             case SID_BLUETOOTH_SENDDOC:
2336             case SID_MAIL_SENDDOC:
2337             case SID_MAIL_SENDDOCASFORMAT:
2338             case SID_MAIL_SENDDOCASMS:
2339             case SID_MAIL_SENDDOCASOOO:
2340             case SID_MAIL_SENDDOCASPDF:
2341             {
2342 #if HAVE_FEATURE_MACOSX_SANDBOX
2343                 rSet.DisableItem(nSID);
2344 #endif
2345                 if (pSh && pSh->isExportLocked())
2346                     rSet.DisableItem(nSID);
2347                 break;
2348             }
2349             case SID_WEBHTML:
2350             {
2351                 if (pSh && pSh->isExportLocked())
2352                     rSet.DisableItem(nSID);
2353                 break;
2354             }
2355             // Printer functions
2356             case SID_PRINTDOC:
2357             case SID_PRINTDOCDIRECT:
2358             case SID_SETUPPRINTER:
2359             case SID_PRINTER_NAME:
2360             {
2361                 if (Application::GetSettings().GetMiscSettings().GetDisablePrinting()
2362                     || (pSh && pSh->isPrintLocked()))
2363                 {
2364                     rSet.DisableItem(nSID);
2365                     break;
2366                 }
2367 
2368                 SfxPrinter *pPrinter = GetPrinter();
2369 
2370                 if ( SID_PRINTDOCDIRECT == nSID )
2371                 {
2372                     OUString aPrinterName;
2373                     if ( pPrinter != nullptr )
2374                         aPrinterName = pPrinter->GetName();
2375                     else
2376                     {
2377                         // tdf#109149 don't poll the Default Printer Name on every query.
2378                         // We are queried on every change, so on every
2379                         // keystroke, and we are only using this to fill in the
2380                         // printername inside the label of "Print Directly (printer-name)"
2381                         // On Printer::GetDefaultPrinterName() is implemented with
2382                         // GetDefaultPrinter so don't call this excessively. 5 mins
2383                         // seems a reasonable refresh time.
2384                         std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now();
2385                         std::chrono::minutes five_mins(5);
2386                         if (now > pImpl->m_nDefaultPrinterNameFetchTime + five_mins)
2387                         {
2388                             pImpl->m_sDefaultPrinterName = Printer::GetDefaultPrinterName();
2389                             pImpl->m_nDefaultPrinterNameFetchTime = now;
2390                         }
2391                         aPrinterName = pImpl->m_sDefaultPrinterName;
2392                     }
2393                     if ( !aPrinterName.isEmpty() )
2394                     {
2395                         uno::Reference < frame::XFrame > xFrame( rFrame.GetFrame().GetFrameInterface() );
2396 
2397                         auto aProperties = vcl::CommandInfoProvider::GetCommandProperties(u".uno:PrintDefault"_ustr,
2398                             vcl::CommandInfoProvider::GetModuleIdentifier(xFrame));
2399                         OUString val = vcl::CommandInfoProvider::GetLabelForCommand(aProperties) +
2400                                         " (" + aPrinterName + ")";
2401 
2402                         rSet.Put( SfxStringItem( SID_PRINTDOCDIRECT, val ) );
2403                     }
2404                 }
2405                 break;
2406             }
2407             case SID_STYLE_FAMILY :
2408             {
2409                 rSet.Put( SfxUInt16Item( SID_STYLE_FAMILY, pImpl->m_nFamily ) );
2410                 break;
2411             }
2412         }
2413     }
2414 }
2415 
SetZoomFactor(const Fraction & rZoomX,const Fraction & rZoomY)2416 void SfxViewShell::SetZoomFactor( const Fraction &rZoomX,
2417     const Fraction &rZoomY )
2418 {
2419     DBG_ASSERT( GetWindow(), "no window" );
2420     MapMode aMap( GetWindow()->GetMapMode() );
2421     aMap.SetScaleX( rZoomX );
2422     aMap.SetScaleY( rZoomY );
2423     GetWindow()->SetMapMode( aMap );
2424 }
2425 
DoVerb(sal_Int32)2426 ErrCode SfxViewShell::DoVerb(sal_Int32 /*nVerb*/)
2427 
2428 /*  [Description]
2429 
2430     Virtual Method used to perform a Verb on a selected Object.
2431     Since this Object is only known by the derived classes, they must override
2432     DoVerb.
2433 */
2434 
2435 {
2436     return ERRCODE_SO_NOVERBS;
2437 }
2438 
OutplaceActivated(bool bActive)2439 void SfxViewShell::OutplaceActivated( bool bActive )
2440 {
2441     if ( !bActive )
2442     {
2443         if (SfxViewFrame* pFrame = GetFrame())
2444             pFrame->GetFrame().Appear();
2445     }
2446 }
2447 
UIActivating(SfxInPlaceClient *)2448 void SfxViewShell::UIActivating( SfxInPlaceClient* /*pClient*/ )
2449 {
2450     uno::Reference < frame::XFrame > xOwnFrame( rFrame.GetFrame().GetFrameInterface() );
2451     uno::Reference < frame::XFramesSupplier > xParentFrame = xOwnFrame->getCreator();
2452     if ( xParentFrame.is() )
2453         xParentFrame->setActiveFrame( xOwnFrame );
2454 
2455     rFrame.GetBindings().HidePopups();
2456     rFrame.GetDispatcher()->Update_Impl( true );
2457 }
2458 
UIDeactivated(SfxInPlaceClient *)2459 void SfxViewShell::UIDeactivated( SfxInPlaceClient* /*pClient*/ )
2460 {
2461     if ( !rFrame.GetFrame().IsClosing_Impl() || SfxViewFrame::Current() != &rFrame )
2462         rFrame.GetDispatcher()->Update_Impl( true );
2463     rFrame.GetBindings().HidePopups(false);
2464 
2465     rFrame.GetBindings().InvalidateAll(true);
2466 }
2467 
FindIPClient(const uno::Reference<embed::XEmbeddedObject> & xObj,vcl::Window * pObjParentWin) const2468 SfxInPlaceClient* SfxViewShell::FindIPClient
2469 (
2470     const uno::Reference < embed::XEmbeddedObject >& xObj,
2471     vcl::Window*             pObjParentWin
2472 )   const
2473 {
2474     std::vector< SfxInPlaceClient* >& rClients = pImpl->GetIPClients_Impl();
2475     if ( rClients.empty() )
2476         return nullptr;
2477 
2478     if( !pObjParentWin )
2479         pObjParentWin = GetWindow();
2480     for (SfxInPlaceClient* pIPClient : rClients)
2481     {
2482         if ( pIPClient->GetObject() == xObj && pIPClient->GetEditWin() == pObjParentWin )
2483             return pIPClient;
2484     }
2485 
2486     return nullptr;
2487 }
2488 
2489 
GetIPClient() const2490 SfxInPlaceClient* SfxViewShell::GetIPClient() const
2491 {
2492     return GetUIActiveClient();
2493 }
2494 
2495 
GetUIActiveIPClient_Impl() const2496 SfxInPlaceClient* SfxViewShell::GetUIActiveIPClient_Impl() const
2497 {
2498     // this method is needed as long as SFX still manages the border space for ChildWindows (see SfxFrame::Resize)
2499     std::vector< SfxInPlaceClient* >& rClients = pImpl->GetIPClients_Impl();
2500     if ( rClients.empty() )
2501         return nullptr;
2502 
2503     for (SfxInPlaceClient* pIPClient : rClients)
2504     {
2505         if ( pIPClient->IsUIActive() )
2506             return pIPClient;
2507     }
2508 
2509     return nullptr;
2510 }
2511 
GetUIActiveClient() const2512 SfxInPlaceClient* SfxViewShell::GetUIActiveClient() const
2513 {
2514     std::vector< SfxInPlaceClient* >& rClients = pImpl->GetIPClients_Impl();
2515     if ( rClients.empty() )
2516         return nullptr;
2517 
2518     const bool bIsTiledRendering = comphelper::LibreOfficeKit::isActive();
2519 
2520     for (SfxInPlaceClient* pIPClient : rClients)
2521     {
2522         if ( pIPClient->IsObjectUIActive() || ( bIsTiledRendering && pIPClient->IsObjectInPlaceActive() ) )
2523             return pIPClient;
2524     }
2525 
2526     return nullptr;
2527 }
2528 
2529 
Activate(bool bMDI)2530 void SfxViewShell::Activate( bool bMDI )
2531 {
2532     if ( bMDI )
2533     {
2534         SfxObjectShell *pSh = GetViewFrame().GetObjectShell();
2535         if (const auto xModel = pSh->GetModel())
2536             xModel->setCurrentController(GetController());
2537 
2538         SetCurrentDocument();
2539     }
2540 }
2541 
2542 
Deactivate(bool)2543 void SfxViewShell::Deactivate(bool /*bMDI*/)
2544 {
2545 }
2546 
2547 
Move()2548 void SfxViewShell::Move()
2549 
2550 /*  [Description]
2551 
2552     This virtual Method is called when the window displayed in the
2553     SfxViewShell gets a StarView-Move() notification.
2554 
2555     This base implementation does not have to be called.     .
2556 
2557     [Note]
2558 
2559     This Method can be used to cancel a selection, in order to catch the
2560     mouse movement which is due to moving a window.
2561 
2562     For now the notification does not work In-Place.
2563 */
2564 
2565 {
2566 }
2567 
2568 
OuterResizePixel(const Point &,const Size &)2569 void SfxViewShell::OuterResizePixel
2570 (
2571     const Point&    /*rToolOffset*/,// Upper left corner Tools in Frame-Window
2572     const Size&     /*rSize*/       // All available sizes.
2573 )
2574 
2575 /*  [Description]
2576 
2577     Override this Method to be able to react to the size-change of
2578     the View. Thus the View is defined as the Edit window and also the
2579     attached Tools are defined (for example the ruler).
2580 
2581     The Edit window must not be changed either in size or position.
2582 
2583     The Vis-Area of SfxObjectShell, its scale and position can be changed
2584     here. The main use is to change the size of the Vis-Area.
2585 
2586     If the Border is changed due to the new calculation then this has to be set
2587     by <SfxViewShell::SetBorderPixel(const SvBorder&)>. The Positioning of Tools
2588     is only allowed after the calling of 'SetBorderPixel'.
2589 
2590     [Example]
2591 
2592     void AppViewSh::OuterViewResizePixel( const Point &rOfs, const Size &rSz )
2593     {
2594         // Calculate Tool position and size externally, do not set!
2595         // (due to the following Border calculation)
2596         Point aHLinPos...; Size aHLinSz...;
2597         ...
2598 
2599         // Calculate and Set a Border of Tools which matches rSize.
2600         SvBorder aBorder...
2601         SetBorderPixel( aBorder ); // Allow Positioning from here on.
2602 
2603         // Arrange Tools
2604         pHLin->SetPosSizePixel( aHLinPos, aHLinSz );
2605         ...
2606     }
2607 
2608     [Cross-reference]
2609 
2610         <SfxViewShell::InnerResizePixel(const Point&,const Size& rSize)>
2611 */
2612 
2613 {
2614     SetBorderPixel( SvBorder() );
2615 }
2616 
2617 
InnerResizePixel(const Point &,const Size &,bool)2618 void SfxViewShell::InnerResizePixel
2619 (
2620     const Point&    /*rToolOffset*/,// Upper left corner Tools in Frame-Window
2621     const Size&     /*rSize*/,      // All available sizes.
2622     bool
2623 )
2624 
2625 /*  [Description]
2626 
2627     Override this Method to be able to react to the size-change of
2628     the Edit window.
2629 
2630     The Edit window must not be changed either in size or position.
2631     Neither the Vis-Area of SfxObjectShell nor its scale or position are
2632     allowed to be changed
2633 
2634     If the Border is changed due to the new calculation then is has to be set
2635     by <SfxViewShell::SetBorderPixel(const SvBorder&)>.
2636     The Positioning of Tools is only allowed after the calling of
2637     'SetBorderPixel'.
2638 
2639 
2640     [Note]
2641 
2642     void AppViewSh::InnerViewResizePixel( const Point &rOfs, const Size &rSz )
2643     {
2644         // Calculate Tool position and size internally, do not set!
2645         // (due to the following Border calculation)
2646         Point aHLinPos...; Size aHLinSz...;
2647         ...
2648 
2649         // Calculate and Set a Border of Tools which matches rSize.
2650         SvBorder aBorder...
2651         SetBorderPixel( aBorder ); // Allow Positioning from here on.
2652 
2653         // Arrange Tools
2654         pHLin->SetPosSizePixel( aHLinPos, aHLinSz );
2655         ...
2656     }
2657 
2658     [Cross-reference]
2659 
2660         <SfxViewShell::OuterResizePixel(const Point&,const Size& rSize)>
2661 */
2662 
2663 {
2664     SetBorderPixel( SvBorder() );
2665 }
2666 
InvalidateBorder()2667 void SfxViewShell::InvalidateBorder()
2668 {
2669     GetViewFrame().InvalidateBorderImpl( this );
2670     if (pImpl->m_pController.is())
2671     {
2672         pImpl->m_pController->BorderWidthsChanged_Impl();
2673     }
2674 }
2675 
SetBorderPixel(const SvBorder & rBorder)2676 void SfxViewShell::SetBorderPixel( const SvBorder &rBorder )
2677 {
2678     GetViewFrame().SetBorderPixelImpl( this, rBorder );
2679 
2680     // notify related controller that border size is changed
2681     if (pImpl->m_pController.is())
2682     {
2683         pImpl->m_pController->BorderWidthsChanged_Impl();
2684     }
2685 }
2686 
GetBorderPixel() const2687 const SvBorder& SfxViewShell::GetBorderPixel() const
2688 {
2689     return GetViewFrame().GetBorderPixelImpl();
2690 }
2691 
SetWindow(vcl::Window * pViewPort)2692 void SfxViewShell::SetWindow
2693 (
2694     vcl::Window*     pViewPort   // For example Null pointer in the Destructor.
2695 )
2696 
2697 /*  [Description]
2698 
2699     With this method the SfxViewShell is set in the data window. This is
2700     needed for the in-place container and for restoring the proper focus.
2701 
2702     Even in-place-active the conversion of the ViewPort Windows is forbidden.
2703 */
2704 
2705 {
2706     if( pWindow == pViewPort )
2707         return;
2708 
2709     // Disconnect existing IP-Clients if possible
2710     DisconnectAllClients();
2711 
2712     // Switch View-Port
2713     bool bHadFocus = pWindow && pWindow->HasChildPathFocus( true );
2714     pWindow = pViewPort;
2715 
2716     if( pWindow )
2717     {
2718         // Disable automatic GUI mirroring (right-to-left) for document windows
2719         pWindow->EnableRTL( false );
2720     }
2721 
2722     if ( bHadFocus && pWindow )
2723         pWindow->GrabFocus();
2724     //TODO/CLEANUP
2725     //Do we still need this Method?!
2726     //SfxGetpApp()->GrabFocus( pWindow );
2727 }
2728 
2729 ViewShellDocId SfxViewShell::mnCurrentDocId(0);
2730 
SfxViewShell(SfxViewFrame & rViewFrame,SfxViewShellFlags nFlags)2731 SfxViewShell::SfxViewShell
2732 (
2733     SfxViewFrame&     rViewFrame,     /*  <SfxViewFrame>, which will be
2734                                           displayed in this View */
2735     SfxViewShellFlags nFlags          /*  See <SfxViewShell-Flags> */
2736 )
2737 
2738 :   SfxShell(this)
2739 ,   pImpl( new SfxViewShell_Impl(nFlags, SfxViewShell::mnCurrentDocId) )
2740 ,   rFrame(rViewFrame)
2741 ,   pWindow(nullptr)
2742 ,   bNoNewWindow( nFlags & SfxViewShellFlags::NO_NEWWINDOW )
2743 ,   mbPrinterSettingsModified(false)
2744 ,   maLOKLanguageTag(LANGUAGE_NONE)
2745 ,   maLOKLocale(LANGUAGE_NONE)
2746 ,   maLOKDeviceFormFactor(LOKDeviceFormFactor::UNKNOWN)
2747 ,   mbLOKAccessibilityEnabled(false)
2748 ,   mbLOKColorPreviewEnabled(false)
2749 {
2750     SetMargin( rViewFrame.GetMargin_Impl() );
2751 
2752     SetPool( &rViewFrame.GetObjectShell()->GetPool() );
2753     StartListening(*rViewFrame.GetObjectShell());
2754 
2755     // Insert into list
2756     std::vector<SfxViewShell*> &rViewArr = SfxGetpApp()->GetViewShells_Impl();
2757     rViewArr.push_back(this);
2758 
2759     if (comphelper::LibreOfficeKit::isActive())
2760     {
2761         maLOKLanguageTag = SfxLokHelper::getDefaultLanguage();
2762         maLOKLocale = SfxLokHelper::getDefaultLanguage();
2763 
2764         const auto [isTimezoneSet, aTimezone] = SfxLokHelper::getDefaultTimezone();
2765         maLOKIsTimezoneSet = isTimezoneSet;
2766         maLOKTimezone = aTimezone;
2767 
2768         maLOKDeviceFormFactor = SfxLokHelper::getDeviceFormFactor();
2769 
2770         vcl::Window* pFrameWin = rViewFrame.GetWindow().GetFrameWindow();
2771         if (pFrameWin && !pFrameWin->GetLOKNotifier())
2772             pFrameWin->SetLOKNotifier(this, true);
2773     }
2774 }
2775 
~SfxViewShell()2776 SfxViewShell::~SfxViewShell()
2777 {
2778     // Remove from list
2779     const SfxViewShell *pThis = this;
2780     std::vector<SfxViewShell*> &rViewArr = SfxGetpApp()->GetViewShells_Impl();
2781     auto it = std::find( rViewArr.begin(), rViewArr.end(), pThis );
2782     rViewArr.erase( it );
2783 
2784     if ( pImpl->xClipboardListener.is() )
2785     {
2786         pImpl->xClipboardListener->DisconnectViewShell();
2787         pImpl->xClipboardListener = nullptr;
2788     }
2789 
2790     if (pImpl->m_pController.is())
2791     {
2792         pImpl->m_pController->ReleaseShell_Impl();
2793         pImpl->m_pController.clear();
2794     }
2795 
2796     vcl::Window* pFrameWin = GetViewFrame().GetWindow().GetFrameWindow();
2797     if (pFrameWin && pFrameWin->GetLOKNotifier() == this)
2798         pFrameWin->ReleaseLOKNotifier();
2799 }
2800 
getA11yFocusedParagraph() const2801 OUString SfxViewShell::getA11yFocusedParagraph() const
2802 {
2803     const LOKDocumentFocusListener& rDocFocusListener = GetLOKDocumentFocusListener();
2804     return rDocFocusListener.getFocusedParagraph();
2805 }
2806 
getA11yCaretPosition() const2807 int SfxViewShell::getA11yCaretPosition() const
2808 {
2809     const LOKDocumentFocusListener& rDocFocusListener = GetLOKDocumentFocusListener();
2810     return rDocFocusListener.getCaretPosition();
2811 }
2812 
SetSigningCertificate(const svl::crypto::CertificateOrName & rCertificate)2813 void SfxViewShell::SetSigningCertificate(const svl::crypto::CertificateOrName& rCertificate)
2814 {
2815     pImpl->m_aSigningCertificate = rCertificate;
2816 }
2817 
GetSigningCertificate() const2818 svl::crypto::CertificateOrName SfxViewShell::GetSigningCertificate() const
2819 {
2820     return pImpl->m_aSigningCertificate;
2821 }
2822 
2823 namespace
2824 {
2825 uno::Reference<beans::XPropertySet>
GetSelectedShapeOfView(const uno::Reference<frame::XController> & xController)2826 GetSelectedShapeOfView(const uno::Reference<frame::XController>& xController)
2827 {
2828     uno::Reference<view::XSelectionSupplier> xSelectionSupplier(xController, uno::UNO_QUERY);
2829     if (!xSelectionSupplier)
2830     {
2831         return {};
2832     }
2833     uno::Reference<drawing::XShapes> xShapes(xSelectionSupplier->getSelection(), uno::UNO_QUERY);
2834     if (!xShapes.is() || xShapes->getCount() != 1)
2835     {
2836         return {};
2837     }
2838 
2839     return uno::Reference<beans::XPropertySet>(xShapes->getByIndex(0), uno::UNO_QUERY);
2840 }
2841 }
2842 
SetSignPDFCertificate(const svl::crypto::CertificateOrName & rCertificateOrName)2843 void SfxViewShell::SetSignPDFCertificate(const svl::crypto::CertificateOrName& rCertificateOrName)
2844 {
2845     uno::Reference<beans::XPropertySet> xShape = GetSelectedShapeOfView(GetController());
2846     if (!xShape.is() || !xShape->getPropertySetInfo()->hasPropertyByName("InteropGrabBag"))
2847     {
2848         return;
2849     }
2850 
2851     comphelper::SequenceAsHashMap aMap(xShape->getPropertyValue("InteropGrabBag"));
2852 
2853     auto it = aMap.find("SignatureCertificate");
2854     if (rCertificateOrName.Is())
2855     {
2856         if (rCertificateOrName.m_xCertificate.is())
2857         {
2858             aMap["SignatureCertificate"] <<= rCertificateOrName.m_xCertificate;
2859         }
2860         else
2861         {
2862             aMap["SignatureCertificate"] <<= rCertificateOrName.m_aName;
2863         }
2864     }
2865     else if (it != aMap.end())
2866     {
2867         aMap.erase(it);
2868     }
2869     xShape->setPropertyValue("InteropGrabBag", uno::Any(aMap.getAsConstPropertyValueList()));
2870     if (!rCertificateOrName.Is())
2871     {
2872         // The shape's property is now reset, so the doc model is no longer modified.
2873         GetObjectShell()->SetModified(false);
2874     }
2875 }
2876 
GetSignPDFCertificate() const2877 svl::crypto::CertificateOrName SfxViewShell::GetSignPDFCertificate() const
2878 {
2879     uno::Reference<beans::XPropertySet> xShape = GetSelectedShapeOfView(GetController());
2880     if (!xShape.is() || !xShape->getPropertySetInfo()->hasPropertyByName("InteropGrabBag"))
2881     {
2882         return {};
2883     }
2884 
2885     comphelper::SequenceAsHashMap aMap(xShape->getPropertyValue("InteropGrabBag"));
2886     auto it = aMap.find("SignatureCertificate");
2887     if (it == aMap.end())
2888     {
2889         return {};
2890     }
2891 
2892     svl::crypto::CertificateOrName aCertificateOrName;
2893     if (it->second.has<uno::Reference<security::XCertificate>>())
2894     {
2895         it->second >>= aCertificateOrName.m_xCertificate;
2896     }
2897     else
2898     {
2899         it->second >>= aCertificateOrName.m_aName;
2900     }
2901     return aCertificateOrName;
2902 }
2903 
PrepareClose(bool bUI)2904 bool SfxViewShell::PrepareClose
2905 (
2906     bool bUI     // TRUE: Allow Dialog and so on, FALSE: silent-mode
2907 )
2908 {
2909     if (GetViewFrame().GetWindow().GetLOKNotifier() == this)
2910         GetViewFrame().GetWindow().ReleaseLOKNotifier();
2911 
2912     SfxPrinter *pPrinter = GetPrinter();
2913     if ( pPrinter && pPrinter->IsPrinting() )
2914     {
2915         if ( bUI )
2916         {
2917             std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(GetViewFrame().GetFrameWeld(),
2918                                                                      VclMessageType::Info, VclButtonsType::Ok,
2919                                                                      SfxResId(STR_CANT_CLOSE)));
2920             xBox->run();
2921         }
2922 
2923         return false;
2924     }
2925 
2926     if( GetViewFrame().IsInModalMode() )
2927         return false;
2928 
2929     if( bUI && GetViewFrame().GetDispatcher()->IsLocked() )
2930         return false;
2931 
2932     return true;
2933 }
2934 
Current()2935 SfxViewShell* SfxViewShell::Current()
2936 {
2937     SfxViewFrame *pCurrent = SfxViewFrame::Current();
2938     return pCurrent ? pCurrent->GetViewShell() : nullptr;
2939 }
2940 
IsCurrentLokViewReadOnly()2941 bool SfxViewShell::IsCurrentLokViewReadOnly()
2942 {
2943     if (!comphelper::LibreOfficeKit::isActive())
2944         return false;
2945     SfxViewShell* pCurrent = Current();
2946     return pCurrent && pCurrent->IsLokReadOnlyView();
2947 }
2948 
Get(const Reference<XController> & i_rController)2949 SfxViewShell* SfxViewShell::Get( const Reference< XController>& i_rController )
2950 {
2951     if ( !i_rController.is() )
2952         return nullptr;
2953 
2954     for (   SfxViewShell* pViewShell = SfxViewShell::GetFirst( false );
2955             pViewShell;
2956             pViewShell = SfxViewShell::GetNext( *pViewShell, false )
2957         )
2958     {
2959         if ( pViewShell->GetController() == i_rController )
2960             return pViewShell;
2961     }
2962     return nullptr;
2963 }
2964 
GetDrawView() const2965 SdrView* SfxViewShell::GetDrawView() const
2966 
2967 /*  [Description]
2968 
2969     This virtual Method has to be overloaded by the sub classes, to be able
2970     make the Property-Editor available.
2971 
2972     The default implementation does always return zero.
2973 */
2974 
2975 {
2976     return nullptr;
2977 }
2978 
2979 
GetSelectionText(bool,bool)2980 OUString SfxViewShell::GetSelectionText
2981 (
2982     bool /*bCompleteWords*/, /*  FALSE (default)
2983                                 Only the actual selected text is returned.
2984 
2985                                 TRUE
2986                                 The selected text is expanded so that only
2987                                 whole words are returned. As word separators
2988                                 these are used: white spaces and punctuation
2989                                 ".,;" and single and double quotes.
2990                             */
2991     bool /*bOnlyASample*/ /* used by some dialogs to avoid constructing monster strings e.g. in calc */
2992 )
2993 
2994 /*  [Description]
2995 
2996     Override this Method to return a text that
2997     is included in the current selection. This is for example used when
2998     sending emails.
2999 
3000     When called with "CompleteWords == TRUE", it is for example sufficient
3001     with having the Cursor positioned somewhere within a URL in-order
3002     to have the entire URL returned.
3003 */
3004 
3005 {
3006     return OUString();
3007 }
3008 
3009 
HasSelection(bool) const3010 bool SfxViewShell::HasSelection( bool ) const
3011 
3012 /*  [Description]
3013 
3014     With this virtual Method can a for example a Dialog be queried, to
3015     check if something is selected in the current view. If the Parameter
3016     is <BOOL> TRUE then it is checked whether some text is selected.
3017 */
3018 
3019 {
3020     return false;
3021 }
3022 
AddSubShell(SfxShell & rShell)3023 void SfxViewShell::AddSubShell( SfxShell& rShell )
3024 {
3025     pImpl->aArr.push_back(&rShell);
3026     SfxDispatcher *pDisp = rFrame.GetDispatcher();
3027     if ( pDisp->IsActive(*this) )
3028     {
3029         pDisp->Push(rShell);
3030         pDisp->Flush();
3031     }
3032 }
3033 
RemoveSubShell(SfxShell * pShell)3034 void SfxViewShell::RemoveSubShell( SfxShell* pShell )
3035 {
3036     SfxDispatcher *pDisp = rFrame.GetDispatcher();
3037     if ( !pShell )
3038     {
3039         size_t nCount = pImpl->aArr.size();
3040         if ( pDisp->IsActive(*this) )
3041         {
3042             for(size_t n = nCount; n > 0; --n)
3043                 pDisp->Pop(*pImpl->aArr[n - 1]);
3044             pDisp->Flush();
3045         }
3046         pImpl->aArr.clear();
3047     }
3048     else
3049     {
3050         SfxShellArr_Impl::iterator i = std::find(pImpl->aArr.begin(), pImpl->aArr.end(), pShell);
3051         if(i != pImpl->aArr.end())
3052         {
3053             pImpl->aArr.erase(i);
3054             if(pDisp->IsActive(*this))
3055             {
3056                 pDisp->RemoveShell_Impl(*pShell);
3057                 pDisp->Flush();
3058             }
3059         }
3060     }
3061 }
3062 
GetSubShell(sal_uInt16 nNo)3063 SfxShell* SfxViewShell::GetSubShell( sal_uInt16 nNo )
3064 {
3065     sal_uInt16 nCount = pImpl->aArr.size();
3066     if(nNo < nCount)
3067         return pImpl->aArr[nCount - nNo - 1];
3068     return nullptr;
3069 }
3070 
PushSubShells_Impl(bool bPush)3071 void SfxViewShell::PushSubShells_Impl( bool bPush )
3072 {
3073     SfxDispatcher *pDisp = rFrame.GetDispatcher();
3074     if ( bPush )
3075     {
3076         for (auto const& elem : pImpl->aArr)
3077             pDisp->Push(*elem);
3078     }
3079     else if(!pImpl->aArr.empty())
3080     {
3081         SfxShell& rPopUntil = *pImpl->aArr[0];
3082         if ( pDisp->GetShellLevel( rPopUntil ) != USHRT_MAX )
3083             pDisp->Pop( rPopUntil, SfxDispatcherPopFlags::POP_UNTIL );
3084     }
3085 
3086     pDisp->Flush();
3087 }
3088 
3089 
WriteUserData(OUString &,bool)3090 void SfxViewShell::WriteUserData( OUString&, bool )
3091 {
3092 }
3093 
3094 
ReadUserData(const OUString &,bool)3095 void SfxViewShell::ReadUserData(const OUString&, bool )
3096 {
3097 }
3098 
ReadUserDataSequence(const uno::Sequence<beans::PropertyValue> &)3099 void SfxViewShell::ReadUserDataSequence ( const uno::Sequence < beans::PropertyValue >& )
3100 {
3101 }
3102 
WriteUserDataSequence(uno::Sequence<beans::PropertyValue> &)3103 void SfxViewShell::WriteUserDataSequence ( uno::Sequence < beans::PropertyValue >& )
3104 {
3105 }
3106 
3107 
3108 // returns the first shell of spec. type viewing the specified doc.
GetFirst(bool bOnlyVisible,const std::function<bool (const SfxViewShell &)> & isViewShell)3109 SfxViewShell* SfxViewShell::GetFirst
3110 (
3111     bool          bOnlyVisible,
3112     const std::function< bool ( const SfxViewShell& ) >& isViewShell
3113 )
3114 {
3115     // search for a SfxViewShell of the specified type
3116     std::vector<SfxViewShell*> &rShells = SfxGetpApp()->GetViewShells_Impl();
3117     for (SfxViewShell* pShell : rShells)
3118     {
3119         if ( pShell )
3120         {
3121             // This code used to check that the frame exists in the other list,
3122             // because of https://bz.apache.org/ooo/show_bug.cgi?id=62084, with the explanation:
3123             // sometimes dangling SfxViewShells exist that point to a dead SfxViewFrame
3124             // these ViewShells shouldn't be accessible anymore
3125             // a destroyed ViewFrame is not in the ViewFrame array anymore, so checking this array helps
3126             // That doesn't seem to be needed anymore, but keep an assert, just in case.
3127             assert(std::find(SfxGetpApp()->GetViewFrames_Impl().begin(), SfxGetpApp()->GetViewFrames_Impl().end(),
3128                 &pShell->GetViewFrame()) != SfxGetpApp()->GetViewFrames_Impl().end());
3129             if ( ( !bOnlyVisible || pShell->GetViewFrame().IsVisible() ) && (!isViewShell || isViewShell(*pShell)))
3130                 return pShell;
3131         }
3132     }
3133 
3134     return nullptr;
3135 }
3136 
3137 // returns the next shell of spec. type viewing the specified doc.
GetNext(const SfxViewShell & rPrev,bool bOnlyVisible,const std::function<bool (const SfxViewShell &)> & isViewShell)3138 SfxViewShell* SfxViewShell::GetNext
3139 (
3140     const SfxViewShell& rPrev,
3141     bool                bOnlyVisible,
3142     const std::function<bool ( const SfxViewShell& )>& isViewShell
3143 )
3144 {
3145     std::vector<SfxViewShell*> &rShells = SfxGetpApp()->GetViewShells_Impl();
3146     size_t nPos;
3147     for ( nPos = 0; nPos < rShells.size(); ++nPos )
3148         if ( rShells[nPos] == &rPrev )
3149             break;
3150 
3151     for ( ++nPos; nPos < rShells.size(); ++nPos )
3152     {
3153         SfxViewShell *pShell = rShells[nPos];
3154         if ( pShell )
3155         {
3156             assert(std::find(SfxGetpApp()->GetViewFrames_Impl().begin(), SfxGetpApp()->GetViewFrames_Impl().end(),
3157                 &pShell->GetViewFrame()) != SfxGetpApp()->GetViewFrames_Impl().end());
3158             if ( ( !bOnlyVisible || pShell->GetViewFrame().IsVisible() ) && (!isViewShell || isViewShell(*pShell)) )
3159                 return pShell;
3160         }
3161     }
3162 
3163     return nullptr;
3164 }
3165 
Notify(SfxBroadcaster & rBC,const SfxHint & rHint)3166 void SfxViewShell::Notify( SfxBroadcaster& rBC,
3167                             const SfxHint& rHint )
3168 {
3169     if (rHint.GetId() != SfxHintId::ThisIsAnSfxEventHint ||
3170         static_cast<const SfxEventHint&>(rHint).GetEventId() != SfxEventHintId::LoadFinished)
3171     {
3172         return;
3173     }
3174 
3175     if ( !GetController().is() )
3176         return;
3177 
3178     // avoid access to dangling ViewShells
3179     auto &rFrames = SfxGetpApp()->GetViewFrames_Impl();
3180     for (SfxViewFrame* frame : rFrames)
3181     {
3182         if ( frame == &GetViewFrame() && &rBC == GetObjectShell() )
3183         {
3184             SfxItemSet& rSet = GetObjectShell()->GetMedium()->GetItemSet();
3185             const SfxUnoAnyItem* pItem = rSet.GetItem(SID_VIEW_DATA, false);
3186             if ( pItem )
3187             {
3188                 pImpl->m_pController->restoreViewData( pItem->GetValue() );
3189                 rSet.ClearItem( SID_VIEW_DATA );
3190             }
3191             break;
3192         }
3193     }
3194 }
3195 
ExecKey_Impl(const KeyEvent & aKey)3196 bool SfxViewShell::ExecKey_Impl(const KeyEvent& aKey)
3197 {
3198     bool setModuleConfig = false; // In case libreofficekit is active, we will re-set the module config class.
3199     if (!pImpl->m_xAccExec)
3200     {
3201         pImpl->m_xAccExec = ::svt::AcceleratorExecute::createAcceleratorHelper();
3202         pImpl->m_xAccExec->init(::comphelper::getProcessComponentContext(),
3203             rFrame.GetFrame().GetFrameInterface());
3204         setModuleConfig = true;
3205     }
3206 
3207     if (comphelper::LibreOfficeKit::isActive())
3208     {
3209         // Get the module name.
3210         const css::uno::Reference< css::uno::XComponentContext >&  xContext      (::comphelper::getProcessComponentContext());
3211         css::uno::Reference< css::frame::XModuleManager2 >  xModuleManager(css::frame::ModuleManager::create(xContext));
3212         OUString sModule = xModuleManager->identify(rFrame.GetFrame().GetFrameInterface());
3213 
3214         // Get the language name.
3215         OUString viewLang = GetLOKLanguageTag().getBcp47();
3216 
3217         // Merge them & have a key.
3218         OUString key = sModule + viewLang;
3219 
3220         // Check it in configurations map. Create a configuration manager if there isn't one for the key.
3221         std::unordered_map<OUString, css::uno::Reference<css::ui::XAcceleratorConfiguration>>& acceleratorConfs = SfxApplication::Get()->GetAcceleratorConfs_Impl();
3222         if (acceleratorConfs.find(key) == acceleratorConfs.end())
3223         {
3224             // Create a new configuration manager for the module.
3225 
3226             OUString actualLang = officecfg::Setup::L10N::ooLocale::get();
3227 
3228             std::shared_ptr<comphelper::ConfigurationChanges> batch(comphelper::ConfigurationChanges::create());
3229             officecfg::Setup::L10N::ooLocale::set(viewLang, batch);
3230             batch->commit();
3231 
3232             // We have set the language. Time to create the config manager.
3233             acceleratorConfs[key] = svt::AcceleratorExecute::lok_createNewAcceleratorConfiguration(::comphelper::getProcessComponentContext(), sModule);
3234 
3235             std::shared_ptr<comphelper::ConfigurationChanges> batch2(comphelper::ConfigurationChanges::create());
3236             officecfg::Setup::L10N::ooLocale::set(actualLang, batch2);
3237             batch2->commit();
3238         }
3239 
3240         if (setModuleConfig)
3241             pImpl->m_xAccExec->lok_setModuleConfig(acceleratorConfs[key]);
3242     }
3243 
3244     return pImpl->m_xAccExec->execute(aKey.GetKeyCode());
3245 }
3246 
setLibreOfficeKitViewCallback(SfxLokCallbackInterface * pCallback)3247 void SfxViewShell::setLibreOfficeKitViewCallback(SfxLokCallbackInterface* pCallback)
3248 {
3249     pImpl->m_pLibreOfficeKitViewCallback = pCallback;
3250 
3251     afterCallbackRegistered();
3252 
3253     if (!pImpl->m_pLibreOfficeKitViewCallback)
3254         return;
3255 
3256     // Ask other views to tell us about their cursors.
3257     SfxViewShell* pViewShell = SfxViewShell::GetFirst();
3258     while (pViewShell)
3259     {
3260         if (pViewShell->GetDocId() == GetDocId())
3261             pViewShell->NotifyCursor(this);
3262         pViewShell = SfxViewShell::GetNext(*pViewShell);
3263     }
3264 }
3265 
getLibreOfficeKitViewCallback() const3266 SfxLokCallbackInterface* SfxViewShell::getLibreOfficeKitViewCallback() const
3267 {
3268     return pImpl->m_pLibreOfficeKitViewCallback;
3269 }
3270 
dumpLibreOfficeKitViewState(rtl::OStringBuffer & rState)3271 void SfxViewShell::dumpLibreOfficeKitViewState(rtl::OStringBuffer &rState)
3272 {
3273     rState.append("\n    SfxViewShell: ");
3274     rState.append(OString::number(reinterpret_cast<sal_uInt64>(this), 16));
3275     rState.append("\n\tDocId:\t");
3276     auto nDocId = static_cast<int>(GetDocId());
3277     rState.append(static_cast<sal_Int32>(nDocId));
3278     rState.append("\n\tViewId:\t");
3279     rState.append(static_cast<sal_Int32>(GetViewShellId()));
3280     rState.append("\n\tPart:\t");
3281     rState.append(static_cast<sal_Int32>(getPart()));
3282     rState.append("\n\tLang:\t");
3283     rState.append(OUStringToOString(GetLOKLanguageTag().getBcp47(), RTL_TEXTENCODING_UTF8));
3284     rState.append("\n\tA11y:\t");
3285     rState.append(GetLOKAccessibilityState() ? "enabled" : "disabled");
3286 
3287     if (pImpl->m_pLibreOfficeKitViewCallback)
3288         pImpl->m_pLibreOfficeKitViewCallback->dumpState(rState);
3289 }
3290 
ignoreLibreOfficeKitViewCallback(int nType,const SfxViewShell_Impl * pImpl)3291 static bool ignoreLibreOfficeKitViewCallback(int nType, const SfxViewShell_Impl* pImpl)
3292 {
3293     if (!comphelper::LibreOfficeKit::isActive())
3294         return true;
3295 
3296     if (comphelper::LibreOfficeKit::isTiledPainting())
3297     {
3298         switch (nType)
3299         {
3300         case LOK_CALLBACK_FORM_FIELD_BUTTON:
3301         case LOK_CALLBACK_TEXT_SELECTION:
3302         case LOK_CALLBACK_COMMENT:
3303         case LOK_CALLBACK_DOCUMENT_SIZE_CHANGED:
3304             break;
3305         default:
3306             // Reject e.g. invalidate during paint.
3307             return true;
3308         }
3309     }
3310 
3311     if (pImpl->m_bTiledSearching)
3312     {
3313         switch (nType)
3314         {
3315         case LOK_CALLBACK_TEXT_SELECTION:
3316         case LOK_CALLBACK_TEXT_VIEW_SELECTION:
3317         case LOK_CALLBACK_TEXT_SELECTION_START:
3318         case LOK_CALLBACK_TEXT_SELECTION_END:
3319         case LOK_CALLBACK_GRAPHIC_SELECTION:
3320         case LOK_CALLBACK_GRAPHIC_VIEW_SELECTION:
3321             return true;
3322         }
3323     }
3324 
3325     return false;
3326 }
3327 
libreOfficeKitViewInvalidateTilesCallback(const tools::Rectangle * pRect,int nPart,int nMode) const3328 void SfxViewShell::libreOfficeKitViewInvalidateTilesCallback(const tools::Rectangle* pRect, int nPart, int nMode) const
3329 {
3330     if (ignoreLibreOfficeKitViewCallback(LOK_CALLBACK_INVALIDATE_TILES, pImpl.get()))
3331         return;
3332     if (pImpl->m_pLibreOfficeKitViewCallback)
3333         pImpl->m_pLibreOfficeKitViewCallback->libreOfficeKitViewInvalidateTilesCallback(pRect, nPart, nMode);
3334     else
3335         SAL_INFO(
3336             "sfx.view",
3337             "SfxViewShell::libreOfficeKitViewInvalidateTilesCallback no callback set!");
3338 }
3339 
libreOfficeKitViewCallbackWithViewId(int nType,const OString & pPayload,int nViewId) const3340 void SfxViewShell::libreOfficeKitViewCallbackWithViewId(int nType, const OString& pPayload, int nViewId) const
3341 {
3342     if (ignoreLibreOfficeKitViewCallback(nType, pImpl.get()))
3343         return;
3344     if (pImpl->m_pLibreOfficeKitViewCallback)
3345         pImpl->m_pLibreOfficeKitViewCallback->libreOfficeKitViewCallbackWithViewId(nType, pPayload, nViewId);
3346     else
3347         SAL_INFO(
3348             "sfx.view",
3349             "SfxViewShell::libreOfficeKitViewCallbackWithViewId no callback set! Dropped payload of type "
3350             << lokCallbackTypeToString(nType) << ": [" << pPayload << ']');
3351 }
3352 
libreOfficeKitViewCallback(int nType,const OString & pPayload) const3353 void SfxViewShell::libreOfficeKitViewCallback(int nType, const OString& pPayload) const
3354 {
3355     if (ignoreLibreOfficeKitViewCallback(nType, pImpl.get()))
3356         return;
3357     if (pImpl->m_pLibreOfficeKitViewCallback)
3358         pImpl->m_pLibreOfficeKitViewCallback->libreOfficeKitViewCallback(nType, pPayload);
3359     else
3360         SAL_INFO(
3361             "sfx.view",
3362             "SfxViewShell::libreOfficeKitViewCallback no callback set! Dropped payload of type "
3363             << lokCallbackTypeToString(nType) << ": [" << pPayload << ']');
3364 }
3365 
libreOfficeKitViewUpdatedCallback(int nType) const3366 void SfxViewShell::libreOfficeKitViewUpdatedCallback(int nType) const
3367 {
3368     if (ignoreLibreOfficeKitViewCallback(nType, pImpl.get()))
3369         return;
3370     if (pImpl->m_pLibreOfficeKitViewCallback)
3371         pImpl->m_pLibreOfficeKitViewCallback->libreOfficeKitViewUpdatedCallback(nType);
3372     else
3373         SAL_INFO(
3374             "sfx.view",
3375             "SfxViewShell::libreOfficeKitViewUpdatedCallback no callback set! Dropped payload of type "
3376             << lokCallbackTypeToString(nType));
3377 }
3378 
libreOfficeKitViewUpdatedCallbackPerViewId(int nType,int nViewId,int nSourceViewId) const3379 void SfxViewShell::libreOfficeKitViewUpdatedCallbackPerViewId(int nType, int nViewId, int nSourceViewId) const
3380 {
3381     if (ignoreLibreOfficeKitViewCallback(nType, pImpl.get()))
3382         return;
3383     if (pImpl->m_pLibreOfficeKitViewCallback)
3384         pImpl->m_pLibreOfficeKitViewCallback->libreOfficeKitViewUpdatedCallbackPerViewId(nType, nViewId, nSourceViewId);
3385     else
3386         SAL_INFO(
3387             "sfx.view",
3388             "SfxViewShell::libreOfficeKitViewUpdatedCallbackPerViewId no callback set! Dropped payload of type "
3389             << lokCallbackTypeToString(nType));
3390 }
3391 
libreOfficeKitViewAddPendingInvalidateTiles()3392 void SfxViewShell::libreOfficeKitViewAddPendingInvalidateTiles()
3393 {
3394     if (pImpl->m_pLibreOfficeKitViewCallback)
3395         pImpl->m_pLibreOfficeKitViewCallback->libreOfficeKitViewAddPendingInvalidateTiles();
3396     else
3397         SAL_INFO(
3398             "sfx.view",
3399             "SfxViewShell::libreOfficeKitViewAddPendingInvalidateTiles no callback set!");
3400 }
3401 
afterCallbackRegistered()3402 void SfxViewShell::afterCallbackRegistered()
3403 {
3404     LOK_INFO("sfx.view", "SfxViewShell::afterCallbackRegistered invoked");
3405     if (GetLOKAccessibilityState())
3406     {
3407         LOKDocumentFocusListener& rDocFocusListener = GetLOKDocumentFocusListener();
3408         rDocFocusListener.notifyFocusedParagraphChanged();
3409     }
3410 }
3411 
flushPendingLOKInvalidateTiles()3412 void SfxViewShell::flushPendingLOKInvalidateTiles()
3413 {
3414     // SfxViewShell itself does not delay any tile invalidations.
3415 }
3416 
getLOKPayload(int nType,int) const3417 std::optional<OString> SfxViewShell::getLOKPayload(int nType, int /*nViewId*/) const
3418 {
3419     // SfxViewShell itself currently doesn't handle any updated-payload types.
3420     SAL_WARN("sfx.view", "SfxViewShell::getLOKPayload unhandled type " << lokCallbackTypeToString(nType));
3421     abort();
3422 }
3423 
GetEditWindowForActiveOLEObj() const3424 vcl::Window* SfxViewShell::GetEditWindowForActiveOLEObj() const
3425 {
3426     vcl::Window* pEditWin = nullptr;
3427     SfxInPlaceClient* pIPClient = GetIPClient();
3428     if (pIPClient)
3429     {
3430         pEditWin = pIPClient->GetEditWin();
3431     }
3432     return pEditWin;
3433 }
3434 
GetColorConfigColor(svtools::ColorConfigEntry eEntry) const3435 ::Color SfxViewShell::GetColorConfigColor(svtools::ColorConfigEntry eEntry) const
3436 {
3437     SAL_WARN("sfx.view", "SfxViewShell::GetColorConfigColor not overridden!");
3438     svtools::ColorConfig aColorConfig;
3439     return aColorConfig.GetColorValue(eEntry).nColor;
3440 }
3441 
SetLOKLanguageTag(const OUString & rBcp47LanguageTag)3442 void SfxViewShell::SetLOKLanguageTag(const OUString& rBcp47LanguageTag)
3443 {
3444     LanguageTag aTag(rBcp47LanguageTag, true);
3445 
3446     css::uno::Sequence<OUString> inst(officecfg::Setup::Office::InstalledLocales::get()->getElementNames());
3447     LanguageTag aFallbackTag = LanguageTag(getInstalledLocaleForSystemUILanguage(inst, /* bRequestInstallIfMissing */ false, rBcp47LanguageTag), true).makeFallback();
3448 
3449     // If we want de-CH, and the de localisation is available, we don't want to use de-DE as then
3450     // the magic in Translate::get() won't turn ess-zet into double s.
3451     if (rBcp47LanguageTag == "de-CH")
3452         maLOKLanguageTag = std::move(aTag);
3453     else
3454         maLOKLanguageTag = std::move(aFallbackTag);
3455 }
3456 
GetLOKDocumentFocusListener()3457 LOKDocumentFocusListener& SfxViewShell::GetLOKDocumentFocusListener()
3458 {
3459     if (mpLOKDocumentFocusListener)
3460         return *mpLOKDocumentFocusListener;
3461 
3462     mpLOKDocumentFocusListener = new LOKDocumentFocusListener(this);
3463     return *mpLOKDocumentFocusListener;
3464 }
3465 
GetLOKDocumentFocusListener() const3466 const LOKDocumentFocusListener& SfxViewShell::GetLOKDocumentFocusListener() const
3467 {
3468     return const_cast<SfxViewShell*>(this)->GetLOKDocumentFocusListener();
3469 }
3470 
SetLOKAccessibilityState(bool bEnabled)3471 void SfxViewShell::SetLOKAccessibilityState(bool bEnabled)
3472 {
3473     if (bEnabled == mbLOKAccessibilityEnabled)
3474         return;
3475     mbLOKAccessibilityEnabled = bEnabled;
3476 
3477     LOKDocumentFocusListener& rDocumentFocusListener = GetLOKDocumentFocusListener();
3478 
3479     if (!pWindow)
3480         return;
3481 
3482     uno::Reference< accessibility::XAccessible > xAccessible =
3483         pWindow->GetAccessible();
3484 
3485     if (!xAccessible.is())
3486         return;
3487 
3488     if (mbLOKAccessibilityEnabled)
3489     {
3490         try
3491         {
3492             rDocumentFocusListener.attachRecursive(xAccessible);
3493         }
3494         catch (const uno::Exception&)
3495         {
3496             LOK_WARN("SetLOKAccessibilityState", "Exception caught processing LOKDocumentFocusListener::attachRecursive");
3497         }
3498     }
3499     else
3500     {
3501         try
3502         {
3503             rDocumentFocusListener.detachRecursive(xAccessible, /*bForce*/ true);
3504         }
3505         catch (const uno::Exception&)
3506         {
3507             LOK_WARN("SetLOKAccessibilityState", "Exception caught processing LOKDocumentFocusListener::detachRecursive");
3508         }
3509     }
3510 }
3511 
SetLOKColorPreviewState(bool bEnabled)3512 void SfxViewShell::SetLOKColorPreviewState(bool bEnabled)
3513 {
3514     mbLOKColorPreviewEnabled = bEnabled;
3515 }
3516 
SetLOKLocale(const OUString & rBcp47LanguageTag)3517 void SfxViewShell::SetLOKLocale(const OUString& rBcp47LanguageTag)
3518 {
3519     maLOKLocale = LanguageTag(rBcp47LanguageTag, true).makeFallback();
3520     if (this == Current())
3521     {
3522         // update the current LOK language and locale for the dialog tunneling
3523         comphelper::LibreOfficeKit::setLanguageTag(GetLOKLanguageTag());
3524         comphelper::LibreOfficeKit::setLocale(GetLOKLocale());
3525     }
3526 }
3527 
NotifyCursor(SfxViewShell *) const3528 void SfxViewShell::NotifyCursor(SfxViewShell* /*pViewShell*/) const
3529 {
3530 }
3531 
setTiledSearching(bool bTiledSearching)3532 void SfxViewShell::setTiledSearching(bool bTiledSearching)
3533 {
3534     pImpl->m_bTiledSearching = bTiledSearching;
3535 }
3536 
getPart() const3537 int SfxViewShell::getPart() const
3538 {
3539     return 0;
3540 }
3541 
getEditMode() const3542 int SfxViewShell::getEditMode() const
3543 {
3544     return 0;
3545 }
3546 
GetViewShellId() const3547 ViewShellId SfxViewShell::GetViewShellId() const
3548 {
3549     return pImpl->m_nViewShellId;
3550 }
3551 
SetCurrentDocId(ViewShellDocId nId)3552 void SfxViewShell::SetCurrentDocId(ViewShellDocId nId)
3553 {
3554     mnCurrentDocId = nId;
3555 }
3556 
GetDocId() const3557 ViewShellDocId SfxViewShell::GetDocId() const
3558 {
3559     assert(pImpl->m_nDocId >= ViewShellDocId(0) && "m_nDocId should have been initialized, but it is invalid.");
3560     return pImpl->m_nDocId;
3561 }
3562 
notifyInvalidation(tools::Rectangle const * pRect) const3563 void SfxViewShell::notifyInvalidation(tools::Rectangle const* pRect) const
3564 {
3565     SfxLokHelper::notifyInvalidation(this, pRect);
3566 }
3567 
NotifyOtherViews(int nType,const OString & rKey,const OString & rPayload)3568 void SfxViewShell::NotifyOtherViews(int nType, const OString& rKey, const OString& rPayload)
3569 {
3570     SfxLokHelper::notifyOtherViews(this, nType, rKey, rPayload);
3571 }
3572 
NotifyOtherView(OutlinerViewShell * pOther,int nType,const OString & rKey,const OString & rPayload)3573 void SfxViewShell::NotifyOtherView(OutlinerViewShell* pOther, int nType, const OString& rKey, const OString& rPayload)
3574 {
3575     auto pOtherShell = dynamic_cast<SfxViewShell*>(pOther);
3576     if (!pOtherShell)
3577         return;
3578 
3579     SfxLokHelper::notifyOtherView(*this, pOtherShell, nType, rKey, rPayload);
3580 }
3581 
dumpAsXml(xmlTextWriterPtr pWriter) const3582 void SfxViewShell::dumpAsXml(xmlTextWriterPtr pWriter) const
3583 {
3584     (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SfxViewShell"));
3585     (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this);
3586     (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("id"), BAD_CAST(OString::number(static_cast<sal_Int32>(GetViewShellId())).getStr()));
3587     (void)xmlTextWriterEndElement(pWriter);
3588 }
3589 
KeyInput(const KeyEvent & rKeyEvent)3590 bool SfxViewShell::KeyInput( const KeyEvent &rKeyEvent )
3591 
3592 /*  [Description]
3593 
3594     This Method executes the KeyEvent 'rKeyEvent' of the Keys (Accelerator)
3595     configured either direct or indirect (for example by the Application)
3596     in the SfxViewShell.
3597 
3598     [Return value]
3599 
3600     bool                    TRUE
3601                             The Key (Accelerator) is configured and the
3602                             associated Handler was called
3603 
3604                             FALSE
3605                             The Key (Accelerator) is not configured and
3606                             subsequently no Handler was called
3607 
3608     [Cross-reference]
3609 
3610     <SfxApplication::KeyInput(const KeyEvent&)>
3611 */
3612 {
3613     return ExecKey_Impl(rKeyEvent);
3614 }
3615 
GlobalKeyInput_Impl(const KeyEvent & rKeyEvent)3616 bool SfxViewShell::GlobalKeyInput_Impl( const KeyEvent &rKeyEvent )
3617 {
3618     return ExecKey_Impl(rKeyEvent);
3619 }
3620 
3621 
ShowCursor(bool)3622 void SfxViewShell::ShowCursor( bool /*bOn*/ )
3623 
3624 /*  [Description]
3625 
3626     Subclasses must override this Method so that SFx can switch the
3627     Cursor on and off, for example while a <SfxProgress> is running.
3628 */
3629 
3630 {
3631 }
3632 
3633 
ResetAllClients_Impl(SfxInPlaceClient const * pIP)3634 void SfxViewShell::ResetAllClients_Impl( SfxInPlaceClient const *pIP )
3635 {
3636 
3637     std::vector< SfxInPlaceClient* >& rClients = pImpl->GetIPClients_Impl();
3638     if ( rClients.empty() )
3639         return;
3640 
3641     for (SfxInPlaceClient* pIPClient : rClients)
3642     {
3643         if( pIPClient != pIP )
3644             pIPClient->ResetObject();
3645     }
3646 }
3647 
3648 
DisconnectAllClients()3649 void SfxViewShell::DisconnectAllClients()
3650 {
3651     std::vector< SfxInPlaceClient* >& rClients = pImpl->GetIPClients_Impl();
3652     if ( rClients.empty() )
3653         return;
3654 
3655     for ( size_t n = 0; n < rClients.size(); )
3656         // clients will remove themselves from the list
3657         delete rClients.at( n );
3658 }
3659 
3660 
QueryObjAreaPixel(tools::Rectangle &) const3661 void SfxViewShell::QueryObjAreaPixel( tools::Rectangle& ) const
3662 {
3663 }
3664 
3665 
VisAreaChanged()3666 void SfxViewShell::VisAreaChanged()
3667 {
3668     std::vector< SfxInPlaceClient* >& rClients = pImpl->GetIPClients_Impl();
3669     if ( rClients.empty() )
3670         return;
3671 
3672     for (SfxInPlaceClient* pIPClient : rClients)
3673     {
3674         if ( pIPClient->IsObjectInPlaceActive() )
3675             // client is active, notify client that the VisArea might have changed
3676             pIPClient->VisAreaChanged();
3677     }
3678 }
3679 
3680 
CheckIPClient_Impl(SfxInPlaceClient const * const pIPClient,const tools::Rectangle & rVisArea)3681 void SfxViewShell::CheckIPClient_Impl(
3682         SfxInPlaceClient const *const pIPClient, const tools::Rectangle& rVisArea)
3683 {
3684     if ( GetObjectShell()->IsInClose() )
3685         return;
3686 
3687     bool bAlwaysActive =
3688         ( ( pIPClient->GetObjectMiscStatus() & embed::EmbedMisc::EMBED_ACTIVATEIMMEDIATELY ) != 0 );
3689     bool bActiveWhenVisible =
3690         ( pIPClient->GetObjectMiscStatus() & embed::EmbedMisc::MS_EMBED_ACTIVATEWHENVISIBLE ) != 0;
3691 
3692     // this method is called when a client is created
3693     if (pIPClient->IsObjectInPlaceActive())
3694         return;
3695 
3696     // object in client is currently not active
3697     // check if the object wants to be activated always or when it becomes at least partially visible
3698     // TODO/LATER: maybe we should use the scaled area instead of the ObjArea?!
3699     if (bAlwaysActive || (bActiveWhenVisible && rVisArea.Overlaps(pIPClient->GetObjArea())))
3700     {
3701         try
3702         {
3703             pIPClient->GetObject()->changeState( embed::EmbedStates::INPLACE_ACTIVE );
3704         }
3705         catch (const uno::Exception&)
3706         {
3707             TOOLS_WARN_EXCEPTION("sfx.view", "SfxViewShell::CheckIPClient_Impl");
3708         }
3709     }
3710 }
3711 
GetObjectShell()3712 SfxObjectShell* SfxViewShell::GetObjectShell()
3713 {
3714     return rFrame.GetObjectShell();
3715 }
3716 
GetCurrentDocument() const3717 Reference< XModel > SfxViewShell::GetCurrentDocument() const
3718 {
3719     Reference< XModel > xDocument;
3720 
3721     const SfxObjectShell* pDocShell( const_cast< SfxViewShell* >( this )->GetObjectShell() );
3722     OSL_ENSURE( pDocShell, "SfxViewFrame::GetCurrentDocument: no DocShell!?" );
3723     if ( pDocShell )
3724         xDocument = pDocShell->GetModel();
3725     return xDocument;
3726 }
3727 
3728 
SetCurrentDocument() const3729 void SfxViewShell::SetCurrentDocument() const
3730 {
3731     uno::Reference< frame::XModel > xDocument( GetCurrentDocument() );
3732     if ( xDocument.is() )
3733         SfxObjectShell::SetCurrentComponent( xDocument );
3734 }
3735 
3736 
GetMargin() const3737 const Size& SfxViewShell::GetMargin() const
3738 {
3739     return pImpl->aMargin;
3740 }
3741 
3742 
SetMargin(const Size & rSize)3743 void SfxViewShell::SetMargin( const Size& rSize )
3744 {
3745     // the default margin was verified using www.apple.com !!
3746     Size aMargin = rSize;
3747     if ( aMargin.Width() == -1 )
3748         aMargin.setWidth( DEFAULT_MARGIN_WIDTH );
3749     if ( aMargin.Height() == -1 )
3750         aMargin.setHeight( DEFAULT_MARGIN_HEIGHT );
3751 
3752     if ( aMargin != pImpl->aMargin )
3753     {
3754         pImpl->aMargin = aMargin;
3755         MarginChanged();
3756     }
3757 }
3758 
MarginChanged()3759 void SfxViewShell::MarginChanged()
3760 {
3761 }
3762 
JumpToMark(const OUString & rMark)3763 void SfxViewShell::JumpToMark( const OUString& rMark )
3764 {
3765     SfxStringItem aMarkItem( SID_JUMPTOMARK, rMark );
3766     GetViewFrame().GetDispatcher()->ExecuteList(
3767         SID_JUMPTOMARK,
3768         SfxCallMode::SYNCHRON|SfxCallMode::RECORD,
3769         { &aMarkItem });
3770 }
3771 
SetController(SfxBaseController * pController)3772 void SfxViewShell::SetController( SfxBaseController* pController )
3773 {
3774     pImpl->m_pController = pController;
3775 
3776     // there should be no old listener, but if there is one, it should be disconnected
3777     if (  pImpl->xClipboardListener.is() )
3778         pImpl->xClipboardListener->DisconnectViewShell();
3779 
3780     pImpl->xClipboardListener = new SfxClipboardChangeListener( this, GetClipboardNotifier() );
3781 }
3782 
GetController() const3783 Reference < XController > SfxViewShell::GetController() const
3784 {
3785     return pImpl->m_pController;
3786 }
3787 
GetBaseController_Impl() const3788 SfxBaseController* SfxViewShell::GetBaseController_Impl() const
3789 {
3790     return pImpl->m_pController.get();
3791 }
3792 
AddContextMenuInterceptor_Impl(const uno::Reference<ui::XContextMenuInterceptor> & xInterceptor)3793 void SfxViewShell::AddContextMenuInterceptor_Impl( const uno::Reference< ui::XContextMenuInterceptor >& xInterceptor )
3794 {
3795     std::unique_lock g(pImpl->aMutex);
3796     pImpl->aInterceptorContainer.addInterface( g, xInterceptor );
3797 }
3798 
RemoveContextMenuInterceptor_Impl(const uno::Reference<ui::XContextMenuInterceptor> & xInterceptor)3799 void SfxViewShell::RemoveContextMenuInterceptor_Impl( const uno::Reference< ui::XContextMenuInterceptor >& xInterceptor )
3800 {
3801     std::unique_lock g(pImpl->aMutex);
3802     pImpl->aInterceptorContainer.removeInterface( g, xInterceptor );
3803 }
3804 
TryContextMenuInterception(const rtl::Reference<VCLXPopupMenu> & rIn,const OUString & rMenuIdentifier,rtl::Reference<VCLXPopupMenu> & rOut,ui::ContextMenuExecuteEvent aEvent)3805 bool SfxViewShell::TryContextMenuInterception(const rtl::Reference<VCLXPopupMenu>& rIn,
3806                                               const OUString& rMenuIdentifier,
3807                                               rtl::Reference<VCLXPopupMenu>& rOut,
3808                                               ui::ContextMenuExecuteEvent aEvent)
3809 {
3810     rOut.clear();
3811     bool bModified = false;
3812 
3813     // create container from menu
3814     aEvent.ActionTriggerContainer = ::framework::ActionTriggerHelper::CreateActionTriggerContainerFromMenu(
3815         rIn, &rMenuIdentifier);
3816 
3817     // get selection from controller
3818     aEvent.Selection.set( GetController(), uno::UNO_QUERY );
3819 
3820     // call interceptors
3821     std::unique_lock g(pImpl->aMutex);
3822     std::vector<uno::Reference< ui::XContextMenuInterceptor>> aInterceptors =
3823         pImpl->aInterceptorContainer.getElements(g);
3824     g.unlock();
3825     for (const auto & rListener : aInterceptors )
3826     {
3827         try
3828         {
3829             ui::ContextMenuInterceptorAction eAction;
3830             {
3831                 SolarMutexReleaser rel;
3832                 eAction = rListener->notifyContextMenuExecute( aEvent );
3833             }
3834             switch ( eAction )
3835             {
3836                 case ui::ContextMenuInterceptorAction_CANCELLED :
3837                     // interceptor does not want execution
3838                     return false;
3839                 case ui::ContextMenuInterceptorAction_EXECUTE_MODIFIED :
3840                     // interceptor wants his modified menu to be executed
3841                     bModified = true;
3842                     break;
3843                 case ui::ContextMenuInterceptorAction_CONTINUE_MODIFIED :
3844                     // interceptor has modified menu, but allows for calling other interceptors
3845                     bModified = true;
3846                     continue;
3847                 case ui::ContextMenuInterceptorAction_IGNORED :
3848                     // interceptor is indifferent
3849                     continue;
3850                 default:
3851                     OSL_FAIL("Wrong return value of ContextMenuInterceptor!");
3852                     continue;
3853             }
3854         }
3855         catch (...)
3856         {
3857             g.lock();
3858             pImpl->aInterceptorContainer.removeInterface(g, rListener);
3859             g.unlock();
3860         }
3861 
3862         break;
3863     }
3864 
3865     if (bModified)
3866     {
3867         // container was modified, create a new menu out of it
3868         rOut = new VCLXPopupMenu();
3869         ::framework::ActionTriggerHelper::CreateMenuFromActionTriggerContainer(rOut, aEvent.ActionTriggerContainer);
3870     }
3871 
3872     return true;
3873 }
3874 
TryContextMenuInterception(const rtl::Reference<VCLXPopupMenu> & rPopupMenu,const OUString & rMenuIdentifier,css::ui::ContextMenuExecuteEvent aEvent)3875 bool SfxViewShell::TryContextMenuInterception(const rtl::Reference<VCLXPopupMenu>& rPopupMenu,
3876                                               const OUString& rMenuIdentifier, css::ui::ContextMenuExecuteEvent aEvent)
3877 {
3878     bool bModified = false;
3879 
3880     // create container from menu
3881     aEvent.ActionTriggerContainer = ::framework::ActionTriggerHelper::CreateActionTriggerContainerFromMenu(
3882         rPopupMenu, &rMenuIdentifier);
3883 
3884     // get selection from controller
3885     aEvent.Selection = css::uno::Reference< css::view::XSelectionSupplier >( GetController(), css::uno::UNO_QUERY );
3886 
3887     // call interceptors
3888     std::unique_lock g(pImpl->aMutex);
3889     std::vector<uno::Reference< ui::XContextMenuInterceptor>> aInterceptors =
3890         pImpl->aInterceptorContainer.getElements(g);
3891     g.unlock();
3892     for (const auto & rListener : aInterceptors )
3893     {
3894         try
3895         {
3896             css::ui::ContextMenuInterceptorAction eAction;
3897             {
3898                 SolarMutexReleaser rel;
3899                 eAction = rListener->notifyContextMenuExecute( aEvent );
3900             }
3901             switch ( eAction )
3902             {
3903                 case css::ui::ContextMenuInterceptorAction_CANCELLED:
3904                     // interceptor does not want execution
3905                     return false;
3906                 case css::ui::ContextMenuInterceptorAction_EXECUTE_MODIFIED:
3907                     // interceptor wants his modified menu to be executed
3908                     bModified = true;
3909                     break;
3910                 case css::ui::ContextMenuInterceptorAction_CONTINUE_MODIFIED:
3911                     // interceptor has modified menu, but allows for calling other interceptors
3912                     bModified = true;
3913                     continue;
3914                 case css::ui::ContextMenuInterceptorAction_IGNORED:
3915                     // interceptor is indifferent
3916                     continue;
3917                 default:
3918                     SAL_WARN( "sfx.view", "Wrong return value of ContextMenuInterceptor!" );
3919                     continue;
3920             }
3921         }
3922         catch (...)
3923         {
3924             g.lock();
3925             pImpl->aInterceptorContainer.removeInterface(g, rListener);
3926             g.unlock();
3927         }
3928 
3929         break;
3930     }
3931 
3932     if ( bModified )
3933     {
3934         rPopupMenu->clear();
3935         ::framework::ActionTriggerHelper::CreateMenuFromActionTriggerContainer(rPopupMenu, aEvent.ActionTriggerContainer);
3936     }
3937 
3938     return true;
3939 }
3940 
HandleNotifyEvent_Impl(NotifyEvent const & rEvent)3941 bool SfxViewShell::HandleNotifyEvent_Impl( NotifyEvent const & rEvent )
3942 {
3943     if (pImpl->m_pController.is())
3944         return pImpl->m_pController->HandleEvent_Impl( rEvent );
3945     return false;
3946 }
3947 
HasKeyListeners_Impl() const3948 bool SfxViewShell::HasKeyListeners_Impl() const
3949 {
3950     return (pImpl->m_pController.is())
3951         && pImpl->m_pController->HasKeyListeners_Impl();
3952 }
3953 
HasMouseClickListeners_Impl() const3954 bool SfxViewShell::HasMouseClickListeners_Impl() const
3955 {
3956     return (pImpl->m_pController.is())
3957         && pImpl->m_pController->HasMouseClickListeners_Impl();
3958 }
3959 
Escape()3960 bool SfxViewShell::Escape()
3961 {
3962     return GetViewFrame().GetBindings().Execute(SID_TERMINATE_INPLACEACTIVATION).is();
3963 }
3964 
GetRenderable()3965 Reference< view::XRenderable > SfxViewShell::GetRenderable()
3966 {
3967     Reference< view::XRenderable >xRender;
3968     SfxObjectShell* pObj = GetObjectShell();
3969     if( pObj )
3970     {
3971         Reference< frame::XModel > xModel( pObj->GetModel() );
3972         if( xModel.is() )
3973             xRender.set( xModel, UNO_QUERY );
3974     }
3975     return xRender;
3976 }
3977 
notifyWindow(vcl::LOKWindowId nDialogId,const OUString & rAction,const std::vector<vcl::LOKPayloadItem> & rPayload) const3978 void SfxViewShell::notifyWindow(vcl::LOKWindowId nDialogId, const OUString& rAction, const std::vector<vcl::LOKPayloadItem>& rPayload) const
3979 {
3980     SfxLokHelper::notifyWindow(this, nDialogId, rAction, rPayload);
3981 }
3982 
dumpNotifyState() const3983 OString SfxViewShell::dumpNotifyState() const
3984 {
3985     return OString("sfxviewsh: " +
3986                    OString::number(reinterpret_cast<sal_uInt64>(this), 16) +
3987                    " doc: " + OString::number(static_cast<sal_Int32>(static_cast<int>(GetDocId()))) +
3988                    " view: " +
3989                    OString::number(static_cast<sal_Int32>(GetViewShellId())));
3990 }
3991 
GetClipboardNotifier() const3992 uno::Reference< datatransfer::clipboard::XClipboardNotifier > SfxViewShell::GetClipboardNotifier() const
3993 {
3994     uno::Reference< datatransfer::clipboard::XClipboardNotifier > xClipboardNotifier;
3995     xClipboardNotifier.set(GetViewFrame().GetWindow().GetClipboard(), uno::UNO_QUERY);
3996     return xClipboardNotifier;
3997 }
3998 
AddRemoveClipboardListener(const uno::Reference<datatransfer::clipboard::XClipboardListener> & rClp,bool bAdd)3999 void SfxViewShell::AddRemoveClipboardListener( const uno::Reference < datatransfer::clipboard::XClipboardListener >& rClp, bool bAdd )
4000 {
4001     try
4002     {
4003         uno::Reference< datatransfer::clipboard::XClipboard > xClipboard(GetViewFrame().GetWindow().GetClipboard());
4004         if( xClipboard.is() )
4005         {
4006             uno::Reference< datatransfer::clipboard::XClipboardNotifier > xClpbrdNtfr( xClipboard, uno::UNO_QUERY );
4007             if( xClpbrdNtfr.is() )
4008             {
4009                 if( bAdd )
4010                     xClpbrdNtfr->addClipboardListener( rClp );
4011                 else
4012                     xClpbrdNtfr->removeClipboardListener( rClp );
4013             }
4014         }
4015     }
4016     catch (const uno::Exception&)
4017     {
4018     }
4019 }
4020 
GetFrameWeld() const4021 weld::Window* SfxViewShell::GetFrameWeld() const
4022 {
4023     return pWindow ? pWindow->GetFrameWeld() : nullptr;
4024 }
4025 
setBlockedCommandList(const char * blockedCommandList)4026 void SfxViewShell::setBlockedCommandList(const char* blockedCommandList)
4027 {
4028     if(!mvLOKBlockedCommandList.empty())
4029         return;
4030 
4031     OUString BlockedListString(blockedCommandList, strlen(blockedCommandList), RTL_TEXTENCODING_UTF8);
4032     OUString command = BlockedListString.getToken(0, ' ');
4033     for (size_t i = 1; !command.isEmpty(); i++)
4034     {
4035         mvLOKBlockedCommandList.emplace(command);
4036         command = BlockedListString.getToken(i, ' ');
4037     }
4038 }
4039 
isBlockedCommand(const OUString & command) const4040 bool SfxViewShell::isBlockedCommand(const OUString & command) const
4041 {
4042     return mvLOKBlockedCommandList.find(command) != mvLOKBlockedCommandList.end();
4043 }
4044 
4045 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
4046