xref: /AOO41X/main/sd/source/ui/slidesorter/cache/SlsPageCacheManager.cxx (revision cdf0e10c4e3984b49a9502b011690b615761d4a3)
1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_sd.hxx"
30 
31 #include "cache/SlsPageCacheManager.hxx"
32 
33 #include "SlsBitmapCache.hxx"
34 #include "view/SlideSorterView.hxx"
35 #include "model/SlideSorterModel.hxx"
36 
37 #include <deque>
38 #include <map>
39 #include <boost/weak_ptr.hpp>
40 
41 namespace {
42 
43 /** Collection of data that is stored for all active preview caches.
44 */
45 class CacheDescriptor
46 {
47 public:
48     ::sd::slidesorter::cache::PageCacheManager::DocumentKey mpDocument;
49     Size maPreviewSize;
50 
51     CacheDescriptor(
52         ::sd::slidesorter::cache::PageCacheManager::DocumentKey pDocument,
53         const Size& rPreviewSize)
54         :mpDocument(pDocument),maPreviewSize(rPreviewSize)
55     {}
56     /// Test for equality with respect to all members.
57     class Equal {public: bool operator() (
58         const CacheDescriptor& rDescriptor1, const CacheDescriptor& rDescriptor2) const {
59         return rDescriptor1.mpDocument==rDescriptor2.mpDocument
60             && rDescriptor1.maPreviewSize==rDescriptor2.maPreviewSize;
61     } };
62     /// Hash function that takes all members into account.
63     class Hash {public: size_t operator() (const CacheDescriptor& rDescriptor) const {
64         return (size_t)rDescriptor.mpDocument.get() + rDescriptor.maPreviewSize.Width();
65     } };
66 };
67 
68 
69 
70 
71 /** Collection of data that is stored for the inactive, recently used
72     caches.
73 */
74 class RecentlyUsedCacheDescriptor
75 {
76 public:
77     ::sd::slidesorter::cache::PageCacheManager::DocumentKey mpDocument;
78     Size maPreviewSize;
79     ::boost::shared_ptr< ::sd::slidesorter::cache::PageCacheManager::Cache> mpCache;
80 
81     RecentlyUsedCacheDescriptor(
82         ::sd::slidesorter::cache::PageCacheManager::DocumentKey pDocument,
83         const Size& rPreviewSize,
84         const ::boost::shared_ptr< ::sd::slidesorter::cache::PageCacheManager::Cache>& rpCache)
85         :mpDocument(pDocument),maPreviewSize(rPreviewSize),mpCache(rpCache)
86     {}
87 };
88 
89 
90 
91 
92 /** The list of recently used caches is organized as queue.  When elements
93     are added the list is shortened to the maximally allowed number of
94     elements by removing the least recently used elements.
95 */
96 typedef ::std::deque<RecentlyUsedCacheDescriptor> RecentlyUsedQueue;
97 
98 
99 
100 
101 /** Compare the caches by preview size.  Those that match the given size
102     come first, then, regardless of the given size, the largest ones before
103     the smaller ones.
104 */
105 class BestFittingCacheComparer
106 {
107 public:
108     BestFittingCacheComparer (const Size& rPreferredSize)
109         : maPreferredSize(rPreferredSize)
110     {}
111     bool operator()(const ::sd::slidesorter::cache::PageCacheManager::BestFittingPageCaches::value_type& rElement1,
112         const ::sd::slidesorter::cache::PageCacheManager::BestFittingPageCaches::value_type& rElement2)
113     {
114         if (rElement1.first == maPreferredSize)
115             return true;
116         else if (rElement2.first == maPreferredSize)
117             return false;
118         else
119             return (rElement1.first.Width()*rElement1.first.Height()
120                 > rElement2.first.Width()*rElement2.first.Height());
121     }
122 
123 private:
124     Size maPreferredSize;
125 };
126 
127 } // end of anonymous namespace
128 
129 
130 namespace sd { namespace slidesorter { namespace cache {
131 
132 /** Container for the active caches.
133 */
134 class PageCacheManager::PageCacheContainer
135     : public ::std::hash_map<CacheDescriptor,
136                              ::boost::shared_ptr<PageCacheManager::Cache>,
137                              CacheDescriptor::Hash,
138                              CacheDescriptor::Equal>
139 {
140 public:
141     PageCacheContainer (void) {}
142 
143     /** Compare entries in the cache container with respect to the cache
144         address only.
145     */
146     class CompareWithCache { public:
147         CompareWithCache(const ::boost::shared_ptr<PageCacheManager::Cache>& rpCache)
148             : mpCache(rpCache) {}
149         bool operator () (const PageCacheContainer::value_type& rValue)
150         { return rValue.second == mpCache; }
151     private:
152         ::boost::shared_ptr<PageCacheManager::Cache> mpCache;
153     };
154 };
155 
156 
157 /** The recently used caches are stored in one queue for each document.
158 */
159 class PageCacheManager::RecentlyUsedPageCaches
160     : public ::std::map<DocumentKey,RecentlyUsedQueue>
161 {
162 public:
163     RecentlyUsedPageCaches (void) {};
164 };
165 
166 
167 
168 
169 class PageCacheManager::Deleter
170 {
171 public:
172     void operator() (PageCacheManager* pObject) { delete pObject; }
173 };
174 
175 
176 
177 //===== PageCacheManager ====================================================
178 
179 ::boost::weak_ptr<PageCacheManager> PageCacheManager::mpInstance;
180 
181 ::boost::shared_ptr<PageCacheManager> PageCacheManager::Instance (void)
182 {
183     ::boost::shared_ptr<PageCacheManager> pInstance;
184 
185     ::osl::MutexGuard aGuard (::osl::Mutex::getGlobalMutex());
186 
187     pInstance = mpInstance.lock();
188     if (pInstance.get() == NULL)
189     {
190         pInstance = ::boost::shared_ptr<PageCacheManager>(
191             new PageCacheManager(),
192             PageCacheManager::Deleter());
193         mpInstance = pInstance;
194     }
195 
196     return pInstance;
197 }
198 
199 
200 
201 
202 PageCacheManager::PageCacheManager (void)
203     : mpPageCaches(new PageCacheContainer()),
204       mpRecentlyUsedPageCaches(new RecentlyUsedPageCaches()),
205       mnMaximalRecentlyCacheCount(2)
206 {
207 }
208 
209 
210 
211 
212 PageCacheManager::~PageCacheManager (void)
213 {
214 }
215 
216 
217 
218 
219 ::boost::shared_ptr<PageCacheManager::Cache> PageCacheManager::GetCache (
220     DocumentKey pDocument,
221     const Size& rPreviewSize)
222 {
223     ::boost::shared_ptr<Cache> pResult;
224 
225     // Look for the cache in the list of active caches.
226     CacheDescriptor aKey (pDocument, rPreviewSize);
227     PageCacheContainer::iterator iCache (mpPageCaches->find(aKey));
228     if (iCache != mpPageCaches->end())
229         pResult = iCache->second;
230 
231     // Look for the cache in the list of recently used caches.
232     if (pResult.get() == NULL)
233         pResult = GetRecentlyUsedCache(pDocument, rPreviewSize);
234 
235     // Create the cache when no suitable one does exist.
236     if (pResult.get() == NULL)
237         pResult.reset(new Cache());
238 
239     // The cache may be newly created and thus empty or is old and may
240     // contain previews that are not up-to-date.  Recycle previews from
241     // other caches to fill in the holes.
242     Recycle(pResult, pDocument,rPreviewSize);
243 
244     // Put the new (or old) cache into the container.
245     if (pResult.get() != NULL)
246         mpPageCaches->insert(PageCacheContainer::value_type(aKey, pResult));
247 
248     return pResult;
249 }
250 
251 
252 
253 
254 void PageCacheManager::Recycle (
255     const ::boost::shared_ptr<Cache>& rpCache,
256     DocumentKey pDocument,
257     const Size& rPreviewSize)
258 {
259     BestFittingPageCaches aCaches;
260 
261     // Add bitmap caches from active caches.
262     PageCacheContainer::iterator iActiveCache;
263     for (iActiveCache=mpPageCaches->begin(); iActiveCache!=mpPageCaches->end(); ++iActiveCache)
264     {
265         if (iActiveCache->first.mpDocument == pDocument)
266             aCaches.push_back(BestFittingPageCaches::value_type(
267                 iActiveCache->first.maPreviewSize, iActiveCache->second));
268     }
269 
270     // Add bitmap caches from recently used caches.
271     RecentlyUsedPageCaches::iterator iQueue (mpRecentlyUsedPageCaches->find(pDocument));
272     if (iQueue != mpRecentlyUsedPageCaches->end())
273     {
274         RecentlyUsedQueue::const_iterator iRecentCache;
275         for (iRecentCache=iQueue->second.begin();iRecentCache!=iQueue->second.end();++iRecentCache)
276             aCaches.push_back(BestFittingPageCaches::value_type(
277                 iRecentCache->maPreviewSize, iRecentCache->mpCache));
278     }
279 
280     ::std::sort(aCaches.begin(), aCaches.end(), BestFittingCacheComparer(rPreviewSize));
281 
282     BestFittingPageCaches::const_iterator iBestCache;
283     for (iBestCache=aCaches.begin(); iBestCache!=aCaches.end(); ++iBestCache)
284     {
285         rpCache->Recycle(*iBestCache->second);
286     }
287 }
288 
289 
290 
291 
292 void PageCacheManager::ReleaseCache (const ::boost::shared_ptr<Cache>& rpCache)
293 {
294     PageCacheContainer::iterator iCache (::std::find_if(
295         mpPageCaches->begin(),
296         mpPageCaches->end(),
297         PageCacheContainer::CompareWithCache(rpCache)));
298 
299     if (iCache != mpPageCaches->end())
300     {
301         OSL_ASSERT(iCache->second == rpCache);
302 
303         PutRecentlyUsedCache(iCache->first.mpDocument,iCache->first.maPreviewSize,rpCache);
304 
305         mpPageCaches->erase(iCache);
306     }
307 }
308 
309 
310 
311 
312 ::boost::shared_ptr<PageCacheManager::Cache> PageCacheManager::ChangeSize (
313     const ::boost::shared_ptr<Cache>& rpCache,
314     const Size& rOldPreviewSize,
315     const Size& rNewPreviewSize)
316 {
317     (void)rOldPreviewSize;
318 
319     ::boost::shared_ptr<Cache> pResult;
320 
321     if (rpCache.get() != NULL)
322     {
323         // Look up the given cache in the list of active caches.
324         PageCacheContainer::iterator iCacheToChange (::std::find_if(
325             mpPageCaches->begin(),
326             mpPageCaches->end(),
327             PageCacheContainer::CompareWithCache(rpCache)));
328         if (iCacheToChange != mpPageCaches->end())
329         {
330             OSL_ASSERT(iCacheToChange->second == rpCache);
331 
332             // Now, we can change the preview size of the existing one by
333             // removing the cache from the list and re-insert it with the
334             // updated size.
335             const ::sd::slidesorter::cache::PageCacheManager::DocumentKey aKey (
336                 iCacheToChange->first.mpDocument);
337             mpPageCaches->erase(iCacheToChange);
338             mpPageCaches->insert(PageCacheContainer::value_type(
339                 CacheDescriptor(aKey,rNewPreviewSize),
340                 rpCache));
341 
342             pResult = rpCache;
343         }
344         else
345         {
346             OSL_ASSERT(iCacheToChange != mpPageCaches->end());
347         }
348     }
349 
350     return pResult;
351 }
352 
353 
354 
355 
356 bool PageCacheManager::InvalidatePreviewBitmap (
357     DocumentKey pDocument,
358     const SdrPage* pKey)
359 {
360     bool bHasChanged (false);
361 
362     if (pDocument!=NULL)
363     {
364         // Iterate over all caches that are currently in use and invalidate
365         // the previews in those that belong to the document.
366         PageCacheContainer::iterator iCache;
367         for (iCache=mpPageCaches->begin(); iCache!=mpPageCaches->end();  ++iCache)
368             if (iCache->first.mpDocument == pDocument)
369                 bHasChanged |= iCache->second->InvalidateBitmap(pKey);
370 
371         // Invalidate the previews in the recently used caches belonging to
372         // the given document.
373         RecentlyUsedPageCaches::iterator iQueue (mpRecentlyUsedPageCaches->find(pDocument));
374         if (iQueue != mpRecentlyUsedPageCaches->end())
375         {
376             RecentlyUsedQueue::const_iterator iCache2;
377             for (iCache2=iQueue->second.begin(); iCache2!=iQueue->second.end(); ++iCache2)
378                 bHasChanged |= iCache2->mpCache->InvalidateBitmap(pKey);
379         }
380     }
381 
382     return bHasChanged;
383 }
384 
385 
386 
387 
388 void PageCacheManager::InvalidateAllPreviewBitmaps (DocumentKey pDocument)
389 {
390     if (pDocument == NULL)
391         return;
392 
393     // Iterate over all caches that are currently in use and invalidate the
394     // previews in those that belong to the document.
395     PageCacheContainer::iterator iCache;
396     for (iCache=mpPageCaches->begin(); iCache!=mpPageCaches->end();  ++iCache)
397         if (iCache->first.mpDocument == pDocument)
398             iCache->second->InvalidateCache();
399 
400     // Invalidate the previews in the recently used caches belonging to the
401     // given document.
402     RecentlyUsedPageCaches::iterator iQueue (mpRecentlyUsedPageCaches->find(pDocument));
403     if (iQueue != mpRecentlyUsedPageCaches->end())
404     {
405         RecentlyUsedQueue::const_iterator iCache2;
406         for (iCache2=iQueue->second.begin(); iCache2!=iQueue->second.end(); ++iCache2)
407             iCache2->mpCache->InvalidateCache();
408     }
409 }
410 
411 
412 
413 
414 void PageCacheManager::InvalidateAllCaches (void)
415 {
416     // Iterate over all caches that are currently in use and invalidate
417     // them.
418     PageCacheContainer::iterator iCache;
419     for (iCache=mpPageCaches->begin(); iCache!=mpPageCaches->end();  ++iCache)
420         iCache->second->InvalidateCache();
421 
422     // Remove all recently used caches, there is not much sense in storing
423     // invalidated and unused caches.
424     mpRecentlyUsedPageCaches->clear();
425 }
426 
427 
428 
429 
430 void PageCacheManager::ReleasePreviewBitmap (const SdrPage* pPage)
431 {
432     PageCacheContainer::iterator iCache;
433     for (iCache=mpPageCaches->begin(); iCache!=mpPageCaches->end(); ++iCache)
434         iCache->second->ReleaseBitmap(pPage);
435 }
436 
437 
438 
439 
440 ::boost::shared_ptr<PageCacheManager::Cache> PageCacheManager::GetRecentlyUsedCache (
441     DocumentKey pDocument,
442     const Size& rPreviewSize)
443 {
444     ::boost::shared_ptr<Cache> pCache;
445 
446     // Look for the cache in the list of recently used caches.
447     RecentlyUsedPageCaches::iterator iQueue (mpRecentlyUsedPageCaches->find(pDocument));
448     if (iQueue != mpRecentlyUsedPageCaches->end())
449     {
450         RecentlyUsedQueue::iterator iCache;
451         for (iCache=iQueue->second.begin(); iCache!= iQueue->second.end(); ++iCache)
452             if (iCache->maPreviewSize == rPreviewSize)
453             {
454                 pCache = iCache->mpCache;
455                 iQueue->second.erase(iCache);
456                 break;
457             }
458     }
459 
460     return pCache;
461 }
462 
463 
464 
465 
466 void PageCacheManager::PutRecentlyUsedCache(
467     DocumentKey pDocument,
468     const Size& rPreviewSize,
469     const ::boost::shared_ptr<Cache>& rpCache)
470 {
471     // Look up the list of recently used caches for the given document.
472     RecentlyUsedPageCaches::iterator iQueue (mpRecentlyUsedPageCaches->find(pDocument));
473     if (iQueue == mpRecentlyUsedPageCaches->end())
474         iQueue = mpRecentlyUsedPageCaches->insert(
475             RecentlyUsedPageCaches::value_type(pDocument, RecentlyUsedQueue())
476             ).first;
477 
478     if (iQueue != mpRecentlyUsedPageCaches->end())
479     {
480         iQueue->second.push_front(RecentlyUsedCacheDescriptor(pDocument,rPreviewSize,rpCache));
481         // Shorten the list of recently used caches to the allowed maximal length.
482         while (iQueue->second.size() > mnMaximalRecentlyCacheCount)
483             iQueue->second.pop_back();
484     }
485 }
486 
487 
488 
489 } } } // end of namespace ::sd::slidesorter::cache
490