From f22d9d28973713011114266e3c5d3235484a0d14 Mon Sep 17 00:00:00 2001 From: Michal Humpula Date: Mon, 24 Feb 2014 23:42:10 +0100 Subject: [PATCH] Add Magnifier REVIEW: 113973 GUI --- CMakeLists.txt | 1 + conf/okular.kcfg | 1 + part.rc | 3 +- ui/magnifierview.cpp | 203 +++++++++++++++++++++++++++++++++++++++++++ ui/magnifierview.h | 61 +++++++++++++ ui/pageview.cpp | 104 ++++++++++++++++++++++ ui/pageview.h | 6 ++ 7 files changed, 378 insertions(+), 1 deletion(-) create mode 100644 ui/magnifierview.cpp create mode 100644 ui/magnifierview.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 63d2922fb..e288e01e8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -182,6 +182,7 @@ set(okularpart_SRCS ui/pagesizelabel.cpp ui/pageviewannotator.cpp ui/pageview.cpp + ui/magnifierview.cpp ui/pageviewutils.cpp ui/presentationsearchbar.cpp ui/presentationwidget.cpp diff --git a/conf/okular.kcfg b/conf/okular.kcfg index f954c9acf..f98098968 100644 --- a/conf/okular.kcfg +++ b/conf/okular.kcfg @@ -228,6 +228,7 @@ + diff --git a/part.rc b/part.rc index 0b9cee540..60f86e5ba 100644 --- a/part.rc +++ b/part.rc @@ -1,5 +1,5 @@ - + &File @@ -73,6 +73,7 @@ + diff --git a/ui/magnifierview.cpp b/ui/magnifierview.cpp new file mode 100644 index 000000000..331fd656a --- /dev/null +++ b/ui/magnifierview.cpp @@ -0,0 +1,203 @@ +/* + * 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) version 3 or any later version + * accepted by the membership of KDE e.V. (or its successor approved + * by the membership of KDE e.V.), which shall act as a proxy + * defined in Section 14 of version 3 of the license. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include "magnifierview.h" + +#include +#include +#include +#include + +#include +#include + +#include "core/document.h" +#include "ui/pagepainter.h" +#include "core/generator.h" +#include "priorities.h" + +static const int SCALE = 10; + +MagnifierView::MagnifierView(Okular::Document* document, QWidget* parent) + : QWidget(parent) + , m_document(document) + , m_page(0) +{ +} + +void MagnifierView::notifySetup(const QVector< Okular::Page* >& pages, int setupFlags) +{ + if (!(setupFlags & Okular::DocumentObserver::DocumentChanged)) { + return; + } + + m_pages = pages; + m_page = 0; + m_current = -1; +} + +void MagnifierView::notifyPageChanged(int page, int flags) +{ + Q_UNUSED(page); + Q_UNUSED(flags); + + if (isVisible()) { + update(); + } +} + +bool MagnifierView::canUnloadPixmap(int page) const +{ + return (page != m_current); +} + +void MagnifierView::notifyCurrentPageChanged(int previous, int current) +{ + Q_UNUSED(previous); + + if (current != m_current) { + m_current = current; + m_page = m_pages[current]; + + if (isVisible()) { + requestPixmap(); + update(); + } + } +} + +void MagnifierView::updateView( const Okular::NormalizedPoint& p, const Okular::Page *page ) +{ + m_viewpoint = p; + + if (page != m_page) // ok, we are screwed + { + m_page = page; + m_current = page->number(); + } + + if (isVisible()) { + requestPixmap(); + update(); + } +} + +void MagnifierView::paintEvent(QPaintEvent* e) +{ + Q_UNUSED(e); + + QPainter p(this); + + if (m_page) + { + QRect where = QRect(0, 0, width(), height()); + PagePainter::paintCroppedPageOnPainter(&p, m_page, this, 0, m_page->width() * SCALE, m_page->height() * SCALE, where, normalizedView(), NULL); + } + + drawTicks(&p); +} + +void MagnifierView::move( int x, int y) +{ + QWidget::move( x, y ); + requestPixmap(); +} + +void MagnifierView::requestPixmap() +{ + const int full_width = m_page->width() * SCALE; + const int full_height = m_page->height() * SCALE; + + Okular::NormalizedRect nrect = normalizedView(); + + if (m_page && !m_page->hasPixmap( this, full_width, full_height, nrect )) + { + QLinkedList< Okular::PixmapRequest * > requestedPixmaps; + + Okular::PixmapRequest *p = new Okular::PixmapRequest( this, m_current, full_width, full_height, PAGEVIEW_PRIO, Okular::PixmapRequest::Asynchronous ); + + if ( m_page->hasTilesManager( this ) ) { + p->setTile( true ); + } + + // request a little bit bigger rectangle then currently viewed, but not the full scale page + const double rect_width = (nrect.right - nrect.left) * 0.5, + rect_height = (nrect.bottom - nrect.top) * 0.5; + + const double top = qMax(nrect.top - rect_height, 0.0); + const double bottom = qMin(nrect.bottom + rect_height, 1.0); + const double left = qMax(nrect.left - rect_width, 0.0); + const double right = qMin(nrect.right + rect_width, 1.0); + + p->setNormalizedRect( Okular::NormalizedRect(left, top, right, bottom) ); + requestedPixmaps.push_back( p ); + + m_document->requestPixmaps( requestedPixmaps ); + } +} + +Okular::NormalizedRect MagnifierView::normalizedView() const +{ + double h = (double)height() / (SCALE * m_page->height() * 2); + double w = (double)width() / (SCALE * m_page->width() * 2); + return Okular::NormalizedRect(m_viewpoint.x - w, m_viewpoint.y - h, m_viewpoint.x + w, m_viewpoint.y + h); +} + +void MagnifierView::drawTicks( QPainter *p ) +{ + p->save(); + + p->setPen(QPen(QBrush(Qt::SolidPattern), 1, Qt::SolidLine, Qt::FlatCap)); + // the cross + p->drawLine(width() / 2, 0, width() / 2, height()); + p->drawLine(0, height() / 2, width(), height() / 2); + + // the borders + p->drawLine(1, 1, width() - 1, 1); + p->drawLine(width() - 1, 1, width() - 1, height() - 1); + p->drawLine(1, height() - 1, width() - 1, height() - 1); + p->drawLine(1, height() - 1, 1, 1); + + // ticks + // TODO posibility to switch units (pt, mm, cc, in, printing dots) + float ps = (float)SCALE * 5;// how much pixels in widget is one pixel in document * how often + int tw = 10; // tick size in pixels + + for ( float x = 0; x < width(); x += ps ) + { + p->drawLine(x, 1, x, tw); + p->drawLine(x, height(), x, height() - tw); + p->drawLine(1, x, tw, x); + p->drawLine(width(), x, width() - tw, x); + } + + ps *= 5; // thick ones + p->setPen(QPen(QBrush(Qt::SolidPattern), 2, Qt::SolidLine, Qt::FlatCap)); + for ( float x = 0; x < width(); x += ps ) + { + p->drawLine(x, 1, x, tw); + p->drawLine(x, height(), x, height() - tw); + p->drawLine(1, x, tw, x); + p->drawLine(width(), x, width() - tw, x); + } + + p->restore(); +} + +#include "magnifierview.moc" diff --git a/ui/magnifierview.h b/ui/magnifierview.h new file mode 100644 index 000000000..a647e71af --- /dev/null +++ b/ui/magnifierview.h @@ -0,0 +1,61 @@ +/* + * 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) version 3 or any later version + * accepted by the membership of KDE e.V. (or its successor approved + * by the membership of KDE e.V.), which shall act as a proxy + * defined in Section 14 of version 3 of the license. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#ifndef MAGNIFIERVIEW_H +#define MAGNIFIERVIEW_H + +#include +#include "core/view.h" +#include "core/observer.h" +#include + +class QLabel; + +class MagnifierView : public QWidget, public Okular::DocumentObserver +{ + Q_OBJECT + + public: + MagnifierView( Okular::Document *document, QWidget *parent = 0 ); + + void notifySetup( const QVector< Okular::Page * > & pages, int setupFlags ); + void notifyPageChanged( int page, int flags ); + void notifyCurrentPageChanged( int previous, int current ); + virtual bool canUnloadPixmap( int page ) const; + + void updateView( const Okular::NormalizedPoint &p, const Okular::Page * page ); + void move( int x, int y ); + + protected: + void paintEvent( QPaintEvent *e ); + + private: + Okular::NormalizedRect normalizedView() const; + void requestPixmap(); + void drawTicks( QPainter *p ); + + private: + Okular::Document *m_document; + Okular::NormalizedPoint m_viewpoint; + const Okular::Page *m_page; + int m_current; + QVector m_pages; +}; + +#endif // MAGNIFIERVIEW_H diff --git a/ui/pageview.cpp b/ui/pageview.cpp index 7c7f785a3..b3a7a93cd 100644 --- a/ui/pageview.cpp +++ b/ui/pageview.cpp @@ -83,6 +83,7 @@ #include "settings.h" #include "settings_core.h" #include "url_utils.h" +#include "magnifierview.h" static int pageflags = PagePainter::Accessibility | PagePainter::EnhanceLinks | PagePainter::EnhanceImages | PagePainter::Highlights | @@ -121,6 +122,7 @@ public: Okular::Document * document; QVector< PageViewItem * > items; QLinkedList< PageViewItem * > visibleItems; + MagnifierView *magnifierView; // view layout (columns and continuous in Settings), zoom and mouse PageView::ZoomMode zoomMode; @@ -193,6 +195,7 @@ public: KAction * aMouseSelect; KAction * aMouseTextSelect; KAction * aMouseTableSelect; + KAction * aMouseMagnifier; KToggleAction * aToggleAnnotator; KSelectAction * aZoom; KAction * aZoomIn; @@ -386,6 +389,11 @@ PageView::PageView( QWidget *parent, Okular::Document *document ) // connect(...); setAttribute( Qt::WA_InputMethodEnabled, true ); + d->magnifierView = new MagnifierView(document, this); + d->magnifierView->hide(); + d->magnifierView->setGeometry(0, 0, 350, 200); // TODO: more dynamic? + document->addObserver(d->magnifierView); + connect(document, SIGNAL(processMovieAction(const Okular::MovieAction*)), this, SLOT(slotProcessMovieAction(const Okular::MovieAction*))); connect(document, SIGNAL(processRenditionAction(const Okular::RenditionAction*)), this, SLOT(slotProcessRenditionAction(const Okular::RenditionAction*))); @@ -575,6 +583,15 @@ void PageView::setupActions( KActionCollection * ac ) d->aMouseTableSelect->setActionGroup( d->mouseModeActionGroup ); d->aMouseTableSelect->setChecked( Okular::Settings::mouseMode() == Okular::Settings::EnumMouseMode::TableSelect ); + d->aMouseMagnifier = new KAction(KIcon( "magnifier" ), i18n("&Magnifier"), this); + ac->addAction("mouse_magnifier", d->aMouseMagnifier ); + connect( d->aMouseMagnifier, SIGNAL(triggered()), this, SLOT(slotSetMouseMagnifier()) ); + d->aMouseMagnifier->setIconText( i18nc( "Magnifier Tool", "Magnifier" ) ); + d->aMouseMagnifier->setCheckable( true ); + d->aMouseMagnifier->setShortcut( Qt::CTRL + Qt::Key_6 ); + d->aMouseMagnifier->setActionGroup( d->mouseModeActionGroup ); + d->aMouseMagnifier->setChecked( Okular::Settings::mouseMode() == Okular::Settings::EnumMouseMode::Magnifier ); + d->aToggleAnnotator = new KToggleAction(KIcon( "draw-freehand" ), i18n("&Review"), this); ac->addAction("mouse_toggle_annotate", d->aToggleAnnotator ); d->aToggleAnnotator->setCheckable( true ); @@ -1054,6 +1071,8 @@ void PageView::updateActionState( bool haspages, bool documentChanged, bool hasf d->aSpeakDoc->setEnabled( enablettsactions ); d->aSpeakPage->setEnabled( enablettsactions ); } + + d->aMouseMagnifier->setEnabled(d->document->supportsTiles()); } bool PageView::areSourceLocationsShownGraphically() const @@ -1989,6 +2008,15 @@ void PageView::mouseMoveEvent( QMouseEvent * e ) if ( d->mouseSelecting ) updateSelection( eventPos ); break; + + case Okular::Settings::EnumMouseMode::Magnifier: + if ( e->buttons() ) // if any button is pressed at all + { + moveMagnifier( e->pos() ); + updateMagnifier( eventPos ); + } + break; + case Okular::Settings::EnumMouseMode::TextSelect: // if mouse moves 5 px away from the press point and the document soupports text extraction, do 'textselection' if ( !d->mouseTextSelecting && !d->mousePressPos.isNull() && d->document->supportsSearching() && ( ( eventPos - d->mouseSelectPos ).manhattanLength() > 5 ) ) @@ -2131,6 +2159,12 @@ void PageView::mousePressEvent( QMouseEvent * e ) updateZoom( ZoomOut ); break; + case Okular::Settings::EnumMouseMode::Magnifier: + moveMagnifier( e->pos() ); + d->magnifierView->show(); + updateMagnifier( eventPos ); + break; + case Okular::Settings::EnumMouseMode::RectSelect: // set first corner of the selection rect if ( leftButton ) { @@ -2474,6 +2508,10 @@ void PageView::mouseReleaseEvent( QMouseEvent * e ) } break; + case Okular::Settings::EnumMouseMode::Magnifier: + d->magnifierView->hide(); + break; + case Okular::Settings::EnumMouseMode::RectSelect: { // if mouse is released and selection is null this is a rightClick @@ -3738,6 +3776,8 @@ void PageView::updateCursor( const QPoint &p ) // if over a ObjectRect (of type Link) change cursor to hand if ( Okular::Settings::mouseMode() == Okular::Settings::EnumMouseMode::TextSelect ) setCursor( Qt::IBeamCursor ); + else if ( Okular::Settings::mouseMode() == Okular::Settings::EnumMouseMode::Magnifier ) + setCursor( Qt::CrossCursor ); else if ( Okular::Settings::mouseMode() == Okular::Settings::EnumMouseMode::RectSelect ) setCursor( Qt::CrossCursor ); else if ( d->mouseAnn ) @@ -3800,6 +3840,60 @@ void PageView::updateCursor( const QPoint &p ) } } +void PageView::moveMagnifier( const QPoint& p ) // non scaled point +{ + const int w = d->magnifierView->width() * 0.5; + const int h = d->magnifierView->height() * 0.5; + + int x = p.x() - w; + int y = p.y() - h; + + const int max_x = viewport()->width(); + const int max_y = viewport()->height(); + + QPoint scroll(0,0); + + if (x < 0) + { + if (horizontalScrollBar()->value() > 0) scroll.setX(x - w); + x = 0; + } + + if (y < 0) + { + if (verticalScrollBar()->value() > 0) scroll.setY(y - h); + y = 0; + } + + if (p.x() + w > max_x) + { + if (horizontalScrollBar()->value() < horizontalScrollBar()->maximum()) scroll.setX(p.x() + 2 * w - max_x); + x = max_x - d->magnifierView->width() - 1; + } + + if (p.y() + h > max_y) + { + if (verticalScrollBar()->value() < verticalScrollBar()->maximum()) scroll.setY(p.y() + 2 * h - max_y); + y = max_y - d->magnifierView->height() - 1; + } + + if (!scroll.isNull()) + scrollPosIntoView(contentAreaPoint(p + scroll)); + + d->magnifierView->move(x, y); +} + +void PageView::updateMagnifier( const QPoint& p ) // scaled point +{ + /* translate mouse coordinates to page coordinates and inform the magnifier of the situation */ + PageViewItem *item = pickItemOnPoint(p.x(), p.y()); + if (item) + { + Okular::NormalizedPoint np(item->absToPageX(p.x()), item->absToPageY(p.y())); + d->magnifierView->updateView( np, item->page() ); + } +} + int PageView::viewColumns() const { int vm = Okular::Settings::viewMode(); @@ -4521,6 +4615,16 @@ void PageView::slotSetMouseZoom() Okular::Settings::self()->writeConfig(); } +void PageView::slotSetMouseMagnifier() +{ + Okular::Settings::setMouseMode( Okular::Settings::EnumMouseMode::Magnifier ); + d->messageWindow->display( i18n( "Click to see the magnified view." ), QString() ); + + // force an update of the cursor + updateCursor(); + Okular::Settings::self()->writeConfig(); +} + void PageView::slotSetMouseSelect() { Okular::Settings::setMouseMode( Okular::Settings::EnumMouseMode::RectSelect ); diff --git a/ui/pageview.h b/ui/pageview.h index 9c15af674..577b90863 100644 --- a/ui/pageview.h +++ b/ui/pageview.h @@ -44,6 +44,8 @@ class RenditionAction; class FormWidgetIface; class PageViewPrivate; +class MagnifierView; + /** * @short The main view. Handles zoom and continuous mode.. oh, and page * @short display of course :-) @@ -187,6 +189,9 @@ Q_OBJECT // updates cursor void updateCursor( const QPoint &p ); + void moveMagnifier( const QPoint &p ); + void updateMagnifier( const QPoint &p ); + int viewColumns() const; void center(int cx, int cy); @@ -238,6 +243,7 @@ Q_OBJECT void slotContinuousToggled( bool ); void slotSetMouseNormal(); void slotSetMouseZoom(); + void slotSetMouseMagnifier(); void slotSetMouseSelect(); void slotSetMouseTextSelect(); void slotSetMouseTableSelect();