Commit 6a175f76 authored by Enrico Ros's avatar Enrico Ros

Merged Selection tools: a popup asks wether to save/copy text or gfx.

Added 'continous zooming' using mid mouse button. Clipped selections to
viewport. Fixed a bad rounded float-to-int conversion (gives more pixel
precision in zooming).
CCMAIL: burellil@dei.unipd.it

svn path=/branches/kpdf_experiments/kdegraphics/kpdf/; revision=363468
parent f962069a
......@@ -8,25 +8,29 @@ Legend:
In progress on the branch (first item comes first):
-> ADD: viewport changes the right way when clicking links (also suggested by Mikolaj Machowski) [40% done]
-> memory manager with different profiles (mem/cpu tradeoff: {memory saving, normal, memory aggressive}) [0%]
-> async document generator using Albert's generator thread [0%]
Things to do in order to merge in HEAD (first item has highest priority):
-> memory manager with different profiles (mem/cpu tradeoff: {memory saving, normal, memory aggressive})
-> async document generator using Albert's generator thread
-> import Marco Martin's "another kpdf icon" (kde-look: 16146)
-> link thumbnails view with document
-> usability: layout 2PPV [1 2,3 4,5 6] -> [1,2 3,4 5]. add option for 'ebook' style alignemnt. (by Mikolaj)
-> usability: clear search must restore the full thumbs list (by Mikolaj)
-> usability: trigger redraw on 'filter text' on current page (by Mikolaj)
-> new icons (contest at kde-look has ended and we know the winner. good choice :-)
-> watch out for FIXMEs in code
-> take care of naming on merge, too differences (remove some kpdf_* prefixes
and rename internals toodocument->kpdfdocument)
and rename internals too document->kpdfdocument, page->kpdfpage, etc..)
More items (first items will enter 'In progress list' first):
*THIS ITEMS ARE CURRENTLY FROZEN SINCE "HEAD-MERGE" LIST IS CLEARED*
-> display current page / total pages (with analog indicator too (progressbar/...))
maybe this can be done on a small widget at the top of the toolbox, displaying
'document' informations (pages, current pg, some metadata, etc..)
-> take care of TODOs in code
-> find: scroll page if the the searched string is not visible [after 'viewport changes']
-> ADD: click over image allows "save image" [60% done]
-> screen editing (annotations): framework (BR67300,BR62793)
-> screen editing (annotations): tools (BR67300)
-> display current page / total pages (with analog indicator too (progressbar/indicator/...))
-> session support: restoring page location (BR82589)
-> export all text in plain_text/html
-> extract(export?) images (have a look at ImageOutputDev.cc and pdfimages.cc from xpdf (not in our xpdf sources))
......@@ -45,6 +49,8 @@ More items (first items will enter 'In progress list' first):
-> rotate the whole document / individual pages
Done (newest feature comes firts):
-> ADD: dynamic zoom with mid mouse button (click and drag up-down to zoom in-out)
-> FIX: merge select text & select gfx, two sections on the same pop-up menu
-> ADD: reading aids (inverted display, recolor, black/white, draw link border, draw image border)
-> FIX: zoom preserved when switching modes and flickerless drawing
-> ADD: Printing as PS instead of as image (Albert)
......@@ -85,7 +91,6 @@ Here comes a list of suggestions from a dot post http://dot.kde.org/1095261317 a
Tool: ruler, measure: distance, perimeter, ?area?
Tool: color picker
Annotations: yellow notes 'post-it' like
Speak: integration with a voice synthesizer
Export: export to other formats keeping formatting (a dream.. except for PNG :-)
PDF: <theICEBear> pdf forms support.... :D if at all possible
......
......@@ -49,8 +49,6 @@
<Separator/>
<Action name="mouse_drag"/>
<Action name="mouse_zoom"/>
<Action name="mouse_select_text"/>
<Action name="mouse_select_gfx"/>
<Action name="mouse_draw"/>
<Action name="mouse_select"/>
</ToolBar>
</kpartgui>
......@@ -41,7 +41,6 @@
#include "page.h"
#include "settings.h"
// structure used internally by PageView for data storage
class PageViewPrivate
{
......@@ -58,10 +57,10 @@ public:
PageView::MouseMode mouseMode;
QPoint mouseGrabPos;
QPoint mouseStartPos;
int mouseMidStartY;
bool mouseOnLink;
bool mouseOnActiveRect;
QRect mouseSelectionRect;
PageViewItem * mouseSelectionItem;
// other stuff
QTimer * delayTimer;
......@@ -103,9 +102,9 @@ PageView::PageView( QWidget *parent, KPDFDocument *document )
d->zoomMode = ZoomFixed;
d->zoomFactor = 1.0;
d->mouseMode = MouseNormal;
d->mouseMidStartY = -1;
d->mouseOnLink = false;
d->mouseOnActiveRect = false;
d->mouseSelectionItem = 0;
d->delayTimer = 0;
d->scrollTimer = 0;
d->scrollIncrement = 0;
......@@ -115,7 +114,8 @@ PageView::PageView( QWidget *parent, KPDFDocument *document )
// widget setup: setup focus, accept drops and track mouse
viewport()->setFocusProxy( this );
viewport()->setFocusPolicy( StrongFocus );
viewport()->setPaletteBackgroundColor( Qt::gray );
//viewport()->setPaletteBackgroundColor( Qt::white );
viewport()->setBackgroundMode( Qt::NoBackground );
setResizePolicy( Manual );
setAcceptDrops( true );
setDragAutoScroll( false );
......@@ -174,12 +174,9 @@ void PageView::setupActions( KActionCollection * ac )
KToggleAction * mz = new KRadioAction( i18n("Zoom Tool"), "viewmag", 0, this, SLOT( slotSetMouseZoom() ), ac, "mouse_zoom" );
mz->setExclusiveGroup( "MouseType" );
KToggleAction * mst = new KRadioAction( i18n("Select Text"), "frame_edit", 0, this, SLOT( slotSetMouseSelText() ), ac, "mouse_select_text" );
KToggleAction * mst = new KRadioAction( i18n("Select"), "frame_edit", 0, this, SLOT( slotSetMouseSelect() ), ac, "mouse_select" );
mst->setExclusiveGroup( "MouseType" );
KToggleAction * msg = new KRadioAction( i18n("Select Graphics"), "frame_image", 0, this, SLOT( slotSetMouseSelGfx() ), ac, "mouse_select_gfx" );
msg->setExclusiveGroup( "MouseType" );
d->aMouseEdit = new KRadioAction( i18n("Draw"), "edit", 0, this, SLOT( slotSetMouseDraw() ), ac, "mouse_draw" );
d->aMouseEdit->setExclusiveGroup("MouseType");
d->aMouseEdit->setEnabled( false ); // implement feature before removing this line
......@@ -480,8 +477,19 @@ void PageView::keyPressEvent( QKeyEvent * e )
void PageView::contentsMouseMoveEvent( QMouseEvent * e )
{
bool leftButton = e->state() & LeftButton;
// if holding mouse mid button, perform zoom
if ( (e->state() & MidButton) && d->mouseMidStartY > 0 )
{
int deltaY = d->mouseMidStartY - e->globalPos().y();
d->mouseMidStartY = e->globalPos().y();
d->zoomFactor *= ( 1.0 + ( (double)deltaY / 500.0 ) );
updateZoom( ZoomRefreshCurrent );
// uncomment following line to force a complete redraw
//viewport()->repaint();
return;
}
bool leftButton = e->state() & LeftButton;
switch ( d->mouseMode )
{
case MouseNormal:
......@@ -531,8 +539,7 @@ void PageView::contentsMouseMoveEvent( QMouseEvent * e )
break;
case MouseZoom:
case MouseSelText:
case MouseSelGfx:
case MouseSelect:
// set second corner of selection in selection pageItem
if ( leftButton && !d->mouseSelectionRect.isNull() )
selectionEndPoint( e->x(), e->y() );
......@@ -545,9 +552,16 @@ void PageView::contentsMouseMoveEvent( QMouseEvent * e )
void PageView::contentsMousePressEvent( QMouseEvent * e )
{
bool leftButton = e->button() & LeftButton;
// if pressing mid mouse button while not doing other things, begin 'comtinous zoom' mode
if ( (e->button() & MidButton) && d->mouseSelectionRect.isNull() )
{
d->mouseMidStartY = e->globalPos().y();
setCursor( sizeVerCursor );
return;
}
// handle mode dependant mouse press actions
bool leftButton = e->button() & LeftButton;
switch ( d->mouseMode )
{
case MouseNormal: // drag start / click / link following
......@@ -563,21 +577,11 @@ void PageView::contentsMousePressEvent( QMouseEvent * e )
break;
case MouseZoom:
case MouseSelGfx:
case MouseSelect: // set first corner of the selection rect
if ( leftButton )
selectionStart( e->x(), e->y(), false );
break;
case MouseSelText: // set first corner of the selection rect
if ( leftButton )
{
PageViewItem * item = pickItemOnPoint( e->x(), e->y() );
if ( item )
selectionStart( e->x(), e->y(), false, item );
}
break;
case MouseEdit: // ? place the beginning of [tool] ?
break;
}
......@@ -585,14 +589,21 @@ void PageView::contentsMousePressEvent( QMouseEvent * e )
void PageView::contentsMouseReleaseEvent( QMouseEvent * e )
{
// handle mode indepent mid buttom zoom
if ( (e->state() & MidButton) && d->mouseMidStartY > 0 )
{
d->mouseMidStartY = -1;
setCursor( arrowCursor );
return;
}
bool leftButton = e->button() & LeftButton,
rightButton = e->button() & RightButton;
PageViewItem * pageItem = pickItemOnPoint( e->x(), e->y() );
switch ( d->mouseMode )
{
case MouseNormal:
case MouseNormal:{ // do Follow Link or Display RMB
setCursor( arrowCursor );
PageViewItem * pageItem = pickItemOnPoint( e->x(), e->y() );
if ( leftButton && pageItem )
{
if ( d->mouseOnLink )
......@@ -654,10 +665,9 @@ void PageView::contentsMouseReleaseEvent( QMouseEvent * e )
}
// reset start position
d->mouseStartPos = QPoint();
break;
}break;
case MouseZoom:
// handle 'Zoom To Area', in every mouse mode
case MouseZoom: // do ZOOM
if ( leftButton && !d->mouseSelectionRect.isNull() )
{
QRect selRect = d->mouseSelectionRect.normalize();
......@@ -694,88 +704,91 @@ void PageView::contentsMouseReleaseEvent( QMouseEvent * e )
}
break;
case MouseSelText:
if ( leftButton && !d->mouseSelectionRect.isNull() )
{
QRect relativeRect = d->mouseSelectionRect.normalize();
if ( relativeRect.width() < 2 || relativeRect.height() < 2 )
break;
// request the textpage if there isn't one
const KPDFPage * kpdfPage = d->mouseSelectionItem->page();
if ( !kpdfPage->hasSearchPage() )
d->document->requestTextPage( kpdfPage->number() );
case MouseSelect:{ // do SELECT
if ( !leftButton || d->mouseSelectionRect.isNull() )
break;
// copy text into the clipboard
QClipboard *cb = QApplication::clipboard();
relativeRect.moveBy( -d->mouseSelectionItem->geometry().left(),
-d->mouseSelectionItem->geometry().top() );
const QString & selection = kpdfPage->getTextInRect( relativeRect, d->mouseSelectionItem->zoomFactor() );
cb->setText( selection, QClipboard::Clipboard );
if ( cb->supportsSelection() )
cb->setText( selection, QClipboard::Selection );
// clear widget selection and invalidate rect
selectionClear();
QRect selectionRect = d->mouseSelectionRect.normalize();
if ( selectionRect.width() < 5 || selectionRect.height() < 5 )
break;
// user friendly message
if ( selection.length() < 1 )
d->messageWindow->display( i18n( "No characters copied to clipboard." ), PageViewMessage::Error );
else
d->messageWindow->display( i18n( "%1 characters copied to clipboard." ).arg( selection.length() ) );
// grab text in selection by extracting it from all intersected pages
QString selectedText;
QValueVector< PageViewItem * >::iterator iIt = d->items.begin(), iEnd = d->items.end();
for ( ; iIt != iEnd; ++iIt )
{
PageViewItem * item = *iIt;
const QRect & itemRect = item->geometry();
if ( selectionRect.intersects( itemRect ) )
{
// request the textpage if there isn't one
const KPDFPage * kpdfPage = item->page();
if ( !kpdfPage->hasSearchPage() )
d->document->requestTextPage( kpdfPage->number() );
// grab text
QRect relativeRect = selectionRect.intersect( itemRect );
relativeRect.moveBy( -itemRect.left(), -itemRect.top() );
selectedText += kpdfPage->getTextInRect( relativeRect, item->zoomFactor() );
}
}
break;
case MouseSelGfx:
if ( leftButton && !d->mouseSelectionRect.isNull() )
// popup that ask to copy:text and copy/save:image
KPopupMenu menu( this );
if ( !selectedText.isEmpty() )
{
menu.insertTitle( i18n( "Text ( %1 characters )" ).arg( selectedText.length() ) );
menu.insertItem( SmallIcon("editcopy"), i18n( "Copy to Clipboard" ), 1 );
}
menu.insertTitle( i18n( "Image ( %1 by %2 pixels )" ).arg( selectionRect.width() ).arg( selectionRect.height() ) );
menu.insertItem( SmallIcon("image"), i18n( "Copy to Clipboard" ), 2 );
menu.insertItem( SmallIcon("filesave"), i18n( "Save to File ..." ), 3 );
int choice = menu.exec( e->globalPos() );
// IMAGE operation choosen
if ( choice > 1 )
{
QRect relativeRect = d->mouseSelectionRect.normalize();
if ( relativeRect.width() > 4 && relativeRect.height() > 4 )
// renders page into a pixmap
QPixmap copyPix( selectionRect.width(), selectionRect.height() );
QPainter copyPainter( &copyPix );
copyPainter.translate( -selectionRect.left(), -selectionRect.top() );
paintItems( &copyPainter, selectionRect );
if ( choice == 2 )
{
// grab rendered page into the pixmap
QPixmap copyPix( relativeRect.width(), relativeRect.height() );
QPainter copyPainter( &copyPix );
copyPainter.translate( -relativeRect.left(), -relativeRect.top() );
paintItems( &copyPainter, relativeRect );
// popup that ask to copy or save image
KPopupMenu * m_popup = new KPopupMenu( this, "rmb popup" );
m_popup->insertTitle( i18n( "Copy Image [%1x%2]" ).arg( copyPix.width() ).arg( copyPix.height() ) );
m_popup->insertItem( SmallIcon("editcopy"), i18n("Copy to Clipboard"), 1 );
m_popup->insertItem( SmallIcon("filesave"), i18n("Save to File ..."), 2 );
switch ( m_popup->exec(e->globalPos()) )
// [2] copy pixmap to clipboard
QClipboard *cb = QApplication::clipboard();
cb->setPixmap( copyPix, QClipboard::Clipboard );
if ( cb->supportsSelection() )
cb->setPixmap( copyPix, QClipboard::Selection );
d->messageWindow->display( i18n( "Image [%1x%2] copied to clipboard." ).arg( copyPix.width() ).arg( copyPix.height() ) );
}
else
{
// [3] save pixmap to file
QString fileName = KFileDialog::getSaveFileName( QString::null, "image/png image/jpeg", this );
if ( fileName.isNull() )
d->messageWindow->display( i18n( "File not saved." ), PageViewMessage::Warning );
else
{
case 1:{
// save pixmap to clipboard
QClipboard *cb = QApplication::clipboard();
cb->setPixmap( copyPix, QClipboard::Clipboard );
if ( cb->supportsSelection() )
cb->setPixmap( copyPix, QClipboard::Selection );
d->messageWindow->display( i18n( "Image [%1x%2] copied to clipboard." ).arg( copyPix.width() ).arg( copyPix.height() ) );
}break;
case 2:
// save pixmap to file
QString fileName = KFileDialog::getSaveFileName( QString::null, "image/png image/jpeg", this );
if ( !fileName.isNull() )
{
QString type( KImageIO::type( fileName ) );
if ( type.isNull() )
type = "PNG";
copyPix.save( fileName, type.latin1() );
d->messageWindow->display( i18n( "Image [%1x%2] saved to %3 file." ).arg( copyPix.width() ).arg( copyPix.height() ).arg( type ) );
}
else
d->messageWindow->display( i18n( "File not saved." ), PageViewMessage::Warning );
break;
QString type( KImageIO::type( fileName ) );
if ( type.isNull() )
type = "PNG";
copyPix.save( fileName, type.latin1() );
d->messageWindow->display( i18n( "Image [%1x%2] saved to %3 file." ).arg( copyPix.width() ).arg( copyPix.height() ).arg( type ) );
}
delete m_popup;
}
// clear widget selection and invalidate rect
selectionClear();
}
break;
// TEXT operation choosen
else if ( choice == 1 )
{
QClipboard *cb = QApplication::clipboard();
cb->setText( selectedText, QClipboard::Clipboard );
if ( cb->supportsSelection() )
cb->setText( selectedText, QClipboard::Selection );
}
// clear widget selection and invalidate rect
selectionClear();
}break;
case MouseEdit: // ? apply [tool] ?
break;
......@@ -947,10 +960,8 @@ PageViewItem * PageView::pickItemOnPoint( int x, int y )
return item;
}
void PageView::selectionStart( int x, int y, bool /*aboveAll*/, PageViewItem * pageLock)
void PageView::selectionStart( int x, int y, bool /*aboveAll*/ )
{
// pick current page as the active one
d->mouseSelectionItem = pageLock;
d->mouseSelectionRect.setRect( x, y, 1, 1 );
// ensures page doesn't scroll
if ( d->scrollTimer )
......@@ -962,13 +973,10 @@ void PageView::selectionStart( int x, int y, bool /*aboveAll*/, PageViewItem * p
void PageView::selectionEndPoint( int x, int y )
{
// clip selection to the current page (if set)
if ( d->mouseSelectionItem )
{
const QRect & itemRect = d->mouseSelectionItem->geometry();
x = QMAX( QMIN( x, itemRect.right() ), itemRect.left() ),
y = QMAX( QMIN( y, itemRect.bottom() ), itemRect.top() );
}
// clip selection to the viewport
QRect viewportRect( contentsX(), contentsY(), visibleWidth(), visibleHeight() );
x = QMAX( QMIN( x, viewportRect.right() ), viewportRect.left() ),
y = QMAX( QMIN( y, viewportRect.bottom() ), viewportRect.top() );
// if selection changed update rect
if ( d->mouseSelectionRect.right() != x || d->mouseSelectionRect.bottom() != y )
{
......@@ -997,7 +1005,6 @@ void PageView::selectionClear()
{
updateContents( d->mouseSelectionRect.normalize() );
d->mouseSelectionRect.setCoords( 0, 0, -1, -1 );
d->mouseSelectionItem = 0;
}
void PageView::updateZoom( ZoomMode newZoomMode )
......@@ -1132,9 +1139,9 @@ void PageView::slotRelayoutPages()
// look for the item closest to viewport center and the relative position
// between the item and the viewport center (for viewport restoring at end)
PageViewItem * focusedPage = 0;
float focusedX = 0.5,
focusedY = 0.0,
minDistance = -1.0;
double focusedX = 0.5,
focusedY = 0.0,
minDistance = -1.0;
// find the page nearest to viewport center
for ( iIt = d->items.begin(); iIt != iEnd; ++iIt )
{
......@@ -1142,7 +1149,7 @@ void PageView::slotRelayoutPages()
if ( geometry.intersects( viewportRect ) )
{
// compute distance between item center and viewport center
float distance = hypotf( geometry.left() + geometry.width() / 2 - viewportCenterX,
double distance = hypot( geometry.left() + geometry.width() / 2 - viewportCenterX,
geometry.top() + geometry.height() / 2 - viewportCenterY );
if ( distance >= minDistance && minDistance != -1.0 )
continue;
......@@ -1150,8 +1157,8 @@ void PageView::slotRelayoutPages()
minDistance = distance;
if ( geometry.height() > 0 && geometry.width() > 0 )
{
focusedX = ( viewportCenterX - geometry.left() ) / (float)geometry.width();
focusedY = ( viewportCenterY - geometry.top() ) / (float)geometry.height();
focusedX = (double)( viewportCenterX - geometry.left() ) / (double)geometry.width();
focusedY = (double)( viewportCenterY - geometry.top() ) / (double)geometry.height();
}
}
}
......@@ -1288,8 +1295,8 @@ void PageView::slotRelayoutPages()
if ( focusedPage )
{
const QRect & geometry = focusedPage->geometry();
center( geometry.left() + (int)( focusedX * (float)geometry.width() ),
geometry.top() + (int)( focusedY * (float)geometry.height() ) );
center( geometry.left() + (int) round( focusedX * (double)geometry.width() ),
geometry.top() + (int) round( focusedY * (double)geometry.height() ) );
}
else
center( fullWidth / 2, 0 );
......@@ -1405,19 +1412,13 @@ void PageView::slotSetMouseNormal()
void PageView::slotSetMouseZoom()
{
d->mouseMode = MouseZoom;
d->messageWindow->display( i18n( "Select Zooming Area. Right-Click to zoom out." ), PageViewMessage::Info );
}
void PageView::slotSetMouseSelText()
{
d->mouseMode = MouseSelText;
d->messageWindow->display( i18n( "Draw a rectangle around the text to copy." ), PageViewMessage::Info, 2000 );
d->messageWindow->display( i18n( "Select Zooming Area. Right-Click to zoom out." ), PageViewMessage::Info, -1 );
}
void PageView::slotSetMouseSelGfx()
void PageView::slotSetMouseSelect()
{
d->mouseMode = MouseSelGfx;
d->messageWindow->display( i18n( "Draw a rectangle around the graphics to copy." ), PageViewMessage::Info, 2000 );
d->mouseMode = MouseSelect;
d->messageWindow->display( i18n( "Draw a rectangle around the text/gfx to copy." ), PageViewMessage::Info, -1 );
}
void PageView::slotSetMouseDraw()
......
......@@ -46,7 +46,7 @@ class PageView : public QScrollView, public KPDFDocumentObserver
// Zoom mode ( last 4 are internally used only! )
enum ZoomMode { ZoomFixed, ZoomFitWidth, ZoomFitPage, ZoomFitText,
ZoomIn, ZoomOut, ZoomRefreshCurrent };
enum MouseMode { MouseNormal, MouseZoom, MouseSelText, MouseSelGfx, MouseEdit };
enum MouseMode { MouseNormal, MouseZoom, MouseSelect, MouseEdit };
// create actions that interact with this widget
void setupActions( KActionCollection * collection );
......@@ -86,7 +86,7 @@ class PageView : public QScrollView, public KPDFDocumentObserver
// return the widget placed on a certain point or 0 if clicking on empty space
PageViewItem * pickItemOnPoint( int x, int y );
// start / modify / clear selection rectangle
void selectionStart( int x, int y, bool aboveAll = false, PageViewItem * pageLock = 0 );
void selectionStart( int x, int y, bool aboveAll = false );
void selectionEndPoint( int x, int y );
void selectionClear();
// update internal zoom values and end in a slotRelayoutPages();
......@@ -116,8 +116,7 @@ class PageView : public QScrollView, public KPDFDocumentObserver
void slotContinousToggled( bool );
void slotSetMouseNormal();
void slotSetMouseZoom();
void slotSetMouseSelText();
void slotSetMouseSelGfx();
void slotSetMouseSelect();
void slotSetMouseDraw();
void slotScrollUp();
void slotScrollDown();
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment