// Copyright (C) 2016 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "addlibrarywizard.h" #include "librarydetailscontroller.h" #include "qmakeprojectmanagertr.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace Utils; namespace QmakeProjectManager::Internal { const char qt_file_dialog_filter_reg_exp[] = "^(.*)\\(([a-zA-Z0-9_.*? +;#\\-\\[\\]@\\{\\}/!<>\\$%&=^~:\\|]*)\\)$"; static QStringList qt_clean_filter_list(const QString &filter) { static const QRegularExpression regexp(qt_file_dialog_filter_reg_exp); const QRegularExpressionMatch match = regexp.match(filter); QString f = filter; if (match.hasMatch()) f = match.captured(2); return f.split(QLatin1Char(' '), Qt::SkipEmptyParts); } static FancyLineEdit::AsyncValidationResult validateLibraryPath(const QString &input, const QString &promptDialogFilter) { const FilePath filePath = FilePath::fromUserInput(input); if (!filePath.exists()) return make_unexpected(::QmakeProjectManager::Tr::tr("File does not exist.")); const QString fileName = filePath.fileName(); QRegularExpression::PatternOption option = HostOsInfo::fileNameCaseSensitivity() == Qt::CaseInsensitive ? QRegularExpression::CaseInsensitiveOption : QRegularExpression::NoPatternOption; const QStringList filters = qt_clean_filter_list(promptDialogFilter); for (const QString &filter : filters) { QString pattern = QRegularExpression::wildcardToRegularExpression(filter); QRegularExpression regExp(pattern, option); if (regExp.match(fileName).hasMatch()) return input; } return make_unexpected(::QmakeProjectManager::Tr::tr("File does not match filter.")); } AddLibraryWizard::AddLibraryWizard(const FilePath &proFile, QWidget *parent) : Wizard(parent) , m_proFile(proFile) { setWindowTitle(Tr::tr("Add Library")); m_libraryTypePage = new LibraryTypePage(this); addPage(m_libraryTypePage); m_detailsPage = new DetailsPage(this); addPage(m_detailsPage); m_summaryPage = new SummaryPage(this); addPage(m_summaryPage); } AddLibraryWizard::~AddLibraryWizard() = default; FilePath 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::tr("Library Type")); setSubTitle(Tr::tr("Choose the type of the library to link to")); auto *layout = new QVBoxLayout(this); m_internalRadio = new QRadioButton(Tr::tr("Internal library"), this); layout->addWidget(m_internalRadio); QLabel *internalLabel = new QLabel(Tr::tr("Links to a library " "that is located in your build " "tree.\nAdds the library and " "include paths to the .pro file.")); internalLabel->setWordWrap(true); internalLabel->setAttribute(Qt::WA_MacSmallSize, true); layout->addWidget(internalLabel); m_externalRadio = new QRadioButton(Tr::tr("External library"), this); layout->addWidget(m_externalRadio); QLabel *externalLabel = new QLabel(Tr::tr("Links to a library " "that is not located in your " "build tree.\nAdds the library " "and include paths to the .pro file.")); externalLabel->setWordWrap(true); externalLabel->setAttribute(Qt::WA_MacSmallSize, true); layout->addWidget(externalLabel); m_systemRadio = new QRadioButton(Tr::tr("System library"), this); layout->addWidget(m_systemRadio); QLabel *systemLabel = new QLabel(Tr::tr("Links to a system library." "\nNeither the path to the " "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_packageRadio = new QRadioButton(Tr::tr("System package"), this); layout->addWidget(m_packageRadio); QLabel *packageLabel = new QLabel(Tr::tr("Links to a system library using pkg-config.")); packageLabel->setWordWrap(true); packageLabel->setAttribute(Qt::WA_MacSmallSize, true); layout->addWidget(packageLabel); if (HostOsInfo::isWindowsHost()) { m_packageRadio->setVisible(false); packageLabel->setVisible(false); } // select the default m_internalRadio->setChecked(true); setProperty(SHORT_TITLE_PROPERTY, Tr::tr("Type")); } AddLibraryWizard::LibraryKind LibraryTypePage::libraryKind() const { if (m_internalRadio->isChecked()) return AddLibraryWizard::InternalLibrary; if (m_externalRadio->isChecked()) return AddLibraryWizard::ExternalLibrary; if (m_systemRadio->isChecked()) return AddLibraryWizard::SystemLibrary; return AddLibraryWizard::PackageLibrary; } ///////////// DetailsPage::DetailsPage(AddLibraryWizard *parent) : QWizardPage(parent), m_libraryWizard(parent) { m_libraryDetailsWidget = new LibraryDetailsWidget(this); PathChooser * const libPathChooser = m_libraryDetailsWidget->libraryPathChooser; libPathChooser->setHistoryCompleter("Qmake.LibDir.History"); const auto pathValidator = [libPathChooser](const QString &text) -> FancyLineEdit::AsyncValidationFuture { return libPathChooser->defaultValidationFunction()(text).then( [pDialogFilter = libPathChooser->promptDialogFilter()]( const FancyLineEdit::AsyncValidationResult &result) { if (!result) return result; return validateLibraryPath(result.value(), pDialogFilter); }); }; libPathChooser->setValidationFunction(pathValidator); setProperty(SHORT_TITLE_PROPERTY, Tr::tr("Details")); } bool DetailsPage::isComplete() const { if (m_libraryDetailsController) return m_libraryDetailsController->isComplete(); return false; } QString DetailsPage::snippet() const { if (m_libraryDetailsController) return m_libraryDetailsController->snippet(); return QString(); } void DetailsPage::initializePage() { if (m_libraryDetailsController) { delete m_libraryDetailsController; m_libraryDetailsController = nullptr; } QString title; QString subTitle; switch (m_libraryWizard->libraryKind()) { case AddLibraryWizard::InternalLibrary: title = Tr::tr("Internal Library"); subTitle = Tr::tr("Choose the project file of the library to link to"); m_libraryDetailsController = new InternalLibraryDetailsController( m_libraryDetailsWidget, m_libraryWizard->proFile(), this); break; case AddLibraryWizard::ExternalLibrary: title = Tr::tr("External Library"); subTitle = Tr::tr("Specify the library to link to and the includes path"); m_libraryDetailsController = new ExternalLibraryDetailsController( m_libraryDetailsWidget, m_libraryWizard->proFile(), this); break; case AddLibraryWizard::SystemLibrary: title = Tr::tr("System Library"); subTitle = Tr::tr("Specify the library to link to"); m_libraryDetailsController = new SystemLibraryDetailsController( m_libraryDetailsWidget, m_libraryWizard->proFile(), this); break; case AddLibraryWizard::PackageLibrary: title = Tr::tr("System Package"); subTitle = Tr::tr("Specify the package to link to"); m_libraryDetailsController = new PackageLibraryDetailsController( m_libraryDetailsWidget, m_libraryWizard->proFile(), this); break; default: break; } setTitle(title); setSubTitle(subTitle); if (m_libraryDetailsController) { connect(m_libraryDetailsController, &LibraryDetailsController::completeChanged, this, &QWizardPage::completeChanged); } } ///////////// SummaryPage::SummaryPage(AddLibraryWizard *parent) : QWizardPage(parent), m_libraryWizard(parent) { setTitle(Tr::tr("Summary")); setFinalPage(true); auto *layout = new QVBoxLayout(this); const auto scrollArea = new QScrollArea; const auto snippetWidget = new QWidget; const auto snippetLayout = new QVBoxLayout(snippetWidget); m_summaryLabel = new QLabel(this); m_snippetLabel = new QLabel(this); m_snippetLabel->setWordWrap(true); layout->addWidget(m_summaryLabel); snippetLayout->addWidget(m_snippetLabel); snippetLayout->addStretch(1); scrollArea->setWidget(snippetWidget); scrollArea->setWidgetResizable(true); layout->addWidget(scrollArea); m_summaryLabel->setTextFormat(Qt::RichText); m_snippetLabel->setTextFormat(Qt::RichText); m_snippetLabel->setTextInteractionFlags(Qt::TextBrowserInteraction); setProperty(SHORT_TITLE_PROPERTY, Tr::tr("Summary")); } void SummaryPage::initializePage() { m_snippet = m_libraryWizard->snippet(); m_summaryLabel->setText( Tr::tr("The following snippet will be added to the
%1 file:") .arg(m_libraryWizard->proFile().fileName())); QString richSnippet; { QTextStream str(&richSnippet); str << ""; QString text = m_snippet; text.replace(QLatin1Char('\n'), QLatin1String("
")); text.replace(QLatin1Char(' '), QLatin1String(" ")); str << text; str << "
"; } m_snippetLabel->setText(richSnippet); } QString SummaryPage::snippet() const { return m_snippet; } LibraryDetailsWidget::LibraryDetailsWidget(QWidget *parent) { includePathChooser = new PathChooser(parent); packageLineEdit = new QLineEdit(parent); libraryPathChooser = new PathChooser(parent); libraryComboBox = new QComboBox(parent); libraryTypeComboBox = new QComboBox(parent); platformGroupBox = new QGroupBox(Tr::tr("Platform:")); platformGroupBox->setFlat(true); linkageGroupBox = new QGroupBox(Tr::tr("Linkage:")); linkageGroupBox->setFlat(true); macGroupBox = new QGroupBox(Tr::tr("Mac:")); macGroupBox->setFlat(true); winGroupBox = new QGroupBox(Tr::tr("Windows:")); winGroupBox->setFlat(true); linCheckBox = new QCheckBox(Tr::tr("Linux")); linCheckBox->setChecked(true); macCheckBox = new QCheckBox(Tr::tr("Mac")); macCheckBox->setChecked(true); winCheckBox = new QCheckBox(Tr::tr("Windows")); winCheckBox->setChecked(true); dynamicRadio = new QRadioButton(Tr::tr("Dynamic"), linkageGroupBox); staticRadio = new QRadioButton(Tr::tr("Static"), linkageGroupBox); libraryRadio = new QRadioButton(Tr::tr("Library"), macGroupBox); frameworkRadio = new QRadioButton(Tr::tr("Framework"), macGroupBox); useSubfoldersCheckBox = new QCheckBox(Tr::tr("Library inside \"debug\" or \"release\" subfolder"), winGroupBox); useSubfoldersCheckBox->setChecked(true); addSuffixCheckBox = new QCheckBox(Tr::tr("Add \"d\" suffix for debug version"), winGroupBox); removeSuffixCheckBox = new QCheckBox(Tr::tr("Remove \"d\" suffix for release version"), winGroupBox); using namespace Layouting; Column { linCheckBox, macCheckBox, winCheckBox, st }.attachTo(platformGroupBox); Row { dynamicRadio, staticRadio }.attachTo(linkageGroupBox); Row { libraryRadio, frameworkRadio }.attachTo(macGroupBox); Column { useSubfoldersCheckBox, addSuffixCheckBox, removeSuffixCheckBox }.attachTo(winGroupBox); libraryLabel = new QLabel(Tr::tr("Library:")); libraryFileLabel = new QLabel(Tr::tr("Library file:")); libraryTypeLabel = new QLabel(Tr::tr("Library type:")); packageLabel = new QLabel(Tr::tr("Package:")); includeLabel = new QLabel(Tr::tr("Include path:")); Column { Form { libraryLabel, libraryComboBox, br, libraryTypeLabel, libraryTypeComboBox, br, libraryFileLabel, libraryPathChooser, br, packageLabel, packageLineEdit, br, includeLabel, includePathChooser }, Row { platformGroupBox, Column { linkageGroupBox, macGroupBox, winGroupBox, } }, st }.attachTo(parent); } } // QmakeProjectManager::Internal