#include "./canvas.h"
#include <QMouseEvent>
#include <QPainter>
#include <QDebug>
#include <QElapsedTimer>
#include <QWheelEvent>
#include <QMenu>
#include <QStack>
#include "../core/util.h"
#include "../core/math.h"
#include "../core/symbol.h"
#include "../core/feature.h"
#include "./popviewwindow.h"


struct PopViewInfo {
    QRectF canvasRect;
    QRect popviewRect;
};

class PcbCamCanvasPrivate
{
    Q_DECLARE_PUBLIC(PcbCamCanvas)
public:
    explicit PcbCamCanvasPrivate(PcbCamCanvas *qptr)
        : q_ptr(qptr){}

    ~PcbCamCanvasPrivate(){}

    QRectF scaleRect(const QRectF &iRect, qreal iScale) {
        QPointF pc = iRect.center();
        qreal w = iRect.width() * iScale;
        qreal h = iRect.height() * iScale;
        return QRectF(pc.x()-w/2.0, pc.y()-h/2.0, w, h);
    }

    void pushDisplayHist(const QRectF &iRect) {
        if (!displayRectHist.isEmpty() && displayRectHist.last() == iRect) {
            return;
        }
        displayRectHist.push(iRect);
        if (displayRectHist.count() > 100) {
            displayRectHist.pop_front();
        }
    }


    PcbCamJob *jobData {nullptr};
    QMenu *contextMenu {nullptr};

    PcbCamCanvas::MouseMode mouseMode {PcbCamCanvas::ZoomAreaMode};

    PcbCam::UnitsType unitsType {PcbCam::Units_Inch};

    QRectF rubberBandRect;
    QRectF tmpRubberBandRect;
    QPolygonF polygonBand;

    int mouseClickTimes {0};

    QString currentStep;

    QMap<QString, QString> dispLayer;

    QRectF currentDisplayRect {0, 0, 20, 20};

    QTransform currentMatrix;
    QTransform orientMatrix;

    QColor bgColor {Qt::black};
    QColor rubberColor {Qt::white};
    QColor highlightColor {Qt::white};
    QColor selectionColor {Qt::white};
    QColor profileColor {Qt::white};
    QColor snapColor {"#CBCB41"};

    //QPixmap bufferPixmap;
    QMap<QString, QPixmap> pixBuffers;

    QStack<QRectF> displayRectHist;

    PcbCam::DisplayOptions displayOptions {PcbCam::DisplayFill|PcbCam::DisplayProfile|PcbCam::DisplayDatumn|PcbCam::DisplayTextValue};

    PcbCam::FeatureSelectionMode curFeatSelMode {PcbCam::ContainsFeatureShape};
    PcbCam::FeatureSelectionOperation curFeatSelOp {PcbCam::ReplaceFeatureSelection};


    bool panviewDrag {false};
    QPoint panviewStartPos;
    QRectF panviewStartRect;

    //绝对座标原点
    QPointF origPoint {0.0, 0.0};

    //相对坐标原点
    QPointF lastCoordPoint {0.0, 0.0};

    //鼠标坐标
    QPoint curMousePos;

    //是否绘制相对坐标
    bool drawRelativeOrig {false};

    //距离量测值
    QLineF measureLine;

    //CheckList高亮对象
    QVariantList checkMeasList;

    //PopView对象列表
    QList<PcbCamPopViewWindow*> popviewList;
    PcbCamPopViewWindow *currentMovePopView {nullptr};
    QRect currentMovePopViewOrigRect;

    // 高亮缓存
    QMap<qlonglong, PcbCamCanvas::FeatureInfo> highlightBuffer;
    qlonglong highlightIndex {0};

    // 间距测量对象缓存
    QList<PcbCamCanvas::FeatureInfo> spacingFeatBuffer;

    //单选缓存数据
    QMap<QString, QList<int> > lastSingleSelectMap;
    QMap<QString, int> lastSingleSelectIndex;

    //捕捉对象时的Key
    QString snapFeatRequestKey;

    // 上次鼠标指针样式
    QCursor lastCursor;

    // Feature过滤器
    PcbCam::FeatureSelectionFilter currentFeatureFilter;

    // 捕捉模式
    PcbCam::SnapModes snapMode {PcbCam::SnapOff};

    // 捕捉对象
    PcbCam::FeatureTypes snapFeatureType {PcbCam::AllFeatures};

    // 捕捉层
    QStringList snapLayers;

    // 捕捉点
    QPointF snapPoint;
    bool isSnapPointValid {false};

protected:
    PcbCamCanvas * const q_ptr;

};

PcbCamCanvas::PcbCamCanvas(QWidget *parent) : QWidget(parent),
    d_ptr(new PcbCamCanvasPrivate(this))
{
    this->setMouseTracking(true);
    this->setFocusPolicy(Qt::StrongFocus);
}

PcbCamCanvas::~PcbCamCanvas()
{

}

void PcbCamCanvas::fitInView(const QRectF &iRect, bool iSaveHist)
{
    Q_D(PcbCamCanvas);
    if (iSaveHist) {
        d->pushDisplayHist(d->currentDisplayRect);
    }
    d->currentDisplayRect = iRect;
    QRect wgtRect = this->rect();
    auto disprect = d->orientMatrix.mapRect(d->currentDisplayRect.normalized()).normalized();
    qreal scale = qMin(double(wgtRect.width())/disprect.width(),
                           double(wgtRect.height())/disprect.height());
    QTransform mat = d->orientMatrix * QTransform(scale, 0, 0, -scale, 0, 0);
    disprect = mat.mapRect(d->currentDisplayRect.normalized());
    d->currentMatrix = mat * QTransform(1, 0, 0, 1, wgtRect.center().x() - disprect.center().x(), wgtRect.center().y() - disprect.center().y());
    d->pixBuffers.clear();
    update();

}


void PcbCamCanvas::setCurrentStep(const QString &iStep)
{
    Q_D(PcbCamCanvas);
    d->currentStep = iStep;
    zoomHome();
}

QString PcbCamCanvas::currentStep() const
{
    Q_D(const PcbCamCanvas);
    return d->currentStep;
}

void PcbCamCanvas::zoomHome()
{
    Q_D(PcbCamCanvas);
    if (d->jobData == nullptr || !d->jobData->isStepExists(d->currentStep)) return;

    QRectF dispRect;
    for (const QString &layername : d->dispLayer.keys()) {
        if (!d->jobData->isStepLayerExists(d->currentStep, layername)) continue;
        PcbCamLayer *layer = d->jobData->layer(d->currentStep, layername);
        dispRect = dispRect.united(layer->boundingRect());
    }
    dispRect = dispRect.united(d->jobData->step(d->currentStep)->profile().boundingRect());

    if (dispRect.width() < 0.00001 || dispRect.height() < 0.00001) {
        dispRect = QRectF(0, 0, 10, 10);
    }
    fitInView(d->scaleRect(dispRect, 1.1));
}

void PcbCamCanvas::zoomIn()
{
    Q_D(PcbCamCanvas);
    fitInView(d->scaleRect(d->currentDisplayRect, 1.0/1.2));
}

void PcbCamCanvas::zoomOut()
{
    Q_D(PcbCamCanvas);
    fitInView(d->scaleRect(d->currentDisplayRect, 1.2));
}

void PcbCamCanvas::zoomPrevious()
{
    Q_D(PcbCamCanvas);
    if (!d->displayRectHist.isEmpty()) {
        fitInView(d->displayRectHist.pop(), false);
    }
}

void PcbCamCanvas::viewLeft(qreal iRate)
{
    Q_D(PcbCamCanvas);
    QRectF rect = d->orientMatrix.mapRect(d->currentDisplayRect).normalized();
    rect.moveRight(rect.right() + rect.width()*iRate);
    rect = d->orientMatrix.inverted().mapRect(rect).normalized();
    fitInView(rect);
}

void PcbCamCanvas::viewRight(qreal iRate)
{
    Q_D(PcbCamCanvas);
    QRectF rect = d->orientMatrix.mapRect(d->currentDisplayRect).normalized();
    rect.moveLeft(rect.left() - rect.width()*iRate);
    rect = d->orientMatrix.inverted().mapRect(rect).normalized();
    fitInView(rect);
}

void PcbCamCanvas::viewUp(qreal iRate)
{
    Q_D(PcbCamCanvas);
    QRectF rect = d->orientMatrix.mapRect(d->currentDisplayRect).normalized();
    rect.moveBottom(rect.bottom() - rect.height()*iRate);
    rect = d->orientMatrix.inverted().mapRect(rect).normalized();
    fitInView(rect);
}

void PcbCamCanvas::viewDown(qreal iRate)
{
    Q_D(PcbCamCanvas);
    QRectF rect = d->orientMatrix.mapRect(d->currentDisplayRect).normalized();
    rect.moveTop(rect.top() + rect.height()*iRate);
    rect = d->orientMatrix.inverted().mapRect(rect).normalized();
    fitInView(rect);
}

void PcbCamCanvas::setOrientMatrix(const QTransform &iMatrix)
{
    Q_D(PcbCamCanvas);
    d->orientMatrix = iMatrix;
    auto rect = d->currentDisplayRect.normalized();
    qreal size = qMax(rect.width(), rect.height());
    this->fitInView(QRectF(rect.center().x() - size/2.0, rect.center().y() - size/2.0, size, size), false);
}

QTransform PcbCamCanvas::orientMatrix() const
{
    Q_D(const PcbCamCanvas);
    return d->orientMatrix;
}



QTransform PcbCamCanvas::currentMatrix() const
{
    Q_D(const PcbCamCanvas);
    return d->currentMatrix;
}

void PcbCamCanvas::setUnitsType(PcbCam::UnitsType iUnits)
{
    Q_D(PcbCamCanvas);
    d->unitsType = iUnits;
}

PcbCam::UnitsType PcbCamCanvas::unitsType() const
{
    Q_D(const PcbCamCanvas);
    return d->unitsType;
}

void PcbCamCanvas::display(const QString &iLayer, const QString &iColor)
{
    Q_D(PcbCamCanvas);
    if (iColor.isEmpty()) {
        d->dispLayer.remove(iLayer);
    }
    else {
        d->dispLayer.insert(iLayer, iColor);
        if (d->jobData != nullptr) d->jobData->loadStepLayer(d->currentStep, iLayer);
    }
    d->pixBuffers.remove(d->currentStep +"/SELECTION");
    d->pixBuffers.remove("ALL");
    update();
}

void PcbCamCanvas::resetDisplayLayers(const QMap<QString, QString> &iDispInfo)
{
    Q_D(PcbCamCanvas);
    d->dispLayer.clear();
    if (d->jobData != nullptr) {
        d->jobData->clearStepDisplayLayer(d->currentStep);
    }
    for (auto layer : iDispInfo.keys()) {
        auto color = iDispInfo.value(layer);
        if (!color.isEmpty()) {
            d->dispLayer.insert(layer, color);
            if (d->jobData != nullptr) {
                d->jobData->loadStepLayer(d->currentStep, layer);
                auto joblayer = d->jobData->layer(d->currentStep, layer);
                if (joblayer != nullptr) {
                    joblayer->setDisplayColor(color);
                }
            }
        }
    }
    d->pixBuffers.remove(d->currentStep +"/SELECTION");
    d->pixBuffers.remove("ALL");
    update();
}



void PcbCamCanvas::clearDisplay()
{
    Q_D(PcbCamCanvas);
    d->dispLayer.clear();
    d->pixBuffers.remove("ALL");
    d->highlightBuffer.clear();
    d->displayRectHist.clear();
    d->checkMeasList.clear();
    d->currentStep.clear();
    d->mouseClickTimes = 0;
    d->rubberBandRect = QRect();
    d->tmpRubberBandRect = QRect();
    update();
}

void PcbCamCanvas::refresh()
{
    Q_D(PcbCamCanvas);
    d->pixBuffers.clear();
    update();
}


void PcbCamCanvas::setBgColor(const QString &iBgColor)
{
    Q_D(PcbCamCanvas);
    d->bgColor = QColor(iBgColor);
}

QString PcbCamCanvas::bgColor() const
{
    Q_D(const PcbCamCanvas);
    return d->bgColor.name();
}

void PcbCamCanvas::setRubberColor(const QString &iColor)
{
    Q_D(PcbCamCanvas);
    d->rubberColor = QColor(iColor);
}

QString PcbCamCanvas::rubberColor() const
{
    Q_D(const PcbCamCanvas);
    return d->rubberColor.name();
}

void PcbCamCanvas::setHighlightColor(const QString &iColor)
{
    Q_D(PcbCamCanvas);
    d->highlightColor = QColor(iColor);
}

