File indexing completed on 2024-12-22 04:17:35
0001 /*************************************************************************** 0002 * * 0003 * copyright : (C) 2007 The University of Toronto * 0004 * netterfield@astro.utoronto.ca * 0005 * * 0006 * This program is free software; you can redistribute it and/or modify * 0007 * it under the terms of the GNU General Public License as published by * 0008 * the Free Software Foundation; either version 2 of the License, or * 0009 * (at your option) any later version. * 0010 * * 0011 ***************************************************************************/ 0012 0013 0014 #include "lineitem.h" 0015 #include "lineitemdialog.h" 0016 0017 #include "view.h" 0018 0019 #include <debug.h> 0020 0021 #include <QDebug> 0022 #include <QGraphicsScene> 0023 #include <QGraphicsSceneContextMenuEvent> 0024 0025 #include "plotitem.h" 0026 #include "cartesianrenderitem.h" 0027 0028 namespace Kst { 0029 0030 LineItem::LineItem(View *parent) 0031 : ViewItem(parent), _lineEditDialog(0) { 0032 _created = false; 0033 setTypeName(tr("Line", "a line in a picture")); 0034 setAllowedGrips(RightMidGrip | LeftMidGrip); 0035 setAllowedGripModes(Resize); 0036 setAllowsLayout(false); 0037 QPen p = pen(); 0038 p.setWidthF(1); 0039 storePen(p); 0040 applyDialogDefaultsStroke(); 0041 applyDialogDefaultsLockPosToData(); 0042 } 0043 0044 0045 LineItem::~LineItem() { 0046 } 0047 0048 0049 void LineItem::edit() { 0050 if (!_lineEditDialog) { 0051 _lineEditDialog = new LineItemDialog(this); 0052 } 0053 _lineEditDialog->show(); 0054 _lineEditDialog->raise(); 0055 } 0056 0057 0058 void LineItem::paint(QPainter *painter) { 0059 painter->drawLine(line()); 0060 } 0061 0062 0063 void LineItem::save(QXmlStreamWriter &xml) { 0064 if (isVisible()) { 0065 xml.writeStartElement("line"); 0066 ViewItem::save(xml); 0067 xml.writeEndElement(); 0068 } 0069 } 0070 0071 0072 QLineF LineItem::line() const { 0073 return QLineF(rect().left(), rect().center().y(), rect().right(), rect().center().y()); 0074 } 0075 0076 0077 QPainterPath LineItem::leftMidGrip() const { 0078 QRectF bound = gripBoundingRect(); 0079 QRectF grip = QRectF(bound.topLeft(), sizeOfGrip()); 0080 grip.moveCenter(QPointF(grip.center().x(), bound.center().y())); 0081 QPainterPath path; 0082 path.addEllipse(grip); 0083 0084 return path; 0085 } 0086 0087 0088 QPainterPath LineItem::rightMidGrip() const { 0089 QRectF bound = gripBoundingRect(); 0090 QRectF grip = QRectF(bound.topRight() - QPointF(sizeOfGrip().width(), 0), sizeOfGrip()); 0091 grip.moveCenter(QPointF(grip.center().x(), bound.center().y())); 0092 QPainterPath path; 0093 path.addEllipse(grip); 0094 0095 return path; 0096 } 0097 0098 0099 QPainterPath LineItem::grips() const { 0100 QPainterPath grips; 0101 grips.addPath(leftMidGrip()); 0102 grips.addPath(rightMidGrip()); 0103 return grips; 0104 } 0105 0106 0107 QPointF LineItem::centerOfRotation() const { 0108 if (activeGrip() == RightMidGrip) 0109 return line().p1(); 0110 else if (activeGrip() == LeftMidGrip) 0111 return line().p2(); 0112 0113 // when creating the object with the mouse, use p1 as the rotation point. 0114 // otherwise, use the center 0115 if (_created) { 0116 return QPointF((line().x1()+line().x2())*0.5, (line().y1()+line().y2())*0.5); 0117 } else { 0118 return QPointF(line().p1()); 0119 } 0120 } 0121 0122 0123 void LineItem::creationPolygonChanged(View::CreationEvent event) { 0124 if (event == View::EscapeEvent) { 0125 ViewItem::creationPolygonChanged(event); 0126 return; 0127 } 0128 0129 if (event == View::MousePress) { 0130 const QPolygonF poly = mapFromScene(view()->creationPolygon(View::MousePress)); 0131 setPos(poly.first().x(), poly.first().y()); 0132 setViewRect(QRectF(0.0, -sizeOfGrip().height()*0.5, 0.0, sizeOfGrip().height())); 0133 view()->scene()->addItem(this); 0134 return; 0135 } 0136 0137 if (event == View::MouseMove) { 0138 const QPolygonF poly = mapFromScene(view()->creationPolygon(View::MouseMove)); 0139 if (!rect().isEmpty()) { 0140 rotateTowards(line().p2(), poly.last()); 0141 } 0142 QRectF r = rect(); 0143 r.setSize(QSizeF(QLineF(line().p1(), poly.last()).length(), r.height())); 0144 setViewRect(r); 0145 return; 0146 } 0147 0148 if (event == View::MouseRelease) { 0149 const QPolygonF poly = mapFromScene(view()->creationPolygon(View::MouseRelease)); 0150 view()->disconnect(this, SLOT(deleteLater())); //Don't delete ourself 0151 view()->disconnect(this, SLOT(creationPolygonChanged(View::CreationEvent))); 0152 view()->setMouseMode(View::Default); 0153 updateViewItemParent(); 0154 _created = true; 0155 emit creationComplete(); 0156 return; 0157 } 0158 } 0159 0160 0161 void LineItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event) { 0162 0163 if (view()->viewMode() == View::Data) { 0164 event->ignore(); 0165 return; 0166 } 0167 0168 if (!dragStartPosition.isNull() && event->buttons() & Qt::LeftButton) { 0169 if (view()->mouseMode() == View::Move) { 0170 startDragging(event->widget(), dragStartPosition.toPoint()); 0171 return; 0172 } 0173 } 0174 0175 if (view()->mouseMode() == View::Default) { 0176 if (gripMode() == ViewItem::Move || activeGrip() == NoGrip) { 0177 view()->setMouseMode(View::Move); 0178 view()->undoStack()->beginMacro(tr("Move")); 0179 } else if (gripMode() == ViewItem::Resize) { 0180 view()->setMouseMode(View::Resize); 0181 view()->undoStack()->beginMacro(tr("Resize")); 0182 } 0183 } 0184 0185 if (activeGrip() == NoGrip) 0186 return QGraphicsRectItem::mouseMoveEvent(event); 0187 0188 double width = 0; 0189 double height = 0; 0190 double theta = 0; 0191 QPointF centerP; 0192 QPointF P1, P2; 0193 0194 if (gripMode() == ViewItem::Resize) { 0195 switch(activeGrip()) { 0196 case RightMidGrip: 0197 P1 = mapToParent(QPoint(rect().left(), rect().center().y())); 0198 P2 = mapToParent(event->pos()); 0199 break; 0200 case LeftMidGrip: 0201 P1 = mapToParent(event->pos()); 0202 P2 = mapToParent(QPoint(rect().right(), rect().center().y())); 0203 break; 0204 default: 0205 return; 0206 } 0207 centerP = (P1 + P2) * 0.5; 0208 theta = atan2(P2.y() - P1.y(), P2.x() - P1.x()); 0209 height = rect().height(); 0210 double dx = P1.x() - P2.x(); 0211 double dy = P1.y() - P2.y(); 0212 width = sqrt(dx*dx + dy*dy)+1.0; 0213 0214 if (activeGrip() == RightMidGrip) { 0215 setPos(P1); 0216 setViewRect(0,-height*0.5,width,height); 0217 } else if (activeGrip() == LeftMidGrip) { 0218 setPos(P2); 0219 setViewRect(-width, -height*0.5, width, height); 0220 } 0221 //setPos(centerP.x(), centerP.y()); 0222 //setViewRect(-width*0.5, -height*0.5, width, height); 0223 0224 QTransform transform; 0225 transform.rotateRadians(theta); 0226 0227 setTransform(transform); 0228 updateRelativeSize(true); 0229 } 0230 } 0231 0232 0233 void LineItem::updateDataRelativeRect(bool force) { 0234 CartesianRenderItem* plot = dynamic_cast<CartesianRenderItem*>(parentViewItem()); 0235 if (plot) { 0236 if ((!lockPosToData()) || force) { 0237 QPointF P1 = (rect().topLeft() + rect().bottomLeft())/2; 0238 QPointF P2 = (rect().topRight() + rect().bottomRight())/2; 0239 _dataRelativeRect.setTopLeft(plot->plotItem()->mapToProjection(mapToParent(P1))); 0240 _dataRelativeRect.setBottomRight(plot->plotItem()->mapToProjection(mapToParent(P2))); 0241 } 0242 } 0243 } 0244 0245 0246 void LineItem::applyDataLockedDimensions() { 0247 PlotRenderItem *render_item = dynamic_cast<PlotRenderItem *>(parentViewItem()); 0248 if (render_item) { 0249 qreal parentWidth = render_item->width(); 0250 qreal parentHeight = render_item->height(); 0251 qreal parentX = render_item->rect().x(); 0252 qreal parentY = render_item->rect().y(); 0253 qreal parentDX = render_item->plotItem()->xMax() - render_item->plotItem()->xMin(); 0254 qreal parentDY = render_item->plotItem()->yMax() - render_item->plotItem()->yMin(); 0255 0256 QPointF drP1 = _dataRelativeRect.topLeft(); 0257 QPointF drP2 = _dataRelativeRect.bottomRight(); 0258 0259 QPointF P1(parentX + parentWidth*(drP1.x()-render_item->plotItem()->xMin())/parentDX, 0260 parentY + parentHeight*(render_item->plotItem()->yMax() - drP1.y())/parentDY); 0261 QPointF P2(parentX + parentWidth*(drP2.x()-render_item->plotItem()->xMin())/parentDX, 0262 parentY + parentHeight*(render_item->plotItem()->yMax() - drP2.y())/parentDY); 0263 0264 QPointF centerP = (P1 + P2) * 0.5; 0265 qreal theta = atan2(P2.y() - P1.y(), P2.x() - P1.x()); 0266 qreal height = rect().height(); 0267 qreal dx = P1.x() - P2.x(); 0268 qreal dy = P1.y() - P2.y(); 0269 qreal width = sqrt(dx*dx + dy*dy)+1.0; 0270 0271 setPos(centerP.x(), centerP.y()); 0272 setViewRect(-width*0.5, -height*0.5, width, height); 0273 0274 QTransform transform; 0275 transform.rotateRadians(theta); 0276 0277 setTransform(transform); 0278 updateRelativeSize(); 0279 0280 } else { 0281 qDebug() << "apply data locked dimensions called without a render item (!)"; 0282 } 0283 } 0284 0285 void LineItem::setEndpoints(double x1, double y1, double x2, double y2) 0286 { 0287 if (lockPosToData()){ 0288 QRectF dr; 0289 dr.setTopLeft(QPointF(x1,y1)); 0290 dr.setBottomRight(QPointF(x2,y2)); 0291 0292 setDataRelativeRect(dr); 0293 0294 applyDataLockedDimensions(); 0295 0296 } else { 0297 //QRectF parentRect = parentRect(); 0298 qreal parentWidth = parentRect().width(); 0299 qreal parentHeight = parentRect().height(); 0300 qreal parentX = parentRect().x(); 0301 qreal parentY = parentRect().y(); 0302 0303 x1 = x1*parentWidth + parentX; 0304 x2 = x2*parentWidth + parentX; 0305 y1 = y1*parentHeight + parentY; 0306 y2 = y2*parentHeight + parentY; 0307 qreal dx = x2-x1; 0308 qreal dy = y2-y1; 0309 0310 qreal w = sqrt(dx*dx + dy*dy); 0311 qreal h = height(); 0312 0313 setPos(x1,y1); 0314 setViewRect(0,-h/2,w,h); 0315 0316 qreal rotation = atan2(dy,dx); 0317 0318 QTransform transform; 0319 transform.rotateRadians(rotation); 0320 0321 setTransform(transform); 0322 updateRelativeSize(); 0323 } 0324 } 0325 0326 void LineItem::mousePressEvent(QGraphicsSceneMouseEvent *event) { 0327 ViewItem::mousePressEvent(event); 0328 } 0329 0330 0331 void LineItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) { 0332 ViewItem::mouseReleaseEvent(event); 0333 } 0334 0335 0336 void LineItem::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event) { 0337 ViewItem::mouseDoubleClickEvent(event); 0338 } 0339 0340 0341 void LineItem::hoverMoveEvent(QGraphicsSceneHoverEvent *event) { 0342 QGraphicsRectItem::hoverMoveEvent(event); 0343 if (isSelected()) { 0344 QPointF p = event->pos(); 0345 if ((isAllowed(RightMidGrip) && rightMidGrip().contains(p)) || (isAllowed(LeftMidGrip) && leftMidGrip().contains(p))) { 0346 view()->setCursor(Qt::CrossCursor); 0347 } else { 0348 view()->setCursor(Qt::SizeAllCursor); 0349 } 0350 } else { 0351 //view()->setCursor(Qt::SizeAllCursor); 0352 } 0353 } 0354 0355 0356 void LineItem::updateChildGeometry(const QRectF &oldParentRect, const QRectF &newParentRect) { 0357 // parent has been resized: update the line's dimensions: 0358 0359 // we would like to have lines in terms of relative endpoint locations, 0360 // but instead they are in terms of length (relative to parent width) 0361 // and angle, relative to the parent. 0362 qreal theta = rotationAngle()*M_PI/180.0; 0363 qreal oldL = relativeWidth()*oldParentRect.width(); 0364 0365 // we want to keep the endpoints fixed relative to the parent, so 0366 // we need to calculate new lengths and angles. 0367 qreal newDx = cos(theta)*oldL*newParentRect.width()/oldParentRect.width(); 0368 qreal newDy = sin(theta)*oldL*newParentRect.height()/oldParentRect.height(); 0369 qreal newWidth = sqrt(newDx*newDx + newDy*newDy); 0370 QTransform transform; 0371 transform.rotate(atan2(newDy, newDx)*180.0/M_PI); 0372 0373 // my brain hurts less for rotations when we center the object at 0,0 0374 QRectF itemRect(-newWidth*0.5, -rect().height()*0.5, 0375 newWidth, rect().height()); 0376 0377 // we don't now what the parents's origin is, so, add .x() and .y() 0378 setPos(relativeCenter().x() * newParentRect.width() + newParentRect.x(), 0379 relativeCenter().y() * newParentRect.height()+ newParentRect.y()); 0380 setViewRect(itemRect, true); 0381 0382 setTransform(transform); 0383 setRelativeWidth(newWidth / newParentRect.width()); 0384 } 0385 0386 0387 void CreateLineCommand::createItem() { 0388 _item = new LineItem(_view); 0389 _view->setCursor(Qt::CrossCursor); 0390 0391 CreateCommand::createItem(); 0392 } 0393 0394 0395 LineItemFactory::LineItemFactory() 0396 : GraphicsFactory() { 0397 registerFactory("line", this); 0398 } 0399 0400 0401 LineItemFactory::~LineItemFactory() { 0402 } 0403 0404 0405 ViewItem* LineItemFactory::generateGraphics(QXmlStreamReader& xml, ObjectStore *store, View *view, ViewItem *parent) { 0406 LineItem *rc = 0; 0407 while (!xml.atEnd()) { 0408 bool validTag = true; 0409 if (xml.isStartElement()) { 0410 if (!rc && xml.name().toString() == "line") { 0411 Q_ASSERT(!rc); 0412 rc = new LineItem(view); 0413 if (parent) { 0414 rc->setParentViewItem(parent); 0415 } 0416 // Add any new specialized LineItem Properties here. 0417 } else { 0418 Q_ASSERT(rc); 0419 if (!rc->parse(xml, validTag) && validTag) { 0420 ViewItem *i = GraphicsFactory::parse(xml, store, view, rc); 0421 if (!i) { 0422 } 0423 } 0424 } 0425 } else if (xml.isEndElement()) { 0426 if (xml.name().toString() == "line") { 0427 break; 0428 } else { 0429 validTag = false; 0430 } 0431 } 0432 if (!validTag) { 0433 qDebug("invalid Tag\n"); 0434 Debug::self()->log(QObject::tr("Error creating line object from Kst file."), Debug::Warning); 0435 delete rc; 0436 return 0; 0437 } 0438 xml.readNext(); 0439 } 0440 0441 return rc; 0442 } 0443 0444 } 0445 0446 // vim: ts=2 sw=2 et