diff options
Diffstat (limited to 'kbruch/src/exerciseconvert.cpp')
-rw-r--r-- | kbruch/src/exerciseconvert.cpp | 467 |
1 files changed, 467 insertions, 0 deletions
diff --git a/kbruch/src/exerciseconvert.cpp b/kbruch/src/exerciseconvert.cpp new file mode 100644 index 00000000..1207ecbf --- /dev/null +++ b/kbruch/src/exerciseconvert.cpp @@ -0,0 +1,467 @@ +/*************************************************************************** + exerciseconvert.h + ------------------- + begin : 2004/06/04 + copyright : (C) 2004 by Sebastian Stein + email : seb.kde@hpfsc.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "exerciseconvert.h" +#include "exerciseconvert.moc" + +/* these includes are needed for KDE support */ +#include <kapplication.h> +#include <klocale.h> +#include <kmessagebox.h> +#include <knumvalidator.h> + +/* these includes are needed for Qt support */ +#include <qlabel.h> +#include <qlayout.h> +#include <qlineedit.h> +#include <qpushbutton.h> +#include <qtooltip.h> +#include <qwhatsthis.h> + +/* standard C++ library includes */ +#include <stdlib.h> + +#include "rationalwidget.h" +#include "resultwidget.h" + +/* ----- public member functions ----- */ + +/* constructor */ +ExerciseConvert::ExerciseConvert(QWidget * parent, const char * name): + ExerciseBase(parent, name) +{ +#ifdef DEBUG + kdDebug() << "constructor ExerciseConvert()" << endl; +#endif + + /* create a new task */ + QApplication::setOverrideCursor(waitCursor); /* show the sand clock */ + createTask(); + QApplication::restoreOverrideCursor(); /* show the normal cursor */ + + // the next thing to do on a button click would be to check the entered + // result + m_currentState = _CHECK_TASK; + + baseWidget = new QWidget(this, "baseWidget"); + baseGrid = new QGridLayout(this, 1, 1, 0, -1, "baseGrid"); + baseGrid->addWidget(baseWidget, 0, 0); + + // this is a VBox + realLayout = new QVBoxLayout(baseWidget, 5, 5, "realLayout"); + + // add a spacer at the top of the VBox + QSpacerItem * v_spacer = new QSpacerItem(1, 1); + realLayout->addItem(v_spacer); + + // now a line holding the task, input fields and result + QHBoxLayout * taskLineHBoxLayout = new QHBoxLayout(5, "taskLineHBoxLayout"); + realLayout->addLayout(taskLineHBoxLayout); + + // first left is the rational widget + m_rationalWidget = new RationalWidget(baseWidget, "m_rationalWidget", m_number, m_periodStart, m_periodLength); +// taskLineHBoxLayout->addWidget(m_rationalWidget); + + // now we have the input fields aligned in a VBox + QVBoxLayout * inputLayout = new QVBoxLayout(5, "inputLayout"); +// taskLineHBoxLayout->addLayout(inputLayout); + + // to validate, that the input is an int + KIntValidator *valnum = new KIntValidator( this ); + + /* add input box so the user can enter numerator */ + numer_edit = new QLineEdit(baseWidget, "numer_edit"); + numer_edit->setValidator( valnum ); // use the int validator + QToolTip::add(numer_edit, i18n("Enter the numerator of your result")); + inputLayout->addWidget(numer_edit); + + /* add a line between the edit boxes */ + edit_line = new QFrame(baseWidget, "edit_line"); + edit_line->setGeometry(QRect(100, 100, 20, 20)); + edit_line->setFrameStyle(QFrame::HLine | QFrame::Sunken); + inputLayout->addWidget(edit_line); + + /* add input box so the user can enter denominator */ + deno_edit = new QLineEdit(baseWidget, "deno_edit"); + deno_edit->setValidator( valnum ); // use the int validator + QToolTip::add(deno_edit, i18n("Enter the denominator of your result")); + inputLayout->addWidget(deno_edit); + + // next is the result widget + m_resultWidget = new ResultWidget(baseWidget, "m_resultWidget", m_result); +// taskLineHBoxLayout->addWidget(m_resultWidget); + m_resultWidget->hide(); + + // at the right end we have a label just showing CORRECT or WRONG + result_label = new QLabel(baseWidget, "result_lable"); + result_label->setText(i18n("WRONG")); +// taskLineHBoxLayout->addWidget(result_label); + result_label->hide(); + + // add another spacer in the middle of the VBox + v_spacer = new QSpacerItem(1, 1, QSizePolicy::Expanding, QSizePolicy::Minimum); +// taskLineHBoxLayout->addItem(v_spacer); + + // --- that is the end of the horizontal line --- + + // in RTL desktops, we still need to allign the + // execise to the left. On Qt4, you can set the direction + // of the layout to LTR (instead of inherit), but on Qt3 + // the only way of fixing it is inserting the widgets in reversed + // order to the layout. + // + // if you need help with this feel free to contact me - Diego <elcuco@kde.org> ) + // This should fix parts of bug #116831 + if (QApplication::reverseLayout()) + { + taskLineHBoxLayout->addItem(v_spacer); + taskLineHBoxLayout->addWidget(result_label); + taskLineHBoxLayout->addWidget(m_resultWidget); + taskLineHBoxLayout->addLayout(inputLayout); + taskLineHBoxLayout->addWidget(m_rationalWidget); + } + else + { + taskLineHBoxLayout->addWidget(m_rationalWidget); + taskLineHBoxLayout->addLayout(inputLayout); + taskLineHBoxLayout->addWidget(m_resultWidget); + taskLineHBoxLayout->addWidget(result_label); + taskLineHBoxLayout->addItem(v_spacer); + } + + // add another spacer in the middle of the VBox + v_spacer = new QSpacerItem(1, 1); + realLayout->addItem(v_spacer); + + // the lower part of the VBox holds just a right aligned button + QHBoxLayout * lowerHBox = new QHBoxLayout(1, "lowerHBox"); + realLayout->addLayout(lowerHBox); + lowerHBox->addStretch(100); + + // the right aligned button + m_checkButton = new QPushButton( baseWidget, "m_checkButton" ); + m_checkButton->setText(i18n("&Check Task")); + m_checkButton->setDefault(true); // is the default button of the dialog + QToolTip::add(m_checkButton, i18n("Click on this button to check your result. The button will not work if you have not entered a result yet.")); + lowerHBox->addWidget(m_checkButton, 1, Qt::AlignRight); + QObject::connect(m_checkButton, SIGNAL(clicked()), this, SLOT(slotCheckButtonClicked())); + + // that the user can start typing without moving the focus + numer_edit->setFocus(); + + // show the whole layout + baseWidget->show(); + + // add tooltip and qwhatsthis help to the widget + QToolTip::add(this, i18n("In this exercise you have to convert a number into a fraction.")); + QWhatsThis::add(this, i18n("In this exercise you have to convert a given number into a fraction by entering numerator and denominator. Do not forget to reduce the result!")); +} + +/* destructor */ +ExerciseConvert::~ExerciseConvert() +{ +#ifdef DEBUG + kdDebug() << "destructor ExerciseConvert()" << endl; +#endif + + /* no need to delete any child widgets, Qt does it by itself */ +} + +/** resets the current state, creates a new task and count the last task as + * wrong, if it wasn't solved (in _NEXT_TASK state) yet + * mainly used after changing the task parameters */ +void ExerciseConvert::forceNewTask() +{ +#ifdef DEBUG + kdDebug() << "forceNewTask ExerciseConvert()" << endl; +#endif + + if (m_currentState == _CHECK_TASK) + { + // emit the signal for wrong + signalExerciseSolvedWrong(); + } + m_currentState = _CHECK_TASK; + m_checkButton->setText(i18n("&Check Task")); + + // generate next task + (void) nextTask(); +} + + +/* ------ public slots ------ */ + +void ExerciseConvert::update() +{ + // call update of components + m_rationalWidget->updateAndRepaint(); + m_resultWidget->updateAndRepaint(); + + // update for itself + ((QWidget *) this)->update(); +} + + +/* ------ private member functions ------ */ + +void ExerciseConvert::createTask() +{ + // the tasks are hardcoded here; there are some algorithms to convert + // rational numbers to fractions, but it is not worth the effort here + switch(int((double(rand()) / RAND_MAX) * 18 + 1)) + { + case 0 : m_number = KGlobal::locale()->formatNumber(0.5, 1); + m_periodStart = 2; + m_periodLength = 0; + m_result = ratio(1, 2); + break; + case 1 : m_number = KGlobal::locale()->formatNumber(0.3, 1); + m_periodStart = 2; + m_periodLength = 1; + m_result = ratio(1, 3); + break; + case 2 : m_number = KGlobal::locale()->formatNumber(0.6, 1); + m_periodStart = 2; + m_periodLength = 1; + m_result = ratio(2, 3); + break; + case 3 : m_number = KGlobal::locale()->formatNumber(0.25, 2); + m_periodStart = 2; + m_periodLength = 0; + m_result = ratio(1, 4); + break; + case 4 : m_number = KGlobal::locale()->formatNumber(0.75, 2); + m_periodStart = 2; + m_periodLength = 0; + m_result = ratio(3, 4); + break; + case 5 : m_number = KGlobal::locale()->formatNumber(0.2, 1); + m_periodStart = 2; + m_periodLength = 0; + m_result = ratio(1, 5); + break; + case 6 : m_number = KGlobal::locale()->formatNumber(0.4, 1); + m_periodStart = 2; + m_periodLength = 0; + m_result = ratio(2, 5); + break; + case 7 : m_number = KGlobal::locale()->formatNumber(0.6, 1); + m_periodStart = 2; + m_periodLength = 0; + m_result = ratio(3, 5); + break; + case 8 : m_number = KGlobal::locale()->formatNumber(0.8, 1); + m_periodStart = 2; + m_periodLength = 0; + m_result = ratio(4, 5); + break; + case 9 : m_number = KGlobal::locale()->formatNumber(0.16, 2); + m_periodStart = 3; + m_periodLength = 1; + m_result = ratio(1, 6); + break; + case 10 : m_number = KGlobal::locale()->formatNumber(0.142857, 6); + m_periodStart = 2; + m_periodLength = 6; + m_result = ratio(1, 7); + break; + case 11 : m_number = KGlobal::locale()->formatNumber(0.125, 3); + m_periodStart = 2; + m_periodLength = 0; + m_result = ratio(1, 8); + break; + case 12 : m_number = KGlobal::locale()->formatNumber(0.375, 3); + m_periodStart = 2; + m_periodLength = 0; + m_result = ratio(3, 8); + break; + case 13 : m_number = KGlobal::locale()->formatNumber(0.1, 1); + m_periodStart = 2; + m_periodLength = 1; + m_result = ratio(1, 9); + break; + case 14 : m_number = KGlobal::locale()->formatNumber(0.1, 1); + m_periodStart = 2; + m_periodLength = 0; + m_result = ratio(1, 10); + break; + case 15 : m_number = KGlobal::locale()->formatNumber(0.05, 2); + m_periodStart = 2; + m_periodLength = 0; + m_result = ratio(1, 20); + break; + case 16 : m_number = KGlobal::locale()->formatNumber(0.01, 2); + m_periodStart = 2; + m_periodLength = 0; + m_result = ratio(1, 100); + break; + case 17 : m_number = KGlobal::locale()->formatNumber(0.83, 2); + m_periodStart = 3; + m_periodLength = 1; + m_result = ratio(5, 6); + break; + default : + case 18 : m_number = KGlobal::locale()->formatNumber(0.001, 3); + m_periodStart = 2; + m_periodLength = 0; + m_result = ratio(1, 1000); + break; + } + + return; +} + +/** - checks, if the user solved the task correctly + - emits signals if task was solved correctly or wrong */ +void ExerciseConvert::showResult() +{ + QString tmp_str; /* to build a string for a label */ + QPalette pal; + QColorGroup cg; + ratio entered_result; + + // change the tooltip of the check button + QToolTip::add(m_checkButton, i18n("Click on this button to get to the next task.")); + + numer_edit->setEnabled(false); + deno_edit->setEnabled(false); + + m_resultWidget->setResult(m_result); + m_resultWidget->show(); + + // an empty numerator field will be interpreted as 0 + if (numer_edit->text().isEmpty() == true) + numer_edit->setText("0"); + + // an empty denominator field will be interpreted as 1 + if (deno_edit->text().isEmpty() == true) + deno_edit->setText("1"); + + /* store the entered result to check it, but without reducing */ + entered_result.setNumerator(numer_edit->text().toInt(), false); + entered_result.setDenominator(deno_edit->text().toInt(), false); + + // check the entered result; 0/1 == 0/5 -> true, + // but 0/1 == 0/0 -> false + // a 0 for denominator is never allowed (always counted as wrong) + // + // we have to get the 0 directly from the input field, because + // Ratio::setDenominator(0, false) will set the denominator to 1 to ensure + // the Ratio is valid + if ( (deno_edit->text().toInt() != 0) && ((entered_result == m_result) || + (m_result.numerator() == 0 && entered_result.numerator() == 0)) ) + { + // emit the signal for correct + signalExerciseSolvedCorrect(); + + /* yes, the user entered the correct result */ + result_label->setText(i18n("CORRECT")); + pal = result_label->palette(); /* set green font color */ + cg = pal.active(); + cg.setColor(QColorGroup::Foreground, QColor(6, 179, 0)); + pal.setActive(cg); + cg = pal.inactive(); + cg.setColor(QColorGroup::Foreground, QColor(6, 179, 0)); + pal.setInactive(cg); + result_label->setPalette(pal); + result_label->show(); /* show the result at the end of the task */ + } else { + // emit the signal for wrong + signalExerciseSolvedWrong(); + + /* no, the user entered the wrong result */ + result_label->setText(i18n("WRONG")); + pal = result_label->palette(); /* set red font color */ + cg = pal.active(); + cg.setColor(QColorGroup::Foreground, QColor(red)); + pal.setActive(cg); + cg = pal.inactive(); + cg.setColor(QColorGroup::Foreground, QColor(red)); + pal.setInactive(cg); + result_label->setPalette(pal); + + result_label->show(); /* show the result at the end of the task */ + + // if the user entered a 0 for the denominator (division by 0) we have to + // get the 0 directly from the input field, because + // Ratio::setDenominator(0, true) will set the denominator to 1 to ensure + // the Ratio is valid + if (deno_edit->text().toInt() == 0) + { + KMessageBox::information(this, + i18n("You entered a 0 as the denominator. This means division by zero, which is not allowed. This task will be counted as not correctly solved.")); + } else { + /* maybe the entered ratio was not reduced */ + entered_result.reduce(); + if (entered_result == m_result) + KMessageBox::information(this, + i18n("You entered the correct result, but not reduced.\nAlways enter your results as reduced. This task will be counted as not correctly solved.")); + } + } /* if (entered_result == result) */ + + return; +} + +/** generate the next task and show it to the user */ +void ExerciseConvert::nextTask() +{ + // change the tooltip of the check button + QToolTip::add(m_checkButton, i18n("Click on this button to check your result. The button will not work if you have not entered a result yet.")); + + numer_edit->setEnabled(true); + deno_edit->setEnabled(true); + + result_label->hide(); /* do not show the result at the end of the task */ + m_resultWidget->hide(); + + /* clear user input */ + deno_edit->setText(""); + numer_edit->setText(""); + numer_edit->setFocus(); + + /* create a new task */ + QApplication::setOverrideCursor(waitCursor); /* show the sand clock */ + createTask(); + QApplication::restoreOverrideCursor(); /* show the normal cursor */ + + // update the task widget + m_rationalWidget->setRational(m_number, m_periodStart, m_periodLength); + + return; +} + +/* ------ private slots ------ */ + +void ExerciseConvert::slotCheckButtonClicked() +{ + if (m_currentState == _CHECK_TASK) + { + // if nothing has been entered by the user, we don't check the result yet + if (numer_edit->text().isEmpty() == true && deno_edit->text().isEmpty() == +true) + return; + m_currentState = _NEXT_TASK; + m_checkButton->setText(i18n("N&ext Task")); + (void) showResult(); + } else { + m_currentState = _CHECK_TASK; + m_checkButton->setText(i18n("&Check Task")); + (void) nextTask(); + } + + return; +} |