1 /*******************************************************************
2
3 Part of the Fritzing project - http://fritzing.org
4 Copyright (c) 2007-2014 Fachhochschule Potsdam - http://fh-potsdam.de
5
6 Fritzing is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10
11 Fritzing is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with Fritzing. If not, see <http://www.gnu.org/licenses/>.
18
19 ********************************************************************
20
21 $Revision: 6904 $:
22 $Author: irascibl@gmail.com $:
23 $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $
24
25 ********************************************************************/
26
27 #include <QLineEdit>
28 #include <QKeyEvent>
29 #include <QFile>
30 #include <QTextStream>
31 #include <QTimer>
32 #include <QHBoxLayout>
33 #include <QValidator>
34 #include <QLabel>
35 #include <limits>
36
37 #include "zoomslider.h"
38 #include "../debugdialog.h"
39
40 double ZoomSlider::ZoomStep;
41 QList<double> ZoomSlider::ZoomFactors;
42
43 static const int MIN_VALUE = 10;
44 static const int STARTING_VALUE = 100;
45 static const int HEIGHT = 16;
46 static const int STEP = 100;
47
48 static int AutoRepeatDelay = 0;
49 static int AutoRepeatInterval = 0;
50
51 ///////////////////////////////////////////////
52
ZoomLabel(QWidget * parent)53 ZoomLabel::ZoomLabel(QWidget * parent) : QLabel(parent)
54 {
55 if (AutoRepeatDelay == 0) {
56 QPushButton button;
57 AutoRepeatInterval = button.autoRepeatInterval();
58 AutoRepeatDelay = button.autoRepeatDelay();
59 }
60 m_autoRepeat = m_mouseIsDown = m_mouseIsIn = m_repeated = false;
61 m_timer.setSingleShot(false);
62 m_timer.setInterval(AutoRepeatInterval);
63 #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
64 m_timer.setTimerType(Qt::PreciseTimer);
65 #endif
66 connect(&m_timer, SIGNAL(timeout()), this, SLOT(repeat()));
67 }
68
~ZoomLabel()69 ZoomLabel::~ZoomLabel()
70 {
71 }
72
setAutoRepeat(bool autoRepeat)73 void ZoomLabel::setAutoRepeat(bool autoRepeat)
74 {
75 m_autoRepeat = autoRepeat;
76 }
77
setImages(const QString & normal,const QString & pressed)78 void ZoomLabel::setImages(const QString & normal, const QString & pressed)
79 {
80 m_normal.load(normal);
81 m_pressed.load(pressed);
82 setPixmap(m_normal);
83 }
84
85
repeat()86 void ZoomLabel::repeat()
87 {
88 if (m_mouseIsIn && m_mouseIsDown) {
89 m_repeated = true;
90 emit clicked();
91 }
92 }
93
mousePressEvent(QMouseEvent * event)94 void ZoomLabel::mousePressEvent(QMouseEvent * event)
95 {
96 m_timer.stop();
97 this->setMouseTracking(true);
98 this->setPixmap(m_pressed);
99 QLabel::mousePressEvent(event);
100 m_mouseIsDown = m_mouseIsIn = true;
101 m_repeated = false;
102 if (m_autoRepeat) {
103 m_timer.start(AutoRepeatDelay);
104 }
105 }
106
mouseMoveEvent(QMouseEvent * event)107 void ZoomLabel::mouseMoveEvent(QMouseEvent * event)
108 {
109 QLabel::mouseMoveEvent(event);
110 if (m_mouseIsDown) {
111 QPoint p = event->pos();
112 bool mouseIsIn = p.x() >= 0 && p.y() >= 0 && p.x() <= width() && p.y() <= height();
113 if (m_mouseIsIn && !mouseIsIn) {
114 m_timer.stop();
115 this->setPixmap(m_normal);
116 m_mouseIsIn = false;
117 }
118 else if (!m_mouseIsIn && mouseIsIn) {
119 this->setPixmap(m_pressed);
120 m_mouseIsIn = true;
121 m_timer.start(AutoRepeatDelay);
122 }
123 }
124 }
125
mouseReleaseEvent(QMouseEvent * event)126 void ZoomLabel::mouseReleaseEvent(QMouseEvent * event)
127 {
128 m_timer.stop();
129 this->setMouseTracking(false);
130 m_mouseIsDown = false;
131 this->setPixmap(m_normal);
132 QLabel::mouseReleaseEvent(event);
133 if (!m_repeated && m_mouseIsIn) {
134 emit clicked();
135 }
136 }
137
138 /////////////////////////////////////////////
139
ZoomSlider(int maxValue,QWidget * parent)140 ZoomSlider::ZoomSlider(int maxValue, QWidget * parent) : QFrame(parent)
141 {
142 // layout doesn't seem to work: the slider appears too far down in the status bar
143 // because the status bar layout is privileged for the message text
144
145 m_firstTime = true;
146 if (ZoomFactors.size() == 0) {
147 loadFactors();
148 }
149
150 this->setObjectName("ZoomSliderFrame");
151
152
153 m_lineEdit = new QLineEdit(this);
154 m_lineEdit->setObjectName("ZoomSliderValue");
155 m_lineEdit->setText(QString("%1").arg(STARTING_VALUE));
156 m_lineEdit->setValidator(new QIntValidator(MIN_VALUE, maxValue + MIN_VALUE, this));
157 m_lineEdit->setAttribute(Qt::WA_MacShowFocusRect, 0);
158 m_lineEdit->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
159
160 m_suffix = new QLabel(tr("%"), this);
161 m_suffix->setObjectName("ZoomSliderLabel");
162
163 m_minusButton = new ZoomLabel(this);
164 m_minusButton->setImages(":/resources/images/icons/zoomSliderMinus.png", ":/resources/images/icons/zoomSliderMinusPressed.png");
165 m_minusButton->setAutoRepeat(true);
166 m_minusButton->setObjectName("ZoomSliderButton");
167 connect(m_minusButton, SIGNAL(clicked()), this, SLOT(minusClicked()));
168
169 m_slider = new QSlider(this);
170 m_slider->setObjectName("ZoomSliderSlider");
171 m_slider->setOrientation(Qt::Horizontal);
172 m_slider->setRange(MIN_VALUE, maxValue + MIN_VALUE);
173 m_slider->setValue(STARTING_VALUE);
174 m_slider->setTickPosition(QSlider::TicksBelow);
175 m_slider->setTickInterval(500);
176
177 m_plusButton = new ZoomLabel(this);
178 m_plusButton->setImages(":/resources/images/icons/zoomSliderPlus.png", ":/resources/images/icons/zoomSliderPlusPressed.png");
179 m_plusButton->setAutoRepeat(true);
180 m_plusButton->setObjectName("ZoomSliderButton");
181 connect(m_plusButton, SIGNAL(clicked()), this, SLOT(plusClicked()));
182
183 connect(m_slider, SIGNAL(valueChanged(int)), this, SLOT(sliderValueChanged(int)));
184 connect(m_lineEdit, SIGNAL(textEdited(const QString &)), this, SLOT(sliderTextEdited(const QString &)));
185
186 //m_userStillWriting = false;
187
188 //m_valueBackup = editText();
189 //m_indexBackup = itemIndex(m_valueBackup);
190 }
191
loadFactors()192 void ZoomSlider::loadFactors() {
193 QFile file(":/resources/zoomfactors.txt");
194 file.open(QFile::ReadOnly);
195 QTextStream stream( &file );
196 int lineNumber = 0;
197 while(!stream.atEnd()) {
198 QString line = stream.readLine();
199 if(lineNumber != 0) {
200 ZoomFactors << line.toDouble();
201 }
202 else
203 {
204 ZoomStep = line.toDouble();
205 }
206 lineNumber++;
207 }
208 file.close();
209 }
210
211
setValue(double value)212 void ZoomSlider::setValue(double value) {
213 QString newText = QString("%1").arg(qRound(value));
214 m_lineEdit->setText(newText);
215 sliderTextEdited(newText, false);
216 }
217
value()218 double ZoomSlider::value() {
219 return m_lineEdit->text().toDouble();
220 }
221
minusClicked()222 void ZoomSlider::minusClicked() {
223 step(-1);
224 }
225
plusClicked()226 void ZoomSlider::plusClicked() {
227 step(1);
228 }
229
step(int direction)230 void ZoomSlider::step(int direction) {
231 int minIndex = 0;
232 double minDiff = std::numeric_limits<double>::max();
233 double v = value();
234 for (int i = 0; i < ZoomFactors.count(); i++) {
235 double f = ZoomFactors[i];
236 if (qAbs(f - v) < minDiff) {
237 minDiff = qAbs(f - v);
238 minIndex = i;
239 }
240 }
241
242 minIndex += direction;
243 if (minIndex < 0) {
244 minIndex = 0;
245 }
246 else if (minIndex >= ZoomFactors.count()) {
247 minIndex = ZoomFactors.count() - 1;
248 }
249
250 setValue(ZoomFactors[minIndex]);
251 emit zoomChanged(ZoomFactors[minIndex]);
252 }
253
sliderValueChanged(int newValue)254 void ZoomSlider::sliderValueChanged(int newValue) {
255 newValue = (newValue / 10) * 10; // make it so moving the slider outputs nice rounded values
256 QString newText = QString("%1").arg(newValue);
257 if (newText.compare(m_lineEdit->text()) != 0) {
258 m_lineEdit->setText(newText);
259 emit zoomChanged(newValue);
260 }
261 }
262
sliderTextEdited(const QString & newText)263 void ZoomSlider::sliderTextEdited(const QString & newText) {
264 sliderTextEdited(newText, true);
265 }
266
sliderTextEdited(const QString & newText,bool doEmit)267 void ZoomSlider::sliderTextEdited(const QString & newText, bool doEmit)
268 {
269 int value = newText.toInt();
270 if (m_slider->value() != value) {
271 disconnect(m_slider, SIGNAL(valueChanged(int)), this, SLOT(sliderValueChanged(int)));
272 m_slider->setValue(value);
273 connect(m_slider, SIGNAL(valueChanged(int)), this, SLOT(sliderValueChanged(int)));
274 if (doEmit) emit zoomChanged(value);
275 }
276 }
277
zoomIn()278 void ZoomSlider::zoomIn () {
279 plusClicked();
280 }
281
zoomOut()282 void ZoomSlider::zoomOut () {
283 minusClicked();
284 }
285
showEvent(QShowEvent * event)286 void ZoomSlider::showEvent(QShowEvent * event)
287 {
288 // can't get QHLayout to work, so shoving widgets into place here
289 // because widths aren't set at constructor time
290 QFrame::showEvent(event);
291 if (m_firstTime) {
292 m_firstTime = false;
293 int soFar = 0;
294 m_lineEdit->move(soFar, -2);
295 soFar += m_lineEdit->width();
296 m_suffix->move(soFar, 0);
297 soFar += m_suffix->width();
298 m_minusButton->move(soFar, 0);
299 soFar += m_minusButton->width();
300 m_slider->move(soFar, 0);
301 soFar += m_slider->width();
302 m_plusButton->move(soFar, 0);
303 }
304 }
305