1 /*
2 For general Scribus (>=1.3.2) copyright and licensing information please refer
3 to the COPYING file provided with the program. Following this notice may exist
4 a copyright and/or license notice that predates the release of Scribus 1.3.2
5 for which a new license (GPL+exception) is in place.
6 */
7 #include "curvewidget.h"
8
9 #include <cmath>
10
11 #include <QApplication>
12 #include <QCursor>
13 #include <QEvent>
14 #include <QHBoxLayout>
15 #include <QMessageBox>
16 #include <QPainter>
17 #include <QPixmap>
18 #include <QPushButton>
19 #include <QSpacerItem>
20 #include <QTextStream>
21 #include <QToolTip>
22 #include <QVBoxLayout>
23
24
25 #include "commonstrings.h"
26 #include "iconmanager.h"
27 #include "prefsfile.h"
28 #include "prefsmanager.h"
29 #include "scclocale.h"
30 #include "ui/customfdialog.h"
31 #include "ui/scmessagebox.h"
32 #include "util.h"
33 #include "util_color.h"
34
35
36
KCurve(QWidget * parent)37 KCurve::KCurve(QWidget *parent) : QWidget(parent),
38 m_leftmost(0.0),
39 m_rightmost(0.0),
40 m_pos(0),
41 m_dragging(false),
42 m_linear(false),
43 m_grabOffsetX(0.0),
44 m_grabOffsetY(0.0)
45 {
46 setMouseTracking(true);
47 setMinimumSize(150, 150);
48 m_points.resize(0);
49 m_points.addPoint(0.0, 0.0);
50 m_points.addPoint(1.0, 1.0);
51 setFocusPolicy(Qt::StrongFocus);
52 }
53
~KCurve()54 KCurve::~KCurve()
55 {
56 }
57
paintEvent(QPaintEvent *)58 void KCurve::paintEvent(QPaintEvent *)
59 {
60 int x = 0;
61 int wWidth = width() - 1;
62 int wHeight = height() - 1;
63 // Drawing selection or all histogram values.
64 QPainter p1;
65 p1.begin(this);
66 // draw background
67 p1.fillRect(QRect(0, 0, wWidth, wHeight), QColor(255, 255, 255));
68 // Draw grid separators.
69 p1.setPen(QPen(Qt::gray, 1, Qt::SolidLine));
70 p1.drawLine(wWidth/4, 0, wWidth/4, wHeight);
71 p1.drawLine(wWidth/2, 0, wWidth/2, wHeight);
72 p1.drawLine(3*wWidth/4, 0, 3*wWidth/4, wHeight);
73 p1.drawLine(0, wHeight/4, wWidth, wHeight/4);
74 p1.drawLine(0, wHeight/2, wWidth, wHeight/2);
75 p1.drawLine(0, 3*wHeight/4, wWidth, 3*wHeight/4);
76
77 // Draw curve.
78 double curvePrevVal = getCurveValue(0.0);
79 p1.setPen(QPen(Qt::black, 1, Qt::SolidLine));
80 for (x = 0 ; x < wWidth ; x++)
81 {
82 double curveX;
83 double curveVal;
84 // curveX = (x + 0.5) / wWidth;
85 curveX = x / static_cast<double>(wWidth);
86 curveVal = getCurveValue(curveX);
87 p1.drawLine(x - 1, wHeight - int(curvePrevVal * wHeight), x, wHeight - int(curveVal * wHeight));
88 curvePrevVal = curveVal;
89 }
90 p1.drawLine(x - 1, wHeight - int(curvePrevVal * wHeight), x, wHeight - int(getCurveValue(1.0) * wHeight));
91 for (int dh = 0; dh < m_points.size(); dh++)
92 {
93 FPoint p = m_points.point(dh);
94 if (p == m_grab_point)
95 {
96 p1.setPen(QPen(Qt::red, 3, Qt::SolidLine));
97 p1.drawEllipse( int(p.x() * wWidth) - 2, wHeight - 2 - int(p.y() * wHeight), 4, 4 );
98 }
99 else
100 {
101 p1.setPen(QPen(Qt::red, 1, Qt::SolidLine));
102 p1.drawEllipse( int(p.x() * wWidth) - 3, wHeight - 3 - int(p.y() * wHeight), 6, 6 );
103 }
104 }
105 p1.end();
106 }
107
keyPressEvent(QKeyEvent * e)108 void KCurve::keyPressEvent(QKeyEvent *e)
109 {
110 if (e->key() == Qt::Key_Delete || e->key() == Qt::Key_Backspace)
111 {
112 if (m_points.size() > 2)
113 {
114 FPoint closest_point = m_points.point(0);
115 FPoint p = m_points.point(0);
116 int pos = 0;
117 int cc = 0;
118 double distance = 1000; // just a big number
119 while (cc < m_points.size())
120 {
121 p = m_points.point(cc);
122 if (fabs (m_grab_point.x() - p.x()) < distance)
123 {
124 distance = fabs(m_grab_point.x() - p.x());
125 closest_point = p;
126 m_pos = pos;
127 }
128 cc++;
129 pos++;
130 }
131 FPointArray cli;
132 cli.putPoints(0, m_pos, m_points);
133 cli.putPoints(cli.size(), m_points.size()-m_pos-1, m_points, m_pos+1);
134 m_points.resize(0);
135 m_points = cli.copy();
136 m_grab_point = closest_point;
137 repaint();
138 emit modified();
139 QWidget::keyPressEvent(e);
140 }
141 }
142 }
143
mousePressEvent(QMouseEvent * e)144 void KCurve::mousePressEvent ( QMouseEvent * e )
145 {
146 FPoint closest_point = FPoint();
147 double distance;
148 if (e->button() != Qt::LeftButton)
149 return;
150 double x = e->pos().x() / (float)width();
151 double y = 1.0 - e->pos().y() / (float)height();
152 distance = 1000; // just a big number
153 FPoint p = m_points.point(0);
154 int insert_pos =0;
155 int pos = 0;
156 int cc = 0;
157 while (cc < m_points.size())
158 {
159 p = m_points.point(cc);
160 if (fabs (x - p.x()) < distance)
161 {
162 distance = fabs(x - p.x());
163 closest_point = p;
164 insert_pos = pos;
165 }
166 cc++;
167 pos++;
168 }
169 m_pos = insert_pos;
170 m_grab_point = closest_point;
171 m_grabOffsetX = m_grab_point.x() - x;
172 m_grabOffsetY = m_grab_point.y() - y;
173 m_grab_point = FPoint(x + m_grabOffsetX, y + m_grabOffsetY);
174 double curveVal = getCurveValue(x);
175 if (distance * width() > 5)
176 {
177 m_dragging = false;
178 if (fabs(y - curveVal) * width() > 5)
179 return;
180 if (m_points.size() < 14)
181 {
182 if (x > closest_point.x())
183 m_pos++;
184 FPointArray cli;
185 cli.putPoints(0, m_pos, m_points);
186 cli.resize(cli.size()+1);
187 cli.putPoints(cli.size()-1, 1, x, curveVal);
188 cli.putPoints(cli.size(), m_points.size()-m_pos, m_points, m_pos);
189 m_points.resize(0);
190 m_points = cli.copy();
191 m_dragging = true;
192 m_grab_point = m_points.point(m_pos);
193 m_grabOffsetX = m_grab_point.x() - x;
194 m_grabOffsetY = m_grab_point.y() - curveVal;
195 m_grab_point = FPoint(x + m_grabOffsetX, curveVal + m_grabOffsetY);
196 setCursor(QCursor(Qt::CrossCursor));
197 }
198 }
199 else
200 {
201 if (fabs(y - closest_point.y()) * width() > 5)
202 return;
203 m_dragging = true;
204 setCursor(QCursor(Qt::CrossCursor));
205 }
206 // Determine the leftmost and rightmost points.
207 m_leftmost = 0;
208 m_rightmost = 1;
209 cc = 0;
210 while (cc < m_points.size())
211 {
212 p = m_points.point(cc);
213 if (p != m_grab_point)
214 {
215 if (p.x() > m_leftmost && p.x() < x)
216 m_leftmost = p.x();
217 if (p.x() < m_rightmost && p.x() > x)
218 m_rightmost = p.x();
219 }
220 cc++;
221 }
222 repaint();
223 emit modified();
224 }
225
mouseReleaseEvent(QMouseEvent * e)226 void KCurve::mouseReleaseEvent ( QMouseEvent * e )
227 {
228 if (e->button() != Qt::LeftButton)
229 return;
230 setCursor(QCursor(Qt::ArrowCursor));
231 m_dragging = false;
232 repaint();
233 emit modified();
234 }
235
mouseMoveEvent(QMouseEvent * e)236 void KCurve::mouseMoveEvent ( QMouseEvent * e )
237 {
238 double x = e->pos().x() / (float)width();
239 double y = 1.0 - e->pos().y() / (float)height();
240
241 if (!m_dragging) // If no point is selected set the the cursor shape if on top
242 {
243 double distance = 1000;
244 double ydistance = 1000;
245 int cc = 0;
246 while (cc < m_points.size())
247 {
248 FPoint p = m_points.point(cc);
249 if (fabs (x - p.x()) < distance)
250 {
251 distance = fabs(x - p.x());
252 ydistance = fabs(y - p.y());
253 }
254 cc++;
255 }
256 if (distance * width() > 5 || ydistance * height() > 5)
257 setCursor(QCursor(Qt::ArrowCursor));
258 else
259 setCursor(QCursor(Qt::CrossCursor));
260 }
261 else // Else, drag the selected point
262 {
263 setCursor(QCursor(Qt::CrossCursor));
264 x += m_grabOffsetX;
265 y += m_grabOffsetY;
266 if (x <= m_leftmost)
267 x = m_leftmost + 1E-4; // the addition so we can grab the dot later.
268 if (x >= m_rightmost)
269 x = m_rightmost - 1E-4;
270 if (y > 1.0)
271 y = 1.0;
272 if (y < 0.0)
273 y = 0.0;
274 m_grab_point = FPoint(x, y);
275 m_points.setPoint( m_pos, m_grab_point);
276 repaint();
277 emit modified();
278 }
279 }
280
getCurveValue(double x)281 double KCurve::getCurveValue(double x)
282 {
283 return getCurveYValue(m_points, x, m_linear);
284 }
285
getCurve()286 FPointArray KCurve::getCurve()
287 {
288 return m_points.copy();
289 }
290
setCurve(const FPointArray & inlist)291 void KCurve::setCurve(const FPointArray& inlist)
292 {
293 m_points_back = m_points.copy();
294 m_points.resize(0);
295 m_points = inlist.copy();
296 repaint();
297 emit modified();
298 }
299
resetCurve()300 void KCurve::resetCurve()
301 {
302 m_points.resize(0);
303 m_points = m_points_back.copy();
304 repaint();
305 emit modified();
306 }
307
setLinear(bool setter)308 void KCurve::setLinear(bool setter)
309 {
310 m_linear = setter;
311 repaint();
312 emit modified();
313 }
314
isLinear()315 bool KCurve::isLinear()
316 {
317 return m_linear;
318 }
319
CurveWidget(QWidget * parent)320 CurveWidget::CurveWidget( QWidget* parent ) : QWidget( parent )
321 {
322 CurveWidgetLayout = new QHBoxLayout(this);
323 CurveWidgetLayout->setContentsMargins(0, 0, 0, 0);
324 CurveWidgetLayout->setSpacing(6);
325
326 layout1 = new QVBoxLayout;
327 layout1->setContentsMargins(0, 0, 0, 0);
328 layout1->setSpacing(6);
329
330 invertButton = new QPushButton( this );
331 invertButton->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed));
332 invertButton->setIcon( IconManager::instance().loadIcon("invert.png") );
333 invertButton->setIconSize(QSize(22, 22));
334 layout1->addWidget( invertButton );
335
336 resetButton = new QPushButton( this );
337 resetButton->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed));
338 resetButton->setIcon( IconManager::instance().loadIcon("reload.png") );
339 resetButton->setIconSize(QSize(22, 22));
340 layout1->addWidget( resetButton );
341 linearButton = new QPushButton( this );
342 QIcon ic;
343 ic.addPixmap(IconManager::instance().loadPixmap("curvebezier.png"), QIcon::Normal, QIcon::Off);
344 ic.addPixmap(IconManager::instance().loadPixmap("curvelinear.png"), QIcon::Normal, QIcon::On);
345 linearButton->setIcon(ic);
346 linearButton->setCheckable( true );
347 linearButton->setChecked(false);
348 linearButton->setIconSize(QSize(22, 22));
349 linearButton->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed));
350 layout1->addWidget( linearButton );
351 spacer1 = new QSpacerItem( 2, 3, QSizePolicy::Minimum, QSizePolicy::Expanding );
352 layout1->addItem( spacer1 );
353
354 loadButton = new QPushButton( this );
355 loadButton->setIcon( IconManager::instance().loadIcon("22/document-open.png") );
356 loadButton->setIconSize(QSize(22, 22));
357 loadButton->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed));
358 layout1->addWidget( loadButton );
359
360 saveButton = new QPushButton( this );
361 saveButton->setIcon( IconManager::instance().loadIcon("22/document-save-as.png") );
362 saveButton->setIconSize(QSize(22, 22));
363 saveButton->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed));
364 layout1->addWidget( saveButton );
365 CurveWidgetLayout->addLayout( layout1 );
366
367 cDisplay = new KCurve(this);
368 cDisplay->setMinimumSize( QSize( 150, 150 ) );
369 CurveWidgetLayout->addWidget( cDisplay );
370 languageChange();
371
372 connect(invertButton, SIGNAL(clicked()), this, SLOT(doInvert()));
373 connect(resetButton, SIGNAL(clicked()), this, SLOT(doReset()));
374 connect(linearButton, SIGNAL(clicked()), this, SLOT(doLinear()));
375 connect(loadButton, SIGNAL(clicked()), this, SLOT(doLoad()));
376 connect(saveButton, SIGNAL(clicked()), this, SLOT(doSave()));
377 }
378
doInvert()379 void CurveWidget::doInvert()
380 {
381 FPointArray curve = cDisplay->getCurve();
382 for (int a = 0; a < curve.size(); a++)
383 {
384 FPoint p = curve.point(a);
385 curve.setPoint(a, p.x(), 1.0 - p.y());
386 }
387 cDisplay->setCurve(curve);
388 }
389
doReset()390 void CurveWidget::doReset()
391 {
392 cDisplay->resetCurve();
393 }
394
doLinear()395 void CurveWidget::doLinear()
396 {
397 cDisplay->setLinear(linearButton->isChecked());
398 }
399
setLinear(bool setter)400 void CurveWidget::setLinear(bool setter)
401 {
402 cDisplay->setLinear(setter);
403 linearButton->setChecked(setter);
404 }
405
doLoad()406 void CurveWidget::doLoad()
407 {
408 QString fileName;
409 PrefsContext* dirs = PrefsManager::instance().prefsFile->getContext("dirs");
410 QString wdir = dirs->get("curves", ".");
411 CustomFDialog dia(this, wdir, tr("Open"), tr("Curve Files (*.scu *.SCU);;All Files (*)"), fdHidePreviewCheckBox | fdExistingFiles);
412 if (dia.exec() == QDialog::Accepted)
413 fileName = dia.selectedFile();
414 else
415 return;
416 if (!fileName.isEmpty())
417 {
418 dirs->set("curves", fileName.left(fileName.lastIndexOf("/")));
419 QFile f(fileName);
420 if (f.open(QIODevice::ReadOnly))
421 {
422 QTextStream fp(&f);
423 int numVals;
424 double xval, yval;
425 FPointArray curve;
426 curve.resize(0);
427 fp >> numVals;
428 for (int nv = 0; nv < numVals; nv++)
429 {
430 QString s;
431 fp >> s;
432 xval = ScCLocale::toDoubleC(s);
433 fp >> s;
434 yval = ScCLocale::toDoubleC(s);
435 curve.addPoint(xval, yval);
436 }
437 cDisplay->setCurve(curve);
438 int lin;
439 fp >> lin;
440 cDisplay->setLinear(lin);
441 }
442 }
443 }
444
doSave()445 void CurveWidget::doSave()
446 {
447 QString fileName;
448 QString wdir = PrefsManager::instance().prefsFile->getContext("dirs")->get("curves", ".");
449 CustomFDialog dia(this, wdir, tr("Save as"), tr("Curve Files (*.scu *.SCU);;All Files (*)"), fdHidePreviewCheckBox | fdNone);
450 if (dia.exec() == QDialog::Accepted)
451 fileName = dia.selectedFile();
452 else
453 return;
454 if (!fileName.isEmpty())
455 {
456 if (!fileName.endsWith(".scu"))
457 fileName += ".scu";
458 PrefsManager::instance().prefsFile->getContext("dirs")->set("curves", fileName.left(fileName.lastIndexOf("/")));
459 if (overwrite(this, fileName))
460 {
461 QString efval = "";
462 FPointArray Vals = cDisplay->getCurve();
463 QString tmp;
464 tmp.setNum(Vals.size());
465 efval += tmp;
466 for (int p = 0; p < Vals.size(); p++)
467 {
468 const FPoint& pv = Vals.point(p);
469 efval += QString(" %1 %2").arg(pv.x()).arg(pv.y());
470 }
471 if (cDisplay->isLinear())
472 efval += " 1";
473 else
474 efval += " 0";
475 QFile fx(fileName);
476 if (fx.open(QIODevice::WriteOnly))
477 {
478 QTextStream tsx(&fx);
479 tsx << efval;
480 fx.close();
481 }
482 else
483 ScMessageBox::warning(this, CommonStrings::trWarning, tr("Cannot write the file: \n%1").arg(fileName));
484 }
485 }
486 }
487
changeEvent(QEvent * e)488 void CurveWidget::changeEvent(QEvent *e)
489 {
490 if (e->type() == QEvent::LanguageChange)
491 {
492 languageChange();
493 }
494 else
495 QWidget::changeEvent(e);
496 }
497
languageChange()498 void CurveWidget::languageChange()
499 {
500 invertButton->setText( QString() );
501 resetButton->setText( QString() );
502 loadButton->setText( QString() );
503 saveButton->setText( QString() );
504 invertButton->setToolTip("");
505 resetButton->setToolTip("");
506 linearButton->setToolTip("");
507 loadButton->setToolTip("");
508 saveButton->setToolTip("");
509 invertButton->setToolTip( tr( "Inverts the curve" ) );
510 resetButton->setToolTip( tr( "Resets the curve" ) );
511 linearButton->setToolTip( tr( "Switches between linear and cubic interpolation of the curve" ) );
512 loadButton->setToolTip( tr( "Loads a curve" ) );
513 saveButton->setToolTip( tr( "Saves this curve" ) );
514 }
515
516
517