1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the QtWidgets module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39 
40 #include "qlcdnumber.h"
41 
42 #include "qbitarray.h"
43 #include "qpainter.h"
44 #include "private/qframe_p.h"
45 
46 QT_BEGIN_NAMESPACE
47 
48 class QLCDNumberPrivate : public QFramePrivate
49 {
50     Q_DECLARE_PUBLIC(QLCDNumber)
51 public:
52     void init();
53     void internalSetString(const QString& s);
54     void drawString(const QString& s, QPainter &, QBitArray * = nullptr, bool = true);
55     //void drawString(const QString &, QPainter &, QBitArray * = 0) const;
56     void drawDigit(const QPoint &, QPainter &, int, char, char = ' ');
57     void drawSegment(const QPoint &, char, QPainter &, int, bool = false);
58 
59     int ndigits;
60     double val;
61     uint base : 2;
62     uint smallPoint : 1;
63     uint fill : 1;
64     uint shadow : 1;
65     QString digitStr;
66     QBitArray points;
67 };
68 
69 /*!
70     \class QLCDNumber
71 
72     \brief The QLCDNumber widget displays a number with LCD-like digits.
73 
74     \ingroup basicwidgets
75     \inmodule QtWidgets
76 
77     \image windows-lcdnumber.png
78 
79     It can display a number in just about any size. It can display
80     decimal, hexadecimal, octal or binary numbers. It is easy to
81     connect to data sources using the display() slot, which is
82     overloaded to take any of five argument types.
83 
84     There are also slots to change the base with setMode() and the
85     decimal point with setSmallDecimalPoint().
86 
87     QLCDNumber emits the overflow() signal when it is asked to display
88     something beyond its range. The range is set by setDigitCount(),
89     but setSmallDecimalPoint() also influences it. If the display is
90     set to hexadecimal, octal or binary, the integer equivalent of the
91     value is displayed.
92 
93     These digits and other symbols can be shown: 0/O, 1, 2, 3, 4, 5/S,
94     6, 7, 8, 9/g, minus, decimal point, A, B, C, D, E, F, h, H, L, o,
95     P, r, u, U, Y, colon, degree sign (which is specified as single
96     quote in the string) and space. QLCDNumber substitutes spaces for
97     illegal characters.
98 
99     It is not possible to retrieve the contents of a QLCDNumber
100     object, although you can retrieve the numeric value with value().
101     If you really need the text, we recommend that you connect the
102     signals that feed the display() slot to another slot as well and
103     store the value there.
104 
105     Incidentally, QLCDNumber is the very oldest part of Qt, tracing
106     its roots back to a BASIC program on the \l{Sinclair Spectrum}{Sinclair Spectrum}.
107 
108     \sa QLabel, QFrame, {Digital Clock Example}, {Tetrix Example}
109 */
110 
111 /*!
112     \enum QLCDNumber::Mode
113 
114     This type determines how numbers are shown.
115 
116     \value Hex  Hexadecimal
117     \value Dec  Decimal
118     \value Oct  Octal
119     \value Bin  Binary
120 
121     If the display is set to hexadecimal, octal or binary, the integer
122     equivalent of the value is displayed.
123 */
124 
125 /*!
126     \enum QLCDNumber::SegmentStyle
127 
128     This type determines the visual appearance of the QLCDNumber
129     widget.
130 
131     \value Outline gives raised segments filled with the background color.
132     \value Filled gives raised segments filled with the windowText color.
133     \value Flat gives flat segments filled with the windowText color.
134 */
135 
136 
137 
138 /*!
139     \fn void QLCDNumber::overflow()
140 
141     This signal is emitted whenever the QLCDNumber is asked to display
142     a too-large number or a too-long string.
143 
144     It is never emitted by setDigitCount().
145 */
146 
147 
int2string(int num,int base,int ndigits,bool * oflow)148 static QString int2string(int num, int base, int ndigits, bool *oflow)
149 {
150     QString s;
151     bool negative;
152     if (num < 0) {
153         negative = true;
154         num      = -num;
155     } else {
156         negative = false;
157     }
158     switch(base) {
159         case QLCDNumber::Hex:
160             s = QString::asprintf("%*x", ndigits, num);
161             break;
162         case QLCDNumber::Dec:
163             s = QString::asprintf("%*i", ndigits, num);
164             break;
165         case QLCDNumber::Oct:
166             s = QString::asprintf("%*o", ndigits, num);
167             break;
168         case QLCDNumber::Bin:
169             {
170                 char buf[42];
171                 char *p = &buf[41];
172                 uint n = num;
173                 int len = 0;
174                 *p = '\0';
175                 do {
176                     *--p = (char)((n&1)+'0');
177                     n >>= 1;
178                     len++;
179                 } while (n != 0);
180                 len = ndigits - len;
181                 if (len > 0)
182                     s += QString(len, QLatin1Char(' '));
183                 s += QLatin1String(p);
184             }
185             break;
186     }
187     if (negative) {
188         for (int i=0; i<(int)s.length(); i++) {
189             if (s[i] != QLatin1Char(' ')) {
190                 if (i != 0) {
191                     s[i-1] = QLatin1Char('-');
192                 } else {
193                     s.insert(0, QLatin1Char('-'));
194                 }
195                 break;
196             }
197         }
198     }
199     if (oflow)
200         *oflow = (int)s.length() > ndigits;
201     return s;
202 }
203 
204 
double2string(double num,int base,int ndigits,bool * oflow)205 static QString double2string(double num, int base, int ndigits, bool *oflow)
206 {
207     QString s;
208     if (base != QLCDNumber::Dec) {
209         bool of = num >= 2147483648.0 || num < -2147483648.0;
210         if (of) {                             // oops, integer overflow
211             if (oflow)
212                 *oflow = true;
213             return s;
214         }
215         s = int2string((int)num, base, ndigits, nullptr);
216     } else {                                    // decimal base
217         int nd = ndigits;
218         do {
219             s = QString::asprintf("%*.*g", ndigits, nd, num);
220             int i = s.indexOf(QLatin1Char('e'));
221             if (i > 0 && s[i+1]==QLatin1Char('+')) {
222                 s[i] = QLatin1Char(' ');
223                 s[i+1] = QLatin1Char('e');
224             }
225         } while (nd-- && (int)s.length() > ndigits);
226     }
227     if (oflow)
228         *oflow = (int)s.length() > ndigits;
229     return s;
230 }
231 
232 
getSegments(char ch)233 static const char *getSegments(char ch)               // gets list of segments for ch
234 {
235     static const char segments[30][8] =
236        { { 0, 1, 2, 4, 5, 6,99, 0},             // 0    0 / O
237          { 2, 5,99, 0, 0, 0, 0, 0},             // 1    1
238          { 0, 2, 3, 4, 6,99, 0, 0},             // 2    2
239          { 0, 2, 3, 5, 6,99, 0, 0},             // 3    3
240          { 1, 2, 3, 5,99, 0, 0, 0},             // 4    4
241          { 0, 1, 3, 5, 6,99, 0, 0},             // 5    5 / S
242          { 0, 1, 3, 4, 5, 6,99, 0},             // 6    6
243          { 0, 2, 5,99, 0, 0, 0, 0},             // 7    7
244          { 0, 1, 2, 3, 4, 5, 6,99},             // 8    8
245          { 0, 1, 2, 3, 5, 6,99, 0},             // 9    9 / g
246          { 3,99, 0, 0, 0, 0, 0, 0},             // 10   -
247          { 7,99, 0, 0, 0, 0, 0, 0},             // 11   .
248          { 0, 1, 2, 3, 4, 5,99, 0},             // 12   A
249          { 1, 3, 4, 5, 6,99, 0, 0},             // 13   B
250          { 0, 1, 4, 6,99, 0, 0, 0},             // 14   C
251          { 2, 3, 4, 5, 6,99, 0, 0},             // 15   D
252          { 0, 1, 3, 4, 6,99, 0, 0},             // 16   E
253          { 0, 1, 3, 4,99, 0, 0, 0},             // 17   F
254          { 1, 3, 4, 5,99, 0, 0, 0},             // 18   h
255          { 1, 2, 3, 4, 5,99, 0, 0},             // 19   H
256          { 1, 4, 6,99, 0, 0, 0, 0},             // 20   L
257          { 3, 4, 5, 6,99, 0, 0, 0},             // 21   o
258          { 0, 1, 2, 3, 4,99, 0, 0},             // 22   P
259          { 3, 4,99, 0, 0, 0, 0, 0},             // 23   r
260          { 4, 5, 6,99, 0, 0, 0, 0},             // 24   u
261          { 1, 2, 4, 5, 6,99, 0, 0},             // 25   U
262          { 1, 2, 3, 5, 6,99, 0, 0},             // 26   Y
263          { 8, 9,99, 0, 0, 0, 0, 0},             // 27   :
264          { 0, 1, 2, 3,99, 0, 0, 0},             // 28   '
265          {99, 0, 0, 0, 0, 0, 0, 0} };           // 29   empty
266 
267     if (ch >= '0' && ch <= '9')
268         return segments[ch - '0'];
269     if (ch >= 'A' && ch <= 'F')
270         return segments[ch - 'A' + 12];
271     if (ch >= 'a' && ch <= 'f')
272         return segments[ch - 'a' + 12];
273 
274     int n;
275     switch (ch) {
276         case '-':
277             n = 10;  break;
278         case 'O':
279             n = 0;   break;
280         case 'g':
281             n = 9;   break;
282         case '.':
283             n = 11;  break;
284         case 'h':
285             n = 18;  break;
286         case 'H':
287             n = 19;  break;
288         case 'l':
289         case 'L':
290             n = 20;  break;
291         case 'o':
292             n = 21;  break;
293         case 'p':
294         case 'P':
295             n = 22;  break;
296         case 'r':
297         case 'R':
298             n = 23;  break;
299         case 's':
300         case 'S':
301             n = 5;   break;
302         case 'u':
303             n = 24;  break;
304         case 'U':
305             n = 25;  break;
306         case 'y':
307         case 'Y':
308             n = 26;  break;
309         case ':':
310             n = 27;  break;
311         case '\'':
312             n = 28;  break;
313         default:
314             n = 29;  break;
315     }
316     return segments[n];
317 }
318 
319 
320 
321 /*!
322     Constructs an LCD number, sets the number of digits to 5, the base
323     to decimal, the decimal point mode to 'small' and the frame style
324     to a raised box. The segmentStyle() is set to \c Outline.
325 
326     The \a parent argument is passed to the QFrame constructor.
327 
328     \sa setDigitCount(), setSmallDecimalPoint()
329 */
330 
QLCDNumber(QWidget * parent)331 QLCDNumber::QLCDNumber(QWidget *parent)
332     : QLCDNumber(5, parent)
333 {
334 }
335 
336 
337 /*!
338     Constructs an LCD number, sets the number of digits to \a
339     numDigits, the base to decimal, the decimal point mode to 'small'
340     and the frame style to a raised box. The segmentStyle() is set to
341     \c Filled.
342 
343     The \a parent argument is passed to the QFrame constructor.
344 
345     \sa setDigitCount(), setSmallDecimalPoint()
346 */
347 
QLCDNumber(uint numDigits,QWidget * parent)348 QLCDNumber::QLCDNumber(uint numDigits, QWidget *parent)
349         : QFrame(*new QLCDNumberPrivate, parent)
350 {
351     Q_D(QLCDNumber);
352     d->ndigits = numDigits;
353     d->init();
354 }
355 
init()356 void QLCDNumberPrivate::init()
357 {
358     Q_Q(QLCDNumber);
359 
360     q->setFrameStyle(QFrame::Box | QFrame::Raised);
361     val        = 0;
362     base       = QLCDNumber::Dec;
363     smallPoint = false;
364     q->setDigitCount(ndigits);
365     q->setSegmentStyle(QLCDNumber::Filled);
366     q->setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum));
367 }
368 
369 /*!
370     Destroys the LCD number.
371 */
372 
~QLCDNumber()373 QLCDNumber::~QLCDNumber()
374 {
375 }
376 
377 
378 /*!
379     \since 4.6
380     \property QLCDNumber::digitCount
381     \brief the current number of digits displayed
382 
383     Corresponds to the current number of digits. If \l
384     QLCDNumber::smallDecimalPoint is false, the decimal point occupies
385     one digit position.
386 
387     By default, this property contains a value of 5.
388 
389     \sa smallDecimalPoint
390 */
391 
392 /*!
393   Sets the current number of digits to \a numDigits. Must
394   be in the range 0..99.
395  */
setDigitCount(int numDigits)396 void QLCDNumber::setDigitCount(int numDigits)
397 {
398     Q_D(QLCDNumber);
399     if (Q_UNLIKELY(numDigits > 99)) {
400         qWarning("QLCDNumber::setNumDigits: (%s) Max 99 digits allowed",
401                  objectName().toLocal8Bit().constData());
402         numDigits = 99;
403     }
404     if (Q_UNLIKELY(numDigits < 0)) {
405         qWarning("QLCDNumber::setNumDigits: (%s) Min 0 digits allowed",
406                  objectName().toLocal8Bit().constData());
407         numDigits = 0;
408     }
409     if (d->digitStr.isNull()) {                  // from constructor
410         d->ndigits = numDigits;
411         d->digitStr.fill(QLatin1Char(' '), d->ndigits);
412         d->points.fill(0, d->ndigits);
413         d->digitStr[d->ndigits - 1] = QLatin1Char('0'); // "0" is the default number
414     } else {
415         bool doDisplay = d->ndigits == 0;
416         if (numDigits == d->ndigits)             // no change
417             return;
418         int i;
419         int dif;
420         if (numDigits > d->ndigits) {            // expand
421             dif = numDigits - d->ndigits;
422             QString buf;
423             buf.fill(QLatin1Char(' '), dif);
424             d->digitStr.insert(0, buf);
425             d->points.resize(numDigits);
426             for (i=numDigits-1; i>=dif; i--)
427                 d->points.setBit(i, d->points.testBit(i-dif));
428             for (i=0; i<dif; i++)
429                 d->points.clearBit(i);
430         } else {                                        // shrink
431             dif = d->ndigits - numDigits;
432             d->digitStr = d->digitStr.right(numDigits);
433             QBitArray tmpPoints = d->points;
434             d->points.resize(numDigits);
435             for (i=0; i<(int)numDigits; i++)
436                 d->points.setBit(i, tmpPoints.testBit(i+dif));
437         }
438         d->ndigits = numDigits;
439         if (doDisplay)
440             display(value());
441         update();
442     }
443 }
444 
445 /*!
446   Returns the current number of digits.
447  */
digitCount() const448 int QLCDNumber::digitCount() const
449 {
450     Q_D(const QLCDNumber);
451     return d->ndigits;
452 }
453 
454 /*!
455     \overload
456 
457     Returns \c true if \a num is too big to be displayed in its entirety;
458     otherwise returns \c false.
459 
460     \sa display(), digitCount(), smallDecimalPoint()
461 */
462 
checkOverflow(int num) const463 bool QLCDNumber::checkOverflow(int num) const
464 {
465     Q_D(const QLCDNumber);
466     bool of;
467     int2string(num, d->base, d->ndigits, &of);
468     return of;
469 }
470 
471 
472 /*!
473     Returns \c true if \a num is too big to be displayed in its entirety;
474     otherwise returns \c false.
475 
476     \sa display(), digitCount(), smallDecimalPoint()
477 */
478 
checkOverflow(double num) const479 bool QLCDNumber::checkOverflow(double num) const
480 {
481     Q_D(const QLCDNumber);
482     bool of;
483     double2string(num, d->base, d->ndigits, &of);
484     return of;
485 }
486 
487 
488 /*!
489     \property QLCDNumber::mode
490     \brief the current display mode (number base)
491 
492     Corresponds to the current display mode, which is one of \c Bin,
493     \c Oct, \c Dec (the default) and \c Hex. \c Dec mode can display
494     floating point values, the other modes display the integer
495     equivalent.
496 
497     \sa smallDecimalPoint(), setHexMode(), setDecMode(), setOctMode(), setBinMode()
498 */
499 
mode() const500 QLCDNumber::Mode QLCDNumber::mode() const
501 {
502     Q_D(const QLCDNumber);
503     return (QLCDNumber::Mode) d->base;
504 }
505 
setMode(Mode m)506 void QLCDNumber::setMode(Mode m)
507 {
508     Q_D(QLCDNumber);
509     d->base = m;
510     display(d->val);
511 }
512 
513 
514 /*!
515     \property QLCDNumber::value
516     \brief the displayed value
517 
518     This property corresponds to the current value displayed by the
519     LCDNumber.
520 
521     If the displayed value is not a number, the property has a value
522     of 0.
523 
524     By default, this property contains a value of 0.
525 */
526 
value() const527 double QLCDNumber::value() const
528 {
529     Q_D(const QLCDNumber);
530     return d->val;
531 }
532 
533 /*!
534     \overload
535 
536     Displays the number \a num.
537 */
display(double num)538 void QLCDNumber::display(double num)
539 {
540     Q_D(QLCDNumber);
541     d->val = num;
542     bool of;
543     QString s = double2string(d->val, d->base, d->ndigits, &of);
544     if (of)
545         emit overflow();
546     else
547         d->internalSetString(s);
548 }
549 
550 /*!
551     \property QLCDNumber::intValue
552     \brief the displayed value rounded to the nearest integer
553 
554     This property corresponds to the nearest integer to the current
555     value displayed by the LCDNumber. This is the value used for
556     hexadecimal, octal and binary modes.
557 
558     If the displayed value is not a number, the property has a value
559     of 0.
560 
561     By default, this property contains a value of 0.
562 */
intValue() const563 int QLCDNumber::intValue() const
564 {
565     Q_D(const QLCDNumber);
566     return qRound(d->val);
567 }
568 
569 
570 /*!
571     \overload
572 
573     Displays the number \a num.
574 */
display(int num)575 void QLCDNumber::display(int num)
576 {
577     Q_D(QLCDNumber);
578     d->val = (double)num;
579     bool of;
580     QString s = int2string(num, d->base, d->ndigits, &of);
581     if (of)
582         emit overflow();
583     else
584         d->internalSetString(s);
585 }
586 
587 
588 /*!
589     Displays the number represented by the string \a s.
590 
591     This version of the function disregards mode() and
592     smallDecimalPoint().
593 
594     These digits and other symbols can be shown: 0/O, 1, 2, 3, 4, 5/S,
595     6, 7, 8, 9/g, minus, decimal point, A, B, C, D, E, F, h, H, L, o,
596     P, r, u, U, Y, colon, degree sign (which is specified as single
597     quote in the string) and space. QLCDNumber substitutes spaces for
598     illegal characters.
599 */
600 
display(const QString & s)601 void QLCDNumber::display(const QString &s)
602 {
603     Q_D(QLCDNumber);
604     d->val = 0;
605     bool ok = false;
606     double v = s.toDouble(&ok);
607     if (ok)
608         d->val = v;
609     d->internalSetString(s);
610 }
611 
612 /*!
613     Calls setMode(Hex). Provided for convenience (e.g. for
614     connecting buttons to it).
615 
616     \sa setMode(), setDecMode(), setOctMode(), setBinMode(), mode()
617 */
618 
setHexMode()619 void QLCDNumber::setHexMode()
620 {
621     setMode(Hex);
622 }
623 
624 
625 /*!
626     Calls setMode(Dec). Provided for convenience (e.g. for
627     connecting buttons to it).
628 
629     \sa setMode(), setHexMode(), setOctMode(), setBinMode(), mode()
630 */
631 
setDecMode()632 void QLCDNumber::setDecMode()
633 {
634     setMode(Dec);
635 }
636 
637 
638 /*!
639     Calls setMode(Oct). Provided for convenience (e.g. for
640     connecting buttons to it).
641 
642     \sa setMode(), setHexMode(), setDecMode(), setBinMode(), mode()
643 */
644 
setOctMode()645 void QLCDNumber::setOctMode()
646 {
647     setMode(Oct);
648 }
649 
650 
651 /*!
652     Calls setMode(Bin). Provided for convenience (e.g. for
653     connecting buttons to it).
654 
655     \sa setMode(), setHexMode(), setDecMode(), setOctMode(), mode()
656 */
657 
setBinMode()658 void QLCDNumber::setBinMode()
659 {
660     setMode(Bin);
661 }
662 
663 
664 /*!
665     \property QLCDNumber::smallDecimalPoint
666     \brief the style of the decimal point
667 
668     If true the decimal point is drawn between two digit positions.
669     Otherwise it occupies a digit position of its own, i.e. is drawn
670     in a digit position. The default is false.
671 
672     The inter-digit space is made slightly wider when the decimal
673     point is drawn between the digits.
674 
675     \sa mode
676 */
677 
setSmallDecimalPoint(bool b)678 void QLCDNumber::setSmallDecimalPoint(bool b)
679 {
680     Q_D(QLCDNumber);
681     d->smallPoint = b;
682     update();
683 }
684 
smallDecimalPoint() const685 bool QLCDNumber::smallDecimalPoint() const
686 {
687     Q_D(const QLCDNumber);
688     return d->smallPoint;
689 }
690 
691 
692 
693 /*!\reimp
694 */
695 
696 
paintEvent(QPaintEvent *)697 void QLCDNumber::paintEvent(QPaintEvent *)
698 {
699     Q_D(QLCDNumber);
700     QPainter p(this);
701     drawFrame(&p);
702     p.setRenderHint(QPainter::Antialiasing);
703     if (d->shadow)
704         p.translate(0.5, 0.5);
705 
706     if (d->smallPoint)
707         d->drawString(d->digitStr, p, &d->points, false);
708     else
709         d->drawString(d->digitStr, p, nullptr, false);
710 }
711 
712 
internalSetString(const QString & s)713 void QLCDNumberPrivate::internalSetString(const QString& s)
714 {
715     Q_Q(QLCDNumber);
716     QString buffer(ndigits, QChar());
717     int i;
718     int len = s.length();
719     QBitArray newPoints(ndigits);
720 
721     if (!smallPoint) {
722         if (len == ndigits)
723             buffer = s;
724         else
725             buffer = s.right(ndigits).rightJustified(ndigits, QLatin1Char(' '));
726     } else {
727         int  index = -1;
728         bool lastWasPoint = true;
729         newPoints.clearBit(0);
730         for (i=0; i<len; i++) {
731             if (s[i] == QLatin1Char('.')) {
732                 if (lastWasPoint) {           // point already set for digit?
733                     if (index == ndigits - 1) // no more digits
734                         break;
735                     index++;
736                     buffer[index] = QLatin1Char(' ');        // 2 points in a row, add space
737                 }
738                 newPoints.setBit(index);        // set decimal point
739                 lastWasPoint = true;
740             } else {
741                 if (index == ndigits - 1)
742                     break;
743                 index++;
744                 buffer[index] = s[i];
745                 newPoints.clearBit(index);      // decimal point default off
746                 lastWasPoint = false;
747             }
748         }
749         if (index < ((int) ndigits) - 1) {
750             for(i=index; i>=0; i--) {
751                 buffer[ndigits - 1 - index + i] = buffer[i];
752                 newPoints.setBit(ndigits - 1 - index + i,
753                                    newPoints.testBit(i));
754             }
755             for(i=0; i<ndigits-index-1; i++) {
756                 buffer[i] = QLatin1Char(' ');
757                 newPoints.clearBit(i);
758             }
759         }
760     }
761 
762     if (buffer == digitStr)
763         return;
764 
765     digitStr = buffer;
766     if (smallPoint)
767         points = newPoints;
768     q->update();
769 }
770 
771 /*!
772   \internal
773 */
774 
drawString(const QString & s,QPainter & p,QBitArray * newPoints,bool newString)775 void QLCDNumberPrivate::drawString(const QString &s, QPainter &p,
776                                    QBitArray *newPoints, bool newString)
777 {
778     Q_Q(QLCDNumber);
779     QPoint  pos;
780 
781     int digitSpace = smallPoint ? 2 : 1;
782     int xSegLen    = q->width()*5/(ndigits*(5 + digitSpace) + digitSpace);
783     int ySegLen    = q->height()*5/12;
784     int segLen     = ySegLen > xSegLen ? xSegLen : ySegLen;
785     int xAdvance   = segLen*(5 + digitSpace)/5;
786     int xOffset    = (q->width() - ndigits*xAdvance + segLen/5)/2;
787     int yOffset    = (q->height() - segLen*2)/2;
788 
789     for (int i=0;  i<ndigits; i++) {
790         pos = QPoint(xOffset + xAdvance*i, yOffset);
791         if (newString)
792             drawDigit(pos, p, segLen, s[i].toLatin1(), digitStr[i].toLatin1());
793         else
794             drawDigit(pos, p, segLen, s[i].toLatin1());
795         if (newPoints) {
796             char newPoint = newPoints->testBit(i) ? '.' : ' ';
797             if (newString) {
798                 char oldPoint = points.testBit(i) ? '.' : ' ';
799                 drawDigit(pos, p, segLen, newPoint, oldPoint);
800             } else {
801                 drawDigit(pos, p, segLen, newPoint);
802             }
803         }
804     }
805     if (newString) {
806         digitStr = s;
807         digitStr.truncate(ndigits);
808         if (newPoints)
809             points = *newPoints;
810     }
811 }
812 
813 
814 /*!
815   \internal
816 */
817 
drawDigit(const QPoint & pos,QPainter & p,int segLen,char newCh,char oldCh)818 void QLCDNumberPrivate::drawDigit(const QPoint &pos, QPainter &p, int segLen,
819                                   char newCh, char oldCh)
820 {
821 // Draws and/or erases segments to change display of a single digit
822 // from oldCh to newCh
823 
824     char updates[18][2];        // can hold 2 times number of segments, only
825                                 // first 9 used if segment table is correct
826     int  nErases;
827     int  nUpdates;
828     const char *segs;
829     int  i,j;
830 
831     const char erase      = 0;
832     const char draw       = 1;
833     const char leaveAlone = 2;
834 
835     segs = getSegments(oldCh);
836     for (nErases=0; segs[nErases] != 99; nErases++) {
837         updates[nErases][0] = erase;            // get segments to erase to
838         updates[nErases][1] = segs[nErases];    // remove old char
839     }
840     nUpdates = nErases;
841     segs = getSegments(newCh);
842     for(i = 0 ; segs[i] != 99 ; i++) {
843         for (j=0;  j<nErases; j++)
844             if (segs[i] == updates[j][1]) {   // same segment ?
845                 updates[j][0] = leaveAlone;     // yes, already on screen
846                 break;
847             }
848         if (j == nErases) {                   // if not already on screen
849             updates[nUpdates][0] = draw;
850             updates[nUpdates][1] = segs[i];
851             nUpdates++;
852         }
853     }
854     for (i=0; i<nUpdates; i++) {
855         if (updates[i][0] == draw)
856             drawSegment(pos, updates[i][1], p, segLen);
857         if (updates[i][0] == erase)
858             drawSegment(pos, updates[i][1], p, segLen, true);
859     }
860 }
861 
862 
addPoint(QPolygon & a,const QPoint & p)863 static void addPoint(QPolygon &a, const QPoint &p)
864 {
865     uint n = a.size();
866     a.resize(n + 1);
867     a.setPoint(n, p);
868 }
869 
870 /*!
871   \internal
872 */
873 
drawSegment(const QPoint & pos,char segmentNo,QPainter & p,int segLen,bool erase)874 void QLCDNumberPrivate::drawSegment(const QPoint &pos, char segmentNo, QPainter &p,
875                                     int segLen, bool erase)
876 {
877     Q_Q(QLCDNumber);
878     QPoint ppt;
879     QPoint pt = pos;
880     int width = segLen/5;
881 
882     const QPalette &pal = q->palette();
883     QColor lightColor,darkColor,fgColor;
884     if (erase){
885         lightColor = pal.color(q->backgroundRole());
886         darkColor  = lightColor;
887         fgColor    = lightColor;
888     } else {
889         lightColor = pal.light().color();
890         darkColor  = pal.dark().color();
891         fgColor    = pal.color(q->foregroundRole());
892     }
893 
894 
895 #define LINETO(X,Y) addPoint(a, QPoint(pt.x() + (X),pt.y() + (Y)))
896 #define LIGHT
897 #define DARK
898 
899     if (fill) {
900         QPolygon a(0);
901         //The following is an exact copy of the switch below.
902         //don't make any changes here
903         switch (segmentNo) {
904         case 0 :
905             ppt = pt;
906             LIGHT;
907             LINETO(segLen - 1,0);
908             DARK;
909             LINETO(segLen - width - 1,width);
910             LINETO(width,width);
911             LINETO(0,0);
912             break;
913         case 1 :
914             pt += QPoint(0 , 1);
915             ppt = pt;
916             LIGHT;
917             LINETO(width,width);
918             DARK;
919             LINETO(width,segLen - width/2 - 2);
920             LINETO(0,segLen - 2);
921             LIGHT;
922             LINETO(0,0);
923             break;
924         case 2 :
925             pt += QPoint(segLen - 1 , 1);
926             ppt = pt;
927             DARK;
928             LINETO(0,segLen - 2);
929             LINETO(-width,segLen - width/2 - 2);
930             LIGHT;
931             LINETO(-width,width);
932             LINETO(0,0);
933             break;
934         case 3 :
935             pt += QPoint(0 , segLen);
936             ppt = pt;
937             LIGHT;
938             LINETO(width,-width/2);
939             LINETO(segLen - width - 1,-width/2);
940             LINETO(segLen - 1,0);
941             DARK;
942             if (width & 1) {            // adjust for integer division error
943                 LINETO(segLen - width - 3,width/2 + 1);
944                 LINETO(width + 2,width/2 + 1);
945             } else {
946                 LINETO(segLen - width - 1,width/2);
947                 LINETO(width,width/2);
948             }
949             LINETO(0,0);
950             break;
951         case 4 :
952             pt += QPoint(0 , segLen + 1);
953             ppt = pt;
954             LIGHT;
955             LINETO(width,width/2);
956             DARK;
957             LINETO(width,segLen - width - 2);
958             LINETO(0,segLen - 2);
959             LIGHT;
960             LINETO(0,0);
961             break;
962         case 5 :
963             pt += QPoint(segLen - 1 , segLen + 1);
964             ppt = pt;
965             DARK;
966             LINETO(0,segLen - 2);
967             LINETO(-width,segLen - width - 2);
968             LIGHT;
969             LINETO(-width,width/2);
970             LINETO(0,0);
971             break;
972         case 6 :
973             pt += QPoint(0 , segLen*2);
974             ppt = pt;
975             LIGHT;
976             LINETO(width,-width);
977             LINETO(segLen - width - 1,-width);
978             LINETO(segLen - 1,0);
979             DARK;
980             LINETO(0,0);
981             break;
982         case 7 :
983             if (smallPoint)   // if smallpoint place'.' between other digits
984                 pt += QPoint(segLen + width/2 , segLen*2);
985             else
986                 pt += QPoint(segLen/2 , segLen*2);
987             ppt = pt;
988             DARK;
989             LINETO(width,0);
990             LINETO(width,-width);
991             LIGHT;
992             LINETO(0,-width);
993             LINETO(0,0);
994             break;
995         case 8 :
996             pt += QPoint(segLen/2 - width/2 + 1 , segLen/2 + width);
997             ppt = pt;
998             DARK;
999             LINETO(width,0);
1000             LINETO(width,-width);
1001             LIGHT;
1002             LINETO(0,-width);
1003             LINETO(0,0);
1004             break;
1005         case 9 :
1006             pt += QPoint(segLen/2 - width/2 + 1 , 3*segLen/2 + width);
1007             ppt = pt;
1008             DARK;
1009             LINETO(width,0);
1010             LINETO(width,-width);
1011             LIGHT;
1012             LINETO(0,-width);
1013             LINETO(0,0);
1014             break;
1015         default :
1016             qWarning("QLCDNumber::drawSegment: (%s) Illegal segment id: %d\n",
1017                      q->objectName().toLocal8Bit().constData(), segmentNo);
1018         }
1019         // End exact copy
1020         p.setPen(Qt::NoPen);
1021         p.setBrush(fgColor);
1022         p.drawPolygon(a);
1023         p.setBrush(Qt::NoBrush);
1024 
1025         pt = pos;
1026     }
1027 #undef LINETO
1028 #undef LIGHT
1029 #undef DARK
1030 
1031 #define LINETO(X,Y) p.drawLine(ppt.x(), ppt.y(), pt.x()+(X), pt.y()+(Y)); \
1032                     ppt = QPoint(pt.x()+(X), pt.y()+(Y))
1033 #define LIGHT p.setPen(lightColor)
1034 #define DARK  p.setPen(darkColor)
1035     if (shadow)
1036         switch (segmentNo) {
1037         case 0 :
1038             ppt = pt;
1039             LIGHT;
1040             LINETO(segLen - 1,0);
1041             DARK;
1042             LINETO(segLen - width - 1,width);
1043             LINETO(width,width);
1044             LINETO(0,0);
1045             break;
1046         case 1 :
1047             pt += QPoint(0,1);
1048             ppt = pt;
1049             LIGHT;
1050             LINETO(width,width);
1051             DARK;
1052             LINETO(width,segLen - width/2 - 2);
1053             LINETO(0,segLen - 2);
1054             LIGHT;
1055             LINETO(0,0);
1056             break;
1057         case 2 :
1058             pt += QPoint(segLen - 1 , 1);
1059             ppt = pt;
1060             DARK;
1061             LINETO(0,segLen - 2);
1062             LINETO(-width,segLen - width/2 - 2);
1063             LIGHT;
1064             LINETO(-width,width);
1065             LINETO(0,0);
1066             break;
1067         case 3 :
1068             pt += QPoint(0 , segLen);
1069             ppt = pt;
1070             LIGHT;
1071             LINETO(width,-width/2);
1072             LINETO(segLen - width - 1,-width/2);
1073             LINETO(segLen - 1,0);
1074             DARK;
1075             if (width & 1) {            // adjust for integer division error
1076                 LINETO(segLen - width - 3,width/2 + 1);
1077                 LINETO(width + 2,width/2 + 1);
1078             } else {
1079                 LINETO(segLen - width - 1,width/2);
1080                 LINETO(width,width/2);
1081             }
1082             LINETO(0,0);
1083             break;
1084         case 4 :
1085             pt += QPoint(0 , segLen + 1);
1086             ppt = pt;
1087             LIGHT;
1088             LINETO(width,width/2);
1089             DARK;
1090             LINETO(width,segLen - width - 2);
1091             LINETO(0,segLen - 2);
1092             LIGHT;
1093             LINETO(0,0);
1094             break;
1095         case 5 :
1096             pt += QPoint(segLen - 1 , segLen + 1);
1097             ppt = pt;
1098             DARK;
1099             LINETO(0,segLen - 2);
1100             LINETO(-width,segLen - width - 2);
1101             LIGHT;
1102             LINETO(-width,width/2);
1103             LINETO(0,0);
1104             break;
1105         case 6 :
1106             pt += QPoint(0 , segLen*2);
1107             ppt = pt;
1108             LIGHT;
1109             LINETO(width,-width);
1110             LINETO(segLen - width - 1,-width);
1111             LINETO(segLen - 1,0);
1112             DARK;
1113             LINETO(0,0);
1114             break;
1115         case 7 :
1116             if (smallPoint)   // if smallpoint place'.' between other digits
1117                 pt += QPoint(segLen + width/2 , segLen*2);
1118             else
1119                 pt += QPoint(segLen/2 , segLen*2);
1120             ppt = pt;
1121             DARK;
1122             LINETO(width,0);
1123             LINETO(width,-width);
1124             LIGHT;
1125             LINETO(0,-width);
1126             LINETO(0,0);
1127             break;
1128         case 8 :
1129             pt += QPoint(segLen/2 - width/2 + 1 , segLen/2 + width);
1130             ppt = pt;
1131             DARK;
1132             LINETO(width,0);
1133             LINETO(width,-width);
1134             LIGHT;
1135             LINETO(0,-width);
1136             LINETO(0,0);
1137             break;
1138         case 9 :
1139             pt += QPoint(segLen/2 - width/2 + 1 , 3*segLen/2 + width);
1140             ppt = pt;
1141             DARK;
1142             LINETO(width,0);
1143             LINETO(width,-width);
1144             LIGHT;
1145             LINETO(0,-width);
1146             LINETO(0,0);
1147             break;
1148         default :
1149             qWarning("QLCDNumber::drawSegment: (%s) Illegal segment id: %d\n",
1150                      q->objectName().toLocal8Bit().constData(), segmentNo);
1151         }
1152 
1153 #undef LINETO
1154 #undef LIGHT
1155 #undef DARK
1156 }
1157 
1158 
1159 
1160 /*!
1161     \property QLCDNumber::segmentStyle
1162     \brief the style of the LCDNumber
1163 
1164     \table
1165     \header \li Style \li Result
1166     \row \li \c Outline
1167          \li Produces raised segments filled with the background color
1168     \row \li \c Filled
1169             (this is the default).
1170          \li Produces raised segments filled with the foreground color.
1171     \row \li \c Flat
1172          \li Produces flat segments filled with the foreground color.
1173     \endtable
1174 
1175     \c Outline and \c Filled will additionally use
1176     QPalette::light() and QPalette::dark() for shadow effects.
1177 */
setSegmentStyle(SegmentStyle s)1178 void QLCDNumber::setSegmentStyle(SegmentStyle s)
1179 {
1180     Q_D(QLCDNumber);
1181     d->fill = (s == Flat || s == Filled);
1182     d->shadow = (s == Outline || s == Filled);
1183     update();
1184 }
1185 
segmentStyle() const1186 QLCDNumber::SegmentStyle QLCDNumber::segmentStyle() const
1187 {
1188     Q_D(const QLCDNumber);
1189     Q_ASSERT(d->fill || d->shadow);
1190     if (!d->fill && d->shadow)
1191         return Outline;
1192     if (d->fill && d->shadow)
1193         return Filled;
1194     return Flat;
1195 }
1196 
1197 
1198 /*!\reimp
1199 */
sizeHint() const1200 QSize QLCDNumber::sizeHint() const
1201 {
1202     return QSize(10 + 9 * (digitCount() + (smallDecimalPoint() ? 0 : 1)), 23);
1203 }
1204 
1205 /*! \reimp */
event(QEvent * e)1206 bool QLCDNumber::event(QEvent *e)
1207 {
1208     return QFrame::event(e);
1209 }
1210 
1211 QT_END_NAMESPACE
1212 
1213 #include "moc_qlcdnumber.cpp"
1214