graphicsscenerectmove.cpp 36.4 KB
Newer Older
1
/***************************************************************************
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
2
 *   copyright (C) 2008 by Marco Gittler (g.marco@freenet.de)                                 *
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
 *   Copyright (C) 2008 by Jean-Baptiste Mardelle (jb@kdenlive.org)        *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA          *
 ***************************************************************************/

#include "graphicsscenerectmove.h"
22
23
#include "titler/titledocument.h"
#include "titler/gradientwidget.h"
24

Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
25
#include <QDebug>
Marco Gittler's avatar
Marco Gittler committed
26
27
#include <QGraphicsSceneMouseEvent>
#include <QGraphicsRectItem>
28
#include <QGraphicsSvgItem>
29
#include <QScrollBar>
Marco Gittler's avatar
Marco Gittler committed
30
31
#include <QGraphicsView>
#include <QCursor>
32
#include <QTextCursor>
33
#include <QTextDocument>
34
#include <QList>
35
#include <QKeyEvent>
36
#include <QApplication>
37
#include <QTextBlock>
38

39
40
41
42
MyQGraphicsEffect::MyQGraphicsEffect(QObject *parent) :
    QGraphicsEffect(parent)
    , m_xOffset(0)
    , m_yOffset(0)
43
    , m_blur(0)
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
{
}

void MyQGraphicsEffect::setShadow(QImage image)
{
    m_shadow = image;
}

void MyQGraphicsEffect::setOffset(int xOffset, int yOffset, int blur)
{
    m_xOffset = xOffset;
    m_yOffset = yOffset;
    m_blur = blur;
    updateBoundingRect();
}

void MyQGraphicsEffect::draw(QPainter *painter)
{
    painter->fillRect(boundingRect(), Qt::transparent);
    painter->drawImage(-2 * m_blur + m_xOffset, -2 * m_blur + m_yOffset, m_shadow);
    drawSource(painter);
}


68
69
70
71
MyTextItem::MyTextItem(const QString &txt, QGraphicsItem *parent) :
    QGraphicsTextItem(txt, parent)
    , m_alignment(Qt::AlignLeft)
{
72
    setCacheMode(QGraphicsItem::ItemCoordinateCache);
73
    setFlag(QGraphicsItem::ItemSendsGeometryChanges, true);
74
    document()->setDocumentMargin(0);
75
    m_shadowEffect = new MyQGraphicsEffect(this);
76
    m_shadowEffect->setEnabled(false);
77
    setGraphicsEffect(m_shadowEffect);
78
    updateGeometry();
79
80
81
82
83
84
85
86
87
    connect(document(), SIGNAL(contentsChange(int, int, int)),
            this, SLOT(updateGeometry(int, int, int)));
}

Qt::Alignment MyTextItem::alignment() const
{
    return m_alignment;
}

88
89
90
91
92
void MyTextItem::updateShadow(bool enabled, int blur, int xoffset, int yoffset, QColor color)
{
    m_shadowOffset = QPoint(xoffset, yoffset);
    m_shadowBlur = blur;
    m_shadowColor = color;
93
94
    m_shadowEffect->setEnabled(enabled);
    m_shadowEffect->setOffset(xoffset, yoffset, blur);
95
96
97
    if (enabled) {
        updateShadow();
    }
98
99
100
    update();
}

101
102
103
104
105
106
void MyTextItem::setTextColor(const QColor &col)
{
    setDefaultTextColor(col);
    refreshFormat();
}

107
108
109
QStringList MyTextItem::shadowInfo() const
{
    QStringList info;
110
    info << QString::number(m_shadowEffect->isEnabled()) << m_shadowColor.name(QColor::HexArgb) << QString::number( m_shadowBlur) << QString::number(m_shadowOffset.x()) << QString::number(m_shadowOffset.y());
111
112
113
114
115
116
117
118
119
    return info;
}

void MyTextItem::loadShadow(QStringList info)
{
    if (info.count() < 5) return;
    updateShadow((info.at(0).toInt() == true), info.at(2).toInt(), info.at(3).toInt(),info.at(4).toInt(), QColor(info.at(1)));
}

120
121
122
123
124
125
126
127
128
129
130
131
132
133
void MyTextItem::setAlignment(Qt::Alignment alignment)
{
    m_alignment = alignment;
    QTextBlockFormat format;
    format.setAlignment(alignment);
    QTextCursor cursor = textCursor();      // save cursor position
    int position = textCursor().position();
    cursor.select(QTextCursor::Document);
    cursor.mergeBlockFormat(format);
    cursor.clearSelection();
    cursor.setPosition(position);           // restore cursor position
    setTextCursor(cursor);
}

134
void MyTextItem::refreshFormat()
135
{
136
    QString gradientData = data(TitleDocument::Gradient).toString();
137
138
139
140
141
142
143
144
145
146
    QTextCursor cursor = textCursor();
    QTextCharFormat cformat;
    cursor.select(QTextCursor::Document);
    int position = textCursor().position();

    // Formatting can be lost on paste, since our QTextCursor gets overwritten, so re-apply all formatting here
    QColor fgColor = defaultTextColor();
    cformat.setForeground(fgColor);
    cformat.setFont(font());

147
148
149
150
151
    if (!gradientData.isEmpty()) {
        QRectF rect = boundingRect();
        QLinearGradient gr = GradientWidget::gradientFromString(gradientData, rect.width(), rect.height());
        cformat.setForeground(QBrush(gr));
    }
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166

    //Apply
    cursor.mergeCharFormat(cformat);
    // restore cursor position
    cursor.clearSelection();
    cursor.setPosition(position);
    setTextCursor(cursor);
}

void MyTextItem::updateGeometry(int, int, int)
{
    updateGeometry();
    // update gradient if necessary
    refreshFormat();

167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
    QString text = toPlainText();
    m_path = QPainterPath();
    if (text.isEmpty()) {
        //
    } else {
        QFontMetrics metrics(font());
        double lineSpacing = data(TitleDocument::LineSpacing).toInt() + metrics.lineSpacing();

        // Calculate line width
        QStringList lines = text.split('\n');
        double linePos = metrics.ascent();
        QRectF bounding = boundingRect();
        /*if (lines.count() > 0) {
            lineSpacing = bounding.height() / lines.count();
            if (lineSpacing != data(TitleDocument::LineSpacing).toInt() + metrics.lineSpacing()) {
                linePos = 2 * lineSpacing - metrics.descent() - metrics.height();
            }
        }*/

        foreach(const QString &line, lines)
        {
            QPainterPath linePath;
            linePath.addText(0, linePos, font(), line);
            linePos += lineSpacing;
            if ( m_alignment == Qt::AlignHCenter ) {
                double offset = (bounding.width() - metrics.width(line)) / 2;
                linePath.translate(offset, 0);
            } else if ( m_alignment == Qt::AlignRight ) {
195
                double offset = bounding.width() - metrics.width(line);
196
197
198
199
200
201
                linePath.translate(offset, 0);
            }
            m_path.addPath(linePath);
        }
    }

202
    if (m_shadowEffect->isEnabled()) {
203
204
        updateShadow();
    }
205
    update();
206
207
}

208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
void MyTextItem::paint( QPainter *painter, const QStyleOptionGraphicsItem * option, QWidget* w)
{
    if (textInteractionFlags() & Qt::TextEditable) {
        QGraphicsTextItem::paint(painter, option, w);
    }
    else {
        painter->setRenderHint(QPainter::Antialiasing);
        int outline = data(TitleDocument::OutlineWidth).toInt();
        QTextCursor cursor(document());
        cursor.select(QTextCursor::Document);
        QColor fontcolor = cursor.charFormat().foreground().color();
        painter->fillPath(m_path, QBrush(fontcolor));
        if ( outline > 0 )
        {
            QVariant variant = data(TitleDocument::OutlineColor);
            QColor outlineColor = variant.value<QColor>();
            QPen pen(outlineColor);
            pen.setWidthF(outline);
            painter->strokePath(m_path, pen);
        }
        if (isSelected()) {
            QPen pen(Qt::red);
            pen.setStyle(Qt::DashLine);
            painter->setPen(pen);
            painter->drawRect(boundingRect());
        }
    }
}

237
238
239
240
void MyTextItem::updateShadow()
{
    QString text = toPlainText();
    if (text.isEmpty()) {
241
        m_shadowEffect->setShadow(QImage());
242
243
244
        return;
    }
    QRectF bounding = boundingRect();
245
    QPainterPath path = m_path;
246
    // Calculate position of text in parent item
247
    path.translate(QPointF(2 * m_shadowBlur, 2 * m_shadowBlur));
248
    QRectF fullSize = bounding.united(path.boundingRect());
249
250
251
    QImage shadow(fullSize.width() + qAbs(m_shadowOffset.x()) + 4 * m_shadowBlur, fullSize.height() + qAbs(m_shadowOffset.y()) + 4 * m_shadowBlur, QImage::Format_ARGB32_Premultiplied);
    shadow.fill(Qt::transparent);
    QPainter painter(&shadow);
252
253
254
    painter.fillPath(path, QBrush(m_shadowColor));
    painter.end();
    if (m_shadowBlur > 0) {
255
        blurShadow(shadow, m_shadowBlur);
256
    }
257
    m_shadowEffect->setShadow(shadow);
258
259
}

260
void MyTextItem::blurShadow(QImage &result, int radius)
261
262
263
264
265
{
    int tab[] = { 14, 10, 8, 6, 5, 5, 4, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2 };
    int alpha = (radius < 1)  ? 16 : (radius > 17) ? 1 : tab[radius-1];

    int r1 = 0;
266
    int r2 = result.height() - 1;
267
    int c1 = 0;
268
    int c2 = result.width() - 1;
269

270
    int bpl = result.bytesPerLine();
271
272
273
274
275
276
277
    int rgba[4];
    unsigned char* p;

    int i1 = 0;
    int i2 = 3;

    for (int col = c1; col <= c2; col++) {
278
        p = result.scanLine(r1) + col * 4;
279
280
281
282
283
284
285
286
287
288
        for (int i = i1; i <= i2; i++)
            rgba[i] = p[i] << 4;

        p += bpl;
        for (int j = r1; j < r2; j++, p += bpl)
            for (int i = i1; i <= i2; i++)
                p[i] = (rgba[i] += ((p[i] << 4) - rgba[i]) * alpha / 16) >> 4;
    }

    for (int row = r1; row <= r2; row++) {
289
        p = result.scanLine(row) + c1 * 4;
290
291
292
293
294
295
296
297
298
299
        for (int i = i1; i <= i2; i++)
            rgba[i] = p[i] << 4;

        p += 4;
        for (int j = c1; j < c2; j++, p += 4)
            for (int i = i1; i <= i2; i++)
                p[i] = (rgba[i] += ((p[i] << 4) - rgba[i]) * alpha / 16) >> 4;
    }

    for (int col = c1; col <= c2; col++) {
300
        p = result.scanLine(r2) + col * 4;
301
302
303
304
305
306
307
308
309
310
        for (int i = i1; i <= i2; i++)
            rgba[i] = p[i] << 4;

        p -= bpl;
        for (int j = r1; j < r2; j++, p -= bpl)
            for (int i = i1; i <= i2; i++)
                p[i] = (rgba[i] += ((p[i] << 4) - rgba[i]) * alpha / 16) >> 4;
    }

    for (int row = r1; row <= r2; row++) {
311
        p = result.scanLine(row) + c2 * 4;
312
313
314
315
316
317
318
319
320
321
        for (int i = i1; i <= i2; i++)
            rgba[i] = p[i] << 4;

        p -= 4;
        for (int j = c1; j < c2; j++, p -= 4)
            for (int i = i1; i <= i2; i++)
                p[i] = (rgba[i] += ((p[i] << 4) - rgba[i]) * alpha / 16) >> 4;
    }
}

322
323
324
325
326
327
328
329
330
331
332
333
334
335
void MyTextItem::updateGeometry()
{
    QPointF topRightPrev = boundingRect().topRight();
    setTextWidth(-1);
    setTextWidth(boundingRect().width());
    setAlignment(m_alignment);
    QPointF topRight = boundingRect().topRight();
 
    if (m_alignment & Qt::AlignRight)
    {
        setPos(pos() + (topRightPrev - topRight));
    }
}

336
QRectF MyTextItem::baseBoundingRect() const
337
338
339
340
341
342
343
{
    QRectF base = QGraphicsTextItem::boundingRect();
    QTextCursor cur(document());
    cur.select(QTextCursor::Document);
    QTextBlockFormat format = cur.blockFormat();
    int lineHeight = format.lineHeight();
    int lineHeight2 = QFontMetrics(font()).lineSpacing();
344
    int lines = document()->lineCount();
345
346
347
    if (lines > 1) {
        base.setHeight(lines * lineHeight2 + lineHeight * (lines - 1));
    }
348
349
350
351
352
353
    return base;
}

QRectF MyTextItem::boundingRect() const
{
    QRectF base = baseBoundingRect();
354
    if (m_shadowEffect->isEnabled() && m_shadowOffset.x() > 0)
355
        base.setRight(base.right() + m_shadowOffset.x());
356
    if (m_shadowEffect->isEnabled() && m_shadowOffset.y() > 0)
357
        base.setBottom(base.bottom() + m_shadowOffset.y());
358
359
360
    return base;
}

361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
QVariant MyTextItem::itemChange(GraphicsItemChange change, const QVariant &value) 
{
    if (change == ItemPositionChange && scene()) {
        QPoint newPos = value.toPoint();
        if (QApplication::mouseButtons() == Qt::LeftButton && qobject_cast<GraphicsSceneRectMove*> (scene())) {
            GraphicsSceneRectMove* customScene = qobject_cast<GraphicsSceneRectMove*> (scene());
            int gridSize = customScene->gridSize();
            int xV = (newPos.x()/gridSize)*gridSize;
            int yV = (newPos.y()/gridSize)*gridSize;
            newPos = QPoint(xV, yV);
        }
        return newPos;
    } else if (change == QGraphicsItem::ItemSelectedHasChanged) {
        if (value.toBool() == false) {
            // Make sure to deselect text when item loses focus
            QTextCursor cur(document());
            cur.clearSelection();
            setTextCursor(cur);
        }
    }
    return QGraphicsItem::itemChange(change, value);
}

384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
void MyTextItem::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *evt)
{
    if(textInteractionFlags() == Qt::TextEditorInteraction)
    {
        // if editor mode is already on: pass double click events on to the editor:
        QGraphicsTextItem::mouseDoubleClickEvent(evt);
        return;
    }
    // if editor mode is off:
    // 1. turn editor mode on and set selected and focused:
    //SetTextInteraction(true);
    setTextInteractionFlags(Qt::TextEditorInteraction);
    setFocus(Qt::MouseFocusReason);
    // 2. send a single click to this QGraphicsTextItem (this will set the cursor to the mouse position):
    // create a new mouse event with the same parameters as evt
    QGraphicsSceneMouseEvent *click = new QGraphicsSceneMouseEvent(QEvent::GraphicsSceneMousePress);
    click->setButton(evt->button());
    click->setPos(evt->pos());
    QGraphicsTextItem::mousePressEvent(click);
    delete click; // don't forget to delete the event
}

406
407
408
MyRectItem::MyRectItem(QGraphicsItem *parent) :
    QGraphicsRectItem(parent)
{
409
    setCacheMode(QGraphicsItem::ItemCoordinateCache);
410
    setFlag(QGraphicsItem::ItemSendsGeometryChanges, true);
411
412
413
414
415
416
417
418
419
420
421
}

void MyRectItem::setRect(const QRectF & rectangle)
{
    QGraphicsRectItem::setRect(rectangle);
    if (m_rect != rectangle && !data(TitleDocument::Gradient).isNull()) {
        m_rect = rectangle;
        QLinearGradient gr = GradientWidget::gradientFromString(data(TitleDocument::Gradient).toString(), m_rect.width(), m_rect.height());
        setBrush(QBrush(gr));
    }
}
Marco Gittler's avatar
Marco Gittler committed
422

