Loading files
This file is pinned to the selected release. Download the original to open it in your design tool.
8a92ff80bThis file is pinned to the selected release. Download the original to open it in your design tool.
/*
* LibrePCB - Professional EDA for everyone!
* Copyright (C) 2013 LibrePCB Developers, see AUTHORS.md for contributors.
* https://librepcb.org/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*******************************************************************************
* Includes
******************************************************************************/
#include "componentvarianteditor.h"
#include "../../graphics/graphicsscene.h"
#include "../../graphics/slintgraphicsview.h"
#include "../../undocommand.h"
#include "../../undocommandgroup.h"
#include "../../undostack.h"
#include "../../utils/slinthelpers.h"
#include "../cmd/cmdcomponentpinsignalmapitemedit.h"
#include "../cmd/cmdcomponentsymbolvariantedit.h"
#include "../libraryelementcache.h"
#include "componentgatelistmodel.h"
#include "componentsignalnamelistmodel.h"
#include <librepcb/core/library/cmp/component.h>
#include <librepcb/core/library/cmp/componentsignal.h>
#include <librepcb/core/library/cmp/componentsymbolvariant.h>
#include <librepcb/core/library/sym/symbol.h>
#include <librepcb/core/workspace/colorrole.h>
#include <librepcb/core/workspace/workspace.h>
#include <librepcb/core/workspace/workspacesettings.h>
#include <QtCore>
#include <QtWidgets>
/*******************************************************************************
* Namespace
******************************************************************************/
namespace librepcb {
namespace editor {
/*******************************************************************************
* Constructors / Destructor
******************************************************************************/
ComponentVariantEditor::ComponentVariantEditor(
const Workspace& ws, const GraphicsLayerList& layers,
const LibraryElementCache& cache, QPointer<Component> component,
const std::shared_ptr<ComponentSignalNameListModel>& sigs,
std::shared_ptr<ComponentSymbolVariant> variant, UndoStack* stack,
const bool* wizardMode, QObject* parent) noexcept
: QObject(parent),
mWorkspace(ws),
mLayers(layers),
mCache(cache),
mComponent(component),
mVariant(variant),
mUndoStack(stack),
mWizardMode(wizardMode),
mScene(new GraphicsScene()),
mFrameIndex(0),
mGates(new ComponentGateListModel(ws, layers, cache)),
mHasUnassignedSignals(false) {
// Setup preview scene.
const ColorScheme& scheme =
mWorkspace.getSettings().schematicColorSchemes.getActive();
const auto background = scheme.getColors(ColorRole::schematicBackground());
mScene->setBackgroundColors(background.primary, background.secondary);
const auto overlay = scheme.getColors(ColorRole::schematicOverlays());
mScene->setOverlayColors(overlay.primary, overlay.secondary);
const auto selection = scheme.getColors(ColorRole::schematicSelection());
mScene->setSelectionRectColors(selection.primary, selection.secondary);
mScene->setGridStyle(GridStyle::Lines); // Required for positioning.
mScene->setOriginCrossVisible(true); // Required for positioning.
connect(mScene.get(), &GraphicsScene::changed, this, [this]() {
++mFrameIndex;
emit uiDataChanged();
});
// Connect models.
mGates->setReferences(&mVariant->getSymbolItems(), component, mScene.get(),
sigs, mUndoStack, mWizardMode);
connect(sigs.get(), &ComponentSignalNameListModel::modified, this,
&ComponentVariantEditor::updateUnassignedSignals);
updateUnassignedSignals();
}
ComponentVariantEditor::~ComponentVariantEditor() noexcept {
mGates->setReferences(nullptr, nullptr, nullptr, nullptr, nullptr, nullptr);
}
/*******************************************************************************
* General Methods
******************************************************************************/
ui::ComponentVariantData ComponentVariantEditor::getUiData() const {
return ui::ComponentVariantData{
q2s(mVariant->getUuid().toStr().left(8)), // ID
q2s(*mVariant->getNames().getDefaultValue()), // Name
q2s(mVariant->getDescriptions().getDefaultValue()), // Description
q2s(mVariant->getNorm()), // Norm
mGates, // Gates
mHasUnassignedSignals, // Has unassigned signals
ui::ComponentVariantAction::None, // Action
mFrameIndex, // Frame index
};
}
void ComponentVariantEditor::setUiData(
const ui::ComponentVariantData& data) noexcept {
try {
const QString name = s2q(data.name);
const QString description = s2q(data.description);
const QString norm = s2q(data.norm);
std::unique_ptr<CmdComponentSymbolVariantEdit> cmd(
new CmdComponentSymbolVariantEdit(*mVariant));
if (name != mVariant->getNames().getDefaultValue()) {
if (auto parsed = parseElementName(cleanElementName(name))) {
if (mComponent && mComponent->getSymbolVariants().contains(**parsed)) {
throw RuntimeError(
__FILE__, __LINE__,
tr("There is already a variant with the name \"%1\".").arg(name));
}
auto names = mVariant->getNames();
names.setDefaultValue(*parsed);
cmd->setNames(names);
}
}
if (description != mVariant->getDescriptions().getDefaultValue()) {
auto descriptions = mVariant->getDescriptions();
descriptions.setDefaultValue(description.trimmed());
cmd->setDescriptions(descriptions);
}
if (norm != mVariant->getNorm()) {
cmd->setNorm(norm.trimmed());
}
execCmd(cmd.release());
} catch (const Exception& e) {
QMessageBox::critical(qApp->activeWindow(), tr("Error"), e.getMsg());
}
}
slint::Image ComponentVariantEditor::renderScene(int gate, float width,
float height) noexcept {
if (gate == static_cast<int>(mGates->row_count())) {
SlintGraphicsView view(SlintGraphicsView::defaultSymbolSceneRect(),
SlintGraphicsView::defaultMargins());
view.setUseOpenGl(mWorkspace.getSettings().useOpenGl.get());
return view.render(*mScene, width, height);
} else {
return mGates->renderScene(gate, width, height);
}
}
void ComponentVariantEditor::addGate() {
mGates->add();
}
void ComponentVariantEditor::autoConnectPins() {
if (!mComponent) {
return;
}
QHash<QString, int> numbers; // Memory how many ambiguous names were used.
std::unique_ptr<UndoCommandGroup> cmdGrp(
new UndoCommandGroup(tr("Auto-Assign Component Signals")));
for (ComponentSymbolVariantItem& item : mVariant->getSymbolItems()) {
if (std::shared_ptr<const Symbol> symbol =
mCache.getSymbol(item.getSymbolUuid(), false)) {
for (auto mapItem : item.getPinSignalMap().values()) {
CircuitIdentifier pinName =
symbol->getPins().get(mapItem->getPinUuid())->getName();
std::shared_ptr<const ComponentSignal> signal =
mComponent->getSignals().find(*pinName);
if (!signal) {
// Also look for names with a number at the end.
int number = numbers.value(*pinName, 0) + 1;
signal = mComponent->getSignals().find(
appendNumberToSignalName(*pinName, number));
if (signal) {
numbers[*pinName] = number;
}
}
std::optional<Uuid> signalUuid =
signal ? std::make_optional(signal->getUuid()) : std::nullopt;
std::unique_ptr<CmdComponentPinSignalMapItemEdit> cmd(
new CmdComponentPinSignalMapItemEdit(mapItem));
cmd->setSignalUuid(signalUuid);
cmdGrp->appendChild(cmd.release()); // can throw
}
}
}
execCmd(cmdGrp.release()); // can throw
}
void ComponentVariantEditor::updateUnassignedSignals() noexcept {
mHasUnassignedSignals = false;
if (mComponent) {
QSet<Uuid> connectedSignals;
int unconnectedPins = 0;
for (const auto& gate : mVariant->getSymbolItems()) {
for (const auto& pinout : gate.getPinSignalMap()) {
if (auto uuid = pinout.getSignalUuid()) {
connectedSignals.insert(*uuid);
} else {
++unconnectedPins;
}
}
}
mHasUnassignedSignals = (unconnectedPins > 0) &&
(!(mComponent->getSignals().getUuidSet() - connectedSignals).isEmpty());
}
}
/*******************************************************************************
* Private Methods
******************************************************************************/
void ComponentVariantEditor::execCmd(UndoCommand* cmd) {
if (mUndoStack) {
mUndoStack->execCmd(cmd);
} else {
std::unique_ptr<UndoCommand> cmdGuard(cmd);
cmdGuard->execute();
}
}
QString ComponentVariantEditor::appendNumberToSignalName(QString name,
int number) noexcept {
name.truncate(CircuitIdentifierConstraint::MAX_LENGTH - 4);
if ((!name.isEmpty()) && (name.back().isDigit())) {
name.append("_");
}
name.append(QString::number(number));
return cleanCircuitIdentifier(name);
}
/*******************************************************************************
* End of File
******************************************************************************/
} // namespace editor
} // namespace librepcb