qt: playlist: use item title if available
[vlc.git] / modules / gui / skins2 / src / theme_loader.cpp
blob80f680172e81e25406d58b81e3d5aa2118504d6b
1 /*****************************************************************************
2 * theme_loader.cpp
3 *****************************************************************************
4 * Copyright (C) 2003 the VideoLAN team
6 * Authors: Cyril Deguet <asmax@via.ecp.fr>
7 * Olivier Teulière <ipkiss@via.ecp.fr>
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22 *****************************************************************************/
24 #ifdef HAVE_CONFIG_H
25 # include "config.h"
26 #endif
28 #include <fcntl.h>
29 #include <sys/stat.h>
30 #include <unistd.h>
31 #include <fstream>
32 #include <memory>
34 #include <vlc_common.h>
35 #include <vlc_fs.h>
36 #include <vlc_url.h>
37 #include <vlc_stream_extractor.h>
39 #include "theme_loader.hpp"
40 #include "theme.hpp"
41 #include "../parser/builder.hpp"
42 #include "../parser/skin_parser.hpp"
43 #include "../src/os_factory.hpp"
44 #include "../src/vlcproc.hpp"
45 #include "../src/window_manager.hpp"
47 #define DEFAULT_XML_FILE "theme.xml"
48 #define WINAMP2_XML_FILE "winamp2.xml"
50 /* Recursive make directory
51 * Abort if you get an ENOENT errno somewhere in the middle
52 * e.g. ignore error "mkdir on existing directory"
54 * return 1 if OK, 0 on error
56 static int makedir( const char *newdir )
58 char *p, *buffer = strdup( newdir );
59 int len = strlen( buffer );
61 if( len <= 0 )
63 free( buffer );
64 return 0;
67 if( buffer[len-1] == '/' )
69 buffer[len-1] = '\0';
72 if( vlc_mkdir( buffer, 0775 ) == 0 )
74 free( buffer );
75 return 1;
78 p = buffer + 1;
79 while( 1 )
81 char hold;
83 while( *p && *p != '\\' && *p != '/' ) p++;
84 hold = *p;
85 *p = 0;
86 if( ( vlc_mkdir( buffer, 0775 ) == -1 ) && ( errno == ENOENT ) )
88 fprintf( stderr, "couldn't create directory %s\n", buffer );
89 free( buffer );
90 return 0;
92 if( hold == 0 ) break;
93 *p++ = hold;
95 free( buffer );
96 return 1;
99 bool ThemeLoader::load( const std::string &fileName )
101 std::string path = getFilePath( fileName );
103 //Before all, let's see if the file is present
104 struct stat p_stat;
105 if( vlc_stat( fileName.c_str(), &p_stat ) )
106 return false;
108 // First, we try to un-targz the file, and if it fails we hope it's a XML
109 // file...
111 if( ! extract( fileName ) && ! parse( path, fileName ) )
112 return false;
114 Theme *pNewTheme = getIntf()->p_sys->p_theme;
115 if( !pNewTheme )
116 return false;
118 // Restore the theme configuration
119 getIntf()->p_sys->p_theme->loadConfig();
121 // Retain new loaded skins in config
122 config_PutPsz( "skins2-last", fileName.c_str() );
124 return true;
127 bool ThemeLoader::extract( const std::string &fileName )
129 bool result = true;
130 std::string tempPath = getTmpDir();
131 if( tempPath.empty() )
132 return false;
134 if( unarchive( fileName, tempPath ) == false )
136 msg_Err( getIntf(), "extraction from %s failed", fileName.c_str() );
137 return false;
140 std::string path;
141 std::string xmlFile;
142 OSFactory *pOsFactory = OSFactory::instance( getIntf() );
143 // Find the XML file in the theme
144 if( findFile( tempPath, DEFAULT_XML_FILE, xmlFile ) )
146 path = getFilePath( xmlFile );
148 else
150 // No XML file, check if it is a winamp2 skin
151 std::string mainBmp;
152 if( findFile( tempPath, "main.bmp", mainBmp ) )
154 msg_Dbg( getIntf(), "trying to load a winamp2 skin" );
155 path = getFilePath( mainBmp );
157 // Look for winamp2.xml in the resource path
158 std::list<std::string> resPath = pOsFactory->getResourcePath();
159 std::list<std::string>::const_iterator it;
160 for( it = resPath.begin(); it != resPath.end(); ++it )
162 if( findFile( *it, WINAMP2_XML_FILE, xmlFile ) )
163 break;
168 if( !xmlFile.empty() )
170 // Parse the XML file
171 if (! parse( path, xmlFile ) )
173 msg_Err( getIntf(), "error while parsing %s", xmlFile.c_str() );
174 result = false;
177 else
179 msg_Err( getIntf(), "no XML found in theme %s", fileName.c_str() );
180 result = false;
183 // Clean-up
184 deleteTempFiles( tempPath );
185 return result;
188 bool ThemeLoader::unarchive( const std::string& fileName, const std::string &tempPath )
190 #define UPTR_HELPER(type,deleter) []( type * data ) { \
191 return std::unique_ptr< type, decltype( deleter )> ( data, deleter ); }
193 auto make_input_node_ptr = UPTR_HELPER( input_item_node_t, &input_item_node_Delete );
194 auto make_input_item_ptr = UPTR_HELPER( input_item_t, &input_item_Release );
195 auto make_stream_ptr = UPTR_HELPER( stream_t, &vlc_stream_Delete );
196 auto make_cstr_ptr = UPTR_HELPER( char, &std::free );
198 #undef UPTR_HELPER
200 auto uri = make_cstr_ptr( vlc_path2uri( fileName.c_str(), "file" ) );
201 if( !uri )
203 msg_Err( getIntf(), "unable to convert %s to local URI",
204 fileName.c_str() );
205 return false;
208 auto input = make_stream_ptr( vlc_stream_NewURL( getIntf(), uri.get() ) );
209 if( !input )
211 msg_Err( getIntf(), "unable to open %s", uri.get() );
212 return false;
215 stream_t* stream = input.get();
216 if( vlc_stream_directory_Attach( &stream, NULL ) )
218 msg_Err( getIntf(), "unable to attach stream_directory, treat as XML!" );
220 else
222 input.release();
223 input.reset( stream );
225 auto item = make_input_item_ptr( input_item_New( "vlc://dummy", "vlc://dummy" ) );
226 auto node = make_input_node_ptr( (input_item_node_t*)std::calloc( 1, sizeof( input_item_node_t ) ) );
228 if( !item || !node )
229 return false;
231 input_item_AddOption( item.get(), "ignore-filetypes=\"\"", VLC_INPUT_OPTION_TRUSTED );
232 input_item_AddOption( item.get(), "extractor-flatten", VLC_INPUT_OPTION_TRUSTED );
233 node->p_item = item.release();
235 if( vlc_stream_ReadDir( input.get(), node.get() ) )
237 msg_Err( getIntf(), "unable to read items in %s", uri.get() );
238 return false;
241 for( int i = 0; i < node->i_children; ++i )
243 auto child = node->pp_children[i]->p_item;
244 auto child_stream = make_stream_ptr( vlc_stream_NewMRL( getIntf(), child->psz_uri ) );
245 if( !child_stream )
247 msg_Err( getIntf(), "unable to open %s for reading", child->psz_name );
248 return false;
251 auto out_path = tempPath + "/" + child->psz_name;
253 { /* create directory tree */
254 auto out_directory = out_path.substr( 0, out_path.find_last_of( '/' ) );
256 if( makedir( out_directory.c_str() ) == false )
258 msg_Err( getIntf(), "failed to create directory tree for %s (%s)",
259 out_path.c_str(), out_directory.c_str() );
261 return false;
265 { /* write data to disk */
266 std::string contents;
268 char buf[1024];
269 ssize_t n;
271 while( ( n = vlc_stream_Read( child_stream.get(), buf, sizeof buf ) ) > 0 )
272 contents.append( buf, n );
274 std::ofstream out_stream( out_path, std::ios::binary );
276 if( out_stream.write( contents.data(), contents.size() ) )
278 msg_Dbg( getIntf(), "finished writing %zu bytes to %s",
279 size_t{ contents.size() }, out_path.c_str() );
281 else
283 msg_Err( getIntf(), "unable to write %zu bytes to %s",
284 size_t{ contents.size() }, out_path.c_str() );
285 return false;
291 return true;
294 void ThemeLoader::deleteTempFiles( const std::string &path )
296 OSFactory::instance( getIntf() )->rmDir( path );
299 bool ThemeLoader::parse( const std::string &path, const std::string &xmlFile )
301 // File loaded
302 msg_Dbg( getIntf(), "using skin file: %s", xmlFile.c_str() );
304 // Start the parser
305 SkinParser parser( getIntf(), xmlFile, path );
306 if( ! parser.parse() )
307 return false;
309 // Build and store the theme
310 Builder builder( getIntf(), parser.getData(), path );
311 getIntf()->p_sys->p_theme = builder.build();
313 return true;
317 std::string ThemeLoader::getFilePath( const std::string &rFullPath )
319 OSFactory *pOsFactory = OSFactory::instance( getIntf() );
320 const std::string &sep = pOsFactory->getDirSeparator();
321 // Find the last separator ('/' or '\')
322 std::string::size_type p = rFullPath.rfind( sep, rFullPath.size() );
323 std::string basePath;
324 if( p != std::string::npos )
326 if( p < rFullPath.size() - 1)
328 basePath = rFullPath.substr( 0, p );
330 else
332 basePath = rFullPath;
335 return basePath;
338 bool ThemeLoader::findFile( const std::string &rootDir, const std::string &rFileName,
339 std::string &themeFilePath )
341 // Path separator
342 const std::string &sep = OSFactory::instance( getIntf() )->getDirSeparator();
344 const char *pszDirContent;
346 // Open the dir
347 DIR *pCurrDir = vlc_opendir( rootDir.c_str() );
349 if( pCurrDir == NULL )
351 // An error occurred
352 msg_Dbg( getIntf(), "cannot open directory %s", rootDir.c_str() );
353 return false;
356 // While we still have entries in the directory
357 while( ( pszDirContent = vlc_readdir( pCurrDir ) ) != NULL )
359 std::string newURI = rootDir + sep + pszDirContent;
361 // Skip . and ..
362 if( std::string( pszDirContent ) != "." &&
363 std::string( pszDirContent ) != ".." )
365 #if defined( S_ISDIR )
366 struct stat stat_data;
368 if( ( vlc_stat( newURI.c_str(), &stat_data ) == 0 )
369 && S_ISDIR(stat_data.st_mode) )
370 #elif defined( DT_DIR )
371 if( pDirContent->d_type & DT_DIR )
372 #else
373 if( 0 )
374 #endif
376 // Can we find the file in this subdirectory?
377 if( findFile( newURI, rFileName, themeFilePath ) )
379 closedir( pCurrDir );
380 return true;
383 else
385 // Found the theme file?
386 if( rFileName == std::string( pszDirContent ) )
388 themeFilePath = newURI;
389 closedir( pCurrDir );
390 return true;
396 closedir( pCurrDir );
397 return false;
400 // FIXME: could become a skins2 OS factory function or a vlc core function
401 std::string ThemeLoader::getTmpDir( )
403 #if defined( _WIN32 )
404 wchar_t *tmpdir = _wtempnam( NULL, L"vlt" );
405 if( tmpdir == NULL )
406 return "";
407 char* utf8 = FromWide( tmpdir );
408 free( tmpdir );
409 std::string tempPath( utf8 ? utf8 : "" );
410 free( utf8 );
411 return tempPath;
413 #elif defined( __OS2__ )
414 char *tmpdir = tempnam( NULL, "vlt" );
415 if( tmpdir == NULL )
416 return "";
417 std::string tempPath( sFromLocale( tmpdir ));
418 free( tmpdir );
419 return tempPath;
421 #else
422 char templ[] = "/tmp/vltXXXXXX";
423 char *tmpdir = mkdtemp( templ );
424 return std::string( tmpdir ? tmpdir : "");
425 #endif