QString PcbCamCanvas::highlightColor() const
{
    Q_D(const PcbCamCanvas);
    return d->highlightColor.name();
}

void PcbCamCanvas::setSelectionColor(const QString &iColor)
{
    Q_D(PcbCamCanvas);
    d->selectionColor = QColor(iColor);
}

QString PcbCamCanvas::selectionColor() const
{
    Q_D(const PcbCamCanvas);
    return d->selectionColor.name();
}

void PcbCamCanvas::setProfileColor(const QString &iColor)
{
    Q_D(PcbCamCanvas);
    d->profileColor = QColor(iColor);
}

QString PcbCamCanvas::profileColor() const
{
    Q_D(const PcbCamCanvas);
    return d->profileColor.name();
}

QPointF PcbCamCanvas::mapToScene(const QPoint &iPoint) const
{
    Q_D(const PcbCamCanvas);
    return d->currentMatrix.inverted().map(QPointF(iPoint));
}

QRectF PcbCamCanvas::mapToScene(const QRect &iRect) const
{
    Q_D(const PcbCamCanvas);
    return d->currentMatrix.inverted().mapRect(QRectF(iRect));
}

void PcbCamCanvas::setMouseMode(const PcbCamCanvas::MouseMode iMouseMode)
{
    Q_D(PcbCamCanvas);
    d->mouseMode = iMouseMode;
    if (d->mouseMode == PcbCamCanvas::PanViewMode) {
        this->setCursor(QCursor(Qt::OpenHandCursor));
    }
    else if (d->mouseMode == PcbCamCanvas::SnapFeatMode) {
        this->setCursor(QCursor(Qt::PointingHandCursor));
    }
    else {
        this->setCursor(QCursor(Qt::ArrowCursor));
    }
    d->mouseClickTimes = 0;
    d->rubberBandRect = QRect();
    d->tmpRubberBandRect = QRect();
    d->polygonBand.clear();
    //d->spacingFeatBuffer.clear();
    //d->measureLine = QLineF(0.0, 0.0, 0.0, 0.0);
    update();
}

PcbCamCanvas::MouseMode PcbCamCanvas::mouseMode() const
{
    Q_D(const PcbCamCanvas);
    return d->mouseMode;
}

QList<PcbCamCanvas::FeatureInfo> PcbCamCanvas::getFeaturesAt(const QPoint &iPoint, LayerScope iLayerScope, int iTolPix) const
{
    Q_D(const PcbCamCanvas);
    QList<PcbCamCanvas::FeatureInfo> res;
    if (d->jobData == nullptr || d->currentStep.isEmpty()) return res;
    QList<PcbCamLayer*> layers;
    if (iLayerScope & WorkLayer) {
        auto layer = d->jobData->stepWorkLayer(d->currentStep);
        if (layer != nullptr) layers << layer;
    }
    if (iLayerScope & AffectedLayer) {
        for (auto layer : d->jobData->stepAffectedLayer(d->currentStep)) {
            if (!layers.contains(layer)) layers << layer;
        }
    }
    if (iLayerScope & DisplayLayer) {
        for (auto layer: d->jobData->stepDispalyLayer(d->currentStep)) {
            if (!layers.contains(layer)) layers << layer;
        }
    }

    auto step = d->jobData->step(d->currentStep);
    QRectF rect = mapToScene(QRect(iPoint.x() - iTolPix, iPoint.y() - iTolPix, 2 * iTolPix, 2 * iTolPix));
    QPainterPath rectpath;
    rectpath.addRect(rect);
    for (auto layer: layers) {
        for (auto feat : layer->selectFeature(rectpath, PcbCam::FeatureSelectionFilter(), PcbCam::NoFeatureSelection, PcbCam::IntersectsFeatureShape)) {
            FeatureInfo featinfo;
            featinfo.feature = feat;
            featinfo.layer = layer;
            featinfo.step = step;
            res << featinfo;
        }
    }
    return res;
}

PcbCamCanvas::FeatureInfo PcbCamCanvas::getFirstFeatureAt(const QPoint &iPoint, PcbCamCanvas::LayerScope iLayerScope, int iTolPix) const
{
    Q_D(const PcbCamCanvas);
    PcbCamCanvas::FeatureInfo res;
    if (d->jobData == nullptr || d->currentStep.isEmpty()) return res;
    QList<PcbCamLayer*> layers;
    if (iLayerScope & WorkLayer) {
        auto layer = d->jobData->stepWorkLayer(d->currentStep);
        if (layer != nullptr) layers << layer;
    }
    if (iLayerScope & AffectedLayer) {
        for (auto layer : d->jobData->stepAffectedLayer(d->currentStep)) {
            if (!layers.contains(layer)) layers << layer;
        }
    }
    if (iLayerScope & DisplayLayer) {
        for (auto layer: d->jobData->stepDispalyLayer(d->currentStep)) {
            if (!layers.contains(layer)) layers << layer;
        }
    }
    auto step = d->jobData->step(d->currentStep);
    QRectF rect = mapToScene(QRect(iPoint.x() - iTolPix, iPoint.y() - iTolPix, 2 * iTolPix, 2 * iTolPix));
    QPainterPath rectpath;
    rectpath.addRect(rect);
    for (auto layer: layers) {
        auto feats = layer->selectFeature(rectpath, PcbCam::FeatureSelectionFilter(), PcbCam::NoFeatureSelection, PcbCam::IntersectsFeatureShape);
        if (!feats.isEmpty()) {
            res.feature = feats.first();
            res.step = step;
            res.layer = layer;
        }
    }
    return res;
}


QVariantMap PcbCamCanvas::snapFeatInfo(const QPoint &iPoint, int iTolPix)
{
    Q_D(const PcbCamCanvas);
    if (d->jobData == nullptr) return QVariantMap();
    QVariantMap res;
    auto curfeatinfo = getFirstFeatureAt(iPoint, DisplayLayer, iTolPix);
    if (curfeatinfo.feature != nullptr) {
        auto curfeat = curfeatinfo.feature;
        res.insert("request_key", d->snapFeatRequestKey);
        res.insert("polarity", curfeat->polarity() == PcbCam::Negative ? "NEG" : "POS");
        res.insert("attrs", curfeat->attr());
        res.insert("step", curfeatinfo.step->name());
        res.insert("layer", curfeatinfo.layer->name());
        res.insert("index", curfeat->index());
        if (curfeat->type() == PcbCam::PadFeature) {
            const auto feat = static_cast<const PcbCamFeaturePad *>(curfeat);
            res.insert("type", "Pad");
            res.insert("x", feat->pos().x());
            res.insert("y", feat->pos().y());
            res.insert("symbol", feat->symbol()->name());
            res.insert("angle", PcbCamUtil::orient2angle(feat->orient()));
            res.insert("mirror", PcbCamUtil::orient2mirror(feat->orient()));
        }
        else if (curfeat->type() == PcbCam::LineFeature) {
            const auto feat = static_cast<const PcbCamFeatureLine *>(curfeat);
            res.insert("type", "Line");
            res.insert("xs", feat->line().x1());
            res.insert("ys", feat->line().y1());
            res.insert("xe", feat->line().x2());
            res.insert("ye", feat->line().y2());
            res.insert("symbol", feat->symbol()->name());
        }
        else if (curfeat->type() == PcbCam::ArcFeature) {
            const auto feat = static_cast<const PcbCamFeatureArc *>(curfeat);
            res.insert("type", "Arc");
            res.insert("xc", feat->pc().x());
            res.insert("yc", feat->pc().y());
            res.insert("xs", feat->ps().x());
            res.insert("ys", feat->ps().y());
            res.insert("xe", feat->pe().x());
            res.insert("ye", feat->pe().y());
            res.insert("cw", feat->cw());
            res.insert("symbol", feat->symbol()->name());
        }
        else if (curfeat->type() == PcbCam::SurfaceFeature) {
            const auto feat = static_cast<const PcbCamFeatureSurface *>(curfeat);
            res.insert("type", "Surface");
            auto pc = feat->boundingRect().center();
            res.insert("xc", pc.x());
            res.insert("yc", pc.y());
        }
        else if (curfeat->type() == PcbCam::TextFeature) {
            const auto feat = static_cast<const PcbCamFeatureText *>(curfeat);
            res.insert("type", "Text");
            res.insert("x", feat->pos().x());
            res.insert("y", feat->pos().y());
            res.insert("font", feat->font()->name());
            res.insert("text", feat->text());
            res.insert("angle", PcbCamUtil::orient2angle(feat->orient()));
            res.insert("mirror", PcbCamUtil::orient2mirror(feat->orient()));
        }
        emit snapFeatChanged(QVariant::fromValue(res));
    }
    return res;
}

void PcbCamCanvas::setSnapFeatRequestKey(const QString &iKey)
{
    Q_D(PcbCamCanvas);
    d->snapFeatRequestKey = iKey;
}



void PcbCamCanvas::singleClickSelection(const QPoint &iPoint, PcbCam::FeatureSelectionOperation iSelOp)
{
    Q_D(PcbCamCanvas);
    if (d->jobData == nullptr || d->currentStep.isEmpty()) return;
    int tol = 2;
    QRectF rect = mapToScene(QRect(iPoint.x() - tol, iPoint.y() - tol, 2 * tol, 2 * tol));
    QPainterPath rectpath;
    rectpath.addRect(rect);
    QList<PcbCamLayer *> affected_layers = d->jobData->stepAffectedLayer(d->currentStep);
    PcbCamLayer *worklayer = d->jobData->stepWorkLayer(d->currentStep);
    if (worklayer != nullptr) affected_layers.prepend(worklayer);

    PcbCamFeature *curfeat {nullptr};
    QString curfeat_layer;
    int selfeat_count {0};
    if (iSelOp == PcbCam::RemoveFeatureSelection) {
        for (PcbCamLayer *layer : affected_layers) {
            auto feats = layer->selectFeature(rectpath, PcbCam::FeatureSelectionFilter(), PcbCam::NoFeatureSelection, PcbCam::IntersectsFeatureShape);
            for (auto feat : feats) {
                if (feat->isSelected()) {
                    feat->setSelected(false);
                    break;
                }
            }
        }
    }
    else {
        for (PcbCamLayer *layer : affected_layers) {
            if (iSelOp == PcbCam::ReplaceFeatureSelection) {
                layer->clearSelection();
            }
            auto feats = layer->selectFeature(rectpath, PcbCam::FeatureSelectionFilter(), PcbCam::NoFeatureSelection, PcbCam::IntersectsFeatureShape);
            if (feats.isEmpty()) continue;

            QList<int> selindex;
            for (auto feat : feats) {
                selindex.append(feat->index());
            }

            QList<int> lastindex = d->lastSingleSelectMap.value(layer->name());
            int curindex;
            if (selindex == lastindex) {
                curindex = (d->lastSingleSelectIndex.value(layer->name(), -1) + 1) % selindex.count();
            }
            else {
                d->lastSingleSelectMap.insert(layer->name(), selindex);
                curindex = 0;
            }
            d->lastSingleSelectIndex.insert(layer->name(), curindex);
            feats[curindex]->setSelected(true);
            curfeat = feats[curindex];
            curfeat_layer = layer->name();
            selfeat_count++;
        }
    }

    if (selfeat_count == 1) {
        QVariantMap msginfo;
        msginfo.insert("INFO_TYPE", "FEATURE");
        msginfo.insert("polarity", curfeat->polarity() == PcbCam::Negative ? "NEG" : "POS");
        msginfo.insert("attrs", curfeat->attr());
        msginfo.insert("layer", curfeat_layer);
        msginfo.insert("index", curfeat->index());
        if (curfeat->type() == PcbCam::PadFeature) {
            const auto feat = static_cast<const PcbCamFeaturePad *>(curfeat);
            msginfo.insert("type", "Pad");
            msginfo.insert("x", feat->pos().x());
            msginfo.insert("y", feat->pos().y());
            msginfo.insert("symbol", feat->symbol()->name());
            msginfo.insert("angle", PcbCamUtil::orient2angle(feat->orient()));
            msginfo.insert("mirror", PcbCamUtil::orient2mirror(feat->orient()));
        }
        else if (curfeat->type() == PcbCam::LineFeature) {
            const auto feat = static_cast<const PcbCamFeatureLine *>(curfeat);
            msginfo.insert("type", "Line");
            msginfo.insert("xs", feat->line().x1());
            msginfo.insert("ys", feat->line().y1());
            msginfo.insert("xe", feat->line().x2());
            msginfo.insert("ye", feat->line().y2());
            msginfo.insert("symbol", feat->symbol()->name());
        }
        else if (curfeat->type() == PcbCam::ArcFeature) {
            const auto feat = static_cast<const PcbCamFeatureArc *>(curfeat);
            msginfo.insert("type", "Arc");
            msginfo.insert("xc", feat->pc().x());
            msginfo.insert("yc", feat->pc().y());
            msginfo.insert("xs", feat->ps().x());
            msginfo.insert("ys", feat->ps().y());
            msginfo.insert("xe", feat->pe().x());
            msginfo.insert("ye", feat->pe().y());
            msginfo.insert("cw", feat->cw());
            msginfo.insert("symbol", feat->symbol()->name());
        }
        else if (curfeat->type() == PcbCam::SurfaceFeature) {
            const auto feat = static_cast<const PcbCamFeatureSurface *>(curfeat);
            msginfo.insert("type", "Surface");
            auto pc = feat->boundingRect().center();
            msginfo.insert("xc", pc.x());
            msginfo.insert("yc", pc.y());
        }
        else if (curfeat->type() == PcbCam::TextFeature) {
            const auto feat = static_cast<const PcbCamFeatureText *>(curfeat);
            msginfo.insert("type", "Text");
            msginfo.insert("x", feat->pos().x());
            msginfo.insert("y", feat->pos().y());
            msginfo.insert("font", feat->font()->name());
            msginfo.insert("text", feat->text());
            msginfo.insert("angle", PcbCamUtil::orient2angle(feat->orient()));
            msginfo.insert("mirror", PcbCamUtil::orient2mirror(feat->orient()));
        }

        emit messageNotified(msginfo);

    }


    d->pixBuffers.remove(d->currentStep +"/SELECTION");
    d->pixBuffers.remove("ALL");
    emit selectionChanged();
    update();
}

