Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
openSUSE:Leap:15.2:Staging:N
kdelibs4
ksuseinstall.diff
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File ksuseinstall.diff of Package kdelibs4
Index: CMakeLists.txt =================================================================== --- CMakeLists.txt.orig +++ CMakeLists.txt @@ -317,6 +317,7 @@ check_library_exists(socket connect "" H add_subdirectory( cmake ) add_subdirectory( kdecore ) add_subdirectory( kdeui ) +add_subdirectory( suseinstall ) if (UNIX) add_subdirectory( kpty ) add_subdirectory( kdesu ) Index: kio/CMakeLists.txt =================================================================== --- kio/CMakeLists.txt.orig +++ kio/CMakeLists.txt @@ -44,6 +44,7 @@ include_directories( ${CMAKE_CURRENT_BINARY_DIR}/kssl ${CMAKE_CURRENT_BINARY_DIR} # e.g. for observer_stub.h ${KDE4_KDEUI_INCLUDES} + ${CMAKE_SOURCE_DIR}/suseinstall ${KDE4_KDECORE_INCLUDES}/ ${ZLIB_INCLUDE_DIR} @@ -333,6 +334,7 @@ if(NOT KIO_NO_NEPOMUK) endif(NOT KIO_NO_NEPOMUK) target_link_libraries(kio LINK_PRIVATE ${KDE4_KDEUI_LIBS} ${ZLIB_LIBRARY} ${QT_QTNETWORK_LIBRARY} ${QT_QTXML_LIBRARY} ${QT_QTSVG_LIBRARY} ${X11_LIBRARIES} ${KIO_EXTRA_LIBS}) target_link_libraries(kio LINK_PUBLIC kdeui kdecore ${QT_QTNETWORK_LIBRARY} ${QT_QTXML_LIBRARY} ${QT_QTGUI_LIBRARY} ) +target_link_libraries(kio ksuseinstall) if(ACL_FOUND) target_link_libraries(kio LINK_PRIVATE ${ACL_LIBS}) Index: kio/kio/krun.cpp =================================================================== --- kio/kio/krun.cpp.orig +++ kio/kio/krun.cpp @@ -84,6 +84,8 @@ #include <QDesktopServices> #endif +#include <ksuseinstall.h> + KRun::KRunPrivate::KRunPrivate(KRun *parent) : q(parent), m_showingDialog(false) @@ -172,6 +174,7 @@ bool KRun::runUrl(const KUrl& u, const Q KUrl::List lst; lst.append(u); + KSUSEInstall::checkMimeTypeInstalled( _mimetype, window ); KService::Ptr offer = KMimeTypeTrader::self()->preferredService(_mimetype); if (!offer) { Index: suseinstall/CMakeLists.txt =================================================================== --- /dev/null +++ suseinstall/CMakeLists.txt @@ -0,0 +1,27 @@ +#cmake_minimum_required( VERSION 2.6 ) + +#project( ksuseinstall ) + +#find_package( KDE4 REQUIRED ) +#include( MacroLibrary ) +#include( KDE4Defaults ) +#add_definitions( ${QT_DEFINITIONS} ${KDE4_DEFINITIONS} -DHAVE_CONFIG_H=1 ) +include_directories( ${CMAKE_SOURCE_DIR} ${CMAKE_BINARY_DIR} ${KDE4_KDECORE_INCLUDES} ${KDE4_KDEUI_INCLUDES} ) + +########### next target ############### + +set( ksuseinstall_SRCS + ksuseinstall.cpp + kbuildsycocaprogressdialog.cpp + ) + +kde4_add_library( ksuseinstall SHARED ${ksuseinstall_SRCS} ) +target_link_libraries( ksuseinstall ${KDE4_KDEUI_LIBS}) +set_target_properties( ksuseinstall PROPERTIES VERSION 1 SOVERSION 1 ) +install( TARGETS ksuseinstall EXPORT kdelibsLibraryTargets ${INSTALL_TARGETS_DEFAULT_ARGS}) +install( FILES + ksuseinstall.h + ksuseinstall_export.h + DESTINATION ${INCLUDE_INSTALL_DIR} COMPONENT Devel + ) + Index: suseinstall/kbuildsycocaprogressdialog.cpp =================================================================== --- /dev/null +++ suseinstall/kbuildsycocaprogressdialog.cpp @@ -0,0 +1,110 @@ +/* This file is part of the KDE project + Copyright (C) 2003 Waldo Bastian <bastian@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + 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 "kbuildsycocaprogressdialog.h" +#include <ksycoca.h> +#include <kprocess.h> +#include <kstandarddirs.h> +#include <klocale.h> +#include <kapplication.h> +#include <QtDBus/QtDBus> + +namespace KSUSE +{ + +class KBuildSycocaProgressDialogPrivate +{ +public: + KBuildSycocaProgressDialogPrivate( KBuildSycocaProgressDialog *parent ) + : m_parent(parent) + { + } + + void _k_slotProgress(); + void _k_slotFinished(); + + KBuildSycocaProgressDialog *m_parent; + QTimer m_timer; + int m_timeStep; +}; + +void KBuildSycocaProgressDialog::rebuildKSycoca(QWidget *parent) +{ + KBuildSycocaProgressDialog dlg(parent, + i18n("Updating System Configuration"), + i18n("Updating system configuration.")); + + QDBusInterface kbuildsycoca("org.kde.kded", "/kbuildsycoca", + "org.kde.kbuildsycoca"); + if (kbuildsycoca.isValid()) { + kbuildsycoca.callWithCallback("recreate", QVariantList(), &dlg, SLOT(_k_slotFinished())); + } else { + // kded not running, e.g. when using keditfiletype out of a KDE session + QObject::connect(KSycoca::self(), SIGNAL(databaseChanged(QStringList)), &dlg, SLOT(_k_slotFinished())); + KProcess* proc = new KProcess(&dlg); + (*proc) << KStandardDirs::findExe(KBUILDSYCOCA_EXENAME); + proc->start(); + } + dlg.exec(); +} + +KBuildSycocaProgressDialog::KBuildSycocaProgressDialog(QWidget *_parent, + const QString &_caption, const QString &text) + : QProgressDialog(_parent) + , d( new KBuildSycocaProgressDialogPrivate(this) ) +{ + connect(&d->m_timer, SIGNAL(timeout()), this, SLOT(_k_slotProgress())); + setWindowTitle(_caption); + setModal(true); + setLabelText(text); + setRange(0, 20); + d->m_timeStep = 700; + d->m_timer.start(d->m_timeStep); + setAutoClose(false); +} + +KBuildSycocaProgressDialog::~KBuildSycocaProgressDialog() +{ + delete d; +} + +void KBuildSycocaProgressDialogPrivate::_k_slotProgress() +{ + const int p = m_parent->value(); + if (p == 18) + { + m_parent->reset(); + m_parent->setValue(1); + m_timeStep = m_timeStep * 2; + m_timer.start(m_timeStep); + } + else + { + m_parent->setValue(p+1); + } +} + +void KBuildSycocaProgressDialogPrivate::_k_slotFinished() +{ + m_parent->setValue(20); + m_timer.stop(); + QTimer::singleShot(1000, m_parent, SLOT(close())); +} + +} + +#include "kbuildsycocaprogressdialog.moc" Index: suseinstall/kbuildsycocaprogressdialog.h =================================================================== --- /dev/null +++ suseinstall/kbuildsycocaprogressdialog.h @@ -0,0 +1,60 @@ +/* This file is part of the KDE project + Copyright (C) 2003 Waldo Bastian <bastian@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + 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. +*/ + +#ifndef KBUILDSYCOCAPROGRESSDIALOG_H +#define KBUILDSYCOCAPROGRESSDIALOG_H + +#include <QtCore/QTimer> +#include <QtGui/QProgressDialog> + +// This is a copy from kio, since suseinstall doesn't link kio. +// The class should be moved from kio to kdeui probably. +namespace KSUSE +{ + +class KBuildSycocaProgressDialogPrivate; +/** + * Progress dialog while ksycoca is being rebuilt (by kbuildsycoca). + * Usage: KBuildSycocaProgressDialog::rebuildKSycoca(parentWidget) + */ +class KBuildSycocaProgressDialog : public QProgressDialog +{ + Q_OBJECT +public: + + /** + * Rebuild KSycoca and show a progress dialog while doing so. + * @param parent Parent widget for the progress dialog + */ + static void rebuildKSycoca(QWidget *parent); + +private: + KBuildSycocaProgressDialog(QWidget *parent, + const QString &caption, const QString &text); + ~KBuildSycocaProgressDialog(); + +private: + KBuildSycocaProgressDialogPrivate * const d; + + Q_PRIVATE_SLOT( d, void _k_slotProgress() ) + Q_PRIVATE_SLOT( d, void _k_slotFinished() ) +}; + +} + +#endif Index: suseinstall/ksuseinstall.cpp =================================================================== --- /dev/null +++ suseinstall/ksuseinstall.cpp @@ -0,0 +1,583 @@ +/***************************************************************************** + +Copyright (C) 2010 Lubos Lunak <l.lunak@suse.cz> + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +*****************************************************************************/ + +#include "ksuseinstall.h" + +#include <assert.h> +#include <kdebug.h> +#include <klocale.h> +#include <kmessagebox.h> +#include <kmimetype.h> +#include <kmimetypetrader.h> +#include <kprocess.h> +#include <kprogressdialog.h> +#include <kstandarddirs.h> +#include <qeventloop.h> +#include <qlabel.h> +#include <qlayout.h> +#include <qtreewidget.h> +#include <qwidget.h> + +#include "kbuildsycocaprogressdialog.h" + +bool KSUSEInstall::checkBinaryInstalled( const QString& name, QWidget* parent ) + { + if( !KStandardDirs::findExe( name ).isEmpty()) + return true; + if( globallyDisabled()) + return false; + if( KMessageBox::warningYesNo( parent, + // TODO what if it is not required for the app, but only would be useful? + i18n( "<qt><p>The application or utility <b>%1</b> is required but it does not appear to be installed.</p>" + "<p>Do you want to try to install it?</p></qt>", name ), + i18n( "Install Additional Software" ), + KStandardGuiItem::yes(), KStandardGuiItem::no(), ":suseinstall_binary_" + name, + // stupid defaults, remove Dangerous + KMessageBox::Notify ) != KMessageBox::Yes ) + { + return false; + } + // TODO zypper can't search for a specific file + return installCapabilities( QStringList() << name, FullInstallRequired, parent ); + } + +bool KSUSEInstall::canInstallDebuginfo() + { + return !KStandardDirs::findExe( "pbuildid" ).isEmpty() && !globallyDisabled(); + } + +bool KSUSEInstall::installDebuginfo( const QStringList& files, QWidget* parent ) + { + if( globallyDisabled()) + return false; + QStringList capabilities; + foreach( const QString& file, files ) + { + KProcess proc; + proc.setOutputChannelMode( KProcess::OnlyStdoutChannel ); + proc << KStandardDirs::findExe( "pbuildid" ) << file; + if( proc.execute() == 0 ) + { + QStringList output = QString::fromLocal8Bit( proc.readAll()).trimmed().split( " ", QString::SkipEmptyParts ); + if( output.count() == 2 ) + capabilities << ( "debuginfo(build-id) = " + output[ 1 ] ); + } + else + kWarning() << "pbuildid failed"; + } + return installCapabilities( capabilities, PartialInstallAllowed, parent ); + } + +// TODO also for other mimetypes (e.g. KParts)? +bool KSUSEInstall::checkMimeTypeInstalled( const QString& mimetype, QWidget* parent ) + { + QString fallbackMimeType; + if( KService::Ptr handler = KMimeTypeTrader::self()->preferredService( mimetype )) + { + if( handler->serviceTypes().contains( mimetype )) + return true; + // Some common mimetypes (text/plain subclasses mostly) are not explicitly listed as handled + // by many KDE apps and the inheritance is relied on, and it makes sense for these types + // to actually use it here. + if( mimeTypeInheritanceAllowed( mimetype )) + return true; + // If the preferred service doesn't directly support the mimetype but another + // handler with lower priority does, assume it's intentionally that way and the problem + // is just that the preferred service relies on mimetype inheritance or has been made + // preferred manually). + foreach( KService::Ptr service, KMimeTypeTrader::self()->query( mimetype )) + { + if( service->serviceTypes().contains( mimetype )) + return true; + } + // Otherwise ignore, it's just mimetype inheritance, but that's often broken here + // (for example, application/x-kvtml is a subclass of application/xml, so even + // if KWordQuiz is not installed, KMimeTypeTrader will still offer Konqueror, which + // will open the file as plain XML without understanding that the XML format is just + // an internal detail - the mimetype inheritance most probably should not be specified + // for this mimetype at all). + fallbackMimeType = findFallbackMimeType( mimetype, handler ); + } + if( mimetype == "application/octet-stream" ) + return false; // the everything and anything mimetype, don't even try + if( globallyDisabled()) + return !fallbackMimeType.isEmpty(); + QString msg; + QString mimeName = mimetype; + if( KMimeType::Ptr ptr = KMimeType::mimeType( mimetype )) + mimeName = ptr->comment() + " (" + mimetype + ")"; + if( !fallbackMimeType.isEmpty()) + { + QString fallbackMimeName = fallbackMimeType; + if( KMimeType::Ptr ptr = KMimeType::mimeType( fallbackMimeType )) + fallbackMimeName = ptr->comment() + " (" + fallbackMimeType + ")"; + msg = i18n( "<qt><p>There is no specialized application for files of the type <b>%1</b>.</p>" + "<p>The file can be opened in a more generic application as file type <b>%2</b>," + " or you can try to install a specialized application for file type <b>%1</b>.</p>" + "<p>Do you want to try to install a specialized application?</p>", mimeName, fallbackMimeName ); + } + else + { + msg = i18n( "<qt><p>There is no application installed that can open files of the type <b>%1</b>.</p>" + "<p>Do you want to try to install one?</p></qt>", mimeName ); + } + KGuiItem installGuiItem( KStandardGuiItem::yes()); + installGuiItem.setText( i18nc( "@action:button", "Install" )); + if( KMessageBox::warningYesNo( parent, msg, i18n( "Install Additional Software" ), + installGuiItem, KStandardGuiItem::no(), ":suseinstall_mimetype_" + mimetype ) != KMessageBox::Yes ) + { + return false; + } + QStringList mimetypes; + mimetypes << ( "mimetype(" + mimetype + ")" ); + mimetypes << ( "mimehandler(" + mimetype + ")" ); // used by rpm 4.9 +#if 0 // do not use wildcarded mimetypes - the only provider for image/* is Evince and for text/* is some Gtk editor + if( mimetype.contains( '/' ) && !mimetype.endsWith( "/*" )) // video/avi -> try also video/* + { + QString mimeWildcard = mimetype.left( mimetype.indexOf( '/' )) + "/*"; + mimetypes << ( "mimetype(" + mimeWildcard + ")" ); + mimetypes << ( "mimehandler(" + mimeWildcard + ")" ); // used by rpm 4.9 + } +#endif + bool changes; + bool ret = performInstallCapabilities( mimetypes, OneItemInstall, parent, &changes ); + if( ret && changes ) + KSUSE::KBuildSycocaProgressDialog::rebuildKSycoca( parent ); + return ret; + } + +// See usage above. If e.g. application/x-kvtml would be opened by Konqueror, but that's only because of +// mimetype inheritance, find as which mimetype it would be actually open (application/xml, here). +QString KSUSEInstall::findFallbackMimeType( const QString& mimetype, KService::Ptr handler ) + { + KMimeType::Ptr mime = KMimeType::mimeType( mimetype ); + foreach( const QString& parent, mime->parentMimeTypes()) + { + if( handler->serviceTypes().contains( parent )) + return parent; + } + foreach( const QString& parent, mime->parentMimeTypes()) + { // try indirect + QString ret = findFallbackMimeType( parent, handler ); + if( !ret.isEmpty()) + return ret; + } + return QString(); + } + +bool KSUSEInstall::installCapabilities( const QStringList& capabilities, InstallType type, QWidget* parent ) + { + if( globallyDisabled()) + return false; + return performInstallCapabilities( capabilities, type, parent, NULL ); + } + +bool KSUSEInstall::performInstallCapabilities( const QStringList& capabilities, InstallType type, QWidget* parent, + bool* installationChanges ) + { + if( installationChanges != NULL ) + *installationChanges = false; + KProgressDialog progress( parent, i18n( "Install Additional Software" ), + i18n( "Checking for additional software to install" )); + progress.setWindowModality( Qt::WindowModal ); + progress.progressBar()->setRange( 0, capabilities.count() + 1 ); + progress.setMinimumDuration( 0 ); + bool notAvailable = false; + QStringList toInstall = packagesToInstall( capabilities, type, parent, ¬Available, progress ); + kDebug() << "Not available:" << notAvailable << ", to install:" << toInstall; + while( notAvailable ) + { + if( type == OneItemInstall && !toInstall.isEmpty()) + break; // we have something, that's enough + if( progress.wasCancelled()) + return false; + if( tryAddRepositories( !toInstall.isEmpty(), &progress )) + toInstall = packagesToInstall( capabilities, type, parent, ¬Available, progress ); + else + break; + kDebug() << "Not available:" << notAvailable << ", to install:" << toInstall; + } + if( toInstall.isEmpty() && !notAvailable ) + return true; // all ok + if( toInstall.isEmpty() || ( notAvailable && type == FullInstallRequired )) + { + // TODO messagebox? + return !notAvailable; + } + assert( type != OneItemInstall || toInstall.count() == 1 ); // exactly one package for OneItemInstall + progress.progressBar()->setValue( capabilities.count()); // almost done + bool ret = installPackages( toInstall, &progress ); + if( ret && installationChanges != NULL ) + *installationChanges = true; // there were changes in installed packages + return ret; + } + +struct KSUSEInstall::PackageInstallData + { + PackageInstallData() + : installStatus( Available ) + {} + QString package; + InstallStatus installStatus; + }; + +QStringList KSUSEInstall::packagesToInstall( const QStringList& capabilities, InstallType type, QWidget* parent, + bool* notAvailable, KProgressDialog& progress ) + { + if( notAvailable != NULL ) + *notAvailable = false; + progress.progressBar()->setValue( 0 ); + QStringList toInstall; + foreach( const QString& capability, capabilities ) + { + QList< PackageInstallData > data = packagesForCapability( capability ); + if( progress.wasCancelled()) + return QStringList(); + progress.progressBar()->setValue( progress.progressBar()->value() + 1 ); + if( data.isEmpty()) + { + if( notAvailable != NULL ) + *notAvailable = true; + continue; + } + if( data.count() > 1 && type != OneItemInstall ) + data = ( QList< PackageInstallData >() << data.first()); // just use the first one, shouldn't matter + QStringList add; + foreach( const PackageInstallData& d, data ) + { + if( d.installStatus == Installed ) + { // something with this capability is installed, nothing more needed + add.clear(); + break; + } + else // d.installStatus == Available + add << d.package; + } + toInstall += add; + } + if( type == OneItemInstall ) + toInstall = selectOnePackageToInstall( toInstall, parent ); + return toInstall; + } + +bool KSUSEInstall::installPackages( const QStringList& packages, QWidget* parent ) + { + KProcess proc; + QString kdesu = KStandardDirs::findExe( "kdesu" ); + if( kdesu.isEmpty()) + return false; + proc << kdesu << "--noignorebutton"; + if( parent != NULL ) + proc << "--attach" << QString::number( parent->window()->winId()); + proc << "--" << "yast2" << "sw_single" << packages; + return run( proc ); + } + +bool KSUSEInstall::tryAddRepositories( bool partial, QWidget* parent ) + { + QString msg; + QString url = "<a href=\"http://help.opensuse.org/ksuseinstall\">http://help.opensuse.org/ksuseinstall</a>"; + if( partial ) + { + msg = i18nc( "added text is URL", + "<qt><p>Some of the software to install could not be found in the currently enabled software repositories." + "It may be located in other repositories.</p>" + "<p>See %1 for details.</p>" + "<p>Do you want to configure your repositories now?</p></qt>", url ); + } + else + { + msg = i18nc( "added text is URL", + "<qt><p>The software to install could not be found in the currently enabled software repositories." + "It may be located in other repositories.</p>" + "<p>See %1 for details.</p>" + "<p>Do you want to configure your repositories?</p></qt>", url ); + } + // TODO this re-enters the event loop + if( KMessageBox::questionYesNo( parent, msg, i18n( "Configure Software Repositories" ), + KStandardGuiItem::yes(), KStandardGuiItem::no(), ":suseinstall_add_repo", + KMessageBox::Notify | KMessageBox::AllowLink ) != KMessageBox::Yes ) + { + return false; + } + KProcess proc; + QString kdesu = KStandardDirs::findExe( "kdesu" ); + if( kdesu.isEmpty()) + return false; + proc << kdesu << "--noignorebutton"; + if( parent != NULL ) + proc << "--attach" << QString::number( parent->window()->winId()); + proc << "--" << "yast2" << "repositories"; +// Only 11.3 and newer have the extra argument that makes all enabled repos also be refreshed (bnc#608562) +#define HAVE_YAST2_REPOSITORIES_REFRESH_ENABLED +#ifdef HAVE_YAST2_REPOSITORIES_REFRESH_ENABLED + proc << "refresh-enabled"; +#endif + return run( proc ); + } + +QList< KSUSEInstall::PackageInstallData > KSUSEInstall::packagesForCapability( const QString& capability ) + { + kDebug() << "Searching for packages for capability \"" << capability << "\""; + KProcess proc; + proc.setEnv( "LC_ALL", "C" ); // it will be parsed + proc << "zypper" << "-n" << "-q" << "--no-refresh" << "what-provides" << capability; + proc.setOutputChannelMode( KProcess::OnlyStdoutChannel ); + if( !run( proc )) + return QList< PackageInstallData >(); // TODO + QStringList lines = QString::fromLocal8Bit( proc.readAll()).split( '\n', QString::SkipEmptyParts ); + if( lines.count() < 2 ) + return QList< PackageInstallData >(); // TODO + QString line = lines.takeFirst(); + // "S | Name | Type | Version | Arch | Repository" + QStringList tmp = line.split( '|' ); + if( tmp.count() < 3 || tmp[ 0 ].trimmed() != "S" || tmp[ 1 ].trimmed() != "Name" || tmp[ 2 ].trimmed() != "Type" ) + // new format since 13.1: "S | Name | Summary | Type" + if( tmp.count() < 4 || tmp[ 0 ].trimmed() != "S" || tmp[ 1 ].trimmed() != "Name" || tmp[ 3 ].trimmed() != "Type" ) + return QList< PackageInstallData >(); // TODO + lines.removeFirst(); // header separator line + QList< PackageInstallData > possibilities; + QString previous; + foreach( const QString& line, lines ) + { + QStringList tmp = line.split( '|' ); + if( tmp.count() == 4) // new format + tmp.removeAt(2); // remove "Summary" column to bring it in line with older format + if( tmp.count() < 3 ) + continue; // huh? + if( tmp[ 2 ].trimmed() != "package" ) + continue; + PackageInstallData data; + kDebug() << line; + // 'i' means installed, 'v' means installed but different version, empty means not installed + data.installStatus = ( tmp[ 0 ].trimmed().isEmpty() ? Available : Installed ); + data.package = tmp[ 1 ].trimmed(); + if( data.installStatus == Installed ) + return ( QList< PackageInstallData >() << data ); // it is installed, so any match is enough + if( previous != data.package ) // there may be several hits (i.e. different versions) + { + possibilities.append( data ); + previous = data.package; + } + else + { // several possibilities, make sure it's considered installed if any version is + if( data.installStatus == Installed ) + possibilities.last().installStatus = Installed; + } + } + if( possibilities.isEmpty()) + return QList< PackageInstallData >(); // not available + return possibilities; + } + +struct KSUSEInstall::PackageInfoData + { + QString name; + QString version; + QString summary; + QString description; + }; + +QStringList KSUSEInstall::selectOnePackageToInstall( const QStringList& packages, QWidget* parent ) + { + if( packages.count() <= 1 ) + return packages; + QList< PackageInfoData > data = readPackagesInfo( packages ); + if( data.count() == 1 ) + return ( QStringList() << data.first().name ); + if( data.isEmpty()) + return QStringList(); + SelectPackageDialog dialog( data, parent ); + if( dialog.exec() == QDialog::Accepted ) + return ( QStringList() << dialog.selectedPackage()); + return QStringList(); + } + +QList< KSUSEInstall::PackageInfoData > KSUSEInstall::readPackagesInfo( const QStringList& packages ) + { + kDebug() << "Reading info for packages:" << packages; + KProcess proc; + // TODO This is kind of broken, but it's currently ok, as the package information itself + // does not seem to be translated, and parsing localized zypper output would be a problem. + // And the "xml" output mode of zypper is just a joke. + proc.setEnv( "LC_ALL", "C" ); + proc << "zypper" << "-n" << "-q" << "--no-refresh" << "info" << packages; + proc.setOutputChannelMode( KProcess::OnlyStdoutChannel ); + if( !run( proc )) + return QList< PackageInfoData >(); // TODO + QStringList lines = QString::fromLocal8Bit( proc.readAll()).split( '\n' ); + kDebug() << lines; + if( lines.count() < 2 ) + return QList< PackageInfoData >(); // TODO + QList< PackageInfoData > data; + foreach( const QString& package, packages ) + { + PackageInfoData d; + while( !lines.isEmpty()) + { + QString line = lines.takeFirst(); + if( line == "Information for package " + package + ":" ) + break; + } + if( lines.isEmpty()) + { + kWarning() << "Failed to find package info for package:" << package; + return QList< PackageInfoData >(); + } + bool description = false; + bool wasEmpty = false; + while( !lines.isEmpty()) + { + QString line = lines.takeFirst(); + if( description ) + { + if( line.startsWith( "Information for package " ) && wasEmpty ) + { + lines.prepend( line ); + break; + } + wasEmpty = ( line.isEmpty()); + d.description += line + '\n'; + continue; + } + if( line.startsWith( "Name:" )) + d.name = QString( line ).remove( "Name:" ).trimmed(); + if( line.startsWith( "Version:" )) + d.version = QString( line ).remove( "Version:" ).trimmed(); + if( line.startsWith( "Summary:" )) + d.summary = QString( line ).remove( "Summary:" ).trimmed(); + if( line.startsWith( "Description:" )) + description = true; // read the rest as description + } + if( d.name.isEmpty() || d.version.isEmpty() || d.summary.isEmpty() || d.description.isEmpty()) + { + kWarning() << "Failed to parse zypper info output for package:" << package; + return QList< PackageInfoData >(); + } + data.append( d ); + } + return data; + } + +// like proc.execute() == 0, but enters the event loop, so that the progress dialog works etc. +bool KSUSEInstall::run( KProcess& proc ) + { + QEventLoop loop; + QObject::connect( &proc, SIGNAL( error( QProcess::ProcessError )), &loop, SLOT( quit())); + QObject::connect( &proc, SIGNAL( finished( int, QProcess::ExitStatus )), &loop, SLOT( quit())); + proc.start(); + loop.exec(); + return proc.exitStatus() == QProcess::NormalExit && proc.exitCode() == 0; + } + +// See checkMimeTypeInstalled() - mimetype inheritance is often wrong. +// Check here it inheritance actually makes sense. +bool KSUSEInstall::mimeTypeInheritanceAllowed( const QString& mimetype ) + { + // text/plain: + if( mimetype == "text/css" + || mimetype == "text/enriched" + || mimetype == "text/rfc822-headers" + || mimetype == "text/richtext" + || mimetype == "text/sgml" + || mimetype == "text/troff" + || mimetype == "text/x-adasrc" + || mimetype == "text/x-authors" + || mimetype == "text/x-c++hdr" // kwrite doesn't list most devel text files explicitly + || mimetype == "text/x-c++src" + || mimetype == "text/x-changelog" + || mimetype == "text/x-chdr" + || mimetype == "text/x-cmake" + || mimetype == "text/x-copying" + || mimetype == "text/x-credits" + || mimetype == "text/x-csharp" + || mimetype == "text/x-csrc" + || mimetype == "text/x-dcl" + || mimetype == "text/x-dsl" + || mimetype == "text/x-dsrc" + || mimetype == "text/x-java" + || mimetype == "text/x-log" + || mimetype == "text/x-makefile" + || mimetype == "text/x-matlab" + || mimetype == "text/x-moc" + || mimetype == "text/x-nfo" + || mimetype == "text/x-patch" + || mimetype == "text/x-python" + || mimetype == "text/x-readme" + || mimetype == "text/x-rpm-spec" + || mimetype == "text/x-tcl" + || mimetype == "text/x-tex" + || mimetype == "text/x-texinfo" ) + { + return true; + } + // text/html: + if( mimetype == "text/htmlh" ) + return true; + return false; + } + +bool KSUSEInstall::globallyDisabled() + { + return KConfigGroup( KGlobal::config(), "KSUSEInstall" ).readEntry( "Enabled", true ) == false; + } + +KSUSEInstall::SelectPackageDialog::SelectPackageDialog( const QList< PackageInfoData >& data, QWidget* parent ) + : KDialog( parent ) + { + setCaption( i18n( "Select package to install" )); + setButtons( Ok | Cancel ); + QWidget* widget = new QWidget( this ); + QVBoxLayout* layout = new QVBoxLayout( widget ); + QLabel* intro = new QLabel( + i18n( "There are several alternatives.\n\nPlease select which package to install." ), widget ); + layout->addWidget( intro ); + list = new QTreeWidget( widget ); + list->setHeaderLabels( QStringList() << i18n( "Package name" ) << i18n( "Summary" ) << i18n( "Version" )); + list->setAllColumnsShowFocus( true ); + list->setRootIsDecorated( false ); + foreach( const PackageInfoData& d, data ) + { + list->addTopLevelItem( new QTreeWidgetItem( QStringList() << d.name << d.summary << d.version )); + descriptions.append( d.description ); + } + connect( list, SIGNAL( currentItemChanged( QTreeWidgetItem*, QTreeWidgetItem* )), this, SLOT( currentChanged())); + layout->addWidget( list ); + QLabel* ldesc = new QLabel( "<b>" + i18n( "Description:" ) + "</b>", widget ); + layout->addWidget( ldesc ); + description = new QLabel( widget ); + layout->addWidget( description ); + setMainWidget( widget ); + list->setCurrentItem( list->topLevelItem( 0 )); // TODO select the most suitable + currentChanged(); // set description + } + +QString KSUSEInstall::SelectPackageDialog::selectedPackage() const + { + return list->currentItem()->text( 0 ); + } + +void KSUSEInstall::SelectPackageDialog::currentChanged() + { + description->setText( descriptions[ list->indexOfTopLevelItem( list->currentItem()) ] ); + } Index: suseinstall/ksuseinstall_export.h =================================================================== --- /dev/null +++ suseinstall/ksuseinstall_export.h @@ -0,0 +1,40 @@ +/***************************************************************************** + +Copyright (C) 2010 Lubos Lunak <l.lunak@suse.cz> + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +*****************************************************************************/ + +#ifndef KSUSEINSTALL_EXPORT_H +#define KSUSEINSTALL_EXPORT_H + +/* needed for KDE_EXPORT and KDE_IMPORT macros */ +#include <kdemacros.h> + +#ifndef KSUSEINSTALL_EXPORT +# if defined(MAKE_KSUSEINSTALL_LIB) + /* We are building this library */ +# define KSUSEINSTALL_EXPORT KDE_EXPORT +# else + /* We are using this library */ +# define KSUSEINSTALL_EXPORT KDE_IMPORT +# endif +#endif + +#endif Index: suseinstall/ksuseinstall.h =================================================================== --- /dev/null +++ suseinstall/ksuseinstall.h @@ -0,0 +1,93 @@ +/***************************************************************************** + +Copyright (C) 2010 Lubos Lunak <l.lunak@suse.cz> + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +*****************************************************************************/ + +#ifndef KSUSEINSTALL_H +#define KSUSEINSTALL_H + +/* + + This API is internal and not stable. +*/ + +#include <ksuseinstall_export.h> + +#include <QtCore/QStringList> + +#include <kdialog.h> +#include <kservice.h> + +class KProcess; +class KProgressDialog; +class QLabel; +class QTreeWidget; + +class KSUSEINSTALL_EXPORT KSUSEInstall + { + public: + static bool checkBinaryInstalled( const QString& name, QWidget* parent ); + static bool installDebuginfo( const QStringList& files, QWidget* parent ); + static bool canInstallDebuginfo(); + enum InstallType + { + PartialInstallAllowed, /// it is ok if at least one item can be installed + FullInstallRequired, /// all requested items must be installed + OneItemInstall /// exactly one item in the list must be installed, offer choice if there are more + }; + static bool installCapabilities( const QStringList& capabilities, InstallType type, QWidget* parent ); + static bool checkMimeTypeInstalled( const QString& mimetype, QWidget* parent ); + private: + static bool globallyDisabled(); + static bool run( KProcess& proc ); + static bool performInstallCapabilities( const QStringList& capabilities, InstallType type, QWidget* parent, + bool* installationChanges ); + static QStringList packagesToInstall( const QStringList& capabilities, InstallType type, QWidget* parent, + bool* notAvailable, KProgressDialog& progress ); + struct PackageInstallData; + static QList< PackageInstallData > packagesForCapability( const QString& capability ); + static bool installPackages( const QStringList& packages, QWidget* parent ); + static bool tryAddRepositories( bool partial, QWidget* parent ); + static bool mimeTypeInheritanceAllowed( const QString& mimetype ); + static QString findFallbackMimeType( const QString& mimetype, KService::Ptr handler ); + static QStringList selectOnePackageToInstall( const QStringList& packages, QWidget* parent ); + struct PackageInfoData; + static QList< PackageInfoData > readPackagesInfo( const QStringList& packages ); + enum InstallStatus { Available, Installed }; + class SelectPackageDialog; + }; + +class KSUSEInstall::SelectPackageDialog + : public KDialog + { + Q_OBJECT + public: + SelectPackageDialog( const QList< PackageInfoData >& data, QWidget* parent ); + QString selectedPackage() const; + private slots: + void currentChanged(); + private: + QTreeWidget* list; + QLabel* description; + QStringList descriptions; + }; + +#endif
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor