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,579 @@
+/*****************************************************************************  
+ 
+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, &notAvailable, 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, &notAvailable, 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" << "-i" << 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" )  
+        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() < 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  