423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
QVariant MyRectItem::itemChange(GraphicsItemChange change, const QVariant &value) 
{
    if (change == ItemPositionChange && scene()) {
        QPoint newPos = value.toPoint();
        if (QApplication::mouseButtons() == Qt::LeftButton && qobject_cast<GraphicsSceneRectMove*> (scene())) {
            GraphicsSceneRectMove* customScene = qobject_cast<GraphicsSceneRectMove*> (scene());
            int gridSize = customScene->gridSize();
            int xV = (newPos.x()/gridSize)*gridSize;
            int yV = (newPos.y()/gridSize)*gridSize;
            newPos = QPoint(xV, yV);
        }
        return newPos;
    }
    return QGraphicsItem::itemChange(change, value);
}

MyPixmapItem::MyPixmapItem(const QPixmap &pixmap, QGraphicsItem *parent) :
    QGraphicsPixmapItem(pixmap, parent)
{
    setCacheMode(QGraphicsItem::ItemCoordinateCache);
    setFlag(QGraphicsItem::ItemSendsGeometryChanges, true);
}

QVariant MyPixmapItem::itemChange(GraphicsItemChange change, const QVariant &value) 
{
    if (change == ItemPositionChange && scene()) {
        QPoint newPos = value.toPoint();
        if (QApplication::mouseButtons() == Qt::LeftButton && qobject_cast<GraphicsSceneRectMove*> (scene())) {
            GraphicsSceneRectMove* customScene = qobject_cast<GraphicsSceneRectMove*> (scene());
            int gridSize = customScene->gridSize();
            int xV = (newPos.x()/gridSize)*gridSize;
            int yV = (newPos.y()/gridSize)*gridSize;
            newPos = QPoint(xV, yV);
        }
        return newPos;
    }
    return QGraphicsItem::itemChange(change, value);
}

MySvgItem::MySvgItem(const QString &fileName, QGraphicsItem *parent) :
    QGraphicsSvgItem(fileName, parent)
{
    setCacheMode(QGraphicsItem::ItemCoordinateCache);
    setFlag(QGraphicsItem::ItemSendsGeometryChanges, true);
}

QVariant MySvgItem::itemChange(GraphicsItemChange change, const QVariant &value) 
{
    if (change == ItemPositionChange && scene()) {
        QPoint newPos = value.toPoint();
        if (QApplication::mouseButtons() == Qt::LeftButton && qobject_cast<GraphicsSceneRectMove*> (scene())) {
            GraphicsSceneRectMove* customScene = qobject_cast<GraphicsSceneRectMove*> (scene());
            int gridSize = customScene->gridSize();
            int xV = (newPos.x()/gridSize)*gridSize;
            int yV = (newPos.y()/gridSize)*gridSize;
            newPos = QPoint(xV, yV);
        }
        return newPos;
    }
    return QGraphicsItem::itemChange(change, value);
}
484
GraphicsSceneRectMove::GraphicsSceneRectMove(QObject *parent) :
485
486
487
    QGraphicsScene(parent),
    m_selectedItem(NULL),
    m_resizeMode(NoResize),
488
    m_possibleAction(NoResize),
489
490
    m_tool(TITLE_RECTANGLE),
    m_gridSize(20),
491
    m_createdText(false),
Mikko Rapeli's avatar
Mikko Rapeli committed
492
    m_moveStarted(false),
493
    m_pan(false)
494
{
495
    //grabMouse();
496
    m_zoom = 1.0;
497
    setBackgroundBrush(QBrush(Qt::transparent));
498
    m_fontSize = 0;
Marco Gittler's avatar
Marco Gittler committed
499
500
}

501
502
void GraphicsSceneRectMove::setSelectedItem(QGraphicsItem *item)
{
503
    clearSelection();
504
505
    m_selectedItem = item;
    item->setSelected(true);
506
507
508
    update();
}

509
TITLETOOL GraphicsSceneRectMove::tool() const
510
{
511
512
513
    return m_tool;
}

514
515
void GraphicsSceneRectMove::setTool(TITLETOOL tool)
{
516
517
518
519
520
521
522
523
524
525
526
    m_tool = tool;
    switch (m_tool) {
    case TITLE_RECTANGLE:
        setCursor(Qt::CrossCursor);
        break;
    case TITLE_TEXT:
        setCursor(Qt::IBeamCursor);
        break;
    default:
        setCursor(Qt::ArrowCursor);
    }
527
528
}

529
530
void GraphicsSceneRectMove::keyPressEvent(QKeyEvent * keyEvent)
{
531
    if (m_selectedItem == NULL || !(m_selectedItem->flags() & QGraphicsItem::ItemIsMovable)) {
532
533
534
        QGraphicsScene::keyPressEvent(keyEvent);
        return;
    }
535
    if (m_selectedItem->type() == QGraphicsTextItem::Type) {
536
        MyTextItem *t = static_cast<MyTextItem *>(m_selectedItem);
537
538
539
540
        if (t->textInteractionFlags() & Qt::TextEditorInteraction) {
            QGraphicsScene::keyPressEvent(keyEvent);
            return;
        }
541
    }
542
543
    int diff = m_gridSize;
    if (keyEvent->modifiers() & Qt::ControlModifier) diff = m_gridSize * 5;
544
545
    switch (keyEvent->key()) {
    case Qt::Key_Left:
546
        foreach (QGraphicsItem *qgi, selectedItems()) { qgi->moveBy(-diff,0); }
547
        emit itemMoved();
548
549
        break;
    case Qt::Key_Right:
550
        foreach (QGraphicsItem *qgi, selectedItems()) { qgi->moveBy( diff,0); }
551
        emit itemMoved();
552
553
        break;
    case Qt::Key_Up:
554
        foreach (QGraphicsItem *qgi, selectedItems()) { qgi->moveBy(0,-diff); }
555
        emit itemMoved();
556
557
        break;
    case Qt::Key_Down:
558
        foreach (QGraphicsItem *qgi, selectedItems()) { qgi->moveBy(0, diff); }
559
560
561
562
        emit itemMoved();
        break;
    case Qt::Key_Delete:
    case Qt::Key_Backspace:
563
        foreach (QGraphicsItem *qgi, selectedItems()) {
564
565
566
            if (qgi->data(-1).toInt() == -1) continue;
            removeItem(qgi);
            delete qgi;
567
        }
568
569
        m_selectedItem = NULL;
        emit selectionChanged();
570
571
572
573
        break;
    default:
        QGraphicsScene::keyPressEvent(keyEvent);
    }
574
    emit actionFinished();
575
576
}

577
578
void GraphicsSceneRectMove::mouseDoubleClickEvent(QGraphicsSceneMouseEvent* e)
{
579
580
    QPointF p = e->scenePos();
    p += QPoint(-2, -2);
581
    m_resizeMode = NoResize;
582
    m_selectedItem = NULL;
583
584
585

    // http://www.kdenlive.org/mantis/view.php?id=1035
    QList<QGraphicsItem*> i = items(QRectF(p , QSizeF(4, 4)).toRect());
586
    if (i.isEmpty()) return;
587

588
    int ix = 1;
589
    QGraphicsItem* g = i.first();
590
591
592
593
594
    while (!(g->flags() & QGraphicsItem::ItemIsSelectable) && ix < i.count()) {
        g = i.at(ix);
        ix++;
    }
    if (g && g->type() == QGraphicsTextItem::Type && g->flags() & QGraphicsItem::ItemIsSelectable) {
595
596
        m_selectedItem = g;
    } else emit doubleClickEvent();
597
598
    QGraphicsScene::mouseDoubleClickEvent(e);
}
599

600
601
void GraphicsSceneRectMove::mouseReleaseEvent(QGraphicsSceneMouseEvent *e)
{
602
    m_pan = false;
603
    if (m_tool == TITLE_RECTANGLE && m_selectedItem) setSelectedItem(m_selectedItem);
604
605
606
607
608
609
610
611
    if (m_createdText) {
        m_selectedItem->setSelected(true);
        MyTextItem *newText = static_cast<MyTextItem*>(m_selectedItem);
        QTextCursor cur(newText->document());
        cur.select(QTextCursor::Document);
        newText->setTextCursor(cur);
        m_createdText = false;
    }
612
613
614
615
616
617
618
619
620
    if (e->modifiers() & Qt::ShiftModifier) {
        e->accept();
    } else {
        QGraphicsScene::mouseReleaseEvent(e);
    }
    QList<QGraphicsView*> viewlist = views();
    if (!viewlist.isEmpty()) {
        viewlist.first()->setDragMode(QGraphicsView::RubberBandDrag);
    }
621
    emit actionFinished();
622
623
}

