Quantum GIS API Documentation
1.8
|
00001 /*************************************************************************** 00002 qgscomposeritem.cpp 00003 ------------------- 00004 begin : January 2005 00005 copyright : (C) 2005 by Radim Blazek 00006 email : [email protected] 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 }