#include "ebase.h"
#include "gqt.h"
#include "tools.h"
#include <QDirIterator>
#include <QGraphicsScene>
#include <QGraphicsSceneMouseEvent>
#include <QJsonArray>
#include <QJsonObject>
#include <QLabel>
#include <QPainter>
#include <QSpinBox>
#include <QtMath>

struct BorderImg {
    QString name;
    QPixmap img;
};
QVector<BorderImg> borderImgs;
int borderImgMaxWidth = 0;
int borderImgMaxHeight = 0;
struct Initer{
    Initer() {
        QDirIterator it(":res/borders/", QDirIterator::Subdirectories);
        while(it.hasNext()) {
            BorderImg bdImg;
            bdImg.name = it.next();
            bdImg.img = QPixmap(bdImg.name);
            int idx = bdImg.name.lastIndexOf('/');
            if(idx>-1) bdImg.name = bdImg.name.mid(idx+1);
            borderImgs.append(bdImg);
            if(bdImg.img.width() > borderImgMaxWidth) borderImgMaxWidth = bdImg.img.width();
            if(bdImg.img.height() > borderImgMaxHeight) borderImgMaxHeight = bdImg.img.height();
        }
    }
};
EBase::EBase(EBase *multiWin) : mMultiWin(multiWin) {
    static struct Initer aaa;
    if(mMultiWin == nullptr) {
        setFlag(ItemIsMovable);
        setFlag(ItemIsSelectable);
    }
    mSidePen.setCapStyle(Qt::FlatCap);
    mSidePen.setDashPattern(QVector<qreal>{1,3});
}

void EBase::setBaseAttr(const QJsonObject &json) {
    mStartTime = json["startTime"].toInt();
    auto geometry = json["geometry"].toObject();
    setPos(geometry["x"].toInt(), geometry["y"].toInt());
    setSize(geometry["w"].toInt(), geometry["h"].toInt());
    setZValue(geometry["order"].toInt());
    QString bdName = json["border"].toString();
    if(! bdName.isEmpty()) {
        for(int i=0; i<borderImgs.size(); i++) if(borderImgs[i].name==bdName) {bdImgIdx = i; break;}
        bdEff = json["borderEff"].toString();
        bdSpeed = json["borderSpeed"].toInt(2);
    } else {
        bdName = geometry["border"].toString();
        if(! bdName.isEmpty()) {
            for(int i=0; i<borderImgs.size(); i++) if(borderImgs[i].name==bdName) {bdImgIdx = i; break;}
            bdEff = geometry["border_eff"].toString();
            bdSpeed = geometry["border_speed"].toInt(2);
        }
    }
}
void EBase::addBaseAttr(QJsonObject &obj) const {
    auto ele = mMultiWin!=nullptr ? mMultiWin : this;
    int bdWidth = ele->bdImgIdx > -1 ? borderImgs[ele->bdImgIdx].img.height() : 0;
    obj.insert("startTime", mStartTime);
    obj.insert("innerX", ele->x()+bdWidth);
    obj.insert("innerY", ele->y()+bdWidth);
    obj.insert("innerW", ele->mWidth-bdWidth-bdWidth);
    obj.insert("innerH", ele->mHeight-bdWidth-bdWidth);
    QJsonObject geometry;
    geometry["order"] = zValue();
    geometry["x"] = (int)ele->x();
    geometry["y"] = (int)ele->y();
    geometry["w"] = (int)ele->mWidth;
    geometry["h"] = (int)ele->mHeight;
    if(bdImgIdx>-1) {
        obj["border"] = borderImgs[bdImgIdx].name;
        obj["borderSize"] = QJsonArray{borderImgs[bdImgIdx].img.width(), borderImgs[bdImgIdx].img.height()};
        obj["borderEff"] = bdEff.isEmpty() ? QJsonValue() : bdEff;
        obj["borderSpeed"] = bdSpeed;
//        geometry["border"] = borderImgs[bdImgIdx].name;
//        geometry["border_eff"] = bdEff.isEmpty() ? QJsonValue() : bdEff;
//        geometry["border_speed"] = bdSpeed;
    }
    obj.insert("geometry", geometry);
}

QRectF EBase::innerRect() const {
    auto ele = mMultiWin!=nullptr ? mMultiWin : this;
    int bdWidth = ele->bdImgIdx > -1 ? borderImgs[ele->bdImgIdx].img.height() : 0;
    return QRectF(bdWidth, bdWidth, ele->mWidth-bdWidth-bdWidth, ele->mHeight-bdWidth-bdWidth);
}

void EBase::fitProgSize() {
    if(gProgItem->mWidth < mWidth || gProgItem->mHeight < mHeight) {
        auto rate = qMin(gProgItem->mWidth / mWidth, gProgItem->mHeight / mHeight);
        prepareGeometryChange();
        mWidth *= rate;
        mHeight *= rate;
        emit sizeChanged();
    }
    int lmt = gProgItem->mWidth - mWidth;
    if(x() > lmt) setX(lmt);
    lmt = gProgItem->mHeight - mHeight;
    if(y() > lmt) setY(lmt);
}

QRectF EBase::boundingRect() const {
    qreal xy = -m_handleLen / 2;
    return QRectF(xy, xy, mWidth + m_handleLen, mHeight + m_handleLen);
}
//绘制选中和未选中的区域边框
void EBase::paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) {
    if(mMultiWin!=nullptr) return;
    painter->save();
    //绘制边框
    if(bdImgIdx > -1) {
        if(bdTimerId==0 && !bdEff.isEmpty()) {
            if(bdEff.startsWith("ro")) bdTimerId = startTimer(bdSpeed==1 ? 66 : (bdSpeed==2 ? 33 : 16), Qt::PreciseTimer);
            else bdTimerId = startTimer(bdSpeed==1 ? 500 : (bdSpeed==2 ? 250 : 66));
        }
        if(!bdEff.startsWith("bl") || bdOff==0) {
            int bdWidth = borderImgs[bdImgIdx].img.height();
            int halfBdWidth = (bdWidth+1)/2;
            QBrush brush(borderImgs[bdImgIdx].img);
            QTransform transTop = QTransform::fromTranslate(halfBdWidth+bdOff, 0);
            QTransform transRight = QTransform::fromTranslate(mWidth - bdWidth, halfBdWidth*3-mWidth+bdOff);
            transRight.rotate(90);
            QTransform transBottom = QTransform::fromTranslate(halfBdWidth*3-mHeight-bdOff, mHeight - bdWidth);
            transBottom.rotate(180);
            QTransform transLeft = QTransform::fromTranslate(0, halfBdWidth-bdOff);
            transLeft.rotate(270);

            brush.setTransform(transTop);
            QPainterPath path(QPointF(0, 0));
            path.lineTo(mWidth, 0);
            path.lineTo(mWidth - bdWidth, bdWidth);
            path.lineTo(bdWidth, bdWidth);
            path.closeSubpath();
            painter->fillPath(path, brush);

            brush.setTransform(transRight);
            path = QPainterPath(QPointF(mWidth, 0));
            path.lineTo(mWidth, mHeight);
            path.lineTo(mWidth - bdWidth, mHeight - bdWidth);
            path.lineTo(mWidth - bdWidth, bdWidth);
            path.closeSubpath();
            painter->fillPath(path, brush);

            brush.setTransform(transBottom);
            path = QPainterPath(QPointF(mWidth, mHeight));
            path.lineTo(0, mHeight);
            path.lineTo(bdWidth, mHeight - bdWidth);
            path.lineTo(mWidth - bdWidth, mHeight - bdWidth);
            path.closeSubpath();
            painter->fillPath(path, brush);

            brush.setTransform(transLeft);
            path = QPainterPath(QPointF(0, mHeight));
            path.lineTo(0, 0);
            path.lineTo(bdWidth, bdWidth);
            path.lineTo(bdWidth, mHeight - bdWidth);
            path.closeSubpath();
            painter->fillPath(path, brush);
        }
    }

    if(isSelected()) {
        mSidePen.setColor(Qt::green);
        painter->setPen(mSidePen);
        painter->drawRect(0, 0, mWidth, mHeight);
        m_rLT = QRectF(-m_handleLen/2, -m_handleLen/2, m_handleLen, m_handleLen);//左上角
        m_rT  = QRectF(mWidth/2 - m_handleLen/2, -m_handleLen/2, m_handleLen, m_handleLen);//上中
        m_rRT = QRectF(mWidth - m_handleLen/2, - m_handleLen/2, m_handleLen, m_handleLen);//右上角
        m_rL  = QRectF(-m_handleLen/2, mHeight/2 - m_handleLen/2, m_handleLen, m_handleLen);
        m_rR  = QRectF(mWidth - m_handleLen/2, mHeight/2 - m_handleLen/2, m_handleLen, m_handleLen);
        m_rLB = QRectF(-m_handleLen/2, mHeight - m_handleLen/2, m_handleLen, m_handleLen);
        m_rB  = QRectF(mWidth/2 - m_handleLen/2, mHeight - m_handleLen/2, m_handleLen, m_handleLen);
        m_rRB = QRectF(mWidth - m_handleLen/2, mHeight - m_handleLen/2, m_handleLen, m_handleLen);
        static QPen handlePen = QPen(Qt::green);
        painter->setPen(handlePen);
        painter->drawRect(m_rLT);
        painter->drawRect(m_rT);
        painter->drawRect(m_rRT);
        painter->drawRect(m_rL);
        painter->drawRect(m_rR);
        painter->drawRect(m_rLB);
        painter->drawRect(m_rB);
        painter->drawRect(m_rRB);
    } else {
        mSidePen.setColor(Qt::darkGreen);
        painter->setPen(mSidePen);
        painter->drawRect(0, 0, mWidth, mHeight);
    }

    //磁条吸附时两化吸附的边
    static QPen snapPen(Qt::green);
    painter->setPen(snapPen);
    if(mLRSnap==1) painter->drawLine(0, 0, 0, mHeight);
    else if(mLRSnap==2) painter->drawLine(mWidth, 0, mWidth, mHeight);
    if(mTBSnap==1) painter->drawLine(0, 0, mWidth, 0);
    else if(mTBSnap==2) painter->drawLine(0, mHeight, mWidth, mHeight);
    painter->restore();
}

