Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
openSUSE
kdelibs4.6738
ksuseinstall.diff
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File ksuseinstall.diff of Package kdelibs4.6738
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