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 #include "precompiled_sd.hxx" 23 24 #include "RecentlyUsedMasterPages.hxx" 25 #include "MasterPageObserver.hxx" 26 #include "MasterPagesSelector.hxx" 27 #include "MasterPageDescriptor.hxx" 28 #include "tools/ConfigurationAccess.hxx" 29 #include "drawdoc.hxx" 30 #include "sdpage.hxx" 31 32 #include <algorithm> 33 #include <vector> 34 35 #include <comphelper/processfactory.hxx> 36 #include "unomodel.hxx" 37 #include <com/sun/star/drawing/XDrawPagesSupplier.hpp> 38 #include <com/sun/star/drawing/XDrawPages.hpp> 39 #include <com/sun/star/frame/XComponentLoader.hpp> 40 #include <com/sun/star/container/XNameAccess.hpp> 41 #include <com/sun/star/container/XHierarchicalNameAccess.hpp> 42 #include <com/sun/star/lang/XMultiServiceFactory.hpp> 43 #include <com/sun/star/beans/PropertyValue.hpp> 44 #include <com/sun/star/beans/PropertyState.hpp> 45 #include <tools/urlobj.hxx> 46 #include <unotools/confignode.hxx> 47 #include <osl/doublecheckedlocking.h> 48 #include <osl/getglobalmutex.hxx> 49 50 using namespace ::std; 51 using ::rtl::OUString; 52 using namespace ::com::sun::star; 53 using namespace ::com::sun::star::uno; 54 55 56 namespace { 57 58 static const OUString& GetPathToImpressConfigurationRoot (void) 59 { 60 static const OUString sPathToImpressConfigurationRoot ( 61 RTL_CONSTASCII_USTRINGPARAM("/org.openoffice.Office.Impress/")); 62 return sPathToImpressConfigurationRoot; 63 } 64 static const OUString& GetPathToSetNode (void) 65 { 66 static const OUString sPathToSetNode( 67 RTL_CONSTASCII_USTRINGPARAM( 68 "MultiPaneGUI/ToolPanel/RecentlyUsedMasterPages")); 69 return sPathToSetNode; 70 } 71 72 73 class Descriptor 74 { 75 public: 76 ::rtl::OUString msURL; 77 ::rtl::OUString msName; 78 ::sd::sidebar::MasterPageContainer::Token maToken; 79 Descriptor (const ::rtl::OUString& rsURL, const ::rtl::OUString& rsName) 80 : msURL(rsURL), 81 msName(rsName), 82 maToken(::sd::sidebar::MasterPageContainer::NIL_TOKEN) 83 {} 84 Descriptor (::sd::sidebar::MasterPageContainer::Token aToken, 85 const ::rtl::OUString& rsURL, const ::rtl::OUString& rsName) 86 : msURL(rsURL), 87 msName(rsName), 88 maToken(aToken) 89 {} 90 class TokenComparator 91 { public: 92 TokenComparator(::sd::sidebar::MasterPageContainer::Token aToken) 93 : maToken(aToken) {} 94 bool operator () (const Descriptor& rDescriptor) 95 { return maToken==rDescriptor.maToken; } 96 private: ::sd::sidebar::MasterPageContainer::Token maToken; 97 }; 98 }; 99 100 } // end of anonymous namespace 101 102 103 104 105 namespace sd { namespace sidebar { 106 107 class RecentlyUsedMasterPages::MasterPageList : public ::std::vector<Descriptor> 108 { 109 public: 110 MasterPageList (void) {} 111 }; 112 113 114 RecentlyUsedMasterPages* RecentlyUsedMasterPages::mpInstance = NULL; 115 116 117 RecentlyUsedMasterPages& RecentlyUsedMasterPages::Instance (void) 118 { 119 if (mpInstance == NULL) 120 { 121 ::osl::GetGlobalMutex aMutexFunctor; 122 ::osl::MutexGuard aGuard (aMutexFunctor()); 123 if (mpInstance == NULL) 124 { 125 RecentlyUsedMasterPages* pInstance = new RecentlyUsedMasterPages(); 126 pInstance->LateInit(); 127 SdGlobalResourceContainer::Instance().AddResource ( 128 ::std::auto_ptr<SdGlobalResource>(pInstance)); 129 OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER(); 130 mpInstance = pInstance; 131 } 132 } 133 else { 134 OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER(); 135 } 136 137 return *mpInstance; 138 } 139 140 141 142 143 RecentlyUsedMasterPages::RecentlyUsedMasterPages (void) 144 : maListeners(), 145 mpMasterPages(new MasterPageList()), 146 mnMaxListSize(8), 147 mpContainer(new MasterPageContainer()) 148 { 149 } 150 151 152 153 154 RecentlyUsedMasterPages::~RecentlyUsedMasterPages (void) 155 { 156 Link aLink (LINK(this,RecentlyUsedMasterPages,MasterPageContainerChangeListener)); 157 mpContainer->RemoveChangeListener(aLink); 158 159 MasterPageObserver::Instance().RemoveEventListener( 160 LINK(this,RecentlyUsedMasterPages,MasterPageChangeListener)); 161 } 162 163 164 165 166 void RecentlyUsedMasterPages::LateInit (void) 167 { 168 Link aLink (LINK(this,RecentlyUsedMasterPages,MasterPageContainerChangeListener)); 169 mpContainer->AddChangeListener(aLink); 170 171 LoadPersistentValues (); 172 MasterPageObserver::Instance().AddEventListener( 173 LINK(this,RecentlyUsedMasterPages,MasterPageChangeListener)); 174 } 175 176 177 178 179 void RecentlyUsedMasterPages::LoadPersistentValues (void) 180 { 181 try 182 { 183 do 184 { 185 tools::ConfigurationAccess aConfiguration ( 186 GetPathToImpressConfigurationRoot(), 187 tools::ConfigurationAccess::READ_ONLY); 188 Reference<container::XNameAccess> xSet ( 189 aConfiguration.GetConfigurationNode(GetPathToSetNode()), 190 UNO_QUERY); 191 if ( ! xSet.is()) 192 break; 193 194 const String sURLMemberName (OUString::createFromAscii("URL")); 195 const String sNameMemberName (OUString::createFromAscii("Name")); 196 OUString sURL; 197 OUString sName; 198 199 // Read the names and URLs of the master pages. 200 Sequence<OUString> aKeys (xSet->getElementNames()); 201 mpMasterPages->clear(); 202 mpMasterPages->reserve(aKeys.getLength()); 203 for (int i=0; i<aKeys.getLength(); i++) 204 { 205 Reference<container::XNameAccess> xSetItem ( 206 xSet->getByName(aKeys[i]), UNO_QUERY); 207 if (xSetItem.is()) 208 { 209 Any aURL (xSetItem->getByName(sURLMemberName)); 210 Any aName (xSetItem->getByName(sNameMemberName)); 211 aURL >>= sURL; 212 aName >>= sName; 213 SharedMasterPageDescriptor pDescriptor (new MasterPageDescriptor( 214 MasterPageContainer::TEMPLATE, 215 -1, 216 sURL, 217 String(), 218 sName, 219 false, 220 ::boost::shared_ptr<PageObjectProvider>( 221 new TemplatePageObjectProvider(sURL)), 222 ::boost::shared_ptr<PreviewProvider>( 223 new TemplatePreviewProvider(sURL)))); 224 // For user supplied templates we use a different 225 // preview provider: The preview in the document shows 226 // not only shapes on the master page but also shapes on 227 // the foreground. This is misleading and therefore 228 // these previews are discarded and created directly 229 // from the page objects. 230 if (pDescriptor->GetURLClassification() == MasterPageDescriptor::URLCLASS_USER) 231 pDescriptor->mpPreviewProvider = ::boost::shared_ptr<PreviewProvider>( 232 new PagePreviewProvider()); 233 MasterPageContainer::Token aToken (mpContainer->PutMasterPage(pDescriptor)); 234 mpMasterPages->push_back(Descriptor(aToken,sURL,sName)); 235 } 236 } 237 238 ResolveList(); 239 } 240 while (false); 241 } 242 catch (Exception&) 243 { 244 // Ignore exception. 245 } 246 } 247 248 249 250 251 void RecentlyUsedMasterPages::SavePersistentValues (void) 252 { 253 try 254 { 255 do 256 { 257 tools::ConfigurationAccess aConfiguration ( 258 GetPathToImpressConfigurationRoot(), 259 tools::ConfigurationAccess::READ_WRITE); 260 Reference<container::XNameContainer> xSet ( 261 aConfiguration.GetConfigurationNode(GetPathToSetNode()), 262 UNO_QUERY); 263 if ( ! xSet.is()) 264 break; 265 266 // Clear the set. 267 Sequence<OUString> aKeys (xSet->getElementNames()); 268 sal_Int32 i; 269 for (i=0; i<aKeys.getLength(); i++) 270 xSet->removeByName (aKeys[i]); 271 272 // Fill it with the URLs of this object. 273 const String sURLMemberName (OUString::createFromAscii("URL")); 274 const String sNameMemberName (OUString::createFromAscii("Name")); 275 Any aValue; 276 Reference<lang::XSingleServiceFactory> xChildFactory ( 277 xSet, UNO_QUERY); 278 if ( ! xChildFactory.is()) 279 break; 280 MasterPageList::const_iterator iDescriptor; 281 sal_Int32 nIndex(0); 282 for (iDescriptor=mpMasterPages->begin(); 283 iDescriptor!=mpMasterPages->end(); 284 ++iDescriptor,++nIndex) 285 { 286 // Create new child. 287 OUString sKey (OUString::createFromAscii("index_")); 288 sKey += OUString::valueOf(nIndex); 289 Reference<container::XNameReplace> xChild( 290 xChildFactory->createInstance(), UNO_QUERY); 291 if (xChild.is()) 292 { 293 xSet->insertByName (sKey, makeAny(xChild)); 294 295 aValue <<= OUString(iDescriptor->msURL); 296 xChild->replaceByName (sURLMemberName, aValue); 297 298 aValue <<= OUString(iDescriptor->msName); 299 xChild->replaceByName (sNameMemberName, aValue); 300 } 301 } 302 303 // Write the data back to disk. 304 aConfiguration.CommitChanges(); 305 } 306 while (false); 307 } 308 catch (Exception&) 309 { 310 // Ignore exception. 311 } 312 } 313 314 315 316 317 void RecentlyUsedMasterPages::AddEventListener (const Link& rEventListener) 318 { 319 if (::std::find ( 320 maListeners.begin(), 321 maListeners.end(), 322 rEventListener) == maListeners.end()) 323 { 324 maListeners.push_back (rEventListener); 325 } 326 } 327 328 329 330 331 void RecentlyUsedMasterPages::RemoveEventListener (const Link& rEventListener) 332 { 333 maListeners.erase ( 334 ::std::find ( 335 maListeners.begin(), 336 maListeners.end(), 337 rEventListener)); 338 } 339 340 341 342 343 int RecentlyUsedMasterPages::GetMasterPageCount (void) const 344 { 345 return mpMasterPages->size(); 346 } 347 348 349 350 351 MasterPageContainer::Token RecentlyUsedMasterPages::GetTokenForIndex (sal_uInt32 nIndex) const 352 { 353 if(nIndex<mpMasterPages->size()) 354 return (*mpMasterPages)[nIndex].maToken; 355 else 356 return MasterPageContainer::NIL_TOKEN; 357 } 358 359 360 361 362 void RecentlyUsedMasterPages::SendEvent (void) 363 { 364 ::std::vector<Link>::iterator aLink (maListeners.begin()); 365 ::std::vector<Link>::iterator aEnd (maListeners.end()); 366 while (aLink!=aEnd) 367 { 368 aLink->Call (NULL); 369 ++aLink; 370 } 371 } 372 373 374 375 376 IMPL_LINK(RecentlyUsedMasterPages, MasterPageChangeListener, 377 MasterPageObserverEvent*, pEvent) 378 { 379 switch (pEvent->meType) 380 { 381 case MasterPageObserverEvent::ET_MASTER_PAGE_ADDED: 382 case MasterPageObserverEvent::ET_MASTER_PAGE_EXISTS: 383 AddMasterPage( 384 mpContainer->GetTokenForStyleName(pEvent->mrMasterPageName)); 385 break; 386 387 case MasterPageObserverEvent::ET_MASTER_PAGE_REMOVED: 388 // Do not change the list of recently master pages (the deleted 389 // page was recently used) but tell the listeners. They may want 390 // to update their lists. 391 SendEvent(); 392 break; 393 } 394 return 0; 395 } 396 397 398 399 400 IMPL_LINK(RecentlyUsedMasterPages, MasterPageContainerChangeListener, 401 MasterPageContainerChangeEvent*, pEvent) 402 { 403 if (pEvent != NULL) 404 switch (pEvent->meEventType) 405 { 406 case MasterPageContainerChangeEvent::CHILD_ADDED: 407 case MasterPageContainerChangeEvent::CHILD_REMOVED: 408 case MasterPageContainerChangeEvent::INDEX_CHANGED: 409 case MasterPageContainerChangeEvent::INDEXES_CHANGED: 410 ResolveList(); 411 break; 412 413 default: 414 // Ignored. 415 break; 416 } 417 return 0; 418 } 419 420 421 422 423 void RecentlyUsedMasterPages::AddMasterPage ( 424 MasterPageContainer::Token aToken, 425 bool bMakePersistent) 426 { 427 // For the page to be inserted the token has to be valid and the page 428 // has to have a valid URL. This excludes master pages that do not come 429 // from template files. 430 if (aToken != MasterPageContainer::NIL_TOKEN 431 && mpContainer->GetURLForToken(aToken).Len()>0) 432 { 433 434 MasterPageList::iterator aIterator ( 435 ::std::find_if(mpMasterPages->begin(),mpMasterPages->end(), 436 Descriptor::TokenComparator(aToken))); 437 if (aIterator != mpMasterPages->end()) 438 { 439 // When an entry for the given token already exists then remove 440 // it now and insert it later at the head of the list. 441 mpMasterPages->erase (aIterator); 442 } 443 444 mpMasterPages->insert(mpMasterPages->begin(), 445 Descriptor( 446 aToken, 447 mpContainer->GetURLForToken(aToken), 448 mpContainer->GetStyleNameForToken(aToken))); 449 450 // Shorten list to maximal size. 451 while (mpMasterPages->size() > mnMaxListSize) 452 { 453 mpMasterPages->pop_back (); 454 } 455 456 if (bMakePersistent) 457 SavePersistentValues (); 458 SendEvent(); 459 } 460 } 461 462 463 464 465 void RecentlyUsedMasterPages::ResolveList (void) 466 { 467 bool bNotify (false); 468 469 MasterPageList::iterator iDescriptor; 470 for (iDescriptor=mpMasterPages->begin(); iDescriptor!=mpMasterPages->end(); ++iDescriptor) 471 { 472 if (iDescriptor->maToken == MasterPageContainer::NIL_TOKEN) 473 { 474 MasterPageContainer::Token aToken (mpContainer->GetTokenForURL(iDescriptor->msURL)); 475 iDescriptor->maToken = aToken; 476 if (aToken != MasterPageContainer::NIL_TOKEN) 477 bNotify = true; 478 } 479 else 480 { 481 if ( ! mpContainer->HasToken(iDescriptor->maToken)) 482 { 483 iDescriptor->maToken = MasterPageContainer::NIL_TOKEN; 484 bNotify = true; 485 } 486 } 487 } 488 489 if (bNotify) 490 SendEvent(); 491 } 492 493 494 } } // end of namespace sd::sidebar 495