compile
[kdegraphics.git] / okular / ui / pageviewannotator.cpp
blob39fb9f8746fc69a60feefa1debedde3a031b3db4
1 /***************************************************************************
2 * Copyright (C) 2005 by Enrico Ros <eros.kde@email.it> *
3 * *
4 * This program is free software; you can redistribute it and/or modify *
5 * it under the terms of the GNU General Public License as published by *
6 * the Free Software Foundation; either version 2 of the License, or *
7 * (at your option) any later version. *
8 ***************************************************************************/
10 #include "pageviewannotator.h"
12 // qt / kde includes
13 #include <qapplication.h>
14 #include <qfile.h>
15 #include <qcolor.h>
16 #include <qevent.h>
17 #include <qlist.h>
18 #include <qpainter.h>
19 #include <qset.h>
20 #include <qvariant.h>
21 #include <kiconloader.h>
22 #include <klocale.h>
23 #include <kstandarddirs.h>
24 #include <kinputdialog.h>
25 #include <kuser.h>
26 #include <kdebug.h>
27 #include <kmenu.h>
29 // system includes
30 #include <math.h>
32 // local includes
33 #include "core/area.h"
34 #include "core/document.h"
35 #include "core/page.h"
36 #include "core/annotations.h"
37 #include "settings.h"
38 #include "annotationtools.h"
39 #include "pageview.h"
41 /** @short PickPointEngine */
42 class PickPointEngine : public AnnotatorEngine
44 public:
45 PickPointEngine( const QDomElement & engineElement )
46 : AnnotatorEngine( engineElement ), clicked( false ), pixmap( 0 ),
47 xscale( 1.0 ), yscale( 1.0 )
49 // parse engine specific attributes
50 pixmapName = engineElement.attribute( "hoverIcon" );
51 QString stampname = m_annotElement.attribute( "icon" );
52 if ( m_annotElement.attribute( "type" ) == "Stamp" && !stampname.simplified().isEmpty() )
53 pixmapName = stampname;
54 center = QVariant( engineElement.attribute( "center" ) ).toBool();
55 bool ok = true;
56 size = engineElement.attribute( "size", "32" ).toInt( &ok );
57 if ( !ok )
58 size = 32;
59 m_block = QVariant( engineElement.attribute( "block" ) ).toBool();
61 // create engine objects
62 if ( !pixmapName.simplified().isEmpty() )
63 pixmap = new QPixmap( DesktopIcon( pixmapName, size ) );
66 ~PickPointEngine()
68 delete pixmap;
71 QRect event( EventType type, Button button, double nX, double nY, double xScale, double yScale, const Okular::Page * page )
73 xscale=xScale;
74 yscale=yScale;
75 pagewidth = page->width();
76 pageheight = page->height();
77 // only proceed if pressing left button
78 if ( button != Left )
79 return QRect();
81 // start operation on click
82 if ( type == Press && clicked == false )
84 clicked = true;
85 startpoint.x=nX;
86 startpoint.y=nY;
88 // repaint if moving while pressing
89 else if ( type == Move && clicked == true )
92 // operation finished on release
93 else if ( type == Release && clicked == true )
95 m_creationCompleted = true;
97 else
98 return QRect();
100 // update variables and extents (zoom invariant rect)
101 point.x = nX;
102 point.y = nY;
103 if ( center && pixmap )
105 rect.left = nX - ( pixmap->width() / ( xScale * 2.0 ) );
106 rect.top = nY - ( pixmap->height() / ( yScale * 2.0 ) );
108 else
110 rect.left = nX;
111 rect.top = nY;
113 rect.right = rect.left + ( pixmap ? pixmap->width() / xScale : 0 );
114 rect.bottom = rect.top + ( pixmap ? pixmap->height() / yScale : 0 );
115 QRect boundrect = rect.geometry( (int)xScale, (int)yScale ).adjusted( 0, 0, 1, 1 );
116 if ( m_block )
118 Okular::NormalizedRect tmprect( qMin( startpoint.x, point.x ), qMin( startpoint.y, point.y ), qMax( startpoint.x, point.x ), qMax( startpoint.y, point.y ) );
119 boundrect |= tmprect.geometry( (int)xScale, (int)yScale ).adjusted( 0, 0, 1, 1 );
121 return boundrect;
124 void paint( QPainter * painter, double xScale, double yScale, const QRect & /*clipRect*/ )
126 if ( clicked )
128 if ( m_block )
130 QPen origpen = painter->pen();
131 QPen pen = painter->pen();
132 pen.setStyle( Qt::DashLine );
133 painter->setPen( pen );
134 Okular::NormalizedRect tmprect( qMin( startpoint.x, point.x ), qMin( startpoint.y, point.y ), qMax( startpoint.x, point.x ), qMax( startpoint.y, point.y ) );
135 QRect realrect = tmprect.geometry( (int)xScale, (int)yScale );
136 painter->drawRect( realrect );
137 painter->setPen( origpen );
139 if ( pixmap )
140 painter->drawPixmap( QPointF( rect.left * xScale, rect.top * yScale ), *pixmap );
144 QList< Okular::Annotation* > end()
146 m_creationCompleted = false;
148 // find out annotation's description node
149 if ( m_annotElement.isNull() )
150 return QList< Okular::Annotation* >();
152 // find out annotation's type
153 Okular::Annotation * ann = 0;
154 QString typeString = m_annotElement.attribute( "type" );
155 // create TextAnnotation from path
156 if ( typeString == "FreeText") //<annotation type="Text"
158 //note dialog
159 QString prompt = i18n( "Text of the new note:" );
160 bool resok;
161 QString note = KInputDialog::getMultiLineText( i18n( "New Text Note" ), prompt, QString(), &resok );
162 if(resok)
164 //add note
165 Okular::TextAnnotation * ta = new Okular::TextAnnotation();
166 ann = ta;
167 ta->setInplaceText( note );
168 ta->setTextType( Okular::TextAnnotation::InPlace );
169 //set boundary
170 rect.left = qMin(startpoint.x,point.x);
171 rect.top = qMin(startpoint.y,point.y);
172 rect.right = qMax(startpoint.x,point.x);
173 rect.bottom = qMax(startpoint.y,point.y);
174 kDebug().nospace() << "xyScale=" << xscale << "," << yscale;
175 static int padding = 2;
176 QFontMetricsF mf(ta->textFont());
177 QRectF rcf = mf.boundingRect( Okular::NormalizedRect( rect.left, rect.top, 1.0, 1.0 ).geometry( (int)pagewidth, (int)pageheight ).adjusted( padding, padding, -padding, -padding ),
178 Qt::AlignTop | Qt::AlignLeft | Qt::TextWordWrap, ta->inplaceText() );
179 rect.right = qMax(rect.right, rect.left+(rcf.width()+padding*2)/pagewidth);
180 rect.bottom = qMax(rect.bottom, rect.top+(rcf.height()+padding*2)/pageheight);
181 ta->setBoundingRectangle( this->rect );
182 ta->window().setSummary( "TextBox" );
185 else if ( typeString == "Text")
187 Okular::TextAnnotation * ta = new Okular::TextAnnotation();
188 ann = ta;
189 ta->setTextType( Okular::TextAnnotation::Linked );
190 ta->window().setText( QString() );
191 //ta->window.flags &= ~(Okular::Annotation::Hidden);
192 double iconhei=0.03;
193 rect.left = point.x;
194 rect.top = point.y;
195 rect.right=rect.left+iconhei;
196 rect.bottom=rect.top+iconhei*xscale/yscale;
197 ta->setBoundingRectangle( this->rect );
198 ta->window().setSummary( "Note" );
200 // create StampAnnotation from path
201 else if ( typeString == "Stamp" )
203 Okular::StampAnnotation * sa = new Okular::StampAnnotation();
204 ann = sa;
205 sa->setStampIconName( pixmapName );
206 // set boundary
207 rect.left = qMin( startpoint.x, point.x );
208 rect.top = qMin( startpoint.y, point.y );
209 rect.right = qMax( startpoint.x, point.x );
210 rect.bottom = qMax( startpoint.y, point.y );
211 QRectF rcf = rect.geometry( (int)xscale, (int)yscale );
212 const int ml = ( rcf.bottomRight() - rcf.topLeft() ).toPoint().manhattanLength();
213 if ( ml <= QApplication::startDragDistance() )
215 double stampxscale = size / xscale;
216 double stampyscale = size / yscale;
217 if ( center )
219 rect.left = point.x - stampxscale / 2;
220 rect.top = point.y - stampyscale / 2;
222 else
224 rect.left = point.x;
225 rect.top = point.y;
227 rect.right = rect.left + stampxscale;
228 rect.bottom = rect.top + stampyscale;
230 sa->setBoundingRectangle( rect );
232 // create GeomAnnotation
233 else if ( typeString == "GeomSquare" || typeString == "GeomCircle" )
235 Okular::GeomAnnotation * ga = new Okular::GeomAnnotation();
236 ann = ga;
237 // set the type
238 if ( typeString == "GeomSquare" )
239 ga->setGeometricalType( Okular::GeomAnnotation::InscribedSquare );
240 else
241 ga->setGeometricalType( Okular::GeomAnnotation::InscribedCircle );
242 ga->style().setWidth( 5 );
243 //set boundary
244 rect.left = qMin( startpoint.x, point.x );
245 rect.top = qMin( startpoint.y, point.y );
246 rect.right = qMax( startpoint.x, point.x );
247 rect.bottom = qMax( startpoint.y, point.y );
248 ga->setBoundingRectangle( rect );
251 // safety check
252 if ( !ann )
253 return QList< Okular::Annotation* >();
255 // set common attributes
256 ann->style().setColor( m_annotElement.hasAttribute( "color" ) ?
257 m_annotElement.attribute( "color" ) : m_engineColor );
258 if ( m_annotElement.hasAttribute( "opacity" ) )
259 ann->style().setOpacity( m_annotElement.attribute( "opacity", "1.0" ).toDouble() );
261 // return annotation
262 return QList< Okular::Annotation* >() << ann;
265 private:
266 bool clicked;
267 Okular::NormalizedRect rect;
268 Okular::NormalizedPoint startpoint;
269 Okular::NormalizedPoint point;
270 QPixmap * pixmap;
271 QString pixmapName;
272 int size;
273 double xscale,yscale;
274 double pagewidth, pageheight;
275 bool center;
276 bool m_block;
279 /** @short PolyLineEngine */
280 class PolyLineEngine : public AnnotatorEngine
282 public:
283 PolyLineEngine( const QDomElement & engineElement )
284 : AnnotatorEngine( engineElement ), last( false )
286 // parse engine specific attributes
287 m_block = engineElement.attribute( "block" ) == "true";
288 bool ok = true;
289 // numofpoints represents the max number of points for the current
290 // polygon/polyline, with a pair of exceptions:
291 // -1 means: the polyline must close on the first point (polygon)
292 // 0 means: construct as many points as you want, right-click
293 // to construct the last point
294 numofpoints = engineElement.attribute( "points" ).toInt( &ok );
295 if ( !ok )
296 numofpoints = -1;
299 QRect event( EventType type, Button button, double nX, double nY, double xScale, double yScale, const Okular::Page * /*page*/ )
301 // only proceed if pressing left button
302 // if ( button != Left )
303 // return rect;
305 // start operation
306 if ( type == Press )
308 newPoint.x = nX;
309 newPoint.y = nY;
310 if ( button == Right )
311 last = true;
313 // move the second point
314 else if ( type == Move )
316 movingpoint.x = nX;
317 movingpoint.y = nY;
318 QRect oldmovingrect = movingrect;
319 movingrect = rect | QRect( (int)( movingpoint.x * xScale ), (int)( movingpoint.y * yScale ), 1, 1 );
320 return oldmovingrect | movingrect;
322 else if ( type == Release )
324 Okular::NormalizedPoint tmppoint;
325 tmppoint.x = nX;
326 tmppoint.y = nY;
327 if ( fabs( tmppoint.x - newPoint.x + tmppoint.y - newPoint.y ) > 1e-2 )
328 return rect;
330 if ( numofpoints == -1 && points.count() > 1 && ( fabs( points[0].x - newPoint.x + points[0].y - newPoint.y ) < 1e-2 ) )
332 last = true;
334 else
336 points.append( newPoint );
337 rect |= QRect( (int)( newPoint.x * xScale ), (int)( newPoint.y * yScale ), 1, 1 );
339 // end creation if we have constructed the last point of enough points
340 if ( last || points.count() == numofpoints )
342 m_creationCompleted = true;
343 last = false;
344 normRect = Okular::NormalizedRect( rect, xScale, yScale );
348 return rect;
351 void paint( QPainter * painter, double xScale, double yScale, const QRect & /*clipRect*/ )
353 if ( points.count() < 1 )
354 return;
356 if ( m_block && points.count() == 2 )
358 Okular::NormalizedPoint first = points[0];
359 Okular::NormalizedPoint second = points[1];
360 // draw a semitransparent block around the 2 points
361 painter->setPen( m_engineColor );
362 painter->setBrush( QBrush( m_engineColor.light(), Qt::Dense4Pattern ) );
363 painter->drawRect( (int)(first.x * (double)xScale), (int)(first.y * (double)yScale),
364 (int)((second.x - first.x) * (double)xScale), (int)((second.y - first.y) * (double)yScale) );
366 else
368 // draw a polyline that connects the constructed points
369 painter->setPen( QPen( m_engineColor, 2 ) );
370 for ( int i = 1; i < points.count(); ++i )
371 painter->drawLine( (int)(points[i - 1].x * (double)xScale), (int)(points[i - 1].y * (double)yScale),
372 (int)(points[i].x * (double)xScale), (int)(points[i].y * (double)yScale) );
373 painter->drawLine( (int)(points.last().x * (double)xScale), (int)(points.last().y * (double)yScale),
374 (int)(movingpoint.x * (double)xScale), (int)(movingpoint.y * (double)yScale) );
378 QList< Okular::Annotation* > end()
380 m_creationCompleted = false;
382 // find out annotation's description node
383 if ( m_annotElement.isNull() )
384 return QList< Okular::Annotation* >();
386 // find out annotation's type
387 Okular::Annotation * ann = 0;
388 QString typeString = m_annotElement.attribute( "type" );
390 // create LineAnnotation from path
391 if ( typeString == "Line" || typeString == "Polyline" || typeString == "Polygon" )
393 if ( points.count() < 2 )
394 return QList< Okular::Annotation* >();
396 //add note
397 Okular::LineAnnotation * la = new Okular::LineAnnotation();
398 ann = la;
399 QLinkedList<Okular::NormalizedPoint> list;
400 for ( int i = 0; i < points.count(); ++i )
401 list.append( points[ i ] );
403 la->setLinePoints( list );
405 if ( numofpoints == -1 )
406 la->setLineClosed( true );
408 la->setBoundingRectangle( normRect );
412 // safety check
413 if ( !ann )
414 return QList< Okular::Annotation* >();
416 // set common attributes
417 ann->style().setColor( m_annotElement.hasAttribute( "color" ) ?
418 m_annotElement.attribute( "color" ) : m_engineColor );
419 if ( m_annotElement.hasAttribute( "opacity" ) )
420 ann->style().setOpacity( m_annotElement.attribute( "opacity", "1.0" ).toDouble() );
421 // return annotation
423 return QList< Okular::Annotation* >() << ann;
426 private:
427 QList<Okular::NormalizedPoint> points;
428 Okular::NormalizedPoint newPoint;
429 Okular::NormalizedPoint movingpoint;
430 QRect rect;
431 QRect movingrect;
432 Okular::NormalizedRect normRect;
433 bool m_block;
434 bool last;
435 int numofpoints;
438 /** @short TextSelectorEngine */
439 class TextSelectorEngine : public AnnotatorEngine
441 public:
442 TextSelectorEngine( const QDomElement & engineElement, PageView * pageView )
443 : AnnotatorEngine( engineElement ), m_pageView( pageView ),
444 selection( 0 )
446 // parse engine specific attributes
449 ~TextSelectorEngine()
451 delete selection;
454 QRect event( EventType type, Button button, double nX, double nY, double xScale, double yScale, const Okular::Page * /*page*/ )
456 // only proceed if pressing left button
457 if ( button != Left )
458 return QRect();
460 if ( type == Press )
462 lastPoint.x = nX;
463 lastPoint.y = nY;
464 QRect oldrect = rect;
465 rect = QRect();
466 return oldrect;
468 else if ( type == Move )
470 if ( item() )
472 QPoint start( (int)( lastPoint.x * item()->uncroppedWidth() ), (int)( lastPoint.y * item()->uncroppedHeight() ) );
473 QPoint end( (int)( nX * item()->uncroppedWidth() ), (int)( nY * item()->uncroppedHeight() ) );
474 delete selection;
475 selection = 0;
476 Okular::RegularAreaRect * newselection = m_pageView->textSelectionForItem( item(), start, end );
477 if ( !newselection->isEmpty() )
479 QList<QRect> geom = newselection->geometry( (int)xScale, (int)yScale );
480 QRect newrect;
481 Q_FOREACH ( const QRect& r, geom )
483 if ( newrect.isNull() )
484 newrect = r;
485 else
486 newrect |= r;
488 rect |= newrect;
489 selection = newselection;
491 else
493 delete newselection;
497 else if ( type == Release && selection )
499 m_creationCompleted = true;
501 return rect;
504 void paint( QPainter * painter, double xScale, double yScale, const QRect & /*clipRect*/ )
506 if ( selection )
508 painter->setPen( Qt::NoPen );
509 QColor col = m_engineColor;
510 col.setAlphaF( 0.5 );
511 painter->setBrush( col );
512 foreach( const Okular::NormalizedRect & r, *selection )
514 painter->drawRect( r.geometry( (int)xScale, (int)yScale ) );
519 QList< Okular::Annotation* > end()
521 m_creationCompleted = false;
523 // safety checks
524 if ( m_annotElement.isNull() || !selection )
525 return QList< Okular::Annotation* >();
527 // find out annotation's type
528 Okular::Annotation * ann = 0;
529 QString typeString = m_annotElement.attribute( "type" );
531 Okular::HighlightAnnotation::HighlightType type = Okular::HighlightAnnotation::Highlight;
532 bool typevalid = false;
533 // create HighlightAnnotation's from the selected area
534 if ( typeString == "Highlight" )
536 type = Okular::HighlightAnnotation::Highlight;
537 typevalid = true;
539 else if ( typeString == "Squiggly" )
541 type = Okular::HighlightAnnotation::Squiggly;
542 typevalid = true;
544 else if ( typeString == "Underline" )
546 type = Okular::HighlightAnnotation::Underline;
547 typevalid = true;
549 else if ( typeString == "StrikeOut" )
551 type = Okular::HighlightAnnotation::StrikeOut;
552 typevalid = true;
554 if ( typevalid )
556 Okular::HighlightAnnotation * ha = new Okular::HighlightAnnotation();
557 ha->setHighlightType( type );
558 ha->setBoundingRectangle( Okular::NormalizedRect( rect, item()->uncroppedWidth(), item()->uncroppedHeight() ) );
559 foreach ( const Okular::NormalizedRect & r, *selection )
561 Okular::HighlightAnnotation::Quad q;
562 q.setCapStart( false );
563 q.setCapEnd( false );
564 q.setFeather( 1.0 );
565 q.setPoint( Okular::NormalizedPoint( r.left, r.bottom ), 0 );
566 q.setPoint( Okular::NormalizedPoint( r.right, r.bottom ), 1 );
567 q.setPoint( Okular::NormalizedPoint( r.right, r.top ), 2 );
568 q.setPoint( Okular::NormalizedPoint( r.left, r.top ), 3 );
569 ha->highlightQuads().append( q );
571 ann = ha;
574 delete selection;
575 selection = 0;
577 // safety check
578 if ( !ann )
579 return QList< Okular::Annotation* >();
581 // set common attributes
582 ann->style().setColor( m_annotElement.hasAttribute( "color" ) ?
583 m_annotElement.attribute( "color" ) : m_engineColor );
584 if ( m_annotElement.hasAttribute( "opacity" ) )
585 ann->style().setOpacity( m_annotElement.attribute( "opacity", "1.0" ).toDouble() );
587 // return annotations
588 return QList< Okular::Annotation* >() << ann;
591 private:
592 // data
593 PageView * m_pageView;
594 // TODO: support more pages
595 Okular::RegularAreaRect * selection;
596 Okular::NormalizedPoint lastPoint;
597 QRect rect;
601 /** PageViewAnnotator **/
603 PageViewAnnotator::PageViewAnnotator( PageView * parent, Okular::Document * storage )
604 : QObject( parent ), m_document( storage ), m_pageView( parent ),
605 m_toolBar( 0 ), m_engine( 0 ), m_textToolsEnabled( false ), m_toolsEnabled( false ),
606 m_lastToolID( -1 ), m_lockedItem( 0 )
608 // load the tools from the 'xml tools definition' file. store the tree internally.
609 QFile infoFile( KStandardDirs::locate("data", "okular/tools.xml") );
610 if ( infoFile.exists() && infoFile.open( QIODevice::ReadOnly ) )
612 QDomDocument doc( "annotatingTools" );
613 if ( doc.setContent( &infoFile ) )
615 m_toolsDefinition = doc.elementsByTagName("annotatingTools").item( 0 ).toElement();
617 // create the AnnotationToolItems from the XML dom tree
618 QDomNode toolDescription = m_toolsDefinition.firstChild();
619 while ( toolDescription.isElement() )
621 QDomElement toolElement = toolDescription.toElement();
622 if ( toolElement.tagName() == "tool" )
624 AnnotationToolItem item;
625 item.id = toolElement.attribute("id").toInt();
626 item.text = i18n( toolElement.attribute( "name" ).toUtf8() );
627 item.pixmap = toolElement.attribute("pixmap");
628 QDomNode shortcutNode = toolElement.elementsByTagName( "shortcut" ).item( 0 );
629 if ( shortcutNode.isElement() )
630 item.shortcut = shortcutNode.toElement().text();
631 QDomNodeList engineNodeList = toolElement.elementsByTagName( "engine" );
632 if ( engineNodeList.size() > 0 )
634 QDomElement engineEl = engineNodeList.item( 0 ).toElement();
635 if ( !engineEl.isNull() && engineEl.hasAttribute( "type" ) )
636 item.isText = engineEl.attribute( "type" ) == QLatin1String( "TextSelector" );
638 m_items.push_back( item );
640 toolDescription = toolDescription.nextSibling();
643 else
644 kWarning() << "AnnotatingTools XML file seems to be damaged";
645 infoFile.close();
647 else
648 kWarning() << "Unable to open AnnotatingTools XML definition";
651 PageViewAnnotator::~PageViewAnnotator()
653 delete m_engine;
656 void PageViewAnnotator::setEnabled( bool on )
658 if ( !on )
660 // remove toolBar
661 if ( m_toolBar )
662 m_toolBar->hideAndDestroy();
663 m_toolBar = 0;
664 // deactivate the active tool, if any
665 slotToolSelected( -1 );
666 return;
669 // if no tools are defined, don't show the toolbar
670 if ( !m_toolsDefinition.hasChildNodes() )
671 return;
673 // create toolBar
674 if ( !m_toolBar )
676 m_toolBar = new PageViewToolBar( m_pageView, m_pageView->viewport() );
677 m_toolBar->setSide( (PageViewToolBar::Side)Okular::Settings::editToolBarPlacement() );
678 m_toolBar->setItems( m_items );
679 m_toolBar->setToolsEnabled( m_toolsEnabled );
680 m_toolBar->setTextToolsEnabled( m_textToolsEnabled );
681 connect( m_toolBar, SIGNAL( toolSelected(int) ),
682 this, SLOT( slotToolSelected(int) ) );
683 connect( m_toolBar, SIGNAL( orientationChanged(int) ),
684 this, SLOT( slotSaveToolbarOrientation(int) ) );
687 // show the toolBar
688 m_toolBar->showAndAnimate();
690 // ask for Author's name if not already set
691 if ( Okular::Settings::identityAuthor().isEmpty() )
693 // get default username from the kdelibs/kdecore/KUser
694 KUser currentUser;
695 QString userName = currentUser.property( KUser::FullName ).toString();
696 // ask the user for confirmation/change
697 bool firstTry = true;
698 while ( firstTry || userName.isEmpty() )
700 QString prompt = firstTry ? i18n( "Please insert your name or initials:" ) :
701 i18n( "You must set this name:" );
702 userName = KInputDialog::getText( i18n("Annotations author"), prompt, userName );
703 firstTry = false;
705 // save the name
706 Okular::Settings::setIdentityAuthor( userName );
707 Okular::Settings::self()->writeConfig();
711 void PageViewAnnotator::setTextToolsEnabled( bool enabled )
713 m_textToolsEnabled = enabled;
714 if ( m_toolBar )
715 m_toolBar->setTextToolsEnabled( m_textToolsEnabled );
718 void PageViewAnnotator::setToolsEnabled( bool enabled )
720 m_toolsEnabled = enabled;
721 if ( m_toolBar )
722 m_toolBar->setToolsEnabled( m_toolsEnabled );
725 bool PageViewAnnotator::routeEvents() const
727 return m_engine && m_toolBar;
730 QRect PageViewAnnotator::routeEvent( QMouseEvent * e, PageViewItem * item )
732 if ( !item ) return QRect();
734 // find out mouse event type
735 AnnotatorEngine::EventType eventType = AnnotatorEngine::Press;
736 if ( e->type() == QEvent::MouseMove )
737 eventType = AnnotatorEngine::Move;
738 else if ( e->type() == QEvent::MouseButtonRelease )
739 eventType = AnnotatorEngine::Release;
741 // find out the pressed button
742 AnnotatorEngine::Button button = AnnotatorEngine::None;
743 Qt::MouseButtons buttonState = ( eventType == AnnotatorEngine::Move ) ? e->buttons() : e->button();
744 if ( buttonState == Qt::LeftButton )
745 button = AnnotatorEngine::Left;
746 else if ( buttonState == Qt::RightButton )
747 button = AnnotatorEngine::Right;
749 // find out normalized mouse coords inside current item
750 const QRect & itemRect = item->uncroppedGeometry();
751 double nX = item->absToPageX(e->x());
752 double nY = item->absToPageY(e->y());
754 QRect modifiedRect;
756 // 1. lock engine to current item
757 if ( m_lockedItem && item != m_lockedItem )
758 return QRect();
759 if ( !m_lockedItem && eventType == AnnotatorEngine::Press )
761 m_lockedItem = item;
762 m_engine->setItem( m_lockedItem );
765 // 2. use engine to perform operations
766 QRect paintRect = m_engine->event( eventType, button, nX, nY, itemRect.width(), itemRect.height(), item->page() );
768 // 3. update absolute extents rect and send paint event(s)
769 if ( paintRect.isValid() )
771 // 3.1. unite old and new painting regions
772 QRegion compoundRegion( m_lastDrawnRect );
773 m_lastDrawnRect = paintRect;
774 m_lastDrawnRect.translate( itemRect.left(), itemRect.top() );
775 // 3.2. decompose paint region in rects and send paint events
776 QVector<QRect> rects = compoundRegion.unite( m_lastDrawnRect ).rects();
777 for ( int i = 0; i < rects.count(); i++ )
778 m_pageView->widget()->update( rects[i] );
779 modifiedRect = compoundRegion.boundingRect() | m_lastDrawnRect;
782 // 4. if engine has finished, apply Annotation to the page
783 if ( m_engine->creationCompleted() )
785 // apply engine data to the Annotation's and reset engine
786 QList< Okular::Annotation* > annotations = m_engine->end();
787 // attach the newly filled annotations to the page
788 foreach ( Okular::Annotation * annotation, annotations )
790 if ( !annotation ) continue;
792 annotation->setCreationDate( QDateTime::currentDateTime() );
793 annotation->setModificationDate( QDateTime::currentDateTime() );
794 annotation->setAuthor( Okular::Settings::identityAuthor() );
795 m_document->addPageAnnotation( m_lockedItem->pageNumber(), annotation );
798 // go on creating annotations of the same type
799 // for now, disable the "construct again the same annotation"
800 //slotToolSelected( m_lastToolID );
801 slotToolSelected( -1 );
802 m_toolBar->selectButton( -1 );
805 return modifiedRect;
808 bool PageViewAnnotator::routeKeyEvent( QKeyEvent * event )
810 if ( event->key() == Qt::Key_Escape )
812 m_toolBar->selectButton( -1 );
813 return true;
815 return false;
818 bool PageViewAnnotator::routePaints( const QRect & wantedRect ) const
820 return m_engine && m_toolBar && wantedRect.intersects( m_lastDrawnRect ) && m_lockedItem;
823 void PageViewAnnotator::routePaint( QPainter * painter, const QRect & paintRect )
825 // if there's no locked item, then there's no decided place to draw on
826 if ( !m_lockedItem )
827 return;
829 #ifndef NDEBUG
830 // [DEBUG] draw the paint region if enabled
831 if ( Okular::Settings::debugDrawAnnotationRect() )
832 painter->drawRect( paintRect );
833 #endif
834 // move painter to current itemGeometry rect
835 const QRect & itemRect = m_lockedItem->uncroppedGeometry();
836 painter->save();
837 painter->translate( itemRect.topLeft() );
838 // TODO: Clip annotation painting to cropped page.
840 // transform cliprect from absolute to item relative coords
841 QRect annotRect = paintRect.intersect( m_lastDrawnRect );
842 annotRect.translate( -itemRect.topLeft() );
844 // use current engine for painting (in virtual page coordinates)
845 m_engine->paint( painter, m_lockedItem->uncroppedWidth(), m_lockedItem->uncroppedHeight(), annotRect );
846 painter->restore();
849 void PageViewAnnotator::slotToolSelected( int toolID )
851 // terminate any previous operation
852 if ( m_engine )
854 delete m_engine;
855 m_engine = 0;
857 m_lockedItem = 0;
858 if ( m_lastDrawnRect.isValid() )
860 m_pageView->widget()->update( m_lastDrawnRect );
861 m_lastDrawnRect = QRect();
864 // store current tool for later usage
865 m_lastToolID = toolID;
867 // handle tool deselection
868 if ( toolID == -1 )
870 m_pageView->displayMessage( QString() );
871 return;
874 // for the selected tool create the Engine
875 QDomNode toolNode = m_toolsDefinition.firstChild();
876 while ( toolNode.isElement() )
878 QDomElement toolElement = toolNode.toElement();
879 toolNode = toolNode.nextSibling();
881 // only find out the element describing selected tool
882 if ( toolElement.tagName() != "tool" || toolElement.attribute("id").toInt() != toolID )
883 continue;
885 // parse tool properties
886 QDomNode toolSubNode = toolElement.firstChild();
887 while ( toolSubNode.isElement() )
889 QDomElement toolSubElement = toolSubNode.toElement();
890 toolSubNode = toolSubNode.nextSibling();
892 // create the AnnotatorEngine
893 if ( toolSubElement.tagName() == "engine" )
895 QString type = toolSubElement.attribute( "type" );
896 if ( type == "SmoothLine" )
897 m_engine = new SmoothPathEngine( toolSubElement );
898 else if ( type == "PickPoint" )
899 m_engine = new PickPointEngine( toolSubElement );
900 else if ( type == "PolyLine" )
901 m_engine = new PolyLineEngine( toolSubElement );
902 else if ( type == "TextSelector" )
903 m_engine = new TextSelectorEngine( toolSubElement, m_pageView );
904 else
905 kWarning().nospace() << "tools.xml: engine type:'" << type << "' is not defined!";
907 // display the tooltip
908 else if ( toolSubElement.tagName() == "tooltip" )
910 QString tip = toolSubElement.text();
911 if ( !tip.isEmpty() )
912 m_pageView->displayMessage( i18nc( "Annotation tool", tip.toUtf8() ), PageViewMessage::Annotation );
916 // consistancy warning
917 if ( !m_engine )
918 kWarning() << "tools.xml: couldn't find good engine description. check xml.";
920 // stop after parsing selected tool's node
921 break;
925 void PageViewAnnotator::slotSaveToolbarOrientation( int side )
927 Okular::Settings::setEditToolBarPlacement( (int)side );
928 Okular::Settings::self()->writeConfig();
931 #include "pageviewannotator.moc"