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/configmgr.hxx>
37 #include <unotools/ucbstreamhelper.hxx>
38 #include <unotools/pathoptions.hxx>
39 #include <sfx2/docfile.hxx>
40 #include "svx/gallery.hxx"
41 #include "gallery.hrc"
42 #include "strings.hxx"
43 #include "svx/galmisc.hxx"
44 #include "svx/galtheme.hxx"
45 #include "svx/gallery1.hxx"
46 #include <com/sun/star/sdbc/XResultSet.hpp>
47 #include <com/sun/star/ucb/XContentAccess.hpp>
51 using namespace ::com::sun::star
;
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
)
105 //some of these are supposed to *not* be localized
106 //so catch them before looking up the resource
107 const std::pair
<sal_uInt16
, const char*> aUnlocalized
[] =
109 { GALLERY_THEME_HOMEPAGE
, RID_GALLERYSTR_THEME_HTMLBUTTONS
},
110 { GALLERY_THEME_POWERPOINT
, RID_GALLERYSTR_THEME_POWERPOINT
},
111 { GALLERY_THEME_USERSOUNDS
, RID_GALLERYSTR_THEME_USERSOUNDS
},
112 { GALLERY_THEME_DUMMY5
, RID_GALLERYSTR_THEME_DUMMY5
},
113 { GALLERY_THEME_FONTWORK
, RID_GALLERYSTR_THEME_FONTWORK
},
114 { GALLERY_THEME_FONTWORK_VERTICAL
, RID_GALLERYSTR_THEME_FONTWORK_VERTICAL
}
116 for (size_t i
= 0; i
< SAL_N_ELEMENTS(aUnlocalized
); ++i
)
118 if (aUnlocalized
[i
].first
== nId
)
120 aName
= OUString::createFromAscii(aUnlocalized
[i
].second
);
124 //look up the rest of the ids in string resources
126 aName
= GalResId(RID_GALLERYSTR_THEME_START
+ (sal_uInt16
) nId
);
129 if( aName
.isEmpty() )
133 INetURLObject
GalleryThemeEntry::ImplGetURLIgnoreCase( const INetURLObject
& rURL
)
135 INetURLObject
aURL( rURL
);
137 // check original file name
138 if( !FileExists( aURL
) )
140 // check upper case file name
141 aURL
.setName( aURL
.getName().toAsciiUpperCase() );
143 if(!FileExists( aURL
) )
145 // check lower case file name
146 aURL
.setName( aURL
.getName().toAsciiLowerCase() );
153 void GalleryThemeEntry::SetName( const OUString
& rNewName
)
155 if( aName
!= rNewName
)
159 bThemeNameFromResource
= false;
163 void GalleryThemeEntry::SetId( sal_uInt32 nNewId
, bool bResetThemeName
)
167 bThemeNameFromResource
= ( nId
&& bResetThemeName
);
171 class GalleryThemeCacheEntry
175 const GalleryThemeEntry
* mpThemeEntry
;
176 std::unique_ptr
<GalleryTheme
> mpTheme
;
180 GalleryThemeCacheEntry( const GalleryThemeEntry
* pThemeEntry
, GalleryTheme
* pTheme
) :
181 mpThemeEntry( pThemeEntry
), mpTheme( pTheme
) {}
183 const GalleryThemeEntry
* GetThemeEntry() const { return mpThemeEntry
; }
184 GalleryTheme
* GetTheme() const { return mpTheme
.get(); }
188 Gallery::Gallery( const OUString
& rMultiPath
)
189 : bMultiPath ( false )
191 ImplLoad( rMultiPath
);
197 for (GalleryThemeEntry
* p
: aThemeList
)
202 Gallery
* Gallery::GetGalleryInstance()
204 static Gallery
* s_pGallery
= nullptr;
208 ::osl::MutexGuard
aGuard( ::osl::Mutex::getGlobalMutex() );
209 if (!s_pGallery
&& !utl::ConfigManager::IsAvoidConfig())
211 s_pGallery
= new Gallery( SvtPathOptions().GetGalleryPath() );
218 void Gallery::ImplLoad( const OUString
& rMultiPath
)
220 const sal_Int32 nTokenCount
= comphelper::string::getTokenCount(rMultiPath
, ';');
223 bMultiPath
= ( nTokenCount
> 0 );
225 INetURLObject
aCurURL(SvtPathOptions().GetConfigPath());
226 ImplLoadSubDirs( aCurURL
, bIsReadOnlyDir
);
228 if( !bIsReadOnlyDir
)
233 aRelURL
= INetURLObject( rMultiPath
.getToken(0, ';') );
235 for( sal_Int32 i
= 0; i
< nTokenCount
; ++i
)
237 aCurURL
= INetURLObject(rMultiPath
.getToken(i
, ';'));
239 ImplLoadSubDirs( aCurURL
, bIsReadOnlyDir
);
241 if( !bIsReadOnlyDir
)
246 aRelURL
= INetURLObject( rMultiPath
);
248 DBG_ASSERT( aUserURL
.GetProtocol() != INetProtocol::NotValid
, "no writable Gallery user directory available" );
249 DBG_ASSERT( aRelURL
.GetProtocol() != INetProtocol::NotValid
, "invalid URL" );
252 void Gallery::ImplLoadSubDirs( const INetURLObject
& rBaseURL
, bool& rbDirIsReadOnly
)
254 rbDirIsReadOnly
= false;
258 uno::Reference
< ucb::XCommandEnvironment
> xEnv
;
259 ::ucbhelper::Content
aCnt( rBaseURL
.GetMainURL( INetURLObject::DecodeMechanism::NONE
), xEnv
, comphelper::getProcessComponentContext() );
261 uno::Sequence
<OUString
> aProps
{ "Url" };
263 uno::Reference
< sdbc::XResultSet
> xResultSet( aCnt
.createCursor( aProps
, ::ucbhelper::INCLUDE_DOCUMENTS_ONLY
) );
265 #if defined(MACOSX) && HAVE_FEATURE_READONLY_INSTALLSET
266 if( rBaseURL
.GetProtocol() == INetProtocol::File
)
268 const char *appBundle
= [[[NSBundle mainBundle
] bundlePath
] UTF8String
];
269 OUString path
= rBaseURL
.GetURLPath();
270 if( path
.startsWith( OUString( appBundle
, strlen( appBundle
), RTL_TEXTENCODING_UTF8
) + "/" ) )
271 rbDirIsReadOnly
= true;
276 // check readonlyness the very hard way
277 INetURLObject
aTestURL( rBaseURL
);
278 OUString
aTestFile( "cdefghij.klm" );
280 aTestURL
.Append( aTestFile
);
281 std::unique_ptr
<SvStream
> pTestStm(::utl::UcbStreamHelper::CreateStream( aTestURL
.GetMainURL( INetURLObject::DecodeMechanism::NONE
), StreamMode::WRITE
));
285 pTestStm
->WriteInt32( sal_Int32(1) );
287 if( pTestStm
->GetError() )
288 rbDirIsReadOnly
= true;
291 KillFile( aTestURL
);
294 rbDirIsReadOnly
= true;
296 catch( const ucb::ContentCreationException
& )
299 catch( const uno::RuntimeException
& )
302 catch( const uno::Exception
& )
306 if( xResultSet
.is() )
308 uno::Reference
< ucb::XContentAccess
> xContentAccess( xResultSet
, uno::UNO_QUERY
);
310 if( xContentAccess
.is() )
312 static const char s_sTitle
[] = "Title";
313 static const char s_sIsReadOnly
[] = "IsReadOnly";
315 while( xResultSet
->next() )
317 INetURLObject
aThmURL( xContentAccess
->queryContentIdentifierString() );
319 if(aThmURL
.GetExtension().equalsIgnoreAsciiCase("thm"))
321 INetURLObject
aSdgURL( aThmURL
); aSdgURL
.SetExtension( "sdg" );
322 INetURLObject
aSdvURL( aThmURL
); aSdvURL
.SetExtension( "sdv" );
327 ::ucbhelper::Content
aThmCnt( aThmURL
.GetMainURL( INetURLObject::DecodeMechanism::NONE
), xEnv
, comphelper::getProcessComponentContext() );
328 ::ucbhelper::Content
aSdgCnt( aSdgURL
.GetMainURL( INetURLObject::DecodeMechanism::NONE
), xEnv
, comphelper::getProcessComponentContext() );
329 ::ucbhelper::Content
aSdvCnt( aSdvURL
.GetMainURL( INetURLObject::DecodeMechanism::NONE
), xEnv
, comphelper::getProcessComponentContext() );
333 aThmCnt
.getPropertyValue( s_sTitle
) >>= aTitle
;
335 catch( const uno::RuntimeException
& )
338 catch( const uno::Exception
& )
342 if( !aTitle
.isEmpty() )
344 bool bReadOnly
= false;
348 aThmCnt
.getPropertyValue( s_sIsReadOnly
) >>= bReadOnly
;
350 catch( const uno::RuntimeException
& )
353 catch( const uno::Exception
& )
361 aSdgCnt
.getPropertyValue( s_sTitle
) >>= aTitle
;
363 catch( const css::uno::RuntimeException
& )
366 catch( const css::uno::Exception
& )
370 if( !aTitle
.isEmpty() )
374 aSdgCnt
.getPropertyValue( s_sIsReadOnly
) >>= bReadOnly
;
376 catch( const uno::RuntimeException
& )
379 catch( const uno::Exception
& )
389 aSdvCnt
.getPropertyValue( s_sTitle
) >>= aTitle
;
391 catch( const css::uno::RuntimeException
& )
394 catch( const css::uno::Exception
& )
398 if( !aTitle
.isEmpty() )
402 aSdvCnt
.getPropertyValue( s_sIsReadOnly
) >>= bReadOnly
;
404 catch( const uno::RuntimeException
& )
407 catch( const uno::Exception
& )
413 GalleryThemeEntry
* pEntry
= GalleryTheme::CreateThemeEntry( aThmURL
, rbDirIsReadOnly
|| bReadOnly
);
416 aThemeList
.push_back( pEntry
);
419 catch( const ucb::ContentCreationException
& )
422 catch( const uno::RuntimeException
& )
425 catch( const uno::Exception
& )
433 catch( const ucb::ContentCreationException
& )
436 catch( const uno::RuntimeException
& )
439 catch( const uno::Exception
& )
444 GalleryThemeEntry
* Gallery::ImplGetThemeEntry( const OUString
& rThemeName
)
446 GalleryThemeEntry
* pFound
= nullptr;
448 if( !rThemeName
.isEmpty() )
450 for ( size_t i
= 0, n
= aThemeList
.size(); i
< n
&& !pFound
; ++i
)
451 if( rThemeName
== aThemeList
[ i
]->GetThemeName() )
452 pFound
= aThemeList
[ i
];
458 OUString
Gallery::GetThemeName( sal_uIntPtr nThemeId
) const
460 GalleryThemeEntry
* pFound
= nullptr;
462 for ( size_t i
= 0, n
= aThemeList
.size(); i
< n
&& !pFound
; ++i
)
464 GalleryThemeEntry
* pEntry
= aThemeList
[ i
];
465 if( nThemeId
== pEntry
->GetId() )
469 // try fallback, if no entry was found
476 case GALLERY_THEME_3D
:
477 aFallback
= GalResId(RID_GALLERYSTR_THEME_3D
);
479 case GALLERY_THEME_BULLETS
:
480 aFallback
= GalResId(RID_GALLERYSTR_THEME_BULLETS
);
482 case GALLERY_THEME_HOMEPAGE
:
483 aFallback
= GalResId(RID_GALLERYSTR_THEME_HOMEPAGE
);
485 case GALLERY_THEME_POWERPOINT
:
486 aFallback
= RID_GALLERYSTR_THEME_POWERPOINT
;
488 case GALLERY_THEME_FONTWORK
:
489 aFallback
= RID_GALLERYSTR_THEME_FONTWORK
;
491 case GALLERY_THEME_FONTWORK_VERTICAL
:
492 aFallback
= RID_GALLERYSTR_THEME_FONTWORK_VERTICAL
;
494 case GALLERY_THEME_SOUNDS
:
495 aFallback
= GalResId(RID_GALLERYSTR_THEME_SOUNDS
);
497 case RID_GALLERYSTR_THEME_ARROWS
:
498 case RID_GALLERYSTR_THEME_COMPUTERS
:
499 case RID_GALLERYSTR_THEME_DIAGRAMS
:
500 case RID_GALLERYSTR_THEME_EDUCATION
:
501 case RID_GALLERYSTR_THEME_ENVIRONMENT
:
502 case RID_GALLERYSTR_THEME_FINANCE
:
503 case RID_GALLERYSTR_THEME_PEOPLE
:
504 case RID_GALLERYSTR_THEME_SYMBOLS
:
505 case RID_GALLERYSTR_THEME_TRANSPORT
:
506 case RID_GALLERYSTR_THEME_TXTSHAPES
:
507 aFallback
= GalResId(static_cast<sal_uInt32
>(nThemeId
));
513 pFound
= const_cast<Gallery
*>(this)->ImplGetThemeEntry(aFallback
);
516 return( pFound
? pFound
->GetThemeName() : OUString() );
519 bool Gallery::HasTheme( const OUString
& rThemeName
)
521 return( ImplGetThemeEntry( rThemeName
) != nullptr );
524 bool Gallery::CreateTheme( const OUString
& rThemeName
)
528 if( !HasTheme( rThemeName
) && ( GetUserURL().GetProtocol() != INetProtocol::NotValid
) )
530 INetURLObject
aURL( GetUserURL() );
531 aURL
.Append( rThemeName
);
532 GalleryThemeEntry
* pNewEntry
= new GalleryThemeEntry(
533 true, aURL
, rThemeName
,
534 false, true, 0, false );
536 aThemeList
.push_back( pNewEntry
);
537 delete( new GalleryTheme( this, pNewEntry
) );
538 Broadcast( GalleryHint( GalleryHintType::THEME_CREATED
, rThemeName
) );
545 void Gallery::RenameTheme( const OUString
& rOldName
, const OUString
& rNewName
)
547 GalleryThemeEntry
* pThemeEntry
= ImplGetThemeEntry( rOldName
);
549 // check if the new theme name is already present
550 if( pThemeEntry
&& !HasTheme( rNewName
) && !pThemeEntry
->IsReadOnly() )
552 SfxListener aListener
;
553 GalleryTheme
* pThm
= AcquireTheme( rOldName
, aListener
);
557 pThemeEntry
->SetName( rNewName
);
560 Broadcast( GalleryHint( GalleryHintType::THEME_RENAMED
, rOldName
, pThm
->GetName() ) );
561 ReleaseTheme( pThm
, aListener
);
566 bool Gallery::RemoveTheme( const OUString
& rThemeName
)
568 GalleryThemeEntry
* pThemeEntry
= ImplGetThemeEntry( rThemeName
);
571 if( pThemeEntry
&& !pThemeEntry
->IsReadOnly() )
573 Broadcast( GalleryHint( GalleryHintType::CLOSE_THEME
, rThemeName
) );
575 SfxListener aListener
;
576 GalleryTheme
* pThm
= AcquireTheme( rThemeName
, aListener
);
580 INetURLObject
aThmURL( pThm
->GetThmURL() );
581 INetURLObject
aSdgURL( pThm
->GetSdgURL() );
582 INetURLObject
aSdvURL( pThm
->GetSdvURL() );
583 INetURLObject
aStrURL( pThm
->GetSdvURL() );
585 ReleaseTheme( pThm
, aListener
);
593 GalleryThemeList::const_iterator aEnd
= aThemeList
.end();
594 for ( GalleryThemeList::iterator it
= aThemeList
.begin(); it
!= aEnd
; ++it
)
596 if ( pThemeEntry
== *it
) {
598 aThemeList
.erase( it
);
603 Broadcast( GalleryHint( GalleryHintType::THEME_REMOVED
, rThemeName
) );
611 GalleryTheme
* Gallery::ImplGetCachedTheme(const GalleryThemeEntry
* pThemeEntry
)
613 GalleryTheme
* pTheme
= nullptr;
617 for (GalleryCacheThemeList::const_iterator it
= aThemeCache
.begin(); it
!= aThemeCache
.end(); ++it
)
619 if (pThemeEntry
== (*it
)->GetThemeEntry())
621 pTheme
= (*it
)->GetTheme();
628 INetURLObject aURL
= pThemeEntry
->GetThmURL();
630 DBG_ASSERT( aURL
.GetProtocol() != INetProtocol::NotValid
, "invalid URL" );
632 if( FileExists( aURL
) )
634 std::unique_ptr
<SvStream
> pIStm(::utl::UcbStreamHelper::CreateStream( aURL
.GetMainURL( INetURLObject::DecodeMechanism::NONE
), StreamMode::READ
));
640 pTheme
= new GalleryTheme( this, const_cast<GalleryThemeEntry
*>(pThemeEntry
) );
641 ReadGalleryTheme( *pIStm
, *pTheme
);
643 if( pIStm
->GetError() )
649 catch (const css::ucb::ContentCreationException
&)
656 aThemeCache
.push_back( new GalleryThemeCacheEntry( pThemeEntry
, pTheme
));
663 void Gallery::ImplDeleteCachedTheme( GalleryTheme
* pTheme
)
665 GalleryCacheThemeList::const_iterator aEnd
= aThemeCache
.end();
666 for (GalleryCacheThemeList::iterator it
= aThemeCache
.begin(); it
!= aEnd
; ++it
)
668 if (pTheme
== (*it
)->GetTheme())
671 aThemeCache
.erase(it
);
677 GalleryTheme
* Gallery::AcquireTheme( const OUString
& rThemeName
, SfxListener
& rListener
)
679 GalleryTheme
* pTheme
= nullptr;
680 GalleryThemeEntry
* pThemeEntry
= ImplGetThemeEntry( rThemeName
);
682 if( pThemeEntry
&& ( ( pTheme
= ImplGetCachedTheme( pThemeEntry
) ) != nullptr ) )
683 rListener
.StartListening( *pTheme
);
688 void Gallery::ReleaseTheme( GalleryTheme
* pTheme
, SfxListener
& rListener
)
692 rListener
.EndListening( *pTheme
);
694 if( !pTheme
->HasListeners() )
695 ImplDeleteCachedTheme( pTheme
);
699 bool GalleryThemeEntry::IsDefault() const
701 return ( nId
> 0 ) && ( nId
!= ( RID_GALLERYSTR_THEME_MYTHEME
- RID_GALLERYSTR_THEME_START
) );
704 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */