File ksuseinstall.diff of Package mingw64-kdelibs4
Index: CMakeLists.txt
===================================================================
--- CMakeLists.txt.orig
+++ CMakeLists.txt
@@ -311,6 +311,7 @@ check_library_exists(socket connect "" HAVE_SOCKET_LIBRARY)
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}
@@ -331,6 +332,7 @@ if(NOT KIO_NO_NEPOMUK)
endif(NOT KIO_NO_NEPOMUK)
target_link_libraries(kio ${KDE4_KDEUI_LIBS} ${ZLIB_LIBRARY} ${QT_QTNETWORK_LIBRARY} ${QT_QTXML_LIBRARY} ${QT_QTSVG_LIBRARY} ${X11_LIBRARIES} ${KIO_EXTRA_LIBS})
target_link_libraries(kio LINK_INTERFACE_LIBRARIES kdeui kdecore ${QT_QTNETWORK_LIBRARY} ${QT_QTXML_LIBRARY} ${QT_QTGUI_LIBRARY} )
+target_link_libraries(kio ksuseinstall)
if(ACL_FOUND)
target_link_libraries(kio ${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 QString& _mimetype, QWidget* window, bool
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 << "--" << "zypper" << "--non-interactive" << "install" << "--force-resolution" << 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