SDRAngel  4.11.5
Developer docs for <a href="https://github.com/f4exb/sdrangel">SDRangel<\a>, an Open Source Qt5 / OpenGL 3.0+ SDR and signal analyzer frontend to various hardware.
valuedial.cpp
Go to the documentation of this file.
1 // Copyright (C) 2012 maintech GmbH, Otto-Hahn-Str. 15, 97204 Hoechberg, Germany //
3 // written by Christian Daniel //
4 // //
5 // This program is free software; you can redistribute it and/or modify //
6 // it under the terms of the GNU General Public License as published by //
7 // the Free Software Foundation as version 3 of the License, or //
8 // (at your option) any later version. //
9 // //
10 // This program is distributed in the hope that it will be useful, //
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of //
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
13 // GNU General Public License V3 for more details. //
14 // //
15 // You should have received a copy of the GNU General Public License //
16 // along with this program. If not, see <http://www.gnu.org/licenses/>. //
18 
19 #include <QKeyEvent>
20 #include <QLocale>
21 #include <QMouseEvent>
22 #include <QPainter>
23 #include <QWheelEvent>
24 #include <cstdlib>
25 
26 #include "gui/valuedial.h"
27 
28 ValueDial::ValueDial(QWidget *parent, ColorMapper colorMapper) :
29  QWidget(parent),
30  m_animationState(0),
31  m_colorMapper(colorMapper)
32 {
33  setAutoFillBackground(false);
34  setAttribute(Qt::WA_OpaquePaintEvent, true);
35  setAttribute(Qt::WA_NoSystemBackground, true);
36  setMouseTracking(true);
37  setFocusPolicy(Qt::StrongFocus);
38 
39  m_background.setStart(0, 0);
40  m_background.setFinalStop(0, 1);
41  m_background.setCoordinateMode(QGradient::ObjectBoundingMode);
42 
43  ColorMapper::colormap::const_iterator cmit = m_colorMapper.getDialBackgroundColorMap().begin();
44  ColorMapper::colormap::const_iterator cmitEnd = m_colorMapper.getDialBackgroundColorMap().end();
45 
46  for (; cmit != cmitEnd; ++cmit) {
47  m_background.setColorAt(cmit->first, cmit->second);
48  }
49 
50  m_value = 0;
51  m_valueNew = 0;
52  m_valueMin = 0;
53  m_valueMax = 2200000;
54  m_numDigits = 7;
56  m_cursor = -1;
57  m_digitWidth = 0;
58  m_digitHeight = 0;
59 
62  m_cursorState = false;
63 
64  const QLocale &cLocale = QLocale::c();
65  m_groupSeparator = cLocale.groupSeparator();
66 
67  connect(&m_animationTimer, SIGNAL(timeout()), this, SLOT(animate()));
68  connect(&m_blinkTimer, SIGNAL(timeout()), this, SLOT(blink()));
69 }
70 
71 void ValueDial::setFont(const QFont &font)
72 {
73  QWidget::setFont(font);
74 
75  QFontMetrics fm(font);
76  m_digitWidth = fm.width('0');
77  m_digitHeight = fm.ascent();
80  }
81  setFixedWidth((m_numDigits + m_numDecimalPoints) * m_digitWidth + 2);
82  setFixedHeight(m_digitHeight * 2 + 2);
83 }
84 
85 void ValueDial::setBold(bool bold)
86 {
87  QFont f = font();
88  f.setBold(bold);
89  setFont(f);
90 }
91 
93 {
94  m_colorMapper = colorMapper;
95 
96  ColorMapper::colormap::const_iterator cmit = m_colorMapper.getDialBackgroundColorMap().begin();
97  ColorMapper::colormap::const_iterator cmitEnd = m_colorMapper.getDialBackgroundColorMap().end();
98 
99  for (; cmit != cmitEnd; ++cmit) {
100  m_background.setColorAt(cmit->first, cmit->second);
101  }
102 }
103 
104 void ValueDial::setValue(quint64 value)
105 {
106  m_valueNew = value;
107 
108  if (m_valueNew < m_valueMin) {
110  } else if (m_valueNew > m_valueMax) {
112  }
113 
114  if (m_valueNew < m_value) {
115  m_animationState = 1;
116  } else if (m_valueNew > m_value) {
117  m_animationState = -1;
118  } else {
119  return;
120  }
121 
122  m_animationTimer.start(20);
124 }
125 
126 void ValueDial::setValueRange(uint numDigits, quint64 min, quint64 max)
127 {
128  m_numDigits = numDigits;
129  m_numDecimalPoints = m_numDigits < 3 ? 0 : (m_numDigits % 3) == 0 ? (m_numDigits / 3) - 1 : m_numDigits / 3;
130  m_valueMin = min;
131  m_valueMax = max;
132 
134 
135  if (m_value < min) {
136  setValue(min);
137  } else if (m_value > max) {
138  setValue(max);
139  }
140 
141  setFixedWidth((m_numDigits + m_numDecimalPoints) * m_digitWidth + 2);
142 }
143 
144 quint64 ValueDial::findExponent(int digit)
145 {
146  quint64 e = 1;
147  int d = (m_numDigits + m_numDecimalPoints) - digit;
148  d = d - (d / 4) - 1;
149 
150  for (int i = 0; i < d; i++) {
151  e *= 10;
152  }
153 
154  return e;
155 }
156 
157 QChar ValueDial::digitNeigh(QChar c, bool dir)
158 {
159  if (dir)
160  {
161  if (c == QChar('0')) {
162  return QChar('9');
163  } else {
164  return QChar::fromLatin1(c.toLatin1() - 1);
165  }
166  }
167  else
168  {
169  if (c == QChar('9')) {
170  return QChar('0');
171  } else {
172  return QChar::fromLatin1(c.toLatin1() + 1);
173  }
174  }
175 }
176 
177 QString ValueDial::formatText(quint64 value)
178 {
179  QString str = QString("%1").arg(value, m_numDigits, 10, QChar('0'));
180 
181  for (int i = 0; i < m_numDecimalPoints; i++)
182  {
183  int ipoint = m_numDigits - 3 - 3 * i;
184 
185  if (ipoint != 0)
186  { // do not insert leading point
187  const QLocale &cLocale = QLocale::c();
188  str.insert(ipoint, cLocale.groupSeparator());
189  }
190  }
191 
192  return str;
193 }
194 
195 void ValueDial::paintEvent(QPaintEvent *)
196 {
197  QPainter painter(this);
198 
199  painter.setPen(Qt::black);
200  painter.setBrush(m_background);
201 
202  painter.drawRect(0, 0, width() - 1, height() - 1);
203 
204  painter.setPen(m_colorMapper.getBoundaryColor());
205  painter.setBrush(Qt::NoBrush);
206 
207  for (int i = 1; i < m_numDigits + m_numDecimalPoints; i++)
208  {
209  painter.setPen(m_colorMapper.getBoundaryColor());
210  painter.drawLine(1 + i * m_digitWidth, 1, 1 + i * m_digitWidth, height() - 1);
211  painter.setPen(m_colorMapper.getBoundaryAlphaColor());
212  painter.drawLine(0 + i * m_digitWidth, 1, 0 + i * m_digitWidth, height() - 1);
213  painter.drawLine(2 + i * m_digitWidth, 1, 2 + i * m_digitWidth, height() - 1);
214  }
215 
216  painter.setPen(m_colorMapper.getBoundaryAlphaColor());
217  painter.drawLine(1, 1, 1, height() - 1);
218  painter.drawLine(width() - 2, 1, width() - 2, height() - 1);
219 
220  // dial borders
221  painter.setPen(m_colorMapper.getDarkBorderColor());
222  painter.drawLine(0, 0, width() - 2, 0);
223  painter.drawLine(0, height() - 1, 0, 0);
224  painter.setPen(m_colorMapper.getLightBorderColor());
225  painter.drawLine(1, height() - 1, width() - 1, height() - 1);
226  painter.drawLine(width() - 1, height() - 1, width() - 1, 0);
227 
228  if (m_hightlightedDigit >= 0)
229  {
230  painter.setPen(Qt::NoPen);
231  painter.setBrush(m_colorMapper.getHighlightColor());
232  painter.drawRect(2 + m_hightlightedDigit * m_digitWidth, 1, m_digitWidth - 1, height() - 1);
233  }
234 
235  if (m_animationState == 0)
236  {
237  for (int i = 0; i < m_text.length(); i++)
238  {
239  painter.setClipRect(1 + i * m_digitWidth, 1, m_digitWidth, m_digitHeight * 2);
240  painter.setPen(m_colorMapper.getSecondaryForegroundColor());
241  painter.drawText(QRect(1 + i * m_digitWidth, m_digitHeight * 0.6, m_digitWidth, m_digitHeight), Qt::AlignCenter, m_text.mid(i, 1));
242 
243  if (m_text[i] != m_groupSeparator)
244  {
245  painter.setPen(m_colorMapper.getForegroundColor());
246  painter.drawText(QRect(1 + i * m_digitWidth, m_digitHeight * -0.7, m_digitWidth, m_digitHeight), Qt::AlignCenter, digitNeigh(m_text[i], true));
247  painter.drawText(QRect(1 + i * m_digitWidth, m_digitHeight * 1.9, m_digitWidth, m_digitHeight), Qt::AlignCenter, digitNeigh(m_text[i], false));
248  }
249  }
250 
251  painter.setClipping(false);
252 
253  if ((m_cursor >= 0) && (m_cursorState))
254  {
255  painter.setPen(Qt::NoPen);
256  painter.setBrush(m_colorMapper.getSecondaryForegroundColor());
257  painter.drawRect(4 + m_cursor * m_digitWidth, 1 + m_digitHeight * 1.5, m_digitWidth - 5, m_digitHeight / 6);
258  }
259  }
260  else
261  {
262  for (int i = 0; i < m_text.length(); i++)
263  {
264  if (m_text[i] == m_textNew[i])
265  {
266  painter.setClipRect(1 + i * m_digitWidth, 1, m_digitWidth, m_digitHeight * 2);
267  painter.setPen(m_colorMapper.getSecondaryForegroundColor());
268  painter.drawText(QRect(1 + i * m_digitWidth, m_digitHeight * 0.6, m_digitWidth, m_digitHeight), Qt::AlignCenter, m_text.mid(i, 1));
269 
270  if (m_text[i] != m_groupSeparator)
271  {
272  painter.setPen(m_colorMapper.getForegroundColor());
273  painter.drawText(QRect(1 + i * m_digitWidth, m_digitHeight * -0.7, m_digitWidth, m_digitHeight), Qt::AlignCenter, digitNeigh(m_text[i], true));
274  painter.drawText(QRect(1 + i * m_digitWidth, m_digitHeight * 1.9, m_digitWidth, m_digitHeight), Qt::AlignCenter, digitNeigh(m_text[i], false));
275  }
276  }
277  else
278  {
279  int h = m_digitHeight * 0.6 + m_digitHeight * m_animationState / 2.0;
280  painter.setClipRect(1 + i * m_digitWidth, 1, m_digitWidth, m_digitHeight * 2);
281  painter.setPen(m_colorMapper.getSecondaryForegroundColor());
282  painter.drawText(QRect(1 + i * m_digitWidth, h, m_digitWidth, m_digitHeight), Qt::AlignCenter, m_text.mid(i, 1));
283 
284  if (m_text[i] != m_groupSeparator)
285  {
286  painter.setPen(m_colorMapper.getForegroundColor());
287  painter.drawText(QRect(1 + i * m_digitWidth, h + m_digitHeight * -0.7, m_digitWidth, m_digitHeight), Qt::AlignCenter, digitNeigh(m_text[i], true));
288  painter.drawText(QRect(1 + i * m_digitWidth, h + m_digitHeight * 1.9, m_digitWidth, m_digitHeight), Qt::AlignCenter, digitNeigh(m_text[i], false));
289  }
290  }
291  }
292  }
293 }
294 
295 void ValueDial::mousePressEvent(QMouseEvent *event)
296 {
297  int i;
298 
299  i = (event->x() - 1) / m_digitWidth;
300 
301  if (m_text[i] == m_groupSeparator)
302  {
303  i++;
304 
305  if (i > m_numDigits + m_numDecimalPoints) {
306  return;
307  }
308  }
309 
310  Qt::MouseButton mouseButton = event->button();
311 
312  if (mouseButton == Qt::RightButton) // ceil value at current digit
313  {
314  if (m_cursor >= 0)
315  {
316  m_cursor = -1;
317  m_blinkTimer.stop();
318  update();
319  }
320 
321  quint64 e = findExponent(i);
322  m_valueNew = (m_value / e) * e;
324  emit changed(m_valueNew);
325  //qDebug("ValueDial::mousePressEvent: Qt::RightButton: i: %d e: %llu new: %llu", i, e, valueNew);
326  }
327  else if (mouseButton == Qt::LeftButton) // set cursor at current digit
328  {
329  m_cursor = i;
330  m_cursorState = true;
331  m_blinkTimer.start(400);
332 
333  update();
334  }
335 }
336 
337 void ValueDial::mouseMoveEvent(QMouseEvent *event)
338 {
339  int i;
340  i = (event->x() - 1) / m_digitWidth;
341 
342  if (m_text[i] == m_groupSeparator) {
343  i = -1;
344  }
345 
346  if (i != m_hightlightedDigit)
347  {
349  update();
350  }
351 }
352 
353 void ValueDial::wheelEvent(QWheelEvent *event)
354 {
355  int i;
356  i = (event->x() - 1) / m_digitWidth;
357 
358  if (m_text[i] != m_groupSeparator) {
360  } else {
361  return;
362  }
363 
364  if (m_cursor >= 0)
365  {
366  m_cursor = -1;
367  m_blinkTimer.stop();
368  update();
369  }
370 
371  quint64 e = findExponent(m_hightlightedDigit);
372 
373  if (m_animationState == 0)
374  {
375  if (event->delta() < 0)
376  {
377  if (event->modifiers() & Qt::ShiftModifier) {
378  e *= 5;
379  } else if (event->modifiers() & Qt::ControlModifier) {
380  e *= 2;
381  }
382 
383  if (m_value < e) {
385  } else {
386  m_valueNew = m_value - e;
387  }
388  }
389  else
390  {
391  if (event->modifiers() & Qt::ShiftModifier) {
392  e *= 5;
393  } else if (event->modifiers() & Qt::ControlModifier) {
394  e *= 2;
395  }
396 
397  if (m_valueMax - m_value < e) {
399  } else {
400  m_valueNew = m_value + e;
401  }
402  }
403 
405  emit changed(m_valueNew);
406  event->accept();
407  }
408 }
409 
410 void ValueDial::leaveEvent(QEvent *)
411 {
412  if (m_hightlightedDigit != -1)
413  {
414  m_hightlightedDigit = -1;
415  update();
416  }
417 }
418 
419 void ValueDial::keyPressEvent(QKeyEvent *value)
420 {
421  if (m_cursor >= 0)
422  {
423  if ((value->key() == Qt::Key_Return) || (value->key() == Qt::Key_Enter) || (value->key() == Qt::Key_Escape))
424  {
425  m_cursor = -1;
426  m_cursorState = false;
427  m_blinkTimer.stop();
428  update();
429  return;
430  }
431  }
432 
433  if ((m_cursor < 0) && (m_hightlightedDigit >= 0))
434  {
436 
437  if (m_text[m_cursor] == m_groupSeparator) {
438  m_cursor++;
439  }
440 
442  return;
443  }
444 
445  m_cursorState = true;
446  m_blinkTimer.start(400);
447  update();
448  }
449 
450  if (m_cursor < 0) {
451  return;
452  }
453 
454  if ((value->key() == Qt::Key_Left) || (value->key() == Qt::Key_Backspace))
455  {
456  if (m_cursor > 0)
457  {
458  m_cursor--;
459 
460  if (m_text[m_cursor] == m_groupSeparator) {
461  m_cursor--;
462  }
463 
464  if (m_cursor < 0) {
465  m_cursor++;
466  }
467 
468  m_cursorState = true;
469  update();
470  return;
471  }
472  }
473  else if (value->key() == Qt::Key_Right)
474  {
476  {
477  m_cursor++;
478 
479  if (m_text[m_cursor] == m_groupSeparator) {
480  m_cursor++;
481  }
482 
484  m_cursor--;
485  }
486 
487  m_cursorState = true;
488  update();
489  return;
490  }
491  }
492  else if (value->key() == Qt::Key_Up)
493  {
494  quint64 e = findExponent(m_cursor);
495 
496  if (value->modifiers() & Qt::ShiftModifier) {
497  e *= 5;
498  } else if (value->modifiers() & Qt::ControlModifier) {
499  e *= 2;
500  }
501 
502  if (m_animationState != 0) {
504  }
505 
506  if (m_valueMax - m_value < e) {
508  } else {
509  m_valueNew = m_value + e;
510  }
511 
513  emit changed(m_valueNew);
514  }
515  else if (value->key() == Qt::Key_Down)
516  {
517  quint64 e = findExponent(m_cursor);
518 
519  if (value->modifiers() & Qt::ShiftModifier) {
520  e *= 5;
521  } else if (value->modifiers() & Qt::ControlModifier) {
522  e *= 2;
523  }
524 
525  if (m_animationState != 0) {
527  }
528 
529  if (m_value < e) {
531  } else {
532  m_valueNew = m_value - e;
533  }
534 
536  emit changed(m_valueNew);
537  }
538 
539  if (value->text().length() != 1) {
540  return;
541  }
542 
543  QChar c = value->text()[0];
544 
545  if (c >= QChar('0') && (c <= QChar('9')))
546  {
547  int d = c.toLatin1() - '0';
548  quint64 e = findExponent(m_cursor);
549  quint64 v = (m_value / e) % 10;
550 
551  if (m_animationState != 0) {
553  }
554 
555  v = m_value - v * e;
556  v += d * e;
557  setValue(v);
558  emit changed(m_valueNew);
559  m_cursor++;
560 
561  if (m_text[m_cursor] == m_groupSeparator) {
562  m_cursor++;
563  }
564 
566  {
567  m_cursor = -1;
568  m_blinkTimer.stop();
569  }
570  else
571  {
572  m_cursorState = true;
573  }
574 
575  update();
576  }
577 }
578 
579 void ValueDial::focusInEvent(QFocusEvent *)
580 {
581  if (m_cursor == -1)
582  {
583  m_cursor = 0;
584  m_cursorState = true;
585  m_blinkTimer.start(400);
586  update();
587  }
588 }
589 
590 void ValueDial::focusOutEvent(QFocusEvent *)
591 {
592  m_cursor = -1;
593  m_blinkTimer.stop();
594  update();
595 }
596 
598 {
599  update();
600 
601  if (m_animationState > 0)
602  {
604  }
605  else if (m_animationState < 0) {
607  }
608  else
609  {
610  m_animationTimer.stop();
611  m_animationState = 0;
612  return;
613  }
614 
615  if (abs(m_animationState) >= 4)
616  {
617  m_animationState = 0;
618  m_animationTimer.stop();
620  m_text = m_textNew;
621  }
622 }
623 
625 {
626  if (m_cursor >= 0)
627  {
629  update();
630  }
631 }
QChar m_groupSeparator
Definition: valuedial.h:60
int m_numDecimalPoints
Definition: valuedial.h:44
quint64 m_valueMin
Definition: valuedial.h:52
const QColor & getBoundaryColor() const
Definition: colormapper.h:40
int m_digitHeight
Definition: valuedial.h:46
const QColor & getForegroundColor() const
Definition: colormapper.h:37
void wheelEvent(QWheelEvent *)
Definition: valuedial.cpp:353
const QColor & getHighlightColor() const
Definition: colormapper.h:39
QString formatText(quint64 value)
Definition: valuedial.cpp:177
Fixed< IntType, IntBits > abs(Fixed< IntType, IntBits > const &x)
Definition: fixed.h:2313
QTimer m_blinkTimer
Definition: valuedial.h:59
quint64 m_valueMax
Definition: valuedial.h:51
ColorMapper m_colorMapper
Definition: valuedial.h:62
void setValue(quint64 value)
Definition: valuedial.cpp:104
int m_cursor
Definition: valuedial.h:48
void setValueRange(uint numDigits, quint64 min, quint64 max)
Definition: valuedial.cpp:126
const colormap & getDialBackgroundColorMap() const
Definition: colormapper.h:36
void setBold(bool bold)
Definition: valuedial.cpp:85
void keyPressEvent(QKeyEvent *)
Definition: valuedial.cpp:419
void blink()
Definition: valuedial.cpp:624
void focusInEvent(QFocusEvent *)
Definition: valuedial.cpp:579
const QColor & getBoundaryAlphaColor() const
Definition: colormapper.h:41
ValueDial(QWidget *parent=NULL, ColorMapper colorMapper=ColorMapper(ColorMapper::Normal))
Definition: valuedial.cpp:28
int32_t i
Definition: decimators.h:244
QChar digitNeigh(QChar c, bool dir)
Definition: valuedial.cpp:157
void paintEvent(QPaintEvent *)
Definition: valuedial.cpp:195
QTimer m_animationTimer
Definition: valuedial.h:58
void mouseMoveEvent(QMouseEvent *)
Definition: valuedial.cpp:337
const QColor & getDarkBorderColor() const
Definition: colormapper.h:43
int m_digitWidth
Definition: valuedial.h:45
QString m_textNew
Definition: valuedial.h:56
void setColorMapper(ColorMapper colorMapper)
Definition: valuedial.cpp:92
bool m_cursorState
Definition: valuedial.h:49
int m_numDigits
Definition: valuedial.h:43
QString m_text
Definition: valuedial.h:53
void leaveEvent(QEvent *)
Definition: valuedial.cpp:410
const QColor & getLightBorderColor() const
Definition: colormapper.h:42
void setFont(const QFont &font)
Definition: valuedial.cpp:71
int m_hightlightedDigit
Definition: valuedial.h:47
QLinearGradient m_background
Definition: valuedial.h:42
quint64 m_value
Definition: valuedial.h:50
int m_animationState
Definition: valuedial.h:57
void focusOutEvent(QFocusEvent *)
Definition: valuedial.cpp:590
quint64 m_valueNew
Definition: valuedial.h:55
void changed(quint64 value)
void mousePressEvent(QMouseEvent *)
Definition: valuedial.cpp:295
const QColor & getSecondaryForegroundColor() const
Definition: colormapper.h:38
void animate()
Definition: valuedial.cpp:597
T max(const T &x, const T &y)
Definition: framework.h:446
quint64 findExponent(int digit)
Definition: valuedial.cpp:144
T min(const T &x, const T &y)
Definition: framework.h:440