kde-extraapps/kdevplatform/plugins/openwith/openwithplugin.cpp

277 lines
9.6 KiB
C++
Raw Normal View History

/*
* This file is part of KDevelop
* Copyright 2009 Andreas Pakulat <apaku@gmx.de>
*
* 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 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, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "openwithplugin.h"
#include <QVariantList>
#include <kpluginfactory.h>
#include <kpluginloader.h>
#include <kaboutdata.h>
#include <klocale.h>
#include <kaction.h>
#include <interfaces/contextmenuextension.h>
#include <interfaces/context.h>
#include <project/projectmodel.h>
#include <util/path.h>
#include <kmimetype.h>
#include <kservice.h>
#include <kservicetypetrader.h>
#include <kmimetypetrader.h>
#include <QSignalMapper>
#include <kmenu.h>
#include <krun.h>
#include <interfaces/icore.h>
#include <interfaces/iuicontroller.h>
#include <interfaces/iruncontroller.h>
#include <interfaces/idocumentcontroller.h>
#include <kparts/mainwindow.h>
#include <KMessageBox>
#include <QApplication>
#include <KConfigGroup>
using namespace KDevelop;
K_PLUGIN_FACTORY(KDevOpenWithFactory, registerPlugin<OpenWithPlugin>(); )
K_EXPORT_PLUGIN(KDevOpenWithFactory(KAboutData("kdevopenwith","kdevopenwith", ki18n("Open With"), "0.1", ki18n("This plugin allows to open files with associated external applications."), KAboutData::License_GPL)))
namespace {
bool sortActions(QAction* left, QAction* right)
{
return left->text() < right->text();
}
bool isTextEditor(const KService::Ptr& service)
{
return service->serviceTypes().contains( "KTextEditor/Document" );
}
QString defaultForMimeType(const QString& mimeType)
{
KConfigGroup config = KGlobal::config()->group("Open With Defaults");
if (config.hasKey(mimeType)) {
QString storageId = config.readEntry(mimeType, QString());
if (!storageId.isEmpty() && KService::serviceByStorageId(storageId)) {
return storageId;
}
}
return QString();
}
bool canOpenDefault(const QString& mimeType)
{
if (defaultForMimeType(mimeType).isEmpty() && mimeType == "inode/directory") {
// potentially happens in non-kde environments apparently, see https://git.reviewboard.kde.org/r/122373
return KMimeTypeTrader::self()->preferredService(mimeType);
} else {
return true;
}
}
}
OpenWithPlugin::OpenWithPlugin ( QObject* parent, const QVariantList& )
: IPlugin ( KDevOpenWithFactory::componentData(), parent ),
m_actionMap( 0 )
{
KDEV_USE_EXTENSION_INTERFACE( IOpenWith )
}
OpenWithPlugin::~OpenWithPlugin()
{
}
KDevelop::ContextMenuExtension OpenWithPlugin::contextMenuExtension( KDevelop::Context* context )
{
// do not recurse
if (context->hasType(KDevelop::Context::OpenWithContext)) {
return ContextMenuExtension();
}
m_urls.clear();
m_actionMap.reset();
m_services.clear();
FileContext* filectx = dynamic_cast<FileContext*>( context );
ProjectItemContext* projctx = dynamic_cast<ProjectItemContext*>( context );
if ( filectx && filectx->urls().count() > 0 ) {
m_urls = filectx->urls();
} else if ( projctx && projctx->items().count() > 0 ) {
// For now, let's handle *either* files only *or* directories only
const int wantedType = projctx->items().first()->type();
foreach( ProjectBaseItem* item, projctx->items() ) {
if (wantedType == ProjectBaseItem::File && item->file()) {
m_urls << item->file()->path().toUrl();
} else if ((wantedType == ProjectBaseItem::Folder || wantedType == ProjectBaseItem::BuildFolder) && item->folder()) {
m_urls << item->folder()->path().toUrl();
}
}
}
if (m_urls.isEmpty()) {
return KDevelop::ContextMenuExtension();
}
m_actionMap.reset(new QSignalMapper( this ));
connect( m_actionMap.data(), SIGNAL(mapped(QString)), SLOT(open(QString)) );
// Ok, lets fetch the mimetype for the !!first!! url and the relevant services
// TODO: Think about possible alternatives to using the mimetype of the first url.
KMimeType::Ptr mimetype = KMimeType::findByUrl( m_urls.first() );
m_mimeType = mimetype->name();
QList<QAction*> partActions = actionsForServiceType("KParts/ReadOnlyPart");
QList<QAction*> appActions = actionsForServiceType("Application");
OpenWithContext subContext(m_urls, mimetype);
QList<ContextMenuExtension> extensions = ICore::self()->pluginController()->queryPluginsForContextMenuExtensions( &subContext );
foreach( const ContextMenuExtension& ext, extensions ) {
appActions += ext.actions(ContextMenuExtension::OpenExternalGroup);
partActions += ext.actions(ContextMenuExtension::OpenEmbeddedGroup);
}
// Now setup a menu with actions for each part and app
KMenu* menu = new KMenu( i18n("Open With" ) );
menu->setIcon( SmallIcon( "document-open" ) );
if (!partActions.isEmpty()) {
menu->addTitle(i18n("Embedded Editors"));
menu->addActions( partActions );
}
if (!appActions.isEmpty()) {
menu->addTitle(i18n("External Applications"));
menu->addActions( appActions );
}
KDevelop::ContextMenuExtension ext;
if (canOpenDefault(m_mimeType)) {
KAction* openAction = new KAction( i18n( "Open" ), this );
openAction->setIcon( SmallIcon( "document-open" ) );
connect( openAction, SIGNAL(triggered()), SLOT(openDefault()) );
ext.addAction( KDevelop::ContextMenuExtension::FileGroup, openAction );
}
if (!menu->isEmpty()) {
ext.addAction(KDevelop::ContextMenuExtension::FileGroup, menu->menuAction());
} else {
delete menu;
}
return ext;
}
QList<QAction*> OpenWithPlugin::actionsForServiceType( const QString& serviceType )
{
KService::List list = KMimeTypeTrader::self()->query( m_mimeType, serviceType );
KService::Ptr pref = KMimeTypeTrader::self()->preferredService( m_mimeType, serviceType );
m_services += list;
QList<QAction*> actions;
QAction* standardAction = 0;
const QString defaultId = defaultForMimeType(m_mimeType);
foreach( KService::Ptr svc, list ) {
KAction* act = new KAction( isTextEditor(svc) ? i18n("Default Editor") : svc->name(), this );
act->setIcon( SmallIcon( svc->icon() ) );
if (svc->storageId() == defaultId || (defaultId.isEmpty() && isTextEditor(svc))) {
QFont font = act->font();
font.setBold(true);
act->setFont(font);
}
connect(act, SIGNAL(triggered()), m_actionMap.data(), SLOT(map()));
m_actionMap->setMapping( act, svc->storageId() );
actions << act;
if ( isTextEditor(svc) ) {
standardAction = act;
} else if ( svc->storageId() == pref->storageId() ) {
standardAction = act;
}
}
qSort(actions.begin(), actions.end(), sortActions);
if (standardAction) {
actions.removeOne(standardAction);
actions.prepend(standardAction);
}
return actions;
}
void OpenWithPlugin::openDefault()
{
// check preferred handler
const QString defaultId = defaultForMimeType(m_mimeType);
if (!defaultId.isEmpty()) {
open(defaultId);
return;
}
// default handlers
if (m_mimeType == "inode/directory") {
KService::Ptr service = KMimeTypeTrader::self()->preferredService(m_mimeType);
KRun::run(*service, m_urls, ICore::self()->uiController()->activeMainWindow());
} else {
foreach( const KUrl& u, m_urls ) {
ICore::self()->documentController()->openDocument( u );
}
}
}
void OpenWithPlugin::open( const QString& storageid )
{
KService::Ptr svc = KService::serviceByStorageId( storageid );
if( svc->isApplication() ) {
KRun::run( *svc, m_urls, ICore::self()->uiController()->activeMainWindow() );
} else {
QString prefName = svc->desktopEntryName();
if ( isTextEditor(svc) ) {
// If the user chose a KTE part, lets make sure we're creating a TextDocument instead of
// a PartDocument by passing no preferredpart to the documentcontroller
// TODO: Solve this rather inside DocumentController
prefName = "";
}
foreach( const KUrl& u, m_urls ) {
ICore::self()->documentController()->openDocument( u, prefName );
}
}
KConfigGroup config = KGlobal::config()->group("Open With Defaults");
if (storageid != config.readEntry(m_mimeType, QString())) {
int setDefault = KMessageBox::questionYesNo(
qApp->activeWindow(),
i18nc("%1: mime type name, %2: app/part name", "Do you want to open all '%1' files by default with %2?",
m_mimeType, svc->name() ),
i18n("Set as default?"),
KStandardGuiItem::yes(), KStandardGuiItem::no(),
QString("OpenWith-%1").arg(m_mimeType)
);
if (setDefault == KMessageBox::Yes) {
config.writeEntry(m_mimeType, storageid);
}
}
}
void OpenWithPlugin::openFilesInternal( const KUrl::List& files )
{
if (files.isEmpty()) {
return;
}
m_urls = files;
m_mimeType = KMimeType::findByUrl( m_urls.first() )->name();
openDefault();
}