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