Subversion Repositories Code-Repo

Rev

Blame | Last modification | View Log | RSS feed

#include "Wire.h"

Wire::Wire(QGraphicsItem *parent) :
    QGraphicsObject(parent)
{
    setFlags(ItemIsSelectable);

    createActions();

    this->auxSelected = false;
    this->showValues = true;

    // Create the default pens to draw with
    this->defaultPen = QPen(QColor(COLOR_MAIN), DEFAULT_LINE_WIDTH, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin);
    this->defaultBrush = QBrush(Qt::NoBrush);
    this->selectedPen = QPen(QColor(COLOR_MAIN_SELECTED), DEFAULT_LINE_WIDTH, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin);
    this->highlightedPen = QPen(QColor(COLOR_MAIN_SELECTED), DEFAULT_LINE_WIDTH, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin);
    this->defaultFont = QFont("Consolas", 20);

    this->wireValue = logicValue_X;
    this->wireFaultyValue = logicValue_X;
    this->valueString = "X/X";

#ifdef _DEBUG
    this->debugPen = QPen(QColor(COLOR_DEBUG), 1, Qt::DashLine);
    this->debugBrush = QBrush(Qt::NoBrush);
    this->debugSelectedPen = QPen(QColor(COLOR_DEBUG_SELECTED), 1, Qt::DashLine);
    this->debugErrorPen = QPen(QColor(COLOR_DEBUG_ERROR), 2, Qt::DashLine);
#endif
}

Wire::~Wire()
{

}

QRectF Wire::boundingRect() const
{
    QMargins margin(SELECTION_WIDTH, SELECTION_WIDTH, SELECTION_WIDTH, SELECTION_WIDTH);
    return bRect + margin;
}

QPainterPath Wire::shape() const
{
    return shapeArea;
}

void Wire::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
    Q_UNUSED(widget);
    Q_UNUSED(option);

#ifdef _DEBUG
    painter->save();
    painter->setPen((auxSelected) ? debugSelectedPen : debugPen);
    painter->setBrush(debugBrush);
    painter->drawRect(this->boundingRect());
    painter->drawPath(shapeArea);
    painter->restore();
#endif

    // Set line color accordingly
    if (auxSelected) painter->setPen(highlightedPen);
    else painter->setPen(defaultPen);
    painter->setBrush(defaultBrush);

    // Draw the line
    painter->drawPath(line);

    QPainterPath textBox;
    textBox.addText(0, 0, defaultFont, valueString);

    // Draw the text (wire value)
    if (showValues) {
        QPainterPath text;
        text.addText(bRect.center().x() - (textBox.boundingRect().width() / 2),
                     bRect.center().y() + (textBox.boundingRect().height() / 2) - 2,
                     defaultFont, valueString);

        QPainterPathStroker stroker;
        stroker.setCapStyle(Qt::RoundCap);
        stroker.setJoinStyle(Qt::RoundJoin);
        stroker.setWidth(10);
        QPainterPath textOutline = stroker.createStroke(text);
        painter->save();
        painter->setPen(QPen(Qt::white));
        painter->setBrush(QBrush(Qt::white));
        painter->drawPath(textOutline);
        painter->restore();

        painter->drawPath(text);
    }
}

/**
 * Draw line from given point to point
 */
void Wire::setPoints(Gate_BASE *source, Gate_BASE *sink, int sinkID)
{
    this->gateOutput = source;
    this->gateInput = sink;
    this->gateInputID = sinkID;

    // Calculate the bounding box of this line (normalized)
    bRect = QRectF(gateOutput->gateOutputPoint, gateInput->gateInputPoints[gateInputID]).normalized();

    // Move box to center of object
    QPointF center = bRect.center();
    bRect.translate(- center.x(), - center.y());

    // Calculate new I/O points in local coordinates
    QPointF tOutput = gateOutput->gateOutputPoint - center;
    QPointF tInput = gateInput->gateInputPoints[gateInputID] - center;

    // Draw the line
    linePoints.append(tOutput);
    linePoints.append(QPointF(bRect.center().x(), tOutput.y()));
    linePoints.append(QPointF(bRect.center().x(), tInput.y()));
    linePoints.append(tInput);
    line.moveTo(linePoints[0]);
    for (int i = 1; i < linePoints.size(); i++) {
        line.lineTo(linePoints[i]);
    }

    // Stroke the line to allow for selection
    QPainterPathStroker stroke;
    stroke.setWidth(SELECTION_WIDTH);
    shapeArea = stroke.createStroke(line);

    // Move wire on canvas to proper location
    this->setPos(center.x(), center.y());
    prepareGeometryChange();
}

/**
 * Sets the color of the pen drawing the line
 */
void Wire::setPenColor(QColor color)
{
    this->defaultPen.setColor(color);
}

/**
 * Sets the highlight color when wire is selected
 */
void Wire::setHighlight(bool state, QColor color)
{
    auxSelected = state;
    if (state) {
        highlightedPen.setColor(color);
        setZValue(SELECTED_Z);
    } else {
        setZValue(WIRE_DEFAULT_Z);
    }
    update();
}

/**
 * Resets the value of the wire to unknown 'X'
 */
void Wire::reset()
{
    setValue(logicValue_X, logicValue_X);
    update();
}

/**
 * Toggles if wire values should be shown
 */
void Wire::toggleShowValues()
{
    showValues = !showValues;
    update();
}

/**
 * Sets the value of the wire
 */
void Wire::setValue(logicValue value, logicValue faultyValue, bool recurse)
{
    wireValue = value;
    wireFaultyValue = faultyValue;

    // Compute the status string
    valueString = "";
    if (wireValue == logicValue_0) valueString += "0";
    else if (wireValue == logicValue_1) valueString += "1";
    else valueString += "X";
    valueString += "/";
    if (wireFaultyValue == logicValue_0) valueString += "0";
    else if (wireFaultyValue == logicValue_1) valueString += "1";
    else valueString += "X";

    // Propogate value to sink/source gates
    gateInput->setInputValue(gateInputID, wireValue, wireFaultyValue);
    gateInput->setOutputValue(wireValue, wireFaultyValue);

    // Propogate to gates if necessary
    if (recurse) {
        for (int i = 0; i < gateOutput->gateOutputWires.size(); i++) {
            gateOutput->gateOutputWires[i]->setValue(wireValue, wireFaultyValue, false);
        }
    }

    update();
}

void Wire::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
    QString status = QString("Wire Source: Gate %1 | Sink: Gate %2 | Value: ").arg(gateOutput->gateID).arg(gateInput->gateID);
    status += valueString;
    emit updateStatus(status);

    // On selection, highlight sink and source gates
    setZValue(1);
    setHighlight(true, QColor(COLOR_MAIN_SELECTED));
    gateOutput->setHighlight(true, QColor(COLOR_OUTPUT_SELECTED));
    gateInput->setHighlight(true, QColor(COLOR_INPUT_SELECTED));
    event->accept();
}

void Wire::contextMenuEvent(QGraphicsSceneContextMenuEvent *event)
{
    QMenu menu;
    menu.addAction(injectValueAction);
    menu.addAction(injectFaultAction);
    menu.addAction(resetValuesAction);
    menu.exec(event->screenPos());
    event->accept();
}

void Wire::createActions()
{
    injectValueAction = new QAction("&Set Value", this);
    connect(injectValueAction, SIGNAL(triggered()), this, SLOT(promptValue()));

    injectFaultAction = new QAction("&Inject Fault", this);
    connect(injectFaultAction, SIGNAL(triggered()), this, SLOT(promptFaultyValue()));

    resetValuesAction = new QAction("&Reset Values", this);
    connect(resetValuesAction, SIGNAL(triggered()), this, SLOT(reset()));
}

void Wire::promptValue()
{
    bool ok;
    QStringList options;
    options << "0" << "1";
    QString sel = QInputDialog::getItem(0, "Choose Value", "Enter Value:", options, 0, false, &ok);
    if (ok && !sel.isEmpty()) {
        if (sel == "0") setValue(logicValue_0, logicValue_0);
        else if (sel == "1") setValue(logicValue_1, logicValue_1);
        else setValue(logicValue_X, logicValue_X);
    }
}

void Wire::promptFaultyValue()
{
    bool ok;
    QStringList options;
    options << "0" << "1";
    QString sel = QInputDialog::getItem(0, "Choose Value", "Enter Faulty Value:", options, 0, false, &ok);
    if (ok && !sel.isEmpty()) {
        if (sel == "0") setValue(logicValue_1, logicValue_0);
        else if (sel == "1") setValue(logicValue_0, logicValue_1);
        else setValue(logicValue_X, logicValue_X);
    }
}