1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <config_features.h>
22 #if defined(MACOSX) && HAVE_FEATURE_READONLY_INSTALLSET
23 #define __ASSERT_MACROS_DEFINE_VERSIONS_WITHOUT_UNDERSCORES 0
25 #include <Foundation/Foundation.h>
29 #include "sal/config.h"
31 #include <comphelper/processfactory.hxx>
32 #include <comphelper/string.hxx>
33 #include <osl/thread.h>
34 #include <tools/vcompat.hxx>
35 #include <ucbhelper/content.hxx>
36 #include <unotools/ucbstreamhelper.hxx>
37 #include <unotools/pathoptions.hxx>
38 #include <sfx2/docfile.hxx>
39 #include "svx/gallery.hxx"
40 #include "gallery.hrc"
41 #include "svx/galmisc.hxx"
42 #include "svx/galtheme.hxx"
43 #include "svx/gallery1.hxx"
44 #include <com/sun/star/sdbc/XResultSet.hpp>
45 #include <com/sun/star/ucb/XContentAccess.hpp>
46 #include <boost/scoped_ptr.hpp>
50 using namespace ::com::sun::star
;
52 // - GalleryThemeEntry -
54 static bool FileExists( const INetURLObject
&rURL
, const rtl::OUString
&rExt
)
56 INetURLObject
aURL( rURL
);
57 aURL
.setExtension( rExt
);
58 return FileExists( aURL
);
61 GalleryThemeEntry::GalleryThemeEntry( bool bCreateUniqueURL
,
62 const INetURLObject
& rBaseURL
, const OUString
& rName
,
63 bool _bReadOnly
, bool _bNewFile
,
64 sal_uInt32 _nId
, bool _bThemeNameFromResource
) :
66 bReadOnly ( _bReadOnly
),
67 bThemeNameFromResource ( _bThemeNameFromResource
)
69 INetURLObject
aURL( rBaseURL
);
70 DBG_ASSERT( aURL
.GetProtocol() != INetProtocol::NotValid
, "invalid URL" );
74 INetURLObject
aBaseNoCase( ImplGetURLIgnoreCase( rBaseURL
) );
76 static sal_Int32 nIdx
= 0;
77 while( FileExists( aURL
, "thm" ) )
81 aURL
.setName( aURL
.getName() + OUString::number(nIdx
));
85 aURL
.setExtension( "thm" );
86 aThmURL
= ImplGetURLIgnoreCase( aURL
);
88 aURL
.setExtension( "sdg" );
89 aSdgURL
= ImplGetURLIgnoreCase( aURL
);
91 aURL
.setExtension( "sdv" );
92 aSdvURL
= ImplGetURLIgnoreCase( aURL
);
94 aURL
.setExtension( "str" );
95 aStrURL
= ImplGetURLIgnoreCase( aURL
);
97 SetModified( _bNewFile
);
99 aName
= ReadStrFromIni( "name" );
101 // This is awful - we shouldn't use these resources if we
102 // possibly can avoid them
103 if( aName
.isEmpty() && nId
&& bThemeNameFromResource
)
104 aName
= GAL_RESSTR( RID_GALLERYSTR_THEME_START
+ (sal_uInt16
) nId
);
106 if( aName
.isEmpty() )
110 INetURLObject
GalleryThemeEntry::ImplGetURLIgnoreCase( const INetURLObject
& rURL
)
112 INetURLObject
aURL( rURL
);
114 // check original file name
115 if( !FileExists( aURL
) )
117 // check upper case file name
118 aURL
.setName( aURL
.getName().toAsciiUpperCase() );
120 if(!FileExists( aURL
) )
122 // check lower case file name
123 aURL
.setName( aURL
.getName().toAsciiLowerCase() );
130 void GalleryThemeEntry::SetName( const OUString
& rNewName
)
132 if( aName
!= rNewName
)
136 bThemeNameFromResource
= false;
140 void GalleryThemeEntry::SetId( sal_uInt32 nNewId
, bool bResetThemeName
)
144 bThemeNameFromResource
= ( nId
&& bResetThemeName
);
147 // - GalleryThemeCacheEntry -
149 class GalleryThemeCacheEntry
153 const GalleryThemeEntry
* mpThemeEntry
;
154 GalleryTheme
* mpTheme
;
158 GalleryThemeCacheEntry( const GalleryThemeEntry
* pThemeEntry
, GalleryTheme
* pTheme
) :
159 mpThemeEntry( pThemeEntry
), mpTheme( pTheme
) {}
160 ~GalleryThemeCacheEntry() { delete mpTheme
; }
162 const GalleryThemeEntry
* GetThemeEntry() const { return mpThemeEntry
; }
163 GalleryTheme
* GetTheme() const { return mpTheme
; }
168 Gallery::Gallery( const OUString
& rMultiPath
)
169 : nReadTextEncoding ( osl_getThreadTextEncoding() )
170 , bMultiPath ( false )
172 ImplLoad( rMultiPath
);
178 for ( size_t i
= 0, n
= aThemeList
.size(); i
< n
; ++i
)
179 delete aThemeList
[ i
];
183 Gallery
* Gallery::GetGalleryInstance()
185 static Gallery
* pGallery
= NULL
;
189 ::osl::MutexGuard
aGuard( ::osl::Mutex::getGlobalMutex() );
192 pGallery
= new Gallery( SvtPathOptions().GetGalleryPath() );
199 void Gallery::ImplLoad( const OUString
& rMultiPath
)
201 const sal_Int32 nTokenCount
= comphelper::string::getTokenCount(rMultiPath
, ';');
204 bMultiPath
= ( nTokenCount
> 0 );
206 INetURLObject
aCurURL(SvtPathOptions().GetConfigPath());
207 ImplLoadSubDirs( aCurURL
, bIsReadOnlyDir
);
209 if( !bIsReadOnlyDir
)
214 aRelURL
= INetURLObject( rMultiPath
.getToken(0, ';') );
216 for( sal_Int32 i
= 0; i
< nTokenCount
; ++i
)
218 aCurURL
= INetURLObject(rMultiPath
.getToken(i
, ';'));
220 ImplLoadSubDirs( aCurURL
, bIsReadOnlyDir
);
222 if( !bIsReadOnlyDir
)
227 aRelURL
= INetURLObject( rMultiPath
);
229 DBG_ASSERT( aUserURL
.GetProtocol() != INetProtocol::NotValid
, "no writable Gallery user directory available" );
230 DBG_ASSERT( aRelURL
.GetProtocol() != INetProtocol::NotValid
, "invalid URL" );
233 void Gallery::ImplLoadSubDirs( const INetURLObject
& rBaseURL
, bool& rbDirIsReadOnly
)
235 rbDirIsReadOnly
= false;
239 uno::Reference
< ucb::XCommandEnvironment
> xEnv
;
240 ::ucbhelper::Content
aCnt( rBaseURL
.GetMainURL( INetURLObject::NO_DECODE
), xEnv
, comphelper::getProcessComponentContext() );
242 uno::Sequence
< OUString
> aProps( 1 );
243 aProps
.getArray()[ 0 ] = "Url";
245 uno::Reference
< sdbc::XResultSet
> xResultSet( aCnt
.createCursor( aProps
, ::ucbhelper::INCLUDE_DOCUMENTS_ONLY
) );
247 #if defined(MACOSX) && HAVE_FEATURE_READONLY_INSTALLSET
248 if( rBaseURL
.GetProtocol() == INetProtocol::File
)
250 const char *appBundle
= [[[NSBundle mainBundle
] bundlePath
] UTF8String
];
251 OUString path
= rBaseURL
.GetURLPath();
252 if( path
.startsWith( OUString( appBundle
, strlen( appBundle
), RTL_TEXTENCODING_UTF8
) + "/" ) )
253 rbDirIsReadOnly
= true;
258 // check readonlyness the very hard way
259 INetURLObject
aTestURL( rBaseURL
);
260 OUString
aTestFile( "cdefghij.klm" );
262 aTestURL
.Append( aTestFile
);
263 boost::scoped_ptr
<SvStream
> pTestStm(::utl::UcbStreamHelper::CreateStream( aTestURL
.GetMainURL( INetURLObject::NO_DECODE
), StreamMode::WRITE
));
267 pTestStm
->WriteInt32( sal_Int32(1) );
269 if( pTestStm
->GetError() )
270 rbDirIsReadOnly
= true;
273 KillFile( aTestURL
);
276 rbDirIsReadOnly
= true;
278 catch( const ucb::ContentCreationException
& )
281 catch( const uno::RuntimeException
& )
284 catch( const uno::Exception
& )
288 if( xResultSet
.is() )
290 uno::Reference
< ucb::XContentAccess
> xContentAccess( xResultSet
, uno::UNO_QUERY
);
292 if( xContentAccess
.is() )
294 static const char s_sTitle
[] = "Title";
295 static const char s_sIsReadOnly
[] = "IsReadOnly";
296 static const char s_sSDG_EXT
[] = "sdg";
297 static const char s_sSDV_EXT
[] = "sdv";
299 while( xResultSet
->next() )
301 INetURLObject
aThmURL( xContentAccess
->queryContentIdentifierString() );
303 if(aThmURL
.GetExtension().equalsIgnoreAsciiCase("thm"))
305 INetURLObject
aSdgURL( aThmURL
); aSdgURL
.SetExtension( s_sSDG_EXT
);
306 INetURLObject
aSdvURL( aThmURL
); aSdvURL
.SetExtension( s_sSDV_EXT
);
311 ::ucbhelper::Content
aThmCnt( aThmURL
.GetMainURL( INetURLObject::NO_DECODE
), xEnv
, comphelper::getProcessComponentContext() );
312 ::ucbhelper::Content
aSdgCnt( aSdgURL
.GetMainURL( INetURLObject::NO_DECODE
), xEnv
, comphelper::getProcessComponentContext() );
313 ::ucbhelper::Content
aSdvCnt( aSdvURL
.GetMainURL( INetURLObject::NO_DECODE
), xEnv
, comphelper::getProcessComponentContext() );
317 aThmCnt
.getPropertyValue( s_sTitle
) >>= aTitle
;
319 catch( const uno::RuntimeException
& )
322 catch( const uno::Exception
& )
326 if( !aTitle
.isEmpty() )
328 bool bReadOnly
= false;
332 aThmCnt
.getPropertyValue( s_sIsReadOnly
) >>= bReadOnly
;
334 catch( const uno::RuntimeException
& )
337 catch( const uno::Exception
& )
345 aSdgCnt
.getPropertyValue( s_sTitle
) >>= aTitle
;
347 catch( const ::com::sun::star::uno::RuntimeException
& )
350 catch( const ::com::sun::star::uno::Exception
& )
354 if( !aTitle
.isEmpty() )
358 aSdgCnt
.getPropertyValue( s_sIsReadOnly
) >>= bReadOnly
;
360 catch( const uno::RuntimeException
& )
363 catch( const uno::Exception
& )
373 aSdvCnt
.getPropertyValue( s_sTitle
) >>= aTitle
;
375 catch( const ::com::sun::star::uno::RuntimeException
& )
378 catch( const ::com::sun::star::uno::Exception
& )
382 if( !aTitle
.isEmpty() )
386 aSdvCnt
.getPropertyValue( s_sIsReadOnly
) >>= bReadOnly
;
388 catch( const uno::RuntimeException
& )
391 catch( const uno::Exception
& )
397 GalleryThemeEntry
* pEntry
= GalleryTheme::CreateThemeEntry( aThmURL
, rbDirIsReadOnly
|| bReadOnly
);
400 aThemeList
.push_back( pEntry
);
403 catch( const ucb::ContentCreationException
& )
406 catch( const uno::RuntimeException
& )
409 catch( const uno::Exception
& )
417 catch( const ucb::ContentCreationException
& )
420 catch( const uno::RuntimeException
& )
423 catch( const uno::Exception
& )
428 GalleryThemeEntry
* Gallery::ImplGetThemeEntry( const OUString
& rThemeName
)
430 GalleryThemeEntry
* pFound
= NULL
;
432 if( !rThemeName
.isEmpty() )
434 for ( size_t i
= 0, n
= aThemeList
.size(); i
< n
&& !pFound
; ++i
)
435 if( rThemeName
== aThemeList
[ i
]->GetThemeName() )
436 pFound
= aThemeList
[ i
];
442 OUString
Gallery::GetThemeName( sal_uIntPtr nThemeId
) const
444 GalleryThemeEntry
* pFound
= NULL
;
446 for ( size_t i
= 0, n
= aThemeList
.size(); i
< n
&& !pFound
; ++i
)
448 GalleryThemeEntry
* pEntry
= aThemeList
[ i
];
449 if( nThemeId
== pEntry
->GetId() )
453 // try fallback, if no entry was found
460 case( GALLERY_THEME_3D
):
461 aFallback
= GAL_RESSTR(RID_GALLERYSTR_THEME_3D
);
463 case( GALLERY_THEME_BULLETS
):
464 aFallback
= GAL_RESSTR(RID_GALLERYSTR_THEME_BULLETS
);
466 case( GALLERY_THEME_HOMEPAGE
):
467 aFallback
= GAL_RESSTR(RID_GALLERYSTR_THEME_HOMEPAGE
);
469 case( GALLERY_THEME_POWERPOINT
):
470 aFallback
= GAL_RESSTR(RID_GALLERYSTR_THEME_POWERPOINT
);
472 case( GALLERY_THEME_FONTWORK
):
473 aFallback
= GAL_RESSTR(RID_GALLERYSTR_THEME_FONTWORK
);
475 case( GALLERY_THEME_FONTWORK_VERTICAL
):
476 aFallback
= GAL_RESSTR(RID_GALLERYSTR_THEME_FONTWORK_VERTICAL
);
478 case( GALLERY_THEME_SOUNDS
):
479 aFallback
= GAL_RESSTR(RID_GALLERYSTR_THEME_SOUNDS
);
481 case( RID_GALLERYSTR_THEME_ARROWS
):
482 case( RID_GALLERYSTR_THEME_COMPUTERS
):
483 case( RID_GALLERYSTR_THEME_DIAGRAMS
):
484 case( RID_GALLERYSTR_THEME_EDUCATION
):
485 case( RID_GALLERYSTR_THEME_ENVIRONMENT
):
486 case( RID_GALLERYSTR_THEME_FINANCE
):
487 case( RID_GALLERYSTR_THEME_PEOPLE
):
488 case( RID_GALLERYSTR_THEME_SYMBOLS
):
489 case( RID_GALLERYSTR_THEME_TRANSPORT
):
490 case( RID_GALLERYSTR_THEME_TXTSHAPES
):
491 aFallback
= GAL_RESSTR(static_cast<sal_uInt32
>(nThemeId
));
497 pFound
= const_cast<Gallery
*>(this)->ImplGetThemeEntry(aFallback
);
500 return( pFound
? pFound
->GetThemeName() : OUString() );
503 bool Gallery::HasTheme( const OUString
& rThemeName
)
505 return( ImplGetThemeEntry( rThemeName
) != NULL
);
508 bool Gallery::CreateTheme( const OUString
& rThemeName
)
512 if( !HasTheme( rThemeName
) && ( GetUserURL().GetProtocol() != INetProtocol::NotValid
) )
514 INetURLObject
aURL( GetUserURL() );
515 aURL
.Append( rThemeName
);
516 GalleryThemeEntry
* pNewEntry
= new GalleryThemeEntry(
517 true, aURL
, rThemeName
,
518 false, true, 0, false );
520 aThemeList
.push_back( pNewEntry
);
521 delete( new GalleryTheme( this, pNewEntry
) );
522 Broadcast( GalleryHint( GalleryHintType::THEME_CREATED
, rThemeName
) );
529 bool Gallery::RenameTheme( const OUString
& rOldName
, const OUString
& rNewName
)
531 GalleryThemeEntry
* pThemeEntry
= ImplGetThemeEntry( rOldName
);
534 // check if the new theme name is already present
535 if( pThemeEntry
&& !HasTheme( rNewName
) && !pThemeEntry
->IsReadOnly() )
537 SfxListener aListener
;
538 GalleryTheme
* pThm
= AcquireTheme( rOldName
, aListener
);
542 const OUString
aOldName( rOldName
);
544 pThemeEntry
->SetName( rNewName
);
547 Broadcast( GalleryHint( GalleryHintType::THEME_RENAMED
, aOldName
, pThm
->GetName() ) );
548 ReleaseTheme( pThm
, aListener
);
556 bool Gallery::RemoveTheme( const OUString
& rThemeName
)
558 GalleryThemeEntry
* pThemeEntry
= ImplGetThemeEntry( rThemeName
);
561 if( pThemeEntry
&& !pThemeEntry
->IsReadOnly() )
563 Broadcast( GalleryHint( GalleryHintType::CLOSE_THEME
, rThemeName
) );
565 SfxListener aListener
;
566 GalleryTheme
* pThm
= AcquireTheme( rThemeName
, aListener
);
570 INetURLObject
aThmURL( pThm
->GetThmURL() );
571 INetURLObject
aSdgURL( pThm
->GetSdgURL() );
572 INetURLObject
aSdvURL( pThm
->GetSdvURL() );
573 INetURLObject
aStrURL( pThm
->GetSdvURL() );
575 ReleaseTheme( pThm
, aListener
);
583 for ( GalleryThemeList::iterator it
= aThemeList
.begin(); it
!= aThemeList
.end(); ++it
)
585 if ( pThemeEntry
== *it
) {
587 aThemeList
.erase( it
);
592 Broadcast( GalleryHint( GalleryHintType::THEME_REMOVED
, rThemeName
) );
600 GalleryTheme
* Gallery::ImplGetCachedTheme(const GalleryThemeEntry
* pThemeEntry
)
602 GalleryTheme
* pTheme
= NULL
;
606 for (GalleryCacheThemeList::const_iterator it
= aThemeCache
.begin(); it
!= aThemeCache
.end(); ++it
)
608 if (pThemeEntry
== (*it
)->GetThemeEntry())
610 pTheme
= (*it
)->GetTheme();
617 INetURLObject aURL
= pThemeEntry
->GetThmURL();
619 DBG_ASSERT( aURL
.GetProtocol() != INetProtocol::NotValid
, "invalid URL" );
621 if( FileExists( aURL
) )
623 boost::scoped_ptr
<SvStream
> pIStm(::utl::UcbStreamHelper::CreateStream( aURL
.GetMainURL( INetURLObject::NO_DECODE
), StreamMode::READ
));
629 pTheme
= new GalleryTheme( this, const_cast<GalleryThemeEntry
*>(pThemeEntry
) );
630 ReadGalleryTheme( *pIStm
, *pTheme
);
632 if( pIStm
->GetError() )
634 delete pTheme
, pTheme
= NULL
;
637 catch (const css::ucb::ContentCreationException
&)
644 aThemeCache
.push_back( new GalleryThemeCacheEntry( pThemeEntry
, pTheme
));
651 void Gallery::ImplDeleteCachedTheme( GalleryTheme
* pTheme
)
653 for (GalleryCacheThemeList::iterator it
= aThemeCache
.begin(); it
!= aThemeCache
.end(); ++it
)
655 if (pTheme
== (*it
)->GetTheme())
658 aThemeCache
.erase(it
);
664 GalleryTheme
* Gallery::AcquireTheme( const OUString
& rThemeName
, SfxListener
& rListener
)
666 GalleryTheme
* pTheme
= NULL
;
667 GalleryThemeEntry
* pThemeEntry
= ImplGetThemeEntry( rThemeName
);
669 if( pThemeEntry
&& ( ( pTheme
= ImplGetCachedTheme( pThemeEntry
) ) != NULL
) )
670 rListener
.StartListening( *pTheme
);
675 void Gallery::ReleaseTheme( GalleryTheme
* pTheme
, SfxListener
& rListener
)
679 rListener
.EndListening( *pTheme
);
681 if( !pTheme
->HasListeners() )
682 ImplDeleteCachedTheme( pTheme
);
686 bool GalleryThemeEntry::IsDefault() const
688 return ( nId
> 0 ) && ( nId
!= ( RID_GALLERYSTR_THEME_MYTHEME
- RID_GALLERYSTR_THEME_START
) );
691 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */