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