diff --git a/core/generator_pdf/generator_pdf.cpp b/core/generator_pdf/generator_pdf.cpp index 7659bf1b573621727691a2e88dcc7901babc6774..9479a0eb4b91f2371c43f94e0896d3c1a8af6fba 100644 --- a/core/generator_pdf/generator_pdf.cpp +++ b/core/generator_pdf/generator_pdf.cpp @@ -470,7 +470,7 @@ void PDFGenerator::generatePixmap( PixmapRequest * request ) bool genTextPage = !page->hasSearchPage() && (request->width == page->width()) && (request->height == page->height()); // generate links and image rects if rendering pages on pageview - bool genObjectRects = request->id == PAGEVIEW_ID; + bool genObjectRects = request->id & (PAGEVIEW_ID | PRESENTATION_ID); // 0. LOCK [waits for the thread end] docLock.lock(); @@ -2225,7 +2225,7 @@ void PDFPixmapGeneratorThread::run() ( height == page->height() ); // generate links and image rects if rendering pages on pageview - bool genObjectRects = d->currentRequest->id == PAGEVIEW_ID; + bool genObjectRects = d->currentRequest->id & (PAGEVIEW_ID | PRESENTATION_ID); // 0. LOCK s[tart locking XPDF thread unsafe classes] d->generator->docLock.lock(); diff --git a/ui/presentationwidget.cpp b/ui/presentationwidget.cpp index 88153880175fc1e62bc7c30b942cf2419f2de697..6c4c06d48d84264167578c591d3a00473c48c438 100644 --- a/ui/presentationwidget.cpp +++ b/ui/presentationwidget.cpp @@ -11,13 +11,14 @@ #include #include #include +#include #include #include #include #include #include -#include #include +#include #include #include #include @@ -32,6 +33,7 @@ #include "pagepainter.h" #include "core/generator.h" #include "core/page.h" +#include "core/link.h" #include "conf/settings.h" @@ -49,7 +51,8 @@ struct PresentationFrame PresentationWidget::PresentationWidget( QWidget * parent, KPDFDocument * doc ) - : QDialog( parent, "presentationWidget", true, WDestructiveClose | WStyle_NoBorder), m_document( doc ), m_frameIndex( -1 ) + : QDialog( parent, "presentationWidget", true, WDestructiveClose | WStyle_NoBorder), + m_pressedLink( 0 ), m_handCursor( false ), m_document( doc ), m_frameIndex( -1 ) { // set look and geometry setBackgroundMode( Qt::NoBackground ); @@ -222,32 +225,62 @@ void PresentationWidget::mousePressEvent( QMouseEvent * e ) // pressing left button if ( e->button() == Qt::LeftButton ) { + // if pressing on a link, skip other checks + if ( ( m_pressedLink = getLink( e->x(), e->y() ) ) ) + return; + + // handle clicking on top-right overlay if ( m_overlayGeometry.contains( e->pos() ) ) + { overlayClick( e->pos() ); - else - slotNextPage(); + return; + } + + // if no other actions, go to next page + slotNextPage(); } // pressing right button else if ( e->button() == Qt::RightButton ) slotPrevPage(); } +void PresentationWidget::mouseReleaseEvent( QMouseEvent * e ) +{ + // if releasing on the same link we pressed over, execute it + if ( m_pressedLink && e->button() == Qt::LeftButton ) + { + const KPDFLink * link = getLink( e->x(), e->y() ); + if ( link == m_pressedLink ) + m_document->processLink( link ); + m_pressedLink = 0; + } +} + void PresentationWidget::mouseMoveEvent( QMouseEvent * e ) { - if (m_width == -1) return; - - // hide a shown bar when exiting the area + // safety check + if ( m_width == -1 ) + return; + + // update cursor and tooltip if hovering a link + if ( Settings::slidesCursor() != Settings::EnumSlidesCursor::Hidden ) + testCursorOnLink( e->x(), e->y() ); + if ( m_topBar->isShown() ) { + // hide a shown bar when exiting the area if ( e->y() > ( m_topBar->height() + 1 ) ) m_topBar->hide(); } - // show a hidden bar if mouse reaches the top of the screen - else if ( !e->y() ) - m_topBar->show(); - // change page if dragging the mouse over the 'wheel' - else if ( e->state() == Qt::LeftButton && m_overlayGeometry.contains( e->pos() ) ) + else + { + // show the bar if reaching top 2 pixels + if ( e->y() <= (geometry().top() + 1) ) + m_topBar->show(); + // handle "dragging the wheel" if clicking on its geometry + else if ( e->state() == Qt::LeftButton && m_overlayGeometry.contains( e->pos() ) ) overlayClick( e->pos() ); + } } void PresentationWidget::paintEvent( QPaintEvent * pe ) @@ -326,6 +359,63 @@ void PresentationWidget::paintEvent( QPaintEvent * pe ) // +const KPDFLink * PresentationWidget::getLink( int x, int y, QRect * geometry ) const +{ + // no links on invalid pages + if ( geometry && !geometry->isNull() ) + geometry->setRect( 0, 0, -1, -1 ); + if ( m_frameIndex < 0 || m_frameIndex >= (int)m_frames.size() ) + return 0; + + // get frame, page and geometry + const PresentationFrame * frame = m_frames[ m_frameIndex ]; + const KPDFPage * page = frame->page; + const QRect & frameGeometry = frame->geometry; + + // compute normalized x and y + double nx = (double)(x - frameGeometry.left()) / (double)frameGeometry.width(); + double ny = (double)(y - frameGeometry.top()) / (double)frameGeometry.height(); + + // no links outside the pages + if ( nx < 0 || nx > 1 || ny < 0 || ny > 1 ) + return 0; + + // check if 1) there is an object and 2) it's a link + const ObjectRect * object = page->getObjectRect( ObjectRect::Link, nx, ny ); + if ( !object ) + return 0; + + // compute link geometry if destination rect present + if ( geometry ) + { + *geometry = object->geometry( frameGeometry.width(), frameGeometry.height() ); + geometry->moveBy( frameGeometry.left(), frameGeometry.top() ); + } + + // return the link pointer + return (KPDFLink *)object->pointer(); +} + +void PresentationWidget::testCursorOnLink( int x, int y ) +{ + // get rect + QRect linkRect; + const KPDFLink * link = getLink( x, y, &linkRect ); + + // only react on changes (in/out from a link) + if ( (link && !m_handCursor) || (!link && m_handCursor) ) + { + // change cursor shape + m_handCursor = link != 0; + setCursor( m_handCursor ? KCursor::handCursor() : KCursor::arrowCursor() ); + + // set tooltip over link's rect + QString tip = link ? link->linkTip() : ""; + if ( m_handCursor && !tip.isEmpty() ) + QToolTip::add( this, linkRect, tip ); + } +} + void PresentationWidget::overlayClick( const QPoint & position ) { // clicking the progress indicator @@ -357,12 +447,20 @@ void PresentationWidget::changePage( int newPage ) // notifyPixmapChanged call or else we can proceed to pixmap generation if ( !frame->page->hasPixmap( PRESENTATION_ID, pixW, pixH ) ) { + // operation will take long: set busy cursor + QApplication::setOverrideCursor( KCursor::workingCursor() ); + // request the pixmap QValueList< PixmapRequest * > request; request.push_back( new PixmapRequest( PRESENTATION_ID, m_frameIndex, pixW, pixH, PRESENTATION_PRIO ) ); m_document->requestPixmaps( request ); + // restore cursor + QApplication::restoreOverrideCursor(); } else + { + // make the background pixmap generatePage(); + } } void PresentationWidget::generatePage() @@ -396,6 +494,13 @@ void PresentationWidget::generatePage() KPDFPageTransition trans = defaultTransition(); initTransition( &trans ); } + + // update cursor + tooltip + if ( Settings::slidesCursor() != Settings::EnumSlidesCursor::Hidden ) + { + QPoint p = mapFromGlobal( QCursor::pos() ); + testCursorOnLink( p.x(), p.y() ); + } } void PresentationWidget::generateIntroPage( QPainter & p ) @@ -478,12 +583,14 @@ void PresentationWidget::generateContentsPage( int pageNum, QPainter & p ) } } +// from Arthur - Qt4 - (is defined elsewhere as 'qt_div_255' to not break final compilation) +inline int qt_div255(int x) { return (x + (x>>8) + 0x80) >> 8; } void PresentationWidget::generateOverlay() { #ifdef ENABLE_PROGRESS_OVERLAY // calculate overlay geometry and resize pixmap if needed int side = m_width / 16; - m_overlayGeometry.setRect( m_width - side, 0, side, side ); + m_overlayGeometry.setRect( m_width - side - 4, 4, side, side ); if ( m_lastRenderedOverlay.width() != side ) m_lastRenderedOverlay.resize( side, side ); @@ -497,13 +604,14 @@ void PresentationWidget::generateOverlay() // draw PIE SLICES in blue levels (the levels will then be the alpha component) int pages = m_document->pages(); - if ( pages > 36 ) + if ( pages > 28 ) { // draw continuous slices int degrees = (int)( 360 * (float)(m_frameIndex + 1) / (float)pages ); - pixmapPainter.setPen( 0x20 ); - pixmapPainter.setBrush( 0x10 ); + pixmapPainter.setPen( 0x05 ); + pixmapPainter.setBrush( 0x40 ); pixmapPainter.drawPie( 2, 2, side - 4, side - 4, 90*16, (360-degrees)*16 ); - pixmapPainter.setBrush( 0xC0 ); + pixmapPainter.setPen( 0x40 ); + pixmapPainter.setBrush( 0xF0 ); pixmapPainter.drawPie( 2, 2, side - 4, side - 4, 90*16, -degrees*16 ); } else @@ -513,7 +621,7 @@ void PresentationWidget::generateOverlay() { float newCoord = -90 + 360 * (float)(i + 1) / (float)pages; pixmapPainter.setPen( i <= m_frameIndex ? 0x40 : 0x05 ); - pixmapPainter.setBrush( i <= m_frameIndex ? 0xC0 : 0x10 ); + pixmapPainter.setBrush( i <= m_frameIndex ? 0xF0 : 0x40 ); pixmapPainter.drawPie( 2, 2, side - 4, side - 4, (int)( -16*(oldCoord + 1) ), (int)( -16*(newCoord - (oldCoord + 2)) ) ); oldCoord = newCoord; @@ -534,17 +642,52 @@ void PresentationWidget::generateOverlay() // end drawing pixmap and halve image pixmapPainter.end(); - side /= 2; - QImage image( doublePixmap.convertToImage().smoothScale( side, side ) ); + QImage image( doublePixmap.convertToImage().smoothScale( side / 2, side / 2 ) ); image.setAlphaBuffer( true ); - // FIXME: obey palette (highlighe colors), but only after dropping an - // inverse shadow that will make contrast with the wheel - int red = 52, green = 115, blue = 178, - pixels = image.width() * image.height(); - unsigned int * data = (unsigned int *)image.bits(); - for( int i = 0; i < pixels; ++i ) - data[i] = qRgba( red, green, blue, data[i] & 0xFF ); + // draw circular shadow using the same technique + doublePixmap.fill( Qt::black ); + pixmapPainter.begin( &doublePixmap ); + pixmapPainter.setPen( 0x40 ); + pixmapPainter.setBrush( 0x80 ); + pixmapPainter.drawEllipse( 0, 0, side, side ); + pixmapPainter.end(); + QImage shadow( doublePixmap.convertToImage().smoothScale( side / 2, side / 2 ) ); + + // generate a 2 colors pixmap using mixing shadow (made with highlight color) + // and image (made with highlightedText color) + QColor color = palette().active().highlightedText(); + int red = color.red(), green = color.green(), blue = color.blue(); + color = palette().active().highlight(); + int sRed = color.red(), sGreen = color.green(), sBlue = color.blue(); + // pointers + unsigned int * data = (unsigned int *)image.bits(), + * shadowData = (unsigned int *)shadow.bits(), + pixels = image.width() * image.height(); + // cache data (reduce computation time to 26%!) + int c1 = -1, c2 = -1, cR = 0, cG = 0, cB = 0, cA = 0; + // foreach pixel + for( unsigned int i = 0; i < pixels; ++i ) + { + // alpha for shadow and image + int shadowAlpha = shadowData[i] & 0xFF, + srcAlpha = data[i] & 0xFF; + // cache values + if ( srcAlpha != c1 || shadowAlpha != c2 ) + { + c1 = srcAlpha; + c2 = shadowAlpha; + // fuse color components and alpha value of image over shadow + data[i] = qRgba( + cR = qt_div255( srcAlpha * red + (255 - srcAlpha) * sRed ), + cG = qt_div255( srcAlpha * green + (255 - srcAlpha) * sGreen ), + cB = qt_div255( srcAlpha * blue + (255 - srcAlpha) * sBlue ), + cA = qt_div255( srcAlpha * srcAlpha + (255 - srcAlpha) * shadowAlpha ) + ); + } + else + data[i] = qRgba( cR, cG, cB, cA ); + } m_lastRenderedOverlay.convertFromImage( image ); // start the autohide timer diff --git a/ui/presentationwidget.h b/ui/presentationwidget.h index fd59855fa9153a4f00f9aec4fcff746eeede4080..d601fb1797a6576cbdc852ab10b804fc05463f1c 100644 --- a/ui/presentationwidget.h +++ b/ui/presentationwidget.h @@ -22,6 +22,7 @@ class QTimer; class KPDFDocument; class KPDFPage; +class KPDFLink; class PresentationFrame; /** @@ -49,10 +50,13 @@ class PresentationWidget : public QDialog, public DocumentObserver void keyPressEvent( QKeyEvent * e ); void wheelEvent( QWheelEvent * e ); void mousePressEvent( QMouseEvent * e ); + void mouseReleaseEvent( QMouseEvent * e ); void mouseMoveEvent( QMouseEvent * e ); void paintEvent( QPaintEvent * e ); private: + const KPDFLink * getLink( int x, int y, QRect * geometry = 0 ) const; + void testCursorOnLink( int x, int y ); void overlayClick( const QPoint & position ); void changePage( int newPage ); void generatePage(); @@ -69,6 +73,8 @@ class PresentationWidget : public QDialog, public DocumentObserver QPixmap m_lastRenderedPixmap; QPixmap m_lastRenderedOverlay; QRect m_overlayGeometry; + const KPDFLink * m_pressedLink; + bool m_handCursor; // transition related QTimer * m_transitionTimer;