Coverage Report

Created: 2026-04-09 11:41

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/sfx2/source/view/lokhelper.cxx
Line
Count
Source
1
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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
10
#include <sal/config.h>
11
12
#include <string>
13
#include <string_view>
14
#include <list>
15
#include <mutex>
16
17
#include <sfx2/lokcomponenthelpers.hxx>
18
#include <sfx2/lokhelper.hxx>
19
#include <sfx2/lokunocmdlist.hxx>
20
21
#include <com/sun/star/frame/Desktop.hpp>
22
#include <com/sun/star/ui/ContextChangeEventObject.hpp>
23
#include <com/sun/star/xml/crypto/SEInitializer.hpp>
24
#include <com/sun/star/xml/crypto/XCertificateCreator.hpp>
25
26
#include <comphelper/dispatchcommand.hxx>
27
#include <comphelper/JsonToPropertyValues_with_boost.hxx>
28
#include <comphelper/processfactory.hxx>
29
#include <comphelper/propertyvalue.hxx>
30
#include <comphelper/sequence.hxx>
31
#include <o3tl/string_view.hxx>
32
#include <rtl/strbuf.hxx>
33
#include <vcl/lok.hxx>
34
#include <vcl/svapp.hxx>
35
#include <vcl/commandevent.hxx>
36
#include <vcl/vclevent.hxx>
37
#include <vcl/window.hxx>
38
#include <sal/log.hxx>
39
#include <sfx2/app.hxx>
40
#include <sfx2/docfile.hxx>
41
#include <sfx2/msg.hxx>
42
#include <sfx2/viewsh.hxx>
43
#include <sfx2/request.hxx>
44
#include <sfx2/sfxsids.hrc>
45
#include <sfx2/viewfrm.hxx>
46
#include <LibreOfficeKit/LibreOfficeKitEnums.h>
47
#include <comphelper/lok.hxx>
48
#include <sfx2/msgpool.hxx>
49
#include <comphelper/scopeguard.hxx>
50
#include <comphelper/base64.hxx>
51
#include <tools/json_writer.hxx>
52
#include <svl/cryptosign.hxx>
53
#include <tools/urlobj.hxx>
54
55
#include <boost/property_tree/json_parser.hpp>
56
57
using namespace com::sun::star;
58
59
namespace {
60
bool g_bSettingView(false);
61
62
/// Used to disable callbacks.
63
/// Needed to avoid recursion when switching views,
64
/// which can cause clients to invoke LOKit API and
65
/// implicitly set the view, which might cause an
66
/// infinite recursion if not detected and prevented.
67
class DisableCallbacks
68
{
69
public:
70
    DisableCallbacks()
71
0
    {
72
0
        assert(m_nDisabled >= 0 && "Expected non-negative DisabledCallbacks state when disabling.");
73
0
        ++m_nDisabled;
74
0
    }
75
76
    ~DisableCallbacks()
77
0
    {
78
0
        assert(m_nDisabled > 0 && "Expected positive DisabledCallbacks state when re-enabling.");
79
0
        --m_nDisabled;
80
0
    }
81
82
    static inline bool disabled()
83
11.8k
    {
84
11.8k
        return !comphelper::LibreOfficeKit::isActive() || m_nDisabled != 0;
85
11.8k
    }
86
87
private:
88
    static int m_nDisabled;
89
};
90
91
int DisableCallbacks::m_nDisabled = 0;
92
}
93
94
namespace
95
{
96
LanguageTag g_defaultLanguageTag(u"en-US"_ustr, true);
97
LanguageTag g_loadLanguageTag(u"en-US"_ustr, true); //< The language used to load.
98
LOKDeviceFormFactor g_deviceFormFactor = LOKDeviceFormFactor::UNKNOWN;
99
bool g_isDefaultTimezoneSet = false;
100
OUString g_DefaultTimezone;
101
const std::size_t g_logNotifierCacheMaxSize = 50;
102
::std::list<::std::string> g_logNotifierCache;
103
}
104
105
#if !defined NDEBUG
106
static bool isSfxMediumMissingInteractionHandled(SfxViewFrame& rViewFrame)
107
{
108
    const SfxObjectShell* pObjSh = rViewFrame.GetObjectShell();
109
    const SfxMedium* pMed = pObjSh ? pObjSh->GetMedium() : nullptr;
110
    if (!pMed)
111
    {
112
        // In this unlikely case it's not going to matter.
113
        return false;
114
    }
115
    const SfxUnoAnyItem *pItem = pMed->GetItemSet().GetItemIfSet(SID_INTERACTIONHANDLER, false);
116
    return !pItem || !pItem->GetValue().hasValue();
117
}
118
#endif
119
120
int SfxLokHelper::createView(SfxViewFrame& rViewFrame, ViewShellDocId docId)
121
0
{
122
0
    assert(docId >= ViewShellDocId(0) && "Cannot createView for invalid (negative) DocId.");
123
124
    // The XInteractionHandler installed during the original lo_documentLoad
125
    // should still be present when new views are created. If it is not
126
    // something has likely cleared the original SID_INTERACTIONHANDLER
127
0
    assert(!isSfxMediumMissingInteractionHandled(rViewFrame) && "original XInteractionHandler missing");
128
129
0
    comphelper::LibreOfficeKit::setDocId(docId);
130
0
    SfxRequest aRequest(rViewFrame, SID_NEWWINDOW);
131
0
    rViewFrame.ExecView_Impl(aRequest);
132
0
    SfxViewShell* pViewShell = SfxViewShell::Current();
133
0
    if (pViewShell == nullptr)
134
0
        return -1;
135
136
0
    assert(pViewShell->GetDocId() == docId && "DocId must be already set!");
137
0
    return static_cast<sal_Int32>(pViewShell->GetViewShellId());
138
0
}
139
140
int SfxLokHelper::createView()
141
0
{
142
    // Assumes a single document, or at least that the
143
    // current view belongs to the document on which the
144
    // view will be created.
145
0
    SfxViewShell* pViewShell = SfxViewShell::Current();
146
0
    if (pViewShell == nullptr)
147
0
        return -1;
148
149
0
    return createView(pViewShell->GetViewFrame(), pViewShell->GetDocId());
150
0
}
151
152
std::unordered_map<OUString, css::uno::Reference<css::ui::XAcceleratorConfiguration>>& SfxLokHelper::getAcceleratorConfs()
153
0
{
154
0
    return SfxApplication::GetOrCreate()->GetAcceleratorConfs_Impl();
155
0
}
156
157
int SfxLokHelper::createView(int nDocId)
158
0
{
159
0
    const SfxApplication* pApp = SfxApplication::Get();
160
0
    if (pApp == nullptr)
161
0
        return -1;
162
163
    // Find a shell with the given DocId.
164
0
    const ViewShellDocId docId(nDocId);
165
0
    for (const SfxViewShell* pViewShell : pApp->GetViewShells_Impl())
166
0
    {
167
0
        if (pViewShell->GetDocId() == docId)
168
0
            return createView(pViewShell->GetViewFrame(), docId);
169
0
    }
170
171
    // No frame with nDocId found.
172
0
    return -1;
173
0
}
174
175
void SfxLokHelper::setEditMode(int nMode, vcl::ITiledRenderable* pDoc)
176
0
{
177
0
    DisableCallbacks dc;
178
0
    pDoc->setEditMode(nMode);
179
0
}
180
181
void SfxLokHelper::destroyView(int nId)
182
0
{
183
0
    if (SfxViewShell* pViewShell = getViewOfId(nId))
184
0
    {
185
0
        pViewShell->SetLOKAccessibilityState(false);
186
0
        SfxViewFrame& rViewFrame = pViewShell->GetViewFrame();
187
0
        SfxRequest aRequest(rViewFrame, SID_CLOSEWIN);
188
0
        rViewFrame.Exec_Impl(aRequest);
189
0
    }
190
0
}
191
192
bool SfxLokHelper::isSettingView()
193
0
{
194
0
    return g_bSettingView;
195
0
}
196
197
void SfxLokHelper::setView(int nId)
198
0
{
199
0
    g_bSettingView = true;
200
0
    comphelper::ScopeGuard g([] { g_bSettingView = false; });
201
202
0
    const SfxViewShell* pViewShell = getViewOfId(nId);
203
0
    if (!pViewShell)
204
0
        return;
205
206
0
    DisableCallbacks dc;
207
208
0
    bool bIsCurrShell = (pViewShell == SfxViewShell::Current());
209
0
    if (bIsCurrShell && comphelper::LibreOfficeKit::getLanguageTag().getBcp47() == pViewShell->GetLOKLanguageTag().getBcp47())
210
0
        return;
211
212
0
    if (bIsCurrShell)
213
0
    {
214
        // If we wanted to set the SfxViewShell that is actually set, we could skip it.
215
        // But it looks like that the language can go wrong, so we have to fix that.
216
        // This can happen, when someone sets the language or SfxViewShell::Current() separately.
217
0
        SAL_WARN("lok", "LANGUAGE mismatch at setView! ... old (wrong) lang:"
218
0
                        << comphelper::LibreOfficeKit::getLanguageTag().getBcp47()
219
0
                        << " new lang:" << pViewShell->GetLOKLanguageTag().getBcp47());
220
0
    }
221
222
    // update the current LOK language and locale for the dialog tunneling
223
0
    comphelper::LibreOfficeKit::setLanguageTag(pViewShell->GetLOKLanguageTag());
224
0
    comphelper::LibreOfficeKit::setLocale(pViewShell->GetLOKLocale());
225
226
0
    if (bIsCurrShell)
227
0
        return;
228
229
0
    SfxViewFrame& rViewFrame = pViewShell->GetViewFrame();
230
0
    rViewFrame.MakeActive_Impl(false);
231
232
    // Make comphelper::dispatchCommand() find the correct frame.
233
0
    uno::Reference<frame::XFrame> xFrame = rViewFrame.GetFrame().GetFrameInterface();
234
0
    uno::Reference<frame::XDesktop2> xDesktop = frame::Desktop::create(comphelper::getProcessComponentContext());
235
0
    xDesktop->setActiveFrame(xFrame);
236
0
}
237
238
SfxViewShell* SfxLokHelper::getViewOfId(int nId)
239
0
{
240
0
    SfxApplication* pApp = SfxApplication::Get();
241
0
    if (pApp == nullptr)
242
0
        return nullptr;
243
244
0
    const ViewShellId nViewShellId(nId);
245
0
    std::vector<SfxViewShell*>& rViewArr = pApp->GetViewShells_Impl();
246
0
    for (SfxViewShell* pViewShell : rViewArr)
247
0
    {
248
0
        if (pViewShell->GetViewShellId() == nViewShellId)
249
0
            return pViewShell;
250
0
    }
251
252
0
    return nullptr;
253
0
}
254
255
int SfxLokHelper::getView(const SfxViewShell& rViewShell)
256
0
{
257
0
    return static_cast<sal_Int32>(rViewShell.GetViewShellId());
258
0
}
259
260
int SfxLokHelper::getCurrentView()
261
0
{
262
0
    SfxViewShell* pViewShell = SfxViewShell::Current();
263
    // No valid view shell? Then no idea.
264
0
    if (!pViewShell)
265
0
        return -1;
266
0
    return SfxLokHelper::getView(*pViewShell);
267
0
}
268
269
std::size_t SfxLokHelper::getViewsCount(int nDocId)
270
0
{
271
0
    assert(nDocId != -1 && "Cannot getViewsCount for invalid DocId -1");
272
273
0
    SfxApplication* pApp = SfxApplication::Get();
274
0
    if (!pApp)
275
0
        return 0;
276
277
0
    const ViewShellDocId nCurrentDocId(nDocId);
278
0
    std::size_t n = 0;
279
0
    SfxViewShell* pViewShell = SfxViewShell::GetFirst();
280
0
    while (pViewShell)
281
0
    {
282
0
        if (pViewShell->GetDocId() == nCurrentDocId)
283
0
            n++;
284
0
        pViewShell = SfxViewShell::GetNext(*pViewShell);
285
0
    }
286
287
0
    return n;
288
0
}
289
290
// SfxApplication::SetViewFrame_Impl ensures that ViewShells
291
// are kept in MRU order
292
int SfxLokHelper::getViewId(int nDocId)
293
0
{
294
0
    assert(nDocId != -1 && "Cannot getViewId for invalid DocId -1");
295
296
0
    SfxApplication* pApp = SfxApplication::Get();
297
0
    if (!pApp)
298
0
        return -1;
299
300
0
    SfxViewShell* pViewShell = SfxViewShell::GetFirst();
301
0
    while (pViewShell)
302
0
    {
303
0
        if (pViewShell->GetDocId().get() == nDocId)
304
0
            return pViewShell->GetViewShellId().get();
305
0
        pViewShell = SfxViewShell::GetNext(*pViewShell);
306
0
    }
307
308
0
    return -1;
309
0
}
310
311
std::size_t SfxLokHelper::getDocsCount()
312
0
{
313
0
    SfxApplication* pApp = SfxApplication::Get();
314
0
    if (!pApp)
315
0
        return 0;
316
317
0
    std::set<ViewShellDocId> aDocs;
318
319
0
    SfxViewShell* pViewShell = SfxViewShell::GetFirst();
320
0
    while (pViewShell)
321
0
    {
322
0
        aDocs.insert(pViewShell->GetDocId());
323
0
        pViewShell = SfxViewShell::GetNext(*pViewShell);
324
0
    }
325
326
0
    return aDocs.size();
327
0
}
328
329
bool SfxLokHelper::getViewIds(int nDocId, int* pArray, size_t nSize)
330
0
{
331
0
    assert(nDocId != -1 && "Cannot getViewsIds for invalid DocId -1");
332
333
0
    SfxApplication* pApp = SfxApplication::Get();
334
0
    if (!pApp)
335
0
        return false;
336
337
0
    const ViewShellDocId nCurrentDocId(nDocId);
338
0
    std::size_t n = 0;
339
0
    SfxViewShell* pViewShell = SfxViewShell::GetFirst();
340
0
    while (pViewShell)
341
0
    {
342
0
        if (pViewShell->GetDocId() == nCurrentDocId)
343
0
        {
344
0
            if (n == nSize)
345
0
                return false;
346
347
0
            pArray[n] = static_cast<sal_Int32>(pViewShell->GetViewShellId());
348
0
            n++;
349
0
        }
350
351
0
        pViewShell = SfxViewShell::GetNext(*pViewShell);
352
0
    }
353
354
0
    return true;
355
0
}
356
357
int SfxLokHelper::getDocumentIdOfView(int nViewId)
358
0
{
359
0
    SfxViewShell* pViewShell = SfxViewShell::GetFirst();
360
0
    while (pViewShell)
361
0
    {
362
0
        if (pViewShell->GetViewShellId() == ViewShellId(nViewId))
363
0
            return static_cast<int>(pViewShell->GetDocId());
364
0
        pViewShell = SfxViewShell::GetNext(*pViewShell);
365
0
    }
366
0
    return -1;
367
0
}
368
369
const LanguageTag & SfxLokHelper::getDefaultLanguage()
370
0
{
371
0
    return g_defaultLanguageTag;
372
0
}
373
374
void SfxLokHelper::setDefaultLanguage(const OUString& rBcp47LanguageTag)
375
0
{
376
0
    g_defaultLanguageTag = LanguageTag(rBcp47LanguageTag, true);
377
0
}
378
379
0
const LanguageTag& SfxLokHelper::getLoadLanguage() { return g_loadLanguageTag; }
380
381
void SfxLokHelper::setLoadLanguage(const OUString& rBcp47LanguageTag)
382
0
{
383
0
    g_loadLanguageTag = LanguageTag(rBcp47LanguageTag, true);
384
0
}
385
386
void SfxLokHelper::setViewLanguage(int nId, const OUString& rBcp47LanguageTag)
387
0
{
388
0
    if (SfxViewShell* pViewShell = getViewOfId(nId))
389
0
    {
390
0
        pViewShell->SetLOKLanguageTag(rBcp47LanguageTag);
391
        // sync also global getter if we are the current view
392
0
        bool bIsCurrShell = (pViewShell == SfxViewShell::Current());
393
0
        if (bIsCurrShell)
394
0
            comphelper::LibreOfficeKit::setLanguageTag(LanguageTag(rBcp47LanguageTag));
395
0
    }
396
0
}
397
398
void SfxLokHelper::setViewLanguageAndLocale(int nId, const OUString& rBcp47LanguageTag)
399
0
{
400
0
    std::vector<SfxViewShell*>& rViewArr = SfxGetpApp()->GetViewShells_Impl();
401
402
0
    for (SfxViewShell* pViewShell : rViewArr)
403
0
    {
404
0
        if (pViewShell->GetViewShellId() == ViewShellId(nId))
405
0
        {
406
0
            pViewShell->SetLOKLanguageAndLocale(rBcp47LanguageTag);
407
            // sync also global getter if we are the current view
408
0
            bool bIsCurrShell = (pViewShell == SfxViewShell::Current());
409
0
            if (bIsCurrShell)
410
0
            {
411
0
                comphelper::LibreOfficeKit::setLanguageTag(LanguageTag(rBcp47LanguageTag));
412
0
                comphelper::LibreOfficeKit::setLocale(LanguageTag(rBcp47LanguageTag));
413
0
            }
414
0
            return;
415
0
        }
416
0
    }
417
0
}
418
419
void SfxLokHelper::setViewReadOnly(int nId, bool readOnly)
420
0
{
421
0
    if (SfxViewShell* pViewShell = getViewOfId(nId))
422
0
    {
423
0
        LOK_INFO("lok.readonlyview", "SfxLokHelper::setViewReadOnly: view id: " << nId << ", readOnly: " << readOnly);
424
0
        pViewShell->SetLokReadOnlyView(readOnly);
425
0
    }
426
0
}
427
428
void SfxLokHelper::setAllowChangeComments(int nId, bool allow)
429
0
{
430
0
    if (SfxViewShell* pViewShell = getViewOfId(nId))
431
0
    {
432
0
        LOK_INFO("lok.readonlyview", "SfxLokHelper::setAllowChangeComments: view id: " << nId << ", allow: " << allow);
433
0
        pViewShell->SetAllowChangeComments(allow);
434
0
    }
435
0
}
436
437
void SfxLokHelper::setAllowManageRedlines(int nId, bool allow)
438
0
{
439
0
    if (SfxViewShell* pViewShell = getViewOfId(nId))
440
0
    {
441
0
        LOK_INFO("lok.readonlyview", "SfxLokHelper::setAllowManageRedlines: view id: " << nId << ", allow: " << allow);
442
0
        pViewShell->SetAllowManageRedlines(allow);
443
0
    }
444
0
}
445
446
void SfxLokHelper::setAccessibilityState(int nId, bool nEnabled)
447
0
{
448
0
    if (SfxViewShell* pViewShell = getViewOfId(nId))
449
0
    {
450
0
        LOK_INFO("lok.a11y", "SfxLokHelper::setAccessibilityState: view id: " << nId << ", nEnabled: " << nEnabled);
451
0
        pViewShell->SetLOKAccessibilityState(nEnabled);
452
0
    }
453
0
}
454
455
void SfxLokHelper::setViewLocale(int nId, const OUString& rBcp47LanguageTag)
456
0
{
457
0
    if (SfxViewShell* pViewShell = getViewOfId(nId))
458
0
    {
459
0
        pViewShell->SetLOKLocale(rBcp47LanguageTag);
460
0
        if (pViewShell->GetViewShellId() == ViewShellId(nId))
461
0
        {
462
            // sync also global getter if we are the current view
463
0
            bool bIsCurrShell = (pViewShell == SfxViewShell::Current());
464
0
            if (bIsCurrShell)
465
0
            {
466
0
                comphelper::LibreOfficeKit::setLocale(LanguageTag(rBcp47LanguageTag));
467
0
            }
468
0
            return;
469
0
        }
470
0
    }
471
0
}
472
473
LOKDeviceFormFactor SfxLokHelper::getDeviceFormFactor()
474
0
{
475
0
    return g_deviceFormFactor;
476
0
}
477
478
void SfxLokHelper::setDeviceFormFactor(std::u16string_view rDeviceFormFactor)
479
0
{
480
0
    if (rDeviceFormFactor == u"desktop")
481
0
        g_deviceFormFactor = LOKDeviceFormFactor::DESKTOP;
482
0
    else if (rDeviceFormFactor == u"tablet")
483
0
        g_deviceFormFactor = LOKDeviceFormFactor::TABLET;
484
0
    else if (rDeviceFormFactor == u"mobile")
485
0
        g_deviceFormFactor = LOKDeviceFormFactor::MOBILE;
486
0
    else
487
0
        g_deviceFormFactor = LOKDeviceFormFactor::UNKNOWN;
488
0
}
489
490
void SfxLokHelper::setColorPreviewState(int nId, bool nEnabled)
491
0
{
492
0
    std::vector<SfxViewShell*>& rViewArr = SfxGetpApp()->GetViewShells_Impl();
493
494
0
    for (SfxViewShell* pViewShell : rViewArr)
495
0
    {
496
0
        if (pViewShell && pViewShell->GetViewShellId() == ViewShellId(nId))
497
0
        {
498
0
            pViewShell->SetLOKColorPreviewState(nEnabled);
499
0
            return;
500
0
        }
501
0
    }
502
0
}
503
504
void SfxLokHelper::setDefaultTimezone(bool isSet, const OUString& rTimezone)
505
0
{
506
0
    g_isDefaultTimezoneSet = isSet;
507
0
    g_DefaultTimezone = rTimezone;
508
0
}
509
510
std::pair<bool, OUString> SfxLokHelper::getDefaultTimezone()
511
0
{
512
0
    return { g_isDefaultTimezoneSet, g_DefaultTimezone };
513
0
}
514
515
void SfxLokHelper::setViewTimezone(int nId, bool isSet, const OUString& rTimezone)
516
0
{
517
0
    if (SfxViewShell* pViewShell = getViewOfId(nId))
518
0
    {
519
0
        pViewShell->SetLOKTimezone(isSet, rTimezone);
520
0
    }
521
0
}
522
523
std::pair<bool, OUString> SfxLokHelper::getViewTimezone(int nId)
524
0
{
525
0
    if (SfxViewShell* pViewShell = getViewOfId(nId))
526
0
    {
527
0
        return pViewShell->GetLOKTimezone();
528
0
    }
529
530
0
    return {};
531
0
}
532
533
/*
534
* Used for putting a whole JSON string into a string value
535
* e.g { key: "{JSON}" }
536
*/
537
static OString lcl_sanitizeJSONAsValue(const OString &rStr)
538
0
{
539
0
    if (rStr.getLength() < 1)
540
0
        return rStr;
541
    // FIXME: need an optimized 'escape' method for O[U]String.
542
0
    OStringBuffer aBuf(rStr.getLength() + 8);
543
0
    for (sal_Int32 i = 0; i < rStr.getLength(); ++i)
544
0
    {
545
0
        if (rStr[i] == '"' || rStr[i] == '\\')
546
0
            aBuf.append('\\');
547
548
0
        if (rStr[i] != '\n')
549
0
            aBuf.append(rStr[i]);
550
0
    }
551
0
    return aBuf.makeStringAndClear();
552
0
}
553
554
static OString lcl_generateJSON(const SfxViewShell& rView, const boost::property_tree::ptree& rTree)
555
0
{
556
0
    boost::property_tree::ptree aMessageProps = rTree;
557
0
    aMessageProps.put("viewId", SfxLokHelper::getView(rView));
558
0
    aMessageProps.put("part", rView.getPart());
559
0
    aMessageProps.put("mode", rView.getEditMode());
560
0
    std::stringstream aStream;
561
0
    boost::property_tree::write_json(aStream, aMessageProps, false /* pretty */);
562
0
    return OString(o3tl::trim(aStream.str()));
563
0
}
564
565
static inline OString lcl_generateJSON(const SfxViewShell& rView, int nViewId, std::string_view rKey,
566
                                       const OString& rPayload)
567
0
{
568
0
    return OString::Concat("{ \"viewId\": \"") + OString::number(nViewId)
569
0
           + "\", \"part\": \"" + OString::number(rView.getPart()) + "\", \"mode\": \""
570
0
           + OString::number(rView.getEditMode()) + "\", \"" + rKey + "\": \""
571
0
           + lcl_sanitizeJSONAsValue(rPayload) + "\" }";
572
0
}
573
574
static inline OString lcl_generateJSON(const SfxViewShell& rView, std::string_view rKey,
575
                                       const OString& rPayload)
576
0
{
577
0
    return lcl_generateJSON(rView, SfxLokHelper::getView(rView), rKey, rPayload);
578
0
}
579
580
void SfxLokHelper::notifyOtherView(const SfxViewShell& rThisView, SfxViewShell const* pOtherView,
581
                                   int nType, std::string_view rKey, const OString& rPayload)
582
0
{
583
0
    if (DisableCallbacks::disabled())
584
0
        return;
585
586
0
    const OString aPayload = lcl_generateJSON(rThisView, rKey, rPayload);
587
0
    const int viewId = SfxLokHelper::getView(rThisView);
588
0
    pOtherView->libreOfficeKitViewCallbackWithViewId(nType, aPayload, viewId);
589
0
}
590
591
void SfxLokHelper::notifyOtherView(const SfxViewShell& rThisView, SfxViewShell const* pOtherView,
592
                                   int nType, const boost::property_tree::ptree& rTree)
