Coverage Report

Created: 2025-07-07 10:01

/src/libreoffice/sw/source/uibase/uno/unodispatch.cxx
Line
Count
Source (jump to first uncovered line)
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
 * 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
#include <config_fuzzers.h>
22
23
#include <com/sun/star/frame/XFrame.hpp>
24
#include <com/sun/star/view/XSelectionSupplier.hpp>
25
26
#include <sfx2/viewfrm.hxx>
27
#include <sfx2/dispatch.hxx>
28
#include <svx/dataaccessdescriptor.hxx>
29
#include <comphelper/servicehelper.hxx>
30
#include <osl/diagnose.h>
31
#include <unodispatch.hxx>
32
#include <view.hxx>
33
#include <cmdid.h>
34
#include <wrtsh.hxx>
35
#include <dbmgr.hxx>
36
37
using namespace ::com::sun::star;
38
39
const char cURLFormLetter[] = ".uno:DataSourceBrowser/FormLetter";
40
const char cURLInsertContent[] = ".uno:DataSourceBrowser/InsertContent";//data into fields
41
const char cURLInsertColumns[] = ".uno:DataSourceBrowser/InsertColumns";//data into text
42
const char cURLDocumentDataSource[] = ".uno:DataSourceBrowser/DocumentDataSource";//current data source of the document
43
const char cInternalDBChangeNotification[] = ".uno::Writer/DataSourceChanged";
44
45
SwXDispatchProviderInterceptor::SwXDispatchProviderInterceptor(SwView& rVw) :
46
8.47k
    m_pView(&rVw)
47
8.47k
{
48
8.47k
    uno::Reference< frame::XFrame> xUnoFrame = m_pView->GetViewFrame().GetFrame().GetFrameInterface();
49
8.47k
    m_xIntercepted.set(xUnoFrame, uno::UNO_QUERY);
50
8.47k
    if(m_xIntercepted.is())
51
8.47k
    {
52
8.47k
        osl_atomic_increment(&m_refCount);
53
8.47k
        m_xIntercepted->registerDispatchProviderInterceptor(static_cast<frame::XDispatchProviderInterceptor*>(this));
54
        // this should make us the top-level dispatch-provider for the component, via a call to our
55
        // setDispatchProvider we should have got a fallback for requests we (i.e. our master) cannot fulfill
56
8.47k
        uno::Reference< lang::XComponent> xInterceptedComponent(m_xIntercepted, uno::UNO_QUERY);
57
8.47k
        if (xInterceptedComponent.is())
58
8.47k
            xInterceptedComponent->addEventListener(static_cast<lang::XEventListener*>(this));
59
8.47k
        osl_atomic_decrement(&m_refCount);
60
8.47k
    }
61
8.47k
}
62
63
SwXDispatchProviderInterceptor::~SwXDispatchProviderInterceptor()
64
8.47k
{
65
8.47k
}
66
67
uno::Reference< frame::XDispatch > SwXDispatchProviderInterceptor::queryDispatch(
68
    const util::URL& aURL, const OUString& aTargetFrameName, sal_Int32 nSearchFlags )
69
0
{
70
0
    DispatchMutexLock_Impl aLock;
71
0
    uno::Reference< frame::XDispatch> xResult;
72
    // create some dispatch ...
73
0
    if(m_pView && aURL.Complete.startsWith(".uno:DataSourceBrowser/"))
74
0
    {
75
0
        if(aURL.Complete == cURLFormLetter ||
76
0
            aURL.Complete == cURLInsertContent ||
77
0
                aURL.Complete == cURLInsertColumns ||
78
0
                    aURL.Complete == cURLDocumentDataSource)
79
0
        {
80
0
            if(!m_xDispatch.is())
81
0
                m_xDispatch = new SwXDispatch(*m_pView);
82
0
            xResult = m_xDispatch;
83
0
        }
84
0
    }
85
86
    // ask our slave provider
87
0
    if (!xResult.is() && m_xSlaveDispatcher.is())
88
0
        xResult = m_xSlaveDispatcher->queryDispatch(aURL, aTargetFrameName, nSearchFlags);
89
90
0
    return xResult;
91
0
}
92
93
uno::Sequence<OUString> SAL_CALL SwXDispatchProviderInterceptor::getInterceptedURLs()
94
8.47k
{
95
8.47k
    uno::Sequence<OUString> aRet =
96
8.47k
    {
97
8.47k
         u".uno:DataSourceBrowser/*"_ustr
98
8.47k
    };
99
100
8.47k
    return aRet;
101
8.47k
}
102
103
uno::Sequence< uno::Reference< frame::XDispatch > > SwXDispatchProviderInterceptor::queryDispatches(
104
    const uno::Sequence< frame::DispatchDescriptor >& aDescripts )