void PcbCamCanvas::doubleClickSelection(const QPoint &iPoint, PcbCam::FeatureSelectionOperation iSelOp)
{
    Q_D(PcbCamCanvas);
    if (d->jobData == nullptr || d->currentStep.isEmpty()) return;

    int tol = 2;
    QRectF rect = mapToScene(QRect(iPoint.x() - tol, iPoint.y() - tol, 2 * tol, 2 * tol));
    QPainterPath rectpath;
    rectpath.addRect(rect);
    QList<PcbCamLayer *> affected_layers = d->jobData->stepAffectedLayer(d->currentStep);
    PcbCamLayer *worklayer = d->jobData->stepWorkLayer(d->currentStep);
    if (worklayer != nullptr) affected_layers.prepend(worklayer);

    PcbCamFeature *curfeat {nullptr};
    PcbCamFeature *firstfeat {nullptr};
    for (PcbCamLayer *layer : affected_layers) {
        auto feats = layer->selectFeature(rectpath, PcbCam::FeatureSelectionFilter(), PcbCam::NoFeatureSelection, PcbCam::IntersectsFeatureShape);
        if (!feats.isEmpty()) {
            if (firstfeat == nullptr) firstfeat = feats.first();
            for (auto f : feats) {
                if (f->isSelected()) {
                    curfeat = f;
                    break;
                }
            }
            if (curfeat != nullptr) break;
        }
    }
    if (curfeat == nullptr) curfeat = firstfeat;

    //清除之前的选择
    if (iSelOp == PcbCam::ReplaceFeatureSelection) {
        for (PcbCamLayer *layer : affected_layers) {
            layer->clearSelection();
        }
    }

    if (curfeat != nullptr) {
        auto curtype = curfeat->type();
        auto cursym = curfeat->symbol();
        int count = 0;
        for (auto layer : affected_layers) {
            for (auto feat : layer->features()) {
                if (feat->type() != curtype) continue;
                if ((feat->type() == PcbCam::LineFeature || feat->type() == PcbCam::PadFeature || feat->type() == PcbCam::ArcFeature)
                        && feat->symbol() != cursym) continue;
                feat->setSelected(true);
                count++;
            }
        }

        QVariantMap msginfo;
        msginfo.insert("INFO_TYPE", "DBCLICK_SELECT");
        msginfo.insert("count", count);

        if (curtype == PcbCam::LineFeature) {
            msginfo.insert("type", "Line");
            msginfo.insert("symbol", cursym->name());
        }
        else if (curtype == PcbCam::PadFeature) {
            msginfo.insert("type", "Pad");
            msginfo.insert("symbol", cursym->name());
        }
        else if (curtype == PcbCam::ArcFeature) {
            msginfo.insert("type", "Arc");
            msginfo.insert("symbol", cursym->name());
        }
        else if (curtype == PcbCam::SurfaceFeature) {
            msginfo.insert("type", "Surface");
        }
        else if (curtype == PcbCam::TextFeature) {
            msginfo.insert("type", "Text");
        }

        emit messageNotified(msginfo);

    }

    d->pixBuffers.remove(d->currentStep +"/SELECTION");
    d->pixBuffers.remove("ALL");
    emit selectionChanged();
    update();

}

QList<PcbCamFeature *> PcbCamCanvas::selectFeature(const QStringList &iLayers,
                                                   const QPainterPath &iPath,
                                                   const PcbCam::FeatureSelectionFilter &iFilter,
                                                   PcbCam::FeatureSelectionOperation iSelOp,
                                                   PcbCam::FeatureSelectionMode iMode)
{
    Q_D(PcbCamCanvas);
    QList<PcbCamFeature *> res;
    if (d->jobData == nullptr || d->currentStep.isEmpty()) return res;
    auto affected_layers = d->jobData->stepAffectedLayer(d->currentStep);
    auto worklayer = d->jobData->stepWorkLayer(d->currentStep);
    if (worklayer != nullptr) affected_layers.prepend(worklayer);

    for (auto layer : affected_layers) {
        if (iSelOp == PcbCam::ReplaceFeatureSelection) {
            layer->clearSelection();
        }
        if (iLayers.isEmpty() || iLayers.contains(layer->name())) {
            res.append(layer->selectFeature(iPath, iFilter, iSelOp, iMode));
        }
    }

    if (iSelOp != PcbCam::NoFeatureSelection && iSelOp != PcbCam::RemoveFeatureSelection) {
        QVariantMap msginfo;
        msginfo.insert("INFO_TYPE", "REGION_SELECT");
        msginfo.insert("count", res.count());
        emit messageNotified(msginfo);
    }

    d->pixBuffers.remove(d->currentStep +"/SELECTION");
    d->pixBuffers.remove("ALL");
    emit selectionChanged();
    update();
    return res;
}

QList<PcbCamFeature *> PcbCamCanvas::selectedFeatures() const
{
    Q_D(const PcbCamCanvas);
    QList<PcbCamFeature *> ret;
    QList<PcbCamLayer *> affected_layers = d->jobData->stepAffectedLayer(d->currentStep);
    PcbCamLayer *worklayer = d->jobData->stepWorkLayer(d->currentStep);
    if (worklayer != nullptr) affected_layers.prepend(worklayer);
    for (auto layer: affected_layers) {
        ret << layer->selectedFeatures();
    }
    return ret;
}

void PcbCamCanvas::layerNetSelection(const QStringList &iLayers, const QPoint &iPoint, PcbCam::FeatureSelectionOperation iSelOp, const PcbCam::FeatureSelectionFilter &iFilter)
{
    Q_D(PcbCamCanvas);
    if (d->jobData == nullptr || d->currentStep.isEmpty()) return;
    int tol = 4;
    QRectF rect = mapToScene(QRect(iPoint.x() - tol, iPoint.y() - tol, 2 * tol, 2 * tol));
    QPainterPath selpath;
    selpath.addRect(rect);
    QList<PcbCamLayer *> affected_layers = d->jobData->stepAffectedLayer(d->currentStep);
    PcbCamLayer *worklayer = d->jobData->stepWorkLayer(d->currentStep);
    if (worklayer != nullptr) affected_layers.prepend(worklayer);

    auto filter = iFilter;
    //filter.polarity = PcbCam::Positive;
    for (auto layer: affected_layers) {
        if (iSelOp == PcbCam::ReplaceFeatureSelection) {
            layer->clearSelection();
        }
        if (!iLayers.isEmpty() && !iLayers.contains(layer->name())) {
            continue;
        }

        QSet<PcbCamFeature*> selectedFeatures;
        while (!selpath.isEmpty()) {
            QList<PcbCamFeature*> selfeats;
            QPainterPath tmppath;
            for (auto f : layer->selectFeature(selpath, filter, PcbCam::NoFeatureSelection, PcbCam::IntersectsFeatureShape)){
                if (!selectedFeatures.contains(f)) {
                    if (f->polarity() == PcbCam::Positive) {
                        tmppath = tmppath.united(f->shape());
                    }
                    else {
                        tmppath = tmppath.subtracted(f->shape());
                    }
                    if (iSelOp == PcbCam::RemoveFeatureSelection) {
                        f->setSelected(false);
                    }
                    else if (f->polarity() == PcbCam::Positive) {
                        f->setSelected(true);
                    }
                    selectedFeatures.insert(f);
                    // 第一个对象只能是鼠标下的一个Feature,不然会同时选中多个net
                    if (selectedFeatures.count() == 1) {
                        break;
                    }
                }
            }
            selpath = tmppath;
        }
    }
    d->pixBuffers.remove(d->currentStep +"/SELECTION");
    d->pixBuffers.remove("ALL");
    emit selectionChanged();
    update();
}

void PcbCamCanvas::highlightFeature(const QString &iStep,
                                    const QStringList &iLayers,
                                    const PcbCam::FeatureSelectionFilter &iFilter,
                                    PcbCam::FeatureSelectionOperation iSelOp,
                                    const QTransform &iMatrix,
                                    bool iStepRepeat)
{
    Q_D(PcbCamCanvas);
    if (d->jobData == nullptr) return;
    auto step = d->jobData->step(iStep);
    if (step == nullptr) return;

    auto affected_layers = d->jobData->stepAffectedLayer(iStep);
    auto worklayer = d->jobData->stepWorkLayer(iStep);
    if (worklayer != nullptr) affected_layers.prepend(worklayer);

    QMap<QString, PcbCamLayer*> layerMap;
    if (!iLayers.isEmpty()) {
        for (auto ln : iLayers) {
            auto layer = d->jobData->layer(iStep, ln);
            if (layer != nullptr)  layerMap.insert(ln, layer);
        }
    }
    else {
        for (auto layer : affected_layers) {
            layerMap.insert(layer->name(), layer);
        }
    }

    if (iSelOp == PcbCam::ReplaceFeatureSelection) {
        d->highlightBuffer.clear();
    }


    auto stepProfile = step->profile();
    bool isFilterProfile = (iFilter.profile != PcbCam::AllProfile && !stepProfile.isEmpty());
    bool isFilterInclueSym = !iFilter.includeSymbols.isEmpty();
    bool isFilterExcludeSym = !iFilter.excludeSymbols.isEmpty();
    bool isFilterIncludeAttr = !iFilter.includeAttributes.isEmpty();
    bool isFilterExcludeAttr = !iFilter.excludeAttributes.isEmpty();

    QSet<PcbCamSymbol*> includeSymbols;
    for (auto symname : iFilter.includeSymbols) {
        auto symbol = d->jobData->symbol(symname);
        if (symbol != nullptr) {
            includeSymbols.insert(symbol);
        }
    }

    QSet<PcbCamSymbol*> excludeSymbols;
    for (auto symname : iFilter.excludeSymbols) {
        auto symbol = d->jobData->symbol(symname);
        if (symbol != nullptr) {
            excludeSymbols.insert(symbol);
        }
    }

    QList<PcbCam::AttrCompareInfo> includeAttrs;
    for (auto attr : iFilter.includeAttributes) {
        includeAttrs.append(PcbCam::AttrCompareInfo(attr));
    }

    QList<PcbCam::AttrCompareInfo> excludeAttrs;
    for (auto attr : iFilter.excludeAttributes) {
        excludeAttrs.append(PcbCam::AttrCompareInfo(attr));
    }

    if (iSelOp == PcbCam::RemoveFeatureSelection) {

        for (auto k : d->highlightBuffer.keys()) {
            auto hinfo = d->highlightBuffer[k];
            if (layerMap.contains(hinfo.layer->name())) {
                bool match = PcbCamUtil::isFeatMatchFilter(hinfo.feature,
                                                           iFilter.type,
                                                           iFilter.polarity,
                                                           isFilterProfile,
                                                           iFilter.profile,
                                                           stepProfile,
                                                           isFilterInclueSym,
                                                           includeSymbols,
                                                           isFilterExcludeSym,
                                                           excludeSymbols,
                                                           isFilterIncludeAttr,
                                                           includeAttrs,
                                                           iFilter.includeAttributesLogic,
                                                           isFilterExcludeAttr,
                                                           excludeAttrs,
                                                           iFilter.excludeAttributesLogic
                                                           );

                if (match) d->highlightBuffer.remove(k);
            }
        }
    }
    else {
        for (auto layer : layerMap.values()) {
            for (auto feat : layer->features()) {
                bool match = PcbCamUtil::isFeatMatchFilter(feat,
                                                           iFilter.type,
                                                           iFilter.polarity,
                                                           isFilterProfile,
                                                           iFilter.profile,
                                                           stepProfile,
                                                           isFilterInclueSym,
                                                           includeSymbols,
                                                           isFilterExcludeSym,
                                                           excludeSymbols,
                                                           isFilterIncludeAttr,
                                                           includeAttrs,
                                                           iFilter.includeAttributesLogic,
                                                           isFilterExcludeAttr,
                                                           excludeAttrs,
                                                           iFilter.excludeAttributesLogic
                                                           );
                if (match) {
                    FeatureInfo hinfo;
                    hinfo.layer = layer;
                    hinfo.step = step;
                    hinfo.feature = feat;
                    hinfo.matrix = iMatrix;
                    d->highlightBuffer.insert(d->highlightIndex++, hinfo);
                }
            }
        }
        if (iStepRepeat) {
            for (auto sr : step->getBreakedRepeats()) {
                auto srstep = d->jobData->step(sr.step());
                QTransform srmat =  PcbCamUtil::matrix(QPointF(sr.x(), sr.y()), srstep->datum(), sr.angle(), sr.mirror()) * iMatrix;
                highlightFeature(srstep->name(), iLayers, iFilter, iSelOp, srmat, iStepRepeat);
            }
        }
    }

    d->pixBuffers.remove(d->currentStep +"/HIGHLIGHT");
    d->pixBuffers.remove("ALL");
    update();
}