593
0
{
594
0
    if (DisableCallbacks::disabled() || !pOtherView)
595
0
        return;
596
597
0
    const int viewId = SfxLokHelper::getView(rThisView);
598
0
    pOtherView->libreOfficeKitViewCallbackWithViewId(nType, lcl_generateJSON(rThisView, rTree), viewId);
599
0
}
600
601
void SfxLokHelper::notifyOtherViews(const SfxViewShell* pThisView, int nType, std::string_view rKey,
602
                                    const OString& rPayload)
603
11.8k
{
604
11.8k
    assert(pThisView != nullptr && "pThisView must be valid");
605
11.8k
    if (DisableCallbacks::disabled())
606
11.8k
        return;
607
608
    // Cache the payload so we only have to generate it once, at most.
609
0
    OString aPayload;
610
0
    int viewId = -1;
611
612
0
    const ViewShellDocId nCurrentDocId = pThisView->GetDocId();
613
0
    SfxViewShell* pViewShell = SfxViewShell::GetFirst();
614
0
    while (pViewShell)
615
0
    {
616
0
        if (pViewShell != pThisView && nCurrentDocId == pViewShell->GetDocId())
617
0
        {
618
            // Payload is only dependent on pThisView.
619
0
            if (aPayload.isEmpty())
620
0
            {
621
0
                aPayload = lcl_generateJSON(*pThisView, rKey, rPayload);
622
0
                viewId = SfxLokHelper::getView(*pThisView);
623
0
            }
624
625
0
            pViewShell->libreOfficeKitViewCallbackWithViewId(nType, aPayload, viewId);
626
0
        }
627
628
0
        pViewShell = SfxViewShell::GetNext(*pViewShell);
629
0
    }
630
0
}
631
632
void SfxLokHelper::notifyOtherViews(const SfxViewShell* pThisView, int nType,
633
                                    const boost::property_tree::ptree& rTree)
