hydrogen 1.2.6
Playlist.cpp
Go to the documentation of this file.
1/*
2 * Hydrogen
3 * Copyright(c) 2002-2008 by Alex >Comix< Cominu [comix@users.sourceforge.net]
4 * Copyright(c) 2008-2025 The hydrogen development team [hydrogen-devel@lists.sourceforge.net]
5 *
6 * http://www.hydrogen-music.org
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY, without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see https://www.gnu.org/licenses
20 *
21 */
22
24#include <core/Hydrogen.h>
27#include <core/Helpers/Legacy.h>
28#include <core/Helpers/Xml.h>
29#include <core/EventQueue.h>
30
31namespace H2Core
32{
33
35
43
45{
46 clear();
47 __instance = nullptr;
48}
49
51{
52 if ( __instance == nullptr ) {
53 __instance = new Playlist();
54 }
55}
56
58{
59 for ( int i = 0; i < __entries.size(); i++ ) {
60 delete __entries[i];
61 }
62 __entries.clear();
63}
64
65Playlist* Playlist::load_file( const QString& pl_path, bool useRelativePaths )
66{
67 XMLDoc doc;
68 doc.read( pl_path );
69
70 XMLNode rootNode = doc.firstChildElement( "playlist" );
71 if ( rootNode.isNull() ) {
72 ERRORLOG( "playlist node not found" );
73 return nullptr;
74 }
75
76 // Check whether we deal with a legacy format.
77 XMLNode legacyNextNode;
78 XMLNode capitalizedSongsNode = rootNode.firstChildElement( "Songs" );
79 if ( ! capitalizedSongsNode.isNull() ) {
80 legacyNextNode = capitalizedSongsNode.firstChildElement( "next" );
81 }
82 if ( ! legacyNextNode.isNull() ) {
83 Playlist* pl = new Playlist();
84 Playlist* ret = Legacy::load_playlist( pl, pl_path );
85 if ( ret == nullptr ) {
86 delete pl;
87 return nullptr;
88 }
89
90 WARNINGLOG( QString( "Upgrading playlist [%1]" ).arg( pl_path ) );
91 pl->save_file( pl_path, pl->getFilename(), true, useRelativePaths );
92 return pl;
93 }
94
95 QFileInfo fileInfo = QFileInfo( pl_path );
96
97 // Check whether the file was created using a newer version of Hydrogen.
98 auto formatVersionNode = rootNode.firstChildElement( "formatVersion" );
99 if ( ! formatVersionNode.isNull() ) {
100 WARNINGLOG( QString( "Playlist file [%1] was created with a more recent version of Hydrogen than the current one!" )
101 .arg( fileInfo.absoluteFilePath() ) );
102 }
103
104 Playlist* pPlaylist = new Playlist();
105 pPlaylist->setFilename( fileInfo.absoluteFilePath() );
106
107 XMLNode songsNode = rootNode.firstChildElement( "songs" );
108 if ( !songsNode.isNull() ) {
109 XMLNode nextNode = songsNode.firstChildElement( "song" );
110 while ( !nextNode.isNull() ) {
111
112 QString songPath = nextNode.read_string( "path", "", false, false );
113 if ( !songPath.isEmpty() ) {
114 Playlist::Entry* pEntry = new Playlist::Entry();
115 QFileInfo songPathInfo( fileInfo.absoluteDir(), songPath );
116 pEntry->filePath = songPathInfo.absoluteFilePath();
117 pEntry->fileExists = songPathInfo.isReadable();
118 pEntry->scriptPath = nextNode.read_string( "scriptPath", "" );
119 pEntry->scriptEnabled = nextNode.read_bool( "scriptEnabled", false );
120 pPlaylist->add( pEntry );
121 }
122
123 nextNode = nextNode.nextSiblingElement( "song" );
124 }
125 } else {
126 WARNINGLOG( "songs node not found" );
127 }
128 return pPlaylist;
129}
130
131bool Playlist::save_file( const QString& pl_path, const QString& name, bool overwrite, bool useRelativePaths )
132{
133 INFOLOG( QString( "Saving palylist to %1" ).arg( pl_path ) );
134 if( !overwrite && Filesystem::file_exists( pl_path, true ) ) {
135 ERRORLOG( QString( "palylist %1 already exists" ).arg( pl_path ) );
136 return false;
137 }
138
139 setFilename( pl_path );
140
141 XMLDoc doc;
142 XMLNode root = doc.set_root( "playlist", "playlist" );
143 root.write_string( "name", name);
144 XMLNode songs = root.createNode( "songs" );
145 save_to( &songs, useRelativePaths );
146 return doc.write( pl_path );
147}
148
149void Playlist::save_to( XMLNode* node, bool useRelativePaths )
150{
151 QFileInfo fileInfo( __filename );
152
153 for (int i = 0; i < size(); i++ ) {
154 Entry* entry = get( i );
155 QString path = entry->filePath;
156 if ( useRelativePaths ) {
157 path = fileInfo.absoluteDir().relativeFilePath( path );
158 }
159 XMLNode song_node = node->createNode( "song" );
160 song_node.write_string( "path", path );
161 song_node.write_string( "scriptPath", entry->scriptPath );
162 song_node.write_bool( "scriptEnabled", entry->scriptEnabled);
163 }
164}
165
166Playlist* Playlist::load( const QString& filename, bool useRelativePaths )
167{
168 // load_file might set __instance = 0;
169 Playlist* prev = __instance;
170 Playlist* playlist = Playlist::load_file( filename, useRelativePaths );
171
172 if ( playlist != nullptr ) {
173 delete prev;
174 __instance = playlist;
175 } else {
176 __instance = prev;
177 }
178
179 return playlist;
180}
181
182/* This method is called by Event dispatcher thread ( GUI ) */
183void Playlist::activateSong( int songNumber )
184{
185 setSelectedSongNr( songNumber );
186 setActiveSongNumber( songNumber );
187
188 execScript( songNumber );
189}
190
191bool Playlist::getSongFilenameByNumber( int songNumber, QString& filename)
192{
193 bool Success = true;
194
195 if ( size() == 0 || songNumber >= size() ) {
196 Success = false;
197 }
198
199 if( Success) {
200 filename = get( songNumber )->filePath;
201 }
202
203 return Success;
204}
205
206/* This method is called by MIDI thread */
207void Playlist::setNextSongByNumber( int songNumber )
208{
209 if ( size() == 0 || songNumber >= size() ) {
210 return;
211 }
212
213 /* NOTE: we are in MIDI thread and can't just call loadSong from here :( */
215}
216
217void Playlist::execScript( int index)
218{
219 QString file = get( index )->scriptPath;
220
221 if ( !get( index )->scriptEnabled || !QFile( file ).exists() ) {
222 return;
223 }
224
225 std::system( file.toLocal8Bit() );
226
227 return;
228}
229
230QString Playlist::toQString( const QString& sPrefix, bool bShort ) const {
231 QString s = Base::sPrintIndention;
232 QString sOutput;
233 if ( ! bShort ) {
234 sOutput = QString( "%1[Playlist]\n" ).arg( sPrefix )
235 .append( QString( "%1%2filename: %3\n" ).arg( sPrefix ).arg( s ).arg( __filename ) )
236 .append( QString( "%1%2m_nSelectedSongNumber: %3\n" ).arg( sPrefix ).arg( s ).arg( m_nSelectedSongNumber ) )
237 .append( QString( "%1%2m_nActiveSongNumber: %3\n" ).arg( sPrefix ).arg( s ).arg( m_nActiveSongNumber ) )
238 .append( QString( "%1%2entries:\n" ).arg( sPrefix ).arg( s ) );
239 if ( size() > 0 ) {
240 for ( auto ii : __entries ) {
241 sOutput.append( QString( "%1%2Entry:\n" ).arg( sPrefix ).arg( s + s ) )
242 .append( QString( "%1%2filePath: %3\n" ).arg( sPrefix ).arg( s + s + s ).arg( ii->filePath ) )
243 .append( QString( "%1%2fileExists: %3\n" ).arg( sPrefix ).arg( s + s + s ).arg( ii->fileExists ) )
244 .append( QString( "%1%2scriptPath: %3\n" ).arg( sPrefix ).arg( s + s + s ).arg( ii->scriptPath ) )
245 .append( QString( "%1%2scriptEnabled: %3\n" ).arg( sPrefix ).arg( s + s + s ).arg( ii->scriptEnabled ) );
246 }
247 }
248 sOutput.append( QString( "%1%2m_bIsModified: %3\n" ).arg( sPrefix ).arg( s ).arg( m_bIsModified ) );
249 } else {
250 sOutput = QString( "[Playlist]" )
251 .append( QString( " filename: %1" ).arg( __filename ) )
252 .append( QString( ", m_nSelectedSongNumber: %1" ).arg( m_nSelectedSongNumber ) )
253 .append( QString( ", m_nActiveSongNumber: %1" ).arg( m_nActiveSongNumber ) )
254 .append( ", entries: {" );
255 if ( size() > 0 ) {
256 for ( auto ii : __entries ) {
257 sOutput.append( QString( "[filePath: %1" ).arg( ii->filePath ) )
258 .append( QString( ", fileExists: %1" ).arg( ii->fileExists ) )
259 .append( QString( ", scriptPath: %1" ).arg( ii->scriptPath ) )
260 .append( QString( ", scriptEnabled: %1] " ).arg( ii->scriptEnabled ) );
261
262
263 }
264 }
265 sOutput.append( QString( "}, m_bIsModified: %1\n" ).arg( m_bIsModified ) );
266 }
267
268 return sOutput;
269}
270};
#define INFOLOG(x)
Definition Object.h:240
#define WARNINGLOG(x)
Definition Object.h:241
#define ERRORLOG(x)
Definition Object.h:242
static QString sPrintIndention
String used to format the debugging string output of some core classes.
Definition Object.h:127
static EventQueue * get_instance()
Returns a pointer to the current EventQueue singleton stored in __instance.
Definition EventQueue.h:224
void push_event(const EventType type, const int nValue)
Queues the next event into the EventQueue.
static bool file_exists(const QString &path, bool silent=false)
returns true if the given path is an existing regular file
static Playlist * load_playlist(Playlist *pl, const QString &pl_path)
load playlist from a file
Definition Legacy.cpp:244
Drumkit info.
Definition Playlist.h:37
void add(Entry *entry)
Definition Playlist.h:133
static Playlist * __instance
Object holding the current Playlist singleton.
Definition Playlist.h:105
void setNextSongByNumber(int SongNumber)
Definition Playlist.cpp:207
std::vector< Entry * > __entries
Definition Playlist.h:108
int m_nSelectedSongNumber
Definition Playlist.h:110
int m_nActiveSongNumber
Definition Playlist.h:111
void save_to(XMLNode *node, bool useRelativePaths)
Definition Playlist.cpp:149
static void create_instance()
If __instance equals 0, a new Playlist singleton will be created and stored in it.
Definition Playlist.cpp:50
void activateSong(int SongNumber)
Definition Playlist.cpp:183
void execScript(int index)
Definition Playlist.cpp:217
void setFilename(const QString &filename)
Definition Playlist.h:163
void setSelectedSongNr(int songNumber)
Definition Playlist.h:143
Entry * get(int idx)
Definition Playlist.h:127
QString toQString(const QString &sPrefix="", bool bShort=true) const override
Formatted string version for debugging purposes.
Definition Playlist.cpp:230
void setActiveSongNumber(int ActiveSongNumber)
Definition Playlist.h:153
QString __filename
Definition Playlist.h:106
bool save_file(const QString &pl_path, const QString &name, bool overwrite, bool useRelativePaths)
Definition Playlist.cpp:131
const QString & getFilename()
Definition Playlist.h:158
static Playlist * load(const QString &filename, bool useRelativePaths)
Definition Playlist.cpp:166
static Playlist * load_file(const QString &pl_path, bool useRelativePaths)
Definition Playlist.cpp:65
int size() const
Definition Playlist.h:122
bool getSongFilenameByNumber(int songNumber, QString &fileName)
Definition Playlist.cpp:191
XMLDoc is a subclass of QDomDocument with read and write methods.
Definition Xml.h:182
XMLNode set_root(const QString &node_name, const QString &xmlns=nullptr)
create the xml header and root node
Definition Xml.cpp:336
bool read(const QString &filepath, bool bSilent=false)
read the content of an xml file
Definition Xml.cpp:277
bool write(const QString &filepath)
write itself into a file
Definition Xml.cpp:311
XMLNode is a subclass of QDomNode with read and write values methods.
Definition Xml.h:39
bool read_bool(const QString &node, bool default_value, bool inexistent_ok=true, bool empty_ok=true, bool bSilent=false)
reads a boolean stored into a child node
Definition Xml.cpp:165
QString read_string(const QString &node, const QString &default_value, bool inexistent_ok=true, bool empty_ok=true, bool bSilent=false)
reads a string stored into a child node
Definition Xml.cpp:76
XMLNode createNode(const QString &name)
create a new XMLNode that has to be appended into de XMLDoc
Definition Xml.cpp:44
void write_string(const QString &node, const QString &value)
write a string into a child node
Definition Xml.cpp:250
void write_bool(const QString &node, const bool value)
write a boolean into a child node
Definition Xml.cpp:269
@ EVENT_PLAYLIST_LOADSONG
Definition EventQueue.h:100