File indexing completed on 2024-04-21 05:50:08
0001 /* 0002 SPDX-FileCopyrightText: 2001-2013 Evan Teran <evan.teran@gmail.com> 0003 SPDX-FileCopyrightText: 1996-2000 Bernd Johannes Wuebben <wuebben@kde.org> 0004 SPDX-FileCopyrightText: 2022 Harald Sitter <sitter@kde.org> 0005 0006 SPDX-License-Identifier: GPL-2.0-or-later 0007 */ 0008 0009 #include "kcalcdisplay.h" 0010 0011 #include <QApplication> 0012 #include <QClipboard> 0013 #include <QMouseEvent> 0014 #include <QPainter> 0015 #include <QStyle> 0016 #include <QStyleOption> 0017 #include <QTimer> 0018 0019 #include <KNotification> 0020 0021 #include "kcalc_core.h" 0022 #include "kcalc_settings.h" 0023 0024 //------------------------------------------------------------------------------ 0025 // Name: KCalcDisplay 0026 // Desc: constructor 0027 //------------------------------------------------------------------------------ 0028 KCalcDisplay::KCalcDisplay(QWidget *parent) 0029 : QFrame(parent) 0030 , beep_(false) 0031 , groupdigits_(true) 0032 , twoscomplement_(true) 0033 , button_(0) 0034 , lit_(false) 0035 , num_base_(NB_DECIMAL) 0036 , precision_(9) 0037 , fixed_precision_(-1) 0038 , display_amount_(0) 0039 , history_index_(0) 0040 , selection_timer_(new QTimer(this)) 0041 { 0042 setAccessibleDescription(i18nc("@label accessibility description of the calculation result display", "Result Display")); 0043 setFocusPolicy(Qt::StrongFocus); 0044 0045 setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed)); 0046 0047 setBackgroundRole(QPalette::Base); 0048 setForegroundRole(QPalette::Text); 0049 setFrameStyle(QFrame::NoFrame); // set in kalc.ui 0050 setContentsMargins(6, 0, 6, 6); 0051 0052 KNumber::setDefaultFloatOutput(true); 0053 KNumber::setDefaultFractionalInput(true); 0054 0055 connect(this, &KCalcDisplay::clicked, this, &KCalcDisplay::slotDisplaySelected); 0056 connect(selection_timer_, &QTimer::timeout, this, &KCalcDisplay::slotSelectionTimedOut); 0057 0058 sendEvent(EventReset); 0059 } 0060 0061 //------------------------------------------------------------------------------ 0062 // Name: ~KCalcDisplay 0063 // Desc: destructor 0064 //------------------------------------------------------------------------------ 0065 KCalcDisplay::~KCalcDisplay() = default; 0066 0067 //------------------------------------------------------------------------------ 0068 // Name: changeSettings 0069 // Desc: 0070 //------------------------------------------------------------------------------ 0071 void KCalcDisplay::changeSettings() 0072 { 0073 QPalette pal = palette(); 0074 0075 pal.setColor(QPalette::Text, KCalcSettings::foreColor()); 0076 pal.setColor(QPalette::Base, KCalcSettings::backColor()); 0077 0078 setPalette(pal); 0079 0080 setFont(KCalcSettings::displayFont()); 0081 0082 setPrecision(KCalcSettings::precision()); 0083 0084 if (!KCalcSettings::fixed()) { 0085 setFixedPrecision(-1); 0086 } else { 0087 setFixedPrecision(KCalcSettings::fixedPrecision()); 0088 } 0089 0090 setBeep(KCalcSettings::beep()); 0091 setGroupDigits(KCalcSettings::groupDigits()); 0092 setTwosComplement(KCalcSettings::twosComplement()); 0093 setBinaryGrouping(KCalcSettings::binaryGrouping()); 0094 setOctalGrouping(KCalcSettings::octalGrouping()); 0095 setHexadecimalGrouping(KCalcSettings::hexadecimalGrouping()); 0096 updateDisplay(); 0097 } 0098 0099 //------------------------------------------------------------------------------ 0100 // Name: 0101 // Desc: 0102 //------------------------------------------------------------------------------ 0103 void KCalcDisplay::updateFromCore(const CalcEngine &core, bool store_result_in_history) 0104 { 0105 bool tmp_error; 0106 const KNumber &output = core.lastOutput(tmp_error); 0107 0108 #if 0 0109 // TODO: do we really need explicit error tracking? 0110 // isn't the type of the KNumber good enough? 0111 // I think it is and that this error tracking is cruft 0112 // left over from a LONG time ago... 0113 if(output.type() == KNumber::TYPE_ERROR) { 0114 #else 0115 if (tmp_error) { 0116 #endif 0117 sendEvent(EventError); 0118 } 0119 0120 if (setAmount(output) && store_result_in_history && (output != KNumber::Zero)) { 0121 // add this latest value to our history 0122 history_list_.insert(history_list_.begin(), output); 0123 history_index_ = 0; 0124 } 0125 } 0126 0127 //------------------------------------------------------------------------------ 0128 // Name: enterDigit 0129 // Desc: 0130 //------------------------------------------------------------------------------ 0131 void KCalcDisplay::enterDigit(int data) 0132 { 0133 switch (data) { 0134 case 0: 0135 newCharacter(QLatin1Char('0')); 0136 break; 0137 case 1: 0138 newCharacter(QLatin1Char('1')); 0139 break; 0140 case 2: 0141 newCharacter(QLatin1Char('2')); 0142 break; 0143 case 3: 0144 newCharacter(QLatin1Char('3')); 0145 break; 0146 case 4: 0147 newCharacter(QLatin1Char('4')); 0148 break; 0149 case 5: 0150 newCharacter(QLatin1Char('5')); 0151 break; 0152 case 6: 0153 newCharacter(QLatin1Char('6')); 0154 break; 0155 case 7: 0156 newCharacter(QLatin1Char('7')); 0157 break; 0158 case 8: 0159 newCharacter(QLatin1Char('8')); 0160 break; 0161 case 9: 0162 newCharacter(QLatin1Char('9')); 0163 break; 0164 case 0xa: 0165 newCharacter(QLatin1Char('A')); 0166 break; 0167 case 0xb: 0168 newCharacter(QLatin1Char('B')); 0169 break; 0170 case 0xc: 0171 newCharacter(QLatin1Char('C')); 0172 break; 0173 case 0xd: 0174 newCharacter(QLatin1Char('D')); 0175 break; 0176 case 0xe: 0177 newCharacter(QLatin1Char('E')); 0178 break; 0179 case 0xf: 0180 newCharacter(QLatin1Char('F')); 0181 break; 0182 default: 0183 Q_ASSERT(0); 0184 break; 0185 } 0186 } 0187 0188 //------------------------------------------------------------------------------ 0189 // Name: slotHistoryForward 0190 // Desc: 0191 //------------------------------------------------------------------------------ 0192 void KCalcDisplay::slotHistoryForward() 0193 { 0194 if (history_list_.empty()) { 0195 return; 0196 } 0197 0198 if (history_index_ <= 0) { 0199 return; 0200 } 0201 0202 history_index_--; 0203 setAmount(history_list_[history_index_]); 0204 } 0205 0206 //------------------------------------------------------------------------------ 0207 // Name: slotHistoryBack 0208 // Desc: 0209 //------------------------------------------------------------------------------ 0210 void KCalcDisplay::slotHistoryBack() 0211 { 0212 if (history_list_.empty()) { 0213 return; 0214 } 0215 0216 if (history_index_ >= history_list_.size()) { 0217 return; 0218 } 0219 0220 setAmount(history_list_[history_index_]); 0221 history_index_++; 0222 } 0223 0224 //------------------------------------------------------------------------------ 0225 // Name: sendEvent 0226 // Desc: 0227 //------------------------------------------------------------------------------ 0228 bool KCalcDisplay::sendEvent(Event event) 0229 { 0230 switch (event) { 0231 case EventClear: 0232 case EventReset: 0233 display_amount_ = KNumber::Zero; 0234 str_int_ = QStringLiteral("0"); 0235 str_int_exp_.clear(); 0236 0237 eestate_ = false; 0238 period_ = false; 0239 neg_sign_ = false; 0240 0241 updateDisplay(); 0242 0243 return true; 0244 0245 case EventChangeSign: 0246 return changeSign(); 0247 0248 case EventError: 0249 updateDisplay(); 0250 return true; 0251 0252 default: 0253 return false; 0254 } 0255 } 0256 0257 //------------------------------------------------------------------------------ 0258 // Name: slotCut 0259 // Desc: 0260 //------------------------------------------------------------------------------ 0261 void KCalcDisplay::slotCut() 0262 { 0263 slotCopy(); 0264 sendEvent(EventReset); 0265 } 0266 0267 //------------------------------------------------------------------------------ 0268 // Name: slotCopy 0269 // Desc: 0270 //------------------------------------------------------------------------------ 0271 void KCalcDisplay::slotCopy() 0272 { 0273 QString txt = text_; 0274 0275 switch (num_base_) { 0276 case NB_HEX: 0277 txt.prepend(QLatin1String("0x")); 0278 txt.remove(QLatin1Char(' ')); 0279 break; 0280 case NB_BINARY: 0281 txt.prepend(QLatin1String("0b")); 0282 txt.remove(QLatin1Char(' ')); 0283 break; 0284 case NB_OCTAL: 0285 txt.prepend(QLatin1String("0")); 0286 txt.remove(QLatin1Char(' ')); 0287 break; 0288 case NB_DECIMAL: 0289 txt.remove(QLocale().groupSeparator()); 0290 break; 0291 } 0292 0293 QApplication::clipboard()->setText(txt, QClipboard::Clipboard); 0294 QApplication::clipboard()->setText(txt, QClipboard::Selection); 0295 } 0296 0297 //------------------------------------------------------------------------------ 0298 // Name: slotDisplaySelected 0299 // Desc: 0300 //------------------------------------------------------------------------------ 0301 void KCalcDisplay::slotDisplaySelected() 0302 { 0303 if (button_ == Qt::LeftButton) { 0304 if (lit_) { 0305 slotCopy(); 0306 selection_timer_->start(100); 0307 } else { 0308 selection_timer_->stop(); 0309 } 0310 0311 invertColors(); 0312 } 0313 } 0314 0315 //------------------------------------------------------------------------------ 0316 // Name: slotSelectionTimedOut 0317 // Desc: 0318 //------------------------------------------------------------------------------ 0319 void KCalcDisplay::slotSelectionTimedOut() 0320 { 0321 lit_ = false; 0322 invertColors(); 0323 selection_timer_->stop(); 0324 } 0325 0326 //------------------------------------------------------------------------------ 0327 // Name: invertColors 0328 // Desc: 0329 //------------------------------------------------------------------------------ 0330 void KCalcDisplay::invertColors() 0331 { 0332 QPalette tmp_palette = palette(); 0333 tmp_palette.setColor(QPalette::Base, palette().color(QPalette::Text)); 0334 tmp_palette.setColor(QPalette::Text, palette().color(QPalette::Base)); 0335 setPalette(tmp_palette); 0336 } 0337 0338 //------------------------------------------------------------------------------ 0339 // Name: mousePressEvent 0340 // Desc: 0341 //------------------------------------------------------------------------------ 0342 void KCalcDisplay::mousePressEvent(QMouseEvent *e) 0343 { 0344 if (e->button() == Qt::LeftButton) { 0345 lit_ = !lit_; 0346 button_ = Qt::LeftButton; 0347 } else { 0348 button_ = Qt::MiddleButton; 0349 } 0350 0351 Q_EMIT clicked(); 0352 } 0353 0354 //------------------------------------------------------------------------------ 0355 // Name: setPrecision 0356 // Desc: 0357 //------------------------------------------------------------------------------ 0358 void KCalcDisplay::setPrecision(int precision) 0359 { 0360 precision_ = precision; 0361 } 0362 0363 //------------------------------------------------------------------------------ 0364 // Name: setFixedPrecision 0365 // Desc: 0366 //------------------------------------------------------------------------------ 0367 void KCalcDisplay::setFixedPrecision(int precision) 0368 { 0369 if (fixed_precision_ > precision_) { 0370 fixed_precision_ = -1; 0371 } else { 0372 fixed_precision_ = precision; 0373 } 0374 } 0375 0376 //------------------------------------------------------------------------------ 0377 // Name: setBeep 0378 // Desc: 0379 //------------------------------------------------------------------------------ 0380 void KCalcDisplay::setBeep(bool flag) 0381 { 0382 beep_ = flag; 0383 } 0384 0385 //------------------------------------------------------------------------------ 0386 // Name: setGroupDigits 0387 // Desc: 0388 //------------------------------------------------------------------------------ 0389 void KCalcDisplay::setGroupDigits(bool flag) 0390 { 0391 groupdigits_ = flag; 0392 } 0393 0394 //------------------------------------------------------------------------------ 0395 // Name: setTwosComplement 0396 // Desc: 0397 //------------------------------------------------------------------------------ 0398 void KCalcDisplay::setTwosComplement(bool flag) 0399 { 0400 twoscomplement_ = flag; 0401 } 0402 0403 //------------------------------------------------------------------------------ 0404 // Name: setBinaryGrouping 0405 // Desc: 0406 //------------------------------------------------------------------------------ 0407 void KCalcDisplay::setBinaryGrouping(int digits) 0408 { 0409 binaryGrouping_ = digits; 0410 } 0411 0412 //------------------------------------------------------------------------------ 0413 // Name: setOctalGrouping 0414 // Desc: 0415 //------------------------------------------------------------------------------ 0416 void KCalcDisplay::setOctalGrouping(int digits) 0417 { 0418 octalGrouping_ = digits; 0419 } 0420 0421 //------------------------------------------------------------------------------ 0422 // Name: setHexadecimalGrouping 0423 // Desc: 0424 //------------------------------------------------------------------------------ 0425 void KCalcDisplay::setHexadecimalGrouping(int digits) 0426 { 0427 hexadecimalGrouping_ = digits; 0428 } 0429 0430 //------------------------------------------------------------------------------ 0431 // Name: getAmount 0432 // Desc: 0433 //------------------------------------------------------------------------------ 0434 const KNumber &KCalcDisplay::getAmount() const 0435 { 0436 return display_amount_; 0437 } 0438 0439 //------------------------------------------------------------------------------ 0440 // Name: setAmount 0441 // Desc: 0442 //------------------------------------------------------------------------------ 0443 bool KCalcDisplay::setAmount(const KNumber &new_amount) 0444 { 0445 QString display_str; 0446 0447 str_int_ = QStringLiteral("0"); 0448 str_int_exp_.clear(); 0449 period_ = false; 0450 neg_sign_ = false; 0451 eestate_ = false; 0452 0453 if ((num_base_ != NB_DECIMAL) && (new_amount.type() != KNumber::TYPE_ERROR)) { 0454 display_amount_ = new_amount.integerPart(); 0455 0456 if (twoscomplement_) { 0457 // treat number as 64-bit unsigned 0458 const quint64 tmp_workaround = display_amount_.toUint64(); 0459 display_str = QString::number(tmp_workaround, num_base_).toUpper(); 0460 } else { 0461 // QString::number treats non-decimal as unsigned 0462 qint64 tmp_workaround = display_amount_.toInt64(); 0463 const bool neg = tmp_workaround < 0; 0464 if (neg) { 0465 tmp_workaround = qAbs(tmp_workaround); 0466 } 0467 0468 display_str = QString::number(tmp_workaround, num_base_).toUpper(); 0469 if (neg) { 0470 display_str.prepend(QLocale().negativeSign()); 0471 } 0472 } 0473 } else { 0474 // num_base_ == NB_DECIMAL || new_amount.type() == KNumber::TYPE_ERROR 0475 display_amount_ = new_amount; 0476 display_str = display_amount_.toQString(KCalcSettings::precision(), fixed_precision_); 0477 } 0478 0479 setText(display_str); 0480 Q_EMIT changedAmount(display_amount_); 0481 return true; 0482 } 0483 0484 //------------------------------------------------------------------------------ 0485 // Name: setText 0486 // Desc: 0487 //------------------------------------------------------------------------------ 0488 void KCalcDisplay::setText(const QString &string) 0489 { 0490 // note that "C" locale is being used internally 0491 text_ = string; 0492 0493 // don't mess with special numbers 0494 const bool special = (string.contains(QLatin1String("nan")) || string.contains(QLatin1String("inf"))); 0495 0496 const bool error = (string.contains(QLatin1String("error")) || string.contains(QLatin1String("malformed"))); 0497 0498 // The decimal mode needs special treatment for two reasons, because: a) it uses KGlobal::locale() to get a localized 0499 // format and b) it has possible numbers after the decimal place. Neither applies to Binary, Hexadecimal or Octal. 0500 0501 if ((groupdigits_ || num_base_ == NB_DECIMAL) && !special && !error) { 0502 switch (num_base_) { 0503 case NB_DECIMAL: 0504 text_ = formatDecimalNumber(text_); 0505 break; 0506 0507 case NB_BINARY: 0508 text_ = groupDigits(text_, binaryGrouping_); 0509 break; 0510 0511 case NB_OCTAL: 0512 text_ = groupDigits(text_, octalGrouping_); 0513 break; 0514 0515 case NB_HEX: 0516 text_ = groupDigits(text_, hexadecimalGrouping_); 0517 break; 0518 } 0519 } else if (special) { 0520 #if 0 0521 // TODO: enable this code, it replaces the "inf" with an actual infinity 0522 // symbol, but what should be put into the clip board when they copy? 0523 if(string.contains(QLatin1String("inf"))) { 0524 text_.replace("inf", QChar(0x221e)); 0525 } 0526 #endif 0527 } 0528 0529 update(); 0530 setAccessibleName(text_); // "Labels should be represented by only QAccessibleInterface and return their text as name" 0531 Q_EMIT changedText(text_); 0532 } 0533 0534 //------------------------------------------------------------------------------ 0535 // Name: setFont 0536 // Desc: Set the base font and recalculate the font size to better fit 0537 //------------------------------------------------------------------------------ 0538 void KCalcDisplay::setFont(const QFont &font) 0539 { 0540 // Overwrite current baseFont 0541 baseFont_ = font; 0542 updateFont(); 0543 } 0544 0545 //------------------------------------------------------------------------------ 0546 // Name: updateFont 0547 // Desc: Update font using baseFont to better fit 0548 //------------------------------------------------------------------------------ 0549 void KCalcDisplay::updateFont() 0550 { 0551 // Make a working copy of the font 0552 QFont* newFont = new QFont(baseFont()); 0553 0554 // Calculate ideal font size 0555 // constants arbitrarily chosen, adjust/increase if scaling issues arise 0556 newFont->setPointSizeF(qMax(double(baseFont().pointSize()), qMin(contentsRect().height() / 4.5, contentsRect().width() / 24.6))); 0557 0558 // Apply font 0559 QFrame::setFont(*newFont); 0560 0561 // Free the memory 0562 delete newFont; 0563 } 0564 0565 //------------------------------------------------------------------------------ 0566 // Name: baseFont 0567 // Desc: 0568 //------------------------------------------------------------------------------ 0569 const QFont& KCalcDisplay::baseFont() const 0570 { 0571 return baseFont_; 0572 } 0573 0574 //------------------------------------------------------------------------------ 0575 // Name: formatDecimalNumber 0576 // Desc: Convert decimal number to locale-dependent format. 0577 // We cannot use QLocale::formatNumber(), because the 0578 // precision is limited to "double". 0579 //------------------------------------------------------------------------------ 0580 QString KCalcDisplay::formatDecimalNumber(QString string) 0581 { 0582 QLocale locale; 0583 0584 string.replace(QLatin1Char('.'), locale.decimalPoint()); 0585 0586 if (groupdigits_ && !(locale.numberOptions() & QLocale::OmitGroupSeparator)) { 0587 // find position after last digit 0588 int pos = string.indexOf(locale.decimalPoint()); 0589 if (pos < 0) { 0590 // do not group digits after the exponent part 0591 const int expPos = string.indexOf(QLatin1Char('e')); 0592 if (expPos > 0) { 0593 pos = expPos; 0594 } else { 0595 pos = string.length(); 0596 } 0597 } 0598 0599 // find first digit to not group leading spaces or signs 0600 int firstDigitPos = 0; 0601 for (int i = 0, total = string.length(); i < total; ++i) { 0602 if (string.at(i).isDigit()) { 0603 firstDigitPos = i; 0604 break; 0605 } 0606 } 0607 0608 const auto groupSeparator = locale.groupSeparator(); 0609 const int groupSize = 3; 0610 0611 string.reserve(string.length() + (pos - 1) / groupSize); 0612 while ((pos -= groupSize) > firstDigitPos) { 0613 string.insert(pos, groupSeparator); 0614 } 0615 } 0616 0617 string.replace(QLatin1Char('-'), locale.negativeSign()); 0618 string.replace(QLatin1Char('+'), locale.positiveSign()); 0619 0620 // Digits in unicode is encoded in contiguous range and with the digit zero as the first. 0621 // To convert digits to other locales, 0622 // just add the digit value and the leading zero's code point. 0623 // ref: Unicode15 chapter 4.6 Numeric Value https://www.unicode.org/versions/Unicode15.0.0/ch04.pdf 0624 0625 // QLocale switched return type of many functions from QChar to QString, 0626 // because UTF-16 may need surrogate pairs to represent these fields. 0627 // We only need digits, thus we only need the first QChar with Qt>=6. 0628 0629 auto zero = locale.zeroDigit().at(0).unicode(); 0630 0631 for (auto &i : string) { 0632 if (i.isDigit()) { 0633 i = QChar(zero + i.digitValue()); 0634 } 0635 } 0636 0637 return string; 0638 } 0639 0640 //------------------------------------------------------------------------------ 0641 // Name: groupDigits 0642 // Desc: 0643 //------------------------------------------------------------------------------ 0644 QString KCalcDisplay::groupDigits(const QString &displayString, int numDigits) 0645 { 0646 QString tmpDisplayString; 0647 const int stringLength = displayString.length(); 0648 0649 for (int i = stringLength; i > 0; i--) { 0650 if (i % numDigits == 0 && i != stringLength) { 0651 tmpDisplayString = tmpDisplayString + QLatin1Char(' '); 0652 } 0653 0654 tmpDisplayString = tmpDisplayString + displayString[stringLength - i]; 0655 } 0656 0657 return tmpDisplayString; 0658 } 0659 0660 //------------------------------------------------------------------------------ 0661 // Name: text 0662 // Desc: 0663 //------------------------------------------------------------------------------ 0664 QString KCalcDisplay::text() const 0665 { 0666 return text_; 0667 } 0668 0669 //------------------------------------------------------------------------------ 0670 // Name: setBase 0671 // Desc: change representation of display to new base (i.e. binary, decimal, 0672 // octal, hexadecimal). The amount being displayed is changed to this 0673 // base, but for now this amount can not be modified anymore (like 0674 // being set with "setAmount"). Return value is the new base. 0675 //------------------------------------------------------------------------------ 0676 int KCalcDisplay::setBase(NumBase new_base) 0677 { 0678 switch (new_base) { 0679 case NB_HEX: 0680 num_base_ = NB_HEX; 0681 period_ = false; 0682 break; 0683 case NB_DECIMAL: 0684 num_base_ = NB_DECIMAL; 0685 break; 0686 case NB_OCTAL: 0687 num_base_ = NB_OCTAL; 0688 period_ = false; 0689 break; 0690 case NB_BINARY: 0691 num_base_ = NB_BINARY; 0692 period_ = false; 0693 break; 0694 default: 0695 Q_ASSERT(0); 0696 } 0697 0698 // reset amount 0699 setAmount(display_amount_); 0700 return num_base_; 0701 } 0702 0703 //------------------------------------------------------------------------------ 0704 // Name: setStatusText 0705 // Desc: 0706 //------------------------------------------------------------------------------ 0707 void KCalcDisplay::setStatusText(int i, const QString &text) 0708 { 0709 if (i < NUM_STATUS_TEXT) { 0710 str_status_[i] = text; 0711 } 0712 0713 update(); 0714 } 0715 0716 //------------------------------------------------------------------------------ 0717 // Name: updateDisplay 0718 // Desc: 0719 //------------------------------------------------------------------------------ 0720 void KCalcDisplay::updateDisplay() 0721 { 0722 // Put sign in front. 0723 QString tmp_string; 0724 if (neg_sign_) { 0725 tmp_string = QLatin1Char('-') + str_int_; 0726 } else { 0727 tmp_string = str_int_; 0728 } 0729 0730 bool ok; 0731 0732 switch (num_base_) { 0733 case NB_BINARY: 0734 Q_ASSERT(!period_ && !eestate_); 0735 setText(tmp_string); 0736 display_amount_ = KNumber(str_int_.toULongLong(&ok, 2)); 0737 if (neg_sign_) { 0738 display_amount_ = -display_amount_; 0739 } 0740 break; 0741 0742 case NB_OCTAL: 0743 Q_ASSERT(!period_ && !eestate_); 0744 setText(tmp_string); 0745 display_amount_ = KNumber(str_int_.toULongLong(&ok, 8)); 0746 if (neg_sign_) { 0747 display_amount_ = -display_amount_; 0748 } 0749 break; 0750 0751 case NB_HEX: 0752 Q_ASSERT(!period_ && !eestate_); 0753 setText(tmp_string); 0754 display_amount_ = KNumber(str_int_.toULongLong(&ok, 16)); 0755 if (neg_sign_) { 0756 display_amount_ = -display_amount_; 0757 } 0758 break; 0759 0760 case NB_DECIMAL: 0761 if (!eestate_) { 0762 setText(tmp_string); 0763 display_amount_ = KNumber(tmp_string); 0764 } else { 0765 if (str_int_exp_.isNull()) { 0766 // add 'e0' to display but not to conversion 0767 display_amount_ = KNumber(tmp_string); 0768 setText(tmp_string + QLatin1String("e0")); 0769 } else { 0770 tmp_string += QLatin1Char('e') + str_int_exp_; 0771 setText(tmp_string); 0772 display_amount_ = KNumber(tmp_string); 0773 } 0774 } 0775 break; 0776 0777 default: 0778 Q_ASSERT(0); 0779 } 0780 0781 Q_EMIT changedAmount(display_amount_); 0782 } 0783 0784 //------------------------------------------------------------------------------ 0785 // Name: newCharacter 0786 // Desc: 0787 //------------------------------------------------------------------------------ 0788 void KCalcDisplay::newCharacter(const QChar new_char) 0789 { 0790 // test if character is valid 0791 switch (new_char.toLatin1()) { 0792 case 'e': 0793 // EE can be set only once and in decimal mode 0794 if (num_base_ != NB_DECIMAL || eestate_) { 0795 if (beep_) { 0796 KNotification::beep(); 0797 } 0798 return; 0799 } 0800 eestate_ = true; 0801 break; 0802 0803 case 'F': 0804 case 'E': 0805 case 'D': 0806 case 'C': 0807 case 'B': 0808 case 'A': 0809 if (num_base_ == NB_DECIMAL) { 0810 if (beep_) { 0811 KNotification::beep(); 0812 } 0813 return; 0814 } 0815 Q_FALLTHROUGH(); 0816 case '9': 0817 case '8': 0818 if (num_base_ == NB_OCTAL) { 0819 if (beep_) { 0820 KNotification::beep(); 0821 } 0822 return; 0823 } 0824 Q_FALLTHROUGH(); 0825 case '7': 0826 case '6': 0827 case '5': 0828 case '4': 0829 case '3': 0830 case '2': 0831 if (num_base_ == NB_BINARY) { 0832 if (beep_) { 0833 KNotification::beep(); 0834 } 0835 return; 0836 } 0837 Q_FALLTHROUGH(); 0838 case '1': 0839 case '0': 0840 break; 0841 0842 default: 0843 if (new_char == QLocale().decimalPoint()) { 0844 // Period can be set only once and only in decimal 0845 // mode, also not in EE-mode 0846 if (num_base_ != NB_DECIMAL || period_ || eestate_) { 0847 if (beep_) { 0848 KNotification::beep(); 0849 } 0850 return; 0851 } 0852 period_ = true; 0853 } else { 0854 if (beep_) { 0855 KNotification::beep(); 0856 } 0857 return; 0858 } 0859 } 0860 0861 // change exponent or mantissa 0862 if (eestate_) { 0863 // ignore '.' before 'e'. turn e.g. '123.e' into '123e' 0864 if (new_char == QLatin1Char('e') && str_int_.endsWith(QLocale().decimalPoint())) { 0865 str_int_.chop(1); 0866 period_ = false; 0867 } 0868 0869 // 'e' only starts ee_mode, leaves strings unchanged 0870 // do not add '0' if at start of exp 0871 if (new_char != QLatin1Char('e') && !(str_int_exp_.isNull() && new_char == QLatin1Char('0'))) { 0872 str_int_exp_.append(new_char); 0873 } 0874 } else { 0875 // handle first character 0876 if (str_int_ == QLatin1Char('0')) { 0877 switch (new_char.toLatin1()) { 0878 case 'e': 0879 // display "0e" not just "e" 0880 // "0e" does not make sense either, but... 0881 str_int_.append(new_char); 0882 break; 0883 default: 0884 if (new_char == QLocale().decimalPoint()) { 0885 // display "0." not just "." 0886 str_int_.append(new_char); 0887 } else { 0888 // no leading '0's 0889 str_int_[0] = new_char; 0890 } 0891 } 0892 } else { 0893 str_int_.append(new_char); 0894 } 0895 } 0896 0897 updateDisplay(); 0898 } 0899 0900 //------------------------------------------------------------------------------ 0901 // Name: deleteLastDigit 0902 // Desc: 0903 //------------------------------------------------------------------------------ 0904 void KCalcDisplay::deleteLastDigit() 0905 { 0906 // Only partially implemented !! 0907 if (eestate_) { 0908 if (str_int_exp_.isNull()) { 0909 eestate_ = false; 0910 } else { 0911 const int length = str_int_exp_.length(); 0912 if (length > 1) { 0913 str_int_exp_.chop(1); 0914 } else { 0915 str_int_exp_ = QLatin1String((const char *)nullptr); 0916 } 0917 } 0918 } else { 0919 const int length = str_int_.length(); 0920 if (length > 1) { 0921 if (str_int_[length - 1] == QLocale().decimalPoint()) { 0922 period_ = false; 0923 } 0924 str_int_.chop(1); 0925 } else { 0926 Q_ASSERT(!period_); 0927 str_int_[0] = QLatin1Char('0'); 0928 } 0929 } 0930 0931 updateDisplay(); 0932 } 0933 0934 //------------------------------------------------------------------------------ 0935 // Name: changeSign 0936 // Desc: change Sign of display. Problem: Only possible here, when in input 0937 // mode. Otherwise return 'false' so that the kcalc_core can handle 0938 // things. 0939 //------------------------------------------------------------------------------ 0940 bool KCalcDisplay::changeSign() 0941 { 0942 // stupid way, to see if in input_mode or display_mode 0943 if (str_int_ == QLatin1Char('0')) { 0944 return false; 0945 } 0946 0947 if (eestate_) { 0948 if (!str_int_exp_.isNull()) { 0949 if (str_int_exp_[0] != QLatin1Char('-')) { 0950 str_int_exp_.prepend(QLatin1Char('-')); 0951 } else { 0952 str_int_exp_.remove(QLatin1Char('-')); 0953 } 0954 } 0955 } else { 0956 neg_sign_ = !neg_sign_; 0957 } 0958 0959 updateDisplay(); 0960 return true; 0961 } 0962 0963 //------------------------------------------------------------------------------ 0964 // Name: initStyleOption 0965 // Desc: 0966 //------------------------------------------------------------------------------ 0967 void KCalcDisplay::initStyleOption(QStyleOptionFrame *option) const 0968 { 0969 if (!option) { 0970 return; 0971 } 0972 0973 option->initFrom(this); 0974 option->state &= ~QStyle::State_HasFocus; // don't draw focus highlight 0975 0976 if (frameShadow() == QFrame::Sunken) { 0977 option->state |= QStyle::State_Sunken; 0978 } else if (frameShadow() == QFrame::Raised) { 0979 option->state |= QStyle::State_Raised; 0980 } 0981 0982 option->lineWidth = style()->pixelMetric(QStyle::PM_DefaultFrameWidth, option, this); 0983 option->midLineWidth = 0; 0984 } 0985 0986 //------------------------------------------------------------------------------ 0987 // Name: paintEvent 0988 // Desc: 0989 //------------------------------------------------------------------------------ 0990 void KCalcDisplay::paintEvent(QPaintEvent *) 0991 { 0992 QPainter painter(this); 0993 0994 QStyleOptionFrame option; 0995 initStyleOption(&option); 0996 0997 style()->drawPrimitive(QStyle::PE_PanelLineEdit, &option, &painter, this); 0998 0999 // draw display text 1000 const int margin = style()->pixelMetric(QStyle::PM_DefaultFrameWidth, nullptr, nullptr); 1001 QRect cr = contentsRect(); 1002 cr.adjust(margin * 2, 0, -margin * 2, 0); // provide a margin 1003 1004 const int align = QStyle::visualAlignment(layoutDirection(), Qt::AlignRight | Qt::AlignVCenter); 1005 painter.drawText(cr, align | Qt::TextSingleLine, text_); 1006 1007 // draw the status texts using half of the normal 1008 // font size but not smaller than 7pt 1009 QFont fnt(font()); 1010 fnt.setPointSizeF(qMax((fnt.pointSize() / 2.0), 7.0)); 1011 painter.setFont(fnt); 1012 1013 QFontMetrics fm(fnt); 1014 const uint w = fm.boundingRect(QStringLiteral("________")).width(); 1015 const uint h = fm.height(); 1016 1017 for (int n = 0; n < NUM_STATUS_TEXT; ++n) { 1018 painter.drawText(5 + n * w, h, str_status_[n]); 1019 } 1020 } 1021 1022 //------------------------------------------------------------------------------ 1023 // Name: resizeEvent 1024 // Desc: resize display and adjust font size 1025 //------------------------------------------------------------------------------ 1026 void KCalcDisplay::resizeEvent(QResizeEvent* event) 1027 { 1028 QFrame::resizeEvent(event); 1029 1030 // Update font size 1031 updateFont(); 1032 } 1033 1034 //------------------------------------------------------------------------------ 1035 // Name: sizeHint 1036 // Desc: 1037 //------------------------------------------------------------------------------ 1038 QSize KCalcDisplay::sizeHint() const 1039 { 1040 // font metrics of base font 1041 const QFontMetrics fmBase(baseFont()); 1042 1043 // basic size 1044 QSize sz = fmBase.size(0, QStringLiteral("M")); 1045 1046 // expanded by 3/4 font height to make room for the status texts 1047 QFont fnt(baseFont()); 1048 fnt.setPointSize(qMax(((fnt.pointSize() * 3) / 4), 7)); 1049 1050 const QFontMetrics fm(fnt); 1051 sz.setHeight(sz.height() + fm.height()); 1052 1053 QStyleOptionFrame option; 1054 initStyleOption(&option); 1055 1056 return (style()->sizeFromContents(QStyle::CT_LineEdit, &option, sz, this)); 1057 } 1058 1059 #include "moc_kcalcdisplay.cpp"