Coverage Report

Created: 2026-07-10 11:04

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/svtools/source/misc/acceleratorexecute.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 <memory>
21
#include <svtools/acceleratorexecute.hxx>
22
23
#include <com/sun/star/awt/KeyEvent.hpp>
24
#include <com/sun/star/frame/ModuleManager.hpp>
25
#include <com/sun/star/frame/Desktop.hpp>
26
#include <com/sun/star/ui/GlobalAcceleratorConfiguration.hpp>
27
#include <com/sun/star/ui/XUIConfigurationManager.hpp>
28
#include <com/sun/star/ui/XUIConfigurationManager2.hpp>
29
#include <com/sun/star/ui/XModuleUIConfigurationManager2.hpp>
30
#include <com/sun/star/ui/theModuleUIConfigurationManagerSupplier.hpp>
31
#include <com/sun/star/ui/XUIConfigurationManagerSupplier.hpp>
32
#include <com/sun/star/awt/KeyModifier.hpp>
33
#include <com/sun/star/uno/Sequence.hxx>
34
#include <com/sun/star/beans/PropertyValue.hpp>
35
#include <com/sun/star/util/URLTransformer.hpp>
36
#include <cppuhelper/implbase.hxx>
37
38
#include <utility>
39
#include <vcl/evntpost.hxx>
40
#include <vcl/keycod.hxx>
41
#include <sal/log.hxx>
42
#include <vcl/lok.hxx>
43
#include <rtl/ref.hxx>
44
45
#include <comphelper/lok.hxx>
46
47
namespace svt
48
{
49
50
namespace {
51
52
class AsyncAccelExec : public cppu::WeakImplHelper<css::lang::XEventListener>
53
{
54
    private:
55
        css::uno::Reference<css::lang::XComponent> m_xFrame;
56
        css::uno::Reference< css::frame::XDispatch > m_xDispatch;
57
        css::util::URL m_aURL;
58
        vcl::EventPoster m_aAsyncCallback;
59
    public:
60
61
        /** creates a new instance of this class, which can be used
62
            one times only!
63
64
            This instance can be forced to execute its internal set request
65
            asynchronous. After that it deletes itself!
66
         */
67
        static rtl::Reference<AsyncAccelExec> createOneShotInstance(const css::uno::Reference<css::lang::XComponent>& xFrame,
68
                                                    const css::uno::Reference<css::frame::XDispatch>& xDispatch,
69
                                                    const css::util::URL& rURL);
70
71
        void execAsync();
72
    private:
73
74
        virtual void SAL_CALL disposing(const css::lang::EventObject&) override
75
0
        {
76
0
            m_xFrame->removeEventListener(this);
77
0
            m_xFrame.clear();
78
0
            m_xDispatch.clear();
79
0
        }
80
81
        /** @short  allow creation of instances of this class
82
                    by using our factory only!
83
         */
84
        AsyncAccelExec(css::uno::Reference<css::lang::XComponent> xFrame,
85
                                      css::uno::Reference< css::frame::XDispatch > xDispatch,
86
                                      css::util::URL aURL);
87
88
        DECL_LINK(impl_ts_asyncCallback, LinkParamNone*, void);
89
};
90
91
}
92
93
AcceleratorExecute::AcceleratorExecute()
94
0
{
95
0
}
96
97
AcceleratorExecute::~AcceleratorExecute()
98
0
{
99
    // does nothing real
100
0
}
101
102
103
std::unique_ptr<AcceleratorExecute> AcceleratorExecute::createAcceleratorHelper()
104
0
{
105
0
    return std::unique_ptr<AcceleratorExecute>(new AcceleratorExecute);
106
0
}
107
108
109
void AcceleratorExecute::init(const css::uno::Reference< css::uno::XComponentContext >& rxContext,
110
                              const css::uno::Reference< css::frame::XFrame >&              xEnv )
111
0
{
112
    // SAFE -> ----------------------------------
113
0
    std::unique_lock aLock(m_aLock);
114
115
    // take over the uno service manager
116
0
    m_xContext = rxContext;
117
118
    // specify our internal dispatch provider
119
    // frame or desktop?! => document or global config.
120
0
    bool bDesktopIsUsed = false;
121
0
    m_xDispatcher.set(xEnv, css::uno::UNO_QUERY);
122
0
    if (!m_xDispatcher.is())
123
0
    {
124
0
        aLock.unlock();
125
        // <- SAFE ------------------------------
126
127
0
        css::uno::Reference< css::frame::XDispatchProvider > xDispatcher(css::frame::Desktop::create(rxContext), css::uno::UNO_QUERY_THROW);
128
129
        // SAFE -> ------------------------------
130
0
        aLock.lock();
131
132
0
        m_xDispatcher  = std::move(xDispatcher);
133
0
        bDesktopIsUsed = true;
134
0
    }
135
136
0
    aLock.unlock();
137
    // <- SAFE ----------------------------------
138
139
    // open all needed configuration objects
140
0
    css::uno::Reference< css::ui::XAcceleratorConfiguration > xGlobalCfg;
141
0
    css::uno::Reference< css::ui::XAcceleratorConfiguration > xModuleCfg;
142
0
    css::uno::Reference< css::ui::XAcceleratorConfiguration > xDocCfg   ;
143
144
    // global cfg
145
0
    xGlobalCfg = css::ui::GlobalAcceleratorConfiguration::create(rxContext);
146
0
    if (!bDesktopIsUsed)
147
0
    {
148
        // module cfg
149
0
        xModuleCfg = AcceleratorExecute::st_openModuleConfig(rxContext, xEnv);
150
151
        // doc cfg
152
0
        css::uno::Reference< css::frame::XController > xController;
153
0
        css::uno::Reference< css::frame::XModel >      xModel;
154
0
        xController = xEnv->getController();
155
0
        if (xController.is())
156
0
            xModel = xController->getModel();
157
0
        if (xModel.is())
158
0
            xDocCfg = AcceleratorExecute::st_openDocConfig(xModel);
159
0
    }
160
161
    // SAFE -> ------------------------------
162
0
    aLock.lock();
163
164
0
    m_xGlobalCfg = std::move(xGlobalCfg);
165
0
    m_xModuleCfg = std::move(xModuleCfg);
166
0
    m_xDocCfg    = std::move(xDocCfg);
167
168
0
    aLock.unlock();
169
    // <- SAFE ----------------------------------
170
0
}
171
172
173
bool AcceleratorExecute::execute(const vcl::KeyCode& aVCLKey)
174
0
{
175
0
    css::awt::KeyEvent aAWTKey = AcceleratorExecute::st_VCLKey2AWTKey(aVCLKey);
176
0
    return execute(aAWTKey);
177
0
}
178
179
180
bool AcceleratorExecute::execute(const css::awt::KeyEvent& aAWTKey)
181
0
{
182
0
    OUString sCommand = impl_ts_findCommand(aAWTKey);
183
184
    // No Command found? Do nothing! User is not interested on any error handling .-)
185
0
    if (sCommand.isEmpty())
186
0
        return false;
187
188
    // SAFE -> ----------------------------------
189
0
    std::unique_lock aLock(m_aLock);
190
191
    // or for some reason m_xContext is NULL (which would crash impl_ts_getURLParser()
192
0
    if (!m_xContext.is())
193
0
        return false;
194
195
0
    css::uno::Reference< css::frame::XDispatchProvider > xProvider = m_xDispatcher;
196
197
0
    aLock.unlock();
198
    // <- SAFE ----------------------------------
199
200
    // convert command in URL structure
201
0
    css::uno::Reference< css::util::XURLTransformer > xParser = impl_ts_getURLParser();
202
0
    css::util::URL aURL;
203
0
    aURL.Complete = sCommand;
204
0
    xParser->parseStrict(aURL);
205
206
    // ask for dispatch object
207
0
    css::uno::Reference< css::frame::XDispatch > xDispatch = xProvider->queryDispatch(aURL, OUString(), 0);
208
0
    bool bRet = xDispatch.is();
209
0
    if ( bRet )
210
0
    {
211
        // Note: Such instance can be used one times only and destroy itself afterwards .-)
212
0
        css::uno::Reference<css::lang::XComponent> xFrame(xProvider, css::uno::UNO_QUERY);
213
0
        if (vcl::lok::isUnipoll())
214
0
        { // tdf#130382 - all synchronous really.
215
0
            try {
216
0
                xDispatch->dispatch (aURL, css::uno::Sequence< css::beans::PropertyValue >());
217
0
            }
218
0
            catch(const css::uno::Exception&ev)
219
0
            {
220
0
                SAL_INFO("svtools", "exception on key emission: " << ev.Message);
221
0
            }
222
0
        }
223
0
        else
224
0
        {
225
0
            rtl::Reference<AsyncAccelExec> pExec = AsyncAccelExec::createOneShotInstance(xFrame, xDispatch, aURL);
226
0
            pExec->execAsync();
227
0
        }
228
0
    }
229
230
0
    return bRet;
231
0
}
232
233
234
css::awt::KeyEvent AcceleratorExecute::st_VCLKey2AWTKey(const vcl::KeyCode& aVCLKey)
235
0
{
236
0
    css::awt::KeyEvent aAWTKey;
237
0
    aAWTKey.Modifiers = 0;
238
0
    aAWTKey.KeyCode   = static_cast<sal_Int16>(aVCLKey.GetCode());
239
240
0
    if (aVCLKey.IsShift())
241
0
        aAWTKey.Modifiers |= css::awt::KeyModifier::SHIFT;
242
0
    if (aVCLKey.IsMod1())
243
0
        aAWTKey.Modifiers |= css::awt::KeyModifier::MOD1;
244
0
    if (aVCLKey.IsMod2())
245
0
        aAWTKey.Modifiers |= css::awt::KeyModifier::MOD2;
246
0
    if (aVCLKey.IsMod3())
247
0
        aAWTKey.Modifiers |= css::awt::KeyModifier::MOD3;
248
0
    return aAWTKey;
249
0
}
250
251
252
vcl::KeyCode AcceleratorExecute::st_AWTKey2VCLKey(const css::awt::KeyEvent& aAWTKey)
253
0
{
254
0
    bool bShift = ((aAWTKey.Modifiers & css::awt::KeyModifier::SHIFT) == css::awt::KeyModifier::SHIFT );
255
0
    bool bMod1  = ((aAWTKey.Modifiers & css::awt::KeyModifier::MOD1 ) == css::awt::KeyModifier::MOD1  );
256
0
    bool bMod2  = ((aAWTKey.Modifiers & css::awt::KeyModifier::MOD2 ) == css::awt::KeyModifier::MOD2  );
257
0
    bool bMod3  = ((aAWTKey.Modifiers & css::awt::KeyModifier::MOD3 ) == css::awt::KeyModifier::MOD3  );
258
0
    sal_uInt16   nKey   = static_cast<sal_uInt16>(aAWTKey.KeyCode);
259
260
0
    return vcl::KeyCode(nKey, bShift, bMod1, bMod2, bMod3);
261
0
}
262
263
OUString AcceleratorExecute::findCommand(const css::awt::KeyEvent& aKey)
264
0
{
265
0
    return impl_ts_findCommand(aKey);
266
0
}
267
268
OUString AcceleratorExecute::impl_ts_findCommand(const css::awt::KeyEvent& aKey)
269
0
{
270
    // SAFE -> ----------------------------------
271
0
    std::unique_lock aLock(m_aLock);
272
273
0
    css::uno::Reference< css::ui::XAcceleratorConfiguration > xGlobalCfg = m_xGlobalCfg;
274
0
    css::uno::Reference< css::ui::XAcceleratorConfiguration > xModuleCfg = m_xModuleCfg;
275
0
    css::uno::Reference< css::ui::XAcceleratorConfiguration > xDocCfg    = m_xDocCfg   ;
276
277
0
    aLock.unlock();
278
    // <- SAFE ----------------------------------
279
280
0
    OUString sCommand;
281
282
0
    try
283
0
    {
284
0
        if (xDocCfg.is())
285
0
            sCommand = xDocCfg->getCommandByKeyEvent(aKey);
286
0
        if (!sCommand.isEmpty())
287
0
            return sCommand;
288
0
    }
289
0
    catch(const css::container::NoSuchElementException&)
290
0
        {}
291
292
0
    try
293
0
    {
294
0
        if (xModuleCfg.is())
295
0
            sCommand = xModuleCfg->getCommandByKeyEvent(aKey);
296
0
        if (!sCommand.isEmpty())
297
0
            return sCommand;
298
0
    }
299
0
    catch(const css::container::NoSuchElementException&)
300
0
        {}
301
302
0
    try
303
0
    {
304
0
        if (xGlobalCfg.is())
305
0
            sCommand = xGlobalCfg->getCommandByKeyEvent(aKey);
306
0
        if (!sCommand.isEmpty())
307
0
            return sCommand;
308
0
    }
309
0
    catch(const css::container::NoSuchElementException&)
310
0
        {}
311
312
    // fall back to functional key codes
313
0
    if( aKey.Modifiers == 0 )
314
0
    {
315
0
        switch( aKey.KeyCode )
316
0
        {
317
0
        case css::awt::Key::DELETE_TO_BEGIN_OF_LINE:
318
0
            return u".uno:DelToStartOfLine"_ustr;
319
0
        case css::awt::Key::DELETE_TO_END_OF_LINE:
320
0
            return u".uno:DelToEndOfLine"_ustr;
321
0
        case css::awt::Key::DELETE_TO_BEGIN_OF_PARAGRAPH:
322
0
            return u".uno:DelToStartOfPara"_ustr;
323
0
        case css::awt::Key::DELETE_TO_END_OF_PARAGRAPH:
324
0
            return u".uno:DelToEndOfPara"_ustr;
325
0
        case css::awt::Key::DELETE_WORD_BACKWARD:
326
0
            return u".uno:DelToStartOfWord"_ustr;
327
0
        case css::awt::Key::DELETE_WORD_FORWARD:
328
0
            return u".uno:DelToEndOfWord"_ustr;
329
0
        case css::awt::Key::INSERT_LINEBREAK:
330
0
            return u".uno:InsertLinebreak"_ustr;
331
0
        case css::awt::Key::INSERT_PARAGRAPH:
332
0
            return u".uno:InsertPara"_ustr;
333
0
        case css::awt::Key::MOVE_WORD_BACKWARD:
334
0
            return u".uno:GoToPrevWord"_ustr;
335
0
        case css::awt::Key::MOVE_WORD_FORWARD:
336
0
            return u".uno:GoToNextWord"_ustr;
337
0
        case css::awt::Key::MOVE_TO_BEGIN_OF_LINE:
338
0
            return u".uno:GoToStartOfLine"_ustr;
339
0
        case css::awt::Key::MOVE_TO_END_OF_LINE:
340
0
            return u".uno:GoToEndOfLine"_ustr;
341
0
        case css::awt::Key::MOVE_TO_BEGIN_OF_PARAGRAPH:
342
0
            return u".uno:GoToStartOfPara"_ustr;
343
0
        case css::awt::Key::MOVE_TO_END_OF_PARAGRAPH:
344
0
            return u".uno:GoToEndOfPara"_ustr;
345
0
        case css::awt::Key::MOVE_TO_BEGIN_OF_DOCUMENT:
346
0
            return u".uno:GoToStartOfDoc"_ustr;
347
0
        case css::awt::Key::MOVE_TO_END_OF_DOCUMENT:
348
0
            return u".uno:GoToEndOfDoc"_ustr;
349
0
        case css::awt::Key::SELECT_BACKWARD:
350
0
            return u".uno:CharLeftSel"_ustr;
351
0
        case css::awt::Key::SELECT_FORWARD:
352
0
            return u".uno:CharRightSel"_ustr;
353
0
        case css::awt::Key::SELECT_WORD_BACKWARD:
354
0
            return u".uno:WordLeftSel"_ustr;
355
0
        case css::awt::Key::SELECT_WORD_FORWARD:
356
0
            return u".uno:WordRightSel"_ustr;
357
0
        case css::awt::Key::SELECT_WORD:
358
0
            return u".uno:SelectWord"_ustr;
359
0
        case css::awt::Key::SELECT_LINE:
360
0
            return OUString();
361
0
        case css::awt::Key::SELECT_PARAGRAPH:
362
0
            return u".uno:SelectText"_ustr;
363
0
        case css::awt::Key::SELECT_TO_BEGIN_OF_LINE:
364
0
            return u".uno:StartOfLineSel"_ustr;
365
0
        case css::awt::Key::SELECT_TO_END_OF_LINE:
366
0
            return u".uno:EndOfLineSel"_ustr;
367
0
        case css::awt::Key::SELECT_TO_BEGIN_OF_PARAGRAPH:
368
0
            return u".uno:StartOfParaSel"_ustr;
369
0
        case css::awt::Key::SELECT_TO_END_OF_PARAGRAPH:
370
0
            return u".uno:EndOfParaSel"_ustr;
371
0
        case css::awt::Key::SELECT_TO_BEGIN_OF_DOCUMENT:
372
0
            return u".uno:StartOfDocumentSel"_ustr;
373
0
        case css::awt::Key::SELECT_TO_END_OF_DOCUMENT:
374
0
            return u".uno:EndOfDocumentSel"_ustr;
375
0
        case css::awt::Key::SELECT_ALL:
376
0
            return u".uno:SelectAll"_ustr;
377
0
        default:
378
0
            break;
379
0
        }
380
0
    }
381
382
0
    return OUString();
383
0
}
384
385
386
css::uno::Reference< css::ui::XAcceleratorConfiguration > AcceleratorExecute::st_openModuleConfig(const css::uno::Reference< css::uno::XComponentContext >& rxContext,
387
                                                                                                   const css::uno::Reference< css::frame::XFrame >&              xFrame)
388
0
{
389
0
    css::uno::Reference< css::frame::XModuleManager2 > xModuleDetection(
390
0
        css::frame::ModuleManager::create(rxContext));
391
392
0
    OUString sModule;
393
0
    try
394
0
    {
395
0
        sModule = xModuleDetection->identify(xFrame);
396
0
    }
397
0
    catch(const css::uno::RuntimeException&)
398
0
        { throw; }
399
0
    catch(const css::uno::Exception&)
400
0
        { return css::uno::Reference< css::ui::XAcceleratorConfiguration >(); }
401
402
0
    css::uno::Reference< css::ui::XModuleUIConfigurationManagerSupplier > xUISupplier(
403
0
        css::ui::theModuleUIConfigurationManagerSupplier::get(rxContext) );
404
405
0
    css::uno::Reference< css::ui::XAcceleratorConfiguration > xAccCfg;
406
0
    try
407
0
    {
408
0
        css::uno::Reference< css::ui::XUIConfigurationManager >   xUIManager = xUISupplier->getUIConfigurationManager(sModule);
409
0
        xAccCfg = xUIManager->getShortCutManager();
410
0
    }
411
0
    catch(const css::container::NoSuchElementException&)
412
0
        {}
413
0
    return xAccCfg;
414
0
}
415
416
css::uno::Reference<css::ui::XAcceleratorConfiguration> AcceleratorExecute::lok_createNewAcceleratorConfiguration(const css::uno::Reference< css::uno::XComponentContext >& rxContext, const OUString& sModule)
417
0
{
418
0
    css::uno::Reference< css::ui::XModuleUIConfigurationManagerSupplier > xUISupplier(css::ui::theModuleUIConfigurationManagerSupplier::get(rxContext));
419
420
0
    try
421
0
    {
422
0
        css::uno::Reference<css::ui::XUIConfigurationManager> xUIManager = xUISupplier->getUIConfigurationManager(sModule);
423
424
0
        css::ui::XModuleUIConfigurationManager2* t = static_cast<css::ui::XModuleUIConfigurationManager2*>(xUIManager.get());
425
426
        // Return new short cut manager in case current view's language is different from previous ones.
427
0
        return t->createShortCutManager();
428
0
    }
429
0
    catch(const css::container::NoSuchElementException&)
430
0
    {}
431
432
0
    return css::uno::Reference<css::ui::XAcceleratorConfiguration>();
433
0
}
434
435
void AcceleratorExecute::lok_setModuleConfig(const css::uno::Reference<css::ui::XAcceleratorConfiguration>& acceleratorConfig)
436
0
{
437
0
    this->m_xModuleCfg = acceleratorConfig;
438
0
}
439
440
css::uno::Reference< css::ui::XAcceleratorConfiguration > AcceleratorExecute::st_openDocConfig(const css::uno::Reference< css::frame::XModel >& xModel)
441
0
{
442
0
    css::uno::Reference< css::ui::XAcceleratorConfiguration >       xAccCfg;
443
0
    css::uno::Reference< css::ui::XUIConfigurationManagerSupplier > xUISupplier(xModel, css::uno::UNO_QUERY);
444
0
    if (xUISupplier.is())
445
0
    {
446
0
        css::uno::Reference< css::ui::XUIConfigurationManager >     xUIManager = xUISupplier->getUIConfigurationManager();
447
0
        xAccCfg = xUIManager->getShortCutManager();
448
0
    }
449
0
    return xAccCfg;
450
0
}
451
452
453
css::uno::Reference< css::util::XURLTransformer > AcceleratorExecute::impl_ts_getURLParser()
454
0
{
455
    // SAFE -> ----------------------------------
456
0
    std::unique_lock aLock(m_aLock);
457
458
0
    if (m_xURLParser.is())
459
0
        return m_xURLParser;
460
0
    css::uno::Reference< css::uno::XComponentContext > xContext = m_xContext;
461
462
0
    aLock.unlock();
463
    // <- SAFE ----------------------------------
464
465
0
    css::uno::Reference< css::util::XURLTransformer > xParser =  css::util::URLTransformer::create( xContext );
466
467
    // SAFE -> ----------------------------------
468
0
    aLock.lock();
469
0
    m_xURLParser = xParser;
470
0
    aLock.unlock();
471
    // <- SAFE ----------------------------------
472
473
0
    return xParser;
474
0
}
475
476
AsyncAccelExec::AsyncAccelExec(css::uno::Reference<css::lang::XComponent> xFrame,
477
                               css::uno::Reference<css::frame::XDispatch> xDispatch,
478
                               css::util::URL aURL)
479
0
    : m_xFrame(std::move(xFrame))
480
0
    , m_xDispatch(std::move(xDispatch))
481
0
    , m_aURL(std::move(aURL))
482
0
    , m_aAsyncCallback(LINK(this, AsyncAccelExec, impl_ts_asyncCallback))
483
0
{
484
0
    acquire();
485
0
}
486
487
rtl::Reference<AsyncAccelExec> AsyncAccelExec::createOneShotInstance(const css::uno::Reference<css::lang::XComponent> &xFrame,
488
                                                     const css::uno::Reference< css::frame::XDispatch >& xDispatch,
489
                                                     const css::util::URL& rURL)
490
0
{
491
0
    rtl::Reference<AsyncAccelExec> pExec = new AsyncAccelExec(xFrame, xDispatch, rURL);
492
0
    return pExec;
493
0
}
494
495
496
void AsyncAccelExec::execAsync()
497
0
{
498
0
    if (m_xFrame.is())
499
0
        m_xFrame->addEventListener(this);
500
0
    m_aAsyncCallback.Post();
501
0
}
502
503
IMPL_LINK_NOARG(AsyncAccelExec, impl_ts_asyncCallback, LinkParamNone*, void)
504
0
{
505
0
    if (m_xDispatch.is())
506
0
    {
507
0
        try
508
0
        {
509
0
            if (m_xFrame.is())
510
0
                m_xFrame->removeEventListener(this);
511
0
            m_xDispatch->dispatch(m_aURL, css::uno::Sequence< css::beans::PropertyValue >());
512
0
        }
513
0
        catch(const css::uno::Exception&)
514
0
        {
515
0
        }
516
0
    }
517
0
    release();
518
0
}
519
520
} // namespace svt
521
522
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */