kis_curve_widget_p.h 7.27 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>
Boudewijn Rempt's avatar
Boudewijn Rempt committed
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
 *  Copyright (c) 2009 Dmitry Kazakov <dimula73@gmail.com>
 *
 *  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.
 */
19 20
#ifndef _KIS_CURVE_WIDGET_P_H_
#define _KIS_CURVE_WIDGET_P_H_
21
#include <kis_cubic_curve.h>
22 23
#include <QApplication>
#include <QPalette>
24
#include <KisSpinBoxSplineUnitConverter.h>
25

Boudewijn Rempt's avatar
Boudewijn Rempt committed
26
enum enumState {
27 28 29 30 31
    ST_NORMAL,
    ST_DRAG
};

/**
32
 * Private members for KisCurveWidget class
33
 */
34
class Q_DECL_HIDDEN KisCurveWidget::Private
35 36
{

37
    KisCurveWidget *m_curveWidget;
38
    KisSpinBoxSplineUnitConverter unitConverter;
39 40 41


public:
42
    Private(KisCurveWidget *parent);
43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59

    /* Dragging variables */
    int m_grab_point_index;
    double m_grabOffsetX;
    double m_grabOffsetY;
    double m_grabOriginalX;
    double m_grabOriginalY;
    QPointF m_draggedAwayPoint;
    int m_draggedAwayPointIndex;

    bool m_readOnlyMode;
    bool m_guideVisible;
    QColor m_colorGuide;


    /* The curve itself */
    bool    m_splineDirty;
60
    KisCubicCurve m_curve;
61 62

    QPixmap m_pix;
63
    QPixmap m_pixmapBase;
64 65 66 67 68 69
    bool m_pixmapDirty;
    QPixmap *m_pixmapCache;

    /* In/Out controls */
    QSpinBox *m_intIn;
    QSpinBox *m_intOut;
Boudewijn Rempt's avatar
Boudewijn Rempt committed
70

71
    /* Working range of them */
72 73 74 75
    int m_inMin;
    int m_inMax;
    int m_outMin;
    int m_outMax;
76 77

    /**
Boudewijn Rempt's avatar
Boudewijn Rempt committed
78
     * State functions.
79
     * At the moment used only for dragging.
Boudewijn Rempt's avatar
Boudewijn Rempt committed
80
     */
Boudewijn Rempt's avatar
Boudewijn Rempt committed
81
    enumState m_state;
82 83 84 85

    inline void setState(enumState st);
    inline enumState state() const;

86 87 88 89
    /**
     * Compresses the modified() signals
     */
    KisThreadSafeSignalCompressor m_modifiedSignalsCompressor;
90 91


luz paz's avatar
luz paz committed
92
    /*** Internal routines ***/
Boudewijn Rempt's avatar
Boudewijn Rempt committed
93

94
    /**
luz paz's avatar
luz paz committed
95
     * Common update routines
96
     */
97
    void setCurveModified(bool rewriteSpinBoxesValues);
98 99
    void setCurveRepaint();

Boudewijn Rempt's avatar
Boudewijn Rempt committed
100

101
    /**
Boudewijn Rempt's avatar
Boudewijn Rempt committed
102 103
     * Convert working range of
     * In/Out controls to normalized
104
     * range of spline (and reverse)
105
     * See notes on KisSpinBoxSplineUnitConverter
106
     */
107 108
    double io2sp(int x, int min, int max);
    int sp2io(double x, int min, int max);
Boudewijn Rempt's avatar
Boudewijn Rempt committed
109

110 111

    /**
Yuri Chornoivan's avatar
Yuri Chornoivan committed
112 113 114
     * Check whether newly created/moved point @p pt doesn't overlap
     * with any of existing ones from @p m_points and adjusts its coordinates.
     * @p skipIndex is the index of the point, that shouldn't be taken
115
     * into account during the search
Yuri Chornoivan's avatar
Yuri Chornoivan committed
116
     * (e.g. because it's @p pt itself)
117 118 119 120 121 122 123 124
     *
     * Returns false in case the point can't be placed anywhere
     * without overlapping
     */
    bool jumpOverExistingPoints(QPointF &pt, int skipIndex);


    /**
Boudewijn Rempt's avatar
Boudewijn Rempt committed
125
     * Synchronize In/Out spinboxes with the curve
126 127 128 129
     */
    void syncIOControls();

    /**
Yuri Chornoivan's avatar
Yuri Chornoivan committed
130
     * Find the nearest point to @p pt from m_points
131 132
     */
    int nearestPointInRange(QPointF pt, int wWidth, int wHeight) const;
Boudewijn Rempt's avatar
Boudewijn Rempt committed
133

134 135 136
    /**
     * Nothing to be said! =)
     */
Boudewijn Rempt's avatar
Boudewijn Rempt committed
137
    inline
138 139 140 141
    void drawGrid(QPainter &p, int wWidth, int wHeight);

};

142
KisCurveWidget::Private::Private(KisCurveWidget *parent)
143
    : m_modifiedSignalsCompressor(100, KisSignalCompressor::Mode::FIRST_INACTIVE)
144
{
Boudewijn Rempt's avatar
Boudewijn Rempt committed
145
    m_curveWidget = parent;
146 147
}

148
double KisCurveWidget::Private::io2sp(int x, int min, int max)
149
{
150
    return unitConverter.io2sp(x, min, max);
151 152
}

153
int KisCurveWidget::Private::sp2io(double x, int min, int max)
154
{
155
    return unitConverter.sp2io(x, min, max);
156 157 158
}


