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