1 /*
2  * Hydrogen
3  * Copyright(c) 2002-2008 by Alex >Comix< Cominu [comix@users.sourceforge.net]
4  *
5  * http://www.hydrogen-music.org
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY, without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  *
21  */
22 
23 
24 #include "PlaylistDialog.h"
25 #include "../HydrogenApp.h"
26 #include "../InstrumentRack.h"
27 #include "SoundLibrary/SoundLibraryPanel.h"
28 #include "SongEditor/SongEditorPanel.h"
29 #include "Widgets/PixmapWidget.h"
30 
31 #include <hydrogen/helpers/files.h>
32 #include <hydrogen/helpers/filesystem.h>
33 #include <hydrogen/h2_exception.h>
34 #include <hydrogen/Preferences.h>
35 #include <hydrogen/hydrogen.h>
36 #include <hydrogen/timeline.h>
37 #include <hydrogen/event_queue.h>
38 #include <hydrogen/basics/playlist.h>
39 
40 #include "../Widgets/Button.h"
41 
42 #include <QTreeWidget>
43 #include <QDomDocument>
44 #include <QMessageBox>
45 #include <QHeaderView>
46 #include <QFileDialog>
47 #include <vector>
48 #include <cstdlib>
49 #include <iostream>
50 #include <fstream>
51 #include <memory>
52 
53 using namespace H2Core;
54 using namespace std;
55 
56 const char* PlaylistDialog::__class_name = "PlaylistDialog";
57 
PlaylistDialog(QWidget * pParent)58 PlaylistDialog::PlaylistDialog ( QWidget* pParent )
59 		: QDialog ( pParent )
60 		, Object ( __class_name )
61 {
62 
63 	setupUi ( this );
64 	INFOLOG ( "INIT" );
65 	setWindowTitle ( tr ( "Playlist Browser" ) + QString(" - ") + Playlist::get_instance()->getFilename() );
66 	setFixedSize ( width(), height() );
67 
68 	installEventFilter( this );
69 
70 	// menubar
71 	QMenuBar *m_pMenubar = new QMenuBar( this );
72 
73 	// Playlist menu
74 	QMenu *m_pPlaylistMenu = m_pMenubar->addMenu( tr( "&Playlist" ) );
75 
76 	m_pPlaylistMenu->addAction( tr( "Add song to Play&list" ), this, SLOT( addSong() ), QKeySequence( "Ctrl+A" ) );
77 	m_pPlaylistMenu->addAction( tr( "Add &current song to Playlist" ), this, SLOT( addCurrentSong() ), QKeySequence( "Ctrl+Alt+A" ) );
78 	m_pPlaylistMenu->addSeparator();				// -----
79 	m_pPlaylistMenu->addAction( tr( "&Remove selected song from Playlist" ), this, SLOT( removeFromList() ), QKeySequence::Delete );
80 	m_pPlaylistMenu->addAction( tr( "&New Playlist" ), this, SLOT( clearPlaylist() ), QKeySequence( "Ctrl+N" ) );
81 	m_pPlaylistMenu->addSeparator();
82 	m_pPlaylistMenu->addAction( tr( "&Open Playlist" ), this, SLOT( loadList() ), QKeySequence( "Ctrl+O" ) );
83 	m_pPlaylistMenu->addSeparator();
84 	m_pPlaylistMenu->addAction( tr( "&Save Playlist" ), this, SLOT( saveList() ), QKeySequence( "Ctrl+S" ) );
85 	m_pPlaylistMenu->addAction( tr( "Save Playlist &as" ), this, SLOT( saveListAs() ), QKeySequence( "Ctrl+Shift+S" ) );
86 
87 #ifdef WIN32
88 	//no scripts under windows
89 #else
90 	// Script menu
91 	QMenu *m_pScriptMenu = m_pMenubar->addMenu( tr( "&Scripts" ) );
92 
93 	m_pScriptMenu->addAction( tr( "&Add Script to selected song" ), this, SLOT( loadScript() ), QKeySequence( "" ) );
94 	m_pScriptMenu->addAction( tr( "&Edit selected Script" ), this, SLOT( editScript() ), QKeySequence( "" ) );
95 	m_pScriptMenu->addSeparator();
96 	m_pScriptMenu->addAction( tr( "&Remove selected Script" ), this, SLOT( removeScript() ), QKeySequence( "" ) );
97 	m_pScriptMenu->addSeparator();
98 	m_pScriptMenu->addAction( tr( "&Create a new Script" ), this, SLOT( newScript() ), QKeySequence( "" ) );
99 #endif
100 
101 	// CONTROLS
102 	PixmapWidget *pControlsPanel = new PixmapWidget( nullptr );
103 	pControlsPanel->setFixedSize( 119, 32 );
104 	pControlsPanel->setPixmap( "/playerControlPanel/playlist_background_Control.png" );
105 	vboxLayout->addWidget( pControlsPanel );
106 
107 	// Rewind button
108 	m_pRwdBtn = new Button(
109 			pControlsPanel,
110 			"/playerControlPanel/btn_rwd_on.png",
111 			"/playerControlPanel/btn_rwd_off.png",
112 			"/playerControlPanel/btn_rwd_over.png",
113 			QSize(21, 15)
114 	);
115 	m_pRwdBtn->move(6, 6);
116 	m_pRwdBtn->setToolTip( tr("Rewind") );
117 	connect(m_pRwdBtn, SIGNAL(clicked(Button*)), this, SLOT(rewindBtnClicked(Button*)));
118 
119 	// Play button
120 	m_pPlayBtn = new ToggleButton(
121 			pControlsPanel,
122 			"/playerControlPanel/btn_play_on.png",
123 			"/playerControlPanel/btn_play_off.png",
124 			"/playerControlPanel/btn_play_over.png",
125 			QSize(33, 17)
126 	);
127 	m_pPlayBtn->move(33, 6);
128 	m_pPlayBtn->setPressed(false);
129 	m_pPlayBtn->setToolTip( tr("Play/ Pause/ Load selected song") );
130 	connect(m_pPlayBtn, SIGNAL(clicked(Button*)), this, SLOT(nodePlayBTN(Button*)));
131 
132 	// Stop button
133 	m_pStopBtn = new Button(
134 			pControlsPanel,
135 			"/playerControlPanel/btn_stop_on.png",
136 			"/playerControlPanel/btn_stop_off.png",
137 			"/playerControlPanel/btn_stop_over.png",
138 			QSize(21, 15)
139 	);
140 	m_pStopBtn->move(65, 6);
141 	m_pStopBtn->setToolTip( tr("Stop") );
142 	connect(m_pStopBtn, SIGNAL(clicked(Button*)), this, SLOT(nodeStopBTN(Button*)));
143 
144 	// Fast forward button
145 	m_pFfwdBtn = new Button(
146 			pControlsPanel,
147 			"/playerControlPanel/btn_ffwd_on.png",
148 			"/playerControlPanel/btn_ffwd_off.png",
149 			"/playerControlPanel/btn_ffwd_over.png",
150 			QSize(21, 15)
151 	);
152 	m_pFfwdBtn->move(92, 6);
153 	m_pFfwdBtn->setToolTip( tr("Fast Forward") );
154 	connect(m_pFfwdBtn, SIGNAL(clicked(Button*)), this, SLOT(ffWDBtnClicked(Button*)));
155 
156 #ifdef WIN32
157 	QStringList headers;
158 	headers << tr ( "Song list" );
159 	QTreeWidgetItem* header = new QTreeWidgetItem ( headers );
160 	m_pPlaylistTree->setHeaderItem ( header );
161 	m_pPlaylistTree->setAlternatingRowColors( true );
162 
163 		/*addSongBTN->setEnabled ( true );
164 	loadListBTN->setEnabled ( true );
165 	removeFromListBTN->setEnabled ( false );
166 	removeFromListBTN->setEnabled ( false );
167 	saveListBTN->setEnabled ( false );
168 	saveListAsBTN->setEnabled ( false );
169 	loadScriptBTN->hide();
170 	removeScriptBTN->hide();
171 	editScriptBTN->hide();
172 	newScriptBTN->hide();
173 		clearPlBTN->setEnabled ( false );*/
174 
175 	QVBoxLayout *pSideBarLayout = new QVBoxLayout(sideBarWidget);
176 	pSideBarLayout->setSpacing(0);
177 	pSideBarLayout->setMargin(0);
178 
179 #else
180 	QStringList headers;
181 	headers << tr ( "Song list" ) << tr ( "Script" ) << tr ( "exec Script" );
182 	QTreeWidgetItem* header = new QTreeWidgetItem ( headers );
183 	m_pPlaylistTree->setHeaderItem ( header );
184 	m_pPlaylistTree->header()->resizeSection ( 0, 405 );
185 	m_pPlaylistTree->header()->resizeSection ( 1, 405 );
186 	m_pPlaylistTree->header()->resizeSection ( 2, 15 );
187 	m_pPlaylistTree->setAlternatingRowColors( true );
188 
189 
190 	QVBoxLayout *pSideBarLayout = new QVBoxLayout(sideBarWidget);
191 	pSideBarLayout->setSpacing(0);
192 	pSideBarLayout->setMargin(0);
193 #endif
194 
195 	// zoom-in btn
196 	Button *pUpBtn = new Button(
197 			nullptr,
198 			"/songEditor/btn_up_on.png",
199 			"/songEditor/btn_up_off.png",
200 			"/songEditor/btn_up_over.png",
201 			QSize(18, 13)
202 	);
203 
204 	pUpBtn->setFontSize(7);
205 	pUpBtn->setToolTip( tr( "sort" ) );
206 	connect(pUpBtn, SIGNAL(clicked(Button*)), this, SLOT(o_upBClicked()) );
207 	pSideBarLayout->addWidget(pUpBtn);
208 
209 	// zoom-in btn
210 	Button *pDownBtn = new Button(
211 			nullptr,
212 			"/songEditor/btn_down_on.png",
213 			"/songEditor/btn_down_off.png",
214 			"/songEditor/btn_down_over.png",
215 			QSize(18, 13)
216 	);
217 
218 	pDownBtn->setFontSize(7);
219 	pDownBtn->setToolTip( tr( "sort" ) );
220 	connect(pDownBtn, SIGNAL(clicked(Button*)), this, SLOT(o_downBClicked()));
221 	pSideBarLayout->addWidget(pDownBtn);
222 
223 	//restore the playlist
224 	Playlist* pPlaylist = Playlist::get_instance();
225 	if( pPlaylist->size() > 0 ){
226 		for ( uint i = 0; i < pPlaylist->size(); ++i ){
227 			QTreeWidgetItem* pPlaylistItem = new QTreeWidgetItem ( m_pPlaylistTree );
228 
229 			pPlaylistItem->setText( 0, pPlaylist->get( i )->filePath );
230 			pPlaylistItem->setText( 1, pPlaylist->get( i )->scriptPath );
231 
232 			if ( pPlaylist->get( i )->scriptEnabled ) {
233 				pPlaylistItem->setCheckState( 2, Qt::Checked );
234 			} else {
235 				pPlaylistItem->setCheckState( 2, Qt::Unchecked );
236 			}
237 		}
238 
239 		//restore the selected item
240 		int selected = Playlist::get_instance()->getActiveSongNumber();
241 		int Selected = Playlist::get_instance()->getSelectedSongNr();
242 		if( selected == -1 && Selected == -1 ) return;
243 
244 		int aselected = 0;
245 		if( selected == -1 ){
246 			aselected = Selected;
247 		} else {
248 			aselected = selected ;
249 		}
250 
251 		QTreeWidgetItem* m_pPlaylistItem = m_pPlaylistTree->topLevelItem ( aselected );
252 		m_pPlaylistItem->setBackground( 0, QColor( 50, 50, 50) );
253 		m_pPlaylistItem->setBackground( 1, QColor( 50, 50, 50) );
254 		m_pPlaylistItem->setBackground( 2, QColor( 50, 50, 50) );
255 	}
256 
257 	timer = new QTimer( this );
258 	connect(timer, SIGNAL(timeout() ), this, SLOT( updateActiveSongNumber() ) );
259 	timer->start( 1000 );	// update player control at 1 fps
260 }
261 
~PlaylistDialog()262 PlaylistDialog::~PlaylistDialog()
263 {
264 	INFOLOG ( "DESTROY" );
265 }
266 
keyPressEvent(QKeyEvent * ev)267 void PlaylistDialog::keyPressEvent( QKeyEvent* ev )
268 {
269 	if(ev->key() == Qt::Key_Escape) {
270 		HydrogenApp::get_instance()->showPlaylistDialog();
271 	}
272 }
273 
closeEvent(QCloseEvent * ev)274 void PlaylistDialog::closeEvent( QCloseEvent* ev )
275 {
276 	HydrogenApp::get_instance()->showPlaylistDialog();
277 }
278 
addSong()279 void PlaylistDialog::addSong()
280 {
281 	QFileDialog fd(this);
282 	fd.setWindowTitle( tr( "Add Song to PlayList" ) );
283 	fd.setFileMode( QFileDialog::ExistingFiles );
284 	fd.setNameFilter( Filesystem::songs_filter_name );
285 	fd.setDirectory( Filesystem::songs_dir() );
286 
287 	if ( fd.exec() != QDialog::Accepted ) {
288 		return;
289 	}
290 
291 	foreach( QString filePath, fd.selectedFiles() ) {
292 		updatePlayListNode( filePath );
293 	}
294 }
295 
addCurrentSong()296 void PlaylistDialog::addCurrentSong()
297 {
298 	Song *	pSong = Hydrogen::get_instance()->getSong();
299 	QString filename = 	pSong->get_filename();
300 
301 	if (filename == "") {
302 		// just in case!
303 		QMessageBox::information ( this, "Hydrogen", tr ( "Please save your song first" ));
304 		return;
305 	}
306 	updatePlayListNode ( filename );
307 }
308 
removeFromList()309 void PlaylistDialog::removeFromList()
310 {
311 	QTreeWidgetItem* pPlaylistItem = m_pPlaylistTree->currentItem();
312 	int index = m_pPlaylistTree->indexOfTopLevelItem ( pPlaylistItem );
313 	QTreeWidgetItem * pTmpItem = m_pPlaylistTree->topLevelItem ( 1 );
314 
315 	if (pPlaylistItem == nullptr){
316 		QMessageBox::information ( this, "Hydrogen", tr ( "No Song selected!" ));
317 	} else {
318 		if (pTmpItem == nullptr){
319 			m_pPlaylistTree->clear();
320 			Playlist::get_instance()->clear();
321 			Playlist::get_instance()->setSelectedSongNr( -1 );
322 			Playlist::get_instance()->setActiveSongNumber( -1 );
323 			Playlist::get_instance()->setFilename( "" );
324 			setWindowTitle ( tr ( "Playlist Browser" ) );
325 
326 			return;
327 		} else {
328 			///avoid segfault if the last item will be removed!!
329 			///
330 			delete pPlaylistItem;
331 
332 			updatePlayListVector();
333 			if ( Playlist::get_instance()->getActiveSongNumber() == index ){
334 				Playlist::get_instance()->setActiveSongNumber( -1 );
335 			} else if ( Playlist::get_instance()->getActiveSongNumber() > index  ){
336 				Playlist::get_instance()->setActiveSongNumber(  Playlist::get_instance()->getActiveSongNumber() -1 );
337 			}
338 		}
339 	}
340 }
341 
clearPlaylist()342 void PlaylistDialog::clearPlaylist()
343 {
344 	bool DiscardChanges = false;
345 	bool IsModified = Playlist::get_instance()->getIsModified();
346 
347 	if( IsModified )
348 	{
349 		switch(QMessageBox::information( this, "Hydrogen",
350 										 tr("\nThe current playlist contains unsaved changes.\n"
351 												"Do you want to discard the changes?\n"),
352 										tr("&Discard"), tr("&Cancel"),
353 										 nullptr,      // Enter == button 0
354 										 2 ) ) { // Escape == button 1
355 		case 0: // Discard clicked or Alt+D pressed
356 			// don't save but exit
357 			DiscardChanges = true;
358 			break;
359 		case 1: // Cancel clicked or Alt+C pressed or Escape pressed
360 			// don't exit
361 			DiscardChanges = false;
362 			break;
363 		}
364 	}
365 
366 	if(!IsModified || (IsModified && DiscardChanges))
367 	{
368 		m_pPlaylistTree->clear();
369 		Playlist::get_instance()->clear();
370 		Playlist::get_instance()->setSelectedSongNr( -1 );
371 		Playlist::get_instance()->setActiveSongNumber( -1 );
372 		Playlist::get_instance()->setFilename ( "" );
373 		setWindowTitle ( tr ( "Playlist Browser" ) );
374 
375 		Playlist::get_instance()->setIsModified(false);
376 	}
377 	return;
378 }
379 
updatePlayListNode(QString file)380 void PlaylistDialog::updatePlayListNode ( QString file )
381 {
382 	QTreeWidgetItem* m_pPlaylistItem = new QTreeWidgetItem ( m_pPlaylistTree );
383 	m_pPlaylistItem->setText ( 0, file );
384 	m_pPlaylistItem->setText ( 1, tr("no Script") );
385 	m_pPlaylistItem->setCheckState( 2, Qt::Unchecked );
386 
387 	updatePlayListVector();
388 
389 	m_pPlaylistTree->setCurrentItem ( m_pPlaylistItem );
390 }
391 
loadList()392 void PlaylistDialog::loadList()
393 {
394 	QFileDialog fd(this);
395 	fd.setWindowTitle( tr( "Load Playlist" ) );
396 	fd.setFileMode( QFileDialog::ExistingFile );
397 	fd.setDirectory( Filesystem::playlists_dir() );
398 	fd.setNameFilter( Filesystem::playlists_filter_name );
399 
400 	if ( fd.exec() != QDialog::Accepted ) {
401 		return;
402 	}
403 
404 	QString filename = fd.selectedFiles().first();
405 
406 	bool relativePaths = Preferences::get_instance()->isPlaylistUsingRelativeFilenames();
407 	Playlist* pPlaylist = Playlist::load( filename, relativePaths);
408 	if ( ! pPlaylist ) {
409 		_ERRORLOG( "Error loading the playlist" );
410 		/* FIXME: get current instance (?) */
411 		pPlaylist = Playlist::get_instance();
412 	}
413 
414 	Playlist* playlist = Playlist::get_instance();
415 	if( playlist->size() > 0 ) {
416 		QTreeWidget* m_pPlaylist = m_pPlaylistTree;
417 		m_pPlaylist->clear();
418 
419 		for ( uint i = 0; i < playlist->size(); ++i ){
420 			QTreeWidgetItem* m_pPlaylistItem = new QTreeWidgetItem ( m_pPlaylistTree );
421 
422 			if( playlist->get( i )->fileExists ){
423 				m_pPlaylistItem->setText( 0, playlist->get( i )->filePath );
424 			} else {
425 				m_pPlaylistItem->setText( 0, tr("File not found: ") + playlist->get( i )->filePath );
426 			}
427 
428 			m_pPlaylistItem->setText ( 1, playlist->get( i )->scriptPath );
429 
430 			if ( playlist->get( i )->scriptEnabled ) {
431 				m_pPlaylistItem->setCheckState( 2, Qt::Checked );
432 			} else {
433 				m_pPlaylistItem->setCheckState( 2, Qt::Unchecked );
434 			}
435 		}
436 
437 		QTreeWidgetItem* m_pPlaylistItem = m_pPlaylist->topLevelItem ( 0 );
438 		m_pPlaylist->setCurrentItem ( m_pPlaylistItem );
439 		pPlaylist->setSelectedSongNr( 0 );
440 		setWindowTitle ( tr ( "Playlist Browser" ) + QString(" - ") + pPlaylist->getFilename() );
441 	}
442 }
443 
newScript()444 void PlaylistDialog::newScript()
445 {
446 
447 	Preferences *pPref = Preferences::get_instance();
448 
449 	QFileDialog fd(this);
450 	fd.setFileMode ( QFileDialog::AnyFile );
451 	fd.setNameFilter( Filesystem::scripts_filter_name );
452 	fd.setAcceptMode ( QFileDialog::AcceptSave );
453 	fd.setWindowTitle ( tr ( "New Script" ) );
454 	fd.setDirectory( Filesystem::scripts_dir() );
455 
456 	QString defaultFilename;
457 
458 	defaultFilename += ".sh";
459 
460 	fd.selectFile ( defaultFilename );
461 
462 	QString filename;
463 	if ( fd.exec() != QDialog::Accepted ) return;
464 
465 	filename = fd.selectedFiles().first();
466 
467 	if( filename.contains(" ", Qt::CaseInsensitive)){
468 		QMessageBox::information ( this, "Hydrogen", tr ( "Script name or path to the script contains whitespaces.\nIMPORTANT\nThe path to the script and the scriptname must be without whitespaces.") );
469 		return;
470 	}
471 
472 	QFile chngPerm ( filename );
473 	if (!chngPerm.open(QIODevice::WriteOnly | QIODevice::Text)) {
474 		return;
475 	}
476 
477 	QTextStream out(&chngPerm);
478 	out <<  "#!/bin/sh\n\n#have phun";
479 	chngPerm.close();
480 
481 	if (chngPerm.exists() ) {
482 		chngPerm.setPermissions( QFile::ReadOwner|QFile::WriteOwner|QFile::ExeOwner );
483 		QMessageBox::information ( this, "Hydrogen", tr ( "WARNING, the new file is executable by the owner of the file!" ) );
484 	}
485 
486 	if( pPref->getDefaultEditor().isEmpty() ){
487 		QMessageBox::information ( this, "Hydrogen", tr ( "No Default Editor Set. Please set your Default Editor\nDo not use a console based Editor\nSorry, but this will not work for the moment." ) );
488 
489 		static QString lastUsedDir = "/usr/bin/";
490 
491 		QFileDialog fd(this);
492 		fd.setFileMode ( QFileDialog::ExistingFile );
493 		fd.setDirectory ( lastUsedDir );
494 
495 		fd.setWindowTitle ( tr ( "Set your Default Editor" ) );
496 
497 		QString filename;
498 		if ( fd.exec() == QDialog::Accepted ){
499 			filename = fd.selectedFiles().first();
500 
501 			pPref->setDefaultEditor( filename );
502 		}
503 	}
504 
505 	QString  openfile = pPref->getDefaultEditor() + " " + filename + "&";
506 
507 	char *ofile;
508 	ofile = new char[openfile.length() + 1];
509 	strcpy(ofile, openfile.toLatin1());
510 	std::system( ofile );
511 	delete [] ofile;
512 	return;
513 }
514 
saveListAs()515 void PlaylistDialog::saveListAs()
516 {
517 	QFileDialog fd(this);
518 	fd.setWindowTitle( tr( "Save Playlist" ) );
519 	fd.setFileMode( QFileDialog::AnyFile );
520 	fd.setNameFilter( Filesystem::playlists_filter_name );
521 	fd.setAcceptMode( QFileDialog::AcceptSave );
522 	fd.setDirectory( Filesystem::playlists_dir() );
523 	fd.selectFile( Filesystem::untitled_playlist_file_name() );
524 	fd.setDefaultSuffix( Filesystem::playlist_ext );
525 
526 	if ( fd.exec() != QDialog::Accepted ) {
527 		return;
528 	}
529 
530 	QString filename = fd.selectedFiles().first();
531 
532 	Playlist* pPlaylist = Playlist::get_instance();
533 	bool relativePaths = Preferences::get_instance()->isPlaylistUsingRelativeFilenames();
534 	if ( Files::savePlaylistPath( filename, pPlaylist, relativePaths ) == nullptr ) {
535 		return;
536 	}
537 
538 	pPlaylist->setIsModified( false );
539 
540 	setWindowTitle( tr( "Playlist Browser" ) + QString(" - %1").arg( filename ) );
541 }
542 
saveList()543 void PlaylistDialog::saveList()
544 {
545 	Playlist* pPlaylist = Playlist::get_instance();
546 	if ( pPlaylist->getFilename().isEmpty() ) {
547 		return saveListAs();
548 	}
549 
550 	bool relativePaths = Preferences::get_instance()->isPlaylistUsingRelativeFilenames();
551 	if ( Files::savePlaylistPath( pPlaylist->getFilename(), pPlaylist, relativePaths ) == nullptr ) {
552 		return;
553 	}
554 
555 	pPlaylist->setIsModified( false );
556 }
557 
loadScript()558 void PlaylistDialog::loadScript()
559 {
560 
561 	QTreeWidgetItem* pPlaylistItem = m_pPlaylistTree->currentItem();
562 	if ( pPlaylistItem == nullptr ){
563 		QMessageBox::information ( this, "Hydrogen", tr ( "No Song in List or no Song selected!" ) );
564 		return;
565 	}
566 
567 	static QString lastUsedDir = Filesystem::scripts_dir();
568 
569 	QFileDialog fd(this);
570 	fd.setFileMode ( QFileDialog::ExistingFile );
571 	fd.setDirectory ( lastUsedDir );
572 	fd.setNameFilter ( tr ( "Hydrogen Playlist (*.sh)" ) );
573 	fd.setWindowTitle ( tr ( "Add Script to selected Song" ) );
574 
575 	QString filename;
576 	if ( fd.exec() == QDialog::Accepted ){
577 		filename = fd.selectedFiles().first();
578 
579 		if( filename.contains(" ", Qt::CaseInsensitive)){
580 			QMessageBox::information ( this, "Hydrogen", tr ( "Script name or path to the script contains whitespaces.\nIMPORTANT\nThe path to the script and the scriptname must without whitespaces.") );
581 			return;
582 		}
583 
584 		pPlaylistItem->setText ( 1, filename );
585 		updatePlayListVector();
586 
587 	}
588 }
589 
removeScript()590 void PlaylistDialog::removeScript()
591 {
592 	QTreeWidgetItem* m_pPlaylistItem = m_pPlaylistTree->currentItem();
593 
594 
595 	if (m_pPlaylistItem == nullptr){
596 		QMessageBox::information ( this, "Hydrogen", tr ( "No Song selected!" ));
597 		return;
598 	} else {
599 		QString selected;
600 		selected = m_pPlaylistItem->text ( 1 );
601 		if( !QFile( selected ).exists()  ){
602 			QMessageBox::information ( this, "Hydrogen", tr ( "No Script in use!" ));
603 			return;
604 		} else {
605 			m_pPlaylistItem->setText ( 1, tr("no Script") );
606 
607 			m_pPlaylistItem->setCheckState( 2, Qt::Unchecked );
608 			updatePlayListVector();
609 		}
610 	}
611 
612 }
613 
editScript()614 void PlaylistDialog::editScript()
615 {
616 	Preferences *pPref = Preferences::get_instance();
617 	if( pPref->getDefaultEditor().isEmpty() ){
618 		QMessageBox::information ( this, "Hydrogen", tr ( "No Default Editor Set. Please set your Default Editor\nDo not use a console based Editor\nSorry, but this will not work for the moment." ) );
619 
620 		static QString lastUsedDir = "/usr/bin/";
621 
622 		QFileDialog fd(this);
623 		fd.setFileMode ( QFileDialog::ExistingFile );
624 		fd.setDirectory ( lastUsedDir );
625 
626 		fd.setWindowTitle ( tr ( "Set your Default Editor" ) );
627 
628 		QString filename;
629 		if ( fd.exec() == QDialog::Accepted ){
630 			filename = fd.selectedFiles().first();
631 
632 			pPref->setDefaultEditor( filename );
633 		}
634 	}
635 
636 	QTreeWidgetItem* pPlaylistItem = m_pPlaylistTree->currentItem();
637 
638 	if ( pPlaylistItem == nullptr ){
639 		QMessageBox::information ( this, "Hydrogen", tr ( "No Song selected!" ) );
640 		return;
641 	}
642 	QString selected;
643 	selected = pPlaylistItem->text ( 1 );
644 
645 	QString filename = pPref->getDefaultEditor() + " " + selected + "&";
646 
647 	if( !QFile( selected ).exists()  ){
648 		QMessageBox::information ( this, "Hydrogen", tr ( "No Script selected!" ));
649 		return;
650 	}
651 
652 	char *file;
653 	file = new char[ filename.length() + 1 ];
654 	strcpy( file , filename.toLatin1() );
655 	std::system( file );
656 	delete [] file;
657 	return;
658 
659 }
660 
o_upBClicked()661 void PlaylistDialog::o_upBClicked()
662 {
663 	timer->stop();
664 
665 	Playlist* pPlaylist = Playlist::get_instance();
666 
667 	QTreeWidget* pPlaylistTree = m_pPlaylistTree;
668 	QTreeWidgetItem* pPlaylistTreeItem = m_pPlaylistTree->currentItem();
669 	int index = pPlaylistTree->indexOfTopLevelItem ( pPlaylistTreeItem );
670 
671 	if (index == 0 ){
672 		timer->start( 1000 );
673 		return;
674 	}
675 
676 	QTreeWidgetItem* tmpPlaylistItem = pPlaylistTree->takeTopLevelItem ( index );
677 
678 	pPlaylistTree->insertTopLevelItem ( index -1, tmpPlaylistItem );
679 	pPlaylistTree->setCurrentItem ( tmpPlaylistItem );
680 
681 	if ( pPlaylist->getSelectedSongNr() >= 0 ){
682 		pPlaylist->setSelectedSongNr( pPlaylist->getSelectedSongNr() -1 );
683 	}
684 
685 	if ( pPlaylist->getActiveSongNumber() == index ){
686 		pPlaylist->setActiveSongNumber( pPlaylist->getActiveSongNumber() -1 );
687 	}else if ( pPlaylist->getActiveSongNumber() == index -1 ){
688 		pPlaylist->setActiveSongNumber( pPlaylist->getActiveSongNumber() +1 );
689 	}
690 
691 	updatePlayListVector();
692 }
693 
o_downBClicked()694 void PlaylistDialog::o_downBClicked()
695 {
696 	timer->stop();
697 	Playlist* pPlaylist = Playlist::get_instance();
698 
699 	QTreeWidget* m_pPlaylist = m_pPlaylistTree;
700 	int length = m_pPlaylist->topLevelItemCount();
701 	QTreeWidgetItem* m_pPlaylistItem = m_pPlaylistTree->currentItem();
702 	int index = m_pPlaylist->indexOfTopLevelItem ( m_pPlaylistItem );
703 
704 	if ( index == length - 1){
705 		timer->start( 1000 );
706 		return;
707 	}
708 
709 	QTreeWidgetItem* pTmpPlaylistItem = m_pPlaylist->takeTopLevelItem ( index );
710 
711 	m_pPlaylist->insertTopLevelItem ( index +1, pTmpPlaylistItem );
712 	m_pPlaylist->setCurrentItem ( pTmpPlaylistItem );
713 
714 	if ( pPlaylist->getSelectedSongNr() >= 0 ) {
715 		pPlaylist->setSelectedSongNr( pPlaylist->getSelectedSongNr() +1 );
716 	}
717 
718 	if (pPlaylist ->getActiveSongNumber() == index ){
719 		pPlaylist->setActiveSongNumber( pPlaylist->getActiveSongNumber() +1 );
720 	}else if ( pPlaylist->getActiveSongNumber() == index +1 ){
721 		pPlaylist->setActiveSongNumber( pPlaylist->getActiveSongNumber() -1 );
722 	}
723 	updatePlayListVector();
724 
725 }
726 
on_m_pPlaylistTree_itemClicked(QTreeWidgetItem * item,int column)727 void PlaylistDialog::on_m_pPlaylistTree_itemClicked ( QTreeWidgetItem * item, int column )
728 {
729 	if ( column == 2 ){
730 		QString selected;
731 		selected = item->text ( 1 );
732 
733 		if( !QFile( selected ).exists() ){
734 			QMessageBox::information ( this, "Hydrogen", tr ( "No Script!" ));
735 			item->setCheckState( 2, Qt::Unchecked );
736 			return;
737 		}
738 		updatePlayListVector();
739 	}
740 	return;
741 }
742 
nodePlayBTN(Button * ref)743 void PlaylistDialog::nodePlayBTN( Button* ref )
744 {
745 	Hydrogen *		pEngine = Hydrogen::get_instance();
746 	HydrogenApp *	pH2App = HydrogenApp::get_instance();
747 
748 	if (ref->isPressed()) {
749 		QTreeWidgetItem* m_pPlaylistItem = m_pPlaylistTree->currentItem();
750 		if ( m_pPlaylistItem == nullptr ){
751 			QMessageBox::information ( this, "Hydrogen", tr ( "No valid song selected!" ) );
752 			m_pPlayBtn->setPressed(false);
753 			return;
754 		}
755 		QString selected = "";
756 		selected = m_pPlaylistItem->text ( 0 );
757 
758 		if( selected == pEngine->getSong()->get_filename()){
759 			pEngine->sequencer_play();
760 			return;
761 		}
762 
763 		if ( pEngine->getState() == STATE_PLAYING ){
764 			pEngine->sequencer_stop();
765 		}
766 
767 		Song *pSong = Song::load ( selected );
768 		if ( pSong == nullptr ){
769 			QMessageBox::information ( this, "Hydrogen", tr ( "Error loading song." ) );
770 			m_pPlayBtn->setPressed(false);
771 			return;
772 		}
773 
774 		QTreeWidget* m_pPlaylist = m_pPlaylistTree;
775 		int index = m_pPlaylist->indexOfTopLevelItem ( m_pPlaylistItem );
776 		Playlist::get_instance()->setActiveSongNumber( index );
777 
778 		pH2App->setSong ( pSong );
779 		pEngine->setSelectedPatternNumber ( 0 );
780 
781 		pEngine->sequencer_play();
782 	}else
783 	{
784 		pEngine->sequencer_stop();
785 		pH2App->setStatusBarMessage(tr("Pause."), 5000);
786 	}
787 }
788 
nodeStopBTN(Button * ref)789 void PlaylistDialog::nodeStopBTN( Button* ref )
790 {
791 	UNUSED( ref );
792 	m_pPlayBtn->setPressed(false);
793 	Hydrogen::get_instance()->sequencer_stop();
794 	Hydrogen::get_instance()->setPatternPos ( 0 );
795 }
796 
ffWDBtnClicked(Button * ref)797 void PlaylistDialog::ffWDBtnClicked( Button* ref)
798 {
799 	UNUSED( ref );
800 	Hydrogen *pEngine = Hydrogen::get_instance();
801 	pEngine->setPatternPos( pEngine->getPatternPos() + 1 );
802 }
803 
rewindBtnClicked(Button * ref)804 void PlaylistDialog::rewindBtnClicked( Button* ref )
805 {
806 	UNUSED( ref );
807 	Hydrogen *pEngine = Hydrogen::get_instance();
808 	pEngine->setPatternPos( pEngine->getPatternPos() - 1 );
809 }
810 
on_m_pPlaylistTree_itemDoubleClicked()811 void PlaylistDialog::on_m_pPlaylistTree_itemDoubleClicked ()
812 {
813 	QTreeWidgetItem* pPlaylistItem = m_pPlaylistTree->currentItem();
814 	if ( pPlaylistItem == nullptr ){
815 		QMessageBox::information ( this, "Hydrogen", tr ( "No Song selected!" ) );
816 		return;
817 	}
818 
819 	QString selected;
820 	selected = pPlaylistItem->text ( 0 );
821 
822 	int index = m_pPlaylistTree->indexOfTopLevelItem ( pPlaylistItem );
823 	Playlist::get_instance()->setSelectedSongNr( index );
824 	Playlist::get_instance()->setActiveSongNumber( index );
825 
826 	HydrogenApp *pH2App = HydrogenApp::get_instance();
827 	Hydrogen *pEngine = Hydrogen::get_instance();
828 
829 	if ( pEngine->getState() == STATE_PLAYING ){
830 		pEngine->sequencer_stop();
831 	}
832 
833 	m_pPlayBtn->setPressed(false);
834 
835 	Timeline* pTimeline = pEngine->getTimeline();
836 	pTimeline->m_timelinetagvector.clear();
837 
838 	Song *pSong = Song::load ( selected );
839 	if ( pSong == nullptr ){
840 		QMessageBox::information ( this, "Hydrogen", tr ( "Error loading song." ) );
841 		return;
842 	}
843 
844 	pH2App->setSong ( pSong );
845 	pEngine->setSelectedPatternNumber ( 0 );
846 
847 	HydrogenApp::get_instance()->getSongEditorPanel()->updatePositionRuler();
848 	pH2App->setStatusBarMessage( tr( "Playlist: set song no. %1" ).arg( index +1 ), 5000 );
849 
850 	HydrogenApp::get_instance()->getInstrumentRack()->getSoundLibraryPanel()->update_background_color();
851 
852 	EventQueue::get_instance()->push_event( EVENT_METRONOME, 3 );
853 
854 ///exec script
855 ///this is very very simple and only an experiment
856 #ifdef WIN32
857 	//I know nothing about windows scripts -wolke-
858 	return;
859 #else
860 	QString execscript;
861 	selected = pPlaylistItem->text ( 1 );
862 	bool execcheckbox = pPlaylistItem->checkState ( 2 );
863 
864 	if( execcheckbox == false){
865 		//QMessageBox::information ( this, "Hydrogen", tr ( "No Script selected!" ));
866 		return;
867 	}
868 
869 	if( execscript == "Script not used"){
870 		//QMessageBox::information ( this, "Hydrogen", tr ( "Script not in use!" ));
871 		return;
872 	}
873 
874 	char *file;
875 	file = new char[ selected.length() + 1 ];
876 	strcpy( file , selected.toLatin1() );
877 	std::system( file );
878 	delete [] file;
879 	return;
880 #endif
881 
882 }
883 
884 
updatePlayListVector()885 void PlaylistDialog::updatePlayListVector()
886 {
887 	int length = m_pPlaylistTree->topLevelItemCount();
888 
889 	Playlist::get_instance()->clear();
890 
891 	for (int i = 0 ;i < length; i++){
892 		QTreeWidgetItem * pPlaylistItem = m_pPlaylistTree->topLevelItem ( i );
893 
894 		Playlist::Entry* entry = new Playlist::Entry();
895 		entry->filePath = pPlaylistItem->text( 0 );
896 		entry->scriptPath = pPlaylistItem->text( 1 );
897 		entry->scriptEnabled = pPlaylistItem->checkState( 2 );
898 
899 		Playlist::get_instance()->add( entry );
900 		Playlist::get_instance()->setIsModified(true);
901 	}
902 	timer->start( 1000 );
903 }
904 
905 
updateActiveSongNumber()906 void PlaylistDialog::updateActiveSongNumber()
907 {
908 	for ( uint i = 0; i < Playlist::get_instance()->size(); ++i ){
909 		if ( !m_pPlaylistTree->topLevelItem( i ) ) {
910 			break;
911 		}
912 		( m_pPlaylistTree->topLevelItem( i ) )->setBackground( 0, QBrush() );
913 		( m_pPlaylistTree->topLevelItem( i ) )->setBackground( 1, QBrush() );
914 		( m_pPlaylistTree->topLevelItem( i ) )->setBackground( 2, QBrush() );
915 
916 	}
917 
918 	int selected = Playlist::get_instance()->getActiveSongNumber();
919 	if ( selected == -1 ) {
920 		return;
921 	}
922 
923 	QTreeWidgetItem* pPlaylistItem = m_pPlaylistTree->topLevelItem ( selected );
924 	if ( pPlaylistItem != nullptr ){
925 		pPlaylistItem->setBackground( 0, QColor( 50, 50, 50) );
926 		pPlaylistItem->setBackground( 1, QColor( 50, 50, 50) );
927 		pPlaylistItem->setBackground( 2, QColor( 50, 50, 50) );
928 	}
929 }
930 
931 
eventFilter(QObject * o,QEvent * e)932 bool PlaylistDialog::eventFilter ( QObject *o, QEvent *e )
933 {
934 	UNUSED ( o );
935 	if ( e->type() == QEvent::KeyPress ) {
936 		QKeyEvent *k = ( QKeyEvent * ) e;
937 
938 		switch ( k->key() ) {
939 		case  Qt::Key_F5 :
940 			if(    Playlist::get_instance()->size() == 0
941 				|| Playlist::get_instance()->getActiveSongNumber() <=0) {
942 				break;
943 			}
944 
945 			Playlist::get_instance()->setNextSongByNumber(Playlist::get_instance()->getActiveSongNumber()-1);
946 			return true;
947 			break;
948 		case  Qt::Key_F6 :
949 			if(		Playlist::get_instance()->size() == 0
950 				||	Playlist::get_instance()->getActiveSongNumber() >= Playlist::get_instance()->size() -1) {
951 				break;
952 			}
953 
954 			Playlist::get_instance()->setNextSongByNumber(Playlist::get_instance()->getActiveSongNumber()+1);
955 			return true;
956 			break;
957 		}
958 	} else {
959 		return false; // standard event processing
960 	}
961 
962 	return false;
963 }
964 
loadListByFileName(QString filename)965 bool PlaylistDialog::loadListByFileName( QString filename )
966 {
967 	bool bUseRelativeFilenames = Preferences::get_instance()->isPlaylistUsingRelativeFilenames();
968 
969 	Playlist* pPlaylist = Playlist::load ( filename, bUseRelativeFilenames );
970 	if ( !pPlaylist ) {
971 		_ERRORLOG( "Error loading the playlist" );
972 		return false;
973 	}
974 
975 	Preferences::get_instance()->setLastPlaylistFilename( filename );
976 
977 	Playlist* playlist = Playlist::get_instance();
978 	if ( playlist->size() > 0 ) {
979 		QTreeWidget* m_pPlaylist = m_pPlaylistTree;
980 		m_pPlaylist->clear();
981 
982 		for ( uint i = 0; i < playlist->size(); ++i ){
983 			QTreeWidgetItem* m_pPlaylistItem = new QTreeWidgetItem ( m_pPlaylistTree );
984 			m_pPlaylistItem->setText( 0, playlist->get( i )->filePath );
985 			m_pPlaylistItem->setText( 1, playlist->get( i )->scriptPath );
986 
987 			if ( playlist->get( i )->scriptEnabled ) {
988 				m_pPlaylistItem->setCheckState( 2, Qt::Checked );
989 			} else {
990 				m_pPlaylistItem->setCheckState( 2, Qt::Unchecked );
991 			}
992 		}
993 
994 		QTreeWidgetItem* m_pPlaylistItem = m_pPlaylist->topLevelItem ( 0 );
995 		m_pPlaylist->setCurrentItem ( m_pPlaylistItem );
996 		pPlaylist->setSelectedSongNr( 0 );
997 		setWindowTitle ( tr ( "Playlist Browser" ) + QString(" - ") + pPlaylist->getFilename() );
998 	}
999 
1000 	return true;
1001 }
1002