/* * kis_tool_transform.cc -- part of Chalk * * Copyright (c) 2006 Cyrille Berger * * Based on the transform tool from : * Copyright (c) 2004 Boudewijn Rempt * Copyright (c) 2005 Casper Boemann * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 2 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_tool_perspectivetransform.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include //#include "wdg_tool_transform.h" #include "kis_canvas.h" #include "kis_canvas_painter.h" namespace { class PerspectiveTransformCmd : public KisSelectedTransaction { typedef KisSelectedTransaction super; public: PerspectiveTransformCmd(KisToolPerspectiveTransform *tool, KisPaintDeviceSP device, KisPaintDeviceSP origDevice, KisPoint topleft, KisPoint topright, KisPoint bottomleft, KisPoint bottomright, KisSelectionSP origSel, TQRect initialRect); virtual ~PerspectiveTransformCmd(); public: virtual void execute(); virtual void unexecute(); void transformArgs(KisPoint &topleft, KisPoint &topright, KisPoint &bottomleft, KisPoint& bottomright) const; KisSelectionSP origSelection(TQRect& initialRect) const; KisPaintDeviceSP theDevice(); KisPaintDeviceSP origDevice(); private: TQRect m_initialRect; KisPoint m_topleft, m_topright, m_bottomleft, m_bottomright; KisToolPerspectiveTransform *m_tool; KisSelectionSP m_origSelection; KisPaintDeviceSP m_device; KisPaintDeviceSP m_origDevice; }; PerspectiveTransformCmd::PerspectiveTransformCmd(KisToolPerspectiveTransform *tool, KisPaintDeviceSP device, KisPaintDeviceSP origDevice, KisPoint topleft, KisPoint topright, KisPoint bottomleft, KisPoint bottomright, KisSelectionSP origSel, TQRect initialRect) : super(i18n("Perspective Transform"), device), m_initialRect(initialRect) , m_topleft(topleft), m_topright(topright), m_bottomleft(bottomleft), m_bottomright(bottomright) , m_tool(tool), m_origSelection(origSel), m_device(device), m_origDevice(origDevice) { } PerspectiveTransformCmd::~PerspectiveTransformCmd() { } void PerspectiveTransformCmd::transformArgs(KisPoint &topleft, KisPoint &topright, KisPoint &bottomleft, KisPoint& bottomright) const { topleft = m_topleft; topright = m_topright; bottomleft = m_bottomleft; bottomright = m_bottomright; } KisSelectionSP PerspectiveTransformCmd::origSelection(TQRect& initialRect) const { initialRect = m_initialRect; return m_origSelection; } void PerspectiveTransformCmd::execute() { super::execute(); } void PerspectiveTransformCmd::unexecute() { super::unexecute(); } KisPaintDeviceSP PerspectiveTransformCmd::theDevice() { return m_device; } KisPaintDeviceSP PerspectiveTransformCmd::origDevice() { return m_origDevice; } } KisToolPerspectiveTransform::KisToolPerspectiveTransform() : super(i18n("Perspective Transform")) { setName("tool_perspectivetransform"); setCursor(KisCursor::selectCursor()); m_subject = 0; m_origDevice = 0; m_origSelection = 0; m_handleHalfSize = 8; m_handleSize = 2 * m_handleHalfSize; m_handleSelected = NOHANDLE; } KisToolPerspectiveTransform::~KisToolPerspectiveTransform() { } void KisToolPerspectiveTransform::deactivate() { if (m_subject && m_subject->undoAdapter()) m_subject->undoAdapter()->removeCommandHistoryListener( this ); KisImageSP img = m_subject->currentImg(); if (!img) return; paintOutline(); disconnect(m_subject->currentImg().data(), TQT_SIGNAL(sigLayerActivated(KisLayerSP)), this, TQT_SLOT(slotLayerActivated(KisLayerSP))); } void KisToolPerspectiveTransform::activate() { super::activate(); m_currentSelectedPoint = 0; if(m_subject && m_subject->currentImg() && m_subject->currentImg()->activeDevice()) { //connect(m_subject, commandExecuted(KCommand *c), this, notifyCommandAdded( KCommand * c)); m_subject->undoAdapter()->setCommandHistoryListener( this ); // KisToolControllerInterface *controller = m_subject->toolController(); // if (controller) // controller->setCurrentTool(this); PerspectiveTransformCmd * cmd=0; if(m_subject->currentImg()->undoAdapter()->presentCommand()) cmd = dynamic_cast(m_subject->currentImg()->undoAdapter()->presentCommand()); // One of our commands is on top if(cmd &&cmd->theDevice() == m_subject->currentImg()->activeDevice()) { m_interractionMode = EDITRECTINTERRACTION; // and it even has the same device // We should ask for tool args and orig selection m_origDevice = cmd->origDevice(); cmd->transformArgs(m_topleft, m_topright, m_bottomleft, m_bottomright); m_origSelection = cmd->origSelection(m_initialRect); paintOutline(); } else { m_interractionMode = DRAWRECTINTERRACTION; m_points.clear(); initHandles(); } } connect(m_subject->currentImg(), TQT_SIGNAL(sigLayerActivated(KisLayerSP)), this, TQT_SLOT(slotLayerActivated(KisLayerSP))); } void KisToolPerspectiveTransform::initHandles() { // TQ_INT32 x,y,w,h; KisImageSP img = m_subject->currentImg(); KisPaintDeviceSP dev = img->activeDevice(); if (!dev ) return; // Create a lazy copy of the current state m_origDevice = new KisPaintDevice(*dev.data()); Q_ASSERT(m_origDevice); if(dev->hasSelection()) { KisSelectionSP sel = dev->selection(); m_origSelection = new KisSelection(*sel.data()); m_initialRect = sel->selectedExactRect(); } else { m_initialRect = dev->exactBounds(); } m_topleft = KisPoint(m_initialRect.topLeft()); m_topright = KisPoint(m_initialRect.topRight()); m_bottomleft = KisPoint(m_initialRect.bottomLeft()); m_bottomright = KisPoint(m_initialRect.bottomRight()); m_subject->canvasController() ->updateCanvas(); } void KisToolPerspectiveTransform::paint(KisCanvasPainter& gc) { paintOutline(gc, TQRect()); } void KisToolPerspectiveTransform::paint(KisCanvasPainter& gc, const TQRect& rc) { paintOutline(gc, rc); } bool KisToolPerspectiveTransform::mouseNear(const TQPoint& mousep, const TQPoint point) { return (TQRect( (point.x() - m_handleHalfSize), (point.y() - m_handleHalfSize), m_handleSize, m_handleSize).contains(mousep) ); } void KisToolPerspectiveTransform::buttonPress(KisButtonPressEvent *event) { if (m_subject) { switch(m_interractionMode) { case DRAWRECTINTERRACTION: { if (m_points.isEmpty()) { m_dragging = false; m_dragStart = event->pos(); m_dragEnd = event->pos(); m_points.append(m_dragStart); paintOutline(); } else { m_dragging = true; m_dragStart = m_dragEnd; m_dragEnd = event->pos(); paintOutline(); } } case EDITRECTINTERRACTION: { KisImageSP img = m_subject->currentImg(); if (img && img->activeDevice() && event->button() == Qt::LeftButton) { m_actualyMoveWhileSelected = false; m_dragEnd = event->pos(); KisCanvasController *controller = m_subject->canvasController(); TQPoint mousep = controller->windowToView( event->pos().roundTQPoint() ); if( mouseNear( mousep, controller->windowToView(m_topleft.roundTQPoint() ) ) ) { kdDebug() << " PRESS TOPLEFT HANDLE " << endl; m_currentSelectedPoint = &m_topleft; } else if( mouseNear( mousep, controller->windowToView(m_topright.roundTQPoint() ) ) ) { kdDebug() << " PRESS TOPRIGHT HANDLE " << endl; m_currentSelectedPoint = &m_topright; } else if( mouseNear( mousep, controller->windowToView(m_bottomleft.roundTQPoint() ) ) ) { kdDebug() << " PRESS BOTTOMLEFT HANDLE " << endl; m_currentSelectedPoint = &m_bottomleft; } else if( mouseNear( mousep, controller->windowToView(m_bottomright.roundTQPoint() ) ) ) { kdDebug() << " PRESS BOTTOMRIGHT HANDLE " << endl; m_currentSelectedPoint = &m_bottomright; } else if( mouseNear( mousep, controller->windowToView(KisPoint((m_topleft+m_topright)*0.5).roundTQPoint() ) ) ) { kdDebug() << " PRESS TOP HANDLE " << endl; m_handleSelected = TOPHANDLE; }else if( mouseNear( mousep, controller->windowToView(KisPoint((m_topleft+m_bottomleft)*0.5).roundTQPoint() ) ) ) { kdDebug() << " PRESS LEFT HANDLE " << endl; m_handleSelected = LEFTHANDLE; }else if( mouseNear( mousep, controller->windowToView(KisPoint((m_bottomleft+m_bottomright)*0.5).roundTQPoint() ) ) ) { kdDebug() << " PRESS BOTTOM HANDLE " << endl; m_handleSelected = BOTTOMHANDLE; }else if( mouseNear( mousep, controller->windowToView(KisPoint((m_bottomright+m_topright)*0.5).roundTQPoint() ) ) ) { kdDebug() << " PRESS RIGHT HANDLE " << endl; m_handleSelected = RIGHTHANDLE; }else if( mouseNear( mousep, controller->windowToView(KisPoint((m_topleft+m_bottomleft + m_bottomright+m_topright)*0.25).roundTQPoint() ) ) ) { kdDebug() << " PRESS MIDDLE HANDLE " << endl; m_handleSelected = MIDDLEHANDLE; } } } } } } void KisToolPerspectiveTransform::move(KisMoveEvent *event) { switch(m_interractionMode) { case DRAWRECTINTERRACTION: { if (m_dragging) { // erase old lines on canvas paintOutline(); // get current mouse position m_dragEnd = event->pos(); // draw new lines on canvas paintOutline(); } } case EDITRECTINTERRACTION: { if(m_currentSelectedPoint) { paintOutline(); KisPoint translate = event->pos() - m_dragEnd; m_dragEnd = event->pos(); *m_currentSelectedPoint += translate;; paintOutline(); m_actualyMoveWhileSelected = true; } else if(m_handleSelected == TOPHANDLE || m_handleSelected == LEFTHANDLE || m_handleSelected == BOTTOMHANDLE || m_handleSelected == RIGHTHANDLE) { paintOutline(); KisPoint translate = event->pos() - m_dragEnd; m_dragEnd = event->pos(); double matrixFrom[3][3]; double* b = KisPerspectiveMath::computeMatrixTransfoToPerspective(m_topleft, m_topright, m_bottomleft, m_bottomright, m_initialRect); for(int i = 0; i < 3; i++) { for(int j = 0; j < 3; j++) { matrixFrom[i][j] = b[3*i+j]; } } delete b; KisPoint topLeft = KisPerspectiveMath::matProd(matrixFrom, KisPoint(m_initialRect.topLeft()) ); KisPoint topRight = KisPerspectiveMath::matProd(matrixFrom, KisPoint(m_initialRect.topRight()) ); KisPoint bottomLeft = KisPerspectiveMath::matProd(matrixFrom, KisPoint(m_initialRect.bottomLeft()) ); KisPoint bottomRight = KisPerspectiveMath::matProd(matrixFrom, KisPoint(m_initialRect.bottomRight()) ); TQRect dstRect = m_initialRect; switch(m_handleSelected) { case TOPHANDLE: dstRect.setTop( static_cast( dstRect.top() + translate.y() ) ) ; break; case LEFTHANDLE: dstRect.setLeft( static_cast( dstRect.left() + translate.x() ) ); break; case BOTTOMHANDLE: dstRect.setBottom( static_cast( dstRect.bottom() + translate.y() ) ); break; case RIGHTHANDLE: dstRect.setRight( static_cast( dstRect.right() + translate.x() ) ); break; case MIDDLEHANDLE: case NOHANDLE: kdDebug() << "Should NOT happen" << endl; } double matrixTo[3][3]; b = KisPerspectiveMath::computeMatrixTransfoToPerspective(topLeft, topRight, bottomLeft, bottomRight, dstRect ); for(int i = 0; i < 3; i++) { for(int j = 0; j < 3; j++) { matrixTo[i][j] = b[3*i+j]; } } delete b; m_topleft = KisPerspectiveMath::matProd(matrixTo, KisPoint(m_initialRect.topLeft())); m_topright = KisPerspectiveMath::matProd(matrixTo, KisPoint(m_initialRect.topRight())); m_bottomleft = KisPerspectiveMath::matProd(matrixTo, KisPoint(m_initialRect.bottomLeft())); m_bottomright = KisPerspectiveMath::matProd(matrixTo, KisPoint(m_initialRect.bottomRight())); paintOutline(); m_actualyMoveWhileSelected = true; } else if (m_handleSelected == MIDDLEHANDLE) { paintOutline(); KisPoint translate = event->pos() - m_dragEnd; m_dragEnd = event->pos(); m_topleft += translate; m_topright += translate; m_bottomleft += translate; m_bottomright += translate; paintOutline(); m_actualyMoveWhileSelected = true; } } }; } void KisToolPerspectiveTransform::buttonRelease(KisButtonReleaseEvent * event) { KisImageSP img = m_subject->currentImg(); if (!img) return; if( event->button() == Qt::LeftButton) { switch(m_interractionMode) { case DRAWRECTINTERRACTION: { if (m_dragging && event->button() == Qt::LeftButton) { paintOutline(); m_dragging = false; m_points.append (m_dragEnd); if( m_points.size() == 4) { // from the points, select which is topleft ? topright ? bottomright ? and bottomleft ? m_topleft = m_points[0]; m_topright = m_points[1]; m_bottomleft = m_points[3]; m_bottomright = m_points[2]; double matrix[3][3]; double* b = KisPerspectiveMath::computeMatrixTransfoToPerspective(m_topleft, m_topright, m_bottomleft, m_bottomright, m_initialRect ); for(int i = 0; i < 3; i++) { for(int j = 0; j < 3; j++) { kdDebug() << "sol[" << 3*i+j << "]=" << b[3*i+j] << endl; matrix[i][j] = b[3*i+j]; } } m_topleft = KisPerspectiveMath::matProd(matrix, KisPoint(m_initialRect.topLeft())); m_topright = KisPerspectiveMath::matProd(matrix, KisPoint(m_initialRect.topRight())); m_bottomleft = KisPerspectiveMath::matProd(matrix, KisPoint(m_initialRect.bottomLeft())); m_bottomright = KisPerspectiveMath::matProd(matrix, KisPoint(m_initialRect.bottomRight())); m_interractionMode = EDITRECTINTERRACTION; paintOutline(); TQApplication::setOverrideCursor(KisCursor::waitCursor()); transform(); TQApplication::restoreOverrideCursor(); } else { paintOutline(); } } } break; case EDITRECTINTERRACTION: { if(m_currentSelectedPoint ) { m_currentSelectedPoint = 0; if(m_actualyMoveWhileSelected) { paintOutline(); TQApplication::setOverrideCursor(KisCursor::waitCursor()); transform(); TQApplication::restoreOverrideCursor(); } } if(m_handleSelected != NOHANDLE) { m_handleSelected = NOHANDLE; if(m_actualyMoveWhileSelected) { // paintOutline(); TQApplication::setOverrideCursor(KisCursor::waitCursor()); transform(); TQApplication::restoreOverrideCursor(); } } } break; } } } void KisToolPerspectiveTransform::paintOutline() { if (m_subject) { KisCanvasController *controller = m_subject->canvasController(); KisCanvas *canvas = controller->kiscanvas(); KisCanvasPainter gc(canvas); TQRect rc; paintOutline(gc, rc); } } void KisToolPerspectiveTransform::paintOutline(KisCanvasPainter& gc, const TQRect&) { if (m_subject) { KisCanvasController *controller = m_subject->canvasController(); RasterOp op = gc.rasterOp(); TQPen old = gc.pen(); TQPen pen(TQt::SolidLine); pen.setWidth(1); Q_ASSERT(controller); switch(m_interractionMode) { case DRAWRECTINTERRACTION: { kdDebug() << "DRAWRECTINTERRACTION paintOutline " << m_points.size() << endl; KisPoint start, end; TQPoint startPos; TQPoint endPos; for (KisPointVector::iterator it = m_points.begin(); it != m_points.end(); ++it) { if (it == m_points.begin()) { start = (*it); } else { end = (*it); startPos = controller->windowToView(start.floorTQPoint()); endPos = controller->windowToView(end.floorTQPoint()); gc.drawLine(startPos, endPos); start = end; } } } break; case EDITRECTINTERRACTION: { TQPoint topleft = controller->windowToView(m_topleft ).roundTQPoint(); TQPoint topright = controller->windowToView(m_topright).roundTQPoint(); TQPoint bottomleft = controller->windowToView(m_bottomleft).roundTQPoint(); TQPoint bottomright = controller->windowToView(m_bottomright).roundTQPoint(); gc.setRasterOp(TQt::NotROP); gc.setPen(pen); gc.drawRect(topleft.x()-4, topleft.y()-4, 8, 8); gc.drawLine(topleft.x(), topleft.y(), (topleft.x()+topright.x())/2, (topleft.y()+topright.y())/2); gc.drawRect((topleft.x()+topright.x())/2-4, (topleft.y()+topright.y())/2-4, 8, 8); gc.drawLine((topleft.x()+topright.x())/2, (topleft.y()+topright.y())/2, topright.x(), topright.y()); gc.drawRect(topright.x()-4, topright.y()-4, 8, 8); gc.drawLine(topright.x(), topright.y(), (topright.x()+bottomright.x())/2, (topright.y()+bottomright.y())/2); gc.drawRect((topright.x()+bottomright.x())/2-4, (topright.y()+bottomright.y())/2-4, 8, 8); gc.drawLine((topright.x()+bottomright.x())/2, (topright.y()+bottomright.y())/2,bottomright.x(), bottomright.y()); gc.drawRect(bottomright.x()-4, bottomright.y()-4, 8, 8); gc.drawLine(bottomright.x(), bottomright.y(), (bottomleft.x()+bottomright.x())/2, (bottomleft.y()+bottomright.y())/2); gc.drawRect((bottomleft.x()+bottomright.x())/2-4, (bottomleft.y()+bottomright.y())/2-4, 8, 8); gc.drawLine((bottomleft.x()+bottomright.x())/2, (bottomleft.y()+bottomright.y())/2, bottomleft.x(), bottomleft.y()); gc.drawRect(bottomleft.x()-4, bottomleft.y()-4, 8, 8); gc.drawLine(bottomleft.x(), bottomleft.y(), (topleft.x()+bottomleft.x())/2, (topleft.y()+bottomleft.y())/2); gc.drawRect((topleft.x()+bottomleft.x())/2-4, (topleft.y()+bottomleft.y())/2-4, 8, 8); gc.drawLine((topleft.x()+bottomleft.x())/2, (topleft.y()+bottomleft.y())/2, topleft.x(), topleft.y()); gc.drawRect((bottomleft.x()+bottomright.x()+topleft.x()+topright.x())/4-4, (bottomleft.y()+bottomright.y()+topleft.y()+topright.y())/4-4, 8, 8); } break; } gc.setRasterOp(op); gc.setPen(old); } } void KisToolPerspectiveTransform::transform() { KisImageSP img = m_subject->currentImg(); if (!img || !img->activeDevice()) return; KisProgressDisplayInterface *progress = m_subject->progressDisplay(); // This mementoes the current state of the active device. PerspectiveTransformCmd * transaction = new PerspectiveTransformCmd(this, img->activeDevice(), m_origDevice, m_topleft, m_topright, m_bottomleft, m_bottomright, m_origSelection, m_initialRect); // Copy the original state back. TQRect rc = m_origDevice->extent(); rc = rc.normalize(); img->activeDevice()->clear(); KisPainter gc(img->activeDevice()); gc.bitBlt(rc.x(), rc.y(), COMPOSITE_COPY, m_origDevice, rc.x(), rc.y(), rc.width(), rc.height()); gc.end(); // Also restore the original selection. if(m_origSelection) { TQRect rc = m_origSelection->selectedRect(); rc = rc.normalize(); img->activeDevice()->selection()->clear(); KisPainter sgc(img->activeDevice()->selection().data()); sgc.bitBlt(rc.x(), rc.y(), COMPOSITE_COPY, m_origSelection.data(), rc.x(), rc.y(), rc.width(), rc.height()); sgc.end(); } else if(img->activeDevice()->hasSelection()) img->activeDevice()->selection()->clear(); // Perform the transform. Since we copied the original state back, this doesn't degrade // after many tweaks. Since we started the transaction before the copy back, the memento // has the previous state. KisPerspectiveTransformWorker t(img->activeDevice(),m_topleft, m_topright, m_bottomleft, m_bottomright, progress); t.run(); // If canceled, go back to the memento if(t.isCanceled()) { transaction->unexecute(); delete transaction; return; } img->activeDevice()->setDirty(rc); // XXX: This is not enough - should union with new extent // Else add the command -- this will have the memento from the previous state, // and the transformed state from the original device we cached in our activated() // method. if (transaction) { if (img->undo()) img->undoAdapter()->addCommand(transaction); else delete transaction; } } void KisToolPerspectiveTransform::notifyCommandAdded( KCommand * command) { PerspectiveTransformCmd * cmd = dynamic_cast(command); if (cmd == 0) { // The last added command wasn't one of ours; // we should reset to the new state of the canvas. // In effect we should treat this as if the tool has been just activated initHandles(); } } void KisToolPerspectiveTransform::notifyCommandExecuted( KCommand * command) { Q_UNUSED(command); PerspectiveTransformCmd * cmd=0; if(m_subject->currentImg()->undoAdapter()->presentCommand()) cmd = dynamic_cast(m_subject->currentImg()->undoAdapter()->presentCommand()); if (cmd == 0) { // The command now on the top of the stack isn't one of ours // We should treat this as if the tool has been just activated initHandles(); } else { // One of our commands is now on top // We should ask for tool args and orig selection m_origDevice = cmd->origDevice(); cmd->transformArgs(m_topleft, m_topright, m_bottomleft, m_bottomright); m_origSelection = cmd->origSelection(m_initialRect); m_subject->canvasController() ->updateCanvas(); } } void KisToolPerspectiveTransform::slotLayerActivated(KisLayerSP) { activate(); } TQWidget* KisToolPerspectiveTransform::createOptionWidget(TQWidget* /*parent*/) { #if 0 m_optWidget = new WdgToolPerspectiveTransform(parent); TQ_CHECK_PTR(m_optWidget); m_optWidget->cmbFilter->clear(); m_optWidget->cmbFilter->setIDList(KisFilterStrategyRegistry::instance()->listKeys()); m_optWidget->cmbFilter->setCurrentText("Mitchell"); connect(m_optWidget->cmbFilter, TQT_SIGNAL(activated(const KisID &)), this, TQT_SLOT(slotSetFilter(const KisID &))); KisID filterID = m_optWidget->cmbFilter->currentItem(); m_filter = KisFilterStrategyRegistry::instance()->get(filterID); /* connect(m_optWidget->intStartX, TQT_SIGNAL(valueChanged(int)), this, TQT_SLOT(setStartX(int))); connect(m_optWidget->intStartY, TQT_SIGNAL(valueChanged(int)), this, TQT_SLOT(setStartY(int))); connect(m_optWidget->intEndX, TQT_SIGNAL(valueChanged(int)), this, TQT_SLOT(setEndX(int))); connect(m_optWidget->intEndY, TQT_SIGNAL(valueChanged(int)), this, TQT_SLOT(setEndY(int))); */ m_optWidget->intStartX->hide(); m_optWidget->intStartY->hide(); m_optWidget->intEndX->hide(); m_optWidget->intEndY->hide(); m_optWidget->textLabel1->hide(); m_optWidget->textLabel2->hide(); m_optWidget->textLabel3->hide(); m_optWidget->textLabel4->hide(); #endif return 0; } TQWidget* KisToolPerspectiveTransform::optionWidget() { return 0; } void KisToolPerspectiveTransform::setup(TDEActionCollection *collection) { m_action = static_cast(collection->action(name())); if (m_action == 0) { m_action = new TDERadioAction(i18n("&Perspective Transform"), "tool_perspectivetransform", 0, this, TQT_SLOT(activate()), collection, name()); TQ_CHECK_PTR(m_action); m_action->setToolTip(i18n("Perspective transform a layer or a selection")); m_action->setExclusiveGroup("tools"); m_ownAction = true; } } #include "kis_tool_perspectivetransform.moc"