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 <osl/file.hxx>
21 #include <unotools/historyoptions.hxx>
22 #include <unotools/configmgr.hxx>
23 #include <unotools/configitem.hxx>
24 #include <com/sun/star/uno/Any.hxx>
25 #include <com/sun/star/uno/Sequence.hxx>
31 #include "itemholder1.hxx"
33 #include <com/sun/star/beans/XPropertySet.hpp>
34 #include <com/sun/star/container/XNameAccess.hpp>
35 #include <com/sun/star/container/XNameContainer.hpp>
36 #include <com/sun/star/lang/XSingleServiceFactory.hpp>
37 #include <comphelper/configurationhelper.hxx>
38 #include <comphelper/processfactory.hxx>
40 using namespace ::std
;
41 using namespace ::utl
;
42 using namespace ::osl
;
43 using namespace ::com::sun::star
;
44 using namespace ::com::sun::star::uno
;
45 using namespace ::com::sun::star::beans
;
48 static const ::sal_Int32 s_nOffsetURL
= 0;
49 static const ::sal_Int32 s_nOffsetFilter
= 1;
50 static const ::sal_Int32 s_nOffsetTitle
= 2;
51 static const ::sal_Int32 s_nOffsetPassword
= 3;
52 static const ::sal_Int32 s_nOffsetThumbnail
= 4;
54 const char s_sCommonHistory
[] = "org.openoffice.Office.Common/History";
55 const char s_sHistories
[] = "org.openoffice.Office.Histories/Histories";
56 const char s_sPickListSize
[] = "PickListSize";
57 const char s_sHelpBookmarksSize
[] = "HelpBookmarkSize";
58 const char s_sPickList
[] = "PickList";
59 const char s_sHelpBookmarks
[] = "HelpBookmarks";
60 const char s_sItemList
[] = "ItemList";
61 const char s_sOrderList
[] = "OrderList";
62 const char s_sHistoryItemRef
[] = "HistoryItemRef";
63 const char s_sFilter
[] = "Filter";
64 const char s_sTitle
[] = "Title";
65 const char s_sPassword
[] = "Password";
66 const char s_sThumbnail
[] = "Thumbnail";
68 class theHistoryOptionsMutex
: public rtl::Static
<osl::Mutex
, theHistoryOptionsMutex
>{};
71 /// Internal implementation of the SvtHistoryOptions.
72 class SvtHistoryOptions_Impl
75 SvtHistoryOptions_Impl();
76 ~SvtHistoryOptions_Impl();
78 /// Returns the maximum size of the internal lists, ie. the capacity not the size.
79 sal_uInt32
GetCapacity(EHistoryType eHistory
);
81 /// Clear the specified history list.
82 void Clear(EHistoryType eHistory
);
84 /// Get a sequence list from the items.
85 Sequence
< Sequence
<PropertyValue
> > GetList(EHistoryType eHistory
);
87 void AppendItem(EHistoryType eHistory
,
88 const OUString
& sURL
, const OUString
& sFilter
, const OUString
& sTitle
,
89 const OUString
& sPassword
, const OUString
& sThumbnail
);
91 void DeleteItem(EHistoryType eHistory
, const OUString
& sURL
);
94 /// Return the appropriate list of recent documents (based on eHistory).
95 uno::Reference
<container::XNameAccess
> GetListAccess(EHistoryType eHistory
) const;
97 void impl_truncateList(EHistoryType eHistory
, sal_uInt32 nSize
);
100 uno::Reference
<container::XNameAccess
> m_xCfg
;
101 uno::Reference
<container::XNameAccess
> m_xCommonXCU
;
104 SvtHistoryOptions_Impl::SvtHistoryOptions_Impl()
108 m_xCfg
= Reference
<container::XNameAccess
> (
109 ::comphelper::ConfigurationHelper::openConfig(
110 ::comphelper::getProcessComponentContext(),
112 ::comphelper::ConfigurationHelper::E_STANDARD
),
115 m_xCommonXCU
= Reference
<container::XNameAccess
> (
116 ::comphelper::ConfigurationHelper::openConfig(
117 ::comphelper::getProcessComponentContext(),
119 ::comphelper::ConfigurationHelper::E_STANDARD
),
122 catch(const uno::Exception
& ex
)
125 m_xCommonXCU
.clear();
127 SAL_WARN("unotools.config", "Caught unexpected: " << ex
.Message
);
131 SvtHistoryOptions_Impl::~SvtHistoryOptions_Impl()
135 sal_uInt32
SvtHistoryOptions_Impl::GetCapacity(EHistoryType eHistory
)
137 uno::Reference
<beans::XPropertySet
> xListAccess(m_xCommonXCU
, uno::UNO_QUERY
);
139 if (!xListAccess
.is())
142 sal_uInt32 nSize
= 0;
149 xListAccess
->getPropertyValue(s_sPickListSize
) >>= nSize
;
153 xListAccess
->getPropertyValue(s_sHelpBookmarksSize
) >>= nSize
;
160 catch (const uno::Exception
& ex
)
162 SAL_WARN("unotools.config", "Caught unexpected: " << ex
.Message
);
168 uno::Reference
<container::XNameAccess
> SvtHistoryOptions_Impl::GetListAccess(EHistoryType eHistory
) const
170 uno::Reference
<container::XNameAccess
> xListAccess
;
177 m_xCfg
->getByName(s_sPickList
) >>= xListAccess
;
181 m_xCfg
->getByName(s_sHelpBookmarks
) >>= xListAccess
;
188 catch (const uno::Exception
& ex
)
190 SAL_WARN("unotools.config", "Caught unexpected: " << ex
.Message
);
196 void SvtHistoryOptions_Impl::impl_truncateList(EHistoryType eHistory
, sal_uInt32 nSize
)
198 uno::Reference
<container::XNameAccess
> xList(GetListAccess(eHistory
));
202 uno::Reference
<container::XNameContainer
> xItemList
;
203 uno::Reference
<container::XNameContainer
> xOrderList
;
204 uno::Reference
<beans::XPropertySet
> xSet
;
208 xList
->getByName(s_sOrderList
) >>= xOrderList
;
209 xList
->getByName(s_sItemList
) >>= xItemList
;
211 const sal_uInt32 nLength
= xOrderList
->getElementNames().getLength();
214 for (sal_uInt32 i
=nLength
-1; i
>=nSize
; --i
)
217 const OUString sRemove
= OUString::number(i
);
218 xOrderList
->getByName(sRemove
) >>= xSet
;
219 xSet
->getPropertyValue(s_sHistoryItemRef
) >>= sTmp
;
220 xItemList
->removeByName(sTmp
);
221 xOrderList
->removeByName(sRemove
);
224 ::comphelper::ConfigurationHelper::flush(m_xCfg
);
227 catch(const uno::Exception
& ex
)
229 SAL_WARN("unotools.config", "Caught unexpected: " << ex
.Message
);
233 void SvtHistoryOptions_Impl::Clear( EHistoryType eHistory
)
235 uno::Reference
<container::XNameAccess
> xListAccess(GetListAccess(eHistory
));
236 if (!xListAccess
.is())
239 uno::Reference
<container::XNameContainer
> xNode
;
244 xListAccess
->getByName(s_sItemList
) >>= xNode
;
245 Sequence
<OUString
> aStrings(xNode
->getElementNames());
247 const sal_Int32 nLength
= aStrings
.getLength();
248 for (sal_Int32 i
= 0; i
< nLength
; ++i
)
249 xNode
->removeByName(aStrings
[i
]);
252 xListAccess
->getByName(s_sOrderList
) >>= xNode
;
253 aStrings
= xNode
->getElementNames();
255 for (sal_Int32 j
= 0; j
< nLength
; ++j
)
256 xNode
->removeByName(aStrings
[j
]);
258 ::comphelper::ConfigurationHelper::flush(m_xCfg
);
260 catch(const uno::Exception
& ex
)
262 SAL_WARN("unotools.config", "Caught unexpected: " << ex
.Message
);
266 static bool lcl_fileOpenable(const OUString
&rURL
)
268 osl::File
aRecentFile(rURL
);
269 if(!aRecentFile
.open(osl_File_OpenFlag_Read
))
278 Sequence
< Sequence
<PropertyValue
> > SvtHistoryOptions_Impl::GetList(EHistoryType eHistory
)
280 uno::Reference
<container::XNameAccess
> xListAccess(GetListAccess(eHistory
));
281 if (!xListAccess
.is())
282 return Sequence
< Sequence
<PropertyValue
> >();
284 impl_truncateList(eHistory
, GetCapacity(eHistory
));
286 Sequence
<PropertyValue
> seqProperties(5);
287 seqProperties
[s_nOffsetURL
].Name
= HISTORY_PROPERTYNAME_URL
;
288 seqProperties
[s_nOffsetFilter
].Name
= HISTORY_PROPERTYNAME_FILTER
;
289 seqProperties
[s_nOffsetTitle
].Name
= HISTORY_PROPERTYNAME_TITLE
;
290 seqProperties
[s_nOffsetPassword
].Name
= HISTORY_PROPERTYNAME_PASSWORD
;
291 seqProperties
[s_nOffsetThumbnail
].Name
= HISTORY_PROPERTYNAME_THUMBNAIL
;
293 uno::Reference
<container::XNameAccess
> xItemList
;
294 uno::Reference
<container::XNameAccess
> xOrderList
;
297 xListAccess
->getByName(s_sItemList
) >>= xItemList
;
298 xListAccess
->getByName(s_sOrderList
) >>= xOrderList
;
300 catch(const uno::Exception
& ex
)
302 SAL_WARN("unotools.config", "Caught unexpected: " << ex
.Message
);
305 const sal_Int32 nLength
= xOrderList
->getElementNames().getLength();
306 Sequence
< Sequence
<PropertyValue
> > aRet(nLength
);
307 sal_Int32 nCount
= 0;
309 for (sal_Int32 nItem
= 0; nItem
< nLength
; ++nItem
)
314 uno::Reference
<beans::XPropertySet
> xSet
;
315 xOrderList
->getByName(OUString::number(nItem
)) >>= xSet
;
316 xSet
->getPropertyValue(s_sHistoryItemRef
) >>= sUrl
;
318 if (!sUrl
.startsWith("file://") || lcl_fileOpenable(sUrl
))
320 xItemList
->getByName(sUrl
) >>= xSet
;
321 seqProperties
[s_nOffsetURL
].Value
<<= sUrl
;
323 xSet
->getPropertyValue(s_sFilter
) >>= seqProperties
[s_nOffsetFilter
].Value
;
324 xSet
->getPropertyValue(s_sTitle
) >>= seqProperties
[s_nOffsetTitle
].Value
;
325 xSet
->getPropertyValue(s_sPassword
) >>= seqProperties
[s_nOffsetPassword
].Value
;
326 xSet
->getPropertyValue(s_sThumbnail
)>>= seqProperties
[s_nOffsetThumbnail
].Value
;
327 aRet
[nCount
++] = seqProperties
;
330 catch(const uno::Exception
& ex
)
332 // <https://bugs.libreoffice.org/show_bug.cgi?id=46074>
333 // "FILEOPEN: No Recent Documents..." discusses a problem
334 // with corrupted /org.openoffice.Office/Histories/Histories
335 // configuration items; to work around that problem, simply
336 // ignore such corrupted individual items here, so that at
337 // least newly added items are successfully reported back
338 // from this function:
339 SAL_WARN("unotools.config", "Caught unexpected: " << ex
.Message
);
342 assert(nCount
<= nLength
);
343 aRet
.realloc(nCount
);
347 void SvtHistoryOptions_Impl::AppendItem(EHistoryType eHistory
,
348 const OUString
& sURL
, const OUString
& sFilter
, const OUString
& sTitle
,
349 const OUString
& sPassword
, const OUString
& sThumbnail
)
351 uno::Reference
<container::XNameAccess
> xListAccess(GetListAccess(eHistory
));
352 if (!xListAccess
.is())
355 impl_truncateList(eHistory
, GetCapacity(eHistory
));
357 sal_Int32 nMaxSize
= GetCapacity(eHistory
);
361 uno::Reference
<container::XNameContainer
> xItemList
;
362 uno::Reference
<container::XNameContainer
> xOrderList
;
363 uno::Reference
<beans::XPropertySet
> xSet
;
367 xListAccess
->getByName(s_sItemList
) >>= xItemList
;
368 xListAccess
->getByName(s_sOrderList
) >>= xOrderList
;
369 sal_Int32 nLength
= xOrderList
->getElementNames().getLength();
371 // The item to be appended already exists
372 if (xItemList
->hasByName(sURL
))
374 if (!sThumbnail
.isEmpty())
376 // update the thumbnail
377 xItemList
->getByName(sURL
) >>= xSet
;
378 xSet
->setPropertyValue(s_sThumbnail
, uno::makeAny(sThumbnail
));
381 for (sal_Int32 i
=0; i
<nLength
; ++i
)
384 xOrderList
->getByName(OUString::number(i
)) >>= xSet
;
385 xSet
->getPropertyValue(s_sHistoryItemRef
) >>= aItem
;
389 for (sal_Int32 j
= i
- 1; j
>= 0; --j
)
391 uno::Reference
<beans::XPropertySet
> xPrevSet
;
392 uno::Reference
<beans::XPropertySet
> xNextSet
;
393 xOrderList
->getByName(OUString::number(j
+1)) >>= xPrevSet
;
394 xOrderList
->getByName(OUString::number(j
)) >>= xNextSet
;
397 xNextSet
->getPropertyValue(s_sHistoryItemRef
) >>= sTemp
;
398 xPrevSet
->setPropertyValue(s_sHistoryItemRef
, uno::makeAny(sTemp
));
400 xOrderList
->getByName(OUString::number(0)) >>= xSet
;
401 xSet
->setPropertyValue(s_sHistoryItemRef
, uno::makeAny(aItem
));
406 ::comphelper::ConfigurationHelper::flush(m_xCfg
);
408 else // The item to be appended does not exist yet
410 uno::Reference
<lang::XSingleServiceFactory
> xFac
;
411 uno::Reference
<uno::XInterface
> xInst
;
412 uno::Reference
<beans::XPropertySet
> xPrevSet
;
413 uno::Reference
<beans::XPropertySet
> xNextSet
;
415 // Append new item to OrderList.
416 if ( nLength
== nMaxSize
)
419 xOrderList
->getByName(OUString::number(nLength
-1)) >>= xSet
;
420 xSet
->getPropertyValue(s_sHistoryItemRef
) >>= sRemove
;
423 xItemList
->removeByName(sRemove
);
425 catch (container::NoSuchElementException
&)
427 // <https://bugs.libreoffice.org/show_bug.cgi?id=46074>
428 // "FILEOPEN: No Recent Documents..." discusses a problem
429 // with corrupted /org.openoffice.Office/Histories/Histories
430 // configuration items; to work around that problem, simply
431 // ignore such corrupted individual items here, so that at
432 // least newly added items are successfully added:
433 if (!sRemove
.isEmpty())
439 if (nLength
!= nMaxSize
)
441 xFac
= uno::Reference
<lang::XSingleServiceFactory
>(xOrderList
, uno::UNO_QUERY
);
442 xInst
= xFac
->createInstance();
443 OUString sPush
= OUString::number(nLength
++);
444 xOrderList
->insertByName(sPush
, uno::makeAny(xInst
));
446 for (sal_Int32 j
=nLength
-1; j
>0; --j
)
448 xOrderList
->getByName( OUString::number(j
) ) >>= xPrevSet
;
449 xOrderList
->getByName( OUString::number(j
-1) ) >>= xNextSet
;
451 xNextSet
->getPropertyValue(s_sHistoryItemRef
) >>= sTemp
;
452 xPrevSet
->setPropertyValue(s_sHistoryItemRef
, uno::makeAny(sTemp
));
454 xOrderList
->getByName( OUString::number(0) ) >>= xSet
;
455 xSet
->setPropertyValue(s_sHistoryItemRef
, uno::makeAny(sURL
));
457 // Append the item to ItemList.
458 xFac
= uno::Reference
<lang::XSingleServiceFactory
>(xItemList
, uno::UNO_QUERY
);
459 xInst
= xFac
->createInstance();
460 xItemList
->insertByName(sURL
, uno::makeAny(xInst
));
462 xSet
= uno::Reference
<beans::XPropertySet
>(xInst
, uno::UNO_QUERY
);
463 xSet
->setPropertyValue(s_sFilter
, uno::makeAny(sFilter
));
464 xSet
->setPropertyValue(s_sTitle
, uno::makeAny(sTitle
));
465 xSet
->setPropertyValue(s_sPassword
, uno::makeAny(sPassword
));
466 xSet
->setPropertyValue(s_sThumbnail
, uno::makeAny(sThumbnail
));
468 ::comphelper::ConfigurationHelper::flush(m_xCfg
);
471 catch(const uno::Exception
& ex
)
473 SAL_WARN("unotools.config", "Caught unexpected: " << ex
.Message
);
477 void SvtHistoryOptions_Impl::DeleteItem(EHistoryType eHistory
, const OUString
& sURL
)
479 uno::Reference
<container::XNameAccess
> xListAccess(GetListAccess(eHistory
));
480 if (!xListAccess
.is())
483 uno::Reference
<container::XNameContainer
> xItemList
;
484 uno::Reference
<container::XNameContainer
> xOrderList
;
485 uno::Reference
<beans::XPropertySet
> xSet
;
489 xListAccess
->getByName(s_sItemList
) >>= xItemList
;
490 xListAccess
->getByName(s_sOrderList
) >>= xOrderList
;
491 sal_Int32 nLength
= xOrderList
->getElementNames().getLength();
493 // if it does not exist, nothing to do
494 if (!xItemList
->hasByName(sURL
))
497 // it's the last one, just clear the lists
504 // find it in the OrderList
505 sal_Int32 nFromWhere
= 0;
506 for (; nFromWhere
< nLength
- 1; ++nFromWhere
)
509 xOrderList
->getByName(OUString::number(nFromWhere
)) >>= xSet
;
510 xSet
->getPropertyValue(s_sHistoryItemRef
) >>= aItem
;
516 // and shift the rest of the items in OrderList accordingly
517 for (sal_Int32 i
= nFromWhere
; i
< nLength
- 1; ++i
)
519 uno::Reference
<beans::XPropertySet
> xPrevSet
;
520 uno::Reference
<beans::XPropertySet
> xNextSet
;
521 xOrderList
->getByName(OUString::number(i
)) >>= xPrevSet
;
522 xOrderList
->getByName(OUString::number(i
+ 1)) >>= xNextSet
;
525 xNextSet
->getPropertyValue(s_sHistoryItemRef
) >>= sTemp
;
526 xPrevSet
->setPropertyValue(s_sHistoryItemRef
, uno::makeAny(sTemp
));
528 xOrderList
->removeByName(OUString::number(nLength
- 1));
530 // and finally remove it from the ItemList
531 xItemList
->removeByName(sURL
);
533 ::comphelper::ConfigurationHelper::flush(m_xCfg
);
535 catch (const uno::Exception
& ex
)
537 SAL_WARN("unotools.config", "Caught unexpected: " << ex
.Message
);
541 // initialize static member
542 // DON'T DO IT IN YOUR HEADER!
543 // see definition for further information
545 SvtHistoryOptions_Impl
* SvtHistoryOptions::m_pDataContainer
= NULL
;
546 sal_Int32
SvtHistoryOptions::m_nRefCount
= 0;
550 SvtHistoryOptions::SvtHistoryOptions()
552 MutexGuard
aGuard(theHistoryOptionsMutex::get());
554 // Increase our refcount ...
556 // ... and initialize our data container only if it not already exist!
557 if( m_pDataContainer
== NULL
)
559 m_pDataContainer
= new SvtHistoryOptions_Impl
;
561 ItemHolder1::holdConfigItem(E_HISTORYOPTIONS
);
567 SvtHistoryOptions::~SvtHistoryOptions()
569 MutexGuard
aGuard(theHistoryOptionsMutex::get());
571 // Decrease our refcount.
573 // If last instance was deleted ...
574 // we must destroy our static data container!
575 if( m_nRefCount
<= 0 )
577 delete m_pDataContainer
;
578 m_pDataContainer
= NULL
;
582 sal_uInt32
SvtHistoryOptions::GetSize( EHistoryType eHistory
) const
584 MutexGuard
aGuard(theHistoryOptionsMutex::get());
586 return m_pDataContainer
->GetCapacity(eHistory
);
589 void SvtHistoryOptions::Clear( EHistoryType eHistory
)
591 MutexGuard
aGuard(theHistoryOptionsMutex::get());
593 m_pDataContainer
->Clear( eHistory
);
596 Sequence
< Sequence
< PropertyValue
> > SvtHistoryOptions::GetList( EHistoryType eHistory
) const
598 MutexGuard
aGuard(theHistoryOptionsMutex::get());
600 return m_pDataContainer
->GetList( eHistory
);
603 void SvtHistoryOptions::AppendItem(EHistoryType eHistory
,
604 const OUString
& sURL
, const OUString
& sFilter
, const OUString
& sTitle
,
605 const OUString
& sPassword
, const OUString
& sThumbnail
)
607 MutexGuard
aGuard(theHistoryOptionsMutex::get());
609 m_pDataContainer
->AppendItem(eHistory
, sURL
, sFilter
, sTitle
, sPassword
, sThumbnail
);
612 void SvtHistoryOptions::DeleteItem(EHistoryType eHistory
, const OUString
& sURL
)
614 MutexGuard
aGuard(theHistoryOptionsMutex::get());
616 m_pDataContainer
->DeleteItem(eHistory
, sURL
);
619 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */