isNeverAsked() || 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
new file mode 100644
index 0000000..527159b
--- /dev/null
+++ b/chapter.h
@@ -0,0 +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
diff --git a/chaptermodel.cpp b/chaptermodel.cpp
new file mode 100644
index 0000000..f0b5825
--- /dev/null
+++ b/chaptermodel.cpp
@@ -0,0 +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();
+}
+
diff --git a/chaptermodel.h b/chaptermodel.h
new file mode 100644
index 0000000..4122271
--- /dev/null
+++ b/chaptermodel.h
@@ -0,0 +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
diff --git a/dlgexam.cpp b/dlgexam.cpp
new file mode 100644
index 0000000..03d8cbd
--- /dev/null
+++ b/dlgexam.cpp
@@ -0,0 +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);
+ }
+}
diff --git a/dlgexam.h b/dlgexam.h
new file mode 100644
index 0000000..f3a70b3
--- /dev/null
+++ b/dlgexam.h
@@ -0,0 +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;
+};
+
diff --git a/dlgexam.ui b/dlgexam.ui
new file mode 100644
index 0000000..0373422
--- /dev/null
+++ b/dlgexam.ui
@@ -0,0 +1,529 @@
+
+ DlgExam
+
+
+
+ 0
+ 0
+ 607
+ 507
+
+
+
+ Prüfungssimulation
+
+
+
+ 9
+
+
+ 6
+
+ -
+
+
+
+ 12
+ 75
+ true
+
+
+
+ Bezeichnung
+
+
+ Qt::AlignCenter
+
+
+
+ -
+
+
+ 0
+
+
+ 6
+
+
-
+
+
+ 0
+
+
+ 6
+
+
-
+
+
+ Frage
+
+
+
+ 9
+
+
+ 6
+
+
-
+
+
+
+ 7
+ 7
+ 0
+ 0
+
+
+
+
+ 300
+ 300
+
+
+
+
+ 10
+
+
+
+
+
+
+
+ -
+
+
+ Ihre Antwort
+
+
+
+ 9
+
+
+ 6
+
+
-
+
+
+ &A
+
+
+ A
+
+
+
+ -
+
+
+ &B
+
+
+ B
+
+
+
+ -
+
+
+ &C
+
+
+ C
+
+
+
+ -
+
+
+ &D
+
+
+ D
+
+
+
+ -
+
+
+ &Keine Ahnung
+
+
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+
+
+
+ -
+
+
+ Navigation
+
+
+
+ 9
+
+
+ 6
+
+
-
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+
+ 40
+ 16777215
+
+
+
+ <<
+
+
+
+ -
+
+
+
+ 40
+ 16777215
+
+
+
+ <
+
+
+
+ -
+
+
+ -
+
+
+
+ 40
+ 16777215
+
+
+
+ >
+
+
+
+ -
+
+
+
+ 40
+ 16777215
+
+
+
+ >>
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+
+
+
+
+
+ -
+
+
+ 0
+
+
+ 6
+
+
-
+
+
+
+ 300
+ 16777215
+
+
+
+ Zeit
+
+
+
+ 9
+
+
+ 6
+
+
-
+
+
+ TextLabel
+
+
+
+ -
+
+
+ 24
+
+
+ Qt::Horizontal
+
+
+
+
+
+
+ -
+
+
+
+ 300
+ 16777215
+
+
+
+ Beantwortete Fragen
+
+
+
+ 9
+
+
+ 6
+
+
-
+
+
+ TextLabel
+
+
+
+ -
+
+
+ 24
+
+
+ Qt::Horizontal
+
+
+
+
+
+
+ -
+
+
+
+ 300
+ 16777215
+
+
+
+ Auswertung
+
+
+
+ 9
+
+
+ 6
+
+
-
+
+
+ Richtige Antworten:
+
+
+
+ -
+
+
+ Falsche Antworten:
+
+
+
+ -
+
+
+ ?
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+ -
+
+
+ ?
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+ -
+
+
+ max. erlaubte falsche Antworten:
+
+
+ 10
+
+
+
+ -
+
+
+ 10
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+
+
+
+ -
+
+
+
+ 12
+ 75
+ true
+
+
+
+
+
+
+ Qt::AlignCenter
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 31
+
+
+
+
+ -
+
+
+ 0
+
+
+ 6
+
+
-
+
+
+
+ 150
+ 0
+
+
+
+ Abgeben && auswerten
+
+
+
+
+
+ -
+
+
+ 0
+
+
+ 6
+
+
-
+
+
+
+ 150
+ 0
+
+
+
+ Abbrechen
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ pbCancel
+ clicked()
+ DlgExam
+ reject()
+
+
+ 468
+ 482
+
+
+ 514
+ 501
+
+
+
+
+
diff --git a/dlgexamselect.cpp b/dlgexamselect.cpp
new file mode 100644
index 0000000..a13ba74
--- /dev/null
+++ b/dlgexamselect.cpp
@@ -0,0 +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();
+}
+
diff --git a/dlgexamselect.h b/dlgexamselect.h
new file mode 100644
index 0000000..fa2b711
--- /dev/null
+++ b/dlgexamselect.h
@@ -0,0 +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;
+};
diff --git a/dlgexamselect.ui b/dlgexamselect.ui
new file mode 100644
index 0000000..b5a0a8a
--- /dev/null
+++ b/dlgexamselect.ui
@@ -0,0 +1,178 @@
+
+ DlgExamSelect
+
+
+
+ 0
+ 0
+ 491
+ 418
+
+
+
+ Prüfung auswählen
+
+
+
+ 9
+
+
+ 6
+
+ -
+
+
+ Verfügbare Prüfungen
+
+
+
+ 9
+
+
+ 6
+
+
-
+
+
+ false
+
+
+
+
+
+
+ -
+
+
+ Detailinformation
+
+
+
+ 9
+
+
+ 6
+
+
-
+
+
+
+ 3
+ 5
+ 0
+ 0
+
+
+
+
+
+
+
+ -
+
+
+
+
+
+ true
+
+
+
+ -
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+ -
+
+
+ Max. erlaubte Fehler:
+
+
+
+ -
+
+
+ Anzahl Fragen:
+
+
+
+ -
+
+
+ Dauer:
+
+
+
+ -
+
+
+ Hinweise:
+
+
+
+ -
+
+
+ Bezeichnung:
+
+
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+ QDialogButtonBox::Cancel|QDialogButtonBox::NoButton|QDialogButtonBox::Ok
+
+
+ true
+
+
+
+
+
+
+ twList
+ buttonBox
+
+
+
+
+ buttonBox
+ rejected()
+ DlgExamSelect
+ reject()
+
+
+ 291
+ 384
+
+
+ 286
+ 274
+
+
+
+
+
diff --git a/dlgexamstatistic.cpp b/dlgexamstatistic.cpp
new file mode 100644
index 0000000..c2396fd
--- /dev/null
+++ b/dlgexamstatistic.cpp
@@ -0,0 +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')) : "--");
+}
+
diff --git a/dlgexamstatistic.h b/dlgexamstatistic.h
new file mode 100644
index 0000000..a8337e9
--- /dev/null
+++ b/dlgexamstatistic.h
@@ -0,0 +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;
+};
diff --git a/dlgexamstatistic.ui b/dlgexamstatistic.ui
new file mode 100644
index 0000000..da96a51
--- /dev/null
+++ b/dlgexamstatistic.ui
@@ -0,0 +1,367 @@
+
+ DlgExamStatistic
+
+
+
+ 0
+ 0
+ 452
+ 433
+
+
+
+ Prüfungs-Statistik
+
+
+
+ 9
+
+
+ 6
+
+ -
+
+
+ 0
+
+
+ 6
+
+
-
+
+
+ Prüfung:
+
+
+
+ -
+
+
+
+ 3
+ 0
+ 0
+ 0
+
+
+
+
+ 200
+ 0
+
+
+
+
+
+
+ -
+
+
+
+ 10
+ 75
+ true
+
+
+
+ Prüfungs-Rahmenbedinungen
+
+
+
+ -
+
+
+ 0
+
+
+ 6
+
+
-
+
+
+ 0
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+ -
+
+
+ 0
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+ -
+
+
+ Verfügbare Zeit:
+
+
+
+ -
+
+
+ Max. erlaubte Anzahl falscher Fragen:
+
+
+
+ -
+
+
+ Anzahl der Fragen:
+
+
+
+ -
+
+
+ 0
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ -
+
+
+
+ 10
+ 75
+ true
+
+
+
+ Statistik
+
+
+
+ -
+
+
+ 0
+
+
+ 6
+
+
-
+
+
+ 0
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+ -
+
+
+ 0
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+ -
+
+
+ 0
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+ -
+
+
+ davon bestanden:
+
+
+ 10
+
+
+
+ -
+
+
+ Durchschn. Anzahl falscher Fragen:
+
+
+
+ -
+
+
+ davon nicht bestanden:
+
+
+ 10
+
+
+
+ -
+
+
+ 0
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+ -
+
+
+ 0.0 %
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+ -
+
+
+ 0.0 %
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+ -
+
+
+ Anzahl Prüfungen durchgeführt:
+
+
+
+ -
+
+
+ Durchschn. benötigte Zeit pro Prüfung / pro Frage:
+
+
+
+ -
+
+
+ 0 m 0 s
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+ -
+
+
+ 0 m 0 s
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ -
+
+
+
+ 10
+ 75
+ true
+
+
+
+ Liste durchgeführter Prüfungen
+
+
+
+ -
+
+
+ false
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+ QDialogButtonBox::Ok
+
+
+ true
+
+
+
+
+
+
+ cbExam
+ twExamStat
+ buttonBox
+
+
+
+
+ buttonBox
+ accepted()
+ DlgExamStatistic
+ accept()
+
+
+ 248
+ 254
+
+
+ 157
+ 274
+
+
+
+
+ buttonBox
+ rejected()
+ DlgExamStatistic
+ reject()
+
+
+ 316
+ 260
+
+
+ 286
+ 274
+
+
+
+
+
diff --git a/dlginformation.cpp b/dlginformation.cpp
new file mode 100644
index 0000000..d4a295e
--- /dev/null
+++ b/dlginformation.cpp
@@ -0,0 +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;
+}
+
diff --git a/dlginformation.h b/dlginformation.h
new file mode 100644
index 0000000..6cc7f43
--- /dev/null
+++ b/dlginformation.h
@@ -0,0 +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
diff --git a/dlginformation.ui b/dlginformation.ui
new file mode 100644
index 0000000..ed345dc
--- /dev/null
+++ b/dlginformation.ui
@@ -0,0 +1,304 @@
+
+ DlgInformation
+
+
+
+ 0
+ 0
+ 366
+ 331
+
+
+
+ Datei-Information
+
+
+ :/icons/16x16/info.png
+
+
+
+ 9
+
+
+ 6
+
+ -
+
+
+ Original-Fragenkatalog
+
+
+
+ 9
+
+
+ 6
+
+
-
+
+
+ TextLabel
+
+
+
+ -
+
+
+
+ 50
+ 0
+
+
+
+ 0
+
+
+
+ -
+
+
+
+ 50
+ 0
+
+
+
+ 0
+
+
+
+ -
+
+
+ TextLabel
+
+
+
+ -
+
+
+ TextLabel
+
+
+
+ -
+
+
+ TextLabel
+
+
+
+ -
+
+
+ TextLabel
+
+
+
+ -
+
+
+ TextLabel
+
+
+
+ -
+
+
+ Herausgeber:
+
+
+ Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop
+
+
+
+ -
+
+
+ Anzahl der Kapitel:
+
+
+
+ -
+
+
+ Anzahl der Fragen:
+
+
+
+ -
+
+
+ Gültig bis:
+
+
+
+ -
+
+
+ Gültig ab:
+
+
+
+ -
+
+
+ Name:
+
+
+ Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop
+
+
+
+ -
+
+
+ Version:
+
+
+
+ -
+
+
+ Datum:
+
+
+
+
+
+
+ -
+
+
+ AFUTrainer-Fragenkatalog
+
+
+
+ 9
+
+
+ 6
+
+
-
+
+
+ TextLabel
+
+
+
+ -
+
+
+ Erstellt von:
+
+
+ Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop
+
+
+
+ -
+
+
+ TextLabel
+
+
+ true
+
+
+
+ -
+
+
+
+ 3
+ 5
+ 0
+ 0
+
+
+
+ TextLabel
+
+
+
+ -
+
+
+ Kommentar:
+
+
+ Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop
+
+
+
+ -
+
+
+
+ 1
+ 5
+ 0
+ 0
+
+
+
+ Erstellt am:
+
+
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+ QDialogButtonBox::Ok
+
+
+ true
+
+
+
+
+
+
+
+
+
+
+ buttonBox
+ accepted()
+ DlgInformation
+ accept()
+
+
+ 248
+ 254
+
+
+ 157
+ 274
+
+
+
+
+ buttonBox
+ rejected()
+ DlgInformation
+ reject()
+
+
+ 316
+ 260
+
+
+ 286
+ 274
+
+
+
+
+
diff --git a/dlglearn.cpp b/dlglearn.cpp
new file mode 100644
index 0000000..91718d0
--- /dev/null
+++ b/dlglearn.cpp
@@ -0,0 +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()));
+}
diff --git a/dlglearn.h b/dlglearn.h
new file mode 100644
index 0000000..535fe19
--- /dev/null
+++ b/dlglearn.h
@@ -0,0 +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
diff --git a/dlglearn.ui b/dlglearn.ui
new file mode 100644
index 0000000..033cd51
--- /dev/null
+++ b/dlglearn.ui
@@ -0,0 +1,1139 @@
+
+ DlgLearn
+
+
+
+ 0
+ 0
+ 786
+ 644
+
+
+
+ Fragen lernen
+
+
+
+ 9
+
+
+ 6
+
+ -
+
+
+ 0
+
+
+ 6
+
+
-
+
+
+ Frage
+
+
+
+ 9
+
+
+ 6
+
+
-
+
+
+
+ MS Shell Dlg 2
+ 10
+
+
+
+ true
+
+
+
+
+
+
+ -
+
+
+ Antwort
+
+
+
+ 9
+
+
+ 6
+
+
-
+
+
+ 0
+
+
+ 6
+
+
-
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+
+ 50
+ 16777215
+
+
+
+ Hotkey: A
+
+
+ &A
+
+
+ A
+
+
+
+ -
+
+
+
+ 50
+ 16777215
+
+
+
+ Hotkey: B
+
+
+ &B
+
+
+ B
+
+
+
+ -
+
+
+
+ 50
+ 16777215
+
+
+
+ Hotkey: C
+
+
+ &C
+
+
+ C
+
+
+
+ -
+
+
+
+ 50
+ 16777215
+
+
+
+ Hotkey: D
+
+
+ &D
+
+
+ D
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+ QSizePolicy::Fixed
+
+
+
+ 20
+ 20
+
+
+
+
+ -
+
+
+
+ 100
+ 0
+
+
+
+ Hotkey: S
+
+
+ Ãœber&springen
+
+
+ S
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+
+
+ -
+
+
+ 0
+
+
+ 6
+
+
-
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+
+ 100
+ 0
+
+
+
+ Hotkey: T
+
+
+ &Tipps anzeigen
+
+
+ T
+
+
+
+ -
+
+
+ false
+
+
+
+ 100
+ 0
+
+
+
+ Hotkey: L
+
+
+ &Letzte Frage...
+
+
+ L
+
+
+
+ -
+
+
+
+ 100
+ 0
+
+
+
+ &Beenden
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+ QSizePolicy::Minimum
+
+
+
+ 20
+ 0
+
+
+
+
+
+
+
+
+
+ -
+
+
+ 0
+
+
+ 6
+
+
-
+
+
+
+ 350
+ 16
+
+
+
+
+ 400
+ 16777215
+
+
+
+ Statistik
+
+
+
+ 9
+
+
+ 6
+
+
-
+
+
+
+ MS Shell Dlg 2
+ 10
+ 75
+ false
+ true
+ false
+ false
+
+
+
+ Kapitel-Name
+
+
+
+ -
+
+
+ Lernfortschritts-Statistik zu diesem Kapitel:
+
+
+
+ -
+
+
+ 0
+
+
+ 6
+
+
-
+
+
+ Durchschnittlicher Lernfortschritt:
+
+
+
+ -
+
+
+ TextLabel
+
+
+
+ -
+
+
+
+
+
+
+ -
+
+
+ 0
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+ -
+
+
+ Anfänger:
+
+
+ 20
+
+
+
+ -
+
+
+ 0
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+ -
+
+
+ 0.0 = Ahnungslos<br>1.0 = Anfänger<br>2.0 = Fortgeschritten<br>3.0 = Experte<br>4.0 = Freak<br>5.0 = Professor
+
+
+ 0.0
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+ -
+
+
+ Freak:
+
+
+ 20
+
+
+
+ -
+
+
+ TextLabel
+
+
+
+ -
+
+
+ Gesamt-Anzahl der Fragen dieses Kapitels
+
+
+ 0
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+ -
+
+
+ 0
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+ -
+
+
+ Fortgeschritten:
+
+
+ 20
+
+
+
+ -
+
+
+ 0
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+ -
+
+
+
+ 40
+ 0
+
+
+
+ 0
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+ -
+
+
+
+ 1
+ 5
+ 0
+ 0
+
+
+
+
+
+
+ :/icons/16x16/level3.png
+
+
+
+ -
+
+
+
+ 1
+ 5
+ 0
+ 0
+
+
+
+
+
+
+ :/icons/16x16/level0.png
+
+
+
+ -
+
+
+ Anzahl der Fragen:
+
+
+
+ -
+
+
+ 0
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+ -
+
+
+ TextLabel
+
+
+
+ -
+
+
+ Experte:
+
+
+ 20
+
+
+
+ -
+
+
+ Professor:
+
+
+ 20
+
+
+
+ -
+
+
+
+ 1
+ 5
+ 0
+ 0
+
+
+
+
+
+
+ :/icons/16x16/level2.png
+
+
+
+ -
+
+
+
+
+
+ :/icons/16x16/level0.png
+
+
+
+ -
+
+
+ Smiley mit Doktorhut
+
+
+
+
+
+ :/icons/16x16/level5.png
+
+
+
+ -
+
+
+
+ 1
+ 5
+ 0
+ 0
+
+
+
+
+
+
+ :/icons/16x16/level4.png
+
+
+
+ -
+
+
+ TextLabel
+
+
+
+ -
+
+
+
+ 3
+ 5
+ 0
+ 0
+
+
+
+ Ahnungslos:
+
+
+ 20
+
+
+
+ -
+
+
+ TextLabel
+
+
+
+ -
+
+
+ TextLabel
+
+
+
+ -
+
+
+
+ 1
+ 5
+ 0
+ 0
+
+
+
+
+
+
+ :/icons/16x16/level1.png
+
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ -
+
+
+
+ MS Shell Dlg 2
+ 10
+ 75
+ false
+ true
+ false
+ false
+
+
+
+ Frage
+
+
+
+ -
+
+
+ Statistik zu dieser Frage:
+
+
+
+ -
+
+
+ 0
+
+
+ 6
+
+
-
+
+
+
+ 3
+ 5
+ 0
+ 0
+
+
+
+ Lernfortschritt:
+
+
+
+ -
+
+
+ 0
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+ -
+
+
+ Anzahl Abfragen:
+
+
+
+ -
+
+
+ 0
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+ -
+
+
+ Zuletzt hintereinander:
+
+
+
+ -
+
+
+ davon falsch:
+
+
+ 10
+
+
+
+ -
+
+
+ 0
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+ -
+
+
+ davon richtig:
+
+
+ 10
+
+
+
+ -
+
+
+
+ 40
+ 0
+
+
+
+ 0
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+ -
+
+
+ letze Abfrage:
+
+
+
+ -
+
+
+ noch nie
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+ -
+
+
+ 0
+
+
+ 6
+
+
-
+
+
+ Ahnungslos
+
+
+
+ -
+
+
+
+ 16
+ 16777215
+
+
+
+
+
+
+ :/icons/16x16/level0.png
+
+
+
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ -
+
+
+
+ 10
+ 75
+ true
+
+
+
+ Heutiger Lernaufwand
+
+
+
+ -
+
+
+ 0
+
+
+ 6
+
+
-
+
+
+ 0 h 00 m
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+ -
+
+
+ Anzahl Abfragen:
+
+
+
+ -
+
+
+ 0
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+ -
+
+
+ davon richtig:
+
+
+ 10
+
+
+
+ -
+
+
+ Dauer:
+
+
+
+ -
+
+
+ davon falsch:
+
+
+ 10
+
+
+
+ -
+
+
+ 0
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+ -
+
+
+ 0
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 330
+ 20
+
+
+
+
+
+
+
+ -
+
+
+
+ 400
+ 16777215
+
+
+
+ Lernassistent
+
+
+
+ 9
+
+
+ 6
+
+
-
+
+
+ 0
+
+
+ 6
+
+
-
+
+
+
+
+
+ :/icons/16x16/idea.png
+
+
+
+ -
+
+
+
+ 7
+ 1
+ 0
+ 0
+
+
+
+ TextLabel
+
+
+ true
+
+
+
+
+
+ -
+
+
+ 0
+
+
+ 6
+
+
-
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+ Kapitelübersicht ...
+
+
+ :/icons/16x16/idea_info.png
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/dlglearnassistant.cpp b/dlglearnassistant.cpp
new file mode 100644
index 0000000..abf9142
--- /dev/null
+++ b/dlglearnassistant.cpp
@@ -0,0 +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;
+}
diff --git a/dlglearnassistant.h b/dlglearnassistant.h
new file mode 100644
index 0000000..bc8ff9d
--- /dev/null
+++ b/dlglearnassistant.h
@@ -0,0 +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
diff --git a/dlglearnassistant.ui b/dlglearnassistant.ui
new file mode 100644
index 0000000..a38797b
--- /dev/null
+++ b/dlglearnassistant.ui
@@ -0,0 +1,112 @@
+
+ DlgLearnAssistant
+
+
+
+ 0
+ 0
+ 493
+ 422
+
+
+
+ Lern-Assistent
+
+
+ :/icons/16x16/idea_info.png
+
+
+
+ 9
+
+
+ 6
+
+ -
+
+
+ Vorschläge des Lern-Assistents
+
+
+
+ 9
+
+
+ 6
+
+
-
+
+
+ Alle heute zu wiederholenden Fragen lernen
+
+
+
+ -
+
+
+ TextLabel
+
+
+ 20
+
+
+
+ -
+
+
+ Kapitel aus folgender Liste lernen
+
+
+
+ -
+
+
+ false
+
+
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+ QDialogButtonBox::Cancel|QDialogButtonBox::NoButton|QDialogButtonBox::Ok
+
+
+ true
+
+
+
+
+
+
+ rbRepeat
+ rbList
+ buttonBox
+
+
+
+
+
+
+ buttonBox
+ rejected()
+ DlgLearnAssistant
+ reject()
+
+
+ 293
+ 397
+
+
+ 416
+ 418
+
+
+
+
+
diff --git a/dlglearnstatistic.cpp b/dlglearnstatistic.cpp
new file mode 100644
index 0000000..557be68
--- /dev/null
+++ b/dlglearnstatistic.cpp
@@ -0,0 +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);
+}
diff --git a/dlglearnstatistic.h b/dlglearnstatistic.h
new file mode 100644
index 0000000..35ea607
--- /dev/null
+++ b/dlglearnstatistic.h
@@ -0,0 +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;
+};
diff --git a/dlglearnstatistic.ui b/dlglearnstatistic.ui
new file mode 100644
index 0000000..bd8efd1
--- /dev/null
+++ b/dlglearnstatistic.ui
@@ -0,0 +1,991 @@
+
+ DlgLearnStatistic
+
+
+
+ 0
+ 0
+ 502
+ 552
+
+
+
+ Lernstatistik
+
+
+
+ 9
+
+
+ 6
+
+ -
+
+
+
+ MS Shell Dlg 2
+ 10
+ 75
+ false
+ true
+ false
+ false
+
+
+
+ Kapitel-Name
+
+
+ Qt::AlignCenter
+
+
+
+ -
+
+
+ 0
+
+
+
+ Aktueller Status
+
+
+
+ 9
+
+
+ 6
+
+
-
+
+
+
+ 75
+ true
+
+
+
+ Lernfortschritt
+
+
+
+ -
+
+
+ 0
+
+
+ 6
+
+
-
+
+
+ Durchschnittlicher Lernfortschritt:
+
+
+
+ -
+
+
+ TextLabel
+
+
+
+ -
+
+
+
+
+
+
+ -
+
+
+ 0
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+ -
+
+
+ Anfänger:
+
+
+ 20
+
+
+
+ -
+
+
+ 0
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+ -
+
+
+ 0.0 = Ahnungslos<br>1.0 = Anfänger<br>2.0 = Fortgeschritten<br>3.0 = Experte<br>4.0 = Freak<br>5.0 = Professor
+
+
+ 0.0
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+ -
+
+
+ Freak:
+
+
+ 20
+
+
+
+ -
+
+
+ TextLabel
+
+
+
+ -
+
+
+ Gesamt-Anzahl der Fragen dieses Kapitels
+
+
+ 0
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+ -
+
+
+ 0
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+ -
+
+
+ Fortgeschritten:
+
+
+ 20
+
+
+
+ -
+
+
+ 0
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+ -
+
+
+
+ 40
+ 0
+
+
+
+ 0
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+ -
+
+
+
+ 1
+ 5
+ 0
+ 0
+
+
+
+
+
+
+ :/icons/16x16/level3.png
+
+
+
+ -
+
+
+
+ 1
+ 5
+ 0
+ 0
+
+
+
+
+
+
+ :/icons/16x16/level0.png
+
+
+
+ -
+
+
+ Anzahl der Fragen:
+
+
+
+ -
+
+
+ 0
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+ -
+
+
+ TextLabel
+
+
+
+ -
+
+
+ Experte:
+
+
+ 20
+
+
+
+ -
+
+
+ Professor:
+
+
+ 20
+
+
+
+ -
+
+
+
+ 1
+ 5
+ 0
+ 0
+
+
+
+
+
+
+ :/icons/16x16/level2.png
+
+
+
+ -
+
+
+
+
+
+ :/icons/16x16/level0.png
+
+
+
+ -
+
+
+ Smiley mit Doktorhut
+
+
+
+
+
+ :/icons/16x16/level5.png
+
+
+
+ -
+
+
+
+ 1
+ 5
+ 0
+ 0
+
+
+
+
+
+
+ :/icons/16x16/level4.png
+
+
+
+ -
+
+
+ TextLabel
+
+
+
+ -
+
+
+
+ 3
+ 5
+ 0
+ 0
+
+
+
+ Ahnungslos:
+
+
+ 20
+
+
+
+ -
+
+
+ TextLabel
+
+
+
+ -
+
+
+ TextLabel
+
+
+
+ -
+
+
+
+ 1
+ 5
+ 0
+ 0
+
+
+
+
+
+
+ :/icons/16x16/level1.png
+
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ -
+
+
+
+ 75
+ true
+
+
+
+ Abfragen
+
+
+
+ -
+
+
+ 0
+
+
+ 6
+
+
-
+
+
+ 0
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+ -
+
+
+ 0.0 %
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+ -
+
+
+ davon falsche Antworten:
+
+
+ 20
+
+
+
+ -
+
+
+ davon richtige Antworten:
+
+
+ 20
+
+
+
+ -
+
+
+ 0
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+ -
+
+
+ Abfragen gesamt:
+
+
+
+ -
+
+
+ 0
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+ -
+
+
+ 0.0 %
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+ -
+
+
+
+ true
+
+
+
+ Anzahl
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+ -
+
+
+
+ true
+
+
+
+ Anteil
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ -
+
+
+
+ 75
+ true
+
+
+
+ Zeitaufwand
+
+
+
+ -
+
+
+ 0
+
+
+ 6
+
+
-
+
+
+ 0.0 %
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+ -
+
+
+ 0.0 %
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+ -
+
+
+ davon für richtige Antworten:
+
+
+ 20
+
+
+
+ -
+
+
+ 0 m 0 s
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+ -
+
+
+ 0 m 0 s
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+ -
+
+
+ Zeitaufwand gesamt:
+
+
+
+ -
+
+
+ 0
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+ -
+
+
+ 0
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+ -
+
+
+ davon für falsche Antworten:
+
+
+ 20
+
+
+
+ -
+
+
+ 0
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+ -
+
+
+
+ true
+
+
+
+ Dauer
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+ -
+
+
+
+ true
+
+
+
+ Anteil
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+ -
+
+
+
+ true
+
+
+
+ Ø pro Frage
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+ -
+
+
+ 0 m 0 s
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 40
+
+
+
+
+
+
+
+
+ Zeitlicher Verlauf
+
+
+
+ 9
+
+
+ 6
+
+ -
+
+
+ 0
+
+
+ 6
+
+
-
+
+
+ Zeitraum:
+
+
+
+ -
+
+
+
+ 200
+ 0
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+
+
+ -
+
+
+
+ 0
+ 200
+
+
+
+ Lernfortschritt
+
+
+
+ 9
+
+
+ 6
+
+
+
+
+ -
+
+
+
+ 0
+ 200
+
+
+
+ Abfragen
+
+
+
+ 9
+
+
+ 6
+
+
+
+
+
+
+
+
+ Zeitaufwand
+
+
+
+ 9
+
+
+ 6
+
+ -
+
+
+ 0
+
+
+ 6
+
+
-
+
+
+ Zeitraum:
+
+
+
+ -
+
+
+
+ 200
+ 0
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+
+
+ -
+
+
+
+ 0
+ 200
+
+
+
+ Zeitaufwand [min]
+
+
+
+ 9
+
+
+ 6
+
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 40
+
+
+
+
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+ QDialogButtonBox::Ok
+
+
+ true
+
+
+
+
+
+
+ tabWidget
+ buttonBox
+ cbPeriod
+
+
+
+
+ buttonBox
+ accepted()
+ DlgLearnStatistic
+ accept()
+
+
+ 248
+ 254
+
+
+ 157
+ 274
+
+
+
+
+ buttonBox
+ rejected()
+ DlgLearnStatistic
+ reject()
+
+
+ 316
+ 260
+
+
+ 286
+ 274
+
+
+
+
+
diff --git a/dlgviewquestion.cpp b/dlgviewquestion.cpp
new file mode 100644
index 0000000..2734ccb
--- /dev/null
+++ b/dlgviewquestion.cpp
@@ -0,0 +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();
+}
diff --git a/dlgviewquestion.h b/dlgviewquestion.h
new file mode 100644
index 0000000..757ceff
--- /dev/null
+++ b/dlgviewquestion.h
@@ -0,0 +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
diff --git a/dlgviewquestion.ui b/dlgviewquestion.ui
new file mode 100644
index 0000000..af758bc
--- /dev/null
+++ b/dlgviewquestion.ui
@@ -0,0 +1,99 @@
+
+ DlgViewQuestion
+
+
+
+ 0
+ 0
+ 658
+ 577
+
+
+
+ Frage ansehen
+
+
+
+ 9
+
+
+ 6
+
+ -
+
+
+
+ 10
+
+
+
+
+ -
+
+
+ 0
+
+
+ 6
+
+
-
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+ OK
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+
+
+
+
+
+ textBrowser
+ pbOK
+
+
+
+
+ pbOK
+ clicked()
+ DlgViewQuestion
+ accept()
+
+
+ 367
+ 566
+
+
+ 358
+ 293
+
+
+
+
+
diff --git a/error.cpp b/error.cpp
new file mode 100644
index 0000000..caa25b1
--- /dev/null
+++ b/error.cpp
@@ -0,0 +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);
+}
+
diff --git a/error.h b/error.h
new file mode 100644
index 0000000..2036394
--- /dev/null
+++ b/error.h
@@ -0,0 +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
diff --git a/exam.cpp b/exam.cpp
new file mode 100644
index 0000000..b87b532
--- /dev/null
+++ b/exam.cpp
@@ -0,0 +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
+#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
new file mode 100644
index 0000000..a87b3eb
--- /dev/null
+++ b/helper.cpp
@@ -0,0 +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 += ""+author()+" | ";
+ str += ""+dateString()+" |
";
+ str += ""+text()+" |
";
+ str += " |
";
+ str += "
";
+*/
+
+ str += "" + text() + "
"; //"" + author() + " " + date() + "
";
+ return (str);
+}
diff --git a/helper.h b/helper.h
new file mode 100644
index 0000000..d2ece47
--- /dev/null
+++ b/helper.h
@@ -0,0 +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
diff --git a/icons/16x16/book1.png b/icons/16x16/book1.png
new file mode 100644
index 0000000..56b98e0
Binary files /dev/null and b/icons/16x16/book1.png differ
diff --git a/icons/16x16/button_cancel.png b/icons/16x16/button_cancel.png
new file mode 100644
index 0000000..2415dfc
Binary files /dev/null and b/icons/16x16/button_cancel.png differ
diff --git a/icons/16x16/button_ok.png b/icons/16x16/button_ok.png
new file mode 100644
index 0000000..543710f
Binary files /dev/null and b/icons/16x16/button_ok.png differ
diff --git a/icons/16x16/cancel.png b/icons/16x16/cancel.png
new file mode 100644
index 0000000..6b990a2
Binary files /dev/null and b/icons/16x16/cancel.png differ
diff --git a/icons/16x16/clock.png b/icons/16x16/clock.png
new file mode 100644
index 0000000..dca2c7e
Binary files /dev/null and b/icons/16x16/clock.png differ
diff --git a/icons/16x16/contexthelp.png b/icons/16x16/contexthelp.png
new file mode 100644
index 0000000..7b88a0c
Binary files /dev/null and b/icons/16x16/contexthelp.png differ
diff --git a/icons/16x16/exit.png b/icons/16x16/exit.png
new file mode 100644
index 0000000..a77152b
Binary files /dev/null and b/icons/16x16/exit.png differ
diff --git a/icons/16x16/filenew.png b/icons/16x16/filenew.png
new file mode 100644
index 0000000..83f3752
Binary files /dev/null and b/icons/16x16/filenew.png differ
diff --git a/icons/16x16/fileopen.png b/icons/16x16/fileopen.png
new file mode 100644
index 0000000..037c2da
Binary files /dev/null and b/icons/16x16/fileopen.png differ
diff --git a/icons/16x16/filesave.png b/icons/16x16/filesave.png
new file mode 100644
index 0000000..41b3f43
Binary files /dev/null and b/icons/16x16/filesave.png differ
diff --git a/icons/16x16/filesaveas.png b/icons/16x16/filesaveas.png
new file mode 100644
index 0000000..3e28d5d
Binary files /dev/null and b/icons/16x16/filesaveas.png differ
diff --git a/icons/16x16/finish.png b/icons/16x16/finish.png
new file mode 100644
index 0000000..64609dd
Binary files /dev/null and b/icons/16x16/finish.png differ
diff --git a/icons/16x16/folder.png b/icons/16x16/folder.png
new file mode 100644
index 0000000..7b6f0a7
Binary files /dev/null and b/icons/16x16/folder.png differ
diff --git a/icons/16x16/help.png b/icons/16x16/help.png
new file mode 100644
index 0000000..b580f51
Binary files /dev/null and b/icons/16x16/help.png differ
diff --git a/icons/16x16/idea.png b/icons/16x16/idea.png
new file mode 100644
index 0000000..8d72794
Binary files /dev/null and b/icons/16x16/idea.png differ
diff --git a/icons/16x16/idea_gray.png b/icons/16x16/idea_gray.png
new file mode 100644
index 0000000..d83467d
Binary files /dev/null and b/icons/16x16/idea_gray.png differ
diff --git a/icons/16x16/idea_info.png b/icons/16x16/idea_info.png
new file mode 100644
index 0000000..d05a927
Binary files /dev/null and b/icons/16x16/idea_info.png differ
diff --git a/icons/16x16/info.png b/icons/16x16/info.png
new file mode 100644
index 0000000..1903fab
Binary files /dev/null and b/icons/16x16/info.png differ
diff --git a/icons/16x16/level0.png b/icons/16x16/level0.png
new file mode 100644
index 0000000..2190643
Binary files /dev/null and b/icons/16x16/level0.png differ
diff --git a/icons/16x16/level1.png b/icons/16x16/level1.png
new file mode 100644
index 0000000..611e8f8
Binary files /dev/null and b/icons/16x16/level1.png differ
diff --git a/icons/16x16/level1a.png b/icons/16x16/level1a.png
new file mode 100644
index 0000000..c808d88
Binary files /dev/null and b/icons/16x16/level1a.png differ
diff --git a/icons/16x16/level2.png b/icons/16x16/level2.png
new file mode 100644
index 0000000..1659635
Binary files /dev/null and b/icons/16x16/level2.png differ
diff --git a/icons/16x16/level2a.png b/icons/16x16/level2a.png
new file mode 100644
index 0000000..880003b
Binary files /dev/null and b/icons/16x16/level2a.png differ
diff --git a/icons/16x16/level3.png b/icons/16x16/level3.png
new file mode 100644
index 0000000..8737a36
Binary files /dev/null and b/icons/16x16/level3.png differ
diff --git a/icons/16x16/level3a.png b/icons/16x16/level3a.png
new file mode 100644
index 0000000..2b8eb47
Binary files /dev/null and b/icons/16x16/level3a.png differ
diff --git a/icons/16x16/level4.png b/icons/16x16/level4.png
new file mode 100644
index 0000000..de202ca
Binary files /dev/null and b/icons/16x16/level4.png differ
diff --git a/icons/16x16/level5.png b/icons/16x16/level5.png
new file mode 100644
index 0000000..245f0bd
Binary files /dev/null and b/icons/16x16/level5.png differ
diff --git a/icons/16x16/next.png b/icons/16x16/next.png
new file mode 100644
index 0000000..a930053
Binary files /dev/null and b/icons/16x16/next.png differ
diff --git a/icons/16x16/previous.png b/icons/16x16/previous.png
new file mode 100644
index 0000000..f258c11
Binary files /dev/null and b/icons/16x16/previous.png differ
diff --git a/icons/16x16/start.png b/icons/16x16/start.png
new file mode 100644
index 0000000..56a6a5d
Binary files /dev/null and b/icons/16x16/start.png differ
diff --git a/icons/16x16/stats.png b/icons/16x16/stats.png
new file mode 100644
index 0000000..acbfe1e
Binary files /dev/null and b/icons/16x16/stats.png differ
diff --git a/icons/16x16/stop.png b/icons/16x16/stop.png
new file mode 100644
index 0000000..4d84554
Binary files /dev/null and b/icons/16x16/stop.png differ
diff --git a/icons/16x16/viewmag.png b/icons/16x16/viewmag.png
new file mode 100644
index 0000000..593a566
Binary files /dev/null and b/icons/16x16/viewmag.png differ
diff --git a/icons/32x32/idea.png b/icons/32x32/idea.png
new file mode 100644
index 0000000..0ad4e99
Binary files /dev/null and b/icons/32x32/idea.png differ
diff --git a/icons/32x32/qt.png b/icons/32x32/qt.png
new file mode 100644
index 0000000..a616ea0
Binary files /dev/null and b/icons/32x32/qt.png differ
diff --git a/icons/64x16-old/veryrare.png b/icons/64x16-old/veryrare.png
new file mode 100644
index 0000000..8cfbdad
Binary files /dev/null and b/icons/64x16-old/veryrare.png differ
diff --git a/icons/64x16/level0.png b/icons/64x16/level0.png
new file mode 100644
index 0000000..273ce2b
Binary files /dev/null and b/icons/64x16/level0.png differ
diff --git a/icons/64x16/level1.png b/icons/64x16/level1.png
new file mode 100644
index 0000000..2ac9295
Binary files /dev/null and b/icons/64x16/level1.png differ
diff --git a/icons/64x16/level2.png b/icons/64x16/level2.png
new file mode 100644
index 0000000..b2724b0
Binary files /dev/null and b/icons/64x16/level2.png differ
diff --git a/icons/64x16/level3.png b/icons/64x16/level3.png
new file mode 100644
index 0000000..bd85ba5
Binary files /dev/null and b/icons/64x16/level3.png differ
diff --git a/icons/64x16/level4.png b/icons/64x16/level4.png
new file mode 100644
index 0000000..4503d28
Binary files /dev/null and b/icons/64x16/level4.png differ
diff --git a/icons/level.xcf b/icons/level.xcf
new file mode 100644
index 0000000..d79401d
Binary files /dev/null and b/icons/level.xcf differ
diff --git a/icons/veryrare.xcf b/icons/veryrare.xcf
new file mode 100644
index 0000000..a043d91
Binary files /dev/null and b/icons/veryrare.xcf differ
diff --git a/main.cpp b/main.cpp
new file mode 100644
index 0000000..e3fe576
--- /dev/null
+++ b/main.cpp
@@ -0,0 +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();
+}
diff --git a/mainwindow.cpp b/mainwindow.cpp
new file mode 100644
index 0000000..6ea2908
--- /dev/null
+++ b/mainwindow.cpp
@@ -0,0 +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();
+}
+*/
diff --git a/mainwindow.h b/mainwindow.h
new file mode 100644
index 0000000..5fdae48
--- /dev/null
+++ b/mainwindow.h
@@ -0,0 +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
+
diff --git a/mainwindow.ui b/mainwindow.ui
new file mode 100644
index 0000000..196b33a
--- /dev/null
+++ b/mainwindow.ui
@@ -0,0 +1,397 @@
+
+ MainWindow
+
+
+
+ 0
+ 0
+ 800
+ 600
+
+
+
+ AFUTrainer 3.0
+
+
+ :/icons/32x32/idea.png
+
+
+
+
+
+
+ Qt::Horizontal
+
+
+ 4
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ :/icons/16x16/fileopen.png
+
+
+ Ö&ffnen
+
+
+ Öffnet einen Fragenkatalog
+
+
+ Öffnet einen Fragenkatalog
+
+
+ <b>Datei öffnen</b><p>Öffnet einen existierenden Fragenkatalog
+
+
+ Ctrl+O
+
+
+
+
+ :/icons/16x16/exit.png
+
+
+ &Beenden
+
+
+ Beendet den AFUTrainer
+
+
+ Beendet den AFUTrainer
+
+
+ <b>Beenden</b><p>Beendet den AFUTrainer
+
+
+ Alt+F4
+
+
+
+
+ true
+
+
+ true
+
+
+ &Werkzeugleiste
+
+
+ Aktiviert/deaktiviert die Werkzeugleiste
+
+
+ Aktiviert/deaktiviert die Werkzeugleiste
+
+
+ <b>Werkzeugleiste</b><p>Aktiviert/deaktiviert die Werkzeugleiste
+
+
+
+
+ true
+
+
+ true
+
+
+ &Statusleiste
+
+
+ Aktiviert/deaktiviert die Statusleiste
+
+
+ Aktiviert/deaktiviert die Statusleiste
+
+
+ <b>Statusleiste</b><p>Aktiviert/deaktiviert die Statusleiste
+
+
+
+
+ :/icons/16x16/idea.png
+
+
+ &Ãœber...
+
+
+ Ãœber
+
+
+ Ãœber den AFUTrainer
+
+
+ Ãœber den AFUTrainer
+
+
+ <b>Ãœber</b><p>Copyright-Hinweise und Autoren
+
+
+
+
+ :/icons/16x16/contexthelp.png
+
+
+ Was ist &das?
+
+
+ Was ist das?
+
+
+ Shift+F1
+
+
+
+
+ :/icons/16x16/viewmag.png
+
+
+ &Ansehen...
+
+
+ Zeigt ausgewählte Frage an
+
+
+ Zeigt ausgewählte Frage an
+
+
+ <b>Frage ansehen</b><p>Zeigt die Frage mit Anworten und Statistik<br>in einem neuen Fenster an
+
+
+ F3
+
+
+
+
+ :/icons/16x16/idea.png
+
+
+ &Lernen...
+
+
+ Fragen lernen
+
+
+ Fragen lernen
+
+
+ <b>Fragen lernen</b><p>Hier können Sie die Fragen lernen. Eine Frage wird so lange abgefragt, bis Sie sie richtig beantworten können.
+
+
+ F4
+
+
+
+
+ :/icons/16x16/stats.png
+
+
+ &Lernstatistik...
+
+
+ Zeigt Lernstatistik des ausgewählten Kapitels an
+
+
+ Zeigt Lernstatistik des ausgewählten Kapitels an
+
+
+ <b>Lernstatistik</b><p>Zeigt Lernstatistik des ausgewählten Kapitels an
+
+
+
+
+ :/icons/16x16/clock.png
+
+
+ &Prüfungssimulation...
+
+
+ Simuliert die Prüfung
+
+
+ Simuliert die Prüfung
+
+
+ <b>Prüfungssimulation</b><p>Sie erhalten zufällig generierte Fragenbögen,<br>die Sie in vorgegebener Zeit lösen müssen!
+
+
+ F6
+
+
+
+
+ &Prüfstatistik...
+
+
+ Zeigt Ergebnisse der Prüfungssimulationen an
+
+
+ Zeigt Ergebnisse der Prüfungssimulationen an
+
+
+ <b>Prüfstatistik</b><p>Sie erhalten eine detaillierte Auflistung<br>der Ergebnisse der Prüfungssimulationen.
+
+
+
+
+ :/icons/32x32/qt.png
+
+
+ Ãœber &Qt...
+
+
+ Ãœber die Qt-Bibliothek
+
+
+ Ãœber die Qt-Bibliothek
+
+
+ <b>Ãœber Qt</b><p>Zeigt Copyright-Hinweise zur verwendeten Qt-Bibliothek an
+
+
+
+
+ &Import DE...
+
+
+ Import DE
+
+
+ Import DE
+
+
+
+
+ :/icons/16x16/filenew.png
+
+
+ &Neu
+
+
+
+
+ :/icons/16x16/filesave.png
+
+
+ &Speichern
+
+
+
+
+ :/icons/16x16/filesaveas.png
+
+
+ Speichern &unter...
+
+
+
+
+ :/icons/16x16/idea_info.png
+
+
+ Lern-Assistent...
+
+
+ Ruft den Lern-Assistenten auf
+
+
+ Ruft den Lern-Assistenten auf
+
+
+ <b>Lern-Assistent</b><p>Der Lern-Assistent hilft Ihnen beim Auswahl der zu lerndenden Fragen.
+
+
+ F5
+
+
+
+
+ Import US...
+
+
+
+
+ :/icons/16x16/info.png
+
+
+ &Information...
+
+
+ Informationen zum Fragenkatalog
+
+
+ Zeigt Informationen zum Fragenkatalog an.
+
+
+ <b>Information</b><p>Zeigt Informationen, wie z.B. Gültigkeitseitraum, Herausgeber, Version,... zum aktuell geladenen Fragenkatalog an.
+
+
+
+
+
+
+
+
diff --git a/osziparchive.cpp b/osziparchive.cpp
new file mode 100644
index 0000000..e5d998b
--- /dev/null
+++ b/osziparchive.cpp
@@ -0,0 +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;
+}
+
diff --git a/osziparchive.h b/osziparchive.h
new file mode 100644
index 0000000..f1abba0
--- /dev/null
+++ b/osziparchive.h
@@ -0,0 +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
+
diff --git a/plotwidget.cpp b/plotwidget.cpp
new file mode 100644
index 0000000..c84054f
--- /dev/null
+++ b/plotwidget.cpp
@@ -0,0 +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
+#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
new file mode 100644
index 0000000..bb6682d
--- /dev/null
+++ b/question.cpp
@@ -0,0 +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";
+ // Icon
+ str += " | ";
+ // Answer number
+ str += QString(" %1 | ").arg(QChar('A' + i));
+ // Text
+ str += "" + m_listAnswer[i].text() + " | ";
+ str += "";
+ }
+ str += "
";
+ strHints = pCatalog->hintText(id());
+ if (!strHints.isEmpty())
+ {
+ str += "Hilfestellung
" + strHints;
+ }
+ str += "Abfrageverlauf
";
+ str += lastClickedTextExtended();
+ str += " " + repeatDateTextExtended();
+ if (!m_listAnswerClicked.isEmpty())
+ {
+ str += "
Datum/Uhrzeit | Antwort | Richtig? | Benötigte Zeit |
";
+ for (int i=0; i" + ac.dateTime().toString(Qt::LocalDate) + " | " + ac.answerText() + " | ";
+ if (ac.isCorrect(m_listAnswer))
+ str += tr("richtig");
+ else
+ str += tr("falsch");
+ str += " | " + QString ("%1").arg(ac.neededTimeText()) + " | ";
+ }
+ str += "
";
+ }
+ //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";
+ // Answer number
+ str += QString(" %1 | ").arg(QChar('A' + i));
+ // Text
+ str += "";
+ //CHEAT: if (mixedAnswerAt(i).isCorrect()) str += "OK ";
+ str += learnAnswerAt(i).text();
+ str += " | ";
+ }
+ 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
new file mode 100644
index 0000000..8553e07
--- /dev/null
+++ b/question.h
@@ -0,0 +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
diff --git a/questionmodel.cpp b/questionmodel.cpp
new file mode 100644
index 0000000..23417ec
--- /dev/null
+++ b/questionmodel.cpp
@@ -0,0 +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();
+}
+
diff --git a/questionmodel.h b/questionmodel.h
new file mode 100644
index 0000000..2b1b118
--- /dev/null
+++ b/questionmodel.h
@@ -0,0 +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
diff --git a/recentfiles.cpp b/recentfiles.cpp
new file mode 100644
index 0000000..337d298
--- /dev/null
+++ b/recentfiles.cpp
@@ -0,0 +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);
+}
diff --git a/recentfiles.h b/recentfiles.h
new file mode 100644
index 0000000..ae8ac09
--- /dev/null
+++ b/recentfiles.h
@@ -0,0 +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;
+};
diff --git a/tools.cpp b/tools.cpp
new file mode 100644
index 0000000..06a6337
--- /dev/null
+++ b/tools.cpp
@@ -0,0 +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;
+}
diff --git a/tools.h b/tools.h
new file mode 100644
index 0000000..1f759bd
--- /dev/null
+++ b/tools.h
@@ -0,0 +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
diff --git a/translations/qt_de.qm b/translations/qt_de.qm
new file mode 100644
index 0000000..da88adf
Binary files /dev/null and b/translations/qt_de.qm differ
diff --git a/zlib/zconf.h b/zlib/zconf.h
new file mode 100644
index 0000000..03a9431
--- /dev/null
+++ b/zlib/zconf.h
@@ -0,0 +1,332 @@
+/* zconf.h -- configuration of the zlib compression library
+ * Copyright (C) 1995-2005 Jean-loup Gailly.
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* @(#) $Id$ */
+
+#ifndef ZCONF_H
+#define ZCONF_H
+
+/*
+ * If you *really* need a unique prefix for all types and library functions,
+ * compile with -DZ_PREFIX. The "standard" zlib should be compiled without it.
+ */
+#ifdef Z_PREFIX
+# define deflateInit_ z_deflateInit_
+# define deflate z_deflate
+# define deflateEnd z_deflateEnd
+# define inflateInit_ z_inflateInit_
+# define inflate z_inflate
+# define inflateEnd z_inflateEnd
+# define deflateInit2_ z_deflateInit2_
+# define deflateSetDictionary z_deflateSetDictionary
+# define deflateCopy z_deflateCopy
+# define deflateReset z_deflateReset
+# define deflateParams z_deflateParams
+# define deflateBound z_deflateBound
+# define deflatePrime z_deflatePrime
+# define inflateInit2_ z_inflateInit2_
+# define inflateSetDictionary z_inflateSetDictionary
+# define inflateSync z_inflateSync
+# define inflateSyncPoint z_inflateSyncPoint
+# define inflateCopy z_inflateCopy
+# define inflateReset z_inflateReset
+# define inflateBack z_inflateBack
+# define inflateBackEnd z_inflateBackEnd
+# define compress z_compress
+# define compress2 z_compress2
+# define compressBound z_compressBound
+# define uncompress z_uncompress
+# define adler32 z_adler32
+# define crc32 z_crc32
+# define get_crc_table z_get_crc_table
+# define zError z_zError
+
+# define alloc_func z_alloc_func
+# define free_func z_free_func
+# define in_func z_in_func
+# define out_func z_out_func
+# define Byte z_Byte
+# define uInt z_uInt
+# define uLong z_uLong
+# define Bytef z_Bytef
+# define charf z_charf
+# define intf z_intf
+# define uIntf z_uIntf
+# define uLongf z_uLongf
+# define voidpf z_voidpf
+# define voidp z_voidp
+#endif
+
+#if defined(__MSDOS__) && !defined(MSDOS)
+# define MSDOS
+#endif
+#if (defined(OS_2) || defined(__OS2__)) && !defined(OS2)
+# define OS2
+#endif
+#if defined(_WINDOWS) && !defined(WINDOWS)
+# define WINDOWS
+#endif
+#if defined(_WIN32) || defined(_WIN32_WCE) || defined(__WIN32__)
+# ifndef WIN32
+# define WIN32
+# endif
+#endif
+#if (defined(MSDOS) || defined(OS2) || defined(WINDOWS)) && !defined(WIN32)
+# if !defined(__GNUC__) && !defined(__FLAT__) && !defined(__386__)
+# ifndef SYS16BIT
+# define SYS16BIT
+# endif
+# endif
+#endif
+
+/*
+ * Compile with -DMAXSEG_64K if the alloc function cannot allocate more
+ * than 64k bytes at a time (needed on systems with 16-bit int).
+ */
+#ifdef SYS16BIT
+# define MAXSEG_64K
+#endif
+#ifdef MSDOS
+# define UNALIGNED_OK
+#endif
+
+#ifdef __STDC_VERSION__
+# ifndef STDC
+# define STDC
+# endif
+# if __STDC_VERSION__ >= 199901L
+# ifndef STDC99
+# define STDC99
+# endif
+# endif
+#endif
+#if !defined(STDC) && (defined(__STDC__) || defined(__cplusplus))
+# define STDC
+#endif
+#if !defined(STDC) && (defined(__GNUC__) || defined(__BORLANDC__))
+# define STDC
+#endif
+#if !defined(STDC) && (defined(MSDOS) || defined(WINDOWS) || defined(WIN32))
+# define STDC
+#endif
+#if !defined(STDC) && (defined(OS2) || defined(__HOS_AIX__))
+# define STDC
+#endif
+
+#if defined(__OS400__) && !defined(STDC) /* iSeries (formerly AS/400). */
+# define STDC
+#endif
+
+#ifndef STDC
+# ifndef const /* cannot use !defined(STDC) && !defined(const) on Mac */
+# define const /* note: need a more gentle solution here */
+# endif
+#endif
+
+/* Some Mac compilers merge all .h files incorrectly: */
+#if defined(__MWERKS__)||defined(applec)||defined(THINK_C)||defined(__SC__)
+# define NO_DUMMY_DECL
+#endif
+
+/* Maximum value for memLevel in deflateInit2 */
+#ifndef MAX_MEM_LEVEL
+# ifdef MAXSEG_64K
+# define MAX_MEM_LEVEL 8
+# else
+# define MAX_MEM_LEVEL 9
+# endif
+#endif
+
+/* Maximum value for windowBits in deflateInit2 and inflateInit2.
+ * WARNING: reducing MAX_WBITS makes minigzip unable to extract .gz files
+ * created by gzip. (Files created by minigzip can still be extracted by
+ * gzip.)
+ */
+#ifndef MAX_WBITS
+# define MAX_WBITS 15 /* 32K LZ77 window */
+#endif
+
+/* The memory requirements for deflate are (in bytes):
+ (1 << (windowBits+2)) + (1 << (memLevel+9))
+ that is: 128K for windowBits=15 + 128K for memLevel = 8 (default values)
+ plus a few kilobytes for small objects. For example, if you want to reduce
+ the default memory requirements from 256K to 128K, compile with
+ make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7"
+ Of course this will generally degrade compression (there's no free lunch).
+
+ The memory requirements for inflate are (in bytes) 1 << windowBits
+ that is, 32K for windowBits=15 (default value) plus a few kilobytes
+ for small objects.
+*/
+
+ /* Type declarations */
+
+#ifndef OF /* function prototypes */
+# ifdef STDC
+# define OF(args) args
+# else
+# define OF(args) ()
+# endif
+#endif
+
+/* The following definitions for FAR are needed only for MSDOS mixed
+ * model programming (small or medium model with some far allocations).
+ * This was tested only with MSC; for other MSDOS compilers you may have
+ * to define NO_MEMCPY in zutil.h. If you don't need the mixed model,
+ * just define FAR to be empty.
+ */
+#ifdef SYS16BIT
+# if defined(M_I86SM) || defined(M_I86MM)
+ /* MSC small or medium model */
+# define SMALL_MEDIUM
+# ifdef _MSC_VER
+# define FAR _far
+# else
+# define FAR far
+# endif
+# endif
+# if (defined(__SMALL__) || defined(__MEDIUM__))
+ /* Turbo C small or medium model */
+# define SMALL_MEDIUM
+# ifdef __BORLANDC__
+# define FAR _far
+# else
+# define FAR far
+# endif
+# endif
+#endif
+
+#if defined(WINDOWS) || defined(WIN32)
+ /* If building or using zlib as a DLL, define ZLIB_DLL.
+ * This is not mandatory, but it offers a little performance increase.
+ */
+# ifdef ZLIB_DLL
+# if defined(WIN32) && (!defined(__BORLANDC__) || (__BORLANDC__ >= 0x500))
+# ifdef ZLIB_INTERNAL
+# define ZEXTERN extern __declspec(dllexport)
+# else
+# define ZEXTERN extern __declspec(dllimport)
+# endif
+# endif
+# endif /* ZLIB_DLL */
+ /* If building or using zlib with the WINAPI/WINAPIV calling convention,
+ * define ZLIB_WINAPI.
+ * Caution: the standard ZLIB1.DLL is NOT compiled using ZLIB_WINAPI.
+ */
+# ifdef ZLIB_WINAPI
+# ifdef FAR
+# undef FAR
+# endif
+# include
+ /* No need for _export, use ZLIB.DEF instead. */
+ /* For complete Windows compatibility, use WINAPI, not __stdcall. */
+# define ZEXPORT WINAPI
+# ifdef WIN32
+# define ZEXPORTVA WINAPIV
+# else
+# define ZEXPORTVA FAR CDECL
+# endif
+# endif
+#endif
+
+#if defined (__BEOS__)
+# ifdef ZLIB_DLL
+# ifdef ZLIB_INTERNAL
+# define ZEXPORT __declspec(dllexport)
+# define ZEXPORTVA __declspec(dllexport)
+# else
+# define ZEXPORT __declspec(dllimport)
+# define ZEXPORTVA __declspec(dllimport)
+# endif
+# endif
+#endif
+
+#ifndef ZEXTERN
+# define ZEXTERN extern
+#endif
+#ifndef ZEXPORT
+# define ZEXPORT
+#endif
+#ifndef ZEXPORTVA
+# define ZEXPORTVA
+#endif
+
+#ifndef FAR
+# define FAR
+#endif
+
+#if !defined(__MACTYPES__)
+typedef unsigned char Byte; /* 8 bits */
+#endif
+typedef unsigned int uInt; /* 16 bits or more */
+typedef unsigned long uLong; /* 32 bits or more */
+
+#ifdef SMALL_MEDIUM
+ /* Borland C/C++ and some old MSC versions ignore FAR inside typedef */
+# define Bytef Byte FAR
+#else
+ typedef Byte FAR Bytef;
+#endif
+typedef char FAR charf;
+typedef int FAR intf;
+typedef uInt FAR uIntf;
+typedef uLong FAR uLongf;
+
+#ifdef STDC
+ typedef void const *voidpc;
+ typedef void FAR *voidpf;
+ typedef void *voidp;
+#else
+ typedef Byte const *voidpc;
+ typedef Byte FAR *voidpf;
+ typedef Byte *voidp;
+#endif
+
+#if 0 /* HAVE_UNISTD_H -- this line is updated by ./configure */
+# include /* for off_t */
+# include /* for SEEK_* and off_t */
+# ifdef VMS
+# include /* for off_t */
+# endif
+# define z_off_t off_t
+#endif
+#ifndef SEEK_SET
+# define SEEK_SET 0 /* Seek from beginning of file. */
+# define SEEK_CUR 1 /* Seek from current position. */
+# define SEEK_END 2 /* Set file pointer to EOF plus "offset" */
+#endif
+#ifndef z_off_t
+# define z_off_t long
+#endif
+
+#if defined(__OS400__)
+# define NO_vsnprintf
+#endif
+
+#if defined(__MVS__)
+# define NO_vsnprintf
+# ifdef FAR
+# undef FAR
+# endif
+#endif
+
+/* MVS linker does not support external names larger than 8 bytes */
+#if defined(__MVS__)
+# pragma map(deflateInit_,"DEIN")
+# pragma map(deflateInit2_,"DEIN2")
+# pragma map(deflateEnd,"DEEND")
+# pragma map(deflateBound,"DEBND")
+# pragma map(inflateInit_,"ININ")
+# pragma map(inflateInit2_,"ININ2")
+# pragma map(inflateEnd,"INEND")
+# pragma map(inflateSync,"INSY")
+# pragma map(inflateSetDictionary,"INSEDI")
+# pragma map(compressBound,"CMBND")
+# pragma map(inflate_table,"INTABL")
+# pragma map(inflate_fast,"INFA")
+# pragma map(inflate_copyright,"INCOPY")
+#endif
+
+#endif /* ZCONF_H */
diff --git a/zlib/zdll.exp b/zlib/zdll.exp
new file mode 100644
index 0000000..2ca08c5
Binary files /dev/null and b/zlib/zdll.exp differ
diff --git a/zlib/zdll.lib b/zlib/zdll.lib
new file mode 100644
index 0000000..01f4e10
Binary files /dev/null and b/zlib/zdll.lib differ
diff --git a/zlib/zlib.def b/zlib/zlib.def
new file mode 100644
index 0000000..a47cbc1
--- /dev/null
+++ b/zlib/zlib.def
@@ -0,0 +1,60 @@
+LIBRARY
+; zlib data compression library
+
+EXPORTS
+; basic functions
+ zlibVersion
+ deflate
+ deflateEnd
+ inflate
+ inflateEnd
+; advanced functions
+ deflateSetDictionary
+ deflateCopy
+ deflateReset
+ deflateParams
+ deflateBound
+ deflatePrime
+ inflateSetDictionary
+ inflateSync
+ inflateCopy
+ inflateReset
+ inflateBack
+ inflateBackEnd
+ zlibCompileFlags
+; utility functions
+ compress
+ compress2
+ compressBound
+ uncompress
+ gzopen
+ gzdopen
+ gzsetparams
+ gzread
+ gzwrite
+ gzprintf
+ gzputs
+ gzgets
+ gzputc
+ gzgetc
+ gzungetc
+ gzflush
+ gzseek
+ gzrewind
+ gztell
+ gzeof
+ gzclose
+ gzerror
+ gzclearerr
+; checksum functions
+ adler32
+ crc32
+; various hacks, don't look :)
+ deflateInit_
+ deflateInit2_
+ inflateInit_
+ inflateInit2_
+ inflateBackInit_
+ inflateSyncPoint
+ get_crc_table
+ zError
diff --git a/zlib/zlib.h b/zlib/zlib.h
new file mode 100644
index 0000000..0228179
--- /dev/null
+++ b/zlib/zlib.h
@@ -0,0 +1,1357 @@
+/* zlib.h -- interface of the 'zlib' general purpose compression library
+ version 1.2.3, July 18th, 2005
+
+ Copyright (C) 1995-2005 Jean-loup Gailly and Mark Adler
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+ 3. This notice may not be removed or altered from any source distribution.
+
+ Jean-loup Gailly Mark Adler
+ jloup@gzip.org madler@alumni.caltech.edu
+
+
+ The data format used by the zlib library is described by RFCs (Request for
+ Comments) 1950 to 1952 in the files http://www.ietf.org/rfc/rfc1950.txt
+ (zlib format), rfc1951.txt (deflate format) and rfc1952.txt (gzip format).
+*/
+
+#ifndef ZLIB_H
+#define ZLIB_H
+
+#include "zconf.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define ZLIB_VERSION "1.2.3"
+#define ZLIB_VERNUM 0x1230
+
+/*
+ The 'zlib' compression library provides in-memory compression and
+ decompression functions, including integrity checks of the uncompressed
+ data. This version of the library supports only one compression method
+ (deflation) but other algorithms will be added later and will have the same
+ stream interface.
+
+ Compression can be done in a single step if the buffers are large
+ enough (for example if an input file is mmap'ed), or can be done by
+ repeated calls of the compression function. In the latter case, the
+ application must provide more input and/or consume the output
+ (providing more output space) before each call.
+
+ The compressed data format used by default by the in-memory functions is
+ the zlib format, which is a zlib wrapper documented in RFC 1950, wrapped
+ around a deflate stream, which is itself documented in RFC 1951.
+
+ The library also supports reading and writing files in gzip (.gz) format
+ with an interface similar to that of stdio using the functions that start
+ with "gz". The gzip format is different from the zlib format. gzip is a
+ gzip wrapper, documented in RFC 1952, wrapped around a deflate stream.
+
+ This library can optionally read and write gzip streams in memory as well.
+
+ The zlib format was designed to be compact and fast for use in memory
+ and on communications channels. The gzip format was designed for single-
+ file compression on file systems, has a larger header than zlib to maintain
+ directory information, and uses a different, slower check method than zlib.
+
+ The library does not install any signal handler. The decoder checks
+ the consistency of the compressed data, so the library should never
+ crash even in case of corrupted input.
+*/
+
+typedef voidpf (*alloc_func) OF((voidpf opaque, uInt items, uInt size));
+typedef void (*free_func) OF((voidpf opaque, voidpf address));
+
+struct internal_state;
+
+typedef struct z_stream_s {
+ Bytef *next_in; /* next input byte */
+ uInt avail_in; /* number of bytes available at next_in */
+ uLong total_in; /* total nb of input bytes read so far */
+
+ Bytef *next_out; /* next output byte should be put there */
+ uInt avail_out; /* remaining free space at next_out */
+ uLong total_out; /* total nb of bytes output so far */
+
+ char *msg; /* last error message, NULL if no error */
+ struct internal_state FAR *state; /* not visible by applications */
+
+ alloc_func zalloc; /* used to allocate the internal state */
+ free_func zfree; /* used to free the internal state */
+ voidpf opaque; /* private data object passed to zalloc and zfree */
+
+ int data_type; /* best guess about the data type: binary or text */
+ uLong adler; /* adler32 value of the uncompressed data */
+ uLong reserved; /* reserved for future use */
+} z_stream;
+
+typedef z_stream FAR *z_streamp;
+
+/*
+ gzip header information passed to and from zlib routines. See RFC 1952
+ for more details on the meanings of these fields.
+*/
+typedef struct gz_header_s {
+ int text; /* true if compressed data believed to be text */
+ uLong time; /* modification time */
+ int xflags; /* extra flags (not used when writing a gzip file) */
+ int os; /* operating system */
+ Bytef *extra; /* pointer to extra field or Z_NULL if none */
+ uInt extra_len; /* extra field length (valid if extra != Z_NULL) */
+ uInt extra_max; /* space at extra (only when reading header) */
+ Bytef *name; /* pointer to zero-terminated file name or Z_NULL */
+ uInt name_max; /* space at name (only when reading header) */
+ Bytef *comment; /* pointer to zero-terminated comment or Z_NULL */
+ uInt comm_max; /* space at comment (only when reading header) */
+ int hcrc; /* true if there was or will be a header crc */
+ int done; /* true when done reading gzip header (not used
+ when writing a gzip file) */
+} gz_header;
+
+typedef gz_header FAR *gz_headerp;
+
+/*
+ The application must update next_in and avail_in when avail_in has
+ dropped to zero. It must update next_out and avail_out when avail_out
+ has dropped to zero. The application must initialize zalloc, zfree and
+ opaque before calling the init function. All other fields are set by the
+ compression library and must not be updated by the application.
+
+ The opaque value provided by the application will be passed as the first
+ parameter for calls of zalloc and zfree. This can be useful for custom
+ memory management. The compression library attaches no meaning to the
+ opaque value.
+
+ zalloc must return Z_NULL if there is not enough memory for the object.
+ If zlib is used in a multi-threaded application, zalloc and zfree must be
+ thread safe.
+
+ On 16-bit systems, the functions zalloc and zfree must be able to allocate
+ exactly 65536 bytes, but will not be required to allocate more than this
+ if the symbol MAXSEG_64K is defined (see zconf.h). WARNING: On MSDOS,
+ pointers returned by zalloc for objects of exactly 65536 bytes *must*
+ have their offset normalized to zero. The default allocation function
+ provided by this library ensures this (see zutil.c). To reduce memory
+ requirements and avoid any allocation of 64K objects, at the expense of
+ compression ratio, compile the library with -DMAX_WBITS=14 (see zconf.h).
+
+ The fields total_in and total_out can be used for statistics or
+ progress reports. After compression, total_in holds the total size of
+ the uncompressed data and may be saved for use in the decompressor
+ (particularly if the decompressor wants to decompress everything in
+ a single step).
+*/
+
+ /* constants */
+
+#define Z_NO_FLUSH 0
+#define Z_PARTIAL_FLUSH 1 /* will be removed, use Z_SYNC_FLUSH instead */
+#define Z_SYNC_FLUSH 2
+#define Z_FULL_FLUSH 3
+#define Z_FINISH 4
+#define Z_BLOCK 5
+/* Allowed flush values; see deflate() and inflate() below for details */
+
+#define Z_OK 0
+#define Z_STREAM_END 1
+#define Z_NEED_DICT 2
+#define Z_ERRNO (-1)
+#define Z_STREAM_ERROR (-2)
+#define Z_DATA_ERROR (-3)
+#define Z_MEM_ERROR (-4)
+#define Z_BUF_ERROR (-5)
+#define Z_VERSION_ERROR (-6)
+/* Return codes for the compression/decompression functions. Negative
+ * values are errors, positive values are used for special but normal events.
+ */
+
+#define Z_NO_COMPRESSION 0
+#define Z_BEST_SPEED 1
+#define Z_BEST_COMPRESSION 9
+#define Z_DEFAULT_COMPRESSION (-1)
+/* compression levels */
+
+#define Z_FILTERED 1
+#define Z_HUFFMAN_ONLY 2
+#define Z_RLE 3
+#define Z_FIXED 4
+#define Z_DEFAULT_STRATEGY 0
+/* compression strategy; see deflateInit2() below for details */
+
+#define Z_BINARY 0
+#define Z_TEXT 1
+#define Z_ASCII Z_TEXT /* for compatibility with 1.2.2 and earlier */
+#define Z_UNKNOWN 2
+/* Possible values of the data_type field (though see inflate()) */
+
+#define Z_DEFLATED 8
+/* The deflate compression method (the only one supported in this version) */
+
+#define Z_NULL 0 /* for initializing zalloc, zfree, opaque */
+
+#define zlib_version zlibVersion()
+/* for compatibility with versions < 1.0.2 */
+
+ /* basic functions */
+
+ZEXTERN const char * ZEXPORT zlibVersion OF((void));
+/* The application can compare zlibVersion and ZLIB_VERSION for consistency.
+ If the first character differs, the library code actually used is
+ not compatible with the zlib.h header file used by the application.
+ This check is automatically made by deflateInit and inflateInit.
+ */
+
+/*
+ZEXTERN int ZEXPORT deflateInit OF((z_streamp strm, int level));
+
+ Initializes the internal stream state for compression. The fields
+ zalloc, zfree and opaque must be initialized before by the caller.
+ If zalloc and zfree are set to Z_NULL, deflateInit updates them to
+ use default allocation functions.
+
+ The compression level must be Z_DEFAULT_COMPRESSION, or between 0 and 9:
+ 1 gives best speed, 9 gives best compression, 0 gives no compression at
+ all (the input data is simply copied a block at a time).
+ Z_DEFAULT_COMPRESSION requests a default compromise between speed and
+ compression (currently equivalent to level 6).
+
+ deflateInit returns Z_OK if success, Z_MEM_ERROR if there was not
+ enough memory, Z_STREAM_ERROR if level is not a valid compression level,
+ Z_VERSION_ERROR if the zlib library version (zlib_version) is incompatible
+ with the version assumed by the caller (ZLIB_VERSION).
+ msg is set to null if there is no error message. deflateInit does not
+ perform any compression: this will be done by deflate().
+*/
+
+
+ZEXTERN int ZEXPORT deflate OF((z_streamp strm, int flush));
+/*
+ deflate compresses as much data as possible, and stops when the input
+ buffer becomes empty or the output buffer becomes full. It may introduce some
+ output latency (reading input without producing any output) except when
+ forced to flush.
+
+ The detailed semantics are as follows. deflate performs one or both of the
+ following actions:
+
+ - Compress more input starting at next_in and update next_in and avail_in
+ accordingly. If not all input can be processed (because there is not
+ enough room in the output buffer), next_in and avail_in are updated and
+ processing will resume at this point for the next call of deflate().
+
+ - Provide more output starting at next_out and update next_out and avail_out
+ accordingly. This action is forced if the parameter flush is non zero.
+ Forcing flush frequently degrades the compression ratio, so this parameter
+ should be set only when necessary (in interactive applications).
+ Some output may be provided even if flush is not set.
+
+ Before the call of deflate(), the application should ensure that at least
+ one of the actions is possible, by providing more input and/or consuming
+ more output, and updating avail_in or avail_out accordingly; avail_out
+ should never be zero before the call. The application can consume the
+ compressed output when it wants, for example when the output buffer is full
+ (avail_out == 0), or after each call of deflate(). If deflate returns Z_OK
+ and with zero avail_out, it must be called again after making room in the
+ output buffer because there might be more output pending.
+
+ Normally the parameter flush is set to Z_NO_FLUSH, which allows deflate to
+ decide how much data to accumualte before producing output, in order to
+ maximize compression.
+
+ If the parameter flush is set to Z_SYNC_FLUSH, all pending output is
+ flushed to the output buffer and the output is aligned on a byte boundary, so
+ that the decompressor can get all input data available so far. (In particular
+ avail_in is zero after the call if enough output space has been provided
+ before the call.) Flushing may degrade compression for some compression
+ algorithms and so it should be used only when necessary.
+
+ If flush is set to Z_FULL_FLUSH, all output is flushed as with
+ Z_SYNC_FLUSH, and the compression state is reset so that decompression can
+ restart from this point if previous compressed data has been damaged or if
+ random access is desired. Using Z_FULL_FLUSH too often can seriously degrade
+ compression.
+
+ If deflate returns with avail_out == 0, this function must be called again
+ with the same value of the flush parameter and more output space (updated
+ avail_out), until the flush is complete (deflate returns with non-zero
+ avail_out). In the case of a Z_FULL_FLUSH or Z_SYNC_FLUSH, make sure that
+ avail_out is greater than six to avoid repeated flush markers due to
+ avail_out == 0 on return.
+
+ If the parameter flush is set to Z_FINISH, pending input is processed,
+ pending output is flushed and deflate returns with Z_STREAM_END if there
+ was enough output space; if deflate returns with Z_OK, this function must be
+ called again with Z_FINISH and more output space (updated avail_out) but no
+ more input data, until it returns with Z_STREAM_END or an error. After
+ deflate has returned Z_STREAM_END, the only possible operations on the
+ stream are deflateReset or deflateEnd.
+
+ Z_FINISH can be used immediately after deflateInit if all the compression
+ is to be done in a single step. In this case, avail_out must be at least
+ the value returned by deflateBound (see below). If deflate does not return
+ Z_STREAM_END, then it must be called again as described above.
+
+ deflate() sets strm->adler to the adler32 checksum of all input read
+ so far (that is, total_in bytes).
+
+ deflate() may update strm->data_type if it can make a good guess about
+ the input data type (Z_BINARY or Z_TEXT). In doubt, the data is considered
+ binary. This field is only for information purposes and does not affect
+ the compression algorithm in any manner.
+
+ deflate() returns Z_OK if some progress has been made (more input
+ processed or more output produced), Z_STREAM_END if all input has been
+ consumed and all output has been produced (only when flush is set to
+ Z_FINISH), Z_STREAM_ERROR if the stream state was inconsistent (for example
+ if next_in or next_out was NULL), Z_BUF_ERROR if no progress is possible
+ (for example avail_in or avail_out was zero). Note that Z_BUF_ERROR is not
+ fatal, and deflate() can be called again with more input and more output
+ space to continue compressing.
+*/
+
+
+ZEXTERN int ZEXPORT deflateEnd OF((z_streamp strm));
+/*
+ All dynamically allocated data structures for this stream are freed.
+ This function discards any unprocessed input and does not flush any
+ pending output.
+
+ deflateEnd returns Z_OK if success, Z_STREAM_ERROR if the
+ stream state was inconsistent, Z_DATA_ERROR if the stream was freed
+ prematurely (some input or output was discarded). In the error case,
+ msg may be set but then points to a static string (which must not be
+ deallocated).
+*/
+
+
+/*
+ZEXTERN int ZEXPORT inflateInit OF((z_streamp strm));
+
+ Initializes the internal stream state for decompression. The fields
+ next_in, avail_in, zalloc, zfree and opaque must be initialized before by
+ the caller. If next_in is not Z_NULL and avail_in is large enough (the exact
+ value depends on the compression method), inflateInit determines the
+ compression method from the zlib header and allocates all data structures
+ accordingly; otherwise the allocation will be deferred to the first call of
+ inflate. If zalloc and zfree are set to Z_NULL, inflateInit updates them to
+ use default allocation functions.
+
+ inflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough
+ memory, Z_VERSION_ERROR if the zlib library version is incompatible with the
+ version assumed by the caller. msg is set to null if there is no error
+ message. inflateInit does not perform any decompression apart from reading
+ the zlib header if present: this will be done by inflate(). (So next_in and
+ avail_in may be modified, but next_out and avail_out are unchanged.)
+*/
+
+
+ZEXTERN int ZEXPORT inflate OF((z_streamp strm, int flush));
+/*
+ inflate decompresses as much data as possible, and stops when the input
+ buffer becomes empty or the output buffer becomes full. It may introduce
+ some output latency (reading input without producing any output) except when
+ forced to flush.
+
+ The detailed semantics are as follows. inflate performs one or both of the
+ following actions:
+
+ - Decompress more input starting at next_in and update next_in and avail_in
+ accordingly. If not all input can be processed (because there is not
+ enough room in the output buffer), next_in is updated and processing
+ will resume at this point for the next call of inflate().
+
+ - Provide more output starting at next_out and update next_out and avail_out
+ accordingly. inflate() provides as much output as possible, until there
+ is no more input data or no more space in the output buffer (see below
+ about the flush parameter).
+
+ Before the call of inflate(), the application should ensure that at least
+ one of the actions is possible, by providing more input and/or consuming
+ more output, and updating the next_* and avail_* values accordingly.
+ The application can consume the uncompressed output when it wants, for
+ example when the output buffer is full (avail_out == 0), or after each
+ call of inflate(). If inflate returns Z_OK and with zero avail_out, it
+ must be called again after making room in the output buffer because there
+ might be more output pending.
+
+ The flush parameter of inflate() can be Z_NO_FLUSH, Z_SYNC_FLUSH,
+ Z_FINISH, or Z_BLOCK. Z_SYNC_FLUSH requests that inflate() flush as much
+ output as possible to the output buffer. Z_BLOCK requests that inflate() stop
+ if and when it gets to the next deflate block boundary. When decoding the
+ zlib or gzip format, this will cause inflate() to return immediately after
+ the header and before the first block. When doing a raw inflate, inflate()
+ will go ahead and process the first block, and will return when it gets to
+ the end of that block, or when it runs out of data.
+
+ The Z_BLOCK option assists in appending to or combining deflate streams.
+ Also to assist in this, on return inflate() will set strm->data_type to the
+ number of unused bits in the last byte taken from strm->next_in, plus 64
+ if inflate() is currently decoding the last block in the deflate stream,
+ plus 128 if inflate() returned immediately after decoding an end-of-block
+ code or decoding the complete header up to just before the first byte of the
+ deflate stream. The end-of-block will not be indicated until all of the
+ uncompressed data from that block has been written to strm->next_out. The
+ number of unused bits may in general be greater than seven, except when
+ bit 7 of data_type is set, in which case the number of unused bits will be
+ less than eight.
+
+ inflate() should normally be called until it returns Z_STREAM_END or an
+ error. However if all decompression is to be performed in a single step
+ (a single call of inflate), the parameter flush should be set to
+ Z_FINISH. In this case all pending input is processed and all pending
+ output is flushed; avail_out must be large enough to hold all the
+ uncompressed data. (The size of the uncompressed data may have been saved
+ by the compressor for this purpose.) The next operation on this stream must
+ be inflateEnd to deallocate the decompression state. The use of Z_FINISH
+ is never required, but can be used to inform inflate that a faster approach
+ may be used for the single inflate() call.
+
+ In this implementation, inflate() always flushes as much output as
+ possible to the output buffer, and always uses the faster approach on the
+ first call. So the only effect of the flush parameter in this implementation
+ is on the return value of inflate(), as noted below, or when it returns early
+ because Z_BLOCK is used.
+
+ If a preset dictionary is needed after this call (see inflateSetDictionary
+ below), inflate sets strm->adler to the adler32 checksum of the dictionary
+ chosen by the compressor and returns Z_NEED_DICT; otherwise it sets
+ strm->adler to the adler32 checksum of all output produced so far (that is,
+ total_out bytes) and returns Z_OK, Z_STREAM_END or an error code as described
+ below. At the end of the stream, inflate() checks that its computed adler32
+ checksum is equal to that saved by the compressor and returns Z_STREAM_END
+ only if the checksum is correct.
+
+ inflate() will decompress and check either zlib-wrapped or gzip-wrapped
+ deflate data. The header type is detected automatically. Any information
+ contained in the gzip header is not retained, so applications that need that
+ information should instead use raw inflate, see inflateInit2() below, or
+ inflateBack() and perform their own processing of the gzip header and
+ trailer.
+
+ inflate() returns Z_OK if some progress has been made (more input processed
+ or more output produced), Z_STREAM_END if the end of the compressed data has
+ been reached and all uncompressed output has been produced, Z_NEED_DICT if a
+ preset dictionary is needed at this point, Z_DATA_ERROR if the input data was
+ corrupted (input stream not conforming to the zlib format or incorrect check
+ value), Z_STREAM_ERROR if the stream structure was inconsistent (for example
+ if next_in or next_out was NULL), Z_MEM_ERROR if there was not enough memory,
+ Z_BUF_ERROR if no progress is possible or if there was not enough room in the
+ output buffer when Z_FINISH is used. Note that Z_BUF_ERROR is not fatal, and
+ inflate() can be called again with more input and more output space to
+ continue decompressing. If Z_DATA_ERROR is returned, the application may then
+ call inflateSync() to look for a good compression block if a partial recovery
+ of the data is desired.
+*/
+
+
+ZEXTERN int ZEXPORT inflateEnd OF((z_streamp strm));
+/*
+ All dynamically allocated data structures for this stream are freed.
+ This function discards any unprocessed input and does not flush any
+ pending output.
+
+ inflateEnd returns Z_OK if success, Z_STREAM_ERROR if the stream state
+ was inconsistent. In the error case, msg may be set but then points to a
+ static string (which must not be deallocated).
+*/
+
+ /* Advanced functions */
+
+/*
+ The following functions are needed only in some special applications.
+*/
+
+/*
+ZEXTERN int ZEXPORT deflateInit2 OF((z_streamp strm,
+ int level,
+ int method,
+ int windowBits,
+ int memLevel,
+ int strategy));
+
+ This is another version of deflateInit with more compression options. The
+ fields next_in, zalloc, zfree and opaque must be initialized before by
+ the caller.
+
+ The method parameter is the compression method. It must be Z_DEFLATED in
+ this version of the library.
+
+ The windowBits parameter is the base two logarithm of the window size
+ (the size of the history buffer). It should be in the range 8..15 for this
+ version of the library. Larger values of this parameter result in better
+ compression at the expense of memory usage. The default value is 15 if
+ deflateInit is used instead.
+
+ windowBits can also be -8..-15 for raw deflate. In this case, -windowBits
+ determines the window size. deflate() will then generate raw deflate data
+ with no zlib header or trailer, and will not compute an adler32 check value.
+
+ windowBits can also be greater than 15 for optional gzip encoding. Add
+ 16 to windowBits to write a simple gzip header and trailer around the
+ compressed data instead of a zlib wrapper. The gzip header will have no
+ file name, no extra data, no comment, no modification time (set to zero),
+ no header crc, and the operating system will be set to 255 (unknown). If a
+ gzip stream is being written, strm->adler is a crc32 instead of an adler32.
+
+ The memLevel parameter specifies how much memory should be allocated
+ for the internal compression state. memLevel=1 uses minimum memory but
+ is slow and reduces compression ratio; memLevel=9 uses maximum memory
+ for optimal speed. The default value is 8. See zconf.h for total memory
+ usage as a function of windowBits and memLevel.
+
+ The strategy parameter is used to tune the compression algorithm. Use the
+ value Z_DEFAULT_STRATEGY for normal data, Z_FILTERED for data produced by a
+ filter (or predictor), Z_HUFFMAN_ONLY to force Huffman encoding only (no
+ string match), or Z_RLE to limit match distances to one (run-length
+ encoding). Filtered data consists mostly of small values with a somewhat
+ random distribution. In this case, the compression algorithm is tuned to
+ compress them better. The effect of Z_FILTERED is to force more Huffman
+ coding and less string matching; it is somewhat intermediate between
+ Z_DEFAULT and Z_HUFFMAN_ONLY. Z_RLE is designed to be almost as fast as
+ Z_HUFFMAN_ONLY, but give better compression for PNG image data. The strategy
+ parameter only affects the compression ratio but not the correctness of the
+ compressed output even if it is not set appropriately. Z_FIXED prevents the
+ use of dynamic Huffman codes, allowing for a simpler decoder for special
+ applications.
+
+ deflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough
+ memory, Z_STREAM_ERROR if a parameter is invalid (such as an invalid
+ method). msg is set to null if there is no error message. deflateInit2 does
+ not perform any compression: this will be done by deflate().
+*/
+
+ZEXTERN int ZEXPORT deflateSetDictionary OF((z_streamp strm,
+ const Bytef *dictionary,
+ uInt dictLength));
+/*
+ Initializes the compression dictionary from the given byte sequence
+ without producing any compressed output. This function must be called
+ immediately after deflateInit, deflateInit2 or deflateReset, before any
+ call of deflate. The compressor and decompressor must use exactly the same
+ dictionary (see inflateSetDictionary).
+
+ The dictionary should consist of strings (byte sequences) that are likely
+ to be encountered later in the data to be compressed, with the most commonly
+ used strings preferably put towards the end of the dictionary. Using a
+ dictionary is most useful when the data to be compressed is short and can be
+ predicted with good accuracy; the data can then be compressed better than
+ with the default empty dictionary.
+
+ Depending on the size of the compression data structures selected by
+ deflateInit or deflateInit2, a part of the dictionary may in effect be
+ discarded, for example if the dictionary is larger than the window size in
+ deflate or deflate2. Thus the strings most likely to be useful should be
+ put at the end of the dictionary, not at the front. In addition, the
+ current implementation of deflate will use at most the window size minus
+ 262 bytes of the provided dictionary.
+
+ Upon return of this function, strm->adler is set to the adler32 value
+ of the dictionary; the decompressor may later use this value to determine
+ which dictionary has been used by the compressor. (The adler32 value
+ applies to the whole dictionary even if only a subset of the dictionary is
+ actually used by the compressor.) If a raw deflate was requested, then the
+ adler32 value is not computed and strm->adler is not set.
+
+ deflateSetDictionary returns Z_OK if success, or Z_STREAM_ERROR if a
+ parameter is invalid (such as NULL dictionary) or the stream state is
+ inconsistent (for example if deflate has already been called for this stream
+ or if the compression method is bsort). deflateSetDictionary does not
+ perform any compression: this will be done by deflate().
+*/
+
+ZEXTERN int ZEXPORT deflateCopy OF((z_streamp dest,
+ z_streamp source));
+/*
+ Sets the destination stream as a complete copy of the source stream.
+
+ This function can be useful when several compression strategies will be
+ tried, for example when there are several ways of pre-processing the input
+ data with a filter. The streams that will be discarded should then be freed
+ by calling deflateEnd. Note that deflateCopy duplicates the internal
+ compression state which can be quite large, so this strategy is slow and
+ can consume lots of memory.
+
+ deflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not
+ enough memory, Z_STREAM_ERROR if the source stream state was inconsistent
+ (such as zalloc being NULL). msg is left unchanged in both source and
+ destination.
+*/
+
+ZEXTERN int ZEXPORT deflateReset OF((z_streamp strm));
+/*
+ This function is equivalent to deflateEnd followed by deflateInit,
+ but does not free and reallocate all the internal compression state.
+ The stream will keep the same compression level and any other attributes
+ that may have been set by deflateInit2.
+
+ deflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source
+ stream state was inconsistent (such as zalloc or state being NULL).
+*/
+
+ZEXTERN int ZEXPORT deflateParams OF((z_streamp strm,
+ int level,
+ int strategy));
+/*
+ Dynamically update the compression level and compression strategy. The
+ interpretation of level and strategy is as in deflateInit2. This can be
+ used to switch between compression and straight copy of the input data, or
+ to switch to a different kind of input data requiring a different
+ strategy. If the compression level is changed, the input available so far
+ is compressed with the old level (and may be flushed); the new level will
+ take effect only at the next call of deflate().
+
+ Before the call of deflateParams, the stream state must be set as for
+ a call of deflate(), since the currently available input may have to
+ be compressed and flushed. In particular, strm->avail_out must be non-zero.
+
+ deflateParams returns Z_OK if success, Z_STREAM_ERROR if the source
+ stream state was inconsistent or if a parameter was invalid, Z_BUF_ERROR
+ if strm->avail_out was zero.
+*/
+
+ZEXTERN int ZEXPORT deflateTune OF((z_streamp strm,
+ int good_length,
+ int max_lazy,
+ int nice_length,
+ int max_chain));
+/*
+ Fine tune deflate's internal compression parameters. This should only be
+ used by someone who understands the algorithm used by zlib's deflate for
+ searching for the best matching string, and even then only by the most
+ fanatic optimizer trying to squeeze out the last compressed bit for their
+ specific input data. Read the deflate.c source code for the meaning of the
+ max_lazy, good_length, nice_length, and max_chain parameters.
+
+ deflateTune() can be called after deflateInit() or deflateInit2(), and
+ returns Z_OK on success, or Z_STREAM_ERROR for an invalid deflate stream.
+ */
+
+ZEXTERN uLong ZEXPORT deflateBound OF((z_streamp strm,
+ uLong sourceLen));
+/*
+ deflateBound() returns an upper bound on the compressed size after
+ deflation of sourceLen bytes. It must be called after deflateInit()
+ or deflateInit2(). This would be used to allocate an output buffer
+ for deflation in a single pass, and so would be called before deflate().
+*/
+
+ZEXTERN int ZEXPORT deflatePrime OF((z_streamp strm,
+ int bits,
+ int value));
+/*
+ deflatePrime() inserts bits in the deflate output stream. The intent
+ is that this function is used to start off the deflate output with the
+ bits leftover from a previous deflate stream when appending to it. As such,
+ this function can only be used for raw deflate, and must be used before the
+ first deflate() call after a deflateInit2() or deflateReset(). bits must be
+ less than or equal to 16, and that many of the least significant bits of
+ value will be inserted in the output.
+
+ deflatePrime returns Z_OK if success, or Z_STREAM_ERROR if the source
+ stream state was inconsistent.
+*/
+
+ZEXTERN int ZEXPORT deflateSetHeader OF((z_streamp strm,
+ gz_headerp head));
+/*
+ deflateSetHeader() provides gzip header information for when a gzip
+ stream is requested by deflateInit2(). deflateSetHeader() may be called
+ after deflateInit2() or deflateReset() and before the first call of
+ deflate(). The text, time, os, extra field, name, and comment information
+ in the provided gz_header structure are written to the gzip header (xflag is
+ ignored -- the extra flags are set according to the compression level). The
+ caller must assure that, if not Z_NULL, name and comment are terminated with
+ a zero byte, and that if extra is not Z_NULL, that extra_len bytes are
+ available there. If hcrc is true, a gzip header crc is included. Note that
+ the current versions of the command-line version of gzip (up through version
+ 1.3.x) do not support header crc's, and will report that it is a "multi-part
+ gzip file" and give up.
+
+ If deflateSetHeader is not used, the default gzip header has text false,
+ the time set to zero, and os set to 255, with no extra, name, or comment
+ fields. The gzip header is returned to the default state by deflateReset().
+
+ deflateSetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source
+ stream state was inconsistent.
+*/
+
+/*
+ZEXTERN int ZEXPORT inflateInit2 OF((z_streamp strm,
+ int windowBits));
+
+ This is another version of inflateInit with an extra parameter. The
+ fields next_in, avail_in, zalloc, zfree and opaque must be initialized
+ before by the caller.
+
+ The windowBits parameter is the base two logarithm of the maximum window
+ size (the size of the history buffer). It should be in the range 8..15 for
+ this version of the library. The default value is 15 if inflateInit is used
+ instead. windowBits must be greater than or equal to the windowBits value
+ provided to deflateInit2() while compressing, or it must be equal to 15 if
+ deflateInit2() was not used. If a compressed stream with a larger window
+ size is given as input, inflate() will return with the error code
+ Z_DATA_ERROR instead of trying to allocate a larger window.
+
+ windowBits can also be -8..-15 for raw inflate. In this case, -windowBits
+ determines the window size. inflate() will then process raw deflate data,
+ not looking for a zlib or gzip header, not generating a check value, and not
+ looking for any check values for comparison at the end of the stream. This
+ is for use with other formats that use the deflate compressed data format
+ such as zip. Those formats provide their own check values. If a custom
+ format is developed using the raw deflate format for compressed data, it is
+ recommended that a check value such as an adler32 or a crc32 be applied to
+ the uncompressed data as is done in the zlib, gzip, and zip formats. For
+ most applications, the zlib format should be used as is. Note that comments
+ above on the use in deflateInit2() applies to the magnitude of windowBits.
+
+ windowBits can also be greater than 15 for optional gzip decoding. Add
+ 32 to windowBits to enable zlib and gzip decoding with automatic header
+ detection, or add 16 to decode only the gzip format (the zlib format will
+ return a Z_DATA_ERROR). If a gzip stream is being decoded, strm->adler is
+ a crc32 instead of an adler32.
+
+ inflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough
+ memory, Z_STREAM_ERROR if a parameter is invalid (such as a null strm). msg
+ is set to null if there is no error message. inflateInit2 does not perform
+ any decompression apart from reading the zlib header if present: this will
+ be done by inflate(). (So next_in and avail_in may be modified, but next_out
+ and avail_out are unchanged.)
+*/
+
+ZEXTERN int ZEXPORT inflateSetDictionary OF((z_streamp strm,
+ const Bytef *dictionary,
+ uInt dictLength));
+/*
+ Initializes the decompression dictionary from the given uncompressed byte
+ sequence. This function must be called immediately after a call of inflate,
+ if that call returned Z_NEED_DICT. The dictionary chosen by the compressor
+ can be determined from the adler32 value returned by that call of inflate.
+ The compressor and decompressor must use exactly the same dictionary (see
+ deflateSetDictionary). For raw inflate, this function can be called
+ immediately after inflateInit2() or inflateReset() and before any call of
+ inflate() to set the dictionary. The application must insure that the
+ dictionary that was used for compression is provided.
+
+ inflateSetDictionary returns Z_OK if success, Z_STREAM_ERROR if a
+ parameter is invalid (such as NULL dictionary) or the stream state is
+ inconsistent, Z_DATA_ERROR if the given dictionary doesn't match the
+ expected one (incorrect adler32 value). inflateSetDictionary does not
+ perform any decompression: this will be done by subsequent calls of
+ inflate().
+*/
+
+ZEXTERN int ZEXPORT inflateSync OF((z_streamp strm));
+/*
+ Skips invalid compressed data until a full flush point (see above the
+ description of deflate with Z_FULL_FLUSH) can be found, or until all
+ available input is skipped. No output is provided.
+
+ inflateSync returns Z_OK if a full flush point has been found, Z_BUF_ERROR
+ if no more input was provided, Z_DATA_ERROR if no flush point has been found,
+ or Z_STREAM_ERROR if the stream structure was inconsistent. In the success
+ case, the application may save the current current value of total_in which
+ indicates where valid compressed data was found. In the error case, the
+ application may repeatedly call inflateSync, providing more input each time,
+ until success or end of the input data.
+*/
+
+ZEXTERN int ZEXPORT inflateCopy OF((z_streamp dest,
+ z_streamp source));
+/*
+ Sets the destination stream as a complete copy of the source stream.
+
+ This function can be useful when randomly accessing a large stream. The
+ first pass through the stream can periodically record the inflate state,
+ allowing restarting inflate at those points when randomly accessing the
+ stream.
+
+ inflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not
+ enough memory, Z_STREAM_ERROR if the source stream state was inconsistent
+ (such as zalloc being NULL). msg is left unchanged in both source and
+ destination.
+*/
+
+ZEXTERN int ZEXPORT inflateReset OF((z_streamp strm));
+/*
+ This function is equivalent to inflateEnd followed by inflateInit,
+ but does not free and reallocate all the internal decompression state.
+ The stream will keep attributes that may have been set by inflateInit2.
+
+ inflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source
+ stream state was inconsistent (such as zalloc or state being NULL).
+*/
+
+ZEXTERN int ZEXPORT inflatePrime OF((z_streamp strm,
+ int bits,
+ int value));
+/*
+ This function inserts bits in the inflate input stream. The intent is
+ that this function is used to start inflating at a bit position in the
+ middle of a byte. The provided bits will be used before any bytes are used
+ from next_in. This function should only be used with raw inflate, and
+ should be used before the first inflate() call after inflateInit2() or
+ inflateReset(). bits must be less than or equal to 16, and that many of the
+ least significant bits of value will be inserted in the input.
+
+ inflatePrime returns Z_OK if success, or Z_STREAM_ERROR if the source
+ stream state was inconsistent.
+*/
+
+ZEXTERN int ZEXPORT inflateGetHeader OF((z_streamp strm,
+ gz_headerp head));
+/*
+ inflateGetHeader() requests that gzip header information be stored in the
+ provided gz_header structure. inflateGetHeader() may be called after
+ inflateInit2() or inflateReset(), and before the first call of inflate().
+ As inflate() processes the gzip stream, head->done is zero until the header
+ is completed, at which time head->done is set to one. If a zlib stream is
+ being decoded, then head->done is set to -1 to indicate that there will be
+ no gzip header information forthcoming. Note that Z_BLOCK can be used to
+ force inflate() to return immediately after header processing is complete
+ and before any actual data is decompressed.
+
+ The text, time, xflags, and os fields are filled in with the gzip header
+ contents. hcrc is set to true if there is a header CRC. (The header CRC
+ was valid if done is set to one.) If extra is not Z_NULL, then extra_max
+ contains the maximum number of bytes to write to extra. Once done is true,
+ extra_len contains the actual extra field length, and extra contains the
+ extra field, or that field truncated if extra_max is less than extra_len.
+ If name is not Z_NULL, then up to name_max characters are written there,
+ terminated with a zero unless the length is greater than name_max. If
+ comment is not Z_NULL, then up to comm_max characters are written there,
+ terminated with a zero unless the length is greater than comm_max. When
+ any of extra, name, or comment are not Z_NULL and the respective field is
+ not present in the header, then that field is set to Z_NULL to signal its
+ absence. This allows the use of deflateSetHeader() with the returned
+ structure to duplicate the header. However if those fields are set to
+ allocated memory, then the application will need to save those pointers
+ elsewhere so that they can be eventually freed.
+
+ If inflateGetHeader is not used, then the header information is simply
+ discarded. The header is always checked for validity, including the header
+ CRC if present. inflateReset() will reset the process to discard the header
+ information. The application would need to call inflateGetHeader() again to
+ retrieve the header from the next gzip stream.
+
+ inflateGetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source
+ stream state was inconsistent.
+*/
+
+/*
+ZEXTERN int ZEXPORT inflateBackInit OF((z_streamp strm, int windowBits,
+ unsigned char FAR *window));
+
+ Initialize the internal stream state for decompression using inflateBack()
+ calls. The fields zalloc, zfree and opaque in strm must be initialized
+ before the call. If zalloc and zfree are Z_NULL, then the default library-
+ derived memory allocation routines are used. windowBits is the base two
+ logarithm of the window size, in the range 8..15. window is a caller
+ supplied buffer of that size. Except for special applications where it is
+ assured that deflate was used with small window sizes, windowBits must be 15
+ and a 32K byte window must be supplied to be able to decompress general
+ deflate streams.
+
+ See inflateBack() for the usage of these routines.
+
+ inflateBackInit will return Z_OK on success, Z_STREAM_ERROR if any of
+ the paramaters are invalid, Z_MEM_ERROR if the internal state could not
+ be allocated, or Z_VERSION_ERROR if the version of the library does not
+ match the version of the header file.
+*/
+
+typedef unsigned (*in_func) OF((void FAR *, unsigned char FAR * FAR *));
+typedef int (*out_func) OF((void FAR *, unsigned char FAR *, unsigned));
+
+ZEXTERN int ZEXPORT inflateBack OF((z_streamp strm,
+ in_func in, void FAR *in_desc,
+ out_func out, void FAR *out_desc));
+/*
+ inflateBack() does a raw inflate with a single call using a call-back
+ interface for input and output. This is more efficient than inflate() for
+ file i/o applications in that it avoids copying between the output and the
+ sliding window by simply making the window itself the output buffer. This
+ function trusts the application to not change the output buffer passed by
+ the output function, at least until inflateBack() returns.
+
+ inflateBackInit() must be called first to allocate the internal state
+ and to initialize the state with the user-provided window buffer.
+ inflateBack() may then be used multiple times to inflate a complete, raw
+ deflate stream with each call. inflateBackEnd() is then called to free
+ the allocated state.
+
+ A raw deflate stream is one with no zlib or gzip header or trailer.
+ This routine would normally be used in a utility that reads zip or gzip
+ files and writes out uncompressed files. The utility would decode the
+ header and process the trailer on its own, hence this routine expects
+ only the raw deflate stream to decompress. This is different from the
+ normal behavior of inflate(), which expects either a zlib or gzip header and
+ trailer around the deflate stream.
+
+ inflateBack() uses two subroutines supplied by the caller that are then
+ called by inflateBack() for input and output. inflateBack() calls those
+ routines until it reads a complete deflate stream and writes out all of the
+ uncompressed data, or until it encounters an error. The function's
+ parameters and return types are defined above in the in_func and out_func
+ typedefs. inflateBack() will call in(in_desc, &buf) which should return the
+ number of bytes of provided input, and a pointer to that input in buf. If
+ there is no input available, in() must return zero--buf is ignored in that
+ case--and inflateBack() will return a buffer error. inflateBack() will call
+ out(out_desc, buf, len) to write the uncompressed data buf[0..len-1]. out()
+ should return zero on success, or non-zero on failure. If out() returns
+ non-zero, inflateBack() will return with an error. Neither in() nor out()
+ are permitted to change the contents of the window provided to
+ inflateBackInit(), which is also the buffer that out() uses to write from.
+ The length written by out() will be at most the window size. Any non-zero
+ amount of input may be provided by in().
+
+ For convenience, inflateBack() can be provided input on the first call by
+ setting strm->next_in and strm->avail_in. If that input is exhausted, then
+ in() will be called. Therefore strm->next_in must be initialized before
+ calling inflateBack(). If strm->next_in is Z_NULL, then in() will be called
+ immediately for input. If strm->next_in is not Z_NULL, then strm->avail_in
+ must also be initialized, and then if strm->avail_in is not zero, input will
+ initially be taken from strm->next_in[0 .. strm->avail_in - 1].
+
+ The in_desc and out_desc parameters of inflateBack() is passed as the
+ first parameter of in() and out() respectively when they are called. These
+ descriptors can be optionally used to pass any information that the caller-
+ supplied in() and out() functions need to do their job.
+
+ On return, inflateBack() will set strm->next_in and strm->avail_in to
+ pass back any unused input that was provided by the last in() call. The
+ return values of inflateBack() can be Z_STREAM_END on success, Z_BUF_ERROR
+ if in() or out() returned an error, Z_DATA_ERROR if there was a format
+ error in the deflate stream (in which case strm->msg is set to indicate the
+ nature of the error), or Z_STREAM_ERROR if the stream was not properly
+ initialized. In the case of Z_BUF_ERROR, an input or output error can be
+ distinguished using strm->next_in which will be Z_NULL only if in() returned
+ an error. If strm->next is not Z_NULL, then the Z_BUF_ERROR was due to
+ out() returning non-zero. (in() will always be called before out(), so
+ strm->next_in is assured to be defined if out() returns non-zero.) Note
+ that inflateBack() cannot return Z_OK.
+*/
+
+ZEXTERN int ZEXPORT inflateBackEnd OF((z_streamp strm));
+/*
+ All memory allocated by inflateBackInit() is freed.
+
+ inflateBackEnd() returns Z_OK on success, or Z_STREAM_ERROR if the stream
+ state was inconsistent.
+*/
+
+ZEXTERN uLong ZEXPORT zlibCompileFlags OF((void));
+/* Return flags indicating compile-time options.
+
+ Type sizes, two bits each, 00 = 16 bits, 01 = 32, 10 = 64, 11 = other:
+ 1.0: size of uInt
+ 3.2: size of uLong
+ 5.4: size of voidpf (pointer)
+ 7.6: size of z_off_t
+
+ Compiler, assembler, and debug options:
+ 8: DEBUG
+ 9: ASMV or ASMINF -- use ASM code
+ 10: ZLIB_WINAPI -- exported functions use the WINAPI calling convention
+ 11: 0 (reserved)
+
+ One-time table building (smaller code, but not thread-safe if true):
+ 12: BUILDFIXED -- build static block decoding tables when needed
+ 13: DYNAMIC_CRC_TABLE -- build CRC calculation tables when needed
+ 14,15: 0 (reserved)
+
+ Library content (indicates missing functionality):
+ 16: NO_GZCOMPRESS -- gz* functions cannot compress (to avoid linking
+ deflate code when not needed)
+ 17: NO_GZIP -- deflate can't write gzip streams, and inflate can't detect
+ and decode gzip streams (to avoid linking crc code)
+ 18-19: 0 (reserved)
+
+ Operation variations (changes in library functionality):
+ 20: PKZIP_BUG_WORKAROUND -- slightly more permissive inflate
+ 21: FASTEST -- deflate algorithm with only one, lowest compression level
+ 22,23: 0 (reserved)
+
+ The sprintf variant used by gzprintf (zero is best):
+ 24: 0 = vs*, 1 = s* -- 1 means limited to 20 arguments after the format
+ 25: 0 = *nprintf, 1 = *printf -- 1 means gzprintf() not secure!
+ 26: 0 = returns value, 1 = void -- 1 means inferred string length returned
+
+ Remainder:
+ 27-31: 0 (reserved)
+ */
+
+
+ /* utility functions */
+
+/*
+ The following utility functions are implemented on top of the
+ basic stream-oriented functions. To simplify the interface, some
+ default options are assumed (compression level and memory usage,
+ standard memory allocation functions). The source code of these
+ utility functions can easily be modified if you need special options.
+*/
+
+ZEXTERN int ZEXPORT compress OF((Bytef *dest, uLongf *destLen,
+ const Bytef *source, uLong sourceLen));
+/*
+ Compresses the source buffer into the destination buffer. sourceLen is
+ the byte length of the source buffer. Upon entry, destLen is the total
+ size of the destination buffer, which must be at least the value returned
+ by compressBound(sourceLen). Upon exit, destLen is the actual size of the
+ compressed buffer.
+ This function can be used to compress a whole file at once if the
+ input file is mmap'ed.
+ compress returns Z_OK if success, Z_MEM_ERROR if there was not
+ enough memory, Z_BUF_ERROR if there was not enough room in the output
+ buffer.
+*/
+
+ZEXTERN int ZEXPORT compress2 OF((Bytef *dest, uLongf *destLen,
+ const Bytef *source, uLong sourceLen,
+ int level));
+/*
+ Compresses the source buffer into the destination buffer. The level
+ parameter has the same meaning as in deflateInit. sourceLen is the byte
+ length of the source buffer. Upon entry, destLen is the total size of the
+ destination buffer, which must be at least the value returned by
+ compressBound(sourceLen). Upon exit, destLen is the actual size of the
+ compressed buffer.
+
+ compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough
+ memory, Z_BUF_ERROR if there was not enough room in the output buffer,
+ Z_STREAM_ERROR if the level parameter is invalid.
+*/
+
+ZEXTERN uLong ZEXPORT compressBound OF((uLong sourceLen));
+/*
+ compressBound() returns an upper bound on the compressed size after
+ compress() or compress2() on sourceLen bytes. It would be used before
+ a compress() or compress2() call to allocate the destination buffer.
+*/
+
+ZEXTERN int ZEXPORT uncompress OF((Bytef *dest, uLongf *destLen,
+ const Bytef *source, uLong sourceLen));
+/*
+ Decompresses the source buffer into the destination buffer. sourceLen is
+ the byte length of the source buffer. Upon entry, destLen is the total
+ size of the destination buffer, which must be large enough to hold the
+ entire uncompressed data. (The size of the uncompressed data must have
+ been saved previously by the compressor and transmitted to the decompressor
+ by some mechanism outside the scope of this compression library.)
+ Upon exit, destLen is the actual size of the compressed buffer.
+ This function can be used to decompress a whole file at once if the
+ input file is mmap'ed.
+
+ uncompress returns Z_OK if success, Z_MEM_ERROR if there was not
+ enough memory, Z_BUF_ERROR if there was not enough room in the output
+ buffer, or Z_DATA_ERROR if the input data was corrupted or incomplete.
+*/
+
+
+typedef voidp gzFile;
+
+ZEXTERN gzFile ZEXPORT gzopen OF((const char *path, const char *mode));
+/*
+ Opens a gzip (.gz) file for reading or writing. The mode parameter
+ is as in fopen ("rb" or "wb") but can also include a compression level
+ ("wb9") or a strategy: 'f' for filtered data as in "wb6f", 'h' for
+ Huffman only compression as in "wb1h", or 'R' for run-length encoding
+ as in "wb1R". (See the description of deflateInit2 for more information
+ about the strategy parameter.)
+
+ gzopen can be used to read a file which is not in gzip format; in this
+ case gzread will directly read from the file without decompression.
+
+ gzopen returns NULL if the file could not be opened or if there was
+ insufficient memory to allocate the (de)compression state; errno
+ can be checked to distinguish the two cases (if errno is zero, the
+ zlib error is Z_MEM_ERROR). */
+
+ZEXTERN gzFile ZEXPORT gzdopen OF((int fd, const char *mode));
+/*
+ gzdopen() associates a gzFile with the file descriptor fd. File
+ descriptors are obtained from calls like open, dup, creat, pipe or
+ fileno (in the file has been previously opened with fopen).
+ The mode parameter is as in gzopen.
+ The next call of gzclose on the returned gzFile will also close the
+ file descriptor fd, just like fclose(fdopen(fd), mode) closes the file
+ descriptor fd. If you want to keep fd open, use gzdopen(dup(fd), mode).
+ gzdopen returns NULL if there was insufficient memory to allocate
+ the (de)compression state.
+*/
+
+ZEXTERN int ZEXPORT gzsetparams OF((gzFile file, int level, int strategy));
+/*
+ Dynamically update the compression level or strategy. See the description
+ of deflateInit2 for the meaning of these parameters.
+ gzsetparams returns Z_OK if success, or Z_STREAM_ERROR if the file was not
+ opened for writing.
+*/
+
+ZEXTERN int ZEXPORT gzread OF((gzFile file, voidp buf, unsigned len));
+/*
+ Reads the given number of uncompressed bytes from the compressed file.
+ If the input file was not in gzip format, gzread copies the given number
+ of bytes into the buffer.
+ gzread returns the number of uncompressed bytes actually read (0 for
+ end of file, -1 for error). */
+
+ZEXTERN int ZEXPORT gzwrite OF((gzFile file,
+ voidpc buf, unsigned len));
+/*
+ Writes the given number of uncompressed bytes into the compressed file.
+ gzwrite returns the number of uncompressed bytes actually written
+ (0 in case of error).
+*/
+
+ZEXTERN int ZEXPORTVA gzprintf OF((gzFile file, const char *format, ...));
+/*
+ Converts, formats, and writes the args to the compressed file under
+ control of the format string, as in fprintf. gzprintf returns the number of
+ uncompressed bytes actually written (0 in case of error). The number of
+ uncompressed bytes written is limited to 4095. The caller should assure that
+ this limit is not exceeded. If it is exceeded, then gzprintf() will return
+ return an error (0) with nothing written. In this case, there may also be a
+ buffer overflow with unpredictable consequences, which is possible only if
+ zlib was compiled with the insecure functions sprintf() or vsprintf()
+ because the secure snprintf() or vsnprintf() functions were not available.
+*/
+
+ZEXTERN int ZEXPORT gzputs OF((gzFile file, const char *s));
+/*
+ Writes the given null-terminated string to the compressed file, excluding
+ the terminating null character.
+ gzputs returns the number of characters written, or -1 in case of error.
+*/
+
+ZEXTERN char * ZEXPORT gzgets OF((gzFile file, char *buf, int len));
+/*
+ Reads bytes from the compressed file until len-1 characters are read, or
+ a newline character is read and transferred to buf, or an end-of-file
+ condition is encountered. The string is then terminated with a null
+ character.
+ gzgets returns buf, or Z_NULL in case of error.
+*/
+
+ZEXTERN int ZEXPORT gzputc OF((gzFile file, int c));
+/*
+ Writes c, converted to an unsigned char, into the compressed file.
+ gzputc returns the value that was written, or -1 in case of error.
+*/
+
+ZEXTERN int ZEXPORT gzgetc OF((gzFile file));
+/*
+ Reads one byte from the compressed file. gzgetc returns this byte
+ or -1 in case of end of file or error.
+*/
+
+ZEXTERN int ZEXPORT gzungetc OF((int c, gzFile file));
+/*
+ Push one character back onto the stream to be read again later.
+ Only one character of push-back is allowed. gzungetc() returns the
+ character pushed, or -1 on failure. gzungetc() will fail if a
+ character has been pushed but not read yet, or if c is -1. The pushed
+ character will be discarded if the stream is repositioned with gzseek()
+ or gzrewind().
+*/
+
+ZEXTERN int ZEXPORT gzflush OF((gzFile file, int flush));
+/*
+ Flushes all pending output into the compressed file. The parameter
+ flush is as in the deflate() function. The return value is the zlib
+ error number (see function gzerror below). gzflush returns Z_OK if
+ the flush parameter is Z_FINISH and all output could be flushed.
+ gzflush should be called only when strictly necessary because it can
+ degrade compression.
+*/
+
+ZEXTERN z_off_t ZEXPORT gzseek OF((gzFile file,
+ z_off_t offset, int whence));
+/*
+ Sets the starting position for the next gzread or gzwrite on the
+ given compressed file. The offset represents a number of bytes in the
+ uncompressed data stream. The whence parameter is defined as in lseek(2);
+ the value SEEK_END is not supported.
+ If the file is opened for reading, this function is emulated but can be
+ extremely slow. If the file is opened for writing, only forward seeks are
+ supported; gzseek then compresses a sequence of zeroes up to the new
+ starting position.
+
+ gzseek returns the resulting offset location as measured in bytes from
+ the beginning of the uncompressed stream, or -1 in case of error, in
+ particular if the file is opened for writing and the new starting position
+ would be before the current position.
+*/
+
+ZEXTERN int ZEXPORT gzrewind OF((gzFile file));
+/*
+ Rewinds the given file. This function is supported only for reading.
+
+ gzrewind(file) is equivalent to (int)gzseek(file, 0L, SEEK_SET)
+*/
+
+ZEXTERN z_off_t ZEXPORT gztell OF((gzFile file));
+/*
+ Returns the starting position for the next gzread or gzwrite on the
+ given compressed file. This position represents a number of bytes in the
+ uncompressed data stream.
+
+ gztell(file) is equivalent to gzseek(file, 0L, SEEK_CUR)
+*/
+
+ZEXTERN int ZEXPORT gzeof OF((gzFile file));
+/*
+ Returns 1 when EOF has previously been detected reading the given
+ input stream, otherwise zero.
+*/
+
+ZEXTERN int ZEXPORT gzdirect OF((gzFile file));
+/*
+ Returns 1 if file is being read directly without decompression, otherwise
+ zero.
+*/
+
+ZEXTERN int ZEXPORT gzclose OF((gzFile file));
+/*
+ Flushes all pending output if necessary, closes the compressed file
+ and deallocates all the (de)compression state. The return value is the zlib
+ error number (see function gzerror below).
+*/
+
+ZEXTERN const char * ZEXPORT gzerror OF((gzFile file, int *errnum));
+/*
+ Returns the error message for the last error which occurred on the
+ given compressed file. errnum is set to zlib error number. If an
+ error occurred in the file system and not in the compression library,
+ errnum is set to Z_ERRNO and the application may consult errno
+ to get the exact error code.
+*/
+
+ZEXTERN void ZEXPORT gzclearerr OF((gzFile file));
+/*
+ Clears the error and end-of-file flags for file. This is analogous to the
+ clearerr() function in stdio. This is useful for continuing to read a gzip
+ file that is being written concurrently.
+*/
+
+ /* checksum functions */
+
+/*
+ These functions are not related to compression but are exported
+ anyway because they might be useful in applications using the
+ compression library.
+*/
+
+ZEXTERN uLong ZEXPORT adler32 OF((uLong adler, const Bytef *buf, uInt len));
+/*
+ Update a running Adler-32 checksum with the bytes buf[0..len-1] and
+ return the updated checksum. If buf is NULL, this function returns
+ the required initial value for the checksum.
+ An Adler-32 checksum is almost as reliable as a CRC32 but can be computed
+ much faster. Usage example:
+
+ uLong adler = adler32(0L, Z_NULL, 0);
+
+ while (read_buffer(buffer, length) != EOF) {
+ adler = adler32(adler, buffer, length);
+ }
+ if (adler != original_adler) error();
+*/
+
+ZEXTERN uLong ZEXPORT adler32_combine OF((uLong adler1, uLong adler2,
+ z_off_t len2));
+/*
+ Combine two Adler-32 checksums into one. For two sequences of bytes, seq1
+ and seq2 with lengths len1 and len2, Adler-32 checksums were calculated for
+ each, adler1 and adler2. adler32_combine() returns the Adler-32 checksum of
+ seq1 and seq2 concatenated, requiring only adler1, adler2, and len2.
+*/
+
+ZEXTERN uLong ZEXPORT crc32 OF((uLong crc, const Bytef *buf, uInt len));
+/*
+ Update a running CRC-32 with the bytes buf[0..len-1] and return the
+ updated CRC-32. If buf is NULL, this function returns the required initial
+ value for the for the crc. Pre- and post-conditioning (one's complement) is
+ performed within this function so it shouldn't be done by the application.
+ Usage example:
+
+ uLong crc = crc32(0L, Z_NULL, 0);
+
+ while (read_buffer(buffer, length) != EOF) {
+ crc = crc32(crc, buffer, length);
+ }
+ if (crc != original_crc) error();
+*/
+
+ZEXTERN uLong ZEXPORT crc32_combine OF((uLong crc1, uLong crc2, z_off_t len2));
+
+/*
+ Combine two CRC-32 check values into one. For two sequences of bytes,
+ seq1 and seq2 with lengths len1 and len2, CRC-32 check values were
+ calculated for each, crc1 and crc2. crc32_combine() returns the CRC-32
+ check value of seq1 and seq2 concatenated, requiring only crc1, crc2, and
+ len2.
+*/
+
+
+ /* various hacks, don't look :) */
+
+/* deflateInit and inflateInit are macros to allow checking the zlib version
+ * and the compiler's view of z_stream:
+ */
+ZEXTERN int ZEXPORT deflateInit_ OF((z_streamp strm, int level,
+ const char *version, int stream_size));
+ZEXTERN int ZEXPORT inflateInit_ OF((z_streamp strm,
+ const char *version, int stream_size));
+ZEXTERN int ZEXPORT deflateInit2_ OF((z_streamp strm, int level, int method,
+ int windowBits, int memLevel,
+ int strategy, const char *version,
+ int stream_size));
+ZEXTERN int ZEXPORT inflateInit2_ OF((z_streamp strm, int windowBits,
+ const char *version, int stream_size));
+ZEXTERN int ZEXPORT inflateBackInit_ OF((z_streamp strm, int windowBits,
+ unsigned char FAR *window,
+ const char *version,
+ int stream_size));
+#define deflateInit(strm, level) \
+ deflateInit_((strm), (level), ZLIB_VERSION, sizeof(z_stream))
+#define inflateInit(strm) \
+ inflateInit_((strm), ZLIB_VERSION, sizeof(z_stream))
+#define deflateInit2(strm, level, method, windowBits, memLevel, strategy) \
+ deflateInit2_((strm),(level),(method),(windowBits),(memLevel),\
+ (strategy), ZLIB_VERSION, sizeof(z_stream))
+#define inflateInit2(strm, windowBits) \
+ inflateInit2_((strm), (windowBits), ZLIB_VERSION, sizeof(z_stream))
+#define inflateBackInit(strm, windowBits, window) \
+ inflateBackInit_((strm), (windowBits), (window), \
+ ZLIB_VERSION, sizeof(z_stream))
+
+
+#if !defined(ZUTIL_H) && !defined(NO_DUMMY_DECL)
+ struct internal_state {int dummy;}; /* hack for buggy compilers */
+#endif
+
+ZEXTERN const char * ZEXPORT zError OF((int));
+ZEXTERN int ZEXPORT inflateSyncPoint OF((z_streamp z));
+ZEXTERN const uLongf * ZEXPORT get_crc_table OF((void));
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* ZLIB_H */