634
0
{
635
0
    assert(pThisView != nullptr && "pThisView must be valid");
636
0
    if (!pThisView || DisableCallbacks::disabled())
637
0
        return;
638
639
    // Cache the payload so we only have to generate it once, at most.
640
0
    OString aPayload;
641
0
    int viewId = -1;
642
643
0
    const ViewShellDocId nCurrentDocId = pThisView->GetDocId();
644
0
    SfxViewShell* pViewShell = SfxViewShell::GetFirst();
645
0
    while (pViewShell)
646
0
    {
647
0
        if (pViewShell != pThisView && nCurrentDocId == pViewShell->GetDocId())
648
0
        {
649
            // Payload is only dependent on pThisView.
650
0
            if (aPayload.isEmpty())
651
0
            {
652
0
                aPayload = lcl_generateJSON(*pThisView, rTree);
653
0
                viewId = SfxLokHelper::getView(*pThisView);
654
0
            }
655
656
0
            pViewShell->libreOfficeKitViewCallbackWithViewId(nType, aPayload, viewId);
657
0
        }
658
659
0
        pViewShell = SfxViewShell::GetNext(*pViewShell);
660
0
    }
661
0
}
662
663
OString SfxLokHelper::makePayloadJSON(const SfxViewShell* pThisView, int nViewId, std::string_view rKey, const OString& rPayload)
664
0
{
665
0
    return lcl_generateJSON(*pThisView, nViewId, rKey, rPayload);
666
0
}
667
668
namespace {
669
    OUString lcl_getNameForSlot(const SfxViewShell* pShell, sal_uInt16 nWhich)
670
0
    {
671
0
        if (pShell && pShell->GetFrame())
672
0
        {
673
0
            const SfxSlot* pSlot = SfxSlotPool::GetSlotPool(pShell->GetFrame()).GetSlot(nWhich);
674
0
            if (pSlot)
675
0
            {
676
0
                if (!pSlot->GetUnoName().isEmpty())
677
0
                {
678
0
                    return pSlot->GetCommand();
679
0
                }
680
0
            }
681
0
        }
682
683
0
        return u""_ustr;
684
0
    }
685
}
686
687
void SfxLokHelper::sendUnoStatus(const SfxViewShell* pShell, const SfxPoolItem* pItem)
688
0
{
689
0
    if (!pShell || !pItem || IsInvalidItem(pItem) || DisableCallbacks::disabled())
690
0
        return;
691
692
0
    boost::property_tree::ptree aItem = pItem->dumpAsJSON();
693
694
0
    if (aItem.count("state"))
695
0
    {
696
0
        OUString sCommand = lcl_getNameForSlot(pShell, pItem->Which());
697
0
        if (!sCommand.isEmpty())
698
0
            aItem.put("commandName", sCommand);
699
700
0
        std::stringstream aStream;
701
0
        boost::property_tree::write_json(aStream, aItem);
702
0
        pShell->libreOfficeKitViewCallback(LOK_CALLBACK_STATE_CHANGED, OString(aStream.str()));
703
0
    }
704
0
}
705
706
void SfxLokHelper::notifyViewRenderState(const SfxViewShell* pShell, vcl::ITiledRenderable* pDoc)
707
8.23k
{
708
8.23k
    pShell->libreOfficeKitViewCallback(LOK_CALLBACK_VIEW_RENDER_STATE, pDoc->getViewRenderState());
709
8.23k
}
710
711
void SfxLokHelper::notifyWindow(const SfxViewShell* pThisView,
712
                                vcl::LOKWindowId nLOKWindowId,
713
                                std::u16string_view rAction,
714
                                const std::vector<vcl::LOKPayloadItem>& rPayload)
715
0
{
716
0
    assert(pThisView != nullptr && "pThisView must be valid");
717
718
0
    if (nLOKWindowId == 0 || DisableCallbacks::disabled())
719
0
        return;
720
721
0
    OStringBuffer aPayload =
722
0
        "{ \"id\": \"" + OString::number(nLOKWindowId) + "\""
723
0
        ", \"action\": \"" + OUStringToOString(rAction, RTL_TEXTENCODING_UTF8) + "\"";
724
725
0
    for (const auto& rItem: rPayload)
726
0
    {
727
0
        if (!rItem.first.isEmpty() && !rItem.second.isEmpty())
728
0
        {
729
0
            auto aFirst = rItem.first.replaceAll("\""_ostr, "\\\""_ostr);
730
0
            auto aSecond = rItem.second.replaceAll("\""_ostr, "\\\""_ostr);
731
0
            aPayload.append(", \"" + aFirst + "\": \"" + aSecond + "\"");
732
0
        }
733
0
    }
734
0
    aPayload.append('}');
735
736
0
    const OString s = aPayload.makeStringAndClear();
737
0
    pThisView->libreOfficeKitViewCallback(LOK_CALLBACK_WINDOW, s);
738
0
}
739
740
void SfxLokHelper::notifyCursorInvalidation(SfxViewShell const* pThisView, tools::Rectangle const* pRect, bool bControlEvent, int windowID)
741
0
{
742
0
    int nViewId = SfxLokHelper::getView(*pThisView);
743
0
    OString sPayload = OString::Concat("{ \"viewId\": \"") + OString::number(nViewId) + "\", \"rectangle\": \"" + pRect->toString();
744
0
    if (bControlEvent)
745
0
    {
746
0
        sPayload += "\", \"controlEvent\": true, \"windowId\": \"" + OString::number(windowID) + "\"";
747
0
    }
748
0
    sPayload += " }";
749
0
    pThisView->libreOfficeKitViewCallback(LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR, sPayload);
750
0
}
751
752
void SfxLokHelper::notifyInvalidation(SfxViewShell const* pThisView, tools::Rectangle const* pRect)
753
0
{
754
    // -1 means all parts
755
0
    const int nPart = comphelper::LibreOfficeKit::isPartInInvalidation() ? pThisView->getPart() : INT_MIN;
756
0
    SfxLokHelper::notifyInvalidation(pThisView, nPart, pRect);
757
0
}
758
759
void SfxLokHelper::notifyInvalidation(SfxViewShell const* pThisView, const int nInPart, tools::Rectangle const* pRect)
760
0
{
761
0
    if (DisableCallbacks::disabled())
762
0
        return;
763
764
    // -1 means all parts
765
0
    const int nPart = comphelper::LibreOfficeKit::isPartInInvalidation() ? nInPart : INT_MIN;
766
0
    const int nMode = pThisView->getEditMode();
767
0
    pThisView->libreOfficeKitViewInvalidateTilesCallback(pRect, nPart, nMode);
768
0
}
769
770
void SfxLokHelper::notifyDocumentSizeChanged(SfxViewShell const* pThisView, const OString& rPayload, vcl::ITiledRenderable* pDoc, bool bInvalidateAll)
771
0
{
772
0
    if (!pDoc || pDoc->isDisposed() || DisableCallbacks::disabled())
773
0
        return;
774
775
0
    if (bInvalidateAll)
776
0
    {
777
0
        for (int i = 0; i < pDoc->getParts(); ++i)
778
0
        {
779
0
            tools::Rectangle aRectangle(0, 0, 1000000000, 1000000000);
780
0
            const int nMode = pThisView->getEditMode();
781
0
            pThisView->libreOfficeKitViewInvalidateTilesCallback(&aRectangle, i, nMode);
782
0
        }
783
0
    }
784
0
    pThisView->libreOfficeKitViewCallback(LOK_CALLBACK_DOCUMENT_SIZE_CHANGED, rPayload);
785
0
}
786
787
void SfxLokHelper::notifyDocumentSizeChangedAllViews(vcl::ITiledRenderable* pDoc, bool bInvalidateAll)
788
0
{
789
0
    if (DisableCallbacks::disabled())
790
0
        return;
791
792
    // FIXME: Do we know whether it is the views for the document that is in the "current" view that has changed?
793
0
    const SfxViewShell* const pCurrentViewShell = SfxViewShell::Current();
794
0
    SfxViewShell* pViewShell = SfxViewShell::GetFirst();
795
0
    while (pViewShell)
796
0
    {
797
        // FIXME: What if SfxViewShell::Current() returned null?
798
        // Should we then do this for all views of all open documents
799
        // or not?
800
0
        if (pCurrentViewShell == nullptr || pViewShell->GetDocId() == pCurrentViewShell-> GetDocId())
801
0
        {
802
0
            SfxLokHelper::notifyDocumentSizeChanged(pViewShell, ""_ostr, pDoc, bInvalidateAll);
803
0
            bInvalidateAll = false; // we direct invalidations to all views anyway.
804
0
        }
805
0
        pViewShell = SfxViewShell::GetNext(*pViewShell);
806
0
    }
807
0
}
808
809
void SfxLokHelper::notifyCurrentPageSizeChangedAllViews(const vcl::ITiledRenderable *pDoc)
810
0
{
811
0
    if (!pDoc || pDoc->isDisposed() || DisableCallbacks::disabled())
812
0
        return;
813
814
0
    const SfxViewShell* const pCurrentViewShell = SfxViewShell::Current();
815
0
    SfxViewShell* pViewShell = SfxViewShell::GetFirst();
816
0
    while (pViewShell)
817
0
    {
818
0
        if (pCurrentViewShell == nullptr || pViewShell->GetDocId() == pCurrentViewShell->GetDocId())
819
0
        {
820
0
            OString aPayload = ".uno:CurrentPageResize"_ostr;
821
0
            pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_STATE_CHANGED, aPayload);
822
0
        }
823
0
        pViewShell = SfxViewShell::GetNext(*pViewShell);
824
0
    }
825
0
}
826
827
void SfxLokHelper::notifyPartSizeChangedAllViews(vcl::ITiledRenderable* pDoc, int nPart)
828
0
{
829
0
    if (DisableCallbacks::disabled())
830
0
        return;
831
832
0
    SfxViewShell* pViewShell = SfxViewShell::GetFirst();
833
0
    while (pViewShell)
834
0
    {
835
0
        if (// FIXME should really filter on pViewShell->GetDocId() too
836
0
            pViewShell->getPart() == nPart)
837
0
            SfxLokHelper::notifyDocumentSizeChanged(pViewShell, ""_ostr, pDoc, false);
838
0
        pViewShell = SfxViewShell::GetNext(*pViewShell);
839
0
    }
840
0
}
841
842
OString SfxLokHelper::makeVisCursorInvalidation(int nViewId, const OString& rRectangle,
843
    bool bMispelledWord, const OString& rHyperlink)
844
0
{
845
0
    if (comphelper::LibreOfficeKit::isViewIdForVisCursorInvalidation())
846
0
    {
847
0
        OString sHyperlink = rHyperlink.isEmpty() ? "{}"_ostr : rHyperlink;
848
0
        return OString::Concat("{ \"viewId\": \"") + OString::number(nViewId) +
849
0
            "\", \"rectangle\": \"" + rRectangle +
850
0
            "\", \"mispelledWord\": \"" +  OString::number(bMispelledWord ? 1 : 0) +
851
0
            "\", \"hyperlink\": " + sHyperlink + " }";
852
0
    }
853
0
    else
854
0
    {
855
0
        return rRectangle;
856
0
    }
857
0
}
858
859
void SfxLokHelper::notifyAllViews(int nType, const OString& rPayload)
860
0
{
861
0
    if (DisableCallbacks::disabled())
862
0
        return;
863
864
0
    const auto payload = rPayload.getStr();
865
0
    const SfxViewShell* const pCurrentViewShell = SfxViewShell::Current();
866
0
    if (!pCurrentViewShell)
867
0
        return;
868
0
    SfxViewShell* pViewShell = SfxViewShell::GetFirst();
869
0
    while (pViewShell)
870
0
    {
871
0
        if (pViewShell->GetDocId() == pCurrentViewShell->GetDocId())
872
0
            pViewShell->libreOfficeKitViewCallback(nType, payload);
873
0
        pViewShell = SfxViewShell::GetNext(*pViewShell);
874
0
    }
875
0
}
876
877
void SfxLokHelper::notifyContextChange(const css::ui::ContextChangeEventObject& rEvent)
878
0
{
879
0
    if (DisableCallbacks::disabled())
880
0
        return;
881
882
0
    SfxViewShell* pViewShell = SfxViewShell::Get({ rEvent.Source, css::uno::UNO_QUERY });
883
0
    if (!pViewShell)
884
0
        return;
885
886
0
    OUString aBuffer =
887
0
        rEvent.ApplicationName.replace(' ', '_') +
888
0
        " " +
889
0
        rEvent.ContextName.replace(' ', '_');
890
0
    pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_CONTEXT_CHANGED, aBuffer.toUtf8());
891
0
}
892
893
void SfxLokHelper::notifyLog(const std::ostringstream& stream)
894
0
{
895
0
    if (DisableCallbacks::disabled())
896
0
       return;
897
898
0
    SfxViewShell* pViewShell = SfxViewShell::Current();
899
0
    if (!pViewShell)
900
0
       return;
901
0
    if (pViewShell->getLibreOfficeKitViewCallback())
902
0
    {
903
0
        if (!g_logNotifierCache.empty())
904
0
        {
905
0
            for (const auto& msg : g_logNotifierCache)
906
0
            {
907
0
                pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_CORE_LOG, msg.c_str());
908
0
            }
909
0
            g_logNotifierCache.clear();
910
0
        }
911
0
        pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_CORE_LOG, stream.str().c_str());
912
0
    }
913
0
    else
914
0
    {
915
0
        while (g_logNotifierCache.size() >= g_logNotifierCacheMaxSize)
916
0
            g_logNotifierCache.pop_front();
917
0
        g_logNotifierCache.push_back(stream.str());
918
0
    }
919
0
}
920
921
namespace
922
{
923
std::string extractCertificateWithOffset(const std::string& certificate, size_t& rOffset)
924
0
{
925
0
    static constexpr std::string_view header("-----BEGIN CERTIFICATE-----");
926
0
    static constexpr std::string_view footer("-----END CERTIFICATE-----");
927
928
0
    std::string result;
929
930
0
    size_t pos1 = certificate.find(header, rOffset);
931
0
    if (pos1 == std::string::npos)
932
0
        return result;
933
934
0
    size_t pos2 = certificate.find(footer, pos1 + 1);
935
0
    if (pos2 == std::string::npos)
936
0
        return result;
937
938
0
    pos1 = pos1 + header.length();
939
0
    size_t len = pos2 - pos1;
940
941
0
    rOffset = pos2;
942
0
    return certificate.substr(pos1, len);
943
0
}
944
}
945
946
std::string SfxLokHelper::extractCertificate(const std::string & certificate)
947
0
{
948
0
    size_t nOffset = 0;
949
0
    return extractCertificateWithOffset(certificate, nOffset);
950
0
}
951
952
std::vector<std::string> SfxLokHelper::extractCertificates(const std::string& rCerts)
953
0
{
954
0
    std::vector<std::string> aRet;
955
0
    size_t nOffset = 0;
956
0
    while (true)
957
0
    {
958
0
        std::string aNext = extractCertificateWithOffset(rCerts, nOffset);
959
0
        if (aNext.empty())
960
0
        {
961
0
            break;
962
0
        }
963
964
0
        aRet.push_back(std::move(aNext));
965
0
    }
966
0
    return aRet;
967
0
}
968
969
namespace
970
{
971
std::string extractKey(const std::string & privateKey)
972
0
{
973
0
    static constexpr std::string_view header("-----BEGIN PRIVATE KEY-----");
974
0
    static constexpr std::string_view footer("-----END PRIVATE KEY-----");
975
976
0
    std::string result;
977
978
0
    size_t pos1 = privateKey.find(header);
979
0
    if (pos1 == std::string::npos)
980
0
        return result;
981
982
0
    size_t pos2 = privateKey.find(footer, pos1 + 1);
983
0
    if (pos2 == std::string::npos)
984
0
        return result;
985
986
0
    pos1 = pos1 + header.length();
987
0
    pos2 = pos2 - pos1;
988
989
0
    return privateKey.substr(pos1, pos2);
990
0
}
991
}
992
993
css::uno::Reference<css::security::XCertificate> SfxLokHelper::getSigningCertificate(const std::string& rCert, const std::string& rKey)
994
0
{
995
0
    const uno::Reference<uno::XComponentContext>& xContext = comphelper::getProcessComponentContext();
996
0
    uno::Reference<xml::crypto::XSEInitializer> xSEInitializer = xml::crypto::SEInitializer::create(xContext);
997
0
    uno::Reference<xml::crypto::XXMLSecurityContext> xSecurityContext = xSEInitializer->createSecurityContext(OUString());
998
0
    if (!xSecurityContext.is())
999
0
    {
1000
0
        return {};
1001
0
    }
1002
1003
0
    uno::Reference<xml::crypto::XSecurityEnvironment> xSecurityEnvironment = xSecurityContext->getSecurityEnvironment();
1004
0
    uno::Reference<xml::crypto::XCertificateCreator> xCertificateCreator(xSecurityEnvironment, uno::UNO_QUERY);
1005
1006
0
    if (!xCertificateCreator.is())
1007
0
    {
1008
0
        return {};
1009
0
    }
1010
1011
0
    uno::Sequence<sal_Int8> aCertificateSequence;
1012
1013
0
    std::string aCertificateBase64String = extractCertificate(rCert);
1014
0
    if (!aCertificateBase64String.empty())
1015
0
    {
1016
0
        OUString aBase64OUString = OUString::createFromAscii(aCertificateBase64String);
1017
0
        comphelper::Base64::decode(aCertificateSequence, aBase64OUString);
1018
0
    }
1019
0
    else
1020
0
    {
1021
0
        aCertificateSequence.realloc(rCert.size());
1022
0
        std::copy(rCert.c_str(), rCert.c_str() + rCert.size(), aCertificateSequence.getArray());
1023
0
    }
1024
1025
0
    uno::Sequence<sal_Int8> aPrivateKeySequence;
1026
0
    std::string aPrivateKeyBase64String = extractKey(rKey);
1027
0
    if (!aPrivateKeyBase64String.empty())
1028
0
    {
1029
0
        OUString aBase64OUString = OUString::createFromAscii(aPrivateKeyBase64String);
1030
0
        comphelper::Base64::decode(aPrivateKeySequence, aBase64OUString);
1031
0
    }
1032
0
    else
1033
0
    {
1034
0
        aPrivateKeySequence.realloc(rKey.size());
1035
0
        std::copy(rKey.c_str(), rKey.c_str() + rKey.size(), aPrivateKeySequence.getArray());
1036
0
    }
1037
1038
0
    uno::Reference<security::XCertificate> xCertificate = xCertificateCreator->createDERCertificateWithPrivateKey(aCertificateSequence, aPrivateKeySequence);
1039
0
    return xCertificate;
1040
0
}
1041
1042
uno::Reference<security::XCertificate> SfxLokHelper::addCertificate(
1043
    const css::uno::Reference<css::xml::crypto::XCertificateCreator>& xCertificateCreator,
1044
    const css::uno::Sequence<sal_Int8>& rCert)