105
0
{
106
0
    DispatchMutexLock_Impl aLock;
107
0
    uno::Sequence< uno::Reference< frame::XDispatch> > aReturn(aDescripts.getLength());
108
0
    std::transform(aDescripts.begin(), aDescripts.end(), aReturn.getArray(),
109
0
        [this](const frame::DispatchDescriptor& rDescr) -> uno::Reference<frame::XDispatch> {
110
0
            return queryDispatch(rDescr.FeatureURL, rDescr.FrameName, rDescr.SearchFlags); });
111
0
    return aReturn;
112
0
}
113
114
uno::Reference< frame::XDispatchProvider > SwXDispatchProviderInterceptor::getSlaveDispatchProvider(  )
115
8.47k
{
116
8.47k
    DispatchMutexLock_Impl aLock;
117
8.47k
    return m_xSlaveDispatcher;
118
8.47k
}
119
120
void SwXDispatchProviderInterceptor::setSlaveDispatchProvider(
121
    const uno::Reference< frame::XDispatchProvider >& xNewDispatchProvider )
122
16.9k
{
123
16.9k
    DispatchMutexLock_Impl aLock;
124
16.9k
    m_xSlaveDispatcher = xNewDispatchProvider;
125
16.9k
}
126
127
uno::Reference< frame::XDispatchProvider > SwXDispatchProviderInterceptor::getMasterDispatchProvider(  )
128
8.47k
{
129
8.47k
    DispatchMutexLock_Impl aLock;
130
8.47k
    return m_xMasterDispatcher;
131
8.47k
}
132
133
void SwXDispatchProviderInterceptor::setMasterDispatchProvider(
134
    const uno::Reference< frame::XDispatchProvider >& xNewSupplier )
135
16.9k
{
136
16.9k
    DispatchMutexLock_Impl aLock;
137
16.9k
    m_xMasterDispatcher = xNewSupplier;
138
16.9k
}
139
140
void SwXDispatchProviderInterceptor::disposing( const lang::EventObject& )
141
0
{
142
0
    DispatchMutexLock_Impl aLock;
143
0
    if (m_xIntercepted.is())
144
0
    {
145
0
        m_xIntercepted->releaseDispatchProviderInterceptor(static_cast<frame::XDispatchProviderInterceptor*>(this));
146
0
        uno::Reference< lang::XComponent> xInterceptedComponent(m_xIntercepted, uno::UNO_QUERY);
147
0
        if (xInterceptedComponent.is())
148
0
            xInterceptedComponent->removeEventListener(static_cast<lang::XEventListener*>(this));
149
0
        m_xDispatch       = nullptr;
150
0
    }
151
0
    m_xIntercepted = nullptr;
152
0
}
153
154
void    SwXDispatchProviderInterceptor::Invalidate()
155
8.47k
{
156
8.47k
    DispatchMutexLock_Impl aLock;
157
8.47k
    if (m_xIntercepted.is())
158
8.47k
    {
159
8.47k
        m_xIntercepted->releaseDispatchProviderInterceptor(static_cast<frame::XDispatchProviderInterceptor*>(this));
160
8.47k
        uno::Reference< lang::XComponent> xInterceptedComponent(m_xIntercepted, uno::UNO_QUERY);
161
8.47k
        if (xInterceptedComponent.is())
162
8.47k
            xInterceptedComponent->removeEventListener(static_cast<lang::XEventListener*>(this));
163
8.47k
        m_xDispatch       = nullptr;
164
8.47k
    }
165
8.47k
    m_xIntercepted = nullptr;
166
8.47k
    m_pView = nullptr;
167
8.47k
}
168
169
SwXDispatch::SwXDispatch(SwView& rVw) :
170
0
    m_pView(&rVw),
171
0
    m_bOldEnable(false),
172
0
    m_bListenerAdded(false)
173
0
{
174
0
}
175
176
SwXDispatch::~SwXDispatch()
177
0
{
178
0
    if(m_bListenerAdded && m_pView)
179
0
    {
180
0
        uno::Reference<view::XSelectionSupplier> xSupplier = m_pView->GetUNOObject();
181
0
        uno::Reference<view::XSelectionChangeListener> xThis = this;
182
0
        xSupplier->removeSelectionChangeListener(xThis);
183
0
    }
184
0
}
185
186
void SwXDispatch::dispatch(const util::URL& aURL,
187
    const uno::Sequence< beans::PropertyValue >& aArgs)
188
0
{
189
0
    if(!m_pView)
190
0
        throw uno::RuntimeException();
191
0
#if !HAVE_FEATURE_DBCONNECTIVITY || ENABLE_FUZZERS
192
0
    (void) aArgs;
193
0
    if (false)
194
0
    {
195
0
    }
196
#else
197
    SwWrtShell& rSh = m_pView->GetWrtShell();
198
    SwDBManager* pDBManager = rSh.GetDBManager();
199
    if(aURL.Complete == cURLInsertContent)
200
    {
201
        svx::ODataAccessDescriptor aDescriptor(aArgs);
202
        SwMergeDescriptor aMergeDesc( DBMGR_MERGE, rSh, aDescriptor );
203
        pDBManager->Merge(aMergeDesc);
204
    }
205
    else if(aURL.Complete == cURLInsertColumns)
206
    {
207
        SwDBManager::InsertText(rSh, aArgs);
208
    }
209
    else if(aURL.Complete == cURLFormLetter)
210
    {
211
        SfxUnoAnyItem aDBProperties(FN_PARAM_DATABASE_PROPERTIES, uno::Any(aArgs));
212
        m_pView->GetViewFrame().GetDispatcher()->ExecuteList(
213
            FN_MAILMERGE_WIZARD,
214
            SfxCallMode::ASYNCHRON,
215
            { &aDBProperties });
216
    }
217
#endif
218
0
    else if(aURL.Complete == cURLDocumentDataSource)
219
0
    {
220
0
        OSL_FAIL("SwXDispatch::dispatch: this URL is not to be dispatched!");
221
0
    }
222
0
    else if(aURL.Complete == cInternalDBChangeNotification)
223
0
    {
224
0
        frame::FeatureStateEvent aEvent;
225
0
        aEvent.Source = getXWeak();
226
227
0
        const SwDBData& rData = m_pView->GetWrtShell().GetDBData();
228
0
        svx::ODataAccessDescriptor aDescriptor;
229
0
        aDescriptor.setDataSource(rData.sDataSource);
230
0
        aDescriptor[svx::DataAccessDescriptorProperty::Command]       <<= rData.sCommand;
231
0
        aDescriptor[svx::DataAccessDescriptorProperty::CommandType]   <<= rData.nCommandType;
232
233
0
        aEvent.State <<= aDescriptor.createPropertyValueSequence();
234
0
        aEvent.IsEnabled = !rData.sDataSource.isEmpty();
235
236
        // calls to statusChanged may call addStatusListener or removeStatusListener
237
        // so copy m_aStatusListenerVector on stack
238
0
        auto copyStatusListenerVector = m_aStatusListenerVector;
239
0
        for (auto & status : copyStatusListenerVector)
240
0
        {
241
0
            if(status.aURL.Complete == cURLDocumentDataSource)
242
0
            {
243
0
                aEvent.FeatureURL = status.aURL;
244
0
                status.xListener->statusChanged( aEvent );
245
0
            }
246
0
        }
247
0
    }
248
0
    else
249
0
        throw uno::RuntimeException();
250
251
0
}
252
253
void SwXDispatch::addStatusListener(
254
    const uno::Reference< frame::XStatusListener >& xControl, const util::URL& aURL )