void EBase::mousePressEvent(QGraphicsSceneMouseEvent *e) {
    QGraphicsObject::mousePressEvent(e);
    if(e->button() != Qt::LeftButton) return;
    setFrmSec(e->pos());
    auto mousePos = e->scenePos();
    auto elePos = pos();
    if(mFrmSec==Qt::TitleBarArea || mFrmSec==Qt::TopSection || mFrmSec==Qt::LeftSection || mFrmSec==Qt::TopLeftSection) mPressRel = elePos - mousePos;
    else if(mFrmSec==Qt::BottomRightSection) mPressRel = QPointF(mWidth - mousePos.x(), mHeight - mousePos.y());
    else if(mFrmSec==Qt::RightSection      ) mPressRel = QPointF(mWidth - mousePos.x(), mHeight               );
    else if(mFrmSec==Qt::BottomSection     ) mPressRel = QPointF(mWidth               , mHeight - mousePos.y());
    else if(mFrmSec==Qt::TopRightSection   ) mPressRel = QPointF(elePos.x()+mWidth - mousePos.x(), elePos.y()         - mousePos.y());
    else if(mFrmSec==Qt::BottomLeftSection ) mPressRel = QPointF(elePos.x()        - mousePos.x(), elePos.y()+mHeight - mousePos.y());
    else if(mFrmSec==Qt::NoSection) mPressRel.setX(FLT_MAX);
    if(mPressRel.x()!=FLT_MAX) {
        mOtherEles.clear();
        auto scene = this->scene();
        if(0 == scene) return;
        auto items = scene->items();
        foreach(auto item, items) {
            if(item==this) continue;
            auto ele = static_cast<EBase*>(item);
            if(ele->mMultiWin) continue;
            mOtherEles.append(ele);
        }
    }
}

void EBase::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) {
    QGraphicsObject::mouseReleaseEvent(event);
    if(Qt::LeftButton == event->button()) {
        mPressRel.setX(FLT_MAX);
        clearSnap();
        foreach(auto ele, mOtherEles) ele->clearSnap();
    }
}
#define SnapSpace 6
void EBase::mouseMoveEvent(QGraphicsSceneMouseEvent *e){
    if(! (e->buttons() & Qt::LeftButton)) return;
    if(! isSelected()) return;
    if(mFrmSec==Qt::NoSection || mPressRel.x()>=FLT_MAX) return;
    auto mousePos = e->scenePos();
    auto dstHor = mPressRel.x() + mousePos.x();
    auto dstVer = mPressRel.y() + mousePos.y();
    mLRSnap = mTBSnap = 0;
    foreach(auto ele, mOtherEles) ele->clearSnap();
    if(mFrmSec==Qt::TitleBarArea) {
        dstHor = qBound(0.0, dstHor, gProgItem->mWidth - mWidth);
        dstVer = qBound(0.0, dstVer, gProgItem->mHeight - mHeight);
        if(dstHor==0) mLRSnap = 1;
        else if(dstHor==gProgItem->mWidth - mWidth) mLRSnap = 2;
        if(dstVer==0) mTBSnap = 1;
        else if(dstVer==gProgItem->mHeight - mHeight) mTBSnap = 2;
        if(mLRSnap==0) foreach(EBase *ele, mOtherEles) {//左右
            if(fabs(dstHor - ele->x()) < SnapSpace && ele->x() <= gProgItem->mWidth - mWidth) {
                dstHor = ele->x();
                mLRSnap = 1;
                ele->mLRSnap = 1;
                ele->update();
                break;
            }
            auto eleRight = ele->x() + ele->mWidth;
            if(fabs(dstHor - eleRight) < SnapSpace && eleRight <= gProgItem->mWidth - mWidth) {
                dstHor = eleRight;
                mLRSnap = 1;
                ele->mLRSnap = 2;
                ele->update();
                break;
            }
            auto right = dstHor + mWidth;
            if(fabs(right - ele->x()) < SnapSpace && ele->x() - mWidth >= 0) {
                dstHor = ele->x() - mWidth;
                mLRSnap = 2;
                ele->mLRSnap = 1;
                ele->update();
                break;
            }
            if(fabs(right - eleRight) < SnapSpace && eleRight - mWidth >= 0) {
                dstHor = eleRight - mWidth;
                mLRSnap = 2;
                ele->mLRSnap = 2;
                ele->update();
                break;
            }
        }
        if(mTBSnap==0) foreach(EBase *ele, mOtherEles) {//上下
            if(fabs(dstVer-ele->y()) < SnapSpace && ele->y() <= gProgItem->mHeight - mHeight) {
                dstVer = ele->y();
                mTBSnap = 1;
                ele->mTBSnap = 1;
                ele->update();
                break;
            }
            auto eleBtm = ele->y() + ele->mHeight;
            if(fabs(dstVer - eleBtm) < SnapSpace && eleBtm <= gProgItem->mHeight - mHeight) {
                dstVer = eleBtm;
                mTBSnap = 1;
                ele->mTBSnap = 2;
                ele->update();
                break;
            }
            auto btm = dstVer + mHeight;
            if(fabs(btm - ele->y()) < SnapSpace && ele->y() - mHeight >= 0) {
                dstVer = ele->y() - mHeight;
                mTBSnap = 2;
                ele->mTBSnap = 1;
                ele->update();
                break;
            }
            if(fabs(btm - eleBtm) < SnapSpace && eleBtm - mHeight >= 0) {
                dstVer = eleBtm - mHeight;
                mTBSnap = 2;
                ele->mTBSnap = 2;
                ele->update();
                break;
            }
        }
        setPos(dstHor, dstVer);
    } else if(mFrmSec==Qt::BottomRightSection) {
        if(dstHor < m_handleLen) dstHor = m_handleLen;
        if(dstVer < m_handleLen) dstVer = m_handleLen;
        if(mType!=Audio && gProgItem->mWidth>0 && gProgItem->mHeight>0) {
            dstHor = qMin(dstHor, gProgItem->mWidth - x());
            dstVer = qMin(dstVer, gProgItem->mHeight - y());
        }
        setSize(dstHor, dstVer);
    } else if(mFrmSec==Qt::RightSection) {
        if(dstHor < m_handleLen) dstHor = m_handleLen;
        if(mType!=Audio && gProgItem->mWidth>0 && gProgItem->mHeight>0) dstHor = qMin(dstHor, gProgItem->mWidth - x());
        auto right = x() + dstHor;
        if(right < gProgItem->mWidth-8) foreach(EBase *ele, mOtherEles) {//左右
            if(fabs(right - ele->x()) < SnapSpace) {
                dstHor = ele->x() - x();
                mLRSnap = 2;
                ele->mLRSnap = 1;
                ele->update();
                break;
            }
            auto eleRight = ele->x() + ele->mWidth;
            if(fabs(right - eleRight) < SnapSpace) {
                dstHor = eleRight - x();
                mLRSnap = 2;
                ele->mLRSnap = 2;
                ele->update();
                break;
            }
        }
        setSize(dstHor, mPressRel.y());
    } else if(mFrmSec==Qt::BottomSection) {
        if(dstVer < m_handleLen) dstVer = m_handleLen;
        if(mType!=Audio && gProgItem->mWidth>0 && gProgItem->mHeight>0) dstVer = qMin(dstVer, gProgItem->mHeight - y());
        auto btm = y() + dstVer;
        if(btm < gProgItem->mHeight-8) foreach(EBase *ele, mOtherEles) {//上下
            auto eleBtm = ele->y() + ele->mHeight;
            if(fabs(btm - ele->y()) < SnapSpace) {
                dstVer = ele->y() - y();
                mTBSnap = 2;
                ele->mTBSnap = 1;
                ele->update();
                break;
            }
            if(fabs(btm - eleBtm) < SnapSpace) {
                dstVer = eleBtm - y();
                mTBSnap = 2;
                ele->mTBSnap = 2;
                ele->update();
                break;
            }
        }
        setSize(mPressRel.rx(), dstVer);
    } else {
        QRectF geo(x(), y(), mWidth, mHeight);
        if(mFrmSec==Qt::LeftSection) {
            dstHor = qMin(dstHor, geo.right() - m_handleLen);
            if(mType!=Audio && dstHor < 0) dstHor = 0;
            if(dstHor > 8) foreach(EBase *ele, mOtherEles) {//左右
                if(fabs(dstHor - ele->x()) < SnapSpace) {
                    dstHor = ele->x();
                    mLRSnap = 1;
                    ele->mLRSnap = 1;
                    ele->update();
                    break;
                }
                auto eleRight = ele->x() + ele->mWidth;
                if(fabs(dstHor - eleRight) < SnapSpace) {
                    dstHor = eleRight;
                    mLRSnap = 1;
                    ele->mLRSnap = 2;
                    ele->update();
                    break;
                }
            }
            geo.setLeft(dstHor);
            setX(dstHor);
        } else if(mFrmSec==Qt::TopSection) {
            dstVer = qMin(dstVer, geo.bottom() - m_handleLen);
            if(mType!=Audio && dstVer < 0) dstVer = 0;
            if(dstVer > 8) foreach(EBase *ele, mOtherEles) {//上下
                if(fabs(dstVer - ele->y()) < SnapSpace) {
                    dstVer = ele->y();
                    mTBSnap = 1;
                    ele->mTBSnap = 1;
                    ele->update();
                    break;
                }
                auto eleBtm = ele->y() + ele->mHeight;
                if(fabs(dstVer - eleBtm) < SnapSpace) {
                    dstVer = eleBtm;
                    mTBSnap = 1;
                    ele->mTBSnap = 2;
                    ele->update();
                    break;
                }
            }
            geo.setTop(dstVer);
            setY(dstVer);
        } else if(mFrmSec==Qt::TopLeftSection) {
            dstHor = qMin(dstHor, geo.right() - m_handleLen);
            dstVer = qMin(dstVer, geo.bottom() - m_handleLen);
            if(mType!=Audio) {
                if(dstHor < 0) dstHor = 0;
                if(dstVer < 0) dstVer = 0;
            }
            geo.setLeft(dstHor);
            geo.setTop(dstVer);
            setPos(dstHor, dstVer);
        } else if(mFrmSec==Qt::TopRightSection) {
            dstHor = qMax(dstHor, geo.x() + m_handleLen);
            dstVer = qMin(dstVer, geo.bottom() - m_handleLen);
            if(mType!=Audio) {
                if(dstHor > gProgItem->mWidth) dstHor = gProgItem->mWidth;
                if(dstVer < 0) dstVer = 0;
            }
            geo.setRight(dstHor);
            geo.setTop(dstVer);
            setY(dstVer);
        } else if(mFrmSec==Qt::BottomLeftSection) {
            dstHor = qMin(dstHor, geo.right() - m_handleLen);
            dstVer = qMax(dstVer, geo.y() + m_handleLen);
            if(mType!=Audio) {
                if(dstHor < 0) dstHor = 0;
                if(dstVer > gProgItem->mHeight) dstVer = gProgItem->mHeight;
            }
            geo.setLeft(dstHor);
            geo.setBottom(dstVer);
            setX(dstHor);
        }
        setSize(geo.width(), geo.height());
    }
}
void EBase::hoverMoveEvent(QGraphicsSceneHoverEvent *e) {
    setFrmSec(e->pos());
}
void EBase::hoverLeaveEvent(QGraphicsSceneHoverEvent *) {
    setFrmSecIfNeed(Qt::NoSection, Qt::ArrowCursor);
    mPressRel.setX(FLT_MAX);
}

