[prev in list] [next in list] [prev in thread] [next in thread]
List: kde-commits
Subject: [kdelibs/frameworks] /: Merge remote-tracking branch 'origin/master' into frameworks
From: David Faure <faure+bluesystems () kde ! org>
Date: 2013-03-23 15:27:40
Message-ID: 20130323152740.D398BA604F () git ! kde ! org
[Download RAW message or body]
Git commit 465381c3e322ed15038e0d54f2cb0597ab9938df by David Faure.
Committed on 23/03/2013 at 16:13.
Pushed by dfaure into branch 'frameworks'.
Merge remote-tracking branch 'origin/master' into frameworks
Conflicts:
kio/kio/accessmanagerreply_p.cpp [qDebug]
knewstuff/knewstuff3/uploaddialog.cpp [QDialog, moved ported code...]
solid/solid/backends/udisks2/udisksdevicebackend.cpp [moved by hand to tier1]
solid/solid/backends/udisks2/udisksdevicebackend.h [moved by hand to tier1]
tier1/solid/src/solid/CMakeLists.txt [hupnp detection, quite different...]
M +14 -0 khtml/imload/decoders/gifloader.cpp
M +1 -2 khtml/java/kjavaappletwidget.cpp
M +4 -1 khtml/khtml_part.cpp
M +2 -1 kimgio/eps.cpp
M +2 -2 kio/kio/accessmanagerreply_p.cpp
M +1 -1 kio/kio/kfileitemactions.cpp
M +2 -1 kio/kssl/ksslcertificate.cpp
M +42 -0 kio/tests/kdirlistertest.cpp
M +1 -0 kio/tests/kdirlistertest.h
M +11 -0 knewstuff/knewstuff3/upload/atticahelper.cpp
M +53 -44 knewstuff/knewstuff3/uploaddialog.cpp
M +1 -1 knewstuff/knewstuff3/uploaddialog_p.h
A +8 -0 tier1/kcoreaddons/src/io/kdirwatch.cpp [License: LGPL (v2)]
A +24 -12 tier1/solid/src/solid/CMakeLists.txt
A +5 -4 tier1/solid/src/solid/backends/udev/udevaudiointerface.cpp \
[License: LGPL] A +1 -3 tier1/solid/src/solid/backends/udev/udevmanager.cpp \
[License: LGPL] A +4 -8 tier1/solid/src/solid/backends/udisks/udisksdevice.cpp \
[License: LGPL] R +2 -0 tier1/solid/src/solid/backends/udisks/udisksdevice.h
A +239 -0 tier1/solid/src/solid/backends/udisks2/udisksdevicebackend.cpp \
[License: LGPL] A +84 -0 \
tier1/solid/src/solid/backends/udisks2/udisksdevicebackend.h [License: LGPL] R \
+25 -0 tier1/solid/src/solid/backends/udisks2/udisksmanager.cpp R +1 -0 \
tier1/solid/src/solid/backends/udisks2/udisksmanager.h A +11 -1 \
tier2/kconfig/autotests/kconfig_compiler/CMakeLists.txt R +22 -0 \
tier2/kconfig/autotests/kconfig_compiler/test12.cpp.ref R +35 -0 \
tier2/kconfig/autotests/kconfig_compiler/test12.h.ref R +10 -0 \
tier2/kconfig/autotests/kconfig_compiler/test12.kcfg R +1 -0 \
tier2/kconfig/autotests/kconfig_compiler/test12.kcfgc R +30 -0 \
tier2/kconfig/autotests/kconfig_compiler/test12main.cpp A +5 -1 \
tier2/kconfig/src/kconfig_compiler/kconfig_compiler.cpp [License: GENERATED FILE] \
*
The files marked with a * at the end have a non valid license. Please read: \
http://techbase.kde.org/Policies/Licensing_Policy and use the headers which are \
listed at that page.
http://commits.kde.org/kdelibs/465381c3e322ed15038e0d54f2cb0597ab9938df
diff --cc kio/tests/kdirlistertest.cpp
index b0bb86f,a0b296d..946338d
--- a/kio/tests/kdirlistertest.cpp
+++ b/kio/tests/kdirlistertest.cpp
@@@ -1062,11 -1052,52 +1062,53 @@@ void KDirListerTest::testWatchingAfterC
}
QCOMPARE(m_items.count(), 1);
- newDir.unlink();
- QVERIFY(QTest::kWaitForSignal(&m_dirLister, SIGNAL(clear()), 1000));
+ newDir.remove();
+ QSignalSpy clearSpy(&m_dirLister, SIGNAL(clear()));
+ QVERIFY(clearSpy.wait(1000));
}
+ void KDirListerTest::testRemoveWatchedDirectory()
+ {
+ m_items.clear();
+
+ KTempDir newDir;
+ const QString path = newDir.name();
+
+ // List and watch an empty dir
+ connect(&m_dirLister, SIGNAL(newItems(KFileItemList)), this, \
SLOT(slotNewItems(KFileItemList))); + m_dirLister.openUrl(KUrl(path));
+ QVERIFY(QTest::kWaitForSignal(&m_dirLister, SIGNAL(completed()), 1000));
+ QVERIFY(m_dirLister.isFinished());
+ QVERIFY(m_items.isEmpty());
+
+ // Create a subfolder.
+ const QString subDirPath = path + "abc";
+ QVERIFY(QDir().mkdir(subDirPath));
+
+ QVERIFY(QTest::kWaitForSignal(&m_dirLister, SIGNAL(completed()), 1000));
+ QVERIFY(m_dirLister.isFinished());
+ QCOMPARE(m_items.count(), 1);
+ const KFileItem item = m_items.at(0);
+
+ // Watch the subfolder for changes, independently.
+ // This is what triggers the bug.
+ // (Technically, this could become a KDirWatch unittest, but if one day we use \
QFSW, good to have the tests here) + KDirWatch watcher;
+ watcher.addDir(subDirPath);
+
+ // Remove the subfolder.
+ m_items.clear();
+ QVERIFY(QDir().rmdir(path + "abc"));
+
+ // This should trigger an update.
+ QVERIFY(QTest::kWaitForSignal(&m_dirLister, SIGNAL(completed()), 1000));
+ QVERIFY(m_dirLister.isFinished());
+ QCOMPARE(m_items.count(), 0);
+ QCOMPARE(m_dirLister.spyItemsDeleted.count(), 1);
+ const KFileItem deletedItem = \
m_dirLister.spyItemsDeleted.at(0).at(0).value<KFileItemList>().at(0); + \
QCOMPARE(item, deletedItem); + }
+
void KDirListerTest::enterLoop(int exitCount)
{
//qDebug("enterLoop");
diff --cc knewstuff/knewstuff3/uploaddialog.cpp
index 640e8d8,922469e..2e0989a
--- a/knewstuff/knewstuff3/uploaddialog.cpp
+++ b/knewstuff/knewstuff3/uploaddialog.cpp
@@@ -41,35 -40,58 +41,82 @@@
using namespace KNS3;
- void UploadDialog::Private::init()
+ bool UploadDialog::Private::init(const QString& configfile)
{
+ QVBoxLayout *layout = new QVBoxLayout;
+ q->setLayout(layout);
+
QWidget* _mainWidget = new QWidget(q);
- q->setMainWidget(_mainWidget);
ui.setupUi(_mainWidget);
++
+ layout->addWidget(_mainWidget);
+
+ backButton = new QPushButton;
+ KGuiItem::assign(backButton, KStandardGuiItem::back(KStandardGuiItem::UseRTL));
+
+ nextButton = new QPushButton;
+ nextButton->setText(i18nc("Opposite to Back", "Next"));
+ nextButton->setIcon(KStandardGuiItem::forward(KStandardGuiItem::UseRTL).icon());
+ nextButton->setDefault(true);
+
+ finishButton = new QPushButton;
+ finishButton->setText(i18n("Finish"));
+ finishButton->setIcon(QIcon::fromTheme("dialog-ok-apply"));
+
+ buttonBox = new QDialogButtonBox(q);
+ buttonBox->addButton(backButton, QDialogButtonBox::ActionRole);
+ buttonBox->addButton(nextButton, QDialogButtonBox::ActionRole);
+ buttonBox->addButton(finishButton, QDialogButtonBox::AcceptRole);
+ buttonBox->setStandardButtons(QDialogButtonBox::Cancel);
+ layout->addWidget(buttonBox);
+
atticaHelper = new AtticaHelper(q);
+
+ bool success = true;
+ KConfig conf(configfile);
+ if (conf.accessMode() == KConfig::NoAccess) {
+ kError() << "No knsrc file named '" << configfile << "' was found." << \
endl; + success = false;
+ }
+ // FIXME: accessMode() doesn't return NoAccess for non-existing files
+ // - bug in kdecore?
+ // - this needs to be looked at again until KConfig backend changes for KDE 4
+ // the check below is a workaround
- if (KStandardDirs::locate("config", configfile).isEmpty()) {
++ if (QStandardPaths::locate(QStandardPaths::ConfigLocation, \
configfile).isEmpty()) { + kError() << "No knsrc file named '" << configfile \
<< "' was found." << endl; + success = false;
+ }
+
+ KConfigGroup group;
+ if (conf.hasGroup("KNewStuff3")) {
+ kDebug() << "Loading KNewStuff3 config: " << configfile;
+ group = conf.group("KNewStuff3");
+ } else {
+ kError() << "A knsrc file was found but it doesn't contain a KNewStuff3 \
section." << endl; + success = false;
+ }
+
+ if ( success ) {
+ const QString providersFileUrl = group.readEntry("ProvidersUrl", \
QString()); +
+ categoryNames = group.readEntry("UploadCategories", QStringList());
+ // fall back to download categories
+ if (categoryNames.isEmpty()) {
+ categoryNames = group.readEntry("Categories", QStringList());
+ }
+
+ atticaHelper->addProviderFile(QUrl(providersFileUrl));
+ }
+
+ ui.mCategoryCombo->addItems(categoryNames);
+
+ if (categoryNames.size() == 1) {
+ ui.mCategoryLabel->setVisible(false);
+ ui.mCategoryCombo->setVisible(false);
+ }
+
+ kDebug() << "Categories: " << categoryNames;
+
q->connect(atticaHelper, SIGNAL(providersLoaded(QStringList)), q, \
SLOT(_k_providersLoaded(QStringList)));
q->connect(atticaHelper, SIGNAL(loginChecked(bool)), q, \
SLOT(_k_checkCredentialsFinished(bool)));
q->connect(atticaHelper, SIGNAL(licensesLoaded(Attica::License::List)), q, \
SLOT(_k_licensesLoaded(Attica::License::List))); @@@ -381,10 -401,18 +426,10 @@@ \
UploadDialog::~UploadDialog(
bool UploadDialog::init(const QString &configfile)
{
- d->init();
+ bool success = d->init(configfile);
- setCaption(i18n("Share Hot New Stuff"));
+ setWindowTitle(i18n("Share Hot New Stuff"));
- setButtons(KDialog::Cancel | KDialog::User1 | KDialog::User2 | KDialog::User3 | \
KDialog::Help);
- setButtonGuiItem( BackButton, KStandardGuiItem::back(KStandardGuiItem::UseRTL) \
);
-
- setButtonText( NextButton, i18nc("Opposite to Back", "Next") );
- setButtonIcon( NextButton, \
KStandardGuiItem::forward(KStandardGuiItem::UseRTL).icon() );
- setButtonText(FinishButton, i18n("Finish"));
- setButtonIcon( FinishButton, KIcon("dialog-ok-apply") );
- setDefaultButton(NextButton);
d->_k_updatePage();
connect(d->ui.username, SIGNAL(textChanged(QString)), this, \
SLOT(_k_updatePage())); @@@ -396,63 -424,23 +441,27 @@@
connect(d->ui.uploadButton, SIGNAL(clicked()), this, SLOT(_k_startUpload()));
- connect(this, SIGNAL(user3Clicked()), this, SLOT(_k_backPage()));
- connect(this, SIGNAL(user2Clicked()), this, SLOT(_k_nextPage()));
- connect(this, SIGNAL(user1Clicked()), this, SLOT(accept()));
+ connect(d->backButton, SIGNAL(clicked()), this, SLOT(_k_backPage()));
+ connect(d->nextButton, SIGNAL(clicked()), this, SLOT(_k_nextPage()));
+ connect(d->buttonBox, SIGNAL(accepted()), this, SLOT(accept()));
+ connect(d->buttonBox, SIGNAL(rejected()), this, SLOT(reject()));
+ QString displayName = QGuiApplication::applicationDisplayName();
+ if (displayName.isEmpty())
+ displayName = QCoreApplication::applicationName();
d->ui.mTitleWidget->setText(i18nc("Program name followed by 'Add On Uploader'",
"%1 Add-On Uploader",
- \
KGlobal::activeComponent().aboutData()->programName()));
- d->ui.mTitleWidget->setPixmap(KIcon(KGlobal::activeComponent().aboutData()->programIconName()));
+ displayName));
+ //d->ui.mTitleWidget->setPixmap(QIcon::fromTheme(KGlobal::activeComponent().aboutData()->programIconName()));
- KConfig conf(configfile);
- if (conf.accessMode() == KConfig::NoAccess) {
- kError() << "No knsrc file named '" << configfile << "' was found." << \
endl;
- return false;
- }
- // FIXME: accessMode() doesn't return NoAccess for non-existing files
- // - bug in kdecore?
- // - this needs to be looked at again until KConfig backend changes for KDE 4
- // the check below is a workaround
- if (QStandardPaths::locate(QStandardPaths::ConfigLocation, \
configfile).isEmpty()) {
- kError() << "No knsrc file named '" << configfile << "' was found." << \
endl;
- return false;
+ if ( success ) {
+ d->_k_showPage(0);
}
- KConfigGroup group;
- if (conf.hasGroup("KNewStuff3")) {
- kDebug() << "Loading KNewStuff3 config: " << configfile;
- group = conf.group("KNewStuff3");
- } else {
- kError() << "A knsrc file was found but it doesn't contain a KNewStuff3 \
section." << endl;
- return false;
- }
-
- d->categoryNames = group.readEntry("UploadCategories", QStringList());
- // fall back to download categories
- if (d->categoryNames.isEmpty()) {
- d->categoryNames = group.readEntry("Categories", QStringList());
- }
-
- d->ui.mCategoryCombo->addItems(d->categoryNames);
-
- if (d->categoryNames.size() == 1) {
- d->ui.mCategoryLabel->setVisible(false);
- d->ui.mCategoryCombo->setVisible(false);
- }
-
- kDebug() << "Categories: " << d->categoryNames;
-
- d->_k_showPage(0);
-
- return true;
+ return success;
}
-void UploadDialog::setUploadFile(const KUrl& payloadFile)
+void UploadDialog::setUploadFile(const QUrl & payloadFile)
{
d->uploadFile = payloadFile;
diff --cc tier1/kcoreaddons/src/io/kdirwatch.cpp
index 8687808,0000000..ddbc8f5
mode 100644,000000..100644
--- a/tier1/kcoreaddons/src/io/kdirwatch.cpp
+++ b/tier1/kcoreaddons/src/io/kdirwatch.cpp
@@@ -1,1915 -1,0 +1,1923 @@@
+/* This file is part of the KDE libraries
+ Copyright (C) 1998 Sven Radej <sven@lisa.exp.univie.ac.at>
+ Copyright (C) 2006 Dirk Mueller <mueller@kde.org>
+ Copyright (C) 2007 Flavio Castelli <flavio.castelli@gmail.com>
+ Copyright (C) 2008 Rafal Rzepecki <divided.mind@gmail.com>
+ Copyright (C) 2010 David Faure <faure@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.
+*/
+
+
+// CHANGES:
+// Jul 30, 2008 - Don't follow symlinks when recursing to avoid loops (Rafal)
+// Aug 6, 2007 - KDirWatch::WatchModes support complete, flags work fine also
+// when using FAMD (Flavio Castelli)
+// Aug 3, 2007 - Handled KDirWatch::WatchModes flags when using inotify, now
+// recursive and file monitoring modes are implemented (Flavio Castelli)
+// Jul 30, 2007 - Substituted addEntry boolean params with KDirWatch::WatchModes
+// flag (Flavio Castelli)
+// Oct 4, 2005 - Inotify support (Dirk Mueller)
+// Februar 2002 - Add file watching and remote mount check for STAT
+// Mar 30, 2001 - Native support for Linux dir change notification.
+// Jan 28, 2000 - Usage of FAM service on IRIX (Josef.Weidendorfer@in.tum.de)
+// May 24. 1998 - List of times introduced, and some bugs are fixed. (sven)
+// May 23. 1998 - Removed static pointer - you can have more instances.
+// It was Needed for KRegistry. KDirWatch now emits signals and doesn't
+// call (or need) KFM. No more URL's - just plain paths. (sven)
+// Mar 29. 1998 - added docs, stop/restart for particular Dirs and
+// deep copies for list of dirs. (sven)
+// Mar 28. 1998 - Created. (sven)
+
+#include "kdirwatch.h"
+#include "kdirwatch_p.h"
+#include "kfilesystemtype_p.h"
+
+#include <io/config-kdirwatch.h>
+
+#include <sys/stat.h>
+#include <assert.h>
+#include <errno.h>
+#include <QtCore/QDebug>
+#include <QtCore/QDir>
+#include <QtCore/QFile>
+#include <QtCore/QSocketNotifier>
+#include <QtCore/QTimer>
+#include <QtCore/QCoreApplication>
+
+#include <qplatformdefs.h> // QT_LSTAT, QT_STAT, QT_STATBUF
+
+#include <stdlib.h>
+#include <string.h>
+
+// debug
+#include <sys/ioctl.h>
+
+
+#include <sys/utsname.h>
+
+// set this to true for much more verbose debug output
+static const bool s_verboseDebug = false;
+
+// The KDirWatchPrivate instance is refcounted, and deleted by the last KDirWatch \
instance +static KDirWatchPrivate* dwp_self = 0;
+static KDirWatchPrivate* createPrivate() {
+ if (!dwp_self)
+ dwp_self = new KDirWatchPrivate;
+ return dwp_self;
+}
+
+// Convert a string into a watch Method
+static KDirWatch::Method methodFromString(const QString& method) {
+ if (method == QLatin1String("Fam")) {
+ return KDirWatch::FAM;
+ } else if (method == QLatin1String("Stat")) {
+ return KDirWatch::Stat;
+ } else if (method == QLatin1String("QFSWatch")) {
+ return KDirWatch::QFSWatch;
+ } else {
+#ifdef Q_OS_LINUX
+ // inotify supports delete+recreate+modify, which QFSWatch doesn't support
+ return KDirWatch::INotify;
+#else
+ return KDirWatch::QFSWatch;
+#endif
+ }
+}
+
+#ifndef NDEBUG
+static const char* methodToString(KDirWatch::Method method)
+{
+ switch (method) {
+ case KDirWatch::FAM:
+ return "Fam";
+ case KDirWatch::INotify:
+ return "INotify";
+ case KDirWatch::DNotify:
+ return "DNotify";
+ case KDirWatch::Stat:
+ return "Stat";
+ case KDirWatch::QFSWatch:
+ return "QFSWatch";
+ default:
+ return "ERROR!";
+ }
+}
+#endif
+
+//
+// Class KDirWatchPrivate (singleton)
+//
+
+/* All entries (files/directories) to be watched in the
+ * application (coming from multiple KDirWatch instances)
+ * are registered in a single KDirWatchPrivate instance.
+ *
+ * At the moment, the following methods for file watching
+ * are supported:
+ * - Polling: All files to be watched are polled regularly
+ * using stat (more precise: QFileInfo.lastModified()).
+ * The polling frequency is determined from global kconfig
+ * settings, defaulting to 500 ms for local directories
+ * and 5000 ms for remote mounts
+ * - FAM (File Alternation Monitor): first used on IRIX, SGI
+ * has ported this method to LINUX. It uses a kernel part
+ * (IMON, sending change events to /dev/imon) and a user
+ * level damon (fam), to which applications connect for
+ * notification of file changes. For NFS, the fam damon
+ * on the NFS server machine is used; if IMON is not built
+ * into the kernel, fam uses polling for local files.
+ * - INOTIFY: In LINUX 2.6.13, inode change notification was
+ * introduced. You're now able to watch arbitrary inode's
+ * for changes, and even get notification when they're
+ * unmounted.
+ */
+
+KDirWatchPrivate::KDirWatchPrivate()
+ : timer(),
+ freq( 3600000 ), // 1 hour as upper bound
+ statEntries( 0 ),
+ m_ref( 0 ),
+ delayRemove( false ),
+ rescan_all( false ),
+ rescan_timer()
+{
+ timer.setObjectName(QLatin1String("KDirWatchPrivate::timer"));
+ connect (&timer, SIGNAL(timeout()), this, SLOT(slotRescan()));
+
+#pragma message("KDE5 TODO: Remove dependencies on Kconfig and KGlobal")
+#if 0
+ KConfigGroup config(KSharedConfig::openConfig(), "DirWatch");
+ m_nfsPollInterval = config.readEntry("NFSPollInterval", 5000);
+ m_PollInterval = config.readEntry("PollInterval", 500);
+
+ QString method = config.readEntry("PreferredMethod", "inotify");
+ m_preferredMethod = methodFromString(method);
+
+ // The nfs method defaults to the normal (local) method
+ m_nfsPreferredMethod = methodFromString(config.readEntry("nfsPreferredMethod", \
"Fam")); +#endif
+#pragma message("KDE5 FIXME: We use the default values until the todo above is \
fixed") + m_nfsPollInterval = 5000;
+ m_PollInterval = 500;
+ m_preferredMethod = methodFromString(QLatin1String("inotify"));
+ m_nfsPreferredMethod = methodFromString(QLatin1String("Fam"));
+
+ QList<QByteArray> availableMethods;
+
+ availableMethods << "Stat";
+
+ // used for FAM and inotify
+ rescan_timer.setObjectName(QString::fromLatin1("KDirWatchPrivate::rescan_timer"));
+ rescan_timer.setSingleShot( true );
+ connect(&rescan_timer, SIGNAL(timeout()), this, SLOT(slotRescan()));
+
+#if HAVE_FAM
+ // It's possible that FAM server can't be started
+ if (FAMOpen(&fc) ==0) {
+ availableMethods << "FAM";
+ use_fam=true;
+ sn = new QSocketNotifier( FAMCONNECTION_GETFD(&fc),
+ QSocketNotifier::Read, this);
+ connect( sn, SIGNAL(activated(int)),
+ this, SLOT(famEventReceived()) );
+ }
+ else {
+ qDebug() << "Can't use FAM (fam daemon not running?)";
+ use_fam=false;
+ }
+#endif
+
+#if HAVE_SYS_INOTIFY_H
+ supports_inotify = true;
+
+ m_inotify_fd = inotify_init();
+
+ if ( m_inotify_fd <= 0 ) {
+ qDebug() << "Can't use Inotify, kernel doesn't support it";
+ supports_inotify = false;
+ }
+
+ {
+ struct utsname uts;
+ int major, minor, patch;
+ if (uname(&uts) < 0) {
+ supports_inotify = false;
+ qDebug() << "Unable to get uname";
+ } else if (sscanf(uts.release, "%d.%d", &major, &minor) != 2) {
+ supports_inotify = false;
+ qDebug() << "The version is malformed: " << uts.release;
+ } else if(major == 2 && minor == 6) { // If it is 2.6 check further...
+ if (sscanf(uts.release, "%d.%d.%d", &major, &minor, &patch) != 3) {
+ supports_inotify = false;
+ qDebug() << "Detected 2.6 kernel but can't know more: " << uts.release;
+ } else if (major * 1000000 + minor * 1000 + patch < 2006014 ){
+ supports_inotify = false;
+ qDebug() << "Can't use INotify, Linux kernel too old " << uts.release;
+ }
+ }
+ }
+
+ //qDebug() << "INotify available: " << supports_inotify;
+ if ( supports_inotify ) {
+ availableMethods << "INotify";
+ (void)fcntl(m_inotify_fd, F_SETFD, FD_CLOEXEC);
+
+ mSn = new QSocketNotifier( m_inotify_fd, QSocketNotifier::Read, this );
+ connect( mSn, SIGNAL(activated(int)),
+ this, SLOT(inotifyEventReceived()) );
+ }
+#endif
+#if HAVE_QFILESYSTEMWATCHER
+ availableMethods << "QFileSystemWatcher";
+ fsWatcher = 0;
+#endif
+#ifndef NDEBUG
+ qDebug() << "Available methods: " << availableMethods << "preferred=" << \
methodToString(m_preferredMethod); +#endif
+}
+
+// This is called on app exit (when K_GLOBAL_STATIC deletes KDirWatch::self)
+KDirWatchPrivate::~KDirWatchPrivate()
+{
+ timer.stop();
+
+ /* remove all entries being watched */
+ removeEntries(0);
+
+#if HAVE_FAM
+ if (use_fam) {
+ FAMClose(&fc);
+ }
+#endif
+#if HAVE_SYS_INOTIFY_H
+ if ( supports_inotify )
+ ::close( m_inotify_fd );
+#endif
+#if HAVE_QFILESYSTEMWATCHER
+ delete fsWatcher;
+#endif
+}
+
+void KDirWatchPrivate::inotifyEventReceived()
+{
+ //kDebug(7001);
+#if HAVE_SYS_INOTIFY_H
+ if ( !supports_inotify )
+ return;
+
+ int pending = -1;
+ int offsetStartRead = 0; // where we read into buffer
+ char buf[8192];
+ assert( m_inotify_fd > -1 );
+ ioctl( m_inotify_fd, FIONREAD, &pending );
+
+ while ( pending > 0 ) {
+
+ const int bytesToRead = qMin( pending, (int)sizeof( buf ) - offsetStartRead );
+
+ int bytesAvailable = read( m_inotify_fd, &buf[offsetStartRead], bytesToRead );
+ pending -= bytesAvailable;
+ bytesAvailable += offsetStartRead;
+ offsetStartRead = 0;
+
+ int offsetCurrent = 0;
+ while ( bytesAvailable >= (int)sizeof( struct inotify_event ) ) {
+ const struct inotify_event * const event = (struct inotify_event *) \
&buf[offsetCurrent]; + const int eventSize = sizeof( struct inotify_event ) + \
event->len; + if ( bytesAvailable < eventSize ) {
+ break;
+ }
+
+ bytesAvailable -= eventSize;
+ offsetCurrent += eventSize;
+
+ QString path;
+ QByteArray cpath(event->name, event->len);
+ if(event->len)
+ path = QFile::decodeName ( cpath );
+
+ if ( path.length() && isNoisyFile( cpath.data() ) )
+ continue;
+
+ // now we're in deep trouble of finding the
+ // associated entries
+ // for now, we suck and iterate
+ for ( EntryMap::Iterator it = m_mapEntries.begin();
+ it != m_mapEntries.end(); ) {
+ Entry* e = &( *it );
+ ++it;
+ if ( e->wd == event->wd ) {
+ e->dirty = true;
+
+ //if (s_verboseDebug) {
+ // kDebug(7001) << "got event" << "0x"+QString::number(event->mask, 16) \
<< "for" << e->path; + //}
+
+ if( event->mask & IN_DELETE_SELF) {
+ if (s_verboseDebug) {
+ qDebug() << "-->got deleteself signal for" << e->path;
+ }
+ e->m_status = NonExistent;
+ e->wd = -1;
+ e->m_ctime = invalid_ctime;
+ emitEvent(e, Deleted, e->path);
++ // If the parent dir was already watched, tell it something changed
++ Entry* parentEntry = entry(e->parentDirectory());
++ if (parentEntry)
++ parentEntry->dirty = true;
+ // Add entry to parent dir to notice if the entry gets recreated
+ addEntry(0, e->parentDirectory(), e, true /*isDir*/);
+ }
+ if ( event->mask & IN_IGNORED ) {
+ // Causes bug #207361 with kernels 2.6.31 and 2.6.32!
+ //e->wd = -1;
+ }
+ if ( event->mask & (IN_CREATE|IN_MOVED_TO) ) {
+ const QString tpath = e->path + QLatin1Char('/') + path;
+ Entry* sub_entry = e->findSubEntry(tpath);
+
+ if (s_verboseDebug) {
+ qDebug() << "-->got CREATE signal for" << (tpath) << "sub_entry=" << \
sub_entry; + qDebug() << *e;
+ }
+
+ // The code below is very similar to the one in checkFAMEvent...
+ if (sub_entry) {
+ // We were waiting for this new file/dir to be created
+ sub_entry->dirty = true;
+ rescan_timer.start(0); // process this asap, to start watching that \
dir + } else if (e->isDir && !e->m_clients.empty()) {
+ bool isDir = false;
+ const QList<Client *> clients = e->clientsForFileOrDir(tpath, \
&isDir); + Q_FOREACH(Client *client, clients) {
+ // See discussion in addEntry for why we don't addEntry for \
individual + // files in WatchFiles mode with inotify.
+ if (isDir) {
+ addEntry(client->instance, tpath, 0, isDir,
+ isDir ? client->m_watchModes : KDirWatch::WatchDirOnly);
+ }
+ }
+ if (!clients.isEmpty()) {
+ emitEvent(e, Created, tpath);
+ qDebug().nospace() << clients.count() << " instance(s) monitoring \
the new " + << (isDir ? "dir " : "file ") << \
tpath; + }
+ e->m_pendingFileChanges.append(e->path);
+ if (!rescan_timer.isActive())
+ rescan_timer.start(m_PollInterval); // singleshot
+ }
+ }
+ if (event->mask & (IN_DELETE|IN_MOVED_FROM)) {
+ const QString tpath = e->path + QLatin1Char('/') + path;
+ if (s_verboseDebug) {
+ qDebug() << "-->got DELETE signal for" << tpath;
+ }
+ if ((e->isDir) && (!e->m_clients.empty())) {
+ Client* client = 0;
+ // A file in this directory has been removed. It wasn't an \
explicitly + // watched file as it would have its own watch descriptor, \
so + // no addEntry/ removeEntry bookkeeping should be required. Emit
+ // the event immediately if any clients are interested.
+ QT_STATBUF stat_buf;
+ // Unlike clientsForFileOrDir, the stat can fail here (item deleted),
+ // so in that case we'll just take both kinds of clients and emit \
Deleted. + KDirWatch::WatchModes flag = KDirWatch::WatchSubDirs | \
KDirWatch::WatchFiles; + if \
(QT_STAT(QFile::encodeName(tpath).constData(), &stat_buf) == 0) { + \
bool isDir = S_ISDIR(stat_buf.st_mode); + flag = isDir ? \
KDirWatch::WatchSubDirs : KDirWatch::WatchFiles; + }
+ int counter = 0;
+ Q_FOREACH(client, e->m_clients) { // krazy:exclude=Q_FOREACH
+ if (client->m_watchModes & flag) {
+ counter++;
+ }
+ }
+ if (counter != 0) {
+ emitEvent(e, Deleted, tpath);
+ }
+ }
+ }
+ if (event->mask & (IN_MODIFY|IN_ATTRIB)) {
+ if ((e->isDir) && (!e->m_clients.empty())) {
+ const QString tpath = e->path + QLatin1Char('/') + path;
+ if (s_verboseDebug) {
+ qDebug() << "-->got MODIFY signal for" << (tpath);
+ }
+ // A file in this directory has been changed. No
+ // addEntry/ removeEntry bookkeeping should be required.
+ // Add the path to the list of pending file changes if
+ // there are any interested clients.
+ //QT_STATBUF stat_buf;
+ //QByteArray tpath = QFile::encodeName(e->path+'/'+path);
+ //QT_STAT(tpath, &stat_buf);
+ //bool isDir = S_ISDIR(stat_buf.st_mode);
+
+ // The API doc is somewhat vague as to whether we should emit
+ // dirty() for implicitly watched files when WatchFiles has
+ // not been specified - we'll assume they are always interested,
+ // regardless.
+ // Don't worry about duplicates for the time
+ // being; this is handled in slotRescan.
+ e->m_pendingFileChanges.append(tpath);
+ }
+ }
+
+ if (!rescan_timer.isActive())
+ rescan_timer.start(m_PollInterval); // singleshot
+
+ break;
+ }
+ }
+ }
+ if (bytesAvailable > 0) {
+ // copy partial event to beginning of buffer
+ memmove(buf, &buf[offsetCurrent], bytesAvailable);
+ offsetStartRead = bytesAvailable;
+ }
+ }
+#endif
+}
+
+/* In FAM mode, only entries which are marked dirty are scanned.
+ * We first need to mark all yet nonexistent, but possible created
+ * entries as dirty...
+ */
+void KDirWatchPrivate::Entry::propagate_dirty()
+{
+ Q_FOREACH(Entry *sub_entry, m_entries)
+ {
+ if (!sub_entry->dirty)
+ {
+ sub_entry->dirty = true;
+ sub_entry->propagate_dirty();
+ }
+ }
+}
+
+
+/* A KDirWatch instance is interested in getting events for
+ * this file/Dir entry.
+ */
+void KDirWatchPrivate::Entry::addClient(KDirWatch* instance,
+ KDirWatch::WatchModes watchModes)
+{
+ if (instance == 0)
+ return;
+
+ Q_FOREACH(Client* client, m_clients) {
+ if (client->instance == instance) {
+ client->count++;
+ client->m_watchModes = watchModes;
+ return;
+ }
+ }
+
+ Client* client = new Client;
+ client->instance = instance;
+ client->count = 1;
+ client->watchingStopped = instance->isStopped();
+ client->pending = NoChange;
+ client->m_watchModes = watchModes;
+
+ m_clients.append(client);
+}
+
+void KDirWatchPrivate::Entry::removeClient(KDirWatch* instance)
+{
+ QList<Client *>::iterator it = m_clients.begin();
+ const QList<Client *>::iterator end = m_clients.end();
+ for ( ; it != end ; ++it ) {
+ Client* client = *it;
+ if (client->instance == instance) {
+ client->count--;
+ if (client->count == 0) {
+ m_clients.erase(it);
+ delete client;
+ }
+ return;
+ }
+ }
+}
+
+/* get number of clients */
+int KDirWatchPrivate::Entry::clientCount() const
+{
+ int clients = 0;
+ Q_FOREACH(Client* client, m_clients)
+ clients += client->count;
+
+ return clients;
+}
+
+QString KDirWatchPrivate::Entry::parentDirectory() const
+{
+ return QDir::cleanPath(path + QLatin1String("/.."));
+}
+
+QList<KDirWatchPrivate::Client *> \
KDirWatchPrivate::Entry::clientsForFileOrDir(const QString& tpath, bool* isDir) const \
+{ + QList<Client *> ret;
+ QFileInfo fi(tpath);
+ if (fi.exists()) {
+ *isDir = fi.isDir();
+ const KDirWatch::WatchModes flag =
+ *isDir ? KDirWatch::WatchSubDirs : KDirWatch::WatchFiles;
+ Q_FOREACH(Client *client, this->m_clients) {
+ if (client->m_watchModes & flag) {
+ ret.append(client);
+ }
+ }
+ } else {
+ // Happens frequently, e.g. ERROR: couldn't stat "/home/dfaure/.viminfo.tmp"
+ //kDebug(7001) << "ERROR: couldn't stat" << tpath;
+ // In this case isDir is not set, but ret is empty anyway
+ // so isDir won't be used.
+ }
+ return ret;
+}
+
+QDebug operator<<(QDebug debug, const KDirWatchPrivate::Entry &entry)
+{
+ debug.nospace() << "[ Entry for " << entry.path << ", " << (entry.isDir ? "dir" : \
"file"); + if (entry.m_status == KDirWatchPrivate::NonExistent)
+ debug << ", non-existent";
+ debug << ", using " << ((entry.m_mode == KDirWatchPrivate::FAMMode) ? "FAM" :
+ (entry.m_mode == KDirWatchPrivate::INotifyMode) ? "INotify" \
: + (entry.m_mode == KDirWatchPrivate::DNotifyMode) ? \
"DNotify" : + (entry.m_mode == KDirWatchPrivate::QFSWatchMode) \
? "QFSWatch" : + (entry.m_mode == KDirWatchPrivate::StatMode) \
? "Stat" : "Unknown Method"); +#if HAVE_SYS_INOTIFY_H
+ if (entry.m_mode == KDirWatchPrivate::INotifyMode)
+ debug << " inotify_wd=" << entry.wd;
+#endif
+ debug << ", has " << entry.m_clients.count() << " clients";
+ debug.space();
+ if (!entry.m_entries.isEmpty()) {
+ debug << ", nonexistent subentries:";
+ Q_FOREACH(KDirWatchPrivate::Entry* subEntry, entry.m_entries)
+ debug << subEntry << subEntry->path;
+ }
+ debug << ']';
+ return debug;
+}
+
+KDirWatchPrivate::Entry* KDirWatchPrivate::entry(const QString& _path)
+{
+// we only support absolute paths
+ if (_path.isEmpty() || QDir::isRelativePath(_path)) {
+ return 0;
+ }
+
+ QString path (_path);
+
+ if ( path.length() > 1 && path.endsWith( QLatin1Char( '/' ) ) )
+ path.truncate( path.length() - 1 );
+
+ EntryMap::Iterator it = m_mapEntries.find( path );
+ if ( it == m_mapEntries.end() )
+ return 0;
+ else
+ return &(*it);
+}
+
+// set polling frequency for a entry and adjust global freq if needed
+void KDirWatchPrivate::useFreq(Entry* e, int newFreq)
+{
+ e->freq = newFreq;
+
+ // a reasonable frequency for the global polling timer
+ if (e->freq < freq) {
+ freq = e->freq;
+ if (timer.isActive()) timer.start(freq);
+ qDebug() << "Global Poll Freq is now" << freq << "msec";
+ }
+}
+
+
+#if HAVE_FAM
+// setup FAM notification, returns false if not possible
+bool KDirWatchPrivate::useFAM(Entry* e)
+{
+ if (!use_fam) return false;
+
+ // handle FAM events to avoid deadlock
+ // (FAM sends back all files in a directory when monitoring)
+ famEventReceived();
+
+ e->m_mode = FAMMode;
+ e->dirty = false;
+
+ if (e->isDir) {
+ if (e->m_status == NonExistent) {
+ // If the directory does not exist we watch the parent directory
+ addEntry(0, e->parentDirectory(), e, true);
+ }
+ else {
+ int res =FAMMonitorDirectory(&fc, QFile::encodeName(e->path).data(),
+ &(e->fr), e);
+ if (res<0) {
+ e->m_mode = UnknownMode;
+ use_fam=false;
+ delete sn; sn = 0;
+ return false;
+ }
+ qDebug().nospace() << " Setup FAM (Req " << FAMREQUEST_GETREQNUM(&(e->fr))
+ << ") for " << e->path;
+ }
+ }
+ else {
+ if (e->m_status == NonExistent) {
+ // If the file does not exist we watch the directory
+ addEntry(0, QFileInfo(e->path).absolutePath(), e, true);
+ }
+ else {
+ int res = FAMMonitorFile(&fc, QFile::encodeName(e->path).data(),
+ &(e->fr), e);
+ if (res<0) {
+ e->m_mode = UnknownMode;
+ use_fam=false;
+ delete sn; sn = 0;
+ return false;
+ }
+
+ qDebug().nospace() << " Setup FAM (Req " << FAMREQUEST_GETREQNUM(&(e->fr))
+ << ") for " << e->path;
+ }
+ }
+
+ // handle FAM events to avoid deadlock
+ // (FAM sends back all files in a directory when monitoring)
+ famEventReceived();
+
+ return true;
+}
+#endif
+
+#if HAVE_SYS_INOTIFY_H
+// setup INotify notification, returns false if not possible
+bool KDirWatchPrivate::useINotify( Entry* e )
+{
+ //kDebug (7001) << "trying to use inotify for monitoring";
+
+ e->wd = -1;
+ e->dirty = false;
+
+ if (!supports_inotify) return false;
+
+ e->m_mode = INotifyMode;
+
+ if ( e->m_status == NonExistent ) {
+ addEntry(0, e->parentDirectory(), e, true);
+ return true;
+ }
+
+ // May as well register for almost everything - it's free!
+ int mask = IN_DELETE|IN_DELETE_SELF|IN_CREATE|IN_MOVE|IN_MOVE_SELF|IN_DONT_FOLLOW|IN_MOVED_FROM|IN_MODIFY|IN_ATTRIB;
+
+ if ( ( e->wd = inotify_add_watch( m_inotify_fd,
+ QFile::encodeName( e->path ).data(), mask) ) >= \
0) + {
+ if (s_verboseDebug) {
+ qDebug() << "inotify successfully used for monitoring" << e->path << "wd=" << \
e->wd; + }
+ return true;
+ }
+
+ qDebug() << "inotify failed for monitoring" << e->path << ":" << \
strerror(errno); + return false;
+}
+#endif
+#if HAVE_QFILESYSTEMWATCHER
+bool KDirWatchPrivate::useQFSWatch(Entry* e)
+{
+ e->m_mode = QFSWatchMode;
+ e->dirty = false;
+
+ if ( e->m_status == NonExistent ) {
+ addEntry(0, e->parentDirectory(), e, true /*isDir*/);
+ return true;
+ }
+
+ qDebug() << "fsWatcher->addPath" << e->path;
+ if (!fsWatcher) {
+ fsWatcher = new KFileSystemWatcher();
+ connect(fsWatcher, SIGNAL(directoryChanged(QString)), this, \
SLOT(fswEventReceived(QString))); + connect(fsWatcher, \
SIGNAL(fileChanged(QString)), this, SLOT(fswEventReceived(QString))); + }
+ fsWatcher->addPath( e->path );
+ return true;
+}
+#endif
+
+bool KDirWatchPrivate::useStat(Entry* e)
+{
+ if (KFileSystemType::fileSystemType(e->path) == KFileSystemType::Nfs) // TODO: or \
Smbfs? + useFreq(e, m_nfsPollInterval);
+ else
+ useFreq(e, m_PollInterval);
+
+ if (e->m_mode != StatMode) {
+ e->m_mode = StatMode;
+ statEntries++;
+
+ if ( statEntries == 1 ) {
+ // if this was first STAT entry (=timer was stopped)
+ timer.start(freq); // then start the timer
+ qDebug() << " Started Polling Timer, freq " << freq;
+ }
+ }
+
+ qDebug() << " Setup Stat (freq " << e->freq << ") for " << e->path;
+
+ return true;
+}
+
+
+/* If <instance> !=0, this KDirWatch instance wants to watch at <_path>,
+ * providing in <isDir> the type of the entry to be watched.
+ * Sometimes, entries are dependant on each other: if <sub_entry> !=0,
+ * this entry needs another entry to watch himself (when notExistent).
+ */
+void KDirWatchPrivate::addEntry(KDirWatch* instance, const QString& _path,
+ Entry* sub_entry, bool isDir, KDirWatch::WatchModes watchModes)
+{
+ QString path (_path);
+ if (path.isEmpty()
+#ifndef Q_OS_WIN
+ || path == QLatin1String("/dev")
+ || (path.startsWith(QLatin1String("/dev/")) && \
!path.startsWith(QLatin1String("/dev/."))) +#endif
+ )
+ return; // Don't even go there.
+
+ if ( path.length() > 1 && path.endsWith( QLatin1Char( '/' ) ) )
+ path.truncate( path.length() - 1 );
+
+ EntryMap::Iterator it = m_mapEntries.find( path );
+ if ( it != m_mapEntries.end() )
+ {
+ if (sub_entry) {
+ (*it).m_entries.append(sub_entry);
+ if (s_verboseDebug) {
+ qDebug() << "Added already watched Entry" << path
+ << "(for" << sub_entry->path << ")";
+ }
+#if HAVE_SYS_INOTIFY_H
+ Entry* e = &(*it);
+ if( (e->m_mode == INotifyMode) && (e->wd >= 0) ) {
+ int mask = \
IN_DELETE|IN_DELETE_SELF|IN_CREATE|IN_MOVE|IN_MOVE_SELF|IN_DONT_FOLLOW; + \
if(!e->isDir) + mask |= IN_MODIFY|IN_ATTRIB;
+ else
+ mask |= IN_ONLYDIR;
+
+ inotify_rm_watch (m_inotify_fd, e->wd);
+ e->wd = inotify_add_watch( m_inotify_fd, QFile::encodeName( e->path \
).data(), + mask);
+ //Q_ASSERT(e->wd >= 0); // fails in KDirListerTest::testDeleteCurrentDir
+ }
+#endif
+ }
+ else {
+ (*it).addClient(instance, watchModes);
+ if (s_verboseDebug) {
+ qDebug() << "Added already watched Entry" << path
+ << "(now" << (*it).clientCount() << "clients)"
+ << QString::fromLatin1("[%1]").arg(instance->objectName());
+ }
+ }
+ return;
+ }
+
+ // we have a new path to watch
+
+ QT_STATBUF stat_buf;
+ bool exists = (QT_STAT(QFile::encodeName(path).constData(), &stat_buf) == 0);
+
+ EntryMap::iterator newIt = m_mapEntries.insert( path, Entry() );
+ // the insert does a copy, so we have to use <e> now
+ Entry* e = &(*newIt);
+
+ if (exists) {
+ e->isDir = S_ISDIR(stat_buf.st_mode);
+
+ if (e->isDir && !isDir) {
+ if (QT_LSTAT(QFile::encodeName(path).constData(), &stat_buf) == 0) {
+ if (S_ISLNK(stat_buf.st_mode))
+ // if it's a symlink, don't follow it
+ e->isDir = false;
+ else
+ qWarning() << "KDirWatch:" << path << "is a directory. Use addDir!";
+ }
+ } else if (!e->isDir && isDir)
+ qWarning("KDirWatch: %s is a file. Use addFile!", qPrintable(path));
+
+ if (!e->isDir && ( watchModes != KDirWatch::WatchDirOnly)) {
+ qWarning() << "KDirWatch:" << path << "is a file. You can't use recursive or \
" + "watchFiles options";
+ watchModes = KDirWatch::WatchDirOnly;
+ }
+
+#ifdef Q_OS_WIN
+ // ctime is the 'creation time' on windows - use mtime instead
+ e->m_ctime = stat_buf.st_mtime;
+#else
+ e->m_ctime = stat_buf.st_ctime;
+#endif
+ e->m_status = Normal;
+ e->m_nlink = stat_buf.st_nlink;
+ e->m_ino = stat_buf.st_ino;
+ }
+ else {
+ e->isDir = isDir;
+ e->m_ctime = invalid_ctime;
+ e->m_status = NonExistent;
+ e->m_nlink = 0;
+ e->m_ino = 0;
+ }
+
+ e->path = path;
+ if (sub_entry)
+ e->m_entries.append(sub_entry);
+ else
+ e->addClient(instance, watchModes);
+
+ qDebug().nospace() << "Added " << (e->isDir ? "Dir " : "File ") << path
+ << (e->m_status == NonExistent ? " NotExisting" : "")
+ << " for " << (sub_entry ? sub_entry->path : QString())
+ << " [" << (instance ? instance->objectName() : QString()) << "]";
+
+ // now setup the notification method
+ e->m_mode = UnknownMode;
+ e->msecLeft = 0;
+
+ if ( isNoisyFile( QFile::encodeName( path ).data() ) )
+ return;
+
+ if (exists && e->isDir && (watchModes != KDirWatch::WatchDirOnly)) {
+ QFlags<QDir::Filter> filters = QDir::NoDotAndDotDot;
+
+ if ((watchModes & KDirWatch::WatchSubDirs) &&
+ (watchModes & KDirWatch::WatchFiles)) {
+ filters |= (QDir::Dirs|QDir::Files);
+ } else if (watchModes & KDirWatch::WatchSubDirs) {
+ filters |= QDir::Dirs;
+ } else if (watchModes & KDirWatch::WatchFiles) {
+ filters |= QDir::Files;
+ }
+
+#if HAVE_SYS_INOTIFY_H
+ if (e->m_mode == INotifyMode || (e->m_mode == UnknownMode && m_preferredMethod \
== KDirWatch::INotify) ) + {
+ //kDebug(7001) << "Ignoring WatchFiles directive - this is implicit with \
inotify"; + // Placing a watch on individual files is redundant with inotify
+ // (inotify gives us WatchFiles functionality "for free") and indeed
+ // actively harmful, so prevent it. WatchSubDirs is necessary, though.
+ filters &= ~QDir::Files;
+ }
+#endif
+
+ QDir basedir (e->path);
+ const QFileInfoList contents = basedir.entryInfoList(filters);
+ for (QFileInfoList::const_iterator iter = contents.constBegin();
+ iter != contents.constEnd(); ++iter)
+ {
+ const QFileInfo &fileInfo = *iter;
+ // treat symlinks as files--don't follow them.
+ bool isDir = fileInfo.isDir() && !fileInfo.isSymLink();
+
+ addEntry (instance, fileInfo.absoluteFilePath(), 0, isDir,
+ isDir ? watchModes : KDirWatch::WatchDirOnly);
+ }
+ }
+
+ addWatch(e);
+}
+
+void KDirWatchPrivate::addWatch(Entry* e)
+{
+ // If the watch is on a network filesystem use the nfsPreferredMethod as the
+ // default, otherwise use preferredMethod as the default, if the methods are
+ // the same we can skip the mountpoint check
+
+ // This allows to configure a different method for NFS mounts, since inotify
+ // cannot detect changes made by other machines. However as a default inotify
+ // is fine, since the most common case is a NFS-mounted home, where all changes
+ // are made locally. #177892.
+ KDirWatch::Method preferredMethod = m_preferredMethod;
+ if (m_nfsPreferredMethod != m_preferredMethod) {
+ if (KFileSystemType::fileSystemType(e->path) == KFileSystemType::Nfs) {
+ preferredMethod = m_nfsPreferredMethod;
+ }
+ }
+
+ // Try the appropriate preferred method from the config first
+ bool entryAdded = false;
+ switch (preferredMethod) {
+#if HAVE_FAM
+ case KDirWatch::FAM: entryAdded = useFAM(e); break;
+#endif
+#if HAVE_SYS_INOTIFY_H
+ case KDirWatch::INotify: entryAdded = useINotify(e); break;
+#endif
+#if HAVE_QFILESYSTEMWATCHER
+ case KDirWatch::QFSWatch: entryAdded = useQFSWatch(e); break;
+#endif
+ case KDirWatch::Stat: entryAdded = useStat(e); break;
+ default: break;
+ }
+
+ // Failing that try in order INotify, FAM, QFSWatch, Stat
+ if (!entryAdded) {
+#if HAVE_SYS_INOTIFY_H
+ if (useINotify(e)) return;
+#endif
+#if HAVE_FAM
+ if (useFAM(e)) return;
+#endif
+#if HAVE_QFILESYSTEMWATCHER
+ if (useQFSWatch(e)) return;
+#endif
+ useStat(e);
+ }
+}
+
+void KDirWatchPrivate::removeWatch(Entry* e)
+{
+#if HAVE_FAM
+ if (e->m_mode == FAMMode) {
+ FAMCancelMonitor(&fc, &(e->fr) );
+ qDebug().nospace() << "Cancelled FAM (Req " << \
FAMREQUEST_GETREQNUM(&(e->fr)) + << ") for " << \
e->path; + }
+#endif
+#if HAVE_SYS_INOTIFY_H
+ if (e->m_mode == INotifyMode) {
+ (void) inotify_rm_watch( m_inotify_fd, e->wd );
+ if (s_verboseDebug) {
+ qDebug().nospace() << "Cancelled INotify (fd " << m_inotify_fd << ", "
+ << e->wd << ") for " << e->path;
+ }
+ }
+#endif
+#if HAVE_QFILESYSTEMWATCHER
+ if (e->m_mode == QFSWatchMode && fsWatcher) {
+ if (s_verboseDebug)
+ qDebug() << "fsWatcher->removePath" << e->path;
+ fsWatcher->removePath(e->path);
+ }
+#endif
+}
+
+void KDirWatchPrivate::removeEntry(KDirWatch* instance,
+ const QString& _path,
+ Entry* sub_entry)
+{
+ if (s_verboseDebug) {
+ qDebug() << "path=" << _path << "sub_entry:" << sub_entry;
+ }
+ Entry* e = entry(_path);
+ if (!e) {
+ qWarning() << "doesn't know" << _path;
+ return;
+ }
+
+ removeEntry(instance, e, sub_entry);
+}
+
+void KDirWatchPrivate::removeEntry(KDirWatch* instance,
+ Entry* e,
+ Entry* sub_entry)
+{
+ removeList.remove(e);
+
+ if (sub_entry)
+ e->m_entries.removeAll(sub_entry);
+ else
+ e->removeClient(instance);
+
+ if (e->m_clients.count() || e->m_entries.count())
+ return;
+
+ if (delayRemove) {
+ removeList.insert(e);
+ // now e->isValid() is false
+ return;
+ }
+
+ if ( e->m_status == Normal) {
+ removeWatch(e);
+ } else {
+ // Removed a NonExistent entry - we just remove it from the parent
+ if (e->isDir)
+ removeEntry(0, e->parentDirectory(), e);
+ else
+ removeEntry(0, QFileInfo(e->path).absolutePath(), e);
+ }
+
+ if (e->m_mode == StatMode) {
+ statEntries--;
+ if ( statEntries == 0 ) {
+ timer.stop(); // stop timer if lists are empty
+ qDebug() << " Stopped Polling Timer";
+ }
+ }
+
+ if (s_verboseDebug) {
+ qDebug().nospace() << "Removed " << (e->isDir ? "Dir ":"File ") << e->path
+ << " for " << (sub_entry ? sub_entry->path : QString())
+ << " [" << (instance ? instance->objectName() : \
QString()) << "]"; + }
+ m_mapEntries.remove( e->path ); // <e> not valid any more
+}
+
+
+/* Called from KDirWatch destructor:
+ * remove <instance> as client from all entries
+ */
+void KDirWatchPrivate::removeEntries( KDirWatch* instance )
+{
+ int minfreq = 3600000;
+
+ QStringList pathList;
+ // put all entries where instance is a client in list
+ EntryMap::Iterator it = m_mapEntries.begin();
+ for( ; it != m_mapEntries.end(); ++it ) {
+ Client* c = 0;
+ Q_FOREACH(Client* client, (*it).m_clients) {
+ if (client->instance == instance) {
+ c = client;
+ break;
+ }
+ }
+ if (c) {
+ c->count = 1; // forces deletion of instance as client
+ pathList.append((*it).path);
+ }
+ else if ( (*it).m_mode == StatMode && (*it).freq < minfreq )
+ minfreq = (*it).freq;
+ }
+
+ Q_FOREACH(const QString &path, pathList)
+ removeEntry(instance, path, 0);
+
+ if (minfreq > freq) {
+ // we can decrease the global polling frequency
+ freq = minfreq;
+ if (timer.isActive()) timer.start(freq);
+ qDebug() << "Poll Freq now" << freq << "msec";
+ }
+}
+
+// instance ==0: stop scanning for all instances
+bool KDirWatchPrivate::stopEntryScan( KDirWatch* instance, Entry* e)
+{
+ int stillWatching = 0;
+ Q_FOREACH(Client* client, e->m_clients) {
+ if (!instance || instance == client->instance)
+ client->watchingStopped = true;
+ else if (!client->watchingStopped)
+ stillWatching += client->count;
+ }
+
+ qDebug() << (instance ? instance->objectName() : QString::fromLatin1("all"))
+ << "stopped scanning" << e->path << "(now"
+ << stillWatching << "watchers)";
+
+ if (stillWatching == 0) {
+ // if nobody is interested, we don't watch
+ if ( e->m_mode != INotifyMode ) {
+ e->m_ctime = invalid_ctime; // invalid
+ e->m_status = NonExistent;
+ }
+ // e->m_status = Normal;
+ }
+ return true;
+}
+
+// instance ==0: start scanning for all instances
+bool KDirWatchPrivate::restartEntryScan( KDirWatch* instance, Entry* e,
+ bool notify)
+{
+ int wasWatching = 0, newWatching = 0;
+ Q_FOREACH(Client* client, e->m_clients) {
+ if (!client->watchingStopped)
+ wasWatching += client->count;
+ else if (!instance || instance == client->instance) {
+ client->watchingStopped = false;
+ newWatching += client->count;
+ }
+ }
+ if (newWatching == 0)
+ return false;
+
+ qDebug() << (instance ? instance->objectName() : QString::fromLatin1("all"))
+ << "restarted scanning" << e->path
+ << "(now" << wasWatching+newWatching << "watchers)";
+
+ // restart watching and emit pending events
+
+ int ev = NoChange;
+ if (wasWatching == 0) {
+ if (!notify) {
+ QT_STATBUF stat_buf;
+ bool exists = (QT_STAT(QFile::encodeName(e->path).constData(), &stat_buf) == \
0); + if (exists) {
+#ifdef Q_OS_WIN
+ // ctime is the 'creation time' on windows - use mtime instead
+ e->m_ctime = stat_buf.st_mtime;
+#else
+ e->m_ctime = stat_buf.st_ctime;
+#endif
+ e->m_status = Normal;
+ if (s_verboseDebug) {
+ qDebug() << "Setting status to Normal for" << e << e->path;
+ }
+ e->m_nlink = stat_buf.st_nlink;
+ e->m_ino = stat_buf.st_ino;
+
+ // Same as in scanEntry: ensure no subentry in parent dir
+ removeEntry(0, e->parentDirectory(), e);
+ }
+ else {
+ e->m_ctime = invalid_ctime;
+ e->m_status = NonExistent;
+ e->m_nlink = 0;
+ if (s_verboseDebug) {
+ qDebug() << "Setting status to NonExistent for" << e << e->path;
+ }
+ }
+ }
+ e->msecLeft = 0;
+ ev = scanEntry(e);
+ }
+ emitEvent(e,ev);
+
+ return true;
+}
+
+// instance ==0: stop scanning for all instances
+void KDirWatchPrivate::stopScan(KDirWatch* instance)
+{
+ EntryMap::Iterator it = m_mapEntries.begin();
+ for( ; it != m_mapEntries.end(); ++it )
+ stopEntryScan(instance, &(*it));
+}
+
+
+void KDirWatchPrivate::startScan(KDirWatch* instance,
+ bool notify, bool skippedToo )
+{
+ if (!notify)
+ resetList(instance,skippedToo);
+
+ EntryMap::Iterator it = m_mapEntries.begin();
+ for( ; it != m_mapEntries.end(); ++it )
+ restartEntryScan(instance, &(*it), notify);
+
+ // timer should still be running when in polling mode
+}
+
+
+// clear all pending events, also from stopped
+void KDirWatchPrivate::resetList( KDirWatch* /*instance*/, bool skippedToo )
+{
+ EntryMap::Iterator it = m_mapEntries.begin();
+ for( ; it != m_mapEntries.end(); ++it ) {
+
+ Q_FOREACH(Client* client, (*it).m_clients) {
+ if (!client->watchingStopped || skippedToo)
+ client->pending = NoChange;
+ }
+ }
+}
+
+// Return event happened on <e>
+//
+int KDirWatchPrivate::scanEntry(Entry* e)
+{
+ // Shouldn't happen: Ignore "unknown" notification method
+ if (e->m_mode == UnknownMode) return NoChange;
+
+ if (e->m_mode == FAMMode || e->m_mode == INotifyMode) {
+ // we know nothing has changed, no need to stat
+ if(!e->dirty) return NoChange;
+ e->dirty = false;
+ }
+
+ if (e->m_mode == StatMode) {
+ // only scan if timeout on entry timer happens;
+ // e.g. when using 500msec global timer, a entry
+ // with freq=5000 is only watched every 10th time
+
+ e->msecLeft -= freq;
+ if (e->msecLeft>0) return NoChange;
+ e->msecLeft += e->freq;
+ }
+
+ QT_STATBUF stat_buf;
+ const bool exists = (QT_STAT(QFile::encodeName(e->path).constData(), &stat_buf) \
== 0); + if (exists) {
+
+ if (e->m_status == NonExistent) {
+ // ctime is the 'creation time' on windows, but with qMax
+ // we get the latest change of any kind, on any platform.
+ e->m_ctime = qMax(stat_buf.st_ctime, stat_buf.st_mtime);
+ e->m_status = Normal;
+ e->m_ino = stat_buf.st_ino;
+ if (s_verboseDebug) {
+ qDebug() << "Setting status to Normal for just created" << e << e->path;
+ }
+ // We need to make sure the entry isn't listed in its parent's subentries... \
(#222974, testMoveTo) + removeEntry(0, e->parentDirectory(), e);
+
+ return Created;
+ }
+
+#if 1 // for debugging the if() below
+ if (s_verboseDebug) {
+ struct tm* tmp = localtime(&e->m_ctime);
+ char outstr[200];
+ strftime(outstr, sizeof(outstr), "%T", tmp);
+ qDebug() << "e->m_ctime=" << e->m_ctime << outstr
+ << "stat_buf.st_ctime=" << stat_buf.st_ctime
+ << "e->m_nlink=" << e->m_nlink
+ << "stat_buf.st_nlink=" << stat_buf.st_nlink
+ << "e->m_ino=" << e->m_ino
+ << "stat_buf.st_ino=" << stat_buf.st_ino;
+ }
+#endif
+
+ if ( ((e->m_ctime != invalid_ctime) &&
+ (qMax(stat_buf.st_ctime, stat_buf.st_mtime) != e->m_ctime ||
+ stat_buf.st_ino != e->m_ino ||
+ stat_buf.st_nlink != nlink_t(e->m_nlink)))
+#ifdef Q_OS_WIN
+ // we trust QFSW to get it right, the ctime comparisons above
+ // fail for example when adding files to directories on Windows
+ // which doesn't change the mtime of the directory
+ || e->m_mode == QFSWatchMode
+#endif
+ ) {
+ e->m_ctime = qMax(stat_buf.st_ctime, stat_buf.st_mtime);
+ e->m_nlink = stat_buf.st_nlink;
+ if (e->m_ino != stat_buf.st_ino) {
+ // The file got deleted and recreated. We need to watch it again.
+ removeWatch(e);
+ addWatch(e);
+ }
+ e->m_ino = stat_buf.st_ino;
+ return Changed;
+ }
+
+ return NoChange;
+ }
+
+ // dir/file doesn't exist
+
+ e->m_nlink = 0;
+ e->m_ino = 0;
+ e->m_status = NonExistent;
+
+ if (e->m_ctime == invalid_ctime) {
+ return NoChange;
+ }
+
+ e->m_ctime = invalid_ctime;
+ return Deleted;
+}
+
+/* Notify all interested KDirWatch instances about a given event on an entry
+ * and stored pending events. When watching is stopped, the event is
+ * added to the pending events.
+ */
+void KDirWatchPrivate::emitEvent(const Entry* e, int event, const QString \
&fileName) +{
+ QString path (e->path);
+ if (!fileName.isEmpty()) {
+ if (!QDir::isRelativePath(fileName))
+ path = fileName;
+ else {
+#ifdef Q_OS_UNIX
+ path += QLatin1Char('/') + fileName;
+#elif defined(Q_OS_WIN)
+ //current drive is passed instead of /
+ path += QDir::currentPath().left(2) + QLatin1Char('/') + fileName;
+#endif
+ }
+ }
+
+ if (s_verboseDebug) {
+ qDebug() << event << path << e->m_clients.count() << "clients";
+ }
+
+ Q_FOREACH(Client* c, e->m_clients)
+ {
+ if (c->instance==0 || c->count==0) continue;
+
+ if (c->watchingStopped) {
+ // add event to pending...
+ if (event == Changed)
+ c->pending |= event;
+ else if (event == Created || event == Deleted)
+ c->pending = event;
+ continue;
+ }
+ // not stopped
+ if (event == NoChange || event == Changed)
+ event |= c->pending;
+ c->pending = NoChange;
+ if (event == NoChange) continue;
+
+ // Emit the signals delayed, to avoid unexpected re-entrancy from the slots \
(#220153) +
+ if (event & Deleted) {
+ QMetaObject::invokeMethod(c->instance, "setDeleted", Qt::QueuedConnection, \
Q_ARG(QString, path)); + // emit only Deleted event...
+ continue;
+ }
+
+ if (event & Created) {
+ QMetaObject::invokeMethod(c->instance, "setCreated", Qt::QueuedConnection, \
Q_ARG(QString, path)); + // possible emit Change event after creation
+ }
+
+ if (event & Changed) {
+ QMetaObject::invokeMethod(c->instance, "setDirty", Qt::QueuedConnection, \
Q_ARG(QString, path)); + }
+ }
+}
+
+// Remove entries which were marked to be removed
+void KDirWatchPrivate::slotRemoveDelayed()
+{
+ delayRemove = false;
+ // Removing an entry could also take care of removing its parent
+ // (e.g. in FAM or inotify mode), which would remove other entries in removeList,
+ // so don't use Q_FOREACH or iterators here...
+ while (!removeList.isEmpty()) {
+ Entry* entry = *removeList.begin();
+ removeEntry(0, entry, 0); // this will remove entry from removeList
+ }
+}
+
+/* Scan all entries to be watched for changes. This is done regularly
+ * when polling. FAM and inotify use a single-shot timer to call this slot delayed.
+ */
+void KDirWatchPrivate::slotRescan()
+{
+ if (s_verboseDebug)
+ qDebug();
+
+ EntryMap::Iterator it;
+
+ // People can do very long things in the slot connected to dirty(),
+ // like showing a message box. We don't want to keep polling during
+ // that time, otherwise the value of 'delayRemove' will be reset.
+ // ### TODO: now the emitEvent delays emission, this can be cleaned up
+ bool timerRunning = timer.isActive();
+ if ( timerRunning )
+ timer.stop();
+
+ // We delay deletions of entries this way.
+ // removeDir(), when called in slotDirty(), can cause a crash otherwise
+ // ### TODO: now the emitEvent delays emission, this can be cleaned up
+ delayRemove = true;
+
+ if (rescan_all)
+ {
+ // mark all as dirty
+ it = m_mapEntries.begin();
+ for( ; it != m_mapEntries.end(); ++it )
+ (*it).dirty = true;
+ rescan_all = false;
+ }
+ else
+ {
+ // progate dirty flag to dependant entries (e.g. file watches)
+ it = m_mapEntries.begin();
+ for( ; it != m_mapEntries.end(); ++it )
+ if (((*it).m_mode == INotifyMode || (*it).m_mode == QFSWatchMode) && \
(*it).dirty ) + (*it).propagate_dirty();
+ }
+
+#if HAVE_SYS_INOTIFY_H
+ QList<Entry*> cList;
+#endif
+
+ it = m_mapEntries.begin();
+ for( ; it != m_mapEntries.end(); ++it ) {
+ // we don't check invalid entries (i.e. remove delayed)
+ Entry* entry = &(*it);
+ if (!entry->isValid()) continue;
+
+ const int ev = scanEntry(entry);
+ if (s_verboseDebug)
+ qDebug() << "scanEntry for" << entry->path << "says" << ev;
+
+ switch(entry->m_mode) {
+#if HAVE_SYS_INOTIFY_H
+ case INotifyMode:
+ if ( ev == Deleted ) {
+ if (s_verboseDebug)
+ qDebug() << "scanEntry says" << entry->path << "was deleted";
+ addEntry(0, entry->parentDirectory(), entry, true);
+ } else if (ev == Created) {
+ if (s_verboseDebug)
+ qDebug() << "scanEntry says" << entry->path << "was created. wd=" << \
entry->wd; + if (entry->wd < 0) {
+ cList.append(entry);
+ addWatch(entry);
+ }
+ }
+ break;
+#endif
+ case FAMMode:
+ case QFSWatchMode:
+ if (ev == Created) {
+ addWatch(entry);
+ }
+ break;
+ default:
+ // dunno about StatMode...
+ break;
+ }
+
+#if HAVE_SYS_INOTIFY_H
+ if (entry->isDir) {
+ // Report and clear the the list of files that have changed in this \
directory. + // Remove duplicates by changing to set and back again:
+ // we don't really care about preserving the order of the
+ // original changes.
+ QStringList pendingFileChanges = entry->m_pendingFileChanges;
+ pendingFileChanges.removeDuplicates();
+ Q_FOREACH(const QString &changedFilename, pendingFileChanges) {
+ if (s_verboseDebug) {
+ qDebug() << "processing pending file change for" << changedFilename;
+ }
+ emitEvent(entry, Changed, changedFilename);
+ }
+ entry->m_pendingFileChanges.clear();
+ }
+#endif
+
+ if ( ev != NoChange ) {
+ emitEvent(entry, ev);
+ }
+ }
+
+ if ( timerRunning )
+ timer.start(freq);
+
+#if HAVE_SYS_INOTIFY_H
+ // Remove watch of parent of new created directories
+ Q_FOREACH(Entry* e, cList)
+ removeEntry(0, e->parentDirectory(), e);
+#endif
+
+ QTimer::singleShot(0, this, SLOT(slotRemoveDelayed()));
+}
+
+bool KDirWatchPrivate::isNoisyFile( const char * filename )
+{
+ // $HOME/.X.err grows with debug output, so don't notify change
+ if ( *filename == '.') {
+ if (strncmp(filename, ".X.err", 6) == 0) return true;
+ if (strncmp(filename, ".xsession-errors", 16) == 0) return true;
+ // fontconfig updates the cache on every KDE app start
+ // (inclusive kio_thumbnail slaves)
+ if (strncmp(filename, ".fonts.cache", 12) == 0) return true;
+ }
+
+ return false;
+}
+
+#if HAVE_FAM
+void KDirWatchPrivate::famEventReceived()
+{
+ static FAMEvent fe;
+
+ delayRemove = true;
+
+ //kDebug(7001) << "Fam event received";
+
+ while(use_fam && FAMPending(&fc)) {
+ if (FAMNextEvent(&fc, &fe) == -1) {
+ qWarning() << "FAM connection problem, switching to polling.";
+ use_fam = false;
+ delete sn; sn = 0;
+
+ // Replace all FAMMode entries with INotify/Stat
+ EntryMap::Iterator it = m_mapEntries.begin();
+ for( ; it != m_mapEntries.end(); ++it )
+ if ((*it).m_mode == FAMMode && (*it).m_clients.count()>0) {
+ Entry* e = &(*it);
+ addWatch(e);
+ }
+ }
+ else
+ checkFAMEvent(&fe);
+ }
+
+ QTimer::singleShot(0, this, SLOT(slotRemoveDelayed()));
+}
+
+void KDirWatchPrivate::checkFAMEvent(FAMEvent* fe)
+{
+ //kDebug(7001);
+
+ // Don't be too verbose ;-)
+ if ((fe->code == FAMExists) ||
+ (fe->code == FAMEndExist) ||
+ (fe->code == FAMAcknowledge)) return;
+
+ if ( isNoisyFile( fe->filename ) )
+ return;
+
+ Entry* e = 0;
+ EntryMap::Iterator it = m_mapEntries.begin();
+ for( ; it != m_mapEntries.end(); ++it )
+ if (FAMREQUEST_GETREQNUM(&( (*it).fr )) ==
+ FAMREQUEST_GETREQNUM(&(fe->fr)) ) {
+ e = &(*it);
+ break;
+ }
+
+ // Entry* e = static_cast<Entry*>(fe->userdata);
+
+ if (s_verboseDebug) { // don't enable this except when debugging, see #88538
+ qDebug() << "Processing FAM event ("
+ << ((fe->code == FAMChanged) ? "FAMChanged" :
+ (fe->code == FAMDeleted) ? "FAMDeleted" :
+ (fe->code == FAMStartExecuting) ? "FAMStartExecuting" :
+ (fe->code == FAMStopExecuting) ? "FAMStopExecuting" :
+ (fe->code == FAMCreated) ? "FAMCreated" :
+ (fe->code == FAMMoved) ? "FAMMoved" :
+ (fe->code == FAMAcknowledge) ? "FAMAcknowledge" :
+ (fe->code == FAMExists) ? "FAMExists" :
+ (fe->code == FAMEndExist) ? "FAMEndExist" : "Unknown Code")
+ << ", " << fe->filename
+ << ", Req " << FAMREQUEST_GETREQNUM(&(fe->fr)) << ") e=" << e;
+ }
+
+ if (!e) {
+ // this happens e.g. for FAMAcknowledge after deleting a dir...
+ // kDebug(7001) << "No entry for FAM event ?!";
+ return;
+ }
+
+ if (e->m_status == NonExistent) {
+ qDebug() << "FAM event for nonExistent entry " << e->path;
+ return;
+ }
+
+ // Delayed handling. This rechecks changes with own stat calls.
+ e->dirty = true;
+ if (!rescan_timer.isActive())
+ rescan_timer.start(m_PollInterval); // singleshot
+
+ // needed FAM control actions on FAM events
+ switch (fe->code) {
+ case FAMDeleted:
+ // fe->filename is an absolute path when a watched file-or-dir is deleted
+ if (!QDir::isRelativePath(QFile::decodeName(fe->filename))) {
+ FAMCancelMonitor(&fc, &(e->fr) ); // needed ?
+ qDebug() << "Cancelled FAMReq"
+ << FAMREQUEST_GETREQNUM(&(e->fr))
+ << "for" << e->path;
+ e->m_status = NonExistent;
+ e->m_ctime = invalid_ctime;
+ emitEvent(e, Deleted, e->path);
++ // If the parent dir was already watched, tell it something changed
++ Entry* parentEntry = entry(e->parentDirectory());
++ if (parentEntry)
++ parentEntry->dirty = true;
+ // Add entry to parent dir to notice if the entry gets recreated
+ addEntry(0, e->parentDirectory(), e, true /*isDir*/);
+ } else {
+ // A file in this directory has been removed, and wasn't explicitly \
watched. + // We could still inform clients, like inotify does? But stat \
can't. + // For now we just marked e dirty and slotRescan will emit the \
dir as dirty. + //kDebug(7001) << "Got FAMDeleted for" << \
QFile::decodeName(fe->filename) << "in" << e->path << ". Absolute path -> NOOP!"; + \
} + break;
+
+ case FAMCreated: {
+ // check for creation of a directory we have to watch
+ QString tpath(e->path + QLatin1Char('/') + \
QFile::decodeName(fe->filename)); +
+ // This code is very similar to the one in inotifyEventReceived...
+ Entry* sub_entry = e->findSubEntry(tpath);
+ if (sub_entry /*&& sub_entry->isDir*/) {
+ // We were waiting for this new file/dir to be created
+ emitEvent(sub_entry, Created);
+ sub_entry->dirty = true;
+ rescan_timer.start(0); // process this asap, to start watching that dir
+ } else if (e->isDir && !e->m_clients.empty()) {
+ bool isDir = false;
+ const QList<Client *> clients = e->clientsForFileOrDir(tpath, &isDir);
+ Q_FOREACH(Client *client, clients) {
+ addEntry (client->instance, tpath, 0, isDir,
+ isDir ? client->m_watchModes : KDirWatch::WatchDirOnly);
+ }
+
+ if (!clients.isEmpty()) {
+ emitEvent(e, Created, tpath);
+
+ qDebug().nospace() << clients.count() << " instance(s) monitoring the \
new " + << (isDir ? "dir " : "file ") << tpath;
+ }
+ }
+ }
+ break;
+ default:
+ break;
+ }
+}
+#else
+void KDirWatchPrivate::famEventReceived()
+{
+ qWarning () << "Fam event received but FAM is not supported";
+}
+#endif
+
+
+void KDirWatchPrivate::statistics()
+{
+ EntryMap::Iterator it;
+
+ qDebug() << "Entries watched:";
+ if (m_mapEntries.count()==0) {
+ qDebug() << " None.";
+ }
+ else {
+ it = m_mapEntries.begin();
+ for( ; it != m_mapEntries.end(); ++it ) {
+ Entry* e = &(*it);
+ qDebug() << " " << *e;
+
+ Q_FOREACH(Client* c, e->m_clients) {
+ QByteArray pending;
+ if (c->watchingStopped) {
+ if (c->pending & Deleted) pending += "deleted ";
+ if (c->pending & Created) pending += "created ";
+ if (c->pending & Changed) pending += "changed ";
+ if (!pending.isEmpty()) pending = " (pending: " + pending + ')';
+ pending = ", stopped" + pending;
+ }
+ qDebug() << " by " << c->instance->objectName()
+ << " (" << c->count << " times)" << pending;
+ }
+ if (e->m_entries.count()>0) {
+ qDebug() << " dependent entries:";
+ Q_FOREACH(Entry *d, e->m_entries) {
+ qDebug() << " " << d << d->path << (d->m_status == NonExistent ? \
"NonExistent" : "EXISTS!!! ERROR!"); + if (s_verboseDebug) {
+ Q_ASSERT(d->m_status == NonExistent); // it doesn't belong here \
otherwise + }
+ }
+ }
+ }
+ }
+}
+
+#if HAVE_QFILESYSTEMWATCHER
+// Slot for QFileSystemWatcher
+void KDirWatchPrivate::fswEventReceived(const QString &path)
+{
+ if (s_verboseDebug)
+ qDebug() << path;
+ EntryMap::Iterator it = m_mapEntries.find(path);
+ if(it != m_mapEntries.end()) {
+ Entry* e = &(*it);
+ e->dirty = true;
+ const int ev = scanEntry(e);
+ if (s_verboseDebug)
+ qDebug() << "scanEntry for" << e->path << "says" << ev;
+ if (ev != NoChange)
+ emitEvent(e, ev);
+ if(ev == Deleted) {
+ if (e->isDir)
+ addEntry(0, e->parentDirectory(), e, true);
+ else
+ addEntry(0, QFileInfo(e->path).absolutePath(), e, true);
+ } else if (ev == Created) {
+ // We were waiting for it to appear; now watch it
+ addWatch(e);
+ } else if (e->isDir) {
+ // Check if any file or dir was created under this directory, that we were \
waiting for + Q_FOREACH(Entry* sub_entry, e->m_entries) {
+ fswEventReceived(sub_entry->path); // recurse, to call scanEntry and see \
if something changed + }
+ }
+ }
+}
+#else
+void KDirWatchPrivate::fswEventReceived(const QString &path)
+{
+ Q_UNUSED(path);
+ qWarning () << "QFileSystemWatcher event received but QFileSystemWatcher is not \
supported"; +}
+#endif // HAVE_QFILESYSTEMWATCHER
+
+//
+// Class KDirWatch
+//
+
+Q_GLOBAL_STATIC(KDirWatch, s_pKDirWatchSelf)
+KDirWatch* KDirWatch::self()
+{
+ return s_pKDirWatchSelf();
+}
+
+// <steve> is this used anywhere?
+// <dfaure> yes, see kio/kdirlister_p.h:328
+bool KDirWatch::exists()
+{
+#pragma message("Qt5 TODO: use new Q_GLOBAL_STATIC isDestroyed")
+// return s_pKDirWatchSelf.exists();
+ return true;
+}
+
+static void cleanupQFSWatcher()
+{
+ s_pKDirWatchSelf()->deleteQFSWatcher();
+}
+
+KDirWatch::KDirWatch (QObject* parent)
+ : QObject(parent), d(createPrivate())
+{
+ static int nameCounter = 0;
+
+ nameCounter++;
+ setObjectName(QString::fromLatin1("KDirWatch-%1").arg(nameCounter) );
+
+ d->ref();
+
+ d->_isStopped = false;
+
+ static bool cleanupRegistered = false;
+ if (!cleanupRegistered) {
+ cleanupRegistered = true;
+ // Must delete QFileSystemWatcher before qApp is gone - bug 261541
+ qAddPostRoutine(cleanupQFSWatcher);
+ }
+}
+
+KDirWatch::~KDirWatch()
+{
+ d->removeEntries(this);
+ if ( d->deref() )
+ {
+ // delete it if it's the last one
+ delete d;
+ dwp_self = 0;
+ }
+}
+
+void KDirWatch::addDir( const QString& _path, WatchModes watchModes)
+{
+ if (d) d->addEntry(this, _path, 0, true, watchModes);
+}
+
+void KDirWatch::addFile( const QString& _path )
+{
+ if ( !d )
+ return;
+
+ d->addEntry(this, _path, 0, false);
+}
+
+QDateTime KDirWatch::ctime( const QString &_path ) const
+{
+ KDirWatchPrivate::Entry* e = d->entry(_path);
+
+ if (!e)
+ return QDateTime();
+
+ return QDateTime::fromTime_t(e->m_ctime);
+}
+
+void KDirWatch::removeDir( const QString& _path )
+{
+ if (d) d->removeEntry(this, _path, 0);
+}
+
+void KDirWatch::removeFile( const QString& _path )
+{
+ if (d) d->removeEntry(this, _path, 0);
+}
+
+bool KDirWatch::stopDirScan( const QString& _path )
+{
+ if (d) {
+ KDirWatchPrivate::Entry *e = d->entry(_path);
+ if (e && e->isDir) return d->stopEntryScan(this, e);
+ }
+ return false;
+}
+
+bool KDirWatch::restartDirScan( const QString& _path )
+{
+ if (d) {
+ KDirWatchPrivate::Entry *e = d->entry(_path);
+ if (e && e->isDir)
+ // restart without notifying pending events
+ return d->restartEntryScan(this, e, false);
+ }
+ return false;
+}
+
+void KDirWatch::stopScan()
+{
+ if (d) {
+ d->stopScan(this);
+ d->_isStopped = true;
+ }
+}
+
+bool KDirWatch::isStopped()
+{
+ return d->_isStopped;
+}
+
+void KDirWatch::startScan( bool notify, bool skippedToo )
+{
+ if (d) {
+ d->_isStopped = false;
+ d->startScan(this, notify, skippedToo);
+ }
+}
+
+
+bool KDirWatch::contains( const QString& _path ) const
+{
+ KDirWatchPrivate::Entry* e = d->entry(_path);
+ if (!e)
+ return false;
+
+ Q_FOREACH(KDirWatchPrivate::Client* client, e->m_clients) {
+ if (client->instance == this)
+ return true;
+ }
+
+ return false;
+}
+
+void KDirWatch::deleteQFSWatcher()
+{
+ delete d->fsWatcher;
+ d->fsWatcher = 0;
+}
+
+void KDirWatch::statistics()
+{
+ if (!dwp_self) {
+ qDebug() << "KDirWatch not used";
+ return;
+ }
+ dwp_self->statistics();
+}
+
+
+void KDirWatch::setCreated( const QString & _file )
+{
+ qDebug() << objectName() << "emitting created" << _file;
+ Q_EMIT created( _file );
+}
+
+void KDirWatch::setDirty( const QString & _file )
+{
+ //kDebug(7001) << objectName() << "emitting dirty" << _file;
+ Q_EMIT dirty( _file );
+}
+
+void KDirWatch::setDeleted( const QString & _file )
+{
+ qDebug() << objectName() << "emitting deleted" << _file;
+ Q_EMIT deleted( _file );
+}
+
+KDirWatch::Method KDirWatch::internalMethod() const
+{
+ return d->m_preferredMethod;
+}
+
+
+#include "moc_kdirwatch.cpp"
+#include "moc_kdirwatch_p.cpp"
+
+//sven
diff --cc tier1/solid/src/solid/CMakeLists.txt
index 323ba1f,0000000..9b225e4
mode 100644,000000..100644
--- a/tier1/solid/src/solid/CMakeLists.txt
+++ b/tier1/solid/src/solid/CMakeLists.txt
@@@ -1,466 -1,0 +1,478 @@@
+include (CheckCXXSourceCompiles)
+
+if(MSVC)
+ check_cxx_source_compiles("int main() { __asm { pxor mm0, mm0 }; }" \
HAVE_X86_MMX) + check_cxx_source_compiles("int main() { __asm { xorps xmm0, xmm0 \
}; }" HAVE_X86_SSE) + check_cxx_source_compiles("int main() { __asm { xorpd xmm0, \
xmm0 }; }" HAVE_X86_SSE2) + check_cxx_source_compiles("int main() { __asm { femms \
}; }" HAVE_X86_3DNOW) +else(MSVC)
+ check_cxx_source_compiles(" #ifdef __SUNPRO_CC
+ #define __asm__ asm
+ #endif
+ int main() { __asm__(\"pxor %mm0, %mm0\") ; }" HAVE_X86_MMX)
+ check_cxx_source_compiles(" #ifdef __SUNPRO_CC
+ #define __asm__ asm
+ #endif
+ int main() { __asm__(\"xorps %xmm0, %xmm0\"); }" HAVE_X86_SSE)
+ check_cxx_source_compiles(" #ifdef __SUNPRO_CC
+ #define __asm__ asm
+ #endif
+ int main() { __asm__(\"xorpd %xmm0, %xmm0\"); }" HAVE_X86_SSE2)
+ check_cxx_source_compiles(" #ifdef __SUNPRO_CC
+ #define __asm__ asm
+ #endif
+ int main() { __asm__(\"femms\"); }" HAVE_X86_3DNOW)
+endif(MSVC)
+check_cxx_source_compiles(" #ifdef __SUNPRO_CC
+ #define __asm__ asm
+ #endif
+ int main() { __asm__(\"mtspr 256, %0; vand %%v0, %%v0, %%v0\" : : \"r\"(-1) ); \
}" HAVE_PPC_ALTIVEC) +
+configure_file(config-processor.h.cmake \
${CMAKE_CURRENT_BINARY_DIR}/config-processor.h ) +
+
+
+########### next target ###############
+
+file(MAKE_DIRECTORY
+ ${CMAKE_CURRENT_BINARY_DIR}/backends/fakehw
+ ${CMAKE_CURRENT_BINARY_DIR}/backends/hal
+ ${CMAKE_CURRENT_BINARY_DIR}/backends/udev
+ ${CMAKE_CURRENT_BINARY_DIR}/backends/wmi
+)
+
+if(WIN32)
+ add_definitions(-DYY_NO_UNISTD_H)
+endif(WIN32)
+
+set(solid_LIB_SRCS
+ solidnamespace.cpp
+ managerbase.cpp
+ device.cpp
+ devicemanager.cpp
+ deviceinterface.cpp
+ genericinterface.cpp
+ processor.cpp
+ block.cpp
+ storagedrive.cpp
+ opticaldrive.cpp
+ storagevolume.cpp
+ opticaldisc.cpp
+ storageaccess.cpp
+ camera.cpp
+ portablemediaplayer.cpp
+ networkinterface.cpp
+ networkshare.cpp
+ serialinterface.cpp
+ acadapter.cpp
+ battery.cpp
+ button.cpp
+ audiointerface.cpp
+ dvbinterface.cpp
+ predicate.cpp
+ predicateparse.cpp
+ predicate_lexer.c
+ predicate_parser.c
+ powermanagement.cpp
+ networking.cpp
+ video.cpp
+ smartcardreader.cpp
+ internetgateway.cpp
+
+ ifaces/acadapter.cpp
+ ifaces/audiointerface.cpp
+ ifaces/battery.cpp
+ ifaces/block.cpp
+ ifaces/button.cpp
+ ifaces/camera.cpp
+ ifaces/opticaldrive.cpp
+ ifaces/device.cpp
+ ifaces/deviceinterface.cpp
+ ifaces/devicemanager.cpp
+ ifaces/dvbinterface.cpp
+ ifaces/genericinterface.cpp
+ ifaces/networkinterface.cpp
+ ifaces/networkshare.cpp
+ ifaces/serialinterface.cpp
+ ifaces/opticaldisc.cpp
+ ifaces/portablemediaplayer.cpp
+ ifaces/processor.cpp
+ ifaces/storagedrive.cpp
+ ifaces/storagevolume.cpp
+ ifaces/storageaccess.cpp
+ ifaces/video.cpp
+ ifaces/smartcardreader.cpp
+ ifaces/internetgateway.cpp
+
+ backends/fakehw/fakeacadapter.cpp
+ backends/fakehw/fakeaudiointerface.cpp
+ backends/fakehw/fakebattery.cpp
+ backends/fakehw/fakeblock.cpp
+ backends/fakehw/fakebutton.cpp
+ backends/fakehw/fakecamera.cpp
+ backends/fakehw/fakecdrom.cpp
+ backends/fakehw/fakedevice.cpp
+ backends/fakehw/fakedeviceinterface.cpp
+ backends/fakehw/fakedvbinterface.cpp
+ backends/fakehw/fakegenericinterface.cpp
+ backends/fakehw/fakemanager.cpp
+ backends/fakehw/fakenetworkshare.cpp
+ backends/fakehw/fakenetworkinterface.cpp
+ backends/fakehw/fakeopticaldisc.cpp
+ backends/fakehw/fakeportablemediaplayer.cpp
+ backends/fakehw/fakeprocessor.cpp
+ backends/fakehw/fakestorage.cpp
+ backends/fakehw/fakestorageaccess.cpp
+ backends/fakehw/fakevideo.cpp
+ backends/fakehw/fakevolume.cpp
+ backends/fakehw/fakesmartcardreader.cpp
+
+ backends/shared/rootdevice.cpp
+ backends/shared/cpufeatures.cpp
+)
+
- if ( HUPNP_FOUND )
- include_directories( ${HUPNP_INCLUDE_DIR} )
- message(STATUS "Building Solid UPnP backend." )
- set(solid_LIB_SRCS ${solid_LIB_SRCS}
- backends/upnp/upnpdevice.cpp
- backends/upnp/upnpdevicemanager.cpp
- backends/upnp/upnpdeviceinterface.cpp
- backends/upnp/upnpmediaserver.cpp
- backends/upnp/upnpinternetgateway.cpp
- backends/upnp/upnpcontrolpoint.cpp
- )
- endif ()
++if(NOT APPLE)
++ set(HUPNP_ENABLED FALSE CACHE BOOL "explicitly enable the compilation of the \
hupnp backend") ++ if (HUPNP_ENABLED AND HUPNP_FOUND AND ((HUPNP_VERSION_MAJOR EQUAL \
0 AND HUPNP_VERSION_MINOR EQUAL 9) OR (HUPNP_VERSION_MAJOR EQUAL 1))) ++ \
include_directories( ${HUPNP_INCLUDE_DIR} ) ++ message(STATUS "Building Solid UPnP \
backend." ) ++ set(solid_LIB_SRCS ${solid_LIB_SRCS}
++ backends/upnp/upnpdevice.cpp
++ backends/upnp/upnpdevicemanager.cpp
++ backends/upnp/upnpdeviceinterface.cpp
++ backends/upnp/upnpmediaserver.cpp
++ backends/upnp/upnpinternetgateway.cpp
++ backends/upnp/upnpcontrolpoint.cpp
++ )
++ else ()
++ if(NOT HUPNP_ENABLED)
++ message(STATUS "To have UPnP support, explicitly enable HUPNP_ENABLED in \
the cmake cache") ++ endif(NOT HUPNP_ENABLED)
++ set(HUPNP_FOUND false)
++ endif ()
++endif(NOT APPLE)
+
+if(NOT WIN32 AND NOT APPLE)
+# message(STATUS "Building Solid KUPnP backend." )
+# set(solid_LIB_SRCS ${solid_LIB_SRCS}
+
+# backends/kupnp/cagibidbuscodec.cpp
+# backends/kupnp/cagibidevice.cpp
+# backends/kupnp/kupnpstorageaccess.cpp
+# backends/kupnp/kupnpdeviceinterface.cpp
+# backends/kupnp/mediaserver1.cpp
+# backends/kupnp/mediaserver2.cpp
+# backends/kupnp/mediaserver3.cpp
+# backends/kupnp/internetgatewaydevice1.cpp
+# backends/kupnp/kupnpdevice.cpp
+# backends/kupnp/kupnprootdevice.cpp
+# backends/kupnp/kupnpmanager.cpp
+
+# )
+
+ if ( UDEV_FOUND )
+ message(STATUS "Building Solid UDev backend." )
+ set(solid_LIB_SRCS ${solid_LIB_SRCS}
+ backends/udev/udevdevice.cpp
+ backends/udev/udevmanager.cpp
+ backends/udev/udevdeviceinterface.cpp
+ backends/udev/udevgenericinterface.cpp
+ backends/udev/cpuinfo.cpp
+ backends/udev/udevprocessor.cpp
+ backends/udev/udevcamera.cpp
+ backends/udev/udevvideo.cpp
+ backends/udev/udevaudiointerface.cpp
+ backends/udev/udevnetworkinterface.cpp
+ backends/udev/udevserialinterface.cpp
+ backends/udev/udevaudiointerface_p.cpp
+ backends/udev/udevportablemediaplayer.cpp
+ backends/udev/udevdvbinterface.cpp
+ backends/udev/udevblock.cpp
+ backends/shared/udevqtclient.cpp
+ backends/shared/udevqtdevice.cpp
+ )
++ set(UDEV_DETAILED_OUTPUT OFF CACHE BOOL "provide extended output regarding \
udev events") ++ if(UDEV_DETAILED_OUTPUT)
++ add_definitions(-DUDEV_DETAILED_OUTPUT)
++ endif(UDEV_DETAILED_OUTPUT)
+
+ # check for media-player-info (runtime-only optional dependency)
+ find_package(MediaPlayerInfo)
+ set_package_properties(MediaPlayerInfo PROPERTIES
+ DESCRIPTION "Enables identification and querying of portable media \
players" + PURPOSE "Runtime-only dependency of the udev solid backend. \
Support for m-p-i is included even if not found during build" + URL \
"http://www.freedesktop.org/wiki/Software/media-player-info" + TYPE RUNTIME
+ )
+ endif( UDEV_FOUND )
+
+
+ message(STATUS "Building Solid HAL backend." )
+ set(solid_LIB_SRCS ${solid_LIB_SRCS}
+ backends/hal/halacadapter.cpp
+ backends/hal/halaudiointerface.cpp
+ backends/hal/halbattery.cpp
+ backends/hal/halblock.cpp
+ backends/hal/halbutton.cpp
+ backends/hal/halcamera.cpp
+ backends/hal/halcdrom.cpp
+ backends/hal/haldeviceinterface.cpp
+ backends/hal/haldvbinterface.cpp
+ backends/hal/halfstabhandling.cpp
+ backends/hal/halgenericinterface.cpp
+ backends/hal/haldevice.cpp
+ backends/hal/halmanager.cpp
+ backends/hal/halnetworkinterface.cpp
+ backends/hal/halserialinterface.cpp
+ backends/hal/halopticaldisc.cpp
+ backends/hal/halportablemediaplayer.cpp
+ backends/hal/halprocessor.cpp
+ backends/hal/halstorageaccess.cpp
+ backends/hal/halstorage.cpp
+ backends/hal/halvideo.cpp
+ backends/hal/halvolume.cpp
+ backends/hal/halsmartcardreader.cpp
+ )
+
+ message(STATUS "Building Solid UPower backend." )
+ set(solid_LIB_SRCS ${solid_LIB_SRCS}
+ backends/upower/upowermanager.cpp
+ backends/upower/upowerdevice.cpp
+ backends/upower/upowerbattery.cpp
+ backends/upower/upoweracadapter.cpp
+ backends/upower/upowerdeviceinterface.cpp
+ backends/upower/upowergenericinterface.cpp
+ )
+
+ # FIXME: this should work on more Unix systems
+ if (CMAKE_SYSTEM_NAME MATCHES Linux)
+ if ( WITH_SOLID_UDISKS2 )
+ message(STATUS "Building Solid UDisks2 backend." )
+ add_definitions(-DWITH_SOLID_UDISKS2)
+ set(solid_LIB_SRCS ${solid_LIB_SRCS}
+ backends/udisks2/udisksmanager.cpp
+ backends/udisks2/udisksdevice.cpp
+ backends/udisks2/udisksdevicebackend.cpp
+ backends/udisks2/udisksblock.cpp
+ backends/udisks2/udisksstoragevolume.cpp
+ backends/udisks2/udisksdeviceinterface.cpp
+ backends/udisks2/udisksopticaldisc.cpp
+ backends/udisks2/udisksopticaldrive.cpp
+ backends/udisks2/udisksstoragedrive.cpp
+ backends/udisks2/udisksstorageaccess.cpp
+ backends/udisks2/udisksgenericinterface.cpp
+ backends/udisks2/dbus/manager.cpp
+ )
+ else ( WITH_SOLID_UDISKS2 )
+ message(STATUS "Building Solid UDisks backend." )
+ set(solid_LIB_SRCS ${solid_LIB_SRCS}
+ backends/udisks/udisksmanager.cpp
+ backends/udisks/udisksdevice.cpp
+ backends/udisks/udisksblock.cpp
+ backends/udisks/udisksstoragevolume.cpp
+ backends/udisks/udisksdeviceinterface.cpp
+ backends/udisks/udisksopticaldisc.cpp
+ backends/udisks/udisksopticaldrive.cpp
+ backends/udisks/udisksstoragedrive.cpp
+ backends/udisks/udisksstorageaccess.cpp
+ backends/udisks/udisksgenericinterface.cpp
+ )
+ endif ( WITH_SOLID_UDISKS2 )
+ endif (CMAKE_SYSTEM_NAME MATCHES Linux)
+
+ message(STATUS "Building Solid fstab backend." )
+ set(solid_LIB_SRCS ${solid_LIB_SRCS}
+ backends/fstab/fstabmanager.cpp
+ backends/fstab/fstabdevice.cpp
+ backends/fstab/fstabnetworkshare.cpp
+ backends/fstab/fstabstorageaccess.cpp
+ backends/fstab/fstabhandling.cpp
+ backends/fstab/fstabwatcher.cpp
+ )
+
+endif(NOT WIN32 AND NOT APPLE)
+
+include(CheckIncludeFiles)
+include(CheckFunctionExists)
+include(CheckCXXSourceCompiles)
+
+check_include_files(mntent.h HAVE_MNTENT_H)
+check_include_files(sys/types.h HAVE_SYS_TYPES_H)
+check_include_files(sys/param.h HAVE_SYS_PARAM_H)
+check_include_files("stdio.h;sys/mnttab.h" HAVE_SYS_MNTTAB_H)
+check_include_files("sys/param.h;sys/mount.h" HAVE_SYS_MOUNT_H)
+
+check_function_exists(getmntinfo HAVE_GETMNTINFO)
+
+check_function_exists(setmntent HAVE_SETMNTENT)
+
+check_cxx_source_compiles("
+ #include <sys/types.h>
+ #include <sys/statvfs.h>
+ int main(){
+ struct statvfs *mntbufp;
+ int flags;
+ return getmntinfo(&mntbufp, flags);
+ }
+" GETMNTINFO_USES_STATVFS )
+
+configure_file( config-solid.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-solid.h )
+
+if(APPLE)
+ find_package(IOKit REQUIRED)
+
+ message(STATUS "-- Building Solid IOKit backend." )
+ set(solid_LIB_SRCS ${solid_LIB_SRCS}
+ backends/iokit/iokitmanager.cpp
+ backends/iokit/iokitdevice.cpp
+ backends/iokit/cfhelper.cpp
+ backends/iokit/iokitdeviceinterface.cpp
+ backends/iokit/iokitgenericinterface.cpp
+ backends/iokit/iokitprocessor.cpp
+ backends/iokit/iokitnetworkinterface.cpp
+ backends/iokit/iokitserialinterface.cpp
+ backends/iokit/iokitbattery.cpp
+ )
+endif(APPLE)
+
+if(WIN32)
+
+ if(WBEM_FOUND)
+ set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DHAVE_WBEM")
+ message(STATUS "-- Building Solid WMI backend." )
+
+ set(solid_LIB_SRCS ${solid_LIB_SRCS}
+ backends/wmi/wmiacadapter.cpp
+ backends/wmi/wmiaudiointerface.cpp
+ backends/wmi/wmibattery.cpp
+ backends/wmi/wmiblock.cpp
+ backends/wmi/wmibutton.cpp
+ backends/wmi/wmicamera.cpp
+ backends/wmi/wmicdrom.cpp
+ backends/wmi/wmideviceinterface.cpp
+ backends/wmi/wmidvbinterface.cpp
+ backends/wmi/wmigenericinterface.cpp
+ backends/wmi/wmidevice.cpp
+ backends/wmi/wmimanager.cpp
+ backends/wmi/wminetworkinterface.cpp
+ backends/wmi/wmiopticaldisc.cpp
+ backends/wmi/wmiportablemediaplayer.cpp
+ backends/wmi/wmiprocessor.cpp
+ backends/wmi/wmiquery.cpp
+ backends/wmi/wmistorageaccess.cpp
+ backends/wmi/wmistorage.cpp
+ backends/wmi/wmivideo.cpp
+ backends/wmi/wmivolume.cpp
+ )
+ endif()
+endif(WIN32)
+
+set_source_files_properties( org.freedesktop.PowerManagement.xml
+ org.freedesktop.PowerManagement.Inhibit.xml
+ org.kde.Solid.PowerManagement.PolicyAgent.xml
+ PROPERTIES NO_NAMESPACE TRUE)
+
+qt4_add_dbus_interfaces(solid_LIB_SRCS org.freedesktop.PowerManagement.xml
+ org.freedesktop.PowerManagement.Inhibit.xml
+ org.kde.Solid.PowerManagement.PolicyAgent.xml)
+
+qt4_add_dbus_interface(solid_LIB_SRCS org.kde.Solid.Networking.Client.xml
+ org_kde_solid_networking_client)
+
+set(solid_OPTIONAL_LIBS)
+
+if(WIN32)
+ if(WBEM_FOUND)
+ set(solid_OPTIONAL_LIBS ${solid_OPTIONAL_LIBS} ${WBEM_LIBRARIES})
+ endif()
+endif()
+
+if(APPLE)
+ set(solid_OPTIONAL_LIBS ${IOKIT_LIBRARY})
+endif()
+
+if ( HUPNP_FOUND )
+ set(solid_OPTIONAL_LIBS ${solid_OPTIONAL_LIBS} ${HUPNP_LIBS})
+endif()
+
+if ( UDEV_FOUND )
+ set(solid_OPTIONAL_LIBS ${solid_OPTIONAL_LIBS} ${UDEV_LIBS})
+endif ()
+
+add_library(Solid ${solid_LIB_SRCS})
+target_include_directories(Solid PUBLIC \
"$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/..;${CMAKE_CURRENT_BINARY_DIR}/..>") \
+generate_export_header(Solid) +
+target_link_libraries(Solid LINK_PUBLIC ${QT_QTCORE_LIBRARY}
+ LINK_PRIVATE ${QT_QTDBUS_LIBRARY}
+ ${QT_QTXML_LIBRARY}
+ Qt5::Widgets
+ ${solid_OPTIONAL_LIBS}
+ )
+
+set_target_properties(Solid PROPERTIES VERSION ${SOLID_VERSION_STRING}
+ SOVERSION ${SOLID_SOVERSION} )
+
+install(TARGETS Solid EXPORT SolidTargets ${INSTALL_TARGETS_DEFAULT_ARGS})
+
+install(FILES org.kde.Solid.Networking.Client.xml
+ org.kde.Solid.PowerManagement.PolicyAgent.xml
+ org.freedesktop.PowerManagement.xml
+ org.freedesktop.PowerManagement.Inhibit.xml
+ DESTINATION ${DBUS_INTERFACES_INSTALL_DIR})
+
+########### static lib for tests ###############
+
+add_library(Solid_static STATIC ${solid_LIB_SRCS})
+set_target_properties(Solid_static PROPERTIES COMPILE_FLAGS \
-DSOLID_STATIC_DEFINE=1) +
+target_link_libraries(Solid_static LINK_PUBLIC ${QT_QTCORE_LIBRARY})
+target_link_libraries(Solid_static ${QT_QTDBUS_LIBRARY} ${QT_QTXML_LIBRARY} \
Qt5::Widgets ${solid_OPTIONAL_LIBS}) +target_include_directories(Solid_static PUBLIC \
"$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/..;${CMAKE_CURRENT_BINARY_DIR}/..>") \
+ +########### install files ###############
+
+install( FILES ${CMAKE_CURRENT_BINARY_DIR}/solid_export.h solidnamespace.h device.h
+ devicenotifier.h deviceinterface.h genericinterface.h processor.h \
block.h + storageaccess.h storagedrive.h opticaldrive.h \
storagevolume.h opticaldisc.h + camera.h portablemediaplayer.h \
networkinterface.h acadapter.h battery.h + button.h audiointerface.h \
dvbinterface.h predicate.h powermanagement.h + networking.h video.h \
serialinterface.h smartcardreader.h internetgateway.h + networkshare.h
+ DESTINATION ${INCLUDE_INSTALL_DIR}/solid COMPONENT Devel)
+
+########### parser build ###############
+
+set(lexer_FILE predicate_lexer)
+set(parser_FILE predicate_parser)
+
+find_package(FLEX)
+set_package_properties(FLEX PROPERTIES DESCRIPTION "Allows the Solid predicate \
parser to be updated" + URL "http://flex.sourceforge.net"
+ TYPE OPTIONAL
+ PURPOSE "Required by the UpdateSolidPredicateParser target \
(mainly useful for developers)" + )
+
+find_package(BISON)
+set_package_properties(BISON PROPERTIES
+ URL "http://www.gnu.org/software/bison"
+ DESCRIPTION "Allows the Solid predicate parser to be updated"
+ TYPE OPTIONAL
+ PURPOSE "Required by the UpdateSolidPredicateParser target (mainly useful for \
developers)" + )
+
+if (FLEX_FOUND AND BISON_FOUND)
+
+ add_custom_target(UpdateSolidPredicateParser
+ COMMAND ${FLEX_EXECUTABLE} -P Solid -o${lexer_FILE}.c \
${lexer_FILE}.l + COMMAND ${BISON_EXECUTABLE} -p Solid -d -b \
${parser_FILE} ${parser_FILE}.y + COMMAND ${CMAKE_COMMAND} -E \
copy ${parser_FILE}.tab.c ${CMAKE_CURRENT_SOURCE_DIR}/${parser_FILE}.c + \
COMMAND ${CMAKE_COMMAND} -E copy ${parser_FILE}.tab.h \
${CMAKE_CURRENT_SOURCE_DIR}/${parser_FILE}.h + COMMAND \
${CMAKE_COMMAND} -E remove ${parser_FILE}.tab.c ${parser_FILE}.tab.h + \
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) +
+else ()
+ add_custom_target(UpdateSolidPredicateParser
+ COMMAND echo "flex and/or bison not found, so target \
UpdateSolidPredicateParser inactive") +endif ()
+
diff --cc tier1/solid/src/solid/backends/udev/udevaudiointerface.cpp
index 4f53f2e,0000000..c7e6eaf
mode 100644,000000..100644
--- a/tier1/solid/src/solid/backends/udev/udevaudiointerface.cpp
+++ b/tier1/solid/src/solid/backends/udev/udevaudiointerface.cpp
@@@ -1,86 -1,0 +1,87 @@@
+/*
+ Copyright 2010 Alex Fiestas <alex@eyeos.org>
+ Copyright 2010 UFO Coders <info@ufocoders.com>
+
+ This library 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.1 of the License, or (at your option) version 3, or any
+ later version accepted by the membership of KDE e.V. (or its
+ successor approved by the membership of KDE e.V.), which shall
+ act as a proxy defined in Section 6 of version 3 of the license.
+
+ 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "udevaudiointerface.h"
+
+#include "udevaudiointerface_p.h"
+#include "udevdevice.h"
+
+#include <QFile>
++#include <QDebug>
+
+using namespace Solid::Backends::UDev;
+
+AudioInterface::AudioInterface(UDevDevice *device)
+ : DeviceInterface(device),
+ d(new UdevAudioInterfacePrivate(device))
+{
+}
+
+AudioInterface::~AudioInterface()
+{
+
+}
+
+Solid::AudioInterface::AudioDriver AudioInterface::driver() const
+{
+ return d->m_driver;
+}
+
+QVariant AudioInterface::driverHandle() const
+{
+ if (d->m_driver == Solid::AudioInterface::Alsa)
+ {
+ QList<QVariant> list;
+ if (d->m_cardnum != -1) {
- list << QVariant(d->m_cardnum);
- if (d->m_devicenum != -1) {
- list << QVariant(d->m_devicenum);
- }
++ QVariant card_id = d->m_cardnum;
++ QVariant dev_id = d->m_devicenum != -1 ? d->m_devicenum : QVariant();
++ QVariant subdev_id;
++ list << card_id << dev_id << subdev_id;
+ } else if(!d->m_deviceFile.isEmpty()) {
+ list << QVariant(d->m_deviceFile);
+ }
+ return list;
+ }
+ else if (d->m_driver == Solid::AudioInterface::OpenSoundSystem)
+ {
+ if (!d->m_deviceFile.isEmpty()) {
+ return QVariant(d->m_deviceFile);
+ }
+ }
+
+ return QVariant();
+}
+
+QString AudioInterface::name() const
+{
+ return d->m_name;
+}
+
+Solid::AudioInterface::AudioInterfaceTypes AudioInterface::deviceType() const
+{
+ return d->m_type;
+}
+
+Solid::AudioInterface::SoundcardType AudioInterface::soundcardType() const
+{
+ return d->soundcardType();
+}
+
diff --cc tier1/solid/src/solid/backends/udev/udevmanager.cpp
index 0faf9b3,0000000..1446868
mode 100644,000000..100644
--- a/tier1/solid/src/solid/backends/udev/udevmanager.cpp
+++ b/tier1/solid/src/solid/backends/udev/udevmanager.cpp
@@@ -1,211 -1,0 +1,209 @@@
+/*
+ Copyright 2010 Rafael Fernández López <ereslibre@kde.org>
+
+ This library 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.1 of the License, or (at your option) version 3, or any
+ later version accepted by the membership of KDE e.V. (or its
+ successor approved by the membership of KDE e.V.), which shall
+ act as a proxy defined in Section 6 of version 3 of the license.
+
+ 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "udevmanager.h"
+
+#include "udev.h"
+#include "udevdevice.h"
+#include "../shared/rootdevice.h"
+
+#include <QtCore/QCoreApplication>
+#include <QtCore/QSet>
+#include <QtCore/QFile>
+#include <QtCore/QDebug>
+
- #define DETAILED_OUTPUT 0
-
+using namespace Solid::Backends::UDev;
+using namespace Solid::Backends::Shared;
+
+class UDevManager::Private
+{
+public:
+ Private();
+ ~Private();
+
+ bool isOfInterest(const UdevQt::Device &device);
+
+ UdevQt::Client *m_client;
+ QSet<Solid::DeviceInterface::Type> m_supportedInterfaces;
+};
+
+UDevManager::Private::Private()
+{
+ QStringList subsystems;
+ subsystems << "processor";
+ subsystems << "sound";
+ subsystems << "tty";
+ subsystems << "dvb";
+ subsystems << "video4linux";
+ subsystems << "net";
+ subsystems << "usb";
+ m_client = new UdevQt::Client(subsystems);
+}
+
+UDevManager::Private::~Private()
+{
+ delete m_client;
+}
+
+bool UDevManager::Private::isOfInterest(const UdevQt::Device &device)
+{
- #if DETAILED_OUTPUT
++#ifdef UDEV_DETAILED_OUTPUT
+ qDebug() << "<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<";
+ qDebug() << "Path:" << device.sysfsPath();
+ qDebug() << "Properties:" << device.deviceProperties();
+ Q_FOREACH (const QString &key, device.deviceProperties()) {
+ qDebug() << "\t" << key << ":" << device.deviceProperty(key).toString();
+ }
+ qDebug() << "Driver:" << device.driver();
+ qDebug() << "Subsystem:" << device.subsystem();
+ qDebug() << ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>";
+#endif
+ if (device.driver() == QLatin1String("processor")) {
+ // Linux ACPI reports processor slots, rather than processors.
+ // Empty slots will not have a system device associated with them.
+ return QFile::exists(device.sysfsPath() + "/sysdev");
+ }
+ if (device.subsystem() == QLatin1String("sound") &&
+ device.deviceProperty("SOUND_FORM_FACTOR").toString() != "internal") {
+ return true;
+ }
+
+ if (device.subsystem() == QLatin1String("tty")) {
+ QString path = device.deviceProperty("DEVPATH").toString();
+
+ int lastSlash = path.length() - path.lastIndexOf(QLatin1String("/")) -1;
+ QByteArray lastElement = path.right(lastSlash).toLatin1();
+
+ if (lastElement.startsWith("tty") && !path.startsWith("/devices/virtual")) \
{ + return true;
+ }
+ }
+ return device.subsystem() == QLatin1String("dvb") ||
+ device.subsystem() == QLatin1String("video4linux") ||
+ device.subsystem() == QLatin1String("net") ||
+ device.deviceProperty("ID_MEDIA_PLAYER").toString().isEmpty() == false \
|| // media-player-info recognized devices + \
device.deviceProperty("ID_GPHOTO2").toInt() == 1; // GPhoto2 cameras +}
+
+UDevManager::UDevManager(QObject *parent)
+ : Solid::Ifaces::DeviceManager(parent),
+ d(new Private)
+{
+ connect(d->m_client, SIGNAL(deviceAdded(UdevQt::Device)), this, \
SLOT(slotDeviceAdded(UdevQt::Device))); + connect(d->m_client, \
SIGNAL(deviceRemoved(UdevQt::Device)), this, \
SLOT(slotDeviceRemoved(UdevQt::Device))); +
+ d->m_supportedInterfaces << Solid::DeviceInterface::GenericInterface
+ << Solid::DeviceInterface::Processor
+ << Solid::DeviceInterface::AudioInterface
+ << Solid::DeviceInterface::NetworkInterface
+ << Solid::DeviceInterface::SerialInterface
+ << Solid::DeviceInterface::Camera
+ << Solid::DeviceInterface::PortableMediaPlayer
+ << Solid::DeviceInterface::DvbInterface
+ << Solid::DeviceInterface::Block
+ << Solid::DeviceInterface::Video;
+}
+
+UDevManager::~UDevManager()
+{
+ delete d;
+}
+
+QString UDevManager::udiPrefix() const
+{
+ return QString::fromLatin1(UDEV_UDI_PREFIX);
+}
+
+QSet<Solid::DeviceInterface::Type> UDevManager::supportedInterfaces() const
+{
+ return d->m_supportedInterfaces;
+}
+
+QStringList UDevManager::allDevices()
+{
+ QStringList res;
+ const UdevQt::DeviceList deviceList = d->m_client->allDevices();
+ Q_FOREACH (const UdevQt::Device &device, deviceList) {
+ if (d->isOfInterest(device)) {
+ res << udiPrefix() + device.sysfsPath();
+ }
+ }
+ return res;
+}
+
+QStringList UDevManager::devicesFromQuery(const QString &parentUdi,
+ Solid::DeviceInterface::Type type)
+{
+ QStringList allDev = allDevices();
+ QStringList result;
+
+ if (!parentUdi.isEmpty()) {
+ Q_FOREACH (const QString &udi, allDev) {
+ UDevDevice device(d->m_client->deviceBySysfsPath(udi.right(udi.size() - \
udiPrefix().size()))); + if (device.queryDeviceInterface(type) && \
device.parentUdi() == parentUdi) { + result << udi;
+ }
+ }
+
+ return result;
+ } else if (type != Solid::DeviceInterface::Unknown) {
+ Q_FOREACH (const QString &udi, allDev) {
+ UDevDevice device(d->m_client->deviceBySysfsPath(udi.right(udi.size() - \
udiPrefix().size()))); + if (device.queryDeviceInterface(type)) {
+ result << udi;
+ }
+ }
+
+ return result;
+ } else {
+ return allDev;
+ }
+}
+
+QObject *UDevManager::createDevice(const QString &udi_)
+{
+ if (udi_ == udiPrefix()) {
+ RootDevice *const device = new RootDevice(UDEV_UDI_PREFIX);
+ device->setProduct(QCoreApplication::translate("", "Devices"));
+ device->setDescription(QCoreApplication::translate("", "Devices declared in \
your system")); + device->setIcon("computer");
+ return device;
+ }
+ const QString udi = udi_.right(udi_.size() - udiPrefix().size());
+ UdevQt::Device device = d->m_client->deviceBySysfsPath(udi);
+ if (d->isOfInterest(device) || QFile::exists(udi)) {
+ return new UDevDevice(device);
+ }
+ return 0;
+}
+
+void UDevManager::slotDeviceAdded(const UdevQt::Device &device)
+{
+ if (d->isOfInterest(device)) {
+ Q_EMIT deviceAdded(udiPrefix() + device.sysfsPath());
+ }
+}
+
+void UDevManager::slotDeviceRemoved(const UdevQt::Device &device)
+{
+ if (d->isOfInterest(device)) {
+ Q_EMIT deviceRemoved(udiPrefix() + device.sysfsPath());
+ }
+}
diff --cc tier1/solid/src/solid/backends/udisks/udisksdevice.cpp
index 2c30ecd,0000000..e1c47af
mode 100644,000000..100644
--- a/tier1/solid/src/solid/backends/udisks/udisksdevice.cpp
+++ b/tier1/solid/src/solid/backends/udisks/udisksdevice.cpp
@@@ -1,778 -1,0 +1,774 @@@
+/*
+ Copyright 2010 Michael Zanetti <mzanetti@kde.org>
+ Copyright 2010-2011 Lukas Tinkl <ltinkl@redhat.com>
+
+ This library 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.1 of the License, or (at your option) version 3, or any
+ later version accepted by the membership of KDE e.V. (or its
+ successor approved by the membership of KDE e.V.), which shall
+ act as a proxy defined in Section 6 of version 3 of the license.
+
+ 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "udisks.h"
+#include "udisksdevice.h"
+#include "udisksdeviceinterface.h"
+#include "udisksstoragevolume.h"
+#include "udisksopticaldisc.h"
+#include "udisksopticaldrive.h"
+#include "udisksstorageaccess.h"
+#include "udisksgenericinterface.h"
+
+#include <solid/genericinterface.h>
+#include <solid/deviceinterface.h>
+#include <solid/device.h>
+
+#include <QtCore/QCoreApplication>
+#include <QtCore/QStringList>
+#include <QtCore/QDebug>
+
+#include <QtDBus/QDBusMessage>
+#include <QtDBus/QDBusMetaType>
+#include <QtDBus/QDBusPendingReply>
+
+using namespace Solid::Backends::UDisks;
+
+// Adapted from KLocale as Solid needs to be Qt-only
+static QString formatByteSize(double size)
+{
+ // Per IEC 60027-2
+
+ // Binary prefixes
+ //Tebi-byte TiB 2^40 1,099,511,627,776 bytes
+ //Gibi-byte GiB 2^30 1,073,741,824 bytes
+ //Mebi-byte MiB 2^20 1,048,576 bytes
+ //Kibi-byte KiB 2^10 1,024 bytes
+
+ QString s;
+ // Gibi-byte
+ if ( size >= 1073741824.0 )
+ {
+ size /= 1073741824.0;
+ if ( size > 1024 ) // Tebi-byte
+ s = QCoreApplication::translate("", "%1 \
TiB").arg(QLocale().toString(size / 1024.0, 'f', 1)); + else
+ s = QCoreApplication::translate("", "%1 \
GiB").arg(QLocale().toString(size, 'f', 1)); + }
+ // Mebi-byte
+ else if ( size >= 1048576.0 )
+ {
+ size /= 1048576.0;
+ s = QCoreApplication::translate("", "%1 MiB").arg(QLocale().toString(size, \
'f', 1)); + }
+ // Kibi-byte
+ else if ( size >= 1024.0 )
+ {
+ size /= 1024.0;
+ s = QCoreApplication::translate("", "%1 KiB").arg(QLocale().toString(size, \
'f', 1)); + }
+ // Just byte
+ else if ( size > 0 )
+ {
+ s = QCoreApplication::translate("", "%1 B").arg(QLocale().toString(size, \
'f', 1)); + }
+ // Nothing
+ else
+ {
+ s = QCoreApplication::translate("", "0 B");
+ }
+ return s;
+}
+
+UDisksDevice::UDisksDevice(const QString &udi)
+ : Solid::Ifaces::Device()
+ , m_udi(udi)
+{
+ QString realUdi = m_udi;
+ if (realUdi.endsWith(":media")) {
+ realUdi.chop(6);
+ }
+ m_device = new QDBusInterface(UD_DBUS_SERVICE, realUdi,
+ UD_DBUS_INTERFACE_DISKS_DEVICE,
+ QDBusConnection::systemBus());
+
+ if (m_device->isValid())
+ connect(m_device, SIGNAL(Changed()), this, SLOT(slotChanged()));
+}
+
+UDisksDevice::~UDisksDevice()
+{
+ delete m_device;
+}
+
+QObject* UDisksDevice::createDeviceInterface(const Solid::DeviceInterface::Type& \
type) +{
+ if (!queryDeviceInterface(type)) {
+ return 0;
+ }
+
+ DeviceInterface *iface = 0;
+ switch (type)
+ {
+ case Solid::DeviceInterface::GenericInterface:
+ iface = new GenericInterface(this);
+ break;
+ case Solid::DeviceInterface::Block:
+ iface = new Block(this);
+ break;
+ case Solid::DeviceInterface::StorageAccess:
+ iface = new UDisksStorageAccess(this);
+ break;
+ case Solid::DeviceInterface::StorageDrive:
+ iface = new UDisksStorageDrive(this);
+ break;
+ case Solid::DeviceInterface::OpticalDrive:
+ iface = new UDisksOpticalDrive(this);
+ break;
+ case Solid::DeviceInterface::StorageVolume:
+ iface = new UDisksStorageVolume(this);
+ break;
+ case Solid::DeviceInterface::OpticalDisc:
+ iface = new OpticalDisc(this);
+ break;
+ default:
+ break;
+ }
+ return iface;
+}
+
+bool UDisksDevice::queryDeviceInterface(const Solid::DeviceInterface::Type& type) \
const +{
+ switch (type) {
+ case Solid::DeviceInterface::GenericInterface:
+ return true;
+ case Solid::DeviceInterface::Block:
+ return prop("DeviceMajor").toInt() != -1;
+ case Solid::DeviceInterface::StorageVolume:
+ if (prop("DeviceIsOpticalDisc").toBool()) {
+ return m_udi.endsWith(":media");
+ } else {
+ return prop("DeviceIsPartition").toBool()
+ || prop("IdUsage").toString()=="filesystem"
+ || prop("IdUsage").toString()=="crypto";
+ }
+
+ case Solid::DeviceInterface::StorageAccess:
+ if (prop("DeviceIsOpticalDisc").toBool()) {
+ return prop("IdUsage").toString()=="filesystem"
+ && m_udi.endsWith(":media");
+
+ } else {
+ return prop("IdUsage").toString()=="filesystem"
+ || prop("IdUsage").toString()=="crypto";
+ }
+
+ case Solid::DeviceInterface::StorageDrive:
+ return !m_udi.endsWith(":media") && prop("DeviceIsDrive").toBool();
+ case Solid::DeviceInterface::OpticalDrive:
+ return !m_udi.endsWith(":media")
+ && prop( "DeviceIsDrive" ).toBool()
+ && !prop( "DriveMediaCompatibility" ).toStringList().filter( \
"optical_" ).isEmpty(); + case Solid::DeviceInterface::OpticalDisc:
+ return m_udi.endsWith(":media") && \
prop("DeviceIsOpticalDisc").toBool(); + default:
+ return false;
+ }
+}
+
+QStringList UDisksDevice::emblems() const
+{
+ QStringList res;
+
+ if (queryDeviceInterface(Solid::DeviceInterface::StorageAccess))
+ {
+
+ bool isEncrypted = false;
+ if (queryDeviceInterface(Solid::DeviceInterface::StorageVolume))
+ {
+ const UDisks::UDisksStorageVolume volIface(const_cast<UDisksDevice \
*>(this)); + isEncrypted = (volIface.usage() == \
Solid::StorageVolume::Encrypted); + }
+
+ const UDisks::UDisksStorageAccess accessIface(const_cast<UDisksDevice \
*>(this)); + if (accessIface.isAccessible())
+ {
+ if (isEncrypted)
+ res << "emblem-encrypted-unlocked";
+ else
+ res << "emblem-mounted";
+ }
+ else
+ {
+ if (isEncrypted)
+ res << "emblem-encrypted-locked";
+ else
+ res << "emblem-unmounted";
+ }
+ }
+
+ return res;
+}
+
+QString UDisksDevice::description() const
+{
+ if (queryDeviceInterface(Solid::DeviceInterface::StorageDrive))
+ return storageDescription();
+ else if (queryDeviceInterface(Solid::DeviceInterface::StorageVolume))
+ return volumeDescription();
+ else
+ return product();
+}
+
+QString UDisksDevice::storageDescription() const
+{
+ QString description;
+ const UDisks::UDisksStorageDrive storageDrive(const_cast<UDisksDevice*>(this));
+ Solid::StorageDrive::DriveType drive_type = storageDrive.driveType();
+ bool drive_is_hotpluggable = storageDrive.isHotpluggable();
+ const UDisks::UDisksStorageVolume \
storageVolume(const_cast<UDisksDevice*>(this)); +
+ if (drive_type == Solid::StorageDrive::CdromDrive)
+ {
+ const UDisks::UDisksOpticalDrive \
opticalDrive(const_cast<UDisksDevice*>(this)); + \
Solid::OpticalDrive::MediumTypes mediumTypes = opticalDrive.supportedMedia(); + \
QString first; + QString second;
+
+ first = QCoreApplication::translate("", "CD-ROM", "First item of %1%2 Drive \
sentence"); + if (mediumTypes & Solid::OpticalDrive::Cdr)
+ first = QCoreApplication::translate("", "CD-R", "First item of %1%2 \
Drive sentence"); + if (mediumTypes & Solid::OpticalDrive::Cdrw)
+ first = QCoreApplication::translate("", "CD-RW", "First item of %1%2 \
Drive sentence"); +
+ if (mediumTypes & Solid::OpticalDrive::Dvd)
+ second = QCoreApplication::translate("", "/DVD-ROM", "Second item of \
%1%2 Drive sentence"); + if (mediumTypes & Solid::OpticalDrive::Dvdplusr)
+ second = QCoreApplication::translate("", "/DVD+R", "Second item of %1%2 \
Drive sentence"); + if (mediumTypes & Solid::OpticalDrive::Dvdplusrw)
+ second = QCoreApplication::translate("", "/DVD+RW", "Second item of \
%1%2 Drive sentence"); + if (mediumTypes & Solid::OpticalDrive::Dvdr)
+ second = QCoreApplication::translate("", "/DVD-R", "Second item of %1%2 \
Drive sentence"); + if (mediumTypes & Solid::OpticalDrive::Dvdrw)
+ second = QCoreApplication::translate("", "/DVD-RW", "Second item of \
%1%2 Drive sentence"); + if (mediumTypes & Solid::OpticalDrive::Dvdram)
+ second = QCoreApplication::translate("", "/DVD-RAM", "Second item of \
%1%2 Drive sentence"); + if ((mediumTypes & Solid::OpticalDrive::Dvdr) && \
(mediumTypes & Solid::OpticalDrive::Dvdplusr)) + {
+ if(mediumTypes & Solid::OpticalDrive::Dvdplusdl)
+ second = QObject::trUtf8("/DVD ±R DL", "Second item of %1%2 Drive \
sentence"); + else
+ second = QObject::trUtf8("/DVD ±R", "Second item of %1%2 Drive \
sentence"); + }
+ if ((mediumTypes & Solid::OpticalDrive::Dvdrw) && (mediumTypes & \
Solid::OpticalDrive::Dvdplusrw)) + {
+ if((mediumTypes & Solid::OpticalDrive::Dvdplusdl) || (mediumTypes & \
Solid::OpticalDrive::Dvdplusdlrw)) + second = QObject::trUtf8("/DVD \
±RW DL", "Second item of %1%2 Drive sentence"); + else
+ second = QObject::trUtf8("/DVD ±RW", "Second item of %1%2 Drive \
sentence"); + }
+ if (mediumTypes & Solid::OpticalDrive::Bd)
+ second = QCoreApplication::translate("", "/BD-ROM", "Second item of \
%1%2 Drive sentence"); + if (mediumTypes & Solid::OpticalDrive::Bdr)
+ second = QCoreApplication::translate("", "/BD-R", "Second item of %1%2 \
Drive sentence"); + if (mediumTypes & Solid::OpticalDrive::Bdre)
+ second = QCoreApplication::translate("", "/BD-RE", "Second item of %1%2 \
Drive sentence"); + if (mediumTypes & Solid::OpticalDrive::HdDvd)
+ second = QCoreApplication::translate("", "/HD DVD-ROM", "Second item of \
%1%2 Drive sentence"); + if (mediumTypes & Solid::OpticalDrive::HdDvdr)
+ second = QCoreApplication::translate("", "/HD DVD-R", "Second item of \
%1%2 Drive sentence"); + if (mediumTypes & Solid::OpticalDrive::HdDvdrw)
+ second = QCoreApplication::translate("", "/HD DVD-RW", "Second item of \
%1%2 Drive sentence"); +
+ if (drive_is_hotpluggable)
+ description = QCoreApplication::translate("", "External %1%2 Drive", \
"%1 is CD-ROM/CD-R/etc; %2 is '/DVD-ROM'/'/DVD-R'/etc (with leading \
slash)").arg(first).arg(second); + else
+ description = QCoreApplication::translate("", "%1%2 Drive", "%1 is \
CD-ROM/CD-R/etc; %2 is '/DVD-ROM'/'/DVD-R'/etc (with leading \
slash)").arg(first).arg(second); +
+ return description;
+ }
+
+ if (drive_type == Solid::StorageDrive::Floppy)
+ {
+ if (drive_is_hotpluggable)
+ description = QCoreApplication::translate("", "External Floppy Drive");
+ else
+ description = QCoreApplication::translate("", "Floppy Drive");
+
+ return description;
+ }
+
+ bool drive_is_removable = storageDrive.isRemovable();
+
+ if (drive_type == Solid::StorageDrive::HardDisk && !drive_is_removable)
+ {
+ QString size_str = formatByteSize(storageVolume.size());
+ if (!size_str.isEmpty())
+ {
+ if (drive_is_hotpluggable)
+ description = QCoreApplication::translate("", "%1 External Hard \
Drive", "%1 is the size").arg(size_str); + else
+ description = QCoreApplication::translate("", "%1 Hard Drive", "%1 \
is the size").arg(size_str); + } else {
+ if (drive_is_hotpluggable)
+ description = QCoreApplication::translate("", "External Hard \
Drive"); + else
+ description = QCoreApplication::translate("", "Hard Drive");
+ }
+
+ return description;
+ }
+
+ QString vendormodel_str;
+ QString model = product();
+ QString vendor_str = vendor();
+
+ if (vendor_str.isEmpty())
+ {
+ if (!model.isEmpty())
+ vendormodel_str = model;
+ }
+ else
+ {
+ if (model.isEmpty())
+ vendormodel_str = vendor_str;
+ else
+ {
+ if (model.startsWith(vendor_str))
+ {
+ // e.g. vendor is "Nokia" and model is "Nokia N950" we do not want \
"Nokia Nokia N950" as description + vendormodel_str = model;
+ }
+ else
+ {
+ vendormodel_str = QCoreApplication::translate("", "%1 %2", "%1 is \
the vendor, %2 is the model of the device").arg(vendor_str).arg(model); + \
} + }
+ }
+
+ if (vendormodel_str.isEmpty())
+ description = QCoreApplication::translate("", "Drive");
+ else
+ description = vendormodel_str;
+
+ return description;
+}
+
+QString UDisksDevice::volumeDescription() const
+{
+ QString description;
+ const UDisks::UDisksStorageVolume \
storageVolume(const_cast<UDisksDevice*>(this)); + QString volume_label = \
prop("IdLabel").toString(); + if (volume_label.isEmpty())
+ volume_label = prop("PartitionLabel").toString();
+ if (!volume_label.isEmpty())
+ return volume_label;
+
+ const UDisks::UDisksStorageDrive storageDrive(const_cast<UDisksDevice*>(this));
+ Solid::StorageDrive::DriveType drive_type = storageDrive.driveType();
+
+ // Handle media in optical drives
+ if (drive_type == Solid::StorageDrive::CdromDrive)
+ {
+ const UDisks::OpticalDisc disc(const_cast<UDisksDevice*>(this));
+ switch (disc.discType())
+ {
+ case Solid::OpticalDisc::UnknownDiscType:
+ case Solid::OpticalDisc::CdRom:
+ description = QCoreApplication::translate("", "CD-ROM");
+ break;
+
+ case Solid::OpticalDisc::CdRecordable:
+ if (disc.isBlank())
+ description = QCoreApplication::translate("", "Blank CD-R");
+ else
+ description = QCoreApplication::translate("", "CD-R");
+ break;
+
+ case Solid::OpticalDisc::CdRewritable:
+ if (disc.isBlank())
+ description = QCoreApplication::translate("", "Blank CD-RW");
+ else
+ description = QCoreApplication::translate("", "CD-RW");
+ break;
+
+ case Solid::OpticalDisc::DvdRom:
+ description = QCoreApplication::translate("", "DVD-ROM");
+ break;
+
+ case Solid::OpticalDisc::DvdRam:
+ if (disc.isBlank())
+ description = QCoreApplication::translate("", "Blank DVD-RAM");
+ else
+ description = QCoreApplication::translate("", "DVD-RAM");
+ break;
+
+ case Solid::OpticalDisc::DvdRecordable:
+ if (disc.isBlank())
+ description = QCoreApplication::translate("", "Blank DVD-R");
+ else
+ description = QCoreApplication::translate("", "DVD-R");
+ break;
+
+ case Solid::OpticalDisc::DvdPlusRecordableDuallayer:
+ if (disc.isBlank())
+ description = QCoreApplication::translate("", "Blank DVD+R \
Dual-Layer"); + else
+ description = QCoreApplication::translate("", "DVD+R \
Dual-Layer"); + break;
+
+ case Solid::OpticalDisc::DvdRewritable:
+ if (disc.isBlank())
+ description = QCoreApplication::translate("", "Blank DVD-RW");
+ else
+ description = QCoreApplication::translate("", "DVD-RW");
+ break;
+
+ case Solid::OpticalDisc::DvdPlusRecordable:
+ if (disc.isBlank())
+ description = QCoreApplication::translate("", "Blank DVD+R");
+ else
+ description = QCoreApplication::translate("", "DVD+R");
+ break;
+
+ case Solid::OpticalDisc::DvdPlusRewritable:
+ if (disc.isBlank())
+ description = QCoreApplication::translate("", "Blank DVD+RW");
+ else
+ description = QCoreApplication::translate("", "DVD+RW");
+ break;
+
+ case Solid::OpticalDisc::DvdPlusRewritableDuallayer:
+ if (disc.isBlank())
+ description = QCoreApplication::translate("", "Blank DVD+RW \
Dual-Layer"); + else
+ description = QCoreApplication::translate("", "DVD+RW \
Dual-Layer"); + break;
+
+ case Solid::OpticalDisc::BluRayRom:
+ description = QCoreApplication::translate("", "BD-ROM");
+ break;
+
+ case Solid::OpticalDisc::BluRayRecordable:
+ if (disc.isBlank())
+ description = QCoreApplication::translate("", "Blank BD-R");
+ else
+ description = QCoreApplication::translate("", "BD-R");
+ break;
+
+ case Solid::OpticalDisc::BluRayRewritable:
+ if (disc.isBlank())
+ description = QCoreApplication::translate("", "Blank BD-RE");
+ else
+ description = QCoreApplication::translate("", "BD-RE");
+ break;
+
+ case Solid::OpticalDisc::HdDvdRom:
+ description = QCoreApplication::translate("", "HD DVD-ROM");
+ break;
+
+ case Solid::OpticalDisc::HdDvdRecordable:
+ if (disc.isBlank())
+ description = QCoreApplication::translate("", "Blank HD \
DVD-R"); + else
+ description = QCoreApplication::translate("", "HD DVD-R");
+ break;
+
+ case Solid::OpticalDisc::HdDvdRewritable:
+ if (disc.isBlank())
+ description = QCoreApplication::translate("", "Blank HD \
DVD-RW"); + else
+ description = QCoreApplication::translate("", "HD DVD-RW");
+ break;
+ }
+
+ // Special case for pure audio disc
+ if (disc.availableContent() == Solid::OpticalDisc::Audio)
+ description = QCoreApplication::translate("", "Audio CD");
+
+ return description;
+ }
+
+ bool drive_is_removable = storageDrive.isRemovable();
+ bool drive_is_hotpluggable = storageDrive.isHotpluggable();
+ bool drive_is_encrypted_container = (storageVolume.usage() == \
Solid::StorageVolume::Encrypted); +
+ QString size_str = formatByteSize(storageVolume.size());
+ if (drive_is_encrypted_container)
+ {
+ if (!size_str.isEmpty())
+ description = QCoreApplication::translate("", "%1 Encrypted Container", \
"%1 is the size").arg(size_str); + else
+ description = QCoreApplication::translate("", "Encrypted Container");
+ }
+ else if (drive_type == Solid::StorageDrive::HardDisk && !drive_is_removable)
+ {
+ if (!size_str.isEmpty())
+ {
+ if (drive_is_hotpluggable)
+ description = QCoreApplication::translate("", "%1 External Hard \
Drive", "%1 is the size").arg(size_str); + else
+ description = QCoreApplication::translate("", "%1 Hard Drive", "%1 \
is the size").arg(size_str); + }
+ else
+ {
+ if (drive_is_hotpluggable)
+ description = QCoreApplication::translate("", "External Hard \
Drive"); + else
+ description = QCoreApplication::translate("", "Hard Drive");
+ }
+ }
+ else
+ {
+ if (drive_is_removable)
+ description = QCoreApplication::translate("", "%1 Removable Media", "%1 \
is the size").arg(size_str); + else
+ description = QCoreApplication::translate("", "%1 Media", "%1 is the \
size").arg(size_str); + }
+
+ return description;
+}
+
+QString UDisksDevice::icon() const
+{
+ QString iconName = prop( "DevicePresentationIconName" ).toString();
+
+ if ( !iconName.isEmpty() )
+ {
+ return iconName;
+ }
+ else
+ {
+ bool isPartition = prop( "DeviceIsPartition" ).toBool();
+ if ( isPartition ) // this is a slave device, we need to return its \
parent's icon + {
- UDisksDevice* parent = 0;
- if ( !parentUdi().isEmpty() )
- parent = new UDisksDevice( parentUdi() );
++ if ( !m_parentDevice && !parentUdi().isEmpty() )
++ m_parentDevice = new UDisksDevice( parentUdi() );
+
- if ( parent )
- {
- iconName = parent->icon();
- delete parent;
- }
++ if ( m_parentDevice )
++ iconName = m_parentDevice->icon();
+
+ if ( !iconName.isEmpty() )
+ return iconName;
+ }
+
+ // handle mounted ISOs
+ bool isLoop = prop( "DeviceIsLinuxLoop" ).toBool();
+ QString fstype = prop("IdType").toString();
+
+ if( isLoop && ( fstype == "iso9660" || fstype == "udf" ) )
+ {
+ return "media-optical";
+ }
+
+ // handle media
+ const QString media = prop( "DriveMedia" ).toString();
+ bool isOptical = prop( "DeviceIsOpticalDisc" ).toBool();
+
+ if ( !media.isEmpty() )
+ {
+ if ( isOptical ) // optical stuff
+ {
+ bool isWritable = prop( "OpticalDiscIsBlank" ).toBool() || \
prop("OpticalDiscIsAppendable").toBool(); +
+ const UDisks::OpticalDisc disc(const_cast<UDisksDevice*>(this));
+ Solid::OpticalDisc::ContentTypes availContent = \
disc.availableContent(); +
+ if (availContent & Solid::OpticalDisc::VideoDvd) // Video DVD
+ return "media-optical-dvd-video";
+ else if ((availContent & Solid::OpticalDisc::VideoCd) || \
(availContent & Solid::OpticalDisc::SuperVideoCd)) // Video CD + \
return "media-optical-video"; + else if ((availContent & \
Solid::OpticalDisc::Data) && (availContent & Solid::OpticalDisc::Audio)) // Mixed CD \
+ return "media-optical-mixed-cd"; + else if \
(availContent & Solid::OpticalDisc::Audio) // Audio CD + return \
"media-optical-audio"; + else if (availContent & \
Solid::OpticalDisc::Data) // Data CD + return \
"media-optical-data"; + else if ( isWritable )
+ return "media-optical-recordable";
+ else
+ {
+ if ( media.startsWith( "optical_dvd" ) || media.startsWith( \
"optical_hddvd" ) ) // DVD + return "media-optical-dvd";
+ else if ( media.startsWith( "optical_bd" ) ) // BluRay
+ return "media-optical-blu-ray";
+ }
+
+ // fallback for every other optical disc
+ return "media-optical";
+ }
+
+ if ( media == "flash_ms" ) // Flash & Co.
+ return "media-flash-memory-stick";
+ else if ( media == "flash_sd" || media == "flash_sdhc" || media == \
"flash_mmc" ) + return "media-flash-sd-mmc";
+ else if ( media == "flash_sm" )
+ return "media-flash-smart-media";
+ else if ( media.startsWith( "flash" ) )
+ return "media-flash";
+ else if ( media == "floppy" ) // the good ol' floppy
+ return "media-floppy";
+
+ }
+
+ // handle drives
+ bool isRemovable = prop( "DeviceIsRemovable" ).toBool();
+ const QString conn = prop( "DriveConnectionInterface" ).toString();
+
+ if ( queryDeviceInterface(Solid::DeviceInterface::OpticalDrive) )
+ return "drive-optical";
+ else if ( isRemovable && !isOptical )
+ {
+ if ( conn == "usb" )
+ return "drive-removable-media-usb";
+ else
+ return "drive-removable-media";
+ }
+ }
+
+ return "drive-harddisk"; // general fallback
+}
+
+QString UDisksDevice::product() const
+{
+ QString product = prop("DriveModel").toString();
+ const bool isDrive = prop( "DeviceIsDrive" ).toBool() && \
!m_udi.endsWith(":media"); +
+ if (!isDrive) {
+ QString label = prop("IdLabel").toString();
+ if (!label.isEmpty()) {
+ product = label;
+ }
+ }
+
+ return product;
+}
+
+QString UDisksDevice::vendor() const
+{
+ return prop("DriveVendor").toString();
+}
+
+QString UDisksDevice::udi() const
+{
+ return m_udi;
+}
+
+QString UDisksDevice::parentUdi() const
+{
+ if (m_udi.endsWith(QLatin1String(":media"))) {
+ QString result = m_udi;
+ return result.remove(":media");
+ }
+ else if ( prop( "DeviceIsLuksCleartext" ).toBool() )
+ return prop( "LuksCleartextSlave" ).value<QDBusObjectPath>().path();
+ else {
+ QString parent = prop("PartitionSlave").value<QDBusObjectPath>().path();
+ if (parent.isEmpty() || parent=="/") {
+ parent = UD_UDI_DISKS_PREFIX;
+ }
+ return parent;
+ }
+}
+
+void UDisksDevice::checkCache(const QString &key) const
+{
+ if (m_cache.isEmpty()) // recreate the cache
+ allProperties();
+
+ if (m_cache.contains(key))
+ return;
+
+ QVariant reply = m_device->property(key.toUtf8());
+
+ if (reply.isValid()) {
+ m_cache[key] = reply;
+ } else {
+ m_cache[key] = QVariant();
+ }
+}
+
+QVariant UDisksDevice::prop(const QString &key) const
+{
+ checkCache(key);
+ return m_cache.value(key);
+}
+
+bool UDisksDevice::propertyExists(const QString &key) const
+{
+ checkCache(key);
+ return m_cache.contains(key);
+}
+
+QMap<QString, QVariant> UDisksDevice::allProperties() const
+{
+ QDBusMessage call = QDBusMessage::createMethodCall(m_device->service(), \
m_device->path(), + \
"org.freedesktop.DBus.Properties", "GetAll"); + call << m_device->interface();
+ QDBusPendingReply< QVariantMap > reply = \
QDBusConnection::systemBus().asyncCall(call); + reply.waitForFinished();
+
+ if (reply.isValid())
+ m_cache = reply.value();
+ else
+ m_cache.clear();
+
+ return m_cache;
+}
+
+void UDisksDevice::slotChanged()
+{
+ // given we cannot know which property/ies changed, clear the cache
+ m_cache.clear();
+ Q_EMIT changed();
+}
+
+bool UDisksDevice::isDeviceBlacklisted() const
+{
+ return prop("DevicePresentationHide").toBool() ||
+ prop("DeviceMountPaths").toStringList().contains("/boot") ||
+ prop("IdLabel").toString() == "System Reserved" ||
+ ( prop("IdUsage").toString().isEmpty() && \
!(prop("OpticalDiscIsBlank").toBool() || (prop("OpticalDiscNumAudioTracks").toInt() > \
0) )); +}
+
+QString UDisksDevice::errorToString(const QString & error) const
+{
+ if (error == UD_ERROR_UNAUTHORIZED)
+ return QCoreApplication::translate("", "You are not authorized to perform \
this operation."); + else if (error == UD_ERROR_BUSY)
+ return QCoreApplication::translate("", "The device is currently busy.");
+ else if (error == UD_ERROR_FAILED)
+ return QCoreApplication::translate("", "The requested operation has \
failed."); + else if (error == UD_ERROR_CANCELED)
+ return QCoreApplication::translate("", "The requested operation has been \
canceled."); + else if (error == UD_ERROR_INVALID_OPTION)
+ return QCoreApplication::translate("", "An invalid or malformed option has \
been given."); + else if (error == UD_ERROR_MISSING_DRIVER)
+ return QCoreApplication::translate("", "The kernel driver for this \
filesystem type is not available."); + else
+ return QCoreApplication::translate("", "An unspecified error has \
occurred."); +}
+
+Solid::ErrorType UDisksDevice::errorToSolidError(const QString & error) const
+{
+ if (error == UD_ERROR_BUSY)
+ return Solid::DeviceBusy;
+ else if (error == UD_ERROR_FAILED)
+ return Solid::OperationFailed;
+ else if (error == UD_ERROR_CANCELED)
+ return Solid::UserCanceled;
+ else if (error == UD_ERROR_INVALID_OPTION)
+ return Solid::InvalidOption;
+ else if (error == UD_ERROR_MISSING_DRIVER)
+ return Solid::MissingDriver;
+ else
+ return Solid::UnauthorizedOperation;
+}
diff --cc tier1/solid/src/solid/backends/udisks2/udisksdevicebackend.cpp
index 0000000,0000000..2a01b86
new file mode 100644
--- /dev/null
+++ b/tier1/solid/src/solid/backends/udisks2/udisksdevicebackend.cpp
@@@ -1,0 -1,0 +1,239 @@@
++/*
++ Copyright 2010 Michael Zanetti <mzanetti@kde.org>
++ Copyright 2010-2012 Lukáš Tinkl <ltinkl@redhat.com>
++ Copyright 2012 Dan Vrátil <dvratil@redhat.com>
++
++ This library 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.1 of the License, or (at your option) version 3, or any
++ later version accepted by the membership of KDE e.V. (or its
++ successor approved by the membership of KDE e.V.), which shall
++ act as a proxy defined in Section 6 of version 3 of the license.
++
++ 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
++ Lesser General Public License for more details.
++
++ You should have received a copy of the GNU Lesser General Public
++ License along with this library. If not, see <http://www.gnu.org/licenses/>.
++*/
++
++#include "udisksdevicebackend.h"
++
++#include <QtDBus/QDBusConnection>
++#include <QtDBus/QDBusInterface>
++#include <QtXml/QDomDocument>
++
++#include "solid/deviceinterface.h"
++#include "solid/genericinterface.h"
++
++using namespace Solid::Backends::UDisks2;
++
++/* Static cache for DeviceBackends for all UDIs */
++QMap<QString /* UDI */, DeviceBackend*> DeviceBackend::s_backends;
++
++DeviceBackend* DeviceBackend::backendForUDI(const QString& udi)
++{
++ DeviceBackend *backend = 0;
++ if (udi.isEmpty()) {
++ return backend;
++ }
++
++ if (s_backends.contains(udi)) {
++ backend = s_backends.value(udi);
++ } else {
++ backend = new DeviceBackend(udi);
++ s_backends.insert(udi, backend);
++ }
++
++ return backend;
++}
++
++void DeviceBackend::destroyBackend(const QString& udi)
++{
++ if (s_backends.contains(udi)) {
++ DeviceBackend *backend = s_backends.value(udi);
++ s_backends.remove(udi);
++ delete backend;
++ }
++}
++
++DeviceBackend::DeviceBackend(const QString& udi)
++ : m_udi(udi)
++{
++ //qDebug() << "Creating backend for device" << m_udi;
++ m_device = new QDBusInterface(UD2_DBUS_SERVICE, m_udi,
++ QString(), // no interface, we aggregate them
++ QDBusConnection::systemBus(), this);
++
++ if (m_device->isValid()) {
++ QDBusConnection::systemBus().connect(UD2_DBUS_SERVICE, m_udi, \
DBUS_INTERFACE_PROPS, "PropertiesChanged", this, ++ \
SLOT(slotPropertiesChanged(QString,QVariantMap,QStringList))); ++ \
QDBusConnection::systemBus().connect(UD2_DBUS_SERVICE, UD2_DBUS_PATH, \
DBUS_INTERFACE_MANAGER, "InterfacesAdded", ++ \
this, SLOT(slotInterfacesAdded(QDBusObjectPath,QVariantMapMap))); ++ \
QDBusConnection::systemBus().connect(UD2_DBUS_SERVICE, UD2_DBUS_PATH, \
DBUS_INTERFACE_MANAGER, "InterfacesRemoved", ++ \
this, SLOT(slotInterfacesRemoved(QDBusObjectPath,QStringList))); ++
++ initInterfaces();
++ }
++}
++
++DeviceBackend::~DeviceBackend()
++{
++ //qDebug() << "Destroying backend for device" << m_udi;
++}
++
++void DeviceBackend::initInterfaces()
++{
++ m_interfaces.clear();
++
++ const QString xmlData = introspect();
++ if (xmlData.isEmpty()) {
++ qDebug() << m_udi << "has no interfaces!";
++ return;
++ }
++
++ QDomDocument dom;
++ dom.setContent(xmlData);
++
++ QDomNodeList ifaceNodeList = dom.elementsByTagName("interface");
++ for (int i = 0; i < ifaceNodeList.count(); i++) {
++ QDomElement ifaceElem = ifaceNodeList.item(i).toElement();
++ /* Accept only org.freedesktop.UDisks2.* interfaces so that when the device \
is unplugged, ++ * m_interfaces goes empty and we can easily verify that the \
device is gone. */ ++ if (!ifaceElem.isNull() && \
ifaceElem.attribute("name").startsWith(UD2_DBUS_SERVICE)) { ++ \
m_interfaces.append(ifaceElem.attribute("name")); ++ }
++ }
++
++ //qDebug() << m_udi << "has interfaces:" << m_interfaces;
++}
++
++QStringList DeviceBackend::interfaces() const
++{
++ return m_interfaces;
++}
++
++const QString& DeviceBackend::udi() const
++{
++ return m_udi;
++}
++
++QVariant DeviceBackend::prop(const QString& key) const
++{
++ checkCache(key);
++ return m_propertyCache.value(key);
++}
++
++bool DeviceBackend::propertyExists(const QString& key) const
++{
++ checkCache(key);
++ /* checkCache() will put an invalid QVariant in cache when the property
++ * does not exist, so check for validity, not for an actual presence. */
++ return m_propertyCache.value(key).isValid();
++}
++
++QVariantMap DeviceBackend::allProperties() const
++{
++ QDBusMessage call = QDBusMessage::createMethodCall(UD2_DBUS_SERVICE, m_udi, \
DBUS_INTERFACE_PROPS, "GetAll"); ++
++ Q_FOREACH (const QString & iface, m_interfaces) {
++ call.setArguments(QVariantList() << iface);
++ QDBusPendingReply<QVariantMap> reply = \
QDBusConnection::systemBus().call(call); ++
++ if (reply.isValid()) {
++ m_propertyCache.unite(reply.value());
++ } else {
++ qWarning() << "Error getting props:" << reply.error().name() << \
reply.error().message(); ++ }
++ //qDebug() << "After iface" << iface << ", cache now contains" << \
m_cache.size() << "items"; ++ }
++
++ return m_propertyCache;
++}
++
++QString DeviceBackend::introspect() const
++{
++ QDBusMessage call = QDBusMessage::createMethodCall(UD2_DBUS_SERVICE, m_udi,
++ DBUS_INTERFACE_INTROSPECT, \
"Introspect"); ++ QDBusPendingReply<QString> reply = \
QDBusConnection::systemBus().call(call); ++
++ if (reply.isValid())
++ return reply.value();
++ else {
++ return QString();
++ }
++}
++
++void DeviceBackend::checkCache(const QString& key) const
++{
++ if (m_propertyCache.isEmpty()) { // recreate the cache
++ allProperties();
++ }
++
++ if (m_propertyCache.contains(key)) {
++ return;
++ }
++
++ QVariant reply = m_device->property(key.toUtf8());
++ m_propertyCache.insert(key, reply);
++
++ if (!reply.isValid()) {
++ /* Store the item in the cache anyway so next time we don't have to
++ * do the DBus call to find out it does not exist but just check whether
++ * prop(key).isValid() */
++ qDebug() << m_udi << ": property" << key << "does not exist";
++ }
++}
++
++void DeviceBackend::slotPropertiesChanged(const QString& ifaceName, const \
QVariantMap& changedProps, const QStringList& invalidatedProps) ++{
++ //qDebug() << m_udi << "'s interface" << ifaceName << "changed props:";
++
++ QMap<QString, int> changeMap;
++
++ Q_FOREACH(const QString & key, invalidatedProps) {
++ m_propertyCache.remove(key);
++ changeMap.insert(key, Solid::GenericInterface::PropertyRemoved);
++ //qDebug() << "\t invalidated:" << key;
++ }
++
++ QMapIterator<QString, QVariant> i(changedProps);
++ while (i.hasNext()) {
++ i.next();
++ const QString key = i.key();
++ m_propertyCache.insert(key, i.value()); // replace the value
++ changeMap.insert(key, Solid::GenericInterface::PropertyModified);
++ //qDebug() << "\t modified:" << key << ":" << m_propertyCache.value(key);
++ }
++
++ Q_EMIT propertyChanged(changeMap);
++ Q_EMIT changed();
++}
++
++void DeviceBackend::slotInterfacesAdded(const QDBusObjectPath& object_path, const \
QVariantMapMap& interfaces_and_properties) ++{
++ if (object_path.path() != m_udi) {
++ return;
++ }
++
++ Q_FOREACH(const QString & iface, interfaces_and_properties.keys()) {
++ /* Don't store generic DBus interfaces */
++ if (iface.startsWith(UD2_DBUS_SERVICE)) {
++ m_interfaces.append(interfaces_and_properties.keys());
++ }
++ }
++}
++
++void DeviceBackend::slotInterfacesRemoved(const QDBusObjectPath& object_path, const \
QStringList& interfaces) ++{
++ if (object_path.path() != m_udi) {
++ return;
++ }
++
++ Q_FOREACH(const QString & iface, interfaces) {
++ m_interfaces.removeAll(iface);
++ }
++}
diff --cc tier1/solid/src/solid/backends/udisks2/udisksdevicebackend.h
index 0000000,0000000..829fa41
new file mode 100644
--- /dev/null
+++ b/tier1/solid/src/solid/backends/udisks2/udisksdevicebackend.h
@@@ -1,0 -1,0 +1,84 @@@
++/*
++ Copyright 2010 Michael Zanetti <mzanetti@kde.org>
++ Copyright 2010-2012 Lukáš Tinkl <ltinkl@redhat.com>
++ Copyright 2012 Dan Vrátil <dvratil@redhat.com>
++
++ This library 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.1 of the License, or (at your option) version 3, or any
++ later version accepted by the membership of KDE e.V. (or its
++ successor approved by the membership of KDE e.V.), which shall
++ act as a proxy defined in Section 6 of version 3 of the license.
++
++ 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
++ Lesser General Public License for more details.
++
++ You should have received a copy of the GNU Lesser General Public
++ License along with this library. If not, see <http://www.gnu.org/licenses/>.
++*/
++
++#ifndef UDISKSDEVICEBACKEND_H
++#define UDISKSDEVICEBACKEND_H
++
++#include <QObject>
++#include <QtDBus/QDBusConnection>
++#include <QtDBus/QDBusObjectPath>
++#include <QtDBus/QDBusInterface>
++#include <QStringList>
++
++#include "udisks2.h"
++
++namespace Solid {
++namespace Backends {
++namespace UDisks2 {
++
++class DeviceBackend: public QObject {
++
++ Q_OBJECT
++
++ public:
++ static DeviceBackend* backendForUDI(const QString &udi);
++ static void destroyBackend(const QString &udi);
++
++ DeviceBackend(const QString &udi);
++ ~DeviceBackend();
++
++ QVariant prop(const QString &key) const;
++ bool propertyExists(const QString &key) const;
++ QVariantMap allProperties() const;
++
++ QStringList interfaces() const;
++ const QString & udi() const;
++
++ Q_SIGNALS:
++ void propertyChanged(const QMap<QString, int> &changeMap);
++ void changed();
++
++ private Q_SLOTS:
++ void slotInterfacesAdded(const QDBusObjectPath &object_path, const \
QVariantMapMap &interfaces_and_properties); ++ void slotInterfacesRemoved(const \
QDBusObjectPath &object_path, const QStringList &interfaces); ++ void \
slotPropertiesChanged(const QString &ifaceName, const QVariantMap &changedProps, \
const QStringList &invalidatedProps); ++
++ private:
++ void initInterfaces();
++ QString introspect() const;
++ void checkCache(const QString &key) const;
++
++ QDBusInterface *m_device;
++
++ mutable QVariantMap m_propertyCache;
++ QStringList m_interfaces;
++ QString m_udi;
++
++ static QMap<QString, DeviceBackend*> s_backends;
++
++};
++
++} /* namespace UDisks2 */
++} /* namespace Backends */
++} /* namespace Solid */
++
++#endif /* UDISKSDEVICEBACKEND_H */
diff --cc tier2/kconfig/autotests/kconfig_compiler/CMakeLists.txt
index 054c117,0000000..36265de
mode 100644,000000..100644
--- a/tier2/kconfig/autotests/kconfig_compiler/CMakeLists.txt
+++ b/tier2/kconfig/autotests/kconfig_compiler/CMakeLists.txt
@@@ -1,198 -1,0 +1,208 @@@
+
+#test5.cpp test5.h: $(srcdir)/test5.kcfg ../kconfig_compiler $(srcdir)/test5.kcfgc
+# ../kconfig_compiler $(srcdir)/test5.kcfg $(srcdir)/test5.kcfgc
+
+macro(GEN_KCFG_TEST_SOURCE _testName _srcs)
+ add_custom_command(
+ OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${_testName}.cpp \
${CMAKE_CURRENT_BINARY_DIR}/${_testName}.h + COMMAND \
${KConfig_KCFGC_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/${_testName}.kcfg \
${CMAKE_CURRENT_SOURCE_DIR}/${_testName}.kcfgc + DEPENDS \
${CMAKE_CURRENT_SOURCE_DIR}/${_testName}.kcfg \
${CMAKE_CURRENT_SOURCE_DIR}/${_testName}.kcfgc kconfig_compiler) +
+# set_source_files_properties(${CMAKE_CURRENT_BINARY_DIR}/${_testName}.h \
PROPERTIES GENERATED TRUE) + \
qt4_generate_moc(${CMAKE_CURRENT_BINARY_DIR}/${_testName}.h \
${CMAKE_CURRENT_BINARY_DIR}/${_testName}.moc ) +# do not run automoc on the \
generated file + set_source_files_properties(${CMAKE_CURRENT_BINARY_DIR}/${_testName}.cpp \
PROPERTIES SKIP_AUTOMOC TRUE) + set( ${_srcs} ${${_srcs}} \
${CMAKE_CURRENT_BINARY_DIR}/${_testName}.cpp \
${CMAKE_CURRENT_BINARY_DIR}/${_testName}.h ) +
+ macro_add_file_dependencies(${CMAKE_CURRENT_BINARY_DIR}/${_testName}.cpp \
${CMAKE_CURRENT_BINARY_DIR}/${_testName}.moc ) +endmacro(GEN_KCFG_TEST_SOURCE)
+
+########### next target ###############
+
+set(test1_SRCS test1main.cpp )
+
+
+gen_kcfg_test_source(test1 test1_SRCS)
+
+add_executable(test1 ${test1_SRCS})
+
+target_link_libraries(test1 Qt5::Widgets KConfigCore KConfigGui)
+
+
+########### next target ###############
+
+set(test2_SRCS test2main.cpp )
+
+
+gen_kcfg_test_source(test2 test2_SRCS)
+
+add_executable(test2 ${test2_SRCS})
+
+target_link_libraries(test2 Qt5::Widgets KConfigCore KConfigGui)
+
+
+########### next target ###############
+
+set(test3_SRCS test3main.cpp )
+
+
+gen_kcfg_test_source(test3 test3_SRCS)
+
+add_executable(test3 ${test3_SRCS})
+
+target_link_libraries(test3 Qt5::Widgets KConfigCore KConfigGui)
+
+
+########### next target ###############
+
+set(test3a_SRCS test3amain.cpp )
+
+
+gen_kcfg_test_source(test3a test3a_SRCS)
+
+add_executable(test3a ${test3a_SRCS})
+
+target_link_libraries(test3a Qt5::Widgets KConfigCore KConfigGui)
+
+
+########### next target ###############
+
+set(test4_SRCS test4main.cpp )
+
+
+gen_kcfg_test_source(test4 test4_SRCS)
+
+add_executable(test4 ${test4_SRCS})
+
+target_link_libraries(test4 Qt5::Widgets KConfigCore KConfigGui)
+
+
+########### next target ###############
+
+set(test5_SRCS test5main.cpp )
+
+
+gen_kcfg_test_source(test5 test5_SRCS)
+
+add_executable(test5 ${test5_SRCS})
+
+target_link_libraries(test5 Qt5::Widgets KConfigCore KConfigGui)
+
+
+########### next target ###############
+
+set(test6_SRCS test6main.cpp )
+
+
+gen_kcfg_test_source(test6 test6_SRCS)
+
+add_executable(test6 ${test6_SRCS})
+
+target_link_libraries(test6 Qt5::Widgets KConfigCore KConfigGui)
+
+
+########### next target ###############
+
+set(test7_SRCS test7main.cpp )
+
+
+gen_kcfg_test_source(test7 test7_SRCS)
+
+add_executable(test7 ${test7_SRCS})
+
+target_link_libraries(test7 Qt5::Widgets KConfigCore KConfigGui)
+
+
+########### next target ###############
+
+set(test8_SRCS test8main.cpp )
+
+
+gen_kcfg_test_source(test8a test8_SRCS)
+gen_kcfg_test_source(test8b test8_SRCS)
+
+add_executable(test8 ${test8_SRCS})
+
+target_link_libraries(test8 Qt5::Widgets KConfigCore KConfigGui)
+
+
+########### next target ###############
+
+set(test9_SRCS test9main.cpp )
+
+
+gen_kcfg_test_source(test9 test9_SRCS)
+
+add_executable(test9 ${test9_SRCS})
+
+target_link_libraries(test9 Qt5::Widgets KConfigCore KConfigGui)
+
+
+########### next target ###############
+
+set(test10_SRCS test10main.cpp )
+
+
+gen_kcfg_test_source(test10 test10_SRCS)
+
+add_executable(test10 ${test10_SRCS})
+
+target_link_libraries(test10 Qt5::Widgets KConfigCore KConfigGui \
${KCoreAddons_LIBRARIES} ) +
+
+########### next target ###############
+
+set(test11_SRCS test11main.cpp )
+
+
+gen_kcfg_test_source(test11 test11_SRCS)
+gen_kcfg_test_source(test11a test11_SRCS)
+
+add_executable(test11 ${test11_SRCS})
+
+target_link_libraries(test11 Qt5::Widgets KConfigCore KConfigGui)
+
+
+########### next target ###############
+
- set(test_dpointer_SRCS test_dpointer_main.cpp )
++set(test12_SRCS test12main.cpp )
++
++gen_kcfg_test_source(test12 test12_SRCS)
++
++add_executable(test12 ${test11_SRCS})
++
++target_link_libraries(test12 ${KDE4_KDEUI_LIBS} )
+
+
++########### next target ###############
++
++set(test_dpointer_SRCS test_dpointer_main.cpp )
++
+gen_kcfg_test_source(test_dpointer test_dpointer_SRCS)
+
+add_executable(test_dpointer ${test_dpointer_SRCS})
+
+target_link_libraries(test_dpointer Qt5::Widgets KConfigCore KConfigGui)
+
+
+########### next target ###############
+
+set(test_signal_SRCS test_signal_main.cpp )
+gen_kcfg_test_source(test_signal test_signal_SRCS)
+add_executable(test_signal ${test_signal_SRCS})
+target_link_libraries(test_signal Qt5::Widgets KConfigCore KConfigGui)
+
+########### next target ###############
+
+set(kconfigcompiler_test_SRCS kconfigcompiler_test.cpp )
+add_executable(kconfigcompiler_test ${kconfigcompiler_test_SRCS})
+add_test(kconfig-kconfigcompiler kconfigcompiler_test)
+
+target_link_libraries(kconfigcompiler_test ${QT_QTCORE_LIBS} ${QT_QTTEST_LIBRARY} \
${KCoreAddons_LIBRARIES}) +
+########### install files ###############
+
+
+
+
+
diff --cc tier2/kconfig/autotests/kconfig_compiler/test12.cpp.ref
index 0000000,f3bec18..f3bec18
mode 000000,100644..100644
--- a/tier2/kconfig/autotests/kconfig_compiler/test12.cpp.ref
+++ b/tier2/kconfig/autotests/kconfig_compiler/test12.cpp.ref
diff --cc tier2/kconfig/autotests/kconfig_compiler/test12.h.ref
index 0000000,9cec995..9cec995
mode 000000,100644..100644
--- a/tier2/kconfig/autotests/kconfig_compiler/test12.h.ref
+++ b/tier2/kconfig/autotests/kconfig_compiler/test12.h.ref
diff --cc tier2/kconfig/autotests/kconfig_compiler/test12.kcfg
index 0000000,57663ab..57663ab
mode 000000,100644..100644
--- a/tier2/kconfig/autotests/kconfig_compiler/test12.kcfg
+++ b/tier2/kconfig/autotests/kconfig_compiler/test12.kcfg
diff --cc tier2/kconfig/autotests/kconfig_compiler/test12.kcfgc
index 0000000,1ed82e7..1ed82e7
mode 000000,100644..100644
--- a/tier2/kconfig/autotests/kconfig_compiler/test12.kcfgc
+++ b/tier2/kconfig/autotests/kconfig_compiler/test12.kcfgc
diff --cc tier2/kconfig/autotests/kconfig_compiler/test12main.cpp
index 0000000,d39a70f..d39a70f
mode 000000,100644..100644
--- a/tier2/kconfig/autotests/kconfig_compiler/test12main.cpp
+++ b/tier2/kconfig/autotests/kconfig_compiler/test12main.cpp
diff --cc tier2/kconfig/src/kconfig_compiler/kconfig_compiler.cpp
index de2ca3b,0000000..2253a2b
mode 100644,000000..100644
--- a/tier2/kconfig/src/kconfig_compiler/kconfig_compiler.cpp
+++ b/tier2/kconfig/src/kconfig_compiler/kconfig_compiler.cpp
@@@ -1,2330 -1,0 +1,2334 @@@
+// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; -*-
+/*
+ This file is part of KDE.
+
+ Copyright (c) 2003 Cornelius Schumacher <schumacher@kde.org>
+ Copyright (c) 2003 Waldo Bastian <bastian@kde.org>
+ Copyright (c) 2003 Zack Rusin <zack@kde.org>
+ Copyright (c) 2006 Michaël Larouche <michael.larouche@kdemail.net>
+ Copyright (c) 2008 Allen Winter <winter@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 as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ 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.
+*/
+
+// Compiling this file with this flag is just crazy
+#undef QT_NO_CAST_FROM_ASCII
+
+#include <QtCore/QCoreApplication>
+#include <QtCore/QFile>
+#include <QtCore/QFileInfo>
+#include <QtCore/QSettings>
+#include <QtCore/QTextStream>
+#include <QtXml/QDomAttr>
+#include <QtCore/QRegExp>
+#include <QtCore/QStringList>
+
+#include <ostream>
+#include <iostream>
+#include <stdlib.h>
+
+namespace
+{
+ QTextStream cout(stdout);
+ QTextStream cerr(stderr);
+}
+
+static void parseArgs(const QStringList &args, QString &directory, QString &file1, \
QString &file2) +{
+ int fileCount = 0;
+ directory = QChar::fromLatin1('.');
+
+ for (int i = 1; i < args.count(); ++i) {
+ if (args.at(i) == QLatin1String("-d") || args.at(i) == \
QLatin1String("--directory")) { + if (i + 1 > args.count()) {
+ cerr << args.at(i) << " needs an argument" << endl;
+ exit(1);
+ }
+ directory = args.at(++i);
+ } else if (args.at(i).startsWith(QLatin1String("-d"))) {
+ directory = args.at(i).mid(2);
+ } else if (args.at(i) == QLatin1String("--help") || args.at(i) == \
QLatin1String("-h")) { + cout << "Options:" << endl;
+ cout << " -L --license Display software license" << endl;
+ cout << " -d, --directory <dir> Directory to generate files in \
[.]" << endl; + cout << " -h, --help Display this help" \
<< endl; + cout << endl;
+ cout << "Arguments:" << endl;
+ cout << " file.kcfg Input kcfg XML file" << endl;
+ cout << " file.kcfgc Code generation options file" \
<< endl; + exit(0);
+ } else if (args.at(i) == QLatin1String("--license") || args.at(i) == \
QLatin1String("-L")) { + cout << "Copyright 2003 Cornelius Schumacher, \
Waldo Bastian, Zack Rusin," << endl; + cout << " Reinhold Kainhofer, \
Duncan Mac-Vicar P., Harald Fernengel" << endl; + cout << "This program \
comes with ABSOLUTELY NO WARRANTY." << endl; + cout << "You may \
redistribute copies of this program" << endl; + cout << "under the terms \
of the GNU Library Public License." << endl; + cout << "For more \
information about these matters, see the file named COPYING." << endl; + \
exit(0); + } else if (args.at(i).startsWith(QLatin1Char('-'))) {
+ cerr << "Unknown option: " << args.at(i) << endl;
+ exit(1);
+ } else if (fileCount == 0) {
+ file1 = args.at(i);
+ ++fileCount;
+ } else if (fileCount == 1) {
+ file2 = args.at(i);
+ ++fileCount;
+ } else {
+ cerr << "Too many arguments" << endl;
+ exit(1);
+ }
+ }
+ if (fileCount < 2) {
+ cerr << "Too few arguments" << endl;
+ exit(1);
+ }
+}
+
+QStringList allNames;
+QRegExp *validNameRegexp;
+QString This;
+QString Const;
+
+/**
+ Configuration Compiler Configuration
+*/
+class CfgConfig
+{
+public:
+ CfgConfig( const QString &codegenFilename )
+ {
+ // Configure the compiler with some settings
+ QSettings codegenConfig(codegenFilename, QSettings::IniFormat);
+
+ nameSpace = codegenConfig.value("NameSpace").toString();
+ className = codegenConfig.value("ClassName").toString();
+ if ( className.isEmpty() ) {
+ cerr << "Class name missing" << endl;
+ exit(1);
+ }
+ inherits = codegenConfig.value("Inherits").toString();
+ if ( inherits.isEmpty() ) inherits = "KConfigSkeleton";
+ visibility = codegenConfig.value("Visibility").toString();
+ if ( !visibility.isEmpty() ) visibility += ' ';
+ forceStringFilename = codegenConfig.value("ForceStringFilename", \
false).toBool(); + singleton = codegenConfig.value("Singleton", false).toBool();
+ staticAccessors = singleton;
+ customAddons = codegenConfig.value("CustomAdditions", false).toBool();
+ memberVariables = codegenConfig.value("MemberVariables").toString();
+ dpointer = (memberVariables == "dpointer");
+ headerIncludes = codegenConfig.value("IncludeFiles", \
QStringList()).toStringList(); + sourceIncludes = \
codegenConfig.value("SourceIncludeFiles", QStringList()).toStringList(); + \
mutators = codegenConfig.value("Mutators", QStringList()).toStringList(); + \
allMutators = ((mutators.count() == 1) && (mutators.at(0).toLower() == "true")); + \
itemAccessors = codegenConfig.value("ItemAccessors", false).toBool(); + \
setUserTexts = codegenConfig.value("SetUserTexts", false).toBool(); + \
defaultGetters = codegenConfig.value("DefaultValueGetters", \
QStringList()).toStringList(); + allDefaultGetters = (defaultGetters.count() == \
1) && (defaultGetters.at(0).toLower() == "true"); + globalEnums = \
codegenConfig.value("GlobalEnums", false).toBool(); + useEnumTypes = \
codegenConfig.value("UseEnumTypes", false).toBool(); +
+ const QString trString = \
codegenConfig.value("TranslationSystem").toString().toLower(); + if ( trString == \
"kde" ) { + translationSystem = KdeTranslation;
+ } else {
+ if ( !trString.isEmpty() && trString != "qt" ) {
+ cerr << "Unknown translation system, falling back to Qt tr()" << endl;
+ }
+ translationSystem = QtTranslation;
+ }
+ }
+
+public:
+ enum TranslationSystem {
+ QtTranslation,
+ KdeTranslation
+ };
+
+ // These are read from the .kcfgc configuration file
+ QString nameSpace; // The namespace for the class to be generated
+ QString className; // The class name to be generated
+ QString inherits; // The class the generated class inherits (if empty, from \
KConfigSkeleton) + QString visibility;
+ bool forceStringFilename;
+ bool singleton; // The class will be a singleton
+ bool staticAccessors; // provide or not static accessors
+ bool customAddons;
+ QString memberVariables;
+ QStringList headerIncludes;
+ QStringList sourceIncludes;
+ QStringList mutators;
+ QStringList defaultGetters;
+ bool allMutators;
+ bool setUserTexts;
+ bool allDefaultGetters;
+ bool dpointer;
+ bool globalEnums;
+ bool useEnumTypes;
+ bool itemAccessors;
+ TranslationSystem translationSystem;
+};
+
+
+struct SignalArguments
+{
+ QString type;
+ QString variableName;
+};
+
+class Signal {
+public:
+ QString name;
+ QString label;
+ QList<SignalArguments> arguments;
+};
+
+
+
+
+class CfgEntry
+{
+ public:
+ struct Choice
+ {
+ QString name;
+ QString context;
+ QString label;
+ QString toolTip;
+ QString whatsThis;
+ };
+ class Choices
+ {
+ public:
+ Choices() {}
+ Choices( const QList<Choice> &d, const QString &n, const QString &p )
+ : prefix(p), choices(d), mName(n)
+ {
+ int i = n.indexOf(QLatin1String("::"));
+ if (i >= 0)
+ mExternalQual = n.left(i + 2);
+ }
+ QString prefix;
+ QList<Choice> choices;
+ const QString& name() const { return mName; }
+ const QString& externalQualifier() const { return mExternalQual; }
+ bool external() const { return !mExternalQual.isEmpty(); }
+ private:
+ QString mName;
+ QString mExternalQual;
+ };
+
+ CfgEntry( const QString &group, const QString &type, const QString &key,
+ const QString &name, const QString &labelContext, const QString \
&label, + const QString &toolTipContext, const QString &toolTip, const \
QString &whatsThisContext, const QString &whatsThis, const QString &code, + \
const QString &defaultValue, const Choices &choices, const QList<Signal> signalList, \
+ bool hidden ) + : mGroup( group ), mType( type ), mKey( key ), \
mName( name ), + mLabelContext( labelContext ), mLabel( label ), \
mToolTipContext( toolTipContext ), mToolTip( toolTip ), + mWhatsThisContext( \
whatsThisContext ), mWhatsThis( whatsThis ), + mCode( code ), mDefaultValue( \
defaultValue ), mChoices( choices ), + mSignalList(signalList), mHidden( \
hidden ) + {
+ }
+
+ void setGroup( const QString &group ) { mGroup = group; }
+ QString group() const { return mGroup; }
+
+ void setType( const QString &type ) { mType = type; }
+ QString type() const { return mType; }
+
+ void setKey( const QString &key ) { mKey = key; }
+ QString key() const { return mKey; }
+
+ void setName( const QString &name ) { mName = name; }
+ QString name() const { return mName; }
+
+ void setLabelContext( const QString &labelContext ) { mLabelContext = \
labelContext; } + QString labelContext() const { return mLabelContext; }
+
+ void setLabel( const QString &label ) { mLabel = label; }
+ QString label() const { return mLabel; }
+
+ void setToolTipContext( const QString &toolTipContext ) { mToolTipContext = \
toolTipContext; } + QString toolTipContext() const { return mToolTipContext; }
+
+ void setToolTip( const QString &toolTip ) { mToolTip = toolTip; }
+ QString toolTip() const { return mToolTip; }
+
+ void setWhatsThisContext( const QString &whatsThisContext ) { mWhatsThisContext \
= whatsThisContext; } + QString whatsThisContext() const { return \
mWhatsThisContext; } +
+ void setWhatsThis( const QString &whatsThis ) { mWhatsThis = whatsThis; }
+ QString whatsThis() const { return mWhatsThis; }
+
+ void setDefaultValue( const QString &d ) { mDefaultValue = d; }
+ QString defaultValue() const { return mDefaultValue; }
+
+ void setCode( const QString &d ) { mCode = d; }
+ QString code() const { return mCode; }
+
+ void setMinValue( const QString &d ) { mMin = d; }
+ QString minValue() const { return mMin; }
+
+ void setMaxValue( const QString &d ) { mMax = d; }
+ QString maxValue() const { return mMax; }
+
+ void setParam( const QString &d ) { mParam = d; }
+ QString param() const { return mParam; }
+
+ void setParamName( const QString &d ) { mParamName = d; }
+ QString paramName() const { return mParamName; }
+
+ void setParamType( const QString &d ) { mParamType = d; }
+ QString paramType() const { return mParamType; }
+
+ void setChoices( const QList<Choice> &d, const QString &n, const QString &p ) { \
mChoices = Choices( d, n, p ); } + Choices choices() const { return mChoices; }
+
+ void setParamValues( const QStringList &d ) { mParamValues = d; }
+ QStringList paramValues() const { return mParamValues; }
+
+ void setParamDefaultValues( const QStringList &d ) { mParamDefaultValues = d; }
+ QString paramDefaultValue(int i) const { return mParamDefaultValues[i]; }
+
+ void setParamMax( int d ) { mParamMax = d; }
+ int paramMax() const { return mParamMax; }
+
+ void setSignalList( const QList<Signal> &value ) { mSignalList = value; }
+ QList<Signal> signalList() const { return mSignalList; }
+
+ bool hidden() const { return mHidden; }
+
+ void dump() const
+ {
+ cerr << "<entry>" << endl;
+ cerr << " group: " << mGroup << endl;
+ cerr << " type: " << mType << endl;
+ cerr << " key: " << mKey << endl;
+ cerr << " name: " << mName << endl;
+ cerr << " label context: " << mLabelContext << endl;
+ cerr << " label: " << mLabel << endl;
+// whatsthis
+ cerr << " code: " << mCode << endl;
+// cerr << " values: " << mValues.join(":") << endl;
+
+ if (!param().isEmpty())
+ {
+ cerr << " param name: "<< mParamName << endl;
+ cerr << " param type: "<< mParamType << endl;
+ cerr << " paramvalues: " << mParamValues.join(QChar::fromLatin1(':')) << \
endl; + }
+ cerr << " default: " << mDefaultValue << endl;
+ cerr << " hidden: " << mHidden << endl;
+ cerr << " min: " << mMin << endl;
+ cerr << " max: " << mMax << endl;
+ cerr << "</entry>" << endl;
+ }
+
+ private:
+ QString mGroup;
+ QString mType;
+ QString mKey;
+ QString mName;
+ QString mLabelContext;
+ QString mLabel;
+ QString mToolTipContext;
+ QString mToolTip;
+ QString mWhatsThisContext;
+ QString mWhatsThis;
+ QString mCode;
+ QString mDefaultValue;
+ QString mParam;
+ QString mParamName;
+ QString mParamType;
+ Choices mChoices;
+ QList<Signal> mSignalList;
+ QStringList mParamValues;
+ QStringList mParamDefaultValues;
+ int mParamMax;
+ bool mHidden;
+ QString mMin;
+ QString mMax;
+};
+
+class Param {
+public:
+ QString name;
+ QString type;
+};
+
+// returns the name of an member variable
+// use itemPath to know the full path
+// like using d-> in case of dpointer
+static QString varName(const QString &n, const CfgConfig &cfg)
+{
+ QString result;
+ if ( !cfg.dpointer ) {
+ result = QChar::fromLatin1('m') + n;
+ result[1] = result[1].toUpper();
+ }
+ else {
+ result = n;
+ result[0] = result[0].toLower();
+ }
+ return result;
+}
+
+static QString varPath(const QString &n, const CfgConfig &cfg)
+{
+ QString result;
+ if ( cfg.dpointer ) {
+ result = "d->"+varName(n, cfg);
+ }
+ else {
+ result = varName(n, cfg);
+ }
+ return result;
+}
+
+static QString enumName(const QString &n)
+{
+ QString result = QString::fromLatin1("Enum") + n;
+ result[4] = result[4].toUpper();
+ return result;
+}
+
+static QString enumName(const QString &n, const CfgEntry::Choices &c)
+{
+ QString result = c.name();
+ if ( result.isEmpty() )
+ {
+ result = QString::fromLatin1("Enum") + n;
+ result[4] = result[4].toUpper();
+ }
+ return result;
+}
+
+static QString enumType(const CfgEntry *e, bool globalEnums)
+{
+ QString result = e->choices().name();
+ if ( result.isEmpty() )
+ {
+ result = QString::fromLatin1("Enum") + e->name();
+ if( !globalEnums )
+ result += QString::fromLatin1("::type");
+ result[4] = result[4].toUpper();
+ }
+ return result;
+}
+
+static QString enumTypeQualifier(const QString &n, const CfgEntry::Choices &c)
+{
+ QString result = c.name();
+ if ( result.isEmpty() )
+ {
+ result = QString::fromLatin1("Enum") + n + QString::fromLatin1("::");
+ result[4] = result[4].toUpper();
+ }
+ else if ( c.external() )
+ result = c.externalQualifier();
+ else
+ result.clear();
+ return result;
+}
+
+static QString setFunction(const QString &n, const QString &className = QString())
+{
+ QString result = QString::fromLatin1("set") + n;
+ result[3] = result[3].toUpper();
+
+ if ( !className.isEmpty() )
+ result = className + QString::fromLatin1("::") + result;
+ return result;
+}
+
+static QString getDefaultFunction(const QString &n, const QString &className = \
QString()) +{
+ QString result = QString::fromLatin1("default") + n + \
QString::fromLatin1("Value"); + result[7] = result[7].toUpper();
+
+ if ( !className.isEmpty() )
+ result = className + QString::fromLatin1("::") + result;
+ return result;
+}
+
+static QString getFunction(const QString &n, const QString &className = QString())
+{
+ QString result = n;
+ result[0] = result[0].toLower();
+
+ if ( !className.isEmpty() )
+ result = className + QString::fromLatin1("::") + result;
+ return result;
+}
+
+
+static void addQuotes( QString &s )
+{
+ if ( !s.startsWith( QLatin1Char('"') ) )
+ s.prepend( QLatin1Char('"') );
+ if ( !s.endsWith( QLatin1Char('"') ) )
+ s.append( QLatin1Char('"') );
+}
+
+static QString quoteString( const QString &s )
+{
+ QString r = s;
+ r.replace( QLatin1Char('\\'), QLatin1String("\\\\") );
+ r.replace( QLatin1Char('\"'), QLatin1String("\\\"") );
+ r.remove( QLatin1Char('\r') );
+ r.replace( QLatin1Char('\n'), QLatin1String("\\n\"\n\"") );
+ return QLatin1Char('\"') + r + QLatin1Char('\"');
+}
+
+static QString literalString( const QString &s )
+{
+ bool isAscii = true;
+ for(int i = s.length(); i--;)
+ if (s[i].unicode() > 127) isAscii = false;
+
+ if (isAscii)
+ return QString::fromLatin1("QLatin1String( ") + quoteString(s) + \
QString::fromLatin1(" )"); + else
+ return QString::fromLatin1("QString::fromUtf8( ") + quoteString(s) + \
QString::fromLatin1(" )"); +}
+
+static QString dumpNode(const QDomNode &node)
+{
+ QString msg;
+ QTextStream s(&msg, QIODevice::WriteOnly );
+ node.save(s, 0);
+
+ msg = msg.simplified();
+ if (msg.length() > 40)
+ return msg.left(37) + QString::fromLatin1("...");
+ return msg;
+}
+
+static QString filenameOnly(const QString& path)
+{
+ int i = path.lastIndexOf(QRegExp(QLatin1String("[/\\]")));
+ if (i >= 0)
+ return path.mid(i+1);
+ return path;
+}
+
+static QString signalEnumName(const QString &signalName)
+{
+ QString result;
+ result = QString::fromLatin1("signal") + signalName;
+ result[6] = result[6].toUpper();
+
+ return result;
+}
+
+static void preProcessDefault( QString &defaultValue, const QString &name,
+ const QString &type,
+ const CfgEntry::Choices &choices,
+ QString &code, const CfgConfig &cfg )
+{
+ if ( type == QLatin1String("String") && !defaultValue.isEmpty() ) {
+ defaultValue = literalString(defaultValue);
+
+ } else if ( type == QLatin1String("Path") && !defaultValue.isEmpty() ) {
+ defaultValue = literalString( defaultValue );
+ } else if ( type == QLatin1String("Url") && !defaultValue.isEmpty() ) {
+ // Use fromUserInput in order to support absolute paths and absolute urls, \
like KDE4's KUrl(QString) did. + defaultValue = \
QString::fromLatin1("QUrl::fromUserInput( ") + literalString(defaultValue) + \
QLatin1Char(')'); + } else if ( ( type == QLatin1String("UrlList") || type == \
QLatin1String("StringList") || type == QLatin1String("PathList")) && \
!defaultValue.isEmpty() ) { + QTextStream cpp( &code, QIODevice::WriteOnly | \
QIODevice::Append ); + if (!code.isEmpty())
+ cpp << endl;
+
- cpp << " QStringList default" << name << ";" << endl;
++ if( type == "UrlList" ) {
++ cpp << " KUrl::List default" << name << ";" << endl;
++ } else {
++ cpp << " QStringList default" << name << ";" << endl;
++ }
+ const QStringList defaults = defaultValue.split(QLatin1Char(','));
+ QStringList::ConstIterator it;
+ for( it = defaults.constBegin(); it != defaults.constEnd(); ++it ) {
+ cpp << " default" << name << ".append( ";
+ if( type == QLatin1String("UrlList") ) {
+ cpp << "QUrl::fromUserInput(";
+ }
+ cpp << "QString::fromUtf8( \"" << *it << "\" ) ";
+ if( type == QLatin1String("UrlList") ) {
+ cpp << ") ";
+ }
+ cpp << ");" << endl;
+ }
+ defaultValue = QString::fromLatin1("default") + name;
+
+ } else if ( type == QLatin1String("Color") && !defaultValue.isEmpty() ) {
+ QRegExp colorRe(QLatin1String("\\d+,\\s*\\d+,\\s*\\d+(,\\s*\\d+)?"));
+ if (colorRe.exactMatch(defaultValue))
+ {
+ defaultValue = QLatin1String("QColor( ") + defaultValue + QLatin1String(" \
)"); + }
+ else
+ {
+ defaultValue = QLatin1String("QColor( \"") + defaultValue + \
QLatin1String("\" )"); + }
+
+ } else if ( type == QLatin1String("Enum") ) {
+ QList<CfgEntry::Choice>::ConstIterator it;
+ for( it = choices.choices.constBegin(); it != choices.choices.constEnd(); \
++it ) { + if ( (*it).name == defaultValue ) {
+ if ( cfg.globalEnums && choices.name().isEmpty() )
+ defaultValue.prepend( choices.prefix );
+ else
+ defaultValue.prepend( enumTypeQualifier(name, choices) + choices.prefix \
); + break;
+ }
+ }
+
+ } else if ( type == QLatin1String("IntList") ) {
+ QTextStream cpp( &code, QIODevice::WriteOnly | QIODevice::Append );
+ if (!code.isEmpty())
+ cpp << endl;
+
+ cpp << " QList<int> default" << name << ";" << endl;
+ if (!defaultValue.isEmpty())
+ {
+ const QStringList defaults = defaultValue.split( QLatin1Char(',') );
+ QStringList::ConstIterator it;
+ for( it = defaults.constBegin(); it != defaults.constEnd(); ++it ) {
+ cpp << " default" << name << ".append( " << *it << " );"
+ << endl;
+ }
+ }
+ defaultValue = QString::fromLatin1("default") + name;
+ }
+}
+
+
+CfgEntry *parseEntry( const QString &group, const QDomElement &element, const \
CfgConfig &cfg ) +{
+ bool defaultCode = false;
+ QString type = element.attribute( "type" );
+ QString name = element.attribute( "name" );
+ QString key = element.attribute( "key" );
+ QString hidden = element.attribute( "hidden" );
+ QString labelContext;
+ QString label;
+ QString toolTipContext;
+ QString toolTip;
+ QString whatsThisContext;
+ QString whatsThis;
+ QString defaultValue;
+ QString code;
+ QString param;
+ QString paramName;
+ QString paramType;
+ CfgEntry::Choices choices;
+ QList<Signal> signalList;
+ QStringList paramValues;
+ QStringList paramDefaultValues;
+ QString minValue;
+ QString maxValue;
+ int paramMax = 0;
+
+ for ( QDomElement e = element.firstChildElement(); !e.isNull(); e = \
e.nextSiblingElement() ) { + QString tag = e.tagName();
+ if ( tag == "label" ) {
+ label = e.text();
+ labelContext = e.attribute( "context" );
+ }
+ else if ( tag == "tooltip" ) {
+ toolTip = e.text();
+ toolTipContext = e.attribute( "context" );
+ }
+ else if ( tag == "whatsthis" ) {
+ whatsThis = e.text();
+ whatsThisContext = e.attribute( "context" );
+ }
+ else if ( tag == "min" ) minValue = e.text();
+ else if ( tag == "max" ) maxValue = e.text();
+ else if ( tag == "code" ) code = e.text();
+ else if ( tag == "parameter" )
+ {
+ param = e.attribute( "name" );
+ paramType = e.attribute( "type" );
+ if ( param.isEmpty() ) {
+ cerr << "Parameter must have a name: " << dumpNode(e) << endl;
+ return 0;
+ }
+ if ( paramType.isEmpty() ) {
+ cerr << "Parameter must have a type: " << dumpNode(e) << endl;
+ return 0;
+ }
+ if ((paramType == "Int") || (paramType == "UInt"))
+ {
+ bool ok;
+ paramMax = e.attribute("max").toInt(&ok);
+ if (!ok)
+ {
+ cerr << "Integer parameter must have a maximum (e.g. max=\"0\"): "
+ << dumpNode(e) << endl;
+ return 0;
+ }
+ }
+ else if (paramType == "Enum")
+ {
+ for ( QDomElement e2 = e.firstChildElement(); !e2.isNull(); e2 = \
e2.nextSiblingElement() ) { + if (e2.tagName() == "values")
+ {
+ for ( QDomElement e3 = e2.firstChildElement(); !e3.isNull(); e3 = \
e3.nextSiblingElement() ) { + if (e3.tagName() == "value")
+ {
+ paramValues.append( e3.text() );
+ }
+ }
+ break;
+ }
+ }
+ if (paramValues.isEmpty())
+ {
+ cerr << "No values specified for parameter '" << param
+ << "'." << endl;
+ return 0;
+ }
+ paramMax = paramValues.count()-1;
+ }
+ else
+ {
+ cerr << "Parameter '" << param << "' has type " << paramType
+ << " but must be of type int, uint or Enum." << endl;
+ return 0;
+ }
+ }
+ else if ( tag == "default" )
+ {
+ if (e.attribute("param").isEmpty())
+ {
+ defaultValue = e.text();
+ if (e.attribute( "code" ) == "true")
+ defaultCode = true;
+ }
+ }
+ else if ( tag == "choices" ) {
+ QString name = e.attribute( "name" );
+ QString prefix = e.attribute( "prefix" );
+ QList<CfgEntry::Choice> chlist;
+ for( QDomElement e2 = e.firstChildElement(); !e2.isNull(); e2 = \
e2.nextSiblingElement() ) { + if ( e2.tagName() == "choice" ) {
+ CfgEntry::Choice choice;
+ choice.name = e2.attribute( "name" );
+ if ( choice.name.isEmpty() ) {
+ cerr << "Tag <choice> requires attribute 'name'." << endl;
+ }
+ for( QDomElement e3 = e2.firstChildElement(); !e3.isNull(); e3 = \
e3.nextSiblingElement() ) { + if ( e3.tagName() == "label" ) {
+ choice.label = e3.text();
+ choice.context = e3.attribute( "context" );
+ }
+ if ( e3.tagName() == "tooltip" ) {
+ choice.toolTip = e3.text();
+ choice.context = e3.attribute( "context" );
+ }
+ if ( e3.tagName() == "whatsthis" ) {
+ choice.whatsThis = e3.text();
+ choice.context = e3.attribute( "context" );
+ }
+ }
+ chlist.append( choice );
+ }
+ }
+ choices = CfgEntry::Choices( chlist, name, prefix );
+ }
+ else if ( tag == "emit" ) {
+ QDomNode signalNode;
+ Signal signal;
+ signal.name = e.attribute( "signal" );
+ signalList.append( signal);
+ }
+ }
+
+
+ bool nameIsEmpty = name.isEmpty();
+ if ( nameIsEmpty && key.isEmpty() ) {
+ cerr << "Entry must have a name or a key: " << dumpNode(element) << endl;
+ return 0;
+ }
+
+ if ( key.isEmpty() ) {
+ key = name;
+ }
+
+ if ( nameIsEmpty ) {
+ name = key;
+ name.remove( ' ' );
+ } else if ( name.contains( ' ' ) ) {
+ cout<<"Entry '"<<name<<"' contains spaces! <name> elements can not contain \
spaces!"<<endl; + name.remove( ' ' );
+ }
+
+ if (name.contains("$("))
+ {
+ if (param.isEmpty())
+ {
+ cerr << "Name may not be parameterized: " << name << endl;
+ return 0;
+ }
+ }
+ else
+ {
+ if (!param.isEmpty())
+ {
+ cerr << "Name must contain '$(" << param << ")': " << name << endl;
+ return 0;
+ }
+ }
+
+ if ( label.isEmpty() ) {
+ label = key;
+ }
+
+ if ( type.isEmpty() ) type = "String"; // XXX : implicit type might be bad
+
+ if (!param.isEmpty())
+ {
+ // Adjust name
+ paramName = name;
+ name.remove("$("+param+')');
+ // Lookup defaults for indexed entries
+ for(int i = 0; i <= paramMax; i++)
+ {
+ paramDefaultValues.append(QString());
+ }
+
+ for ( QDomElement e = element.firstChildElement(); !e.isNull(); e = \
e.nextSiblingElement() ) { + QString tag = e.tagName();
+ if ( tag == "default" )
+ {
+ QString index = e.attribute("param");
+ if (index.isEmpty())
+ continue;
+
+ bool ok;
+ int i = index.toInt(&ok);
+ if (!ok)
+ {
+ i = paramValues.indexOf(index);
+ if (i == -1)
+ {
+ cerr << "Index '" << index << "' for default value is unknown." << \
endl; + return 0;
+ }
+ }
+
+ if ((i < 0) || (i > paramMax))
+ {
+ cerr << "Index '" << i << "' for default value is out of range [0, "<< \
paramMax<<"]." << endl; + return 0;
+ }
+
+ QString tmpDefaultValue = e.text();
+
+ if (e.attribute( "code" ) != "true")
+ preProcessDefault(tmpDefaultValue, name, type, choices, code, cfg);
+
+ paramDefaultValues[i] = tmpDefaultValue;
+ }
+ }
+ }
+
+ if (!validNameRegexp->exactMatch(name))
+ {
+ if (nameIsEmpty)
+ cerr << "The key '" << key << "' can not be used as name for the entry \
because " + "it is not a valid name. You need to specify a valid \
name for this entry." << endl; + else
+ cerr << "The name '" << name << "' is not a valid name for an entry." << \
endl; + return 0;
+ }
+
+ if (allNames.contains(name))
+ {
+ if (nameIsEmpty)
+ cerr << "The key '" << key << "' can not be used as name for the entry \
because " + "it does not result in a unique name. You need to \
specify a unique name for this entry." << endl; + else
+ cerr << "The name '" << name << "' is not unique." << endl;
+ return 0;
+ }
+ allNames.append(name);
+
+ if (!defaultCode)
+ {
+ preProcessDefault(defaultValue, name, type, choices, code, cfg);
+ }
+
+ CfgEntry *result = new CfgEntry( group, type, key, name, labelContext, label, \
toolTipContext, toolTip, whatsThisContext, whatsThis, + \
code, defaultValue, choices, signalList, + hidden \
== "true" ); + if (!param.isEmpty())
+ {
+ result->setParam(param);
+ result->setParamName(paramName);
+ result->setParamType(paramType);
+ result->setParamValues(paramValues);
+ result->setParamDefaultValues(paramDefaultValues);
+ result->setParamMax(paramMax);
+ }
+ result->setMinValue(minValue);
+ result->setMaxValue(maxValue);
+
+ return result;
+}
+
+static bool isUnsigned(const QString& type)
+{
+ if ( type == "UInt" ) return true;
+ if ( type == "ULongLong" ) return true;
+ return false;
+}
+
+/**
+ Return parameter declaration for given type.
+*/
+QString param( const QString &t )
+{
+ const QString type = t.toLower();
+ if ( type == "string" ) return "const QString &";
+ else if ( type == "stringlist" ) return "const QStringList &";
+ else if ( type == "font" ) return "const QFont &";
+ else if ( type == "rect" ) return "const QRect &";
+ else if ( type == "size" ) return "const QSize &";
+ else if ( type == "color" ) return "const QColor &";
+ else if ( type == "point" ) return "const QPoint &";
+ else if ( type == "int" ) return "int";
+ else if ( type == "uint" ) return "uint";
+ else if ( type == "bool" ) return "bool";
+ else if ( type == "double" ) return "double";
+ else if ( type == "datetime" ) return "const QDateTime &";
+ else if ( type == "longlong" ) return "qint64";
+ else if ( type == "ulonglong" ) return "quint64";
+ else if ( type == "intlist" ) return "const QList<int> &";
+ else if ( type == "enum" ) return "int";
+ else if ( type == "path" ) return "const QString &";
+ else if ( type == "pathlist" ) return "const QStringList &";
+ else if ( type == "password" ) return "const QString &";
+ else if ( type == "url" ) return "const QUrl &";
+ else if ( type == "urllist" ) return "const QList<QUrl> &";
+ else {
+ cerr <<"kconfig_compiler does not support type \""<< type <<"\""<<endl;
+ return "QString"; //For now, but an assert would be better
+ }
+}
+
+/**
+ Actual C++ storage type for given type.
+*/
+QString cppType( const QString &t )
+{
+ const QString type = t.toLower();
+ if ( type == "string" ) return "QString";
+ else if ( type == "stringlist" ) return "QStringList";
+ else if ( type == "font" ) return "QFont";
+ else if ( type == "rect" ) return "QRect";
+ else if ( type == "size" ) return "QSize";
+ else if ( type == "color" ) return "QColor";
+ else if ( type == "point" ) return "QPoint";
+ else if ( type == "int" ) return "int";
+ else if ( type == "uint" ) return "uint";
+ else if ( type == "bool" ) return "bool";
+ else if ( type == "double" ) return "double";
+ else if ( type == "datetime" ) return "QDateTime";
+ else if ( type == "longlong" ) return "qint64";
+ else if ( type == "ulonglong" ) return "quint64";
+ else if ( type == "intlist" ) return "QList<int>";
+ else if ( type == "enum" ) return "int";
+ else if ( type == "path" ) return "QString";
+ else if ( type == "pathlist" ) return "QStringList";
+ else if ( type == "password" ) return "QString";
+ else if ( type == "url" ) return "QUrl";
+ else if ( type == "urllist" ) return "QList<QUrl>";
+ else {
+ cerr<<"kconfig_compiler does not support type \""<< type <<"\""<<endl;
+ return "QString"; //For now, but an assert would be better
+ }
+}
+
+QString defaultValue( const QString &t )
+{
+ const QString type = t.toLower();
+ if ( type == "string" ) return "\"\""; // Use empty string, not null \
string! + else if ( type == "stringlist" ) return "QStringList()";
+ else if ( type == "font" ) return "QFont()";
+ else if ( type == "rect" ) return "QRect()";
+ else if ( type == "size" ) return "QSize()";
+ else if ( type == "color" ) return "QColor(128, 128, 128)";
+ else if ( type == "point" ) return "QPoint()";
+ else if ( type == "int" ) return "0";
+ else if ( type == "uint" ) return "0";
+ else if ( type == "bool" ) return "false";
+ else if ( type == "double" ) return "0.0";
+ else if ( type == "datedime" ) return "QDateTime()";
+ else if ( type == "longlong" ) return "0";
+ else if ( type == "ulonglong" ) return "0";
+ else if ( type == "intlist" ) return "QList<int>()";
+ else if ( type == "enum" ) return "0";
+ else if ( type == "path" ) return "\"\""; // Use empty string, not null \
string! + else if ( type == "pathlist" ) return "QStringList()";
+ else if ( type == "password" ) return "\"\""; // Use empty string, not null \
string! + else if ( type == "url" ) return "QUrl()";
+ else if ( type == "urllist" ) return "QList<QUrl>()";
+ else {
+ cerr<<"Error, kconfig_compiler does not support the \""<< type <<"\" \
type!"<<endl; + return "QString"; //For now, but an assert would be better
+ }
+}
+
+QString itemType( const QString &type )
+{
+ QString t;
+
+ t = type;
+ t.replace( 0, 1, t.left( 1 ).toUpper() );
+
+ return t;
+}
+
+static QString itemDeclaration(const CfgEntry *e, const CfgConfig &cfg)
+{
+ if (cfg.itemAccessors)
+ return QString();
+
+ QString fCap = e->name();
+ fCap[0] = fCap[0].toUpper();
+ return " "+cfg.inherits+"::Item"+itemType( e->type() ) +
+ " *item" + fCap +
+ ( (!e->param().isEmpty())?(QString("[%1]").arg(e->paramMax()+1)) : \
QString()) + + ";\n";
+}
+
+// returns the name of an item variable
+// use itemPath to know the full path
+// like using d-> in case of dpointer
+static QString itemVar(const CfgEntry *e, const CfgConfig &cfg)
+{
+ QString result;
+ if (cfg.itemAccessors)
+ {
+ if ( !cfg.dpointer )
+ {
+ result = 'm' + e->name() + "Item";
+ result[1] = result[1].toUpper();
+ }
+ else
+ {
+ result = e->name() + "Item";
+ result[0] = result[0].toLower();
+ }
+ }
+ else
+ {
+ result = "item" + e->name();
+ result[4] = result[4].toUpper();
+ }
+ return result;
+}
+
+static QString itemPath(const CfgEntry *e, const CfgConfig &cfg)
+{
+ QString result;
+ if ( cfg.dpointer ) {
+ result = "d->"+itemVar(e, cfg);
+ }
+ else {
+ result = itemVar(e, cfg);
+ }
+ return result;
+}
+
+QString newItem( const QString &type, const QString &name, const QString &key,
+ const QString &defaultValue, const CfgConfig &cfg, const QString \
¶m = QString()) +{
+ QString t = "new "+cfg.inherits+"::Item" + itemType( type ) +
+ "( currentGroup(), " + key + ", " + varPath( name, cfg ) + param;
+ if ( type == "Enum" ) t += ", values" + name;
+ if ( !defaultValue.isEmpty() ) {
+ t += ", ";
+ if ( type == "String" ) t += defaultValue;
+ else t+= defaultValue;
+ }
+ t += " );";
+
+ return t;
+}
+
+QString paramString(const QString &s, const CfgEntry *e, int i)
+{
+ QString result = s;
+ QString needle = "$("+e->param()+')';
+ if (result.contains(needle))
+ {
+ QString tmp;
+ if (e->paramType() == "Enum")
+ {
+ tmp = e->paramValues()[i];
+ }
+ else
+ {
+ tmp = QString::number(i);
+ }
+
+ result.replace(needle, tmp);
+ }
+ return result;
+}
+
+QString paramString(const QString &group, const QList<Param> ¶meters)
+{
+ QString paramString = group;
+ QString arguments;
+ int i = 1;
+ for (QList<Param>::ConstIterator it = parameters.constBegin();
+ it != parameters.constEnd(); ++it)
+ {
+ if (paramString.contains("$("+(*it).name+')'))
+ {
+ QString tmp;
+ tmp.sprintf("%%%d", i++);
+ paramString.replace("$("+(*it).name+')', tmp);
+ arguments += ".arg( mParam"+(*it).name+" )";
+ }
+ }
+ if (arguments.isEmpty())
+ return "QLatin1String( \""+group+"\" )";
+
+ return "QString( QLatin1String( \""+paramString+"\" ) )"+arguments;
+}
+
+QString translatedString(const CfgConfig &cfg, const QString &string, const QString \
&context = QString(), const QString ¶m = QString(), const QString ¶mValue = \
QString()) +{
+ QString result;
+
+ switch (cfg.translationSystem) {
+ case CfgConfig::QtTranslation:
+ if (!context.isEmpty()) {
+ result+= "/*: " + context + " */ QObject::tr(";
+ } else {
+ result+= "QObject::tr(";
+ }
+ break;
+
+ case CfgConfig::KdeTranslation:
+ if (!context.isEmpty()) {
+ result+= "i18nc(" + quoteString(context) + ", ";
+ } else {
+ result+= "i18n(";
+ }
+ break;
+ }
+
+ if (!param.isEmpty()) {
+ QString resolvedString = string;
+ resolvedString.replace("$("+param+')', paramValue);
+ result+= quoteString(resolvedString);
+ } else {
+ result+= quoteString(string);
+ }
+
+ result+= ')';
+
+ return result;
+}
+
+/* int i is the value of the parameter */
+QString userTextsFunctions( CfgEntry *e, const CfgConfig &cfg, QString \
itemVarStr=QString(), QString i=QString() ) +{
+ QString txt;
+ if (itemVarStr.isNull()) itemVarStr=itemPath(e, cfg);
+ if ( !e->label().isEmpty() ) {
+ txt += " " + itemVarStr + "->setLabel( ";
+ txt += translatedString(cfg, e->label(), e->labelContext(), e->param(), i);
+ txt += " );\n";
+ }
+ if ( !e->toolTip().isEmpty() ) {
+ txt += " " + itemVarStr + "->setToolTip( ";
+ txt += translatedString(cfg, e->toolTip(), e->toolTipContext(), e->param(), i);
+ txt += " );\n";
+ }
+ if ( !e->whatsThis().isEmpty() ) {
+ txt += " " + itemVarStr + "->setWhatsThis( ";
+ txt += translatedString(cfg, e->whatsThis(), e->whatsThisContext(), e->param(), \
i); + txt += " );\n";
+ }
+ return txt;
+}
+
+// returns the member accesor implementation
+// which should go in the h file if inline
+// or the cpp file if not inline
+QString memberAccessorBody( CfgEntry *e, bool globalEnums, const CfgConfig &cfg )
+{
+ QString result;
+ QTextStream out(&result, QIODevice::WriteOnly);
+ QString n = e->name();
+ QString t = e->type();
+ bool useEnumType = cfg.useEnumTypes && t == "Enum";
+
+ out << "return ";
+ if (useEnumType)
+ out << "static_cast<" << enumType(e, globalEnums) << ">(";
+ out << This << varPath(n, cfg);
+ if (!e->param().isEmpty())
+ out << "[i]";
+ if (useEnumType)
+ out << ")";
+ out << ";" << endl;
+
+ return result;
+}
+
+// returns the member mutator implementation
+// which should go in the h file if inline
+// or the cpp file if not inline
+QString memberMutatorBody( CfgEntry *e, const CfgConfig &cfg )
+{
+ QString result;
+ QTextStream out(&result, QIODevice::WriteOnly);
+ QString n = e->name();
+ QString t = e->type();
+
+ if (!e->minValue().isEmpty())
+ {
+ if (e->minValue() != "0" || !isUnsigned(t)) { // skip writing "if uint<0" \
(#187579) + out << "if (v < " << e->minValue() << ")" << endl;
+ out << "{" << endl;
+ out << " qDebug() << \"" << setFunction(n);
+ out << ": value \" << v << \" is less than the minimum value of ";
+ out << e->minValue()<< "\";" << endl;
+ out << " v = " << e->minValue() << ";" << endl;
+ out << "}" << endl;
+ }
+ }
+
+ if (!e->maxValue().isEmpty())
+ {
+ out << endl << "if (v > " << e->maxValue() << ")" << endl;
+ out << "{" << endl;
+ out << " qDebug() << \"" << setFunction(n);
+ out << ": value \" << v << \" is greater than the maximum value of ";
+ out << e->maxValue()<< "\";" << endl;
+ out << " v = " << e->maxValue() << ";" << endl;
+ out << "}" << endl << endl;
+ }
+
+ out << "if (!" << This << "isImmutable( QString::fromLatin1( \"";
+ if (!e->param().isEmpty())
+ {
+ out << e->paramName().replace("$("+e->param()+")", "%1") << "\" ).arg( ";
+ if ( e->paramType() == "Enum" ) {
+ out << "QLatin1String( ";
+
+ if (cfg.globalEnums)
+ out << enumName(e->param()) << "ToString[i]";
+ else
+ out << enumName(e->param()) << "::enumToString[i]";
+
+ out << " )";
+ }
+ else
+ {
+ out << "i";
+ }
+ out << " )";
+ }
+ else
+ {
+ out << n << "\" )";
+ }
+ out << " ))" << (!e->signalList().empty() ? " {" : "") << endl;
+ out << " " << This << varPath(n, cfg);
+ if (!e->param().isEmpty())
+ out << "[i]";
+ out << " = v;" << endl;
+
+ if ( !e->signalList().empty() ) {
+ foreach(const Signal &signal, e->signalList()) {
+ out << " " << This << varPath("settingsChanged", cfg) << " |= " << \
signalEnumName(signal.name) << ";" << endl; + }
+ out << "}" << endl;
+ }
+
+ return result;
+}
+
+// returns the member get default implementation
+// which should go in the h file if inline
+// or the cpp file if not inline
+QString memberGetDefaultBody( CfgEntry *e )
+{
+ QString result = e->code();
+ QTextStream out(&result, QIODevice::WriteOnly);
+ out << endl;
+
+ if (!e->param().isEmpty()) {
+ out << " switch (i) {" << endl;
+ for (int i = 0; i <= e->paramMax(); ++i) {
+ if (!e->paramDefaultValue(i).isEmpty()) {
+ out << " case " << i << ": return " << e->paramDefaultValue(i) << ';' << \
endl; + }
+ }
+ out << " default:" << endl;
+ out << " return " << e->defaultValue().replace("$("+e->param()+')', "i") << \
';' << endl; + out << " }" << endl;
+ } else {
+ out << " return " << e->defaultValue() << ';';
+ }
+
+ return result;
+}
+
+// returns the item accesor implementation
+// which should go in the h file if inline
+// or the cpp file if not inline
+QString itemAccessorBody( CfgEntry *e, const CfgConfig &cfg )
+{
+ QString result;
+ QTextStream out(&result, QIODevice::WriteOnly);
+
+ out << "return " << itemPath(e, cfg);
+ if (!e->param().isEmpty()) out << "[i]";
+ out << ";" << endl;
+
+ return result;
+}
+
+//indents text adding X spaces per line
+QString indent(QString text, int spaces)
+{
+ QString result;
+ QTextStream out(&result, QIODevice::WriteOnly);
+ QTextStream in(&text, QIODevice::ReadOnly);
+ QString currLine;
+ while ( !in.atEnd() )
+ {
+ currLine = in.readLine();
+ if (!currLine.isEmpty())
+ for (int i=0; i < spaces; i++)
+ out << " ";
+ out << currLine << endl;
+ }
+ return result;
+}
+
+// adds as many 'namespace foo {' lines to p_out as
+// there are namespaces in p_ns
+void beginNamespaces(const QString &p_ns, QTextStream &p_out)
+{
+ if ( !p_ns.isEmpty() ) {
+ const QStringList nameSpaces = p_ns.split( "::" );
+ foreach (const QString &ns, nameSpaces )
+ p_out << "namespace " << ns << " {" << endl;
+ p_out << endl;
+ }
+}
+
+// adds as many '}' lines to p_out as
+// there are namespaces in p_ns
+void endNamespaces(const QString &p_ns, QTextStream &p_out)
+{
+ if ( !p_ns.isEmpty() ) {
+ const int namespaceCount = p_ns.count( "::" ) + 1;
+ for ( int i = 0; i < namespaceCount; ++i )
+ p_out << "}" << endl;
+ p_out << endl;
+ }
+}
+
+
+int main( int argc, char **argv )
+{
+ QCoreApplication app(argc, argv);
+
+ validNameRegexp = new QRegExp("[a-zA-Z_][a-zA-Z0-9_]*");
+
+ QString directoryName, inputFilename, codegenFilename;
+ parseArgs(app.arguments(), directoryName, inputFilename, codegenFilename);
+
+ QString baseDir = directoryName;
+#ifdef Q_OS_WIN
+ if (!baseDir.endsWith('/') && !baseDir.endsWith('\\'))
+#else
+ if (!baseDir.endsWith('/'))
+#endif
+ baseDir.append("/");
+
+ if (!codegenFilename.endsWith(QLatin1String(".kcfgc")))
+ {
+ cerr << "Codegen options file must have extension .kcfgc" << endl;
+ return 1;
+ }
+ QString baseName = QFileInfo(codegenFilename).fileName();
+ baseName = baseName.left(baseName.length() - 6);
+
+ CfgConfig cfg = CfgConfig( codegenFilename );
+
+ QFile input( inputFilename );
+
+ QDomDocument doc;
+ QString errorMsg;
+ int errorRow;
+ int errorCol;
+ if ( !doc.setContent( &input, &errorMsg, &errorRow, &errorCol ) ) {
+ cerr << "Unable to load document." << endl;
+ cerr << "Parse error in " << inputFilename << ", line " << errorRow << ", col " \
<< errorCol << ": " << errorMsg << endl; + return 1;
+ }
+
+ QDomElement cfgElement = doc.documentElement();
+
+ if ( cfgElement.isNull() ) {
+ cerr << "No document in kcfg file" << endl;
+ return 1;
+ }
+
+ QString cfgFileName;
+ bool cfgFileNameArg = false;
+ QList<Param> parameters;
+ QList<Signal> signalList;
+ QStringList includes;
+ bool hasSignals = false;
+
+ QList<CfgEntry*> entries;
+
+ for ( QDomElement e = cfgElement.firstChildElement(); !e.isNull(); e = \
e.nextSiblingElement() ) { + QString tag = e.tagName();
+
+ if ( tag == "include" ) {
+ QString includeFile = e.text();
+ if (!includeFile.isEmpty())
+ includes.append(includeFile);
+
+ } else if ( tag == "kcfgfile" ) {
+ cfgFileName = e.attribute( "name" );
+ cfgFileNameArg = e.attribute( "arg" ).toLower() == "true";
+ for( QDomElement e2 = e.firstChildElement(); !e2.isNull(); e2 = \
e2.nextSiblingElement() ) { + if ( e2.tagName() == "parameter" ) {
+ Param p;
+ p.name = e2.attribute( "name" );
+ p.type = e2.attribute( "type" );
+ if (p.type.isEmpty())
+ p.type = "String";
+ parameters.append( p );
+ }
+ }
+
+ } else if ( tag == "group" ) {
+ QString group = e.attribute( "name" );
+ if ( group.isEmpty() ) {
+ cerr << "Group without name" << endl;
+ return 1;
+ }
+ for( QDomElement e2 = e.firstChildElement(); !e2.isNull(); e2 = \
e2.nextSiblingElement() ) { + if ( e2.tagName() != "entry" ) continue;
+ CfgEntry *entry = parseEntry( group, e2, cfg );
+ if ( entry ) entries.append( entry );
+ else {
+ cerr << "Can not parse entry." << endl;
+ return 1;
+ }
+ }
+ }
+ else if ( tag == "signal" ) {
+ QString signalName = e.attribute( "name" );
+ if ( signalName.isEmpty() ) {
+ cerr << "Signal without name." << endl;
+ return 1;
+ }
+ Signal theSignal;
+ theSignal.name = signalName;
+
+ for( QDomElement e2 = e.firstChildElement(); !e2.isNull(); e2 = \
e2.nextSiblingElement() ) { + if ( e2.tagName() == "argument") {
+ SignalArguments argument;
+ argument.type = e2.attribute("type");
+ if ( argument.type.isEmpty() ) {
+ cerr << "Signal argument without type." << endl;
+ return 1;
+ }
+ argument.variableName = e2.text();
+ theSignal.arguments.append(argument);
+ }
+ else if( e2.tagName() == "label") {
+ theSignal.label = e2.text();
+ }
+ }
+ signalList.append(theSignal);
+ }
+ }
+
+ if ( cfg.className.isEmpty() ) {
+ cerr << "Class name missing" << endl;
+ return 1;
+ }
+
+ if ( cfg.singleton && !parameters.isEmpty() ) {
+ cerr << "Singleton class can not have parameters" << endl;
+ return 1;
+ }
+
+ if ( !cfgFileName.isEmpty() && cfgFileNameArg)
+ {
+ cerr << "Having both a fixed filename and a filename as argument is not \
possible." << endl; + return 1;
+ }
+
+ if ( entries.isEmpty() ) {
+ cerr << "No entries." << endl;
+ }
+
+#if 0
+ CfgEntry *cfg;
+ for( cfg = entries.first(); cfg; cfg = entries.next() ) {
+ cfg->dump();
+ }
+#endif
+
+ hasSignals = !signalList.empty();
+ QString headerFileName = baseName + ".h";
+ QString implementationFileName = baseName + ".cpp";
+ QString mocFileName = baseName + ".moc";
+ QString cppPreamble; // code to be inserted at the beginnin of the cpp file, e.g. \
initialization of static values +
+ QFile header( baseDir + headerFileName );
+ if ( !header.open( QIODevice::WriteOnly ) ) {
+ cerr << "Can not open '" << baseDir << headerFileName << "for writing." << \
endl; + return 1;
+ }
+
+ QTextStream h( &header );
+
+ h << "// This file is generated by kconfig_compiler from " << \
QFileInfo(inputFilename).fileName() << "." << endl; + h << "// All changes you do \
to this file will be lost." << endl; +
+ h << "#ifndef " << ( !cfg.nameSpace.isEmpty() ? QString \
(QString(cfg.nameSpace).replace( "::", "_" ).toUpper() + '_') : "" ) + << \
cfg.className.toUpper() << "_H" << endl; + h << "#define " << ( \
!cfg.nameSpace.isEmpty() ? QString (QString(cfg.nameSpace).replace( "::", "_" \
).toUpper() + '_') : "" ) + << cfg.className.toUpper() << "_H" << endl << endl;
+
+ // Includes
+ QStringList::ConstIterator it;
+ for( it = cfg.headerIncludes.constBegin(); it != cfg.headerIncludes.constEnd(); \
++it ) { + if ( (*it).startsWith('"') )
+ h << "#include " << *it << endl;
+ else
+ h << "#include <" << *it << ">" << endl;
+ }
+
+ if ( cfg.headerIncludes.count() > 0 ) h << endl;
+
+ if ( !cfg.singleton && parameters.isEmpty() )
+ h << "#include <qglobal.h>" << endl;
+
+ if ( cfg.inherits=="KCoreConfigSkeleton" ) {
+ h << "#include <kcoreconfigskeleton.h>" << endl;
+ } else {
+ h << "#include <kconfigskeleton.h>" << endl;
+ }
+
+ h << "#include <QDebug>" << endl << endl;
+
+ // Includes
+ for( it = includes.constBegin(); it != includes.constEnd(); ++it ) {
+ if ( (*it).startsWith('"') )
+ h << "#include " << *it << endl;
+ else
+ h << "#include <" << *it << ">" << endl;
+ }
+
+ beginNamespaces(cfg.nameSpace, h);
+
+ // Private class declaration
+ if ( cfg.dpointer )
+ h << "class " << cfg.className << "Private;" << endl << endl;
+
+ // Class declaration header
+ h << "class " << cfg.visibility << cfg.className << " : public " << cfg.inherits \
<< endl; +
+ h << "{" << endl;
+ // Add Q_OBJECT macro if the config need signals.
+ if( hasSignals )
+ h << " Q_OBJECT" << endl;
+ h << " public:" << endl;
+
+ // enums
+ QList<CfgEntry*>::ConstIterator itEntry;
+ for( itEntry = entries.constBegin(); itEntry != entries.constEnd(); ++itEntry ) {
+ const CfgEntry::Choices &choices = (*itEntry)->choices();
+ const QList<CfgEntry::Choice> chlist = choices.choices;
+ if ( !chlist.isEmpty() ) {
+ QStringList values;
+ QList<CfgEntry::Choice>::ConstIterator itChoice;
+ for( itChoice = chlist.constBegin(); itChoice != chlist.constEnd(); \
++itChoice ) { + values.append( choices.prefix + (*itChoice).name );
+ }
+ if ( choices.name().isEmpty() ) {
+ if ( cfg.globalEnums ) {
+ h << " enum " << enumName( (*itEntry)->name(), (*itEntry)->choices() ) \
<< " { " << values.join( ", " ) << " };" << endl; + } else {
+ // Create an automatically named enum
+ h << " class " << enumName( (*itEntry)->name(), (*itEntry)->choices() \
) << endl; + h << " {" << endl;
+ h << " public:" << endl;
+ h << " enum type { " << values.join( ", " ) << ", COUNT };" << endl;
+ h << " };" << endl;
+ }
+ } else if ( !choices.external() ) {
+ // Create a named enum
+ h << " enum " << enumName( (*itEntry)->name(), (*itEntry)->choices() ) \
<< " { " << values.join( ", " ) << " };" << endl; + }
+ }
+ const QStringList values = (*itEntry)->paramValues();
+ if ( !values.isEmpty() ) {
+ if ( cfg.globalEnums ) {
+ // ### FIXME!!
+ // make the following string table an index-based string search!
+ // ###
+ h << " enum " << enumName( (*itEntry)->param() ) << " { " << \
values.join( ", " ) << " };" << endl; + h << " static const char* const " \
<< enumName( (*itEntry)->param() ) << "ToString[];" << endl; + cppPreamble += \
"const char* const " + cfg.className + "::" + enumName( (*itEntry)->param() ) + + \
"ToString[] = { \"" + values.join( "\", \"" ) + "\" };\n"; + } else {
+ h << " class " << enumName( (*itEntry)->param() ) << endl;
+ h << " {" << endl;
+ h << " public:" << endl;
+ h << " enum type { " << values.join( ", " ) << ", COUNT };" << endl;
+ h << " static const char* const enumToString[];" << endl;
+ h << " };" << endl;
+ cppPreamble += "const char* const " + cfg.className + "::" + enumName( \
(*itEntry)->param() ) + + "::enumToString[] = { \"" + values.join( "\", \
\"" ) + "\" };\n"; + }
+ }
+ }
+ if ( hasSignals ) {
+ h << "\n enum {" << endl;
+ unsigned val = 1;
+ QList<Signal>::ConstIterator it, itEnd = signalList.constEnd();
+ for ( it = signalList.constBegin(); it != itEnd; val <<= 1) {
+ if ( !val ) {
+ cerr << "Too many signals to create unique bit masks" << endl;
+ exit(1);
+ }
+ Signal signal = *it;
+ h << " " << signalEnumName(signal.name) << " = 0x" << hex << val;
+ if ( ++it != itEnd )
+ h << ",";
+ h << endl;
+ }
+ h << " };" << dec << endl;
+ }
+ h << endl;
+ // Constructor or singleton accessor
+ if ( !cfg.singleton ) {
+ h << " " << cfg.className << "(";
+ if (cfgFileNameArg)
+ {
+ if(cfg.forceStringFilename)
+ h << " const QString &cfgfilename"
+ << (parameters.isEmpty() ? " = QString()" : ", ");
+ else
+ h << " KSharedConfig::Ptr config"
+ << (parameters.isEmpty() ? " = KSharedConfig::openConfig()" : ", \
"); + }
+ for (QList<Param>::ConstIterator it = parameters.constBegin();
+ it != parameters.constEnd(); ++it)
+ {
+ if (it != parameters.constBegin())
+ h << ",";
+ h << " " << param((*it).type) << " " << (*it).name;
+ }
+ h << " );" << endl;
+ } else {
+ h << " static " << cfg.className << " *self();" << endl;
+ if (cfgFileNameArg)
+ {
+ h << " static void instance(const QString& cfgfilename);" << endl;
+ }
+ }
+
+ // Destructor
+ h << " ~" << cfg.className << "();" << endl << endl;
+
+ // global variables
+ if (cfg.staticAccessors)
+ This = "self()->";
+ else
+ Const = " const";
+
+ for( itEntry = entries.constBegin(); itEntry != entries.constEnd(); ++itEntry ) {
+ QString n = (*itEntry)->name();
+ QString t = (*itEntry)->type();
+
+ // Manipulator
+ if (cfg.allMutators || cfg.mutators.contains(n))
+ {
+ h << " /**" << endl;
+ h << " Set " << (*itEntry)->label() << endl;
+ h << " */" << endl;
+ if (cfg.staticAccessors)
+ h << " static" << endl;
+ h << " void " << setFunction(n) << "( ";
+ if (!(*itEntry)->param().isEmpty())
+ h << cppType((*itEntry)->paramType()) << " i, ";
+ if (cfg.useEnumTypes && t == "Enum")
+ h << enumType(*itEntry, cfg.globalEnums);
+ else
+ h << param( t );
+ h << " v )";
+ // function body inline only if not using dpointer
+ // for BC mode
+ if ( !cfg.dpointer )
+ {
+ h << endl << " {" << endl;
+ h << indent(memberMutatorBody(*itEntry, cfg), 6 );
+ h << " }" << endl;
+ }
+ else
+ {
+ h << ";" << endl;
+ }
+ }
+ h << endl;
+ // Accessor
+ h << " /**" << endl;
+ h << " Get " << (*itEntry)->label() << endl;
+ h << " */" << endl;
+ if (cfg.staticAccessors)
+ h << " static" << endl;
+ h << " ";
+ if (cfg.useEnumTypes && t == "Enum")
+ h << enumType(*itEntry, cfg.globalEnums);
+ else
+ h << cppType(t);
+ h << " " << getFunction(n) << "(";
+ if (!(*itEntry)->param().isEmpty())
+ h << " " << cppType((*itEntry)->paramType()) <<" i ";
+ h << ")" << Const;
+ // function body inline only if not using dpointer
+ // for BC mode
+ if ( !cfg.dpointer )
+ {
+ h << endl << " {" << endl;
+ h << indent(memberAccessorBody(*itEntry, cfg.globalEnums, cfg), 6 );
+ h << " }" << endl;
+ }
+ else
+ {
+ h << ";" << endl;
+ }
+
+ // Default value Accessor
+ if ((cfg.allDefaultGetters || cfg.defaultGetters.contains(n)) && \
!(*itEntry)->defaultValue().isEmpty()) { + h << endl;
+ h << " /**" << endl;
+ h << " Get " << (*itEntry)->label() << " default value" << endl;
+ h << " */" << endl;
+ if (cfg.staticAccessors)
+ h << " static" << endl;
+ h << " ";
+ if (cfg.useEnumTypes && t == "Enum")
+ h << enumType(*itEntry, cfg.globalEnums);
+ else
+ h << cppType(t);
+ h << " " << getDefaultFunction(n) << "(";
+ if ( !(*itEntry)->param().isEmpty() )
+ h << " " << cppType( (*itEntry)->paramType() ) <<" i ";
+ h << ")" << Const << endl;
+ h << " {" << endl;
+ h << " return ";
+ if (cfg.useEnumTypes && t == "Enum")
+ h << "static_cast<" << enumType(*itEntry, cfg.globalEnums) << ">(";
+ h << getDefaultFunction(n) << "_helper(";
+ if ( !(*itEntry)->param().isEmpty() )
+ h << " i ";
+ h << ")";
+ if (cfg.useEnumTypes && t == "Enum")
+ h << ")";
+ h << ";" << endl;
+ h << " }" << endl;
+ }
+
+ // Item accessor
+ if ( cfg.itemAccessors ) {
+ h << endl;
+ h << " /**" << endl;
+ h << " Get Item object corresponding to " << n << "()"
+ << endl;
+ h << " */" << endl;
+ h << " Item" << itemType( (*itEntry)->type() ) << " *"
+ << getFunction( n ) << "Item(";
+ if (!(*itEntry)->param().isEmpty()) {
+ h << " " << cppType((*itEntry)->paramType()) << " i ";
+ }
+ h << ")";
+ if ( !cfg.dpointer )
+ {
+ h << endl << " {" << endl;
+ h << indent( itemAccessorBody((*itEntry), cfg), 6);
+ h << " }" << endl;
+ }
+ else
+ {
+ h << ";" << endl;
+ }
+ }
+
+ h << endl;
+ }
+
+
+ // Signal definition.
+ if( hasSignals ) {
+ h << endl;
+ h << " Q_SIGNALS:";
+ foreach(const Signal &signal, signalList) {
+ h << endl;
+ if ( !signal.label.isEmpty() ) {
+ h << " /**" << endl;
+ h << " " << signal.label << endl;
+ h << " */" << endl;
+ }
+ h << " void " << signal.name << "(";
+ QList<SignalArguments>::ConstIterator it, itEnd = \
signal.arguments.constEnd(); + for ( it = signal.arguments.constBegin(); it != \
itEnd; ) { + SignalArguments argument = *it;
+ QString type = param(argument.type);
+ if ( cfg.useEnumTypes && argument.type == "Enum" ) {
+ for ( int i = 0, end = entries.count(); i < end; ++i ) {
+ if ( entries[i]->name() == argument.variableName ) {
+ type = enumType(entries[i], cfg.globalEnums);
+ break;
+ }
+ }
+ }
+ h << type << " " << argument.variableName;
+ if ( ++it != itEnd ) {
+ h << ", ";
+ }
+ }
+ h << ");" << endl;
+ }
+ h << endl;
+ }
+
+ h << " protected:" << endl;
+
+ // Private constructor for singleton
+ if ( cfg.singleton ) {
+ h << " " << cfg.className << "(";
+ if ( cfgFileNameArg )
+ h << "const QString& arg";
+ h << ");" << endl;
+ h << " friend class " << cfg.className << "Helper;" << endl << endl;
+ }
+
+ if ( hasSignals ) {
+ h << " virtual void usrWriteConfig();" << endl;
+ }
+
+ // Member variables
+ if ( !cfg.memberVariables.isEmpty() && cfg.memberVariables != "private" && \
cfg.memberVariables != "dpointer") { + h << " " << cfg.memberVariables << ":" << \
endl; + }
+
+ // Class Parameters
+ for (QList<Param>::ConstIterator it = parameters.constBegin();
+ it != parameters.constEnd(); ++it)
+ {
+ h << " " << cppType((*it).type) << " mParam" << (*it).name << ";" << endl;
+ }
+
+ if ( cfg.memberVariables != "dpointer" )
+ {
+ QString group;
+ for( itEntry = entries.constBegin(); itEntry != entries.constEnd(); ++itEntry ) \
{ + if ( (*itEntry)->group() != group ) {
+ group = (*itEntry)->group();
+ h << endl;
+ h << " // " << group << endl;
+ }
+ h << " " << cppType( (*itEntry)->type() ) << " " << varName( \
(*itEntry)->name(), cfg ); + if ( !(*itEntry)->param().isEmpty() )
+ {
+ h << QString("[%1]").arg( (*itEntry)->paramMax()+1 );
+ }
+ h << ";" << endl;
+
+ if ( cfg.allDefaultGetters || cfg.defaultGetters.contains((*itEntry)->name()) \
) + {
+ h << " ";
+ if (cfg.staticAccessors)
+ h << "static ";
+ h << cppType((*itEntry)->type()) << " " << \
getDefaultFunction((*itEntry)->name()) << "_helper("; + if ( \
!(*itEntry)->param().isEmpty() ) + h << " " << cppType( \
(*itEntry)->paramType() ) <<" i "; + h << ")" << Const << ";" << endl;
+ }
+ }
+
+ h << endl << " private:" << endl;
+ if ( cfg.itemAccessors ) {
+ for( itEntry = entries.constBegin(); itEntry != entries.constEnd(); \
++itEntry ) { + h << " Item" << itemType( (*itEntry)->type() ) << " *" << \
itemVar( *itEntry, cfg ); + if ( !(*itEntry)->param().isEmpty() ) h << \
QString("[%1]").arg( (*itEntry)->paramMax()+1 ); + h << ";" << endl;
+ }
+ }
+ if ( hasSignals )
+ h << " uint " << varName("settingsChanged", cfg) << ";" << endl;
+
+ }
+ else
+ {
+ // use a private class for both member variables and items
+ h << " private:" << endl;
+ for( itEntry = entries.constBegin(); itEntry != entries.constEnd(); ++itEntry ) \
{ + if ( cfg.allDefaultGetters || \
cfg.defaultGetters.contains((*itEntry)->name()) ) { + h << " ";
+ if (cfg.staticAccessors)
+ h << "static ";
+ h << cppType((*itEntry)->type()) << " " << \
getDefaultFunction((*itEntry)->name()) << "_helper("; + if ( \
!(*itEntry)->param().isEmpty() ) + h << " " << cppType( \
(*itEntry)->paramType() ) <<" i "; + h << ")" << Const << ";" << endl;
+ }
+ }
+ h << " " + cfg.className + "Private *d;" << endl;
+ }
+
+ if (cfg.customAddons)
+ {
+ h << " // Include custom additions" << endl;
+ h << " #include \"" << filenameOnly(baseName) << "_addons.h\"" <<endl;
+ }
+
+ h << "};" << endl << endl;
+
+ endNamespaces(cfg.nameSpace, h);
+
+ h << "#endif" << endl << endl;
+
+
+ header.close();
+
+ QFile implementation( baseDir + implementationFileName );
+ if ( !implementation.open( QIODevice::WriteOnly ) ) {
+ cerr << "Can not open '" << implementationFileName << "for writing."
+ << endl;
+ return 1;
+ }
+
+ QTextStream cpp( &implementation );
+
+
+ cpp << "// This file is generated by kconfig_compiler from " << \
QFileInfo(inputFilename).fileName() << "." << endl; + cpp << "// All changes you do \
to this file will be lost." << endl << endl; +
+ cpp << "#include \"" << headerFileName << "\"" << endl << endl;
+
+ for( it = cfg.sourceIncludes.constBegin(); it != cfg.sourceIncludes.constEnd(); \
++it ) { + if ( (*it).startsWith('"') )
+ cpp << "#include " << *it << endl;
+ else
+ cpp << "#include <" << *it << ">" << endl;
+ }
+
+ if ( cfg.sourceIncludes.count() > 0 ) cpp << endl;
+
+ if ( cfg.setUserTexts && cfg.translationSystem==CfgConfig::KdeTranslation)
+ cpp << "#include <klocalizedstring.h>" << endl << endl;
+
+ // Header required by singleton implementation
+ if ( cfg.singleton )
+ cpp << "#include <qglobal.h>" << endl << "#include <QtCore/QFile>" << endl << \
endl; + if ( cfg.singleton && cfgFileNameArg )
+ cpp << "#include <QDebug>" << endl << endl;
+
+ if ( !cfg.nameSpace.isEmpty() )
+ cpp << "using namespace " << cfg.nameSpace << ";" << endl << endl;
+
+ QString group;
+
+ // private class implementation
+ if ( cfg.dpointer )
+ {
+ beginNamespaces(cfg.nameSpace, cpp);
+ cpp << "class " << cfg.className << "Private" << endl;
+ cpp << "{" << endl;
+ cpp << " public:" << endl;
+ for( itEntry = entries.constBegin(); itEntry != entries.constEnd(); ++itEntry ) \
{ + if ( (*itEntry)->group() != group ) {
+ group = (*itEntry)->group();
+ cpp << endl;
+ cpp << " // " << group << endl;
+ }
+ cpp << " " << cppType( (*itEntry)->type() ) << " " << varName( \
(*itEntry)->name(), cfg ); + if ( !(*itEntry)->param().isEmpty() )
+ {
+ cpp << QString("[%1]").arg( (*itEntry)->paramMax()+1 );
+ }
+ cpp << ";" << endl;
+ }
+ cpp << endl << " // items" << endl;
+ for( itEntry = entries.constBegin(); itEntry != entries.constEnd(); ++itEntry ) \
{ + cpp << " "+cfg.inherits+"::Item" << itemType( (*itEntry)->type() ) << " \
*" << itemVar( *itEntry, cfg ); + if ( !(*itEntry)->param().isEmpty() ) cpp << \
QString("[%1]").arg( (*itEntry)->paramMax()+1 ); + cpp << ";" << endl;
+ }
+ if ( hasSignals ) {
+ cpp << " uint " << varName("settingsChanged", cfg) << ";" << endl;
+ }
+
+ cpp << "};" << endl << endl;
+ endNamespaces(cfg.nameSpace, cpp);
+ }
+
+ // Singleton implementation
+ if ( cfg.singleton ) {
+ beginNamespaces(cfg.nameSpace, cpp);
+ cpp << "class " << cfg.className << "Helper" << endl;
+ cpp << '{' << endl;
+ cpp << " public:" << endl;
+ cpp << " " << cfg.className << "Helper() : q(0) {}" << endl;
+ cpp << " ~" << cfg.className << "Helper() { delete q; }" << endl;
+ cpp << " " << cfg.className << " *q;" << endl;
+ cpp << "};" << endl;
+ endNamespaces(cfg.nameSpace, cpp);
+ cpp << "Q_GLOBAL_STATIC(" << cfg.className << "Helper, s_global" << \
cfg.className << ")" << endl; +
+ cpp << cfg.className << " *" << cfg.className << "::self()" << endl;
+ cpp << "{" << endl;
+ if ( cfgFileNameArg ) {
+ cpp << " if (!s_global" << cfg.className << "()->q)" << endl;
+ cpp << " qFatal(\"you need to call " << cfg.className << "::instance \
before using\");" << endl; + } else {
+ cpp << " if (!s_global" << cfg.className << "()->q) {" << endl;
+ cpp << " new " << cfg.className << ';' << endl;
+ cpp << " s_global" << cfg.className << "()->q->readConfig();" << endl;
+ cpp << " }" << endl << endl;
+ }
+ cpp << " return s_global" << cfg.className << "()->q;" << endl;
+ cpp << "}" << endl << endl;
+
+ if ( cfgFileNameArg ) {
+ cpp << "void " << cfg.className << "::instance(const QString& cfgfilename)" \
<< endl; + cpp << "{" << endl;
+ cpp << " if (s_global" << cfg.className << "()->q) {" << endl;
+ cpp << " qDebug() << \"" << cfg.className << "::instance called after the \
first use - ignoring\";" << endl; + cpp << " return;" << endl;
+ cpp << " }" << endl;
+ cpp << " new " << cfg.className << "(cfgfilename);" << endl;
+ cpp << " s_global" << cfg.className << "()->q->readConfig();" << endl;
+ cpp << "}" << endl << endl;
+ }
+ }
+
+ if ( !cppPreamble.isEmpty() )
+ cpp << cppPreamble << endl;
+
+ // Constructor
+ cpp << cfg.className << "::" << cfg.className << "( ";
+ if ( cfgFileNameArg ) {
+ if ( !cfg.singleton && ! cfg.forceStringFilename)
+ cpp << " KSharedConfig::Ptr config";
+ else
+ cpp << " const QString& config";
+ cpp << (parameters.isEmpty() ? " " : ", ");
+ }
+
+ for (QList<Param>::ConstIterator it = parameters.constBegin();
+ it != parameters.constEnd(); ++it)
+ {
+ if (it != parameters.constBegin())
+ cpp << ",";
+ cpp << " " << param((*it).type) << " " << (*it).name;
+ }
+ cpp << " )" << endl;
+
+ cpp << " : " << cfg.inherits << "(";
+ if ( !cfgFileName.isEmpty() ) cpp << " QLatin1String( \"" << cfgFileName << "\" \
"; + if ( cfgFileNameArg ) cpp << " config ";
+ if ( !cfgFileName.isEmpty() ) cpp << ") ";
+ cpp << ")" << endl;
+
+ // Store parameters
+ for (QList<Param>::ConstIterator it = parameters.constBegin();
+ it != parameters.constEnd(); ++it)
+ {
+ cpp << " , mParam" << (*it).name << "(" << (*it).name << ")" << endl;
+ }
+
+ if ( hasSignals && !cfg.dpointer )
+ cpp << " , " << varName("settingsChanged", cfg) << "(0)" << endl;
+
+ cpp << "{" << endl;
+
+ if (cfg.dpointer)
+ {
+ cpp << " d = new " + cfg.className + "Private;" << endl;
+ if (hasSignals)
+ cpp << " " << varPath("settingsChanged", cfg) << " = 0;" << endl;
+ }
+ // Needed in case the singleton class is used as baseclass for
+ // another singleton.
+ if (cfg.singleton) {
+ cpp << " Q_ASSERT(!s_global" << cfg.className << "()->q);" << endl;
+ cpp << " s_global" << cfg.className << "()->q = this;" << endl;
+ }
+
+ group.clear();
+
+ for( itEntry = entries.constBegin(); itEntry != entries.constEnd(); ++itEntry ) {
+ if ( (*itEntry)->group() != group ) {
+ if ( !group.isEmpty() ) cpp << endl;
+ group = (*itEntry)->group();
+ cpp << " setCurrentGroup( " << paramString(group, parameters) << " );" << \
endl << endl; + }
+
+ QString key = paramString( (*itEntry)->key(), parameters );
+ if ( !(*itEntry)->code().isEmpty() ) {
+ cpp << (*itEntry)->code() << endl;
+ }
+ if ( (*itEntry)->type() == "Enum" ) {
+ cpp << " QList<"+cfg.inherits+"::ItemEnum::Choice> values"
+ << (*itEntry)->name() << ";" << endl;
+ const QList<CfgEntry::Choice> choices = (*itEntry)->choices().choices;
+ QList<CfgEntry::Choice>::ConstIterator it;
+ for( it = choices.constBegin(); it != choices.constEnd(); ++it ) {
+ cpp << " {" << endl;
+ cpp << " "+cfg.inherits+"::ItemEnum::Choice choice;" << endl;
+ cpp << " choice.name = QLatin1String(\"" << (*it).name << "\");" << \
endl; + if ( cfg.setUserTexts ) {
+ if ( !(*it).label.isEmpty() ) {
+ cpp << " choice.label = "
+ << translatedString(cfg, (*it).label, (*it).context)
+ << ";" << endl;
+ }
+ if ( !(*it).toolTip.isEmpty() ) {
+ cpp << " choice.toolTip = "
+ << translatedString(cfg, (*it).toolTip, (*it).context)
+ << ";" << endl;
+ }
+ if ( !(*it).whatsThis.isEmpty() ) {
+ cpp << " choice.whatsThis = "
+ << translatedString(cfg, (*it).whatsThis, (*it).context)
+ << ";" << endl;
+ }
+ }
+ cpp << " values" << (*itEntry)->name() << ".append( choice );" << endl;
+ cpp << " }" << endl;
+ }
+ }
+
+ if (!cfg.dpointer)
+ cpp << itemDeclaration( *itEntry, cfg );
+
+ if ( (*itEntry)->param().isEmpty() )
+ {
+ // Normal case
+ cpp << " " << itemPath( *itEntry, cfg ) << " = "
+ << newItem( (*itEntry)->type(), (*itEntry)->name(), key, \
(*itEntry)->defaultValue(), cfg ) << endl; +
+ if ( !(*itEntry)->minValue().isEmpty() )
+ cpp << " " << itemPath( *itEntry, cfg ) << "->setMinValue(" << \
(*itEntry)->minValue() << ");" << endl; + if ( \
!(*itEntry)->maxValue().isEmpty() ) + cpp << " " << itemPath( *itEntry, cfg \
) << "->setMaxValue(" << (*itEntry)->maxValue() << ");" << endl; +
+ if ( cfg.setUserTexts )
+ cpp << userTextsFunctions( (*itEntry), cfg );
+
+ cpp << " addItem( " << itemPath( *itEntry, cfg );
+ QString quotedName = (*itEntry)->name();
+ addQuotes( quotedName );
+ if ( quotedName != key ) cpp << ", QLatin1String( \"" << (*itEntry)->name() \
<< "\" )"; + cpp << " );" << endl;
+ }
+ else
+ {
+ // Indexed
+ for(int i = 0; i <= (*itEntry)->paramMax(); i++)
+ {
+ QString defaultStr;
+ QString itemVarStr(itemPath( *itEntry, cfg )+QString("[%1]").arg(i));
+
+ if ( !(*itEntry)->paramDefaultValue(i).isEmpty() )
+ defaultStr = (*itEntry)->paramDefaultValue(i);
+ else if ( !(*itEntry)->defaultValue().isEmpty() )
+ defaultStr = paramString( (*itEntry)->defaultValue(), (*itEntry), i );
+ else
+ defaultStr = defaultValue( (*itEntry)->type() );
+
+ cpp << " " << itemVarStr << " = "
+ << newItem( (*itEntry)->type(), (*itEntry)->name(), paramString(key, \
*itEntry, i), defaultStr,cfg, QString("[%1]").arg(i) ) + << endl;
+
+ if ( cfg.setUserTexts )
+ cpp << userTextsFunctions( *itEntry, cfg, itemVarStr, \
(*itEntry)->paramName() ); +
+ // Make mutators for enum parameters work by adding them with $(..) \
replaced by the + // param name. The check for isImmutable in the set* \
functions doesn't have the param + // name available, just the corresponding \
enum value (int), so we need to store the + // param names in a separate \
static list!. + cpp << " addItem( " << itemVarStr << ", QLatin1String( \"";
+ if ( (*itEntry)->paramType()=="Enum" )
+ cpp << (*itEntry)->paramName().replace( "$("+(*itEntry)->param()+')', \
"%1").arg((*itEntry)->paramValues()[i] ); + else
+ cpp << (*itEntry)->paramName().replace( "$("+(*itEntry)->param()+')', \
"%1").arg(i); + cpp << "\" ) );" << endl;
+ }
+ }
+ }
+
+ cpp << "}" << endl << endl;
+
+ if (cfg.dpointer)
+ {
+ // setters and getters go in Cpp if in dpointer mode
+ for( itEntry = entries.constBegin(); itEntry != entries.constEnd(); ++itEntry ) \
{ + QString n = (*itEntry)->name();
+ QString t = (*itEntry)->type();
+
+ // Manipulator
+ if (cfg.allMutators || cfg.mutators.contains(n))
+ {
+ cpp << "void " << setFunction(n, cfg.className) << "( ";
+ if ( !(*itEntry)->param().isEmpty() )
+ cpp << cppType( (*itEntry)->paramType() ) << " i, ";
+ if (cfg.useEnumTypes && t == "Enum")
+ cpp << enumType(*itEntry, cfg.globalEnums);
+ else
+ cpp << param( t );
+ cpp << " v )" << endl;
+ // function body inline only if not using dpointer
+ // for BC mode
+ cpp << "{" << endl;
+ cpp << indent(memberMutatorBody( *itEntry, cfg ), 6);
+ cpp << "}" << endl << endl;
+ }
+
+ // Accessor
+ if (cfg.useEnumTypes && t == "Enum")
+ cpp << enumType(*itEntry, cfg.globalEnums);
+ else
+ cpp << cppType(t);
+ cpp << " " << getFunction(n, cfg.className) << "(";
+ if ( !(*itEntry)->param().isEmpty() )
+ cpp << " " << cppType( (*itEntry)->paramType() ) <<" i ";
+ cpp << ")" << Const << endl;
+ // function body inline only if not using dpointer
+ // for BC mode
+ cpp << "{" << endl;
+ cpp << indent(memberAccessorBody( *itEntry, cfg.globalEnums, cfg ), 2);
+ cpp << "}" << endl << endl;
+
+ // Default value Accessor -- written by the loop below
+
+ // Item accessor
+ if ( cfg.itemAccessors )
+ {
+ cpp << endl;
+ cpp << cfg.inherits+"::Item" << itemType( (*itEntry)->type() ) << " *"
+ << getFunction( n, cfg.className ) << "Item(";
+ if ( !(*itEntry)->param().isEmpty() ) {
+ cpp << " " << cppType( (*itEntry)->paramType() ) << " i ";
+ }
+ cpp << ")" << endl;
+ cpp << "{" << endl;
+ cpp << indent(itemAccessorBody( *itEntry, cfg ), 2);
+ cpp << "}" << endl;
+ }
+
+ cpp << endl;
+ }
+ }
+
+ // default value getters always go in Cpp
+ for( itEntry = entries.constBegin(); itEntry != entries.constEnd(); ++itEntry ) {
+ QString n = (*itEntry)->name();
+ QString t = (*itEntry)->type();
+
+ // Default value Accessor, as "helper" function
+ if (( cfg.allDefaultGetters || cfg.defaultGetters.contains(n) ) && \
!(*itEntry)->defaultValue().isEmpty() ) { + cpp << cppType(t) << " " << \
getDefaultFunction(n, cfg.className) << "_helper("; + if ( \
!(*itEntry)->param().isEmpty() ) + cpp << " " << cppType( \
(*itEntry)->paramType() ) <<" i "; + cpp << ")" << Const << endl;
+ cpp << "{" << endl;
+ cpp << memberGetDefaultBody(*itEntry) << endl;
+ cpp << "}" << endl << endl;
+ }
+ }
+
+ // Destructor
+ cpp << cfg.className << "::~" << cfg.className << "()" << endl;
+ cpp << "{" << endl;
+ if ( cfg.singleton ) {
+ if ( cfg.dpointer )
+ cpp << " delete d;" << endl;
+ cpp << " s_global" << cfg.className << "()->q = 0;" << endl;
+ }
+ cpp << "}" << endl << endl;
+
+ if ( hasSignals ) {
+ cpp << "void " << cfg.className << "::" << "usrWriteConfig()" << endl;
+ cpp << "{" << endl;
+ cpp << " " << cfg.inherits << "::usrWriteConfig();" << endl << endl;
+ foreach(const Signal &signal, signalList) {
+ cpp << " if ( " << varPath("settingsChanged", cfg) << " & " << \
signalEnumName(signal.name) << " ) " << endl; + cpp << " emit " << \
signal.name << "("; + QList<SignalArguments>::ConstIterator it, itEnd = \
signal.arguments.constEnd(); + for ( it = signal.arguments.constBegin(); it != \
itEnd; ) { + SignalArguments argument = *it;
+ bool cast = false;
+ if ( cfg.useEnumTypes && argument.type == "Enum" ) {
+ for ( int i = 0, end = entries.count(); i < end; ++i ) {
+ if ( entries[i]->name() == argument.variableName ) {
+ cpp << "static_cast<" << enumType(entries[i], cfg.globalEnums) << \
">("; + cast = true;
+ break;
+ }
+ }
+ }
+ cpp << varPath(argument.variableName, cfg);
+ if ( cast )
+ cpp << ")";
+ if ( ++it != itEnd )
+ cpp << ", ";
+ }
+ cpp << ");" << endl << endl;
+ }
+ cpp << " " << varPath("settingsChanged", cfg) << " = 0;" << endl;
+ cpp << "}" << endl;
+ }
+
+ // Add includemoc if they are signals defined.
+ if( hasSignals ) {
+ cpp << endl;
+ cpp << "#include \"" << mocFileName << "\"" << endl;
+ cpp << endl;
+ }
+
+ // clear entries list
+ qDeleteAll( entries );
+
+ implementation.close();
+}
[prev in list] [next in list] [prev in thread] [next in thread]
Configure |
About |
News |
Add a list |
Sponsored by KoreLogic