File indexing completed on 2024-05-26 04:38:41
0001 /* This file is part of the TikZKit project. 0002 * 0003 * Copyright (C) 2014 Dominik Haumann <dhaumann@kde.org> 0004 * 0005 * This library is free software; you can redistribute it and/or modify 0006 * it under the terms of the GNU Library General Public License as published 0007 * by the Free Software Foundation, either version 2 of the License, or 0008 * (at your option) any later version. 0009 * 0010 * This library is distributed in the hope that it will be useful, 0011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 0012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 0013 * GNU Library General Public License for more details. 0014 * 0015 * You should have received a copy of the GNU Library General Public License 0016 * along with this library; see the file COPYING.LIB. If not, see 0017 * <http://www.gnu.org/licenses/>. 0018 */ 0019 0020 #include "EllipseTool.h" 0021 #include "ResizeHandle.h" 0022 #include "RotateHandle.h" 0023 #include "MoveHandle.h" 0024 #include "EllipsePathItem.h" 0025 #include "AnchorManager.h" 0026 #include "DocumentPrivate.h" 0027 #include "ViewPrivate.h" 0028 #include "Renderer.h" 0029 0030 #include <tikz/core/Style.h> 0031 #include <tikz/core/EllipsePath.h> 0032 #include <tikz/core/Transaction.h> 0033 #include <tikz/core/UndoSetProperty.h> 0034 0035 #include <QApplication> 0036 #include <QGraphicsScene> 0037 0038 #include <QDebug> 0039 0040 #include <cmath> 0041 0042 namespace tikz { 0043 namespace ui { 0044 0045 EllipseTool::EllipseTool(tikz::ui::PathItem * path, QGraphicsScene * scene) 0046 : AbstractTool(path->document(), scene) 0047 , m_path(qobject_cast<tikz::ui::EllipsePathItem *>(path)) 0048 , m_anchorManager(new AnchorManager(scene, path->document(), this)) 0049 , m_transaction(nullptr) 0050 { 0051 // show all path handles 0052 createPathHandles(); 0053 0054 connect(m_path->path(), SIGNAL(changed()), this, SLOT(updateHandlePositions())); 0055 } 0056 0057 EllipseTool::~EllipseTool() 0058 { 0059 qDeleteAll(m_handles); 0060 m_handles.clear(); 0061 } 0062 0063 void EllipseTool::mouseMoveEvent(QGraphicsSceneMouseEvent * event) 0064 { 0065 } 0066 0067 void EllipseTool::mousePressEvent(QGraphicsSceneMouseEvent * event) 0068 { 0069 } 0070 0071 void EllipseTool::mouseReleaseEvent(QGraphicsSceneMouseEvent * event) 0072 { 0073 } 0074 0075 void EllipseTool::createPathHandles() 0076 { 0077 // on hide, just delte all handles 0078 qDeleteAll(m_handles); 0079 m_handles.clear(); 0080 0081 // create and show handles 0082 m_handles.append(new ResizeHandle(Handle::TopLeftCorner)); 0083 m_handles.append(new ResizeHandle(Handle::TopRightCorner)); 0084 m_handles.append(new ResizeHandle(Handle::BottomLeftCorner)); 0085 m_handles.append(new ResizeHandle(Handle::BottomRightCorner)); 0086 m_handles.append(new ResizeHandle(Handle::LeftBorder)); 0087 m_handles.append(new ResizeHandle(Handle::TopBorder)); 0088 m_handles.append(new ResizeHandle(Handle::RightBorder)); 0089 m_handles.append(new ResizeHandle(Handle::BottomBorder)); 0090 m_handles.append(new ResizeHandle(Handle::TopLeftCorner)); 0091 m_handles.append(new MoveHandle(Handle::Center)); 0092 m_handles.append(new RotateHandle(Handle::ResizePos)); 0093 0094 // make sure the handles are positioned correctly 0095 updateHandlePositions(); 0096 0097 // show and connect to get handle movements 0098 for (Handle * handle : qAsConst(m_handles)) { 0099 scene()->addItem(handle); 0100 handle->show(); 0101 connect(handle, SIGNAL(positionChanged(tikz::ui::Handle *, const QPointF &, QGraphicsView *)), 0102 this, SLOT(handleMoved(tikz::ui::Handle *, const QPointF &, QGraphicsView *))); 0103 connect(handle, SIGNAL(mousePressed(tikz::ui::Handle *, const QPointF &, QGraphicsView *)), 0104 this, SLOT(handleMousePressed(tikz::ui::Handle *, const QPointF &, QGraphicsView *))); 0105 connect(handle, SIGNAL(mouseReleased(tikz::ui::Handle *, const QPointF &, QGraphicsView *)), 0106 this, SLOT(handleMouseReleased(tikz::ui::Handle *, const QPointF &, QGraphicsView *))); 0107 } 0108 } 0109 0110 void EllipseTool::updateHandlePositions() 0111 { 0112 for (Handle * handle : qAsConst(m_handles)) { 0113 handle->setPos(handlePos(handle->handlePos())); 0114 handle->setRotation(-m_path->style()->rotation()); 0115 } 0116 } 0117 0118 QPointF EllipseTool::handlePos(Handle::Position pos) 0119 { 0120 const QPointF c = m_path->pos(); 0121 const qreal w = m_path->style()->radiusX().toPoint(); 0122 const qreal h = m_path->style()->radiusY().toPoint(); 0123 QPointF p(0, 0); 0124 0125 switch (pos) { 0126 case Handle::TopLeftCorner: p = QPointF(-w, h); break; 0127 case Handle::TopBorder: p = QPointF(0, h); break; 0128 case Handle::TopRightCorner: p = QPointF(w, h); break; 0129 case Handle::LeftBorder: p = QPointF(-w, 0); break; 0130 case Handle::Center: break; 0131 case Handle::RightBorder: p = QPointF(w, 0); break; 0132 case Handle::BottomLeftCorner: p = QPointF(-w, -h); break; 0133 case Handle::BottomBorder: p = QPointF(0, -h); break; 0134 case Handle::BottomRightCorner: p = QPointF(w, -h); break; 0135 case Handle::ResizePos: p = QPointF(0, -h - tikz::mm2pt(5)); break; 0136 default: Q_ASSERT(false); 0137 } 0138 QTransform t; 0139 t.rotate(m_path->style()->rotation()); 0140 return c + t.map(p); 0141 } 0142 0143 void EllipseTool::handleMoved(Handle * handle, const QPointF & scenePos, QGraphicsView * view) 0144 { 0145 auto tikzView = qobject_cast<Renderer *>(view); 0146 0147 // 0148 // rotate 0149 // 0150 if (handle->handleType() == Handle::RotateHandle) { 0151 const QPointF delta = m_path->pos() - scenePos; 0152 const qreal rad = atan2(-delta.y(), -delta.x()); 0153 const qreal deg = tikzView->snapAngle(rad * 180 / M_PI + 90); 0154 0155 m_path->document()->addUndoItem(new tikz::core::UndoSetProperty(m_path->path()->styleUid(), "rotation", deg)); 0156 0157 return; 0158 } 0159 0160 // later: preferred unit 0161 const tikz::Unit unit = tikz::Unit::Centimeter; 0162 0163 // 0164 // move 0165 // 0166 if (handle->handlePos() == Handle::Center) { 0167 // try to attach to anchor 0168 tikz::core::EllipsePath * ep = m_path->ellipsePath(); 0169 tikz::core::MetaPos metaPos = m_anchorManager->anchorAt(scenePos, view); 0170 if (! metaPos.node()) { 0171 tikz::Pos p = tikz::Pos(scenePos).convertTo(unit); 0172 p = tikzView->snapPos(p); 0173 metaPos.setPos(p); 0174 } 0175 0176 ep->setMetaPos(metaPos); 0177 return; 0178 } 0179 0180 // 0181 // resize 0182 // 0183 QTransform t; 0184 t.rotate(-m_path->style()->rotation()); 0185 0186 // honor rotation of path 0187 const tikz::Pos delta = tikz::Pos(t.map(m_path->pos() - scenePos)).convertTo(unit); 0188 tikz::Value w = m_path->style()->radiusX(); 0189 tikz::Value h = m_path->style()->radiusY(); 0190 0191 switch (handle->handlePos()) { 0192 case Handle::TopLeftCorner: 0193 case Handle::TopRightCorner: 0194 case Handle::BottomLeftCorner: 0195 case Handle::BottomRightCorner: { 0196 w = delta.x(); 0197 h = delta.y(); 0198 0199 // snap to raster 0200 w = tikzView->snapValue(w); 0201 h = tikzView->snapValue(h); 0202 0203 break; 0204 } 0205 case Handle::TopBorder: 0206 case Handle::BottomBorder: { 0207 h = delta.y(); 0208 // snap to raster 0209 h = tikzView->snapValue(h); 0210 break; 0211 } 0212 case Handle::LeftBorder: 0213 case Handle::RightBorder: { 0214 w = delta.x(); 0215 // snap to raster 0216 w = tikzView->snapValue(w); 0217 break; 0218 } 0219 case Handle::Center: Q_ASSERT(false); 0220 case Handle::ResizePos: Q_ASSERT(false); 0221 default: Q_ASSERT(false); 0222 } 0223 0224 // for now, only allow positive values 0225 w = tikz::Value(qAbs(w.value()), w.unit()); 0226 h = tikz::Value(qAbs(h.value()), h.unit()); 0227 0228 m_path->document()->addUndoItem(new tikz::core::UndoSetProperty(m_path->path()->styleUid(), "radiusX", w)); 0229 m_path->document()->addUndoItem(new tikz::core::UndoSetProperty(m_path->path()->styleUid(), "radiusY", h)); 0230 } 0231 0232 void EllipseTool::handleMousePressed(Handle * handle, const QPointF & scenePos, QGraphicsView * view) 0233 { 0234 // qDebug() << "ellipse tool: mouse handle pressed " << scenePos; 0235 0236 QString action; 0237 switch (handle->handleType()) { 0238 case Handle::MoveHandle: 0239 m_anchorManager->addAllNodes(); 0240 m_anchorManager->showAnchors(); 0241 action = "Move Ellipse"; 0242 break; 0243 case Handle::ResizeHandle: 0244 action = "Resize Ellipse"; 0245 break; 0246 case Handle::RotateHandle: 0247 action = "Rotate Ellipse"; 0248 break; 0249 default: Q_ASSERT(false); 0250 } 0251 0252 m_transaction.reset(new tikz::core::Transaction(document(), action)); 0253 } 0254 0255 void EllipseTool::handleMouseReleased(Handle * handle, const QPointF & scenePos, QGraphicsView * view) 0256 { 0257 // qDebug() << "ellipse tool:mouse handle released" << scenePos; 0258 0259 m_transaction->finish(); 0260 m_anchorManager->clear(); 0261 } 0262 0263 } 0264 } 0265 0266 // kate: indent-width 4; replace-tabs on;