Controller.cpp

Go to the documentation of this file.
00001 #include "Controller.h"
00002 
00003 const QString Controller::vocabTreeFilename = QString( "tree" ); 
00004 
00005 Controller::Controller() : vocabTree( NULL ), scheduler( prefs ) {
00006     applicationDirName = QDir::homeDirPath() + QString( "/.toMOTko" );
00007     markedXmlFilename = applicationDirName + QString( "/marked.xml" );
00008     markedFilename = applicationDirName + QString( "/marked.dat.z" );
00009     prefs.setApplicationDirName( applicationDirName );
00010     scheduler.setApplicationDirName( applicationDirName );
00011 }
00012 
00013 bool Controller::init() {
00014     QDir applDir( applicationDirName );
00015     if( !applDir.exists() ) {
00016         if( !applDir.mkdir( applicationDirName ) )
00017             return( false );
00018     }
00019     getPreferences().load();
00020     return( true ); 
00021 }
00022 
00023 const QString& Controller::getApplicationDirName() const {
00024     return( applicationDirName );
00025 }
00026 
00027 Preferences& Controller::getPreferences() {
00028     return( prefs );
00029 }
00030 
00031 Folder* Controller::getVocabTree() {
00032     return( vocabTree );
00033 }
00034 
00035 void Controller::startQuiz() {
00036     scheduler.init( getPreferences().getFirstLanguage(), getPreferences().getTestLanguage(), vocabTree );
00037 }
00038 
00039 void Controller::restartQuiz() {
00040     scheduler.reinit();
00041 }
00042 
00043 bool Controller::isResumableQuizAvailable() {
00044     return( scheduler.isResumableQuizAvailable( BilingualKey( getPreferences().getFirstLanguage(), getPreferences().getTestLanguage() ) ) );
00045 }
00046 
00047 bool Controller::resumeQuiz() {
00048     BilingualKey key( getPreferences().getFirstLanguage(), getPreferences().getTestLanguage() );
00049     return( scheduler.load( key ) );
00050 }
00051 
00052 void Controller::prepareQuiz() {
00053     emit( progressChanged( getProgress() ) );
00054 }
00055 
00056 void Controller::concludeQuiz() {
00057     scheduler.concludeQuiz();
00058 }
00059 
00060 Term* Controller::getCurrentTerm() {
00061     TermKey key = scheduler.getCurrentTerm();
00062     Vocabulary* vocab = vocabTree->getVocabulary( key.getVocabId() );
00063     if( vocab && !vocab->isMarkedForDeletion() && vocab->isTermExists( key.getTermId() ) ) {
00064         Term& term = vocab->getTerm( key.getTermId() );
00065         return( &term );
00066     }
00067     // The term is invalid.  Either the container vocabulary or the term itself have been deleted.
00068     // Let's try to fetch the next term.
00069     scheduler.discardCurrentTerm();
00070     return( getNextTerm() );
00071 }
00072 
00073 Term* Controller::getTerm( const TermKey& termKey ) {
00074     Vocabulary* vocab = vocabTree->getVocabulary( termKey.getVocabId() );
00075     if( vocab && !vocab->isMarkedForDeletion() && vocab->isTermExists( termKey.getTermId() ) ) {
00076         Term& term = vocab->getTerm( termKey.getTermId() );
00077         return( &term );
00078     }
00079     return( NULL );
00080 }
00081 
00082 Term* Controller::getNextTerm() {
00083     TermKey nextTerm = scheduler.getNextTerm();
00084     if( nextTerm.isNull() )
00085         return( NULL );
00086 
00087     TermKey currTerm = scheduler.getCurrentTerm();
00088     Term* term = getTerm( currTerm );
00089     if( term )
00090         return( term );
00091 
00092     // The term is invalid.  Either the container vocabulary or the term itself have been deleted.
00093     // Let's try to fetch the next term.
00094     scheduler.discardCurrentTerm();
00095     return( scheduler.hasNextTerm() ? getNextTerm() : NULL );
00096 }
00097 
00098 bool Controller::hasNextTerm() const {
00099     return( scheduler.hasNextTerm() );
00100 }
00101 
00102 QString Controller::getQuizFirstLanguage() const {
00103     return( scheduler.getQuizFirstLanguage() );
00104 }
00105 
00106 QString Controller::getQuizTestLanguage() const {
00107     return( scheduler.getQuizTestLanguage() );
00108 }
00109 
00110 bool Controller::isQuizInProgress() const {
00111     return( scheduler.isQuizInProgress() );
00112 }
00113 
00114 int Controller::getProgress() const {
00115     return( scheduler.getProgress() );
00116 }
00117 
00118 int Controller::getInitialTermCount() const {
00119     return( scheduler.getInitialTermCount() );
00120 }
00121 
00122 Sequence& Controller::getRevealingSequence() {
00123     return( currRevealingSeq );
00124 }
00125 
00126 int Controller::getRevealingSequenceStep() const {
00127     return( currRevealingSeqStep );
00128 }
00129 
00130 void Controller::incrementRevealingSequenceStep() {
00131     currRevealingSeqStep++;
00132 }
00133 
00134 Folder* Controller::addFolder( Folder* parentFolder, Folder* folder = NULL ) {
00135     int newId = parentFolder->getRoot()->getMaxId() + 1;
00136 
00137     QString folderLocation = applicationDirName + "/" + parentFolder->getPath() /*+ "/" + QString::number( newId )*/;
00138     QDir folderDir( folderLocation );
00139     if( !folderDir.exists() ) {
00140         if( !Util::makeDirectory( folderDir.path() ) ) {
00141             cerr << "Cannot create directory " << folderLocation << endl;
00142             return( NULL );
00143         }
00144     }
00145 
00146     QString newTitle = ( folder ? folder->getTitle() : QObject::tr( "NewFolder" ) );
00147     Folder* newFolder = new Folder( newId, newTitle );
00148     newFolder->setAuthor( folder ? folder->getAuthor() : parentFolder->getAuthor() );
00149     if( folder ) {
00150         newFolder->setDescription( folder->getDescription() );
00151         newFolder->setCreationDate( folder->getCreationDate() );
00152         newFolder->setModificationDate( folder->getModificationDate() );
00153     }
00154     newFolder->setDirty( true ); // Force saving data.
00155     if( !saveFolder( newFolder, folderLocation ) ) {
00156         cerr << "Could not save the new folder." << endl;
00157         return( NULL );
00158     }
00159     parentFolder->add( newFolder );
00160     return( newFolder );
00161 }
00162 
00163 Vocabulary* Controller::addVocabulary( Folder* parentFolder, Vocabulary* vocab = NULL ) {
00164     int newId = parentFolder->getRoot()->getMaxVocabId() + 1;
00165 
00166     QString vocabLocation = applicationDirName + "/" + parentFolder->getPath() + "/v-" + QString::number( newId );
00167     QDir vocabDir( vocabLocation );
00168     if( !vocabDir.exists() ) {
00169         if( !Util::makeDirectory( vocabDir.path() ) ) {
00170             cerr << "Cannot create directory " << vocabLocation << endl;
00171             return( NULL );
00172         }
00173     }
00174 
00175     QString newTitle = ( vocab ? vocab->getTitle() : QObject::tr( "NewGlossary" ) );
00176     Vocabulary* newVocab = new Vocabulary( newId, newTitle );
00177     newVocab->setAuthor( vocab ? vocab->getAuthor() : parentFolder->getAuthor() );
00178     if( vocab ) {
00179         newVocab->setDescription( vocab->getDescription() );
00180         newVocab->setCreationDate( vocab->getCreationDate() );
00181         newVocab->setModificationDate( vocab->getModificationDate() );
00182         for( Vocabulary::TermMap::ConstIterator it = vocab->begin(); it != vocab->end(); it++ ) {
00183             const Term& term = *it;
00184             Term newTerm( newVocab->getMaxTermId() + 1, newVocab->getId() );
00185             for( Term::TranslationMap::ConstIterator it2 = term.translationsBegin(); it2 != term.translationsEnd(); it2++ ) {
00186                 const Translation& trans = it2.data();
00187                 newTerm.addTranslation( trans );
00188             }
00189             for( Term::CommentMap::ConstIterator it3 = term.commentsBegin(); it3 != term.commentsEnd(); it3++ ) {
00190                 const BilingualKey& key = it3.key();
00191                 const QString& comment = it3.data();
00192                 newTerm.addComment( key, comment );
00193             }
00194             if( !term.getImagePath().isNull() ) {
00195                 // If the path refers to a file outside toMOTko's directory, we just copy the path.
00196                 // Otherwise, we copy the image as well as the container vocabulary may be moved or deleted ulteriorly.
00197                 if( term.getImagePath().left( getApplicationDirName().length() ) == getApplicationDirName() ) {
00198                     QFileInfo imageToCopyInfo( term.getImagePath() );
00199                     QString imageFilename = imageToCopyInfo.fileName();
00200                     if( Util::copy( term.getImagePath(), vocabLocation + "/" + imageFilename ) )
00201                         newTerm.setImagePath( imageFilename ); 
00202                     else
00203                         cerr << "Could not copy " << term.getImagePath() << " to " << ( vocabLocation + "/" + imageFilename ) << endl;
00204                 }
00205                 else
00206                     newTerm.setImagePath( term.getImagePath() ); 
00207             }
00208             newVocab->addTerm( newTerm );
00209         }
00210         newVocab->setDirty( true ); // Force saving data.
00211     }
00212     if( !saveVocabulary( newVocab, vocabLocation ) ) {
00213         cerr << "Could not save the new vocabulary." << endl;
00214         return( NULL );
00215     }
00216     parentFolder->add( newVocab );
00217     return( newVocab );
00218 }
00219 
00220 void Controller::copy( Vocabulary* vocab ) {
00221     QString firstLang( getPreferences().getFirstLanguage() );
00222     QString testLang( getPreferences().getTestLanguage() );
00223     Vocabulary* vocabCopy = makeCopy( vocab, firstLang, testLang );
00224 
00225     QByteArray data;
00226     QDataStream out( data, IO_WriteOnly );
00227     out << *vocabCopy;
00228     setClipboardData( QString( "vocabulary" ), Util::qCompress( data ) );
00229 }
00230 
00231 void Controller::copy( Folder* folder ) {
00232     QString firstLang( getPreferences().getFirstLanguage() );
00233     QString testLang( getPreferences().getTestLanguage() );
00234     Folder* folderCopy = makeCopy( folder, firstLang, testLang );
00235 
00236     QByteArray data;
00237     QDataStream out( data, IO_WriteOnly );
00238     out << *folderCopy;
00239 
00240     // The Folder::operator<< will only write references for vocabularies.
00241     // We build a map containing copies of the vocabularies.
00242     
00243     QMap<int,Vocabulary> vocabularies;
00244     folderCopy->buildVocabCopiesMap( vocabularies );
00245     out << vocabularies;
00246 
00247     setClipboardData( QString( "folder" ), Util::qCompress( data ) );
00248 }
00249 
00250 Vocabulary* Controller::makeCopy( Vocabulary* vocab, const QString& firstLang, const QString& testLang ) const {
00251     QStringList languages;
00252     languages << firstLang << testLang;
00253 
00254     Vocabulary* vocabCopy = new Vocabulary( vocab->getId(), vocab->getTitle() );
00255     vocabCopy->setDescription( vocab->getDescription() );
00256     vocabCopy->setAuthor( vocab->getAuthor() );
00257     vocabCopy->setCreationDate( vocab->getCreationDate() );
00258     vocabCopy->setModificationDate( vocab->getModificationDate() );
00259     for( Vocabulary::TermMap::ConstIterator it = vocab->begin(); it != vocab->end(); it++ ) {
00260         const Term& term = it.data();
00261         Term* termCopy = new Term( vocabCopy->getMaxTermId() + 1, vocabCopy->getId() );
00262 
00263         for( QStringList::ConstIterator it = languages.begin(); it != languages.end(); it++ ) {
00264             const QString& lang = *it;
00265             if( term.isTranslationExists( lang ) ) {
00266                 Translation transCopy( term.getTranslation( lang ) );
00267                 termCopy->addTranslation( transCopy );
00268             }
00269         }
00270         BilingualKey commentKey( firstLang, testLang );
00271         if( term.isCommentExists( commentKey ) )
00272             termCopy->addComment( commentKey, term.getComment( commentKey ) );
00273 
00274         termCopy->setImagePath( getResolvedImagePath( term.getImagePath(), *vocab ) );
00275 
00276         vocabCopy->addTerm( *termCopy );
00277     }
00278     return( vocabCopy );
00279 }
00280 
00281 Folder* Controller::makeCopy( Folder* folder, const QString& firstLang, const QString& testLang ) const {
00282     QStringList languages;
00283     languages << firstLang << testLang;
00284 
00285     Folder* folderCopy = new Folder( folder->getId(), folder->getTitle() );
00286     folderCopy->setDescription( folder->getDescription() );
00287     folderCopy->setAuthor( folder->getAuthor() );
00288     folderCopy->setCreationDate( folder->getCreationDate() );
00289     folderCopy->setModificationDate( folder->getModificationDate() );
00290     for( Base* folderChild = folder->first(); folderChild; folderChild = folder->next() ) { 
00291         if( strcmp( folderChild->className(), "Folder" ) == 0 ) {
00292             Folder* childFolder = (Folder*)folderChild;
00293             if( childFolder->containsTermWithTranslations( firstLang, testLang ) ) {
00294                 Folder* childFolderCopy = makeCopy( childFolder, firstLang, testLang );
00295                 folderCopy->add( childFolderCopy );
00296             }
00297         }
00298         else if( strcmp( folderChild->className(), "Vocabulary" ) == 0 ) {
00299             Vocabulary* childVocab = (Vocabulary*)folderChild;
00300             if( childVocab->containsTermWithTranslations( firstLang, testLang ) ) {
00301                 Vocabulary* childVocabCopy = makeCopy( childVocab, firstLang, testLang );
00302                 folderCopy->add( childVocabCopy );
00303             }
00304         }
00305     }
00306     return( folderCopy );
00307 }
00308 
00309 Vocabulary* Controller::loadVocabulary( const QString& parentDir ) {
00310     Vocabulary* vocab = NULL;
00311     QDir dir( parentDir );
00312     QString vocabDirName = dir.dirName();
00313     int indexOfDash = vocabDirName.find( "-" );
00314     if( indexOfDash > 0 ) {
00315         bool isOk;
00316         QString strVocabId = vocabDirName.right( vocabDirName.length() - indexOfDash - 1 );
00317         int vocabId = strVocabId.toInt( &isOk );
00318         QString filename( parentDir + QString( "/vocab-" ) + QString::number( vocabId ) + QString( ".gz" ) );
00319         QFile compressedBinFile( filename );
00320         if( compressedBinFile.exists() ) {
00321             vocab = new Vocabulary();
00322             if( !vocab->load( filename ) ) {
00323                 delete( vocab );
00324                 vocab = NULL;
00325             }
00326         }
00327     }
00328     if( vocab == NULL )
00329         cerr << "Could not load vocab file in directory " << parentDir << endl;
00330     return( vocab );
00331 }
00332 
00333 bool Controller::loadVocabulariesRec( Folder* folder ) {
00334     for( Base* child = folder->first(); child; child = folder->next() ) { 
00335         if( strcmp( child->className(), "Vocabulary" ) == 0 ) {
00336             Vocabulary* vocab = (Vocabulary*)child;
00337             QString filename( applicationDirName + QString( "/" ) + QString::number( vocab->getId() ) + QString( ".dat.z" ) );
00338             QFile compressedBinFile( filename );
00339             if( compressedBinFile.exists() ) {
00340                 if( !vocab->load( filename ) )
00341                     return( false );
00342             }
00343         }
00344         else if( strcmp( child->className(), "Folder" ) == 0 ) {
00345             if( !loadVocabulariesRec( (Folder*)child ) )
00346                 return( false );
00347         }
00348     }
00349     return( true );
00350 }
00351 
00352 Base* Controller::importData( Folder* folder, const QString& filename, const QStringList& languages ) {
00353     Folder* rootFolder = folder->getRoot();
00354 
00355     int status;
00356     Base* newItem = NULL;
00357 
00358     zipFile inputFile = unzOpen( filename.latin1() );
00359     if( inputFile == NULL )
00360         return( NULL );
00361 
00362     unz_global_info gi;
00363     status = unzGetGlobalInfo( inputFile, &gi );
00364 
00365     if( status == UNZ_OK ) {
00366         QMap<int,Vocabulary*> newVocabs;
00367         QMap<int,Folder*> newFolders;
00368         Folder* newFolder = NULL;
00369         Vocabulary* newVocab = NULL;
00370         int newVocabId = -1;
00371         int newFolderId = -1;
00372             
00373         // Handle all entries.
00374         for( uLong i = 0; i < gi.number_entry; i++ ) {
00375             char filename_inzip[ 256 ];
00376             unz_file_info file_info;
00377             status = unzGetCurrentFileInfo( inputFile, &file_info, filename_inzip, sizeof( filename_inzip ), NULL, 0, NULL, 0 );
00378             if( status != UNZ_OK ) 
00379                 break;
00380 
00381             QString filenameInZip( filename_inzip );
00382             QFileInfo fileInfo( filenameInZip );
00383             //cout << "f=" << filename_inzip << " dp=" << fileInfo.dirPath() << " dpa=" << fileInfo.dirPath( true ) << " fn=" << fileInfo.fileName() << endl;
00384 
00385             if( fileInfo.extension() == "gif" || fileInfo.extension() == "png" ) {
00386                 int importedVocabId = findVocabId( fileInfo.dirPath() );
00387                 if( newVocabs.contains( importedVocabId ) )
00388                     newVocab = newVocabs[ importedVocabId ];
00389                 else{
00390                     newVocabId = ( newVocabId == -1 ? rootFolder->getMaxVocabId() + 1 : newVocabId + 1 );
00391                     newVocab = new Vocabulary( newVocabId );
00392                     newVocabs.insert( importedVocabId, newVocab );
00393                     if( !newItem )
00394                         newItem = newVocab;
00395                     else {
00396                         int parentFolderId = findParentFolderId( fileInfo.dirPath() );
00397                         if( parentFolderId != -1 && newFolders.contains( parentFolderId ) )
00398                             newFolders[ parentFolderId ]->add( newVocab ); 
00399                     }
00400                 }
00401 
00402                 QString imageLocation = applicationDirName + "/" + folder->getPath() + convertPath( fileInfo.dirPath(), newFolders ) +
00403                     "/v-" + QString::number( newVocab->getId() ) + "/" + fileInfo.fileName();
00404                 importImageFromZip( imageLocation, inputFile );
00405             }
00406             else if( fileInfo.extension() == "xml" ) {
00407                 if( fileInfo.fileName().left( 7 ) == "folder-" ) {
00408                     int importedFolderId = findFolderId( fileInfo.fileName() );
00409                    
00410                     if( newFolders.contains( importedFolderId ) ) 
00411                         newFolder = newFolders[ importedFolderId ];
00412                     else {
00413                         newFolderId = ( newFolderId == -1 ? rootFolder->getMaxId() + 1 : newFolderId + 1 );
00414                         newFolder = new Folder( newFolderId );
00415                         newFolders.insert( importedFolderId, newFolder );
00416                         if( !newItem )
00417                             newItem = newFolder;
00418                         else {
00419                             int parentFolderId = findParentFolderId( fileInfo.dirPath() );
00420                             if( parentFolderId != -1 && newFolders.contains( parentFolderId ) )
00421                                 newFolders[ parentFolderId ]->add( newFolder ); 
00422                         }
00423                     }
00424                 
00425                     QString folderLocation = applicationDirName + "/" + folder->getPath() + convertPath( fileInfo.dirPath(), newFolders );
00426                     importFolderFromZip( newFolder, folderLocation, inputFile ); // Should we handle error here?
00427                 }
00428                 else if( fileInfo.fileName().left( 6 ) == "vocab-" ) {
00429                     int importedVocabId = findVocabId( fileInfo.dirPath() );
00430                     if( newVocabs.contains( importedVocabId ) ) 
00431                         newVocab = newVocabs[ importedVocabId ];
00432                     else {
00433                         newVocabId = ( newVocabId == -1 ? rootFolder->getMaxVocabId() + 1 : newVocabId + 1 );
00434                         newVocab = new Vocabulary( newVocabId );
00435                         newVocabs.insert( importedVocabId, newVocab );
00436                         if( !newItem )
00437                             newItem = newVocab;
00438                         else {
00439                             int parentFolderId = findParentFolderId( fileInfo.dirPath() );
00440                             if( parentFolderId != -1 && newFolders.contains( parentFolderId ) )
00441                                 newFolders[ parentFolderId ]->add( newVocab ); 
00442                         }
00443                     }
00444 
00445                     QString vocabLocation = applicationDirName + "/" + folder->getPath() + convertPath( fileInfo.dirPath(), newFolders ) +
00446                         "/v-" + QString::number( newVocab->getId() );
00447                     importVocabularyFromZip( newVocab, vocabLocation, languages, inputFile ); // Should we handle error here?
00448                 }
00449             }
00450 
00451             status = unzGoToNextFile( inputFile );
00452             if( status == UNZ_END_OF_LIST_OF_FILE ) {
00453                 status = UNZ_OK;
00454                 break;
00455             }
00456             else if( status != UNZ_OK ) {
00457                 // A problem occurred so exit loop.
00458                 break;
00459             }
00460         }
00461     }
00462 
00463     // If an error occurred, we recover memory if necessary.
00464     if( status != UNZ_OK ) {
00465         if( newItem ) {
00466             delete( newItem );
00467             newItem = NULL;
00468         }
00469     }
00470 
00471     if( unzClose( inputFile ) != UNZ_OK )
00472         return( NULL );
00473 
00474     return( newItem );
00475 }
00476 
00477 QStringList Controller::getTranslationLanguagesFromFile( const QString& filename ) const {
00478     QStringList languages;
00479 
00480     zipFile inputFile = unzOpen( filename.latin1() );
00481     if( inputFile ) {
00482         unz_global_info gi;
00483         int status = unzGetGlobalInfo( inputFile, &gi );
00484 
00485         if( status == UNZ_OK ) {
00486             for( uLong i = 0; i < gi.number_entry; i++ ) {
00487                 char filename_inzip[ 256 ];
00488                 unz_file_info file_info;
00489                 status = unzGetCurrentFileInfo( inputFile, &file_info, filename_inzip, sizeof( filename_inzip ), NULL, 0, NULL, 0 );
00490                 if( status != UNZ_OK ) 
00491                     break;
00492 
00493                 QString filenameInZip( filename_inzip );
00494                 QFileInfo fileInfo( filenameInZip );
00495                 if( fileInfo.fileName().left( 6 ) == "vocab-" && fileInfo.extension() == "xml" ) {
00496                     QStringList vocabLanguages = getVocabularyTranslationLanguagesFromZip( inputFile );
00497                     for( QStringList::Iterator it = vocabLanguages.begin(); it != vocabLanguages.end(); it++ ) {
00498                         const QString& lang = *it;
00499                         if( !languages.contains( lang ) )
00500                             languages.append( lang );
00501                     }
00502                 }
00503 
00504                 status = unzGoToNextFile( inputFile );
00505                 if( status == UNZ_END_OF_LIST_OF_FILE ) {
00506                     status = UNZ_OK;
00507                     break;
00508                 }
00509                 else if( status != UNZ_OK ) {
00510                     cerr << "Cannot get next entry in Zip file: " << filename.latin1() << endl;
00511                     break; // An error has occured so exit the loop.
00512                 }
00513             }
00514         }
00515 
00516         if( unzClose( inputFile ) != UNZ_OK )
00517             cerr << "Cannot close file: " << filename.latin1() << endl;
00518     }
00519     else
00520         cerr << "Cannot open file: " << filename.latin1() << endl;
00521     
00522     return( languages );
00523 }
00524 
00525 QStringList Controller::getVocabularyTranslationLanguagesFromZip( zipFile inputFile ) const {
00526     QStringList languages;
00527 
00528     int status = unzOpenCurrentFile( inputFile );
00529     if( status == UNZ_OK ) {
00530         bool isOk = true;
00531 
00532         char* buf = NULL;
00533         uInt size_buf = 8192; // Arbitrary size for now.
00534         buf = (char*)malloc( size_buf );
00535         if( buf == NULL ) {
00536             cerr << "Cannot allocate memory for unzip buffer." << endl;
00537             status = UNZ_INTERNALERROR;
00538         }
00539 
00540         if( status == UNZ_OK ) {
00541             QByteArray ba;
00542             QTextStream ts( ba, IO_WriteOnly );
00543 
00544             int totalByteCount = 0;
00545             int readStatus;
00546             for( ;; ) {
00547                 readStatus = unzReadCurrentFile( inputFile, buf, size_buf );
00548                 if( readStatus > 0 ) {
00549                     ts.writeRawBytes( buf, readStatus );
00550                     totalByteCount += readStatus;
00551                 }
00552                 else
00553                     break;
00554             }
00555 
00556             if( readStatus == 0 ) {
00557                 Vocabulary* vocab = new Vocabulary();
00558                 QTextStream ts2( ba, IO_ReadOnly );
00559                 VocabParser parser( *vocab, languages );
00560                 QXmlInputSource source( ts2 );
00561 
00562                 QXmlSimpleReader reader;
00563                 reader.setContentHandler( &parser );
00564                 isOk = reader.parse( source );
00565 
00566                 languages = vocab->getTranslationLanguages();
00567 
00568                 delete( vocab );
00569                 vocab = NULL;
00570             }
00571         }
00572 
00573         free( buf );
00574 
00575         if( unzCloseCurrentFile( inputFile ) != UNZ_OK )
00576             cerr << "Cannot close zip entry." << endl; 
00577     }
00578     else
00579         cerr << "Cannot open zip entry." << endl;
00580 
00581     return( languages );
00582 }
00583 
00584 bool Controller::importFolderFromZip( Folder* folder, const QString& folderLocation, zipFile inputFile ) {
00585     int status = unzOpenCurrentFile( inputFile );
00586     if( status != UNZ_OK )
00587         return( false );
00588 
00589     bool isOk = true;
00590 
00591     char* buf = NULL;
00592     uInt size_buf = 8192; // Arbitrary size for now.
00593     buf = (char*)malloc( size_buf );
00594     if( buf == NULL ) {
00595         cerr << "Cannot allocate memory for unzip buffer." << endl;
00596         status = UNZ_INTERNALERROR;
00597     }
00598 
00599     if( status == UNZ_OK ) {
00600         QByteArray ba;
00601         QTextStream ts( ba, IO_WriteOnly );
00602 
00603         int totalByteCount = 0;
00604         int readStatus;
00605         for( ;; ) {
00606             readStatus = unzReadCurrentFile( inputFile, buf, size_buf );
00607             if( readStatus > 0 ) {
00608                 ts.writeRawBytes( buf, readStatus );
00609                 totalByteCount += readStatus;
00610             }
00611             else
00612                 break;
00613         }
00614 
00615         if( readStatus == 0 ) {
00616             QTextStream ts2( ba, IO_ReadOnly );
00617             FolderParser parser( *folder, folderLocation );
00618             QXmlInputSource source( ts2 );
00619 
00620             QXmlSimpleReader reader;
00621             reader.setContentHandler( &parser );
00622             isOk = reader.parse( source );
00623 
00624             if( isOk ) {
00625                 // Create the containing folder if needed.
00626                 isOk = Util::makeDirectory( folderLocation );
00627                 if( isOk ) {
00628                     const QString& folderDataFilename( folderLocation + "/folder-" + QString::number( folder->getId() ) + ".gz" );
00629                     isOk = folder->saveMetadata( folderDataFilename );
00630                     if( isOk )
00631                         folder->setDirty( false );
00632                 }
00633                 else {
00634                     cerr << "Could not create directory " << folderLocation << endl;
00635                     isOk = false;
00636                 }
00637             }
00638 
00639             //if( !isOk || !parser.isVocabularyFile() ) {
00640             //    delete( vocab );
00641             //    vocab = NULL;
00642             //}
00643         }
00644     }
00645 
00646     free( buf );
00647 
00648     /*status =*/ unzCloseCurrentFile( inputFile );
00649     //if( readStatus != UNZ_OK || status != UNZ_OK ) 
00650     //    return( NULL );
00651     return( isOk );
00652 }
00653 
00654 bool Controller::importVocabularyFromZip( Vocabulary* vocab, const QString& vocabLocation, const QStringList& languages, zipFile inputFile ) {
00655     int status = unzOpenCurrentFile( inputFile );
00656     if( status != UNZ_OK )
00657         return( false );
00658 
00659     bool isOk = true;
00660 
00661     char* buf = NULL;
00662     uInt size_buf = 8192; // Arbitrary size for now.
00663     buf = (char*)malloc( size_buf );
00664     if( buf == NULL ) {
00665         cerr << "Cannot allocate memory for unzip buffer." << endl;
00666         status = UNZ_INTERNALERROR;
00667     }
00668 
00669     if( status == UNZ_OK ) {
00670         QByteArray ba;
00671         QTextStream ts( ba, IO_WriteOnly );
00672 
00673         int totalByteCount = 0;
00674         int readStatus;
00675         for( ;; ) {
00676             readStatus = unzReadCurrentFile( inputFile, buf, size_buf );
00677             if( readStatus > 0 ) {
00678                 ts.writeRawBytes( buf, readStatus );
00679                 totalByteCount += readStatus;
00680             }
00681             else
00682                 break;
00683         }
00684 
00685         if( readStatus == 0 ) {
00686             QTextStream ts2( ba, IO_ReadOnly );
00687             VocabParser parser( *vocab, languages );
00688             QXmlInputSource source( ts2 );
00689 
00690             QXmlSimpleReader reader;
00691             reader.setContentHandler( &parser );
00692             isOk = reader.parse( source );
00693             if( isOk ) {
00694                 vocab->setDirty( true ); // Force saving data.
00695                 isOk = saveVocabulary( vocab, vocabLocation );
00696             }
00697         }
00698     }
00699 
00700     free( buf );
00701 
00702     /*status =*/ unzCloseCurrentFile( inputFile );
00703     //if( readStatus != UNZ_OK || status != UNZ_OK ) 
00704     //    return( NULL );
00705     return( isOk );
00706 }
00707 
00708 bool Controller::importImageFromZip( const QString& imagePath, zipFile inputFile ) {
00709     int status = unzOpenCurrentFile( inputFile );
00710     if( status != UNZ_OK )
00711         return( false );
00712 
00713     bool isOk = true;
00714 
00715     char* buf = NULL;
00716     uInt size_buf = 8192; // Arbitrary size for now.
00717     buf = (char*)malloc( size_buf );
00718     if( buf == NULL ) {
00719         cerr << "Cannot allocate memory for unzip buffer." << endl;
00720         status = UNZ_INTERNALERROR;
00721     }
00722 
00723     if( status == UNZ_OK ) {
00724         QFile imageFile( imagePath );
00725 
00726         const QString imageDir = QFileInfo( imageFile ).dirPath( true );
00727         isOk = Util::makeDirectory( imageDir );
00728         if( isOk ) {
00729             isOk = imageFile.open( IO_WriteOnly );
00730             if( isOk ) {
00731                 QTextStream ts( &imageFile );
00732 
00733                 int totalByteCount = 0;
00734                 int readStatus;
00735                 for( ;; ) {
00736                     readStatus = unzReadCurrentFile( inputFile, buf, size_buf );
00737                     if( readStatus > 0 ) {
00738                         ts.writeRawBytes( buf, readStatus );
00739                         totalByteCount += readStatus;
00740                     }
00741                     else
00742                         break;
00743                 }
00744                 imageFile.close();
00745             }
00746             else 
00747                 cerr << "Could not create file " << imagePath << endl;
00748         }
00749         else
00750             cerr << "Could not create directory " << imageDir << endl;
00751     }
00752 
00753     free( buf );
00754 
00755     /*status =*/ unzCloseCurrentFile( inputFile );
00756     //if( readStatus != UNZ_OK || status != UNZ_OK ) 
00757     //    return( NULL );
00758     return( isOk );
00759 }
00760 
00761 void Controller::loadData() {
00762     vocabTree = loadFolder( applicationDirName );
00763 
00764     if( !vocabTree ) {
00765         Folder* folder = new Folder( 1, QObject::tr( "MyGlossaries" ) );
00766         folder->setMarkedForStudy( true );
00767         folder->setDirty( true );
00768         vocabTree = folder;
00769     }
00770     loadMarkedItems( vocabTree );
00771 }
00772 
00773 void Controller::rightAnswer() {
00774     scheduler.rightAnswer();
00775     emit( progressChanged( getProgress() ) );
00776 }
00777 
00778 void Controller::wrongAnswer() {
00779     scheduler.wrongAnswer();
00780     emit( progressChanged( getProgress() ) );
00781 }
00782 
00783 void Controller::reveal() {
00784 }
00785 
00786 Folder* Controller::loadFolder( const QString& parentDir ) {
00787     Folder* newFolder = NULL;
00788 
00789     QDir dir( parentDir );
00790     bool isOk;
00791     dir.dirName().toInt( &isOk );
00792 
00793     if( isOk ) {
00794         newFolder = new Folder();
00795         QString folderMetadataFile = QString( parentDir + "/folder-" + dir.dirName() + ".gz" );
00796         if( !newFolder->loadMetadata( folderMetadataFile ) ) {
00797             cerr << "Could not load metadata file " << folderMetadataFile << endl;
00798             delete( newFolder );
00799             return( NULL );
00800         }
00801     }
00802 
00803     QStringList entries = dir.entryList();
00804     for( QStringList::Iterator it = entries.begin(); it != entries.end(); it++ ) {
00805         QString entry = *it;
00806         QString entryPath = parentDir + "/" + entry;
00807         QFileInfo info( entryPath );
00808         if( info.isDir() && entry != "." && entry != ".." ) {
00809             if( !isOk ) 
00810                 newFolder = loadFolder( entryPath );
00811             else { 
00812                 if( entry.left( 2 ) == QString( "v-" ) ) {
00813                     Vocabulary* childVocab = loadVocabulary( entryPath );
00814                     if( childVocab )
00815                         newFolder->add( childVocab );
00816                 }
00817                 else {
00818                     Folder* childFolder = loadFolder( entryPath );
00819                     if( childFolder )
00820                         newFolder->add( childFolder ); 
00821                 }
00822             }
00823         }
00824     }
00825 
00826     return( newFolder );
00827 }
00828 
00829 bool Controller::saveFolder( Folder* folder, const QString& parentDir ) const {
00830     //cerr << "saveFolder folder=" << folder << " location=" << parentDir << " dirty? " << folder->isDirty() << endl;
00831     // Create the folder.
00832     QString folderPath( parentDir + QString( "/" ) + QString::number( folder->getId() ) );
00833     QDir folderDir( folderPath );
00834     if( folder->isDirty() && !folderDir.exists() ) {
00835         if( !folderDir.mkdir( folderDir.path() ) ) {
00836             cerr << "Could not make directory " << folderPath << endl;
00837             return( false );
00838         }
00839     }
00840 
00841     // Write the folder data.
00842     if( folder->isDirty() ) {
00843         QString folderDataFilename( QString( folderDir.path() + QString( "/folder-" ) + QString::number( folder->getId() ) + QString( ".gz" ) ) );
00844         if( !folder->saveMetadata( folderDataFilename ) ) {
00845             cerr << "Could not write folder metadata " << folderDataFilename << endl;
00846             return( false );
00847         }
00848         folder->setDirty( false );
00849     }
00850 
00851     // Handle children recursively.
00852     if( !folder->isEmpty() ) {
00853         for( Base* folderChild = folder->first(); folderChild; folderChild = folder->next() ) {
00854             if( strcmp( folderChild->className(), "Folder" ) == 0 ) {
00855                 Folder* childFolder = (Folder*)folderChild;
00856                 if( !childFolder->isMarkedForDeletion() ) 
00857                     saveFolder( childFolder, folderDir.path() );
00858             }
00859             else if( strcmp( folderChild->className(), "Vocabulary" ) == 0 ) {
00860                 Vocabulary* childVocab = (Vocabulary*)folderChild;
00861                 if( !childVocab->isMarkedForDeletion() )
00862                     saveVocabulary( childVocab, folderDir.path() );
00863             }
00864         }
00865     }
00866 
00867     return( true );
00868 }
00869 
00870 void Controller::writeVocabulariesInXml( Folder* folder, int depth, QTextStream& ts, QStringList* languages ) {
00871     if( !folder->isEmpty() ) {
00872         for( Base* folderChild = folder->first(); folderChild; folderChild = folder->next() ) {
00873             if( strcmp( folderChild->className(), "Folder" ) == 0 )
00874                 writeVocabulariesInXml( (Folder*)folderChild, depth, ts, languages );
00875             else if( strcmp( folderChild->className(), "Vocabulary" ) == 0 ) {
00876                 Vocabulary* vocab = (Vocabulary*)folderChild;
00877                 writeVocabularyInXml( ts, *vocab, languages, false, depth );
00878             }
00879         }
00880     }
00881 }
00882 
00883 bool Controller::deleteItemsMarkedForDeletion( Folder* folder ) {
00884     for( Base* childItem = folder->first(); childItem; childItem = folder->next() ) {
00885         if( strcmp( childItem->className(), "Folder" ) == 0 ) {
00886             Folder* childFolder = new Folder( *((Folder*)childItem) );
00887             deleteItemsMarkedForDeletion( childFolder );
00888         }
00889         else if( strcmp( childItem->className(), "Vocabulary" ) == 0 ) {
00890             Vocabulary* childVocab = new Vocabulary( *((Vocabulary*)childItem) ); 
00891             if( childVocab->isMarkedForDeletion() ) {
00892                 QString vocabDir( applicationDirName + "/" + folder->getPath() + "/v-" + QString::number( childVocab->getId() ) );
00893                 if( !Util::deleteDirectory( vocabDir ) ) {
00894                     cerr << "Cannot delete glossary directory " << vocabDir << endl;
00895                     return( false );
00896                 }
00897             }
00898         }
00899     }
00900 
00901     if( folder->isMarkedForDeletion() ) {
00902         // To remove the reference to the deleted folder in preferences, we set it as opened.
00903         getPreferences().setFolderOpen( folder->getId(), true );
00904         QString folderDir( applicationDirName + "/" + folder->getPath() );
00905         if( !Util::deleteDirectory( folderDir ) ) {
00906             cerr << "Cannot delete folder directory " << folderDir << endl;
00907             return( false );
00908         }
00909     }
00910 
00911     return( true );
00912 }
00913 
00914 int Controller::findFolderId( const QString& filename ) const {
00915     int indexOfDash = filename.find( "-" );
00916     int indexOfDot = filename.find( "." );
00917     const QString& strFolderId = filename.mid( indexOfDash + 1, indexOfDot - indexOfDash - 1 );
00918     bool isOk;
00919     int folderId = strFolderId.toInt( &isOk );
00920     return( isOk ? folderId : -1 );
00921 }
00922 
00923 int Controller::findParentFolderId( const QString& dirPath ) const {
00924     int lastSlashPos = dirPath.findRev( "/" );
00925     if( lastSlashPos > 0 ) {
00926         int beforeLastSlashPos = dirPath.findRev( "/", lastSlashPos - 1 );
00927         if( beforeLastSlashPos > 0 ) {
00928             QString strParentFolderId = dirPath.mid( beforeLastSlashPos + 1, lastSlashPos - beforeLastSlashPos - 1 );
00929             bool isOk;
00930             int parentFolderId = strParentFolderId.toInt( &isOk );
00931             return( isOk ? parentFolderId : -1 );
00932         }
00933         else {
00934             QString strParentFolderId = dirPath.left( lastSlashPos );
00935             bool isOk;
00936             int parentFolderId = strParentFolderId.toInt( &isOk );
00937             return( isOk ? parentFolderId : -1 );
00938         }
00939     }
00940     return( -1 );
00941 }
00942 
00943 //int Controller::findVocabId( const QString& filename ) const {
00944 //    int posOfDash = filename.find( "-" );
00945 //    int posOfDot = filename.find( "." );
00946 //    QString strImportedVocabId = filename.mid( posOfDash + 1, posOfDot - posOfDash - 1 );
00947 //    bool isOk;
00948 //    int importedVocabId = strImportedVocabId.toInt( &isOk );
00949 //    return( isOk ? importedVocabId : -1 );
00950 //}
00951 
00952 int Controller::findVocabId( const QString& dirPath ) const {
00953     int posOfLeftDelim = dirPath.find( "v-" );
00954     int posOfRightDelim = dirPath.findRev( "/" );
00955     QString strImportedVocabId = dirPath.mid( posOfLeftDelim + 1, posOfRightDelim - posOfLeftDelim - 1 );
00956     bool isOk;
00957     int importedVocabId = strImportedVocabId.toInt( &isOk );
00958     return( isOk ? importedVocabId : -1 );
00959 }
00960 
00961 QString Controller::convertPath( const QString& path, QMap<int,Folder*>& newFolders ) const {
00962     QString realPath;
00963     QStringList subDirs = QStringList::split( "/", path );
00964     for( QStringList::Iterator it = subDirs.begin(); it != subDirs.end(); it++ ) {
00965         const QString& strFolderId = (*it);
00966         bool isOk;
00967         int subDirFolderId = strFolderId.toInt( &isOk );
00968         if( isOk && newFolders.contains( subDirFolderId ) ) {
00969             Folder* assocFolder = newFolders[ subDirFolderId ];
00970             realPath += "/" + QString::number( assocFolder->getId() );
00971         }
00972     }
00973     return( realPath );
00974 }
00975 
00976 bool Controller::saveData() {
00977     if( !deleteObsoleteData() ) {
00978         // Just write a warning message.  We don't return( false ) here
00979         // because we want to try to save the new data, at least.
00980         cerr << "Could not delete obsolete data files." << endl;
00981     }
00982     if( !deleteItemsMarkedForDeletion( vocabTree ) ) {
00983         // Just write a warning message.  We don't return( false ) here
00984         // because we want to try to save the new data, at least.
00985         cerr << "Could not delete all items marked for deletion." << endl;
00986     }
00987     if( !saveFolder( vocabTree, applicationDirName ) )
00988         return( false );
00989     if( !saveMarkedItems( vocabTree ) )
00990         return( false );
00991     return( prefs.save() );
00992 }
00993 
00994 bool Controller::saveMarkedItems( Folder* folder ) {
00995     QByteArray data;
00996 
00997     QDataStream out( data, IO_WriteOnly );
00998     out.setVersion( 3 /* QDataStream::Qt_3 ? */ );
00999 
01000     // 0x0011 means 0.11.x version.
01001     out << Q_UINT32( Preferences::magicNumber ) << Q_UINT16( 0x0011 );
01002 
01003     IdList folderIds;
01004     IdList vocabIds;
01005     IdListMap termIds;
01006 
01007     saveMarkedItemsRec( folder, folderIds, vocabIds, termIds );
01008 
01009     out << folderIds << vocabIds << termIds;
01010 
01011     QByteArray compressedData( Util::qCompress( data ) ); 
01012 
01013     QFile dataFile( markedFilename );
01014     QFileInfo dataFileInfo( dataFile );
01015 
01016     if( !Util::makeDirectory( dataFileInfo.dirPath() ) )
01017         return( false );
01018 
01019     if( !dataFile.open( IO_WriteOnly ) )
01020         return( false );
01021 
01022     int ret = dataFile.writeBlock( compressedData );
01023     dataFile.close();
01024 
01025     if( ret == -1 || dataFile.status() != IO_Ok ) {
01026         dataFile.resetStatus();
01027         return( false );
01028     }
01029 
01030     return( true );
01031 }
01032 
01033 void Controller::saveMarkedItemsRec( Folder* folder, IdList& folderIds, IdList& vocabIds, IdListMap& termIds ) {
01034     if( folder->isMarkedForStudy() )
01035         folderIds.append( folder->getId() );
01036     if( !folder->isEmpty() ) {
01037         for( Base* folderChild = folder->first(); folderChild; folderChild = folder->next() ) {
01038             if( strcmp( folderChild->className(), "Folder" ) == 0 )
01039                 saveMarkedItemsRec( (Folder*)folderChild, folderIds, vocabIds, termIds );
01040             else if( strcmp( folderChild->className(), "Vocabulary" ) == 0 )
01041                 saveMarkedItemsRec( (Vocabulary*)folderChild, vocabIds, termIds );
01042         }
01043     }
01044 }
01045 
01046 void Controller::saveMarkedItemsRec( Vocabulary* vocab, IdList& vocabIds, IdListMap& termIds ) {
01047     if( vocab->isMarkedForStudy() )
01048         vocabIds.append( vocab->getId() );
01049     IdList termIdList;
01050     for( Vocabulary::TermMap::ConstIterator it = vocab->begin(); it != vocab->end(); it++ ) {
01051         const Term& term = it.data();
01052         if( term.isMarkedForStudy() )
01053             termIdList.append( term.getId() );
01054     }
01055     termIds.insert( vocab->getId(), termIdList );
01056 }
01057 
01058 void Controller::loadMarkedItems( Folder* folder ) {
01059     QFile markedFile( markedFilename );
01060     if( markedFile.exists() ) {
01061         if( !markedFile.open( IO_ReadOnly ) )
01062             return;
01063 
01064         QByteArray compressedData( markedFile.readAll() );
01065         QByteArray data( Util::qUncompress( compressedData ) );
01066 
01067         QDataStream in( data, IO_ReadOnly );
01068 
01069         Q_UINT32 tempMagicNumber;
01070         Q_UINT16 tempVersion;
01071 
01072         IdList tempFolderIds;
01073         IdList tempVocabIds;
01074         IdListMap tempTermIds;
01075 
01076         in >> tempMagicNumber >> tempVersion;
01077 
01078         if( tempMagicNumber != Preferences::magicNumber )
01079             cerr << "Wrong magic number: Incompatible data file for marked file." << endl;
01080         if( tempVersion > 0x0011 )
01081             cerr << "Marked data file is from a more recent version.  Upgrade toMOTko." << endl;
01082 
01083         in.setVersion( 3 );
01084         in >> tempFolderIds >> tempVocabIds >> tempTermIds; 
01085 
01086         markedFile.close();
01087         
01088         initMarkedForStudyRec( folder, tempFolderIds, tempVocabIds, tempTermIds );
01089     }
01090     else {
01091         QFile markedXmlFile( markedXmlFilename );
01092         if( markedXmlFile.exists() ) {
01093             MarkedItemsParser parser;
01094             QXmlInputSource source( markedXmlFile );
01095             QXmlSimpleReader reader;
01096             reader.setContentHandler( &parser );
01097             reader.parse( source );
01098             initMarkedForStudyRec( folder, *(parser.getMarkedFolders()), *(parser.getMarkedVocabs()), *(parser.getMarkedTerms()) );
01099         }
01100 
01101     }
01102 }
01103 
01104 void Controller::initMarkedForStudyRec( Folder* folder, IdList& folderIds, IdList& vocabIds, IdListMap& termIds ) {
01105     if( folderIds.contains( folder->getId() ) )
01106         folder->setMarkedForStudy( true );
01107     if( !folder->isEmpty() ) {
01108         for( Base* folderChild = folder->first(); folderChild; folderChild = folder->next() ) {
01109             if( strcmp( folderChild->className(), "Folder" ) == 0 )
01110                 initMarkedForStudyRec( (Folder*)folderChild, folderIds, vocabIds, termIds );
01111             else if( strcmp( folderChild->className(), "Vocabulary" ) == 0 )
01112                 initMarkedForStudyRec( (Vocabulary*)folderChild, vocabIds, termIds );
01113         }
01114     }
01115 }
01116 
01117 void Controller::initMarkedForStudyRec( Vocabulary* vocab, IdList& vocabIds, IdListMap& termIds ) {
01118     if( vocabIds.contains( vocab->getId() ) )
01119         vocab->setMarkedForStudy( true );
01120     IdList termIdList = termIds[ vocab->getId() ];
01121     for( IdList::ConstIterator it = termIdList.begin(); it != termIdList.end(); it++ ) {
01122         int termId = *it;
01123         if( vocab->isTermExists( termId ) ) {
01124             Term& term = vocab->getTerm( termId );
01125             term.setMarkedForStudy( true );
01126         }
01127     }
01128 }
01129 
01130 bool Controller::exportData( Vocabulary* vocab, const QString& file, QStringList* languages ) const {
01131     zipFile outputFile = zipOpen( file.latin1(), APPEND_STATUS_CREATE );
01132     if( outputFile == NULL )
01133         return( false );
01134 
01135     bool isOk = exportVocabularyIntoZip( vocab, outputFile, QString::null, languages );
01136 
01137     if( zipClose( outputFile, "Closing comment" ) != 0 )
01138         return( false );
01139 
01140     return( isOk );
01141 }
01142 
01143 bool Controller::exportVocabularyIntoZip( Vocabulary* vocab, zipFile outputFile, QString path, QStringList* languages ) const {
01144     QString vocabPath = ( path == QString::null ? QString( "v-" + QString::number( vocab->getId() ) ) : 
01145         path + "/v-" + QString::number( vocab->getId() ) );
01146 
01147     // Copy the referred images first.
01148     for( Vocabulary::TermMap::ConstIterator it = vocab->begin(); it != vocab->end(); it++ ) {
01149         const Term& term = it.data();
01150         if( term.getImagePath() != QString::null ) {
01151             QString fileExtension = term.getImagePath().right( 4 );
01152             QCString imageDataFilename = QString( vocabPath + "/" + QString::number( term.getId() ) + fileExtension ).latin1();
01153             const char* filenameInZip = (const char*)imageDataFilename.data();
01154             QString absPath = getResolvedImagePath( term.getImagePath(), *vocab );
01155             QFile imageFile( absPath );
01156             if( imageFile.exists() ) {
01157                 if( !imageFile.open( IO_ReadOnly ) )
01158                     return( false );
01159                 QByteArray buffer = imageFile.readAll();
01160                 imageFile.close();
01161 
01162                 int err = writeFileIntoZipFile( outputFile, filenameInZip, buffer.data(), buffer.size() );
01163                 if( err != ZIP_OK )
01164                     return( false );
01165             }
01166             else {
01167                 cerr << "Image " << imageFile.name() << " referenced by glossary " << vocab->getTitle() << 
01168                     " was not found, and therefore, was discarded during export." << endl;
01169             }
01170         }
01171     }
01172 
01173     // Copy the vocabulary itself in XML.
01174     QCString dataFilename = QString( vocabPath + "/vocab-" + QString::number( vocab->getId() ) + ".xml" ).latin1();
01175     const char* filenameInZip = (const char*)dataFilename.data();
01176 
01177     QByteArray buffer;
01178     QTextStream ts( buffer, IO_WriteOnly );
01179     ts.setEncoding( QTextStream::UnicodeUTF8 );
01180     writeVocabularyInXml( ts, *vocab, languages );
01181     int err = writeFileIntoZipFile( outputFile, filenameInZip, buffer.data(), buffer.size() );
01182 
01183     return( err == ZIP_OK );
01184 }
01185 
01186 bool Controller::exportData( Folder* folder, const QString& file, QStringList* languages ) const {
01187     zipFile outputFile = zipOpen( file.latin1(), APPEND_STATUS_CREATE );
01188     if( outputFile == NULL )
01189         return( false );
01190 
01191     bool isOk = exportFolderRecIntoZip( folder, outputFile, QString::null, languages );
01192 
01193     if( zipClose( outputFile, "Closing comment" ) != 0 )
01194         return( false );
01195 
01196     return( isOk );
01197 }
01198 
01199 bool Controller::exportFolderRecIntoZip( Folder* folder, zipFile outputFile, QString path, QStringList* languages ) const {
01200     QString folderPath = ( path == QString::null ? QString::number( folder->getId() ) : 
01201         path + QString( "/" ) + QString::number( folder->getId() ) );
01202     if( !folder->isEmpty() ) {
01203         QCString folderDataFilename = QString( folderPath + "/folder-" + QString::number( folder->getId() ) + ".xml" ).latin1();
01204         const char* filenameInZip = (const char*) ( folderDataFilename.data() );
01205 
01206         QByteArray buffer;
01207         QTextStream ts( buffer, IO_WriteOnly );
01208         ts.setEncoding( QTextStream::UnicodeUTF8 );
01209         writeFolderDataInXml( ts, *folder );
01210 
01211         int err = writeFileIntoZipFile( outputFile, filenameInZip, buffer.data(), buffer.size() );
01212         if( err != ZIP_OK )
01213             return( false );
01214     }
01215 
01216     for( Base* child = folder->first(); child; child = folder->next() ) { 
01217         if( strcmp( child->className(), "Vocabulary" ) == 0 ) {
01218             Vocabulary* childVocab = (Vocabulary*)child;
01219             if( !exportVocabularyIntoZip( childVocab, outputFile, folderPath, languages ) )
01220                 return( false );
01221         }
01222         else if( strcmp( child->className(), "Folder" ) == 0 ) {
01223             Folder* childFolder = (Folder*)child;
01224             if( !exportFolderRecIntoZip( childFolder, outputFile, folderPath, languages ) )
01225                 return( false );
01226         }
01227     }
01228 
01229     return( true );
01230 }
01231 
01232 int Controller::writeFileIntoZipFile( zipFile outputFile, const char* filename, const char* data, int length ) const {
01233     zip_fileinfo zipFileInfo;
01234 
01235     zipFileInfo.tmz_date.tm_sec = zipFileInfo.tmz_date.tm_min = zipFileInfo.tmz_date.tm_hour =
01236         zipFileInfo.tmz_date.tm_mday = zipFileInfo.tmz_date.tm_mon = zipFileInfo.tmz_date.tm_year = 0;
01237     zipFileInfo.dosDate = 0;
01238     zipFileInfo.internal_fa = 0;
01239     zipFileInfo.external_fa = 0;
01240     //filetime( filenameInZip, &zipFileInfo.tmz_date, &zipFileInfo.dosDate );
01241 
01242     int err = zipOpenNewFileInZip3( outputFile, filename, &zipFileInfo,
01243          NULL, 0, NULL, 0, NULL /* comment*/,
01244          Z_DEFLATED /* method */,
01245          5 /* level */, 0,
01246          /* -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, */
01247          -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY,
01248          NULL, 0 );
01249     if( err )
01250         return( err );
01251 
01252     int writeErr = zipWriteInFileInZip( outputFile, data, length );
01253 
01254     err = zipCloseFileInZip( outputFile );
01255 
01256     if( writeErr || err )
01257         return( writeErr ? writeErr : err );
01258 
01259     return( ZIP_OK );
01260 }
01261 
01262 void Controller::writeFolderDataInXml( QTextStream& ts, const Folder& folder ) const {
01263     ts << QString( "<?xml version=\"1.0\"?>" ) << endl;
01264     ts << QString( "<folder id=\"" ) << folder.getId() << QString( "\" name=\"" ) << Util::escapeXml( folder.getTitle() ) << QString( "\"" );
01265 
01266     if( !folder.getAuthor().isNull() )
01267         ts << QString( " author=\"" ) << Util::escapeXml( folder.getAuthor() ) << QString( "\"" );
01268     ts << QString( ">" ) << endl;
01269 
01270     if( !folder.getDescription().isNull() )
01271         ts << QString( "\t<desc>" ) << Util::escapeXml( folder.getDescription() ) << QString( "</desc>" ) << endl;
01272     ts << QString( "</folder>" ) << endl;
01273 }
01274 
01275 void Controller::writeVocabularyInXml( QTextStream& ts, const Vocabulary& vocab, QStringList* languages, bool writeXmlDirective = true, int depth = 0 ) const {
01276     if( writeXmlDirective ) {
01277         for( int i = 0; i < depth; i++ )
01278             ts << "\t";
01279         ts << QString( "<?xml version=\"1.0\"?>" ) << endl;
01280     }
01281 
01282     for( int i = 0; i < depth; i++ )
01283         ts << "\t";
01284     ts << QString( "<glossary id=\"" ) << vocab.getId() << "\" name=\"" << Util::escapeXml( vocab.getTitle() ) << "\" ";
01285     ts << QString( "author=\"" ) << Util::escapeXml( vocab.getAuthor() ) << "\">" << endl;
01286 
01287     for( int i = 0; i < depth; i++ )
01288         ts << "\t";
01289     ts << QString( "\t<desc>" ) << Util::escapeXml( vocab.getDescription() ) << QString( "</desc>" ) << endl;
01290     for( Vocabulary::TermMap::ConstIterator it = vocab.begin(); it != vocab.end(); it++ ) {
01291         const Term& term = it.data();
01292         for( int i = 0; i < depth; i++ )
01293             ts << "\t";
01294         ts << Util::term2Xml( term, languages, depth + 1 );
01295     }
01296     for( int i = 0; i < depth; i++ )
01297         ts << "\t";
01298     ts << QString( "</glossary>" ) << endl;
01299 }
01300 
01301 bool Controller::saveVocabulary( Vocabulary* vocab, const QString& location ) const {
01302     //cerr << "saveVocabulary vocab=" << vocab << " location=" << location << " isDirty? " << vocab->isDirty() << endl;
01303     // Create the containing folder if needed.
01304     QString folderPath( location.find( "v-" ) == -1 ? location + QString( "/v-" ) + QString::number( vocab->getId() ) : location );
01305     QDir folderDir( folderPath );
01306     if( vocab->isDirty() && !folderDir.exists() ) {
01307         if( !folderDir.mkdir( folderDir.path() ) ) {
01308             cerr << "Cannot create directory " << folderPath << endl;
01309             return( false );
01310         }
01311     }
01312 
01313     // Write the vocab data.
01314     if( vocab->isDirty() ) {
01315         QString dataFilename( QString( folderDir.path() + QString( "/" ) + QString( "vocab-" ) + QString::number( vocab->getId() ) + QString( ".gz" ) ) );
01316         if( !vocab->save( dataFilename ) ) {
01317             cerr << "Could not write vocab data " << dataFilename << endl;
01318             return( false );
01319         }
01320         vocab->setDirty( false );
01321     }
01322 
01323     return( true );
01324 }
01325 
01326 void Controller::initRevealingSequence() {
01327     int index = ( rand() % prefs.getActiveRevealingSequenceCount() );
01328     int sequenceCount = prefs.getRevealingSequenceCount();
01329     for( int i = 0, j = 0; i < sequenceCount; i++ ) {
01330         Sequence seq = prefs.getRevealingSequenceAt( i );
01331         if( seq.isEnabled() ) {
01332             if( j == index ) {
01333                 currRevealingSeq = seq;
01334                 break;
01335             }
01336             else 
01337                 j++;
01338         }
01339     }
01340     currRevealingSeqStep = 0;
01341 }
01342 
01343 void Controller::setClipboardData( const QString& type, const QByteArray& data ) {
01344     clipboardDataType = type;
01345     clipboard = data;
01346 }
01347 
01348 QByteArray Controller::getClipboardData() const {
01349     return( clipboard );
01350 }
01351 
01352 QString Controller::getClipboardDataType() const {
01353     return( clipboardDataType );
01354 }
01355 
01356 bool Controller::isImagePathValid( const QString& path, const Vocabulary& vocab ) const {
01357     if( !path.isNull() ) {
01358         QString absPath = getResolvedImagePath( path, vocab );
01359         QFileInfo info( absPath );
01360         if( info.exists() ) {
01361             QString format = QPixmap::imageFormat( absPath );
01362             if( format == "GIF" || format == "PNG" ) 
01363                 return( true );
01364         }
01365     }
01366     return( false );
01367 }
01368 
01369 QString Controller::getResolvedImagePath( const QString& path, const Vocabulary& vocab ) const {
01370     if( path.isNull() )
01371         return( QString::null );
01372     else if( path.left( 1 ) == "/" )
01373         return( path );
01374     else {
01375         QString absPath = getApplicationDirName() + "/" + vocab.getParent()->getPath() + "/v-" + QString::number( vocab.getId() ) + "/" + path;
01376         return( absPath );
01377     }
01378 }
01379 
01380 void Controller::clearSearch() {
01381     searchQuery = QString::null;
01382     searchResults.clear();
01383 }
01384 
01385 QValueList<TermKey> Controller::search( const QString& query, const QString& firstLang = QString::null, const QString& testLang = QString::null ) {
01386     searchQuery = query;
01387     searchResults.clear();
01388     QString strippedQuery = query.stripWhiteSpace();
01389     if( !strippedQuery.isEmpty() )
01390         searchRec( strippedQuery, firstLang, testLang, vocabTree, searchResults );  
01391     return( searchResults );
01392 }
01393 
01394 void Controller::searchRec( const QString& query, const QString& firstLang, const QString& testLang, Folder* folder, QValueList<TermKey>& results ) {
01395     if( !folder || folder->isMarkedForDeletion() )
01396         return;
01397 
01398     for( Base* child = folder->first(); child; child = folder->next() ) { 
01399         if( strcmp( child->className(), "Vocabulary" ) == 0 ) {
01400             Vocabulary* childVocab = (Vocabulary*)child;
01401             searchRec( query, firstLang, testLang, childVocab, results );
01402         }
01403         else if( strcmp( child->className(), "Folder" ) == 0 ) {
01404             Folder* childFolder = (Folder*)child;
01405             searchRec( query, firstLang, testLang, childFolder, results );
01406         }
01407     }
01408 
01409 }
01410 
01411 void Controller::searchRec( const QString& query, const QString& firstLang, const QString& testLang, Vocabulary* vocab, QValueList<TermKey>& results ) {
01412     if( !vocab || vocab->isMarkedForDeletion() )
01413         return;
01414 
01415     for( Vocabulary::TermMap::ConstIterator it = vocab->begin(); it != vocab->end(); it++ ) {
01416         const Term& term = *it;
01417         bool isStringFound = false;
01418 
01419         if( prefs.isLanguageFilterEnabled() ) {
01420             if( term.isTranslationExists( firstLang ) && term.isTranslationExists( testLang ) ) {
01421                 const Translation& firstLangTrans = term.getTranslation( firstLang );
01422                 const Translation& testLangTrans = term.getTranslation( testLang );
01423                 if( firstLangTrans.getWord().find( query ) != -1 || firstLangTrans.getAlt().find( query ) != -1 ||
01424                     testLangTrans.getWord().find( query ) != -1 || testLangTrans.getAlt().find( query ) != -1 ) {
01425                     isStringFound = true;
01426                 }
01427             }
01428         }
01429         else {
01430             for( Term::TranslationMap::ConstIterator it2 = term.translationsBegin(); it2 != term.translationsEnd(); it2++ ) {
01431                 const Translation& trans = it2.data();
01432                 if( trans.getWord().find( query ) != -1 || trans.getAlt().find( query ) != -1 ) {
01433                     isStringFound = true;
01434                     break;
01435                 }
01436             }
01437         }
01438 
01439         if( !isStringFound ) {
01440             if( prefs.isLanguageFilterEnabled() ) {
01441                 BilingualKey key( firstLang, testLang );
01442                 if( term.isCommentExists( key ) && term.getComment( key ).find( query ) != -1 )
01443                     isStringFound = true;
01444             }
01445             else {
01446                 for( Term::CommentMap::ConstIterator it = term.commentsBegin(); it != term.commentsEnd(); it++ ) {
01447                     const QString& comment = it.data();
01448                     if( comment.find( query ) != -1 ) {
01449                         isStringFound = true;
01450                         break;
01451                     }
01452                 }
01453             }
01454         }
01455 
01456         if( isStringFound ) {
01457             TermKey termKey( term.getId(), vocab->getId() );
01458             results.append( termKey );
01459         }
01460     }
01461 }
01462 
01463 QValueList<TermKey> Controller::getSearchResults() const {
01464     return( searchResults ); 
01465 }
01466 
01467 int Controller::getSearchResultsCount() const {
01468     return( searchResults.count() );
01469 }
01470 
01471 bool Controller::deleteObsoleteData() {
01472     QDir applDir( applicationDirName );
01473     if( applDir.exists() ) {
01474         QStringList fileList = applDir.entryList( "*.dat.z" );
01475         for( QStringList::Iterator it = fileList.begin(); it != fileList.end(); it++ ) {
01476             QString filename = *it;
01477             if( filename.left( 5 ) != "quiz_" ) {
01478                 if( !QFile::remove( applicationDirName + "/" + filename ) )
01479                     return( false );
01480             }
01481         }
01482     }
01483     return( true );
01484 }

Generated on Sun Mar 1 17:30:47 2009 for toMOTko by  doxygen 1.5.6