OSDN Git Service

generic: replace obsolete Qt::MidButton with Qt::MiddleButton
[kde/kde-extraapps.git] / kcalc / kcalcdisplay.cpp
1 /*
2 Copyright (C) 2001 - 2013 Evan Teran
3                           evan.teran@gmail.com
4
5 Copyright (C) 1996 - 2000 Bernd Johannes Wuebben
6                           wuebben@kde.org
7
8 This program is free software; you can redistribute it and/or
9 modify it under the terms of the GNU General Public License as
10 published by the Free Software Foundation; either version 2 of 
11 the License, or (at your option) any later version.
12
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with this program.  If not, see <http://www.gnu.org/licenses/>.
20 */
21
22 #include "kcalcdisplay.h"
23
24 #include <QClipboard>
25 #include <QtGui/qevent.h>
26 #include <QPainter>
27 #include <QStyle>
28 #include <QStyleOption>
29 #include <QTimer>
30
31 #include <kglobal.h>
32 #include <klocale.h>
33 #include <knotification.h>
34
35 #include "kcalc_core.h"
36 #include "kcalc_settings.h"
37
38 #include "moc_kcalcdisplay.cpp"
39
40 //------------------------------------------------------------------------------
41 // Name: KCalcDisplay
42 // Desc: constructor
43 //------------------------------------------------------------------------------
44 KCalcDisplay::KCalcDisplay(QWidget *parent) : QFrame(parent), beep_(false), 
45                 groupdigits_(true), twoscomplement_(true), button_(0), lit_(false),
46                 num_base_(NB_DECIMAL), precision_(9), fixed_precision_(-1), display_amount_(0), 
47                 history_index_(0), selection_timer_(new QTimer(this)) {
48                 
49         setFocusPolicy(Qt::StrongFocus);
50
51         setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed));
52
53         setBackgroundRole(QPalette::Base);
54         setForegroundRole(QPalette::Text);
55         setFrameStyle(QFrame::StyledPanel | QFrame::Sunken);   // set in kalc.ui
56
57         KNumber::setDefaultFloatOutput(true);
58         KNumber::setDefaultFractionalInput(true);
59
60         connect(this, SIGNAL(clicked()), this, SLOT(slotDisplaySelected()));
61         connect(selection_timer_, SIGNAL(timeout()), this, SLOT(slotSelectionTimedOut()));
62
63         sendEvent(EventReset);
64 }
65
66 //------------------------------------------------------------------------------
67 // Name: ~KCalcDisplay
68 // Desc: destructor
69 //------------------------------------------------------------------------------
70 KCalcDisplay::~KCalcDisplay() {
71 }
72
73 //------------------------------------------------------------------------------
74 // Name: changeSettings
75 // Desc: 
76 //------------------------------------------------------------------------------
77 void KCalcDisplay::changeSettings() {
78         QPalette pal = palette();
79
80         pal.setColor(QPalette::Text, KCalcSettings::foreColor());
81         pal.setColor(QPalette::Base, KCalcSettings::backColor());
82
83         setPalette(pal);
84
85         setFont(KCalcSettings::displayFont());
86
87         setPrecision(KCalcSettings::precision());
88
89         if (!KCalcSettings::fixed()) {
90                 setFixedPrecision(-1);
91         } else {
92                 setFixedPrecision(KCalcSettings::fixedPrecision());
93         }
94
95         setBeep(KCalcSettings::beep());
96         setGroupDigits(KCalcSettings::groupDigits());
97         setTwosComplement(KCalcSettings::twosComplement());
98         setBinaryGrouping(KCalcSettings::binaryGrouping());
99         setOctalGrouping(KCalcSettings::octalGrouping());
100         setHexadecimalGrouping(KCalcSettings::hexadecimalGrouping());
101         updateDisplay();
102 }
103
104 //------------------------------------------------------------------------------
105 // Name: 
106 // Desc: 
107 //------------------------------------------------------------------------------
108 void KCalcDisplay::updateFromCore(const CalcEngine &core, bool store_result_in_history) {
109
110         bool tmp_error;
111         const KNumber &output = core.lastOutput(tmp_error);
112
113 #if 0
114         // TODO: do we really need explicit error tracking?
115         // isn't the type of the KNumber good enough?
116         // I think it is and that this error tracking is cruft
117         // left over from a LONG time ago...
118         if(output.type() == KNumber::TYPE_ERROR) {
119 #else
120         if (tmp_error) {
121 #endif
122                 sendEvent(EventError);
123         }
124         
125         if (setAmount(output) && store_result_in_history && (output != KNumber::Zero)) {
126                 // add this latest value to our history
127                 history_list_.insert(history_list_.begin(), output);
128                 history_index_ = 0;
129         }
130 }
131
132 //------------------------------------------------------------------------------
133 // Name: enterDigit
134 // Desc: 
135 //------------------------------------------------------------------------------
136 void KCalcDisplay::enterDigit(int data) {
137
138         switch (data) {
139         case 0: newCharacter(QLatin1Char('0')); break;
140         case 1: newCharacter(QLatin1Char('1')); break;
141         case 2: newCharacter(QLatin1Char('2')); break;
142         case 3: newCharacter(QLatin1Char('3')); break;
143         case 4: newCharacter(QLatin1Char('4')); break;
144         case 5: newCharacter(QLatin1Char('5')); break;
145         case 6: newCharacter(QLatin1Char('6')); break;
146         case 7: newCharacter(QLatin1Char('7')); break;
147         case 8: newCharacter(QLatin1Char('8')); break;
148         case 9: newCharacter(QLatin1Char('9')); break;
149         case 0xa: newCharacter(QLatin1Char('A')); break;
150         case 0xb: newCharacter(QLatin1Char('B')); break;
151         case 0xc: newCharacter(QLatin1Char('C')); break;
152         case 0xd: newCharacter(QLatin1Char('D')); break;
153         case 0xe: newCharacter(QLatin1Char('E')); break;
154         case 0xf: newCharacter(QLatin1Char('F')); break;
155         default:
156                 Q_ASSERT(0);
157                 break;
158         }
159 }
160
161 //------------------------------------------------------------------------------
162 // Name: slotHistoryForward
163 // Desc: 
164 //------------------------------------------------------------------------------
165 void KCalcDisplay::slotHistoryForward() {
166
167         if (history_list_.empty()) {
168                 return;
169         }
170
171         if (history_index_ <= 0) {
172                 return;
173         }
174
175         history_index_--;
176         setAmount(history_list_[history_index_]);
177 }
178
179 //------------------------------------------------------------------------------
180 // Name: slotHistoryBack
181 // Desc: 
182 //------------------------------------------------------------------------------
183 void KCalcDisplay::slotHistoryBack() {
184
185         if (history_list_.empty()) {
186                 return;
187         }
188
189         if (history_index_ >= history_list_.size()) {
190                 return;
191         }
192
193         setAmount(history_list_[history_index_]);
194         history_index_++;
195 }
196
197 //------------------------------------------------------------------------------
198 // Name: sendEvent
199 // Desc: 
200 //------------------------------------------------------------------------------
201 bool KCalcDisplay::sendEvent(Event event) {
202
203         switch (event) {
204         case EventClear:
205         case EventReset:
206                 display_amount_ = KNumber::Zero;
207                 str_int_ = QLatin1String("0");
208                 str_int_exp_.clear();
209
210                 eestate_ = false;
211                 period_ = false;
212                 neg_sign_ = false;
213
214                 updateDisplay();
215
216                 return true;
217         
218         case EventChangeSign:
219                 return changeSign();
220         
221         case EventError:
222                 updateDisplay();
223                 return true;
224                 
225         default:
226                 return false;
227         }
228 }
229
230 //------------------------------------------------------------------------------
231 // Name: slotCut
232 // Desc: 
233 //------------------------------------------------------------------------------
234 void KCalcDisplay::slotCut() {
235
236     slotCopy();
237     sendEvent(EventReset);
238 }
239
240 //------------------------------------------------------------------------------
241 // Name: slotCopy
242 // Desc: 
243 //------------------------------------------------------------------------------
244 void KCalcDisplay::slotCopy() {
245
246         QString txt = text_;
247
248         switch(num_base_) {
249         case NB_HEX:
250                 txt.prepend(QLatin1String("0x"));
251                 txt.remove(QLatin1Char(' '));
252                 break;
253         case NB_BINARY:
254                 txt.prepend(QLatin1String("0b"));
255                 txt.remove(QLatin1Char(' '));
256                 break;
257         case NB_OCTAL:
258                 txt.prepend(QLatin1String("0"));
259                 txt.remove(QLatin1Char(' '));
260                 break;
261         case NB_DECIMAL:
262                 break;
263         }
264
265         (QApplication::clipboard())->setText(txt, QClipboard::Clipboard);
266         (QApplication::clipboard())->setText(txt, QClipboard::Selection);
267 }
268
269 //------------------------------------------------------------------------------
270 // Name: slotPaste
271 // Desc: 
272 //------------------------------------------------------------------------------
273 void KCalcDisplay::slotPaste(bool bClipboard) {
274
275         QString tmp_str = (QApplication::clipboard())->text(bClipboard ? QClipboard::Clipboard : QClipboard::Selection);
276
277         if (tmp_str.isNull()) {
278                 if (beep_) {
279                         KNotification::beep();
280                 }
281                 return;
282         }
283
284         NumBase tmp_num_base = num_base_;
285
286         // fix up string
287         tmp_str = tmp_str.trimmed();
288         
289         if (groupdigits_) {
290                 tmp_str.remove(KGlobal::locale()->thousandsSeparator());
291         }
292         
293         tmp_str = tmp_str.toLower();
294
295         // determine base
296         if (tmp_str.startsWith(QLatin1String("0x"))) {
297                 tmp_num_base = NB_HEX;
298                 tmp_str.remove(0, 2);
299         } else if (tmp_str.startsWith(QLatin1String("0b"))) {
300                 tmp_num_base = NB_BINARY;
301                 tmp_str.remove(0, 2);
302         } else if (tmp_str.startsWith(QLatin1String("0"))) {
303                 // we don't want this to trigger on "0.xxxxxx" cases
304                 if(tmp_str.length() < 2 || QString(tmp_str[1]) != KNumber::decimalSeparator()) {
305                         tmp_num_base = NB_OCTAL;
306                         tmp_str.remove(0, 1);
307                 }
308         }
309
310         if (tmp_num_base != NB_DECIMAL) {
311                 bool was_ok;
312                 const qint64 tmp_result = tmp_str.toULongLong(&was_ok, tmp_num_base);
313
314                 if (!was_ok) {
315                         setAmount(KNumber::NaN);
316                         if (beep_) {
317                                 KNotification::beep();
318                         }
319                         return;
320                 }
321                 setAmount(KNumber(tmp_result));
322         } else {
323                 setAmount(KNumber(tmp_str));
324                 if (beep_ && display_amount_ == KNumber::NaN) {
325                         KNotification::beep();
326                 }
327         }
328 }
329
330 //------------------------------------------------------------------------------
331 // Name: slotDisplaySelected
332 // Desc: 
333 //------------------------------------------------------------------------------
334 void KCalcDisplay::slotDisplaySelected() {
335
336         if (button_ == Qt::LeftButton) {
337                 if (lit_) {
338                         slotCopy();
339                         selection_timer_->start(100);
340                 } else {
341                         selection_timer_->stop();
342                 }
343
344                 invertColors();
345         } else {
346                 slotPaste(false);   // Selection
347         }
348 }
349
350 //------------------------------------------------------------------------------
351 // Name: slotSelectionTimedOut
352 // Desc: 
353 //------------------------------------------------------------------------------
354 void KCalcDisplay::slotSelectionTimedOut() {
355
356         lit_ = false;
357         invertColors();
358         selection_timer_->stop();
359 }
360
361 //------------------------------------------------------------------------------
362 // Name: invertColors
363 // Desc: 
364 //------------------------------------------------------------------------------
365 void KCalcDisplay::invertColors() {
366
367         QPalette tmp_palette = palette();
368         tmp_palette.setColor(QPalette::Base, palette().color(QPalette::Text));
369         tmp_palette.setColor(QPalette::Text, palette().color(QPalette::Base));
370         setPalette(tmp_palette);
371 }
372
373 //------------------------------------------------------------------------------
374 // Name: mousePressEvent
375 // Desc: 
376 //------------------------------------------------------------------------------
377 void KCalcDisplay::mousePressEvent(QMouseEvent *e) {
378
379         if (e->button() == Qt::LeftButton) {
380                 lit_ = !lit_;
381                 button_ = Qt::LeftButton;
382         } else {
383                 button_ = Qt::MiddleButton;
384         }
385
386         emit clicked();
387 }
388
389 //------------------------------------------------------------------------------
390 // Name: setPrecision
391 // Desc: 
392 //------------------------------------------------------------------------------
393 void KCalcDisplay::setPrecision(int precision) {
394
395         precision_ = precision;
396 }
397
398 //------------------------------------------------------------------------------
399 // Name: setFixedPrecision
400 // Desc: 
401 //------------------------------------------------------------------------------
402 void KCalcDisplay::setFixedPrecision(int precision) {
403
404         if (fixed_precision_ > precision_) {
405                 fixed_precision_ = -1;
406         } else {
407                 fixed_precision_ = precision;
408         }
409 }
410
411 //------------------------------------------------------------------------------
412 // Name: setBeep
413 // Desc: 
414 //------------------------------------------------------------------------------
415 void KCalcDisplay::setBeep(bool flag) {
416         beep_ = flag;
417 }
418
419 //------------------------------------------------------------------------------
420 // Name: setGroupDigits
421 // Desc: 
422 //------------------------------------------------------------------------------
423 void KCalcDisplay::setGroupDigits(bool flag) {
424         groupdigits_ = flag;
425 }
426
427 //------------------------------------------------------------------------------
428 // Name: setTwosComplement
429 // Desc: 
430 //------------------------------------------------------------------------------
431 void KCalcDisplay::setTwosComplement(bool flag) {
432         twoscomplement_ = flag;
433 }
434
435 //------------------------------------------------------------------------------
436 // Name: setBinaryGrouping
437 // Desc: 
438 //------------------------------------------------------------------------------
439 void KCalcDisplay::setBinaryGrouping(int digits) {
440         binaryGrouping_ = digits;
441 }
442
443 //------------------------------------------------------------------------------
444 // Name: setOctalGrouping
445 // Desc: 
446 //------------------------------------------------------------------------------
447 void KCalcDisplay::setOctalGrouping(int digits) {
448         octalGrouping_ = digits;
449 }
450
451 //------------------------------------------------------------------------------
452 // Name: setHexadecimalGrouping
453 // Desc: 
454 //------------------------------------------------------------------------------
455 void KCalcDisplay::setHexadecimalGrouping(int digits) {
456         hexadecimalGrouping_ = digits;
457 }
458
459 //------------------------------------------------------------------------------
460 // Name: getAmount
461 // Desc: 
462 //------------------------------------------------------------------------------
463 const KNumber &KCalcDisplay::getAmount() const {
464         return display_amount_;
465 }
466
467 //------------------------------------------------------------------------------
468 // Name: setAmount
469 // Desc: 
470 //------------------------------------------------------------------------------
471 bool KCalcDisplay::setAmount(const KNumber &new_amount) {
472
473         QString display_str;
474
475         str_int_ = QLatin1String("0");
476         str_int_exp_.clear();
477         period_   = false;
478         neg_sign_ = false;
479         eestate_  = false;
480
481         if ((num_base_ != NB_DECIMAL) && (new_amount.type() != KNumber::TYPE_ERROR)) {
482                 display_amount_ = new_amount.integerPart();
483         
484                 if (twoscomplement_) {
485                         // treat number as 64-bit unsigned
486                         const quint64 tmp_workaround = display_amount_.toUint64();
487                         display_str = QString::number(tmp_workaround, num_base_).toUpper();
488                 } else {
489                         // QString::number treats non-decimal as unsigned
490                         qint64 tmp_workaround = display_amount_.toInt64();
491                         const bool neg = tmp_workaround < 0;
492                         if (neg) {
493                                 tmp_workaround = qAbs(tmp_workaround);
494                         }
495                         
496                         display_str = QString::number(tmp_workaround, num_base_).toUpper();
497                         if (neg) {
498                                 display_str.prepend(KGlobal::locale()->negativeSign());
499                         }
500                 }
501         } else {
502                 // num_base_ == NB_DECIMAL || new_amount.type() == KNumber::TYPE_ERROR
503                 display_amount_ = new_amount;
504                 display_str = display_amount_.toQString(KCalcSettings::precision(), fixed_precision_);
505         }
506
507         setText(display_str);
508         emit changedAmount(display_amount_);
509         return true;
510 }
511
512 //------------------------------------------------------------------------------
513 // Name: setText
514 // Desc: 
515 //------------------------------------------------------------------------------
516 void KCalcDisplay::setText(const QString &string)
517 {
518     // note that "C" locale is being used internally
519     text_ = string;
520     
521         // don't mess with special numbers
522     const bool special = (string.contains(QLatin1String("nan")) || string.contains(QLatin1String("inf")));
523         
524         // The decimal mode needs special treatment for two reasons, because: a) it uses KGlobal::locale() to get a localized 
525         // format and b) it has possible numbers after the decimal place. Neither applies to Binary, Hexadecimal or Octal.
526         
527         if (groupdigits_ && !special){
528                 switch (num_base_) {
529                 case NB_DECIMAL:
530                 if (string.endsWith(QLatin1Char('.'))) {
531                     text_.chop(1);
532                     // Note: rounding happened already above!
533                     text_ = KGlobal::locale()->formatNumber(text_, false, 0);
534                     text_.append(KGlobal::locale()->decimalSymbol());
535                 } else {
536                     // Note: rounding happened already above!
537                     text_ = KGlobal::locale()->formatNumber(text_, false, 0);
538                         }
539                         break;
540                         
541                 case NB_BINARY:
542                         text_ = groupDigits(text_, binaryGrouping_);
543                         break;
544                 
545                 case NB_OCTAL:
546                         text_ = groupDigits(text_, octalGrouping_);
547                         break;
548                         
549                 case NB_HEX:
550                         text_ = groupDigits(text_, hexadecimalGrouping_);
551                         break;
552                 }
553     } else if(special) {
554 #if 0
555                 // TODO: enable this code, it replaces the "inf" with an actual infinity
556                 //       symbol, but what should be put into the clip board when they copy?
557                 if(string.contains(QLatin1String("inf"))) {
558                         text_.replace("inf", QChar(0x221e));
559                 }
560 #endif
561         }
562
563     update();
564     emit changedText(text_);
565 }
566
567 //------------------------------------------------------------------------------
568 // Name: groupDigits
569 // Desc: 
570 //------------------------------------------------------------------------------
571 QString KCalcDisplay::groupDigits(const QString &displayString, int numDigits) {
572
573         QString tmpDisplayString;
574         const int stringLength = displayString.length();
575
576         for (int i = stringLength; i > 0 ; i--){
577                 if(i % numDigits == 0 && i != stringLength) {
578                         tmpDisplayString = tmpDisplayString + ' ';
579                 }
580
581                 tmpDisplayString = tmpDisplayString + displayString[stringLength - i];
582         }
583
584         return tmpDisplayString;
585 }
586
587 //------------------------------------------------------------------------------
588 // Name: text
589 // Desc: 
590 //------------------------------------------------------------------------------
591 QString KCalcDisplay::text() const {
592     return text_;
593 }
594
595 //------------------------------------------------------------------------------
596 // Name: setBase
597 // Desc: change representation of display to new base (i.e. binary, decimal,
598 //       octal, hexadecimal). The amount being displayed is changed to this
599 //       base, but for now this amount can not be modified anymore (like
600 //       being set with "setAmount"). Return value is the new base.
601 //------------------------------------------------------------------------------
602 int KCalcDisplay::setBase(NumBase new_base) {
603
604         switch (new_base) {
605         case NB_HEX:
606                 num_base_ = NB_HEX;
607                 period_  = false;
608                 break;
609         case NB_DECIMAL:
610                 num_base_ = NB_DECIMAL;
611                 break;
612         case NB_OCTAL:
613                 num_base_ = NB_OCTAL;
614                 period_  = false;
615                 break;
616         case NB_BINARY:
617                 num_base_ = NB_BINARY;
618                 period_  = false;
619                 break;
620         default:
621                 Q_ASSERT(0);
622         }
623
624         // reset amount
625         setAmount(display_amount_);
626         return num_base_;
627 }
628
629 //------------------------------------------------------------------------------
630 // Name: setStatusText
631 // Desc: 
632 //------------------------------------------------------------------------------
633 void KCalcDisplay::setStatusText(int i, const QString &text) {
634
635     if (i < NUM_STATUS_TEXT) {
636         str_status_[i] = text;
637         }
638         
639     update();
640 }
641
642 //------------------------------------------------------------------------------
643 // Name: updateDisplay
644 // Desc: 
645 //------------------------------------------------------------------------------
646 void KCalcDisplay::updateDisplay() {
647
648         // Put sign in front.
649         QString tmp_string;
650         if (neg_sign_) {
651                 tmp_string = QLatin1Char('-') + str_int_;
652         } else {
653                 tmp_string = str_int_;
654         }
655
656         bool ok;
657
658         switch (num_base_) {
659         case NB_BINARY:
660                 Q_ASSERT(!period_ && !eestate_);
661                 setText(tmp_string);
662                 display_amount_ = KNumber(str_int_.toULongLong(&ok, 2));
663                 if (neg_sign_) {
664                         display_amount_ = -display_amount_;
665                 }
666                 break;
667
668         case NB_OCTAL:
669                 Q_ASSERT(!period_ && !eestate_);
670                 setText(tmp_string);
671                 display_amount_ = KNumber(str_int_.toULongLong(&ok, 8));
672                 if (neg_sign_) {
673                         display_amount_ = -display_amount_;
674                 }
675                 break;
676
677         case NB_HEX:
678                 Q_ASSERT(!period_ && !eestate_);
679                 setText(tmp_string);
680                 display_amount_ = KNumber(str_int_.toULongLong(&ok, 16));
681                 if (neg_sign_) {
682                         display_amount_ = -display_amount_;
683                 }
684                 break;
685
686         case NB_DECIMAL:
687                 if (!eestate_) {
688                         setText(tmp_string);
689                         display_amount_ = KNumber(tmp_string);
690                 } else {
691                         if (str_int_exp_.isNull()) {
692                                 // add 'e0' to display but not to conversion
693                                 display_amount_ = KNumber(tmp_string);
694                                 setText(tmp_string + QLatin1String("e0"));
695                         } else {
696                                 tmp_string +=  QLatin1Char('e') + str_int_exp_;
697                                 setText(tmp_string);
698                                 display_amount_ = KNumber(tmp_string);
699                         }
700                 }
701                 break;
702
703         default:
704                 Q_ASSERT(0);
705         }
706
707         emit changedAmount(display_amount_);
708 }
709
710 //------------------------------------------------------------------------------
711 // Name: newCharacter
712 // Desc: 
713 //------------------------------------------------------------------------------
714 void KCalcDisplay::newCharacter(const QChar new_char) {
715
716         // test if character is valid
717         switch (new_char.toLatin1()) {
718         case 'e':
719                 // EE can be set only once and in decimal mode
720                 if (num_base_ != NB_DECIMAL || eestate_) {
721                         if (beep_) {
722                                 KNotification::beep();
723                         }
724                         return;
725                 }
726                 eestate_ = true;
727                 break;
728
729         case 'F':
730         case 'E':
731         case 'D':
732         case 'C':
733         case 'B':
734         case 'A':
735                 if (num_base_ == NB_DECIMAL) {
736                         if (beep_) {
737                                 KNotification::beep();
738                         }
739                         return;
740                 }
741                 // no break
742         case '9':
743         case '8':
744                 if (num_base_ == NB_OCTAL) {
745                         if (beep_) {
746                                 KNotification::beep();
747                         }
748                         return;
749                 }
750                 // no break
751         case '7':
752         case '6':
753         case '5':
754         case '4':
755         case '3':
756         case '2':
757                 if (num_base_ == NB_BINARY) {
758                         if (beep_) {
759                                 KNotification::beep();
760                         }
761                         return;
762                 }
763                 // no break
764         case '1':
765         case '0':
766                 break;
767
768         default:
769                 if(new_char == KGlobal::locale()->decimalSymbol()[0]) {
770                         // Period can be set only once and only in decimal
771                         // mode, also not in EE-mode
772                         if (num_base_ != NB_DECIMAL || period_ || eestate_) {
773                                 if (beep_) {
774                                         KNotification::beep();
775                                 }
776                                 return;
777                         }
778                         period_ = true;
779                 } else {
780                         if (beep_) {
781                                 KNotification::beep();
782                         }
783                         return;
784                 }
785         }
786
787         // change exponent or mantissa
788         if (eestate_) {
789                 // ignore '.' before 'e'. turn e.g. '123.e' into '123e'
790                 if (new_char == QLatin1Char('e') && str_int_.endsWith(KGlobal::locale()->decimalSymbol())) {
791                         str_int_.chop(1);
792                         period_ = false;
793                 }
794
795                 // 'e' only starts ee_mode, leaves strings unchanged
796                 // do not add '0' if at start of exp
797                 if (new_char != QLatin1Char('e')  && !(str_int_exp_.isNull() && new_char == QLatin1Char('0'))) {
798                         str_int_exp_.append(new_char);
799                 }
800         } else {
801                 // handle first character
802                 if (str_int_ == QLatin1String("0")) {
803                         switch (new_char.toLatin1()) {
804                         case 'e':
805                                 // display "0e" not just "e"
806                                 // "0e" does not make sense either, but...
807                                 str_int_.append(new_char);
808                                 break;
809                         default:
810                                 if(new_char == KGlobal::locale()->decimalSymbol()[0]) {
811                                         // display "0." not just "."
812                                         str_int_.append(new_char);
813                                 } else {
814                                         // no leading '0's
815                                         str_int_[0] = new_char;
816                                 }
817                         }
818                 } else {
819                         str_int_.append(new_char);
820                 }
821         }
822
823         updateDisplay();
824 }
825
826 //------------------------------------------------------------------------------
827 // Name: deleteLastDigit
828 // Desc: 
829 //------------------------------------------------------------------------------
830 void KCalcDisplay::deleteLastDigit() {
831
832         // Only partially implemented !!
833         if (eestate_) {
834                 if (str_int_exp_.isNull()) {
835                         eestate_ = false;
836                 } else {
837                         const int length = str_int_exp_.length();
838                         if (length > 1) {
839                                 str_int_exp_.chop(1);
840                         } else {
841                                 str_int_exp_ = QLatin1String((const char *)0);
842                         }
843                 }
844         } else {
845                 const int length = str_int_.length();
846                 if (length > 1) {
847                         if (str_int_[length-1] == KGlobal::locale()->decimalSymbol()[0]) {
848                                 period_ = false;
849                         }
850                         str_int_.chop(1);
851                 } else {
852                         Q_ASSERT(!period_);
853                         str_int_[0] = QLatin1Char('0');
854                 }
855         }
856
857         updateDisplay();
858 }
859
860 //------------------------------------------------------------------------------
861 // Name: changeSign
862 // Desc: change Sign of display. Problem: Only possible here, when in input
863 //       mode. Otherwise return 'false' so that the kcalc_core can handle
864 //       things.
865 //------------------------------------------------------------------------------
866 bool KCalcDisplay::changeSign() {
867
868         //stupid way, to see if in input_mode or display_mode
869         if (str_int_ == QLatin1String("0")) {
870                 return false;
871         }
872
873         if (eestate_) {
874                 if (!str_int_exp_.isNull()) {
875                         if (str_int_exp_[0] != QLatin1Char('-')) {
876                                 str_int_exp_.prepend(QLatin1Char('-'));
877                         } else {
878                                 str_int_exp_.remove(QLatin1Char('-'));
879                         }
880                 }
881         } else {
882                 neg_sign_ = !neg_sign_;
883         }
884
885         updateDisplay();
886         return true;
887 }
888
889 //------------------------------------------------------------------------------
890 // Name: initStyleOption
891 // Desc: 
892 //------------------------------------------------------------------------------
893 void KCalcDisplay::initStyleOption(QStyleOptionFrame *option) const {
894
895         if (!option) {
896                 return;
897         }
898
899         option->initFrom(this);
900         option->state &= ~QStyle::State_HasFocus; // don't draw focus highlight
901
902         if (frameShadow() == QFrame::Sunken) {
903                 option->state |= QStyle::State_Sunken;
904         } else if (frameShadow() == QFrame::Raised) {
905                 option->state |= QStyle::State_Raised;
906         }
907         
908         option->lineWidth = style()->pixelMetric(QStyle::PM_DefaultFrameWidth, option, this);
909         option->midLineWidth = 0;
910 }
911
912 //------------------------------------------------------------------------------
913 // Name: paintEvent
914 // Desc: 
915 //------------------------------------------------------------------------------
916 void KCalcDisplay::paintEvent(QPaintEvent *) {
917
918         QPainter painter(this);
919
920         QStyleOptionFrame option;
921         initStyleOption(&option);
922
923         style()->drawPrimitive(QStyle::PE_PanelLineEdit, &option, &painter, this);
924
925         // draw display text
926         const int margin = style()->pixelMetric(QStyle::PM_DefaultFrameWidth, 0, 0);
927         QRect cr = contentsRect();
928         cr.adjust(margin*2, 0, -margin*2, 0);   // provide a margin
929         
930         const int align = QStyle::visualAlignment(layoutDirection(), Qt::AlignRight | Qt::AlignVCenter);
931         painter.drawText(cr, align | Qt::TextSingleLine, text_);
932
933         // draw the status texts using half of the normal
934         // font size but not smaller than 7pt
935         QFont fnt(font());
936         fnt.setPointSize(qMax((fnt.pointSize() / 2), 7));
937         painter.setFont(fnt);
938         
939         QFontMetrics fm(fnt);
940         const uint w = fm.width(QLatin1String("________"));
941         const uint h = fm.height();
942
943         for (int n = 0; n < NUM_STATUS_TEXT; ++n) {
944                 painter.drawText(5 + n * w, h, str_status_[n]);
945         }
946 }
947
948 //------------------------------------------------------------------------------
949 // Name: sizeHint
950 // Desc: 
951 //------------------------------------------------------------------------------
952 QSize KCalcDisplay::sizeHint() const {
953
954         // basic size
955         QSize sz = fontMetrics().size(Qt::TextSingleLine, text_);
956
957         // expanded by half font height to make room for  the status texts
958         QFont fnt(font());
959         fnt.setPointSize(qMax((fnt.pointSize() / 2), 7));
960         
961         const QFontMetrics fm(fnt);
962         sz.setHeight(sz.height() + fm.height());
963
964         QStyleOptionFrame option;
965         initStyleOption(&option);
966
967         return (style()->sizeFromContents(QStyle::CT_LineEdit, &option, sz.expandedTo(QApplication::globalStrut()),     this));
968 }