1045
0
{
1046
    // Trust arg is handled by CERT_DecodeTrustString(), see 'man certutil'.
1047
0
    return xCertificateCreator->addDERCertificateToTheDatabase(rCert, u"TCu,Cu,Tu"_ustr);
1048
0
}
1049
1050
void SfxLokHelper::addCertificates(const std::vector<std::string>& rCerts)
1051
0
{
1052
0
    const uno::Reference<uno::XComponentContext>& xContext = comphelper::getProcessComponentContext();
1053
0
    uno::Reference<xml::crypto::XSEInitializer> xSEInitializer = xml::crypto::SEInitializer::create(xContext);
1054
0
    uno::Reference<xml::crypto::XXMLSecurityContext> xSecurityContext = xSEInitializer->createSecurityContext(OUString());
1055
0
    if (!xSecurityContext.is())
1056
0
    {
1057
0
        return;
1058
0
    }
1059
1060
0
    uno::Reference<xml::crypto::XSecurityEnvironment> xSecurityEnvironment = xSecurityContext->getSecurityEnvironment();
1061
0
    uno::Reference<xml::crypto::XCertificateCreator> xCertificateCreator(xSecurityEnvironment, uno::UNO_QUERY);
1062
0
    if (!xCertificateCreator.is())
1063
0
    {
1064
0
        return;
1065
0
    }
1066
1067
0
    for (const auto& rCert : rCerts)
1068
0
    {
1069
0
        uno::Sequence<sal_Int8> aCertificateSequence;
1070
0
        OUString aBase64OUString = OUString::fromUtf8(rCert);
1071
0
        comphelper::Base64::decode(aCertificateSequence, aBase64OUString);
1072
0
        addCertificate(xCertificateCreator, aCertificateSequence);
1073
0
    }
1074
1075
    // Update the signature state, perhaps the signing certificate is now trusted.
1076
0
    SfxObjectShell* pObjectShell = SfxObjectShell::Current();
1077
0
    if (!pObjectShell)
1078
0
    {
1079
0
        return;
1080
0
    }
1081
1082
0
    pObjectShell->RecheckSignature(false);
1083
0
}
1084
1085
bool SfxLokHelper::supportsCommand(std::u16string_view rCommand)
1086
0
{
1087
0
    static const std::initializer_list<std::u16string_view> vSupport = { u"Signature" };
1088
1089
0
    return std::find(vSupport.begin(), vSupport.end(), rCommand) != vSupport.end();
1090
0
}
1091
1092
std::map<OUString, OUString> SfxLokHelper::parseCommandParameters(std::u16string_view rCommand)
1093
0
{
1094
0
    std::map<OUString, OUString> aMap;
1095
1096
0
    INetURLObject aParser(rCommand);
1097
0
    OUString aArguments = aParser.GetParam();
1098
0
    sal_Int32 nParamIndex = 0;
1099
0
    do
1100
0
    {
1101
0
        std::u16string_view aParam = o3tl::getToken(aArguments, 0, '&', nParamIndex);
1102
0
        sal_Int32 nIndex = 0;
1103
0
        OUString aKey;
1104
0
        OUString aValue;
1105
0
        do
1106
0
        {
1107
0
            std::u16string_view aToken = o3tl::getToken(aParam, 0, '=', nIndex);
1108
0
            if (aKey.isEmpty())
1109
0
                aKey = aToken;
1110
0
            else
1111
0
                aValue = aToken;
1112
0
        } while (nIndex >= 0);
1113
0
        aMap[aKey] = INetURLObject::decode(aValue, INetURLObject::DecodeMechanism::WithCharset);
1114
0
    } while (nParamIndex >= 0);
1115
1116
0
    return aMap;
1117
0
}
1118
1119
void SfxLokHelper::getCommandValues(tools::JsonWriter& rJsonWriter, std::string_view rCommand)
1120
0
{
1121
0
    static constexpr OString aSignature(".uno:Signature"_ostr);
1122
0
    if (!o3tl::starts_with(rCommand, aSignature))
1123
0
    {
1124
0
        return;
1125
0
    }
1126
1127
0
    SfxObjectShell* pObjectShell = SfxObjectShell::Current();
1128
0
    if (!pObjectShell)
1129
0
    {
1130
0
        return;
1131
0
    }
1132
1133
0
    svl::crypto::SigningContext aSigningContext;
1134
0
    std::map<OUString, OUString> aMap
1135
0
        = SfxLokHelper::parseCommandParameters(OUString::fromUtf8(rCommand));
1136
0
    auto it = aMap.find("signatureTime");
1137
0
    if (it != aMap.end())
1138
0
    {
1139
        // Signature time is provided: prefer it over the system time.
1140
0
        aSigningContext.m_nSignatureTime = it->second.toInt64();
1141
0
    }
1142
0
    pObjectShell->SignDocumentContentUsingCertificate(aSigningContext);
1143
    // Set commandName, this is a reply to a request.
1144
0
    rJsonWriter.put("commandName", aSignature);
1145
0
    auto aCommandValues = rJsonWriter.startNode("commandValues");
1146
0
    rJsonWriter.put("signatureTime", aSigningContext.m_nSignatureTime);
1147
1148
0
    uno::Sequence<sal_Int8> aDigest(reinterpret_cast<sal_Int8*>(aSigningContext.m_aDigest.data()),
1149
0
                                    aSigningContext.m_aDigest.size());
1150
0
    OUStringBuffer aBuffer;
1151
0
    comphelper::Base64::encode(aBuffer, aDigest);
1152
0
    rJsonWriter.put("digest", aBuffer.makeStringAndClear());
1153
0
}
1154
1155
void SfxLokHelper::notifyUpdate(SfxViewShell const* pThisView, int nType)
1156
0
{
1157
0
    if (DisableCallbacks::disabled() || !pThisView)
1158
0
        return;
1159
1160
0
    pThisView->libreOfficeKitViewUpdatedCallback(nType);
1161
0
}
1162
1163
void SfxLokHelper::notifyUpdatePerViewId(SfxViewShell const& rThisView, int nType)
1164
0
{
1165
0
    notifyUpdatePerViewId(rThisView, &rThisView, rThisView, nType);
1166
0
}
1167
1168
void SfxLokHelper::notifyUpdatePerViewId(SfxViewShell const& rTargetShell, SfxViewShell const* pViewShell,
1169
    SfxViewShell const& rSourceShell, int nType)
1170
0
{
1171
0
    if (DisableCallbacks::disabled())
1172
0
        return;
1173
1174
    // This getCurrentView() is dubious
1175
0
    SAL_WARN_IF(!pViewShell, "lok", "no explicit viewshell set");
1176
0
    int viewId = pViewShell ? SfxLokHelper::getView(*pViewShell) : SfxLokHelper::getCurrentView();
1177
0
    int sourceViewId = SfxLokHelper::getView(rSourceShell);
1178
0
    rTargetShell.libreOfficeKitViewUpdatedCallbackPerViewId(nType, viewId, sourceViewId);
1179
0
}
1180
1181
void SfxLokHelper::notifyOtherViewsUpdatePerViewId(SfxViewShell const* pThisView, int nType)
1182
0
{
1183
0
    if (DisableCallbacks::disabled() || !pThisView)
1184
0
        return;
1185
1186
0
    int viewId = SfxLokHelper::getView(*pThisView);
1187
0
    const ViewShellDocId nCurrentDocId = pThisView->GetDocId();
1188
0
    SfxViewShell* pViewShell = SfxViewShell::GetFirst();
1189
0
    while (pViewShell)
1190
0
    {
1191
0
        if (pViewShell != pThisView && nCurrentDocId == pViewShell->GetDocId())
1192
0
            pViewShell->libreOfficeKitViewUpdatedCallbackPerViewId(nType, viewId, viewId);
1193
1194
0
        pViewShell = SfxViewShell::GetNext(*pViewShell);
1195
0
    }
1196
0
}
1197
1198
void SfxLokHelper::registerViewCallbacks()
1199
0
{
1200
0
    comphelper::LibreOfficeKit::setViewSetter([](int nView) {
1201
0
        SfxLokHelper::setView(nView);
1202
0
    });
1203
0
    comphelper::LibreOfficeKit::setViewGetter([]() -> int {
1204
0
        return SfxLokHelper::getCurrentView();
1205
0
    });
1206
0
}
1207
1208
void SfxLokHelper::dispatchUnoCommand(const boost::property_tree::ptree& tree)
1209
0
{
1210
0
    auto command = OStringToOUString(tree.get_child("name").get_value<std::string>(),
1211
0
                                     RTL_TEXTENCODING_UTF8);
1212
    // Check if the uno command is allowed
1213
0
    if (std::u16string_view rest;
1214
0
        !command.startsWith(".uno:", &rest) || !GetKitUnoCommandList().contains(rest))
1215
0
    {
1216
0
        LOK_WARN("lok.transform",
1217
0
                 "UnoCommand command is not recognized: '" << command << "'");
1218
0
        return;
1219
0
    }
1220
0
    std::vector<beans::PropertyValue> arguments;
1221
0
    if (auto args = tree.get_child_optional("arguments"))
1222
0
    {
1223
0
        arguments = comphelper::JsonToPropertyValues(*args);
1224
0
    }
1225
    // Make the uno command synchron
1226
0
    arguments.push_back(comphelper::makePropertyValue(u"SynchronMode"_ustr, true));
1227
1228
0
    comphelper::dispatchCommand(command, comphelper::containerToSequence(arguments));
1229
0
}
1230
1231
namespace
1232
{
1233
    struct LOKAsyncEventData
1234
    {
1235
        int mnView; // Window is not enough.
1236
        VclPtr<vcl::Window> mpWindow;
1237
        VclEventId mnEvent;
1238
        MouseEvent maMouseEvent;
1239
        KeyEvent maKeyEvent;
1240
        OUString maText;
1241
    };
1242
1243
    void LOKPostAsyncEvent(void* pEv, void*)
1244
0
    {
1245
0
        std::unique_ptr<LOKAsyncEventData> pLOKEv(static_cast<LOKAsyncEventData*>(pEv));
1246
0
        if (pLOKEv->mpWindow->isDisposed())
1247
0
            return;
1248
1249
0
        int nView = SfxLokHelper::getCurrentView();
1250
0
        if (nView != pLOKEv->mnView)
1251
0
        {
1252
0
            SAL_INFO("sfx.view", "LOK - view mismatch " << nView << " vs. " << pLOKEv->mnView);
1253
0
            SfxLokHelper::setView(pLOKEv->mnView);
1254
0
        }
1255
1256
0
        if (!pLOKEv->mpWindow->HasChildPathFocus(true))
1257
0
        {
1258
0
            SAL_INFO("sfx.view", "LOK - focus mismatch, switching focus");
1259
0
            pLOKEv->mpWindow->GrabFocus();
1260
0
        }
1261
1262
0
        VclPtr<vcl::Window> pFocusWindow = pLOKEv->mpWindow->GetFocusedWindow();
1263
0
        if (!pFocusWindow)
1264
0
            pFocusWindow = pLOKEv->mpWindow;
1265
1266
0
        if (pLOKEv->mpWindow->isDisposed())
1267
0
            return;
1268
1269
0
        switch (pLOKEv->mnEvent)
1270
0
        {
1271
0
        case VclEventId::WindowKeyInput:
1272
0
        {
1273
0
            sal_uInt16 nRepeat = pLOKEv->maKeyEvent.GetRepeat();
1274
0
            KeyEvent singlePress(pLOKEv->maKeyEvent.GetCharCode(),
1275
0
                                 pLOKEv->maKeyEvent.GetKeyCode());
1276
0
            for (sal_uInt16 i = 0; i <= nRepeat; ++i)
1277
0
                if (!pFocusWindow->isDisposed())
1278
0
                    pFocusWindow->KeyInput(singlePress);
1279
1280
0
            if (pLOKEv->maKeyEvent.GetKeyCode().GetCode() == KEY_CONTEXTMENU)
1281
0
            {
1282
                // later do use getCaretPosition probably, or get focused obj position, smt like that
1283
0
                Point aPos = pFocusWindow->GetPointerPosPixel();
1284
0
                CommandEvent aCEvt( aPos, CommandEventId::ContextMenu);
1285
0
                pFocusWindow->Command(aCEvt);
1286
0
            }
1287
0
            break;
1288
0
        }
1289
0
        case VclEventId::WindowKeyUp:
1290
0
            if (!pFocusWindow->isDisposed())
1291
0
                pFocusWindow->KeyUp(pLOKEv->maKeyEvent);
1292
0
            break;
1293
0
        case VclEventId::WindowMouseButtonDown:
1294
0
            pLOKEv->mpWindow->SetLastMousePos(pLOKEv->maMouseEvent.GetPosPixel());
1295
0
            pLOKEv->mpWindow->MouseButtonDown(pLOKEv->maMouseEvent);
1296
            // Invoke the context menu
1297
0
            if (pLOKEv->maMouseEvent.GetButtons() & MOUSE_RIGHT)
1298
0
            {
1299
0
                const CommandEvent aCEvt(pLOKEv->maMouseEvent.GetPosPixel(), CommandEventId::ContextMenu, true, nullptr);
1300
0
                pLOKEv->mpWindow->Command(aCEvt);
1301
0
            }
1302
0
            break;
1303
0
        case VclEventId::WindowMouseButtonUp:
1304
0
            pLOKEv->mpWindow->SetLastMousePos(pLOKEv->maMouseEvent.GetPosPixel());
1305
0
            pLOKEv->mpWindow->MouseButtonUp(pLOKEv->maMouseEvent);
1306
1307
            // sometimes MouseButtonDown captures mouse and starts tracking, and VCL
1308
            // will not take care of releasing that with tiled rendering
1309
0
            if (pLOKEv->mpWindow->IsTracking())
1310
0
                pLOKEv->mpWindow->EndTracking();
1311
1312
0
            break;
1313
0
        case VclEventId::WindowMouseMove:
1314
0
            pLOKEv->mpWindow->SetLastMousePos(pLOKEv->maMouseEvent.GetPosPixel());
1315
0
            pLOKEv->mpWindow->MouseMove(pLOKEv->maMouseEvent);
1316
0
            pLOKEv->mpWindow->RequestHelp(HelpEvent{
1317
0
                pLOKEv->mpWindow->OutputToScreenPixel(pLOKEv->maMouseEvent.GetPosPixel()),
1318
0
                HelpEventMode::QUICK }); // If needed, HelpEventMode should be taken from a config
1319
0
            break;
1320
0
        case VclEventId::ExtTextInput:
1321
0
        case VclEventId::EndExtTextInput:
1322
0
            pLOKEv->mpWindow->PostExtTextInputEvent(pLOKEv->mnEvent, pLOKEv->maText);
1323
0
            break;
1324
0
        default:
1325
0
            assert(false);
1326
0
            break;
1327
0
        }
1328
0
    }
1329
1330
    void postEventAsync(LOKAsyncEventData *pEvent)
1331
0
    {
1332
0
        if (!pEvent->mpWindow || pEvent->mpWindow->isDisposed())
1333
0
        {
1334
0
            SAL_WARN("vcl", "Async event post - but no valid window as destination " << pEvent->mpWindow.get());
1335
0
            delete pEvent;
1336
0
            return;
1337
0
        }
1338
1339
0
        pEvent->mnView = SfxLokHelper::getCurrentView();
1340
0
        if (vcl::lok::isUnipoll())
1341
0
        {
1342
0
            if (!Application::IsMainThread())
1343
0
                SAL_WARN("lok", "Posting event directly but not called from main thread!");
1344
0
            LOKPostAsyncEvent(pEvent, nullptr);
1345
0
        }
1346
0
        else
1347
0
            Application::PostUserEvent(LINK_NONMEMBER(pEvent, LOKPostAsyncEvent));
1348
0
    }
1349
}
1350
1351
void SfxLokHelper::postKeyEventAsync(const VclPtr<vcl::Window> &xWindow,
1352
                                     int nType, int nCharCode, int nKeyCode, int nRepeat)
1353
0
{
1354
0
    LOKAsyncEventData* pLOKEv = new LOKAsyncEventData;
1355
0
    switch (nType)
1356
0
    {
1357
0
    case LOK_KEYEVENT_KEYINPUT:
1358
0
        pLOKEv->mnEvent = VclEventId::WindowKeyInput;
1359
0
        break;
1360
0
    case LOK_KEYEVENT_KEYUP:
1361
0
        pLOKEv->mnEvent = VclEventId::WindowKeyUp;
1362
0
        break;
1363
0
    default:
1364
0
        assert(false);
1365
0
    }
1366
0
    pLOKEv->maKeyEvent = KeyEvent(nCharCode, nKeyCode, nRepeat);
1367
0
    pLOKEv->mpWindow = xWindow;
1368
0
    postEventAsync(pLOKEv);
1369
0
}
1370
1371
void SfxLokHelper::setBlockedCommandList(int nViewId, const char* blockedCommandList)
1372
0
{
1373
0
    SfxViewShell* pViewShell = SfxLokHelper::getViewOfId(nViewId);
1374
1375
0
    if(pViewShell)
1376
0
    {
1377
0
        pViewShell->setBlockedCommandList(blockedCommandList);
1378
0
    }
1379
0
}
1380
1381
void SfxLokHelper::postExtTextEventAsync(const VclPtr<vcl::Window> &xWindow,
1382
                                         int nType, const OUString &rText)
1383
0
{
1384
0
    LOKAsyncEventData* pLOKEv = new LOKAsyncEventData;
1385
0
    switch (nType)
1386
0
    {
1387
0
    case LOK_EXT_TEXTINPUT:
1388
0
        pLOKEv->mnEvent = VclEventId::ExtTextInput;
1389
0
        pLOKEv->maText = rText;
1390
0
        break;
1391
0
    case LOK_EXT_TEXTINPUT_END:
1392
0
        pLOKEv->mnEvent = VclEventId::EndExtTextInput;
1393
0
        pLOKEv->maText = "";
1394
0
        break;
1395
0
    default:
1396
0
        assert(false);
1397
0
    }
1398
0
    pLOKEv->mpWindow = xWindow;
1399
0
    postEventAsync(pLOKEv);
1400
0
}
1401
1402
void SfxLokHelper::postMouseEventAsync(const VclPtr<vcl::Window> &xWindow, LokMouseEventData const & rLokMouseEventData)
1403
0
{
1404
0
    LOKAsyncEventData* pLOKEv = new LOKAsyncEventData;
1405
0
    switch (rLokMouseEventData.mnType)
1406
0
    {
1407
0
    case LOK_MOUSEEVENT_MOUSEBUTTONDOWN:
1408
0
        pLOKEv->mnEvent = VclEventId::WindowMouseButtonDown;
1409
0
        break;
1410
0
    case LOK_MOUSEEVENT_MOUSEBUTTONUP:
1411
0
        pLOKEv->mnEvent = VclEventId::WindowMouseButtonUp;
1412
0
        break;
1413
0
    case LOK_MOUSEEVENT_MOUSEMOVE:
1414
0
        pLOKEv->mnEvent = VclEventId::WindowMouseMove;
1415
0
        break;
1416
0
    default:
1417
0
        assert(false);
1418
0
    }
1419
1420
    // no reason - just always true so far.
1421
0
    assert (rLokMouseEventData.meModifiers == MouseEventModifiers::SIMPLECLICK);
1422
1423
0
    pLOKEv->maMouseEvent = MouseEvent(rLokMouseEventData.maPosition, rLokMouseEventData.mnCount,
1424
0
                                      rLokMouseEventData.meModifiers, rLokMouseEventData.mnButtons,
1425
0
                                      rLokMouseEventData.mnModifier);
1426
0
    if (rLokMouseEventData.maLogicPosition)
1427
0
    {
1428
0
        pLOKEv->maMouseEvent.setLogicPosition(*rLokMouseEventData.maLogicPosition);
1429
0
    }
1430
0
    pLOKEv->mpWindow = xWindow;
1431
0
    postEventAsync(pLOKEv);
1432
0
}
1433
1434
void SfxLokHelper::dumpState(rtl::OStringBuffer &rState)
1435
0
{
1436
0
    SfxViewShell* pShell = SfxViewShell::Current();
1437
0
    sal_Int32 nDocId = pShell ? static_cast<sal_Int32>(pShell->GetDocId().get()) : -1;
1438
1439
0
    rState.append("\n\tDocId:\t");
1440
0
    rState.append(nDocId);
1441
1442
0
    if (nDocId < 0)
1443
0
        return;
1444
1445
0
    rState.append("\n\tViewCount:\t");
1446
0
    rState.append(static_cast<sal_Int32>(getViewsCount(nDocId)));
1447
1448
0
    const SfxViewShell* const pCurrentViewShell = SfxViewShell::Current();
1449
0
    SfxViewShell* pViewShell = SfxViewShell::GetFirst();
1450
0
    while (pViewShell)
1451
0
    {
1452
0
        if (pCurrentViewShell == nullptr || pViewShell->GetDocId() == pCurrentViewShell-> GetDocId())
1453
0
            pViewShell->dumpLibreOfficeKitViewState(rState);
1454
1455
0
        pViewShell = SfxViewShell::GetNext(*pViewShell);
1456
0
    }
1457
0
}
1458
1459
bool SfxLokHelper::testInPlaceComponentMouseEventHit(SfxViewShell* pViewShell, int nType, int nX,
1460
                                                     int nY, int nCount, int nButtons,
1461
                                                     int nModifier, double fScaleX, double fScaleY,
1462
                                                     bool bNegativeX)
