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