You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdeedu/kbruch/src/exerciseconvert.cpp

468 lines
15 KiB

/***************************************************************************
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 <tdeapplication.h>
#include <tdelocale.h>
#include <tdemessagebox.h>
#include <knumvalidator.h>
/* these includes are needed for TQt support */
#include <tqlabel.h>
#include <tqlayout.h>
#include <tqlineedit.h>
#include <tqpushbutton.h>
#include <tqtooltip.h>
#include <tqwhatsthis.h>
/* standard C++ library includes */
#include <stdlib.h>
#include "rationalwidget.h"
#include "resultwidget.h"
/* ----- public member functions ----- */
/* constructor */
ExerciseConvert::ExerciseConvert(TQWidget * parent, const char * name):
ExerciseBase(parent, name)
{
#ifdef DEBUG
kdDebug() << "constructor ExerciseConvert()" << endl;
#endif
/* create a new task */
TQApplication::setOverrideCursor(waitCursor); /* show the sand clock */
createTask();
TQApplication::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 TQWidget(this, "baseWidget");
baseGrid = new TQGridLayout(this, 1, 1, 0, -1, "baseGrid");
baseGrid->addWidget(baseWidget, 0, 0);
// this is a VBox
realLayout = new TQVBoxLayout(baseWidget, 5, 5, "realLayout");
// add a spacer at the top of the VBox
TQSpacerItem * v_spacer = new TQSpacerItem(1, 1);
realLayout->addItem(v_spacer);
// now a line holding the task, input fields and result
TQHBoxLayout * taskLineHBoxLayout = new TQHBoxLayout(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
TQVBoxLayout * inputLayout = new TQVBoxLayout(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 TQLineEdit(baseWidget, "numer_edit");
numer_edit->setValidator( valnum ); // use the int validator
TQToolTip::add(numer_edit, i18n("Enter the numerator of your result"));
inputLayout->addWidget(numer_edit);
/* add a line between the edit boxes */
edit_line = new TQFrame(baseWidget, "edit_line");
edit_line->setGeometry(TQRect(100, 100, 20, 20));
edit_line->setFrameStyle(TQFrame::HLine | TQFrame::Sunken);
inputLayout->addWidget(edit_line);
/* add input box so the user can enter denominator */
deno_edit = new TQLineEdit(baseWidget, "deno_edit");
deno_edit->setValidator( valnum ); // use the int validator
TQToolTip::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 TQLabel(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 TQSpacerItem(1, 1, TQSizePolicy::Expanding, TQSizePolicy::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 TQt4, you can set the direction
// of the layout to LTR (instead of inherit), but on TQt3
// 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 (TQApplication::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 TQSpacerItem(1, 1);
realLayout->addItem(v_spacer);
// the lower part of the VBox holds just a right aligned button
TQHBoxLayout * lowerHBox = new TQHBoxLayout(1, "lowerHBox");
realLayout->addLayout(lowerHBox);
lowerHBox->addStretch(100);
// the right aligned button
m_checkButton = new TQPushButton( baseWidget, "m_checkButton" );
m_checkButton->setText(i18n("&Check Task"));
m_checkButton->setDefault(true); // is the default button of the dialog
TQToolTip::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, TQt::AlignRight);
TQObject::connect(m_checkButton, TQT_SIGNAL(clicked()), this, TQT_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
TQToolTip::add(this, i18n("In this exercise you have to convert a number into a fraction."));
TQWhatsThis::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, TQt 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
((TQWidget *) 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 = TDEGlobal::locale()->formatNumber(0.5, 1);
m_periodStart = 2;
m_periodLength = 0;
m_result = ratio(1, 2);
break;
case 1 : m_number = TDEGlobal::locale()->formatNumber(0.3, 1);
m_periodStart = 2;
m_periodLength = 1;
m_result = ratio(1, 3);
break;
case 2 : m_number = TDEGlobal::locale()->formatNumber(0.6, 1);
m_periodStart = 2;
m_periodLength = 1;
m_result = ratio(2, 3);
break;
case 3 : m_number = TDEGlobal::locale()->formatNumber(0.25, 2);
m_periodStart = 2;
m_periodLength = 0;
m_result = ratio(1, 4);
break;
case 4 : m_number = TDEGlobal::locale()->formatNumber(0.75, 2);
m_periodStart = 2;
m_periodLength = 0;
m_result = ratio(3, 4);
break;
case 5 : m_number = TDEGlobal::locale()->formatNumber(0.2, 1);
m_periodStart = 2;
m_periodLength = 0;
m_result = ratio(1, 5);
break;
case 6 : m_number = TDEGlobal::locale()->formatNumber(0.4, 1);
m_periodStart = 2;
m_periodLength = 0;
m_result = ratio(2, 5);
break;
case 7 : m_number = TDEGlobal::locale()->formatNumber(0.6, 1);
m_periodStart = 2;
m_periodLength = 0;
m_result = ratio(3, 5);
break;
case 8 : m_number = TDEGlobal::locale()->formatNumber(0.8, 1);
m_periodStart = 2;
m_periodLength = 0;
m_result = ratio(4, 5);
break;
case 9 : m_number = TDEGlobal::locale()->formatNumber(0.16, 2);
m_periodStart = 3;
m_periodLength = 1;
m_result = ratio(1, 6);
break;
case 10 : m_number = TDEGlobal::locale()->formatNumber(0.142857, 6);
m_periodStart = 2;
m_periodLength = 6;
m_result = ratio(1, 7);
break;
case 11 : m_number = TDEGlobal::locale()->formatNumber(0.125, 3);
m_periodStart = 2;
m_periodLength = 0;
m_result = ratio(1, 8);
break;
case 12 : m_number = TDEGlobal::locale()->formatNumber(0.375, 3);
m_periodStart = 2;
m_periodLength = 0;
m_result = ratio(3, 8);
break;
case 13 : m_number = TDEGlobal::locale()->formatNumber(0.1, 1);
m_periodStart = 2;
m_periodLength = 1;
m_result = ratio(1, 9);
break;
case 14 : m_number = TDEGlobal::locale()->formatNumber(0.1, 1);
m_periodStart = 2;
m_periodLength = 0;
m_result = ratio(1, 10);
break;
case 15 : m_number = TDEGlobal::locale()->formatNumber(0.05, 2);
m_periodStart = 2;
m_periodLength = 0;
m_result = ratio(1, 20);
break;
case 16 : m_number = TDEGlobal::locale()->formatNumber(0.01, 2);
m_periodStart = 2;
m_periodLength = 0;
m_result = ratio(1, 100);
break;
case 17 : m_number = TDEGlobal::locale()->formatNumber(0.83, 2);
m_periodStart = 3;
m_periodLength = 1;
m_result = ratio(5, 6);
break;
default :
case 18 : m_number = TDEGlobal::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()
{
TQString tmp_str; /* to build a string for a label */
TQPalette pal;
TQColorGroup cg;
ratio entered_result;
// change the tooltip of the check button
TQToolTip::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(TQColorGroup::Foreground, TQColor(6, 179, 0));
pal.setActive(cg);
cg = pal.inactive();
cg.setColor(TQColorGroup::Foreground, TQColor(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(TQColorGroup::Foreground, TQColor(red));
pal.setActive(cg);
cg = pal.inactive();
cg.setColor(TQColorGroup::Foreground, TQColor(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
TQToolTip::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 */
TQApplication::setOverrideCursor(waitCursor); /* show the sand clock */
createTask();
TQApplication::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;
}