File indexing completed on 2024-05-19 04:36:38
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 "NodeTool.h" 0021 #include "ResizeHandle.h" 0022 #include "RotateHandle.h" 0023 #include "MoveHandle.h" 0024 #include "NodeItem.h" 0025 #include "DocumentPrivate.h" 0026 #include "ViewPrivate.h" 0027 #include "Renderer.h" 0028 0029 #include <tikz/core/Style.h> 0030 #include <tikz/core/Transaction.h> 0031 #include <tikz/core/UndoSetProperty.h> 0032 0033 #include <QApplication> 0034 #include <QGraphicsScene> 0035 #include <QKeyEvent> 0036 #include <QInputDialog> 0037 0038 #include <QDebug> 0039 0040 #include <cmath> 0041 0042 namespace tikz { 0043 namespace ui { 0044 0045 static void setProp(const tikz::core::Uid & entity, const QString & key, const QVariant & value) 0046 { 0047 entity.document()->addUndoItem(new tikz::core::UndoSetProperty(entity, key, value)); 0048 } 0049 0050 NodeTool::NodeTool(NodeItem * node, QGraphicsScene * scene) 0051 : AbstractTool(node->document(), scene) 0052 , m_node(node) 0053 , m_transaction(nullptr) 0054 { 0055 // show all node handles 0056 createNodeHandles(); 0057 0058 connect(m_node, SIGNAL(changed()), this, SLOT(updateHandlePositions())); 0059 } 0060 0061 NodeTool::~NodeTool() 0062 { 0063 qDeleteAll(m_handles); 0064 m_handles.clear(); 0065 } 0066 0067 void NodeTool::mouseMoveEvent(QGraphicsSceneMouseEvent * event) 0068 { 0069 } 0070 0071 void NodeTool::mousePressEvent(QGraphicsSceneMouseEvent * event) 0072 { 0073 } 0074 0075 void NodeTool::mouseReleaseEvent(QGraphicsSceneMouseEvent * event) 0076 { 0077 } 0078 0079 void NodeTool::keyPressEvent(QKeyEvent * event) 0080 { 0081 if (event->key() == Qt::Key_Escape) { 0082 if (m_transaction && m_transaction->isRunning()) { 0083 m_transaction->cancel(); 0084 event->accept(); 0085 } 0086 } 0087 0088 if (event->key() == Qt::Key_Delete) { 0089 document()->deleteNodeItem(m_node); 0090 } 0091 0092 if (event->key() == Qt::Key_T) { 0093 bool ok = true; 0094 QString text = QInputDialog::getMultiLineText(nullptr, "Set Text", "Enter node text in LaTeX", m_node->node()->text(), &ok); 0095 if (ok) { 0096 setProp(m_node->uid(), "text", text); 0097 } 0098 } 0099 } 0100 0101 void NodeTool::createNodeHandles() 0102 { 0103 // on hide, just delte all handles 0104 qDeleteAll(m_handles); 0105 m_handles.clear(); 0106 0107 // create and show handles 0108 m_handles.append(new ResizeHandle(Handle::TopLeftCorner)); 0109 m_handles.append(new ResizeHandle(Handle::TopRightCorner)); 0110 m_handles.append(new ResizeHandle(Handle::BottomLeftCorner)); 0111 m_handles.append(new ResizeHandle(Handle::BottomRightCorner)); 0112 m_handles.append(new ResizeHandle(Handle::LeftBorder)); 0113 m_handles.append(new ResizeHandle(Handle::TopBorder)); 0114 m_handles.append(new ResizeHandle(Handle::RightBorder)); 0115 m_handles.append(new ResizeHandle(Handle::BottomBorder)); 0116 m_handles.append(new ResizeHandle(Handle::TopLeftCorner)); 0117 m_handles.append(new MoveHandle(Handle::Center)); 0118 m_handles.append(new RotateHandle(Handle::ResizePos)); 0119 0120 // make sure the handles are positioned correctly 0121 updateHandlePositions(); 0122 0123 // show and connect to get handle movements 0124 for (Handle * handle: qAsConst(m_handles)) { 0125 scene()->addItem(handle); 0126 handle->show(); 0127 connect(handle, SIGNAL(positionChanged(tikz::ui::Handle *, const QPointF &, QGraphicsView *)), 0128 this, SLOT(handleMoved(tikz::ui::Handle *, const QPointF &, QGraphicsView *))); 0129 connect(handle, SIGNAL(mousePressed(tikz::ui::Handle *, const QPointF &, QGraphicsView *)), 0130 this, SLOT(handleMousePressed(tikz::ui::Handle *, const QPointF &, QGraphicsView *))); 0131 connect(handle, SIGNAL(mouseReleased(tikz::ui::Handle *, const QPointF &, QGraphicsView *)), 0132 this, SLOT(handleMouseReleased(tikz::ui::Handle *, const QPointF &, QGraphicsView *))); 0133 } 0134 } 0135 0136 void NodeTool::updateHandlePositions() 0137 { 0138 for (Handle * handle : qAsConst(m_handles)) { 0139 handle->setPos(handlePos(handle->handlePos())); 0140 handle->setRotation(-m_node->style()->rotation()); 0141 } 0142 } 0143 0144 QPointF NodeTool::handlePos(Handle::Position pos) 0145 { 0146 const QPointF c = m_node->node()->pos(); 0147 const QRectF r = m_node->shapeRect(); 0148 const qreal w = r.width() / 2.0; 0149 const qreal h = r.height() / 2.0; 0150 QPointF p(0, 0); 0151 0152 switch (pos) { 0153 case Handle::TopLeftCorner: p = QPointF(-w, h); break; 0154 case Handle::TopBorder: p = QPointF(0, h); break; 0155 case Handle::TopRightCorner: p = QPointF(w, h); break; 0156 case Handle::LeftBorder: p = QPointF(-w, 0); break; 0157 case Handle::Center: break; 0158 case Handle::RightBorder: p = QPointF(w, 0); break; 0159 case Handle::BottomLeftCorner: p = QPointF(-w, -h); break; 0160 case Handle::BottomBorder: p = QPointF(0, -h); break; 0161 case Handle::BottomRightCorner: p = QPointF(w, -h); break; 0162 case Handle::ResizePos: p = QPointF(0, -h - tikz::mm2pt(5)); break; 0163 default: Q_ASSERT(false); 0164 } 0165 QTransform t; 0166 t.rotate(m_node->style()->rotation()); 0167 return c + t.map(p); 0168 } 0169 0170 void NodeTool::handleMoved(Handle * handle, const QPointF & scenePos, QGraphicsView * view) 0171 { 0172 if (!m_transaction || ! m_transaction->isRunning()) { 0173 return; 0174 } 0175 0176 auto tikzView = qobject_cast<Renderer *>(view); 0177 0178 // later: preferred unit 0179 const tikz::Unit unit = tikz::Unit::Centimeter; 0180 0181 // 0182 // rotate 0183 // 0184 if (handle->handleType() == Handle::RotateHandle) { 0185 const QPointF delta = m_node->node()->pos() - tikz::Pos(scenePos); 0186 const qreal rad = atan2(-delta.y(), -delta.x()); 0187 const qreal deg = tikzView->snapAngle(rad * 180 / M_PI + 90); 0188 setProp(m_node->style()->uid(), "rotation", deg); 0189 return; 0190 } 0191 0192 // 0193 // move 0194 // 0195 if (handle->handlePos() == Handle::Center) { 0196 tikz::Pos p = tikz::Pos(scenePos).convertTo(unit); 0197 p = tikzView->snapPos(p); 0198 m_node->node()->setPos(p); 0199 return; 0200 } 0201 0202 // 0203 // resize 0204 // 0205 QTransform t; 0206 t.rotate(-m_node->style()->rotation()); 0207 0208 // honor rotation of node 0209 const tikz::Pos delta = tikz::Pos(2 * t.map(m_node->node()->pos() - tikz::Pos(scenePos))).convertTo(unit); 0210 const tikz::Value oldW = m_node->style()->minimumWidth(); 0211 const tikz::Value oldH = m_node->style()->minimumHeight(); 0212 tikz::Value w = m_node->style()->minimumWidth(); 0213 tikz::Value h = m_node->style()->minimumHeight(); 0214 0215 switch (handle->handlePos()) { 0216 case Handle::TopLeftCorner: 0217 case Handle::TopRightCorner: 0218 case Handle::BottomLeftCorner: 0219 case Handle::BottomRightCorner: { 0220 w = delta.x(); 0221 h = delta.y(); 0222 0223 // snap to raster 0224 w = tikzView->snapValue(w); 0225 h = tikzView->snapValue(h); 0226 0227 break; 0228 } 0229 case Handle::TopBorder: 0230 case Handle::BottomBorder: { 0231 h = delta.y(); 0232 // snap to raster 0233 h = tikzView->snapValue(h); 0234 break; 0235 } 0236 case Handle::LeftBorder: 0237 case Handle::RightBorder: { 0238 w = delta.x(); 0239 // snap to raster 0240 w = tikzView->snapValue(w); 0241 break; 0242 } 0243 case Handle::Center: Q_ASSERT(false); 0244 case Handle::ResizePos: Q_ASSERT(false); 0245 default: Q_ASSERT(false); 0246 } 0247 0248 // for now, only allow positive values 0249 w = tikz::Value(qAbs(w.value()), w.unit()); 0250 h = tikz::Value(qAbs(h.value()), h.unit()); 0251 0252 if (w != oldW) setProp(m_node->style()->uid(), "minimumWidth", w); 0253 if (h != oldH) setProp(m_node->style()->uid(), "minimumHeight", h); 0254 } 0255 0256 void NodeTool::handleMousePressed(Handle * handle, const QPointF & scenePos, QGraphicsView * view) 0257 { 0258 qDebug() << "mouse handle pressed " << scenePos; 0259 0260 QString action; 0261 switch (handle->handleType()) { 0262 case Handle::MoveHandle: action = "Move Node"; break; 0263 case Handle::ResizeHandle: action = "Resize Node"; break; 0264 case Handle::RotateHandle: action = "Rotate Node"; break; 0265 default: Q_ASSERT(false); 0266 } 0267 0268 m_transaction.reset(new tikz::core::Transaction(document(), action)); 0269 } 0270 0271 void NodeTool::handleMouseReleased(Handle * handle, const QPointF & scenePos, QGraphicsView * view) 0272 { 0273 qDebug() << "mouse handle released" << scenePos; 0274 0275 m_transaction.reset(); 0276 } 0277 0278 } 0279 } 0280 0281 // kate: indent-width 4; replace-tabs on;