1 /*
2  *  Copyright (c) 2004 Cyrille Berger <cberger@cberger.net>
3  *                2016 Sven Langkamp <sven.langkamp@gmail.com>
4  *
5  *  This program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2 of the License, or
8  *  (at your option) any later version.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with this program; if not, write to the Free Software
17  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18  */
19 
20 #include "kis_stopgradient_editor.h"
21 #include <QPainter>
22 #include <QSpinBox>
23 #include <QDoubleSpinBox>
24 #include <QPoint>
25 #include <QMenu>
26 
27 #include <KoColorSpace.h>
28 #include <resources/KoStopGradient.h>
29 
30 #include "kis_debug.h"
31 
32 #include <kis_icon_utils.h>
33 
34 /****************************** KisStopGradientEditor ******************************/
35 
KisStopGradientEditor(QWidget * parent)36 KisStopGradientEditor::KisStopGradientEditor(QWidget *parent)
37     : QWidget(parent),
38       m_gradient(0),
39       m_fgColor(KoColor()),
40       m_bgColor(KoColor())
41 {
42     setupUi(this);
43 
44     connect(gradientSlider, SIGNAL(sigSelectedStop(int)), this, SLOT(stopChanged(int)));
45     connect(nameedit, SIGNAL(editingFinished()), this, SLOT(nameChanged()));
46     connect(colorButton, SIGNAL(changed(KoColor)), SLOT(colorChanged(KoColor)));
47 
48 
49     connect(colorRadioButton, SIGNAL(toggled(bool)), this, SLOT(stopTypeChanged()));
50     connect(foregroundRadioButton, SIGNAL(toggled(bool)), this, SLOT(stopTypeChanged()));
51     connect(backgroundRadioButton, SIGNAL(toggled(bool)), this, SLOT(stopTypeChanged()));
52 
53     opacitySlider->setPrefix(i18n("Opacity: "));
54     opacitySlider->setRange(0.0, 1.0, 2);
55     connect(opacitySlider, SIGNAL(valueChanged(qreal)), this, SLOT(opacityChanged(qreal)));
56 
57 
58     buttonReverse->setIcon(KisIconUtils::loadIcon("transform_icons_mirror_x"));
59     buttonReverse->setToolTip(i18n("Flip Gradient"));
60     KisIconUtils::updateIcon(buttonReverse);
61     connect(buttonReverse, SIGNAL(pressed()), SLOT(reverse()));
62 
63     buttonReverseSecond->setIcon(KisIconUtils::loadIcon("transform_icons_mirror_x"));
64     buttonReverseSecond->setToolTip(i18n("Flip Gradient"));
65     KisIconUtils::updateIcon(buttonReverseSecond);
66     connect(buttonReverseSecond, SIGNAL(clicked()), SLOT(reverse()));
67 
68     this->setContextMenuPolicy(Qt::CustomContextMenu);
69     connect(this, SIGNAL(customContextMenuRequested(const QPoint &)),
70             this, SLOT(showContextMenu(const QPoint &)));
71 
72     setCompactMode(false);
73 
74     setGradient(0);
75     stopChanged(-1);
76 }
77 
KisStopGradientEditor(KoStopGradient * gradient,QWidget * parent,const char * name,const QString & caption,const KoColor & fgColor,const KoColor & bgColor)78 KisStopGradientEditor::KisStopGradientEditor(KoStopGradient* gradient, QWidget *parent, const char* name, const QString& caption,
79       const KoColor &fgColor, const KoColor &bgColor)
80     : KisStopGradientEditor(parent)
81 {
82     m_fgColor = fgColor;
83     m_bgColor = bgColor;
84     setObjectName(name);
85     setWindowTitle(caption);
86     setGradient(gradient);
87 }
88 
setCompactMode(bool value)89 void KisStopGradientEditor::setCompactMode(bool value)
90 {
91     lblName->setVisible(!value);
92     buttonReverse->setVisible(!value);
93     nameedit->setVisible(!value);
94     foregroundRadioButton->setVisible(!value);
95     backgroundRadioButton->setVisible(!value);
96     colorRadioButton->setVisible(!value);
97 
98     buttonReverseSecond->setVisible(value);
99 }
100 
setGradient(KoStopGradient * gradient)101 void KisStopGradientEditor::setGradient(KoStopGradient *gradient)
102 {
103     m_gradient = gradient;
104     setEnabled(m_gradient);
105 
106     if (m_gradient) {
107         gradientSlider->setGradientResource(m_gradient);
108         nameedit->setText(gradient->name());
109         stopChanged(gradientSlider->selectedStop());
110     }
111 
112     emit sigGradientChanged();
113 }
114 
notifyGlobalColorChanged(const KoColor & color)115 void KisStopGradientEditor::notifyGlobalColorChanged(const KoColor &color)
116 {
117     if (colorButton->isEnabled() &&
118         color != colorButton->color()) {
119 
120         colorButton->setColor(color);
121     }
122 }
123 
currentActiveStopColor() const124 boost::optional<KoColor> KisStopGradientEditor::currentActiveStopColor() const
125 {
126     if (!colorButton->isEnabled()) return boost::none;
127     return colorButton->color();
128 }
129 
stopChanged(int stop)130 void KisStopGradientEditor::stopChanged(int stop)
131 {
132     if (!m_gradient) return;
133 
134     const bool hasStopSelected = stop >= 0;
135 
136     opacitySlider->setEnabled(hasStopSelected);
137     colorButton->setEnabled(hasStopSelected);
138     stopLabel->setEnabled(hasStopSelected);
139     foregroundRadioButton->setEnabled(hasStopSelected);
140     backgroundRadioButton->setEnabled(hasStopSelected);
141     colorRadioButton->setEnabled(hasStopSelected);
142 
143     if (hasStopSelected) {
144         KoColor color;
145         KoGradientStopType type = m_gradient->stops()[stop].type;
146         if (type == FOREGROUNDSTOP) {
147             foregroundRadioButton->setChecked(true);
148             opacitySlider->setEnabled(false);
149             color = m_fgColor;
150         }
151         else if (type == BACKGROUNDSTOP) {
152             backgroundRadioButton->setChecked(true);
153             opacitySlider->setEnabled(false);
154             color = m_bgColor;
155         }
156         else {
157             colorRadioButton->setChecked(true);
158             opacitySlider->setEnabled(true);
159             color = m_gradient->stops()[stop].color;
160         }
161 
162         opacitySlider->setValue(color.opacityF());
163 
164         color.setOpacity(1.0);
165         colorButton->setColor(color);
166 
167     }
168 
169     emit sigGradientChanged();
170 }
171 
stopTypeChanged()172 void KisStopGradientEditor::stopTypeChanged() {
173     QList<KoGradientStop> stops = m_gradient->stops();
174     int currentStop = gradientSlider->selectedStop();
175     double t = stops[currentStop].position;
176     KoColor color = stops[currentStop].color;
177 
178     KoGradientStopType type;
179     if (foregroundRadioButton->isChecked()) {
180         type = FOREGROUNDSTOP;
181         color = KoColor(m_fgColor, color.colorSpace());
182         opacitySlider->setEnabled(false);
183     } else if (backgroundRadioButton->isChecked()) {
184         type = BACKGROUNDSTOP;
185         color = KoColor(m_bgColor, color.colorSpace());
186         opacitySlider->setEnabled(false);
187     }
188     else {
189         type = COLORSTOP;
190         opacitySlider->setEnabled(true);
191     }
192 
193     stops.removeAt(currentStop);
194     stops.insert(currentStop, KoGradientStop(t, color, type));
195     m_gradient->setStops(stops);
196     gradientSlider->update(); //setSelectedStopType(type);
197     emit sigGradientChanged();
198 }
199 
colorChanged(const KoColor & color)200 void KisStopGradientEditor::colorChanged(const KoColor& color)
201 {
202     if (!m_gradient) return;
203 
204     QList<KoGradientStop> stops = m_gradient->stops();
205 
206     int currentStop = gradientSlider->selectedStop();
207     double t = stops[currentStop].position;
208 
209     KoColor c(color, stops[currentStop].color.colorSpace());
210     c.setOpacity(stops[currentStop].color.opacityU8());
211 
212     KoGradientStopType type = stops[currentStop].type;
213 
214     stops.removeAt(currentStop);
215     stops.insert(currentStop, KoGradientStop(t, c, type));
216     m_gradient->setStops(stops);
217     gradientSlider->update();
218 
219     emit sigGradientChanged();
220 }
221 
opacityChanged(qreal value)222 void KisStopGradientEditor::opacityChanged(qreal value)
223 {
224     if (!m_gradient) return;
225 
226     QList<KoGradientStop> stops = m_gradient->stops();
227 
228     int currentStop = gradientSlider->selectedStop();
229     double t = stops[currentStop].position;
230 
231     KoColor c = stops[currentStop].color;
232     c.setOpacity(value);
233 
234     KoGradientStopType type = stops[currentStop].type;
235 
236     stops.removeAt(currentStop);
237     stops.insert(currentStop, KoGradientStop(t, c, type));
238     m_gradient->setStops(stops);
239     gradientSlider->update();
240 
241     emit sigGradientChanged();
242 }
243 
244 
nameChanged()245 void KisStopGradientEditor::nameChanged()
246 {
247     if (!m_gradient) return;
248 
249     m_gradient->setName(nameedit->text());
250     emit sigGradientChanged();
251 }
252 
reverse()253 void KisStopGradientEditor::reverse()
254 {
255     if (!m_gradient) return;
256 
257     QList<KoGradientStop> stops = m_gradient->stops();
258     QList<KoGradientStop> reversedStops;
259     for(const KoGradientStop& stop : stops) {
260         reversedStops.push_front(KoGradientStop(1 - stop.position, stop.color, stop.type));
261     }
262     m_gradient->setStops(reversedStops);
263     gradientSlider->setSelectedStop(stops.size() - 1 - gradientSlider->selectedStop());
264 
265     emit sigGradientChanged();
266 }
267 
sortByValue(SortFlags flags=SORT_ASCENDING)268 void KisStopGradientEditor::sortByValue( SortFlags flags = SORT_ASCENDING )
269 {
270     if (!m_gradient) return;
271 
272     bool ascending = (flags & SORT_ASCENDING) > 0;
273     bool evenDistribution = (flags & EVEN_DISTRIBUTION) > 0;
274 
275     QList<KoGradientStop> stops = m_gradient->stops();
276     const int stopCount = stops.size();
277 
278     QList<KoGradientStop> sortedStops;
279     std::sort(stops.begin(), stops.end(), KoGradientStopValueSort());
280 
281     int stopIndex = 0;
282     for (const KoGradientStop& stop : stops) {
283         const float value = evenDistribution ? (float)stopIndex / (float)(stopCount - 1) : stop.color.toQColor().valueF();
284         const float position = ascending ? value : 1.f - value;
285 
286         if (ascending) {
287             sortedStops.push_back(KoGradientStop(position, stop.color, stop.type));
288         } else {
289             sortedStops.push_front(KoGradientStop(position, stop.color, stop.type));
290         }
291 
292         stopIndex++;
293     }
294 
295     m_gradient->setStops(sortedStops);
296     gradientSlider->setSelectedStop(stopCount - 1);
297     gradientSlider->update();
298 
299     emit sigGradientChanged();
300 }
301 
showContextMenu(const QPoint & origin)302 void KisStopGradientEditor::showContextMenu(const QPoint &origin)
303 {
304     QMenu contextMenu(i18n("Options"), this);
305 
306     QAction reverseValues(i18n("Reverse Values"), this);
307     connect(&reverseValues, &QAction::triggered, this, &KisStopGradientEditor::reverse);
308 
309     QAction sortAscendingValues(i18n("Sort by Value"), this);
310     connect(&sortAscendingValues, &QAction::triggered, this, [this]{ this->sortByValue(SORT_ASCENDING); } );
311     QAction sortAscendingDistributed(i18n("Sort by Value (Even Distribution)"), this);
312     connect(&sortAscendingDistributed, &QAction::triggered, this, [this]{ this->sortByValue(SORT_ASCENDING | EVEN_DISTRIBUTION);} );
313 
314     contextMenu.addAction(&reverseValues);
315     contextMenu.addSeparator();
316     contextMenu.addAction(&sortAscendingValues);
317     contextMenu.addAction(&sortAscendingDistributed);
318 
319     contextMenu.exec(mapToGlobal(origin));
320 }
321 
322