Coverage Report

Created: 2026-09-28 10:59

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