Quantum GIS API Documentation
1.7.4
|
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 }