Coverage Report

Created: 2025-12-08 09:28

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