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