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 | |
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(format: "%*x" , ndigits, num); |
161 | break; |
162 | case QLCDNumber::Dec: |
163 | s = QString::asprintf(format: "%*i" , ndigits, num); |
164 | break; |
165 | case QLCDNumber::Oct: |
166 | s = QString::asprintf(format: "%*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(i: 0, c: QLatin1Char('-')); |
194 | } |
195 | break; |
196 | } |
197 | } |
198 | } |
199 | if (oflow) |
200 | *oflow = (int)s.length() > ndigits; |
201 | return s; |
202 | } |
203 | |
204 | |
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(num: (int)num, base, ndigits, oflow: nullptr); |
216 | } else { // decimal base |
217 | int nd = ndigits; |
218 | do { |
219 | s = QString::asprintf(format: "%*.*g" , ndigits, nd, num); |
220 | int i = s.indexOf(c: 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 | |
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 | |
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 | |
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 | |
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 | |
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 | */ |
396 | void QLCDNumber::setDigitCount(int numDigits) |
397 | { |
398 | Q_D(QLCDNumber); |
399 | if (Q_UNLIKELY(numDigits > 99)) { |
400 | qWarning(msg: "QLCDNumber::setNumDigits: (%s) Max 99 digits allowed" , |
401 | objectName().toLocal8Bit().constData()); |
402 | numDigits = 99; |
403 | } |
404 | if (Q_UNLIKELY(numDigits < 0)) { |
405 | qWarning(msg: "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(c: QLatin1Char(' '), size: d->ndigits); |
412 | d->points.fill(aval: 0, asize: 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(c: QLatin1Char(' '), size: dif); |
424 | d->digitStr.insert(i: 0, s: buf); |
425 | d->points.resize(size: numDigits); |
426 | for (i=numDigits-1; i>=dif; i--) |
427 | d->points.setBit(i, val: d->points.testBit(i: 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(n: numDigits); |
433 | QBitArray tmpPoints = d->points; |
434 | d->points.resize(size: numDigits); |
435 | for (i=0; i<(int)numDigits; i++) |
436 | d->points.setBit(i, val: tmpPoints.testBit(i: i+dif)); |
437 | } |
438 | d->ndigits = numDigits; |
439 | if (doDisplay) |
440 | display(num: value()); |
441 | update(); |
442 | } |
443 | } |
444 | |
445 | /*! |
446 | Returns the current number of digits. |
447 | */ |
448 | 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 | |
463 | bool QLCDNumber::checkOverflow(int num) const |
464 | { |
465 | Q_D(const QLCDNumber); |
466 | bool of; |
467 | int2string(num, base: d->base, ndigits: d->ndigits, oflow: &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 | |
479 | bool QLCDNumber::checkOverflow(double num) const |
480 | { |
481 | Q_D(const QLCDNumber); |
482 | bool of; |
483 | double2string(num, base: d->base, ndigits: d->ndigits, oflow: &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 | |
500 | QLCDNumber::Mode QLCDNumber::mode() const |
501 | { |
502 | Q_D(const QLCDNumber); |
503 | return (QLCDNumber::Mode) d->base; |
504 | } |
505 | |
506 | void QLCDNumber::setMode(Mode m) |
507 | { |
508 | Q_D(QLCDNumber); |
509 | d->base = m; |
510 | display(num: 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 | |
527 | 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 | */ |
538 | void QLCDNumber::display(double num) |
539 | { |
540 | Q_D(QLCDNumber); |
541 | d->val = num; |
542 | bool of; |
543 | QString s = double2string(num: d->val, base: d->base, ndigits: d->ndigits, oflow: &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 | */ |
563 | int QLCDNumber::intValue() const |
564 | { |
565 | Q_D(const QLCDNumber); |
566 | return qRound(d: d->val); |
567 | } |
568 | |
569 | |
570 | /*! |
571 | \overload |
572 | |
573 | Displays the number \a num. |
574 | */ |
575 | void QLCDNumber::display(int num) |
576 | { |
577 | Q_D(QLCDNumber); |
578 | d->val = (double)num; |
579 | bool of; |
580 | QString s = int2string(num, base: d->base, ndigits: d->ndigits, oflow: &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 | |
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: &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 | |
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 | |
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 | |
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 | |
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 | |
678 | void QLCDNumber::setSmallDecimalPoint(bool b) |
679 | { |
680 | Q_D(QLCDNumber); |
681 | d->smallPoint = b; |
682 | update(); |
683 | } |
684 | |
685 | bool QLCDNumber::smallDecimalPoint() const |
686 | { |
687 | Q_D(const QLCDNumber); |
688 | return d->smallPoint; |
689 | } |
690 | |
691 | |
692 | |
693 | /*!\reimp |
694 | */ |
695 | |
696 | |
697 | void QLCDNumber::paintEvent(QPaintEvent *) |
698 | { |
699 | Q_D(QLCDNumber); |
700 | QPainter p(this); |
701 | drawFrame(&p); |
702 | p.setRenderHint(hint: QPainter::Antialiasing); |
703 | if (d->shadow) |
704 | p.translate(dx: 0.5, dy: 0.5); |
705 | |
706 | if (d->smallPoint) |
707 | d->drawString(s: d->digitStr, p, &d->points, false); |
708 | else |
709 | d->drawString(s: d->digitStr, p, nullptr, false); |
710 | } |
711 | |
712 | |
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(n: ndigits).rightJustified(width: ndigits, fill: QLatin1Char(' ')); |
726 | } else { |
727 | int index = -1; |
728 | bool lastWasPoint = true; |
729 | newPoints.clearBit(i: 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(i: 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(i: ndigits - 1 - index + i, |
753 | val: 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 | |
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(pos: ndigits); |
808 | if (newPoints) |
809 | points = *newPoints; |
810 | } |
811 | } |
812 | |
813 | |
814 | /*! |
815 | \internal |
816 | */ |
817 | |
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(ch: 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(ch: 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 | |
863 | static void addPoint(QPolygon &a, const QPoint &p) |
864 | { |
865 | uint n = a.size(); |
866 | a.resize(size: n + 1); |
867 | a.setPoint(index: n, pt: p); |
868 | } |
869 | |
870 | /*! |
871 | \internal |
872 | */ |
873 | |
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(cr: q->backgroundRole()); |
886 | darkColor = lightColor; |
887 | fgColor = lightColor; |
888 | } else { |
889 | lightColor = pal.light().color(); |
890 | darkColor = pal.dark().color(); |
891 | fgColor = pal.color(cr: 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(msg: "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(polygon: 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(msg: "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 | */ |
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 | |
1186 | 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 | */ |
1200 | QSize QLCDNumber::sizeHint() const |
1201 | { |
1202 | return QSize(10 + 9 * (digitCount() + (smallDecimalPoint() ? 0 : 1)), 23); |
1203 | } |
1204 | |
1205 | /*! \reimp */ |
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 | |