Quantum GIS API Documentation  1.8
src/core/composer/qgscomposeritem.cpp
Go to the documentation of this file.
00001 /***************************************************************************
00002                          qgscomposeritem.cpp
00003                              -------------------
00004     begin                : January 2005
00005     copyright            : (C) 2005 by Radim Blazek
00006     email                : blazek@itc.it
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 #include <QWidget>
00018 #include <QDomNode>
00019 #include <QFile>
00020 #include <QGraphicsScene>
00021 #include <QGraphicsSceneMouseEvent>
00022 #include <QGraphicsView>
00023 #include <QPainter>
00024 
00025 #include "qgscomposition.h"
00026 #include "qgscomposeritem.h"
00027 
00028 
00029 #include <limits>
00030 #include "qgsapplication.h"
00031 #include "qgsrectangle.h" //just for debugging
00032 #include "qgslogger.h"
00033 #include "qgssymbollayerv2utils.h" //for pointOnLineWithDistance
00034 
00035 #include <cmath>
00036 
00037 #define FONT_WORKAROUND_SCALE 10 //scale factor for upscaling fontsize and downscaling painter
00038 
00039 QgsComposerItem::QgsComposerItem( QgsComposition* composition, bool manageZValue )
00040     : QObject( 0 )
00041     , QGraphicsRectItem( 0 )
00042     , mComposition( composition )
00043     , mBoundingResizeRectangle( 0 )
00044     , mFrame( true )
00045     , mItemPositionLocked( false )
00046     , mLastValidViewScaleFactor( -1 )
00047     , mRotation( 0 )
00048 {
00049   init( manageZValue );
00050 }
00051 
00052 QgsComposerItem::QgsComposerItem( qreal x, qreal y, qreal width, qreal height, QgsComposition* composition, bool manageZValue )
00053     : QObject( 0 )
00054     , QGraphicsRectItem( 0, 0, width, height, 0 )
00055     , mComposition( composition )
00056     , mBoundingResizeRectangle( 0 )
00057     , mFrame( true )
00058     , mItemPositionLocked( false )
00059     , mLastValidViewScaleFactor( -1 )
00060     , mRotation( 0 )
00061 {
00062   init( manageZValue );
00063   QTransform t;
00064   t.translate( x, y );
00065   setTransform( t );
00066 }
00067 
00068 void QgsComposerItem::init( bool manageZValue )
00069 {
00070   setFlag( QGraphicsItem::ItemIsSelectable, true );
00071   setAcceptsHoverEvents( true );
00072   //set default pen and brush
00073   setBrush( QBrush( QColor( 255, 255, 255, 255 ) ) );
00074   QPen defaultPen( QColor( 0, 0, 0 ) );
00075   defaultPen.setWidthF( 0.3 );
00076   setPen( defaultPen );
00077   //let z-Value be managed by composition
00078   if ( mComposition && manageZValue )
00079   {
00080     mComposition->addItemToZList( this );
00081   }
00082 }
00083 
00084 QgsComposerItem::~QgsComposerItem()
00085 {
00086   if ( mComposition )
00087   {
00088     mComposition->removeItemFromZList( this );
00089   }
00090 
00091   delete mBoundingResizeRectangle;
00092 }
00093 
00094 void QgsComposerItem::setSelected( bool s )
00095 {
00096   QgsDebugMsg( "entered." );
00097   QGraphicsRectItem::setSelected( s );
00098   update(); //to draw selection boxes
00099 }
00100 
00101 bool QgsComposerItem::writeSettings( void )  { return true; }
00102 
00103 bool QgsComposerItem::readSettings( void )  { return true; }
00104 
00105 bool QgsComposerItem::removeSettings( void )  { return true; }
00106 
00107 bool QgsComposerItem::_writeXML( QDomElement& itemElem, QDomDocument& doc ) const
00108 {
00109   if ( itemElem.isNull() )
00110   {
00111     return false;
00112   }
00113 
00114   QDomElement composerItemElem = doc.createElement( "ComposerItem" );
00115 
00116   //frame
00117   if ( mFrame )
00118   {
00119     composerItemElem.setAttribute( "frame", "true" );
00120   }
00121   else
00122   {
00123     composerItemElem.setAttribute( "frame", "false" );
00124   }
00125 
00126   //scene rect
00127   composerItemElem.setAttribute( "x", QString::number( transform().dx() ) );
00128   composerItemElem.setAttribute( "y", QString::number( transform().dy() ) );
00129   composerItemElem.setAttribute( "width", QString::number( rect().width() ) );
00130   composerItemElem.setAttribute( "height", QString::number( rect().height() ) );
00131   composerItemElem.setAttribute( "zValue", QString::number( zValue() ) );
00132   composerItemElem.setAttribute( "outlineWidth", QString::number( pen().widthF() ) );
00133   composerItemElem.setAttribute( "rotation",  QString::number( mRotation ) );
00134   composerItemElem.setAttribute( "id", mId );
00135   //position lock for mouse moves/resizes
00136   if ( mItemPositionLocked )
00137   {
00138     composerItemElem.setAttribute( "positionLock", "true" );
00139   }
00140   else
00141   {
00142     composerItemElem.setAttribute( "positionLock", "false" );
00143   }
00144 
00145   composerItemElem.setAttribute( "lastValidViewScaleFactor", QString::number( mLastValidViewScaleFactor ) );
00146 
00147 
00148   //frame color
00149   QDomElement frameColorElem = doc.createElement( "FrameColor" );
00150   QColor frameColor = pen().color();
00151   frameColorElem.setAttribute( "red", QString::number( frameColor.red() ) );
00152   frameColorElem.setAttribute( "green", QString::number( frameColor.green() ) );
00153   frameColorElem.setAttribute( "blue", QString::number( frameColor.blue() ) );
00154   frameColorElem.setAttribute( "alpha", QString::number( frameColor.alpha() ) );
00155   composerItemElem.appendChild( frameColorElem );
00156 
00157   //background color
00158   QDomElement bgColorElem = doc.createElement( "BackgroundColor" );
00159   QColor bgColor = brush().color();
00160   bgColorElem.setAttribute( "red", QString::number( bgColor.red() ) );
00161   bgColorElem.setAttribute( "green", QString::number( bgColor.green() ) );
00162   bgColorElem.setAttribute( "blue", QString::number( bgColor.blue() ) );
00163   bgColorElem.setAttribute( "alpha", QString::number( bgColor.alpha() ) );
00164   composerItemElem.appendChild( bgColorElem );
00165 
00166   itemElem.appendChild( composerItemElem );
00167 
00168   return true;
00169 }
00170 
00171 bool QgsComposerItem::_readXML( const QDomElement& itemElem, const QDomDocument& doc )
00172 {
00173   Q_UNUSED( doc );
00174   if ( itemElem.isNull() )
00175   {
00176     return false;
00177   }
00178 
00179   //rotation
00180   mRotation = itemElem.attribute( "rotation", "0" ).toDouble();
00181 
00182   //id
00183   mId = itemElem.attribute( "id", "" );
00184 
00185   //frame
00186   QString frame = itemElem.attribute( "frame" );
00187   if ( frame.compare( "true", Qt::CaseInsensitive ) == 0 )
00188   {
00189     mFrame = true;
00190   }
00191   else
00192   {
00193     mFrame = false;
00194   }
00195 
00196   //position lock for mouse moves/resizes
00197   QString positionLock = itemElem.attribute( "positionLock" );
00198   if ( positionLock.compare( "true", Qt::CaseInsensitive ) == 0 )
00199   {
00200     mItemPositionLocked = true;
00201   }
00202   else
00203   {
00204     mItemPositionLocked = false;
00205   }
00206 
00207   //position
00208   double x, y, width, height;
00209   bool xOk, yOk, widthOk, heightOk;
00210 
00211   x = itemElem.attribute( "x" ).toDouble( &xOk );
00212   y = itemElem.attribute( "y" ).toDouble( &yOk );
00213   width = itemElem.attribute( "width" ).toDouble( &widthOk );
00214   height = itemElem.attribute( "height" ).toDouble( &heightOk );
00215 
00216   if ( !xOk || !yOk || !widthOk || !heightOk )
00217   {
00218     return false;
00219   }
00220 
00221   mLastValidViewScaleFactor = itemElem.attribute( "lastValidViewScaleFactor", "-1" ).toDouble();
00222 
00223   setSceneRect( QRectF( x, y, width, height ) );
00224   setZValue( itemElem.attribute( "zValue" ).toDouble() );
00225 
00226   //pen
00227   QDomNodeList frameColorList = itemElem.elementsByTagName( "FrameColor" );
00228   if ( frameColorList.size() > 0 )
00229   {
00230     QDomElement frameColorElem = frameColorList.at( 0 ).toElement();
00231     bool redOk, greenOk, blueOk, alphaOk, widthOk;
00232     int penRed, penGreen, penBlue, penAlpha;
00233     double penWidth;
00234 
00235     penWidth = itemElem.attribute( "outlineWidth" ).toDouble( &widthOk );
00236     penRed = frameColorElem.attribute( "red" ).toDouble( &redOk );
00237     penGreen = frameColorElem.attribute( "green" ).toDouble( &greenOk );
00238     penBlue = frameColorElem.attribute( "blue" ).toDouble( &blueOk );
00239     penAlpha = frameColorElem.attribute( "alpha" ).toDouble( &alphaOk );
00240     if ( redOk && greenOk && blueOk && alphaOk && widthOk )
00241     {
00242       QPen framePen( QColor( penRed, penGreen, penBlue, penAlpha ) );
00243       framePen.setWidthF( penWidth );
00244       setPen( framePen );
00245     }
00246   }
00247 
00248   //brush
00249   QDomNodeList bgColorList = itemElem.elementsByTagName( "BackgroundColor" );
00250   if ( bgColorList.size() > 0 )
00251   {
00252     QDomElement bgColorElem = bgColorList.at( 0 ).toElement();
00253     bool redOk, greenOk, blueOk, alphaOk;
00254     int bgRed, bgGreen, bgBlue, bgAlpha;
00255     bgRed = bgColorElem.attribute( "red" ).toDouble( &redOk );
00256     bgGreen = bgColorElem.attribute( "green" ).toDouble( &greenOk );
00257     bgBlue = bgColorElem.attribute( "blue" ).toDouble( &blueOk );
00258     bgAlpha = bgColorElem.attribute( "alpha" ).toDouble( &alphaOk );
00259     if ( redOk && greenOk && blueOk && alphaOk )
00260     {
00261       QColor brushColor( bgRed, bgGreen, bgBlue, bgAlpha );
00262       setBrush( QBrush( brushColor ) );
00263     }
00264   }
00265   return true;
00266 }
00267 
00268 void QgsComposerItem::beginCommand( const QString& commandText, QgsComposerMergeCommand::Context c )
00269 {
00270   if ( mComposition )
00271   {
00272     mComposition->beginCommand( this, commandText, c );
00273   }
00274 }
00275 
00276 void QgsComposerItem::endCommand()
00277 {
00278   if ( mComposition )
00279   {
00280     mComposition->endCommand();
00281   }
00282 }
00283 
00284 void QgsComposerItem::cancelCommand()
00285 {
00286   if ( mComposition )
00287   {
00288     mComposition->cancelCommand();
00289   }
00290 }
00291 
00292 void QgsComposerItem::mouseMoveEvent( QGraphicsSceneMouseEvent * event )
00293 {
00294   if ( mItemPositionLocked )
00295   {
00296     return;
00297   }
00298 
00299   if ( !isSelected() )
00300   {
00301     return;
00302   }
00303 
00304   if ( mBoundingResizeRectangle )
00305   {
00306     double diffX = event->lastScenePos().x() - mLastMouseEventPos.x();
00307     double diffY = event->lastScenePos().y() - mLastMouseEventPos.y();
00308 
00309     changeItemRectangle( event->lastScenePos(), mMouseMoveStartPos, this, diffX, diffY, mBoundingResizeRectangle );
00310   }
00311   mLastMouseEventPos = event->lastScenePos();
00312 }
00313 
00314 void QgsComposerItem::mousePressEvent( QGraphicsSceneMouseEvent * event )
00315 {
00316   if ( mItemPositionLocked )
00317   {
00318     return;
00319   }
00320 
00321   if ( !isSelected() )
00322   {
00323     return;
00324   }
00325 
00326   //set current position and type of mouse move action
00327   mMouseMoveStartPos = event->lastScenePos();
00328   mLastMouseEventPos = event->lastScenePos();
00329   mCurrentMouseMoveAction = mouseMoveActionForPosition( event->pos() );
00330 
00331   //remove the old rubber band item if it is still there
00332   if ( mBoundingResizeRectangle )
00333   {
00334     scene()->removeItem( mBoundingResizeRectangle );
00335     delete mBoundingResizeRectangle;
00336     mBoundingResizeRectangle = 0;
00337   }
00338   //create and show bounding rectangle
00339   mBoundingResizeRectangle = new QGraphicsRectItem( 0 );
00340   scene()->addItem( mBoundingResizeRectangle );
00341   mBoundingResizeRectangle->setRect( QRectF( 0, 0, rect().width(), rect().height() ) );
00342   QTransform resizeTransform;
00343   resizeTransform.translate( transform().dx(), transform().dy() );
00344   mBoundingResizeRectangle->setTransform( resizeTransform );
00345 
00346   mBoundingResizeRectangle->setBrush( Qt::NoBrush );
00347   mBoundingResizeRectangle->setPen( QPen( QColor( 0, 0, 0 ), 0 ) );
00348   mBoundingResizeRectangle->setZValue( 90 );
00349   mBoundingResizeRectangle->show();
00350 }
00351 
00352 void QgsComposerItem::mouseReleaseEvent( QGraphicsSceneMouseEvent * event )
00353 {
00354 
00355   if ( mItemPositionLocked )
00356   {
00357     return;
00358   }
00359 
00360   if ( !isSelected() )
00361   {
00362     return;
00363   }
00364 
00365   //delete frame rectangle
00366   if ( mBoundingResizeRectangle )
00367   {
00368     scene()->removeItem( mBoundingResizeRectangle );
00369     delete mBoundingResizeRectangle;
00370     mBoundingResizeRectangle = 0;
00371   }
00372 
00373   QPointF mouseMoveStopPoint = event->lastScenePos();
00374   double diffX = mouseMoveStopPoint.x() - mMouseMoveStartPos.x();
00375   double diffY = mouseMoveStopPoint.y() - mMouseMoveStartPos.y();
00376 
00377   //it was only a click
00378   if ( qAbs( diffX ) < std::numeric_limits<double>::min() && qAbs( diffY ) < std::numeric_limits<double>::min() )
00379   {
00380     return;
00381   }
00382 
00383   beginCommand( tr( "Change item position" ) );
00384   changeItemRectangle( mouseMoveStopPoint, mMouseMoveStartPos, this, diffX, diffY, this );
00385   endCommand();
00386 
00387   //reset default action
00388   mCurrentMouseMoveAction = QgsComposerItem::MoveItem;
00389   setCursor( Qt::ArrowCursor );
00390 }
00391 
00392 Qt::CursorShape QgsComposerItem::cursorForPosition( const QPointF& itemCoordPos )
00393 {
00394   QgsComposerItem::MouseMoveAction mouseAction = mouseMoveActionForPosition( itemCoordPos );
00395   switch ( mouseAction )
00396   {
00397     case NoAction:
00398       return Qt::ForbiddenCursor;
00399     case MoveItem:
00400       return Qt::SizeAllCursor;
00401     case ResizeUp:
00402     case ResizeDown:
00403       return Qt::SizeVerCursor;
00404     case ResizeLeft:
00405     case ResizeRight:
00406       return Qt::SizeHorCursor;
00407     case ResizeLeftUp:
00408     case ResizeRightDown:
00409       return Qt::SizeFDiagCursor;
00410     case ResizeRightUp:
00411     case ResizeLeftDown:
00412       return Qt::SizeBDiagCursor;
00413     default:
00414       return Qt::ArrowCursor;
00415   }
00416 }
00417 
00418 QgsComposerItem::MouseMoveAction QgsComposerItem::mouseMoveActionForPosition( const QPointF& itemCoordPos )
00419 {
00420 
00421   //no action at all if item position is locked for mouse
00422   if ( mItemPositionLocked )
00423   {
00424     return QgsComposerItem::NoAction;
00425   }
00426 
00427   bool nearLeftBorder = false;
00428   bool nearRightBorder = false;
00429   bool nearLowerBorder = false;
00430   bool nearUpperBorder = false;
00431 
00432   double borderTolerance = rectHandlerBorderTolerance();
00433 
00434   if ( itemCoordPos.x() < borderTolerance )
00435   {
00436     nearLeftBorder = true;
00437   }
00438   if ( itemCoordPos.y() < borderTolerance )
00439   {
00440     nearUpperBorder = true;
00441   }
00442   if ( itemCoordPos.x() > ( rect().width() - borderTolerance ) )
00443   {
00444     nearRightBorder = true;
00445   }
00446   if ( itemCoordPos.y() > ( rect().height() - borderTolerance ) )
00447   {
00448     nearLowerBorder = true;
00449   }
00450 
00451   if ( nearLeftBorder && nearUpperBorder )
00452   {
00453     return QgsComposerItem::ResizeLeftUp;
00454   }
00455   else if ( nearLeftBorder && nearLowerBorder )
00456   {
00457     return QgsComposerItem::ResizeLeftDown;
00458   }
00459   else if ( nearRightBorder && nearUpperBorder )
00460   {
00461     return QgsComposerItem::ResizeRightUp;
00462   }
00463   else if ( nearRightBorder && nearLowerBorder )
00464   {
00465     return QgsComposerItem::ResizeRightDown;
00466   }
00467   else if ( nearLeftBorder )
00468   {
00469     return QgsComposerItem::ResizeLeft;
00470   }
00471   else if ( nearRightBorder )
00472   {
00473     return QgsComposerItem::ResizeRight;
00474   }
00475   else if ( nearUpperBorder )
00476   {
00477     return QgsComposerItem::ResizeUp;
00478   }
00479   else if ( nearLowerBorder )
00480   {
00481     return QgsComposerItem::ResizeDown;
00482   }
00483 
00484   return QgsComposerItem::MoveItem; //default
00485 }
00486 
00487 void QgsComposerItem::changeItemRectangle( const QPointF& currentPosition,
00488     const QPointF& mouseMoveStartPos,
00489     const QGraphicsRectItem* originalItem,
00490     double dx, double dy,
00491     QGraphicsRectItem* changeItem )
00492 {
00493   Q_UNUSED( dx );
00494   Q_UNUSED( dy );
00495   if ( !changeItem || !originalItem || !mComposition )
00496   {
00497     return;
00498   }
00499 
00500   //test if change item is a composer item. If so, prefer call to  setSceneRect() instead of setTransform() and setRect()
00501   QgsComposerItem* changeComposerItem = dynamic_cast<QgsComposerItem *>( changeItem );
00502 
00503   double mx = 0.0, my = 0.0, rx = 0.0, ry = 0.0;
00504   QPointF snappedPosition = mComposition->snapPointToGrid( currentPosition );
00505   //double diffX = snappedPosition.x() - mouseMoveStartPos.x();
00506   //double diffY = snappedPosition.y() - mouseMoveStartPos.y();
00507   double diffX = 0;
00508   double diffY = 0;
00509 
00510   switch ( mCurrentMouseMoveAction )
00511   {
00512       //vertical resize
00513     case QgsComposerItem::ResizeUp:
00514       diffY = snappedPosition.y() - originalItem->transform().dy();
00515       mx = 0; my = diffY; rx = 0; ry = -diffY;
00516       break;
00517 
00518     case QgsComposerItem::ResizeDown:
00519       diffY = snappedPosition.y() - ( originalItem->transform().dy() + originalItem->rect().height() );
00520       mx = 0; my = 0; rx = 0; ry = diffY;
00521       break;
00522 
00523       //horizontal resize
00524     case QgsComposerItem::ResizeLeft:
00525       diffX = snappedPosition.x() - originalItem->transform().dx();
00526       mx = diffX, my = 0; rx = -diffX; ry = 0;
00527       break;
00528 
00529     case QgsComposerItem::ResizeRight:
00530       diffX = snappedPosition.x() - ( originalItem->transform().dx() + originalItem->rect().width() );
00531       mx = 0; my = 0; rx = diffX, ry = 0;
00532       break;
00533 
00534       //diagonal resize
00535     case QgsComposerItem::ResizeLeftUp:
00536       diffX = snappedPosition.x() - originalItem->transform().dx();
00537       diffY = snappedPosition.y() - originalItem->transform().dy();
00538       mx = diffX, my = diffY; rx = -diffX; ry = -diffY;
00539       break;
00540 
00541     case QgsComposerItem::ResizeRightDown:
00542       diffX = snappedPosition.x() - ( originalItem->transform().dx() + originalItem->rect().width() );
00543       diffY = snappedPosition.y() - ( originalItem->transform().dy() + originalItem->rect().height() );
00544       mx = 0; my = 0; rx = diffX, ry = diffY;
00545       break;
00546 
00547     case QgsComposerItem::ResizeRightUp:
00548       diffX = snappedPosition.x() - ( originalItem->transform().dx() + originalItem->rect().width() );
00549       diffY = snappedPosition.y() - originalItem->transform().dy();
00550       mx = 0; my = diffY, rx = diffX, ry = -diffY;
00551       break;
00552 
00553     case QgsComposerItem::ResizeLeftDown:
00554       diffX = snappedPosition.x() - originalItem->transform().dx();
00555       diffY = snappedPosition.y() - ( originalItem->transform().dy() + originalItem->rect().height() );
00556       mx = diffX, my = 0; rx = -diffX; ry = diffY;
00557       break;
00558 
00559     case QgsComposerItem::MoveItem:
00560     {
00561       //calculate total move difference
00562       double moveX = currentPosition.x() - mouseMoveStartPos.x();
00563       double moveY = currentPosition.y() - mouseMoveStartPos.y();
00564 
00565       QPointF upperLeftPoint( originalItem->transform().dx() + moveX, originalItem->transform().dy() + moveY );
00566       QPointF snappedLeftPoint = mComposition->snapPointToGrid( upperLeftPoint );
00567 
00568       double moveRectX = snappedLeftPoint.x() - originalItem->transform().dx();
00569       double moveRectY = snappedLeftPoint.y() - originalItem->transform().dy();
00570 
00571       if ( !changeComposerItem )
00572       {
00573         QTransform moveTransform;
00574         moveTransform.translate( originalItem->transform().dx() + moveRectX, originalItem->transform().dy() + moveRectY );
00575         changeItem->setTransform( moveTransform );
00576       }
00577       else  //for composer items, we prefer setSceneRect as subclasses can implement custom behaviour (e.g. item group)
00578       {
00579         changeComposerItem->setSceneRect( QRectF( originalItem->transform().dx() + moveRectX,
00580                                           originalItem->transform().dy() + moveRectY,
00581                                           originalItem->rect().width(), originalItem->rect().height() ) );
00582         changeComposerItem->updateItem();
00583       }
00584     }
00585     return;
00586     case QgsComposerItem::NoAction:
00587       break;
00588   }
00589 
00590   if ( !changeComposerItem )
00591   {
00592     QTransform itemTransform;
00593     itemTransform.translate( originalItem->transform().dx() + mx, originalItem->transform().dy() + my );
00594     changeItem->setTransform( itemTransform );
00595     QRectF itemRect( 0, 0, originalItem->rect().width() + rx,  originalItem->rect().height() + ry );
00596     changeItem->setRect( itemRect );
00597   }
00598   else //for composer items, we prefer setSceneRect as subclasses can implement custom behaviour (e.g. item group)
00599   {
00600     changeComposerItem->setSceneRect( QRectF( originalItem->transform().dx() + mx, originalItem->transform().dy() + my,
00601                                       originalItem->rect().width() + rx, originalItem->rect().height() + ry ) );
00602     changeComposerItem->updateItem();
00603   }
00604 }
00605 
00606 void QgsComposerItem::drawSelectionBoxes( QPainter* p )
00607 {
00608   if ( !mComposition )
00609   {
00610     return;
00611   }
00612 
00613   if ( mComposition->plotStyle() == QgsComposition::Preview )
00614   {
00615     //size of symbol boxes depends on zoom level in composer view
00616     double rectHandlerSize = rectHandlerBorderTolerance();
00617     double sizeLockSymbol = lockSymbolSize();
00618 
00619     if ( mItemPositionLocked )
00620     {
00621       //draw lock symbol at upper left edge. Use QImage to be independent of the graphic system
00622       QString lockIconPath = QgsApplication::activeThemePath() + "/mIconLock.png";
00623       if ( !QFile::exists( lockIconPath ) )
00624       {
00625         lockIconPath = QgsApplication::defaultThemePath() + "/mIconLock.png";
00626       }
00627 
00628       QImage lockImage( lockIconPath );
00629       if ( !lockImage.isNull() )
00630       {
00631         p->drawImage( QRectF( 0, 0, sizeLockSymbol, sizeLockSymbol ), lockImage, QRectF( 0, 0, lockImage.width(), lockImage.height() ) );
00632       }
00633     }
00634     else //draw blue squares
00635     {
00636       p->setPen( QColor( 50, 100, 120, 200 ) );
00637       p->setBrush( QColor( 200, 200, 210, 120 ) );
00638       p->drawRect( QRectF( 0, 0, rectHandlerSize, rectHandlerSize ) );
00639       p->drawRect( QRectF( rect().width() - rectHandlerSize, 0, rectHandlerSize, rectHandlerSize ) );
00640       p->drawRect( QRectF( rect().width() - rectHandlerSize, rect().height() - rectHandlerSize, rectHandlerSize, rectHandlerSize ) );
00641       p->drawRect( QRectF( 0, rect().height() - rectHandlerSize, rectHandlerSize, rectHandlerSize ) );
00642     }
00643   }
00644 }
00645 
00646 void QgsComposerItem::drawFrame( QPainter* p )
00647 {
00648   if ( mFrame && p )
00649   {
00650     p->setPen( pen() );
00651     p->setBrush( Qt::NoBrush );
00652     p->setRenderHint( QPainter::Antialiasing, true );
00653     p->drawRect( QRectF( 0, 0, rect().width(), rect().height() ) );
00654   }
00655 }
00656 
00657 void QgsComposerItem::move( double dx, double dy )
00658 {
00659   QTransform t = transform();
00660   QRectF newSceneRect( t.dx() + dx, t.dy() + dy, rect().width(), rect().height() );
00661   setSceneRect( newSceneRect );
00662 }
00663 
00664 void QgsComposerItem::setItemPosition( double x, double y, ItemPositionMode itemPoint )
00665 {
00666   double width = rect().width();
00667   double height = rect().height();
00668   setItemPosition( x, y, width, height, itemPoint );
00669 }
00670 
00671 void QgsComposerItem::setItemPosition( double x, double y, double width, double height, ItemPositionMode itemPoint )
00672 {
00673   double upperLeftX = x;
00674   double upperLeftY = y;
00675 
00676   //adjust x-coordinate if placement is not done to a left point
00677   if ( itemPoint == UpperMiddle || itemPoint == Middle || itemPoint == LowerMiddle )
00678   {
00679     upperLeftX -= width / 2.0;
00680   }
00681   else if ( itemPoint == UpperRight || itemPoint == MiddleRight || itemPoint == LowerRight )
00682   {
00683     upperLeftX -= width;
00684   }
00685 
00686   //adjust y-coordinate if placement is not done to an upper point
00687   if ( itemPoint == MiddleLeft || itemPoint == Middle || itemPoint == MiddleRight )
00688   {
00689     upperLeftY -= height / 2.0;
00690   }
00691   else if ( itemPoint == LowerLeft || itemPoint == LowerMiddle || itemPoint == LowerRight )
00692   {
00693     upperLeftY -= height;
00694   }
00695 
00696   setSceneRect( QRectF( upperLeftX, upperLeftY, width, height ) );
00697 }
00698 
00699 void QgsComposerItem::setSceneRect( const QRectF& rectangle )
00700 {
00701   //setRect in item coordinates
00702   double newWidth = rectangle.width();
00703   double newHeight = rectangle.height();
00704   double xTranslation = rectangle.x();
00705   double yTranslation = rectangle.y();
00706 
00707   //correction if width and/or height are negative
00708   if ( rectangle.width() < 0 )
00709   {
00710     newWidth = - rectangle.width();
00711     xTranslation -= newWidth;
00712   }
00713 
00714   if ( rectangle.height() < 0 )
00715   {
00716     newHeight = - rectangle.height();
00717     yTranslation -= newHeight;
00718   }
00719 
00720   QRectF newRect( 0, 0, newWidth, newHeight );
00721   QGraphicsRectItem::setRect( newRect );
00722 
00723   //set up transformation matrix for item coordinates
00724   QTransform t;
00725   t.translate( xTranslation, yTranslation );
00726   setTransform( t );
00727 }
00728 
00729 void QgsComposerItem::drawBackground( QPainter* p )
00730 {
00731   if ( p )
00732   {
00733     p->setBrush( brush() );
00734     p->setPen( Qt::NoPen );
00735     p->setRenderHint( QPainter::Antialiasing, true );
00736     p->drawRect( QRectF( 0, 0, rect().width(), rect().height() ) );
00737   }
00738 }
00739 
00740 void QgsComposerItem::hoverMoveEvent( QGraphicsSceneHoverEvent * event )
00741 {
00742   if ( isSelected() )
00743   {
00744     setCursor( cursorForPosition( event->pos() ) );
00745   }
00746   else
00747   {
00748     setCursor( Qt::ArrowCursor );
00749   }
00750 }
00751 
00752 void QgsComposerItem::drawText( QPainter* p, double x, double y, const QString& text, const QFont& font ) const
00753 {
00754   QFont textFont = scaledFontPixelSize( font );
00755 
00756   p->save();
00757   p->setFont( textFont );
00758   p->setPen( QColor( 0, 0, 0 ) ); //draw text always in black
00759   double scaleFactor = 1.0 / FONT_WORKAROUND_SCALE;
00760   p->scale( scaleFactor, scaleFactor );
00761   p->drawText( QPointF( x * FONT_WORKAROUND_SCALE, y * FONT_WORKAROUND_SCALE ), text );
00762   p->restore();
00763 }
00764 
00765 void QgsComposerItem::drawText( QPainter* p, const QRectF& rect, const QString& text, const QFont& font, Qt::AlignmentFlag halignement, Qt::AlignmentFlag valignment ) const
00766 {
00767   QFont textFont = scaledFontPixelSize( font );
00768 
00769   QRectF scaledRect( rect.x() * FONT_WORKAROUND_SCALE, rect.y() * FONT_WORKAROUND_SCALE,
00770                      rect.width() * FONT_WORKAROUND_SCALE, rect.height() * FONT_WORKAROUND_SCALE );
00771 
00772   p->save();
00773   p->setFont( textFont );
00774   double scaleFactor = 1.0 / FONT_WORKAROUND_SCALE;
00775   p->scale( scaleFactor, scaleFactor );
00776   p->drawText( scaledRect, halignement | valignment | Qt::TextWordWrap, text );
00777   p->restore();
00778 }
00779 void QgsComposerItem::drawArrowHead( QPainter* p, double x, double y, double angle, double arrowHeadWidth ) const
00780 {
00781   if ( !p )
00782   {
00783     return;
00784   }
00785   double angleRad = angle / 180.0 * M_PI;
00786   QPointF middlePoint( x, y );
00787   //rotate both arrow points
00788   QPointF p1 = QPointF( -arrowHeadWidth / 2.0, arrowHeadWidth );
00789   QPointF p2 = QPointF( arrowHeadWidth / 2.0, arrowHeadWidth );
00790 
00791   QPointF p1Rotated, p2Rotated;
00792   p1Rotated.setX( p1.x() * cos( angleRad ) + p1.y() * -sin( angleRad ) );
00793   p1Rotated.setY( p1.x() * sin( angleRad ) + p1.y() * cos( angleRad ) );
00794   p2Rotated.setX( p2.x() * cos( angleRad ) + p2.y() * -sin( angleRad ) );
00795   p2Rotated.setY( p2.x() * sin( angleRad ) + p2.y() * cos( angleRad ) );
00796 
00797   QPolygonF arrowHeadPoly;
00798   arrowHeadPoly << middlePoint;
00799   arrowHeadPoly << QPointF( middlePoint.x() + p1Rotated.x(), middlePoint.y() + p1Rotated.y() );
00800   arrowHeadPoly << QPointF( middlePoint.x() + p2Rotated.x(), middlePoint.y() + p2Rotated.y() );
00801 
00802   p->save();
00803 
00804   QPen arrowPen = p->pen();
00805   arrowPen.setJoinStyle( Qt::RoundJoin );
00806   QBrush arrowBrush = p->brush();
00807   arrowBrush.setStyle( Qt::SolidPattern );
00808   p->setPen( arrowPen );
00809   p->setBrush( arrowBrush );
00810   arrowBrush.setStyle( Qt::SolidPattern );
00811   p->drawPolygon( arrowHeadPoly );
00812 
00813   p->restore();
00814 }
00815 
00816 double QgsComposerItem::textWidthMillimeters( const QFont& font, const QString& text ) const
00817 {
00818   QFont metricsFont = scaledFontPixelSize( font );
00819   QFontMetrics fontMetrics( metricsFont );
00820   return ( fontMetrics.width( text ) / FONT_WORKAROUND_SCALE );
00821 }
00822 
00823 double QgsComposerItem::fontHeightCharacterMM( const QFont& font, const QChar& c ) const
00824 {
00825   QFont metricsFont = scaledFontPixelSize( font );
00826   QFontMetricsF fontMetrics( metricsFont );
00827   return ( fontMetrics.boundingRect( c ).height() / FONT_WORKAROUND_SCALE );
00828 }
00829 
00830 double QgsComposerItem::fontAscentMillimeters( const QFont& font ) const
00831 {
00832   QFont metricsFont = scaledFontPixelSize( font );
00833   QFontMetricsF fontMetrics( metricsFont );
00834   return ( fontMetrics.ascent() / FONT_WORKAROUND_SCALE );
00835 }
00836 
00837 double QgsComposerItem::pixelFontSize( double pointSize ) const
00838 {
00839   return ( pointSize * 0.3527 );
00840 }
00841 
00842 QFont QgsComposerItem::scaledFontPixelSize( const QFont& font ) const
00843 {
00844   QFont scaledFont = font;
00845   double pixelSize = pixelFontSize( font.pointSizeF() ) * FONT_WORKAROUND_SCALE + 0.5;
00846   scaledFont.setPixelSize( pixelSize );
00847   return scaledFont;
00848 }
00849 
00850 double QgsComposerItem::angle( const QPointF& p1, const QPointF& p2 ) const
00851 {
00852   double xDiff = p2.x() - p1.x();
00853   double yDiff = p2.y() - p1.y();
00854   double length = sqrt( xDiff * xDiff + yDiff * yDiff );
00855   if ( length <= 0 )
00856   {
00857     return 0;
00858   }
00859 
00860   double angle = acos(( -yDiff * length ) / ( length * length ) ) * 180 / M_PI;
00861   if ( xDiff < 0 )
00862   {
00863     return ( 360 - angle );
00864   }
00865   return angle;
00866 }
00867 
00868 double QgsComposerItem::horizontalViewScaleFactor() const
00869 {
00870   double result = -1;
00871   if ( scene() )
00872   {
00873     QList<QGraphicsView*> viewList = scene()->views();
00874     if ( viewList.size() > 0 ) //if not, probably this function was called from non-gui code
00875     {
00876       QGraphicsView* currentView = viewList.at( 0 );
00877       if ( currentView->isVisible() )
00878       {
00879         result = currentView->transform().m11();
00880         mLastValidViewScaleFactor = result;
00881       }
00882     }
00883   }
00884   return result;
00885 }
00886 
00887 double QgsComposerItem::rectHandlerBorderTolerance() const
00888 {
00889   //size of symbol boxes depends on zoom level in composer view
00890   double viewScaleFactor = horizontalViewScaleFactor();
00891   double rectHandlerSize = 10.0 / viewScaleFactor;
00892 
00893   //make sure the boxes don't get too large
00894   if ( rectHandlerSize > ( rect().width() / 3 ) )
00895   {
00896     rectHandlerSize = rect().width() / 3;
00897   }
00898   if ( rectHandlerSize > ( rect().height() / 3 ) )
00899   {
00900     rectHandlerSize = rect().height() / 3;
00901   }
00902   return rectHandlerSize;
00903 }
00904 
00905 double QgsComposerItem::lockSymbolSize() const
00906 {
00907   double lockSymbolSize = 20.0 / horizontalViewScaleFactor();
00908 
00909   if ( lockSymbolSize > ( rect().width() / 3 ) )
00910   {
00911     lockSymbolSize = rect().width() / 3;
00912   }
00913   if ( lockSymbolSize > ( rect().height() / 3 ) )
00914   {
00915     lockSymbolSize = rect().height() / 3;
00916   }
00917   return lockSymbolSize;
00918 }
00919 
00920 void QgsComposerItem::updateCursor( const QPointF& itemPos )
00921 {
00922   setCursor( cursorForPosition( itemPos ) );
00923 }
00924 
00925 void QgsComposerItem::setRotation( double r )
00926 {
00927   if ( r > 360 )
00928   {
00929     mRotation = (( int )r ) % 360;
00930   }
00931   else
00932   {
00933     mRotation = r;
00934   }
00935   emit rotationChanged( r );
00936   update();
00937 }
00938 
00939 bool QgsComposerItem::imageSizeConsideringRotation( double& width, double& height ) const
00940 {
00941   if ( qAbs( mRotation ) <= 0.0 ) //width and height stays the same if there is no rotation
00942   {
00943     return true;
00944   }
00945 
00946   double x1 = 0;
00947   double y1 = 0;
00948   double x2 = width;
00949   double y2 = 0;
00950   double x3 = width;
00951   double y3 = height;
00952   double x4 = 0;
00953   double y4 = height;
00954   double midX = width / 2.0;
00955   double midY = height / 2.0;
00956 
00957   if ( !cornerPointOnRotatedAndScaledRect( x1, y1, width, height ) )
00958   {
00959     return false;
00960   }
00961   if ( !cornerPointOnRotatedAndScaledRect( x2, y2, width, height ) )
00962   {
00963     return false;
00964   }
00965   if ( !cornerPointOnRotatedAndScaledRect( x3, y3, width, height ) )
00966   {
00967     return false;
00968   }
00969   if ( !cornerPointOnRotatedAndScaledRect( x4, y4, width, height ) )
00970   {
00971     return false;
00972   }
00973 
00974 
00975   //assume points 1 and 3 are on the rectangle boundaries. Calculate 2 and 4.
00976   double distM1 = sqrt(( x1 - midX ) * ( x1 - midX ) + ( y1 - midY ) * ( y1 - midY ) );
00977   QPointF p2 = QgsSymbolLayerV2Utils::pointOnLineWithDistance( QPointF( midX, midY ), QPointF( x2, y2 ), distM1 );
00978 
00979   if ( p2.x() < width && p2.x() > 0 && p2.y() < height && p2.y() > 0 )
00980   {
00981     width = sqrt(( p2.x() - x1 ) * ( p2.x() - x1 ) + ( p2.y() - y1 ) * ( p2.y() - y1 ) );
00982     height = sqrt(( x3 - p2.x() ) * ( x3 - p2.x() ) + ( y3 - p2.y() ) * ( y3 - p2.y() ) );
00983     return true;
00984   }
00985 
00986   //else assume that points 2 and 4 are on the rectangle boundaries. Calculate 1 and 3
00987   double distM2 = sqrt(( x2 - midX ) * ( x2 - midX ) + ( y2 - midY ) * ( y2 - midY ) );
00988   QPointF p1 = QgsSymbolLayerV2Utils::pointOnLineWithDistance( QPointF( midX, midY ), QPointF( x1, y1 ), distM2 );
00989   QPointF p3 = QgsSymbolLayerV2Utils::pointOnLineWithDistance( QPointF( midX, midY ), QPointF( x3, y3 ), distM2 );
00990   width = sqrt(( x2 - p1.x() ) * ( x2 - p1.x() ) + ( y2 - p1.y() ) * ( y2 - p1.y() ) );
00991   height = sqrt(( p3.x() - x2 ) * ( p3.x() - x2 ) + ( p3.y() - y2 ) * ( p3.y() - y2 ) );
00992   return true;
00993 
00994 
00995 #if 0
00996   double x1 = 0;
00997   double y1 = 0;
00998   double x2 = width;
00999   double y2 = 0;
01000   double x3 = width;
01001   double y3 = height;
01002 
01003   if ( !cornerPointOnRotatedAndScaledRect( x1, y1, width, height ) )
01004   {
01005     return false;
01006   }
01007   if ( !cornerPointOnRotatedAndScaledRect( x2, y2, width, height ) )
01008   {
01009     return false;
01010   }
01011   if ( !cornerPointOnRotatedAndScaledRect( x3, y3, width, height ) )
01012   {
01013     return false;
01014   }
01015 
01016   width = sqrt(( x2 - x1 ) * ( x2 - x1 ) + ( y2 - y1 ) * ( y2 - y1 ) );
01017   height = sqrt(( x3 - x2 ) * ( x3 - x2 ) + ( y3 - y2 ) * ( y3 - y2 ) );
01018   return true;
01019 #endif //0
01020 }
01021 
01022 bool QgsComposerItem::cornerPointOnRotatedAndScaledRect( double& x, double& y, double width, double height ) const
01023 {
01024   //first rotate point clockwise
01025   double rotToRad = mRotation * M_PI / 180.0;
01026   QPointF midpoint( width / 2.0, height / 2.0 );
01027   double xVector = x - midpoint.x();
01028   double yVector = y - midpoint.y();
01029   //double xRotated = cos(rotToRad) * xVector + sin(rotToRad) * yVector;
01030   //double yRotated = -sin(rotToRad) * xVector + cos(rotToRad) * yVector;
01031   double xRotated = cos( rotToRad ) * xVector - sin( rotToRad ) * yVector;
01032   double yRotated = sin( rotToRad ) * xVector + cos( rotToRad ) * yVector;
01033 
01034   //create line from midpoint to rotated point
01035   QLineF line( midpoint.x(), midpoint.y(), midpoint.x() + xRotated, midpoint.y() + yRotated );
01036 
01037   //intersect with all four borders and return result
01038   QList<QLineF> borders;
01039   borders << QLineF( 0, 0, width, 0 );
01040   borders << QLineF( width, 0, width, height );
01041   borders << QLineF( width, height, 0, height );
01042   borders << QLineF( 0, height, 0, 0 );
01043 
01044   QList<QLineF>::const_iterator it = borders.constBegin();
01045   QPointF intersectionPoint;
01046 
01047   for ( ; it != borders.constEnd(); ++it )
01048   {
01049     if ( line.intersect( *it, &intersectionPoint ) == QLineF::BoundedIntersection )
01050     {
01051       x = intersectionPoint.x();
01052       y = intersectionPoint.y();
01053       return true;
01054     }
01055   }
01056   return false;
01057 }
01058 
01059 void QgsComposerItem::sizeChangedByRotation( double& width, double& height )
01060 {
01061   if ( mRotation == 0.0 )
01062   {
01063     return;
01064   }
01065 
01066   //vector to p1
01067   double x1 = -width / 2.0;
01068   double y1 = -height / 2.0;
01069   rotate( mRotation, x1, y1 );
01070   //vector to p2
01071   double x2 = width / 2.0;
01072   double y2 = -height / 2.0;
01073   rotate( mRotation, x2, y2 );
01074   //vector to p3
01075   double x3 = width / 2.0;
01076   double y3 = height / 2.0;
01077   rotate( mRotation, x3, y3 );
01078   //vector to p4
01079   double x4 = -width / 2.0;
01080   double y4 = height / 2.0;
01081   rotate( mRotation, x4, y4 );
01082 
01083   //double midpoint
01084   QPointF midpoint( width / 2.0, height / 2.0 );
01085 
01086   QPolygonF rotatedRectPoly;
01087   rotatedRectPoly << QPointF( midpoint.x() + x1, midpoint.y() + y1 );
01088   rotatedRectPoly << QPointF( midpoint.x() + x2, midpoint.y() + y2 );
01089   rotatedRectPoly << QPointF( midpoint.x() + x3, midpoint.y() + y3 );
01090   rotatedRectPoly << QPointF( midpoint.x() + x4, midpoint.y() + y4 );
01091   QRectF boundingRect = rotatedRectPoly.boundingRect();
01092   width = boundingRect.width();
01093   height = boundingRect.height();
01094 }
01095 
01096 void QgsComposerItem::rotate( double angle, double& x, double& y ) const
01097 {
01098   double rotToRad = angle * M_PI / 180.0;
01099   double xRot, yRot;
01100   xRot = x * cos( rotToRad ) - y * sin( rotToRad );
01101   yRot = x * sin( rotToRad ) + y * cos( rotToRad );
01102   x = xRot;
01103   y = yRot;
01104 }
01105 
01106 void QgsComposerItem::repaint()
01107 {
01108   update();
01109 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines