source: terepaima/terepaima-0.4.16/sources/pageitem.cpp

desarrollostretch
Last change on this file was 1f4adec, checked in by aosorio <aosorio@…>, 8 years ago

Agregado proyecto base, esto luego del dh_make -f

  • Property mode set to 100644
File size: 33.2 KB
Line 
1/*
2
3Copyright 2012-2015 Adam Reichold
4
5This file is part of qpdfview.
6
7qpdfview is free software: you can redistribute it and/or modify
8it under the terms of the GNU General Public License as published by
9the Free Software Foundation, either version 2 of the License, or
10(at your option) any later version.
11
12qpdfview is distributed in the hope that it will be useful,
13but WITHOUT ANY WARRANTY; without even the implied warranty of
14MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15GNU General Public License for more details.
16
17You should have received a copy of the GNU General Public License
18along with qpdfview.  If not, see <http://www.gnu.org/licenses/>.
19
20*/
21
22#include "pageitem.h"
23
24#include <QApplication>
25#include <QClipboard>
26#include <QFileDialog>
27#include <QGraphicsProxyWidget>
28#include <QGraphicsScene>
29#include <QGraphicsSceneHoverEvent>
30#include <qmath.h>
31#include <QMenu>
32#include <QMessageBox>
33#include <QPainter>
34#include <QStyleOptionGraphicsItem>
35#include <QTimer>
36#include <QToolTip>
37#include <QUrl>
38
39#include "settings.h"
40#include "model.h"
41#include "rendertask.h"
42#include "tileitem.h"
43
44namespace
45{
46
47using namespace qpdfview;
48
49const int largeTilesThreshold = 8;
50const int veryLargeTilesThreshold = 16;
51
52const qreal proxyPadding = 2.0;
53
54bool modifiersUseMouseButton(Settings* settings, Qt::MouseButton mouseButton)
55{
56    return ((settings->pageItem().copyToClipboardModifiers() | settings->pageItem().addAnnotationModifiers()) & mouseButton) != 0;
57}
58
59} // anonymous
60
61namespace qpdfview
62{
63
64Settings* PageItem::s_settings = 0;
65
66PageItem::PageItem(Model::Page* page, int index, PaintMode paintMode, QGraphicsItem* parent) : QGraphicsObject(parent),
67    m_page(page),
68    m_size(page->size()),
69    m_cropRect(),
70    m_index(index),
71    m_paintMode(paintMode),
72    m_highlights(),
73    m_links(),
74    m_annotations(),
75    m_formFields(),
76    m_rubberBandMode(ModifiersMode),
77    m_rubberBand(),
78    m_annotationOverlay(),
79    m_formFieldOverlay(),
80    m_renderParam(),
81    m_transform(),
82    m_normalizedTransform(),
83    m_boundingRect(),
84    m_tileItems()
85{
86    if(s_settings == 0)
87    {
88        s_settings = Settings::instance();
89    }
90
91    setAcceptHoverEvents(true);
92
93    setFlag(QGraphicsItem::ItemUsesExtendedStyleOption, useTiling());
94
95    if(!useTiling())
96    {
97        m_tileItems.resize(1);
98        m_tileItems.squeeze();
99
100        m_tileItems.replace(0, new TileItem(this));
101    }
102
103    QTimer::singleShot(0, this, SLOT(loadInteractiveElements()));
104
105    prepareGeometry();
106}
107
108PageItem::~PageItem()
109{
110    hideAnnotationOverlay(false);
111    hideFormFieldOverlay(false);
112
113    TileItem::dropCachedPixmaps(this);
114
115    qDeleteAll(m_links);
116    qDeleteAll(m_annotations);
117    qDeleteAll(m_formFields);
118}
119
120QRectF PageItem::boundingRect() const
121{
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;
135}
136
137void PageItem::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget*)
138{
139    paintPage(painter, option->exposedRect);
140
141    paintLinks(painter);
142    paintFormFields(painter);
143
144    paintHighlights(painter);
145    paintRubberBand(painter);
146}
147
148QSizeF PageItem::displayedSize(const RenderParam& renderParam) const
149{
150    const bool rotationChanged = m_renderParam.rotation() != renderParam.rotation();
151
152    const bool flagsChanged = m_renderParam.flags() != renderParam.flags();
153
154    const bool useCropRect = !m_cropRect.isNull() && !rotationChanged && !flagsChanged;
155
156    const qreal cropWidth = useCropRect ? m_cropRect.width() : 1.0;
157    const qreal cropHeight = useCropRect ? m_cropRect.height() : 1.0;
158
159    switch(renderParam.rotation())
160    {
161    default:
162    case RotateBy0:
163    case RotateBy180:
164        return QSizeF(renderParam.resolutionX() / 72.0 * cropWidth * m_size.width(),
165                      renderParam.resolutionY() / 72.0 * cropHeight * m_size.height());
166    case RotateBy90:
167    case RotateBy270:
168        return QSizeF(renderParam.resolutionX() / 72.0 * cropHeight * m_size.height(),
169                      renderParam.resolutionY() / 72.0 * cropWidth * m_size.width());
170    }
171}
172
173void PageItem::setHighlights(const QList< QRectF >& highlights)
174{
175    m_highlights = highlights;
176
177    update();
178}
179
180void PageItem::setRubberBandMode(RubberBandMode rubberBandMode)
181{
182    if(m_rubberBandMode != rubberBandMode && rubberBandMode >= 0 && rubberBandMode < NumberOfRubberBandModes)
183    {
184        m_rubberBandMode = rubberBandMode;
185
186        if(m_rubberBandMode == ModifiersMode)
187        {
188            unsetCursor();
189        }
190        else
191        {
192            setCursor(Qt::CrossCursor);
193        }
194    }
195}
196
197void PageItem::setRenderParam(const RenderParam& renderParam)
198{
199    if(m_renderParam != renderParam)
200    {
201        const bool resolutionChanged = m_renderParam.resolutionX() != renderParam.resolutionX()
202                || m_renderParam.resolutionY() != renderParam.resolutionY()
203                || !qFuzzyCompare(m_renderParam.devicePixelRatio(), renderParam.devicePixelRatio())
204                || !qFuzzyCompare(m_renderParam.scaleFactor(), renderParam.scaleFactor());
205
206        const bool rotationChanged = m_renderParam.rotation() != renderParam.rotation();
207
208        const RenderFlags changedFlags = m_renderParam.flags() ^ renderParam.flags();
209
210        refresh(!rotationChanged && changedFlags == 0);
211
212        m_renderParam = renderParam;
213
214        if(resolutionChanged || rotationChanged)
215        {
216            prepareGeometryChange();
217            prepareGeometry();
218        }
219
220        if(changedFlags.testFlag(TrimMargins))
221        {
222            setFlag(QGraphicsItem::ItemClipsToShape, m_renderParam.trimMargins());
223        }
224    }
225}
226
227void PageItem::refresh(bool keepObsoletePixmaps, bool dropCachedPixmaps)
228{
229    if(!useTiling())
230    {
231        m_tileItems.first()->refresh(keepObsoletePixmaps);
232    }
233    else
234    {
235        foreach(TileItem* tile, m_tileItems)
236        {
237            tile->refresh(keepObsoletePixmaps);
238        }
239    }
240
241    if(!keepObsoletePixmaps)
242    {
243        prepareGeometryChange();
244
245        m_cropRect = QRectF();
246    }
247
248    if(dropCachedPixmaps)
249    {
250        TileItem::dropCachedPixmaps(this);
251    }
252
253    update();
254}
255
256int PageItem::startRender(bool prefetch)
257{
258    int cost = 0;
259
260    if(!useTiling())
261    {
262        cost += m_tileItems.first()->startRender(prefetch);
263    }
264    else
265    {
266        foreach(TileItem* tile, m_tileItems)
267        {
268            cost += tile->startRender(prefetch);
269        }
270    }
271
272    return cost;
273}
274
275void PageItem::cancelRender()
276{
277    if(!useTiling())
278    {
279        m_tileItems.first()->cancelRender();
280    }
281    else
282    {
283        foreach(TileItem* tile, m_exposedTileItems)
284        {
285            tile->cancelRender();
286        }
287
288        m_exposedTileItems.clear();
289    }
290}
291
292void PageItem::showAnnotationOverlay(Model::Annotation* selectedAnnotation)
293{
294    if(s_settings->pageItem().annotationOverlay())
295    {
296        showOverlay(m_annotationOverlay, SLOT(hideAnnotationOverlay()), m_annotations, selectedAnnotation);
297    }
298    else
299    {
300        hideAnnotationOverlay(false);
301
302        addProxy(m_annotationOverlay, SLOT(hideAnnotationOverlay()), selectedAnnotation);
303        m_annotationOverlay.value(selectedAnnotation)->widget()->setFocus();
304    }
305}
306
307void PageItem::hideAnnotationOverlay(bool deleteLater)
308{
309    hideOverlay(m_annotationOverlay, deleteLater);
310}
311
312void PageItem::updateAnnotationOverlay()
313{
314    updateOverlay(m_annotationOverlay);
315}
316
317void PageItem::showFormFieldOverlay(Model::FormField* selectedFormField)
318{
319    if(s_settings->pageItem().formFieldOverlay())
320    {
321        showOverlay(m_formFieldOverlay, SLOT(hideFormFieldOverlay()), m_formFields, selectedFormField);
322    }
323    else
324    {
325        hideFormFieldOverlay(false);
326
327        addProxy(m_formFieldOverlay, SLOT(hideFormFieldOverlay()), selectedFormField);
328        m_formFieldOverlay.value(selectedFormField)->widget()->setFocus();
329    }
330}
331
332void PageItem::updateFormFieldOverlay()
333{
334    updateOverlay(m_formFieldOverlay);
335}
336
337void PageItem::hideFormFieldOverlay(bool deleteLater)
338{
339    hideOverlay(m_formFieldOverlay, deleteLater);
340}
341
342void PageItem::hoverEnterEvent(QGraphicsSceneHoverEvent*)
343{
344}
345
346void PageItem::hoverMoveEvent(QGraphicsSceneHoverEvent* event)
347{
348    if(m_rubberBandMode == ModifiersMode && event->modifiers() == Qt::NoModifier)
349    {
350        // links
351
352        foreach(const Model::Link* link, m_links)
353        {
354            if(m_normalizedTransform.map(link->boundary).contains(event->pos()))
355            {
356                if(link->page != -1 && (link->urlOrFileName.isNull() || !presentationMode()))
357                {
358                    setCursor(Qt::PointingHandCursor);
359
360                    if(link->urlOrFileName.isNull())
361                    {
362                        QToolTip::showText(event->screenPos(), tr("Go to page %1.").arg(link->page));
363                    }
364                    else
365                    {
366                        QToolTip::showText(event->screenPos(), tr("Go to page %1 of file '%2'.").arg(link->page).arg(link->urlOrFileName));
367                    }
368
369                    return;
370                }
371                else if(!link->urlOrFileName.isNull() && !presentationMode())
372                {
373                    setCursor(Qt::PointingHandCursor);
374                    QToolTip::showText(event->screenPos(), tr("Open '%1'.").arg(link->urlOrFileName));
375
376                    return;
377                }
378            }
379        }
380
381        if(presentationMode())
382        {
383            unsetCursor();
384            QToolTip::hideText();
385
386            return;
387        }
388
389        // annotations
390
391        foreach(const Model::Annotation* annotation, m_annotations)
392        {
393            if(m_normalizedTransform.mapRect(annotation->boundary()).contains(event->pos()))
394            {
395                setCursor(Qt::PointingHandCursor);
396                QToolTip::showText(event->screenPos(), annotation->contents());
397
398                return;
399            }
400        }
401
402        // form fields
403
404        foreach(const Model::FormField* formField, m_formFields)
405        {
406            if(m_normalizedTransform.mapRect(formField->boundary()).contains(event->pos()))
407            {
408                setCursor(Qt::PointingHandCursor);
409                QToolTip::showText(event->screenPos(), tr("Edit form field '%1'.").arg(formField->name()));
410
411                return;
412            }
413        }
414
415        unsetCursor();
416        QToolTip::hideText();
417    }
418}
419
420void PageItem::hoverLeaveEvent(QGraphicsSceneHoverEvent*)
421{
422}
423
424void PageItem::mousePressEvent(QGraphicsSceneMouseEvent* event)
425{
426    const Qt::KeyboardModifiers copyToClipboardModifiers = s_settings->pageItem().copyToClipboardModifiers();
427    const Qt::KeyboardModifiers addAnnotationModifiers = s_settings->pageItem().addAnnotationModifiers();
428    const Qt::KeyboardModifiers zoomToSelectionModifiers = s_settings->pageItem().zoomToSelectionModifiers();
429
430    const bool copyToClipboardModifiersActive = event->modifiers() == copyToClipboardModifiers || ((event->buttons() & copyToClipboardModifiers) != 0);
431    const bool addAnnotationModifiersActive = event->modifiers() == addAnnotationModifiers || ((event->buttons() & addAnnotationModifiers) != 0);
432    const bool zoomToSelectionModifiersActive = event->modifiers() == zoomToSelectionModifiers || ((event->buttons() & zoomToSelectionModifiers) != 0);
433
434    // rubber band
435
436    if(m_rubberBandMode == ModifiersMode && !presentationMode()
437            && (copyToClipboardModifiersActive || addAnnotationModifiersActive || zoomToSelectionModifiersActive)
438            && event->button() == Qt::LeftButton)
439    {
440        setCursor(Qt::CrossCursor);
441
442        if(copyToClipboardModifiersActive)
443        {
444            m_rubberBandMode = CopyToClipboardMode;
445        }
446        else if(addAnnotationModifiersActive)
447        {
448            m_rubberBandMode = AddAnnotationMode;
449        }
450        else if(zoomToSelectionModifiersActive)
451        {
452            m_rubberBandMode = ZoomToSelectionMode;
453        }
454    }
455
456    if(m_rubberBandMode != ModifiersMode)
457    {
458        m_rubberBand = QRectF(event->pos(), QSizeF());
459
460        emit rubberBandStarted();
461
462        update();
463
464        event->accept();
465        return;
466    }
467
468    if(event->modifiers() == Qt::NoModifier
469            && (event->button() == Qt::LeftButton || event->button() == Qt::MidButton))
470    {
471        // links
472
473        foreach(const Model::Link* link, m_links)
474        {
475            if(m_normalizedTransform.map(link->boundary).contains(event->pos()))
476            {
477                unsetCursor();
478
479                if(link->page != -1 && (link->urlOrFileName.isNull() || !presentationMode()))
480                {
481                    const bool newTab = event->button() == Qt::MidButton;
482
483                    if(link->urlOrFileName.isNull())
484                    {
485                        emit linkClicked(newTab, link->page, link->left, link->top);
486                    }
487                    else
488                    {
489                        emit linkClicked(newTab, link->urlOrFileName, link->page);
490                    }
491
492                    event->accept();
493                    return;
494                }
495                else if(!link->urlOrFileName.isNull() && !presentationMode())
496                {
497                    emit linkClicked(link->urlOrFileName);
498
499                    event->accept();
500                    return;
501                }
502            }
503        }
504
505        if(event->button() == Qt::MidButton || presentationMode())
506        {
507            event->ignore();
508            return;
509        }
510
511        // annotations
512
513        foreach(Model::Annotation* annotation, m_annotations)
514        {
515            if(m_normalizedTransform.mapRect(annotation->boundary()).contains(event->pos()))
516            {
517                unsetCursor();
518
519                showAnnotationOverlay(annotation);
520
521                event->accept();
522                return;
523            }
524        }
525
526        hideAnnotationOverlay();
527
528        // form fields
529
530        foreach(Model::FormField* formField, m_formFields)
531        {
532            if(m_normalizedTransform.mapRect(formField->boundary()).contains(event->pos()))
533            {
534                unsetCursor();
535
536                showFormFieldOverlay(formField);
537
538                event->accept();
539                return;
540            }
541        }
542
543        hideFormFieldOverlay();
544    }
545
546    event->ignore();
547}
548
549void PageItem::mouseMoveEvent(QGraphicsSceneMouseEvent* event)
550{
551    if(!m_rubberBand.isNull())
552    {
553        if(m_boundingRect.contains(event->pos()))
554        {
555            m_rubberBand.setBottomRight(event->pos());
556
557            update();
558
559            event->accept();
560            return;
561        }
562    }
563
564    event->ignore();
565}
566
567void PageItem::mouseReleaseEvent(QGraphicsSceneMouseEvent* event)
568{
569    if(!m_rubberBand.isNull())
570    {
571        unsetCursor();
572
573        m_rubberBand = m_rubberBand.normalized();
574
575        if(m_rubberBandMode == CopyToClipboardMode)
576        {
577            copyToClipboard(event->screenPos());
578        }
579        else if(m_rubberBandMode == AddAnnotationMode)
580        {
581            addAnnotation(event->screenPos());
582        }
583        else if(m_rubberBandMode == ZoomToSelectionMode)
584        {
585            emit zoomToSelection(m_index + 1, m_normalizedTransform.inverted().mapRect(m_rubberBand));
586        }
587
588        m_rubberBandMode = ModifiersMode;
589        m_rubberBand = QRectF();
590
591        emit rubberBandFinished();
592
593        update();
594
595        event->accept();
596        return;
597    }
598
599    event->ignore();
600}
601
602void PageItem::contextMenuEvent(QGraphicsSceneContextMenuEvent* event)
603{
604    if(event->reason() == QGraphicsSceneContextMenuEvent::Mouse && modifiersUseMouseButton(s_settings, Qt::RightButton))
605    {
606        event->accept();
607        return;
608    }
609
610    if(presentationMode())
611    {
612        event->ignore();
613        return;
614    }
615
616    foreach(Model::Link* link, m_links)
617    {
618        if(m_normalizedTransform.map(link->boundary).contains(event->pos()))
619        {
620            unsetCursor();
621
622            showLinkContextMenu(link, event->screenPos());
623
624            event->accept();
625            return;
626        }
627    }
628
629    foreach(Model::Annotation* annotation, m_annotations)
630    {
631        if(m_normalizedTransform.mapRect(annotation->boundary()).contains(event->pos()))
632        {
633            unsetCursor();
634
635            showAnnotationContextMenu(annotation, event->screenPos());
636
637            event->accept();
638            return;
639        }
640    }
641
642    event->ignore();
643}
644
645void PageItem::loadInteractiveElements()
646{
647    m_links = m_page->links();
648
649    if(!presentationMode())
650    {
651        m_annotations = m_page->annotations();
652
653        foreach(const Model::Annotation* annotation, m_annotations)
654        {
655            connect(annotation, SIGNAL(wasModified()), SIGNAL(wasModified()));
656        }
657
658        m_formFields = m_page->formFields();
659
660        foreach(const Model::FormField* formField, m_formFields)
661        {
662            connect(formField, SIGNAL(wasModified()), SIGNAL(wasModified()));
663        }
664    }
665
666    update();
667}
668
669void PageItem::updateCropRect()
670{
671    QRectF cropRect;
672
673    if(!useTiling())
674    {
675        cropRect = m_tileItems.first()->cropRect();
676    }
677    else
678    {
679        foreach(TileItem* tile, m_tileItems)
680        {
681            const QRect& tileRect = tile->rect();
682            const QRectF& tileCropRect = tile->cropRect();
683
684            if(tileCropRect.isNull())
685            {
686                cropRect = QRectF();
687                break;
688            }
689
690            const qreal left = (tileRect.left() + tileCropRect.left() * tileRect.width()) / m_boundingRect.width();
691            const qreal top = (tileRect.top() + tileCropRect.top() * tileRect.height()) / m_boundingRect.height();
692            const qreal width = tileCropRect.width() * tileRect.width() / m_boundingRect.width();
693            const qreal height = tileCropRect.height() * tileRect.height() / m_boundingRect.height();
694
695            cropRect = cropRect.united(QRectF(left, top, width, height));
696        }
697    }
698
699    if(m_cropRect.isNull() && !cropRect.isNull())
700    {
701        prepareGeometryChange();
702
703        m_cropRect = cropRect;
704
705        emit cropRectChanged();
706    }
707}
708
709inline bool PageItem::presentationMode() const
710{
711    return m_paintMode == PresentationMode;
712}
713
714inline bool PageItem::thumbnailMode() const
715{
716    return m_paintMode == ThumbnailMode;
717}
718
719bool PageItem::useTiling() const
720{
721    return m_paintMode != ThumbnailMode && s_settings->pageItem().useTiling();
722}
723
724void PageItem::copyToClipboard(const QPoint& screenPos)
725{
726    QMenu menu;
727
728    QAction* copyTextAction = menu.addAction(tr("Copy &text"));
729    QAction* selectTextAction = menu.addAction(tr("&Select text"));
730    const QAction* copyImageAction = menu.addAction(tr("Copy &image"));
731    const QAction* saveImageToFileAction = menu.addAction(tr("Save image to &file..."));
732
733    const QString text = m_page->text(m_transform.inverted().mapRect(m_rubberBand));
734
735    copyTextAction->setVisible(!text.isEmpty());
736    selectTextAction->setVisible(!text.isEmpty() && QApplication::clipboard()->supportsSelection());
737
738    const QAction* action = menu.exec(screenPos);
739
740    if(action == copyTextAction || action == selectTextAction)
741    {
742        if(action == copyTextAction)
743        {
744            QApplication::clipboard()->setText(text);
745        }
746        else
747        {
748            QApplication::clipboard()->setText(text, QClipboard::Selection);
749        }
750    }
751    else if(action == copyImageAction || action == saveImageToFileAction)
752    {
753        const QRect rect = m_rubberBand.translated(-m_boundingRect.topLeft()).toRect();
754        const QImage image = m_page->render(m_renderParam.resolutionX() * m_renderParam.scaleFactor(),
755                                            m_renderParam.resolutionY() * m_renderParam.scaleFactor(),
756                                            m_renderParam.rotation(), rect);
757
758        if(!image.isNull())
759        {
760            if(action == copyImageAction)
761            {
762                QApplication::clipboard()->setImage(image);
763            }
764            else if(action == saveImageToFileAction)
765            {
766                const QString fileName = QFileDialog::getSaveFileName(0, tr("Save image to file"), QDir::homePath(), "Portable network graphics (*.png)");
767
768                if(!image.save(fileName, "PNG"))
769                {
770                    QMessageBox::warning(0, tr("Warning"), tr("Could not save image to file '%1'.").arg(fileName));
771                }
772            }
773        }
774    }
775}
776
777void PageItem::addAnnotation(const QPoint& screenPos)
778{
779    if(m_page->canAddAndRemoveAnnotations())
780    {
781        QMenu menu;
782
783        const QAction* addTextAction = menu.addAction(tr("Add &text"));
784        const QAction* addHighlightAction = menu.addAction(tr("Add &highlight"));
785
786        const QAction* action = menu.exec(screenPos);
787
788        if(action == addTextAction || action == addHighlightAction)
789        {
790            QRectF boundary = m_normalizedTransform.inverted().mapRect(m_rubberBand);
791
792            Model::Annotation* annotation = 0;
793
794            if(action == addTextAction)
795            {
796                boundary.setWidth(24.0 / m_size.width());
797                boundary.setHeight(24.0 / m_size.height());
798
799                annotation = m_page->addTextAnnotation(boundary, s_settings->pageItem().annotationColor());
800            }
801            else if(action == addHighlightAction)
802            {
803                annotation = m_page->addHighlightAnnotation(boundary, s_settings->pageItem().annotationColor());
804            }
805
806            m_annotations.append(annotation);
807            connect(annotation, SIGNAL(wasModified()), SIGNAL(wasModified()));
808
809            refresh(false, true);
810            emit wasModified();
811
812            showAnnotationOverlay(annotation);
813        }
814    }
815}
816
817void PageItem::showLinkContextMenu(Model::Link* link, const QPoint& screenPos)
818{
819    if(link->page == -1)
820    {
821        QMenu menu;
822
823        const QAction* copyLinkAddressAction = menu.addAction(tr("&Copy link address"));
824        QAction* selectLinkAddressAction = menu.addAction(tr("&Select link address"));
825
826        selectLinkAddressAction->setVisible(QApplication::clipboard()->supportsSelection());
827
828        const QAction* action = menu.exec(screenPos);
829
830        if(action == copyLinkAddressAction)
831        {
832            QApplication::clipboard()->setText(link->urlOrFileName);
833        }
834        else if(action == selectLinkAddressAction)
835        {
836            QApplication::clipboard()->setText(link->urlOrFileName, QClipboard::Selection);
837        }
838    }
839}
840
841void PageItem::showAnnotationContextMenu(Model::Annotation* annotation, const QPoint& screenPos)
842{
843    if(m_page->canAddAndRemoveAnnotations())
844    {
845        QMenu menu;
846
847        const QAction* removeAnnotationAction = menu.addAction(tr("&Remove annotation"));
848
849        const QAction* action = menu.exec(screenPos);
850
851        if(action == removeAnnotationAction)
852        {
853            m_annotations.removeAll(annotation);
854            m_page->removeAnnotation(annotation);
855
856            annotation->deleteLater();
857
858            refresh(false, true);
859            emit wasModified();
860        }
861    }
862}
863
864template< typename Overlay, typename Element >
865void PageItem::showOverlay(Overlay& overlay, const char* hideOverlay, const QList< Element* >& elements, Element* selectedElement)
866{
867    foreach(Element* element, elements)
868    {
869        if(!overlay.contains(element))
870        {
871            addProxy(overlay, hideOverlay, element);
872        }
873
874        if(element == selectedElement)
875        {
876            overlay.value(element)->widget()->setFocus();
877        }
878    }
879}
880
881template< typename Overlay, typename Element >
882void PageItem::addProxy(Overlay& overlay, const char* hideOverlay, Element* element)
883{
884    QGraphicsProxyWidget* proxy = new QGraphicsProxyWidget(this);
885    proxy->setWidget(element->createWidget());
886
887#if QT_VERSION >= QT_VERSION_CHECK(4,7,0)
888
889    proxy->setAutoFillBackground(true);
890
891#endif // QT_VERSION
892
893    overlay.insert(element, proxy);
894    setProxyGeometry(element, proxy);
895
896    connect(proxy, SIGNAL(visibleChanged()), hideOverlay);
897}
898
899template< typename Overlay >
900void PageItem::hideOverlay(Overlay& overlay, bool deleteLater)
901{
902#if QT_VERSION >= QT_VERSION_CHECK(4,8,0)
903
904    Overlay discardedOverlay;
905    discardedOverlay.swap(overlay);
906
907#else
908
909    Overlay discardedOverlay(overlay);
910    overlay = Overlay();
911
912#endif // QT_VERSION
913
914    if(!discardedOverlay.isEmpty())
915    {
916        for(typename Overlay::const_iterator i = discardedOverlay.constBegin(); i != discardedOverlay.constEnd(); ++i)
917        {
918            if(deleteLater)
919            {
920                i.value()->deleteLater();
921            }
922            else
923            {
924                delete i.value();
925            }
926        }
927
928        refresh(false, true);
929    }
930}
931
932template< typename Overlay >
933void PageItem::updateOverlay(const Overlay& overlay) const
934{
935    for(typename Overlay::const_iterator i = overlay.constBegin(); i != overlay.constEnd(); ++i)
936    {
937        setProxyGeometry(i.key(), i.value());
938    }
939}
940
941void PageItem::setProxyGeometry(Model::Annotation* annotation, QGraphicsProxyWidget* proxy) const
942{
943    const QPointF center = m_normalizedTransform.map(annotation->boundary().center());
944
945    qreal x = center.x() - 0.5 * proxy->preferredWidth();
946    qreal y = center.y() - 0.5 * proxy->preferredHeight();
947    qreal width = proxy->preferredWidth();
948    qreal height = proxy->preferredHeight();
949
950    x = qMax(x, m_boundingRect.left() + proxyPadding);
951    y = qMax(y, m_boundingRect.top() + proxyPadding);
952
953    width = qMin(width, m_boundingRect.right() - proxyPadding - x);
954    height = qMin(height, m_boundingRect.bottom() - proxyPadding - y);
955
956    proxy->setGeometry(QRectF(x, y, width, height));
957}
958
959void PageItem::setProxyGeometry(Model::FormField* formField, QGraphicsProxyWidget* proxy) const
960{
961    QRectF rect = m_normalizedTransform.mapRect(formField->boundary());
962
963    qreal x = rect.x();
964    qreal y = rect.y();
965    qreal width = rect.width();
966    qreal height = rect.height();
967
968    switch(m_renderParam.rotation())
969    {
970    default:
971    case RotateBy0:
972        proxy->setRotation(0.0);
973        break;
974    case RotateBy90:
975        x += width;
976        qSwap(width, height);
977
978        proxy->setRotation(90.0);
979        break;
980    case RotateBy180:
981        x += width;
982        y += height;
983
984        proxy->setRotation(180.0);
985        break;
986    case RotateBy270:
987        y += height;
988        qSwap(width, height);
989
990        proxy->setRotation(270.0);
991        break;
992    }
993
994    width /= m_renderParam.scaleFactor();
995    height /= m_renderParam.scaleFactor();
996
997    proxy->setScale(m_renderParam.scaleFactor());
998
999    proxy->setGeometry(QRectF(x - proxyPadding, y - proxyPadding, width + proxyPadding, height + proxyPadding));
1000}
1001
1002void PageItem::prepareGeometry()
1003{
1004    m_transform.reset();
1005
1006    m_transform.scale(m_renderParam.resolutionX() * m_renderParam.scaleFactor() / 72.0,
1007                      m_renderParam.resolutionY() * m_renderParam.scaleFactor() / 72.0);
1008
1009    switch(m_renderParam.rotation())
1010    {
1011    default:
1012    case RotateBy0:
1013        break;
1014    case RotateBy90:
1015        m_transform.rotate(90.0);
1016        break;
1017    case RotateBy180:
1018        m_transform.rotate(180.0);
1019        break;
1020    case RotateBy270:
1021        m_transform.rotate(270.0);
1022        break;
1023    }
1024
1025    m_normalizedTransform = m_transform;
1026    m_normalizedTransform.scale(m_size.width(), m_size.height());
1027
1028
1029    m_boundingRect = m_transform.mapRect(QRectF(QPointF(), m_size));
1030
1031    m_boundingRect.setWidth(qRound(m_boundingRect.width()));
1032    m_boundingRect.setHeight(qRound(m_boundingRect.height()));
1033
1034
1035    prepareTiling();
1036
1037    updateAnnotationOverlay();
1038    updateFormFieldOverlay();
1039}
1040
1041void PageItem::prepareTiling()
1042{
1043    if(!useTiling())
1044    {
1045        m_tileItems.first()->setRect(QRect(0, 0, m_boundingRect.width(), m_boundingRect.height()));
1046
1047        return;
1048    }
1049
1050
1051    const qreal pageWidth = m_boundingRect.width();
1052    const qreal pageHeight = m_boundingRect.height();
1053    const qreal pageSize = qMax(pageWidth, pageHeight);
1054
1055    int tileSize = s_settings->pageItem().tileSize();
1056
1057    if(tileSize * veryLargeTilesThreshold < pageSize)
1058    {
1059        tileSize *= 4;
1060    }
1061    else if(tileSize * largeTilesThreshold < pageSize)
1062    {
1063        tileSize *= 2;
1064    }
1065
1066    int tileWidth = pageWidth < pageHeight ? tileSize * pageWidth / pageHeight : tileSize;
1067    int tileHeight = pageHeight < pageWidth ? tileSize * pageHeight / pageWidth : tileSize;
1068
1069    const int columnCount = qCeil(pageWidth / tileWidth);
1070    const int rowCount = qCeil(pageHeight / tileHeight);
1071
1072    tileWidth = qCeil(pageWidth / columnCount);
1073    tileHeight = qCeil(pageHeight / rowCount);
1074
1075
1076    const int newCount = columnCount * rowCount;
1077    const int oldCount = m_tileItems.count();
1078
1079    if(oldCount != newCount)
1080    {
1081        for(int index = newCount; index < oldCount; ++index)
1082        {
1083            m_tileItems.at(index)->deleteAfterRender();
1084        }
1085
1086        m_tileItems.resize(newCount);
1087
1088        for(int index = oldCount; index < newCount; ++index)
1089        {
1090            m_tileItems.replace(index, new TileItem(this));
1091        }
1092
1093        foreach(TileItem* tile, m_tileItems)
1094        {
1095            tile->dropObsoletePixmap();
1096        }
1097    }
1098
1099    m_exposedTileItems.clear();
1100
1101
1102    for(int column = 0; column < columnCount; ++column)
1103    {
1104        for(int row = 0; row < rowCount; ++row)
1105        {
1106            const int left = column > 0 ? column * tileWidth : 0.0;
1107            const int top = row > 0 ? row * tileHeight : 0.0;
1108
1109            const int width = column < (columnCount - 1) ? tileWidth : pageWidth - left;
1110            const int height = row < (rowCount - 1) ? tileHeight : pageHeight - top;
1111
1112            m_tileItems.at(column * rowCount + row)->setRect(QRect(left, top, width, height));
1113        }
1114    }
1115}
1116
1117inline void PageItem::paintPage(QPainter* painter, const QRectF& exposedRect) const
1118{
1119    if(s_settings->pageItem().decoratePages() && !presentationMode())
1120    {
1121        // background
1122
1123        QColor paperColor = s_settings->pageItem().paperColor();
1124
1125        if(m_renderParam.invertColors())
1126        {
1127            paperColor.setRgb(~paperColor.rgb());
1128        }
1129
1130        painter->fillRect(m_boundingRect, QBrush(paperColor));
1131    }
1132
1133    // tiles
1134
1135    if(!useTiling())
1136    {
1137        TileItem* tile = m_tileItems.first();
1138
1139        if(tile->paint(painter, m_boundingRect.topLeft()))
1140        {
1141            tile->dropPixmap();
1142        }
1143    }
1144    else
1145    {
1146        const QRectF& translatedExposedRect = exposedRect.translated(-m_boundingRect.topLeft());
1147
1148        foreach(TileItem* tile, m_tileItems)
1149        {
1150            const bool intersects = translatedExposedRect.intersects(tile->rect());
1151            const bool contains = m_exposedTileItems.contains(tile);
1152
1153            if(intersects && !contains)
1154            {
1155                m_exposedTileItems.insert(tile);
1156            }
1157            else if(!intersects && contains)
1158            {
1159                m_exposedTileItems.remove(tile);
1160
1161                tile->cancelRender();
1162            }
1163        }
1164
1165        bool allExposedPainted = true;
1166
1167        foreach(TileItem* tile, m_exposedTileItems)
1168        {
1169            if(!tile->paint(painter, m_boundingRect.topLeft()))
1170            {
1171                allExposedPainted = false;
1172            }
1173        }
1174
1175        if(allExposedPainted)
1176        {
1177            foreach(TileItem* tile, m_exposedTileItems)
1178            {
1179                tile->dropPixmap();
1180            }
1181        }
1182    }
1183
1184    if(s_settings->pageItem().decoratePages() && !presentationMode())
1185    {
1186        // border
1187
1188        painter->save();
1189
1190        painter->setClipping(false);
1191
1192        painter->drawRect(m_renderParam.trimMargins() ? PageItem::boundingRect() : PageItem::uncroppedBoundingRect());
1193
1194        painter->restore();
1195    }
1196}
1197
1198inline void PageItem::paintLinks(QPainter* painter) const
1199{
1200    if(s_settings->pageItem().decorateLinks() && !presentationMode() && !m_links.isEmpty())
1201    {
1202        painter->save();
1203
1204        painter->setTransform(m_normalizedTransform, true);
1205        painter->setPen(QPen(Qt::red, 0.0));
1206
1207        foreach(const Model::Link* link, m_links)
1208        {
1209            painter->drawPath(link->boundary);
1210        }
1211
1212        painter->restore();
1213    }
1214}
1215
1216inline void PageItem::paintFormFields(QPainter* painter) const
1217{
1218    if(s_settings->pageItem().decorateFormFields() && !presentationMode() && !m_formFields.isEmpty())
1219    {
1220        painter->save();
1221
1222        painter->setTransform(m_normalizedTransform, true);
1223        painter->setPen(QPen(Qt::blue, 0.0));
1224
1225        foreach(const Model::FormField* formField, m_formFields)
1226        {
1227            painter->drawRect(formField->boundary());
1228        }
1229
1230        painter->restore();
1231    }
1232}
1233
1234inline void PageItem::paintHighlights(QPainter* painter) const
1235{
1236    if(!m_highlights.isEmpty())
1237    {
1238        painter->save();
1239
1240        painter->setTransform(m_transform, true);
1241        painter->setPen(QPen(s_settings->pageItem().highlightColor(), 0.0));
1242        painter->setBrush(QBrush(s_settings->pageItem().highlightColor()));
1243        painter->setCompositionMode(QPainter::CompositionMode_Multiply);
1244
1245        foreach(const QRectF highlight, m_highlights)
1246        {
1247            painter->drawRect(highlight.normalized());
1248        }
1249
1250        painter->restore();
1251    }
1252}
1253
1254inline void PageItem::paintRubberBand(QPainter* painter) const
1255{
1256    if(!m_rubberBand.isNull())
1257    {
1258        painter->save();
1259
1260        painter->setPen(QPen(Qt::white, 0.0, Qt::DashLine));
1261        painter->setCompositionMode(QPainter::CompositionMode_Difference);
1262
1263        painter->drawRect(m_rubberBand);
1264
1265        painter->restore();
1266    }
1267}
1268
1269} // qpdfview
Note: See TracBrowser for help on using the repository browser.