436 lines
14 KiB
C++
436 lines
14 KiB
C++
/***************************************************************************
|
|
* Copyright (C) 2003-2007 by Oliver Saal *
|
|
* osaal@gmx.de *
|
|
* http://www.oliver-saal.de/software/afutrainer/ *
|
|
* *
|
|
* 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. *
|
|
* *
|
|
* This program is distributed in the hope that it will be useful, *
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
|
* GNU General Public License for more details. *
|
|
* *
|
|
* You should have received a copy of the GNU General Public License *
|
|
* along with this program; if not, write to the *
|
|
* Free Software Foundation, Inc., *
|
|
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
|
|
***************************************************************************/
|
|
|
|
#include "catalog.h"
|
|
#include "dlglearn.h"
|
|
#include "dlgviewquestion.h"
|
|
#include "dlglearnassistant.h"
|
|
#include "tools.h"
|
|
|
|
#include <qmessagebox.h>
|
|
|
|
CDlgLearn::CDlgLearn (QWidget *pParent) : QDialog (pParent, Qt::WindowMaximizeButtonHint)
|
|
{
|
|
m_pCatalog = 0;
|
|
m_pChapter = 0;
|
|
m_pQuestion=0;
|
|
m_pLastQuestion=0;
|
|
m_uLastAnswerMask=0;
|
|
m_bHintsUsed=false;
|
|
m_uElapsedBeforeBreak=0;
|
|
#ifdef _DEBUG
|
|
m_bCheatEnable=true;
|
|
#else
|
|
m_bCheatEnable=false;
|
|
#endif
|
|
m_bAssistantEnable=true;
|
|
setupUi(this);
|
|
|
|
if (!m_bAssistantEnable)
|
|
gbAssistant->hide();
|
|
}
|
|
|
|
CDlgLearn::~CDlgLearn()
|
|
{
|
|
}
|
|
|
|
void CDlgLearn::go (CCatalog *pCatalog, CChapter *pChapter)
|
|
{
|
|
Q_ASSERT (pChapter != 0);
|
|
m_pCatalog = pCatalog;
|
|
setNewChapter(pChapter);
|
|
|
|
m_ds = pCatalog->dayStatistic(QDate::currentDate());
|
|
onUpdateDS();
|
|
|
|
exec();
|
|
}
|
|
|
|
void CDlgLearn::setNewChapter(CChapter *pChapter)
|
|
{
|
|
m_pQuestion=0;
|
|
m_bHintsUsed=false;
|
|
m_pChapter = pChapter;
|
|
m_listQuestion = m_pChapter->questionPool();
|
|
nextQuestion();
|
|
}
|
|
|
|
void CDlgLearn::updateStatistic()
|
|
{
|
|
const int w=60, h=16;
|
|
|
|
// CHAPTER STATISTICS
|
|
labChapter->setText (m_pChapter->text());
|
|
labChapterCount->setText(QString("%1").arg(m_listQuestion.size()));
|
|
labChapterVeryOften->setText(QString("%1").arg(m_pChapter->countQuestion(0)));
|
|
labChapterOften->setText(QString("%1").arg(m_pChapter->countQuestion(1)));
|
|
labChapterNormal->setText(QString("%1").arg(m_pChapter->countQuestion(2)));
|
|
labChapterRare->setText(QString("%1").arg(m_pChapter->countQuestion(3)));
|
|
labChapterVeryRare->setText(QString("%1").arg(m_pChapter->countQuestion(4)));
|
|
labChapterExtremeRare->setText(QString("%1").arg(m_pChapter->countQuestion(5)));
|
|
labChapterAvgText->setText(m_pChapter->levelAvgText());
|
|
labChapterAvgIcon->setPixmap(m_pChapter->levelAvgPixmap());
|
|
labChapterAvgIcon->setToolTip(QString("Kennzahl: %1").arg(m_pChapter->levelAvg(), 4, 'g', 2));
|
|
|
|
double dQuestionCount = m_listQuestion.size(), dPercent;
|
|
// dPercent = (double)m_listQuestion.size()/(double)m_pCatalog->countSubQuestion();
|
|
// labChapterCountBar->setPixmap(createProgressBar(w, h, dPercent));
|
|
// labChapterCountBar->setToolTip(QString("%1 %").arg(dPercent * 100, 0, 'f', 1));
|
|
dPercent = (double)m_pChapter->countQuestion(0)/dQuestionCount;
|
|
labChapterVeryOftenBar->setPixmap(createProgressBar(w, h, dPercent));
|
|
labChapterVeryOftenBar->setToolTip(QString("%1 %").arg(dPercent * 100, 0, 'f', 1));
|
|
dPercent = (double)m_pChapter->countQuestion(1)/dQuestionCount;
|
|
labChapterOftenBar->setPixmap(createProgressBar(w, h, dPercent));
|
|
labChapterOftenBar->setToolTip(QString("%1 %").arg(dPercent * 100, 0, 'f', 1));
|
|
dPercent = (double)m_pChapter->countQuestion(2)/dQuestionCount;
|
|
labChapterNormalBar->setPixmap(createProgressBar(w, h, dPercent));
|
|
labChapterNormalBar->setToolTip(QString("%1 %").arg(dPercent * 100, 0, 'f', 1));
|
|
dPercent = (double)m_pChapter->countQuestion(3)/dQuestionCount;
|
|
labChapterRareBar->setPixmap(createProgressBar(w, h, dPercent));
|
|
labChapterRareBar->setToolTip(QString("%1 %").arg(dPercent * 100, 0, 'f', 1));
|
|
dPercent = (double)m_pChapter->countQuestion(4)/dQuestionCount;
|
|
labChapterVeryRareBar->setPixmap(createProgressBar(w, h, dPercent));
|
|
labChapterVeryRareBar->setToolTip(QString("%1 %").arg(dPercent * 100, 0, 'f', 1));
|
|
dPercent = (double)m_pChapter->countQuestion(5)/dQuestionCount;
|
|
labChapterExtremeRareBar->setPixmap(createProgressBar(w, h, dPercent));
|
|
labChapterExtremeRareBar->setToolTip(QString("%1 %").arg(dPercent * 100, 0, 'f', 1));
|
|
|
|
// QUESTION STATISTICS
|
|
labQuestion->setText (tr("Frage %1").arg(m_pQuestion->id()));
|
|
labQuestionLevelIcon->setPixmap(m_pQuestion->levelPixmap());
|
|
labQuestionLevelText->setText(m_pQuestion->levelText());
|
|
if (m_pQuestion->clickedCorrectSuccessive() != 0)
|
|
labQuestionSuccessive->setText(tr("%1x richtig").arg(m_pQuestion->clickedCorrectSuccessive()));
|
|
else if (m_pQuestion->clickedWrongSuccessive() != 0)
|
|
labQuestionSuccessive->setText(tr("%1x falsch").arg(m_pQuestion->clickedWrongSuccessive()));
|
|
else
|
|
labQuestionSuccessive->setText(tr("--"));
|
|
|
|
labQuestionCount->setText(QString("%1").arg(m_pQuestion->clickedCount()));
|
|
labQuestionCorrect->setText(QString("%1").arg(m_pQuestion->clickedCorrect()));
|
|
labQuestionWrong->setText(QString("%1").arg(m_pQuestion->clickedWrong()));
|
|
QDateTime dtLastClicked = m_pQuestion->lastClicked();
|
|
if (!dtLastClicked.isValid())
|
|
labQuestionDate->setText("--");
|
|
else
|
|
{
|
|
labQuestionDate->setText(m_pQuestion->lastClickedText());
|
|
}
|
|
|
|
onUpdateDS();
|
|
}
|
|
|
|
void CDlgLearn::updateLearnAssistant()
|
|
{
|
|
QString strRecom1, strRecom2, str;
|
|
QPixmap pixRecom1, pixRecom2, pix;
|
|
|
|
// first recommendation
|
|
strRecom1 = m_pChapter->recommendationTextExtended(m_pCatalog);
|
|
if (m_pChapter->isRecommendedNow(m_pCatalog))
|
|
strRecom1 = "<font color='green'>" + strRecom1 + "</font>";
|
|
else
|
|
strRecom1 = "<font color='red'>" + strRecom1 + "</font>";
|
|
pixRecom1 = QPixmap (m_pChapter->recommendationIconName (m_pCatalog));
|
|
|
|
// second recommendation
|
|
strRecom2 = m_pChapter->recommendationTextExtended2(m_pCatalog);
|
|
pixRecom2 = QPixmap (m_pChapter->recommendationIconName (m_pChapter->recommendation2(), m_pCatalog));
|
|
|
|
|
|
if (m_pChapter->recommendation() == CChapter::RecommendationSubChapter && m_pChapter->recommendation2() == CChapter::RecommendationRepeatToday)
|
|
{
|
|
labRecommendationIcon->setPixmap (pixRecom2);
|
|
labRecommendation->setText("<font color='green'>" + strRecom2 + "</font>");
|
|
}
|
|
else
|
|
{
|
|
str = strRecom1;
|
|
if (!strRecom2.isEmpty())
|
|
str += "<p><font color='green'>" + strRecom2 + "</font>";
|
|
labRecommendationIcon->setPixmap (pixRecom1);
|
|
labRecommendation->setText(str);
|
|
}
|
|
}
|
|
|
|
CQuestion *CDlgLearn::findNextQuestion()
|
|
{
|
|
CQuestion *p = 0;
|
|
|
|
if (m_bAssistantEnable && m_pChapter->hasRecommendedQuestions())
|
|
{
|
|
// if (afu_random (0, 99) >= 0) // hier kann ggf. das Verhältnis zwischen RecommendedQuestion und Zufalls-Question ausgewählt werden
|
|
// {
|
|
p = findNextTargetQuestion();
|
|
// Wichtig: p kann hier immernoch 0 sein! Dies bedeutet, dass keine passende Frage im TargetPool gefunden wurde.
|
|
// }
|
|
}
|
|
|
|
if (p == 0)
|
|
p = findNextPoolQuestion();
|
|
|
|
return p;
|
|
}
|
|
|
|
CQuestion *CDlgLearn::findNextTargetQuestion()
|
|
{
|
|
CQuestion *pQuestion=0;
|
|
QList<CQuestion*> list = m_pChapter->recommendedQuestions();
|
|
int iSize = list.size();
|
|
unsigned uRnd=0;
|
|
|
|
if (iSize == 0) return 0;
|
|
if (iSize == 1)
|
|
{
|
|
if (list.at(0) == m_pQuestion)
|
|
// Regel: Niemals die gleiche Frage mehrfach hintereinander
|
|
// -> kann hier nicht erfüllt werden -> 0 zurückgeben -> hole Frage aus allg. Pool (siehe findNextQuestion)
|
|
return 0;
|
|
else
|
|
return list.at(0);
|
|
}
|
|
|
|
do
|
|
{
|
|
uRnd = afu_random (0, iSize-1);
|
|
pQuestion = list.at(uRnd);
|
|
}
|
|
while (m_pQuestion == pQuestion);
|
|
|
|
return pQuestion;
|
|
}
|
|
|
|
CQuestion *CDlgLearn::findNextPoolQuestion()
|
|
{
|
|
CQuestion *pQuestion=0;
|
|
unsigned uCountQuestion[LEVEL_MAX+1];
|
|
unsigned uRnd=0, uLevel=0;
|
|
int i=0;
|
|
unsigned uGewichtung[LEVEL_MAX+1], uEdge[LEVEL_MAX+1];
|
|
unsigned uRndMax = 0;
|
|
|
|
memset (uCountQuestion, 0, sizeof(unsigned)*(LEVEL_MAX+1));
|
|
for (i=0;i<m_listQuestion.size(); i++)
|
|
{
|
|
uLevel = m_listQuestion[i]->level();
|
|
Q_ASSERT(uLevel < LEVEL_MAX+1);
|
|
if (uLevel > LEVEL_MAX) continue;
|
|
uCountQuestion[uLevel]++;
|
|
}
|
|
|
|
uGewichtung[LEVEL_MAX] = 1;
|
|
uRndMax = 1;
|
|
for (i=LEVEL_MAX-1; i>=0; i--)
|
|
{
|
|
uRndMax += uGewichtung[i+1];
|
|
uGewichtung[i] = uRndMax;
|
|
}
|
|
|
|
for (i=0; i<=LEVEL_MAX; i++)
|
|
uGewichtung[i] *= uCountQuestion[i];
|
|
|
|
uRndMax=0;
|
|
for (i=0; i<=LEVEL_MAX; i++)
|
|
{
|
|
uLevel = LEVEL_MAX-i;
|
|
uRndMax += uGewichtung[uLevel];
|
|
uEdge[uLevel] = uRndMax;
|
|
}
|
|
|
|
do /* diese Schleife verhindert, dass eine Frage zweimal hintereinander kommt */
|
|
{
|
|
do /* diese Schleife wählt die Abfragehäufigkeit aus */
|
|
{
|
|
uRnd = afu_random (1, uRndMax);
|
|
uLevel = LEVEL_MAX;
|
|
while (uLevel != LEVEL_VERYOFTEN && uRnd > uEdge[uLevel])
|
|
uLevel--;
|
|
|
|
Q_ASSERT(uCountQuestion[uLevel] != 0); // Wenn hier assertion failed => algorithmus falsch
|
|
}
|
|
while (uCountQuestion[uLevel] == 0);
|
|
|
|
// Zufällige Frage bestimmen
|
|
uRnd = afu_random (1, uCountQuestion[uLevel]);
|
|
for (i=0; i<m_listQuestion.size()-1; i++)
|
|
if (m_listQuestion[i]->level() == uLevel && --uRnd == 0) break;
|
|
}
|
|
while (m_pQuestion == m_listQuestion[i] && m_listQuestion.size() > 1);
|
|
pQuestion = m_listQuestion[i];
|
|
return pQuestion;
|
|
}
|
|
|
|
void CDlgLearn::nextQuestion()
|
|
{
|
|
QString str, strCheat;
|
|
|
|
if (m_listQuestion.size() == 0)
|
|
{
|
|
QMessageBox::critical (this, "Fehler", "Es gibt keine Fragen, die gelernt werden könnten!");
|
|
accept();
|
|
return;
|
|
}
|
|
|
|
if (m_pQuestion)
|
|
{ // Save current question as new last question
|
|
pbLastQuestion->setEnabled(true);
|
|
m_pLastQuestion = m_pQuestion;
|
|
}
|
|
|
|
m_pQuestion = findNextQuestion();
|
|
|
|
// show answer
|
|
m_pQuestion->mixAnswers();
|
|
|
|
str = m_pQuestion->learnText(m_pCatalog, true, false);
|
|
if (m_bCheatEnable)
|
|
{
|
|
strCheat = "<p><font size=-1><i><b>Schummel-Modus</b><br>Richtige Antwort: " + CAnswerClicked::answerText (m_pQuestion->correctAnswerMask()) + "</i>";
|
|
strCheat += "<br>Empfohlene Wiederholung: " + m_pQuestion->repeatDateText();
|
|
strCheat += QString("<br>isLearningNew(): %1").arg(m_pQuestion->isLearningNew());
|
|
|
|
strCheat += "</font><p>";
|
|
}
|
|
teQuestion->setHtml(strCheat + str);
|
|
|
|
pbShowHelper->setEnabled (m_pCatalog->hasHints(m_pQuestion->id()));
|
|
m_bHintsUsed = false;
|
|
m_timeElapsed.restart();
|
|
updateStatistic();
|
|
updateLearnAssistant();
|
|
}
|
|
|
|
void CDlgLearn::on_pbAnswerA_clicked()
|
|
{
|
|
handleAnswer(0);
|
|
}
|
|
|
|
void CDlgLearn::on_pbAnswerB_clicked()
|
|
{
|
|
handleAnswer(1);
|
|
}
|
|
|
|
void CDlgLearn::on_pbAnswerC_clicked()
|
|
{
|
|
handleAnswer(2);
|
|
}
|
|
|
|
void CDlgLearn::on_pbAnswerD_clicked()
|
|
{
|
|
handleAnswer(3);
|
|
}
|
|
|
|
void CDlgLearn::handleAnswer(const int i)
|
|
{
|
|
unsigned uAnswerMask = 1<<i;
|
|
|
|
if (!m_pQuestion->isCorrectAnswer(uAnswerMask))
|
|
{
|
|
QMessageBox::information(this, tr("Hinweis"), m_pQuestion->correctionText(uAnswerMask));
|
|
}
|
|
m_uLastAnswerMask=uAnswerMask;
|
|
|
|
if (!m_bHintsUsed)
|
|
{
|
|
m_pQuestion->registerAnswerClicked(uAnswerMask, m_timeElapsed.elapsed() + m_uElapsedBeforeBreak);
|
|
|
|
bool bIsRecommendedOld = m_pChapter->isRecommendedNow(m_pCatalog) ;
|
|
|
|
m_pCatalog->updateStatistic();
|
|
m_ds = m_pCatalog->dayStatistic(QDate::currentDate());
|
|
|
|
if (bIsRecommendedOld && !m_pChapter->isRecommendedNow(m_pCatalog))
|
|
{
|
|
QMessageBox msgBox(this);
|
|
QPushButton *pbAssistant = msgBox.addButton(tr("Assistent"), QMessageBox::AcceptRole);
|
|
QPushButton *pbIgnore = msgBox.addButton(QMessageBox::Ignore);
|
|
QPushButton *pbExit = msgBox.addButton(QMessageBox::Cancel);
|
|
pbAssistant->setIcon(QIcon(":/icons/16x16/idea_info.png"));
|
|
pbAssistant->setToolTip(tr("Lern-Assistent aufrufen"));
|
|
pbIgnore->setToolTip(tr("Meldung ignorieren, dieses Kapitel weiterlernen"));
|
|
pbExit->setToolTip(tr("Lernmodus beenden"));
|
|
msgBox.setText(tr("<b>Herzlichen Glückwunsch!</b><p>Sie haben das heutige Lernziel für dieses Kapitel erreicht.<br>Bitte folgen Sie den weiteren Empfehlungen des Lernassistents."));
|
|
msgBox.setWindowTitle(tr("Ziel erreicht"));
|
|
|
|
msgBox.exec();
|
|
if (msgBox.clickedButton() == pbExit)
|
|
{
|
|
reject();
|
|
return;
|
|
}
|
|
else if (msgBox.clickedButton() == pbAssistant)
|
|
{
|
|
on_pbLearnAssistant_clicked();
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
nextQuestion();
|
|
}
|
|
|
|
void CDlgLearn::on_pbShowHelper_clicked()
|
|
{
|
|
m_bHintsUsed = true;
|
|
QString str = m_pQuestion->learnText(m_pCatalog, true, true);
|
|
teQuestion->setHtml(str);
|
|
}
|
|
|
|
void CDlgLearn::on_pbLastQuestion_clicked()
|
|
{
|
|
CDlgViewQuestion dlg(this);
|
|
dlg.go(m_pCatalog, m_pLastQuestion, m_uLastAnswerMask);
|
|
}
|
|
|
|
void CDlgLearn::on_pbSkip_clicked()
|
|
{
|
|
m_uLastAnswerMask=0;
|
|
nextQuestion();
|
|
}
|
|
|
|
void CDlgLearn::on_pbQuit_clicked()
|
|
{
|
|
m_pCatalog->updateStatistic();
|
|
accept();
|
|
}
|
|
|
|
void CDlgLearn::on_pbLearnAssistant_clicked()
|
|
{
|
|
CDlgLearnAssistant dlg(this);
|
|
CChapter *pChapter=0;
|
|
if (!dlg.setup(m_pCatalog))
|
|
{
|
|
QMessageBox::information(this, tr("Information"), tr("Derzeit gibt es keine Empfehlung des Lernassistentes."));
|
|
return;
|
|
}
|
|
if (dlg.exec() != QDialog::Accepted) return;
|
|
pChapter = dlg.selectedChapter();
|
|
if (pChapter == 0) return;
|
|
setNewChapter(pChapter);
|
|
}
|
|
|
|
void CDlgLearn::onUpdateDS()
|
|
{
|
|
labDSTime->setText(QString("%1 h %2 m").arg(m_ds.timeExpediture()/1000/3600).arg(m_ds.timeExpediture()/1000/60 % 60, 2, 10, QChar('0')));
|
|
labDSCount->setText(QString::number(m_ds.clickedCount()));
|
|
labDSCorrect->setText(QString::number(m_ds.clickedCorrect()));
|
|
labDSWrong->setText(QString::number(m_ds.clickedWrong()));
|
|
}
|