void PcbCamCanvas::clearSelection()
{
    Q_D(PcbCamCanvas);
    if (d->jobData == nullptr) return;
    QList<PcbCamLayer *> affected_layers = d->jobData->stepAffectedLayer(d->currentStep);
    PcbCamLayer *worklayer = d->jobData->stepWorkLayer(d->currentStep);
    if (worklayer != nullptr) affected_layers.prepend(worklayer);
    for (PcbCamLayer *layer : affected_layers) {
        layer->clearSelection();
    }

    d->pixBuffers.remove(d->currentStep + "/SELECTION");
    d->pixBuffers.remove("ALL");
    update();
}

void PcbCamCanvas::clearHighlight()
{
    Q_D(PcbCamCanvas);
    d->highlightBuffer.clear();
    d->spacingFeatBuffer.clear();
    d->measureLine = QLineF(0.0, 0.0, 0.0, 0.0);
    d->pixBuffers.remove(d->currentStep + "/HIGHLIGHT");
    d->pixBuffers.remove("ALL");
    update();
}


void PcbCamCanvas::setCheckMeasList(const QVariantList &iData)
{
    Q_D(PcbCamCanvas);
    d->checkMeasList = iData;
    d->pixBuffers.remove(d->currentStep+"/CHECKMEAS");
    d->pixBuffers.remove("ALL");
    update();
}

void PcbCamCanvas::clearPopView()
{
    Q_D(PcbCamCanvas);
    for (auto popview : d->popviewList) {
        popview->deleteLater();
    }
    d->popviewList.clear();
}

void PcbCamCanvas::onPopViewClosed()
{
    Q_D(PcbCamCanvas);
    PcbCamPopViewWindow *popview = qobject_cast<PcbCamPopViewWindow*>(sender());
    auto idx = d->popviewList.indexOf(popview);
    if (idx != -1) {
        d->popviewList.removeAt(idx);
    }
    popview->deleteLater();
    update();
}

void PcbCamCanvas::doSpacingMeasure()
{
    Q_D(PcbCamCanvas);
    if (d->spacingFeatBuffer.count() < 2) return;
    auto feat1 = d->spacingFeatBuffer.value(0).feature;
    auto feat2 = d->spacingFeatBuffer.value(1).feature;
    auto shape1 = feat1->shape();
    auto shape2 = feat2->shape();
    if (shape1.intersects(shape2)) {
        d->measureLine = QLineF(0.0, 0.0, 0.0, 0.0);
    }
    else {
        QTransform mat;
        mat.scale(10000, 10000);
        double dist = LONG_MAX;
        auto polys1 = mat.map(shape1).toSubpathPolygons();
        auto polys2 = mat.map(shape2).toSubpathPolygons();
        double tmpdist;
        QLineF spacingLine;
        QPointF tmpSnapPoint;
        for (auto poly1 : polys1) {
           for (auto poly2 : polys2) {
               for (auto pt : poly2) {
                   tmpdist = PcbCamMath::point2polylineDist(pt, poly1, tmpSnapPoint);
                   if (tmpdist < dist) {
                       dist = tmpdist;
                       spacingLine.setP1(pt);
                       spacingLine.setP2(tmpSnapPoint);
                   }
               }
           }
        }
        for (auto poly2 : polys2) {
           for (auto poly1 : polys1) {
               for (auto pt : poly1) {
                   tmpdist = PcbCamMath::point2polylineDist(pt, poly2, tmpSnapPoint);
                   if (tmpdist < dist) {
                       dist = tmpdist;
                       spacingLine.setP1(pt);
                       spacingLine.setP2(tmpSnapPoint);
                   }
               }
           }
        }

        d->measureLine = mat.inverted().map(spacingLine);
    }
    QVariantMap msginfo;
    msginfo.insert("INFO_TYPE", "MEASURE_SPACING");
    msginfo.insert("line", d->measureLine);
    emit messageNotified(msginfo);
}

void PcbCamCanvas::doDistMeasure()
{
    Q_D(PcbCamCanvas);
    QVariantMap msginfo;
    msginfo.insert("INFO_TYPE", "MEASURE_DIST");
    msginfo.insert("line", d->measureLine);
    emit messageNotified(msginfo);
}

void PcbCamCanvas::createPopView(const QRectF &iSourceRect, const QRect &iTargetRect)
{
    Q_D(PcbCamCanvas);
    auto popview = new PcbCamPopViewWindow(iSourceRect, iTargetRect, this);
    popview->setStyleSheet(".PcbCamPopViewWindow{background-color:#E9EEF1;}");

    connect(this, &PcbCamCanvas::displayUpdated, popview, &PcbCamPopViewWindow::refresh);
    connect(popview, &PcbCamPopViewWindow::aboutToClose, this, &PcbCamCanvas::onPopViewClosed);
    connect(popview, &PcbCamPopViewWindow::geometryChanged, [this](){this->update();});
    popview->show();
    d->popviewList.append(popview);
}

void PcbCamCanvas::drawStepLayer(QPainter *painter,
                                 const QString &iStep,
                                 const QString &iLayer,
                                 const QColor &iColor,
                                 bool iStepRepeat,
                                 const QTransform &iMatrix,
                                 const QRectF &iSceneRect,
                                 PcbCam::DisplayOptions iDispOption) const
{
    Q_D(const PcbCamCanvas);
    if (d->jobData == nullptr
            || !d->jobData->isStepExists(d->currentStep) ||
            !d->jobData->isStepLayerExists(iStep, iLayer))
    {
        return ;
    }
    PcbCamStep *step = d->jobData->step(iStep);
    PcbCamLayer *layer = d->jobData->layer(iStep, iLayer);
    if (iStepRepeat) {
        for (const PcbCamStepRepeat &sr : step->getBreakedRepeats()) {
            PcbCamStep *srstep = d->jobData->step(sr.step());
            QTransform srmat = PcbCamUtil::matrix(QPointF(sr.x(), sr.y()), srstep->datum(), sr.angle(), sr.mirror()) * iMatrix;
            drawStepLayer(painter, sr.step(), iLayer, iColor, iStepRepeat, srmat,
                          srmat.inverted().mapRect(iMatrix.mapRect(iSceneRect)),iDispOption);
        }
    }
    PcbCamUtil::drawFeatures(painter,
                             layer->features(),
                             PcbCam::Positive,
                             iColor,
                             iMatrix,
                             iSceneRect,
                             iDispOption,
                             d->jobData,
                             step,
                             layer);
}

void PcbCamCanvas::drawStepProfile(QPainter *painter, const QString &iStep, const QColor &iColor, const QTransform &iMatrix, bool iSelected) const
{
    Q_D(const PcbCamCanvas);
    if (d->jobData == nullptr || !d->jobData->isStepExists(iStep)) return ;

    painter->save();
    PcbCamStep *step = d->jobData->step(iStep);
    for (const PcbCamStepRepeat &sr : step->getBreakedRepeats()) {
        PcbCamStep *srstep = d->jobData->step(sr.step());
        QTransform srmat = PcbCamUtil::matrix(QPointF(sr.x(), sr.y()), srstep->datum(), sr.angle(), sr.mirror()) * iMatrix;
        drawStepProfile(painter, sr.step(), iColor, srmat, sr.isSelected());
    }
    QPen pen(iColor);
    if (iSelected) {
        pen.setColor(d->snapColor);
    }
    pen.setWidth(1);
    pen.setCosmetic(true);
    painter->setPen(pen);
    painter->setBrush(Qt::NoBrush);
    painter->setTransform(iMatrix);
    painter->drawPath(step->profile());
    painter->restore();
}

QPixmap PcbCamCanvas::render(const QTransform &iMatrix, const QSize &iSize)
{
    Q_D(PcbCamCanvas);
    QPainter painter;
    QRect renderrect(QPoint(0, 0), iSize);
    QPixmap retpix(renderrect.size());
    painter.begin(&retpix);
    painter.fillRect(renderrect, d->bgColor);
    painter.end();
    if (d->jobData == nullptr || !d->jobData->isStepExists(d->currentStep)) return retpix;

    PcbCamStep *step = d->jobData->step(d->currentStep);
    QRectF sceneRect = iMatrix.inverted().mapRect(QRectF(renderrect));

    QList<QPixmap> layerpixmaps;
    for (const QString &layername : d->dispLayer.keys()) {
        QPixmap pixmap(renderrect.size());
        pixmap.fill(Qt::transparent);
        painter.begin(&pixmap);
        drawStepLayer(&painter, step->name(), layername,
                      QColor(d->dispLayer.value(layername)),
                      true, iMatrix, sceneRect,
                      d->displayOptions);
        painter.end();
        layerpixmaps.append(pixmap);
    }

    //绘制selection
    QList<PcbCamLayer *> affected_layers = d->jobData->stepAffectedLayer(d->currentStep);
    PcbCamLayer *worklayer = d->jobData->stepWorkLayer(d->currentStep);
    if (worklayer != nullptr) affected_layers.prepend(worklayer);
    QPixmap selpixmap(renderrect.size());
    selpixmap.fill(Qt::transparent);
    painter.begin(&selpixmap);
    for (auto layer : affected_layers) {
        auto selfeats = layer->selectedFeatures();
        if (selfeats.isEmpty()) continue;
        PcbCamUtil::drawFeatures(&painter,
                                 selfeats,
                                 PcbCam::Positive,
                                 d->selectionColor,
                                 iMatrix,
                                 sceneRect,
                                 d->displayOptions,
                                 d->jobData,
                                 step,
                                 layer);

    }
    painter.end();


    //绘制highlight
    QPixmap highlightpixmap(renderrect.size());
    highlightpixmap.fill(Qt::transparent);
    painter.begin(&highlightpixmap);
    auto dispopt = d->displayOptions;
    dispopt |= PcbCam::DisplayNegative;
    QRectF srect;
    for (auto hinfo : d->highlightBuffer) {
        auto mat = hinfo.matrix * iMatrix;
        srect = mat.inverted().mapRect(QRectF(renderrect));
        PcbCamUtil::drawFeatures(&painter,
                                 QList<PcbCamFeature*>{hinfo.feature},
                                 PcbCam::Positive,
                                 d->highlightColor,
                                 mat ,
                                 srect,
                                 dispopt,
                                 d->jobData,
                                 hinfo.step,
                                 hinfo.layer
                                 );

    }
    painter.end();


    //绘制checklist高亮项
    QPixmap chkpixmap = renderChkMeans(iMatrix, renderrect.size());

    QPixmap allpix(renderrect.size());
    allpix.fill(Qt::transparent);
    painter.begin(&allpix);
    painter.setCompositionMode(QPainter::CompositionMode_Screen);
    for (const QPixmap &pix: layerpixmaps) {
        painter.drawPixmap(0, 0, pix);
    }

    if (!chkpixmap.isNull()) {
        painter.setCompositionMode(QPainter::CompositionMode_SourceOver);
        painter.drawPixmap(0, 0, chkpixmap);
    }

    if (!highlightpixmap.isNull()) {
        painter.setCompositionMode(QPainter::CompositionMode_SourceOver);
        painter.drawPixmap(0, 0, highlightpixmap);
    }

    if (!selpixmap.isNull()) {
        painter.setCompositionMode(QPainter::CompositionMode_SourceOver);
        painter.drawPixmap(0, 0, selpixmap);
    }

    if (d->displayOptions & PcbCam::DisplayProfile) {
        drawStepProfile(&painter, d->currentStep, d->profileColor, iMatrix);
    }
    painter.end();

    painter.begin(&retpix);
    painter.drawPixmap(0, 0, allpix);
    painter.end();
    return retpix;
}

