Version 7.6.3.2-android, tag libreoffice-7.6.3.2-android
[LibreOffice.git] / framework / source / accelerators / presethandler.cxx
blob0572049ab785c98a4eb8bc799163ef98e3468b54
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <accelerators/presethandler.hxx>
22 #include <classes/fwkresid.hxx>
24 #include <strings.hrc>
26 #include <com/sun/star/configuration/CorruptedUIConfigurationException.hpp>
27 #include <com/sun/star/container/XNameAccess.hpp>
28 #include <com/sun/star/embed/ElementModes.hpp>
29 #include <com/sun/star/embed/FileSystemStorageFactory.hpp>
30 #include <com/sun/star/lang/XSingleServiceFactory.hpp>
31 #include <com/sun/star/util/thePathSettings.hpp>
33 #include <utility>
34 #include <vcl/svapp.hxx>
35 #include <cppuhelper/exc_hlp.hxx>
36 #include <rtl/ustrbuf.hxx>
37 #include <osl/diagnose.h>
38 #include <i18nlangtag/languagetag.hxx>
40 const ::sal_Int32 ID_CORRUPT_UICONFIG_SHARE = 1;
41 const ::sal_Int32 ID_CORRUPT_UICONFIG_USER = 2;
42 const ::sal_Int32 ID_CORRUPT_UICONFIG_GENERAL = 3;
44 namespace framework
47 namespace {
49 /** @short because a concurrent access to the same storage from different implementations
50 isn't supported, we have to share it with others.
52 @descr This struct is allegedly shared and must be used within a
53 synchronized section. But it isn't.
55 struct TSharedStorages final
57 StorageHolder m_lStoragesShare;
58 StorageHolder m_lStoragesUser;
60 TSharedStorages()
61 {};
64 /** @short provides access to the:
65 a) shared root storages
66 b) shared "inbetween" storages
67 of the share and user layer. */
68 TSharedStorages& SharedStorages()
70 static TSharedStorages theStorages;
71 return theStorages;
76 PresetHandler::PresetHandler(css::uno::Reference< css::uno::XComponentContext > xContext)
77 : m_xContext(std::move(xContext))
78 , m_eConfigType(E_GLOBAL)
82 PresetHandler::PresetHandler(const PresetHandler& rCopy)
84 m_xContext = rCopy.m_xContext;
85 m_eConfigType = rCopy.m_eConfigType;
86 m_xWorkingStorageShare = rCopy.m_xWorkingStorageShare;
87 m_xWorkingStorageNoLang = rCopy.m_xWorkingStorageNoLang;
88 m_xWorkingStorageUser = rCopy.m_xWorkingStorageUser;
89 m_lDocumentStorages = rCopy.m_lDocumentStorages;
90 m_sRelPathShare = rCopy.m_sRelPathShare;
91 m_sRelPathUser = rCopy.m_sRelPathUser;
94 PresetHandler::~PresetHandler()
96 m_xWorkingStorageShare.clear();
97 m_xWorkingStorageNoLang.clear();
98 m_xWorkingStorageUser.clear();
100 /* #i46497#
101 Don't call forgetCachedStorages() here for shared storages.
102 Because we opened different sub storages by using openPath().
103 And every already open path was reused and referenced (means it's
104 ref count was increased!)
105 So now we have to release our ref counts to these shared storages
106 only ... and not to free all used storages.
107 Otherwise we will disconnect all other open configuration access
108 objects which base on these storages.
110 auto & sharedStorages = SharedStorages();
111 sharedStorages.m_lStoragesShare.closePath(m_sRelPathShare);
112 sharedStorages.m_lStoragesUser.closePath (m_sRelPathUser );
114 /* On the other side closePath() is not needed for our special handled
115 document storage. Because it's not shared with others ... so we can
116 free it.
118 m_lDocumentStorages.forgetCachedStorages();
121 void PresetHandler::forgetCachedStorages()
123 SolarMutexGuard g;
125 if (m_eConfigType == E_DOCUMENT)
127 m_xWorkingStorageShare.clear();
128 m_xWorkingStorageNoLang.clear();
129 m_xWorkingStorageUser.clear();
132 m_lDocumentStorages.forgetCachedStorages();
135 namespace {
137 OUString lcl_getLocalizedMessage(::sal_Int32 nID)
139 OUString sMessage("Unknown error.");
141 switch(nID)
143 case ID_CORRUPT_UICONFIG_SHARE :
144 sMessage = FwkResId(STR_CORRUPT_UICFG_SHARE);
146 break;
148 case ID_CORRUPT_UICONFIG_USER :
149 sMessage = FwkResId(STR_CORRUPT_UICFG_USER);
150 break;
152 case ID_CORRUPT_UICONFIG_GENERAL :
153 sMessage = FwkResId(STR_CORRUPT_UICFG_GENERAL);
154 break;
157 return sMessage;
160 void lcl_throwCorruptedUIConfigurationException(
161 css::uno::Any const & exception, sal_Int32 id)
163 css::uno::Exception e;
164 bool ok = (exception >>= e);
165 OSL_ASSERT(ok);
166 throw css::configuration::CorruptedUIConfigurationException(
167 lcl_getLocalizedMessage(id),
168 css::uno::Reference< css::uno::XInterface >(),
169 exception.getValueTypeName() + ": \"" + e.Message + "\"");
174 css::uno::Reference< css::embed::XStorage > PresetHandler::getOrCreateRootStorageShare()
176 auto & sharedStorages = SharedStorages();
177 css::uno::Reference< css::embed::XStorage > xRoot = sharedStorages.m_lStoragesShare.getRootStorage();
178 if (xRoot.is())
179 return xRoot;
181 css::uno::Reference< css::uno::XComponentContext > xContext;
183 SolarMutexGuard g;
184 xContext = m_xContext;
187 css::uno::Reference< css::util::XPathSettings > xPathSettings =
188 css::util::thePathSettings::get( xContext );
190 OUString sShareLayer = xPathSettings->getBasePathShareLayer();
192 // "UIConfig" is a "multi path" ... use first part only here!
193 sal_Int32 nPos = sShareLayer.indexOf(';');
194 if (nPos > 0)
195 sShareLayer = sShareLayer.copy(0, nPos);
197 // Note: May be an user uses URLs without a final slash! Check it ...
198 nPos = sShareLayer.lastIndexOf('/');
199 if (nPos != sShareLayer.getLength()-1)
200 sShareLayer += "/";
202 sShareLayer += "soffice.cfg";
204 // TODO remove me!
205 // Attention: This is temp. workaround ... We create a temp. storage file
206 // based of a system directory. This must be used so, till the storage implementation
207 // can work on directories too.
209 css::uno::Sequence< css::uno::Any > lArgs{
210 css::uno::Any(sShareLayer),
211 css::uno::Any(css::embed::ElementModes::READ | css::embed::ElementModes::NOCREATE)
214 css::uno::Reference< css::lang::XSingleServiceFactory > xStorageFactory = css::embed::FileSystemStorageFactory::create( xContext );
215 css::uno::Reference< css::embed::XStorage > xStorage;
219 xStorage.set(xStorageFactory->createInstanceWithArguments(lArgs), css::uno::UNO_QUERY_THROW);
221 catch(const css::uno::Exception&)
223 css::uno::Any ex(cppu::getCaughtException());
224 lcl_throwCorruptedUIConfigurationException(
225 ex, ID_CORRUPT_UICONFIG_SHARE);
228 sharedStorages.m_lStoragesShare.setRootStorage(xStorage);
230 return xStorage;
233 css::uno::Reference< css::embed::XStorage > PresetHandler::getOrCreateRootStorageUser()
235 auto & sharedStorages = SharedStorages();
236 css::uno::Reference< css::embed::XStorage > xRoot = sharedStorages.m_lStoragesUser.getRootStorage();
237 if (xRoot.is())
238 return xRoot;
240 css::uno::Reference< css::uno::XComponentContext > xContext;
242 SolarMutexGuard g;
243 xContext = m_xContext;
246 css::uno::Reference< css::util::XPathSettings > xPathSettings =
247 css::util::thePathSettings::get( xContext );
249 OUString sUserLayer = xPathSettings->getBasePathUserLayer();
251 // Note: May be an user uses URLs without a final slash! Check it ...
252 sal_Int32 nPos = sUserLayer.lastIndexOf('/');
253 if (nPos != sUserLayer.getLength()-1)
254 sUserLayer += "/";
256 sUserLayer += "soffice.cfg"; // storage file
258 css::uno::Sequence< css::uno::Any > lArgs{ css::uno::Any(sUserLayer),
259 css::uno::Any(css::embed::ElementModes::READWRITE) };
261 css::uno::Reference< css::lang::XSingleServiceFactory > xStorageFactory = css::embed::FileSystemStorageFactory::create( xContext );
262 css::uno::Reference< css::embed::XStorage > xStorage;
266 xStorage.set(xStorageFactory->createInstanceWithArguments(lArgs), css::uno::UNO_QUERY_THROW);
268 catch(const css::uno::Exception&)
270 css::uno::Any ex(cppu::getCaughtException());
271 lcl_throwCorruptedUIConfigurationException(
272 ex, ID_CORRUPT_UICONFIG_USER);
275 sharedStorages.m_lStoragesUser.setRootStorage(xStorage);
277 return xStorage;
280 css::uno::Reference< css::embed::XStorage > PresetHandler::getWorkingStorageUser() const
282 SolarMutexGuard g;
283 return m_xWorkingStorageUser;
286 css::uno::Reference< css::embed::XStorage > PresetHandler::getParentStorageShare()
288 css::uno::Reference< css::embed::XStorage > xWorking;
290 SolarMutexGuard g;
291 xWorking = m_xWorkingStorageShare;
294 return SharedStorages().m_lStoragesShare.getParentStorage(xWorking);
297 css::uno::Reference< css::embed::XStorage > PresetHandler::getParentStorageUser()
299 css::uno::Reference< css::embed::XStorage > xWorking;
301 SolarMutexGuard g;
302 xWorking = m_xWorkingStorageUser;
305 return SharedStorages().m_lStoragesUser.getParentStorage(xWorking);
308 void PresetHandler::connectToResource( PresetHandler::EConfigType eConfigType ,
309 std::u16string_view sResource ,
310 std::u16string_view sModule ,
311 const css::uno::Reference< css::embed::XStorage >& xDocumentRoot,
312 const LanguageTag& rLanguageTag )
314 // TODO free all current open storages!
317 SolarMutexGuard g;
318 m_eConfigType = eConfigType;
321 css::uno::Reference< css::embed::XStorage > xShare;
322 css::uno::Reference< css::embed::XStorage > xNoLang;
323 css::uno::Reference< css::embed::XStorage > xUser;
325 // special case for documents
326 // use outside root storage, if we run in E_DOCUMENT mode!
327 if (eConfigType == E_DOCUMENT)
329 if (!xDocumentRoot.is())
330 throw css::uno::RuntimeException(
331 "There is valid root storage, where the UI configuration can work on.");
332 m_lDocumentStorages.setRootStorage(xDocumentRoot);
333 xShare = xDocumentRoot;
334 xUser = xDocumentRoot;
336 else
338 xShare = getOrCreateRootStorageShare();
339 xUser = getOrCreateRootStorageUser();
342 // #...#
346 // a) inside share layer we should not create any new structures... We have to use
347 // existing ones only!
348 // b) inside user layer we can (SOFT mode!) but sometimes we should not (HARD mode!)
349 // create new empty structures. We should prefer using of any existing structure.
350 sal_Int32 eShareMode = (css::embed::ElementModes::READ | css::embed::ElementModes::NOCREATE);
351 sal_Int32 eUserMode = css::embed::ElementModes::READWRITE;
353 OUStringBuffer sRelPathBuf(1024);
354 OUString sRelPathShare;
355 OUString sRelPathUser;
356 switch(eConfigType)
358 case E_GLOBAL :
360 sRelPathShare = OUString::Concat("global/") + sResource;
361 sRelPathUser = sRelPathShare;
363 xShare = impl_openPathIgnoringErrors(sRelPathShare, eShareMode, true );
364 xUser = impl_openPathIgnoringErrors(sRelPathUser , eUserMode , false);
366 break;
368 case E_MODULES :
370 sRelPathShare = OUString::Concat("modules/") + sModule + "/" + sResource;
371 sRelPathUser = sRelPathShare;
373 xShare = impl_openPathIgnoringErrors(sRelPathShare, eShareMode, true );
374 xUser = impl_openPathIgnoringErrors(sRelPathUser , eUserMode , false);
376 break;
378 case E_DOCUMENT :
380 // A document does not have a share layer in real.
381 // It has one layer only, and this one should be opened READ_WRITE.
382 // So we open the user layer here only and set the share layer equals to it .-)
384 sRelPathBuf.append(sResource);
385 sRelPathUser = sRelPathBuf.makeStringAndClear();
386 sRelPathShare = sRelPathUser;
390 xUser = m_lDocumentStorages.openPath(sRelPathUser , eUserMode );
391 xShare = xUser;
393 catch(const css::uno::RuntimeException&)
394 { throw; }
395 catch(const css::uno::Exception&)
396 { xShare.clear(); xUser.clear(); }
398 break;
401 // Non-localized global share
402 xNoLang = xShare;
404 if (
405 (rLanguageTag != LanguageTag(LANGUAGE_USER_PRIV_NOTRANSLATE)) && // localized level?
406 (eConfigType != E_DOCUMENT ) // no localization in document mode!
409 // First try to find the right localized set inside share layer.
410 // Fallbacks are allowed there.
411 OUString aShareLocale( rLanguageTag.getBcp47());
412 OUString sLocalizedSharePath(sRelPathShare);
413 bool bAllowFallbacks = true;
414 xShare = impl_openLocalizedPathIgnoringErrors(sLocalizedSharePath, eShareMode, true , aShareLocale, bAllowFallbacks);
416 // The try to locate the right sub dir inside user layer ... without using fallbacks!
417 // Normally the corresponding sub dir should be created matching the specified locale.
418 // Because we allow creation of storages inside user layer by default.
419 OUString aUserLocale( rLanguageTag.getBcp47());
420 OUString sLocalizedUserPath(sRelPathUser);
421 bAllowFallbacks = false;
422 xUser = impl_openLocalizedPathIgnoringErrors(sLocalizedUserPath, eUserMode, false, aUserLocale, bAllowFallbacks);
424 sRelPathShare = sLocalizedSharePath;
425 sRelPathUser = sLocalizedUserPath;
429 SolarMutexGuard g;
430 m_xWorkingStorageShare = xShare;
431 m_xWorkingStorageNoLang= xNoLang;
432 m_xWorkingStorageUser = xUser;
433 m_sRelPathShare = sRelPathShare;
434 m_sRelPathUser = sRelPathUser;
438 catch(const css::uno::Exception&)
440 css::uno::Any ex(cppu::getCaughtException());
441 lcl_throwCorruptedUIConfigurationException(
442 ex, ID_CORRUPT_UICONFIG_GENERAL);
446 void PresetHandler::copyPresetToTarget(std::u16string_view sPreset,
447 std::u16string_view sTarget)
449 // don't check our preset list, if element exists
450 // We try to open it and forward all errors to the user!
452 css::uno::Reference< css::embed::XStorage > xWorkingShare;
453 css::uno::Reference< css::embed::XStorage > xWorkingNoLang;
454 css::uno::Reference< css::embed::XStorage > xWorkingUser;
456 SolarMutexGuard g;
457 xWorkingShare = m_xWorkingStorageShare;
458 xWorkingNoLang= m_xWorkingStorageNoLang;
459 xWorkingUser = m_xWorkingStorageUser;
462 // e.g. module without any config data ?!
463 if (
464 (!xWorkingShare.is()) ||
465 (!xWorkingUser.is() )
468 return;
471 OUString sPresetFile = OUString::Concat(sPreset) + ".xml";
472 OUString sTargetFile = OUString::Concat(sTarget) + ".xml";
474 // remove existing elements before you try to copy the preset to that location ...
475 // Otherwise w will get an ElementExistException inside copyElementTo()!
476 css::uno::Reference< css::container::XNameAccess > xCheckingUser(xWorkingUser, css::uno::UNO_QUERY_THROW);
477 if (xCheckingUser->hasByName(sTargetFile))
478 xWorkingUser->removeElement(sTargetFile);
480 xWorkingShare->copyElementTo(sPresetFile, xWorkingUser, sTargetFile);
482 // If our storages work in transacted mode, we have
483 // to commit all changes from bottom to top!
484 commitUserChanges();
487 css::uno::Reference< css::io::XStream > PresetHandler::openPreset(std::u16string_view sPreset)
489 css::uno::Reference< css::embed::XStorage > xFolder;
491 SolarMutexGuard g;
492 xFolder = m_xWorkingStorageNoLang;
495 // e.g. module without any config data ?!
496 if (!xFolder.is())
497 return css::uno::Reference< css::io::XStream >();
499 OUString sFile = OUString::Concat(sPreset) + ".xml";
501 // inform user about errors (use original exceptions!)
502 css::uno::Reference< css::io::XStream > xStream = xFolder->openStreamElement(sFile, css::embed::ElementModes::READ);
503 return xStream;
506 css::uno::Reference< css::io::XStream > PresetHandler::openTarget(
507 std::u16string_view sTarget, sal_Int32 const nMode)
509 css::uno::Reference< css::embed::XStorage > xFolder;
511 SolarMutexGuard g;
512 xFolder = m_xWorkingStorageUser;
515 // e.g. module without any config data ?!
516 if (!xFolder.is())
517 return css::uno::Reference< css::io::XStream >();
519 OUString const sFile(OUString::Concat(sTarget) + ".xml");
521 return xFolder->openStreamElement(sFile, nMode);
524 void PresetHandler::commitUserChanges()
526 css::uno::Reference< css::embed::XStorage > xWorking;
527 EConfigType eCfgType;
529 SolarMutexGuard g;
530 xWorking = m_xWorkingStorageUser;
531 eCfgType = m_eConfigType;
534 // e.g. module without any config data ?!
535 if (!xWorking.is())
536 return;
538 OUString sPath;
540 switch(eCfgType)
542 case E_GLOBAL :
543 case E_MODULES :
545 auto & sharedStorages = SharedStorages();
546 sPath = sharedStorages.m_lStoragesUser.getPathOfStorage(xWorking);
547 sharedStorages.m_lStoragesUser.commitPath(sPath);
548 sharedStorages.m_lStoragesUser.notifyPath(sPath);
550 break;
552 case E_DOCUMENT :
554 sPath = m_lDocumentStorages.getPathOfStorage(xWorking);
555 m_lDocumentStorages.commitPath(sPath);
556 m_lDocumentStorages.notifyPath(sPath);
558 break;
562 void PresetHandler::addStorageListener(XMLBasedAcceleratorConfiguration* pListener)
564 OUString sRelPath;
565 EConfigType eCfgType;
567 SolarMutexGuard g;
568 sRelPath = m_sRelPathUser; // use user path ... because we don't work directly on the share layer!
569 eCfgType = m_eConfigType;
572 if (sRelPath.isEmpty())
573 return;
575 switch(eCfgType)
577 case E_GLOBAL :
578 case E_MODULES :
580 SharedStorages().m_lStoragesUser.addStorageListener(pListener, sRelPath);
582 break;
584 case E_DOCUMENT :
586 m_lDocumentStorages.addStorageListener(pListener, sRelPath);
588 break;
592 void PresetHandler::removeStorageListener(XMLBasedAcceleratorConfiguration* pListener)
594 OUString sRelPath;
595 EConfigType eCfgType;
597 SolarMutexGuard g;
598 sRelPath = m_sRelPathUser; // use user path ... because we don't work directly on the share layer!
599 eCfgType = m_eConfigType;
602 if (sRelPath.isEmpty())
603 return;
605 switch(eCfgType)
607 case E_GLOBAL :
608 case E_MODULES :
610 SharedStorages().m_lStoragesUser.removeStorageListener(pListener, sRelPath);
612 break;
614 case E_DOCUMENT :
616 m_lDocumentStorages.removeStorageListener(pListener, sRelPath);
618 break;
622 css::uno::Reference< css::embed::XStorage > PresetHandler::impl_openPathIgnoringErrors(const OUString& sPath ,
623 sal_Int32 eMode ,
624 bool bShare)
626 css::uno::Reference< css::embed::XStorage > xPath;
629 if (bShare)
630 xPath = SharedStorages().m_lStoragesShare.openPath(sPath, eMode);
631 else
632 xPath = SharedStorages().m_lStoragesUser.openPath(sPath, eMode);
634 catch(const css::uno::RuntimeException&)
635 { throw; }
636 catch(const css::uno::Exception&)
637 { xPath.clear(); }
638 return xPath;
641 ::std::vector< OUString >::const_iterator PresetHandler::impl_findMatchingLocalizedValue(
642 const ::std::vector< OUString >& lLocalizedValues,
643 OUString& rLanguageTag,
644 bool bAllowFallbacks )
646 ::std::vector< OUString >::const_iterator pFound = lLocalizedValues.end();
647 if (bAllowFallbacks)
649 pFound = LanguageTag::getFallback(lLocalizedValues, rLanguageTag);
650 // if we found a valid locale ... take it over to our in/out parameter
651 // rLanguageTag
652 if (pFound != lLocalizedValues.end())
654 rLanguageTag = *pFound;
657 else
659 pFound = std::find(lLocalizedValues.begin(), lLocalizedValues.end(), rLanguageTag);
662 return pFound;
665 css::uno::Reference< css::embed::XStorage > PresetHandler::impl_openLocalizedPathIgnoringErrors(
666 OUString& sPath ,
667 sal_Int32 eMode ,
668 bool bShare ,
669 OUString& rLanguageTag ,
670 bool bAllowFallback)
672 css::uno::Reference< css::embed::XStorage > xPath = impl_openPathIgnoringErrors(sPath, eMode, bShare);
673 ::std::vector< OUString > lSubFolders = impl_getSubFolderNames(xPath);
674 ::std::vector< OUString >::const_iterator pLocaleFolder = impl_findMatchingLocalizedValue(lSubFolders, rLanguageTag, bAllowFallback);
676 // no fallback ... creation not allowed => no storage
677 if (
678 (pLocaleFolder == lSubFolders.end() ) &&
679 ((eMode & css::embed::ElementModes::NOCREATE) == css::embed::ElementModes::NOCREATE)
681 return css::uno::Reference< css::embed::XStorage >();
683 // it doesn't matter, if there is a locale fallback or not
684 // If creation of storages is allowed, we do it anyway.
685 // Otherwise we have no acc config at all, which can make other trouble.
686 OUString sLocalizedPath = sPath + "/";
687 if (pLocaleFolder != lSubFolders.end())
688 sLocalizedPath += *pLocaleFolder;
689 else
690 sLocalizedPath += rLanguageTag;
692 css::uno::Reference< css::embed::XStorage > xLocalePath = impl_openPathIgnoringErrors(sLocalizedPath, eMode, bShare);
694 if (xLocalePath.is())
695 sPath = sLocalizedPath;
696 else
697 sPath.clear();
699 return xLocalePath;
702 ::std::vector< OUString > PresetHandler::impl_getSubFolderNames(const css::uno::Reference< css::embed::XStorage >& xFolder)
704 if (!xFolder.is())
705 return ::std::vector< OUString >();
707 ::std::vector< OUString > lSubFolders;
708 const css::uno::Sequence< OUString > lNames = xFolder->getElementNames();
709 const OUString* pNames = lNames.getConstArray();
710 sal_Int32 c = lNames.getLength();
711 sal_Int32 i = 0;
713 for (i=0; i<c; ++i)
717 if (xFolder->isStorageElement(pNames[i]))
718 lSubFolders.push_back(pNames[i]);
720 catch(const css::uno::RuntimeException&)
721 { throw; }
722 catch(const css::uno::Exception&)
726 return lSubFolders;
729 } // namespace framework
731 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */