/* ============================================================ * Copyright 2004-2005 by Gilles Caulier * Copyright 2005 by Casper Boemann (reworked to be generic) * * 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, 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. * * ============================================================ */ // C++ includes. #include #include // TQt includes. #include #include #include #include #include #include #include #include #include // KDE includes. #include #include #include // Local includes. #include "kcurve.h" KCurve::KCurve(TQWidget *parent, const char *name, WFlags f) : TQWidget(parent, name, f) { m_grab_point = NULL; m_readOnlyMode = false; m_guideVisible = false; m_dragging = false; m_pix = NULL; setMouseTracking(true); setPaletteBackgroundColor(TQt::NoBackground); setMinimumSize(150, 50); TQPair *p = new TQPair; p->first = 0.0; p->second=0.0; m_points.append(p); p = new TQPair; p->first = 1.0; p->second=1.0; m_points.append(p); m_points.setAutoDelete(true); setFocusPolicy(TQWidget::StrongFocus); } KCurve::~KCurve() { if (m_pix) delete m_pix; } void KCurve::reset(void) { m_grab_point = NULL; m_guideVisible = false; repaint(false); } void KCurve::setCurveGuide(TQColor color) { m_guideVisible = true; m_colorGuide = color; repaint(false); } void KCurve::setPixmap(TQPixmap pix) { if (m_pix) delete m_pix; m_pix = new TQPixmap(pix); repaint(false); } void KCurve::keyPressEvent(TQKeyEvent *e) { if(e->key() == TQt::Key_Delete || e->key() == TQt::Key_Backspace) { TQPair *closest_point=NULL; if(m_grab_point) { //first find closest point to get focus afterwards TQPair *p = m_points.first(); double distance = 1000; // just a big number while(p) { if(p!=m_grab_point) if (fabs (m_grab_point->first - p->first) < distance) { distance = fabs(m_grab_point->first - p->first); closest_point = p; } p = m_points.next(); } m_points.remove(m_grab_point); } m_grab_point = closest_point; repaint(false); } else TQWidget::keyPressEvent(e); } void KCurve::paintEvent(TQPaintEvent *) { int x, y; int wWidth = width(); int wHeight = height(); x = 0; y = 0; // Drawing selection or all histogram values. // A TQPixmap is used for enable the double buffering. TQPixmap pm(size()); TQPainter p1; p1.begin(TQT_TQPAINTDEVICE(&pm), this); // draw background if(m_pix) { p1.scale(1.0*wWidth/m_pix->width(), 1.0*wHeight/m_pix->height()); p1.drawPixmap(0, 0, *m_pix); p1.resetXForm(); } else pm.fill(); // Draw grid separators. p1.setPen(TQPen(TQt::gray, 1, TQt::SolidLine)); p1.drawLine(wWidth/3, 0, wWidth/3, wHeight); p1.drawLine(2*wWidth/3, 0, 2*wWidth/3, wHeight); p1.drawLine(0, wHeight/3, wWidth, wHeight/3); p1.drawLine(0, 2*wHeight/3, wWidth, 2*wHeight/3); // Draw curve. double curvePrevVal = getCurveValue(0.0); p1.setPen(TQPen(TQt::black, 1, TQt::SolidLine)); for (x = 0 ; x < wWidth ; x++) { double curveX; double curveVal; curveX = (x + 0.5) / wWidth; curveVal = getCurveValue(curveX); p1.drawLine(x - 1, wHeight - int(curvePrevVal * wHeight), x, wHeight - int(curveVal * wHeight)); curvePrevVal = curveVal; } p1.drawLine(x - 1, wHeight - int(curvePrevVal * wHeight), x, wHeight - int(getCurveValue(1.0) * wHeight)); // Drawing curve handles. if ( !m_readOnlyMode ) { TQPair *p = m_points.first(); while(p) { double curveX = p->first; double curveY = p->second; if(p == m_grab_point) { p1.setPen(TQPen(TQt::red, 3, TQt::SolidLine)); p1.drawEllipse( int(curveX * wWidth) - 2, wHeight - 2 - int(curveY * wHeight), 4, 4 ); } else { p1.setPen(TQPen(TQt::red, 1, TQt::SolidLine)); p1.drawEllipse( int(curveX * wWidth) - 3, wHeight - 3 - int(curveY * wHeight), 6, 6 ); } p = m_points.next(); } } p1.end(); bitBlt(this, 0, 0, &pm); } void KCurve::mousePressEvent ( TQMouseEvent * e ) { if (m_readOnlyMode) return; TQPair *closest_point=NULL; double distance; if (e->button() != TQt::LeftButton) return; double x = e->pos().x() / (float)width(); double y = 1.0 - e->pos().y() / (float)height(); distance = 1000; // just a big number TQPair *p = m_points.first(); int insert_pos,pos=0; while(p) { if (fabs (x - p->first) < distance) { distance = fabs(x - p->first); closest_point = p; if(x < p->first) insert_pos = pos; else insert_pos = pos + 1; } p = m_points.next(); pos++; } if(closest_point == NULL) { closest_point = new TQPair; closest_point->first = x; closest_point->second = y; m_points.append(closest_point); } else if(distance * width() > 5) { closest_point = new TQPair; closest_point->first = x; closest_point->second = y; m_points.insert(insert_pos, closest_point); } else if(fabs(y - closest_point->second) * width() > 5) return; m_grab_point = closest_point; m_grabOffsetX = m_grab_point->first - x; m_grabOffsetY = m_grab_point->second - y; m_grab_point->first = x + m_grabOffsetX; m_grab_point->second = y + m_grabOffsetY; m_dragging = true; setCursor( KCursor::crossCursor() ); // Determine the leftmost and rightmost points. m_leftmost = 0; m_rightmost = 1; p = m_points.first(); while(p) { if (p != m_grab_point) { if(p->first> m_leftmost && p->first < x) m_leftmost = p->first; if(p->first < m_rightmost && p->first > x) m_rightmost = p->first; } p = m_points.next(); } repaint(false); } void KCurve::mouseReleaseEvent ( TQMouseEvent * e ) { if (m_readOnlyMode) return; if (e->button() != TQt::LeftButton) return; setCursor( KCursor::arrowCursor() ); m_dragging = false; repaint(false); emit modified(); } void KCurve::mouseMoveEvent ( TQMouseEvent * e ) { if (m_readOnlyMode) return; double x = e->pos().x() / (float)width(); double y = 1.0 - e->pos().y() / (float)height(); if (m_dragging == false) // If no point is selected set the the cursor shape if on top { double distance = 1000; double ydistance = 1000; TQPair *p = m_points.first(); while(p) { if (fabs (x - p->first) < distance) { distance = fabs(x - p->first); ydistance = fabs(y - p->second); } p = m_points.next(); } if (distance * width() > 5 || ydistance * height() > 5) setCursor( KCursor::arrowCursor() ); else setCursor( KCursor::crossCursor() ); } else // Else, drag the selected point { setCursor( KCursor::crossCursor() ); x += m_grabOffsetX; y += m_grabOffsetY; if (x <= m_leftmost) x = m_leftmost + 1E-4; // the addition so we can grab the dot later. if(x >= m_rightmost) x = m_rightmost - 1E-4; if(y > 1.0) y = 1.0; if(y < 0.0) y = 0.0; m_grab_point->first = x; m_grab_point->second = y; emit modified(); } repaint(false); } double KCurve::getCurveValue(double x) { return getCurveValue(m_points, x); } double KCurve::getCurveValue(TQPtrList > &curve, double x) { double t; TQPair *p; TQPair *p0,*p1,*p2,*p3; double c0,c1,c2,c3; double val; if(curve.count() == 0) return 0.5; // First find curve segment p = curve.first(); if(x < p->first) return p->second; p = curve.last(); if(x >= p->first) return p->second; // Find the four control points (two on each side of x) p = curve.first(); while(x >= p->first) { p = curve.next(); } curve.prev(); if((p0 = curve.prev()) == NULL) p1 = p0 = curve.first(); else p1 = curve.next(); p2 = curve.next(); if( (p = curve.next()) ) p3 = p; else p3 = p2; // Calculate the value t = (x - p1->first) / (p2->first - p1->first); c2 = (p2->second - p0->second) * (p2->first-p1->first) / (p2->first-p0->first); c3 = p1->second; c0 = -2*p2->second + 2*c3 + c2 + (p3->second - p1->second) * (p2->first - p1->first) / (p3->first - p1->first); c1 = p2->second - c3 - c2 - c0; val = ((c0*t + c1)*t + c2)*t + c3; if(val < 0.0) val = 0.0; if(val > 1.0) val = 1.0; return val; } TQPtrList > KCurve::getCurve() { TQPtrList > outlist; TQPair *p; TQPair *outpoint; p = m_points.first(); while(p) { outpoint = new TQPair(p->first, p->second); outlist.append(outpoint); p = m_points.next(); } return outlist; } void KCurve::setCurve(TQPtrList >inlist) { TQPair *p; TQPair *inpoint; m_points.clear(); inpoint = inlist.first(); while(inpoint) { p = new TQPair(inpoint->first, inpoint->second); m_points.append(p); inpoint = inlist.next(); } } void KCurve::leaveEvent( TQEvent * ) { } #include "kcurve.moc"