00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020 #include "konq_historymgr.h"
00021
00022
00023 #include <dcopclient.h>
00024
00025 #include <kapplication.h>
00026 #include <kdebug.h>
00027 #include <ksavefile.h>
00028 #include <ksimpleconfig.h>
00029 #include <kstandarddirs.h>
00030
00031 #include <zlib.h>
00032
00033 const Q_UINT32 KonqHistoryManager::s_historyVersion = 2;
00034
00035 KonqHistoryManager::KonqHistoryManager( QObject *parent, const char *name )
00036 : KParts::HistoryProvider( parent, name ),
00037 KonqHistoryComm( "KonqHistoryManager" )
00038 {
00039 m_updateTimer = new QTimer( this );
00040
00041
00042 KConfig *config = KGlobal::config();
00043 KConfigGroupSaver cs( config, "HistorySettings" );
00044 m_maxCount = config->readNumEntry( "Maximum of History entries", 500 );
00045 m_maxCount = QMAX( 1, m_maxCount );
00046 m_maxAgeDays = config->readNumEntry( "Maximum age of History entries", 90);
00047
00048 m_history.setAutoDelete( true );
00049 m_filename = locateLocal( "data",
00050 QString::fromLatin1("konqueror/konq_history" ));
00051
00052 if ( !kapp->dcopClient()->isAttached() )
00053 kapp->dcopClient()->attach();
00054
00055
00056
00057 m_pCompletion = new KCompletion;
00058 m_pCompletion->setOrder( KCompletion::Weighted );
00059
00060
00061 loadHistory();
00062
00063 connect( m_updateTimer, SIGNAL( timeout() ), SLOT( slotEmitUpdated() ));
00064 }
00065
00066
00067 KonqHistoryManager::~KonqHistoryManager()
00068 {
00069 delete m_pCompletion;
00070 clearPending();
00071 }
00072
00073 bool KonqHistoryManager::isSenderOfBroadcast()
00074 {
00075 DCOPClient *dc = callingDcopClient();
00076 return !dc || (dc->senderId() == dc->appId());
00077 }
00078
00079
00080 bool KonqHistoryManager::loadHistory()
00081 {
00082 clearPending();
00083 m_history.clear();
00084 m_pCompletion->clear();
00085
00086 QFile file( m_filename );
00087 if ( !file.open( IO_ReadOnly ) ) {
00088 if ( file.exists() )
00089 kdWarning() << "Can't open " << file.name() << endl;
00090
00091
00092 bool ret = loadFallback();
00093 emit loadingFinished();
00094 return ret;
00095 }
00096
00097 QDataStream fileStream( &file );
00098 QByteArray data;
00099
00100
00101 QDataStream crcStream( data, IO_ReadOnly );
00102
00103 if ( !fileStream.atEnd() ) {
00104 Q_UINT32 version;
00105 fileStream >> version;
00106
00107 QDataStream *stream = &fileStream;
00108
00109 bool crcChecked = false;
00110 bool crcOk = false;
00111
00112 if ( version == 2 ) {
00113 Q_UINT32 crc;
00114
00115 crcChecked = true;
00116
00117 fileStream >> crc >> data;
00118
00119 crcOk = crc32( 0, reinterpret_cast<unsigned char *>( data.data() ), data.size() ) == crc;
00120
00121 stream = &crcStream;
00122 }
00123 else if ( version == 1 )
00124 version = 2;
00125
00126 if ( s_historyVersion != version || ( crcChecked && !crcOk ) ) {
00127 kdWarning() << "The history version doesn't match, aborting loading" << endl;
00128 file.close();
00129 emit loadingFinished();
00130 return false;
00131 }
00132
00133
00134
00135
00136 Q_UINT32 dummy;
00137 *stream >> dummy;
00138 *stream >> dummy;
00139
00140 while ( !stream->atEnd() ) {
00141 KonqHistoryEntry *entry = new KonqHistoryEntry;
00142 Q_CHECK_PTR( entry );
00143 *stream >> *entry;
00144
00145
00146 m_history.append( entry );
00147
00148
00149 m_pCompletion->addItem( entry->url.prettyURL(),
00150 entry->numberOfTimesVisited );
00151 m_pCompletion->addItem( entry->typedURL,
00152 entry->numberOfTimesVisited );
00153
00154
00155 QString urlString = entry->url.url();
00156 KParts::HistoryProvider::insert( urlString );
00157
00158
00159 QString urlString2 = entry->url.prettyURL();
00160 if ( urlString != urlString2 )
00161 KParts::HistoryProvider::insert( urlString2 );
00162 }
00163
00164 kdDebug(1203) << "## loaded: " << m_history.count() << " entries." << endl;
00165
00166 m_history.sort();
00167 adjustSize();
00168 }
00169
00170
00171
00172
00173
00174
00175 file.close();
00176 emit loadingFinished();
00177
00178 return true;
00179 }
00180
00181
00182
00183 bool KonqHistoryManager::saveHistory()
00184 {
00185 KSaveFile file( m_filename );
00186 if ( file.status() != 0 ) {
00187 kdWarning() << "Can't open " << file.name() << endl;
00188 return false;
00189 }
00190
00191 QDataStream *fileStream = file.dataStream();
00192 *fileStream << s_historyVersion;
00193
00194 QByteArray data;
00195 QDataStream stream( data, IO_WriteOnly );
00196
00197 stream << m_maxCount;
00198 stream << m_maxAgeDays;
00199
00200 QPtrListIterator<KonqHistoryEntry> it( m_history );
00201 KonqHistoryEntry *entry;
00202 while ( (entry = it.current()) ) {
00203 stream << *entry;
00204 ++it;
00205 }
00206
00207 Q_UINT32 crc = crc32( 0, reinterpret_cast<unsigned char *>( data.data() ), data.size() );
00208 *fileStream << crc << data;
00209
00210 file.close();
00211
00212 return true;
00213 }
00214
00215
00216 void KonqHistoryManager::adjustSize()
00217 {
00218 KonqHistoryEntry *entry = m_history.getFirst();
00219
00220 while ( m_history.count() > m_maxCount || isExpired( entry ) ) {
00221 m_pCompletion->removeItem( entry->url.prettyURL() );
00222 m_pCompletion->removeItem( entry->typedURL );
00223
00224 QString urlString = entry->url.url();
00225 KParts::HistoryProvider::remove( urlString );
00226
00227 addToUpdateList( urlString );
00228
00229 emit entryRemoved( m_history.getFirst() );
00230 m_history.removeFirst();
00231
00232 entry = m_history.getFirst();
00233 }
00234 }
00235
00236
00237 void KonqHistoryManager::addPending( const KURL& url, const QString& typedURL,
00238 const QString& title )
00239 {
00240 addToHistory( true, url, typedURL, title );
00241 }
00242
00243 void KonqHistoryManager::confirmPending( const KURL& url,
00244 const QString& typedURL,
00245 const QString& title )
00246 {
00247 addToHistory( false, url, typedURL, title );
00248 }
00249
00250
00251 void KonqHistoryManager::addToHistory( bool pending, const KURL& _url,
00252 const QString& typedURL,
00253 const QString& title )
00254 {
00255 kdDebug(1203) << "## addToHistory: " << _url.prettyURL() << "Typed URL: " << typedURL << ", Title: " << title << endl;
00256
00257 if ( filterOut( _url ) )
00258 return;
00259 KURL url( _url );
00260 url.setPass( "" );
00261 url.setHost( url.host().lower() );
00262 KonqHistoryEntry entry;
00263 QString u = url.prettyURL();
00264 entry.url = url;
00265 if ( u != typedURL )
00266 entry.typedURL = typedURL;
00267
00268
00269
00270
00271 if ( !pending && u != title )
00272 entry.title = title;
00273 entry.firstVisited = QDateTime::currentDateTime();
00274 entry.lastVisited = entry.firstVisited;
00275
00276 if ( !pending ) {
00277 QMapIterator<QString,KonqHistoryEntry*> it = m_pending.find( u );
00278
00279 if ( it != m_pending.end() ) {
00280 delete it.data();
00281 m_pending.remove( it );
00282
00283
00284
00285
00286 entry.numberOfTimesVisited = 0;
00287 }
00288 }
00289
00290 else {
00291
00292
00293
00294 KonqHistoryEntry *oldEntry = findEntry( url );
00295 m_pending.insert( u, oldEntry ?
00296 new KonqHistoryEntry( *oldEntry ) : 0L );
00297 }
00298
00299
00300 emitAddToHistory( entry );
00301 }
00302
00303
00304
00305
00306
00307
00308 void KonqHistoryManager::insert( const QString& url )
00309 {
00310 KURL u = url;
00311 if ( !filterOut( url ) || u.protocol() == "about" ) {
00312 return;
00313 }
00314
00315 KonqHistoryEntry entry;
00316 entry.url = u;
00317 entry.firstVisited = QDateTime::currentDateTime();
00318 entry.lastVisited = entry.firstVisited;
00319 emitAddToHistory( entry );
00320 }
00321
00322 void KonqHistoryManager::emitAddToHistory( const KonqHistoryEntry& entry )
00323 {
00324 QByteArray data;
00325 QDataStream stream( data, IO_WriteOnly );
00326 stream << entry << objId();
00327 kapp->dcopClient()->send( "konqueror*", "KonqHistoryManager",
00328 "notifyHistoryEntry(KonqHistoryEntry, QCString)",
00329 data );
00330 }
00331
00332
00333 void KonqHistoryManager::removePending( const KURL& url )
00334 {
00335
00336
00337 if ( url.isLocalFile() )
00338 return;
00339
00340 QMapIterator<QString,KonqHistoryEntry*> it = m_pending.find( url.prettyURL() );
00341 if ( it != m_pending.end() ) {
00342 KonqHistoryEntry *oldEntry = it.data();
00343 emitRemoveFromHistory( url );
00344
00345 if ( oldEntry )
00346 emitAddToHistory( *oldEntry );
00347
00348 delete oldEntry;
00349 m_pending.remove( it );
00350 }
00351 }
00352
00353
00354 void KonqHistoryManager::clearPending()
00355 {
00356 QMapIterator<QString,KonqHistoryEntry*> it = m_pending.begin();
00357 while ( it != m_pending.end() ) {
00358 delete it.data();
00359 ++it;
00360 }
00361 m_pending.clear();
00362 }
00363
00364 void KonqHistoryManager::emitRemoveFromHistory( const KURL& url )
00365 {
00366 QByteArray data;
00367 QDataStream stream( data, IO_WriteOnly );
00368 stream << url << objId();
00369 kapp->dcopClient()->send( "konqueror*", "KonqHistoryManager",
00370 "notifyRemove(KURL, QCString)", data );
00371 }
00372
00373 void KonqHistoryManager::emitRemoveFromHistory( const KURL::List& urls )
00374 {
00375 QByteArray data;
00376 QDataStream stream( data, IO_WriteOnly );
00377 stream << urls << objId();
00378 kapp->dcopClient()->send( "konqueror*", "KonqHistoryManager",
00379 "notifyRemove(KURL::List, QCString)", data );
00380 }
00381
00382 void KonqHistoryManager::emitClear()
00383 {
00384 QByteArray data;
00385 QDataStream stream( data, IO_WriteOnly );
00386 stream << objId();
00387 kapp->dcopClient()->send( "konqueror*", "KonqHistoryManager",
00388 "notifyClear(QCString)", data );
00389 }
00390
00391 void KonqHistoryManager::emitSetMaxCount( Q_UINT32 count )
00392 {
00393 QByteArray data;
00394 QDataStream stream( data, IO_WriteOnly );
00395 stream << count << objId();
00396 kapp->dcopClient()->send( "konqueror*", "KonqHistoryManager",
00397 "notifyMaxCount(Q_UINT32, QCString)", data );
00398 }
00399
00400 void KonqHistoryManager::emitSetMaxAge( Q_UINT32 days )
00401 {
00402 QByteArray data;
00403 QDataStream stream( data, IO_WriteOnly );
00404 stream << days << objId();
00405 kapp->dcopClient()->send( "konqueror*", "KonqHistoryManager",
00406 "notifyMaxAge(Q_UINT32, QCString)", data );
00407 }
00408
00410
00411
00412 void KonqHistoryManager::notifyHistoryEntry( KonqHistoryEntry e,
00413 QCString )
00414 {
00415
00416
00417 KonqHistoryEntry *entry = findEntry( e.url );
00418 QString urlString = e.url.url();
00419
00420 if ( !entry ) {
00421 entry = new KonqHistoryEntry;
00422 entry->url = e.url;
00423 entry->firstVisited = e.firstVisited;
00424 entry->numberOfTimesVisited = 0;
00425 m_history.append( entry );
00426 KParts::HistoryProvider::insert( urlString );
00427 }
00428
00429 if ( !e.typedURL.isEmpty() )
00430 entry->typedURL = e.typedURL;
00431 if ( !e.title.isEmpty() )
00432 entry->title = e.title;
00433 entry->numberOfTimesVisited += e.numberOfTimesVisited;
00434 entry->lastVisited = e.lastVisited;
00435
00436 m_pCompletion->addItem( entry->url.prettyURL() );
00437 m_pCompletion->addItem( entry->typedURL );
00438
00439
00440
00441 adjustSize();
00442
00443 if ( isSenderOfBroadcast() )
00444 saveHistory();
00445
00446 addToUpdateList( urlString );
00447 emit entryAdded( entry );
00448 }
00449
00450 void KonqHistoryManager::notifyMaxCount( Q_UINT32 count, QCString )
00451 {
00452 m_maxCount = count;
00453 clearPending();
00454 adjustSize();
00455
00456 KConfig *config = KGlobal::config();
00457 KConfigGroupSaver cs( config, "HistorySettings" );
00458 config->writeEntry( "Maximum of History entries", m_maxCount );
00459
00460 if ( isSenderOfBroadcast() ) {
00461 saveHistory();
00462 config->sync();
00463 }
00464 }
00465
00466 void KonqHistoryManager::notifyMaxAge( Q_UINT32 days, QCString )
00467 {
00468 m_maxAgeDays = days;
00469 clearPending();
00470 adjustSize();
00471
00472 KConfig *config = KGlobal::config();
00473 KConfigGroupSaver cs( config, "HistorySettings" );
00474 config->writeEntry( "Maximum age of History entries", m_maxAgeDays );
00475
00476 if ( isSenderOfBroadcast() ) {
00477 saveHistory();
00478 config->sync();
00479 }
00480 }
00481
00482 void KonqHistoryManager::notifyClear( QCString )
00483 {
00484 clearPending();
00485 m_history.clear();
00486 m_pCompletion->clear();
00487
00488 if ( isSenderOfBroadcast() )
00489 saveHistory();
00490
00491 KParts::HistoryProvider::clear();
00492 }
00493
00494 void KonqHistoryManager::notifyRemove( KURL url, QCString )
00495 {
00496 kdDebug(1203) << "#### Broadcast: remove entry:: " << url.prettyURL() << endl;
00497
00498 KonqHistoryEntry *entry = m_history.findEntry( url );
00499 if ( entry ) {
00500 m_pCompletion->removeItem( entry->url.prettyURL() );
00501 m_pCompletion->removeItem( entry->typedURL );
00502
00503 QString urlString = entry->url.url();
00504 KParts::HistoryProvider::remove( urlString );
00505
00506 addToUpdateList( urlString );
00507
00508 m_history.take();
00509 emit entryRemoved( entry );
00510 delete entry;
00511
00512 if ( isSenderOfBroadcast() )
00513 saveHistory();
00514 }
00515 }
00516
00517 void KonqHistoryManager::notifyRemove( KURL::List urls, QCString )
00518 {
00519 kdDebug(1203) << "#### Broadcast: removing list!" << endl;
00520
00521 bool doSave = false;
00522 KURL::List::Iterator it = urls.begin();
00523 while ( it != urls.end() ) {
00524 KonqHistoryEntry *entry = m_history.findEntry( *it );
00525 if ( entry ) {
00526 m_pCompletion->removeItem( entry->url.prettyURL() );
00527 m_pCompletion->removeItem( entry->typedURL );
00528
00529 QString urlString = entry->url.url();
00530 KParts::HistoryProvider::remove( urlString );
00531
00532 addToUpdateList( urlString );
00533
00534 m_history.take();
00535 emit entryRemoved( entry );
00536 delete entry;
00537 doSave = true;
00538 }
00539
00540 ++it;
00541 }
00542
00543 if (doSave && isSenderOfBroadcast())
00544 saveHistory();
00545 }
00546
00547
00548
00549 bool KonqHistoryManager::loadFallback()
00550 {
00551 QString file = locateLocal( "config", QString::fromLatin1("konq_history"));
00552 if ( file.isEmpty() )
00553 return false;
00554
00555 KonqHistoryEntry *entry;
00556 KSimpleConfig config( file );
00557 config.setGroup("History");
00558 QStringList items = config.readListEntry( "CompletionItems" );
00559 QStringList::Iterator it = items.begin();
00560
00561 while ( it != items.end() ) {
00562 entry = createFallbackEntry( *it );
00563 if ( entry ) {
00564 m_history.append( entry );
00565 m_pCompletion->addItem( entry->url.prettyURL(),
00566 entry->numberOfTimesVisited );
00567
00568 KParts::HistoryProvider::insert( entry->url.url() );
00569 }
00570 ++it;
00571 }
00572
00573 m_history.sort();
00574 adjustSize();
00575 saveHistory();
00576
00577 return true;
00578 }
00579
00580
00581
00582
00583 KonqHistoryEntry * KonqHistoryManager::createFallbackEntry(const QString& item) const
00584 {
00585
00586 uint len = item.length();
00587 uint weight = 1;
00588
00589
00590 int index = item.findRev(':');
00591 if ( index > 0 ) {
00592 bool ok;
00593 weight = item.mid( index + 1 ).toUInt( &ok );
00594 if ( !ok )
00595 weight = 1;
00596
00597 len = index;
00598 }
00599
00600
00601 KonqHistoryEntry *entry = 0L;
00602 KURL u( item.left( len ));
00603 if ( !u.isMalformed() ) {
00604 entry = new KonqHistoryEntry;
00605
00606 entry->url = u;
00607 entry->numberOfTimesVisited = weight;
00608
00609 entry->lastVisited = QDateTime::currentDateTime();
00610 }
00611
00612 return entry;
00613 }
00614
00615 KonqHistoryEntry * KonqHistoryManager::findEntry( const KURL& url )
00616 {
00617
00618 if ( !KParts::HistoryProvider::contains( url.url() ) )
00619 return 0L;
00620
00621 return m_history.findEntry( url );
00622 }
00623
00624 bool KonqHistoryManager::filterOut( const KURL& url )
00625 {
00626 return ( url.isLocalFile() || url.host().isEmpty() );
00627 }
00628
00629 void KonqHistoryManager::slotEmitUpdated()
00630 {
00631 emit KParts::HistoryProvider::updated( m_updateURLs );
00632 m_updateURLs.clear();
00633 }
00634
00635 QStringList KonqHistoryManager::allURLs() const
00636 {
00637 QStringList list;
00638 KonqHistoryIterator it ( m_history );
00639 for ( ; it.current(); ++it )
00640 list.append( it.current()->url.url() );
00641
00642 return list;
00643 }
00644
00646
00647
00648 KonqHistoryEntry * KonqHistoryList::findEntry( const KURL& url )
00649 {
00650
00651 KonqHistoryEntry *entry = last();
00652 while ( entry ) {
00653 if ( entry->url == url )
00654 return entry;
00655
00656 entry = prev();
00657 }
00658
00659 return 0L;
00660 }
00661
00662
00663 int KonqHistoryList::compareItems( QPtrCollection::Item item1,
00664 QPtrCollection::Item item2 )
00665 {
00666 KonqHistoryEntry *entry1 = static_cast<KonqHistoryEntry *>( item1 );
00667 KonqHistoryEntry *entry2 = static_cast<KonqHistoryEntry *>( item2 );
00668
00669 if ( entry1->lastVisited > entry2->lastVisited )
00670 return 1;
00671 else if ( entry1->lastVisited < entry2->lastVisited )
00672 return -1;
00673 else
00674 return 0;
00675 }
00676
00677 using namespace KParts;
00678
00679 #include "konq_historymgr.moc"