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"
33 #include "statusbar.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>
67 void closeOpenFiles(int out
, int in
, int err
) {
68 for(int i
= sysconf(_SC_OPEN_MAX
) - 1; i
> 2; i
--)
69 if(i
!=out
&& i
!=in
&& i
!=err
)
74 * This constructor is needed so that the correct codec is used. K3ProcIO defaults
75 * to latin1, while the scanner uses UTF-8.
77 ProcIO::ProcIO() : K3ProcIO( QTextCodec::codecForName( "UTF-8" ) ) {}
80 proxyForUrl(const QString
& url
)
86 if ( KProtocolManager::proxyForUrl( kurl
) !=
87 QString::fromLatin1( "DIRECT" ) ) {
88 KProtocolManager::slaveProtocol ( kurl
, proxy
);
95 proxyForProtocol(const QString
& protocol
)
97 return KProtocolManager::proxyFor( protocol
);
103 ////////////////////////////////////////////////////////////////////////////////
104 // class AmarokScriptNewStuff
105 ////////////////////////////////////////////////////////////////////////////////
108 * GHNS Customised Download implementation.
110 #if 0 //TODO: PORT to KNS2
111 class AmarokScriptNewStuff
: public KNewStuff
114 AmarokScriptNewStuff(const QString
&type
, QWidget
*parentWidget
=0)
115 : KNewStuff( type
, parentWidget
)
118 bool install( const QString
& fileName
)
120 return ScriptManager::instance()->slotInstallScript( fileName
);
123 virtual bool createUploadFile( const QString
& ) { return false; } //make compile on kde 3.5
127 ////////////////////////////////////////////////////////////////////////////////
128 // class ScriptManager
129 ////////////////////////////////////////////////////////////////////////////////
131 ScriptManager
* ScriptManager::s_instance
= 0;
134 ScriptManager::ScriptManager( QWidget
*parent
, const char *name
)
136 , EngineObserver( EngineController::instance() )
137 , m_gui( new Ui::ScriptManagerBase() )
140 setObjectName( name
);
143 setDefaultButton( Close
);
144 showButtonSeparator( true );
149 kapp
->setTopWidget( this );
150 setCaption( KDialog::makeStandardCaption( i18n( "Script Manager" ) ) );
152 // Gives the window a small title bar, and skips a taskbar entry
154 KWindowSystem::setType( winId(), NET::Utility
);
155 KWindowSystem::setState( winId(), NET::SkipTaskbar
);
158 QWidget
* main
= new QWidget( this );
159 m_gui
->setupUi( main
);
161 setMainWidget( main
);
164 m_generalCategory
= new QTreeWidgetItem( m_gui
->treeWidget
);
165 m_lyricsCategory
= new QTreeWidgetItem( m_gui
->treeWidget
);
166 m_scoreCategory
= new QTreeWidgetItem( m_gui
->treeWidget
);
167 m_transcodeCategory
= new QTreeWidgetItem( m_gui
->treeWidget
);
168 m_contextCategory
= new QTreeWidgetItem( m_gui
->treeWidget
);
170 m_generalCategory
->setText( 0, i18n( "General" ) );
171 m_lyricsCategory
->setText( 0, i18n( "Lyrics" ) );
172 m_scoreCategory
->setText( 0, i18n( "Score" ) );
173 m_transcodeCategory
->setText( 0, i18n( "Transcoding" ) );
174 m_contextCategory
->setText( 0, i18n( "Context Browser" ) );
176 m_generalCategory
->setFlags( Qt::ItemIsEnabled
);
177 m_lyricsCategory
->setFlags( Qt::ItemIsEnabled
);
178 m_scoreCategory
->setFlags( Qt::ItemIsEnabled
);
179 m_transcodeCategory
->setFlags( Qt::ItemIsEnabled
);
180 m_contextCategory
->setFlags( Qt::ItemIsEnabled
);
182 m_generalCategory
->setIcon( 0, SmallIcon( Amarok::icon( "files" ) ) );
183 m_lyricsCategory
->setIcon( 0, SmallIcon( Amarok::icon( "files" ) ) );
184 m_scoreCategory
->setIcon( 0, SmallIcon( Amarok::icon( "files" ) ) );
185 m_transcodeCategory
->setIcon( 0, SmallIcon( Amarok::icon( "files" ) ) );
186 m_contextCategory
->setIcon( 0, SmallIcon( Amarok::icon( "files" ) ) );
188 // Restore the open/closed state of the category items
189 KConfigGroup config
= Amarok::config( "ScriptManager" );
190 m_generalCategory
->setExpanded( config
.readEntry( "General category open", false ) );
191 m_lyricsCategory
->setExpanded( config
.readEntry( "Lyrics category open", false ) );
192 m_scoreCategory
->setExpanded( config
.readEntry( "Score category State", false ) );
193 m_transcodeCategory
->setExpanded( config
.readEntry( "Transcode category open", false ) );
194 m_contextCategory
->setExpanded( config
.readEntry( "Context category open", false ) );
196 connect( m_gui
->treeWidget
, SIGNAL( currentItemChanged( QTreeWidgetItem
*, QTreeWidgetItem
* ) ), SLOT( slotCurrentChanged( QTreeWidgetItem
* ) ) );
197 connect( m_gui
->treeWidget
, SIGNAL( itemDoubleClicked( QTreeWidgetItem
*, int ) ), SLOT( slotRunScript() ) );
198 connect( m_gui
->treeWidget
, SIGNAL( customContextMenuRequested ( const QPoint
& ) ), SLOT( slotShowContextMenu( const QPoint
& ) ) );
200 connect( m_gui
->installButton
, SIGNAL( clicked() ), SLOT( slotInstallScript() ) );
201 connect( m_gui
->retrieveButton
, SIGNAL( clicked() ), SLOT( slotRetrieveScript() ) );
202 connect( m_gui
->uninstallButton
, SIGNAL( clicked() ), SLOT( slotUninstallScript() ) );
203 connect( m_gui
->runButton
, SIGNAL( clicked() ), SLOT( slotRunScript() ) );
204 connect( m_gui
->stopButton
, SIGNAL( clicked() ), SLOT( slotStopScript() ) );
205 connect( m_gui
->configureButton
, SIGNAL( clicked() ), SLOT( slotConfigureScript() ) );
206 connect( m_gui
->aboutButton
, SIGNAL( clicked() ), SLOT( slotAboutScript() ) );
208 m_gui
->installButton
->setIcon( KIcon( Amarok::icon( "files" ) ) );
209 m_gui
->retrieveButton
->setIcon( KIcon( Amarok::icon( "download" ) ) );
210 m_gui
->uninstallButton
->setIcon( KIcon( Amarok::icon( "remove" ) ) );
211 m_gui
->runButton
->setIcon( KIcon( Amarok::icon( "play" ) ) );
212 m_gui
->stopButton
->setIcon( KIcon( Amarok::icon( "stop" ) ) );
213 m_gui
->configureButton
->setIcon( KIcon( Amarok::icon( "configure" ) ) );
214 m_gui
->aboutButton
->setIcon( KIcon( Amarok::icon( "info" ) ) );
216 QSize sz
= sizeHint();
217 setMinimumSize( qMax( 350, sz
.width() ), qMax( 250, sz
.height() ) );
218 resize( sizeHint() );
220 //FIXME: contex tbrowser changes
221 // connect( this, SIGNAL(lyricsScriptChanged()), ContextBrowser::instance(), SLOT( lyricsScriptChanged() ) );
223 // Delay this call via eventloop, because it's a bit slow and would block
224 QTimer::singleShot( 0, this, SLOT( findScripts() ) );
228 ScriptManager::~ScriptManager()
232 QStringList runningScripts
;
233 foreach( QString key
, m_scripts
.keys() ) {
234 if( m_scripts
[key
].process
) {
235 terminateProcess( &m_scripts
[key
].process
);
236 runningScripts
<< key
;
241 KConfigGroup config
= Amarok::config( "ScriptManager" );
242 config
.writeEntry( "Running Scripts", runningScripts
);
244 // Save the open/closed state of the category items
245 config
.writeEntry( "General category open", m_generalCategory
->isExpanded() );
246 config
.writeEntry( "Lyrics category open", m_lyricsCategory
->isExpanded() );
247 config
.writeEntry( "Score category open", m_scoreCategory
->isExpanded() );
248 config
.writeEntry( "Transcode category open", m_transcodeCategory
->isExpanded() );
249 config
.writeEntry( "Context category open", m_contextCategory
->isExpanded() );
254 ////////////////////////////////////////////////////////////////////////////////
256 ////////////////////////////////////////////////////////////////////////////////
259 ScriptManager::runScript( const QString
& name
, bool silent
)
261 if( !m_scripts
.contains( name
) )
264 m_gui
->treeWidget
->setCurrentItem( m_scripts
[name
].li
);
265 return slotRunScript( silent
);
270 ScriptManager::stopScript( const QString
& name
)
272 if( !m_scripts
.contains( name
) )
275 m_gui
->treeWidget
->setCurrentItem( m_scripts
[name
].li
);
283 ScriptManager::listRunningScripts()
285 QStringList runningScripts
;
286 foreach( QString key
, m_scripts
.keys() )
287 if( m_scripts
[key
].process
)
288 runningScripts
<< key
;
290 return runningScripts
;
295 ScriptManager::customMenuClicked( const QString
& message
)
297 notifyScripts( "customMenuClicked: " + message
);
302 ScriptManager::specForScript( const QString
& name
)
304 if( !m_scripts
.contains( name
) )
306 QFileInfo
info( m_scripts
[name
].url
.path() );
307 const QString specPath
= info
.path() + '/' + info
.completeBaseName() + ".spec";
314 ScriptManager::notifyFetchLyrics( const QString
& artist
, const QString
& title
)
316 const QString args
= QUrl::toPercentEncoding( artist
) + ' ' + QUrl::toPercentEncoding( title
);
317 notifyScripts( "fetchLyrics " + args
);
322 ScriptManager::notifyFetchLyricsByUrl( const QString
& url
)
324 notifyScripts( "fetchLyricsByUrl " + url
);
328 void ScriptManager::notifyTranscode( const QString
& srcUrl
, const QString
& filetype
)
330 notifyScripts( "transcode " + srcUrl
+ ' ' + filetype
);
335 ScriptManager::requestNewScore( const QString
&url
, double prevscore
, int playcount
, int length
, float percentage
, const QString
&reason
)
337 const QString script
= ensureScoreScriptRunning();
338 if( script
.isNull() )
340 Amarok::StatusBar::instance()->longMessage(
341 i18n( "No score scripts were found, or none of them worked. Automatic scoring will be disabled. Sorry." ),
342 KDE::StatusBar::Sorry
);
346 m_scripts
[script
].process
->writeStdin(
347 QString( "requestNewScore %6 %1 %2 %3 %4 %5" )
353 .arg( QString( QUrl::toPercentEncoding( url
) ) ) ); //last because it might have %s
356 ////////////////////////////////////////////////////////////////////////////////
358 ////////////////////////////////////////////////////////////////////////////////
361 ScriptManager::findScripts() //SLOT
363 const QStringList allFiles
= KGlobal::dirs()->findAllResources( "data", "amarok/scripts/*",KStandardDirs::Recursive
);
365 // Add found scripts to treeWidget:
366 foreach( QString str
, allFiles
)
367 if( QFileInfo( str
).isExecutable() )
372 KConfigGroup config
= Amarok::config( "ScriptManager" );
373 const QStringList runningScripts
= config
.readEntry( "Running Scripts", QStringList() );
375 foreach( QString str
, runningScripts
)
376 if( m_scripts
.contains( str
) ) {
377 debug() << "Auto-running script: " << str
;
378 m_gui
->treeWidget
->setCurrentItem( m_scripts
[str
].li
);
382 //FIXME m_gui->treeWidget->setCurrentItem( m_gui->treeWidget->firstChild() );
383 slotCurrentChanged( m_gui
->treeWidget
->currentItem() );
388 ScriptManager::slotCurrentChanged( QTreeWidgetItem
* item
)
390 const bool isCategory
= item
== m_generalCategory
||
391 item
== m_lyricsCategory
||
392 item
== m_scoreCategory
||
393 item
== m_transcodeCategory
;
395 if( item
&& !isCategory
) {
396 const QString name
= item
->text( 0 );
397 m_gui
->uninstallButton
->setEnabled( true );
398 m_gui
->runButton
->setEnabled( !m_scripts
[name
].process
);
399 m_gui
->stopButton
->setEnabled( m_scripts
[name
].process
);
400 m_gui
->configureButton
->setEnabled( m_scripts
[name
].process
);
401 m_gui
->aboutButton
->setEnabled( true );
404 m_gui
->uninstallButton
->setEnabled( false );
405 m_gui
->runButton
->setEnabled( false );
406 m_gui
->stopButton
->setEnabled( false );
407 m_gui
->configureButton
->setEnabled( false );
408 m_gui
->aboutButton
->setEnabled( false );
414 ScriptManager::slotInstallScript( const QString
& path
)
416 QString _path
= path
;
418 if( path
.isNull() ) {
419 _path
= KFileDialog::getOpenFileName( KUrl(),
420 "*.amarokscript.tar *.amarokscript.tar.bz2 *.amarokscript.tar.gz|"
421 + i18n( "Script Packages (*.amarokscript.tar, *.amarokscript.tar.bz2, *.amarokscript.tar.gz)" )
423 if( _path
.isNull() ) return false;
426 KTar
archive( _path
);
427 if( !archive
.open( QIODevice::ReadOnly
) ) {
428 KMessageBox::sorry( 0, i18n( "Could not read this package." ) );
432 QString destination
= Amarok::saveLocation( "scripts/" );
433 const KArchiveDirectory
* const archiveDir
= archive
.directory();
435 // Prevent installing a script that's already installed
436 const QString scriptFolder
= destination
+ archiveDir
->entries().first();
437 if( QFile::exists( scriptFolder
) ) {
438 KMessageBox::error( 0, i18n( "A script with the name '%1' is already installed. "
439 "Please uninstall it first.", archiveDir
->entries().first() ) );
443 archiveDir
->copyTo( destination
);
444 m_installSuccess
= false;
445 recurseInstall( archiveDir
, destination
);
447 if( m_installSuccess
) {
448 KMessageBox::information( 0, i18n( "Script successfully installed." ) );
452 KMessageBox::sorry( 0, i18n( "<p>Script installation failed.</p>"
453 "<p>The package did not contain an executable file. "
454 "Please inform the package maintainer about this error.</p>" ) );
456 // Delete directory recursively
457 KIO::NetAccess::del( KUrl( scriptFolder
), 0 );
465 ScriptManager::recurseInstall( const KArchiveDirectory
* archiveDir
, const QString
& destination
)
467 const QStringList entries
= archiveDir
->entries();
469 foreach( QString entry
, entries
) {
470 const KArchiveEntry
* const archEntry
= archiveDir
->entry( entry
);
472 if( archEntry
->isDirectory() ) {
473 const KArchiveDirectory
* const dir
= static_cast<const KArchiveDirectory
*>( archEntry
);
474 recurseInstall( dir
, destination
+ entry
+ '/' );
477 ::chmod( QFile::encodeName( destination
+ entry
), archEntry
->permissions() );
479 if( QFileInfo( destination
+ entry
).isExecutable() ) {
480 loadScript( destination
+ entry
);
481 m_installSuccess
= true;
489 ScriptManager::slotRetrieveScript()
491 #if 0 //FIXME: PORT To KNS2
492 // Delete KNewStuff's configuration entries. These entries reflect which scripts
493 // are already installed. As we cannot yet keep them in sync after uninstalling
494 // scripts, we deactivate the check marks entirely.
495 Amarok::config()->deleteGroup( "KNewStuffStatus" );
497 // we need this because KNewStuffGeneric's install function isn't clever enough
498 AmarokScriptNewStuff
*kns
= new AmarokScriptNewStuff( "amarok/script", this );
499 KNS::Engine
*engine
= new KNS::Engine( kns
, "amarok/script", this );
500 KNS::DownloadDialog
*d
= new KNS::DownloadDialog( engine
, this );
501 d
->setType( "amarok/script" );
502 // you have to do this by hand when providing your own Engine
503 KNS::ProviderLoader
*p
= new KNS::ProviderLoader( this );
504 QObject::connect( p
, SIGNAL( providersLoaded(Provider::List
*) ), d
, SLOT( slotProviders (Provider::List
*) ) );
505 p
->load( "amarok/script", "http://amarok.kde.org/knewstuff/amarokscripts-providers.xml" );
513 ScriptManager::slotUninstallScript()
515 const QString name
= m_gui
->treeWidget
->currentItem()->text( 0 );
517 if( KMessageBox::warningContinueCancel( this, i18n( "Are you sure you want to uninstall the script '%1'?", name
), i18n("Uninstall Script"), KGuiItem( i18n("Uninstall") ) ) == KMessageBox::Cancel
)
520 if( m_scripts
.find( name
) == m_scripts
.end() )
523 const QString directory
= m_scripts
[name
].url
.directory();
525 // Delete directory recursively
526 const KUrl url
= KUrl( directory
);
527 if( !KIO::NetAccess::del( url
, 0 ) ) {
528 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>" ) );
534 // Find all scripts that were in the uninstalled folder
535 foreach( QString key
, m_scripts
.keys() )
536 if( m_scripts
[key
].url
.directory() == directory
)
539 // Terminate script processes, remove entries from script list
540 foreach( QString key
, keys
) {
541 delete m_scripts
[key
].li
;
542 terminateProcess( &m_scripts
[key
].process
);
543 m_scripts
.remove( key
);
549 ScriptManager::slotRunScript( bool silent
)
551 if( !m_gui
->runButton
->isEnabled() ) return false;
553 QTreeWidgetItem
* const li
= m_gui
->treeWidget
->currentItem();
554 const QString name
= li
->text( 0 );
556 if( m_scripts
[name
].type
== "lyrics" && lyricsScriptRunning() != QString::null
) {
558 KMessageBox::sorry( 0, i18n( "Another lyrics script is already running. "
559 "You may only run one lyrics script at a time." ) );
563 if( m_scripts
[name
].type
== "transcode" && transcodeScriptRunning() != QString::null
) {
565 KMessageBox::sorry( 0, i18n( "Another transcode script is already running. "
566 "You may only run one transcode script at a time." ) );
570 // Don't start a script twice
571 if( m_scripts
[name
].process
) return false;
573 Amarok::ProcIO
* script
= new Amarok::ProcIO();
574 script
->setComm( static_cast<K3Process::Communication
>( K3Process::All
) );
575 const KUrl url
= m_scripts
[name
].url
;
576 *script
<< url
.path();
577 script
->setWorkingDirectory( Amarok::saveLocation( "scripts-data/" ) );
579 connect( script
, SIGNAL( receivedStderr( K3Process
*, char*, int ) ), SLOT( slotReceivedStderr( K3Process
*, char*, int ) ) );
580 connect( script
, SIGNAL( receivedStdout( K3Process
*, char*, int ) ), SLOT( slotReceivedStdout( K3Process
*, char*, int ) ) );
581 connect( script
, SIGNAL( processExited( K3Process
* ) ), SLOT( scriptFinished( K3Process
* ) ) );
583 if( script
->start( K3Process::NotifyOnExit
) )
585 if( m_scripts
[name
].type
== "score" && !scoreScriptRunning().isNull() )
587 stopScript( scoreScriptRunning() );
588 m_gui
->treeWidget
->setCurrentItem( li
);
590 AmarokConfig::setLastScoreScript( name
);
595 KMessageBox::sorry( 0, i18n( "<p>Could not start the script <i>%1</i>.</p>"
596 "<p>Please make sure that the file has execute (+x) permissions.</p>", name
) );
601 li
->setIcon( 0, SmallIcon( Amarok::icon( "play" ) ) );
602 debug() << "Running script: " << url
.path();
604 m_scripts
[name
].process
= script
;
605 slotCurrentChanged( m_gui
->treeWidget
->currentItem() );
606 if( m_scripts
[name
].type
== "lyrics" )
607 emit
lyricsScriptChanged();
614 ScriptManager::slotStopScript()
616 QTreeWidgetItem
* const li
= m_gui
->treeWidget
->currentItem();
617 const QString name
= li
->text( 0 );
619 // Just a sanity check
620 if( m_scripts
.find( name
) == m_scripts
.end() )
623 terminateProcess( &m_scripts
[name
].process
);
624 m_scripts
[name
].log
= QString::null
;
625 slotCurrentChanged( m_gui
->treeWidget
->currentItem() );
627 li
->setIcon( 0, QPixmap() );
632 ScriptManager::slotConfigureScript()
634 const QString name
= m_gui
->treeWidget
->currentItem()->text( 0 );
635 if( !m_scripts
[name
].process
) return;
637 const KUrl url
= m_scripts
[name
].url
;
638 QDir::setCurrent( url
.directory() );
640 m_scripts
[name
].process
->writeStdin( QString("configure") );
645 ScriptManager::slotAboutScript()
647 const QString name
= m_gui
->treeWidget
->currentItem()->text( 0 );
648 QFile
readme( m_scripts
[name
].url
.directory( KUrl::AppendTrailingSlash
) + "README" );
649 QFile
license( m_scripts
[name
].url
.directory( KUrl::AppendTrailingSlash
) + "COPYING" );
651 if( !readme
.open( QIODevice::ReadOnly
) ) {
652 KMessageBox::sorry( 0, i18n( "There is no information available for this script." ) );
656 KAboutData
aboutData( name
.toLatin1(), 0, ki18n(name
.toLatin1()), "1.0", ki18n(readme
.readAll()) );
658 KAboutApplicationDialog
* about
= new KAboutApplicationDialog( &aboutData
, this );
659 about
->setButtons( KDialog::Ok
);
660 about
->setDefaultButton( KDialog::Ok
);
662 kapp
->setTopWidget( about
);
663 about
->setCaption( KDialog::makeStandardCaption( i18n( "About %1", name
) ) );
665 about
->setInitialSize( QSize( 500, 350 ) );
671 ScriptManager::slotShowContextMenu( const QPoint
& pos
)
673 QTreeWidgetItem
* item
= m_gui
->treeWidget
->itemAt( pos
);
675 const bool isCategory
= item
== m_generalCategory
||
676 item
== m_lyricsCategory
||
677 item
== m_scoreCategory
||
678 item
== m_transcodeCategory
||
679 item
== m_contextCategory
;
681 if( !item
|| isCategory
) return;
683 // Find the script entry in our map
685 foreach( key
, m_scripts
.keys() )
686 if( m_scripts
[key
].li
== item
) break;
688 enum { SHOW_LOG
, EDIT
};
690 menu
.addTitle( i18n( "Debugging" ) );
691 QAction
* logAction
= menu
.addAction( KIcon( Amarok::icon( "clock" ) ), i18n( "Show Output &Log" ) );
692 QAction
* editAction
= menu
.addAction( KIcon( Amarok::icon( "edit" ) ), i18n( "&Edit" ) );
693 logAction
->setData( SHOW_LOG
);
694 editAction
->setData( EDIT
);
696 logAction
->setEnabled( m_scripts
[key
].process
!= 0 );
698 QAction
* choice
= menu
.exec( mapToGlobal( pos
) );
699 if( !choice
) return;
700 const int id
= choice
->data().toInt();
705 KRun::runCommand( "kwrite " + m_scripts
[key
].url
.path(), 0 );
710 while( m_scripts
[key
].process
->readln( line
) != -1 )
711 m_scripts
[key
].log
+= line
;
713 KTextEdit
* editor
= new KTextEdit( m_scripts
[key
].log
);
714 kapp
->setTopWidget( editor
);
715 editor
->setWindowTitle( KDialog::makeStandardCaption( i18n( "Output Log for %1" ).arg( key
) ) );
716 editor
->setReadOnly( true );
718 QFont
font( "fixed" );
719 font
.setFixedPitch( true );
720 font
.setStyleHint( QFont::TypeWriter
);
721 editor
->setFont( font
);
723 editor
->resize( 500, 380 );
730 /* This is just a workaround, some scripts crash for some people if stdout is not handled. */
732 ScriptManager::slotReceivedStdout( K3Process
*, char* buf
, int len
)
734 debug() << QString::fromLatin1( buf
, len
);
739 ScriptManager::slotReceivedStderr( K3Process
* process
, char* buf
, int len
)
741 // Look up script entry in our map
742 ScriptMap::Iterator it
;
743 ScriptMap::Iterator
end( m_scripts
.end() );
744 for( it
= m_scripts
.begin(); it
!= end
; ++it
)
745 if( it
.value().process
== process
) break;
747 const QString text
= QString::fromLatin1( buf
, len
);
748 error() << it
.key() << ":\n" << text
;
750 if( it
.value().log
.length() > 20000 )
751 it
.value().log
= "==== LOG TRUNCATED HERE ====\n";
752 it
.value().log
+= text
;
757 ScriptManager::scriptFinished( K3Process
* process
) //SLOT
759 // Look up script entry in our map
760 ScriptMap::Iterator it
;
761 ScriptMap::Iterator
end( m_scripts
.end() );
762 for( it
= m_scripts
.begin(); it
!= end
; ++it
)
763 if( it
.value().process
== process
) break;
765 // Check if there was an error on exit
766 if( process
->normalExit() && process
->exitStatus() != 0 )
767 KMessageBox::detailedError( 0, i18n( "The script '%1' exited with error code: %2", it
.key(), process
->exitStatus() )
770 // Destroy script process
771 delete it
.value().process
;
772 it
.value().process
= 0;
773 it
.value().log
.clear();
774 it
.value().li
->setIcon( 0, QPixmap() );
775 slotCurrentChanged( m_gui
->treeWidget
->currentItem() );
779 ////////////////////////////////////////////////////////////////////////////////
781 ////////////////////////////////////////////////////////////////////////////////
784 ScriptManager::scriptsOfType( const QString
&type
) const
787 foreach( QString key
, m_scripts
.keys() )
788 if( m_scripts
[key
].type
== type
)
796 ScriptManager::scriptRunningOfType( const QString
&type
) const
798 foreach( QString key
, m_scripts
.keys() )
799 if( m_scripts
[key
].process
&& m_scripts
[key
].type
== type
)
807 ScriptManager::ensureScoreScriptRunning()
809 QString s
= scoreScriptRunning();
813 if( runScript( AmarokConfig::lastScoreScript(), true /*silent*/ ) )
814 return AmarokConfig::lastScoreScript();
816 const QString def
= i18n( "Score" ) + ": " + "Default";
817 if( runScript( def
, true ) )
820 const QStringList scripts
= scoreScripts();
821 for( QStringList::const_iterator it
= scripts
.begin(), end
= scripts
.end(); it
!= end
; ++it
)
822 if( runScript( *it
, true ) )
830 ScriptManager::terminateProcess( K3ProcIO
** proc
)
833 (*proc
)->kill(); // Sends SIGTERM
843 ScriptManager::notifyScripts( const QString
& message
)
845 foreach( ScriptItem item
, m_scripts
) {
846 K3ProcIO
* const proc
= item
.process
;
847 if( proc
) proc
->writeStdin( message
);
853 ScriptManager::loadScript( const QString
& path
)
855 if( !path
.isEmpty() ) {
856 const KUrl url
= KUrl( path
);
857 QString name
= url
.fileName();
858 QString type
= "generic";
860 // Read and parse .spec file, if exists
861 QFileInfo
info( path
);
862 QTreeWidgetItem
* li
= 0;
863 const QString specPath
= info
.path() + '/' + info
.completeBaseName() + ".spec";
864 if( QFile::exists( specPath
) ) {
865 debug() << "Spec file found: " << specPath
;
866 QSettings
spec( specPath
, QSettings::IniFormat
);
867 if( spec
.contains( "name" ) )
868 name
= spec
.value( "name" ).toString();
869 if( spec
.contains( "type" ) ) {
870 type
= spec
.value( "type" ).toString();
871 if( type
== "lyrics" ) {
872 li
= new QTreeWidgetItem( m_lyricsCategory
);
873 li
->setText( 0, name
);
875 if( type
== "transcode" ) {
876 li
= new QTreeWidgetItem( m_transcodeCategory
);
877 li
->setText( 0, name
);
879 if( type
== "score" ) {
880 li
= new QTreeWidgetItem( m_scoreCategory
);
881 li
->setText( 0, name
);
883 if( type
== "context" ) {
884 li
= new QTreeWidgetItem( m_contextCategory
);
885 li
->setText( 0, name
);
891 li
= new QTreeWidgetItem( m_generalCategory
);
892 li
->setText( 0, name
);
895 li
->setIcon( 0, QPixmap() );
903 m_scripts
[name
] = item
;
904 debug() << "Loaded: " << name
;
906 slotCurrentChanged( m_gui
->treeWidget
->currentItem() );
912 ScriptManager::engineStateChanged( Engine::State state
, Engine::State
/*oldState*/ )
917 notifyScripts( "engineStateChange: empty" );
921 notifyScripts( "engineStateChange: idle" );
925 notifyScripts( "engineStateChange: paused" );
928 case Engine::Playing
:
929 notifyScripts( "engineStateChange: playing" );
936 ScriptManager::engineNewMetaData( const MetaBundle
& /*bundle*/, bool /*trackChanged*/ )
938 notifyScripts( "trackChange" );
943 ScriptManager::engineVolumeChanged( int newVolume
)
945 notifyScripts( "volumeChange: " + QString::number( newVolume
) );
949 #include "scriptmanager.moc"