159
bool KisCurveWidget::Private::jumpOverExistingPoints(QPointF &pt, int skipIndex)
160
{
161
    Q_FOREACH (const QPointF &it, m_curve.points()) {
162
        if (m_curve.points().indexOf(it) == skipIndex)
Boudewijn Rempt's avatar
Boudewijn Rempt committed
163
            continue;
164
        if (fabs(it.x() - pt.x()) < POINT_AREA) {
Boudewijn Rempt's avatar
Boudewijn Rempt committed
165 166
            pt.rx() = pt.x() >= it.x() ?
                      it.x() + POINT_AREA : it.x() - POINT_AREA;
167
        }
168
    }
Boudewijn Rempt's avatar
Boudewijn Rempt committed
169
    return (pt.x() >= 0 && pt.x() <= 1.);
170 171
}

172
int KisCurveWidget::Private::nearestPointInRange(QPointF pt, int wWidth, int wHeight) const
173 174 175 176 177
{
    double nearestDistanceSquared = 1000;
    int nearestIndex = -1;
    int i = 0;

178
    Q_FOREACH (const QPointF & point, m_curve.points()) {
179
        double distanceSquared = (pt.x() - point.x()) *
Boudewijn Rempt's avatar
Boudewijn Rempt committed
180 181 182
                                 (pt.x() - point.x()) +
                                 (pt.y() - point.y()) *
                                 (pt.y() - point.y());
183 184 185 186 187 188 189 190 191

        if (distanceSquared < nearestDistanceSquared) {
            nearestIndex = i;
            nearestDistanceSquared = distanceSquared;
        }
        ++i;
    }

    if (nearestIndex >= 0) {
192 193
        if (fabs(pt.x() - m_curve.points()[nearestIndex].x()) *(wWidth - 1) < 5 &&
                fabs(pt.y() - m_curve.points()[nearestIndex].y()) *(wHeight - 1) < 5) {
194 195 196 197 198 199 200 201 202 203 204
            return nearestIndex;
        }
    }

    return -1;
}


#define div2_round(x) (((x)+1)>>1)
#define div4_round(x) (((x)+2)>>2)

205
void KisCurveWidget::Private::drawGrid(QPainter &p, int wWidth, int wHeight)
206 207 208 209
{
    /**
     * Hint: widget size should conform
     * formula 4n+5 to draw grid correctly
Boudewijn Rempt's avatar
Boudewijn Rempt committed
210
     * without curious shifts between
211 212 213 214 215
     * spline and it caused by rounding
     *
     * That is not mandatory but desirable
     */

216 217 218
    QPalette appPalette = QApplication::palette();

    p.setPen(QPen(appPalette.color(QPalette::Background), 1, Qt::SolidLine));
219 220 221
    p.drawLine(div4_round(wWidth), 0, div4_round(wWidth), wHeight);
    p.drawLine(div2_round(wWidth), 0, div2_round(wWidth), wHeight);
    p.drawLine(div4_round(3*wWidth), 0, div4_round(3*wWidth), wHeight);
Boudewijn Rempt's avatar
Boudewijn Rempt committed
222

223 224 225 226 227 228
    p.drawLine(0, div4_round(wHeight), wWidth, div4_round(wHeight));
    p.drawLine(0, div2_round(wHeight), wWidth, div2_round(wHeight));
    p.drawLine(0, div4_round(3*wHeight), wWidth, div4_round(3*wHeight));

}

229
void KisCurveWidget::Private::syncIOControls()
230
{
Boudewijn Rempt's avatar
Boudewijn Rempt committed
231 232
    if (!m_intIn || !m_intOut)
        return;
233

Boudewijn Rempt's avatar
Boudewijn Rempt committed
234
    bool somethingSelected = (m_grab_point_index >= 0);
235 236 237 238

    m_intIn->setEnabled(somethingSelected);
    m_intOut->setEnabled(somethingSelected);

Boudewijn Rempt's avatar
Boudewijn Rempt committed
239 240 241
    if (m_grab_point_index >= 0) {
        m_intIn->blockSignals(true);
        m_intOut->blockSignals(true);
Boudewijn Rempt's avatar
Boudewijn Rempt committed
242

243 244
        m_intIn->setValue(sp2io(m_curve.points()[m_grab_point_index].x(), m_inMin, m_inMax));
        m_intOut->setValue(sp2io(m_curve.points()[m_grab_point_index].y(), m_outMin, m_outMax));
Boudewijn Rempt's avatar
Boudewijn Rempt committed
245

Boudewijn Rempt's avatar
Boudewijn Rempt committed
246 247 248 249
        m_intIn->blockSignals(false);
        m_intOut->blockSignals(false);
    } else {
        /*FIXME: Ideally, these controls should hide away now */
250 251 252
    }
}

253
void KisCurveWidget::Private::setCurveModified(bool rewriteSpinBoxesValues = true)
254
{
255 256 257
    if (rewriteSpinBoxesValues) {
        syncIOControls();
    }
Boudewijn Rempt's avatar
Boudewijn Rempt committed
258
    m_splineDirty = true;
259
    m_curveWidget->update();
260
    m_curveWidget->emit compressorShouldEmitModified();
261 262
}

263
void KisCurveWidget::Private::setCurveRepaint()
264 265 266 267
{
    m_curveWidget->update();
}

268
void KisCurveWidget::Private::setState(enumState st)
269
{
Boudewijn Rempt's avatar
Boudewijn Rempt committed
270
    m_state = st;
271 272 273
}


274
enumState KisCurveWidget::Private::state() const
275 276 277 278 279 280
{
    return m_state;
}


#endif /* _KIS_CURVE_WIDGET_P_H_ */