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