kis_curve_widget.cpp 16 KB
Newer Older
Boudewijn Rempt's avatar
Boudewijn Rempt committed
1
/*
C. Boemann's avatar
C. Boemann committed
2
 *  Copyright (c) 2005 C. Boemann <cbo@boemann.dk>
3
 *  Copyright (c) 2009 Dmitry Kazakov <dimula73@gmail.com>
Casper Boemann's avatar
Casper Boemann committed
4
 *
Boudewijn Rempt's avatar
Boudewijn Rempt committed
5 6 7 8
 *  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.
Adrian Page's avatar
Adrian Page committed
9
 *
Boudewijn Rempt's avatar
Boudewijn Rempt committed
10 11 12 13
 *  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.
Adrian Page's avatar
Adrian Page committed
14
 *
Boudewijn Rempt's avatar
Boudewijn Rempt committed
15 16 17 18
 *  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.
 */
Adrian Page's avatar
Adrian Page committed
19

Boudewijn Rempt's avatar
Boudewijn Rempt committed
20

Casper Boemann's avatar
Casper Boemann committed
21 22 23 24 25 26 27
// C++ includes.

#include <cmath>
#include <cstdlib>

// Qt includes.

28 29
#include <QPixmap>
#include <QPainter>
30
#include <QPoint>
31 32 33
#include <QPen>
#include <QEvent>
#include <QRect>
34
#include <QFont>
35
#include <QFontMetrics>
Laurent Montel's avatar
qt3to4  
Laurent Montel committed
36 37 38
#include <QMouseEvent>
#include <QKeyEvent>
#include <QPaintEvent>
Adrian Page's avatar
Adrian Page committed
39
#include <QList>
40
#include <QApplication>
Casper Boemann's avatar
Casper Boemann committed
41

42 43
#include <QSpinBox>

Casper Boemann's avatar
Casper Boemann committed
44 45
// KDE includes.

46
#include <kis_debug.h>
47
#include <kis_config.h>
48
#include <klocalizedstring.h>
Casper Boemann's avatar
Casper Boemann committed
49

50 51 52 53
#include <kis_signal_compressor.h>
#include <kis_thread_safe_signal_compressor.h>


Casper Boemann's avatar
Casper Boemann committed
54 55
// Local includes.

56 57 58
#include "widgets/kis_curve_widget.h"


59 60 61 62 63
#define bounds(x,a,b) (x<a ? a : (x>b ? b :x))
#define MOUSE_AWAY_THRES 15
#define POINT_AREA       1E-4
#define CURVE_AREA       1E-4

64 65
#include "kis_curve_widget_p.h"

66
KisCurveWidget::KisCurveWidget(QWidget *parent, Qt::WindowFlags f)
Boudewijn Rempt's avatar
Boudewijn Rempt committed
67
        : QWidget(parent, f), d(new KisCurveWidget::Private(this))
68
{
69
    setObjectName("KisCurveWidget");
70 71 72
    d->m_grab_point_index = -1;
    d->m_readOnlyMode   = false;
    d->m_guideVisible   = false;
Boudewijn Rempt's avatar
Boudewijn Rempt committed
73
    d->m_pixmapDirty = true;
74
    d->m_pixmapCache = 0;
75 76
    d->setState(ST_NORMAL);

77 78
    d->m_intIn = 0;
    d->m_intOut = 0;
Adrian Page's avatar
Adrian Page committed
79

80 81 82
    connect(&d->m_modifiedSignalsCompressor, SIGNAL(timeout()), SLOT(notifyModified()));
    connect(this, SIGNAL(compressorShouldEmitModified()), SLOT(slotCompressorShouldEmitModified()));

83
    setMouseTracking(true);
Adrian Page's avatar
Adrian Page committed
84 85
    setAutoFillBackground(false);
    setAttribute(Qt::WA_OpaquePaintEvent);
86
    setMinimumSize(150, 50);
Boudewijn Rempt's avatar
Boudewijn Rempt committed
87
    setMaximumSize(250, 250);
88 89


Laurent Montel's avatar
Laurent Montel committed
90
    setFocusPolicy(Qt::StrongFocus);
91 92
}

93
KisCurveWidget::~KisCurveWidget()
Casper Boemann's avatar
Casper Boemann committed
94
{
95
    delete d->m_pixmapCache;
96
    delete d;
Casper Boemann's avatar
Casper Boemann committed
97 98
}

99
void KisCurveWidget::setupInOutControls(QSpinBox *in, QSpinBox *out, int inMin, int inMax, int outMin, int outMax)
100
{
101 102
    dropInOutControls();

Boudewijn Rempt's avatar
Boudewijn Rempt committed
103 104
    d->m_intIn = in;
    d->m_intOut = out;
Boudewijn Rempt's avatar
Boudewijn Rempt committed
105

Boudewijn Rempt's avatar
Boudewijn Rempt committed
106
    if (!d->m_intIn || !d->m_intOut)
Boudewijn Rempt's avatar
Boudewijn Rempt committed
107
        return;
108

109 110 111 112
    d->m_inMin = inMin;
    d->m_inMax = inMax;
    d->m_outMin = outMin;
    d->m_outMax = outMax;
Boudewijn Rempt's avatar
Boudewijn Rempt committed
113

114 115 116 117
    int realInMin = qMin(inMin, inMax); // tilt elevation has range (90, 0), which QSpinBox can't handle
    int realInMax = qMax(inMin, inMax);

    d->m_intIn->setRange(realInMin, realInMax);
118
    d->m_intOut->setRange(d->m_outMin, d->m_outMax);
119

120 121
    connect(d->m_intIn, SIGNAL(valueChanged(int)), this, SLOT(inOutChanged(int)), Qt::UniqueConnection);
    connect(d->m_intOut, SIGNAL(valueChanged(int)), this, SLOT(inOutChanged(int)), Qt::UniqueConnection);
122

123
    d->syncIOControls();
Boudewijn Rempt's avatar
Boudewijn Rempt committed
124

125
}
126
void KisCurveWidget::dropInOutControls()
127
{
Boudewijn Rempt's avatar
Boudewijn Rempt committed
128
    if (!d->m_intIn || !d->m_intOut)
Boudewijn Rempt's avatar
Boudewijn Rempt committed
129
        return;
130 131 132 133

    disconnect(d->m_intIn, SIGNAL(valueChanged(int)), this, SLOT(inOutChanged(int)));
    disconnect(d->m_intOut, SIGNAL(valueChanged(int)), this, SLOT(inOutChanged(int)));

134
    d->m_intIn = d->m_intOut = 0;
Boudewijn Rempt's avatar
Boudewijn Rempt committed
135

136 137
}

138
void KisCurveWidget::inOutChanged(int)
139 140 141
{
    QPointF pt;

Boudewijn Rempt's avatar
Boudewijn Rempt committed
142
    Q_ASSERT(d->m_grab_point_index >= 0);
143

144 145
    pt.setX(d->io2sp(d->m_intIn->value(), d->m_inMin, d->m_inMax));
    pt.setY(d->io2sp(d->m_intOut->value(), d->m_outMin, d->m_outMax));
Boudewijn Rempt's avatar
Boudewijn Rempt committed
146

147
    bool newPoint = false;
Boudewijn Rempt's avatar
Boudewijn Rempt committed
148
    if (d->jumpOverExistingPoints(pt, d->m_grab_point_index)) {
149 150
        newPoint = true;

151 152
        d->m_curve.setPoint(d->m_grab_point_index, pt);
        d->m_grab_point_index = d->m_curve.points().indexOf(pt);
153
        emit pointSelectedChanged();
154
    } else {
155
        pt = d->m_curve.points()[d->m_grab_point_index];
156
    }
Boudewijn Rempt's avatar
Boudewijn Rempt committed
157

158 159
    if (!newPoint) {
        // if there is a new Point, no point in rewriting values in spinboxes
160

161 162
        d->m_intIn->blockSignals(true);
        d->m_intOut->blockSignals(true);
163

164 165
        d->m_intIn->setValue(d->sp2io(pt.x(), d->m_inMin, d->m_inMax));
        d->m_intOut->setValue(d->sp2io(pt.y(), d->m_outMin, d->m_outMax));
166

167 168 169
        d->m_intIn->blockSignals(false);
        d->m_intOut->blockSignals(false);
    }
170

171
    d->setCurveModified(false);
172 173 174
}