624
625
void GraphicsSceneRectMove::mousePressEvent(QGraphicsSceneMouseEvent* e)
{
626
627
628
629
630
631
632
633
634
635
636
    if (e->buttons() & Qt::MiddleButton) {
        clearTextSelection();
        QList<QGraphicsView*> viewlist = views();
        if (!viewlist.isEmpty()) {
            viewlist.first()->setDragMode(QGraphicsView::ScrollHandDrag);
            m_pan = true;
            e->accept();
            QGraphicsScene::mousePressEvent(e);
            return;
        }
    }
637
638
    int xPos = ((int) e->scenePos().x() / m_gridSize) * m_gridSize;
    int yPos = ((int) e->scenePos().y() / m_gridSize) * m_gridSize;
639
640
    m_moveStarted = false;
    m_clickPoint = e->scenePos();
641
    m_resizeMode = m_possibleAction;
642
    const QList <QGraphicsItem *> list = items(e->scenePos());
643
    QGraphicsItem *item = NULL;
644
    if (m_tool == TITLE_SELECT) {
645
646
        QList<QGraphicsView*> viewlist = views();
        if (e->modifiers() & Qt::ControlModifier) {
647
            clearTextSelection();
648
649
650
651
652
653
654
655
656
657
658
659
660
            if (!viewlist.isEmpty()) {
                viewlist.first()->setDragMode(QGraphicsView::ScrollHandDrag);
                e->ignore();
                //QGraphicsScene::mousePressEvent(e);
                return;
            }
        } else {
            if (!viewlist.isEmpty()) {
                viewlist.first()->setRubberBandSelectionMode(Qt::IntersectsItemShape);
            }
        }
        QList<QGraphicsItem *> selected = selectedItems();
        bool alreadySelected = false;
661
        foreach(QGraphicsItem *g, list) {
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
662
            //qDebug() << " - - CHECKING ITEM Z:" << g->zValue() << ", TYPE: " << g->type();
663
            // check is there is a selected item in list
664
665
666
667
            if (!(g->flags() & QGraphicsItem::ItemIsSelectable)) {
                continue;
            }
            if (g->zValue() > -1000/* && g->isSelected()*/) {
668
                alreadySelected = g->isSelected();
669
                g->setSelected(true);
670
671
672
673
                item = g;
                break;
            }
        }
674
675
676
        if (item == NULL || (e->modifiers() != Qt::ShiftModifier && !alreadySelected)) {
            clearTextSelection();
        } else if (item && item->flags() & QGraphicsItem::ItemIsMovable) {
677
            m_sceneClickPoint = e->scenePos();
678
            m_selectedItem = item;
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
679
            //qDebug() << "/////////  ITEM TYPE: " << item->type();
680
            if (item->type() == QGraphicsTextItem::Type) {
681
                MyTextItem *t = static_cast<MyTextItem *>(item);
682
683
684
685
686
                if (t->textInteractionFlags() == Qt::TextEditorInteraction) {
                    QGraphicsScene::mousePressEvent(e);
                    return;
                }
                t->setTextInteractionFlags(Qt::NoTextInteraction);
687
                t->setFlag(QGraphicsItem::ItemSendsGeometryChanges, true);
688
                setCursor(Qt::ClosedHandCursor);
689
            } else if (item->type() == QGraphicsRectItem::Type || item->type() == QGraphicsSvgItem::Type || item->type() == QGraphicsPixmapItem::Type) {
690
                QRectF r1;
691
                if (m_selectedItem->type() == QGraphicsRectItem::Type)
692
                    r1 = ((QGraphicsRectItem*)m_selectedItem)->rect().normalized();
693
                else
694
695
                    r1 = m_selectedItem->boundingRect().normalized();

696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
                r1.translate(m_selectedItem->scenePos());
                switch (m_resizeMode) {
                    case BottomRight:
                    case Right:
                    case Down:
                        m_clickPoint = r1.topLeft();
                        e->accept();
                        break;
                    case TopLeft:
                    case Left:
                    case Up:
                        m_clickPoint = r1.bottomRight();
                        e->accept();
                        break;
                    case TopRight:
                        m_clickPoint = r1.bottomLeft();
                        e->accept();
                        break;
                    case BottomLeft:
                        m_clickPoint = r1.topRight();
                        e->accept();
                        break;
                    default:
                        break;
                }
721
722
            }
        }
723
        QGraphicsScene::mousePressEvent(e);
724
    } else if (m_tool == TITLE_RECTANGLE) {
725
        clearTextSelection();
726
        m_sceneClickPoint = QPointF(xPos, yPos);
727
        m_selectedItem = NULL;
728
        e->ignore();
729
    } else if (m_tool == TITLE_TEXT) {
730
        clearTextSelection();
731
        MyTextItem *textItem = new MyTextItem(i18n("Text"), NULL);
732
733
        yPos = (((int) e->scenePos().y() - (int)(m_fontSize / 2)) / m_gridSize) * m_gridSize;
        textItem->setPos(xPos, yPos);
734
735
736
        addItem(textItem);
        textItem->setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable);
        textItem->setTextInteractionFlags(Qt::TextEditorInteraction);
737
        textItem->setFocus(Qt::MouseFocusReason);
738
        emit newText(textItem);
739
        m_selectedItem = textItem;
740
        m_selectedItem->setSelected(true);
741
        m_createdText = true;
742
    }
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
743
    //qDebug() << "//////  MOUSE CLICK, RESIZE MODE: " << m_resizeMode;
744
745
}

746
747
void GraphicsSceneRectMove::clearTextSelection()
{
748
    if (m_selectedItem && m_selectedItem->type() == QGraphicsTextItem::Type) {
749
        // disable text editing
750
        MyTextItem *t = static_cast<MyTextItem *>(m_selectedItem);
751
752
753
754
755
756
757
758
        t->textCursor().setPosition(0);
        QTextBlock cur = t->textCursor().block();
        t->setTextCursor(QTextCursor(cur));
        t->setTextInteractionFlags(Qt::NoTextInteraction);
    }
    m_selectedItem = NULL;
    clearSelection();
}
759

