bump product version to 5.0.4.1
[LibreOffice.git] / sd / source / ui / slidesorter / cache / SlsPageCacheManager.cxx
blob9e8624bb3add62cc3b12b5a8c4ca09547f2b43b0
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 "cache/SlsPageCacheManager.hxx"
22 #include "SlsBitmapCache.hxx"
23 #include "view/SlideSorterView.hxx"
24 #include "model/SlideSorterModel.hxx"
26 #include <deque>
27 #include <map>
28 #include <boost/weak_ptr.hpp>
30 namespace {
32 /** Collection of data that is stored for all active preview caches.
34 class CacheDescriptor
36 public:
37 ::sd::slidesorter::cache::PageCacheManager::DocumentKey mpDocument;
38 Size maPreviewSize;
40 CacheDescriptor(
41 ::sd::slidesorter::cache::PageCacheManager::DocumentKey pDocument,
42 const Size& rPreviewSize)
43 :mpDocument(pDocument),maPreviewSize(rPreviewSize)
45 /// Test for equality with respect to all members.
46 class Equal {public: bool operator() (
47 const CacheDescriptor& rDescriptor1, const CacheDescriptor& rDescriptor2) const {
48 return rDescriptor1.mpDocument==rDescriptor2.mpDocument
49 && rDescriptor1.maPreviewSize==rDescriptor2.maPreviewSize;
50 } };
51 /// Hash function that takes all members into account.
52 class Hash {public: size_t operator() (const CacheDescriptor& rDescriptor) const {
53 return reinterpret_cast<size_t>(rDescriptor.mpDocument.get()) + rDescriptor.maPreviewSize.Width();
54 } };
57 /** Collection of data that is stored for the inactive, recently used
58 caches.
60 class RecentlyUsedCacheDescriptor
62 public:
63 ::sd::slidesorter::cache::PageCacheManager::DocumentKey mpDocument;
64 Size maPreviewSize;
65 ::boost::shared_ptr< ::sd::slidesorter::cache::PageCacheManager::Cache> mpCache;
67 RecentlyUsedCacheDescriptor(
68 ::sd::slidesorter::cache::PageCacheManager::DocumentKey pDocument,
69 const Size& rPreviewSize,
70 const ::boost::shared_ptr< ::sd::slidesorter::cache::PageCacheManager::Cache>& rpCache)
71 :mpDocument(pDocument),maPreviewSize(rPreviewSize),mpCache(rpCache)
75 /** The list of recently used caches is organized as queue. When elements
76 are added the list is shortened to the maximally allowed number of
77 elements by removing the least recently used elements.
79 typedef ::std::deque<RecentlyUsedCacheDescriptor> RecentlyUsedQueue;
81 /** Compare the caches by preview size. Those that match the given size
82 come first, then, regardless of the given size, the largest ones before
83 the smaller ones.
85 class BestFittingCacheComparer
87 public:
88 BestFittingCacheComparer (const Size& rPreferredSize)
89 : maPreferredSize(rPreferredSize)
91 bool operator()(const ::sd::slidesorter::cache::PageCacheManager::BestFittingPageCaches::value_type& rElement1,
92 const ::sd::slidesorter::cache::PageCacheManager::BestFittingPageCaches::value_type& rElement2)
94 if (rElement1.first == maPreferredSize)
95 return true;
96 else if (rElement2.first == maPreferredSize)
97 return false;
98 else
99 return (rElement1.first.Width()*rElement1.first.Height()
100 > rElement2.first.Width()*rElement2.first.Height());
103 private:
104 Size maPreferredSize;
107 } // end of anonymous namespace
109 namespace sd { namespace slidesorter { namespace cache {
111 /** Container for the active caches.
113 class PageCacheManager::PageCacheContainer
114 : public std::unordered_map<CacheDescriptor,
115 ::boost::shared_ptr<PageCacheManager::Cache>,
116 CacheDescriptor::Hash,
117 CacheDescriptor::Equal>
119 public:
120 PageCacheContainer() {}
122 /** Compare entries in the cache container with respect to the cache
123 address only.
125 class CompareWithCache { public:
126 CompareWithCache(const ::boost::shared_ptr<PageCacheManager::Cache>& rpCache)
127 : mpCache(rpCache) {}
128 bool operator () (const PageCacheContainer::value_type& rValue) const
129 { return rValue.second == mpCache; }
130 private:
131 ::boost::shared_ptr<PageCacheManager::Cache> mpCache;
135 /** The recently used caches are stored in one queue for each document.
137 class PageCacheManager::RecentlyUsedPageCaches
139 public:
140 typedef DocumentKey key_type;
141 typedef RecentlyUsedQueue mapped_type;
142 typedef std::pair<const key_type,mapped_type> value_type;
143 typedef std::map<key_type,mapped_type>::iterator iterator;
144 private:
145 std::map<key_type,mapped_type> maMap;
146 public:
147 RecentlyUsedPageCaches () {};
149 iterator end() { return maMap.end(); }
150 void clear() { maMap.clear(); }
151 iterator find(const key_type& key) { return maMap.find(key); }
152 std::pair<iterator,bool> insert(const value_type& value) { return maMap.insert(value); }
155 class PageCacheManager::Deleter
157 public:
158 void operator() (PageCacheManager* pObject) { delete pObject; }
161 //===== PageCacheManager ====================================================
163 ::boost::weak_ptr<PageCacheManager> PageCacheManager::mpInstance;
165 ::boost::shared_ptr<PageCacheManager> PageCacheManager::Instance()
167 ::boost::shared_ptr<PageCacheManager> pInstance;
169 ::osl::MutexGuard aGuard (::osl::Mutex::getGlobalMutex());
171 pInstance = mpInstance.lock();
172 if (pInstance.get() == NULL)
174 pInstance = ::boost::shared_ptr<PageCacheManager>(
175 new PageCacheManager(),
176 PageCacheManager::Deleter());
177 mpInstance = pInstance;
180 return pInstance;
183 PageCacheManager::PageCacheManager()
184 : mpPageCaches(new PageCacheContainer()),
185 mpRecentlyUsedPageCaches(new RecentlyUsedPageCaches()),
186 mnMaximalRecentlyCacheCount(2)
190 PageCacheManager::~PageCacheManager()
194 ::boost::shared_ptr<PageCacheManager::Cache> PageCacheManager::GetCache (
195 DocumentKey pDocument,
196 const Size& rPreviewSize)
198 ::boost::shared_ptr<Cache> pResult;
200 // Look for the cache in the list of active caches.
201 CacheDescriptor aKey (pDocument, rPreviewSize);
202 PageCacheContainer::iterator iCache (mpPageCaches->find(aKey));
203 if (iCache != mpPageCaches->end())
204 pResult = iCache->second;
206 // Look for the cache in the list of recently used caches.
207 if (pResult.get() == NULL)
208 pResult = GetRecentlyUsedCache(pDocument, rPreviewSize);
210 // Create the cache when no suitable one does exist.
211 if (pResult.get() == NULL)
212 pResult.reset(new Cache());
214 // The cache may be newly created and thus empty or is old and may
215 // contain previews that are not up-to-date. Recycle previews from
216 // other caches to fill in the holes.
217 Recycle(pResult, pDocument,rPreviewSize);
219 // Put the new (or old) cache into the container.
220 if (pResult.get() != NULL)
221 mpPageCaches->insert(PageCacheContainer::value_type(aKey, pResult));
223 return pResult;
226 void PageCacheManager::Recycle (
227 const ::boost::shared_ptr<Cache>& rpCache,
228 DocumentKey pDocument,
229 const Size& rPreviewSize)
231 BestFittingPageCaches aCaches;
233 // Add bitmap caches from active caches.
234 PageCacheContainer::iterator iActiveCache;
235 for (iActiveCache=mpPageCaches->begin(); iActiveCache!=mpPageCaches->end(); ++iActiveCache)
237 if (iActiveCache->first.mpDocument == pDocument)
238 aCaches.push_back(BestFittingPageCaches::value_type(
239 iActiveCache->first.maPreviewSize, iActiveCache->second));
242 // Add bitmap caches from recently used caches.
243 RecentlyUsedPageCaches::iterator iQueue (mpRecentlyUsedPageCaches->find(pDocument));
244 if (iQueue != mpRecentlyUsedPageCaches->end())
246 RecentlyUsedQueue::const_iterator iRecentCache;
247 for (iRecentCache=iQueue->second.begin();iRecentCache!=iQueue->second.end();++iRecentCache)
248 aCaches.push_back(BestFittingPageCaches::value_type(
249 iRecentCache->maPreviewSize, iRecentCache->mpCache));
252 ::std::sort(aCaches.begin(), aCaches.end(), BestFittingCacheComparer(rPreviewSize));
254 BestFittingPageCaches::const_iterator iBestCache;
255 for (iBestCache=aCaches.begin(); iBestCache!=aCaches.end(); ++iBestCache)
257 rpCache->Recycle(*iBestCache->second);
261 void PageCacheManager::ReleaseCache (const ::boost::shared_ptr<Cache>& rpCache)
263 PageCacheContainer::iterator iCache (::std::find_if(
264 mpPageCaches->begin(),
265 mpPageCaches->end(),
266 PageCacheContainer::CompareWithCache(rpCache)));
268 if (iCache != mpPageCaches->end())
270 OSL_ASSERT(iCache->second == rpCache);
272 PutRecentlyUsedCache(iCache->first.mpDocument,iCache->first.maPreviewSize,rpCache);
274 mpPageCaches->erase(iCache);
278 ::boost::shared_ptr<PageCacheManager::Cache> PageCacheManager::ChangeSize (
279 const ::boost::shared_ptr<Cache>& rpCache,
280 const Size& rOldPreviewSize,
281 const Size& rNewPreviewSize)
283 (void)rOldPreviewSize;
285 ::boost::shared_ptr<Cache> pResult;
287 if (rpCache.get() != NULL)
289 // Look up the given cache in the list of active caches.
290 PageCacheContainer::iterator iCacheToChange (::std::find_if(
291 mpPageCaches->begin(),
292 mpPageCaches->end(),
293 PageCacheContainer::CompareWithCache(rpCache)));
294 if (iCacheToChange != mpPageCaches->end())
296 OSL_ASSERT(iCacheToChange->second == rpCache);
298 // Now, we can change the preview size of the existing one by
299 // removing the cache from the list and re-insert it with the
300 // updated size.
301 const ::sd::slidesorter::cache::PageCacheManager::DocumentKey aKey (
302 iCacheToChange->first.mpDocument);
303 mpPageCaches->erase(iCacheToChange);
304 mpPageCaches->insert(PageCacheContainer::value_type(
305 CacheDescriptor(aKey,rNewPreviewSize),
306 rpCache));
308 pResult = rpCache;
310 else
312 OSL_ASSERT(iCacheToChange != mpPageCaches->end());
316 return pResult;
319 bool PageCacheManager::InvalidatePreviewBitmap (
320 DocumentKey pDocument,
321 const SdrPage* pKey)
323 bool bHasChanged (false);
325 if (pDocument!=NULL)
327 // Iterate over all caches that are currently in use and invalidate
328 // the previews in those that belong to the document.
329 PageCacheContainer::iterator iCache;
330 for (iCache=mpPageCaches->begin(); iCache!=mpPageCaches->end(); ++iCache)
331 if (iCache->first.mpDocument == pDocument)
332 bHasChanged |= iCache->second->InvalidateBitmap(pKey);
334 // Invalidate the previews in the recently used caches belonging to
335 // the given document.
336 RecentlyUsedPageCaches::iterator iQueue (mpRecentlyUsedPageCaches->find(pDocument));
337 if (iQueue != mpRecentlyUsedPageCaches->end())
339 RecentlyUsedQueue::const_iterator iCache2;
340 for (iCache2=iQueue->second.begin(); iCache2!=iQueue->second.end(); ++iCache2)
341 bHasChanged |= iCache2->mpCache->InvalidateBitmap(pKey);
345 return bHasChanged;
348 void PageCacheManager::InvalidateAllPreviewBitmaps (DocumentKey pDocument)
350 if (pDocument == NULL)
351 return;
353 // Iterate over all caches that are currently in use and invalidate the
354 // previews in those that belong to the document.
355 PageCacheContainer::iterator iCache;
356 for (iCache=mpPageCaches->begin(); iCache!=mpPageCaches->end(); ++iCache)
357 if (iCache->first.mpDocument == pDocument)
358 iCache->second->InvalidateCache();
360 // Invalidate the previews in the recently used caches belonging to the
361 // given document.
362 RecentlyUsedPageCaches::iterator iQueue (mpRecentlyUsedPageCaches->find(pDocument));
363 if (iQueue != mpRecentlyUsedPageCaches->end())
365 RecentlyUsedQueue::const_iterator iCache2;
366 for (iCache2=iQueue->second.begin(); iCache2!=iQueue->second.end(); ++iCache2)
367 iCache2->mpCache->InvalidateCache();
371 void PageCacheManager::InvalidateAllCaches()
373 // Iterate over all caches that are currently in use and invalidate
374 // them.
375 PageCacheContainer::iterator iCache;
376 for (iCache=mpPageCaches->begin(); iCache!=mpPageCaches->end(); ++iCache)
377 iCache->second->InvalidateCache();
379 // Remove all recently used caches, there is not much sense in storing
380 // invalidated and unused caches.
381 mpRecentlyUsedPageCaches->clear();
384 void PageCacheManager::ReleasePreviewBitmap (const SdrPage* pPage)
386 PageCacheContainer::iterator iCache;
387 for (iCache=mpPageCaches->begin(); iCache!=mpPageCaches->end(); ++iCache)
388 iCache->second->ReleaseBitmap(pPage);
391 ::boost::shared_ptr<PageCacheManager::Cache> PageCacheManager::GetRecentlyUsedCache (
392 DocumentKey pDocument,
393 const Size& rPreviewSize)
395 ::boost::shared_ptr<Cache> pCache;
397 // Look for the cache in the list of recently used caches.
398 RecentlyUsedPageCaches::iterator iQueue (mpRecentlyUsedPageCaches->find(pDocument));
399 if (iQueue != mpRecentlyUsedPageCaches->end())
401 RecentlyUsedQueue::iterator iCache;
402 for (iCache=iQueue->second.begin(); iCache!= iQueue->second.end(); ++iCache)
403 if (iCache->maPreviewSize == rPreviewSize)
405 pCache = iCache->mpCache;
406 iQueue->second.erase(iCache);
407 break;
411 return pCache;
414 void PageCacheManager::PutRecentlyUsedCache(
415 DocumentKey pDocument,
416 const Size& rPreviewSize,
417 const ::boost::shared_ptr<Cache>& rpCache)
419 // Look up the list of recently used caches for the given document.
420 RecentlyUsedPageCaches::iterator iQueue (mpRecentlyUsedPageCaches->find(pDocument));
421 if (iQueue == mpRecentlyUsedPageCaches->end())
422 iQueue = mpRecentlyUsedPageCaches->insert(
423 RecentlyUsedPageCaches::value_type(pDocument, RecentlyUsedQueue())
424 ).first;
426 if (iQueue != mpRecentlyUsedPageCaches->end())
428 iQueue->second.push_front(RecentlyUsedCacheDescriptor(pDocument,rPreviewSize,rpCache));
429 // Shorten the list of recently used caches to the allowed maximal length.
430 while (iQueue->second.size() > mnMaximalRecentlyCacheCount)
431 iQueue->second.pop_back();
435 } } } // end of namespace ::sd::slidesorter::cache
437 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */