~ubuntu-branches/ubuntu/vivid/qpdfview/vivid

« back to all changes in this revision

Viewing changes to sources/pageitem.cpp

  • Committer: Package Import Robot
  • Author(s): Benjamin Eltzner
  • Date: 2014-10-22 21:49:15 UTC
  • mfrom: (1.2.15)
  • Revision ID: package-import@ubuntu.com-20141022214915-agqeoe318lzs2s4d
Tags: 0.4.12-1
* New upstream release.
* Fixed option to zoom to selection and implemented tiled rendering
  (Closes: #739554)
* Enable support for qt5 and poppler-qt5.
* Explicit dependence on hicolor-icon-theme.

Show diffs side-by-side

added added

removed removed

Lines of Context:
31
31
#include <QMenu>
32
32
#include <QMessageBox>
33
33
#include <QPainter>
 
34
#include <QStyleOptionGraphicsItem>
34
35
#include <QTimer>
35
36
#include <QToolTip>
36
37
#include <QUrl>
38
39
#include "settings.h"
39
40
#include "model.h"
40
41
#include "rendertask.h"
 
42
#include "tileitem.h"
 
43
 
 
44
namespace
 
45
{
 
46
 
 
47
using namespace qpdfview;
 
48
 
 
49
const qreal proxyPadding = 2.0;
 
50
 
 
51
bool modifiersUseMouseButton(Settings* settings, Qt::MouseButton mouseButton)
 
52
{
 
53
    return ((settings->pageItem().copyToClipboardModifiers() | settings->pageItem().addAnnotationModifiers()) & mouseButton) != 0;
 
54
}
 
55
 
 
56
} // anonymous
41
57
 
42
58
namespace qpdfview
43
59
{
44
60
 
45
61
Settings* PageItem::s_settings = 0;
46
62
 
47
 
QCache< PageItem*, QPixmap > PageItem::s_cache;
48
 
 
49
63
PageItem::PageItem(Model::Page* page, int index, bool presentationMode, QGraphicsItem* parent) : QGraphicsObject(parent),
50
 
    m_page(0),
51
 
    m_index(-1),
52
 
    m_size(),
 
64
    m_page(page),
 
65
    m_size(page->size()),
 
66
    m_cropRect(),
 
67
    m_index(index),
 
68
    m_presentationMode(presentationMode),
 
69
    m_highlights(),
53
70
    m_links(),
54
71
    m_annotations(),
55
72
    m_formFields(),
 
73
    m_rubberBandMode(ModifiersMode),
 
74
    m_rubberBand(),
56
75
    m_annotationOverlay(),
57
76
    m_formFieldOverlay(),
58
 
    m_presentationMode(presentationMode),
59
 
    m_invertColors(false),
60
 
    m_highlights(),
61
 
    m_rubberBandMode(ModifiersMode),
62
 
    m_rubberBand(),
63
 
    m_resolutionX(72),
64
 
    m_resolutionY(72),
65
 
    m_devicePixelRatio(1.0),
66
 
    m_scaleFactor(1.0),
67
 
    m_rotation(RotateBy0),
 
77
    m_renderParam(),
68
78
    m_transform(),
69
79
    m_normalizedTransform(),
70
80
    m_boundingRect(),
71
 
    m_pixmap(),
72
 
    m_renderTask(0),
73
 
    m_obsoletePixmap(),
74
 
    m_obsoleteTransform()
 
81
    m_tileItems()
75
82
{
76
83
    if(s_settings == 0)
77
84
    {
78
85
        s_settings = Settings::instance();
79
86
    }
80
87
 
81
 
    s_cache.setMaxCost(s_settings->pageItem().cacheSize());
82
 
 
83
88
    setAcceptHoverEvents(true);
84
89
 
85
 
    m_renderTask = new RenderTask(this);
86
 
 
87
 
    connect(m_renderTask, SIGNAL(finished()), SLOT(on_renderTask_finished()));
88
 
    connect(m_renderTask, SIGNAL(imageReady(int,int,qreal,qreal,Rotation,bool,bool,QImage)), SLOT(on_renderTask_imageReady(int,int,qreal,qreal,Rotation,bool,bool,QImage)));
89
 
 
90
 
    m_page = page;
91
 
 
92
 
    m_index = index;
93
 
    m_size = m_page->size();
 
90
    setFlag(QGraphicsItem::ItemUsesExtendedStyleOption, s_settings->pageItem().useTiling());
 
91
    setFlag(QGraphicsItem::ItemClipsToShape, s_settings->pageItem().trimMargins());
 
92
 
 
93
    if(!s_settings->pageItem().useTiling())
 
94
    {
 
95
        TileItem* tile = new TileItem(this);
 
96
 
 
97
        m_tileItems.resize(1);
 
98
        m_tileItems.squeeze();
 
99
 
 
100
        m_tileItems.replace(0, tile);
 
101
    }
94
102
 
95
103
    QTimer::singleShot(0, this, SLOT(loadInteractiveElements()));
96
104
 
102
110
    hideAnnotationOverlay(false);
103
111
    hideFormFieldOverlay(false);
104
112
 
105
 
    m_renderTask->cancel();
106
 
    m_renderTask->wait();
107
 
 
108
 
    s_cache.remove(this);
 
113
    TileItem::dropCachedPixmaps(this);
109
114
 
110
115
    qDeleteAll(m_links);
111
116
    qDeleteAll(m_annotations);
114
119
 
115
120
QRectF PageItem::boundingRect() const
116
121
{
117
 
    return m_boundingRect;
 
122
    if(m_cropRect.isNull())
 
123
    {
 
124
        return m_boundingRect;
 
125
    }
 
126
 
 
127
    QRectF boundingRect;
 
128
 
 
129
    boundingRect.setLeft(m_boundingRect.left() + m_cropRect.left() * m_boundingRect.width());
 
130
    boundingRect.setTop(m_boundingRect.top() + m_cropRect.top() * m_boundingRect.height());
 
131
    boundingRect.setWidth(m_cropRect.width() * m_boundingRect.width());
 
132
    boundingRect.setHeight(m_cropRect.height() * m_boundingRect.height());
 
133
 
 
134
    return boundingRect;
118
135
}
119
136
 
120
 
void PageItem::paint(QPainter* painter, const QStyleOptionGraphicsItem*, QWidget*)
 
137
void PageItem::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget*)
121
138
{
122
 
    paintPage(painter, cachedPixmap());
 
139
    paintPage(painter, option->exposedRect);
123
140
 
124
141
    paintLinks(painter);
125
142
    paintFormFields(painter);
128
145
    paintRubberBand(painter);
129
146
}
130
147
 
131
 
void PageItem::setInvertColors(bool invertColors)
132
 
{
133
 
    m_invertColors = invertColors;
134
 
 
135
 
    refresh();
 
148
qreal PageItem::displayedWidth() const
 
149
{
 
150
    const qreal cropWidth = m_cropRect.isNull() ? 1.0 : m_cropRect.width();
 
151
    const qreal cropHeight = m_cropRect.isNull() ? 1.0 : m_cropRect.height();
 
152
 
 
153
    switch(m_renderParam.rotation)
 
154
    {
 
155
    default:
 
156
    case RotateBy0:
 
157
    case RotateBy180:
 
158
        return m_renderParam.resolution.resolutionX / 72.0 * cropWidth * m_size.width();
 
159
    case RotateBy90:
 
160
    case RotateBy270:
 
161
        return m_renderParam.resolution.resolutionX / 72.0 * cropHeight * m_size.height();
 
162
    }
 
163
}
 
164
 
 
165
qreal PageItem::displayedHeight() const
 
166
{
 
167
    const qreal cropHeight = m_cropRect.isNull() ? 1.0 : m_cropRect.height();
 
168
    const qreal cropWidth = m_cropRect.isNull() ? 1.0 : m_cropRect.width();
 
169
 
 
170
    switch(m_renderParam.rotation)
 
171
    {
 
172
    default:
 
173
    case RotateBy0:
 
174
    case RotateBy180:
 
175
        return m_renderParam.resolution.resolutionY / 72.0 * cropHeight * m_size.height();
 
176
    case RotateBy90:
 
177
    case RotateBy270:
 
178
        return m_renderParam.resolution.resolutionY / 72.0 * cropWidth * m_size.width();
 
179
    }
136
180
}
137
181
 
138
182
void PageItem::setHighlights(const QList< QRectF >& highlights)
161
205
 
162
206
void PageItem::setResolution(int resolutionX, int resolutionY)
163
207
{
164
 
    if((m_resolutionX != resolutionX || m_resolutionY != resolutionY) && resolutionX > 0 && resolutionY > 0)
 
208
    if((m_renderParam.resolution.resolutionX != resolutionX || m_renderParam.resolution.resolutionY != resolutionY) && resolutionX > 0 && resolutionY > 0)
165
209
    {
166
 
        refresh();
 
210
        refresh(true);
167
211
 
168
 
        m_resolutionX = resolutionX;
169
 
        m_resolutionY = resolutionY;
 
212
        m_renderParam.resolution.resolutionX = resolutionX;
 
213
        m_renderParam.resolution.resolutionY = resolutionY;
170
214
 
171
215
        prepareGeometryChange();
172
216
        prepareGeometry();
175
219
 
176
220
void PageItem::setDevicePixelRatio(qreal devicePixelRatio)
177
221
{
178
 
    if(!qFuzzyCompare(m_devicePixelRatio, devicePixelRatio) && devicePixelRatio > 0.0)
179
 
    {
180
 
        refresh();
181
 
 
182
 
        m_devicePixelRatio = devicePixelRatio;
 
222
    if(!s_settings->pageItem().useDevicePixelRatio())
 
223
    {
 
224
        return;
 
225
    }
 
226
 
 
227
    if(!qFuzzyCompare(m_renderParam.resolution.devicePixelRatio, devicePixelRatio) && devicePixelRatio > 0.0)
 
228
    {
 
229
        refresh(true);
 
230
 
 
231
        m_renderParam.resolution.devicePixelRatio = devicePixelRatio;
183
232
 
184
233
        prepareGeometryChange();
185
234
        prepareGeometry();
188
237
 
189
238
void PageItem::setScaleFactor(qreal scaleFactor)
190
239
{
191
 
    if(!qFuzzyCompare(m_scaleFactor, scaleFactor) && scaleFactor > 0.0)
 
240
    if(!qFuzzyCompare(m_renderParam.scaleFactor, scaleFactor) && scaleFactor > 0.0)
192
241
    {
193
 
        refresh();
 
242
        refresh(true);
194
243
 
195
 
        m_scaleFactor = scaleFactor;
 
244
        m_renderParam.scaleFactor = scaleFactor;
196
245
 
197
246
        prepareGeometryChange();
198
247
        prepareGeometry();
201
250
 
202
251
void PageItem::setRotation(Rotation rotation)
203
252
{
204
 
    if(m_rotation != rotation && rotation >= 0 && rotation < NumberOfRotations)
 
253
    if(m_renderParam.rotation != rotation && rotation >= 0 && rotation < NumberOfRotations)
205
254
    {
206
 
        refresh();
 
255
        refresh(false);
207
256
 
208
 
        m_rotation = rotation;
 
257
        m_renderParam.rotation = rotation;
209
258
 
210
259
        prepareGeometryChange();
211
260
        prepareGeometry();
212
261
    }
213
262
}
214
263
 
215
 
void PageItem::refresh()
216
 
{
217
 
    m_renderTask->cancel();
218
 
 
219
 
    if(s_settings->pageItem().keepObsoletePixmaps() && s_cache.contains(this))
220
 
    {
221
 
        m_obsoletePixmap = *s_cache.object(this);
222
 
        m_obsoleteTopLeft = m_boundingRect.topLeft();
223
 
        m_obsoleteTransform = m_transform.inverted();
224
 
    }
225
 
 
226
 
    m_pixmap = QPixmap();
227
 
    s_cache.remove(this);
 
264
void PageItem::setInvertColors(bool invertColors)
 
265
{
 
266
    if(m_renderParam.invertColors != invertColors)
 
267
    {
 
268
        refresh(false);
 
269
 
 
270
        m_renderParam.invertColors = invertColors;
 
271
    }
 
272
}
 
273
 
 
274
 
 
275
void PageItem::refresh(bool keepObsoletePixmaps, bool dropCachedPixmaps)
 
276
{
 
277
    if(!s_settings->pageItem().useTiling())
 
278
    {
 
279
        m_tileItems.first()->refresh(keepObsoletePixmaps);
 
280
    }
 
281
    else
 
282
    {
 
283
        foreach(TileItem* tile, m_tileItems)
 
284
        {
 
285
            tile->refresh(keepObsoletePixmaps);
 
286
        }
 
287
    }
 
288
 
 
289
    if(!keepObsoletePixmaps)
 
290
    {
 
291
        m_cropRect = QRectF();
 
292
    }
 
293
 
 
294
    if(dropCachedPixmaps)
 
295
    {
 
296
        TileItem::dropCachedPixmaps(this);
 
297
    }
228
298
 
229
299
    update();
230
300
}
231
301
 
232
 
void PageItem::startRender(bool prefetch)
 
302
int PageItem::startRender(bool prefetch)
233
303
{
234
 
    if(prefetch && s_cache.contains(this))
235
 
    {
236
 
        return;
237
 
    }
238
 
 
239
 
    if(!m_renderTask->isRunning())
240
 
    {
241
 
        m_renderTask->start(m_page, m_resolutionX, m_resolutionY, effectiveDevicePixelRatio(), m_scaleFactor, m_rotation, m_invertColors, prefetch);
242
 
    }
 
304
    int cost = 0;
 
305
 
 
306
    if(!s_settings->pageItem().useTiling())
 
307
    {
 
308
        cost += m_tileItems.first()->startRender(prefetch);
 
309
    }
 
310
    else
 
311
    {
 
312
        foreach(TileItem* tile, m_tileItems)
 
313
        {
 
314
            cost += tile->startRender(prefetch);
 
315
        }
 
316
    }
 
317
 
 
318
    return cost;
243
319
}
244
320
 
245
321
void PageItem::cancelRender()
246
322
{
247
 
    m_renderTask->cancel();
248
 
 
249
 
    m_pixmap = QPixmap();
250
 
    m_obsoletePixmap = QPixmap();
251
 
}
252
 
 
253
 
void PageItem::on_renderTask_finished()
254
 
{
255
 
    update();
256
 
}
257
 
 
258
 
void PageItem::on_renderTask_imageReady(int resolutionX, int resolutionY, qreal devicePixelRatio, qreal scaleFactor, Rotation rotation, bool invertColors, bool prefetch, QImage image)
259
 
{
260
 
    if(m_resolutionX != resolutionX || m_resolutionY != resolutionY
261
 
            || !qFuzzyCompare(effectiveDevicePixelRatio(), devicePixelRatio)
262
 
            || !qFuzzyCompare(m_scaleFactor, scaleFactor) || m_rotation != rotation
263
 
            || m_invertColors != invertColors)
264
 
    {
265
 
        return;
266
 
    }
267
 
 
268
 
    if(image.isNull())
269
 
    {
270
 
        // error icon
271
 
 
272
 
        const qreal extent = qMin(0.1 * m_boundingRect.width(), 0.1 * m_boundingRect.height());
273
 
        const QRectF rect(0.01 * m_boundingRect.width(), 0.01 * m_boundingRect.height(), extent, extent);
274
 
 
275
 
        image = QImage(qFloor(0.01 * m_boundingRect.width() + extent), qFloor(0.01 * m_boundingRect.height() + extent), QImage::Format_ARGB32);
276
 
        image.fill(Qt::transparent);
277
 
 
278
 
        QPainter painter(&image);
279
 
        s_settings->pageItem().errorIcon().paint(&painter, rect.toRect());
280
 
    }
281
 
 
282
 
    if(prefetch)
283
 
    {
284
 
        QPixmap* pixmap = new QPixmap(QPixmap::fromImage(image));
285
 
 
286
 
        int cost = pixmap->width() * pixmap->height() * pixmap->depth() / 8;
287
 
        s_cache.insert(this, pixmap, cost);
 
323
    if(!s_settings->pageItem().useTiling())
 
324
    {
 
325
        m_tileItems.first()->cancelRender();
288
326
    }
289
327
    else
290
328
    {
291
 
        if(!m_renderTask->wasCanceled())
 
329
        foreach(TileItem* tile, m_tileItems)
292
330
        {
293
 
            m_pixmap = QPixmap::fromImage(image);
 
331
            tile->cancelRender();
294
332
        }
295
333
    }
296
 
 
297
 
    m_obsoletePixmap = QPixmap();
298
334
}
299
335
 
300
336
void PageItem::showAnnotationOverlay(Model::Annotation* selectedAnnotation)
431
467
 
432
468
void PageItem::mousePressEvent(QGraphicsSceneMouseEvent* event)
433
469
{
 
470
    const Qt::KeyboardModifiers copyToClipboardModifiers = s_settings->pageItem().copyToClipboardModifiers();
 
471
    const Qt::KeyboardModifiers addAnnotationModifiers = s_settings->pageItem().addAnnotationModifiers();
 
472
 
 
473
    const bool copyToClipboardModifiersActive = event->modifiers() == copyToClipboardModifiers || ((event->buttons() & copyToClipboardModifiers) != 0);
 
474
    const bool addAnnotationModifiersActive = event->modifiers() == addAnnotationModifiers || ((event->buttons() & addAnnotationModifiers) != 0);
 
475
 
434
476
    // rubber band
435
477
 
436
478
    if(m_rubberBandMode == ModifiersMode && !m_presentationMode
437
 
            && (event->modifiers() == Qt::NoModifier
438
 
                || event->modifiers() == s_settings->pageItem().copyToClipboardModifiers()
439
 
                || event->modifiers() == s_settings->pageItem().addAnnotationModifiers())
 
479
            && (event->modifiers() == Qt::NoModifier || copyToClipboardModifiersActive || addAnnotationModifiersActive)
440
480
            && (event->button() == Qt::LeftButton || event->button() == Qt::MidButton))
441
481
    {
442
482
        setCursor(Qt::CrossCursor);
443
483
 
444
 
        if(event->modifiers() == s_settings->pageItem().copyToClipboardModifiers() && event->button() == Qt::LeftButton)
 
484
        if(copyToClipboardModifiersActive && event->button() == Qt::LeftButton)
445
485
        {
446
486
            m_rubberBandMode = CopyToClipboardMode;
447
487
        }
448
 
        else if(event->modifiers() == s_settings->pageItem().addAnnotationModifiers() && event->button() == Qt::LeftButton)
 
488
        else if(addAnnotationModifiersActive && event->button() == Qt::LeftButton)
449
489
        {
450
490
            m_rubberBandMode = AddAnnotationMode;
451
491
        }
608
648
 
609
649
void PageItem::contextMenuEvent(QGraphicsSceneContextMenuEvent* event)
610
650
{
 
651
    if(event->reason() == QGraphicsSceneContextMenuEvent::Mouse && modifiersUseMouseButton(s_settings, Qt::RightButton))
 
652
    {
 
653
        event->accept();
 
654
        return;
 
655
    }
 
656
 
611
657
    if(m_presentationMode)
612
658
    {
613
659
        event->ignore();
614
660
        return;
615
661
    }
616
662
 
 
663
    foreach(Model::Link* link, m_links)
 
664
    {
 
665
        if(m_normalizedTransform.map(link->boundary).contains(event->pos()))
 
666
        {
 
667
            unsetCursor();
 
668
 
 
669
            showLinkContextMenu(link, event->screenPos());
 
670
 
 
671
            event->accept();
 
672
            return;
 
673
        }
 
674
    }
 
675
 
617
676
    foreach(Model::Annotation* annotation, m_annotations)
618
677
    {
619
678
        if(m_normalizedTransform.mapRect(annotation->boundary()).contains(event->pos()))
620
679
        {
621
680
            unsetCursor();
622
681
 
623
 
            removeAnnotation(annotation, event->screenPos());
 
682
            showAnnotationContextMenu(annotation, event->screenPos());
624
683
 
625
684
            event->accept();
626
685
            return;
654
713
    update();
655
714
}
656
715
 
 
716
void PageItem::updateCropRect()
 
717
{
 
718
    QRectF cropRect;
 
719
 
 
720
    if(!s_settings->pageItem().useTiling())
 
721
    {
 
722
        cropRect = m_tileItems.first()->cropRect();
 
723
    }
 
724
    else
 
725
    {
 
726
        foreach(TileItem* tile, m_tileItems)
 
727
        {
 
728
            const QRect& tileRect = tile->rect();
 
729
            const QRectF& tileCropRect = tile->cropRect();
 
730
 
 
731
            if(tileCropRect.isNull())
 
732
            {
 
733
                cropRect = QRectF();
 
734
                break;
 
735
            }
 
736
 
 
737
            const qreal left = (tileRect.left() + tileCropRect.left() * tileRect.width()) / m_boundingRect.width();
 
738
            const qreal top = (tileRect.top() + tileCropRect.top() * tileRect.height()) / m_boundingRect.height();
 
739
            const qreal width = tileCropRect.width() * tileRect.width() / m_boundingRect.width();
 
740
            const qreal height = tileCropRect.height() * tileRect.height() / m_boundingRect.height();
 
741
 
 
742
            cropRect = cropRect.united(QRectF(left, top, width, height));
 
743
        }
 
744
    }
 
745
 
 
746
    if(m_cropRect.isNull() && !cropRect.isNull())
 
747
    {
 
748
        m_cropRect = cropRect;
 
749
 
 
750
        prepareGeometryChange();
 
751
        emit cropRectChanged();
 
752
    }
 
753
}
 
754
 
657
755
void PageItem::copyToClipboard(const QPoint& screenPos)
658
756
{
659
757
    QMenu menu;
660
758
 
661
 
    const QAction* copyTextAction = menu.addAction(tr("Copy &text"));
662
 
    QAction* useTextAsSelectionAction = menu.addAction(tr("Use text as &selection"));
 
759
    QAction* copyTextAction = menu.addAction(tr("Copy &text"));
 
760
    QAction* selectTextAction = menu.addAction(tr("&Select text"));
663
761
    const QAction* copyImageAction = menu.addAction(tr("Copy &image"));
664
762
    const QAction* saveImageToFileAction = menu.addAction(tr("Save image to &file..."));
665
763
 
666
 
    useTextAsSelectionAction->setVisible(QApplication::clipboard()->supportsSelection());
 
764
    const QString text = m_page->text(m_transform.inverted().mapRect(m_rubberBand));
 
765
 
 
766
    copyTextAction->setVisible(!text.isEmpty());
 
767
    selectTextAction->setVisible(!text.isEmpty() && QApplication::clipboard()->supportsSelection());
667
768
 
668
769
    const QAction* action = menu.exec(screenPos);
669
770
 
670
 
    if(action == copyTextAction || action == useTextAsSelectionAction)
 
771
    if(action == copyTextAction || action == selectTextAction)
671
772
    {
672
 
        const QString text = m_page->text(m_transform.inverted().mapRect(m_rubberBand));
673
 
 
674
 
        if(!text.isEmpty())
675
 
        {
676
 
            if(action == copyTextAction)
677
 
            {
678
 
                QApplication::clipboard()->setText(text);
679
 
            }
680
 
            else
681
 
            {
682
 
                QApplication::clipboard()->setText(text, QClipboard::Selection);
683
 
            }
 
773
        if(action == copyTextAction)
 
774
        {
 
775
            QApplication::clipboard()->setText(text);
 
776
        }
 
777
        else
 
778
        {
 
779
            QApplication::clipboard()->setText(text, QClipboard::Selection);
684
780
        }
685
781
    }
686
782
    else if(action == copyImageAction || action == saveImageToFileAction)
687
783
    {
688
784
        const QRect rect = m_rubberBand.translated(-m_boundingRect.topLeft()).toRect();
689
 
        const QImage image = s_cache.contains(this) ? s_cache.object(this)->copy(rect).toImage() : m_page->render(m_resolutionX * m_scaleFactor, m_scaleFactor * m_resolutionY, m_rotation, rect);
 
785
        const QImage image = m_page->render(m_renderParam.resolution.resolutionX * m_renderParam.scaleFactor,
 
786
                                            m_renderParam.resolution.resolutionY * m_renderParam.scaleFactor,
 
787
                                            m_renderParam.rotation, rect);
690
788
 
691
789
        if(!image.isNull())
692
790
        {
739
837
            m_annotations.append(annotation);
740
838
            connect(annotation, SIGNAL(wasModified()), SIGNAL(wasModified()));
741
839
 
742
 
            refresh();
 
840
            refresh(false, true);
743
841
            emit wasModified();
744
842
 
745
843
            showAnnotationOverlay(annotation);
747
845
    }
748
846
}
749
847
 
750
 
void PageItem::removeAnnotation(Model::Annotation* annotation, const QPoint& screenPos)
 
848
void PageItem::showLinkContextMenu(Model::Link* link, const QPoint& screenPos)
 
849
{
 
850
    if(link->page == -1)
 
851
    {
 
852
        QMenu menu;
 
853
 
 
854
        const QAction* copyLinkAddressAction = menu.addAction(tr("&Copy link address"));
 
855
        QAction* selectLinkAddressAction = menu.addAction(tr("&Select link address"));
 
856
 
 
857
        selectLinkAddressAction->setVisible(QApplication::clipboard()->supportsSelection());
 
858
 
 
859
        const QAction* action = menu.exec(screenPos);
 
860
 
 
861
        if(action == copyLinkAddressAction)
 
862
        {
 
863
            QApplication::clipboard()->setText(link->urlOrFileName);
 
864
        }
 
865
        else if(action == selectLinkAddressAction)
 
866
        {
 
867
            QApplication::clipboard()->setText(link->urlOrFileName, QClipboard::Selection);
 
868
        }
 
869
    }
 
870
}
 
871
 
 
872
void PageItem::showAnnotationContextMenu(Model::Annotation* annotation, const QPoint& screenPos)
751
873
{
752
874
    if(m_page->canAddAndRemoveAnnotations())
753
875
    {
764
886
 
765
887
            annotation->deleteLater();
766
888
 
767
 
            refresh();
 
889
            refresh(false, true);
768
890
            emit wasModified();
769
891
        }
770
892
    }
808
930
template< typename Overlay >
809
931
void PageItem::hideOverlay(Overlay& overlay, bool deleteLater)
810
932
{
 
933
#if QT_VERSION >= QT_VERSION_CHECK(4,8,0)
 
934
 
811
935
    Overlay discardedOverlay;
812
 
 
813
 
#if QT_VERSION >= QT_VERSION_CHECK(4,8,0)
814
 
 
815
936
    discardedOverlay.swap(overlay);
816
937
 
817
938
#else
818
939
 
819
 
    discardedOverlay = overlay;
 
940
    Overlay discardedOverlay(overlay);
820
941
    overlay = Overlay();
821
942
 
822
943
#endif // QT_VERSION
835
956
            }
836
957
        }
837
958
 
838
 
        refresh();
 
959
        refresh(false, true);
839
960
    }
840
961
}
841
962
 
875
996
    qreal width = rect.width();
876
997
    qreal height = rect.height();
877
998
 
878
 
    switch(m_rotation)
 
999
    switch(m_renderParam.rotation)
879
1000
    {
880
1001
    default:
881
1002
    case RotateBy0:
901
1022
        break;
902
1023
    }
903
1024
 
904
 
    width /= m_scaleFactor;
905
 
    height /= m_scaleFactor;
 
1025
    width /= m_renderParam.scaleFactor;
 
1026
    height /= m_renderParam.scaleFactor;
906
1027
 
907
 
    proxy->setScale(m_scaleFactor);
 
1028
    proxy->setScale(m_renderParam.scaleFactor);
908
1029
 
909
1030
    proxy->setGeometry(QRectF(x - proxyPadding, y - proxyPadding, width + proxyPadding, height + proxyPadding));
910
1031
}
911
1032
 
912
 
qreal PageItem::effectiveDevicePixelRatio()
913
 
{
914
 
#if QT_VERSION >= QT_VERSION_CHECK(5,1,0)
915
 
 
916
 
    return s_settings->pageItem().useDevicePixelRatio() ? m_devicePixelRatio : 1.0;
917
 
 
918
 
#else
919
 
 
920
 
    return 1.0;
921
 
 
922
 
#endif // QT_VERSION
923
 
}
924
 
 
925
1033
void PageItem::prepareGeometry()
926
1034
{
927
1035
    m_transform.reset();
928
 
    m_normalizedTransform.reset();
929
 
 
930
 
    switch(m_rotation)
 
1036
 
 
1037
    m_transform.scale(m_renderParam.resolution.resolutionX * m_renderParam.scaleFactor / 72.0,
 
1038
                      m_renderParam.resolution.resolutionY * m_renderParam.scaleFactor / 72.0);
 
1039
 
 
1040
    switch(m_renderParam.rotation)
931
1041
    {
932
1042
    default:
933
1043
    case RotateBy0:
934
1044
        break;
935
1045
    case RotateBy90:
936
1046
        m_transform.rotate(90.0);
937
 
        m_normalizedTransform.rotate(90.0);
938
1047
        break;
939
1048
    case RotateBy180:
940
1049
        m_transform.rotate(180.0);
941
 
        m_normalizedTransform.rotate(180.0);
942
1050
        break;
943
1051
    case RotateBy270:
944
1052
        m_transform.rotate(270.0);
945
 
        m_normalizedTransform.rotate(270.0);
946
 
        break;
947
 
    }
948
 
 
949
 
    switch(m_rotation)
950
 
    {
951
 
    default:
952
 
    case RotateBy0:
953
 
    case RotateBy180:
954
 
        m_transform.scale(m_scaleFactor * m_resolutionX / 72.0, m_scaleFactor * m_resolutionY / 72.0);
955
 
        m_normalizedTransform.scale(m_scaleFactor * m_resolutionX / 72.0 * m_size.width(), m_scaleFactor * m_resolutionY / 72.0 * m_size.height());
956
 
        break;
957
 
    case RotateBy90:
958
 
    case RotateBy270:
959
 
        m_transform.scale(m_scaleFactor * m_resolutionY / 72.0, m_scaleFactor * m_resolutionX / 72.0);
960
 
        m_normalizedTransform.scale(m_scaleFactor * m_resolutionY / 72.0 * m_size.width(), m_scaleFactor * m_resolutionX / 72.0 * m_size.height());
961
 
        break;
962
 
    }
 
1053
        break;
 
1054
    }
 
1055
 
 
1056
    m_normalizedTransform = m_transform;
 
1057
    m_normalizedTransform.scale(m_size.width(), m_size.height());
 
1058
 
963
1059
 
964
1060
    m_boundingRect = m_transform.mapRect(QRectF(QPointF(), m_size));
965
1061
 
966
1062
    m_boundingRect.setWidth(qRound(m_boundingRect.width()));
967
1063
    m_boundingRect.setHeight(qRound(m_boundingRect.height()));
968
1064
 
 
1065
 
 
1066
    prepareTiling();
 
1067
 
969
1068
    updateAnnotationOverlay();
970
1069
    updateFormFieldOverlay();
971
1070
}
972
1071
 
973
 
QPixmap PageItem::cachedPixmap()
 
1072
void PageItem::prepareTiling()
974
1073
{
975
 
    QPixmap pixmap;
976
 
 
977
 
    if(s_cache.contains(this))
978
 
    {
979
 
        pixmap = *s_cache.object(this);
980
 
    }
981
 
    else
982
 
    {
983
 
        if(!m_pixmap.isNull())
984
 
        {
985
 
            pixmap = m_pixmap;
986
 
            m_pixmap = QPixmap();
987
 
 
988
 
            int cost = pixmap.width() * pixmap.height() * pixmap.depth() / 8;
989
 
            s_cache.insert(this, new QPixmap(pixmap), cost);
990
 
        }
991
 
        else
992
 
        {
993
 
            startRender();
994
 
        }
995
 
    }
996
 
 
997
 
    return pixmap;
 
1074
    if(!s_settings->pageItem().useTiling())
 
1075
    {
 
1076
        m_tileItems.first()->setRect(QRect(0, 0, m_boundingRect.width(), m_boundingRect.height()));
 
1077
 
 
1078
        return;
 
1079
    }
 
1080
 
 
1081
 
 
1082
    const qreal pageWidth = m_boundingRect.width();
 
1083
    const qreal pageHeight = m_boundingRect.height();
 
1084
 
 
1085
    const int tileSize = s_settings->pageItem().tileSize();
 
1086
 
 
1087
    int tileWidth = pageWidth < pageHeight ? tileSize * pageWidth / pageHeight : tileSize;
 
1088
    int tileHeight = pageHeight < pageWidth ? tileSize * pageHeight / pageWidth : tileSize;
 
1089
 
 
1090
    const int columnCount = qCeil(pageWidth / tileWidth);
 
1091
    const int rowCount = qCeil(pageHeight / tileHeight);
 
1092
 
 
1093
    tileWidth = qCeil(pageWidth / columnCount);
 
1094
    tileHeight = qCeil(pageHeight / rowCount);
 
1095
 
 
1096
 
 
1097
    const int newCount = columnCount * rowCount;
 
1098
    const int oldCount = m_tileItems.count();
 
1099
 
 
1100
    for(int index = newCount; index < oldCount; ++index)
 
1101
    {
 
1102
        m_tileItems.at(index)->deleteAfterRender();
 
1103
    }
 
1104
 
 
1105
    m_tileItems.resize(newCount);
 
1106
 
 
1107
    for(int index = oldCount; index < newCount; ++index)
 
1108
    {
 
1109
        m_tileItems.replace(index, new TileItem(this));
 
1110
    }
 
1111
 
 
1112
    if(oldCount != newCount)
 
1113
    {
 
1114
        foreach(TileItem* tile, m_tileItems)
 
1115
        {
 
1116
            tile->dropObsoletePixmap();
 
1117
        }
 
1118
    }
 
1119
 
 
1120
 
 
1121
    for(int column = 0; column < columnCount; ++column)
 
1122
    {
 
1123
        for(int row = 0; row < rowCount; ++row)
 
1124
        {
 
1125
            const int left = column > 0 ? column * tileWidth : 0.0;
 
1126
            const int top = row > 0 ? row * tileHeight : 0.0;
 
1127
 
 
1128
            const int width = column < (columnCount - 1) ? tileWidth : pageWidth - left;
 
1129
            const int height = row < (rowCount - 1) ? tileHeight : pageHeight - top;
 
1130
 
 
1131
            m_tileItems.at(column * rowCount + row)->setRect(QRect(left, top, width, height));
 
1132
        }
 
1133
    }
998
1134
}
999
1135
 
1000
 
void PageItem::paintPage(QPainter* painter, const QPixmap& pixmap) const
 
1136
void PageItem::paintPage(QPainter* painter, const QRectF& exposedRect) const
1001
1137
{
1002
1138
    if(s_settings->pageItem().decoratePages() && !m_presentationMode)
1003
1139
    {
 
1140
        // background
 
1141
 
1004
1142
        QColor paperColor = s_settings->pageItem().paperColor();
1005
1143
 
1006
 
        if(m_invertColors)
 
1144
        if(m_renderParam.invertColors)
1007
1145
        {
1008
1146
            paperColor.setRgb(~paperColor.rgb());
1009
1147
        }
1011
1149
        painter->fillRect(m_boundingRect, QBrush(paperColor));
1012
1150
    }
1013
1151
 
1014
 
    if(!pixmap.isNull())
1015
 
    {
1016
 
        painter->drawPixmap(m_boundingRect.topLeft(), pixmap);
1017
 
    }
1018
 
    else if(!m_obsoletePixmap.isNull())
1019
 
    {
1020
 
        painter->save();
1021
 
 
1022
 
        painter->setTransform(m_obsoleteTransform, true);
1023
 
        painter->setTransform(m_transform, true);
1024
 
 
1025
 
        painter->drawPixmap(m_obsoleteTopLeft, m_obsoletePixmap);
1026
 
 
1027
 
        painter->restore();
 
1152
    // tiles
 
1153
 
 
1154
    if(!s_settings->pageItem().useTiling())
 
1155
    {
 
1156
        m_tileItems.first()->paint(painter, m_boundingRect.topLeft());
1028
1157
    }
1029
1158
    else
1030
1159
    {
1031
 
        // progess icon
1032
 
 
1033
 
        const qreal extent = qMin(0.1 * m_boundingRect.width(), 0.1 * m_boundingRect.height());
1034
 
        const QRectF rect(m_boundingRect.left() + 0.01 * m_boundingRect.width(), m_boundingRect.top() + 0.01 * m_boundingRect.height(), extent, extent);
1035
 
 
1036
 
        s_settings->pageItem().progressIcon().paint(painter, rect.toRect());
 
1160
        const QRectF& translatedExposedRect = exposedRect.translated(-m_boundingRect.topLeft());
 
1161
 
 
1162
        foreach(TileItem* tile, m_tileItems)
 
1163
        {
 
1164
            if(translatedExposedRect.intersects(tile->rect()))
 
1165
            {
 
1166
                tile->paint(painter, m_boundingRect.topLeft());
 
1167
            }
 
1168
            else
 
1169
            {
 
1170
                tile->cancelRender();
 
1171
            }
 
1172
        }
1037
1173
    }
1038
1174
 
1039
1175
    if(s_settings->pageItem().decoratePages() && !m_presentationMode)
1040
1176
    {
1041
 
        painter->drawRect(m_boundingRect);
 
1177
        // border
 
1178
 
 
1179
        if(!s_settings->pageItem().trimMargins())
 
1180
        {
 
1181
            painter->drawRect(m_boundingRect);
 
1182
        }
 
1183
        else
 
1184
        {
 
1185
            painter->save();
 
1186
 
 
1187
            painter->setClipping(false);
 
1188
 
 
1189
            painter->drawRect(boundingRect());
 
1190
 
 
1191
            painter->restore();
 
1192
        }
1042
1193
    }
1043
1194
}
1044
1195
 
1113
1264
    }
1114
1265
}
1115
1266
 
1116
 
ThumbnailItem::ThumbnailItem(Model::Page* page, int index, QGraphicsItem* parent) : PageItem(page, index, false, parent),
1117
 
#if QT_VERSION >= QT_VERSION_CHECK(4,7,0)
1118
 
    m_text(QString::number(index + 1)),
1119
 
#endif // QT_VERSION
1120
 
    m_current(false)
1121
 
{
1122
 
    setAcceptHoverEvents(false);
1123
 
}
1124
 
 
1125
 
QRectF ThumbnailItem::boundingRect() const
1126
 
{
1127
 
#if QT_VERSION >= QT_VERSION_CHECK(4,7,0)
1128
 
 
1129
 
    return PageItem::boundingRect().adjusted(0.0, 0.0, 0.0, 2.0 * m_text.size().height());
1130
 
 
1131
 
#else
1132
 
 
1133
 
    return PageItem::boundingRect().adjusted(0.0, 0.0, 0.0, 2.0 * QFontMetrics(QFont()).height());
1134
 
 
1135
 
#endif // QT_VERSION
1136
 
}
1137
 
 
1138
 
void ThumbnailItem::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget)
1139
 
{
1140
 
    PageItem::paint(painter, option, widget);
1141
 
 
1142
 
    const QRectF boundingRect = PageItem::boundingRect();
1143
 
 
1144
 
#if QT_VERSION >= QT_VERSION_CHECK(4,7,0)
1145
 
 
1146
 
    const QSizeF textSize = m_text.size();
1147
 
 
1148
 
    QPointF pos = boundingRect.bottomLeft();
1149
 
    pos.rx() += 0.5 * (boundingRect.width() - textSize.width());
1150
 
    pos.ry() += 0.5 * textSize.height();
1151
 
 
1152
 
    painter->drawStaticText(pos, m_text);
1153
 
 
1154
 
#else
1155
 
 
1156
 
    const QString text = QString::number(index() + 1);
1157
 
    const QFontMetrics fontMetrics = QFontMetrics(QFont());
1158
 
 
1159
 
    QPointF pos = boundingRect.bottomLeft();
1160
 
    pos.rx() += 0.5 * (boundingRect.width() - fontMetrics.width(text));
1161
 
    pos.ry() += fontMetrics.height();
1162
 
 
1163
 
    painter->drawText(pos, text);
1164
 
 
1165
 
#endif // QT_VERSION
1166
 
 
1167
 
    if(m_current)
1168
 
    {
1169
 
        painter->save();
1170
 
 
1171
 
        painter->setCompositionMode(QPainter::CompositionMode_Multiply);
1172
 
        painter->fillRect(boundingRect, widget->palette().highlight());
1173
 
 
1174
 
        painter->restore();
1175
 
    }
1176
 
}
1177
 
 
1178
 
void ThumbnailItem::setCurrent(bool current)
1179
 
{
1180
 
    if(m_current != current)
1181
 
    {
1182
 
        m_current = current;
1183
 
 
1184
 
        update();
1185
 
    }
1186
 
}
1187
 
 
1188
 
void ThumbnailItem::mousePressEvent(QGraphicsSceneMouseEvent* event)
1189
 
{
1190
 
    if(event->modifiers() == Qt::NoModifier
1191
 
            && (event->button() == Qt::LeftButton || event->button() == Qt::MidButton))
1192
 
    {
1193
 
        emit linkClicked(event->button() == Qt::MidButton, index() + 1);
1194
 
 
1195
 
        event->accept();
1196
 
        return;
1197
 
    }
1198
 
 
1199
 
    event->ignore();
1200
 
}
1201
 
 
1202
 
void ThumbnailItem::mouseDoubleClickEvent(QGraphicsSceneMouseEvent*)
1203
 
{
1204
 
}
1205
 
 
1206
 
void ThumbnailItem::mouseMoveEvent(QGraphicsSceneMouseEvent*)
1207
 
{
1208
 
}
1209
 
 
1210
 
void ThumbnailItem::mouseReleaseEvent(QGraphicsSceneMouseEvent*)
1211
 
{
1212
 
}
1213
 
 
1214
 
void ThumbnailItem::contextMenuEvent(QGraphicsSceneContextMenuEvent*)
1215
 
{
1216
 
}
1217
 
 
1218
 
void ThumbnailItem::loadInteractiveElements()
1219
 
{
1220
 
    const qreal width = size().width() / 72.0 * 25.4;
1221
 
    const qreal height = size().height() / 72.0 * 25.4;
1222
 
 
1223
 
    const qreal longEdge = qMax(width, height);
1224
 
    const qreal shortEdge = qMin(width, height);
1225
 
 
1226
 
    QString paperSize;
1227
 
 
1228
 
    if(qAbs(longEdge - 279.4) <= 1.0 && qAbs(shortEdge - 215.9) <= 1.0)
1229
 
    {
1230
 
        paperSize = QLatin1String(" (Letter)");
1231
 
    }
1232
 
    else
1233
 
    {
1234
 
        qreal longEdgeA = 1189.0;
1235
 
        qreal shortEdgeA = 841.0;
1236
 
 
1237
 
        qreal longEdgeB = 1414.0;
1238
 
        qreal shortEdgeB = 1000.0;
1239
 
 
1240
 
        for(int i = 0; i <= 10; ++i)
1241
 
        {
1242
 
            if(qAbs(longEdge - longEdgeA) <= 1.0 && qAbs(shortEdge - shortEdgeA) <= 1.0)
1243
 
            {
1244
 
                paperSize = QString(" (A%1)").arg(i);
1245
 
                break;
1246
 
            }
1247
 
            else if(qAbs(longEdge - longEdgeB) <= 1.0 && qAbs(shortEdge - shortEdgeB) <= 1.0)
1248
 
            {
1249
 
                paperSize = QString(" (B%1)").arg(i);
1250
 
                break;
1251
 
            }
1252
 
 
1253
 
            longEdgeA = shortEdgeA;
1254
 
            shortEdgeA /= qSqrt(2.0);
1255
 
 
1256
 
            longEdgeB = shortEdgeB;
1257
 
            shortEdgeB /= qSqrt(2.0);
1258
 
        }
1259
 
    }
1260
 
 
1261
 
    setToolTip(QString("%1 mm x %2 mm%3").arg(width, 0, 'f', 1).arg(height, 0, 'f', 1).arg(paperSize));
1262
 
}
1263
 
 
1264
1267
} // qpdfview