kde-extraapps/kdevplatform/plugins/externalscript/externalscriptplugin.cpp
2015-07-26 14:23:17 +03:00

370 lines
13 KiB
C++

/*
This plugin is part of KDevelop.
Copyright (C) 2010 Milian Wolff <mail@milianw.de>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "externalscriptplugin.h"
#include "externalscriptview.h"
#include "externalscriptitem.h"
#include "externalscriptjob.h"
#include "externalscriptdebug.h"
#include <interfaces/icore.h>
#include <interfaces/iuicontroller.h>
#include <interfaces/iruncontroller.h>
#include <interfaces/idocumentcontroller.h>
#include <interfaces/contextmenuextension.h>
#include <interfaces/context.h>
#include <interfaces/isession.h>
#include <outputview/outputjob.h>
#include <outputview/outputmodel.h>
#include <project/projectmodel.h>
#include <util/path.h>
#include <language/interfaces/editorcontext.h>
#include <KPluginFactory>
#include <KAboutData>
#include <KAction>
#include <KProcess>
#include <QStandardItemModel>
#include <QDBusConnection>
#include <QMenu>
K_PLUGIN_FACTORY( ExternalScriptFactory, registerPlugin<ExternalScriptPlugin>(); )
K_EXPORT_PLUGIN( ExternalScriptFactory( KAboutData( "kdevexternalscript", "kdevexternalscript", ki18n( "External Scripts" ),
"0.1", ki18n( "Run external scripts or applications to manipulate the editor contents or do other arbitrary actions." ),
KAboutData::License_GPL ) ) )
class ExternalScriptViewFactory: public KDevelop::IToolViewFactory
{
public:
ExternalScriptViewFactory( ExternalScriptPlugin *plugin ): m_plugin( plugin ) {}
virtual QWidget* create( QWidget *parent = 0 ) {
return new ExternalScriptView( m_plugin, parent );
}
virtual Qt::DockWidgetArea defaultPosition() {
return Qt::RightDockWidgetArea;
}
virtual QString id() const {
return "org.kdevelop.ExternalScriptView";
}
private:
ExternalScriptPlugin *m_plugin;
};
ExternalScriptPlugin* ExternalScriptPlugin::m_self = 0;
ExternalScriptPlugin::ExternalScriptPlugin( QObject* parent, const QVariantList& /*args*/ )
: IPlugin( ExternalScriptFactory::componentData(), parent ),
m_model( new QStandardItemModel( this ) ), m_factory( new ExternalScriptViewFactory( this ) )
{
Q_ASSERT( !m_self );
m_self = this;
QDBusConnection::sessionBus().registerObject( "/org/kdevelop/ExternalScriptPlugin", this, QDBusConnection::ExportScriptableSlots );
setXMLFile( "kdevexternalscript.rc" );
//BEGIN load config
KConfigGroup config = getConfig();
foreach( const QString group, config.groupList() ) {
KConfigGroup script = config.group( group );
if ( script.hasKey( "name" ) && script.hasKey( "command" ) ) {
ExternalScriptItem* item = new ExternalScriptItem;
item->setText( script.readEntry( "name" ) );
item->setCommand( script.readEntry( "command" ));
item->setInputMode( static_cast<ExternalScriptItem::InputMode>( script.readEntry( "inputMode", 0u ) ) );
item->setOutputMode( static_cast<ExternalScriptItem::OutputMode>( script.readEntry( "outputMode", 0u ) ) );
item->setErrorMode( static_cast<ExternalScriptItem::ErrorMode>( script.readEntry( "errorMode", 0u ) ) );
item->setSaveMode( static_cast<ExternalScriptItem::SaveMode>( script.readEntry( "saveMode", 0u ) ) );
item->setFilterMode( script.readEntry( "filterMode", 0u ));
item->action()->setShortcut( KShortcut( script.readEntry( "shortcuts" ) ) );
item->setShowOutput( script.readEntry( "showOutput", true ) );
m_model->appendRow( item );
}
}
//END load config
core()->uiController()->addToolView( i18n( "External Scripts" ), m_factory );
connect( m_model, SIGNAL(rowsRemoved(QModelIndex,int,int)),
this, SLOT(rowsRemoved(QModelIndex,int,int)) );
connect( m_model, SIGNAL(rowsInserted(QModelIndex,int,int)),
this, SLOT(rowsInserted(QModelIndex,int,int)) );
const bool firstUse = config.readEntry( "firstUse", true );
if ( firstUse ) {
// some example scripts
ExternalScriptItem* item = new ExternalScriptItem;
item->setText( i18n("Quick Compile") );
item->setCommand( "g++ -o %b %f && ./%b" );
m_model->appendRow( item );
item = new ExternalScriptItem;
item->setText( i18n("Google Selection") );
item->setCommand( "xdg-open \"http://www.google.de/search?q=%s\"" );
item->setShowOutput( false );
m_model->appendRow( item );
item = new ExternalScriptItem;
item->setText( i18n("Sort Selection") );
item->setCommand( "sort" );
item->setInputMode( ExternalScriptItem::InputSelectionOrDocument );
item->setOutputMode( ExternalScriptItem::OutputReplaceSelectionOrDocument );
item->setShowOutput( false );
m_model->appendRow( item );
config.writeEntry( "firstUse", false );
config.sync();
}
}
ExternalScriptPlugin* ExternalScriptPlugin::self()
{
return m_self;
}
ExternalScriptPlugin::~ExternalScriptPlugin()
{
m_self = 0;
}
KDevelop::ContextMenuExtension ExternalScriptPlugin::contextMenuExtension( KDevelop::Context* context )
{
m_urls.clear();
int folderCount = 0;
if ( context->type() == KDevelop::Context::FileContext ) {
KDevelop::FileContext* filectx = dynamic_cast<KDevelop::FileContext*>( context );
m_urls = filectx->urls();
} else if ( context->type() == KDevelop::Context::ProjectItemContext ) {
KDevelop::ProjectItemContext* projctx = dynamic_cast<KDevelop::ProjectItemContext*>( context );
foreach( KDevelop::ProjectBaseItem* item, projctx->items() ) {
if ( item->file() ) {
m_urls << item->file()->path().toUrl();
} else if ( item->folder() ) {
m_urls << item->folder()->path().toUrl();
folderCount++;
}
}
} else if ( context->type() == KDevelop::Context::EditorContext ) {
KDevelop::EditorContext *econtext = dynamic_cast<KDevelop::EditorContext*>(context);
m_urls << econtext->url();
}
if ( !m_urls.isEmpty() ) {
KDevelop::ContextMenuExtension ext;
QMenu* menu = new QMenu();
menu->setTitle( i18n("External Scripts") );
for ( int row = 0; row < m_model->rowCount(); ++row ) {
ExternalScriptItem* item = dynamic_cast<ExternalScriptItem*>( m_model->item( row ) );
Q_ASSERT( item );
if (context->type() != KDevelop::Context::EditorContext) {
// filter scripts that depend on an opened document
// if the context menu was not requested inside the editor
if (item->performParameterReplacement() && item->command().contains("%s")) {
continue;
} else if (item->inputMode() == ExternalScriptItem::InputSelectionOrNone) {
continue;
}
}
if ( folderCount == m_urls.count() ) {
// when only folders filter items that don't have %d parameter (or another parameter)
if (item->performParameterReplacement() &&
(!item->command().contains("%d") ||
item->command().contains("%s") ||
item->command().contains("%u") ||
item->command().contains("%f") ||
item->command().contains("%b") ||
item->command().contains("%n")
)
) {
continue;
}
}
KAction* scriptAction = new KAction( item->text(), this );
scriptAction->setData( QVariant::fromValue<ExternalScriptItem*>( item ));
connect( scriptAction, SIGNAL( triggered() ), SLOT( executeScriptFromContextMenu() ) );
menu->addAction( scriptAction );
}
ext.addAction( KDevelop::ContextMenuExtension::ExtensionGroup, menu->menuAction() );
return ext;
}
return KDevelop::IPlugin::contextMenuExtension( context );
}
void ExternalScriptPlugin::unload()
{
core()->uiController()->removeToolView( m_factory );
KDevelop::IPlugin::unload();
}
KConfigGroup ExternalScriptPlugin::getConfig() const
{
return KGlobal::config()->group("External Scripts");
}
QStandardItemModel* ExternalScriptPlugin::model() const
{
return m_model;
}
void ExternalScriptPlugin::execute( ExternalScriptItem* item, const KUrl& url ) const
{
ExternalScriptJob* job = new ExternalScriptJob( item, url, const_cast<ExternalScriptPlugin*>(this) );
KDevelop::ICore::self()->runController()->registerJob( job );
}
void ExternalScriptPlugin::execute(ExternalScriptItem* item) const
{
auto document = KDevelop::ICore::self()->documentController()->activeDocument();
execute( item, document ? document->url() : KUrl() );
}
bool ExternalScriptPlugin::executeCommand ( QString command, QString workingDirectory ) const
{
// We extend ExternalScriptJob so that it deletes the temporarily created item on destruction
class ExternalScriptJobOwningItem : public ExternalScriptJob {
public:
ExternalScriptJobOwningItem( ExternalScriptItem* item, const KUrl &url, ExternalScriptPlugin* parent ) : ExternalScriptJob(item, url, parent), m_item(item) {
}
~ExternalScriptJobOwningItem() {
delete m_item;
}
private:
ExternalScriptItem* m_item;
};
ExternalScriptItem* item = new ExternalScriptItem;
item->setCommand(command);
item->setWorkingDirectory(workingDirectory);
item->setPerformParameterReplacement(false);
debug() << "executing command " << command << " in dir " << workingDirectory << " as external script";
ExternalScriptJobOwningItem* job = new ExternalScriptJobOwningItem( item, KUrl(), const_cast<ExternalScriptPlugin*>(this) );
// When a command is executed, for example through the terminal, we don't want the command output to be risen
job->setVerbosity(KDevelop::OutputJob::Silent);
KDevelop::ICore::self()->runController()->registerJob( job );
return true;
}
QString ExternalScriptPlugin::executeCommandSync ( QString command, QString workingDirectory ) const
{
debug() << "executing command " << command << " in working-dir " << workingDirectory;
KProcess process;
process.setWorkingDirectory( workingDirectory );
process.setShellCommand( command );
process.setOutputChannelMode( KProcess::OnlyStdoutChannel );
process.execute();
return QString::fromLocal8Bit(process.readAll());
}
void ExternalScriptPlugin::executeScriptFromActionData() const
{
KAction* action = dynamic_cast<KAction*>( sender() );
Q_ASSERT( action );
ExternalScriptItem* item = action->data().value<ExternalScriptItem*>();
Q_ASSERT( item );
execute( item );
}
void ExternalScriptPlugin::executeScriptFromContextMenu() const
{
KAction* action = dynamic_cast<KAction*>( sender() );
Q_ASSERT( action );
ExternalScriptItem* item = action->data().value<ExternalScriptItem*>();
Q_ASSERT( item );
foreach( const KUrl& url, m_urls) {
KDevelop::ICore::self()->documentController()->openDocument( url );
execute( item, url );
}
}
void ExternalScriptPlugin::rowsInserted( const QModelIndex& /*parent*/, int start, int end )
{
for ( int i = start; i <= end; ++i ) {
saveItemForRow( i );
}
}
void ExternalScriptPlugin::rowsRemoved( const QModelIndex& /*parent*/, int start, int end )
{
KConfigGroup config = getConfig();
for ( int i = start; i <= end; ++i ) {
KConfigGroup child = config.group( QString("script %1").arg(i) );
debug() << "removing config group:" << child.name();
child.deleteGroup();
}
config.sync();
}
void ExternalScriptPlugin::saveItem( const ExternalScriptItem* item )
{
const QModelIndex index = m_model->indexFromItem( item );
Q_ASSERT( index.isValid() );
saveItemForRow( index.row() );
}
void ExternalScriptPlugin::saveItemForRow( int row )
{
const QModelIndex idx = m_model->index( row, 0 );
Q_ASSERT( idx.isValid() );
ExternalScriptItem* item = dynamic_cast<ExternalScriptItem*>( m_model->item( row ) );
Q_ASSERT( item );
debug() << "save extern script:" << item << idx;
KConfigGroup config = getConfig().group( QString("script %1").arg( row ) );
config.writeEntry( "name", item->text() );
config.writeEntry( "command", item->command() );
config.writeEntry( "inputMode", (uint) item->inputMode() );
config.writeEntry( "outputMode", (uint) item->outputMode() );
config.writeEntry( "errorMode", (uint) item->errorMode() );
config.writeEntry( "saveMode", (uint) item->saveMode() );
config.writeEntry( "shortcuts", item->action()->shortcut().toString() );
config.writeEntry( "showOutput", item->showOutput() );
config.writeEntry( "filterMode", item->filterMode());
config.sync();
}
#include "moc_externalscriptplugin.cpp"
// kate: indent-mode cstyle; space-indent on; indent-width 2; replace-tabs on;