00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
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 "keduvocleitnerbox.h"
00035 #include "keduvocwordtype.h"
00036 #include "keduvockvtmlwriter.h"
00037 #include "keduvockvtml2writer.h"
00038 #include "keduvoccsvreader.h"
00039 #include "keduvoccsvwriter.h"
00040 #include "keduvockvtml2reader.h"
00041 #include "keduvocwqlreader.h"
00042 #include "keduvocpaukerreader.h"
00043 #include "keduvocvokabelnreader.h"
00044 #include "keduvocxdxfreader.h"
00045
00046 #define WQL_IDENT "WordQuiz"
00047
00048 #define KVTML_EXT "kvtml"
00049 #define CSV_EXT "csv"
00050 #define TXT_EXT "txt"
00051 #define WQL_EXT "wql"
00052
00053 class KEduVocDocument::KEduVocDocumentPrivate
00054 {
00055 public:
00056 KEduVocDocumentPrivate( KEduVocDocument* qq )
00057 : q( qq )
00058 {
00059 m_lessonContainer = 0;
00060 m_wordTypeContainer = 0;
00061 m_leitnerContainer = 0;
00062 init();
00063 }
00064
00065 ~KEduVocDocumentPrivate();
00066
00067 void init();
00068
00069 KEduVocDocument* q;
00070
00071 bool m_dirty;
00072 KUrl m_url;
00073
00074
00075 QList<KEduVocIdentifier> m_identifiers;
00076
00077 QList<int> m_extraSizeHints;
00078 QList<int> m_sizeHints;
00079
00080 QString m_generator;
00081 QString m_queryorg;
00082 QString m_querytrans;
00083
00084 QStringList m_tenseDescriptions;
00085 QSet<QString> m_usages;
00086
00087 QString m_title;
00088 QString m_author;
00089 QString m_authorContact;
00090 QString m_license;
00091 QString m_comment;
00092 QString m_version;
00093 QString m_csvDelimiter;
00094
00098 QString m_category;
00099
00100 KEduVocLesson * m_lessonContainer;
00101 KEduVocWordType * m_wordTypeContainer;
00102 KEduVocLeitnerBox * m_leitnerContainer;
00103 };
00104
00105 KEduVocDocument::KEduVocDocumentPrivate::~KEduVocDocumentPrivate()
00106 {
00107 delete m_lessonContainer;
00108 delete m_wordTypeContainer;
00109 delete m_leitnerContainer;
00110 }
00111
00112 void KEduVocDocument::KEduVocDocumentPrivate::init()
00113 {
00114 delete m_lessonContainer;
00115 m_lessonContainer = new KEduVocLesson(i18nc("The top level lesson which contains all other lessons of the document.", "Document Lesson"));
00116 m_lessonContainer->setContainerType(KEduVocLesson::Lesson);
00117 delete m_wordTypeContainer;
00118 m_wordTypeContainer = new KEduVocWordType(i18n( "Word types" ));
00119
00120 if ( m_leitnerContainer ) {
00121 delete m_leitnerContainer;
00122 }
00123 m_leitnerContainer = new KEduVocLeitnerBox(i18n( "Leitner Box" ));
00124
00125 m_tenseDescriptions.clear();
00126 m_identifiers.clear();
00127 m_extraSizeHints.clear();
00128 m_sizeHints.clear();
00129 m_dirty = false;
00130 m_queryorg = "";
00131 m_querytrans = "";
00132 m_url.setFileName( i18n( "Untitled" ) );
00133 m_author = "";
00134 m_title = "";
00135 m_comment = "";
00136 m_version = "";
00137 m_generator = "";
00138 m_csvDelimiter = QString( '\t' );
00139 m_usages.clear();
00140 m_license.clear();
00141 m_category.clear();
00142 }
00143
00144
00145 KEduVocDocument::KEduVocDocument( QObject *parent )
00146 : QObject( parent ), d( new KEduVocDocumentPrivate( this ) )
00147 {
00148 kDebug() << "constructor done";
00149 }
00150
00151
00152 KEduVocDocument::~KEduVocDocument()
00153 {
00154 delete d;
00155 }
00156
00157
00158 void KEduVocDocument::setModified( bool dirty )
00159 {
00160 d->m_dirty = dirty;
00161 emit docModified( d->m_dirty );
00162 }
00163
00164
00165 KEduVocDocument::FileType KEduVocDocument::detectFileType( const QString &fileName )
00166 {
00167 QIODevice * f = KFilterDev::deviceForFile( fileName );
00168 if ( !f->open( QIODevice::ReadOnly ) ) {
00169 kDebug(1100) << "Warning, could not open QIODevice for file: " << fileName;
00170 delete f;
00171 return Csv;
00172 }
00173
00174 QTextStream ts( f );
00175 QString line1;
00176 QString line2;
00177
00178 line1 = ts.readLine();
00179 if ( !ts.atEnd() ) {
00180 line2 = ts.readLine();
00181 }
00182
00183
00184
00185
00186
00187
00188
00189
00190
00191
00192
00193
00194
00195
00196
00197
00198
00199
00200 QString tmp;
00201
00202 if ( line1.startsWith(QChar::fromLatin1('"'))) {
00203 ts.seek(0);
00204 tmp = ts.readLine();
00205
00206 for ( int x=0; x < 10; x++) {
00207 if (tmp.contains( "\"," )) {
00208 tmp = ts.readLine();
00209 if (tmp.endsWith('0')) {
00210 f->close();
00211 delete f;
00212 return Vokabeln;
00213 }
00214 }
00215 tmp = ts.readLine();
00216 }
00217 }
00218 f->close();
00219 delete f;
00220
00221
00222 if ( line1.startsWith(QString::fromLatin1("<?xml")) ) {
00223 if ( line2.indexOf( "pauker", 0 ) > 0 ) {
00224 return Pauker;
00225 }
00226 if ( line2.indexOf( "xdxf", 0 ) > 0 ) {
00227 return Xdxf;
00228 } else {
00229 return Kvtml;
00230 }
00231 }
00232
00233 if ( line1 == WQL_IDENT ) {
00234 return Wql;
00235 }
00236
00237 return Csv;
00238 }
00239
00240
00241 int KEduVocDocument::open( const KUrl& url )
00242 {
00243
00244 QString csv = d->m_csvDelimiter;
00245
00246 d->init();
00247 if ( !url.isEmpty() ) {
00248 d->m_url = url;
00249 }
00250 d->m_csvDelimiter = csv;
00251
00252 bool read = false;
00253 QString errorMessage = i18n( "<qt>Cannot open file<br /><b>%1</b></qt>", url.path() );
00254 QString temporaryFile;
00255 if ( KIO::NetAccess::download( url, temporaryFile, 0 ) ) {
00256 QIODevice * f = KFilterDev::deviceForFile( temporaryFile );
00257
00258 if ( !f->open( QIODevice::ReadOnly ) ) {
00259 kError() << errorMessage;
00260 delete f;
00261 return FileCannotRead;
00262 }
00263
00264 FileType ft = detectFileType( temporaryFile );
00265
00266 switch ( ft ) {
00267 case Kvtml: {
00268 kDebug(1100) << "Reading KVTML document...";
00269 KEduVocKvtml2Reader kvtmlReader( f );
00270 read = kvtmlReader.readDoc( this );
00271 if ( !read ) {
00272 errorMessage = kvtmlReader.errorMessage();
00273 }
00274 }
00275 break;
00276
00277 case Wql: {
00278 kDebug(1100) << "Reading WordQuiz (WQL) document...";
00279 KEduVocWqlReader wqlReader( f );
00280 d->m_url.setFileName( i18n( "Untitled" ) );
00281 read = wqlReader.readDoc( this );
00282 if ( !read ) {
00283 errorMessage = wqlReader.errorMessage();
00284 }
00285 }
00286 break;
00287
00288 case Pauker: {
00289 kDebug(1100) << "Reading Pauker document...";
00290 KEduVocPaukerReader paukerReader( this );
00291 d->m_url.setFileName( i18n( "Untitled" ) );
00292 read = paukerReader.read( f );
00293 if ( !read ) {
00294 errorMessage = i18n( "Parse error at line %1, column %2:\n%3", paukerReader.lineNumber(), paukerReader.columnNumber(), paukerReader.errorString() );
00295 }
00296 }
00297 break;
00298
00299 case Vokabeln: {
00300 kDebug(1100) << "Reading Vokabeln document...";
00301 KEduVocVokabelnReader vokabelnReader( f );
00302 d->m_url.setFileName( i18n( "Untitled" ) );
00303 read = vokabelnReader.readDoc( this );
00304 if ( !read ) {
00305 errorMessage = vokabelnReader.errorMessage();
00306 }
00307 }
00308 break;
00309
00310 case Csv: {
00311 kDebug(1100) << "Reading CVS document...";
00312 KEduVocCsvReader csvReader( f );
00313 read = csvReader.readDoc( this );
00314 if ( !read ) {
00315 errorMessage = csvReader.errorMessage();
00316 }
00317 }
00318 break;
00319
00320 case Xdxf: {
00321 kDebug(1100) << "Reading XDXF document...";
00322 KEduVocXdxfReader xdxfReader( this );
00323 d->m_url.setFileName( i18n( "Untitled" ) );
00324 read = xdxfReader.read( f );
00325 if ( !read ) {
00326 errorMessage = i18n( "Parse error at line %1, column %2:\n%3", xdxfReader.lineNumber(), xdxfReader.columnNumber(), xdxfReader.errorString() );
00327 }
00328 }
00329 break;
00330
00331 default: {
00332 kDebug(1100) << "Reading KVTML document (fallback)...";
00333 KEduVocKvtml2Reader kvtmlReader( f );
00334 read = kvtmlReader.readDoc( this );
00335 if ( !read ) {
00336 errorMessage = kvtmlReader.errorMessage();
00337 }
00338 }
00339 }
00340
00341 if ( !read ) {
00342 QString msg = i18n( "Could not open or properly read \"%1\"\n(Error reported: %2)", url.path(), errorMessage );
00343 kError() << msg << i18n( "Error Opening File" );
00345 delete f;
00346 return FileReaderFailed;
00347 }
00348
00349 f->close();
00350 delete f;
00351 KIO::NetAccess::removeTempFile( temporaryFile );
00352 }
00353
00354 if ( !read ) {
00355 return FileReaderFailed;
00356 }
00357
00358 return 0;
00359 }
00360
00361
00362 int KEduVocDocument::saveAs( const KUrl & url, FileType ft, const QString & generator )
00363 {
00364 KUrl tmp( url );
00365
00366 if ( ft == Automatic ) {
00367 if ( tmp.path().right( strlen( "." KVTML_EXT ) ) == "." KVTML_EXT )
00368 ft = Kvtml;
00369 else if ( tmp.path().right( strlen( "." CSV_EXT ) ) == "." CSV_EXT )
00370 ft = Csv;
00371 else {
00372 return FileTypeUnknown;
00373 }
00374 }
00375
00376 QFile f( tmp.path() );
00377
00378 if ( !f.open( QIODevice::WriteOnly ) ) {
00379 kError() << i18n( "Cannot write to file %1", tmp.path() );
00380 return FileCannotWrite;
00381 }
00382
00383 bool saved = false;
00384
00385 switch ( ft ) {
00386 case Kvtml: {
00387
00388 KEduVocKvtml2Writer kvtmlWriter( &f );
00389 saved = kvtmlWriter.writeDoc( this, generator );
00390 }
00391 break;
00393
00394
00395
00396
00397
00398
00399 case Csv: {
00400 KEduVocCsvWriter csvWriter( &f );
00401 saved = csvWriter.writeDoc( this, generator );
00402 }
00403 break;
00404 default: {
00405 kError() << "kvcotrainDoc::saveAs(): unknown filetype" << endl;
00406 }
00407 break;
00408 }
00409
00410 f.close();
00411
00412 if ( !saved ) {
00413 kError() << "Error Saving File" << tmp.path();
00414 return FileWriterFailed;
00415 }
00416
00417 d->m_url = tmp;
00418 setModified( false );
00419 return 0;
00420 }
00421
00422 QByteArray KEduVocDocument::toByteArray(const QString &generator)
00423 {
00424
00425 KEduVocKvtml2Writer kvtmlWriter(0);
00426 return kvtmlWriter.toByteArray( this, generator );
00427 }
00428
00429 void KEduVocDocument::merge( KEduVocDocument *docToMerge, bool matchIdentifiers )
00430 {
00431 Q_UNUSED(docToMerge)
00432 Q_UNUSED(matchIdentifiers)
00433 kDebug(1100) << "Merging of docs is not implemented";
00434
00435
00436
00437
00438
00439
00440
00441
00442
00443
00444
00445
00446
00447
00448
00449
00450
00451
00452
00453
00454
00455
00456
00457
00458
00459
00460
00461
00462
00463
00464
00465
00466
00467
00468
00469
00470
00471
00472
00473
00474
00475
00476
00477
00478
00479
00480
00481
00482
00483
00484
00485
00486
00487
00488
00489
00490
00491
00492
00493
00494
00495
00496
00497
00498
00499
00500
00501
00502
00503
00504
00505
00506
00507
00508
00509
00510
00511
00512
00513
00514
00515
00516
00517
00518
00519
00520
00521
00522
00523
00524
00525
00526
00527
00528
00529
00530
00531
00532
00533
00534
00535
00536
00537
00538
00539
00540
00541
00542
00543
00544
00545
00546
00547
00548
00549
00550
00551
00552
00553
00554
00555
00556
00557
00558
00559
00560
00561
00562
00563
00564
00565
00566
00567
00568
00569
00570
00571
00572
00573
00574
00575
00576
00577
00578
00579
00580
00581
00582
00583
00584
00585
00586
00587
00588
00589
00590
00591
00592
00593
00594
00595
00596
00597
00598
00599
00600
00601
00602
00603
00604
00605
00606
00607
00608
00609
00610
00611
00612
00613
00614
00615
00616
00617
00618
00619
00620
00621
00622
00623
00624
00625
00626
00627
00628
00629
00630
00631
00632
00633
00634
00635
00636
00637
00638
00639
00640
00641
00642
00643 }
00644
00645 const KEduVocIdentifier& KEduVocDocument::identifier( int index ) const
00646 {
00647 if ( index < 0 || index >= d->m_identifiers.size() ) {
00648 kError() << " Error: Invalid identifier index: " << index;
00649 }
00650 return d->m_identifiers.value(index);
00651 }
00652
00653 KEduVocIdentifier& KEduVocDocument::identifier( int index )
00654 {
00655 if ( index < 0 || index >= d->m_identifiers.size() ) {
00656 kError() << " Error: Invalid identifier index: " << index;
00657 }
00658 return d->m_identifiers[index];
00659 }
00660
00661 void KEduVocDocument::setIdentifier( int idx, const KEduVocIdentifier &id )
00662 {
00663 if ( idx >= 0 && idx < d->m_identifiers.size() ) {
00664 d->m_identifiers[idx] = id;
00665 }
00666 setModified(true);
00667 }
00668
00669
00670 int KEduVocDocument::indexOfIdentifier( const QString &name ) const
00671 {
00672 for (int i = 0; i < identifierCount(); i++)
00673 if (identifier(i).locale() == name)
00674 return i;
00675 return -1;
00676 }
00677
00678 void KEduVocDocument::removeIdentifier( int index )
00679 {
00680 if ( index < d->m_identifiers.size() && index >= 0 ) {
00681 d->m_identifiers.removeAt( index );
00682 d->m_lessonContainer->removeTranslation( index );
00683 }
00684 }
00685
00686
00687 bool KEduVocDocument::isModified() const
00688 {
00689 return d->m_dirty;
00690 }
00691
00692
00693 int KEduVocDocument::identifierCount() const
00694 {
00695 return d->m_identifiers.count();
00696 }
00697
00698 int KEduVocDocument::appendIdentifier( const KEduVocIdentifier& id )
00699 {
00700 int i = d->m_identifiers.size();
00701
00702 d->m_identifiers.append( id );
00703 if ( id.name().isEmpty() ) {
00704 if ( i == 0 ) {
00705 identifier(i).setName(i18nc("The name of the first language/column of vocabulary, if we have to guess it.", "Original"));
00706 } else {
00707 identifier(i).setName(i18nc( "The name of the second, third ... language/column of vocabulary, if we have to guess it.", "Translation %1", i ) );
00708 }
00709 }
00710
00711
00712 return i;
00713 }
00714
00715 KEduVocLesson * KEduVocDocument::lesson()
00716 {
00717 return d->m_lessonContainer;
00718 }
00719
00720 KEduVocWordType * KEduVocDocument::wordTypeContainer()
00721 {
00722 return d->m_wordTypeContainer;
00723 }
00724
00725 KEduVocLeitnerBox * KEduVocDocument::leitnerContainer()
00726 {
00727 return d->m_leitnerContainer;
00728 }
00729
00730 KUrl KEduVocDocument::url() const
00731 {
00732 return d->m_url;
00733 }
00734
00735 void KEduVocDocument::setUrl( const KUrl& url )
00736 {
00737 d->m_url = url;
00738 }
00739
00740 QString KEduVocDocument::title() const
00741 {
00742 if ( d->m_title.isEmpty() )
00743 return d->m_url.fileName();
00744 else
00745 return d->m_title;
00746 }
00747
00748 void KEduVocDocument::setTitle( const QString & title )
00749 {
00750 d->m_title = title;
00751 d->m_lessonContainer->setName(title);
00752 setModified(true);
00753 }
00754
00755 QString KEduVocDocument::author() const
00756 {
00757 return d->m_author;
00758 }
00759
00760 void KEduVocDocument::setAuthor( const QString & s )
00761 {
00762 d->m_author = s.simplified();
00763 setModified(true);
00764 }
00765
00766 QString KEduVocDocument::authorContact() const
00767 {
00768 return d->m_authorContact;
00769 }
00770
00771 void KEduVocDocument::setAuthorContact( const QString & s )
00772 {
00773 d->m_authorContact = s.simplified();
00774 setModified(true);
00775 }
00776
00777 QString KEduVocDocument::license() const
00778 {
00779 return d->m_license;
00780 }
00781
00782 QString KEduVocDocument::documentComment() const
00783 {
00784 return d->m_comment;
00785 }
00786
00787 void KEduVocDocument::setCategory( const QString & category )
00788 {
00789 d->m_category = category;
00790 setModified(true);
00791 }
00792
00793 QString KEduVocDocument::category() const
00794 {
00795 return d->m_category;
00797 }
00798
00799 void KEduVocDocument::queryIdentifier( QString &org, QString &trans ) const
00800 {
00801 org = d->m_queryorg;
00802 trans = d->m_querytrans;
00803 }
00804
00805 void KEduVocDocument::setQueryIdentifier( const QString &org, const QString &trans )
00806 {
00807 d->m_queryorg = org;
00808 d->m_querytrans = trans;
00809 setModified(true);
00810 }
00811
00812 void KEduVocDocument::setLicense( const QString & s )
00813 {
00814 d->m_license = s.simplified();
00815 setModified(true);
00816 }
00817
00818 void KEduVocDocument::setDocumentComment( const QString & s )
00819 {
00820 d->m_comment = s.trimmed();
00821 setModified(true);
00822 }
00823
00824 void KEduVocDocument::setGenerator( const QString & generator )
00825 {
00826 d->m_generator = generator;
00827 setModified(true);
00828 }
00829
00830 QString KEduVocDocument::generator() const
00831 {
00832 return d->m_generator;
00833 }
00834
00835 QString KEduVocDocument::version() const
00836 {
00837 return d->m_version;
00838 }
00839
00840 void KEduVocDocument::setVersion( const QString & vers )
00841 {
00842 d->m_version = vers;
00843 setModified(true);
00844 }
00845
00846 QString KEduVocDocument::csvDelimiter() const
00847 {
00848 return d->m_csvDelimiter;
00849 }
00850
00851 void KEduVocDocument::setCsvDelimiter( const QString &delimiter )
00852 {
00853 d->m_csvDelimiter = delimiter;
00854 setModified(true);
00855 }
00856
00857
00858 QString KEduVocDocument::pattern( FileDialogMode mode )
00859 {
00860 static const struct SupportedFilter {
00861 bool reading;
00862 bool writing;
00863 const char* extensions;
00864 const char* description;
00865 }
00866 filters[] = {
00867 { true, true, "*.kvtml", I18N_NOOP( "KDE Vocabulary Document" ) },
00868 { true, false, "*.wql", I18N_NOOP( "KWordQuiz Document" ) },
00869 { true, false, "*.xml.qz *.pau.gz", I18N_NOOP( "Pauker Lesson" ) },
00870 { true, false, "*.voc", I18N_NOOP( "Vokabeltrainer" ) },
00871 { true, false, "*.xdxf", I18N_NOOP( "XML Dictionary Exchange Format" ) },
00872 { true, true, "*.csv", I18N_NOOP( "Comma Separated Values (CSV)" ) },
00873
00874 { false, false, 0, 0 }
00875 };
00876 QStringList newfilters;
00877 QStringList allext;
00878 for ( int i = 0; filters[i].extensions; ++i ) {
00879 if (( mode == Reading && filters[i].reading ) ||
00880 ( mode == Writing && filters[i].writing ) ) {
00881 newfilters.append( QLatin1String( filters[i].extensions ) + '|' + i18n( filters[i].description ) );
00882 allext.append( QLatin1String( filters[i].extensions ) );
00883 }
00884 }
00885 if ( mode == Reading ) {
00886 newfilters.prepend( allext.join( " " ) + '|' + i18n( "All supported documents" ) );
00887 }
00888 return newfilters.join( "\n" );
00889 }
00890
00891 QString KEduVocDocument::errorDescription( int errorCode )
00892 {
00893 switch (errorCode) {
00894 case NoError:
00895 return i18n("No error found.");
00896
00897 case InvalidXml:
00898 return i18n("Invalid XML in document.");
00899 case FileTypeUnknown:
00900 return i18n("Unknown file type.");
00901 case FileCannotWrite:
00902 return i18n("File is not writeable.");
00903 case FileWriterFailed:
00904 return i18n("File writer failed.");
00905 case FileCannotRead:
00906 return i18n("File is not readable.");
00907 case FileReaderFailed:
00908 return i18n("The file reader failed.");
00909 case FileDoesNotExist:
00910 return i18n("The file does not exist.");
00911 case Unknown:
00912 default:
00913 return i18n("Unknown error.");
00914 }
00915 }
00916
00917 #include "keduvocdocument.moc"
00918