File indexing completed on 2024-04-21 04:52:24

0001 /*
0002     SPDX-FileCopyrightText: 2008 Marco Gittler <g.marco@freenet.de>
0003     SPDX-FileCopyrightText: 2008 Jean-Baptiste Mardelle <jb@kdenlive.org>
0004 
0005 SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0006 */
0007 
0008 #include "graphicsscenerectmove.h"
0009 #include "kdenlivesettings.h"
0010 #include "titler/gradientwidget.h"
0011 #include "titler/titledocument.h"
0012 
0013 #include "kdenlive_debug.h"
0014 #include <QApplication>
0015 #include <QCursor>
0016 #include <QGraphicsRectItem>
0017 #include <QGraphicsSceneMouseEvent>
0018 #include <QGraphicsView>
0019 #include <QKeyEvent>
0020 #include <QList>
0021 #include <QScrollBar>
0022 #include <QTextBlock>
0023 #include <QTextCursor>
0024 #include <QTextDocument>
0025 #include <utility>
0026 
0027 static int TITLERVERSION = 0;
0028 
0029 MyQGraphicsEffect::MyQGraphicsEffect(QObject *parent)
0030     : QGraphicsEffect(parent)
0031 
0032 {
0033 }
0034 
0035 void MyQGraphicsEffect::setShadow(const QImage &image)
0036 {
0037     m_shadow = image;
0038 }
0039 
0040 void MyQGraphicsEffect::setOffset(int xOffset, int yOffset, int blur)
0041 {
0042     m_xOffset = xOffset;
0043     m_yOffset = yOffset;
0044     m_blur = blur;
0045     updateBoundingRect();
0046 }
0047 
0048 void MyQGraphicsEffect::draw(QPainter *painter)
0049 {
0050     painter->fillRect(boundingRect(), Qt::transparent);
0051     painter->drawImage(-2 * m_blur + m_xOffset, -2 * m_blur + m_yOffset, m_shadow);
0052     drawSource(painter);
0053 }
0054 
0055 MyTextItem::MyTextItem(const QString &txt, QGraphicsItem *parent)
0056     : QGraphicsTextItem(txt, parent)
0057     , m_alignment(QFlags<Qt::AlignmentFlag>())
0058 {
0059     // Disabled because cache makes text cursor invisible and borders ugly
0060     // setCacheMode(QGraphicsItem::ItemCoordinateCache);
0061     setFlag(QGraphicsItem::ItemSendsGeometryChanges, true);
0062     document()->setDocumentMargin(0);
0063     m_shadowEffect = new MyQGraphicsEffect(this);
0064     m_shadowEffect->setEnabled(false);
0065     setGraphicsEffect(m_shadowEffect);
0066     updateGeometry();
0067     connect(document(), &QTextDocument::contentsChange, this, &MyTextItem::doUpdateGeometry);
0068     updateTW(false, 2, 1, 0, 0);
0069 }
0070 
0071 Qt::Alignment MyTextItem::alignment() const
0072 {
0073     return m_alignment;
0074 }
0075 
0076 void MyTextItem::updateShadow(bool enabled, int blur, int xoffset, int yoffset, QColor color)
0077 {
0078     m_shadowOffset = QPoint(xoffset, yoffset);
0079     m_shadowBlur = blur;
0080     m_shadowColor = std::move(color);
0081     m_shadowEffect->setEnabled(enabled);
0082     m_shadowEffect->setOffset(xoffset, yoffset, blur);
0083     if (enabled) {
0084         updateShadow();
0085     }
0086     update();
0087 }
0088 
0089 void MyTextItem::setTextColor(const QColor &col)
0090 {
0091     setDefaultTextColor(col);
0092     refreshFormat();
0093 }
0094 
0095 QStringList MyTextItem::shadowInfo() const
0096 {
0097     QStringList info;
0098     info << QString::number(static_cast<int>(m_shadowEffect->isEnabled())) << m_shadowColor.name(QColor::HexArgb) << QString::number(m_shadowBlur)
0099          << QString::number(m_shadowOffset.x()) << QString::number(m_shadowOffset.y());
0100     return info;
0101 }
0102 
0103 void MyTextItem::loadShadow(const QStringList &info)
0104 {
0105     if (info.count() < 5) {
0106         return;
0107     }
0108     updateShadow((static_cast<bool>(info.at(0).toInt())), info.at(2).toInt(), info.at(3).toInt(), info.at(4).toInt(), QColor(info.at(1)));
0109 }
0110 
0111 void MyTextItem::setAlignment(Qt::Alignment alignment)
0112 {
0113     m_alignment = alignment;
0114     QTextBlockFormat format;
0115     format.setAlignment(alignment);
0116     QTextCursor cursor = textCursor(); // save cursor position
0117     int position = textCursor().position();
0118     cursor.select(QTextCursor::Document);
0119     cursor.mergeBlockFormat(format);
0120     cursor.clearSelection();
0121     cursor.setPosition(position); // restore cursor position
0122     setTextCursor(cursor);
0123 }
0124 
0125 void MyTextItem::refreshFormat()
0126 {
0127     QString gradientData = data(TitleDocument::Gradient).toString();
0128     QTextCursor cursor = textCursor();
0129     QTextCharFormat cformat;
0130     cursor.select(QTextCursor::Document);
0131     int position = textCursor().position();
0132 
0133     // Formatting can be lost on paste, since our QTextCursor gets overwritten, so re-apply all formatting here
0134     QColor fgColor = defaultTextColor();
0135     cformat.setForeground(fgColor);
0136     cformat.setFont(font());
0137 
0138     if (!gradientData.isEmpty()) {
0139         QRectF rect = boundingRect();
0140         QLinearGradient gr = GradientWidget::gradientFromString(gradientData, int(rect.width()), int(rect.height()));
0141         cformat.setForeground(QBrush(gr));
0142     }
0143 
0144     // Apply
0145     cursor.mergeCharFormat(cformat);
0146     // restore cursor position
0147     cursor.clearSelection();
0148     cursor.setPosition(position);
0149     setTextCursor(cursor);
0150 }
0151 
0152 void MyTextItem::doUpdateGeometry()
0153 {
0154     updateGeometry();
0155     // update gradient if necessary
0156     refreshFormat();
0157 
0158     QString text = toPlainText();
0159     m_path = QPainterPath();
0160     m_path.setFillRule(Qt::WindingFill);
0161     if (text.isEmpty()) {
0162         //
0163     } else {
0164         QFontMetrics metrics(font());
0165         double lineSpacing = data(TitleDocument::LineSpacing).toInt() + metrics.lineSpacing();
0166 
0167         // Calculate line width
0168         const QStringList lines = text.split(QLatin1Char('\n'));
0169         double linePos = metrics.ascent();
0170         QRectF bounding = boundingRect();
0171         /*if (lines.count() > 0) {
0172             lineSpacing = bounding.height() / lines.count();
0173             if (lineSpacing != data(TitleDocument::LineSpacing).toInt() + metrics.lineSpacing()) {
0174                 linePos = 2 * lineSpacing - metrics.descent() - metrics.height();
0175             }
0176         }*/
0177 
0178         for (const QString &line : lines) {
0179             QPainterPath linePath;
0180             linePath.addText(0, linePos, font(), line);
0181             linePos += lineSpacing;
0182             if (m_alignment == Qt::AlignHCenter) {
0183                 double offset = (bounding.width() - metrics.horizontalAdvance(line)) / 2;
0184                 linePath.translate(offset, 0);
0185             } else if (m_alignment == Qt::AlignRight) {
0186                 double offset = bounding.width() - metrics.horizontalAdvance(line);
0187                 linePath.translate(offset, 0);
0188             }
0189             m_path.addPath(linePath);
0190         }
0191     }
0192 
0193     if (m_shadowEffect->isEnabled()) {
0194         updateShadow();
0195     }
0196     update();
0197 }
0198 
0199 void MyTextItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *w)
0200 {
0201     if ((textInteractionFlags() & static_cast<int>((Qt::TextEditable) != 0)) != 0) {
0202         document()->setDocumentMargin(0);
0203         QGraphicsTextItem::paint(painter, option, w);
0204     } else {
0205         painter->setRenderHint(QPainter::Antialiasing);
0206         int outline = data(TitleDocument::OutlineWidth).toInt();
0207         QString gradientData = data(TitleDocument::Gradient).toString();
0208         QTextCursor cursor(document());
0209         cursor.select(QTextCursor::Document);
0210         QBrush paintBrush;
0211         if (gradientData.isEmpty()) {
0212             paintBrush = QBrush(cursor.charFormat().foreground().color());
0213         } else {
0214             QRectF rect = boundingRect();
0215             paintBrush = QBrush(GradientWidget::gradientFromString(gradientData, int(rect.width()), int(rect.height())));
0216         }
0217         if (TITLERVERSION < 300) {
0218             painter->fillPath(m_path, paintBrush);
0219         }
0220         if (outline > 0) {
0221             QVariant variant = data(TitleDocument::OutlineColor);
0222             QColor outlineColor = variant.value<QColor>();
0223             QPen pen(outlineColor);
0224             pen.setWidthF(outline);
0225             painter->strokePath(m_path.simplified(), pen);
0226         }
0227         if (TITLERVERSION >= 300) {
0228             painter->fillPath(m_path, paintBrush);
0229         }
0230         document()->setDocumentMargin(toPlainText().isEmpty() ? 6 : 0);
0231         if (isSelected() || toPlainText().isEmpty()) {
0232             QPen pen(isSelected() ? Qt::red : Qt::blue);
0233             pen.setStyle(Qt::DashLine);
0234             painter->setPen(pen);
0235             painter->drawRect(boundingRect());
0236         }
0237     }
0238 }
0239 
0240 void MyTextItem::updateTW(bool enabled, int step, int mode, int sigma, int seed)
0241 {
0242     m_tw_enabled = enabled;
0243     m_tw_step = step;
0244     m_tw_mode = mode;
0245     m_tw_sigma = sigma;
0246     m_tw_seed = seed;
0247 }
0248 
0249 void MyTextItem::loadTW(const QStringList &info)
0250 {
0251     if (info.count() < 5) {
0252         return;
0253     }
0254     updateTW((static_cast<bool>(info.at(0).toInt())), info.at(1).toInt(), info.at(2).toInt(), info.at(3).toInt(), info.at(4).toInt());
0255 }
0256 
0257 QStringList MyTextItem::twInfo() const
0258 {
0259     QStringList info;
0260     info << QString::number(int(m_tw_enabled)) << QString::number(m_tw_step) << QString::number(m_tw_mode) << QString::number(m_tw_sigma)
0261          << QString::number(m_tw_seed);
0262     return info;
0263 }
0264 
0265 void MyTextItem::updateShadow()
0266 {
0267     QString text = toPlainText();
0268     if (text.isEmpty()) {
0269         m_shadowEffect->setShadow(QImage());
0270         return;
0271     }
0272     QRectF bounding = boundingRect();
0273     QPainterPath path = m_path;
0274     // Calculate position of text in parent item
0275     path.translate(QPointF(2 * m_shadowBlur, 2 * m_shadowBlur));
0276     QRectF fullSize = bounding.united(path.boundingRect());
0277     QImage shadow(int(fullSize.width()) + qAbs(m_shadowOffset.x()) + 4 * m_shadowBlur, int(fullSize.height()) + qAbs(m_shadowOffset.y()) + 4 * m_shadowBlur,
0278                   QImage::Format_ARGB32_Premultiplied);
0279     shadow.fill(Qt::transparent);
0280 
0281     QPainter painter(&shadow);
0282     int outline = data(TitleDocument::OutlineWidth).toInt();
0283     if (outline > 0) {
0284         QPainterPathStroker strokePath;
0285         strokePath.setWidth(outline);
0286         QPainterPath stroke = strokePath.createStroke(path);
0287         path.addPath(stroke);
0288     }
0289     painter.fillPath(path, QBrush(m_shadowColor));
0290     painter.end();
0291     if (m_shadowBlur > 0) {
0292         blurShadow(shadow, m_shadowBlur);
0293     }
0294     m_shadowEffect->setShadow(shadow);
0295 }
0296 
0297 void MyTextItem::blurShadow(QImage &result, int radius)
0298 {
0299     int tab[] = {14, 10, 8, 6, 5, 5, 4, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2};
0300     int alpha = (radius < 1) ? 16 : (radius > 17) ? 1 : tab[radius - 1];
0301 
0302     int r1 = 0;
0303     int r2 = result.height() - 1;
0304     int c1 = 0;
0305     int c2 = result.width() - 1;
0306 
0307     int bpl = result.bytesPerLine();
0308     int rgba[4];
0309     unsigned char *p;
0310 
0311     int i1 = 0;
0312     int i2 = 3;
0313 
0314     for (int col = c1; col <= c2; col++) {
0315         p = result.scanLine(r1) + col * 4;
0316         for (int i = i1; i <= i2; i++) {
0317             rgba[i] = p[i] << 4;
0318         }
0319 
0320         p += bpl;
0321         for (int j = r1; j < r2; j++, p += bpl)
0322             for (int i = i1; i <= i2; i++) {
0323                 p[i] = uchar((rgba[i] += ((p[i] << 4) - rgba[i]) * alpha / 16) >> 4);
0324             }
0325     }
0326 
0327     for (int row = r1; row <= r2; row++) {
0328         p = result.scanLine(row) + c1 * 4;
0329         for (int i = i1; i <= i2; i++) {
0330             rgba[i] = p[i] << 4;
0331         }
0332 
0333         p += 4;
0334         for (int j = c1; j < c2; j++, p += 4)
0335             for (int i = i1; i <= i2; i++) {
0336                 p[i] = uchar((rgba[i] += ((p[i] << 4) - rgba[i]) * alpha / 16) >> 4);
0337             }
0338     }
0339 
0340     for (int col = c1; col <= c2; col++) {
0341         p = result.scanLine(r2) + col * 4;
0342         for (int i = i1; i <= i2; i++) {
0343             rgba[i] = p[i] << 4;
0344         }
0345 
0346         p -= bpl;
0347         for (int j = r1; j < r2; j++, p -= bpl)
0348             for (int i = i1; i <= i2; i++) {
0349                 p[i] = uchar((rgba[i] += ((p[i] << 4) - rgba[i]) * alpha / 16) >> 4);
0350             }
0351     }
0352 
0353     for (int row = r1; row <= r2; row++) {
0354         p = result.scanLine(row) + c2 * 4;
0355         for (int i = i1; i <= i2; i++) {
0356             rgba[i] = p[i] << 4;
0357         }
0358 
0359         p -= 4;
0360         for (int j = c1; j < c2; j++, p -= 4)
0361             for (int i = i1; i <= i2; i++) {
0362                 p[i] = uchar((rgba[i] += ((p[i] << 4) - rgba[i]) * alpha / 16) >> 4);
0363             }
0364     }
0365 }
0366 
0367 void MyTextItem::updateGeometry()
0368 {
0369     QPointF topRightPrev = boundingRect().topRight();
0370     setTextWidth(-1);
0371     setTextWidth(boundingRect().width());
0372     setAlignment(m_alignment);
0373     QPointF topRight = boundingRect().topRight();
0374 
0375     // if the text is right-aligned, then shift the container leftwards by the
0376     // same amount it grew to maintain right-alignment
0377     if (m_alignment & Qt::AlignRight) {
0378         setPos(pos() + (topRightPrev - topRight));
0379     }
0380     // likewise, shift it halfway if we're center-aligned
0381     else if (m_alignment & Qt::AlignHCenter) {
0382         setPos(pos() + (topRightPrev - topRight) / 2);
0383     }
0384 }
0385 
0386 QRectF MyTextItem::baseBoundingRect() const
0387 {
0388     // Ensure text document layout is updated
0389     document()->documentLayout();
0390     QRectF base = QGraphicsTextItem::boundingRect();
0391     QTextCursor cur(document());
0392     cur.select(QTextCursor::Document);
0393     QTextBlockFormat format = cur.blockFormat();
0394     int lineHeight = int(format.lineHeight());
0395     int lineHeight2 = QFontMetrics(font()).lineSpacing();
0396     int blkCount = document()->blockCount();
0397     int lines = 0;
0398     for (int i = 0; i < blkCount; i++) {
0399         QTextBlock block = document()->findBlockByNumber(i);
0400         lines += block.layout()->lineCount();
0401     }
0402     if (lines > 1) {
0403         base.setHeight(lines * lineHeight2 + lineHeight * (lines - 1));
0404     }
0405     return base;
0406 }
0407 
0408 QRectF MyTextItem::boundingRect() const
0409 {
0410     QRectF base = baseBoundingRect();
0411     if (m_shadowEffect->isEnabled() && m_shadowOffset.x() > 0) {
0412         base.setRight(base.right() + m_shadowOffset.x());
0413     }
0414     if (m_shadowEffect->isEnabled() && m_shadowOffset.y() > 0) {
0415         base.setBottom(base.bottom() + m_shadowOffset.y());
0416     }
0417     return base;
0418 }
0419 
0420 QVariant MyTextItem::itemChange(GraphicsItemChange change, const QVariant &value)
0421 {
0422     if (change == ItemPositionChange && (scene() != nullptr)) {
0423         QPoint newPos = value.toPoint();
0424         if (QApplication::mouseButtons() == Qt::LeftButton && (qobject_cast<GraphicsSceneRectMove *>(scene()) != nullptr)) {
0425             auto *customScene = qobject_cast<GraphicsSceneRectMove *>(scene());
0426             int gridSize = customScene->gridSize();
0427             int xV = (newPos.x() / gridSize) * gridSize;
0428             int yV = (newPos.y() / gridSize) * gridSize;
0429             if (QApplication::keyboardModifiers() == Qt::ShiftModifier) {
0430                 xV = pos().x();
0431             }
0432             if (QApplication::keyboardModifiers() == (Qt::ShiftModifier | Qt::AltModifier)) {
0433                 yV = pos().y();
0434             }
0435             newPos = QPoint(xV, yV);
0436         }
0437         return newPos;
0438     }
0439     if (change == QGraphicsItem::ItemSelectedHasChanged) {
0440         if (!value.toBool()) {
0441             // Make sure to deselect text when item loses focus
0442             QTextCursor cur(document());
0443             cur.clearSelection();
0444             setTextCursor(cur);
0445         }
0446     }
0447     return QGraphicsItem::itemChange(change, value);
0448 }
0449 
0450 void MyTextItem::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *evt)
0451 {
0452     if (textInteractionFlags() == Qt::TextEditorInteraction) {
0453         // if editor mode is already on: pass double click events on to the editor:
0454         QGraphicsTextItem::mouseDoubleClickEvent(evt);
0455         return;
0456     }
0457     // if editor mode is off:
0458     // 1. turn editor mode on and set selected and focused:
0459     // SetTextInteraction(true);
0460     setTextInteractionFlags(Qt::TextEditorInteraction);
0461     setFocus(Qt::MouseFocusReason);
0462     setCursor(QCursor(Qt::IBeamCursor));
0463     // 2. send a single click to this QGraphicsTextItem (this will set the cursor to the mouse position):
0464     // create a new mouse event with the same parameters as evt
0465     auto *click = new QGraphicsSceneMouseEvent(QEvent::GraphicsSceneMousePress);
0466     click->setButton(evt->button());
0467     click->setPos(evt->pos());
0468     QGraphicsTextItem::mousePressEvent(click);
0469     delete click; // don't forget to delete the event
0470 }
0471 
0472 MyRectItem::MyRectItem(QGraphicsItem *parent)
0473     : QGraphicsRectItem(parent)
0474 {
0475     // Disabled because cache makes text cursor invisible and borders ugly
0476     // setCacheMode(QGraphicsItem::ItemCoordinateCache);
0477     setFlag(QGraphicsItem::ItemSendsGeometryChanges, true);
0478 }
0479 
0480 void MyRectItem::setRect(const QRectF &rectangle)
0481 {
0482     QGraphicsRectItem::setRect(rectangle);
0483     if (m_rect != rectangle && !data(TitleDocument::Gradient).isNull()) {
0484         m_rect = rectangle;
0485         QLinearGradient gr = GradientWidget::gradientFromString(data(TitleDocument::Gradient).toString(), int(m_rect.width()), int(m_rect.height()));
0486         setBrush(QBrush(gr));
0487     }
0488 }
0489 
0490 QVariant MyRectItem::itemChange(GraphicsItemChange change, const QVariant &value)
0491 {
0492     if (change == ItemPositionChange && (scene() != nullptr)) {
0493         QPoint newPos = value.toPoint();
0494         if (QApplication::mouseButtons() == Qt::LeftButton && (qobject_cast<GraphicsSceneRectMove *>(scene()) != nullptr)) {
0495             auto *customScene = qobject_cast<GraphicsSceneRectMove *>(scene());
0496             int gridSize = customScene->gridSize();
0497             int xV = (newPos.x() / gridSize) * gridSize;
0498             int yV = (newPos.y() / gridSize) * gridSize;
0499             if (QApplication::keyboardModifiers() == Qt::ShiftModifier) {
0500                 xV = pos().x();
0501             }
0502             if (QApplication::keyboardModifiers() == (Qt::ShiftModifier | Qt::AltModifier)) {
0503                 yV = pos().y();
0504             }
0505             newPos = QPoint(xV, yV);
0506         }
0507         return newPos;
0508     }
0509     return QGraphicsItem::itemChange(change, value);
0510 }
0511 
0512 MyEllipseItem::MyEllipseItem(QGraphicsItem *parent)
0513     : QGraphicsEllipseItem(parent)
0514 {
0515     // Disabled because cache makes text cursor invisible and borders ugly
0516     // setCacheMode(QGraphicsItem::ItemCoordinateCache);
0517     setFlag(QGraphicsItem::ItemSendsGeometryChanges, true);
0518 }
0519 
0520 void MyEllipseItem::setRect(const QRectF &rectangle)
0521 {
0522     QGraphicsEllipseItem::setRect(rectangle);
0523     if (m_ellipse != rectangle && !data(TitleDocument::Gradient).isNull()) {
0524         m_ellipse = rectangle;
0525         QLinearGradient gr = GradientWidget::gradientFromString(data(TitleDocument::Gradient).toString(), int(m_ellipse.width()), int(m_ellipse.height()));
0526         setBrush(QBrush(gr));
0527     }
0528 }
0529 
0530 QVariant MyEllipseItem::itemChange(GraphicsItemChange change, const QVariant &value)
0531 {
0532     if (change == ItemPositionChange && (scene() != nullptr)) {
0533         QPoint newPos = value.toPoint();
0534         if (QApplication::mouseButtons() == Qt::LeftButton && (qobject_cast<GraphicsSceneRectMove *>(scene()) != nullptr)) {
0535             auto *customScene = qobject_cast<GraphicsSceneRectMove *>(scene());
0536             int gridSize = customScene->gridSize();
0537             int xV = (newPos.x() / gridSize) * gridSize;
0538             int yV = (newPos.y() / gridSize) * gridSize;
0539             if (QApplication::keyboardModifiers() == Qt::ShiftModifier) {
0540                 xV = pos().x();
0541             }
0542             if (QApplication::keyboardModifiers() == (Qt::ShiftModifier | Qt::AltModifier)) {
0543                 yV = pos().y();
0544             }
0545             newPos = QPoint(xV, yV);
0546         }
0547         return newPos;
0548     }
0549     return QGraphicsItem::itemChange(change, value);
0550 }
0551 
0552 MyPixmapItem::MyPixmapItem(const QPixmap &pixmap, QGraphicsItem *parent)
0553     : QGraphicsPixmapItem(pixmap, parent)
0554 {
0555     // Disabled because cache makes text cursor invisible and borders ugly
0556     // setCacheMode(QGraphicsItem::ItemCoordinateCache);
0557     setFlag(QGraphicsItem::ItemSendsGeometryChanges, true);
0558 }
0559 
0560 QVariant MyPixmapItem::itemChange(GraphicsItemChange change, const QVariant &value)
0561 {
0562     if (change == ItemPositionChange && (scene() != nullptr)) {
0563         QPoint newPos = value.toPoint();
0564         if (QApplication::mouseButtons() == Qt::LeftButton && (qobject_cast<GraphicsSceneRectMove *>(scene()) != nullptr)) {
0565             auto *customScene = qobject_cast<GraphicsSceneRectMove *>(scene());
0566             int gridSize = customScene->gridSize();
0567             int xV = (newPos.x() / gridSize) * gridSize;
0568             int yV = (newPos.y() / gridSize) * gridSize;
0569             if (QApplication::keyboardModifiers() == Qt::ShiftModifier) {
0570                 xV = pos().x();
0571             }
0572             if (QApplication::keyboardModifiers() == (Qt::ShiftModifier | Qt::AltModifier)) {
0573                 yV = pos().y();
0574             }
0575             newPos = QPoint(xV, yV);
0576         }
0577         return newPos;
0578     }
0579     return QGraphicsItem::itemChange(change, value);
0580 }
0581 
0582 MySvgItem::MySvgItem(const QString &fileName, QGraphicsItem *parent)
0583     : QGraphicsSvgItem(fileName, parent)
0584 {
0585     // Disabled because cache makes text cursor invisible and borders ugly
0586     // setCacheMode(QGraphicsItem::ItemCoordinateCache);
0587     setFlag(QGraphicsItem::ItemSendsGeometryChanges, true);
0588 }
0589 
0590 QVariant MySvgItem::itemChange(GraphicsItemChange change, const QVariant &value)
0591 {
0592     if (change == ItemPositionChange && (scene() != nullptr)) {
0593         QPoint newPos = value.toPoint();
0594         if (QApplication::mouseButtons() == Qt::LeftButton && (qobject_cast<GraphicsSceneRectMove *>(scene()) != nullptr)) {
0595             auto *customScene = qobject_cast<GraphicsSceneRectMove *>(scene());
0596             int gridSize = customScene->gridSize();
0597             int xV = (newPos.x() / gridSize) * gridSize;
0598             int yV = (newPos.y() / gridSize) * gridSize;
0599             if (QApplication::keyboardModifiers() == Qt::ShiftModifier) {
0600                 xV = pos().x();
0601             }
0602             if (QApplication::keyboardModifiers() == (Qt::ShiftModifier | Qt::AltModifier)) {
0603                 yV = pos().y();
0604             }
0605             newPos = QPoint(xV, yV);
0606         }
0607         return newPos;
0608     }
0609     return QGraphicsItem::itemChange(change, value);
0610 }
0611 GraphicsSceneRectMove::GraphicsSceneRectMove(int titlerVersion, QObject *parent)
0612     : QGraphicsScene(parent)
0613 
0614 {
0615     // grabMouse();
0616     TITLERVERSION = titlerVersion;
0617     m_zoom = 1.0;
0618     setBackgroundBrush(QBrush(Qt::transparent));
0619     m_fontSize = 0;
0620 }
0621 
0622 void GraphicsSceneRectMove::contextMenuEvent(QGraphicsSceneContextMenuEvent *)
0623 {
0624     // Disable QGraphicsScene standard context menu that was crashing
0625 }
0626 
0627 void GraphicsSceneRectMove::setSelectedItem(QGraphicsItem *item)
0628 {
0629     clearSelection();
0630     m_selectedItem = item;
0631     item->setSelected(true);
0632     update();
0633 }
0634 
0635 TITLETOOL GraphicsSceneRectMove::tool() const
0636 {
0637     return m_tool;
0638 }
0639 
0640 void GraphicsSceneRectMove::setTool(TITLETOOL tool)
0641 {
0642     m_tool = tool;
0643     switch (m_tool) {
0644     case TITLE_ELLIPSE:
0645     case TITLE_RECTANGLE:
0646         setCursor(Qt::CrossCursor);
0647         break;
0648     case TITLE_TEXT:
0649         setCursor(Qt::IBeamCursor);
0650         break;
0651     default:
0652         setCursor(Qt::ArrowCursor);
0653     }
0654 }
0655 
0656 void GraphicsSceneRectMove::keyPressEvent(QKeyEvent *keyEvent)
0657 {
0658     if (m_selectedItem == nullptr || !(m_selectedItem->flags() & QGraphicsItem::ItemIsMovable)) {
0659         QGraphicsScene::keyPressEvent(keyEvent);
0660         return;
0661     }
0662     if (m_selectedItem->type() == QGraphicsTextItem::Type) {
0663         auto *t = static_cast<MyTextItem *>(m_selectedItem);
0664         if ((t->textInteractionFlags() & static_cast<int>((Qt::TextEditorInteraction) != 0)) != 0) {
0665             QGraphicsScene::keyPressEvent(keyEvent);
0666             return;
0667         }
0668     }
0669     int diff = m_gridSize;
0670     if ((keyEvent->modifiers() & Qt::ControlModifier) != 0u) {
0671         diff = m_gridSize * 5;
0672     }
0673     switch (keyEvent->key()) {
0674     case Qt::Key_Left:
0675         for (QGraphicsItem *qgi : selectedItems()) {
0676             qgi->moveBy(-diff, 0);
0677         }
0678         Q_EMIT itemMoved();
0679         break;
0680     case Qt::Key_Right:
0681         for (QGraphicsItem *qgi : selectedItems()) {
0682             qgi->moveBy(diff, 0);
0683         }
0684         Q_EMIT itemMoved();
0685         break;
0686     case Qt::Key_Up:
0687         for (QGraphicsItem *qgi : selectedItems()) {
0688             qgi->moveBy(0, -diff);
0689         }
0690         Q_EMIT itemMoved();
0691         break;
0692     case Qt::Key_Down:
0693         for (QGraphicsItem *qgi : selectedItems()) {
0694             qgi->moveBy(0, diff);
0695         }
0696         Q_EMIT itemMoved();
0697         break;
0698     case Qt::Key_Delete:
0699     case Qt::Key_Backspace:
0700         for (QGraphicsItem *qgi : selectedItems()) {
0701             if (qgi->data(-1).toInt() == -1) {
0702                 continue;
0703             }
0704             removeItem(qgi);
0705             delete qgi;
0706         }
0707         m_selectedItem = nullptr;
0708         Q_EMIT selectionChanged();
0709         break;
0710     default:
0711         QGraphicsScene::keyPressEvent(keyEvent);
0712     }
0713     Q_EMIT actionFinished();
0714 }
0715 
0716 void GraphicsSceneRectMove::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *e)
0717 {
0718     QPointF p = e->scenePos();
0719     p += QPoint(-2, -2);
0720     m_resizeMode = NoResize;
0721     m_selectedItem = nullptr;
0722 
0723     // http://web.archive.org/web/20140728070013/http://www.kdenlive.org/mantis/view.php?id=1035
0724     QList<QGraphicsItem *> i = items(QRectF(p, QSizeF(4, 4)).toRect());
0725     if (i.isEmpty()) {
0726         return;
0727     }
0728 
0729     int ix = 1;
0730     QGraphicsItem *g = i.constFirst();
0731     while (!(g->flags() & QGraphicsItem::ItemIsSelectable) && ix < i.count()) {
0732         g = i.at(ix);
0733         ix++;
0734     }
0735     if ((g != nullptr) && g->type() == QGraphicsTextItem::Type && (((g->flags() & static_cast<int>((QGraphicsItem::ItemIsSelectable) != 0))) != 0)) {
0736         m_selectedItem = g;
0737     } else {
0738         Q_EMIT doubleClickEvent();
0739     }
0740     QGraphicsScene::mouseDoubleClickEvent(e);
0741 }
0742 
0743 void GraphicsSceneRectMove::mouseReleaseEvent(QGraphicsSceneMouseEvent *e)
0744 {
0745     m_pan = false;
0746     if (m_tool == TITLE_RECTANGLE && (m_selectedItem != nullptr)) {
0747         setSelectedItem(m_selectedItem);
0748     }
0749     if (m_createdText && m_selectedItem) {
0750         m_selectedItem->setSelected(true);
0751         auto *newText = static_cast<MyTextItem *>(m_selectedItem);
0752         QTextCursor cur(newText->document());
0753         cur.select(QTextCursor::Document);
0754         newText->setTextCursor(cur);
0755         m_createdText = false;
0756     }
0757     if ((e->modifiers() & Qt::ShiftModifier) != 0u) {
0758         e->accept();
0759     } else {
0760         QGraphicsScene::mouseReleaseEvent(e);
0761     }
0762     QList<QGraphicsView *> viewlist = views();
0763     if (!viewlist.isEmpty()) {
0764         viewlist.constFirst()->setDragMode(QGraphicsView::RubberBandDrag);
0765     }
0766     Q_EMIT actionFinished();
0767 }
0768 
0769 void GraphicsSceneRectMove::mousePressEvent(QGraphicsSceneMouseEvent *e)
0770 {
0771     if ((e->buttons() & Qt::MiddleButton) != 0u) {
0772         clearTextSelection();
0773         QList<QGraphicsView *> viewlist = views();
0774         if (!viewlist.isEmpty()) {
0775             viewlist.constFirst()->setDragMode(QGraphicsView::ScrollHandDrag);
0776             m_pan = true;
0777             e->accept();
0778             QGraphicsScene::mousePressEvent(e);
0779             return;
0780         }
0781     }
0782     int xPos = (int(e->scenePos().x()) / m_gridSize) * m_gridSize;
0783     int yPos = (int(e->scenePos().y()) / m_gridSize) * m_gridSize;
0784     m_moveStarted = false;
0785     m_clickPoint = e->scenePos();
0786     m_resizeMode = m_possibleAction;
0787     const QList<QGraphicsItem *> list = items(e->scenePos());
0788     QGraphicsItem *item = nullptr;
0789     if (m_tool == TITLE_SELECT) {
0790         QList<QGraphicsView *> viewlist = views();
0791         if ((e->modifiers() & Qt::ControlModifier) != 0u) {
0792             clearTextSelection();
0793             if (!viewlist.isEmpty()) {
0794                 viewlist.constFirst()->setDragMode(QGraphicsView::ScrollHandDrag);
0795                 e->ignore();
0796                 // QGraphicsScene::mousePressEvent(e);
0797                 return;
0798             }
0799         } else {
0800             if (!viewlist.isEmpty()) {
0801                 viewlist.constFirst()->setRubberBandSelectionMode(Qt::IntersectsItemShape);
0802             }
0803         }
0804         bool alreadySelected = false;
0805         for (QGraphicsItem *g : list) {
0806             // qDebug() << " - - CHECKING ITEM Z:" << g->zValue() << ", TYPE: " << g->type();
0807             // check is there is a selected item in list
0808             if (!(g->flags() & QGraphicsItem::ItemIsSelectable)) {
0809                 continue;
0810             }
0811             if (g->zValue() > -1000 /* && g->isSelected()*/) {
0812                 alreadySelected = g->isSelected();
0813                 if (!alreadySelected) {
0814                     g->setSelected(true);
0815                 }
0816                 item = g;
0817                 break;
0818             }
0819         }
0820         if (item == nullptr || (e->modifiers() != Qt::ShiftModifier && !alreadySelected)) {
0821             clearTextSelection();
0822         } else if ((e->modifiers() & Qt::ShiftModifier) != 0u) {
0823             clearTextSelection(false);
0824         }
0825         if ((item != nullptr) && ((item->flags() & QGraphicsItem::ItemIsMovable) != 0)) {
0826             m_sceneClickPoint = e->scenePos();
0827             m_selectedItem = item;
0828             // qCDebug(KDENLIVE_LOG) << "/////////  ITEM TYPE: " << item->type();
0829             if (item->type() == QGraphicsTextItem::Type) {
0830                 auto *t = static_cast<MyTextItem *>(item);
0831                 if (t->textInteractionFlags() == Qt::TextEditorInteraction) {
0832                     QGraphicsScene::mousePressEvent(e);
0833                     return;
0834                 }
0835                 t->setTextInteractionFlags(Qt::NoTextInteraction);
0836                 t->setFlag(QGraphicsItem::ItemSendsGeometryChanges, true);
0837                 setCursor(Qt::ClosedHandCursor);
0838             } else if (item->type() == QGraphicsRectItem::Type || item->type() == QGraphicsEllipseItem::Type || item->type() == QGraphicsSvgItem::Type ||
0839                        item->type() == QGraphicsPixmapItem::Type) {
0840                 QRectF r1;
0841                 if (m_selectedItem->type() == QGraphicsRectItem::Type) {
0842                     r1 = static_cast<QGraphicsRectItem *>(m_selectedItem)->rect().normalized();
0843                 } else {
0844                     r1 = m_selectedItem->boundingRect().normalized();
0845                 }
0846 
0847                 r1.translate(m_selectedItem->scenePos());
0848                 switch (m_resizeMode) {
0849                 case BottomRight:
0850                 case Right:
0851                 case Down:
0852                     m_clickPoint = r1.topLeft();
0853                     e->accept();
0854                     break;
0855                 case TopLeft:
0856                 case Left:
0857                 case Up:
0858                     m_clickPoint = r1.bottomRight();
0859                     e->accept();
0860                     break;
0861                 case TopRight:
0862                     m_clickPoint = r1.bottomLeft();
0863                     e->accept();
0864                     break;
0865                 case BottomLeft:
0866                     m_clickPoint = r1.topRight();
0867                     e->accept();
0868                     break;
0869                 default:
0870                     break;
0871                 }
0872             }
0873         }
0874         QGraphicsScene::mousePressEvent(e);
0875     } else if (m_tool == TITLE_RECTANGLE || m_tool == TITLE_ELLIPSE) {
0876         clearTextSelection();
0877         m_sceneClickPoint = QPointF(xPos, yPos);
0878         m_selectedItem = nullptr;
0879         e->ignore();
0880     } else if (m_tool == TITLE_TEXT) {
0881         if (e->button() == Qt::LeftButton) {
0882             clearTextSelection();
0883             MyTextItem *textItem = new MyTextItem(i18n("Text"), nullptr);
0884             yPos = ((int(e->scenePos().y()) - (m_fontSize / 2)) / m_gridSize) * m_gridSize;
0885             textItem->setPos(xPos, yPos);
0886             addItem(textItem);
0887             textItem->setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable);
0888             textItem->setTextInteractionFlags(Qt::TextEditorInteraction);
0889             textItem->setFocus(Qt::MouseFocusReason);
0890             textItem->setAlignment(QFlags<Qt::AlignmentFlag>(KdenliveSettings::titlerAlign()));
0891             Q_EMIT newText(textItem);
0892             m_selectedItem = textItem;
0893             m_selectedItem->setSelected(true);
0894             m_createdText = true;
0895         } else {
0896             QGraphicsScene::mousePressEvent(e);
0897         }
0898     }
0899     // qCDebug(KDENLIVE_LOG) << "//////  MOUSE CLICK, RESIZE MODE: " << m_resizeMode;
0900 }
0901 
0902 void GraphicsSceneRectMove::clearTextSelection(bool reset)
0903 {
0904     if ((m_selectedItem != nullptr) && m_selectedItem->type() == QGraphicsTextItem::Type) {
0905         // disable text editing
0906         auto *t = static_cast<MyTextItem *>(m_selectedItem);
0907         t->textCursor().setPosition(0);
0908         QTextBlock cur = t->textCursor().block();
0909         t->setTextCursor(QTextCursor(cur));
0910         t->setTextInteractionFlags(Qt::NoTextInteraction);
0911         t->unsetCursor();
0912     }
0913     if (reset) {
0914         m_selectedItem = nullptr;
0915         clearSelection();
0916     }
0917 }
0918 
0919 void GraphicsSceneRectMove::mouseMoveEvent(QGraphicsSceneMouseEvent *e)
0920 {
0921     QList<QGraphicsView *> viewlist = views();
0922     if (viewlist.isEmpty()) {
0923         e->ignore();
0924         return;
0925     }
0926     QGraphicsView *view = viewlist.constFirst();
0927     if (m_pan) {
0928         QPoint diff = e->lastScreenPos() - e->screenPos();
0929         view->horizontalScrollBar()->setValue(view->horizontalScrollBar()->value() + diff.x());
0930         view->verticalScrollBar()->setValue(view->verticalScrollBar()->value() + diff.y());
0931         e->accept();
0932         QGraphicsScene::mouseMoveEvent(e);
0933         return;
0934     }
0935     if (e->buttons() != Qt::NoButton && !m_moveStarted) {
0936         if ((view->mapFromScene(e->scenePos()) - view->mapFromScene(m_clickPoint)).manhattanLength() < QApplication::startDragDistance()) {
0937             e->ignore();
0938             return;
0939         }
0940         m_moveStarted = true;
0941     }
0942     if ((m_selectedItem != nullptr) && ((e->buttons() & Qt::LeftButton) != 0u)) {
0943         if (m_selectedItem->type() == QGraphicsRectItem::Type || m_selectedItem->type() == QGraphicsEllipseItem::Type ||
0944             m_selectedItem->type() == QGraphicsSvgItem::Type || m_selectedItem->type() == QGraphicsPixmapItem::Type) {
0945             QRectF newrect;
0946             if (m_selectedItem->type() == QGraphicsRectItem::Type) {
0947                 newrect = static_cast<QGraphicsRectItem *>(m_selectedItem)->rect();
0948             } else {
0949                 newrect = m_selectedItem->boundingRect();
0950             }
0951             int xPos = (int(e->scenePos().x()) / m_gridSize) * m_gridSize;
0952             int yPos = (int(e->scenePos().y()) / m_gridSize) * m_gridSize;
0953             QPointF newpoint(xPos, yPos);
0954             switch (m_resizeMode) {
0955             case BottomRight:
0956             case BottomLeft:
0957             case TopRight:
0958             case TopLeft:
0959                 newrect = QRectF(m_clickPoint, newpoint).normalized();
0960                 break;
0961             case Up:
0962                 newrect = QRectF(m_clickPoint, QPointF(m_clickPoint.x() - newrect.width(), newpoint.y())).normalized();
0963                 break;
0964             case Down:
0965                 newrect = QRectF(m_clickPoint, QPointF(newrect.width() + m_clickPoint.x(), newpoint.y())).normalized();
0966                 break;
0967             case Right:
0968                 newrect = QRectF(m_clickPoint, QPointF(newpoint.x(), m_clickPoint.y() + newrect.height())).normalized();
0969                 break;
0970             case Left:
0971                 newrect = QRectF(m_clickPoint, QPointF(newpoint.x(), m_clickPoint.y() - newrect.height())).normalized();
0972                 break;
0973             default:
0974                 break;
0975             }
0976 
0977             if (m_selectedItem->type() == QGraphicsRectItem::Type && m_resizeMode != NoResize) {
0978                 auto *gi = static_cast<MyRectItem *>(m_selectedItem);
0979                 // Resize using aspect ratio
0980                 if (!m_selectedItem->data(0).isNull()) {
0981                     // we want to keep aspect ratio
0982                     double hRatio = newrect.width() / m_selectedItem->data(0).toInt();
0983                     double vRatio = newrect.height() / m_selectedItem->data(1).toInt();
0984                     if (hRatio < vRatio) {
0985                         newrect.setHeight(m_selectedItem->data(1).toInt() * hRatio);
0986                     } else {
0987                         newrect.setWidth(m_selectedItem->data(0).toInt() * vRatio);
0988                     }
0989                 }
0990                 gi->setPos(newrect.topLeft());
0991                 gi->setRect(QRectF(QPointF(), newrect.bottomRight() - newrect.topLeft()));
0992                 return;
0993             }
0994             if (m_selectedItem->type() == QGraphicsEllipseItem::Type && m_resizeMode != NoResize) {
0995                 auto *gi = static_cast<MyEllipseItem *>(m_selectedItem);
0996                 // Resize using aspect ratio
0997                 if (!m_selectedItem->data(0).isNull()) {
0998                     // we want to keep aspect ratio
0999                     double hRatio = newrect.width() / m_selectedItem->data(0).toInt();
1000                     double vRatio = newrect.height() / m_selectedItem->data(1).toInt();
1001                     if (hRatio < vRatio) {
1002                         newrect.setHeight(m_selectedItem->data(1).toInt() * hRatio);
1003                     } else {
1004                         newrect.setWidth(m_selectedItem->data(0).toInt() * vRatio);
1005                     }
1006                 }
1007                 gi->setPos(newrect.topLeft());
1008                 gi->setRect(QRectF(QPointF(), newrect.bottomRight() - newrect.topLeft()));
1009                 return;
1010             }
1011             QGraphicsScene::mouseMoveEvent(e);
1012         } else if (m_selectedItem->type() == QGraphicsTextItem::Type) {
1013             auto *t = static_cast<MyTextItem *>(m_selectedItem);
1014             if ((t->textInteractionFlags() & static_cast<int>((Qt::TextEditorInteraction) != 0)) != 0) {
1015                 QGraphicsScene::mouseMoveEvent(e);
1016                 return;
1017             }
1018             QGraphicsScene::mouseMoveEvent(e);
1019             m_sceneClickPoint = e->scenePos();
1020         }
1021         Q_EMIT itemMoved();
1022     } else if (m_tool == TITLE_SELECT) {
1023         QPointF p = e->scenePos();
1024         p += QPoint(-2, -2);
1025         m_resizeMode = NoResize;
1026         bool itemFound = false;
1027         QList<QGraphicsItem *> list = items(QRectF(p, QSizeF(4, 4)).toRect());
1028         for (const QGraphicsItem *g : qAsConst(list)) {
1029             if (!(g->flags() & QGraphicsItem::ItemIsSelectable)) {
1030                 continue;
1031             }
1032             if ((g->type() == QGraphicsSvgItem::Type || g->type() == QGraphicsPixmapItem::Type) && g->zValue() > -1000) {
1033                 // image or svg item
1034                 setCursor(Qt::OpenHandCursor);
1035                 itemFound = true;
1036                 break;
1037             } else if ((g->type() == QGraphicsRectItem::Type || g->type() == QGraphicsEllipseItem::Type) && g->zValue() > -1000) {
1038                 if (view == nullptr) {
1039                     continue;
1040                 }
1041                 QRectF r1;
1042                 if (g->type() == QGraphicsRectItem::Type) {
1043                     r1 = static_cast<const QGraphicsRectItem *>(g)->rect().normalized();
1044                 } else {
1045                     r1 = static_cast<const QGraphicsEllipseItem *>(g)->rect().normalized();
1046                 }
1047                 itemFound = true;
1048 
1049                 // Item mapped coordinates
1050                 QPolygon r = g->deviceTransform(view->viewportTransform()).map(r1).toPolygon();
1051                 QPainterPath top(r.point(0));
1052                 top.lineTo(r.point(1));
1053                 QPainterPath bottom(r.point(2));
1054                 bottom.lineTo(r.point(3));
1055                 QPainterPath left(r.point(0));
1056                 left.lineTo(r.point(3));
1057                 QPainterPath right(r.point(1));
1058                 right.lineTo(r.point(2));
1059 
1060                 // The area interested by the mouse pointer
1061                 QPoint viewPos = view->mapFromScene(e->scenePos());
1062                 QPainterPath mouseArea;
1063                 QFontMetrics metrics(font());
1064                 int box = metrics.lineSpacing() / 2;
1065                 mouseArea.addRect(viewPos.x() - box, viewPos.y() - box, 2 * box, 2 * box);
1066 
1067                 // Check for collisions between the mouse and the borders
1068                 if (mouseArea.contains(r.point(0))) {
1069                     m_possibleAction = TopLeft;
1070                     setCursor(Qt::SizeFDiagCursor);
1071                 } else if (mouseArea.contains(r.point(2))) {
1072                     m_possibleAction = BottomRight;
1073                     setCursor(Qt::SizeFDiagCursor);
1074                 } else if (mouseArea.contains(r.point(1))) {
1075                     m_possibleAction = TopRight;
1076                     setCursor(Qt::SizeBDiagCursor);
1077                 } else if (mouseArea.contains(r.point(3))) {
1078                     m_possibleAction = BottomLeft;
1079                     setCursor(Qt::SizeBDiagCursor);
1080                 } else if (top.intersects(mouseArea)) {
1081                     m_possibleAction = Up;
1082                     setCursor(Qt::SizeVerCursor);
1083                 } else if (bottom.intersects(mouseArea)) {
1084                     m_possibleAction = Down;
1085                     setCursor(Qt::SizeVerCursor);
1086                 } else if (right.intersects(mouseArea)) {
1087                     m_possibleAction = Right;
1088                     setCursor(Qt::SizeHorCursor);
1089                 } else if (left.intersects(mouseArea)) {
1090                     m_possibleAction = Left;
1091                     setCursor(Qt::SizeHorCursor);
1092                 } else {
1093                     setCursor(Qt::OpenHandCursor);
1094                     m_possibleAction = NoResize;
1095                 }
1096             }
1097             break;
1098         }
1099         if (!itemFound) {
1100             m_possibleAction = NoResize;
1101             setCursor(Qt::ArrowCursor);
1102         }
1103         QGraphicsScene::mouseMoveEvent(e);
1104     } else if (m_tool == TITLE_RECTANGLE && ((e->buttons() & Qt::LeftButton) != 0u)) {
1105         if (m_selectedItem == nullptr) {
1106             // create new rect item
1107             QRectF r(0, 0, e->scenePos().x() - m_sceneClickPoint.x(), e->scenePos().y() - m_sceneClickPoint.y());
1108             r = r.normalized();
1109             auto *rect = new MyRectItem();
1110             rect->setRect(QRectF(0, 0, r.width(), r.height()));
1111             addItem(rect);
1112             m_selectedItem = rect;
1113             m_selectedItem->setPos(m_sceneClickPoint);
1114             m_selectedItem->setSelected(true);
1115             Q_EMIT newRect(rect);
1116             m_selectedItem->setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable | QGraphicsItem::ItemSendsGeometryChanges);
1117             m_resizeMode = BottomRight;
1118             QGraphicsScene::mouseMoveEvent(e);
1119         }
1120     } else if (m_tool == TITLE_ELLIPSE && ((e->buttons() & Qt::LeftButton) != 0u)) {
1121         if (m_selectedItem == nullptr) {
1122             // create new rect item
1123             QRectF r(0, 0, e->scenePos().x() - m_sceneClickPoint.x(), e->scenePos().y() - m_sceneClickPoint.y());
1124             r = r.normalized();
1125             auto *ellipse = new MyEllipseItem();
1126             ellipse->setRect(QRectF(0, 0, r.width(), r.height()));
1127             addItem(ellipse);
1128             m_selectedItem = ellipse;
1129             m_selectedItem->setPos(m_sceneClickPoint);
1130             m_selectedItem->setSelected(true);
1131             Q_EMIT newEllipse(ellipse);
1132             m_selectedItem->setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable | QGraphicsItem::ItemSendsGeometryChanges);
1133             m_resizeMode = BottomRight;
1134             QGraphicsScene::mouseMoveEvent(e);
1135         }
1136     }
1137 }
1138 
1139 void GraphicsSceneRectMove::wheelEvent(QGraphicsSceneWheelEvent *wheelEvent)
1140 {
1141     if (wheelEvent->modifiers() == Qt::ControlModifier) {
1142         QList<QGraphicsView *> viewlist = views();
1143         ////qCDebug(KDENLIVE_LOG) << wheelEvent->delta() << ' ' << zoom;
1144         if (!viewlist.isEmpty()) {
1145             if (wheelEvent->delta() > 0) {
1146                 Q_EMIT sceneZoom(true);
1147             } else {
1148                 Q_EMIT sceneZoom(false);
1149             }
1150         }
1151     } else {
1152         wheelEvent->setAccepted(false);
1153     }
1154 }
1155 
1156 void GraphicsSceneRectMove::setScale(double s)
1157 {
1158     if (m_zoom < 1.0 / 7.0 && s < 1.0) {
1159         return;
1160     }
1161     if (m_zoom > 10.0 / 7.9 && s > 1.0) {
1162         return;
1163     }
1164     QList<QGraphicsView *> viewlist = views();
1165     if (!viewlist.isEmpty()) {
1166         viewlist[0]->scale(s, s);
1167         m_zoom = m_zoom * s;
1168     }
1169     ////qCDebug(KDENLIVE_LOG)<<"//////////  ZOOM: "<<zoom;
1170 }
1171 
1172 void GraphicsSceneRectMove::setZoom(double s)
1173 {
1174     QList<QGraphicsView *> viewlist = views();
1175     if (!viewlist.isEmpty()) {
1176         viewlist[0]->resetTransform();
1177         viewlist[0]->scale(s, s);
1178         m_zoom = s;
1179     }
1180 
1181     ////qCDebug(KDENLIVE_LOG)<<"//////////  ZOOM: "<<zoom;
1182 }
1183 
1184 void GraphicsSceneRectMove::setCursor(const QCursor &c)
1185 {
1186     const QList<QGraphicsView *> l = views();
1187     for (QGraphicsView *v : l) {
1188         v->setCursor(c);
1189     }
1190 }
1191 
1192 void GraphicsSceneRectMove::slotUpdateFontSize(int s)
1193 {
1194     m_fontSize = s;
1195 }
1196 
1197 void GraphicsSceneRectMove::drawForeground(QPainter *painter, const QRectF &rect)
1198 {
1199     // draw the grid if needed
1200     if (m_gridSize <= 1) {
1201         return;
1202     }
1203 
1204     QPen pen(QColor(255, 0, 0, 100));
1205     painter->setPen(pen);
1206 
1207     qreal left = int(rect.left()) - (int(rect.left()) % m_gridSize);
1208     qreal top = int(rect.top()) - (int(rect.top()) % m_gridSize);
1209     QVector<QPointF> points;
1210     for (qreal x = left; x < rect.right(); x += m_gridSize) {
1211         for (qreal y = top; y < rect.bottom(); y += m_gridSize) {
1212             points.append(QPointF(x, y));
1213         }
1214     }
1215     painter->drawPoints(points.data(), points.size());
1216 }
1217 
1218 int GraphicsSceneRectMove::gridSize() const
1219 {
1220     return m_gridSize;
1221 }
1222 
1223 void GraphicsSceneRectMove::slotUseGrid(bool enableGrid)
1224 {
1225     m_gridSize = enableGrid ? 20 : 1;
1226 }
1227 
1228 void GraphicsSceneRectMove::addNewItem(QGraphicsItem *item)
1229 {
1230     clearSelection();
1231     addItem(item);
1232     item->setSelected(true);
1233     m_selectedItem = item;
1234 }