1 /****************************************************************************
2 **
3 ** Copyright (C) 2015 The Qt Company Ltd.
4 ** Contact: http://www.qt.io/licensing/
5 **
6 ** This file is part of the tools applications of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see http://www.qt.io/terms-conditions. For further
15 ** information use the contact form at http://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 or version 3 as published by the Free
20 ** Software Foundation and appearing in the file LICENSE.LGPLv21 and
21 ** LICENSE.LGPLv3 included in the packaging of this file. Please review the
22 ** following information to ensure the GNU Lesser General Public License
23 ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
24 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 **
26 ** As a special exception, The Qt Company gives you certain additional
27 ** rights. These rights are described in The Qt Company LGPL Exception
28 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 **
30 ** GNU General Public License Usage
31 ** Alternatively, this file may be used under the terms of the GNU
32 ** General Public License version 3.0 as published by the Free Software
33 ** Foundation and appearing in the file LICENSE.GPL included in the
34 ** packaging of this file. Please review the following information to
35 ** ensure the GNU General Public License version 3.0 requirements will be
36 ** met: http://www.gnu.org/copyleft/gpl.html.
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qtgradientstopswidget.h"
43 #include "qtgradientstopsmodel.h"
44
45 #include <QtCore/QMap>
46 #include <QtGui/QImage>
47 #include <QtGui/QPainter>
48 #include <QtGui/QScrollBar>
49 #include <QtGui/QMouseEvent>
50 #include <QtGui/QRubberBand>
51 #include <QtGui/QMenu>
52
53 QT_BEGIN_NAMESPACE
54
55 class QtGradientStopsWidgetPrivate
56 {
57 QtGradientStopsWidget *q_ptr;
58 Q_DECLARE_PUBLIC(QtGradientStopsWidget)
59 public:
60 typedef QMap<qreal, QColor> PositionColorMap;
61 typedef QMap<QtGradientStop *, qreal> StopPositionMap;
62
63 void slotStopAdded(QtGradientStop *stop);
64 void slotStopRemoved(QtGradientStop *stop);
65 void slotStopMoved(QtGradientStop *stop, qreal newPos);
66 void slotStopsSwapped(QtGradientStop *stop1, QtGradientStop *stop2);
67 void slotStopChanged(QtGradientStop *stop, const QColor &newColor);
68 void slotStopSelected(QtGradientStop *stop, bool selected);
69 void slotCurrentStopChanged(QtGradientStop *stop);
70 void slotNewStop();
71 void slotDelete();
72 void slotFlipAll();
73 void slotSelectAll();
74 void slotZoomIn();
75 void slotZoomOut();
76 void slotResetZoom();
77
78 double fromViewport(int x) const;
79 double toViewport(double x) const;
80 QtGradientStop *stopAt(const QPoint &viewportPos) const;
81 QList<QtGradientStop *> stopsAt(const QPoint &viewportPos) const;
82 void setupMove(QtGradientStop *stop, int x);
83 void ensureVisible(double x); // x = stop position
84 void ensureVisible(QtGradientStop *stop);
85 QtGradientStop *newStop(const QPoint &viewportPos);
86
87 bool m_backgroundCheckered;
88 QtGradientStopsModel *m_model;
89 double m_handleSize;
90 int m_scaleFactor;
91 double m_zoom;
92
93 #ifndef QT_NO_DRAGANDDROP
94 QtGradientStop *m_dragStop;
95 QtGradientStop *m_changedStop;
96 QtGradientStop *m_clonedStop;
97 QtGradientStopsModel *m_dragModel;
98 QColor m_dragColor;
99 void clearDrag();
100 void removeClonedStop();
101 void restoreChangedStop();
102 void changeStop(qreal pos);
103 void cloneStop(qreal pos);
104 #endif
105
106 QRubberBand *m_rubber;
107 QPoint m_clickPos;
108
109 QList<QtGradientStop *> m_stops;
110
111 bool m_moving;
112 int m_moveOffset;
113 StopPositionMap m_moveStops;
114
115 PositionColorMap m_moveOriginal;
116 };
117
fromViewport(int x) const118 double QtGradientStopsWidgetPrivate::fromViewport(int x) const
119 {
120 QSize size = q_ptr->viewport()->size();
121 int w = size.width();
122 int max = q_ptr->horizontalScrollBar()->maximum();
123 int val = q_ptr->horizontalScrollBar()->value();
124 return ((double)x * m_scaleFactor + w * val) / (w * (m_scaleFactor + max));
125 }
126
toViewport(double x) const127 double QtGradientStopsWidgetPrivate::toViewport(double x) const
128 {
129 QSize size = q_ptr->viewport()->size();
130 int w = size.width();
131 int max = q_ptr->horizontalScrollBar()->maximum();
132 int val = q_ptr->horizontalScrollBar()->value();
133 return w * (x * (m_scaleFactor + max) - val) / m_scaleFactor;
134 }
135
stopAt(const QPoint & viewportPos) const136 QtGradientStop *QtGradientStopsWidgetPrivate::stopAt(const QPoint &viewportPos) const
137 {
138 double posY = m_handleSize / 2;
139 QListIterator<QtGradientStop *> itStop(m_stops);
140 while (itStop.hasNext()) {
141 QtGradientStop *stop = itStop.next();
142
143 double posX = toViewport(stop->position());
144
145 double x = viewportPos.x() - posX;
146 double y = viewportPos.y() - posY;
147
148 if ((m_handleSize * m_handleSize / 4) > (x * x + y * y))
149 return stop;
150 }
151 return 0;
152 }
153
stopsAt(const QPoint & viewportPos) const154 QList<QtGradientStop *> QtGradientStopsWidgetPrivate::stopsAt(const QPoint &viewportPos) const
155 {
156 QList<QtGradientStop *> stops;
157 double posY = m_handleSize / 2;
158 QListIterator<QtGradientStop *> itStop(m_stops);
159 while (itStop.hasNext()) {
160 QtGradientStop *stop = itStop.next();
161
162 double posX = toViewport(stop->position());
163
164 double x = viewportPos.x() - posX;
165 double y = viewportPos.y() - posY;
166
167 if ((m_handleSize * m_handleSize / 4) > (x * x + y * y))
168 stops.append(stop);
169 }
170 return stops;
171 }
172
setupMove(QtGradientStop * stop,int x)173 void QtGradientStopsWidgetPrivate::setupMove(QtGradientStop *stop, int x)
174 {
175 m_model->setCurrentStop(stop);
176
177 int viewportX = qRound(toViewport(stop->position()));
178 m_moveOffset = x - viewportX;
179
180 QList<QtGradientStop *> stops = m_stops;
181 m_stops.clear();
182 QListIterator<QtGradientStop *> itStop(stops);
183 while (itStop.hasNext()) {
184 QtGradientStop *s = itStop.next();
185 if (m_model->isSelected(s) || s == stop) {
186 m_moveStops[s] = s->position() - stop->position();
187 m_stops.append(s);
188 } else {
189 m_moveOriginal[s->position()] = s->color();
190 }
191 }
192 itStop.toFront();
193 while (itStop.hasNext()) {
194 QtGradientStop *s = itStop.next();
195 if (!m_model->isSelected(s))
196 m_stops.append(s);
197 }
198 m_stops.removeAll(stop);
199 m_stops.prepend(stop);
200 }
201
ensureVisible(double x)202 void QtGradientStopsWidgetPrivate::ensureVisible(double x)
203 {
204 double viewX = toViewport(x);
205 if (viewX < 0 || viewX > q_ptr->viewport()->size().width()) {
206 int max = q_ptr->horizontalScrollBar()->maximum();
207 int newVal = qRound(x * (max + m_scaleFactor) - m_scaleFactor / 2);
208 q_ptr->horizontalScrollBar()->setValue(newVal);
209 }
210 }
211
ensureVisible(QtGradientStop * stop)212 void QtGradientStopsWidgetPrivate::ensureVisible(QtGradientStop *stop)
213 {
214 if (!stop)
215 return;
216 ensureVisible(stop->position());
217 }
218
newStop(const QPoint & viewportPos)219 QtGradientStop *QtGradientStopsWidgetPrivate::newStop(const QPoint &viewportPos)
220 {
221 QtGradientStop *copyStop = stopAt(viewportPos);
222 double posX = fromViewport(viewportPos.x());
223 QtGradientStop *stop = m_model->at(posX);
224 if (!stop) {
225 QColor newColor;
226 if (copyStop)
227 newColor = copyStop->color();
228 else
229 newColor = m_model->color(posX);
230 if (!newColor.isValid())
231 newColor = Qt::white;
232 stop = m_model->addStop(posX, newColor);
233 }
234 return stop;
235 }
236
slotStopAdded(QtGradientStop * stop)237 void QtGradientStopsWidgetPrivate::slotStopAdded(QtGradientStop *stop)
238 {
239 m_stops.append(stop);
240 q_ptr->viewport()->update();
241 }
242
slotStopRemoved(QtGradientStop * stop)243 void QtGradientStopsWidgetPrivate::slotStopRemoved(QtGradientStop *stop)
244 {
245 m_stops.removeAll(stop);
246 q_ptr->viewport()->update();
247 }
248
slotStopMoved(QtGradientStop * stop,qreal newPos)249 void QtGradientStopsWidgetPrivate::slotStopMoved(QtGradientStop *stop, qreal newPos)
250 {
251 Q_UNUSED(stop)
252 Q_UNUSED(newPos)
253 q_ptr->viewport()->update();
254 }
255
slotStopsSwapped(QtGradientStop * stop1,QtGradientStop * stop2)256 void QtGradientStopsWidgetPrivate::slotStopsSwapped(QtGradientStop *stop1, QtGradientStop *stop2)
257 {
258 Q_UNUSED(stop1)
259 Q_UNUSED(stop2)
260 q_ptr->viewport()->update();
261 }
262
slotStopChanged(QtGradientStop * stop,const QColor & newColor)263 void QtGradientStopsWidgetPrivate::slotStopChanged(QtGradientStop *stop, const QColor &newColor)
264 {
265 Q_UNUSED(stop)
266 Q_UNUSED(newColor)
267 q_ptr->viewport()->update();
268 }
269
slotStopSelected(QtGradientStop * stop,bool selected)270 void QtGradientStopsWidgetPrivate::slotStopSelected(QtGradientStop *stop, bool selected)
271 {
272 Q_UNUSED(stop)
273 Q_UNUSED(selected)
274 q_ptr->viewport()->update();
275 }
276
slotCurrentStopChanged(QtGradientStop * stop)277 void QtGradientStopsWidgetPrivate::slotCurrentStopChanged(QtGradientStop *stop)
278 {
279 Q_UNUSED(stop)
280
281 if (!m_model)
282 return;
283 q_ptr->viewport()->update();
284 if (stop) {
285 m_stops.removeAll(stop);
286 m_stops.prepend(stop);
287 }
288 }
289
slotNewStop()290 void QtGradientStopsWidgetPrivate::slotNewStop()
291 {
292 if (!m_model)
293 return;
294
295 QtGradientStop *stop = newStop(m_clickPos);
296
297 if (!stop)
298 return;
299
300 m_model->clearSelection();
301 m_model->selectStop(stop, true);
302 m_model->setCurrentStop(stop);
303 }
304
slotDelete()305 void QtGradientStopsWidgetPrivate::slotDelete()
306 {
307 if (!m_model)
308 return;
309
310 m_model->deleteStops();
311 }
312
slotFlipAll()313 void QtGradientStopsWidgetPrivate::slotFlipAll()
314 {
315 if (!m_model)
316 return;
317
318 m_model->flipAll();
319 }
320
slotSelectAll()321 void QtGradientStopsWidgetPrivate::slotSelectAll()
322 {
323 if (!m_model)
324 return;
325
326 m_model->selectAll();
327 }
328
slotZoomIn()329 void QtGradientStopsWidgetPrivate::slotZoomIn()
330 {
331 double newZoom = q_ptr->zoom() * 2;
332 if (newZoom > 100)
333 newZoom = 100;
334 if (newZoom == q_ptr->zoom())
335 return;
336
337 q_ptr->setZoom(newZoom);
338 emit q_ptr->zoomChanged(q_ptr->zoom());
339 }
340
slotZoomOut()341 void QtGradientStopsWidgetPrivate::slotZoomOut()
342 {
343 double newZoom = q_ptr->zoom() / 2;
344 if (newZoom < 1)
345 newZoom = 1;
346 if (newZoom == q_ptr->zoom())
347 return;
348
349 q_ptr->setZoom(newZoom);
350 emit q_ptr->zoomChanged(q_ptr->zoom());
351 }
352
slotResetZoom()353 void QtGradientStopsWidgetPrivate::slotResetZoom()
354 {
355 if (1 == q_ptr->zoom())
356 return;
357
358 q_ptr->setZoom(1);
359 emit q_ptr->zoomChanged(1);
360 }
361
QtGradientStopsWidget(QWidget * parent)362 QtGradientStopsWidget::QtGradientStopsWidget(QWidget *parent)
363 : QAbstractScrollArea(parent), d_ptr(new QtGradientStopsWidgetPrivate)
364 {
365 d_ptr->q_ptr = this;
366 d_ptr->m_backgroundCheckered = true;
367 d_ptr->m_model = 0;
368 d_ptr->m_handleSize = 25.0;
369 d_ptr->m_scaleFactor = 1000;
370 d_ptr->m_moving = false;
371 d_ptr->m_zoom = 1;
372 d_ptr->m_rubber = new QRubberBand(QRubberBand::Rectangle, this);
373 #ifndef QT_NO_DRAGANDDROP
374 d_ptr->m_dragStop = 0;
375 d_ptr->m_changedStop = 0;
376 d_ptr->m_clonedStop = 0;
377 d_ptr->m_dragModel = 0;
378 #endif
379 setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
380 setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
381 horizontalScrollBar()->setRange(0, (int)(d_ptr->m_scaleFactor * (d_ptr->m_zoom - 1) + 0.5));
382 horizontalScrollBar()->setPageStep(d_ptr->m_scaleFactor);
383 horizontalScrollBar()->setSingleStep(4);
384 viewport()->setAutoFillBackground(false);
385
386 setAcceptDrops(true);
387
388 setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred));
389 }
390
~QtGradientStopsWidget()391 QtGradientStopsWidget::~QtGradientStopsWidget()
392 {
393 }
394
sizeHint() const395 QSize QtGradientStopsWidget::sizeHint() const
396 {
397 return QSize(qRound(2 * d_ptr->m_handleSize), qRound(3 * d_ptr->m_handleSize) + horizontalScrollBar()->sizeHint().height());
398 }
399
minimumSizeHint() const400 QSize QtGradientStopsWidget::minimumSizeHint() const
401 {
402 return QSize(qRound(2 * d_ptr->m_handleSize), qRound(3 * d_ptr->m_handleSize) + horizontalScrollBar()->minimumSizeHint().height());
403 }
404
setBackgroundCheckered(bool checkered)405 void QtGradientStopsWidget::setBackgroundCheckered(bool checkered)
406 {
407 if (d_ptr->m_backgroundCheckered == checkered)
408 return;
409 d_ptr->m_backgroundCheckered = checkered;
410 update();
411 }
412
isBackgroundCheckered() const413 bool QtGradientStopsWidget::isBackgroundCheckered() const
414 {
415 return d_ptr->m_backgroundCheckered;
416 }
417
setGradientStopsModel(QtGradientStopsModel * model)418 void QtGradientStopsWidget::setGradientStopsModel(QtGradientStopsModel *model)
419 {
420 if (d_ptr->m_model == model)
421 return;
422
423 if (d_ptr->m_model) {
424 disconnect(d_ptr->m_model, SIGNAL(stopAdded(QtGradientStop*)),
425 this, SLOT(slotStopAdded(QtGradientStop*)));
426 disconnect(d_ptr->m_model, SIGNAL(stopRemoved(QtGradientStop*)),
427 this, SLOT(slotStopRemoved(QtGradientStop*)));
428 disconnect(d_ptr->m_model, SIGNAL(stopMoved(QtGradientStop*,qreal)),
429 this, SLOT(slotStopMoved(QtGradientStop*,qreal)));
430 disconnect(d_ptr->m_model, SIGNAL(stopsSwapped(QtGradientStop*,QtGradientStop*)),
431 this, SLOT(slotStopsSwapped(QtGradientStop*,QtGradientStop*)));
432 disconnect(d_ptr->m_model, SIGNAL(stopChanged(QtGradientStop*,QColor)),
433 this, SLOT(slotStopChanged(QtGradientStop*,QColor)));
434 disconnect(d_ptr->m_model, SIGNAL(stopSelected(QtGradientStop*,bool)),
435 this, SLOT(slotStopSelected(QtGradientStop*,bool)));
436 disconnect(d_ptr->m_model, SIGNAL(currentStopChanged(QtGradientStop*)),
437 this, SLOT(slotCurrentStopChanged(QtGradientStop*)));
438
439 d_ptr->m_stops.clear();
440 }
441
442 d_ptr->m_model = model;
443
444 if (d_ptr->m_model) {
445 connect(d_ptr->m_model, SIGNAL(stopAdded(QtGradientStop*)),
446 this, SLOT(slotStopAdded(QtGradientStop*)));
447 connect(d_ptr->m_model, SIGNAL(stopRemoved(QtGradientStop*)),
448 this, SLOT(slotStopRemoved(QtGradientStop*)));
449 connect(d_ptr->m_model, SIGNAL(stopMoved(QtGradientStop*,qreal)),
450 this, SLOT(slotStopMoved(QtGradientStop*,qreal)));
451 connect(d_ptr->m_model, SIGNAL(stopsSwapped(QtGradientStop*,QtGradientStop*)),
452 this, SLOT(slotStopsSwapped(QtGradientStop*,QtGradientStop*)));
453 connect(d_ptr->m_model, SIGNAL(stopChanged(QtGradientStop*,QColor)),
454 this, SLOT(slotStopChanged(QtGradientStop*,QColor)));
455 connect(d_ptr->m_model, SIGNAL(stopSelected(QtGradientStop*,bool)),
456 this, SLOT(slotStopSelected(QtGradientStop*,bool)));
457 connect(d_ptr->m_model, SIGNAL(currentStopChanged(QtGradientStop*)),
458 this, SLOT(slotCurrentStopChanged(QtGradientStop*)));
459
460 QList<QtGradientStop *> stops = d_ptr->m_model->stops().values();
461 QListIterator<QtGradientStop *> itStop(stops);
462 while (itStop.hasNext())
463 d_ptr->slotStopAdded(itStop.next());
464
465 QList<QtGradientStop *> selected = d_ptr->m_model->selectedStops();
466 QListIterator<QtGradientStop *> itSelect(selected);
467 while (itSelect.hasNext())
468 d_ptr->slotStopSelected(itSelect.next(), true);
469
470 d_ptr->slotCurrentStopChanged(d_ptr->m_model->currentStop());
471 }
472 }
473
mousePressEvent(QMouseEvent * e)474 void QtGradientStopsWidget::mousePressEvent(QMouseEvent *e)
475 {
476 typedef QtGradientStopsModel::PositionStopMap PositionStopMap;
477 if (!d_ptr->m_model)
478 return;
479
480 if (e->button() != Qt::LeftButton)
481 return;
482
483 d_ptr->m_moving = true;
484
485 d_ptr->m_moveStops.clear();
486 d_ptr->m_moveOriginal.clear();
487 d_ptr->m_clickPos = e->pos();
488 QtGradientStop *stop = d_ptr->stopAt(e->pos());
489 if (stop) {
490 if (e->modifiers() & Qt::ControlModifier) {
491 d_ptr->m_model->selectStop(stop, !d_ptr->m_model->isSelected(stop));
492 } else if (e->modifiers() & Qt::ShiftModifier) {
493 QtGradientStop *oldCurrent = d_ptr->m_model->currentStop();
494 if (oldCurrent) {
495 PositionStopMap stops = d_ptr->m_model->stops();
496 PositionStopMap::ConstIterator itSt = stops.constFind(oldCurrent->position());
497 if (itSt != stops.constEnd()) {
498 while (itSt != stops.constFind(stop->position())) {
499 d_ptr->m_model->selectStop(itSt.value(), true);
500 if (oldCurrent->position() < stop->position())
501 ++itSt;
502 else
503 --itSt;
504 }
505 }
506 }
507 d_ptr->m_model->selectStop(stop, true);
508 } else {
509 if (!d_ptr->m_model->isSelected(stop)) {
510 d_ptr->m_model->clearSelection();
511 d_ptr->m_model->selectStop(stop, true);
512 }
513 }
514 d_ptr->setupMove(stop, e->pos().x());
515 } else {
516 d_ptr->m_model->clearSelection();
517 d_ptr->m_rubber->setGeometry(QRect(d_ptr->m_clickPos, QSize()));
518 d_ptr->m_rubber->show();
519 }
520 viewport()->update();
521 }
522
mouseReleaseEvent(QMouseEvent * e)523 void QtGradientStopsWidget::mouseReleaseEvent(QMouseEvent *e)
524 {
525 if (!d_ptr->m_model)
526 return;
527
528 if (e->button() != Qt::LeftButton)
529 return;
530
531 d_ptr->m_moving = false;
532 d_ptr->m_rubber->hide();
533 d_ptr->m_moveStops.clear();
534 d_ptr->m_moveOriginal.clear();
535 }
536
mouseMoveEvent(QMouseEvent * e)537 void QtGradientStopsWidget::mouseMoveEvent(QMouseEvent *e)
538 {
539 typedef QtGradientStopsWidgetPrivate::PositionColorMap PositionColorMap;
540 typedef QtGradientStopsModel::PositionStopMap PositionStopMap;
541 typedef QtGradientStopsWidgetPrivate::StopPositionMap StopPositionMap;
542 if (!d_ptr->m_model)
543 return;
544
545 if (!(e->buttons() & Qt::LeftButton))
546 return;
547
548 if (!d_ptr->m_moving)
549 return;
550
551 if (!d_ptr->m_moveStops.isEmpty()) {
552 double maxOffset = 0.0;
553 double minOffset = 0.0;
554 bool first = true;
555 StopPositionMap::ConstIterator itStop = d_ptr->m_moveStops.constBegin();
556 while (itStop != d_ptr->m_moveStops.constEnd()) {
557 double offset = itStop.value();
558
559 if (first) {
560 maxOffset = offset;
561 minOffset = offset;
562 first = false;
563 } else {
564 if (maxOffset < offset)
565 maxOffset = offset;
566 else if (minOffset > offset)
567 minOffset = offset;
568 }
569 ++itStop;
570 }
571
572 double viewportMin = d_ptr->toViewport(-minOffset);
573 double viewportMax = d_ptr->toViewport(1.0 - maxOffset);
574
575 PositionStopMap newPositions;
576
577 int viewportX = e->pos().x() - d_ptr->m_moveOffset;
578
579 if (viewportX > viewport()->size().width())
580 viewportX = viewport()->size().width();
581 else if (viewportX < 0)
582 viewportX = 0;
583
584 double posX = d_ptr->fromViewport(viewportX);
585
586 if (viewportX > viewportMax)
587 posX = 1.0 - maxOffset;
588 else if (viewportX < viewportMin)
589 posX = -minOffset;
590
591 itStop = d_ptr->m_moveStops.constBegin();
592 while (itStop != d_ptr->m_moveStops.constEnd()) {
593 QtGradientStop *stop = itStop.key();
594
595 newPositions[posX + itStop.value()] = stop;
596
597 ++itStop;
598 }
599
600 bool forward = true;
601 PositionStopMap::ConstIterator itNewPos = newPositions.constBegin();
602 if (itNewPos.value()->position() < itNewPos.key())
603 forward = false;
604
605 itNewPos = forward ? newPositions.constBegin() : newPositions.constEnd();
606 while (itNewPos != (forward ? newPositions.constEnd() : newPositions.constBegin())) {
607 if (!forward)
608 --itNewPos;
609 QtGradientStop *stop = itNewPos.value();
610 double newPos = itNewPos.key();
611 if (newPos > 1)
612 newPos = 1;
613 else if (newPos < 0)
614 newPos = 0;
615
616 QtGradientStop *existingStop = d_ptr->m_model->at(newPos);
617 if (existingStop && !d_ptr->m_moveStops.contains(existingStop))
618 d_ptr->m_model->removeStop(existingStop);
619 d_ptr->m_model->moveStop(stop, newPos);
620
621 if (forward)
622 ++itNewPos;
623 }
624
625 PositionColorMap::ConstIterator itOld = d_ptr->m_moveOriginal.constBegin();
626 while (itOld != d_ptr->m_moveOriginal.constEnd()) {
627 double position = itOld.key();
628 if (!d_ptr->m_model->at(position))
629 d_ptr->m_model->addStop(position, itOld.value());
630
631 ++itOld;
632 }
633
634 } else {
635 QRect r(QRect(d_ptr->m_clickPos, e->pos()).normalized());
636 r.translate(1, 0);
637 d_ptr->m_rubber->setGeometry(r);
638 //d_ptr->m_model->clearSelection();
639
640 int xv1 = d_ptr->m_clickPos.x();
641 int xv2 = e->pos().x();
642 if (xv1 > xv2) {
643 int temp = xv1;
644 xv1 = xv2;
645 xv2 = temp;
646 }
647 int yv1 = d_ptr->m_clickPos.y();
648 int yv2 = e->pos().y();
649 if (yv1 > yv2) {
650 int temp = yv1;
651 yv1 = yv2;
652 yv2 = temp;
653 }
654
655 QPoint p1, p2;
656
657 if (yv2 < d_ptr->m_handleSize / 2) {
658 p1 = QPoint(xv1, yv2);
659 p2 = QPoint(xv2, yv2);
660 } else if (yv1 > d_ptr->m_handleSize / 2) {
661 p1 = QPoint(xv1, yv1);
662 p2 = QPoint(xv2, yv1);
663 } else {
664 p1 = QPoint(xv1, qRound(d_ptr->m_handleSize / 2));
665 p2 = QPoint(xv2, qRound(d_ptr->m_handleSize / 2));
666 }
667
668 QList<QtGradientStop *> beginList = d_ptr->stopsAt(p1);
669 QList<QtGradientStop *> endList = d_ptr->stopsAt(p2);
670
671 double x1 = d_ptr->fromViewport(xv1);
672 double x2 = d_ptr->fromViewport(xv2);
673
674 QListIterator<QtGradientStop *> itStop(d_ptr->m_stops);
675 while (itStop.hasNext()) {
676 QtGradientStop *stop = itStop.next();
677 if ((stop->position() >= x1 && stop->position() <= x2) ||
678 beginList.contains(stop) || endList.contains(stop))
679 d_ptr->m_model->selectStop(stop, true);
680 else
681 d_ptr->m_model->selectStop(stop, false);
682 }
683 }
684 }
685
mouseDoubleClickEvent(QMouseEvent * e)686 void QtGradientStopsWidget::mouseDoubleClickEvent(QMouseEvent *e)
687 {
688 if (!d_ptr->m_model)
689 return;
690
691 if (e->button() != Qt::LeftButton)
692 return;
693
694 if (d_ptr->m_clickPos != e->pos()) {
695 mousePressEvent(e);
696 return;
697 }
698 d_ptr->m_moving = true;
699 d_ptr->m_moveStops.clear();
700 d_ptr->m_moveOriginal.clear();
701
702 QtGradientStop *stop = d_ptr->newStop(e->pos());
703
704 if (!stop)
705 return;
706
707 d_ptr->m_model->clearSelection();
708 d_ptr->m_model->selectStop(stop, true);
709
710 d_ptr->setupMove(stop, e->pos().x());
711
712 viewport()->update();
713 }
714
keyPressEvent(QKeyEvent * e)715 void QtGradientStopsWidget::keyPressEvent(QKeyEvent *e)
716 {
717 typedef QtGradientStopsModel::PositionStopMap PositionStopMap;
718 if (!d_ptr->m_model)
719 return;
720
721 if (e->key() == Qt::Key_Delete || e->key() == Qt::Key_Backspace) {
722 d_ptr->m_model->deleteStops();
723 } else if (e->key() == Qt::Key_Left || e->key() == Qt::Key_Right ||
724 e->key() == Qt::Key_Home || e->key() == Qt::Key_End) {
725 PositionStopMap stops = d_ptr->m_model->stops();
726 if (stops.isEmpty())
727 return;
728 QtGradientStop *newCurrent = 0;
729 QtGradientStop *current = d_ptr->m_model->currentStop();
730 if (!current || e->key() == Qt::Key_Home || e->key() == Qt::Key_End) {
731 if (e->key() == Qt::Key_Left || e->key() == Qt::Key_Home)
732 newCurrent = stops.constBegin().value();
733 else if (e->key() == Qt::Key_Right || e->key() == Qt::Key_End)
734 newCurrent = (--stops.constEnd()).value();
735 } else {
736 PositionStopMap::ConstIterator itStop = stops.constBegin();
737 while (itStop.value() != current)
738 ++itStop;
739 if (e->key() == Qt::Key_Left && itStop != stops.constBegin())
740 --itStop;
741 else if (e->key() == Qt::Key_Right && itStop != --stops.constEnd())
742 ++itStop;
743 newCurrent = itStop.value();
744 }
745 d_ptr->m_model->clearSelection();
746 d_ptr->m_model->selectStop(newCurrent, true);
747 d_ptr->m_model->setCurrentStop(newCurrent);
748 d_ptr->ensureVisible(newCurrent);
749 } else if (e->key() == Qt::Key_A) {
750 if (e->modifiers() & Qt::ControlModifier)
751 d_ptr->m_model->selectAll();
752 }
753 }
754
paintEvent(QPaintEvent * e)755 void QtGradientStopsWidget::paintEvent(QPaintEvent *e)
756 {
757 Q_UNUSED(e)
758 if (!d_ptr->m_model)
759 return;
760
761 QtGradientStopsModel *model = d_ptr->m_model;
762 #ifndef QT_NO_DRAGANDDROP
763 if (d_ptr->m_dragModel)
764 model = d_ptr->m_dragModel;
765 #endif
766
767 QSize size = viewport()->size();
768 int w = size.width();
769 double h = size.height() - d_ptr->m_handleSize;
770 if (w <= 0)
771 return;
772
773 QPixmap pix(size);
774 QPainter p;
775
776 if (d_ptr->m_backgroundCheckered) {
777 int pixSize = 20;
778 QPixmap pm(2 * pixSize, 2 * pixSize);
779 QPainter pmp(&pm);
780 pmp.fillRect(0, 0, pixSize, pixSize, Qt::white);
781 pmp.fillRect(pixSize, pixSize, pixSize, pixSize, Qt::white);
782 pmp.fillRect(0, pixSize, pixSize, pixSize, Qt::black);
783 pmp.fillRect(pixSize, 0, pixSize, pixSize, Qt::black);
784
785 p.begin(&pix);
786 p.setBrushOrigin((size.width() % pixSize + pixSize) / 2, (size.height() % pixSize + pixSize) / 2);
787 p.fillRect(viewport()->rect(), pm);
788 p.setBrushOrigin(0, 0);
789 } else {
790 p.begin(viewport());
791 }
792
793 double viewBegin = (double)w * horizontalScrollBar()->value() / d_ptr->m_scaleFactor;
794
795 int val = horizontalScrollBar()->value();
796 int max = horizontalScrollBar()->maximum();
797
798 double begin = (double)val / (d_ptr->m_scaleFactor + max);
799 double end = (double)(val + d_ptr->m_scaleFactor) / (d_ptr->m_scaleFactor + max);
800 double width = end - begin;
801
802 if (h > 0) {
803 QLinearGradient lg(0, 0, w, 0);
804 QMap<qreal, QtGradientStop *> stops = model->stops();
805 QMapIterator<qreal, QtGradientStop *> itStop(stops);
806 while (itStop.hasNext()) {
807 QtGradientStop *stop = itStop.next().value();
808 double pos = stop->position();
809 if (pos >= begin && pos <= end) {
810 double gradPos = (pos - begin) / width;
811 QColor c = stop->color();
812 lg.setColorAt(gradPos, c);
813 }
814 //lg.setColorAt(stop->position(), stop->color());
815 }
816 lg.setColorAt(0, model->color(begin));
817 lg.setColorAt(1, model->color(end));
818 QImage img(w, 1, QImage::Format_ARGB32_Premultiplied);
819 QPainter p1(&img);
820 p1.setCompositionMode(QPainter::CompositionMode_Source);
821
822 /*
823 if (viewBegin != 0)
824 p1.translate(-viewBegin, 0);
825 if (d_ptr->m_zoom != 1)
826 p1.scale(d_ptr->m_zoom, 1);
827 */
828 p1.fillRect(0, 0, w, 1, lg);
829
830 p.fillRect(QRectF(0, d_ptr->m_handleSize, w, h), QPixmap::fromImage(img));
831 }
832
833
834 double handleWidth = d_ptr->m_handleSize * d_ptr->m_scaleFactor / (w * (d_ptr->m_scaleFactor + max));
835
836 QColor insideColor = QColor::fromRgb(0x20, 0x20, 0x20, 0xFF);
837 QColor drawColor;
838 QColor back1 = QColor(Qt::lightGray);
839 QColor back2 = QColor(Qt::darkGray);
840 QColor back = QColor::fromRgb((back1.red() + back2.red()) / 2,
841 (back1.green() + back2.green()) / 2,
842 (back1.blue() + back2.blue()) / 2);
843
844 QPen pen;
845 p.setRenderHint(QPainter::Antialiasing);
846 QListIterator<QtGradientStop *> itStop(d_ptr->m_stops);
847 itStop.toBack();
848 while (itStop.hasPrevious()) {
849 QtGradientStop *stop = itStop.previous();
850 double x = stop->position();
851 if (x >= begin - handleWidth / 2 && x <= end + handleWidth / 2) {
852 double viewX = x * w * (d_ptr->m_scaleFactor + max) / d_ptr->m_scaleFactor - viewBegin;
853 p.save();
854 QColor c = stop->color();
855 #ifndef QT_NO_DRAGANDDROP
856 if (stop == d_ptr->m_dragStop)
857 c = d_ptr->m_dragColor;
858 #endif
859 if ((0.3 * c.redF() + 0.59 * c.greenF() + 0.11 * c.blueF()) * c.alphaF() +
860 (0.3 * back.redF() + 0.59 * back.greenF() + 0.11 * back.blueF()) * (1.0 - c.alphaF()) < 0.5) {
861 drawColor = QColor::fromRgb(0xC0, 0xC0, 0xC0, 0xB0);
862 } else {
863 drawColor = QColor::fromRgb(0x40, 0x40, 0x40, 0x80);
864 }
865 QRectF rect(viewX - d_ptr->m_handleSize / 2, 0, d_ptr->m_handleSize, d_ptr->m_handleSize);
866 rect.adjust(0.5, 0.5, -0.5, -0.5);
867 if (h > 0) {
868 pen.setWidthF(1);
869 QLinearGradient lg(0, d_ptr->m_handleSize, 0, d_ptr->m_handleSize + h / 2);
870 lg.setColorAt(0, drawColor);
871 QColor alphaZero = drawColor;
872 alphaZero.setAlpha(0);
873 lg.setColorAt(1, alphaZero);
874 pen.setBrush(lg);
875 p.setPen(pen);
876 p.drawLine(QPointF(viewX, d_ptr->m_handleSize), QPointF(viewX, d_ptr->m_handleSize + h / 2));
877
878 pen.setWidthF(1);
879 pen.setBrush(drawColor);
880 p.setPen(pen);
881 QRectF r1 = rect.adjusted(0.5, 0.5, -0.5, -0.5);
882 QRectF r2 = rect.adjusted(1.5, 1.5, -1.5, -1.5);
883 QColor inColor = QColor::fromRgb(0x80, 0x80, 0x80, 0x80);
884 if (!d_ptr->m_model->isSelected(stop)) {
885 p.setBrush(c);
886 p.drawEllipse(rect);
887 } else {
888 pen.setBrush(insideColor);
889 pen.setWidthF(2);
890 p.setPen(pen);
891 p.setBrush(Qt::NoBrush);
892 p.drawEllipse(r1);
893
894 pen.setBrush(inColor);
895 pen.setWidthF(1);
896 p.setPen(pen);
897 p.setBrush(c);
898 p.drawEllipse(r2);
899 }
900
901 if (d_ptr->m_model->currentStop() == stop) {
902 p.setBrush(Qt::NoBrush);
903 pen.setWidthF(5);
904 pen.setBrush(drawColor);
905 int corr = 4;
906 if (!d_ptr->m_model->isSelected(stop)) {
907 corr = 3;
908 pen.setWidthF(7);
909 }
910 p.setPen(pen);
911 p.drawEllipse(rect.adjusted(corr, corr, -corr, -corr));
912 }
913
914 }
915 p.restore();
916 }
917 }
918 if (d_ptr->m_backgroundCheckered) {
919 p.end();
920 p.begin(viewport());
921 p.drawPixmap(0, 0, pix);
922 }
923 p.end();
924 }
925
focusInEvent(QFocusEvent * e)926 void QtGradientStopsWidget::focusInEvent(QFocusEvent *e)
927 {
928 Q_UNUSED(e)
929 viewport()->update();
930 }
931
focusOutEvent(QFocusEvent * e)932 void QtGradientStopsWidget::focusOutEvent(QFocusEvent *e)
933 {
934 Q_UNUSED(e)
935 viewport()->update();
936 }
937
contextMenuEvent(QContextMenuEvent * e)938 void QtGradientStopsWidget::contextMenuEvent(QContextMenuEvent *e)
939 {
940 if (!d_ptr->m_model)
941 return;
942
943 d_ptr->m_clickPos = e->pos();
944
945 QMenu menu(this);
946 QAction *newStopAction = new QAction(tr("New Stop"), &menu);
947 QAction *deleteAction = new QAction(tr("Delete"), &menu);
948 QAction *flipAllAction = new QAction(tr("Flip All"), &menu);
949 QAction *selectAllAction = new QAction(tr("Select All"), &menu);
950 QAction *zoomInAction = new QAction(tr("Zoom In"), &menu);
951 QAction *zoomOutAction = new QAction(tr("Zoom Out"), &menu);
952 QAction *zoomAllAction = new QAction(tr("Reset Zoom"), &menu);
953 if (d_ptr->m_model->selectedStops().isEmpty() && !d_ptr->m_model->currentStop())
954 deleteAction->setEnabled(false);
955 if (zoom() <= 1) {
956 zoomOutAction->setEnabled(false);
957 zoomAllAction->setEnabled(false);
958 } else if (zoom() >= 100) {
959 zoomInAction->setEnabled(false);
960 }
961 connect(newStopAction, SIGNAL(triggered()), this, SLOT(slotNewStop()));
962 connect(deleteAction, SIGNAL(triggered()), this, SLOT(slotDelete()));
963 connect(flipAllAction, SIGNAL(triggered()), this, SLOT(slotFlipAll()));
964 connect(selectAllAction, SIGNAL(triggered()), this, SLOT(slotSelectAll()));
965 connect(zoomInAction, SIGNAL(triggered()), this, SLOT(slotZoomIn()));
966 connect(zoomOutAction, SIGNAL(triggered()), this, SLOT(slotZoomOut()));
967 connect(zoomAllAction, SIGNAL(triggered()), this, SLOT(slotResetZoom()));
968 menu.addAction(newStopAction);
969 menu.addAction(deleteAction);
970 menu.addAction(flipAllAction);
971 menu.addAction(selectAllAction);
972 menu.addSeparator();
973 menu.addAction(zoomInAction);
974 menu.addAction(zoomOutAction);
975 menu.addAction(zoomAllAction);
976 menu.exec(e->globalPos());
977 }
978
wheelEvent(QWheelEvent * e)979 void QtGradientStopsWidget::wheelEvent(QWheelEvent *e)
980 {
981 int numDegrees = e->delta() / 8;
982 int numSteps = numDegrees / 15;
983
984 int shift = numSteps;
985 if (shift < 0)
986 shift = -shift;
987 int pow = 1 << shift;
988 //const double c = 0.7071067; // 2 steps per doubled value
989 const double c = 0.5946036; // 4 steps pre doubled value
990 // in general c = pow(2, 1 / n) / 2; where n is the step
991 double factor = pow * c;
992
993 double newZoom = zoom();
994 if (numSteps < 0)
995 newZoom /= factor;
996 else
997 newZoom *= factor;
998 if (newZoom > 100)
999 newZoom = 100;
1000 if (newZoom < 1)
1001 newZoom = 1;
1002
1003 if (newZoom == zoom())
1004 return;
1005
1006 setZoom(newZoom);
1007 emit zoomChanged(zoom());
1008 }
1009
1010 #ifndef QT_NO_DRAGANDDROP
dragEnterEvent(QDragEnterEvent * event)1011 void QtGradientStopsWidget::dragEnterEvent(QDragEnterEvent *event)
1012 {
1013 const QMimeData *mime = event->mimeData();
1014 if (!mime->hasColor())
1015 return;
1016 event->accept();
1017 d_ptr->m_dragModel = d_ptr->m_model->clone();
1018
1019 d_ptr->m_dragColor = qvariant_cast<QColor>(mime->colorData());
1020 update();
1021 }
1022
dragMoveEvent(QDragMoveEvent * event)1023 void QtGradientStopsWidget::dragMoveEvent(QDragMoveEvent *event)
1024 {
1025 QRectF rect = viewport()->rect();
1026 rect.adjust(0, d_ptr->m_handleSize, 0, 0);
1027 double x = d_ptr->fromViewport(event->pos().x());
1028 QtGradientStop *dragStop = d_ptr->stopAt(event->pos());
1029 if (dragStop) {
1030 event->accept();
1031 d_ptr->removeClonedStop();
1032 d_ptr->changeStop(dragStop->position());
1033 } else if (rect.contains(event->pos())) {
1034 event->accept();
1035 if (d_ptr->m_model->at(x)) {
1036 d_ptr->removeClonedStop();
1037 d_ptr->changeStop(x);
1038 } else {
1039 d_ptr->restoreChangedStop();
1040 d_ptr->cloneStop(x);
1041 }
1042 } else {
1043 event->ignore();
1044 d_ptr->removeClonedStop();
1045 d_ptr->restoreChangedStop();
1046 }
1047
1048 update();
1049 }
1050
dragLeaveEvent(QDragLeaveEvent * event)1051 void QtGradientStopsWidget::dragLeaveEvent(QDragLeaveEvent *event)
1052 {
1053 event->accept();
1054 d_ptr->clearDrag();
1055 update();
1056 }
1057
dropEvent(QDropEvent * event)1058 void QtGradientStopsWidget::dropEvent(QDropEvent *event)
1059 {
1060 event->accept();
1061 if (!d_ptr->m_dragModel)
1062 return;
1063
1064 if (d_ptr->m_changedStop)
1065 d_ptr->m_model->changeStop(d_ptr->m_model->at(d_ptr->m_changedStop->position()), d_ptr->m_dragColor);
1066 else if (d_ptr->m_clonedStop)
1067 d_ptr->m_model->addStop(d_ptr->m_clonedStop->position(), d_ptr->m_dragColor);
1068
1069 d_ptr->clearDrag();
1070 update();
1071 }
1072
clearDrag()1073 void QtGradientStopsWidgetPrivate::clearDrag()
1074 {
1075 removeClonedStop();
1076 restoreChangedStop();
1077 delete m_dragModel;
1078 m_dragModel = 0;
1079 }
1080
removeClonedStop()1081 void QtGradientStopsWidgetPrivate::removeClonedStop()
1082 {
1083 if (!m_clonedStop)
1084 return;
1085 m_dragModel->removeStop(m_clonedStop);
1086 m_clonedStop = 0;
1087 }
1088
restoreChangedStop()1089 void QtGradientStopsWidgetPrivate::restoreChangedStop()
1090 {
1091 if (!m_changedStop)
1092 return;
1093 m_dragModel->changeStop(m_changedStop, m_model->at(m_changedStop->position())->color());
1094 m_changedStop = 0;
1095 m_dragStop = 0;
1096 }
1097
changeStop(qreal pos)1098 void QtGradientStopsWidgetPrivate::changeStop(qreal pos)
1099 {
1100 QtGradientStop *stop = m_dragModel->at(pos);
1101 if (!stop)
1102 return;
1103
1104 m_dragModel->changeStop(stop, m_dragColor);
1105 m_changedStop = stop;
1106 m_dragStop = m_model->at(stop->position());
1107 }
1108
cloneStop(qreal pos)1109 void QtGradientStopsWidgetPrivate::cloneStop(qreal pos)
1110 {
1111 if (m_clonedStop) {
1112 m_dragModel->moveStop(m_clonedStop, pos);
1113 return;
1114 }
1115 QtGradientStop *stop = m_dragModel->at(pos);
1116 if (stop)
1117 return;
1118
1119 m_clonedStop = m_dragModel->addStop(pos, m_dragColor);
1120 }
1121
1122 #endif
1123
setZoom(double zoom)1124 void QtGradientStopsWidget::setZoom(double zoom)
1125 {
1126 double z = zoom;
1127 if (z < 1)
1128 z = 1;
1129 else if (z > 100)
1130 z = 100;
1131
1132 if (d_ptr->m_zoom == z)
1133 return;
1134
1135 d_ptr->m_zoom = z;
1136 int oldMax = horizontalScrollBar()->maximum();
1137 int oldVal = horizontalScrollBar()->value();
1138 horizontalScrollBar()->setRange(0, qRound(d_ptr->m_scaleFactor * (d_ptr->m_zoom - 1)));
1139 int newMax = horizontalScrollBar()->maximum();
1140 double newVal = (oldVal + (double)d_ptr->m_scaleFactor / 2) * (newMax + d_ptr->m_scaleFactor)
1141 / (oldMax + d_ptr->m_scaleFactor) - (double)d_ptr->m_scaleFactor / 2;
1142 horizontalScrollBar()->setValue(qRound(newVal));
1143 viewport()->update();
1144 }
1145
zoom() const1146 double QtGradientStopsWidget::zoom() const
1147 {
1148 return d_ptr->m_zoom;
1149 }
1150
1151 QT_END_NAMESPACE
1152
1153 #include "moc_qtgradientstopswidget.cpp"
1154