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