@@ -10,6 +10,9 @@ namespace rack { | |||||
namespace app { | namespace app { | ||||
struct CableWidget; | |||||
struct PlugWidget : widget::Widget { | struct PlugWidget : widget::Widget { | ||||
struct Internal; | struct Internal; | ||||
Internal* internal; | Internal* internal; | ||||
@@ -19,8 +22,9 @@ struct PlugWidget : widget::Widget { | |||||
void step() override; | void step() override; | ||||
PRIVATE void setColor(NVGcolor color); | PRIVATE void setColor(NVGcolor color); | ||||
PRIVATE void setAngle(float angle); | PRIVATE void setAngle(float angle); | ||||
PRIVATE void setPortWidget(PortWidget* portWidget); | |||||
PRIVATE void setTop(bool top); | PRIVATE void setTop(bool top); | ||||
CableWidget* getCable(); | |||||
engine::Port::Type getType(); | |||||
}; | }; | ||||
@@ -41,6 +45,7 @@ struct CableWidget : widget::Widget { | |||||
CableWidget(); | CableWidget(); | ||||
~CableWidget(); | ~CableWidget(); | ||||
/** Returns whether cable is connected to 2 ports. */ | |||||
bool isComplete(); | bool isComplete(); | ||||
/** Based on the input/output ports, re-creates the cable and removes/adds it to the Engine. */ | /** Based on the input/output ports, re-creates the cable and removes/adds it to the Engine. */ | ||||
void updateCable(); | void updateCable(); | ||||
@@ -50,6 +55,15 @@ struct CableWidget : widget::Widget { | |||||
*/ | */ | ||||
void setCable(engine::Cable* cable); | void setCable(engine::Cable* cable); | ||||
engine::Cable* getCable(); | engine::Cable* getCable(); | ||||
PlugWidget*& getPlug(engine::Port::Type type) { | |||||
return type == engine::Port::INPUT ? inputPlug : outputPlug; | |||||
} | |||||
PortWidget*& getPort(engine::Port::Type type) { | |||||
return type == engine::Port::INPUT ? inputPort : outputPort; | |||||
} | |||||
PortWidget*& getHoveredPort(engine::Port::Type type) { | |||||
return type == engine::Port::INPUT ? hoveredInputPort : hoveredOutputPort; | |||||
} | |||||
math::Vec getInputPos(); | math::Vec getInputPos(); | ||||
math::Vec getOutputPos(); | math::Vec getOutputPos(); | ||||
void mergeJson(json_t* rootJ); | void mergeJson(json_t* rootJ); | ||||
@@ -115,23 +115,29 @@ struct RackWidget : widget::OpaqueWidget { | |||||
void clearCablesAction(); | void clearCablesAction(); | ||||
/** Removes all cables connected to the port */ | /** Removes all cables connected to the port */ | ||||
void clearCablesOnPort(PortWidget* port); | void clearCablesOnPort(PortWidget* port); | ||||
/** Adds a complete cable and adopts ownership. | |||||
/** Adds a cable and adopts ownership. | |||||
*/ | */ | ||||
void addCable(CableWidget* cw); | void addCable(CableWidget* cw); | ||||
/** Removes cable and releases ownership to caller. | /** Removes cable and releases ownership to caller. | ||||
*/ | */ | ||||
void removeCable(CableWidget* cw); | void removeCable(CableWidget* cw); | ||||
CableWidget* getIncompleteCable(); | |||||
/** Takes ownership of `cw` and adds it as a child if it isn't already. */ | |||||
void setIncompleteCable(CableWidget* cw); | |||||
CableWidget* releaseIncompleteCable(); | |||||
/** Returns the most recently added complete cable connected to the given Port, i.e. the top of the stack. */ | |||||
/** Returns the top incomplete cable. Use getIncompleteCables() instead. */ | |||||
DEPRECATED CableWidget* getIncompleteCable(); | |||||
/** Returns the topmost plug stacked on the port. */ | |||||
PlugWidget* getTopPlug(PortWidget* port); | |||||
/** Returns the cable with the topmost plug stacked on the port. */ | |||||
CableWidget* getTopCable(PortWidget* port); | CableWidget* getTopCable(PortWidget* port); | ||||
CableWidget* getCable(int64_t cableId); | CableWidget* getCable(int64_t cableId); | ||||
CableWidget* getCable(PortWidget* outputPort, PortWidget* inputPort); | CableWidget* getCable(PortWidget* outputPort, PortWidget* inputPort); | ||||
/** Returns all cables, complete and incomplete. */ | |||||
std::vector<CableWidget*> getCables(); | |||||
/** Returns all cables attached to 2 ports. */ | |||||
std::vector<CableWidget*> getCompleteCables(); | std::vector<CableWidget*> getCompleteCables(); | ||||
/** Returns all cables attached to port, complete or not. */ | |||||
/** Returns all cables attached to less than 2 ports. */ | |||||
std::vector<CableWidget*> getIncompleteCables(); | |||||
/** Returns all cables attached to the port, complete or not. */ | |||||
std::vector<CableWidget*> getCablesOnPort(PortWidget* port); | std::vector<CableWidget*> getCablesOnPort(PortWidget* port); | ||||
/** Returns all complete cables attached to the port. */ | |||||
std::vector<CableWidget*> getCompleteCablesOnPort(PortWidget* port); | std::vector<CableWidget*> getCompleteCablesOnPort(PortWidget* port); | ||||
/** Returns but does not advance the next cable color. */ | /** Returns but does not advance the next cable color. */ | ||||
int getNextCableColorId(); | int getNextCableColorId(); | ||||
@@ -34,17 +34,17 @@ struct PlugLight : componentlibrary::TRedGreenBlueLight<app::MultiLightWidget> { | |||||
struct PlugWidget::Internal { | struct PlugWidget::Internal { | ||||
CableWidget* cableWidget; | |||||
engine::Port::Type type; | |||||
/** Initially pointing upward. */ | /** Initially pointing upward. */ | ||||
float angle = 0.5f * M_PI; | float angle = 0.5f * M_PI; | ||||
PortWidget* portWidget = NULL; | |||||
widget::FramebufferWidget* fb; | widget::FramebufferWidget* fb; | ||||
widget::TransformWidget* plugTransform; | widget::TransformWidget* plugTransform; | ||||
TintWidget* plugTint; | TintWidget* plugTint; | ||||
widget::SvgWidget* plug; | widget::SvgWidget* plug; | ||||
widget::SvgWidget* plugPort; | widget::SvgWidget* plugPort; | ||||
app::MultiLightWidget* plugLight; | app::MultiLightWidget* plugLight; | ||||
}; | }; | ||||
@@ -87,8 +87,10 @@ PlugWidget::~PlugWidget() { | |||||
void PlugWidget::step() { | void PlugWidget::step() { | ||||
std::vector<float> values(3); | std::vector<float> values(3); | ||||
if (internal->portWidget && internal->plugLight->isVisible()) { | |||||
engine::Port* port = internal->portWidget->getPort(); | |||||
PortWidget* pw = internal->cableWidget->getPort(internal->type); | |||||
if (pw && internal->plugLight->isVisible()) { | |||||
engine::Port* port = pw->getPort(); | |||||
if (port) { | if (port) { | ||||
for (int i = 0; i < 3; i++) { | for (int i = 0; i < 3; i++) { | ||||
values[i] = port->plugLights[i].getBrightness(); | values[i] = port->plugLights[i].getBrightness(); | ||||
@@ -116,14 +118,18 @@ void PlugWidget::setAngle(float angle) { | |||||
internal->fb->setDirty(); | internal->fb->setDirty(); | ||||
} | } | ||||
void PlugWidget::setPortWidget(PortWidget* portWidget) { | |||||
internal->portWidget = portWidget; | |||||
} | |||||
void PlugWidget::setTop(bool top) { | void PlugWidget::setTop(bool top) { | ||||
internal->plugLight->setVisible(top); | internal->plugLight->setVisible(top); | ||||
} | } | ||||
CableWidget* PlugWidget::getCable() { | |||||
return internal->cableWidget; | |||||
} | |||||
engine::Port::Type PlugWidget::getType() { | |||||
return internal->type; | |||||
} | |||||
struct CableWidget::Internal { | struct CableWidget::Internal { | ||||
}; | }; | ||||
@@ -134,7 +140,12 @@ CableWidget::CableWidget() { | |||||
color = color::BLACK_TRANSPARENT; | color = color::BLACK_TRANSPARENT; | ||||
outputPlug = new PlugWidget; | outputPlug = new PlugWidget; | ||||
outputPlug->internal->cableWidget = this; | |||||
outputPlug->internal->type = engine::Port::OUTPUT; | |||||
inputPlug = new PlugWidget; | inputPlug = new PlugWidget; | ||||
inputPlug->internal->cableWidget = this; | |||||
inputPlug->internal->type = engine::Port::INPUT; | |||||
} | } | ||||
@@ -267,20 +278,18 @@ void CableWidget::step() { | |||||
colorOpaque.a = 1.f; | colorOpaque.a = 1.f; | ||||
// Draw output plug | // Draw output plug | ||||
bool outputTop = !isComplete() || APP->scene->rack->getTopCable(outputPort) == this; | |||||
outputPlug->setPosition(outputPos); | outputPlug->setPosition(outputPos); | ||||
outputPlug->setTop(outputTop); | |||||
// bool outputTop = isComplete() && APP->scene->rack->getTopCable(outputPort) == this; | |||||
// outputPlug->setTop(outputTop); | |||||
outputPlug->setAngle(slump.minus(outputPos).arg()); | outputPlug->setAngle(slump.minus(outputPos).arg()); | ||||
outputPlug->setColor(colorOpaque); | outputPlug->setColor(colorOpaque); | ||||
outputPlug->setPortWidget(outputPort); | |||||
// Draw input plug | // Draw input plug | ||||
bool inputTop = !isComplete() || APP->scene->rack->getTopCable(inputPort) == this; | |||||
inputPlug->setPosition(inputPos); | inputPlug->setPosition(inputPos); | ||||
inputPlug->setTop(inputTop); | |||||
// bool inputTop = isComplete() && APP->scene->rack->getTopCable(inputPort) == this; | |||||
// inputPlug->setTop(inputTop); | |||||
inputPlug->setAngle(slump.minus(inputPos).arg()); | inputPlug->setAngle(slump.minus(inputPos).arg()); | ||||
inputPlug->setColor(colorOpaque); | inputPlug->setColor(colorOpaque); | ||||
inputPlug->setPortWidget(inputPort); | |||||
Widget::step(); | Widget::step(); | ||||
} | } | ||||
@@ -329,8 +338,9 @@ void CableWidget::drawLayer(const DrawArgs& args, int layer) { | |||||
// The endpoints are off-center | // The endpoints are off-center | ||||
math::Vec slump = getSlumpPos(outputPos, inputPos); | math::Vec slump = getSlumpPos(outputPos, inputPos); | ||||
outputPos = outputPos.plus(slump.minus(outputPos).normalize().mult(13.0)); | |||||
inputPos = inputPos.plus(slump.minus(inputPos).normalize().mult(13.0)); | |||||
float dist = 14.f; | |||||
outputPos = outputPos.plus(slump.minus(outputPos).normalize().mult(dist)); | |||||
inputPos = inputPos.plus(slump.minus(inputPos).normalize().mult(dist)); | |||||
nvgLineCap(args.vg, NVG_ROUND); | nvgLineCap(args.vg, NVG_ROUND); | ||||
// Avoids glitches when cable is bent | // Avoids glitches when cable is bent | ||||
@@ -48,7 +48,7 @@ struct PortTooltip : ui::Tooltip { | |||||
text += string::f("%d: ", i + 1); | text += string::f("%d: ", i + 1); | ||||
text += string::f("% .3fV", math::normalizeZero(v)); | text += string::f("% .3fV", math::normalizeZero(v)); | ||||
} | } | ||||
// Connected to | |||||
// From/To | |||||
std::vector<CableWidget*> cables = APP->scene->rack->getCompleteCablesOnPort(portWidget); | std::vector<CableWidget*> cables = APP->scene->rack->getCompleteCablesOnPort(portWidget); | ||||
for (auto it = cables.rbegin(); it != cables.rend(); it++) { | for (auto it = cables.rbegin(); it != cables.rend(); it++) { | ||||
CableWidget* cable = *it; | CableWidget* cable = *it; | ||||
@@ -136,6 +136,8 @@ struct PortCableItem : ui::ColorDotMenuItem { | |||||
ui::Menu* createChildMenu() override { | ui::Menu* createChildMenu() override { | ||||
ui::Menu* menu = new ui::Menu; | ui::Menu* menu = new ui::Menu; | ||||
// menu->addChild(createMenuLabel(string::f("ID: %ld", cw->cable->id))); | |||||
for (NVGcolor color : settings::cableColors) { | for (NVGcolor color : settings::cableColors) { | ||||
// Include extra leading spaces for the color circle | // Include extra leading spaces for the color circle | ||||
CableColorItem* item = createMenuItem<CableColorItem>("Set color"); | CableColorItem* item = createMenuItem<CableColorItem>("Set color"); | ||||
@@ -342,6 +344,12 @@ void PortWidget::step() { | |||||
void PortWidget::draw(const DrawArgs& args) { | void PortWidget::draw(const DrawArgs& args) { | ||||
PortWidget* draggedPw = dynamic_cast<PortWidget*>(APP->event->getDraggedWidget()); | |||||
if (draggedPw) { | |||||
// TODO | |||||
} | |||||
// TODO Reimplement this | |||||
#if 0 | |||||
CableWidget* cw = APP->scene->rack->getIncompleteCable(); | CableWidget* cw = APP->scene->rack->getIncompleteCable(); | ||||
if (cw) { | if (cw) { | ||||
// Dim the PortWidget if the active cable cannot plug into this PortWidget | // Dim the PortWidget if the active cable cannot plug into this PortWidget | ||||
@@ -349,6 +357,7 @@ void PortWidget::draw(const DrawArgs& args) { | |||||
nvgTint(args.vg, nvgRGBf(0.33, 0.33, 0.33)); | nvgTint(args.vg, nvgRGBf(0.33, 0.33, 0.33)); | ||||
} | } | ||||
} | } | ||||
#endif | |||||
Widget::draw(args); | Widget::draw(args); | ||||
} | } | ||||
@@ -426,32 +435,35 @@ void PortWidget::onDragStart(const DragStartEvent& e) { | |||||
h->setCable(cw); | h->setCable(cw); | ||||
APP->history->push(h); | APP->history->push(h); | ||||
// Disconnect and reuse existing cable | |||||
APP->scene->rack->removeCable(cw); | |||||
if (type == engine::Port::OUTPUT) | |||||
cw->outputPort = NULL; | |||||
else | |||||
cw->inputPort = NULL; | |||||
// Reuse existing cable | |||||
cw->getPort(type) = NULL; | |||||
cw->updateCable(); | cw->updateCable(); | ||||
} | } | ||||
} | } | ||||
// If not using existing cable, create new cable | |||||
if (!cw) { | if (!cw) { | ||||
// Create a new cable | |||||
cw = new CableWidget; | cw = new CableWidget; | ||||
// Set color | // Set color | ||||
cw->color = APP->scene->rack->getNextCableColor(); | cw->color = APP->scene->rack->getNextCableColor(); | ||||
// Set port | // Set port | ||||
if (type == engine::Port::OUTPUT) | |||||
cw->outputPort = this; | |||||
else | |||||
cw->inputPort = this; | |||||
cw->getPort(type) = this; | |||||
cw->updateCable(); | cw->updateCable(); | ||||
} | } | ||||
APP->scene->rack->setIncompleteCable(cw); | |||||
// Add cable to rack if not already added | |||||
if (!cw->getParent()) { | |||||
APP->scene->rack->addCable(cw); | |||||
} | |||||
else { | |||||
// Move grabbed plug to top of stack | |||||
PlugWidget* plug = cw->getPlug(type); | |||||
assert(plug); | |||||
APP->scene->rack->getPlugContainer()->removeChild(plug); | |||||
APP->scene->rack->getPlugContainer()->addChild(plug); | |||||
} | |||||
} | } | ||||
@@ -459,20 +471,31 @@ void PortWidget::onDragEnd(const DragEndEvent& e) { | |||||
if (e.button != GLFW_MOUSE_BUTTON_LEFT) | if (e.button != GLFW_MOUSE_BUTTON_LEFT) | ||||
return; | return; | ||||
CableWidget* cw = APP->scene->rack->releaseIncompleteCable(); | |||||
if (!cw) | |||||
std::vector<CableWidget*> cws = APP->scene->rack->getIncompleteCables(); | |||||
if (cws.empty()) | |||||
return; | return; | ||||
if (cw->isComplete()) { | |||||
APP->scene->rack->addCable(cw); | |||||
history::ComplexAction* h = new history::ComplexAction; | |||||
// history::CableAdd | |||||
history::CableAdd* h = new history::CableAdd; | |||||
h->setCable(cw); | |||||
APP->history->push(h); | |||||
for (CableWidget* cw : cws) { | |||||
if (cw->isComplete()) { | |||||
// history::CableAdd | |||||
history::CableAdd* hAdd = new history::CableAdd; | |||||
hAdd->setCable(cw); | |||||
h->push(hAdd); | |||||
} | |||||
else { | |||||
APP->scene->rack->removeCable(cw); | |||||
delete cw; | |||||
} | |||||
} | |||||
// Push history | |||||
if (h->isEmpty()) { | |||||
delete h; | |||||
} | } | ||||
else { | else { | ||||
delete cw; | |||||
APP->history->push(h); | |||||
} | } | ||||
} | } | ||||
@@ -481,18 +504,28 @@ void PortWidget::onDragDrop(const DragDropEvent& e) { | |||||
if (e.button != GLFW_MOUSE_BUTTON_LEFT) | if (e.button != GLFW_MOUSE_BUTTON_LEFT) | ||||
return; | return; | ||||
PortWidget* pwOrigin = dynamic_cast<PortWidget*>(e.origin); | |||||
if (!pwOrigin) | |||||
return; | |||||
// HACK: Only delete tooltip if we're not (normal) dragging it. | // HACK: Only delete tooltip if we're not (normal) dragging it. | ||||
if (e.origin == this) | |||||
if (pwOrigin == this) { | |||||
createTooltip(); | createTooltip(); | ||||
} | |||||
CableWidget* cw = APP->scene->rack->getIncompleteCable(); | |||||
if (cw) { | |||||
cw->hoveredOutputPort = cw->hoveredInputPort = NULL; | |||||
if (type == engine::Port::OUTPUT && cw->inputPort && !APP->scene->rack->getCable(this, cw->inputPort)) { | |||||
cw->outputPort = this; | |||||
for (CableWidget* cw : APP->scene->rack->getIncompleteCables()) { | |||||
cw->hoveredOutputPort = NULL; | |||||
cw->hoveredInputPort = NULL; | |||||
if (type == engine::Port::OUTPUT) { | |||||
// Check that similar cable doesn't exist | |||||
if (cw->inputPort && !APP->scene->rack->getCable(this, cw->inputPort)) { | |||||
cw->outputPort = this; | |||||
} | |||||
} | } | ||||
if (type == engine::Port::INPUT && cw->outputPort && !APP->scene->rack->getCable(cw->outputPort, this)) { | |||||
cw->inputPort = this; | |||||
else { | |||||
if (cw->outputPort && !APP->scene->rack->getCable(cw->outputPort, this)) { | |||||
cw->inputPort = this; | |||||
} | |||||
} | } | ||||
cw->updateCable(); | cw->updateCable(); | ||||
} | } | ||||
@@ -503,18 +536,25 @@ void PortWidget::onDragEnter(const DragEnterEvent& e) { | |||||
if (e.button != GLFW_MOUSE_BUTTON_LEFT) | if (e.button != GLFW_MOUSE_BUTTON_LEFT) | ||||
return; | return; | ||||
PortWidget* pw = dynamic_cast<PortWidget*>(e.origin); | |||||
if (pw) { | |||||
createTooltip(); | |||||
} | |||||
// Check if dragging from another port, which implies that a cable is being dragged | |||||
PortWidget* pwOrigin = dynamic_cast<PortWidget*>(e.origin); | |||||
if (!pwOrigin) | |||||
return; | |||||
CableWidget* cw = APP->scene->rack->getIncompleteCable(); | |||||
if (cw) { | |||||
if (type == engine::Port::OUTPUT && cw->inputPort && !APP->scene->rack->getCable(this, cw->inputPort)) { | |||||
cw->hoveredOutputPort = this; | |||||
createTooltip(); | |||||
// Make all incomplete cables hover this port | |||||
for (CableWidget* cw : APP->scene->rack->getIncompleteCables()) { | |||||
if (type == engine::Port::OUTPUT) { | |||||
// Check that similar cable doesn't exist | |||||
if (cw->inputPort && !APP->scene->rack->getCable(this, cw->inputPort)) { | |||||
cw->hoveredOutputPort = this; | |||||
} | |||||
} | } | ||||
if (type == engine::Port::INPUT && cw->outputPort && !APP->scene->rack->getCable(cw->outputPort, this)) { | |||||
cw->hoveredInputPort = this; | |||||
else { | |||||
if (cw->outputPort && !APP->scene->rack->getCable(cw->outputPort, this)) { | |||||
cw->hoveredInputPort = this; | |||||
} | |||||
} | } | ||||
} | } | ||||
} | } | ||||
@@ -526,16 +566,12 @@ void PortWidget::onDragLeave(const DragLeaveEvent& e) { | |||||
if (e.button != GLFW_MOUSE_BUTTON_LEFT) | if (e.button != GLFW_MOUSE_BUTTON_LEFT) | ||||
return; | return; | ||||
PortWidget* originPort = dynamic_cast<PortWidget*>(e.origin); | |||||
if (!originPort) | |||||
PortWidget* pwOrigin = dynamic_cast<PortWidget*>(e.origin); | |||||
if (!pwOrigin) | |||||
return; | return; | ||||
CableWidget* cw = APP->scene->rack->getIncompleteCable(); | |||||
if (cw) { | |||||
if (type == engine::Port::OUTPUT) | |||||
cw->hoveredOutputPort = NULL; | |||||
if (type == engine::Port::INPUT) | |||||
cw->hoveredInputPort = NULL; | |||||
for (CableWidget* cw : APP->scene->rack->getIncompleteCables()) { | |||||
cw->getHoveredPort(type) = NULL; | |||||
} | } | ||||
} | } | ||||
@@ -28,7 +28,6 @@ struct RackWidget::Internal { | |||||
widget::Widget* moduleContainer = NULL; | widget::Widget* moduleContainer = NULL; | ||||
widget::Widget* plugContainer = NULL; | widget::Widget* plugContainer = NULL; | ||||
widget::Widget* cableContainer = NULL; | widget::Widget* cableContainer = NULL; | ||||
CableWidget* incompleteCable = NULL; | |||||
int nextCableColorId = 0; | int nextCableColorId = 0; | ||||
/** The last mouse position in the RackWidget */ | /** The last mouse position in the RackWidget */ | ||||
math::Vec mousePos; | math::Vec mousePos; | ||||
@@ -289,6 +288,13 @@ void RackWidget::mergeJson(json_t* rootJ) { | |||||
json_object_set_new(moduleJ, "pos", posJ); | json_object_set_new(moduleJ, "pos", posJ); | ||||
} | } | ||||
// Calculate plug orders | |||||
std::map<Widget*, int> plugOrders; | |||||
int plugOrder = 1; | |||||
for (Widget* w : internal->plugContainer->children) { | |||||
plugOrders[w] = plugOrder++; | |||||
} | |||||
// cables | // cables | ||||
json_t* cablesJ = json_object_get(rootJ, "cables"); | json_t* cablesJ = json_object_get(rootJ, "cables"); | ||||
if (!cablesJ) | if (!cablesJ) | ||||
@@ -308,6 +314,20 @@ void RackWidget::mergeJson(json_t* rootJ) { | |||||
} | } | ||||
cw->mergeJson(cableJ); | cw->mergeJson(cableJ); | ||||
// inputPlugOrder | |||||
auto plugOrderIt = plugOrders.find(cw->inputPlug); | |||||
if (plugOrderIt != plugOrders.end()) { | |||||
int inputPlugOrder = plugOrderIt->second; | |||||
json_object_set_new(cableJ, "inputPlugOrder", json_integer(inputPlugOrder)); | |||||
} | |||||
// outputPlugOrder | |||||
plugOrderIt = plugOrders.find(cw->outputPlug); | |||||
if (plugOrderIt != plugOrders.end()) { | |||||
int outputPlugOrder = plugOrderIt->second; | |||||
json_object_set_new(cableJ, "outputPlugOrder", json_integer(outputPlugOrder)); | |||||
} | |||||
} | } | ||||
} | } | ||||
@@ -369,6 +389,8 @@ void RackWidget::fromJson(json_t* rootJ) { | |||||
updateExpanders(); | updateExpanders(); | ||||
std::map<Widget*, int> plugOrders; | |||||
// cables | // cables | ||||
json_t* cablesJ = json_object_get(rootJ, "cables"); | json_t* cablesJ = json_object_get(rootJ, "cables"); | ||||
// In <=v0.6, cables were called wires | // In <=v0.6, cables were called wires | ||||
@@ -409,7 +431,24 @@ void RackWidget::fromJson(json_t* rootJ) { | |||||
continue; | continue; | ||||
} | } | ||||
addCable(cw); | addCable(cw); | ||||
// inputPlugOrder | |||||
json_t* inputPlugOrderJ = json_object_get(cableJ, "inputPlugOrder"); | |||||
if (inputPlugOrderJ) { | |||||
plugOrders[cw->inputPlug] = json_integer_value(inputPlugOrderJ); | |||||
} | |||||
// outputPlugOrder | |||||
json_t* outputPlugOrderJ = json_object_get(cableJ, "outputPlugOrder"); | |||||
if (outputPlugOrderJ) { | |||||
plugOrders[cw->outputPlug] = json_integer_value(outputPlugOrderJ); | |||||
} | |||||
} | } | ||||
// Reorder plugs, approximately O(n log(n) log(n)) | |||||
internal->plugContainer->children.sort([&](Widget* w1, Widget* w2) { | |||||
return get(plugOrders, w1, 0) < get(plugOrders, w2, 0); | |||||
}); | |||||
} | } | ||||
struct PasteJsonResult { | struct PasteJsonResult { | ||||
@@ -680,7 +719,6 @@ std::vector<ModuleWidget*> RackWidget::getModules() { | |||||
assert(mw); | assert(mw); | ||||
mws.push_back(mw); | mws.push_back(mw); | ||||
} | } | ||||
mws.shrink_to_fit(); | |||||
return mws; | return mws; | ||||
} | } | ||||
@@ -1010,8 +1048,8 @@ json_t* RackWidget::selectionToJson(bool cables) { | |||||
if (cables) { | if (cables) { | ||||
// cables | // cables | ||||
json_t* cablesJ = json_array(); | json_t* cablesJ = json_array(); | ||||
// Only add complete cables to JSON | |||||
for (CableWidget* cw : getCompleteCables()) { | for (CableWidget* cw : getCompleteCables()) { | ||||
// Only add cables attached on both ends to selected modules | |||||
engine::Cable* cable = cw->getCable(); | engine::Cable* cable = cw->getCable(); | ||||
if (!cable || !cable->inputModule || !cable->outputModule) | if (!cable || !cable->inputModule || !cable->outputModule) | ||||
continue; | continue; | ||||
@@ -1394,8 +1432,7 @@ void RackWidget::appendSelectionContextMenu(ui::Menu* menu) { | |||||
} | } | ||||
void RackWidget::clearCables() { | void RackWidget::clearCables() { | ||||
internal->incompleteCable = NULL; | |||||
// Since cables manage plugs, all plugs are removed from plugContainer | |||||
// Since cables manage plugs, all plugs will be removed from plugContainer | |||||
internal->cableContainer->clearChildren(); | internal->cableContainer->clearChildren(); | ||||
} | } | ||||
@@ -1421,61 +1458,46 @@ void RackWidget::clearCablesAction() { | |||||
void RackWidget::clearCablesOnPort(PortWidget* port) { | void RackWidget::clearCablesOnPort(PortWidget* port) { | ||||
for (CableWidget* cw : getCablesOnPort(port)) { | for (CableWidget* cw : getCablesOnPort(port)) { | ||||
// Check if cable is connected to port | |||||
if (cw == internal->incompleteCable) { | |||||
internal->incompleteCable = NULL; | |||||
internal->cableContainer->removeChild(cw); | |||||
} | |||||
else { | |||||
removeCable(cw); | |||||
} | |||||
removeCable(cw); | |||||
delete cw; | delete cw; | ||||
} | } | ||||
} | } | ||||
void RackWidget::addCable(CableWidget* cw) { | void RackWidget::addCable(CableWidget* cw) { | ||||
assert(cw->isComplete()); | |||||
internal->cableContainer->addChild(cw); | internal->cableContainer->addChild(cw); | ||||
} | } | ||||
void RackWidget::removeCable(CableWidget* cw) { | void RackWidget::removeCable(CableWidget* cw) { | ||||
assert(cw->isComplete()); | |||||
internal->cableContainer->removeChild(cw); | internal->cableContainer->removeChild(cw); | ||||
} | } | ||||
CableWidget* RackWidget::getIncompleteCable() { | CableWidget* RackWidget::getIncompleteCable() { | ||||
return internal->incompleteCable; | |||||
} | |||||
void RackWidget::setIncompleteCable(CableWidget* cw) { | |||||
if (internal->incompleteCable) { | |||||
internal->cableContainer->removeChild(internal->incompleteCable); | |||||
delete internal->incompleteCable; | |||||
internal->incompleteCable = NULL; | |||||
} | |||||
if (cw) { | |||||
internal->cableContainer->addChild(cw); | |||||
internal->incompleteCable = cw; | |||||
for (auto it = internal->cableContainer->children.rbegin(); it != internal->cableContainer->children.rend(); it++) { | |||||
CableWidget* cw = dynamic_cast<CableWidget*>(*it); | |||||
assert(cw); | |||||
if (!cw->isComplete()) | |||||
return cw; | |||||
} | } | ||||
return NULL; | |||||
} | } | ||||
CableWidget* RackWidget::releaseIncompleteCable() { | |||||
if (!internal->incompleteCable) | |||||
return NULL; | |||||
CableWidget* cw = internal->incompleteCable; | |||||
internal->cableContainer->removeChild(internal->incompleteCable); | |||||
internal->incompleteCable = NULL; | |||||
return cw; | |||||
PlugWidget* RackWidget::getTopPlug(PortWidget* port) { | |||||
assert(port); | |||||
for (auto it = internal->plugContainer->children.rbegin(); it != internal->plugContainer->children.rend(); it++) { | |||||
PlugWidget* plug = dynamic_cast<PlugWidget*>(*it); | |||||
assert(plug); | |||||
CableWidget* cw = plug->getCable(); | |||||
PortWidget* port2 = cw->getPort(plug->getType()); | |||||
if (port2 == port) | |||||
return plug; | |||||
} | |||||
return NULL; | |||||
} | } | ||||
CableWidget* RackWidget::getTopCable(PortWidget* port) { | CableWidget* RackWidget::getTopCable(PortWidget* port) { | ||||
for (auto it = internal->cableContainer->children.rbegin(); it != internal->cableContainer->children.rend(); it++) { | |||||
CableWidget* cw = dynamic_cast<CableWidget*>(*it); | |||||
assert(cw); | |||||
if (cw->inputPort == port || cw->outputPort == port) | |||||
return cw; | |||||
} | |||||
PlugWidget* plug = getTopPlug(port); | |||||
if (plug) | |||||
return plug->getCable(); | |||||
return NULL; | return NULL; | ||||
} | } | ||||
@@ -1501,8 +1523,20 @@ CableWidget* RackWidget::getCable(PortWidget* outputPort, PortWidget* inputPort) | |||||
return NULL; | return NULL; | ||||
} | } | ||||
std::vector<CableWidget*> RackWidget::getCables() { | |||||
std::vector<CableWidget*> cws; | |||||
cws.reserve(internal->cableContainer->children.size()); | |||||
for (widget::Widget* w : internal->cableContainer->children) { | |||||
CableWidget* cw = dynamic_cast<CableWidget*>(w); | |||||
assert(cw); | |||||
cws.push_back(cw); | |||||
} | |||||
return cws; | |||||
} | |||||
std::vector<CableWidget*> RackWidget::getCompleteCables() { | std::vector<CableWidget*> RackWidget::getCompleteCables() { | ||||
std::vector<CableWidget*> cws; | std::vector<CableWidget*> cws; | ||||
// Assume that most cables are complete, so pre-allocate and shrink vector. | |||||
cws.reserve(internal->cableContainer->children.size()); | cws.reserve(internal->cableContainer->children.size()); | ||||
for (widget::Widget* w : internal->cableContainer->children) { | for (widget::Widget* w : internal->cableContainer->children) { | ||||
CableWidget* cw = dynamic_cast<CableWidget*>(w); | CableWidget* cw = dynamic_cast<CableWidget*>(w); | ||||
@@ -1514,15 +1548,27 @@ std::vector<CableWidget*> RackWidget::getCompleteCables() { | |||||
return cws; | return cws; | ||||
} | } | ||||
std::vector<CableWidget*> RackWidget::getCablesOnPort(PortWidget* port) { | |||||
assert(port); | |||||
std::vector<CableWidget*> RackWidget::getIncompleteCables() { | |||||
std::vector<CableWidget*> cws; | std::vector<CableWidget*> cws; | ||||
for (widget::Widget* w : internal->cableContainer->children) { | for (widget::Widget* w : internal->cableContainer->children) { | ||||
CableWidget* cw = dynamic_cast<CableWidget*>(w); | CableWidget* cw = dynamic_cast<CableWidget*>(w); | ||||
assert(cw); | assert(cw); | ||||
if (cw->inputPort == port || cw->outputPort == port) { | |||||
if (!cw->isComplete()) | |||||
cws.push_back(cw); | |||||
} | |||||
return cws; | |||||
} | |||||
std::vector<CableWidget*> RackWidget::getCablesOnPort(PortWidget* port) { | |||||
assert(port); | |||||
std::vector<CableWidget*> cws; | |||||
for (widget::Widget* w : internal->plugContainer->children) { | |||||
PlugWidget* plug = dynamic_cast<PlugWidget*>(w); | |||||
assert(plug); | |||||
CableWidget* cw = plug->getCable(); | |||||
PortWidget* port2 = cw->getPort(plug->getType()); | |||||
if (port2 == port) | |||||
cws.push_back(cw); | cws.push_back(cw); | ||||
} | |||||
} | } | ||||
return cws; | return cws; | ||||
} | } | ||||
@@ -1530,14 +1576,15 @@ std::vector<CableWidget*> RackWidget::getCablesOnPort(PortWidget* port) { | |||||
std::vector<CableWidget*> RackWidget::getCompleteCablesOnPort(PortWidget* port) { | std::vector<CableWidget*> RackWidget::getCompleteCablesOnPort(PortWidget* port) { | ||||
assert(port); | assert(port); | ||||
std::vector<CableWidget*> cws; | std::vector<CableWidget*> cws; | ||||
for (widget::Widget* w : internal->cableContainer->children) { | |||||
CableWidget* cw = dynamic_cast<CableWidget*>(w); | |||||
assert(cw); | |||||
for (widget::Widget* w : internal->plugContainer->children) { | |||||
PlugWidget* plug = dynamic_cast<PlugWidget*>(w); | |||||
assert(plug); | |||||
CableWidget* cw = plug->getCable(); | |||||
if (!cw->isComplete()) | if (!cw->isComplete()) | ||||
continue; | continue; | ||||
if (cw->inputPort == port || cw->outputPort == port) { | |||||
PortWidget* port2 = cw->getPort(plug->getType()); | |||||
if (port2 == port) | |||||
cws.push_back(cw); | cws.push_back(cw); | ||||
} | |||||
} | } | ||||
return cws; | return cws; | ||||
} | } | ||||