diff options
author | Jarek Kobus <jkobus@trolltech.com> | 2010-08-16 18:23:30 +0200 |
---|---|---|
committer | Jarek Kobus <jkobus@trolltech.com> | 2010-08-16 18:25:38 +0200 |
commit | 573006f3707247980c8eace6682352acb53f51c7 (patch) | |
tree | a871a6d227084cbc37ba1932dcdbf02c16ee428d /src/plugins | |
parent | 8cd3b54949bf0be5a7d1b75c4e591b0b79dab990 (diff) | |
download | qt-creator-573006f3707247980c8eace6682352acb53f51c7.tar.gz |
Add "Add library wizard" to the pro file editor
Reviewed-by: dt <qtc-committer@nokia.com>
Task-number: QTCREATORBUG-125
Diffstat (limited to 'src/plugins')
16 files changed, 1850 insertions, 28 deletions
diff --git a/src/plugins/qt4projectmanager/addlibrarywizard.cpp b/src/plugins/qt4projectmanager/addlibrarywizard.cpp new file mode 100644 index 0000000000..da99e2889a --- /dev/null +++ b/src/plugins/qt4projectmanager/addlibrarywizard.cpp @@ -0,0 +1,264 @@ +#include "addlibrarywizard.h" +#include "ui_librarydetailswidget.h" +#include "librarydetailscontroller.h" + +#include <QtGui/QVBoxLayout> +#include <QtGui/QRadioButton> +#include <QtGui/QLabel> +#include <QtCore/QFileInfo> + +#include <QDebug> + +using namespace Qt4ProjectManager; +using namespace Qt4ProjectManager::Internal; + + +const char *qt_file_dialog_filter_reg_exp = +"^(.*)\\(([a-zA-Z0-9_.*? +;#\\-\\[\\]@\\{\\}/!<>\\$%&=^~:\\|]*)\\)$"; + +// taken from qfiledialog.cpp +QStringList qt_clean_filter_list(const QString &filter) +{ + QRegExp regexp(QString::fromLatin1(qt_file_dialog_filter_reg_exp)); + QString f = filter; + int i = regexp.indexIn(f); + if (i >= 0) + f = regexp.cap(2); + return f.split(QLatin1Char(' '), QString::SkipEmptyParts); +} + +LibraryPathChooser::LibraryPathChooser(QWidget *parent) + : Utils::PathChooser(parent) +{ +} + +bool LibraryPathChooser::validatePath(const QString &path, QString *errorMessage) +{ + bool result = PathChooser::validatePath(path, errorMessage); + if (!result) + return false; + + QFileInfo fi(path); + if (!fi.exists()) + return false; + + const QString fileName = fi.fileName(); + + QStringList filters = qt_clean_filter_list(promptDialogFilter()); + for (int i = 0; i < filters.count(); i++) { + QRegExp regExp(filters.at(i)); + regExp.setPatternSyntax(QRegExp::Wildcard); + if (regExp.exactMatch(fileName)) + return true; + } + return false; +} + +AddLibraryWizard::AddLibraryWizard(const QString &fileName, QWidget *parent) : + Utils::Wizard(parent), m_proFile(fileName) +{ + setWindowTitle(tr("Add Library")); + setAutomaticProgressCreationEnabled(false); + m_libraryTypePage = new LibraryTypePage(this); + m_detailsPage = new DetailsPage(this); + m_summaryPage = new SummaryPage(this); + setPage(LibraryTypePageId, m_libraryTypePage); + setPage(DetailsPageId, m_detailsPage); + setPage(SummaryPageId, m_summaryPage); + + Utils::WizardProgress *progress = wizardProgress(); + Utils::WizardProgressItem *kindItem = progress->addItem(tr("Kind")); + Utils::WizardProgressItem *detailsItem = progress->addItem(tr("Details")); + Utils::WizardProgressItem *summaryItem = progress->addItem(tr("Summary")); + + kindItem->addPage(LibraryTypePageId); + detailsItem->addPage(DetailsPageId); + summaryItem->addPage(SummaryPageId); + + kindItem->setNextItems(QList<Utils::WizardProgressItem *>() << detailsItem); + detailsItem->setNextItems(QList<Utils::WizardProgressItem *>() << summaryItem); + + setStartId(LibraryTypePageId); +} + +AddLibraryWizard::~AddLibraryWizard() +{ +} + +QString AddLibraryWizard::proFile() const +{ + return m_proFile; +} + +AddLibraryWizard::LibraryKind AddLibraryWizard::libraryKind() const +{ + return m_libraryTypePage->libraryKind(); +} + +QString AddLibraryWizard::snippet() const +{ + return m_detailsPage->snippet(); +} + +///////////// + +LibraryTypePage::LibraryTypePage(AddLibraryWizard *parent) + : QWizardPage(parent) +{ + setTitle(tr("Kind of Library")); + setSubTitle(tr("Choose the kind of library which you want to link against")); + + QVBoxLayout *layout = new QVBoxLayout(this); + + m_systemRadio = new QRadioButton(tr("System Library"), this); + m_systemRadio->setChecked(true); + layout->addWidget(m_systemRadio); + QLabel *systemLabel = new QLabel(tr("Adds linkage against system " + "library.\nNeither the path to the " + "selected library nor the path to its " + "includes is added to the pro file.")); + systemLabel->setWordWrap(true); + systemLabel->setAttribute(Qt::WA_MacSmallSize, true); + layout->addWidget(systemLabel); + + m_externalRadio = new QRadioButton(tr("External Library"), this); + layout->addWidget(m_externalRadio); + QLabel *externalLabel = new QLabel(tr("Adds linkage against external " + "library which is not a part of your " + "build tree.\nIt also adds the library " + "and include paths to the pro file.")); + externalLabel->setWordWrap(true); + externalLabel->setAttribute(Qt::WA_MacSmallSize, true); + layout->addWidget(externalLabel); + + m_internalRadio = new QRadioButton(tr("Internal Library"), this); + layout->addWidget(m_internalRadio); + QLabel *internalLabel = new QLabel(tr("Adds linkage against internal " + "library which is a part of your build " + "tree.\nIt also adds the library and " + "include paths to the pro file.")); + internalLabel->setWordWrap(true); + internalLabel->setAttribute(Qt::WA_MacSmallSize, true); + layout->addWidget(internalLabel); +} + +AddLibraryWizard::LibraryKind LibraryTypePage::libraryKind() const +{ + if (m_internalRadio->isChecked()) + return AddLibraryWizard::InternalLibrary; + if (m_systemRadio->isChecked()) + return AddLibraryWizard::SystemLibrary; + return AddLibraryWizard::ExternalLibrary; +} + +int LibraryTypePage::nextId() const +{ + return AddLibraryWizard::DetailsPageId; +} + +///////////// + +DetailsPage::DetailsPage(AddLibraryWizard *parent) + : QWizardPage(parent), m_libraryWizard(parent), m_libraryDetailsController(0) +{ + m_libraryDetailsWidget = new Ui::LibraryDetailsWidget(); + m_libraryDetailsWidget->setupUi(this); +} + +bool DetailsPage::isComplete() const +{ + if (m_libraryDetailsController) + return m_libraryDetailsController->isComplete(); + return false; +} + +int DetailsPage::nextId() const +{ + return AddLibraryWizard::SummaryPageId; +} + +QString DetailsPage::snippet() const +{ + if (m_libraryDetailsController) + return m_libraryDetailsController->snippet(); + return QString(); +} + +void DetailsPage::initializePage() +{ + if (m_libraryDetailsController) { + delete m_libraryDetailsController; + m_libraryDetailsController = 0; + } + QString title; + QString subTitle; + switch (m_libraryWizard->libraryKind()) { + case AddLibraryWizard::SystemLibrary: + title = tr("System Library"); + subTitle = tr("Specify the library which you want to link against"); + m_libraryDetailsController = new SystemLibraryDetailsController(m_libraryDetailsWidget, this); + break; + case AddLibraryWizard::ExternalLibrary: + title = tr("External Library"); + subTitle = tr("Specify the library which you want to link against and the includes path"); + m_libraryDetailsController = new ExternalLibraryDetailsController(m_libraryDetailsWidget, this); + break; + case AddLibraryWizard::InternalLibrary: + title = tr("Internal Library"); + subTitle = tr("Choose the project file of the library which you want to link against"); + m_libraryDetailsController = new InternalLibraryDetailsController(m_libraryDetailsWidget, this); + break; + default: + break; + } + setTitle(title); + setSubTitle(subTitle); + if (m_libraryDetailsController) { + m_libraryDetailsController->setProFile(m_libraryWizard->proFile()); + connect(m_libraryDetailsController, SIGNAL(completeChanged()), + this, SIGNAL(completeChanged())); + } +} + +///////////// + +SummaryPage::SummaryPage(AddLibraryWizard *parent) + : QWizardPage(parent), m_libraryWizard(parent) +{ + setTitle(tr("Summary")); + setFinalPage(true); + + QVBoxLayout *layout = new QVBoxLayout(this); + m_summaryLabel = new QLabel(this); + m_snippetLabel = new QLabel(this); + layout->addWidget(m_summaryLabel); + layout->addWidget(m_snippetLabel); + m_summaryLabel->setTextFormat(Qt::RichText); + m_snippetLabel->setTextFormat(Qt::RichText); + m_snippetLabel->setTextInteractionFlags(Qt::TextBrowserInteraction); +} + +void SummaryPage::initializePage() +{ + m_snippet = m_libraryWizard->snippet(); + QFileInfo fi(m_libraryWizard->proFile()); + m_summaryLabel->setText( + tr("The following snippet will be added to the<br><b>%1</b> file:") + .arg(fi.fileName())); + QString richSnippet; + { + QTextStream str(&richSnippet); + str << "<code>"; + QString text = m_snippet; + text.replace(QLatin1Char('\n'), QLatin1String("<br>")); + str << text; + str << "</code>"; + } + + m_snippetLabel->setText(richSnippet); +} + +QString SummaryPage::snippet() const +{ + return m_snippet; +} diff --git a/src/plugins/qt4projectmanager/addlibrarywizard.h b/src/plugins/qt4projectmanager/addlibrarywizard.h new file mode 100644 index 0000000000..acddb49c77 --- /dev/null +++ b/src/plugins/qt4projectmanager/addlibrarywizard.h @@ -0,0 +1,137 @@ +#ifndef ADDLIBRARYWIZARD_H +#define ADDLIBRARYWIZARD_H + +#include <utils/wizard.h> +#include <utils/pathchooser.h> + +QT_BEGIN_NAMESPACE +class QRadioButton; +class QCheckBox; +class QLabel; +QT_END_NAMESPACE + +namespace Qt4ProjectManager { +namespace Internal { + +class LibraryDetailsWidget; +class LibraryDetailsController; +class LibraryTypePage; +class DetailsPage; +class SummaryPage; + +namespace Ui { + class LibraryDetailsWidget; +} + +class AddLibraryWizard : public Utils::Wizard +{ + Q_OBJECT +public: + enum PageId { + LibraryTypePageId, + DetailsPageId, + SummaryPageId + }; + + enum LibraryKind { + SystemLibrary, + ExternalLibrary, + InternalLibrary + }; + + enum LinkageType { + DynamicLinkage, + StaticLinkage, + NoLinkage + }; + + enum MacLibraryType { + FrameworkType, + LibraryType, + NoLibraryType + }; + + enum Platform { + LinuxPlatform = 0x01, + MacPlatform = 0x02, + WindowsPlatform = 0x04, + SymbianPlatform = 0x08 + }; + + Q_DECLARE_FLAGS(Platforms, Platform) + + explicit AddLibraryWizard(const QString &fileName, QWidget *parent = 0); + ~AddLibraryWizard(); + + LibraryKind libraryKind() const; + QString proFile() const; + QString snippet() const; + +signals: + +private: + LibraryTypePage *m_libraryTypePage; + DetailsPage *m_detailsPage; + SummaryPage *m_summaryPage; + QString m_proFile; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(AddLibraryWizard::Platforms) + +class LibraryTypePage : public QWizardPage +{ + Q_OBJECT +public: + LibraryTypePage(AddLibraryWizard *parent); + AddLibraryWizard::LibraryKind libraryKind() const; + virtual int nextId() const; + +private: + QRadioButton *m_systemRadio; + QRadioButton *m_externalRadio; + QRadioButton *m_internalRadio; +}; + +class DetailsPage : public QWizardPage +{ + Q_OBJECT +public: + DetailsPage(AddLibraryWizard *parent); + virtual void initializePage(); + virtual int nextId() const; + virtual bool isComplete() const; + QString snippet() const; + +private: + AddLibraryWizard *m_libraryWizard; + Ui::LibraryDetailsWidget *m_libraryDetailsWidget; + LibraryDetailsController *m_libraryDetailsController; +}; + +class SummaryPage : public QWizardPage +{ + Q_OBJECT +public: + SummaryPage(AddLibraryWizard *parent); + virtual void initializePage(); + QString snippet() const; +private: + AddLibraryWizard *m_libraryWizard; + QLabel *m_summaryLabel; + QLabel *m_snippetLabel; + QString m_snippet; +}; + +class LibraryPathChooser : public Utils::PathChooser +{ + Q_OBJECT +public: + LibraryPathChooser(QWidget *parent); + virtual bool validatePath(const QString &path, QString *errorMessage); +}; + + +} // namespace Internal +} // namespace Qt4ProjectManager + +#endif // ADDLIBRARYWIZARD_H diff --git a/src/plugins/qt4projectmanager/findqt4profiles.cpp b/src/plugins/qt4projectmanager/findqt4profiles.cpp new file mode 100644 index 0000000000..b841f8bcfb --- /dev/null +++ b/src/plugins/qt4projectmanager/findqt4profiles.cpp @@ -0,0 +1,18 @@ +#include "findqt4profiles.h" +#include "qt4nodes.h" + +using namespace Qt4ProjectManager; +using namespace Qt4ProjectManager::Internal; + +QList<Qt4ProFileNode *> FindQt4ProFiles::operator()(ProjectExplorer::ProjectNode *root) +{ + m_proFiles.clear(); + root->accept(this); + return m_proFiles; +} + +void FindQt4ProFiles::visitProjectNode(ProjectExplorer::ProjectNode *projectNode) +{ + if (Qt4ProFileNode *pro = qobject_cast<Qt4ProFileNode *>(projectNode)) + m_proFiles.append(pro); +} diff --git a/src/plugins/qt4projectmanager/findqt4profiles.h b/src/plugins/qt4projectmanager/findqt4profiles.h new file mode 100644 index 0000000000..dbe0082a9a --- /dev/null +++ b/src/plugins/qt4projectmanager/findqt4profiles.h @@ -0,0 +1,25 @@ +#ifndef FINDQT4PROFILES_H +#define FINDQT4PROFILES_H + +#include <projectexplorer/nodesvisitor.h> + +namespace Qt4ProjectManager { +namespace Internal { + +class Qt4ProFileNode; + +class FindQt4ProFiles: protected ProjectExplorer::NodesVisitor { + +public: + QList<Qt4ProFileNode *> operator()(ProjectExplorer::ProjectNode *root); +protected: + virtual void visitProjectNode(ProjectExplorer::ProjectNode *projectNode); +private: + QList<Qt4ProFileNode *> m_proFiles; +}; + +} // namespace Internal +} // namespace Qt4ProjectManager + +#endif // FINDQT4PROFILES_H + diff --git a/src/plugins/qt4projectmanager/librarydetailscontroller.cpp b/src/plugins/qt4projectmanager/librarydetailscontroller.cpp new file mode 100644 index 0000000000..04620c4274 --- /dev/null +++ b/src/plugins/qt4projectmanager/librarydetailscontroller.cpp @@ -0,0 +1,943 @@ +#include "librarydetailscontroller.h" +#include "ui_librarydetailswidget.h" +#include "findqt4profiles.h" +#include "qt4nodes.h" + +#include <projectexplorer/projectexplorer.h> +#include <projectexplorer/session.h> + +#include <QtCore/QFileInfo> +#include <QtCore/QDir> +#include <QtCore/QTextStream> + +using namespace Qt4ProjectManager; +using namespace Qt4ProjectManager::Internal; + +LibraryDetailsController::LibraryDetailsController( + Ui::LibraryDetailsWidget *libraryDetails, QObject *parent) : + QObject(parent), + m_platforms(AddLibraryWizard::LinuxPlatform + | AddLibraryWizard::MacPlatform + | AddLibraryWizard::WindowsPlatform + | AddLibraryWizard::SymbianPlatform), + m_linkageType(AddLibraryWizard::NoLinkage), + m_macLibraryType(AddLibraryWizard::NoLibraryType), + m_ignoreGuiSignals(false), + m_includePathChanged(false), + m_linkageRadiosVisible(true), + m_macLibraryRadiosVisible(true), + m_includePathVisible(true), + m_windowsGroupVisible(true), + m_libraryDetailsWidget(libraryDetails) +{ +#ifdef Q_OS_MAC + setMacLibraryRadiosVisible(false); +#endif + +#ifndef Q_OS_WIN + setLinkageRadiosVisible(false); +#endif + + connect(m_libraryDetailsWidget->includePathChooser, SIGNAL(changed(QString)), + this, SLOT(slotIncludePathChanged())); + connect(m_libraryDetailsWidget->frameworkRadio, SIGNAL(clicked(bool)), + this, SLOT(slotMacLibraryTypeChanged())); + connect(m_libraryDetailsWidget->libraryRadio, SIGNAL(clicked(bool)), + this, SLOT(slotMacLibraryTypeChanged())); + connect(m_libraryDetailsWidget->useSubfoldersCheckBox, SIGNAL(toggled(bool)), + this, SLOT(slotUseSubfoldersChanged(bool))); + connect(m_libraryDetailsWidget->addSuffixCheckBox, SIGNAL(toggled(bool)), + this, SLOT(slotAddSuffixChanged(bool))); + connect(m_libraryDetailsWidget->linCheckBox, SIGNAL(clicked(bool)), + this, SLOT(slotPlatformChanged())); + connect(m_libraryDetailsWidget->macCheckBox, SIGNAL(clicked(bool)), + this, SLOT(slotPlatformChanged())); + connect(m_libraryDetailsWidget->winCheckBox, SIGNAL(clicked(bool)), + this, SLOT(slotPlatformChanged())); + connect(m_libraryDetailsWidget->symCheckBox, SIGNAL(clicked(bool)), + this, SLOT(slotPlatformChanged())); +} + +void LibraryDetailsController::setProFile(const QString &proFile) +{ + m_proFile = proFile; + proFileChanged(); + updateGui(); + + emit completeChanged(); +} + +Ui::LibraryDetailsWidget *LibraryDetailsController::libraryDetailsWidget() const +{ + return m_libraryDetailsWidget; +} + +AddLibraryWizard::Platforms LibraryDetailsController::platforms() const +{ + return m_platforms; +} + +AddLibraryWizard::LinkageType LibraryDetailsController::linkageType() const +{ + return m_linkageType; +} + +AddLibraryWizard::MacLibraryType LibraryDetailsController::macLibraryType() const +{ + return m_macLibraryType; +} + +void LibraryDetailsController::updateGui() +{ + // read values from gui + m_platforms = 0; + if (libraryDetailsWidget()->linCheckBox->isChecked()) + m_platforms |= AddLibraryWizard::LinuxPlatform; + if (libraryDetailsWidget()->macCheckBox->isChecked()) + m_platforms |= AddLibraryWizard::MacPlatform; + if (libraryDetailsWidget()->winCheckBox->isChecked()) + m_platforms |= AddLibraryWizard::WindowsPlatform; + if (libraryDetailsWidget()->symCheckBox->isChecked()) + m_platforms |= AddLibraryWizard::SymbianPlatform; + + bool macLibraryTypeUpdated = false; + if (!m_linkageRadiosVisible) { + m_linkageType = suggestedLinkageType(); + if (m_linkageType == AddLibraryWizard::StaticLinkage) { + m_macLibraryType = AddLibraryWizard::LibraryType; + macLibraryTypeUpdated = true; + } + } else { + m_linkageType = AddLibraryWizard::DynamicLinkage; // the default + if (libraryDetailsWidget()->staticRadio->isChecked()) + m_linkageType = AddLibraryWizard::StaticLinkage; + } + + if (!macLibraryTypeUpdated) { + if (!m_macLibraryRadiosVisible) { + m_macLibraryType = suggestedMacLibraryType(); + } else { + m_macLibraryType = AddLibraryWizard::LibraryType; // the default + if (libraryDetailsWidget()->frameworkRadio->isChecked()) + m_macLibraryType = AddLibraryWizard::FrameworkType; + } + } + + // enable or disable some parts of gui + libraryDetailsWidget()->macGroupBox->setEnabled(platforms() + & AddLibraryWizard::MacPlatform); + updateWindowsOptionsEnablement(); + const bool macRadiosEnabled = m_linkageRadiosVisible || + linkageType() != AddLibraryWizard::StaticLinkage; + libraryDetailsWidget()->libraryRadio->setEnabled(macRadiosEnabled); + libraryDetailsWidget()->frameworkRadio->setEnabled(macRadiosEnabled); + + // update values in gui + setIgnoreGuiSignals(true); + + showLinkageType(linkageType()); + showMacLibraryType(macLibraryType()); + if (!m_includePathChanged) + libraryDetailsWidget()->includePathChooser->setPath(suggestedIncludePath()); + + setIgnoreGuiSignals(false); +} + +QString LibraryDetailsController::proFile() const +{ + return m_proFile; +} + +bool LibraryDetailsController::isIncludePathChanged() const +{ + return m_includePathChanged; +} + +void LibraryDetailsController::setIgnoreGuiSignals(bool ignore) +{ + m_ignoreGuiSignals = ignore; +} + +bool LibraryDetailsController::guiSignalsIgnored() const +{ + return m_ignoreGuiSignals; +} + +void LibraryDetailsController::showLinkageType( + AddLibraryWizard::LinkageType linkageType) +{ + const QString linkage(tr("Linkage:")); + QString linkageTitle; + switch (linkageType) { + case AddLibraryWizard::DynamicLinkage: + libraryDetailsWidget()->dynamicRadio->setChecked(true); + linkageTitle = tr("%1 Dynamic").arg(linkage); + break; + case AddLibraryWizard::StaticLinkage: + libraryDetailsWidget()->staticRadio->setChecked(true); + linkageTitle = tr("%1 Static").arg(linkage); + break; + default: + libraryDetailsWidget()->dynamicRadio->setChecked(false); + libraryDetailsWidget()->staticRadio->setChecked(false); + linkageTitle = linkage; + break; + } + libraryDetailsWidget()->linkageGroupBox->setTitle(linkageTitle); +} + +void LibraryDetailsController::showMacLibraryType( + AddLibraryWizard::MacLibraryType libType) +{ + const QString libraryType(tr("Mac:")); + QString libraryTypeTitle; + switch (libType) { + case AddLibraryWizard::FrameworkType: + libraryDetailsWidget()->frameworkRadio->setChecked(true); + libraryTypeTitle = tr("%1 Framework").arg(libraryType); + break; + case AddLibraryWizard::LibraryType: + libraryDetailsWidget()->libraryRadio->setChecked(true); + libraryTypeTitle = tr("%1 Library").arg(libraryType); + break; + default: + libraryDetailsWidget()->frameworkRadio->setChecked(false); + libraryDetailsWidget()->libraryRadio->setChecked(false); + libraryTypeTitle = libraryType; + break; + } + libraryDetailsWidget()->macGroupBox->setTitle(libraryTypeTitle); +} + +void LibraryDetailsController::setLinkageRadiosVisible(bool ena) +{ + m_linkageRadiosVisible = ena; + libraryDetailsWidget()->staticRadio->setVisible(ena); + libraryDetailsWidget()->dynamicRadio->setVisible(ena); +} + +void LibraryDetailsController::setMacLibraryRadiosVisible(bool ena) +{ + m_macLibraryRadiosVisible = ena; + libraryDetailsWidget()->frameworkRadio->setVisible(ena); + libraryDetailsWidget()->libraryRadio->setVisible(ena); +} + +void LibraryDetailsController::setLibraryPathChooserVisible(bool ena) +{ + libraryDetailsWidget()->libraryPathChooser->setVisible(ena); + libraryDetailsWidget()->libraryFileLabel->setVisible(ena); +} + +void LibraryDetailsController::setLibraryComboBoxVisible(bool ena) +{ + libraryDetailsWidget()->libraryComboBox->setVisible(ena); + libraryDetailsWidget()->libraryLabel->setVisible(ena); +} + +void LibraryDetailsController::setIncludePathVisible(bool ena) +{ + m_includePathVisible = ena; + libraryDetailsWidget()->includeLabel->setVisible(ena); + libraryDetailsWidget()->includePathChooser->setVisible(ena); +} + +void LibraryDetailsController::setWindowsGroupVisible(bool ena) +{ + m_windowsGroupVisible = ena; + libraryDetailsWidget()->winGroupBox->setVisible(ena); +} + +void LibraryDetailsController::setRemoveSuffixVisible(bool ena) +{ + libraryDetailsWidget()->removeSuffixCheckBox->setVisible(ena); +} + +bool LibraryDetailsController::isMacLibraryRadiosVisible() const +{ + return m_macLibraryRadiosVisible; +} + +bool LibraryDetailsController::isIncludePathVisible() const +{ + return m_includePathVisible; +} + +bool LibraryDetailsController::isWindowsGroupVisible() const +{ + return m_windowsGroupVisible; +} + +void LibraryDetailsController::slotIncludePathChanged() +{ + if (m_ignoreGuiSignals) + return; + m_includePathChanged = true; +} + +void LibraryDetailsController::slotPlatformChanged() +{ + updateGui(); + emit completeChanged(); +} + +void LibraryDetailsController::slotMacLibraryTypeChanged() +{ + if (guiSignalsIgnored()) + return; + + if (m_linkageRadiosVisible + && libraryDetailsWidget()->frameworkRadio->isChecked()) { + setIgnoreGuiSignals(true); + libraryDetailsWidget()->dynamicRadio->setChecked(true); + setIgnoreGuiSignals(true); + } + + updateGui(); +} + +void LibraryDetailsController::slotUseSubfoldersChanged(bool ena) +{ + if (ena) { + libraryDetailsWidget()->addSuffixCheckBox->setChecked(false); + libraryDetailsWidget()->removeSuffixCheckBox->setChecked(false); + } +} + +void LibraryDetailsController::slotAddSuffixChanged(bool ena) +{ + if (ena) { + libraryDetailsWidget()->useSubfoldersCheckBox->setChecked(false); + libraryDetailsWidget()->removeSuffixCheckBox->setChecked(false); + } +} + +static QString appendSpaceIfNotEmpty(const QString &aString) +{ + if (aString.isEmpty()) + return aString; + return aString + QLatin1Char(' '); +} + +static QString appendSeparator(const QString &aString) +{ + if (aString.isEmpty()) + return aString; + if (aString.at(aString.size() - 1) == QLatin1Char('/')) + return aString; + return aString + QLatin1Char('/'); +} + +static QString commonScopes(AddLibraryWizard::Platforms scopes, + AddLibraryWizard::Platforms excludedScopes) +{ + QString scopesString; + QTextStream str(&scopesString); + AddLibraryWizard::Platforms common = scopes | excludedScopes; + bool unixLikeScopes = false; + if (scopes & ~QFlags<AddLibraryWizard::Platform>(AddLibraryWizard::WindowsPlatform)) { + unixLikeScopes = true; + if (common & AddLibraryWizard::LinuxPlatform) { + str << "unix"; + if (!(common & AddLibraryWizard::MacPlatform)) + str << ":!macx"; + if (!(common & AddLibraryWizard::SymbianPlatform)) + str << ":!symbian"; + } else { + if (scopes & AddLibraryWizard::MacPlatform) + str << "macx"; + if (scopes & AddLibraryWizard::MacPlatform && + scopes & AddLibraryWizard::SymbianPlatform) + str << "|"; + if (scopes & AddLibraryWizard::SymbianPlatform) + str << "symbian"; + } + } + if (scopes & AddLibraryWizard::WindowsPlatform) { + if (unixLikeScopes) + str << "|"; + str << "win32"; + } + return scopesString; +} + +static QString generateLibsSnippet(AddLibraryWizard::Platforms platforms, + AddLibraryWizard::MacLibraryType macLibraryType, + const QString &libName, + const QString &targetRelativePath, const QString &pwd, + bool useSubfolders, bool addSuffix, bool generateLibPath) +{ + // if needed it contains: $$[pwd]/_PATH_ + const QString libraryPathSnippet = generateLibPath ? + QLatin1String("$$") + pwd + QLatin1Char('/') + + targetRelativePath : QString(); + + // if needed it contains: -L$$[pwd]/_PATH_ + const QString simpleLibraryPathSnippet = generateLibPath ? + QLatin1String("-L") + libraryPathSnippet : QString(); + // if needed it contains: -F$$[pwd]/_PATH_ + const QString macLibraryPathSnippet = generateLibPath ? + QLatin1String("-F") + libraryPathSnippet : QString(); + + AddLibraryWizard::Platforms commonPlatforms = platforms; + if (macLibraryType == AddLibraryWizard::FrameworkType) // we will generate a separate -F -framework line + commonPlatforms &= ~QFlags<AddLibraryWizard::Platform>(AddLibraryWizard::MacPlatform); + if (useSubfolders || addSuffix) // we will generate a separate debug/release conditions + commonPlatforms &= ~QFlags<AddLibraryWizard::Platform>(AddLibraryWizard::WindowsPlatform); + if (generateLibPath) // we will generate a separate line without -L + commonPlatforms &= ~QFlags<AddLibraryWizard::Platform>(AddLibraryWizard::SymbianPlatform); + + AddLibraryWizard::Platforms diffPlatforms = platforms ^ commonPlatforms; + AddLibraryWizard::Platforms generatedPlatforms = 0; + + QString snippetMessage; + QTextStream str(&snippetMessage); + + if (diffPlatforms & AddLibraryWizard::WindowsPlatform) { + str << "win32:CONFIG(release, debug|release): LIBS += "; + if (useSubfolders) + str << simpleLibraryPathSnippet << "release/ " << "-l" << libName << "\n"; + else if (addSuffix) + str << appendSpaceIfNotEmpty(simpleLibraryPathSnippet) << "-l" << libName << "\n"; + + str << "else:win32:CONFIG(debug, debug|release): LIBS += "; + if (useSubfolders) + str << simpleLibraryPathSnippet << "debug/ " << "-l" << libName << "\n"; + else if (addSuffix) + str << appendSpaceIfNotEmpty(simpleLibraryPathSnippet) << "-l" << libName << "d\n"; + generatedPlatforms |= AddLibraryWizard::WindowsPlatform; + } + if (diffPlatforms & AddLibraryWizard::MacPlatform) { + if (generatedPlatforms) + str << "else:"; + str << "mac: LIBS += " << appendSpaceIfNotEmpty(macLibraryPathSnippet) + << "-framework " << libName << "\n"; + generatedPlatforms |= AddLibraryWizard::MacPlatform; + } + if (diffPlatforms & AddLibraryWizard::SymbianPlatform) { + if (generatedPlatforms) + str << "else:"; + str << "symbian: LIBS += -l" << libName << "\n"; + generatedPlatforms |= AddLibraryWizard::SymbianPlatform; + } + + if (commonPlatforms) { + if (generatedPlatforms) + str << "else:"; + str << commonScopes(commonPlatforms, generatedPlatforms) << ": LIBS += " + << appendSpaceIfNotEmpty(simpleLibraryPathSnippet) << "-l" << libName << "\n"; + } + return snippetMessage; +} + +static QString generateIncludePathSnippet(const QString &includeRelativePath) +{ + return QLatin1String("\nINCLUDEPATH += $$PWD/") + + includeRelativePath + QLatin1Char('\n') + + QLatin1String("DEPENDPATH += $$PWD/") + + includeRelativePath + QLatin1Char('\n'); +} + +static QString generatePreTargetDepsSnippet(AddLibraryWizard::Platforms platforms, + AddLibraryWizard::LinkageType linkageType, + const QString &libName, + const QString &targetRelativePath, const QString &pwd, + bool useSubfolders, bool addSuffix) +{ + if (linkageType != AddLibraryWizard::StaticLinkage) + return QString(); + + // if needed it contains: PRE_TARGETDEPS += $$[pwd]/_PATH_TO_LIB_WITHOUT_LIB_NAME_ + const QString preTargetDepsSnippet = QLatin1String("PRE_TARGETDEPS += $$") + + pwd + QLatin1Char('/') + targetRelativePath; + + QString snippetMessage; + QTextStream str(&snippetMessage); + str << "\n"; + AddLibraryWizard::Platforms generatedPlatforms = 0; + if (platforms & AddLibraryWizard::WindowsPlatform) { + if (useSubfolders || addSuffix) { + str << "win32:CONFIG(release, debug|release): " + << preTargetDepsSnippet; + if (useSubfolders) + str << "release/" << libName << ".lib\n"; + else if (addSuffix) + str << libName << ".lib\n"; + + str << "else:win32:CONFIG(debug, debug|release): " + << preTargetDepsSnippet; + if (useSubfolders) + str << "debug/" << libName << ".lib\n"; + else if (addSuffix) + str << libName << "d.lib\n"; + } else { + str << "win32: " << preTargetDepsSnippet << libName << ".lib\n"; + } + generatedPlatforms |= AddLibraryWizard::WindowsPlatform; + } + AddLibraryWizard::Platforms commonPlatforms = platforms; + commonPlatforms &= ~QFlags<AddLibraryWizard::Platform>(AddLibraryWizard::WindowsPlatform); + // don't generate PRE_TARGETDEPS for symbian - relinking static lib apparently works without that + commonPlatforms &= ~QFlags<AddLibraryWizard::Platform>(AddLibraryWizard::SymbianPlatform); + if (commonPlatforms) { + if (generatedPlatforms) + str << "else:"; + str << commonScopes(commonPlatforms, generatedPlatforms) << ": " + << preTargetDepsSnippet << "lib" << libName << ".a\n"; + } + return snippetMessage; +} + +NonInternalLibraryDetailsController::NonInternalLibraryDetailsController( + Ui::LibraryDetailsWidget *libraryDetails, QObject *parent) : + LibraryDetailsController(libraryDetails, parent) +{ + setLibraryComboBoxVisible(false); + setLibraryPathChooserVisible(true); + +#ifdef Q_OS_WIN + libraryDetailsWidget()->libraryPathChooser->setPromptDialogFilter( + QLatin1String("Library file (*.lib)")); + setLinkageRadiosVisible(true); + setRemoveSuffixVisible(true); +#else + setLinkageRadiosVisible(false); + setRemoveSuffixVisible(false); +#endif + +#ifdef Q_OS_LINUX + libraryDetailsWidget()->libraryPathChooser->setPromptDialogFilter( + QLatin1String("Library file (lib*.so lib*.a)")); +#endif + +#ifdef Q_OS_MAC + libraryDetailsWidget()->libraryPathChooser->setPromptDialogFilter( + QLatin1String("Library file (*.dylib *.a *.framework)")); + // QLatin1String("Library file (lib*.dylib lib*.a *.framework)")); + libraryDetailsWidget()->libraryPathChooser->setExpectedKind(Utils::PathChooser::Any); +#else + libraryDetailsWidget()->libraryPathChooser->setExpectedKind(Utils::PathChooser::File); +#endif + + connect(libraryDetailsWidget()->libraryPathChooser, SIGNAL(validChanged()), + this, SIGNAL(completeChanged())); + connect(libraryDetailsWidget()->libraryPathChooser, SIGNAL(changed(QString)), + this, SLOT(slotLibraryPathChanged())); + connect(libraryDetailsWidget()->removeSuffixCheckBox, SIGNAL(toggled(bool)), + this, SLOT(slotRemoveSuffixChanged(bool))); + connect(libraryDetailsWidget()->dynamicRadio, SIGNAL(clicked(bool)), + this, SLOT(slotLinkageTypeChanged())); + connect(libraryDetailsWidget()->staticRadio, SIGNAL(clicked(bool)), + this, SLOT(slotLinkageTypeChanged())); +} + +AddLibraryWizard::LinkageType NonInternalLibraryDetailsController::suggestedLinkageType() const +{ + AddLibraryWizard::LinkageType type = AddLibraryWizard::NoLinkage; +#ifndef Q_OS_WIN + if (libraryDetailsWidget()->libraryPathChooser->isValid()) { + QFileInfo fi(libraryDetailsWidget()->libraryPathChooser->path()); + if (fi.suffix() == QLatin1String("a")) + type = AddLibraryWizard::StaticLinkage; + else + type = AddLibraryWizard::DynamicLinkage; + } +#endif + return type; +} + +AddLibraryWizard::MacLibraryType NonInternalLibraryDetailsController::suggestedMacLibraryType() const +{ + AddLibraryWizard::MacLibraryType type = AddLibraryWizard::NoLibraryType; +#ifdef Q_OS_MAC + if (libraryDetailsWidget()->libraryPathChooser->isValid()) { + QFileInfo fi(libraryDetailsWidget()->libraryPathChooser->path()); + if (fi.suffix() == QLatin1String("framework")) + type = AddLibraryWizard::FrameworkType; + else + type = AddLibraryWizard::LibraryType; + } +#endif + return type; +} + +QString NonInternalLibraryDetailsController::suggestedIncludePath() const +{ + QString includePath; + if (libraryDetailsWidget()->libraryPathChooser->isValid()) { + QFileInfo fi(libraryDetailsWidget()->libraryPathChooser->path()); + includePath = fi.absolutePath(); + QFileInfo dfi(includePath); + // TODO: Win: remove debug or release folder first if appropriate + if (dfi.fileName() == QLatin1String("lib")) { + QDir dir = dfi.absoluteDir(); + includePath = dir.absolutePath(); + QDir includeDir(dir.absoluteFilePath(QLatin1String("include"))); + if (includeDir.exists()) + includePath = includeDir.absolutePath(); + } + } + return includePath; +} + +void NonInternalLibraryDetailsController::updateWindowsOptionsEnablement() +{ + bool ena = platforms() & AddLibraryWizard::WindowsPlatform; +#ifdef Q_OS_WIN + libraryDetailsWidget()->addSuffixCheckBox->setEnabled(ena); + ena = true; +#endif + libraryDetailsWidget()->winGroupBox->setEnabled(ena); +} + +void NonInternalLibraryDetailsController::slotLinkageTypeChanged() +{ + if (guiSignalsIgnored()) + return; + + if (isMacLibraryRadiosVisible() + && libraryDetailsWidget()->staticRadio->isChecked()) { + setIgnoreGuiSignals(true); + libraryDetailsWidget()->libraryRadio->setChecked(true); + setIgnoreGuiSignals(true); + } + + updateGui(); +} + +void NonInternalLibraryDetailsController::slotRemoveSuffixChanged(bool ena) +{ + if (ena) { + libraryDetailsWidget()->useSubfoldersCheckBox->setChecked(false); + libraryDetailsWidget()->addSuffixCheckBox->setChecked(false); + } +} + +void NonInternalLibraryDetailsController::slotLibraryPathChanged() +{ +#ifdef Q_OS_WIN + bool subfoldersEnabled = true; + bool removeSuffixEnabled = true; + if (libraryDetailsWidget()->libraryPathChooser->isValid()) { + QFileInfo fi(libraryDetailsWidget()->libraryPathChooser->path()); + QFileInfo dfi(fi.absolutePath()); + const QString parentFolderName = dfi.fileName().toLower(); + if (parentFolderName != QLatin1String("debug") && + parentFolderName != QLatin1String("release")) + subfoldersEnabled = false; + const QString baseName = fi.baseName(); + + if (baseName.isEmpty() || baseName.at(baseName.size() - 1).toLower() != QLatin1Char('d')) + removeSuffixEnabled = false; + + if (subfoldersEnabled) + libraryDetailsWidget()->useSubfoldersCheckBox->setChecked(true); + else if (removeSuffixEnabled) + libraryDetailsWidget()->removeSuffixCheckBox->setChecked(true); + else + libraryDetailsWidget()->addSuffixCheckBox->setChecked(true); + } +#endif + + updateGui(); + + emit completeChanged(); +} + +bool NonInternalLibraryDetailsController::isComplete() const +{ + return libraryDetailsWidget()->libraryPathChooser->isValid() && + platforms(); +} + +QString NonInternalLibraryDetailsController::snippet() const +{ + QString libPath = libraryDetailsWidget()->libraryPathChooser->path(); + QFileInfo fi(libPath); + QString libName; + const bool removeSuffix = isWindowsGroupVisible() + && libraryDetailsWidget()->removeSuffixCheckBox->isChecked(); +#if defined (Q_OS_WIN) + libName = fi.baseName(); + if (removeSuffix && !libName.isEmpty()) // remove last letter which needs to be "d" + libName = libName.left(libName.size() - 1); +#elif defined (Q_OS_MAC) + if (macLibraryType() == AddLibraryWizard::FrameworkType) + libName = fi.baseName(); + else + libName = fi.baseName().mid(3); // cut the "lib" prefix +#else + libName = fi.baseName().mid(3); // cut the "lib" prefix +#endif + QString targetRelativePath; + QString includeRelativePath; + bool useSubfolders = false; + bool addSuffix = false; + if (isWindowsGroupVisible()) { + const bool useSubfoldersCondition = +#ifdef Q_OS_WIN + true; // we are on Win but we in case don't generate the code for Win we still need to remove "debug" or "release" subfolder +#else + platforms() & AddLibraryWizard::WindowsPlatform; +#endif + if (useSubfoldersCondition) + useSubfolders = libraryDetailsWidget()->useSubfoldersCheckBox->isChecked(); + if (platforms() & AddLibraryWizard::WindowsPlatform) + addSuffix = libraryDetailsWidget()->addSuffixCheckBox->isChecked() || removeSuffix; + } + if (isIncludePathVisible()) { // generate also the path to lib + QFileInfo pfi(proFile()); + QDir pdir = pfi.absoluteDir(); + QString absoluteLibraryPath = fi.absolutePath(); +#if defined (Q_OS_WIN) + if (useSubfolders) { // drop last subfolder which needs to be "debug" or "release" + QFileInfo libfi(absoluteLibraryPath); + absoluteLibraryPath = libfi.absolutePath(); + } +#endif // Q_OS_WIN + targetRelativePath = appendSeparator(pdir.relativeFilePath(absoluteLibraryPath)); + + const QString includePath = libraryDetailsWidget()->includePathChooser->path(); + if (!includePath.isEmpty()) + includeRelativePath = pdir.relativeFilePath(includePath); + } + + QString snippetMessage; + QTextStream str(&snippetMessage); + str << "\n"; + str << generateLibsSnippet(platforms(), macLibraryType(), libName, + targetRelativePath, QLatin1String("PWD"), + useSubfolders, addSuffix, isIncludePathVisible()); + if (isIncludePathVisible()) { + str << generateIncludePathSnippet(includeRelativePath); + str << generatePreTargetDepsSnippet(platforms(), linkageType(), libName, + targetRelativePath, QLatin1String("PWD"), + useSubfolders, addSuffix); + } + return snippetMessage; +} + +///////////// + +SystemLibraryDetailsController::SystemLibraryDetailsController( + Ui::LibraryDetailsWidget *libraryDetails, QObject *parent) + : NonInternalLibraryDetailsController(libraryDetails, parent) +{ + setIncludePathVisible(false); + setWindowsGroupVisible(false); +} + +///////////// + +ExternalLibraryDetailsController::ExternalLibraryDetailsController( + Ui::LibraryDetailsWidget *libraryDetails, QObject *parent) + : NonInternalLibraryDetailsController(libraryDetails, parent) +{ + setIncludePathVisible(true); + setWindowsGroupVisible(true); +} + +void ExternalLibraryDetailsController::updateWindowsOptionsEnablement() +{ + NonInternalLibraryDetailsController::updateWindowsOptionsEnablement(); +#ifdef Q_OS_WIN + bool subfoldersEnabled = true; + bool removeSuffixEnabled = true; + if (libraryDetailsWidget()->libraryPathChooser->isValid()) { + QFileInfo fi(libraryDetailsWidget()->libraryPathChooser->path()); + QFileInfo dfi(fi.absolutePath()); + const QString parentFolderName = dfi.fileName().toLower(); + if (parentFolderName != QLatin1String("debug") && + parentFolderName != QLatin1String("release")) + subfoldersEnabled = false; + const QString baseName = fi.baseName(); + + if (baseName.isEmpty() || baseName.at(baseName.size() - 1).toLower() != QLatin1Char('d')) + removeSuffixEnabled = false; + + } + libraryDetailsWidget()->useSubfoldersCheckBox->setEnabled(subfoldersEnabled); + libraryDetailsWidget()->removeSuffixCheckBox->setEnabled(removeSuffixEnabled); +#endif +} + +///////////// + +InternalLibraryDetailsController::InternalLibraryDetailsController( + Ui::LibraryDetailsWidget *libraryDetails, QObject *parent) + : LibraryDetailsController(libraryDetails, parent), + m_proFileNode(0) +{ + setLinkageRadiosVisible(false); + setLibraryPathChooserVisible(false); + setLibraryComboBoxVisible(true); + setIncludePathVisible(true); + setWindowsGroupVisible(true); + setRemoveSuffixVisible(false); + +#ifdef Q_OS_WIN + libraryDetailsWidget()->useSubfoldersCheckBox->setEnabled(true); +#endif + + connect(libraryDetailsWidget()->libraryComboBox, SIGNAL(currentIndexChanged(int)), + this, SLOT(slotCurrentLibraryChanged())); +} + +AddLibraryWizard::LinkageType InternalLibraryDetailsController::suggestedLinkageType() const +{ + const int currentIndex = libraryDetailsWidget()->libraryComboBox->currentIndex(); + AddLibraryWizard::LinkageType type = AddLibraryWizard::NoLinkage; + if (currentIndex >= 0) { + Qt4ProFileNode *proFileNode = m_proFileNodes.at(currentIndex); + const QStringList configVar = proFileNode->variableValue(ConfigVar); + if (configVar.contains(QLatin1String("staticlib")) + || configVar.contains(QLatin1String("static"))) + type = AddLibraryWizard::StaticLinkage; + else + type = AddLibraryWizard::DynamicLinkage; + } + return type; +} + +AddLibraryWizard::MacLibraryType InternalLibraryDetailsController::suggestedMacLibraryType() const +{ + const int currentIndex = libraryDetailsWidget()->libraryComboBox->currentIndex(); + AddLibraryWizard::MacLibraryType type = AddLibraryWizard::NoLibraryType; + if (currentIndex >= 0) { + Qt4ProFileNode *proFileNode = m_proFileNodes.at(currentIndex); + const QStringList configVar = proFileNode->variableValue(ConfigVar); + if (configVar.contains(QLatin1String("lib_bundle"))) + type = AddLibraryWizard::FrameworkType; + else + type = AddLibraryWizard::LibraryType; + } + return type; +} + +QString InternalLibraryDetailsController::suggestedIncludePath() const +{ + const int currentIndex = libraryDetailsWidget()->libraryComboBox->currentIndex(); + QString includePath; + if (currentIndex >= 0) { + Qt4ProFileNode *proFileNode = m_proFileNodes.at(currentIndex); + QFileInfo fi(proFileNode->path()); + includePath = fi.absolutePath(); + } + return includePath; +} + +void InternalLibraryDetailsController::updateWindowsOptionsEnablement() +{ +#ifdef Q_OS_WIN + libraryDetailsWidget()->addSuffixCheckBox->setEnabled(true); +#endif + libraryDetailsWidget()->winGroupBox->setEnabled(platforms() + & AddLibraryWizard::WindowsPlatform); +} + +void InternalLibraryDetailsController::proFileChanged() +{ + m_proFileNodes.clear(); + libraryDetailsWidget()->libraryComboBox->clear(); + m_proFileNode = 0; + + const ProjectExplorer::Project *project = + ProjectExplorer::ProjectExplorerPlugin::instance()->session()->projectForFile(proFile()); + if (!project) + return; + + setIgnoreGuiSignals(true); + + ProjectExplorer::ProjectNode *rootProject = project->rootProjectNode(); + QFileInfo fi(rootProject->path()); + QDir rootDir(fi.absolutePath()); + FindQt4ProFiles findQt4ProFiles; + QList<Qt4ProFileNode *> proFiles = findQt4ProFiles(rootProject); + foreach (Qt4ProFileNode *proFileNode, proFiles) { + const QString proFilePath = proFileNode->path(); + if (proFilePath == proFile()) { + m_proFileNode = proFileNode; + } else if (proFileNode->projectType() == LibraryTemplate) { + const QStringList configVar = proFileNode->variableValue(ConfigVar); + if (!configVar.contains(QLatin1String("plugin"))) { + const QString relProFilePath = rootDir.relativeFilePath(proFilePath); + TargetInformation targetInfo = proFileNode->targetInformation(); + const QString itemToolTip = tr("%1 (%2)").arg(targetInfo.target).arg(relProFilePath); + m_proFileNodes.append(proFileNode); + libraryDetailsWidget()->libraryComboBox->addItem(targetInfo.target); + libraryDetailsWidget()->libraryComboBox->setItemData( + libraryDetailsWidget()->libraryComboBox->count() - 1, + itemToolTip, Qt::ToolTipRole); + } + } + } + + setIgnoreGuiSignals(false); +} + +void InternalLibraryDetailsController::slotCurrentLibraryChanged() +{ + const int currentIndex = libraryDetailsWidget()->libraryComboBox->currentIndex(); + if (currentIndex >= 0) { + libraryDetailsWidget()->libraryComboBox->setToolTip( + libraryDetailsWidget()->libraryComboBox->itemData( + currentIndex, Qt::ToolTipRole).toString()); + Qt4ProFileNode *proFileNode = m_proFileNodes.at(currentIndex); + const QStringList configVar = proFileNode->variableValue(ConfigVar); +#ifdef Q_OS_WIN + bool useSubfolders = false; + if (configVar.contains(QLatin1String("debug_and_release")) + && configVar.contains(QLatin1String("debug_and_release_target"))) + useSubfolders = true; + libraryDetailsWidget()->useSubfoldersCheckBox->setChecked(useSubfolders); + libraryDetailsWidget()->addSuffixCheckBox->setChecked(!useSubfolders); +#endif // Q_OS_WIN + } + + if (guiSignalsIgnored()) + return; + + updateGui(); + + emit completeChanged(); +} + +bool InternalLibraryDetailsController::isComplete() const +{ + return libraryDetailsWidget()->libraryComboBox->count() && platforms(); +} + +QString InternalLibraryDetailsController::snippet() const +{ + const int currentIndex = libraryDetailsWidget()->libraryComboBox->currentIndex(); + if (currentIndex < 0) + return QString(); + + if (!m_proFileNode) + return QString(); + + Qt4ProFileNode *proFileNode = m_proFileNodes.at(currentIndex); + + QFileInfo fi(proFile()); + QDir projectBuildDir(m_proFileNode->buildDir()); + QDir projectSrcDir(fi.absolutePath()); + TargetInformation targetInfo = proFileNode->targetInformation(); + + const QString targetRelativePath = appendSeparator(projectBuildDir.relativeFilePath(targetInfo.buildDir)); + const QString includeRelativePath = projectSrcDir.relativeFilePath(libraryDetailsWidget()->includePathChooser->path()); + + const bool useSubfolders = libraryDetailsWidget()->useSubfoldersCheckBox->isChecked(); + const bool addSuffix = libraryDetailsWidget()->addSuffixCheckBox->isChecked(); + + QString snippetMessage; + QTextStream str(&snippetMessage); + str << "\n"; + str << generateLibsSnippet(platforms(), macLibraryType(), targetInfo.target, + targetRelativePath, QLatin1String("OUT_PWD"), + useSubfolders, addSuffix, true); + str << generateIncludePathSnippet(includeRelativePath); + str << generatePreTargetDepsSnippet(platforms(), linkageType(), targetInfo.target, + targetRelativePath, QLatin1String("OUT_PWD"), + useSubfolders, addSuffix); + return snippetMessage; +} diff --git a/src/plugins/qt4projectmanager/librarydetailscontroller.h b/src/plugins/qt4projectmanager/librarydetailscontroller.h new file mode 100644 index 0000000000..1f75f557a4 --- /dev/null +++ b/src/plugins/qt4projectmanager/librarydetailscontroller.h @@ -0,0 +1,149 @@ +#ifndef LIBRARYDETAILSCONTROLLER_H +#define LIBRARYDETAILSCONTROLLER_H + +#include <QtGui/QWidget> +#include "addlibrarywizard.h" + +namespace Qt4ProjectManager { +namespace Internal { + +namespace Ui { + class LibraryDetailsWidget; +} + +class Qt4ProFileNode; + +class LibraryDetailsController : public QObject +{ + Q_OBJECT +public: + explicit LibraryDetailsController(Ui::LibraryDetailsWidget *libraryDetails, + QObject *parent = 0); + virtual bool isComplete() const = 0; + void setProFile(const QString &proFile); + virtual QString snippet() const = 0; +signals: + void completeChanged(); +protected: + + Ui::LibraryDetailsWidget *libraryDetailsWidget() const; + + AddLibraryWizard::Platforms platforms() const; + AddLibraryWizard::LinkageType linkageType() const; + AddLibraryWizard::MacLibraryType macLibraryType() const; + QString proFile() const; + bool isIncludePathChanged() const; + bool guiSignalsIgnored() const; + + virtual void proFileChanged() {} + + void updateGui(); + virtual AddLibraryWizard::LinkageType suggestedLinkageType() const = 0; + virtual AddLibraryWizard::MacLibraryType suggestedMacLibraryType() const = 0; + virtual QString suggestedIncludePath() const = 0; + virtual void updateWindowsOptionsEnablement() = 0; + + void setIgnoreGuiSignals(bool ignore); + + void setLinkageRadiosVisible(bool ena); + void setMacLibraryRadiosVisible(bool ena); + void setLibraryPathChooserVisible(bool ena); + void setLibraryComboBoxVisible(bool ena); + void setIncludePathVisible(bool ena); + void setWindowsGroupVisible(bool ena); + void setRemoveSuffixVisible(bool ena); + + bool isMacLibraryRadiosVisible() const; + bool isIncludePathVisible() const; + bool isWindowsGroupVisible() const; + +private slots: + void slotIncludePathChanged(); + void slotPlatformChanged(); + void slotMacLibraryTypeChanged(); + void slotUseSubfoldersChanged(bool ena); + void slotAddSuffixChanged(bool ena); +private: + + void showLinkageType(AddLibraryWizard::LinkageType linkageType); + void showMacLibraryType(AddLibraryWizard::MacLibraryType libType); + + AddLibraryWizard::Platforms m_platforms; + AddLibraryWizard::LinkageType m_linkageType; + AddLibraryWizard::MacLibraryType m_macLibraryType; + + QString m_proFile; + + bool m_ignoreGuiSignals; + bool m_includePathChanged; + + bool m_linkageRadiosVisible; + bool m_macLibraryRadiosVisible; + bool m_includePathVisible; + bool m_windowsGroupVisible; + + Ui::LibraryDetailsWidget *m_libraryDetailsWidget; +}; + +class NonInternalLibraryDetailsController : public LibraryDetailsController +{ + Q_OBJECT +public: + explicit NonInternalLibraryDetailsController(Ui::LibraryDetailsWidget *libraryDetails, + QObject *parent = 0); + virtual bool isComplete() const; + virtual QString snippet() const; +protected: + virtual AddLibraryWizard::LinkageType suggestedLinkageType() const; + virtual AddLibraryWizard::MacLibraryType suggestedMacLibraryType() const; + virtual QString suggestedIncludePath() const; + virtual void updateWindowsOptionsEnablement(); +private slots: + void slotLinkageTypeChanged(); + void slotRemoveSuffixChanged(bool ena); + void slotLibraryPathChanged(); +}; + +class SystemLibraryDetailsController : public NonInternalLibraryDetailsController +{ + Q_OBJECT +public: + explicit SystemLibraryDetailsController(Ui::LibraryDetailsWidget *libraryDetails, + QObject *parent = 0); +}; + +class ExternalLibraryDetailsController : public NonInternalLibraryDetailsController +{ + Q_OBJECT +public: + explicit ExternalLibraryDetailsController(Ui::LibraryDetailsWidget *libraryDetails, + QObject *parent = 0); +protected: + virtual void updateWindowsOptionsEnablement(); +}; + +class InternalLibraryDetailsController : public LibraryDetailsController +{ + Q_OBJECT +public: + explicit InternalLibraryDetailsController(Ui::LibraryDetailsWidget *libraryDetails, + QObject *parent = 0); + virtual bool isComplete() const; + virtual QString snippet() const; +protected: + virtual AddLibraryWizard::LinkageType suggestedLinkageType() const; + virtual AddLibraryWizard::MacLibraryType suggestedMacLibraryType() const; + virtual QString suggestedIncludePath() const; + virtual void updateWindowsOptionsEnablement(); + virtual void proFileChanged(); +private slots: + void slotCurrentLibraryChanged(); +private: + Qt4ProFileNode *m_proFileNode; + QVector<Qt4ProFileNode *> m_proFileNodes; +}; + +} // namespace Internal +} // namespace Qt4ProjectManager + +#endif // LIBRARYDETAILSCONTROLLER_H diff --git a/src/plugins/qt4projectmanager/librarydetailswidget.ui b/src/plugins/qt4projectmanager/librarydetailswidget.ui new file mode 100644 index 0000000000..cf8da064a4 --- /dev/null +++ b/src/plugins/qt4projectmanager/librarydetailswidget.ui @@ -0,0 +1,256 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>Qt4ProjectManager::Internal::LibraryDetailsWidget</class> + <widget class="QWidget" name="Qt4ProjectManager::Internal::LibraryDetailsWidget"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>368</width> + <height>306</height> + </rect> + </property> + <property name="windowTitle"> + <string>Form</string> + </property> + <layout class="QGridLayout" name="gridLayout_2"> + <item row="0" column="0" colspan="2"> + <layout class="QGridLayout" name="gridLayout"> + <item row="0" column="0"> + <widget class="QLabel" name="libraryLabel"> + <property name="text"> + <string>Library:</string> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="QComboBox" name="libraryComboBox"/> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="libraryFileLabel"> + <property name="text"> + <string>Library file:</string> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="Qt4ProjectManager::Internal::LibraryPathChooser" name="libraryPathChooser" native="true"/> + </item> + <item row="2" column="0"> + <widget class="QLabel" name="includeLabel"> + <property name="text"> + <string>Include path:</string> + </property> + </widget> + </item> + <item row="2" column="1"> + <widget class="Utils::PathChooser" name="includePathChooser" native="true"/> + </item> + </layout> + </item> + <item row="1" column="0"> + <layout class="QVBoxLayout" name="verticalLayout_3"> + <item> + <widget class="QGroupBox" name="platformGroupBox"> + <property name="title"> + <string>Platform</string> + </property> + <property name="flat"> + <bool>true</bool> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <widget class="QCheckBox" name="linCheckBox"> + <property name="text"> + <string>Linux</string> + </property> + <property name="checked"> + <bool>true</bool> + </property> + </widget> + </item> + <item> + <widget class="QCheckBox" name="macCheckBox"> + <property name="text"> + <string>Mac</string> + </property> + <property name="checked"> + <bool>true</bool> + </property> + </widget> + </item> + <item> + <widget class="QCheckBox" name="winCheckBox"> + <property name="text"> + <string>Windows</string> + </property> + <property name="checked"> + <bool>true</bool> + </property> + </widget> + </item> + <item> + <widget class="QCheckBox" name="symCheckBox"> + <property name="text"> + <string>Symbian</string> + </property> + <property name="checked"> + <bool>true</bool> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <spacer name="verticalSpacer_2"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>0</height> + </size> + </property> + </spacer> + </item> + </layout> + </item> + <item row="1" column="1"> + <layout class="QVBoxLayout" name="verticalLayout_4"> + <item> + <widget class="QGroupBox" name="linkageGroupBox"> + <property name="title"> + <string>Linkage:</string> + </property> + <property name="flat"> + <bool>true</bool> + </property> + <layout class="QHBoxLayout" name="horizontalLayout_2"> + <item> + <widget class="QRadioButton" name="dynamicRadio"> + <property name="text"> + <string>Dynamic</string> + </property> + </widget> + </item> + <item> + <widget class="QRadioButton" name="staticRadio"> + <property name="text"> + <string>Static</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QGroupBox" name="macGroupBox"> + <property name="title"> + <string>Mac:</string> + </property> + <property name="flat"> + <bool>true</bool> + </property> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <widget class="QRadioButton" name="libraryRadio"> + <property name="text"> + <string>Library</string> + </property> + </widget> + </item> + <item> + <widget class="QRadioButton" name="frameworkRadio"> + <property name="text"> + <string>Framework</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QGroupBox" name="winGroupBox"> + <property name="title"> + <string>Windows:</string> + </property> + <property name="flat"> + <bool>true</bool> + </property> + <layout class="QVBoxLayout" name="verticalLayout_2"> + <item> + <widget class="QCheckBox" name="useSubfoldersCheckBox"> + <property name="text"> + <string>Library inside "debug" or "release" subfolder</string> + </property> + <property name="checked"> + <bool>true</bool> + </property> + </widget> + </item> + <item> + <widget class="QCheckBox" name="addSuffixCheckBox"> + <property name="text"> + <string>Add "d" suffix for debug version</string> + </property> + </widget> + </item> + <item> + <widget class="QCheckBox" name="removeSuffixCheckBox"> + <property name="text"> + <string>Remove "d" suffix for release version</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <spacer name="verticalSpacer"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>17</width> + <height>0</height> + </size> + </property> + </spacer> + </item> + </layout> + </item> + <item row="2" column="0" colspan="2"> + <spacer name="verticalSpacer_3"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>55</height> + </size> + </property> + </spacer> + </item> + </layout> + </widget> + <customwidgets> + <customwidget> + <class>Utils::PathChooser</class> + <extends>QWidget</extends> + <header location="global">utils/pathchooser.h</header> + <container>1</container> + </customwidget> + <customwidget> + <class>Qt4ProjectManager::Internal::LibraryPathChooser</class> + <extends>QWidget</extends> + <header>addlibrarywizard.h</header> + <container>1</container> + </customwidget> + </customwidgets> + <resources/> + <connections/> +</ui> diff --git a/src/plugins/qt4projectmanager/profileeditor.cpp b/src/plugins/qt4projectmanager/profileeditor.cpp index 4fbbd23d91..8b79faf183 100644 --- a/src/plugins/qt4projectmanager/profileeditor.cpp +++ b/src/plugins/qt4projectmanager/profileeditor.cpp @@ -33,6 +33,7 @@ #include "qt4projectmanager.h" #include "qt4projectmanagerconstants.h" #include "profileeditorfactory.h" +#include "addlibrarywizard.h" #include <coreplugin/icore.h> #include <coreplugin/actionmanager/actionmanager.h> @@ -147,6 +148,26 @@ void ProFileEditor::setFontSettings(const TextEditor::FontSettings &fs) highlighter->rehighlight(); } +void ProFileEditor::addLibrary() +{ + AddLibraryWizard wizard(file()->fileName(), this); + if (wizard.exec() != QDialog::Accepted) + return; + + TextEditor::BaseTextEditorEditable *editable = editableInterface(); + const int endOfDoc = editable->position(TextEditor::ITextEditor::EndOfDoc); + editable->setCurPos(endOfDoc); + QString snippet = wizard.snippet(); + + // add extra \n in case the last line is not empty + int line, column; + editable->convertPosition(endOfDoc, &line, &column); + if (!editable->textAt(endOfDoc - column, column).simplified().isEmpty()) + snippet = QLatin1Char('\n') + snippet; + + editable->insert(snippet); +} + // // ProFileDocument // diff --git a/src/plugins/qt4projectmanager/profileeditor.h b/src/plugins/qt4projectmanager/profileeditor.h index 4cf88c228a..025cd5edab 100644 --- a/src/plugins/qt4projectmanager/profileeditor.h +++ b/src/plugins/qt4projectmanager/profileeditor.h @@ -81,13 +81,13 @@ public: TextEditor::TextEditorActionHandler *actionHandler() const { return m_ah; } void unCommentSelection(); - protected: TextEditor::BaseTextEditorEditable *createEditableInterface(); void contextMenuEvent(QContextMenuEvent *); public slots: virtual void setFontSettings(const TextEditor::FontSettings &); + void addLibrary(); private: ProFileEditorFactory *m_factory; diff --git a/src/plugins/qt4projectmanager/qt4nodes.cpp b/src/plugins/qt4projectmanager/qt4nodes.cpp index b29bda9641..630179d89c 100644 --- a/src/plugins/qt4projectmanager/qt4nodes.cpp +++ b/src/plugins/qt4projectmanager/qt4nodes.cpp @@ -1508,6 +1508,7 @@ void Qt4ProFileNode::applyEvaluate(bool parseResult, bool async) QStringList() << m_projectDir, 0); newVarValues[LibDirectoriesVar] = libDirectories(m_readerExact); + newVarValues[ConfigVar] = m_readerExact->values(QLatin1String("CONFIG")); if (m_varValues != newVarValues) { m_varValues = newVarValues; diff --git a/src/plugins/qt4projectmanager/qt4nodes.h b/src/plugins/qt4projectmanager/qt4nodes.h index b72d89a8a5..b0d3c9dfcc 100644 --- a/src/plugins/qt4projectmanager/qt4nodes.h +++ b/src/plugins/qt4projectmanager/qt4nodes.h @@ -93,7 +93,8 @@ enum Qt4Variable { MocDirVar, PkgConfigVar, PrecompiledHeaderVar, - LibDirectoriesVar + LibDirectoriesVar, + ConfigVar }; class Qt4PriFileNode; diff --git a/src/plugins/qt4projectmanager/qt4project.cpp b/src/plugins/qt4projectmanager/qt4project.cpp index 314357f360..f2eef77923 100644 --- a/src/plugins/qt4projectmanager/qt4project.cpp +++ b/src/plugins/qt4projectmanager/qt4project.cpp @@ -39,6 +39,7 @@ #include "qt4projectmanagerconstants.h" #include "projectloadwizard.h" #include "qt4buildconfiguration.h" +#include "findqt4profiles.h" #include <coreplugin/icore.h> #include <coreplugin/messagemanager.h> @@ -48,8 +49,6 @@ #include <cpptools/cppmodelmanagerinterface.h> #include <projectexplorer/buildenvironmentwidget.h> #include <projectexplorer/customexecutablerunconfiguration.h> -#include <projectexplorer/nodesvisitor.h> -#include <projectexplorer/project.h> #include <utils/qtcassert.h> #include <QtCore/QDebug> @@ -354,27 +353,6 @@ Qt4Target *Qt4Project::activeTarget() const return static_cast<Qt4Target *>(Project::activeTarget()); } -namespace { - class FindQt4ProFiles: protected ProjectExplorer::NodesVisitor { - QList<Qt4ProFileNode *> m_proFiles; - - public: - QList<Qt4ProFileNode *> operator()(ProjectNode *root) - { - m_proFiles.clear(); - root->accept(this); - return m_proFiles; - } - - protected: - virtual void visitProjectNode(ProjectNode *projectNode) - { - if (Qt4ProFileNode *pro = qobject_cast<Qt4ProFileNode *>(projectNode)) - m_proFiles.append(pro); - } - }; -} - void Qt4Project::onAddedTarget(ProjectExplorer::Target *t) { Q_ASSERT(t); diff --git a/src/plugins/qt4projectmanager/qt4projectmanager.pro b/src/plugins/qt4projectmanager/qt4projectmanager.pro index 82de87a107..7db432dee0 100644 --- a/src/plugins/qt4projectmanager/qt4projectmanager.pro +++ b/src/plugins/qt4projectmanager/qt4projectmanager.pro @@ -47,7 +47,10 @@ HEADERS += qt4deployconfiguration.h \ qt4buildconfiguration.h \ qt4target.h \ qmakeparser.h \ - qtoutputformatter.h + qtoutputformatter.h \ + addlibrarywizard.h \ + librarydetailscontroller.h \ + findqt4profiles.h SOURCES += qt4deployconfiguration.cpp \ qtparser.cpp \ qt4projectmanagerplugin.cpp \ @@ -91,7 +94,10 @@ SOURCES += qt4deployconfiguration.cpp \ qt4buildconfiguration.cpp \ qt4target.cpp \ qmakeparser.cpp \ - qtoutputformatter.cpp + qtoutputformatter.cpp \ + addlibrarywizard.cpp \ + librarydetailscontroller.cpp \ + findqt4profiles.cpp FORMS += makestep.ui \ qmakestep.ui \ qt4projectconfigwidget.ui \ @@ -99,7 +105,8 @@ FORMS += makestep.ui \ showbuildlog.ui \ gettingstartedwelcomepagewidget.ui \ wizards/testwizardpage.ui \ - wizards/targetsetuppage.ui + wizards/targetsetuppage.ui \ + librarydetailswidget.ui RESOURCES += qt4projectmanager.qrc \ wizards/wizards.qrc DEFINES += PROPARSER_THREAD_SAFE PROEVALUATOR_THREAD_SAFE diff --git a/src/plugins/qt4projectmanager/qt4projectmanagerconstants.h b/src/plugins/qt4projectmanager/qt4projectmanagerconstants.h index eec59bc6ad..d7f9b5b7b6 100644 --- a/src/plugins/qt4projectmanager/qt4projectmanagerconstants.h +++ b/src/plugins/qt4projectmanager/qt4projectmanagerconstants.h @@ -64,6 +64,7 @@ const char * const RUNQMAKECONTEXTMENU = "Qt4Builder.RunQMakeContextMenu"; const char * const BUILDSUBDIR = "Qt4Builder.BuildSubDir"; const char * const REBUILDSUBDIR = "Qt4Builder.RebuildSubDir"; const char * const CLEANSUBDIR = "Qt4Builder.CleanSubDir"; +const char * const ADDLIBRARY = "Qt4.AddLibrary"; //configurations const char * const CONFIG_DEBUG = "debug"; diff --git a/src/plugins/qt4projectmanager/qt4projectmanagerplugin.cpp b/src/plugins/qt4projectmanager/qt4projectmanagerplugin.cpp index 8fc4b18e64..eb0f387db0 100644 --- a/src/plugins/qt4projectmanager/qt4projectmanagerplugin.cpp +++ b/src/plugins/qt4projectmanager/qt4projectmanagerplugin.cpp @@ -43,6 +43,7 @@ #include "qt4projectmanagerconstants.h" #include "qt4project.h" #include "qt4runconfiguration.h" +#include "profileeditor.h" #include "profilereader.h" #include "qtversionmanager.h" #include "qtoptionspage.h" @@ -65,6 +66,7 @@ #include <coreplugin/actionmanager/actionmanager.h> #include <coreplugin/actionmanager/actioncontainer.h> #include <coreplugin/actionmanager/command.h> +#include <coreplugin/editormanager/editormanager.h> #include <texteditor/texteditoractionhandler.h> #include <texteditor/texteditorconstants.h> @@ -230,6 +232,16 @@ bool Qt4ProjectManagerPlugin::initialize(const QStringList &arguments, QString * cmd = am->command(TextEditor::Constants::UN_COMMENT_SELECTION); contextMenu->addAction(cmd); + Core::Context proFileEditorContext = Core::Context(Qt4ProjectManager::Constants::PROJECT_ID); + + QAction *addLibrary = new QAction(tr("Add Library..."), this); + cmd = am->registerAction(addLibrary, + Constants::ADDLIBRARY, proFileEditorContext); + //cmd->setDefaultKeySequence(QKeySequence(Qt::Key_F2)); + connect(addLibrary, SIGNAL(triggered()), + this, SLOT(addLibrary())); + contextMenu->addAction(cmd); + return true; } @@ -283,6 +295,14 @@ void Qt4ProjectManagerPlugin::buildStateChanged(ProjectExplorer::Project *pro) m_runQMakeActionContextMenu->setEnabled(!m_projectExplorer->buildManager()->isBuilding(pro)); } +void Qt4ProjectManagerPlugin::addLibrary() +{ + Core::EditorManager *em = Core::EditorManager::instance(); + ProFileEditor *editor = qobject_cast<ProFileEditor*>(em->currentEditor()->widget()); + if (editor) + editor->addLibrary(); +} + #ifdef WITH_TESTS void Qt4ProjectManagerPlugin::testBasicProjectLoading() { diff --git a/src/plugins/qt4projectmanager/qt4projectmanagerplugin.h b/src/plugins/qt4projectmanager/qt4projectmanagerplugin.h index b5c41ecc71..3fcfc8eb40 100644 --- a/src/plugins/qt4projectmanager/qt4projectmanagerplugin.h +++ b/src/plugins/qt4projectmanager/qt4projectmanagerplugin.h @@ -74,6 +74,7 @@ private slots: ProjectExplorer::Node *node); void currentProjectChanged(); void buildStateChanged(ProjectExplorer::Project *pro); + void addLibrary(); #ifdef WITH_TESTS void testBasicProjectLoading(); // Test fails! |