175
void KisCurveWidget::reset(void)
Casper Boemann's avatar
Casper Boemann committed
176
{
177
    d->m_grab_point_index = -1;
178
    emit pointSelectedChanged();
179
    d->m_guideVisible = false;
Boudewijn Rempt's avatar
Boudewijn Rempt committed
180

181 182 183 184 185
    //remove total - 2 points.
    while (d->m_curve.points().count() - 2 ) {
        d->m_curve.removePoint(d->m_curve.points().count() - 2);
    }

186
    d->setCurveModified();
Casper Boemann's avatar
Casper Boemann committed
187 188
}

189
void KisCurveWidget::setCurveGuide(const QColor & color)
Casper Boemann's avatar
Casper Boemann committed
190
{
191 192 193
    d->m_guideVisible = true;
    d->m_colorGuide   = color;

Casper Boemann's avatar
Casper Boemann committed
194 195
}

196
void KisCurveWidget::setPixmap(const QPixmap & pix)
Casper Boemann's avatar
Casper Boemann committed
197
{
198
    d->m_pix = pix;
Boudewijn Rempt's avatar
Boudewijn Rempt committed
199
    d->m_pixmapDirty = true;
200
    d->setCurveRepaint();
Casper Boemann's avatar
Casper Boemann committed
201 202
}

203 204 205 206 207 208 209 210 211 212 213 214 215 216 217
QPixmap KisCurveWidget::getPixmap()
{
    return d->m_pix;
}

void KisCurveWidget::setBasePixmap(const QPixmap &pix)
{
    d->m_pixmapBase = pix;
}

QPixmap KisCurveWidget::getBasePixmap()
{
    return d->m_pixmapBase;
}

218 219 220 221 222
bool KisCurveWidget::pointSelected() const
{
    return d->m_grab_point_index > 0 && d->m_grab_point_index < d->m_curve.points().count() - 1;
}

223
void KisCurveWidget::keyPressEvent(QKeyEvent *e)
Casper Boemann's avatar
Casper Boemann committed
224
{
Boudewijn Rempt's avatar
Boudewijn Rempt committed
225
    if (e->key() == Qt::Key_Delete || e->key() == Qt::Key_Backspace) {
226
        if (d->m_grab_point_index > 0 && d->m_grab_point_index < d->m_curve.points().count() - 1) {
227 228
            //x() find closest point to get focus afterwards
            double grab_point_x = d->m_curve.points()[d->m_grab_point_index].x();
Adrian Page's avatar
Adrian Page committed
229

230 231
            int left_of_grab_point_index = d->m_grab_point_index - 1;
            int right_of_grab_point_index = d->m_grab_point_index + 1;
Adrian Page's avatar
Adrian Page committed
232 233
            int new_grab_point_index;

234 235
            if (fabs(d->m_curve.points()[left_of_grab_point_index].x() - grab_point_x) <
                    fabs(d->m_curve.points()[right_of_grab_point_index].x() - grab_point_x)) {
Adrian Page's avatar
Adrian Page committed
236
                new_grab_point_index = left_of_grab_point_index;
Boudewijn Rempt's avatar
Boudewijn Rempt committed
237
            } else {
238
                new_grab_point_index = d->m_grab_point_index;
Adrian Page's avatar
Adrian Page committed
239
            }
240
            d->m_curve.removePoint(d->m_grab_point_index);
241
            d->m_grab_point_index = new_grab_point_index;
242
            emit pointSelectedChanged();
Boudewijn Rempt's avatar
Boudewijn Rempt committed
243 244
            setCursor(Qt::ArrowCursor);
            d->setState(ST_NORMAL);
245
        }
246
        e->accept();
Boudewijn Rempt's avatar
Boudewijn Rempt committed
247
        d->setCurveModified();
Boudewijn Rempt's avatar
Boudewijn Rempt committed
248
    } else if (e->key() == Qt::Key_Escape && d->state() != ST_NORMAL) {
249
        d->m_curve.setPoint(d->m_grab_point_index, QPointF(d->m_grabOriginalX, d->m_grabOriginalY) );
Boudewijn Rempt's avatar
Boudewijn Rempt committed
250
        setCursor(Qt::ArrowCursor);
Boudewijn Rempt's avatar
Boudewijn Rempt committed
251
        d->setState(ST_NORMAL);
252

253
        e->accept();
Boudewijn Rempt's avatar
Boudewijn Rempt committed
254
        d->setCurveModified();
Boudewijn Rempt's avatar
Boudewijn Rempt committed
255
    } else if ((e->key() == Qt::Key_A || e->key() == Qt::Key_Insert) && d->state() == ST_NORMAL) {
Boudewijn Rempt's avatar
Boudewijn Rempt committed
256
        /* FIXME: Lets user choose the hotkeys */
257
        addPointInTheMiddle();
258
        e->accept();
Boudewijn Rempt's avatar
Boudewijn Rempt committed
259
    } else
Thomas Zander's avatar
Thomas Zander committed
260
        QWidget::keyPressEvent(e);
Casper Boemann's avatar
Casper Boemann committed
261 262
}

263
void KisCurveWidget::addPointInTheMiddle()
Casper Boemann's avatar
Casper Boemann committed
264
{
Adrian Page's avatar
Adrian Page committed
265
    QPointF pt(0.5, d->m_curve.value(0.5));
Boudewijn Rempt's avatar
Boudewijn Rempt committed
266

Boudewijn Rempt's avatar
Boudewijn Rempt committed
267
    if (!d->jumpOverExistingPoints(pt, -1))
Boudewijn Rempt's avatar
Boudewijn Rempt committed
268
        return;
269

270
    d->m_grab_point_index = d->m_curve.addPoint(pt);
271
    emit pointSelectedChanged();
Boudewijn Rempt's avatar
Boudewijn Rempt committed
272

Boudewijn Rempt's avatar
Boudewijn Rempt committed
273
    if (d->m_intIn)
Boudewijn Rempt's avatar
Boudewijn Rempt committed
274
        d->m_intIn->setFocus(Qt::TabFocusReason);
275 276
    d->setCurveModified();
}
Adrian Page's avatar
Adrian Page committed
277

278
void KisCurveWidget::resizeEvent(QResizeEvent *e)
279
{
Boudewijn Rempt's avatar
Boudewijn Rempt committed
280
    d->m_pixmapDirty = true;
281 282 283
    QWidget::resizeEvent(e);
}

