mirror of
https://invent.kde.org/graphics/okular
synced 2024-10-01 05:33:33 +00:00
table selection tool
BUGS: 279859 REVIEW: 102358 FIXED-IN: 4.8.0
This commit is contained in:
parent
9d6926f9b8
commit
f81a49fafa
1
AUTHORS
1
AUTHORS
|
@ -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>
|
||||
|
|
|
@ -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;
|
||||
|
|
11
part.cpp
11
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 */ ) );
|
||||
}
|
||||
}
|
||||
|
||||
|
|
1
part.h
1
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;
|
||||
|
|
1
part.rc
1
part.rc
|
@ -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/>
|
||||
|
|
437
ui/pageview.cpp
437
ui/pageview.cpp
|
@ -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('&', "&").replace('<', "<").replace('>', ">");
|
||||
// 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
|
||||
|
|
|
@ -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();
|
||||
|
|
Loading…
Reference in a new issue