760
761
void GraphicsSceneRectMove::mouseMoveEvent(QGraphicsSceneMouseEvent* e)
{
762
763
764
765
766
767
768
769
770
771
772
773
774
775
    QList<QGraphicsView*> viewlist = views();
    if (viewlist.isEmpty()) {
        e->ignore();
        return;
    }
    QGraphicsView *view = viewlist.first();
    if (m_pan) {
        QPoint diff = e->lastScreenPos() - e->screenPos();
        view->horizontalScrollBar()->setValue(view->horizontalScrollBar()->value() + diff.x());
        view->verticalScrollBar()->setValue(view->verticalScrollBar()->value() + diff.y());
        e->accept();
        QGraphicsScene::mouseMoveEvent(e);
        return;
    }
776
777
778
    if (e->buttons() != Qt::NoButton && !m_moveStarted) {
        if ((view->mapFromScene(e->scenePos()) - view->mapFromScene(m_clickPoint)).manhattanLength() < QApplication::startDragDistance()) {
            e->ignore();
779
            QGraphicsScene::mouseMoveEvent(e);
780
            return;
781
782
        } else {
            m_moveStarted = true;
783
        }
784
    }
785
    if (m_selectedItem && (e->buttons() & Qt::LeftButton)) {
786
        if (m_selectedItem->type() == QGraphicsRectItem::Type || m_selectedItem->type() == QGraphicsSvgItem::Type || m_selectedItem->type() == QGraphicsPixmapItem::Type) {
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
787
            QRectF newrect;
788
            if (m_selectedItem->type() == QGraphicsRectItem::Type)
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
789
                newrect = ((QGraphicsRectItem*)m_selectedItem)->rect();
790
791
            else
                newrect = m_selectedItem->boundingRect();
792
793
794
            int xPos = ((int) e->scenePos().x() / m_gridSize) * m_gridSize;
            int yPos = ((int) e->scenePos().y() / m_gridSize) * m_gridSize;
            QPointF newpoint(xPos, yPos);
795
            switch (m_resizeMode) {
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
                case BottomRight:
                case BottomLeft:
                case TopRight:
                case TopLeft:
                    newrect = QRectF(m_clickPoint, newpoint).normalized();
                    break;
                case Up:
                    newrect = QRectF(m_clickPoint, QPointF(m_clickPoint.x() - newrect.width(), newpoint.y())).normalized();
                    break;
                case Down:
                    newrect = QRectF(m_clickPoint, QPointF(newrect.width() + m_clickPoint.x(), newpoint.y())).normalized();
                    break;
                case Right:
                    newrect = QRectF(m_clickPoint, QPointF(newpoint.x(), m_clickPoint.y() + newrect.height())).normalized();
                    break;
                case Left:
                    newrect = QRectF(m_clickPoint, QPointF(newpoint.x(), m_clickPoint.y() - newrect.height())).normalized();
                    break;
                default:
                    break;
816
            }
817

818
            if (m_selectedItem->type() == QGraphicsRectItem::Type && m_resizeMode != NoResize) {
819
                MyRectItem *gi = static_cast<MyRectItem*>(m_selectedItem);
820
821
822
823
824
825
826
827
                // Resize using aspect ratio
                if (!m_selectedItem->data(0).isNull()) {
                    // we want to keep aspect ratio
                    double hRatio = (double) newrect.width() / m_selectedItem->data(0).toInt();
                    double vRatio = (double) newrect.height() / m_selectedItem->data(1).toInt();
                    if (hRatio < vRatio) newrect.setHeight(m_selectedItem->data(1).toInt() * hRatio);
                    else newrect.setWidth(m_selectedItem->data(0).toInt() * vRatio);
                }
828
829
                gi->setPos(newrect.topLeft());
                gi->setRect(QRectF(QPointF(), newrect.bottomRight() - newrect.topLeft()));
830
                return;
831
832
            }
            QGraphicsScene::mouseMoveEvent(e);
833
        } else if (m_selectedItem->type() == QGraphicsTextItem::Type) {
834
            MyTextItem *t = static_cast<MyTextItem *>(m_selectedItem);
835
836
837
838
            if (t->textInteractionFlags() & Qt::TextEditorInteraction) {
                QGraphicsScene::mouseMoveEvent(e);
                return;
            }
839
            QGraphicsScene::mouseMoveEvent(e);
840
            m_sceneClickPoint = e->scenePos();
841
        }
842
        emit itemMoved();
843
    } else if (m_tool == TITLE_SELECT) {
844
845
        QPointF p = e->scenePos();
        p += QPoint(-2, -2);
846
        m_resizeMode = NoResize;
847
        bool itemFound = false;
848
849
        QList<QGraphicsItem *> list = items(QRectF(p , QSizeF(4, 4)).toRect());
        foreach(const QGraphicsItem* g, list) {
850
851
852
            if (!(g->flags() & QGraphicsItem::ItemIsSelectable)) {
                continue;
            }
853
            if ((g->type() == QGraphicsSvgItem::Type || g->type() == QGraphicsPixmapItem::Type) && g->zValue() > -1000) {
854
855
                // image or svg item
                setCursor(Qt::OpenHandCursor);
856
                itemFound = true;
857
                break;
858
            } else if (g->type() == QGraphicsRectItem::Type && g->zValue() > -1000) {
859
860
                if (view == NULL) continue;
                QRectF r1 = ((const QGraphicsRectItem*)g)->rect().normalized();
861
                itemFound = true;
862
863
864
865
866
867
868
869
870
871
872
873

                // Item mapped coordinates
                QPolygon r = g->deviceTransform(view->viewportTransform()).map(r1).toPolygon();
                QPainterPath top(r.point(0));
                top.lineTo(r.point(1));
                QPainterPath bottom(r.point(2));
                bottom.lineTo(r.point(3));
                QPainterPath left(r.point(0));
                left.lineTo(r.point(3));
                QPainterPath right(r.point(1));
                right.lineTo(r.point(2));

874
                // The area interested by the mouse pointer
875
                QPoint viewPos = view->mapFromScene(e->scenePos());
876
                QPainterPath mouseArea;
877
878
879
                QFontMetrics metrics(font());
                int box = metrics.lineSpacing() / 2;
                mouseArea.addRect(viewPos.x() - box, viewPos.y() - box, 2 * box, 2 * box);
880

881
                // Check for collisions between the mouse and the borders
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
                if (mouseArea.contains(r.point(0))) {
                    m_possibleAction = TopLeft;
                    setCursor(Qt::SizeFDiagCursor);
                } else if (mouseArea.contains(r.point(2))) {
                    m_possibleAction = BottomRight;
                    setCursor(Qt::SizeFDiagCursor);
                }
                else if (mouseArea.contains(r.point(1))) {
                    m_possibleAction = TopRight;
                    setCursor(Qt::SizeBDiagCursor);
                } else if (mouseArea.contains(r.point(3))) {
                    m_possibleAction = BottomLeft;
                    setCursor(Qt::SizeBDiagCursor);
                }
                else if (top.intersects(mouseArea)) {
                    m_possibleAction = Up;
                    setCursor(Qt::SizeVerCursor);
                } else if (bottom.intersects(mouseArea)) {
                    m_possibleAction = Down;
                    setCursor(Qt::SizeVerCursor);
                }
                else if (right.intersects(mouseArea)) {
                    m_possibleAction = Right;
                    setCursor(Qt::SizeHorCursor);
                } else if (left.intersects(mouseArea)) {
                    m_possibleAction = Left;
                    setCursor(Qt::SizeHorCursor);
                }
                else {
911
                    setCursor(Qt::OpenHandCursor);
912
913
914
915
                    m_possibleAction = NoResize;
                }
            }
            break;
916
        }
917
918
919
920
        if (!itemFound) {
            m_possibleAction = NoResize;
            setCursor(Qt::ArrowCursor);
        }
921
        QGraphicsScene::mouseMoveEvent(e);
922
    } else if (m_tool == TITLE_RECTANGLE && e->buttons() & Qt::LeftButton) {
923
        if (m_selectedItem == NULL) {
924
            // create new rect item
925
926
            QRectF r(0, 0, e->scenePos().x() - m_sceneClickPoint.x(), e->scenePos().y() - m_sceneClickPoint.y());
            r = r.normalized();
927
928
929
930
            MyRectItem *rect = new MyRectItem();
            rect->setRect(QRectF(0, 0, r.width(), r.height()));
            addItem(rect);
            m_selectedItem = rect;
931
            m_selectedItem->setPos(m_sceneClickPoint);
932
            m_selectedItem->setSelected(true);
933
            emit newRect(rect);
934
            m_selectedItem->setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable | QGraphicsItem::ItemSendsGeometryChanges);
935
            m_resizeMode = BottomRight;
936
937
            QGraphicsScene::mouseMoveEvent(e);
        }
938
    }
Marco Gittler's avatar
Marco Gittler committed
939
940
}

941
942
void GraphicsSceneRectMove::wheelEvent(QGraphicsSceneWheelEvent * wheelEvent)
{
943
944
    if (wheelEvent->modifiers() == Qt::ControlModifier) {
        QList<QGraphicsView*> viewlist = views();
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
945
        ////qDebug() << wheelEvent->delta() << ' ' << zoom;
946
947
948
949
        if (viewlist.size() > 0) {
            if (wheelEvent->delta() > 0) emit sceneZoom(true);
            else emit sceneZoom(false);
        }
950
    } else wheelEvent->setAccepted(false);
951
}
952

953
954
void GraphicsSceneRectMove::setScale(double s)
{
955
956
    if (m_zoom < 1.0 / 7.0 && s < 1.0) return;
    else if (m_zoom > 10.0 / 7.9 && s > 1.0) return;
957
958
959
    QList<QGraphicsView*> viewlist = views();
    if (viewlist.size() > 0) {
        viewlist[0]->scale(s, s);
960
        m_zoom = m_zoom * s;
961
    }
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
962
    ////qDebug()<<"//////////  ZOOM: "<<zoom;
963
964
}

965
966
void GraphicsSceneRectMove::setZoom(double s)
{
967
968
969
970
    QList<QGraphicsView*> viewlist = views();
    if (viewlist.size() > 0) {
        viewlist[0]->resetTransform();
        viewlist[0]->scale(s, s);
971
        m_zoom = s;
972
973
    }

Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
974
    ////qDebug()<<"//////////  ZOOM: "<<zoom;
Marco Gittler's avatar
Marco Gittler committed
975
976
}

977
978
void GraphicsSceneRectMove::setCursor(QCursor c)
{
979
    const QList<QGraphicsView*> l = views();
980
981
982
    foreach(QGraphicsView* v, l) {
        v->setCursor(c);
    }
Marco Gittler's avatar
Marco Gittler committed
983
}
984

985
986
void GraphicsSceneRectMove::slotUpdateFontSize(int s)
{
987
    m_fontSize = s;
988
989
}

990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
void GraphicsSceneRectMove::drawForeground(QPainter *painter, const QRectF &rect) {
    // draw the grid if needed
    if (m_gridSize <= 1)
        return;

    QPen pen(QColor(255, 0, 0, 100));
    painter->setPen(pen);

    qreal left = int(rect.left()) - (int(rect.left()) % m_gridSize);
    qreal top = int(rect.top()) - (int(rect.top()) % m_gridSize);
    QVector<QPointF> points;
    for (qreal x = left; x < rect.right(); x += m_gridSize){
        for (qreal y = top; y < rect.bottom(); y += m_gridSize){
            points.append(QPointF(x,y));
        }
    }
    painter->drawPoints(points.data(), points.size());
}

int GraphicsSceneRectMove::gridSize() const
{
    return m_gridSize;
}

void GraphicsSceneRectMove::slotUseGrid(bool enableGrid)
{
    m_gridSize = enableGrid ? 20 : 1;
}

void GraphicsSceneRectMove::addNewItem(QGraphicsItem *item)
{
    clearSelection();
    addItem(item);
    item->setSelected(true);
    m_selectedItem = item;
}

1027