void EBase::setFrmSec(const QPointF &pos) {
    if(m_rLT.contains(pos))      setFrmSecIfNeed(Qt::TopLeftSection, Qt::SizeFDiagCursor);
    else if(m_rT.contains(pos))  setFrmSecIfNeed(Qt::TopSection, Qt::SizeVerCursor);
    else if(m_rRT.contains(pos)) setFrmSecIfNeed(Qt::TopRightSection, Qt::SizeBDiagCursor);
    else if(m_rL.contains(pos))  setFrmSecIfNeed(Qt::LeftSection, Qt::SizeHorCursor);
    else if(m_rR.contains(pos))  setFrmSecIfNeed(Qt::RightSection, Qt::SizeHorCursor);
    else if(m_rLB.contains(pos)) setFrmSecIfNeed(Qt::BottomLeftSection, Qt::SizeBDiagCursor);
    else if(m_rB.contains(pos))  setFrmSecIfNeed(Qt::BottomSection, Qt::SizeVerCursor);
    else if(m_rRB.contains(pos)) setFrmSecIfNeed(Qt::BottomRightSection, Qt::SizeFDiagCursor);
    else if(pos.x()>=0 && pos.x()<=mWidth && pos.y()>=0 && pos.y()<=mHeight) setFrmSecIfNeed(Qt::TitleBarArea, Qt::SizeAllCursor);
    else setFrmSecIfNeed(Qt::NoSection, Qt::ArrowCursor);
}
void EBase::setFrmSecIfNeed(Qt::WindowFrameSection frmSec, Qt::CursorShape cursor) {
    if(mFrmSec==frmSec) return;
    mFrmSec = frmSec;
    if(cursor==Qt::ArrowCursor) unsetCursor();
    else setCursor(cursor);
}

QVariant EBase::itemChange(GraphicsItemChange change, const QVariant &value) {
    if(change==QGraphicsItem::ItemVisibleChange) {
        if(value.toBool()) goto end;
        if(bdTimerId>0) {
            killTimer(bdTimerId);
            bdTimerId = 0;
        }
    } else if(change==QGraphicsItem::ItemSelectedChange) {
        bool isSel = value.toBool();
        setAcceptHoverEvents(isSel);
        if(! isSel) unsetCursor();
    }
    end: return QGraphicsObject::itemChange(change, value);
}
void EBase::timerEvent(QTimerEvent *event) {
    if(event->timerId()!=bdTimerId) QGraphicsObject::timerEvent(event);
    else {
        if(bdEff.isEmpty()){
            killTimer(bdTimerId);
            bdTimerId = 0;
            return;
        }
        if(bdEff.startsWith("ro")) {
            if(bdImgIdx < 0) return;
            if(bdOff >= borderImgs[bdImgIdx].img.width() - 1) bdOff = 0;
            else bdOff++;
        } else bdOff = bdOff==0 ? 1 : 0;
        update();
    }
}

void EBase::clearSnap() {
    if(mLRSnap==0 && mTBSnap==0) return;
    mLRSnap = mTBSnap = 0;
    update();
}