QPixmap PcbCamCanvas::renderChkMeans(const QTransform &iMatrix, const QSize &iSize)
{
    Q_D(PcbCamCanvas);
    QPainter painter;
    QPixmap pixmap(iSize);
    pixmap.fill(Qt::transparent);
    painter.begin(&pixmap);

    painter.save();
    painter.setTransform(iMatrix);
    QColor meascolor(255, 255, 255, 200);
    QPen pen(meascolor);
    pen.setWidthF(0.005);
    pen.setCapStyle(Qt::RoundCap);
    pen.setJoinStyle(Qt::RoundJoin);
    painter.setPen(pen);
    painter.setBrush(Qt::NoBrush);

    for (auto meas : d->checkMeasList) {
        auto measinfo = meas.toMap();
        auto shape = measinfo.value("shape").toString();
        auto prop = measinfo.value("shapeprop").toMap();
        if (shape == "PT") {
            QRectF rectf(prop.value("x").toDouble() - 0.015, prop.value("y").toDouble() - 0.015,  0.03, 0.03);
            painter.drawLine(rectf.topLeft(), rectf.bottomRight());
            painter.drawLine(rectf.bottomLeft(), rectf.topRight());
        }
        else if (shape == "SG") {
            painter.drawLine(QPointF(prop.value("xs").toDouble(), prop.value("ys").toDouble()),
                             QPointF(prop.value("xe").toDouble(), prop.value("ye").toDouble()));
        }
        else if (shape == "CU") {
            double r = QLineF(prop.value("xs").toDouble(), prop.value("ys").toDouble(), prop.value("xc").toDouble(), prop.value("yc").toDouble()).length();
            QRectF rect(prop.value("xc").toDouble() - r, prop.value("yc").toDouble() -r , r*2, r*2);
            painter.drawRect(rect);
        }
        else if (shape == "LN") {
            QPen newpen = pen;
            newpen.setWidthF(prop.value("size").toDouble() * 2.0);
            //newpen.setCapStyle(prop.value("cap").toString() == "S" ? Qt::SquareCap : Qt::RoundCap);
            painter.setPen(newpen);
            painter.drawLine(QPointF(prop.value("xs").toDouble(), prop.value("ys").toDouble()),
                             QPointF(prop.value("xe").toDouble(), prop.value("ye").toDouble()));
            painter.setPen(pen);

        }
        else if (shape == "CR") {
            double sz = prop.value("size").toDouble()/2.0;
            QRectF rect(prop.value("x").toDouble(), prop.value("y").toDouble(), sz , sz);
            painter.drawEllipse(rect);
        }
        else if (shape == "SQ") {
            double sz = prop.value("size").toDouble()/2.0;
            QRectF rect(prop.value("x").toDouble(), prop.value("y").toDouble(), sz , sz);
            painter.drawRect(rect);
        }
        else if (shape == "RC") {
            QRectF rect(prop.value("xll").toDouble(), prop.value("yll").toDouble(), prop.value("xur").toDouble() , prop.value("yur").toDouble());
            painter.drawRect(rect);
        }
        else if (shape == "AR") {
            double r = QLineF(prop.value("xs").toDouble(), prop.value("ys").toDouble(), prop.value("xc").toDouble(), prop.value("yc").toDouble()).length();
            QRectF rect(prop.value("xc").toDouble() - r, prop.value("yc").toDouble() -r , r*2, r*2);
            double sz = prop.value("size").toDouble()/2.0;
            rect = rect.marginsAdded(QMarginsF(sz, sz, sz, sz));
            painter.drawRect(rect);
        }
    }

    painter.restore();
    painter.end();
    return pixmap;
}

QVariantMap PcbCamCanvas::getSnapPoint(const QPoint &iCursorPos, int iSnapRange)
{
    Q_D(PcbCamCanvas);
    QVariantMap retData;
    d->isSnapPointValid = false;
    retData.insert("snaped", false);
    if (d->jobData == nullptr) {
        retData.insert("point", QPointF());
        return retData;
    }
    QPointF curpos = mapToScene(iCursorPos);
    if (d->snapMode == PcbCam::SnapOff) {
        retData.insert("point", curpos);
        return retData;
    }

    QRectF range = mapToScene(QRect(iCursorPos.x() - iSnapRange, iCursorPos.y() - iSnapRange, iSnapRange*2, iSnapRange *2));

    QPainterPath snapFeatPaths;
    QPolygonF snapFeatPoints;
    bool disp_sr = (d->displayOptions & PcbCam::DisplayStepRepeat);

    getSnapPathsInRect(d->currentStep, range, disp_sr, QTransform(), snapFeatPaths, snapFeatPoints);

    QTransform mat;
    mat.scale(10000, 10000);
    QPointF scaledCurPos = mat.map(curpos);
    QRectF scaledRange = mat.mapRect(range);

    bool snapExists = false;

    double dist = LONG_MAX;
    double tmpdist;
    QPointF snapPoint;
    QPointF tmpSnapPoint;
    snapFeatPaths = mat.map(snapFeatPaths);
    for (auto polyline: snapFeatPaths.toSubpathPolygons()) {
        tmpdist = PcbCamMath::point2polylineDist(scaledCurPos, polyline, tmpSnapPoint);
        if (tmpdist < dist) {
            dist = tmpdist;
            snapPoint = tmpSnapPoint;
            snapExists = true;
        }
    }

    snapFeatPoints = mat.map(snapFeatPoints);
    for (auto point : snapFeatPoints) {
        tmpdist = PcbCamMath::point2pointDist(scaledCurPos, point);
        if (tmpdist < dist) {
            dist = tmpdist;
            snapPoint = point;
            snapExists = true;
        }
    }

    if (snapExists && scaledRange.contains(snapPoint)) {
        retData.insert("snaped", true);
        d->isSnapPointValid = true;
        d->snapPoint = mat.inverted().map(snapPoint);
        retData.insert("point", d->snapPoint);
        return retData;
    }
    else {
        retData.insert("point", curpos);
        return retData;
    }
}

void PcbCamCanvas::getSnapPathsInRect(const QString &iStep, const QRectF &iRange, bool iIncludeStepRepeat, const QTransform &iMatrix, QPainterPath &oSnapFeatPaths, QPolygonF &oSnapFeatPoints) const
{
    Q_D(const PcbCamCanvas);
    if (d->jobData == nullptr) return ;
    auto step = d->jobData->step(iStep);
    if (step == nullptr) return;
    QPainterPath rectpath;
    rectpath.addRect(iRange);
    PcbCam::FeatureSelectionFilter snapfilter;
    snapfilter.type = d->snapFeatureType;
    for (const auto &layername : d->snapLayers) {
        auto layer = d->jobData->layer(iStep, layername);
        if (layer == nullptr) continue;
        for (auto feat : layer->selectFeature(rectpath, snapfilter , PcbCam::NoFeatureSelection, PcbCam::IntersectsFeatureShape)) {
            //捕捉边缘
            if (d->snapMode & PcbCam::SnapEdge) {
                oSnapFeatPaths.addPath(iMatrix.map(feat->shape()));
            }

            if (feat->type() == PcbCam::PadFeature) {
                auto pad = static_cast<PcbCamFeaturePad*>(feat);
                if (d->snapMode & PcbCam::SnapCenter || d->snapMode & PcbCam::SnapMidPoint || d->snapMode & PcbCam::SnapSkeleton) {
                    oSnapFeatPoints << iMatrix.map(pad->pos());
                }
            }
            else if (feat->type() == PcbCam::LineFeature) {
                auto line = static_cast<PcbCamFeatureLine*>(feat);
                if (d->snapMode & PcbCam::SnapMidPoint || d->snapMode & PcbCam::SnapCenter) {
                    oSnapFeatPoints << iMatrix.map(line->line().p1()) << iMatrix.map(line->line().p2());
                    if (d->snapMode & PcbCam::SnapMidPoint) {
                        oSnapFeatPoints << iMatrix.map(line->line().center());
                    }
                }
                if (d->snapMode & PcbCam::SnapSkeleton) {
                    oSnapFeatPaths.moveTo(iMatrix.map(line->line().p1()));
                    oSnapFeatPaths.lineTo(iMatrix.map(line->line().p2()));
                }
            }
            else if (feat->type() == PcbCam::ArcFeature) {
                auto arc = static_cast<PcbCamFeatureArc*>(feat);
                if (d->snapMode & PcbCam::SnapCenter || d->snapMode & PcbCam::SnapMidPoint) {
                    oSnapFeatPoints << iMatrix.map(arc->ps()) << iMatrix.map(arc->pe()) << iMatrix.map(arc->pc());
                }
                if (d->snapMode & PcbCam::SnapSkeleton) {
                    oSnapFeatPaths.addPath(iMatrix.map(PcbCamUtil::arc2path(arc->ps(), arc->pe(), arc->pc(), arc->cw())));
                }
            }
            else if (feat->type() == PcbCam::SurfaceFeature) {
                if (d->snapMode & PcbCam::SnapMidPoint) {
                    oSnapFeatPoints << iMatrix.map(feat->boundingRect().center());
                }
            }
        }
    }

    if (d->snapMode & PcbCam::SnapProfile) {
        auto step = d->jobData->step(iStep);
        if (step != nullptr) {
            oSnapFeatPaths.addPath(iMatrix.map(step->profile()));
        }
    }

    if (iIncludeStepRepeat) {
        for (const auto &sr : step->getBreakedRepeats()) {
            auto srstep = d->jobData->step(sr.step());
            QTransform srmat =  PcbCamUtil::matrix(QPointF(sr.x(), sr.y()), srstep->datum(), sr.angle(), sr.mirror());// * iMatrix;
            auto srprofile = srstep->profile();
            if (!srmat.map(srprofile).intersects(iRange)) continue;
            auto srrange = srmat.inverted().mapRect(iRange);
            getSnapPathsInRect(srstep->name(), srrange, iIncludeStepRepeat, srmat*iMatrix, oSnapFeatPaths, oSnapFeatPoints);
        }
    }
}



void PcbCamCanvas::setDisplayOption(PcbCam::DisplayOptions iDispOption)
{
    Q_D(PcbCamCanvas);
    d->displayOptions = iDispOption;
    d->pixBuffers.clear();
    update();
}

PcbCam::DisplayOptions PcbCamCanvas::displayOption() const
{
    Q_D(const PcbCamCanvas);
    return d->displayOptions;
}


void PcbCamCanvas::setContextMenu(QMenu *iMenu)
{
    Q_D(PcbCamCanvas);
    d->contextMenu = iMenu;
}

QMenu *PcbCamCanvas::contextMenu() const
{
    Q_D(const PcbCamCanvas);
    return d->contextMenu;
}

void PcbCamCanvas::setCurrentFeatureFilter(const PcbCam::FeatureSelectionFilter &iFilter)
{
    Q_D(PcbCamCanvas);
    d->currentFeatureFilter = iFilter;
}

PcbCam::FeatureSelectionFilter PcbCamCanvas::currentFeatureFilter() const
{
    Q_D(const PcbCamCanvas);
    return d->currentFeatureFilter;
}

void PcbCamCanvas::setSnapLayers(const QStringList &iSnapLayers)
{
    Q_D(PcbCamCanvas);
    d->snapLayers = iSnapLayers;
}

QStringList PcbCamCanvas::snapLayers() const
{
    Q_D(const PcbCamCanvas);
    return d->snapLayers;
}

void PcbCamCanvas::setSnapMode(PcbCam::SnapModes iMode)
{
    Q_D(PcbCamCanvas);
    d->snapMode = iMode;
}

PcbCam::SnapModes PcbCamCanvas::snapMode() const
{
    Q_D(const PcbCamCanvas);
    return d->snapMode;
}

void PcbCamCanvas::setSnapFeatureType(PcbCam::FeatureTypes iType)
{
    Q_D(PcbCamCanvas);
    d->snapFeatureType = iType;
}

PcbCam::FeatureTypes PcbCamCanvas::snapFeatureType() const
{
    Q_D(const PcbCamCanvas);
    return d->snapFeatureType;
}

