table selection tool

BUGS: 279859
REVIEW: 102358
FIXED-IN: 4.8.0
This commit is contained in:
Jiri Baum 2011-10-12 15:50:56 +02:00 committed by Albert Astals Cid
parent 9d6926f9b8
commit f81a49fafa
7 changed files with 423 additions and 39 deletions

View file

@ -2,3 +2,4 @@ Albert Astals Cid <tsdgeos@yahoo.es>
Enrico Ros <eros.kde@email.it>
Wilco Greven <greven@kde.org>
Christophe Devriese <oelewapperke@ulyssis.org>
Jiri Baum - NICTA <jiri@baum.com.au>

View file

@ -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;

View file

@ -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 */ ) );
}
}

1
part.h
View file

@ -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;

View file

@ -68,6 +68,7 @@
<Action name="mouse_zoom"/>
<Action name="mouse_select"/>
<Action name="mouse_textselect"/>
<Action name="mouse_tableselect"/>
<Separator/>
<Action name="mouse_toggle_annotate"/>
<Separator/>

View file

@ -9,6 +9,7 @@
* 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 *
@ -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<double> tableSelectionCols;
QList<double> tableSelectionRows;
QList<TableSelectionPart> 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; i<d->tableSelectionParts.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; i<d->tableSelectionParts.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; i<d->tableSelectionParts.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; i<d->tableSelectionCols.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; i<d->tableSelectionRows.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; i<d->tableSelectionParts.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 = fromTop<fromBottom ? fromTop : fromBottom;
const int rowScore = fromLeft<fromRight ? fromLeft : fromRight;
if (colScore < rowScore) {
bool deleted=false;
for(int i=0; i<d->tableSelectionCols.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; i<d->tableSelectionRows.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<double> xs = d->tableSelectionCols;
QList<double> ys = d->tableSelectionRows;
xs.prepend(0.0);
xs.append(1.0);
ys.prepend(0.0);
ys.append(1.0);
selHtml = "<html><head>"
"<meta content=\"text/html; charset=utf-8\" http-equiv=\"Content-Type\">"
"</head><body><table>";
for (int r=0; r+1<ys.length(); r++) {
selHtml += "<tr>";
for (int c=0; c+1<xs.length(); c++) {
Okular::NormalizedRect cell(xs[c], ys[r], xs[c+1], ys[r+1]);
if (c) selText += '\t';
QString txt;
for (int i=0; i<d->tableSelectionParts.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('&', "&amp;").replace('<', "&lt;").replace('>', "&gt;");
// Remove newlines, do not turn them into <br>, because
// Excel interprets <br> within cell as new cell...
html.replace('\n', " ");
selHtml += "<td>"+html+"</td>";
}
selText += '\n';
selHtml += "</tr>\n";
}
selHtml += "</table></body></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; i<d->tableSelectionParts.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

View file

@ -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();