284
void KisCurveWidget::paintEvent(QPaintEvent *)
285
{
Boudewijn Rempt's avatar
Boudewijn Rempt committed
286 287
    int    wWidth = width() - 1;
    int    wHeight = height() - 1;
Adrian Page's avatar
Adrian Page committed
288

289

290
    QPainter p(this);
291

Boudewijn Rempt's avatar
Boudewijn Rempt committed
292
    // Antialiasing is not a good idea here, because
293 294 295
    // the grid will drift one pixel to any side due to rounding of int
    // FIXME: let's user tell the last word (in config)
    //p.setRenderHint(QPainter::Antialiasing);
296 297 298
     QPalette appPalette = QApplication::palette();
     p.fillRect(rect(), appPalette.color(QPalette::Base)); // clear out previous paint call results

299
     // make the entire widget grayed out if it is disabled
300 301 302 303
     if (!this->isEnabled()) {
        p.setOpacity(0.2);
     }

304

Adrian Page's avatar
Adrian Page committed
305

306
    //  draw background
307
    if (!d->m_pix.isNull()) {
Boudewijn Rempt's avatar
Boudewijn Rempt committed
308
        if (d->m_pixmapDirty || !d->m_pixmapCache) {
309
            delete d->m_pixmapCache;
Boudewijn Rempt's avatar
Boudewijn Rempt committed
310 311 312 313 314
            d->m_pixmapCache = new QPixmap(width(), height());
            QPainter cachePainter(d->m_pixmapCache);

            cachePainter.scale(1.0*width() / d->m_pix.width(), 1.0*height() / d->m_pix.height());
            cachePainter.drawPixmap(0, 0, d->m_pix);
Boudewijn Rempt's avatar
Boudewijn Rempt committed
315
            d->m_pixmapDirty = false;
Boudewijn Rempt's avatar
Boudewijn Rempt committed
316
        }
Boudewijn Rempt's avatar
Boudewijn Rempt committed
317
        p.drawPixmap(0, 0, *d->m_pixmapCache);
Scott Petrovic's avatar
Scott Petrovic committed
318
    }
Boudewijn Rempt's avatar
Boudewijn Rempt committed
319

320 321
    d->drawGrid(p, wWidth, wHeight);

322 323
    KisConfig cfg(true);
    if (cfg.antialiasCurves()) {
324
        p.setRenderHint(QPainter::Antialiasing);
325
    }
326

327
    // Draw curve.
328 329 330 331
    double curY;
    double normalizedX;
    int x;

Boudewijn Rempt's avatar
Boudewijn Rempt committed
332 333
    QPolygonF poly;

334
    p.setPen(QPen(appPalette.color(QPalette::Text), 2, Qt::SolidLine));
Boudewijn Rempt's avatar
Boudewijn Rempt committed
335
    for (x = 0 ; x < wWidth ; x++) {
336
        normalizedX = double(x) / wWidth;
337
        curY = wHeight - d->m_curve.value(normalizedX) * wHeight;
338

Boudewijn Rempt's avatar
Boudewijn Rempt committed
339 340
        /**
         * Keep in mind that QLineF rounds doubles
341
         * to ints mathematically, not just rounds down
Boudewijn Rempt's avatar
Boudewijn Rempt committed
342 343
         * like in C
         */
Boudewijn Rempt's avatar
Boudewijn Rempt committed
344
        poly.append(QPointF(x, curY));
345
    }
Boudewijn Rempt's avatar
Boudewijn Rempt committed
346 347
    poly.append(QPointF(x, wHeight - d->m_curve.value(1.0) * wHeight));
    p.drawPolyline(poly);
Adrian Page's avatar
Adrian Page committed
348

349 350 351 352 353 354 355 356 357 358 359 360 361 362 363
    QPainterPath fillCurvePath;
    QPolygonF fillPoly = poly;
    fillPoly.append(QPoint(rect().width(), rect().height()));
    fillPoly.append(QPointF(0,rect().height()));

    // add a couple points to the edges so it fills in below always

    QColor fillColor = appPalette.color(QPalette::Text);
    fillColor.setAlphaF(0.2);

    fillCurvePath.addPolygon(fillPoly);
    p.fillPath(fillCurvePath, fillColor);



364
    // Drawing curve handles.
365 366 367
    double curveX;
    double curveY;
    if (!d->m_readOnlyMode) {
368 369 370
        for (int i = 0; i < d->m_curve.points().count(); ++i) {
            curveX = d->m_curve.points().at(i).x();
            curveY = d->m_curve.points().at(i).y();
371

372 373
            int handleSize = 12; // how big should control points be (diamater size)

374
            if (i == d->m_grab_point_index) {
375 376 377 378 379 380
                // active point is slightly more "bold"
                p.setPen(QPen(appPalette.color(QPalette::Text), 4, Qt::SolidLine));
                p.drawEllipse(QRectF(curveX * wWidth - (handleSize*0.5),
                                     wHeight - (handleSize*0.5) - curveY * wHeight,
                                     handleSize,
                                     handleSize));
Boudewijn Rempt's avatar
Boudewijn Rempt committed
381
            } else {
382
                p.setPen(QPen(appPalette.color(QPalette::Text), 2, Qt::SolidLine));
383 384 385 386
                p.drawEllipse(QRectF(curveX * wWidth - (handleSize*0.5),
                                     wHeight - (handleSize*0.5) - curveY * wHeight,
                                     handleSize,
                                     handleSize));
387 388 389
            }
        }
    }
390 391 392 393 394

    // add border around widget to help contain everything
    QPainterPath widgetBoundsPath;
    widgetBoundsPath.addRect(rect());
    p.strokePath(widgetBoundsPath, appPalette.color(QPalette::Text));
395 396 397


    p.setOpacity(1.0); // reset to 1.0 in case we were drawing a disabled widget before
Casper Boemann's avatar
Casper Boemann committed
398 399
}

