File indexing completed on 2021-12-21 14:36:19

0001 /*
0002     SPDX-FileCopyrightText: 2012-2013 Evan Teran <evan.teran@gmail.com>
0003     SPDX-FileCopyrightText: 2006 Michel Marti <mma@objectxp.com>
0004 
0005     SPDX-License-Identifier: GPL-2.0-or-later
0006 */
0007 
0008 #include "kcalc_bitset.h"
0009 #include "bitbutton.h"
0010 
0011 #include <KLocalizedString>
0012 #include <QButtonGroup>
0013 #include <QGridLayout>
0014 #include <QHBoxLayout>
0015 #include <QLabel>
0016 
0017 // TODO: I think it would actually be appropriate to use a std::bitset<64>
0018 //       for the internal representation of this class perhaps
0019 //       the only real caveat is the conversion to/from quint64
0020 
0021 //------------------------------------------------------------------------------
0022 // Name: KCalcBitset
0023 // Desc: constructor
0024 //------------------------------------------------------------------------------
0025 KCalcBitset::KCalcBitset(QWidget *parent)
0026     : QFrame(parent)
0027     , bit_button_group_(new QButtonGroup(this))
0028     , value_(0)
0029 {
0030     setFrameStyle(QFrame::Panel | QFrame::Sunken);
0031 
0032     connect(bit_button_group_, qOverload<QAbstractButton *>(&QButtonGroup::buttonClicked), this, &KCalcBitset::slotToggleBit);
0033 
0034     // smaller label font
0035     QFont fnt = font();
0036     if (fnt.pointSize() > 6) {
0037         fnt.setPointSize(fnt.pointSize() - 1);
0038     }
0039 
0040     // main layout
0041     auto layout = new QGridLayout(this);
0042     layout->setContentsMargins(2, 2, 2, 2);
0043     layout->setSpacing(0);
0044 
0045     // create bits
0046     int bitCounter = 63;
0047     for (int rows = 0; rows < 2; rows++) {
0048         for (int cols = 0; cols < 4; cols++) {
0049             // two rows of four words
0050             auto const wordlayout = new QHBoxLayout();
0051             wordlayout->setContentsMargins(2, 2, 2, 2);
0052             wordlayout->setSpacing(2);
0053             layout->addLayout(wordlayout, rows, cols);
0054 
0055             for (int bit = 0; bit < 8; bit++) {
0056                 auto const tmpBitButton = new BitButton(this);
0057                 tmpBitButton->setToolTip(i18n("Bit %1 = %2", bitCounter, 1ULL << bitCounter));
0058                 wordlayout->addWidget(tmpBitButton);
0059                 wordlayout->setStretch(bit, 1);
0060                 bit_button_group_->addButton(tmpBitButton, bitCounter);
0061                 bitCounter--;
0062             }
0063 
0064             // label word
0065             auto label = new QLabel(this);
0066             label->setText(QString::number(bitCounter + 1));
0067             label->setFont(fnt);
0068             label->setMinimumSize(label->fontMetrics().size(Qt::TextSingleLine, QStringLiteral("56"))); // Make all labels have same size
0069             wordlayout->addWidget(label);
0070             wordlayout->setStretch(8, 1);
0071         }
0072         layout->setRowStretch(rows, 1);
0073     }
0074     
0075     // layout stretch for columns
0076     for (int cols = 0; cols < 4; cols++) {
0077         layout->setColumnStretch(cols, 1);
0078     }
0079 
0080     // store current aspect ratio (using width:height)
0081     QSize initialSize(size());
0082     if (initialSize.height() != 0.0 && float(initialSize.width()) / float(initialSize.height()) < 2.5) {
0083         ratio_ = float(initialSize.width()) / float(initialSize.height());
0084     } else {
0085         ratio_ = 1.355163727959698; // 538/397
0086     }
0087 }
0088 
0089 //------------------------------------------------------------------------------
0090 // Name: setValue
0091 // Desc: set the value of the bitset based on an unsigned 64-bit number
0092 //------------------------------------------------------------------------------
0093 void KCalcBitset::setValue(quint64 value)
0094 {
0095     if (value_ == value) {
0096         // don't waste time if there was no change..
0097         return;
0098     }
0099 
0100     value_ = value;
0101 
0102     // set each bit button
0103     for (int i = 0; i < 64; i++) {
0104         if (auto bb = qobject_cast<BitButton *>(bit_button_group_->button(i))) {
0105             bb->setOn(value & 1);
0106         }
0107         value >>= 1;
0108     }
0109 }
0110 
0111 //------------------------------------------------------------------------------
0112 // Name: getValue
0113 // Desc: returns the bitset value as an unsigned 64-bit number
0114 //------------------------------------------------------------------------------
0115 quint64 KCalcBitset::getValue() const
0116 {
0117     return value_;
0118 }
0119 
0120 //------------------------------------------------------------------------------
0121 // Name: slotToggleBit
0122 // Desc: inverts the value of a single bit
0123 //------------------------------------------------------------------------------
0124 void KCalcBitset::slotToggleBit(QAbstractButton *button)
0125 {
0126     if (button) {
0127         const int bit = bit_button_group_->id(button);
0128         const quint64 nv = getValue() ^ (1LL << bit);
0129         setValue(nv);
0130         Q_EMIT valueChanged(value_);
0131     }
0132 }
0133 
0134 //------------------------------------------------------------------------------
0135 // Name: resizeEvent
0136 // Desc: make sure all bitButtons have the same size
0137 //------------------------------------------------------------------------------
0138 void KCalcBitset::resizeEvent(QResizeEvent *event)
0139 {
0140     // Call the overridden resize event
0141     QFrame::resizeEvent(event);
0142 
0143     // Set our maximum size based on the space available in the parent (to keep aspect ratio)
0144     QWidget *parent = parentWidget();
0145     if (parent) {
0146         QSize maxSize(parent->contentsRect().width(), parent->contentsRect().height());
0147         if (maxSize.width() != 0 && maxSize.height() != 0) {
0148             float actualRatio = float(maxSize.width()) / float(maxSize.height());
0149 
0150             if (actualRatio > ratio_) {
0151                 // available space is too wide, limit width
0152                 maxSize.setWidth(ratio_ * maxSize.height());
0153             } else if (actualRatio < ratio_) {
0154                 // available space is too tall, limit height
0155                 maxSize.setHeight(maxSize.width() / ratio_);
0156             }
0157 
0158             setMaximumSize(maxSize.width(), maxSize.height());
0159         }
0160     }
0161 
0162     // Get the minimum size of all buttons
0163     int minWidth = INT_MAX;
0164     int minHeight = INT_MAX;
0165     for (QObject *obj : bit_button_group_->buttons()) {
0166         if (auto const button = qobject_cast<BitButton *>(obj)) {
0167             minWidth = qMin(minWidth, button->rect().width());
0168             minHeight = qMin(minHeight, button->rect().height());
0169         }
0170     }
0171     
0172     // If this worked, set the renderSize for all BitButtons
0173     if (minWidth != INT_MAX && minHeight != INT_MAX) {
0174         // Make sure the size is square
0175         if (minWidth > minHeight)
0176             minWidth = minHeight;
0177         else if (minHeight > minWidth)
0178             minHeight = minWidth;
0179         
0180         // Set it for all buttons
0181         for (QObject *obj : bit_button_group_->buttons()) {
0182             if (auto const button = qobject_cast<BitButton *>(obj)) {
0183                 QSize size = QSize(button->renderSize());
0184                 size.setWidth(minWidth);
0185                 size.setHeight(minHeight);
0186                 button->setRenderSize(size);
0187             }
0188         }
0189     }
0190 
0191     updateGeometry();
0192 }