source: trunk/tools/shared/qtgradienteditor/qtgradientstopswidget.cpp@ 397

Last change on this file since 397 was 2, checked in by Dmitry A. Kuminov, 16 years ago

Initially imported qt-all-opensource-src-4.5.1 from Trolltech.

File size: 36.1 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4** Contact: Qt Software Information ([email protected])
5**
6** This file is part of the tools applications of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial Usage
10** Licensees holding valid Qt Commercial licenses may use this file in
11** accordance with the Qt Commercial License Agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and Nokia.
14**
15** GNU Lesser General Public License Usage
16** Alternatively, this file may be used under the terms of the GNU Lesser
17** General Public License version 2.1 as published by the Free Software
18** Foundation and appearing in the file LICENSE.LGPL included in the
19** packaging of this file. Please review the following information to
20** ensure the GNU Lesser General Public License version 2.1 requirements
21** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
22**
23** In addition, as a special exception, Nokia gives you certain
24** additional rights. These rights are described in the Nokia Qt LGPL
25** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
26** package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you are unsure which license is appropriate for your use, please
37** contact the sales department at [email protected].
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
53QT_BEGIN_NAMESPACE
54
55class QtGradientStopsWidgetPrivate
56{
57 QtGradientStopsWidget *q_ptr;
58 Q_DECLARE_PUBLIC(QtGradientStopsWidget)
59public:
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
118double 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
127double 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
136QtGradientStop *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
154QList<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
173void 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
202void 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
212void QtGradientStopsWidgetPrivate::ensureVisible(QtGradientStop *stop)
213{
214 if (!stop)
215 return;
216 ensureVisible(stop->position());
217}
218
219QtGradientStop *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
237void QtGradientStopsWidgetPrivate::slotStopAdded(QtGradientStop *stop)
238{
239 m_stops.append(stop);
240 q_ptr->viewport()->update();
241}
242
243void QtGradientStopsWidgetPrivate::slotStopRemoved(QtGradientStop *stop)
244{
245 m_stops.removeAll(stop);
246 q_ptr->viewport()->update();
247}
248
249void QtGradientStopsWidgetPrivate::slotStopMoved(QtGradientStop *stop, qreal newPos)
250{
251 Q_UNUSED(stop)
252 Q_UNUSED(newPos)
253 q_ptr->viewport()->update();
254}
255
256void QtGradientStopsWidgetPrivate::slotStopsSwapped(QtGradientStop *stop1, QtGradientStop *stop2)
257{
258 Q_UNUSED(stop1)
259 Q_UNUSED(stop2)
260 q_ptr->viewport()->update();
261}
262
263void QtGradientStopsWidgetPrivate::slotStopChanged(QtGradientStop *stop, const QColor &newColor)
264{
265 Q_UNUSED(stop)
266 Q_UNUSED(newColor)
267 q_ptr->viewport()->update();
268}
269
270void QtGradientStopsWidgetPrivate::slotStopSelected(QtGradientStop *stop, bool selected)
271{
272 Q_UNUSED(stop)
273 Q_UNUSED(selected)
274 q_ptr->viewport()->update();
275}
276
277void 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
290void 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
305void QtGradientStopsWidgetPrivate::slotDelete()
306{
307 if (!m_model)
308 return;
309
310 m_model->deleteStops();
311}
312
313void QtGradientStopsWidgetPrivate::slotFlipAll()
314{
315 if (!m_model)
316 return;
317
318 m_model->flipAll();
319}
320
321void QtGradientStopsWidgetPrivate::slotSelectAll()
322{
323 if (!m_model)
324 return;
325
326 m_model->selectAll();
327}
328
329void 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
341void 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
353void 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
362QtGradientStopsWidget::QtGradientStopsWidget(QWidget *parent)
363 : QAbstractScrollArea(parent)
364{
365 d_ptr = new QtGradientStopsWidgetPrivate;
366 d_ptr->q_ptr = this;
367 d_ptr->m_backgroundCheckered = true;
368 d_ptr->m_model = 0;
369 d_ptr->m_handleSize = 25.0;
370 d_ptr->m_scaleFactor = 1000;
371 d_ptr->m_moving = false;
372 d_ptr->m_zoom = 1;
373 d_ptr->m_rubber = new QRubberBand(QRubberBand::Rectangle, this);
374#ifndef QT_NO_DRAGANDDROP
375 d_ptr->m_dragStop = 0;
376 d_ptr->m_changedStop = 0;
377 d_ptr->m_clonedStop = 0;
378 d_ptr->m_dragModel = 0;
379#endif
380 setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
381 setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
382 horizontalScrollBar()->setRange(0, (int)(d_ptr->m_scaleFactor * (d_ptr->m_zoom - 1) + 0.5));
383 horizontalScrollBar()->setPageStep(d_ptr->m_scaleFactor);
384 horizontalScrollBar()->setSingleStep(4);
385 viewport()->setAutoFillBackground(false);
386
387 setAcceptDrops(true);
388
389 setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred));
390}
391
392QtGradientStopsWidget::~QtGradientStopsWidget()
393{
394 delete d_ptr;
395}
396
397QSize QtGradientStopsWidget::sizeHint() const
398{
399 return QSize(qRound(2 * d_ptr->m_handleSize), qRound(3 * d_ptr->m_handleSize) + horizontalScrollBar()->sizeHint().height());
400}
401
402QSize QtGradientStopsWidget::minimumSizeHint() const
403{
404 return QSize(qRound(2 * d_ptr->m_handleSize), qRound(3 * d_ptr->m_handleSize) + horizontalScrollBar()->minimumSizeHint().height());
405}
406
407void QtGradientStopsWidget::setBackgroundCheckered(bool checkered)
408{
409 if (d_ptr->m_backgroundCheckered == checkered)
410 return;
411 d_ptr->m_backgroundCheckered = checkered;
412 update();
413}
414
415bool QtGradientStopsWidget::isBackgroundCheckered() const
416{
417 return d_ptr->m_backgroundCheckered;
418}
419
420void QtGradientStopsWidget::setGradientStopsModel(QtGradientStopsModel *model)
421{
422 if (d_ptr->m_model == model)
423 return;
424
425 if (d_ptr->m_model) {
426 disconnect(d_ptr->m_model, SIGNAL(stopAdded(QtGradientStop *)),
427 this, SLOT(slotStopAdded(QtGradientStop *)));
428 disconnect(d_ptr->m_model, SIGNAL(stopRemoved(QtGradientStop *)),
429 this, SLOT(slotStopRemoved(QtGradientStop *)));
430 disconnect(d_ptr->m_model, SIGNAL(stopMoved(QtGradientStop *, qreal)),
431 this, SLOT(slotStopMoved(QtGradientStop *, qreal)));
432 disconnect(d_ptr->m_model, SIGNAL(stopsSwapped(QtGradientStop *, QtGradientStop *)),
433 this, SLOT(slotStopsSwapped(QtGradientStop *, QtGradientStop *)));
434 disconnect(d_ptr->m_model, SIGNAL(stopChanged(QtGradientStop *, const QColor &)),
435 this, SLOT(slotStopChanged(QtGradientStop *, const QColor &)));
436 disconnect(d_ptr->m_model, SIGNAL(stopSelected(QtGradientStop *, bool)),
437 this, SLOT(slotStopSelected(QtGradientStop *, bool)));
438 disconnect(d_ptr->m_model, SIGNAL(currentStopChanged(QtGradientStop *)),
439 this, SLOT(slotCurrentStopChanged(QtGradientStop *)));
440
441 d_ptr->m_stops.clear();
442 }
443
444 d_ptr->m_model = model;
445
446 if (d_ptr->m_model) {
447 connect(d_ptr->m_model, SIGNAL(stopAdded(QtGradientStop *)),
448 this, SLOT(slotStopAdded(QtGradientStop *)));
449 connect(d_ptr->m_model, SIGNAL(stopRemoved(QtGradientStop *)),
450 this, SLOT(slotStopRemoved(QtGradientStop *)));
451 connect(d_ptr->m_model, SIGNAL(stopMoved(QtGradientStop *, qreal)),
452 this, SLOT(slotStopMoved(QtGradientStop *, qreal)));
453 connect(d_ptr->m_model, SIGNAL(stopsSwapped(QtGradientStop *, QtGradientStop *)),
454 this, SLOT(slotStopsSwapped(QtGradientStop *, QtGradientStop *)));
455 connect(d_ptr->m_model, SIGNAL(stopChanged(QtGradientStop *, const QColor &)),
456 this, SLOT(slotStopChanged(QtGradientStop *, const QColor &)));
457 connect(d_ptr->m_model, SIGNAL(stopSelected(QtGradientStop *, bool)),
458 this, SLOT(slotStopSelected(QtGradientStop *, bool)));
459 connect(d_ptr->m_model, SIGNAL(currentStopChanged(QtGradientStop *)),
460 this, SLOT(slotCurrentStopChanged(QtGradientStop *)));
461
462 QList<QtGradientStop *> stops = d_ptr->m_model->stops().values();
463 QListIterator<QtGradientStop *> itStop(stops);
464 while (itStop.hasNext())
465 d_ptr->slotStopAdded(itStop.next());
466
467 QList<QtGradientStop *> selected = d_ptr->m_model->selectedStops();
468 QListIterator<QtGradientStop *> itSelect(selected);
469 while (itSelect.hasNext())
470 d_ptr->slotStopSelected(itSelect.next(), true);
471
472 d_ptr->slotCurrentStopChanged(d_ptr->m_model->currentStop());
473 }
474}
475
476void QtGradientStopsWidget::mousePressEvent(QMouseEvent *e)
477{
478 typedef QtGradientStopsModel::PositionStopMap PositionStopMap;
479 if (!d_ptr->m_model)
480 return;
481
482 if (e->button() != Qt::LeftButton)
483 return;
484
485 d_ptr->m_moving = true;
486
487 d_ptr->m_moveStops.clear();
488 d_ptr->m_moveOriginal.clear();
489 d_ptr->m_clickPos = e->pos();
490 QtGradientStop *stop = d_ptr->stopAt(e->pos());
491 if (stop) {
492 if (e->modifiers() & Qt::ControlModifier) {
493 d_ptr->m_model->selectStop(stop, !d_ptr->m_model->isSelected(stop));
494 } else if (e->modifiers() & Qt::ShiftModifier) {
495 QtGradientStop *oldCurrent = d_ptr->m_model->currentStop();
496 if (oldCurrent) {
497 PositionStopMap stops = d_ptr->m_model->stops();
498 PositionStopMap::ConstIterator itSt = stops.constFind(oldCurrent->position());
499 if (itSt != stops.constEnd()) {
500 while (itSt != stops.constFind(stop->position())) {
501 d_ptr->m_model->selectStop(itSt.value(), true);
502 if (oldCurrent->position() < stop->position())
503 ++itSt;
504 else
505 --itSt;
506 }
507 }
508 }
509 d_ptr->m_model->selectStop(stop, true);
510 } else {
511 if (!d_ptr->m_model->isSelected(stop)) {
512 d_ptr->m_model->clearSelection();
513 d_ptr->m_model->selectStop(stop, true);
514 }
515 }
516 d_ptr->setupMove(stop, e->pos().x());
517 } else {
518 d_ptr->m_model->clearSelection();
519 d_ptr->m_rubber->setGeometry(QRect(d_ptr->m_clickPos, QSize()));
520 d_ptr->m_rubber->show();
521 }
522 viewport()->update();
523}
524
525void QtGradientStopsWidget::mouseReleaseEvent(QMouseEvent *e)
526{
527 if (!d_ptr->m_model)
528 return;
529
530 if (e->button() != Qt::LeftButton)
531 return;
532
533 d_ptr->m_moving = false;
534 d_ptr->m_rubber->hide();
535 d_ptr->m_moveStops.clear();
536 d_ptr->m_moveOriginal.clear();
537}
538
539void QtGradientStopsWidget::mouseMoveEvent(QMouseEvent *e)
540{
541 typedef QtGradientStopsWidgetPrivate::PositionColorMap PositionColorMap;
542 typedef QtGradientStopsModel::PositionStopMap PositionStopMap;
543 typedef QtGradientStopsWidgetPrivate::StopPositionMap StopPositionMap;
544 if (!d_ptr->m_model)
545 return;
546
547 if (!(e->buttons() & Qt::LeftButton))
548 return;
549
550 if (!d_ptr->m_moving)
551 return;
552
553 if (!d_ptr->m_moveStops.isEmpty()) {
554 double maxOffset = 0.0;
555 double minOffset = 0.0;
556 bool first = true;
557 StopPositionMap::ConstIterator itStop = d_ptr->m_moveStops.constBegin();
558 while (itStop != d_ptr->m_moveStops.constEnd()) {
559 double offset = itStop.value();
560
561 if (first) {
562 maxOffset = offset;
563 minOffset = offset;
564 first = false;
565 } else {