400
void KisCurveWidget::mousePressEvent(QMouseEvent * e)
Adrian Page's avatar
Adrian Page committed
401
{
402
    if (d->m_readOnlyMode) return;
Adrian Page's avatar
Adrian Page committed
403 404 405 406

    if (e->button() != Qt::LeftButton)
        return;

Boudewijn Rempt's avatar
Boudewijn Rempt committed
407 408
    double x = e->pos().x() / (double)(width() - 1);
    double y = 1.0 - e->pos().y() / (double)(height() - 1);
409

Adrian Page's avatar
Adrian Page committed
410

Boudewijn Rempt's avatar
Boudewijn Rempt committed
411

412
    int closest_point_index = d->nearestPointInRange(QPointF(x, y), width(), height());
Boudewijn Rempt's avatar
Boudewijn Rempt committed
413
    if (closest_point_index < 0) {
Boudewijn Rempt's avatar
Boudewijn Rempt committed
414
        QPointF newPoint(x, y);
Boudewijn Rempt's avatar
Boudewijn Rempt committed
415 416
        if (!d->jumpOverExistingPoints(newPoint, -1))
            return;
417
        d->m_grab_point_index = d->m_curve.addPoint(newPoint);
418
        emit pointSelectedChanged();
Boudewijn Rempt's avatar
Boudewijn Rempt committed
419
    } else {
Boudewijn Rempt's avatar
Boudewijn Rempt committed
420
        d->m_grab_point_index = closest_point_index;
421
        emit pointSelectedChanged();
422
    }
Boudewijn Rempt's avatar
Boudewijn Rempt committed
423

424 425 426 427
    d->m_grabOriginalX = d->m_curve.points()[d->m_grab_point_index].x();
    d->m_grabOriginalY = d->m_curve.points()[d->m_grab_point_index].y();
    d->m_grabOffsetX = d->m_curve.points()[d->m_grab_point_index].x() - x;
    d->m_grabOffsetY = d->m_curve.points()[d->m_grab_point_index].y() - y;
428
    d->m_curve.setPoint(d->m_grab_point_index, QPointF(x + d->m_grabOffsetX, y + d->m_grabOffsetY));
Boudewijn Rempt's avatar
Boudewijn Rempt committed
429

430
    d->m_draggedAwayPointIndex = -1;
Boudewijn Rempt's avatar
Boudewijn Rempt committed
431 432
    d->setState(ST_DRAG);

433 434

    d->setCurveModified();
Casper Boemann's avatar
Casper Boemann committed
435 436
}

437

438
void KisCurveWidget::mouseReleaseEvent(QMouseEvent *e)
Casper Boemann's avatar
Casper Boemann committed
439
{
440
    if (d->m_readOnlyMode) return;
Adrian Page's avatar
Adrian Page committed
441

442 443
    if (e->button() != Qt::LeftButton)
        return;
Adrian Page's avatar
Adrian Page committed
444

Boudewijn Rempt's avatar
Boudewijn Rempt committed
445
    setCursor(Qt::ArrowCursor);
446
    d->setState(ST_NORMAL);
Boudewijn Rempt's avatar
Boudewijn Rempt committed
447

448
    d->setCurveModified();
Casper Boemann's avatar
Casper Boemann committed
449 450
}

451

452
void KisCurveWidget::mouseMoveEvent(QMouseEvent * e)
Casper Boemann's avatar
Casper Boemann committed
453
{
454
    if (d->m_readOnlyMode) return;
Adrian Page's avatar
Adrian Page committed
455

Boudewijn Rempt's avatar
Boudewijn Rempt committed
456 457
    double x = e->pos().x() / (double)(width() - 1);
    double y = 1.0 - e->pos().y() / (double)(height() - 1);
Adrian Page's avatar
Adrian Page committed
458

459
    if (d->state() == ST_NORMAL) { // If no point is selected set the cursor shape if on top
460
        int nearestPointIndex = d->nearestPointInRange(QPointF(x, y), width(), height());
Boudewijn Rempt's avatar
Boudewijn Rempt committed
461

Adrian Page's avatar
Adrian Page committed
462
        if (nearestPointIndex < 0)
Boudewijn Rempt's avatar
Boudewijn Rempt committed
463
            setCursor(Qt::ArrowCursor);
464
        else
Boudewijn Rempt's avatar
Boudewijn Rempt committed
465
            setCursor(Qt::CrossCursor);
Boudewijn Rempt's avatar
Boudewijn Rempt committed
466
    } else { // Else, drag the selected point
Boudewijn Rempt's avatar
Boudewijn Rempt committed
467 468 469 470 471
        bool crossedHoriz = e->pos().x() - width() > MOUSE_AWAY_THRES ||
                            e->pos().x() < -MOUSE_AWAY_THRES;
        bool crossedVert =  e->pos().y() - height() > MOUSE_AWAY_THRES ||
                            e->pos().y() < -MOUSE_AWAY_THRES;

Boudewijn Rempt's avatar
Boudewijn Rempt committed
472
        bool removePoint = (crossedHoriz || crossedVert);
473

Boudewijn Rempt's avatar
Boudewijn Rempt committed
474
        if (!removePoint && d->m_draggedAwayPointIndex >= 0) {
475
            // point is no longer dragged away so reinsert it
476
            QPointF newPoint(d->m_draggedAwayPoint);
477
            d->m_grab_point_index = d->m_curve.addPoint(newPoint);
478
            d->m_draggedAwayPointIndex = -1;
479 480
        }

Boudewijn Rempt's avatar
Boudewijn Rempt committed
481
        if (removePoint &&
Boudewijn Rempt's avatar
Boudewijn Rempt committed
482
                (d->m_draggedAwayPointIndex >= 0))
Boudewijn Rempt's avatar
Boudewijn Rempt committed
483
            return;
484

485

Boudewijn Rempt's avatar
Boudewijn Rempt committed
486
        setCursor(Qt::CrossCursor);
Adrian Page's avatar
Adrian Page committed
487

488 489
        x += d->m_grabOffsetX;
        y += d->m_grabOffsetY;
Adrian Page's avatar
Adrian Page committed
490

491 492
        double leftX;
        double rightX;
493
        if (d->m_grab_point_index == 0) {
494
            leftX = 0.0;
495
            if (d->m_curve.points().count() > 1)
496
                rightX = d->m_curve.points()[d->m_grab_point_index + 1].x() - POINT_AREA;
497 498
            else
                rightX = 1.0;
499
        } else if (d->m_grab_point_index == d->m_curve.points().count() - 1) {
500
            leftX = d->m_curve.points()[d->m_grab_point_index - 1].x() + POINT_AREA;
501
            rightX = 1.0;
Boudewijn Rempt's avatar
Boudewijn Rempt committed
502
        } else {
503
            Q_ASSERT(d->m_grab_point_index > 0 && d->m_grab_point_index < d->m_curve.points().count() - 1);
Adrian Page's avatar
Adrian Page committed
504

505
            // the 1E-4 addition so we can grab the dot later.
506 507
            leftX = d->m_curve.points()[d->m_grab_point_index - 1].x() + POINT_AREA;
            rightX = d->m_curve.points()[d->m_grab_point_index + 1].x() - POINT_AREA;
508
        }
Adrian Page's avatar
Adrian Page committed
509

Boudewijn Rempt's avatar
Boudewijn Rempt committed
510 511
        x = bounds(x, leftX, rightX);
        y = bounds(y, 0., 1.);
Adrian Page's avatar
Adrian Page committed
512

513
        d->m_curve.setPoint(d->m_grab_point_index, QPointF(x, y));
Adrian Page's avatar
Adrian Page committed
514

515
        if (removePoint && d->m_curve.points().count() > 2) {
516
            d->m_draggedAwayPoint = d->m_curve.points()[d->m_grab_point_index];
517
            d->m_draggedAwayPointIndex = d->m_grab_point_index;
518 519
            d->m_curve.removePoint(d->m_grab_point_index);
            d->m_grab_point_index = bounds(d->m_grab_point_index, 0, d->m_curve.points().count() - 1);
520
            emit pointSelectedChanged();
521 522
        }

Boudewijn Rempt's avatar
Boudewijn Rempt committed
523
        d->setCurveModified();
524
    }
525
}
Casper Boemann's avatar
Casper Boemann committed
526

527
KisCubicCurve KisCurveWidget::curve()
528
{
529
    return d->m_curve;
530 531
}

532
void KisCurveWidget::setCurve(KisCubicCurve inlist)
533
{
534 535
    d->m_curve = inlist;
    d->m_grab_point_index = qBound(0, d->m_grab_point_index, d->m_curve.points().count() - 1);
536
    d->setCurveModified();
537
    emit pointSelectedChanged();
538 539
}

540
void KisCurveWidget::leaveEvent(QEvent *)
Casper Boemann's avatar
Casper Boemann committed
541 542 543
{
}

544 545 546 547 548 549 550 551 552
void KisCurveWidget::notifyModified()
{
    emit modified();
}

void KisCurveWidget::slotCompressorShouldEmitModified()
{
    d->m_modifiedSignalsCompressor.start();
}