Version 7.6.3.2-android, tag libreoffice-7.6.3.2-android
[LibreOffice.git] / unotools / source / config / historyoptions.cxx
blob2cda950858d240cd9658be1df8e1abf24524f490
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 <unotools/historyoptions.hxx>
21 #include <com/sun/star/uno/Any.hxx>
22 #include <com/sun/star/uno/Sequence.hxx>
24 #include <com/sun/star/beans/XPropertySet.hpp>
25 #include <com/sun/star/container/XNameAccess.hpp>
26 #include <com/sun/star/container/XNameContainer.hpp>
27 #include <com/sun/star/lang/XSingleServiceFactory.hpp>
28 #include <comphelper/configurationhelper.hxx>
29 #include <comphelper/processfactory.hxx>
30 #include <comphelper/diagnose_ex.hxx>
31 #include <optional>
33 using namespace ::com::sun::star;
34 using namespace ::com::sun::star::uno;
35 using namespace ::com::sun::star::beans;
37 namespace {
38 constexpr OUStringLiteral s_sItemList = u"ItemList";
39 constexpr OUStringLiteral s_sOrderList = u"OrderList";
40 constexpr OUStringLiteral s_sHistoryItemRef = u"HistoryItemRef";
41 constexpr OUStringLiteral s_sFilter = u"Filter";
42 constexpr OUStringLiteral s_sTitle = u"Title";
43 constexpr OUStringLiteral s_sPassword = u"Password";
44 constexpr OUStringLiteral s_sThumbnail = u"Thumbnail";
45 constexpr OUStringLiteral s_sReadOnly = u"ReadOnly";
46 constexpr OUStringLiteral s_sPinned = u"Pinned";
49 static uno::Reference<container::XNameAccess> GetConfig();
50 static uno::Reference<container::XNameAccess> GetCommonXCU();
51 static uno::Reference<container::XNameAccess> GetListAccess(
52 uno::Reference<container::XNameAccess> const & xCfg,
53 EHistoryType eHistory);
54 static void TruncateList(
55 const uno::Reference<container::XNameAccess>& xCfg,
56 const uno::Reference<container::XNameAccess>& xList,
57 sal_uInt32 nSize);
59 static void PrependItem(const uno::Reference<container::XNameAccess>& xCfg,
60 uno::Reference<container::XNameContainer>& xList, std::u16string_view sURL);
61 static void MoveItemToUnpinned(const uno::Reference<container::XNameAccess>& xCfg,
62 uno::Reference<container::XNameContainer>& xOrderList,
63 uno::Reference<container::XNameContainer>& xItemList,
64 std::u16string_view sURL);
66 static sal_uInt32 GetCapacity(const uno::Reference<container::XNameAccess>& xCommonXCU, EHistoryType eHistory);
68 namespace SvtHistoryOptions
71 void Clear( EHistoryType eHistory )
73 try
75 uno::Reference<container::XNameAccess> xCfg = GetConfig();
76 uno::Reference<container::XNameAccess> xListAccess(GetListAccess(xCfg, eHistory));
78 // clear ItemList
79 uno::Reference<container::XNameContainer> xNode;
80 xListAccess->getByName(s_sItemList) >>= xNode;
81 Sequence<OUString> aStrings(xNode->getElementNames());
83 for (const auto& rString : std::as_const(aStrings))
84 xNode->removeByName(rString);
86 // clear OrderList
87 xListAccess->getByName(s_sOrderList) >>= xNode;
88 aStrings = xNode->getElementNames();
90 for (const auto& rString : std::as_const(aStrings))
91 xNode->removeByName(rString);
93 ::comphelper::ConfigurationHelper::flush(xCfg);
95 catch(const uno::Exception&)
97 DBG_UNHANDLED_EXCEPTION("unotools.config");
101 std::vector< HistoryItem > GetList( EHistoryType eHistory )
103 std::vector< HistoryItem > aRet;
106 uno::Reference<container::XNameAccess> xCfg = GetConfig();
107 uno::Reference<container::XNameAccess> xCommonXCU = GetCommonXCU();
108 uno::Reference<container::XNameAccess> xListAccess(GetListAccess(xCfg, eHistory));
110 TruncateList(xCfg, xListAccess, GetCapacity(xCommonXCU, eHistory));
112 uno::Reference<container::XNameAccess> xItemList;
113 uno::Reference<container::XNameAccess> xOrderList;
114 xListAccess->getByName(s_sItemList) >>= xItemList;
115 xListAccess->getByName(s_sOrderList) >>= xOrderList;
117 const sal_Int32 nLength = xOrderList->getElementNames().getLength();
118 aRet.reserve(nLength);
120 for (sal_Int32 nItem = 0; nItem < nLength; ++nItem)
124 OUString sUrl;
125 uno::Reference<beans::XPropertySet> xSet;
126 xOrderList->getByName(OUString::number(nItem)) >>= xSet;
127 xSet->getPropertyValue(s_sHistoryItemRef) >>= sUrl;
129 xItemList->getByName(sUrl) >>= xSet;
130 HistoryItem aItem;
131 aItem.sURL = sUrl;
132 xSet->getPropertyValue(s_sFilter) >>= aItem.sFilter;
133 xSet->getPropertyValue(s_sTitle) >>= aItem.sTitle;
134 xSet->getPropertyValue(s_sPassword) >>= aItem.sPassword;
135 xSet->getPropertyValue(s_sThumbnail) >>= aItem.sThumbnail;
136 xSet->getPropertyValue(s_sReadOnly) >>= aItem.isReadOnly;
137 if (xSet->getPropertySetInfo()->hasPropertyByName(s_sPinned))
138 xSet->getPropertyValue(s_sPinned) >>= aItem.isPinned;
140 aRet.push_back(aItem);
142 catch(const uno::Exception&)
144 // <https://bugs.libreoffice.org/show_bug.cgi?id=46074>
145 // "FILEOPEN: No Recent Documents..." discusses a problem
146 // with corrupted /org.openoffice.Office/Histories/Histories
147 // configuration items; to work around that problem, simply
148 // ignore such corrupted individual items here, so that at
149 // least newly added items are successfully reported back
150 // from this function:
151 DBG_UNHANDLED_EXCEPTION("unotools.config");
155 catch(const uno::Exception&)
157 DBG_UNHANDLED_EXCEPTION("unotools.config");
159 return aRet;
162 void AppendItem(EHistoryType eHistory, const OUString& sURL, const OUString& sFilter,
163 const OUString& sTitle, const std::optional<OUString>& sThumbnail,
164 ::std::optional<bool> const oIsReadOnly)
168 uno::Reference<container::XNameAccess> xCfg = GetConfig();
169 uno::Reference<container::XNameAccess> xCommonXCU = GetCommonXCU();
170 uno::Reference<container::XNameAccess> xListAccess(GetListAccess(xCfg, eHistory));
172 TruncateList(xCfg, xListAccess, GetCapacity(xCommonXCU, eHistory));
174 sal_Int32 nMaxSize = GetCapacity(xCommonXCU, eHistory);
175 if (nMaxSize == 0)
176 return;
178 uno::Reference<container::XNameContainer> xItemList;
179 uno::Reference<container::XNameContainer> xOrderList;
180 xListAccess->getByName(s_sItemList) >>= xItemList;
181 xListAccess->getByName(s_sOrderList) >>= xOrderList;
182 sal_Int32 nLength = xOrderList->getElementNames().getLength();
184 // The item to be appended already exists
185 if (xItemList->hasByName(sURL))
187 uno::Reference<beans::XPropertySet> xSet;
188 xItemList->getByName(sURL) >>= xSet;
189 if (sThumbnail)
191 // update the thumbnail
192 xSet->setPropertyValue(s_sThumbnail, uno::Any(*sThumbnail));
194 if (oIsReadOnly)
196 xSet->setPropertyValue(s_sReadOnly, uno::Any(*oIsReadOnly));
199 // tdf#38742 - check the current pinned state of the item and move it accordingly
200 bool bIsItemPinned = false;
201 if (xSet->getPropertySetInfo()->hasPropertyByName(s_sPinned))
202 xSet->getPropertyValue(s_sPinned) >>= bIsItemPinned;
203 if (bIsItemPinned)
204 PrependItem(xCfg, xOrderList, sURL);
205 else
206 MoveItemToUnpinned(xCfg, xOrderList, xItemList, sURL);
208 else // The item to be appended does not exist yet
210 uno::Reference<beans::XPropertySet> xSet;
211 uno::Reference<lang::XSingleServiceFactory> xFac;
212 uno::Reference<uno::XInterface> xInst;
213 uno::Reference<beans::XPropertySet> xPrevSet;
214 uno::Reference<beans::XPropertySet> xNextSet;
216 // Append new item to OrderList.
217 if ( nLength == nMaxSize )
219 OUString sRemove;
220 xOrderList->getByName(OUString::number(nLength-1)) >>= xSet;
221 xSet->getPropertyValue(s_sHistoryItemRef) >>= sRemove;
224 xItemList->removeByName(sRemove);
226 catch (container::NoSuchElementException &)
228 // <https://bugs.libreoffice.org/show_bug.cgi?id=46074>
229 // "FILEOPEN: No Recent Documents..." discusses a problem
230 // with corrupted /org.openoffice.Office/Histories/Histories
231 // configuration items; to work around that problem, simply
232 // ignore such corrupted individual items here, so that at
233 // least newly added items are successfully added:
234 if (!sRemove.isEmpty())
236 throw;
240 if (nLength != nMaxSize)
242 xFac.set(xOrderList, uno::UNO_QUERY);
243 xInst = xFac->createInstance();
244 OUString sPush = OUString::number(nLength++);
245 xOrderList->insertByName(sPush, uno::Any(xInst));
247 for (sal_Int32 j=nLength-1; j>0; --j)
249 xOrderList->getByName( OUString::number(j) ) >>= xPrevSet;
250 xOrderList->getByName( OUString::number(j-1) ) >>= xNextSet;
251 OUString sTemp;
252 xNextSet->getPropertyValue(s_sHistoryItemRef) >>= sTemp;
253 xPrevSet->setPropertyValue(s_sHistoryItemRef, uno::Any(sTemp));
255 xOrderList->getByName( OUString::number(0) ) >>= xSet;
256 xSet->setPropertyValue(s_sHistoryItemRef, uno::Any(sURL));
258 // Append the item to ItemList.
259 xFac.set(xItemList, uno::UNO_QUERY);
260 xInst = xFac->createInstance();
261 xItemList->insertByName(sURL, uno::Any(xInst));
263 xSet.set(xInst, uno::UNO_QUERY);
264 xSet->setPropertyValue(s_sFilter, uno::Any(sFilter));
265 xSet->setPropertyValue(s_sTitle, uno::Any(sTitle));
266 xSet->setPropertyValue(s_sPassword, uno::Any(OUString()));
267 xSet->setPropertyValue(s_sThumbnail, uno::Any(sThumbnail.value_or(OUString())));
268 if (oIsReadOnly)
270 xSet->setPropertyValue(s_sReadOnly, uno::Any(*oIsReadOnly));
273 // tdf#38742 - check the current pinned state of the item and move it accordingly
274 bool bIsItemPinned = false;
275 if (xSet->getPropertySetInfo()->hasPropertyByName(s_sPinned))
276 xSet->getPropertyValue(s_sPinned) >>= bIsItemPinned;
277 if (bIsItemPinned)
278 PrependItem(xCfg, xOrderList, sURL);
279 else
280 MoveItemToUnpinned(xCfg, xOrderList, xItemList, sURL);
282 ::comphelper::ConfigurationHelper::flush(xCfg);
285 catch(const uno::Exception&)
287 DBG_UNHANDLED_EXCEPTION("unotools.config");
291 void DeleteItem(EHistoryType eHistory, const OUString& sURL)
295 uno::Reference<container::XNameAccess> xCfg = GetConfig();
296 uno::Reference<container::XNameAccess> xListAccess(GetListAccess(xCfg, eHistory));
298 uno::Reference<container::XNameContainer> xItemList;
299 uno::Reference<container::XNameContainer> xOrderList;
300 xListAccess->getByName(s_sItemList) >>= xItemList;
301 xListAccess->getByName(s_sOrderList) >>= xOrderList;
302 sal_Int32 nLength = xOrderList->getElementNames().getLength();
304 // if it does not exist, nothing to do
305 if (!xItemList->hasByName(sURL))
306 return;
308 // it's the last one, just clear the lists
309 if (nLength == 1)
311 Clear(eHistory);
312 return;
315 // find it in the OrderList
316 sal_Int32 nFromWhere = 0;
317 for (; nFromWhere < nLength - 1; ++nFromWhere)
319 uno::Reference<beans::XPropertySet> xSet;
320 OUString aItem;
321 xOrderList->getByName(OUString::number(nFromWhere)) >>= xSet;
322 xSet->getPropertyValue(s_sHistoryItemRef) >>= aItem;
324 if (aItem == sURL)
325 break;
328 // and shift the rest of the items in OrderList accordingly
329 for (sal_Int32 i = nFromWhere; i < nLength - 1; ++i)
331 uno::Reference<beans::XPropertySet> xPrevSet;
332 uno::Reference<beans::XPropertySet> xNextSet;
333 xOrderList->getByName(OUString::number(i)) >>= xPrevSet;
334 xOrderList->getByName(OUString::number(i + 1)) >>= xNextSet;
336 OUString sTemp;
337 xNextSet->getPropertyValue(s_sHistoryItemRef) >>= sTemp;
338 xPrevSet->setPropertyValue(s_sHistoryItemRef, uno::Any(sTemp));
340 xOrderList->removeByName(OUString::number(nLength - 1));
342 // and finally remove it from the ItemList
343 xItemList->removeByName(sURL);
345 ::comphelper::ConfigurationHelper::flush(xCfg);
347 catch (const uno::Exception&)
349 DBG_UNHANDLED_EXCEPTION("unotools.config");
353 void TogglePinItem(EHistoryType eHistory, const OUString& sURL)
357 uno::Reference<container::XNameAccess> xCfg = GetConfig();
358 uno::Reference<container::XNameAccess> xListAccess(GetListAccess(xCfg, eHistory));
360 uno::Reference<container::XNameContainer> xItemList;
361 xListAccess->getByName(s_sItemList) >>= xItemList;
363 // Check if item exists
364 if (xItemList->hasByName(sURL))
366 // Toggle pinned option
367 uno::Reference<beans::XPropertySet> xSet;
368 xItemList->getByName(sURL) >>= xSet;
369 bool bIsItemPinned = false;
370 if (xSet->getPropertySetInfo()->hasPropertyByName(s_sPinned))
371 xSet->getPropertyValue(s_sPinned) >>= bIsItemPinned;
372 xSet->setPropertyValue(s_sPinned, uno::Any(!bIsItemPinned));
374 uno::Reference<container::XNameContainer> xOrderList;
375 xListAccess->getByName(s_sOrderList) >>= xOrderList;
377 // Shift item to the beginning of the document list if is not pinned now
378 if (bIsItemPinned)
379 MoveItemToUnpinned(xCfg, xOrderList, xItemList, sURL);
380 else
381 PrependItem(xCfg, xOrderList, sURL);
384 catch (const uno::Exception&)
386 DBG_UNHANDLED_EXCEPTION("unotools.config");
390 } // namespace
393 static uno::Reference<container::XNameAccess> GetConfig()
395 return uno::Reference<container::XNameAccess>(
396 ::comphelper::ConfigurationHelper::openConfig(
397 ::comphelper::getProcessComponentContext(),
398 "org.openoffice.Office.Histories/Histories",
399 ::comphelper::EConfigurationModes::Standard),
400 uno::UNO_QUERY_THROW);
403 static uno::Reference<container::XNameAccess> GetCommonXCU()
405 return uno::Reference<container::XNameAccess>(
406 ::comphelper::ConfigurationHelper::openConfig(
407 ::comphelper::getProcessComponentContext(),
408 "org.openoffice.Office.Common/History",
409 ::comphelper::EConfigurationModes::Standard),
410 uno::UNO_QUERY_THROW);
413 static uno::Reference<container::XNameAccess> GetListAccess(
414 const uno::Reference<container::XNameAccess>& xCfg,
415 EHistoryType eHistory)
417 uno::Reference<container::XNameAccess> xListAccess;
418 switch (eHistory)
420 case EHistoryType::PickList:
421 xCfg->getByName("PickList") >>= xListAccess;
422 break;
424 case EHistoryType::HelpBookmarks:
425 xCfg->getByName("HelpBookmarks") >>= xListAccess;
426 break;
428 return xListAccess;
431 static void TruncateList(
432 const uno::Reference<container::XNameAccess>& xCfg,
433 const uno::Reference<container::XNameAccess>& xList,
434 sal_uInt32 nSize)
436 uno::Reference<container::XNameContainer> xItemList;
437 uno::Reference<container::XNameContainer> xOrderList;
438 xList->getByName(s_sOrderList) >>= xOrderList;
439 xList->getByName(s_sItemList) >>= xItemList;
441 const sal_uInt32 nLength = xOrderList->getElementNames().getLength();
442 if (nSize >= nLength)
443 return;
445 for (sal_uInt32 i=nLength-1; i>=nSize; --i)
447 uno::Reference<beans::XPropertySet> xSet;
448 OUString sTmp;
449 const OUString sRemove = OUString::number(i);
450 xOrderList->getByName(sRemove) >>= xSet;
451 xSet->getPropertyValue(s_sHistoryItemRef) >>= sTmp;
452 xItemList->removeByName(sTmp);
453 xOrderList->removeByName(sRemove);
456 ::comphelper::ConfigurationHelper::flush(xCfg);
459 static void PrependItem(const uno::Reference<container::XNameAccess>& xCfg,
460 uno::Reference<container::XNameContainer>& xList, std::u16string_view sURL)
462 uno::Reference<beans::XPropertySet> xSet;
463 const sal_Int32 nLength = xList->getElementNames().getLength();
464 for (sal_Int32 i = 0; i < nLength; i++)
466 OUString aItem;
467 xList->getByName(OUString::number(i)) >>= xSet;
468 xSet->getPropertyValue(s_sHistoryItemRef) >>= aItem;
470 if (aItem == sURL)
472 for (sal_Int32 j = i - 1; j >= 0; --j)
474 uno::Reference<beans::XPropertySet> xPrevSet;
475 uno::Reference<beans::XPropertySet> xNextSet;
476 xList->getByName(OUString::number(j + 1)) >>= xPrevSet;
477 xList->getByName(OUString::number(j)) >>= xNextSet;
479 OUString sTemp;
480 xNextSet->getPropertyValue(s_sHistoryItemRef) >>= sTemp;
481 xPrevSet->setPropertyValue(s_sHistoryItemRef, uno::Any(sTemp));
483 xList->getByName(OUString::number(0)) >>= xSet;
484 xSet->setPropertyValue(s_sHistoryItemRef, uno::Any(aItem));
485 ::comphelper::ConfigurationHelper::flush(xCfg);
486 return;
491 static void MoveItemToUnpinned(const uno::Reference<container::XNameAccess>& xCfg,
492 uno::Reference<container::XNameContainer>& xOrderList,
493 uno::Reference<container::XNameContainer>& xItemList,
494 std::u16string_view sURL)
496 uno::Reference<beans::XPropertySet> xSet;
497 const sal_Int32 nLength = xOrderList->getElementNames().getLength();
498 // Search for item in the ordered list list
499 for (sal_Int32 i = 0; i < nLength; i++)
501 OUString aItem;
502 xOrderList->getByName(OUString::number(i)) >>= xSet;
503 xSet->getPropertyValue(s_sHistoryItemRef) >>= aItem;
505 if (aItem == sURL)
507 // Move item to the unpinned document section to the right if it was previously pinned
508 for (sal_Int32 j = i + 1; j < nLength; j++)
510 uno::Reference<beans::XPropertySet> xNextSet;
511 xOrderList->getByName(OUString::number(j)) >>= xNextSet;
513 OUString aNextItem;
514 xNextSet->getPropertyValue(s_sHistoryItemRef) >>= aNextItem;
516 uno::Reference<beans::XPropertySet> xNextItemSet;
517 xItemList->getByName(aNextItem) >>= xNextItemSet;
518 bool bIsItemPinned = false;
519 if (xNextItemSet->getPropertySetInfo()->hasPropertyByName(s_sPinned))
520 xNextItemSet->getPropertyValue(s_sPinned) >>= bIsItemPinned;
521 if (bIsItemPinned)
523 xOrderList->getByName(OUString::number(j - 1)) >>= xSet;
524 xSet->getPropertyValue(s_sHistoryItemRef) >>= aItem;
525 xSet->setPropertyValue(s_sHistoryItemRef, uno::Any(aNextItem));
526 xNextSet->setPropertyValue(s_sHistoryItemRef, uno::Any(aItem));
528 else
529 break;
532 // Move item to the unpinned document section to the left if it was previously unpinned
533 for (sal_Int32 j = i - 1; j >= 0; --j)
535 uno::Reference<beans::XPropertySet> xPrevSet;
536 xOrderList->getByName(OUString::number(j)) >>= xPrevSet;
538 OUString aPrevItem;
539 xPrevSet->getPropertyValue(s_sHistoryItemRef) >>= aPrevItem;
541 uno::Reference<beans::XPropertySet> xPrevItemSet;
542 xItemList->getByName(aPrevItem) >>= xPrevItemSet;
543 bool bIsItemPinned = false;
544 if (xPrevItemSet->getPropertySetInfo()->hasPropertyByName(s_sPinned))
545 xPrevItemSet->getPropertyValue(s_sPinned) >>= bIsItemPinned;
546 if (!bIsItemPinned)
548 xOrderList->getByName(OUString::number(j + 1)) >>= xSet;
549 xSet->getPropertyValue(s_sHistoryItemRef) >>= aItem;
550 xSet->setPropertyValue(s_sHistoryItemRef, uno::Any(aPrevItem));
551 xPrevSet->setPropertyValue(s_sHistoryItemRef, uno::Any(aItem));
553 else
554 break;
557 ::comphelper::ConfigurationHelper::flush(xCfg);
558 return;
563 static sal_uInt32 GetCapacity(const uno::Reference<container::XNameAccess>& xCommonXCU, EHistoryType eHistory)
565 uno::Reference<beans::XPropertySet> xListAccess(xCommonXCU, uno::UNO_QUERY_THROW);
567 sal_uInt32 nSize = 0;
569 switch (eHistory)
571 case EHistoryType::PickList:
572 xListAccess->getPropertyValue("PickListSize") >>= nSize;
573 break;
575 case EHistoryType::HelpBookmarks:
576 xListAccess->getPropertyValue("HelpBookmarkSize") >>= nSize;
577 break;
580 return nSize;
583 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */