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 <sal/config.h>
21
22 #include <utility>
23
24 #include <accelerators/acceleratorconfiguration.hxx>
25 #include <accelerators/keymapping.hxx>
26 #include <accelerators/presethandler.hxx>
27
28 #include <xml/saxnamespacefilter.hxx>
29 #include <xml/acceleratorconfigurationreader.hxx>
30 #include <xml/acceleratorconfigurationwriter.hxx>
31
32 #include <com/sun/star/xml/sax/Parser.hpp>
33 #include <com/sun/star/xml/sax/InputSource.hpp>
34 #include <com/sun/star/xml/sax/Writer.hpp>
35 #include <com/sun/star/io/IOException.hpp>
36 #include <com/sun/star/embed/ElementModes.hpp>
37 #include <com/sun/star/io/XSeekable.hpp>
38 #include <com/sun/star/io/XTruncate.hpp>
39
40 #include <vcl/svapp.hxx>
41 #include <com/sun/star/container/XNamed.hpp>
42 #include <com/sun/star/container/XNameContainer.hpp>
43 #include <com/sun/star/container/XHierarchicalNameAccess.hpp>
44 #include <com/sun/star/awt/KeyEvent.hpp>
45 #include <com/sun/star/awt/KeyModifier.hpp>
46 #include <com/sun/star/lang/XSingleServiceFactory.hpp>
47 #include <comphelper/configurationhelper.hxx>
48 #include <comphelper/sequence.hxx>
49 #include <officecfg/Setup.hxx>
50 #include <unotools/configpaths.hxx>
51 #include <svtools/acceleratorexecute.hxx>
52 #include <sal/log.hxx>
53 #include <rtl/ustrbuf.hxx>
54 #include <o3tl/string_view.hxx>
55
56 constexpr OUString PRESET_DEFAULT = u"default"_ustr;
57 constexpr OUString TARGET_CURRENT = u"current"_ustr;
58
59 namespace framework
60 {
61 constexpr OUString CFG_ENTRY_SECONDARY = u"SecondaryKeys"_ustr;
62 constexpr OUString CFG_PROP_COMMAND = u"Command"_ustr;
63
lcl_getKeyString(const css::awt::KeyEvent & aKeyEvent)64 static OUString lcl_getKeyString(const css::awt::KeyEvent& aKeyEvent)
65 {
66 const sal_Int32 nBeginIndex = 4; // "KEY_" is the prefix of an identifier...
67 OUString sKey(KeyMapping::get().mapCodeToIdentifier(aKeyEvent.KeyCode));
68 if (sKey.getLength() < nBeginIndex) // dead key
69 return OUString();
70 OUStringBuffer sKeyBuffer(sKey.subView(nBeginIndex));
71
72 if ( (aKeyEvent.Modifiers & css::awt::KeyModifier::SHIFT) == css::awt::KeyModifier::SHIFT )
73 sKeyBuffer.append("_SHIFT");
74 if ( (aKeyEvent.Modifiers & css::awt::KeyModifier::MOD1 ) == css::awt::KeyModifier::MOD1 )
75 sKeyBuffer.append("_MOD1");
76 if ( (aKeyEvent.Modifiers & css::awt::KeyModifier::MOD2 ) == css::awt::KeyModifier::MOD2 )
77 sKeyBuffer.append("_MOD2");
78 if ( (aKeyEvent.Modifiers & css::awt::KeyModifier::MOD3 ) == css::awt::KeyModifier::MOD3 )
79 sKeyBuffer.append("_MOD3");
80
81 return sKeyBuffer.makeStringAndClear();
82 }
83
XMLBasedAcceleratorConfiguration(const css::uno::Reference<css::uno::XComponentContext> & xContext)84 XMLBasedAcceleratorConfiguration::XMLBasedAcceleratorConfiguration(const css::uno::Reference< css::uno::XComponentContext >& xContext)
85 : m_xContext (xContext )
86 , m_aPresetHandler(xContext )
87 {
88 }
89
~XMLBasedAcceleratorConfiguration()90 XMLBasedAcceleratorConfiguration::~XMLBasedAcceleratorConfiguration()
91 {
92 SAL_WARN_IF(m_pWriteCache, "fwk.accelerators", "XMLBasedAcceleratorConfiguration::~XMLBasedAcceleratorConfiguration(): Changes not flushed. Ignore it ...");
93 }
94
getAllKeyEvents()95 css::uno::Sequence< css::awt::KeyEvent > SAL_CALL XMLBasedAcceleratorConfiguration::getAllKeyEvents()
96 {
97 SolarMutexGuard g;
98 AcceleratorCache& rCache = impl_getCFG();
99 AcceleratorCache::TKeyList lKeys = rCache.getAllKeys();
100 return comphelper::containerToSequence(lKeys);
101 }
102
getCommandByKeyEvent(const css::awt::KeyEvent & aKeyEvent)103 OUString SAL_CALL XMLBasedAcceleratorConfiguration::getCommandByKeyEvent(const css::awt::KeyEvent& aKeyEvent)
104 {
105 SolarMutexGuard g;
106 AcceleratorCache& rCache = impl_getCFG();
107 if (!rCache.hasKey(aKeyEvent))
108 throw css::container::NoSuchElementException(
109 OUString(),
110 static_cast< ::cppu::OWeakObject* >(this));
111 return rCache.getCommandByKey(aKeyEvent);
112 }
113
setKeyEvent(const css::awt::KeyEvent & aKeyEvent,const OUString & sCommand)114 void SAL_CALL XMLBasedAcceleratorConfiguration::setKeyEvent(const css::awt::KeyEvent& aKeyEvent,
115 const OUString& sCommand )
116 {
117 if (
118 (aKeyEvent.KeyCode == 0) &&
119 (aKeyEvent.KeyChar == 0) &&
120 (aKeyEvent.KeyFunc == 0) &&
121 (aKeyEvent.Modifiers == 0)
122 )
123 throw css::lang::IllegalArgumentException(
124 u"Such key event seems not to be supported by any operating system."_ustr,
125 static_cast< ::cppu::OWeakObject* >(this),
126 0);
127
128 if (sCommand.isEmpty())
129 throw css::lang::IllegalArgumentException(
130 u"Empty command strings are not allowed here."_ustr,
131 static_cast< ::cppu::OWeakObject* >(this),
132 1);
133
134 SolarMutexGuard g;
135 AcceleratorCache& rCache = impl_getCFG(true); // sal_True => force getting of a writeable cache!
136 rCache.setKeyCommandPair(aKeyEvent, sCommand);
137 }
138
removeKeyEvent(const css::awt::KeyEvent & aKeyEvent)139 void SAL_CALL XMLBasedAcceleratorConfiguration::removeKeyEvent(const css::awt::KeyEvent& aKeyEvent)
140 {
141 SolarMutexGuard g;
142 AcceleratorCache& rCache = impl_getCFG(true); // true => force using of a writeable cache
143 if (!rCache.hasKey(aKeyEvent))
144 throw css::container::NoSuchElementException(
145 OUString(),
146 static_cast< ::cppu::OWeakObject* >(this));
147 rCache.removeKey(aKeyEvent);
148 }
149
getKeyEventsByCommand(const OUString & sCommand)150 css::uno::Sequence< css::awt::KeyEvent > SAL_CALL XMLBasedAcceleratorConfiguration::getKeyEventsByCommand(const OUString& sCommand)
151 {
152 if (sCommand.isEmpty())
153 throw css::lang::IllegalArgumentException(
154 u"Empty command strings are not allowed here."_ustr,
155 static_cast< ::cppu::OWeakObject* >(this),
156 1);
157
158 SolarMutexGuard g;
159 AcceleratorCache& rCache = impl_getCFG();
160 if (!rCache.hasCommand(sCommand))
161 throw css::container::NoSuchElementException(
162 OUString(),
163 static_cast< ::cppu::OWeakObject* >(this));
164
165 AcceleratorCache::TKeyList lKeys = rCache.getKeysByCommand(sCommand);
166 return comphelper::containerToSequence(lKeys);
167 }
168
getPreferredKeyEventsForCommandList(const css::uno::Sequence<OUString> & lCommandList)169 css::uno::Sequence< css::uno::Any > SAL_CALL XMLBasedAcceleratorConfiguration::getPreferredKeyEventsForCommandList(const css::uno::Sequence< OUString >& lCommandList)
170 {
171 SolarMutexGuard g;
172
173 sal_Int32 i = 0;
174 sal_Int32 c = lCommandList.getLength();
175 css::uno::Sequence< css::uno::Any > lPreferredOnes (c); // don't pack list!
176 AcceleratorCache& rCache = impl_getCFG();
177
178 auto lPreferredOnesRange = asNonConstRange(lPreferredOnes);
179 for (i=0; i<c; ++i)
180 {
181 const OUString& rCommand = lCommandList[i];
182 if (rCommand.isEmpty())
183 throw css::lang::IllegalArgumentException(
184 u"Empty command strings are not allowed here."_ustr,
185 static_cast< ::cppu::OWeakObject* >(this),
186 static_cast<sal_Int16>(i));
187
188 if (!rCache.hasCommand(rCommand))
189 continue;
190
191 AcceleratorCache::TKeyList lKeys = rCache.getKeysByCommand(rCommand);
192 if ( lKeys.empty() )
193 continue;
194
195 css::uno::Any& rAny = lPreferredOnesRange[i];
196 rAny <<= *(lKeys.begin());
197 }
198
199 return lPreferredOnes;
200 }
201
removeCommandFromAllKeyEvents(const OUString & sCommand)202 void SAL_CALL XMLBasedAcceleratorConfiguration::removeCommandFromAllKeyEvents(const OUString& sCommand)
203 {
204 if (sCommand.isEmpty())
205 throw css::lang::IllegalArgumentException(
206 u"Empty command strings are not allowed here."_ustr,
207 static_cast< ::cppu::OWeakObject* >(this),
208 0);
209
210 SolarMutexGuard g;
211 AcceleratorCache& rCache = impl_getCFG(true); // sal_True => force getting of a writeable cache!
212 if (!rCache.hasCommand(sCommand))
213 throw css::container::NoSuchElementException(
214 u"Command does not exists inside this container."_ustr,
215 static_cast< ::cppu::OWeakObject* >(this));
216 rCache.removeCommand(sCommand);
217 }
218
reload()219 void SAL_CALL XMLBasedAcceleratorConfiguration::reload()
220 {
221 css::uno::Reference< css::io::XStream > xStream;
222 css::uno::Reference< css::io::XStream > xStreamNoLang;
223 {
224 SolarMutexGuard g;
225 xStream = m_aPresetHandler.openTarget(TARGET_CURRENT,
226 css::embed::ElementModes::READ);
227 try
228 {
229 xStreamNoLang = m_aPresetHandler.openPreset(PRESET_DEFAULT);
230 }
231 catch(const css::io::IOException&) {} // does not have to exist
232 }
233
234 css::uno::Reference< css::io::XInputStream > xIn;
235 if (xStream.is())
236 xIn = xStream->getInputStream();
237 if (!xIn.is())
238 throw css::io::IOException(
239 u"Could not open accelerator configuration for reading."_ustr,
240 static_cast< ::cppu::OWeakObject* >(this));
241
242 // impl_ts_load() does not clear the cache
243 {
244 SolarMutexGuard g;
245 m_aReadCache = AcceleratorCache();
246 }
247
248 impl_ts_load(xIn);
249
250 // Load also the general language independent default accelerators
251 // (ignoring the already defined accelerators)
252 if (xStreamNoLang.is())
253 {
254 xIn = xStreamNoLang->getInputStream();
255 if (xIn.is())
256 impl_ts_load(xIn);
257 }
258 }
259
store()260 void SAL_CALL XMLBasedAcceleratorConfiguration::store()
261 {
262 css::uno::Reference< css::io::XStream > xStream;
263 {
264 SolarMutexGuard g;
265 xStream = m_aPresetHandler.openTarget(TARGET_CURRENT,
266 css::embed::ElementModes::READWRITE); // open or create!
267 }
268
269 css::uno::Reference< css::io::XOutputStream > xOut;
270 if (xStream.is())
271 xOut = xStream->getOutputStream();
272
273 if (!xOut.is())
274 throw css::io::IOException(
275 u"Could not open accelerator configuration for saving."_ustr,
276 static_cast< ::cppu::OWeakObject* >(this));
277
278 impl_ts_save(xOut);
279
280 xOut.clear();
281 xStream.clear();
282
283 m_aPresetHandler.commitUserChanges();
284 }
285
storeToStorage(const css::uno::Reference<css::embed::XStorage> & xStorage)286 void SAL_CALL XMLBasedAcceleratorConfiguration::storeToStorage(const css::uno::Reference< css::embed::XStorage >& xStorage)
287 {
288 // no fallback from read/write to readonly!
289 css::uno::Reference< css::io::XStream > xStream = xStorage->openStreamElement(TARGET_CURRENT, css::embed::ElementModes::READWRITE);
290
291 css::uno::Reference< css::io::XOutputStream > xOut;
292 if (xStream.is())
293 xOut = xStream->getOutputStream();
294
295 if (!xOut.is())
296 throw css::io::IOException(
297 u"Could not open accelerator configuration for saving."_ustr,
298 static_cast< ::cppu::OWeakObject* >(this));
299
300 impl_ts_save(xOut);
301
302 // TODO inform listener about success, so it can flush the root and sub storage of this stream!
303 }
304
isModified()305 sal_Bool SAL_CALL XMLBasedAcceleratorConfiguration::isModified()
306 {
307 SolarMutexGuard g;
308 return (m_pWriteCache != nullptr);
309 }
310
isReadOnly()311 sal_Bool SAL_CALL XMLBasedAcceleratorConfiguration::isReadOnly()
312 {
313 css::uno::Reference< css::io::XStream > xStream;
314 {
315 SolarMutexGuard g;
316 xStream = m_aPresetHandler.openTarget(TARGET_CURRENT,
317 css::embed::ElementModes::READWRITE); // open or create!
318 }
319
320 css::uno::Reference< css::io::XOutputStream > xOut;
321 if (xStream.is())
322 xOut = xStream->getOutputStream();
323 return !(xOut.is());
324 }
325
setStorage(const css::uno::Reference<css::embed::XStorage> &)326 void SAL_CALL XMLBasedAcceleratorConfiguration::setStorage(const css::uno::Reference< css::embed::XStorage >& /*xStorage*/)
327 {
328 SAL_INFO("fwk.accelerators", "XMLBasedAcceleratorConfiguration::setStorage(): implement this HACK .-)");
329 }
330
hasStorage()331 sal_Bool SAL_CALL XMLBasedAcceleratorConfiguration::hasStorage()
332 {
333 SAL_INFO("fwk.accelerators", "XMLBasedAcceleratorConfiguration::hasStorage(): implement this HACK .-)");
334 return false;
335 }
336
addConfigurationListener(const css::uno::Reference<css::ui::XUIConfigurationListener> &)337 void SAL_CALL XMLBasedAcceleratorConfiguration::addConfigurationListener(const css::uno::Reference< css::ui::XUIConfigurationListener >& /*xListener*/)
338 {
339 SAL_INFO("fwk.accelerators", "XMLBasedAcceleratorConfiguration::addConfigurationListener(): implement me");
340 }
341
removeConfigurationListener(const css::uno::Reference<css::ui::XUIConfigurationListener> &)342 void SAL_CALL XMLBasedAcceleratorConfiguration::removeConfigurationListener(const css::uno::Reference< css::ui::XUIConfigurationListener >& /*xListener*/)
343 {
344 SAL_INFO("fwk.accelerators", "XMLBasedAcceleratorConfiguration::removeConfigurationListener(): implement me");
345 }
346
reset()347 void SAL_CALL XMLBasedAcceleratorConfiguration::reset()
348 {
349 {
350 SolarMutexGuard g;
351 m_aPresetHandler.copyPresetToTarget(PRESET_DEFAULT, TARGET_CURRENT);
352 }
353
354 reload();
355 }
356
addResetListener(const css::uno::Reference<css::form::XResetListener> &)357 void SAL_CALL XMLBasedAcceleratorConfiguration::addResetListener(const css::uno::Reference< css::form::XResetListener >& /*xListener*/)
358 {
359 SAL_INFO("fwk.accelerators", "XMLBasedAcceleratorConfiguration::addResetListener(): implement me");
360 }
361
removeResetListener(const css::uno::Reference<css::form::XResetListener> &)362 void SAL_CALL XMLBasedAcceleratorConfiguration::removeResetListener(const css::uno::Reference< css::form::XResetListener >& /*xListener*/)
363 {
364 SAL_INFO("fwk.accelerators", "XMLBasedAcceleratorConfiguration::removeResetListener(): implement me");
365 }
366
367 // IStorageListener
changesOccurred()368 void XMLBasedAcceleratorConfiguration::changesOccurred()
369 {
370 reload();
371 }
372
impl_ts_load(const css::uno::Reference<css::io::XInputStream> & xStream)373 void XMLBasedAcceleratorConfiguration::impl_ts_load(const css::uno::Reference< css::io::XInputStream >& xStream)
374 {
375 css::uno::Reference< css::uno::XComponentContext > xContext;
376 {
377 SolarMutexGuard g;
378 xContext = m_xContext;
379 m_pWriteCache.reset();
380 }
381
382 css::uno::Reference< css::io::XSeekable > xSeek(xStream, css::uno::UNO_QUERY);
383 if (xSeek.is())
384 xSeek->seek(0);
385
386 SolarMutexGuard g;
387
388 // create the parser queue
389 // Note: Use special filter object between parser and reader
390 // to get filtered xml with right namespaces ...
391 // Use further a temp cache for reading!
392 rtl::Reference<AcceleratorConfigurationReader> pReader = new AcceleratorConfigurationReader(m_aReadCache);
393 rtl::Reference<SaxNamespaceFilter> pFilter = new SaxNamespaceFilter(pReader);
394
395 // connect parser, filter and stream
396 css::uno::Reference< css::xml::sax::XParser > xParser = css::xml::sax::Parser::create(xContext);
397 xParser->setDocumentHandler(pFilter);
398
399 css::xml::sax::InputSource aSource;
400 aSource.aInputStream = xStream;
401
402 // TODO think about error handling
403 xParser->parseStream(aSource);
404 }
405
impl_ts_save(const css::uno::Reference<css::io::XOutputStream> & xStream)406 void XMLBasedAcceleratorConfiguration::impl_ts_save(const css::uno::Reference< css::io::XOutputStream >& xStream)
407 {
408 bool bChanged;
409 AcceleratorCache aCache;
410 css::uno::Reference< css::uno::XComponentContext > xContext;
411 {
412 SolarMutexGuard g;
413 bChanged = (m_pWriteCache != nullptr);
414 if (bChanged)
415 aCache = *m_pWriteCache;
416 else
417 aCache = m_aReadCache;
418 xContext = m_xContext;
419 }
420
421 css::uno::Reference< css::io::XTruncate > xClearable(xStream, css::uno::UNO_QUERY_THROW);
422 xClearable->truncate();
423
424 // TODO can be removed if seek(0) is done by truncate() automatically!
425 css::uno::Reference< css::io::XSeekable > xSeek(xStream, css::uno::UNO_QUERY);
426 if (xSeek.is())
427 xSeek->seek(0);
428
429 // combine writer/cache/stream etcpp.
430 css::uno::Reference< css::xml::sax::XWriter > xWriter = css::xml::sax::Writer::create(xContext);
431 xWriter->setOutputStream(xStream);
432
433 // write into the stream
434 css::uno::Reference< css::xml::sax::XDocumentHandler > xHandler(xWriter, css::uno::UNO_QUERY_THROW);
435 AcceleratorConfigurationWriter aWriter(aCache, xHandler);
436 aWriter.flush();
437
438 SolarMutexGuard g;
439 // take over all changes into the readonly cache ...
440 // and forget the copy-on-write copied cache
441 if (bChanged)
442 {
443 m_aReadCache = *m_pWriteCache;
444 m_pWriteCache.reset();
445 }
446 }
447
impl_getCFG(bool bWriteAccessRequested)448 AcceleratorCache& XMLBasedAcceleratorConfiguration::impl_getCFG(bool bWriteAccessRequested)
449 {
450 SolarMutexGuard g;
451
452 //create copy of our readonly-cache, if write access is forced ... but
453 //not still possible!
454 if ( bWriteAccessRequested && !m_pWriteCache )
455 {
456 m_pWriteCache.reset(new AcceleratorCache(m_aReadCache));
457 }
458
459 // in case, we have a writeable cache, we use it for reading too!
460 // Otherwise the API user can't find its own changes...
461 if (m_pWriteCache)
462 return *m_pWriteCache;
463 else
464 return m_aReadCache;
465 }
466
467 // static
impl_ts_getLocale()468 OUString XMLBasedAcceleratorConfiguration::impl_ts_getLocale()
469 {
470 OUString sISOLocale = officecfg::Setup::L10N::ooLocale::get();
471
472 if (sISOLocale.isEmpty())
473 return u"en-US"_ustr;
474 return sISOLocale;
475 }
476
477 /*******************************************************************************
478 *
479 * XCU based accelerator configuration
480 *
481 *******************************************************************************/
482
XCUBasedAcceleratorConfiguration(css::uno::Reference<css::uno::XComponentContext> xContext)483 XCUBasedAcceleratorConfiguration::XCUBasedAcceleratorConfiguration(css::uno::Reference< css::uno::XComponentContext > xContext)
484 : m_xContext (std::move(xContext ))
485 {
486 m_xCfg.set(
487 ::comphelper::ConfigurationHelper::openConfig( m_xContext, u"org.openoffice.Office.Accelerators"_ustr, ::comphelper::EConfigurationModes::AllLocales ),
488 css::uno::UNO_QUERY );
489 }
490
~XCUBasedAcceleratorConfiguration()491 XCUBasedAcceleratorConfiguration::~XCUBasedAcceleratorConfiguration()
492 {
493 }
494
getAllKeyEvents()495 css::uno::Sequence< css::awt::KeyEvent > SAL_CALL XCUBasedAcceleratorConfiguration::getAllKeyEvents()
496 {
497 SolarMutexGuard g;
498
499 AcceleratorCache::TKeyList lKeys = impl_getCFG(true).getAllKeys(); //get keys from PrimaryKeys set
500
501 AcceleratorCache::TKeyList lSecondaryKeys = impl_getCFG(false).getAllKeys(); //get keys from SecondaryKeys set
502 lKeys.reserve(lKeys.size()+lSecondaryKeys.size());
503 for (auto const& secondaryKey : lSecondaryKeys)
504 lKeys.push_back(secondaryKey);
505
506 return comphelper::containerToSequence(lKeys);
507 }
508
getCommandByKeyEvent(const css::awt::KeyEvent & aKeyEvent)509 OUString SAL_CALL XCUBasedAcceleratorConfiguration::getCommandByKeyEvent(const css::awt::KeyEvent& aKeyEvent)
510 {
511 SolarMutexGuard g;
512
513 AcceleratorCache& rPrimaryCache = impl_getCFG(true );
514 AcceleratorCache& rSecondaryCache = impl_getCFG(false);
515
516 if (!rPrimaryCache.hasKey(aKeyEvent) && !rSecondaryCache.hasKey(aKeyEvent))
517 throw css::container::NoSuchElementException(
518 OUString(),
519 static_cast< ::cppu::OWeakObject* >(this));
520
521 if (rPrimaryCache.hasKey(aKeyEvent))
522 return rPrimaryCache.getCommandByKey(aKeyEvent);
523 else
524 return rSecondaryCache.getCommandByKey(aKeyEvent);
525 }
526
setKeyEvent(const css::awt::KeyEvent & aKeyEvent,const OUString & sCommand)527 void SAL_CALL XCUBasedAcceleratorConfiguration::setKeyEvent(const css::awt::KeyEvent& aKeyEvent,
528 const OUString& sCommand )
529 {
530 SAL_INFO( "fwk.accelerators", "XCUBasedAcceleratorConfiguration::setKeyEvent" );
531
532 if (
533 (aKeyEvent.KeyCode == 0) &&
534 (aKeyEvent.KeyChar == 0) &&
535 (aKeyEvent.KeyFunc == 0) &&
536 (aKeyEvent.Modifiers == 0)
537 )
538 throw css::lang::IllegalArgumentException(
539 u"Such key event seems not to be supported by any operating system."_ustr,
540 static_cast< ::cppu::OWeakObject* >(this),
541 0);
542
543 if (sCommand.isEmpty())
544 throw css::lang::IllegalArgumentException(
545 u"Empty command strings are not allowed here."_ustr,
546 static_cast< ::cppu::OWeakObject* >(this),
547 1);
548
549 SolarMutexGuard g;
550
551 AcceleratorCache& rPrimaryCache = impl_getCFG(true, true ); // sal_True => force getting of a writeable cache!
552 AcceleratorCache& rSecondaryCache = impl_getCFG(false, true); // sal_True => force getting of a writeable cache!
553
554 if ( rPrimaryCache.hasKey(aKeyEvent) )
555 {
556 OUString sOriginalCommand = rPrimaryCache.getCommandByKey(aKeyEvent);
557 if ( sCommand != sOriginalCommand )
558 {
559 if (rSecondaryCache.hasCommand(sOriginalCommand))
560 {
561 AcceleratorCache::TKeyList lSecondaryKeys = rSecondaryCache.getKeysByCommand(sOriginalCommand);
562 rSecondaryCache.removeKey(lSecondaryKeys[0]);
563 rPrimaryCache.setKeyCommandPair(lSecondaryKeys[0], sOriginalCommand);
564 }
565
566 if (rPrimaryCache.hasCommand(sCommand))
567 {
568 AcceleratorCache::TKeyList lPrimaryKeys = rPrimaryCache.getKeysByCommand(sCommand);
569 rPrimaryCache.removeKey(lPrimaryKeys[0]);
570 rSecondaryCache.setKeyCommandPair(lPrimaryKeys[0], sCommand);
571 }
572
573 rPrimaryCache.setKeyCommandPair(aKeyEvent, sCommand);
574 }
575 }
576
577 else if ( rSecondaryCache.hasKey(aKeyEvent) )
578 {
579 OUString sOriginalCommand = rSecondaryCache.getCommandByKey(aKeyEvent);
580 if (sCommand != sOriginalCommand)
581 {
582 if (rPrimaryCache.hasCommand(sCommand))
583 {
584 AcceleratorCache::TKeyList lPrimaryKeys = rPrimaryCache.getKeysByCommand(sCommand);
585 rPrimaryCache.removeKey(lPrimaryKeys[0]);
586 rSecondaryCache.setKeyCommandPair(lPrimaryKeys[0], sCommand);
587 }
588
589 rSecondaryCache.removeKey(aKeyEvent);
590 rPrimaryCache.setKeyCommandPair(aKeyEvent, sCommand);
591 }
592 }
593
594 else
595 {
596 if (rPrimaryCache.hasCommand(sCommand))
597 {
598 AcceleratorCache::TKeyList lPrimaryKeys = rPrimaryCache.getKeysByCommand(sCommand);
599 rPrimaryCache.removeKey(lPrimaryKeys[0]);
600 rSecondaryCache.setKeyCommandPair(lPrimaryKeys[0], sCommand);
601 }
602
603 rPrimaryCache.setKeyCommandPair(aKeyEvent, sCommand);
604 }
605 }
606
removeKeyEvent(const css::awt::KeyEvent & aKeyEvent)607 void SAL_CALL XCUBasedAcceleratorConfiguration::removeKeyEvent(const css::awt::KeyEvent& aKeyEvent)
608 {
609 SolarMutexGuard g;
610
611 AcceleratorCache& rPrimaryCache = impl_getCFG(true, true );
612 AcceleratorCache& rSecondaryCache = impl_getCFG(false, true);
613
614 if (!rPrimaryCache.hasKey(aKeyEvent) && !rSecondaryCache.hasKey(aKeyEvent))
615 throw css::container::NoSuchElementException(
616 OUString(),
617 static_cast< ::cppu::OWeakObject* >(this));
618
619 if (rPrimaryCache.hasKey(aKeyEvent))
620 {
621 OUString sOriginalCommand = rPrimaryCache.getCommandByKey(aKeyEvent);
622 if (!sOriginalCommand.isEmpty())
623 {
624 if (rSecondaryCache.hasCommand(sOriginalCommand))
625 {
626 AcceleratorCache::TKeyList lSecondaryKeys = rSecondaryCache.getKeysByCommand(sOriginalCommand);
627 rSecondaryCache.removeKey(lSecondaryKeys[0]);
628 rPrimaryCache.setKeyCommandPair(lSecondaryKeys[0], sOriginalCommand);
629 }
630
631 rPrimaryCache.removeKey(aKeyEvent);
632 }
633
634 }
635 else
636 {
637 OUString sDelCommand = rSecondaryCache.getCommandByKey(aKeyEvent);
638 if (!sDelCommand.isEmpty())
639 rSecondaryCache.removeKey(aKeyEvent);
640 }
641 }
642
getKeyEventsByCommand(const OUString & sCommand)643 css::uno::Sequence< css::awt::KeyEvent > SAL_CALL XCUBasedAcceleratorConfiguration::getKeyEventsByCommand(const OUString& sCommand)
644 {
645 if (sCommand.isEmpty())
646 throw css::lang::IllegalArgumentException(
647 u"Empty command strings are not allowed here."_ustr,
648 static_cast< ::cppu::OWeakObject* >(this),
649 1);
650
651 SolarMutexGuard g;
652
653 AcceleratorCache& rPrimaryCache = impl_getCFG(true );
654 AcceleratorCache& rSecondaryCache = impl_getCFG(false);
655
656 if (!rPrimaryCache.hasCommand(sCommand) && !rSecondaryCache.hasCommand(sCommand))
657 throw css::container::NoSuchElementException(
658 OUString(),
659 static_cast< ::cppu::OWeakObject* >(this));
660
661 AcceleratorCache::TKeyList lKeys = rPrimaryCache.getKeysByCommand(sCommand);
662
663 AcceleratorCache::TKeyList lSecondaryKeys = rSecondaryCache.getKeysByCommand(sCommand);
664 for (auto const& secondaryKey : lSecondaryKeys)
665 lKeys.push_back(secondaryKey);
666
667 return comphelper::containerToSequence(lKeys);
668 }
669
lcl_getPreferredKey(const AcceleratorCache::TKeyList & lKeys)670 static AcceleratorCache::TKeyList::const_iterator lcl_getPreferredKey(const AcceleratorCache::TKeyList& lKeys)
671 {
672 return std::find_if(lKeys.begin(), lKeys.end(), [](const css::awt::KeyEvent& rAWTKey) {
673 return !::svt::AcceleratorExecute::st_AWTKey2VCLKey(rAWTKey).GetName().isEmpty(); });
674 }
675
getPreferredKeyEventsForCommandList(const css::uno::Sequence<OUString> & lCommandList)676 css::uno::Sequence< css::uno::Any > SAL_CALL XCUBasedAcceleratorConfiguration::getPreferredKeyEventsForCommandList(const css::uno::Sequence< OUString >& lCommandList)
677 {
678 SolarMutexGuard g;
679
680 sal_Int32 i = 0;
681 sal_Int32 c = lCommandList.getLength();
682 css::uno::Sequence< css::uno::Any > lPreferredOnes (c); // don't pack list!
683 AcceleratorCache& rCache = impl_getCFG(true);
684
685 auto lPreferredOnesRange = asNonConstRange(lPreferredOnes);
686 for (i=0; i<c; ++i)
687 {
688 const OUString& rCommand = lCommandList[i];
689 if (rCommand.isEmpty())
690 throw css::lang::IllegalArgumentException(
691 u"Empty command strings are not allowed here."_ustr,
692 static_cast< ::cppu::OWeakObject* >(this),
693 static_cast<sal_Int16>(i));
694
695 if (!rCache.hasCommand(rCommand))
696 continue;
697
698 AcceleratorCache::TKeyList lKeys = rCache.getKeysByCommand(rCommand);
699 if ( lKeys.empty() )
700 continue;
701
702 AcceleratorCache::TKeyList::const_iterator pPreferredKey = lcl_getPreferredKey(lKeys);
703 if (pPreferredKey != lKeys.end ())
704 {
705 css::uno::Any& rAny = lPreferredOnesRange[i];
706 rAny <<= *pPreferredKey;
707 }
708 }
709
710 return lPreferredOnes;
711 }
712
removeCommandFromAllKeyEvents(const OUString & sCommand)713 void SAL_CALL XCUBasedAcceleratorConfiguration::removeCommandFromAllKeyEvents(const OUString& sCommand)
714 {
715 if (sCommand.isEmpty())
716 throw css::lang::IllegalArgumentException(
717 u"Empty command strings are not allowed here."_ustr,
718 static_cast< ::cppu::OWeakObject* >(this),
719 0);
720
721 SolarMutexGuard g;
722
723 AcceleratorCache& rPrimaryCache = impl_getCFG(true, true );
724 AcceleratorCache& rSecondaryCache = impl_getCFG(false, true);
725
726 if (!rPrimaryCache.hasCommand(sCommand) && !rSecondaryCache.hasCommand(sCommand))
727 throw css::container::NoSuchElementException(
728 u"Command does not exists inside this container."_ustr,
729 static_cast< ::cppu::OWeakObject* >(this));
730
731 if (rPrimaryCache.hasCommand(sCommand))
732 rPrimaryCache.removeCommand(sCommand);
733 if (rSecondaryCache.hasCommand(sCommand))
734 rSecondaryCache.removeCommand(sCommand);
735 }
736
reload()737 void SAL_CALL XCUBasedAcceleratorConfiguration::reload()
738 {
739 SAL_INFO( "fwk.accelerators", "XCUBasedAcceleratorConfiguration::reload()" );
740
741 SolarMutexGuard g;
742
743 bool bPreferred;
744 css::uno::Reference< css::container::XNameAccess > xAccess;
745
746 bPreferred = true;
747 m_aPrimaryReadCache = AcceleratorCache();
748 m_pPrimaryWriteCache.reset();
749 m_xCfg->getByName(CFG_ENTRY_PRIMARY) >>= xAccess;
750 impl_ts_load(bPreferred, xAccess); // load the preferred keys
751
752 bPreferred = false;
753 m_aSecondaryReadCache = AcceleratorCache();
754 m_pSecondaryWriteCache.reset();
755 m_xCfg->getByName(CFG_ENTRY_SECONDARY) >>= xAccess;
756 impl_ts_load(bPreferred, xAccess); // load the secondary keys
757 }
758
store()759 void SAL_CALL XCUBasedAcceleratorConfiguration::store()
760 {
761 SAL_INFO( "fwk.accelerators", "XCUBasedAcceleratorConfiguration::store()" );
762
763 SolarMutexGuard g;
764
765 bool bPreferred;
766
767 bPreferred = true;
768 // on-demand creation of the primary write cache
769 impl_getCFG(bPreferred, true);
770 impl_ts_save(bPreferred);
771
772 bPreferred = false;
773 // on-demand creation of the secondary write cache
774 impl_getCFG(bPreferred, true);
775 impl_ts_save(bPreferred);
776 }
777
storeToStorage(const css::uno::Reference<css::embed::XStorage> & xStorage)778 void SAL_CALL XCUBasedAcceleratorConfiguration::storeToStorage(const css::uno::Reference< css::embed::XStorage >& xStorage)
779 {
780 // use m_aCache + old AcceleratorXMLWriter to store data directly on storage given as parameter ...
781 if (!xStorage.is())
782 return;
783
784 tools::Long nOpenModes = css::embed::ElementModes::READWRITE;
785 css::uno::Reference< css::embed::XStorage > xAcceleratorTypeStorage = xStorage->openStorageElement(u"accelerator"_ustr, nOpenModes);
786 if (!xAcceleratorTypeStorage.is())
787 return;
788
789 css::uno::Reference< css::io::XStream > xStream = xAcceleratorTypeStorage->openStreamElement(u"current"_ustr, nOpenModes);
790 css::uno::Reference< css::io::XOutputStream > xOut;
791 if (xStream.is())
792 xOut = xStream->getOutputStream();
793 if (!xOut.is())
794 throw css::io::IOException(
795 u"Could not open accelerator configuration for saving."_ustr,
796 static_cast< ::cppu::OWeakObject* >(this));
797
798 // the original m_aCache has been split into primary cache and secondary cache...
799 // we should merge them before storing to storage
800 AcceleratorCache aCache;
801 {
802 SolarMutexGuard g;
803
804 if (m_pPrimaryWriteCache != nullptr)
805 aCache = *m_pPrimaryWriteCache;
806 else
807 aCache = m_aPrimaryReadCache;
808
809 AcceleratorCache::TKeyList lKeys;
810 if (m_pSecondaryWriteCache!=nullptr)
811 {
812 lKeys = m_pSecondaryWriteCache->getAllKeys();
813 for (auto const& lKey : lKeys)
814 aCache.setKeyCommandPair(lKey, m_pSecondaryWriteCache->getCommandByKey(lKey));
815 }
816 else
817 {
818 lKeys = m_aSecondaryReadCache.getAllKeys();
819 for (auto const& lKey : lKeys)
820 aCache.setKeyCommandPair(lKey, m_aSecondaryReadCache.getCommandByKey(lKey));
821 }
822 }
823
824 css::uno::Reference< css::io::XTruncate > xClearable(xOut, css::uno::UNO_QUERY_THROW);
825 xClearable->truncate();
826 css::uno::Reference< css::io::XSeekable > xSeek(xOut, css::uno::UNO_QUERY);
827 if (xSeek.is())
828 xSeek->seek(0);
829
830 css::uno::Reference< css::xml::sax::XWriter > xWriter = css::xml::sax::Writer::create(m_xContext);
831 xWriter->setOutputStream(xOut);
832
833 // write into the stream
834 css::uno::Reference< css::xml::sax::XDocumentHandler > xHandler(xWriter, css::uno::UNO_QUERY_THROW);
835 AcceleratorConfigurationWriter aWriter(aCache, xHandler);
836 aWriter.flush();
837 }
838
isModified()839 sal_Bool SAL_CALL XCUBasedAcceleratorConfiguration::isModified()
840 {
841 return false;
842 }
843
isReadOnly()844 sal_Bool SAL_CALL XCUBasedAcceleratorConfiguration::isReadOnly()
845 {
846 return false;
847 }
848
setStorage(const css::uno::Reference<css::embed::XStorage> &)849 void SAL_CALL XCUBasedAcceleratorConfiguration::setStorage(const css::uno::Reference< css::embed::XStorage >& /*xStorage*/)
850 {
851 SAL_INFO("fwk.accelerators", "XCUBasedAcceleratorConfiguration::setStorage(): implement this HACK .-)");
852 }
853
hasStorage()854 sal_Bool SAL_CALL XCUBasedAcceleratorConfiguration::hasStorage()
855 {
856 SAL_INFO("fwk.accelerators", "XCUBasedAcceleratorConfiguration::hasStorage(): implement this HACK .-)");
857 return false;
858 }
859
addConfigurationListener(const css::uno::Reference<css::ui::XUIConfigurationListener> &)860 void SAL_CALL XCUBasedAcceleratorConfiguration::addConfigurationListener(const css::uno::Reference< css::ui::XUIConfigurationListener >& /*xListener*/)
861 {
862 SAL_INFO("fwk.accelerators", "XCUBasedAcceleratorConfiguration::addConfigurationListener(): implement me");
863 }
864
removeConfigurationListener(const css::uno::Reference<css::ui::XUIConfigurationListener> &)865 void SAL_CALL XCUBasedAcceleratorConfiguration::removeConfigurationListener(const css::uno::Reference< css::ui::XUIConfigurationListener >& /*xListener*/)
866 {
867 SAL_INFO("fwk.accelerators", "XCUBasedAcceleratorConfiguration::removeConfigurationListener(): implement me");
868 }
869
reset()870 void SAL_CALL XCUBasedAcceleratorConfiguration::reset()
871 {
872 css::uno::Reference< css::container::XNamed > xNamed(m_xCfg, css::uno::UNO_QUERY);
873 OUString sConfig = xNamed->getName();
874 if ( sConfig == "Global" )
875 {
876 m_xCfg.set(
877 ::comphelper::ConfigurationHelper::openConfig( m_xContext, CFG_ENTRY_GLOBAL, ::comphelper::EConfigurationModes::AllLocales ),
878 css::uno::UNO_QUERY );
879 XCUBasedAcceleratorConfiguration::reload();
880 }
881 else if ( sConfig == "Modules" )
882 {
883 m_xCfg.set(
884 ::comphelper::ConfigurationHelper::openConfig( m_xContext, CFG_ENTRY_MODULES, ::comphelper::EConfigurationModes::AllLocales ),
885 css::uno::UNO_QUERY );
886 XCUBasedAcceleratorConfiguration::reload();
887 }
888 }
889
addResetListener(const css::uno::Reference<css::form::XResetListener> &)890 void SAL_CALL XCUBasedAcceleratorConfiguration::addResetListener(const css::uno::Reference< css::form::XResetListener >& /*xListener*/)
891 {
892 SAL_INFO("fwk.accelerators", "XCUBasedAcceleratorConfiguration::addResetListener(): implement me");
893 }
894
removeResetListener(const css::uno::Reference<css::form::XResetListener> &)895 void SAL_CALL XCUBasedAcceleratorConfiguration::removeResetListener(const css::uno::Reference< css::form::XResetListener >& /*xListener*/)
896 {
897 SAL_INFO("fwk.accelerators", "XCUBasedAcceleratorConfiguration::removeResetListener(): implement me");
898 }
899
changesOccurred(const css::util::ChangesEvent & aEvent)900 void SAL_CALL XCUBasedAcceleratorConfiguration::changesOccurred(const css::util::ChangesEvent& aEvent)
901 {
902 SAL_INFO( "fwk.accelerators", "XCUBasedAcceleratorConfiguration::changesOccurred()" );
903
904 css::uno::Reference< css::container::XHierarchicalNameAccess > xHAccess;
905 aEvent.Base >>= xHAccess;
906 if (! xHAccess.is ())
907 return;
908
909 css::util::ChangesEvent aReceivedEvents( aEvent );
910 const sal_Int32 c = aReceivedEvents.Changes.getLength();
911 for (sal_Int32 i=0; i<c; ++i)
912 {
913 const css::util::ElementChange& aChange = aReceivedEvents.Changes[i];
914
915 // Only path of form "PrimaryKeys/Modules/Module['<module_name>']/Key['<command_url>']/Command[<locale>]" will
916 // be interesting for use. Sometimes short path values are given also by the broadcaster ... but they must be ignored :-)
917 // So we try to split the path into 3 parts (module isn't important here, because we already know it ... because
918 // these instance is bound to a specific module configuration ... or it''s the global configuration where no module is given at all.
919
920 OUString sOrgPath;
921 OUString sPath;
922 OUString sKey;
923
924 aChange.Accessor >>= sOrgPath;
925 sPath = sOrgPath;
926 OUString sPrimarySecondary = ::utl::extractFirstFromConfigurationPath(sPath, &sPath);
927 OUString sGlobalModules = ::utl::extractFirstFromConfigurationPath(sPath, &sPath);
928
929 if ( sGlobalModules == CFG_ENTRY_GLOBAL )
930 {
931 sKey = ::utl::extractFirstFromConfigurationPath(sPath, &sPath);
932 if ( !sKey.isEmpty() && !sPath.isEmpty() )
933 reloadChanged(sPrimarySecondary, sGlobalModules, OUString(), sKey);
934 }
935 else if ( sGlobalModules == CFG_ENTRY_MODULES )
936 {
937 OUString sModule = ::utl::extractFirstFromConfigurationPath(sPath, &sPath);
938 sKey = ::utl::extractFirstFromConfigurationPath(sPath, &sPath);
939
940 if ( !sKey.isEmpty() && !sPath.isEmpty() )
941 {
942 reloadChanged(sPrimarySecondary, sGlobalModules, sModule, sKey);
943 }
944 }
945 }
946 }
947
disposing(const css::lang::EventObject &)948 void SAL_CALL XCUBasedAcceleratorConfiguration::disposing(const css::lang::EventObject& /*aSource*/)
949 {
950 }
951
impl_ts_load(bool bPreferred,const css::uno::Reference<css::container::XNameAccess> & xCfg)952 void XCUBasedAcceleratorConfiguration::impl_ts_load( bool bPreferred, const css::uno::Reference< css::container::XNameAccess >& xCfg )
953 {
954 AcceleratorCache aReadCache;
955 css::uno::Reference< css::container::XNameAccess > xAccess;
956 if ( m_sGlobalOrModules == "Global" )
957 xCfg->getByName(CFG_ENTRY_GLOBAL) >>= xAccess;
958 else if ( m_sGlobalOrModules == "Modules" )
959 {
960 css::uno::Reference< css::container::XNameAccess > xModules;
961 xCfg->getByName(CFG_ENTRY_MODULES) >>= xModules;
962 xModules->getByName(m_sModuleCFG) >>= xAccess;
963 }
964
965 const OUString sIsoLang = impl_ts_getLocale();
966 static constexpr OUStringLiteral sDefaultLocale(u"en-US");
967
968 css::uno::Reference< css::container::XNameAccess > xKey;
969 css::uno::Reference< css::container::XNameAccess > xCommand;
970 if (xAccess.is())
971 {
972 css::uno::Sequence< OUString > lKeys = xAccess->getElementNames();
973 sal_Int32 nKeys = lKeys.getLength();
974 for ( sal_Int32 i=0; i<nKeys; ++i )
975 {
976 OUString sKey = lKeys[i];
977 xAccess->getByName(sKey) >>= xKey;
978 xKey->getByName(CFG_PROP_COMMAND) >>= xCommand;
979
980 const css::uno::Sequence< OUString > lLocales = xCommand->getElementNames();
981 ::std::vector< OUString > aLocales { lLocales.begin(), lLocales.end() };
982
983 OUString sLocale;
984 for (auto const& locale : aLocales)
985 {
986 if ( locale == sIsoLang )
987 {
988 sLocale = locale;
989 break;
990 }
991 }
992
993 if (sLocale.isEmpty())
994 {
995 for (auto const& locale : aLocales)
996 {
997 if ( locale == sDefaultLocale )
998 {
999 sLocale = locale;
1000 break;
1001 }
1002 }
1003
1004 if (sLocale.isEmpty())
1005 continue;
1006 }
1007
1008 OUString sCommand;
1009 xCommand->getByName(sLocale) >>= sCommand;
1010 if (sCommand.isEmpty())
1011 continue;
1012
1013 css::awt::KeyEvent aKeyEvent;
1014
1015 sal_Int32 nIndex = 0;
1016 std::u16string_view sKeyCommand = o3tl::getToken(sKey, 0, '_', nIndex);
1017 aKeyEvent.KeyCode = KeyMapping::get().mapIdentifierToCode(OUString::Concat("KEY_") + sKeyCommand);
1018
1019 const sal_Int32 nToken = 4;
1020 bool bValid = true;
1021 sal_Int32 k;
1022 for (k = 0; k < nToken; ++k)
1023 {
1024 if (nIndex < 0)
1025 break;
1026
1027 std::u16string_view sToken = o3tl::getToken(sKey, 0, '_', nIndex);
1028 if (sToken.empty())
1029 {
1030 bValid = false;
1031 break;
1032 }
1033
1034 if ( sToken == u"SHIFT" )
1035 aKeyEvent.Modifiers |= css::awt::KeyModifier::SHIFT;
1036 else if ( sToken == u"MOD1" )
1037 aKeyEvent.Modifiers |= css::awt::KeyModifier::MOD1;
1038 else if ( sToken == u"MOD2" )
1039 aKeyEvent.Modifiers |= css::awt::KeyModifier::MOD2;
1040 else if ( sToken == u"MOD3" )
1041 aKeyEvent.Modifiers |= css::awt::KeyModifier::MOD3;
1042 else
1043 {
1044 bValid = false;
1045 break;
1046 }
1047 }
1048
1049 if ( !aReadCache.hasKey(aKeyEvent) && bValid && k<nToken)
1050 aReadCache.setKeyCommandPair(aKeyEvent, sCommand);
1051 }
1052 }
1053
1054 if (bPreferred)
1055 m_aPrimaryReadCache = std::move(aReadCache);
1056 else
1057 m_aSecondaryReadCache = std::move(aReadCache);
1058 }
1059
impl_ts_save(bool bPreferred)1060 void XCUBasedAcceleratorConfiguration::impl_ts_save(bool bPreferred)
1061 {
1062 if (bPreferred)
1063 {
1064 AcceleratorCache::TKeyList lPrimaryReadKeys = m_aPrimaryReadCache.getAllKeys();
1065 AcceleratorCache::TKeyList lPrimaryWriteKeys = m_pPrimaryWriteCache->getAllKeys();
1066
1067 for (auto const& primaryReadKey : lPrimaryReadKeys)
1068 {
1069 if (!m_pPrimaryWriteCache->hasKey(primaryReadKey))
1070 removeKeyFromConfiguration(primaryReadKey, true);
1071 }
1072
1073 for (auto const& primaryWriteKey : lPrimaryWriteKeys)
1074 {
1075 OUString sCommand = m_pPrimaryWriteCache->getCommandByKey(primaryWriteKey);
1076 if (!m_aPrimaryReadCache.hasKey(primaryWriteKey))
1077 {
1078 insertKeyToConfiguration(primaryWriteKey, sCommand, true);
1079 }
1080 else
1081 {
1082 OUString sReadCommand = m_aPrimaryReadCache.getCommandByKey(primaryWriteKey);
1083 if (sReadCommand != sCommand)
1084 insertKeyToConfiguration(primaryWriteKey, sCommand, true);
1085 }
1086 }
1087
1088 // take over all changes into the original container
1089 SolarMutexGuard g;
1090 // coverity[check_after_deref] - confusing but correct
1091 if (m_pPrimaryWriteCache)
1092 {
1093 m_aPrimaryReadCache = *m_pPrimaryWriteCache;
1094 m_pPrimaryWriteCache.reset();
1095 }
1096 }
1097
1098 else
1099 {
1100 AcceleratorCache::TKeyList lSecondaryReadKeys = m_aSecondaryReadCache.getAllKeys();
1101 AcceleratorCache::TKeyList lSecondaryWriteKeys = m_pSecondaryWriteCache->getAllKeys();
1102
1103 for (auto const& secondaryReadKey : lSecondaryReadKeys)
1104 {
1105 if (!m_pSecondaryWriteCache->hasKey(secondaryReadKey))
1106 removeKeyFromConfiguration(secondaryReadKey, false);
1107 }
1108
1109 for (auto const& secondaryWriteKey : lSecondaryWriteKeys)
1110 {
1111 OUString sCommand = m_pSecondaryWriteCache->getCommandByKey(secondaryWriteKey);
1112 if (!m_aSecondaryReadCache.hasKey(secondaryWriteKey))
1113 {
1114 insertKeyToConfiguration(secondaryWriteKey, sCommand, false);
1115 }
1116 else
1117 {
1118 OUString sReadCommand = m_aSecondaryReadCache.getCommandByKey(secondaryWriteKey);
1119 if (sReadCommand != sCommand)
1120 insertKeyToConfiguration(secondaryWriteKey, sCommand, false);
1121 }
1122 }
1123
1124 // take over all changes into the original container
1125 SolarMutexGuard g;
1126 // coverity[check_after_deref] - confusing but correct
1127 if (m_pSecondaryWriteCache)
1128 {
1129 m_aSecondaryReadCache = *m_pSecondaryWriteCache;
1130 m_pSecondaryWriteCache.reset();
1131 }
1132 }
1133
1134 ::comphelper::ConfigurationHelper::flush(m_xCfg);
1135 }
1136
insertKeyToConfiguration(const css::awt::KeyEvent & aKeyEvent,const OUString & sCommand,const bool bPreferred)1137 void XCUBasedAcceleratorConfiguration::insertKeyToConfiguration( const css::awt::KeyEvent& aKeyEvent, const OUString& sCommand, const bool bPreferred )
1138 {
1139 css::uno::Reference< css::container::XNameAccess > xAccess;
1140 css::uno::Reference< css::container::XNameContainer > xContainer;
1141 css::uno::Reference< css::lang::XSingleServiceFactory > xFac;
1142 css::uno::Reference< css::uno::XInterface > xInst;
1143
1144 if ( bPreferred )
1145 m_xCfg->getByName(CFG_ENTRY_PRIMARY) >>= xAccess;
1146 else
1147 m_xCfg->getByName(CFG_ENTRY_SECONDARY) >>= xAccess;
1148
1149 if ( m_sGlobalOrModules == CFG_ENTRY_GLOBAL )
1150 xAccess->getByName(CFG_ENTRY_GLOBAL) >>= xContainer;
1151 else if ( m_sGlobalOrModules == CFG_ENTRY_MODULES )
1152 {
1153 css::uno::Reference< css::container::XNameContainer > xModules;
1154 xAccess->getByName(CFG_ENTRY_MODULES) >>= xModules;
1155 if ( !xModules->hasByName(m_sModuleCFG) )
1156 {
1157 xFac.set(xModules, css::uno::UNO_QUERY);
1158 xInst = xFac->createInstance();
1159 xModules->insertByName(m_sModuleCFG, css::uno::Any(xInst));
1160 }
1161 xModules->getByName(m_sModuleCFG) >>= xContainer;
1162 }
1163
1164 const OUString sKey = lcl_getKeyString(aKeyEvent);
1165 css::uno::Reference< css::container::XNameAccess > xKey;
1166 css::uno::Reference< css::container::XNameContainer > xCommand;
1167 if ( !xContainer->hasByName(sKey) )
1168 {
1169 xFac.set(xContainer, css::uno::UNO_QUERY);
1170 xInst = xFac->createInstance();
1171 xContainer->insertByName(sKey, css::uno::Any(xInst));
1172 }
1173 xContainer->getByName(sKey) >>= xKey;
1174
1175 xKey->getByName(CFG_PROP_COMMAND) >>= xCommand;
1176 OUString sLocale = impl_ts_getLocale();
1177 if ( !xCommand->hasByName(sLocale) )
1178 xCommand->insertByName(sLocale, css::uno::Any(sCommand));
1179 else
1180 xCommand->replaceByName(sLocale, css::uno::Any(sCommand));
1181 }
1182
removeKeyFromConfiguration(const css::awt::KeyEvent & aKeyEvent,const bool bPreferred)1183 void XCUBasedAcceleratorConfiguration::removeKeyFromConfiguration( const css::awt::KeyEvent& aKeyEvent, const bool bPreferred )
1184 {
1185 css::uno::Reference< css::container::XNameAccess > xAccess;
1186 css::uno::Reference< css::container::XNameContainer > xContainer;
1187
1188 if ( bPreferred )
1189 m_xCfg->getByName(CFG_ENTRY_PRIMARY) >>= xAccess;
1190 else
1191 m_xCfg->getByName(CFG_ENTRY_SECONDARY) >>= xAccess;
1192
1193 if ( m_sGlobalOrModules == CFG_ENTRY_GLOBAL )
1194 xAccess->getByName(CFG_ENTRY_GLOBAL) >>= xContainer;
1195 else if ( m_sGlobalOrModules == CFG_ENTRY_MODULES )
1196 {
1197 css::uno::Reference< css::container::XNameAccess > xModules;
1198 xAccess->getByName(CFG_ENTRY_MODULES) >>= xModules;
1199 if ( !xModules->hasByName(m_sModuleCFG) )
1200 return;
1201 xModules->getByName(m_sModuleCFG) >>= xContainer;
1202 }
1203
1204 const OUString sKey = lcl_getKeyString(aKeyEvent);
1205 xContainer->removeByName(sKey);
1206 }
1207
reloadChanged(const OUString & sPrimarySecondary,std::u16string_view sGlobalModules,const OUString & sModule,const OUString & sKey)1208 void XCUBasedAcceleratorConfiguration::reloadChanged( const OUString& sPrimarySecondary, std::u16string_view sGlobalModules, const OUString& sModule, const OUString& sKey )
1209 {
1210 css::uno::Reference< css::container::XNameAccess > xAccess;
1211 css::uno::Reference< css::container::XNameContainer > xContainer;
1212
1213 m_xCfg->getByName(sPrimarySecondary) >>= xAccess;
1214 if ( sGlobalModules == CFG_ENTRY_GLOBAL )
1215 xAccess->getByName(CFG_ENTRY_GLOBAL) >>= xContainer;
1216 else
1217 {
1218 css::uno::Reference< css::container::XNameAccess > xModules;
1219 xAccess->getByName(CFG_ENTRY_MODULES) >>= xModules;
1220 if ( !xModules->hasByName(sModule) )
1221 return;
1222 xModules->getByName(sModule) >>= xContainer;
1223 }
1224
1225 css::awt::KeyEvent aKeyEvent;
1226
1227 sal_Int32 nIndex = 0;
1228 std::u16string_view sKeyIdentifier = o3tl::getToken(sKey, 0, '_', nIndex);
1229 aKeyEvent.KeyCode = KeyMapping::get().mapIdentifierToCode(OUString::Concat("KEY_") + sKeyIdentifier);
1230
1231 const int nToken = 4;
1232 for (sal_Int32 i = 0; i < nToken; ++i)
1233 {
1234 if ( nIndex < 0 )
1235 break;
1236
1237 std::u16string_view sToken = o3tl::getToken(sKey, 0, '_', nIndex);
1238 if ( sToken == u"SHIFT" )
1239 aKeyEvent.Modifiers |= css::awt::KeyModifier::SHIFT;
1240 else if ( sToken == u"MOD1" )
1241 aKeyEvent.Modifiers |= css::awt::KeyModifier::MOD1;
1242 else if ( sToken == u"MOD2" )
1243 aKeyEvent.Modifiers |= css::awt::KeyModifier::MOD2;
1244 else if ( sToken == u"MOD3" )
1245 aKeyEvent.Modifiers |= css::awt::KeyModifier::MOD3;
1246 }
1247
1248 css::uno::Reference< css::container::XNameAccess > xKey;
1249 css::uno::Reference< css::container::XNameAccess > xCommand;
1250 OUString sCommand;
1251
1252 if (xContainer->hasByName(sKey))
1253 {
1254 OUString sLocale = impl_ts_getLocale();
1255 xContainer->getByName(sKey) >>= xKey;
1256 xKey->getByName(CFG_PROP_COMMAND) >>= xCommand;
1257 xCommand->getByName(sLocale) >>= sCommand;
1258 }
1259
1260 if ( sPrimarySecondary == CFG_ENTRY_PRIMARY )
1261 {
1262 if (sCommand.isEmpty())
1263 m_aPrimaryReadCache.removeKey(aKeyEvent);
1264 else
1265 m_aPrimaryReadCache.setKeyCommandPair(aKeyEvent, sCommand);
1266 }
1267 else if ( sPrimarySecondary == CFG_ENTRY_SECONDARY )
1268 {
1269 if (sCommand.isEmpty())
1270 m_aSecondaryReadCache.removeKey(aKeyEvent);
1271 else
1272 m_aSecondaryReadCache.setKeyCommandPair(aKeyEvent, sCommand);
1273 }
1274 }
1275
impl_getCFG(bool bPreferred,bool bWriteAccessRequested)1276 AcceleratorCache& XCUBasedAcceleratorConfiguration::impl_getCFG(bool bPreferred, bool bWriteAccessRequested)
1277 {
1278 SolarMutexGuard g;
1279
1280 if (bPreferred)
1281 {
1282 //create copy of our readonly-cache, if write access is forced ... but
1283 //not still possible!
1284 if ( bWriteAccessRequested && !m_pPrimaryWriteCache )
1285 {
1286 m_pPrimaryWriteCache.reset(new AcceleratorCache(m_aPrimaryReadCache));
1287 }
1288
1289 // in case, we have a writeable cache, we use it for reading too!
1290 // Otherwise the API user can't find its own changes...
1291 if (m_pPrimaryWriteCache)
1292 return *m_pPrimaryWriteCache;
1293 else
1294 return m_aPrimaryReadCache;
1295 }
1296
1297 else
1298 {
1299 //create copy of our readonly-cache, if write access is forced ... but
1300 //not still possible!
1301 if ( bWriteAccessRequested && !m_pSecondaryWriteCache )
1302 {
1303 m_pSecondaryWriteCache.reset(new AcceleratorCache(m_aSecondaryReadCache));
1304 }
1305
1306 // in case, we have a writeable cache, we use it for reading too!
1307 // Otherwise the API user can't find its own changes...
1308 if (m_pSecondaryWriteCache)
1309 return *m_pSecondaryWriteCache;
1310 else
1311 return m_aSecondaryReadCache;
1312 }
1313 }
1314
1315 // static
impl_ts_getLocale()1316 OUString XCUBasedAcceleratorConfiguration::impl_ts_getLocale()
1317 {
1318 OUString sISOLocale = officecfg::Setup::L10N::ooLocale::get();
1319
1320 if (sISOLocale.isEmpty())
1321 return u"en-US"_ustr;
1322 return sISOLocale;
1323 }
1324
1325 } // namespace framework
1326
1327 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1328