Quantum GIS API Documentation  1.8
src/gui/qgsannotationitem.cpp
Go to the documentation of this file.
00001 /***************************************************************************
00002                               qgsannotationitem.cpp
00003                               ----------------------
00004   begin                : February 9, 2010
00005   copyright            : (C) 2010 by Marco Hugentobler
00006   email                : marco dot hugentobler at hugis dot net
00007  ***************************************************************************/
00008 
00009 /***************************************************************************
00010  *                                                                         *
00011  *   This program is free software; you can redistribute it and/or modify  *
00012  *   it under the terms of the GNU General Public License as published by  *
00013  *   the Free Software Foundation; either version 2 of the License, or     *
00014  *   (at your option) any later version.                                   *
00015  *                                                                         *
00016  ***************************************************************************/
00017 
00018 #include "qgsannotationitem.h"
00019 #include "qgsmapcanvas.h"
00020 #include "qgsrendercontext.h"
00021 #include "qgssymbollayerv2utils.h"
00022 #include "qgssymbolv2.h"
00023 #include <QPainter>
00024 #include <QPen>
00025 
00026 QgsAnnotationItem::QgsAnnotationItem( QgsMapCanvas* mapCanvas ): QgsMapCanvasItem( mapCanvas ), mMapPositionFixed( true ), mOffsetFromReferencePoint( QPointF( 50, -50 ) )
00027 {
00028   setFlag( QGraphicsItem::ItemIsSelectable, true );
00029   mMarkerSymbol = new QgsMarkerSymbolV2();
00030   mFrameBorderWidth = 1.0;
00031   mFrameColor = QColor( 0, 0, 0 );
00032   mFrameBackgroundColor = QColor( 255, 255, 255 );
00033   setData( 0, "AnnotationItem" );
00034 }
00035 
00036 QgsAnnotationItem::~QgsAnnotationItem()
00037 {
00038   delete mMarkerSymbol;
00039 }
00040 
00041 void QgsAnnotationItem::setMarkerSymbol( QgsMarkerSymbolV2* symbol )
00042 {
00043   delete mMarkerSymbol;
00044   mMarkerSymbol = symbol;
00045   updateBoundingRect();
00046 }
00047 
00048 void QgsAnnotationItem::setMapPosition( const QgsPoint& pos )
00049 {
00050   mMapPosition = pos;
00051   setPos( toCanvasCoordinates( mMapPosition ) );
00052 }
00053 
00054 void QgsAnnotationItem::setOffsetFromReferencePoint( const QPointF& offset )
00055 {
00056   mOffsetFromReferencePoint = offset;
00057   updateBoundingRect();
00058   updateBalloon();
00059 }
00060 
00061 void QgsAnnotationItem::setMapPositionFixed( bool fixed )
00062 {
00063   if ( mMapPositionFixed && !fixed )
00064   {
00065     //set map position to the top left corner of the balloon
00066     setMapPosition( toMapCoordinates( QPointF( pos() + mOffsetFromReferencePoint ).toPoint() ) );
00067     mOffsetFromReferencePoint = QPointF( 0, 0 );
00068   }
00069   else if ( fixed && !mMapPositionFixed )
00070   {
00071     setMapPosition( toMapCoordinates( QPointF( pos() + QPointF( -100, -100 ) ).toPoint() ) );
00072     mOffsetFromReferencePoint = QPointF( 100, 100 );
00073   }
00074   mMapPositionFixed = fixed;
00075   updateBoundingRect();
00076   updateBalloon();
00077   update();
00078 }
00079 
00080 void QgsAnnotationItem::updatePosition()
00081 {
00082   if ( mMapPositionFixed )
00083   {
00084     setPos( toCanvasCoordinates( mMapPosition ) );
00085   }
00086   else
00087   {
00088     mMapPosition = toMapCoordinates( pos().toPoint() );
00089   }
00090 }
00091 
00092 QRectF QgsAnnotationItem::boundingRect() const
00093 {
00094   return mBoundingRect;
00095 }
00096 
00097 QSizeF QgsAnnotationItem::minimumFrameSize() const
00098 {
00099   return QSizeF( 0, 0 );
00100 }
00101 
00102 void QgsAnnotationItem::updateBoundingRect()
00103 {
00104   prepareGeometryChange();
00105   double halfSymbolSize = 0.0;
00106   if ( mMarkerSymbol )
00107   {
00108     halfSymbolSize = scaledSymbolSize() / 2.0;
00109   }
00110 
00111   double xMinPos = qMin( -halfSymbolSize, mOffsetFromReferencePoint.x() - mFrameBorderWidth );
00112   double xMaxPos = qMax( halfSymbolSize, mOffsetFromReferencePoint.x() + mFrameSize.width() + mFrameBorderWidth );
00113   double yMinPos = qMin( -halfSymbolSize, mOffsetFromReferencePoint.y() - mFrameBorderWidth );
00114   double yMaxPos = qMax( halfSymbolSize, mOffsetFromReferencePoint.y() + mFrameSize.height() + mFrameBorderWidth );
00115   mBoundingRect = QRectF( xMinPos, yMinPos, xMaxPos - xMinPos, yMaxPos - yMinPos );
00116 }
00117 
00118 void QgsAnnotationItem::updateBalloon()
00119 {
00120   //first test if the point is in the frame. In that case we don't need a balloon.
00121   if ( !mMapPositionFixed ||
00122        ( mOffsetFromReferencePoint.x() < 0 && ( mOffsetFromReferencePoint.x() + mFrameSize.width() ) > 0
00123          && mOffsetFromReferencePoint.y() < 0 && ( mOffsetFromReferencePoint.y() + mFrameSize.height() ) > 0 ) )
00124   {
00125     mBalloonSegment = -1;
00126     return;
00127   }
00128 
00129   //edge list
00130   QList<QLineF> segmentList;
00131   segmentList << segment( 0 ); segmentList << segment( 1 ); segmentList << segment( 2 ); segmentList << segment( 3 );
00132 
00133   //find  closest edge / closest edge point
00134   double minEdgeDist = DBL_MAX;
00135   int minEdgeIndex = -1;
00136   QLineF minEdge;
00137   QgsPoint minEdgePoint;
00138   QgsPoint origin( 0, 0 );
00139 
00140   for ( int i = 0; i < 4; ++i )
00141   {
00142     QLineF currentSegment = segmentList.at( i );
00143     QgsPoint currentMinDistPoint;
00144     double currentMinDist = origin.sqrDistToSegment( currentSegment.x1(), currentSegment.y1(), currentSegment.x2(), currentSegment.y2(), currentMinDistPoint );
00145     if ( currentMinDist < minEdgeDist )
00146     {
00147       minEdgeIndex = i;
00148       minEdgePoint = currentMinDistPoint;
00149       minEdgeDist = currentMinDist;
00150       minEdge = currentSegment;
00151     }
00152   }
00153 
00154   if ( minEdgeIndex < 0 )
00155   {
00156     return;
00157   }
00158 
00159   //make that configurable for the item
00160   double segmentPointWidth = 10;
00161 
00162   mBalloonSegment = minEdgeIndex;
00163   QPointF minEdgeEnd = minEdge.p2();
00164   mBalloonSegmentPoint1 = QPointF( minEdgePoint.x(), minEdgePoint.y() );
00165   if ( sqrt( minEdgePoint.sqrDist( minEdgeEnd.x(), minEdgeEnd.y() ) ) < segmentPointWidth )
00166   {
00167     mBalloonSegmentPoint1 = pointOnLineWithDistance( minEdge.p2(), minEdge.p1(), segmentPointWidth );
00168   }
00169 
00170   mBalloonSegmentPoint2 = pointOnLineWithDistance( mBalloonSegmentPoint1, minEdge.p2(), 10 );
00171 }
00172 
00173 void QgsAnnotationItem::drawFrame( QPainter* p )
00174 {
00175   QPen framePen( mFrameColor );
00176   framePen.setWidthF( mFrameBorderWidth );
00177 
00178   p->setPen( framePen );
00179   QBrush frameBrush( mFrameBackgroundColor );
00180   p->setBrush( frameBrush );
00181   p->setRenderHint( QPainter::Antialiasing, true );
00182 
00183   QPolygonF poly;
00184   for ( int i = 0; i < 4; ++i )
00185   {
00186     QLineF currentSegment = segment( i );
00187     poly << currentSegment.p1();
00188     if ( i == mBalloonSegment && mMapPositionFixed )
00189     {
00190       poly << mBalloonSegmentPoint1;
00191       poly << QPointF( 0, 0 );
00192       poly << mBalloonSegmentPoint2;
00193     }
00194     poly << currentSegment.p2();
00195   }
00196   p->drawPolygon( poly );
00197 }
00198 
00199 void QgsAnnotationItem::setFrameSize( const QSizeF& size )
00200 {
00201   QSizeF frameSize = minimumFrameSize().expandedTo( size ); //don't allow frame sizes below minimum
00202   mFrameSize = frameSize;
00203   updateBoundingRect();
00204   updateBalloon();
00205 }
00206 
00207 void QgsAnnotationItem::drawMarkerSymbol( QPainter* p )
00208 {
00209   if ( !p )
00210   {
00211     return;
00212   }
00213 
00214   QgsRenderContext renderContext;
00215   if ( !setRenderContextVariables( p, renderContext ) )
00216   {
00217     return;
00218   }
00219 
00220   if ( mMarkerSymbol )
00221   {
00222     mMarkerSymbol->startRender( renderContext );
00223     mMarkerSymbol->renderPoint( QPointF( 0, 0 ), 0, renderContext );
00224     mMarkerSymbol->stopRender( renderContext );
00225   }
00226 }
00227 
00228 void QgsAnnotationItem::drawSelectionBoxes( QPainter* p )
00229 {
00230   if ( !p )
00231   {
00232     return;
00233   }
00234 
00235   //no selection boxes for composer mode
00236   if ( data( 1 ).toString() == "composer" )
00237   {
00238     return;
00239   }
00240 
00241   double handlerSize = 10;
00242   p->setPen( Qt::NoPen );
00243   p->setBrush( QColor( 200, 200, 210, 120 ) );
00244   p->drawRect( QRectF( mBoundingRect.left(), mBoundingRect.top(), handlerSize, handlerSize ) );
00245   p->drawRect( QRectF( mBoundingRect.right() - handlerSize, mBoundingRect.top(), handlerSize, handlerSize ) );
00246   p->drawRect( QRectF( mBoundingRect.right() - handlerSize, mBoundingRect.bottom() - handlerSize, handlerSize, handlerSize ) );
00247   p->drawRect( QRectF( mBoundingRect.left(), mBoundingRect.bottom() - handlerSize, handlerSize, handlerSize ) );
00248 }
00249 
00250 QLineF QgsAnnotationItem::segment( int index )
00251 {
00252   switch ( index )
00253   {
00254     case 0:
00255       return QLineF( mOffsetFromReferencePoint.x(), mOffsetFromReferencePoint.y(), mOffsetFromReferencePoint.x()
00256                      + mFrameSize.width(), mOffsetFromReferencePoint.y() );
00257     case 1:
00258       return QLineF( mOffsetFromReferencePoint.x() + mFrameSize.width(), mOffsetFromReferencePoint.y(),
00259                      mOffsetFromReferencePoint.x() + mFrameSize.width(), mOffsetFromReferencePoint.y() + mFrameSize.height() );
00260     case 2:
00261       return QLineF( mOffsetFromReferencePoint.x() + mFrameSize.width(), mOffsetFromReferencePoint.y() + mFrameSize.height(),
00262                      mOffsetFromReferencePoint.x(), mOffsetFromReferencePoint.y() + mFrameSize.height() );
00263     case 3:
00264       return QLineF( mOffsetFromReferencePoint.x(), mOffsetFromReferencePoint.y() + mFrameSize.height(),
00265                      mOffsetFromReferencePoint.x(), mOffsetFromReferencePoint.y() );
00266     default:
00267       return QLineF();
00268   }
00269 }
00270 
00271 QPointF QgsAnnotationItem::pointOnLineWithDistance( const QPointF& startPoint, const QPointF& directionPoint, double distance ) const
00272 {
00273   double dx = directionPoint.x() - startPoint.x();
00274   double dy = directionPoint.y() - startPoint.y();
00275   double length = sqrt( dx * dx + dy * dy );
00276   double scaleFactor = distance / length;
00277   return QPointF( startPoint.x() + dx * scaleFactor, startPoint.y() + dy * scaleFactor );
00278 }
00279 
00280 QgsAnnotationItem::MouseMoveAction QgsAnnotationItem::moveActionForPosition( const QPointF& pos ) const
00281 {
00282   QPointF itemPos = mapFromScene( pos );
00283 
00284   int cursorSensitivity = 7;
00285 
00286   if ( qAbs( itemPos.x() ) < cursorSensitivity && qAbs( itemPos.y() ) < cursorSensitivity ) //move map point if position is close to the origin
00287   {
00288     return MoveMapPosition;
00289   }
00290 
00291   bool left, right, up, down;
00292   left = qAbs( itemPos.x() - mOffsetFromReferencePoint.x() ) < cursorSensitivity;
00293   right = qAbs( itemPos.x() - ( mOffsetFromReferencePoint.x() + mFrameSize.width() ) ) < cursorSensitivity;
00294   up = qAbs( itemPos.y() - mOffsetFromReferencePoint.y() ) < cursorSensitivity;
00295   down = qAbs( itemPos.y() - ( mOffsetFromReferencePoint.y() + mFrameSize.height() ) ) < cursorSensitivity;
00296 
00297   if ( left && up )
00298   {
00299     return ResizeFrameLeftUp;
00300   }
00301   else if ( right && up )
00302   {
00303     return ResizeFrameRightUp;
00304   }
00305   else if ( left && down )
00306   {
00307     return ResizeFrameLeftDown;
00308   }
00309   else if ( right && down )
00310   {
00311     return ResizeFrameRightDown;
00312   }
00313   if ( left )
00314   {
00315     return ResizeFrameLeft;
00316   }
00317   if ( right )
00318   {
00319     return ResizeFrameRight;
00320   }
00321   if ( up )
00322   {
00323     return ResizeFrameUp;
00324   }
00325   if ( down )
00326   {
00327     return ResizeFrameDown;
00328   }
00329 
00330   //finally test if pos is in the frame area
00331   if ( itemPos.x() >= mOffsetFromReferencePoint.x() && itemPos.x() <= ( mOffsetFromReferencePoint.x() + mFrameSize.width() )
00332        && itemPos.y() >= mOffsetFromReferencePoint.y() && itemPos.y() <= ( mOffsetFromReferencePoint.y() + mFrameSize.height() ) )
00333   {
00334     return MoveFramePosition;
00335   }
00336   return NoAction;
00337 }
00338 
00339 Qt::CursorShape QgsAnnotationItem::cursorShapeForAction( MouseMoveAction moveAction ) const
00340 {
00341   switch ( moveAction )
00342   {
00343     case NoAction:
00344       return Qt::ArrowCursor;
00345     case MoveMapPosition:
00346     case MoveFramePosition:
00347       return Qt::SizeAllCursor;
00348     case ResizeFrameUp:
00349     case ResizeFrameDown:
00350       return Qt::SizeVerCursor;
00351     case ResizeFrameLeft:
00352     case ResizeFrameRight:
00353       return Qt::SizeHorCursor;
00354     case ResizeFrameLeftUp:
00355     case ResizeFrameRightDown:
00356       return Qt::SizeFDiagCursor;
00357     case ResizeFrameRightUp:
00358     case ResizeFrameLeftDown:
00359       return Qt::SizeBDiagCursor;
00360     default:
00361       return Qt::ArrowCursor;
00362   }
00363 }
00364 
00365 double QgsAnnotationItem::scaledSymbolSize() const
00366 {
00367   if ( !mMarkerSymbol )
00368   {
00369     return 0.0;
00370   }
00371 
00372   if ( !mMapCanvas )
00373   {
00374     return mMarkerSymbol->size();
00375   }
00376 
00377   double dpmm = mMapCanvas->logicalDpiX() / 25.4;
00378   return dpmm * mMarkerSymbol->size();
00379 }
00380 
00381 void QgsAnnotationItem::_writeXML( QDomDocument& doc, QDomElement& itemElem ) const
00382 {
00383   if ( itemElem.isNull() )
00384   {
00385     return;
00386   }
00387   QDomElement annotationElem = doc.createElement( "AnnotationItem" );
00388   annotationElem.setAttribute( "mapPositionFixed", mMapPositionFixed );
00389   annotationElem.setAttribute( "mapPosX", QString::number( mMapPosition.x() ) );
00390   annotationElem.setAttribute( "mapPosY", QString::number( mMapPosition.y() ) );
00391   annotationElem.setAttribute( "offsetX", QString::number( mOffsetFromReferencePoint.x() ) );
00392   annotationElem.setAttribute( "offsetY", QString::number( mOffsetFromReferencePoint.y() ) );
00393   annotationElem.setAttribute( "frameWidth", QString::number( mFrameSize.width() ) );
00394   annotationElem.setAttribute( "frameHeight", QString::number( mFrameSize.height() ) );
00395   QPointF canvasPos = pos();
00396   annotationElem.setAttribute( "canvasPosX", QString::number( canvasPos.x() ) );
00397   annotationElem.setAttribute( "canvasPosY", QString::number( canvasPos.y() ) );
00398   annotationElem.setAttribute( "frameBorderWidth", QString::number( mFrameBorderWidth ) );
00399   annotationElem.setAttribute( "frameColor", mFrameColor.name() );
00400   annotationElem.setAttribute( "frameBackgroundColor", mFrameBackgroundColor.name() );
00401   annotationElem.setAttribute( "frameBackgroundColorAlpha", mFrameBackgroundColor.alpha() );
00402   annotationElem.setAttribute( "visible", isVisible() );
00403   if ( mMarkerSymbol )
00404   {
00405     QDomElement symbolElem = QgsSymbolLayerV2Utils::saveSymbol( "marker symbol", mMarkerSymbol, doc );
00406     if ( !symbolElem.isNull() )
00407     {
00408       annotationElem.appendChild( symbolElem );
00409     }
00410   }
00411   itemElem.appendChild( annotationElem );
00412 }
00413 
00414 void QgsAnnotationItem::_readXML( const QDomDocument& doc, const QDomElement& annotationElem )
00415 {
00416   Q_UNUSED( doc );
00417   if ( annotationElem.isNull() )
00418   {
00419     return;
00420   }
00421   QPointF pos;
00422   pos.setX( annotationElem.attribute( "canvasPosX", "0" ).toDouble() );
00423   pos.setY( annotationElem.attribute( "canvasPosY", "0" ).toDouble() );
00424   setPos( pos );
00425   QgsPoint mapPos;
00426   mapPos.setX( annotationElem.attribute( "mapPosX", "0" ).toDouble() );
00427   mapPos.setY( annotationElem.attribute( "mapPosY", "0" ).toDouble() );
00428   mMapPosition = mapPos;
00429   mFrameBorderWidth = annotationElem.attribute( "frameBorderWidth", "0.5" ).toDouble();
00430   mFrameColor.setNamedColor( annotationElem.attribute( "frameColor", "#000000" ) );
00431   mFrameBackgroundColor.setNamedColor( annotationElem.attribute( "frameBackgroundColor" ) );
00432   mFrameBackgroundColor.setAlpha( annotationElem.attribute( "frameBackgroundColorAlpha", "255" ).toInt() );
00433   mFrameSize.setWidth( annotationElem.attribute( "frameWidth", "50" ).toDouble() );
00434   mFrameSize.setHeight( annotationElem.attribute( "frameHeight", "50" ).toDouble() );
00435   mOffsetFromReferencePoint.setX( annotationElem.attribute( "offsetX", "0" ).toDouble() );
00436   mOffsetFromReferencePoint.setY( annotationElem.attribute( "offsetY", "0" ).toDouble() );
00437   mMapPositionFixed = annotationElem.attribute( "mapPositionFixed", "1" ).toInt();
00438   setVisible( annotationElem.attribute( "visible", "1" ).toInt() );
00439 
00440   //marker symbol
00441   QDomElement symbolElem = annotationElem.firstChildElement( "symbol" );
00442   if ( !symbolElem.isNull() )
00443   {
00444     QgsMarkerSymbolV2* symbol = dynamic_cast<QgsMarkerSymbolV2*>( QgsSymbolLayerV2Utils::loadSymbol( symbolElem ) );
00445     if ( symbol )
00446     {
00447       delete mMarkerSymbol;
00448       mMarkerSymbol = symbol;
00449     }
00450   }
00451 
00452   updateBoundingRect();
00453   updateBalloon();
00454 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines