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
if ( !pending ) {
00302 QMapIterator<QString,KonqHistoryEntry*> it = m_pending.find( u );
00303
00304
if ( it != m_pending.end() ) {
00305
delete it.data();
00306 m_pending.remove( it );
00307
00308
00309
00310
00311 entry.numberOfTimesVisited = 0;
00312 }
00313 }
00314
00315
else {
00316
00317
00318
00319 KonqHistoryEntry *oldEntry = findEntry( url );
00320 m_pending.insert( u, oldEntry ?
00321
new KonqHistoryEntry( *oldEntry ) : 0L );
00322 }
00323
00324
00325
emitAddToHistory( entry );
00326 }
00327
00328
00329
00330
00331
00332
00333 void KonqHistoryManager::insert(
const QString& url )
00334 {
00335 KURL u ( url );
00336
if ( !
filterOut( u ) || u.protocol() ==
"about" ) {
00337
return;
00338 }
00339
00340 KonqHistoryEntry entry;
00341 entry.url = u;
00342 entry.firstVisited = QDateTime::currentDateTime();
00343 entry.lastVisited = entry.firstVisited;
00344
emitAddToHistory( entry );
00345 }
00346
00347 void KonqHistoryManager::emitAddToHistory(
const KonqHistoryEntry& entry )
00348 {
00349 QByteArray data;
00350 QDataStream stream( data, IO_WriteOnly );
00351 stream << entry << objId();
00352 kapp->dcopClient()->send(
"konqueror*",
"KonqHistoryManager",
00353
"notifyHistoryEntry(KonqHistoryEntry, QCString)",
00354 data );
00355 }
00356
00357
00358 void KonqHistoryManager::removePending(
const KURL& url )
00359 {
00360
00361
00362
if ( url.isLocalFile() )
00363
return;
00364
00365 QMapIterator<QString,KonqHistoryEntry*> it = m_pending.find( url.prettyURL() );
00366
if ( it != m_pending.end() ) {
00367 KonqHistoryEntry *oldEntry = it.data();
00368
emitRemoveFromHistory( url );
00369
00370
if ( oldEntry )
00371
emitAddToHistory( *oldEntry );
00372
00373
delete oldEntry;
00374 m_pending.remove( it );
00375 }
00376 }
00377
00378
00379
void KonqHistoryManager::clearPending()
00380 {
00381 QMapIterator<QString,KonqHistoryEntry*> it = m_pending.begin();
00382
while ( it != m_pending.end() ) {
00383
delete it.data();
00384 ++it;
00385 }
00386 m_pending.clear();
00387 }
00388
00389 void KonqHistoryManager::emitRemoveFromHistory(
const KURL& url )
00390 {
00391 QByteArray data;
00392 QDataStream stream( data, IO_WriteOnly );
00393 stream << url << objId();
00394 kapp->dcopClient()->send(
"konqueror*",
"KonqHistoryManager",
00395
"notifyRemove(KURL, QCString)", data );
00396 }
00397
00398 void KonqHistoryManager::emitRemoveFromHistory(
const KURL::List& urls )
00399 {
00400 QByteArray data;
00401 QDataStream stream( data, IO_WriteOnly );
00402 stream << urls << objId();
00403 kapp->dcopClient()->send(
"konqueror*",
"KonqHistoryManager",
00404
"notifyRemove(KURL::List, QCString)", data );
00405 }
00406
00407 void KonqHistoryManager::emitClear()
00408 {
00409 QByteArray data;
00410 QDataStream stream( data, IO_WriteOnly );
00411 stream << objId();
00412 kapp->dcopClient()->send(
"konqueror*",
"KonqHistoryManager",
00413
"notifyClear(QCString)", data );
00414 }
00415
00416 void KonqHistoryManager::emitSetMaxCount( Q_UINT32 count )
00417 {
00418 QByteArray data;
00419 QDataStream stream( data, IO_WriteOnly );
00420 stream << count << objId();
00421 kapp->dcopClient()->send(
"konqueror*",
"KonqHistoryManager",
00422
"notifyMaxCount(Q_UINT32, QCString)", data );
00423 }
00424
00425 void KonqHistoryManager::emitSetMaxAge( Q_UINT32 days )
00426 {
00427 QByteArray data;
00428 QDataStream stream( data, IO_WriteOnly );
00429 stream << days << objId();
00430 kapp->dcopClient()->send(
"konqueror*",
"KonqHistoryManager",
00431
"notifyMaxAge(Q_UINT32, QCString)", data );
00432 }
00433
00435
00436
00437 void KonqHistoryManager::notifyHistoryEntry( KonqHistoryEntry e,
00438 QCString )
00439 {
00440
00441
00442 KonqHistoryEntry *entry = findEntry( e.url );
00443 QString urlString = e.url.url();
00444
00445
if ( !entry ) {
00446 entry =
new KonqHistoryEntry;
00447 entry->url = e.url;
00448 entry->firstVisited = e.firstVisited;
00449 entry->numberOfTimesVisited = 0;
00450 m_history.append( entry );
00451 KParts::HistoryProvider::insert( urlString );
00452 }
00453
00454
if ( !e.typedURL.isEmpty() )
00455 entry->typedURL = e.typedURL;
00456
if ( !e.title.isEmpty() )
00457 entry->title = e.title;
00458 entry->numberOfTimesVisited += e.numberOfTimesVisited;
00459 entry->lastVisited = e.lastVisited;
00460
00461 addToCompletion( entry->url.prettyURL(), entry->typedURL );
00462
00463
00464
00465
adjustSize();
00466
00467
00468
00469
00470
bool updated = KonqBookmarkManager::self()->updateAccessMetadata( urlString );
00471
00472
if ( isSenderOfBroadcast() ) {
00473
00474
saveHistory();
00475
00476
if (updated)
00477 KonqBookmarkManager::self()->save();
00478 }
00479
00480 addToUpdateList( urlString );
00481 emit
entryAdded( entry );
00482 }
00483
00484 void KonqHistoryManager::notifyMaxCount( Q_UINT32 count, QCString )
00485 {
00486 m_maxCount = count;
00487 clearPending();
00488
adjustSize();
00489
00490 KConfig *config = KGlobal::config();
00491 KConfigGroupSaver cs( config,
"HistorySettings" );
00492 config->writeEntry(
"Maximum of History entries", m_maxCount );
00493
00494
if ( isSenderOfBroadcast() ) {
00495
saveHistory();
00496 config->sync();
00497 }
00498 }
00499
00500 void KonqHistoryManager::notifyMaxAge( Q_UINT32 days, QCString )
00501 {
00502 m_maxAgeDays = days;
00503 clearPending();
00504
adjustSize();
00505
00506 KConfig *config = KGlobal::config();
00507 KConfigGroupSaver cs( config,
"HistorySettings" );
00508 config->writeEntry(
"Maximum age of History entries", m_maxAgeDays );
00509
00510
if ( isSenderOfBroadcast() ) {
00511
saveHistory();
00512 config->sync();
00513 }
00514 }
00515
00516 void KonqHistoryManager::notifyClear( QCString )
00517 {
00518 clearPending();
00519 m_history.clear();
00520 m_pCompletion->clear();
00521
00522
if ( isSenderOfBroadcast() )
00523
saveHistory();
00524
00525 KParts::HistoryProvider::clear();
00526 }
00527
00528 void KonqHistoryManager::notifyRemove( KURL url, QCString )
00529 {
00530 kdDebug(1203) <<
"#### Broadcast: remove entry:: " << url.prettyURL() << endl;
00531
00532
00533 KonqHistoryEntry *entry = m_history.findEntry( url );
00534
00535
if ( entry ) {
00536 removeFromCompletion( entry->url.prettyURL(), entry->typedURL );
00537
00538 QString urlString = entry->url.url();
00539 KParts::HistoryProvider::remove( urlString );
00540
00541 addToUpdateList( urlString );
00542
00543 m_history.take();
00544 emit
entryRemoved( entry );
00545
delete entry;
00546
00547
if ( isSenderOfBroadcast() )
00548
saveHistory();
00549 }
00550 }
00551
00552 void KonqHistoryManager::notifyRemove( KURL::List urls, QCString )
00553 {
00554 kdDebug(1203) <<
"#### Broadcast: removing list!" << endl;
00555
00556
bool doSave =
false;
00557 KURL::List::Iterator it = urls.begin();
00558
while ( it != urls.end() ) {
00559 KonqHistoryEntry *entry = m_history.findEntry( *it );
00560
00561
if ( entry ) {
00562 removeFromCompletion( entry->url.prettyURL(), entry->typedURL );
00563
00564 QString urlString = entry->url.url();
00565 KParts::HistoryProvider::remove( urlString );
00566
00567 addToUpdateList( urlString );
00568
00569 m_history.take();
00570 emit
entryRemoved( entry );
00571
delete entry;
00572 doSave =
true;
00573 }
00574
00575 ++it;
00576 }
00577
00578
if (doSave && isSenderOfBroadcast())
00579
saveHistory();
00580 }
00581
00582
00583
00584
bool KonqHistoryManager::loadFallback()
00585 {
00586 QString file = locateLocal(
"config", QString::fromLatin1(
"konq_history"));
00587
if ( file.isEmpty() )
00588
return false;
00589
00590 KonqHistoryEntry *entry;
00591 KSimpleConfig config( file );
00592 config.setGroup(
"History");
00593 QStringList items = config.readListEntry(
"CompletionItems" );
00594 QStringList::Iterator it = items.begin();
00595
00596
while ( it != items.end() ) {
00597 entry = createFallbackEntry( *it );
00598
if ( entry ) {
00599 m_history.append( entry );
00600 addToCompletion( entry->url.prettyURL(), QString::null, entry->numberOfTimesVisited );
00601
00602 KParts::HistoryProvider::insert( entry->url.url() );
00603 }
00604 ++it;
00605 }
00606
00607 m_history.sort();
00608
adjustSize();
00609
saveHistory();
00610
00611
return true;
00612 }
00613
00614
00615
00616
00617 KonqHistoryEntry * KonqHistoryManager::createFallbackEntry(
const QString& item)
const
00618
{
00619
00620 uint len = item.length();
00621 uint weight = 1;
00622
00623
00624
int index = item.findRev(
':');
00625
if ( index > 0 ) {
00626
bool ok;
00627 weight = item.mid( index + 1 ).toUInt( &ok );
00628
if ( !ok )
00629 weight = 1;
00630
00631 len = index;
00632 }
00633
00634
00635 KonqHistoryEntry *entry = 0L;
00636 KURL u( item.left( len ));
00637
if ( u.isValid() ) {
00638 entry =
new KonqHistoryEntry;
00639
00640 entry->url = u;
00641 entry->numberOfTimesVisited = weight;
00642
00643 entry->lastVisited = QDateTime::currentDateTime();
00644 }
00645
00646
return entry;
00647 }
00648
00649 KonqHistoryEntry * KonqHistoryManager::findEntry(
const KURL& url )
00650 {
00651
00652
if ( !KParts::HistoryProvider::contains( url.url() ) )
00653
return 0L;
00654
00655
return m_history.findEntry( url );
00656 }
00657
00658 bool KonqHistoryManager::filterOut(
const KURL& url )
00659 {
00660
return ( url.isLocalFile() || url.host().isEmpty() );
00661 }
00662
00663
void KonqHistoryManager::slotEmitUpdated()
00664 {
00665 emit KParts::HistoryProvider::updated( m_updateURLs );
00666 m_updateURLs.clear();
00667 }
00668
00669 QStringList
KonqHistoryManager::allURLs()
const
00670
{
00671 QStringList list;
00672 KonqHistoryIterator it ( m_history );
00673
for ( ; it.current(); ++it )
00674 list.append( it.current()->url.url() );
00675
00676
return list;
00677 }
00678
00679
void KonqHistoryManager::addToCompletion(
const QString& url,
const QString& typedURL,
00680
int numberOfTimesVisited )
00681 {
00682 m_pCompletion->addItem( url, numberOfTimesVisited );
00683
00684 m_pCompletion->addItem( typedURL, numberOfTimesVisited +10 );
00685 }
00686
00687
void KonqHistoryManager::removeFromCompletion(
const QString& url,
const QString& typedURL )
00688 {
00689 m_pCompletion->removeItem( url );
00690 m_pCompletion->removeItem( typedURL );
00691 }
00692
00694
00695
00696 KonqHistoryEntry * KonqHistoryList::findEntry(
const KURL& url )
00697 {
00698
00699 KonqHistoryEntry *entry = last();
00700
while ( entry ) {
00701
if ( entry->url == url )
00702
return entry;
00703
00704 entry = prev();
00705 }
00706
00707
return 0L;
00708 }
00709
00710
00711
int KonqHistoryList::compareItems( QPtrCollection::Item item1,
00712 QPtrCollection::Item item2 )
00713 {
00714 KonqHistoryEntry *entry1 = static_cast<KonqHistoryEntry *>( item1 );
00715 KonqHistoryEntry *entry2 = static_cast<KonqHistoryEntry *>( item2 );
00716
00717
if ( entry1->lastVisited > entry2->lastVisited )
00718
return 1;
00719
else if ( entry1->lastVisited < entry2->lastVisited )
00720
return -1;
00721
else
00722
return 0;
00723 }
00724
00725
using namespace KParts;
00726
00727
#include "konq_historymgr.moc"