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