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