Coverage Report

Created: 2025-11-16 09:57

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/svtools/source/uno/popupmenucontrollerbase.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
 * 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 <svtools/popupmenucontrollerbase.hxx>
21
22
#include <com/sun/star/beans/PropertyValue.hpp>
23
#include <com/sun/star/frame/XDispatchProvider.hpp>
24
#include <com/sun/star/frame/XFrame.hpp>
25
#include <com/sun/star/lang/DisposedException.hpp>
26
#include <com/sun/star/util/URLTransformer.hpp>
27
28
#include <utility>
29
#include <vcl/svapp.hxx>
30
#include <osl/mutex.hxx>
31
#include <cppuhelper/supportsservice.hxx>
32
#include <toolkit/awt/vclxmenu.hxx>
33
34
using namespace com::sun::star;
35
using namespace css::uno;
36
using namespace css::lang;
37
using namespace css::frame;
38
using namespace css::beans;
39
using namespace css::util;
40
41
namespace svt
42
{
43
44
namespace {
45
46
struct PopupMenuControllerBaseDispatchInfo
47
{
48
    Reference< XDispatch > mxDispatch;
49
    const URL maURL;
50
    const Sequence< PropertyValue > maArgs;
51
52
    PopupMenuControllerBaseDispatchInfo( const Reference< XDispatch >& xDispatch, URL aURL, const Sequence< PropertyValue >& rArgs )
53
0
        : mxDispatch( xDispatch ), maURL(std::move( aURL )), maArgs( rArgs ) {}
54
};
55
56
}
57
58
PopupMenuControllerBase::PopupMenuControllerBase( const Reference< XComponentContext >& xContext ) :
59
0
    m_bInitialized( false )
60
0
{
61
0
    if ( xContext.is() )
62
0
        m_xURLTransformer.set( util::URLTransformer::create( xContext ) );
63
0
}
64
65
PopupMenuControllerBase::~PopupMenuControllerBase()
66
0
{
67
0
}
68
69
// protected function
70
void PopupMenuControllerBase::resetPopupMenu( css::uno::Reference< css::awt::XPopupMenu > const & rPopupMenu )
71
0
{
72
0
    if ( rPopupMenu.is() && rPopupMenu->getItemCount() > 0 )
73
0
    {
74
0
        rPopupMenu->clear();
75
0
    }
76
0
}
77
78
void  PopupMenuControllerBase::disposing(std::unique_lock<std::mutex>& /*rGuard*/)
79
0
{
80
    // Reset our members and set disposed flag
81
0
    m_xFrame.clear();
82
0
    m_xDispatch.clear();
83
0
    m_xPopupMenu.clear();
84
0
}
85
86
// XServiceInfo
87
sal_Bool SAL_CALL PopupMenuControllerBase::supportsService( const OUString& ServiceName )
88
0
{
89
0
    return cppu::supportsService(this, ServiceName);
90
0
}
91
92
// XEventListener
93
void SAL_CALL PopupMenuControllerBase::disposing( const EventObject& )
94
0
{
95
0
    std::unique_lock aLock( m_aMutex );
96
0
    m_xFrame.clear();
97
0
    m_xDispatch.clear();
98
0
    m_xPopupMenu.clear();
99
0
}
100
101
// XMenuListener
102
void SAL_CALL PopupMenuControllerBase::itemHighlighted( const awt::MenuEvent& )
103
0
{
104
0
}
105
106
void SAL_CALL PopupMenuControllerBase::itemSelected( const awt::MenuEvent& rEvent )
107
0
{
108
0
    std::unique_lock aLock( m_aMutex );
109
0
    throwIfDisposed(aLock);
110
111
0
    if( m_xPopupMenu.is() )
112
0
    {
113
0
        Sequence<PropertyValue> aArgs;
114
0
        dispatchCommandImpl( aLock, m_xPopupMenu->getCommand( rEvent.MenuId ), aArgs, OUString() );
115
0
    }
116
0
}
117
118
void PopupMenuControllerBase::dispatchCommand( const OUString& sCommandURL,
119
                                               const css::uno::Sequence< css::beans::PropertyValue >& rArgs,
120
                                               const OUString& sTarget )
121
0
{
122
0
    std::unique_lock aLock( m_aMutex );
123
0
    throwIfDisposed(aLock);
124
0
    dispatchCommandImpl(aLock, sCommandURL, rArgs, sTarget);
125
0
}
126
127
void PopupMenuControllerBase::dispatchCommandImpl( std::unique_lock<std::mutex>& /*rGuard*/,
128
                                               const OUString& sCommandURL,
129
                                               const css::uno::Sequence< css::beans::PropertyValue >& rArgs,
130
                                               const OUString& sTarget )
131
0
{
132
133
0
    try
134
0
    {
135
0
        Reference< XDispatchProvider > xDispatchProvider( m_xFrame, UNO_QUERY_THROW );
136
0
        URL aURL;
137
0
        aURL.Complete = sCommandURL;
138
0
        m_xURLTransformer->parseStrict( aURL );
139
140
0
        Reference< XDispatch > xDispatch( xDispatchProvider->queryDispatch( aURL, sTarget, 0 ), UNO_SET_THROW );
141
142
0
        Application::PostUserEvent( LINK(nullptr, PopupMenuControllerBase, ExecuteHdl_Impl), new PopupMenuControllerBaseDispatchInfo( xDispatch, std::move(aURL), rArgs ) );
143
144
0
    }
145
0
    catch( Exception& )
146
0
    {
147
0
    }
148
149
0
}
150
151
IMPL_STATIC_LINK( PopupMenuControllerBase, ExecuteHdl_Impl, void*, p, void )
152
0
{
153
0
    PopupMenuControllerBaseDispatchInfo* pDispatchInfo = static_cast<PopupMenuControllerBaseDispatchInfo*>(p);
154
0
    pDispatchInfo->mxDispatch->dispatch( pDispatchInfo->maURL, pDispatchInfo->maArgs );
155
0
    delete pDispatchInfo;
156
0
}
157
158
void SAL_CALL PopupMenuControllerBase::itemActivated( const awt::MenuEvent& )
159
0
{
160
0
}
161
162
void SAL_CALL PopupMenuControllerBase::itemDeactivated( const awt::MenuEvent& )
163
0
{
164
0
}
165
166
void SAL_CALL PopupMenuControllerBase::updatePopupMenu()
167
0
{
168
0
    {
169
0
        std::unique_lock aLock(m_aMutex);
170
0
        throwIfDisposed(aLock);
171
0
    }
172
173
0
    updateCommand( m_aCommandURL );
174
0
}
175
176
void PopupMenuControllerBase::updateCommand( const OUString& rCommandURL )
177
0
{
178
0
    std::unique_lock aLock( m_aMutex );
179
0
    Reference< XStatusListener > xStatusListener(this);
180
0
    Reference< XDispatch > xDispatch( m_xDispatch );
181
0
    URL aTargetURL;
182
0
    aTargetURL.Complete = rCommandURL;
183
0
    m_xURLTransformer->parseStrict( aTargetURL );
184
0
    aLock.unlock();
185
186
    // Add/remove status listener to get a status update once
187
0
    if ( xDispatch.is() )
188
0
    {
189
0
        xDispatch->addStatusListener( xStatusListener, aTargetURL );
190
0
        xDispatch->removeStatusListener( xStatusListener, aTargetURL );
191
0
    }
192
0
}
193
194
195
// XDispatchProvider
196
Reference< XDispatch > SAL_CALL
197
PopupMenuControllerBase::queryDispatch(
198
    const URL& /*aURL*/,
199
    const OUString& /*sTarget*/,
200
    sal_Int32 /*nFlags*/ )
201
0
{
202
    // must be implemented by subclass
203
0
    std::unique_lock aLock( m_aMutex );
204
0
    throwIfDisposed(aLock);
205
206
0
    return Reference< XDispatch >();
207
0
}
208
209
Sequence< Reference< XDispatch > > SAL_CALL PopupMenuControllerBase::queryDispatches( const Sequence< DispatchDescriptor >& lDescriptor )
210
0
{
211
    // Create return list - which must have same size then the given descriptor
212
    // It's not allowed to pack it!
213
0
    {
214
0
        std::unique_lock aLock(m_aMutex);
215
0
        throwIfDisposed(aLock);
216
0
    }
217
218
0
    sal_Int32                                                          nCount = lDescriptor.getLength();
219
0
    uno::Sequence< uno::Reference< frame::XDispatch > > lDispatcher( nCount );
220
221
    // Step over all descriptors and try to get any dispatcher for it.
222
0
    std::transform(lDescriptor.begin(), lDescriptor.end(), lDispatcher.getArray(),
223
0
        [this](const DispatchDescriptor& rDesc) -> uno::Reference< frame::XDispatch > {
224
0
            return queryDispatch(rDesc.FeatureURL, rDesc.FrameName, rDesc.SearchFlags); });
225
226
0
    return lDispatcher;
227
0
}
228
229
// XDispatch
230
void SAL_CALL
231
PopupMenuControllerBase::dispatch(
232
    const URL& /*aURL*/,
233
    const Sequence< PropertyValue >& /*seqProperties*/ )
234
0
{
235
    // must be implemented by subclass
236
0
    std::unique_lock aLock( m_aMutex );
237
0
    throwIfDisposed(aLock);
238
0
}
239
240
void SAL_CALL
241
PopupMenuControllerBase::addStatusListener(
242
    const Reference< XStatusListener >& xControl,
243
    const URL& aURL )
244
0
{
245
0
    std::unique_lock aLock( m_aMutex );
246
0
    throwIfDisposed(aLock);
247
248
0
    bool bStatusUpdate( false );
249
0
    maStatusListeners.addInterface( aLock, xControl );
250
251
0
    if ( aURL.Complete.startsWith( m_aBaseURL ) )
252
0
        bStatusUpdate = true;
253
0
    aLock.unlock();
254
255
0
    if ( bStatusUpdate )
256
0
    {
257
        // Dummy update for popup menu controllers
258
0
        FeatureStateEvent aEvent;
259
0
        aEvent.FeatureURL = aURL;
260
0
        aEvent.IsEnabled  = true;
261
0
        aEvent.Requery    = false;
262
0
        aEvent.State      = Any();
263
0
        xControl->statusChanged( aEvent );
264
0
    }
265
0
}
266
267
void SAL_CALL PopupMenuControllerBase::removeStatusListener(
268
    const Reference< XStatusListener >& xControl,
269
    const URL& /*aURL*/ )
270
0
{
271
0
    std::unique_lock aLock( m_aMutex );
272
0
    maStatusListeners.removeInterface( aLock, xControl );
273
0
}
274
275
OUString PopupMenuControllerBase::determineBaseURL( std::u16string_view aURL )
276
0
{
277
    // Just use the main part of the URL for popup menu controllers
278
0
    OUString aMainURL( u"vnd.sun.star.popup:"_ustr );
279
280
0
    size_t nSchemePart = aURL.find( ':' );
281
0
    if (( nSchemePart != std::u16string_view::npos && nSchemePart > 0 ) &&
282
0
        ( aURL.size() > ( nSchemePart+1 )))
283
0
    {
284
0
        size_t nQueryPart = aURL.find( '?', nSchemePart );
285
0
        if ( nQueryPart != std::u16string_view::npos && nQueryPart > 0 )
286
0
            aMainURL += aURL.substr( nSchemePart, nQueryPart-nSchemePart );
287
0
        else if ( nQueryPart == std::u16string_view::npos )
288
0
            aMainURL += aURL.substr( nSchemePart+1 );
289
0
    }
290
291
0
    return aMainURL;
292
0
}
293
294
// XInitialization
295
void SAL_CALL PopupMenuControllerBase::initialize( const Sequence< Any >& aArguments )
296
0
{
297
0
    std::unique_lock aLock( m_aMutex );
298
0
    initializeImpl(aLock, aArguments);
299
0
}
300
301
// XInitialization
302
void PopupMenuControllerBase::initializeImpl( std::unique_lock<std::mutex>& /*rGuard*/, const Sequence< Any >& aArguments )
303
0
{
304
0
    bool bInitialized( m_bInitialized );
305
0
    if ( bInitialized )
306
0
        return;
307
308
0
    PropertyValue       aPropValue;
309
0
    OUString       aCommandURL;
310
0
    Reference< XFrame > xFrame;
311
312
0
    for ( const auto& rArgument : aArguments )
313
0
    {
314
0
        if ( rArgument >>= aPropValue )
315
0
        {
316
0
            if ( aPropValue.Name == "Frame" )
317
0
                aPropValue.Value >>= xFrame;
318
0
            else if ( aPropValue.Name == "CommandURL" )
319
0
                aPropValue.Value >>= aCommandURL;
320
0
            else if ( aPropValue.Name == "ModuleIdentifier" )
321
0
                aPropValue.Value >>= m_aModuleName;
322
0
        }
323
0
    }
324
325
0
    if ( xFrame.is() && !aCommandURL.isEmpty() )
326
0
    {
327
0
        m_xFrame        = std::move(xFrame);
328
0
        m_aCommandURL   = aCommandURL;
329
0
        m_aBaseURL      = determineBaseURL( aCommandURL );
330
0
        m_bInitialized  = true;
331
0
    }
332
0
}
333
// XPopupMenuController
334
void SAL_CALL PopupMenuControllerBase::setPopupMenu( const Reference< awt::XPopupMenu >& xPopupMenu )
335
0
{
336
0
    {
337
0
        std::unique_lock aLock( m_aMutex );
338
0
        throwIfDisposed(aLock);
339
340
0
        if ( !m_xFrame.is() || m_xPopupMenu.is() )
341
0
            return;
342
343
        // Create popup menu on demand
344
0
        SolarMutexGuard aSolarMutexGuard;
345
346
0
        m_xPopupMenu = dynamic_cast<VCLXPopupMenu*>(xPopupMenu.get());
347
0
        assert(bool(xPopupMenu) == bool(m_xPopupMenu) && "we only support VCLXPopupMenu");
348
0
        m_xPopupMenu->addMenuListener( Reference< awt::XMenuListener >(this) );
349
350
0
        Reference< XDispatchProvider > xDispatchProvider( m_xFrame, UNO_QUERY );
351
352
0
        URL aTargetURL;
353
0
        aTargetURL.Complete = m_aCommandURL;
354
0
        m_xURLTransformer->parseStrict( aTargetURL );
355
0
        m_xDispatch = xDispatchProvider->queryDispatch( aTargetURL, OUString(), 0 );
356
357
0
        impl_setPopupMenu(aLock);
358
0
    }
359
0
    updatePopupMenu();
360
0
}
361
362
void PopupMenuControllerBase::impl_setPopupMenu(std::unique_lock<std::mutex>& /*rGuard*/)
363
0
{
364
0
}
365
366
}
367
368
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */