diff --git a/answer.cpp b/answer.cpp index 67602b4..af17984 100644 --- a/answer.cpp +++ b/answer.cpp @@ -1,119 +1,119 @@ -/*************************************************************************** - * 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 "answer.h" - -#include - - -CAnswer::CAnswer(const QString& strText, const bool bCorrect) -{ - m_strText = strText; - m_bIsCorrect = bCorrect; -} - -void CAnswer::clear() -{ - m_strText.clear(); - m_bIsCorrect = false; -} - -QString CAnswerClicked::tr (const char *sourceText, const char *comment) -{ - return QCoreApplication::translate("CAnswerClicked", sourceText, comment); -} - -void CAnswerClicked::clear() -{ - m_dt = QDateTime::currentDateTime(); - m_uAnswer=0; - m_uNeededTime=0; -} - -bool CAnswerClicked::operator < (const CAnswerClicked& ac) const -{ - return m_dt < ac.m_dt; -} - -QString CAnswerClicked::answerText(const unsigned uAnswer) -{ -QString strRet; - for (int i=0; i<32; i++) - { - if (uAnswer & (1<& listAnswer) const -{ -unsigned uMask=0; - for (int i=0; i + + +CAnswer::CAnswer(const QString& strText, const bool bCorrect) +{ + m_strText = strText; + m_bIsCorrect = bCorrect; +} + +void CAnswer::clear() +{ + m_strText.clear(); + m_bIsCorrect = false; +} + +QString CAnswerClicked::tr (const char *sourceText, const char *comment) +{ + return QCoreApplication::translate("CAnswerClicked", sourceText, comment); +} + +void CAnswerClicked::clear() +{ + m_dt = QDateTime::currentDateTime(); + m_uAnswer=0; + m_uNeededTime=0; +} + +bool CAnswerClicked::operator < (const CAnswerClicked& ac) const +{ + return m_dt < ac.m_dt; +} + +QString CAnswerClicked::answerText(const unsigned uAnswer) +{ +QString strRet; + for (int i=0; i<32; i++) + { + if (uAnswer & (1<& listAnswer) const +{ +unsigned uMask=0; + for (int i=0; i -#include -#include -#include - -//! Die Klasse CAnswer speichert eine einzelne Antwort auf eine Frage -/*! -Das Schreiben und Lesen von XML-Daten wird von der Klasse CQuestion übernommen -*/ - -class CAnswer -{ -public: - //! Standard-Konstruktor - /*! Initialisiert die Klasse, indem die Funktion clear() aufgerufen wird. */ - CAnswer() { clear(); } - - //! Konstruktor inkl. setzen eines Textes und ob die Antwort richtig ist - /*! - \param strText Antworttext - \param bCorrect true: Die Antwort ist korrekt - \sa m_strText, m_bIsCorrect - */ - CAnswer(const QString& strText, const bool bCorrect); - - //! Standard-Destruktor - /*! In der Standard-Implementierung tut der Destruktor nichts. */ - ~CAnswer() {} - - //! Zurücksetzen aller Werte - /*! Es werden alle Daten der Antwort gelöscht. */ - void clear(); - //! Überprüfen, ob die Klasse eine Antwort enthält - /*! \return true: Die Klasse ist leer und enthält keine Antwort */ - inline bool isEmpty() const { return m_strText.isEmpty(); } - - //! Auslesen, ob diese Antwort richtig ist - /*! \return true: Die Antwort ist richtig */ - inline bool isCorrect() const { return m_bIsCorrect; } - //! Auslesen des Antworttextes - /*! \return Antworttext */ - inline QString text() const { return m_strText; } - //! Setzen, ob diese Antwort richtig ist - /*! - \param bCorrect true: Die Antwort ist korrekt - \sa m_bIsCorrect - */ - inline void setCorrect (const bool bCorrect) { m_bIsCorrect = bCorrect; } - //! Setzen des Antworttextes - /*! - \param strText Antworttext - \sa m_strText - */ - inline void setText(const QString& strText) { m_strText = strText; } - - //! Anhängen eines Textes an den Antworttext - /*! - \param strText Anzuhängender Text - \sa m_strText - */ - inline void appendText(const QString& strText) { m_strText += strText; } - -protected: - //! Antworttext - /*! - Der Antworttext darf HTML-Code und HTML-Verweise auf Grafiken/Bilder enthalten. Bei Verweisen ist zu achten, dass kein Verzeichnis angegeben werden darf! - - Default: leer - */ - QString m_strText; - //! Richtige oder falsche Antwort - /*! Default: false */ - bool m_bIsCorrect; -}; - -//! Die Klasse CAnswerClicked speichert eine einzelne Beantwortung des Benutzers -/*! -*/ - -class CAnswerClicked -{ -public: - //! Standard-Konstruktor - /*! Initialisiert die Klasse, indem die Funktion clear() aufgerufen wird. */ - CAnswerClicked () { clear(); } - CAnswerClicked (const unsigned uAnswer, const unsigned uNeededTime) - { m_uAnswer = uAnswer; m_dt = QDateTime::currentDateTime(); m_uNeededTime = uNeededTime; } - //! Standard-Destruktor - /*! In der Standard-Implementierung tut der Destruktor nichts. */ - ~CAnswerClicked () {} - - //! Zurücksetzen aller Werte - /*! Es werden alle Daten auf Default-Werte zurückgesetzt. */ - void clear(); - - inline QDateTime dateTime() const { return m_dt; } - inline unsigned neededTime() const { return m_uNeededTime; } - inline unsigned answer() const { return m_uAnswer; } - static QString answerText(const unsigned uAnswer); - QString answerText() const; - QString neededTimeText() const; - - bool isCorrect(const QList& listAnswer) const; - - bool load (QDomElement elem); - void save (QDomElement& parent, QDomDocument& doc) const; - - static QString tr (const char *sourceText, const char *comment=0); - - bool operator < (const CAnswerClicked& ac) const; - -protected: - QDateTime m_dt; //!< Zeitstempel der Antwort - unsigned m_uAnswer; //!< Antwort-Bitmaske - unsigned m_uNeededTime; //!< Benötigte Zeit in ms -}; - -#endif +/*************************************************************************** + * 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. * + ***************************************************************************/ + +#ifndef ANSWER_H +#define ANSWER_H + +#include +#include +#include +#include + +//! Die Klasse CAnswer speichert eine einzelne Antwort auf eine Frage +/*! +Das Schreiben und Lesen von XML-Daten wird von der Klasse CQuestion übernommen +*/ + +class CAnswer +{ +public: + //! Standard-Konstruktor + /*! Initialisiert die Klasse, indem die Funktion clear() aufgerufen wird. */ + CAnswer() { clear(); } + + //! Konstruktor inkl. setzen eines Textes und ob die Antwort richtig ist + /*! + \param strText Antworttext + \param bCorrect true: Die Antwort ist korrekt + \sa m_strText, m_bIsCorrect + */ + CAnswer(const QString& strText, const bool bCorrect); + + //! Standard-Destruktor + /*! In der Standard-Implementierung tut der Destruktor nichts. */ + ~CAnswer() {} + + //! Zurücksetzen aller Werte + /*! Es werden alle Daten der Antwort gelöscht. */ + void clear(); + //! Überprüfen, ob die Klasse eine Antwort enthält + /*! \return true: Die Klasse ist leer und enthält keine Antwort */ + inline bool isEmpty() const { return m_strText.isEmpty(); } + + //! Auslesen, ob diese Antwort richtig ist + /*! \return true: Die Antwort ist richtig */ + inline bool isCorrect() const { return m_bIsCorrect; } + //! Auslesen des Antworttextes + /*! \return Antworttext */ + inline QString text() const { return m_strText; } + //! Setzen, ob diese Antwort richtig ist + /*! + \param bCorrect true: Die Antwort ist korrekt + \sa m_bIsCorrect + */ + inline void setCorrect (const bool bCorrect) { m_bIsCorrect = bCorrect; } + //! Setzen des Antworttextes + /*! + \param strText Antworttext + \sa m_strText + */ + inline void setText(const QString& strText) { m_strText = strText; } + + //! Anhängen eines Textes an den Antworttext + /*! + \param strText Anzuhängender Text + \sa m_strText + */ + inline void appendText(const QString& strText) { m_strText += strText; } + +protected: + //! Antworttext + /*! + Der Antworttext darf HTML-Code und HTML-Verweise auf Grafiken/Bilder enthalten. Bei Verweisen ist zu achten, dass kein Verzeichnis angegeben werden darf! + + Default: leer + */ + QString m_strText; + //! Richtige oder falsche Antwort + /*! Default: false */ + bool m_bIsCorrect; +}; + +//! Die Klasse CAnswerClicked speichert eine einzelne Beantwortung des Benutzers +/*! +*/ + +class CAnswerClicked +{ +public: + //! Standard-Konstruktor + /*! Initialisiert die Klasse, indem die Funktion clear() aufgerufen wird. */ + CAnswerClicked () { clear(); } + CAnswerClicked (const unsigned uAnswer, const unsigned uNeededTime) + { m_uAnswer = uAnswer; m_dt = QDateTime::currentDateTime(); m_uNeededTime = uNeededTime; } + //! Standard-Destruktor + /*! In der Standard-Implementierung tut der Destruktor nichts. */ + ~CAnswerClicked () {} + + //! Zurücksetzen aller Werte + /*! Es werden alle Daten auf Default-Werte zurückgesetzt. */ + void clear(); + + inline QDateTime dateTime() const { return m_dt; } + inline unsigned neededTime() const { return m_uNeededTime; } + inline unsigned answer() const { return m_uAnswer; } + static QString answerText(const unsigned uAnswer); + QString answerText() const; + QString neededTimeText() const; + + bool isCorrect(const QList& listAnswer) const; + + bool load (QDomElement elem); + void save (QDomElement& parent, QDomDocument& doc) const; + + static QString tr (const char *sourceText, const char *comment=0); + + bool operator < (const CAnswerClicked& ac) const; + +protected: + QDateTime m_dt; //!< Zeitstempel der Antwort + unsigned m_uAnswer; //!< Antwort-Bitmaske + unsigned m_uNeededTime; //!< Benötigte Zeit in ms +}; + +#endif diff --git a/catalog.cpp b/catalog.cpp index 0178850..fe3126a 100644 --- a/catalog.cpp +++ b/catalog.cpp @@ -1,433 +1,433 @@ -/*************************************************************************** - * 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 "osziparchive.h" -#include "tools.h" - -#include -#include -#include -#include -#include - -CCatalog::~CCatalog() -{ - qDeleteAll(m_listFiles); -} - -void CCatalog::clear() -{ - CChapter::clear(); - m_bMixAnswers = true; - m_bSort = false; - m_strFileName.clear(); - m_listHint.clear(); - m_strUniqueName.clear(); - qDeleteAll(m_listFiles); - m_strPublisher.clear(); - m_strContact.clear(); - m_dateValidFrom = QDate(); - m_dateValidUntil = QDate(); - m_dateCreated = QDate(); - m_datePublished = QDate(); - m_strVersion.clear(); - m_listExam.clear(); - m_listExamStat.clear(); -} - -bool CCatalog::isEmpty() -{ - return m_strText.isEmpty(); -} - -bool CCatalog::isValid() const -{ - if (m_dateValidFrom.isValid() && QDate::currentDate() < m_dateValidFrom) return false; - if (m_dateValidUntil.isValid() && QDate::currentDate() > m_dateValidUntil) return false; - return true; -} - -bool CCatalog::hasHints (const QString& strQuestionId) const -{ - for (int i=0; itr("Datei-Fehler"), pParent->tr("Konnte folgende Datei nicht zum Lesen öffnen:\n%1").arg(strFileName)); - return false; - } - - // Fragen entpacken - CZipFile *pzfQuestions = zip.findFile("questions.xml"); - if (pzfQuestions == 0) - { - QMessageBox::critical(pParent, pParent->tr("Datei-Fehler"), pParent->tr("Konnte in der Datei '%1' keine Fragen finden.").arg(strFileName)); - return false; - } - strXML = QString::fromUtf8(pzfQuestions->deflateToByteArray()); - - clear(); - - // Alle Grafiken in temp. Dateien entpacken -// pParent->setCursor(Qt::WaitCursor); - for (int i=0; ifileName().right(3); - if (str == "png" || str == "jpg" || str == "gif" || str == "bmp") - { // Datei entpacken und Pfade im XML anpassen - str = QDir::temp().absoluteFilePath(pzf->fileName()+".XXXXXX"); - QTemporaryFile *ptf = new QTemporaryFile(str); - ptf->open(); - str = ptf->fileName(); - pzf->deflateToFile(*ptf); - ptf->close(); - m_listFiles.append(ptf); - strXML.replace(pzf->fileName(), str); - //qDebug ("Deflating %s to %s", qPrintable(pzf->fileName()), qPrintable(str)); - } - } -// pParent->setCursor(Qt::ArrowCursor); - - if (!doc.setContent(strXML, true, &str, &iErrLine, &iErrCol)) - { - QMessageBox::critical(pParent, pParent->tr("XML-Fehler"), pParent->tr("Fragenkatalog: ") + strFileName + "\n" + str + "\n" + QString (pParent->tr("Zeile: %1 Spalte %2")).arg(iErrLine).arg(iErrCol)); - return false; - } - - elemRoot = doc.documentElement (); - if (elemRoot.tagName() != "aqdf") - { - QMessageBox::critical(pParent, pParent->tr("Datei-Fehler"), pParent->tr("Unbekanntes XML-Datenformat '%1'.").arg(elemRoot.tagName())); - return false; - } - //m_strText = elemRoot.attribute ("name"); - m_dateCreated = QDate::fromString(elemRoot.attribute("created"), Qt::ISODate); - dVersion = elemRoot.attribute("version").toDouble(); - if (dVersion != 1.0) - { - QMessageBox::information(pParent, pParent->tr("Information"), pParent->tr("Kann die Dateiversion %1 des Fragenkatalogs '%2' nicht lesen.").arg(dVersion).arg(strFileName)); - return false; - } - - - n = elemRoot.firstChild(); - while (!n.isNull()) - { - if (n.isElement ()) - { - e = n.toElement (); - if (e.tagName() == QString ("chapter")) - { - pChapter = new CChapter(); - if (pChapter->load (e)) - appendChapter(pChapter); - else - delete pChapter; - } - else if (e.tagName() == QString ("exam")) - { - CExam exam; - if (exam.load(e)) m_listExam.append(exam); - } - else if (e.tagName () == "hint") - { - CHint hint; - if (hint.load (e)) - m_listHint.append (hint); - } - else if (e.tagName () == "title") - { - m_strText = e.text(); - m_strUniqueName = e.attribute("unique"); - } - else if (e.tagName () == "comment") - m_strComment = e.text(); - else if (e.tagName () == "contact") - m_strContact = e.text(); - else if (e.tagName () == "publisher") - m_strPublisher = e.text(); - else if (e.tagName() == "valid") - { - m_dateValidFrom = QDate::fromString(e.attribute("from"), Qt::ISODate); - m_dateValidUntil = QDate::fromString(e.attribute("until"), Qt::ISODate); - } - else if (e.tagName() == "version") - { - m_datePublished = QDate::fromString(e.attribute("published"), Qt::ISODate); - m_strVersion = e.text(); - } - else if (e.tagName() == "options") - { - m_bMixAnswers = QVariant(e.attribute("mixanswers", "true")).toBool(); - m_bSort = QVariant(e.attribute("sort", "false")).toBool(); - } - } - n = n.nextSibling(); - } - - if (m_strUniqueName.isEmpty()) - m_strUniqueName = QDir(strFileName).dirName(); - - if (m_bSort) sortAll(); - - loadStatistic (pParent); - updateStatistic(); - - m_strFileName = strFileName; - return true; -} - -bool CCatalog::save (const QString& strFileName, QWidget *pParent) -{ -QFile file (strFileName); - - if (strFileName.isEmpty()) return false; - if (!file.open(QIODevice::WriteOnly)) - { - QMessageBox::critical(pParent, QString(pParent->tr("Datei-Fehler")), pParent->tr("Konnte folgende Datei nicht zum Schreiben öffnen:\n")+strFileName); - return false; - } - QTextStream out(&file); - - QDomDocument doc("afutrainer"); - QDomElement elemRoot = doc.createElement("afutrainer"); - //elemRoot.setAttribute("name", name()); - elemRoot.setAttribute("version", 3.0); - elemRoot.setAttribute("created", QDate::currentDate().toString(Qt::ISODate)); - doc.appendChild(elemRoot); - - - // save unique name - QDomElement elemTitle = createXmlTextElement("title", name(), doc); - elemTitle.setAttribute("unique", m_strUniqueName); - elemRoot.appendChild (elemTitle); - - // save comment - if (!m_strComment.isEmpty()) - elemRoot.appendChild (createXmlTextElement("comment", m_strComment, doc)); - - // save contact - if (!m_strContact.isEmpty()) - elemRoot.appendChild (createXmlTextElement("contact", m_strContact, doc)); - - // save publisher - if (!m_strPublisher.isEmpty()) - elemRoot.appendChild (createXmlTextElement("publisher", m_strPublisher, doc)); - - // save version - QDomElement elemVersion = createXmlTextElement("version", m_strVersion, doc); - elemVersion.setAttribute("published", m_datePublished.toString(Qt::ISODate)); - elemRoot.appendChild(elemVersion); - - // save dates - if (m_dateValidFrom.isValid() || m_dateValidUntil.isValid()) - { - QDomElement elemValid = doc.createElement("valid"); - elemValid.setAttribute("from", m_dateValidFrom.toString(Qt::ISODate)); - elemValid.setAttribute("until", m_dateValidUntil.toString(Qt::ISODate)); - elemRoot.appendChild(elemValid); - } - - // TODO: save tests - - - // save chapters - for (int i=0; isave(elemRoot, doc); - - // save helpers - for (int i=0; itr("Datei-Fehler")), pParent->tr("Konnte folgende Datei nicht zum Lesen öffnen:\n")+statisticFileName()); - return false; - } - QTextStream in (&file); - strXML = in.readAll(); - - if (!doc.setContent(strXML, true, &str, &iErrLine, &iErrCol)) - { - QMessageBox::critical(pParent, pParent->tr("XML-Fehler"), pParent->tr("Statistik zum Fragenkatalog: ") + statisticFileName() + "\n" + str + "\n" + QString (pParent->tr("Zeile: %1 Spalte %2")).arg(iErrLine).arg(iErrCol)); - return false; - } - file.close (); - - elemRoot = doc.documentElement (); - if (doc.doctype().name() != "AFUTrainerStatistics") return false; - if (elemRoot.tagName() != "statistic") return false; - - n = elemRoot.firstChild(); - while (!n.isNull()) - { - if (n.isElement ()) - { - e = n.toElement (); - if (e.tagName() == QString ("learning")) - { - loadLearnStatistic(e); - } - else if (e.tagName() == QString("exams")) - loadExamStatistic(e); - } - n = n.nextSibling(); - } - - return true; -} - -bool CCatalog::saveStatistic(QWidget *pParent) -{ -QDomDocument doc ("AFUTrainerStatistics"); -QString strFileName = statisticFileName(); -QFile file (strFileName); - - if (m_strUniqueName.isEmpty()) return false; - QDomElement elemRoot = doc.createElement ("statistic"); - elemRoot.setAttribute ("name", name()); - elemRoot.setAttribute("version", 2); - elemRoot.setAttribute("date", QDate::currentDate().toString(Qt::ISODate)); - doc.appendChild (elemRoot); - - QDomElement elemLearn = doc.createElement ("learning"); - elemRoot.appendChild (elemLearn); - - saveLearnStatistic(elemLearn, doc); - - QDomElement elemExams = doc.createElement ("exams"); - elemRoot.appendChild (elemExams); - saveExamStatistic(elemExams, doc); - - if (!file.open (QIODevice::WriteOnly)) - { - QMessageBox::critical (pParent, pParent->tr("Fehler"), pParent->tr("Konnte folgende Datei nicht zum Schreiben öffnen.\n")+strFileName); - return false; - } - QTextStream out(&file); - out << doc.toString (); - return true; -} - -bool CCatalog::loadExamStatistic (QDomElement& elemRoot) -{ -QDomNode n; -QDomElement e; - - n = elemRoot.firstChild(); - while (!n.isNull()) - { - if (n.isElement ()) - { - e = n.toElement (); - if (e.tagName() == QString ("exam")) - { - CExamStat es; - if (es.load(e)) m_listExamStat.append(es); - } - } - n = n.nextSibling(); - } - return true; -} - -void CCatalog::saveExamStatistic (QDomElement& parent, QDomDocument& doc) -{ - for (int i=0; i CCatalog::chapters() -{ -QList list; - list.append(this); - list << subChapters(); - return list; -} - -CExam CCatalog::examById(const QString& strId) const -{ - for (int i=0; i +#include +#include +#include +#include + +CCatalog::~CCatalog() +{ + qDeleteAll(m_listFiles); +} + +void CCatalog::clear() +{ + CChapter::clear(); + m_bMixAnswers = true; + m_bSort = false; + m_strFileName.clear(); + m_listHint.clear(); + m_strUniqueName.clear(); + qDeleteAll(m_listFiles); + m_strPublisher.clear(); + m_strContact.clear(); + m_dateValidFrom = QDate(); + m_dateValidUntil = QDate(); + m_dateCreated = QDate(); + m_datePublished = QDate(); + m_strVersion.clear(); + m_listExam.clear(); + m_listExamStat.clear(); +} + +bool CCatalog::isEmpty() +{ + return m_strText.isEmpty(); +} + +bool CCatalog::isValid() const +{ + if (m_dateValidFrom.isValid() && QDate::currentDate() < m_dateValidFrom) return false; + if (m_dateValidUntil.isValid() && QDate::currentDate() > m_dateValidUntil) return false; + return true; +} + +bool CCatalog::hasHints (const QString& strQuestionId) const +{ + for (int i=0; itr("Datei-Fehler"), pParent->tr("Konnte folgende Datei nicht zum Lesen öffnen:\n%1").arg(strFileName)); + return false; + } + + // Fragen entpacken + CZipFile *pzfQuestions = zip.findFile("questions.xml"); + if (pzfQuestions == 0) + { + QMessageBox::critical(pParent, pParent->tr("Datei-Fehler"), pParent->tr("Konnte in der Datei '%1' keine Fragen finden.").arg(strFileName)); + return false; + } + strXML = QString::fromUtf8(pzfQuestions->deflateToByteArray()); + + clear(); + + // Alle Grafiken in temp. Dateien entpacken +// pParent->setCursor(Qt::WaitCursor); + for (int i=0; ifileName().right(3); + if (str == "png" || str == "jpg" || str == "gif" || str == "bmp") + { // Datei entpacken und Pfade im XML anpassen + str = QDir::temp().absoluteFilePath(pzf->fileName()+".XXXXXX"); + QTemporaryFile *ptf = new QTemporaryFile(str); + ptf->open(); + str = ptf->fileName(); + pzf->deflateToFile(*ptf); + ptf->close(); + m_listFiles.append(ptf); + strXML.replace(pzf->fileName(), str); + //qDebug ("Deflating %s to %s", qPrintable(pzf->fileName()), qPrintable(str)); + } + } +// pParent->setCursor(Qt::ArrowCursor); + + if (!doc.setContent(strXML, true, &str, &iErrLine, &iErrCol)) + { + QMessageBox::critical(pParent, pParent->tr("XML-Fehler"), pParent->tr("Fragenkatalog: ") + strFileName + "\n" + str + "\n" + QString (pParent->tr("Zeile: %1 Spalte %2")).arg(iErrLine).arg(iErrCol)); + return false; + } + + elemRoot = doc.documentElement (); + if (elemRoot.tagName() != "aqdf") + { + QMessageBox::critical(pParent, pParent->tr("Datei-Fehler"), pParent->tr("Unbekanntes XML-Datenformat '%1'.").arg(elemRoot.tagName())); + return false; + } + //m_strText = elemRoot.attribute ("name"); + m_dateCreated = QDate::fromString(elemRoot.attribute("created"), Qt::ISODate); + dVersion = elemRoot.attribute("version").toDouble(); + if (dVersion != 1.0) + { + QMessageBox::information(pParent, pParent->tr("Information"), pParent->tr("Kann die Dateiversion %1 des Fragenkatalogs '%2' nicht lesen.").arg(dVersion).arg(strFileName)); + return false; + } + + + n = elemRoot.firstChild(); + while (!n.isNull()) + { + if (n.isElement ()) + { + e = n.toElement (); + if (e.tagName() == QString ("chapter")) + { + pChapter = new CChapter(); + if (pChapter->load (e)) + appendChapter(pChapter); + else + delete pChapter; + } + else if (e.tagName() == QString ("exam")) + { + CExam exam; + if (exam.load(e)) m_listExam.append(exam); + } + else if (e.tagName () == "hint") + { + CHint hint; + if (hint.load (e)) + m_listHint.append (hint); + } + else if (e.tagName () == "title") + { + m_strText = e.text(); + m_strUniqueName = e.attribute("unique"); + } + else if (e.tagName () == "comment") + m_strComment = e.text(); + else if (e.tagName () == "contact") + m_strContact = e.text(); + else if (e.tagName () == "publisher") + m_strPublisher = e.text(); + else if (e.tagName() == "valid") + { + m_dateValidFrom = QDate::fromString(e.attribute("from"), Qt::ISODate); + m_dateValidUntil = QDate::fromString(e.attribute("until"), Qt::ISODate); + } + else if (e.tagName() == "version") + { + m_datePublished = QDate::fromString(e.attribute("published"), Qt::ISODate); + m_strVersion = e.text(); + } + else if (e.tagName() == "options") + { + m_bMixAnswers = QVariant(e.attribute("mixanswers", "true")).toBool(); + m_bSort = QVariant(e.attribute("sort", "false")).toBool(); + } + } + n = n.nextSibling(); + } + + if (m_strUniqueName.isEmpty()) + m_strUniqueName = QDir(strFileName).dirName(); + + if (m_bSort) sortAll(); + + loadStatistic (pParent); + updateStatistic(); + + m_strFileName = strFileName; + return true; +} + +bool CCatalog::save (const QString& strFileName, QWidget *pParent) +{ +QFile file (strFileName); + + if (strFileName.isEmpty()) return false; + if (!file.open(QIODevice::WriteOnly)) + { + QMessageBox::critical(pParent, QString(pParent->tr("Datei-Fehler")), pParent->tr("Konnte folgende Datei nicht zum Schreiben öffnen:\n")+strFileName); + return false; + } + QTextStream out(&file); + + QDomDocument doc("afutrainer"); + QDomElement elemRoot = doc.createElement("afutrainer"); + //elemRoot.setAttribute("name", name()); + elemRoot.setAttribute("version", 3.0); + elemRoot.setAttribute("created", QDate::currentDate().toString(Qt::ISODate)); + doc.appendChild(elemRoot); + + + // save unique name + QDomElement elemTitle = createXmlTextElement("title", name(), doc); + elemTitle.setAttribute("unique", m_strUniqueName); + elemRoot.appendChild (elemTitle); + + // save comment + if (!m_strComment.isEmpty()) + elemRoot.appendChild (createXmlTextElement("comment", m_strComment, doc)); + + // save contact + if (!m_strContact.isEmpty()) + elemRoot.appendChild (createXmlTextElement("contact", m_strContact, doc)); + + // save publisher + if (!m_strPublisher.isEmpty()) + elemRoot.appendChild (createXmlTextElement("publisher", m_strPublisher, doc)); + + // save version + QDomElement elemVersion = createXmlTextElement("version", m_strVersion, doc); + elemVersion.setAttribute("published", m_datePublished.toString(Qt::ISODate)); + elemRoot.appendChild(elemVersion); + + // save dates + if (m_dateValidFrom.isValid() || m_dateValidUntil.isValid()) + { + QDomElement elemValid = doc.createElement("valid"); + elemValid.setAttribute("from", m_dateValidFrom.toString(Qt::ISODate)); + elemValid.setAttribute("until", m_dateValidUntil.toString(Qt::ISODate)); + elemRoot.appendChild(elemValid); + } + + // TODO: save tests + + + // save chapters + for (int i=0; isave(elemRoot, doc); + + // save helpers + for (int i=0; itr("Datei-Fehler")), pParent->tr("Konnte folgende Datei nicht zum Lesen öffnen:\n")+statisticFileName()); + return false; + } + QTextStream in (&file); + strXML = in.readAll(); + + if (!doc.setContent(strXML, true, &str, &iErrLine, &iErrCol)) + { + QMessageBox::critical(pParent, pParent->tr("XML-Fehler"), pParent->tr("Statistik zum Fragenkatalog: ") + statisticFileName() + "\n" + str + "\n" + QString (pParent->tr("Zeile: %1 Spalte %2")).arg(iErrLine).arg(iErrCol)); + return false; + } + file.close (); + + elemRoot = doc.documentElement (); + if (doc.doctype().name() != "AFUTrainerStatistics") return false; + if (elemRoot.tagName() != "statistic") return false; + + n = elemRoot.firstChild(); + while (!n.isNull()) + { + if (n.isElement ()) + { + e = n.toElement (); + if (e.tagName() == QString ("learning")) + { + loadLearnStatistic(e); + } + else if (e.tagName() == QString("exams")) + loadExamStatistic(e); + } + n = n.nextSibling(); + } + + return true; +} + +bool CCatalog::saveStatistic(QWidget *pParent) +{ +QDomDocument doc ("AFUTrainerStatistics"); +QString strFileName = statisticFileName(); +QFile file (strFileName); + + if (m_strUniqueName.isEmpty()) return false; + QDomElement elemRoot = doc.createElement ("statistic"); + elemRoot.setAttribute ("name", name()); + elemRoot.setAttribute("version", 2); + elemRoot.setAttribute("date", QDate::currentDate().toString(Qt::ISODate)); + doc.appendChild (elemRoot); + + QDomElement elemLearn = doc.createElement ("learning"); + elemRoot.appendChild (elemLearn); + + saveLearnStatistic(elemLearn, doc); + + QDomElement elemExams = doc.createElement ("exams"); + elemRoot.appendChild (elemExams); + saveExamStatistic(elemExams, doc); + + if (!file.open (QIODevice::WriteOnly)) + { + QMessageBox::critical (pParent, pParent->tr("Fehler"), pParent->tr("Konnte folgende Datei nicht zum Schreiben öffnen.\n")+strFileName); + return false; + } + QTextStream out(&file); + out << doc.toString (); + return true; +} + +bool CCatalog::loadExamStatistic (QDomElement& elemRoot) +{ +QDomNode n; +QDomElement e; + + n = elemRoot.firstChild(); + while (!n.isNull()) + { + if (n.isElement ()) + { + e = n.toElement (); + if (e.tagName() == QString ("exam")) + { + CExamStat es; + if (es.load(e)) m_listExamStat.append(es); + } + } + n = n.nextSibling(); + } + return true; +} + +void CCatalog::saveExamStatistic (QDomElement& parent, QDomDocument& doc) +{ + for (int i=0; i CCatalog::chapters() +{ +QList list; + list.append(this); + list << subChapters(); + return list; +} + +CExam CCatalog::examById(const QString& strId) const +{ + for (int i=0; i -#include - -class CCatalog : public CChapter -{ -public: - CCatalog() : CChapter() { clear(); } - ~CCatalog(); - - void clear(); - bool isEmpty(); - - inline QString name() const { return m_strText; } - inline void setName(const QString& strName) { m_strText = strName; } - - inline QString contact() const { return m_strContact; } - inline void setContact(const QString& strContact) { m_strContact = strContact; } - inline QString publisher() const { return m_strPublisher; } - inline void setPublisher(const QString& strPublisher) { m_strPublisher = strPublisher; } - - // Validation - inline QDate validFrom() const { return m_dateValidFrom; } - inline void setValidFrom(const QDate& date) { m_dateValidFrom = date; } - inline QDate validUntil() const { return m_dateValidUntil; } - inline void setValidUntil(const QDate& date) { m_dateValidUntil = date; } - bool isValid() const; - inline QDate created() const { return m_dateCreated; } - inline void setCreated(const QDate& date) { m_dateCreated = date; } - inline QDate published() const { return m_datePublished; } - inline void setPublished(const QDate& date) { m_datePublished = date; } - inline QString versionText() const { return m_strVersion; } - inline void setVersionText(const QString& str) { m_strVersion = str; } - - // file operations - inline QString fileName() const { return m_strFileName; } - bool load (const QString& strFileName, QWidget *pParent); - bool save (const QString& strFileName, QWidget *pParent); - - bool loadStatistic(QWidget *pParent); - bool saveStatistic(QWidget *pParent); - - // hints - inline int countHint() const { return m_listHint.size(); } - inline const CHint& hintAt(int i) const { return m_listHint.at(i); } - inline CHint& hintAt(int i) { return m_listHint[i]; } - inline void appendHint(const CHint& a) { m_listHint.append(a); } - bool hasHints (const QString& strQuestionId) const; - QString hintText (const QString& strQuestionId) const; - - // exams - inline int countExam() const { return m_listExam.size(); } - inline const CExam& examAt(int i) const { return m_listExam.at(i); } - inline CExam& examAt(int i) { return m_listExam[i]; } - inline void appendExam(const CExam& a) { m_listExam.append(a); } - CExam examById(const QString& strId) const; - - inline void appendExamStat(const CExamStat& a) { m_listExamStat.append(a); } - inline int countExamStat() const { return m_listExamStat.size(); } - inline const CExamStat& examStatAt(int i) const { return m_listExamStat.at(i); } - - // options - inline bool useMixedAnswers() const { return m_bMixAnswers; } - - // others - QList chapters(); - -protected: - QString statisticFileName() const; - bool loadExamStatistic (QDomElement& elemRoot); - void saveExamStatistic (QDomElement& parent, QDomDocument& doc); - -protected: - QString m_strFileName; - QString m_strUniqueName; - QList m_listFiles; - - QList m_listHint; - QString m_strContact; - QString m_strPublisher; - QDate m_dateValidFrom; - QDate m_dateValidUntil; - QDate m_dateCreated; - QDate m_datePublished; - QString m_strVersion; - QList m_listExam; - QList m_listExamStat; - - // Optionen - bool m_bMixAnswers; - bool m_bSort; -}; - -#endif +/*************************************************************************** + * 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. * + ***************************************************************************/ + +#ifndef CATALOG_H +#define CATALOG_H + +#include "chapter.h" +#include "exam.h" + +#include +#include + +class CCatalog : public CChapter +{ +public: + CCatalog() : CChapter() { clear(); } + ~CCatalog(); + + void clear(); + bool isEmpty(); + + inline QString name() const { return m_strText; } + inline void setName(const QString& strName) { m_strText = strName; } + + inline QString contact() const { return m_strContact; } + inline void setContact(const QString& strContact) { m_strContact = strContact; } + inline QString publisher() const { return m_strPublisher; } + inline void setPublisher(const QString& strPublisher) { m_strPublisher = strPublisher; } + + // Validation + inline QDate validFrom() const { return m_dateValidFrom; } + inline void setValidFrom(const QDate& date) { m_dateValidFrom = date; } + inline QDate validUntil() const { return m_dateValidUntil; } + inline void setValidUntil(const QDate& date) { m_dateValidUntil = date; } + bool isValid() const; + inline QDate created() const { return m_dateCreated; } + inline void setCreated(const QDate& date) { m_dateCreated = date; } + inline QDate published() const { return m_datePublished; } + inline void setPublished(const QDate& date) { m_datePublished = date; } + inline QString versionText() const { return m_strVersion; } + inline void setVersionText(const QString& str) { m_strVersion = str; } + + // file operations + inline QString fileName() const { return m_strFileName; } + bool load (const QString& strFileName, QWidget *pParent); + bool save (const QString& strFileName, QWidget *pParent); + + bool loadStatistic(QWidget *pParent); + bool saveStatistic(QWidget *pParent); + + // hints + inline int countHint() const { return m_listHint.size(); } + inline const CHint& hintAt(int i) const { return m_listHint.at(i); } + inline CHint& hintAt(int i) { return m_listHint[i]; } + inline void appendHint(const CHint& a) { m_listHint.append(a); } + bool hasHints (const QString& strQuestionId) const; + QString hintText (const QString& strQuestionId) const; + + // exams + inline int countExam() const { return m_listExam.size(); } + inline const CExam& examAt(int i) const { return m_listExam.at(i); } + inline CExam& examAt(int i) { return m_listExam[i]; } + inline void appendExam(const CExam& a) { m_listExam.append(a); } + CExam examById(const QString& strId) const; + + inline void appendExamStat(const CExamStat& a) { m_listExamStat.append(a); } + inline int countExamStat() const { return m_listExamStat.size(); } + inline const CExamStat& examStatAt(int i) const { return m_listExamStat.at(i); } + + // options + inline bool useMixedAnswers() const { return m_bMixAnswers; } + + // others + QList chapters(); + +protected: + QString statisticFileName() const; + bool loadExamStatistic (QDomElement& elemRoot); + void saveExamStatistic (QDomElement& parent, QDomDocument& doc); + +protected: + QString m_strFileName; + QString m_strUniqueName; + QList m_listFiles; + + QList m_listHint; + QString m_strContact; + QString m_strPublisher; + QDate m_dateValidFrom; + QDate m_dateValidUntil; + QDate m_dateCreated; + QDate m_datePublished; + QString m_strVersion; + QList m_listExam; + QList m_listExamStat; + + // Optionen + bool m_bMixAnswers; + bool m_bSort; +}; + +#endif diff --git a/catalogmodel.cpp b/catalogmodel.cpp index 65367f2..cd8cd51 100644 --- a/catalogmodel.cpp +++ b/catalogmodel.cpp @@ -1,205 +1,205 @@ -/*************************************************************************** - * 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 "catalogmodel.h" - -#include "catalog.h" - -#define COL_CHAPTER 0 -#define COL_QUESTION 1 -#define COL_ASSIST 2 -#define COL_AVG 3 - -CCatalogModel::CCatalogModel (QObject *pParent) : QAbstractItemModel (pParent) -{ - m_pCatalog = 0; -} - -CCatalogModel::~CCatalogModel() -{ -} - - -void CCatalogModel::onLanguageChanged() -{ - headerDataChanged(Qt::Horizontal, 0, columnCount()-1); -} - -void CCatalogModel::setModelData (CCatalog *pCatalog) -{ - reset(); - m_pCatalog = pCatalog; - reset(); -} - -int CCatalogModel::columnCount (const QModelIndex& parent) const -{ - Q_UNUSED(parent); - return 4; -} - -QVariant CCatalogModel::headerData (int section, Qt::Orientation orientation, int role) const -{ - if (orientation == Qt::Vertical) return QVariant(); - - if (role == Qt::DisplayRole) - { - if (section == COL_CHAPTER) - return tr("Kapitel"); - else if (section == COL_QUESTION) - return tr("Fragen"); - else if (section == COL_AVG) - return tr("Ø"); - else if (section == COL_ASSIST) - return tr("Lernassistent"); - } - else if (role == Qt::ToolTipRole) - { - if (section == COL_CHAPTER) - return tr("Kapitelname\nBitte markieren Sie das Kapitel, das Sie lernen möchten."); - else if (section == COL_QUESTION) - return tr("Anzahl d. Fragen inkl. Unterkapitel"); - else if (section == COL_AVG) - return tr("Durchschnittlicher Lernfortschritt"); - else if (section == COL_ASSIST) - return tr("Empfehlung des Lernassistentes"); - } - else if (role == Qt::WhatsThisRole) - { - - } - - return QVariant(); -} - -QVariant CCatalogModel::data (const QModelIndex& index, int role) const -{ -CChapter *p = (CChapter*) index.internalPointer(); - - if (m_pCatalog == 0 || !index.isValid()) return QVariant(); - - if (role == Qt::TextAlignmentRole && index.column() == COL_QUESTION) return Qt::AlignRight; - - if (p == 0) return QVariant(); - - if (role == Qt::DisplayRole) - { - if (index.column() == COL_CHAPTER) - { - if (p->id().isEmpty()) - return p->text(); - else - return p->id() + " - " + p->text(); - } - else if (index.column() == COL_QUESTION) - return QString("%1").arg(p->countSubQuestion()); - else if (index.column() == COL_ASSIST) - { - return p->recommendationText(); - } - } - else if (role == Qt::DecorationRole) - { - if (index.column() == COL_CHAPTER) - return QIcon(":/icons/16x16/folder.png"); - else if (index.column() == COL_AVG) - { - return p->levelAvgIcon(); - } - else if (index.column() == COL_ASSIST) - { - return p->recommendationIcon(m_pCatalog); - } - } - else if (role == Qt::ToolTipRole) - { - if (index.column() == COL_AVG) - { - return QString ("%1 - Kennzahl: %2").arg(p->/*statistic().*/levelAvgText()).arg(p->/*statistic().*/levelAvg(), 4, 'f', 2); - } - else if (index.column() == COL_ASSIST) - { - return p->recommendationToolTip(); - } - } - - return QVariant(); -} - -bool CCatalogModel::hasChildren (const QModelIndex& parent) const -{ -CChapter *p = (CChapter*) parent.internalPointer(); - - if (m_pCatalog == 0) return false; - - if (parent.isValid() && p) - return (p->countChapter() > 0); - else // root item - return true; -} - -QModelIndex CCatalogModel::index (int row, int column, const QModelIndex& parent) const -{ -CChapter *pParent = (CChapter*) parent.internalPointer(); - - if (!parent.isValid()) - { // root item - return createIndex (row, column, (void*) m_pCatalog); - } - else - { - // take entry from parent item - if (row > pParent->countChapter()) - return QModelIndex(); - return createIndex (row, column, (void*)pParent->chapterAt(row)); - } - return QModelIndex(); -} - -QModelIndex CCatalogModel::parent (const QModelIndex& index) const -{ -CChapter *pIndex = (CChapter*) index.internalPointer(); -int iRow=0; - - if (pIndex == 0 || pIndex->parentChapter() == 0) // root item - return QModelIndex(); // has no parent - - CChapter *pParent = pIndex->parentChapter(); - if (pParent->parentChapter()) - iRow = pParent->parentChapter()->indexOfChapter(pParent); - else // parent is the root item (parent of the parent doesn't exist) - iRow = 0; - - return createIndex (iRow, 0 /*index.column()*/, pParent); -} - -int CCatalogModel::rowCount (const QModelIndex& parent) const -{ -CChapter *pParent = (CChapter*) parent.internalPointer(); - - if (m_pCatalog == 0 || m_pCatalog->isEmpty()) return 0; - - if (!parent.isValid()) // root item - return 1; - - return pParent->countChapter(); -} - +/*************************************************************************** + * 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 "catalogmodel.h" + +#include "catalog.h" + +#define COL_CHAPTER 0 +#define COL_QUESTION 1 +#define COL_ASSIST 2 +#define COL_AVG 3 + +CCatalogModel::CCatalogModel (QObject *pParent) : QAbstractItemModel (pParent) +{ + m_pCatalog = 0; +} + +CCatalogModel::~CCatalogModel() +{ +} + + +void CCatalogModel::onLanguageChanged() +{ + headerDataChanged(Qt::Horizontal, 0, columnCount()-1); +} + +void CCatalogModel::setModelData (CCatalog *pCatalog) +{ + reset(); + m_pCatalog = pCatalog; + reset(); +} + +int CCatalogModel::columnCount (const QModelIndex& parent) const +{ + Q_UNUSED(parent); + return 4; +} + +QVariant CCatalogModel::headerData (int section, Qt::Orientation orientation, int role) const +{ + if (orientation == Qt::Vertical) return QVariant(); + + if (role == Qt::DisplayRole) + { + if (section == COL_CHAPTER) + return tr("Kapitel"); + else if (section == COL_QUESTION) + return tr("Fragen"); + else if (section == COL_AVG) + return tr("Ø"); + else if (section == COL_ASSIST) + return tr("Lernassistent"); + } + else if (role == Qt::ToolTipRole) + { + if (section == COL_CHAPTER) + return tr("Kapitelname\nBitte markieren Sie das Kapitel, das Sie lernen möchten."); + else if (section == COL_QUESTION) + return tr("Anzahl d. Fragen inkl. Unterkapitel"); + else if (section == COL_AVG) + return tr("Durchschnittlicher Lernfortschritt"); + else if (section == COL_ASSIST) + return tr("Empfehlung des Lernassistentes"); + } + else if (role == Qt::WhatsThisRole) + { + + } + + return QVariant(); +} + +QVariant CCatalogModel::data (const QModelIndex& index, int role) const +{ +CChapter *p = (CChapter*) index.internalPointer(); + + if (m_pCatalog == 0 || !index.isValid()) return QVariant(); + + if (role == Qt::TextAlignmentRole && index.column() == COL_QUESTION) return Qt::AlignRight; + + if (p == 0) return QVariant(); + + if (role == Qt::DisplayRole) + { + if (index.column() == COL_CHAPTER) + { + if (p->id().isEmpty()) + return p->text(); + else + return p->id() + " - " + p->text(); + } + else if (index.column() == COL_QUESTION) + return QString("%1").arg(p->countSubQuestion()); + else if (index.column() == COL_ASSIST) + { + return p->recommendationText(); + } + } + else if (role == Qt::DecorationRole) + { + if (index.column() == COL_CHAPTER) + return QIcon(":/icons/16x16/folder.png"); + else if (index.column() == COL_AVG) + { + return p->levelAvgIcon(); + } + else if (index.column() == COL_ASSIST) + { + return p->recommendationIcon(m_pCatalog); + } + } + else if (role == Qt::ToolTipRole) + { + if (index.column() == COL_AVG) + { + return QString ("%1 - Kennzahl: %2").arg(p->/*statistic().*/levelAvgText()).arg(p->/*statistic().*/levelAvg(), 4, 'f', 2); + } + else if (index.column() == COL_ASSIST) + { + return p->recommendationToolTip(); + } + } + + return QVariant(); +} + +bool CCatalogModel::hasChildren (const QModelIndex& parent) const +{ +CChapter *p = (CChapter*) parent.internalPointer(); + + if (m_pCatalog == 0) return false; + + if (parent.isValid() && p) + return (p->countChapter() > 0); + else // root item + return true; +} + +QModelIndex CCatalogModel::index (int row, int column, const QModelIndex& parent) const +{ +CChapter *pParent = (CChapter*) parent.internalPointer(); + + if (!parent.isValid()) + { // root item + return createIndex (row, column, (void*) m_pCatalog); + } + else + { + // take entry from parent item + if (row > pParent->countChapter()) + return QModelIndex(); + return createIndex (row, column, (void*)pParent->chapterAt(row)); + } + return QModelIndex(); +} + +QModelIndex CCatalogModel::parent (const QModelIndex& index) const +{ +CChapter *pIndex = (CChapter*) index.internalPointer(); +int iRow=0; + + if (pIndex == 0 || pIndex->parentChapter() == 0) // root item + return QModelIndex(); // has no parent + + CChapter *pParent = pIndex->parentChapter(); + if (pParent->parentChapter()) + iRow = pParent->parentChapter()->indexOfChapter(pParent); + else // parent is the root item (parent of the parent doesn't exist) + iRow = 0; + + return createIndex (iRow, 0 /*index.column()*/, pParent); +} + +int CCatalogModel::rowCount (const QModelIndex& parent) const +{ +CChapter *pParent = (CChapter*) parent.internalPointer(); + + if (m_pCatalog == 0 || m_pCatalog->isEmpty()) return 0; + + if (!parent.isValid()) // root item + return 1; + + return pParent->countChapter(); +} + diff --git a/catalogmodel.h b/catalogmodel.h index 41ae5af..972cfe5 100644 --- a/catalogmodel.h +++ b/catalogmodel.h @@ -1,55 +1,55 @@ -/*************************************************************************** - * 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. * - ***************************************************************************/ - -#ifndef CATALOGMODEL_H -#define CATALOGMODEL_H - -#include - -class CCatalog; - -class CCatalogModel : public QAbstractItemModel -{ - Q_OBJECT -public: - CCatalogModel (QObject *pParent=0); - ~CCatalogModel(); - - void setModelData (CCatalog *pCatalog); - -public: - virtual int columnCount ( const QModelIndex & parent = QModelIndex() ) const; - virtual QVariant data ( const QModelIndex & index, int role = Qt::DisplayRole ) const; - virtual bool hasChildren (const QModelIndex & parent = QModelIndex()) const; - virtual QVariant headerData ( int section, Qt::Orientation orientation, int role = Qt::DisplayRole ) const; - virtual QModelIndex index (int row, int column, const QModelIndex & parent = QModelIndex()) const; - virtual QModelIndex parent ( const QModelIndex & index ) const; - virtual int rowCount ( const QModelIndex & parent = QModelIndex() ) const; - -public slots: - void onLanguageChanged(); - -protected: - CCatalog *m_pCatalog; -}; - -#endif - +/*************************************************************************** + * 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. * + ***************************************************************************/ + +#ifndef CATALOGMODEL_H +#define CATALOGMODEL_H + +#include + +class CCatalog; + +class CCatalogModel : public QAbstractItemModel +{ + Q_OBJECT +public: + CCatalogModel (QObject *pParent=0); + ~CCatalogModel(); + + void setModelData (CCatalog *pCatalog); + +public: + virtual int columnCount ( const QModelIndex & parent = QModelIndex() ) const; + virtual QVariant data ( const QModelIndex & index, int role = Qt::DisplayRole ) const; + virtual bool hasChildren (const QModelIndex & parent = QModelIndex()) const; + virtual QVariant headerData ( int section, Qt::Orientation orientation, int role = Qt::DisplayRole ) const; + virtual QModelIndex index (int row, int column, const QModelIndex & parent = QModelIndex()) const; + virtual QModelIndex parent ( const QModelIndex & index ) const; + virtual int rowCount ( const QModelIndex & parent = QModelIndex() ) const; + +public slots: + void onLanguageChanged(); + +protected: + CCatalog *m_pCatalog; +}; + +#endif + diff --git a/chapter.cpp b/chapter.cpp index 39015dc..7e289cf 100644 --- a/chapter.cpp +++ b/chapter.cpp @@ -1,808 +1,808 @@ -/*************************************************************************** - * 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 - - -QString CChapter::tr (const char *sourceText, const char *comment) -{ - return QCoreApplication::translate("CChapter", sourceText, comment); -} - -void CChapter::clear() -{ - m_pParentChapter=0; - m_strId.clear(); - m_strText.clear(); - m_strComment.clear(); - m_recom = RecommendationNone; - m_recom2 = RecommendationNone; - qDeleteAll(m_listChapter); - qDeleteAll(m_listQuestion); - m_bHasLearningNew = false; - m_bHasKnownQuestions = false; - m_bHasKnownQuestionsRepeatToday = false; - m_uNeverAskedCount = 0; - for (int i=0; i<=LEVEL_MAX; i++) m_uLevelCount[i] = 0; - for (int i=0; i<=RecommendationMax; i++) m_uRecomCount[i] = 0; -// m_mapExam.clear(); -} - -int CChapter::countSubQuestion() const -{ -int i=0, iRet=0; - for (i=0; icountSubQuestion(); - return (iRet + m_listQuestion.size()); -} - -bool CChapter::load (QDomElement elem) -{ -CChapter *pChapter=0; - - if (elem.tagName () != QString ("chapter")) return false; - if (!elem.hasAttribute ("name")) return false; -// if (pParent != NULL && !elem.hasAttribute ("id")) return false; - m_strId = elem.attribute ("id"); - m_strText = elem.attribute ("name"); - QDomNode n = elem.firstChild(); - while (!n.isNull()) - { - if (n.isElement ()) - { - QDomElement e = n.toElement (); - if (e.tagName() == "comment") - m_strComment = e.text(); - else if (e.tagName() == QString ("chapter")) - { - pChapter = new CChapter(); - if (pChapter->load (e)) - appendChapter(pChapter); - else - delete pChapter; - } - else if (e.tagName() == QString ("question")) - { - CQuestion *pQuestion = new CQuestion(); - if (pQuestion->load (e)) - appendQuestion(pQuestion); - else - delete pQuestion; - } - } - n = n.nextSibling(); - } - updateStatistic(); - return true; -} - -void CChapter::save (QDomElement& parent, QDomDocument& doc) -{ -QDomElement elem = doc.createElement("chapter"); - - elem.setAttribute("name", text()); - elem.setAttribute("id", id()); - - if (!m_strComment.isEmpty()) - { - QDomElement elemComment = doc.createElement("comment"); - QDomText textComment = doc.createTextNode(text()); - elemComment.appendChild(textComment); - elem.appendChild(elemComment); - } - - parent.appendChild(elem); - - // save exams -/* QStringList strl = m_mapExam.keys(); - for (int i=0; isave(elem, doc); - } - - // save questions - for (int i=0; isave(elem, doc); - } -} - -bool CChapter::loadLearnStatistic (QDomElement elem) -{ -QList listQuestionPool = questionPool(); - - if (elem.tagName () != QString ("learning")) return false; - - QDomNode n = elem.firstChild(); - while (!n.isNull()) - { - if (n.isElement ()) - { - QDomElement e = n.toElement (); - if (e.tagName() == QString ("question")) - { - QString strId = e.attribute("id"); - for (int i=0; iid() == strId) - listQuestionPool[i]->loadLearnStatistic(e); - } - } - } - n = n.nextSibling(); - } - updateRecommendation(); - return true; -} - -bool CChapter::saveLearnStatistic (QDomElement& parent, QDomDocument& doc) -{ - // questions from sub-chapters - for (int i=0; isaveLearnStatistic(parent, doc); - } - - // save questions - for (int i=0; isaveLearnStatistic(parent, doc); - } - return true; -} - -QString CChapter::checkForErrors() const -{ -QString str; - - // check chapters - for (int i=0; icheckForErrors(); - } - - // check questions - for (int i=0; icheckForErrors(); - } - return str; -} - -QList CChapter::subChapters() const -{ -QList list; - - for (int i=0; isubChapters(); - } - return list; -} - -QList CChapter::questionPool() const -{ -QList list; - for (int i=0; iquestionPool(); - } - list << m_listQuestion; - return list; -} - -QList CChapter::questionPoolLevel(const unsigned uLevel) const -{ -QList list; -int i; - for (i=0; iquestionPoolLevel(uLevel); - for (i=0; ilevel() == uLevel) - list.append (m_listQuestion.at(i)); - } - return list; -} - -QList CChapter::questionPoolDeepen() const -{ -QList list; -int i; - for (i=0; iquestionPoolDeepen(); - for (i=0; ilevel() == LEVEL_VERYOFTEN && !q->isNeverAsked()) - list.append (q); - } - return list; -} - -QList CChapter::questionPoolRepeat(const QDate d) const -{ -QList list; -int i; - for (i=0; iquestionPoolRepeat(d); - for (i=0; irepeatDate() <= d) - list.append (q); - } - return list; -} - - -void CChapter::updateStatisticCount() -{ -/* for (int i=0; iisLearningNew()) return true; - - for (int i=0; iisLearningNew()) return true; - - return false; -*/ - // Alle Statistiken zurücksetzen - m_uNeverAskedCount = 0; - m_bHasLearningNew = false; - m_bHasKnownQuestions = false; - m_bHasKnownQuestionsRepeatToday = false; - for (int i=0; i<=LEVEL_MAX; i++) m_uLevelCount[i] = 0; - - // Zuerst Statistiken für Unterkapitel berechnen und integrieren - for (int i=0; iupdateStatisticCount(); - for (int j=0; j<=LEVEL_MAX; j++) - m_uLevelCount[j] += p->m_uLevelCount[j]; - m_uNeverAskedCount += p->m_uNeverAskedCount; - if (p->hasLearningNewQuestions()) m_bHasLearningNew = true; - if (p->hasKnownQuestions()) m_bHasKnownQuestions = true; - if (p->hasKnownQuestionsRepeatToday()) m_bHasKnownQuestionsRepeatToday = true; - } - - // Anschließend Statistiken für eigene Fragen neu berechnen und hinzufügen - for (int i=0; iisNeverAsked()) m_uNeverAskedCount++; - m_uLevelCount[q->level()]++; - if (q->isLearningNew()) m_bHasLearningNew = true; - if (q->isKnownQuestion()) m_bHasKnownQuestions = true; - if (q->isKnownQuestion() && q->isRepeatToday()) m_bHasKnownQuestionsRepeatToday = true; - } -} - -void CChapter::updateRecommendationStatistic() -{ - for (int i=0; iupdateRecommendationStatistic(); - for (int j=0; jm_uRecomCount[j]; - } -} - -/* -Berechnet die Statistik für das Kapitel inkl. aller Unterkapitel neu. -*/ -void CChapter::updateStatistic() -{ - updateStatisticCount(); - updateRecommendation(); - updateRecommendationStatistic(); -} - -/* -CRecommendation CChapter::recommendation() const -{ -CRecommendation r; - r.create(this); - return r; -} -*/ -/* -CRecommendationStatistic CChapter::recommendationStatistic() const -{ -CRecommendationStatistic rs; - - for (int i=0; irecommendationStatistic()); - } - //TODO -// rs.append(recommendation()); - return rs; -} -*/ - -QString CChapter::idWithParents() const -{ -CChapter *pParent=m_pParentChapter; -QString str; - str = id(); - while (pParent != 0) - { - str = pParent->id() + str; - pParent = pParent->m_pParentChapter; - } - - return str; -} - -/*! -Das empfohlene Wiederholdatum eines Kapitels das frühestete Datum, -an dem eine Frage des Kapitels oder Unterkapitels wiederholt werden sollte. -\return Datum, wann das Kapitel wiederholt werden soll. -Gibt es kein solches Datum, so wird ein ungültiges Datum (QDate::isValid()) zurückgegeben. -Hinweis: Das Datum kann auch in der Vergangenheit liegen! -*/ -QDate CChapter::repeatDate() const -{ -QDate d; -QList list = questionPool(); - - for (int i=0; irepeatDate(); - if (!dq.isValid()) continue; - if (!d.isValid() || dq < d) d = dq; - } - return d; -} - -/*! -Die Variable m_recomRepeatDate muss up to date sein! - -\return Lernempfehlung dieses Kapitels ohne Berücksichtigung evt. vorhandener ünter- oder übergeordneter Kapitel. - -\sa m_recomRepeatDate -*/ -CChapter::Recommendation CChapter::recommendationIndividual() const -{ - if (!m_recomRepeatDate.isValid()) - return RecommendationLearnNew; - else if (m_recomRepeatDate <= QDate::currentDate()) - { - if (hasLearningNewQuestions() && !hasKnownQuestionsRepeatToday()) - return RecommendationLearnNew; - else - return RecommendationRepeatToday; - } - else - { - if (countNeverAsked() > 0) - return RecommendationLearnNew; - else - return RecommendationRepeatLater; - } -} - -/*! -Aktualisiert die Lernempfehlung für dieses Kapitel und alle Unterkapitel -\sa m_recom -*/ -void CChapter::updateRecommendation() -{ - m_recom = RecommendationNone; - m_recom2 = RecommendationNone; - - m_recomRepeatDate = repeatDate(); /*QDate();*/ - if (m_listChapter.isEmpty() && m_listQuestion.isEmpty()) return; - - if (m_pParentChapter && m_pParentChapter->recommendation() != RecommendationSubChapter) - m_recom = RecommendationParentChapter; - else - { - if (countSubQuestion() > 20) - { // Nur wenn in den Unterkapiteln zusammen mehr als x Fragen sind, zuerst die Unterkapitel einzeln lernen lassen - for (int i=0; ilevelAvg() < LEVEL_NORMAL || p->countNeverAsked() > 0) - m_recom = RecommendationSubChapter; - } - } - - if (m_recom == RecommendationNone) - { - //m_recomRepeatDate = repeatDate(); - m_recom = recommendationIndividual(); - } - } - - // Alternative Empfehlung - if (m_recom == RecommendationParentChapter || m_recom == RecommendationSubChapter) - { - m_recom2 = recommendationIndividual(); - if (m_recom2 != RecommendationLearnNew && m_recom2 != RecommendationRepeatToday) - m_recom2 = RecommendationNone; - } - - // find recommended questions - m_listQuestionRecommended.clear(); - QList list = questionPool(); - for (int i=0; iisNeverAsked() || q->isLearningNew()) - m_listQuestionRecommended.append(q); - } - else if (m_recom == RecommendationRepeatToday || m_recom2 == RecommendationRepeatToday) - { - if (q->isRepeatToday()) - m_listQuestionRecommended.append(q); - } - } - - // update child chapters - for (int i=0; iupdateRecommendation(); - //m_listQuestionRecommended << p->m_listQuestionRecommended; - } -} -/* -bool CChapter::hasRecommendedQuestions() const -{ - return (m_recom == RecommendationLearnNew || m_recom == RecommendationRepeatToday); -} - -QList CChapter::recommendedQuestions() const -{ -QList listRet, list = questionPool(); - - if (m_recom != RecommendationLearnNew && m_recom != RecommendationRepeatToday) return listRet; - - for (int i=0; iisNeverAsked() || q->isLearningNew()) - listRet.append(q); - break; - case RecommendationRepeatToday: - if (q->isRepeatToday()) - listRet.append(q); - break; - } - } - return listRet; -} -*/ - -QString CChapter::recommendationText(const Recommendation r, const QDate dRepeat) -{ -unsigned uDays=0; - switch (r) - { - default: - case RecommendationNone: - return tr("Keine Lernempfehlung"); - case RecommendationSubChapter: - return tr("Zuerst Unterkapitel lernen"); - case RecommendationParentChapter: - return tr("Ãœbergeordnetes Kapitel lernen"); - case RecommendationLearnNew: - return tr("Neue Fragen lernen"); - case RecommendationRepeatToday: - return tr("Heute wiederholen"); - case RecommendationRepeatLater: - uDays = QDate::currentDate().daysTo(dRepeat); - if (uDays == 1) - return tr("Morgen wiederholen"); - else - return tr("In %1 Tagen wiederholen").arg(uDays); - } - return QString(); -} - -QString CChapter::recommendationText() const -{ - return recommendationText(m_recom, m_recomRepeatDate); -} - -QString CChapter::recommendationToolTip() const -{ -QString str = recommendationText(); -unsigned uCount = recommendedQuestionCount(); - if (hasRecommendedQuestions() && m_recom2 == RecommendationNone) - { - str += " "; - if (uCount == 1) - str += tr("(1 Frage)"); - else - str += tr("(%1 Fragen)").arg(uCount); - } - if (m_recom2 != RecommendationNone) - { - str += "\n" + tr("Alternativ: "); - if (m_recom2 == RecommendationLearnNew) - { - if (uCount == 1) - str += tr("1 neue Frage lernen"); - else - str += tr("%1 neue Fragen lernen").arg(uCount); - } - else if (m_recom2 == RecommendationRepeatToday) - { - if (uCount == 1) - str += tr("1 Frage wiederholen"); - else - str += tr("%1 Fragen wiederholen").arg(uCount); - } - else - str += recommendationText (m_recom2, m_recomRepeatDate); - } - return str; -} - -QString CChapter::recommendationTextExtended(const CCatalog *pCatalog) const -{ -unsigned uDays=0; -QString str; - - switch (m_recom) - { - default: - case RecommendationNone: - str = tr("Keine Lernempfehlung"); - break; - case RecommendationSubChapter: - str = tr("Dieses Kapitel enthält Unterkapitel, dessen Fragen Sie noch nicht ausreichend gelernt haben.\nEs wird empohlen in kleinen Etappen zu lernen und damit zuerst die Unterkapitel zu vertiefen."); - break; - case RecommendationParentChapter: - if (levelAvgRounded() >= LEVEL_NORMAL) - str = tr("Sie können die Fragen dieses Kapitels gut beantworten.\n"); - str += tr("Es wird empfohlen, alle Fragen des übergeordneten Kapitels gemischt zusammen zu lernen."); - break; - case RecommendationLearnNew: - if (!isRecommendedNow(pCatalog)) - str = tr("Es gibt andere Kapitel, deren Fragen heute wiederholt werden sollten. Bitte lernen Sie diese Kapitel zuerst."); - else - str = tr("Bitte beantworten Sie alle neuen Fragen mindestens einmal richtig."); - break; - case RecommendationRepeatToday: - str = tr("Bitte lernen Sie alle heute zu wiederholenden Fragen, bis sie eine Lernfortschritts-Stufe höher eingestuft sind."); - break; - case RecommendationRepeatLater: - uDays = QDate::currentDate().daysTo(m_recomRepeatDate); - if (uDays > 1) - str = tr("Die Wiederholung dieses Kapitels ist erst in %1 Tagen geplant.\n").arg(uDays); - else - str = tr("Die Wiederholung dieses Kapitels ist erst für morgen geplant.\n"); - if (pCatalog->m_uRecomCount[RecommendationRepeatToday] > 0) - str += tr("Es gibt andere Kapitel, deren Fragen heute wiederholt werden müssen. Bitte lernen Sie diese Kapitel zuerst."); - else if (pCatalog->m_uRecomCount[RecommendationLearnNew] > 0) - str += tr("Bitte lernen Sie zuerst Kapitel mit neuen Fragen."); - break; - } - - if (hasRecommendedQuestions() && isRecommendedNow(pCatalog)) - str += tr("

Dafür sind noch %1 Fragen zu lernen.").arg(recommendedQuestionCount()); - - return str; -} - -QString CChapter::recommendationTextExtended2(const CCatalog *pCatalog) const -{ -QString str; - - if (m_recom2 == RecommendationLearnNew || (m_recom == RecommendationLearnNew && !isRecommendedNow(pCatalog))) - { - //str = tr("Bitte beantworten Sie alle neuen Fragen mindestens einmal richtig."); - str = tr("Alternativ können Sie jetzt die neuen Fragen dieses Kapitels lernen (%1 Fragen).").arg(recommendedQuestionCount()); - } - else if (m_recom2 == RecommendationRepeatToday) - { - if (m_recom == RecommendationSubChapter) - str = tr("Bitte lernen Sie alle heute zu wiederholenden Fragen, bis sie eine Lernfortschritts-Stufe höher eingestuft sind (%1 Fragen).").arg(recommendedQuestionCount()); - else - str = tr("Alternativ können Sie jetzt die heute zu wiederholenden Fragen dieses Kapitels lernen (%1 Fragen).").arg(recommendedQuestionCount()); - } - - return str; -} - -QString CChapter::recommendationIconName(const Recommendation r, const CCatalog *pCatalog) -{ - switch (r) - { - case RecommendationSubChapter: - return QString(":/icons/16x16/button_cancel.png"); - case RecommendationParentChapter: - return QString(":/icons/16x16/button_ok.png"); - case RecommendationLearnNew: - if (pCatalog->m_uRecomCount[RecommendationRepeatToday] > 0) - return QString(":/icons/16x16/idea_gray.png"); - else - return QString(":/icons/16x16/idea.png"); - case RecommendationRepeatToday: - return QString(":/icons/16x16/idea.png"); - case RecommendationRepeatLater: - return QString(":/icons/16x16/idea_gray.png"); - default: - return QString(); - } -} - -QString CChapter::recommendationIconName(const CCatalog *pCatalog) const -{ - return recommendationIconName(m_recom, pCatalog); -} - -/*! -\return true: Kapitel kann jetzt gelernt werden, -false: Kapitel sollte überhaupt nicht oder erst später gelernt werden -*/ - -bool CChapter::isRecommendedNow(const CCatalog *pCatalog) const -{ - switch (m_recom) - { - case RecommendationSubChapter: - if (pCatalog->m_uRecomCount[RecommendationRepeatToday] > 0) - return true; - break; - case RecommendationRepeatToday: - return true; - case RecommendationLearnNew: - if (pCatalog->m_uRecomCount[RecommendationRepeatToday] == 0) - return true; - break; - default: - break; - } - return false; -} - -/* -\return true: Das Kapitel enthält noch neue Fragen, die gerade (="heute") gelernt werden -bool CChapter::isLearningNew() const -{ - for (int i=0; iisLearningNew()) return true; - - for (int i=0; iisLearningNew()) return true; - - return false; -} -*/ - -CDayStatistic CChapter::dayStatistic (const QDate& date) const -{ -CDayStatistic dsRet, ds; -QList listPool = questionPool(); - - for (int i=0; idayStatistic(date); - dsRet += ds; - } - if (listPool.size() != 0) dsRet.m_dLevel /= listPool.size(); - return dsRet; -} - -CDayStatistic CChapter::completeStatistic() const -{ - return dayStatistic(QDate()); -} - -QDateTime CChapter::firstAnswerClicked() const -{ -QList listPool = questionPool(); -QDateTime dtRet, dt; - - for (int i=0; ifirstClicked(); - if (dt.isNull()) continue; - if (dtRet.isNull() || dt < dtRet) dtRet = dt; - } - return dtRet; -} - -double CChapter::levelAvg() const -{ -double d=0.0; -double dCount=0.0; - - for (int i=0; i<=LEVEL_MAX; i++) - { - d += m_uLevelCount[i] * i; - dCount += m_uLevelCount[i]; - } - if (dCount != 0.0) d /= dCount; - return d; -} - -unsigned CChapter::levelAvgRounded() const -{ - return ((unsigned) (levelAvg()+0.5)); -} - -QString CChapter::levelAvgText() const -{ - return QString("%1").arg(CQuestion::levelText(levelAvgRounded())); -} - -QIcon CChapter::levelAvgIcon() const -{ - return QIcon(CQuestion::levelIconName(levelAvgRounded())); -} - -QPixmap CChapter::levelAvgPixmap() const -{ - return QPixmap(CQuestion::levelIconName(levelAvgRounded())); -} - -static bool chapterLessThan(const CChapter *c1, const CChapter *c2) -{ - return c1->id() < c2->id(); -} - -void CChapter::sortSubChapters(bool bSortQuestions) -{ - if (bSortQuestions) sortQuestions(); - qSort (m_listChapter.begin(), m_listChapter.end(), chapterLessThan); - - for (int i=0; isortSubChapters(bSortQuestions); - } -} - -static bool questionLessThan(const CQuestion *q1, const CQuestion *q2) -{ - return q1->id() < q2->id(); -} - -void CChapter::sortQuestions() -{ - qSort (m_listQuestion.begin(), m_listQuestion.end(), questionLessThan); -} - +/*************************************************************************** + * 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 + + +QString CChapter::tr (const char *sourceText, const char *comment) +{ + return QCoreApplication::translate("CChapter", sourceText, comment); +} + +void CChapter::clear() +{ + m_pParentChapter=0; + m_strId.clear(); + m_strText.clear(); + m_strComment.clear(); + m_recom = RecommendationNone; + m_recom2 = RecommendationNone; + qDeleteAll(m_listChapter); + qDeleteAll(m_listQuestion); + m_bHasLearningNew = false; + m_bHasKnownQuestions = false; + m_bHasKnownQuestionsRepeatToday = false; + m_uNeverAskedCount = 0; + for (int i=0; i<=LEVEL_MAX; i++) m_uLevelCount[i] = 0; + for (int i=0; i<=RecommendationMax; i++) m_uRecomCount[i] = 0; +// m_mapExam.clear(); +} + +int CChapter::countSubQuestion() const +{ +int i=0, iRet=0; + for (i=0; icountSubQuestion(); + return (iRet + m_listQuestion.size()); +} + +bool CChapter::load (QDomElement elem) +{ +CChapter *pChapter=0; + + if (elem.tagName () != QString ("chapter")) return false; + if (!elem.hasAttribute ("name")) return false; +// if (pParent != NULL && !elem.hasAttribute ("id")) return false; + m_strId = elem.attribute ("id"); + m_strText = elem.attribute ("name"); + QDomNode n = elem.firstChild(); + while (!n.isNull()) + { + if (n.isElement ()) + { + QDomElement e = n.toElement (); + if (e.tagName() == "comment") + m_strComment = e.text(); + else if (e.tagName() == QString ("chapter")) + { + pChapter = new CChapter(); + if (pChapter->load (e)) + appendChapter(pChapter); + else + delete pChapter; + } + else if (e.tagName() == QString ("question")) + { + CQuestion *pQuestion = new CQuestion(); + if (pQuestion->load (e)) + appendQuestion(pQuestion); + else + delete pQuestion; + } + } + n = n.nextSibling(); + } + updateStatistic(); + return true; +} + +void CChapter::save (QDomElement& parent, QDomDocument& doc) +{ +QDomElement elem = doc.createElement("chapter"); + + elem.setAttribute("name", text()); + elem.setAttribute("id", id()); + + if (!m_strComment.isEmpty()) + { + QDomElement elemComment = doc.createElement("comment"); + QDomText textComment = doc.createTextNode(text()); + elemComment.appendChild(textComment); + elem.appendChild(elemComment); + } + + parent.appendChild(elem); + + // save exams +/* QStringList strl = m_mapExam.keys(); + for (int i=0; isave(elem, doc); + } + + // save questions + for (int i=0; isave(elem, doc); + } +} + +bool CChapter::loadLearnStatistic (QDomElement elem) +{ +QList listQuestionPool = questionPool(); + + if (elem.tagName () != QString ("learning")) return false; + + QDomNode n = elem.firstChild(); + while (!n.isNull()) + { + if (n.isElement ()) + { + QDomElement e = n.toElement (); + if (e.tagName() == QString ("question")) + { + QString strId = e.attribute("id"); + for (int i=0; iid() == strId) + listQuestionPool[i]->loadLearnStatistic(e); + } + } + } + n = n.nextSibling(); + } + updateRecommendation(); + return true; +} + +bool CChapter::saveLearnStatistic (QDomElement& parent, QDomDocument& doc) +{ + // questions from sub-chapters + for (int i=0; isaveLearnStatistic(parent, doc); + } + + // save questions + for (int i=0; isaveLearnStatistic(parent, doc); + } + return true; +} + +QString CChapter::checkForErrors() const +{ +QString str; + + // check chapters + for (int i=0; icheckForErrors(); + } + + // check questions + for (int i=0; icheckForErrors(); + } + return str; +} + +QList CChapter::subChapters() const +{ +QList list; + + for (int i=0; isubChapters(); + } + return list; +} + +QList CChapter::questionPool() const +{ +QList list; + for (int i=0; iquestionPool(); + } + list << m_listQuestion; + return list; +} + +QList CChapter::questionPoolLevel(const unsigned uLevel) const +{ +QList list; +int i; + for (i=0; iquestionPoolLevel(uLevel); + for (i=0; ilevel() == uLevel) + list.append (m_listQuestion.at(i)); + } + return list; +} + +QList CChapter::questionPoolDeepen() const +{ +QList list; +int i; + for (i=0; iquestionPoolDeepen(); + for (i=0; ilevel() == LEVEL_VERYOFTEN && !q->isNeverAsked()) + list.append (q); + } + return list; +} + +QList CChapter::questionPoolRepeat(const QDate d) const +{ +QList list; +int i; + for (i=0; iquestionPoolRepeat(d); + for (i=0; irepeatDate() <= d) + list.append (q); + } + return list; +} + + +void CChapter::updateStatisticCount() +{ +/* for (int i=0; iisLearningNew()) return true; + + for (int i=0; iisLearningNew()) return true; + + return false; +*/ + // Alle Statistiken zurücksetzen + m_uNeverAskedCount = 0; + m_bHasLearningNew = false; + m_bHasKnownQuestions = false; + m_bHasKnownQuestionsRepeatToday = false; + for (int i=0; i<=LEVEL_MAX; i++) m_uLevelCount[i] = 0; + + // Zuerst Statistiken für Unterkapitel berechnen und integrieren + for (int i=0; iupdateStatisticCount(); + for (int j=0; j<=LEVEL_MAX; j++) + m_uLevelCount[j] += p->m_uLevelCount[j]; + m_uNeverAskedCount += p->m_uNeverAskedCount; + if (p->hasLearningNewQuestions()) m_bHasLearningNew = true; + if (p->hasKnownQuestions()) m_bHasKnownQuestions = true; + if (p->hasKnownQuestionsRepeatToday()) m_bHasKnownQuestionsRepeatToday = true; + } + + // Anschließend Statistiken für eigene Fragen neu berechnen und hinzufügen + for (int i=0; iisNeverAsked()) m_uNeverAskedCount++; + m_uLevelCount[q->level()]++; + if (q->isLearningNew()) m_bHasLearningNew = true; + if (q->isKnownQuestion()) m_bHasKnownQuestions = true; + if (q->isKnownQuestion() && q->isRepeatToday()) m_bHasKnownQuestionsRepeatToday = true; + } +} + +void CChapter::updateRecommendationStatistic() +{ + for (int i=0; iupdateRecommendationStatistic(); + for (int j=0; jm_uRecomCount[j]; + } +} + +/* +Berechnet die Statistik für das Kapitel inkl. aller Unterkapitel neu. +*/ +void CChapter::updateStatistic() +{ + updateStatisticCount(); + updateRecommendation(); + updateRecommendationStatistic(); +} + +/* +CRecommendation CChapter::recommendation() const +{ +CRecommendation r; + r.create(this); + return r; +} +*/ +/* +CRecommendationStatistic CChapter::recommendationStatistic() const +{ +CRecommendationStatistic rs; + + for (int i=0; irecommendationStatistic()); + } + //TODO +// rs.append(recommendation()); + return rs; +} +*/ + +QString CChapter::idWithParents() const +{ +CChapter *pParent=m_pParentChapter; +QString str; + str = id(); + while (pParent != 0) + { + str = pParent->id() + str; + pParent = pParent->m_pParentChapter; + } + + return str; +} + +/*! +Das empfohlene Wiederholdatum eines Kapitels das frühestete Datum, +an dem eine Frage des Kapitels oder Unterkapitels wiederholt werden sollte. +\return Datum, wann das Kapitel wiederholt werden soll. +Gibt es kein solches Datum, so wird ein ungültiges Datum (QDate::isValid()) zurückgegeben. +Hinweis: Das Datum kann auch in der Vergangenheit liegen! +*/ +QDate CChapter::repeatDate() const +{ +QDate d; +QList list = questionPool(); + + for (int i=0; irepeatDate(); + if (!dq.isValid()) continue; + if (!d.isValid() || dq < d) d = dq; + } + return d; +} + +/*! +Die Variable m_recomRepeatDate muss up to date sein! + +\return Lernempfehlung dieses Kapitels ohne Berücksichtigung evt. vorhandener ünter- oder übergeordneter Kapitel. + +\sa m_recomRepeatDate +*/ +CChapter::Recommendation CChapter::recommendationIndividual() const +{ + if (!m_recomRepeatDate.isValid()) + return RecommendationLearnNew; + else if (m_recomRepeatDate <= QDate::currentDate()) + { + if (hasLearningNewQuestions() && !hasKnownQuestionsRepeatToday()) + return RecommendationLearnNew; + else + return RecommendationRepeatToday; + } + else + { + if (countNeverAsked() > 0) + return RecommendationLearnNew; + else + return RecommendationRepeatLater; + } +} + +/*! +Aktualisiert die Lernempfehlung für dieses Kapitel und alle Unterkapitel +\sa m_recom +*/ +void CChapter::updateRecommendation() +{ + m_recom = RecommendationNone; + m_recom2 = RecommendationNone; + + m_recomRepeatDate = repeatDate(); /*QDate();*/ + if (m_listChapter.isEmpty() && m_listQuestion.isEmpty()) return; + + if (m_pParentChapter && m_pParentChapter->recommendation() != RecommendationSubChapter) + m_recom = RecommendationParentChapter; + else + { + if (countSubQuestion() > 20) + { // Nur wenn in den Unterkapiteln zusammen mehr als x Fragen sind, zuerst die Unterkapitel einzeln lernen lassen + for (int i=0; ilevelAvg() < LEVEL_NORMAL || p->countNeverAsked() > 0) + m_recom = RecommendationSubChapter; + } + } + + if (m_recom == RecommendationNone) + { + //m_recomRepeatDate = repeatDate(); + m_recom = recommendationIndividual(); + } + } + + // Alternative Empfehlung + if (m_recom == RecommendationParentChapter || m_recom == RecommendationSubChapter) + { + m_recom2 = recommendationIndividual(); + if (m_recom2 != RecommendationLearnNew && m_recom2 != RecommendationRepeatToday) + m_recom2 = RecommendationNone; + } + + // find recommended questions + m_listQuestionRecommended.clear(); + QList list = questionPool(); + for (int i=0; iisNeverAsked() || q->isLearningNew()) + m_listQuestionRecommended.append(q); + } + else if (m_recom == RecommendationRepeatToday || m_recom2 == RecommendationRepeatToday) + { + if (q->isRepeatToday()) + m_listQuestionRecommended.append(q); + } + } + + // update child chapters + for (int i=0; iupdateRecommendation(); + //m_listQuestionRecommended << p->m_listQuestionRecommended; + } +} +/* +bool CChapter::hasRecommendedQuestions() const +{ + return (m_recom == RecommendationLearnNew || m_recom == RecommendationRepeatToday); +} + +QList CChapter::recommendedQuestions() const +{ +QList listRet, list = questionPool(); + + if (m_recom != RecommendationLearnNew && m_recom != RecommendationRepeatToday) return listRet; + + for (int i=0; iisNeverAsked() || q->isLearningNew()) + listRet.append(q); + break; + case RecommendationRepeatToday: + if (q->isRepeatToday()) + listRet.append(q); + break; + } + } + return listRet; +} +*/ + +QString CChapter::recommendationText(const Recommendation r, const QDate dRepeat) +{ +unsigned uDays=0; + switch (r) + { + default: + case RecommendationNone: + return tr("Keine Lernempfehlung"); + case RecommendationSubChapter: + return tr("Zuerst Unterkapitel lernen"); + case RecommendationParentChapter: + return tr("Übergeordnetes Kapitel lernen"); + case RecommendationLearnNew: + return tr("Neue Fragen lernen"); + case RecommendationRepeatToday: + return tr("Heute wiederholen"); + case RecommendationRepeatLater: + uDays = QDate::currentDate().daysTo(dRepeat); + if (uDays == 1) + return tr("Morgen wiederholen"); + else + return tr("In %1 Tagen wiederholen").arg(uDays); + } + return QString(); +} + +QString CChapter::recommendationText() const +{ + return recommendationText(m_recom, m_recomRepeatDate); +} + +QString CChapter::recommendationToolTip() const +{ +QString str = recommendationText(); +unsigned uCount = recommendedQuestionCount(); + if (hasRecommendedQuestions() && m_recom2 == RecommendationNone) + { + str += " "; + if (uCount == 1) + str += tr("(1 Frage)"); + else + str += tr("(%1 Fragen)").arg(uCount); + } + if (m_recom2 != RecommendationNone) + { + str += "\n" + tr("Alternativ: "); + if (m_recom2 == RecommendationLearnNew) + { + if (uCount == 1) + str += tr("1 neue Frage lernen"); + else + str += tr("%1 neue Fragen lernen").arg(uCount); + } + else if (m_recom2 == RecommendationRepeatToday) + { + if (uCount == 1) + str += tr("1 Frage wiederholen"); + else + str += tr("%1 Fragen wiederholen").arg(uCount); + } + else + str += recommendationText (m_recom2, m_recomRepeatDate); + } + return str; +} + +QString CChapter::recommendationTextExtended(const CCatalog *pCatalog) const +{ +unsigned uDays=0; +QString str; + + switch (m_recom) + { + default: + case RecommendationNone: + str = tr("Keine Lernempfehlung"); + break; + case RecommendationSubChapter: + str = tr("Dieses Kapitel enthält Unterkapitel, dessen Fragen Sie noch nicht ausreichend gelernt haben.\nEs wird empohlen in kleinen Etappen zu lernen und damit zuerst die Unterkapitel zu vertiefen."); + break; + case RecommendationParentChapter: + if (levelAvgRounded() >= LEVEL_NORMAL) + str = tr("Sie können die Fragen dieses Kapitels gut beantworten.\n"); + str += tr("Es wird empfohlen, alle Fragen des übergeordneten Kapitels gemischt zusammen zu lernen."); + break; + case RecommendationLearnNew: + if (!isRecommendedNow(pCatalog)) + str = tr("Es gibt andere Kapitel, deren Fragen heute wiederholt werden sollten. Bitte lernen Sie diese Kapitel zuerst."); + else + str = tr("Bitte beantworten Sie alle neuen Fragen mindestens einmal richtig."); + break; + case RecommendationRepeatToday: + str = tr("Bitte lernen Sie alle heute zu wiederholenden Fragen, bis sie eine Lernfortschritts-Stufe höher eingestuft sind."); + break; + case RecommendationRepeatLater: + uDays = QDate::currentDate().daysTo(m_recomRepeatDate); + if (uDays > 1) + str = tr("Die Wiederholung dieses Kapitels ist erst in %1 Tagen geplant.\n").arg(uDays); + else + str = tr("Die Wiederholung dieses Kapitels ist erst für morgen geplant.\n"); + if (pCatalog->m_uRecomCount[RecommendationRepeatToday] > 0) + str += tr("Es gibt andere Kapitel, deren Fragen heute wiederholt werden müssen. Bitte lernen Sie diese Kapitel zuerst."); + else if (pCatalog->m_uRecomCount[RecommendationLearnNew] > 0) + str += tr("Bitte lernen Sie zuerst Kapitel mit neuen Fragen."); + break; + } + + if (hasRecommendedQuestions() && isRecommendedNow(pCatalog)) + str += tr("

Dafür sind noch %1 Fragen zu lernen.").arg(recommendedQuestionCount()); + + return str; +} + +QString CChapter::recommendationTextExtended2(const CCatalog *pCatalog) const +{ +QString str; + + if (m_recom2 == RecommendationLearnNew || (m_recom == RecommendationLearnNew && !isRecommendedNow(pCatalog))) + { + //str = tr("Bitte beantworten Sie alle neuen Fragen mindestens einmal richtig."); + str = tr("Alternativ können Sie jetzt die neuen Fragen dieses Kapitels lernen (%1 Fragen).").arg(recommendedQuestionCount()); + } + else if (m_recom2 == RecommendationRepeatToday) + { + if (m_recom == RecommendationSubChapter) + str = tr("Bitte lernen Sie alle heute zu wiederholenden Fragen, bis sie eine Lernfortschritts-Stufe höher eingestuft sind (%1 Fragen).").arg(recommendedQuestionCount()); + else + str = tr("Alternativ können Sie jetzt die heute zu wiederholenden Fragen dieses Kapitels lernen (%1 Fragen).").arg(recommendedQuestionCount()); + } + + return str; +} + +QString CChapter::recommendationIconName(const Recommendation r, const CCatalog *pCatalog) +{ + switch (r) + { + case RecommendationSubChapter: + return QString(":/icons/16x16/button_cancel.png"); + case RecommendationParentChapter: + return QString(":/icons/16x16/button_ok.png"); + case RecommendationLearnNew: + if (pCatalog->m_uRecomCount[RecommendationRepeatToday] > 0) + return QString(":/icons/16x16/idea_gray.png"); + else + return QString(":/icons/16x16/idea.png"); + case RecommendationRepeatToday: + return QString(":/icons/16x16/idea.png"); + case RecommendationRepeatLater: + return QString(":/icons/16x16/idea_gray.png"); + default: + return QString(); + } +} + +QString CChapter::recommendationIconName(const CCatalog *pCatalog) const +{ + return recommendationIconName(m_recom, pCatalog); +} + +/*! +\return true: Kapitel kann jetzt gelernt werden, +false: Kapitel sollte überhaupt nicht oder erst später gelernt werden +*/ + +bool CChapter::isRecommendedNow(const CCatalog *pCatalog) const +{ + switch (m_recom) + { + case RecommendationSubChapter: + if (pCatalog->m_uRecomCount[RecommendationRepeatToday] > 0) + return true; + break; + case RecommendationRepeatToday: + return true; + case RecommendationLearnNew: + if (pCatalog->m_uRecomCount[RecommendationRepeatToday] == 0) + return true; + break; + default: + break; + } + return false; +} + +/* +\return true: Das Kapitel enthält noch neue Fragen, die gerade (="heute") gelernt werden +bool CChapter::isLearningNew() const +{ + for (int i=0; iisLearningNew()) return true; + + for (int i=0; iisLearningNew()) return true; + + return false; +} +*/ + +CDayStatistic CChapter::dayStatistic (const QDate& date) const +{ +CDayStatistic dsRet, ds; +QList listPool = questionPool(); + + for (int i=0; idayStatistic(date); + dsRet += ds; + } + if (listPool.size() != 0) dsRet.m_dLevel /= listPool.size(); + return dsRet; +} + +CDayStatistic CChapter::completeStatistic() const +{ + return dayStatistic(QDate()); +} + +QDateTime CChapter::firstAnswerClicked() const +{ +QList listPool = questionPool(); +QDateTime dtRet, dt; + + for (int i=0; ifirstClicked(); + if (dt.isNull()) continue; + if (dtRet.isNull() || dt < dtRet) dtRet = dt; + } + return dtRet; +} + +double CChapter::levelAvg() const +{ +double d=0.0; +double dCount=0.0; + + for (int i=0; i<=LEVEL_MAX; i++) + { + d += m_uLevelCount[i] * i; + dCount += m_uLevelCount[i]; + } + if (dCount != 0.0) d /= dCount; + return d; +} + +unsigned CChapter::levelAvgRounded() const +{ + return ((unsigned) (levelAvg()+0.5)); +} + +QString CChapter::levelAvgText() const +{ + return QString("%1").arg(CQuestion::levelText(levelAvgRounded())); +} + +QIcon CChapter::levelAvgIcon() const +{ + return QIcon(CQuestion::levelIconName(levelAvgRounded())); +} + +QPixmap CChapter::levelAvgPixmap() const +{ + return QPixmap(CQuestion::levelIconName(levelAvgRounded())); +} + +static bool chapterLessThan(const CChapter *c1, const CChapter *c2) +{ + return c1->id() < c2->id(); +} + +void CChapter::sortSubChapters(bool bSortQuestions) +{ + if (bSortQuestions) sortQuestions(); + qSort (m_listChapter.begin(), m_listChapter.end(), chapterLessThan); + + for (int i=0; isortSubChapters(bSortQuestions); + } +} + +static bool questionLessThan(const CQuestion *q1, const CQuestion *q2) +{ + return q1->id() < q2->id(); +} + +void CChapter::sortQuestions() +{ + qSort (m_listQuestion.begin(), m_listQuestion.end(), questionLessThan); +} + diff --git a/chapter.h b/chapter.h index ca7e679..527159b 100644 --- a/chapter.h +++ b/chapter.h @@ -1,242 +1,242 @@ -/*************************************************************************** - * 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. * - ***************************************************************************/ - -#ifndef CHAPTER_H -#define CHAPTER_H - -#include "question.h" -//#include "recommendation.h" -//#include "chapterstatistic.h" - -#include - -//! Die Klasse CChapter speichert ein Kapitel mit allen Unterkapiteln und Fragen -/*! -*/ - -class CChapter -{ -public: - //! Empfehlung für Kapitel - enum Recommendation - { - RecommendationNone=0, //!< Keine Emfehlung - RecommendationSubChapter=1, //!< Untergeordnetes Kapitel lernen - RecommendationParentChapter=2, //!< Übergeordnetes Kapitel lernen - RecommendationRepeatToday=3, //!< Kapitel heute wiederholen - RecommendationLearnNew=4, //!< Neue Fragen lernen - RecommendationRepeatLater=5, //!< Kapitel später wiederholen - RecommendationMax=6 //!< Für for-Schleifen, etc. - }; - - //! Standard-Konstruktor - /*! Initialisiert die Klasse, indem die Funktion clear() aufgerufen wird. */ - CChapter() { clear(); } - - //! Standard-Destruktor - /*! Es werden alle Unterkapitel und Fragen dieses Kapitels aus dem Speicher gelöscht. */ - ~CChapter() { qDeleteAll(m_listChapter); qDeleteAll(m_listQuestion); } - - //! Zurücksetzen aller Werte - /*! Es werden alle Unterkapitel und Fragen dieses Kapitels aus dem Speicher gelöscht und alle andere Daten auf die Default-Werte zurückgesetzt. */ - void clear(); - - //! Elternkapitel auslesen - /*! - \return Elternkapitel m_pParentChapter - \sa setParentChapter(), m_pParentChapter - */ - inline CChapter* parentChapter() const { return m_pParentChapter; } - - //! Elternkapitel setzen - /*! - Durch diese Funktion wird lediglich die Variable m_pParentChapter gesetzt, jedoch nicht die Statistiken - des Eltern-Kapitels aktualisiert. - - \param pChapter Das zu setzende Elternkapitel dieses Kapitels - \sa parentChapter(), m_pParentChapter - */ - inline void setParentChapter(CChapter *pChapter) { m_pParentChapter = pChapter; } - - //! ID dieses Kapitels auslesen - inline QString id() const { return m_strId; } - //! ID dieses Kapitels zusammengesetzt mit allen IDs der Eltern-Kapitel auslesen - QString idWithParents() const; - //! Kapitelbeschreibung auslesen - inline QString text() const { return m_strText; } - //! Kommentar - inline QString comment() const { return m_strComment; } - - //! ID setzen - inline void setId (const QString& strId) { m_strId = strId; } - //! Kapitelbeschreibung setzen - inline void setText (const QString& strText) { m_strText = strText; } - //! Kommentar setzen - inline void setComment (const QString& strComment) { m_strComment = strComment; } - - //! Anhängen eines Textes an die Kapitelbeschreibung - /*! - \param strText Anzuhängender Text - \sa m_strText - */ - inline void appendText(const QString& strText) { m_strText += strText; } - - - //! Anzahl der Unterkapitel ermitteln - /*! \return Anzahl der Unterkapitel */ - inline int countChapter() const { return m_listChapter.size(); } - inline const CChapter* chapterAt(int i) const { return m_listChapter.at(i); } - inline int indexOfChapter(CChapter* c) const { return m_listChapter.indexOf(c); } - inline void appendChapter(CChapter* c) { m_listChapter.append(c); c->setParentChapter(this); } -// inline void removeChapter(int i) { delete m_listChapter.takeAt(i); } - QList subChapters() const; - inline void sortAll() { sortSubChapters(true); sortQuestions(); } - void sortSubChapters(bool bSortQuestions); - void sortQuestions(); - - //! Anzahl der Fragen dieses Kapitels und aller Unterkapitel ermitteln - /*! \return Anzahl der Fragen */ - int countSubQuestion() const; - //! Anzahl der Fragen dieses Kapitels (ohne Unterkapitel) ermitteln - /*! \return Anzahl der Fragen */ - inline int countQuestion() const { return m_listQuestion.size(); } - inline const CQuestion* questionAt(int i) const { return m_listQuestion.at(i); } - inline CQuestion* questionAt(int i) { return m_listQuestion.at(i); } - inline void appendQuestion(CQuestion* q) { m_listQuestion.append(q); q->setParentChapter(this); } - - QList questionPool() const; - QList questionPoolLevel(const unsigned uLevel) const; - QList questionPoolDeepen() const; - QList questionPoolRepeat(const QDate d=QDate::currentDate()) const; - - - - - bool load (QDomElement elem); - void save (QDomElement& parent, QDomDocument& doc); - bool loadLearnStatistic (QDomElement elem); - bool saveLearnStatistic (QDomElement& parent, QDomDocument& doc); - - QString checkForErrors() const; - - - // Kapitelstatistik auslesen - /* - \return Kapitelstatistik (Variable m_cs) - \sa m_cs - */ -// inline CChapterStatistic statistic() const { return m_cs; } - - - //! @name Funktionen zur Statistik - //@{ - void updateStatistic(); //!< Kapitelstatistik aktualisieren - //! Anzahl der Fragen einer bestimmten Abfragehäufigkeit - inline unsigned countQuestion(const unsigned uLevel) const { return m_uLevelCount[uLevel]; } - double levelAvg() const; //!< Durchschnittlicher Lernfortschritt des Kapitels - unsigned levelAvgRounded() const; //!< Durchschnittlicher, gerundeter Lernfortschritt des Kapitels - QString levelAvgText() const; //!< Durchschnittlicher Lernfortschritt als Text - QIcon levelAvgIcon() const; //!< Icon zum durchschnittlichen Lernfortschritt - QPixmap levelAvgPixmap() const; //!< Pixmap zum durchschnittlichen Lernfortschritt - //! Anzahl der noch nie abgefragen Fragen - inline unsigned countNeverAsked() const { return m_uNeverAskedCount; } - //!< Kapitel inkl. Unterkapitel enthalten Fragen, die gerade gelernt werden - inline bool hasLearningNewQuestions() const { return m_bHasLearningNew; } - inline bool hasKnownQuestions() const { return m_bHasKnownQuestions; } - inline bool hasKnownQuestionsRepeatToday() const { return m_bHasKnownQuestionsRepeatToday; } - - CDayStatistic dayStatistic (const QDate& date) const; // recommendedQuestions() const { return m_listQuestionRecommended; } - //! Kapitel hat Fragen, die beantwortet müssen, um die Lernempfehlung zu erfüllen - inline bool hasRecommendedQuestions() const { return m_listQuestionRecommended.size() > 0; } - //! Anzahl der Fragen, die beantwortet müssen, um die Lernempfehlung zu erfüllen - inline int recommendedQuestionCount() const { return m_listQuestionRecommended.size(); } - //! Alternative Lernempfehlung für das Kapitel - inline Recommendation recommendation2() const { return m_recom2; } - QString recommendationTextExtended2(const CCatalog *pCatalog) const; //!< Alternativer Lernempfehlungstext (ausführlich) - //@} - - //! @name Funktionen zu Prüfungen - //@{ -// inline void setExam(const QString& strId, const unsigned uQuestionCount) { m_mapExam.insert(strId, uQuestionCount); } -// inline unsigned examQuestionCount(const QString& strId) const { return m_mapExam[strId]; } - //@} - - // static - static QString tr (const char *sourceText, const char *comment=0); - static QString recommendationText(const Recommendation r, const QDate dRepeat); - static QString recommendationIconName(const Recommendation r, const CCatalog *pCatalog); - -protected: - void updateStatisticCount(); //!< Zählt alle Fragen des Kapitels (inkl. Unterkapitel) für die Statistik - void updateRecommendation(); //!< Lernempfehlung für das Kapitel (inkl. Unterkapitel) aktualisieren - void updateRecommendationStatistic(); //!< Statistik über Lernempfehlungen für das Kapitel (inkl. Unterkapitel) aktualisieren - Recommendation recommendationIndividual() const; //!< Empfehlung nur für dieses Kapitel, Unterkapitel und Elternkapitel werden ignoriert - -protected: - CChapter *m_pParentChapter; //!< Übergeordnetes Kapitel - QString m_strId; //!< ID des Kapitels - QString m_strText; //!< Name des Kapitels - QString m_strComment; //!< Kommentar - - QList m_listChapter; //!< Liste mit Unterkapiteln - QList m_listQuestion; //!< Liste mit Fragen - - // Statistik - unsigned m_uLevelCount[LEVEL_MAX+1]; //!< Anzahl der Fragen einer bestimmten Abfragehäufigkeit - unsigned m_uNeverAskedCount; //!< Anzahl der Fragen, die noch nie abgefragt wurden - unsigned m_uRecomCount[RecommendationMax]; //!< Anzahl der Unterkapitel (inkl. selbst) mit bestimmter Lernempfehlung - bool m_bHasLearningNew; //!< Kapitel enthält Fragen, die gerade neu gelernt werden - bool m_bHasKnownQuestions; //!< Kapitel enthält Fragen, die seit mind. gestern bekannt sind - bool m_bHasKnownQuestionsRepeatToday; //!< Kapitel enthält Fragen, die seit mind. gestern bekannt sind und heute wiederholt werden sollten - - Recommendation m_recom; //!< Lernempfehlung - Recommendation m_recom2; //!< Alternative Lernempfehlung - - //! Empfohlenes Datum zur Wiederholung der Fragen - /*! Diese Variable wird mit der Funktion updateRecommendation() aktualisiert */ - QDate m_recomRepeatDate; - //! Liste mit Fragen zum Erfüllen der Lernempfehlung - /*! Diese Liste wird mit der Funktion updateRecommendation() aktualisiert */ - QList m_listQuestionRecommended; -}; - -#endif +/*************************************************************************** + * 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. * + ***************************************************************************/ + +#ifndef CHAPTER_H +#define CHAPTER_H + +#include "question.h" +//#include "recommendation.h" +//#include "chapterstatistic.h" + +#include + +//! Die Klasse CChapter speichert ein Kapitel mit allen Unterkapiteln und Fragen +/*! +*/ + +class CChapter +{ +public: + //! Empfehlung für Kapitel + enum Recommendation + { + RecommendationNone=0, //!< Keine Emfehlung + RecommendationSubChapter=1, //!< Untergeordnetes Kapitel lernen + RecommendationParentChapter=2, //!< Übergeordnetes Kapitel lernen + RecommendationRepeatToday=3, //!< Kapitel heute wiederholen + RecommendationLearnNew=4, //!< Neue Fragen lernen + RecommendationRepeatLater=5, //!< Kapitel später wiederholen + RecommendationMax=6 //!< Für for-Schleifen, etc. + }; + + //! Standard-Konstruktor + /*! Initialisiert die Klasse, indem die Funktion clear() aufgerufen wird. */ + CChapter() { clear(); } + + //! Standard-Destruktor + /*! Es werden alle Unterkapitel und Fragen dieses Kapitels aus dem Speicher gelöscht. */ + ~CChapter() { qDeleteAll(m_listChapter); qDeleteAll(m_listQuestion); } + + //! Zurücksetzen aller Werte + /*! Es werden alle Unterkapitel und Fragen dieses Kapitels aus dem Speicher gelöscht und alle andere Daten auf die Default-Werte zurückgesetzt. */ + void clear(); + + //! Elternkapitel auslesen + /*! + \return Elternkapitel m_pParentChapter + \sa setParentChapter(), m_pParentChapter + */ + inline CChapter* parentChapter() const { return m_pParentChapter; } + + //! Elternkapitel setzen + /*! + Durch diese Funktion wird lediglich die Variable m_pParentChapter gesetzt, jedoch nicht die Statistiken + des Eltern-Kapitels aktualisiert. + + \param pChapter Das zu setzende Elternkapitel dieses Kapitels + \sa parentChapter(), m_pParentChapter + */ + inline void setParentChapter(CChapter *pChapter) { m_pParentChapter = pChapter; } + + //! ID dieses Kapitels auslesen + inline QString id() const { return m_strId; } + //! ID dieses Kapitels zusammengesetzt mit allen IDs der Eltern-Kapitel auslesen + QString idWithParents() const; + //! Kapitelbeschreibung auslesen + inline QString text() const { return m_strText; } + //! Kommentar + inline QString comment() const { return m_strComment; } + + //! ID setzen + inline void setId (const QString& strId) { m_strId = strId; } + //! Kapitelbeschreibung setzen + inline void setText (const QString& strText) { m_strText = strText; } + //! Kommentar setzen + inline void setComment (const QString& strComment) { m_strComment = strComment; } + + //! Anhängen eines Textes an die Kapitelbeschreibung + /*! + \param strText Anzuhängender Text + \sa m_strText + */ + inline void appendText(const QString& strText) { m_strText += strText; } + + + //! Anzahl der Unterkapitel ermitteln + /*! \return Anzahl der Unterkapitel */ + inline int countChapter() const { return m_listChapter.size(); } + inline const CChapter* chapterAt(int i) const { return m_listChapter.at(i); } + inline int indexOfChapter(CChapter* c) const { return m_listChapter.indexOf(c); } + inline void appendChapter(CChapter* c) { m_listChapter.append(c); c->setParentChapter(this); } +// inline void removeChapter(int i) { delete m_listChapter.takeAt(i); } + QList subChapters() const; + inline void sortAll() { sortSubChapters(true); sortQuestions(); } + void sortSubChapters(bool bSortQuestions); + void sortQuestions(); + + //! Anzahl der Fragen dieses Kapitels und aller Unterkapitel ermitteln + /*! \return Anzahl der Fragen */ + int countSubQuestion() const; + //! Anzahl der Fragen dieses Kapitels (ohne Unterkapitel) ermitteln + /*! \return Anzahl der Fragen */ + inline int countQuestion() const { return m_listQuestion.size(); } + inline const CQuestion* questionAt(int i) const { return m_listQuestion.at(i); } + inline CQuestion* questionAt(int i) { return m_listQuestion.at(i); } + inline void appendQuestion(CQuestion* q) { m_listQuestion.append(q); q->setParentChapter(this); } + + QList questionPool() const; + QList questionPoolLevel(const unsigned uLevel) const; + QList questionPoolDeepen() const; + QList questionPoolRepeat(const QDate d=QDate::currentDate()) const; + + + + + bool load (QDomElement elem); + void save (QDomElement& parent, QDomDocument& doc); + bool loadLearnStatistic (QDomElement elem); + bool saveLearnStatistic (QDomElement& parent, QDomDocument& doc); + + QString checkForErrors() const; + + + // Kapitelstatistik auslesen + /* + \return Kapitelstatistik (Variable m_cs) + \sa m_cs + */ +// inline CChapterStatistic statistic() const { return m_cs; } + + + //! @name Funktionen zur Statistik + //@{ + void updateStatistic(); //!< Kapitelstatistik aktualisieren + //! Anzahl der Fragen einer bestimmten Abfragehäufigkeit + inline unsigned countQuestion(const unsigned uLevel) const { return m_uLevelCount[uLevel]; } + double levelAvg() const; //!< Durchschnittlicher Lernfortschritt des Kapitels + unsigned levelAvgRounded() const; //!< Durchschnittlicher, gerundeter Lernfortschritt des Kapitels + QString levelAvgText() const; //!< Durchschnittlicher Lernfortschritt als Text + QIcon levelAvgIcon() const; //!< Icon zum durchschnittlichen Lernfortschritt + QPixmap levelAvgPixmap() const; //!< Pixmap zum durchschnittlichen Lernfortschritt + //! Anzahl der noch nie abgefragen Fragen + inline unsigned countNeverAsked() const { return m_uNeverAskedCount; } + //!< Kapitel inkl. Unterkapitel enthalten Fragen, die gerade gelernt werden + inline bool hasLearningNewQuestions() const { return m_bHasLearningNew; } + inline bool hasKnownQuestions() const { return m_bHasKnownQuestions; } + inline bool hasKnownQuestionsRepeatToday() const { return m_bHasKnownQuestionsRepeatToday; } + + CDayStatistic dayStatistic (const QDate& date) const; // recommendedQuestions() const { return m_listQuestionRecommended; } + //! Kapitel hat Fragen, die beantwortet müssen, um die Lernempfehlung zu erfüllen + inline bool hasRecommendedQuestions() const { return m_listQuestionRecommended.size() > 0; } + //! Anzahl der Fragen, die beantwortet müssen, um die Lernempfehlung zu erfüllen + inline int recommendedQuestionCount() const { return m_listQuestionRecommended.size(); } + //! Alternative Lernempfehlung für das Kapitel + inline Recommendation recommendation2() const { return m_recom2; } + QString recommendationTextExtended2(const CCatalog *pCatalog) const; //!< Alternativer Lernempfehlungstext (ausführlich) + //@} + + //! @name Funktionen zu Prüfungen + //@{ +// inline void setExam(const QString& strId, const unsigned uQuestionCount) { m_mapExam.insert(strId, uQuestionCount); } +// inline unsigned examQuestionCount(const QString& strId) const { return m_mapExam[strId]; } + //@} + + // static + static QString tr (const char *sourceText, const char *comment=0); + static QString recommendationText(const Recommendation r, const QDate dRepeat); + static QString recommendationIconName(const Recommendation r, const CCatalog *pCatalog); + +protected: + void updateStatisticCount(); //!< Zählt alle Fragen des Kapitels (inkl. Unterkapitel) für die Statistik + void updateRecommendation(); //!< Lernempfehlung für das Kapitel (inkl. Unterkapitel) aktualisieren + void updateRecommendationStatistic(); //!< Statistik über Lernempfehlungen für das Kapitel (inkl. Unterkapitel) aktualisieren + Recommendation recommendationIndividual() const; //!< Empfehlung nur für dieses Kapitel, Unterkapitel und Elternkapitel werden ignoriert + +protected: + CChapter *m_pParentChapter; //!< Übergeordnetes Kapitel + QString m_strId; //!< ID des Kapitels + QString m_strText; //!< Name des Kapitels + QString m_strComment; //!< Kommentar + + QList m_listChapter; //!< Liste mit Unterkapiteln + QList m_listQuestion; //!< Liste mit Fragen + + // Statistik + unsigned m_uLevelCount[LEVEL_MAX+1]; //!< Anzahl der Fragen einer bestimmten Abfragehäufigkeit + unsigned m_uNeverAskedCount; //!< Anzahl der Fragen, die noch nie abgefragt wurden + unsigned m_uRecomCount[RecommendationMax]; //!< Anzahl der Unterkapitel (inkl. selbst) mit bestimmter Lernempfehlung + bool m_bHasLearningNew; //!< Kapitel enthält Fragen, die gerade neu gelernt werden + bool m_bHasKnownQuestions; //!< Kapitel enthält Fragen, die seit mind. gestern bekannt sind + bool m_bHasKnownQuestionsRepeatToday; //!< Kapitel enthält Fragen, die seit mind. gestern bekannt sind und heute wiederholt werden sollten + + Recommendation m_recom; //!< Lernempfehlung + Recommendation m_recom2; //!< Alternative Lernempfehlung + + //! Empfohlenes Datum zur Wiederholung der Fragen + /*! Diese Variable wird mit der Funktion updateRecommendation() aktualisiert */ + QDate m_recomRepeatDate; + //! Liste mit Fragen zum Erfüllen der Lernempfehlung + /*! Diese Liste wird mit der Funktion updateRecommendation() aktualisiert */ + QList m_listQuestionRecommended; +}; + +#endif diff --git a/chaptermodel.cpp b/chaptermodel.cpp index 4924da5..31b1673 100644 --- a/chaptermodel.cpp +++ b/chaptermodel.cpp @@ -1,184 +1,184 @@ -/*************************************************************************** - * 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 "chaptermodel.h" -#include "catalog.h" - -#include - -#define COL_CHAPTER 0 -#define COL_QUESTION 1 -#define COL_ASSIST 2 -#define COL_AVG 3 - -CChapterModel::CChapterModel(QObject *pParent) : QAbstractItemModel (pParent) -{ - m_pCatalog = 0; -} - -CChapterModel::~CChapterModel() -{ -} - -void CChapterModel::onLanguageChanged() -{ - headerDataChanged(Qt::Horizontal, 0, columnCount()-1); -} - -void CChapterModel::clear() -{ - m_listChapter.clear(); - m_pCatalog = 0; - reset(); -} - -void CChapterModel::setModelData (CCatalog *pCatalog, QList listChapter) -{ - m_listChapter = listChapter; - m_pCatalog = pCatalog; - reset(); -} - -int CChapterModel::columnCount (const QModelIndex & parent) const -{ - Q_UNUSED(parent) - return 4; -} - -QVariant CChapterModel::data (const QModelIndex & index, int role) const -{ -CChapter *p = (CChapter*) index.internalPointer(); - - if (m_pCatalog == 0 || !index.isValid()) return QVariant(); - - if (role == Qt::TextAlignmentRole && index.column() == COL_QUESTION) return Qt::AlignRight; - - if (p == 0) return QVariant(); - - if (role == Qt::DisplayRole) - { - if (index.column() == COL_CHAPTER) - { - if (p->id().isEmpty()) - return p->text(); - else - return p->idWithParents() + " - " + p->text(); - } - else if (index.column() == COL_QUESTION) - return QString("%1").arg(p->countSubQuestion()); - else if (index.column() == COL_ASSIST) - { - return p->recommendationText(); -/* CRecommendation r(p); - return r.textShort(); -*/ - } - } - else if (role == Qt::DecorationRole) - { - if (index.column() == COL_CHAPTER) - return QIcon(":/icons/16x16/folder.png"); - else if (index.column() == COL_AVG) - { - return p->levelAvgIcon(); - } - else if (index.column() == COL_ASSIST) - { - return p->recommendationIcon(m_pCatalog); -//TODO return p->recommendation().icon(m_pCatalog); - } - } - else if (role == Qt::ToolTipRole) - { - if (index.column() == COL_AVG) - { - return QString ("%1 - Kennzahl: %2").arg(p->/*statistic().*/levelAvgText()).arg(p->/*statistic().*/levelAvg(), 4, 'f', 2); - } - else if (index.column() == COL_ASSIST) - { - return p->recommendationToolTip(); -//TODO return p->recommendation().recommendationText(m_pCatalog); - } - } - - return QVariant(); -} - -bool CChapterModel::hasChildren (const QModelIndex & parent) const -{ - if (parent.isValid()) return false; - return (!m_listChapter.isEmpty()); -} - -QVariant CChapterModel::headerData (int section, Qt::Orientation orientation, int role) const -{ - if (orientation == Qt::Vertical) return QVariant(); - - if (role == Qt::DisplayRole) - { - if (section == COL_CHAPTER) - return tr("Kapitel"); - else if (section == COL_QUESTION) - return tr("Fragen"); - else if (section == COL_AVG) - return tr("Ø"); - else if (section == COL_ASSIST) - return tr("Lernassistent"); - } - else if (role == Qt::ToolTipRole) - { - if (section == COL_CHAPTER) - return tr("Kapitelname\nBitte markieren Sie das Kapitel, das Sie lernen möchten."); - else if (section == COL_QUESTION) - return tr("Anzahl d. Fragen inkl. Unterkapitel"); - else if (section == COL_AVG) - return tr("Durchschnittlicher Lernfortschritt"); - else if (section == COL_ASSIST) - return tr("Empfehlung des Lernassistentes"); - } - else if (role == Qt::WhatsThisRole) - { - - } - return QVariant(); -} - -QModelIndex CChapterModel::index (int row, int column, const QModelIndex & parent) const -{ -CChapter *p=0; - - if (parent.isValid() || row >= m_listChapter.size()) return QModelIndex(); - p = m_listChapter.at (row); - return createIndex (row, column, (void*)p); -} - -QModelIndex CChapterModel::parent (const QModelIndex & index) const -{ - Q_UNUSED(index); - return QModelIndex(); -} - -int CChapterModel::rowCount (const QModelIndex & parent) const -{ - if (parent.isValid()) return 0; - return m_listChapter.size(); -} - +/*************************************************************************** + * 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 "chaptermodel.h" +#include "catalog.h" + +#include + +#define COL_CHAPTER 0 +#define COL_QUESTION 1 +#define COL_ASSIST 2 +#define COL_AVG 3 + +CChapterModel::CChapterModel(QObject *pParent) : QAbstractItemModel (pParent) +{ + m_pCatalog = 0; +} + +CChapterModel::~CChapterModel() +{ +} + +void CChapterModel::onLanguageChanged() +{ + headerDataChanged(Qt::Horizontal, 0, columnCount()-1); +} + +void CChapterModel::clear() +{ + m_listChapter.clear(); + m_pCatalog = 0; + reset(); +} + +void CChapterModel::setModelData (CCatalog *pCatalog, QList listChapter) +{ + m_listChapter = listChapter; + m_pCatalog = pCatalog; + reset(); +} + +int CChapterModel::columnCount (const QModelIndex & parent) const +{ + Q_UNUSED(parent) + return 4; +} + +QVariant CChapterModel::data (const QModelIndex & index, int role) const +{ +CChapter *p = (CChapter*) index.internalPointer(); + + if (m_pCatalog == 0 || !index.isValid()) return QVariant(); + + if (role == Qt::TextAlignmentRole && index.column() == COL_QUESTION) return Qt::AlignRight; + + if (p == 0) return QVariant(); + + if (role == Qt::DisplayRole) + { + if (index.column() == COL_CHAPTER) + { + if (p->id().isEmpty()) + return p->text(); + else + return p->idWithParents() + " - " + p->text(); + } + else if (index.column() == COL_QUESTION) + return QString("%1").arg(p->countSubQuestion()); + else if (index.column() == COL_ASSIST) + { + return p->recommendationText(); +/* CRecommendation r(p); + return r.textShort(); +*/ + } + } + else if (role == Qt::DecorationRole) + { + if (index.column() == COL_CHAPTER) + return QIcon(":/icons/16x16/folder.png"); + else if (index.column() == COL_AVG) + { + return p->levelAvgIcon(); + } + else if (index.column() == COL_ASSIST) + { + return p->recommendationIcon(m_pCatalog); +//TODO return p->recommendation().icon(m_pCatalog); + } + } + else if (role == Qt::ToolTipRole) + { + if (index.column() == COL_AVG) + { + return QString ("%1 - Kennzahl: %2").arg(p->/*statistic().*/levelAvgText()).arg(p->/*statistic().*/levelAvg(), 4, 'f', 2); + } + else if (index.column() == COL_ASSIST) + { + return p->recommendationToolTip(); +//TODO return p->recommendation().recommendationText(m_pCatalog); + } + } + + return QVariant(); +} + +bool CChapterModel::hasChildren (const QModelIndex & parent) const +{ + if (parent.isValid()) return false; + return (!m_listChapter.isEmpty()); +} + +QVariant CChapterModel::headerData (int section, Qt::Orientation orientation, int role) const +{ + if (orientation == Qt::Vertical) return QVariant(); + + if (role == Qt::DisplayRole) + { + if (section == COL_CHAPTER) + return tr("Kapitel"); + else if (section == COL_QUESTION) + return tr("Fragen"); + else if (section == COL_AVG) + return tr("Ø"); + else if (section == COL_ASSIST) + return tr("Lernassistent"); + } + else if (role == Qt::ToolTipRole) + { + if (section == COL_CHAPTER) + return tr("Kapitelname\nBitte markieren Sie das Kapitel, das Sie lernen möchten."); + else if (section == COL_QUESTION) + return tr("Anzahl d. Fragen inkl. Unterkapitel"); + else if (section == COL_AVG) + return tr("Durchschnittlicher Lernfortschritt"); + else if (section == COL_ASSIST) + return tr("Empfehlung des Lernassistentes"); + } + else if (role == Qt::WhatsThisRole) + { + + } + return QVariant(); +} + +QModelIndex CChapterModel::index (int row, int column, const QModelIndex & parent) const +{ +CChapter *p=0; + + if (parent.isValid() || row >= m_listChapter.size()) return QModelIndex(); + p = m_listChapter.at (row); + return createIndex (row, column, (void*)p); +} + +QModelIndex CChapterModel::parent (const QModelIndex & index) const +{ + Q_UNUSED(index); + return QModelIndex(); +} + +int CChapterModel::rowCount (const QModelIndex & parent) const +{ + if (parent.isValid()) return 0; + return m_listChapter.size(); +} + diff --git a/chaptermodel.h b/chaptermodel.h index e9ef482..4122271 100644 --- a/chaptermodel.h +++ b/chaptermodel.h @@ -1,60 +1,60 @@ -/*************************************************************************** - * 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. * - ***************************************************************************/ - -#ifndef CHAPTERMODEL_H -#define CHAPTERMODEL_H - -#include - -class CChapter; -class CCatalog; - -class CChapterModel : public QAbstractItemModel -{ - Q_OBJECT -public: - CChapterModel(QObject *pParent=0); - ~CChapterModel(); - - void clear(); - void setModelData (CCatalog *pCatalog, QList listChapter); - -public: - virtual int columnCount ( const QModelIndex & parent = QModelIndex() ) const; - virtual QVariant data ( const QModelIndex & index, int role = Qt::DisplayRole ) const; - virtual bool hasChildren (const QModelIndex & parent = QModelIndex()) const; - virtual QVariant headerData ( int section, Qt::Orientation orientation, int role = Qt::DisplayRole ) const; - virtual QModelIndex index (int row, int column, const QModelIndex & parent = QModelIndex()) const; - virtual QModelIndex parent ( const QModelIndex & index ) const; - virtual int rowCount ( const QModelIndex & parent = QModelIndex() ) const; - -public slots: - void onLanguageChanged(); - -protected: - void recalcColumn(); - -protected: - CCatalog *m_pCatalog; - QList m_listChapter; -}; - -#endif // CHAPTERMODEL_H +/*************************************************************************** + * 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. * + ***************************************************************************/ + +#ifndef CHAPTERMODEL_H +#define CHAPTERMODEL_H + +#include + +class CChapter; +class CCatalog; + +class CChapterModel : public QAbstractItemModel +{ + Q_OBJECT +public: + CChapterModel(QObject *pParent=0); + ~CChapterModel(); + + void clear(); + void setModelData (CCatalog *pCatalog, QList listChapter); + +public: + virtual int columnCount ( const QModelIndex & parent = QModelIndex() ) const; + virtual QVariant data ( const QModelIndex & index, int role = Qt::DisplayRole ) const; + virtual bool hasChildren (const QModelIndex & parent = QModelIndex()) const; + virtual QVariant headerData ( int section, Qt::Orientation orientation, int role = Qt::DisplayRole ) const; + virtual QModelIndex index (int row, int column, const QModelIndex & parent = QModelIndex()) const; + virtual QModelIndex parent ( const QModelIndex & index ) const; + virtual int rowCount ( const QModelIndex & parent = QModelIndex() ) const; + +public slots: + void onLanguageChanged(); + +protected: + void recalcColumn(); + +protected: + CCatalog *m_pCatalog; + QList m_listChapter; +}; + +#endif // CHAPTERMODEL_H diff --git a/dlgexam.cpp b/dlgexam.cpp index 74dc34f..9580e8d 100644 --- a/dlgexam.cpp +++ b/dlgexam.cpp @@ -1,300 +1,300 @@ -/*************************************************************************** - * 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 "dlgexam.h" -#include "question.h" -#include "error.h" -#include "catalog.h" - -#include - -CDlgExam::CDlgExam (CCatalog *pCatalog, QWidget *pParent) : QDialog (pParent, Qt::WindowMaximizeButtonHint) -{ - m_pCatalog = pCatalog; - m_iCurrentQuestion = -1; - m_bIsFinished = false; - m_bTimeout = false; - setupUi(this); - connect (&m_timer, SIGNAL(timeout()), this, SLOT(onTimer())); -} - -CDlgExam::~CDlgExam () -{ -} - -bool CDlgExam::setup (const CExam& exam) -{ - m_exam = exam; - labHeader->setText(exam.name()); - - try - { - m_listQuestion = exam.createQuestionPool(m_pCatalog->questionPool()); - } - catch (CError e) - { - QMessageBox::critical(this, tr("Fehler"), e.toHtml()); - return false; - } - - m_listAnswerMask.clear(); - while (m_listAnswerMask.size() < m_listQuestion.size()) - m_listAnswerMask.append(0); - - // Antworten bei Bedarf durchmischen - if (m_pCatalog->useMixedAnswers()) - { - for (int i=0; imixAnswers(); - } - init(); - return true; -} - -void CDlgExam::init() -{ - // Zeitbalken & Zeitüberwachung - m_timer.setInterval(1000); - m_timer.start(); - m_dtStart = QDateTime::currentDateTime(); - pgTime->setMaximum (m_exam.duration() * 60); - - // Sonstiges - pgQuestions->setMaximum (m_listQuestion.size()); - m_bIsFinished = false; - m_bTimeout = false; - labMaxWrong->setText(QString("%1").arg(m_exam.maxErrorPoints())); - - // Navigation - spinBox->setRange (1, m_listQuestion.size()); - spinBox->setValue(1); - m_iCurrentQuestion = 1; - - // Alles updaten - updateNavi(); - showQuestion(); - updateProgressTimer(); - updateProgressQuestion(); -} - -void CDlgExam::onTimer() -{ - updateProgressTimer(); -} - -void CDlgExam::updateNavi() -{ - pbFirst->setEnabled (spinBox->value() > spinBox->minimum()); - pbPrev->setEnabled (spinBox->value() > spinBox->minimum()); - pbNext->setEnabled (spinBox->value() < spinBox->maximum()); - pbLast->setEnabled (spinBox->value() < spinBox->maximum()); -} - -unsigned CDlgExam::answeredQuestionCount() -{ -unsigned u=0; - for (int i=0; isetText(tr("%1 von %2").arg(uAnswered).arg(m_listAnswerMask.size())); - pgQuestions->setValue(uAnswered); -} - -void CDlgExam::updateProgressTimer() -{ -QDateTime dt = QDateTime::currentDateTime(); -unsigned uSecs = m_dtStart.secsTo(dt); - - labProgessTime->setText(tr("Abgelaufene Zeit: %1:%2 min von %3:00 min") - .arg(uSecs / 60) - .arg(uSecs % 60, 2, 10, QChar('0')) - .arg(m_exam.duration())); - - pgTime->setValue (uSecs); - - if (uSecs > m_exam.duration()*60) - { - m_bTimeout = true; - m_timer.stop(); - QMessageBox::information(this, tr("Information"), tr("Die Zeit ist abgelaufen.\nDie Prüfung wird deswegen beendet.")); - on_pbFinish_clicked(); - } -} - -void CDlgExam::on_pbFirst_clicked() -{ - spinBox->setValue(1); -} - -void CDlgExam::on_pbPrev_clicked() -{ -int iNew = spinBox->value() - 1; - if (iNew < 1) iNew = 1; - spinBox->setValue(iNew); -} - -void CDlgExam::on_pbNext_clicked() -{ -int iNew = spinBox->value() + 1; - if (iNew > m_listQuestion.size()) iNew = m_listQuestion.size(); - spinBox->setValue(iNew); -} - -void CDlgExam::on_pbLast_clicked() -{ - spinBox->setValue(m_listQuestion.size()); -} - -void CDlgExam::on_spinBox_valueChanged(int i) -{ - Q_UNUSED(i); - onQuestionChanged(); -} - -void CDlgExam::saveCurrentAnswer() -{ -unsigned uAnswerMask=0; - if (m_iCurrentQuestion >= 0) - { // Antwort zur aktuellen Frage abspeichern - if (rbA->isChecked()) uAnswerMask |= 1; - if (rbB->isChecked()) uAnswerMask |= 2; - if (rbC->isChecked()) uAnswerMask |= 4; - if (rbD->isChecked()) uAnswerMask |= 8; - m_listAnswerMask[m_iCurrentQuestion] = uAnswerMask; - } -} - -void CDlgExam::onQuestionChanged() -{ - saveCurrentAnswer(); - updateNavi(); - updateProgressQuestion(); - showQuestion(); // neue Frage anzeigen -} - -void CDlgExam::showQuestion() -{ -CQuestion *q=0; -unsigned uAnswerMask; -QString str; - - m_iCurrentQuestion = spinBox->value()-1; - q = m_listQuestion.at(m_iCurrentQuestion); - uAnswerMask = m_listAnswerMask.at(m_iCurrentQuestion); - str = q->learnText(m_pCatalog, m_bIsFinished, false); - if (m_bIsFinished) - { - str += "


"; - if (!q->isCorrectAnswer(uAnswerMask)) - str += ""; - else - str += ""; - - str += q->correctionText(uAnswerMask) + "

"; - } - textBrowser->setHtml(str); - - rbA->setChecked(uAnswerMask & 1); - rbB->setChecked(uAnswerMask & 2); - rbC->setChecked(uAnswerMask & 4); - rbD->setChecked(uAnswerMask & 8); - rbNoIdea->setChecked(uAnswerMask == 0); -} - -void CDlgExam::on_pbFinish_clicked() -{ -unsigned uCorrect=0, uWrong=0, uAnsweredCount = answeredQuestionCount(), uErrorPoints=0; -bool bSaveStat=true, bPassed=false; -QDateTime dt = QDateTime::currentDateTime(); -unsigned uSecs = m_dtStart.secsTo(dt); - - saveCurrentAnswer(); - if (uAnsweredCount < (unsigned)m_listQuestion.size() && !m_bTimeout) - { - if (QMessageBox::question (this, - tr("Sicherheitsabfrage"), - tr("Sie haben noch nicht alle Fragen beantwortet.\nWirklich abgeben?"), - QMessageBox::Yes | QMessageBox::No, QMessageBox::No) == QMessageBox::No) return; - } - - m_timer.stop(); - pbFinish->setEnabled(false); - rbA->setEnabled(false); - rbB->setEnabled(false); - rbC->setEnabled(false); - rbD->setEnabled(false); - rbNoIdea->setEnabled(false); - pbCancel->setText(tr("Beenden")); - m_bIsFinished = true; - showQuestion(); - - for (int i=0; iisCorrectAnswer(uAnswerMask)) - uCorrect++; - else - { - uWrong++; - uErrorPoints+=q->errorPoints(); - } - } - labCorrect->setText(QString("%1").arg(uCorrect)); - labWrong->setText(QString("%1").arg(uWrong)); - - if (uAnsweredCount < (unsigned)m_listQuestion.size()/2) - { - bPassed = false; - bSaveStat = false; - labResult->setText(tr("Ohne Wertung\n(Nicht bestanden)")); - QMessageBox::information(this, tr("Information"), tr("Sie haben weniger als die Hälfte aller Fragen beantwortet.\nDiese Prüfung wird deswegen nicht gewertet.")); - } - else if (uErrorPoints > m_exam.maxErrorPoints()) - { - bPassed = false; - labResult->setText(tr("Nicht bestanden")); - QMessageBox::information(this, tr("Ergebnis"), tr("Sie haben die Prüfung leider nicht bestanden.")); - } - else - { - bPassed = true; - labResult->setText(tr("Bestanden")); - QMessageBox::information(this, tr("Ergebnis"), tr("Herzlichen Glückwunsch!

Sie haben die Prüfung bestanden!")); - } - - if (bSaveStat) - { // Prüfungsstatistik speichern - CExamStat es(m_exam); - es.setSecs(uSecs); - es.setQuestions(m_listQuestion, m_listAnswerMask); - es.setResult(uCorrect, uWrong, uErrorPoints, bPassed); - m_pCatalog->appendExamStat(es); - m_pCatalog->saveStatistic(this); - } -} +/*************************************************************************** + * 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 "dlgexam.h" +#include "question.h" +#include "error.h" +#include "catalog.h" + +#include + +CDlgExam::CDlgExam (CCatalog *pCatalog, QWidget *pParent) : QDialog (pParent, Qt::WindowMaximizeButtonHint) +{ + m_pCatalog = pCatalog; + m_iCurrentQuestion = -1; + m_bIsFinished = false; + m_bTimeout = false; + setupUi(this); + connect (&m_timer, SIGNAL(timeout()), this, SLOT(onTimer())); +} + +CDlgExam::~CDlgExam () +{ +} + +bool CDlgExam::setup (const CExam& exam) +{ + m_exam = exam; + labHeader->setText(exam.name()); + + try + { + m_listQuestion = exam.createQuestionPool(m_pCatalog->questionPool()); + } + catch (CError e) + { + QMessageBox::critical(this, tr("Fehler"), e.toHtml()); + return false; + } + + m_listAnswerMask.clear(); + while (m_listAnswerMask.size() < m_listQuestion.size()) + m_listAnswerMask.append(0); + + // Antworten bei Bedarf durchmischen + if (m_pCatalog->useMixedAnswers()) + { + for (int i=0; imixAnswers(); + } + init(); + return true; +} + +void CDlgExam::init() +{ + // Zeitbalken & Zeitüberwachung + m_timer.setInterval(1000); + m_timer.start(); + m_dtStart = QDateTime::currentDateTime(); + pgTime->setMaximum (m_exam.duration() * 60); + + // Sonstiges + pgQuestions->setMaximum (m_listQuestion.size()); + m_bIsFinished = false; + m_bTimeout = false; + labMaxWrong->setText(QString("%1").arg(m_exam.maxErrorPoints())); + + // Navigation + spinBox->setRange (1, m_listQuestion.size()); + spinBox->setValue(1); + m_iCurrentQuestion = 1; + + // Alles updaten + updateNavi(); + showQuestion(); + updateProgressTimer(); + updateProgressQuestion(); +} + +void CDlgExam::onTimer() +{ + updateProgressTimer(); +} + +void CDlgExam::updateNavi() +{ + pbFirst->setEnabled (spinBox->value() > spinBox->minimum()); + pbPrev->setEnabled (spinBox->value() > spinBox->minimum()); + pbNext->setEnabled (spinBox->value() < spinBox->maximum()); + pbLast->setEnabled (spinBox->value() < spinBox->maximum()); +} + +unsigned CDlgExam::answeredQuestionCount() +{ +unsigned u=0; + for (int i=0; isetText(tr("%1 von %2").arg(uAnswered).arg(m_listAnswerMask.size())); + pgQuestions->setValue(uAnswered); +} + +void CDlgExam::updateProgressTimer() +{ +QDateTime dt = QDateTime::currentDateTime(); +unsigned uSecs = m_dtStart.secsTo(dt); + + labProgessTime->setText(tr("Abgelaufene Zeit: %1:%2 min von %3:00 min") + .arg(uSecs / 60) + .arg(uSecs % 60, 2, 10, QChar('0')) + .arg(m_exam.duration())); + + pgTime->setValue (uSecs); + + if (uSecs > m_exam.duration()*60) + { + m_bTimeout = true; + m_timer.stop(); + QMessageBox::information(this, tr("Information"), tr("Die Zeit ist abgelaufen.\nDie Prüfung wird deswegen beendet.")); + on_pbFinish_clicked(); + } +} + +void CDlgExam::on_pbFirst_clicked() +{ + spinBox->setValue(1); +} + +void CDlgExam::on_pbPrev_clicked() +{ +int iNew = spinBox->value() - 1; + if (iNew < 1) iNew = 1; + spinBox->setValue(iNew); +} + +void CDlgExam::on_pbNext_clicked() +{ +int iNew = spinBox->value() + 1; + if (iNew > m_listQuestion.size()) iNew = m_listQuestion.size(); + spinBox->setValue(iNew); +} + +void CDlgExam::on_pbLast_clicked() +{ + spinBox->setValue(m_listQuestion.size()); +} + +void CDlgExam::on_spinBox_valueChanged(int i) +{ + Q_UNUSED(i); + onQuestionChanged(); +} + +void CDlgExam::saveCurrentAnswer() +{ +unsigned uAnswerMask=0; + if (m_iCurrentQuestion >= 0) + { // Antwort zur aktuellen Frage abspeichern + if (rbA->isChecked()) uAnswerMask |= 1; + if (rbB->isChecked()) uAnswerMask |= 2; + if (rbC->isChecked()) uAnswerMask |= 4; + if (rbD->isChecked()) uAnswerMask |= 8; + m_listAnswerMask[m_iCurrentQuestion] = uAnswerMask; + } +} + +void CDlgExam::onQuestionChanged() +{ + saveCurrentAnswer(); + updateNavi(); + updateProgressQuestion(); + showQuestion(); // neue Frage anzeigen +} + +void CDlgExam::showQuestion() +{ +CQuestion *q=0; +unsigned uAnswerMask; +QString str; + + m_iCurrentQuestion = spinBox->value()-1; + q = m_listQuestion.at(m_iCurrentQuestion); + uAnswerMask = m_listAnswerMask.at(m_iCurrentQuestion); + str = q->learnText(m_pCatalog, m_bIsFinished, false); + if (m_bIsFinished) + { + str += "


"; + if (!q->isCorrectAnswer(uAnswerMask)) + str += ""; + else + str += ""; + + str += q->correctionText(uAnswerMask) + "

"; + } + textBrowser->setHtml(str); + + rbA->setChecked(uAnswerMask & 1); + rbB->setChecked(uAnswerMask & 2); + rbC->setChecked(uAnswerMask & 4); + rbD->setChecked(uAnswerMask & 8); + rbNoIdea->setChecked(uAnswerMask == 0); +} + +void CDlgExam::on_pbFinish_clicked() +{ +unsigned uCorrect=0, uWrong=0, uAnsweredCount = answeredQuestionCount(), uErrorPoints=0; +bool bSaveStat=true, bPassed=false; +QDateTime dt = QDateTime::currentDateTime(); +unsigned uSecs = m_dtStart.secsTo(dt); + + saveCurrentAnswer(); + if (uAnsweredCount < (unsigned)m_listQuestion.size() && !m_bTimeout) + { + if (QMessageBox::question (this, + tr("Sicherheitsabfrage"), + tr("Sie haben noch nicht alle Fragen beantwortet.\nWirklich abgeben?"), + QMessageBox::Yes | QMessageBox::No, QMessageBox::No) == QMessageBox::No) return; + } + + m_timer.stop(); + pbFinish->setEnabled(false); + rbA->setEnabled(false); + rbB->setEnabled(false); + rbC->setEnabled(false); + rbD->setEnabled(false); + rbNoIdea->setEnabled(false); + pbCancel->setText(tr("Beenden")); + m_bIsFinished = true; + showQuestion(); + + for (int i=0; iisCorrectAnswer(uAnswerMask)) + uCorrect++; + else + { + uWrong++; + uErrorPoints+=q->errorPoints(); + } + } + labCorrect->setText(QString("%1").arg(uCorrect)); + labWrong->setText(QString("%1").arg(uWrong)); + + if (uAnsweredCount < (unsigned)m_listQuestion.size()/2) + { + bPassed = false; + bSaveStat = false; + labResult->setText(tr("Ohne Wertung\n(Nicht bestanden)")); + QMessageBox::information(this, tr("Information"), tr("Sie haben weniger als die Hälfte aller Fragen beantwortet.\nDiese Prüfung wird deswegen nicht gewertet.")); + } + else if (uErrorPoints > m_exam.maxErrorPoints()) + { + bPassed = false; + labResult->setText(tr("Nicht bestanden")); + QMessageBox::information(this, tr("Ergebnis"), tr("Sie haben die Prüfung leider nicht bestanden.")); + } + else + { + bPassed = true; + labResult->setText(tr("Bestanden")); + QMessageBox::information(this, tr("Ergebnis"), tr("Herzlichen Glückwunsch!

Sie haben die Prüfung bestanden!")); + } + + if (bSaveStat) + { // Prüfungsstatistik speichern + CExamStat es(m_exam); + es.setSecs(uSecs); + es.setQuestions(m_listQuestion, m_listAnswerMask); + es.setResult(uCorrect, uWrong, uErrorPoints, bPassed); + m_pCatalog->appendExamStat(es); + m_pCatalog->saveStatistic(this); + } +} diff --git a/dlgexam.h b/dlgexam.h index 159783a..f3a70b3 100644 --- a/dlgexam.h +++ b/dlgexam.h @@ -1,81 +1,81 @@ -/*************************************************************************** - * 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. * - ***************************************************************************/ - -#pragma once - -#include "exam.h" - -#include "ui_dlgexam.h" -#include -#include -#include - -class CCatalog; -class CQuestion; - -class CDlgExam : public QDialog, Ui::DlgExam -{ -Q_OBJECT -public: - CDlgExam (CCatalog *pCatalog, QWidget *pParent=0); - ~CDlgExam (); - - bool setup (const CExam& exam); - -protected: - void init(); - void updateNavi(); - void updateProgressQuestion(); - void updateProgressTimer(); - void showQuestion(); - void onQuestionChanged(); - unsigned answeredQuestionCount(); - void saveCurrentAnswer(); - -protected slots: - void onTimer(); - void on_pbFirst_clicked(); - void on_pbPrev_clicked(); - void on_pbNext_clicked(); - void on_pbLast_clicked(); - void on_spinBox_valueChanged(int i); - void on_pbFinish_clicked(); - - inline void on_rbA_toggled(bool bChecked) { Q_UNUSED(bChecked); saveCurrentAnswer(); updateProgressQuestion(); } - inline void on_rbB_toggled(bool bChecked) { Q_UNUSED(bChecked); saveCurrentAnswer(); updateProgressQuestion(); } - inline void on_rbC_toggled(bool bChecked) { Q_UNUSED(bChecked); saveCurrentAnswer(); updateProgressQuestion(); } - inline void on_rbD_toggled(bool bChecked) { Q_UNUSED(bChecked); saveCurrentAnswer(); updateProgressQuestion(); } - inline void on_rbNoIdea_toggled(bool bChecked) { Q_UNUSED(bChecked); saveCurrentAnswer(); updateProgressQuestion(); } - -protected: - CCatalog *m_pCatalog; - CExam m_exam; - - QList m_listQuestion; - QList m_listAnswerMask; - QTimer m_timer; - QDateTime m_dtStart; - QDateTime m_dtStop; - int m_iCurrentQuestion; - bool m_bIsFinished; - bool m_bTimeout; -}; - +/*************************************************************************** + * 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. * + ***************************************************************************/ + +#pragma once + +#include "exam.h" + +#include "ui_dlgexam.h" +#include +#include +#include + +class CCatalog; +class CQuestion; + +class CDlgExam : public QDialog, Ui::DlgExam +{ +Q_OBJECT +public: + CDlgExam (CCatalog *pCatalog, QWidget *pParent=0); + ~CDlgExam (); + + bool setup (const CExam& exam); + +protected: + void init(); + void updateNavi(); + void updateProgressQuestion(); + void updateProgressTimer(); + void showQuestion(); + void onQuestionChanged(); + unsigned answeredQuestionCount(); + void saveCurrentAnswer(); + +protected slots: + void onTimer(); + void on_pbFirst_clicked(); + void on_pbPrev_clicked(); + void on_pbNext_clicked(); + void on_pbLast_clicked(); + void on_spinBox_valueChanged(int i); + void on_pbFinish_clicked(); + + inline void on_rbA_toggled(bool bChecked) { Q_UNUSED(bChecked); saveCurrentAnswer(); updateProgressQuestion(); } + inline void on_rbB_toggled(bool bChecked) { Q_UNUSED(bChecked); saveCurrentAnswer(); updateProgressQuestion(); } + inline void on_rbC_toggled(bool bChecked) { Q_UNUSED(bChecked); saveCurrentAnswer(); updateProgressQuestion(); } + inline void on_rbD_toggled(bool bChecked) { Q_UNUSED(bChecked); saveCurrentAnswer(); updateProgressQuestion(); } + inline void on_rbNoIdea_toggled(bool bChecked) { Q_UNUSED(bChecked); saveCurrentAnswer(); updateProgressQuestion(); } + +protected: + CCatalog *m_pCatalog; + CExam m_exam; + + QList m_listQuestion; + QList m_listAnswerMask; + QTimer m_timer; + QDateTime m_dtStart; + QDateTime m_dtStop; + int m_iCurrentQuestion; + bool m_bIsFinished; + bool m_bTimeout; +}; + diff --git a/dlgexamselect.cpp b/dlgexamselect.cpp index 75c14eb..569c663 100644 --- a/dlgexamselect.cpp +++ b/dlgexamselect.cpp @@ -1,93 +1,93 @@ -/*************************************************************************** - * 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 "dlgexamselect.h" - -#include -#include -#include "catalog.h" - -CDlgExamSelect::CDlgExamSelect (QWidget *pParent) : QDialog (pParent) -{ - m_pCatalog = 0; - m_iSelectedExam = -1; - setupUi(this); - - twList->headerItem()->setText(0, tr("Name")); - twList->headerItem()->setText(1, tr("Dauer (min)")); - //twList->header()->setStretchLastSection(false); - twList->header()->resizeSection(0, 370); - //twList->header()->resizeSection(1, 70); - -} - -CDlgExamSelect::~CDlgExamSelect () -{ -} - -void CDlgExamSelect::setup (const CCatalog *pCatalog) -{ - m_pCatalog = pCatalog; - for (int i=0; icountExam(); i++) - { - CExam exam = m_pCatalog->examAt(i); - QTreeWidgetItem *pItem = new QTreeWidgetItem (twList); - pItem->setText(0, exam.name()); - pItem->setText(1, QString("%1 min").arg(exam.duration())); - pItem->setTextAlignment(1, Qt::AlignRight); - pItem->setData(0, Qt::UserRole, i); - } -} - -void CDlgExamSelect::on_twList_currentItemChanged (QTreeWidgetItem *current, QTreeWidgetItem *previous) -{ - Q_UNUSED(previous); - if (current) - { - m_iSelectedExam = current->data(0, Qt::UserRole).toInt(); - CExam exam = m_pCatalog->examAt(m_iSelectedExam); - labName->setText(exam.name()); - labComment->setText(exam.comment()); - labDuration->setText(QString("%1 min").arg(exam.duration())); - labQuestions->setText(QString("%1").arg(exam.questionCount())); - labError->setText(QString("%1").arg(exam.maxErrorPoints())); - } - else - { - m_iSelectedExam = -1; - labName->clear(); - labComment->clear(); - labDuration->clear(); - labQuestions->clear(); - labError->clear(); - } -} - -void CDlgExamSelect::on_buttonBox_accepted() -{ - if (m_iSelectedExam == -1) - { - QMessageBox::information(this, tr("Information"), tr("Bitte eine Prüfung auswählen!")); - return; - } - accept(); -} - +/*************************************************************************** + * 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 "dlgexamselect.h" + +#include +#include +#include "catalog.h" + +CDlgExamSelect::CDlgExamSelect (QWidget *pParent) : QDialog (pParent) +{ + m_pCatalog = 0; + m_iSelectedExam = -1; + setupUi(this); + + twList->headerItem()->setText(0, tr("Name")); + twList->headerItem()->setText(1, tr("Dauer (min)")); + //twList->header()->setStretchLastSection(false); + twList->header()->resizeSection(0, 370); + //twList->header()->resizeSection(1, 70); + +} + +CDlgExamSelect::~CDlgExamSelect () +{ +} + +void CDlgExamSelect::setup (const CCatalog *pCatalog) +{ + m_pCatalog = pCatalog; + for (int i=0; icountExam(); i++) + { + CExam exam = m_pCatalog->examAt(i); + QTreeWidgetItem *pItem = new QTreeWidgetItem (twList); + pItem->setText(0, exam.name()); + pItem->setText(1, QString("%1 min").arg(exam.duration())); + pItem->setTextAlignment(1, Qt::AlignRight); + pItem->setData(0, Qt::UserRole, i); + } +} + +void CDlgExamSelect::on_twList_currentItemChanged (QTreeWidgetItem *current, QTreeWidgetItem *previous) +{ + Q_UNUSED(previous); + if (current) + { + m_iSelectedExam = current->data(0, Qt::UserRole).toInt(); + CExam exam = m_pCatalog->examAt(m_iSelectedExam); + labName->setText(exam.name()); + labComment->setText(exam.comment()); + labDuration->setText(QString("%1 min").arg(exam.duration())); + labQuestions->setText(QString("%1").arg(exam.questionCount())); + labError->setText(QString("%1").arg(exam.maxErrorPoints())); + } + else + { + m_iSelectedExam = -1; + labName->clear(); + labComment->clear(); + labDuration->clear(); + labQuestions->clear(); + labError->clear(); + } +} + +void CDlgExamSelect::on_buttonBox_accepted() +{ + if (m_iSelectedExam == -1) + { + QMessageBox::information(this, tr("Information"), tr("Bitte eine Prüfung auswählen!")); + return; + } + accept(); +} + diff --git a/dlgexamselect.h b/dlgexamselect.h index 5189cc8..fa2b711 100644 --- a/dlgexamselect.h +++ b/dlgexamselect.h @@ -1,46 +1,46 @@ -/*************************************************************************** - * 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. * - ***************************************************************************/ - -#pragma once - -#include "ui_dlgexamselect.h" -#include - -class CCatalog; - -class CDlgExamSelect : public QDialog, Ui::DlgExamSelect -{ -Q_OBJECT -public: - CDlgExamSelect (QWidget *pParent=0); - ~CDlgExamSelect (); - - void setup (const CCatalog *pCatalog); - inline int selectedExam() const { return m_iSelectedExam; } - -protected slots: - void on_buttonBox_accepted(); - void on_twList_currentItemChanged (QTreeWidgetItem *current, QTreeWidgetItem *previous); - -protected: - const CCatalog *m_pCatalog; - int m_iSelectedExam; -}; +/*************************************************************************** + * 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. * + ***************************************************************************/ + +#pragma once + +#include "ui_dlgexamselect.h" +#include + +class CCatalog; + +class CDlgExamSelect : public QDialog, Ui::DlgExamSelect +{ +Q_OBJECT +public: + CDlgExamSelect (QWidget *pParent=0); + ~CDlgExamSelect (); + + void setup (const CCatalog *pCatalog); + inline int selectedExam() const { return m_iSelectedExam; } + +protected slots: + void on_buttonBox_accepted(); + void on_twList_currentItemChanged (QTreeWidgetItem *current, QTreeWidgetItem *previous); + +protected: + const CCatalog *m_pCatalog; + int m_iSelectedExam; +}; diff --git a/dlgexamstatistic.cpp b/dlgexamstatistic.cpp index bc8c2f6..1a78e20 100644 --- a/dlgexamstatistic.cpp +++ b/dlgexamstatistic.cpp @@ -1,109 +1,109 @@ -/*************************************************************************** - * 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 "dlgexamstatistic.h" -#include "catalog.h" - -#include -#include - -CDlgExamStatistic::CDlgExamStatistic(QWidget *pParent) : QDialog(pParent) -{ - m_pCatalog = 0; - setupUi(this); - - twExamStat->headerItem()->setText(0, "Datum/Uhrzeit"); - twExamStat->headerItem()->setText(1, "Ergebnis"); - twExamStat->headerItem()->setText(2, "Fehler"); - twExamStat->headerItem()->setText(3, "Dauer"); - twExamStat->headerItem()->setText(4, "Ø pro Frage"); - - twExamStat->header()->resizeSection(0, 130); - twExamStat->header()->resizeSection(2, 50); - twExamStat->header()->resizeSection(3, 75); -} - -void CDlgExamStatistic::go (CCatalog *pCatalog) -{ - m_pCatalog = pCatalog; - - if (pCatalog->countExamStat() == 0) - { - QMessageBox::information(parentWidget(), tr("Information"), tr("Es wurden bisher noch keine Prüfungen durchgeführt.")); - return; - } - - cbExam->clear(); - for (int i=0; icountExam(); i++) - { - cbExam->addItem(pCatalog->examAt(i).name(), pCatalog->examAt(i).id()); - } - - exec(); -} - -void CDlgExamStatistic::on_cbExam_currentIndexChanged (int index) -{ -QString strExamId = cbExam->itemData(index).toString(); -CExam exam = m_pCatalog->examById(strExamId); -unsigned uExamCount=0, uPassedCount=0, uFailedCount=0, uErrorCount=0, uDuration=0, uQuestions=0; -QTreeWidgetItem *pItem=0; - - twExamStat->clear(); - - labExamQCount->setText(QString::number(exam.questionCount())); - labExamTime->setText(QString("%1 min").arg(exam.duration())); - labExamWrong->setText(QString::number(exam.maxErrorPoints())); - - for (int i=0; icountExamStat(); i++) - { - CExamStat es = m_pCatalog->examStatAt(i); - if (es.id() != strExamId) continue; - uExamCount++; - if (es.passed()) uPassedCount++; else uFailedCount++; - uErrorCount += es.errorPoints(); - uDuration += es.duration(); - uQuestions += es.correctAnswers() + es.wrongAnswers(); - pItem = new QTreeWidgetItem (twExamStat); - pItem->setText(0, es.datetime().toString(Qt::LocalDate)); - pItem->setText(1, es.passed() ? tr("Bestanden") : tr("Nicht bestanden")); - pItem->setText(2, QString::number(es.errorPoints())); - pItem->setTextAlignment(2, Qt::AlignRight); - pItem->setText(3, QString("%1 m %2 s").arg(es.duration() / 60).arg(es.duration() % 60, 2, 10, QChar('0'))); - pItem->setTextAlignment(3, Qt::AlignRight); - unsigned uQuestionCount = es.correctAnswers() + es.wrongAnswers(); - if (uQuestionCount != 0) - pItem->setText(4, QString("%1 m %2 s").arg(es.duration() / uQuestionCount / 60).arg(es.duration() / uQuestionCount % 60, 2, 10, QChar('0'))); - else - pItem->setText(4, "--"); - pItem->setTextAlignment(4, Qt::AlignRight); - } - - labExamCount->setText(QString::number(uExamCount)); - labExamPassed->setText(uExamCount != 0 ? QString::number(uPassedCount) : "--"); - labExamFailed->setText(uExamCount != 0 ? QString::number(uFailedCount) : "--"); - labExamPassedP->setText(uExamCount != 0 ? QString("%1 %").arg((double)uPassedCount / uExamCount * 100.0, 0, 'f', 1) : "--"); - labExamFailedP->setText(uExamCount != 0 ? QString("%1 %").arg((double)uFailedCount / uExamCount * 100.0, 0, 'f', 1) : "--"); - labAvgWrong->setText(uExamCount != 0 ? QString::number(uErrorCount / uExamCount) : "--"); - labTimeExam->setText(uExamCount != 0 ? QString("%1 m %2 s").arg(uDuration / uExamCount / 60).arg(uDuration / uExamCount % 60, 2, 10, QChar('0')) : "--"); - labTimeQuestion->setText(uQuestions != 0 ? QString("%1 m %2 s").arg(uDuration / uQuestions / 60).arg(uDuration / uQuestions % 60, 2, 10, QChar('0')) : "--"); -} - +/*************************************************************************** + * 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 "dlgexamstatistic.h" +#include "catalog.h" + +#include +#include + +CDlgExamStatistic::CDlgExamStatistic(QWidget *pParent) : QDialog(pParent) +{ + m_pCatalog = 0; + setupUi(this); + + twExamStat->headerItem()->setText(0, "Datum/Uhrzeit"); + twExamStat->headerItem()->setText(1, "Ergebnis"); + twExamStat->headerItem()->setText(2, "Fehler"); + twExamStat->headerItem()->setText(3, "Dauer"); + twExamStat->headerItem()->setText(4, "Ø pro Frage"); + + twExamStat->header()->resizeSection(0, 130); + twExamStat->header()->resizeSection(2, 50); + twExamStat->header()->resizeSection(3, 75); +} + +void CDlgExamStatistic::go (CCatalog *pCatalog) +{ + m_pCatalog = pCatalog; + + if (pCatalog->countExamStat() == 0) + { + QMessageBox::information(parentWidget(), tr("Information"), tr("Es wurden bisher noch keine Prüfungen durchgeführt.")); + return; + } + + cbExam->clear(); + for (int i=0; icountExam(); i++) + { + cbExam->addItem(pCatalog->examAt(i).name(), pCatalog->examAt(i).id()); + } + + exec(); +} + +void CDlgExamStatistic::on_cbExam_currentIndexChanged (int index) +{ +QString strExamId = cbExam->itemData(index).toString(); +CExam exam = m_pCatalog->examById(strExamId); +unsigned uExamCount=0, uPassedCount=0, uFailedCount=0, uErrorCount=0, uDuration=0, uQuestions=0; +QTreeWidgetItem *pItem=0; + + twExamStat->clear(); + + labExamQCount->setText(QString::number(exam.questionCount())); + labExamTime->setText(QString("%1 min").arg(exam.duration())); + labExamWrong->setText(QString::number(exam.maxErrorPoints())); + + for (int i=0; icountExamStat(); i++) + { + CExamStat es = m_pCatalog->examStatAt(i); + if (es.id() != strExamId) continue; + uExamCount++; + if (es.passed()) uPassedCount++; else uFailedCount++; + uErrorCount += es.errorPoints(); + uDuration += es.duration(); + uQuestions += es.correctAnswers() + es.wrongAnswers(); + pItem = new QTreeWidgetItem (twExamStat); + pItem->setText(0, es.datetime().toString(Qt::LocalDate)); + pItem->setText(1, es.passed() ? tr("Bestanden") : tr("Nicht bestanden")); + pItem->setText(2, QString::number(es.errorPoints())); + pItem->setTextAlignment(2, Qt::AlignRight); + pItem->setText(3, QString("%1 m %2 s").arg(es.duration() / 60).arg(es.duration() % 60, 2, 10, QChar('0'))); + pItem->setTextAlignment(3, Qt::AlignRight); + unsigned uQuestionCount = es.correctAnswers() + es.wrongAnswers(); + if (uQuestionCount != 0) + pItem->setText(4, QString("%1 m %2 s").arg(es.duration() / uQuestionCount / 60).arg(es.duration() / uQuestionCount % 60, 2, 10, QChar('0'))); + else + pItem->setText(4, "--"); + pItem->setTextAlignment(4, Qt::AlignRight); + } + + labExamCount->setText(QString::number(uExamCount)); + labExamPassed->setText(uExamCount != 0 ? QString::number(uPassedCount) : "--"); + labExamFailed->setText(uExamCount != 0 ? QString::number(uFailedCount) : "--"); + labExamPassedP->setText(uExamCount != 0 ? QString("%1 %").arg((double)uPassedCount / uExamCount * 100.0, 0, 'f', 1) : "--"); + labExamFailedP->setText(uExamCount != 0 ? QString("%1 %").arg((double)uFailedCount / uExamCount * 100.0, 0, 'f', 1) : "--"); + labAvgWrong->setText(uExamCount != 0 ? QString::number(uErrorCount / uExamCount) : "--"); + labTimeExam->setText(uExamCount != 0 ? QString("%1 m %2 s").arg(uDuration / uExamCount / 60).arg(uDuration / uExamCount % 60, 2, 10, QChar('0')) : "--"); + labTimeQuestion->setText(uQuestions != 0 ? QString("%1 m %2 s").arg(uDuration / uQuestions / 60).arg(uDuration / uQuestions % 60, 2, 10, QChar('0')) : "--"); +} + diff --git a/dlgexamstatistic.h b/dlgexamstatistic.h index a647dfc..a8337e9 100644 --- a/dlgexamstatistic.h +++ b/dlgexamstatistic.h @@ -1,43 +1,43 @@ -/*************************************************************************** - * 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. * - ***************************************************************************/ - -#pragma once - -#include -#include "ui_dlgexamstatistic.h" - -class CCatalog; - -class CDlgExamStatistic : public QDialog, Ui::DlgExamStatistic -{ -Q_OBJECT -public: - CDlgExamStatistic(QWidget *pParent=0); - ~CDlgExamStatistic() {} - - void go (CCatalog *pCatalog); - -protected slots: - void on_cbExam_currentIndexChanged (int index); - -protected: - CCatalog *m_pCatalog; -}; +/*************************************************************************** + * 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. * + ***************************************************************************/ + +#pragma once + +#include +#include "ui_dlgexamstatistic.h" + +class CCatalog; + +class CDlgExamStatistic : public QDialog, Ui::DlgExamStatistic +{ +Q_OBJECT +public: + CDlgExamStatistic(QWidget *pParent=0); + ~CDlgExamStatistic() {} + + void go (CCatalog *pCatalog); + +protected slots: + void on_cbExam_currentIndexChanged (int index); + +protected: + CCatalog *m_pCatalog; +}; diff --git a/dlginformation.cpp b/dlginformation.cpp index c89bfbf..d4a295e 100644 --- a/dlginformation.cpp +++ b/dlginformation.cpp @@ -1,51 +1,51 @@ -/*************************************************************************** - * 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 "dlginformation.h" - -#include - -#include "catalog.h" - -CDlgInformation::CDlgInformation (QWidget *pParent) : QDialog(pParent) -{ - m_pCatalog=0; - setupUi(this); - -} - - -bool CDlgInformation::setup(CCatalog *pCatalog) -{ - labName->setText(pCatalog->name()); - labVersion->setText(pCatalog->versionText()); - labPublished->setText(pCatalog->published().toString(Qt::LocalDate)); - labStatQuestion->setText(QString("%1").arg(pCatalog->countSubQuestion())); - labStatChapter->setText(QString("%1").arg(pCatalog->subChapters().size())); - labValidFrom->setText(pCatalog->validFrom().toString(Qt::LocalDate)); - labValidUntil->setText(pCatalog->validUntil().toString(Qt::LocalDate)); - labDate->setText(pCatalog->created().toString(Qt::LocalDate)); - labComment->setText(pCatalog->comment()); - labPublisher->setText(pCatalog->publisher()); - labContact->setText(pCatalog->contact()); - return true; -} - +/*************************************************************************** + * 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 "dlginformation.h" + +#include + +#include "catalog.h" + +CDlgInformation::CDlgInformation (QWidget *pParent) : QDialog(pParent) +{ + m_pCatalog=0; + setupUi(this); + +} + + +bool CDlgInformation::setup(CCatalog *pCatalog) +{ + labName->setText(pCatalog->name()); + labVersion->setText(pCatalog->versionText()); + labPublished->setText(pCatalog->published().toString(Qt::LocalDate)); + labStatQuestion->setText(QString("%1").arg(pCatalog->countSubQuestion())); + labStatChapter->setText(QString("%1").arg(pCatalog->subChapters().size())); + labValidFrom->setText(pCatalog->validFrom().toString(Qt::LocalDate)); + labValidUntil->setText(pCatalog->validUntil().toString(Qt::LocalDate)); + labDate->setText(pCatalog->created().toString(Qt::LocalDate)); + labComment->setText(pCatalog->comment()); + labPublisher->setText(pCatalog->publisher()); + labContact->setText(pCatalog->contact()); + return true; +} + diff --git a/dlginformation.h b/dlginformation.h index 3b07688..6cc7f43 100644 --- a/dlginformation.h +++ b/dlginformation.h @@ -1,47 +1,47 @@ -/*************************************************************************** - * 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. * - ***************************************************************************/ - -#ifndef DLGINFORMATION_H -#define DLGINFORMATION_H - -#include -#include "ui_dlginformation.h" - -class CCatalog; - -class CDlgInformation : public QDialog, Ui::DlgInformation -{ - Q_OBJECT -public: - CDlgInformation (QWidget *pParent=0); - ~CDlgInformation () {} - - bool setup(CCatalog *pCatalog); - -protected slots: - - -protected: - CCatalog *m_pCatalog; - -}; - -#endif +/*************************************************************************** + * 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. * + ***************************************************************************/ + +#ifndef DLGINFORMATION_H +#define DLGINFORMATION_H + +#include +#include "ui_dlginformation.h" + +class CCatalog; + +class CDlgInformation : public QDialog, Ui::DlgInformation +{ + Q_OBJECT +public: + CDlgInformation (QWidget *pParent=0); + ~CDlgInformation () {} + + bool setup(CCatalog *pCatalog); + +protected slots: + + +protected: + CCatalog *m_pCatalog; + +}; + +#endif diff --git a/dlglearn.cpp b/dlglearn.cpp index b9dd52c..64ecbdf 100644 --- a/dlglearn.cpp +++ b/dlglearn.cpp @@ -1,435 +1,435 @@ -/*************************************************************************** - * 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 - -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 = "" + strRecom1 + ""; - else - strRecom1 = "" + strRecom1 + ""; - 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("" + strRecom2 + ""); - } - else - { - str = strRecom1; - if (!strRecom2.isEmpty()) - str += "

" + strRecom2 + ""; - 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 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;ilevel(); - 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; ilevel() == 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 = "

Schummel-Modus
Richtige Antwort: " + CAnswerClicked::answerText (m_pQuestion->correctAnswerMask()) + "
"; - strCheat += "
Empfohlene Wiederholung: " + m_pQuestion->repeatDateText(); - strCheat += QString("
isLearningNew(): %1").arg(m_pQuestion->isLearningNew()); - - strCheat += "

"; - } - 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<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("Herzlichen Glückwunsch!

Sie haben das heutige Lernziel für dieses Kapitel erreicht.
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())); -} +/*************************************************************************** + * 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 + +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 = "" + strRecom1 + ""; + else + strRecom1 = "" + strRecom1 + ""; + 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("" + strRecom2 + ""); + } + else + { + str = strRecom1; + if (!strRecom2.isEmpty()) + str += "

" + strRecom2 + ""; + 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 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;ilevel(); + 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; ilevel() == 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 = "

Schummel-Modus
Richtige Antwort: " + CAnswerClicked::answerText (m_pQuestion->correctAnswerMask()) + "
"; + strCheat += "
Empfohlene Wiederholung: " + m_pQuestion->repeatDateText(); + strCheat += QString("
isLearningNew(): %1").arg(m_pQuestion->isLearningNew()); + + strCheat += "

"; + } + 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<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("Herzlichen Glückwunsch!

Sie haben das heutige Lernziel für dieses Kapitel erreicht.
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())); +} diff --git a/dlglearn.h b/dlglearn.h index 906037b..535fe19 100644 --- a/dlglearn.h +++ b/dlglearn.h @@ -1,84 +1,84 @@ -/*************************************************************************** - * 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. * - ***************************************************************************/ - -#ifndef DLGLEARN_H -#define DLGLEARN_H - -#include -#include -#include -#include "question.h" - -#include "ui_dlglearn.h" - -class CChapter; -class CCatalog; - -class CDlgLearn : public QDialog, Ui::DlgLearn -{ - Q_OBJECT -public: - CDlgLearn (QWidget *pParent=0); - ~CDlgLearn(); - - void go (CCatalog *pCatalog, CChapter *pChapter); - -protected: - void setNewChapter(CChapter *pChapter); - void updateStatistic(); - void updateLearnAssistant(); - void nextQuestion(); - CQuestion *findNextQuestion(); - CQuestion *findNextPoolQuestion(); - CQuestion *findNextTargetQuestion(); - - void handleAnswer(const int i); - -protected slots: - void on_pbAnswerA_clicked(); - void on_pbAnswerB_clicked(); - void on_pbAnswerC_clicked(); - void on_pbAnswerD_clicked(); - void on_pbShowHelper_clicked(); - void on_pbSkip_clicked(); - void on_pbLastQuestion_clicked(); - void on_pbQuit_clicked(); - void on_pbLearnAssistant_clicked(); - void onUpdateDS(); - -protected: - CCatalog *m_pCatalog; - CChapter *m_pChapter; - QList m_listQuestion; - CQuestion *m_pQuestion; - CQuestion *m_pLastQuestion; - unsigned m_uLastAnswerMask; - bool m_bHintsUsed; - QTime m_timeElapsed; - unsigned m_uElapsedBeforeBreak; // if the user makes a break - bool m_bMsgBoxTargetReachedShowed; - CDayStatistic m_ds; - - bool m_bAssistantEnable; - bool m_bCheatEnable; -}; - -#endif +/*************************************************************************** + * 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. * + ***************************************************************************/ + +#ifndef DLGLEARN_H +#define DLGLEARN_H + +#include +#include +#include +#include "question.h" + +#include "ui_dlglearn.h" + +class CChapter; +class CCatalog; + +class CDlgLearn : public QDialog, Ui::DlgLearn +{ + Q_OBJECT +public: + CDlgLearn (QWidget *pParent=0); + ~CDlgLearn(); + + void go (CCatalog *pCatalog, CChapter *pChapter); + +protected: + void setNewChapter(CChapter *pChapter); + void updateStatistic(); + void updateLearnAssistant(); + void nextQuestion(); + CQuestion *findNextQuestion(); + CQuestion *findNextPoolQuestion(); + CQuestion *findNextTargetQuestion(); + + void handleAnswer(const int i); + +protected slots: + void on_pbAnswerA_clicked(); + void on_pbAnswerB_clicked(); + void on_pbAnswerC_clicked(); + void on_pbAnswerD_clicked(); + void on_pbShowHelper_clicked(); + void on_pbSkip_clicked(); + void on_pbLastQuestion_clicked(); + void on_pbQuit_clicked(); + void on_pbLearnAssistant_clicked(); + void onUpdateDS(); + +protected: + CCatalog *m_pCatalog; + CChapter *m_pChapter; + QList m_listQuestion; + CQuestion *m_pQuestion; + CQuestion *m_pLastQuestion; + unsigned m_uLastAnswerMask; + bool m_bHintsUsed; + QTime m_timeElapsed; + unsigned m_uElapsedBeforeBreak; // if the user makes a break + bool m_bMsgBoxTargetReachedShowed; + CDayStatistic m_ds; + + bool m_bAssistantEnable; + bool m_bCheatEnable; +}; + +#endif diff --git a/dlglearnassistant.cpp b/dlglearnassistant.cpp index 64ea2b3..cdca32b 100644 --- a/dlglearnassistant.cpp +++ b/dlglearnassistant.cpp @@ -1,117 +1,117 @@ -/*************************************************************************** - * 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 "dlglearnassistant.h" - -#include -#include - -#include "catalog.h" - -CDlgLearnAssistant::CDlgLearnAssistant (QWidget *pParent) : QDialog(pParent) -{ - m_pCatalog=0; - setupUi(this); - - tvChapters->setModel (&m_modelChapter); - tvChapters->header()->setStretchLastSection(false); - tvChapters->header()->setResizeMode (0, QHeaderView::Stretch); - tvChapters->header()->setResizeMode (1, QHeaderView::Interactive); - tvChapters->header()->setResizeMode (3, QHeaderView::Interactive); - tvChapters->header()->resizeSection(1, 50); - tvChapters->header()->resizeSection(2, 140); - tvChapters->header()->resizeSection(3, 20); -} - - -bool CDlgLearnAssistant::setup(CCatalog *pCatalog) -{ -bool bEnableRepeat; -QList listAll, listNow, listToday, listSelected; -CChapter *c; - - m_pCatalog = pCatalog; - if (pCatalog->recommendationCount (CChapter::RecommendationRepeatToday) > 0) - { - bEnableRepeat = true; - labRepeat->setText(tr("Es sind %1 Fragen vorhanden").arg(pCatalog->recommendedQuestions().count())); - rbRepeat->setChecked(true); - } - else - { - bEnableRepeat = false; - labRepeat->setText(tr("Keine Fragen vorhanden.")); - rbList->setChecked(true); - } - rbRepeat->setEnabled(bEnableRepeat); - labRepeat->setEnabled(bEnableRepeat); - - // update list - listAll = pCatalog->chapters(); - for (int i=0; iisRecommendedNow(pCatalog)) - listNow << c; - else if (c->recommendation() == CChapter::RecommendationLearnNew) - listToday << c; - } - - listSelected << listNow << listToday; - m_modelChapter.setModelData (pCatalog, listSelected); - - if (!bEnableRepeat && listSelected.isEmpty()) return false; - - updateEnable(); - - return true; -} - -void CDlgLearnAssistant::updateEnable() -{ - labRepeat->setEnabled(rbRepeat->isChecked()); - tvChapters->setEnabled(rbList->isChecked()); -} - - -void CDlgLearnAssistant::on_buttonBox_accepted() -{ -QModelIndexList list = tvChapters->selectionModel()->selectedIndexes(); - if (rbList->isChecked() && list.isEmpty()) - { - QMessageBox::information(this, tr("Information"), tr("Bitte markieren Sie in der Liste ein Kapitel, das Sie lernen möchten!")); - return; - } - accept(); -} - -CChapter* CDlgLearnAssistant::selectedChapter() -{ -QModelIndexList list = tvChapters->selectionModel()->selectedIndexes(); - if (rbRepeat->isChecked()) - return m_pCatalog; - if (rbList->isChecked()) - { - if (list.isEmpty()) return 0; - return (CChapter*)list.first().internalPointer(); - } - return 0; -} +/*************************************************************************** + * 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 "dlglearnassistant.h" + +#include +#include + +#include "catalog.h" + +CDlgLearnAssistant::CDlgLearnAssistant (QWidget *pParent) : QDialog(pParent) +{ + m_pCatalog=0; + setupUi(this); + + tvChapters->setModel (&m_modelChapter); + tvChapters->header()->setStretchLastSection(false); + tvChapters->header()->setResizeMode (0, QHeaderView::Stretch); + tvChapters->header()->setResizeMode (1, QHeaderView::Interactive); + tvChapters->header()->setResizeMode (3, QHeaderView::Interactive); + tvChapters->header()->resizeSection(1, 50); + tvChapters->header()->resizeSection(2, 140); + tvChapters->header()->resizeSection(3, 20); +} + + +bool CDlgLearnAssistant::setup(CCatalog *pCatalog) +{ +bool bEnableRepeat; +QList listAll, listNow, listToday, listSelected; +CChapter *c; + + m_pCatalog = pCatalog; + if (pCatalog->recommendationCount (CChapter::RecommendationRepeatToday) > 0) + { + bEnableRepeat = true; + labRepeat->setText(tr("Es sind %1 Fragen vorhanden").arg(pCatalog->recommendedQuestions().count())); + rbRepeat->setChecked(true); + } + else + { + bEnableRepeat = false; + labRepeat->setText(tr("Keine Fragen vorhanden.")); + rbList->setChecked(true); + } + rbRepeat->setEnabled(bEnableRepeat); + labRepeat->setEnabled(bEnableRepeat); + + // update list + listAll = pCatalog->chapters(); + for (int i=0; iisRecommendedNow(pCatalog)) + listNow << c; + else if (c->recommendation() == CChapter::RecommendationLearnNew) + listToday << c; + } + + listSelected << listNow << listToday; + m_modelChapter.setModelData (pCatalog, listSelected); + + if (!bEnableRepeat && listSelected.isEmpty()) return false; + + updateEnable(); + + return true; +} + +void CDlgLearnAssistant::updateEnable() +{ + labRepeat->setEnabled(rbRepeat->isChecked()); + tvChapters->setEnabled(rbList->isChecked()); +} + + +void CDlgLearnAssistant::on_buttonBox_accepted() +{ +QModelIndexList list = tvChapters->selectionModel()->selectedIndexes(); + if (rbList->isChecked() && list.isEmpty()) + { + QMessageBox::information(this, tr("Information"), tr("Bitte markieren Sie in der Liste ein Kapitel, das Sie lernen möchten!")); + return; + } + accept(); +} + +CChapter* CDlgLearnAssistant::selectedChapter() +{ +QModelIndexList list = tvChapters->selectionModel()->selectedIndexes(); + if (rbRepeat->isChecked()) + return m_pCatalog; + if (rbList->isChecked()) + { + if (list.isEmpty()) return 0; + return (CChapter*)list.first().internalPointer(); + } + return 0; +} diff --git a/dlglearnassistant.h b/dlglearnassistant.h index 28a4ba3..bc8ff9d 100644 --- a/dlglearnassistant.h +++ b/dlglearnassistant.h @@ -1,56 +1,56 @@ -/*************************************************************************** - * 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. * - ***************************************************************************/ - -#ifndef DLGLEARNASSISTANT_H -#define DLGLEARNASSISTANT_H - -#include -#include "ui_dlglearnassistant.h" - -#include "chaptermodel.h" - -class CDlgLearnAssistant : public QDialog, Ui::DlgLearnAssistant -{ - Q_OBJECT -public: - CDlgLearnAssistant (QWidget *pParent=0); - ~CDlgLearnAssistant () {} - - bool setup(CCatalog *pCatalog); - CChapter* selectedChapter(); - -protected slots: - void on_buttonBox_accepted(); - inline void on_rbList_toggled() { updateEnable(); } - inline void on_rbRepeat_toggled() { updateEnable(); } - -protected: - void updateEnable(); - -protected: - CCatalog *m_pCatalog; - CChapterModel m_modelChapter; - -// CRecommendation m_recomDeepen; -// CRecommendation m_recomRepeat; -}; - -#endif +/*************************************************************************** + * 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. * + ***************************************************************************/ + +#ifndef DLGLEARNASSISTANT_H +#define DLGLEARNASSISTANT_H + +#include +#include "ui_dlglearnassistant.h" + +#include "chaptermodel.h" + +class CDlgLearnAssistant : public QDialog, Ui::DlgLearnAssistant +{ + Q_OBJECT +public: + CDlgLearnAssistant (QWidget *pParent=0); + ~CDlgLearnAssistant () {} + + bool setup(CCatalog *pCatalog); + CChapter* selectedChapter(); + +protected slots: + void on_buttonBox_accepted(); + inline void on_rbList_toggled() { updateEnable(); } + inline void on_rbRepeat_toggled() { updateEnable(); } + +protected: + void updateEnable(); + +protected: + CCatalog *m_pCatalog; + CChapterModel m_modelChapter; + +// CRecommendation m_recomDeepen; +// CRecommendation m_recomRepeat; +}; + +#endif diff --git a/dlglearnstatistic.cpp b/dlglearnstatistic.cpp index 6a8cce4..557be68 100644 --- a/dlglearnstatistic.cpp +++ b/dlglearnstatistic.cpp @@ -1,417 +1,417 @@ -/*************************************************************************** - * 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 "dlglearnstatistic.h" -#include "chapter.h" -#include "tools.h" -#include "plotwidget.h" - -#include - -CDlgLearnStatistic::CDlgLearnStatistic(QWidget *pParent) : QDialog(pParent) -{ - - - m_pChapter = 0; - setupUi(this); - cbPeriod->addItem(tr("2 Wochen"), 14); - cbPeriod->addItem(tr("1 Monat"), 30); - cbPeriod->addItem(tr("3 Monate"), 90); - cbPeriod->addItem(tr("6 Monate"), 30*6); - cbPeriod->addItem(tr("1 Jahr"), 365); - cbPeriod->setCurrentIndex(0); - - m_pPlotLevel = new CPlotWidget(groupLevel); - m_pPlotLevel->setFrameShape(QFrame::Panel); - m_pPlotLevel->setFrameShadow(QFrame::Sunken); - groupLevel->layout()->addWidget(m_pPlotLevel); - - m_pPlotLevel->setBorderDistance(25, 5, 5, 25); - m_pPlotLevel->setLimitY(0.0, 5.5); - m_pPlotLevel->setBorderPen(QPen(Qt::black)); - m_pPlotLevel->setBorder(CPlotWidget::BorderLeft | CPlotWidget::BorderBottom); - m_pPlotLevel->setTicY(1.0); - - QList listTics; - for (int i=0; i<=LEVEL_MAX; i++) - { - CPlotWidgetTic tic (i, QPixmap(CQuestion::levelIconName(i))); - listTics.append(tic); - } - m_pPlotLevel->setTicListY(listTics); - - m_pPlotClicked = new CPlotWidget(groupClicked); - m_pPlotClicked->setFrameShape(QFrame::Panel); - m_pPlotClicked->setFrameShadow(QFrame::Sunken); - groupClicked->layout()->addWidget(m_pPlotClicked); - - m_pPlotClicked->setType(CPlotWidget::PlotBarsSum); - m_pPlotClicked->setBarOffset(-0.5); - m_pPlotClicked->setBorderDistance(25, 5, 5, 25); - - // Zeitaufwand - cbPeriod2->addItem(tr("2 Wochen"), 14); - cbPeriod2->addItem(tr("1 Monat"), 30); - cbPeriod2->addItem(tr("3 Monate"), 90); - cbPeriod2->addItem(tr("6 Monate"), 30*6); - cbPeriod2->addItem(tr("1 Jahr"), 365); - cbPeriod2->setCurrentIndex(0); - - m_pPlotTime = new CPlotWidget(groupTimeExpediture); - m_pPlotTime->setFrameShape(QFrame::Panel); - m_pPlotTime->setFrameShadow(QFrame::Sunken); - groupTimeExpediture->layout()->addWidget(m_pPlotTime); - - m_pPlotTime->setType(CPlotWidget::PlotBarsSum); - m_pPlotTime->setBarOffset(-0.5); - m_pPlotTime->setBorderDistance(25, 5, 5, 25); -/* - m_pPlotTimePerQuestion = new CPlotWidget(groupTimeExpediturePerQuestion); - m_pPlotTimePerQuestion->setFrameShape(QFrame::Panel); - m_pPlotTimePerQuestion->setFrameShadow(QFrame::Sunken); - groupTimeExpediturePerQuestion->layout()->addWidget(m_pPlotTimePerQuestion); - - m_pPlotTimePerQuestion->setType(CPlotWidget::PlotLines); - m_pPlotTimePerQuestion->setBorderDistance(25, 5, 5, 25); -*/ -} - -void CDlgLearnStatistic::go(CChapter *pChapter) -{ - m_pChapter = pChapter; - updateTable(); - updateHistory(); - updateTimeExpediture(); - exec(); -} - - -void CDlgLearnStatistic::updateTable() -{ -const int w=60, h=16; -QList listQuestion = m_pChapter->questionPool(); -CDayStatistic ds; - - // CHAPTER STATISTICS - labChapter->setText (m_pChapter->text()); - labChapterCount->setText(QString("%1").arg(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 = listQuestion.size(), dPercent=0.0; - 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)); - - ds = m_pChapter->dayStatistic(QDate()); - labClickCount->setText(QString::number(ds.clickedCount())); - labClickCorrect->setText(QString::number(ds.clickedCorrect())); - labClickWrong->setText(QString::number(ds.clickedWrong())); - labClickCorrectP->setText(ds.clickedCount() != 0 ? QString("%1 %").arg(100.0 * ds.clickedCorrect() / ds.clickedCount(), 0, 'f', 1) : "--"); - labClickWrongP->setText(ds.clickedCount() != 0 ? QString("%1 %").arg(100.0 * ds.clickedWrong() / ds.clickedCount(), 0, 'f', 1) : "--"); - labTimeExpediture->setText(QString("%1 h %2 m %3 s").arg(ds.timeExpediture() / 3600000).arg(ds.timeExpediture() / 60000 % 60, 2, 10, QChar('0')).arg(ds.timeExpediture()/1000 % 60, 2, 10, QChar('0'))); - labTimeExpeditureCorrect->setText(QString("%1 h %2 m %3 s").arg(ds.timeExpeditureCorrect() / 3600000).arg(ds.timeExpeditureCorrect() / 60000 % 60, 2, 10, QChar('0')).arg(ds.timeExpeditureCorrect()/1000 % 60, 2, 10, QChar('0'))); - labTimeExpeditureWrong->setText(QString("%1 h %2 m %3 s").arg(ds.timeExpeditureWrong() / 3600000).arg(ds.timeExpeditureWrong() / 60000 % 60, 2, 10, QChar('0')).arg(ds.timeExpeditureWrong()/1000 % 60, 2, 10, QChar('0'))); - labTimeExpeditureCorrectP->setText(ds.timeExpediture() != 0 ? QString("%1 %").arg(100.0 * ds.timeExpeditureCorrect() / ds.timeExpediture(), 0, 'f', 1) : "--"); - labTimeExpeditureWrongP->setText(ds.timeExpediture() != 0 ? QString("%1 %").arg(100.0 * ds.timeExpeditureWrong() / ds.timeExpediture(), 0, 'f', 1) : "--"); - labTimePerQuestion->setText(ds.clickedCount() != 0 ? QString("%2 m %3 s").arg(ds.timeExpediture() / ds.clickedCount() / 60000).arg(ds.timeExpediture() / 1000 / ds.clickedCount() % 60, 2, 10, QChar('0')) : "--"); - labTimePerQuestionCorrect->setText(ds.clickedCorrect() != 0 ? QString("%2 m %3 s").arg(ds.timeExpeditureCorrect() / ds.clickedCorrect() / 60000).arg(ds.timeExpeditureCorrect() / 1000 / ds.clickedCorrect() % 60, 2, 10, QChar('0')) : "--"); - labTimePerQuestionWrong->setText(ds.clickedWrong() != 0 ? QString("%2 m %3 s").arg(ds.timeExpeditureWrong() / ds.clickedWrong() / 60000).arg(ds.timeExpeditureWrong() / 1000 / ds.clickedWrong() % 60, 2, 10, QChar('0')) : "--"); -} - -void CDlgLearnStatistic::on_cbPeriod_currentIndexChanged(int index) -{ - Q_UNUSED(index); - updateHistory(); -} - -void CDlgLearnStatistic::updateHistory() -{ -int iDays = cbPeriod->itemData(cbPeriod->currentIndex()).toInt(), idx=0; -CPlotWidgetCurve curveLevel, curveClickedCorrect, curveClickedWrong; -QDate date = QDate::currentDate(); -CDayStatistic ds; -QList listTics; -CPlotWidgetTic tic; -bool bAddTic=false; -unsigned uMaxClicked=0; - - if (m_pChapter == 0) return; - setCursor(Qt::WaitCursor); - date = date.addDays(-iDays); - - tic.setLineType(CPlotWidgetTic::LineNone); - tic.setFillType(CPlotWidgetTic::FillAll); - tic.setPen(QPen(Qt::darkGray)); - - while (date <= QDate::currentDate()) - { - if (bAddTic) - { - bAddTic=false; - tic.setPos(idx-1); - if (listTics.size() % 2) - tic.setBrush(QBrush(Qt::white)); - else - tic.setBrush(QBrush(QColor(232,232,232))); - listTics.append(tic); - } - - ds = m_pChapter->dayStatistic(date); - curveLevel.append(CPlotWidgetPoint(idx, ds.level())); - curveClickedCorrect.append(CPlotWidgetPoint((double)idx, ds.clickedCorrect())); - curveClickedWrong.append(CPlotWidgetPoint((double)idx, ds.clickedWrong())); - if (ds.clickedCount() > uMaxClicked) uMaxClicked = ds.clickedCount(); - - date = date.addDays(1); - - // X-Tics - if (iDays <= 14) - { // daily tics - tic.setText(date.toString("ddd")); - tic.setWidth(1); - if (date.dayOfWeek() == Qt::Monday) - tic.setLineType(CPlotWidgetTic::LinePlot); - else - tic.setLineType(CPlotWidgetTic::LineNone); - - bAddTic=true; - } - else if (iDays <= 90 && date.dayOfWeek() == Qt::Monday) - { - tic.setText(QString("KW %1").arg(date.weekNumber())); - tic.setWidth(7); - bAddTic=true; - } - else if (iDays > 90 && date.day() == 1) - { - tic.setText(date.toString("MMM")); - tic.setWidth(date.daysInMonth()); - if (date.month() == 1) - tic.setLineType(CPlotWidgetTic::LinePlot); - else - tic.setLineType(CPlotWidgetTic::LineNone); - bAddTic=true; - } - - idx++; - } - - m_pPlotLevel->setTicListX(listTics); - m_pPlotClicked->setTicListX(listTics); - - - curveLevel.setPen(QPen(Qt::darkBlue)); - curveClickedCorrect.setPen(QPen(Qt::green)); - curveClickedCorrect.setBrush(QBrush(Qt::green)); - curveClickedWrong.setPen(QPen(Qt::red)); - curveClickedWrong.setBrush(QBrush(Qt::red)); - - - m_pPlotLevel->clearCurves(); - m_pPlotLevel->appendCurve(curveLevel); - m_pPlotLevel->update(); - - m_pPlotClicked->clearCurves(); - m_pPlotClicked->appendCurve(curveClickedCorrect); - m_pPlotClicked->appendCurve(curveClickedWrong); - m_pPlotClicked->update(); - - unsigned uDiffBase[] = {1, 2, 5, 10}, uDiffMulti=1, uDiff=0, uDiffCount=0; - idx = 0; - do - { - if (idx == 4) { idx = 0; uDiffMulti*=10; } - uDiff = uDiffBase[idx] * uDiffMulti; - uDiffCount = (uMaxClicked / uDiff) + 1; - idx++; - } - while (uDiffCount > 9); - - m_pPlotClicked->setLimitY(0, uDiffCount * uDiff + uDiff/2); - - listTics.clear(); - tic.clear(); - tic.setTextFlags(Qt::AlignRight | Qt::AlignVCenter); - for (unsigned u=0; u<=uDiffCount * uDiff; u+=uDiff) - { - tic.setPos(u); - tic.setText(QString("%1").arg(u)); - listTics.append(tic); - } - m_pPlotClicked->setTicListY(listTics); - - setCursor(Qt::ArrowCursor); -} - - -void CDlgLearnStatistic::on_cbPeriod2_currentIndexChanged(int index) -{ - Q_UNUSED(index); - updateTimeExpediture(); -} - -void CDlgLearnStatistic::updateTimeExpediture() -{ -int iDays = cbPeriod2->itemData(cbPeriod2->currentIndex()).toInt(), idx=0; -CPlotWidgetCurve curveTimeWrong, curveTimeCorrect,curveTimePQWrong, curveTimePQCorrect; // PQ=per question -QDate date = QDate::currentDate(); -CDayStatistic ds; -QList listTics; -CPlotWidgetTic tic; -bool bAddTic=false; -unsigned uMaxTime=0; - - if (m_pChapter == 0) return; - setCursor(Qt::WaitCursor); - date = date.addDays(-iDays); - - tic.setLineType(CPlotWidgetTic::LineNone); - tic.setFillType(CPlotWidgetTic::FillAll); - tic.setPen(QPen(Qt::darkGray)); - - while (date <= QDate::currentDate()) - { - if (bAddTic) - { - bAddTic=false; - tic.setPos(idx-1); - if (listTics.size() % 2) - tic.setBrush(QBrush(Qt::white)); - else - tic.setBrush(QBrush(QColor(232,232,232))); - listTics.append(tic); - } - - ds = m_pChapter->dayStatistic(date); - curveTimeWrong.append(CPlotWidgetPoint(idx, (double)ds.timeExpeditureWrong()/1000.0/60.0)); - curveTimeCorrect.append(CPlotWidgetPoint(idx, (double)ds.timeExpeditureCorrect()/1000.0/60.0)); -/* if (ds.clickedWrong() > 0) - curveTimePQWrong.append(CPlotWidgetPoint(idx, (double)ds.timeExpeditureWrong()/1000.0/60.0/(double)ds.clickedWrong())); - else - curveTimePQWrong.append(CPlotWidgetPoint(idx, 0)); - if (ds.clickedCorrect() > 0) - curveTimePQCorrect.append(CPlotWidgetPoint(idx, (double)ds.timeExpeditureCorrect()/1000.0/60.0/(double)ds.clickedCorrect())); - else - curveTimePQWrong.append(CPlotWidgetPoint(idx, 0));*/ - if (ds.timeExpediture()/1000/60 > uMaxTime) uMaxTime = ds.timeExpediture()/1000/60; - - date = date.addDays(1); - - // X-Tics - if (iDays <= 14) - { // daily tics - tic.setText(date.toString("ddd")); - tic.setWidth(1); - if (date.dayOfWeek() == Qt::Monday) - tic.setLineType(CPlotWidgetTic::LinePlot); - else - tic.setLineType(CPlotWidgetTic::LineNone); - - bAddTic=true; - } - else if (iDays <= 90 && date.dayOfWeek() == Qt::Monday) - { - tic.setText(QString("KW %1").arg(date.weekNumber())); - tic.setWidth(7); - bAddTic=true; - } - else if (iDays > 90 && date.day() == 1) - { - tic.setText(date.toString("MMM")); - tic.setWidth(date.daysInMonth()); - if (date.month() == 1) - tic.setLineType(CPlotWidgetTic::LinePlot); - else - tic.setLineType(CPlotWidgetTic::LineNone); - bAddTic=true; - } - - idx++; - } - - m_pPlotTime->setTicListX(listTics); - //m_pPlotTimePerQuestion->setTicListX(listTics); - - curveTimeCorrect.setPen(QPen(Qt::green)); - curveTimeCorrect.setBrush(QBrush(Qt::green)); - curveTimeWrong.setPen(QPen(Qt::red)); - curveTimeWrong.setBrush(QBrush(Qt::red)); - - m_pPlotTime->clearCurves(); - m_pPlotTime->appendCurve(curveTimeCorrect); - m_pPlotTime->appendCurve(curveTimeWrong); - m_pPlotTime->update(); -/* - m_pPlotTimePerQuestion->clearCurves(); - m_pPlotTimePerQuestion->appendCurve(curveTimePQCorrect); - m_pPlotTimePerQuestion->appendCurve(curveTimePQWrong); - m_pPlotTimePerQuestion->setAutoLimitRoundY(0.1); - m_pPlotTimePerQuestion->update(); -*/ - unsigned uDiffBase[] = {1, 2, 5, 10}, uDiffMulti=1, uDiff=0, uDiffCount=0; - idx = 0; - do - { - if (idx == 4) { idx = 0; uDiffMulti*=10; } - uDiff = uDiffBase[idx] * uDiffMulti; - uDiffCount = (uMaxTime / uDiff) + 1; - idx++; - } - while (uDiffCount > 9); - - m_pPlotTime->setLimitY(0, uDiffCount * uDiff + uDiff/2); - - listTics.clear(); - tic.clear(); - tic.setTextFlags(Qt::AlignRight | Qt::AlignVCenter); - for (unsigned u=0; u<=uDiffCount * uDiff; u+=uDiff) - { - tic.setPos(u); - tic.setText(QString("%1").arg(u)); - listTics.append(tic); - } - m_pPlotTime->setTicListY(listTics); - - setCursor(Qt::ArrowCursor); -} +/*************************************************************************** + * 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 "dlglearnstatistic.h" +#include "chapter.h" +#include "tools.h" +#include "plotwidget.h" + +#include + +CDlgLearnStatistic::CDlgLearnStatistic(QWidget *pParent) : QDialog(pParent) +{ + + + m_pChapter = 0; + setupUi(this); + cbPeriod->addItem(tr("2 Wochen"), 14); + cbPeriod->addItem(tr("1 Monat"), 30); + cbPeriod->addItem(tr("3 Monate"), 90); + cbPeriod->addItem(tr("6 Monate"), 30*6); + cbPeriod->addItem(tr("1 Jahr"), 365); + cbPeriod->setCurrentIndex(0); + + m_pPlotLevel = new CPlotWidget(groupLevel); + m_pPlotLevel->setFrameShape(QFrame::Panel); + m_pPlotLevel->setFrameShadow(QFrame::Sunken); + groupLevel->layout()->addWidget(m_pPlotLevel); + + m_pPlotLevel->setBorderDistance(25, 5, 5, 25); + m_pPlotLevel->setLimitY(0.0, 5.5); + m_pPlotLevel->setBorderPen(QPen(Qt::black)); + m_pPlotLevel->setBorder(CPlotWidget::BorderLeft | CPlotWidget::BorderBottom); + m_pPlotLevel->setTicY(1.0); + + QList listTics; + for (int i=0; i<=LEVEL_MAX; i++) + { + CPlotWidgetTic tic (i, QPixmap(CQuestion::levelIconName(i))); + listTics.append(tic); + } + m_pPlotLevel->setTicListY(listTics); + + m_pPlotClicked = new CPlotWidget(groupClicked); + m_pPlotClicked->setFrameShape(QFrame::Panel); + m_pPlotClicked->setFrameShadow(QFrame::Sunken); + groupClicked->layout()->addWidget(m_pPlotClicked); + + m_pPlotClicked->setType(CPlotWidget::PlotBarsSum); + m_pPlotClicked->setBarOffset(-0.5); + m_pPlotClicked->setBorderDistance(25, 5, 5, 25); + + // Zeitaufwand + cbPeriod2->addItem(tr("2 Wochen"), 14); + cbPeriod2->addItem(tr("1 Monat"), 30); + cbPeriod2->addItem(tr("3 Monate"), 90); + cbPeriod2->addItem(tr("6 Monate"), 30*6); + cbPeriod2->addItem(tr("1 Jahr"), 365); + cbPeriod2->setCurrentIndex(0); + + m_pPlotTime = new CPlotWidget(groupTimeExpediture); + m_pPlotTime->setFrameShape(QFrame::Panel); + m_pPlotTime->setFrameShadow(QFrame::Sunken); + groupTimeExpediture->layout()->addWidget(m_pPlotTime); + + m_pPlotTime->setType(CPlotWidget::PlotBarsSum); + m_pPlotTime->setBarOffset(-0.5); + m_pPlotTime->setBorderDistance(25, 5, 5, 25); +/* + m_pPlotTimePerQuestion = new CPlotWidget(groupTimeExpediturePerQuestion); + m_pPlotTimePerQuestion->setFrameShape(QFrame::Panel); + m_pPlotTimePerQuestion->setFrameShadow(QFrame::Sunken); + groupTimeExpediturePerQuestion->layout()->addWidget(m_pPlotTimePerQuestion); + + m_pPlotTimePerQuestion->setType(CPlotWidget::PlotLines); + m_pPlotTimePerQuestion->setBorderDistance(25, 5, 5, 25); +*/ +} + +void CDlgLearnStatistic::go(CChapter *pChapter) +{ + m_pChapter = pChapter; + updateTable(); + updateHistory(); + updateTimeExpediture(); + exec(); +} + + +void CDlgLearnStatistic::updateTable() +{ +const int w=60, h=16; +QList listQuestion = m_pChapter->questionPool(); +CDayStatistic ds; + + // CHAPTER STATISTICS + labChapter->setText (m_pChapter->text()); + labChapterCount->setText(QString("%1").arg(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 = listQuestion.size(), dPercent=0.0; + 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)); + + ds = m_pChapter->dayStatistic(QDate()); + labClickCount->setText(QString::number(ds.clickedCount())); + labClickCorrect->setText(QString::number(ds.clickedCorrect())); + labClickWrong->setText(QString::number(ds.clickedWrong())); + labClickCorrectP->setText(ds.clickedCount() != 0 ? QString("%1 %").arg(100.0 * ds.clickedCorrect() / ds.clickedCount(), 0, 'f', 1) : "--"); + labClickWrongP->setText(ds.clickedCount() != 0 ? QString("%1 %").arg(100.0 * ds.clickedWrong() / ds.clickedCount(), 0, 'f', 1) : "--"); + labTimeExpediture->setText(QString("%1 h %2 m %3 s").arg(ds.timeExpediture() / 3600000).arg(ds.timeExpediture() / 60000 % 60, 2, 10, QChar('0')).arg(ds.timeExpediture()/1000 % 60, 2, 10, QChar('0'))); + labTimeExpeditureCorrect->setText(QString("%1 h %2 m %3 s").arg(ds.timeExpeditureCorrect() / 3600000).arg(ds.timeExpeditureCorrect() / 60000 % 60, 2, 10, QChar('0')).arg(ds.timeExpeditureCorrect()/1000 % 60, 2, 10, QChar('0'))); + labTimeExpeditureWrong->setText(QString("%1 h %2 m %3 s").arg(ds.timeExpeditureWrong() / 3600000).arg(ds.timeExpeditureWrong() / 60000 % 60, 2, 10, QChar('0')).arg(ds.timeExpeditureWrong()/1000 % 60, 2, 10, QChar('0'))); + labTimeExpeditureCorrectP->setText(ds.timeExpediture() != 0 ? QString("%1 %").arg(100.0 * ds.timeExpeditureCorrect() / ds.timeExpediture(), 0, 'f', 1) : "--"); + labTimeExpeditureWrongP->setText(ds.timeExpediture() != 0 ? QString("%1 %").arg(100.0 * ds.timeExpeditureWrong() / ds.timeExpediture(), 0, 'f', 1) : "--"); + labTimePerQuestion->setText(ds.clickedCount() != 0 ? QString("%2 m %3 s").arg(ds.timeExpediture() / ds.clickedCount() / 60000).arg(ds.timeExpediture() / 1000 / ds.clickedCount() % 60, 2, 10, QChar('0')) : "--"); + labTimePerQuestionCorrect->setText(ds.clickedCorrect() != 0 ? QString("%2 m %3 s").arg(ds.timeExpeditureCorrect() / ds.clickedCorrect() / 60000).arg(ds.timeExpeditureCorrect() / 1000 / ds.clickedCorrect() % 60, 2, 10, QChar('0')) : "--"); + labTimePerQuestionWrong->setText(ds.clickedWrong() != 0 ? QString("%2 m %3 s").arg(ds.timeExpeditureWrong() / ds.clickedWrong() / 60000).arg(ds.timeExpeditureWrong() / 1000 / ds.clickedWrong() % 60, 2, 10, QChar('0')) : "--"); +} + +void CDlgLearnStatistic::on_cbPeriod_currentIndexChanged(int index) +{ + Q_UNUSED(index); + updateHistory(); +} + +void CDlgLearnStatistic::updateHistory() +{ +int iDays = cbPeriod->itemData(cbPeriod->currentIndex()).toInt(), idx=0; +CPlotWidgetCurve curveLevel, curveClickedCorrect, curveClickedWrong; +QDate date = QDate::currentDate(); +CDayStatistic ds; +QList listTics; +CPlotWidgetTic tic; +bool bAddTic=false; +unsigned uMaxClicked=0; + + if (m_pChapter == 0) return; + setCursor(Qt::WaitCursor); + date = date.addDays(-iDays); + + tic.setLineType(CPlotWidgetTic::LineNone); + tic.setFillType(CPlotWidgetTic::FillAll); + tic.setPen(QPen(Qt::darkGray)); + + while (date <= QDate::currentDate()) + { + if (bAddTic) + { + bAddTic=false; + tic.setPos(idx-1); + if (listTics.size() % 2) + tic.setBrush(QBrush(Qt::white)); + else + tic.setBrush(QBrush(QColor(232,232,232))); + listTics.append(tic); + } + + ds = m_pChapter->dayStatistic(date); + curveLevel.append(CPlotWidgetPoint(idx, ds.level())); + curveClickedCorrect.append(CPlotWidgetPoint((double)idx, ds.clickedCorrect())); + curveClickedWrong.append(CPlotWidgetPoint((double)idx, ds.clickedWrong())); + if (ds.clickedCount() > uMaxClicked) uMaxClicked = ds.clickedCount(); + + date = date.addDays(1); + + // X-Tics + if (iDays <= 14) + { // daily tics + tic.setText(date.toString("ddd")); + tic.setWidth(1); + if (date.dayOfWeek() == Qt::Monday) + tic.setLineType(CPlotWidgetTic::LinePlot); + else + tic.setLineType(CPlotWidgetTic::LineNone); + + bAddTic=true; + } + else if (iDays <= 90 && date.dayOfWeek() == Qt::Monday) + { + tic.setText(QString("KW %1").arg(date.weekNumber())); + tic.setWidth(7); + bAddTic=true; + } + else if (iDays > 90 && date.day() == 1) + { + tic.setText(date.toString("MMM")); + tic.setWidth(date.daysInMonth()); + if (date.month() == 1) + tic.setLineType(CPlotWidgetTic::LinePlot); + else + tic.setLineType(CPlotWidgetTic::LineNone); + bAddTic=true; + } + + idx++; + } + + m_pPlotLevel->setTicListX(listTics); + m_pPlotClicked->setTicListX(listTics); + + + curveLevel.setPen(QPen(Qt::darkBlue)); + curveClickedCorrect.setPen(QPen(Qt::green)); + curveClickedCorrect.setBrush(QBrush(Qt::green)); + curveClickedWrong.setPen(QPen(Qt::red)); + curveClickedWrong.setBrush(QBrush(Qt::red)); + + + m_pPlotLevel->clearCurves(); + m_pPlotLevel->appendCurve(curveLevel); + m_pPlotLevel->update(); + + m_pPlotClicked->clearCurves(); + m_pPlotClicked->appendCurve(curveClickedCorrect); + m_pPlotClicked->appendCurve(curveClickedWrong); + m_pPlotClicked->update(); + + unsigned uDiffBase[] = {1, 2, 5, 10}, uDiffMulti=1, uDiff=0, uDiffCount=0; + idx = 0; + do + { + if (idx == 4) { idx = 0; uDiffMulti*=10; } + uDiff = uDiffBase[idx] * uDiffMulti; + uDiffCount = (uMaxClicked / uDiff) + 1; + idx++; + } + while (uDiffCount > 9); + + m_pPlotClicked->setLimitY(0, uDiffCount * uDiff + uDiff/2); + + listTics.clear(); + tic.clear(); + tic.setTextFlags(Qt::AlignRight | Qt::AlignVCenter); + for (unsigned u=0; u<=uDiffCount * uDiff; u+=uDiff) + { + tic.setPos(u); + tic.setText(QString("%1").arg(u)); + listTics.append(tic); + } + m_pPlotClicked->setTicListY(listTics); + + setCursor(Qt::ArrowCursor); +} + + +void CDlgLearnStatistic::on_cbPeriod2_currentIndexChanged(int index) +{ + Q_UNUSED(index); + updateTimeExpediture(); +} + +void CDlgLearnStatistic::updateTimeExpediture() +{ +int iDays = cbPeriod2->itemData(cbPeriod2->currentIndex()).toInt(), idx=0; +CPlotWidgetCurve curveTimeWrong, curveTimeCorrect,curveTimePQWrong, curveTimePQCorrect; // PQ=per question +QDate date = QDate::currentDate(); +CDayStatistic ds; +QList listTics; +CPlotWidgetTic tic; +bool bAddTic=false; +unsigned uMaxTime=0; + + if (m_pChapter == 0) return; + setCursor(Qt::WaitCursor); + date = date.addDays(-iDays); + + tic.setLineType(CPlotWidgetTic::LineNone); + tic.setFillType(CPlotWidgetTic::FillAll); + tic.setPen(QPen(Qt::darkGray)); + + while (date <= QDate::currentDate()) + { + if (bAddTic) + { + bAddTic=false; + tic.setPos(idx-1); + if (listTics.size() % 2) + tic.setBrush(QBrush(Qt::white)); + else + tic.setBrush(QBrush(QColor(232,232,232))); + listTics.append(tic); + } + + ds = m_pChapter->dayStatistic(date); + curveTimeWrong.append(CPlotWidgetPoint(idx, (double)ds.timeExpeditureWrong()/1000.0/60.0)); + curveTimeCorrect.append(CPlotWidgetPoint(idx, (double)ds.timeExpeditureCorrect()/1000.0/60.0)); +/* if (ds.clickedWrong() > 0) + curveTimePQWrong.append(CPlotWidgetPoint(idx, (double)ds.timeExpeditureWrong()/1000.0/60.0/(double)ds.clickedWrong())); + else + curveTimePQWrong.append(CPlotWidgetPoint(idx, 0)); + if (ds.clickedCorrect() > 0) + curveTimePQCorrect.append(CPlotWidgetPoint(idx, (double)ds.timeExpeditureCorrect()/1000.0/60.0/(double)ds.clickedCorrect())); + else + curveTimePQWrong.append(CPlotWidgetPoint(idx, 0));*/ + if (ds.timeExpediture()/1000/60 > uMaxTime) uMaxTime = ds.timeExpediture()/1000/60; + + date = date.addDays(1); + + // X-Tics + if (iDays <= 14) + { // daily tics + tic.setText(date.toString("ddd")); + tic.setWidth(1); + if (date.dayOfWeek() == Qt::Monday) + tic.setLineType(CPlotWidgetTic::LinePlot); + else + tic.setLineType(CPlotWidgetTic::LineNone); + + bAddTic=true; + } + else if (iDays <= 90 && date.dayOfWeek() == Qt::Monday) + { + tic.setText(QString("KW %1").arg(date.weekNumber())); + tic.setWidth(7); + bAddTic=true; + } + else if (iDays > 90 && date.day() == 1) + { + tic.setText(date.toString("MMM")); + tic.setWidth(date.daysInMonth()); + if (date.month() == 1) + tic.setLineType(CPlotWidgetTic::LinePlot); + else + tic.setLineType(CPlotWidgetTic::LineNone); + bAddTic=true; + } + + idx++; + } + + m_pPlotTime->setTicListX(listTics); + //m_pPlotTimePerQuestion->setTicListX(listTics); + + curveTimeCorrect.setPen(QPen(Qt::green)); + curveTimeCorrect.setBrush(QBrush(Qt::green)); + curveTimeWrong.setPen(QPen(Qt::red)); + curveTimeWrong.setBrush(QBrush(Qt::red)); + + m_pPlotTime->clearCurves(); + m_pPlotTime->appendCurve(curveTimeCorrect); + m_pPlotTime->appendCurve(curveTimeWrong); + m_pPlotTime->update(); +/* + m_pPlotTimePerQuestion->clearCurves(); + m_pPlotTimePerQuestion->appendCurve(curveTimePQCorrect); + m_pPlotTimePerQuestion->appendCurve(curveTimePQWrong); + m_pPlotTimePerQuestion->setAutoLimitRoundY(0.1); + m_pPlotTimePerQuestion->update(); +*/ + unsigned uDiffBase[] = {1, 2, 5, 10}, uDiffMulti=1, uDiff=0, uDiffCount=0; + idx = 0; + do + { + if (idx == 4) { idx = 0; uDiffMulti*=10; } + uDiff = uDiffBase[idx] * uDiffMulti; + uDiffCount = (uMaxTime / uDiff) + 1; + idx++; + } + while (uDiffCount > 9); + + m_pPlotTime->setLimitY(0, uDiffCount * uDiff + uDiff/2); + + listTics.clear(); + tic.clear(); + tic.setTextFlags(Qt::AlignRight | Qt::AlignVCenter); + for (unsigned u=0; u<=uDiffCount * uDiff; u+=uDiff) + { + tic.setPos(u); + tic.setText(QString("%1").arg(u)); + listTics.append(tic); + } + m_pPlotTime->setTicListY(listTics); + + setCursor(Qt::ArrowCursor); +} diff --git a/dlglearnstatistic.h b/dlglearnstatistic.h index a54169e..35ea607 100644 --- a/dlglearnstatistic.h +++ b/dlglearnstatistic.h @@ -1,54 +1,54 @@ -/*************************************************************************** - * 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. * - ***************************************************************************/ - -#pragma once - -#include -#include "ui_dlglearnstatistic.h" - -class CChapter; -class CPlotWidget; - -class CDlgLearnStatistic : public QDialog, Ui::DlgLearnStatistic -{ -Q_OBJECT -public: - CDlgLearnStatistic(QWidget *pParent=0); - ~CDlgLearnStatistic() {} - - void go(CChapter *pChapter); - -protected slots: - void on_cbPeriod_currentIndexChanged(int index); - void on_cbPeriod2_currentIndexChanged(int index); - -protected: - void updateTable(); - void updateHistory(); - void updateTimeExpediture(); - -protected: - CChapter *m_pChapter; - CPlotWidget *m_pPlotLevel; - CPlotWidget *m_pPlotClicked; - CPlotWidget *m_pPlotTime; -// CPlotWidget *m_pPlotTimePerQuestion; -}; +/*************************************************************************** + * 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. * + ***************************************************************************/ + +#pragma once + +#include +#include "ui_dlglearnstatistic.h" + +class CChapter; +class CPlotWidget; + +class CDlgLearnStatistic : public QDialog, Ui::DlgLearnStatistic +{ +Q_OBJECT +public: + CDlgLearnStatistic(QWidget *pParent=0); + ~CDlgLearnStatistic() {} + + void go(CChapter *pChapter); + +protected slots: + void on_cbPeriod_currentIndexChanged(int index); + void on_cbPeriod2_currentIndexChanged(int index); + +protected: + void updateTable(); + void updateHistory(); + void updateTimeExpediture(); + +protected: + CChapter *m_pChapter; + CPlotWidget *m_pPlotLevel; + CPlotWidget *m_pPlotClicked; + CPlotWidget *m_pPlotTime; +// CPlotWidget *m_pPlotTimePerQuestion; +}; diff --git a/dlgviewquestion.cpp b/dlgviewquestion.cpp index a1f27b2..2734ccb 100644 --- a/dlgviewquestion.cpp +++ b/dlgviewquestion.cpp @@ -1,43 +1,43 @@ -/*************************************************************************** - * 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 "dlgviewquestion.h" -#include "catalog.h" - -void CDlgViewQuestion::go (CCatalog *pCatalog, CQuestion *pQuestion, const unsigned uAnswer) -{ -QString str; - Q_ASSERT (pCatalog != 0); - if (!pQuestion) return; - str = pQuestion->learnText(pCatalog, true, true); - str += "

"; - if (uAnswer != 0 && pQuestion->isCorrectAnswer(uAnswer)) - str += ""; - else if (uAnswer != 0) - str += ""; - - str += pQuestion->correctionText(uAnswer); - str += "

"; - textBrowser->setHtml(str); - setWindowTitle (tr("Frage") + " " + pQuestion->id()); - exec(); -} +/*************************************************************************** + * 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 "dlgviewquestion.h" +#include "catalog.h" + +void CDlgViewQuestion::go (CCatalog *pCatalog, CQuestion *pQuestion, const unsigned uAnswer) +{ +QString str; + Q_ASSERT (pCatalog != 0); + if (!pQuestion) return; + str = pQuestion->learnText(pCatalog, true, true); + str += "

"; + if (uAnswer != 0 && pQuestion->isCorrectAnswer(uAnswer)) + str += ""; + else if (uAnswer != 0) + str += ""; + + str += pQuestion->correctionText(uAnswer); + str += "

"; + textBrowser->setHtml(str); + setWindowTitle (tr("Frage") + " " + pQuestion->id()); + exec(); +} diff --git a/dlgviewquestion.h b/dlgviewquestion.h index 86fafd2..757ceff 100644 --- a/dlgviewquestion.h +++ b/dlgviewquestion.h @@ -1,44 +1,44 @@ -/*************************************************************************** - * 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. * - ***************************************************************************/ - -#ifndef DLGVIEWQUESTION_H -#define DLGVIEWQUESTION_H - -#include -#include "ui_dlgviewquestion.h" - -class CQuestion; -class CCatalog; - -class CDlgViewQuestion : public QDialog, Ui::DlgViewQuestion -{ - Q_OBJECT -public: - CDlgViewQuestion(QWidget *pParent) : QDialog(pParent) { setupUi(this); } - ~CDlgViewQuestion() { } - - void go (CCatalog *pCatalog, CQuestion *pQuestion, const unsigned uAnswer=0); - -protected: -}; - - -#endif // DLGVIEWQUESTION_H +/*************************************************************************** + * 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. * + ***************************************************************************/ + +#ifndef DLGVIEWQUESTION_H +#define DLGVIEWQUESTION_H + +#include +#include "ui_dlgviewquestion.h" + +class CQuestion; +class CCatalog; + +class CDlgViewQuestion : public QDialog, Ui::DlgViewQuestion +{ + Q_OBJECT +public: + CDlgViewQuestion(QWidget *pParent) : QDialog(pParent) { setupUi(this); } + ~CDlgViewQuestion() { } + + void go (CCatalog *pCatalog, CQuestion *pQuestion, const unsigned uAnswer=0); + +protected: +}; + + +#endif // DLGVIEWQUESTION_H diff --git a/error.cpp b/error.cpp index 21478f9..caa25b1 100644 --- a/error.cpp +++ b/error.cpp @@ -1,98 +1,98 @@ -/*************************************************************************** - * Copyright (C) 1999-2005 by Oliver Saal * - * http://www.oliver-saal.de/ * - * osaal@gmx.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. * - * * - * 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 "error.h" - -#include -#include - -#ifdef ERROR_USE_SQL -#include -#include -//#include -#include -#endif - -CError::CError () -{ - m_uLine = 0; -} - -CError::CError (const QString& strFunc, const QString& strFile, const unsigned int uLine) -{ - m_strFunction = strFunc; - m_strFile = strFile; - m_uLine = uLine; -} - -CError::CError (const QString& strText, const QString& strFunc, const QString& strFile, const unsigned int uLine) -{ - m_strText = strText; - m_strFunction = strFunc; - m_strFile = strFile; - m_uLine = uLine; -} - -#ifdef ERROR_USE_SQL - -CError::CError (const QString& strText, const QSqlDatabase* pDB, const QString& strFunc, const QString& strFile, const unsigned int uLine) -{ - m_strText = QString ("%1\n%3").arg (strText, pDB->lastError().text()); - m_strFunction = strFunc; - m_strFile = strFile; - m_uLine = uLine; -} - -CError::CError (const QString& strText, const QSqlQuery& query, const QString& strFunc, const QString& strFile, const unsigned int uLine) -{ -QMap map; - m_strText = QString ("%1\nSQL: %2\n").arg (strText, query.lastQuery()); - map = query.boundValues(); - if (!map.isEmpty()) - { - m_strText+="Bound values:\n"; - QMap::const_iterator i = map.constBegin(); - while (i != map.constEnd()) - { - m_strText += QString ("%1 / %2 ==> %4\n").arg (i.key(), i.value().typeName(), i.value().isNull() ? "NULL" : i.value().toString()); - ++i; - } - } - - m_strText += query.lastError().text(); - m_strFunction = strFunc; - m_strFile = strFile; - m_uLine = uLine; -} -#endif - -QString CError::toPlainText() const -{ - return QObject::tr("%1\n\nFunction: %2\nFile: %3 Line: %4\n").arg(m_strText, m_strFunction, m_strFile).arg(m_uLine); -} - -QString CError::toHtml() const -{ -QString str = m_strText; - str.replace ('\n', "
"); - return QObject::tr("

%1
Function: %2
File: %3 Line: %4


\n").arg(str, m_strFunction, m_strFile).arg(m_uLine); -} - +/*************************************************************************** + * Copyright (C) 1999-2005 by Oliver Saal * + * http://www.oliver-saal.de/ * + * osaal@gmx.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. * + * * + * 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 "error.h" + +#include +#include + +#ifdef ERROR_USE_SQL +#include +#include +//#include +#include +#endif + +CError::CError () +{ + m_uLine = 0; +} + +CError::CError (const QString& strFunc, const QString& strFile, const unsigned int uLine) +{ + m_strFunction = strFunc; + m_strFile = strFile; + m_uLine = uLine; +} + +CError::CError (const QString& strText, const QString& strFunc, const QString& strFile, const unsigned int uLine) +{ + m_strText = strText; + m_strFunction = strFunc; + m_strFile = strFile; + m_uLine = uLine; +} + +#ifdef ERROR_USE_SQL + +CError::CError (const QString& strText, const QSqlDatabase* pDB, const QString& strFunc, const QString& strFile, const unsigned int uLine) +{ + m_strText = QString ("%1\n%3").arg (strText, pDB->lastError().text()); + m_strFunction = strFunc; + m_strFile = strFile; + m_uLine = uLine; +} + +CError::CError (const QString& strText, const QSqlQuery& query, const QString& strFunc, const QString& strFile, const unsigned int uLine) +{ +QMap map; + m_strText = QString ("%1\nSQL: %2\n").arg (strText, query.lastQuery()); + map = query.boundValues(); + if (!map.isEmpty()) + { + m_strText+="Bound values:\n"; + QMap::const_iterator i = map.constBegin(); + while (i != map.constEnd()) + { + m_strText += QString ("%1 / %2 ==> %4\n").arg (i.key(), i.value().typeName(), i.value().isNull() ? "NULL" : i.value().toString()); + ++i; + } + } + + m_strText += query.lastError().text(); + m_strFunction = strFunc; + m_strFile = strFile; + m_uLine = uLine; +} +#endif + +QString CError::toPlainText() const +{ + return QObject::tr("%1\n\nFunction: %2\nFile: %3 Line: %4\n").arg(m_strText, m_strFunction, m_strFile).arg(m_uLine); +} + +QString CError::toHtml() const +{ +QString str = m_strText; + str.replace ('\n', "
"); + return QObject::tr("

%1
Function: %2
File: %3 Line: %4


\n").arg(str, m_strFunction, m_strFile).arg(m_uLine); +} + diff --git a/error.h b/error.h index 77b8019..2036394 100644 --- a/error.h +++ b/error.h @@ -1,78 +1,78 @@ -/*************************************************************************** - * Copyright (C) 1999-2007 by Oliver Saal * - * http://www.oliver-saal.de/ * - * osaal@gmx.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. * - * * - * 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. * - ***************************************************************************/ - -#ifndef ERROR_H -#define ERROR_H - -//#define ERROR_USE_SQL - -#ifndef __PRETTY_FUNCTION__ -# if defined __FUNCSIG__ -# define __PRETTY_FUNCTION__ __FUNCSIG__ -# elif defined __func__ -# define __PRETTY_FUNCTION__ __func__ -# else -# define __PRETTY_FUNCTION__ __FILE__ -# endif -#endif - - - -#include - -#ifdef ERROR_USE_SQL -#include - -#define THROW_TRANSACTION(x) throw CError (tr("Could not start database transaction."), x, __PRETTY_FUNCTION__, __FILE__, __LINE__); -#define THROW_COMMIT(x) throw CError (tr("Could not commit database transaction."), x, __PRETTY_FUNCTION__, __FILE__, __LINE__); -#endif - -class CError -{ -public: - CError (); - CError (const QString& strFunc, const QString& strFile, const unsigned int uLine); - CError (const QString& strText, const QString& strFunc, const QString& strFile, const unsigned int uLine); -#ifdef ERROR_USE_SQL - CError (const QString& strText, const QSqlDatabase* pDB, const QString& strFunc, const QString& strFile, const unsigned int uLine); - CError (const QString& strText, const QSqlQuery& query, const QString& strFunc, const QString& strFile, const unsigned int uLine); -#endif - - inline void preText (const QString& str) { m_strText = str + m_strText; } - inline void postText (const QString& str) { m_strText += str; } - - inline QString text() const { return m_strText; } - inline QString function() const { return m_strFunction; } - inline QString file() const { return m_strFile; } - inline unsigned line() const { return m_uLine; } - - QString toPlainText() const; - QString toHtml() const; - -protected: - QString m_strText; - QString m_strFunction; - QString m_strFile; - unsigned int m_uLine; -}; - - -#endif // ERROR_H +/*************************************************************************** + * Copyright (C) 1999-2007 by Oliver Saal * + * http://www.oliver-saal.de/ * + * osaal@gmx.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. * + * * + * 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. * + ***************************************************************************/ + +#ifndef ERROR_H +#define ERROR_H + +//#define ERROR_USE_SQL + +#ifndef __PRETTY_FUNCTION__ +# if defined __FUNCSIG__ +# define __PRETTY_FUNCTION__ __FUNCSIG__ +# elif defined __func__ +# define __PRETTY_FUNCTION__ __func__ +# else +# define __PRETTY_FUNCTION__ __FILE__ +# endif +#endif + + + +#include + +#ifdef ERROR_USE_SQL +#include + +#define THROW_TRANSACTION(x) throw CError (tr("Could not start database transaction."), x, __PRETTY_FUNCTION__, __FILE__, __LINE__); +#define THROW_COMMIT(x) throw CError (tr("Could not commit database transaction."), x, __PRETTY_FUNCTION__, __FILE__, __LINE__); +#endif + +class CError +{ +public: + CError (); + CError (const QString& strFunc, const QString& strFile, const unsigned int uLine); + CError (const QString& strText, const QString& strFunc, const QString& strFile, const unsigned int uLine); +#ifdef ERROR_USE_SQL + CError (const QString& strText, const QSqlDatabase* pDB, const QString& strFunc, const QString& strFile, const unsigned int uLine); + CError (const QString& strText, const QSqlQuery& query, const QString& strFunc, const QString& strFile, const unsigned int uLine); +#endif + + inline void preText (const QString& str) { m_strText = str + m_strText; } + inline void postText (const QString& str) { m_strText += str; } + + inline QString text() const { return m_strText; } + inline QString function() const { return m_strFunction; } + inline QString file() const { return m_strFile; } + inline unsigned line() const { return m_uLine; } + + QString toPlainText() const; + QString toHtml() const; + +protected: + QString m_strText; + QString m_strFunction; + QString m_strFile; + unsigned int m_uLine; +}; + + +#endif // ERROR_H diff --git a/exam.cpp b/exam.cpp index 507c162..c660257 100644 --- a/exam.cpp +++ b/exam.cpp @@ -1,306 +1,306 @@ -/*************************************************************************** - * 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 "exam.h" -#include "question.h" -#include "error.h" -#include "tools.h" - -#include -#include - -//#define DEBUGMSG - -void CExamPart::clear() -{ - m_iQuestionCount = 0; - m_strRegExp.clear(); -} - -bool CExamPart::load (QDomElement elem) -{ - if (elem.tagName() != QString ("exam_part")) return false; - m_iQuestionCount = elem.attribute("count").toUInt(); - if (m_iQuestionCount == 0) return false; - m_strGroup = elem.attribute("group"); - m_strRegExp = elem.text (); - if (m_strRegExp.isEmpty()) return false; - return true; -} - -void CExamPart::save (QDomElement& parent, QDomDocument& doc) const -{ -QDomElement elemRoot = doc.createElement("exam_part"); - - elemRoot.setAttribute("count", QString("%1").arg(m_iQuestionCount)); - elemRoot.setAttribute("group", m_strGroup); - elemRoot.appendChild(doc.createTextNode(m_strRegExp)); - - parent.appendChild(elemRoot); -} - -QList CExamPart::createQuestionPool(const QList& listAllQuestions) const -{ -QList listRet, listSelected; -QRegExp regexp(m_strRegExp); -int i=0; -CQuestion *q=0; - - #ifdef DEBUGMSG - qDebug ("\tPart Group = '%s' RegExp '%s' mit %i Fragen", qPrintable (m_strGroup), qPrintable(m_strRegExp), m_iQuestionCount); - #endif - - // Alle Fragen raussuchen, die dem RegExp entsprechen - for (i=0; igroups().contains(m_strGroup, Qt::CaseInsensitive)) - continue; - - if (regexp.exactMatch(q->id())) - { - #ifdef DEBUGMSG - qDebug ("\t\tAdding Question '%s'", qPrintable (q->id())); - #endif - listSelected.append(q); - } - } - - if (listSelected.size() < questionCount()) - { - throw CError ( - QString("Für den regulären Ausdruck '%1' wurden nur %2 Fragen gefunden - zu wenig, um daraus %3 Fragen für die Prüfung auszuwählen.") - .arg(m_strRegExp).arg(listSelected.size()).arg(questionCount()), __PRETTY_FUNCTION__, __FILE__, __LINE__); - } - - #ifdef DEBUGMSG - qDebug ("\tStarte Auswahlverfahren..."); - #endif - for (i=0; i < questionCount(); i++) - { - int rnd = afu_random (0, listSelected.size()-1); - q = listSelected.at(rnd); - #ifdef DEBUGMSG - qDebug ("\t\tSelected question '%s'", qPrintable (q->id())); - #endif - listRet.append (q); - listSelected.removeAt(rnd); - } - - return listRet; -} - - - -void CExam::clear() -{ - m_strId.clear(); - m_strName.clear(); - m_strComment.clear(); - m_uDuration = 0; - m_uMaxErrorPoints = 0; - m_listParts.clear(); -} - -bool CExam::load (QDomElement elem) -{ - if (elem.tagName() != QString ("exam")) return false; - - m_strId = elem.attribute("id"); - m_strName = elem.attribute("name"); - m_uDuration = elem.attribute("duration").toUInt(); - m_uMaxErrorPoints = elem.attribute("maxerrorpoints").toUInt(); - m_strComment = elem.attribute("comment"); - - if (m_strName.isEmpty()) return false; - if (m_uDuration == 0) return false; - - QDomNode n = elem.firstChild(); - while (!n.isNull()) - { - if (n.isElement ()) - { - QDomElement e = n.toElement (); - if (e.tagName() == QString ("exam_part")) - { - CExamPart ep; - if (ep.load(e)) m_listParts.append(ep); - } - } - n = n.nextSibling(); - } - - return true; -} - -void CExam::save (QDomElement& parent, QDomDocument& doc) const -{ -QDomElement elemRoot = doc.createElement("exam"); - - elemRoot.setAttribute("id", m_strId); - elemRoot.setAttribute("name", m_strName); - elemRoot.setAttribute("duration", QString("%1").arg(m_uDuration)); - elemRoot.setAttribute("maxerrorpoints", QString("%1").arg(m_uMaxErrorPoints)); - elemRoot.setAttribute("comment", m_strComment); - for (int i=0; i CExam::createQuestionPool(const QList& listAllQuestions) const -{ -QList listRet; - - #ifdef DEBUGMSG - qDebug ("Erzeuge Prüfungsfragen für '%s'", qPrintable(name())); - #endif - - try - { - for (int i=0; iid())); - #endif - - return listRet; -} - -CExamStat::CExamStat(const CExam& exam) -{ - clear(); - m_strId = exam.id(); -} - -void CExamStat::clear() -{ - m_strId.clear(); - m_dt = QDateTime::currentDateTime(); - m_uSecs = 0; - - m_strlQuestionId.clear(); - m_listAnswer.clear(); - - m_uCorrect = 0; - m_uWrong = 0; - m_uErrorPoints = 0; - m_bPassed = false; -} - -void CExamStat::setQuestions(const QList& listQuestion, const QList& listAnswerMask) -{ - for (int i=0; iid()); - m_listAnswer.append(q->orderedAnswerMask(listAnswerMask.at(i))); - } -} - -void CExamStat::setResult (const unsigned uCorrect, const unsigned uWrong, const unsigned uErrorPoints, const bool bPassed) -{ - m_uCorrect = uCorrect; - m_uWrong = uWrong; - m_uErrorPoints = uErrorPoints; - m_bPassed = bPassed; -} - -bool CExamStat::load (QDomElement elem) -{ - if (elem.tagName() != QString ("exam")) return false; - - m_strId = elem.attribute("id"); - m_uSecs = elem.attribute("secs").toUInt(); - m_dt = QDateTime::fromString(elem.attribute("datetime"), Qt::ISODate); - m_uCorrect = elem.attribute("correct").toUInt(); - m_uWrong = elem.attribute("wrong").toUInt(); - m_uErrorPoints = elem.attribute("errorpoints").toUInt(); - m_bPassed = QVariant(elem.attribute("passed")).toBool(); - - if (m_strId.isEmpty()) return false; - if (m_uSecs == 0) return false; - - QDomNode n = elem.firstChild(); - while (!n.isNull()) - { - if (n.isElement ()) - { - QDomElement e = n.toElement (); - if (e.tagName() == QString ("question")) - { - QString strId = e.attribute("id"); - if (!strId.isEmpty()) - { - m_strlQuestionId.append (strId); - m_listAnswer.append(e.attribute("answer").toUInt()); - } - } - } - n = n.nextSibling(); - } - - return true; -} - -void CExamStat::save (QDomElement& parent, QDomDocument& doc) const -{ -QDomElement elemRoot = doc.createElement("exam"); - - elemRoot.setAttribute("id", m_strId); - elemRoot.setAttribute("secs", QString("%1").arg(m_uSecs)); - elemRoot.setAttribute("datetime", m_dt.toString(Qt::ISODate)); - elemRoot.setAttribute("correct", QString("%1").arg(m_uCorrect)); - elemRoot.setAttribute("wrong", QString("%1").arg(m_uWrong)); - elemRoot.setAttribute("errorpoints", QString("%1").arg(m_uErrorPoints)); - elemRoot.setAttribute("passed", QString("%1").arg(m_bPassed)); - for (int i=0; i +#include + +//#define DEBUGMSG + +void CExamPart::clear() +{ + m_iQuestionCount = 0; + m_strRegExp.clear(); +} + +bool CExamPart::load (QDomElement elem) +{ + if (elem.tagName() != QString ("exam_part")) return false; + m_iQuestionCount = elem.attribute("count").toUInt(); + if (m_iQuestionCount == 0) return false; + m_strGroup = elem.attribute("group"); + m_strRegExp = elem.text (); + if (m_strRegExp.isEmpty()) return false; + return true; +} + +void CExamPart::save (QDomElement& parent, QDomDocument& doc) const +{ +QDomElement elemRoot = doc.createElement("exam_part"); + + elemRoot.setAttribute("count", QString("%1").arg(m_iQuestionCount)); + elemRoot.setAttribute("group", m_strGroup); + elemRoot.appendChild(doc.createTextNode(m_strRegExp)); + + parent.appendChild(elemRoot); +} + +QList CExamPart::createQuestionPool(const QList& listAllQuestions) const +{ +QList listRet, listSelected; +QRegExp regexp(m_strRegExp); +int i=0; +CQuestion *q=0; + + #ifdef DEBUGMSG + qDebug ("\tPart Group = '%s' RegExp '%s' mit %i Fragen", qPrintable (m_strGroup), qPrintable(m_strRegExp), m_iQuestionCount); + #endif + + // Alle Fragen raussuchen, die dem RegExp entsprechen + for (i=0; igroups().contains(m_strGroup, Qt::CaseInsensitive)) + continue; + + if (regexp.exactMatch(q->id())) + { + #ifdef DEBUGMSG + qDebug ("\t\tAdding Question '%s'", qPrintable (q->id())); + #endif + listSelected.append(q); + } + } + + if (listSelected.size() < questionCount()) + { + throw CError ( + QString("Für den regulären Ausdruck '%1' wurden nur %2 Fragen gefunden - zu wenig, um daraus %3 Fragen für die Prüfung auszuwählen.") + .arg(m_strRegExp).arg(listSelected.size()).arg(questionCount()), __PRETTY_FUNCTION__, __FILE__, __LINE__); + } + + #ifdef DEBUGMSG + qDebug ("\tStarte Auswahlverfahren..."); + #endif + for (i=0; i < questionCount(); i++) + { + int rnd = afu_random (0, listSelected.size()-1); + q = listSelected.at(rnd); + #ifdef DEBUGMSG + qDebug ("\t\tSelected question '%s'", qPrintable (q->id())); + #endif + listRet.append (q); + listSelected.removeAt(rnd); + } + + return listRet; +} + + + +void CExam::clear() +{ + m_strId.clear(); + m_strName.clear(); + m_strComment.clear(); + m_uDuration = 0; + m_uMaxErrorPoints = 0; + m_listParts.clear(); +} + +bool CExam::load (QDomElement elem) +{ + if (elem.tagName() != QString ("exam")) return false; + + m_strId = elem.attribute("id"); + m_strName = elem.attribute("name"); + m_uDuration = elem.attribute("duration").toUInt(); + m_uMaxErrorPoints = elem.attribute("maxerrorpoints").toUInt(); + m_strComment = elem.attribute("comment"); + + if (m_strName.isEmpty()) return false; + if (m_uDuration == 0) return false; + + QDomNode n = elem.firstChild(); + while (!n.isNull()) + { + if (n.isElement ()) + { + QDomElement e = n.toElement (); + if (e.tagName() == QString ("exam_part")) + { + CExamPart ep; + if (ep.load(e)) m_listParts.append(ep); + } + } + n = n.nextSibling(); + } + + return true; +} + +void CExam::save (QDomElement& parent, QDomDocument& doc) const +{ +QDomElement elemRoot = doc.createElement("exam"); + + elemRoot.setAttribute("id", m_strId); + elemRoot.setAttribute("name", m_strName); + elemRoot.setAttribute("duration", QString("%1").arg(m_uDuration)); + elemRoot.setAttribute("maxerrorpoints", QString("%1").arg(m_uMaxErrorPoints)); + elemRoot.setAttribute("comment", m_strComment); + for (int i=0; i CExam::createQuestionPool(const QList& listAllQuestions) const +{ +QList listRet; + + #ifdef DEBUGMSG + qDebug ("Erzeuge Prüfungsfragen für '%s'", qPrintable(name())); + #endif + + try + { + for (int i=0; iid())); + #endif + + return listRet; +} + +CExamStat::CExamStat(const CExam& exam) +{ + clear(); + m_strId = exam.id(); +} + +void CExamStat::clear() +{ + m_strId.clear(); + m_dt = QDateTime::currentDateTime(); + m_uSecs = 0; + + m_strlQuestionId.clear(); + m_listAnswer.clear(); + + m_uCorrect = 0; + m_uWrong = 0; + m_uErrorPoints = 0; + m_bPassed = false; +} + +void CExamStat::setQuestions(const QList& listQuestion, const QList& listAnswerMask) +{ + for (int i=0; iid()); + m_listAnswer.append(q->orderedAnswerMask(listAnswerMask.at(i))); + } +} + +void CExamStat::setResult (const unsigned uCorrect, const unsigned uWrong, const unsigned uErrorPoints, const bool bPassed) +{ + m_uCorrect = uCorrect; + m_uWrong = uWrong; + m_uErrorPoints = uErrorPoints; + m_bPassed = bPassed; +} + +bool CExamStat::load (QDomElement elem) +{ + if (elem.tagName() != QString ("exam")) return false; + + m_strId = elem.attribute("id"); + m_uSecs = elem.attribute("secs").toUInt(); + m_dt = QDateTime::fromString(elem.attribute("datetime"), Qt::ISODate); + m_uCorrect = elem.attribute("correct").toUInt(); + m_uWrong = elem.attribute("wrong").toUInt(); + m_uErrorPoints = elem.attribute("errorpoints").toUInt(); + m_bPassed = QVariant(elem.attribute("passed")).toBool(); + + if (m_strId.isEmpty()) return false; + if (m_uSecs == 0) return false; + + QDomNode n = elem.firstChild(); + while (!n.isNull()) + { + if (n.isElement ()) + { + QDomElement e = n.toElement (); + if (e.tagName() == QString ("question")) + { + QString strId = e.attribute("id"); + if (!strId.isEmpty()) + { + m_strlQuestionId.append (strId); + m_listAnswer.append(e.attribute("answer").toUInt()); + } + } + } + n = n.nextSibling(); + } + + return true; +} + +void CExamStat::save (QDomElement& parent, QDomDocument& doc) const +{ +QDomElement elemRoot = doc.createElement("exam"); + + elemRoot.setAttribute("id", m_strId); + elemRoot.setAttribute("secs", QString("%1").arg(m_uSecs)); + elemRoot.setAttribute("datetime", m_dt.toString(Qt::ISODate)); + elemRoot.setAttribute("correct", QString("%1").arg(m_uCorrect)); + elemRoot.setAttribute("wrong", QString("%1").arg(m_uWrong)); + elemRoot.setAttribute("errorpoints", QString("%1").arg(m_uErrorPoints)); + elemRoot.setAttribute("passed", QString("%1").arg(m_bPassed)); + for (int i=0; i -#include -#include -#include -#include - -class CQuestion; - -class CExamPart -{ -public: - CExamPart() { clear(); } - ~CExamPart() {} - - void clear(); - - inline int questionCount() const { return m_iQuestionCount; } - inline void setQuestionCount(int i) { m_iQuestionCount = i; } - - inline QString group() const { return m_strGroup; } - inline void setGroup(const QString& str) { m_strGroup = str; } - - inline QString regexp() const { return m_strRegExp; } - inline void setRegExp(const QString& str) { m_strRegExp = str; } - - bool load (QDomElement elem); - void save (QDomElement& parent, QDomDocument& doc) const; - - QList createQuestionPool(const QList& listAllQuestions) const; - -protected: - int m_iQuestionCount; - QString m_strGroup; - QString m_strRegExp; -}; - -class CExam -{ -public: - CExam() { clear(); } - ~CExam() {} - - //! Zurücksetzen aller Werte - /*! Es werden alle Daten der Prüfung gelöscht. */ - void clear(); - - //! ID abfragen - inline QString id() const { return m_strId; } - //! ID setzen - inline void setId(const QString& strId) { m_strId = strId; } - - inline QString name() const { return m_strName; } - inline QString comment() const { return m_strComment; } - unsigned duration() const { return m_uDuration; } - unsigned maxErrorPoints() const { return m_uMaxErrorPoints; } - - unsigned questionCount() const; - - bool load (QDomElement elem); - void save (QDomElement& parent, QDomDocument& doc) const; - - QList createQuestionPool(const QList& listAllQuestions) const; - -protected: - QString m_strId; //< ID der Prüfung - QString m_strName; //< Name / Bezeichnung - QString m_strComment; //< Kommentar / Hinweis - unsigned m_uDuration; //< Prüfungsdauer in min - unsigned m_uMaxErrorPoints; //< Max. Anzahl an erlaubten Fehlerpunkten, um die Prüfung zu bestehen - QList m_listParts; //< Abschnitte, aus denen sich die Prüfung zusammensetzt. -}; - -class CExamStat -{ -public: - CExamStat() { clear(); } - CExamStat(const CExam& exam); - ~CExamStat() {} - - //! Zurücksetzen aller Werte - /*! Es werden alle Daten dieser Prüfungsstatistik gelöscht. */ - void clear(); - - bool load (QDomElement elem); - void save (QDomElement& parent, QDomDocument& doc) const; - - //! ID abfragen - inline QString id() const { return m_strId; } - //! ID setzen - inline void setId(const QString& strId) { m_strId = strId; } - - inline void setSecs(const unsigned uSecs) { m_uSecs = uSecs; } - void setQuestions(const QList& listQuestion, const QList& listAnswerMask); - void setResult (const unsigned uCorrect, const unsigned uWrong, const unsigned uErrorPoints, const bool bPassed); - - inline unsigned duration() const { return m_uSecs; } - inline bool passed() const { return m_bPassed; } - inline unsigned correctAnswers() const { return m_uCorrect; } - inline unsigned wrongAnswers() const { return m_uWrong; } - inline unsigned errorPoints() const { return m_uErrorPoints; } - QDateTime datetime() const { return m_dt; } - -protected: - QString m_strId; //< ID der Prüfung - QDateTime m_dt; //< Datum/Uhrzeit - unsigned m_uSecs; //< Benötigte Zeit in sec - - QStringList m_strlQuestionId; //< IDs der Fragen - QList m_listAnswer; //< Antworten - - unsigned m_uCorrect; //< Anzahl d. richtigen Fragen - unsigned m_uWrong; //< Anzahl d. falschen Fragen - unsigned m_uErrorPoints; //< Fehlerpunkte - bool m_bPassed; //< Bestanden Ja/Nein -}; - -#endif - +/*************************************************************************** + * 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. * + ***************************************************************************/ + +#ifndef EXAM_H +#define EXAM_H + +#include +#include +#include +#include +#include + +class CQuestion; + +class CExamPart +{ +public: + CExamPart() { clear(); } + ~CExamPart() {} + + void clear(); + + inline int questionCount() const { return m_iQuestionCount; } + inline void setQuestionCount(int i) { m_iQuestionCount = i; } + + inline QString group() const { return m_strGroup; } + inline void setGroup(const QString& str) { m_strGroup = str; } + + inline QString regexp() const { return m_strRegExp; } + inline void setRegExp(const QString& str) { m_strRegExp = str; } + + bool load (QDomElement elem); + void save (QDomElement& parent, QDomDocument& doc) const; + + QList createQuestionPool(const QList& listAllQuestions) const; + +protected: + int m_iQuestionCount; + QString m_strGroup; + QString m_strRegExp; +}; + +class CExam +{ +public: + CExam() { clear(); } + ~CExam() {} + + //! Zurücksetzen aller Werte + /*! Es werden alle Daten der Prüfung gelöscht. */ + void clear(); + + //! ID abfragen + inline QString id() const { return m_strId; } + //! ID setzen + inline void setId(const QString& strId) { m_strId = strId; } + + inline QString name() const { return m_strName; } + inline QString comment() const { return m_strComment; } + unsigned duration() const { return m_uDuration; } + unsigned maxErrorPoints() const { return m_uMaxErrorPoints; } + + unsigned questionCount() const; + + bool load (QDomElement elem); + void save (QDomElement& parent, QDomDocument& doc) const; + + QList createQuestionPool(const QList& listAllQuestions) const; + +protected: + QString m_strId; //< ID der Prüfung + QString m_strName; //< Name / Bezeichnung + QString m_strComment; //< Kommentar / Hinweis + unsigned m_uDuration; //< Prüfungsdauer in min + unsigned m_uMaxErrorPoints; //< Max. Anzahl an erlaubten Fehlerpunkten, um die Prüfung zu bestehen + QList m_listParts; //< Abschnitte, aus denen sich die Prüfung zusammensetzt. +}; + +class CExamStat +{ +public: + CExamStat() { clear(); } + CExamStat(const CExam& exam); + ~CExamStat() {} + + //! Zurücksetzen aller Werte + /*! Es werden alle Daten dieser Prüfungsstatistik gelöscht. */ + void clear(); + + bool load (QDomElement elem); + void save (QDomElement& parent, QDomDocument& doc) const; + + //! ID abfragen + inline QString id() const { return m_strId; } + //! ID setzen + inline void setId(const QString& strId) { m_strId = strId; } + + inline void setSecs(const unsigned uSecs) { m_uSecs = uSecs; } + void setQuestions(const QList& listQuestion, const QList& listAnswerMask); + void setResult (const unsigned uCorrect, const unsigned uWrong, const unsigned uErrorPoints, const bool bPassed); + + inline unsigned duration() const { return m_uSecs; } + inline bool passed() const { return m_bPassed; } + inline unsigned correctAnswers() const { return m_uCorrect; } + inline unsigned wrongAnswers() const { return m_uWrong; } + inline unsigned errorPoints() const { return m_uErrorPoints; } + QDateTime datetime() const { return m_dt; } + +protected: + QString m_strId; //< ID der Prüfung + QDateTime m_dt; //< Datum/Uhrzeit + unsigned m_uSecs; //< Benötigte Zeit in sec + + QStringList m_strlQuestionId; //< IDs der Fragen + QList m_listAnswer; //< Antworten + + unsigned m_uCorrect; //< Anzahl d. richtigen Fragen + unsigned m_uWrong; //< Anzahl d. falschen Fragen + unsigned m_uErrorPoints; //< Fehlerpunkte + bool m_bPassed; //< Bestanden Ja/Nein +}; + +#endif + diff --git a/helper.cpp b/helper.cpp index 0885b02..a87b3eb 100644 --- a/helper.cpp +++ b/helper.cpp @@ -1,84 +1,84 @@ -/*************************************************************************** - * 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 "helper.h" -#include "catalog.h" - -void CHint::clear() -{ - m_strlQuestionId.clear(); - m_strAuthor.clear(); - m_date = QDate(); - m_strText.clear(); - m_strComment.clear(); -} - -bool CHint::load (QDomElement elem) -{ -QString str; - - if (elem.tagName() != QString ("hint")) return false; - m_strAuthor = elem.attribute ("author", "AFUTrainer-Hilfe"); - m_date = QDate::fromString(elem.attribute ("date"), Qt::ISODate); - str = elem.attribute("question"); - str = str.replace(' ', ';').replace(',', ';'); - m_strlQuestionId = str.split(";", QString::SkipEmptyParts); - m_strComment = elem.attribute ("comment"); - m_strText = elem.text (); - - if (m_strText.isEmpty()) return false; - return true; -} - -void CHint::save (QDomElement& parent, QDomDocument& doc) -{ -QDomElement elemRoot = doc.createElement("hint"); - if (!m_strAuthor.isEmpty()) elemRoot.setAttribute("author", m_strAuthor); - if (m_date.isValid()) elemRoot.setAttribute("date", m_date.toString(Qt::ISODate)); - elemRoot.setAttribute("question", m_strlQuestionId.join(";")); - if (!m_strComment.isEmpty()) elemRoot.setAttribute("comment", m_strComment); - parent.appendChild(elemRoot); - elemRoot.appendChild(doc.createTextNode(m_strText)); -} - -QString CHint::showText() const -{ -QString str; - -/* if (text().isEmpty()) - { - if (pCatalog) - return pCatalog->hintText(id()); - else - return QString(); - } -*/ -/* str = "

"; - str += ""; - str += ""; - str += ""; - str += ""; - str += "
"+author()+""+dateString()+"
"+text()+"
 

"; -*/ - - str += "

" + text() + "

"; //"

" + author() + " " + date() + "

"; - return (str); -} +/*************************************************************************** + * 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 "helper.h" +#include "catalog.h" + +void CHint::clear() +{ + m_strlQuestionId.clear(); + m_strAuthor.clear(); + m_date = QDate(); + m_strText.clear(); + m_strComment.clear(); +} + +bool CHint::load (QDomElement elem) +{ +QString str; + + if (elem.tagName() != QString ("hint")) return false; + m_strAuthor = elem.attribute ("author", "AFUTrainer-Hilfe"); + m_date = QDate::fromString(elem.attribute ("date"), Qt::ISODate); + str = elem.attribute("question"); + str = str.replace(' ', ';').replace(',', ';'); + m_strlQuestionId = str.split(";", QString::SkipEmptyParts); + m_strComment = elem.attribute ("comment"); + m_strText = elem.text (); + + if (m_strText.isEmpty()) return false; + return true; +} + +void CHint::save (QDomElement& parent, QDomDocument& doc) +{ +QDomElement elemRoot = doc.createElement("hint"); + if (!m_strAuthor.isEmpty()) elemRoot.setAttribute("author", m_strAuthor); + if (m_date.isValid()) elemRoot.setAttribute("date", m_date.toString(Qt::ISODate)); + elemRoot.setAttribute("question", m_strlQuestionId.join(";")); + if (!m_strComment.isEmpty()) elemRoot.setAttribute("comment", m_strComment); + parent.appendChild(elemRoot); + elemRoot.appendChild(doc.createTextNode(m_strText)); +} + +QString CHint::showText() const +{ +QString str; + +/* if (text().isEmpty()) + { + if (pCatalog) + return pCatalog->hintText(id()); + else + return QString(); + } +*/ +/* str = "

"; + str += ""; + str += ""; + str += ""; + str += ""; + str += "
"+author()+""+dateString()+"
"+text()+"
 

"; +*/ + + str += "

" + text() + "

"; //"

" + author() + " " + date() + "

"; + return (str); +} diff --git a/helper.h b/helper.h index 0e66872..d2ece47 100644 --- a/helper.h +++ b/helper.h @@ -1,71 +1,71 @@ -/*************************************************************************** - * 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. * - ***************************************************************************/ - -#ifndef HELPER_H -#define HELPER_H - -#include -#include -#include -#include - -class CCatalog; - -class CHint -{ -public: - CHint() { clear(); } - ~CHint() {} - - void clear(); - - inline void appendQuestion(const QString& id) { if (!hasQuestion(id)) m_strlQuestionId.append(id); } - inline void removeQuestion(const QString& id) { m_strlQuestionId.removeAll(id); } - inline bool hasQuestion(const QString& id) const { return m_strlQuestionId.contains(id); } - - inline QString author() const { return m_strAuthor; } - inline void setAuthor(const QString& strAuthor) { m_strAuthor = strAuthor; } - - inline QDate date() const { return m_date; } - inline QString dateString() const { return m_date.toString(Qt::LocalDate); } - inline void setDate (const QDate& date) { m_date = date; } - - inline QString text() const { return m_strText; } - inline void setText(const QString& strText) { m_strText = strText; } - - inline QString comment() const { return m_strComment; } - inline void setComment (const QString& strComment) { m_strComment = strComment; } - - - QString showText() const; - - bool load (QDomElement elem); - void save (QDomElement& parent, QDomDocument& doc); - -protected: - QStringList m_strlQuestionId; - QString m_strAuthor; - QDate m_date; - QString m_strText; - QString m_strComment; -}; - -#endif +/*************************************************************************** + * 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. * + ***************************************************************************/ + +#ifndef HELPER_H +#define HELPER_H + +#include +#include +#include +#include + +class CCatalog; + +class CHint +{ +public: + CHint() { clear(); } + ~CHint() {} + + void clear(); + + inline void appendQuestion(const QString& id) { if (!hasQuestion(id)) m_strlQuestionId.append(id); } + inline void removeQuestion(const QString& id) { m_strlQuestionId.removeAll(id); } + inline bool hasQuestion(const QString& id) const { return m_strlQuestionId.contains(id); } + + inline QString author() const { return m_strAuthor; } + inline void setAuthor(const QString& strAuthor) { m_strAuthor = strAuthor; } + + inline QDate date() const { return m_date; } + inline QString dateString() const { return m_date.toString(Qt::LocalDate); } + inline void setDate (const QDate& date) { m_date = date; } + + inline QString text() const { return m_strText; } + inline void setText(const QString& strText) { m_strText = strText; } + + inline QString comment() const { return m_strComment; } + inline void setComment (const QString& strComment) { m_strComment = strComment; } + + + QString showText() const; + + bool load (QDomElement elem); + void save (QDomElement& parent, QDomDocument& doc); + +protected: + QStringList m_strlQuestionId; + QString m_strAuthor; + QDate m_date; + QString m_strText; + QString m_strComment; +}; + +#endif diff --git a/main.cpp b/main.cpp index 3627faa..e3fe576 100644 --- a/main.cpp +++ b/main.cpp @@ -1,42 +1,42 @@ -/*************************************************************************** - * 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 "mainwindow.h" -#include -#include -#include - -int main (int argc, char *argv[]) -{ -QApplication app (argc, argv); -QTranslator translator; - app.setOrganizationName ("osaal"); - app.setApplicationName ("AFUTrainer"); - - translator.load(":/translations/qt_de"); - app.installTranslator(&translator); - - srand (time (NULL)); - - CMainWindow mw; - mw.show(); - return app.exec(); -} +/*************************************************************************** + * 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 "mainwindow.h" +#include +#include +#include + +int main (int argc, char *argv[]) +{ +QApplication app (argc, argv); +QTranslator translator; + app.setOrganizationName ("osaal"); + app.setApplicationName ("AFUTrainer"); + + translator.load(":/translations/qt_de"); + app.installTranslator(&translator); + + srand (time (NULL)); + + CMainWindow mw; + mw.show(); + return app.exec(); +} diff --git a/mainwindow.cpp b/mainwindow.cpp index 4a60587..fcf44ea 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -1,819 +1,819 @@ -/*************************************************************************** - * 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 "mainwindow.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "dlglearn.h" -#include "dlglearnassistant.h" -#include "dlginformation.h" -#include "dlgexamselect.h" -#include "dlgexam.h" -#include "dlgexamstatistic.h" -#include "dlglearnstatistic.h" -#include "catalog.h" - - -CMainWindow::CMainWindow() : QMainWindow() -{ - setupUi(this); - m_pSplitter = new QSplitter (this); - setCentralWidget (m_pSplitter); - m_pViewChapter = new QTreeView (m_pSplitter); - m_pViewChapter->setModel(&m_modelCatalog); - m_pViewChapter->header()->setStretchLastSection(false); - m_pViewChapter->header()->setResizeMode (0, QHeaderView::Stretch); - m_pViewChapter->header()->setResizeMode (1, QHeaderView::Interactive); - m_pViewChapter->header()->setResizeMode (3, QHeaderView::Interactive); - m_pViewChapter->header()->resizeSection(1, 45); - m_pViewChapter->header()->resizeSection(2, 150); - m_pViewChapter->header()->resizeSection(3, 20); - //m_pViewChapter->setSelectionBehavior(QAbstractItemView::SelectRows); - connect (m_pViewChapter->selectionModel(), SIGNAL(selectionChanged (const QItemSelection& , const QItemSelection& )), this, SLOT(onCatalogSelectionChanged (const QItemSelection& , const QItemSelection& ))); - m_pSplitter2 = new QSplitter (m_pSplitter); - m_pSplitter2->setOrientation(Qt::Vertical); - - m_pViewQuestions = new QTreeView (m_pSplitter2); - m_pViewQuestions->setModel(&m_modelQuestion); - m_pViewQuestions->header()->setStretchLastSection(false); - m_pViewQuestions->header()->setResizeMode (0, QHeaderView::Interactive); - m_pViewQuestions->header()->setResizeMode (1, QHeaderView::Stretch); - m_pViewQuestions->header()->resizeSection(0, 60); - m_pViewQuestions->header()->resizeSection(2, 20); - m_pViewQuestions->header()->resizeSection(3, 20); - m_pViewQuestions->header()->resizeSection(4, 100); - m_pViewQuestions->header()->resizeSection(5, 80); - m_pViewQuestions->header()->resizeSection(6, 80); - m_pViewQuestions->setRootIsDecorated(false); - connect (m_pViewQuestions->selectionModel(), SIGNAL(selectionChanged (const QItemSelection& , const QItemSelection& )), this, SLOT(onQuestionSelectionChanged (const QItemSelection& , const QItemSelection& ))); - - m_pTextQuestion = new QTextBrowser (m_pSplitter2); - m_pTextQuestion->setOpenExternalLinks(true); - //m_pTextQuestion->setReadOnly(true); - //connect (m_pTextQuestion, SIGNAL(anchorClicked (const QUrl &)), this, SLOT(onAnchorClicked (const QUrl &))); - - m_pCatalog = 0; - - connect (&m_rf, SIGNAL(loadFile(const QString&)), this, SLOT(onOpenFile(const QString&))); - m_rf.create(QString(), 8); - m_rf.insertToMenu(menuFile, actFileExit); - m_rf.setShowNoEntry(false); - m_rf.setShowSeperator(CRecentFiles::SeperatorBottom); - - // open last file - onOpenFile(m_rf.recentFile(0)); - -#ifndef _DEBUG - actFileSave->setVisible(false); - actFileSaveAs->setVisible(false); - actFileImport->setVisible(false); - actFileImportUS->setVisible(false); -#endif - - updateWindowTitle(); -} - -CMainWindow::~CMainWindow() -{ - if (m_pCatalog) delete m_pCatalog; -} - -void CMainWindow::updateWindowTitle() -{ -QString str; - str = tr("AFUTrainer 3.0"); - if (m_pCatalog) - { - str += " - "; - if (m_pCatalog->name().isEmpty()) - str += tr("unbenannt"); - else - str += m_pCatalog->name(); - - if (!m_pCatalog->fileName().isEmpty()) - str += " [" + m_pCatalog->fileName() + "]"; - } - setWindowTitle(str); -} - -void CMainWindow::setCatalog (CCatalog *pCatalog) -{ - m_modelCatalog.setModelData(pCatalog); - m_pViewChapter->expandAll(); - if (m_pCatalog) delete m_pCatalog; - m_pCatalog = pCatalog; - m_modelQuestion.setModelData(0); - m_pTextQuestion->clear(); - updateWindowTitle(); - - if (m_pCatalog == 0) return; - - QDate d = m_pCatalog->validUntil(); - if (d.isValid() && d < QDate::currentDate()) - { - QString str = tr("Dieser Fragenkatalog ist seit dem %1 nicht mehr gültig!").arg(d.toString(Qt::LocalDate)); - if (!m_pCatalog->publisher().isEmpty()) - str += tr("

Weitere Informationen zur Gültigkeit erhalten Sie vom Herausgeber:
")+m_pCatalog->publisher(); - if (!m_pCatalog->contact().isEmpty()) - str += tr("

Einen aktuellen Fragenkatalog für den AFUTrainer erhalten Sie evt. von:
")+m_pCatalog->contact(); - - QMessageBox::warning(this, tr("Warnung"), str); - } -} - -bool CMainWindow::checkForErrors() -{ -QString str; - - if (m_pCatalog == 0) return false; - str = m_pCatalog->checkForErrors(); - if (str.isEmpty()) return false; - QMessageBox::critical(this, tr("Fehler im Fragenkatalog"), tr("Die gewünschte Funktion kann nicht ausgeführt werden, da der Fragenkatalog folgende Fehler enthält:

")+ str); - return true; -} - -bool CMainWindow::checkForHomeDir() -{ -QString strDir = QDir::homePath() + "/.afutrainer/"; -QDir dir; - if (dir.exists(strDir)) return true; - if (!dir.mkpath(strDir)) - { - QMessageBox::critical(this, tr("Fehler"), tr("Konnte Verzeichnis %1 nicht anlegen!").arg(strDir)); - return false; - } - return true; -} - -void CMainWindow::on_actFileNew_triggered() -{ - if (!checkForHomeDir()) return; - setCatalog(0); -} - -void CMainWindow::on_actFileOpen_triggered() -{ -QString strFileName; - if (!checkForHomeDir()) return; - strFileName = QFileDialog::getOpenFileName(this, tr("Öffne Fragenkatalog"), QString(), tr("Amateur Radio Questionary Data Format (*.aqz)\nAlle Dateien (*.*)")); - onOpenFile(strFileName); -} - -void CMainWindow::onOpenFile(const QString& strFileName) -{ -CCatalog *pCatalog=0; - if (strFileName.isEmpty()) return; - pCatalog = new CCatalog; - if (pCatalog->load(strFileName, this)) - { - setCatalog (pCatalog); - m_rf.setRecentFile(strFileName); - } - else - { - m_rf.removeFile(strFileName); - delete pCatalog; - } -} - -void CMainWindow::on_actFileInformation_triggered() -{ -CDlgInformation dlg(this); - if (!m_pCatalog) - { - QMessageBox::information(this, tr("Information"), tr("Kein Fragenkatalog geladen, zu dem Informationen angezeigt werden könnten.")); - return; - } - dlg.setup(m_pCatalog); - dlg.exec(); -} - -void CMainWindow::on_actFileSave_triggered() -{ - if (!m_pCatalog) return; - if (m_pCatalog->fileName().isEmpty()) - on_actFileSaveAs_triggered(); - else - { - m_pCatalog->save(m_pCatalog->fileName(), this); - updateWindowTitle(); - } -} - -void CMainWindow::on_actFileSaveAs_triggered() -{ -QString strFileName; - if (!m_pCatalog) return; - strFileName = QFileDialog::getSaveFileName(this, tr("Speichere Fragenkatalog"), QString(), tr("XML-Dateien (*.xml)\nAlle Dateien (*.*)")); - if (strFileName.isEmpty()) return; - m_pCatalog->save(strFileName, this); - updateWindowTitle(); -} - -bool ImportDE_isChapter(const QString& strLine) -{ - if (strLine.contains(QRegExp ("^[0-9](\\.[0-9]+)*\\s\\w"))) - return true; - else - return false; -} - -int ImportDE_chapterLevel(const QString& strLine) -{ -QString str = strLine.left(strLine.indexOf(' ')); - return (str.count('.')); -} - -bool ImportDE_isQuestion(const QString& strLine) -{ - return strLine.contains(QRegExp("^[A-Z]{2,2}[0-9]{3,3}")); -} - -bool ImportDE_isAnswer(const QString& strLine) -{ - return strLine.contains(QRegExp("^[A-D]\\s")) || strLine.contains(QRegExp("^[A-D]$")); -} - -void CMainWindow::on_actFileImport_triggered() -{ -QString strFileName, strLine, strChapter; -CChapter *pChapter=0, *pTempChapter=0; -CQuestion *pQuestion=0; -int uCurrentLevel=-1, uLevel=-1; -enum LastAdded {None, Chapter, Question, Answer}; -LastAdded la=None; -CCatalog *pCatalog=0; - -strFileName = QFileDialog::getOpenFileName(this, tr("Öffne Datei zum Fragenkatalog-Import"), QString("D:/projekte/afutrainer/fragenkataloge/2007-02/Klasse A Technik/"), tr("TXT-Dateien (*.txt)\nAlle Dateien (*.*)")); - if (strFileName.isEmpty()) return; - - QFile file(strFileName); - if(!file.open(QIODevice::ReadOnly)) - { - QMessageBox::information(this, tr("Fehler"), tr("Konnte Datei '%1' nicht zum Lesen öffnen!").arg(strFileName)); - return; - } - QTextStream in(&file); - pCatalog = new CCatalog(); - - while (!in.atEnd()) - { - strLine = in.readLine().trimmed(); - if (strLine.isEmpty() || strLine.contains(QRegExp("^Prüfungsfragen")) || strLine.contains("^Bundesnetzagentur")) - { - la = None; - } - else if (ImportDE_isChapter(strLine)) // && (uCurrentLevel+1 == ImportDE_chapterLevel(strLine) || uCurrentLevel-1 == ImportDE_chapterLevel(strLine))) - { - uLevel = ImportDE_chapterLevel(strLine); - while (uCurrentLevel >= uLevel && pChapter) - { - pChapter = pChapter->parentChapter(); - uCurrentLevel--; - } - pTempChapter = new CChapter(); - if (pChapter == 0) - pCatalog->appendChapter(pTempChapter); - else - pChapter->appendChapter(pTempChapter); - pTempChapter->setText(strLine.mid(strLine.indexOf(' ') + 1)); - - pChapter = pTempChapter; - uCurrentLevel++; - strChapter += QString("%1 -- %2
").arg(uLevel).arg(strLine); - qDebug ("%i -- %s", uLevel, qPrintable(strLine)); - la = Chapter; - } - else if (ImportDE_isQuestion(strLine)) - { - pQuestion = new CQuestion(); - pQuestion->setId (strLine.left(5)); - pQuestion->setText (strLine.mid(6).trimmed()); - if (pChapter) - { - pChapter->appendQuestion(pQuestion); - - // update id of chapters - pTempChapter = pChapter; - uLevel = uCurrentLevel; - while (pTempChapter && uLevel >= 0) - { - if (pTempChapter->id().isEmpty()) - pTempChapter->setId(pQuestion->id().at(uLevel)); - pTempChapter = pTempChapter->parentChapter(); - uLevel--; - } - } - else - delete pQuestion; - //qDebug (qPrintable(strLine)); - la = Question; - } - else if (ImportDE_isAnswer(strLine)) - { - CAnswer a(strLine.mid(2).trimmed(), strLine[0] == QChar('A')); - if (pQuestion) - pQuestion->appendAnswer(a); - //qDebug (qPrintable("\t"+strLine)); - la = Answer; - } - else - { - if (la == Chapter && pChapter) - { - if (pChapter->text().isEmpty()) - pChapter->appendText (strLine.trimmed()); - else - pChapter->appendText (" " + strLine.trimmed()); - } - else if (la == Question && pQuestion) - { - if (pQuestion->text().isEmpty() || pQuestion->text().right(1) == "-") - pQuestion->appendText (strLine.trimmed()); - else - pQuestion->appendText (" " + strLine.trimmed()); - } - else if (la == Answer && pQuestion && pQuestion->countAnswer() > 0) - { - QString strText = pQuestion->answerAt(pQuestion->countAnswer()-1).text(); - if (strText.isEmpty() || strText.right(1) == "-") - pQuestion->answerAt(pQuestion->countAnswer()-1).appendText(strLine.trimmed()); - else - pQuestion->answerAt(pQuestion->countAnswer()-1).appendText(" " + strLine.trimmed()); - } - } - } - - // Bilder hinzufügen - QList listPool = pCatalog->questionPool(); - QDir dirPath(strFileName.left(strFileName.lastIndexOf('/'))); - for (int i=0; iid().toLower()+"*"); - for (int j=0; j"; - - if (strImgFile.contains(QRegExp("f\\d*\\."))) - { // Frage - qDebug("Adding image %s to question %s", qPrintable(strImgFile), qPrintable(pQuestion->id())); - pQuestion->appendText("

" + strLink + "

"); - } - else if (strImgFile.contains("a.") && pQuestion->countAnswer() > 0) - { // Antwort A - //qDebug("Adding image %s to answer A of question %s", qPrintable(strImgFile), qPrintable(pQuestion->id())); - pQuestion->answerAt(0).appendText("

" + strLink + "

"); - } - else if (strImgFile.contains("b.") && pQuestion->countAnswer() > 1) - { // Antwort B - //qDebug("Adding image %s to answer B of question %s", qPrintable(strImgFile), qPrintable(pQuestion->id())); - pQuestion->answerAt(1).appendText("

" + strLink + "

"); - } - else if (strImgFile.contains("c.") && pQuestion->countAnswer() > 2) - { // Antwort C - //qDebug("Adding image %s to answer C of question %s", qPrintable(strImgFile), qPrintable(pQuestion->id())); - pQuestion->answerAt(2).appendText("

" + strLink + "

"); - } - else if (strImgFile.contains("d.") && pQuestion->countAnswer() > 3) - { // Antwort D - //qDebug("Adding image %s to answer D of question %s", qPrintable(strImgFile), qPrintable(pQuestion->id())); - pQuestion->answerAt(3).appendText("

" + strLink + "

"); - } - else - { - qDebug ("Konnte Grafik %s nicht zuordnen.\n\tMögliche Gründe: Frage oder Antwort existiert nicht. Dateiname der Grafik falsch geschrieben.", qPrintable(strImgFile)); - } - } - } - - pCatalog->setName("Importierter Katalog von " + strFileName); - QMessageBox::information(this, tr("Information"), - "Bitte Debug-Ausgabe überprüfen, ob alle Grafik-Dateien zugeordnet wurden!

" - "Bitte überprüfen, ob alle Kapitel korrekt erkannt wurden. " - "Wenn nicht, muss die TXT-Datei von Hand kontrolliert werden!


"+strChapter); - - qDebug("Weitere Vorgehensweise:\n\t1. Fragenkatalog als XML-Datei abspeichern.\n\t2. ggf. Korrekturen in der XML-Datei vornehmen\n\t3. XML-Datei und alle Grafik-Dateien in ein ZIP-Archiv packen und die Dateiänderung auf .atc ändern.\n\tFertig!"); - setCatalog (pCatalog); -} - -bool ImportUS_isChapter(const QString& strLine) -{ - if (strLine.contains(QRegExp("^SUBELEMENT")) || strLine.contains(QRegExp("^[A-Z][0-9][A-Z]\\s"))) - return true; - else - return false; -} - -bool ImportUS_isQuestion(const QString& strLine) -{ - return strLine.contains(QRegExp("^[A-Z][0-9][A-Z][0-9]{2,2}")); -} - -bool ImportUS_isAnswer(const QString& strLine) -{ - return strLine.contains(QRegExp("^[A-D]\\.\\s")); -} - -void CMainWindow::on_actFileImportUS_triggered() -{ -QString strFileName, strLine, strChapter, str; -CChapter *pChapter=0, *pTempChapter=0; -CQuestion *pQuestion=0; -int uCurrentLevel=-1, uLevel=-1; -enum LastAdded {None, Chapter, Question, Answer}; -LastAdded la=None; -CCatalog *pCatalog=0; - -strFileName = QFileDialog::getOpenFileName(this, tr("Öffne Datei zum Fragenkatalog-Import"), QString("D:/projekte/afutrainer/fragenkataloge/US/"), tr("TXT-Dateien (*.txt)\nAlle Dateien (*.*)")); - if (strFileName.isEmpty()) return; - - QFile file(strFileName); - if(!file.open(QIODevice::ReadOnly)) - { - QMessageBox::information(this, tr("Fehler"), tr("Konnte Datei '%1' nicht zum Lesen öffnen!").arg(strFileName)); - return; - } - QTextStream in(&file); - pCatalog = new CCatalog(); - - while (!in.atEnd()) - { - strLine = in.readLine().trimmed(); - if (strLine.isEmpty()) - { - //la = None; - } - else if (strLine == "~~") - { - la = None; - } - else if (ImportUS_isChapter(strLine)) - { - pTempChapter = new CChapter(); - if (strLine.contains(QRegExp("^SUBELEMENT"))) - { - uLevel = 0; - strLine = strLine.mid(strLine.indexOf(' ')+1); - pTempChapter->setId(strLine.left(2)); -// str = strLine.mid(strLine.indexOf(QRegExp("[A-Z]"), 2)); -// str.remove(QRegExp("\\[.*\\]")); - pTempChapter->setText(strLine.mid(strLine.indexOf(QRegExp("[A-Z]"), 2))); - } - else - { - uLevel = 1; - pTempChapter->setId(strLine.mid(2, 1)); -// str = strLine.mid(strLine.indexOf(QRegExp("[A-Z]"), 3)); -// str.remove(QRegExp("\\[.*\\]")); - pTempChapter->setText(strLine.mid(strLine.indexOf(QRegExp("[A-Z]"), 3))); - } - while (uCurrentLevel >= uLevel && pChapter) - { - pChapter = pChapter->parentChapter(); - uCurrentLevel--; - } - if (!pChapter) - pCatalog->appendChapter(pTempChapter); - else - { -// pTempChapter->setExam("main", 1); - pChapter->appendChapter(pTempChapter); - } - - strChapter += QString("%1 -- %2 - %3
").arg(uLevel).arg(pTempChapter->id(), pTempChapter->text()); - pChapter = pTempChapter; - uCurrentLevel++; - la = Chapter; - } - else if (ImportUS_isQuestion(strLine)) - { - pQuestion = new CQuestion(); - pQuestion->setId (strLine.left(5)); - pQuestion->setText (strLine.mid(6).trimmed()); - if (pChapter) - pChapter->appendQuestion(pQuestion); - else - delete pQuestion; - //qDebug (qPrintable(strLine)); - la = Question; - } - else if (ImportUS_isAnswer(strLine)) - { - CAnswer a(strLine.mid(3).trimmed(), false); - if (pQuestion) - pQuestion->appendAnswer(a); - //qDebug (qPrintable("\t"+strLine)); - la = Answer; - } - else - { - if (la == Chapter && pChapter) - { - if (pChapter->text().isEmpty()) - pChapter->appendText (strLine.trimmed()); - else - pChapter->appendText (" " + strLine.trimmed()); - } - else if (la == Question && pQuestion) - { - if (pQuestion->text().isEmpty() || pQuestion->text().right(1) == "-") - pQuestion->appendText (strLine.trimmed()); - else - pQuestion->appendText (" " + strLine.trimmed()); - } - else if (la == Answer && pQuestion && pQuestion->countAnswer() > 0) - { - QString strText = pQuestion->answerAt(pQuestion->countAnswer()-1).text(); - if (strText.isEmpty() || strText.right(1) == "-") - pQuestion->answerAt(pQuestion->countAnswer()-1).appendText(strLine.trimmed()); - else - pQuestion->answerAt(pQuestion->countAnswer()-1).appendText(" " + strLine.trimmed()); - } - } - } - - // Fragen durchgehen und richtige Antworten eintragen - QList listPool = pCatalog->questionPool(); - for (int i=0; itext(); - int idx = strText.indexOf(QRegExp("\\([A-Z]\\)")); - if (idx == -1) continue; - int iCorrect = pQuestion->text().at(idx+1).toAscii() - 'A'; - if (pQuestion->countAnswer() > iCorrect) - pQuestion->answerAt(iCorrect).setCorrect(true); - strText.remove(idx, 3); - idx = strText.indexOf(QRegExp("\\[.*\\]")); - if (idx >= 0) - { - int iLen = strText.indexOf(']', idx) - idx + 1; - CHint hint; - hint.appendQuestion(pQuestion->id()); - hint.setAuthor("FCC"); - hint.setText("See FCC rules part " + strText.mid(idx + 1, iLen - 2)); - pCatalog->appendHint(hint); - strText.remove(idx, iLen); - } - pQuestion->setText(strText.trimmed()); - } - - // Kapitelnamen korrigieren - QList listChapter = pCatalog->subChapters(); - for (int i=0; itext(); - //strText = strText.left(strText.indexOf(" - ")); - strText.remove(QRegExp("\\[.*\\]")); - pChapter->setText(strText.trimmed()); - } - - // Allgemeine Angaben: - //pCatalog->setVersionText(""); - pCatalog->setPublisher("NCVEC (National Conference of Volunteer Examiner Coordinators)
Question Pool Committee
http://www.ncvec.org/"); - pCatalog->setContact("Oliver Saal, DM1OLI
http://www.oliver-saal.de/software/afutrainer/
Mail: osaal@gmx.de"); - - pCatalog->setName("Importierter Katalog von " + strFileName); - QMessageBox::information(this, tr("Information"), - "Bitte überprüfen, ob alle Kapitel korrekt erkannt wurden. " - "Wenn nicht, muss die TXT-Datei von Hand kontrolliert werden!
"+strChapter); - - qDebug("Weitere Vorgehensweise:\n\t1. Fragenkatalog als XML-Datei abspeichern.\n\t2. ggf. Korrekturen in der XML-Datei vornehmen\n\t3. XML-Datei und alle Grafik-Dateien in ein ZIP-Archiv packen und die Dateiänderung auf .atc ändern.\n\tFertig!"); - setCatalog (pCatalog); -} - -void CMainWindow::on_actFileExit_triggered() -{ - qApp->quit(); -} - -void CMainWindow::on_actQuestionAssistant_triggered() -{ -CDlgLearnAssistant dlg(this); -CDlgLearn dlgLearn(this); -CChapter *pChapter=0; - - if (m_pCatalog == 0) return; - if (checkForErrors()) return; - 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; - - dlgLearn.go(m_pCatalog, pChapter); - m_pCatalog->saveStatistic(this); -} - -void CMainWindow::on_actQuestionsLearn_triggered() -{ -CDlgLearn dlg(this); -QModelIndexList list = m_pViewChapter->selectionModel()->selectedIndexes(); - - if (checkForErrors()) return; - if (list.isEmpty()) - { - QMessageBox::information(this, tr("Information"), tr("Bitte ein Kapitel zum Lernen auswählen!")); - return; - } - CChapter *p = (CChapter*)list.first().internalPointer(); - Q_ASSERT(p != 0); - dlg.go(m_pCatalog, p); - m_pCatalog->saveStatistic(this); -} - -void CMainWindow::on_actQuestionsLearnStatistics_triggered() -{ -CDlgLearnStatistic dlg(this); -QModelIndexList list = m_pViewChapter->selectionModel()->selectedIndexes(); -CChapter *p=0; - - if (checkForErrors()) return; - if (list.isEmpty()) - p = m_pCatalog; - else - p = (CChapter*)list.first().internalPointer(); - - dlg.go(p); -} - -void CMainWindow::on_actQuestionsTest_triggered() -{ -int iCount=0; -CExam exam; - - if (checkForErrors()) return; - iCount = m_pCatalog->countExam(); - if (iCount == 0) - { - QMessageBox::information(this, tr("Information"), tr("Dieser Fragenkatalog enthält keine Prüfungen.")); - return; - } - else if (iCount == 1) - { - exam = m_pCatalog->examAt(0); - } - else if (iCount > 1) - { - CDlgExamSelect dlg1(this); - dlg1.setup(m_pCatalog); - dlg1.exec(); - if (dlg1.result() == QDialog::Rejected) return; - exam = m_pCatalog->examAt(dlg1.selectedExam()); - } - - CDlgExam dlg2(m_pCatalog, this); - if (dlg2.setup(exam)) - dlg2.exec(); -} - -void CMainWindow::on_actQuestionsTestStatistics_triggered() -{ -CDlgExamStatistic dlg(this); - if (checkForErrors()) return; - if (m_pCatalog->countExam() == 0) - { - QMessageBox::information(this, tr("Information"), tr("Dieser Fragenkatalog enthält keine Prüfungen.")); - return; - } - dlg.go(m_pCatalog); -} - -void CMainWindow::on_actViewToolbar_toggled(bool bChecked) -{ - if (bChecked) - toolBar->show(); - else - toolBar->hide(); -} - -void CMainWindow::on_actViewStatusbar_toggled(bool bChecked) -{ - if (bChecked) - statusbar->show(); - else - statusbar->hide(); -} - -void CMainWindow::on_actHelpWhatsThis_triggered() -{ - QWhatsThis::enterWhatsThisMode(); -} - -void CMainWindow::on_actHelpAbout_triggered() -{ -QString str = tr("AFUTrainer Version 3.0
" - "(c) 2003-2007 by Oliver Saal (DM1OLI)

" - "EMail: 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."); - QMessageBox::about(this,tr("Über..."), str); -} - -void CMainWindow::on_actHelpAboutQt_triggered() -{ - qApp->aboutQt(); -} - -void CMainWindow::onCatalogSelectionChanged (const QItemSelection& selected, const QItemSelection& deselected) -{ - Q_UNUSED(deselected); - m_pTextQuestion->clear(); - if (!selected.indexes().isEmpty()) - { - CChapter *pChapter = (CChapter*)selected.indexes().first().internalPointer(); - if (pChapter) - m_modelQuestion.setModelData(pChapter); - } - else - m_modelQuestion.clear(); -} - -void CMainWindow::onQuestionSelectionChanged (const QItemSelection& selected, const QItemSelection& deselected) -{ - Q_UNUSED(deselected); - m_pTextQuestion->clear(); - if (selected.indexes().isEmpty()) return; - CQuestion *pQuestion = (CQuestion*)selected.indexes().first().internalPointer(); - if (pQuestion == 0) return; - m_pTextQuestion->setHtml(pQuestion->showText(m_pCatalog)); -} -/* -void CMainWindow::onAnchorClicked (const QUrl &link) -{ - QMessageBox::information(this, tr("Click"), link.toString()); - m_pTextQuestion->home(); -} -*/ +/*************************************************************************** + * 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 "mainwindow.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dlglearn.h" +#include "dlglearnassistant.h" +#include "dlginformation.h" +#include "dlgexamselect.h" +#include "dlgexam.h" +#include "dlgexamstatistic.h" +#include "dlglearnstatistic.h" +#include "catalog.h" + + +CMainWindow::CMainWindow() : QMainWindow() +{ + setupUi(this); + m_pSplitter = new QSplitter (this); + setCentralWidget (m_pSplitter); + m_pViewChapter = new QTreeView (m_pSplitter); + m_pViewChapter->setModel(&m_modelCatalog); + m_pViewChapter->header()->setStretchLastSection(false); + m_pViewChapter->header()->setResizeMode (0, QHeaderView::Stretch); + m_pViewChapter->header()->setResizeMode (1, QHeaderView::Interactive); + m_pViewChapter->header()->setResizeMode (3, QHeaderView::Interactive); + m_pViewChapter->header()->resizeSection(1, 45); + m_pViewChapter->header()->resizeSection(2, 150); + m_pViewChapter->header()->resizeSection(3, 20); + //m_pViewChapter->setSelectionBehavior(QAbstractItemView::SelectRows); + connect (m_pViewChapter->selectionModel(), SIGNAL(selectionChanged (const QItemSelection& , const QItemSelection& )), this, SLOT(onCatalogSelectionChanged (const QItemSelection& , const QItemSelection& ))); + m_pSplitter2 = new QSplitter (m_pSplitter); + m_pSplitter2->setOrientation(Qt::Vertical); + + m_pViewQuestions = new QTreeView (m_pSplitter2); + m_pViewQuestions->setModel(&m_modelQuestion); + m_pViewQuestions->header()->setStretchLastSection(false); + m_pViewQuestions->header()->setResizeMode (0, QHeaderView::Interactive); + m_pViewQuestions->header()->setResizeMode (1, QHeaderView::Stretch); + m_pViewQuestions->header()->resizeSection(0, 60); + m_pViewQuestions->header()->resizeSection(2, 20); + m_pViewQuestions->header()->resizeSection(3, 20); + m_pViewQuestions->header()->resizeSection(4, 100); + m_pViewQuestions->header()->resizeSection(5, 80); + m_pViewQuestions->header()->resizeSection(6, 80); + m_pViewQuestions->setRootIsDecorated(false); + connect (m_pViewQuestions->selectionModel(), SIGNAL(selectionChanged (const QItemSelection& , const QItemSelection& )), this, SLOT(onQuestionSelectionChanged (const QItemSelection& , const QItemSelection& ))); + + m_pTextQuestion = new QTextBrowser (m_pSplitter2); + m_pTextQuestion->setOpenExternalLinks(true); + //m_pTextQuestion->setReadOnly(true); + //connect (m_pTextQuestion, SIGNAL(anchorClicked (const QUrl &)), this, SLOT(onAnchorClicked (const QUrl &))); + + m_pCatalog = 0; + + connect (&m_rf, SIGNAL(loadFile(const QString&)), this, SLOT(onOpenFile(const QString&))); + m_rf.create(QString(), 8); + m_rf.insertToMenu(menuFile, actFileExit); + m_rf.setShowNoEntry(false); + m_rf.setShowSeperator(CRecentFiles::SeperatorBottom); + + // open last file + onOpenFile(m_rf.recentFile(0)); + +#ifndef _DEBUG + actFileSave->setVisible(false); + actFileSaveAs->setVisible(false); + actFileImport->setVisible(false); + actFileImportUS->setVisible(false); +#endif + + updateWindowTitle(); +} + +CMainWindow::~CMainWindow() +{ + if (m_pCatalog) delete m_pCatalog; +} + +void CMainWindow::updateWindowTitle() +{ +QString str; + str = tr("AFUTrainer 3.0"); + if (m_pCatalog) + { + str += " - "; + if (m_pCatalog->name().isEmpty()) + str += tr("unbenannt"); + else + str += m_pCatalog->name(); + + if (!m_pCatalog->fileName().isEmpty()) + str += " [" + m_pCatalog->fileName() + "]"; + } + setWindowTitle(str); +} + +void CMainWindow::setCatalog (CCatalog *pCatalog) +{ + m_modelCatalog.setModelData(pCatalog); + m_pViewChapter->expandAll(); + if (m_pCatalog) delete m_pCatalog; + m_pCatalog = pCatalog; + m_modelQuestion.setModelData(0); + m_pTextQuestion->clear(); + updateWindowTitle(); + + if (m_pCatalog == 0) return; + + QDate d = m_pCatalog->validUntil(); + if (d.isValid() && d < QDate::currentDate()) + { + QString str = tr("Dieser Fragenkatalog ist seit dem %1 nicht mehr gültig!").arg(d.toString(Qt::LocalDate)); + if (!m_pCatalog->publisher().isEmpty()) + str += tr("

Weitere Informationen zur Gültigkeit erhalten Sie vom Herausgeber:
")+m_pCatalog->publisher(); + if (!m_pCatalog->contact().isEmpty()) + str += tr("

Einen aktuellen Fragenkatalog für den AFUTrainer erhalten Sie evt. von:
")+m_pCatalog->contact(); + + QMessageBox::warning(this, tr("Warnung"), str); + } +} + +bool CMainWindow::checkForErrors() +{ +QString str; + + if (m_pCatalog == 0) return false; + str = m_pCatalog->checkForErrors(); + if (str.isEmpty()) return false; + QMessageBox::critical(this, tr("Fehler im Fragenkatalog"), tr("Die gewünschte Funktion kann nicht ausgeführt werden, da der Fragenkatalog folgende Fehler enthält:

")+ str); + return true; +} + +bool CMainWindow::checkForHomeDir() +{ +QString strDir = QDir::homePath() + "/.afutrainer/"; +QDir dir; + if (dir.exists(strDir)) return true; + if (!dir.mkpath(strDir)) + { + QMessageBox::critical(this, tr("Fehler"), tr("Konnte Verzeichnis %1 nicht anlegen!").arg(strDir)); + return false; + } + return true; +} + +void CMainWindow::on_actFileNew_triggered() +{ + if (!checkForHomeDir()) return; + setCatalog(0); +} + +void CMainWindow::on_actFileOpen_triggered() +{ +QString strFileName; + if (!checkForHomeDir()) return; + strFileName = QFileDialog::getOpenFileName(this, tr("Öffne Fragenkatalog"), QString(), tr("Amateur Radio Questionary Data Format (*.aqz)\nAlle Dateien (*.*)")); + onOpenFile(strFileName); +} + +void CMainWindow::onOpenFile(const QString& strFileName) +{ +CCatalog *pCatalog=0; + if (strFileName.isEmpty()) return; + pCatalog = new CCatalog; + if (pCatalog->load(strFileName, this)) + { + setCatalog (pCatalog); + m_rf.setRecentFile(strFileName); + } + else + { + m_rf.removeFile(strFileName); + delete pCatalog; + } +} + +void CMainWindow::on_actFileInformation_triggered() +{ +CDlgInformation dlg(this); + if (!m_pCatalog) + { + QMessageBox::information(this, tr("Information"), tr("Kein Fragenkatalog geladen, zu dem Informationen angezeigt werden könnten.")); + return; + } + dlg.setup(m_pCatalog); + dlg.exec(); +} + +void CMainWindow::on_actFileSave_triggered() +{ + if (!m_pCatalog) return; + if (m_pCatalog->fileName().isEmpty()) + on_actFileSaveAs_triggered(); + else + { + m_pCatalog->save(m_pCatalog->fileName(), this); + updateWindowTitle(); + } +} + +void CMainWindow::on_actFileSaveAs_triggered() +{ +QString strFileName; + if (!m_pCatalog) return; + strFileName = QFileDialog::getSaveFileName(this, tr("Speichere Fragenkatalog"), QString(), tr("XML-Dateien (*.xml)\nAlle Dateien (*.*)")); + if (strFileName.isEmpty()) return; + m_pCatalog->save(strFileName, this); + updateWindowTitle(); +} + +bool ImportDE_isChapter(const QString& strLine) +{ + if (strLine.contains(QRegExp ("^[0-9](\\.[0-9]+)*\\s\\w"))) + return true; + else + return false; +} + +int ImportDE_chapterLevel(const QString& strLine) +{ +QString str = strLine.left(strLine.indexOf(' ')); + return (str.count('.')); +} + +bool ImportDE_isQuestion(const QString& strLine) +{ + return strLine.contains(QRegExp("^[A-Z]{2,2}[0-9]{3,3}")); +} + +bool ImportDE_isAnswer(const QString& strLine) +{ + return strLine.contains(QRegExp("^[A-D]\\s")) || strLine.contains(QRegExp("^[A-D]$")); +} + +void CMainWindow::on_actFileImport_triggered() +{ +QString strFileName, strLine, strChapter; +CChapter *pChapter=0, *pTempChapter=0; +CQuestion *pQuestion=0; +int uCurrentLevel=-1, uLevel=-1; +enum LastAdded {None, Chapter, Question, Answer}; +LastAdded la=None; +CCatalog *pCatalog=0; + +strFileName = QFileDialog::getOpenFileName(this, tr("Öffne Datei zum Fragenkatalog-Import"), QString("D:/projekte/afutrainer/fragenkataloge/2007-02/Klasse A Technik/"), tr("TXT-Dateien (*.txt)\nAlle Dateien (*.*)")); + if (strFileName.isEmpty()) return; + + QFile file(strFileName); + if(!file.open(QIODevice::ReadOnly)) + { + QMessageBox::information(this, tr("Fehler"), tr("Konnte Datei '%1' nicht zum Lesen öffnen!").arg(strFileName)); + return; + } + QTextStream in(&file); + pCatalog = new CCatalog(); + + while (!in.atEnd()) + { + strLine = in.readLine().trimmed(); + if (strLine.isEmpty() || strLine.contains(QRegExp("^Prüfungsfragen")) || strLine.contains("^Bundesnetzagentur")) + { + la = None; + } + else if (ImportDE_isChapter(strLine)) // && (uCurrentLevel+1 == ImportDE_chapterLevel(strLine) || uCurrentLevel-1 == ImportDE_chapterLevel(strLine))) + { + uLevel = ImportDE_chapterLevel(strLine); + while (uCurrentLevel >= uLevel && pChapter) + { + pChapter = pChapter->parentChapter(); + uCurrentLevel--; + } + pTempChapter = new CChapter(); + if (pChapter == 0) + pCatalog->appendChapter(pTempChapter); + else + pChapter->appendChapter(pTempChapter); + pTempChapter->setText(strLine.mid(strLine.indexOf(' ') + 1)); + + pChapter = pTempChapter; + uCurrentLevel++; + strChapter += QString("%1 -- %2
").arg(uLevel).arg(strLine); + qDebug ("%i -- %s", uLevel, qPrintable(strLine)); + la = Chapter; + } + else if (ImportDE_isQuestion(strLine)) + { + pQuestion = new CQuestion(); + pQuestion->setId (strLine.left(5)); + pQuestion->setText (strLine.mid(6).trimmed()); + if (pChapter) + { + pChapter->appendQuestion(pQuestion); + + // update id of chapters + pTempChapter = pChapter; + uLevel = uCurrentLevel; + while (pTempChapter && uLevel >= 0) + { + if (pTempChapter->id().isEmpty()) + pTempChapter->setId(pQuestion->id().at(uLevel)); + pTempChapter = pTempChapter->parentChapter(); + uLevel--; + } + } + else + delete pQuestion; + //qDebug (qPrintable(strLine)); + la = Question; + } + else if (ImportDE_isAnswer(strLine)) + { + CAnswer a(strLine.mid(2).trimmed(), strLine[0] == QChar('A')); + if (pQuestion) + pQuestion->appendAnswer(a); + //qDebug (qPrintable("\t"+strLine)); + la = Answer; + } + else + { + if (la == Chapter && pChapter) + { + if (pChapter->text().isEmpty()) + pChapter->appendText (strLine.trimmed()); + else + pChapter->appendText (" " + strLine.trimmed()); + } + else if (la == Question && pQuestion) + { + if (pQuestion->text().isEmpty() || pQuestion->text().right(1) == "-") + pQuestion->appendText (strLine.trimmed()); + else + pQuestion->appendText (" " + strLine.trimmed()); + } + else if (la == Answer && pQuestion && pQuestion->countAnswer() > 0) + { + QString strText = pQuestion->answerAt(pQuestion->countAnswer()-1).text(); + if (strText.isEmpty() || strText.right(1) == "-") + pQuestion->answerAt(pQuestion->countAnswer()-1).appendText(strLine.trimmed()); + else + pQuestion->answerAt(pQuestion->countAnswer()-1).appendText(" " + strLine.trimmed()); + } + } + } + + // Bilder hinzufügen + QList listPool = pCatalog->questionPool(); + QDir dirPath(strFileName.left(strFileName.lastIndexOf('/'))); + for (int i=0; iid().toLower()+"*"); + for (int j=0; j"; + + if (strImgFile.contains(QRegExp("f\\d*\\."))) + { // Frage + qDebug("Adding image %s to question %s", qPrintable(strImgFile), qPrintable(pQuestion->id())); + pQuestion->appendText("

" + strLink + "

"); + } + else if (strImgFile.contains("a.") && pQuestion->countAnswer() > 0) + { // Antwort A + //qDebug("Adding image %s to answer A of question %s", qPrintable(strImgFile), qPrintable(pQuestion->id())); + pQuestion->answerAt(0).appendText("

" + strLink + "

"); + } + else if (strImgFile.contains("b.") && pQuestion->countAnswer() > 1) + { // Antwort B + //qDebug("Adding image %s to answer B of question %s", qPrintable(strImgFile), qPrintable(pQuestion->id())); + pQuestion->answerAt(1).appendText("

" + strLink + "

"); + } + else if (strImgFile.contains("c.") && pQuestion->countAnswer() > 2) + { // Antwort C + //qDebug("Adding image %s to answer C of question %s", qPrintable(strImgFile), qPrintable(pQuestion->id())); + pQuestion->answerAt(2).appendText("

" + strLink + "

"); + } + else if (strImgFile.contains("d.") && pQuestion->countAnswer() > 3) + { // Antwort D + //qDebug("Adding image %s to answer D of question %s", qPrintable(strImgFile), qPrintable(pQuestion->id())); + pQuestion->answerAt(3).appendText("

" + strLink + "

"); + } + else + { + qDebug ("Konnte Grafik %s nicht zuordnen.\n\tMögliche Gründe: Frage oder Antwort existiert nicht. Dateiname der Grafik falsch geschrieben.", qPrintable(strImgFile)); + } + } + } + + pCatalog->setName("Importierter Katalog von " + strFileName); + QMessageBox::information(this, tr("Information"), + "Bitte Debug-Ausgabe überprüfen, ob alle Grafik-Dateien zugeordnet wurden!

" + "Bitte überprüfen, ob alle Kapitel korrekt erkannt wurden. " + "Wenn nicht, muss die TXT-Datei von Hand kontrolliert werden!


"+strChapter); + + qDebug("Weitere Vorgehensweise:\n\t1. Fragenkatalog als XML-Datei abspeichern.\n\t2. ggf. Korrekturen in der XML-Datei vornehmen\n\t3. XML-Datei und alle Grafik-Dateien in ein ZIP-Archiv packen und die Dateiänderung auf .atc ändern.\n\tFertig!"); + setCatalog (pCatalog); +} + +bool ImportUS_isChapter(const QString& strLine) +{ + if (strLine.contains(QRegExp("^SUBELEMENT")) || strLine.contains(QRegExp("^[A-Z][0-9][A-Z]\\s"))) + return true; + else + return false; +} + +bool ImportUS_isQuestion(const QString& strLine) +{ + return strLine.contains(QRegExp("^[A-Z][0-9][A-Z][0-9]{2,2}")); +} + +bool ImportUS_isAnswer(const QString& strLine) +{ + return strLine.contains(QRegExp("^[A-D]\\.\\s")); +} + +void CMainWindow::on_actFileImportUS_triggered() +{ +QString strFileName, strLine, strChapter, str; +CChapter *pChapter=0, *pTempChapter=0; +CQuestion *pQuestion=0; +int uCurrentLevel=-1, uLevel=-1; +enum LastAdded {None, Chapter, Question, Answer}; +LastAdded la=None; +CCatalog *pCatalog=0; + +strFileName = QFileDialog::getOpenFileName(this, tr("Öffne Datei zum Fragenkatalog-Import"), QString("D:/projekte/afutrainer/fragenkataloge/US/"), tr("TXT-Dateien (*.txt)\nAlle Dateien (*.*)")); + if (strFileName.isEmpty()) return; + + QFile file(strFileName); + if(!file.open(QIODevice::ReadOnly)) + { + QMessageBox::information(this, tr("Fehler"), tr("Konnte Datei '%1' nicht zum Lesen öffnen!").arg(strFileName)); + return; + } + QTextStream in(&file); + pCatalog = new CCatalog(); + + while (!in.atEnd()) + { + strLine = in.readLine().trimmed(); + if (strLine.isEmpty()) + { + //la = None; + } + else if (strLine == "~~") + { + la = None; + } + else if (ImportUS_isChapter(strLine)) + { + pTempChapter = new CChapter(); + if (strLine.contains(QRegExp("^SUBELEMENT"))) + { + uLevel = 0; + strLine = strLine.mid(strLine.indexOf(' ')+1); + pTempChapter->setId(strLine.left(2)); +// str = strLine.mid(strLine.indexOf(QRegExp("[A-Z]"), 2)); +// str.remove(QRegExp("\\[.*\\]")); + pTempChapter->setText(strLine.mid(strLine.indexOf(QRegExp("[A-Z]"), 2))); + } + else + { + uLevel = 1; + pTempChapter->setId(strLine.mid(2, 1)); +// str = strLine.mid(strLine.indexOf(QRegExp("[A-Z]"), 3)); +// str.remove(QRegExp("\\[.*\\]")); + pTempChapter->setText(strLine.mid(strLine.indexOf(QRegExp("[A-Z]"), 3))); + } + while (uCurrentLevel >= uLevel && pChapter) + { + pChapter = pChapter->parentChapter(); + uCurrentLevel--; + } + if (!pChapter) + pCatalog->appendChapter(pTempChapter); + else + { +// pTempChapter->setExam("main", 1); + pChapter->appendChapter(pTempChapter); + } + + strChapter += QString("%1 -- %2 - %3
").arg(uLevel).arg(pTempChapter->id(), pTempChapter->text()); + pChapter = pTempChapter; + uCurrentLevel++; + la = Chapter; + } + else if (ImportUS_isQuestion(strLine)) + { + pQuestion = new CQuestion(); + pQuestion->setId (strLine.left(5)); + pQuestion->setText (strLine.mid(6).trimmed()); + if (pChapter) + pChapter->appendQuestion(pQuestion); + else + delete pQuestion; + //qDebug (qPrintable(strLine)); + la = Question; + } + else if (ImportUS_isAnswer(strLine)) + { + CAnswer a(strLine.mid(3).trimmed(), false); + if (pQuestion) + pQuestion->appendAnswer(a); + //qDebug (qPrintable("\t"+strLine)); + la = Answer; + } + else + { + if (la == Chapter && pChapter) + { + if (pChapter->text().isEmpty()) + pChapter->appendText (strLine.trimmed()); + else + pChapter->appendText (" " + strLine.trimmed()); + } + else if (la == Question && pQuestion) + { + if (pQuestion->text().isEmpty() || pQuestion->text().right(1) == "-") + pQuestion->appendText (strLine.trimmed()); + else + pQuestion->appendText (" " + strLine.trimmed()); + } + else if (la == Answer && pQuestion && pQuestion->countAnswer() > 0) + { + QString strText = pQuestion->answerAt(pQuestion->countAnswer()-1).text(); + if (strText.isEmpty() || strText.right(1) == "-") + pQuestion->answerAt(pQuestion->countAnswer()-1).appendText(strLine.trimmed()); + else + pQuestion->answerAt(pQuestion->countAnswer()-1).appendText(" " + strLine.trimmed()); + } + } + } + + // Fragen durchgehen und richtige Antworten eintragen + QList listPool = pCatalog->questionPool(); + for (int i=0; itext(); + int idx = strText.indexOf(QRegExp("\\([A-Z]\\)")); + if (idx == -1) continue; + int iCorrect = pQuestion->text().at(idx+1).toAscii() - 'A'; + if (pQuestion->countAnswer() > iCorrect) + pQuestion->answerAt(iCorrect).setCorrect(true); + strText.remove(idx, 3); + idx = strText.indexOf(QRegExp("\\[.*\\]")); + if (idx >= 0) + { + int iLen = strText.indexOf(']', idx) - idx + 1; + CHint hint; + hint.appendQuestion(pQuestion->id()); + hint.setAuthor("FCC"); + hint.setText("See FCC rules part " + strText.mid(idx + 1, iLen - 2)); + pCatalog->appendHint(hint); + strText.remove(idx, iLen); + } + pQuestion->setText(strText.trimmed()); + } + + // Kapitelnamen korrigieren + QList listChapter = pCatalog->subChapters(); + for (int i=0; itext(); + //strText = strText.left(strText.indexOf(" - ")); + strText.remove(QRegExp("\\[.*\\]")); + pChapter->setText(strText.trimmed()); + } + + // Allgemeine Angaben: + //pCatalog->setVersionText(""); + pCatalog->setPublisher("NCVEC (National Conference of Volunteer Examiner Coordinators)
Question Pool Committee
http://www.ncvec.org/"); + pCatalog->setContact("Oliver Saal, DM1OLI
http://www.oliver-saal.de/software/afutrainer/
Mail: osaal@gmx.de"); + + pCatalog->setName("Importierter Katalog von " + strFileName); + QMessageBox::information(this, tr("Information"), + "Bitte überprüfen, ob alle Kapitel korrekt erkannt wurden. " + "Wenn nicht, muss die TXT-Datei von Hand kontrolliert werden!
"+strChapter); + + qDebug("Weitere Vorgehensweise:\n\t1. Fragenkatalog als XML-Datei abspeichern.\n\t2. ggf. Korrekturen in der XML-Datei vornehmen\n\t3. XML-Datei und alle Grafik-Dateien in ein ZIP-Archiv packen und die Dateiänderung auf .atc ändern.\n\tFertig!"); + setCatalog (pCatalog); +} + +void CMainWindow::on_actFileExit_triggered() +{ + qApp->quit(); +} + +void CMainWindow::on_actQuestionAssistant_triggered() +{ +CDlgLearnAssistant dlg(this); +CDlgLearn dlgLearn(this); +CChapter *pChapter=0; + + if (m_pCatalog == 0) return; + if (checkForErrors()) return; + 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; + + dlgLearn.go(m_pCatalog, pChapter); + m_pCatalog->saveStatistic(this); +} + +void CMainWindow::on_actQuestionsLearn_triggered() +{ +CDlgLearn dlg(this); +QModelIndexList list = m_pViewChapter->selectionModel()->selectedIndexes(); + + if (checkForErrors()) return; + if (list.isEmpty()) + { + QMessageBox::information(this, tr("Information"), tr("Bitte ein Kapitel zum Lernen auswählen!")); + return; + } + CChapter *p = (CChapter*)list.first().internalPointer(); + Q_ASSERT(p != 0); + dlg.go(m_pCatalog, p); + m_pCatalog->saveStatistic(this); +} + +void CMainWindow::on_actQuestionsLearnStatistics_triggered() +{ +CDlgLearnStatistic dlg(this); +QModelIndexList list = m_pViewChapter->selectionModel()->selectedIndexes(); +CChapter *p=0; + + if (checkForErrors()) return; + if (list.isEmpty()) + p = m_pCatalog; + else + p = (CChapter*)list.first().internalPointer(); + + dlg.go(p); +} + +void CMainWindow::on_actQuestionsTest_triggered() +{ +int iCount=0; +CExam exam; + + if (checkForErrors()) return; + iCount = m_pCatalog->countExam(); + if (iCount == 0) + { + QMessageBox::information(this, tr("Information"), tr("Dieser Fragenkatalog enthält keine Prüfungen.")); + return; + } + else if (iCount == 1) + { + exam = m_pCatalog->examAt(0); + } + else if (iCount > 1) + { + CDlgExamSelect dlg1(this); + dlg1.setup(m_pCatalog); + dlg1.exec(); + if (dlg1.result() == QDialog::Rejected) return; + exam = m_pCatalog->examAt(dlg1.selectedExam()); + } + + CDlgExam dlg2(m_pCatalog, this); + if (dlg2.setup(exam)) + dlg2.exec(); +} + +void CMainWindow::on_actQuestionsTestStatistics_triggered() +{ +CDlgExamStatistic dlg(this); + if (checkForErrors()) return; + if (m_pCatalog->countExam() == 0) + { + QMessageBox::information(this, tr("Information"), tr("Dieser Fragenkatalog enthält keine Prüfungen.")); + return; + } + dlg.go(m_pCatalog); +} + +void CMainWindow::on_actViewToolbar_toggled(bool bChecked) +{ + if (bChecked) + toolBar->show(); + else + toolBar->hide(); +} + +void CMainWindow::on_actViewStatusbar_toggled(bool bChecked) +{ + if (bChecked) + statusbar->show(); + else + statusbar->hide(); +} + +void CMainWindow::on_actHelpWhatsThis_triggered() +{ + QWhatsThis::enterWhatsThisMode(); +} + +void CMainWindow::on_actHelpAbout_triggered() +{ +QString str = tr("AFUTrainer Version 3.0
" + "(c) 2003-2007 by Oliver Saal (DM1OLI)

" + "EMail: 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."); + QMessageBox::about(this,tr("Ãœber..."), str); +} + +void CMainWindow::on_actHelpAboutQt_triggered() +{ + qApp->aboutQt(); +} + +void CMainWindow::onCatalogSelectionChanged (const QItemSelection& selected, const QItemSelection& deselected) +{ + Q_UNUSED(deselected); + m_pTextQuestion->clear(); + if (!selected.indexes().isEmpty()) + { + CChapter *pChapter = (CChapter*)selected.indexes().first().internalPointer(); + if (pChapter) + m_modelQuestion.setModelData(pChapter); + } + else + m_modelQuestion.clear(); +} + +void CMainWindow::onQuestionSelectionChanged (const QItemSelection& selected, const QItemSelection& deselected) +{ + Q_UNUSED(deselected); + m_pTextQuestion->clear(); + if (selected.indexes().isEmpty()) return; + CQuestion *pQuestion = (CQuestion*)selected.indexes().first().internalPointer(); + if (pQuestion == 0) return; + m_pTextQuestion->setHtml(pQuestion->showText(m_pCatalog)); +} +/* +void CMainWindow::onAnchorClicked (const QUrl &link) +{ + QMessageBox::information(this, tr("Click"), link.toString()); + m_pTextQuestion->home(); +} +*/ diff --git a/mainwindow.h b/mainwindow.h index 6a062df..5fdae48 100644 --- a/mainwindow.h +++ b/mainwindow.h @@ -1,91 +1,91 @@ -/*************************************************************************** - * 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. * - ***************************************************************************/ - -#ifndef MAINWINDOW_H -#define MAINWINDOW_H - -#include -#include -#include -#include - -#include "catalogmodel.h" -#include "questionmodel.h" -#include "recentfiles.h" - -#include "ui_mainwindow.h" - -class CMainWindow : public QMainWindow, protected Ui::MainWindow -{ - Q_OBJECT -public: - CMainWindow(); - ~CMainWindow(); - -protected: - void setCatalog (CCatalog *pCatalog); - void updateWindowTitle(); - bool checkForErrors(); - bool checkForHomeDir(); - -protected slots: - void on_actFileNew_triggered(); - void on_actFileOpen_triggered(); - void on_actFileInformation_triggered(); - void on_actFileSave_triggered(); - void on_actFileSaveAs_triggered(); - void on_actFileImport_triggered(); - void on_actFileImportUS_triggered(); - void on_actFileExit_triggered(); - void on_actQuestionAssistant_triggered(); - void on_actQuestionsLearn_triggered(); - void on_actQuestionsLearnStatistics_triggered(); - void on_actQuestionsTest_triggered(); - void on_actQuestionsTestStatistics_triggered(); - void on_actViewToolbar_toggled(bool bChecked); - void on_actViewStatusbar_toggled(bool bChecked); - void on_actHelpWhatsThis_triggered(); - void on_actHelpAbout_triggered(); - void on_actHelpAboutQt_triggered(); - - void onCatalogSelectionChanged (const QItemSelection& selected, const QItemSelection& deselected); - void onQuestionSelectionChanged (const QItemSelection& selected, const QItemSelection& deselected); - - void onOpenFile(const QString& strFileName); - -// void onAnchorClicked (const QUrl &link ); - -protected: - CRecentFiles m_rf; - QSplitter *m_pSplitter; // splitter widget - QTreeView *m_pViewChapter; // listview chapters - QTreeView *m_pViewQuestions; // listview questions - QSplitter *m_pSplitter2; - QTextBrowser *m_pTextQuestion; - - CCatalog *m_pCatalog; - CCatalogModel m_modelCatalog; - CQuestionModel m_modelQuestion; -}; - - -#endif // MAINWINDOW_H - +/*************************************************************************** + * 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. * + ***************************************************************************/ + +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include +#include +#include +#include + +#include "catalogmodel.h" +#include "questionmodel.h" +#include "recentfiles.h" + +#include "ui_mainwindow.h" + +class CMainWindow : public QMainWindow, protected Ui::MainWindow +{ + Q_OBJECT +public: + CMainWindow(); + ~CMainWindow(); + +protected: + void setCatalog (CCatalog *pCatalog); + void updateWindowTitle(); + bool checkForErrors(); + bool checkForHomeDir(); + +protected slots: + void on_actFileNew_triggered(); + void on_actFileOpen_triggered(); + void on_actFileInformation_triggered(); + void on_actFileSave_triggered(); + void on_actFileSaveAs_triggered(); + void on_actFileImport_triggered(); + void on_actFileImportUS_triggered(); + void on_actFileExit_triggered(); + void on_actQuestionAssistant_triggered(); + void on_actQuestionsLearn_triggered(); + void on_actQuestionsLearnStatistics_triggered(); + void on_actQuestionsTest_triggered(); + void on_actQuestionsTestStatistics_triggered(); + void on_actViewToolbar_toggled(bool bChecked); + void on_actViewStatusbar_toggled(bool bChecked); + void on_actHelpWhatsThis_triggered(); + void on_actHelpAbout_triggered(); + void on_actHelpAboutQt_triggered(); + + void onCatalogSelectionChanged (const QItemSelection& selected, const QItemSelection& deselected); + void onQuestionSelectionChanged (const QItemSelection& selected, const QItemSelection& deselected); + + void onOpenFile(const QString& strFileName); + +// void onAnchorClicked (const QUrl &link ); + +protected: + CRecentFiles m_rf; + QSplitter *m_pSplitter; // splitter widget + QTreeView *m_pViewChapter; // listview chapters + QTreeView *m_pViewQuestions; // listview questions + QSplitter *m_pSplitter2; + QTextBrowser *m_pTextQuestion; + + CCatalog *m_pCatalog; + CCatalogModel m_modelCatalog; + CQuestionModel m_modelQuestion; +}; + + +#endif // MAINWINDOW_H + diff --git a/osziparchive.cpp b/osziparchive.cpp index 3ac3255..e5d998b 100644 --- a/osziparchive.cpp +++ b/osziparchive.cpp @@ -1,467 +1,467 @@ - -#include "osziparchive.h" - -#include "zlib/zlib.h" - -#include -#include -#include - - -void CZipFileHeader::clear() -{ - m_uVersionMadeBy = 0; - m_uVersionNeeded = 0; - m_uFlags = 0; - m_uCompression = 0; - m_uTime = 0; - m_uDate = 0; - m_uCRC = 0; - m_uSizeCompressed = 0; - m_uSizeUncompressed = 0; - m_uDiskNumberStart = 0; - m_uInternalFileAttributes = 0; - m_uExternalFileAttributes = 0; - m_uRelativeOffsetLocalHeader = 0; - m_strFileName.clear(); - m_strComment.clear(); - m_baExtraField.clear(); - - // helpers - m_uDataPosition = 0; - m_uLocalHeaderPosition = 0; - m_uCentralHeaderPosition = 0; -} - -void CZipFileHeader::integrate(const CZipFileHeader& head) -{ - if (m_uVersionMadeBy == 0) m_uVersionMadeBy = head.m_uVersionMadeBy; - if (m_uVersionNeeded == 0) m_uVersionNeeded = head.m_uVersionNeeded; - if (m_uFlags == 0) m_uFlags = head.m_uFlags; - if (m_uCompression == 0) m_uCompression = head.m_uCompression; - if (m_uTime == 0) m_uTime = head.m_uTime; - if (m_uDate == 0) m_uDate = head.m_uDate; - if (m_uCRC == 0) m_uCRC = head.m_uCRC; - if (m_uSizeCompressed == 0) m_uSizeCompressed = head.m_uSizeCompressed; - if (m_uSizeUncompressed == 0) m_uSizeUncompressed = head.m_uSizeUncompressed; - if (m_uDiskNumberStart == 0) m_uDiskNumberStart = head.m_uDiskNumberStart; - if (m_uInternalFileAttributes == 0) m_uInternalFileAttributes = head.m_uInternalFileAttributes; - if (m_uExternalFileAttributes == 0) m_uExternalFileAttributes = head.m_uExternalFileAttributes; - if (m_uRelativeOffsetLocalHeader == 0) m_uRelativeOffsetLocalHeader = head.m_uRelativeOffsetLocalHeader; - - if (m_strFileName.isEmpty()) m_strFileName = head.m_strFileName; - if (m_baExtraField.isEmpty()) m_baExtraField = head.m_baExtraField; - if (m_strComment.isEmpty()) m_strComment = head.m_strComment; - - if (m_uDataPosition == 0) m_uDataPosition = head.m_uDataPosition; - if (m_uLocalHeaderPosition == 0) m_uLocalHeaderPosition = head.m_uLocalHeaderPosition; - if (m_uCentralHeaderPosition == 0) m_uCentralHeaderPosition = head.m_uCentralHeaderPosition; -} - -bool CZipFileHeader::readLocalFileHeader(QFile& file) -{ -QDataStream in(&file); -quint16 uFileNameLength=0, uExtraFieldLength=0; - - clear(); - in.setByteOrder(QDataStream::LittleEndian); - - m_uLocalHeaderPosition = file.pos(); - - in >> m_uVersionNeeded; - in >> m_uFlags; - in >> m_uCompression; - in >> m_uTime; - in >> m_uDate; - in >> m_uCRC; - in >> m_uSizeCompressed; - in >> m_uSizeUncompressed; - in >> uFileNameLength; - in >> uExtraFieldLength; - - if (uFileNameLength != 0) - { - QByteArray a (uFileNameLength, 0); - in.readRawData(a.data(), uFileNameLength); - m_strFileName = a; - } - if (uExtraFieldLength != 0) - { - m_baExtraField.resize(uExtraFieldLength); - in.readRawData(m_baExtraField.data(), uExtraFieldLength); - } - - m_uDataPosition = file.pos(); - in.skipRawData(m_uSizeCompressed); - - if (hasDataDescriptor()) - { - in >> m_uCRC; - in >> m_uSizeCompressed; - in >> m_uSizeUncompressed; - } - return true; -} - -void CZipFileHeader::writeLocalFileHeader(QFile& file) -{ - Q_UNUSED(file); -} - -bool CZipFileHeader::readCentralDirectoryHeader(QFile& file) -{ -QDataStream in(&file); -quint16 uFileNameLength=0, uExtraFieldLength=0, uCommentLength=0; - - clear(); - in.setByteOrder(QDataStream::LittleEndian); - - m_uCentralHeaderPosition = file.pos(); - - in >> m_uVersionMadeBy; - in >> m_uVersionNeeded; - in >> m_uFlags; - in >> m_uCompression; - in >> m_uTime; - in >> m_uDate; - in >> m_uCRC; - in >> m_uSizeCompressed; - in >> m_uSizeUncompressed; - in >> uFileNameLength; - in >> uExtraFieldLength; - in >> uCommentLength; - in >> m_uDiskNumberStart; - in >> m_uInternalFileAttributes; - in >> m_uExternalFileAttributes; - in >> m_uRelativeOffsetLocalHeader; - - if (uFileNameLength != 0) - { - QByteArray a (uFileNameLength, 0); - in.readRawData(a.data(), uFileNameLength); - m_strFileName = a; - } - if (uExtraFieldLength != 0) - { - m_baExtraField.resize(uExtraFieldLength); - in.readRawData(m_baExtraField.data(), uExtraFieldLength); - } - - if (uCommentLength != 0) - { - QByteArray a (uCommentLength, 0); - in.readRawData(a.data(), uCommentLength); - m_strComment = a; - } - return true; -} - -void CZipFileHeader::writeCentralDirectoryHeader(QFile& file) -{ - Q_UNUSED(file); -} - -void CZipEndRecord::clear() -{ - m_uDisk = 0; - m_uDiskCentralDir = 0; - m_uEntriesCentralDirDisk = 0; - m_uEntriesCentralDir = 0; - m_uCentralDirSize = 0; - m_uCentralDirOffset = 0; - m_strComment.clear(); -} - -bool CZipEndRecord::read (QFile& file) -{ -QDataStream in(&file); -quint16 uCommentLength=0; - - clear(); - in.setByteOrder(QDataStream::LittleEndian); - - in >> m_uDisk; - in >> m_uDiskCentralDir; - in >> m_uEntriesCentralDirDisk; - in >> m_uEntriesCentralDir; - in >> m_uCentralDirSize; - in >> m_uCentralDirOffset; - in >> uCommentLength; - - if (uCommentLength != 0) - { - QByteArray a (uCommentLength, 0); - in.readRawData(a.data(), uCommentLength); - m_strComment = a; - } - return true; -} - -void CZipEndRecord::write (QFile& file) -{ - Q_UNUSED(file); -} - - -CZipFile::CZipFile(CZipArchive *pArchive) -{ - m_pArchive = pArchive; - clear(); -} - -CZipFile::~CZipFile() -{ -} - -void CZipFile::clear() -{ -} - -bool CZipFile::readHeader () -{ -QFile& file = m_pArchive->m_file; - - return (m_head.readLocalFileHeader(file)); -} - -QByteArray CZipFile::deflateToByteArray() -{ -/*QByteArray a, b; - - if (m_head.m_uDataPosition == 0 || m_head.m_uSizeUncompressed == 0) return QByteArray(); - - m_pArchive->m_file.seek (m_head.m_uDataPosition); - a = m_pArchive->m_file.read (m_head.m_uSizeCompressed); - - b.resize(m_head.m_uSizeUncompressed); - quint32 u = m_head.m_uSizeUncompressed; - Bytef* src = (Bytef*)a.data(); - Bytef* dst = (Bytef*)b.data(); - if (uncompress(dst, (uLongf*) &u, src, m_head.m_uSizeCompressed) != Z_OK) - return QByteArray(); - - return b;*/ - -QByteArray a; -z_stream strm; -char *pIn=0, *pOut=0; -int ret; - - memset(&strm, 0, sizeof(z_stream)); - if (m_head.m_uDataPosition == 0 || m_head.m_uSizeUncompressed == 0) return QByteArray(); - - // prepare zip-stream - /* windowBits is passed < 0 to tell that there is no zlib header. - * Note that in this case inflate *requires* an extra "dummy" byte - * after the compressed stream in order to complete decompression and - * return Z_STREAM_END. - */ - if (inflateInit2(&strm, -MAX_WBITS) != Z_OK) return QByteArray(); - - // read data - pIn = new char[m_head.m_uSizeCompressed+1]; - memset (pIn, 0, m_head.m_uSizeCompressed+1); - m_pArchive->m_file.seek (m_head.m_uDataPosition); - m_pArchive->m_file.read (pIn, m_head.m_uSizeCompressed); - - // prepare output - pOut = new char[m_head.m_uSizeUncompressed]; - memset(pOut, 0, m_head.m_uSizeUncompressed); - -/* -// DEBUG START - FILE *fpt; - fpt = fopen ("questions.xml", "rt"); - fread (pOut, m_head.m_uSizeUncompressed, 1, fpt); - fclose (fpt); - - unsigned u = m_head.m_uSizeCompressed; - fpt = fopen ("test2.gz", "wb"); - compress2((Bytef*)pIn, (uLongf*) &u, (Bytef*)pOut, m_head.m_uSizeUncompressed, 9); - fwrite (pIn, u, 1, fpt); - fclose (fpt); -// DEBUG ENDE -*/ - do - { - strm.avail_in = m_head.m_uSizeCompressed+1; - strm.next_in = (Bytef*) pIn; - do - { - strm.avail_out = m_head.m_uSizeUncompressed; - strm.next_out = (Bytef*) pOut; - ret = inflate (&strm, Z_SYNC_FLUSH); - switch (ret) - { - case Z_NEED_DICT: - ret = Z_DATA_ERROR; /* and fall through */ - case Z_DATA_ERROR: - case Z_MEM_ERROR: - inflateEnd(&strm); - delete [] pIn; - delete [] pOut; - return QByteArray(); - } - } - while (strm.avail_out == 0); - - /* done when inflate() says it's done */ - } - while (ret != Z_STREAM_END); - - inflateEnd(&strm); - - a = QByteArray (pOut, m_head.m_uSizeUncompressed); - delete [] pIn; - delete [] pOut; - - return a; -} -/* -QString CZipFile::deflateToString() -{ -QByteArray a = deflateToByteArray(); -QTextStream in (a, QIODevice::ReadOnly); -//QString str; -// in >> str; - return in.readAll(); -} -*/ - -#define CHUNK (1<<16) - -bool CZipFile::deflateToFile (QIODevice& dev) -{ -z_stream strm; -char cIn[CHUNK], cOut[CHUNK]; -int ret; - - if ((dev.openMode() & QIODevice::WriteOnly) == 0) - return false; - - memset(&strm, 0, sizeof(z_stream)); - if (m_head.m_uDataPosition == 0 || m_head.m_uSizeUncompressed == 0) return false; - - // prepare zip-stream - /* windowBits is passed < 0 to tell that there is no zlib header. - * Note that in this case inflate *requires* an extra "dummy" byte - * after the compressed stream in order to complete decompression and - * return Z_STREAM_END. - */ - if (inflateInit2(&strm, -MAX_WBITS) != Z_OK) return false; - - // prepare archive - m_pArchive->m_file.seek (m_head.m_uDataPosition); - - // unzip - unsigned uRead=0, uBytes=0; - do - { - uBytes = m_pArchive->m_file.read(cIn, CHUNK); - if (uRead + uBytes > m_head.m_uSizeCompressed) - uBytes = m_head.m_uSizeCompressed - uRead + 1; // one dummy byte extra - uRead += uBytes; - - strm.avail_in = uBytes; - if (strm.avail_in == 0) break; - strm.next_in = (Bytef*) cIn; - - do - { - strm.avail_out = CHUNK; - strm.next_out = (Bytef*) cOut; - ret = inflate (&strm, Z_NO_FLUSH); - switch (ret) - { - case Z_NEED_DICT: - ret = Z_DATA_ERROR; /* and fall through */ - case Z_DATA_ERROR: - case Z_MEM_ERROR: - inflateEnd(&strm); - return false; - } - unsigned uHave = CHUNK - strm.avail_out; - dev.write(cOut, uHave); - } - while (strm.avail_out == 0); - - /* done when inflate() says it's done */ - } - while (ret != Z_STREAM_END); - - inflateEnd(&strm); - return true; -} - - -CZipArchive::CZipArchive() -{ -} - -CZipArchive::~CZipArchive() -{ - qDeleteAll(m_listFiles); - qDeleteAll(m_listEndRecords); -} - -bool CZipArchive::open (const QString& strFileName, const OpenMode om) -{ -unsigned uSignature=0; -CZipFile *pzf=0; - - if (strFileName.isEmpty()) return false; - if (om != OpenReadOnly) return false; - - m_strFileName = strFileName; - m_file.close(); - m_file.setFileName(strFileName); - if (!m_file.open(QIODevice::ReadOnly)) return false; - QDataStream in(&m_file); - in.setByteOrder(QDataStream::LittleEndian); - - while (!in.atEnd()) - { - uSignature = 0; - in >> uSignature; - if (uSignature == 0x04034b50) - { // local file header with data - pzf = new CZipFile(this); - if (pzf->readHeader()) - m_listFiles.append(pzf); - //qDebug("%7i %7i %s", pzf->m_head.m_uSizeUncompressed, pzf->m_head.m_uSizeCompressed, qPrintable(pzf->fileName())); -// file.deflateToFile("test.tmp"); - } - else if (uSignature == 0x02014b50) - { // central directory file header - CZipFileHeader head; - head.readCentralDirectoryHeader(m_file); - pzf = findFile(head.m_strFileName); - if (pzf) pzf->m_head.integrate(head); - } - else if (uSignature == 0x06054b50) - { // End of central directory record - CZipEndRecord *pzer = new CZipEndRecord(); - pzer->read(m_file); - m_listEndRecords.append(pzer); - } - else - { - qDebug("Unknown signature: %X", uSignature); - break; - } - } - - return true; -} - -CZipFile* CZipArchive::findFile (const QString& strFileName) -{ - for (int i=0; ifileName() == strFileName) - return m_listFiles.at(i); - } - - return 0; -} - + +#include "osziparchive.h" + +#include "zlib/zlib.h" + +#include +#include +#include + + +void CZipFileHeader::clear() +{ + m_uVersionMadeBy = 0; + m_uVersionNeeded = 0; + m_uFlags = 0; + m_uCompression = 0; + m_uTime = 0; + m_uDate = 0; + m_uCRC = 0; + m_uSizeCompressed = 0; + m_uSizeUncompressed = 0; + m_uDiskNumberStart = 0; + m_uInternalFileAttributes = 0; + m_uExternalFileAttributes = 0; + m_uRelativeOffsetLocalHeader = 0; + m_strFileName.clear(); + m_strComment.clear(); + m_baExtraField.clear(); + + // helpers + m_uDataPosition = 0; + m_uLocalHeaderPosition = 0; + m_uCentralHeaderPosition = 0; +} + +void CZipFileHeader::integrate(const CZipFileHeader& head) +{ + if (m_uVersionMadeBy == 0) m_uVersionMadeBy = head.m_uVersionMadeBy; + if (m_uVersionNeeded == 0) m_uVersionNeeded = head.m_uVersionNeeded; + if (m_uFlags == 0) m_uFlags = head.m_uFlags; + if (m_uCompression == 0) m_uCompression = head.m_uCompression; + if (m_uTime == 0) m_uTime = head.m_uTime; + if (m_uDate == 0) m_uDate = head.m_uDate; + if (m_uCRC == 0) m_uCRC = head.m_uCRC; + if (m_uSizeCompressed == 0) m_uSizeCompressed = head.m_uSizeCompressed; + if (m_uSizeUncompressed == 0) m_uSizeUncompressed = head.m_uSizeUncompressed; + if (m_uDiskNumberStart == 0) m_uDiskNumberStart = head.m_uDiskNumberStart; + if (m_uInternalFileAttributes == 0) m_uInternalFileAttributes = head.m_uInternalFileAttributes; + if (m_uExternalFileAttributes == 0) m_uExternalFileAttributes = head.m_uExternalFileAttributes; + if (m_uRelativeOffsetLocalHeader == 0) m_uRelativeOffsetLocalHeader = head.m_uRelativeOffsetLocalHeader; + + if (m_strFileName.isEmpty()) m_strFileName = head.m_strFileName; + if (m_baExtraField.isEmpty()) m_baExtraField = head.m_baExtraField; + if (m_strComment.isEmpty()) m_strComment = head.m_strComment; + + if (m_uDataPosition == 0) m_uDataPosition = head.m_uDataPosition; + if (m_uLocalHeaderPosition == 0) m_uLocalHeaderPosition = head.m_uLocalHeaderPosition; + if (m_uCentralHeaderPosition == 0) m_uCentralHeaderPosition = head.m_uCentralHeaderPosition; +} + +bool CZipFileHeader::readLocalFileHeader(QFile& file) +{ +QDataStream in(&file); +quint16 uFileNameLength=0, uExtraFieldLength=0; + + clear(); + in.setByteOrder(QDataStream::LittleEndian); + + m_uLocalHeaderPosition = file.pos(); + + in >> m_uVersionNeeded; + in >> m_uFlags; + in >> m_uCompression; + in >> m_uTime; + in >> m_uDate; + in >> m_uCRC; + in >> m_uSizeCompressed; + in >> m_uSizeUncompressed; + in >> uFileNameLength; + in >> uExtraFieldLength; + + if (uFileNameLength != 0) + { + QByteArray a (uFileNameLength, 0); + in.readRawData(a.data(), uFileNameLength); + m_strFileName = a; + } + if (uExtraFieldLength != 0) + { + m_baExtraField.resize(uExtraFieldLength); + in.readRawData(m_baExtraField.data(), uExtraFieldLength); + } + + m_uDataPosition = file.pos(); + in.skipRawData(m_uSizeCompressed); + + if (hasDataDescriptor()) + { + in >> m_uCRC; + in >> m_uSizeCompressed; + in >> m_uSizeUncompressed; + } + return true; +} + +void CZipFileHeader::writeLocalFileHeader(QFile& file) +{ + Q_UNUSED(file); +} + +bool CZipFileHeader::readCentralDirectoryHeader(QFile& file) +{ +QDataStream in(&file); +quint16 uFileNameLength=0, uExtraFieldLength=0, uCommentLength=0; + + clear(); + in.setByteOrder(QDataStream::LittleEndian); + + m_uCentralHeaderPosition = file.pos(); + + in >> m_uVersionMadeBy; + in >> m_uVersionNeeded; + in >> m_uFlags; + in >> m_uCompression; + in >> m_uTime; + in >> m_uDate; + in >> m_uCRC; + in >> m_uSizeCompressed; + in >> m_uSizeUncompressed; + in >> uFileNameLength; + in >> uExtraFieldLength; + in >> uCommentLength; + in >> m_uDiskNumberStart; + in >> m_uInternalFileAttributes; + in >> m_uExternalFileAttributes; + in >> m_uRelativeOffsetLocalHeader; + + if (uFileNameLength != 0) + { + QByteArray a (uFileNameLength, 0); + in.readRawData(a.data(), uFileNameLength); + m_strFileName = a; + } + if (uExtraFieldLength != 0) + { + m_baExtraField.resize(uExtraFieldLength); + in.readRawData(m_baExtraField.data(), uExtraFieldLength); + } + + if (uCommentLength != 0) + { + QByteArray a (uCommentLength, 0); + in.readRawData(a.data(), uCommentLength); + m_strComment = a; + } + return true; +} + +void CZipFileHeader::writeCentralDirectoryHeader(QFile& file) +{ + Q_UNUSED(file); +} + +void CZipEndRecord::clear() +{ + m_uDisk = 0; + m_uDiskCentralDir = 0; + m_uEntriesCentralDirDisk = 0; + m_uEntriesCentralDir = 0; + m_uCentralDirSize = 0; + m_uCentralDirOffset = 0; + m_strComment.clear(); +} + +bool CZipEndRecord::read (QFile& file) +{ +QDataStream in(&file); +quint16 uCommentLength=0; + + clear(); + in.setByteOrder(QDataStream::LittleEndian); + + in >> m_uDisk; + in >> m_uDiskCentralDir; + in >> m_uEntriesCentralDirDisk; + in >> m_uEntriesCentralDir; + in >> m_uCentralDirSize; + in >> m_uCentralDirOffset; + in >> uCommentLength; + + if (uCommentLength != 0) + { + QByteArray a (uCommentLength, 0); + in.readRawData(a.data(), uCommentLength); + m_strComment = a; + } + return true; +} + +void CZipEndRecord::write (QFile& file) +{ + Q_UNUSED(file); +} + + +CZipFile::CZipFile(CZipArchive *pArchive) +{ + m_pArchive = pArchive; + clear(); +} + +CZipFile::~CZipFile() +{ +} + +void CZipFile::clear() +{ +} + +bool CZipFile::readHeader () +{ +QFile& file = m_pArchive->m_file; + + return (m_head.readLocalFileHeader(file)); +} + +QByteArray CZipFile::deflateToByteArray() +{ +/*QByteArray a, b; + + if (m_head.m_uDataPosition == 0 || m_head.m_uSizeUncompressed == 0) return QByteArray(); + + m_pArchive->m_file.seek (m_head.m_uDataPosition); + a = m_pArchive->m_file.read (m_head.m_uSizeCompressed); + + b.resize(m_head.m_uSizeUncompressed); + quint32 u = m_head.m_uSizeUncompressed; + Bytef* src = (Bytef*)a.data(); + Bytef* dst = (Bytef*)b.data(); + if (uncompress(dst, (uLongf*) &u, src, m_head.m_uSizeCompressed) != Z_OK) + return QByteArray(); + + return b;*/ + +QByteArray a; +z_stream strm; +char *pIn=0, *pOut=0; +int ret; + + memset(&strm, 0, sizeof(z_stream)); + if (m_head.m_uDataPosition == 0 || m_head.m_uSizeUncompressed == 0) return QByteArray(); + + // prepare zip-stream + /* windowBits is passed < 0 to tell that there is no zlib header. + * Note that in this case inflate *requires* an extra "dummy" byte + * after the compressed stream in order to complete decompression and + * return Z_STREAM_END. + */ + if (inflateInit2(&strm, -MAX_WBITS) != Z_OK) return QByteArray(); + + // read data + pIn = new char[m_head.m_uSizeCompressed+1]; + memset (pIn, 0, m_head.m_uSizeCompressed+1); + m_pArchive->m_file.seek (m_head.m_uDataPosition); + m_pArchive->m_file.read (pIn, m_head.m_uSizeCompressed); + + // prepare output + pOut = new char[m_head.m_uSizeUncompressed]; + memset(pOut, 0, m_head.m_uSizeUncompressed); + +/* +// DEBUG START + FILE *fpt; + fpt = fopen ("questions.xml", "rt"); + fread (pOut, m_head.m_uSizeUncompressed, 1, fpt); + fclose (fpt); + + unsigned u = m_head.m_uSizeCompressed; + fpt = fopen ("test2.gz", "wb"); + compress2((Bytef*)pIn, (uLongf*) &u, (Bytef*)pOut, m_head.m_uSizeUncompressed, 9); + fwrite (pIn, u, 1, fpt); + fclose (fpt); +// DEBUG ENDE +*/ + do + { + strm.avail_in = m_head.m_uSizeCompressed+1; + strm.next_in = (Bytef*) pIn; + do + { + strm.avail_out = m_head.m_uSizeUncompressed; + strm.next_out = (Bytef*) pOut; + ret = inflate (&strm, Z_SYNC_FLUSH); + switch (ret) + { + case Z_NEED_DICT: + ret = Z_DATA_ERROR; /* and fall through */ + case Z_DATA_ERROR: + case Z_MEM_ERROR: + inflateEnd(&strm); + delete [] pIn; + delete [] pOut; + return QByteArray(); + } + } + while (strm.avail_out == 0); + + /* done when inflate() says it's done */ + } + while (ret != Z_STREAM_END); + + inflateEnd(&strm); + + a = QByteArray (pOut, m_head.m_uSizeUncompressed); + delete [] pIn; + delete [] pOut; + + return a; +} +/* +QString CZipFile::deflateToString() +{ +QByteArray a = deflateToByteArray(); +QTextStream in (a, QIODevice::ReadOnly); +//QString str; +// in >> str; + return in.readAll(); +} +*/ + +#define CHUNK (1<<16) + +bool CZipFile::deflateToFile (QIODevice& dev) +{ +z_stream strm; +char cIn[CHUNK], cOut[CHUNK]; +int ret; + + if ((dev.openMode() & QIODevice::WriteOnly) == 0) + return false; + + memset(&strm, 0, sizeof(z_stream)); + if (m_head.m_uDataPosition == 0 || m_head.m_uSizeUncompressed == 0) return false; + + // prepare zip-stream + /* windowBits is passed < 0 to tell that there is no zlib header. + * Note that in this case inflate *requires* an extra "dummy" byte + * after the compressed stream in order to complete decompression and + * return Z_STREAM_END. + */ + if (inflateInit2(&strm, -MAX_WBITS) != Z_OK) return false; + + // prepare archive + m_pArchive->m_file.seek (m_head.m_uDataPosition); + + // unzip + unsigned uRead=0, uBytes=0; + do + { + uBytes = m_pArchive->m_file.read(cIn, CHUNK); + if (uRead + uBytes > m_head.m_uSizeCompressed) + uBytes = m_head.m_uSizeCompressed - uRead + 1; // one dummy byte extra + uRead += uBytes; + + strm.avail_in = uBytes; + if (strm.avail_in == 0) break; + strm.next_in = (Bytef*) cIn; + + do + { + strm.avail_out = CHUNK; + strm.next_out = (Bytef*) cOut; + ret = inflate (&strm, Z_NO_FLUSH); + switch (ret) + { + case Z_NEED_DICT: + ret = Z_DATA_ERROR; /* and fall through */ + case Z_DATA_ERROR: + case Z_MEM_ERROR: + inflateEnd(&strm); + return false; + } + unsigned uHave = CHUNK - strm.avail_out; + dev.write(cOut, uHave); + } + while (strm.avail_out == 0); + + /* done when inflate() says it's done */ + } + while (ret != Z_STREAM_END); + + inflateEnd(&strm); + return true; +} + + +CZipArchive::CZipArchive() +{ +} + +CZipArchive::~CZipArchive() +{ + qDeleteAll(m_listFiles); + qDeleteAll(m_listEndRecords); +} + +bool CZipArchive::open (const QString& strFileName, const OpenMode om) +{ +unsigned uSignature=0; +CZipFile *pzf=0; + + if (strFileName.isEmpty()) return false; + if (om != OpenReadOnly) return false; + + m_strFileName = strFileName; + m_file.close(); + m_file.setFileName(strFileName); + if (!m_file.open(QIODevice::ReadOnly)) return false; + QDataStream in(&m_file); + in.setByteOrder(QDataStream::LittleEndian); + + while (!in.atEnd()) + { + uSignature = 0; + in >> uSignature; + if (uSignature == 0x04034b50) + { // local file header with data + pzf = new CZipFile(this); + if (pzf->readHeader()) + m_listFiles.append(pzf); + //qDebug("%7i %7i %s", pzf->m_head.m_uSizeUncompressed, pzf->m_head.m_uSizeCompressed, qPrintable(pzf->fileName())); +// file.deflateToFile("test.tmp"); + } + else if (uSignature == 0x02014b50) + { // central directory file header + CZipFileHeader head; + head.readCentralDirectoryHeader(m_file); + pzf = findFile(head.m_strFileName); + if (pzf) pzf->m_head.integrate(head); + } + else if (uSignature == 0x06054b50) + { // End of central directory record + CZipEndRecord *pzer = new CZipEndRecord(); + pzer->read(m_file); + m_listEndRecords.append(pzer); + } + else + { + qDebug("Unknown signature: %X", uSignature); + break; + } + } + + return true; +} + +CZipFile* CZipArchive::findFile (const QString& strFileName) +{ + for (int i=0; ifileName() == strFileName) + return m_listFiles.at(i); + } + + return 0; +} + diff --git a/osziparchive.h b/osziparchive.h index da03109..f1abba0 100644 --- a/osziparchive.h +++ b/osziparchive.h @@ -1,155 +1,155 @@ -/*************************************************************************** - * Copyright (C) 2003-2006 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. * - ***************************************************************************/ - -#ifndef OSZIPARCHIVE_H -#define OSZIPARCHIVE_H - -#include -#include -#include - -class CZipFile; -class CZipArchive; - -class CZipFileHeader -{ -public: - CZipFileHeader() { clear(); } - ~CZipFileHeader() {} - - void clear(); - void integrate(const CZipFileHeader& head); - - bool readLocalFileHeader(QFile& file); - void writeLocalFileHeader(QFile& file); - - bool readCentralDirectoryHeader(QFile& file); - void writeCentralDirectoryHeader(QFile& file); - - inline bool hasDataDescriptor() const { return m_uFlags & (1<<3) ? true : false; } -public: - quint16 m_uVersionMadeBy; //!< version made by 2 bytes - quint16 m_uVersionNeeded; //!< version needed to extract 2 bytes - quint16 m_uFlags; //!< general purpose bit flag 2 bytes - quint16 m_uCompression; //!< compression method 2 bytes - quint16 m_uTime; //!< last mod file time 2 bytes - quint16 m_uDate; //!< last mod file date 2 bytes - quint32 m_uCRC; //!< crc-32 4 bytes - quint32 m_uSizeCompressed; //!< compressed size 4 bytes - quint32 m_uSizeUncompressed; //!< uncompressed size 4 bytes - quint16 m_uDiskNumberStart; //!< disk number start 2 bytes - quint16 m_uInternalFileAttributes; //!< internal file attributes 2 bytes - quint32 m_uExternalFileAttributes; //!< external file attributes 4 bytes - quint32 m_uRelativeOffsetLocalHeader; //!< relative offset of local header 4 bytes - - QString m_strFileName; //!< file name (variable size) - QByteArray m_baExtraField; //!< extra field (variable size) - QString m_strComment; //!< file comment (variable size) - - // helpers - quint64 m_uLocalHeaderPosition; //!< Position of the local file header, after the signature field - quint64 m_uDataPosition; //!< Position in the file of the data - quint64 m_uCentralHeaderPosition; //!< Position of the central directory file header, after the signature field -}; - -class CZipEndRecord -{ -public: - CZipEndRecord() { clear(); } - ~CZipEndRecord() { } - - void clear(); - - bool read (QFile& file); - void write (QFile& file); - -public: - quint16 m_uDisk; //!< number of this disk 2 bytes - quint16 m_uDiskCentralDir; //!< number of the disk with the start of the central directory 2 bytes - quint16 m_uEntriesCentralDirDisk; //!< total number of entries in the central directory on this disk 2 bytes - quint16 m_uEntriesCentralDir; //!< total number of entries in the central directory 2 bytes - quint32 m_uCentralDirSize; //!< size of the central directory 4 bytes - quint32 m_uCentralDirOffset; //!< offset of start of central directory with respect to the starting disk number 4 bytes - - QString m_strComment; -}; - -//! Represents a file in a zip archive -class CZipFile -{ -public: - CZipFile() { m_pArchive = 0; clear(); } - CZipFile(CZipArchive *pArchive); - ~CZipFile(); - - void clear(); - inline bool isValid() const { return m_pArchive != 0; } - - //! Reads local file header - //! Position has to be after the signature field - //! After the operation, the current position in the file is after the file data - bool readHeader(); - - bool deflateToFile (QIODevice& dev); - - QByteArray deflateToByteArray(); - //QString deflateToString(); - -// bool read (FILE *fpt, unsigned uPosition); - - //! this file has a data descriptor - - inline QString fileName() const { return m_head.m_strFileName; } - -public: - CZipArchive *m_pArchive; - CZipFileHeader m_head; - -}; - -class CZipArchive -{ -public: - CZipArchive(); - ~CZipArchive(); - - enum OpenMode { OpenReadOnly, OpenCreate, OpenModify }; - - bool open (const QString& strFileName, const OpenMode om); - - CZipFile* findFile (const QString& strFileName); - - inline int fileCount() const { return m_listFiles.size(); } - const CZipFile* fileAt(const int i) const { return m_listFiles.at(i); } - CZipFile* fileAt(const int i) { return m_listFiles.at(i); } - -protected: - friend class CZipFile; - - QFile m_file; - QString m_strFileName; - - QList m_listFiles; - QList m_listEndRecords; -}; - -#endif - +/*************************************************************************** + * Copyright (C) 2003-2006 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. * + ***************************************************************************/ + +#ifndef OSZIPARCHIVE_H +#define OSZIPARCHIVE_H + +#include +#include +#include + +class CZipFile; +class CZipArchive; + +class CZipFileHeader +{ +public: + CZipFileHeader() { clear(); } + ~CZipFileHeader() {} + + void clear(); + void integrate(const CZipFileHeader& head); + + bool readLocalFileHeader(QFile& file); + void writeLocalFileHeader(QFile& file); + + bool readCentralDirectoryHeader(QFile& file); + void writeCentralDirectoryHeader(QFile& file); + + inline bool hasDataDescriptor() const { return m_uFlags & (1<<3) ? true : false; } +public: + quint16 m_uVersionMadeBy; //!< version made by 2 bytes + quint16 m_uVersionNeeded; //!< version needed to extract 2 bytes + quint16 m_uFlags; //!< general purpose bit flag 2 bytes + quint16 m_uCompression; //!< compression method 2 bytes + quint16 m_uTime; //!< last mod file time 2 bytes + quint16 m_uDate; //!< last mod file date 2 bytes + quint32 m_uCRC; //!< crc-32 4 bytes + quint32 m_uSizeCompressed; //!< compressed size 4 bytes + quint32 m_uSizeUncompressed; //!< uncompressed size 4 bytes + quint16 m_uDiskNumberStart; //!< disk number start 2 bytes + quint16 m_uInternalFileAttributes; //!< internal file attributes 2 bytes + quint32 m_uExternalFileAttributes; //!< external file attributes 4 bytes + quint32 m_uRelativeOffsetLocalHeader; //!< relative offset of local header 4 bytes + + QString m_strFileName; //!< file name (variable size) + QByteArray m_baExtraField; //!< extra field (variable size) + QString m_strComment; //!< file comment (variable size) + + // helpers + quint64 m_uLocalHeaderPosition; //!< Position of the local file header, after the signature field + quint64 m_uDataPosition; //!< Position in the file of the data + quint64 m_uCentralHeaderPosition; //!< Position of the central directory file header, after the signature field +}; + +class CZipEndRecord +{ +public: + CZipEndRecord() { clear(); } + ~CZipEndRecord() { } + + void clear(); + + bool read (QFile& file); + void write (QFile& file); + +public: + quint16 m_uDisk; //!< number of this disk 2 bytes + quint16 m_uDiskCentralDir; //!< number of the disk with the start of the central directory 2 bytes + quint16 m_uEntriesCentralDirDisk; //!< total number of entries in the central directory on this disk 2 bytes + quint16 m_uEntriesCentralDir; //!< total number of entries in the central directory 2 bytes + quint32 m_uCentralDirSize; //!< size of the central directory 4 bytes + quint32 m_uCentralDirOffset; //!< offset of start of central directory with respect to the starting disk number 4 bytes + + QString m_strComment; +}; + +//! Represents a file in a zip archive +class CZipFile +{ +public: + CZipFile() { m_pArchive = 0; clear(); } + CZipFile(CZipArchive *pArchive); + ~CZipFile(); + + void clear(); + inline bool isValid() const { return m_pArchive != 0; } + + //! Reads local file header + //! Position has to be after the signature field + //! After the operation, the current position in the file is after the file data + bool readHeader(); + + bool deflateToFile (QIODevice& dev); + + QByteArray deflateToByteArray(); + //QString deflateToString(); + +// bool read (FILE *fpt, unsigned uPosition); + + //! this file has a data descriptor + + inline QString fileName() const { return m_head.m_strFileName; } + +public: + CZipArchive *m_pArchive; + CZipFileHeader m_head; + +}; + +class CZipArchive +{ +public: + CZipArchive(); + ~CZipArchive(); + + enum OpenMode { OpenReadOnly, OpenCreate, OpenModify }; + + bool open (const QString& strFileName, const OpenMode om); + + CZipFile* findFile (const QString& strFileName); + + inline int fileCount() const { return m_listFiles.size(); } + const CZipFile* fileAt(const int i) const { return m_listFiles.at(i); } + CZipFile* fileAt(const int i) { return m_listFiles.at(i); } + +protected: + friend class CZipFile; + + QFile m_file; + QString m_strFileName; + + QList m_listFiles; + QList m_listEndRecords; +}; + +#endif + diff --git a/plotwidget.cpp b/plotwidget.cpp index 03e51b7..c84054f 100644 --- a/plotwidget.cpp +++ b/plotwidget.cpp @@ -1,426 +1,426 @@ -/*************************************************************************** - * 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 "plotwidget.h" -#include - -void CPlotWidgetPoint::clear() -{ -} - - -void CPlotWidgetCurve::clear() -{ - QList::clear(); -} - - -QRectF CPlotWidgetCurve::boundaries() const -{ -QRectF rect; - - for (int i=0; i rect.right()) - rect.setRight(p.x()); - - if (p.y() > rect.bottom()) - rect.setBottom(p.y()); - else if (p.y() < rect.top()) - rect.setTop(p.y()); - } - } - return rect; -} - -CPlotWidgetTic::CPlotWidgetTic(const double dPos, const QString& strText) -{ - clear(); - m_dPos = dPos; - m_strText = strText; -} - -CPlotWidgetTic::CPlotWidgetTic(const double dPos, const double dWidth, const QString& strText) -{ - clear(); - m_dPos = dPos; - m_dWidth = dWidth; - m_strText = strText; -} - -CPlotWidgetTic::CPlotWidgetTic(const double dPos, const QPixmap& pixmap) -{ - clear(); - m_dPos = dPos; - m_pixmap = pixmap; -} - -void CPlotWidgetTic::clear() -{ - m_dPos = 0.0; - m_dWidth = 0.0; - m_pen = QPen(Qt::DashLine); - m_pen.setColor(Qt::darkGray); - m_penFont = QPen(Qt::black); - m_pixmap = QPixmap(); - m_strText.clear(); - m_fillType = FillNone; - m_lineType = LinePlot; - m_iTextFlags = Qt::AlignHCenter | Qt::AlignTop; -} - -void CPlotWidgetTic::paintX (QPainter *pPainter, CPlotWidget *pWidget) const -{ -QPoint ptTL, ptBR; // TopLeft, BottomRight -QRect rectText; - - if (m_fillType != FillNone && m_dWidth > 0.0) - { - ptTL = pWidget->mapToPlot(QPointF(m_dPos, pWidget->m_rectData.top())); - ptBR = pWidget->mapToPlot(QPointF(m_dPos+m_dWidth, pWidget->m_rectData.bottom())); - - if (m_fillType == FillAll) - { - ptTL.setY(pWidget->m_rectPlot.top()); - ptBR.setY(pWidget->m_rectPlot.bottom()); - } - - if (ptTL.x() < pWidget->m_rectPlot.left()) - ptTL.setX(pWidget->m_rectPlot.left()); - - if (ptBR.x() > pWidget->m_rectPlot.right()) - ptBR.setX(pWidget->m_rectPlot.right()); - - pPainter->fillRect(QRect(ptTL, ptBR), m_brush); - - rectText = QRect( - QPoint(ptTL.x(), pWidget->m_rectPlot.bottom() + 5), - QPoint(ptBR.x(), pWidget->rect().bottom())); - } - else - { - rectText = QRect(); - } - - if (m_lineType != LineNone) - { - pPainter->setPen(m_pen); - pPainter->drawLine( - pWidget->mapToPlot(QPointF(m_dPos, pWidget->m_rectData.top())), - pWidget->mapToPlot(QPointF(m_dPos, pWidget->m_rectData.bottom()))); - } - - if (!m_strText.isEmpty()) - { - pPainter->setPen(m_penFont); - pPainter->drawText(rectText, m_iTextFlags, m_strText); - } -} - -void CPlotWidgetTic::paintY (QPainter *pPainter, CPlotWidget *pWidget) const -{ -QPoint ptTL, ptBR; // TopLeft, BottomRight -QRect rectText; - - if (m_fillType != FillNone && m_dWidth > 0.0) - { - ptTL = pWidget->mapToPlot(QPointF(pWidget->m_rectData.left(), m_dPos)); - ptBR = pWidget->mapToPlot(QPointF(pWidget->m_rectData.right(), m_dPos+m_dWidth)); - - if (m_fillType == FillAll) - { - ptTL.setX(pWidget->m_rectPlot.left()); - ptBR.setX(pWidget->m_rectPlot.right()); - } - - if (ptTL.y() < pWidget->m_rectPlot.top()) - ptTL.setY(pWidget->m_rectPlot.top()); - - if (ptBR.y() > pWidget->m_rectPlot.bottom()) - ptBR.setY(pWidget->m_rectPlot.bottom()); - - pPainter->fillRect(QRect(ptTL, ptBR), m_brush); - - rectText = QRect( - QPoint(3, ptTL.y()), - QPoint(pWidget->m_rectPlot.left()-3, ptBR.y())); - } - else - { - QFontMetrics fm(m_font); - QPoint pt = pWidget->mapToPlot(QPointF(pWidget->m_rectData.left(), m_dPos)); - rectText = QRect( - QPoint(3, pt.y()-fm.height()), - QPoint(pWidget->m_rectPlot.left()-3, pt.y()+fm.height())); - } - - if (m_lineType != LineNone) - { - pPainter->setPen(m_pen); - pPainter->drawLine( - pWidget->mapToPlot(QPointF(pWidget->m_rectData.left(), m_dPos)), - pWidget->mapToPlot(QPointF(pWidget->m_rectData.right(), m_dPos))); - } - - if (!m_pixmap.isNull()) - { - // Ziel-Mittelpunkt errechnen - QPoint pt = pWidget->mapToPlot(QPointF(0, m_dPos)); - pt.setX(pWidget->m_rectPlot.left() - m_pixmap.width()/2 - 5); - - // Linke obere Ecke errechnen und malen! - pt -= QPoint (m_pixmap.width()/2, m_pixmap.height()/2); - pPainter->drawPixmap(pt, m_pixmap); - } - - if (!m_strText.isEmpty()) - { - pPainter->setPen(m_penFont); - pPainter->drawText(rectText, m_iTextFlags, m_strText); - } -} - -CPlotWidget::CPlotWidget(QWidget *pParent) : QFrame(pParent) -{ - clear(); -} - -void CPlotWidget::clear() -{ - m_type = PlotLines; - m_bLimitAutoY = true; - m_bLimitAutoX = true; - m_dLimitXMin = 0.0; - m_dLimitXMax = 10.0; - m_dLimitYMin = 0.0; - m_dLimitYMax = 10.0; - m_dLimitXRound = 0.0; - m_dLimitYRound = 0.0; - m_dTicX = 0.0; - m_penTicX = QPen(Qt::DashLine); - m_penTicX.setColor(Qt::darkGray); - m_dTicY = 0.0; - m_penTicY = QPen(Qt::DashLine); - m_penTicY.setColor(Qt::darkGray); - m_iBorder = BorderLeft|BorderBottom; - m_iBorderDistTop = 5; - m_iBorderDistBottom = 5; - m_iBorderDistLeft = 5; - m_iBorderDistRight = 5; - m_brushPlotBkg = QBrush(Qt::white); - m_listTicX.clear(); - m_listTicY.clear(); - m_dBarWidth = 0.6; - m_dBarOffset = 0.0; - m_listCurves.clear(); -} - -void CPlotWidget::setBorderDistance(const int iLeft, const int iRight, const int iTop, const int iBottom) -{ - m_iBorderDistTop = iTop; - m_iBorderDistBottom = iBottom; - m_iBorderDistLeft = iLeft; - m_iBorderDistRight = iRight; -} - -void CPlotWidget::updateCache() -{ - m_rectPlot = plotArea(); - m_rectData = rectData(); -} - -QRect CPlotWidget::plotArea() const -{ -QRect rect = frameRect(); - rect.adjust(m_iBorderDistLeft, m_iBorderDistTop, -m_iBorderDistRight, -m_iBorderDistBottom); - return rect; -} - -QRectF CPlotWidget::rectData() const -{ -QRectF rect; - if (m_bLimitAutoX || m_bLimitAutoY) - { - for (int i=0; i 0.0) - { - rect.setLeft((unsigned)(rect.left() / m_dLimitXRound) * m_dLimitXRound); - rect.setRight((unsigned)(rect.right() / m_dLimitXRound + 1) * m_dLimitXRound); - } - - if (!m_bLimitAutoY) - { - rect.setTop(m_dLimitYMin); - rect.setBottom(m_dLimitYMax); - } - else if (m_dLimitYRound > 0.0) - { - rect.setTop((unsigned)(rect.top() / m_dLimitYRound) * m_dLimitYRound); - rect.setBottom((unsigned)(rect.bottom() / m_dLimitYRound + 1) * m_dLimitYRound); - } - return rect; -} - -QPoint CPlotWidget::mapToPlot (QPointF p) -{ -QPointF ret; -double m; - - // Umrechung X-Koordinate - m = ((double) (m_rectPlot.left() - m_rectPlot.right())) / (m_rectData.left() - m_rectData.right()); - ret.setX(m * (p.x() - m_rectData.left()) + m_rectPlot.left()); - - // Umrechnung Y-Koordinate (mit Spiegelung) - m = ((double) (m_rectPlot.bottom() - m_rectPlot.top())) / (m_rectData.top() - m_rectData.bottom()); - ret.setY(m * (p.y() - m_rectData.top()) + m_rectPlot.bottom()); - - return ret.toPoint(); -} - -void CPlotWidget::paintEvent (QPaintEvent *e) -{ -QList listPoints; -QList listTicsX, listTicsY; -double d=0.0; -int i=0; - - QFrame::paintEvent(e); - QPainter painter(this); - - updateCache(); - - // Draw Background - if (m_brushPlotBkg.style() != Qt::NoBrush) - { - painter.fillRect(m_rectPlot, m_brushPlotBkg); - } - - // Draw Tics - listTicsX = m_listTicX; - if (listTicsX.isEmpty() && m_dTicX > 0.0) - { - d = ((unsigned)(m_rectData.left() / m_dTicX)) * m_dTicX; - if (d < m_rectData.left()) d+=m_dTicX; - while (d <= m_rectData.right()) - { - CPlotWidgetTic tic(d, QString("%1").arg(d,0,'f',2)); - tic.setPen(m_penTicX); - listTicsX.append(tic); - d += m_dTicX; - } - } - listTicsY = m_listTicY; - if (listTicsY.isEmpty() && m_dTicY > 0.0) - { - d = ((unsigned)(m_rectData.top() / m_dTicY)) * m_dTicY; - if (d < m_rectData.top()) d+=m_dTicY; - while (d <= m_rectData.bottom()) - { - CPlotWidgetTic tic(d, QString("%1").arg(d,0,'f',2)); - tic.setPen(m_penTicY); - listTicsY.append(tic); - d += m_dTicY; - } - } - for (i=0; i + +void CPlotWidgetPoint::clear() +{ +} + + +void CPlotWidgetCurve::clear() +{ + QList::clear(); +} + + +QRectF CPlotWidgetCurve::boundaries() const +{ +QRectF rect; + + for (int i=0; i rect.right()) + rect.setRight(p.x()); + + if (p.y() > rect.bottom()) + rect.setBottom(p.y()); + else if (p.y() < rect.top()) + rect.setTop(p.y()); + } + } + return rect; +} + +CPlotWidgetTic::CPlotWidgetTic(const double dPos, const QString& strText) +{ + clear(); + m_dPos = dPos; + m_strText = strText; +} + +CPlotWidgetTic::CPlotWidgetTic(const double dPos, const double dWidth, const QString& strText) +{ + clear(); + m_dPos = dPos; + m_dWidth = dWidth; + m_strText = strText; +} + +CPlotWidgetTic::CPlotWidgetTic(const double dPos, const QPixmap& pixmap) +{ + clear(); + m_dPos = dPos; + m_pixmap = pixmap; +} + +void CPlotWidgetTic::clear() +{ + m_dPos = 0.0; + m_dWidth = 0.0; + m_pen = QPen(Qt::DashLine); + m_pen.setColor(Qt::darkGray); + m_penFont = QPen(Qt::black); + m_pixmap = QPixmap(); + m_strText.clear(); + m_fillType = FillNone; + m_lineType = LinePlot; + m_iTextFlags = Qt::AlignHCenter | Qt::AlignTop; +} + +void CPlotWidgetTic::paintX (QPainter *pPainter, CPlotWidget *pWidget) const +{ +QPoint ptTL, ptBR; // TopLeft, BottomRight +QRect rectText; + + if (m_fillType != FillNone && m_dWidth > 0.0) + { + ptTL = pWidget->mapToPlot(QPointF(m_dPos, pWidget->m_rectData.top())); + ptBR = pWidget->mapToPlot(QPointF(m_dPos+m_dWidth, pWidget->m_rectData.bottom())); + + if (m_fillType == FillAll) + { + ptTL.setY(pWidget->m_rectPlot.top()); + ptBR.setY(pWidget->m_rectPlot.bottom()); + } + + if (ptTL.x() < pWidget->m_rectPlot.left()) + ptTL.setX(pWidget->m_rectPlot.left()); + + if (ptBR.x() > pWidget->m_rectPlot.right()) + ptBR.setX(pWidget->m_rectPlot.right()); + + pPainter->fillRect(QRect(ptTL, ptBR), m_brush); + + rectText = QRect( + QPoint(ptTL.x(), pWidget->m_rectPlot.bottom() + 5), + QPoint(ptBR.x(), pWidget->rect().bottom())); + } + else + { + rectText = QRect(); + } + + if (m_lineType != LineNone) + { + pPainter->setPen(m_pen); + pPainter->drawLine( + pWidget->mapToPlot(QPointF(m_dPos, pWidget->m_rectData.top())), + pWidget->mapToPlot(QPointF(m_dPos, pWidget->m_rectData.bottom()))); + } + + if (!m_strText.isEmpty()) + { + pPainter->setPen(m_penFont); + pPainter->drawText(rectText, m_iTextFlags, m_strText); + } +} + +void CPlotWidgetTic::paintY (QPainter *pPainter, CPlotWidget *pWidget) const +{ +QPoint ptTL, ptBR; // TopLeft, BottomRight +QRect rectText; + + if (m_fillType != FillNone && m_dWidth > 0.0) + { + ptTL = pWidget->mapToPlot(QPointF(pWidget->m_rectData.left(), m_dPos)); + ptBR = pWidget->mapToPlot(QPointF(pWidget->m_rectData.right(), m_dPos+m_dWidth)); + + if (m_fillType == FillAll) + { + ptTL.setX(pWidget->m_rectPlot.left()); + ptBR.setX(pWidget->m_rectPlot.right()); + } + + if (ptTL.y() < pWidget->m_rectPlot.top()) + ptTL.setY(pWidget->m_rectPlot.top()); + + if (ptBR.y() > pWidget->m_rectPlot.bottom()) + ptBR.setY(pWidget->m_rectPlot.bottom()); + + pPainter->fillRect(QRect(ptTL, ptBR), m_brush); + + rectText = QRect( + QPoint(3, ptTL.y()), + QPoint(pWidget->m_rectPlot.left()-3, ptBR.y())); + } + else + { + QFontMetrics fm(m_font); + QPoint pt = pWidget->mapToPlot(QPointF(pWidget->m_rectData.left(), m_dPos)); + rectText = QRect( + QPoint(3, pt.y()-fm.height()), + QPoint(pWidget->m_rectPlot.left()-3, pt.y()+fm.height())); + } + + if (m_lineType != LineNone) + { + pPainter->setPen(m_pen); + pPainter->drawLine( + pWidget->mapToPlot(QPointF(pWidget->m_rectData.left(), m_dPos)), + pWidget->mapToPlot(QPointF(pWidget->m_rectData.right(), m_dPos))); + } + + if (!m_pixmap.isNull()) + { + // Ziel-Mittelpunkt errechnen + QPoint pt = pWidget->mapToPlot(QPointF(0, m_dPos)); + pt.setX(pWidget->m_rectPlot.left() - m_pixmap.width()/2 - 5); + + // Linke obere Ecke errechnen und malen! + pt -= QPoint (m_pixmap.width()/2, m_pixmap.height()/2); + pPainter->drawPixmap(pt, m_pixmap); + } + + if (!m_strText.isEmpty()) + { + pPainter->setPen(m_penFont); + pPainter->drawText(rectText, m_iTextFlags, m_strText); + } +} + +CPlotWidget::CPlotWidget(QWidget *pParent) : QFrame(pParent) +{ + clear(); +} + +void CPlotWidget::clear() +{ + m_type = PlotLines; + m_bLimitAutoY = true; + m_bLimitAutoX = true; + m_dLimitXMin = 0.0; + m_dLimitXMax = 10.0; + m_dLimitYMin = 0.0; + m_dLimitYMax = 10.0; + m_dLimitXRound = 0.0; + m_dLimitYRound = 0.0; + m_dTicX = 0.0; + m_penTicX = QPen(Qt::DashLine); + m_penTicX.setColor(Qt::darkGray); + m_dTicY = 0.0; + m_penTicY = QPen(Qt::DashLine); + m_penTicY.setColor(Qt::darkGray); + m_iBorder = BorderLeft|BorderBottom; + m_iBorderDistTop = 5; + m_iBorderDistBottom = 5; + m_iBorderDistLeft = 5; + m_iBorderDistRight = 5; + m_brushPlotBkg = QBrush(Qt::white); + m_listTicX.clear(); + m_listTicY.clear(); + m_dBarWidth = 0.6; + m_dBarOffset = 0.0; + m_listCurves.clear(); +} + +void CPlotWidget::setBorderDistance(const int iLeft, const int iRight, const int iTop, const int iBottom) +{ + m_iBorderDistTop = iTop; + m_iBorderDistBottom = iBottom; + m_iBorderDistLeft = iLeft; + m_iBorderDistRight = iRight; +} + +void CPlotWidget::updateCache() +{ + m_rectPlot = plotArea(); + m_rectData = rectData(); +} + +QRect CPlotWidget::plotArea() const +{ +QRect rect = frameRect(); + rect.adjust(m_iBorderDistLeft, m_iBorderDistTop, -m_iBorderDistRight, -m_iBorderDistBottom); + return rect; +} + +QRectF CPlotWidget::rectData() const +{ +QRectF rect; + if (m_bLimitAutoX || m_bLimitAutoY) + { + for (int i=0; i 0.0) + { + rect.setLeft((unsigned)(rect.left() / m_dLimitXRound) * m_dLimitXRound); + rect.setRight((unsigned)(rect.right() / m_dLimitXRound + 1) * m_dLimitXRound); + } + + if (!m_bLimitAutoY) + { + rect.setTop(m_dLimitYMin); + rect.setBottom(m_dLimitYMax); + } + else if (m_dLimitYRound > 0.0) + { + rect.setTop((unsigned)(rect.top() / m_dLimitYRound) * m_dLimitYRound); + rect.setBottom((unsigned)(rect.bottom() / m_dLimitYRound + 1) * m_dLimitYRound); + } + return rect; +} + +QPoint CPlotWidget::mapToPlot (QPointF p) +{ +QPointF ret; +double m; + + // Umrechung X-Koordinate + m = ((double) (m_rectPlot.left() - m_rectPlot.right())) / (m_rectData.left() - m_rectData.right()); + ret.setX(m * (p.x() - m_rectData.left()) + m_rectPlot.left()); + + // Umrechnung Y-Koordinate (mit Spiegelung) + m = ((double) (m_rectPlot.bottom() - m_rectPlot.top())) / (m_rectData.top() - m_rectData.bottom()); + ret.setY(m * (p.y() - m_rectData.top()) + m_rectPlot.bottom()); + + return ret.toPoint(); +} + +void CPlotWidget::paintEvent (QPaintEvent *e) +{ +QList listPoints; +QList listTicsX, listTicsY; +double d=0.0; +int i=0; + + QFrame::paintEvent(e); + QPainter painter(this); + + updateCache(); + + // Draw Background + if (m_brushPlotBkg.style() != Qt::NoBrush) + { + painter.fillRect(m_rectPlot, m_brushPlotBkg); + } + + // Draw Tics + listTicsX = m_listTicX; + if (listTicsX.isEmpty() && m_dTicX > 0.0) + { + d = ((unsigned)(m_rectData.left() / m_dTicX)) * m_dTicX; + if (d < m_rectData.left()) d+=m_dTicX; + while (d <= m_rectData.right()) + { + CPlotWidgetTic tic(d, QString("%1").arg(d,0,'f',2)); + tic.setPen(m_penTicX); + listTicsX.append(tic); + d += m_dTicX; + } + } + listTicsY = m_listTicY; + if (listTicsY.isEmpty() && m_dTicY > 0.0) + { + d = ((unsigned)(m_rectData.top() / m_dTicY)) * m_dTicY; + if (d < m_rectData.top()) d+=m_dTicY; + while (d <= m_rectData.bottom()) + { + CPlotWidgetTic tic(d, QString("%1").arg(d,0,'f',2)); + tic.setPen(m_penTicY); + listTicsY.append(tic); + d += m_dTicY; + } + } + for (i=0; i -#include -#include -#include -#include -#include -#include - -class CPlotWidgetPoint; -class CPlotWidgetCurve; -class CPlotWidgetTic; -class CPlotWidget; - -class CPlotWidgetPoint : public QPointF -{ -public: - CPlotWidgetPoint() { clear(); } - CPlotWidgetPoint(const double x, const double y) : QPointF(x,y) {} - ~CPlotWidgetPoint() {} - - void clear(); - -protected: -}; - -class CPlotWidgetCurve : public QList -{ -public: - CPlotWidgetCurve() { clear(); } - ~CPlotWidgetCurve() {} - - void clear(); - QRectF boundaries() const; - - inline void setPen(const QPen& pen) { m_pen = pen; } - inline QPen pen() const { return m_pen; } - inline void setBrush(const QBrush& brush) { m_brush = brush; } - inline QBrush brush() const { return m_brush; } - -protected: - QPen m_pen; - QBrush m_brush; -}; - -class CPlotWidgetTic -{ -public: - enum LineType { LineNone, LineShort, LinePlot, LineFull }; - enum FillType { FillNone, FillPlot, FillAll }; - - CPlotWidgetTic() { clear(); } - CPlotWidgetTic(const double dPos, const QString& strText); - CPlotWidgetTic(const double dPos, const double dWidth, const QString& strText); - CPlotWidgetTic(const double dPos, const QPixmap& pixmap); - ~CPlotWidgetTic() {} - - void clear(); - - inline void setPen(const QPen& pen) { m_pen = pen; } - inline QPen pen() const { return m_pen; } - inline void setBrush(const QBrush& brush) { m_brush = brush; } - inline QBrush brush() const { return m_brush; } - inline void setFont(const QFont& font) { m_font = font; } - inline QFont font() const { return m_font; } - inline void setFontPen(const QPen& pen) { m_penFont = pen; } - inline QPen fontPen() const { return m_penFont; } - inline void setPos(const double dPos) { m_dPos = dPos; } - inline double pos() const { return m_dPos; } - inline void setWidth(const double dWidth) { m_dWidth = dWidth; } - inline double width() const { return m_dWidth; } - inline void setFillType(const FillType t) { m_fillType = t; } - inline FillType fillType () const { return m_fillType; } - inline void setLineType(const LineType t) { m_lineType = t; } - inline LineType lineType () const { return m_lineType; } - inline void setText(const QString& str) { m_strText = str; } - inline QString text() const { return m_strText; } - inline void setTextFlags (const int iFlags) { m_iTextFlags = iFlags; } - inline int textFlags() const { return m_iTextFlags; } - - void paintX (QPainter *pPainter, CPlotWidget *pWidget) const; - void paintY (QPainter *pPainter, CPlotWidget *pWidget) const; - -protected: - double m_dPos; - - // Beschriftung - QPixmap m_pixmap; - QString m_strText; - QFont m_font; - QPen m_penFont; - int m_iTextFlags; - - // Line - QPen m_pen; - LineType m_lineType; - - // Background - double m_dWidth; - QBrush m_brush; - FillType m_fillType; -}; - -class CPlotWidget : public QFrame -{ -Q_OBJECT -public: - enum PlotType { PlotPoints, PlotLines, PlotBarsSum, PlotBars }; - enum Border { BorderTop=0x01, BorderBottom=0x02, BorderLeft=0x04, BorderRight=0x08 }; - - CPlotWidget(QWidget *pParent=0); - ~CPlotWidget() {} - - void clear(); - - inline void clearCurves() { m_listCurves.clear(); } - inline void appendCurve (const CPlotWidgetCurve& c) { m_listCurves.append(c); } - - QRect plotArea() const; - QRectF rectData() const; - - inline void setType (const PlotType t) { m_type = t; } - inline PlotType type() const { return m_type; } - inline void setLimitX(const double min, const double max) { m_dLimitXMin = min; m_dLimitXMax = max; m_bLimitAutoX = false; } - inline void setLimitY(const double min, const double max) { m_dLimitYMin = min; m_dLimitYMax = max; m_bLimitAutoY = false; } - inline void setAutoLimitX(const bool bAutoLimit) { m_bLimitAutoX = bAutoLimit; } - inline void setAutoLimitY(const bool bAutoLimit) { m_bLimitAutoY = bAutoLimit; } - inline void setAutoLimitRoundX(const double dRound) { m_dLimitXRound = dRound; } - inline void setAutoLimitRoundY(const double dRound) { m_dLimitYRound = dRound; } - - inline void setLabelX(const QString& str) { m_strLabelX = str; } - inline void setLabelY(const QString& str) { m_strLabelY = str; } - - inline void setPlotBackground (const QBrush& brush) { m_brushPlotBkg = brush; } - inline void setBorderPen(const QPen& pen) { m_penBorder = pen; } - inline QPen borderPen() const { return m_penBorder; } - inline void setBorder(const int iBorder=BorderLeft|BorderBottom) { m_iBorder = iBorder; } - inline int border() const { return m_iBorder; } - void setBorderDistance(const int iLeft, const int iRight, const int iTop, const int iBottom); - - inline void setTicX(const double dTic) { m_dTicX = dTic; } - inline void setTicY(const double dTic) { m_dTicY = dTic; } - inline void setTicXPen(const QPen& pen) { m_penTicX = pen; } - inline void setTicYPen(const QPen& pen) { m_penTicY = pen; } - inline void setTicListX (const QList& list) { m_listTicX = list; } - inline void setTicListY (const QList& list) { m_listTicY = list; } - - inline void setBarWidth(const double d) { m_dBarWidth = d; } - inline void setBarOffset(const double d) { m_dBarOffset = d; } - -protected: - virtual void paintEvent (QPaintEvent *e); - void updateCache(); - QPoint mapToPlot (QPointF p); - - friend class CPlotWidgetTic; - -protected: - PlotType m_type; - QList m_listCurves; - - double m_dLimitXMin, m_dLimitXMax, m_dLimitYMin, m_dLimitYMax; - double m_dLimitXRound, m_dLimitYRound; - bool m_bLimitAutoX, m_bLimitAutoY; - - // Border - QBrush m_brushPlotBkg; - QPen m_penBorder; - int m_iBorder; //!< Logische Verknüpfung aus BorderTop, BorderBottom, BorderLeft, BorderRight - int m_iBorderDistTop; - int m_iBorderDistBottom; - int m_iBorderDistLeft; - int m_iBorderDistRight; - - // Tics & Labels - QList m_listTicX; - QList m_listTicY; - double m_dTicX; - QPen m_penTicX; - double m_dTicY; - QPen m_penTicY; - QString m_strLabelX; - QString m_strLabelY; - - // Bars - double m_dBarWidth; - double m_dBarOffset; - - // Cache - QRect m_rectPlot; - QRectF m_rectData; -}; - +/*************************************************************************** + * 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. * + ***************************************************************************/ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +class CPlotWidgetPoint; +class CPlotWidgetCurve; +class CPlotWidgetTic; +class CPlotWidget; + +class CPlotWidgetPoint : public QPointF +{ +public: + CPlotWidgetPoint() { clear(); } + CPlotWidgetPoint(const double x, const double y) : QPointF(x,y) {} + ~CPlotWidgetPoint() {} + + void clear(); + +protected: +}; + +class CPlotWidgetCurve : public QList +{ +public: + CPlotWidgetCurve() { clear(); } + ~CPlotWidgetCurve() {} + + void clear(); + QRectF boundaries() const; + + inline void setPen(const QPen& pen) { m_pen = pen; } + inline QPen pen() const { return m_pen; } + inline void setBrush(const QBrush& brush) { m_brush = brush; } + inline QBrush brush() const { return m_brush; } + +protected: + QPen m_pen; + QBrush m_brush; +}; + +class CPlotWidgetTic +{ +public: + enum LineType { LineNone, LineShort, LinePlot, LineFull }; + enum FillType { FillNone, FillPlot, FillAll }; + + CPlotWidgetTic() { clear(); } + CPlotWidgetTic(const double dPos, const QString& strText); + CPlotWidgetTic(const double dPos, const double dWidth, const QString& strText); + CPlotWidgetTic(const double dPos, const QPixmap& pixmap); + ~CPlotWidgetTic() {} + + void clear(); + + inline void setPen(const QPen& pen) { m_pen = pen; } + inline QPen pen() const { return m_pen; } + inline void setBrush(const QBrush& brush) { m_brush = brush; } + inline QBrush brush() const { return m_brush; } + inline void setFont(const QFont& font) { m_font = font; } + inline QFont font() const { return m_font; } + inline void setFontPen(const QPen& pen) { m_penFont = pen; } + inline QPen fontPen() const { return m_penFont; } + inline void setPos(const double dPos) { m_dPos = dPos; } + inline double pos() const { return m_dPos; } + inline void setWidth(const double dWidth) { m_dWidth = dWidth; } + inline double width() const { return m_dWidth; } + inline void setFillType(const FillType t) { m_fillType = t; } + inline FillType fillType () const { return m_fillType; } + inline void setLineType(const LineType t) { m_lineType = t; } + inline LineType lineType () const { return m_lineType; } + inline void setText(const QString& str) { m_strText = str; } + inline QString text() const { return m_strText; } + inline void setTextFlags (const int iFlags) { m_iTextFlags = iFlags; } + inline int textFlags() const { return m_iTextFlags; } + + void paintX (QPainter *pPainter, CPlotWidget *pWidget) const; + void paintY (QPainter *pPainter, CPlotWidget *pWidget) const; + +protected: + double m_dPos; + + // Beschriftung + QPixmap m_pixmap; + QString m_strText; + QFont m_font; + QPen m_penFont; + int m_iTextFlags; + + // Line + QPen m_pen; + LineType m_lineType; + + // Background + double m_dWidth; + QBrush m_brush; + FillType m_fillType; +}; + +class CPlotWidget : public QFrame +{ +Q_OBJECT +public: + enum PlotType { PlotPoints, PlotLines, PlotBarsSum, PlotBars }; + enum Border { BorderTop=0x01, BorderBottom=0x02, BorderLeft=0x04, BorderRight=0x08 }; + + CPlotWidget(QWidget *pParent=0); + ~CPlotWidget() {} + + void clear(); + + inline void clearCurves() { m_listCurves.clear(); } + inline void appendCurve (const CPlotWidgetCurve& c) { m_listCurves.append(c); } + + QRect plotArea() const; + QRectF rectData() const; + + inline void setType (const PlotType t) { m_type = t; } + inline PlotType type() const { return m_type; } + inline void setLimitX(const double min, const double max) { m_dLimitXMin = min; m_dLimitXMax = max; m_bLimitAutoX = false; } + inline void setLimitY(const double min, const double max) { m_dLimitYMin = min; m_dLimitYMax = max; m_bLimitAutoY = false; } + inline void setAutoLimitX(const bool bAutoLimit) { m_bLimitAutoX = bAutoLimit; } + inline void setAutoLimitY(const bool bAutoLimit) { m_bLimitAutoY = bAutoLimit; } + inline void setAutoLimitRoundX(const double dRound) { m_dLimitXRound = dRound; } + inline void setAutoLimitRoundY(const double dRound) { m_dLimitYRound = dRound; } + + inline void setLabelX(const QString& str) { m_strLabelX = str; } + inline void setLabelY(const QString& str) { m_strLabelY = str; } + + inline void setPlotBackground (const QBrush& brush) { m_brushPlotBkg = brush; } + inline void setBorderPen(const QPen& pen) { m_penBorder = pen; } + inline QPen borderPen() const { return m_penBorder; } + inline void setBorder(const int iBorder=BorderLeft|BorderBottom) { m_iBorder = iBorder; } + inline int border() const { return m_iBorder; } + void setBorderDistance(const int iLeft, const int iRight, const int iTop, const int iBottom); + + inline void setTicX(const double dTic) { m_dTicX = dTic; } + inline void setTicY(const double dTic) { m_dTicY = dTic; } + inline void setTicXPen(const QPen& pen) { m_penTicX = pen; } + inline void setTicYPen(const QPen& pen) { m_penTicY = pen; } + inline void setTicListX (const QList& list) { m_listTicX = list; } + inline void setTicListY (const QList& list) { m_listTicY = list; } + + inline void setBarWidth(const double d) { m_dBarWidth = d; } + inline void setBarOffset(const double d) { m_dBarOffset = d; } + +protected: + virtual void paintEvent (QPaintEvent *e); + void updateCache(); + QPoint mapToPlot (QPointF p); + + friend class CPlotWidgetTic; + +protected: + PlotType m_type; + QList m_listCurves; + + double m_dLimitXMin, m_dLimitXMax, m_dLimitYMin, m_dLimitYMax; + double m_dLimitXRound, m_dLimitYRound; + bool m_bLimitAutoX, m_bLimitAutoY; + + // Border + QBrush m_brushPlotBkg; + QPen m_penBorder; + int m_iBorder; //!< Logische Verknüpfung aus BorderTop, BorderBottom, BorderLeft, BorderRight + int m_iBorderDistTop; + int m_iBorderDistBottom; + int m_iBorderDistLeft; + int m_iBorderDistRight; + + // Tics & Labels + QList m_listTicX; + QList m_listTicY; + double m_dTicX; + QPen m_penTicX; + double m_dTicY; + QPen m_penTicY; + QString m_strLabelX; + QString m_strLabelY; + + // Bars + double m_dBarWidth; + double m_dBarOffset; + + // Cache + QRect m_rectPlot; + QRectF m_rectData; +}; + diff --git a/question.cpp b/question.cpp index 304716a..1d46ffe 100644 --- a/question.cpp +++ b/question.cpp @@ -1,709 +1,709 @@ -/*************************************************************************** - * 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 "question.h" -#include "catalog.h" -#include "tools.h" - -#include -#include -#include -#include - - -void CDayStatistic::clear() -{ - m_date = QDate(); - m_uClickedWrong = 0; - m_uClickedCorrect = 0; - m_uTimeExpeditureCorrect = 0; - m_uTimeExpeditureWrong = 0; - m_dLevel = 0.0; -} - -CDayStatistic& CDayStatistic::operator += (const CDayStatistic& ds) -{ - m_date = ds.m_date; - m_uClickedWrong += ds.m_uClickedWrong; - m_uClickedCorrect += ds.m_uClickedCorrect; - m_uTimeExpeditureCorrect += ds.m_uTimeExpeditureCorrect; - m_uTimeExpeditureWrong += ds.m_uTimeExpeditureWrong; - m_dLevel += ds.m_dLevel; - return *this; -} - -void CDayStatistic::debug() const -{ - qDebug("-- Day statistic %s --", qPrintable(m_date.toString(Qt::LocalDate))); - if (m_uClickedCorrect + m_uClickedWrong == 0) - { - qDebug (" No clicks"); - return; - } - qDebug(" %i correct + %i wrong = %i", m_uClickedCorrect, m_uClickedWrong, m_uClickedCorrect + m_uClickedWrong); - qDebug(" %i ms + %i ms = %i ms", m_uTimeExpeditureCorrect, m_uTimeExpeditureWrong, m_uTimeExpeditureCorrect + m_uTimeExpeditureWrong); - qDebug(" Level = %lf", m_dLevel); -} - -QString CQuestion::tr (const char *sourceText, const char *comment) -{ - return QCoreApplication::translate("CQuestion", sourceText, comment); -} - -unsigned CQuestion::waitDaysForRepeat (const unsigned uLevel) -{ - switch (uLevel) - { - case LEVEL_VERYOFTEN: - return 0; - case LEVEL_OFTEN: - return 1; - case LEVEL_NORMAL: - return 2; - case LEVEL_RARE: - return 4; - case LEVEL_VERYRARE: - return 8; - default: - case LEVEL_EXTREMERARE: - return 16; - } -} - - -void CQuestion::clear() -{ - m_pParentChapter = 0; - m_strId.clear(); - m_strText.clear(); - m_listAnswer.clear(); - m_strlGroups.clear(); - m_uErrorPoints = 1; - - m_uCorrectAnswers=0; - m_uCorrectSuccessive=0; - m_uWrongAnswers=0; - m_uWrongSuccessive=0; - m_uLevel=0; -} - -QString CQuestion::plainText() const -{ -QTextDocument doc; - doc.setHtml(m_strText); - return doc.toPlainText(); -} - -QString CQuestion::firstTextLine() const -{ -QString str = plainText(); -int i = str.indexOf('\n'); - return str.left (i); -} - -QString CQuestion::showText(CCatalog *pCatalog) const -{ -QString str, strHints; - str +="

Frage " + id() + "

"; - str += "

" + text() + "


"; - - str += "

"; - for (int i=0; i %1 ").arg(QChar('A' + i)); - // Text - str += ""; - str += ""; - } - str += "
" + m_listAnswer[i].text() + "

"; - strHints = pCatalog->hintText(id()); - if (!strHints.isEmpty()) - { - str += "

Hilfestellung

" + strHints; - } - str += "

Abfrageverlauf

"; - str += lastClickedTextExtended(); - str += " " + repeatDateTextExtended(); - if (!m_listAnswerClicked.isEmpty()) - { - str += "

"; - for (int i=0; i"; - } - str += "
Datum/Uhrzeit Antwort Richtig? Benötigte Zeit
" + ac.answerText() + ""; - if (ac.isCorrect(m_listAnswer)) - str += tr("richtig"); - else - str += tr("falsch"); - str += "" + QString ("%1").arg(ac.neededTimeText()) + "
"; - } - //str +="

Statistik

"; - return str; -} - -QString CQuestion::learnText(CCatalog *pCatalog, const bool bShowId, const bool bShowHints) -{ -QString str; - - if (bShowId) str += "" + id() + " "; - str += text() + "
"; - - str += "

"; - for (int i=0; i %1 ").arg(QChar('A' + i)); - // Text - str += ""; - } - str += "
"; - //CHEAT: if (mixedAnswerAt(i).isCorrect()) str += "OK "; - str += learnAnswerAt(i).text(); - str += "

"; - - if (bShowHints) - { - QString strHints = pCatalog->hintText(id()); - if (!strHints.isEmpty()) str += "
" + strHints; - } - return str; -} - -bool CQuestion::load (QDomElement elem) -{ -QString str; - if (elem.tagName () != "question") return false; - if (!elem.hasAttribute ("id")) return false; - - clear (); - m_strId = elem.attribute ("id"); - m_uErrorPoints = elem.attribute("errorpoints", "1").toUInt(); - str = elem.attribute("groups"); - str = str.replace(' ', ';').replace(',', ';'); - m_strlGroups = str.split(";", QString::SkipEmptyParts); - - if (m_uErrorPoints == 0) return false; - - QDomNode n = elem.firstChild(); - while (!n.isNull()) - { - if (n.isElement ()) - { - QDomElement e = n.toElement (); - str = e.text(); - if (e.tagName () == "textquestion") - m_strText = str; - else if (e.tagName () == "textanswer") - m_listAnswer.append(CAnswer(str, QVariant(e.attribute("correct", "false")).toBool())); - } - n = n.nextSibling(); - } - return true; -} - - -QString CQuestion::removeTempPath(const QString& strText) -{ -QString str=strText, strFileName, strBaseName; -int idxStart=-1, idxEnd=0; - while ((idxStart = str.indexOf(QRegExp("src\\s*=\\s*\""), idxEnd)) != -1) - { - idxStart = str.indexOf('"', idxStart)+1; - idxEnd = str.indexOf('"', idxStart); - strFileName = str.mid(idxStart, idxEnd - idxStart); - if (strFileName.isEmpty()) continue; - QFileInfo fi(strFileName); - strBaseName = fi.fileName(); - strBaseName = strBaseName.left(strBaseName.lastIndexOf('.')); - str.replace(strFileName, strBaseName); - } - return str; -} - -void CQuestion::save (QDomElement& parent, QDomDocument& doc) -{ -QDomElement elemRoot = doc.createElement("question"); - elemRoot.setAttribute("id", id()); - if (m_uErrorPoints > 1) - elemRoot.setAttribute("errorpoints", QString("%1").arg(m_uErrorPoints)); - if (!m_strlGroups.isEmpty()) - elemRoot.setAttribute("groups", m_strlGroups.join("; ")); - - parent.appendChild(elemRoot); - - QDomElement elemQuestion = doc.createElement("textquestion"); - QDomText textQuestion = doc.createTextNode(removeTempPath(text())); - elemQuestion.appendChild(textQuestion); - elemRoot.appendChild(elemQuestion); - - // save answers - for (int i=0; i"; - iAnswerCount = countCorrectAnswer(); - if (iAnswerCount != 1) - str += QObject::tr("Die Frage %1 hat nicht exakt eine richtige Antwort, sondern %2 Stück.").arg(id()).arg(iAnswerCount) + "
"; - for (int j=0; j"; - } - if (text().isEmpty()) - str += QObject::tr("Die Frage %1 enthält keinen Text.").arg(id()) + "
"; - - return str; -} - -void CQuestion::mixAnswers(const bool bMix) -{ -unsigned u1=0, u2=0; - m_listMixedAnswer.clear(); - if (!bMix) return; - for (int i=0; i>i) & 0x0001) << m_listMixedAnswer.at(i); - } - return u; -} - -void CQuestion::registerAnswerClicked (const unsigned uAnswerMask, const unsigned uNeededTime) -{ - m_listAnswerClicked.append(CAnswerClicked(orderedAnswerMask(uAnswerMask), uNeededTime)); - if (correctAnswerMask() == uAnswerMask) - { - m_uCorrectAnswers++; - m_uCorrectSuccessive++; - m_uWrongSuccessive = 0; - //if (m_uLevel > 0 && m_uLevel < LEVEL_MAX || m_uLevel == 0 && m_uCorrectSuccessive == 2) - if (m_uLevel < LEVEL_MAX) - m_uLevel++; - } - else - { - m_uWrongAnswers++; - m_uCorrectSuccessive = 0; - m_uWrongSuccessive++; - if (m_uLevel > 0) - m_uLevel--; - } -/* if (m_pParentChapter) - m_pParentChapter->updateStatistic(true); -*/ -} - -QString CQuestion::levelIconName(const unsigned uLevel) -{ -// if (uLevel <= LEVEL_VERYRARE) - return QString(":/icons/16x16/level%1.png").arg(uLevel); -// else -// return ":/icons/16x16/button_ok.png"; -} - -QIcon CQuestion::levelIcon(const unsigned uLevel) -{ - return QIcon (levelIconName(uLevel)); -} - -QString CQuestion::levelText(const unsigned uLevel) -{ - switch (uLevel) - { - case LEVEL_VERYOFTEN: - return QObject::tr("Ahnungslos"); - case LEVEL_OFTEN: - return QObject::tr("Anfänger"); - case LEVEL_NORMAL: - return QObject::tr("Fortgeschritten"); - case LEVEL_RARE: - return QObject::tr("Experte"); - case LEVEL_VERYRARE: - return QObject::tr("Freak"); - case LEVEL_EXTREMERARE: - return QObject::tr("Professor"); - default: - return QObject::tr("unbekannt"); - } -} - -QDateTime CQuestion::firstClicked() const -{ - // Vorraussetzung: m_listAnswerClicked ist nach Datum aufsteigend sortiert - if (m_listAnswerClicked.size() > 0) - return m_listAnswerClicked.at(0).dateTime(); - return QDateTime(); -} - -QDateTime CQuestion::lastClicked() const -{ - // Vorraussetzung: m_listAnswerClicked ist nach Datum aufsteigend sortiert - if (m_listAnswerClicked.size() > 0) - return m_listAnswerClicked.at(m_listAnswerClicked.size()-1).dateTime(); - return QDateTime(); -} - -QString CQuestion::lastClickedText() const -{ -QDateTime dtLastClicked = lastClicked(); -unsigned uSecs = dtLastClicked.secsTo (QDateTime::currentDateTime()); -unsigned uDays = dtLastClicked.daysTo (QDateTime::currentDateTime()); - - if (!dtLastClicked.isValid()) - return QString(); - - if (uSecs < 60) - return tr("vor < 1 min"); - else if (uSecs < 3600) - return (tr("vor %1 min").arg(uSecs / 60)); - else if (uDays == 1) - return (tr("gestern")); - else if (uDays > 1) - return (tr("vor %1 Tagen").arg(uDays)); - else - return (tr("vor %1 h").arg(uSecs / 3600)); -} - -QString CQuestion::lastClickedTextExtended() const -{ -QDateTime dtLastClicked = lastClicked(); -QString strRet; -unsigned uLevelOld=0, uLevelNew=0; - if (!dtLastClicked.isValid()) - return tr("Die Frage wurde noch nie beantwortet."); - - strRet = tr("Die Frage wurde %1 (%2) zuletzt beantwortet. ", "%1=gestern, vor x Tagen, ... / %2 = exaktes Datum m. Uhrzeit").arg(lastClickedText(), dtLastClicked.toString(Qt::LocalDate)); - - uLevelOld = levelAtEndOfDay(dtLastClicked.date().addDays(-1)); - uLevelNew = levelAtEndOfDay(dtLastClicked.date()); - - if (uLevelOld == uLevelNew) - strRet += tr("Die Einstufung des Lernfortschritts änderte sich an diesem Tag nicht."); - else - strRet += tr("Der Lernfortschritt änderte sich an diesem Tag von '%1' auf '%2'.").arg(levelText(uLevelOld), levelText(uLevelNew)); - - return strRet; -} - -/*! -\return Datum, an dem die Frage wiederholt werden soll. -Gibt es kein solches Datum, so wird ein ungültiges Datum (QDate::isValid()) zurückgegeben. -Hinweis: Das Datum kann auch in der Vergangenheit liegen! -*/ - -QDate CQuestion::repeatDate() const -{ -QDate dLastClicked = lastClicked().date(); -unsigned uLevelOld=0, uLevelNew=0; - - if (!dLastClicked.isValid()) return QDate(); - - uLevelOld = levelAtEndOfDay(dLastClicked.addDays(-1)); - uLevelNew = levelAtEndOfDay(dLastClicked); - - if (uLevelNew <= uLevelOld && uLevelNew != LEVEL_MAX) - return dLastClicked; - - return dLastClicked.addDays(waitDaysForRepeat(m_uLevel)); -} - -bool CQuestion::isRepeatToday() const -{ -QDate d = repeatDate(); - - if (d.isValid() && d <= QDate::currentDate()) return true; - return false; -} - -QString CQuestion::repeatDateText() const -{ -QDate d = repeatDate(); - if (!d.isValid()) return QString(); - if (d <= QDate::currentDate()) - return tr("heute"); - else if (QDate::currentDate().addDays(1) == d) - return tr("morgen"); - else - return tr("in %1 Tagen").arg(QDate::currentDate().daysTo(d)); -} - -QString CQuestion::repeatDateTextExtended() const -{ -QString strRet; -unsigned uLevelOld=0, uLevelNew=0, uLevelTarget=0; -QDate dLastClicked = lastClicked().date(); - - if (!dLastClicked.isValid()) return QString(); - uLevelOld = levelAtEndOfDay(dLastClicked.addDays(-1)); - uLevelNew = levelAtEndOfDay(dLastClicked); - if (uLevelNew > uLevelOld) - uLevelTarget = uLevelNew + 1; - else - uLevelTarget = uLevelOld + 1; - if (uLevelTarget > LEVEL_MAX) uLevelTarget = LEVEL_MAX; - if (uLevelNew == LEVEL_MAX) - strRet = tr("Es wird empfohlen, die Frage %1 zu wiederholen.").arg(repeatDateText()); - else - strRet = tr("Es wird empfohlen, die Frage %1 zu wiederholen, um den Lernfortschritt '%2' zu erreichen.") - .arg(repeatDateText()).arg(levelText(uLevelTarget)); - return strRet; -} - -unsigned CQuestion::levelAtEndOfDay(const QDate& date) const -{ -unsigned uLevel = 0, uCorrectSuccessive=0; -CAnswerClicked ac; - // Vorraussetzung: m_listAnswerClicked ist nach Datum aufsteigend sortiert - for (int i=0; i date) return uLevel; - if (ac.isCorrect(m_listAnswer)) - { - uCorrectSuccessive++; - if (uLevel < LEVEL_MAX) - { - //if (uLevel == LEVEL_VERYOFTEN && uCorrectSuccessive == 2 || uLevel > LEVEL_VERYOFTEN) - uLevel++; - } - } - else - { - uCorrectSuccessive = 0; - if (uLevel > LEVEL_VERYOFTEN) uLevel--; - } - } - return uLevel; -} - -CDayStatistic CQuestion::dayStatistic(const QDate& date) const -{ -CAnswerClicked ac; -CDayStatistic ds; - ds.m_date = date; - if (date.isNull()) - ds.m_dLevel = levelAtEndOfDay(QDate::currentDate()); - else - ds.m_dLevel = levelAtEndOfDay(date); - - // Vorraussetzung: m_listAnswerClicked ist nach Datum aufsteigend sortiert - for (int i=0; i date) break; - if (ac.dateTime().date() < date) continue; - //if (ac.dateTime().date() != date) continue; - } - if (ac.isCorrect(m_listAnswer)) - { - ds.m_uClickedCorrect++; - ds.m_uTimeExpeditureCorrect += ac.neededTime(); - } - else - { - ds.m_uClickedWrong++; - ds.m_uTimeExpeditureWrong += ac.neededTime(); - } - } - - return ds; -} - - -/*! -\return true: Wenn die Frage heute zum ersten Mal beantwortet wurde und noch in der Kategorie LEVEL_VERYOFTEN ist -*/ -bool CQuestion::isLearningNew() const -{ - if (m_uLevel != LEVEL_VERYOFTEN) return false; - if (m_listAnswerClicked.size() > 0 - && m_listAnswerClicked.at(0).dateTime().date() != QDate::currentDate()) return false; - return true; -} - -/*! -\return true: Die Frage wurde gestern oder früher zum ersten Mal beantwortet -*/ -bool CQuestion::isKnownQuestion() const -{ - if (m_listAnswerClicked.size() > 0 - && m_listAnswerClicked.at(0).dateTime().date() < QDate::currentDate()) return true; - return false; -} +/*************************************************************************** + * 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 "question.h" +#include "catalog.h" +#include "tools.h" + +#include +#include +#include +#include + + +void CDayStatistic::clear() +{ + m_date = QDate(); + m_uClickedWrong = 0; + m_uClickedCorrect = 0; + m_uTimeExpeditureCorrect = 0; + m_uTimeExpeditureWrong = 0; + m_dLevel = 0.0; +} + +CDayStatistic& CDayStatistic::operator += (const CDayStatistic& ds) +{ + m_date = ds.m_date; + m_uClickedWrong += ds.m_uClickedWrong; + m_uClickedCorrect += ds.m_uClickedCorrect; + m_uTimeExpeditureCorrect += ds.m_uTimeExpeditureCorrect; + m_uTimeExpeditureWrong += ds.m_uTimeExpeditureWrong; + m_dLevel += ds.m_dLevel; + return *this; +} + +void CDayStatistic::debug() const +{ + qDebug("-- Day statistic %s --", qPrintable(m_date.toString(Qt::LocalDate))); + if (m_uClickedCorrect + m_uClickedWrong == 0) + { + qDebug (" No clicks"); + return; + } + qDebug(" %i correct + %i wrong = %i", m_uClickedCorrect, m_uClickedWrong, m_uClickedCorrect + m_uClickedWrong); + qDebug(" %i ms + %i ms = %i ms", m_uTimeExpeditureCorrect, m_uTimeExpeditureWrong, m_uTimeExpeditureCorrect + m_uTimeExpeditureWrong); + qDebug(" Level = %lf", m_dLevel); +} + +QString CQuestion::tr (const char *sourceText, const char *comment) +{ + return QCoreApplication::translate("CQuestion", sourceText, comment); +} + +unsigned CQuestion::waitDaysForRepeat (const unsigned uLevel) +{ + switch (uLevel) + { + case LEVEL_VERYOFTEN: + return 0; + case LEVEL_OFTEN: + return 1; + case LEVEL_NORMAL: + return 2; + case LEVEL_RARE: + return 4; + case LEVEL_VERYRARE: + return 8; + default: + case LEVEL_EXTREMERARE: + return 16; + } +} + + +void CQuestion::clear() +{ + m_pParentChapter = 0; + m_strId.clear(); + m_strText.clear(); + m_listAnswer.clear(); + m_strlGroups.clear(); + m_uErrorPoints = 1; + + m_uCorrectAnswers=0; + m_uCorrectSuccessive=0; + m_uWrongAnswers=0; + m_uWrongSuccessive=0; + m_uLevel=0; +} + +QString CQuestion::plainText() const +{ +QTextDocument doc; + doc.setHtml(m_strText); + return doc.toPlainText(); +} + +QString CQuestion::firstTextLine() const +{ +QString str = plainText(); +int i = str.indexOf('\n'); + return str.left (i); +} + +QString CQuestion::showText(CCatalog *pCatalog) const +{ +QString str, strHints; + str +="

Frage " + id() + "

"; + str += "

" + text() + "


"; + + str += "

"; + for (int i=0; i %1 ").arg(QChar('A' + i)); + // Text + str += ""; + str += ""; + } + str += "
" + m_listAnswer[i].text() + "

"; + strHints = pCatalog->hintText(id()); + if (!strHints.isEmpty()) + { + str += "

Hilfestellung

" + strHints; + } + str += "

Abfrageverlauf

"; + str += lastClickedTextExtended(); + str += " " + repeatDateTextExtended(); + if (!m_listAnswerClicked.isEmpty()) + { + str += "

"; + for (int i=0; i"; + } + str += "
Datum/Uhrzeit Antwort Richtig? Benötigte Zeit
" + ac.answerText() + ""; + if (ac.isCorrect(m_listAnswer)) + str += tr("richtig"); + else + str += tr("falsch"); + str += "" + QString ("%1").arg(ac.neededTimeText()) + "
"; + } + //str +="

Statistik

"; + return str; +} + +QString CQuestion::learnText(CCatalog *pCatalog, const bool bShowId, const bool bShowHints) +{ +QString str; + + if (bShowId) str += "" + id() + " "; + str += text() + "
"; + + str += "

"; + for (int i=0; i %1 ").arg(QChar('A' + i)); + // Text + str += ""; + } + str += "
"; + //CHEAT: if (mixedAnswerAt(i).isCorrect()) str += "OK "; + str += learnAnswerAt(i).text(); + str += "

"; + + if (bShowHints) + { + QString strHints = pCatalog->hintText(id()); + if (!strHints.isEmpty()) str += "
" + strHints; + } + return str; +} + +bool CQuestion::load (QDomElement elem) +{ +QString str; + if (elem.tagName () != "question") return false; + if (!elem.hasAttribute ("id")) return false; + + clear (); + m_strId = elem.attribute ("id"); + m_uErrorPoints = elem.attribute("errorpoints", "1").toUInt(); + str = elem.attribute("groups"); + str = str.replace(' ', ';').replace(',', ';'); + m_strlGroups = str.split(";", QString::SkipEmptyParts); + + if (m_uErrorPoints == 0) return false; + + QDomNode n = elem.firstChild(); + while (!n.isNull()) + { + if (n.isElement ()) + { + QDomElement e = n.toElement (); + str = e.text(); + if (e.tagName () == "textquestion") + m_strText = str; + else if (e.tagName () == "textanswer") + m_listAnswer.append(CAnswer(str, QVariant(e.attribute("correct", "false")).toBool())); + } + n = n.nextSibling(); + } + return true; +} + + +QString CQuestion::removeTempPath(const QString& strText) +{ +QString str=strText, strFileName, strBaseName; +int idxStart=-1, idxEnd=0; + while ((idxStart = str.indexOf(QRegExp("src\\s*=\\s*\""), idxEnd)) != -1) + { + idxStart = str.indexOf('"', idxStart)+1; + idxEnd = str.indexOf('"', idxStart); + strFileName = str.mid(idxStart, idxEnd - idxStart); + if (strFileName.isEmpty()) continue; + QFileInfo fi(strFileName); + strBaseName = fi.fileName(); + strBaseName = strBaseName.left(strBaseName.lastIndexOf('.')); + str.replace(strFileName, strBaseName); + } + return str; +} + +void CQuestion::save (QDomElement& parent, QDomDocument& doc) +{ +QDomElement elemRoot = doc.createElement("question"); + elemRoot.setAttribute("id", id()); + if (m_uErrorPoints > 1) + elemRoot.setAttribute("errorpoints", QString("%1").arg(m_uErrorPoints)); + if (!m_strlGroups.isEmpty()) + elemRoot.setAttribute("groups", m_strlGroups.join("; ")); + + parent.appendChild(elemRoot); + + QDomElement elemQuestion = doc.createElement("textquestion"); + QDomText textQuestion = doc.createTextNode(removeTempPath(text())); + elemQuestion.appendChild(textQuestion); + elemRoot.appendChild(elemQuestion); + + // save answers + for (int i=0; i"; + iAnswerCount = countCorrectAnswer(); + if (iAnswerCount != 1) + str += QObject::tr("Die Frage %1 hat nicht exakt eine richtige Antwort, sondern %2 Stück.").arg(id()).arg(iAnswerCount) + "
"; + for (int j=0; j"; + } + if (text().isEmpty()) + str += QObject::tr("Die Frage %1 enthält keinen Text.").arg(id()) + "
"; + + return str; +} + +void CQuestion::mixAnswers(const bool bMix) +{ +unsigned u1=0, u2=0; + m_listMixedAnswer.clear(); + if (!bMix) return; + for (int i=0; i>i) & 0x0001) << m_listMixedAnswer.at(i); + } + return u; +} + +void CQuestion::registerAnswerClicked (const unsigned uAnswerMask, const unsigned uNeededTime) +{ + m_listAnswerClicked.append(CAnswerClicked(orderedAnswerMask(uAnswerMask), uNeededTime)); + if (correctAnswerMask() == uAnswerMask) + { + m_uCorrectAnswers++; + m_uCorrectSuccessive++; + m_uWrongSuccessive = 0; + //if (m_uLevel > 0 && m_uLevel < LEVEL_MAX || m_uLevel == 0 && m_uCorrectSuccessive == 2) + if (m_uLevel < LEVEL_MAX) + m_uLevel++; + } + else + { + m_uWrongAnswers++; + m_uCorrectSuccessive = 0; + m_uWrongSuccessive++; + if (m_uLevel > 0) + m_uLevel--; + } +/* if (m_pParentChapter) + m_pParentChapter->updateStatistic(true); +*/ +} + +QString CQuestion::levelIconName(const unsigned uLevel) +{ +// if (uLevel <= LEVEL_VERYRARE) + return QString(":/icons/16x16/level%1.png").arg(uLevel); +// else +// return ":/icons/16x16/button_ok.png"; +} + +QIcon CQuestion::levelIcon(const unsigned uLevel) +{ + return QIcon (levelIconName(uLevel)); +} + +QString CQuestion::levelText(const unsigned uLevel) +{ + switch (uLevel) + { + case LEVEL_VERYOFTEN: + return QObject::tr("Ahnungslos"); + case LEVEL_OFTEN: + return QObject::tr("Anfänger"); + case LEVEL_NORMAL: + return QObject::tr("Fortgeschritten"); + case LEVEL_RARE: + return QObject::tr("Experte"); + case LEVEL_VERYRARE: + return QObject::tr("Freak"); + case LEVEL_EXTREMERARE: + return QObject::tr("Professor"); + default: + return QObject::tr("unbekannt"); + } +} + +QDateTime CQuestion::firstClicked() const +{ + // Vorraussetzung: m_listAnswerClicked ist nach Datum aufsteigend sortiert + if (m_listAnswerClicked.size() > 0) + return m_listAnswerClicked.at(0).dateTime(); + return QDateTime(); +} + +QDateTime CQuestion::lastClicked() const +{ + // Vorraussetzung: m_listAnswerClicked ist nach Datum aufsteigend sortiert + if (m_listAnswerClicked.size() > 0) + return m_listAnswerClicked.at(m_listAnswerClicked.size()-1).dateTime(); + return QDateTime(); +} + +QString CQuestion::lastClickedText() const +{ +QDateTime dtLastClicked = lastClicked(); +unsigned uSecs = dtLastClicked.secsTo (QDateTime::currentDateTime()); +unsigned uDays = dtLastClicked.daysTo (QDateTime::currentDateTime()); + + if (!dtLastClicked.isValid()) + return QString(); + + if (uSecs < 60) + return tr("vor < 1 min"); + else if (uSecs < 3600) + return (tr("vor %1 min").arg(uSecs / 60)); + else if (uDays == 1) + return (tr("gestern")); + else if (uDays > 1) + return (tr("vor %1 Tagen").arg(uDays)); + else + return (tr("vor %1 h").arg(uSecs / 3600)); +} + +QString CQuestion::lastClickedTextExtended() const +{ +QDateTime dtLastClicked = lastClicked(); +QString strRet; +unsigned uLevelOld=0, uLevelNew=0; + if (!dtLastClicked.isValid()) + return tr("Die Frage wurde noch nie beantwortet."); + + strRet = tr("Die Frage wurde %1 (%2) zuletzt beantwortet. ", "%1=gestern, vor x Tagen, ... / %2 = exaktes Datum m. Uhrzeit").arg(lastClickedText(), dtLastClicked.toString(Qt::LocalDate)); + + uLevelOld = levelAtEndOfDay(dtLastClicked.date().addDays(-1)); + uLevelNew = levelAtEndOfDay(dtLastClicked.date()); + + if (uLevelOld == uLevelNew) + strRet += tr("Die Einstufung des Lernfortschritts änderte sich an diesem Tag nicht."); + else + strRet += tr("Der Lernfortschritt änderte sich an diesem Tag von '%1' auf '%2'.").arg(levelText(uLevelOld), levelText(uLevelNew)); + + return strRet; +} + +/*! +\return Datum, an dem die Frage wiederholt werden soll. +Gibt es kein solches Datum, so wird ein ungültiges Datum (QDate::isValid()) zurückgegeben. +Hinweis: Das Datum kann auch in der Vergangenheit liegen! +*/ + +QDate CQuestion::repeatDate() const +{ +QDate dLastClicked = lastClicked().date(); +unsigned uLevelOld=0, uLevelNew=0; + + if (!dLastClicked.isValid()) return QDate(); + + uLevelOld = levelAtEndOfDay(dLastClicked.addDays(-1)); + uLevelNew = levelAtEndOfDay(dLastClicked); + + if (uLevelNew <= uLevelOld && uLevelNew != LEVEL_MAX) + return dLastClicked; + + return dLastClicked.addDays(waitDaysForRepeat(m_uLevel)); +} + +bool CQuestion::isRepeatToday() const +{ +QDate d = repeatDate(); + + if (d.isValid() && d <= QDate::currentDate()) return true; + return false; +} + +QString CQuestion::repeatDateText() const +{ +QDate d = repeatDate(); + if (!d.isValid()) return QString(); + if (d <= QDate::currentDate()) + return tr("heute"); + else if (QDate::currentDate().addDays(1) == d) + return tr("morgen"); + else + return tr("in %1 Tagen").arg(QDate::currentDate().daysTo(d)); +} + +QString CQuestion::repeatDateTextExtended() const +{ +QString strRet; +unsigned uLevelOld=0, uLevelNew=0, uLevelTarget=0; +QDate dLastClicked = lastClicked().date(); + + if (!dLastClicked.isValid()) return QString(); + uLevelOld = levelAtEndOfDay(dLastClicked.addDays(-1)); + uLevelNew = levelAtEndOfDay(dLastClicked); + if (uLevelNew > uLevelOld) + uLevelTarget = uLevelNew + 1; + else + uLevelTarget = uLevelOld + 1; + if (uLevelTarget > LEVEL_MAX) uLevelTarget = LEVEL_MAX; + if (uLevelNew == LEVEL_MAX) + strRet = tr("Es wird empfohlen, die Frage %1 zu wiederholen.").arg(repeatDateText()); + else + strRet = tr("Es wird empfohlen, die Frage %1 zu wiederholen, um den Lernfortschritt '%2' zu erreichen.") + .arg(repeatDateText()).arg(levelText(uLevelTarget)); + return strRet; +} + +unsigned CQuestion::levelAtEndOfDay(const QDate& date) const +{ +unsigned uLevel = 0, uCorrectSuccessive=0; +CAnswerClicked ac; + // Vorraussetzung: m_listAnswerClicked ist nach Datum aufsteigend sortiert + for (int i=0; i date) return uLevel; + if (ac.isCorrect(m_listAnswer)) + { + uCorrectSuccessive++; + if (uLevel < LEVEL_MAX) + { + //if (uLevel == LEVEL_VERYOFTEN && uCorrectSuccessive == 2 || uLevel > LEVEL_VERYOFTEN) + uLevel++; + } + } + else + { + uCorrectSuccessive = 0; + if (uLevel > LEVEL_VERYOFTEN) uLevel--; + } + } + return uLevel; +} + +CDayStatistic CQuestion::dayStatistic(const QDate& date) const +{ +CAnswerClicked ac; +CDayStatistic ds; + ds.m_date = date; + if (date.isNull()) + ds.m_dLevel = levelAtEndOfDay(QDate::currentDate()); + else + ds.m_dLevel = levelAtEndOfDay(date); + + // Vorraussetzung: m_listAnswerClicked ist nach Datum aufsteigend sortiert + for (int i=0; i date) break; + if (ac.dateTime().date() < date) continue; + //if (ac.dateTime().date() != date) continue; + } + if (ac.isCorrect(m_listAnswer)) + { + ds.m_uClickedCorrect++; + ds.m_uTimeExpeditureCorrect += ac.neededTime(); + } + else + { + ds.m_uClickedWrong++; + ds.m_uTimeExpeditureWrong += ac.neededTime(); + } + } + + return ds; +} + + +/*! +\return true: Wenn die Frage heute zum ersten Mal beantwortet wurde und noch in der Kategorie LEVEL_VERYOFTEN ist +*/ +bool CQuestion::isLearningNew() const +{ + if (m_uLevel != LEVEL_VERYOFTEN) return false; + if (m_listAnswerClicked.size() > 0 + && m_listAnswerClicked.at(0).dateTime().date() != QDate::currentDate()) return false; + return true; +} + +/*! +\return true: Die Frage wurde gestern oder früher zum ersten Mal beantwortet +*/ +bool CQuestion::isKnownQuestion() const +{ + if (m_listAnswerClicked.size() > 0 + && m_listAnswerClicked.at(0).dateTime().date() < QDate::currentDate()) return true; + return false; +} diff --git a/question.h b/question.h index c6005b8..8553e07 100644 --- a/question.h +++ b/question.h @@ -1,247 +1,247 @@ -/*************************************************************************** - * 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. * - ***************************************************************************/ - -#ifndef QUESTION_H -#define QUESTION_H - -#include -#include -#include -#include -#include -#include -#include - -#include "answer.h" -#include "helper.h" - -unsigned afu_random (const unsigned uMin, const unsigned uMax); - -class CChapter; - -#define LEVEL_VERYOFTEN 0 -#define LEVEL_OFTEN 1 -#define LEVEL_NORMAL 2 -#define LEVEL_RARE 3 -#define LEVEL_VERYRARE 4 -#define LEVEL_EXTREMERARE 5 -#define LEVEL_MAX 5 - -class CDayStatistic -{ -public: - CDayStatistic() { clear(); } - ~CDayStatistic() {} - - void clear(); - - inline QDate date() const { return m_date; } - - inline unsigned clickedCount() const { return m_uClickedWrong + m_uClickedCorrect; } - inline unsigned clickedCorrect() const { return m_uClickedCorrect; } - inline unsigned clickedWrong() const { return m_uClickedWrong; } - inline unsigned timeExpediture() const { return m_uTimeExpeditureCorrect + m_uTimeExpeditureWrong; } - inline unsigned timeExpeditureCorrect() const { return m_uTimeExpeditureCorrect; } - inline unsigned timeExpeditureWrong() const { return m_uTimeExpeditureWrong; } - - inline unsigned levelRounded() const { return (unsigned) (m_dLevel + 0.5); } - inline double level() const { return m_dLevel; } - - CDayStatistic& operator += (const CDayStatistic& ds); - - void debug() const; - -protected: - QDate m_date; - unsigned m_uClickedWrong; - unsigned m_uClickedCorrect; - unsigned m_uTimeExpeditureCorrect; - unsigned m_uTimeExpeditureWrong; - double m_dLevel; - - friend class CQuestion; - friend class CChapter; -}; - -//! CQuestion speichert eine Frage mit allen Antworten und der Klick-Statistik. - -class CQuestion -{ -public: - //! Standard-Konstruktor - /*! Initialisiert die Klasse, indem die Funktion clear() aufgerufen wird. */ - CQuestion() { clear(); } - //! Standard-Destruktor - /*! In der Standard-Implementierung tut der Destruktor nichts. */ - ~CQuestion() {} - - /** @name Basisdaten - * Auslesen und setzen der Basisdaten einer Frage - */ - //@{ - //! Zurücksetzen aller Werte - /*! Es werden alle Daten der Frage gelöscht. */ - void clear(); - - //! Elternkapitel abfragen - /*! - \return Elternkapitel m_pParentChapter - \sa CChapter, setParentChapter(), m_pParentChapter - */ - inline CChapter* parentChapter() const { return m_pParentChapter; } - - //! Elternkapitel setzen - /*! - Durch diese Funktion wird lediglich die Variable m_pParentChapter gesetzt, jedoch nicht die Statistiken - des Eltern-Kapitels aktualisiert. - - \param pChapter Das zu setzende Elternkapitel der Frage - \sa CChapter, parentChapter(), m_pParentChapter - */ - inline void setParentChapter(CChapter *pChapter) { m_pParentChapter = pChapter; } - //! ID abfragen - inline QString id() const { return m_strId; } - //! Text abfragen - inline QString text() const { return m_strText; } - - //! ID setzen - inline void setId(const QString& strId) { m_strId = strId; } - //! Text setzen - inline void setText(const QString& strText) { m_strText = strText; } - - //! Anhängen eines Textes an den Fragentext - /*! - \param strText Anzuhängender Text - \sa m_strText - */ - inline void appendText(const QString& strText) { m_strText += strText; } - //@} - - QString plainText() const; - QString firstTextLine() const; - - QString showText(CCatalog *pCatalog) const; - QString learnText(CCatalog *pCatalog, const bool bShowId=true, const bool bShowHints=false); - - inline unsigned errorPoints() const { return m_uErrorPoints; } - inline void setErrorPoints (const unsigned u) { m_uErrorPoints = u; } - - QStringList groups() const { return m_strlGroups; } - - bool load (QDomElement elem); - void save (QDomElement& parent, QDomDocument& doc); - static QString removeTempPath(const QString& strText); - bool loadLearnStatistic (QDomElement elem); - bool saveLearnStatistic (QDomElement& parent, QDomDocument& doc); - QString checkForErrors() const; - - // answers - inline int countAnswer() const { return m_listAnswer.size(); } - inline const CAnswer& answerAt(int i) const { return m_listAnswer.at(i); } - inline CAnswer& answerAt(int i) { return m_listAnswer[i]; } - inline void appendAnswer(const CAnswer& a) { m_listAnswer.append(a); } - int countCorrectAnswer() const; - - // helpers -// inline bool hasHints() const { return !m_listHint.isEmpty(); } - - // learning - void mixAnswers(const bool bMix=true); - const CAnswer& learnAnswerAt(int i) const; - unsigned correctAnswerMask() const; - inline bool isCorrectAnswer (const unsigned uAnswerMask) const { return uAnswerMask == correctAnswerMask(); } - unsigned orderedAnswerMask(const unsigned uMixedAnswerMask); - void registerAnswerClicked (const unsigned uAnswerMask, const unsigned uNeededTime); - static QString answerMaskToString(const unsigned uAnswerMask); - QString correctionText(const unsigned uAnswerMask); - - // statistics - //! Abfragehäufigkeit der Frage - inline unsigned level() const { return m_uLevel; } - inline QString levelIconName() const { return levelIconName (m_uLevel); } - inline QIcon levelIcon() const { return levelIcon (m_uLevel); } - inline QPixmap levelPixmap() const { return QPixmap(levelIconName (m_uLevel)); } - inline QString levelText() const { return levelText (m_uLevel); } - inline int clickedCount() const { return m_listAnswerClicked.size(); } - inline unsigned clickedCorrect() const { return m_uCorrectAnswers; } - inline unsigned clickedWrong() const { return m_uWrongAnswers; } - inline unsigned clickedCorrectSuccessive() const { return m_uCorrectSuccessive; } - inline unsigned clickedWrongSuccessive() const { return m_uWrongSuccessive; } - inline bool isNeverAsked() const { return m_listAnswerClicked.isEmpty(); } - - unsigned levelAtEndOfDay(const QDate& date) const; //!< Lernfortschritt am Ende eines bestimmten Tages - CDayStatistic dayStatistic(const QDate& date) const; - - QDateTime firstClicked() const; - QDateTime lastClicked() const; - QString lastClickedText() const; - QString lastClickedTextExtended() const; - QDate repeatDate() const; //!< Wiederhol-Datum der Frage bestimmen - QString repeatDateText() const; - QString repeatDateTextExtended() const; - bool isRepeatToday() const; //!< Frage sollte heute wiederholt werden - - bool isLearningNew() const; //!< Ist dies eine neue Frage, die gerade gelernt wird? - bool isKnownQuestion() const; //!< Ist die Frage schon (länger) bekannt? - - //! Name des Icons einer bestimmten Abfragehäufigkeit ermitteln - static QString levelIconName(const unsigned uLevel); - //! Icon einer bestimmten Abfragehäufigkeit - static QIcon levelIcon(const unsigned uLevel); - //! Name einer bestimmten Abfragehäufigkeit - static QString levelText(const unsigned uLevel); - static QString tr (const char *sourceText, const char *comment=0); - //! Berechnung der Wartezeit für eine bestimmte Abfragehäufigkeit - static unsigned waitDaysForRepeat (const unsigned uLevel); - -protected: - - CChapter *m_pParentChapter; //!< Zeiger auf das (Eltern-) Kapitel, zu dem die Frage gehört. - // masterdata - QString m_strId; //!< ID der Frage - QString m_strText; //!< Fragentext - QList m_listAnswer; //!< Liste mit Antworten - unsigned m_uErrorPoints; //!< Fehlerpunkte bei Falschantwort in Prüfung - QStringList m_strlGroups; //!< Liste der Klassen/Gruppen, zu denen diese Frage gehört - - // learning - //! Durchmischte Antworten - /*! Die Liste enthält die Positionen der Liste m_listAnswer */ - QList m_listMixedAnswer; - - // Statistics - QList m_listAnswerClicked; //!< Liste mit sämtlichen Antwort-Klicks - unsigned m_uCorrectAnswers; //!< Anzahl der richtigen Antworten - unsigned m_uCorrectSuccessive; //!< Anzahl der richtigen Antworten zuletzt hintereinander - unsigned m_uWrongAnswers; //!< Anzahl der falschen Antworten - unsigned m_uWrongSuccessive; //!< Anzahl der falschen Antworten zuletzt hintereinander - - //! Aktuelle Stufe der Abfragehäufigkeit - /*! - Stufe 0: sehr häufige ... Stufe 5: sehr seltene Abfrage - - Die Abfragehäufigkeit wird nicht in der Statistik-Datei gespeichert, sondern bei jedem Laden der - Datei anhand der Beantwortungen in der Vergangenheit neu berechnet. - */ - unsigned m_uLevel; -}; - -#endif +/*************************************************************************** + * 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. * + ***************************************************************************/ + +#ifndef QUESTION_H +#define QUESTION_H + +#include +#include +#include +#include +#include +#include +#include + +#include "answer.h" +#include "helper.h" + +unsigned afu_random (const unsigned uMin, const unsigned uMax); + +class CChapter; + +#define LEVEL_VERYOFTEN 0 +#define LEVEL_OFTEN 1 +#define LEVEL_NORMAL 2 +#define LEVEL_RARE 3 +#define LEVEL_VERYRARE 4 +#define LEVEL_EXTREMERARE 5 +#define LEVEL_MAX 5 + +class CDayStatistic +{ +public: + CDayStatistic() { clear(); } + ~CDayStatistic() {} + + void clear(); + + inline QDate date() const { return m_date; } + + inline unsigned clickedCount() const { return m_uClickedWrong + m_uClickedCorrect; } + inline unsigned clickedCorrect() const { return m_uClickedCorrect; } + inline unsigned clickedWrong() const { return m_uClickedWrong; } + inline unsigned timeExpediture() const { return m_uTimeExpeditureCorrect + m_uTimeExpeditureWrong; } + inline unsigned timeExpeditureCorrect() const { return m_uTimeExpeditureCorrect; } + inline unsigned timeExpeditureWrong() const { return m_uTimeExpeditureWrong; } + + inline unsigned levelRounded() const { return (unsigned) (m_dLevel + 0.5); } + inline double level() const { return m_dLevel; } + + CDayStatistic& operator += (const CDayStatistic& ds); + + void debug() const; + +protected: + QDate m_date; + unsigned m_uClickedWrong; + unsigned m_uClickedCorrect; + unsigned m_uTimeExpeditureCorrect; + unsigned m_uTimeExpeditureWrong; + double m_dLevel; + + friend class CQuestion; + friend class CChapter; +}; + +//! CQuestion speichert eine Frage mit allen Antworten und der Klick-Statistik. + +class CQuestion +{ +public: + //! Standard-Konstruktor + /*! Initialisiert die Klasse, indem die Funktion clear() aufgerufen wird. */ + CQuestion() { clear(); } + //! Standard-Destruktor + /*! In der Standard-Implementierung tut der Destruktor nichts. */ + ~CQuestion() {} + + /** @name Basisdaten + * Auslesen und setzen der Basisdaten einer Frage + */ + //@{ + //! Zurücksetzen aller Werte + /*! Es werden alle Daten der Frage gelöscht. */ + void clear(); + + //! Elternkapitel abfragen + /*! + \return Elternkapitel m_pParentChapter + \sa CChapter, setParentChapter(), m_pParentChapter + */ + inline CChapter* parentChapter() const { return m_pParentChapter; } + + //! Elternkapitel setzen + /*! + Durch diese Funktion wird lediglich die Variable m_pParentChapter gesetzt, jedoch nicht die Statistiken + des Eltern-Kapitels aktualisiert. + + \param pChapter Das zu setzende Elternkapitel der Frage + \sa CChapter, parentChapter(), m_pParentChapter + */ + inline void setParentChapter(CChapter *pChapter) { m_pParentChapter = pChapter; } + //! ID abfragen + inline QString id() const { return m_strId; } + //! Text abfragen + inline QString text() const { return m_strText; } + + //! ID setzen + inline void setId(const QString& strId) { m_strId = strId; } + //! Text setzen + inline void setText(const QString& strText) { m_strText = strText; } + + //! Anhängen eines Textes an den Fragentext + /*! + \param strText Anzuhängender Text + \sa m_strText + */ + inline void appendText(const QString& strText) { m_strText += strText; } + //@} + + QString plainText() const; + QString firstTextLine() const; + + QString showText(CCatalog *pCatalog) const; + QString learnText(CCatalog *pCatalog, const bool bShowId=true, const bool bShowHints=false); + + inline unsigned errorPoints() const { return m_uErrorPoints; } + inline void setErrorPoints (const unsigned u) { m_uErrorPoints = u; } + + QStringList groups() const { return m_strlGroups; } + + bool load (QDomElement elem); + void save (QDomElement& parent, QDomDocument& doc); + static QString removeTempPath(const QString& strText); + bool loadLearnStatistic (QDomElement elem); + bool saveLearnStatistic (QDomElement& parent, QDomDocument& doc); + QString checkForErrors() const; + + // answers + inline int countAnswer() const { return m_listAnswer.size(); } + inline const CAnswer& answerAt(int i) const { return m_listAnswer.at(i); } + inline CAnswer& answerAt(int i) { return m_listAnswer[i]; } + inline void appendAnswer(const CAnswer& a) { m_listAnswer.append(a); } + int countCorrectAnswer() const; + + // helpers +// inline bool hasHints() const { return !m_listHint.isEmpty(); } + + // learning + void mixAnswers(const bool bMix=true); + const CAnswer& learnAnswerAt(int i) const; + unsigned correctAnswerMask() const; + inline bool isCorrectAnswer (const unsigned uAnswerMask) const { return uAnswerMask == correctAnswerMask(); } + unsigned orderedAnswerMask(const unsigned uMixedAnswerMask); + void registerAnswerClicked (const unsigned uAnswerMask, const unsigned uNeededTime); + static QString answerMaskToString(const unsigned uAnswerMask); + QString correctionText(const unsigned uAnswerMask); + + // statistics + //! Abfragehäufigkeit der Frage + inline unsigned level() const { return m_uLevel; } + inline QString levelIconName() const { return levelIconName (m_uLevel); } + inline QIcon levelIcon() const { return levelIcon (m_uLevel); } + inline QPixmap levelPixmap() const { return QPixmap(levelIconName (m_uLevel)); } + inline QString levelText() const { return levelText (m_uLevel); } + inline int clickedCount() const { return m_listAnswerClicked.size(); } + inline unsigned clickedCorrect() const { return m_uCorrectAnswers; } + inline unsigned clickedWrong() const { return m_uWrongAnswers; } + inline unsigned clickedCorrectSuccessive() const { return m_uCorrectSuccessive; } + inline unsigned clickedWrongSuccessive() const { return m_uWrongSuccessive; } + inline bool isNeverAsked() const { return m_listAnswerClicked.isEmpty(); } + + unsigned levelAtEndOfDay(const QDate& date) const; //!< Lernfortschritt am Ende eines bestimmten Tages + CDayStatistic dayStatistic(const QDate& date) const; + + QDateTime firstClicked() const; + QDateTime lastClicked() const; + QString lastClickedText() const; + QString lastClickedTextExtended() const; + QDate repeatDate() const; //!< Wiederhol-Datum der Frage bestimmen + QString repeatDateText() const; + QString repeatDateTextExtended() const; + bool isRepeatToday() const; //!< Frage sollte heute wiederholt werden + + bool isLearningNew() const; //!< Ist dies eine neue Frage, die gerade gelernt wird? + bool isKnownQuestion() const; //!< Ist die Frage schon (länger) bekannt? + + //! Name des Icons einer bestimmten Abfragehäufigkeit ermitteln + static QString levelIconName(const unsigned uLevel); + //! Icon einer bestimmten Abfragehäufigkeit + static QIcon levelIcon(const unsigned uLevel); + //! Name einer bestimmten Abfragehäufigkeit + static QString levelText(const unsigned uLevel); + static QString tr (const char *sourceText, const char *comment=0); + //! Berechnung der Wartezeit für eine bestimmte Abfragehäufigkeit + static unsigned waitDaysForRepeat (const unsigned uLevel); + +protected: + + CChapter *m_pParentChapter; //!< Zeiger auf das (Eltern-) Kapitel, zu dem die Frage gehört. + // masterdata + QString m_strId; //!< ID der Frage + QString m_strText; //!< Fragentext + QList m_listAnswer; //!< Liste mit Antworten + unsigned m_uErrorPoints; //!< Fehlerpunkte bei Falschantwort in Prüfung + QStringList m_strlGroups; //!< Liste der Klassen/Gruppen, zu denen diese Frage gehört + + // learning + //! Durchmischte Antworten + /*! Die Liste enthält die Positionen der Liste m_listAnswer */ + QList m_listMixedAnswer; + + // Statistics + QList m_listAnswerClicked; //!< Liste mit sämtlichen Antwort-Klicks + unsigned m_uCorrectAnswers; //!< Anzahl der richtigen Antworten + unsigned m_uCorrectSuccessive; //!< Anzahl der richtigen Antworten zuletzt hintereinander + unsigned m_uWrongAnswers; //!< Anzahl der falschen Antworten + unsigned m_uWrongSuccessive; //!< Anzahl der falschen Antworten zuletzt hintereinander + + //! Aktuelle Stufe der Abfragehäufigkeit + /*! + Stufe 0: sehr häufige ... Stufe 5: sehr seltene Abfrage + + Die Abfragehäufigkeit wird nicht in der Statistik-Datei gespeichert, sondern bei jedem Laden der + Datei anhand der Beantwortungen in der Vergangenheit neu berechnet. + */ + unsigned m_uLevel; +}; + +#endif diff --git a/questionmodel.cpp b/questionmodel.cpp index 551488d..23417ec 100644 --- a/questionmodel.cpp +++ b/questionmodel.cpp @@ -1,175 +1,175 @@ -/*************************************************************************** - * 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 "chapter.h" -#include "questionmodel.h" - -#include - -CQuestionModel::CQuestionModel(QObject *pParent) : QAbstractItemModel (pParent) -{ - m_pChapter=0; -} - -CQuestionModel::~CQuestionModel() -{ -} - -void CQuestionModel::onLanguageChanged() -{ - headerDataChanged(Qt::Horizontal, 0, columnCount()-1); -} - -void CQuestionModel::clear() -{ - m_pChapter=0; - reset(); -} - -void CQuestionModel::setModelData (CChapter *pChapter) -{ - m_pChapter = pChapter; - reset(); -} - -int CQuestionModel::columnCount (const QModelIndex & parent) const -{ - Q_UNUSED(parent) - return 7; -} - -QVariant CQuestionModel::data (const QModelIndex & index, int role) const -{ -CQuestion *p; -QTextDocument t; - - if (!index.isValid()) return QVariant(); - p = (CQuestion*) index.internalPointer(); - if (p == 0) return QVariant(); - if (role == Qt::DisplayRole) - { - if (index.column() == 0) - return p->id(); - else if (index.column() == 1) - return p->firstTextLine(); - //return p->text(); - else if (index.column() == 2) - return p->clickedCorrect(); - else if (index.column() == 3) - return p->clickedWrong(); - else if (index.column() == 4) - return p->levelText(); - else if (index.column() == 5) - { - t.setHtml(p->lastClickedText()); - return t.toPlainText(); - } - else if (index.column() == 6) - { - t.setHtml(p->repeatDateText()); - return t.toPlainText(); - } - } - else if (role == Qt::DecorationRole) - { - if (index.column() == 4) - //return QIcon (QString(":/icons/16x16/level%1.png").arg(p->level())); - return p->levelIcon(); - } - else if (role == Qt::ToolTipRole) - { - if (index.column() == 5) - return p->lastClickedTextExtended(); - else if (index.column() == 6) - return p->repeatDateTextExtended(); -/* if (index.column() == 4) - return " " + p->levelText(); -*/ } -/* else if (role == Qt::WhatsThisRole) - { - if (index.column() == 4) - return QString("Was ist das?"); - } -*/ - return QVariant(); -} - -bool CQuestionModel::hasChildren (const QModelIndex & parent) const -{ - if (parent.isValid()) return false; - return (m_pChapter != 0); -} - -QVariant CQuestionModel::headerData (int section, Qt::Orientation orientation, int role) const -{ - if (orientation == Qt::Vertical) return QVariant(); - if (role == Qt::DisplayRole) - { - if (section == 0) - return tr("Kennung"); - else if (section == 1) - return tr("Frage"); - else if (section == 2) - return tr("R"); - else if (section == 3) - return tr("F"); - else if (section == 4) - return tr("Lernfortschritt"); - else if (section == 5) - return tr("letzte Abfrage"); - else if (section == 6) - return tr("Wiederholung"); - } -/* else if (role == Qt::DecorationRole) - { - if (section == m_iColBookmarkFlag) - return QIcon (":/icons/16x16/bookmark.png"); - }*/ - else if (role == Qt::ToolTipRole) - { - if (section == 2) - return tr("Anzahl der richtigen Antworten"); - else if (section == 3) - return tr("Anzahl der falschen Antworten"); - } - return QVariant(); -} - -QModelIndex CQuestionModel::index (int row, int column, const QModelIndex & parent) const -{ -CQuestion *p=0; - - if (m_pChapter == 0 || parent.isValid() || row >= m_pChapter->countQuestion()) return QModelIndex(); - p = m_pChapter->questionAt (row); - return createIndex (row, column, (void*)p); -} - -QModelIndex CQuestionModel::parent (const QModelIndex & index) const -{ - return QModelIndex(); -} - -int CQuestionModel::rowCount (const QModelIndex & parent) const -{ - if (m_pChapter == 0 || parent.isValid()) return 0; - return m_pChapter->countQuestion(); -} - +/*************************************************************************** + * 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 "chapter.h" +#include "questionmodel.h" + +#include + +CQuestionModel::CQuestionModel(QObject *pParent) : QAbstractItemModel (pParent) +{ + m_pChapter=0; +} + +CQuestionModel::~CQuestionModel() +{ +} + +void CQuestionModel::onLanguageChanged() +{ + headerDataChanged(Qt::Horizontal, 0, columnCount()-1); +} + +void CQuestionModel::clear() +{ + m_pChapter=0; + reset(); +} + +void CQuestionModel::setModelData (CChapter *pChapter) +{ + m_pChapter = pChapter; + reset(); +} + +int CQuestionModel::columnCount (const QModelIndex & parent) const +{ + Q_UNUSED(parent) + return 7; +} + +QVariant CQuestionModel::data (const QModelIndex & index, int role) const +{ +CQuestion *p; +QTextDocument t; + + if (!index.isValid()) return QVariant(); + p = (CQuestion*) index.internalPointer(); + if (p == 0) return QVariant(); + if (role == Qt::DisplayRole) + { + if (index.column() == 0) + return p->id(); + else if (index.column() == 1) + return p->firstTextLine(); + //return p->text(); + else if (index.column() == 2) + return p->clickedCorrect(); + else if (index.column() == 3) + return p->clickedWrong(); + else if (index.column() == 4) + return p->levelText(); + else if (index.column() == 5) + { + t.setHtml(p->lastClickedText()); + return t.toPlainText(); + } + else if (index.column() == 6) + { + t.setHtml(p->repeatDateText()); + return t.toPlainText(); + } + } + else if (role == Qt::DecorationRole) + { + if (index.column() == 4) + //return QIcon (QString(":/icons/16x16/level%1.png").arg(p->level())); + return p->levelIcon(); + } + else if (role == Qt::ToolTipRole) + { + if (index.column() == 5) + return p->lastClickedTextExtended(); + else if (index.column() == 6) + return p->repeatDateTextExtended(); +/* if (index.column() == 4) + return " " + p->levelText(); +*/ } +/* else if (role == Qt::WhatsThisRole) + { + if (index.column() == 4) + return QString("Was ist das?"); + } +*/ + return QVariant(); +} + +bool CQuestionModel::hasChildren (const QModelIndex & parent) const +{ + if (parent.isValid()) return false; + return (m_pChapter != 0); +} + +QVariant CQuestionModel::headerData (int section, Qt::Orientation orientation, int role) const +{ + if (orientation == Qt::Vertical) return QVariant(); + if (role == Qt::DisplayRole) + { + if (section == 0) + return tr("Kennung"); + else if (section == 1) + return tr("Frage"); + else if (section == 2) + return tr("R"); + else if (section == 3) + return tr("F"); + else if (section == 4) + return tr("Lernfortschritt"); + else if (section == 5) + return tr("letzte Abfrage"); + else if (section == 6) + return tr("Wiederholung"); + } +/* else if (role == Qt::DecorationRole) + { + if (section == m_iColBookmarkFlag) + return QIcon (":/icons/16x16/bookmark.png"); + }*/ + else if (role == Qt::ToolTipRole) + { + if (section == 2) + return tr("Anzahl der richtigen Antworten"); + else if (section == 3) + return tr("Anzahl der falschen Antworten"); + } + return QVariant(); +} + +QModelIndex CQuestionModel::index (int row, int column, const QModelIndex & parent) const +{ +CQuestion *p=0; + + if (m_pChapter == 0 || parent.isValid() || row >= m_pChapter->countQuestion()) return QModelIndex(); + p = m_pChapter->questionAt (row); + return createIndex (row, column, (void*)p); +} + +QModelIndex CQuestionModel::parent (const QModelIndex & index) const +{ + return QModelIndex(); +} + +int CQuestionModel::rowCount (const QModelIndex & parent) const +{ + if (m_pChapter == 0 || parent.isValid()) return 0; + return m_pChapter->countQuestion(); +} + diff --git a/questionmodel.h b/questionmodel.h index 45ba7ca..2b1b118 100644 --- a/questionmodel.h +++ b/questionmodel.h @@ -1,58 +1,58 @@ -/*************************************************************************** - * 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. * - ***************************************************************************/ - -#ifndef QUESTIONMODEL_H -#define QUESTIONMODEL_H - -#include - -class CChapter; - -class CQuestionModel : public QAbstractItemModel -{ - Q_OBJECT -public: - CQuestionModel(QObject *pParent=0); - ~CQuestionModel(); - - void clear(); - void setModelData (CChapter *pChapter); - -public: - virtual int columnCount ( const QModelIndex & parent = QModelIndex() ) const; - virtual QVariant data ( const QModelIndex & index, int role = Qt::DisplayRole ) const; - virtual bool hasChildren (const QModelIndex & parent = QModelIndex()) const; - virtual QVariant headerData ( int section, Qt::Orientation orientation, int role = Qt::DisplayRole ) const; - virtual QModelIndex index (int row, int column, const QModelIndex & parent = QModelIndex()) const; - virtual QModelIndex parent ( const QModelIndex & index ) const; - virtual int rowCount ( const QModelIndex & parent = QModelIndex() ) const; - -public slots: - void onLanguageChanged(); - -protected: - void recalcColumn(); - -protected: - CChapter *m_pChapter; -}; - -#endif +/*************************************************************************** + * 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. * + ***************************************************************************/ + +#ifndef QUESTIONMODEL_H +#define QUESTIONMODEL_H + +#include + +class CChapter; + +class CQuestionModel : public QAbstractItemModel +{ + Q_OBJECT +public: + CQuestionModel(QObject *pParent=0); + ~CQuestionModel(); + + void clear(); + void setModelData (CChapter *pChapter); + +public: + virtual int columnCount ( const QModelIndex & parent = QModelIndex() ) const; + virtual QVariant data ( const QModelIndex & index, int role = Qt::DisplayRole ) const; + virtual bool hasChildren (const QModelIndex & parent = QModelIndex()) const; + virtual QVariant headerData ( int section, Qt::Orientation orientation, int role = Qt::DisplayRole ) const; + virtual QModelIndex index (int row, int column, const QModelIndex & parent = QModelIndex()) const; + virtual QModelIndex parent ( const QModelIndex & index ) const; + virtual int rowCount ( const QModelIndex & parent = QModelIndex() ) const; + +public slots: + void onLanguageChanged(); + +protected: + void recalcColumn(); + +protected: + CChapter *m_pChapter; +}; + +#endif diff --git a/recentfiles.cpp b/recentfiles.cpp index dba76d7..337d298 100644 --- a/recentfiles.cpp +++ b/recentfiles.cpp @@ -1,178 +1,178 @@ -/*************************************************************************** - * 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 "recentfiles.h" - -#include -#include -#include - -CRecentFiles::CRecentFiles(QObject *pParent) : QObject(pParent) -{ - m_actNoEntry = new QAction(this); - m_actSepTop = new QAction(this); - m_actSepTop->setSeparator(true); - m_actSepTop->setVisible(false); - m_actSepBottom = new QAction(this); - m_actSepBottom->setSeparator(true); - m_actSepBottom->setVisible(false); - m_showSeperator = SeperatorNone; - - m_actNoEntry->setEnabled(false); - m_bShowNoEntry = true; - onLanguageChanged(); -} - -CRecentFiles::~CRecentFiles() -{ - qDeleteAll(m_listActions); -} - -void CRecentFiles::clear() -{ - qDeleteAll(m_listActions); - m_listActions.clear(); -} - -void CRecentFiles::onLanguageChanged() -{ - m_actNoEntry->setText(tr("No recent files")); -} - -void CRecentFiles::create (const QString& strName, const unsigned uMaxCount) -{ -QAction *pAct=0; - qDeleteAll(m_listActions); - m_listActions.clear(); - m_strName = strName; - m_uMaxCount = uMaxCount; - - for (unsigned u=0; usetVisible(false); - connect(pAct, SIGNAL(triggered()), this, SLOT(onRecentFile())); - m_listActions.append(pAct); - } - updateActions(); -} - -void CRecentFiles::insertToMenu(QMenu *pMenu, QAction *pBefore) -{ - pMenu->insertAction(pBefore, m_actSepTop); - pMenu->insertAction(pBefore, m_actNoEntry); - pMenu->insertActions(pBefore, m_listActions); - pMenu->insertAction(pBefore, m_actSepBottom); -} - -QString CRecentFiles::settingName() const -{ -QString str = "RecentFileList"; - if (!m_strName.isEmpty()) str += "_" + m_strName; - return str; -} - -void CRecentFiles::onRecentFile() -{ - QAction *action = qobject_cast(sender()); - if (action) - loadFile(action->data().toString()); -} - -void CRecentFiles::setRecentFile(const QString& strFileName) -{ -QSettings set; -QStringList strl = set.value(settingName()).toStringList(); - - strl.removeAll(strFileName); - strl.prepend(strFileName); - while ((unsigned)strl.size() > m_uMaxCount) - strl.removeLast(); - - set.setValue(settingName(), strl); - - updateActions(); -} - -void CRecentFiles::removeFile(const QString& strFileName) -{ -QSettings set; -QStringList strl = set.value(settingName()).toStringList(); - strl.removeAll(strFileName); - set.setValue(settingName(), strl); - updateActions(); -} - -void CRecentFiles::removeAll() -{ -QSettings set; - set.setValue(settingName(), QStringList()); - updateActions(); -} - -void CRecentFiles::updateActions() -{ -QSettings set; -QStringList strl = set.value(settingName()).toStringList(); -unsigned uNumRecentFiles = qMin((unsigned)strl.size(), m_uMaxCount); -QString str; - - m_actNoEntry->setVisible(m_bShowNoEntry && uNumRecentFiles == 0); - m_actSepTop->setVisible((m_showSeperator == SeperatorTop || m_showSeperator == SeperatorBoth) && uNumRecentFiles != 0); - m_actSepBottom->setVisible((m_showSeperator == SeperatorBottom || m_showSeperator == SeperatorBoth) && uNumRecentFiles != 0); - - for (unsigned i=0; isetText(str); - m_listActions[i]->setData(strl[i]); - m_listActions[i]->setVisible(true); - } - for (unsigned j=uNumRecentFiles; jsetVisible(false); -} - -QString CRecentFiles::recentFile(const int idx) const -{ -QSettings set; -QStringList strl = set.value(settingName()).toStringList(); - if (idx >= strl.size()) return QString(); - return strl.at(idx); -} - -void CRecentFiles::setShowNoEntry (const bool bShow) -{ -QSettings set; -QStringList strl = set.value(settingName()).toStringList(); -unsigned uNumRecentFiles = qMin((unsigned)strl.size(), m_uMaxCount); - m_bShowNoEntry = bShow; - m_actNoEntry->setVisible(m_bShowNoEntry && uNumRecentFiles == 0); -} - -void CRecentFiles::setShowSeperator(const ShowSeperator show) -{ -QSettings set; -QStringList strl = set.value(settingName()).toStringList(); -unsigned uNumRecentFiles = qMin((unsigned)strl.size(), m_uMaxCount); - m_showSeperator = show; - m_actSepTop->setVisible((m_showSeperator == SeperatorTop || m_showSeperator == SeperatorBoth) && uNumRecentFiles != 0); - m_actSepBottom->setVisible((m_showSeperator == SeperatorBottom || m_showSeperator == SeperatorBoth) && uNumRecentFiles != 0); -} +/*************************************************************************** + * 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 "recentfiles.h" + +#include +#include +#include + +CRecentFiles::CRecentFiles(QObject *pParent) : QObject(pParent) +{ + m_actNoEntry = new QAction(this); + m_actSepTop = new QAction(this); + m_actSepTop->setSeparator(true); + m_actSepTop->setVisible(false); + m_actSepBottom = new QAction(this); + m_actSepBottom->setSeparator(true); + m_actSepBottom->setVisible(false); + m_showSeperator = SeperatorNone; + + m_actNoEntry->setEnabled(false); + m_bShowNoEntry = true; + onLanguageChanged(); +} + +CRecentFiles::~CRecentFiles() +{ + qDeleteAll(m_listActions); +} + +void CRecentFiles::clear() +{ + qDeleteAll(m_listActions); + m_listActions.clear(); +} + +void CRecentFiles::onLanguageChanged() +{ + m_actNoEntry->setText(tr("No recent files")); +} + +void CRecentFiles::create (const QString& strName, const unsigned uMaxCount) +{ +QAction *pAct=0; + qDeleteAll(m_listActions); + m_listActions.clear(); + m_strName = strName; + m_uMaxCount = uMaxCount; + + for (unsigned u=0; usetVisible(false); + connect(pAct, SIGNAL(triggered()), this, SLOT(onRecentFile())); + m_listActions.append(pAct); + } + updateActions(); +} + +void CRecentFiles::insertToMenu(QMenu *pMenu, QAction *pBefore) +{ + pMenu->insertAction(pBefore, m_actSepTop); + pMenu->insertAction(pBefore, m_actNoEntry); + pMenu->insertActions(pBefore, m_listActions); + pMenu->insertAction(pBefore, m_actSepBottom); +} + +QString CRecentFiles::settingName() const +{ +QString str = "RecentFileList"; + if (!m_strName.isEmpty()) str += "_" + m_strName; + return str; +} + +void CRecentFiles::onRecentFile() +{ + QAction *action = qobject_cast(sender()); + if (action) + loadFile(action->data().toString()); +} + +void CRecentFiles::setRecentFile(const QString& strFileName) +{ +QSettings set; +QStringList strl = set.value(settingName()).toStringList(); + + strl.removeAll(strFileName); + strl.prepend(strFileName); + while ((unsigned)strl.size() > m_uMaxCount) + strl.removeLast(); + + set.setValue(settingName(), strl); + + updateActions(); +} + +void CRecentFiles::removeFile(const QString& strFileName) +{ +QSettings set; +QStringList strl = set.value(settingName()).toStringList(); + strl.removeAll(strFileName); + set.setValue(settingName(), strl); + updateActions(); +} + +void CRecentFiles::removeAll() +{ +QSettings set; + set.setValue(settingName(), QStringList()); + updateActions(); +} + +void CRecentFiles::updateActions() +{ +QSettings set; +QStringList strl = set.value(settingName()).toStringList(); +unsigned uNumRecentFiles = qMin((unsigned)strl.size(), m_uMaxCount); +QString str; + + m_actNoEntry->setVisible(m_bShowNoEntry && uNumRecentFiles == 0); + m_actSepTop->setVisible((m_showSeperator == SeperatorTop || m_showSeperator == SeperatorBoth) && uNumRecentFiles != 0); + m_actSepBottom->setVisible((m_showSeperator == SeperatorBottom || m_showSeperator == SeperatorBoth) && uNumRecentFiles != 0); + + for (unsigned i=0; isetText(str); + m_listActions[i]->setData(strl[i]); + m_listActions[i]->setVisible(true); + } + for (unsigned j=uNumRecentFiles; jsetVisible(false); +} + +QString CRecentFiles::recentFile(const int idx) const +{ +QSettings set; +QStringList strl = set.value(settingName()).toStringList(); + if (idx >= strl.size()) return QString(); + return strl.at(idx); +} + +void CRecentFiles::setShowNoEntry (const bool bShow) +{ +QSettings set; +QStringList strl = set.value(settingName()).toStringList(); +unsigned uNumRecentFiles = qMin((unsigned)strl.size(), m_uMaxCount); + m_bShowNoEntry = bShow; + m_actNoEntry->setVisible(m_bShowNoEntry && uNumRecentFiles == 0); +} + +void CRecentFiles::setShowSeperator(const ShowSeperator show) +{ +QSettings set; +QStringList strl = set.value(settingName()).toStringList(); +unsigned uNumRecentFiles = qMin((unsigned)strl.size(), m_uMaxCount); + m_showSeperator = show; + m_actSepTop->setVisible((m_showSeperator == SeperatorTop || m_showSeperator == SeperatorBoth) && uNumRecentFiles != 0); + m_actSepBottom->setVisible((m_showSeperator == SeperatorBottom || m_showSeperator == SeperatorBoth) && uNumRecentFiles != 0); +} diff --git a/recentfiles.h b/recentfiles.h index 55c6868..ae8ac09 100644 --- a/recentfiles.h +++ b/recentfiles.h @@ -1,82 +1,82 @@ -/*************************************************************************** - * 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. * - ***************************************************************************/ - -#pragma once - -#include -#include -#include - -class QMenu; - -class CRecentFiles : public QObject -{ -Q_OBJECT -public: - enum ShowSeperator { SeperatorNone, SeperatorTop, SeperatorBottom, SeperatorBoth }; - - CRecentFiles(QObject *pParent=0); - ~CRecentFiles(); - - void clear(); - - void create (const QString& strName, const unsigned uMaxCount=4); - void insertToMenu(QMenu *pMenu, QAction *pBefore=0); - - inline unsigned maxCount() const { return m_uMaxCount; } - inline QString name() const { return m_strName; } - - void setRecentFile(const QString& strFileName); - void removeFile(const QString& strFileName); - void removeAll(); - - QString recentFile(const int idx) const; - - void setShowNoEntry (const bool bShow); - inline bool showNoEntry() const { return m_bShowNoEntry; } - - void setShowSeperator(const ShowSeperator show); - inline ShowSeperator showSeperator() const { return m_showSeperator; } - -public slots: - void onLanguageChanged(); - -protected slots: - void onRecentFile(); - -signals: - void loadFile(QString strFile); - -protected: - QString settingName() const; - void updateActions(); - -protected: - QString m_strName; - unsigned m_uMaxCount; - - QList m_listActions; - QAction *m_actNoEntry; - QAction *m_actSepTop; - QAction *m_actSepBottom; - bool m_bShowNoEntry; - ShowSeperator m_showSeperator; -}; +/*************************************************************************** + * 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. * + ***************************************************************************/ + +#pragma once + +#include +#include +#include + +class QMenu; + +class CRecentFiles : public QObject +{ +Q_OBJECT +public: + enum ShowSeperator { SeperatorNone, SeperatorTop, SeperatorBottom, SeperatorBoth }; + + CRecentFiles(QObject *pParent=0); + ~CRecentFiles(); + + void clear(); + + void create (const QString& strName, const unsigned uMaxCount=4); + void insertToMenu(QMenu *pMenu, QAction *pBefore=0); + + inline unsigned maxCount() const { return m_uMaxCount; } + inline QString name() const { return m_strName; } + + void setRecentFile(const QString& strFileName); + void removeFile(const QString& strFileName); + void removeAll(); + + QString recentFile(const int idx) const; + + void setShowNoEntry (const bool bShow); + inline bool showNoEntry() const { return m_bShowNoEntry; } + + void setShowSeperator(const ShowSeperator show); + inline ShowSeperator showSeperator() const { return m_showSeperator; } + +public slots: + void onLanguageChanged(); + +protected slots: + void onRecentFile(); + +signals: + void loadFile(QString strFile); + +protected: + QString settingName() const; + void updateActions(); + +protected: + QString m_strName; + unsigned m_uMaxCount; + + QList m_listActions; + QAction *m_actNoEntry; + QAction *m_actSepTop; + QAction *m_actSepBottom; + bool m_bShowNoEntry; + ShowSeperator m_showSeperator; +}; diff --git a/tools.cpp b/tools.cpp index 62f61f1..06a6337 100644 --- a/tools.cpp +++ b/tools.cpp @@ -1,53 +1,53 @@ -/*************************************************************************** - * 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 "tools.h" - -#include -#include - -unsigned afu_random (const unsigned uMin, const unsigned uMax) -{ -unsigned u=0; -float f = (float) (uMax-uMin+1) / (float) RAND_MAX; - u = (unsigned) ((float) rand () * f) + uMin;; - return (u); -} - -QDomElement createXmlTextElement(const QString& strName, const QString& strText, QDomDocument& doc) -{ -QDomElement elem = doc.createElement(strName); - elem.appendChild(doc.createTextNode(strText)); - return elem; -} - -QPixmap createProgressBar (int w, int h, double dPercent) -{ -QPixmap pix(w,h); -QPainter p(&pix); - - pix.fill(QPalette().color(QPalette::Dark)); - p.setBrush(Qt::blue); - p.setPen(Qt::blue); - if (dPercent != 0.0) - p.drawRect(0, 0, (int)(((double)w)*dPercent), h); - return pix; -} +/*************************************************************************** + * 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 "tools.h" + +#include +#include + +unsigned afu_random (const unsigned uMin, const unsigned uMax) +{ +unsigned u=0; +float f = (float) (uMax-uMin+1) / (float) RAND_MAX; + u = (unsigned) ((float) rand () * f) + uMin;; + return (u); +} + +QDomElement createXmlTextElement(const QString& strName, const QString& strText, QDomDocument& doc) +{ +QDomElement elem = doc.createElement(strName); + elem.appendChild(doc.createTextNode(strText)); + return elem; +} + +QPixmap createProgressBar (int w, int h, double dPercent) +{ +QPixmap pix(w,h); +QPainter p(&pix); + + pix.fill(QPalette().color(QPalette::Dark)); + p.setBrush(Qt::blue); + p.setPen(Qt::blue); + if (dPercent != 0.0) + p.drawRect(0, 0, (int)(((double)w)*dPercent), h); + return pix; +} diff --git a/tools.h b/tools.h index 1f48732..1f759bd 100644 --- a/tools.h +++ b/tools.h @@ -1,32 +1,32 @@ -/*************************************************************************** - * 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. * - ***************************************************************************/ - -#ifndef TOOLS_H -#define TOOLS_H - -#include -#include - -unsigned afu_random (const unsigned uMin, const unsigned uMax); -QDomElement createXmlTextElement(const QString& strName, const QString& strText, QDomDocument& doc); -QPixmap createProgressBar (int w, int h, double dPercent); - -#endif +/*************************************************************************** + * 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. * + ***************************************************************************/ + +#ifndef TOOLS_H +#define TOOLS_H + +#include +#include + +unsigned afu_random (const unsigned uMin, const unsigned uMax); +QDomElement createXmlTextElement(const QString& strName, const QString& strText, QDomDocument& doc); +QPixmap createProgressBar (int w, int h, double dPercent); + +#endif