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