diff --git a/activities/fileitemplugin/FileItemLinkingPlugin.cpp b/activities/fileitemplugin/FileItemLinkingPlugin.cpp index 37d3fce8..28e8f930 100644 --- a/activities/fileitemplugin/FileItemLinkingPlugin.cpp +++ b/activities/fileitemplugin/FileItemLinkingPlugin.cpp @@ -1,213 +1,214 @@ /* * Copyright (C) 2012 - 2016 by Ivan Cukic * * 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) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * 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 "FileItemLinkingPlugin.h" #include "FileItemLinkingPlugin_p.h" #include "FileItemLinkingPluginActionLoader.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "common/dbus/common.h" K_PLUGIN_CLASS_WITH_JSON(FileItemLinkingPlugin, "kactivitymanagerd_fileitem_linking_plugin.json") // Private FileItemLinkingPlugin::Private::Private() : shouldLoad(false) , loaded(false) { connect(&activities, &KActivities::Consumer::serviceStatusChanged, this, &Private::activitiesServiceStatusChanged); } void FileItemLinkingPlugin::Private::activitiesServiceStatusChanged( KActivities::Consumer::ServiceStatus status) { if (status != KActivities::Consumer::Unknown) { loadAllActions(); } } void FileItemLinkingPlugin::Private::rootActionHovered() { shouldLoad = true; loadAllActions(); } void FileItemLinkingPlugin::Private::actionTriggered() { QAction *action = dynamic_cast(sender()); if (!action) { return; } bool link = action->property("link").toBool(); QString activity = action->property("activity").toString(); KAMD_DBUS_DECL_INTERFACE(service, Resources/Linking, ResourcesLinking); - foreach (const auto &item, items.urlList()) { + const auto urlList = items.urlList(); + for (const auto &item : urlList) { service.asyncCall( link ? "LinkResourceToActivity" : "UnlinkResourceFromActivity", QString(), item.toLocalFile(), activity); } } QAction *FileItemLinkingPlugin::Private::basicAction(QWidget *parentWidget) { root = new QAction(QIcon::fromTheme("preferences-activities"), i18n("Activities"), parentWidget); rootMenu = new QMenu(); rootMenu->addAction(new QAction(i18n("Loading..."), this)); connect(root, &QAction::hovered, this, &Private::rootActionHovered); root->setMenu(rootMenu); return root; } void FileItemLinkingPlugin::Private::loadAllActions() { if (!shouldLoad || activities.serviceStatus() == KActivities::Consumer::Unknown) { return; } if (activities.serviceStatus() == KActivities::Consumer::NotRunning) { Action action = { }; action.title = i18n("The Activity Manager is not running"); setActions({ action }); } else if (!loaded) { auto loader = FileItemLinkingPluginActionLoader::create(items); static FileItemLinkingPluginActionStaticInit init; connect(loader, &FileItemLinkingPluginActionLoader::result, this, &Private::setActions, Qt::QueuedConnection); loader->start(); loaded = true; // ignore that the thread may not be finished at this time } } void FileItemLinkingPlugin::Private::setActions(const ActionList &actions) { if (!rootMenu) { return; } for (auto action: rootMenu->actions()) { rootMenu->removeAction(action); action->deleteLater(); } for (const auto& actionInfo: actions) { if (actionInfo.icon != "-") { auto action = new QAction(nullptr); action->setText(actionInfo.title); action->setIcon(QIcon::fromTheme(actionInfo.icon)); action->setProperty("activity", actionInfo.activity); action->setProperty("link", actionInfo.link); rootMenu->addAction(action); connect(action, &QAction::triggered, this, &Private::actionTriggered); } else { auto action = new QAction(actionInfo.title, nullptr); action->setSeparator(true); rootMenu->addAction(action); } } } FileItemLinkingPluginActionStaticInit::FileItemLinkingPluginActionStaticInit() { qRegisterMetaType("Action"); qRegisterMetaType("ActionList"); } // Main class FileItemLinkingPlugin::FileItemLinkingPlugin(QObject *parent, const QVariantList &) : KAbstractFileItemActionPlugin(parent) { } FileItemLinkingPlugin::~FileItemLinkingPlugin() { d->setActions({}); } QList FileItemLinkingPlugin::actions(const KFileItemListProperties &fileItemInfos, QWidget *parentWidget) { // We can only link local files, don't show the menu if there are none // KFileItemListProperties::isLocal() is for *all* being local, we just want *any* local const auto urlList = fileItemInfos.urlList(); const bool hasLocalUrl = std::any_of(urlList.begin(), urlList.end(), [](const QUrl &url) { return url.isLocalFile(); }); if (!hasLocalUrl) { return {}; } d->items = fileItemInfos; return { d->basicAction(parentWidget) }; } #include "FileItemLinkingPlugin.moc" diff --git a/filenamesearch/kio_filenamesearch.cpp b/filenamesearch/kio_filenamesearch.cpp index e6cd130a..b5cfdfa8 100644 --- a/filenamesearch/kio_filenamesearch.cpp +++ b/filenamesearch/kio_filenamesearch.cpp @@ -1,184 +1,184 @@ /*************************************************************************** * Copyright (C) 2010 by Peter Penz * * * * 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, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * ***************************************************************************/ #include "kio_filenamesearch.h" #include #include #include #include #include #include #include #include #include #include Q_LOGGING_CATEGORY(KIO_FILENAMESEARCH, "kio_filenamesearch") FileNameSearchProtocol::FileNameSearchProtocol(const QByteArray &pool, const QByteArray &app) : SlaveBase("search", pool, app) { QDBusInterface kded(QStringLiteral("org.kde.kded5"), QStringLiteral("/kded"), QStringLiteral("org.kde.kded5")); kded.call(QStringLiteral("loadModule"), QStringLiteral("filenamesearchmodule")); } FileNameSearchProtocol::~FileNameSearchProtocol() { } void FileNameSearchProtocol::listDir(const QUrl &url) { const QUrlQuery urlQuery(url); const QString search = urlQuery.queryItemValue("search"); if (search.isEmpty()) { finished(); return; } const QRegularExpression pattern(search, QRegularExpression::CaseInsensitiveOption); std::function validator; if (urlQuery.queryItemValue("checkContent") == QStringLiteral("yes")) { validator = [pattern](const KFileItem &item) -> bool { return item.determineMimeType().inherits(QStringLiteral("text/plain")) && contentContainsPattern(item.url(), pattern); }; } else { validator = [pattern](const KFileItem &item) -> bool { return item.text().contains(pattern); }; } QSet iteratedDirs; const QUrl directory(urlQuery.queryItemValue("url")); searchDirectory(directory, validator, iteratedDirs); finished(); } void FileNameSearchProtocol::searchDirectory(const QUrl &directory, const std::function &itemValidator, QSet &iteratedDirs) { if (directory.path() == QStringLiteral("/proc")) { // Don't try to iterate the /proc directory of Linux return; } // Get all items of the directory QScopedPointer dirLister(new KCoreDirLister); dirLister->setDelayedMimeTypes(true); dirLister->openUrl(directory); QEventLoop eventLoop; QObject::connect(dirLister.data(), static_cast(&KCoreDirLister::canceled), &eventLoop, &QEventLoop::quit); QObject::connect(dirLister.data(), static_cast(&KCoreDirLister::completed), &eventLoop, &QEventLoop::quit); eventLoop.exec(); // Visualize all items that match the search pattern QList pendingDirs; const KFileItemList items = dirLister->items(); - foreach (const KFileItem &item, items) { + for (const KFileItem &item : items) { if (itemValidator(item)) { KIO::UDSEntry entry = item.entry(); entry.replace(KIO::UDSEntry::UDS_URL, item.url().url()); listEntry(entry); } if (item.isDir()) { if (item.isLink()) { // Assure that no endless searching is done in directories that // have already been iterated. const QUrl linkDest = item.url().resolved(QUrl::fromLocalFile(item.linkDest())); if (!iteratedDirs.contains(linkDest.path())) { pendingDirs.append(linkDest); } } else { pendingDirs.append(item.url()); } } } iteratedDirs.insert(directory.path()); dirLister.reset(); // Recursively iterate all sub directories - foreach (const QUrl &pendingDir, pendingDirs) { + for (const QUrl &pendingDir : qAsConst(pendingDirs)) { searchDirectory(pendingDir, itemValidator, iteratedDirs); } } bool FileNameSearchProtocol::contentContainsPattern(const QUrl &fileName, const QRegularExpression &pattern) { auto fileContainsPattern = [&pattern](const QString &path) -> bool { QFile file(path); if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { return false; } QTextStream in(&file); while (!in.atEnd()) { const QString line = in.readLine(); if (line.contains(pattern)) { return true; } } return false; }; if (fileName.isLocalFile()) { return fileContainsPattern(fileName.toLocalFile()); } else { QTemporaryFile tempFile; if (tempFile.open()) { KIO::Job* getJob = KIO::file_copy(fileName, QUrl::fromLocalFile(tempFile.fileName()), -1, KIO::Overwrite | KIO::HideProgressInfo); if (getJob->exec()) { // The non-local file was downloaded successfully. return fileContainsPattern(tempFile.fileName()); } } } return false; } extern "C" int Q_DECL_EXPORT kdemain(int argc, char **argv) { QCoreApplication app(argc, argv); if (argc != 4) { qCDebug(KIO_FILENAMESEARCH) << "Usage: kio_filenamesearch protocol domain-socket1 domain-socket2" << endl; return -1; } FileNameSearchProtocol slave(argv[2], argv[3]); slave.dispatchLoop(); return 0; } diff --git a/network/ioslave/networkslave.cpp b/network/ioslave/networkslave.cpp index 8e45f9d3..8b0a60c0 100644 --- a/network/ioslave/networkslave.cpp +++ b/network/ioslave/networkslave.cpp @@ -1,319 +1,317 @@ /* This file is part of the network kioslave, part of the KDE project. Copyright 2009 Friedrich W. H. Kossebau 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) version 3, or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 6 of version 3 of the license. 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, see . */ #include "networkslave.h" // ioslave #include "networkuri.h" #include "mimetypes.h" #include "networkdbusinterface.h" // network #include #include // Qt #include #include // static const char NetworkIconName[] = "network-workgroup"; NetworkSlave::NetworkSlave( const QByteArray& name, const QByteArray& poolSocket, const QByteArray& programSocket ) : SlaveBase( name, poolSocket, programSocket ) { //qDebug(); mNetworkDBusProxy = new NetworkDBusInterface( QLatin1String("org.kde.kded5"), QLatin1String("/modules/networkwatcher"), QDBusConnection::sessionBus() ); } void NetworkSlave::get( const QUrl& url ) { const NetworkUri networkUri( url ); bool successfulGetting = false; const NetworkUri::Type type = networkUri.type(); //qDebug()<<"type="< reply = mNetworkDBusProxy->serviceData( hostAddress, serviceName, serviceType ); //qDebug()< // network #include #include #include // KDE #include // Qt #include #include namespace Mollet { static inline QString idFrom( const NetworkUri& networkUri ) { return networkUri.hostAddress().isEmpty() ? QString() : networkUri.serviceName().isEmpty() ? networkUri.hostAddress() : /*else*/ networkUri.hostAddress()+QLatin1Char('/')+networkUri.serviceName(); } static inline QString dirIdFor( const NetDevice& device ) { Q_UNUSED( device ) return QString(); } static inline QString pathFor( const NetDevice& device ) { return device.hostAddress(); } static inline QString dirIdFor( const NetService& service ) { return service.device().hostAddress(); } static inline QString pathFor( const NetService& service ) { return service.device().hostAddress() + QLatin1Char('/') + service.name()+QLatin1Char('.')+service.type(); } KioSlaveNotifier::KioSlaveNotifier( Network* network, QObject* parent ) : QObject( parent ) { QDBusConnection sessionBus = QDBusConnection::sessionBus(); const QString allServices; const QString allPaths; const QString interface = QLatin1String( "org.kde.KDirNotify" ); sessionBus.connect( allServices, allPaths, interface, QLatin1String("enteredDirectory"), this, SLOT(onDirectoryEntered(QString)) ); sessionBus.connect( allServices, allPaths, interface, QLatin1String("leftDirectory"), this, SLOT(onDirectoryLeft(QString)) ); new KioSlaveNotifierAdaptor( this ); connect( network, SIGNAL(devicesAdded(QList)), SLOT(onDevicesAdded(QList)) ); connect( network, SIGNAL(devicesRemoved(QList)), SLOT(onDevicesRemoved(QList)) ); connect( network, SIGNAL(servicesAdded(QList)), SLOT(onServicesAdded(QList)) ); connect( network, SIGNAL(servicesRemoved(QList)), SLOT(onServicesRemoved(QList)) ); } QStringList KioSlaveNotifier::watchedDirectories() const { return mWatchedDirs.keys(); } void KioSlaveNotifier::onDirectoryEntered( const QString& directory ) { //qDebug()<::Iterator it = mWatchedDirs.find( id ); if( it == mWatchedDirs.end() ) { mWatchedDirs.insert( id, 1 ); } else *it++; } void KioSlaveNotifier::onDirectoryLeft( const QString& directory ) { //qDebug()<::Iterator it = mWatchedDirs.find( id ); if( it == mWatchedDirs.end() ) return; if( *it == 1 ) mWatchedDirs.erase( it ); else *it--; } void KioSlaveNotifier::notifyAboutAdded( const QString& dirId ) { QHash::Iterator it = mWatchedDirs.find( dirId ); if( it != mWatchedDirs.end() ) { const QUrl url( QLatin1String("network:/") + dirId ); //qDebug()<::Iterator it = mWatchedDirs.find( dirId ); if( it != mWatchedDirs.end() ) { QList itemUrls; itemUrls.append( QUrl( QStringLiteral("network:/") + itemPath ) ); //qDebug()<& deviceList ) { - foreach( const NetDevice& device, deviceList ) - { + for (const NetDevice& device : deviceList) { const QString id = dirIdFor( device ); notifyAboutAdded( id ); } } void KioSlaveNotifier::onDevicesRemoved( const QList& deviceList ) { - foreach( const NetDevice& device, deviceList ) - { + for (const NetDevice& device : deviceList) { const QString dirId = dirIdFor( device ); const QString itemPath = pathFor( device ); notifyAboutRemoved( dirId, itemPath ); } } void KioSlaveNotifier::onServicesAdded( const QList& serviceList ) { - foreach( const NetService& service, serviceList ) - { + for (const NetService& service : serviceList) { const QString id = dirIdFor( service ); notifyAboutAdded( id ); } } void KioSlaveNotifier::onServicesRemoved( const QList& serviceList ) { - foreach( const NetService& service, serviceList ) - { + for (const NetService& service : serviceList) { const QString dirId = dirIdFor( service ); const QString itemPath = pathFor( service ); notifyAboutRemoved( dirId, itemPath ); } } KioSlaveNotifier::~KioSlaveNotifier() { } } diff --git a/network/kded/networkwatcher.cpp b/network/kded/networkwatcher.cpp index f4e2cbe3..c69de2a4 100644 --- a/network/kded/networkwatcher.cpp +++ b/network/kded/networkwatcher.cpp @@ -1,126 +1,122 @@ /* This file is part of the network kioslave, part of the KDE project. Copyright 2009 Friedrich W. H. Kossebau 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) version 3, or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 6 of version 3 of the license. 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, see . */ #include "networkwatcher.h" // module #include "networkdbusadaptor.h" #include "kioslavenotifier.h" // network #include // KF #include #include // Qt #include namespace Mollet { K_PLUGIN_CLASS_WITH_JSON(NetworkWatcher, "networkwatcher.json") NetworkWatcher::NetworkWatcher( QObject* parent, const QList& parameters ) : KDEDModule( parent ) { Q_UNUSED( parameters ) mNetwork = Network::network(); new KioSlaveNotifier( mNetwork ); new NetworkDBusAdaptor( this ); QDBusConnection::sessionBus().registerService( QString::fromLatin1("org.kde.kded5") ); QDBusConnection::sessionBus().registerObject( QString::fromLatin1("/modules/networkwatcher"), this ); } // TODO: instead use networkuri and return QVariant for all these NetDevice NetworkWatcher::deviceData( const QString& hostAddress ) { NetDevice result; const QList deviceList = mNetwork->deviceList(); - foreach( const NetDevice& device, deviceList ) - { + for (const NetDevice& device : deviceList) { if( device.hostAddress() == hostAddress ) { result = device; break; } } return result; } NetService NetworkWatcher::serviceData( const QString& hostAddress, const QString& serviceName, const QString& serviceType ) { NetService result; const QList deviceList = mNetwork->deviceList(); - foreach( const NetDevice& device, deviceList ) - { + for (const NetDevice& device : deviceList) { if( device.hostAddress() == hostAddress ) { const QList serviceList = device.serviceList(); - foreach( const NetService& service, serviceList ) - { + for (const NetService& service : serviceList) { if( service.name() == serviceName && service.type() == serviceType ) { result = service; break; } } break; } } return result; } NetDeviceList NetworkWatcher::deviceDataList() { return mNetwork->deviceList(); } NetServiceList NetworkWatcher::serviceDataList( const QString& hostAddress ) { NetServiceList result; const QList deviceList = mNetwork->deviceList(); - foreach( const NetDevice& device, deviceList ) - { + for (const NetDevice& device : deviceList) { if( device.hostAddress() == hostAddress ) { result = device.serviceList(); break; } } return result; } NetworkWatcher::~NetworkWatcher() { } } #include "networkwatcher.moc" diff --git a/network/network/builder/dnssd/dnssdnetworkbuilder.cpp b/network/network/builder/dnssd/dnssdnetworkbuilder.cpp index 5958f507..b74bb4e8 100644 --- a/network/network/builder/dnssd/dnssdnetworkbuilder.cpp +++ b/network/network/builder/dnssd/dnssdnetworkbuilder.cpp @@ -1,321 +1,316 @@ /* This file is part of the Mollet network library, part of the KDE project. Copyright 2009-2010 Friedrich W. H. Kossebau 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) version 3, or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 6 of version 3 of the license. 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, see . */ #include "dnssdnetworkbuilder.h" // lib #include "dnssdnetsystemable.h" #include "abstractnetsystemfactory.h" #include "network_p.h" #include "netdevice_p.h" // KDE #include #include // Qt #include #include #include namespace Mollet { DNSSDNetworkBuilder::DNSSDNetworkBuilder( NetworkPrivate* networkPrivate ) : AbstractNetworkBuilder() , mNetworkPrivate( networkPrivate ) , mServiceTypeBrowser( nullptr ) { } void DNSSDNetworkBuilder::registerNetSystemFactory( AbstractNetSystemFactory* netSystemFactory ) { DNSSDNetSystemAble* dnssdNetSystemAble = qobject_cast( netSystemFactory ); if( dnssdNetSystemAble ) mNetSystemFactoryList.append( dnssdNetSystemAble ); } void DNSSDNetworkBuilder::start() { mIsInit = true; mNoOfInitServiceTypes = 0; mServiceTypeBrowser = new KDNSSD::ServiceTypeBrowser(); connect(mServiceTypeBrowser, &KDNSSD::ServiceTypeBrowser::serviceTypeAdded, this, &DNSSDNetworkBuilder::addServiceType); connect(mServiceTypeBrowser, &KDNSSD::ServiceTypeBrowser::serviceTypeRemoved, this, &DNSSDNetworkBuilder::removeServiceType); connect(mServiceTypeBrowser, &KDNSSD::ServiceTypeBrowser::finished, this, &DNSSDNetworkBuilder::onServiceTypeBrowserFinished); // TODO: add a signal network initialized to Network, so is cleared when first usable mServiceTypeBrowser->startBrowse(); } void DNSSDNetworkBuilder::addServiceType( const QString& serviceType ) { //qDebug()<startBrowse(); } void DNSSDNetworkBuilder::removeServiceType( const QString& serviceType ) { //qDebug()<::Iterator it = mServiceBrowserTable.find( serviceType ); if( it == mServiceBrowserTable.end() ) return; KDNSSD::ServiceBrowser* serviceBrowser = *it; mServiceBrowserTable.erase( it ); // TODO: will all servicesRemoved be called before? on test NO! serviceBrowser->deleteLater(); #endif } void DNSSDNetworkBuilder::addService( KDNSSD::RemoteService::Ptr service ) { QList& deviceList = mNetworkPrivate->deviceList(); QString hostName = service->hostName(); // TODO: this blocks. and the ip address should be delivered from DNS-SD with resolve const QHostAddress hostAddress = KDNSSD::ServiceBrowser::resolveHostName( hostName ); const QString ipAddress = hostAddress.toString(); // forget domain name if just ip address if( hostName == ipAddress ) hostName.clear(); // device TODO: only search for if we can create the service? NetDevicePrivate* d = nullptr; const NetDevice* deviceOfService = nullptr; - foreach( const NetDevice& device, deviceList ) - { + for (const NetDevice& device : qAsConst(deviceList)) { const QString deviceHostName = device.hostName(); const bool useIpAddress = ( deviceHostName.isEmpty() || hostName.isEmpty() ); const bool isSameAddress = useIpAddress ? ( device.ipAddress() == ipAddress ) : ( deviceHostName == hostName ); //qDebug()<<"existing device:"<serviceName() is fragile, but matches // current approach in removeService(...) QString id; const QString serviceType = service->type(); - foreach( const DNSSDNetSystemAble* factory, mNetSystemFactoryList ) - { + for (const DNSSDNetSystemAble* factory : qAsConst(mNetSystemFactoryList)) { if( factory->canCreateNetSystemFromDNSSD(serviceType) ) { id = factory->dnssdId( service ); break; } } if( d->hasService(id) ) return; deviceOfService = &device; break; } } if( !d ) { const QString deviceName = hostName.left( hostName.indexOf(QLatin1Char('.')) ); d = new NetDevicePrivate( deviceName ); d->setHostName( hostName ); d->setIpAddress( ipAddress ); NetDevice device( d ); deviceList.append( device ); deviceOfService = &deviceList.last(); QList newDevices; newDevices.append( device ); // TODO: the new service will be announced two times, once with the new device and once alone. // what to do about that? which order? okay? for now just do not attach services before. find usecases. mNetworkPrivate->emitDevicesAdded( newDevices ); //qDebug()<<"new device:"<type(); } else { if( d->hostName().isEmpty() && ! hostName.isEmpty() ) d->setHostName( hostName ); } const QString serviceType = service->type(); NetServicePrivate* netServicePrivate = nullptr; // do a priority based lookup who can build the object // TODO: priorisation - foreach( const DNSSDNetSystemAble* factory, mNetSystemFactoryList ) - { + for (const DNSSDNetSystemAble* factory : qAsConst(mNetSystemFactoryList)) { if( factory->canCreateNetSystemFromDNSSD(serviceType) ) { // TODO: here we should rather see if this service already exists netServicePrivate = factory->createNetService( service, *deviceOfService ); break; } } // TODO: create dummy service // if( ! netServicePrivate ) // netServicePrivate = new UnknownService; NetService netService( netServicePrivate ); d->addService( netService ); // try guessing the device type by the services on it // TODO: move into devicefactory NetDevice::Type deviceTypeByService = NetDevice::Unknown; QString deviceName; if( serviceType == QLatin1String("_workstation._tcp") ) { deviceTypeByService = NetDevice::Workstation; deviceName = service->serviceName().left( service->serviceName().lastIndexOf(QLatin1Char('[')) ).trimmed(); } else if( serviceType == QLatin1String("_net-assistant._udp") ) { deviceTypeByService = NetDevice::Workstation; deviceName = service->serviceName(); } else if( serviceType == QLatin1String("_airport._tcp") ) deviceTypeByService = NetDevice::Router; else if( serviceType == QLatin1String("_ipp._tcp") || serviceType == QLatin1String("_printer._tcp") || serviceType == QLatin1String("_pdl-datastream._tcp") ) { deviceTypeByService = NetDevice::Printer; deviceName = service->serviceName(); } if( deviceTypeByService != NetDevice::Unknown ) { if( deviceTypeByService > d->type() ) { d->setType( deviceTypeByService ); if( ! deviceName.isEmpty() ) d->setName( deviceName ); } } QList newServices; newServices.append( netService ); mNetworkPrivate->emitServicesAdded( newServices ); } // TODO: each builder should refcount its services, so a shared one does not get removed to early // (might disappear for one sd because of master breakdown) void DNSSDNetworkBuilder::removeService( KDNSSD::RemoteService::Ptr service ) { QList& deviceList = mNetworkPrivate->deviceList(); const QString hostName = service->hostName(); // device QMutableListIterator it( deviceList ); while( it.hasNext()) { const NetDevice& device = it.next(); if( device.hostName() == hostName ) { // //qDebug()<type(); - foreach( const DNSSDNetSystemAble* factory, mNetSystemFactoryList ) - { + for (const DNSSDNetSystemAble* factory : qAsConst(mNetSystemFactoryList)) { if( factory->canCreateNetSystemFromDNSSD(serviceType) ) { id = factory->dnssdId( service ); break; } } NetDevicePrivate* d = device.dPtr(); NetService netService = d->removeService( id ); if( !netService.isValid() ) break; QList removedServices; removedServices.append( netService ); mNetworkPrivate->emitServicesRemoved( removedServices ); // remove device on last service if( d->serviceList().count() == 0 ) { QList removedDevices; removedDevices.append( device ); // remove only after taking copy from reference into removed list it.remove(); mNetworkPrivate->emitDevicesRemoved( removedDevices ); } break; } } } void DNSSDNetworkBuilder::onServiceTypeBrowserFinished() { // //qDebug(); if( mIsInit ) { mIsInit = false; if( mNoOfInitServiceTypes == 0 ) emit initDone(); } } void DNSSDNetworkBuilder::onServiceBrowserFinished() { --mNoOfInitServiceTypes; // //qDebug()<<"mIsInit="< 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) version 3, or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 6 of version 3 of the license. 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, see . */ #include "upnpnetworkbuilder.h" // lib #include "upnpnetsystemable.h" #include "abstractnetsystemfactory.h" #include "network_p.h" #include "netdevice_p.h" #include "netservice_p.h" #include "cagibidevice.h" #include "cagibidbuscodec.h" // Qt #include #include #include #include #include #include #include #include namespace Mollet { static const char cagibiServiceName[] = "org.kde.Cagibi"; static const char cagibiDeviceListObjectPath[] = "/org/kde/Cagibi/DeviceList"; static const char cagibiDeviceListInterface[] = "org.kde.Cagibi.DeviceList"; UpnpNetworkBuilder::UpnpNetworkBuilder( NetworkPrivate* networkPrivate ) : AbstractNetworkBuilder() , mNetworkPrivate( networkPrivate ) , mCagibiDeviceListDBusProxy( nullptr ) { } void UpnpNetworkBuilder::registerNetSystemFactory( AbstractNetSystemFactory* netSystemFactory ) { UpnpNetSystemAble* upnpNetSystemAble = qobject_cast( netSystemFactory ); if( upnpNetSystemAble ) mNetSystemFactoryList.append( upnpNetSystemAble ); } void UpnpNetworkBuilder::start() { QMetaObject::invokeMethod( this, "startBrowse", Qt::QueuedConnection ); } void UpnpNetworkBuilder::startBrowse() { qDBusRegisterMetaType(); qDBusRegisterMetaType(); QDBusConnection dbusConnection = QDBusConnection::systemBus(); const QString serviceName = QLatin1String( cagibiServiceName ); const QString deviceListObjectPath = QLatin1String( cagibiDeviceListObjectPath ); const QString deviceListInterface = QLatin1String( cagibiDeviceListInterface ); // install service watcher QDBusServiceWatcher* cagibiServiceWatcher = new QDBusServiceWatcher( serviceName, dbusConnection, QDBusServiceWatcher::WatchForOwnerChange, this ); connect( cagibiServiceWatcher, SIGNAL(serviceOwnerChanged(QString,QString,QString)), SLOT(onCagibiServiceOwnerChanged(QString,QString,QString)) ); // create devicelist proxy mCagibiDeviceListDBusProxy = new QDBusInterface( serviceName, deviceListObjectPath, deviceListInterface, dbusConnection, this ); connect( mCagibiDeviceListDBusProxy, SIGNAL(devicesAdded(DeviceTypeMap)), SLOT(onDevicesAdded(DeviceTypeMap)) ); connect( mCagibiDeviceListDBusProxy, SIGNAL(devicesRemoved(DeviceTypeMap)), SLOT(onDevicesRemoved(DeviceTypeMap)) ); // query current devicelist queryCurrentDevices(); emit initDone(); } void UpnpNetworkBuilder::queryCurrentDevices() { // query current devicelist QDBusPendingCall allDevicesCall = mCagibiDeviceListDBusProxy->asyncCall( QLatin1String("allDevices") ); QDBusPendingCallWatcher* allDevicesCallWatcher = new QDBusPendingCallWatcher( allDevicesCall, this ); connect( allDevicesCallWatcher, SIGNAL(finished(QDBusPendingCallWatcher*)), SLOT(onAllDevicesCallFinished(QDBusPendingCallWatcher*)) ); } void UpnpNetworkBuilder::onAllDevicesCallFinished( QDBusPendingCallWatcher* allDevicesCallWatcher ) { QDBusReply reply = *allDevicesCallWatcher; if( reply.isValid() ) { //qDebug() << "Connected to Cagibi, listing of UPnP devices/services started."; const DeviceTypeMap deviceTypeMap = reply; onDevicesAdded( deviceTypeMap ); } else { //qDebug() << "Could not connect to Cagibi, no listing of UPnP devices/services."; //qDebug() << "Error: " << reply.error().name(); } delete allDevicesCallWatcher; } void UpnpNetworkBuilder::addUPnPDevices( const QList& upnpDevices ) { QList addedDevices; QList addedServices; QList& deviceList = mNetworkPrivate->deviceList(); - foreach( const Cagibi::Device& upnpDevice, upnpDevices ) - { + for (const Cagibi::Device& upnpDevice : upnpDevices) { if( upnpDevice.hasParentDevice() ) continue; const QString ipAddress = upnpDevice.ipAddress(); NetDevicePrivate* d = nullptr; const NetDevice* deviceOfService = nullptr; - foreach( const NetDevice& device, deviceList ) - { + for (const NetDevice& device : qAsConst(deviceList)) { const bool isSameAddress = ( device.ipAddress() == ipAddress ); //qDebug()<<"existing device:"<setIpAddress( ipAddress ); NetDevice device( d ); addedDevices.append( device ); deviceList.append( device ); deviceOfService = &deviceList.last(); //qDebug()<<"new device:"<canCreateNetSystemFromUpnp(upnpDevice) ) { // TODO: here we should rather see if this service already exists netServicePrivate = factory->createNetService( upnpDevice, *deviceOfService ); break; } } NetService netService( netServicePrivate ); d->addService( netService ); addedServices.append( netService ); //qDebug()<<"new service:"< d->type() ) { d->setType( deviceTypeByService ); if( ! deviceName.isEmpty() ) d->setName( deviceName ); } } } if( ! addedDevices.isEmpty() ) mNetworkPrivate->emitDevicesAdded( addedDevices ); if( ! addedServices.isEmpty() ) mNetworkPrivate->emitServicesAdded( addedServices ); } void UpnpNetworkBuilder::removeUPnPDevices( const QList& upnpDevices ) { QList removedDevices; QList removedServices; QList& deviceList = mNetworkPrivate->deviceList(); - foreach( const Cagibi::Device& upnpDevice, upnpDevices ) - { + for (const Cagibi::Device& upnpDevice : upnpDevices) { const QString ipAddress = upnpDevice.ipAddress(); QMutableListIterator it( deviceList ); while( it.hasNext()) { const NetDevice& device = it.next(); if( device.ipAddress() == ipAddress ) { QString id; - foreach( const UpnpNetSystemAble* factory, mNetSystemFactoryList ) - { + for (const UpnpNetSystemAble* factory : mNetSystemFactoryList) { if( factory->canCreateNetSystemFromUpnp(upnpDevice) ) { id = factory->upnpId( upnpDevice ); break; } } NetDevicePrivate* d = device.dPtr(); NetService netService = d->removeService( id ); if( ! netService.isValid() ) break; removedServices.append( netService ); // remove device on last service if( d->serviceList().count() == 0 ) { removedDevices.append( device ); // remove only after taking copy from reference into removed list it.remove(); } break; } } } if( ! removedServices.isEmpty() ) mNetworkPrivate->emitServicesRemoved( removedServices ); if( ! removedDevices.isEmpty() ) mNetworkPrivate->emitDevicesRemoved( removedDevices ); } void UpnpNetworkBuilder::onDevicesAdded( const DeviceTypeMap& deviceTypeMap ) { DeviceTypeMap::ConstIterator it = deviceTypeMap.constBegin(); DeviceTypeMap::ConstIterator end = deviceTypeMap.constEnd(); for( ; it != end; ++it ) { const QString udn = it.key(); QList args; args << udn; mCagibiDeviceListDBusProxy->callWithCallback( QLatin1String("deviceDetails"), args, this, SLOT(onAddedDeviceDetails(Cagibi::Device)), nullptr ); } } void UpnpNetworkBuilder::onDevicesRemoved( const DeviceTypeMap& deviceTypeMap ) { QList upnpDevices; DeviceTypeMap::ConstIterator it = deviceTypeMap.constBegin(); DeviceTypeMap::ConstIterator end = deviceTypeMap.constEnd(); for( ; it != end; ++it ) { QHash::Iterator adIt = mActiveDevices.find( it.key() ); if( adIt != mActiveDevices.end() ) { //qDebug()<<"removing UPnP device" << adIt.value().friendlyName(); upnpDevices.append( adIt.value() ); mActiveDevices.erase( adIt ); } } removeUPnPDevices( upnpDevices ); } void UpnpNetworkBuilder::onAddedDeviceDetails( const Cagibi::Device& device ) { // currently only root devices are shown if( device.hasParentDevice() ) return; mActiveDevices.insert( device.udn(), device ); QList devices; devices.append( device ); addUPnPDevices( devices ); } void UpnpNetworkBuilder::onCagibiServiceOwnerChanged( const QString& serviceName, const QString& oldOwner, const QString& newOwner ) { Q_UNUSED(serviceName); Q_UNUSED(newOwner); // old service disappeared? if( ! oldOwner.isEmpty() ) { //qDebug()<<"Cagibi disappeared, removing all UPnP devices"; // remove all registered UPnP devices QList upnpDevices = mActiveDevices.values(); mActiveDevices.clear(); removeUPnPDevices( upnpDevices ); } if( ! newOwner.isEmpty() ) queryCurrentDevices(); } UpnpNetworkBuilder::~UpnpNetworkBuilder() { } } diff --git a/network/network/netdevice_p.cpp b/network/network/netdevice_p.cpp index 7a81e6d5..0bf3123d 100644 --- a/network/network/netdevice_p.cpp +++ b/network/network/netdevice_p.cpp @@ -1,79 +1,78 @@ /* This file is part of the Mollet network library, part of the KDE project. Copyright 2009 Friedrich W. H. Kossebau 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) version 3, or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 6 of version 3 of the license. 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, see . */ #include "netdevice_p.h" // library #include "netservice_p.h" // Qt #include namespace Mollet { NetDevicePrivate::NetDevicePrivate( const QString& name ) : mName( name ) , mType( NetDevice::Unknown ) { } bool NetDevicePrivate::hasService( const QString& id ) const { bool result = false; - foreach( const NetService& service, mServiceList ) - { + for (const NetService& service : mServiceList) { const NetServicePrivate* const d = service.dPtr(); if( d->id() == id ) { result = true; break; } } return result; } NetService NetDevicePrivate::removeService( const QString& id ) { NetService result; QMutableListIterator it( mServiceList ); while( it.hasNext()) { const NetService& service = it.next(); const NetServicePrivate* const d = service.dPtr(); if( d->id() == id ) { result = service; it.remove(); break; } } return result; } NetDevicePrivate::~NetDevicePrivate() { } } diff --git a/network/network/network_p.cpp b/network/network/network_p.cpp index e4ef06a7..6cc93761 100644 --- a/network/network/network_p.cpp +++ b/network/network/network_p.cpp @@ -1,74 +1,74 @@ /* This file is part of the Mollet network library, part of the KDE project. Copyright 2009 Friedrich W. H. Kossebau 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) version 3, or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 6 of version 3 of the license. 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, see . */ #include "network_p.h" // lib #include "builder/dnssd/dnssdnetworkbuilder.h" #include "builder/upnp/upnpnetworkbuilder.h" #include "builder/simpleitemfactory.h" #include namespace Mollet { NetworkPrivate::NetworkPrivate( Network* parent ) : p( parent ) { } void NetworkPrivate::init() { SimpleItemFactory* simpleItemFactory = new SimpleItemFactory(); mNetSystemFactoryList.append( simpleItemFactory ); DNSSDNetworkBuilder* dnssdBuilder = new DNSSDNetworkBuilder( this ); UpnpNetworkBuilder* upnpBuilder = new UpnpNetworkBuilder( this ); mNetworkBuilderList.append( dnssdBuilder ); mNetworkBuilderList.append( upnpBuilder ); mNoOfInitBuilders = mNetworkBuilderList.count(); - foreach( AbstractNetworkBuilder* builder, mNetworkBuilderList ) - { - foreach( AbstractNetSystemFactory* factory, mNetSystemFactoryList ) + for (AbstractNetworkBuilder* builder : qAsConst(mNetworkBuilderList)) { + for (AbstractNetSystemFactory* factory : qAsConst(mNetSystemFactoryList)) { builder->registerNetSystemFactory( factory ); + } p->connect( builder, SIGNAL(initDone()), SLOT(onBuilderInit()) ); builder->start(); } } void NetworkPrivate::onBuilderInit() { --mNoOfInitBuilders; //qDebug()<initDone(); } NetworkPrivate::~NetworkPrivate() { qDeleteAll( mNetworkBuilderList ); qDeleteAll( mNetSystemFactoryList ); } } diff --git a/recentdocuments/recentdocuments.cpp b/recentdocuments/recentdocuments.cpp index 6348c847..8371aab0 100644 --- a/recentdocuments/recentdocuments.cpp +++ b/recentdocuments/recentdocuments.cpp @@ -1,186 +1,186 @@ #include "recentdocuments.h" #include #include #include #include #include #include #include #include #include #include #include #include extern "C" int Q_DECL_EXPORT kdemain(int argc, char **argv) { // necessary to use other kio slaves QCoreApplication app(argc, argv); app.setApplicationName("kio_recentdocuments"); if (argc != 4) { fprintf(stderr, "Usage: kio_recentdocuments protocol domain-socket1 domain-socket2\n"); exit(-1); } // start the slave RecentDocuments slave(argv[2], argv[3]); slave.dispatchLoop(); return 0; } bool isRootUrl(const QUrl &url) { const QString path = url.adjusted(QUrl::StripTrailingSlash).path(); return(!url.hasQuery() && (path.isEmpty() || path == QLatin1String("/"))); } RecentDocuments::RecentDocuments(const QByteArray& pool, const QByteArray& app): ForwardingSlaveBase("recentdocuments", pool, app) { QDBusInterface kded("org.kde.kded5", "/kded", "org.kde.kded5"); kded.call("loadModule", "recentdocumentsnotifier"); } RecentDocuments::~RecentDocuments() { } bool RecentDocuments::rewriteUrl(const QUrl& url, QUrl& newUrl) { if (isRootUrl(url)) { return false; } else { QString desktopFilePath = QString("%1/%2.desktop").arg(KRecentDocument::recentDocumentDirectory()).arg(url.path()); if (KDesktopFile::isDesktopFile(desktopFilePath)) { KDesktopFile file(desktopFilePath); if (file.hasLinkType()) newUrl = QUrl(file.readUrl()); } return !newUrl.isEmpty(); } } void RecentDocuments::listDir(const QUrl& url) { if (isRootUrl(url)) { - QStringList list = KRecentDocument::recentDocuments(); + const QStringList list = KRecentDocument::recentDocuments(); KIO::UDSEntryList udslist; QSet urlSet; - Q_FOREACH(const QString & entry, list) { + for (const QString& entry : list) { if (KDesktopFile::isDesktopFile(entry)) { QFileInfo info(entry); KDesktopFile file(entry); QUrl urlInside(file.readUrl()); QString toDisplayString = urlInside.toDisplayString(); // Filter out things that can't be viewed in a file manager because they don't // meet the user definition of a file for the purpose of "recently accessed files" if (urlInside.scheme() == "recentdocuments" || !KProtocolManager::supportsListing(urlInside) || urlSet.contains(toDisplayString)) continue; KIO::UDSEntry uds; if (urlInside.isLocalFile()) { KIO::StatJob* job = KIO::stat(urlInside, KIO::HideProgressInfo); // we do not want to wait for the event loop to delete the job QScopedPointer sp(job); job->setAutoDelete(false); if (job->exec()) { uds = job->statResult(); } } urlSet.insert(toDisplayString); uds.replace(KIO::UDSEntry::UDS_NAME, info.completeBaseName()); if (urlInside.isLocalFile()) { uds.replace(KIO::UDSEntry::UDS_DISPLAY_NAME, urlInside.toLocalFile()); uds.replace(KIO::UDSEntry::UDS_LOCAL_PATH, urlInside.path()); } else { uds.fastInsert(KIO::UDSEntry::UDS_DISPLAY_NAME, toDisplayString); uds.fastInsert(KIO::UDSEntry::UDS_ICON_NAME, file.readIcon()); } uds.replace(KIO::UDSEntry::UDS_TARGET_URL, toDisplayString); udslist << uds; } } listEntries(udslist); finished(); } else error(KIO::ERR_DOES_NOT_EXIST, url.toDisplayString()); } void RecentDocuments::prepareUDSEntry(KIO::UDSEntry& entry, bool listing) const { ForwardingSlaveBase::prepareUDSEntry(entry, listing); } QString RecentDocuments::desktopFile(KIO::UDSEntry& entry) const { const QString name = entry.stringValue(KIO::UDSEntry::UDS_NAME); if (name == "." || name == "..") return QString(); QUrl url = processedUrl(); url = url.adjusted(QUrl::StripTrailingSlash); url.setPath(url.path() + '/' + name); if (KDesktopFile::isDesktopFile(url.toLocalFile())) return url.toLocalFile(); return QString(); } void RecentDocuments::stat(const QUrl& url) { if (isRootUrl(url)) { qDebug() << "Stat root" << url; // // stat the root path // QString dirName = i18n("Recent Documents"); KIO::UDSEntry uds; uds.fastInsert(KIO::UDSEntry::UDS_NAME, dirName); uds.fastInsert(KIO::UDSEntry::UDS_DISPLAY_NAME, dirName); uds.fastInsert(KIO::UDSEntry::UDS_DISPLAY_TYPE, dirName); uds.fastInsert(KIO::UDSEntry::UDS_ICON_NAME, QString::fromLatin1("document-open-recent")); uds.fastInsert(KIO::UDSEntry::UDS_FILE_TYPE, S_IFDIR); uds.fastInsert(KIO::UDSEntry::UDS_MIME_TYPE, QString::fromLatin1("inode/directory")); statEntry(uds); finished(); } // results are forwarded else { qDebug() << "Stat forward" << url; ForwardingSlaveBase::stat(url); } } void RecentDocuments::mimetype(const QUrl& url) { qDebug() << url; // the root url is always a folder if (isRootUrl(url)) { mimeType(QString::fromLatin1("inode/directory")); finished(); } // results are forwarded else { ForwardingSlaveBase::mimetype(url); } } void RecentDocuments::del(const QUrl& url, bool isFile) { ForwardingSlaveBase::del(url, isFile); } diff --git a/thumbnail/comiccreator.cpp b/thumbnail/comiccreator.cpp index a353d33f..973ecb08 100644 --- a/thumbnail/comiccreator.cpp +++ b/thumbnail/comiccreator.cpp @@ -1,314 +1,315 @@ /** * This file is part of the KDE libraries * * Comic Book Thumbnailer for KDE 4 v0.1 * Creates cover page previews for comic-book files (.cbr/z/t). * Copyright (c) 2009 Harsh J * * Some code borrowed from Okular's comicbook generators, * by Tobias Koenig * * 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) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * 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 . */ // comiccreator.cpp #include "comiccreator.h" #include #include #include #include #include #include #include #include #include #include #include // For KIO-Thumbnail debug outputs // TODO KF5 qCDebug #define KIO_THUMB 11371 extern "C" { Q_DECL_EXPORT ThumbCreator *new_creator() { return new ComicCreator; } } ComicCreator::ComicCreator() : m_loop(nullptr) {} bool ComicCreator::create(const QString& path, int width, int height, QImage& img) { Q_UNUSED(width); Q_UNUSED(height); QImage cover; // Detect mime type. QMimeDatabase db; db.mimeTypeForFile(path, QMimeDatabase::MatchContent); const QMimeType mime = db.mimeTypeForFile(path, QMimeDatabase::MatchContent); if (mime.inherits("application/x-cbz") || mime.inherits("application/zip")) { // ZIP archive. cover = extractArchiveImage(path, ZIP); } else if (mime.inherits("application/x-cbt") || mime.inherits("application/x-gzip") || mime.inherits("application/x-tar")) { // TAR archive cover = extractArchiveImage(path, TAR); } else if (mime.inherits("application/x-cbr") || mime.inherits("application/x-rar")) { // RAR archive. cover = extractRARImage(path); } if (cover.isNull()) { qDebug()<<"Error creating the comic book thumbnail."; return false; } // Copy the extracted cover to KIO::ThumbCreator's img reference. img = cover; return true; } void ComicCreator::filterImages(QStringList& entries) { /// Sort case-insensitive, then remove non-image entries. QMap entryMap; - Q_FOREACH(const QString& entry, entries) { + for (const QString& entry : qAsConst(entries)) { if (entry.endsWith(QLatin1String(".gif"), Qt::CaseInsensitive) || entry.endsWith(QLatin1String(".jpg"), Qt::CaseInsensitive) || entry.endsWith(QLatin1String(".jpeg"), Qt::CaseInsensitive) || entry.endsWith(QLatin1String(".png"), Qt::CaseInsensitive)) { entryMap.insert(entry.toLower(), entry); } } entries = entryMap.values(); } QImage ComicCreator::extractArchiveImage(const QString& path, const ComicCreator::Type type) { /// Extracts the cover image out of the .cbz or .cbt file. QScopedPointer cArchive; if (type==ZIP) { // Open the ZIP archive. cArchive.reset(new KZip(path)); } else if (type==TAR) { // Open the TAR archive. cArchive.reset(new KTar(path)); } else { // Reject all other types for this method. return QImage(); } // Can our archive be opened? if (!cArchive->open(QIODevice::ReadOnly)) { return QImage(); } // Get the archive's directory. const KArchiveDirectory* cArchiveDir = nullptr; cArchiveDir = cArchive->directory(); if (!cArchiveDir) { return QImage(); } QStringList entries; // Get and filter the entries from the archive. getArchiveFileList(entries, QString(), cArchiveDir); filterImages(entries); if (entries.isEmpty()) { return QImage(); } // Extract the cover file. const KArchiveFile *coverFile = static_cast (cArchiveDir->entry(entries[0])); if (!coverFile) { return QImage(); } return QImage::fromData(coverFile->data()); } void ComicCreator::getArchiveFileList(QStringList& entries, const QString& prefix, const KArchiveDirectory *dir) { /// Recursively list all files in the ZIP archive into 'entries'. - Q_FOREACH (const QString& entry, dir->entries()) { + const auto dirEntries = dir->entries(); + for (const QString& entry : dirEntries) { const KArchiveEntry *e = dir->entry(entry); if (e->isDirectory()) { getArchiveFileList(entries, prefix + entry + '/', static_cast(e)); } else if (e->isFile()) { entries.append(prefix + entry); } } } QImage ComicCreator::extractRARImage(const QString& path) { /// Extracts the cover image out of the .cbr file. // Check if unrar is available. Get its path in 'unrarPath'. QString unrar = unrarPath(); if (unrar.isEmpty()) { qDebug()<<"A suitable version of unrar is not available."; return QImage(); } // Get the files and filter the images out. QStringList entries = getRARFileList(path, unrar); filterImages(entries); if (entries.isEmpty()) { return QImage(); } // Clear previously used data arrays. m_stdOut.clear(); m_stdErr.clear(); // Extract the cover file alone. Use verbose paths. // unrar x -n path/to/archive /path/to/temp QTemporaryDir cUnrarTempDir; startProcess(unrar, QStringList() << "x" << "-n" + entries[0] << path << cUnrarTempDir.path()); // Load cover file data into image. QImage cover; cover.load(cUnrarTempDir.path() + QDir::separator() + entries[0]); return cover; } QStringList ComicCreator::getRARFileList(const QString& path, const QString& unrarPath) { /// Get a verbose unrar listing so we can extract a single file later. // CMD: unrar vb /path/to/archive QStringList entries; startProcess(unrarPath, QStringList() << "vb" << path); entries = QString::fromLocal8Bit(m_stdOut).split('\n', QString::SkipEmptyParts); return entries; } QString ComicCreator::unrarPath() const { /// Check the standard paths to see if a suitable unrar is available. QString unrar = QStandardPaths::findExecutable("unrar"); if (unrar.isEmpty()) { unrar = QStandardPaths::findExecutable("unrar-nonfree"); } if (unrar.isEmpty()) { unrar = QStandardPaths::findExecutable("rar"); } if (!unrar.isEmpty()) { QProcess proc; proc.start(unrar, QStringList() << "-version"); proc.waitForFinished(-1); const QStringList lines = QString::fromLocal8Bit(proc.readAllStandardOutput()).split ('\n', QString::SkipEmptyParts); if (!lines.isEmpty()) { if (lines.first().startsWith(QLatin1String("RAR ")) || lines.first().startsWith(QLatin1String("UNRAR "))) { return unrar; } } } return QString(); } void ComicCreator::readProcessOut() { /// Read all std::out data and store to the data array. if (!m_process) return; m_stdOut += m_process->readAllStandardOutput(); } void ComicCreator::readProcessErr() { /// Read available std:err data and kill process if there is any. if (!m_process) return; m_stdErr += m_process->readAllStandardError(); if (!m_stdErr.isEmpty()) { m_process->kill(); return; } } void ComicCreator::finishedProcess(int exitCode, QProcess::ExitStatus exitStatus) { /// Run when process finishes. Q_UNUSED(exitCode) if (m_loop) { m_loop->exit(exitStatus == QProcess::CrashExit ? 1 : 0); } } int ComicCreator::startProcess(const QString& processPath, const QStringList& args) { /// Run a process and store std::out, std::err data in their respective buffers. int ret = 0; #if defined(Q_OS_WIN) m_process.reset(new QProcess(this)); #else m_process.reset(new KPtyProcess(this)); m_process->setOutputChannelMode(KProcess::SeparateChannels); #endif connect(m_process.data(), SIGNAL(readyReadStandardOutput()), SLOT(readProcessOut())); connect(m_process.data(), SIGNAL(readyReadStandardError()), SLOT(readProcessErr())); connect(m_process.data(), SIGNAL(finished(int,QProcess::ExitStatus)), SLOT(finishedProcess(int,QProcess::ExitStatus))); #if defined(Q_OS_WIN) m_process->start(processPath, args, QIODevice::ReadWrite | QIODevice::Unbuffered); ret = m_process->waitForFinished(-1) ? 0 : 1; #else m_process->setProgram(processPath, args); m_process->setNextOpenMode(QIODevice::ReadWrite | QIODevice::Unbuffered); m_process->start(); QEventLoop loop; m_loop = &loop; ret = loop.exec(QEventLoop::WaitForMoreEvents); m_loop = nullptr; #endif return ret; } ThumbCreator::Flags ComicCreator::flags() const { return None; } diff --git a/thumbnail/icoutils_win.cpp b/thumbnail/icoutils_win.cpp index be9a8607..89520327 100644 --- a/thumbnail/icoutils_win.cpp +++ b/thumbnail/icoutils_win.cpp @@ -1,194 +1,193 @@ /* icoutils_win.cpp - Extract Microsoft Window icons and images using icoutils package Copyright (c) 2013 by Andrius da Costa Ribas ************************************************************************* * * * This library 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. * * * ************************************************************************* */ #include "icoutils.h" #include #include #include #include #include extern "C" { // icon structs, as per http://msdn.microsoft.com/en-us/library/ms997538.aspx #pragma pack( push ) #pragma pack( 2 ) typedef struct { BYTE bWidth; // Width, in pixels, of the image BYTE bHeight; // Height, in pixels, of the image BYTE bColorCount; // Number of colors in image (0 if >=8bpp) BYTE bReserved; // Reserved ( must be 0) WORD wPlanes; // Color Planes WORD wBitCount; // Bits per pixel DWORD dwBytesInRes; // How many bytes in this resource? DWORD dwImageOffset; // Where in the file is this image? } ICONDIRENTRY, *LPICONDIRENTRY; typedef struct { WORD idReserved; // Reserved (must be 0) WORD idType; // Resource Type (1 for icons) WORD idCount; // How many images? ICONDIRENTRY idEntries[1]; // An entry for each image (idCount of 'em) } ICONDIR, *LPICONDIR; typedef struct { BYTE bWidth; // Width, in pixels, of the image BYTE bHeight; // Height, in pixels, of the image BYTE bColorCount; // Number of colors in image (0 if >=8bpp) BYTE bReserved; // Reserved WORD wPlanes; // Color Planes WORD wBitCount; // Bits per pixel DWORD dwBytesInRes; // how many bytes in this resource? WORD nID; // the ID } GRPICONDIRENTRY, *LPGRPICONDIRENTRY; typedef struct { WORD idReserved; // Reserved (must be 0) WORD idType; // Resource type (1 for icons) WORD idCount; // How many images? GRPICONDIRENTRY idEntries[1]; // The entries for each image } GRPICONDIR, *LPGRPICONDIR; #pragma pack( pop ) } BOOL CALLBACK enumResNameCallback( HMODULE hModule, LPCTSTR lpszType, LPTSTR lpszName, LONG_PTR lParam ) { QList* iconResources = (QList*) lParam; LPTSTR copyString; if ( IS_INTRESOURCE(lpszName) ) { copyString = lpszName; } else { copyString = new WCHAR[lstrlen(lpszName) + 1]; lstrcpy(copyString, lpszName); } if ( lpszType == RT_GROUP_ICON ) { (*iconResources) << copyString; } return TRUE; } bool IcoUtils::loadIcoImageFromExe(const QString &inputFileName, QIODevice *outputDevice, const qint32 iconNumber) { HMODULE hModule; LPCTSTR fileName; QList iconResources; fileName = (TCHAR*) QDir::toNativeSeparators( inputFileName ).utf16(); hModule = LoadLibraryEx ( fileName, 0, LOAD_LIBRARY_AS_DATAFILE ); if ( !hModule ) { return false; } EnumResourceNames ( hModule, RT_GROUP_ICON, enumResNameCallback, (LONG_PTR) &iconResources ); if ( iconNumber < iconResources.size() ) { HRSRC resourceInfo = FindResourceW ( hModule, (LPCTSTR) iconResources.at(iconNumber), RT_GROUP_ICON ); if ( resourceInfo == 0 ) { FreeLibrary( hModule ); return false; } // we can get rid of the iconResources list now - foreach ( LPTSTR iconRes, iconResources ) - { + for (LPTSTR iconRes : qAsConst(iconResources)) { if ( !IS_INTRESOURCE(iconRes) ) { delete [] iconRes; } } HGLOBAL resourceData = LoadResource ( hModule, resourceInfo ); LPVOID resourcePointer = LockResource ( resourceData ); int resourceSize = SizeofResource( hModule, resourceInfo ); if ( resourceSize == 0 ) { FreeLibrary( hModule ); return false; } LPGRPICONDIR grpIconDir = (LPGRPICONDIR) resourcePointer; QBuffer outBuffer; outBuffer.open(QIODevice::ReadWrite); // helper const int iconDirHeaderSize = sizeof(grpIconDir->idReserved) + sizeof(grpIconDir->idType) + sizeof(grpIconDir->idCount); // copy the common part of GRPICONDIR and ICONDIR structures to the file outBuffer.write( (char*) resourcePointer, iconDirHeaderSize ); DWORD imageOffset = iconDirHeaderSize + (grpIconDir->idCount) * sizeof(ICONDIRENTRY) ; for ( int i = 0 ; i < (grpIconDir->idCount) ; ++i ) { // copy the common part of GRPICONDIRENTRY and ICONDIRENTRY structures to the respective ICONDIRENTRY's position LPGRPICONDIRENTRY grpIconDirEntry = &(grpIconDir->idEntries[i]); outBuffer.seek( iconDirHeaderSize + i * sizeof(ICONDIRENTRY) ); outBuffer.write( (char*) grpIconDirEntry, sizeof(GRPICONDIRENTRY) - sizeof(grpIconDirEntry->nID) ); // now, instead of nID, write the image offset outBuffer.write( (char*) &imageOffset, sizeof(DWORD) ); // find the icon resource resourceInfo = FindResourceW ( hModule, MAKEINTRESOURCE(grpIconDirEntry->nID), RT_ICON ); if ( resourceInfo == 0 ) { FreeLibrary( hModule ); return false; } resourceData = LoadResource ( hModule, resourceInfo ); resourcePointer = LockResource ( resourceData ); resourceSize = SizeofResource( hModule, resourceInfo ); if ( resourceSize == 0 ) { FreeLibrary( hModule ); return false; } // seek to imageOffset outBuffer.seek(imageOffset); // write the icon data outBuffer.write( (char*) resourcePointer, resourceSize ); // increment imageOffset imageOffset += resourceSize; } const bool ok = (outputDevice->write(outBuffer.data()) == outBuffer.size()); FreeLibrary( hModule ); return ok; } FreeLibrary( hModule ); return false; } diff --git a/thumbnail/icoutils_wrestool.cpp b/thumbnail/icoutils_wrestool.cpp index 8d8d2d66..da58eeb6 100644 --- a/thumbnail/icoutils_wrestool.cpp +++ b/thumbnail/icoutils_wrestool.cpp @@ -1,97 +1,98 @@ /* icoutils_wrestool.cpp - Extract Microsoft Window icons and images using icoutils package Copyright (c) 2009-2010 by Pali Rohár ************************************************************************* * * * This library 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. * * * ************************************************************************* */ #include "icoutils.h" #include #include #include #include #include #include #include #include #define abs(n) ( ( n < 0 ) ? -n : n ) typedef QPair < QString, int > IconInExe; bool IcoUtils::loadIcoImageFromExe(const QString &inputFileName, QIODevice *outputDevice, const qint32 iconNumber) { QProcess wrestool; wrestool.start("wrestool", QStringList() << "-l" << inputFileName); wrestool.waitForFinished(); if ( wrestool.exitCode() != 0 ) return false; const QStringList output = QString(wrestool.readAll()).split('\n'); QRegExp regExp("--type=(.*) --name=(.*) --language=(.*) \\[(.*)\\]"); // If we specify number of icon, use only group icons (Windows use only group icons) if ( iconNumber > 0 ) regExp.setPattern("--type=(14) --name=(.*) --language=(.*) \\[(.*)\\]"); QList icons; // First try use group icons (type 14, default first for windows executables), then icons (type 3), then group cursors (type 12) and finally cursors (type 1) // Note: Last icon (type 3) could be in higher resolution // Group Icons - foreach ( const QString &line, output ) + for (const QString &line : output) { if ( regExp.indexIn(line) != -1 && regExp.cap(1).toInt() == 14 ) icons << qMakePair(regExp.cap(2), 14); + } // Icons - foreach ( const QString &line, output ) + for (const QString &line : output) { if ( regExp.indexIn(line) != -1 && regExp.cap(1).toInt() == 3 ) icons << qMakePair(regExp.cap(2), 3); + } if ( icons.isEmpty() ) return false; if ( iconNumber > 0 && icons.size() >= iconNumber ) icons = QList () << icons.at(iconNumber+1); - foreach ( const IconInExe &icon, icons ) - { + for (const IconInExe &icon : qAsConst(icons)) { QString name = icon.first; int type = icon.second; if ( name.at(0) == '\'' ) name = name.mid(1, name.size()-2); wrestool.start("wrestool", QStringList() << "-x" << "-t" << QString::number(type) << "-n" << name << inputFileName); wrestool.waitForFinished(); if (wrestool.exitCode() != 0) { return false; } const QByteArray iconData = wrestool.readAllStandardOutput(); if (outputDevice->write(iconData) != iconData.size()) { return false; } return true; } return false; } diff --git a/thumbnail/thumbnail.cpp b/thumbnail/thumbnail.cpp index c4db1d1f..31c44286 100644 --- a/thumbnail/thumbnail.cpp +++ b/thumbnail/thumbnail.cpp @@ -1,776 +1,776 @@ /* This file is part of the KDE libraries Copyright (C) 2000 Malte Starostik 2000 Carsten Pfeiffer This library 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 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "thumbnail.h" #include #ifdef __FreeBSD__ #include #endif #include #ifndef Q_OS_WIN #include #include #include // nice() #endif #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 #include #include #include // Use correctly KComponentData instead of KApplication (but then no QPixmap) #undef USE_KINSTANCE // Fix thumbnail: protocol #define THUMBNAIL_HACK (1) #ifdef THUMBNAIL_HACK # include #endif #include "imagefilter.h" // Recognized metadata entries: // mimeType - the mime type of the file, used for the overlay icon if any // width - maximum width for the thumbnail // height - maximum height for the thumbnail // iconSize - the size of the overlay icon to use if any // iconAlpha - the transparency value used for icon overlays // plugin - the name of the plugin library to be used for thumbnail creation. // Provided by the application to save an addition KTrader // query here. // enabledPlugins - a list of enabled thumbnailer plugins. PreviewJob does not call // this thumbnail slave when a given plugin isn't enabled. However, // for directory thumbnails it doesn't know that the thumbnailer // internally also loads the plugins. // shmid - the shared memory segment id to write the image's data to. // The segment is assumed to provide enough space for a 32-bit // image sized width x height pixels. // If this is given, the data returned by the slave will be: // int width // int height // int depth // Otherwise, the data returned is the image in PNG format. using namespace KIO; //using namespace KCodecs; extern "C" Q_DECL_EXPORT int kdemain( int argc, char **argv ) { #ifdef HAVE_NICE nice( 5 ); #endif #ifdef USE_KINSTANCE KComponentData componentData("kio_thumbnail"); #else QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts); // creating KApplication in a slave in not a very good idea, // as dispatchLoop() doesn't allow it to process its messages, // so it for example wouldn't reply to ksmserver - on the other // hand, this slave uses QPixmaps for some reason, and they // need QApplication // and HTML previews need even KApplication :( putenv(strdup("SESSION_MANAGER=")); // some thumbnail plugins reuse QWidget-tainted code for the rendering, // so use QApplication here, not just QGuiApplication QApplication app(argc, argv); #endif if (argc != 4) { qCritical() << "Usage: kio_thumbnail protocol domain-socket1 domain-socket2" << endl; exit(-1); } ThumbnailProtocol slave(argv[2], argv[3]); slave.dispatchLoop(); return 0; } ThumbnailProtocol::ThumbnailProtocol(const QByteArray &pool, const QByteArray &app) : SlaveBase("thumbnail", pool, app), m_iconSize(0), m_maxFileSize(0) { } ThumbnailProtocol::~ThumbnailProtocol() { qDeleteAll( m_creators ); m_creators.clear(); } void ThumbnailProtocol::get(const QUrl &url) { m_mimeType = metaData("mimeType"); m_enabledPlugins = metaData("enabledPlugins").split(QLatin1Char(','), QString::SkipEmptyParts); if (m_enabledPlugins.isEmpty()) { const KConfigGroup globalConfig(KSharedConfig::openConfig(), "PreviewSettings"); m_enabledPlugins = globalConfig.readEntry("Plugins", KIO::PreviewJob::defaultPlugins()); } //qDebug() << "Wanting MIME Type:" << m_mimeType; #ifdef THUMBNAIL_HACK // ### HACK bool direct=false; if (m_mimeType.isEmpty()) { QFileInfo info(url.path()); //qDebug() << "PATH: " << url.path() << "isDir:" << info.isDir(); if (!info.exists()) { // The file does not exist error(KIO::ERR_DOES_NOT_EXIST,url.path()); return; } else if (!info.isReadable()) { // The file is not readable! error(KIO::ERR_COULD_NOT_READ,url.path()); return; } if (info.isDir()) { m_mimeType = "inode/directory"; } else { const QMimeDatabase db; m_mimeType = db.mimeTypeForUrl(QUrl(info.filePath())).name(); } //qDebug() << "Guessing MIME Type:" << m_mimeType; direct=true; // thumbnail: URL was probably typed in Konqueror } #endif if (m_mimeType.isEmpty()) { error(KIO::ERR_INTERNAL, i18n("No MIME Type specified.")); return; } m_width = metaData("width").toInt(); m_height = metaData("height").toInt(); int iconSize = metaData("iconSize").toInt(); if (m_width < 0 || m_height < 0) { error(KIO::ERR_INTERNAL, i18n("No or invalid size specified.")); return; } #ifdef THUMBNAIL_HACK else if (!m_width || !m_height) { //qDebug() << "Guessing height, width, icon size!"; m_width = 128; m_height = 128; iconSize = 128; } #endif if (!iconSize) { iconSize = KIconLoader::global()->currentSize(KIconLoader::Desktop); } if (iconSize != m_iconSize) { m_iconDict.clear(); } m_iconSize = iconSize; m_iconAlpha = metaData("iconAlpha").toInt(); QImage img; KConfigGroup group( KSharedConfig::openConfig(), "PreviewSettings" ); bool kfmiThumb = false; // TODO Figure out if we can use KFileMetadata as a last resource ThumbCreator::Flags flags = ThumbCreator::None; if (!kfmiThumb) { QString plugin = metaData("plugin"); if ((plugin.isEmpty() || plugin == "directorythumbnail") && m_mimeType == "inode/directory") { img = thumbForDirectory(url); if(img.isNull()) { error(KIO::ERR_INTERNAL, i18n("Cannot create thumbnail for directory")); return; } } else { #ifdef THUMBNAIL_HACK if (plugin.isEmpty()) { plugin = pluginForMimeType(m_mimeType); } //qDebug() << "Guess plugin: " << plugin; #endif if (plugin.isEmpty()) { error(KIO::ERR_INTERNAL, i18n("No plugin specified.")); return; } ThumbCreator* creator = getThumbCreator(plugin); if(!creator) { error(KIO::ERR_INTERNAL, i18n("Cannot load ThumbCreator %1", plugin)); return; } ThumbSequenceCreator* sequenceCreator = dynamic_cast(creator); if(sequenceCreator) sequenceCreator->setSequenceIndex(sequenceIndex()); if (!creator->create(url.path(), m_width, m_height, img)) { error(KIO::ERR_INTERNAL, i18n("Cannot create thumbnail for %1", url.path())); return; } flags = creator->flags(); } } scaleDownImage(img, m_width, m_height); if (flags & ThumbCreator::DrawFrame) { int x2 = img.width() - 1; int y2 = img.height() - 1; // paint a black rectangle around the "page" QPainter p; p.begin( &img ); p.setPen( QColor( 48, 48, 48 )); p.drawLine( x2, 0, x2, y2 ); p.drawLine( 0, y2, x2, y2 ); p.setPen( QColor( 215, 215, 215 )); p.drawLine( 0, 0, x2, 0 ); p.drawLine( 0, 0, 0, y2 ); p.end(); } if ((flags & ThumbCreator::BlendIcon) && KIconLoader::global()->alphaBlending(KIconLoader::Desktop)) { // blending the mimetype icon in QImage icon = getIcon(); int x = img.width() - icon.width() - 4; x = qMax( x, 0 ); int y = img.height() - icon.height() - 6; y = qMax( y, 0 ); QPainter p(&img); p.setOpacity(m_iconAlpha/255.0); p.drawImage(x, y, icon); } if (img.isNull()) { error(KIO::ERR_INTERNAL, i18n("Failed to create a thumbnail.")); return; } const QString shmid = metaData("shmid"); if (shmid.isEmpty()) { #ifdef THUMBNAIL_HACK if (direct) { // If thumbnail was called directly from Konqueror, then the image needs to be raw //qDebug() << "RAW IMAGE TO STREAM"; QBuffer buf; if (!buf.open(QIODevice::WriteOnly)) { error(KIO::ERR_INTERNAL, i18n("Could not write image.")); return; } img.save(&buf,"PNG"); buf.close(); mimeType("image/png"); data(buf.buffer()); } else #endif { QByteArray imgData; QDataStream stream( &imgData, QIODevice::WriteOnly ); //qDebug() << "IMAGE TO STREAM"; stream << img; mimeType("application/octet-stream"); data(imgData); } } else { #ifndef Q_OS_WIN QByteArray imgData; QDataStream stream( &imgData, QIODevice::WriteOnly ); //qDebug() << "IMAGE TO SHMID"; void *shmaddr = shmat(shmid.toInt(), nullptr, 0); if (shmaddr == (void *)-1) { error(KIO::ERR_INTERNAL, i18n("Failed to attach to shared memory segment %1", shmid)); return; } if (img.width() * img.height() > m_width * m_height) { error(KIO::ERR_INTERNAL, i18n("Image is too big for the shared memory segment")); shmdt((char*)shmaddr); return; } if( img.format() != QImage::Format_ARGB32 ) { // KIO::PreviewJob and this code below completely ignores colortable :-/, img = img.convertToFormat(QImage::Format_ARGB32); // so make sure there is none } // Keep in sync with kdelibs/kio/kio/previewjob.cpp stream << img.width() << img.height() << quint8(img.format()); memcpy(shmaddr, img.bits(), img.byteCount()); shmdt((char*)shmaddr); mimeType("application/octet-stream"); data(imgData); #endif } finished(); } QString ThumbnailProtocol::pluginForMimeType(const QString& mimeType) { KService::List offers = KMimeTypeTrader::self()->query( mimeType, QLatin1String("ThumbCreator")); if (!offers.isEmpty()) { KService::Ptr serv; serv = offers.first(); return serv->library(); } //Match group mimetypes ///@todo Move this into some central location together with the related matching code in previewjob.cpp. This doesn't handle inheritance and such const KService::List plugins = KServiceTypeTrader::self()->query("ThumbCreator"); - foreach(KService::Ptr plugin, plugins) { + for (const KService::Ptr& plugin : plugins) { const QStringList mimeTypes = plugin->serviceTypes(); - foreach(QString mime, mimeTypes) { + for (const QString& mime : mimeTypes) { if(mime.endsWith('*')) { - mime = mime.left(mime.length()-1); - if(mimeType.startsWith(mime)) + const auto mimeGroup = mime.leftRef(mime.length()-1); + if(mimeType.startsWith(mimeGroup)) return plugin->library(); } } } return QString(); } float ThumbnailProtocol::sequenceIndex() const { return metaData("sequence-index").toFloat(); } bool ThumbnailProtocol::isOpaque(const QImage &image) const { // Test the corner pixels return qAlpha(image.pixel(QPoint(0, 0))) == 255 && qAlpha(image.pixel(QPoint(image.width()-1, 0))) == 255 && qAlpha(image.pixel(QPoint(0, image.height()-1))) == 255 && qAlpha(image.pixel(QPoint(image.width()-1, image.height()-1))) == 255; } void ThumbnailProtocol::drawPictureFrame(QPainter *painter, const QPoint ¢erPos, const QImage &image, int frameWidth, QSize imageTargetSize) const { // Scale the image down so it matches the aspect ratio float scaling = 1.0; if ((image.size().width() > imageTargetSize.width()) && (imageTargetSize.width() != 0)) { scaling = float(imageTargetSize.width()) / float(image.size().width()); } QImage frame(imageTargetSize + QSize(frameWidth * 2, frameWidth * 2), QImage::Format_ARGB32); frame.fill(0); float scaledFrameWidth = frameWidth / scaling; QTransform m; m.rotate(qrand() % 17 - 8); // Random rotation ±8° m.scale(scaling, scaling); QRectF frameRect(QPointF(0, 0), QPointF(image.width() + scaledFrameWidth*2, image.height() + scaledFrameWidth*2)); QRect r = m.mapRect(QRectF(frameRect)).toAlignedRect(); QImage transformed(r.size(), QImage::Format_ARGB32); transformed.fill(0); QPainter p(&transformed); p.setRenderHint(QPainter::SmoothPixmapTransform); p.setCompositionMode(QPainter::CompositionMode_Source); p.translate(-r.topLeft()); p.setWorldTransform(m, true); if (isOpaque(image)) { p.setRenderHint(QPainter::Antialiasing); p.setPen(Qt::NoPen); p.setBrush(Qt::white); p.drawRoundedRect(frameRect, scaledFrameWidth / 2, scaledFrameWidth / 2); } p.drawImage(scaledFrameWidth, scaledFrameWidth, image); p.end(); int radius = qMax(frameWidth, 1); QImage shadow(r.size() + QSize(radius * 2, radius * 2), QImage::Format_ARGB32); shadow.fill(0); p.begin(&shadow); p.setCompositionMode(QPainter::CompositionMode_Source); p.drawImage(radius, radius, transformed); p.end(); ImageFilter::shadowBlur(shadow, radius, QColor(0, 0, 0, 128)); r.moveCenter(centerPos); painter->drawImage(r.topLeft() - QPoint(radius / 2, radius / 2), shadow); painter->drawImage(r.topLeft(), transformed); } QImage ThumbnailProtocol::thumbForDirectory(const QUrl& directory) { QImage img; if (m_propagationDirectories.isEmpty()) { // Directories that the directory preview will be propagated into if there is no direct sub-directories const KConfigGroup globalConfig(KSharedConfig::openConfig(), "PreviewSettings"); m_propagationDirectories = globalConfig.readEntry("PropagationDirectories", QStringList() << "VIDEO_TS").toSet(); m_maxFileSize = globalConfig.readEntry("MaximumSize", std::numeric_limits::max()); } const int tiles = 2; //Count of items shown on each dimension const int spacing = 1; const int visibleCount = tiles * tiles; // TODO: the margins are optimized for the Oxygen iconset // Provide a fallback solution for other iconsets (e. g. draw folder // only as small overlay, use no margins) QString localFile = directory.path(); KFileItem item(QUrl::fromLocalFile(localFile)); const int extent = qMin(m_width, m_height); QPixmap folder = QIcon::fromTheme(item.iconName()).pixmap(extent); // Scale up base icon to ensure overlays are rendered with // the best quality possible even for low-res custom folder icons if (qMax(folder.width(), folder.height()) < extent) { folder = folder.scaled(extent, extent, Qt::KeepAspectRatio, Qt::SmoothTransformation); } const int folderWidth = folder.width(); const int folderHeight = folder.height(); const int topMargin = folderHeight * 30 / 100; const int bottomMargin = folderHeight / 6; const int leftMargin = folderWidth / 13; const int rightMargin = leftMargin; const int segmentWidth = (folderWidth - leftMargin - rightMargin + spacing) / tiles - spacing; const int segmentHeight = (folderHeight - topMargin - bottomMargin + spacing) / tiles - spacing; if ((segmentWidth < 5) || (segmentHeight < 5)) { // the segment size is too small for a useful preview return img; } // Multiply with a high number, so we get some semi-random sequence int skipValidItems = ((int)sequenceIndex()) * tiles * tiles; img = QImage(QSize(folderWidth, folderHeight), QImage::Format_ARGB32); img.fill(0); QPainter p; p.begin(&img); p.setCompositionMode(QPainter::CompositionMode_Source); p.drawPixmap(0, 0, folder); p.setCompositionMode(QPainter::CompositionMode_SourceOver); int xPos = leftMargin; int yPos = topMargin; int frameWidth = qRound(folderWidth / 85.); int iterations = 0; QString hadFirstThumbnail; int skipped = 0; const int maxYPos = folderHeight - bottomMargin - segmentHeight; int validThumbnails = 0; while ((skipped <= skipValidItems) && (yPos <= maxYPos) && validThumbnails == 0) { QDirIterator dir(localFile, QDir::Files | QDir::Readable); if (!dir.hasNext()) { break; } while (dir.hasNext() && (yPos <= maxYPos)) { ++iterations; if (iterations > 500) { skipValidItems = skipped = 0; break; } dir.next(); if (validThumbnails > 0 && hadFirstThumbnail == dir.filePath()) { break; // Never show the same thumbnail twice } if (dir.fileInfo().size() > m_maxFileSize) { // don't create thumbnails for files that exceed // the maximum set file size continue; } if (!drawSubThumbnail(p, dir.filePath(), segmentWidth, segmentHeight, xPos, yPos, frameWidth)) { continue; } if (skipped < skipValidItems) { ++skipped; continue; } if (hadFirstThumbnail.isEmpty()) { hadFirstThumbnail = dir.filePath(); } ++validThumbnails; xPos += segmentWidth + spacing; if (xPos > folderWidth - rightMargin - segmentWidth) { xPos = leftMargin; yPos += segmentHeight + spacing; } } if (skipped != 0) { // Round up to full pages const int roundedDown = (skipped / visibleCount) * visibleCount; if (roundedDown < skipped) { skipped = roundedDown + visibleCount; } else { skipped = roundedDown; } } if (skipped == 0) { break; // No valid items were found } // We don't need to iterate again and again: Subtract any multiple of "skipped" from the count we still need to skip skipValidItems -= (skipValidItems / skipped) * skipped; skipped = 0; } p.end(); if (validThumbnails == 0) { // Eventually propagate the contained items from a sub-directory QDirIterator dir(localFile, QDir::Dirs); int max = 50; while (dir.hasNext() && max > 0) { --max; dir.next(); if (m_propagationDirectories.contains(dir.fileName())) { return thumbForDirectory(QUrl(dir.filePath())); } } // If no thumbnail could be found, return an empty image which indicates // that no preview for the directory is available. img = QImage(); } // If only for one file a thumbnail could be generated then paint an image with only one tile if (validThumbnails == 1) { QImage oneTileImg(folder.size(), QImage::Format_ARGB32); oneTileImg.fill(0); QPainter oneTilePainter(&oneTileImg); oneTilePainter.setCompositionMode(QPainter::CompositionMode_Source); oneTilePainter.drawPixmap(0, 0, folder); oneTilePainter.setCompositionMode(QPainter::CompositionMode_SourceOver); const int oneTileWidth = folderWidth - leftMargin - rightMargin; const int oneTileHeight = folderHeight - topMargin - bottomMargin; drawSubThumbnail(oneTilePainter, hadFirstThumbnail, oneTileWidth, oneTileHeight, leftMargin, topMargin, frameWidth); return oneTileImg; } return img; } ThumbCreator* ThumbnailProtocol::getThumbCreator(const QString& plugin) { ThumbCreator *creator = m_creators[plugin]; if (!creator) { // Don't use KPluginFactory here, this is not a QObject and // neither is ThumbCreator QLibrary library(KPluginLoader::findPlugin((plugin))); if (library.load()) { newCreator create = (newCreator)library.resolve("new_creator"); if (create) { creator = create(); } } if (!creator) { return nullptr; } m_creators.insert(plugin, creator); } return creator; } const QImage ThumbnailProtocol::getIcon() { const QMimeDatabase db; ///@todo Can we really do this? It doesn't seem to respect the size if (!m_iconDict.contains(m_mimeType)) { // generate it QImage icon(KIconLoader::global()->loadMimeTypeIcon(db.mimeTypeForName(m_mimeType).iconName(), KIconLoader::Desktop, m_iconSize).toImage()); icon = icon.convertToFormat(QImage::Format_ARGB32); m_iconDict.insert(m_mimeType, icon); return icon; } return m_iconDict.value(m_mimeType); } bool ThumbnailProtocol::createSubThumbnail(QImage& thumbnail, const QString& filePath, int segmentWidth, int segmentHeight) { const QMimeDatabase db; const QUrl fileUrl = QUrl::fromLocalFile(filePath); const QString subPlugin = pluginForMimeType(db.mimeTypeForUrl(fileUrl).name()); if (subPlugin.isEmpty() || !m_enabledPlugins.contains(subPlugin)) { return false; } ThumbCreator* subCreator = getThumbCreator(subPlugin); if (!subCreator) { // qDebug() << "found no creator for" << dir.filePath(); return false; } if ((segmentWidth <= 256) && (segmentHeight <= 256)) { // check whether a cached version of the file is available for // 128 x 128 or 256 x 256 pixels int cacheSize = 0; QCryptographicHash md5(QCryptographicHash::Md5); md5.addData(QFile::encodeName(fileUrl.toString())); const QString thumbName = QFile::encodeName(md5.result().toHex()).append(".png"); if (m_thumbBasePath.isEmpty()) { m_thumbBasePath = QStandardPaths::writableLocation(QStandardPaths::GenericCacheLocation) + QLatin1String("/thumbnails/"); QDir basePath(m_thumbBasePath); basePath.mkpath("normal/"); QFile::setPermissions(basePath.absoluteFilePath("normal"), QFile::ReadOwner | QFile::WriteOwner | QFile::ExeOwner); basePath.mkpath("large/"); QFile::setPermissions(basePath.absoluteFilePath("large"), QFile::ReadOwner | QFile::WriteOwner | QFile::ExeOwner); } QDir thumbPath(m_thumbBasePath); if ((segmentWidth <= 128) && (segmentHeight <= 128)) { cacheSize = 128; thumbPath.cd("normal"); } else { cacheSize = 256; thumbPath.cd("large"); } if (!thumbnail.load(thumbPath.absoluteFilePath(thumbName))) { // no cached version is available, a new thumbnail must be created QSaveFile thumbnailfile(thumbPath.absoluteFilePath(thumbName)); bool savedCorrectly = false; if (subCreator->create(filePath, cacheSize, cacheSize, thumbnail)) { scaleDownImage(thumbnail, cacheSize, cacheSize); // The thumbnail has been created successfully. Store the thumbnail // to the cache for future access. if (thumbnailfile.open(QIODevice::WriteOnly | QIODevice::Truncate)) { savedCorrectly = thumbnail.save(&thumbnailfile, "PNG"); } } else { return false; } if(savedCorrectly) { thumbnailfile.commit(); } } } else if (!subCreator->create(filePath, segmentWidth, segmentHeight, thumbnail)) { return false; } return true; } void ThumbnailProtocol::scaleDownImage(QImage& img, int maxWidth, int maxHeight) { if (img.width() > maxWidth || img.height() > maxHeight) { img = img.scaled(maxWidth, maxHeight, Qt::KeepAspectRatio, Qt::SmoothTransformation); } } bool ThumbnailProtocol::drawSubThumbnail(QPainter& p, const QString& filePath, int width, int height, int xPos, int yPos, int frameWidth) { QImage subThumbnail; if (!createSubThumbnail(subThumbnail, filePath, width, height)) { return false; } // Seed the random number generator so that it always returns the same result // for the same directory and sequence-item qsrand(qHash(filePath)); // Apply fake smooth scaling, as seen on several blogs if (subThumbnail.width() > width * 4 || subThumbnail.height() > height * 4) { subThumbnail = subThumbnail.scaled(width*4, height*4, Qt::KeepAspectRatio, Qt::FastTransformation); } QSize targetSize(subThumbnail.size()); targetSize.scale(width, height, Qt::KeepAspectRatio); // center the image inside the segment boundaries const QPoint centerPos(xPos + (width/ 2), yPos + (height / 2)); drawPictureFrame(&p, centerPos, subThumbnail, frameWidth, targetSize); return true; }