1 // This file is part of the SpeedCrunch project
2 // Copyright (C) 2005-2006 Johan Thelin <e8johan@gmail.com>
3 // Copyright (C) 2007-2009, 2014 @heldercorreia
4 //
5 // This program is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License
7 // as published by the Free Software Foundation; either version 2
8 // of the License, or (at your option) any later version.
9 //
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with this program; see the file COPYING. If not, write to
17 // the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 // Boston, MA 02110-1301, USA.
19
20 #include "gui/keypad.h"
21
22 #include "core/settings.h"
23
24 #include <QLocale>
25 #include <QHash>
26 #include <QSignalMapper>
27 #include <QApplication>
28 #include <QGridLayout>
29 #include <QPushButton>
30 #include <QStyle>
31 #include <QStyleOptionButton>
32
33 #if QT_VERSION >= 0x040400 && defined(Q_WS_MAC) && !defined(QT_NO_STYLE_MAC)
34 #include <QMacStyle>
35 #endif
36
37 const Keypad::KeyDescription Keypad::keyDescriptions[] = {
38 {QString::fromLatin1("0"), Key0, true, 3, 0},
39 {QString::fromLatin1("1"), Key1, true, 2, 0},
40 {QString::fromLatin1("2"), Key2, true, 2, 1},
41 {QString::fromLatin1("3"), Key3, true, 2, 2},
42 {QString::fromLatin1("4"), Key4, true, 1, 0},
43 {QString::fromLatin1("5"), Key5, true, 1, 1},
44 {QString::fromLatin1("6"), Key6, true, 1, 2},
45 {QString::fromLatin1("7"), Key7, true, 0, 0},
46 {QString::fromLatin1("8"), Key8, true, 0, 1},
47 {QString::fromLatin1("9"), Key9, true, 0, 2},
48 {QString::fromLatin1(","), KeyRadixChar, true, 3, 1},
49 {QString::fromUtf8("="), KeyEquals, true, 3, 2},
50 {QString::fromUtf8("÷"), KeyDivide, true, 0, 3},
51 {QString::fromUtf8("×"), KeyTimes, true, 1, 3},
52 {QString::fromUtf8("−"), KeyMinus, true, 2, 3},
53 {QString::fromUtf8("+"), KeyPlus, true, 3, 3},
54 {QString::fromLatin1("arccos"), KeyAcos, false, 2, 8},
55 {QString::fromLatin1("ans"), KeyAns, false, 1, 6},
56 {QString::fromLatin1("arcsin"), KeyAsin, false, 1, 8},
57 {QString::fromLatin1("arctan"), KeyAtan, false, 3, 8},
58 {QString::fromLatin1("C"), KeyClear, false, 0, 4},
59 {QString::fromLatin1("cos"), KeyCos, false, 2, 7},
60 {QString::fromLatin1("E"), KeyEE, false, 1, 4},
61 {QString::fromLatin1("exp"), KeyExp, false, 0, 7},
62 {QString::fromLatin1("!"), KeyFactorial, false, 3, 5},
63 {QString::fromLatin1("ln"), KeyLn, false, 0, 8},
64 {QString::fromLatin1("("), KeyLeftPar, false, 2, 4},
65 {QString::fromLatin1("%"), KeyPercent, false, 3, 4},
66 {QString::fromLatin1("^"), KeyRaise, false, 1, 5},
67 {QString::fromLatin1(")"), KeyRightPar, false, 2, 5},
68 {QString::fromLatin1("sin"), KeySin, false, 1, 7},
69 {QString::fromLatin1("tan"), KeyTan, false, 3, 7},
70 {QString::fromLatin1("x="), KeyXEquals, false, 3, 6},
71 {QString::fromLatin1("x"), KeyX, false, 2, 6},
72 {QString::fromUtf8("π"), KeyPi, false, 0, 6},
73 {QString::fromUtf8("√"), KeySqrt, false, 0, 5}
74 };
75
Keypad(QWidget * parent)76 Keypad::Keypad(QWidget* parent)
77 : QWidget(parent)
78 {
79 createButtons();
80 sizeButtons();
81 layoutButtons();
82 setButtonTooltips();
83 disableButtonFocus();
84 setLayoutDirection(Qt::LeftToRight);
85 connect(&mapper, SIGNAL(mapped(int)), SLOT(emitButtonPressed(int)));
86 }
87
handleRadixCharacterChange()88 void Keypad::handleRadixCharacterChange()
89 {
90 key(KeyRadixChar)->setText(QString(QChar(Settings::instance()->radixCharacter())));
91 }
92
retranslateText()93 void Keypad::retranslateText()
94 {
95 setButtonTooltips();
96 handleRadixCharacterChange();
97 }
98
emitButtonPressed(int button) const99 void Keypad::emitButtonPressed(int button) const
100 {
101 emit buttonPressed(Button(button));
102 }
103
key(Button button) const104 QPushButton* Keypad::key(Button button) const
105 {
106 Q_ASSERT(keys.contains(button));
107 return keys.value(button).first;
108 }
109
createButtons()110 void Keypad::createButtons()
111 {
112 keys.clear();
113
114 QFont boldFont;
115 boldFont.setBold(true);
116 boldFont.setPointSize(boldFont.pointSize() + 3);
117
118 static const int keyDescriptionsCount = int(sizeof keyDescriptions / sizeof keyDescriptions[0]);
119 for (int i = 0; i < keyDescriptionsCount; ++i) {
120 const KeyDescription* description = keyDescriptions + i;
121 QPushButton* key = new QPushButton(description->label);
122 if (description->boldFont)
123 key->setFont(boldFont);
124 const QPair<QPushButton*, const KeyDescription*> hashValue(key, description);
125 keys.insert(description->button, hashValue);
126
127 QObject::connect(key, SIGNAL(clicked()), &mapper, SLOT(map()));
128 mapper.setMapping(key, description->button);
129 }
130
131 handleRadixCharacterChange();
132 }
133
disableButtonFocus()134 void Keypad::disableButtonFocus()
135 {
136 QHashIterator<Button, QPair<QPushButton*, const KeyDescription*> > i(keys);
137 while (i.hasNext()) {
138 i.next();
139 i.value().first->setFocusPolicy(Qt::NoFocus);
140 }
141 }
142
layoutButtons()143 void Keypad::layoutButtons()
144 {
145 int layoutSpacing = 3;
146
147 #if QT_VERSION >= 0x040400 && defined(Q_WS_MAC) && !defined(QT_NO_STYLE_MAC)
148 // Workaround for a layouting bug in QMacStyle, Qt 4.4.0. Buttons would overlap.
149 if (qobject_cast<QMacStyle *>(p->style()))
150 layoutSpacing = -1;
151 #endif
152
153 QGridLayout* layout = new QGridLayout(this);
154 layout->setMargin(3);
155 layout->setSpacing(layoutSpacing);
156
157 QHashIterator<Button, QPair<QPushButton*, const KeyDescription*> > i(keys);
158 while (i.hasNext()) {
159 i.next();
160 QWidget* widget = i.value().first;
161 const int row = i.value().second->gridRow;
162 const int column = i.value().second->gridColumn;
163 layout->addWidget(widget, row, column);
164 }
165 }
166
setButtonTooltips()167 void Keypad::setButtonTooltips()
168 {
169 key(KeyAcos)->setToolTip(Keypad::tr("Inverse cosine"));
170 key(KeyAns)->setToolTip(Keypad::tr("The last result"));
171 key(KeyAsin)->setToolTip(Keypad::tr("Inverse sine"));
172 key(KeyAtan)->setToolTip(Keypad::tr("Inverse tangent"));
173 key(KeyClear)->setToolTip(Keypad::tr("Clear expression"));
174 key(KeyCos)->setToolTip(Keypad::tr("Cosine"));
175 key(KeyEE)->setToolTip(Keypad::tr("Scientific notation"));
176 key(KeyExp)->setToolTip(Keypad::tr("Exponential"));
177 key(KeyLn)->setToolTip(Keypad::tr("Natural logarithm"));
178 key(KeySin)->setToolTip(Keypad::tr("Sine"));
179 key(KeySqrt)->setToolTip(Keypad::tr("Square root"));
180 key(KeyTan)->setToolTip(Keypad::tr("Tangent"));
181 key(KeyXEquals)->setToolTip(Keypad::tr("Assign variable x"));
182 key(KeyX)->setToolTip(Keypad::tr("The variable x"));
183 }
184
sizeButtons()185 void Keypad::sizeButtons()
186 {
187 // The same font in all buttons, so just pick one.
188 QFontMetrics fm = key(Key0)->fontMetrics();
189
190 int maxWidth = fm.width(key(KeyAcos)->text());
191 const int textHeight = qMax(fm.lineSpacing(), 14);
192
193 QStyle::ContentsType type = QStyle::CT_ToolButton;
194 QStyleOptionButton option;
195 const QWidget* exampleWidget = key(KeyAcos);
196 option.initFrom(exampleWidget);
197 QSize minSize = QSize(maxWidth, textHeight).expandedTo(QApplication::globalStrut());
198 QSize size = exampleWidget->style()->sizeFromContents(type, &option, minSize, exampleWidget);
199
200 #ifdef Q_WS_X11
201 // We would like to use the button size as indicated by the widget style, but in some cases,
202 // e.g. KDE's Plastik or Oxygen, another few pixels (typically 5) are added as the content
203 // margin, thereby making the button incredibly wider than necessary. Workaround: take only
204 // the hinted height, adjust the width ourselves (with our own margin).
205 maxWidth += 6;
206 int hh = size.height();
207 int ww = hh * 162 / 100; // Use golden ratio.
208 size = QSize(qMax(ww, maxWidth), hh);
209 #endif
210
211 // limit the size of the buttons
212 QHashIterator<Button, QPair<QPushButton*, const KeyDescription*> > i(keys);
213 while (i.hasNext()) {
214 i.next();
215 i.value().first->setFixedSize(size);
216 }
217 }
218
changeEvent(QEvent * event)219 void Keypad::changeEvent(QEvent* event)
220 {
221 if (event->type() == QEvent::LanguageChange)
222 retranslateText();
223 else
224 QWidget::changeEvent(event);
225 }
226