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>
27 #include "itemholder1.hxx"
29 #include <com/sun/star/beans/PropertyValue.hpp>
30 #include <com/sun/star/beans/XPropertySet.hpp>
31 #include <com/sun/star/container/XNameAccess.hpp>
32 #include <com/sun/star/container/XNameContainer.hpp>
33 #include <com/sun/star/lang/XSingleServiceFactory.hpp>
34 #include <comphelper/configurationhelper.hxx>
35 #include <comphelper/processfactory.hxx>
36 #include <tools/diagnose_ex.h>
37 #include <boost/optional.hpp>
39 using namespace ::std
;
40 using namespace ::utl
;
41 using namespace ::osl
;
42 using namespace ::com::sun::star
;
43 using namespace ::com::sun::star::uno
;
44 using namespace ::com::sun::star::beans
;
47 static const ::sal_Int32 s_nOffsetURL
= 0;
48 static const ::sal_Int32 s_nOffsetFilter
= 1;
49 static const ::sal_Int32 s_nOffsetTitle
= 2;
50 static const ::sal_Int32 s_nOffsetPassword
= 3;
51 static const ::sal_Int32 s_nOffsetThumbnail
= 4;
53 const char s_sItemList
[] = "ItemList";
54 const char s_sOrderList
[] = "OrderList";
55 const char s_sHistoryItemRef
[] = "HistoryItemRef";
56 const char s_sFilter
[] = "Filter";
57 const char s_sTitle
[] = "Title";
58 const char s_sPassword
[] = "Password";
59 const char s_sThumbnail
[] = "Thumbnail";
61 class theHistoryOptionsMutex
: public rtl::Static
<osl::Mutex
, theHistoryOptionsMutex
>{};
64 /// Internal implementation of the SvtHistoryOptions.
65 class SvtHistoryOptions_Impl
68 SvtHistoryOptions_Impl();
70 /// Returns the maximum size of the internal lists, ie. the capacity not the size.
71 sal_uInt32
GetCapacity(EHistoryType eHistory
) const;
73 /// Clear the specified history list.
74 void Clear(EHistoryType eHistory
);
76 /// Get a sequence list from the items.
77 Sequence
< Sequence
<PropertyValue
> > GetList(EHistoryType eHistory
);
79 void AppendItem(EHistoryType eHistory
,
80 const OUString
& sURL
, const OUString
& sFilter
, const OUString
& sTitle
,
81 const boost::optional
<OUString
>& sThumbnail
);
83 void DeleteItem(EHistoryType eHistory
, const OUString
& sURL
);
86 /// Return the appropriate list of recent documents (based on eHistory).
87 uno::Reference
<container::XNameAccess
> GetListAccess(EHistoryType eHistory
) const;
89 void impl_truncateList(EHistoryType eHistory
, sal_uInt32 nSize
);
92 uno::Reference
<container::XNameAccess
> m_xCfg
;
93 uno::Reference
<container::XNameAccess
> m_xCommonXCU
;
96 SvtHistoryOptions_Impl::SvtHistoryOptions_Impl()
101 ::comphelper::ConfigurationHelper::openConfig(
102 ::comphelper::getProcessComponentContext(),
103 "org.openoffice.Office.Histories/Histories",
104 ::comphelper::EConfigurationModes::Standard
),
108 ::comphelper::ConfigurationHelper::openConfig(
109 ::comphelper::getProcessComponentContext(),
110 "org.openoffice.Office.Common/History",
111 ::comphelper::EConfigurationModes::Standard
),
114 catch(const uno::Exception
&)
116 DBG_UNHANDLED_EXCEPTION("unotools.config");
118 m_xCommonXCU
.clear();
122 sal_uInt32
SvtHistoryOptions_Impl::GetCapacity(EHistoryType eHistory
) const
124 uno::Reference
<beans::XPropertySet
> xListAccess(m_xCommonXCU
, uno::UNO_QUERY
);
126 if (!xListAccess
.is())
129 sal_uInt32 nSize
= 0;
136 xListAccess
->getPropertyValue("PickListSize") >>= nSize
;
140 xListAccess
->getPropertyValue("HelpBookmarkSize") >>= nSize
;
147 catch (const uno::Exception
&)
149 DBG_UNHANDLED_EXCEPTION("unotools.config");
155 uno::Reference
<container::XNameAccess
> SvtHistoryOptions_Impl::GetListAccess(EHistoryType eHistory
) const
157 uno::Reference
<container::XNameAccess
> xListAccess
;
164 m_xCfg
->getByName("PickList") >>= xListAccess
;
168 m_xCfg
->getByName("HelpBookmarks") >>= xListAccess
;
175 catch (const uno::Exception
&)
177 DBG_UNHANDLED_EXCEPTION("unotools.config");
183 void SvtHistoryOptions_Impl::impl_truncateList(EHistoryType eHistory
, sal_uInt32 nSize
)
185 uno::Reference
<container::XNameAccess
> xList(GetListAccess(eHistory
));
189 uno::Reference
<container::XNameContainer
> xItemList
;
190 uno::Reference
<container::XNameContainer
> xOrderList
;
191 uno::Reference
<beans::XPropertySet
> xSet
;
195 xList
->getByName(s_sOrderList
) >>= xOrderList
;
196 xList
->getByName(s_sItemList
) >>= xItemList
;
198 const sal_uInt32 nLength
= xOrderList
->getElementNames().getLength();
201 for (sal_uInt32 i
=nLength
-1; i
>=nSize
; --i
)
204 const OUString sRemove
= OUString::number(i
);
205 xOrderList
->getByName(sRemove
) >>= xSet
;
206 xSet
->getPropertyValue(s_sHistoryItemRef
) >>= sTmp
;
207 xItemList
->removeByName(sTmp
);
208 xOrderList
->removeByName(sRemove
);
211 ::comphelper::ConfigurationHelper::flush(m_xCfg
);
214 catch(const uno::Exception
&)
216 DBG_UNHANDLED_EXCEPTION("unotools.config");
220 void SvtHistoryOptions_Impl::Clear( EHistoryType eHistory
)
222 uno::Reference
<container::XNameAccess
> xListAccess(GetListAccess(eHistory
));
223 if (!xListAccess
.is())
226 uno::Reference
<container::XNameContainer
> xNode
;
231 xListAccess
->getByName(s_sItemList
) >>= xNode
;
232 Sequence
<OUString
> aStrings(xNode
->getElementNames());
234 for (const auto& rString
: std::as_const(aStrings
))
235 xNode
->removeByName(rString
);
238 xListAccess
->getByName(s_sOrderList
) >>= xNode
;
239 aStrings
= xNode
->getElementNames();
241 for (const auto& rString
: std::as_const(aStrings
))
242 xNode
->removeByName(rString
);
244 ::comphelper::ConfigurationHelper::flush(m_xCfg
);
246 catch(const uno::Exception
&)
248 DBG_UNHANDLED_EXCEPTION("unotools.config");
252 Sequence
< Sequence
<PropertyValue
> > SvtHistoryOptions_Impl::GetList(EHistoryType eHistory
)
254 uno::Reference
<container::XNameAccess
> xListAccess(GetListAccess(eHistory
));
255 if (!xListAccess
.is())
256 return Sequence
< Sequence
<PropertyValue
> >();
258 impl_truncateList(eHistory
, GetCapacity(eHistory
));
260 Sequence
<PropertyValue
> seqProperties(5);
261 seqProperties
[s_nOffsetURL
].Name
= HISTORY_PROPERTYNAME_URL
;
262 seqProperties
[s_nOffsetFilter
].Name
= HISTORY_PROPERTYNAME_FILTER
;
263 seqProperties
[s_nOffsetTitle
].Name
= HISTORY_PROPERTYNAME_TITLE
;
264 seqProperties
[s_nOffsetPassword
].Name
= HISTORY_PROPERTYNAME_PASSWORD
;
265 seqProperties
[s_nOffsetThumbnail
].Name
= HISTORY_PROPERTYNAME_THUMBNAIL
;
267 uno::Reference
<container::XNameAccess
> xItemList
;
268 uno::Reference
<container::XNameAccess
> xOrderList
;
271 xListAccess
->getByName(s_sItemList
) >>= xItemList
;
272 xListAccess
->getByName(s_sOrderList
) >>= xOrderList
;
274 catch(const uno::Exception
&)
276 DBG_UNHANDLED_EXCEPTION("unotools.config");
279 const sal_Int32 nLength
= xOrderList
->getElementNames().getLength();
280 Sequence
< Sequence
<PropertyValue
> > aRet(nLength
);
281 sal_Int32 nCount
= 0;
283 for (sal_Int32 nItem
= 0; nItem
< nLength
; ++nItem
)
288 uno::Reference
<beans::XPropertySet
> xSet
;
289 xOrderList
->getByName(OUString::number(nItem
)) >>= xSet
;
290 xSet
->getPropertyValue(s_sHistoryItemRef
) >>= sUrl
;
292 xItemList
->getByName(sUrl
) >>= xSet
;
293 seqProperties
[s_nOffsetURL
].Value
<<= sUrl
;
295 seqProperties
[s_nOffsetFilter
].Value
= xSet
->getPropertyValue(s_sFilter
);
296 seqProperties
[s_nOffsetTitle
].Value
= xSet
->getPropertyValue(s_sTitle
);
297 seqProperties
[s_nOffsetPassword
].Value
= xSet
->getPropertyValue(s_sPassword
);
298 seqProperties
[s_nOffsetThumbnail
].Value
= xSet
->getPropertyValue(s_sThumbnail
);
299 aRet
[nCount
++] = seqProperties
;
301 catch(const uno::Exception
&)
303 // <https://bugs.libreoffice.org/show_bug.cgi?id=46074>
304 // "FILEOPEN: No Recent Documents..." discusses a problem
305 // with corrupted /org.openoffice.Office/Histories/Histories
306 // configuration items; to work around that problem, simply
307 // ignore such corrupted individual items here, so that at
308 // least newly added items are successfully reported back
309 // from this function:
310 DBG_UNHANDLED_EXCEPTION("unotools.config");
313 assert(nCount
<= nLength
);
314 aRet
.realloc(nCount
);
318 void SvtHistoryOptions_Impl::AppendItem(EHistoryType eHistory
,
319 const OUString
& sURL
, const OUString
& sFilter
, const OUString
& sTitle
,
320 const boost::optional
<OUString
>& sThumbnail
)
322 uno::Reference
<container::XNameAccess
> xListAccess(GetListAccess(eHistory
));
323 if (!xListAccess
.is())
326 impl_truncateList(eHistory
, GetCapacity(eHistory
));
328 sal_Int32 nMaxSize
= GetCapacity(eHistory
);
332 uno::Reference
<container::XNameContainer
> xItemList
;
333 uno::Reference
<container::XNameContainer
> xOrderList
;
334 uno::Reference
<beans::XPropertySet
> xSet
;
338 xListAccess
->getByName(s_sItemList
) >>= xItemList
;
339 xListAccess
->getByName(s_sOrderList
) >>= xOrderList
;
340 sal_Int32 nLength
= xOrderList
->getElementNames().getLength();
342 // The item to be appended already exists
343 if (xItemList
->hasByName(sURL
))
347 // update the thumbnail
348 xItemList
->getByName(sURL
) >>= xSet
;
349 xSet
->setPropertyValue(s_sThumbnail
, uno::makeAny(*sThumbnail
));
352 for (sal_Int32 i
=0; i
<nLength
; ++i
)
355 xOrderList
->getByName(OUString::number(i
)) >>= xSet
;
356 xSet
->getPropertyValue(s_sHistoryItemRef
) >>= aItem
;
360 for (sal_Int32 j
= i
- 1; j
>= 0; --j
)
362 uno::Reference
<beans::XPropertySet
> xPrevSet
;
363 uno::Reference
<beans::XPropertySet
> xNextSet
;
364 xOrderList
->getByName(OUString::number(j
+1)) >>= xPrevSet
;
365 xOrderList
->getByName(OUString::number(j
)) >>= xNextSet
;
368 xNextSet
->getPropertyValue(s_sHistoryItemRef
) >>= sTemp
;
369 xPrevSet
->setPropertyValue(s_sHistoryItemRef
, uno::makeAny(sTemp
));
371 xOrderList
->getByName(OUString::number(0)) >>= xSet
;
372 xSet
->setPropertyValue(s_sHistoryItemRef
, uno::makeAny(aItem
));
377 ::comphelper::ConfigurationHelper::flush(m_xCfg
);
379 else // The item to be appended does not exist yet
381 uno::Reference
<lang::XSingleServiceFactory
> xFac
;
382 uno::Reference
<uno::XInterface
> xInst
;
383 uno::Reference
<beans::XPropertySet
> xPrevSet
;
384 uno::Reference
<beans::XPropertySet
> xNextSet
;
386 // Append new item to OrderList.
387 if ( nLength
== nMaxSize
)
390 xOrderList
->getByName(OUString::number(nLength
-1)) >>= xSet
;
391 xSet
->getPropertyValue(s_sHistoryItemRef
) >>= sRemove
;
394 xItemList
->removeByName(sRemove
);
396 catch (container::NoSuchElementException
&)
398 // <https://bugs.libreoffice.org/show_bug.cgi?id=46074>
399 // "FILEOPEN: No Recent Documents..." discusses a problem
400 // with corrupted /org.openoffice.Office/Histories/Histories
401 // configuration items; to work around that problem, simply
402 // ignore such corrupted individual items here, so that at
403 // least newly added items are successfully added:
404 if (!sRemove
.isEmpty())
410 if (nLength
!= nMaxSize
)
412 xFac
.set(xOrderList
, uno::UNO_QUERY
);
413 xInst
= xFac
->createInstance();
414 OUString sPush
= OUString::number(nLength
++);
415 xOrderList
->insertByName(sPush
, uno::makeAny(xInst
));
417 for (sal_Int32 j
=nLength
-1; j
>0; --j
)
419 xOrderList
->getByName( OUString::number(j
) ) >>= xPrevSet
;
420 xOrderList
->getByName( OUString::number(j
-1) ) >>= xNextSet
;
422 xNextSet
->getPropertyValue(s_sHistoryItemRef
) >>= sTemp
;
423 xPrevSet
->setPropertyValue(s_sHistoryItemRef
, uno::makeAny(sTemp
));
425 xOrderList
->getByName( OUString::number(0) ) >>= xSet
;
426 xSet
->setPropertyValue(s_sHistoryItemRef
, uno::makeAny(sURL
));
428 // Append the item to ItemList.
429 xFac
.set(xItemList
, uno::UNO_QUERY
);
430 xInst
= xFac
->createInstance();
431 xItemList
->insertByName(sURL
, uno::makeAny(xInst
));
433 xSet
.set(xInst
, uno::UNO_QUERY
);
434 xSet
->setPropertyValue(s_sFilter
, uno::makeAny(sFilter
));
435 xSet
->setPropertyValue(s_sTitle
, uno::makeAny(sTitle
));
436 xSet
->setPropertyValue(s_sPassword
, uno::makeAny(OUString()));
437 xSet
->setPropertyValue(s_sThumbnail
, uno::makeAny(sThumbnail
.get_value_or(OUString())));
439 ::comphelper::ConfigurationHelper::flush(m_xCfg
);
442 catch(const uno::Exception
&)
444 DBG_UNHANDLED_EXCEPTION("unotools.config");
448 void SvtHistoryOptions_Impl::DeleteItem(EHistoryType eHistory
, const OUString
& sURL
)
450 uno::Reference
<container::XNameAccess
> xListAccess(GetListAccess(eHistory
));
451 if (!xListAccess
.is())
454 uno::Reference
<container::XNameContainer
> xItemList
;
455 uno::Reference
<container::XNameContainer
> xOrderList
;
456 uno::Reference
<beans::XPropertySet
> xSet
;
460 xListAccess
->getByName(s_sItemList
) >>= xItemList
;
461 xListAccess
->getByName(s_sOrderList
) >>= xOrderList
;
462 sal_Int32 nLength
= xOrderList
->getElementNames().getLength();
464 // if it does not exist, nothing to do
465 if (!xItemList
->hasByName(sURL
))
468 // it's the last one, just clear the lists
475 // find it in the OrderList
476 sal_Int32 nFromWhere
= 0;
477 for (; nFromWhere
< nLength
- 1; ++nFromWhere
)
480 xOrderList
->getByName(OUString::number(nFromWhere
)) >>= xSet
;
481 xSet
->getPropertyValue(s_sHistoryItemRef
) >>= aItem
;
487 // and shift the rest of the items in OrderList accordingly
488 for (sal_Int32 i
= nFromWhere
; i
< nLength
- 1; ++i
)
490 uno::Reference
<beans::XPropertySet
> xPrevSet
;
491 uno::Reference
<beans::XPropertySet
> xNextSet
;
492 xOrderList
->getByName(OUString::number(i
)) >>= xPrevSet
;
493 xOrderList
->getByName(OUString::number(i
+ 1)) >>= xNextSet
;
496 xNextSet
->getPropertyValue(s_sHistoryItemRef
) >>= sTemp
;
497 xPrevSet
->setPropertyValue(s_sHistoryItemRef
, uno::makeAny(sTemp
));
499 xOrderList
->removeByName(OUString::number(nLength
- 1));
501 // and finally remove it from the ItemList
502 xItemList
->removeByName(sURL
);
504 ::comphelper::ConfigurationHelper::flush(m_xCfg
);
506 catch (const uno::Exception
&)
508 DBG_UNHANDLED_EXCEPTION("unotools.config");
514 std::weak_ptr
<SvtHistoryOptions_Impl
> g_pHistoryOptions
;
518 SvtHistoryOptions::SvtHistoryOptions()
520 MutexGuard
aGuard(theHistoryOptionsMutex::get());
522 m_pImpl
= g_pHistoryOptions
.lock();
525 m_pImpl
= std::make_shared
<SvtHistoryOptions_Impl
>();
526 g_pHistoryOptions
= m_pImpl
;
527 ItemHolder1::holdConfigItem(EItem::HistoryOptions
);
531 SvtHistoryOptions::~SvtHistoryOptions()
533 MutexGuard
aGuard(theHistoryOptionsMutex::get());
538 void SvtHistoryOptions::Clear( EHistoryType eHistory
)
540 MutexGuard
aGuard(theHistoryOptionsMutex::get());
542 m_pImpl
->Clear( eHistory
);
545 Sequence
< Sequence
< PropertyValue
> > SvtHistoryOptions::GetList( EHistoryType eHistory
) const
547 MutexGuard
aGuard(theHistoryOptionsMutex::get());
549 return m_pImpl
->GetList( eHistory
);
552 void SvtHistoryOptions::AppendItem(EHistoryType eHistory
,
553 const OUString
& sURL
, const OUString
& sFilter
, const OUString
& sTitle
,
554 const boost::optional
<OUString
>& sThumbnail
)
556 MutexGuard
aGuard(theHistoryOptionsMutex::get());
558 m_pImpl
->AppendItem(eHistory
, sURL
, sFilter
, sTitle
, sThumbnail
);
561 void SvtHistoryOptions::DeleteItem(EHistoryType eHistory
, const OUString
& sURL
)
563 MutexGuard
aGuard(theHistoryOptionsMutex::get());
565 m_pImpl
->DeleteItem(eHistory
, sURL
);
568 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */