Skip to content

Commit

Permalink
Fixed github issue #3734 by implementing swapping for subparts.
Browse files Browse the repository at this point in the history
  • Loading branch information
Kjell Morgenstern authored and pythonorcpp committed Mar 19, 2024
1 parent dd7f396 commit fcffa4f
Show file tree
Hide file tree
Showing 8 changed files with 329 additions and 25 deletions.
2 changes: 2 additions & 0 deletions pri/sketch.pri
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ HEADERS += \
src/sketch/sketchwidget.h \
src/sketch/welcomeview.h \
src/sketch/zoomablegraphicsview.h \
src/sketch/subpartswapmanager.h \


SOURCES += \
Expand All @@ -34,3 +35,4 @@ SOURCES += \
src/sketch/sketchwidget.cpp \
src/sketch/welcomeview.cpp \
src/sketch/zoomablegraphicsview.cpp \
src/sketch/subpartswapmanager.cpp \
1 change: 1 addition & 0 deletions src/mainwindow/mainwindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2561,6 +2561,7 @@ long MainWindow::swapSelectedAuxAux(ItemBase * itemBase, const QString & moduleI
swapThing.parentCommand = parentCommand;
swapThing.propsMap = propsMap;
swapThing.bbView = m_breadboardGraphicsView;
swapThing.subpartSwapManager = QSharedPointer<SubpartSwapManager>(new SubpartSwapManager(m_referenceModel));

long newID = 0;
for (int i = 0; i < 3; i++) {
Expand Down
109 changes: 85 additions & 24 deletions src/sketch/sketchwidget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6042,51 +6042,81 @@ long SketchWidget::setUpSwap(SwapThing & swapThing, bool master)
{
// setUpSwap is performed once for each of the three views.

ItemBase * itemBase = swapThing.itemBase;
if (itemBase->viewID() != m_viewID) {
itemBase = findItem(itemBase->id());
}

QSharedPointer<SubpartSwapManager> manager = swapThing.subpartSwapManager;

// Only perform this for the first of the three views.
if (swapThing.firstTime) {
manager->generateSubpartModelIndices(swapThing.newModuleID);
manager->correlateOldAndNewSubparts(swapThing.newModuleID, itemBase);

swapThing.firstTime = false;
swapThing.newID = swapStart(swapThing, master);

// need to ensure the parts are all created first thing
Q_EMIT swapStartSignal(swapThing, master);
}

ItemBase * itemBase = swapThing.itemBase;
if (itemBase->viewID() != m_viewID) {
itemBase = findItem(itemBase->id());
if (!itemBase) return swapThing.newID;
if (!itemBase) return swapThing.newID;

manager->resetOldSubParts(itemBase);
Q_FOREACH(NewSubModuleID newSubModuleID, manager->getNewModuleIDs()) {
if (manager->newModuleIDWasCorrelated(newSubModuleID)) {
ItemBase * oldSubPart = manager->extractSubPart(newSubModuleID);
setUpSwapMiddle(swapThing, newSubModuleID, oldSubPart, manager->getNewSubID(newSubModuleID), master);
setUpSwapFinal(swapThing, newSubModuleID, oldSubPart, manager->getNewSubID(newSubModuleID), master);
}
}

setUpSwapMiddle(swapThing, swapThing.newModuleID, itemBase, swapThing.newID, master);
setUpSwapFinal(swapThing, swapThing.newModuleID, itemBase, swapThing.newID, master);

if (master) {
new CleanUpRatsnestsCommand(this, CleanUpWiresCommand::RedoOnly, swapThing.parentCommand);
new CleanUpWiresCommand(this, CleanUpWiresCommand::RedoOnly, swapThing.parentCommand);
}

setUpSwapReconnect(swapThing, itemBase, swapThing.newID, master);
new CheckStickyCommand(this, BaseCommand::SingleView, swapThing.newID, false, CheckStickyCommand::RemoveOnly, swapThing.parentCommand);
return swapThing.newID;
}

void SketchWidget::setUpSwapMiddle(SwapThing & swapThing, QString newModuleID, ItemBase * itemBase, long newID, bool master) {
setUpSwapReconnect(swapThing, newModuleID, itemBase, newID, master);

new CheckStickyCommand(this, BaseCommand::SingleView, newID, false, CheckStickyCommand::RemoveOnly, swapThing.parentCommand);

PartLabel * partLabel = itemBase->partLabel();
if(partLabel != nullptr) {
QDomElement empty;
QDomElement labelGeometry;
partLabel->getLabelGeometry(labelGeometry);
auto * restoreLabelCommand = new RestoreLabelCommand(this, swapThing.newID, empty, labelGeometry, swapThing.parentCommand);
auto * restoreLabelCommand = new RestoreLabelCommand(this, newID, empty, labelGeometry, swapThing.parentCommand);
restoreLabelCommand->setRedoOnly();
}

if (itemBase->isPartLabelVisible()) {
auto * slc = new ShowLabelCommand(this, swapThing.parentCommand);
slc->add(swapThing.newID, true, true);
slc->add(newID, true, true);
}

if(partLabel != nullptr) {
auto * checkPartLabelLayerVisibilityCommand = new CheckPartLabelLayerVisibilityCommand(this, swapThing.newID, swapThing.parentCommand);
auto * checkPartLabelLayerVisibilityCommand = new CheckPartLabelLayerVisibilityCommand(this, newID, swapThing.parentCommand);
checkPartLabelLayerVisibilityCommand->setRedoOnly();
}
}

void SketchWidget::setUpSwapFinal(SwapThing & swapThing, QString newModuleID, ItemBase * itemBase, long newID, bool master) {
// Only perform this for the last of the three views.
if (master) {
auto * selectItemCommand = new SelectItemCommand(this, SelectItemCommand::NormalSelect, swapThing.parentCommand);
selectItemCommand->addRedo(swapThing.newID);
selectItemCommand->addRedo(newID);
selectItemCommand->addUndo(itemBase->id());

new ChangeLabelTextCommand(this, itemBase->id(), itemBase->instanceTitle(), itemBase->instanceTitle(), swapThing.parentCommand);
new ChangeLabelTextCommand(this, swapThing.newID, itemBase->instanceTitle(), itemBase->instanceTitle(), swapThing.parentCommand);
new ChangeLabelTextCommand(this, newID, itemBase->instanceTitle(), itemBase->instanceTitle(), swapThing.parentCommand);

/*
foreach (Wire * wire, swapThing.wiresToDelete) {
Expand All @@ -6104,17 +6134,14 @@ long SketchWidget::setUpSwap(SwapThing & swapThing, bool master)
}
*/

// Why crossview? Because this is called only for the last view.
makeDeleteItemCommand(itemBase, BaseCommand::CrossView, swapThing.parentCommand);
selectItemCommand = new SelectItemCommand(this, SelectItemCommand::NormalSelect, swapThing.parentCommand);
selectItemCommand->addRedo(swapThing.newID); // to make sure new item is selected so it appears in the info view
selectItemCommand->addRedo(newID); // to make sure new item is selected so it appears in the info view

prepDeleteProps(itemBase, swapThing.newID, swapThing.newModuleID, swapThing.propsMap, swapThing.parentCommand);
new CleanUpRatsnestsCommand(this, CleanUpWiresCommand::RedoOnly, swapThing.parentCommand);
new CleanUpWiresCommand(this, CleanUpWiresCommand::RedoOnly, swapThing.parentCommand);
prepDeleteProps(itemBase, newID, newModuleID, swapThing.propsMap, swapThing.parentCommand);
setUpSwapRenamePins(swapThing, itemBase);
}

return swapThing.newID;
}

void SketchWidget::setUpSwapRenamePins(SwapThing & swapThing, ItemBase * itemBase)
Expand Down Expand Up @@ -6157,17 +6184,17 @@ void SketchWidget::setUpSwapRenamePins(SwapThing & swapThing, ItemBase * itemBas
new RenamePinsCommand(this, swapThing.newID, oldLabels, newLabels, swapThing.parentCommand);
}

void SketchWidget::setUpSwapReconnect(SwapThing & swapThing, ItemBase * itemBase, long newID, bool master)
void SketchWidget::setUpSwapReconnect(SwapThing & swapThing, QString newModuleID, ItemBase * itemBase, long newID, bool master)
{
ModelPart * newModelPart = m_referenceModel->retrieveModelPart(swapThing.newModuleID);
ModelPart * newModelPart = m_referenceModel->retrieveModelPart(newModuleID);
if (!newModelPart) return;

QList<ConnectorItem *> fromConnectorItems(itemBase->cachedConnectorItems());

newModelPart->initConnectors(); // make sure the connectors are set up
QList< QPointer<Connector> > newConnectors = newModelPart->connectors().values();

bool checkReplacedby = (!itemBase->modelPart()->replacedby().isEmpty() && itemBase->modelPart()->replacedby() == swapThing.newModuleID);
bool checkReplacedby = (!itemBase->modelPart()->replacedby().isEmpty() && itemBase->modelPart()->replacedby() == newModuleID);

QList<ConnectorItem *> notFound;
QList<ConnectorItem *> other;
Expand Down Expand Up @@ -9521,7 +9548,6 @@ void SketchWidget::canConnect(Wire * from, ItemBase * to, bool & connect) {

long SketchWidget::swapStart(SwapThing & swapThing, bool master) {
Q_UNUSED(master);
// This function is only called for the first of the three views.

long newID = ItemBase::getNextID(swapThing.newModelIndex);

Expand All @@ -9542,8 +9568,9 @@ long SketchWidget::swapStart(SwapThing & swapThing, bool master) {

new MoveItemCommand(this, itemBase->id(), vg, vg, false, swapThing.parentCommand);

// command created for each view
newAddItemCommand(BaseCommand::SingleView, nullptr, swapThing.newModuleID, swapThing.viewLayerPlacement, vg, newID, true, swapThing.newModelIndex, true, swapThing.parentCommand);
new AddItemCommand(this, BaseCommand::SingleView, swapThing.newModuleID, swapThing.viewLayerPlacement, vg, newID, true, swapThing.newModelIndex, swapThing.parentCommand);

swapStartSubParts(swapThing, itemBase, newID, vg);

if (needsTransform) {
QTransform m(oldTransform.m11(), oldTransform.m12(), oldTransform.m21(), oldTransform.m22(), 0, 0);
Expand All @@ -9553,9 +9580,43 @@ long SketchWidget::swapStart(SwapThing & swapThing, bool master) {
m_infoView->updateLocation(itemBase->layerKinChief());
}

return newID;
}

void SketchWidget::swapStartSubParts(SwapThing & swapThing, ItemBase * itemBase, long newID, const ViewGeometry & vg) {
QSharedPointer<SubpartSwapManager> manager = swapThing.subpartSwapManager;
manager->resetOldSubParts(itemBase);
Q_FOREACH(NewSubModuleID newSubModuleID, manager->getNewModuleIDs()) {
bool subNeedsTransform = false;
ItemBase * subPart = nullptr;
QTransform subOldTransform;
ViewGeometry subVgLocal;
if (manager->newModuleIDWasCorrelated(newSubModuleID)) {
subPart = manager->extractSubPart(newSubModuleID);
ViewGeometry subVg = subPart->getViewGeometry();
subOldTransform = subVg.transform();
if (!subOldTransform.isIdentity()) {
// restore identity transform
subVg.setTransform(QTransform());
subNeedsTransform = true;
}
new MoveItemCommand(this, subPart->id(), subVg, subVg, false, swapThing.parentCommand);
subVgLocal = subVg;
}

long newSubID = manager->getNewSubID(newSubModuleID);
new AddItemCommand(this, BaseCommand::SingleView, newSubModuleID, swapThing.viewLayerPlacement, subVgLocal, newSubID, true, manager->getNewModelIndex(newSubModuleID), swapThing.parentCommand);
auto * asc = new AddSubpartCommand(this, BaseCommand::SingleView, newID, newSubID, swapThing.parentCommand);
asc->setRedoOnly();

return newID;
if (subNeedsTransform) {
QTransform m(subOldTransform.m11(), subOldTransform.m12(), subOldTransform.m21(), subOldTransform.m22(), 0, 0);
new TransformItemCommand(this, newSubID, m, m, swapThing.parentCommand);
}
if (m_infoView && subPart != nullptr) {
m_infoView->updateLocation(subPart->layerKinChief());
}
}
}

void SketchWidget::setPasting(bool pasting) {
Expand Down
7 changes: 6 additions & 1 deletion src/sketch/sketchwidget.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ along with Fritzing. If not, see <http://www.gnu.org/licenses/>.
#include "../viewlayer.h"
#include "../utils/misc.h"
#include "../utils/graphutils.h"
#include "../sketch/subpartswapmanager.h"
#include "../commands.h"

#include "renderthing.h"
Expand All @@ -64,6 +65,7 @@ struct SwapThing {
ItemBase * itemBase;
long newModelIndex;
QString newModuleID;
QSharedPointer<SubpartSwapManager> subpartSwapManager;
ViewLayer::ViewLayerPlacement viewLayerPlacement;
QList<Wire *> wiresToDelete;
QUndoCommand * parentCommand;
Expand Down Expand Up @@ -229,6 +231,8 @@ class SketchWidget : public InfoGraphicsView

bool spaceBarIsPressed() noexcept;
virtual long setUpSwap(SwapThing &, bool master);
void setUpSwapMiddle(SwapThing &, QString newModuleID, ItemBase * itemBase, long newID, bool master);
void setUpSwapFinal(SwapThing &, QString newModuleID, ItemBase * itemBase, long newID, bool master);
ConnectorItem * lastHoverEnterConnectorItem();
ItemBase * lastHoverEnterItem();
LayerHash & viewLayers();
Expand Down Expand Up @@ -442,7 +446,7 @@ class SketchWidget : public InfoGraphicsView
void drawForeground( QPainter * painter, const QRectF & rect );
void handleConnect(QDomElement & connect, ModelPart *, const QString & fromConnectorID, ViewLayer::ViewLayerID, QStringList & alreadyConnected,
QHash<long, ItemBase *> & newItems, QUndoCommand * parentCommand, bool seekOutsideConnections);
void setUpSwapReconnect(SwapThing &, ItemBase * itemBase, long newID, bool master);
void setUpSwapReconnect(SwapThing &, QString newModuleID, ItemBase * itemBase, long newID, bool master);
void setUpSwapRenamePins(SwapThing & swapThing, ItemBase * itemBase);
void makeSwapWire(SketchWidget *, ItemBase *, long newID, ConnectorItem * fromConnectorItem, ConnectorItem * toConnectorItem, Connector * newConnector, QUndoCommand * parentCommand);
bool swappedGender(ConnectorItem * originalConnectorItem, Connector * newConnector);
Expand Down Expand Up @@ -527,6 +531,7 @@ class SketchWidget : public InfoGraphicsView
virtual bool updateOK(ConnectorItem *, ConnectorItem *);
virtual void viewGeometryConversionHack(ViewGeometry &, ModelPart *);
void prepDeleteOtherPropsNumbers(const QString & propertyName, ItemBase * itemBase, long id, const QString & newModuleID, QUndoCommand * parentCommand);
void swapStartSubParts(SwapThing & swapThing, ItemBase * itemBase, long newID, const ViewGeometry & vg);

protected:
static bool lessThan(int a, int b);
Expand Down
135 changes: 135 additions & 0 deletions src/sketch/subpartswapmanager.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
/*********************************************************************
Part of the Fritzing project - http://fritzing.org
Copyright (c) 2024 Fritzing
Fritzing 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.
Fritzing 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 Fritzing. If not, see <http://www.gnu.org/licenses/>.
********************************************************************/

#include "subpartswapmanager.h"

#include "../referencemodel/referencemodel.h"
#include "../items/itembase.h"
#include "../utils/textutils.h"
#include "../utils/graphicsutils.h"
#include "../viewgeometry.h"
#include "../debugdialog.h"

SubpartSwapManager::SubpartSwapManager(ReferenceModel *referenceModel)
: m_referenceModel(referenceModel) {
}

//-------------------------------------------------------------------------------------------
// View independent function to be used once per swap session
void SubpartSwapManager::generateSubpartModelIndices(const NewMainModuleID &newModuleID) {
ModelPart * newModelPart = m_referenceModel->retrieveModelPart(newModuleID);
if (newModelPart->hasSubparts()) {
ModelPartShared * modelPartShared = newModelPart->modelPartShared();
if (modelPartShared) {
Q_FOREACH (ModelPartShared* mps, modelPartShared->subparts()) {
long newSubModelIndex = ModelPart::nextIndex();
m_subPartNewModuleID2NewModelIndexMap.insert(mps->moduleID(), newSubModelIndex);
long newSubID = ItemBase::getNextID(newSubModelIndex);
m_subPartNewModuleID2NewSubIDMap.insert(mps->moduleID(), newSubID);
}

}
}
}

void SubpartSwapManager::correlateOldAndNewSubparts(const NewMainModuleID &newModuleID, ItemBase *itemBase) {
if (!itemBase) return;
ModelPart * newModelPart = m_referenceModel->retrieveModelPart(newModuleID);
if (newModelPart->hasSubparts()) {
ModelPartShared * modelPartShared = newModelPart->modelPartShared();
if (modelPartShared) {
if (itemBase->subparts().count() != modelPartShared->subparts().count()) {
DebugDialog::debug(QString("SketchWidget::swapStart: subpart counts for old and new item disagree: old count: %1 new count: %2").arg(itemBase->subparts().count()).arg(modelPartShared->subparts().count()));
}
QMap<QString, ItemBase*> subpartMap;

QStringList oldModuleIDs, newModuleIDs;
Q_FOREACH (ItemBase* subpart, itemBase->subparts()) {
oldModuleIDs << subpart->moduleID();
}
Q_FOREACH (ModelPartShared* mps, modelPartShared->subparts()) {
newModuleIDs << mps->moduleID();
}
QString oldPrefix = TextUtils::commonPrefix(oldModuleIDs);
QString oldSuffix = TextUtils::commonSuffix(oldModuleIDs);
QString newPrefix = TextUtils::commonPrefix(newModuleIDs);
QString newSuffix = TextUtils::commonSuffix(newModuleIDs);

foreach (ItemBase* subpart, itemBase->subparts()) {
QString id = subpart->moduleID();
QString uniqueSubString = id.mid(oldPrefix.length(), id.length() - oldPrefix.length() - oldSuffix.length());
subpartMap.insert(uniqueSubString, subpart);
}

Q_FOREACH (ModelPartShared * mps, modelPartShared->subparts()) {
QString newSubModuleID = mps->moduleID();
QString uniqueSubString = newSubModuleID.mid(newPrefix.length(), newSubModuleID.length() - newPrefix.length() - newSuffix.length());
ItemBase * subPart = nullptr;
if (subpartMap.contains(uniqueSubString)) {
subPart = subpartMap[uniqueSubString];
m_subPartModuleIDNew2OldMap.insert(mps->moduleID(), subPart->moduleID());
m_subPartModuleIDOld2NewMap.insert(subPart->moduleID(), mps->moduleID());
} else {
DebugDialog::debug(QString("SketchWidget::swapStart old subpart with moduleID: %1 not found").arg(mps->moduleID()));
}
}
}
}
}

//-------------------------------------------------------------------------------------------

void SubpartSwapManager::resetOldSubParts(ItemBase * itemBase) {
m_subPartMap.clear();
Q_FOREACH (ItemBase* subPart, itemBase->subparts()) {
m_subPartMap.insert(subPart->moduleID(), subPart);
}
}

ItemBase * SubpartSwapManager::extractSubPart(const NewSubModuleID & newModuleID) {
QString oldSubPartModuleID = getOldModuleID(newModuleID);
ItemBase * subPart = m_subPartMap.value(oldSubPartModuleID, nullptr);
m_subPartMap.remove(oldSubPartModuleID);
return subPart;
}

bool SubpartSwapManager::newModuleIDWasCorrelated(const NewSubModuleID & newModuleID) const {
return m_subPartModuleIDNew2OldMap.contains(newModuleID);
}

NewModelIndex SubpartSwapManager::getNewModelIndex(const NewSubModuleID &newModuleID) const {
return m_subPartNewModuleID2NewModelIndexMap.value(newModuleID, -1);
}

QList<NewSubModuleID> SubpartSwapManager::getNewModuleIDs() const {
return m_subPartNewModuleID2NewModelIndexMap.keys();
}

NewSubID SubpartSwapManager::getNewSubID(const NewSubModuleID &newModuleID) const {
return m_subPartNewModuleID2NewSubIDMap.value(newModuleID, -1);
}

OldSubModuleID SubpartSwapManager::getOldModuleID(const NewSubModuleID &newModuleID) const {
return m_subPartModuleIDNew2OldMap.value(newModuleID);
}

NewSubModuleID SubpartSwapManager::getNewModuleID(const OldSubModuleID &oldModuleID) const {
return m_subPartModuleIDOld2NewMap.value(oldModuleID);
}
Loading

0 comments on commit fcffa4f

Please sign in to comment.