1 /*
2     SPDX-FileCopyrightText: 2008 Marco Gittler <g.marco@freenet.de>
3     SPDX-FileCopyrightText: 2008 Jean-Baptiste Mardelle <jb@kdenlive.org>
4 
5 SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
6 */
7 
8 #include "graphicsscenerectmove.h"
9 #include "titler/gradientwidget.h"
10 #include "titler/titledocument.h"
11 
12 #include "kdenlive_debug.h"
13 #include <QApplication>
14 #include <QCursor>
15 #include <QGraphicsRectItem>
16 #include <QGraphicsSceneMouseEvent>
17 #include <QGraphicsSvgItem>
18 #include <QGraphicsView>
19 #include <QKeyEvent>
20 #include <QList>
21 #include <QScrollBar>
22 #include <QTextBlock>
23 #include <QTextCursor>
24 #include <QTextDocument>
25 #include <utility>
26 
MyQGraphicsEffect(QObject * parent)27 MyQGraphicsEffect::MyQGraphicsEffect(QObject *parent)
28     : QGraphicsEffect(parent)
29 
30 {
31 }
32 
setShadow(const QImage & image)33 void MyQGraphicsEffect::setShadow(const QImage &image)
34 {
35     m_shadow = image;
36 }
37 
setOffset(int xOffset,int yOffset,int blur)38 void MyQGraphicsEffect::setOffset(int xOffset, int yOffset, int blur)
39 {
40     m_xOffset = xOffset;
41     m_yOffset = yOffset;
42     m_blur = blur;
43     updateBoundingRect();
44 }
45 
draw(QPainter * painter)46 void MyQGraphicsEffect::draw(QPainter *painter)
47 {
48     painter->fillRect(boundingRect(), Qt::transparent);
49     painter->drawImage(-2 * m_blur + m_xOffset, -2 * m_blur + m_yOffset, m_shadow);
50     drawSource(painter);
51 }
52 
MyTextItem(const QString & txt,QGraphicsItem * parent)53 MyTextItem::MyTextItem(const QString &txt, QGraphicsItem *parent)
54     : QGraphicsTextItem(txt, parent)
55     , m_alignment(qApp->isLeftToRight() ? Qt::AlignRight : Qt::AlignLeft)
56 {
57     //Disabled because cache makes text cursor invisible and borders ugly
58     //setCacheMode(QGraphicsItem::ItemCoordinateCache);
59     setFlag(QGraphicsItem::ItemSendsGeometryChanges, true);
60     document()->setDocumentMargin(0);
61     m_shadowEffect = new MyQGraphicsEffect(this);
62     m_shadowEffect->setEnabled(false);
63     setGraphicsEffect(m_shadowEffect);
64     updateGeometry();
65     connect(document(), SIGNAL(contentsChange(int,int,int)), this, SLOT(updateGeometry(int,int,int)));
66     updateTW(false, 2, 1, 0, 0);
67 }
68 
alignment() const69 Qt::Alignment MyTextItem::alignment() const
70 {
71     return m_alignment;
72 }
73 
updateShadow(bool enabled,int blur,int xoffset,int yoffset,QColor color)74 void MyTextItem::updateShadow(bool enabled, int blur, int xoffset, int yoffset, QColor color)
75 {
76     m_shadowOffset = QPoint(xoffset, yoffset);
77     m_shadowBlur = blur;
78     m_shadowColor = std::move(color);
79     m_shadowEffect->setEnabled(enabled);
80     m_shadowEffect->setOffset(xoffset, yoffset, blur);
81     if (enabled) {
82         updateShadow();
83     }
84     update();
85 }
86 
setTextColor(const QColor & col)87 void MyTextItem::setTextColor(const QColor &col)
88 {
89     setDefaultTextColor(col);
90     refreshFormat();
91 }
92 
shadowInfo() const93 QStringList MyTextItem::shadowInfo() const
94 {
95     QStringList info;
96     info << QString::number(static_cast<int>(m_shadowEffect->isEnabled())) << m_shadowColor.name(QColor::HexArgb) << QString::number(m_shadowBlur)
97          << QString::number(m_shadowOffset.x()) << QString::number(m_shadowOffset.y());
98     return info;
99 }
100 
loadShadow(const QStringList & info)101 void MyTextItem::loadShadow(const QStringList &info)
102 {
103     if (info.count() < 5) {
104         return;
105     }
106     updateShadow((static_cast<bool>(info.at(0).toInt())), info.at(2).toInt(), info.at(3).toInt(), info.at(4).toInt(), QColor(info.at(1)));
107 }
108 
setAlignment(Qt::Alignment alignment)109 void MyTextItem::setAlignment(Qt::Alignment alignment)
110 {
111     m_alignment = alignment;
112     QTextBlockFormat format;
113     format.setAlignment(alignment);
114     QTextCursor cursor = textCursor(); // save cursor position
115     int position = textCursor().position();
116     cursor.select(QTextCursor::Document);
117     cursor.mergeBlockFormat(format);
118     cursor.clearSelection();
119     cursor.setPosition(position); // restore cursor position
120     setTextCursor(cursor);
121 }
122 
refreshFormat()123 void MyTextItem::refreshFormat()
124 {
125     QString gradientData = data(TitleDocument::Gradient).toString();
126     QTextCursor cursor = textCursor();
127     QTextCharFormat cformat;
128     cursor.select(QTextCursor::Document);
129     int position = textCursor().position();
130 
131     // Formatting can be lost on paste, since our QTextCursor gets overwritten, so re-apply all formatting here
132     QColor fgColor = defaultTextColor();
133     cformat.setForeground(fgColor);
134     cformat.setFont(font());
135 
136     if (!gradientData.isEmpty()) {
137         QRectF rect = boundingRect();
138         QLinearGradient gr = GradientWidget::gradientFromString(gradientData, int(rect.width()), int(rect.height()));
139         cformat.setForeground(QBrush(gr));
140     }
141 
142     // Apply
143     cursor.mergeCharFormat(cformat);
144     // restore cursor position
145     cursor.clearSelection();
146     cursor.setPosition(position);
147     setTextCursor(cursor);
148 }
149 
updateGeometry(int,int,int)150 void MyTextItem::updateGeometry(int, int, int)
151 {
152     updateGeometry();
153     // update gradient if necessary
154     refreshFormat();
155 
156     QString text = toPlainText();
157     m_path = QPainterPath();
158     m_path.setFillRule(Qt::WindingFill);
159     if (text.isEmpty()) {
160         //
161     } else {
162         QFontMetrics metrics(font());
163         double lineSpacing = data(TitleDocument::LineSpacing).toInt() + metrics.lineSpacing();
164 
165         // Calculate line width
166         const QStringList lines = text.split(QLatin1Char('\n'));
167         double linePos = metrics.ascent();
168         QRectF bounding = boundingRect();
169         /*if (lines.count() > 0) {
170             lineSpacing = bounding.height() / lines.count();
171             if (lineSpacing != data(TitleDocument::LineSpacing).toInt() + metrics.lineSpacing()) {
172                 linePos = 2 * lineSpacing - metrics.descent() - metrics.height();
173             }
174         }*/
175 
176         for (const QString &line : lines) {
177             QPainterPath linePath;
178             linePath.addText(0, linePos, font(), line);
179             linePos += lineSpacing;
180             if (m_alignment == Qt::AlignHCenter) {
181                 double offset = (bounding.width() - metrics.horizontalAdvance(line)) / 2;
182                 linePath.translate(offset, 0);
183             } else if (m_alignment == Qt::AlignRight) {
184                 double offset = bounding.width() - metrics.horizontalAdvance(line);
185                 linePath.translate(offset, 0);
186             }
187             m_path.addPath(linePath);
188         }
189     }
190 
191     if (m_shadowEffect->isEnabled()) {
192         updateShadow();
193     }
194     update();
195 }
196 
paint(QPainter * painter,const QStyleOptionGraphicsItem * option,QWidget * w)197 void MyTextItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *w)
198 {
199     if ((textInteractionFlags() & static_cast<int>((Qt::TextEditable) != 0)) != 0) {
200         document()->setDocumentMargin(0);
201         QGraphicsTextItem::paint(painter, option, w);
202     } else {
203         painter->setRenderHint(QPainter::Antialiasing);
204         int outline = data(TitleDocument::OutlineWidth).toInt();
205         QString gradientData = data(TitleDocument::Gradient).toString();
206         QTextCursor cursor(document());
207         cursor.select(QTextCursor::Document);
208         QBrush paintBrush;
209         if (gradientData.isEmpty()) {
210             paintBrush = QBrush(cursor.charFormat().foreground().color());
211         } else {
212             QRectF rect = boundingRect();
213             paintBrush = QBrush(GradientWidget::gradientFromString(gradientData, int(rect.width()), int(rect.height())));
214         }
215         painter->fillPath(m_path, paintBrush);
216         if (outline > 0) {
217             QVariant variant = data(TitleDocument::OutlineColor);
218             QColor outlineColor = variant.value<QColor>();
219             QPen pen(outlineColor);
220             pen.setWidthF(outline);
221             painter->strokePath(m_path, pen);
222         }
223         document()->setDocumentMargin(toPlainText().isEmpty() ? 6 : 0);
224         if (isSelected() || toPlainText().isEmpty()) {
225             QPen pen(isSelected() ? Qt::red : Qt::blue);
226             pen.setStyle(Qt::DashLine);
227             painter->setPen(pen);
228             painter->drawRect(boundingRect());
229         }
230     }
231 }
232 
updateTW(bool enabled,int step,int mode,int sigma,int seed)233 void MyTextItem::updateTW(bool enabled, int step, int mode, int sigma, int seed)
234 {
235     tw_enabled = enabled;
236     tw_step = step;
237     tw_mode = mode;
238     tw_sigma = sigma;
239     tw_seed = seed;
240 }
241 
loadTW(const QStringList & info)242 void MyTextItem::loadTW(const QStringList &info)
243 {
244     if (info.count() < 5) {
245         return;
246     }
247     updateTW((static_cast<bool>(info.at(0).toInt())), info.at(1).toInt(),
248              info.at(2).toInt(), info.at(3).toInt(), info.at(4).toInt());
249 }
250 
twInfo() const251 QStringList MyTextItem::twInfo() const
252 {
253     QStringList info;
254     info << QString::number(tw_enabled) << QString::number(tw_step)
255          << QString::number(tw_mode)
256          << QString::number(tw_sigma) << QString::number(tw_seed);
257     return info;
258 }
259 
updateShadow()260 void MyTextItem::updateShadow()
261 {
262     QString text = toPlainText();
263     if (text.isEmpty()) {
264         m_shadowEffect->setShadow(QImage());
265         return;
266     }
267     QRectF bounding = boundingRect();
268     QPainterPath path = m_path;
269     // Calculate position of text in parent item
270     path.translate(QPointF(2 * m_shadowBlur, 2 * m_shadowBlur));
271     QRectF fullSize = bounding.united(path.boundingRect());
272     QImage shadow(int(fullSize.width()) + qAbs(m_shadowOffset.x()) + 4 * m_shadowBlur, int(fullSize.height()) + qAbs(m_shadowOffset.y()) + 4 * m_shadowBlur,
273                   QImage::Format_ARGB32_Premultiplied);
274     shadow.fill(Qt::transparent);
275     QPainter painter(&shadow);
276     painter.fillPath(path, QBrush(m_shadowColor));
277     painter.end();
278     if (m_shadowBlur > 0) {
279         blurShadow(shadow, m_shadowBlur);
280     }
281     m_shadowEffect->setShadow(shadow);
282 }
283 
blurShadow(QImage & result,int radius)284 void MyTextItem::blurShadow(QImage &result, int radius)
285 {
286     int tab[] = {14, 10, 8, 6, 5, 5, 4, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2};
287     int alpha = (radius < 1) ? 16 : (radius > 17) ? 1 : tab[radius - 1];
288 
289     int r1 = 0;
290     int r2 = result.height() - 1;
291     int c1 = 0;
292     int c2 = result.width() - 1;
293 
294     int bpl = result.bytesPerLine();
295     int rgba[4];
296     unsigned char *p;
297 
298     int i1 = 0;
299     int i2 = 3;
300 
301     for (int col = c1; col <= c2; col++) {
302         p = result.scanLine(r1) + col * 4;
303         for (int i = i1; i <= i2; i++) {
304             rgba[i] = p[i] << 4;
305         }
306 
307         p += bpl;
308         for (int j = r1; j < r2; j++, p += bpl)
309             for (int i = i1; i <= i2; i++) {
310                 p[i] = uchar((rgba[i] += ((p[i] << 4) - rgba[i]) * alpha / 16) >> 4);
311             }
312     }
313 
314     for (int row = r1; row <= r2; row++) {
315         p = result.scanLine(row) + c1 * 4;
316         for (int i = i1; i <= i2; i++) {
317             rgba[i] = p[i] << 4;
318         }
319 
320         p += 4;
321         for (int j = c1; j < c2; j++, p += 4)
322             for (int i = i1; i <= i2; i++) {
323                 p[i] = uchar((rgba[i] += ((p[i] << 4) - rgba[i]) * alpha / 16) >> 4);
324             }
325     }
326 
327     for (int col = c1; col <= c2; col++) {
328         p = result.scanLine(r2) + col * 4;
329         for (int i = i1; i <= i2; i++) {
330             rgba[i] = p[i] << 4;
331         }
332 
333         p -= bpl;
334         for (int j = r1; j < r2; j++, p -= bpl)
335             for (int i = i1; i <= i2; i++) {
336                 p[i] = uchar((rgba[i] += ((p[i] << 4) - rgba[i]) * alpha / 16) >> 4);
337             }
338     }
339 
340     for (int row = r1; row <= r2; row++) {
341         p = result.scanLine(row) + c2 * 4;
342         for (int i = i1; i <= i2; i++) {
343             rgba[i] = p[i] << 4;
344         }
345 
346         p -= 4;
347         for (int j = c1; j < c2; j++, p -= 4)
348             for (int i = i1; i <= i2; i++) {
349                 p[i] = uchar((rgba[i] += ((p[i] << 4) - rgba[i]) * alpha / 16) >> 4);
350             }
351     }
352 }
353 
updateGeometry()354 void MyTextItem::updateGeometry()
355 {
356     QPointF topRightPrev = boundingRect().topRight();
357     setTextWidth(-1);
358     setTextWidth(boundingRect().width());
359     setAlignment(m_alignment);
360     QPointF topRight = boundingRect().topRight();
361 
362     if ((m_alignment & static_cast<int>((Qt::AlignRight) != 0)) != 0) {
363         setPos(pos() + (topRightPrev - topRight));
364     }
365 }
366 
baseBoundingRect() const367 QRectF MyTextItem::baseBoundingRect() const
368 {
369     QRectF base = QGraphicsTextItem::boundingRect();
370     QTextCursor cur(document());
371     cur.select(QTextCursor::Document);
372     QTextBlockFormat format = cur.blockFormat();
373     int lineHeight = int(format.lineHeight());
374     int lineHeight2 = QFontMetrics(font()).lineSpacing();
375     int lines = document()->lineCount();
376     if (lines > 1) {
377         base.setHeight(lines * lineHeight2 + lineHeight * (lines - 1));
378     }
379     return base;
380 }
381 
boundingRect() const382 QRectF MyTextItem::boundingRect() const
383 {
384     QRectF base = baseBoundingRect();
385     if (m_shadowEffect->isEnabled() && m_shadowOffset.x() > 0) {
386         base.setRight(base.right() + m_shadowOffset.x());
387     }
388     if (m_shadowEffect->isEnabled() && m_shadowOffset.y() > 0) {
389         base.setBottom(base.bottom() + m_shadowOffset.y());
390     }
391     return base;
392 }
393 
itemChange(GraphicsItemChange change,const QVariant & value)394 QVariant MyTextItem::itemChange(GraphicsItemChange change, const QVariant &value)
395 {
396     if (change == ItemPositionChange && (scene() != nullptr)) {
397         QPoint newPos = value.toPoint();
398         if (QApplication::mouseButtons() == Qt::LeftButton && (qobject_cast<GraphicsSceneRectMove *>(scene()) != nullptr)) {
399             auto *customScene = qobject_cast<GraphicsSceneRectMove *>(scene());
400             int gridSize = customScene->gridSize();
401             int xV = (newPos.x() / gridSize) * gridSize;
402             int yV = (newPos.y() / gridSize) * gridSize;
403             if (QApplication::keyboardModifiers() == Qt::ShiftModifier) {
404                 xV = pos().x();
405             }
406             if (QApplication::keyboardModifiers() == (Qt::ShiftModifier | Qt::AltModifier)) {
407                 yV = pos().y();
408             }
409             newPos = QPoint(xV, yV);
410         }
411         return newPos;
412     }
413     if (change == QGraphicsItem::ItemSelectedHasChanged) {
414         if (!value.toBool()) {
415             // Make sure to deselect text when item loses focus
416             QTextCursor cur(document());
417             cur.clearSelection();
418             setTextCursor(cur);
419         }
420     }
421     return QGraphicsItem::itemChange(change, value);
422 }
423 
mouseDoubleClickEvent(QGraphicsSceneMouseEvent * evt)424 void MyTextItem::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *evt)
425 {
426     if (textInteractionFlags() == Qt::TextEditorInteraction) {
427         // if editor mode is already on: pass double click events on to the editor:
428         QGraphicsTextItem::mouseDoubleClickEvent(evt);
429         return;
430     }
431     // if editor mode is off:
432     // 1. turn editor mode on and set selected and focused:
433     // SetTextInteraction(true);
434     setTextInteractionFlags(Qt::TextEditorInteraction);
435     setFocus(Qt::MouseFocusReason);
436     setCursor(QCursor(Qt::IBeamCursor));
437     // 2. send a single click to this QGraphicsTextItem (this will set the cursor to the mouse position):
438     // create a new mouse event with the same parameters as evt
439     auto *click = new QGraphicsSceneMouseEvent(QEvent::GraphicsSceneMousePress);
440     click->setButton(evt->button());
441     click->setPos(evt->pos());
442     QGraphicsTextItem::mousePressEvent(click);
443     delete click; // don't forget to delete the event
444 }
445 
MyRectItem(QGraphicsItem * parent)446 MyRectItem::MyRectItem(QGraphicsItem *parent)
447     : QGraphicsRectItem(parent)
448 {
449     //Disabled because cache makes text cursor invisible and borders ugly
450     //setCacheMode(QGraphicsItem::ItemCoordinateCache);
451     setFlag(QGraphicsItem::ItemSendsGeometryChanges, true);
452 }
453 
setRect(const QRectF & rectangle)454 void MyRectItem::setRect(const QRectF &rectangle)
455 {
456     QGraphicsRectItem::setRect(rectangle);
457     if (m_rect != rectangle && !data(TitleDocument::Gradient).isNull()) {
458         m_rect = rectangle;
459         QLinearGradient gr = GradientWidget::gradientFromString(data(TitleDocument::Gradient).toString(), int(m_rect.width()), int(m_rect.height()));
460         setBrush(QBrush(gr));
461     }
462 }
463 
itemChange(GraphicsItemChange change,const QVariant & value)464 QVariant MyRectItem::itemChange(GraphicsItemChange change, const QVariant &value)
465 {
466     if (change == ItemPositionChange && (scene() != nullptr)) {
467         QPoint newPos = value.toPoint();
468         if (QApplication::mouseButtons() == Qt::LeftButton && (qobject_cast<GraphicsSceneRectMove *>(scene()) != nullptr)) {
469             auto *customScene = qobject_cast<GraphicsSceneRectMove *>(scene());
470             int gridSize = customScene->gridSize();
471             int xV = (newPos.x() / gridSize) * gridSize;
472             int yV = (newPos.y() / gridSize) * gridSize;
473             if (QApplication::keyboardModifiers() == Qt::ShiftModifier) {
474                 xV = pos().x();
475             }
476             if (QApplication::keyboardModifiers() == (Qt::ShiftModifier | Qt::AltModifier)) {
477                 yV = pos().y();
478             }
479             newPos = QPoint(xV, yV);
480         }
481         return newPos;
482     }
483     return QGraphicsItem::itemChange(change, value);
484 }
485 
MyEllipseItem(QGraphicsItem * parent)486 MyEllipseItem::MyEllipseItem(QGraphicsItem *parent)
487     : QGraphicsEllipseItem(parent)
488 {
489     //Disabled because cache makes text cursor invisible and borders ugly
490     //setCacheMode(QGraphicsItem::ItemCoordinateCache);
491     setFlag(QGraphicsItem::ItemSendsGeometryChanges, true);
492 }
493 
setRect(const QRectF & rectangle)494 void MyEllipseItem::setRect(const QRectF &rectangle)
495 {
496     QGraphicsEllipseItem::setRect(rectangle);
497     if (m_ellipse != rectangle && !data(TitleDocument::Gradient).isNull()) {
498         m_ellipse = rectangle;
499         QLinearGradient gr = GradientWidget::gradientFromString(data(TitleDocument::Gradient).toString(), int(m_ellipse.width()), int(m_ellipse.height()));
500         setBrush(QBrush(gr));
501     }
502 }
503 
itemChange(GraphicsItemChange change,const QVariant & value)504 QVariant MyEllipseItem::itemChange(GraphicsItemChange change, const QVariant &value)
505 {
506     if (change == ItemPositionChange && (scene() != nullptr)) {
507         QPoint newPos = value.toPoint();
508         if (QApplication::mouseButtons() == Qt::LeftButton && (qobject_cast<GraphicsSceneRectMove *>(scene()) != nullptr)) {
509             auto *customScene = qobject_cast<GraphicsSceneRectMove *>(scene());
510             int gridSize = customScene->gridSize();
511             int xV = (newPos.x() / gridSize) * gridSize;
512             int yV = (newPos.y() / gridSize) * gridSize;
513             if (QApplication::keyboardModifiers() == Qt::ShiftModifier) {
514                 xV = pos().x();
515             }
516             if (QApplication::keyboardModifiers() == (Qt::ShiftModifier | Qt::AltModifier)) {
517                 yV = pos().y();
518             }
519             newPos = QPoint(xV, yV);
520         }
521         return newPos;
522     }
523     return QGraphicsItem::itemChange(change, value);
524 }
525 
MyPixmapItem(const QPixmap & pixmap,QGraphicsItem * parent)526 MyPixmapItem::MyPixmapItem(const QPixmap &pixmap, QGraphicsItem *parent)
527     : QGraphicsPixmapItem(pixmap, parent)
528 {
529     //Disabled because cache makes text cursor invisible and borders ugly
530     //setCacheMode(QGraphicsItem::ItemCoordinateCache);
531     setFlag(QGraphicsItem::ItemSendsGeometryChanges, true);
532 }
533 
itemChange(GraphicsItemChange change,const QVariant & value)534 QVariant MyPixmapItem::itemChange(GraphicsItemChange change, const QVariant &value)
535 {
536     if (change == ItemPositionChange && (scene() != nullptr)) {
537         QPoint newPos = value.toPoint();
538         if (QApplication::mouseButtons() == Qt::LeftButton && (qobject_cast<GraphicsSceneRectMove *>(scene()) != nullptr)) {
539             auto *customScene = qobject_cast<GraphicsSceneRectMove *>(scene());
540             int gridSize = customScene->gridSize();
541             int xV = (newPos.x() / gridSize) * gridSize;
542             int yV = (newPos.y() / gridSize) * gridSize;
543             if (QApplication::keyboardModifiers() == Qt::ShiftModifier) {
544                 xV = pos().x();
545             }
546             if (QApplication::keyboardModifiers() == (Qt::ShiftModifier | Qt::AltModifier)) {
547                 yV = pos().y();
548             }
549             newPos = QPoint(xV, yV);
550         }
551         return newPos;
552     }
553     return QGraphicsItem::itemChange(change, value);
554 }
555 
MySvgItem(const QString & fileName,QGraphicsItem * parent)556 MySvgItem::MySvgItem(const QString &fileName, QGraphicsItem *parent)
557     : QGraphicsSvgItem(fileName, parent)
558 {
559     //Disabled because cache makes text cursor invisible and borders ugly
560     //setCacheMode(QGraphicsItem::ItemCoordinateCache);
561     setFlag(QGraphicsItem::ItemSendsGeometryChanges, true);
562 }
563 
itemChange(GraphicsItemChange change,const QVariant & value)564 QVariant MySvgItem::itemChange(GraphicsItemChange change, const QVariant &value)
565 {
566     if (change == ItemPositionChange && (scene() != nullptr)) {
567         QPoint newPos = value.toPoint();
568         if (QApplication::mouseButtons() == Qt::LeftButton && (qobject_cast<GraphicsSceneRectMove *>(scene()) != nullptr)) {
569             auto *customScene = qobject_cast<GraphicsSceneRectMove *>(scene());
570             int gridSize = customScene->gridSize();
571             int xV = (newPos.x() / gridSize) * gridSize;
572             int yV = (newPos.y() / gridSize) * gridSize;
573             if (QApplication::keyboardModifiers() == Qt::ShiftModifier) {
574                 xV = pos().x();
575             }
576             if (QApplication::keyboardModifiers() == (Qt::ShiftModifier | Qt::AltModifier)) {
577                 yV = pos().y();
578             }
579             newPos = QPoint(xV, yV);
580         }
581         return newPos;
582     }
583     return QGraphicsItem::itemChange(change, value);
584 }
GraphicsSceneRectMove(QObject * parent)585 GraphicsSceneRectMove::GraphicsSceneRectMove(QObject *parent)
586     : QGraphicsScene(parent)
587 
588 {
589     // grabMouse();
590     m_zoom = 1.0;
591     setBackgroundBrush(QBrush(Qt::transparent));
592     m_fontSize = 0;
593 }
594 
contextMenuEvent(QGraphicsSceneContextMenuEvent *)595 void GraphicsSceneRectMove::contextMenuEvent(QGraphicsSceneContextMenuEvent *)
596 {
597     // Disable QGraphicsScene standard context menu that was crashing
598 }
599 
setSelectedItem(QGraphicsItem * item)600 void GraphicsSceneRectMove::setSelectedItem(QGraphicsItem *item)
601 {
602     clearSelection();
603     m_selectedItem = item;
604     item->setSelected(true);
605     update();
606 }
607 
tool() const608 TITLETOOL GraphicsSceneRectMove::tool() const
609 {
610     return m_tool;
611 }
612 
setTool(TITLETOOL tool)613 void GraphicsSceneRectMove::setTool(TITLETOOL tool)
614 {
615     m_tool = tool;
616     switch (m_tool) {
617     case TITLE_ELLIPSE:
618     case TITLE_RECTANGLE:
619         setCursor(Qt::CrossCursor);
620         break;
621     case TITLE_TEXT:
622         setCursor(Qt::IBeamCursor);
623         break;
624     default:
625         setCursor(Qt::ArrowCursor);
626     }
627 }
628 
keyPressEvent(QKeyEvent * keyEvent)629 void GraphicsSceneRectMove::keyPressEvent(QKeyEvent *keyEvent)
630 {
631     if (m_selectedItem == nullptr || !(m_selectedItem->flags() & QGraphicsItem::ItemIsMovable)) {
632         QGraphicsScene::keyPressEvent(keyEvent);
633         return;
634     }
635     if (m_selectedItem->type() == QGraphicsTextItem::Type) {
636         auto *t = static_cast<MyTextItem *>(m_selectedItem);
637         if ((t->textInteractionFlags() & static_cast<int>((Qt::TextEditorInteraction) != 0)) != 0) {
638             QGraphicsScene::keyPressEvent(keyEvent);
639             return;
640         }
641     }
642     int diff = m_gridSize;
643     if ((keyEvent->modifiers() & Qt::ControlModifier) != 0u) {
644         diff = m_gridSize * 5;
645     }
646     switch (keyEvent->key()) {
647     case Qt::Key_Left:
648         foreach (QGraphicsItem *qgi, selectedItems()) {
649             qgi->moveBy(-diff, 0);
650         }
651         emit itemMoved();
652         break;
653     case Qt::Key_Right:
654         foreach (QGraphicsItem *qgi, selectedItems()) {
655             qgi->moveBy(diff, 0);
656         }
657         emit itemMoved();
658         break;
659     case Qt::Key_Up:
660         foreach (QGraphicsItem *qgi, selectedItems()) {
661             qgi->moveBy(0, -diff);
662         }
663         emit itemMoved();
664         break;
665     case Qt::Key_Down:
666         foreach (QGraphicsItem *qgi, selectedItems()) {
667             qgi->moveBy(0, diff);
668         }
669         emit itemMoved();
670         break;
671     case Qt::Key_Delete:
672     case Qt::Key_Backspace:
673         foreach (QGraphicsItem *qgi, selectedItems()) {
674             if (qgi->data(-1).toInt() == -1) {
675                 continue;
676             }
677             removeItem(qgi);
678             delete qgi;
679         }
680         m_selectedItem = nullptr;
681         emit selectionChanged();
682         break;
683     default:
684         QGraphicsScene::keyPressEvent(keyEvent);
685     }
686     emit actionFinished();
687 }
688 
mouseDoubleClickEvent(QGraphicsSceneMouseEvent * e)689 void GraphicsSceneRectMove::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *e)
690 {
691     QPointF p = e->scenePos();
692     p += QPoint(-2, -2);
693     m_resizeMode = NoResize;
694     m_selectedItem = nullptr;
695 
696     // http://web.archive.org/web/20140728070013/http://www.kdenlive.org/mantis/view.php?id=1035
697     QList<QGraphicsItem *> i = items(QRectF(p, QSizeF(4, 4)).toRect());
698     if (i.isEmpty()) {
699         return;
700     }
701 
702     int ix = 1;
703     QGraphicsItem *g = i.constFirst();
704     while (!(g->flags() & QGraphicsItem::ItemIsSelectable) && ix < i.count()) {
705         g = i.at(ix);
706         ix++;
707     }
708     if ((g != nullptr) && g->type() == QGraphicsTextItem::Type && (((g->flags() & static_cast<int>((QGraphicsItem::ItemIsSelectable) != 0))) != 0)) {
709         m_selectedItem = g;
710     } else {
711         emit doubleClickEvent();
712     }
713     QGraphicsScene::mouseDoubleClickEvent(e);
714 }
715 
mouseReleaseEvent(QGraphicsSceneMouseEvent * e)716 void GraphicsSceneRectMove::mouseReleaseEvent(QGraphicsSceneMouseEvent *e)
717 {
718     m_pan = false;
719     if (m_tool == TITLE_RECTANGLE && (m_selectedItem != nullptr)) {
720         setSelectedItem(m_selectedItem);
721     }
722     if (m_createdText && m_selectedItem) {
723         m_selectedItem->setSelected(true);
724         auto *newText = static_cast<MyTextItem *>(m_selectedItem);
725         QTextCursor cur(newText->document());
726         cur.select(QTextCursor::Document);
727         newText->setTextCursor(cur);
728         m_createdText = false;
729     }
730     if ((e->modifiers() & Qt::ShiftModifier) != 0u) {
731         e->accept();
732     } else {
733         QGraphicsScene::mouseReleaseEvent(e);
734     }
735     QList<QGraphicsView *> viewlist = views();
736     if (!viewlist.isEmpty()) {
737         viewlist.constFirst()->setDragMode(QGraphicsView::RubberBandDrag);
738     }
739     emit actionFinished();
740 }
741 
mousePressEvent(QGraphicsSceneMouseEvent * e)742 void GraphicsSceneRectMove::mousePressEvent(QGraphicsSceneMouseEvent *e)
743 {
744     if ((e->buttons() & Qt::MiddleButton) != 0u) {
745         clearTextSelection();
746         QList<QGraphicsView *> viewlist = views();
747         if (!viewlist.isEmpty()) {
748             viewlist.constFirst()->setDragMode(QGraphicsView::ScrollHandDrag);
749             m_pan = true;
750             e->accept();
751             QGraphicsScene::mousePressEvent(e);
752             return;
753         }
754     }
755     int xPos = (int(e->scenePos().x()) / m_gridSize) * m_gridSize;
756     int yPos = (int(e->scenePos().y()) / m_gridSize) * m_gridSize;
757     m_moveStarted = false;
758     m_clickPoint = e->scenePos();
759     m_resizeMode = m_possibleAction;
760     const QList<QGraphicsItem *> list = items(e->scenePos());
761     QGraphicsItem *item = nullptr;
762     if (m_tool == TITLE_SELECT) {
763         QList<QGraphicsView *> viewlist = views();
764         if ((e->modifiers() & Qt::ControlModifier) != 0u) {
765             clearTextSelection();
766             if (!viewlist.isEmpty()) {
767                 viewlist.constFirst()->setDragMode(QGraphicsView::ScrollHandDrag);
768                 e->ignore();
769                 // QGraphicsScene::mousePressEvent(e);
770                 return;
771             }
772         } else {
773             if (!viewlist.isEmpty()) {
774                 viewlist.constFirst()->setRubberBandSelectionMode(Qt::IntersectsItemShape);
775             }
776         }
777         bool alreadySelected = false;
778         for (QGraphicsItem *g : list) {
779             // qDebug() << " - - CHECKING ITEM Z:" << g->zValue() << ", TYPE: " << g->type();
780             // check is there is a selected item in list
781             if (!(g->flags() & QGraphicsItem::ItemIsSelectable)) {
782                 continue;
783             }
784             if (g->zValue() > -1000 /* && g->isSelected()*/) {
785                 alreadySelected = g->isSelected();
786                 if (!alreadySelected) {
787                     g->setSelected(true);
788                 }
789                 item = g;
790                 break;
791             }
792         }
793         if (item == nullptr || (e->modifiers() != Qt::ShiftModifier && !alreadySelected)) {
794             clearTextSelection();
795         } else if ((e->modifiers() & Qt::ShiftModifier) != 0u) {
796             clearTextSelection(false);
797         }
798         if ((item != nullptr) && ((item->flags() & QGraphicsItem::ItemIsMovable) != 0)) {
799             m_sceneClickPoint = e->scenePos();
800             m_selectedItem = item;
801             // qCDebug(KDENLIVE_LOG) << "/////////  ITEM TYPE: " << item->type();
802             if (item->type() == QGraphicsTextItem::Type) {
803                 auto *t = static_cast<MyTextItem *>(item);
804                 if (t->textInteractionFlags() == Qt::TextEditorInteraction) {
805                     QGraphicsScene::mousePressEvent(e);
806                     return;
807                 }
808                 t->setTextInteractionFlags(Qt::NoTextInteraction);
809                 t->setFlag(QGraphicsItem::ItemSendsGeometryChanges, true);
810                 setCursor(Qt::ClosedHandCursor);
811             } else if (item->type() == QGraphicsRectItem::Type || item->type() == QGraphicsEllipseItem::Type || item->type() == QGraphicsSvgItem::Type || item->type() == QGraphicsPixmapItem::Type) {
812                 QRectF r1;
813                 if (m_selectedItem->type() == QGraphicsRectItem::Type) {
814                     r1 = static_cast<QGraphicsRectItem *>(m_selectedItem)->rect().normalized();
815                 } else {
816                     r1 = m_selectedItem->boundingRect().normalized();
817                 }
818 
819                 r1.translate(m_selectedItem->scenePos());
820                 switch (m_resizeMode) {
821                 case BottomRight:
822                 case Right:
823                 case Down:
824                     m_clickPoint = r1.topLeft();
825                     e->accept();
826                     break;
827                 case TopLeft:
828                 case Left:
829                 case Up:
830                     m_clickPoint = r1.bottomRight();
831                     e->accept();
832                     break;
833                 case TopRight:
834                     m_clickPoint = r1.bottomLeft();
835                     e->accept();
836                     break;
837                 case BottomLeft:
838                     m_clickPoint = r1.topRight();
839                     e->accept();
840                     break;
841                 default:
842                     break;
843                 }
844             }
845         }
846         QGraphicsScene::mousePressEvent(e);
847     } else if (m_tool == TITLE_RECTANGLE || m_tool == TITLE_ELLIPSE) {
848         clearTextSelection();
849         m_sceneClickPoint = QPointF(xPos, yPos);
850         m_selectedItem = nullptr;
851         e->ignore();
852     } else if (m_tool == TITLE_TEXT) {
853         if (e->button() == Qt::LeftButton) {
854             clearTextSelection();
855             MyTextItem *textItem = new MyTextItem(i18n("Text"), nullptr);
856             yPos = ((int(e->scenePos().y()) - (m_fontSize / 2)) / m_gridSize) * m_gridSize;
857             textItem->setPos(xPos, yPos);
858             addItem(textItem);
859             textItem->setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable);
860             textItem->setTextInteractionFlags(Qt::TextEditorInteraction);
861             textItem->setFocus(Qt::MouseFocusReason);
862             emit newText(textItem);
863             m_selectedItem = textItem;
864             m_selectedItem->setSelected(true);
865             m_createdText = true;
866         } else {
867             QGraphicsScene::mousePressEvent(e);
868         }
869     }
870     // qCDebug(KDENLIVE_LOG) << "//////  MOUSE CLICK, RESIZE MODE: " << m_resizeMode;
871 }
872 
clearTextSelection(bool reset)873 void GraphicsSceneRectMove::clearTextSelection(bool reset)
874 {
875     if ((m_selectedItem != nullptr) && m_selectedItem->type() == QGraphicsTextItem::Type) {
876         // disable text editing
877         auto *t = static_cast<MyTextItem *>(m_selectedItem);
878         t->textCursor().setPosition(0);
879         QTextBlock cur = t->textCursor().block();
880         t->setTextCursor(QTextCursor(cur));
881         t->setTextInteractionFlags(Qt::NoTextInteraction);
882         t->unsetCursor();
883     }
884     if (reset) {
885         m_selectedItem = nullptr;
886         clearSelection();
887     }
888 }
889 
mouseMoveEvent(QGraphicsSceneMouseEvent * e)890 void GraphicsSceneRectMove::mouseMoveEvent(QGraphicsSceneMouseEvent *e)
891 {
892     QList<QGraphicsView *> viewlist = views();
893     if (viewlist.isEmpty()) {
894         e->ignore();
895         return;
896     }
897     QGraphicsView *view = viewlist.constFirst();
898     if (m_pan) {
899         QPoint diff = e->lastScreenPos() - e->screenPos();
900         view->horizontalScrollBar()->setValue(view->horizontalScrollBar()->value() + diff.x());
901         view->verticalScrollBar()->setValue(view->verticalScrollBar()->value() + diff.y());
902         e->accept();
903         QGraphicsScene::mouseMoveEvent(e);
904         return;
905     }
906     if (e->buttons() != Qt::NoButton && !m_moveStarted) {
907         if ((view->mapFromScene(e->scenePos()) - view->mapFromScene(m_clickPoint)).manhattanLength() < QApplication::startDragDistance()) {
908             e->ignore();
909             return;
910         }
911         m_moveStarted = true;
912     }
913     if ((m_selectedItem != nullptr) && ((e->buttons() & Qt::LeftButton) != 0u)) {
914         if (m_selectedItem->type() == QGraphicsRectItem::Type || m_selectedItem->type() == QGraphicsEllipseItem::Type || m_selectedItem->type() == QGraphicsSvgItem::Type ||
915                 m_selectedItem->type() == QGraphicsPixmapItem::Type) {
916             QRectF newrect;
917             if (m_selectedItem->type() == QGraphicsRectItem::Type) {
918                 newrect = static_cast<QGraphicsRectItem *>(m_selectedItem)->rect();
919             } else {
920                 newrect = m_selectedItem->boundingRect();
921             }
922             int xPos = (int(e->scenePos().x()) / m_gridSize) * m_gridSize;
923             int yPos = (int(e->scenePos().y()) / m_gridSize) * m_gridSize;
924             QPointF newpoint(xPos, yPos);
925             switch (m_resizeMode) {
926             case BottomRight:
927             case BottomLeft:
928             case TopRight:
929             case TopLeft:
930                 newrect = QRectF(m_clickPoint, newpoint).normalized();
931                 break;
932             case Up:
933                 newrect = QRectF(m_clickPoint, QPointF(m_clickPoint.x() - newrect.width(), newpoint.y())).normalized();
934                 break;
935             case Down:
936                 newrect = QRectF(m_clickPoint, QPointF(newrect.width() + m_clickPoint.x(), newpoint.y())).normalized();
937                 break;
938             case Right:
939                 newrect = QRectF(m_clickPoint, QPointF(newpoint.x(), m_clickPoint.y() + newrect.height())).normalized();
940                 break;
941             case Left:
942                 newrect = QRectF(m_clickPoint, QPointF(newpoint.x(), m_clickPoint.y() - newrect.height())).normalized();
943                 break;
944             default:
945                 break;
946             }
947 
948             if (m_selectedItem->type() == QGraphicsRectItem::Type && m_resizeMode != NoResize) {
949                 auto *gi = static_cast<MyRectItem *>(m_selectedItem);
950                 // Resize using aspect ratio
951                 if (!m_selectedItem->data(0).isNull()) {
952                     // we want to keep aspect ratio
953                     double hRatio = newrect.width() / m_selectedItem->data(0).toInt();
954                     double vRatio = newrect.height() / m_selectedItem->data(1).toInt();
955                     if (hRatio < vRatio) {
956                         newrect.setHeight(m_selectedItem->data(1).toInt() * hRatio);
957                     } else {
958                         newrect.setWidth(m_selectedItem->data(0).toInt() * vRatio);
959                     }
960                 }
961                 gi->setPos(newrect.topLeft());
962                 gi->setRect(QRectF(QPointF(), newrect.bottomRight() - newrect.topLeft()));
963                 return;
964             }
965             if (m_selectedItem->type() == QGraphicsEllipseItem::Type && m_resizeMode != NoResize) {
966                 auto *gi = static_cast<MyEllipseItem *>(m_selectedItem);
967                 // Resize using aspect ratio
968                 if (!m_selectedItem->data(0).isNull()) {
969                     // we want to keep aspect ratio
970                     double hRatio = newrect.width() / m_selectedItem->data(0).toInt();
971                     double vRatio = newrect.height() / m_selectedItem->data(1).toInt();
972                     if (hRatio < vRatio) {
973                         newrect.setHeight(m_selectedItem->data(1).toInt() * hRatio);
974                     } else {
975                         newrect.setWidth(m_selectedItem->data(0).toInt() * vRatio);
976                     }
977                 }
978                 gi->setPos(newrect.topLeft());
979                 gi->setRect(QRectF(QPointF(), newrect.bottomRight() - newrect.topLeft()));
980                 return;
981             }
982             QGraphicsScene::mouseMoveEvent(e);
983         } else if (m_selectedItem->type() == QGraphicsTextItem::Type) {
984             auto *t = static_cast<MyTextItem *>(m_selectedItem);
985             if ((t->textInteractionFlags() & static_cast<int>((Qt::TextEditorInteraction) != 0)) != 0) {
986                 QGraphicsScene::mouseMoveEvent(e);
987                 return;
988             }
989             QGraphicsScene::mouseMoveEvent(e);
990             m_sceneClickPoint = e->scenePos();
991         }
992         emit itemMoved();
993     } else if (m_tool == TITLE_SELECT) {
994         QPointF p = e->scenePos();
995         p += QPoint(-2, -2);
996         m_resizeMode = NoResize;
997         bool itemFound = false;
998         QList<QGraphicsItem *> list = items(QRectF(p, QSizeF(4, 4)).toRect());
999         for (const QGraphicsItem *g : qAsConst(list)) {
1000             if (!(g->flags() & QGraphicsItem::ItemIsSelectable)) {
1001                 continue;
1002             }
1003             if ((g->type() == QGraphicsSvgItem::Type || g->type() == QGraphicsPixmapItem::Type) && g->zValue() > -1000) {
1004                 // image or svg item
1005                 setCursor(Qt::OpenHandCursor);
1006                 itemFound = true;
1007                 break;
1008             } else if ((g->type() == QGraphicsRectItem::Type || g->type() == QGraphicsEllipseItem::Type) && g->zValue() > -1000) {
1009                 if (view == nullptr) {
1010                     continue;
1011                 }
1012                 QRectF r1;
1013                 if(g->type() == QGraphicsRectItem::Type) {
1014                     r1 = static_cast<const QGraphicsRectItem *>(g)->rect().normalized();
1015                 } else {
1016                     r1 = static_cast<const QGraphicsEllipseItem *>(g)->rect().normalized();
1017                 }
1018                 itemFound = true;
1019 
1020                 // Item mapped coordinates
1021                 QPolygon r = g->deviceTransform(view->viewportTransform()).map(r1).toPolygon();
1022                 QPainterPath top(r.point(0));
1023                 top.lineTo(r.point(1));
1024                 QPainterPath bottom(r.point(2));
1025                 bottom.lineTo(r.point(3));
1026                 QPainterPath left(r.point(0));
1027                 left.lineTo(r.point(3));
1028                 QPainterPath right(r.point(1));
1029                 right.lineTo(r.point(2));
1030 
1031                 // The area interested by the mouse pointer
1032                 QPoint viewPos = view->mapFromScene(e->scenePos());
1033                 QPainterPath mouseArea;
1034                 QFontMetrics metrics(font());
1035                 int box = metrics.lineSpacing() / 2;
1036                 mouseArea.addRect(viewPos.x() - box, viewPos.y() - box, 2 * box, 2 * box);
1037 
1038                 // Check for collisions between the mouse and the borders
1039                 if (mouseArea.contains(r.point(0))) {
1040                     m_possibleAction = TopLeft;
1041                     setCursor(Qt::SizeFDiagCursor);
1042                 } else if (mouseArea.contains(r.point(2))) {
1043                     m_possibleAction = BottomRight;
1044                     setCursor(Qt::SizeFDiagCursor);
1045                 } else if (mouseArea.contains(r.point(1))) {
1046                     m_possibleAction = TopRight;
1047                     setCursor(Qt::SizeBDiagCursor);
1048                 } else if (mouseArea.contains(r.point(3))) {
1049                     m_possibleAction = BottomLeft;
1050                     setCursor(Qt::SizeBDiagCursor);
1051                 } else if (top.intersects(mouseArea)) {
1052                     m_possibleAction = Up;
1053                     setCursor(Qt::SizeVerCursor);
1054                 } else if (bottom.intersects(mouseArea)) {
1055                     m_possibleAction = Down;
1056                     setCursor(Qt::SizeVerCursor);
1057                 } else if (right.intersects(mouseArea)) {
1058                     m_possibleAction = Right;
1059                     setCursor(Qt::SizeHorCursor);
1060                 } else if (left.intersects(mouseArea)) {
1061                     m_possibleAction = Left;
1062                     setCursor(Qt::SizeHorCursor);
1063                 } else {
1064                     setCursor(Qt::OpenHandCursor);
1065                     m_possibleAction = NoResize;
1066                 }
1067             }
1068             break;
1069         }
1070         if (!itemFound) {
1071             m_possibleAction = NoResize;
1072             setCursor(Qt::ArrowCursor);
1073         }
1074         QGraphicsScene::mouseMoveEvent(e);
1075     } else if (m_tool == TITLE_RECTANGLE && ((e->buttons() & Qt::LeftButton) != 0u)) {
1076         if (m_selectedItem == nullptr) {
1077             // create new rect item
1078             QRectF r(0, 0, e->scenePos().x() - m_sceneClickPoint.x(), e->scenePos().y() - m_sceneClickPoint.y());
1079             r = r.normalized();
1080             auto *rect = new MyRectItem();
1081             rect->setRect(QRectF(0, 0, r.width(), r.height()));
1082             addItem(rect);
1083             m_selectedItem = rect;
1084             m_selectedItem->setPos(m_sceneClickPoint);
1085             m_selectedItem->setSelected(true);
1086             emit newRect(rect);
1087             m_selectedItem->setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable | QGraphicsItem::ItemSendsGeometryChanges);
1088             m_resizeMode = BottomRight;
1089             QGraphicsScene::mouseMoveEvent(e);
1090         }
1091     } else if (m_tool == TITLE_ELLIPSE && ((e->buttons() & Qt::LeftButton) != 0u)) {
1092         if (m_selectedItem == nullptr) {
1093             // create new rect item
1094             QRectF r(0, 0, e->scenePos().x() - m_sceneClickPoint.x(), e->scenePos().y() - m_sceneClickPoint.y());
1095             r = r.normalized();
1096             auto *ellipse = new MyEllipseItem();
1097             ellipse->setRect(QRectF(0, 0, r.width(), r.height()));
1098             addItem(ellipse);
1099             m_selectedItem = ellipse;
1100             m_selectedItem->setPos(m_sceneClickPoint);
1101             m_selectedItem->setSelected(true);
1102             emit newEllipse(ellipse);
1103             m_selectedItem->setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable | QGraphicsItem::ItemSendsGeometryChanges);
1104             m_resizeMode = BottomRight;
1105             QGraphicsScene::mouseMoveEvent(e);
1106         }
1107     }
1108 }
1109 
wheelEvent(QGraphicsSceneWheelEvent * wheelEvent)1110 void GraphicsSceneRectMove::wheelEvent(QGraphicsSceneWheelEvent *wheelEvent)
1111 {
1112     if (wheelEvent->modifiers() == Qt::ControlModifier) {
1113         QList<QGraphicsView *> viewlist = views();
1114         ////qCDebug(KDENLIVE_LOG) << wheelEvent->delta() << ' ' << zoom;
1115         if (!viewlist.isEmpty()) {
1116             if (wheelEvent->delta() > 0) {
1117                 emit sceneZoom(true);
1118             } else {
1119                 emit sceneZoom(false);
1120             }
1121         }
1122     } else {
1123         wheelEvent->setAccepted(false);
1124     }
1125 }
1126 
setScale(double s)1127 void GraphicsSceneRectMove::setScale(double s)
1128 {
1129     if (m_zoom < 1.0 / 7.0 && s < 1.0) {
1130         return;
1131     }
1132     if (m_zoom > 10.0 / 7.9 && s > 1.0) {
1133         return;
1134     }
1135     QList<QGraphicsView *> viewlist = views();
1136     if (!viewlist.isEmpty()) {
1137         viewlist[0]->scale(s, s);
1138         m_zoom = m_zoom * s;
1139     }
1140     ////qCDebug(KDENLIVE_LOG)<<"//////////  ZOOM: "<<zoom;
1141 }
1142 
setZoom(double s)1143 void GraphicsSceneRectMove::setZoom(double s)
1144 {
1145     QList<QGraphicsView *> viewlist = views();
1146     if (!viewlist.isEmpty()) {
1147         viewlist[0]->resetTransform();
1148         viewlist[0]->scale(s, s);
1149         m_zoom = s;
1150     }
1151 
1152     ////qCDebug(KDENLIVE_LOG)<<"//////////  ZOOM: "<<zoom;
1153 }
1154 
setCursor(const QCursor & c)1155 void GraphicsSceneRectMove::setCursor(const QCursor &c)
1156 {
1157     const QList<QGraphicsView *> l = views();
1158     for (QGraphicsView *v : l) {
1159         v->setCursor(c);
1160     }
1161 }
1162 
slotUpdateFontSize(int s)1163 void GraphicsSceneRectMove::slotUpdateFontSize(int s)
1164 {
1165     m_fontSize = s;
1166 }
1167 
drawForeground(QPainter * painter,const QRectF & rect)1168 void GraphicsSceneRectMove::drawForeground(QPainter *painter, const QRectF &rect)
1169 {
1170     // draw the grid if needed
1171     if (m_gridSize <= 1) {
1172         return;
1173     }
1174 
1175     QPen pen(QColor(255, 0, 0, 100));
1176     painter->setPen(pen);
1177 
1178     qreal left = int(rect.left()) - (int(rect.left()) % m_gridSize);
1179     qreal top = int(rect.top()) - (int(rect.top()) % m_gridSize);
1180     QVector<QPointF> points;
1181     for (qreal x = left; x < rect.right(); x += m_gridSize) {
1182         for (qreal y = top; y < rect.bottom(); y += m_gridSize) {
1183             points.append(QPointF(x, y));
1184         }
1185     }
1186     painter->drawPoints(points.data(), points.size());
1187 }
1188 
gridSize() const1189 int GraphicsSceneRectMove::gridSize() const
1190 {
1191     return m_gridSize;
1192 }
1193 
slotUseGrid(bool enableGrid)1194 void GraphicsSceneRectMove::slotUseGrid(bool enableGrid)
1195 {
1196     m_gridSize = enableGrid ? 20 : 1;
1197 }
1198 
addNewItem(QGraphicsItem * item)1199 void GraphicsSceneRectMove::addNewItem(QGraphicsItem *item)
1200 {
1201     clearSelection();
1202     addItem(item);
1203     item->setSelected(true);
1204     m_selectedItem = item;
1205 }
1206