void EBase::addBaseAttrWgt(QVBoxLayout *vBox) {
    if(mMultiWin!=nullptr) return;
    auto hBox = new QHBoxLayout();
    hBox->addWidget(new QLabel(tr("Area")));

    auto line = new QFrame();
    line->setFrameShape(QFrame::HLine);
    line->setFrameShadow(QFrame::Sunken);
    hBox->addWidget(line,1);

    vBox->addLayout(hBox);
    auto spacing = vBox->spacing();
    if(spacing < 0) spacing = 0;
    vBox->addSpacing(-spacing-2);

    hBox = new QHBoxLayout();
    hBox->addStretch();
    hBox->addWidget(new QLabel(tr("X")+": "));

    auto fdX = new QSpinBox();
    fdX->setRange(0, 9999);
    fdX->setValue(x());
    connect(fdX, (void(QSpinBox::*)(int))&QSpinBox::valueChanged, this, [this, fdX](int value) {
        int max = gProgItem->mWidth - mWidth;
        if(value > max) {
            value = max;
            fdX->blockSignals(true);
            fdX->setValue(value);
            fdX->blockSignals(false);
        }
        setX(value);
    });
    hBox->addWidget(fdX);

    hBox->addSpacing(10);

    hBox->addWidget(new QLabel(tr("Y")+": "));
    auto fdY = new QSpinBox();
    fdY->setRange(0, 9999);
    fdY->setValue(y());
    connect(fdY, (void(QSpinBox::*)(int))&QSpinBox::valueChanged, this, [this, fdY](int value) {
        int max = gProgItem->mHeight - mHeight;
        if(value > max) {
            value = max;
            fdY->blockSignals(true);
            fdY->setValue(value);
            fdY->blockSignals(false);
        }
        setY(value);
    });
    hBox->addWidget(fdY);
    hBox->addStretch();

    vBox->addLayout(hBox);

    hBox = new QHBoxLayout();
    hBox->addStretch();
    hBox->addWidget(new QLabel(tr("W")+": "));
    auto fdW = new QSpinBox();
    fdW->setRange(6, 9999);
    fdW->setValue(mWidth);
    connect(fdW, (void(QSpinBox::*)(int))&QSpinBox::valueChanged, this, [this, fdW](int value) {
        int max = gProgItem->mWidth - x();
        if(value > max) {
            value = max;
            fdW->blockSignals(true);
            fdW->setValue(value);
            fdW->blockSignals(false);
        }
        setSize(value, mHeight);
    });
    hBox->addWidget(fdW);

    hBox->addSpacing(10);
    hBox->addWidget(new QLabel(tr("H")+": "));
    auto fdH = new QSpinBox();
    fdH->setRange(6, 9999);
    fdH->setValue(mHeight);
    connect(fdH, (void(QSpinBox::*)(int))&QSpinBox::valueChanged, this, [this, fdH](int value) {
        int max = gProgItem->mHeight - y();
        if(value > max) {
            value = max;
            fdH->blockSignals(true);
            fdH->setValue(value);
            fdH->blockSignals(false);
        }
        setSize(mWidth, value);
    });
    hBox->addWidget(fdH);
    hBox->addStretch();

    vBox->addLayout(hBox);
    vBox->addSpacing(-spacing);

    connect(this, &EBase::xChanged, fdX, [this, fdX] {
        fdX->blockSignals(true);
        fdX->setValue(x());
        fdX->blockSignals(false);
    });
    connect(this, &EBase::yChanged, fdY, [this, fdY] {
        fdY->blockSignals(true);
        fdY->setValue(y());
        fdY->blockSignals(false);
    });
    connect(this, &EBase::sizeChanged, fdW, [this, fdW, fdH] {
        fdW->blockSignals(true);
        fdW->setValue(mWidth);
        fdW->blockSignals(false);
        fdH->blockSignals(true);
        fdH->setValue(mHeight);
        fdH->blockSignals(false);
    });

    hBox = new QHBoxLayout();
    hBox->addWidget(new QLabel(tr("Border")));

    line = new QFrame();
    line->setFrameShape(QFrame::HLine);
    line->setFrameShadow(QFrame::Sunken);
    hBox->addWidget(line, 1);

    vBox->addLayout(hBox);
    vBox->addSpacing(-spacing-2);

    hBox = new QHBoxLayout();
    hBox->setSpacing(0);

    hBox->addStretch();
    auto borderFd = new QComboBox();
    borderFd->addItem(tr("None"));
    for(int i=0; i<borderImgs.size(); i++) borderFd->addItem(QIcon(borderImgs[i].img), QString::number(borderImgs[i].img.height()), borderImgs[i].name);
    borderFd->setIconSize(QSize(borderImgMaxWidth, borderImgMaxHeight));
    connect(borderFd, (void(QComboBox::*)(int))&QComboBox::currentIndexChanged, this, [this](int idx){
        bdImgIdx = idx-1;
        if(bdImgIdx==-1 && bdTimerId>0) {
            killTimer(bdTimerId);
            bdTimerId = 0;
        }
        bdOff = 0;
        update();
        emit sizeChanged();
    });
    hBox->addWidget(borderFd);

    hBox->addSpacing(6);
    hBox->addWidget(new QLabel(tr("Effect")+":"));
    auto borderEffFd = new QComboBox();
    borderEffFd->addItem(tr("Rotate"), "rotate");
    borderEffFd->addItem(tr("Blink"), "blink");
    borderEffFd->addItem(tr("None"), "");
    connect(borderEffFd, (void(QComboBox::*)(int))&QComboBox::currentIndexChanged, this, [this, borderEffFd] {
        bdEff = borderEffFd->currentData().toString();
        if(bdTimerId>0) {
            killTimer(bdTimerId);
            bdTimerId = 0;
        }
        bdOff = 0;
        update();
    });
    hBox->addWidget(borderEffFd);

    hBox->addSpacing(6);
    hBox->addWidget(new QLabel(tr("Speed")+":"));
    auto borderSpeedFd = new QComboBox();
    borderSpeedFd->addItem(tr("Slow"), 1);
    borderSpeedFd->addItem(tr("Moderate"), 2);
    borderSpeedFd->addItem(tr("Fast"), 3);
    borderSpeedFd->setCurrentIndex(1);
    connect(borderSpeedFd, (void(QComboBox::*)(int))&QComboBox::currentIndexChanged, this, [this, borderSpeedFd] {
        bdSpeed = borderSpeedFd->currentData().toInt();
        if(bdTimerId>0) {
            killTimer(bdTimerId);
            bdTimerId = 0;
        }
        update();
    });
    hBox->addWidget(borderSpeedFd);
    hBox->addStretch();

    vBox->addLayout(hBox);

    if(bdImgIdx>-1) {
        borderFd->setCurrentIndex(bdImgIdx+1);
        select(borderEffFd, bdEff);
        select(borderSpeedFd, bdSpeed);
    }
}