1 | /* |
2 | SPDX-FileCopyrightText: 2012-2013 Evan Teran <evan.teran@gmail.com> |
3 | SPDX-FileCopyrightText: 2006 Michel Marti <mma@objectxp.com> |
4 | |
5 | SPDX-License-Identifier: GPL-2.0-or-later |
6 | */ |
7 | |
8 | #include "kcalc_bitset.h" |
9 | #include "bitbutton.h" |
10 | |
11 | #include <KLocalizedString> |
12 | #include <QButtonGroup> |
13 | #include <QGridLayout> |
14 | #include <QHBoxLayout> |
15 | #include <QLabel> |
16 | |
17 | // TODO: I think it would actually be appropriate to use a std::bitset<64> |
18 | // for the internal representation of this class perhaps |
19 | // the only real caveat is the conversion to/from quint64 |
20 | |
21 | //------------------------------------------------------------------------------ |
22 | // Name: KCalcBitset |
23 | // Desc: constructor |
24 | //------------------------------------------------------------------------------ |
25 | KCalcBitset::KCalcBitset(QWidget *parent) |
26 | : QFrame(parent) |
27 | , bit_button_group_(new QButtonGroup(this)) |
28 | , value_(0) |
29 | { |
30 | setFrameStyle(QFrame::Panel | QFrame::Sunken); |
31 | |
32 | connect(bit_button_group_, &QButtonGroup::buttonClicked, this, &KCalcBitset::slotToggleBit); |
33 | |
34 | // smaller label font |
35 | QFont fnt = font(); |
36 | if (fnt.pointSize() > 6) { |
37 | fnt.setPointSize(fnt.pointSize() - 1); |
38 | } |
39 | |
40 | // main layout |
41 | auto layout = new QGridLayout(this); |
42 | layout->setContentsMargins(2, 2, 2, 2); |
43 | layout->setSpacing(0); |
44 | |
45 | // create bits |
46 | int bitCounter = 63; |
47 | for (int rows = 0; rows < 2; rows++) { |
48 | for (int cols = 0; cols < 4; cols++) { |
49 | // two rows of four words |
50 | auto const wordlayout = new QHBoxLayout(); |
51 | wordlayout->setContentsMargins(2, 2, 2, 2); |
52 | wordlayout->setSpacing(2); |
53 | layout->addLayout(wordlayout, rows, cols); |
54 | |
55 | for (int bit = 0; bit < 8; bit++) { |
56 | auto const tmpBitButton = new BitButton(this); |
57 | tmpBitButton->setToolTip(i18n("Bit %1 = %2" , bitCounter, 1ULL << bitCounter)); |
58 | wordlayout->addWidget(tmpBitButton); |
59 | wordlayout->setStretch(bit, 1); |
60 | bit_button_group_->addButton(tmpBitButton, bitCounter); |
61 | bitCounter--; |
62 | } |
63 | |
64 | // label word |
65 | auto label = new QLabel(this); |
66 | label->setText(QString::number(bitCounter + 1)); |
67 | label->setFont(fnt); |
68 | label->setMinimumSize(label->fontMetrics().size(Qt::TextSingleLine, QStringLiteral("56" ))); // Make all labels have same size |
69 | wordlayout->addWidget(label); |
70 | wordlayout->setStretch(8, 1); |
71 | } |
72 | layout->setRowStretch(rows, 1); |
73 | } |
74 | |
75 | // layout stretch for columns |
76 | for (int cols = 0; cols < 4; cols++) { |
77 | layout->setColumnStretch(cols, 1); |
78 | } |
79 | |
80 | // store current aspect ratio (using width:height) |
81 | QSize initialSize(size()); |
82 | if (initialSize.height() != 0.0 && float(initialSize.width()) / float(initialSize.height()) < 2.5) { |
83 | ratio_ = float(initialSize.width()) / float(initialSize.height()); |
84 | } else { |
85 | ratio_ = 1.355163727959698; // 538/397 |
86 | } |
87 | } |
88 | |
89 | //------------------------------------------------------------------------------ |
90 | // Name: setValue |
91 | // Desc: set the value of the bitset based on an unsigned 64-bit number |
92 | //------------------------------------------------------------------------------ |
93 | void KCalcBitset::setValue(quint64 value) |
94 | { |
95 | if (value_ == value) { |
96 | // don't waste time if there was no change. |
97 | return; |
98 | } |
99 | |
100 | value_ = value; |
101 | |
102 | // set each bit button |
103 | for (int i = 0; i < 64; i++) { |
104 | if (auto bb = qobject_cast<BitButton *>(bit_button_group_->button(i))) { |
105 | bb->setOn(value & 1); |
106 | } |
107 | value >>= 1; |
108 | } |
109 | } |
110 | |
111 | //------------------------------------------------------------------------------ |
112 | // Name: getValue |
113 | // Desc: returns the bitset value as an unsigned 64-bit number |
114 | //------------------------------------------------------------------------------ |
115 | quint64 KCalcBitset::getValue() const |
116 | { |
117 | return value_; |
118 | } |
119 | |
120 | //------------------------------------------------------------------------------ |
121 | // Name: slotToggleBit |
122 | // Desc: inverts the value of a single bit |
123 | //------------------------------------------------------------------------------ |
124 | void KCalcBitset::slotToggleBit(QAbstractButton *button) |
125 | { |
126 | if (button) { |
127 | const int bit = bit_button_group_->id(button); |
128 | const quint64 nv = getValue() ^ (1LL << bit); |
129 | setValue(nv); |
130 | Q_EMIT valueChanged(value_); |
131 | } |
132 | } |
133 | |
134 | //------------------------------------------------------------------------------ |
135 | // Name: resizeEvent |
136 | // Desc: make sure all bitButtons have the same size |
137 | //------------------------------------------------------------------------------ |
138 | void KCalcBitset::resizeEvent(QResizeEvent *event) |
139 | { |
140 | // Call the overridden resize event |
141 | QFrame::resizeEvent(event); |
142 | |
143 | // Set our maximum size based on the space available in the parent (to keep aspect ratio) |
144 | QWidget *parent = parentWidget(); |
145 | if (parent) { |
146 | QSize maxSize(parent->contentsRect().width(), parent->contentsRect().height()); |
147 | if (maxSize.width() != 0 && maxSize.height() != 0) { |
148 | float actualRatio = float(maxSize.width()) / float(maxSize.height()); |
149 | |
150 | if (actualRatio > ratio_) { |
151 | // available space is too wide, limit width |
152 | maxSize.setWidth(ratio_ * maxSize.height()); |
153 | } else if (actualRatio < ratio_) { |
154 | // available space is too tall, limit height |
155 | maxSize.setHeight(maxSize.width() / ratio_); |
156 | } |
157 | |
158 | setMaximumSize(maxSize.width(), maxSize.height()); |
159 | } |
160 | } |
161 | |
162 | // Get the minimum size of all buttons |
163 | int minWidth = INT_MAX; |
164 | int minHeight = INT_MAX; |
165 | for (QObject *obj : bit_button_group_->buttons()) { |
166 | if (auto const button = qobject_cast<BitButton *>(obj)) { |
167 | minWidth = qMin(minWidth, button->rect().width()); |
168 | minHeight = qMin(minHeight, button->rect().height()); |
169 | } |
170 | } |
171 | |
172 | // If this worked, set the renderSize for all BitButtons |
173 | if (minWidth != INT_MAX && minHeight != INT_MAX) { |
174 | // Make sure the size is square |
175 | if (minWidth > minHeight) |
176 | minWidth = minHeight; |
177 | else if (minHeight > minWidth) |
178 | minHeight = minWidth; |
179 | |
180 | // Set it for all buttons |
181 | for (QObject *obj : bit_button_group_->buttons()) { |
182 | if (auto const button = qobject_cast<BitButton *>(obj)) { |
183 | QSize size = QSize(button->renderSize()); |
184 | size.setWidth(minWidth); |
185 | size.setHeight(minHeight); |
186 | button->setRenderSize(size); |
187 | } |
188 | } |
189 | } |
190 | |
191 | updateGeometry(); |
192 | } |
193 | |
194 | #include "moc_kcalc_bitset.cpp" |
195 | |