]> Git trees. - libqmvoc.git/commitdiff
Added first version of the new document classes.
authorEric Pignet <eric@erixpage.com>
Fri, 1 Jul 2005 19:33:52 +0000 (19:33 +0000)
committerEric Pignet <eric@erixpage.com>
Fri, 1 Jul 2005 19:33:52 +0000 (19:33 +0000)
There are still many things to do.

svn path=/branches/work/kwordquiz/src/keduvocexpression.h; revision=430557

12 files changed:
kwordquiz/MultipleChoice.cpp [new file with mode: 0644]
kwordquiz/MultipleChoice.h [new file with mode: 0644]
kwordquiz/grammarmanager.cpp [new file with mode: 0644]
kwordquiz/grammarmanager.h [new file with mode: 0644]
kwordquiz/keduvocdocument.cpp [new file with mode: 0644]
kwordquiz/keduvocdocument.h [new file with mode: 0644]
kwordquiz/keduvocexpression.cpp [new file with mode: 0644]
kwordquiz/keduvocexpression.h [new file with mode: 0644]
kwordquiz/keduvockvtmlreader.cpp [new file with mode: 0644]
kwordquiz/keduvockvtmlreader.h [new file with mode: 0644]
kwordquiz/keduvockvtmlwriter.cpp [new file with mode: 0644]
kwordquiz/keduvockvtmlwriter.h [new file with mode: 0644]

diff --git a/kwordquiz/MultipleChoice.cpp b/kwordquiz/MultipleChoice.cpp
new file mode 100644 (file)
index 0000000..e73f358
--- /dev/null
@@ -0,0 +1,119 @@
+/***************************************************************************
+
+              manage multiple choice suggestions for queries
+
+    -----------------------------------------------------------------------
+
+    begin                : Mon Oct 29 18:09:29 1999
+
+    copyright            : (C) 1999-2001 Ewald Arnold
+                           (C) 2001 The KDE-EDU team
+    email                : kvoctrain@ewald-arnold.de
+
+    -----------------------------------------------------------------------
+
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ ***************************************************************************/
+
+#include "MultipleChoice.h"
+
+MultipleChoice::MultipleChoice (
+           const QString &mc1,
+           const QString &mc2,
+           const QString &mc3,
+           const QString &mc4,
+           const QString &mc5
+          )
+{
+   setMC1 (mc1);
+   setMC2 (mc2);
+   setMC3 (mc3);
+   setMC4 (mc4);
+   setMC5 (mc5);
+}
+
+
+bool MultipleChoice::isEmpty() const
+{
+  return   muc1.stripWhiteSpace().isEmpty()
+        && muc2.stripWhiteSpace().isEmpty()
+        && muc3.stripWhiteSpace().isEmpty()
+        && muc4.stripWhiteSpace().isEmpty()
+        && muc5.stripWhiteSpace().isEmpty();
+}
+
+
+void MultipleChoice::clear()
+{
+   muc1 = "";
+   muc2 = "";
+   muc3 = "";
+   muc4 = "";
+   muc5 = "";
+}
+
+
+QString MultipleChoice::mc (unsigned idx) const
+{
+   switch (idx) {
+     case 0: return muc1;
+     case 1: return muc2;
+     case 2: return muc3;
+     case 3: return muc4;
+     case 4: return muc5;
+   }
+   return "";
+}
+
+
+unsigned MultipleChoice::size()
+{
+   normalize();
+   unsigned num = 0;
+   if (!muc1.isEmpty() )
+     ++num;
+   if (!muc2.isEmpty() )
+     ++num;
+   if (!muc3.isEmpty() )
+     ++num;
+   if (!muc4.isEmpty() )
+     ++num;
+   if (!muc5.isEmpty() )
+     ++num;
+   return num;
+}
+
+
+void MultipleChoice::normalize()
+{
+  // fill from first to last
+
+  if (muc1.isEmpty()) {
+    muc1 = muc2;
+    muc2 = "";
+  }
+
+  if (muc2.isEmpty()) {
+    muc2 = muc3;
+    muc3 = "";
+  }
+
+  if (muc3.isEmpty()) {
+    muc3 = muc4;
+    muc4 = "";
+  }
+
+  if (muc4.isEmpty()) {
+    muc4 = muc5;
+    muc5 = "";
+  }
+
+}
diff --git a/kwordquiz/MultipleChoice.h b/kwordquiz/MultipleChoice.h
new file mode 100644 (file)
index 0000000..99bc56e
--- /dev/null
@@ -0,0 +1,74 @@
+/***************************************************************************
+
+              manage multiple choice suggestions for queries
+
+    -----------------------------------------------------------------------
+
+    begin                : Mon Oct 29 18:09:29 1999
+
+    copyright            : (C) 1999-2001 Ewald Arnold
+                           (C) 2001 The KDE-EDU team
+    email                : kvoctrain@ewald-arnold.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.                                   *
+ *                                                                         *
+ ***************************************************************************/
+
+
+#ifndef MultipleChoice_included
+#define MultipleChoice_included
+
+#include <qstring.h>
+
+#define MAX_MULTIPLE_CHOICE  5  // select one out of x
+
+class MultipleChoice
+{
+
+public:
+
+   MultipleChoice() {}
+
+   MultipleChoice (
+           const QString &mc1,
+           const QString &mc2,
+           const QString &mc3,
+           const QString &mc4,
+           const QString &mc5
+          );
+
+   void setMC1 (const QString &s) { muc1 = s; }
+   void setMC2 (const QString &s) { muc2 = s; }
+   void setMC3 (const QString &s) { muc3 = s; }
+   void setMC4 (const QString &s) { muc4 = s; }
+   void setMC5 (const QString &s) { muc5 = s; }
+
+   QString mc1 () const { return muc1; }
+   QString mc2 () const { return muc2; }
+   QString mc3 () const { return muc3; }
+   QString mc4 () const { return muc4; }
+   QString mc5 () const { return muc5; }
+
+   QString mc (unsigned idx) const;
+
+   bool isEmpty() const;
+   void normalize();
+   void clear();
+   unsigned size();
+
+protected:
+
+   QString  muc1, muc2, muc3, muc4, muc5;
+};
+
+
+#endif // MultipleChoice_included
diff --git a/kwordquiz/grammarmanager.cpp b/kwordquiz/grammarmanager.cpp
new file mode 100644 (file)
index 0000000..e586a97
--- /dev/null
@@ -0,0 +1,503 @@
+/***************************************************************************
+
+             manage grammar parts (articles, conjugation)
+
+    -----------------------------------------------------------------------
+
+    begin          : Sat Nov 27 09:50:53 MET 1999
+
+    copyright      : (C) 1999-2001 Ewald Arnold <kvoctrain@ewald-arnold.de>
+                     (C) 2001 The KDE-EDU team
+                     (C) 2004-2005 Peter Hedlund <peter@peterandlinda.com>
+
+    -----------------------------------------------------------------------
+
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ ***************************************************************************/
+
+#include "grammarmanager.h"
+
+#include <klocale.h>
+
+Conjugation::conjug_name_t
+Conjugation::names [] =
+{
+  { CONJ_SIMPLE_PRESENT,    I18N_NOOP("Simple Present") },
+  { CONJ_PRESENT_PROGR,     I18N_NOOP("Preset Progressive") },
+  { CONJ_PRESENT_PERFECT,   I18N_NOOP("Preset Perfect") },
+
+  { CONJ_SIMPLE_PAST,       I18N_NOOP("Simple Past") },
+  { CONJ_PAST_PROGR,        I18N_NOOP("Past Progressive") },
+  { CONJ_PAST_PARTICIPLE,   I18N_NOOP("Past Participle") },
+
+  { CONJ_FUTURE,            I18N_NOOP("Future") }
+};
+
+
+vector<QString> Conjugation::userTenses;
+
+
+//================================================================
+
+Comparison::Comparison (
+           const QString &l1,
+           const QString &l2,
+           const QString &l3
+          )
+{
+   setL1 (l1);
+   setL2 (l2);
+   setL3 (l3);
+}
+
+
+bool Comparison::isEmpty() const
+{
+  return   ls1.stripWhiteSpace().isEmpty()
+        && ls2.stripWhiteSpace().isEmpty()
+        && ls3.stripWhiteSpace().isEmpty();
+}
+
+
+void Comparison::clear()
+{
+   ls1 = "";
+   ls2 = "";
+   ls3 = "";
+}
+
+
+//=================================================================
+
+
+Article::Article
+          (const QString &fem_def, const QString &fem_indef,
+           const QString &mal_def, const QString &mal_indef,
+           const QString &nat_def, const QString &nat_indef
+          )
+{
+   setFemale  (fem_def, fem_indef);
+   setMale    (mal_def, mal_indef);
+   setNatural (nat_def, nat_indef);
+}
+
+
+void Article::setFemale
+  (const QString &def, const QString &indef)
+{
+   fem_def = def;
+   fem_indef = indef;
+}
+
+
+void Article::setMale
+  (const QString &def, const QString &indef)
+{
+   mal_def = def;
+   mal_indef = indef;
+}
+
+
+void Article::setNatural
+  (const QString &def, const QString &indef)
+{
+   nat_def = def;
+   nat_indef = indef;
+}
+
+
+void Article::female
+  (QString &def, QString &indef) const
+{
+   def = fem_def;
+   indef = fem_indef;
+}
+
+
+void Article::male
+  (QString &def, QString &indef) const
+{
+   def = mal_def;
+   indef = mal_indef;
+}
+
+
+void Article::natural
+  (QString &def, QString &indef) const
+{
+   def = nat_def;
+   indef = nat_indef;
+}
+
+
+
+//==============================================================
+
+
+int Conjugation::numEntries() const
+{
+   return conjugations.size();
+}
+
+
+vector<TenseRelation> Conjugation::getRelation ()
+{
+  vector<TenseRelation> vec;
+
+  for (int i = 0; i < numInternalNames(); i++) {
+    vec.push_back(TenseRelation(names[i].abbrev,
+                                i18n(names[i].name)));
+  }
+
+  for (int i = 0; i < (int) userTenses.size(); i++) {
+    QString s;
+    s.setNum(i+1);
+    s.insert(0, UL_USER_TENSE);
+    vec.push_back(TenseRelation(s, userTenses[i]));
+  }
+
+  return vec;
+}
+
+
+void Conjugation::setTenseNames (vector<QString> names)
+{
+  userTenses = names;
+}
+
+
+QString Conjugation::getName (const QString &abbrev)
+{
+   if (abbrev.length() >= 2 && abbrev[0] == QString(UL_USER_TENSE)) {
+     QString s = abbrev;
+     s.remove(0, 1);
+     int i = s.toInt() - 1;
+
+     if (i < (int) userTenses.size() )
+       return userTenses[i];
+     else
+       return "";
+   }
+   else {
+     for (int i = 0; i < (int) numInternalNames(); i++)
+       if (names[i].abbrev == abbrev) {
+         return i18n(names[i].name);
+       }
+   }
+
+   return "";
+}
+
+
+QString Conjugation::getName (int idx)
+{
+   if (idx < numInternalNames() )
+     return i18n(names[idx].name);
+
+   else if (idx < numTenses() )
+     return userTenses[idx-numInternalNames()];
+
+   else
+     return "";
+}
+
+
+QString Conjugation::getAbbrev (const QString &name)
+{
+   for (int i = 0; i < (int) userTenses.size(); i++)
+     if (userTenses[i] == name) {
+       QString s;
+       s.setNum(i+1);
+       s.insert(0, UL_USER_TENSE);
+       return s;
+     }
+
+   for (int i = 0; i < (int) numInternalNames(); i++)
+     if (names[i].name == name)
+       return names[i].abbrev;
+
+   return "";
+}
+
+
+QString Conjugation::getAbbrev (int idx)
+{
+   if (idx < numInternalNames() )
+     return names[idx].abbrev;
+
+   else if (idx < numTenses() ) {
+     QString s;
+     s.setNum(idx-numInternalNames()+1);
+     s.insert(0, UL_USER_TENSE);
+     return s;
+   }
+
+   else
+     return "";
+}
+
+
+int Conjugation::numInternalNames()
+{
+   return sizeof(names) / sizeof(names[0]);
+}
+
+
+int Conjugation::numTenses()
+{
+   return numInternalNames()+userTenses.size();
+}
+
+
+QString Conjugation::getType (int idx)
+{
+  if (idx >= (int) conjugations.size() )
+    return "";
+
+  return conjugations[idx].type;
+}
+
+
+void Conjugation::setType (int idx, const QString & type)
+{
+  if (idx >= (int) conjugations.size() )
+    return;
+
+  conjugations[idx].type = type;
+}
+
+
+void Conjugation::cleanUp ()
+{
+  for (int i = (int)conjugations.size()-1; i >= 0; i--) {
+    const conjug_t *ctp = &conjugations[i];
+    if (   ctp->pers1_sing.stripWhiteSpace().isEmpty()
+        && ctp->pers2_sing.stripWhiteSpace().isEmpty()
+        && ctp->pers3_m_sing.stripWhiteSpace().isEmpty()
+        && ctp->pers3_f_sing.stripWhiteSpace().isEmpty()
+        && ctp->pers3_n_sing.stripWhiteSpace().isEmpty()
+        && ctp->pers1_plur.stripWhiteSpace().isEmpty()
+        && ctp->pers2_plur.stripWhiteSpace().isEmpty()
+        && ctp->pers3_m_plur.stripWhiteSpace().isEmpty()
+        && ctp->pers3_f_plur.stripWhiteSpace().isEmpty()
+        && ctp->pers3_n_plur.stripWhiteSpace().isEmpty()
+       )
+     conjugations.erase(conjugations.begin() + i);
+  }
+}
+
+
+bool Conjugation::isEmpty (int idx)
+{
+  if (idx < (int) conjugations.size()) {
+    const conjug_t *ctp = &conjugations[idx];
+    return ctp->pers1_sing.stripWhiteSpace().isEmpty()
+        && ctp->pers2_sing.stripWhiteSpace().isEmpty()
+        && ctp->pers3_m_sing.stripWhiteSpace().isEmpty()
+        && ctp->pers3_f_sing.stripWhiteSpace().isEmpty()
+        && ctp->pers3_n_sing.stripWhiteSpace().isEmpty()
+        && ctp->pers1_plur.stripWhiteSpace().isEmpty()
+        && ctp->pers2_plur.stripWhiteSpace().isEmpty()
+        && ctp->pers3_m_plur.stripWhiteSpace().isEmpty()
+        && ctp->pers3_f_plur.stripWhiteSpace().isEmpty()
+        && ctp->pers3_n_plur.stripWhiteSpace().isEmpty();
+  }
+  return true;
+}
+
+
+#define _GET_CON_(elem, type, default) \
+   for (int i = 0; i < (int) conjugations.size(); i++) \
+     if (conjugations[i].type == type) \
+        return conjugations[i].elem; \
+   return default;
+
+
+bool Conjugation::pers3SingularCommon(const QString &type) const
+{
+  _GET_CON_(s3common, type, false);
+}
+
+
+bool Conjugation::pers3PluralCommon(const QString &type) const
+{
+  _GET_CON_(p3common, type, false);
+}
+
+
+QString Conjugation::pers1Singular
+  (const QString &type) const
+{
+  _GET_CON_(pers1_sing, type, "");
+}
+
+
+QString Conjugation::pers2Singular
+  (const QString &type) const
+{
+  _GET_CON_(pers2_sing, type, "");
+}
+
+
+QString Conjugation::pers3FemaleSingular
+  (const QString &type) const
+{
+  _GET_CON_(pers3_f_sing, type, "");
+}
+
+
+QString Conjugation::pers3MaleSingular
+  (const QString &type) const
+{
+  _GET_CON_(pers3_m_sing, type, "");
+}
+
+
+QString Conjugation::pers3NaturalSingular
+  (const QString &type) const
+{
+  _GET_CON_(pers3_n_sing, type, "");
+}
+
+
+QString Conjugation::pers1Plural
+  (const QString &type) const
+{
+  _GET_CON_(pers1_plur, type, "");
+}
+
+
+QString Conjugation::pers2Plural
+  (const QString &type) const
+{
+  _GET_CON_(pers2_plur, type, "");
+}
+
+
+QString Conjugation::pers3FemalePlural
+  (const QString &type) const
+{
+  _GET_CON_(pers3_f_plur, type, "");
+}
+
+
+QString Conjugation::pers3MalePlural
+  (const QString &type) const
+{
+  _GET_CON_(pers3_m_plur, type, "");
+}
+
+
+QString Conjugation::pers3NaturalPlural
+  (const QString &type) const
+{
+  _GET_CON_(pers3_n_plur, type, "");
+}
+
+
+#undef _GET_CON_
+
+
+#define _SET_CON_(elem, type, str) \
+   for (int i = 0; i < (int) conjugations.size(); i++) \
+     if (conjugations[i].type == type) { \
+       conjugations[i].elem = str; \
+       return; \
+     } \
+   conjug_t ct; \
+   ct.type = type; \
+   ct.elem = str; \
+   conjugations.push_back(ct);
+
+
+void Conjugation::setPers3PluralCommon(const QString &type, bool f)
+{
+  _SET_CON_(p3common, type, f);
+}
+
+
+void Conjugation::setPers3SingularCommon(const QString &type, bool f)
+{
+  _SET_CON_(s3common, type, f);
+}
+
+
+void Conjugation::setPers1Singular
+  (const QString &type, const QString &str)
+{
+  _SET_CON_(pers1_sing, type, str);
+}
+
+
+void Conjugation::setPers2Singular
+  (const QString &type, const QString &str)
+{
+  _SET_CON_(pers2_sing, type, str);
+}
+
+
+void Conjugation::setPers3FemaleSingular
+  (const QString &type, const QString &str)
+{
+  _SET_CON_(pers3_f_sing, type, str);
+}
+
+
+void Conjugation::setPers3MaleSingular
+  (const QString &type, const QString &str)
+{
+  _SET_CON_(pers3_m_sing, type, str);
+}
+
+
+void Conjugation::setPers3NaturalSingular
+  (const QString &type, const QString &str)
+{
+  _SET_CON_(pers3_n_sing, type, str);
+}
+
+
+void Conjugation::setPers1Plural
+  (const QString &type, const QString &str)
+{
+  _SET_CON_(pers1_plur, type, str);
+}
+
+
+void Conjugation::setPers2Plural
+  (const QString &type, const QString &str)
+{
+  _SET_CON_(pers2_plur, type, str);
+}
+
+
+void Conjugation::setPers3FemalePlural
+  (const QString &type, const QString &str)
+{
+  _SET_CON_(pers3_f_plur, type, str);
+}
+
+
+void Conjugation::setPers3MalePlural
+  (const QString &type, const QString &str)
+{
+  _SET_CON_(pers3_m_plur, type, str);
+}
+
+
+void Conjugation::setPers3NaturalPlural
+  (const QString &type, const QString &str)
+{
+  _SET_CON_(pers3_n_plur, type, str);
+}
+
+#undef _SET_CON_
+
diff --git a/kwordquiz/grammarmanager.h b/kwordquiz/grammarmanager.h
new file mode 100644 (file)
index 0000000..1b9cfe7
--- /dev/null
@@ -0,0 +1,215 @@
+/***************************************************************************
+
+              manage grammer parts (articles, conjugation)
+
+    -----------------------------------------------------------------------
+
+    begin                : Sat Nov 27 09:50:53 MET 1999
+
+    copyright            : (C) 1999-2001 Ewald Arnold
+                           (C) 2001 The KDE-EDU team
+    email                : kvoctrain@ewald-arnold.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.                                   *
+ *                                                                         *
+ ***************************************************************************/
+
+
+#ifndef grammarmanager_included
+#define grammarmanager_included
+
+#include <qstring.h>
+
+#include <vector>
+using namespace std;
+
+#define CONJ_SIMPLE_PRESENT    "PrSi" // I live at home  what you frequently do
+#define CONJ_PRESENT_PROGR     "PrPr" // I am working    what you currently are doing
+#define CONJ_PRESENT_PERFECT   "PrPe" // I have cleaned  tell, #that# something has happened
+
+#define CONJ_SIMPLE_PAST       "PaSi" // the train left 2 min ago  when did it happen
+#define CONJ_PAST_PROGR        "PaPr" // it was raining  what happen at a given time in the past
+#define CONJ_PAST_PARTICIPLE   "PaPa" // I cleaned       tell, #that# it happened
+
+#define CONJ_FUTURE            "FuSi"
+
+#define CONJ_PREFIX            "--"   // definition of prefixes (I, you, ..)
+
+#define UL_USER_TENSE  "#"   // designates number of user tense
+
+class Article
+{
+
+public:
+
+   Article() {}
+
+   Article (
+           const QString &fem_def, const QString &fem_indef,
+           const QString &mal_def, const QString &mal_indef,
+           const QString &nat_def, const QString &nat_indef
+          );
+
+   void setFemale  (const QString &def, const QString &indef);
+   void setMale    (const QString &def, const QString &indef);
+   void setNatural (const QString &def, const QString &indef);
+
+   void female  (QString &def, QString &indef) const;
+   void male    (QString &def, QString &indef) const;
+   void natural (QString &def, QString &indef) const;
+
+protected:
+
+   QString  fem_def, fem_indef,
+            mal_def, mal_indef,
+            nat_def, nat_indef;
+};
+
+
+class Comparison
+{
+
+public:
+
+   Comparison() {}
+
+   Comparison (
+           const QString &l1,
+           const QString &l2,
+           const QString &l3
+          );
+
+   void setL1 (const QString &s) { ls1 = s; }
+   void setL2 (const QString &s) { ls2 = s; }
+   void setL3 (const QString &s) { ls3 = s; }
+
+   QString l1 () const { return ls1; }
+   QString l2 () const { return ls2; }
+   QString l3 () const { return ls3; }
+
+   bool isEmpty() const;
+   void clear();
+
+protected:
+
+   QString  ls1, ls2, ls3;
+};
+
+
+class TenseRelation
+{
+ public:
+
+  TenseRelation (const QString & _short, const QString & _long)
+    : shortId (_short), longId(_long) {}
+
+  inline QString shortStr() const { return shortId; }
+  inline QString longStr()  const { return longId;  }
+
+ protected:
+
+  QString  shortId, longId;
+};
+
+
+class Conjugation
+{
+
+public:
+
+   Conjugation () {}
+
+   int numEntries() const;
+
+   static vector<TenseRelation> getRelation ();
+   static void setTenseNames (vector<QString> names);
+
+   static QString getName (const QString &abbrev);
+   static QString getName (int index);
+   static QString getAbbrev (const QString &name);
+   static QString getAbbrev (int index);
+   static int numInternalNames();
+   static int numTenses();
+
+   QString getType (int index);
+   void setType (int index, const QString & type);
+   void cleanUp();
+   bool isEmpty (int idx);
+
+   QString pers1Singular(const QString &type) const;
+   QString pers2Singular(const QString &type) const;
+   bool    pers3SingularCommon(const QString &type) const;
+   QString pers3FemaleSingular(const QString &type) const;
+   QString pers3MaleSingular(const QString &type) const;
+   QString pers3NaturalSingular(const QString &type) const;
+
+   QString pers1Plural(const QString &type) const;
+   QString pers2Plural(const QString &type) const;
+   bool    pers3PluralCommon(const QString &type) const;
+   QString pers3FemalePlural(const QString &type) const;
+   QString pers3MalePlural(const QString &type) const;
+   QString pers3NaturalPlural(const QString &type) const;
+
+   void setPers1Singular(const QString &type, const QString &str);
+   void setPers2Singular(const QString &type, const QString &str);
+   void setPers3SingularCommon(const QString &type, bool f);
+   void setPers3FemaleSingular(const QString &type, const QString &str);
+   void setPers3MaleSingular(const QString &type, const QString &str);
+   void setPers3NaturalSingular(const QString &type, const QString &str);
+
+   void setPers1Plural(const QString &type, const QString &str);
+   void setPers2Plural(const QString &type, const QString &str);
+   void setPers3PluralCommon(const QString &type, bool f);
+   void setPers3FemalePlural(const QString &type, const QString &str);
+   void setPers3MalePlural(const QString &type, const QString &str);
+   void setPers3NaturalPlural(const QString &type, const QString &str);
+
+private:
+
+   struct conjug_t {
+
+      conjug_t() {
+        p3common = false;
+        s3common = false;
+      }
+
+      QString type;
+      bool    p3common,
+              s3common;
+      QString pers1_sing,
+              pers2_sing,
+              pers3_m_sing,
+              pers3_f_sing,
+              pers3_n_sing,
+              pers1_plur,
+              pers2_plur,
+              pers3_m_plur,
+              pers3_f_plur,
+              pers3_n_plur;
+   };
+
+   struct conjug_name_t {
+      const char *abbrev;
+      const char *name;
+   };
+
+protected:
+
+   vector<conjug_t>       conjugations;
+
+   static conjug_name_t   names [];
+   static vector<QString> userTenses;
+};
+
+
+#endif // grammarmanager_included
+
diff --git a/kwordquiz/keduvocdocument.cpp b/kwordquiz/keduvocdocument.cpp
new file mode 100644 (file)
index 0000000..ba9f546
--- /dev/null
@@ -0,0 +1,1052 @@
+/***************************************************************************
+                        Vocabulary Document for KDE Edu
+    -----------------------------------------------------------------------
+    copyright            : (C) 1999-2001 Ewald Arnold
+                           (C) 2001 The KDE-EDU team
+                           (C) 2005 Peter Hedlung
+    email                : peter@peterandlinda.com
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ ***************************************************************************/
+
+#include "keduvocdocument.h"
+
+#include <kapplication.h>
+#include <klocale.h>
+#include <kdebug.h>
+#include <kmessagebox.h>
+#include <kio/netaccess.h>
+
+#include <qfileinfo.h>
+
+#include <algorithm>
+#include <functional>
+#include <vector>
+using namespace std;
+
+#include <iostream>
+
+#include <float.h>
+
+#include "keduvockvtmlwriter.h"
+#include "keduvockvtmlreader.h"
+//#include "prefs.h"
+
+//********************************************************
+//  KEduVocDocument
+//********************************************************
+
+KEduVocDocument::KEduVocDocument(QObject *parent)
+{
+  Init();
+}
+
+
+KEduVocDocument::~KEduVocDocument()
+{
+}
+
+
+void KEduVocDocument::setVersion (const QString & vers)
+{
+  doc_version = vers;
+}
+
+
+void KEduVocDocument::getVersion(int &, int &, int &)
+{
+}
+
+
+void KEduVocDocument::Init ()
+{
+
+//TODO setVersion (QString::fromUtf8(KVOCTRAIN_VERSION_STRING));
+  lesson_descr.clear();
+  type_descr.clear();
+  tense_descr.clear();
+  langs.clear();
+  extraSizehints.clear();
+  sizehints.clear();
+  vocabulary.clear();
+  dirty = false;
+  sort_allowed = true;
+  unknown_attr = false;
+  unknown_elem = false;
+  sort_lesson = false;
+  for (int i = 0; i < (int) langs.size(); i++)
+    sort_lang.push_back(false);
+  setCurrentLesson (0);
+  queryorg = "";
+  querytrans = "";
+  doc_url.setFileName(i18n("Untitled"));
+  doctitle = "";
+  author = "";
+}
+
+
+bool KEduVocDocument::open(const KURL& url, bool append)
+{
+  Init();
+  if (!url.isEmpty())
+    doc_url = url;
+
+  // TODO EPT  connect( this, SIGNAL(progressChanged(KEduVocDocument*,int)), parent, SLOT(slotProgress(KEduVocDocument*,int)) );
+
+  QString tmpfile;
+  if (KIO::NetAccess::download( url, tmpfile, 0 ))
+  {
+    QFile f(tmpfile);
+    if (!f.open(IO_ReadOnly))
+    {
+      KMessageBox::error(0, i18n("<qt>Cannot open file<br><b>%1</b></qt>").arg(url.path()));
+      return false;
+    }
+
+    FileType ft = detectFT(url.path());
+
+    bool read = false;
+    while (!read) {
+
+      QApplication::setOverrideCursor( waitCursor );
+      switch (ft) {
+        case kvtml:
+        {
+          KEduVocKvtmlReader kvtmlReader(&f);
+          read = kvtmlReader.readDoc(this);
+        }
+        break;
+
+        case vt_lex:
+        {
+          QTextStream is (&f);
+          //TODO read = loadFromLex (is);
+        }
+        break;
+
+        case vt_vcb:
+        {
+          QTextStream is (&f);
+          //TODO read = loadFromVcb (is);
+        }
+        break;
+
+        case csv:
+        {
+          QTextStream is(&f);
+          //TODO read = loadFromCsv(is);
+        }
+        break;
+
+        default:
+        {
+          KEduVocKvtmlReader kvtmlReader(&f);
+          read = kvtmlReader.readDoc(this);
+        }
+      }
+
+      QApplication::restoreOverrideCursor();
+
+      if (!read) {
+        if (unknown_attr || unknown_elem ) {
+          Init();
+          return false;
+        }
+        // TODO new readers provide an explicite error message
+        // the two messages should be merged
+        QString format = i18n("Could not load \"%1\"\nDo you want to try again?");
+        QString msg = format.arg(url.path());
+        int result = KMessageBox::warningContinueCancel(0, msg,
+                                                        kapp->makeStdCaption(i18n("I/O Failure")),
+                                                        i18n("&Retry"));
+        if ( result == KMessageBox::Cancel ) {
+          Init();
+          return false;
+        }
+      }
+    }
+    f.close();
+    KIO::NetAccess::removeTempFile( tmpfile );
+  }
+  return true;
+}
+
+
+bool KEduVocDocument::saveAs(QObject *parent, const KURL & url, QString title, FileType ft)
+{
+  connect( this, SIGNAL(progressChanged(KEduVocDocument*,int)), parent, SLOT(slotProgress(KEduVocDocument*,int)) );
+
+  KURL tmp (url);
+  if (title == i18n("Untitled"))
+    title = QString::null;
+  if (title == doc_url.fileName())
+    title = QString::null;
+
+  if (ft == automatic)
+  {
+    if (tmp.path().right(strlen("." KVTML_EXT)) == "." KVTML_EXT)
+      ft = kvtml;
+    else if (tmp.path().right(strlen("." VT5_LEX_EXT)) == "." VT5_LEX_EXT)
+      ft = vt_lex;
+    else if (tmp.path().right(strlen("." VCB_EXT)) == "." VCB_EXT)
+      ft = vt_vcb;
+    else if (tmp.path().right(strlen("." CSV_EXT)) == "." CSV_EXT)
+      ft = csv;
+    else
+    {
+      tmp.setFileName(tmp.path() + "." KVTML_EXT);
+      ft = kvtml;
+    }
+  }
+
+  bool saved = false;
+  while (!saved)
+  {
+
+    QFile f(tmp.path());
+
+    if (!f.open(IO_WriteOnly))
+    {
+      KMessageBox::error(0, i18n("<qt>Cannot write to file<br><b>%1</b></qt>").arg(tmp.path()));
+      return false;
+    }
+
+    QApplication::setOverrideCursor( waitCursor );
+    switch (ft) {
+      case kvtml: {
+        //TODO handle title
+        KEduVocKvtmlWriter kvtmlWriter(&f);
+        saved = kvtmlWriter.writeDoc(this);
+      }
+      break;
+
+      case vt_lex: {
+        QTextStream os( &f );                       // serialize using f
+        //TODO saved = saveToLex(os, title);
+      }
+      break;
+
+      case vt_vcb: {
+        QTextStream os( &f );                       // serialize using f
+        //TODO saved = saveToVcb(os, title);
+      }
+      break;
+
+      case csv: {
+        QTextStream os( &f );                       // serialize using f
+        //TODO saved = saveToCsv(os, title);
+      }
+      break;
+
+      default: {
+        kdError() << "kvcotrainDoc::saveAs(): unknown filetype" << endl;
+      }
+      break;
+    }
+    f.close();
+    QApplication::restoreOverrideCursor();
+
+    if (!saved) {
+      // TODO new writers provide an explicite error message
+      // the two messages should be merged
+      QString format = i18n("Could not save \"%1\"\nDo you want to try again?");
+      QString msg = format.arg(tmp.path());
+      int result = KMessageBox::warningContinueCancel(0, msg,
+                                                      kapp->makeStdCaption(i18n("I/O Failure")),
+                                                      i18n("&Retry"));
+      if ( result == KMessageBox::Cancel ) return false;
+    }
+  }
+  doc_url = tmp;
+  dirty = false;
+  emit docModified(false);
+  return true;
+}
+
+
+KEduVocExpression *KEduVocDocument::getEntry(int index)
+{
+  if (index < 0 || index >= (int)vocabulary.size() )
+    return 0;
+  else
+    return &vocabulary[index];
+}
+
+
+void KEduVocDocument::removeEntry(int index)
+{
+  if (index >= 0 && index < (int)vocabulary.size() )
+    vocabulary.erase (vocabulary.begin() + index);
+}
+
+
+int KEduVocDocument::findIdent (const QString &lang) const
+{
+  vector<QString>::const_iterator first = langs.begin();
+  int count = 0;
+  while (first != langs.end()) {
+    if ( *first == lang)
+      return count;
+    first++;
+    count++;
+  }
+  return -1;
+}
+
+
+QString KEduVocDocument::getIdent (int index) const
+{
+  if (index >= (int)langs.size() || index < 1 )
+    return "";
+  else
+    return langs[index];
+}
+
+
+void KEduVocDocument::setIdent (int idx, const QString &id)
+{
+  if (idx < (int)langs.size() && idx >= 1 ) {
+    langs[idx] = id;
+  }
+}
+
+
+QString KEduVocDocument::getTypeName (int index) const
+{
+  if (index >= (int)type_descr.size())
+    return "";
+  else
+    return type_descr[index];
+}
+
+
+void KEduVocDocument::setTypeName (int idx, QString &id)
+{
+  if (idx >= (int)type_descr.size())
+    for (int i = (int)type_descr.size(); i <= idx; i++)
+      type_descr.push_back ("");
+
+  type_descr[idx] = id;
+}
+
+
+QString KEduVocDocument::getTenseName (int index) const
+{
+  if (index >= (int)tense_descr.size())
+    return "";
+  else
+    return tense_descr[index];
+}
+
+
+void KEduVocDocument::setTenseName (int idx, QString &id)
+{
+  if (idx >= (int)tense_descr.size())
+    for (int i = (int)tense_descr.size(); i <= idx; i++)
+      tense_descr.push_back ("");
+
+  tense_descr[idx] = id;
+}
+
+
+QString KEduVocDocument::getUsageName (int index) const
+{
+  if (index >= (int)usage_descr.size())
+    return "";
+  else
+    return usage_descr[index];
+}
+
+
+void KEduVocDocument::setUsageName (int idx, QString &id)
+{
+  if (idx >= (int)usage_descr.size())
+    for (int i = (int)usage_descr.size(); i <= idx; i++)
+      usage_descr.push_back ("");
+
+  usage_descr[idx] = id;
+}
+
+
+void KEduVocDocument::setConjugation (int idx, const Conjugation &con)
+{
+  if ( idx < 0) return;
+
+  // extend conjugation with empty elements
+  if ((int)conjugations.size() <= idx )
+    for (int i = conjugations.size(); i < idx+1; i++)
+      conjugations.push_back (Conjugation());
+
+  conjugations[idx] = con;
+}
+
+
+Conjugation KEduVocDocument::getConjugation (int idx) const
+{
+  if (idx >= (int)conjugations.size() || idx < 0) {
+    return Conjugation();
+  }
+  else {
+    return conjugations[idx];
+  }
+}
+
+
+void KEduVocDocument::setArticle (int idx, const Article &art)
+{
+  if ( idx < 0) return;
+
+  // extend conjugation with empty elements
+  if ((int)articles.size() <= idx )
+    for (int i = articles.size(); i < idx+1; i++)
+      articles.push_back (Article());
+
+  articles[idx] = art;
+}
+
+
+Article KEduVocDocument::getArticle (int idx) const
+{
+  if (idx >= (int)articles.size() || idx < 0) {
+    return Article();
+  }
+  else {
+    return articles[idx];
+  }
+}
+
+
+int KEduVocDocument::getSizeHint (int idx) const
+{
+  if (idx < 0) {
+    idx = -idx;
+    if (idx >= (int)extraSizehints.size() )
+      return 80; // make a good guess about column size
+    else {
+//      cout << "gsh " << idx << "  " << extraSizehints[idx] << endl;
+      return extraSizehints[idx];
+    }
+  }
+  else {
+    if (idx >= (int)sizehints.size() )
+      return 150; // make a good guess about column size
+    else {
+//      cout << "gsh " << idx << "  " << sizehints[idx] << endl;
+      return sizehints[idx];
+    }
+  }
+}
+
+
+void KEduVocDocument::setSizeHint (int idx, const int width)
+{
+//  cout << "ssh " << idx << "  " << width << endl;
+  if (idx < 0) {
+    idx = -idx;
+    if (idx >= (int)extraSizehints.size()) {
+      for (int i = (int)extraSizehints.size(); i <= idx; i++)
+        extraSizehints.push_back (80);
+    }
+    extraSizehints[idx] = width;
+
+  }
+  else {
+    if (idx >= (int)sizehints.size()) {
+      for (int i = (int)sizehints.size(); i <= idx; i++)
+        sizehints.push_back (150);
+    }
+    sizehints[idx] = width;
+  }
+}
+
+
+class eraseTrans : public unary_function<KEduVocExpression, void>
+{
+
+public:
+
+  eraseTrans (int idx)
+    : index (idx) {}
+
+  void operator() (KEduVocExpression& x) const
+    {
+      x.removeTranslation(index);
+    }
+
+ private:
+    int index;
+};
+
+
+void KEduVocDocument::removeIdent (int index)
+{
+  if (index < (int)langs.size() && index >= 1 ) {
+    langs.erase(langs.begin() + index);
+    for_each (vocabulary.begin(), vocabulary.end(), eraseTrans(index));
+  }
+}
+
+
+QString KEduVocDocument::getOriginalIdent () const
+{
+  if (langs.size() > 0)
+    return langs[0];
+  else
+    return "";
+}
+
+
+void KEduVocDocument::setOriginalIdent (const QString &id)
+{
+  if (langs.size() > 0) {
+    langs[0] = id;
+  }
+}
+
+
+class sortByOrg : public binary_function<KEduVocExpression, KEduVocExpression, bool>
+{
+
+public:
+
+  sortByOrg (bool _dir)
+    : dir (_dir) {}
+
+  bool operator() (const KEduVocExpression& x, const KEduVocExpression& y) const
+    {
+      return
+        !dir
+        ? (QString::compare(x.getOriginal().upper(),
+                            y.getOriginal().upper() ) < 0)
+        : (QString::compare(x.getOriginal().upper(),
+                            y.getOriginal().upper() ) > 0);
+    }
+
+ private:
+  bool          dir;
+};
+
+
+class sortByLessonAndOrg_alpha
+  : public binary_function<KEduVocExpression, KEduVocExpression, bool>
+{
+
+public:
+
+  sortByLessonAndOrg_alpha (bool _dir, KEduVocDocument &_doc)
+    : dir (_dir), doc(_doc) {}
+
+  bool operator() (const KEduVocExpression& x, const KEduVocExpression& y) const
+    {
+      if (x.getLesson() != y.getLesson() )
+        return
+          !dir
+          ? (QString::compare(doc.getLessonDescr(x.getLesson()).upper(),
+                              doc.getLessonDescr(y.getLesson()).upper() ) < 0)
+          : (QString::compare(doc.getLessonDescr(x.getLesson()).upper(),
+                              doc.getLessonDescr(y.getLesson()).upper() ) > 0);
+      else
+        return
+          !dir
+          ? (QString::compare(x.getOriginal().upper(),
+                              y.getOriginal().upper() ) < 0)
+          : (QString::compare(x.getOriginal().upper(),
+                              y.getOriginal().upper() ) > 0);
+    }
+
+ private:
+  bool          dir;
+  KEduVocDocument &doc;
+};
+
+
+class sortByLessonAndOrg_index
+  : public binary_function<KEduVocExpression, KEduVocExpression, bool>
+{
+
+public:
+
+  sortByLessonAndOrg_index (bool _dir, KEduVocDocument &_doc)
+    : dir (_dir), doc(_doc) {}
+
+  bool operator() (const KEduVocExpression& x, const KEduVocExpression& y) const
+    {
+      if (x.getLesson() != y.getLesson() )
+        return
+          !dir
+          ? x.getLesson() < y.getLesson()
+          : y.getLesson() < x.getLesson();
+      else
+        return
+          !dir
+          ? (QString::compare(x.getOriginal().upper(),
+                              y.getOriginal().upper() ) < 0)
+          : (QString::compare(x.getOriginal().upper(),
+                              y.getOriginal().upper() ) > 0);
+    }
+
+ private:
+  bool          dir;
+  KEduVocDocument &doc;
+};
+
+
+class sortByTrans : public binary_function<KEduVocExpression, KEduVocExpression, bool>
+{
+
+public:
+
+  sortByTrans (int i, bool _dir)
+    : index(i), dir (_dir) {}
+
+  bool operator() (const KEduVocExpression& x, const KEduVocExpression& y) const
+    {
+      return
+        !dir
+        ? (QString::compare(x.getTranslation(index).upper(),
+                            y.getTranslation(index).upper() ) < 0)
+        : (QString::compare(x.getTranslation(index).upper(),
+                            y.getTranslation(index).upper() ) > 0);
+    }
+
+ private:
+  int  index;
+  bool dir;
+};
+
+
+bool KEduVocDocument::sort (int index)
+{
+  if (!sort_allowed)
+    return false;
+
+  if (index >= numLangs())
+    return false;
+
+  if (sort_lang.size() < langs.size())
+    for (int i = sort_lang.size(); i < (int) langs.size(); i++)
+      sort_lang.push_back(false);
+
+  if (index == 0)
+    std::sort (vocabulary.begin(), vocabulary.end(), sortByOrg(sort_lang[0]));
+  else
+    std::sort (vocabulary.begin(), vocabulary.end(), sortByTrans(index, sort_lang[index]));
+  sort_lang[index] = !sort_lang[index];
+  return sort_lang[index];
+}
+
+
+bool KEduVocDocument::sortByLesson_alpha ()
+{
+  if (!sort_allowed)
+    return false;
+
+  std::sort (vocabulary.begin(), vocabulary.end(), sortByLessonAndOrg_alpha(sort_lesson, *this ));
+  sort_lesson = !sort_lesson;
+  return sort_lesson;
+}
+
+
+bool KEduVocDocument::sortByLesson_index ()
+{
+  if (!sort_allowed)
+    return false;
+
+  if (sort_lang.size() < langs.size())
+    for (int i = sort_lang.size(); i < (int) langs.size(); i++)
+      sort_lang.push_back(false);
+
+  std::sort (vocabulary.begin(), vocabulary.end(), sortByLessonAndOrg_index(sort_lesson, *this ));
+  sort_lesson = !sort_lesson;
+  sort_lang[0] = sort_lesson;
+  return sort_lesson;
+}
+
+
+class resetAll : public unary_function<KEduVocExpression, void>
+{
+
+public:
+
+  resetAll (int less)
+    : lesson(less) {}
+
+  void operator() (KEduVocExpression& x)
+    {
+       for (int i = 0; i <= x.numTranslations(); i++) {
+         if (lesson == 0 || lesson == x.getLesson() ) {
+            x.setGrade(i, KV_NORM_GRADE, false);
+            x.setGrade(i, KV_NORM_GRADE, true);
+            x.setQueryCount (i, 0, true);
+            x.setQueryCount (i, 0, false);
+            x.setBadCount (i, 0, true);
+            x.setBadCount (i, 0, false);
+            x.setQueryDate (i, 0, true);
+            x.setQueryDate (i, 0, false);
+         }
+       }
+    }
+ private:
+  int lesson;
+};
+
+
+class resetOne : public unary_function<KEduVocExpression, void>
+{
+
+public:
+
+  resetOne (int idx, int less)
+    : index (idx), lesson(less) {}
+
+  void operator() (KEduVocExpression& x)
+    {
+       if (lesson == 0 || lesson == x.getLesson() ) {
+         x.setGrade(index, KV_NORM_GRADE, false);
+         x.setGrade(index, KV_NORM_GRADE, true);
+         x.setQueryCount (index, 0, true);
+         x.setQueryCount (index, 0, false);
+         x.setBadCount (index, 0, true);
+         x.setBadCount (index, 0, false);
+         x.setQueryDate (index, 0, true);
+         x.setQueryDate (index, 0, false);
+       }
+    }
+
+ private:
+  int index;
+  int lesson;
+};
+
+
+void KEduVocDocument::resetEntry (int index, int lesson)
+{
+  if (index < 0)
+    for_each (vocabulary.begin(), vocabulary.end(), resetAll(lesson) );
+  else
+    for_each (vocabulary.begin(), vocabulary.end(), resetOne(index, lesson) );
+}
+
+
+QString KEduVocDocument::getLessonDescr(int idx) const
+{
+  if (idx == 0)
+    return i18n("<no lesson>");
+
+  if (idx <= 0 || idx > (int) lesson_descr.size() )
+    return "";
+
+  return lesson_descr[idx-1];
+}
+
+
+vector<int> KEduVocDocument::getLessonsInQuery() const
+{
+  vector<int> iqvec;
+  for (unsigned i = 0; i < lessons_in_query.size(); i++)
+    if (lessons_in_query[i]) {
+      iqvec.push_back(i+1);   // Offset <no lesson>
+//      cout << "getliq: " << i+1 << endl;
+    }
+  return iqvec;
+}
+
+
+void KEduVocDocument::setLessonsInQuery(vector<int> lesson_iq)
+{
+  lessons_in_query.clear();
+  for (unsigned i = 0; i < lesson_descr.size(); i++)
+    lessons_in_query.push_back(false);
+
+  for (unsigned i = 0; i < lesson_iq.size(); i++)
+    if (lesson_iq[i] <= (int) lessons_in_query.size() ) {
+      lessons_in_query[lesson_iq[i]-1] = true;    // Offset <no lesson>
+//      cout << "setliq: " << lesson_iq[i] << " " << i << endl;
+    }
+}
+
+
+QString KEduVocDocument::getTitle() const
+{
+  if (doctitle.isEmpty())
+    return doc_url.fileName();
+  else
+    return doctitle;
+}
+
+
+QString KEduVocDocument::getAuthor() const
+{
+  return author;
+}
+
+
+QString KEduVocDocument::getLicense() const
+{
+  return license;
+}
+
+
+QString KEduVocDocument::getDocRemark() const
+{
+  return doc_remark;
+}
+
+
+void KEduVocDocument::setTitle(const QString & title)
+{
+  doctitle = title.stripWhiteSpace();
+}
+
+
+void KEduVocDocument::setAuthor(const QString & s)
+{
+  author = s.stripWhiteSpace();
+}
+
+
+void KEduVocDocument::setLicense(const QString & s)
+{
+  license = s.stripWhiteSpace();
+}
+
+
+void KEduVocDocument::setDocRemark(const QString & s)
+{
+  doc_remark = s.stripWhiteSpace();
+}
+
+
+int KEduVocDocument::search(QString substr, int id,
+                         int first, int last,
+                         bool word_start,
+                         bool)
+{
+   if (last >= numEntries()
+       || last < 0 )
+     last = numEntries();
+
+   if (first < 0)
+     first = 0;
+
+   if (id >= numLangs()
+      || last < first
+      )
+     return -1;
+
+   if (id == 0) {
+     for (int i = first; i < last; i++) {
+       if (word_start) {
+         if (getEntry(i)->getOriginal().find (substr, 0, false) == 0)  // case insensitive
+           return i;
+       }
+       else {
+         if (getEntry(i)->getOriginal().find (substr, 0, false) > -1)  // case insensitive
+           return i;
+       }
+     }
+   }
+   else {
+     for (int i = first; i < last; i++) {
+       if (word_start) {
+         if (getEntry(i)->getTranslation(id).find (substr, 0, false) == 0) // case insensitive
+           return i;
+       }
+       else {
+         if (getEntry(i)->getTranslation(id).find (substr, 0, false) > -1) // case insensitive
+           return i;
+       }
+     }
+   }
+   return -1;
+}
+
+#define _OFFSET     0x40
+#define _BITMASK    0x3F
+#define _BITUSED    6
+
+QString KEduVocDocument::compressDate(unsigned long l) const
+{
+   if (l == 0)
+     return "";
+
+   QString res;
+   if (l <= KVD_ZERO_TIME)
+     l = 1;
+   else
+     l -= KVD_ZERO_TIME;
+   while (l != 0) {
+     char c = _OFFSET + (l & _BITMASK);
+     res.insert (0, c);
+     l >>= _BITUSED;
+   }
+   return res;
+}
+
+
+unsigned long KEduVocDocument::decompressDate(QString s) const
+{
+   if (s.isEmpty())
+     return 0;
+
+   long res = 0;
+   unsigned incr = 0;
+   for (int i = s.length()-1; i >= 0; i--) {
+     char c = s.local8Bit()[i];
+     res += ((c - _OFFSET) & _BITMASK) << incr ;
+     incr += _BITUSED;
+   }
+   return res > 48 ? res+KVD_ZERO_TIME : 0;  // early bug with "0"
+}
+
+
+KEduVocDocument::FileType KEduVocDocument::detectFT(const QString &filename)
+{
+   QFile f( filename );
+   if (!f.open( IO_ReadOnly ))
+     return csv;
+
+   QDataStream is( &f );
+
+   Q_INT8 c1, c2, c3, c4, c5;
+   is >> c1
+      >> c2
+      >> c3
+      >> c4
+      >> c5;  // guess filetype by first x bytes
+
+   QTextStream ts (&f);
+   QString line;
+   line = ts.readLine();
+   line.insert (0, c5);
+   line.insert (0, c4);
+   line.insert (0, c3);
+   line.insert (0, c2);
+   line.insert (0, c1);
+   f.close();
+
+   bool stat = is.device()->status();
+   if (stat != IO_Ok)
+     return kvd_none;
+   if (c1 == '<' && c2 == '?' && c3 == 'x' && c4 == 'm' && c5 == 'l')
+     return kvtml;
+
+   if (line.find (VCB_SEPARATOR) >= 0)
+     return vt_vcb;
+
+   if (line == LEX_IDENT_50)
+     return vt_lex;
+
+   return csv;
+}
+
+
+class expRef {
+
+public:
+
+  expRef (KEduVocExpression *_exp, int _idx)
+   {
+      idx    = _idx;
+      exp    = _exp;
+   }
+
+  bool operator< (const expRef& y) const
+    {
+      QString s1 = exp->getOriginal();
+      QString s2 = y.exp->getOriginal();
+      int cmp = QString::compare(s1.upper(), s2.upper());
+      if (cmp != 0)
+        return cmp < 0;
+
+      for (int i = 1; i < (int) exp->numTranslations(); i++) {
+
+        s1 = exp->getTranslation(i);
+        s2 = y.exp->getTranslation(i);
+        cmp = QString::compare(s1.upper(), s2.upper() );
+        if (cmp != 0)
+          return cmp < 0;
+      }
+      return cmp < 0;
+    }
+
+  int            idx;
+  KEduVocExpression *exp;
+};
+
+
+int KEduVocDocument::cleanUp()
+{
+  int count = 0;
+  KEduVocExpression *kve1, *kve2;
+  vector<expRef> shadow;
+  vector<int> to_delete;
+
+  for (int i = 0; i < (int) vocabulary.size(); i++)
+    shadow.push_back (expRef (getEntry(i), i));
+  std::sort(shadow.begin(), shadow.end());
+
+#ifdef CLEAN_BUG
+  ofstream sso ("shadow.out");
+  for (int i = shadow.size()-1; i > 0; i--) {
+    kve1 = shadow[i].exp;
+    sso << kve1->getOriginal() << "  ";
+    for (int l = 1; l < (int) numLangs(); l++ )
+      sso << kve1->getTranslation(l)  << "  ";
+    sso << endl;
+  }
+#endif
+
+  int ent_no = 0;
+  int ent_percent = vocabulary.size () / 100;
+  float f_ent_percent = vocabulary.size () / 100.0;
+  emit progressChanged(this, 0);
+
+  for (int i = shadow.size()-1; i > 0; i--) {
+    kve1 = shadow[i].exp;
+    kve2 = shadow[i-1].exp;
+
+    ent_no++;
+    if (ent_percent != 0 && (ent_no % ent_percent) == 0 )
+      emit progressChanged(this, (int)((ent_no / f_ent_percent) / 2.0));
+
+    bool equal = true;
+    if (kve1->getOriginal() == kve2->getOriginal() ) {
+      for (int l = 1; equal && l < (int) numLangs(); l++ )
+        if (kve1->getTranslation(l) != kve2->getTranslation(l))
+          equal = false;
+
+      if (equal) {
+        to_delete.push_back(shadow[i-1].idx);
+        count++;
+      }
+    }
+  }
+
+  // removing might take very long
+  ent_no = 0;
+  ent_percent = to_delete.size () / 100;
+  f_ent_percent = to_delete.size () / 100.0;
+  emit progressChanged(this, 0);
+
+  std::sort (to_delete.begin(), to_delete.end() );
+  for (int i = (int) to_delete.size()-1; i >= 0; i--) {
+    ent_no++;
+    if (ent_percent != 0 && (ent_no % ent_percent) == 0 )
+      emit progressChanged(this, (int)(50 + ent_no / f_ent_percent / 2.0));
+#ifdef CLEAN_BUG
+    sso << getEntry(to_delete[i])->getOriginal() << endl;
+#endif
+    removeEntry (to_delete[i]);
+    setModified();
+  }
+
+  return count;
+}
diff --git a/kwordquiz/keduvocdocument.h b/kwordquiz/keduvocdocument.h
new file mode 100644 (file)
index 0000000..0036ef5
--- /dev/null
@@ -0,0 +1,677 @@
+/***************************************************************************
+                        Vocabulary Document for KDE Edu
+    -----------------------------------------------------------------------
+    copyright            : (C) 1999-2001 Ewald Arnold
+                           (C) 2001 The KDE-EDU team
+                           (C) 2005 Peter Hedlung
+    email                : peter@peterandlinda.com
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   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.                                   *
+ *                                                                         *
+ ***************************************************************************/
+
+#ifndef KEDUVOCDOCUMENT_H
+#define KEDUVOCDOCUMENT_H
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <qobject.h>
+
+#include <kurl.h>
+
+#include "keduvocexpression.h"
+
+#define KVD_ZERO_TIME  934329599   // 1999-08-10 23:59:59, never change
+#define KVD_VERS_PREFIX " v"       // kvoctrain v0.1.0
+
+/** XML tags and attribute names */
+
+#define KV_DOCTYPE     "kvtml"     // doctype
+#define KV_TITLE       "title"     // doc title
+#define KV_AUTHOR      "author"    // doc author
+#define KV_LICENSE     "license"   // doc license
+#define KV_DOC_REM     "remark"    // doc remark
+#define KV_LINES       "lines"     // entries
+#define KV_GENERATOR   "generator" // who generated the doc
+#define KV_COLS        "cols"      // columns
+#define KV_ENCODING    "encoding"  // document encoding  (obsolete!)
+
+#define KV_EXPR        "e"         // entry for one expression
+#define KV_ORG         "o"         // original expression in specified language
+#define KV_TRANS       "t"         // translated expression in specified language
+#define KV_LANG        "l"         // language: en, de, it, fr ...
+#define KV_QUERY       "q"         // query: org or translation
+#define KV_O           "o"         // org
+#define KV_T           "t"         // translation
+#define KV_GRADE       "g"         // grade of knowledge: 0=well known, x=not known for x times
+#define KV_LESS_MEMBER "m"         // member of lesson 1 .. x
+#define KV_COUNT       "c"         // number of times queried
+#define KV_SIZEHINT    "width"     // recommended column width
+#define KV_CHARSET     "charset"   // recommended charset (obsolete!)
+#define KV_BAD         "b"         // number of times failed
+#define KV_DATE        "d"         // last query date
+#define KV_DATE2       "w"         // last query date, compressed format
+#define KV_REMARK      "r"         // remark for this entry
+#define KV_FAUX_AMI_F  "ff"        // false friend of this entry from org
+#define KV_FAUX_AMI_T  "tf"        // false friend of this entry to org
+#define KV_SYNONYM     "y"         // synonym (same meaning) of expr
+#define KV_ANTONYM     "a"         // antonym (oppositite) of expr
+#define KV_PRONUNCE    "p"         // how to pronunce this expression
+#define KV_SELECTED    "s"         // entry selected for queries
+#define KV_INACTIVE    "i"         // entry inactive (for queries)
+#define KV_EXPRTYPE    "t"         // type of expression
+#define KV_EXAMPLE     "x"         // example string with word
+#define KV_USAGE       "u"         // usage label
+#define KV_PARAPHRASE  "h"         // paraphrase for expression
+
+/*
+ <type>
+  <desc no="1">My type 1</desc>
+  <desc no="2">My type 2</desc>
+ </type>
+*/
+
+#define KV_TYPE_GRP    "type"      // type descriptor group
+#define KV_TYPE_DESC   "desc"      // type descriptor
+#define KV_TYPE_NO     "no"        // type descriptor number
+
+/*
+ <usage>
+  <desc no="1">My usage 1</desc>
+  <desc no="2">My usage 2</desc>
+ </type>
+*/
+
+#define KV_USAGE_GRP    "usage"     // usage descriptor group
+#define KV_USAGE_DESC   "desc"      // usage descriptor
+#define KV_USAGE_NO     "no"        // usage descriptor number
+
+/*
+ <lesson width="138">
+  <desc no="1">Lesson #1</desc>
+  <desc no="2" query="1">Lesson #2</desc>
+ </lesson>
+*/
+
+#define KV_LESS_GRP    "lesson"    // lesson descriptor group
+#define KV_LESS_CURR   "current"   // is current lesson
+#define KV_LESS_DESC   "desc"      // lesson descriptor
+#define KV_LESS_QUERY  "query"     // lesson contained in query
+#define KV_LESS_NO     "no"        // lesson descriptor number
+
+/*
+ <tense>
+  <desc no="1">user tense #1</desc>
+  <desc no="2">user tense #2</desc>
+ </tense>
+*/
+
+#define KV_TENSE_GRP    "tense"     // tense descriptor group
+#define KV_TENSE_DESC   "desc"      // tense descriptor
+#define KV_TENSE_NO     "no"        // tense descriptor number
+
+/*
+ <options>
+  <sort on="1"/>
+ </options>
+*/
+
+#define KV_OPTION_GRP  "options"   // internal options group
+#define KV_OPT_SORT    "sort"      // allow sorting
+#define KV_BOOL_FLAG   "on"        // general boolean flag
+
+/*
+ <article>
+  <e l="de">        lang determines also lang order in entries !!
+   <fi>eine</fi>    which must NOT differ
+   <fd>die</fd>
+   <mi>ein</mi>
+   <md>der</md>
+   <ni>ein</ni>
+   <nd>das</nd>
+  </e>
+ </article>
+*/
+
+#define KV_ARTICLE_GRP "article"   // article descriptor group
+#define KV_ART_ENTRY   "e"         // article entry
+#define KV_ART_FD      "fd"        // female definite
+#define KV_ART_MD      "md"        // male definite
+#define KV_ART_ND      "nd"        // natural definite
+#define KV_ART_FI      "fi"        // female indefinite
+#define KV_ART_MI      "mi"        // male indefinite
+#define KV_ART_NI      "ni"        // natural indefinite
+
+/*
+ <comparison>
+   <l1>good</l1>
+   <l2>better</l2>
+   <l3>best</l3>
+ </comparison>
+*/
+
+#define KV_COMPARISON_GRP "comparison"   // comparison descriptor group
+#define KV_COMP_L1        "l1"           // base form
+#define KV_COMP_L2        "l2"           // next form
+#define KV_COMP_L3        "l3"           // last form
+
+/*
+ <multiplechoice>
+   <mc1>good</mc1>
+   <mc2>better</mc2>
+   <mc3>best</mc3>
+   <mc4>best 2</mc4>
+   <mc5>best 3</mc5>
+ </multiplechoice>
+*/
+
+#define KV_MULTIPLECHOICE_GRP "multiplechoice"   // multiple choice descriptor group
+#define KV_MC_1        "mc1"           // choice 1
+#define KV_MC_2        "mc2"           // choice 2
+#define KV_MC_3        "mc3"           // choice 3
+#define KV_MC_4        "mc4"           // choice 4
+#define KV_MC_5        "mc5"           // choice 5
+
+/*
+ <conjugation>        used in header for definiton of "prefix"
+  <e l="de">          lang determines also lang order in entries !!
+   <s1>I</s1>         which must NOT differ in subsequent <e>-tags
+   <s2>you<2>
+   <s3f>he</s3f>
+   <s3m>she</s3m>
+   <s3n>it</s3n>
+   <p1>we</p1>
+   <p2>you</p2>
+   <p3f>they</p3f>
+   <p3m>they</p3m>
+   <p3n>they</p3n>
+  </e>
+ </conjugation>
+
+ <conjugation>        and in entry for definition of tenses of (irreg.) verbs
+  <t n="sipa">
+   <s1>go</s1>
+   <s2>go</s2>
+   <s3f>goes</s3f>
+   <s3m>goes</s3m>
+   <s3n>goes</s3n>
+   <p1>go</p1>
+   <p2>go</p2>
+   <p3f>go</p3f>
+   <p3m>go</p3m>
+   <p3n>go</p3n>
+  </t>
+ </conjugation>
+*/
+
+#define KV_CONJUG_GRP  "conjugation" // conjugation descriptor group
+#define KV_CON_ENTRY   "e"           // conjugation entry (header)
+#define KV_CON_TYPE    "t"           // conjugation type  (voc entries)
+#define KV_CON_NAME    "n"           // conjugation type name (voc entries)
+#define KV_CON_P1S     "s1"          // 1. person singular
+#define KV_CON_P2S     "s2"          // 2. person singular
+#define KV_CON_P3SF    "s3f"         // 3. person singular female
+#define KV_CON_P3SM    "s3m"         // 3. person singular male
+#define KV_CON_P3SN    "s3n"         // 3. person singular natural
+#define KV_CON_P1P     "p1"          // 1. person plural
+#define KV_CON_P2P     "p2"          // 2. person plural
+#define KV_CON_P3PF    "p3f"         // 3. person plural female
+#define KV_CON_P3PM    "p3m"         // 3. person plural male
+#define KV_CON_P3PN    "p3n"         // 3. person plural natural
+#define KV_CONJ_COMMON "common"      // female contains common for all three
+
+#define LEX_IDENT_50   "Vocabulary Trainer V5.0"
+
+#define KVTML_EXT        "kvtml"
+#define VT5_LEX_EXT      "lex"
+#define QVOCAB_EXT       "qvo"
+#define VCB_EXT          "vocab"
+#define KVL_EXT          "vl"
+#define CSV_EXT          "csv"
+#define TXT_EXT          "txt"
+
+#define VCB_SEPARATOR    "__"
+
+class QTextStream;
+class QStringList;
+class MultipleChoice;
+
+/*************************************************************
+  * This class contains the expressions of your vocabulary
+  ************************************************************/
+
+class KEduVocDocument : public QObject
+{
+  Q_OBJECT
+  friend class KEduVocKvtmlWriter;
+  friend class KEduVocKvtmlReader;
+ public:
+
+  enum FileType { kvd_none, automatic,
+                  kvtml,
+                  kvtbin,
+                  vt_lex, vt_vcb, csv /*, kvoclearn, qvocab*/ };
+
+  /** Constructor for the fileclass of the application
+   *
+   * @param obj calling object
+   */
+  KEduVocDocument(QObject* obj);
+
+  /** Destructor for the fileclass of the application */
+  ~KEduVocDocument();
+
+  /** indicates that doc is (not) modified
+   *
+   * @param dirty   new state
+   */
+  inline void setModified (bool _dirty = true) { emit docModified(dirty = _dirty); }
+
+  /** appends another entry at the end
+   *
+   * @param expr            expression to append
+   */
+  inline void appendEntry (KEduVocExpression *expr)
+    { vocabulary.push_back (*expr); dirty = true; }
+
+  /** insert an entry
+   *
+   * @param expr            expression to append
+   * @param index           index of entry
+   */
+  inline void insertEntry(KEduVocExpression *expr, int index)
+    { vocabulary.insert(vocabulary.begin()+index, *expr); dirty = true; }
+
+  /** removes entry from doc
+   *
+   * @param index           index of entry
+   */
+  void removeEntry (int index);
+
+  /** sorts vocabulary alphabetically
+   *
+   * @param  index            index expression
+   * @result direction of sorting: true = ascending
+   */
+  bool sort (int index);
+
+  /** removes equal entries (orig + all translations)
+   *
+   * @result                 number of removed entries
+   */
+  int cleanUp();
+
+  /** sorts vocabulary by lesson indices
+   * @result direction of sorting: true = ascending
+   */
+  bool sortByLesson_index ();
+
+  /** sorts vocabulary by lesson name
+   * @result direction of sorting: true = ascending
+   */
+  bool sortByLesson_alpha ();
+
+  /** enables sorting
+   */
+  inline void allowSorting(bool allow) { sort_allowed = allow; }
+
+  /** enables sorting
+   */
+  inline bool isAllowedSorting() { return sort_allowed; }
+
+  /** returns the modification state of the doc */
+  inline bool isModified () const { return dirty; }
+
+  /** returns originals identifier
+   */
+  QString getOriginalIdent () const;
+
+  /** set originals identifier
+   */
+  void setOriginalIdent (const QString &id);
+
+  /** returns identifier of translation x
+   *
+   * @param index            number of translation 1..x
+   * @result                 ident string: de=german, en=englisch, ..
+   */
+  QString getIdent (int index) const;
+
+  /** sets identifier of translation
+   *
+   * @param index            number of translation 1..x
+   * @param lang             ident string: de=german, en=englisch, ..
+   */
+  void setIdent (int index, const QString &lang);
+
+  /** removes identifier an the according translation in all entries
+   *
+   * @param index            number of translation 1..x
+   */
+  void removeIdent (int index);
+
+  /** determines if given translation is available and where
+   *
+   * @param lang             identifier of language
+   * @result                 index of translation, 0=original, -1=none
+   */
+  int findIdent (const QString &lang) const;
+
+  /** returns attribute string
+   *
+   * @param index            number of attribute
+   * @result                 string
+   */
+  QString getTypeName (int index) const;
+
+  /** sets attribute string
+   *
+   * @param index            number of attribute
+   * @param str              name of attribute
+   */
+  void setTypeName (int index, QString &str);
+
+  /** gets descr of types  */
+  inline vector<QString> getTypeDescr() const { return type_descr; }
+
+  /** sets descr of types  */
+  inline void setTypeDescr(vector<QString> names) { type_descr = names; }
+
+  /** returns tense string
+   *
+   * @param index            number of tense
+   * @result                 string
+   */
+  QString getTenseName (int index) const;
+
+  /** sets tense string
+   *
+   * @param index            number of tense
+   * @param str              name of tense
+   */
+  void setTenseName (int index, QString &str);
+
+  /** gets descr of tenses  */
+  inline vector<QString> getTenseDescr() const { return tense_descr; }
+
+  /** sets descr of tenses  */
+  inline void setTenseDescr(vector<QString> names) { tense_descr = names; }
+
+  /** returns usage string
+   *
+   * @param index            number of usage
+   * @result                 string
+   */
+  QString getUsageName (int index) const;
+
+  /** sets usage string
+   *
+   * @param index            number of usage
+   * @param str              name of usage
+   */
+  void setUsageName (int index, QString &str);
+
+  /** gets descr of usages  */
+  inline vector<QString> getUsageDescr() const { return usage_descr; }
+
+  /** sets descr of usages  */
+  inline void setUsageDescr(vector<QString> names) { usage_descr = names; }
+
+  /** open a document file
+   *
+   * @param url     
+   * @result         true if successful
+   */
+  bool open(const KURL& url, bool append);
+
+  /** saves the data under the given name
+   *
+   * @param url     if url is empty (or NULL) actual name is preserved
+   * @result         true if successful
+   */
+  bool saveAs (QObject *parent, const KURL & url, QString title, FileType ft);
+
+  /** returns count of entries
+   */
+  inline int numEntries() const { return vocabulary.size(); }
+
+  /** sets grades to KV_NORM_GRADE, counts to 0 ...
+   *
+   * @param index    index of language 0..x, -1 = all
+   * @param lesson   lesson id, if this is 0 all lesson are affected,
+   *                 otherwise only matching numbers
+   */
+  void resetEntry (int index = -1, int lesson = 0);
+
+  /** returns count of different languages
+   */
+  inline int numLangs() const { return langs.size(); } // org + translations
+
+  /** append new lang ident
+   */
+  inline void appendLang(const QString & id) { langs.push_back(id); }
+
+  /** returns pointer to expression object x
+   *
+   * @param index     index of desired entry
+   * @result          pointer to object or NULL if index out of range
+   */
+  KEduVocExpression *getEntry(int index);
+
+  /** search substring in vocabulary (case insensitive always)
+   *
+   * @param substr     partial string to search
+   * @param id         which language to search: 0=org, 1..x=translation
+   * @param first      index from where to start
+   * @param last       index of last entry, -1 goes till end
+   * @param word_start false: search partial string,
+   *                   true:always from beginning of word
+   * @param tolerant
+   * @result          index of found entry, -1 if none
+   */
+  int search(QString substr, int id,
+             int first=0, int last=-1, bool word_start = false, bool tolerant=false);
+
+  /** returns url of xml file  */
+  inline KURL URL() const {return doc_url; }
+
+  /** sets url of xml file  */
+  inline void setURL(const KURL& url) {doc_url = url;}
+
+  /** returns title of xml file  */
+  QString getTitle() const;
+
+  /** returns author of file  */
+  QString getAuthor() const;
+
+  /** returns license of file  */
+  QString getLicense() const;
+
+  /** returns remark of file  */
+  QString getDocRemark() const;
+
+  inline void getQueryLang(QString &org, QString &trans) const
+    { org = queryorg; trans = querytrans; }
+
+  inline void setQueryLang(const QString &org, const QString &trans)
+    { queryorg = org; querytrans = trans; }
+
+  /** sets title of xml file  */
+  void setTitle(const QString & title);
+
+  /** sets author of file  */
+  void setAuthor(const QString & author);
+
+  /** sets license of file  */
+  void setLicense(const QString & license);
+
+  /** sets remark of file  */
+  void setDocRemark(const QString & rem);
+
+  /** gets version of loaded file  */
+  void getVersion(int &major, int &minor, int &patch);
+
+  /** returns current lesson index  */
+  inline int getCurrentLesson() const { return current_lesson; }
+
+  /** sets current lesson index
+   * @param lesson    index of lesson
+   */
+  inline void setCurrentLesson(int lesson) { current_lesson = lesson; }
+
+  /** returns descr of lesson  */
+  QString getLessonDescr(int index) const;
+
+  /** returns lessons in current query  */
+  vector<int> getLessonsInQuery() const;
+
+  /** sets lessons in current query  */
+  void setLessonsInQuery(vector<int>);
+
+  inline vector<QString> getLessonDescr() const { return lesson_descr; }
+
+  inline int numLessons () const {return (int) lesson_descr.size(); }
+
+  /** sets descr of lesson  */
+  inline void setLessonDescr(vector<QString> names) { lesson_descr = names; }
+
+  /** returns pointer to conjugations if available
+   *
+   * @param index            index of translation
+   */
+  Conjugation getConjugation(int index) const;
+
+  /** sets conjugations
+   *
+   * @param index            index of translation
+   * @param con              conjugation block
+   */
+  void setConjugation(int index, const Conjugation &con);
+
+  /** returns pointer to articles if available
+   *
+   * @param index            index of translation
+   */
+  Article getArticle(int index) const;
+
+  /** sets articles
+   *
+   * @param index            index of translation
+   * @param art              article block
+   */
+  void setArticle(int index, const Article &art);
+
+  /** compress date */
+  QString compressDate(unsigned long) const;
+
+  /** decompress date */
+  unsigned long decompressDate(QString) const;
+
+  /** returns recommended size
+   *
+   * @param index            number of expr, -1 = lesson
+   * @result                 width of column
+   */
+  int getSizeHint (int index) const;
+
+  /** sets recommended size
+   *
+   * @param index            number of expr, -1 = lesson
+   * @param width            width of column
+   */
+  void setSizeHint (int index, const int width);
+
+  bool unknownAttribute (int line, const QString &name, const QString &attr);
+  void unknownElement (int line, const QString &elem );
+  void errorKvtMl (int line, const QString &text );
+  void warningKvtMl (int line, const QString &text );
+
+  void errorLex (int line, const QString &text );
+
+  void errorCsv (int line, const QString &text );
+
+  FileType detectFT(const QString &filename);
+
+  friend class QueryManager;
+
+signals:
+  void progressChanged (KEduVocDocument *, int curr_percent);
+  void docModified (bool mod);
+
+protected:
+
+  void Init();
+
+  /** sets version of loaded file  */
+  void setVersion(const QString & ver);
+
+  /* TODO
+  bool saveToLex     (QTextStream& os, QString &title);
+  bool loadFromLex   (QTextStream& is);
+  bool loadLessonLex (QTextStream& is);
+  bool saveLessonLex (QTextStream& os);
+  bool saveTypeNameLex   (QTextStream& os);
+  bool loadTypeNameLex   (QTextStream& is);
+
+  bool saveToCsv     (QTextStream& os, QString &title);
+  bool loadFromCsv   (QTextStream& is);
+  bool loadLessonCsv (QTextStream& is);
+  bool saveLessonCsv (QTextStream& os);
+  bool saveTypeNameCsv   (QTextStream& os);
+  bool loadTypeNameCsv   (QTextStream& is);
+
+  bool saveTypeNameVcb (QTextStream &os);
+  bool loadTypeNameVcb (QTextStream &is);
+  bool saveLessonVcb (QTextStream &os);
+  bool loadLessonVcb (QTextStream &is);
+  bool saveToVcb (QTextStream& os, QString &title);
+  bool loadFromVcb (QTextStream& is);
+  void errorVcb (int line, const QString &text );
+  */
+
+ private:
+  bool                  dirty;
+  KURL                  doc_url;
+  vector<bool>          sort_lang;
+  bool                  sort_lesson;
+  bool                  unknown_attr;
+  bool                  unknown_elem;
+  bool                  sort_allowed;
+
+  // save these to document
+  vector<QString>        langs;      //0= origin, 1,.. translations
+  int                    cols,
+                         lines;
+  int                    current_lesson;
+  vector<int>            extraSizehints;
+  vector<int>            sizehints;
+  QString                generator;
+  QString                queryorg,
+                         querytrans;
+  vector<KEduVocExpression>  vocabulary;
+  vector<bool>           lessons_in_query;
+  vector<QString>        lesson_descr;
+  vector<QString>        type_descr;
+  vector<QString>        tense_descr;
+  vector<QString>        usage_descr;
+  QString                doctitle;
+  QString                author;
+  QString                license;
+  QString                doc_remark;
+  QString                doc_version;
+
+  vector<Article>        articles;
+  vector<Conjugation>    conjugations;
+};
+
+
+#endif // KEDUVOCDOCUMENT_H
diff --git a/kwordquiz/keduvocexpression.cpp b/kwordquiz/keduvocexpression.cpp
new file mode 100644 (file)
index 0000000..df0342a
--- /dev/null
@@ -0,0 +1,792 @@
+/***************************************************************************
+                        Vocabulary Expression for KDE Edu
+    -----------------------------------------------------------------------
+    copyright            : (C) 1999-2001 Ewald Arnold
+                           (C) 2001 The KDE-EDU team
+                           (C) 2005 Peter Hedlung
+    email                : peter@peterandlinda.com
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ ***************************************************************************/
+
+#include "keduvocexpression.h"
+
+void KEduVocExpression::Init()
+{
+  grades.push_back(KV_NORM_GRADE);
+  rev_grades.push_back(KV_NORM_GRADE);
+  inquery = false;
+  active = true;
+  qcounts.push_back(0);
+  rev_qcounts.push_back(0);
+  bcounts.push_back(0);
+  rev_bcounts.push_back(0);
+  qdates.push_back(0);
+  rev_qdates.push_back(0);
+  lesson = 0;
+}
+
+
+KEduVocExpression::KEduVocExpression (QString &expr, int _lesson)
+{
+  Init();
+  setOriginal(expr.stripWhiteSpace() );
+  lesson = _lesson;
+}
+
+
+KEduVocExpression::KEduVocExpression ()
+{
+  Init();
+}
+
+
+KEduVocExpression::KEduVocExpression (QString &s, QString separator, int _lesson)
+{
+  Init();
+  QString se;
+  lesson = _lesson;
+
+  if (separator.length() ) {
+    int pos = s.find(separator);
+
+    if (pos == -1) {
+      setOriginal(s.stripWhiteSpace());
+    }
+    else {
+      se = s.left(pos).stripWhiteSpace();
+      setOriginal(se);
+      s.remove (0, pos+separator.length() );
+//      s.stripWhiteSpace();
+
+      // gather all translations
+      while ((pos = s.find(separator)) != -1) {
+        se = s.left(pos).stripWhiteSpace();
+        addTranslation(se, KV_NORM_GRADE, KV_NORM_GRADE);
+        s.remove (0, pos+separator.length() );
+//        s.stripWhiteSpace();
+      }
+      addTranslation(s.stripWhiteSpace(), KV_NORM_GRADE, KV_NORM_GRADE);
+    }
+  }
+}
+
+
+KEduVocExpression::~KEduVocExpression()
+{
+}
+
+
+int KEduVocExpression::numTranslations() const
+{
+  return translations.size();
+}
+
+
+QString KEduVocExpression::getRemark (int idx) const
+{
+  if (idx >= (int)remarks.size() || idx < 0) {
+    return "";
+  }
+  else {
+    return remarks[idx];
+  }
+}
+
+
+void KEduVocExpression::setRemark (int idx, const QString & expr)
+{
+  if ( idx < 0) return;
+
+  // extend remarks with empty strings if necessary
+  if ((int)remarks.size() <= idx )
+    for (int i = remarks.size(); i < idx+1; i++)
+      remarks.push_back ("");
+
+  remarks[idx] = expr.stripWhiteSpace();
+}
+
+
+void KEduVocExpression::setFauxAmi (int idx, const QString & expr, bool rev_ami)
+{
+  if (idx < 1) return;
+
+  if (rev_ami) {
+    // extend friend with empty strings if necessary
+    if ((int)rev_fauxAmi.size() <= idx )
+      for (int i = rev_fauxAmi.size(); i < idx+1; i++)
+        rev_fauxAmi.push_back ("");
+
+    rev_fauxAmi[idx] = expr.stripWhiteSpace();
+
+  }
+  else {
+    // extend friend with empty strings if necessary
+    if ((int)fauxAmi.size() <= idx )
+      for (int i = fauxAmi.size(); i < idx+1; i++)
+        fauxAmi.push_back ("");
+
+    fauxAmi[idx] = expr.stripWhiteSpace();
+  }
+}
+
+
+QString KEduVocExpression::getFauxAmi (int idx, bool rev_ami) const
+{
+  if (rev_ami) {
+    if (idx >= (int)rev_fauxAmi.size() || idx < 1 ) {
+      return "";
+    }
+
+    return rev_fauxAmi[idx];
+  }
+
+  if (idx >= (int)fauxAmi.size() || idx < 1 ) {
+    return "";
+  }
+
+  return fauxAmi[idx];
+}
+
+
+void KEduVocExpression::setSynonym (int idx, const QString & expr)
+{
+  if ( idx < 0) return;
+
+  // extend synonym with empty strings if necessary
+  if ((int)synonym.size() <= idx )
+    for (int i = synonym.size(); i < idx+1; i++)
+      synonym.push_back ("-");
+
+  synonym[idx] = expr.stripWhiteSpace();
+}
+
+
+QString KEduVocExpression::getSynonym (int idx) const
+{
+  if (idx >= (int)synonym.size() || idx < 0) {
+    return "";
+  }
+  else {
+    return synonym[idx];
+  }
+}
+
+
+void KEduVocExpression::setExample (int idx, const QString & expr)
+{
+  if ( idx < 0) return;
+
+  // extend exampls with empty strings if necessary
+  if ((int)example.size() <= idx )
+    for (int i = example.size(); i < idx+1; i++)
+      example.push_back ("");
+
+  example[idx] = expr.stripWhiteSpace();
+}
+
+
+QString KEduVocExpression::getExample (int idx) const
+{
+  if (idx >= (int)example.size() || idx < 0) {
+    return "";
+  }
+  else {
+    return example[idx];
+  }
+}
+
+
+void KEduVocExpression::setUsageLabel (int idx, const QString & expr)
+{
+  if ( idx < 0) return;
+
+  // extend labels with empty strings if necessary
+  if ((int)usageLabels.size() <= idx )
+    for (int i = usageLabels.size(); i < idx+1; i++)
+      usageLabels.push_back ("");
+
+  usageLabels[idx] = expr.stripWhiteSpace();
+}
+
+
+QString KEduVocExpression::getUsageLabel (int idx) const
+{
+  if (idx >= (int)usageLabels.size() || idx < 0) {
+    return "";
+  }
+  else {
+    return usageLabels[idx];
+  }
+}
+
+
+void KEduVocExpression::setParaphrase (int idx, const QString & expr)
+{
+  if ( idx < 0) return;
+
+  // extend phrase with empty strings if necessary
+  if ((int) paraphrases.size() <= idx )
+    for (int i = paraphrases.size(); i < idx+1; i++)
+      paraphrases.push_back ("");
+
+  paraphrases[idx] = expr.stripWhiteSpace();
+}
+
+
+QString KEduVocExpression::getParaphrase (int idx) const
+{
+  if (idx >= (int)paraphrases.size() || idx < 0) {
+    return "";
+  }
+  else {
+    return paraphrases[idx];
+  }
+}
+
+
+void KEduVocExpression::setAntonym (int idx, const QString & expr)
+{
+  if ( idx < 0) return;
+
+  // extend antonym with empty strings if necessary
+  if ((int)antonym.size() <= idx )
+    for (int i = antonym.size(); i < idx+1; i++)
+      antonym.push_back ("");
+
+  antonym[idx] = expr.stripWhiteSpace();
+}
+
+
+QString KEduVocExpression::getAntonym (int idx) const
+{
+  if (idx >= (int)antonym.size() || idx < 0) {
+    return "";
+  }
+  else {
+    return antonym[idx];
+  }
+}
+
+
+void KEduVocExpression::setConjugation (int idx, const Conjugation &con)
+{
+  if ( idx < 0) return;
+
+  // extend conjugation with empty elements
+  if ((int)conjugations.size() <= idx )
+    for (int i = conjugations.size(); i < idx+1; i++)
+      conjugations.push_back (Conjugation());
+
+  conjugations[idx] = con;
+}
+
+
+Conjugation KEduVocExpression::getConjugation (int idx) const
+{
+  if (idx >= (int)conjugations.size() || idx < 0) {
+    return Conjugation();
+  }
+  else {
+    return conjugations[idx];
+  }
+}
+
+
+void KEduVocExpression::setComparison (int idx, const Comparison &con)
+{
+  if ( idx < 0) return;
+
+  // extend comparison with empty elements
+  if ((int)comparisons.size() <= idx )
+    for (int i = comparisons.size(); i < idx+1; i++)
+      comparisons.push_back (Comparison());
+
+  comparisons[idx] = con;
+}
+
+
+Comparison KEduVocExpression::getComparison (int idx) const
+{
+  if (idx >= (int)comparisons.size() || idx < 0) {
+    return Comparison();
+  }
+  else {
+    return comparisons[idx];
+  }
+}
+
+
+void KEduVocExpression::setMultipleChoice (int idx, const MultipleChoice &mc)
+{
+  if ( idx < 0) return;
+
+  // extend comparison with empty elements
+  if ((int)mcs.size() <= idx )
+    for (int i = mcs.size(); i < idx+1; i++)
+      mcs.push_back (MultipleChoice());
+
+  mcs[idx] = mc;
+}
+
+
+MultipleChoice KEduVocExpression::getMultipleChoice (int idx) const
+{
+  if (idx >= (int)mcs.size() || idx < 0) {
+    return MultipleChoice();
+  }
+  else {
+    return mcs[idx];
+  }
+}
+
+
+QString KEduVocExpression::getPronunce (int idx) const
+{
+  if (idx >= (int)pronunces.size() || idx < 0) {
+    return "";
+  }
+  else {
+    return pronunces[idx];
+  }
+}
+
+
+void KEduVocExpression::setPronunce (int idx, const QString & expr)
+{
+  if ( idx < 0) return;
+
+  // extend with empty strings if necessary
+  if ((int)pronunces.size() <= idx )
+    for (int i = pronunces.size(); i < idx+1; i++)
+      pronunces.push_back ("");
+
+  pronunces[idx] = expr.stripWhiteSpace();
+}
+
+
+void KEduVocExpression::addTranslation (QString expr,
+                                    grade_t grade, grade_t rev_grade)
+{
+  if (grade > KV_MAX_GRADE)
+    grade = KV_MAX_GRADE;
+
+  if (rev_grade > KV_MAX_GRADE)
+    rev_grade = KV_MAX_GRADE;
+
+  grades.push_back (grade);
+  rev_grades.push_back (rev_grade);
+  translations.push_back (expr.stripWhiteSpace());
+}
+
+
+QString KEduVocExpression::getTranslation (int idx) const
+{
+  if (idx > (int)translations.size() || idx < 1)
+    return "";
+  else
+    return translations[idx-1];
+}
+
+
+void KEduVocExpression::removeTranslation (int idx)
+{
+   if (idx <= 0)
+     return;
+
+   if (idx <= numTranslations())
+     translations.erase (translations.begin() + idx-1);
+
+   if (idx < (int)remarks.size() )
+     remarks.erase (remarks.begin() + idx);
+
+   if (idx < (int)conjugations.size() )
+     conjugations.erase (conjugations.begin() + idx);
+
+   if (idx < (int)comparisons.size() )
+     comparisons.erase (comparisons.begin() + idx);
+
+   if (idx < (int)fauxAmi.size() )
+     fauxAmi.erase (fauxAmi.begin() + idx);
+
+   if (idx < (int)rev_fauxAmi.size() )
+     rev_fauxAmi.erase (rev_fauxAmi.begin() + idx);
+
+   if (idx < (int)synonym.size() )
+     synonym.erase (synonym.begin() + idx);
+
+   if (idx < (int)example.size() )
+     example.erase (example.begin() + idx);
+
+   if (idx < (int)usageLabels.size() )
+     usageLabels.erase (usageLabels.begin() + idx);
+
+   if (idx < (int)paraphrases.size() )
+     paraphrases.erase (paraphrases.begin() + idx);
+
+   if (idx < (int)antonym.size() )
+     antonym.erase (antonym.begin() + idx);
+
+   if (idx < (int)exprtypes.size() )
+     exprtypes.erase (exprtypes.begin() + idx);
+
+   if (idx < (int)pronunces.size() )
+     pronunces.erase (pronunces.begin() + idx);
+
+   if (idx < (int)grades.size() )
+     grades.erase (grades.begin() + idx);
+
+   if (idx < (int)rev_grades.size() )
+     rev_grades.erase (rev_grades.begin() + idx);
+
+   if (idx < (int)qcounts.size() )
+     qcounts.erase (qcounts.begin() + idx);
+
+   if (idx < (int)rev_qcounts.size() )
+     rev_qcounts.erase (rev_qcounts.begin() + idx);
+
+   if (idx < (int)bcounts.size() )
+     bcounts.erase (bcounts.begin() + idx);
+
+   if (idx < (int)rev_bcounts.size() )
+     rev_bcounts.erase (rev_bcounts.begin() + idx);
+
+   if (idx < (int)qdates.size() )
+     qdates.erase (qdates.begin() + idx);
+
+   if (idx < (int)rev_qdates.size() )
+     rev_qdates.erase (rev_qdates.begin() + idx);
+}
+
+
+void KEduVocExpression::setTranslation (int idx, const QString & expr)
+{
+  if ( idx <= 0) return;
+
+  // extend translations with empty strings if necessary
+  if ((int)translations.size() < idx )
+    for (int i = translations.size(); i < idx; i++)
+      translations.push_back ("");
+
+//  if (idx <= (int)translations.size())
+  translations[idx-1] = expr.stripWhiteSpace();
+}
+
+
+QString KEduVocExpression::gradeStr (int idx, bool rev_grade) const
+{
+  QString s;
+  s.setNum(getGrade(idx, rev_grade));
+  return s;
+}
+
+
+grade_t KEduVocExpression::getGrade (int idx, bool rev_grade) const
+{
+  if (rev_grade) {
+    if (idx >= (int)rev_grades.size() || idx < 1 ) {
+      return KV_NORM_GRADE;
+    }
+    else if (rev_grades[idx] > KV_MAX_GRADE) {
+      return KV_MAX_GRADE;
+    }
+
+    return rev_grades[idx];
+
+  }
+  else {
+    if (idx >= (int)grades.size() || idx < 1 ) {
+      return KV_NORM_GRADE;
+    }
+    else if (grades[idx] > KV_MAX_GRADE) {
+      return KV_MAX_GRADE;
+    }
+
+    return grades[idx];
+  }
+}
+
+
+void KEduVocExpression::setGrade (int idx, grade_t grade, bool rev_grade)
+{
+  if (idx < 1) return;
+
+  if (grade > KV_MAX_GRADE)
+    grade = KV_MAX_GRADE;
+  if (grade < KV_MIN_GRADE)
+    grade = KV_MIN_GRADE;
+
+  if (rev_grade) {
+    // extend rev grades with standard grade if necessary
+    if ((int)rev_grades.size() <= idx )
+      for (int i = rev_grades.size(); i <= idx; i++) {
+        rev_grades.push_back (KV_NORM_GRADE);
+    }
+    rev_grades[idx] = grade;
+  }
+  else {
+    // extend grades with standard grade if necessary
+    if ((int)grades.size() <= idx )
+      for (int i = grades.size(); i <= idx; i++) {
+        grades.push_back (KV_NORM_GRADE);
+      }
+    grades[idx] = grade;
+  }
+}
+
+
+void KEduVocExpression::incGrade (int idx, bool rev_grade)
+{
+  if (idx < 1) return;
+
+  if (rev_grade) {
+    // extend rev grades with standard grade if necessary
+    if ((int)rev_grades.size() <= idx )
+      for (int i = rev_grades.size(); i <= idx; i++) {
+        rev_grades.push_back (KV_NORM_GRADE);
+    }
+    if (rev_grades[idx] < KV_MAX_GRADE)
+      rev_grades[idx]++;
+  }
+  else {
+    // extend grades with standard grade if necessary
+    if ((int)grades.size() <= idx )
+      for (int i = grades.size(); i <= idx; i++) {
+        grades.push_back (KV_NORM_GRADE);
+      }
+    if (grades[idx] < KV_MAX_GRADE)
+      grades[idx]++;
+  }
+}
+
+
+void KEduVocExpression::decGrade (int idx, bool rev_grade)
+{
+  if (idx < 1) return;
+
+  if (rev_grade) {
+    // extend rev grades with standard grade if necessary
+    if ((int)rev_grades.size() <= idx )
+      for (int i = rev_grades.size(); i <= idx; i++) {
+        rev_grades.push_back (KV_NORM_GRADE);
+    }
+    if (rev_grades[idx] > KV_MIN_GRADE)
+      rev_grades[idx]--;
+  }
+  else {
+    // extend grades with standard grade if necessary
+    if ((int)grades.size() <= idx )
+      for (int i = grades.size(); i <= idx; i++) {
+        grades.push_back (KV_NORM_GRADE);
+      }
+    if (grades[idx] > KV_MIN_GRADE)
+      grades[idx]--;
+  }
+}
+
+
+count_t KEduVocExpression::getQueryCount (int idx, bool rev_count)  const
+{
+  if (rev_count) {
+    if (idx >= (int)rev_qcounts.size() || idx < 1 ) {
+      return 0;
+    }
+
+    return rev_qcounts[idx];
+  }
+
+  if (idx >= (int)qcounts.size() || idx < 1 ) {
+    return 0;
+  }
+
+  return qcounts[idx];
+}
+
+
+void KEduVocExpression::setQueryCount (int idx, count_t count, bool rev_count)
+{
+  if (idx < 1) return;
+
+  if (rev_count) {
+    // extend rev counts with 0 if necessary
+    if ((int)rev_qcounts.size() <= idx )
+      for (int i = rev_qcounts.size(); i <= idx; i++) {
+        rev_qcounts.push_back (0);
+    }
+
+    rev_qcounts[idx] = count;
+
+  }
+  else {
+    // extend counts with 0 if necessary
+    if ((int)qcounts.size() <= idx )
+      for (int i = qcounts.size(); i <= idx; i++) {
+        qcounts.push_back (0);
+      }
+    qcounts[idx] = count;
+  }
+}
+
+
+count_t KEduVocExpression::getBadCount (int idx, bool rev_count) const
+{
+  if (rev_count) {
+    if (idx >= (int)rev_bcounts.size() || idx < 1 ) {
+      return 0;
+    }
+
+    return rev_bcounts[idx];
+  }
+
+  if (idx >= (int)bcounts.size() || idx < 1 ) {
+    return 0;
+  }
+
+  return bcounts[idx];
+}
+
+
+void KEduVocExpression::setBadCount (int idx, count_t count, bool rev_count)
+{
+  if (idx < 1) return;
+
+  if (rev_count) {
+    // extend rev counts with 0 if necessary
+    if ((int)rev_bcounts.size() <= idx )
+      for (int i = rev_bcounts.size(); i <= idx; i++) {
+        rev_bcounts.push_back (0);
+    }
+
+    rev_bcounts[idx] = count;
+
+  }
+  else {
+    // extend counts with 0 if necessary
+    if ((int)bcounts.size() <= idx )
+      for (int i = bcounts.size(); i <= idx; i++) {
+        bcounts.push_back (0);
+      }
+    bcounts[idx] = count;
+  }
+}
+
+
+time_t KEduVocExpression::getQueryDate (int idx, bool rev_date) const
+{
+  if (rev_date) {
+    if (idx >= (int)rev_qdates.size() || idx < 1 ) {
+      return 0;
+    }
+
+    return rev_qdates[idx];
+  }
+
+  if (idx >= (int)qdates.size() || idx < 1 ) {
+    return 0;
+  }
+
+  return qdates[idx];
+}
+
+
+void KEduVocExpression::setQueryDate (int idx, time_t date, bool rev_date)
+{
+  if (idx < 1) return;
+
+  if (rev_date) {
+    // extend rev dates with 0 if necessary
+    if ((int)rev_qdates.size() <= idx )
+      for (int i = rev_qdates.size(); i <= idx; i++) {
+        rev_qdates.push_back (0);
+    }
+
+    rev_qdates[idx] = date;
+
+  }
+  else {
+    // extend dates with 0 if necessary
+    if ((int)qdates.size() <= idx )
+      for (int i = qdates.size(); i <= idx; i++) {
+        qdates.push_back (0);
+      }
+    qdates[idx] = date;
+  }
+}
+
+
+bool KEduVocExpression::uniqueType () const
+{
+  bool unique = true;
+  QString type0 = getType(0);
+  for (int i = 1; i < numTranslations(); i++)
+    if (type0 != getType(i) )
+      unique = false;
+  return unique;
+}
+
+
+QString KEduVocExpression::getType (int idx) const
+{
+  if (idx >= (int)exprtypes.size() || idx < 0) {
+    return "";
+  }
+  else {
+    return exprtypes[idx];
+  }
+}
+
+
+int KEduVocExpression::getLesson () const
+{
+  return lesson;
+}
+
+void KEduVocExpression::incQueryCount (int index, bool rev_count)
+{
+  setQueryCount (index, getQueryCount(index, rev_count)+1, rev_count);
+}
+
+
+void KEduVocExpression::incBadCount (int index, bool rev_count)
+{
+  setBadCount (index, getBadCount(index, rev_count)+1, rev_count);
+}
+
+
+void KEduVocExpression::setOriginal ( const QString & expr)
+{
+  origin = expr;
+}
+
+
+QString KEduVocExpression::getOriginal () const
+{
+  return origin;
+}
+
+
+void KEduVocExpression::setLesson (int l)
+{
+  lesson = l;
+}
+
+
+void KEduVocExpression::setType (int idx, const QString &type)
+{
+  if ( idx < 0) return;
+
+  // extend types with empty strings if necessary
+  if ((int)exprtypes.size() <= idx )
+    for (int i = exprtypes.size(); i < idx+1; i++)
+      exprtypes.push_back ("");
+
+  exprtypes[idx] = type.stripWhiteSpace();
+}
+
diff --git a/kwordquiz/keduvocexpression.h b/kwordquiz/keduvocexpression.h
new file mode 100644 (file)
index 0000000..615d561
--- /dev/null
@@ -0,0 +1,456 @@
+/***************************************************************************
+                        Vocabulary Expression for KDE Edu
+    -----------------------------------------------------------------------
+    copyright            : (C) 1999-2001 Ewald Arnold
+                           (C) 2001 The KDE-EDU team
+                           (C) 2005 Peter Hedlung
+    email                : peter@peterandlinda.com
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   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.                                   *
+ *                                                                         *
+ ***************************************************************************/
+
+#ifndef KEDUVOCEXPRESSION_H
+#define KEDUVOCEXPRESSION_H
+
+#define KV_MAX_GRADE       7
+#define KV_MIN_GRADE       0
+
+#define KV_NORM_GRADE      0       // not queried yet
+#define KV_NORM_COLOR      Qt::black
+#define KV_NORM_TEXT       I18N_NOOP("Not Queried Yet")
+
+#define KV_LEV1_GRADE      1
+#define KV_LEV1_TEXT       I18N_NOOP("Level 1")
+
+#define KV_LEV2_GRADE      2
+#define KV_LEV2_TEXT       I18N_NOOP("Level 2")
+
+#define KV_LEV3_GRADE      3
+#define KV_LEV3_TEXT       I18N_NOOP("Level 3")
+
+#define KV_LEV4_GRADE      4
+#define KV_LEV4_TEXT       I18N_NOOP("Level 4")
+
+#define KV_LEV5_GRADE      5
+#define KV_LEV5_TEXT       I18N_NOOP("Level 5")
+
+#define KV_LEV6_GRADE      6
+#define KV_LEV6_TEXT       I18N_NOOP("Level 6")
+
+#define KV_LEV7_GRADE      7
+#define KV_LEV7_TEXT       I18N_NOOP("Level 7")
+
+#include <time.h>
+#include <vector>
+using namespace std;
+
+#include "grammarmanager.h"
+#include "MultipleChoice.h"
+
+typedef signed char grade_t;
+typedef unsigned short count_t;
+
+/***************************************************************
+  * This class contains one expression as original or in one
+  * translations
+  **************************************************************/
+
+class KEduVocExpression
+{
+ public:
+
+  /** default constructor for an expression in different languages
+   */
+  ~KEduVocExpression ();
+
+  KEduVocExpression ();
+
+  KEduVocExpression (QString &s, QString separator, int lesson = 0);
+
+  /** Constructor for an expression in different languages
+   *
+   * @param expr             expression
+   */
+  KEduVocExpression (QString &expr, int lesson = 0);
+
+  /** adds a new translation of this entry
+   * @param expr             translation
+   * @param grade            grade of knowledge of this translation
+   * @param rev_grade        dito, in opposite direction
+   */
+  void addTranslation (QString expr, grade_t grade=KV_NORM_GRADE,
+                                     grade_t rev_grade=KV_NORM_GRADE);
+
+  /** removes translation
+   *
+   * @param index            number of translation 1..x
+   */
+  void removeTranslation (int index);
+
+  /** returns index of lesson (0 = none)
+   */
+  int getLesson () const;
+
+  /** sets index of lesson (0 = none)
+   */
+  void setLesson (int l);
+
+  /** returns original expression of this entry
+   */
+  QString getOriginal () const;
+
+  /** sets original expression of this entry
+   */
+  void setOriginal (const QString & expr);
+
+  /** returns number of max. translations of all expressions
+   */
+  int numTranslations() const;
+
+  /** returns flag if entry is "selected" for queries
+   */
+  bool isInQuery() const {return inquery; }
+
+  /** set entry "selected"
+   */
+  void setInQuery(bool flag = true) { inquery = flag; }
+
+  /** returns flag if entry is activated for queries
+   */
+  bool isActive() const  {return active; }
+
+  /** set entry active (enabled for queries)
+   */
+  void setActive(bool flag = true) { active = flag; }
+
+  /** returns translation of this expression
+   *
+   * @param index            number of translation
+   * @result                 expression or "" if no translation available
+   */
+  QString getTranslation (int index) const;
+
+  /** sets translation of this expression
+   *
+   * @param index            number of translation
+   * @param expr             expression of this index
+   */
+  void setTranslation (int index, const QString & expr);
+
+  /** sets remark of this expression
+   *
+   * @param index            index of expression
+   * @param expr             remark of this index
+   */
+  void setPronunce (int index, const QString & expr);
+
+  /** returns pronunciation of this expression
+   *
+   * @param index            index of expression
+   * @result                 pronunciation or "" if none available
+   */
+  QString getPronunce (int index) const;
+
+  /** returns remarks of this expression
+   *
+   * @param index            index of expression
+   * @result                 remark or "" if no remark available
+   */
+  QString getRemark (int index) const;
+
+  /** sets remark of this expression
+   *
+   * @param index            index of expression
+   * @param expr             remark of this index
+   */
+  void setRemark (int index, const QString & expr);
+
+
+  /** sets false friend of this expression
+   *
+   * @param index            index of expression
+   * @param expr             false friend of this index
+   * @param rev_grade        dito, in opposite direction
+   */
+  void setFauxAmi (int index, const QString & expr, bool rev_ami = false);
+
+
+  /** returns false friend of this expression
+   *
+   * @param index            index of expression
+   * @param rev_grade        dito, in opposite direction
+   * @result                 false friend or "" if no string available
+   */
+  QString getFauxAmi (int index, bool rev_ami = false) const;
+
+  /** sets synonym this expression
+   *
+   * @param index            index of expression
+   * @param expr             synonym of this index
+   */
+  void setSynonym (int index, const QString & expr);
+
+
+  /** returns synonym of this expression
+   *
+   * @param index            index of expression
+   * @result                 synonym or "" if no string available
+   */
+  QString getSynonym (int index) const;
+
+  /** sets example this expression
+   *
+   * @param index            index of expression
+   * @param expr             example of this index
+   */
+  void setExample (int index, const QString & expr);
+
+
+  /** returns example of this expression
+   *
+   * @param index            index of expression
+   * @result                 example or "" if no string available
+   */
+  QString getExample (int index) const;
+
+  /** sets usage label this expression
+   *
+   * @param index            index of expression
+   * @param usage            usage label of this index
+   */
+  void setUsageLabel (int index, const QString & usage);
+
+
+  /** returns usage label of this expression
+   *
+   * @param index            index of expression
+   * @result                 usage or "" if no string available
+   */
+  QString getUsageLabel (int index) const;
+
+  /** sets paraphrase of this expression
+   *
+   * @param index            index of expression
+   * @param usage            paraphrase of this index
+   */
+  void setParaphrase (int index, const QString & usage);
+
+
+  /** returns paraphrase of this expression
+   *
+   * @param index            index of expression
+   * @result                 paraphrase or "" if no string available
+   */
+  QString getParaphrase (int index) const;
+
+  /** sets antonym this expression
+   *
+   * @param index            index of expression
+   * @param expr             antonym of this index
+   */
+  void setAntonym (int index, const QString & expr);
+
+
+  /** returns antonym of this expression
+   *
+   * @param index            index of expression
+   * @result                 antonym or "" if no string available
+   */
+  QString getAntonym (int index) const;
+
+  /** returns type of this expression
+   *
+   * @result                 type or "" if no type available
+   */
+  QString getType (int index) const;
+
+  /** all langs have same type ?
+   *
+   * @result                 true if all have same type
+   */
+  bool uniqueType () const;
+
+  /** sets type of this expression
+   *
+   * @param index            index of type
+   * @param type             type of this expression ("" = none)
+   */
+  void setType (int index, const QString &type);
+
+  /** returns grade of given translation as string
+   *
+   * @param index            index of expression
+   * @param rev_grade        dito, in opposite direction
+   * @result                 number of knowlegde: 0=known, x=numbers not knows
+   */
+  QString gradeStr (int index, bool rev_grade = false) const;
+
+  /** sets grade of given translation
+   *
+   * @param index            index of translation
+   * @param grade            number of knowlegde: 0=known, x=numbers not knows
+   */
+  void setGrade (int index, grade_t grade, bool rev_grade = false);
+
+  /** returns grade of given translation as int
+   *
+   * @param index            index of translation
+   * @param rev_grade        dito, in opposite direction
+   * @result                 number of knowlegde: 0=known, x=numbers not knows
+   */
+  grade_t getGrade (int index, bool rev_grade = false) const;
+
+  /** increments grade of given translation
+   *
+   * @param index            index of translation
+   * @param rev_grade        dito, in opposite direction
+   */
+  void incGrade (int index, bool rev_grade = false);
+
+  /** decrements grade of given translation
+   *
+   * @param index            index of translation
+   * @param rev_grade        dito, in opposite direction
+   */
+  void decGrade (int index, bool rev_grade = false);
+
+  /** returns last query date of given translation as int
+   *
+   * @param index            index of translation
+   * @param rev_date         dito, in opposite direction
+   */
+  time_t getQueryDate (int index, bool rev_date = false) const;
+
+  /** set last query date of given translation as int
+   *
+   * @param index            index of translation
+   * @param rev_date         dito, in opposite direction
+   */
+  void setQueryDate (int index, time_t date, bool rev_date = false);
+
+  /** returns conjugations if available
+   *
+   * @param index            index of translation
+   */
+  Conjugation getConjugation(int index) const;
+
+  /** sets conjugations
+   *
+   * @param index            index of translation
+   * @param con              conjugation block
+   */
+  void setConjugation(int index, const Conjugation &con);
+
+  /** returns comparison if available
+   *
+   * @param index            index of translation
+   */
+  Comparison getComparison(int index) const;
+
+  /** sets comparison
+   *
+   * @param index            index of translation
+   * @param con              comparison block
+   */
+  void setComparison(int index, const Comparison &comp);
+
+  /** returns multiple choice if available
+   *
+   * @param index            index of multiple choice
+   */
+  MultipleChoice getMultipleChoice(int index) const;
+
+  /** sets multiple choice
+   *
+   * @param index            index of translation
+   * @param con              multiple choice block
+   */
+  void setMultipleChoice(int index, const MultipleChoice &mc);
+
+  /** returns query count of given translation as int
+   *
+   * @param index            index of translation
+   * @param rev_count        dito, in opposite direction
+   */
+  count_t getQueryCount (int index, bool rev_count = false) const;
+
+  /** set query count of given translation as int
+   *
+   * @param index            index of translation
+   * @param rev_count        dito, in opposite direction
+   */
+  void setQueryCount (int index, count_t count, bool rev_count = false);
+
+  /** returns bad query count of given translation as int
+   *
+   * @param index            index of translation
+   * @param rev_count        dito, in opposite direction
+   */
+  count_t getBadCount (int index, bool rev_count = false) const;
+
+  /** set bad query count of given translation as int
+   *
+   * @param index            index of translation
+   * @param rev_count        dito, in opposite direction
+   */
+  void setBadCount (int index, count_t count, bool rev_count = false);
+
+  /** increment bad query count of given translation by 1
+   *
+   * @param index            index of translation
+   * @param rev_count        dito, in opposite direction
+   */
+  void incBadCount (int index, bool rev_count = false);
+
+  /** increment query count of given translation by 1
+   *
+   * @param index            index of translation
+   * @param rev_count        dito, in opposite direction
+   */
+  void incQueryCount (int index, bool rev_count = false);
+
+ protected:
+
+  void Init();
+
+ private:
+  QString            origin;
+
+  // all these vectors must be deleted in removeTranslation()
+  vector<QString>     exprtypes;
+  vector<QString>     translations;
+  vector<QString>     remarks;
+  vector<QString>     usageLabels;
+  vector<QString>     paraphrases;
+  vector<QString>     fauxAmi;
+  vector<QString>     rev_fauxAmi;
+  vector<QString>     synonym;
+  vector<QString>     example;
+  vector<QString>     antonym;
+  vector<QString>     pronunces;
+  vector<grade_t>     grades;
+  vector<grade_t>     rev_grades;
+  vector<count_t>     qcounts;
+  vector<count_t>     rev_qcounts;
+  vector<count_t>     bcounts;
+  vector<count_t>     rev_bcounts;
+  vector<time_t>      qdates;
+  vector<time_t>      rev_qdates;
+  vector<Conjugation> conjugations;
+  vector<Comparison>  comparisons;
+  vector<MultipleChoice> mcs;
+
+  int                lesson;
+  bool               inquery;
+  bool               active;
+};
+
+#endif // KEduVocExpression_H
+
diff --git a/kwordquiz/keduvockvtmlreader.cpp b/kwordquiz/keduvockvtmlreader.cpp
new file mode 100644 (file)
index 0000000..abd35c8
--- /dev/null
@@ -0,0 +1,1692 @@
+/***************************************************************************
+                     read a KEduVocDocument from a KVTML file
+    -----------------------------------------------------------------------
+    copyright            : (C) 1999-2001 Ewald Arnold
+                           (C) 2001 The KDE-EDU team
+                           (C) 2005 Eric Pignet
+    email                : eric at erixpage.com
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ ***************************************************************************/
+
+#include <kapplication.h>
+#include <kdebug.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+
+#include <qdom.h>
+#include <qtextstream.h>
+
+#include "keduvockvtmlreader.h"
+#include "keduvocdocument.h"
+
+KEduVocKvtmlReader::KEduVocKvtmlReader(QFile *file)
+{
+  // the file must be already open
+  m_inputFile = file;
+}
+
+KEduVocKvtmlReader::~KEduVocKvtmlReader()
+{
+}
+
+
+bool KEduVocKvtmlReader::readLesson(QDomElement &domElementParent)
+{
+  QString s;
+  m_doc->lesson_descr.clear();
+
+  //-------------------------------------------------------------------------
+  // Attributes
+  //-------------------------------------------------------------------------
+
+  QDomAttr domAttrWidth = domElementParent.attributeNode(KV_SIZEHINT);
+  if (!domAttrWidth.isNull())
+    m_doc->setSizeHint(-1, domAttrWidth.value().toInt());
+
+  //-------------------------------------------------------------------------
+  // Children
+  //-------------------------------------------------------------------------
+
+  QDomElement domElementChild = domElementParent.firstChild().toElement();
+
+  while (!domElementChild.isNull())
+  {
+    if (domElementChild.tagName() == KV_LESS_DESC)
+    {
+      //-----------
+      // Attributes
+
+      int no = 0;
+      bool isCurr = false;
+
+      QDomAttr domAttrNo = domElementChild.attributeNode(KV_LESS_NO);
+      if (!domAttrNo.isNull())
+        no = domAttrNo.value().toInt();
+
+      QDomAttr domAttrCurrent = domElementChild.attributeNode(KV_LESS_CURR);
+      if (!domAttrCurrent.isNull())
+        isCurr = domAttrCurrent.value().toInt() != 0;
+
+      if (isCurr && no != 0)
+        m_doc->setCurrentLesson(no);
+
+      QDomAttr domAttrQuery = domElementChild.attributeNode(KV_LESS_QUERY);
+      if (!domAttrQuery.isNull())
+        m_doc->lessons_in_query.push_back(domAttrQuery.value().toInt() != 0);
+      else
+        m_doc->lessons_in_query.push_back(false);
+
+      //-----
+      // Text
+
+      s = domElementChild.text();
+      if (s.isNull())
+        s = "";
+      m_doc->lesson_descr.push_back(s);
+    }
+    else
+    {
+      domErrorUnknownElement(domElementChild.tagName());
+      return false;
+    }
+
+    domElementChild = domElementChild.nextSibling().toElement();
+  }
+
+  return true;
+}
+
+
+bool KEduVocKvtmlReader::readArticle(QDomElement &domElementParent)
+/*
+ <article>
+  <e l="de">        lang determines also lang order in entries !!
+   <fi>eine</fi>    which must NOT differ
+   <fd>die</fd>
+   <mi>ein</mi>
+   <md>der</md>
+   <ni>ein</ni>
+   <nd>das</nd>
+  </e>
+ </article>
+*/
+{
+  bool endOfGroup = false;
+  bool inEntry = false;
+  int count = 0;
+  QString s;
+  m_doc->articles.clear();
+
+  QDomElement domElementEntry = domElementParent.firstChild().toElement();
+
+  while (!domElementEntry.isNull())
+  {
+    if (domElementEntry.tagName() != KV_ART_ENTRY)
+    {
+      domError(i18n("expected tag <%1>").arg(KV_ART_ENTRY));
+      return false;
+    }
+
+    //----------
+    // Attribute
+
+    QString lang;
+    QDomAttr domAttrLang = domElementEntry.attributeNode(KV_LANG);
+
+    if ((int)m_doc->langs.size() <= count)
+    {
+      // first entry
+      if (!domAttrLang.isNull())         // no definition in first entry
+        lang = domAttrLang.value();
+      else
+        lang = "original";
+      m_doc->langs.push_back(lang);
+    }
+    else
+    {
+      if (!domAttrLang.isNull() && domAttrLang.value() != m_doc->langs[count])
+      {
+        // different originals ?
+        domError(i18n("ambiguous definition of language code"));
+        return false;
+      }
+    }
+
+    //---------
+    // Children
+
+    QString fem_def = "";
+    QString  mal_def = "";
+    QString  nat_def = "";
+    QString  fem_indef = "";
+    QString  mal_indef = "";
+    QString  nat_indef = "";
+    
+    QDomElement domElementEntryChild = domElementEntry.firstChild().toElement();
+    while (!domElementEntryChild.isNull())
+    {
+      if (domElementEntryChild.tagName() == KV_ART_FD)
+      {
+        fem_def = domElementEntryChild.text();
+        if (fem_def.isNull())
+          fem_def = "";
+      }
+      else if (domElementEntryChild.tagName() == KV_ART_FI)
+      {
+        fem_indef = domElementEntryChild.text();
+        if (fem_indef.isNull())
+          fem_indef = "";
+      }
+      else if (domElementEntryChild.tagName() == KV_ART_MD)
+      {
+        mal_def = domElementEntryChild.text();
+        if (mal_def.isNull())
+          mal_def = "";
+      }
+      else if (domElementEntryChild.tagName() == KV_ART_MI)
+      {
+        mal_indef = domElementEntryChild.text();
+        if (mal_indef.isNull())
+          mal_indef = "";
+      }
+      else if (domElementEntryChild.tagName() == KV_ART_ND)
+      {
+        nat_def = domElementEntryChild.text();
+        if (nat_def.isNull())
+          nat_def = "";
+      }
+      else if (domElementEntryChild.tagName() == KV_ART_NI)
+      {
+        nat_indef = domElementEntryChild.text();
+        if (nat_indef.isNull())
+          nat_indef = "";
+      }
+      else
+      {
+        domErrorUnknownElement(domElementEntryChild.tagName());
+        return false;
+      }
+      
+      domElementEntryChild = domElementEntryChild.nextSibling().toElement();
+    }
+
+    m_doc->articles.push_back(Article(  fem_def, fem_indef,
+                                        mal_def, mal_indef,
+                                        nat_def, nat_indef));
+
+    domElementEntry = domElementEntry.nextSibling().toElement();
+    count++;
+  }
+
+  return true;
+}
+
+
+bool KEduVocKvtmlReader::readConjug(QDomElement &domElementParent,
+                                    vector<Conjugation> &curr_conjug,
+                                    const QString &entry_tag)
+/*
+ <conjugation>        used in header for definiton of "prefix"
+  <e l="de">          lang determines also lang order in entries !!
+   <s1>I</s1>         which must NOT differ
+   <s2>you<2>
+   <s3f>he</s3f>
+   <s3m>she</s3m>
+   <s3n>it</s3n>
+   <p1>we</p1>
+   <p2>you</p2>
+   <p3f>they</p3f>
+   <p3m>they</p3m>
+   <p3n>they</p3n>
+  </e>
+ </conjugation>
+
+ <conjugation>        and in entry for definition of tenses of (irreg.) verbs
+  <t n="sipa">
+   <s1>go</s1>
+   <s2>go</s2>
+   <s3f>goes</s3f>
+   <s3m>goes</s3m>
+   <s3n>goes</s3n>
+   <p1>go</p1>
+   <p2>go</p2>
+   <p3f>go</p3f>
+   <p3m>go</p3m>
+   <p3n>go</p3n>
+  </t>
+ </conjugation>
+*/
+{
+  QString s;
+
+  curr_conjug.clear();
+
+  bool    p3_common,
+      s3_common;
+  QString pers1_sing,
+      pers2_sing,
+      pers3_m_sing,
+      pers3_f_sing,
+      pers3_n_sing,
+      pers1_plur,
+      pers2_plur,
+      pers3_m_plur,
+      pers3_f_plur,
+      pers3_n_plur;
+
+  QString lang;
+  QString type;
+  int count = 0;
+  curr_conjug.push_back(Conjugation());
+
+  QDomElement domElementConjugChild = domElementParent.firstChild().toElement();
+  while (!domElementConjugChild.isNull())
+  {
+    if (domElementConjugChild.tagName() == KV_CON_ENTRY)
+    {
+      type = CONJ_PREFIX;
+
+      //----------
+      // Attribute
+
+      QString lang;
+      QDomAttr domAttrLang = domElementConjugChild.attributeNode(KV_LANG);
+
+      if ((int)m_doc->langs.size() <= count)
+      {
+        // first entry
+        if (!domAttrLang.isNull())            // no definition in first entry
+          lang = domAttrLang.value();
+        else
+          lang = "original";
+        m_doc->langs.push_back(lang);
+      }
+      else
+      {
+        if (!domAttrLang.isNull() && domAttrLang.value() != m_doc->langs[count])
+        {
+          // different originals ?
+          domError(i18n("ambiguous definition of language code"));
+          return false;
+        }
+      }
+    }
+    else if (domElementConjugChild.tagName() == KV_CON_TYPE)
+    {
+      //----------
+      // Attribute
+
+      QDomAttr domAttrLang = domElementConjugChild.attributeNode(KV_CON_NAME);
+      type = domAttrLang.value();
+      if (type.isNull())
+        type = "";
+
+      if (type.length() != 0 && type.left(1) == UL_USER_TENSE)
+      {
+        int num = QMIN(type.mid (1, 40).toInt(), 1000); // paranoia check
+        if( num > (int) m_doc->tense_descr.size() )
+        {
+          // description missing ?
+          QString s;
+          for (int i = m_doc->tense_descr.size(); i < num; i++)
+          {
+            s.setNum (i+1);
+            s.insert (0, "#");  // invent descr according to number
+            m_doc->tense_descr.push_back(s);
+          }
+        }
+      }
+    }
+
+    pers1_sing = "";
+    pers2_sing = "";
+    pers3_m_sing = "";
+    pers3_f_sing = "";
+    pers3_n_sing = "";
+    pers1_plur = "";
+    pers2_plur = "";
+    pers3_m_plur = "";
+    pers3_f_plur = "";
+    pers3_n_plur = "";
+    p3_common = false;
+    s3_common = false;
+
+    QDomElement domElementConjugGrandChild = domElementConjugChild.firstChild().toElement();
+    while (!domElementConjugGrandChild.isNull())
+    {
+      if (domElementConjugGrandChild.tagName() == KV_CON_P1S)
+      {
+        pers1_sing = domElementConjugGrandChild.text();
+        if (pers1_sing.isNull())
+          pers1_sing = "";
+      }
+      else if (domElementConjugGrandChild.tagName() == KV_CON_P2S)
+      {
+        pers2_sing = domElementConjugGrandChild.text();
+        if (pers2_sing.isNull())
+          pers2_sing = "";
+      }
+      else if (domElementConjugGrandChild.tagName() == KV_CON_P3SF)
+      {
+        QDomAttr domAttrCommon = domElementConjugGrandChild.attributeNode(KV_CONJ_COMMON);
+        if (!domAttrCommon.isNull())
+          s3_common = domAttrCommon.value().toInt();  // returns 0 if the conversion fails
+
+        pers3_f_sing = domElementConjugGrandChild.text();
+        if (pers3_f_sing.isNull())
+          pers3_f_sing = "";
+      }
+      else if (domElementConjugGrandChild.tagName() == KV_CON_P3SM)
+      {
+        pers3_m_sing = domElementConjugGrandChild.text();
+        if (pers3_m_sing.isNull())
+          pers3_m_sing = "";
+      }
+      else if (domElementConjugGrandChild.tagName() == KV_CON_P3SN)
+      {
+        pers3_n_sing = domElementConjugGrandChild.text();
+        if (pers3_n_sing.isNull())
+          pers3_n_sing = "";
+      }
+      else if (domElementConjugGrandChild.tagName() == KV_CON_P1P)
+      {
+        pers1_plur = domElementConjugGrandChild.text();
+        if (pers1_plur.isNull())
+          pers1_plur = "";
+      }
+      else if (domElementConjugGrandChild.tagName() == KV_CON_P2P)
+      {
+        pers2_plur = domElementConjugGrandChild.text();
+        if (pers2_plur.isNull())
+          pers2_plur = "";
+      }
+      else if (domElementConjugGrandChild.tagName() == KV_CON_P3PF)
+      {
+        QDomAttr domAttrCommon = domElementConjugGrandChild.attributeNode(KV_CONJ_COMMON);
+        if (!domAttrCommon.isNull())
+          p3_common = domAttrCommon.value().toInt();  // returns 0 if the conversion fails
+
+        pers3_f_plur = domElementConjugGrandChild.text();
+        if (pers3_f_plur.isNull())
+          pers3_f_plur = "";
+      }
+      else if (domElementConjugGrandChild.tagName() == KV_CON_P3PM)
+      {
+        pers3_m_plur = domElementConjugGrandChild.text();
+        if (pers3_m_plur.isNull())
+          pers3_m_plur = "";
+      }
+      else if (domElementConjugGrandChild.tagName() == KV_CON_P3PN)
+      {
+        pers3_n_plur = domElementConjugGrandChild.text();
+        if (pers3_n_plur.isNull())
+          pers3_n_plur = "";
+      }
+      else
+      {
+        domErrorUnknownElement(domElementConjugGrandChild.tagName());
+        return false;
+      }
+
+      domElementConjugGrandChild = domElementConjugGrandChild.nextSibling().toElement();
+    }
+
+    if (domElementConjugChild.tagName() == KV_CON_ENTRY)
+      while (count+1 > (int) curr_conjug.size() )
+        curr_conjug.push_back(Conjugation());
+
+    curr_conjug[count].setPers3SingularCommon(type, s3_common);
+    curr_conjug[count].setPers3PluralCommon(type, p3_common);
+    curr_conjug[count].setPers1Singular(type, pers1_sing);
+    curr_conjug[count].setPers2Singular(type, pers2_sing);
+    curr_conjug[count].setPers3FemaleSingular(type, pers3_f_sing);
+    curr_conjug[count].setPers3MaleSingular(type, pers3_m_sing);
+    curr_conjug[count].setPers3NaturalSingular(type, pers3_n_sing);
+    curr_conjug[count].setPers1Plural(type, pers1_plur);
+    curr_conjug[count].setPers2Plural(type, pers2_plur);
+    curr_conjug[count].setPers3FemalePlural(type, pers3_f_plur);
+    curr_conjug[count].setPers3MalePlural(type, pers3_m_plur);
+    curr_conjug[count].setPers3NaturalPlural(type, pers3_n_plur);
+
+    if (domElementConjugChild.tagName() == KV_CON_ENTRY)
+      count++;
+
+    domElementConjugChild = domElementConjugChild.nextSibling().toElement();
+  }
+
+  return true;
+}
+
+
+bool KEduVocKvtmlReader::readOptions(QDomElement &domElementParent)
+{
+  QDomElement domElementSort = domElementParent.firstChild().toElement();
+  while (!domElementSort.isNull())
+  {
+    if (domElementSort.tagName() == KV_OPT_SORT)
+    {
+      m_doc->sort_allowed = true;
+      QDomAttr domAttrOn = domElementSort.attributeNode(KV_BOOL_FLAG);
+      if (!domAttrOn.isNull())
+            {         
+                bool ok = true;         
+        m_doc->sort_allowed = domAttrOn.value().toInt(&ok);  // returns 0 if the conversion fails
+          if (!ok)
+              m_doc->sort_allowed = true;
+            }            
+    }
+    
+    domElementSort = domElementSort.nextSibling().toElement();
+  }
+
+  return true;
+}
+
+
+bool KEduVocKvtmlReader::readType(QDomElement &domElementParent)
+{
+  QString s;
+  m_doc->type_descr.clear();
+
+  QDomElement domElementDesc = domElementParent.firstChild().toElement();
+
+  while (!domElementDesc.isNull())
+  {
+    if (domElementDesc.tagName() == KV_TYPE_DESC)
+    {
+      //-----------
+      // Attributes
+
+      int no = 0;
+      bool isCurr = false;
+
+      QDomAttr domAttrNo = domElementDesc.attributeNode(KV_TYPE_NO);
+      if (!domAttrNo.isNull())
+        no = domAttrNo.value().toInt();
+
+      // TODO use 'no' to sort types
+      // but 'no' seems useless, since types are already ordered by their position in the XML doc
+
+      s = domElementDesc.text();
+      if (s.isNull())
+        s = "";
+
+      m_doc->type_descr.push_back (s);
+    }
+    else
+    {
+      domErrorUnknownElement(domElementDesc.tagName());
+      return false;
+    }
+
+    domElementDesc = domElementDesc.nextSibling().toElement();
+  }
+
+  return true;
+}
+
+
+bool KEduVocKvtmlReader::readTense(QDomElement &domElementParent)
+{
+  QString s;
+  m_doc->tense_descr.clear();
+
+  QDomElement domElementDesc = domElementParent.firstChild().toElement();
+
+  while (!domElementDesc.isNull())
+  {
+    if (domElementDesc.tagName() == KV_TENSE_DESC)
+    {
+      //-----------
+      // Attributes
+
+      int no = 0;
+      bool isCurr = false;
+
+      QDomAttr domAttrNo = domElementDesc.attributeNode(KV_TENSE_NO);
+      if (!domAttrNo.isNull())
+        no = domAttrNo.value().toInt();
+
+      // TODO use 'no' to sort tenses
+      // but 'no' seems useless, since tenses are already ordered by their position in the XML doc
+
+      s = domElementDesc.text();
+      if (s.isNull())
+        s = "";
+
+      m_doc->tense_descr.push_back (s);
+    }
+    else
+    {
+      domErrorUnknownElement(domElementDesc.tagName());
+      return false;
+    }
+
+    domElementDesc = domElementDesc.nextSibling().toElement();
+  }
+
+  return true;
+}
+
+
+bool KEduVocKvtmlReader::readUsage(QDomElement &domElementParent)
+{
+  QString s;
+  m_doc->usage_descr.clear();
+
+  QDomElement domElementDesc = domElementParent.firstChild().toElement();
+
+  while (!domElementDesc.isNull())
+  {
+    if (domElementDesc.tagName() == KV_USAGE_DESC)
+    {
+      //-----------
+      // Attributes
+
+      int no = 0;
+      bool isCurr = false;
+
+      QDomAttr domAttrNo = domElementDesc.attributeNode(KV_USAGE_NO);
+      if (!domAttrNo.isNull())
+        no = domAttrNo.value().toInt();
+
+      // TODO use 'no' to sort usages
+      // but 'no' seems useless, since usages are already ordered by their position in the XML doc
+
+      s = domElementDesc.text();
+      if (s.isNull())
+        s = "";
+
+      m_doc->usage_descr.push_back (s);
+    }
+    else
+    {
+      domErrorUnknownElement(domElementDesc.tagName());
+      return false;
+    }
+
+    domElementDesc = domElementDesc.nextSibling().toElement();
+  }
+
+  return true;
+}
+
+
+bool KEduVocKvtmlReader::readComparison(QDomElement &domElementParent,
+                                        Comparison &comp)
+/*
+ <comparison>
+   <l1>good</l1>
+   <l2>better</l2>
+   <l3>best</l3>
+ </comparison>
+*/
+{
+  QString s;
+  comp.clear();
+  
+  QDomElement domElementComparisonChild = domElementParent.firstChild().toElement();
+  while (!domElementComparisonChild.isNull())
+  {
+    if (domElementComparisonChild.tagName() == KV_COMP_L1)
+    {
+      s = domElementComparisonChild.text();
+      if (s.isNull())
+        s = "";
+      comp.setL1(s);
+    }
+    
+    else if (domElementComparisonChild.tagName() == KV_COMP_L2)
+    {
+      s = domElementComparisonChild.text();
+      if (s.isNull())
+        s = "";
+      comp.setL2(s);
+    }
+    
+    else if (domElementComparisonChild.tagName() == KV_COMP_L3)
+    {
+      s = domElementComparisonChild.text();
+      if (s.isNull())
+        s = "";
+      comp.setL3(s);
+    }
+    
+    else
+    {
+      domErrorUnknownElement(domElementComparisonChild.tagName());
+      return false;
+    }
+    
+    domElementComparisonChild = domElementComparisonChild.nextSibling().toElement();
+  }
+  
+  return true;
+}
+
+
+bool KEduVocKvtmlReader::readMultipleChoice(QDomElement &domElementParent,
+                                            MultipleChoice &mc)
+/*
+ <multiplechoice>
+   <mc1>good</mc1>
+   <mc2>better</mc2>
+   <mc3>best</mc3>
+   <mc4>best 2</mc4>
+   <mc5>best 3</mc5>
+ </multiplechoice>
+*/
+
+{
+  QString s;
+  mc.clear();
+  
+  QDomElement domElementChild = domElementParent.firstChild().toElement();
+  while (!domElementChild.isNull())
+  {
+    if (domElementChild.tagName() == KV_MC_1)
+    {
+      s = domElementChild.text();
+      if (s.isNull())
+        s = "";
+      mc.setMC1(s);
+    }
+    
+    else if (domElementChild.tagName() == KV_MC_2)
+    {
+      s = domElementChild.text();
+      if (s.isNull())
+        s = "";
+      mc.setMC2(s);
+    }
+    
+    else if (domElementChild.tagName() == KV_MC_3)
+    {
+      s = domElementChild.text();
+      if (s.isNull())
+        s = "";
+      mc.setMC3(s);
+    }
+    
+    else if (domElementChild.tagName() == KV_MC_4)
+    {
+      s = domElementChild.text();
+      if (s.isNull())
+        s = "";
+      mc.setMC4(s);
+    }
+    
+    else if (domElementChild.tagName() == KV_MC_5)
+    {
+      s = domElementChild.text();
+      if (s.isNull())
+        s = "";
+      mc.setMC5(s);
+    }
+    
+    else
+    {
+      domErrorUnknownElement(domElementChild.tagName());
+      return false;
+    }
+    
+    domElementChild = domElementChild.nextSibling().toElement();
+  }
+    
+  mc.normalize();
+  return true;
+}
+
+
+bool KEduVocKvtmlReader::readExpressionChildAttributes( QDomElement &domElementExpressionChild,
+                                                        QString &lang,
+                                                        grade_t &grade, grade_t &rev_grade,
+                                                        int &count, int &rev_count,
+                                                        time_t &date, time_t &rev_date,
+                                                        QString &remark,
+                                                        int &bcount, int &rev_bcount,
+                                                        QString &query_id,
+                                                        QString &pronunce,
+                                                        int &width,
+                                                        QString &type,
+                                                        QString &faux_ami_f,
+                                                        QString &faux_ami_t,
+                                                        QString &synonym,
+                                                        QString &example,
+                                                        QString &antonym,
+                                                        QString &usage,
+                                                        QString &paraphrase)
+{
+  int pos;
+  
+  lang = "";
+  QDomAttr domAttrLang = domElementExpressionChild.attributeNode(KV_LANG);
+  if (!domAttrLang.isNull())
+    lang = domAttrLang.value();
+  
+  width = -1;
+  QDomAttr domAttrWidth = domElementExpressionChild.attributeNode(KV_SIZEHINT);
+  if (!domAttrWidth.isNull())
+    width = domAttrWidth.value().toInt();
+  
+  grade = KV_NORM_GRADE;
+  rev_grade = KV_NORM_GRADE;
+  QDomAttr domAttrGrade = domElementExpressionChild.attributeNode(KV_LANG);
+  if (!domAttrGrade.isNull())
+  {
+    QString s = domAttrGrade.value();
+    if ((pos = s.find(';')) >= 1)
+    {
+      grade = s.left(pos).toInt();
+      rev_grade = s.mid(pos+1, s.length()).toInt();
+    }
+    else
+      grade = s.toInt();
+  }
+
+  count = 0;
+  rev_count = 0;
+  QDomAttr domAttrCount = domElementExpressionChild.attributeNode(KV_COUNT);
+  if (!domAttrCount.isNull())
+  {
+    QString s = domAttrCount.value();
+    if ((pos = s.find(';')) >= 1)
+    {
+      count = s.left(pos).toInt();
+      rev_count = s.mid(pos+1, s.length()).toInt();
+    }
+    else
+      count = s.toInt();
+  }
+
+  bcount = 0;
+  rev_bcount = 0;
+  QDomAttr domAttrBad = domElementExpressionChild.attributeNode(KV_BAD);
+  if (!domAttrBad.isNull())
+  {
+    QString s = domAttrBad.value();
+    if ((pos = s.find(';')) >= 1)
+    {
+      bcount = s.left(pos).toInt();
+      rev_bcount = s.mid(pos+1, s.length()).toInt();
+    }
+    else
+      bcount = s.toInt();
+  }
+
+  date = 0;
+  rev_date = 0;
+  QDomAttr domAttrDate = domElementExpressionChild.attributeNode(KV_DATE);
+  if (!domAttrDate.isNull())
+  {
+    QString s = domAttrDate.value();
+    if ((pos = s.find(';')) >= 1)
+    {
+      date = s.left(pos).toInt();
+      rev_date = s.mid(pos+1, s.length()).toInt();
+    }
+    else
+      date = s.toInt();
+  }
+
+  QDomAttr domAttrDate2 = domElementExpressionChild.attributeNode(KV_DATE2);
+  if (!domAttrDate2.isNull())
+  {
+    QString s = domAttrDate2.value();
+    if ((pos = s.find(';')) >= 1)
+    {
+      date = m_doc->decompressDate(s.left(pos));
+      rev_date = m_doc->decompressDate(s.mid(pos+1, s.length()));
+    }
+    else
+      date = m_doc->decompressDate(s);
+  }
+
+  remark = "";
+  QDomAttr domAttrRemark = domElementExpressionChild.attributeNode(KV_REMARK);
+  if (!domAttrRemark.isNull())
+    remark = domAttrRemark.value();
+  
+  faux_ami_f = "";
+  QDomAttr domAttrFauxAmiF = domElementExpressionChild.attributeNode(KV_FAUX_AMI_F);
+  if (!domAttrFauxAmiF.isNull())
+    faux_ami_f = domAttrFauxAmiF.value();
+  
+  faux_ami_t = "";
+  QDomAttr domAttrFauxAmiT = domElementExpressionChild.attributeNode(KV_FAUX_AMI_T);
+  if (!domAttrFauxAmiT.isNull())
+    faux_ami_t = domAttrFauxAmiT.value();
+  
+  synonym = "";
+  QDomAttr domAttrSynonym = domElementExpressionChild.attributeNode(KV_SYNONYM);
+  if (!domAttrSynonym.isNull())
+    synonym = domAttrSynonym.value();
+  
+  example = "";
+  QDomAttr domAttrExample = domElementExpressionChild.attributeNode(KV_EXAMPLE);
+  if (!domAttrExample.isNull())
+    example = domAttrExample.value();
+  
+  usage = "";
+  QDomAttr domAttrUsage = domElementExpressionChild.attributeNode(KV_USAGE);
+  if (!domAttrUsage.isNull())
+  {
+    usage = domAttrUsage.value();
+    if (usage.length() != 0 && usage.left(1) == UL_USER_USAGE)
+    {
+      int num = QMIN(usage.mid (1, 40).toInt(), 1000); // paranioa check
+      if( num > (int) m_doc->usage_descr.size() )
+      {
+        // description missing ?
+        QString s;
+        for (int i = m_doc->usage_descr.size(); i < num; i++)
+        {
+          s.setNum (i+1);
+          s.insert (0, "#");  // invent descr according to number
+          m_doc->usage_descr.push_back (s);
+        }
+      }
+    }
+  }
+
+  paraphrase = "";
+  QDomAttr domAttrParaphrase = domElementExpressionChild.attributeNode(KV_PARAPHRASE);
+  if (!domAttrParaphrase.isNull())
+    paraphrase = domAttrParaphrase.value();
+  
+  antonym = "";
+  QDomAttr domAttrAntonym = domElementExpressionChild.attributeNode(KV_ANTONYM);
+  if (!domAttrAntonym.isNull())
+    antonym = domAttrAntonym.value();
+
+  QDomAttr domAttrExprType = domElementExpressionChild.attributeNode(KV_EXPRTYPE);
+  if (!domAttrExprType.isNull())
+  {
+    type = domAttrExprType.value();
+    if (type == "1")
+      type = QM_VERB;
+    else if (type == "2")  // convert from pre-0.5 versions
+      type = QM_NOUN;
+    else if (type == "3")
+      type = QM_NAME;
+
+    if (type.length() != 0 && type.left(1) == QM_USER_TYPE)
+    {
+      int num = QMIN(type.mid (1, 40).toInt(), 1000); // paranoia check
+      if( num > (int) m_doc->type_descr.size() )
+      {
+        // description missing ?
+        QString s;
+        for (int i = m_doc->type_descr.size(); i < num; i++)
+        {
+          s.setNum (i+1);
+          s.insert (0, "#");  // invent descr according to number
+          m_doc->type_descr.push_back (s);
+        }            
+      }
+    }
+  }
+
+  pronunce = "";
+  QDomAttr domAttrPronunce = domElementExpressionChild.attributeNode(KV_PRONUNCE);
+  if (!domAttrPronunce.isNull())
+    pronunce = domAttrPronunce.value();
+  
+  query_id = "";
+  QDomAttr domAttrQuery = domElementExpressionChild.attributeNode(KV_QUERY);
+  if (!domAttrQuery.isNull())
+    query_id = domAttrQuery.value();
+  
+  return true;
+}
+
+
+bool KEduVocKvtmlReader::readExpression(QDomElement &domElementParent)
+{
+  grade_t       grade,
+                r_grade;
+  int           qcount,
+                r_qcount;
+  int           bcount,
+                 r_bcount;
+  QString       remark;
+  QString       pronunce;
+  time_t        qdate,
+                r_qdate;
+  bool          inquery;
+  bool          active;
+  QString       lang;
+  QString       textstr;
+  QString       exprtype;
+  bool          org_found = false;
+  QString       q_org,
+                q_trans;
+  QString       query_id;
+  KEduVocExpression expr;
+  int           lesson;
+  int           width;
+  QString       type;
+  QString       faux_ami_f;
+  QString       faux_ami_t;
+  QString       synonym;
+  QString       example;
+  QString       antonym;
+  QString       usage;
+  QString       paraphrase;
+  vector<Conjugation> conjug;
+  Comparison     comparison;
+  MultipleChoice mc;
+
+  //-------------------------------------------------------------------------
+  // Attributes
+  //-------------------------------------------------------------------------
+
+  QDomAttr domAttrMember = domElementParent.attributeNode(KV_LESS_MEMBER);
+  if (!domAttrMember.isNull())
+    lesson = domAttrMember.value().toInt();
+
+  QDomAttr domAttrSelected = domElementParent.attributeNode(KV_SELECTED);
+  if (!domAttrSelected.isNull())
+    inquery = domAttrSelected.value().toInt();
+
+  QDomAttr domAttrInactive = domElementParent.attributeNode(KV_INACTIVE);
+  if (!domAttrInactive.isNull())
+    active = !domAttrInactive.value().toInt();
+
+  QDomAttr domAttrType = domElementParent.attributeNode(KV_EXPRTYPE);
+  if (!domAttrType.isNull())
+  {
+    exprtype = !domAttrType.value().toInt();
+    if (exprtype == "1")
+      exprtype = QM_VERB;
+    else if (exprtype == "2")  // convert from pre-0.5 versions
+      exprtype = QM_NOUN;
+    else if (exprtype == "3")
+      exprtype = QM_NAME;
+
+    if (exprtype.length() != 0 && exprtype.left(1) == QM_USER_TYPE)
+    {
+      int num = QMIN(exprtype.mid (1, 40).toInt(), 1000); // paranoia check
+      if( num > (int) m_doc->type_descr.size() )
+      {
+        // description missing ?
+        QString s;
+        for (int i = m_doc->type_descr.size(); i < num; i++)
+        {
+          s.setNum (i+1);
+          s.insert (0, "#");  // invent descr according to number
+          m_doc->type_descr.push_back (s);
+        }
+      }
+    }
+  }
+
+  if (lesson && lesson > (int) m_doc->lesson_descr.size() )
+  {
+    // description missing ?
+    QString s;
+    for (int i = m_doc->lesson_descr.size(); i < lesson; i++)
+    {
+      s.setNum (i+1);
+      s.insert (0, "#");  // invent descr according to number
+      m_doc->lesson_descr.push_back (s);
+    }
+  }
+
+  //-------------------------------------------------------------------------
+  // Child 'Original'
+  //-------------------------------------------------------------------------
+
+  // now want "original" and one or more "translations"
+
+  QDomElement domElementExpressionChild = domElementParent.firstChild().toElement();
+
+  unsigned int count = 0;
+  org_found = false;
+
+  if (domElementExpressionChild.tagName() != KV_ORG)
+  {
+    // must be preceded by "original"
+    domError(i18n("starting tag <%1> is missing").arg(KV_ORG));
+    return false;
+  }
+
+  // found original <o>
+
+  org_found = true;
+
+  type = exprtype;
+
+  //-----------
+  // Attributes
+
+    if (!readExpressionChildAttributes( domElementExpressionChild,
+                                        lang,
+                                        grade,  r_grade,
+                                        qcount, r_qcount,
+                                        qdate,  r_qdate,
+                                        remark,
+                                        bcount, r_bcount,
+                                        query_id,
+                                        pronunce,
+                                        width,
+                                        type,
+                                        faux_ami_t,
+                                        faux_ami_f,
+                                        synonym,
+                                        example,
+                                        antonym,
+                                        usage,
+                                        paraphrase))
+    return false;
+
+
+  if (m_doc->vocabulary.size() == 0)
+  {
+    // only accept in first entry
+    if (width >= 0)
+      m_doc->setSizeHint (count, width);
+
+    if (query_id == KV_O)
+      q_org = lang;
+
+    if (query_id == KV_T)
+      q_trans = lang;
+  }
+
+  if (m_doc->langs.size() == 0)
+  {
+    // first entry
+    if (lang.isEmpty())                 // no definition in first entry
+      lang = "original";
+    m_doc->langs.push_back(lang);
+
+  }
+  else
+  {
+    if (lang != m_doc->langs[0] && !lang.isEmpty())
+    {
+      // different originals ?
+      domError(i18n("ambiguous definition of language code"));
+      return false;
+    }
+  }
+  count = 0;
+
+  //---------
+  // Children
+
+  bool bConjug = false;
+  bool bComparison = false;
+  bool bMultipleChoice = false;
+
+  QDomElement domElementOriginalChild = domElementExpressionChild.firstChild().toElement();
+  while (!domElementOriginalChild.isNull())
+  {
+    if (domElementOriginalChild.tagName() == KV_CONJUG_GRP)
+    {
+      if (bConjug)
+      {
+        domError(i18n("repeated occurrence of tag <%1>").arg(domElementOriginalChild.tagName()));
+        return false;
+      }
+      bConjug = true;
+      conjug.clear();
+      if (!readConjug(domElementOriginalChild, conjug, (QString) KV_CON_TYPE))
+        return false;
+    }
+
+    if (domElementOriginalChild.tagName() == KV_COMPARISON_GRP)
+    {
+      if (bComparison)
+      {
+        domError(i18n("repeated occurrence of tag <%1>").arg(domElementOriginalChild.tagName()));
+        return false;
+      }
+      bComparison = true;
+      comparison.clear();
+      if (!readComparison(domElementOriginalChild, comparison))
+        return false;
+    }
+
+    if (domElementOriginalChild.tagName() == KV_MULTIPLECHOICE_GRP)
+    {
+      if (bMultipleChoice)
+      {
+        domError(i18n("repeated occurrence of tag <%1>").arg(domElementOriginalChild.tagName()));
+        return false;
+      }
+      bMultipleChoice = true;
+      mc.clear();
+      if (!readMultipleChoice(domElementOriginalChild, mc))
+        return false;
+    }
+
+    else
+    {
+      domErrorUnknownElement(domElementOriginalChild.tagName());
+      return false;
+    }
+    
+    domElementOriginalChild = domElementOriginalChild.nextSibling().toElement();
+  }
+
+  textstr = domElementExpressionChild.text();
+  if (textstr.isNull())
+    textstr = "";
+
+  expr = KEduVocExpression(textstr);
+  expr.setLesson (lesson);
+  expr.setInQuery(inquery);
+  expr.setActive(active);
+
+  if (conjug.size() > 0)
+  {
+    expr.setConjugation(0, conjug[0]);
+    conjug.clear();
+  }
+  if (!comparison.isEmpty())
+  {
+    expr.setComparison(0, comparison);
+    comparison.clear();
+  }
+  if (!mc.isEmpty())
+  {
+    expr.setMultipleChoice(0, mc);
+    mc.clear();
+  }
+  if (!remark.isEmpty() )
+    expr.setRemark (0, remark);
+  if (!pronunce.isEmpty() )
+    expr.setPronunce (0, pronunce);
+  if (!type.isEmpty() )
+    expr.setType(0, type);
+  if (!synonym.isEmpty() )
+    expr.setSynonym(0, synonym);
+  if (!example.isEmpty() )
+    expr.setExample(0, example);
+  if (!usage.isEmpty() )
+    expr.setUsageLabel(0, usage);
+  if (!paraphrase.isEmpty() )
+    expr.setParaphrase(0, paraphrase);
+  if (!antonym.isEmpty() )
+    expr.setAntonym(0, antonym);
+
+
+  //-------------------------------------------------------------------------
+  // Children 'Translation'
+  //-------------------------------------------------------------------------
+
+  domElementExpressionChild = domElementExpressionChild.nextSibling().toElement();
+
+  while (!domElementExpressionChild.isNull())
+  {
+    if (domElementExpressionChild.tagName() != KV_TRANS)
+    {
+      // "original" must be followed by "translations"
+      domError(i18n("starting tag <%1> is missing").arg(KV_TRANS));
+      return false;
+    }
+  
+    // found translation <t>
+
+    count++;
+    type = exprtype;
+
+    //-----------
+    // Attributes
+
+    if (!readExpressionChildAttributes( domElementExpressionChild,
+                                        lang,
+                                        grade,  r_grade,
+                                        qcount, r_qcount,
+                                        qdate,  r_qdate,
+                                        remark,
+                                        bcount, r_bcount,
+                                        query_id,
+                                        pronunce,
+                                        width,
+                                        type,
+                                        faux_ami_f,
+                                        faux_ami_t,
+                                        synonym,
+                                        example,
+                                        antonym,
+                                        usage,
+                                        paraphrase))
+      return false;
+
+    if (m_doc->vocabulary.size() == 0)
+    {
+      // only accept in first entry
+      if (width >= 0)
+        m_doc->setSizeHint (count, width);
+
+      if (query_id == KV_O)
+        q_org = lang;
+
+      if (query_id == KV_T)
+        q_trans = lang;
+
+    }
+
+    if (m_doc->langs.size() <= count)
+    {
+      // new translation
+      if (lang.isEmpty())
+      {
+        // no definition in first entry ?
+        lang.setNum (m_doc->langs.size() );
+        lang.insert (0, "translation ");
+      }
+      m_doc->langs.push_back(lang);
+
+    }
+    else
+    {
+      if (lang != m_doc->langs[count] && !lang.isEmpty())
+      { // different language ?
+        domError(i18n("ambiguous definition of language code"));
+        return false;
+      }
+    }
+
+    //---------
+    // Children
+
+    bool bConjug = false;
+    bool bComparison = false;
+    bool bMultipleChoice = false;
+
+    QDomElement domElementOriginalChild = domElementExpressionChild.firstChild().toElement();
+    while (!domElementOriginalChild.isNull())
+    {
+      if (domElementOriginalChild.tagName() == KV_CONJUG_GRP)
+      {
+        if (bConjug)
+        {
+          domError(i18n("repeated occurrence of tag <%1>").arg(domElementOriginalChild.tagName()));
+          return false;
+        }
+        bConjug = true;
+        conjug.clear();
+        if (!readConjug(domElementOriginalChild, conjug, (QString) KV_CON_TYPE))
+          return false;
+      }
+  
+      if (domElementOriginalChild.tagName() == KV_COMPARISON_GRP)
+      {
+        if (bComparison)
+        {
+          domError(i18n("repeated occurrence of tag <%1>").arg(domElementOriginalChild.tagName()));
+          return false;
+        }
+        bComparison = true;
+        comparison.clear();
+        if (!readComparison(domElementOriginalChild, comparison))
+          return false;
+      }
+  
+      if (domElementOriginalChild.tagName() == KV_MULTIPLECHOICE_GRP)
+      {
+        if (bMultipleChoice)
+        {
+          domError(i18n("repeated occurrence of tag <%1>").arg(domElementOriginalChild.tagName()));
+          return false;
+        }
+        bMultipleChoice = true;
+        mc.clear();
+        if (!readMultipleChoice(domElementOriginalChild, mc))
+          return false;
+      }
+  
+      else
+      {
+        domErrorUnknownElement(domElementOriginalChild.tagName());
+        return false;
+      }
+      
+      domElementOriginalChild = domElementOriginalChild.nextSibling().toElement();
+    }
+
+    textstr = domElementExpressionChild.text();
+    if (textstr.isNull())
+      textstr = "";
+
+    /*
+      if (qcount == 0)
+      {
+      grade = KV_NORM_GRADE;
+      }
+
+      if (r_qcount == 0)
+      {
+      r_grade = KV_NORM_GRADE;
+      }
+    */
+    expr.addTranslation (textstr, grade, r_grade);
+    expr.setQueryCount  (count, qcount, false);
+    expr.setQueryCount  (count, r_qcount, true);
+    expr.setBadCount    (count, bcount, false);
+    expr.setBadCount    (count, r_bcount, true);
+    expr.setQueryDate   (count, qdate, false);
+    expr.setQueryDate   (count, r_qdate, true);
+
+    if (conjug.size() > 0)
+    {
+      expr.setConjugation(count, conjug[0]);
+      conjug.clear();
+    }
+    if (!comparison.isEmpty())
+    {
+      expr.setComparison(count, comparison);
+      comparison.clear();
+    }
+    if (!mc.isEmpty())
+    {
+      expr.setMultipleChoice(count, mc);
+      mc.clear();
+    }
+    if (!type.isEmpty() )
+      expr.setType (count, type);
+    if (!remark.isEmpty() )
+      expr.setRemark (count, remark);
+    if (!pronunce.isEmpty() )
+      expr.setPronunce (count, pronunce);
+    if (!faux_ami_f.isEmpty() )
+      expr.setFauxAmi (count, faux_ami_f, false);
+    if (!faux_ami_t.isEmpty() )
+      expr.setFauxAmi (count, faux_ami_t, true);
+    if (!synonym.isEmpty() )
+      expr.setSynonym (count, synonym);
+    if (!example.isEmpty() )
+      expr.setExample (count, example);
+    if (!usage.isEmpty() )
+      expr.setUsageLabel (count, usage);
+    if (!paraphrase.isEmpty() )
+      expr.setParaphrase (count, paraphrase);
+    if (!antonym.isEmpty() )
+      expr.setAntonym (count, antonym);
+            
+    domElementExpressionChild = domElementExpressionChild.nextSibling().toElement();
+  }
+  if (m_doc->numEntries() == 0)
+    m_doc->setQueryLang(q_org, q_trans);
+  m_doc->vocabulary.push_back(expr);
+
+  return true;
+}
+
+
+bool KEduVocKvtmlReader::readBody(QDomElement &domElementParent)
+{
+  bool lessgroup = false;
+  bool optgroup = false;
+  bool attrgroup = false;
+  bool tensegroup = false;
+  bool usagegroup = false;
+  bool articlegroup = false;
+  bool conjuggroup = false;
+
+  int ent_no = 0;
+  int ent_percent = (int) m_doc->lines / 100;
+  float f_ent_percent = (int) m_doc->lines / 100.0;
+/* TODO EPT
+if (lines != 0)
+    emit progressChanged(this, 0);
+*/
+
+  QDomElement domElementChild = domElementParent.firstChild().toElement();
+
+  while (!domElementChild.isNull())
+  {
+    if (domElementChild.tagName() == KV_LESS_GRP)
+    {
+      if (lessgroup)
+      {
+        domError(i18n("repeated occurrence of tag <%1>").arg(domElementChild.tagName()));
+        return false;
+      }
+      lessgroup = true;
+      if (!readLesson(domElementChild))
+        return false;
+    }
+
+    else if (domElementChild.tagName() == KV_ARTICLE_GRP)
+    {
+      if (articlegroup)
+      {
+        domError(i18n("repeated occurrence of tag <%1>").arg(domElementChild.tagName()));
+        return false;
+      }
+      articlegroup = true;
+      if (!readArticle(domElementChild))
+        return false;
+    }
+
+    else if (domElementChild.tagName() == KV_CONJUG_GRP)
+    {
+      if (conjuggroup)
+      {
+        domError(i18n("repeated occurrence of tag <%1>").arg(domElementChild.tagName()));
+        return false;
+      }
+      conjuggroup = true;
+      if (!readConjug(domElementChild, m_doc->conjugations, KV_CON_ENTRY))
+        return false;
+    }
+
+    else if (domElementChild.tagName() == KV_OPTION_GRP)
+    {
+      if (optgroup)
+      {
+        domError(i18n("repeated occurrence of tag <%1>").arg(domElementChild.tagName()));
+        return false;
+      }
+      optgroup = true;
+      if (!readOptions(domElementChild))
+        return false;
+    }
+
+    else if (domElementChild.tagName() == KV_TYPE_GRP)
+    {
+      if (attrgroup)
+      {
+        domError(i18n("repeated occurrence of tag <%1>").arg(domElementChild.tagName()));
+        return false;
+      }
+      attrgroup = true;
+      if (!readType(domElementChild))
+        return false;
+    }
+
+    else if (domElementChild.tagName() == KV_TENSE_GRP)
+    {
+      if (tensegroup)
+      {
+        domError(i18n("repeated occurrence of tag <%1>").arg(domElementChild.tagName()));
+        return false;
+      }
+      tensegroup = true;
+      if (!readTense(domElementChild))
+        return false;
+    }
+
+    else if (domElementChild.tagName() == KV_USAGE_GRP)
+    {
+      if (usagegroup)
+      {
+        domError(i18n("repeated occurrence of tag <%1>").arg(domElementChild.tagName()));
+        return false;
+      }
+      usagegroup = true;
+      if (!readUsage(domElementChild))
+        return false;
+    }
+
+    else if (domElementChild.tagName() == KV_EXPR)
+    {
+      /* TODO EPT
+      if (lines != 0)
+      {
+        ent_no++;
+        if (ent_percent != 0 && (ent_no % ent_percent) == 0 )
+          emit progressChanged(this, int(ent_no / f_ent_percent));
+      }*/
+      if (!readExpression(domElementChild))
+        return false;
+    }
+
+    else
+    {
+      domErrorUnknownElement(domElementChild.tagName());
+      return false;
+    }
+
+    domElementChild = domElementChild.nextSibling().toElement();
+  }
+
+  return true;
+}
+
+
+bool KEduVocKvtmlReader::readDoc(KEduVocDocument *doc)
+{
+  m_doc = doc;
+    
+  QDomDocument domDoc("Kvtml" );
+  QString errorMsg;   
+  if( !domDoc.setContent( m_inputFile, &errorMsg ) )
+  {
+    domError(errorMsg);   
+    return false;
+  }
+
+  m_doc->langs.clear();
+  m_doc->vocabulary.clear();
+
+  m_doc->generator = "";
+  m_doc->cols = 0;
+  m_doc->lines = 0;
+  m_doc->doctitle = "";
+  m_doc->author = "";
+  m_doc->license = "";
+  m_doc->doc_remark = "";
+
+
+  QDomElement domElementKvtml = domDoc.documentElement();
+  if( domElementKvtml.tagName() != KV_DOCTYPE )
+  {
+    domError(i18n("Tag <%1> was expected "
+            "but tag <%2> was read." ).arg(KV_DOCTYPE).arg(domElementKvtml.tagName()));
+    return false;
+  }
+
+  //-------------------------------------------------------------------------
+  // Attributes
+  //-------------------------------------------------------------------------
+
+  QDomAttr domAttrEncoding = domElementKvtml.attributeNode(KV_ENCODING);
+  if (!domAttrEncoding.isNull())
+  {
+    // TODO handle old encodings
+    // Qt DOM API autodetects encoding, so is there anything to do ?
+  }
+
+  QDomAttr domAttrTitle = domElementKvtml.attributeNode(KV_TITLE);
+  if (!domAttrTitle.isNull())
+  {
+    m_doc->doctitle = domAttrTitle.value();
+  }
+
+  QDomAttr domAttrAuthor = domElementKvtml.attributeNode(KV_AUTHOR);
+  if (!domAttrAuthor.isNull())
+  {
+    m_doc->author = domAttrAuthor.value();
+  }
+
+  QDomAttr domAttrLicence = domElementKvtml.attributeNode(KV_LICENSE);
+  if (!domAttrLicence.isNull())
+  {
+    m_doc->license = domAttrLicence.value();
+  }
+
+  QDomAttr domAttrRemark = domElementKvtml.attributeNode(KV_DOC_REM);
+  if (!domAttrRemark.isNull())
+  {
+    m_doc->doc_remark = domAttrRemark.value();
+  }
+
+  QDomAttr domAttrGenerator = domElementKvtml.attributeNode(KV_GENERATOR);
+  if (!domAttrGenerator.isNull())
+  {
+    m_doc->generator = domAttrGenerator.value();
+    int pos = m_doc->generator.findRev (KVD_VERS_PREFIX);
+    if (pos >= 0)
+    {
+      m_doc->doc_version = m_doc->generator;
+      m_doc->doc_version.remove (0, pos+2);
+    }
+  }
+
+  QDomAttr domAttrCols = domElementKvtml.attributeNode(KV_COLS);
+  if (!domAttrCols.isNull())
+  {
+    m_doc->cols = domAttrCols.value().toInt();
+  }
+
+  QDomAttr domAttrLines = domElementKvtml.attributeNode(KV_LINES);
+  if (!domAttrLines.isNull())
+  {
+    m_doc->lines = domAttrLines.value().toInt();
+  }
+
+
+  //-------------------------------------------------------------------------
+  // Children
+  //-------------------------------------------------------------------------
+
+  bool result = readBody(domElementKvtml);  // read vocabulary
+
+  // TODO EPT setModified (false);
+  return result;
+}
+
+
+void KEduVocKvtmlReader::domErrorUnknownElement(const QString &elem)
+{
+  QString ln = i18n("File:\t%1\n").arg(m_doc->URL().path());
+
+  QString format = i18n(
+      "Your document contains an unknown tag <%1>.  "  // keep trailing space
+      "Maybe your version of KVocTrain is too old, "
+      "or the document is damaged.\n"
+      "Loading is aborted because KVocTrain cannot "
+      "read documents with unknown elements.\n"
+     );
+  QString msg = format.arg(elem);
+  QApplication::setOverrideCursor( arrowCursor, true );
+  QString s = kapp->makeStdCaption(i18n("Unknown element"));
+  KMessageBox::sorry(0, ln+msg, s);
+  QApplication::restoreOverrideCursor();
+}
+
+void KEduVocKvtmlReader::domError(const QString &text )
+{
+  QApplication::setOverrideCursor( arrowCursor, true );
+  QString s = kapp->makeStdCaption(i18n("Error"));
+  QString ln = i18n("File:\t%1\n").arg(m_doc->URL().path());
+  QString msg = text;
+  KMessageBox::error(0, ln+msg, s);
+  QApplication::restoreOverrideCursor();
+}
+
+//#include "KEduVocKvtmlReader.moc"
diff --git a/kwordquiz/keduvockvtmlreader.h b/kwordquiz/keduvockvtmlreader.h
new file mode 100644 (file)
index 0000000..df96d28
--- /dev/null
@@ -0,0 +1,121 @@
+/***************************************************************************
+                     read a KEduVocDocument from a KVTML file
+    -----------------------------------------------------------------------
+    copyright            : (C) 1999-2001 Ewald Arnold
+                           (C) 2001 The KDE-EDU team
+                                                              (C) 2005 Eric Pignet
+    email                : eric at erixpage.com
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   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.                                   *
+ *                                                                         *
+ ***************************************************************************/
+
+#ifndef KEDUVOCKVTMLREADER_H
+#define KEDUVOCKVTMLREADER_H
+
+#include <qfile.h>
+#include <qdom.h>
+
+#include "keduvocdocument.h"
+#include "grammarmanager.h"
+#include "MultipleChoice.h"
+
+class KEduVocDocument;
+
+// internal types, indented are subtypes
+
+#define QM_VERB           "v"    // go
+#define   QM_VERB_IRR     "ir"
+#define   QM_VERB_REG     "re"
+#define QM_NOUN           "n"    // table, coffee
+#define   QM_NOUN_F       "f"
+#define   QM_NOUN_M       "m"
+#define   QM_NOUN_S       "s"
+#define QM_NAME           "nm"
+#define QM_ART            "ar"   // article
+#define   QM_ART_DEF      "def"  // definite    a/an
+#define   QM_ART_IND      "ind"  // indefinite  the
+#define QM_ADJ            "aj"   // adjective   expensive, good
+#define QM_ADV            "av"   // adverb      today, strongly
+#define QM_PRON           "pr"   // pronoun     you, she
+#define   QM_PRON_POS     "pos"  // possessive  my, your
+#define   QM_PRON_PER     "per"  // personal
+#define QM_PHRASE         "ph"
+#define QM_NUM            "num"  // numeral
+#define   QM_NUM_ORD      "ord"  // ordinal     first, second
+#define   QM_NUM_CARD     "crd"  // cardinal    one, two
+#define QM_INFORMAL       "ifm"
+#define QM_FIG            "fig"
+#define QM_CON            "con"  // conjuncton  and, but
+#define QM_PREP           "pre"  // preposition behind, between
+#define QM_QUEST          "qu"   // question    who, what
+
+// type delimiters
+
+#define QM_USER_TYPE  "#"   // designates number of user type
+#define QM_TYPE_DIV   ":"   // divide main from subtype
+
+// usage delimiters (also declared in UsageManager.h)
+
+#define UL_USER_USAGE  "#"   // designates number of user type
+
+/**
+@author Eric Pignet
+*/
+class KEduVocKvtmlReader : public QObject
+{
+public:
+       KEduVocKvtmlReader(QFile *file);
+       ~KEduVocKvtmlReader();
+
+       bool readDoc(KEduVocDocument *doc);
+
+       bool readLesson(QDomElement &domElementParent);
+       bool readArticle(QDomElement &domElementParent);
+       bool readConjug(QDomElement &domElementParent,
+                                                                       vector<Conjugation> &curr_conjug,
+                  const QString &entry_tag);
+       bool readOptions(QDomElement &domElementParent);
+       bool readType(QDomElement &domElementParent);
+       bool readTense(QDomElement &domElementParent);
+       bool readUsage(QDomElement &domElementParent);
+       bool readComparison(QDomElement &domElementParent,
+                      Comparison &comp);
+       bool readMultipleChoice(QDomElement &domElementParent,
+                          MultipleChoice &mc);
+       bool readExpressionChildAttributes(     QDomElement &domElementExpressionChild,
+                                                                                                                         QString &lang,
+                                                                                                                         grade_t &grade, grade_t &rev_grade,
+                                                                                                                         int &count, int &rev_count,
+                                                                                                                         time_t &date, time_t &rev_date,
+                                                                                                                         QString &remark,
+                                                                                                                         int &bcount, int &rev_bcount,
+                                                                                                                         QString &query_id,
+                                                                                                                         QString &pronunce,
+                                                                                                                         int &width,
+                                                                                                                         QString &type,
+                                                                                                                         QString &faux_ami_f,
+                                                                                                                         QString &faux_ami_t,
+                                                                                                                         QString &synonym,
+                                                                                                                         QString &example,
+                                                                                                                         QString &antonym,
+                                                                                                                         QString &usage,
+                                                                                                                         QString &paraphrase);
+       bool readExpression(QDomElement &domElementParent);
+       bool readBody(QDomElement &domElementParent);
+
+       void domErrorUnknownElement(const QString &elem);
+       void domError(const QString &text );
+
+private:
+  QFile *m_inputFile;
+  KEduVocDocument *m_doc;
+};
+
+#endif
diff --git a/kwordquiz/keduvockvtmlwriter.cpp b/kwordquiz/keduvockvtmlwriter.cpp
new file mode 100644 (file)
index 0000000..4569efd
--- /dev/null
@@ -0,0 +1,950 @@
+/***************************************************************************
+                   export a KEduVocDocument to a KVTML file
+    -----------------------------------------------------------------------
+    copyright            : (C) 1999-2001 Ewald Arnold
+                           (C) 2001 The KDE-EDU team
+                                                              (C) 2005 Eric Pignet
+    email                : eric at erixpage.com
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ ***************************************************************************/
+
+#include <kdebug.h>
+#include <qdom.h>
+#include <qtextstream.h>
+
+#include "keduvockvtmlwriter.h"
+#include "keduvocdocument.h"
+
+KEduVocKvtmlWriter::KEduVocKvtmlWriter(QFile *file)
+{
+  // the file must be already open
+  m_outputFile = file;
+}
+
+KEduVocKvtmlWriter::~KEduVocKvtmlWriter()
+{
+}
+
+bool KEduVocKvtmlWriter::saveTypeNameKvtMl (QDomDocument &domDoc, QDomElement &domElementParent)
+{
+  if (m_doc->type_descr.size() == 0)
+    return true;
+
+  QDomElement domElementType = domDoc.createElement(KV_TYPE_GRP);
+
+  for (int lfn = 0; lfn < (int) m_doc->type_descr.size(); lfn++)
+  {
+    if (!(m_doc->type_descr[lfn].isNull()) )
+    {
+      QDomElement domElementDesc = domDoc.createElement(KV_TYPE_DESC);
+      QDomText domTextDesc = domDoc.createTextNode(m_doc->type_descr[lfn]);
+
+      domElementDesc.setAttribute(KV_TYPE_NO, lfn+1);
+      domElementDesc.appendChild(domTextDesc);
+      domElementType.appendChild(domElementDesc);
+    }
+  }
+
+  domElementParent.appendChild(domElementType);
+  return true;
+}
+
+
+bool KEduVocKvtmlWriter::saveTenseNameKvtMl (QDomDocument &domDoc, QDomElement &domElementParent)
+{
+  if (m_doc->tense_descr.size() == 0)
+    return true;
+
+  QDomElement domElementTense = domDoc.createElement(KV_TENSE_GRP);
+
+  for (int lfn = 0; lfn < (int) m_doc->tense_descr.size(); lfn++)
+  {
+    if (!(m_doc->tense_descr[lfn].isNull()) ) {
+      QDomElement domElementDesc = domDoc.createElement(KV_TENSE_DESC);
+      QDomText domTextDesc = domDoc.createTextNode(m_doc->tense_descr[lfn]);
+
+      domElementDesc.setAttribute(KV_TENSE_NO, lfn+1);
+      domElementDesc.appendChild(domTextDesc);
+      domElementTense.appendChild(domElementDesc);
+    }
+  }
+
+  domElementParent.appendChild(domElementTense);
+  return true;
+}
+
+
+bool KEduVocKvtmlWriter::saveUsageNameKvtMl (QDomDocument &domDoc, QDomElement &domElementParent)
+{
+  if (m_doc->usage_descr.size() == 0)
+    return true;
+
+  QDomElement domElementUsage = domDoc.createElement(KV_USAGE_GRP);
+
+  for (int lfn = 0; lfn < (int) m_doc->usage_descr.size(); lfn++)
+  {
+    if (!(m_doc->usage_descr[lfn].isNull()) )
+    {
+      QDomElement domElementDesc = domDoc.createElement(KV_USAGE_DESC);
+      QDomText domTextDesc = domDoc.createTextNode(m_doc->usage_descr[lfn]);
+
+      domElementDesc.setAttribute(KV_USAGE_NO, lfn+1);
+      domElementDesc.appendChild(domTextDesc);
+      domElementUsage.appendChild(domElementDesc);
+    }
+  }
+
+  domElementParent.appendChild(domElementUsage);
+  return true;
+}
+
+
+bool KEduVocKvtmlWriter::saveLessonKvtMl (QDomDocument &domDoc, QDomElement &domElementParent)
+{
+  if (m_doc->lesson_descr.size() == 0)
+    return true;
+
+  QDomElement domElementLesson = domDoc.createElement(KV_LESS_GRP);
+  domElementLesson.setAttribute(KV_SIZEHINT, m_doc->getSizeHint(-1));
+
+  for (int lfn = 0; lfn < (int) m_doc->lesson_descr.size(); lfn++)
+  {
+    if (!(m_doc->lesson_descr[lfn].isNull()) )
+    {
+      QDomElement domElementDesc = domDoc.createElement(KV_LESS_DESC);
+      QDomText domTextDesc = domDoc.createTextNode(m_doc->lesson_descr[lfn]);
+
+      domElementDesc.setAttribute(KV_LESS_NO, lfn+1);
+      if (m_doc->getCurrentLesson() == lfn+1)
+        domElementDesc.setAttribute (KV_LESS_CURR, 1);
+      if (lfn < (int) m_doc->lessons_in_query.size() && m_doc->lessons_in_query[lfn])
+        domElementDesc.setAttribute (KV_LESS_QUERY, 1);
+
+      domElementDesc.appendChild(domTextDesc);
+      domElementLesson.appendChild(domElementDesc);
+    }
+  }
+
+  domElementParent.appendChild(domElementLesson);
+  return true;
+}
+
+
+bool KEduVocKvtmlWriter::saveConjug(QDomDocument &domDoc, QDomElement &domElementParent,
+                                    const Conjugation &curr_conjug, QString type)
+{
+  if (!curr_conjug.pers1Singular(type).isEmpty() )
+  {
+    QDomElement domElementP1s = domDoc.createElement(KV_CON_P1S);
+    QDomText domTextP1s = domDoc.createTextNode(curr_conjug.pers1Singular(type));
+
+    domElementP1s.appendChild(domTextP1s);
+    domElementParent.appendChild(domElementP1s);
+  }
+
+  if (!curr_conjug.pers2Singular(type).isEmpty() )
+  {
+    QDomElement domElementP2s = domDoc.createElement(KV_CON_P2S);
+    QDomText domTextP2s = domDoc.createTextNode(curr_conjug.pers2Singular(type));
+
+    domElementP2s.appendChild(domTextP2s);
+    domElementParent.appendChild(domElementP2s);
+  }
+
+  if (!curr_conjug.pers3FemaleSingular(type).isEmpty() ||
+    curr_conjug.pers3SingularCommon(type))
+  {
+    QDomElement domElementP3sf = domDoc.createElement(KV_CON_P3SF);
+    QDomText domTextP3sf = domDoc.createTextNode(curr_conjug.pers3FemaleSingular(type));
+
+    if (curr_conjug.pers3SingularCommon(type))
+      domElementP3sf.setAttribute(KV_CONJ_COMMON, 1);
+
+    domElementP3sf.appendChild(domTextP3sf);
+    domElementParent.appendChild(domElementP3sf);
+  }
+
+  if (!curr_conjug.pers3MaleSingular(type).isEmpty() )
+  {
+    QDomElement domElementP3sm = domDoc.createElement(KV_CON_P3SM);
+    QDomText domTextP3sm = domDoc.createTextNode(curr_conjug.pers3MaleSingular(type));
+
+    domElementP3sm.appendChild(domTextP3sm);
+    domElementParent.appendChild(domElementP3sm);
+  }
+
+  if (!curr_conjug.pers3NaturalSingular(type).isEmpty() )
+  {
+    QDomElement domElementP3sn = domDoc.createElement(KV_CON_P3SN);
+    QDomText domTextP3sn = domDoc.createTextNode(curr_conjug.pers3NaturalSingular(type));
+
+    domElementP3sn.appendChild(domTextP3sn);
+    domElementParent.appendChild(domElementP3sn);
+  }
+
+  if (!curr_conjug.pers1Plural(type).isEmpty() )
+  {
+    QDomElement domElementP1p = domDoc.createElement(KV_CON_P1P);
+    QDomText domTextP1p = domDoc.createTextNode(curr_conjug.pers1Plural(type));
+
+    domElementP1p.appendChild(domTextP1p);
+    domElementParent.appendChild(domElementP1p);
+  }
+
+  if (!curr_conjug.pers2Plural(type).isEmpty() )
+  {
+    QDomElement domElementP2p = domDoc.createElement(KV_CON_P2P);
+    QDomText domTextP2p = domDoc.createTextNode(curr_conjug.pers2Plural(type));
+
+    domElementP2p.appendChild(domTextP2p);
+    domElementParent.appendChild(domElementP2p);
+  }
+
+  if (!curr_conjug.pers3FemalePlural(type).isEmpty() ||
+    curr_conjug.pers3PluralCommon(type))
+  {
+    QDomElement domElementP3pf = domDoc.createElement(KV_CON_P3PF);
+    QDomText domTextP3pf = domDoc.createTextNode(curr_conjug.pers3FemalePlural(type));
+
+    if (curr_conjug.pers3PluralCommon(type))
+      domElementP3pf.setAttribute(KV_CONJ_COMMON, 1);
+
+    domElementP3pf.appendChild(domTextP3pf);
+    domElementParent.appendChild(domElementP3pf);
+  }
+
+  if (!curr_conjug.pers3MalePlural(type).isEmpty() )
+  {
+    QDomElement domElementP3pm = domDoc.createElement(KV_CON_P3PM);
+    QDomText domTextP3pm = domDoc.createTextNode(curr_conjug.pers3MalePlural(type));
+
+    domElementP3pm.appendChild(domTextP3pm);
+    domElementParent.appendChild(domElementP3pm);
+  }
+
+  if (!curr_conjug.pers3NaturalPlural(type).isEmpty() )
+  {
+    QDomElement domElementP3pn = domDoc.createElement(KV_CON_P3PN);
+    QDomText domTextP3pn = domDoc.createTextNode(curr_conjug.pers3NaturalPlural(type));
+
+    domElementP3pn.appendChild(domTextP3pn);
+    domElementParent.appendChild(domElementP3pn);
+  }
+
+  return true;
+}
+
+bool KEduVocKvtmlWriter::saveConjugHeader(QDomDocument &domDoc, QDomElement &domElementParent,
+                                          vector<Conjugation> &curr_conjug)
+{
+/*
+ <conjugation>    used in header for definiton of "prefix"
+  <e l="de">      lang determines also lang order in entries !!
+   <s1>I</s1>     which must NOT differ
+   <s2>you<2>
+   <s3f common="0">he</s3f>
+   <s3m>she</s3m>
+   <s3n>it</s3n>
+   <p1>we</p1>
+   <p2>you</p2>
+   <p3f common="1">they</p3f>
+   <p3m>they</p3m>
+   <p3n>they</p3n>
+  </e>
+ </conjugation>
+
+*/
+  if (curr_conjug.size() == 0)
+    return true;
+
+  QDomElement domElementConjug = domDoc.createElement(KV_CONJUG_GRP);
+  QString s;
+
+  for (int ent = 0; ent < QMIN((int) curr_conjug.size(), m_doc->numLangs()); ent++)
+  {
+    QDomElement domElementEntry = domDoc.createElement(KV_CON_ENTRY);
+
+    if (ent == 0)
+    {
+      s = m_doc->getOriginalIdent().stripWhiteSpace();  //EPT le Ident doit ï¿½re superflu
+      if (s.isEmpty() )
+        s = "original";
+    }
+    else
+    {
+      s = m_doc->getIdent(ent).stripWhiteSpace();
+      if (s.isEmpty() )
+      {
+        s.setNum(ent);
+        s.insert(0, "translation ");
+      }
+    }
+    domElementEntry.setAttribute(KV_LANG, s);
+
+    if (!saveConjug (domDoc, domElementEntry, curr_conjug[ent], CONJ_PREFIX))
+      return false;
+
+    domElementConjug.appendChild(domElementEntry);
+  }
+
+  domElementParent.appendChild(domElementConjug);
+  return true;
+}
+
+
+bool KEduVocKvtmlWriter::saveComparison(QDomDocument &domDoc, QDomElement &domElementParent,
+                                        const Comparison &comp)
+/*
+ <comparison>
+   <l1>good</l1>
+   <l2>better</l2>
+   <l3>best</l3>
+ </comparison>
+*/
+{
+  if (comp.isEmpty())
+    return true;
+
+  QDomElement domElementComparison = domDoc.createElement(KV_COMPARISON_GRP);
+
+  if (!comp.l1().isEmpty() )
+  {
+    QDomElement domElementL1 = domDoc.createElement(KV_COMP_L1);
+    QDomText domTextL1 = domDoc.createTextNode(comp.l1());
+
+    domElementL1.appendChild(domTextL1);
+    domElementComparison.appendChild(domElementL1);
+  }
+
+  if (!comp.l2().isEmpty() )
+  {
+    QDomElement domElementL2 = domDoc.createElement(KV_COMP_L2);
+    QDomText domTextL2 = domDoc.createTextNode(comp.l2());
+
+    domElementL2.appendChild(domTextL2);
+    domElementComparison.appendChild(domElementL2);
+  }
+
+  if (!comp.l3().isEmpty() )
+  {
+    QDomElement domElementL3 = domDoc.createElement(KV_COMP_L3);
+    QDomText domTextL3 = domDoc.createTextNode(comp.l3());
+
+    domElementL3.appendChild(domTextL3);
+    domElementComparison.appendChild(domElementL3);
+  }
+
+  domElementParent.appendChild(domElementComparison);
+  return true;
+}
+
+
+bool KEduVocKvtmlWriter::saveMultipleChoice(QDomDocument &domDoc, QDomElement &domElementParent,
+                                            const MultipleChoice &mc)
+/*
+ <multiplechoice>
+   <mc1>good</mc1>
+   <mc2>better</mc2>
+   <mc3>best</mc3>
+   <mc4>best 2</mc4>
+   <mc5>best 3</mc5>
+ </multiplechoice>
+*/
+{
+  if (mc.isEmpty())
+    return true;
+
+  QDomElement domElementMC = domDoc.createElement(KV_MULTIPLECHOICE_GRP);
+
+  if (!mc.mc1().isEmpty() )
+  {
+    QDomElement domElementMC1 = domDoc.createElement(KV_MC_1);
+    QDomText domTextMC1 = domDoc.createTextNode(mc.mc1());
+
+    domElementMC1.appendChild(domTextMC1);
+    domElementMC.appendChild(domElementMC1);
+  }
+
+  if (!mc.mc2().isEmpty() )
+  {
+    QDomElement domElementMC2 = domDoc.createElement(KV_MC_2);
+    QDomText domTextMC2 = domDoc.createTextNode(mc.mc2());
+
+    domElementMC2.appendChild(domTextMC2);
+    domElementMC.appendChild(domElementMC2);
+  }
+
+  if (!mc.mc3().isEmpty() )
+  {
+    QDomElement domElementMC3 = domDoc.createElement(KV_MC_3);
+    QDomText domTextMC3 = domDoc.createTextNode(mc.mc3());
+
+    domElementMC3.appendChild(domTextMC3);
+    domElementMC.appendChild(domElementMC3);
+  }
+
+  if (!mc.mc4().isEmpty() )
+  {
+    QDomElement domElementMC4 = domDoc.createElement(KV_MC_4);
+    QDomText domTextMC4 = domDoc.createTextNode(mc.mc4());
+
+    domElementMC4.appendChild(domTextMC4);
+    domElementMC.appendChild(domElementMC4);
+  }
+
+  if (!mc.mc5().isEmpty() )
+  {
+    QDomElement domElementMC5 = domDoc.createElement(KV_MC_5);
+    QDomText domTextMC5 = domDoc.createTextNode(mc.mc5());
+
+    domElementMC5.appendChild(domTextMC5);
+    domElementMC.appendChild(domElementMC5);
+  }
+
+  domElementParent.appendChild(domElementMC);
+  return true;
+}
+
+
+bool KEduVocKvtmlWriter::saveConjugEntry( QDomDocument &domDoc, QDomElement &domElementParent,
+                                          Conjugation &curr_conjug)
+/*
+ <conjugation>    in entry for definition of tenses of (irreg.) verbs
+  <t n="sipa">
+   <s1>go</s1>
+   <s2>go</s2>
+   <s3f>goes</s3f>
+   <s3m>goes</s3m>
+   <s3n>goes</s3n>
+   <p1>go</p1>
+   <p2>go</p2>
+   <p3f>go</p3f>
+   <p3m>go</p3m>
+   <p3n>go</p3n>
+  </t>
+ </conjugation>
+*/
+{
+  curr_conjug.cleanUp();
+  if (curr_conjug.numEntries() == 0 )
+    return true;
+
+  QDomElement domElementConjug = domDoc.createElement(KV_CONJUG_GRP);
+  QString type;
+
+  for (int lfn = 0; lfn < (int) curr_conjug.numEntries(); lfn++)
+  {
+    QDomElement domElementType = domDoc.createElement(KV_CON_TYPE);
+
+    type = curr_conjug.getType(lfn);
+    domElementType.setAttribute(KV_CON_NAME, type);
+    
+    if (!saveConjug (domDoc, domElementType, curr_conjug, curr_conjug.getType(lfn)) )
+      return false;
+
+    domElementConjug.appendChild(domElementType);
+  }
+
+  domElementParent.appendChild(domElementConjug);
+  return true;
+}
+
+
+bool KEduVocKvtmlWriter::saveArticleKvtMl(QDomDocument &domDoc, QDomElement &domElementParent)
+/*
+ <article>
+  <e l="de">    lang determines also lang order in entries !!
+   <fi>eine</fi>  which must NOT differ
+   <fd>die</fd>
+   <mi>ein</mi>
+   <md>der</md>
+   <ni>ein</ni>
+   <nd>das</nd>
+  </e>
+ </article>
+*/
+{
+  if (m_doc->articles.size() == 0)
+    return true;
+
+  QDomElement domElementArticle = domDoc.createElement(KV_ARTICLE_GRP);
+  QString def, indef, s;
+
+  for (int lfn = 0; lfn < QMIN((int) m_doc->articles.size(), m_doc->numLangs()); lfn++)
+  {
+    QDomElement domElementEntry = domDoc.createElement(KV_ART_ENTRY);
+    if (lfn == 0)
+    {
+      s = m_doc->getOriginalIdent().stripWhiteSpace();
+      if (s.isEmpty() )
+        s = "original";
+    }
+    else
+    {
+      s = m_doc->getIdent(lfn).stripWhiteSpace();
+      if (s.isEmpty() )
+      {
+        s.setNum(lfn);
+        s.insert(0, "translation ");
+      }
+    }
+    domElementEntry.setAttribute(KV_LANG, s);
+
+    m_doc->articles[lfn].female(def, indef);
+    if (!def.isEmpty() )
+    {
+      QDomElement domElementFD = domDoc.createElement(KV_ART_FD);
+      QDomText domTextFD = domDoc.createTextNode(def);
+
+      domElementFD.appendChild(domTextFD);
+      domElementEntry.appendChild(domElementFD);
+    }
+    if (!indef.isEmpty() )
+    {
+      QDomElement domElementFI = domDoc.createElement(KV_ART_FI);
+      QDomText domTextFI = domDoc.createTextNode(indef);
+
+      domElementFI.appendChild(domTextFI);
+      domElementEntry.appendChild(domElementFI);
+    }
+
+    m_doc->articles[lfn].male(def, indef);
+    if (!def.isEmpty() )
+    {
+      QDomElement domElementMD = domDoc.createElement(KV_ART_MD);
+      QDomText domTextMD = domDoc.createTextNode(def);
+
+      domElementMD.appendChild(domTextMD);
+      domElementEntry.appendChild(domElementMD);
+    }
+    if (!indef.isEmpty() )
+    {
+      QDomElement domElementMI = domDoc.createElement(KV_ART_MI);
+      QDomText domTextMI = domDoc.createTextNode(indef);
+
+      domElementMI.appendChild(domTextMI);
+      domElementEntry.appendChild(domElementMI);
+    }
+
+    m_doc->articles[lfn].natural(def, indef);
+    if (!def.isEmpty() )
+    {
+      QDomElement domElementND = domDoc.createElement(KV_ART_ND);
+      QDomText domTextND = domDoc.createTextNode(def);
+
+      domElementND.appendChild(domTextND);
+      domElementEntry.appendChild(domElementND);
+    }
+    if (!indef.isEmpty() )
+    {
+      QDomElement domElementNI = domDoc.createElement(KV_ART_NI);
+      QDomText domTextNI = domDoc.createTextNode(indef);
+
+      domElementNI.appendChild(domTextNI);
+      domElementEntry.appendChild(domElementNI);
+    }
+
+    domElementArticle.appendChild(domElementEntry);
+  }
+
+  domElementParent.appendChild(domElementArticle);
+  return true;
+}
+
+
+bool KEduVocKvtmlWriter::saveOptionsKvtMl(QDomDocument &domDoc, QDomElement &domElementParent)
+{
+  QDomElement domElementOption = domDoc.createElement(KV_OPTION_GRP);
+  QDomElement domElementSort = domDoc.createElement(KV_OPT_SORT);
+
+  domElementSort.setAttribute(KV_BOOL_FLAG, (m_doc->sort_allowed?1:0));
+  domElementOption.appendChild(domElementSort);
+
+  domElementParent.appendChild(domElementOption);
+  return true;
+}
+
+
+bool KEduVocKvtmlWriter::writeDoc(KEduVocDocument *doc)
+{
+  bool first_expr = true;
+
+  m_doc = doc;
+
+  QDomDocument domDoc( "KEduVocDocument" );
+  QDomElement domElementKvtml = domDoc.createElement( "kvtml" );
+
+  QString head( "<?xml version='1.0' encoding='UTF-8' ?><!DOCTYPE kvtml SYSTEM \"kvoctrain.dtd\">" );
+  domDoc.setContent( head );
+
+  QDomComment domComment = domDoc.createComment(QString(
+         "\nThis is a machine generated file.\n"
+         "Be careful when editing here.\n"
+         "\n"
+         "Short definition:\n"
+         "\n"
+         "lesson     lesson group\n"
+         " desc    name\n"
+         "   %no     its index\n"
+         "   %query  is in query selection\n"
+         "   %current  is current lesson\n"
+         "type     type group\n"
+         " desc    name\n"
+         "   %no     its index\n"
+         "e      entry of dictionary\n"
+         "  %s     is selected\n"
+         "  %m     lesson member\n"
+         "  %t     common expression type\n"
+         " o       original\n"
+         "   %q    in query (\"o\" is given, \"t\" is wanted)\n"
+         "   %l    language code\n"
+         "   %r    remark\n"
+         "   %p    pronunciation\n"
+         "   %width  column width\n"
+         "   %t    expression type (see QueryManager.h)\n"
+         "   %tf     false friend from\n"
+         "   %ff     false friend to\n"
+         "   %a    antonym\n"
+         "   %y    synonym\n"
+         "   %x    example\n"
+         "   %u    usage label\n"
+         "   %h    paraphrase\n"
+         " t       translation ..\n"
+         "   %q    in query (\"t\" is given, \"o\" is wanted)\n"
+         "   %l    language code\n"
+         "   %r    remark\n"
+         "   %p    pronunciation\n"
+         "   %width  column width\n"
+         "   %t    expression type\n"
+         "   %tf     false friend from\n"
+         "   %ff     false friend to\n"
+         "   %a    antonym\n"
+         "   %y    synonym\n"
+         "   %x    example\n"
+         "   %u    usage label\n"
+         "   %h    paraphrase\n"
+         "\n"
+         "   %d    last query date (from;to)\n"
+         "   %w    dito, compressed and deprecated\n"
+         "   %g    grade (from;to)\n"
+         "   %c    count (from;to)\n"
+         "   %b    bad count (from;to)\n"
+         "\n"
+         "\nValid xml means:\n"
+         " - Close all tags\n"
+         " - Keep proper hierarchy\n"
+         " - All attributes are quoted\n"));
+
+  domDoc.appendChild(domComment);
+
+  domElementKvtml.setAttribute(KV_ENCODING, (QString)"UTF-8");
+
+  domElementKvtml.setAttribute(KV_GENERATOR, (QString) "kwordquiz");// TODO EPT KVD_VERS_PREFIX KVOCTRAIN_VERSION_STRING);
+  domElementKvtml.setAttribute(KV_COLS, m_doc->numLangs() );
+  domElementKvtml.setAttribute(KV_LINES, m_doc->numEntries() );
+/* TODO EPT add title management
+  if (!title.isEmpty())
+  {
+    domElementKvtml.setAttribute(KV_TITLE, m_doc->getTitle() );
+    m_doc->doctitle = title;
+  }
+  else */if (!m_doc->doctitle.isEmpty())
+    domElementKvtml.setAttribute(KV_TITLE, m_doc->doctitle);
+
+  if (!m_doc->author.isEmpty())
+    domElementKvtml.setAttribute(KV_AUTHOR, m_doc->getAuthor() );
+
+  if (!m_doc->license.isEmpty())
+    domElementKvtml.setAttribute(KV_LICENSE, m_doc->getLicense() );
+
+  if (!m_doc->doc_remark.isEmpty())
+    domElementKvtml.setAttribute(KV_DOC_REM, m_doc->getDocRemark() );
+
+  if (!saveLessonKvtMl(domDoc, domElementKvtml))
+    return false;
+
+  if (!saveArticleKvtMl(domDoc, domElementKvtml))
+    return false;
+
+  if (!saveConjugHeader(domDoc, domElementKvtml, m_doc->conjugations))
+    return false;
+
+  if (!saveOptionsKvtMl(domDoc, domElementKvtml))
+    return false;
+
+  if (!saveTypeNameKvtMl(domDoc, domElementKvtml))
+    return false;
+
+  if (!saveTenseNameKvtMl(domDoc, domElementKvtml))
+    return false;
+
+  if (!saveUsageNameKvtMl(domDoc, domElementKvtml))
+    return false;
+
+  QString q_org, q_trans;
+  vector<KEduVocExpression>::const_iterator first =  m_doc->vocabulary.begin ();
+  m_doc->getQueryLang(q_org, q_trans);
+
+  int ent_no = 0;
+  int ent_percent = (int) m_doc->vocabulary.size () / 100;
+  float f_ent_percent = (int) m_doc->vocabulary.size () / 100.0;
+//TODO emit progressChanged(this, 0);
+
+  while (first != m_doc->vocabulary.end ())
+  {
+    QDomElement domElementExpression = domDoc.createElement(KV_EXPR);
+
+    ent_no++;
+    if (ent_percent != 0 && (ent_no % ent_percent) == 0 )
+//TODO emit progressChanged(this, ent_no / (int) f_ent_percent);
+
+    if ((*first).getLesson() != 0)
+    {
+      // entry belongs to lesson x
+      QString ls;
+      int lm = (*first).getLesson();
+      if (lm > (int) m_doc->lesson_descr.size() )
+      {
+        // should not be
+        kdError() << "index of lesson member too high: " << lm << endl;
+        lm = 0;
+      }
+      ls.setNum (lm);
+      domElementExpression.setAttribute (KV_LESS_MEMBER, ls);
+    }
+
+    if ((*first).isInQuery())
+    {
+      // entry was selected for query
+      domElementExpression.setAttribute (KV_SELECTED, (QString) "1");
+    }
+
+    if (!(*first).isActive())
+    {
+      // entry was inactive
+      domElementExpression.setAttribute (KV_INACTIVE, (QString) "1");
+    }
+
+    if ((*first).uniqueType() && !(*first).getType(0).isEmpty())
+    {
+      domElementExpression.setAttribute (KV_EXPRTYPE, (*first).getType(0));
+    }
+
+    QDomElement domElementOriginal = domDoc.createElement(KV_ORG);
+    if (first_expr)
+    {
+      // save space, only tell language in first entry
+      QString s;
+      s.setNum (m_doc->getSizeHint (0));
+      domElementOriginal.setAttribute(KV_SIZEHINT, s);
+
+      s = m_doc->getOriginalIdent().stripWhiteSpace();
+      if (s.isEmpty() )
+        s = "original";
+      domElementOriginal.setAttribute (KV_LANG, s);
+      if (s == q_org)
+        domElementOriginal.setAttribute(KV_QUERY, (QString) KV_O);
+      else if (s == q_trans)
+        domElementOriginal.setAttribute(KV_QUERY, (QString) KV_T);
+
+    }
+
+    if (!(*first).getRemark(0).isEmpty() )
+      domElementOriginal.setAttribute(KV_REMARK, (*first).getRemark(0));
+
+    if (!(*first).getSynonym(0).isEmpty() )
+      domElementOriginal.setAttribute(KV_SYNONYM, (*first).getSynonym(0));
+
+    if (!(*first).getExample(0).isEmpty() )
+      domElementOriginal.setAttribute(KV_EXAMPLE, (*first).getExample(0));
+
+    if (!(*first).getUsageLabel(0).isEmpty() )
+      domElementOriginal.setAttribute(KV_USAGE, (*first).getUsageLabel(0));
+
+    if (!(*first).getParaphrase(0).isEmpty() )
+      domElementOriginal.setAttribute(KV_PARAPHRASE, (*first).getParaphrase(0));
+
+    if (!(*first).getAntonym(0).isEmpty() )
+      domElementOriginal.setAttribute(KV_ANTONYM, (*first).getAntonym(0));
+
+    if (!(*first).getPronunce(0).isEmpty() )
+      domElementOriginal.setAttribute(KV_PRONUNCE, (*first).getPronunce(0));
+
+    if (!(*first).uniqueType() && !(*first).getType(0).isEmpty())
+      domElementOriginal.setAttribute(KV_EXPRTYPE, (*first).getType(0));
+
+    if (!saveMultipleChoice(domDoc, domElementOriginal, (*first).getMultipleChoice(0)))
+      return false;
+
+    QString s;
+    QString entype = s = (*first).getType(0);
+    int pos = s.find (QM_TYPE_DIV);
+    if (pos >= 0)
+      entype = s.left (pos);
+    else
+      entype = s;
+
+    if (entype == QM_VERB
+        && (*first).getConjugation(0).numEntries() > 0)
+    {
+      Conjugation conj = (*first).getConjugation(0);
+      if (!saveConjugEntry(domDoc, domElementOriginal, conj))
+        return false;
+    }
+    else if (entype == QM_ADJ
+             && !(*first).getComparison(0).isEmpty())
+    {
+      Comparison comp = (*first).getComparison(0);
+      if (!saveComparison(domDoc, domElementOriginal, comp))
+        return false;
+    }
+
+    QDomText domTextOriginal = domDoc.createTextNode((*first).getOriginal());
+    domElementOriginal.appendChild(domTextOriginal);
+    domElementExpression.appendChild(domElementOriginal);
+
+    int trans = 1;
+    while (trans < (int)m_doc->langs.size())
+    {
+      QDomElement domElementTranslation = domDoc.createElement(KV_TRANS);
+      if (first_expr)
+      {
+        // save space, only tell language in first entry
+        QString s;
+        s.setNum (m_doc->getSizeHint (trans));
+        domElementTranslation.setAttribute(KV_SIZEHINT, s);
+
+        s = m_doc->getIdent(trans).stripWhiteSpace();
+        if (s.isEmpty() )
+        {
+          s.setNum (trans);
+          s.insert (0, "translation ");
+        }
+        domElementTranslation.setAttribute(KV_LANG, s);
+        if (s == q_org)
+          domElementTranslation.setAttribute(KV_QUERY, (QString) KV_O);
+        else if (s == q_trans)
+          domElementTranslation.setAttribute(KV_QUERY, (QString) KV_T);
+      }
+
+      QString s1, s2;
+
+      if ((*first).getGrade(trans, false) != 0
+        ||(*first).getGrade(trans, true) != 0)
+      {
+        domElementTranslation.setAttribute(KV_GRADE, (*first).gradeStr(trans, false)
+                  +';'
+                  +(*first).gradeStr(trans, true));
+      }
+
+      if ((*first).getQueryCount(trans, false) != 0
+        ||(*first).getQueryCount(trans, true) != 0)
+      {
+        s1.setNum((*first).getQueryCount(trans, false));
+        s2.setNum((*first).getQueryCount(trans, true));
+        domElementTranslation.setAttribute(KV_COUNT, s1 +';' +s2);
+      }
+
+      if ((*first).getBadCount(trans, false) != 0
+        ||(*first).getBadCount(trans, true) != 0)
+      {
+        s1.setNum((*first).getBadCount(trans, false));
+        s2.setNum((*first).getBadCount(trans, true));
+        domElementTranslation.setAttribute(KV_BAD, s1 +';' +s2);
+      }
+
+      if ((*first).getQueryDate(trans, false) != 0
+        ||(*first).getQueryDate(trans, true) != 0)
+      {
+        s1.setNum((*first).getQueryDate(trans, false));
+        s2.setNum((*first).getQueryDate(trans, true));
+        domElementTranslation.setAttribute(KV_DATE, s1 +';' +s2);
+      }
+
+      if (!(*first).getRemark(trans).isEmpty() )
+        domElementTranslation.setAttribute(KV_REMARK, (*first).getRemark(trans));
+
+      if (!(*first).getFauxAmi(trans, false).isEmpty() )
+        domElementTranslation.setAttribute(KV_FAUX_AMI_F, (*first).getFauxAmi(trans, false));
+
+      if (!(*first).getFauxAmi(trans, true).isEmpty() )
+        domElementTranslation.setAttribute(KV_FAUX_AMI_T, (*first).getFauxAmi(trans, true));
+
+      if (!(*first).getSynonym(trans).isEmpty() )
+        domElementTranslation.setAttribute(KV_SYNONYM, (*first).getSynonym(trans));
+
+      if (!(*first).getExample(trans).isEmpty() )
+        domElementTranslation.setAttribute(KV_EXAMPLE, (*first).getExample(trans));
+
+      if (!(*first).getUsageLabel(trans).isEmpty() )
+        domElementTranslation.setAttribute(KV_USAGE, (*first).getUsageLabel(trans));
+
+      if (!(*first).getParaphrase(trans).isEmpty() )
+        domElementTranslation.setAttribute(KV_PARAPHRASE, (*first).getParaphrase(trans));
+
+      if (!(*first).getAntonym(trans).isEmpty() )
+        domElementTranslation.setAttribute(KV_ANTONYM, (*first).getAntonym(trans));
+
+      if (!(*first).getPronunce(trans).isEmpty() )
+        domElementTranslation.setAttribute(KV_PRONUNCE, (*first).getPronunce(trans));
+
+      if (!(*first).uniqueType() && !(*first).getType(trans).isEmpty())
+        domElementTranslation.setAttribute(KV_EXPRTYPE, (*first).getType(trans));
+
+      // only save conjugations when type == verb
+
+      if (!saveMultipleChoice(domDoc, domElementTranslation, (*first).getMultipleChoice(trans)))
+        return false;
+
+      QString s;
+      QString entype = s = (*first).getType(0);
+      int pos = s.find (QM_TYPE_DIV);
+      if (pos >= 0)
+        entype = s.left (pos);
+      else
+        entype = s;
+
+      if (entype == QM_VERB
+          && (*first).getConjugation(trans).numEntries() > 0)
+      {
+        Conjugation conj = (*first).getConjugation(trans);
+        if (!saveConjugEntry(domDoc, domElementTranslation, conj))
+          return false;
+      }
+
+      if (entype == QM_ADJ
+          && !(*first).getComparison(trans).isEmpty())
+      {
+        Comparison comp = (*first).getComparison(trans);
+        if (!saveComparison(domDoc, domElementTranslation, comp))
+          return false;
+      }
+
+      QDomText domTextTranslation = domDoc.createTextNode((*first).getTranslation(trans));
+      domElementTranslation.appendChild(domTextTranslation);
+      domElementExpression.appendChild(domElementTranslation);
+
+      trans++;
+    }
+
+    domElementKvtml.appendChild(domElementExpression);
+
+    first++;
+    first_expr = false;
+  }
+
+  domDoc.appendChild(domElementKvtml);
+
+  QTextStream ts( m_outputFile );
+  ts << domDoc.toString();
+
+// TODO setModified (false);
+}
diff --git a/kwordquiz/keduvockvtmlwriter.h b/kwordquiz/keduvockvtmlwriter.h
new file mode 100644 (file)
index 0000000..ca1f1f6
--- /dev/null
@@ -0,0 +1,99 @@
+/***************************************************************************
+                     export a KEduVocDocument to a KVTML file
+    -----------------------------------------------------------------------
+    copyright            : (C) 1999-2001 Ewald Arnold
+                           (C) 2001 The KDE-EDU team
+                           (C) 2005 Eric Pignet
+    email                : eric at erixpage.com
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   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.                                   *
+ *                                                                         *
+ ***************************************************************************/
+
+#ifndef KEDUVOCKVTMLWRITER_H
+#define KEDUVOCKVTMLWRITER_H
+
+#include <qfile.h>
+#include <qdom.h>
+
+//#include "keduvocdocument.h"
+#include "grammarmanager.h"
+#include "MultipleChoice.h"
+
+class KEduVocDocument;
+
+// internal types, indented are subtypes
+
+#define QM_VERB           "v"    // go
+#define   QM_VERB_IRR     "ir"
+#define   QM_VERB_REG     "re"
+#define QM_NOUN           "n"    // table, coffee
+#define   QM_NOUN_F       "f"
+#define   QM_NOUN_M       "m"
+#define   QM_NOUN_S       "s"
+#define QM_NAME           "nm"
+#define QM_ART            "ar"   // article
+#define   QM_ART_DEF      "def"  // definite    a/an
+#define   QM_ART_IND      "ind"  // indefinite  the
+#define QM_ADJ            "aj"   // adjective   expensive, good
+#define QM_ADV            "av"   // adverb      today, strongly
+#define QM_PRON           "pr"   // pronoun     you, she
+#define   QM_PRON_POS     "pos"  // possessive  my, your
+#define   QM_PRON_PER     "per"  // personal
+#define QM_PHRASE         "ph"
+#define QM_NUM            "num"  // numeral
+#define   QM_NUM_ORD      "ord"  // ordinal     first, second
+#define   QM_NUM_CARD     "crd"  // cardinal    one, two
+#define QM_INFORMAL       "ifm"
+#define QM_FIG            "fig"
+#define QM_CON            "con"  // conjuncton  and, but
+#define QM_PREP           "pre"  // preposition behind, between
+#define QM_QUEST          "qu"   // question    who, what
+
+// type delimiters
+
+#define QM_USER_TYPE  "#"   // designates number of user type
+#define QM_TYPE_DIV   ":"   // divide main from subtype
+
+/**
+@author Eric Pignet
+*/
+class KEduVocKvtmlWriter
+{
+public:
+  KEduVocKvtmlWriter(QFile *file);
+  ~KEduVocKvtmlWriter();
+
+  bool writeDoc(KEduVocDocument *doc);
+
+  bool saveLessonKvtMl (QDomDocument &domDoc, QDomElement &domElementParent);
+  bool saveTypeNameKvtMl (QDomDocument &domDoc, QDomElement &domElementParent);
+  bool saveTenseNameKvtMl (QDomDocument &domDoc, QDomElement &domElementParent);
+  bool saveUsageNameKvtMl (QDomDocument &domDoc, QDomElement &domElementParent);
+  bool saveOptionsKvtMl  (QDomDocument &domDoc, QDomElement &domElementParent);
+  bool saveArticleKvtMl  (QDomDocument &domDoc, QDomElement &domElementParent);
+  bool saveConjugHeader  (QDomDocument &domDoc, QDomElement &domElementParent,
+                          vector<Conjugation> &curr_conjug);
+  bool saveConjug        (QDomDocument &domDoc, QDomElement &domElementParent,
+                          const Conjugation &curr_conjug, QString type);
+  bool saveConjugEntry   (QDomDocument &domDoc, QDomElement &domElementParent,
+                          Conjugation &curr_conjug);
+
+  bool saveComparison    (QDomDocument &domDoc, QDomElement &domElementParent,
+                          const Comparison &comp);
+
+  bool saveMultipleChoice(QDomDocument &domDoc, QDomElement &domElementParent,
+                          const MultipleChoice &mc);
+
+private:
+  QFile *m_outputFile;
+  KEduVocDocument *m_doc;
+};
+
+#endif