void PcbCamCanvas::showContextMenu(const QPoint &iPos)
{
    Q_D(PcbCamCanvas);
    Q_UNUSED(iPos);
    if (d->contextMenu != nullptr) {
        d->contextMenu->exec(QCursor::pos());
    }

}

void PcbCamCanvas::setMousePoint(const QPointF &iPos)
{
    Q_D(PcbCamCanvas);
    if (d->mouseMode == PcbCamCanvas::ZoomAreaMode || d->mouseMode == PcbCamCanvas::RectangleSelectionMode) {
        //区域放大点选第一点
        if (d->mouseClickTimes == 0) {
            d->rubberBandRect.setTopLeft(iPos);
            d->rubberBandRect.setBottomRight(iPos);
            d->lastCoordPoint = iPos;
            d->mouseClickTimes = 1;
        }
        else if (d->mouseClickTimes == 1) {
            d->rubberBandRect.setBottomRight(iPos);
            d->lastCoordPoint = iPos;
            d->mouseClickTimes = 0;
            if (d->mouseMode ==  PcbCamCanvas::ZoomAreaMode) {
                this->fitInView(d->rubberBandRect);
            }
            else if (d->mouseMode == PcbCamCanvas::RectangleSelectionMode) {
                QPainterPath rectpath; rectpath.addRect(d->rubberBandRect);
                selectFeature(QStringList(), rectpath, d->currentFeatureFilter, d->curFeatSelOp, d->curFeatSelMode);
            }
            d->rubberBandRect = QRectF();
            update();
        }
        else if (d->mouseMode == PcbCamCanvas::SingleSelectionMode) {
            singleClickSelection(d->currentMatrix.map(iPos).toPoint(), d->curFeatSelOp);
        }
        else if (d->mouseMode == PcbCamCanvas::MeasurePointsMode) {
            if (d->mouseClickTimes == 0) {
                d->measureLine.setP1(iPos);
                d->measureLine.setP2(d->measureLine.p1());

                d->rubberBandRect.setTopLeft(iPos);
                d->rubberBandRect.setBottomRight(iPos);
                d->lastCoordPoint = iPos;
                d->mouseClickTimes = 1;
            }
            else if (d->mouseClickTimes == 1) {
                d->measureLine.setP2(iPos);
                d->rubberBandRect.setBottomRight(iPos);
                d->mouseClickTimes = 0;
                d->lastCoordPoint = iPos;
                d->rubberBandRect = QRectF();
                //TODO 抛出量测值
                update();
            }
        }
        else if (d->mouseMode == PcbCamCanvas::PopViewMode) {
            if (d->mouseClickTimes == 0 || d->mouseClickTimes == 2) {
                if (d->currentMovePopView == nullptr) {
                    d->rubberBandRect.setTopLeft(iPos);
                    d->rubberBandRect.setBottomRight(iPos);
                    d->lastCoordPoint = iPos;
                    d->mouseClickTimes += 1;
                }
            }
            else if (d->mouseClickTimes == 1 || d->mouseClickTimes == 3) {
                d->rubberBandRect.setBottomRight(iPos);
                d->lastCoordPoint = iPos;
                if (d->mouseClickTimes == 1) {
                    d->tmpRubberBandRect = d->rubberBandRect;
                    d->rubberBandRect = QRectF();
                    d->mouseClickTimes++;
                }
                else if (d->mouseClickTimes == 3) {
                    QRect poprect = d->currentMatrix.mapRect(d->rubberBandRect.normalized()).toRect();
                    poprect.moveTo(this->mapToGlobal(poprect.topLeft()));
                    this->createPopView(d->tmpRubberBandRect.normalized(), poprect);

                    d->rubberBandRect = QRect();
                    d->tmpRubberBandRect = QRect();
                    d->mouseClickTimes = 0;
                }
                update();
            }
        }
    }
}

void PcbCamCanvas::setDrawRelativeOrig(bool iY)
{
    Q_D(PcbCamCanvas);
    d->drawRelativeOrig = iY;
}

void PcbCamCanvas::setOriginalPoint(double x, double y)
{
    Q_D(PcbCamCanvas);
    d->origPoint.setX(x);
    d->origPoint.setY(y);
}

QPointF PcbCamCanvas::originalPoint() const
{
    Q_D(const PcbCamCanvas);
    return d->origPoint;
}

QPointF PcbCamCanvas::relativeOriginalPoint() const
{
    Q_D(const PcbCamCanvas);
    return d->lastCoordPoint;
}

void PcbCamCanvas::setJobData(PcbCamJob *iJob)
{
    Q_D(PcbCamCanvas);
    d->jobData = iJob;
}



bool PcbCamCanvas::event(QEvent *iEvent)
{
    Q_D(PcbCamCanvas);
    if (iEvent->type() == QEvent::MouseMove) {
        QMouseEvent *evt = static_cast<QMouseEvent*>(iEvent);
        if (d->displayOptions & PcbCam::DisplayFullCursor) {
            d->curMousePos = evt->pos();
            update();
        }
        emit mousePositionChanged(d->currentMatrix.inverted().map(QPointF(evt->pos())));
    }

    if (d->mouseMode == PcbCamCanvas::PanViewMode
            || d->panviewDrag
            || (static_cast<QMouseEvent*>(iEvent) != nullptr &&  (static_cast<QMouseEvent*>(iEvent)->modifiers() & Qt::AltModifier) && static_cast<QMouseEvent*>(iEvent)->button() == Qt::LeftButton))
    {
        if (iEvent->type() == QEvent::MouseButtonDblClick) {
            this->zoomHome();
        }
        else {
            if (iEvent->type() == QEvent::MouseButtonPress) {
                QMouseEvent *evt = static_cast<QMouseEvent*>(iEvent);
                d->panviewDrag = true;
                d->panviewStartPos = evt->pos();
                d->panviewStartRect = d->currentDisplayRect;
                d->lastCursor = this->cursor();
                this->setCursor(QCursor(Qt::ClosedHandCursor));
            }
            else if (d->panviewDrag && iEvent->type() == QEvent::MouseMove) {
                QMouseEvent *evt = static_cast<QMouseEvent*>(iEvent);
                QPointF dist = mapToScene(d->panviewStartPos) - mapToScene(evt->pos());
                this->fitInView(d->panviewStartRect.translated(dist), false);
            }
            else if (iEvent->type() == QEvent::MouseButtonRelease) {
                d->panviewDrag = false;
                this->setCursor(QCursor(d->mouseMode == PcbCamCanvas::PanViewMode ? Qt::OpenHandCursor : d->lastCursor));
            }
        }
    }
    else if (d->mouseMode == PcbCamCanvas::ZoomAreaMode || d->mouseMode == PcbCamCanvas::RectangleSelectionMode) {
        //区域放大点选第一点
        if (d->mouseClickTimes == 0 && iEvent->type() == QEvent::MouseButtonPress ) {
            QMouseEvent *evt = static_cast<QMouseEvent*>(iEvent);
            if (evt->button() == Qt::LeftButton) {
                d->rubberBandRect.setTopLeft(mapToScene(evt->pos()));
                d->rubberBandRect.setBottomRight(mapToScene(evt->pos()));
                d->lastCoordPoint = mapToScene(evt->pos());
                d->mouseClickTimes = 1;
            }
        }
        else if (d->mouseClickTimes == 1 && iEvent->type() == QEvent::MouseMove) {
            QMouseEvent *evt = static_cast<QMouseEvent*>(iEvent);
            d->rubberBandRect.setBottomRight(mapToScene(evt->pos()));
            update();
        }
        else if (d->mouseClickTimes == 1 && iEvent->type() == QEvent::MouseButtonPress) {
            QMouseEvent *evt = static_cast<QMouseEvent*>(iEvent);
            if (evt->button() == Qt::LeftButton) {
                d->rubberBandRect.setBottomRight(mapToScene(evt->pos()));
                d->lastCoordPoint = mapToScene(evt->pos());
                d->mouseClickTimes = 0;
                if (d->mouseMode ==  PcbCamCanvas::ZoomAreaMode) {
                    this->fitInView(d->rubberBandRect);
                }
                else if (d->mouseMode == PcbCamCanvas::RectangleSelectionMode) {
                    PcbCam::FeatureSelectionMode mode = (evt->modifiers() & Qt::ControlModifier) ? PcbCam::IntersectsFeatureShape : PcbCam::ContainsFeatureShape;
                    PcbCam::FeatureSelectionOperation op = (evt->modifiers() & Qt::ShiftModifier) ? PcbCam::AppendFeatureSelection : PcbCam::ReplaceFeatureSelection;
                    QPainterPath rectpath; rectpath.addRect(d->rubberBandRect);
                    selectFeature(QStringList(), rectpath, d->currentFeatureFilter, op, mode);
                }
                d->rubberBandRect = QRectF();
                update();
            }
            else if (evt->button() == Qt::MiddleButton) {
                d->mouseClickTimes = 0;
                d->rubberBandRect = QRectF();
                update();
            }
        }
    }
    else if (d->mouseMode == PcbCamCanvas::PolygonSelectionMode) {
        if (d->mouseClickTimes == 0 && iEvent->type() == QEvent::MouseButtonPress ) {
            QMouseEvent *evt = static_cast<QMouseEvent*>(iEvent);
            if (evt->button() == Qt::LeftButton) {
                d->lastCoordPoint = mapToScene(evt->pos());
                d->curMousePos = evt->pos();
                d->polygonBand.clear();
                d->polygonBand << mapToScene(evt->pos());
                d->mouseClickTimes = 1;
            }
        }
        else if (d->mouseClickTimes == 1 && iEvent->type() == QEvent::MouseMove) {
            QMouseEvent *evt = static_cast<QMouseEvent*>(iEvent);
            d->curMousePos = evt->pos();
            update();
        }
        else if (d->mouseClickTimes == 1 && iEvent->type() == QEvent::MouseButtonPress) {
            QMouseEvent *evt = static_cast<QMouseEvent*>(iEvent);
            if (evt->button() == Qt::LeftButton) {
                d->lastCoordPoint = mapToScene(evt->pos());
                d->polygonBand << mapToScene(evt->pos());
                update();
            }
            else if (evt->button() == Qt::MiddleButton) {
                if (!d->polygonBand.isEmpty()) d->polygonBand.pop_back();
                update();
            }
        }
        else if (d->mouseClickTimes == 1 && iEvent->type() == QEvent::MouseButtonDblClick) {
            QMouseEvent *evt = static_cast<QMouseEvent*>(iEvent);
            if (evt->button() == Qt::LeftButton) {
                d->polygonBand << mapToScene(evt->pos());
                d->lastCoordPoint = mapToScene(evt->pos());
                d->mouseClickTimes = 0;
                if (d->polygonBand.count() > 2) {
                    PcbCam::FeatureSelectionMode mode = (evt->modifiers() & Qt::ControlModifier) ? PcbCam::IntersectsFeatureShape : PcbCam::ContainsFeatureShape;
                    PcbCam::FeatureSelectionOperation op = (evt->modifiers() & Qt::ShiftModifier) ? PcbCam::AppendFeatureSelection : PcbCam::ReplaceFeatureSelection;
                    QPainterPath polypath;
                    polypath.addPolygon(d->polygonBand);
                    polypath.closeSubpath();
                    selectFeature(QStringList(), polypath, d->currentFeatureFilter, op, mode);
                }
                d->polygonBand.clear();
                update();
            }
        }
    }
    else if (d->mouseMode == PcbCamCanvas::SingleSelectionMode) {
        d->mouseClickTimes = 0;
        d->rubberBandRect = QRect();
        if (iEvent->type() == QEvent::MouseButtonDblClick) {
            QMouseEvent *evt = static_cast<QMouseEvent*>(iEvent);
            if (evt->button() == Qt::LeftButton) {
                PcbCam::FeatureSelectionOperation op = (evt->modifiers() & Qt::ShiftModifier)
                        ? PcbCam::AppendFeatureSelection
                        : PcbCam::ReplaceFeatureSelection;
                doubleClickSelection(evt->pos(), op);
            }
        }
        else if (iEvent->type() == QEvent::MouseButtonPress) {
            QMouseEvent *evt = static_cast<QMouseEvent*>(iEvent);
            if (evt->button() == Qt::LeftButton) {
                PcbCam::FeatureSelectionOperation op = (evt->modifiers() & Qt::ShiftModifier)
                        ? PcbCam::AppendFeatureSelection
                        : PcbCam::ReplaceFeatureSelection;
                d->lastCoordPoint = mapToScene(evt->pos());
                singleClickSelection(evt->pos(), op);
            }
            else if (evt->button() == Qt::MiddleButton) {
                singleClickSelection(evt->pos(), PcbCam::RemoveFeatureSelection);
            }

        }
    }
    else if (d->mouseMode == PcbCamCanvas::LayerNetSelectionMode) {
        d->mouseClickTimes = 0;
        d->rubberBandRect = QRect();
        if (iEvent->type() == QEvent::MouseButtonPress) {
            PcbCam::FeatureSelectionFilter filter;
            QMouseEvent *evt = static_cast<QMouseEvent*>(iEvent);
            if (evt->button() == Qt::LeftButton) {
                PcbCam::FeatureSelectionOperation op = (evt->modifiers() & Qt::ShiftModifier)
                        ? PcbCam::AppendFeatureSelection
                        : PcbCam::ReplaceFeatureSelection;
                d->lastCoordPoint = mapToScene(evt->pos());
                layerNetSelection(QStringList(), evt->pos(), op, filter);
            }
            else if (evt->button() == Qt::MiddleButton) {
                layerNetSelection(QStringList(), evt->pos(), PcbCam::RemoveFeatureSelection, filter);
            }

        }
    }
    else if (d->mouseMode == PcbCamCanvas::LayerPolylineSelectionMode) {
        d->mouseClickTimes = 0;
        d->rubberBandRect = QRect();
        if (iEvent->type() == QEvent::MouseButtonPress && d->jobData != nullptr && !d->currentStep.isEmpty()) {
            QMouseEvent *evt = static_cast<QMouseEvent*>(iEvent);
            PcbCam::FeatureSelectionFilter filter;
            filter.type = PcbCam::LineFeature;
            filter.type |= PcbCam::ArcFeature;
            filter.polarity = PcbCam::Positive;

            // 获取鼠标下的symbolname
            auto pos = evt->pos();
            int tol = 4;
            QRectF rect = mapToScene(QRect(pos.x() - tol, pos.y() - tol, 2 * tol, 2 * tol));
            QPainterPath rectpath;
            rectpath.addRect(rect);
            PcbCamLayer *worklayer = d->jobData->stepWorkLayer(d->currentStep);

            if (worklayer != nullptr) {
                PcbCamFeature *firstfeat = worklayer->selectFeature(rectpath, filter, PcbCam::NoFeatureSelection, PcbCam::IntersectsFeatureShape).value(0, nullptr);
                if (firstfeat != nullptr) {
                    filter.includeSymbols.append(firstfeat->symbol()->name());
                }
            }

            if (evt->button() == Qt::LeftButton) {
                PcbCam::FeatureSelectionOperation op = (evt->modifiers() & Qt::ShiftModifier)
                        ? PcbCam::AppendFeatureSelection
                        : PcbCam::ReplaceFeatureSelection;
                d->lastCoordPoint = mapToScene(evt->pos());
                layerNetSelection(QStringList() << worklayer->name(), evt->pos(), op, filter);
            }
            else if (evt->button() == Qt::MiddleButton) {
                d->lastCoordPoint = mapToScene(evt->pos());
                layerNetSelection(QStringList() << worklayer->name(), evt->pos(), PcbCam::RemoveFeatureSelection, filter);
            }
        }
    }
    else if (d->mouseMode == PcbCamCanvas::SnapFeatMode) {
        d->mouseClickTimes = 0;
        d->rubberBandRect = QRect();
        if (iEvent->type() == QEvent::MouseButtonPress) {
            QMouseEvent *evt = static_cast<QMouseEvent*>(iEvent);
            if (evt->button() == Qt::LeftButton) {
                snapFeatInfo(evt->pos());
            }
        }
    }
    else if (d->mouseMode == PcbCamCanvas::MeasurePointsMode) {
        //测量点选第一点
        if (d->mouseClickTimes == 0 && iEvent->type() == QEvent::MouseButtonPress ) {
            QMouseEvent *evt = static_cast<QMouseEvent*>(iEvent);
            if (evt->button() == Qt::LeftButton) {
                QPointF sp = getSnapPoint(evt->pos()).value("point").toPointF();
                d->measureLine.setP1(sp);
                d->measureLine.setP2(d->measureLine.p1());
                d->rubberBandRect.setTopLeft(sp);
                d->rubberBandRect.setBottomRight(sp);
                d->lastCoordPoint = sp;
                d->mouseClickTimes = 1;
            }
        }
        else if (d->mouseClickTimes == 0 && iEvent->type() == QEvent::MouseMove) {
            QMouseEvent *evt = static_cast<QMouseEvent*>(iEvent);
            getSnapPoint(evt->pos()).value("point").toPointF();
            update();
        }
        else if (d->mouseClickTimes == 1 && iEvent->type() == QEvent::MouseMove) {
            QMouseEvent *evt = static_cast<QMouseEvent*>(iEvent);
            QPointF sp = getSnapPoint(evt->pos()).value("point").toPointF();
            d->measureLine.setP2(sp);
            d->rubberBandRect.setBottomRight(sp);
            update();
        }
        else if (d->mouseClickTimes == 1 && iEvent->type() == QEvent::MouseButtonPress) {
            QMouseEvent *evt = static_cast<QMouseEvent*>(iEvent);
            if (evt->button() == Qt::LeftButton) {
                d->lastCoordPoint = d->rubberBandRect.bottomRight();
                d->mouseClickTimes = 0;
                d->isSnapPointValid = false;
                d->rubberBandRect = QRectF();
                doDistMeasure();
                update();
            }
            else if (evt->button() == Qt::MiddleButton) {
                d->mouseClickTimes = 0;
                d->isSnapPointValid = false;
                d->rubberBandRect = QRectF();
                update();
            }
        }
    }
    else if (d->mouseMode == PcbCamCanvas::MeasureSpacingMode) {
        // 间距测量
        if (iEvent->type() == QEvent::MouseButtonPress) {
            QMouseEvent *evt = static_cast<QMouseEvent*>(iEvent);
            if (evt->button() == Qt::LeftButton) {
                auto featinfo = getFirstFeatureAt(evt->pos(), DisplayLayer);
                if (featinfo.feature != nullptr) {
                   d->spacingFeatBuffer << featinfo;
                }
                if (d->spacingFeatBuffer.count() == 2) {
                    doSpacingMeasure();
                }
                else if (d->spacingFeatBuffer.count() > 2) {
                    d->spacingFeatBuffer.pop_front();
                    d->spacingFeatBuffer.pop_front();
                    d->measureLine = QLineF(0.0, 0.0, 0.0, 0.0);
                }
                update();
            }
            else if (evt->button() == Qt::MiddleButton) {
                if (!d->spacingFeatBuffer.isEmpty()) d->spacingFeatBuffer.pop_back();
                update();
            }
        }
    }
    else if (d->mouseMode == PcbCamCanvas::PopViewMode) {
        if ((d->mouseClickTimes == 0 || d->mouseClickTimes == 2 ) && iEvent->type() == QEvent::MouseButtonPress ) {
            QMouseEvent *evt = static_cast<QMouseEvent*>(iEvent);
            if (evt->button() == Qt::LeftButton) {
                //判断是否为移动PopView范围
                if (d->mouseClickTimes == 0) {
                    for (auto popview : d->popviewList) {
                        if (d->currentMatrix.mapRect(popview->sourceRect()).contains(evt->pos())) {
                            d->currentMovePopView = popview;
                            d->currentMovePopViewOrigRect = d->currentMatrix.mapRect(popview->sourceRect()).toRect();
                            d->panviewStartPos = evt->pos();
                            this->setCursor(QCursor(Qt::ClosedHandCursor));
                            break;
                        }
                    }

                }
                if (d->currentMovePopView == nullptr) {
                    d->rubberBandRect.setTopLeft(mapToScene(evt->pos()));
                    d->rubberBandRect.setBottomRight(mapToScene(evt->pos()));
                    d->lastCoordPoint = mapToScene(evt->pos());
                    d->mouseClickTimes += 1;
                }
            }
        }
        else if (d->currentMovePopView && d->mouseClickTimes == 0 && iEvent->type() == QEvent::MouseMove) {
            //移动PopView范围
            QMouseEvent *evt = static_cast<QMouseEvent*>(iEvent);
            QPoint dist(evt->pos().x() - d->panviewStartPos.x() , evt->pos().y() - d->panviewStartPos.y());
            d->currentMovePopView->setSourceRect(mapToScene(d->currentMovePopViewOrigRect.translated(dist)));
            this->update();
        }
        else if (d->currentMovePopView && d->mouseClickTimes == 0 && iEvent->type() == QEvent::MouseButtonRelease) {
            //移动PopView范围完成
            d->currentMovePopView->refresh();
            this->setCursor(QCursor(Qt::ArrowCursor));
            d->currentMovePopView = nullptr;
        }
        else if ((d->mouseClickTimes == 1 || d->mouseClickTimes == 3)  && iEvent->type() == QEvent::MouseMove) {
            QMouseEvent *evt = static_cast<QMouseEvent*>(iEvent);
            d->rubberBandRect.setBottomRight(mapToScene(evt->pos()));
            update();
        }
        else if ((d->mouseClickTimes == 1 || d->mouseClickTimes == 3) && iEvent->type() == QEvent::MouseButtonPress) {
            QMouseEvent *evt = static_cast<QMouseEvent*>(iEvent);
            if (evt->button() == Qt::LeftButton) {
                d->rubberBandRect.setBottomRight(mapToScene(evt->pos()));
                d->lastCoordPoint = mapToScene(evt->pos());
                if (d->mouseClickTimes == 1) {
                    d->tmpRubberBandRect = d->rubberBandRect;
                    d->rubberBandRect = QRectF();
                    d->mouseClickTimes++;
                }
                else if (d->mouseClickTimes == 3) {
                    QRect poprect = d->currentMatrix.mapRect(d->rubberBandRect.normalized()).toRect();
                    poprect.moveTo(this->mapToGlobal(poprect.topLeft()));
                    this->createPopView(d->tmpRubberBandRect.normalized(), poprect);

                    d->rubberBandRect = QRect();
                    d->tmpRubberBandRect = QRect();
                    d->mouseClickTimes = 0;
                }

                update();
            }
        }
        if (iEvent->type() == QEvent::MouseButtonPress && static_cast<QMouseEvent*>(iEvent)->button() == Qt::MiddleButton) {
            d->mouseClickTimes = 0;
            d->rubberBandRect = QRectF();
            d->tmpRubberBandRect = QRectF();
            update();
        }

    }

    if (iEvent->type() == QEvent::KeyPress) {
        QKeyEvent *keyevt = static_cast<QKeyEvent*>(iEvent);
        if (keyevt->modifiers() == Qt::NoModifier) {
            emit keyPressed(keyevt->text());
        }
    }

    return QWidget::event(iEvent);
}

void PcbCamCanvas::paintEvent(QPaintEvent */*iEvent*/)
{
    Q_D(PcbCamCanvas);

    QPainter painter;
    painter.begin(this);
    painter.fillRect(this->rect(), d->bgColor);
    painter.end();

    if (d->jobData == nullptr) return;

    if (!d->jobData->isStepExists(d->currentStep)) return;
    PcbCamStep *curstep = d->jobData->step(d->currentStep);
    QRectF sceneRect = d->currentMatrix.inverted().mapRect(QRectF(this->rect()));

    if (d->pixBuffers.value("ALL").isNull()) {

        QList<QPixmap> pixmaps;
        for (const QString &layername: d->dispLayer.keys()) {
            QString bfkey = curstep->name() + "/" + layername + "/" + d->dispLayer.value(layername);
            if (!d->pixBuffers.contains(bfkey)) {
                QPixmap pixmap(this->rect().size());
                pixmap.fill(Qt::transparent);
                painter.begin(&pixmap);
                drawStepLayer(&painter, curstep->name(), layername,
                              QColor(d->dispLayer.value(layername)),
                              (d->displayOptions & PcbCam::DisplayStepRepeat),
                              d->currentMatrix, sceneRect,d->displayOptions);
                painter.end();
                d->pixBuffers.insert(bfkey, pixmap);
            }

            pixmaps.append(d->pixBuffers.value(bfkey));
        }

        //绘制selection
        QPixmap selpixmap;
        {
            QList<PcbCamLayer *> affected_layers = d->jobData->stepAffectedLayer(d->currentStep);
            PcbCamLayer *worklayer = d->jobData->stepWorkLayer(d->currentStep);
            if (worklayer != nullptr) affected_layers.prepend(worklayer);
            QString bfkey = curstep->name() + "/SELECTION";
            if (!d->pixBuffers.contains(bfkey)) {
                QPixmap pixmap(this->rect().size());
                pixmap.fill(Qt::transparent);
                painter.begin(&pixmap);
                auto dispopt = d->displayOptions;
                dispopt |= PcbCam::DisplayNegative;
                for (auto layer : affected_layers) {
                    auto selfeats = layer->selectedFeatures();
                    if (selfeats.isEmpty()) continue;
                    PcbCamUtil::drawFeatures(&painter,
                                             selfeats,
                                             PcbCam::Positive,
                                             d->selectionColor,
                                             d->currentMatrix,
                                             sceneRect,
                                             dispopt,
                                             d->jobData,
                                             curstep,
                                             layer);

                }
                painter.end();
                d->pixBuffers.insert(bfkey, pixmap);
            }
            selpixmap = d->pixBuffers.value(bfkey);
        }

        //绘制highlight
        QPixmap highlightpixmap;
        {
            QString bfkey = curstep->name() + "/HIGHLIGHT";
            if (!d->pixBuffers.contains(bfkey)) {
                QPixmap pixmap(this->rect().size());
                pixmap.fill(Qt::transparent);
                painter.begin(&pixmap);
                auto dispopt = d->displayOptions;
                dispopt |= PcbCam::DisplayNegative;
                QRectF srect;
                for (auto &hinfo : d->highlightBuffer) {
                    auto mat = hinfo.matrix * d->currentMatrix;
                    srect = mat.inverted().mapRect(QRectF(this->rect()));
                    PcbCamUtil::drawFeatures(&painter,
                                             QList<PcbCamFeature*>{hinfo.feature},
                                             PcbCam::Positive,
                                             d->highlightColor,
                                             mat ,
                                             srect,
                                             dispopt,
                                             d->jobData,
                                             hinfo.step,
                                             hinfo.layer
                                             );

                }

                painter.end();
                d->pixBuffers.insert(bfkey, pixmap);
            }
            highlightpixmap = d->pixBuffers.value(bfkey);
        }

        //绘制checklist高亮项
        QPixmap chkpixmap;
        {
            QString bfkey = curstep->name() + "/CHECKMEAS";
            if (!d->pixBuffers.contains(bfkey) && !d->checkMeasList.isEmpty()) {
                d->pixBuffers.insert(bfkey, renderChkMeans(d->currentMatrix, this->rect().size()));
            }
            chkpixmap = d->pixBuffers.value(bfkey);
        }

        QPixmap allpix(this->rect().size());
        allpix.fill(Qt::transparent);
        painter.begin(&allpix);

        painter.setCompositionMode(QPainter::CompositionMode_Screen);
        for (const QPixmap &pix : pixmaps) {
            painter.drawPixmap(0, 0, pix);
        }

        if (!chkpixmap.isNull()) {
            painter.setCompositionMode(QPainter::CompositionMode_SourceOver);
            painter.drawPixmap(0, 0, chkpixmap);
        }

        if (!highlightpixmap.isNull()) {
            painter.setCompositionMode(QPainter::CompositionMode_SourceOver);
            painter.drawPixmap(0, 0, highlightpixmap);
        }

        if (!selpixmap.isNull()) {
            painter.setCompositionMode(QPainter::CompositionMode_SourceOver);
            painter.drawPixmap(0, 0, selpixmap);
        }

        if (d->displayOptions & PcbCam::DisplayProfile) {
            drawStepProfile(&painter, d->currentStep, d->profileColor, d->currentMatrix);
        }
        painter.end();
        d->pixBuffers.insert("ALL", allpix);
        emit displayUpdated();
    }



    painter.begin(this);
    painter.drawPixmap(0, 0, d->pixBuffers.value("ALL"));

    //绘制座标原点
    QPen pen(d->profileColor);
    pen.setWidth(1);
    pen.setCosmetic(true);
    painter.setPen(pen);
    painter.setBrush(Qt::NoBrush);
    QPoint origPos = d->currentMatrix.map(d->origPoint).toPoint();
    int origSize = 10;
    QRect origrect (origPos.x() - origSize/2.0, origPos.y() - origSize/2.0, origSize, origSize);
    if (d->displayOptions & PcbCam::DisplayDatumn) {
        painter.drawEllipse(origrect);
        painter.drawLine(QLine(origPos.x(), origPos.y() - origSize/2 - 2, origPos.x(), origPos.y() + origSize/2 + 2));
        painter.drawLine(QLine(origPos.x() - origSize/2 - 2, origPos.y(), origPos.x() + origSize/2 + 2, origPos.y()));
    }
    //绘制相对座标原点
    if (d->drawRelativeOrig) {
        QPoint relOrigPos = d->currentMatrix.map(d->lastCoordPoint).toPoint();
        painter.drawPoint(relOrigPos);
        painter.drawLine(relOrigPos.x() - origSize/2.0, relOrigPos.y(), relOrigPos.x(), relOrigPos.y() + origSize/2);
        painter.drawLine(relOrigPos.x(), relOrigPos.y() + origSize/2, relOrigPos.x() + origSize/2.0, relOrigPos.y());
        painter.drawLine(relOrigPos.x() + origSize/2.0, relOrigPos.y(), relOrigPos.x(), relOrigPos.y() - origSize/2);
        painter.drawLine(relOrigPos.x(), relOrigPos.y() - origSize/2, relOrigPos.x() - origSize/2.0, relOrigPos.y());
    }


    //绘制Rubber
    pen.setColor(d->rubberColor);
    pen.setWidth(1);
    pen.setCosmetic(true);
    painter.setPen(pen);

    QColor color(Qt::blue);
    color.setAlphaF(0.2);
    painter.setBrush(QBrush(color));

    if (!d->tmpRubberBandRect.normalized().isEmpty()) {
        painter.drawRect(d->currentMatrix.mapRect(d->tmpRubberBandRect).normalized());
    }
    if (!d->rubberBandRect.normalized().isEmpty()) {
        painter.drawRect(d->currentMatrix.mapRect(d->rubberBandRect).normalized());
    }

    if (d->polygonBand.count() == 1) {
        painter.drawLine(d->currentMatrix.map(d->polygonBand.first()).toPoint(), d->curMousePos);
    }
    else if (d->polygonBand.count() >= 2) {
        auto poly = d->polygonBand;
        poly << mapToScene(d->curMousePos);
        QPainterPath path;
        path.addPolygon(poly);
        path.closeSubpath();
        painter.drawPath(d->currentMatrix.map(path));
    }

    //绘制全屏Cursor
    if (d->displayOptions & PcbCam::DisplayFullCursor) {
        auto rect = this->rect();
        painter.drawLine(d->curMousePos.x(), rect.top(), d->curMousePos.x(), rect.bottom());
        painter.drawLine(rect.left(), d->curMousePos.y(), rect.right(), d->curMousePos.y());
    }

    // 绘制捕捉点
    if (d->isSnapPointValid) {
        painter.setPen(d->snapColor);
        auto snapcolor = d->snapColor;
        snapcolor.setAlphaF(0.2);
        painter.setBrush(QBrush(color));
        double snapsize = 8;
        auto snappos = d->currentMatrix.map(d->snapPoint);
        QPainterPath snappath;
        snappath.moveTo(snappos.x() - snapsize, snappos.y());
        snappath.lineTo(snappos.x(), snappos.y() + snapsize);
        snappath.lineTo(snappos.x() + snapsize, snappos.y());
        snappath.lineTo(snappos.x(), snappos.y() - snapsize);
        snappath.closeSubpath();
        painter.drawPath(snappath);
        painter.drawPoint(snappos);
    }

    //绘制PopView框
    painter.setPen(d->highlightColor);
    for (const PcbCamPopViewWindow *popview: d->popviewList) {
        auto srcRect = d->currentMatrix.mapRect(popview->sourceRect()).normalized();
        auto popRect = popview->viewRect();
        popRect.moveTo(this->mapFromGlobal(popRect.topLeft()));
        popRect = popRect.normalized();
        painter.drawRect(srcRect);
        painter.drawLine(QLineF(srcRect.topLeft(), popRect.topLeft()));
        painter.drawLine(QLineF(srcRect.topRight(), popRect.topRight()));
        painter.drawLine(QLineF(srcRect.bottomLeft(), popRect.bottomLeft()));
        painter.drawLine(QLineF(srcRect.bottomRight(), popRect.bottomRight()));
    }

    //绘制间距量测
    if (!d->spacingFeatBuffer.isEmpty()) {
        if (d->measureLine.length() > 0) {
            painter.setPen(d->highlightColor);
            painter.drawLine(d->currentMatrix.map(d->measureLine));
        }
        auto dispopt = d->displayOptions;
        dispopt |= PcbCam::DisplayNegative;
        for (auto hinfo : d->spacingFeatBuffer) {
            auto mat = hinfo.matrix * d->currentMatrix;
            QRectF srect = mat.inverted().mapRect(QRectF(this->rect()));
            PcbCamUtil::drawFeatures(&painter,
                                     QList<PcbCamFeature*>{hinfo.feature},
                                     PcbCam::Positive,
                                     d->highlightColor,
                                     mat ,
                                     srect,
                                     dispopt,
                                     d->jobData,
                                     hinfo.step,
                                     hinfo.layer
                                     );

        }

    }


    //绘制距离量测
    if (d->mouseMode == PcbCamCanvas::MeasurePointsMode && d->mouseClickTimes == 1) {
        painter.setPen(d->rubberColor);
        QRectF rubberrect = d->currentMatrix.mapRect(d->rubberBandRect);
        rubberrect = rubberrect.normalized();

        QFont font = painter.font();
        font.setPixelSize(16);
        painter.setFont(font);
        QFontMetrics fm(font);
        double dx = d->unitsType == PcbCam::Units_Mm ? d->measureLine.dx() * 25.4 : d->measureLine.dx();
        double dy = d->unitsType == PcbCam::Units_Mm ? d->measureLine.dy() * 25.4 : d->measureLine.dy();
        double len = d->unitsType == PcbCam::Units_Mm ? d->measureLine.length() * 25.4 : d->measureLine.length();

        if ((dx > 0 && dy >0 ) || (dx < 0 && dy < 0)) {
            painter.drawLine(rubberrect.bottomLeft(), rubberrect.topRight());
        }
        else {
            painter.drawLine(rubberrect.topLeft(), rubberrect.bottomRight());
        }

        QString mtext = QString("DX=%1, DY=%2, D=%3").arg(dx).arg(dy).arg(len);
        int textwidth = fm.horizontalAdvance(mtext);
        int textheight = fm.height();
        QRect textrect;
        if (dy > 0) {
            textrect.setTop(rubberrect.top() - textheight);
            textrect.setBottom(rubberrect.top());
        }
        else {
            textrect.setTop(rubberrect.bottom());
            textrect.setBottom(rubberrect.bottom() + textheight);
        }

        if (dx > 0) {
            textrect.setLeft(rubberrect.right() - textwidth);
            textrect.setRight(rubberrect.right());
        }
        else {
            textrect.setLeft(rubberrect.left());
            textrect.setRight(rubberrect.left() + textwidth);
        }

        if (textrect.left() < 0) {
            textrect.moveLeft(0);
        }
        if (textrect.right() > this->contentsRect().right()) {
            textrect.moveRight(this->contentsRect().right());
        }
        painter.setPen(d->highlightColor);
        painter.drawText(textrect, mtext);
    }



    painter.end();


}

void PcbCamCanvas::resizeEvent(QResizeEvent *)
{
    Q_D(PcbCamCanvas);
    fitInView(d->currentDisplayRect, false);
}

void PcbCamCanvas::wheelEvent(QWheelEvent *event)
{
    Q_D(PcbCamCanvas);
    QPoint numDegrees = event->angleDelta() / 8;
    QPointF pos = event->position();
    int steps = numDegrees.y()/15;
    if (steps == 0) {
        event->accept();
        return;
    }
    bool isZoomIn = steps < 0;
    steps = qMin(2, abs(steps));
    qreal scale = isZoomIn ? 1.2*steps : 1.0/(1.2*steps);

    QTransform mat;
    mat.scale(scale, scale);
    auto newpos = mat.map(pos);
    auto newrect = mat.mapRect(QRectF(this->rect()));
    newrect.translate(pos.x() - newpos.x(), pos.y() - newpos.y());

    this->fitInView( d->currentMatrix.inverted().mapRect(QRectF(newrect)), false);

    event->accept();
    update();
}

void PcbCamCanvas::contextMenuEvent(QContextMenuEvent *event)
{
    Q_D(PcbCamCanvas);
    //    if (event->modifiers() & Qt::ControlModifier || event->modifiers() &Qt::AltModifier || event->modifiers() & Qt::ShiftModifier) {
    //        event->ignore();
    //    }
    //    else {
    if (d->contextMenu != nullptr) {
        d->contextMenu->exec(event->globalPos());
    }
    event->accept();
    //    }
}

