00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028 #include "documentlist.h"
00029 #include "serverinterface.h"
00030 #include "mediadlg.h"
00031
00032
00033 #include <opie2/oglobal.h>
00034 #include <opie2/odebug.h>
00035 #include <opie2/oresource.h>
00036 #include <qtopia/config.h>
00037 #include <qtopia/mimetype.h>
00038 #include <qtopia/private/categories.h>
00039 #include <qtopia/qpeapplication.h>
00040 #include <qtopia/applnk.h>
00041 #include <qtopia/storage.h>
00042 #ifdef Q_WS_QWS
00043 #include <qtopia/qcopenvelope_qws.h>
00044 #endif
00045 using namespace Opie::Core;
00046
00047
00048 #include <qtimer.h>
00049 #include <qfileinfo.h>
00050 #include <qtextstream.h>
00051 #include <qfile.h>
00052 #include <qdir.h>
00053 #include <qpainter.h>
00054 #include <qimage.h>
00055 #include <qcopchannel_qws.h>
00056 #include <qlistview.h>
00057 #include <qlist.h>
00058 #include <qpixmap.h>
00059
00060
00061 AppLnkSet *DocumentList::appLnkSet = 0;
00062
00063 static const int MAX_SEARCH_DEPTH = 10;
00064
00065
00066 class DocumentListPrivate : public QObject {
00067 Q_OBJECT
00068 public:
00069 DocumentListPrivate( ServerInterface *gui );
00070 ~DocumentListPrivate();
00071
00072 void initialize();
00073
00074 const QString nextFile();
00075 const DocLnk *iterate();
00076 bool store( DocLnk* dl );
00077 void estimatedPercentScanned();
00078 void appendDocpath(FileSystem*);
00079
00080
00081 DocLnkSet dls;
00082 QDict<void> reference;
00083 QDictIterator<void> *dit;
00084 enum { Find, RemoveKnownFiles, MakeUnknownFiles, Done } state;
00085
00086 QStringList docPaths;
00087 unsigned int docPathsSearched;
00088
00089 int searchDepth;
00090 QDir *listDirs[MAX_SEARCH_DEPTH];
00091 const QFileInfoList *lists[MAX_SEARCH_DEPTH];
00092 unsigned int listPositions[MAX_SEARCH_DEPTH];
00093
00094 StorageInfo *storage;
00095
00096 int tid;
00097
00098 ServerInterface *serverGui;
00099
00100 bool needToSendAllDocLinks;
00101 bool sendAppLnks;
00102 bool sendDocLnks;
00103 bool scanDocs;
00104 };
00105
00106
00107
00108
00109
00110 DocumentList::DocumentList( ServerInterface *serverGui, bool ,
00111 QObject *parent, const char *name )
00112 : QObject( parent, name )
00113 {
00114 appLnkSet = new AppLnkSet( MimeType::appsFolderName() );
00115 d = new DocumentListPrivate( serverGui );
00116 d->needToSendAllDocLinks = false;
00117
00118 Config cfg( "Launcher" );
00119 cfg.setGroup( "DocTab" );
00120 d->scanDocs = cfg.readBoolEntry( "Enable", true );
00121 odebug << "DocumentList::DocumentList() : scanDocs = " << d->scanDocs << "" << oendl;
00122
00123 QTimer::singleShot( 0, this, SLOT( startInitialScan() ) );
00124 }
00125
00126 void DocumentList::startInitialScan()
00127 {
00128 reloadAppLnks();
00129 reloadDocLnks();
00130 }
00131
00132 DocumentList::~DocumentList()
00133 {
00134 delete appLnkSet;
00135 delete d;
00136 }
00137
00138
00139 void DocumentList::add( const DocLnk& doc )
00140 {
00141 if ( d->serverGui && QFile::exists( doc.file() ) )
00142 d->serverGui->documentAdded( doc );
00143 }
00144
00145
00146 void DocumentList::start()
00147 {
00148 resume();
00149 }
00150
00151
00152 void DocumentList::pause()
00153 {
00154
00155 killTimer( d->tid );
00156 d->tid = 0;
00157 }
00158
00159
00160 void DocumentList::resume()
00161 {
00162 if ( d->tid == 0 ) {
00163 d->tid = startTimer( 20 );
00164
00165 }
00166 }
00167
00168
00169
00170
00171
00172
00173
00174
00175
00176
00177
00178
00179
00180
00181
00182
00183
00184
00185
00186
00187
00188 void DocumentList::rescan()
00189 {
00190 owarn << "XXXXXXXXXXX rescan" << oendl;
00191 pause();
00192 d->initialize();
00193 resume();
00194 }
00195
00196
00197 void DocumentList::timerEvent( QTimerEvent *te )
00198 {
00199 if ( te->timerId() == d->tid ) {
00200
00201 if ( d->serverGui )
00202 d->serverGui->aboutToAddBegin();
00203 for (int i = 0; i < 3; i++ ) {
00204 const DocLnk *lnk = d->iterate();
00205 if ( lnk ) {
00206 add( *lnk );
00207 } else {
00208
00209 pause();
00210 if ( d->serverGui )
00211 d->serverGui->documentScanningProgress( 100 );
00212 if ( d->needToSendAllDocLinks )
00213 sendAllDocLinks();
00214 break;
00215 }
00216 }
00217 if ( d->serverGui )
00218 d->serverGui->aboutToAddEnd();
00219 }
00220 }
00221
00222
00223 void DocumentList::reloadAppLnks()
00224 {
00225 if ( d->sendAppLnks && d->serverGui ) {
00226 d->serverGui->applicationScanningProgress( 0 );
00227 d->serverGui->allApplicationsRemoved();
00228 }
00229
00230 delete appLnkSet;
00231 appLnkSet = new AppLnkSet( MimeType::appsFolderName() );
00232
00233 if ( d->sendAppLnks && d->serverGui ) {
00234 static QStringList prevTypeList;
00235 QStringList types = appLnkSet->types();
00236 for ( QStringList::Iterator ittypes=types.begin(); ittypes!=types.end(); ++ittypes) {
00237 if ( !(*ittypes).isEmpty() ) {
00238 if ( !prevTypeList.contains(*ittypes) ) {
00239 QString name = appLnkSet->typeName(*ittypes);
00240 QPixmap pm = appLnkSet->typePixmap(*ittypes);
00241 QPixmap bgPm = appLnkSet->typeBigPixmap(*ittypes);
00242
00243 if (pm.isNull())
00244 {
00245 pm = OResource::loadImage( "UnknownDocument", OResource::SmallIcon );
00246 bgPm = OResource::loadImage( "UnknownDocument", OResource::BigIcon );
00247 }
00248
00249
00250 d->serverGui->typeAdded( *ittypes, name.isNull() ? (*ittypes) : name, pm, bgPm );
00251 }
00252 prevTypeList.remove(*ittypes);
00253 }
00254 }
00255 for ( QStringList::Iterator ittypes=prevTypeList.begin(); ittypes!=prevTypeList.end(); ++ittypes) {
00256 d->serverGui->typeRemoved(*ittypes);
00257 }
00258 prevTypeList = types;
00259 }
00260
00261 QListIterator<AppLnk> itapp( appLnkSet->children() );
00262 AppLnk* l;
00263 while ( (l=itapp.current()) ) {
00264 ++itapp;
00265 if ( d->sendAppLnks && d->serverGui )
00266 d->serverGui->applicationAdded( l->type(), *l );
00267 }
00268
00269 if ( d->sendAppLnks && d->serverGui )
00270 d->serverGui->applicationScanningProgress( 100 );
00271 }
00272
00273 void DocumentList::reloadDocLnks()
00274 {
00275 if ( !d->scanDocs )
00276 return;
00277
00278 if ( d->sendDocLnks && d->serverGui ) {
00279 d->serverGui->documentScanningProgress( 0 );
00280 d->serverGui->allDocumentsRemoved();
00281 }
00282
00283 rescan();
00284 }
00285
00286 void DocumentList::reforceDocuments()
00287 {
00288 Config cfg( "Launcher" );
00289 cfg.setGroup( "DocTab" );
00290 d->scanDocs = cfg.readBoolEntry( "Enable", true );
00291 reloadDocLnks();
00292 }
00293
00294 void DocumentList::linkChanged( QString arg )
00295 {
00296 odebug << "linkchanged( " << arg << " )" << oendl;
00297
00298 if ( arg.isNull() || OGlobal::isAppLnkFileName( arg ) ) {
00299 reloadAppLnks();
00300 } else {
00301
00302 const QList<DocLnk> &list = d->dls.children();
00303 QListIterator<DocLnk> it( list );
00304 while ( it.current() ) {
00305 DocLnk *doc = it.current();
00306 ++it;
00307 if ( ( doc->linkFileKnown() && doc->linkFile() == arg )
00308 || ( doc->fileKnown() && doc->file() == arg ) ) {
00309
00310 DocLnk* dl = new DocLnk( arg );
00311
00312 if ( d->store( dl ) ) {
00313
00314
00315
00316 if ( d->serverGui )
00317 d->serverGui->documentChanged( *doc, *dl );
00318
00319 } else {
00320
00321
00322
00323 if ( d->serverGui )
00324 d->serverGui->documentRemoved( *doc );
00325
00326 }
00327 d->dls.remove( doc );
00328 delete doc;
00329 return;
00330 }
00331 }
00332
00333 DocLnk* dl = new DocLnk( arg );
00334 if ( d->store( dl ) ) {
00335
00336
00337 add( *dl );
00338 }
00339
00340 }
00341 }
00342
00343 void DocumentList::restoreDone()
00344 {
00345 reloadAppLnks();
00346 reloadDocLnks();
00347 }
00348
00349 void DocumentList::DiffAppLnks()
00350 {
00351 static AppLnkSet *appLnkSet2;
00352
00353 appLnkSet2 = new AppLnkSet( MimeType::appsFolderName() );
00354
00355 if ( d->sendAppLnks && d->serverGui ) {
00356 static QStringList prevTypeList = appLnkSet->types();
00357 QStringList types = appLnkSet2->types();
00358 for ( QStringList::Iterator ittypes=types.begin(); ittypes!=types.end(); ++ittypes) {
00359 if ( !(*ittypes).isEmpty() ) {
00360 if ( !prevTypeList.contains(*ittypes) ) {
00361 QString name = appLnkSet2->typeName(*ittypes);
00362 QPixmap pm = appLnkSet2->typePixmap(*ittypes);
00363 QPixmap bgPm = appLnkSet2->typeBigPixmap(*ittypes);
00364
00365 if (pm.isNull())
00366 {
00367 pm = OResource::loadImage( "UnknownDocument", OResource::SmallIcon );
00368 bgPm = OResource::loadImage( "UnknownDocument", OResource::BigIcon );
00369 }
00370
00371
00372 d->serverGui->typeAdded( *ittypes, name.isNull() ? (*ittypes) : name, pm, bgPm );
00373 }
00374 prevTypeList.remove(*ittypes);
00375 }
00376 }
00377 for ( QStringList::Iterator ittypes=prevTypeList.begin(); ittypes!=prevTypeList.end(); ++ittypes) {
00378 d->serverGui->typeRemoved(*ittypes);
00379 }
00380 prevTypeList = types;
00381 }
00382
00383
00384 QListIterator<AppLnk> it1( appLnkSet->children() );
00385 QListIterator<AppLnk> it2( appLnkSet2->children() );
00386
00387 AppLnk *i;
00388 AppLnk *j;
00389 bool found;
00390
00391 while ( (j=it2.current()) ) {
00392 it1 = appLnkSet->children();
00393 found = false;
00394 while ( (i=it1.current()) ){
00395 if (j->name().ascii() && i->name().ascii() && strcmp(i->name().ascii(),j->name().ascii()) == 0)
00396 found = true;
00397 ++it1;
00398 }
00399 if (!found) {
00400 odebug << "Item " << j->name().ascii() << " needs to be added" << oendl;
00401 d->serverGui->applicationAdded( j->type(), *j );
00402 }
00403 ++it2;
00404 }
00405
00406 it1 = appLnkSet->children();
00407 while ( (i=it1.current()) ) {
00408 it2 = appLnkSet2->children();
00409 found = false;
00410 while ( (j=it2.current()) ){
00411 if (j->name().ascii() && i->name().ascii() && strcmp(i->name().ascii(),j->name().ascii()) == 0)
00412 found = true;
00413 ++it2;
00414 }
00415 if (!found) {
00416 odebug << "Item " << i->name().ascii() << " needs to be removed" << oendl;
00417 d->serverGui->applicationRemoved( i->type(), *i );
00418 }
00419
00420 ++it1;
00421 }
00422
00423 delete appLnkSet;
00424 appLnkSet = appLnkSet2;
00425
00426 }
00427 void DocumentList::storageChanged()
00428 {
00429 QTime t;
00430
00431
00432 t.start();
00433 DiffAppLnks();
00434
00435 odebug << "Reload App links took " << t.elapsed() << " ms" << oendl;
00436 reloadDocLnks();
00437
00438 odebug << "Reload All links took " << t.elapsed() << " ms" << oendl;
00439
00440
00441
00442
00443
00444 }
00445
00446 void DocumentList::sendAllDocLinks()
00447 {
00448 if ( d->tid != 0 ) {
00449
00450
00451 d->needToSendAllDocLinks = true;
00452 return;
00453 }
00454
00455 QString contents;
00456 Categories cats;
00457 for ( QListIterator<DocLnk> it( d->dls.children() ); it.current(); ++it ) {
00458 DocLnk *doc = it.current();
00459 QFileInfo fi( doc->file() );
00460 if ( !fi.exists() )
00461 continue;
00462
00463 bool fake = !doc->linkFileKnown();
00464 if ( !fake ) {
00465 QFile f( doc->linkFile() );
00466 if ( f.open( IO_ReadOnly ) ) {
00467 QTextStream ts( &f );
00468 ts.setEncoding( QTextStream::UnicodeUTF8 );
00469 QString docLnk = ts.read();
00470
00471 int start = docLnk.find( "\nLinkFile = " ) + 1;
00472 if ( start > 0 ) {
00473 int end = docLnk.find( "\n", start + 1 ) + 1;
00474 contents += docLnk.left(start);
00475 contents += docLnk.mid(end);
00476 } else {
00477 contents += docLnk;
00478 }
00479 contents += "LinkFile = " + doc->linkFile() + "\n";
00480
00481 f.close();
00482 } else
00483 fake = TRUE;
00484 }
00485 if (fake) {
00486 contents += "[Desktop Entry]\n";
00487 contents += "Categories = " +
00488 cats.labels("Document View",doc->categories()).join(";") + "\n";
00489 contents += "Name = "+doc->name()+"\n";
00490 contents += "Type = "+doc->type()+"\n";
00491 }
00492 contents += "File = "+doc->file()+"\n";
00493 contents += QString("Size = %1\n").arg( fi.size() );
00494 }
00495
00496
00497 #ifndef QT_NO_COP
00498 QCopEnvelope e( "QPE/Desktop", "docLinks(QString)" );
00499 e << contents;
00500 #endif
00501
00502
00503 d->needToSendAllDocLinks = false;
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 DocumentListPrivate::DocumentListPrivate( ServerInterface *gui )
00530 {
00531 storage = new StorageInfo( this );
00532 serverGui = gui;
00533 if ( serverGui ) {
00534 sendAppLnks = serverGui->requiresApplications();
00535 sendDocLnks = serverGui->requiresDocuments();
00536 } else {
00537 sendAppLnks = false;
00538 sendDocLnks = false;
00539 }
00540 for ( int i = 0; i < MAX_SEARCH_DEPTH; i++ ) {
00541 listDirs[i] = 0;
00542 lists[i] = 0;
00543 listPositions[i] = 0;
00544 }
00545 initialize();
00546 tid = 0;
00547 }
00548
00549 void DocumentListPrivate::appendDocpath(FileSystem*fs)
00550 {
00551 Config c( "qpe" );
00552 c.setGroup( "Startup" );
00553 QDir defPath(fs->path()+"/Documents");
00554 QFileInfo f(fs->path()+"/.opiestorage.cf");
00555 if ( c.readNumEntry( "FirstUse", 42 ) == 0 && !f.exists()) {
00556 Mediadlg dlg(fs);
00557 if (QDialog::Accepted != QPEApplication::execDialog( &dlg )) {
00558 return;
00559 }
00560 }
00561 Config conf(f.filePath(), Config::File );
00562 conf.setGroup("main");
00563 if (!conf.readBoolEntry("check",false)) {
00564 return;
00565 }
00566 conf.setGroup("subdirs");
00567 bool read_all = conf.readBoolEntry("wholemedia",true);
00568 if (read_all) {
00569 docPaths+=fs->path();
00570 return;
00571 }
00572 QStringList subDirs = conf.readListEntry("subdirs",':');
00573 if (subDirs.isEmpty()) {
00574 if (defPath.exists()) {
00575 docPaths+=defPath.path();
00576 }
00577 return;
00578 }
00579 for (unsigned c = 0; c < subDirs.count();++c) {
00580 QDir docDir(QString(fs->path()+"/"+subDirs[c]));
00581 if (docDir.exists()) {
00582 docPaths+=docDir.path();
00583 }
00584 }
00585 }
00586
00587 void DocumentListPrivate::initialize()
00588 {
00589
00590 dls.clear();
00591 docPaths.clear();
00592 reference.clear();
00593
00594 QDir docDir( QPEApplication::documentDir() );
00595 if ( docDir.exists() )
00596 docPaths += QPEApplication::documentDir();
00597 int i = 1;
00598 const QList<FileSystem> &fs = storage->fileSystems();
00599 QListIterator<FileSystem> it( fs );
00600 for ( ; it.current(); ++it ) {
00601 if ( (*it)->isRemovable() ) {
00602 appendDocpath((*it));
00603 ++i;
00604 }
00605 }
00606
00607 for ( int i = 0; i < MAX_SEARCH_DEPTH; ++i ) {
00608 if ( listDirs[i] ) {
00609 delete listDirs[i];
00610 listDirs[i] = 0;
00611 }
00612 lists[i] = 0;
00613 listPositions[i] = 0;
00614 }
00615
00616 docPathsSearched = 0;
00617 searchDepth = -1;
00618 state = Find;
00619 dit = 0;
00620 }
00621
00622
00623 DocumentListPrivate::~DocumentListPrivate()
00624 {
00625 for ( int i = 0; i < MAX_SEARCH_DEPTH; i++ )
00626 if ( listDirs[i] )
00627 delete listDirs[i];
00628 delete dit;
00629 }
00630
00631
00632 void DocumentListPrivate::estimatedPercentScanned()
00633 {
00634 double overallProgress = 0.0;
00635 double levelWeight = 75.0;
00636
00637 int topCount = docPaths.count();
00638 if ( topCount > 1 ) {
00639 levelWeight = levelWeight / topCount;
00640 overallProgress += (docPathsSearched - 1) * levelWeight;
00641 }
00642
00643 for ( int d = 0; d <= searchDepth; d++ ) {
00644 if ( listDirs[d] ) {
00645 int items = lists[d]->count();
00646 if ( items > 1 ) {
00647 levelWeight = levelWeight / items;
00648
00649 overallProgress += (listPositions[d] - 3) * levelWeight;
00650 }
00651 } else {
00652 break;
00653 }
00654 }
00655
00656
00657
00658 if ( serverGui )
00659 serverGui->documentScanningProgress( (int)overallProgress );
00660 }
00661
00662
00663 const QString DocumentListPrivate::nextFile()
00664 {
00665 while ( TRUE ) {
00666 while ( searchDepth < 0 ) {
00667
00668 if ( docPathsSearched >= docPaths.count() ) {
00669
00670 return QString::null;
00671 } else {
00672 QDir dir( docPaths[docPathsSearched] );
00673
00674 docPathsSearched++;
00675 if ( !dir.exists( ".Qtopia-ignore" ) ) {
00676 listDirs[0] = new QDir( dir );
00677 lists[0] = listDirs[0]->entryInfoList();
00678 listPositions[0] = 0;
00679 searchDepth = 0;
00680 }
00681 }
00682 }
00683
00684 const QFileInfoList *fil = lists[searchDepth];
00685 if (!fil) {
00686 return QString::null;
00687 }
00688 QFileInfoList *fl = (QFileInfoList *)fil;
00689 unsigned int pos = listPositions[searchDepth];
00690
00691 if ( pos >= fl->count() ) {
00692
00693 delete listDirs[searchDepth];
00694 listDirs[searchDepth] = 0;
00695 lists[searchDepth] = 0;
00696 listPositions[searchDepth] = 0;
00697 searchDepth--;
00698 } else {
00699 const QFileInfo *fi = fl->at(pos);
00700 listPositions[searchDepth]++;
00701 QString bn = fi->fileName();
00702 if ( bn[0] != '.' ) {
00703 if ( fi->isDir() ) {
00704 if ( bn != "CVS" && bn != "Qtopia" && bn != "QtPalmtop"
00705 && bn != "proc" && bn != "dev" && bn != "bin" && bn != "usr"
00706 && bn != "etc" && bn != "lib" && bn != "sbin" && bn != "tmp" && bn != "var") {
00707
00708 QDir dir( fi->filePath() );
00709
00710 if ( !dir.exists( ".Qtopia-ignore" ) ) {
00711 if ( searchDepth < MAX_SEARCH_DEPTH - 1) {
00712 searchDepth++;
00713 listDirs[searchDepth] = new QDir( dir );
00714 lists[searchDepth] = listDirs[searchDepth]->entryInfoList();
00715 listPositions[searchDepth] = 0;
00716 }
00717 }
00718 }
00719 } else {
00720 estimatedPercentScanned();
00721 return fl->at(pos)->filePath();
00722 }
00723 }
00724 }
00725 }
00726
00727 return QString::null;
00728 }
00729
00730
00731 bool DocumentListPrivate::store( DocLnk* dl )
00732 {
00733
00734 if ( dl && dl->fileKnown() ) {
00735 dls.add( dl );
00736 return TRUE;
00737 }
00738
00739
00740 delete dl;
00741 return FALSE;
00742 }
00743
00744
00745 #define MAGIC_NUMBER ((void*)2)
00746
00747 const DocLnk *DocumentListPrivate::iterate()
00748 {
00749 if ( state == Find ) {
00750
00751 QString file = nextFile();
00752 while ( !file.isNull() ) {
00753 if ( file.right(8) == ".desktop" ) {
00754 DocLnk* dl = new DocLnk( file );
00755 if ( store(dl) )
00756 return dl;
00757 } else {
00758 reference.insert( file, MAGIC_NUMBER );
00759 }
00760 file = nextFile();
00761 }
00762 state = RemoveKnownFiles;
00763
00764 if ( serverGui )
00765 serverGui->documentScanningProgress( 75 );
00766 }
00767
00768 static int iterationI;
00769 static int iterationCount;
00770
00771 if ( state == RemoveKnownFiles ) {
00772
00773 const QList<DocLnk> &list = dls.children();
00774 for ( QListIterator<DocLnk> it( list ); it.current(); ++it ) {
00775 reference.remove( (*it)->file() );
00776
00777 }
00778 dit = new QDictIterator<void>(reference);
00779 state = MakeUnknownFiles;
00780
00781 iterationI = 0;
00782 iterationCount = dit->count();
00783 }
00784
00785 if ( state == MakeUnknownFiles ) {
00786
00787 for (void* c; (c=dit->current()); ++(*dit) ) {
00788 if ( c == MAGIC_NUMBER ) {
00789 DocLnk* dl = new DocLnk;
00790 QFileInfo fi( dit->currentKey() );
00791 dl->setFile( fi.filePath() );
00792 dl->setName( fi.baseName() );
00793 if ( store(dl) ) {
00794 ++*dit;
00795 iterationI++;
00796 if ( serverGui )
00797 serverGui->documentScanningProgress( 75 + (25*iterationI)/iterationCount );
00798 return dl;
00799 }
00800 }
00801 iterationI++;
00802 }
00803
00804 delete dit;
00805 dit = 0;
00806 state = Done;
00807 }
00808
00809
00810 return NULL;
00811 }
00812
00813
00814 #include "documentlist.moc"
00815
00816
00817