Prechádzať zdrojové kódy

increased robustness and added error dialog

Malte Veerman 10 rokov pred
rodič
commit
e14e526cc5

+ 2 - 3
lib/src/fan.cpp

@@ -25,7 +25,6 @@
 #include <QtCore/QTextStream>
 #include <QtCore/QDir>
 #include <QtCore/QFile>
-#include <QtCore/QDebug>
 
 #include <KConfigCore/KSharedConfig>
 #include <KConfigCore/KConfigGroup>
@@ -48,7 +47,7 @@ Fan::Fan(Hwmon *parent, uint index) :
             *m_rpmStream >> m_rpm;
         }
         else
-            qDebug() << "Can't open rpmFile " << parent->path() + "/fan" + QString::number(index) + "_input";
+            emit errorChanged("Can't open rpmFile " + parent->path() + "/fan" + QString::number(index) + "_input");
     }
 }
 
@@ -95,7 +94,7 @@ void Fan::reset()
             *m_rpmStream >> m_rpm;
         }
         else
-            qDebug() << "Can't open rpmFile " << m_parent->path() + "/fan" + QString::number(m_index) + "_input";
+            emit errorChanged("Can't open rpmFile " + m_parent->path() + "/fan" + QString::number(m_index) + "_input");
     }
 }
 

+ 24 - 3
lib/src/hwmon.cpp

@@ -22,7 +22,6 @@
 
 #include <QtCore/QDir>
 #include <QtCore/QTextStream>
-#include <QtCore/QDebug>
 
 
 namespace Fancontrol
@@ -30,8 +29,23 @@ namespace Fancontrol
 
 Hwmon::Hwmon(const QString &path, QObject *parent) : QObject(parent),
     m_path(path),
-    m_index(path.split('/').last().remove("hwmon").toInt())
+    m_valid(true)
 {
+    QDir dir(path);
+    if (!dir.isReadable())
+    {
+        emit errorChanged(path + " is not readable!");
+        m_valid = false;
+    }
+    
+    bool success;
+    m_index = path.split('/').last().remove("hwmon").toInt(&success);
+    if (!success)
+    {
+        emit errorChanged(path + "is invalid!");
+        m_valid = false;
+    }
+    
     QFile nameFile(path + "/name");
     if (nameFile.open(QFile::ReadOnly))
         m_name = QTextStream(&nameFile).readLine();
@@ -41,8 +55,10 @@ Hwmon::Hwmon(const QString &path, QObject *parent) : QObject(parent),
     connect(this, SIGNAL(configUpdateNeeded()), parent, SLOT(createConfigFile()));
     connect(this, SIGNAL(pwmFansChanged()), parent, SLOT(emitAllPwmFansChanged()));
     connect(this, SIGNAL(tempsChanged()), parent, SLOT(emitAllTempsChanged()));
+    connect(this, SIGNAL(errorChanged(QString)), parent, SLOT(setError(QString)));
 
-    initialize();
+    if (m_valid)
+        initialize();
 }
 
 void Hwmon::initialize()
@@ -186,4 +202,9 @@ Temp* Hwmon::temp(int i) const
     return m_temps.value(i, Q_NULLPTR);
 }
 
+void Hwmon::setError(const QString &error)
+{
+    emit errorChanged(error);
+}
+
 }

+ 10 - 2
lib/src/hwmon.h

@@ -59,12 +59,18 @@ public:
     Fan * fan(int i) const;
     PwmFan * pwmFan(int i) const;
     Temp * temp(int i) const;
+    bool isValid() const { return m_valid; }
     
 
 public slots:
 
     void updateConfig() { emit configUpdateNeeded(); }
-    void updateSensors() { emit sensorsUpdateNeeded(); }    
+    void updateSensors() { emit sensorsUpdateNeeded(); }
+    
+    
+protected slots:
+    
+    void setError(const QString &error);
 
 
 signals:
@@ -74,13 +80,15 @@ signals:
     void tempsChanged();
     void configUpdateNeeded();
     void sensorsUpdateNeeded();
+    void errorChanged(QString);
 
 
 private:
 
     QString m_name;
     const QString m_path;
-    const int m_index;
+    bool m_valid;
+    int m_index;
     QList<Fan *> m_fans;
     QList<PwmFan *> m_pwmFans;
     QList<Temp *> m_temps;

+ 115 - 116
lib/src/loader.cpp

@@ -37,69 +37,17 @@
 namespace Fancontrol
 {
 
-//function that takes a config file entry and returns the hwmon and sensor numbers
-//returns a pair of <-1, -1> in case an error occurs
-QPair<int, int> getEntryNumbers(const QString &str)
-{
-    if (str.isEmpty())
-    {
-        qWarning() << "Loader::getHwmonNumber(): given empty string.";
-        return QPair<int, int>(-1, -1);
-    }
-        
-    QStringList list = str.split('/', QString::SkipEmptyParts);
-    if (list.size() != 2)
-    {
-        qWarning() << "Invalid entry to parse:" << str << "Should contain exactly one \'/\'";
-        return QPair<int, int>(-1, -1);
-    }
-    QString hwmon = list.at(0);
-    QString sensor = list.at(1);
-    
-    if (!hwmon.startsWith("hwmon"))
-    {
-        qWarning() << "Invalid entry to parse:" << str << "Should begin with \"hwmon\"";
-        return QPair<int, int>(-1, -1);
-    }
-    if (!sensor.contains(QRegExp("pwm|fan|temp|_input")))
-    {
-        qWarning() << "Invalid entry to parse:" << str << "Should contain \"pwm\" or \"fan\" or \"temp\" or \"_input\"";
-        return QPair<int, int>(-1, -1);
-    }
-        
-    bool success;
-    
-    hwmon.remove("hwmon");
-    sensor.remove(QRegExp("pwm|fan|temp|_input"));
-    
-    int hwmonResult = hwmon.toInt(&success);
-    if (!success)
-    {
-        qWarning() << "Invalid entry to parse:" << str << "Could not convert" << hwmon << "to int";
-        return QPair<int, int>(-1, -1);
-    }
-    int sensorResult = sensor.toInt(&success);
-    if (!success)
-    {
-        qWarning() << "Invalid entry to parse:" << str << "Could not convert" << sensor << "to int";
-        return QPair<int, int>(-1, -1);
-    }
-        
-    return QPair<int, int>(hwmonResult, sensorResult - 1);
-}
-
-
 Loader::Loader(QObject *parent) : QObject(parent),
     m_interval(10),
     m_configUrl(QUrl::fromLocalFile("/etc/fancontrol")),
-    m_error("Success"),
+    m_error(""),
     m_timer(new QTimer(this))
 {
     parseHwmons();
-    
+
     m_timer->setSingleShot(false);
     m_timer->start(1);
-    
+
     connect(m_timer, SIGNAL(timeout()), this, SLOT(updateSensors()));
 }
 
@@ -110,16 +58,21 @@ void Loader::parseHwmons()
     if (hwmonDir.isReadable())
         list = hwmonDir.entryList(QDir::AllEntries | QDir::NoDotAndDotDot);
 
+    else if (hwmonDir.exists())
+    {
+        setError(QString(HWMON_PATH) + " is not readable!");
+        return;
+    }
     else
     {
-        qDebug() << HWMON_PATH << " is not readable!";
+        setError(QString(HWMON_PATH) + " does not exist!");
         return;
     }
-    
+
     QStringList dereferencedList;
     while (!list.isEmpty())
         dereferencedList << QFile::symLinkTarget(hwmonDir.absoluteFilePath(list.takeFirst()));
-    
+
     foreach (Hwmon *hwmon, m_hwmons)
     {
         if (!dereferencedList.contains(hwmon->path()))
@@ -135,7 +88,7 @@ void Loader::parseHwmons()
     foreach (const QString &hwmonPath, dereferencedList)
     {
         bool hwmonExists = false;
-        
+
         foreach (const Hwmon *hwmon, m_hwmons)
         {
             if (hwmon->path() == hwmonPath)
@@ -148,39 +101,87 @@ void Loader::parseHwmons()
         if (!hwmonExists)
         {
             Hwmon *newHwmon = new Hwmon(hwmonPath, this);
-            connect(this, SIGNAL(sensorsUpdateNeeded()), newHwmon, SLOT(updateSensors()));
-            m_hwmons << newHwmon;
-            emit hwmonsChanged();
+            if (newHwmon->isValid())
+            {
+                connect(this, SIGNAL(sensorsUpdateNeeded()), newHwmon, SLOT(updateSensors()));
+                m_hwmons << newHwmon;
+                emit hwmonsChanged();
+            }
+            else
+                delete newHwmon;
         }
     }
 }
 
 PwmFan * Loader::getPwmFan(const QPair<int, int> &indexPair) const
 {
-    if (indexPair.first >= m_hwmons.size() || indexPair.first < 0 || indexPair.second < 0)
-        return Q_NULLPTR;
-    
-    Hwmon *hwmon = m_hwmons.at(indexPair.first);
-    
-    if (indexPair.second >= hwmon->pwmFans().size())
+    Hwmon *hwmon = m_hwmons.value(indexPair.first, Q_NULLPTR);
+
+    if (!hwmon)
         return Q_NULLPTR;
-    
+
     return hwmon->pwmFan(indexPair.second);
 }
 
 Temp * Loader::getTemp(const QPair<int, int> &indexPair) const
 {
-    if (indexPair.first >= m_hwmons.size() || indexPair.first < 0 || indexPair.second < 0)
-        return Q_NULLPTR;
-    
-    Hwmon *hwmon = m_hwmons.at(indexPair.first);
+    Hwmon *hwmon = m_hwmons.value(indexPair.first, Q_NULLPTR);
     
-    if (indexPair.second >= hwmon->temps().size())
+    if (!hwmon)
         return Q_NULLPTR;
-    
+
     return hwmon->temp(indexPair.second);
 }
 
+QPair<int, int> Loader::getEntryNumbers(const QString &entry)
+{
+    if (entry.isEmpty())
+    {
+        qWarning() << "Loader::getHwmonNumber(): given empty string.";
+        return QPair<int, int>(-1, -1);
+    }
+
+    QStringList list = entry.split('/', QString::SkipEmptyParts);
+    if (list.size() != 2)
+    {
+        qWarning() << "Invalid entry to parse:" << entry << "Should contain exactly one \'/\'";
+        return QPair<int, int>(-1, -1);
+    }
+    QString hwmon = list.at(0);
+    QString sensor = list.at(1);
+
+    if (!hwmon.startsWith("hwmon"))
+    {
+        qWarning() << "Invalid entry to parse:" << entry << "Should begin with \"hwmon\"";
+        return QPair<int, int>(-1, -1);
+    }
+    if (!sensor.contains(QRegExp("^(pwm|fan|temp|_input)\\d+")))
+    {
+        qWarning() << "Invalid entry to parse:" << entry << "\n Sensor should begin with  pwm|fan|temp|_input followed by a number";
+        return QPair<int, int>(-1, -1);
+    }
+
+    bool success;
+
+    hwmon.remove("hwmon");
+    sensor.remove(QRegExp("^(pwm|fan|temp|_input)"));
+
+    int hwmonResult = hwmon.toInt(&success);
+    if (!success)
+    {
+        qWarning() << "Invalid entry to parse:" << entry << "Could not convert" << hwmon << "to int";
+        return QPair<int, int>(-1, -1);
+    }
+    int sensorResult = sensor.toInt(&success);
+    if (!success)
+    {
+        qWarning() << "Invalid entry to parse:" << entry << "Could not convert" << sensor << "to int";
+        return QPair<int, int>(-1, -1);
+    }
+
+    return QPair<int, int>(hwmonResult, sensorResult - 1);
+}
+
 void Loader::parseConfigLine(const QString &line, void (PwmFan::*memberSetFunction)(int)) const
 {
     if (!memberSetFunction)
@@ -188,26 +189,27 @@ void Loader::parseConfigLine(const QString &line, void (PwmFan::*memberSetFuncti
         qWarning() << "Loader::parseConfigLine(): Null for member function pointer";
         return;
     }
-    
+
     QStringList entries = line.split(' ');
-    
+
     foreach (const QString &entry, entries)
     {
-        QStringList nameValuePair = entry.split('=');
-        if (nameValuePair.size() == 2)
+        QStringList fanValuePair = entry.split('=');
+        if (fanValuePair.size() == 2)
         {
-            QString fan = nameValuePair.at(0);
+            QString fanString = fanValuePair.at(0);
+            QString valueString = fanValuePair.at(1);
             bool success;
-            int value = nameValuePair.at(1).toInt(&success);
-            
+            int value = valueString.toInt(&success);
+
             if (success)
             {
-                PwmFan *pwmPointer = getPwmFan(getEntryNumbers(fan));
-                if (pwmPointer)
-                    (pwmPointer->*memberSetFunction)(value);
+                PwmFan *fan = getPwmFan(getEntryNumbers(fanString));
+                if (fan)
+                    (fan->*memberSetFunction)(value);
             }
             else
-                qWarning() << nameValuePair.at(1) << "is not an int";
+                qWarning() << valueString << "is not an int";
         }
         else
             qWarning() << "Invalid Entry:" << entry;
@@ -224,13 +226,13 @@ bool Loader::load(const QUrl &url)
     }
     else if (url.isLocalFile())
         fileName = url.toLocalFile();
-    
+
     else
     {
         setError("Url is not a local file");
         return false;
     }
-    
+
     QTextStream stream;
     QFile file(fileName);
     QString fileContent;
@@ -251,7 +253,8 @@ bool Loader::load(const QUrl &url)
         KAuth::ExecuteJob *reply = action.execute();
         if (!reply->exec())
         {
-            setError(reply->errorString());
+            qDebug() << reply->error();
+            setError(reply->errorString() + reply->errorText());
             return false;
         }
         else
@@ -261,7 +264,7 @@ bool Loader::load(const QUrl &url)
     }
     else
     {
-        setError("File does not exist"); 
+        setError("File does not exist");
         return false;
     }
 
@@ -321,7 +324,7 @@ bool Loader::load(const QUrl &url)
                     QString temp = nameValuePair.at(1);
                     PwmFan *pwmPointer = getPwmFan(getEntryNumbers(pwm));
                     Temp *tempPointer = getTemp(getEntryNumbers(temp));
-                    
+
                     if (pwmPointer && tempPointer)
                     {
                         pwmPointer->setTemp(tempPointer);
@@ -362,24 +365,23 @@ bool Loader::load(const QUrl &url)
             line.remove("MAXPWM=");
             parseConfigLine(line, &PwmFan::setMaxPwm);
         }
-        else if (!line.startsWith("DEVNAME=") && 
+        else if (!line.startsWith("DEVNAME=") &&
                  !line.startsWith("DEVPATH=") &&
                  !line.startsWith("FCFANS="))
             qWarning() << "Unrecognized line in config:" << line;
     }
-    
+
     //Connect hwmons again
     foreach (Hwmon *hwmon, m_hwmons)
     {
         connect(hwmon, SIGNAL(configUpdateNeeded()), this, SLOT(createConfigFile()));
     }
-    
+
     emit configUrlChanged();
-    
+
     m_configFile = fileContent;
     emit configFileChanged();
-    
-    success();
+
     return true;
 }
 
@@ -393,15 +395,15 @@ bool Loader::save(const QUrl &url)
     }
     else if (url.isLocalFile())
         fileName = url.toLocalFile();
-    
+
     else
     {
         setError("Url is not a local file");
         return false;
     }
-    
+
     QFile file(fileName);
-    
+
     if (file.open(QFile::WriteOnly | QFile::Text))
     {
         QTextStream stream(&file);
@@ -422,12 +424,12 @@ bool Loader::save(const QUrl &url)
 
         if (!reply->exec())
         {
-            setError(reply->errorString());
+            qDebug() << reply->error();
+            setError(reply->errorString() + reply->errorText());
             return false;
         }
     }
-    
-    success();
+
     return true;
 }
 
@@ -450,7 +452,7 @@ void Loader::createConfigFile()
             }
         }
     }
-    
+
     QString configFile = "# This file was created by Fancontrol-GUI \n";
 
     if (m_interval != 0)
@@ -586,16 +588,17 @@ void Loader::detectSensors()
     action.setHelperId("fancontrol.gui.helper");
     QVariantMap map;
     map["action"] = "detectSensors";
-    
+
     action.setArguments(map);
     KAuth::ExecuteJob *reply = action.execute();
 
     if (!reply->exec())
     {
-        setError(reply->errorString());
+        qDebug() << reply->error();
+        setError(reply->errorString() + reply->errorText());
         return;
-    }    
-    
+    }
+
     parseHwmons();
 }
 
@@ -631,13 +634,9 @@ QList<QObject *> Loader::allTemps() const
 
 void Loader::setError (const QString &error)
 {
-    if (error != m_error) 
-    {
-        m_error = error;
-        emit errorChanged();
-        
-    }
-    qDebug() << error;
+    m_error = error;
+    emit errorChanged();
+    qCritical() << error;
 }
 
-}
+}

+ 4 - 3
lib/src/loader.h

@@ -69,6 +69,8 @@ public:
     void setInterval(int interval, bool writeNewConfig = true);
     QString error() const { return m_error; }
     
+    static QPair<int, int> getEntryNumbers(const QString &entry);
+    
     
 public slots:
 
@@ -80,12 +82,11 @@ protected slots:
     void createConfigFile();
     void emitAllPwmFansChanged() { emit allPwmFansChanged(); }
     void emitAllTempsChanged() { emit allTempsChanged(); }
-
+    void setError(const QString &error);
+        
 
 protected:
     
-    void setError(const QString &error);
-    void success() { setError("Success"); }
     void parseConfigLine(const QString &line, void (PwmFan::*memberSetFunction)(int value)) const;
     
     

+ 9 - 9
lib/src/pwmfan.cpp

@@ -79,7 +79,7 @@ PwmFan::PwmFan(Hwmon *parent, uint index) : Fan(parent, index),
             *m_pwmStream >> m_pwm;
         }
         else
-            qDebug() << "Can't open pwmFile " << pwmFile->fileName();
+            emit errorChanged("Can't open pwmFile " + pwmFile->fileName());
 
         QFile *pwmModeFile = new QFile(parent->path() + "/pwm" + QString::number(index) + "_mode", this);
         if (pwmModeFile->open(QFile::ReadWrite))
@@ -93,7 +93,7 @@ PwmFan::PwmFan(Hwmon *parent, uint index) : Fan(parent, index),
             *m_modeStream >> m_pwmMode;
         }
         else
-            qDebug() << "Can't open pwmModeFile " << pwmModeFile->fileName();
+            emit errorChanged("Can't open pwmModeFile " + pwmModeFile->fileName());
     }
 }
 
@@ -138,7 +138,7 @@ void PwmFan::reset()
             *m_pwmStream >> m_pwm;
         }
         else
-            qDebug() << "Can't open pwmFile " << pwmFile->fileName();
+            emit errorChanged("Can't open pwmFile " + pwmFile->fileName());
 
         QFile *pwmModeFile = new QFile(m_parent->path() + "/pwm" + QString::number(m_index) + "_mode", this);
         if (pwmModeFile->open(QFile::ReadWrite))
@@ -152,7 +152,7 @@ void PwmFan::reset()
             *m_modeStream >> m_pwmMode;
         }
         else
-            qDebug() << "Can't open pwmModeFile " << pwmModeFile->fileName();
+            emit errorChanged("Can't open pwmModeFile " + pwmModeFile->fileName());
 }
 
 bool PwmFan::setPwm(int pwm, bool write)
@@ -172,7 +172,7 @@ bool PwmFan::setPwm(int pwm, bool write)
                 action.setHelperId("fancontrol.gui.helper");
                 if (!action.isValid())
                 {
-                    qDebug() << "setPwm action is invalid";
+                    emit errorChanged("Helper error");
                     return false;
                 }
                 
@@ -185,7 +185,7 @@ bool PwmFan::setPwm(int pwm, bool write)
 
                 if (!reply->exec())
                 {
-                    qDebug() << "setPwm error:" << reply->errorString() << reply->errorText();
+                    qWarning() << "setPwm error:" << reply->errorString() << reply->errorText();
                     return false;
                 }
             }
@@ -212,7 +212,7 @@ bool PwmFan::setPwmMode(int pwmMode, bool write)
                 action.setHelperId("fancontrol.gui.helper");
                 if (!action.isValid())
                 {
-                    qDebug() << "setPwmMode action is invalid";
+                    emit errorChanged("Helper error");
                     return false;
                 }
                 
@@ -225,7 +225,7 @@ bool PwmFan::setPwmMode(int pwmMode, bool write)
 
                 if (!reply->exec())
                 {
-                    qDebug() << "setPwmMode error:" << reply->errorString() << reply->errorText();
+                    qWarning() << "setPwmMode error:" << reply->errorString() << reply->errorText();
                     return false;
                 }
             }
@@ -246,7 +246,7 @@ bool PwmFan::test()
     }
     else
     {
-        qDebug() << "Testing failed";
+        emit errorChanged("Testing failed");
         return false;
     }
     

+ 0 - 2
lib/src/pwmfan.h

@@ -103,8 +103,6 @@ protected slots:
 
     void update();
     void continueTest();
-//     void handlePwmActionReply();
-//     void handlePwmModeActionReply();
 
 
 private:

+ 1 - 0
lib/src/sensor.cpp

@@ -31,6 +31,7 @@ Sensor::Sensor(Hwmon *parent, uint index, const QString &path) : QObject(parent)
     m_index(index),
     m_path(path)
 {
+    connect(this, SIGNAL(errorChanged(QString)), parent, SLOT(setError(QString)));
 }
 
 }

+ 1 - 0
lib/src/sensor.h

@@ -58,6 +58,7 @@ public slots:
 signals:
 
     void nameChanged();
+    void errorChanged(QString);
 
 
 protected:

+ 1 - 3
lib/src/systemdcommunicator.cpp

@@ -59,7 +59,7 @@ namespace Fancontrol
 {
 
 SystemdCommunicator::SystemdCommunicator(const QString &serviceName, QObject *parent) : QObject(parent),
-    m_error("Success"),
+    m_error(""),
     m_managerInterface(new QDBusInterface("org.freedesktop.systemd1",
                                           "/org/freedesktop/systemd1",
                                           "org.freedesktop.systemd1.Manager",
@@ -264,7 +264,6 @@ bool SystemdCommunicator::dbusAction(const QString &method, const QVariantList &
             }
             else
             {
-                success();
                 return true;
             }
         }
@@ -272,7 +271,6 @@ bool SystemdCommunicator::dbusAction(const QString &method, const QVariantList &
         return false;
     }
     
-    success();
     return true;
 }
 

+ 0 - 1
lib/src/systemdcommunicator.h

@@ -71,7 +71,6 @@ protected:
     
     bool dbusAction(const QString &method, const QVariantList &arguments = QVariantList());
     void setError(const QString &error) { if (error != m_error) { m_error = error; emit errorChanged(); } }
-    void success() { setError("Success"); }
     
     
 private:

+ 2 - 6
lib/src/temp.cpp

@@ -26,7 +26,6 @@
 #include <QtCore/QTextStream>
 #include <QtCore/QFile>
 #include <QtCore/QDir>
-#include <QtCore/QDebug>
 
 #include <KConfigCore/KSharedConfig>
 #include <KConfigCore/KConfigGroup>
@@ -53,13 +52,10 @@ Temp::Temp(Hwmon *parent, uint index) :
             m_value /= 1000;
         }
         else
-            qDebug() << "Can't open valueFile " << parent->path() + "/temp" + QString::number(index) + "_input";
+            emit errorChanged("Can't open valueFile " + parent->path() + "/temp" + QString::number(index) + "_input");
 
         if (labelFile.open(QFile::ReadOnly))
             m_label = QTextStream(&labelFile).readLine();
-
-        else
-            qDebug() << "Can't open labelFile " << parent->path() + "/temp" + QString::number(index) + "_label";
     }
 }
 
@@ -111,7 +107,7 @@ void Temp::reset()
             m_value /= 1000;
         }
         else
-            qDebug() << "Can't open valueFile " << m_parent->path() + "/temp" + QString::number(m_index) + "_input";
+            emit errorChanged("Can't open valueFile " + m_parent->path() + "/temp" + QString::number(m_index) + "_input");
     }
 }
 

+ 8 - 0
package/contents/ui/Application.qml

@@ -109,6 +109,14 @@ ApplicationWindow {
         }
     }
     
+    ErrorDialog {
+        id: errorDialog
+        visible: !!base.loader.error
+        modality: Qt.ApplicationModal
+        text: base.loader.error
+        onTextChanged: show()
+    }
+    
     Action {
         id: loadAction
         text: i18n("Load configuration file")

+ 35 - 0
package/contents/ui/ErrorDialog.qml

@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2015  Malte Veerman <maldela@halloarsch.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+import QtQuick 2.4
+import QtQuick.Dialogs 1.2
+import QtQuick.Controls 1.2
+
+Dialog {
+    property alias text: text.text
+    
+    title: i18n("Error")
+    standardButtons: StandardButton.Ok
+    onAccepted: close()
+    
+    Label {
+        id: text
+        anchors.centerIn: parent
+    }
+}

+ 8 - 0
package/contents/ui/KCM.qml

@@ -228,4 +228,12 @@ Item {
             }
         }
     }
+    
+    ErrorDialog {
+        id: errorDialog
+        visible: !!base.loader.error
+        modality: Qt.ApplicationModal
+        text: base.loader.error
+        onTextChanged: show()
+    }
 }