File indexing completed on 2024-04-21 03:41:48
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 } 0471 0472 #include "moc_FractionRingWidget.cpp"