• Skip to content
  • Skip to link menu
KDE 4.0 API Reference
  • KDE API Reference
  • kdeedu
  • Sitemap
  • Contact Us
 

libkdeedu/keduvocdocument

keduvocdocument.cpp

Go to the documentation of this file.
00001 /***************************************************************************
00002                         Vocabulary Document for KDE Edu
00003     -----------------------------------------------------------------------
00004     copyright      : (C) 1999-2001 Ewald Arnold <kvoctrain@ewald-arnold.de>
00005 
00006                      (C) 2005-2007 Peter Hedlund <peter.hedlund@kdemail.net>
00007                      (C) 2007 Frederik Gladhorn <frederik.gladhorn@kdemail.net>
00008  ***************************************************************************/
00009 
00010 /***************************************************************************
00011  *                                                                         *
00012  *   This program is free software; you can redistribute it and/or modify  *
00013  *   it under the terms of the GNU General Public License as published by  *
00014  *   the Free Software Foundation; either version 2 of the License, or     *
00015  *   (at your option) any later version.                                   *
00016  *                                                                         *
00017  ***************************************************************************/
00018 
00019 #include "keduvocdocument.h"
00020 
00021 #include <QtCore/QFileInfo>
00022 #include <QtCore/QTextStream>
00023 #include <QtCore/QtAlgorithms>
00024 #include <QtCore/QIODevice>
00025 
00026 #include <klocale.h>
00027 #include <kdebug.h>
00028 #include <kio/netaccess.h>
00029 #include <krandomsequence.h>
00030 #include <kfilterdev.h>
00031 
00032 #include "keduvocexpression.h"
00033 #include "keduvoclesson.h"
00034 #include "keduvocwordtype.h"
00035 #include "keduvockvtmlwriter.h"
00036 #include "keduvockvtml2writer.h"
00037 #include "keduvoccsvreader.h"
00038 #include "keduvoccsvwriter.h"
00039 #include "keduvockvtml2reader.h"
00040 #include "keduvocwqlreader.h"
00041 #include "keduvocpaukerreader.h"
00042 #include "keduvocvokabelnreader.h"
00043 #include "keduvocxdxfreader.h"
00044 
00045 #define WQL_IDENT      "WordQuiz"
00046 
00047 #define KVTML_EXT        "kvtml"
00048 #define CSV_EXT          "csv"
00049 #define TXT_EXT          "txt"
00050 #define WQL_EXT          "wql"
00051 
00052 class KEduVocDocument::KEduVocDocumentPrivate
00053 {
00054 public:
00055     KEduVocDocumentPrivate( KEduVocDocument* qq )
00056             : q( qq )
00057     {
00058         init();
00059     }
00060 
00061     ~KEduVocDocumentPrivate();
00062 
00063     void init();
00064 
00065     KEduVocDocument* q;
00066 
00067     bool                      m_dirty;
00068     KUrl                      m_url;
00069 
00070     // save these to document
00071     QList<KEduVocIdentifier>  m_identifiers;
00072 
00073     int                       m_currentLesson;
00074     QList<int>                m_extraSizeHints;
00075     QList<int>                m_sizeHints;
00076 
00077     QString                   m_generator;
00078     QString                   m_queryorg;
00079     QString                   m_querytrans;
00080     QList<KEduVocExpression>  m_vocabulary;
00081 
00082     QStringList               m_tenseDescriptions;
00083     QSet<QString>             m_usages;
00084     QString                   m_title;
00085     QString                   m_author;
00086     QString                   m_license;
00087     QString                   m_comment;
00088     QString                   m_version;
00089     QString                   m_csvDelimiter;
00090 
00094     QString                   m_category;
00095 
00096     // A map is too error prone. Lesson order is very important.
00097     QList<KEduVocLesson>      m_lessons;
00098 
00099     KEduVocWordType           m_wordTypes;
00100 };
00101 
00102 KEduVocDocument::KEduVocDocumentPrivate::~KEduVocDocumentPrivate()
00103 {
00104 }
00105 
00106 void KEduVocDocument::KEduVocDocumentPrivate::init()
00107 {
00108     m_lessons.clear();
00109     m_tenseDescriptions.clear();
00110     m_identifiers.clear();
00111     m_wordTypes.clear();
00112     m_extraSizeHints.clear();
00113     m_sizeHints.clear();
00114     m_vocabulary.clear();
00115     m_dirty = false;
00116     m_currentLesson = 0;
00117     m_queryorg = "";
00118     m_querytrans = "";
00119     m_url.setFileName( i18n( "Untitled" ) );
00120     m_title = "";
00121     m_author = "";
00122     m_comment = "";
00123     m_version = "";
00124     m_generator = "";
00125     m_csvDelimiter = QString( '\t' );
00126     m_usages.clear();
00127     m_license.clear();
00128     m_category.clear();
00129 }
00130 
00131 
00132 KEduVocDocument::KEduVocDocument( QObject *parent )
00133         : QObject( parent ), d( new KEduVocDocumentPrivate( this ) )
00134 {}
00135 
00136 
00137 KEduVocDocument::~KEduVocDocument()
00138 {
00139     delete d;
00140 }
00141 
00142 
00143 void KEduVocDocument::setModified( bool dirty )
00144 {
00145     d->m_dirty = dirty;
00146     emit docModified( d->m_dirty );
00147 }
00148 
00149 
00150 void KEduVocDocument::appendEntry( KEduVocExpression *expression )
00151 {
00152     insertEntry(expression, d->m_vocabulary.count());
00153 }
00154 
00155 
00156 void KEduVocDocument::insertEntry( KEduVocExpression *expression, int index )
00157 {
00158     d->m_vocabulary.insert( index, *expression );
00159 
00160     // now we need to go fix the entryids that are greater than index in the lessons
00161     for (int i = 0; i < d->m_lessons.size(); ++i)
00162     {
00163         d->m_lessons[i].incrementEntriesAbove(index);
00164     }
00165     // if the expression is added and the lesson already exists (not at doc loading time, but added later) make sure it ends up in the lesson as well.
00166     if ( expression->lesson() >= 0 && expression->lesson() < d->m_lessons.count() ) {
00167         d->m_lessons[expression->lesson()].addEntry(index);
00168     }
00169     setModified();
00170 }
00171 
00172 
00173 KEduVocDocument::FileType KEduVocDocument::detectFileType( const QString &fileName )
00174 {
00175     QIODevice * f = KFilterDev::deviceForFile( fileName );
00176     if ( !f->open( QIODevice::ReadOnly ) ) {
00177         kDebug(1100) << "Warning, could not open QIODevice for file: " << fileName;
00178         delete f;
00179         return Csv;
00180     }
00181 
00182     QTextStream ts( f );
00183     QString line1;
00184     QString line2;
00185 
00186     line1 = ts.readLine();
00187     if ( !ts.atEnd() ) {
00188         line2 = ts.readLine();
00189     }
00190 
00191     /*
00192      * Vokabeln.de files:
00193     The header seems to be something like this:
00194 
00195     "Name
00196     Lang1 - Lang2",123,234,456
00197     0
00198 
00199     or something longer:
00200 
00201     "Name
00202     Lang1 - Lang2
00203     [..]
00204     Blah, blah, blah...",123,234,456
00205     0
00206     */
00207 
00208     QString tmp;
00209 
00210     if ( line1.startsWith(QChar::fromLatin1('"'))) {
00211         ts.seek(0);
00212         tmp = ts.readLine();
00213         // There shouldn't be headers longer than 10 lines.
00214         for ( int x=0; x < 10; x++) {
00215             if (tmp.contains( "\"," )) {
00216                 tmp = ts.readLine();
00217                 if (tmp.endsWith('0')) {
00218                     f->close();
00219                     delete f;
00220                     return Vokabeln;
00221                 }
00222             }
00223             tmp = ts.readLine();
00224         }
00225     }
00226     f->close();
00227     delete f;
00228 
00229 
00230     if ( line1.startsWith(QString::fromLatin1("<?xml")) ) {
00231         if ( line2.indexOf( "pauker", 0 ) >  0 ) {
00232             return Pauker;
00233         }
00234         if ( line2.indexOf( "xdxf", 0 ) >  0 ) {
00235             return Xdxf;
00236         } else {
00237             return Kvtml;
00238         }
00239     }
00240 
00241     if ( line1 == WQL_IDENT ) {
00242         return Wql;
00243     }
00244 
00245     return Csv;
00246 }
00247 
00248 
00249 int KEduVocDocument::open( const KUrl& url )
00250 {
00251     d->init();
00252     if ( !url.isEmpty() ) {
00253         d->m_url = url;
00254     }
00255 
00256     bool read = false;
00257     QString errorMessage = i18n( "<qt>Cannot open file<br /><b>%1</b></qt>", url.path() );
00258     QString temporaryFile;
00259     if ( KIO::NetAccess::download( url, temporaryFile, 0 ) ) {
00260         QIODevice * f = KFilterDev::deviceForFile( temporaryFile );
00261 
00262         if ( !f->open( QIODevice::ReadOnly ) ) {
00263             kError() << errorMessage;
00264             return FileCannotRead;
00265         }
00266 
00267         FileType ft = detectFileType( temporaryFile );
00268 
00269         switch ( ft ) {
00270             case Kvtml: {
00271                 kDebug(1100) << "Reading KVTML document...";
00272                 KEduVocKvtml2Reader kvtmlReader( f );
00273                 read = kvtmlReader.readDoc( this );
00274                 if ( !read ) {
00275                     errorMessage = kvtmlReader.errorMessage();
00276                 }
00277             }
00278             break;
00279 
00280             case Wql: {
00281                 kDebug(1100) << "Reading WordQuiz (WQL) document...";
00282                 KEduVocWqlReader wqlReader( f );
00283                 d->m_url.setFileName( i18n( "Untitled" ) );
00284                 read = wqlReader.readDoc( this );
00285                 if ( !read ) {
00286                     errorMessage = wqlReader.errorMessage();
00287                 }
00288             }
00289             break;
00290 
00291             case Pauker: {
00292                 kDebug(1100) << "Reading Pauker document...";
00293                 KEduVocPaukerReader paukerReader( this );
00294                 d->m_url.setFileName( i18n( "Untitled" ) );
00295                 read = paukerReader.read( f );
00296                 if ( !read ) {
00297                     errorMessage = i18n( "Parse error at line %1, column %2:\n%3", paukerReader.lineNumber(), paukerReader.columnNumber(), paukerReader.errorString() );
00298                 }
00299             }
00300             break;
00301 
00302             case Vokabeln: {
00303                 kDebug(1100) << "Reading Vokabeln document...";
00304                 KEduVocVokabelnReader vokabelnReader( f );
00305                 d->m_url.setFileName( i18n( "Untitled" ) );
00306                 read = vokabelnReader.readDoc( this );
00307                 if ( !read ) {
00308                     errorMessage = vokabelnReader.errorMessage();
00309                 }
00310             }
00311             break;
00312 
00313             case Csv: {
00314                 kDebug(1100) << "Reading CVS document...";
00315                 KEduVocCsvReader csvReader( f );
00316                 read = csvReader.readDoc( this );
00317                 if ( !read ) {
00318                     errorMessage = csvReader.errorMessage();
00319                 }
00320             }
00321             break;
00322 
00323             case Xdxf: {
00324                 kDebug(1100) << "Reading XDXF document...";
00325                 KEduVocXdxfReader xdxfReader( this );
00326                 d->m_url.setFileName( i18n( "Untitled" ) );
00327                 read = xdxfReader.read( f );
00328                 if ( !read ) {
00329                     errorMessage = i18n( "Parse error at line %1, column %2:\n%3", xdxfReader.lineNumber(), xdxfReader.columnNumber(), xdxfReader.errorString() );
00330                 }
00331             }
00332             break;
00333 
00334             default: {
00335                 kDebug(1100) << "Reading KVTML document (fallback)...";
00336                 KEduVocKvtml2Reader kvtmlReader( f );
00337                 read = kvtmlReader.readDoc( this );
00338                 if ( !read ) {
00339                     errorMessage = kvtmlReader.errorMessage();
00340                 }
00341             }
00342         }
00343 
00344         if ( !read ) {
00345             QString msg = i18n( "Could not open or properly read \"%1\"\n(Error reported: %2)", url.path(), errorMessage );
00346             kError() << msg << i18n( "Error Opening File" );
00348             return FileReaderFailed;
00349         }
00350 
00351         f->close();
00352         delete f;
00353         KIO::NetAccess::removeTempFile( temporaryFile );
00354     }
00355 
00356     // Additional cleanup: Put entries without a lesson into a default lesson.
00357     int defaultLessonNumber = appendLesson(i18n("Default Lesson"));
00358     // now make sure we don't have any orphan entries (lesson -1)
00359     for (int i = 0; i < entryCount(); ++i)
00360     {
00361         if (entry(i)->lesson() == -1)
00362         {
00363             entry(i)->setLesson(defaultLessonNumber);
00364             lesson(defaultLessonNumber).addEntry(i);
00365         }
00366     }
00367     if (lesson(defaultLessonNumber).entries().size() == 0)
00368     {
00369         removeLesson(defaultLessonNumber, DeleteEmptyLesson);
00370     }
00371 
00372     if ( !read ) {
00373         return FileReaderFailed;
00374     }
00375 
00376     return 0;
00377 }
00378 
00379 
00380 int KEduVocDocument::saveAs( const KUrl & url, FileType ft, const QString & generator )
00381 {
00382     KUrl tmp( url );
00383 
00384     if ( ft == Automatic ) {
00385         if ( tmp.path().right( strlen( "." KVTML_EXT ) ) == "." KVTML_EXT )
00386             ft = Kvtml;
00387         else if ( tmp.path().right( strlen( "." CSV_EXT ) ) == "." CSV_EXT )
00388             ft = Csv;
00389         else {
00390             return FileTypeUnknown;
00391         }
00392     }
00393 
00394     QFile f( tmp.path() );
00395 
00396     if ( !f.open( QIODevice::WriteOnly ) ) {
00397         kError() << i18n( "Cannot write to file %1", tmp.path() );
00398         return FileCannotWrite;
00399     }
00400 
00401     bool saved = false;
00402 
00403     switch ( ft ) {
00404         case Kvtml: {
00405             // write version 2 file
00406             KEduVocKvtml2Writer kvtmlWriter( &f );
00407             saved = kvtmlWriter.writeDoc( this, generator );
00408         }
00409         break;
00410         case Kvtml1: {
00411             // write old version 1 file
00412             KEduVocKvtmlWriter kvtmlWriter( &f );
00413             saved = kvtmlWriter.writeDoc( this, generator );
00414         }
00415         break;
00416         case Csv: {
00417             KEduVocCsvWriter csvWriter( &f );
00418             saved = csvWriter.writeDoc( this, generator );
00419         }
00420         break;
00421         default: {
00422             kError() << "kvcotrainDoc::saveAs(): unknown filetype" << endl;
00423         }
00424         break;
00425     } // switch
00426 
00427     f.close();
00428 
00429     if ( !saved ) {
00430         kError() << "Error Saving File" << tmp.path();
00431         return FileWriterFailed;
00432     }
00433 
00434     d->m_url = tmp;
00435     setModified( false );
00436     return 0;
00437 }
00438 
00439 void KEduVocDocument::merge( KEduVocDocument *docToMerge, bool matchIdentifiers )
00440 {
00441     kDebug(1100) << "Merging of docs is not implemented"; 
00442     // This code was really horribly broken.
00443     // Now with the new classes we could attempt to reactivate it.
00444     // A rewrite might be easier.
00445 
00446     /*
00447       if (docToMerge) {
00448 
00449         QStringList new_names = docToMerge->lessonDescriptions();
00450 
00451         QStringList new_types = docToMerge->typeDescriptions();
00452 
00453         QStringList new_tenses = docToMerge->tenseDescriptions();
00454 
00455         QList<int> old_in_query = lessonsInPractice();
00456         QList<int> new_in_query = docToMerge->lessonsInPractice();
00457 
00458         QStringList new_usages = docToMerge->usageDescriptions();
00459 
00460         int lesson_offset = d->m_lessonDescriptions.count();
00461         for (int i = 0; i < new_names.count(); i++) {
00462           d->m_lessonDescriptions.append(new_names[i]);
00463         }
00464 
00465         for (int i = 0; i < new_in_query.count(); i++)
00466           old_in_query.append(new_in_query[i] + lesson_offset);
00467         setLessonsInPractice(old_in_query);
00468 
00469         int types_offset = d->m_typeDescriptions.count();
00470         for (int i = 0; i < new_types.count(); i++) {
00471           d->m_typeDescriptions.append(new_types[i]);
00472         }
00473 
00474         int tenses_offset = d->m_tenseDescriptions.count();
00475         for (int i = 0; i < new_tenses.count(); i++) {
00476           d->m_tenseDescriptions.append(new_tenses[i]);
00477         }
00478 
00479         int usages_offset = d->m_usageDescriptions.count();
00480         for (int i = 0; i < new_usages.count(); i++) {
00481           d->m_usageDescriptions.append(new_usages[i]);
00482         }
00483 
00484         bool equal = true;
00485         if (originalIdentifier() != docToMerge->originalIdentifier())
00486           equal = false;
00487         for (int i = 1; i < identifierCount(); i++)
00488           if (identifier(i) != docToMerge->identifier(i))
00489             equal = false;
00490 
00491         if (!matchIdentifiers)
00492           equal = true; ///@todo massive cheating, problem if docToMerge has more identifiers than this
00493 
00494         if (equal) {   // easy way: same language codes, just append
00495 
00496           for (int i = 0; i < docToMerge->entryCount(); i++) {
00497             KEduVocExpression *expr = docToMerge->entry(i);
00498 
00499             expr->setLesson(expr->lesson() + lesson_offset);
00500 
00501             for (int lang = 0; lang <= expr->translationCount(); lang++) {
00502               QString t = expr->translation(lang).type();
00503               // adjust type offset
00504               if (!t.isEmpty() && t.left(1) == QM_USER_TYPE) {
00505                 QString t2;
00506                 t.remove(0, 1);
00507                 t2.setNum(t.toInt() + types_offset);
00508                 t2.prepend(QM_USER_TYPE);
00509                 expr->translation(lang).setType (t2);
00510               }
00511 
00512               t = expr->translation(lang).usageLabel();
00513               // adjust usage offset
00514               QString tg;
00515               if (!t.isEmpty()) {
00516                 QString t2;
00517                 while (t.left(strlen(":")) == UL_USER_USAGE) {
00518                   QString n;
00519                   t.remove(0, 1);
00520                   int next;
00521                   if ((next = t.indexOf(":")) >= 0) {
00522                     n = t.left(next);
00523                     t.remove(0, next + 1);
00524                   }
00525                   else {
00526                     n = t;
00527                     t = "";
00528                   }
00529 
00530                   t2.setNum(n.toInt() + usages_offset);
00531                   t2.prepend(UL_USER_USAGE);
00532                   if (tg.length() == 0)
00533                     tg = t2;
00534                   else
00535                     tg += ':' + t2;
00536                 }
00537 
00538                 if (tg.length() == 0)
00539                   tg = t;
00540                 else if (t.length() != 0)
00541                   tg += ':' + t;
00542 
00543                 expr->translation(lang).setUsageLabel (tg);
00544               }
00545 
00546               KEduVocConjugation conj = expr->translation(lang).conjugation();
00547               bool condirty = false;
00548               for (int ci = 0; ci < conj.entryCount(); ci++) {
00549                 t = conj.getType(ci);
00550                 if (!t.isEmpty() && t.left(1) == UL_USER_TENSE) {
00551                   t.remove(0, strlen(UL_USER_TENSE));
00552                   QString t2;
00553                   t2.setNum(t.toInt() + tenses_offset);
00554                   t2.prepend(UL_USER_TENSE);
00555                   conj.setType(ci, t2);
00556                   condirty = true;
00557                 }
00558                 if (condirty)
00559                   expr->translation(lang).setConjugation(conj);
00560               }
00561             }
00562 
00563             appendEntry(expr);
00564           }
00565           setModified();
00566         }
00567         else { // hard way: move entries around, most attributes get lost
00568           QList<int> move_matrix;
00569           QList<bool> cs_equal;
00570           QString s;
00571 
00572           for (int i = 0; i < qMax (identifierCount(), docToMerge->identifierCount()); i++)
00573             cs_equal.append(false);
00574 
00575           move_matrix.append(docToMerge->indexOfIdentifier(originalIdentifier()));
00576           for (int i = 1; i < identifierCount(); i++)
00577             move_matrix.append(docToMerge->indexOfIdentifier(identifier(i)));
00578 
00579           for (int j = 0; j < docToMerge->entryCount(); j++) {
00580             KEduVocExpression new_expr;
00581             KEduVocExpression *expr = docToMerge->entry(j);
00582             new_expr.setLesson(expr->lesson()+lesson_offset);
00583 
00584             for (int i = 0; i < move_matrix.count(); i++) {
00585               int lpos = move_matrix[i];
00586               if (lpos >= 0) {
00587 
00588                 if (lpos == 0)
00589                   s = expr->original();
00590                 else
00591                   s = expr->translation(lpos);
00592 
00593                 if (!cs_equal[lpos]) {
00594                   cs_equal[lpos] = true;
00595                   QString id = lpos == 0 ? originalIdentifier() : identifier(lpos);
00596                 }
00597 
00598                 if (i == 0)
00599                   new_expr.setOriginal(s);
00600                 else
00601                   new_expr.setTranslation(i, s);
00602                 QString r = expr->remark(lpos);
00603                 new_expr.setRemark (i, r);
00604 
00605                 QString t = expr->type(lpos);
00606                 if (!t.isEmpty() && t.left(1) == QM_USER_TYPE) {
00607                   QString t2;
00608                   t.remove(0, 1);
00609                   t2.setNum(t.toInt() + types_offset);
00610                   t2.prepend(QM_USER_TYPE);
00611                   new_expr.setType(i, t2);
00612                 }
00613 
00614                 t = expr->usageLabel(lpos);
00615                 if (!t.isEmpty() && t.left(1) == QM_USER_TYPE) {
00616                   QString t2;
00617                   t.remove(0, 1);
00618                   t2.setNum(t.toInt() + usages_offset);
00619                   t2.prepend(QM_USER_TYPE);
00620                   new_expr.setUsageLabel(i, t2);
00621                 }
00622 
00623                 KEduVocConjugation conj = expr->conjugation(lpos);
00624                 for (int ci = 0; ci < conj.entryCount(); ci++) {
00625                   t = conj.getType(ci);
00626                   if (!t.isEmpty() && t.left(1) == QM_USER_TYPE) {
00627                     t.remove (0, strlen(QM_USER_TYPE));
00628                     QString t2;
00629                     t2.setNum(t.toInt() + tenses_offset);
00630                     t2.prepend(QM_USER_TYPE);
00631                     conj.setType(ci, t2);
00632                   }
00633                 }
00634 
00635               }
00636             }
00637             // only append if entries are used
00638             bool used = !new_expr.original().isEmpty();
00639             for (int i = 1; i < identifierCount(); i++)
00640               if (!new_expr.translation(i).isEmpty())
00641                 used = true;
00642 
00643             if (used) {
00644               appendEntry(&new_expr);
00645               setModified();
00646             }
00647           }
00648         }
00649       }
00650     */
00651 }
00652 
00653 
00654 KEduVocExpression *KEduVocDocument::entry( int index )
00655 {
00656     if ( index < 0 || index >= d->m_vocabulary.size() )
00657         return 0;
00658     else
00659         return &d->m_vocabulary[index];
00660 }
00661 
00662 
00663 void KEduVocDocument::removeEntry( int index )
00664 {
00665     if ( index >= 0 && index < d->m_vocabulary.size() ) {
00666         d->m_vocabulary.removeAt( index );
00667     }
00668 
00669     // now we need to go fix the entryids that are greater than index in the lessons
00670     for (int i = 0; i < d->m_lessons.size(); ++i)
00671     {
00672         d->m_lessons[i].decrementEntriesAbove(index);
00673     }
00674 }
00675 
00676 
00677 int KEduVocDocument::indexOfIdentifier( const QString& name ) const
00678 {
00679     for ( int i=0; i < d->m_identifiers.count(); i++ ) {
00680         if ( d->m_identifiers.value(i).name() == name ) {
00681             return i;
00682         }
00683     }
00684     return -1;
00685 }
00686 
00687 
00688 KEduVocIdentifier& KEduVocDocument::identifier( int index )
00689 {
00690     if ( index < 0 || index >= d->m_identifiers.size() ) {
00691         kError() << " Error: Invalid identifier index: " << index;
00692     }
00693     return d->m_identifiers[index];
00694 }
00695 
00696 
00697 void KEduVocDocument::setIdentifier( int idx, const KEduVocIdentifier &id )
00698 {
00699     if ( idx >= 0 && idx < d->m_identifiers.size() ) {
00700         d->m_identifiers[idx] = id;
00701     }
00702 }
00703 
00704 
00705 QString KEduVocDocument::tenseName( int index ) const
00706 {
00707     if ( index >= d->m_tenseDescriptions.size() )
00708         return "";
00709     else
00710         return d->m_tenseDescriptions[index];
00711 }
00712 
00713 
00714 void KEduVocDocument::setTenseName( int idx, QString &id )
00715 {
00716     if ( idx >= d->m_tenseDescriptions.size() )
00717         for ( int i = d->m_tenseDescriptions.size(); i <= idx; i++ )
00718             d->m_tenseDescriptions.append( "" );
00719 
00720     d->m_tenseDescriptions[idx] = id;
00721 }
00722 
00723 
00724 QStringList KEduVocDocument::tenseDescriptions() const
00725 {
00726     return d->m_tenseDescriptions;
00727 }
00728 
00729 
00730 void KEduVocDocument::setTenseDescriptions( const QStringList &names )
00731 {
00732     d->m_tenseDescriptions = names;
00733 }
00734 
00735 
00736 int KEduVocDocument::sizeHint( int idx ) const
00737 {
00738     if ( idx < 0 ) {
00739         idx = -idx;
00740         if ( idx >= d->m_extraSizeHints.size() )
00741             return 80; // make a good guess about column size
00742         else {
00743 //      cout << "gsh " << idx << "  " << extraSizehints[idx] << endl;
00744             return d->m_extraSizeHints[idx];
00745         }
00746     } else {
00747         if ( idx >= d->m_sizeHints.size() )
00748             return 150; // make a good guess about column size
00749         else {
00750 //      cout << "gsh " << idx << "  " << sizehints[idx] << endl;
00751             return d->m_sizeHints[idx];
00752         }
00753     }
00754 }
00755 
00756 
00757 void KEduVocDocument::setSizeHint( int idx, const int width )
00758 {
00759 //  cout << "ssh " << idx << "  " << width << endl;
00760     if ( idx < 0 ) {
00761         idx = -idx;
00762         if ( idx >= d->m_extraSizeHints.size() ) {
00763             for ( int i = d->m_extraSizeHints.size(); i <= idx; i++ )
00764                 d->m_extraSizeHints.append( 80 );
00765         }
00766         d->m_extraSizeHints[idx] = width;
00767 
00768     } else {
00769         if ( idx >= d->m_sizeHints.size() ) {
00770             for ( int i = d->m_sizeHints.size(); i <= idx; i++ )
00771                 d->m_sizeHints.append( 150 );
00772         }
00773         d->m_sizeHints[idx] = width;
00774     }
00775 }
00776 
00777 
00778 void KEduVocDocument::removeIdentifier( int index )
00779 {
00780     if ( index < d->m_identifiers.size() && index >= 0 ) {
00781         d->m_identifiers.removeAt( index );
00782         for ( int i = 0; i < d->m_vocabulary.count(); i++ ) {
00783             d->m_vocabulary[i].removeTranslation( index );
00784             for ( int j = index; j < d->m_identifiers.size(); j++ ) {
00785                 d->m_vocabulary[i].translation(j) = d->m_vocabulary[i].translation(j+1);
00786             }
00787         }
00788     }
00789 }
00790 
00791 
00792 bool KEduVocDocument::isModified() const
00793 {
00794     return d->m_dirty;
00795 }
00796 
00797 
00798 int KEduVocDocument::entryCount() const
00799 {
00800     return d->m_vocabulary.count();
00801 }
00802 
00803 
00804 void KEduVocDocument::resetEntry( int index, int lesson )
00805 {
00806     foreach(int entry, this->lesson(lesson).entries()) {
00807         d->m_vocabulary[entry].resetGrades( index );
00808     }
00809 }
00810 
00811 
00812 int KEduVocDocument::identifierCount() const
00813 {
00814     return d->m_identifiers.count();  // number of translations
00815 }
00816 
00817 int KEduVocDocument::appendIdentifier( const KEduVocIdentifier& id )
00818 {
00819     int i = d->m_identifiers.size();
00820 //kDebug(1100) << "appendIdentifier: " << i << id.name() << id.locale();
00821     d->m_identifiers.append( id );
00822     if ( id.name().isEmpty() ) {
00823         if ( i == 0 ) {
00824             identifier(i).setName(i18nc("The name of the first language/column of vocabulary, if we have to guess it.", "Original"));
00825         } else {
00826             identifier(i).setName(i18nc( "The name of the second, third ... language/column of vocabulary, if we have to guess it.", "Translation %1", i ) );
00827         }
00828     }
00829 
00830     if ( i > 0 ) {
00831         for (int j = 0; j < entryCount(); j++) {
00832             entry(j)->translation(i).setType(entry(j)->translation(0).type());
00833         }
00834     }
00835 
00836     return i;
00837 }
00838 
00839 
00840 int KEduVocDocument::appendLesson( const QString &lessonName, bool inPractice )
00841 {
00842     KEduVocLesson lesson;
00843     lesson.setName( lessonName );
00844     lesson.setInPractice( inPractice );
00845     d->m_lessons.append( lesson );
00846     return d->m_lessons.count() - 1;
00847 }
00848 
00849 QList<KEduVocLesson> & KEduVocDocument::lessons() const
00850 {
00851     return d->m_lessons;
00852 }
00853 
00854 KEduVocLesson & KEduVocDocument::lesson( int index )
00855 {
00856     return d->m_lessons[index];
00857 }
00858 
00859 
00860 KUrl KEduVocDocument::url() const
00861 {
00862     return d->m_url;
00863 }
00864 
00865 
00866 void KEduVocDocument::setUrl( const KUrl& url )
00867 {
00868     d->m_url = url;
00869 }
00870 
00871 
00872 QString KEduVocDocument::title() const
00873 {
00874     if ( d->m_title.isEmpty() )
00875         return d->m_url.fileName();
00876     else
00877         return d->m_title;
00878 }
00879 
00880 
00881 QString KEduVocDocument::author() const
00882 {
00883     return d->m_author;
00884 }
00885 
00886 
00887 QString KEduVocDocument::license() const
00888 {
00889     return d->m_license;
00890 }
00891 
00892 
00893 QString KEduVocDocument::documentComment() const
00894 {
00895     return d->m_comment;
00896 }
00897 
00898 void KEduVocDocument::setCategory( const QString & category )
00899 {
00900     d->m_category = category;
00901 }
00902 
00903 QString KEduVocDocument::category() const
00904 {
00905     return d->m_category;
00907 }
00908 
00909 void KEduVocDocument::queryIdentifier( QString &org, QString &trans ) const
00910 {
00911     org = d->m_queryorg;
00912     trans = d->m_querytrans;
00913 }
00914 
00915 
00916 void KEduVocDocument::setQueryIdentifier( const QString &org, const QString &trans )
00917 {
00918     d->m_queryorg = org;
00919     d->m_querytrans = trans;
00920 }
00921 
00922 
00923 void KEduVocDocument::setTitle( const QString & title )
00924 {
00925     d->m_title = title.simplified();
00926 }
00927 
00928 
00929 void KEduVocDocument::setAuthor( const QString & s )
00930 {
00931     d->m_author = s.simplified();
00932 }
00933 
00934 
00935 void KEduVocDocument::setLicense( const QString & s )
00936 {
00937     d->m_license = s.simplified();
00938 }
00939 
00940 
00941 void KEduVocDocument::setDocumentComment( const QString & s )
00942 {
00943     d->m_comment = s.trimmed();
00944 }
00945 
00946 
00947 void KEduVocDocument::setGenerator( const QString & generator )
00948 {
00949     d->m_generator = generator;
00950 }
00951 
00952 
00953 QString KEduVocDocument::generator() const
00954 {
00955     return d->m_generator;
00956 }
00957 
00958 
00959 QString KEduVocDocument::version() const
00960 {
00961     return d->m_version;
00962 }
00963 
00964 
00965 void KEduVocDocument::setVersion( const QString & vers )
00966 {
00967     d->m_version = vers;
00968 }
00969 
00970 
00971 int KEduVocDocument::currentLesson() const
00972 {
00973     return d->m_currentLesson;
00974 }
00975 
00976 
00977 void KEduVocDocument::setCurrentLesson( int lesson )
00978 {
00979     d->m_currentLesson = lesson;
00980 }
00981 
00982 
00983 QStringList KEduVocDocument::lessonNames() const
00984 {
00985     QStringList descriptions;
00986     foreach ( KEduVocLesson lesson, d->m_lessons ) {
00987         descriptions.append(lesson.name());
00988     }
00989     return descriptions;
00990 }
00991 
00992 int KEduVocDocument::lessonCount() const
00993 {
00994     return d->m_lessons.count();
00995 }
00996 
00997 bool KEduVocDocument::removeLesson( int lessonIndex, int deleteMode )
00998 {
00999     if (deleteMode == DeleteEmptyLesson) {
01000         if (d->m_lessons[lessonIndex].entryCount() > 0) {
01001             return false; // stop if there are vocabs left in the lesson
01002         }
01003     }
01004     else if (deleteMode == DeleteEntriesAndLesson) {
01005         while (d->m_lessons[lessonIndex].entryCount() > 0) {
01006             // get the next entryid
01007             int entry = d->m_lessons[lessonIndex].entries()[0];
01008             // take it out of this lesson
01009             d->m_lessons[lessonIndex].removeEntry(entry);
01010             // delete the entry from the document
01011             removeEntry(entry);
01012         }
01013     }
01014 
01015     // for all above this lesson number - reduce lesson by one.
01016     for ( int ent = 0; ent < entryCount(); ent++ ) {
01017         if ( entry( ent )->lesson() > lessonIndex ) {
01018             entry( ent )->setLesson( entry( ent )->lesson()-1 );
01019         }
01020     } // reduce lesson
01021 
01022     // finally just remove the lesson
01023     d->m_lessons.removeAt(lessonIndex);
01024 
01025     return true;
01026 }
01027 
01028 
01029 //void KEduVocDocument::setLessonDescriptions(const QStringList &names)
01030 //{
01031 //  d->m_lessonDescriptions = names;
01032 //}
01033 
01034 //void KEduVocDocument::moveLesson(int from, int to)
01035 //{
01037 //  // still counting from 1
01038 //  d->m_lessonDescriptions.move(from -1, to -1);
01039 
01040 //  /*
01041 //  to > from?
01042 //    lesson >= from && lesson < to: lesson++
01043 //  to < from?
01044 //    lesson >= to && lesson < from: lesson++
01045 //  */
01046 //  for (int ent = 0; ent < entryCount(); ent++) {
01047 //    // put from directly to to
01048 //    if (entry(ent)->lesson() == from) {
01049 //      entry(ent)->setLesson(to);
01050 //    }
01051 //    else
01052 //    {
01053 //      if(to > from)
01054 //      {
01055 //        if(entry(ent)->lesson() >= from && entry(ent)->lesson() < to)
01056 //          entry(ent)->setLesson(entry(ent)->lesson()-1);
01057 //      }
01058 //      else
01059 //      {
01060 //        if(entry(ent)->lesson() >= to && entry(ent)->lesson() < from)
01061 //          entry(ent)->setLesson(entry(ent)->lesson()+1);
01062 //      }
01063 //    }
01064 //  }
01065 //}
01066 
01067 
01068 QString KEduVocDocument::csvDelimiter() const
01069 {
01070     return d->m_csvDelimiter;
01071 }
01072 
01073 
01074 void KEduVocDocument::setCsvDelimiter( const QString &delimiter )
01075 {
01076     d->m_csvDelimiter = delimiter;
01077 }
01078 
01079 
01080 class ExpRef
01081 {
01082 
01083 public:
01084     ExpRef()
01085     {}
01086     ExpRef( KEduVocExpression *_exp, int _idx )
01087     {
01088         idx    = _idx;
01089         exp    = _exp;
01090     }
01091 
01092     bool operator< ( const ExpRef& y ) const
01093     {
01094         QString s1;
01095         QString s2;
01096         int cmp;
01097         foreach( int i, exp->translationIndices() ) {
01098 
01099             s1 = exp->translation( i ).text();
01100             s2 = y.exp->translation( i ).text();
01101             cmp = QString::compare( s1.toUpper(), s2.toUpper() );
01102             if ( cmp != 0 )
01103                 return cmp < 0;
01104         }
01105         return cmp < 0;
01106     }
01107 
01108     int idx;
01109     KEduVocExpression *exp;
01110 };
01111 
01112 typedef QList<ExpRef> ExpRefList;
01113 
01114 int KEduVocDocument::cleanUp()
01115 {
01116     int count = 0;
01117     KEduVocExpression *kve1, *kve2;
01118     ExpRefList shadow;
01119     QList<int> to_delete;
01120 
01121     for ( int i = 0; i < d->m_vocabulary.count(); i++ ) {
01122         shadow.append( ExpRef( entry( i ), i ) );
01123     }
01124     qStableSort( shadow.begin(), shadow.end() );
01125 
01126     int ent_no = 0;
01127     int ent_percent = d->m_vocabulary.size() / 100;
01128     float f_ent_percent = d->m_vocabulary.size() / 100.0;
01129     emit progressChanged( this, 0 );
01130 
01131     for ( int i = shadow.size() - 1; i > 0; i-- ) {
01132         kve1 = shadow[i].exp;
01133         kve2 = shadow[i - 1].exp;
01134 
01135         ent_no++;
01136         if ( ent_percent != 0 && ( ent_no % ent_percent ) == 0 ) {
01137             emit progressChanged( this, ( int )(( ent_no / f_ent_percent ) / 2.0 ) );
01138         }
01139 
01140         bool equal = true;
01141         for ( int l = 0; equal && l < identifierCount(); l++ ) {
01142             if ( kve1->translation( l ).text() != kve2->translation( l ).