From f81a49fafaa6bf0991b72280f8704fe254f2d909 Mon Sep 17 00:00:00 2001 From: Jiri Baum Date: Wed, 12 Oct 2011 15:50:56 +0200 Subject: [PATCH] table selection tool BUGS: 279859 REVIEW: 102358 FIXED-IN: 4.8.0 --- AUTHORS | 1 + aboutdata.h | 4 +- part.cpp | 11 +- part.h | 1 + part.rc | 1 + ui/pageview.cpp | 437 ++++++++++++++++++++++++++++++++++++++++++++---- ui/pageview.h | 7 +- 7 files changed, 423 insertions(+), 39 deletions(-) diff --git a/AUTHORS b/AUTHORS index 55b672e33..1088cf08b 100644 --- a/AUTHORS +++ b/AUTHORS @@ -2,3 +2,4 @@ Albert Astals Cid Enrico Ros Wilco Greven Christophe Devriese +Jiri Baum - NICTA diff --git a/aboutdata.h b/aboutdata.h index f9c5896d7..5050bee97 100644 --- a/aboutdata.h +++ b/aboutdata.h @@ -27,7 +27,8 @@ inline KAboutData okularAboutData( const char* name, const char* iname ) "(C) 2004-2005 Enrico Ros\n" "(C) 2005 Piotr Szymanski\n" "(C) 2004-2009 Albert Astals Cid\n" - "(C) 2006-2009 Pino Toscano"), + "(C) 2006-2009 Pino Toscano\n" + "(C) 2011 Jiri Baum - NICTA"), KLocalizedString(), "http://okular.kde.org" ); @@ -37,6 +38,7 @@ inline KAboutData okularAboutData( const char* name, const char* iname ) about.addAuthor(ki18n("Albert Astals Cid"), ki18n("Current maintainer"), "aacid@kde.org"); about.addAuthor(ki18n("Piotr Szymanski"), ki18n("Created Okular from KPDF codebase"), "djurban@pld-dc.org"); about.addAuthor(ki18n("Enrico Ros"), ki18n("KPDF developer"), "eros.kde@email.it"); + about.addAuthor(ki18n("Jiri Baum - NICTA"), ki18n("Table selection tool"), "jiri@baum.com.au"); about.addCredit(ki18n("Eugene Trounev"), ki18n("Annotations artwork"), "eugene.trounev@gmail.com"); return about; diff --git a/part.cpp b/part.cpp index f0d706895..9219db7d9 100644 --- a/part.cpp +++ b/part.cpp @@ -590,11 +590,10 @@ m_cliPresentation(false), m_embedMode(detectEmbedMode(parentWidget, parent, args reload->setShortcut( KStandardShortcut::reload() ); m_reload = reload; - KAction *closeFindBar = new KAction( i18n( "Close &Find Bar" ), ac ); - ac->addAction("close_find_bar", closeFindBar); - connect(closeFindBar, SIGNAL(triggered()), this, SLOT(slotHideFindBar())); - closeFindBar->setShortcut( QKeySequence( Qt::Key_Escape ) ); - widget()->addAction(closeFindBar); + m_closeFindBar = new KAction( i18n( "Close &Find Bar" ), ac ); + ac->addAction("close_find_bar", m_closeFindBar); + connect(m_closeFindBar, SIGNAL(triggered()), this, SLOT(slotHideFindBar())); + widget()->addAction(m_closeFindBar); KToggleAction *blackscreenAction = new KToggleAction( i18n( "Switch Blackscreen Mode" ), ac ); ac->addAction( "switch_blackscreen_mode", blackscreenAction ); @@ -1408,6 +1407,7 @@ void Part::slotShowFindBar() { m_findBar->show(); m_findBar->focusAndSetCursor(); + m_closeFindBar->setShortcut( QKeySequence( Qt::Key_Escape ) ); } void Part::slotHideFindBar() @@ -1415,6 +1415,7 @@ void Part::slotHideFindBar() if ( m_findBar->maybeHide() ) { m_pageView->setFocus(); + m_closeFindBar->setShortcut( QKeySequence( /* None */ ) ); } } diff --git a/part.h b/part.h index a36031bb1..f30eb6d1d 100644 --- a/part.h +++ b/part.h @@ -257,6 +257,7 @@ class Part : public KParts::ReadOnlyPart, public Okular::DocumentObserver, publi KAction *m_aboutBackend; KAction *m_reload; QMenu *m_exportAsMenu; + KAction *m_closeFindBar; bool m_actionsSearched; BrowserExtension *m_bExtension; diff --git a/part.rc b/part.rc index 928168d09..c0f520fb4 100644 --- a/part.rc +++ b/part.rc @@ -68,6 +68,7 @@ + diff --git a/ui/pageview.cpp b/ui/pageview.cpp index a03e9e7e2..9a15e417e 100644 --- a/ui/pageview.cpp +++ b/ui/pageview.cpp @@ -9,6 +9,7 @@ * Copyright (C) 2003 by Laurent Montel * * Copyright (C) 2003 by Dirk Mueller * * Copyright (C) 2004 by James Ots * + * Copyright (C) 2011 by Jiri Baum - NICTA * * * * 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 * @@ -85,6 +86,20 @@ static inline double normClamp( double value, double def ) return ( value < 0.0 || value > 1.0 ) ? def : value; } +struct TableSelectionPart { +public: + PageViewItem * item; + Okular::NormalizedRect rectInItem; + Okular::NormalizedRect rectInSelection; + + TableSelectionPart(PageViewItem * item_p, Okular::NormalizedRect rectInItem_p, Okular::NormalizedRect rectInSelection_p); +}; + +TableSelectionPart::TableSelectionPart( PageViewItem * item_p, Okular::NormalizedRect rectInItem_p, Okular::NormalizedRect rectInSelection_p) + : item ( item_p ), rectInItem (rectInItem_p), rectInSelection (rectInSelection_p) +{ +} + // structure used internally by PageView for data storage class PageViewPrivate { @@ -119,6 +134,11 @@ public: Okular::Annotation * mouseAnn; QPoint mouseAnnPos; + // table selection + QList tableSelectionCols; + QList tableSelectionRows; + QList tableSelectionParts; + // viewport move bool viewportMoveActive; QTime viewportMoveTime; @@ -162,6 +182,7 @@ public: KAction * aMouseNormal; KAction * aMouseSelect; KAction * aMouseTextSelect; + KAction * aMouseTableSelect; KToggleAction * aToggleAnnotator; KSelectAction * aZoom; KAction * aZoomIn; @@ -495,6 +516,14 @@ do { \ d->aMouseTextSelect->setShortcut( Qt::CTRL + Qt::Key_4 ); d->aMouseTextSelect->setActionGroup( actGroup ); + d->aMouseTableSelect = new KAction(KIcon( "select-table" ), i18n("T&able Selection Tool"), this); + ac->addAction("mouse_tableselect", d->aMouseTableSelect ); + connect( d->aMouseTableSelect, SIGNAL( triggered() ), this, SLOT( slotSetMouseTableSelect() ) ); + d->aMouseTableSelect->setIconText( i18nc( "Table Selection Tool", "Table Selection" ) ); + d->aMouseTableSelect->setCheckable( true ); + d->aMouseTableSelect->setShortcut( Qt::CTRL + Qt::Key_5 ); + d->aMouseTableSelect->setActionGroup( actGroup ); + d->aToggleAnnotator = new KToggleAction(KIcon( "draw-freehand" ), i18n("&Review"), this); ac->addAction("mouse_toggle_annotate", d->aToggleAnnotator ); d->aToggleAnnotator->setCheckable( true ); @@ -868,6 +897,8 @@ void PageView::notifySetup( const QVector< Okular::Page * > & pageSet, int setup } qDeleteAll( d->m_annowindows ); d->m_annowindows.clear(); + + selectionClear(); } void PageView::notifyViewportChanged( bool smoothMove ) @@ -1193,6 +1224,8 @@ void PageView::paintEvent(QPaintEvent *pe) // note: this check will take care of all things requiring alpha blending (not only selection) bool wantCompositing = !selectionRect.isNull() && contentsRect.intersects( selectionRect ); + // also alpha-blend when there is a table selection... + wantCompositing |= !d->tableSelectionParts.isEmpty(); if ( wantCompositing && Okular::Settings::enableCompositing() ) { @@ -1203,7 +1236,7 @@ void PageView::paintEvent(QPaintEvent *pe) // 1) Layer 0: paint items and clear bg on unpainted rects drawDocumentOnPainter( contentsRect, &pixmapPainter ); - // 2) Layer 1a: paint (blend) transparent selection + // 2a) Layer 1a: paint (blend) transparent selection (rectangle) if ( !selectionRect.isNull() && selectionRect.intersects( contentsRect ) && !selectionRectInternal.contains( contentsRect ) ) { @@ -1229,6 +1262,40 @@ void PageView::paintEvent(QPaintEvent *pe) pixmapPainter.setPen( selBlendColor ); pixmapPainter.drawRect( selectionRect.adjusted( 0, 0, -1, -1 ) ); } + // 2b) Layer 1b: paint (blend) transparent selection (table) + for (int i=0; itableSelectionParts.length(); i++) { + TableSelectionPart& tsp = d->tableSelectionParts[i]; + QRect selectionPartRect = tsp.rectInItem.geometry(tsp.item->uncroppedWidth(), tsp.item->uncroppedHeight()); + selectionPartRect.translate( tsp.item->uncroppedGeometry().topLeft () ); + QRect selectionPartRectInternal = selectionPartRect; + selectionPartRectInternal.adjust( 1, 1, -1, -1 ); + if ( !selectionPartRect.isNull() && selectionPartRect.intersects( contentsRect ) && + !selectionPartRectInternal.contains( contentsRect ) ) + { + QRect blendRect = selectionPartRectInternal.intersect( contentsRect ); + // skip rectangles covered by the selection's border + if ( blendRect.isValid() ) + { + // grab current pixmap into a new one to colorize contents + QPixmap blendedPixmap( blendRect.width(), blendRect.height() ); + QPainter p( &blendedPixmap ); + p.drawPixmap( 0, 0, doubleBuffer, + blendRect.left() - contentsRect.left(), blendRect.top() - contentsRect.top(), + blendRect.width(), blendRect.height() ); + + QColor blCol = d->mouseSelectionColor.dark( 140 ); + blCol.setAlphaF( 0.2 ); + p.fillRect( blendedPixmap.rect(), blCol ); + p.end(); + // copy the blended pixmap back to its place + pixmapPainter.drawPixmap( blendRect.left(), blendRect.top(), blendedPixmap ); + } + // draw border (red if the selection is too small) + pixmapPainter.setPen( d->mouseSelectionColor ); + pixmapPainter.drawRect( selectionPartRect.adjusted( 0, 0, -1, -1 ) ); + } + } + drawTableDividers( &pixmapPainter ); // 3) Layer 1: give annotator painting control if ( d->annotator && d->annotator->routePaints( contentsRect ) ) d->annotator->routePaint( &pixmapPainter, contentsRect ); @@ -1247,13 +1314,28 @@ void PageView::paintEvent(QPaintEvent *pe) { // 1) Layer 0: paint items and clear bg on unpainted rects drawDocumentOnPainter( contentsRect, &screenPainter ); - // 2) Layer 1: paint opaque selection + // 2a) Layer 1a: paint opaque selection (rectangle) if ( !selectionRect.isNull() && selectionRect.intersects( contentsRect ) && !selectionRectInternal.contains( contentsRect ) ) { screenPainter.setPen( palette().color( QPalette::Active, QPalette::Highlight ).dark(110) ); screenPainter.drawRect( selectionRect ); } + // 2b) Layer 1b: paint opaque selection (table) + for (int i=0; itableSelectionParts.length(); i++) { + TableSelectionPart& tsp = d->tableSelectionParts[i]; + QRect selectionPartRect = tsp.rectInItem.geometry(tsp.item->uncroppedWidth(), tsp.item->uncroppedHeight()); + selectionPartRect.translate( tsp.item->uncroppedGeometry().topLeft () ); + QRect selectionPartRectInternal = selectionPartRect; + selectionPartRectInternal.adjust( 1, 1, -1, -1 ); + if ( !selectionPartRect.isNull() && selectionPartRect.intersects( contentsRect ) && + !selectionPartRectInternal.contains( contentsRect ) ) + { + screenPainter.setPen( palette().color( QPalette::Active, QPalette::Highlight ).dark(110) ); + screenPainter.drawRect( selectionPartRect ); + } + } + drawTableDividers( &screenPainter ); // 3) Layer 1: give annotator painting control if ( d->annotator && d->annotator->routePaints( contentsRect ) ) d->annotator->routePaint( &screenPainter, contentsRect ); @@ -1267,6 +1349,42 @@ void PageView::paintEvent(QPaintEvent *pe) } } +void PageView::drawTableDividers(QPainter * screenPainter) +{ + if (!d->tableSelectionParts.isEmpty()) { + screenPainter->setPen( d->mouseSelectionColor.dark() ); + for (int i=0; itableSelectionParts.length(); i++) { + TableSelectionPart& tsp = d->tableSelectionParts[i]; + QRect selectionPartRect = tsp.rectInItem.geometry(tsp.item->uncroppedWidth(), tsp.item->uncroppedHeight()); + selectionPartRect.translate( tsp.item->uncroppedGeometry().topLeft () ); + QRect selectionPartRectInternal = selectionPartRect; + selectionPartRectInternal.adjust( 1, 1, -1, -1 ); + for(int i=0; itableSelectionCols.length(); i++) { + double col = d->tableSelectionCols[i]; + if (col >= tsp.rectInSelection.left && col <= tsp.rectInSelection.right) { + col = (col - tsp.rectInSelection.left) / (tsp.rectInSelection.right - tsp.rectInSelection.left); + const int x = selectionPartRect.left() + col * selectionPartRect.width() + 0.5; + screenPainter->drawLine( + x, selectionPartRectInternal.top(), + x, selectionPartRectInternal.top() + selectionPartRectInternal.height() + ); + } + } + for(int i=0; itableSelectionRows.length(); i++) { + double row = d->tableSelectionRows[i]; + if (row >= tsp.rectInSelection.top && row <= tsp.rectInSelection.bottom) { + row = (row - tsp.rectInSelection.top) / (tsp.rectInSelection.bottom - tsp.rectInSelection.top); + const int y = selectionPartRect.top() + row * selectionPartRect.height() + 0.5; + screenPainter->drawLine( + selectionPartRectInternal.left(), y, + selectionPartRectInternal.left() + selectionPartRectInternal.width(), y + ); + } + } + } + } +} + void PageView::resizeEvent( QResizeEvent *e ) { if ( d->items.isEmpty() ) @@ -1606,6 +1724,7 @@ void PageView::mouseMoveEvent( QMouseEvent * e ) case MouseZoom: case MouseSelect: case MouseImageSelect: + case MouseTableSelect: // set second corner of selection if ( d->mouseSelecting ) selectionEndPoint( eventPos ); @@ -1759,6 +1878,84 @@ void PageView::mousePressEvent( QMouseEvent * e ) selectionStart( eventPos, palette().color( QPalette::Active, QPalette::Highlight ).light( 120 ), false ); } break; + case MouseTableSelect: + if ( leftButton ) + { + if (d->tableSelectionParts.isEmpty()) + { + selectionStart( eventPos, palette().color( QPalette::Active, QPalette::Highlight ).light( 120 ), false ); + } else { + QRect updatedRect; + for (int i=0; itableSelectionParts.length(); i++) { + TableSelectionPart& tsp = d->tableSelectionParts[i]; + QRect selectionPartRect = tsp.rectInItem.geometry(tsp.item->uncroppedWidth(), tsp.item->uncroppedHeight()); + selectionPartRect.translate( tsp.item->uncroppedGeometry().topLeft () ); + + // This will update the whole table rather than just the added/removed divider + // (which can span more than one part). + updatedRect = updatedRect.united(selectionPartRect); + + if (!selectionPartRect.contains(eventPos)) + continue; + + // There's probably a neat trick to finding which edge it's closest to, + // but this way has the advantage of simplicity. + const int fromLeft = abs(selectionPartRect.left() - eventPos.x()); + const int fromRight = abs(selectionPartRect.left() + selectionPartRect.width() - eventPos.x()); + const int fromTop = abs(selectionPartRect.top() - eventPos.y()); + const int fromBottom = abs(selectionPartRect.top() + selectionPartRect.height() - eventPos.y()); + const int colScore = fromToptableSelectionCols.length(); i++) { + const double col = (d->tableSelectionCols[i] - tsp.rectInSelection.left) / (tsp.rectInSelection.right - tsp.rectInSelection.left); + const int colX = selectionPartRect.left() + col * selectionPartRect.width() + 0.5; + if (abs(colX - eventPos.x())<=3) { + d->tableSelectionCols.removeAt(i); + deleted=true; + + break; + } + } + if (!deleted) { + double col = eventPos.x() - selectionPartRect.left(); + col /= selectionPartRect.width(); // at this point, it's normalised within the part + col *= (tsp.rectInSelection.right - tsp.rectInSelection.left); + col += tsp.rectInSelection.left; // at this point, it's normalised within the whole table + + d->tableSelectionCols.append(col); + qSort(d->tableSelectionCols); + } + } else { + bool deleted=false; + for(int i=0; itableSelectionRows.length(); i++) { + const double row = (d->tableSelectionRows[i] - tsp.rectInSelection.top) / (tsp.rectInSelection.bottom - tsp.rectInSelection.top); + const int rowY = selectionPartRect.top() + row * selectionPartRect.height() + 0.5; + if (abs(rowY - eventPos.y())<=3) { + d->tableSelectionRows.removeAt(i); + deleted=true; + + break; + } + } + if (!deleted) { + double row = eventPos.y() - selectionPartRect.top(); + row /= selectionPartRect.height(); // at this point, it's normalised within the part + row *= (tsp.rectInSelection.bottom - tsp.rectInSelection.top); + row += tsp.rectInSelection.top; // at this point, it's normalised within the whole table + + d->tableSelectionRows.append(row); + qSort(d->tableSelectionRows); + } + } + } + updatedRect.translate( -contentAreaPosition() ); + viewport()->update( updatedRect ); + } + } + break; case MouseTextSelect: d->mouseSelectPos = eventPos; if ( !rightButton ) @@ -2017,35 +2214,7 @@ void PageView::mouseReleaseEvent( QMouseEvent * e ) } // if we support text generation - QString selectedText; - if (d->document->supportsSearching()) - { - // grab text in selection by extracting it from all intersected pages - const Okular::Page * okularPage=0; - QVector< PageViewItem * >::const_iterator iIt = d->items.constBegin(), iEnd = d->items.constEnd(); - for ( ; iIt != iEnd; ++iIt ) - { - PageViewItem * item = *iIt; - if ( !item->isVisible() ) - continue; - - const QRect & itemRect = item->croppedGeometry(); - if ( selectionRect.intersects( itemRect ) ) - { - // request the textpage if there isn't one - okularPage= item->page(); - kWarning() << "checking if page" << item->pageNumber() << "has text:" << okularPage->hasTextPage(); - if ( !okularPage->hasTextPage() ) - d->document->requestTextPage( okularPage->number() ); - // grab text in the rect that intersects itemRect - QRect relativeRect = selectionRect.intersect( itemRect ); - relativeRect.translate( -item->uncroppedGeometry().topLeft() ); - Okular::RegularAreaRect rects; - rects.append( Okular::NormalizedRect( relativeRect, item->uncroppedWidth(), item->uncroppedHeight() ) ); - selectedText += okularPage->text( &rects ); - } - } - } + QString selectedText = rectExtractText(selectionRect, Okular::TextPage::AnyPixelTextAreaInclusionBehaviour); // popup that ask to copy:text and copy/save:image KMenu menu( this ); @@ -2141,6 +2310,150 @@ void PageView::mouseReleaseEvent( QMouseEvent * e ) d->aPrevAction = 0; } }break; + + case MouseTableSelect: + { + // if mouse is released and selection is null this is a rightClick + if ( rightButton && !d->mouseSelecting ) + { + PageViewItem * pageItem = pickItemOnPoint( eventPos.x(), eventPos.y() ); + emit rightClick( pageItem ? pageItem->page() : 0, e->globalPos() ); + break; + } + + QRect selectionRect = d->mouseSelectionRect.normalized(); + if ( selectionRect.width() <= 8 && selectionRect.height() <= 8 && d->tableSelectionParts.isEmpty() ) + { + selectionClear(); + if ( d->aPrevAction ) + { + d->aPrevAction->trigger(); + d->aPrevAction = 0; + } + break; + } + + if (d->mouseSelecting) { + // break up the selection into page-relative pieces + d->tableSelectionParts.clear(); + const Okular::Page * okularPage=0; + QVector< PageViewItem * >::const_iterator iIt = d->items.constBegin(), iEnd = d->items.constEnd(); + for ( ; iIt != iEnd; ++iIt ) + { + PageViewItem * item = *iIt; + if ( !item->isVisible() ) + continue; + + const QRect & itemRect = item->croppedGeometry(); + if ( selectionRect.intersects( itemRect ) ) + { + // request the textpage if there isn't one + okularPage= item->page(); + kWarning() << "checking if page" << item->pageNumber() << "has text:" << okularPage->hasTextPage(); + if ( !okularPage->hasTextPage() ) + d->document->requestTextPage( okularPage->number() ); + // grab text in the rect that intersects itemRect + QRect rectInItem = selectionRect.intersect( itemRect ); + rectInItem.translate( -item->uncroppedGeometry().topLeft() ); + QRect rectInSelection = selectionRect.intersect( itemRect ); + rectInSelection.translate( -selectionRect.topLeft() ); + d->tableSelectionParts.append( + TableSelectionPart( + item, + Okular::NormalizedRect( rectInItem, item->uncroppedWidth(), item->uncroppedHeight() ), + Okular::NormalizedRect( rectInSelection, selectionRect.width(), selectionRect.height() ) + ) + ); + } + } + + QRect updatedRect = d->mouseSelectionRect.normalized().adjusted( 0, 0, 1, 1 ); + updatedRect.translate( -contentAreaPosition() ); + d->mouseSelecting = false; + d->mouseSelectionRect.setCoords( 0, 0, 0, 0 ); + d->tableSelectionCols.clear(); + d->tableSelectionRows.clear(); + viewport()->update( updatedRect ); + } + + if ( !d->document->isAllowed( Okular::AllowCopy ) ) { + d->messageWindow->display( i18n("Copy forbidden by DRM"), QString(), PageViewMessage::Info, -1 ); + break; + } + + QString selText; + QString selHtml; + QList xs = d->tableSelectionCols; + QList ys = d->tableSelectionRows; + xs.prepend(0.0); + xs.append(1.0); + ys.prepend(0.0); + ys.append(1.0); + selHtml = "" + "" + ""; + for (int r=0; r+1tableSelectionParts.length(); i++) { + TableSelectionPart& tsp = d->tableSelectionParts[i]; + + // first, crop the cell to this part + if (!tsp.rectInSelection.intersects(cell)) + continue; + Okular::NormalizedRect cellPart = tsp.rectInSelection & cell; // intersection + + // second, convert it from table coordinates to part coordinates + cellPart.left -= tsp.rectInSelection.left; + cellPart.left /= (tsp.rectInSelection.right - tsp.rectInSelection.left); + cellPart.right -= tsp.rectInSelection.left; + cellPart.right /= (tsp.rectInSelection.right - tsp.rectInSelection.left); + cellPart.top -= tsp.rectInSelection.top; + cellPart.top /= (tsp.rectInSelection.bottom - tsp.rectInSelection.top); + cellPart.bottom -= tsp.rectInSelection.top; + cellPart.bottom /= (tsp.rectInSelection.bottom - tsp.rectInSelection.top); + + // third, convert from part coordinates to item coordinates + cellPart.left *= (tsp.rectInItem.right - tsp.rectInItem.left); + cellPart.left += tsp.rectInItem.left; + cellPart.right *= (tsp.rectInItem.right - tsp.rectInItem.left); + cellPart.right += tsp.rectInItem.left; + cellPart.top *= (tsp.rectInItem.bottom - tsp.rectInItem.top); + cellPart.top += tsp.rectInItem.top; + cellPart.bottom *= (tsp.rectInItem.bottom - tsp.rectInItem.top); + cellPart.bottom += tsp.rectInItem.top; + + // now get the text + Okular::RegularAreaRect rects; + rects.append( cellPart ); + txt += tsp.item->page()->text( &rects, Okular::TextPage::CentralPixelTextAreaInclusionBehaviour ); + } + QString html = txt; + selText += txt.replace('\n', ' '); + html.replace('&', "&").replace('<', "<").replace('>', ">"); + // Remove newlines, do not turn them into
, because + // Excel interprets
within cell as new cell... + html.replace('\n', " "); + selHtml += "
"; + } + selText += '\n'; + selHtml += "\n"; + } + selHtml += "
"+html+"
\n"; + + + QClipboard *cb = QApplication::clipboard(); + QMimeData *md = new QMimeData(); + md->setText(selText); + md->setHtml(selHtml); + cb->setMimeData( md, QClipboard::Clipboard ); + if ( cb->supportsSelection() ) + cb->setMimeData( md, QClipboard::Selection ); + + }break; case MouseTextSelect: if ( d->mouseTextSelecting ) { @@ -2618,6 +2931,42 @@ PageViewItem * PageView::pickItemOnPoint( int x, int y ) return item; } +const QString PageView::rectExtractText(const QRect &selectionRect, const Okular::TextPage::TextAreaInclusionBehaviour b) +{ + QString selectedText; + + // if we support text generation + if (d->document->supportsSearching()) + { + // grab text in selection by extracting it from all intersected pages + const Okular::Page * okularPage=0; + QVector< PageViewItem * >::const_iterator iIt = d->items.constBegin(), iEnd = d->items.constEnd(); + for ( ; iIt != iEnd; ++iIt ) + { + PageViewItem * item = *iIt; + if ( !item->isVisible() ) + continue; + + const QRect & itemRect = item->croppedGeometry(); + if ( selectionRect.intersects( itemRect ) ) + { + // request the textpage if there isn't one + okularPage= item->page(); + kWarning() << "checking if page" << item->pageNumber() << "has text:" << okularPage->hasTextPage(); + if ( !okularPage->hasTextPage() ) + d->document->requestTextPage( okularPage->number() ); + // grab text in the rect that intersects itemRect + QRect relativeRect = selectionRect.intersect( itemRect ); + relativeRect.translate( -item->uncroppedGeometry().topLeft() ); + Okular::RegularAreaRect rects; + rects.append( Okular::NormalizedRect( relativeRect, item->uncroppedWidth(), item->uncroppedHeight() ) ); + selectedText += okularPage->text( &rects, b ); + } + } + } + return selectedText; +} + void PageView::textSelectionClear() { // something to clear @@ -2724,9 +3073,19 @@ Okular::RegularAreaRect * PageView::textSelectionForItem( PageViewItem * item, c void PageView::selectionClear() { QRect updatedRect = d->mouseSelectionRect.normalized().adjusted( 0, 0, 1, 1 ); - updatedRect.translate( -contentAreaPosition() ); d->mouseSelecting = false; d->mouseSelectionRect.setCoords( 0, 0, 0, 0 ); + d->tableSelectionCols.clear(); + d->tableSelectionRows.clear(); + for (int i=0; itableSelectionParts.length(); i++) { + TableSelectionPart& tsp = d->tableSelectionParts[i]; + QRect selectionPartRect = tsp.rectInItem.geometry(tsp.item->uncroppedWidth(), tsp.item->uncroppedHeight()); + selectionPartRect.translate( tsp.item->uncroppedGeometry().topLeft () ); + // should check whether this is on-screen here? + updatedRect = updatedRect.united(selectionPartRect); + } + d->tableSelectionParts.clear(); + updatedRect.translate( -contentAreaPosition() ); viewport()->update( updatedRect ); } @@ -3556,6 +3915,20 @@ void PageView::slotSetMouseTextSelect() updateCursor( contentAreaPosition() + viewport()->mapFromGlobal( QCursor::pos() ) ); } +void PageView::slotSetMouseTableSelect() +{ + d->mouseMode = MouseTableSelect; + // change the text in messageWindow (and show it if hidden) + d->messageWindow->display( i18n( + "Draw a rectangle around the table, then click near edges to divide up; press Esc to clear." + ), QString(), PageViewMessage::Info, -1 ); + // force hiding of annotator toolbar + if ( d->annotator ) + d->annotator->setEnabled( false ); + // force an update of the cursor + updateCursor( contentAreaPosition() + viewport()->mapFromGlobal( QCursor::pos() ) ); +} + void PageView::slotToggleAnnotator( bool on ) { // the 'inHere' trick is needed as the slotSetMouseZoom() calls this diff --git a/ui/pageview.h b/ui/pageview.h index 78f7838e0..c8f84c551 100644 --- a/ui/pageview.h +++ b/ui/pageview.h @@ -26,6 +26,7 @@ #include "core/area.h" #include "core/observer.h" #include "core/view.h" +#include "core/textpage.h" class KAction; class KActionCollection; @@ -58,7 +59,7 @@ Q_OBJECT // Zoom mode ( last 4 are internally used only! ) enum ZoomMode { ZoomFixed = 0, ZoomFitWidth = 1, ZoomFitPage = 2, ZoomFitText, ZoomIn, ZoomOut, ZoomRefreshCurrent }; - enum MouseMode { MouseNormal, MouseZoom, MouseSelect, MouseImageSelect, MouseTextSelect }; + enum MouseMode { MouseNormal, MouseZoom, MouseSelect, MouseImageSelect, MouseTextSelect, MouseTableSelect }; // create actions that interact with this widget void setupBaseActions( KActionCollection * collection ); @@ -156,10 +157,13 @@ Q_OBJECT void updateItemSize( PageViewItem * item, int columnWidth, int rowHeight ); // return the widget placed on a certain point or 0 if clicking on empty space PageViewItem * pickItemOnPoint( int x, int y ); + // extract text from a rectangular region + const QString rectExtractText(const QRect &selectionRect, const Okular::TextPage::TextAreaInclusionBehaviour b); // start / modify / clear selection rectangle void selectionStart( const QPoint & pos, const QColor & color, bool aboveAll = false ); void selectionEndPoint( const QPoint & pos ); void selectionClear(); + void drawTableDividers(QPainter * screenPainter); // update internal zoom values and end in a slotRelayoutPages(); void updateZoom( ZoomMode newZm ); // update the text on the label using global zoom value or current page's one @@ -216,6 +220,7 @@ Q_OBJECT void slotSetMouseZoom(); void slotSetMouseSelect(); void slotSetMouseTextSelect(); + void slotSetMouseTableSelect(); void slotToggleAnnotator( bool ); void slotScrollUp(); void slotScrollDown();