You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
721 lines
26 KiB
721 lines
26 KiB
/*************************************************************************** |
|
* Copyright (C) 2017 by Tobias Deiminger <haxtibal@t-online.de> * |
|
* Copyright (C) 2004-2005 by Enrico Ros <eros.kde@email.it> * |
|
* Copyright (C) 2004-2006 by Albert Astals Cid <aacid@kde.org> * |
|
* * |
|
* With portions of code from kpdf/kpdf_pagewidget.cc by: * |
|
* Copyright (C) 2002 by Wilco Greven <greven@kde.org> * |
|
* Copyright (C) 2003 by Christophe Devriese * |
|
* <Christophe.Devriese@student.kuleuven.ac.be> * |
|
* Copyright (C) 2003 by Laurent Montel <montel@kde.org> * |
|
* Copyright (C) 2003 by Dirk Mueller <mueller@kde.org> * |
|
* Copyright (C) 2004 by James Ots <kde@jamesots.com> * |
|
* Copyright (C) 2011 by Jiri Baum - NICTA <jiri@baum.com.au> * |
|
* * |
|
* 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. * |
|
***************************************************************************/ |
|
|
|
#include "pageviewmouseannotation.h" |
|
|
|
#include <qpainter.h> |
|
#include <qtooltip.h> |
|
#include <qevent.h> |
|
|
|
#include "core/annotations.h" |
|
#include "core/document.h" |
|
#include "core/page.h" |
|
#include "ui/guiutils.h" |
|
#include "ui/pageview.h" |
|
#include "ui/videowidget.h" |
|
|
|
static const int handleSize = 10; |
|
static const int handleSizeHalf = handleSize / 2; |
|
|
|
bool AnnotationDescription::isValid() const |
|
{ |
|
return ( annotation != 0 ); |
|
} |
|
|
|
void AnnotationDescription::invalidate() |
|
{ |
|
annotation = nullptr; |
|
pageViewItem = nullptr; |
|
pageNumber = -1; |
|
} |
|
|
|
AnnotationDescription::AnnotationDescription( PageViewItem * newPageViewItem, const QPoint& eventPos ) |
|
{ |
|
const Okular::AnnotationObjectRect * annObjRect = 0; |
|
if ( newPageViewItem ) |
|
{ |
|
const QRect & uncroppedPage = newPageViewItem->uncroppedGeometry(); |
|
/* find out normalized mouse coords inside current item (nX and nY will be in the range of 0..1). */ |
|
const double nX = newPageViewItem->absToPageX( eventPos.x() ); |
|
const double nY = newPageViewItem->absToPageY( eventPos.y() ); |
|
annObjRect = (Okular::AnnotationObjectRect *) newPageViewItem->page()->objectRect( |
|
Okular::ObjectRect::OAnnotation, nX, nY, uncroppedPage.width(), uncroppedPage.height() ); |
|
} |
|
|
|
if ( annObjRect ) |
|
{ |
|
annotation = annObjRect->annotation(); |
|
pageViewItem = newPageViewItem; |
|
pageNumber = pageViewItem->pageNumber(); |
|
} |
|
else |
|
{ |
|
invalidate(); |
|
} |
|
} |
|
|
|
MouseAnnotation::MouseAnnotation( PageView * parent, Okular::Document * document) |
|
: QObject( parent ), m_document( document ), m_pageView( parent ), |
|
m_state( StateInactive ), m_handle( RH_None ) |
|
{ |
|
m_resizeHandleList << RH_Left << RH_Right << RH_Top << RH_Bottom |
|
<< RH_TopLeft << RH_TopRight << RH_BottomLeft << RH_BottomRight; |
|
} |
|
|
|
MouseAnnotation::~MouseAnnotation() |
|
{ |
|
} |
|
|
|
void MouseAnnotation::routeMousePressEvent( PageViewItem * pageViewItem, const QPoint & eventPos ) |
|
{ |
|
/* Is there a selected annotation? */ |
|
if ( m_focusedAnnotation.isValid() ) |
|
{ |
|
m_mousePosition = eventPos - pageViewItem->uncroppedGeometry().topLeft(); |
|
m_handle = getHandleAt( m_mousePosition, m_focusedAnnotation ); |
|
if ( m_handle != RH_None ) |
|
{ |
|
/* Returning here means, the selection-rectangle gets control, unconditionally. |
|
* Even if it overlaps with another annotation. */ |
|
return; |
|
} |
|
} |
|
|
|
AnnotationDescription ad( pageViewItem, eventPos ); |
|
/* qDebug() << "routeMousePressEvent: eventPos = " << eventPos; */ |
|
if ( ad.isValid() ) |
|
{ |
|
if ( ad.annotation->subType() == Okular::Annotation::AMovie || |
|
ad.annotation->subType() == Okular::Annotation::AScreen || |
|
ad.annotation->subType() == Okular::Annotation::AFileAttachment ) |
|
{ |
|
/* qDebug() << "routeMousePressEvent: trigger action for AMovie/AScreen/AFileAttachment"; */ |
|
processAction( ad ); |
|
} |
|
else |
|
{ |
|
/* qDebug() << "routeMousePressEvent: select for modification"; */ |
|
m_mousePosition = eventPos - pageViewItem->uncroppedGeometry().topLeft(); |
|
m_handle = getHandleAt( m_mousePosition, ad ); |
|
if ( m_handle != RH_None ) |
|
{ |
|
setState( StateFocused, ad ); |
|
} |
|
} |
|
} |
|
else |
|
{ |
|
/* qDebug() << "routeMousePressEvent: no annotation under mouse, enter StateInactive"; */ |
|
setState( StateInactive, ad ); |
|
} |
|
} |
|
|
|
void MouseAnnotation::routeMouseReleaseEvent() |
|
{ |
|
if ( isModified() ) |
|
{ |
|
/* qDebug() << "routeMouseReleaseEvent: finish command"; */ |
|
finishCommand(); |
|
setState( StateFocused, m_focusedAnnotation ); |
|
} |
|
/* |
|
else |
|
{ |
|
qDebug() << "routeMouseReleaseEvent: ignore"; |
|
} |
|
*/ |
|
} |
|
|
|
void MouseAnnotation::routeMouseMoveEvent( PageViewItem * pageViewItem, const QPoint & eventPos, bool leftButtonPressed ) |
|
{ |
|
if ( !pageViewItem ) |
|
{ |
|
/* qDebug() << "routeMouseMoveEvent: no pageViewItem provided, ignore"; */ |
|
return; |
|
} |
|
|
|
if ( leftButtonPressed ) |
|
{ |
|
if ( isFocused() ) |
|
{ |
|
/* On first move event after annotation is selected, enter modification state */ |
|
if ( m_handle == RH_Content ) |
|
{ |
|
/* qDebug() << "routeMouseMoveEvent: handle " << m_handle << ", enter StateMoving"; */ |
|
setState( StateMoving, m_focusedAnnotation ); |
|
} |
|
else if ( m_handle != RH_None ) |
|
{ |
|
/* qDebug() << "routeMouseMoveEvent: handle " << m_handle << ", enter StateResizing"; */ |
|
setState( StateResizing, m_focusedAnnotation ); |
|
} |
|
} |
|
|
|
if ( isModified() ) |
|
{ |
|
/* qDebug() << "routeMouseMoveEvent: perform command, delta " << eventPos - m_mousePosition; */ |
|
updateViewport( m_focusedAnnotation ); |
|
performCommand( eventPos ); |
|
m_mousePosition = eventPos - pageViewItem->uncroppedGeometry().topLeft(); |
|
updateViewport( m_focusedAnnotation ); |
|
} |
|
} |
|
else |
|
{ |
|
if ( isFocused() ) |
|
{ |
|
/* qDebug() << "routeMouseMoveEvent: update cursor for focused annotation, new eventPos " << eventPos; */ |
|
m_mousePosition = eventPos - pageViewItem->uncroppedGeometry().topLeft(); |
|
m_handle = getHandleAt( m_mousePosition, m_focusedAnnotation ); |
|
m_pageView->updateCursor(); |
|
} |
|
|
|
/* We get here quite frequently. */ |
|
const AnnotationDescription ad( pageViewItem, eventPos ); |
|
m_mousePosition = eventPos - pageViewItem->uncroppedGeometry().topLeft(); |
|
if ( ad.isValid() ) |
|
{ |
|
if ( !( m_mouseOverAnnotation == ad ) ) |
|
{ |
|
/* qDebug() << "routeMouseMoveEvent: Annotation under mouse (subtype " << ad.annotation->subType() << ", flags " << ad.annotation->flags() << ")"; */ |
|
m_mouseOverAnnotation = ad; |
|
m_pageView->updateCursor(); |
|
} |
|
} |
|
else |
|
{ |
|
if ( !( m_mouseOverAnnotation == ad ) ) |
|
{ |
|
/* qDebug() << "routeMouseMoveEvent: Annotation disappeared under mouse."; */ |
|
m_mouseOverAnnotation.invalidate(); |
|
m_pageView->updateCursor(); |
|
} |
|
} |
|
} |
|
} |
|
|
|
void MouseAnnotation::routeKeyPressEvent( const QKeyEvent * e ) |
|
{ |
|
switch ( e->key() ) |
|
{ |
|
case Qt::Key_Escape: |
|
cancel(); |
|
break; |
|
case Qt::Key_Delete: |
|
if ( m_focusedAnnotation.isValid() ) |
|
{ |
|
AnnotationDescription adToBeDeleted = m_focusedAnnotation; |
|
cancel(); |
|
m_document->removePageAnnotation( adToBeDeleted.pageNumber, adToBeDeleted.annotation ); |
|
} |
|
break; |
|
} |
|
} |
|
|
|
void MouseAnnotation::routeTooltipEvent( const QHelpEvent * helpEvent ) |
|
{ |
|
/* qDebug() << "MouseAnnotation::routeTooltipEvent, event " << helpEvent; */ |
|
const QPoint eventPos = m_pageView->contentAreaPoint( helpEvent->pos() ); |
|
if ( m_mouseOverAnnotation.isValid() && |
|
m_mouseOverAnnotation.annotation->subType() != Okular::Annotation::AWidget ) |
|
{ |
|
const ResizeHandle handle = getHandleAt( eventPos - m_mouseOverAnnotation.pageViewItem->uncroppedGeometry().topLeft(), m_mouseOverAnnotation ); |
|
if ( handle != RH_None ) |
|
{ |
|
/* get boundingRect in uncropped page coordinates */ |
|
QRect boundingRect = Okular::AnnotationUtils::annotationGeometry( |
|
m_mouseOverAnnotation.annotation, m_mouseOverAnnotation.pageViewItem->uncroppedWidth(), |
|
m_mouseOverAnnotation.pageViewItem->uncroppedHeight() ); |
|
|
|
/* uncropped page to content area */ |
|
boundingRect.translate( m_mouseOverAnnotation.pageViewItem->uncroppedGeometry().topLeft() ); |
|
/* content area to viewport */ |
|
boundingRect.translate( -m_pageView->contentAreaPosition() ); |
|
|
|
QString tip = GuiUtils::prettyToolTip( m_mouseOverAnnotation.annotation ); |
|
QToolTip::showText( helpEvent->globalPos(), tip, m_pageView->viewport(), boundingRect ); |
|
} |
|
} |
|
} |
|
|
|
void MouseAnnotation::routePaint( QPainter * painter, const QRect & paintRect ) |
|
{ |
|
/* QPainter draws relative to the origin of uncropped viewport. */ |
|
static const QColor borderColor = QColor::fromHsvF( 0, 0, 1.0 ); |
|
static const QColor fillColor = QColor::fromHsvF( 0, 0, 0.75, 0.66 ); |
|
|
|
if ( !isFocused() ) |
|
return; |
|
/* |
|
* Get annotation bounding rectangle in uncropped page coordinates. |
|
* Distinction between AnnotationUtils::annotationGeometry() and AnnotationObjectRect::boundingRect() is, |
|
* that boundingRect would enlarge the QRect to a minimum size of 14 x 14. |
|
* This is useful for getting focus an a very small annotation, |
|
* but for drawing and modification we want the real size. |
|
*/ |
|
const QRect boundingRect = Okular::AnnotationUtils::annotationGeometry( |
|
m_focusedAnnotation.annotation, m_focusedAnnotation.pageViewItem->uncroppedWidth(), |
|
m_focusedAnnotation.pageViewItem->uncroppedHeight() ); |
|
|
|
if ( !paintRect.intersects( boundingRect |
|
.translated( m_focusedAnnotation.pageViewItem->uncroppedGeometry().topLeft() ) |
|
.adjusted( -handleSizeHalf, -handleSizeHalf, handleSizeHalf, handleSizeHalf ) ) ) |
|
{ |
|
/* Our selection rectangle is not in a region that needs to be (re-)drawn. */ |
|
return; |
|
} |
|
|
|
painter->save(); |
|
painter->translate( m_focusedAnnotation.pageViewItem->uncroppedGeometry().topLeft() ); |
|
painter->setPen( QPen( fillColor, 2, Qt::SolidLine, Qt::SquareCap, Qt::BevelJoin ) ); |
|
painter->drawRect( boundingRect ); |
|
if ( m_focusedAnnotation.annotation->canBeResized() ) |
|
{ |
|
painter->setPen(borderColor); |
|
painter->setBrush(fillColor); |
|
Q_FOREACH( const ResizeHandle & handle, m_resizeHandleList ) |
|
{ |
|
QRect rect = getHandleRect( handle, m_focusedAnnotation ); |
|
painter->drawRect( rect ); |
|
} |
|
} |
|
painter->restore(); |
|
} |
|
|
|
Okular::Annotation * MouseAnnotation::annotation() const |
|
{ |
|
if ( m_focusedAnnotation.isValid() ) |
|
{ |
|
return m_focusedAnnotation.annotation; |
|
} |
|
return nullptr; |
|
} |
|
|
|
bool MouseAnnotation::isActive() const |
|
{ |
|
return ( m_state != StateInactive ); |
|
} |
|
|
|
bool MouseAnnotation::isMouseOver() const |
|
{ |
|
return ( m_mouseOverAnnotation.isValid() || m_handle != RH_None ); |
|
} |
|
|
|
bool MouseAnnotation::isFocused() const |
|
{ |
|
return ( m_state == StateFocused ); |
|
} |
|
|
|
bool MouseAnnotation::isMoved() const |
|
{ |
|
return ( m_state == StateMoving ); |
|
} |
|
|
|
bool MouseAnnotation::isResized() const |
|
{ |
|
return ( m_state == StateResizing ); |
|
} |
|
|
|
bool MouseAnnotation::isModified() const |
|
{ |
|
return ( m_state == StateMoving || m_state == StateResizing ); |
|
} |
|
|
|
Qt::CursorShape MouseAnnotation::cursor() const |
|
{ |
|
if ( m_handle != RH_None ) |
|
{ |
|
if ( isMoved() ) |
|
{ |
|
return Qt::SizeAllCursor; |
|
} |
|
else if ( isFocused() || isResized() ) |
|
{ |
|
switch ( m_handle ) |
|
{ |
|
case RH_Top: |
|
return Qt::SizeVerCursor; |
|
case RH_TopRight: |
|
return Qt::SizeBDiagCursor; |
|
case RH_Right: |
|
return Qt::SizeHorCursor; |
|
case RH_BottomRight: |
|
return Qt::SizeFDiagCursor; |
|
case RH_Bottom: |
|
return Qt::SizeVerCursor; |
|
case RH_BottomLeft: |
|
return Qt::SizeBDiagCursor; |
|
case RH_Left: |
|
return Qt::SizeHorCursor; |
|
case RH_TopLeft: |
|
return Qt::SizeFDiagCursor; |
|
case RH_Content: |
|
return Qt::SizeAllCursor; |
|
default: |
|
return Qt::OpenHandCursor; |
|
} |
|
} |
|
} |
|
else if ( m_mouseOverAnnotation.isValid() ) |
|
{ |
|
/* Mouse is over annotation, but the annotation is not yet selected. */ |
|
if ( m_mouseOverAnnotation.annotation->subType() == Okular::Annotation::AMovie ) |
|
{ |
|
return Qt::PointingHandCursor; |
|
} |
|
else if ( m_mouseOverAnnotation.annotation->subType() == Okular::Annotation::ARichMedia ) |
|
{ |
|
return Qt::PointingHandCursor; |
|
} |
|
else if ( m_mouseOverAnnotation.annotation->subType() == Okular::Annotation::AScreen ) |
|
{ |
|
if ( GuiUtils::renditionMovieFromScreenAnnotation( static_cast< const Okular::ScreenAnnotation * >( m_mouseOverAnnotation.annotation ) ) != 0 ) |
|
{ |
|
return Qt::PointingHandCursor; |
|
} |
|
} |
|
else if ( m_mouseOverAnnotation.annotation->subType() == Okular::Annotation::AFileAttachment ) |
|
{ |
|
return Qt::PointingHandCursor; |
|
} |
|
else |
|
{ |
|
return Qt::ArrowCursor; |
|
} |
|
} |
|
|
|
/* There's no none cursor, so we still have to return something. */ |
|
return Qt::ArrowCursor; |
|
} |
|
|
|
void MouseAnnotation::cancel() |
|
{ |
|
if ( isActive() ) |
|
{ |
|
finishCommand(); |
|
setState( StateInactive, m_focusedAnnotation ); |
|
} |
|
} |
|
|
|
void MouseAnnotation::reset() |
|
{ |
|
cancel(); |
|
m_focusedAnnotation.invalidate(); |
|
m_mouseOverAnnotation.invalidate(); |
|
} |
|
|
|
/* Handle state changes for the focused annotation. */ |
|
void MouseAnnotation::setState( MouseAnnotationState state, const AnnotationDescription & ad ) |
|
{ |
|
/* qDebug() << "setState: requested " << state; */ |
|
if ( m_focusedAnnotation.isValid() ) |
|
{ |
|
/* If there was a annotation before, request also repaint for the previous area. */ |
|
updateViewport( m_focusedAnnotation ); |
|
} |
|
|
|
if ( !ad.isValid() ) |
|
{ |
|
/* qDebug() << "No annotation provided, forcing state inactive." << state; */ |
|
state = StateInactive; |
|
} |
|
else if ( ( state == StateMoving && !ad.annotation->canBeMoved() ) || |
|
( state == StateResizing && !ad.annotation->canBeResized() ) ) |
|
{ |
|
/* qDebug() << "Annotation does not support requested state, forcing state selected." << state; */ |
|
state = StateInactive; |
|
} |
|
|
|
switch( state ) |
|
{ |
|
case StateMoving: |
|
m_focusedAnnotation = ad; |
|
m_focusedAnnotation.annotation->setFlags( m_focusedAnnotation.annotation->flags() | Okular::Annotation::BeingMoved ); |
|
updateViewport( m_focusedAnnotation ); |
|
break; |
|
case StateResizing: |
|
m_focusedAnnotation = ad; |
|
m_focusedAnnotation.annotation->setFlags( m_focusedAnnotation.annotation->flags() | Okular::Annotation::BeingResized ); |
|
updateViewport( m_focusedAnnotation ); |
|
break; |
|
case StateFocused: |
|
m_focusedAnnotation = ad; |
|
m_focusedAnnotation.annotation->setFlags( m_focusedAnnotation.annotation->flags() & ~(Okular::Annotation::BeingMoved | Okular::Annotation::BeingResized) ); |
|
updateViewport( m_focusedAnnotation ); |
|
break; |
|
case StateInactive: |
|
default: |
|
if ( m_focusedAnnotation.isValid() ) |
|
{ |
|
m_focusedAnnotation.annotation->setFlags( m_focusedAnnotation.annotation->flags() & ~(Okular::Annotation::BeingMoved | Okular::Annotation::BeingResized) ); |
|
} |
|
m_focusedAnnotation.invalidate(); |
|
m_handle = RH_None; |
|
} |
|
|
|
/* qDebug() << "setState: enter " << state; */ |
|
m_state = state; |
|
m_pageView->updateCursor(); |
|
} |
|
|
|
/* Get the rectangular boundary of the given annotation, enlarged for space needed by resize handles. |
|
* Returns a QRect in page view item coordinates. */ |
|
QRect MouseAnnotation::getFullBoundingRect( const AnnotationDescription & ad ) const |
|
{ |
|
QRect boundingRect; |
|
if ( ad.isValid() ) |
|
{ |
|
boundingRect = Okular::AnnotationUtils::annotationGeometry( |
|
ad.annotation, ad.pageViewItem->uncroppedWidth(), |
|
ad.pageViewItem->uncroppedHeight() ); |
|
boundingRect = boundingRect.adjusted( -handleSizeHalf, -handleSizeHalf, handleSizeHalf, handleSizeHalf ); |
|
} |
|
return boundingRect; |
|
} |
|
|
|
/* Apply the command determined by m_state to the currently focused annotation. */ |
|
void MouseAnnotation::performCommand( const QPoint & newPos ) |
|
{ |
|
const QRect & pageViewItemRect = m_focusedAnnotation.pageViewItem->uncroppedGeometry(); |
|
QPointF mouseDelta( newPos - pageViewItemRect.topLeft() - m_mousePosition ); |
|
QPointF normalizedRotatedMouseDelta( rotateInRect( |
|
QPointF( mouseDelta.x() / pageViewItemRect.width(), |
|
mouseDelta.y() / pageViewItemRect.height() ), |
|
m_focusedAnnotation.pageViewItem->page()->rotation() ) ); |
|
|
|
if ( isMoved() ) |
|
{ |
|
m_document->translatePageAnnotation( m_focusedAnnotation.pageNumber, m_focusedAnnotation.annotation, |
|
Okular::NormalizedPoint( normalizedRotatedMouseDelta.x(), normalizedRotatedMouseDelta.y() ) ); |
|
} |
|
else if ( isResized() ) |
|
{ |
|
QPointF delta1, delta2; |
|
handleToAdjust( normalizedRotatedMouseDelta, delta1, delta2, m_handle, m_focusedAnnotation.pageViewItem->page()->rotation() ); |
|
m_document->adjustPageAnnotation( m_focusedAnnotation.pageNumber, m_focusedAnnotation.annotation, |
|
Okular::NormalizedPoint( delta1.x(), delta1.y() ), Okular::NormalizedPoint( delta2.x(), delta2.y() ) ); |
|
} |
|
} |
|
|
|
/* Finalize a command in progress for the currently focused annotation. */ |
|
void MouseAnnotation::finishCommand() |
|
{ |
|
/* |
|
* Note: |
|
* Translate-/resizePageAnnotation causes PopplerAnnotationProxy::notifyModification, |
|
* where modify flag needs to be already cleared. So it is important to call |
|
* setFlags before translatePageAnnotation-/adjustPageAnnotation. |
|
*/ |
|
if ( isMoved() ) |
|
{ |
|
m_focusedAnnotation.annotation->setFlags( m_focusedAnnotation.annotation->flags() & ~Okular::Annotation::BeingMoved ); |
|
m_document->translatePageAnnotation( m_focusedAnnotation.pageNumber, m_focusedAnnotation.annotation, |
|
Okular::NormalizedPoint( 0.0, 0.0 ) ); |
|
} |
|
else if ( isResized() ) |
|
{ |
|
m_focusedAnnotation.annotation->setFlags( m_focusedAnnotation.annotation->flags() & ~Okular::Annotation::BeingResized ); |
|
m_document->adjustPageAnnotation( m_focusedAnnotation.pageNumber, m_focusedAnnotation.annotation, |
|
Okular::NormalizedPoint( 0.0, 0.0 ), Okular::NormalizedPoint( 0.0, 0.0 ) ); |
|
} |
|
} |
|
|
|
/* Tell viewport widget that the rectangular of the given annotation needs to be repainted. */ |
|
void MouseAnnotation::updateViewport( const AnnotationDescription & ad ) const |
|
{ |
|
const QRect & changedPageViewItemRect = getFullBoundingRect( ad ); |
|
m_pageView->viewport()->update( changedPageViewItemRect |
|
.translated( ad.pageViewItem->uncroppedGeometry().topLeft() ) |
|
.translated( -m_pageView->contentAreaPosition() ) ); |
|
} |
|
|
|
/* eventPos: Mouse position in uncropped page coordinates. |
|
ad: The annotation to get the handle for. */ |
|
MouseAnnotation::ResizeHandle MouseAnnotation::getHandleAt( const QPoint & eventPos, const AnnotationDescription & ad ) const |
|
{ |
|
ResizeHandle selected = RH_None; |
|
|
|
if ( ad.annotation->canBeResized() ) |
|
{ |
|
Q_FOREACH( const ResizeHandle & handle, m_resizeHandleList ) |
|
{ |
|
const QRect rect = getHandleRect( handle, ad ); |
|
if ( rect.contains( eventPos ) ) |
|
{ |
|
selected |= handle; |
|
} |
|
} |
|
|
|
/* |
|
* Handles may overlap when selection is very small. |
|
* Then it can happen that cursor is over more than one handles, |
|
* and therefore maybe more than two flags are set. |
|
* Favor one handle in that case. |
|
*/ |
|
if ( ( selected & RH_BottomRight ) == RH_BottomRight ) |
|
return RH_BottomRight; |
|
if ( ( selected & RH_TopRight ) == RH_TopRight ) |
|
return RH_TopRight; |
|
if ( ( selected & RH_TopLeft ) == RH_TopLeft ) |
|
return RH_TopLeft; |
|
if ( ( selected & RH_BottomLeft ) == RH_BottomLeft ) |
|
return RH_BottomLeft; |
|
} |
|
|
|
if ( selected == RH_None && ad.annotation->canBeMoved() ) |
|
{ |
|
const QRect boundingRect = Okular::AnnotationUtils::annotationGeometry( |
|
ad.annotation, ad.pageViewItem->uncroppedWidth(), ad.pageViewItem->uncroppedHeight() ); |
|
if ( boundingRect.contains( eventPos ) ) |
|
{ |
|
return RH_Content; |
|
} |
|
} |
|
|
|
return selected; |
|
} |
|
|
|
/* Get the rectangle for a specified resizie handle. */ |
|
QRect MouseAnnotation::getHandleRect( ResizeHandle handle, const AnnotationDescription & ad ) const |
|
{ |
|
const QRect boundingRect = Okular::AnnotationUtils::annotationGeometry( |
|
ad.annotation, ad.pageViewItem->uncroppedWidth(), ad.pageViewItem->uncroppedHeight() ); |
|
int left, top; |
|
|
|
if ( handle & RH_Top ) |
|
{ |
|
top = boundingRect.top() - handleSizeHalf; |
|
} |
|
else if ( handle & RH_Bottom ) |
|
{ |
|
top = boundingRect.bottom() - handleSizeHalf; |
|
} |
|
else |
|
{ |
|
top = boundingRect.top() + boundingRect.height() / 2 - handleSizeHalf; |
|
} |
|
|
|
if ( handle & RH_Left ) |
|
{ |
|
left = boundingRect.left() - handleSizeHalf; |
|
} |
|
else if ( handle & RH_Right ) |
|
{ |
|
left = boundingRect.right() - handleSizeHalf; |
|
} |
|
else |
|
{ |
|
left = boundingRect.left() + boundingRect.width() / 2 - handleSizeHalf; |
|
} |
|
|
|
return QRect( left, top, handleSize, handleSize ); |
|
} |
|
|
|
/* Convert a resize handle delta into two adjust delta coordinates. */ |
|
void MouseAnnotation::handleToAdjust( const QPointF & dIn, QPointF & dOut1, QPointF & dOut2, MouseAnnotation::ResizeHandle handle, Okular::Rotation rotation ) |
|
{ |
|
const MouseAnnotation::ResizeHandle rotatedHandle = MouseAnnotation::rotateHandle( handle, rotation); |
|
dOut1.rx() = ( rotatedHandle & MouseAnnotation::RH_Left ) ? dIn.x() : 0; |
|
dOut1.ry() = ( rotatedHandle & MouseAnnotation::RH_Top ) ? dIn.y() : 0; |
|
dOut2.rx() = ( rotatedHandle & MouseAnnotation::RH_Right ) ? dIn.x() : 0; |
|
dOut2.ry() = ( rotatedHandle & MouseAnnotation::RH_Bottom ) ? dIn.y() : 0; |
|
} |
|
|
|
QPointF MouseAnnotation::rotateInRect( const QPointF & rotated, Okular::Rotation rotation ) |
|
{ |
|
QPointF ret; |
|
|
|
switch ( rotation ) |
|
{ |
|
case Okular::Rotation90: |
|
ret = QPointF( rotated.y(), -rotated.x() ); |
|
break; |
|
case Okular::Rotation180: |
|
ret = QPointF( -rotated.x(), -rotated.y() ); |
|
break; |
|
case Okular::Rotation270: |
|
ret = QPointF( -rotated.y(), rotated.x() ); |
|
break; |
|
case Okular::Rotation0: /* no modifications */ |
|
default: /* other cases */ |
|
ret = rotated; |
|
} |
|
|
|
return ret; |
|
} |
|
|
|
MouseAnnotation::ResizeHandle MouseAnnotation::rotateHandle( MouseAnnotation::ResizeHandle handle, Okular::Rotation rotation ) |
|
{ |
|
unsigned int rotatedHandle = 0; |
|
switch( rotation ) |
|
{ |
|
case Okular::Rotation90: |
|
/* bit rotation: #1 => #4, #2 => #1, #3 => #2, #4 => #3 */ |
|
rotatedHandle = (handle << 3 | handle >> (4-3)) & RH_AllHandles; |
|
break; |
|
case Okular::Rotation180: |
|
/* bit rotation: #1 => #3, #2 => #4, #3 => #1, #4 => #2 */ |
|
rotatedHandle = (handle << 2 | handle >> (4-2)) & RH_AllHandles; |
|
break; |
|
case Okular::Rotation270: |
|
/* bit rotation: #1 => #2, #2 => #3, #3 => #4, #4 => #1 */ |
|
rotatedHandle = (handle << 1 | handle >> (4-1)) & RH_AllHandles; |
|
break; |
|
case Okular::Rotation0: /* no modifications */ |
|
default: /* other cases */ |
|
rotatedHandle = handle; |
|
break; |
|
} |
|
return (MouseAnnotation::ResizeHandle) rotatedHandle; |
|
} |
|
|
|
/* Start according action for AMovie/ARichMedia/AScreen/AFileAttachment. |
|
* It was formerly (before mouse annotation refactoring) called on mouse release event. |
|
* Now it's called on mouse press. Should we keep the former behavior? */ |
|
void MouseAnnotation::processAction( const AnnotationDescription& ad ) |
|
{ |
|
if ( ad.isValid() ) |
|
{ |
|
Okular::Annotation *ann = ad.annotation; |
|
PageViewItem * pageItem = ad.pageViewItem; |
|
|
|
if ( ann->subType() == Okular::Annotation::AMovie ) |
|
{ |
|
VideoWidget *vw = pageItem->videoWidgets().value( static_cast<Okular::MovieAnnotation*>( ann )->movie() ); |
|
vw->show(); |
|
vw->play(); |
|
} |
|
else if ( ann->subType() == Okular::Annotation::ARichMedia ) |
|
{ |
|
VideoWidget *vw = pageItem->videoWidgets().value( static_cast<Okular::RichMediaAnnotation*>( ann )->movie() ); |
|
vw->show(); |
|
vw->play(); |
|
} |
|
else if ( ann->subType() == Okular::Annotation::AScreen ) |
|
{ |
|
m_document->processAction( static_cast<Okular::ScreenAnnotation*>( ann )->action() ); |
|
} |
|
else if ( ann->subType() == Okular::Annotation::AFileAttachment ) |
|
{ |
|
const Okular::FileAttachmentAnnotation * fileAttachAnnot = static_cast< Okular::FileAttachmentAnnotation * >( ann ); |
|
GuiUtils::saveEmbeddedFile( fileAttachAnnot->embeddedFile(), m_pageView ); |
|
} |
|
} |
|
}
|
|
|