1 /***************************************************************************
2 * Copyright (C) 2004-2007 by Mark Kretschmann <markey@web.de> *
3 * 2005-2007 by Seb Ruiz <ruiz@kde.org> *
4 * 2006 by Alexandre Oliveira <aleprj@gmail.com> *
5 * 2006 by Martin Ellis <martin.ellis@kdemail.net> *
6 * 2007 by Leonardo Franchi <lfranchi@gmail.com> *
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. *
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. *
18 * You should have received a copy of the GNU General Public License *
19 * along with this program; if not, write to the *
20 * Free Software Foundation, Inc., *
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
22 ***************************************************************************/
24 #define DEBUG_PREFIX "ScriptManager"
26 #include "scriptmanager.h"
29 #include "amarokconfig.h"
31 #include "enginecontroller.h"
32 #include "metabundle.h"
34 #include "ContextStatusBar.h"
36 #include <KAboutApplicationDialog>
38 #include <KApplication>
39 #include <KFileDialog>
40 #include <KIconLoader>
41 #include <KIO/NetAccess>
44 #include <KMessageBox>
45 #include <KProtocolManager>
46 #include <KPushButton>
48 #include <KStandardDirs>
51 #include <KWindowSystem>
64 #include <sys/types.h>
69 proxyForUrl(const QString
& url
)
75 if ( KProtocolManager::proxyForUrl( kurl
) !=
76 QString::fromLatin1( "DIRECT" ) ) {
77 KProtocolManager::slaveProtocol ( kurl
, proxy
);
84 proxyForProtocol(const QString
& protocol
)
86 return KProtocolManager::proxyFor( protocol
);
92 ////////////////////////////////////////////////////////////////////////////////
93 // class AmarokScriptNewStuff
94 ////////////////////////////////////////////////////////////////////////////////
97 * GHNS Customised Download implementation.
99 #if 0 //TODO: PORT to KNS2
100 class AmarokScriptNewStuff
: public KNewStuff
103 AmarokScriptNewStuff(const QString
&type
, QWidget
*parentWidget
=0)
104 : KNewStuff( type
, parentWidget
)
107 bool install( const QString
& fileName
)
109 return ScriptManager::instance()->slotInstallScript( fileName
);
112 virtual bool createUploadFile( const QString
& ) { return false; } //make compile on kde 3.5
116 ////////////////////////////////////////////////////////////////////////////////
117 // class ScriptManager
118 ////////////////////////////////////////////////////////////////////////////////
120 ScriptManager
* ScriptManager::s_instance
= 0;
123 ScriptManager::ScriptManager( QWidget
*parent
, const char *name
)
125 , EngineObserver( EngineController::instance() )
126 , m_gui( new Ui::ScriptManagerBase() )
129 setObjectName( name
);
132 setDefaultButton( Close
);
133 showButtonSeparator( true );
138 kapp
->setTopWidget( this );
139 setCaption( KDialog::makeStandardCaption( i18n( "Script Manager" ) ) );
141 // Gives the window a small title bar, and skips a taskbar entry
143 KWindowSystem::setType( winId(), NET::Utility
);
144 KWindowSystem::setState( winId(), NET::SkipTaskbar
);
147 QWidget
* main
= new QWidget( this );
148 m_gui
->setupUi( main
);
150 setMainWidget( main
);
153 m_generalCategory
= new QTreeWidgetItem( m_gui
->treeWidget
);
154 m_lyricsCategory
= new QTreeWidgetItem( m_gui
->treeWidget
);
155 m_scoreCategory
= new QTreeWidgetItem( m_gui
->treeWidget
);
156 m_transcodeCategory
= new QTreeWidgetItem( m_gui
->treeWidget
);
157 m_contextCategory
= new QTreeWidgetItem( m_gui
->treeWidget
);
159 m_generalCategory
->setText( 0, i18n( "General" ) );
160 m_lyricsCategory
->setText( 0, i18n( "Lyrics" ) );
161 m_scoreCategory
->setText( 0, i18n( "Score" ) );
162 m_transcodeCategory
->setText( 0, i18n( "Transcoding" ) );
163 m_contextCategory
->setText( 0, i18n( "Context Browser" ) );
165 m_generalCategory
->setFlags( Qt::ItemIsEnabled
);
166 m_lyricsCategory
->setFlags( Qt::ItemIsEnabled
);
167 m_scoreCategory
->setFlags( Qt::ItemIsEnabled
);
168 m_transcodeCategory
->setFlags( Qt::ItemIsEnabled
);
169 m_contextCategory
->setFlags( Qt::ItemIsEnabled
);
171 m_generalCategory
->setIcon( 0, SmallIcon( Amarok::icon( "files" ) ) );
172 m_lyricsCategory
->setIcon( 0, SmallIcon( Amarok::icon( "files" ) ) );
173 m_scoreCategory
->setIcon( 0, SmallIcon( Amarok::icon( "files" ) ) );
174 m_transcodeCategory
->setIcon( 0, SmallIcon( Amarok::icon( "files" ) ) );
175 m_contextCategory
->setIcon( 0, SmallIcon( Amarok::icon( "files" ) ) );
177 // Restore the open/closed state of the category items
178 KConfigGroup config
= Amarok::config( "ScriptManager" );
179 m_generalCategory
->setExpanded( config
.readEntry( "General category open", false ) );
180 m_lyricsCategory
->setExpanded( config
.readEntry( "Lyrics category open", false ) );
181 m_scoreCategory
->setExpanded( config
.readEntry( "Score category State", false ) );
182 m_transcodeCategory
->setExpanded( config
.readEntry( "Transcode category open", false ) );
183 m_contextCategory
->setExpanded( config
.readEntry( "Context category open", false ) );
185 connect( m_gui
->treeWidget
, SIGNAL( currentItemChanged( QTreeWidgetItem
*, QTreeWidgetItem
* ) ), SLOT( slotCurrentChanged( QTreeWidgetItem
* ) ) );
186 connect( m_gui
->treeWidget
, SIGNAL( itemDoubleClicked( QTreeWidgetItem
*, int ) ), SLOT( slotRunScript() ) );
187 connect( m_gui
->treeWidget
, SIGNAL( customContextMenuRequested ( const QPoint
& ) ), SLOT( slotShowContextMenu( const QPoint
& ) ) );
189 connect( m_gui
->installButton
, SIGNAL( clicked() ), SLOT( slotInstallScript() ) );
190 connect( m_gui
->retrieveButton
, SIGNAL( clicked() ), SLOT( slotRetrieveScript() ) );
191 connect( m_gui
->uninstallButton
, SIGNAL( clicked() ), SLOT( slotUninstallScript() ) );
192 connect( m_gui
->runButton
, SIGNAL( clicked() ), SLOT( slotRunScript() ) );
193 connect( m_gui
->stopButton
, SIGNAL( clicked() ), SLOT( slotStopScript() ) );
194 connect( m_gui
->configureButton
, SIGNAL( clicked() ), SLOT( slotConfigureScript() ) );
195 connect( m_gui
->aboutButton
, SIGNAL( clicked() ), SLOT( slotAboutScript() ) );
197 m_gui
->installButton
->setIcon( KIcon( Amarok::icon( "files" ) ) );
198 m_gui
->retrieveButton
->setIcon( KIcon( Amarok::icon( "download" ) ) );
199 m_gui
->uninstallButton
->setIcon( KIcon( Amarok::icon( "remove" ) ) );
200 m_gui
->runButton
->setIcon( KIcon( Amarok::icon( "play" ) ) );
201 m_gui
->stopButton
->setIcon( KIcon( Amarok::icon( "stop" ) ) );
202 m_gui
->configureButton
->setIcon( KIcon( Amarok::icon( "configure" ) ) );
203 m_gui
->aboutButton
->setIcon( KIcon( Amarok::icon( "info" ) ) );
205 QSize sz
= sizeHint();
206 setMinimumSize( qMax( 350, sz
.width() ), qMax( 250, sz
.height() ) );
207 resize( sizeHint() );
209 //FIXME: contex tbrowser changes
210 // connect( this, SIGNAL(lyricsScriptChanged()), ContextBrowser::instance(), SLOT( lyricsScriptChanged() ) );
212 // Delay this call via eventloop, because it's a bit slow and would block
213 QTimer::singleShot( 0, this, SLOT( findScripts() ) );
217 ScriptManager::~ScriptManager()
221 QStringList runningScripts
;
222 foreach( const QString
&key
, m_scripts
.keys() ) {
223 if( m_scripts
[key
].process
) {
224 terminateProcess( &m_scripts
[key
].process
);
225 runningScripts
<< key
;
230 KConfigGroup config
= Amarok::config( "ScriptManager" );
231 config
.writeEntry( "Running Scripts", runningScripts
);
233 // Save the open/closed state of the category items
234 config
.writeEntry( "General category open", m_generalCategory
->isExpanded() );
235 config
.writeEntry( "Lyrics category open", m_lyricsCategory
->isExpanded() );
236 config
.writeEntry( "Score category open", m_scoreCategory
->isExpanded() );
237 config
.writeEntry( "Transcode category open", m_transcodeCategory
->isExpanded() );
238 config
.writeEntry( "Context category open", m_contextCategory
->isExpanded() );
243 ////////////////////////////////////////////////////////////////////////////////
245 ////////////////////////////////////////////////////////////////////////////////
248 ScriptManager::runScript( const QString
& name
, bool silent
)
250 if( !m_scripts
.contains( name
) )
253 m_gui
->treeWidget
->setCurrentItem( m_scripts
[name
].li
);
254 return slotRunScript( silent
);
259 ScriptManager::stopScript( const QString
& name
)
261 if( !m_scripts
.contains( name
) )
264 m_gui
->treeWidget
->setCurrentItem( m_scripts
[name
].li
);
272 ScriptManager::listRunningScripts()
274 QStringList runningScripts
;
275 foreach( const QString
&key
, m_scripts
.keys() )
276 if( m_scripts
[key
].process
)
277 runningScripts
<< key
;
279 return runningScripts
;
284 ScriptManager::customMenuClicked( const QString
& message
)
286 notifyScripts( "customMenuClicked: " + message
);
291 ScriptManager::specForScript( const QString
& name
)
293 if( !m_scripts
.contains( name
) )
295 QFileInfo
info( m_scripts
[name
].url
.path() );
296 const QString specPath
= info
.path() + '/' + info
.completeBaseName() + ".spec";
303 ScriptManager::notifyFetchLyrics( const QString
& artist
, const QString
& title
)
305 const QString args
= QUrl::toPercentEncoding( artist
) + ' ' + QUrl::toPercentEncoding( title
);
306 notifyScripts( "fetchLyrics " + args
);
311 ScriptManager::notifyFetchLyricsByUrl( const QString
& url
)
313 notifyScripts( "fetchLyricsByUrl " + url
);
317 void ScriptManager::notifyTranscode( const QString
& srcUrl
, const QString
& filetype
)
319 notifyScripts( "transcode " + srcUrl
+ ' ' + filetype
);
324 ScriptManager::requestNewScore( const QString
&url
, double prevscore
, int playcount
, int length
, float percentage
, const QString
&reason
)
326 const QString script
= ensureScoreScriptRunning();
327 if( script
.isNull() )
329 Amarok::ContextStatusBar::instance()->longMessage(
330 i18n( "No score scripts were found, or none of them worked. Automatic scoring will be disabled. Sorry." ),
331 KDE::StatusBar::Sorry
);
335 m_scripts
[script
].process
->writeStdin(
336 QString( "requestNewScore %6 %1 %2 %3 %4 %5" )
342 .arg( QString( QUrl::toPercentEncoding( url
) ) ) ); //last because it might have %s
345 ////////////////////////////////////////////////////////////////////////////////
347 ////////////////////////////////////////////////////////////////////////////////
350 ScriptManager::findScripts() //SLOT
352 const QStringList allFiles
= KGlobal::dirs()->findAllResources( "data", "amarok/scripts/*",KStandardDirs::Recursive
);
354 // Add found scripts to treeWidget:
355 foreach( const QString
&str
, allFiles
)
356 if( QFileInfo( str
).isExecutable() )
361 KConfigGroup config
= Amarok::config( "ScriptManager" );
362 const QStringList runningScripts
= config
.readEntry( "Running Scripts", QStringList() );
364 foreach( const QString
&str
, runningScripts
)
365 if( m_scripts
.contains( str
) ) {
366 debug() << "Auto-running script: " << str
;
367 m_gui
->treeWidget
->setCurrentItem( m_scripts
[str
].li
);
371 //FIXME m_gui->treeWidget->setCurrentItem( m_gui->treeWidget->firstChild() );
372 slotCurrentChanged( m_gui
->treeWidget
->currentItem() );
377 ScriptManager::slotCurrentChanged( QTreeWidgetItem
* item
)
379 const bool isCategory
= item
== m_generalCategory
||
380 item
== m_lyricsCategory
||
381 item
== m_scoreCategory
||
382 item
== m_transcodeCategory
;
384 if( item
&& !isCategory
) {
385 const QString name
= item
->text( 0 );
386 m_gui
->uninstallButton
->setEnabled( true );
387 m_gui
->runButton
->setEnabled( !m_scripts
[name
].process
);
388 m_gui
->stopButton
->setEnabled( m_scripts
[name
].process
);
389 m_gui
->configureButton
->setEnabled( m_scripts
[name
].process
);
390 m_gui
->aboutButton
->setEnabled( true );
393 m_gui
->uninstallButton
->setEnabled( false );
394 m_gui
->runButton
->setEnabled( false );
395 m_gui
->stopButton
->setEnabled( false );
396 m_gui
->configureButton
->setEnabled( false );
397 m_gui
->aboutButton
->setEnabled( false );
403 ScriptManager::slotInstallScript( const QString
& path
)
405 QString _path
= path
;
407 if( path
.isNull() ) {
408 _path
= KFileDialog::getOpenFileName( KUrl(),
409 "*.amarokscript.tar *.amarokscript.tar.bz2 *.amarokscript.tar.gz|"
410 + i18n( "Script Packages (*.amarokscript.tar, *.amarokscript.tar.bz2, *.amarokscript.tar.gz)" )
412 if( _path
.isNull() ) return false;
415 KTar
archive( _path
);
416 if( !archive
.open( QIODevice::ReadOnly
) ) {
417 KMessageBox::sorry( 0, i18n( "Could not read this package." ) );
421 QString destination
= Amarok::saveLocation( "scripts/" );
422 const KArchiveDirectory
* const archiveDir
= archive
.directory();
424 // Prevent installing a script that's already installed
425 const QString scriptFolder
= destination
+ archiveDir
->entries().first();
426 if( QFile::exists( scriptFolder
) ) {
427 KMessageBox::error( 0, i18n( "A script with the name '%1' is already installed. "
428 "Please uninstall it first.", archiveDir
->entries().first() ) );
432 archiveDir
->copyTo( destination
);
433 m_installSuccess
= false;
434 recurseInstall( archiveDir
, destination
);
436 if( m_installSuccess
) {
437 KMessageBox::information( 0, i18n( "Script successfully installed." ) );
441 KMessageBox::sorry( 0, i18n( "<p>Script installation failed.</p>"
442 "<p>The package did not contain an executable file. "
443 "Please inform the package maintainer about this error.</p>" ) );
445 // Delete directory recursively
446 KIO::NetAccess::del( KUrl( scriptFolder
), 0 );
454 ScriptManager::recurseInstall( const KArchiveDirectory
* archiveDir
, const QString
& destination
)
456 const QStringList entries
= archiveDir
->entries();
458 foreach( const QString
&entry
, entries
) {
459 const KArchiveEntry
* const archEntry
= archiveDir
->entry( entry
);
461 if( archEntry
->isDirectory() ) {
462 const KArchiveDirectory
* const dir
= static_cast<const KArchiveDirectory
*>( archEntry
);
463 recurseInstall( dir
, destination
+ entry
+ '/' );
466 ::chmod( QFile::encodeName( destination
+ entry
), archEntry
->permissions() );
468 if( QFileInfo( destination
+ entry
).isExecutable() ) {
469 loadScript( destination
+ entry
);
470 m_installSuccess
= true;
478 ScriptManager::slotRetrieveScript()
480 #if 0 //FIXME: PORT To KNS2
481 // Delete KNewStuff's configuration entries. These entries reflect which scripts
482 // are already installed. As we cannot yet keep them in sync after uninstalling
483 // scripts, we deactivate the check marks entirely.
484 Amarok::config()->deleteGroup( "KNewStuffStatus" );
486 // we need this because KNewStuffGeneric's install function isn't clever enough
487 AmarokScriptNewStuff
*kns
= new AmarokScriptNewStuff( "amarok/script", this );
488 KNS::Engine
*engine
= new KNS::Engine( kns
, "amarok/script", this );
489 KNS::DownloadDialog
*d
= new KNS::DownloadDialog( engine
, this );
490 d
->setType( "amarok/script" );
491 // you have to do this by hand when providing your own Engine
492 KNS::ProviderLoader
*p
= new KNS::ProviderLoader( this );
493 QObject::connect( p
, SIGNAL( providersLoaded(Provider::List
*) ), d
, SLOT( slotProviders (Provider::List
*) ) );
494 p
->load( "amarok/script", "http://amarok.kde.org/knewstuff/amarokscripts-providers.xml" );
502 ScriptManager::slotUninstallScript()
504 const QString name
= m_gui
->treeWidget
->currentItem()->text( 0 );
506 if( KMessageBox::warningContinueCancel( this, i18n( "Are you sure you want to uninstall the script '%1'?", name
), i18n("Uninstall Script"), KGuiItem( i18n("Uninstall") ) ) == KMessageBox::Cancel
)
509 if( m_scripts
.find( name
) == m_scripts
.end() )
512 const QString directory
= m_scripts
[name
].url
.directory();
514 // Delete directory recursively
515 const KUrl url
= KUrl( directory
);
516 if( !KIO::NetAccess::del( url
, 0 ) ) {
517 KMessageBox::sorry( 0, i18n( "<p>Could not uninstall this script.</p><p>The ScriptManager can only uninstall scripts which have been installed as packages.</p>" ) );
523 // Find all scripts that were in the uninstalled folder
524 foreach( const QString
&key
, m_scripts
.keys() )
525 if( m_scripts
[key
].url
.directory() == directory
)
528 // Terminate script processes, remove entries from script list
529 foreach( const QString
&key
, keys
) {
530 delete m_scripts
[key
].li
;
531 terminateProcess( &m_scripts
[key
].process
);
532 m_scripts
.remove( key
);
538 ScriptManager::slotRunScript( bool silent
)
540 if( !m_gui
->runButton
->isEnabled() ) return false;
542 QTreeWidgetItem
* const li
= m_gui
->treeWidget
->currentItem();
543 const QString name
= li
->text( 0 );
545 if( m_scripts
[name
].type
== "lyrics" && lyricsScriptRunning() != QString::null
) {
547 KMessageBox::sorry( 0, i18n( "Another lyrics script is already running. "
548 "You may only run one lyrics script at a time." ) );
552 if( m_scripts
[name
].type
== "transcode" && transcodeScriptRunning() != QString::null
) {
554 KMessageBox::sorry( 0, i18n( "Another transcode script is already running. "
555 "You may only run one transcode script at a time." ) );
559 // Don't start a script twice
560 if( m_scripts
[name
].process
) return false;
562 ProcIO
* script
= new ProcIO();
563 script
->setOutputChannelMode( ProcIO::SeparateChannels
);
564 const KUrl url
= m_scripts
[name
].url
;
565 *script
<< url
.path();
566 script
->setWorkingDirectory( Amarok::saveLocation( "scripts-data/" ) );
568 connect( script
, SIGNAL( receivedStderr( Process
* ) ), SLOT( slotReceivedStderr( Process
* ) ) );
569 connect( script
, SIGNAL( receivedStdout( Process
* ) ), SLOT( slotReceivedStdout( Process
* ) ) );
570 connect( script
, SIGNAL( processExited( Process
* ) ), SLOT( scriptFinished( Process
* ) ) );
573 if( script
->error() != ProcIO::FailedToStart
)
575 if( m_scripts
[name
].type
== "score" && !scoreScriptRunning().isNull() )
577 stopScript( scoreScriptRunning() );
578 m_gui
->treeWidget
->setCurrentItem( li
);
580 AmarokConfig::setLastScoreScript( name
);
585 KMessageBox::sorry( 0, i18n( "<p>Could not start the script <i>%1</i>.</p>"
586 "<p>Please make sure that the file has execute (+x) permissions.</p>", name
) );
591 li
->setIcon( 0, SmallIcon( Amarok::icon( "play" ) ) );
592 debug() << "Running script: " << url
.path();
594 m_scripts
[name
].process
= script
;
595 slotCurrentChanged( m_gui
->treeWidget
->currentItem() );
596 if( m_scripts
[name
].type
== "lyrics" )
597 emit
lyricsScriptChanged();
604 ScriptManager::slotStopScript()
606 QTreeWidgetItem
* const li
= m_gui
->treeWidget
->currentItem();
607 const QString name
= li
->text( 0 );
609 // Just a sanity check
610 if( m_scripts
.find( name
) == m_scripts
.end() )
613 terminateProcess( &m_scripts
[name
].process
);
614 m_scripts
[name
].log
= QString::null
;
615 slotCurrentChanged( m_gui
->treeWidget
->currentItem() );
617 li
->setIcon( 0, QPixmap() );
622 ScriptManager::slotConfigureScript()
624 const QString name
= m_gui
->treeWidget
->currentItem()->text( 0 );
625 if( !m_scripts
[name
].process
) return;
627 const KUrl url
= m_scripts
[name
].url
;
628 QDir::setCurrent( url
.directory() );
630 m_scripts
[name
].process
->writeStdin( QString("configure") );
635 ScriptManager::slotAboutScript()
637 const QString name
= m_gui
->treeWidget
->currentItem()->text( 0 );
638 QFile
readme( m_scripts
[name
].url
.directory( KUrl::AppendTrailingSlash
) + "README" );
639 QFile
license( m_scripts
[name
].url
.directory( KUrl::AppendTrailingSlash
) + "COPYING" );
641 if( !readme
.open( QIODevice::ReadOnly
) ) {
642 KMessageBox::sorry( 0, i18n( "There is no information available for this script." ) );
646 KAboutData
aboutData( name
.toLatin1(), 0, ki18n(name
.toLatin1()), "1.0", ki18n(readme
.readAll()) );
648 KAboutApplicationDialog
* about
= new KAboutApplicationDialog( &aboutData
, this );
649 about
->setButtons( KDialog::Ok
);
650 about
->setDefaultButton( KDialog::Ok
);
652 kapp
->setTopWidget( about
);
653 about
->setCaption( KDialog::makeStandardCaption( i18n( "About %1", name
) ) );
655 about
->setInitialSize( QSize( 500, 350 ) );
661 ScriptManager::slotShowContextMenu( const QPoint
& pos
)
663 QTreeWidgetItem
* item
= m_gui
->treeWidget
->itemAt( pos
);
665 const bool isCategory
= item
== m_generalCategory
||
666 item
== m_lyricsCategory
||
667 item
== m_scoreCategory
||
668 item
== m_transcodeCategory
||
669 item
== m_contextCategory
;
671 if( !item
|| isCategory
) return;
673 // Find the script entry in our map
675 foreach( key
, m_scripts
.keys() )
676 if( m_scripts
[key
].li
== item
) break;
678 enum { SHOW_LOG
, EDIT
};
680 menu
.addTitle( i18n( "Debugging" ) );
681 QAction
* logAction
= menu
.addAction( KIcon( Amarok::icon( "clock" ) ), i18n( "Show Output &Log" ) );
682 QAction
* editAction
= menu
.addAction( KIcon( Amarok::icon( "edit" ) ), i18n( "&Edit" ) );
683 logAction
->setData( SHOW_LOG
);
684 editAction
->setData( EDIT
);
686 logAction
->setEnabled( m_scripts
[key
].process
!= 0 );
688 QAction
* choice
= menu
.exec( mapToGlobal( pos
) );
689 if( !choice
) return;
690 const int id
= choice
->data().toInt();
695 KRun::runCommand( "kwrite " + m_scripts
[key
].url
.path(), 0 );
700 while( m_scripts
[key
].process
->readln( line
) != -1 )
701 m_scripts
[key
].log
+= line
;
703 KTextEdit
* editor
= new KTextEdit( m_scripts
[key
].log
);
704 kapp
->setTopWidget( editor
);
705 editor
->setWindowTitle( KDialog::makeStandardCaption( i18n( "Output Log for %1" ).arg( key
) ) );
706 editor
->setReadOnly( true );
708 QFont
font( "fixed" );
709 font
.setFixedPitch( true );
710 font
.setStyleHint( QFont::TypeWriter
);
711 editor
->setFont( font
);
713 editor
->resize( 500, 380 );
720 /* This is just a workaround, some scripts crash for some people if stdout is not handled. */
722 ScriptManager::slotReceivedStdout( Process
*process
)
724 debug() << QString::fromLatin1( process
->readAllStandardOutput() );
729 ScriptManager::slotReceivedStderr( Process
* process
)
731 // Look up script entry in our map
732 ScriptMap::Iterator it
;
733 ScriptMap::Iterator
end( m_scripts
.end() );
734 for( it
= m_scripts
.begin(); it
!= end
; ++it
)
735 if( it
.value().process
== process
) break;
737 const QString text
= QString::fromLatin1( process
->readAllStandardError() );
738 error() << it
.key() << ":\n" << text
;
740 if( it
.value().log
.length() > 20000 )
741 it
.value().log
= "==== LOG TRUNCATED HERE ====\n";
742 it
.value().log
+= text
;
747 ScriptManager::scriptFinished( Process
* process
) //SLOT
749 // Look up script entry in our map
750 ScriptMap::Iterator it
;
751 ScriptMap::Iterator
end( m_scripts
.end() );
752 for( it
= m_scripts
.begin(); it
!= end
; ++it
)
753 if( it
.value().process
== process
) break;
755 // Check if there was an error on exit
756 if( process
->error() != Process::Crashed
&& process
->exitStatus() != 0 )
757 KMessageBox::detailedError( 0, i18n( "The script '%1' exited with error code: %2", it
.key(), process
->exitStatus() )
760 // Destroy script process
761 delete it
.value().process
;
762 it
.value().process
= 0;
763 it
.value().log
.clear();
764 it
.value().li
->setIcon( 0, QPixmap() );
765 slotCurrentChanged( m_gui
->treeWidget
->currentItem() );
769 ////////////////////////////////////////////////////////////////////////////////
771 ////////////////////////////////////////////////////////////////////////////////
774 ScriptManager::scriptsOfType( const QString
&type
) const
777 foreach( const QString
&key
, m_scripts
.keys() )
778 if( m_scripts
[key
].type
== type
)
786 ScriptManager::scriptRunningOfType( const QString
&type
) const
788 foreach( const QString
&key
, m_scripts
.keys() )
789 if( m_scripts
[key
].process
&& m_scripts
[key
].type
== type
)
797 ScriptManager::ensureScoreScriptRunning()
799 QString s
= scoreScriptRunning();
803 if( runScript( AmarokConfig::lastScoreScript(), true /*silent*/ ) )
804 return AmarokConfig::lastScoreScript();
806 const QString def
= i18n( "Score" ) + ": " + "Default";
807 if( runScript( def
, true ) )
810 const QStringList scripts
= scoreScripts();
811 for( QStringList::const_iterator it
= scripts
.begin(), end
= scripts
.end(); it
!= end
; ++it
)
812 if( runScript( *it
, true ) )
820 ScriptManager::terminateProcess( ProcIO
** proc
)
823 (*proc
)->kill(); // Sends SIGTERM
832 ScriptManager::notifyScripts( const QString
& message
)
834 foreach( const ScriptItem
&item
, m_scripts
) {
835 ProcIO
* const proc
= item
.process
;
836 if( proc
) proc
->writeStdin( message
);
842 ScriptManager::loadScript( const QString
& path
)
844 if( !path
.isEmpty() ) {
845 const KUrl url
= KUrl( path
);
846 QString name
= url
.fileName();
847 QString type
= "generic";
849 // Read and parse .spec file, if exists
850 QFileInfo
info( path
);
851 QTreeWidgetItem
* li
= 0;
852 const QString specPath
= info
.path() + '/' + info
.completeBaseName() + ".spec";
853 if( QFile::exists( specPath
) ) {
854 debug() << "Spec file found: " << specPath
;
855 QSettings
spec( specPath
, QSettings::IniFormat
);
856 if( spec
.contains( "name" ) )
857 name
= spec
.value( "name" ).toString();
858 if( spec
.contains( "type" ) ) {
859 type
= spec
.value( "type" ).toString();
860 if( type
== "lyrics" ) {
861 li
= new QTreeWidgetItem( m_lyricsCategory
);
862 li
->setText( 0, name
);
864 if( type
== "transcode" ) {
865 li
= new QTreeWidgetItem( m_transcodeCategory
);
866 li
->setText( 0, name
);
868 if( type
== "score" ) {
869 li
= new QTreeWidgetItem( m_scoreCategory
);
870 li
->setText( 0, name
);
872 if( type
== "context" ) {
873 li
= new QTreeWidgetItem( m_contextCategory
);
874 li
->setText( 0, name
);
880 li
= new QTreeWidgetItem( m_generalCategory
);
881 li
->setText( 0, name
);
884 li
->setIcon( 0, QPixmap() );
892 m_scripts
[name
] = item
;
893 debug() << "Loaded: " << name
;
895 slotCurrentChanged( m_gui
->treeWidget
->currentItem() );
901 ScriptManager::engineStateChanged( Engine::State state
, Engine::State
/*oldState*/ )
906 notifyScripts( "engineStateChange: empty" );
910 notifyScripts( "engineStateChange: idle" );
914 notifyScripts( "engineStateChange: paused" );
917 case Engine::Playing
:
918 notifyScripts( "engineStateChange: playing" );
925 ScriptManager::engineNewMetaData( const MetaBundle
& /*bundle*/, bool /*trackChanged*/ )
927 notifyScripts( "trackChange" );
932 ScriptManager::engineVolumeChanged( int newVolume
)
934 notifyScripts( "volumeChange: " + QString::number( newVolume
) );
938 #include "scriptmanager.moc"