moved kdeaccessibility kdeaddons kdeadmin kdeartwork kdebindings kdeedu kdegames...
[kdeedu.git] / kbruch / src / exerciseconvert.cpp
blob1dad4b382e0e004a1588642bd57f63291e3eec7f
1 /***************************************************************************
2 exerciseconvert.h
3 -------------------
4 begin : 2004/06/04
5 copyright : (C) 2004 by Sebastian Stein
6 email : seb.kde@hpfsc.de
7 ***************************************************************************/
9 /***************************************************************************
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 ***************************************************************************/
18 #include "exerciseconvert.h"
19 #include "exerciseconvert.moc"
21 /* these includes are needed for KDE support */
22 #include <kapplication.h>
23 #include <klocale.h>
24 #include <kmessagebox.h>
25 #include <knumvalidator.h>
27 /* these includes are needed for Qt support */
28 #include <qlabel.h>
29 #include <qlayout.h>
30 #include <qlineedit.h>
31 #include <qpushbutton.h>
32 #include <qtooltip.h>
33 #include <qwhatsthis.h>
35 /* standard C++ library includes */
36 #include <stdlib.h>
38 #include "rationalwidget.h"
39 #include "resultwidget.h"
41 /* ----- public member functions ----- */
43 /* constructor */
44 ExerciseConvert::ExerciseConvert(QWidget * parent, const char * name):
45 ExerciseBase(parent, name)
47 #ifdef DEBUG
48 kdDebug() << "constructor ExerciseConvert()" << endl;
49 #endif
51 /* create a new task */
52 QApplication::setOverrideCursor(waitCursor); /* show the sand clock */
53 createTask();
54 QApplication::restoreOverrideCursor(); /* show the normal cursor */
56 // the next thing to do on a button click would be to check the entered
57 // result
58 m_currentState = _CHECK_TASK;
60 baseWidget = new QWidget(this, "baseWidget");
61 baseGrid = new QGridLayout(this, 1, 1, 0, -1, "baseGrid");
62 baseGrid->addWidget(baseWidget, 0, 0);
64 // this is a VBox
65 realLayout = new QVBoxLayout(baseWidget, 5, 5, "realLayout");
67 // add a spacer at the top of the VBox
68 QSpacerItem * v_spacer = new QSpacerItem(1, 1);
69 realLayout->addItem(v_spacer);
71 // now a line holding the task, input fields and result
72 QHBoxLayout * taskLineHBoxLayout = new QHBoxLayout(5, "taskLineHBoxLayout");
73 realLayout->addLayout(taskLineHBoxLayout);
75 // first left is the rational widget
76 m_rationalWidget = new RationalWidget(baseWidget, "m_rationalWidget", m_number, m_periodStart, m_periodLength);
77 taskLineHBoxLayout->addWidget(m_rationalWidget);
79 // now we have the input fields aligned in a VBox
80 QVBoxLayout * inputLayout = new QVBoxLayout(5, "inputLayout");
81 taskLineHBoxLayout->addLayout(inputLayout);
83 // to validate, that the input is an int
84 KIntValidator *valnum = new KIntValidator( this );
86 /* add input box so the user can enter numerator */
87 numer_edit = new QLineEdit(baseWidget, "numer_edit");
88 numer_edit->setValidator( valnum ); // use the int validator
89 QToolTip::add(numer_edit, i18n("Enter the numerator of your result"));
90 inputLayout->addWidget(numer_edit);
92 /* add a line between the edit boxes */
93 edit_line = new QFrame(baseWidget, "edit_line");
94 edit_line->setGeometry(QRect(100, 100, 20, 20));
95 edit_line->setFrameStyle(QFrame::HLine | QFrame::Sunken);
96 inputLayout->addWidget(edit_line);
98 /* add input box so the user can enter denominator */
99 deno_edit = new QLineEdit(baseWidget, "deno_edit");
100 deno_edit->setValidator( valnum ); // use the int validator
101 QToolTip::add(deno_edit, i18n("Enter the denominator of your result"));
102 inputLayout->addWidget(deno_edit);
104 // next is the result widget
105 m_resultWidget = new ResultWidget(baseWidget, "m_resultWidget", m_result);
106 taskLineHBoxLayout->addWidget(m_resultWidget);
107 m_resultWidget->hide();
109 // at the right end we have a label just showing CORRECT or WRONG
110 result_label = new QLabel(baseWidget, "result_lable");
111 result_label->setText(i18n("WRONG"));
112 taskLineHBoxLayout->addWidget(result_label);
113 result_label->hide();
115 // add another spacer in the middle of the VBox
116 v_spacer = new QSpacerItem(1, 1, QSizePolicy::Expanding, QSizePolicy::Minimum);
117 taskLineHBoxLayout->addItem(v_spacer);
119 // --- that is the end of the horizontal line ---
121 // add another spacer in the middle of the VBox
122 v_spacer = new QSpacerItem(1, 1);
123 realLayout->addItem(v_spacer);
125 // the lower part of the VBox holds just a right aligned button
126 QHBoxLayout * lowerHBox = new QHBoxLayout(1, "lowerHBox");
127 realLayout->addLayout(lowerHBox);
128 lowerHBox->addStretch(100);
130 // the right aligned button
131 m_checkButton = new QPushButton( baseWidget, "m_checkButton" );
132 m_checkButton->setText(i18n("&Check Task"));
133 m_checkButton->setDefault(true); // is the default button of the dialog
134 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."));
135 lowerHBox->addWidget(m_checkButton, 1, Qt::AlignRight);
136 QObject::connect(m_checkButton, SIGNAL(clicked()), this, SLOT(slotCheckButtonClicked()));
138 // that the user can start typing without moving the focus
139 numer_edit->setFocus();
141 // show the whole layout
142 baseWidget->show();
144 // add tooltip and qwhatsthis help to the widget
145 QToolTip::add(this, i18n("In this exercise you have to convert a number into a fraction."));
146 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!"));
149 /* destructor */
150 ExerciseConvert::~ExerciseConvert()
152 #ifdef DEBUG
153 kdDebug() << "destructor ExerciseConvert()" << endl;
154 #endif
156 /* no need to delete any child widgets, Qt does it by itself */
159 /** resets the current state, creates a new task and count the last task as
160 * wrong, if it wasn't solved (in _NEXT_TASK state) yet
161 * mainly used after changing the task parameters */
162 void ExerciseConvert::forceNewTask()
164 #ifdef DEBUG
165 kdDebug() << "forceNewTask ExerciseConvert()" << endl;
166 #endif
168 if (m_currentState == _CHECK_TASK)
170 // emit the signal for wrong
171 signalExerciseSolvedWrong();
173 m_currentState = _CHECK_TASK;
174 m_checkButton->setText(i18n("&Check Task"));
176 // generate next task
177 (void) nextTask();
181 /* ------ public slots ------ */
183 void ExerciseConvert::update()
185 // call update of components
186 m_rationalWidget->updateAndRepaint();
187 m_resultWidget->updateAndRepaint();
189 // update for itself
190 ((QWidget *) this)->update();
194 /* ------ private member functions ------ */
196 void ExerciseConvert::createTask()
198 // the tasks are hardcoded here; there are some algorithms to convert
199 // rational numbers to fractions, but it is not worth the effort here
200 switch(int((double(rand()) / RAND_MAX) * 18 + 1))
202 case 0 : m_number = KGlobal::locale()->formatNumber(0.5, 1);
203 m_periodStart = 2;
204 m_periodLength = 0;
205 m_result = ratio(1, 2);
206 break;
207 case 1 : m_number = KGlobal::locale()->formatNumber(0.3, 1);
208 m_periodStart = 2;
209 m_periodLength = 1;
210 m_result = ratio(1, 3);
211 break;
212 case 2 : m_number = KGlobal::locale()->formatNumber(0.6, 1);
213 m_periodStart = 2;
214 m_periodLength = 1;
215 m_result = ratio(2, 3);
216 break;
217 case 3 : m_number = KGlobal::locale()->formatNumber(0.25, 2);
218 m_periodStart = 2;
219 m_periodLength = 0;
220 m_result = ratio(1, 4);
221 break;
222 case 4 : m_number = KGlobal::locale()->formatNumber(0.75, 2);
223 m_periodStart = 2;
224 m_periodLength = 0;
225 m_result = ratio(3, 4);
226 break;
227 case 5 : m_number = KGlobal::locale()->formatNumber(0.2, 1);
228 m_periodStart = 2;
229 m_periodLength = 0;
230 m_result = ratio(1, 5);
231 break;
232 case 6 : m_number = KGlobal::locale()->formatNumber(0.4, 1);
233 m_periodStart = 2;
234 m_periodLength = 0;
235 m_result = ratio(2, 5);
236 break;
237 case 7 : m_number = KGlobal::locale()->formatNumber(0.6, 1);
238 m_periodStart = 2;
239 m_periodLength = 0;
240 m_result = ratio(3, 5);
241 break;
242 case 8 : m_number = KGlobal::locale()->formatNumber(0.8, 1);
243 m_periodStart = 2;
244 m_periodLength = 0;
245 m_result = ratio(4, 5);
246 break;
247 case 9 : m_number = KGlobal::locale()->formatNumber(0.16, 2);
248 m_periodStart = 3;
249 m_periodLength = 1;
250 m_result = ratio(1, 6);
251 break;
252 case 10 : m_number = KGlobal::locale()->formatNumber(0.142857, 6);
253 m_periodStart = 2;
254 m_periodLength = 6;
255 m_result = ratio(1, 7);
256 break;
257 case 11 : m_number = KGlobal::locale()->formatNumber(0.125, 3);
258 m_periodStart = 2;
259 m_periodLength = 0;
260 m_result = ratio(1, 8);
261 break;
262 case 12 : m_number = KGlobal::locale()->formatNumber(0.375, 3);
263 m_periodStart = 2;
264 m_periodLength = 0;
265 m_result = ratio(3, 8);
266 break;
267 case 13 : m_number = KGlobal::locale()->formatNumber(0.1, 1);
268 m_periodStart = 2;
269 m_periodLength = 1;
270 m_result = ratio(1, 9);
271 break;
272 case 14 : m_number = KGlobal::locale()->formatNumber(0.1, 1);
273 m_periodStart = 2;
274 m_periodLength = 0;
275 m_result = ratio(1, 10);
276 break;
277 case 15 : m_number = KGlobal::locale()->formatNumber(0.05, 2);
278 m_periodStart = 2;
279 m_periodLength = 0;
280 m_result = ratio(1, 20);
281 break;
282 case 16 : m_number = KGlobal::locale()->formatNumber(0.01, 2);
283 m_periodStart = 2;
284 m_periodLength = 0;
285 m_result = ratio(1, 100);
286 break;
287 case 17 : m_number = KGlobal::locale()->formatNumber(0.83, 2);
288 m_periodStart = 3;
289 m_periodLength = 1;
290 m_result = ratio(5, 6);
291 break;
292 default :
293 case 18 : m_number = KGlobal::locale()->formatNumber(0.001, 3);
294 m_periodStart = 2;
295 m_periodLength = 0;
296 m_result = ratio(1, 1000);
297 break;
300 return;
303 /** - checks, if the user solved the task correctly
304 - emits signals if task was solved correctly or wrong */
305 void ExerciseConvert::showResult()
307 QString tmp_str; /* to build a string for a label */
308 QPalette pal;
309 QColorGroup cg;
310 ratio entered_result;
312 // change the tooltip of the check button
313 QToolTip::add(m_checkButton, i18n("Click on this button to get to the next task."));
315 numer_edit->setEnabled(false);
316 deno_edit->setEnabled(false);
318 m_resultWidget->setResult(m_result);
319 m_resultWidget->show();
321 // an empty numerator field will be interpreted as 0
322 if (numer_edit->text().isEmpty() == true)
323 numer_edit->setText("0");
325 // an empty denominator field will be interpreted as 1
326 if (deno_edit->text().isEmpty() == true)
327 deno_edit->setText("1");
329 /* store the entered result to check it, but without reducing */
330 entered_result.setNumerator(numer_edit->text().toInt(), false);
331 entered_result.setDenominator(deno_edit->text().toInt(), false);
333 // check the entered result; 0/1 == 0/5 -> true,
334 // but 0/1 == 0/0 -> false
335 // a 0 for denominator is never allowed (always counted as wrong)
337 // we have to get the 0 directly from the input field, because
338 // Ratio::setDenominator(0, false) will set the denominator to 1 to ensure
339 // the Ratio is valid
340 if ( (deno_edit->text().toInt() != 0) && ((entered_result == m_result) ||
341 (m_result.numerator() == 0 && entered_result.numerator() == 0)) )
343 // emit the signal for correct
344 signalExerciseSolvedCorrect();
346 /* yes, the user entered the correct result */
347 result_label->setText(i18n("CORRECT"));
348 pal = result_label->palette(); /* set green font color */
349 cg = pal.active();
350 cg.setColor(QColorGroup::Foreground, QColor(6, 179, 0));
351 pal.setActive(cg);
352 cg = pal.inactive();
353 cg.setColor(QColorGroup::Foreground, QColor(6, 179, 0));
354 pal.setInactive(cg);
355 result_label->setPalette(pal);
356 result_label->show(); /* show the result at the end of the task */
357 } else {
358 // emit the signal for wrong
359 signalExerciseSolvedWrong();
361 /* no, the user entered the wrong result */
362 result_label->setText(i18n("WRONG"));
363 pal = result_label->palette(); /* set red font color */
364 cg = pal.active();
365 cg.setColor(QColorGroup::Foreground, QColor(red));
366 pal.setActive(cg);
367 cg = pal.inactive();
368 cg.setColor(QColorGroup::Foreground, QColor(red));
369 pal.setInactive(cg);
370 result_label->setPalette(pal);
372 result_label->show(); /* show the result at the end of the task */
374 // if the user entered a 0 for the denominator (division by 0) we have to
375 // get the 0 directly from the input field, because
376 // Ratio::setDenominator(0, true) will set the denominator to 1 to ensure
377 // the Ratio is valid
378 if (deno_edit->text().toInt() == 0)
380 KMessageBox::information(this,
381 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."));
382 } else {
383 /* maybe the entered ratio was not reduced */
384 entered_result.reduce();
385 if (entered_result == m_result)
386 KMessageBox::information(this,
387 i18n("You entered the correct result, but not reduced.\nAlways enter your results as reduced. This task will be counted as not correctly solved."));
389 } /* if (entered_result == result) */
391 return;
394 /** generate the next task and show it to the user */
395 void ExerciseConvert::nextTask()
397 // change the tooltip of the check button
398 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."));
400 numer_edit->setEnabled(true);
401 deno_edit->setEnabled(true);
403 result_label->hide(); /* do not show the result at the end of the task */
404 m_resultWidget->hide();
406 /* clear user input */
407 deno_edit->setText("");
408 numer_edit->setText("");
409 numer_edit->setFocus();
411 /* create a new task */
412 QApplication::setOverrideCursor(waitCursor); /* show the sand clock */
413 createTask();
414 QApplication::restoreOverrideCursor(); /* show the normal cursor */
416 // update the task widget
417 m_rationalWidget->setRational(m_number, m_periodStart, m_periodLength);
419 return;
422 /* ------ private slots ------ */
424 void ExerciseConvert::slotCheckButtonClicked()
426 if (m_currentState == _CHECK_TASK)
428 // if nothing has been entered by the user, we don't check the result yet
429 if (numer_edit->text().isEmpty() == true && deno_edit->text().isEmpty() ==
430 true)
431 return;
432 m_currentState = _NEXT_TASK;
433 m_checkButton->setText(i18n("N&ext Task"));
434 (void) showResult();
435 } else {
436 m_currentState = _CHECK_TASK;
437 m_checkButton->setText(i18n("&Check Task"));
438 (void) nextTask();
441 return;