1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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>
33 using namespace ::com::sun::star
;
34 using namespace ::com::sun::star::uno
;
35 using namespace ::com::sun::star::beans
;
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
,
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
)
75 uno::Reference
<container::XNameAccess
> xCfg
= GetConfig();
76 uno::Reference
<container::XNameAccess
> xListAccess(GetListAccess(xCfg
, eHistory
));
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
);
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
)
125 uno::Reference
<beans::XPropertySet
> xSet
;
126 xOrderList
->getByName(OUString::number(nItem
)) >>= xSet
;
127 xSet
->getPropertyValue(s_sHistoryItemRef
) >>= sUrl
;
129 xItemList
->getByName(sUrl
) >>= xSet
;
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");
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
);
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
;
191 // update the thumbnail
192 xSet
->setPropertyValue(s_sThumbnail
, uno::Any(*sThumbnail
));
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
;
204 PrependItem(xCfg
, xOrderList
, sURL
);
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
)
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())
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
;
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())));
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
;
278 PrependItem(xCfg
, xOrderList
, sURL
);
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
))
308 // it's the last one, just clear the lists
315 // find it in the OrderList
316 sal_Int32 nFromWhere
= 0;
317 for (; nFromWhere
< nLength
- 1; ++nFromWhere
)
319 uno::Reference
<beans::XPropertySet
> xSet
;
321 xOrderList
->getByName(OUString::number(nFromWhere
)) >>= xSet
;
322 xSet
->getPropertyValue(s_sHistoryItemRef
) >>= aItem
;
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
;
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
379 MoveItemToUnpinned(xCfg
, xOrderList
, xItemList
, sURL
);
381 PrependItem(xCfg
, xOrderList
, sURL
);
384 catch (const uno::Exception
&)
386 DBG_UNHANDLED_EXCEPTION("unotools.config");
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
;
420 case EHistoryType::PickList
:
421 xCfg
->getByName("PickList") >>= xListAccess
;
424 case EHistoryType::HelpBookmarks
:
425 xCfg
->getByName("HelpBookmarks") >>= xListAccess
;
431 static void TruncateList(
432 const uno::Reference
<container::XNameAccess
>& xCfg
,
433 const uno::Reference
<container::XNameAccess
>& xList
,
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
)
445 for (sal_uInt32 i
=nLength
-1; i
>=nSize
; --i
)
447 uno::Reference
<beans::XPropertySet
> xSet
;
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
++)
467 xList
->getByName(OUString::number(i
)) >>= xSet
;
468 xSet
->getPropertyValue(s_sHistoryItemRef
) >>= aItem
;
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
;
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
);
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
++)
502 xOrderList
->getByName(OUString::number(i
)) >>= xSet
;
503 xSet
->getPropertyValue(s_sHistoryItemRef
) >>= aItem
;
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
;
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
;
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
));
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
;
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
;
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
));
557 ::comphelper::ConfigurationHelper::flush(xCfg
);
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;
571 case EHistoryType::PickList
:
572 xListAccess
->getPropertyValue("PickListSize") >>= nSize
;
575 case EHistoryType::HelpBookmarks
:
576 xListAccess
->getPropertyValue("HelpBookmarkSize") >>= nSize
;
583 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */