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 
GetPathToImpressConfigurationRoot(void)58 static const OUString& GetPathToImpressConfigurationRoot (void)
59 {
60     static const OUString sPathToImpressConfigurationRoot (
61         RTL_CONSTASCII_USTRINGPARAM("/org.openoffice.Office.Impress/"));
62     return sPathToImpressConfigurationRoot;
63 }
GetPathToSetNode(void)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;
Descriptor(const::rtl::OUString & rsURL,const::rtl::OUString & rsName)79     Descriptor (const ::rtl::OUString& rsURL, const ::rtl::OUString& rsName)
80         : msURL(rsURL),
81           msName(rsName),
82           maToken(::sd::sidebar::MasterPageContainer::NIL_TOKEN)
83     {}
Descriptor(::sd::sidebar::MasterPageContainer::Token aToken,const::rtl::OUString & rsURL,const::rtl::OUString & rsName)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:
TokenComparator(::sd::sidebar::MasterPageContainer::Token aToken)92         TokenComparator(::sd::sidebar::MasterPageContainer::Token aToken)
93             : maToken(aToken) {}
operator ()(const Descriptor & rDescriptor)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:
MasterPageList(void)110     MasterPageList (void) {}
111 };
112 
113 
114 RecentlyUsedMasterPages* RecentlyUsedMasterPages::mpInstance = NULL;
115 
116 
Instance(void)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 
RecentlyUsedMasterPages(void)143 RecentlyUsedMasterPages::RecentlyUsedMasterPages (void)
144     : maListeners(),
145       mpMasterPages(new MasterPageList()),
146       mnMaxListSize(8),
147       mpContainer(new MasterPageContainer())
148 {
149 }
150 
151 
152 
153 
~RecentlyUsedMasterPages(void)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 
LateInit(void)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 
LoadPersistentValues(void)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 
SavePersistentValues(void)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 
AddEventListener(const Link & rEventListener)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 
RemoveEventListener(const Link & rEventListener)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 
GetMasterPageCount(void) const343 int RecentlyUsedMasterPages::GetMasterPageCount (void) const
344 {
345     return mpMasterPages->size();
346 }
347 
348 
349 
350 
GetTokenForIndex(sal_uInt32 nIndex) const351 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 
SendEvent(void)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 
IMPL_LINK(RecentlyUsedMasterPages,MasterPageChangeListener,MasterPageObserverEvent *,pEvent)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 
IMPL_LINK(RecentlyUsedMasterPages,MasterPageContainerChangeListener,MasterPageContainerChangeEvent *,pEvent)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 
AddMasterPage(MasterPageContainer::Token aToken,bool bMakePersistent)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 
ResolveList(void)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