diff --git a/src/scripting/scriptconsole/ScriptConsole.cpp b/src/scripting/scriptconsole/ScriptConsole.cpp index 6edd9a2325..e4e0fab03e 100644 --- a/src/scripting/scriptconsole/ScriptConsole.cpp +++ b/src/scripting/scriptconsole/ScriptConsole.cpp @@ -1,431 +1,431 @@ /**************************************************************************************** * Copyright (c) 2013 Anmol Ahuja * * * * This program is free software; you can redistribute it and/or modify it under * * the terms of the GNU General Public License as published by the Free Software * * Foundation; either version 2 of the License, or (at your option) any later * * version. * * * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License along with * * this program. If not, see . * ****************************************************************************************/ #include "ScriptConsole.h" #define DEBUG_PREFIX "ScriptConsole" #include "core/support/Debug.h" #include "MainWindow.h" #include "ScriptEditorDocument.h" #include "ScriptConsoleItem.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace AmarokScript; using namespace ScriptConsoleNS; QWeakPointer ScriptConsole::s_instance; ScriptConsole* ScriptConsole::instance() { if( !s_instance ) s_instance = new ScriptConsole( The::mainWindow() ); return s_instance.data(); } //private ScriptConsole::ScriptConsole( QWidget *parent ) : QMainWindow( parent, Qt::Window ) { m_editor = KTextEditor::EditorChooser::editor(); if ( !m_editor ) { KMessageBox::error( 0, i18n("A KDE text-editor component could not be found.\n" "Please check your KDE installation. Exiting the console!") ); deleteLater(); return; } m_editor->readConfig( KGlobal::config().data() ); m_scriptListDock = new ScriptListDockWidget( this ); m_debugger = new QScriptEngineDebugger( this ); setDockNestingEnabled( true ); setWindowTitle( i18n( "Script Console" ) ); setObjectName( "scriptconsole" ); m_debugger->setAutoShowStandardWindow( false ); m_codeWidget = getWidget( "Code", QScriptEngineDebugger::CodeWidget ); addDockWidget( Qt::BottomDockWidgetArea, m_codeWidget ); QList debugWidgets = QList() << getWidget( i18n( "Console" ), QScriptEngineDebugger::ConsoleWidget ) << getWidget( i18n( "Error" ), QScriptEngineDebugger::ErrorLogWidget ) << getWidget( i18n( "Debug Output" ), QScriptEngineDebugger::DebugOutputWidget ) << getWidget( i18n( "Loaded Scripts" ), QScriptEngineDebugger::ScriptsWidget ) << getWidget( i18n( "Breakpoints" ), QScriptEngineDebugger::BreakpointsWidget ) << getWidget( i18n( "Stack" ), QScriptEngineDebugger::StackWidget ) << getWidget( i18n( "Locals" ), QScriptEngineDebugger::LocalsWidget ); foreach( QDockWidget *widget, debugWidgets ) { addDockWidget( Qt::BottomDockWidgetArea, widget ); } addDockWidget( Qt::BottomDockWidgetArea, m_scriptListDock ); tabifyDockWidget( debugWidgets[0], debugWidgets[1] ); tabifyDockWidget( debugWidgets[1], debugWidgets[2] ); tabifyDockWidget( debugWidgets[3], debugWidgets[4] ); tabifyDockWidget( debugWidgets[5], debugWidgets[6] ); QMenuBar *bar = new QMenuBar( this ); setMenuBar( bar ); bar->addMenu( m_debugger->createStandardMenu( this ) ); QToolBar *toolBar = m_debugger->createStandardToolBar( this ); QAction *action = new QAction( i18n( "Stop" ), this ); action->setIcon( QApplication::style()->standardIcon( QStyle::SP_MediaStop ) ); connect( action, SIGNAL(toggled(bool)), SLOT(slotAbortEvaluation()) ); toolBar->addAction( action ); action = new KAction( KIcon( "media-playback-start" ), i18n("Execute Script"), this ); action->setShortcut( Qt::CTRL + Qt::Key_Enter ); connect( action, SIGNAL(triggered(bool)), SLOT(slotExecuteNewScript()) ); toolBar->addAction( action ); action = new KAction( KIcon( "document-new" ), i18n( "&New Script" ), this ); action->setShortcut( Qt::CTRL + Qt::Key_N ); toolBar->addAction( action ); connect( action, SIGNAL(triggered(bool)), SLOT(slotNewScript()) ); action = new KAction( KIcon( "edit-delete" ), i18n( "&Delete Script" ), this ); toolBar->addAction( action ); connect( action, SIGNAL(triggered(bool)), m_scriptListDock, SLOT(removeCurrentScript()) ); action = new KAction( i18n( "&Clear All Scripts" ), this ); toolBar->addAction( action ); connect( action, SIGNAL(triggered(bool)), m_scriptListDock, SLOT(clear()) ); action = new KAction( i18n("Previous Script"), this ); action->setShortcut( QKeySequence::MoveToPreviousPage ); connect( action, SIGNAL(triggered(bool)), m_scriptListDock, SLOT(prev()) ); toolBar->addAction( action ); action = new KAction( i18n("Next Script"), this ); action->setShortcut( QKeySequence::MoveToNextPage ); connect( action, SIGNAL(triggered(bool)), m_scriptListDock, SLOT(next()) ); toolBar->addAction( action ); addToolBar( toolBar ); QMenu *viewMenu = new QMenu( this ); viewMenu->setTitle( i18n( "&View" ) ); foreach( QDockWidget *dockWidget, findChildren() ) { if( dockWidget->parentWidget() == this ) viewMenu->addAction( dockWidget->toggleViewAction() ); } menuBar()->addMenu( viewMenu ); KTextEditor::ContainerInterface *iface = qobject_cast( m_editor ); if (iface) iface->setContainer( parent ); addDockWidget( Qt::BottomDockWidgetArea, m_scriptListDock ); connect( m_scriptListDock, SIGNAL(edit(ScriptConsoleItem*)), SLOT(slotEditScript(ScriptConsoleItem*)) ); connect( m_scriptListDock, SIGNAL(currentItemChanged(ScriptConsoleItem*)), SLOT(setCurrentScriptItem(ScriptConsoleItem*)) ); QListWidgetItem *item = new QListWidgetItem( "The Amarok Script Console allows you to easily execute" "JavaScript with access to all functions\nand methods you would" "have in an Amarok script.\nInformation on scripting for Amarok is" "available at:\nhttp://community.kde.org/Amarok/Development#Scripting" "\nExecute code: CTRL-Enter\nBack in code history: Page Up" "\nForward in code history: Page Down" "See the debugger manual at: " , 0 ); item->setFlags( Qt::NoItemFlags ); m_scriptListDock->addItem( item ); QSettings settings( "KDE", "Amarok" ); settings.beginGroup( "ScriptConsole" ); restoreGeometry( settings.value("geometry").toByteArray() ); m_savePath = settings.value("savepath").toString(); settings.endGroup(); if( m_savePath.isEmpty() ) - m_savePath = KUrl( KStandardDirs::locate( "data", "amarok/scriptconsole/" ) ).path(); + m_savePath = KUrl( KStandardDirs::locateLocal( "data", "amarok/scriptconsole/" ) ).path(); slotNewScript(); connect( m_debugger, SIGNAL(evaluationSuspended()), SLOT(slotEvaluationSuspended()) ); connect( m_debugger, SIGNAL(evaluationResumed()), SLOT(slotEvaluationResumed()) ); show(); raise(); } void ScriptConsole::slotExecuteNewScript() { if( m_scriptItem.data()->document()->text().isEmpty() ) return; QScriptSyntaxCheckResult syntaxResult = m_scriptItem.data()->engine()->checkSyntax( m_scriptItem.data()->document()->text() ); if( QScriptSyntaxCheckResult::Valid != syntaxResult.state() ) { debug() << "Syntax error: " << syntaxResult.errorLineNumber() << syntaxResult.errorMessage(); KTextEditor::View *view = dynamic_cast( m_codeWidget->widget() ); ScriptEditorDocument::highlight( view, syntaxResult.errorLineNumber(), QColor( 255, 0, 0 ) ); int response = KMessageBox::warningContinueCancel( this, i18n( "Syntax error at line %1, continue anyway?\nError: %2", syntaxResult.errorLineNumber(), syntaxResult.errorMessage() ), i18n( "Syntax Error" ) ); ScriptEditorDocument::clearHighlights( view ); if( response == KMessageBox::Cancel ) return; } m_scriptItem.data()->document()->save(); m_codeWidget->setWidget( m_debugger->widget( QScriptEngineDebugger::CodeWidget ) ); m_scriptItem.data()->start( false ); } void ScriptConsole::closeEvent( QCloseEvent *event ) { QSettings settings( "Amarok", "Script Console" ); settings.beginGroup( "ScriptConsole" ); settings.setValue( "geometry", saveGeometry() ); settings.setValue( "savepath", m_savePath ); settings.endGroup(); QMainWindow::closeEvent( event ); deleteLater(); } void ScriptConsole::slotEditScript( ScriptConsoleItem *item ) { if( m_scriptItem.data()->running() && KMessageBox::warningContinueCancel( this, i18n( "This will stop this script! Continue?" ), QString(), KStandardGuiItem::cont() , KStandardGuiItem::cancel(), "stopRunningScriptWarning" ) == KMessageBox::Cancel ) return; item->pause(); setCurrentScriptItem( item ); } ScriptConsoleItem* ScriptConsole::createScriptItem( const QString &script ) { if( ( m_savePath.isEmpty() || !QDir( m_savePath ).exists() ) && ( m_savePath = QFileDialog::getExistingDirectory(this, i18n( "Choose where to save your scripts" ), "~", QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks) ).isEmpty() ) return 0; QString scriptPath; QString scriptName; do { scriptName = QString( "Script-%1" ).arg( qrand() ); scriptPath = QString( "%1/%2" ).arg( m_savePath, scriptName ); } while ( QDir( scriptPath ).exists() ); QDir().mkdir( scriptPath ); ScriptEditorDocument *document = new ScriptEditorDocument( this, m_editor->createDocument( 0 ) ); document->setText( script ); ScriptConsoleItem *scriptItem = new ScriptConsoleItem( this, scriptName, "Generic", scriptPath, document ); return scriptItem; } ScriptConsole::~ScriptConsole() { m_debugger->detach(); } void ScriptConsole::slotEvaluationSuspended() { if( !m_scriptItem ) { slotNewScript(); return; } debug() << "Is Evaluating() " << m_scriptItem.data()->engine()->isEvaluating(); debug() << "Exception isValid()" << m_scriptItem.data()->engine()->uncaughtException().isValid(); if( m_scriptItem.data()->engine() && m_scriptItem.data()->engine()->uncaughtException().isValid() ) return; KTextEditor::View *view = m_scriptItem.data()->createEditorView( m_codeWidget ); view->installEventFilter( this ); view->document()->installEventFilter( this ); m_codeWidget->setWidget( view ); } void ScriptConsole::slotEvaluationResumed() { debug() << "Is Evaluating() " << m_scriptItem.data()->engine()->isEvaluating(); debug() << "Exception isValid()" << m_scriptItem.data()->engine()->uncaughtException().isValid(); if( !m_scriptItem.data()->engine() || !m_scriptItem.data()->engine()->isEvaluating() ) return; KTextEditor::View *view = m_scriptItem.data()->createEditorView( m_codeWidget ); view->installEventFilter( this ); m_codeWidget->setWidget( view ); } void ScriptConsole::slotAbortEvaluation() { m_scriptItem.data()->pause(); } QDockWidget* ScriptConsole::getWidget( const QString &title, QScriptEngineDebugger::DebuggerWidget widget ) { QDockWidget *debugWidget = new QDockWidget( title, this ); debugWidget->setWidget( m_debugger->widget( widget ) ); return debugWidget; } void ScriptConsole::setCurrentScriptItem( ScriptConsoleItem *item ) { if( !item || m_scriptItem.data() == item ) return; m_debugger->detach(); m_debugger->attachTo( item->engine() ); m_scriptItem = item; if( item->engine() && item->engine()->isEvaluating() ) { m_codeWidget->setWidget( m_debugger->widget( QScriptEngineDebugger::CodeWidget ) ); } else { KTextEditor::View *view = item->createEditorView( m_codeWidget ); view->installEventFilter( this ); m_codeWidget->setWidget( view ); } } void ScriptConsole::slotNewScript() { ScriptConsoleItem *item = createScriptItem( "" ); m_scriptListDock->addScript( item ); setCurrentScriptItem( item ); } bool ScriptConsole::eventFilter( QObject *watched, QEvent *event ) { Q_UNUSED( watched ) if( event->type() == QEvent::KeyPress ) { QKeyEvent *keyEvent = static_cast( event ); if( keyEvent == QKeySequence::MoveToNextPage ) { m_scriptListDock->next(); return true; } else if( keyEvent == QKeySequence::MoveToPreviousPage ) { m_scriptListDock->prev(); return true; } } return false; } ScriptListDockWidget::ScriptListDockWidget( QWidget *parent ) : QDockWidget( i18n( "Scripts" ), parent ) { QWidget *widget = new KVBox( this ); setWidget( widget ); m_scriptListWidget = new QListWidget( widget ); m_scriptListWidget->setVerticalScrollMode( QAbstractItemView::ScrollPerPixel ); connect( m_scriptListWidget, SIGNAL(doubleClicked(QModelIndex)), SLOT(slotDoubleClicked(QModelIndex)) ); connect( m_scriptListWidget, SIGNAL(currentItemChanged(QListWidgetItem*,QListWidgetItem*)), SLOT(slotCurrentItemChanged(QListWidgetItem*,QListWidgetItem*)) ); } void ScriptListDockWidget::addScript( ScriptConsoleItem *script ) { QListWidgetItem *item = new QListWidgetItem( script->name(), 0 ); item->setData( ScriptRole, QVariant::fromValue( script ) ); m_scriptListWidget->addItem( item ); m_scriptListWidget->setCurrentItem( item ); } void ScriptListDockWidget::removeCurrentScript() { QListWidgetItem *item = m_scriptListWidget->takeItem( m_scriptListWidget->currentRow() ); ScriptConsoleItem *scriptItem = qvariant_cast( item->data( ScriptRole ) ); switch( KMessageBox::warningYesNoCancel( this, i18n( "Remove script file from disk?" ), i18n( "Remove Script" ) ) ) { case KMessageBox::Cancel: return; case KMessageBox::Yes: scriptItem->setClearOnDeletion( true ); default: break; } scriptItem->stop(); scriptItem->deleteLater(); delete item; } void ScriptListDockWidget::slotCurrentItemChanged( QListWidgetItem *newItem, QListWidgetItem *oldItem ) { Q_UNUSED( oldItem ) emit currentItemChanged( newItem ? qvariant_cast( newItem->data(ScriptRole) ) : 0 ); } void ScriptListDockWidget::slotDoubleClicked( const QModelIndex &index ) { emit edit( qvariant_cast( index.data(ScriptRole) ) ); } void ScriptListDockWidget::clear() { if( sender() && KMessageBox::warningContinueCancel( 0, i18n("Are you absolutely certain?") ) == KMessageBox::Cancel ) return; for( int i = 0; icount(); ++i ) qvariant_cast( m_scriptListWidget->item( i )->data( ScriptRole ) )->deleteLater(); m_scriptListWidget->clear(); } void ScriptListDockWidget::addItem( QListWidgetItem *item ) { m_scriptListWidget->addItem( item ); } ScriptListDockWidget::~ScriptListDockWidget() { clear(); } void ScriptListDockWidget::next() { int currentRow = m_scriptListWidget->currentRow(); m_scriptListWidget->setCurrentRow( currentRow > 1 ? currentRow - 1 : currentRow ); } void ScriptListDockWidget::prev() { int currentRow = m_scriptListWidget->currentRow(); m_scriptListWidget->setCurrentRow( currentRow + 1 < m_scriptListWidget->count() ? currentRow + 1 : currentRow ); }