1463
0
{
1464
    // In LOK RTL mode draw/svx operates in negative X coordinates
1465
    // But the coordinates from client is always positive, so negate nX.
1466
0
    if (bNegativeX)
1467
0
        nX = -nX;
1468
1469
    // check if the user hit a chart/math object which is being edited by this view
1470
0
    if (LokChartHelper aChartHelper(pViewShell, bNegativeX);
1471
0
        aChartHelper.postMouseEvent(nType, nX, nY, nCount, nButtons, nModifier, fScaleX, fScaleY))
1472
0
        return true;
1473
1474
0
    if (LokStarMathHelper aMathHelper(pViewShell);
1475
0
        aMathHelper.postMouseEvent(nType, nX, nY, nCount, nButtons, nModifier, fScaleX, fScaleY))
1476
0
        return true;
1477
1478
    // check if the user hit a chart which is being edited by someone else
1479
    // and, if so, skip current mouse event
1480
0
    if (nType != LOK_MOUSEEVENT_MOUSEMOVE)
1481
0
    {
1482
0
        if (LokChartHelper::HitAny({nX, nY}, bNegativeX))
1483
0
            return true;
1484
0
    }
1485
1486
0
    return false;
1487
0
}
1488
1489
VclPtr<vcl::Window> SfxLokHelper::getInPlaceDocWindow(SfxViewShell* pViewShell)
1490
0
{
1491
0
    if (VclPtr<vcl::Window> pWindow = LokChartHelper(pViewShell).GetWindow())
1492
0
        return pWindow;
1493
0
    if (VclPtr<vcl::Window> pWindow = LokStarMathHelper(pViewShell).GetWidgetWindow())
1494
0
        return pWindow;
1495
0
    return {};
1496
0
}
1497
1498
void SfxLokHelper::sendNetworkAccessError(std::string_view rAction)
1499
0
{
1500
0
    tools::JsonWriter aWriter;
1501
0
    aWriter.put("code", static_cast<sal_uInt32>(
1502
0
        ErrCode(ErrCodeArea::Inet, sal_uInt16(ErrCodeClass::Access))));
1503
0
    aWriter.put("kind", "network");
1504
0
    aWriter.put("cmd", rAction);
1505
1506
0
    SfxViewShell* pViewShell = SfxViewShell::Current();
1507
0
    if (pViewShell)
1508
0
    {
1509
0
        pViewShell->libreOfficeKitViewCallback(
1510
0
            LOK_CALLBACK_ERROR, aWriter.finishAndGetAsOString());
1511
0
    }
1512
0
}
1513
1514
SfxLokLanguageGuard::SfxLokLanguageGuard(const SfxViewShell* pNewShell)
1515
0
    : m_bSetLanguage(false)
1516
0
    , m_pOldShell(nullptr)
1517
0
{
1518
0
    m_pOldShell = SfxViewShell::Current();
1519
0
    if (!comphelper::LibreOfficeKit::isActive() || !pNewShell || pNewShell == m_pOldShell)
1520
0
    {
1521
0
        return;
1522
0
    }
1523
1524
    // The current view ID is not the one that belongs to this frame, update
1525
    // language/locale.
1526
0
    comphelper::LibreOfficeKit::setLanguageTag(pNewShell->GetLOKLanguageTag());
1527
0
    comphelper::LibreOfficeKit::setLocale(pNewShell->GetLOKLocale());
1528
0
    m_bSetLanguage = true;
1529
0
}
1530
1531
SfxLokLanguageGuard::~SfxLokLanguageGuard()
1532
0
{
1533
0
    if (!m_bSetLanguage || !m_pOldShell)
1534
0
    {
1535
0
        return;
1536
0
    }
1537
1538
0
    comphelper::LibreOfficeKit::setLanguageTag(m_pOldShell->GetLOKLanguageTag());
1539
0
    comphelper::LibreOfficeKit::setLocale(m_pOldShell->GetLOKLocale());
1540
0
}
1541
1542
LOKEditViewHistory::EditViewHistoryMap LOKEditViewHistory::maEditViewHistory;
1543
1544
1545
void LOKEditViewHistory::Update(bool bRemove)
1546
0
{
1547
0
    if (!comphelper::LibreOfficeKit::isActive())
1548
0
        return;
1549
1550
0
    SfxViewShell* pViewShell = SfxViewShell::Current();
1551
0
    if (pViewShell)
1552
0
    {
1553
0
        int nDocId = pViewShell->GetDocId().get();
1554
0
        if (maEditViewHistory.find(nDocId) != maEditViewHistory.end())
1555
0
            maEditViewHistory[nDocId].remove(pViewShell);
1556
0
        if (!bRemove)
1557
0
        {
1558
0
            maEditViewHistory[nDocId].push_back(pViewShell);
1559
0
            if (maEditViewHistory[nDocId].size() > 10)
1560
0
                maEditViewHistory[nDocId].pop_front();
1561
0
        }
1562
0
    }
1563
0
}
1564
1565
ViewShellList LOKEditViewHistory::GetHistoryForDoc(ViewShellDocId aDocId)
1566
0
{
1567
0
    int nDocId = aDocId.get();
1568
0
    ViewShellList aResult;
1569
0
    if (maEditViewHistory.find(nDocId) != maEditViewHistory.end())
1570
0
        aResult = maEditViewHistory.at(nDocId);
1571
0
    return aResult;
1572
0
}
1573
1574
 ViewShellList LOKEditViewHistory::GetSortedViewsForDoc(ViewShellDocId aDocId)
1575
0
 {
1576
0
     ViewShellList aEditViewHistoryForDoc = LOKEditViewHistory::GetHistoryForDoc(aDocId);
1577
     // all views where document is loaded
1578
0
     ViewShellList aCurrentDocViewList;
1579
     // active views that are listed in the edit history
1580
0
     ViewShellList aEditedViewList;
1581
1582
     // Populate aCurrentDocViewList and aEditedViewList
1583
0
     SfxViewShell* pViewShell = SfxViewShell::GetFirst();
1584
0
     while (pViewShell)
1585
0
     {
1586
0
         if (pViewShell->GetDocId() == aDocId)
1587
0
         {
1588
0
             if (aEditViewHistoryForDoc.empty() ||
1589
0
                 std::find(aEditViewHistoryForDoc.begin(), aEditViewHistoryForDoc.end(),
1590
0
                           pViewShell) == aEditViewHistoryForDoc.end())
1591
0
             {
1592
                 // append views not listed in the edit history;
1593
                 // the edit history is limited to 10 views,
1594
                 // so it could miss some view where in place editing is occurring
1595
0
                 aCurrentDocViewList.push_back(pViewShell);
1596
0
             }
1597
0
             else
1598
0
             {
1599
                 // view is listed in the edit history
1600
0
                 aEditedViewList.push_back(pViewShell);
1601
0
             }
1602
0
         }
1603
0
         pViewShell = SfxViewShell::GetNext(*pViewShell);
1604
0
     }
1605
1606
     // in case some no more active view needs to be removed from the history
1607
0
     aEditViewHistoryForDoc.remove_if(
1608
0
         [&aEditedViewList](SfxViewShell* pHistoryItem) {
1609
0
             return std::find(aEditedViewList.begin(), aEditedViewList.end(), pHistoryItem) == aEditedViewList.end();
1610
0
         });
1611
1612
     // place views belonging to the edit history at the end
1613
0
     aCurrentDocViewList.splice(aCurrentDocViewList.end(), aEditViewHistoryForDoc);
1614
1615
0
     return aCurrentDocViewList;
1616
0
 }
1617
1618
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */