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 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. 53 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. 59 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 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: 104 BestFittingCacheComparer (const Size& rPreferredSize) 105 : maPreferredSize(rPreferredSize) 106 {} 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: 137 PageCacheContainer (void) {} 138 139 /** Compare entries in the cache container with respect to the cache 140 address only. 141 */ 142 class CompareWithCache { public: 143 CompareWithCache(const ::boost::shared_ptr<PageCacheManager::Cache>& rpCache) 144 : mpCache(rpCache) {} 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: 159 RecentlyUsedPageCaches (void) {}; 160 }; 161 162 163 164 165 class PageCacheManager::Deleter 166 { 167 public: 168 void operator() (PageCacheManager* pObject) { delete pObject; } 169 }; 170 171 172 173 //===== PageCacheManager ==================================================== 174 175 ::boost::weak_ptr<PageCacheManager> PageCacheManager::mpInstance; 176 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 198 PageCacheManager::PageCacheManager (void) 199 : mpPageCaches(new PageCacheContainer()), 200 mpRecentlyUsedPageCaches(new RecentlyUsedPageCaches()), 201 mnMaximalRecentlyCacheCount(2) 202 { 203 } 204 205 206 207 208 PageCacheManager::~PageCacheManager (void) 209 { 210 } 211 212 213 214 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 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 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 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 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 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 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 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 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 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