/**************************************************************************** ** ** Copyright (C) 2016 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of Qt Creator. ** ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ****************************************************************************/ #include "toolchainoptionspage.h" #include "toolchain.h" #include "abi.h" #include "projectexplorerconstants.h" #include "toolchainconfigwidget.h" #include "toolchainmanager.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace Utils; namespace ProjectExplorer { namespace Internal { class ToolChainTreeItem : public TreeItem { public: ToolChainTreeItem(ToolChain *tc, bool c) : toolChain(tc), changed(c) { widget = tc->configurationWidget(); if (widget) { if (tc->isAutoDetected()) widget->makeReadOnly(); QObject::connect(widget, &ToolChainConfigWidget::dirty, [this] { changed = true; update(); }); } } QVariant data(int column, int role) const { switch (role) { case Qt::DisplayRole: if (column == 0) return toolChain->displayName(); return toolChain->typeDisplayName(); case Qt::FontRole: { QFont font; font.setBold(changed); return font; } case Qt::ToolTipRole: return ToolChainOptionsPage::tr("ABI: %1").arg( changed ? ToolChainOptionsPage::tr("not up-to-date") : toolChain->targetAbi().toString()); } return QVariant(); } ToolChain *toolChain; ToolChainConfigWidget *widget; bool changed; }; // -------------------------------------------------------------------------- // ToolChainOptionsWidget // -------------------------------------------------------------------------- class ToolChainOptionsWidget : public QWidget { public: ToolChainOptionsWidget() { m_factories = ExtensionSystem::PluginManager::getObjects( [](ToolChainFactory *factory) { return factory->canCreate();}); m_model.setHeader({ToolChainOptionsPage::tr("Name"), ToolChainOptionsPage::tr("Type")}); auto autoRoot = new StaticTreeItem(ToolChainOptionsPage::tr("Auto-detected")); auto manualRoot = new StaticTreeItem(ToolChainOptionsPage::tr("Manual")); foreach (const Core::Id &l, ToolChainManager::allLanguages()) { const QString dn = ToolChainManager::displayNameOfLanguageId(l); auto autoNode = new StaticTreeItem(dn); auto manualNode = new StaticTreeItem(dn); autoRoot->appendChild(autoNode); manualRoot->appendChild(manualNode); m_languageMap.insert(l, qMakePair(autoNode, manualNode)); } m_model.rootItem()->appendChild(autoRoot); m_model.rootItem()->appendChild(manualRoot); foreach (ToolChain *tc, ToolChainManager::toolChains()) insertToolChain(tc); m_toolChainView = new QTreeView(this); m_toolChainView->setUniformRowHeights(true); m_toolChainView->setSelectionMode(QAbstractItemView::SingleSelection); m_toolChainView->setSelectionBehavior(QAbstractItemView::SelectRows); m_toolChainView->setModel(&m_model); m_toolChainView->header()->setStretchLastSection(false); m_toolChainView->header()->setSectionResizeMode(0, QHeaderView::ResizeToContents); m_toolChainView->header()->setSectionResizeMode(1, QHeaderView::Stretch); m_toolChainView->expandAll(); m_addButton = new QPushButton(ToolChainOptionsPage::tr("Add"), this); auto addMenu = new QMenu; foreach (ToolChainFactory *factory, m_factories) { QList languages = factory->supportedLanguages().toList(); if (languages.isEmpty()) continue; if (languages.count() == 1) { addMenu->addAction(createAction(factory->displayName(), factory, languages.at(0))); } else { Utils::sort(languages, [](const Core::Id &l1, const Core::Id &l2) { return ToolChainManager::displayNameOfLanguageId(l1) < ToolChainManager::displayNameOfLanguageId(l2); }); auto subMenu = addMenu->addMenu(factory->displayName()); foreach (const Core::Id &l, languages) subMenu->addAction(createAction(ToolChainManager::displayNameOfLanguageId(l), factory, l)); } } m_addButton->setMenu(addMenu); m_cloneButton = new QPushButton(ToolChainOptionsPage::tr("Clone"), this); connect(m_cloneButton, &QAbstractButton::clicked, [this] { cloneToolChain(); }); m_delButton = new QPushButton(ToolChainOptionsPage::tr("Remove"), this); m_container = new DetailsWidget(this); m_container->setState(DetailsWidget::NoSummary); m_container->setVisible(false); auto buttonLayout = new QVBoxLayout; buttonLayout->setSpacing(6); buttonLayout->setContentsMargins(0, 0, 0, 0); buttonLayout->addWidget(m_addButton); buttonLayout->addWidget(m_cloneButton); buttonLayout->addWidget(m_delButton); buttonLayout->addItem(new QSpacerItem(10, 40, QSizePolicy::Minimum, QSizePolicy::Expanding)); auto verticalLayout = new QVBoxLayout; verticalLayout->addWidget(m_toolChainView); verticalLayout->addWidget(m_container); auto horizontalLayout = new QHBoxLayout(this); horizontalLayout->addLayout(verticalLayout); horizontalLayout->addLayout(buttonLayout); connect(ToolChainManager::instance(), &ToolChainManager::toolChainAdded, this, &ToolChainOptionsWidget::addToolChain); connect(ToolChainManager::instance(), &ToolChainManager::toolChainRemoved, this, &ToolChainOptionsWidget::removeToolChain); connect(m_toolChainView->selectionModel(), &QItemSelectionModel::currentChanged, this, &ToolChainOptionsWidget::toolChainSelectionChanged); connect(ToolChainManager::instance(), &ToolChainManager::toolChainsChanged, this, &ToolChainOptionsWidget::toolChainSelectionChanged); connect(m_delButton, &QAbstractButton::clicked, [this] { if (ToolChainTreeItem *item = currentTreeItem()) markForRemoval(item); }); updateState(); } void toolChainSelectionChanged(); void updateState(); void createToolChain(ToolChainFactory *factory, const Core::Id &language); void cloneToolChain(); ToolChainTreeItem *currentTreeItem(); void markForRemoval(ToolChainTreeItem *item); ToolChainTreeItem *insertToolChain(ProjectExplorer::ToolChain *tc, bool changed = false); // Insert directly into model void addToolChain(ProjectExplorer::ToolChain *); void removeToolChain(ProjectExplorer::ToolChain *); StaticTreeItem *parentForToolChain(ToolChain *tc); QAction *createAction(const QString &name, ToolChainFactory *factory, Core::Id language) { auto action = new QAction(name, nullptr); connect(action, &QAction::triggered, [this, factory, language] { createToolChain(factory, language); }); return action; } void apply(); public: TreeModel m_model; QList m_factories; QTreeView *m_toolChainView; DetailsWidget *m_container; QPushButton *m_addButton; QPushButton *m_cloneButton; QPushButton *m_delButton; QHash> m_languageMap; QList m_toAddList; QList m_toRemoveList; }; void ToolChainOptionsWidget::markForRemoval(ToolChainTreeItem *item) { m_model.takeItem(item); if (m_toAddList.contains(item)) { delete item->toolChain; item->toolChain = nullptr; m_toAddList.removeOne(item); delete item; } else { m_toRemoveList.append(item); } } ToolChainTreeItem *ToolChainOptionsWidget::insertToolChain(ToolChain *tc, bool changed) { StaticTreeItem *parent = parentForToolChain(tc); auto item = new ToolChainTreeItem(tc, changed); parent->appendChild(item); return item; } void ToolChainOptionsWidget::addToolChain(ToolChain *tc) { foreach (ToolChainTreeItem *n, m_toAddList) { if (n->toolChain == tc) { // do not delete n: Still used elsewhere! m_toAddList.removeOne(n); return; } } insertToolChain(tc); updateState(); } void ToolChainOptionsWidget::removeToolChain(ToolChain *tc) { foreach (ToolChainTreeItem *n, m_toRemoveList) { if (n->toolChain == tc) { m_toRemoveList.removeOne(n); delete n; return; } } StaticTreeItem *parent = parentForToolChain(tc); auto item = parent->findChildAtLevel(1, [tc](TreeItem *item) { return static_cast(item)->toolChain == tc; }); m_model.destroyItem(item); updateState(); } StaticTreeItem *ToolChainOptionsWidget::parentForToolChain(ToolChain *tc) { QPair nodes = m_languageMap.value(tc->language()); return tc->isAutoDetected() ? nodes.first : nodes.second; } void ToolChainOptionsWidget::toolChainSelectionChanged() { ToolChainTreeItem *item = currentTreeItem(); QWidget *oldWidget = m_container->takeWidget(); // Prevent deletion. if (oldWidget) oldWidget->setVisible(false); QWidget *currentTcWidget = item ? item->widget : nullptr; m_container->setWidget(currentTcWidget); m_container->setVisible(currentTcWidget); updateState(); } void ToolChainOptionsWidget::apply() { // Remove unused tool chains: QList nodes = m_toRemoveList; foreach (ToolChainTreeItem *n, nodes) ToolChainManager::deregisterToolChain(n->toolChain); Q_ASSERT(m_toRemoveList.isEmpty()); // Update tool chains: foreach (const Core::Id &l, m_languageMap.keys()) { StaticTreeItem *parent = m_languageMap.value(l).second; for (TreeItem *item : *parent) { auto tcItem = static_cast(item); Q_ASSERT(tcItem->toolChain); if (tcItem->widget) tcItem->widget->apply(); tcItem->changed = false; tcItem->update(); } } // Add new (and already updated) tool chains QStringList removedTcs; nodes = m_toAddList; foreach (ToolChainTreeItem *n, nodes) { if (!ToolChainManager::registerToolChain(n->toolChain)) removedTcs << n->toolChain->displayName(); } // foreach (ToolChainTreeItem *n, m_toAddList) markForRemoval(n); qDeleteAll(m_toAddList); if (removedTcs.count() == 1) { QMessageBox::warning(Core::ICore::dialogParent(), ToolChainOptionsPage::tr("Duplicate Compilers Detected"), ToolChainOptionsPage::tr("The following compiler was already configured:
" " %1
" "It was not configured again.") .arg(removedTcs.at(0))); } else if (!removedTcs.isEmpty()) { QMessageBox::warning(Core::ICore::dialogParent(), ToolChainOptionsPage::tr("Duplicate Compilers Detected"), ToolChainOptionsPage::tr("The following compilers were already configured:
" " %1
" "They were not configured again.") .arg(removedTcs.join(QLatin1String(",
 ")))); } } void ToolChainOptionsWidget::createToolChain(ToolChainFactory *factory, const Core::Id &language) { QTC_ASSERT(factory, return); QTC_ASSERT(factory->canCreate(), return); QTC_ASSERT(language.isValid(), return); ToolChain *tc = factory->create(language); if (!tc) return; auto item = insertToolChain(tc, true); m_toAddList.append(item); m_toolChainView->setCurrentIndex(m_model.indexForItem(item)); } void ToolChainOptionsWidget::cloneToolChain() { ToolChainTreeItem *current = currentTreeItem(); if (!current) return; ToolChain *tc = current->toolChain->clone(); if (!tc) return; auto item = insertToolChain(tc, true); m_toAddList.append(item); m_toolChainView->setCurrentIndex(m_model.indexForItem(item)); } void ToolChainOptionsWidget::updateState() { bool canCopy = false; bool canDelete = false; if (ToolChainTreeItem *item = currentTreeItem()) { ToolChain *tc = item->toolChain; canCopy = tc->isValid() && tc->canClone(); canDelete = tc->detection() != ToolChain::AutoDetection; } m_cloneButton->setEnabled(canCopy); m_delButton->setEnabled(canDelete); } ToolChainTreeItem *ToolChainOptionsWidget::currentTreeItem() { QModelIndex index = m_toolChainView->currentIndex(); TreeItem *item = m_model.itemForIndex(index); return (item && item->level() == 3) ? static_cast(item) : nullptr; } // -------------------------------------------------------------------------- // ToolChainOptionsPage // -------------------------------------------------------------------------- ToolChainOptionsPage::ToolChainOptionsPage() { setId(Constants::TOOLCHAIN_SETTINGS_PAGE_ID); setDisplayName(tr("Compilers")); setCategory(Constants::PROJECTEXPLORER_SETTINGS_CATEGORY); setDisplayCategory(QCoreApplication::translate("ProjectExplorer", Constants::PROJECTEXPLORER_SETTINGS_TR_CATEGORY)); setCategoryIcon(Utils::Icon(Constants::PROJECTEXPLORER_SETTINGS_CATEGORY_ICON)); } QWidget *ToolChainOptionsPage::widget() { if (!m_widget) m_widget = new ToolChainOptionsWidget; return m_widget; } void ToolChainOptionsPage::apply() { if (m_widget) m_widget->apply(); } void ToolChainOptionsPage::finish() { delete m_widget; m_widget = nullptr; } } // namespace Internal } // namespace ProjectExplorer