255
0
{
256
0
    if(!m_pView)
257
0
        throw uno::RuntimeException();
258
0
    ShellMode eMode = m_pView->GetShellMode();
259
0
    bool bEnable = ShellMode::Text == eMode  ||
260
0
                       ShellMode::ListText == eMode  ||
261
0
                       ShellMode::TableText == eMode  ||
262
0
                       ShellMode::TableListText == eMode;
263
264
0
    m_bOldEnable = bEnable;
265
0
    frame::FeatureStateEvent aEvent;
266
0
    aEvent.IsEnabled = bEnable;
267
0
    aEvent.Source = getXWeak();
268
0
    aEvent.FeatureURL = aURL;
269
270
    // one of the URLs requires a special state...
271
0
    if (aURL.Complete == cURLDocumentDataSource)
272
0
    {
273
0
        const SwDBData& rData = m_pView->GetWrtShell().GetDBData();
274
275
0
        svx::ODataAccessDescriptor aDescriptor;
276
0
        aDescriptor.setDataSource(rData.sDataSource);
277
0
        aDescriptor[svx::DataAccessDescriptorProperty::Command]       <<= rData.sCommand;
278
0
        aDescriptor[svx::DataAccessDescriptorProperty::CommandType]   <<= rData.nCommandType;
279
280
0
        aEvent.State <<= aDescriptor.createPropertyValueSequence();
281
0
        aEvent.IsEnabled = !rData.sDataSource.isEmpty();
282
0
    }
283
284
0
    xControl->statusChanged( aEvent );
285
286
0
    StatusStruct_Impl aStatus;
287
0
    aStatus.xListener = xControl;
288
0
    aStatus.aURL = aURL;
289
0
    m_aStatusListenerVector.emplace_back(aStatus);
290
291
0
    if(!m_bListenerAdded)
292
0
    {
293
0
        uno::Reference<view::XSelectionSupplier> xSupplier = m_pView->GetUNOObject();
294
0
        uno::Reference<view::XSelectionChangeListener> xThis = this;
295
0
        xSupplier->addSelectionChangeListener(xThis);
296
0
        m_bListenerAdded = true;
297
0
    }
298
0
}
299
300
void SwXDispatch::removeStatusListener(
301
    const uno::Reference< frame::XStatusListener >& xControl, const util::URL&  )
302
0
{
303
0
    std::erase_if(
304
0
            m_aStatusListenerVector,
305
0
            [&](const StatusStruct_Impl& status) { return status.xListener.get() == xControl.get(); });
306
0
    if(m_aStatusListenerVector.empty() && m_pView)
307
0
    {
308
0
        uno::Reference<view::XSelectionSupplier> xSupplier = m_pView->GetUNOObject();
309
0
        uno::Reference<view::XSelectionChangeListener> xThis = this;
310
0
        xSupplier->removeSelectionChangeListener(xThis);
311
0
        m_bListenerAdded = false;
312
0
    }
313
0
}
314
315
void SwXDispatch::selectionChanged( const lang::EventObject&  )
316
0
{
317
0
    ShellMode eMode = m_pView->GetShellMode();
318
0
    bool bEnable = ShellMode::Text == eMode  ||
319
0
                       ShellMode::ListText == eMode  ||
320
0
                       ShellMode::TableText == eMode  ||
321
0
                       ShellMode::TableListText == eMode;
322
0
    if(bEnable == m_bOldEnable)
323
0
        return;
324
325
0
    m_bOldEnable = bEnable;
326
0
    frame::FeatureStateEvent aEvent;
327
0
    aEvent.IsEnabled = bEnable;
328
0
    aEvent.Source = getXWeak();
329
330
    // calls to statusChanged may call addStatusListener or removeStatusListener
331
    // so copy m_aStatusListenerVector on stack
332
0
    auto copyStatusListenerVector = m_aStatusListenerVector;
333
0
    for (auto & status : copyStatusListenerVector)
334
0
    {
335
0
        aEvent.FeatureURL = status.aURL;
336
0
        if (status.aURL.Complete != cURLDocumentDataSource)
337
            // the document's data source does not depend on the selection, so it's state does not change here
338
0
            status.xListener->statusChanged( aEvent );
339
0
    }
340
0
}
341
342
void SwXDispatch::disposing( const lang::EventObject& rSource )
343
0
{
344
0
    uno::Reference<view::XSelectionSupplier> xSupplier(rSource.Source, uno::UNO_QUERY);
345
0
    uno::Reference<view::XSelectionChangeListener> xThis = this;
346
0
    xSupplier->removeSelectionChangeListener(xThis);
347
0
    m_bListenerAdded = false;
348
349
0
    lang::EventObject aObject;
350
0
    aObject.Source = getXWeak();
351
    // calls to statusChanged may call addStatusListener or removeStatusListener
352
    // so copy m_aStatusListenerVector on stack
353
0
    auto copyStatusListenerVector = m_aStatusListenerVector;
354
0
    for (auto & status : copyStatusListenerVector)
355
0
    {
356
0
        status.xListener->disposing(aObject);
357
0
    }
358
0
    m_pView = nullptr;
359
0
}
360
361
const char* SwXDispatch::GetDBChangeURL()
362
0
{
363
0
    return cInternalDBChangeNotification;
364
0
}
365
366
SwXDispatchProviderInterceptor::DispatchMutexLock_Impl::DispatchMutexLock_Impl()
367
59.3k
{
368
59.3k
}
369
370
SwXDispatchProviderInterceptor::DispatchMutexLock_Impl::~DispatchMutexLock_Impl()
371
59.3k
{
372
59.3k
}
373
374
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */