You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
koffice/chalk/plugins/tools/tool_curves/kis_tool_curve.cpp

594 lines
17 KiB

/*
* kis_tool_curve.cpp -- part of Chalk
*
* Copyright (c) 2006 Emanuele Tamponi <emanuele@valinor.it>
*
* 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; either version 2 of the License, or
* (at your option) any later version.
*
* 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 <math.h>
#include <limits.h>
#include <tqapplication.h>
#include <tqpainter.h>
#include <tqlayout.h>
#include <tqrect.h>
#include <tdeaction.h>
#include <kdebug.h>
#include <tdelocale.h>
#include <kdebug.h>
#include "kis_global.h"
#include "kis_doc.h"
#include "kis_painter.h"
#include "kis_point.h"
#include "kis_canvas_subject.h"
#include "kis_canvas_controller.h"
#include "kis_button_press_event.h"
#include "kis_button_release_event.h"
#include "kis_move_event.h"
#include "kis_canvas.h"
#include "kis_canvas_painter.h"
#include "kis_cursor.h"
#include "kis_tool_controller.h"
#include "kis_vec.h"
#include "kis_selection.h"
#include "kis_selection_options.h"
#include "kis_selected_transaction.h"
#include "kis_paintop_registry.h"
#include "kis_curve_framework.h"
#include "kis_tool_curve.h"
TQRect KisToolCurve::pivotRect (const TQPoint& pos)
{
return TQRect (pos-TQPoint(4,4),pos+TQPoint(4,4));
}
TQRect KisToolCurve::selectedPivotRect (const TQPoint& pos)
{
return TQRect (pos-TQPoint(5,5),pos+TQPoint(5,5));
}
KisToolCurve::KisToolCurve(const TQString& UIName)
: super(UIName)
{
m_UIName = UIName;
m_currentImage = 0;
m_optWidget = 0;
m_curve = 0;
m_dragging = false;
m_draggingCursor = false;
m_drawPivots = true;
m_drawingPen = TQPen(TQt::white, 0, TQt::SolidLine);
m_pivotPen = TQPen(TQt::gray, 0, TQt::SolidLine);
m_selectedPivotPen = TQPen(TQt::yellow, 0, TQt::SolidLine);
m_pivotRounding = m_selectedPivotRounding = 55;
m_actionOptions = NOOPTIONS;
m_supportMinimalDraw = true;
m_selectAction = SELECTION_ADD;
}
KisToolCurve::~KisToolCurve()
{
}
void KisToolCurve::update (KisCanvasSubject *subject)
{
super::update(subject);
if (m_subject)
m_currentImage = m_subject->currentImg();
}
void KisToolCurve::deactivate()
{
draw(false);
if (m_curve) {
m_curve->clear();
m_curve->endActionOptions();
}
m_actionOptions = NOOPTIONS;
m_dragging = false;
m_drawPivots = true;
}
void KisToolCurve::buttonPress(KisButtonPressEvent *event)
{
updateOptions(event->state());
if (!m_currentImage)
return;
if (event->button() == TQt::LeftButton) {
m_dragging = true;
m_currentPoint = event->pos();
PointPair temp = pointUnderMouse (m_subject->canvasController()->windowToView(event->pos().toTQPoint()));
if (temp.first == m_curve->end() && !(m_actionOptions)) {
draw(true, true);
m_curve->selectAll(false);
draw(true, true);
draw(m_curve->end());
m_previous = m_curve->find(m_curve->last());
m_current = m_curve->pushPivot(event->pos());
if (m_curve->pivots().count() > 1)
m_curve->calculateCurve(m_previous,m_current,m_current);
draw(m_current);
} else {
draw(true, true);
if (temp.second)
m_current = m_curve->selectPivot(temp.first);
else
m_current = selectByMouse(temp.first);
if (!(*m_current).isSelected())
m_dragging = false;
draw(true, true);
}
}
}
void KisToolCurve::keyPress(TQKeyEvent *event)
{
if (event->key() == TQt::Key_Return) {
m_dragging = false;
commitCurve();
} else
if (event->key() == TQt::Key_Escape) {
m_dragging = false;
draw(false);
m_curve->clear();
} else
if (event->key() == TQt::Key_Delete) {
draw(false);
m_dragging = false;
m_curve->deleteSelected();
m_current = m_curve->find(m_curve->last());
m_previous = m_curve->selectPivot(m_current);
draw(false);
}
}
void KisToolCurve::keyRelease(TQKeyEvent *)
{
}
void KisToolCurve::buttonRelease(KisButtonReleaseEvent *event)
{
updateOptions(event->state());
m_dragging = false;
}
void KisToolCurve::doubleClick(KisDoubleClickEvent *)
{
commitCurve();
}
void KisToolCurve::move(KisMoveEvent *event)
{
updateOptions(event->state());
PointPair temp = pointUnderMouse(m_subject->canvasController()->windowToView(event->pos().toTQPoint()));
if (temp.first == m_curve->end() && !m_dragging) {
if (m_draggingCursor) {
setCursor(KisCursor::load(m_cursor, 6, 6));
m_draggingCursor = false;
}
} else {
setCursor(KisCursor::load("tool_curve_dragging.png", 6, 6));
m_draggingCursor = true;
}
if (m_dragging) {
draw();
KisPoint trans = event->pos() - m_currentPoint;
m_curve->moveSelected(trans);
m_currentPoint = event->pos();
draw();
}
}
double pointToSegmentDistance(const KisPoint& p, const KisPoint& l0, const KisPoint& l1)
{
double lineLength = sqrt((l1.x() - l0.x()) * (l1.x() - l0.x()) + (l1.y() - l0.y()) * (l1.y() - l0.y()));
double distance = 0;
KisVector2D v0(l0), v1(l1), v(p), seg(v0-v1), dist0(v0-p), dist1(v1-p);
if (seg.length() < dist0.length() ||
seg.length() < dist1.length()) // the point doesn't perpendicolarly intersecate the segment (or it's too far from the segment)
return (double)INT_MAX;
if (lineLength > DBL_EPSILON) {
distance = ((l0.y() - l1.y()) * p.x() + (l1.x() - l0.x()) * p.y() + l0.x() * l1.y() - l1.x() * l0.y()) / lineLength;
distance = fabs(distance);
}
return distance;
}
PointPair KisToolCurve::pointUnderMouse(const TQPoint& pos)
{
KisCurve::iterator it, next;
TQPoint pos1, pos2;
it = handleUnderMouse(pos);
if (it != m_curve->end())
return PointPair(it,true);
for (it = m_curve->begin(); it != m_curve->end(); it++) {
next = it.next();
if (next == m_curve->end() || it == m_curve->end())
return PointPair(m_curve->end(),false);
if ((*it).hint() > LINEHINT || (*next).hint() > LINEHINT)
continue;
pos1 = m_subject->canvasController()->windowToView((*it).point().toTQPoint());
pos2 = m_subject->canvasController()->windowToView((*next).point().toTQPoint());
if (pos1 == pos2)
continue;
if (pointToSegmentDistance(pos,pos1,pos2) <= MAXDISTANCE)
break;
}
return PointPair(it,false);
}
KisCurve::iterator KisToolCurve::handleUnderMouse(const TQPoint& pos)
{
KisCurve pivs = m_curve->pivots(), inHandle;
KisCurve::iterator it;
for (it = pivs.begin(); it != pivs.end(); it++) {
if (pivotRect(m_subject->canvasController()->windowToView((*it).point().toTQPoint())).contains(pos))
inHandle.pushPoint((*it));
}
if (inHandle.isEmpty())
return m_curve->end();
return m_curve->find(inHandle.last());
}
KisCurve::iterator KisToolCurve::selectByMouse(KisCurve::iterator it)
{
KisCurve::iterator prevPivot, nextPivot;
if ((*it).isPivot())
prevPivot = it;
else
prevPivot = it.previousPivot();
nextPivot = it.nextPivot();
m_curve->selectPivot(prevPivot);
(*nextPivot).setSelected(true);
return prevPivot;
}
int KisToolCurve::updateOptions(int key)
{
int options = 0x0000;
if (key & TQt::ControlButton)
options |= CONTROLOPTION;
if (key & TQt::ShiftButton)
options |= SHIFTOPTION;
if (key & TQt::AltButton)
options |= ALTOPTION;
if (options != m_actionOptions) {
draw(false);
m_actionOptions = options;
m_curve->setActionOptions(m_actionOptions);
draw(false);
}
return m_actionOptions;
}
void KisToolCurve::draw(bool m, bool o)
{
draw(KisCurve::iterator(), o, m);
}
void KisToolCurve::draw(KisCurve::iterator inf, bool pivotonly, bool minimal)
{
if (m_curve->isEmpty())
return;
KisCanvasPainter *gc;
KisCanvasController *controller;
KisCanvas *canvas;
if (m_subject && m_currentImage) {
controller = m_subject->canvasController();
canvas = controller->kiscanvas();
gc = new KisCanvasPainter(canvas);
} else
return;
gc->setPen(m_drawingPen);
gc->setRasterOp(TQt::XorROP);
KisCurve::iterator it, finish;
if (minimal && m_supportMinimalDraw) {
if (pivotonly) {
KisCurve p = m_curve->pivots();
for (KisCurve::iterator i = p.begin(); i != p.end(); i++)
drawPivotHandle (*gc, i);
delete gc;
return;
}
if (inf.target() != 0) {
if (inf != m_curve->end()) {
it = inf.previousPivot();
finish = inf.nextPivot();
} else {
it = --m_curve->end();
finish = m_curve->end();
}
} else {
KisCurve sel = m_curve->selectedPivots();
if (sel.isEmpty()) {
delete gc;
return;
}
for (KisCurve::iterator i = sel.begin(); i != sel.end(); i++) {
it = m_curve->find(*i).previousPivot();
finish = m_curve->find(*i).nextPivot();
if ((*finish).isSelected())
finish = finish.previousPivot();
while (it != finish) {
if ((*it).isPivot())
drawPivotHandle (*gc, it);
it = drawPoint (*gc, it);
}
}
delete gc;
return;
}
} else {
it = m_curve->begin();
finish = m_curve->end();
}
while (it != finish) {
if ((*it).isPivot())
drawPivotHandle (*gc, it);
it = drawPoint (*gc, it);
}
delete gc;
}
KisCurve::iterator KisToolCurve::drawPoint(KisCanvasPainter& gc, KisCurve::iterator point)
{
KisCanvasController *controller = m_subject->canvasController();
TQPoint pos1, pos2;
pos1 = controller->windowToView((*point).point().toTQPoint());
switch ((*point).hint()) {
case POINTHINT:
gc.drawPoint(pos1);
point += 1;
break;
case LINEHINT:
gc.drawPoint(pos1);
if (++point != m_curve->end() && (*point).hint() <= LINEHINT) {
pos2 = controller->windowToView((*point).point().toTQPoint());
gc.drawLine(pos1,pos2);
}
break;
default:
point += 1;
}
return point;
}
void KisToolCurve::drawPivotHandle(KisCanvasPainter& gc, KisCurve::iterator point)
{
KisCanvasController *controller = m_subject->canvasController();
if (m_drawPivots) {
TQPoint pos = controller->windowToView((*point).point().toTQPoint());
if ((*point).isSelected()) {
gc.setPen(m_selectedPivotPen);
gc.drawRoundRect(selectedPivotRect(pos),m_selectedPivotRounding,m_selectedPivotRounding);
} else {
gc.setPen(m_pivotPen);
gc.drawRoundRect(pivotRect(pos),m_pivotRounding,m_pivotRounding);
}
gc.setPen(m_drawingPen);
}
}
void KisToolCurve::paint(KisCanvasPainter&)
{
draw(false);
}
void KisToolCurve::paint(KisCanvasPainter&, const TQRect&)
{
draw(false);
}
void KisToolCurve::commitCurve()
{
if (toolType() == TOOL_SHAPE || toolType() == TOOL_FREEHAND)
paintCurve();
else if (toolType() == TOOL_SELECT)
selectCurve();
else
kdDebug(0) << "NO SUPPORT FOR THIS TYPE OF TOOL" << endl;
m_curve->clear();
m_curve->endActionOptions();
}
void KisToolCurve::paintCurve()
{
KisPaintDeviceSP device = m_currentImage->activeDevice ();
if (!device) return;
KisPainter painter (device);
if (m_currentImage->undo()) painter.beginTransaction (m_transactionMessage);
painter.setPaintColor(m_subject->fgColor());
painter.setBrush(m_subject->currentBrush());
painter.setOpacity(m_opacity);
painter.setCompositeOp(m_compositeOp);
KisPaintOp * op = KisPaintOpRegistry::instance()->paintOp(m_subject->currentPaintop(), m_subject->currentPaintopSettings(), &painter);
painter.setPaintOp(op); // Painter takes ownership
// Call paintPoint
KisCurve::iterator it = m_curve->begin();
while (it != m_curve->end())
it = paintPoint(painter,it);
// Finish
device->setDirty( painter.dirtyRect() );
notifyModified();
if (m_currentImage->undo()) {
m_currentImage->undoAdapter()->addCommand(painter.endTransaction());
}
draw(false);
}
KisCurve::iterator KisToolCurve::paintPoint (KisPainter& painter, KisCurve::iterator point)
{
KisCurve::iterator next = point; next+=1;
switch ((*point).hint()) {
case POINTHINT:
painter.paintAt((*point++).point(), PRESSURE_DEFAULT, 0, 0);
break;
case LINEHINT:
if (next != m_curve->end() && (*next).hint() <= LINEHINT)
painter.paintLine((*point++).point(), PRESSURE_DEFAULT, 0, 0, (*next).point(), PRESSURE_DEFAULT, 0, 0);
else
painter.paintAt((*point++).point(), PRESSURE_DEFAULT, 0, 0);
break;
default:
point += 1;
}
return point;
}
TQValueVector<KisPoint> KisToolCurve::convertCurve()
{
TQValueVector<KisPoint> points;
for (KisCurve::iterator i = m_curve->begin(); i != m_curve->end(); i++)
if ((*i).hint() != NOHINTS)
points.append((*i).point());
return points;
}
void KisToolCurve::selectCurve()
{
TQApplication::setOverrideCursor(KisCursor::waitCursor());
KisPaintDeviceSP dev = m_currentImage->activeDevice();
bool hasSelection = dev->hasSelection();
KisSelectedTransaction *t = 0;
if (m_currentImage->undo()) t = new KisSelectedTransaction(m_transactionMessage, dev);
KisSelectionSP selection = dev->selection();
if (!hasSelection) {
selection->clear();
}
KisPainter painter(selection.data());
painter.setPaintColor(KisColor(TQt::black, selection->colorSpace()));
painter.setFillStyle(KisPainter::FillStyleForegroundColor);
painter.setStrokeStyle(KisPainter::StrokeStyleNone);
painter.setBrush(m_subject->currentBrush());
painter.setOpacity(OPACITY_OPAQUE);
KisPaintOp * op = KisPaintOpRegistry::instance()->paintOp("paintbrush", 0, &painter);
painter.setPaintOp(op); // And now the painter owns the op and will destroy it.
switch (m_selectAction) {
case SELECTION_ADD:
painter.setCompositeOp(COMPOSITE_OVER);
break;
case SELECTION_SUBTRACT:
painter.setCompositeOp(COMPOSITE_SUBTRACT);
break;
default:
break;
}
painter.paintPolygon(convertCurve());
if(hasSelection) {
TQRect dirty(painter.dirtyRect());
dev->setDirty(dirty);
dev->emitSelectionChanged(dirty);
} else {
dev->setDirty();
dev->emitSelectionChanged();
}
if (m_currentImage->undo())
m_currentImage->undoAdapter()->addCommand(t);
TQApplication::restoreOverrideCursor();
draw(false);
}
TQWidget* KisToolCurve::createOptionWidget(TQWidget* parent)
{
if (toolType() == TOOL_SHAPE || toolType() == TOOL_FREEHAND)
return super::createOptionWidget(parent);
else if (toolType() == TOOL_SELECT)
return createSelectionOptionWidget(parent);
else
kdDebug(0) << "NO SUPPORT FOR THIS TOOL TYPE" << endl;
return 0;
}
void KisToolCurve::slotSetAction(int action) {
if (action >= SELECTION_ADD && action <= SELECTION_SUBTRACT)
m_selectAction =(enumSelectionMode)action;
}
TQWidget* KisToolCurve::createSelectionOptionWidget(TQWidget* parent)
{
m_optWidget = new KisSelectionOptions(parent, m_subject);
TQ_CHECK_PTR(m_optWidget);
m_optWidget->setCaption(m_UIName);
connect (m_optWidget, TQT_SIGNAL(actionChanged(int)), this, TQT_SLOT(slotSetAction(int)));
TQVBoxLayout * l = dynamic_cast<TQVBoxLayout*>(m_optWidget->layout());
l->addItem(new TQSpacerItem(1, 1, TQSizePolicy::Fixed, TQSizePolicy::Expanding));
return m_optWidget;
}
TQWidget* KisToolCurve::optionWidget()
{
if (toolType() == TOOL_SELECT)
return m_optWidget;
else
return super::optionWidget();
}
#include "kis_tool_curve.moc"