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