File indexing completed on 2022-12-06 18:52:31

0001 /*
0002     SPDX-FileCopyrightText: 2008 Danilo Balzaque <danilo.balzaque@ltia.fc.unesp.br>
0003     SPDX-FileCopyrightText: 2008 Tadeu Araujo <tadeu.araujo@ltia.fc.unesp.br>
0004     SPDX-FileCopyrightText: 2008 Tiago Porangaba <tiago.porangaba@ltia.fc.unesp.br>
0005 
0006     SPDX-License-Identifier: GPL-2.0-or-later
0007 */
0008 
0009 #include "FractionRingWidget.h"
0010 
0011 /* these includes are needed for KDE support */
0012 #include <KActionCollection>
0013 #include <KConfigDialog>
0014 #include <KLocalizedString>
0015 #include <KStandardAction>
0016 #include <KStandardShortcut>
0017 
0018 /* these includes are needed for Qt support */
0019 #include <QAction>
0020 #include <QGridLayout>
0021 #include <QLabel>
0022 #include <QPainter>
0023 #include <QPushButton>
0024 #include <QRectF>
0025 #include <QSpinBox>
0026 #include <QString>
0027 #include <QTextEdit>
0028 #include <QWidgetAction>
0029 #include <QRandomGenerator>
0030 
0031 #ifdef DEBUG
0032 #include <QDebug>
0033 #endif
0034 
0035 #include "ui_taskfontsbase.h"
0036 #include "settingsclass.h"
0037 #include "TaskColors.h"
0038 
0039 #define MARG_LEFT 10
0040 #define MARG_TOP 70
0041 
0042 /* constructor */
0043 FractionRingWidget::FractionRingWidget()
0044 {
0045 #ifdef DEBUG
0046     qDebug() << QStringLiteral("constructor FractionRingWidget()");
0047 #endif
0048     // creating KActions, used by the FractionRingWidgetui.rc file
0049     setupActions();
0050     createGUI(QStringLiteral("FractionRingWidgetui.rc"));
0051 
0052     setCaption(i18n("Learning"));
0053 
0054     layout1 = new QGridLayout();
0055     layout1->setObjectName(QStringLiteral("layout1"));
0056 
0057     baseWidget = new QWidget();
0058     baseWidget->setObjectName(QStringLiteral("baseWidget"));
0059 
0060     setCentralWidget(baseWidget);
0061 
0062     interfaceWidget = new QWidget(this);
0063     interfaceWidget->setObjectName(QStringLiteral("interfaceWidget"));
0064     interfaceWidget->setFixedSize(QSize(270, 300));
0065 
0066     gridLayout = new QGridLayout();
0067     gridLayout->setObjectName(QStringLiteral("gridLayout"));
0068 
0069     // setup colors ---------------------------------------
0070     bgOutsideRing = QColor(242, 242, 242);
0071     bgOutsideMold = QColor(51, 51, 153);
0072     bgInsideRing = QColor(242, 242, 242);
0073     bgInsideMold = QColor(184, 18, 0);
0074     bgColor = QColor(240, 240, 250);
0075 
0076     colorListLeft[0] = QColor(0, 122, 209);
0077     colorListLeft[1] = QColor(70, 156, 207);
0078     colorListLeft[2] = QColor(121, 205, 241);
0079     colorListLeft[3] = QColor(174, 231, 255);
0080     colorListLeft[4] = QColor(117, 241, 255);
0081 
0082     colorListRight[0] = QColor(186, 18, 24);
0083     colorListRight[1] = QColor(217, 74, 81);
0084     colorListRight[2] = QColor(255, 114, 122);
0085     colorListRight[3] = QColor(230, 147, 152);
0086     colorListRight[4] = QColor(255, 186, 190);
0087 
0088     // 742, 520
0089     setFixedSize(QSize(742, 520));
0090 
0091     setPalette(QPalette(bgColor));
0092     setAutoFillBackground(true);
0093 
0094     move(50, 50);
0095 
0096     fractionWidget = new FractionPainter(this);
0097     fractionWidget->setObjectName(QStringLiteral("fractionWidget"));
0098 
0099     gridLayout->addWidget(fractionWidget, 0, 0, 1, 2, Qt::AlignBottom | Qt::AlignHCenter);
0100 
0101     // Info Labels -------------------------------------
0102     leftInfoLabel = new QLabel(this);
0103     leftInfoLabel->setObjectName(QStringLiteral("leftInfoLabel"));
0104     leftInfoLabel->setText(i18nc("Expands the ratio shown on the left by the given factor. Please note, expanding is not the same as multiplying. For example, expanding a ratio by 2 basically means multiplying the ratio with 2/2 and not with 2/1!", "Expand"));
0105     gridLayout->addWidget(leftInfoLabel, 1, 0, Qt::AlignCenter);
0106 
0107     rightInfoLabel = new QLabel(this);
0108     rightInfoLabel->setObjectName(QStringLiteral("rightInfoLabel"));
0109     rightInfoLabel->setText(i18nc("Expands the ratio shown on the left by the given factor. Please note, expanding is not the same as multiplying. For example, expanding a ratio by 2 basically means multiplying the ratio with 2/2 and not with 2/1!", "Expand"));
0110     gridLayout->addWidget(rightInfoLabel, 1, 1, Qt::AlignCenter);
0111 
0112     // SpinBox -----------------------------------------
0113     leftSpinBox = new QSpinBox(this);
0114     leftSpinBox->setObjectName(QStringLiteral("leftSpinBox"));
0115     leftSpinBox->setRange(1, 6);
0116     leftSpinBox->setMaximumWidth(50);
0117     leftSpinBox->setMinimumWidth(50);
0118     gridLayout->addWidget(leftSpinBox, 2, 0, Qt::AlignTop | Qt::AlignHCenter);
0119 
0120     rightSpinBox = new QSpinBox(this);
0121     rightSpinBox->setObjectName(QStringLiteral("rightSpinBox"));
0122     rightSpinBox->setRange(1, 6);
0123     rightSpinBox->setMaximumWidth(50);
0124     rightSpinBox->setMinimumWidth(50);
0125     gridLayout->addWidget(rightSpinBox, 2, 1, Qt::AlignTop | Qt::AlignHCenter);
0126 
0127     QObject::connect(leftSpinBox, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, &FractionRingWidget::slotLeftSpinBoxValueChanged);
0128     QObject::connect(rightSpinBox, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, &FractionRingWidget::slotRightSpinBoxValueChanged);
0129 
0130     // Reset Button ------------------------------------
0131     resetButton = new QPushButton(this);
0132     resetButton->setObjectName(QStringLiteral("resetButton"));
0133     resetButton->setText(i18n("New"));
0134     resetButton->setMaximumWidth(70);
0135     gridLayout->addWidget(resetButton, 3, 0, 1, 2, Qt::AlignCenter);
0136 
0137     QObject::connect(resetButton, &QPushButton::clicked, this, &FractionRingWidget::NewTask);
0138 
0139     // grid row settings --------------------------------
0140     gridLayout->setRowMinimumHeight(0, 185);
0141     gridLayout->setRowMinimumHeight(1, 10);
0142     gridLayout->setRowMinimumHeight(2, 15);
0143     gridLayout->setRowMinimumHeight(3, 85);
0144 
0145     interfaceWidget->setLayout(gridLayout);
0146     layout1->addWidget(interfaceWidget, 1, 0, Qt::AlignTop | Qt::AlignHCenter);
0147 
0148     // textedit -----------------------------------------
0149     textMsg = new QTextEdit(this);
0150     textMsg->setObjectName(QStringLiteral("textMsg"));
0151     textMsg->setFixedSize(QSize(250 - MARG_LEFT, 300));
0152     textMsg->setReadOnly(true);
0153     textMsg->setPalette(QPalette(QColor(255, 255, 255)));
0154     textMsg->setAutoFillBackground(true);
0155     layout1->addWidget(textMsg, 0, 1, 2, 1, Qt::AlignCenter);
0156 
0157     // grid row settings --------------------------------
0158     layout1->setRowMinimumHeight(0, MARG_TOP - 20);
0159     layout1->setRowMinimumHeight(1, 370 - MARG_TOP);
0160 
0161     // grid column settings -----------------------------
0162     layout1->setColumnMinimumWidth(0, 460 + MARG_LEFT);
0163     layout1->setColumnMinimumWidth(1, 282 - MARG_LEFT);
0164 
0165     baseWidget->setLayout(layout1);
0166 
0167     // Ratio ----------------------------------------------
0168     resetFraction(true);
0169 }
0170 
0171 /* destructor */
0172 FractionRingWidget::~FractionRingWidget()
0173 {
0174 #ifdef DEBUG
0175     qDebug() << QStringLiteral("destructor StatisticsBarWidget()");
0176 #endif
0177 }
0178 
0179 void FractionRingWidget::setupActions()
0180 {
0181 #ifdef DEBUG
0182     qDebug() << QStringLiteral("setupActions FractionRingWidget");
0183 #endif
0184     // new task action
0185     m_NewTaskAction = new QWidgetAction(this);
0186     m_NewTaskAction->setText(i18nc("@action opens a new question", "&New"));
0187     m_NewTaskAction->setIcon(QIcon::fromTheme(QStringLiteral("document-new")));
0188     actionCollection()->addAction(QStringLiteral("NewTask"), m_NewTaskAction);
0189     actionCollection()->setDefaultShortcuts(m_NewTaskAction, KStandardShortcut::shortcut(KStandardShortcut::New));
0190     connect(m_NewTaskAction, &QAction::triggered, this, &FractionRingWidget::NewTask);
0191 
0192     // back action
0193     m_BackAction  = new QWidgetAction(this);
0194     m_BackAction->setText(i18nc("@action go to the main screen", "Back"));
0195     m_BackAction->setIcon(QIcon::fromTheme(QStringLiteral("go-previous")));
0196     actionCollection()->addAction(QStringLiteral("Back"), m_BackAction);
0197     actionCollection()->setDefaultShortcuts(m_BackAction, KStandardShortcut::shortcut(KStandardShortcut::Back));
0198     connect(m_BackAction, &QAction::triggered, this, &FractionRingWidget::GoBack);
0199 
0200     // hint action (hide it as it doesn't exist here)
0201     m_HintAction  = new QAction(QIcon::fromTheme(QStringLiteral("games-hint")), i18nc("@action opens hint", "Hint"), this);
0202     actionCollection()->addAction(QStringLiteral("Hint"), m_HintAction);
0203     connect(m_HintAction, &QAction::triggered, this, &FractionRingWidget::Hint);
0204 
0205     // quit action
0206     KStandardAction::quit(this, SLOT(close()), actionCollection());
0207 
0208     KStandardAction::preferences(this,  SLOT(slotPrefs()), actionCollection());
0209 
0210     resize(QSize(725, 330).expandedTo(minimumSizeHint()));
0211     setupGUI(ToolBar | Keys);
0212 
0213     setAutoSaveSettings();
0214 }
0215 
0216 void FractionRingWidget::slotPrefs()
0217 {
0218 #ifdef DEBUG
0219     qDebug() << QStringLiteral("slotPrefs FractionRingWidget");
0220 #endif
0221     // do not show dialog twice
0222     if (KConfigDialog::showDialog(QStringLiteral("settings")))
0223         return;
0224 
0225     //KConfigDialog didn't find an instance of this dialog, so lets create it :
0226     KConfigDialog* configDialog = new KConfigDialog(this, QStringLiteral("settings"), SettingsClass::self());
0227 
0228     TaskColors * taskColors = new TaskColors(nullptr);
0229     configDialog->addPage(taskColors, i18n("Colors"), QStringLiteral("preferences-desktop-color"));
0230 
0231     // User edited the configuration - update your local copies of the
0232     // configuration data
0233     connect(configDialog, &KConfigDialog::settingsChanged, this, &FractionRingWidget::slotApplySettings);
0234     configDialog->show();
0235 
0236     return;
0237 }
0238 
0239 void FractionRingWidget::slotApplySettings()
0240 {
0241 #ifdef DEBUG
0242     qDebug() << QStringLiteral("slotApplySettings FractionRingWidget");
0243 #endif
0244     fractionWidget->update();
0245     return;
0246 }
0247 
0248 void FractionRingWidget::paintEvent(QPaintEvent *)
0249 {
0250     QPainter painter(this);
0251     painter.setRenderHint(QPainter::Antialiasing);
0252 
0253     QPen penBorder = QPen(Qt::white);
0254     penBorder.setWidth(2);
0255 
0256     painter.setPen(penBorder);
0257     painter.setBrush(bgOutsideRing);
0258 
0259     QRectF rect(MARG_LEFT, MARG_TOP, 440, 440);
0260 
0261     int startAngle = 90 * 16;
0262     int spanAngle = 0;
0263 
0264     // Outside Ring -------------------------------
0265     painter.drawEllipse(rect);
0266 
0267     painter.setPen(Qt::NoPen);
0268 
0269     for (int i = 0; i < rLeft.numerator(); i++) {
0270         painter.setBrush(colorListLeft[i]);
0271         spanAngle = ((16 * 360) << 8) / rLeft.denominator();
0272         painter.drawPie(rect, startAngle + ((i * spanAngle) >> 8), (spanAngle >> 8));
0273     }
0274 
0275     painter.setPen(penBorder);
0276     painter.setBrush(Qt::transparent);
0277     for (int i = 0; i < rLeft.denominator() * multLeft; i++) {
0278         spanAngle = ((16 * 360) << 8) / (rLeft.denominator() * multLeft);
0279         painter.drawPie(rect, startAngle + ((i * spanAngle) >> 8), (spanAngle >> 8));
0280     }
0281 
0282     // Spacer Ring -------------------------------
0283     painter.setPen(penBorder);
0284     painter.setBrush(bgColor);
0285     rect.setRect(MARG_LEFT + 27, MARG_TOP + 27, 386, 386);
0286     painter.drawEllipse(rect);
0287 
0288     // Inside Ring -------------------------------
0289     painter.setPen(penBorder);
0290     painter.setBrush(bgInsideRing);
0291     rect.setRect(MARG_LEFT + 33, MARG_TOP + 33, 374, 374);
0292     painter.drawEllipse(rect);
0293 
0294     painter.setPen(penBorder);
0295 
0296     for (int i = 0; i < rRight.numerator(); i++) {
0297         painter.setBrush(colorListRight[i]);
0298         spanAngle = ((16 * 360) << 8) / rRight.denominator();
0299         painter.drawPie(rect, startAngle + ((i * spanAngle) >> 8), (spanAngle >> 8));
0300     }
0301 
0302     painter.setPen(penBorder);
0303     painter.setBrush(Qt::transparent);
0304     for (int i = 0; i < rRight.denominator() * multRight; i++) {
0305         spanAngle = ((16 * 360) << 8) / ((rRight.denominator() * multRight));
0306         painter.drawPie(rect, startAngle + ((i * spanAngle) >> 8), (spanAngle >> 8));
0307     }
0308 
0309     // Inside Area -------------------------------
0310     painter.setPen(penBorder);
0311     painter.setBrush(QColor(230, 230, 240));
0312     rect.setRect(MARG_LEFT + 60, MARG_TOP + 60, 320, 320);
0313     painter.drawEllipse(rect);
0314 
0315     // left mold ---------------------------------
0316     QPen pen = QPen(bgOutsideMold);
0317     pen.setWidth(2);
0318     painter.setPen(pen);
0319     painter.setBrush(QColor(205, 225, 240, 150));
0320     rect.setRect(MARG_LEFT + 118, MARG_TOP + 120, 80, 160);
0321     painter.drawRect(rect);
0322 
0323     // right mold --------------------------------
0324     pen.setColor(bgInsideMold);
0325     painter.setPen(pen);
0326     painter.setBrush(QColor(240, 210, 160));
0327     rect.setRect(MARG_LEFT + 252, MARG_TOP + 120, 80, 160);
0328     painter.drawRect(rect);
0329 }
0330 
0331 void FractionRingWidget::GoBack()
0332 {
0333     kbruchApp = new AppMenuWidget();
0334     kbruchApp->show();
0335     close();
0336 }
0337 
0338 void FractionRingWidget::Hint()
0339 {
0340     textMsg->setVisible(!textMsg->isVisible());
0341 }
0342 
0343 void FractionRingWidget::slotLeftSpinBoxValueChanged(int value)
0344 {
0345     multLeft = value;
0346 
0347     QString msg;
0348     if (checkTask()) {
0349         msg = i18n("Now the parts of the inside ring and outside ring have "
0350                    "the same size, and we can add the fractions!\n\n"
0351                    "This is called finding the main denominator. "
0352                    "The main denominator divides the rings into equal parts.");
0353         if (rLeft.denominator() * value != MCM(rLeft.denominator(), rRight.denominator())) {
0354             msg += QStringLiteral("\n\n");
0355             msg += i18n("There is a smaller main denominator. Can you find it?");
0356         }
0357 
0358         textMsg->setText(msg);
0359     } else {
0360         msg = i18n("You have just expanded the first fraction by %1. "
0361                    "Our main goal is to make all parts be the same size. "
0362                    "Try expanding the first fraction by %2 and the second fraction by %3.",
0363                    value, rRight.denominator(), rLeft.denominator());
0364         textMsg->setText(msg);
0365     }
0366 
0367     update();
0368     fractionWidget->paintFraction(QStringLiteral("+"), rLeft, multLeft, rRight, multRight);
0369 }
0370 
0371 void FractionRingWidget::slotRightSpinBoxValueChanged(int value)
0372 {
0373     multRight = value;
0374 
0375     QString msg;
0376     if (checkTask()) {
0377         msg = i18n("Now the parts of the inside ring and outside ring have "
0378                    "the same size, and we can add the fractions!\n\n"
0379                    "This is called finding the main denominator. "
0380                    "The main denominator divides the rings into equal parts.");
0381         if (rRight.denominator() * value != MCM(rLeft.denominator(), rRight.denominator())) {
0382             msg += QStringLiteral("\n\n");
0383             msg += i18n("There is a smaller main denominator. Can you find it?");
0384         }
0385 
0386         textMsg->setText(msg);
0387     } else {
0388         msg = i18n("You have just expanded the second fraction by %1. "
0389                    "Our main goal is to make all parts be the same size. "
0390                    "Try expanding the first fraction by %2 and the second fraction by %3.",
0391                    value, rRight.denominator(), rLeft.denominator());
0392         textMsg->setText(msg);
0393     }
0394 
0395     update();
0396     fractionWidget->paintFraction(QStringLiteral("+"), rLeft, multLeft, rRight, multRight);
0397 }
0398 
0399 void FractionRingWidget::NewTask()
0400 {
0401     resetFraction(true);
0402 }
0403 
0404 void FractionRingWidget::resetFraction(bool flag = true)
0405 {
0406     int denominator = QRandomGenerator::global()->bounded(5) + 2;
0407     int numerator = QRandomGenerator::global()->bounded(denominator - 1) + 1;
0408 
0409     rLeft = Ratio(numerator, denominator, false);
0410 
0411     int denominator2 = QRandomGenerator::global()->bounded(5) + 2;
0412     while (denominator2 == denominator) {
0413         denominator2 = QRandomGenerator::global()->bounded(5) + 2;
0414     }
0415     int numerator2 = QRandomGenerator::global()->bounded(denominator2 - 1) + 1;
0416     rRight = Ratio(numerator2, denominator2, false);
0417 
0418     multLeft = 1;
0419     multRight = 1;
0420     leftSpinBox->setValue(1);
0421     rightSpinBox->setValue(1);
0422 
0423     // NOTE: Inserting denominator as %1, as first number is used to determine plural form,
0424     // and in these messages that should be the number next to "...painted parts".
0425     QString insert1 = i18ncp("Inserted as %1 in the message below.",
0426                              "%2 of 1 painted part.", "%2 of %1 painted parts.",
0427                              rLeft.denominator(), rLeft.numerator());
0428     QString insert2 = i18ncp("Inserted as %2 in the message below.",
0429                              "%2 of 1 painted part.", "%2 of %1 painted parts.",
0430                              rRight.denominator(), rRight.numerator());
0431     QString msg = i18nc("%1 and %2 are the two messages translated above.",
0432                         "The outside ring represents the left fraction. %1\n\n"
0433                         "The inside ring represents the right fraction. %2\n\n"
0434                         "The 'Expand' spinboxes expand each fraction. Try to change one of the spinbox values!",
0435                         insert1, insert2);
0436 
0437     textMsg->setText(msg);
0438 
0439     if (flag) {
0440         update();
0441         fractionWidget->paintFraction(QStringLiteral("+"), rLeft, multLeft, rRight, multRight);
0442     }
0443 }
0444 
0445 int FractionRingWidget::MCM(int a, int b)
0446 {
0447     return a * b / GCF(a, b);
0448 }
0449 
0450 int FractionRingWidget::GCF(int a, int b)
0451 {
0452     int tmp;
0453     if (a < b) {
0454         tmp = b;
0455         b = a;
0456         a = tmp;
0457     }
0458     tmp = a % b;
0459     while (tmp != 0) {
0460         a = b;
0461         b = tmp;
0462         tmp = a % b;
0463     }
0464     return b;
0465 }
0466 
0467 bool FractionRingWidget::checkTask()
0468 {
0469     return (rLeft.denominator() * multLeft == rRight.denominator() * multRight);
0470 }