/* * Copyright 2009 Aaron Seigo * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2, 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 Library General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "interactiveconsole.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "plasmaapp.h" #include "scripting/desktopscriptengine.h" //TODO: // use text editor KPart for syntax highlighting? // interative help? static const QString s_autosaveFileName("interactiveconsoleautosave.js"); static const QString s_kwinService = "org.kde.kwin.Scripting"; InteractiveConsole::InteractiveConsole(Plasma::Corona *corona, QWidget *parent) : KDialog(parent), m_corona(corona), m_splitter(new QSplitter(Qt::Vertical, this)), m_docEditor(0), m_editor(0), m_loadAction(KStandardAction::open(this, SLOT(openScriptFile()), this)), m_saveAction(KStandardAction::saveAs(this, SLOT(saveScript()), this)), m_clearAction(KStandardAction::clear(this, SLOT(clearEditor()), this)), m_executeAction(new KAction(KIcon("system-run"), i18n("&Execute"), this)), m_fileDialog(0) { addAction(KStandardAction::close(this, SLOT(close()), this)); addAction(m_saveAction); addAction(m_clearAction); setWindowTitle(KDialog::makeStandardCaption(i18n("Desktop Shell Scripting Console"))); setAttribute(Qt::WA_DeleteOnClose); setButtons(KDialog::None); QWidget *widget = new QWidget(m_splitter); QVBoxLayout *editorLayout = new QVBoxLayout(widget); QLabel *label = new QLabel(i18n("Editor"), widget); QFont f = label->font(); f.setBold(true); label->setFont(f); editorLayout->addWidget(label); KToolBar *toolBar = new KToolBar(this, true, false); toolBar->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); toolBar->addAction(m_loadAction); toolBar->addAction(m_saveAction); toolBar->addAction(m_clearAction); toolBar->addAction(m_executeAction); editorLayout->addWidget(toolBar); m_docEditor = KTextEditor::EditorChooser::editor()->createDocument(widget); if (m_docEditor) { m_docEditor->setHighlightingMode("JavaScript/PlasmaDesktop"); KTextEditor::View * view = m_docEditor->createView(widget); view->setContextMenu(view->defaultContextMenu()); KTextEditor::ConfigInterface *config = qobject_cast(view); if (config) { config->setConfigValue("line-numbers", true); config->setConfigValue("dynamic-word-wrap", true); } editorLayout->addWidget(view); connect(m_docEditor, SIGNAL(textChanged(KTextEditor::Document*)), this, SLOT(scriptTextChanged())); } if (!m_docEditor) { m_editor = new KTextEdit(widget); editorLayout->addWidget(m_editor); connect(m_editor, SIGNAL(textChanged()), this, SLOT(scriptTextChanged())); } m_splitter->addWidget(widget); widget = new QWidget(m_splitter); QVBoxLayout *outputLayout = new QVBoxLayout(widget); label = new QLabel(i18n("Output"), widget); f = label->font(); f.setBold(true); label->setFont(f); outputLayout->addWidget(label); KToolBar *outputToolBar = new KToolBar(widget, true, false); outputToolBar->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); QAction *clearOutputAction = KStandardAction::clear(this, SLOT(clearOutput()), this); outputToolBar->addAction(clearOutputAction); outputLayout->addWidget(outputToolBar); m_output = new QTextBrowser(widget); outputLayout->addWidget(m_output); m_splitter->addWidget(widget); setMainWidget(m_splitter); setInitialSize(QSize(700, 500)); KConfigGroup cg(KGlobal::config(), "InteractiveConsole"); restoreDialogSize(cg); m_splitter->setStretchFactor(0, 10); m_splitter->restoreState(cg.readEntry("SplitterState", QByteArray())); scriptTextChanged(); connect(m_executeAction, SIGNAL(triggered()), this, SLOT(evaluateScript())); m_executeAction->setShortcut(Qt::CTRL + Qt::Key_E); const QString autosave = KStandardDirs::locateLocal("appdata", s_autosaveFileName); if (QFile::exists(autosave)) { loadScript(autosave); } } InteractiveConsole::~InteractiveConsole() { KConfigGroup cg(KGlobal::config(), "InteractiveConsole"); saveDialogSize(cg); cg.writeEntry("SplitterState", m_splitter->saveState()); kDebug(); } void InteractiveConsole::loadScript(const QString &script) { if (m_docEditor) { m_docEditor->closeUrl(false); if (m_docEditor->openUrl(script)) { m_docEditor->setHighlightingMode("JavaScript/PlasmaDesktop"); return; } } else { QFile file(KShell::tildeExpand(script)); if (file.open(QIODevice::ReadOnly | QIODevice::Text) ) { m_editor->setText(file.readAll()); return; } } m_output->append(i18n("Unable to load script file %1", script)); } void InteractiveConsole::showEvent(QShowEvent *) { if (m_docEditor) { m_docEditor->activeView()->setFocus(); } else { m_editor->setFocus(); } } void InteractiveConsole::closeEvent(QCloseEvent *event) { onClose(); KDialog::closeEvent(event); } void InteractiveConsole::reject() { onClose(); KDialog::reject(); } void InteractiveConsole::onClose() { // need to save first! const QString path = KStandardDirs::locateLocal("appdata", s_autosaveFileName); m_closeWhenCompleted = true; saveScript(path); } void InteractiveConsole::print(const QString &string) { m_output->append(string); } void InteractiveConsole::scriptTextChanged() { const bool enable = m_docEditor ? !m_docEditor->isEmpty() : !m_editor->document()->isEmpty(); m_saveAction->setEnabled(enable); m_clearAction->setEnabled(enable); m_executeAction->setEnabled(enable); } void InteractiveConsole::openScriptFile() { delete m_fileDialog; m_fileDialog = new KFileDialog(KUrl(), QString(), 0); m_fileDialog->setOperationMode(KFileDialog::Opening); m_fileDialog->setCaption(i18n("Open Script File")); QStringList mimetypes; mimetypes << "application/javascript"; m_fileDialog->setMimeFilter(mimetypes); connect(m_fileDialog, SIGNAL(finished(int)), this, SLOT(openScriptUrlSelected(int))); m_fileDialog->show(); } void InteractiveConsole::openScriptUrlSelected(int result) { if (!m_fileDialog) { return; } if (result == QDialog::Accepted) { const KUrl url = m_fileDialog->selectedUrl(); if (!url.isEmpty()) { loadScriptFromUrl(url); } } m_fileDialog->deleteLater(); m_fileDialog = 0; } void InteractiveConsole::loadScriptFromUrl(const KUrl &url) { if (m_docEditor) { m_docEditor->closeUrl(false); m_docEditor->openUrl(url); m_docEditor->setHighlightingMode("JavaScript/PlasmaDesktop"); } else { m_editor->clear(); m_editor->setEnabled(false); if (m_job) { m_job.data()->kill(); } m_job = KIO::get(url, KIO::Reload, KIO::HideProgressInfo); connect(m_job.data(), SIGNAL(data(KIO::Job*,QByteArray)), this, SLOT(scriptFileDataRecvd(KIO::Job*,QByteArray))); connect(m_job.data(), SIGNAL(result(KJob*)), this, SLOT(reenableEditor(KJob*))); } } void InteractiveConsole::scriptFileDataRecvd(KIO::Job *job, const QByteArray &data) { Q_ASSERT(m_editor); if (job == m_job.data()) { m_editor->insertPlainText(data); } } void InteractiveConsole::saveScript() { if (m_docEditor) { m_docEditor->documentSaveAs(); return; } delete m_fileDialog; m_fileDialog = new KFileDialog(KUrl(), QString(), 0); m_fileDialog->setOperationMode(KFileDialog::Saving); m_fileDialog->setCaption(i18n("Save Script File")); QStringList mimetypes; mimetypes << "application/javascript"; m_fileDialog->setMimeFilter(mimetypes); connect(m_fileDialog, SIGNAL(finished(int)), this, SLOT(saveScriptUrlSelected(int))); m_fileDialog->show(); } void InteractiveConsole::saveScriptUrlSelected(int result) { if (!m_fileDialog) { return; } if (result == QDialog::Accepted) { const KUrl url = m_fileDialog->selectedUrl(); if (!url.isEmpty()) { saveScript(url); } } m_fileDialog->deleteLater(); m_fileDialog = 0; } void InteractiveConsole::saveScript(const KUrl &url) { if (m_docEditor) { m_docEditor->saveAs(url); } else { m_editor->setEnabled(false); if (m_job) { m_job.data()->kill(); } m_job = KIO::put(url, -1, KIO::HideProgressInfo); connect(m_job.data(), SIGNAL(dataReq(KIO::Job*,QByteArray&)), this, SLOT(scriptFileDataReq(KIO::Job*,QByteArray&))); connect(m_job.data(), SIGNAL(result(KJob*)), this, SLOT(reenableEditor(KJob*))); } } void InteractiveConsole::scriptFileDataReq(KIO::Job *job, QByteArray &data) { Q_ASSERT(m_editor); if (!m_job || m_job.data() != job) { return; } data.append(m_editor->toPlainText().toLocal8Bit()); m_job.clear(); } void InteractiveConsole::reenableEditor(KJob* job) { Q_ASSERT(m_editor); if (m_closeWhenCompleted && job->error() != 0) { close(); } m_closeWhenCompleted = false; m_editor->setEnabled(true); } void InteractiveConsole::evaluateScript() { //kDebug() << "evaluating" << m_editor->toPlainText(); const QString path = KStandardDirs::locateLocal("appdata", s_autosaveFileName); saveScript(path); m_output->moveCursor(QTextCursor::End); QTextCursor cursor = m_output->textCursor(); m_output->setTextCursor(cursor); QTextCharFormat format; format.setFontWeight(QFont::Bold); format.setFontUnderline(true); if (cursor.position() > 0) { cursor.insertText("\n\n"); } QDateTime dt = QDateTime::currentDateTime(); cursor.insertText(i18n("Executing script at %1", KGlobal::locale()->formatDateTime(dt)), format); format.setFontWeight(QFont::Normal); format.setFontUnderline(false); QTextBlockFormat block = cursor.blockFormat(); block.setLeftMargin(10); cursor.insertBlock(block, format); QTime t; t.start(); WorkspaceScripting::DesktopScriptEngine scriptEngine(m_corona, false, this); connect(&scriptEngine, SIGNAL(print(QString)), this, SLOT(print(QString))); connect(&scriptEngine, SIGNAL(printError(QString)), this, SLOT(print(QString))); connect(&scriptEngine, SIGNAL(createPendingPanelViews()), PlasmaApp::self(), SLOT(createWaitingPanels())); scriptEngine.evaluateScript(m_docEditor ? m_docEditor->text() : m_editor->toPlainText()); cursor.insertText("\n\n"); format.setFontWeight(QFont::Bold); // xgettext:no-c-format cursor.insertText(i18n("Runtime: %1ms", QString::number(t.elapsed())), format); block.setLeftMargin(0); cursor.insertBlock(block); m_output->ensureCursorVisible(); } void InteractiveConsole::clearEditor() { if (m_docEditor) { m_docEditor->clear(); } else { m_editor->clear(); } } void InteractiveConsole::clearOutput() { m_output->clear(); } #include "moc_interactiveconsole.cpp"