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