Symbian/Linux: Remove GnuPoc autodetection code

and make it possible to configure it in the settings.
Split up S60Devices up into a class hierarchy and implement
the Autodetected Windows case and the manually configured
Linux case separately for code clarity. Same with the settings
widgets.
Reviewed-by: con
This commit is contained in:
Friedemann Kleint
2010-04-14 15:53:35 +02:00
parent 850c7251ab
commit fbfa825535
7 changed files with 803 additions and 225 deletions

View File

@@ -51,7 +51,7 @@ static QString gcceCommand(const QString &dir)
gcce += QLatin1String(".exe"); gcce += QLatin1String(".exe");
#endif #endif
const QString rc = env.searchInPath(gcce); const QString rc = env.searchInPath(gcce);
if (rc.isEmpty()) { if (debug && rc.isEmpty()) {
const QString msg = QString::fromLatin1("GCCEToolChain: Unable to locate '%1' in '%2' (GCCE root: '%3')") const QString msg = QString::fromLatin1("GCCEToolChain: Unable to locate '%1' in '%2' (GCCE root: '%3')")
.arg(gcce, env.value(QLatin1String("PATH")), dir); .arg(gcce, env.value(QLatin1String("PATH")), dir);
qWarning("%s", qPrintable(msg)); qWarning("%s", qPrintable(msg));

View File

@@ -31,6 +31,7 @@
#include "gccetoolchain.h" #include "gccetoolchain.h"
#include <projectexplorer/environment.h> #include <projectexplorer/environment.h>
#include <coreplugin/icore.h>
#include <QtCore/QSettings> #include <QtCore/QSettings>
#include <QtCore/QXmlStreamReader> #include <QtCore/QXmlStreamReader>
@@ -51,16 +52,36 @@ namespace {
const char * const DEVICE_DEFAULT = "default"; const char * const DEVICE_DEFAULT = "default";
const char * const DEVICE_EPOCROOT = "epocroot"; const char * const DEVICE_EPOCROOT = "epocroot";
const char * const DEVICE_TOOLSROOT = "toolsroot"; const char * const DEVICE_TOOLSROOT = "toolsroot";
const char * const GNUPOC_SETTINGS_GROUP = "GnuPocSDKs";
const char * const AUTODETECT_SETTINGS_GROUP = "SymbianSDKs";
const char * const SDK_QT_ASSOC_SETTINGS_KEY_ROOT = "SymbianSDK";
const char * const SETTINGS_DEFAULT_SDK_POSTFIX = ",default";
} }
namespace Qt4ProjectManager { namespace Qt4ProjectManager {
namespace Internal { namespace Internal {
static int findDefaultDevice(const QList<S60Devices::Device> &d)
{
const int count = d.size();
for (int i = 0; i < count; i++)
if (d.at(i).isDefault)
return i;
return -1;
}
S60Devices::Device::Device() : S60Devices::Device::Device() :
isDefault(false) isDefault(false)
{ {
} }
bool S60Devices::Device::equals(const Device &rhs) const
{
return id == rhs.id && name == rhs.name && isDefault == rhs.isDefault
&& epocRoot == rhs.epocRoot && toolsRoot == rhs.toolsRoot
&& qt == rhs.qt;
}
QString S60Devices::Device::toHtml() const QString S60Devices::Device::toHtml() const
{ {
QString rc; QString rc;
@@ -79,60 +100,180 @@ QString S60Devices::Device::toHtml() const
return rc; return rc;
} }
S60Devices::S60Devices(QObject *parent) // ------------ S60Devices
: QObject(parent) S60Devices::S60Devices(QObject *parent) : QObject(parent)
{ {
} }
// GNU-Poc stuff QList<S60Devices::Device> S60Devices::devices() const
static const char *epocRootC = "EPOCROOT";
static inline QString msgEnvVarNotSet(const char *var)
{ {
return QString::fromLatin1("The environment variable %1 is not set.").arg(QLatin1String(var)); return m_devices;
} }
static inline QString msgEnvVarDirNotExist(const QString &dir, const char *var) void S60Devices::setDevices(const QList<Device> &devices)
{ {
return QString::fromLatin1("The directory %1 pointed to by the environment variable %2 is not set.").arg(dir, QLatin1String(var)); if (m_devices != devices) {
} m_devices = devices;
// Ensure a default device
bool S60Devices::readLinux() if (!m_devices.isEmpty() && findDefaultDevice(m_devices) == -1)
{ m_devices.front().isDefault = true;
// Detect GNUPOC_ROOT/EPOC ROOT writeSettings();
const QByteArray epocRootA = qgetenv(epocRootC); emit qtVersionsChanged();
if (epocRootA.isEmpty()) {
m_errorString = msgEnvVarNotSet(epocRootC);
return false;
} }
const QDir epocRootDir(QString::fromLocal8Bit(epocRootA));
if (!epocRootDir.exists()) {
m_errorString = msgEnvVarDirNotExist(epocRootDir.absolutePath(), epocRootC);
return false;
}
// Check Qt
Device device;
device.id = device.name = QLatin1String("GnuPoc");
device.toolsRoot = device.epocRoot = epocRootDir.absolutePath();
device.isDefault = true;
m_devices.push_back(device);
return true;
} }
bool S60Devices::read() const QList<S60Devices::Device> &S60Devices::devicesList() const
{ {
m_devices.clear(); return m_devices;
m_errorString.clear(); }
QList<S60Devices::Device> &S60Devices::devicesList()
{
return m_devices;
}
S60Devices *S60Devices::createS60Devices(QObject *parent)
{
S60Devices *rc = 0;
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
return readWin(); AutoDetectS60QtDevices *ad = new AutoDetectS60QtDevices(parent);
ad->detectDevices();
rc = ad;
#else #else
return readLinux(); rc = new GnuPocS60Devices(parent);
#endif #endif
rc->readSettings();
return rc;
}
int S60Devices::findById(const QString &id) const
{
const int count = m_devices.size();
for (int i = 0; i < count; i++)
if (m_devices.at(i).id == id)
return i;
return -1;
}
int S60Devices::findByEpocRoot(const QString &er) const
{
const int count = m_devices.size();
for (int i = 0; i < count; i++)
if (m_devices.at(i).epocRoot == er)
return i;
return -1;
}
S60Devices::Device S60Devices::deviceForId(const QString &id) const
{
const int index = findById(id);
return index == -1 ? Device() : m_devices.at(index);
}
S60Devices::Device S60Devices::deviceForEpocRoot(const QString &root) const
{
const int index = findByEpocRoot(root);
return index == -1 ? Device() : m_devices.at(index);
}
S60Devices::Device S60Devices::defaultDevice() const
{
const int index = findDefaultDevice(m_devices);
return index == -1 ? Device() : m_devices.at(index);
}
QString S60Devices::cleanedRootPath(const QString &deviceRoot)
{
QString path = deviceRoot;
#ifdef Q_OS_WIN
// sbsv2 actually recommends having the DK on a separate drive...
// But qmake breaks when doing that!
if (path.size() > 1 && path.at(1) == QChar(':'))
path = path.mid(2);
#endif
if (!path.size() || path.at(path.size()-1) != '/')
path.append('/');
return path;
}
S60Devices::StringStringPairList S60Devices::readSdkQtAssociationSettings(const QSettings *settings,
const QString &group,
int *defaultIndexPtr)
{
StringStringPairList rc;
// Read out numbered pairs of EpocRoot/QtDir as many as exist
// "SymbianSDK1=/epoc,/qt[,default]".
const QChar separator = QLatin1Char(',');
const QString keyRoot = group + QLatin1Char('/') + QLatin1String(SDK_QT_ASSOC_SETTINGS_KEY_ROOT);
int defaultIndex = -1;
for (int i = 1; ; i++) {
// Split pairs of epocroot/qtdir.
const QVariant valueV = settings->value(keyRoot + QString::number(i));
if (!valueV.isValid())
break;
// Check for default postfix
QString value = valueV.toString();
if (value.endsWith(QLatin1String(SETTINGS_DEFAULT_SDK_POSTFIX))) {
value.truncate(value.size() - qstrlen(SETTINGS_DEFAULT_SDK_POSTFIX));
defaultIndex = rc.size();
}
// Split into SDK and Qt
const int separatorPos = value.indexOf(separator);
if (separatorPos == -1)
break;
const QString epocRoot = value.left(separatorPos);
const QString qtDir = value.mid(separatorPos + 1);
rc.push_back(StringStringPair(epocRoot, qtDir));
}
if (defaultIndexPtr)
*defaultIndexPtr = defaultIndex;
return rc;
}
void S60Devices::writeSdkQtAssociationSettings(QSettings *settings, const QString &group) const
{
// Write out as numbered pairs of EpocRoot/QtDir and indicate default
// "SymbianSDK1=/epoc,/qt[,default]".
settings->beginGroup(group);
settings->remove(QString()); // remove all keys
if (const int count = devicesList().size()) {
const QString keyRoot = QLatin1String(SDK_QT_ASSOC_SETTINGS_KEY_ROOT);
const QChar separator = QLatin1Char(',');
for (int i = 0; i < count; i++) {
const QString key = keyRoot + QString::number(i + 1);
QString value = devicesList().at(i).epocRoot;
value += separator;
value += devicesList().at(i).qt;
// Indicate default by postfix ",default"
if (devicesList().at(i).isDefault)
value += QLatin1String(SETTINGS_DEFAULT_SDK_POSTFIX);
settings->setValue(key, value);
}
}
settings->endGroup();
}
void S60Devices::readSettings()
{
}
void S60Devices::writeSettings()
{
}
// ------------------ S60Devices
AutoDetectS60Devices::AutoDetectS60Devices(QObject *parent) :
S60Devices(parent)
{
}
QString AutoDetectS60Devices::errorString() const
{
return m_errorString;
} }
// Windows: Get list of paths containing common program data
// as pointed to by environment. // as pointed to by environment.
static QStringList commonProgramFilesPaths() static QStringList commonProgramFilesPaths()
{ {
@@ -147,8 +288,6 @@ static QStringList commonProgramFilesPaths()
return rc; return rc;
} }
// Windows EPOC
// Find the "devices.xml" file containing the SDKs // Find the "devices.xml" file containing the SDKs
static QString devicesXmlFile(QString *errorMessage) static QString devicesXmlFile(QString *errorMessage)
{ {
@@ -174,8 +313,10 @@ static QString devicesXmlFile(QString *errorMessage)
return QString(); return QString();
} }
bool S60Devices::readWin() bool AutoDetectS60Devices::detectDevices()
{ {
devicesList().clear();
m_errorString.clear();
const QString devicesXmlPath = devicesXmlFile(&m_errorString); const QString devicesXmlPath = devicesXmlFile(&m_errorString);
if (devicesXmlPath.isEmpty()) if (devicesXmlPath.isEmpty())
return false; return false;
@@ -210,7 +351,7 @@ bool S60Devices::readWin()
} }
if (device.toolsRoot.isEmpty()) if (device.toolsRoot.isEmpty())
device.toolsRoot = device.epocRoot; device.toolsRoot = device.epocRoot;
m_devices.append(device); devicesList().append(device);
} }
} }
} else { } else {
@@ -227,6 +368,35 @@ bool S60Devices::readWin()
return true; return true;
} }
void AutoDetectS60Devices::readSettings()
{
// Read the associated Qt version from the settings
// and set on the autodetected SDKs.
bool changed = false;
const QSettings *settings = Core::ICore::instance()->settings();
foreach (const StringStringPair &p, readSdkQtAssociationSettings(settings, QLatin1String(GNUPOC_SETTINGS_GROUP))) {
const int index = findByEpocRoot(p.first);
if (index != -1 && devicesList().at(index).qt != p.second) {
devicesList()[index].qt = p.second;
changed = true;
}
}
if (changed)
emit qtVersionsChanged();
}
void AutoDetectS60Devices::writeSettings()
{
writeSdkQtAssociationSettings(Core::ICore::instance()->settings(), QLatin1String(AUTODETECT_SETTINGS_GROUP));
}
// ========== AutoDetectS60QtDevices
AutoDetectS60QtDevices::AutoDetectS60QtDevices(QObject *parent) :
AutoDetectS60Devices(parent)
{
}
// Detect a Qt version that is installed into a Symbian SDK // Detect a Qt version that is installed into a Symbian SDK
static QString detect_SDK_installedQt(const QString &epocRoot) static QString detect_SDK_installedQt(const QString &epocRoot)
{ {
@@ -263,28 +433,14 @@ static QString detect_SDK_installedQt(const QString &epocRoot)
return QDir(QString::fromLatin1(buffer.mid(index, lastIndex-index))).absolutePath(); return QDir(QString::fromLatin1(buffer.mid(index, lastIndex-index))).absolutePath();
} }
// GnuPoc: Detect a Qt version that is symlinked/below an SDK bool AutoDetectS60QtDevices::detectQtForDevices()
// TODO: Find a proper way of doing that
static QString detectGnuPocQt(const QString &epocRoot)
{
const QFileInfo fi(epocRoot + QLatin1String("/qt"));
if (!fi.exists())
return QString();
if (fi.isSymLink())
return QFileInfo(fi.symLinkTarget()).absoluteFilePath();
return fi.absoluteFilePath();
}
bool S60Devices::detectQtForDevices()
{ {
bool changed = false; bool changed = false;
const int deviceCount = m_devices.size(); const int deviceCount = devicesList().size();
for (int i = 0; i < deviceCount; ++i) { for (int i = 0; i < deviceCount; ++i) {
Device &device = m_devices[i]; Device &device = devicesList()[i];
if (device.qt.isEmpty()) { if (device.qt.isEmpty()) {
device.qt = detect_SDK_installedQt(device.epocRoot); device.qt = detect_SDK_installedQt(device.epocRoot);
if (device.qt.isEmpty())
device.qt = detectGnuPocQt(device.epocRoot);
if (device.qt.isEmpty()) { if (device.qt.isEmpty()) {
qWarning("Unable to detect Qt version for '%s'.", qPrintable(device.epocRoot)); qWarning("Unable to detect Qt version for '%s'.", qPrintable(device.epocRoot));
} else { } else {
@@ -297,59 +453,50 @@ bool S60Devices::detectQtForDevices()
return true; return true;
} }
QString S60Devices::errorString() const bool AutoDetectS60QtDevices::detectDevices()
{ {
return m_errorString; return AutoDetectS60Devices::detectDevices() && detectQtForDevices();
} }
QList<S60Devices::Device> S60Devices::devices() const // ------- GnuPocS60Devices
GnuPocS60Devices::GnuPocS60Devices(QObject *parent) :
S60Devices(parent)
{ {
return m_devices;
} }
S60Devices::Device S60Devices::deviceForId(const QString &id) const S60Devices::Device GnuPocS60Devices::createDevice(const QString &epoc, const QString &qtDir)
{ {
foreach (const S60Devices::Device &i, m_devices) { Device device;
if (i.id == id) { device.id = device.name = QLatin1String("GnuPoc");
return i; device.toolsRoot = device.epocRoot = epoc;
} device.qt = qtDir;
} return device;
return Device();
} }
S60Devices::Device S60Devices::deviceForEpocRoot(const QString &root) const // GnuPoc settings are just the pairs of EpocRoot and Qt Dir.
void GnuPocS60Devices::readSettings()
{ {
foreach (const S60Devices::Device &i, m_devices) { // Read out numbered pairs of EpocRoot/QtDir as many as exist
if (i.epocRoot == root) { // "SymbianSDK1=/epoc,/qt".
return i; devicesList().clear();
int defaultIndex = 0;
const QSettings *settings = Core::ICore::instance()->settings();
const StringStringPairList devices =readSdkQtAssociationSettings(settings, QLatin1String(GNUPOC_SETTINGS_GROUP), &defaultIndex);
foreach (const StringStringPair &p, devices)
devicesList().append(createDevice(p.first, p.second));
// Ensure a default
if (!devicesList().isEmpty()) {
if (defaultIndex >= 0 && defaultIndex < devicesList().size()) {
devicesList()[defaultIndex].isDefault = true;
} else {
devicesList().front().isDefault = true;
} }
} }
return Device();
} }
S60Devices::Device S60Devices::defaultDevice() const void GnuPocS60Devices::writeSettings()
{ {
foreach (const S60Devices::Device &i, m_devices) { writeSdkQtAssociationSettings(Core::ICore::instance()->settings(), QLatin1String(GNUPOC_SETTINGS_GROUP));
if (i.isDefault) {
return i;
}
}
return Device();
}
QString S60Devices::cleanedRootPath(const QString &deviceRoot)
{
QString path = deviceRoot;
#ifdef Q_OS_WIN
// sbsv2 actually recommends having the DK on a separate drive...
// But qmake breaks when doing that!
if (path.size() > 1 && path.at(1) == QChar(':'))
path = path.mid(2);
#endif
if (!path.size() || path.at(path.size()-1) != '/')
path.append('/');
return path;
} }
// S60ToolChainMixin // S60ToolChainMixin

View File

@@ -35,20 +35,25 @@
#include <QtCore/QObject> #include <QtCore/QObject>
#include <QtCore/QString> #include <QtCore/QString>
#include <QtCore/QList> #include <QtCore/QList>
#include <QtCore/QPair>
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
class QDebug; class QDebug;
class QSettings;
QT_END_NAMESPACE QT_END_NAMESPACE
namespace Qt4ProjectManager { namespace Qt4ProjectManager {
namespace Internal { namespace Internal {
// List of S60 devices.
class S60Devices : public QObject class S60Devices : public QObject
{ {
Q_OBJECT Q_OBJECT
public: public:
struct Device { struct Device {
Device(); Device();
bool equals(const Device &rhs) const;
QString toHtml() const; QString toHtml() const;
QString id; QString id;
@@ -59,26 +64,106 @@ public:
QString qt; QString qt;
}; };
S60Devices(QObject *parent = 0); // Construct a devices object, does autodetection if applicable
bool read(); // and restores settings.
QString errorString() const; static S60Devices *createS60Devices(QObject *parent);
QList<Device> devices() const; QList<Device> devices() const;
bool detectQtForDevices(); // Set devices, write settings and emit changed signals accordingly.
void setDevices(const QList<Device> &device);
Device deviceForId(const QString &id) const; Device deviceForId(const QString &id) const;
Device deviceForEpocRoot(const QString &root) const; Device deviceForEpocRoot(const QString &root) const;
Device defaultDevice() const; Device defaultDevice() const;
int findByEpocRoot(const QString &er) const;
static QString cleanedRootPath(const QString &deviceRoot); static QString cleanedRootPath(const QString &deviceRoot);
signals: signals:
void qtVersionsChanged(); void qtVersionsChanged();
protected:
typedef QPair<QString, QString> StringStringPair;
typedef QList<StringStringPair> StringStringPairList;
explicit S60Devices(QObject *parent = 0);
const QList<Device> &devicesList() const;
QList<Device> &devicesList();
int findById(const QString &id) const;
// Helpers to serialize the association of Symbian SDK<->Qt
// to QSettings (pair of SDK/Qt).
static StringStringPairList readSdkQtAssociationSettings(const QSettings *settings,
const QString &group,
int *defaultIndex = 0);
void writeSdkQtAssociationSettings(QSettings *settings, const QString &group) const;
private: private:
bool readLinux(); virtual void readSettings(); // empty stubs
bool readWin(); virtual void writeSettings();
QString m_errorString;
QList<Device> m_devices; QList<Device> m_devices;
}; };
inline bool operator==(const S60Devices::Device &d1, const S60Devices::Device &d2)
{ return d1.equals(d2); }
inline bool operator!=(const S60Devices::Device &d1, const S60Devices::Device &d2)
{ return !d1.equals(d2); }
// Autodetected Symbian Devices (as parsed from devices.xml file on Windows)
// with a manually set version of Qt. Currently not used, but might be by
// makefile-based builds on Windows.
class AutoDetectS60Devices : public S60Devices
{
Q_OBJECT
public:
explicit AutoDetectS60Devices(QObject *parent = 0);
virtual bool detectDevices();
QString errorString() const;
private:
// Write and restore Qt-SDK associations.
virtual void readSettings();
virtual void writeSettings();
QString m_errorString;
};
// Autodetected Symbian-Qt-Devices (with Qt installed
// into the SDK) for ABLD, Raptor. Completely autodetected.
class AutoDetectS60QtDevices : public AutoDetectS60Devices
{
Q_OBJECT
public:
explicit AutoDetectS60QtDevices(QObject *parent = 0);
// Overwritten to detect associated Qt versions in addition.
virtual bool detectDevices();
private:
// No settings as it is completely autodetected.
virtual void readSettings() {}
virtual void writeSettings() {}
bool detectQtForDevices();
};
// Manually configured Symbian Devices completely based on QSettings.
class GnuPocS60Devices : public S60Devices
{
Q_OBJECT
public:
explicit GnuPocS60Devices(QObject *parent = 0);
static Device createDevice(const QString &epoc, const QString &qtDir);
private:
virtual void readSettings();
virtual void writeSettings();
};
/* Mixin for the toolchains with convenience functions for EPOC /* Mixin for the toolchains with convenience functions for EPOC
* (Windows) and GnuPoc (Linux). */ * (Windows) and GnuPoc (Linux). */

View File

@@ -34,50 +34,207 @@
#include <qt4projectmanager/qt4projectmanagerconstants.h> #include <qt4projectmanager/qt4projectmanagerconstants.h>
#include <utils/qtcassert.h> #include <utils/qtcassert.h>
#include <coreplugin/coreconstants.h>
#include <QtCore/QDir> #include <QtCore/QDir>
#include <QtCore/QtDebug> #include <QtCore/QtDebug>
#include <QtCore/QSharedPointer>
using namespace Qt4ProjectManager; #include <QtGui/QFileDialog>
using namespace Qt4ProjectManager::Internal; #include <QtGui/QMessageBox>
#include <QtGui/QIcon>
#include <QtGui/QApplication>
#include <QtGui/QStyle>
#include <QtGui/QStandardItemModel>
#include <QtGui/QStandardItem>
S60DevicesWidget::S60DevicesWidget(QWidget *parent, S60Devices *devices) : enum { deviceRole = Qt::UserRole + 1 };
QWidget(parent),
m_ui(new Ui::S60DevicesPreferencePane), enum Columns { DefaultColumn, EpocColumn, QtColumn, ColumnCount };
m_devices(devices)
typedef QSharedPointer<Qt4ProjectManager::Internal::S60Devices::Device> DevicePtr;
Q_DECLARE_METATYPE(DevicePtr)
typedef QList<QStandardItem *> StandardItemList;
namespace Qt4ProjectManager {
namespace Internal {
static inline DevicePtr deviceFromItem(const QStandardItem *item)
{ {
m_ui->setupUi(this); return qvariant_cast<DevicePtr>(item->data(deviceRole));
connect(m_ui->refreshButton, SIGNAL(clicked()), this, SLOT(updateDevices()));
updateDevicesList();
} }
S60DevicesWidget::~S60DevicesWidget() // Device model storing a shared pointer to the device as user data.
// Provides a checkable 'default' column which works exclusively.
class S60DevicesModel : public QStandardItemModel {
Q_OBJECT
public:
typedef QList<S60Devices::Device> DeviceList;
explicit S60DevicesModel(bool defaultColumnCheckable, QObject *parent = 0);
void setDevices(const DeviceList &list);
DeviceList devices() const;
void appendDevice(const S60Devices::Device &device);
private slots:
void slotItemChanged(QStandardItem *item);
private:
const bool m_defaultColumnCheckable;
};
S60DevicesModel::S60DevicesModel(bool defaultColumnCheckable, QObject *parent) :
QStandardItemModel(0, ColumnCount, parent),
m_defaultColumnCheckable(defaultColumnCheckable)
{
QStringList headers;
headers << S60DevicesBaseWidget::tr("Default")
<< S60DevicesBaseWidget::tr("SDK Location")
<< S60DevicesBaseWidget::tr("Qt Location");
setHorizontalHeaderLabels(headers);
if (m_defaultColumnCheckable)
connect(this, SIGNAL(itemChanged(QStandardItem*)),
this, SLOT(slotItemChanged(QStandardItem*)));
}
void S60DevicesModel::appendDevice(const S60Devices::Device &device)
{
// Create SDK/Qt column items with shared pointer to entry as data.
const QVariant deviceData = qVariantFromValue(DevicePtr(new S60Devices::Device(device)));
const Qt::ItemFlags flags = Qt::ItemIsEnabled|Qt::ItemIsSelectable;
QStandardItem *defaultItem = new QStandardItem;
defaultItem->setCheckable(true);
defaultItem->setCheckState(device.isDefault ? Qt::Checked : Qt::Unchecked);
// Item is only checkable if it is not the default.
Qt::ItemFlags checkFlags = flags;
if (!device.isDefault && m_defaultColumnCheckable)
checkFlags |= Qt::ItemIsUserCheckable;
defaultItem->setFlags(checkFlags);
defaultItem->setData(deviceData);
QStandardItem *epocItem = new QStandardItem(QDir::toNativeSeparators(device.epocRoot));
epocItem->setFlags(flags);
epocItem->setData(deviceData);
const QString qtDesc = device.qt.isEmpty() ?
S60DevicesModel::tr("No Qt installed") :
QDir::toNativeSeparators(device.qt);
QStandardItem *qtItem = new QStandardItem(qtDesc);
qtItem->setFlags(flags);
qtItem->setData(deviceData);
const QString tooltip = device.toHtml();
epocItem->setToolTip(tooltip);
qtItem->setToolTip(tooltip);
StandardItemList row;
row << defaultItem << epocItem << qtItem;
appendRow(row);
}
void S60DevicesModel::setDevices(const DeviceList &list)
{
removeRows(0, rowCount());
foreach(const S60Devices::Device &device, list)
appendDevice(device);
}
S60DevicesModel::DeviceList S60DevicesModel::devices() const
{
S60DevicesModel::DeviceList rc;
const int count = rowCount();
for (int r = 0; r < count; r++)
rc.push_back(S60Devices::Device(*deviceFromItem(item(r, 0))));
return rc;
}
void S60DevicesModel::slotItemChanged(QStandardItem *changedItem)
{
// Sync all "default" checkmarks. Emulate an exclusive group
// by enabling only the unchecked items (preventing the user from unchecking)
// and uncheck all other items. Protect against recursion.
if (changedItem->column() != DefaultColumn || changedItem->checkState() != Qt::Checked)
return;
const int row = changedItem->row();
const int count = rowCount();
for (int r = 0; r < count; r++) {
QStandardItem *rowItem = item(r, DefaultColumn);
if (r == row) { // Prevent uncheck.
rowItem->setFlags(rowItem->flags() & ~Qt::ItemIsUserCheckable);
deviceFromItem(rowItem)->isDefault = true;
} else {
// Uncheck others.
rowItem->setCheckState(Qt::Unchecked);
rowItem->setFlags(rowItem->flags() | Qt::ItemIsUserCheckable);
deviceFromItem(rowItem)->isDefault = false;
}
}
}
// --------------- S60DevicesBaseWidget
S60DevicesBaseWidget::S60DevicesBaseWidget(unsigned flags, QWidget *parent) :
QWidget(parent),
m_ui(new Ui::S60DevicesPreferencePane),
m_model(new S60DevicesModel(flags & DeviceDefaultCheckable))
{
m_ui->setupUi(this);
m_ui->addButton->setIcon(QIcon(QLatin1String(Core::Constants::ICON_PLUS)));
connect(m_ui->addButton, SIGNAL(clicked()), this, SLOT(addDevice()));
m_ui->removeButton->setIcon(QIcon(QLatin1String(Core::Constants::ICON_MINUS)));
connect(m_ui->removeButton, SIGNAL(clicked()), this, SLOT(removeDevice()));
m_ui->refreshButton->setIcon(qApp->style()->standardIcon(QStyle::SP_BrowserReload));
connect(m_ui->refreshButton, SIGNAL(clicked()), this, SLOT(refresh()));
m_ui->changeQtButton->setIcon(QIcon(QLatin1String(":/welcome/images/qt_logo.png")));
connect(m_ui->changeQtButton, SIGNAL(clicked()), this, SLOT(changeQtVersion()));
m_ui->list->setModel(m_model);
connect(m_ui->list->selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)),
this, SLOT(currentChanged(QModelIndex,QModelIndex)));
m_ui->addButton->setVisible(flags & ShowAddButton);
m_ui->removeButton->setVisible(flags & ShowAddButton);
m_ui->removeButton->setEnabled(false);
m_ui->changeQtButton->setVisible(flags & ShowChangeQtButton);
m_ui->removeButton->setEnabled(false);
m_ui->refreshButton->setVisible(flags & ShowRefreshButton);
}
S60DevicesBaseWidget::~S60DevicesBaseWidget()
{ {
delete m_ui; delete m_ui;
} }
void S60DevicesWidget::updateDevices() QStandardItem *S60DevicesBaseWidget::currentItem() const
{ {
m_devices->read(); // Return the column-0 item.
QTC_ASSERT(m_devices->detectQtForDevices(), return); QModelIndex current = m_ui->list->currentIndex();
updateDevicesList(); if (current.isValid()) {
if (current.row() != 0)
current = current.sibling(current.row(), 0);
return m_model->itemFromIndex(current);
}
return 0;
} }
void S60DevicesWidget::updateDevicesList() S60DevicesBaseWidget::DeviceList S60DevicesBaseWidget::devices() const
{ {
QList<S60Devices::Device> devices = m_devices->devices(); return m_model->devices();
m_ui->list->clear(); }
foreach (const S60Devices::Device &device, devices) {
QStringList columns; void S60DevicesBaseWidget::setDevices(const DeviceList &s60devices,
columns << QDir::toNativeSeparators(device.epocRoot) const QString &errorString)
<< (device.qt.isEmpty()?tr("No Qt installed"):QDir::toNativeSeparators(device.qt)); {
QTreeWidgetItem *item = new QTreeWidgetItem(columns); m_model->setDevices(s60devices);
const QString tooltip = device.toHtml();
item->setToolTip(0, tooltip); for (int c = 0; c < ColumnCount; c++)
item->setToolTip(1, tooltip); m_ui->list->resizeColumnToContents(c);
m_ui->list->addTopLevelItem(item);
}
const QString errorString = m_devices->errorString();
if (errorString.isEmpty()) { if (errorString.isEmpty()) {
clearErrorLabel(); clearErrorLabel();
} else { } else {
@@ -85,24 +242,114 @@ void S60DevicesWidget::updateDevicesList()
} }
} }
void S60DevicesWidget::setErrorLabel(const QString& t) void S60DevicesBaseWidget::changeQtVersion()
{
if (const QStandardItem *item = currentItem()) {
const QString qtDir = promptDirectory(tr("Choose Qt folder"));
if (!qtDir.isEmpty()) {
const DevicePtr device = deviceFromItem(item);
device->qt = qtDir;
}
}
}
void S60DevicesBaseWidget::removeDevice()
{
if (const QStandardItem *item = currentItem())
m_model->removeRows(item->row(), 1);
}
void S60DevicesBaseWidget::currentChanged(const QModelIndex &current,
const QModelIndex & /* previous */)
{
const bool hasItem = current.isValid();
m_ui->changeQtButton->setEnabled(hasItem);
m_ui->removeButton->setEnabled(hasItem);
}
void S60DevicesBaseWidget::setErrorLabel(const QString& t)
{ {
m_ui->errorLabel->setText(t); m_ui->errorLabel->setText(t);
m_ui->errorLabel->setVisible(true); m_ui->errorLabel->setVisible(true);
} }
void S60DevicesWidget::clearErrorLabel() void S60DevicesBaseWidget::clearErrorLabel()
{ {
m_ui->errorLabel->setVisible(false); m_ui->errorLabel->setVisible(false);
} }
QString S60DevicesBaseWidget::promptDirectory(const QString &title)
{
return QFileDialog::getExistingDirectory(this, title);
}
void S60DevicesBaseWidget::appendDevice(const S60Devices::Device &d)
{
m_model->appendDevice(d);
}
int S60DevicesBaseWidget::deviceCount() const
{
return m_model->rowCount();
}
// ============ AutoDetectS60DevicesWidget
AutoDetectS60DevicesWidget::AutoDetectS60DevicesWidget(QWidget *parent,
AutoDetectS60Devices *devices,
bool changeQtVersionEnabled) :
S60DevicesBaseWidget(ShowRefreshButton | (changeQtVersionEnabled ? unsigned(ShowChangeQtButton) : 0u),
parent),
m_devices(devices)
{
refresh();
}
void AutoDetectS60DevicesWidget::refresh()
{
m_devices->detectDevices();
setDevices(m_devices->devices(), m_devices->errorString());
}
// ============ GnuPocS60DevicesWidget
GnuPocS60DevicesWidget::GnuPocS60DevicesWidget(QWidget *parent) :
S60DevicesBaseWidget(ShowAddButton|ShowRemoveButton|ShowChangeQtButton|DeviceDefaultCheckable,
parent)
{
}
void GnuPocS60DevicesWidget::addDevice()
{
// 1) Prompt for GnuPoc
const QString epocRoot = promptDirectory(tr("Step 1 of 2: Choose GnuPoc folder"));
if (epocRoot.isEmpty())
return;
// 2) Prompt for Qt. Catch equal inputs just in case someone clicks very rapidly.
QString qtDir;
while (true) {
qtDir = promptDirectory(tr("Step 2 of 2: Choose Qt folder"));
if (qtDir.isEmpty())
return;
if (qtDir == epocRoot) {
QMessageBox::warning(this, tr("Adding GnuPoc"),
tr("GnuPoc and Qt folders must not be identical."));
} else {
break;
}
}
// Add a device, make default if first.
S60Devices::Device device = GnuPocS60Devices::createDevice(epocRoot, qtDir);
if (deviceCount() == 0)
device.isDefault = true;
appendDevice(device);
}
// ================= S60DevicesPreferencePane
S60DevicesPreferencePane::S60DevicesPreferencePane(S60Devices *devices, QObject *parent) S60DevicesPreferencePane::S60DevicesPreferencePane(S60Devices *devices, QObject *parent)
: Core::IOptionsPage(parent), : Core::IOptionsPage(parent),
m_widget(0), m_widget(0),
m_devices(devices) m_devices(devices)
{ {
m_devices->read();
} }
S60DevicesPreferencePane::~S60DevicesPreferencePane() S60DevicesPreferencePane::~S60DevicesPreferencePane()
@@ -134,18 +381,43 @@ QIcon S60DevicesPreferencePane::categoryIcon() const
return QIcon(Constants::QT_SETTINGS_CATEGORY_ICON); return QIcon(Constants::QT_SETTINGS_CATEGORY_ICON);
} }
S60DevicesBaseWidget *S60DevicesPreferencePane::createWidget(QWidget *parent) const
{
// Symbian ABLD/Raptor: Qt installed into SDK, cannot change
if (AutoDetectS60QtDevices *aqd = qobject_cast<AutoDetectS60QtDevices *>(m_devices))
return new AutoDetectS60DevicesWidget(parent, aqd, false);
// Not used yet: Manual association of Qt with auto-detected SDK
if (AutoDetectS60Devices *ad = qobject_cast<AutoDetectS60Devices *>(m_devices))
return new AutoDetectS60DevicesWidget(parent, ad, true);
if (GnuPocS60Devices *gd = qobject_cast<GnuPocS60Devices*>(m_devices)) {
GnuPocS60DevicesWidget *gw = new GnuPocS60DevicesWidget(parent);
gw->setDevices(gd->devices());
return gw;
}
return 0; // Huh?
}
QWidget *S60DevicesPreferencePane::createPage(QWidget *parent) QWidget *S60DevicesPreferencePane::createPage(QWidget *parent)
{ {
if (m_widget) if (m_widget)
delete m_widget; delete m_widget;
m_widget = new S60DevicesWidget(parent, m_devices); m_widget = createWidget(parent);
QTC_ASSERT(m_widget, return 0)
return m_widget; return m_widget;
} }
void S60DevicesPreferencePane::apply() void S60DevicesPreferencePane::apply()
{ {
QTC_ASSERT(m_widget, return)
m_devices->setDevices(m_widget->devices());
} }
void S60DevicesPreferencePane::finish() void S60DevicesPreferencePane::finish()
{ {
} }
} // namespace Internal
} // namespace Qt4ProjectManager
#include "s60devicespreferencepane.moc"

View File

@@ -30,38 +30,96 @@
#ifndef S60DEVICESPREFERENCEPANE_H #ifndef S60DEVICESPREFERENCEPANE_H
#define S60DEVICESPREFERENCEPANE_H #define S60DEVICESPREFERENCEPANE_H
#include "s60devices.h"
#include <coreplugin/dialogs/ioptionspage.h> #include <coreplugin/dialogs/ioptionspage.h>
#include <QtCore/QPointer> #include <QtCore/QPointer>
#include <QtGui/QWidget> #include <QtGui/QWidget>
QT_BEGIN_NAMESPACE
class QStandardItem;
class QAbstractButton;
class QModelIndex;
QT_END_NAMESPACE
namespace Qt4ProjectManager { namespace Qt4ProjectManager {
namespace Internal { namespace Internal {
class S60Devices; class AutoDetectS60Devices;
class GnuPocS60Devices;
class S60DevicesModel;
namespace Ui { namespace Ui {
class S60DevicesPreferencePane; class S60DevicesPreferencePane;
} }
class S60DevicesWidget : public QWidget // Base pane listing Symbian SDKs with "Change Qt" version functionality.
class S60DevicesBaseWidget : public QWidget
{ {
Q_OBJECT Q_OBJECT
public: public:
S60DevicesWidget(QWidget *parent, S60Devices *devices); typedef QList<S60Devices::Device> DeviceList;
~S60DevicesWidget();
private slots: enum Flags { ShowAddButton = 0x1, ShowRemoveButton = 0x2,
void updateDevices(); ShowChangeQtButton =0x4, ShowRefreshButton = 0x8,
DeviceDefaultCheckable = 0x10 };
virtual ~S60DevicesBaseWidget();
DeviceList devices() const;
void setDevices(const DeviceList &dl, const QString &errorString = QString());
protected:
explicit S60DevicesBaseWidget(unsigned flags, QWidget *parent = 0);
private:
void updateDevicesList();
void setErrorLabel(const QString&); void setErrorLabel(const QString&);
void clearErrorLabel(); void clearErrorLabel();
QString promptDirectory(const QString &title);
void appendDevice(const S60Devices::Device &d);
int deviceCount() const;
QStandardItem *currentItem() const;
private slots:
void currentChanged(const QModelIndex &current, const QModelIndex &previous);
void changeQtVersion();
void removeDevice();
virtual void addDevice() {} // Default does nothing
virtual void refresh() {} // Default does nothing
private:
Ui::S60DevicesPreferencePane *m_ui; Ui::S60DevicesPreferencePane *m_ui;
S60Devices *m_devices; S60DevicesModel *m_model;
}; };
// Widget for autodetected SDK's showing a refresh button.
class AutoDetectS60DevicesWidget : public S60DevicesBaseWidget
{
Q_OBJECT
public:
explicit AutoDetectS60DevicesWidget(QWidget *parent,
AutoDetectS60Devices *devices,
bool changeQtVersionEnabled);
private slots:
virtual void refresh();
private:
AutoDetectS60Devices *m_devices;
};
// Widget for manually configured SDK's showing a add/remove buttons.
class GnuPocS60DevicesWidget : public S60DevicesBaseWidget
{
Q_OBJECT
public:
explicit GnuPocS60DevicesWidget(QWidget *parent = 0);
private slots:
virtual void addDevice();
};
// Options Pane.
class S60DevicesPreferencePane : public Core::IOptionsPage class S60DevicesPreferencePane : public Core::IOptionsPage
{ {
Q_OBJECT Q_OBJECT
@@ -80,7 +138,9 @@ public:
void finish(); void finish();
private: private:
QPointer<S60DevicesWidget> m_widget; S60DevicesBaseWidget *createWidget(QWidget *parent) const;
QPointer<S60DevicesBaseWidget> m_widget;
S60Devices *m_devices; S60Devices *m_devices;
}; };

View File

@@ -6,16 +6,21 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>274</width> <width>316</width>
<height>264</height> <height>241</height>
</rect> </rect>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
<string>Form</string> <string>Form</string>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QGroupBox" name="groupBox">
<layout class="QVBoxLayout" name="verticalLayout"> <layout class="QVBoxLayout" name="verticalLayout">
<item> <item>
<widget class="QTreeWidget" name="list"> <layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QTreeView" name="list">
<property name="indentation"> <property name="indentation">
<number>0</number> <number>0</number>
</property> </property>
@@ -25,31 +30,62 @@
<property name="uniformRowHeights"> <property name="uniformRowHeights">
<bool>true</bool> <bool>true</bool>
</property> </property>
<property name="columnCount"> <property name="allColumnsShowFocus">
<number>2</number>
</property>
<attribute name="headerCascadingSectionResizes">
<bool>true</bool> <bool>true</bool>
</attribute>
<attribute name="headerCascadingSectionResizes">
<bool>true</bool>
</attribute>
<column>
<property name="text">
<string>SDK Location</string>
</property> </property>
</column>
<column>
<property name="text">
<string>Qt Location</string>
</property>
</column>
</widget> </widget>
</item> </item>
<item>
<layout class="QVBoxLayout" name="buttonLayout">
<item>
<widget class="QToolButton" name="refreshButton">
<property name="toolTip">
<string>Refresh</string>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="addButton">
<property name="toolTip">
<string>Add</string>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="changeQtButton">
<property name="toolTip">
<string>Change Qt version</string>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="removeButton">
<property name="toolTip">
<string>Remove</string>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</item>
<item> <item>
<widget class="QLabel" name="errorLabel"> <widget class="QLabel" name="errorLabel">
<property name="styleSheet"> <property name="styleSheet">
<string notr="true">color: red;</string> <string notr="true">background-color: red;</string>
</property> </property>
<property name="text"> <property name="text">
<string>Error</string> <string>Error</string>
@@ -62,29 +98,8 @@
</property> </property>
</widget> </widget>
</item> </item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="refreshButton">
<property name="text">
<string>Refresh</string>
</property>
</widget>
</item>
</layout> </layout>
</widget>
</item> </item>
</layout> </layout>
</widget> </widget>

View File

@@ -101,14 +101,13 @@ private:
S60Manager *S60Manager::instance() { return m_instance; } S60Manager *S60Manager::instance() { return m_instance; }
S60Manager::S60Manager(QObject *parent) S60Manager::S60Manager(QObject *parent)
: QObject(parent), : QObject(parent), m_devices(S60Devices::createS60Devices(this))
m_devices(new S60Devices(this))
{ {
m_instance = this; m_instance = this;
#ifdef QTCREATOR_WITH_S60 #ifdef QTCREATOR_WITH_S60
addAutoReleasedObject(new S60DevicesPreferencePane(m_devices, this)); addAutoReleasedObject(new S60DevicesPreferencePane(m_devices, this));
#endif #endif
m_devices->detectQtForDevices(); // Order!
addAutoReleasedObject(new S60EmulatorRunConfigurationFactory); addAutoReleasedObject(new S60EmulatorRunConfigurationFactory);
addAutoReleasedObject(new RunControlFactory<S60EmulatorRunControl, addAutoReleasedObject(new RunControlFactory<S60EmulatorRunControl,