00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023 #include "kbookmarkmanager.h"
00024 #include "kbookmarkmenu.h"
00025 #include "kbookmarkmenu_p.h"
00026 #include "kbookmarkimporter.h"
00027 #include "kbookmarkdialog.h"
00028 #include <kdebug.h>
00029 #include <kstandarddirs.h>
00030 #include <ksavefile.h>
00031 #include <qregexp.h>
00032 #include <kmessagebox.h>
00033 #include <qprocess.h>
00034 #include <klocale.h>
00035 #include <kdirwatch.h>
00036 #include <QtGui/QApplication>
00037 #include <kconfiggroup.h>
00038 #include <qfile.h>
00039 #include <qfileinfo.h>
00040 #include <QtDBus/QtDBus>
00041 #include <QtCore/QTextStream>
00042 #include "kbookmarkmanageradaptor_p.h"
00043
00044 #define BOOKMARK_CHANGE_NOTIFY_INTERFACE "org.kde.KIO.KBookmarkManager"
00045
00046 class KBookmarkManagerList : public QList<KBookmarkManager *>
00047 {
00048 public:
00049 ~KBookmarkManagerList() {
00050 qDeleteAll( begin() , end() );
00051 }
00052 };
00053
00054 K_GLOBAL_STATIC(KBookmarkManagerList, s_pSelf)
00055
00056 class KBookmarkMap : private KBookmarkGroupTraverser {
00057 public:
00058 KBookmarkMap( KBookmarkManager * );
00059 void update();
00060 QList<KBookmark> find( const QString &url ) const
00061 { return m_bk_map[url]; }
00062 private:
00063 virtual void visit(const KBookmark &);
00064 virtual void visitEnter(const KBookmarkGroup &) { ; }
00065 virtual void visitLeave(const KBookmarkGroup &) { ; }
00066 private:
00067 typedef QList<KBookmark> KBookmarkList;
00068 QMap<QString, KBookmarkList> m_bk_map;
00069 KBookmarkManager *m_manager;
00070 };
00071
00072 class KBookmarkMapStatic
00073 {
00074 public:
00075 KBookmarkMapStatic()
00076 : map( 0 )
00077 {
00078 }
00079 ~KBookmarkMapStatic()
00080 {
00081 delete map;
00082 }
00083
00084 KBookmarkMap *map;
00085 };
00086
00087 K_GLOBAL_STATIC(KBookmarkMapStatic, s_bk)
00088
00089 KBookmarkMap::KBookmarkMap( KBookmarkManager *manager ) {
00090 m_manager = manager;
00091 }
00092
00093 void KBookmarkMap::update()
00094 {
00095 m_bk_map.clear();
00096 KBookmarkGroup root = m_manager->root();
00097 traverse(root);
00098 }
00099
00100 void KBookmarkMap::visit(const KBookmark &bk)
00101 {
00102 if (!bk.isSeparator()) {
00103
00104 m_bk_map[bk.internalElement().attribute("href")].append(bk);
00105 }
00106 }
00107
00108
00109
00110 class KBookmarkManager::Private
00111 {
00112 public:
00113 Private(bool bDocIsloaded, const QString &dbusObjectName = QString())
00114 : m_doc("xbel")
00115 , m_dbusObjectName(dbusObjectName)
00116 , m_docIsLoaded(bDocIsloaded)
00117 , m_update(false)
00118 , m_typeExternal(false)
00119 , m_kDirWatch(0)
00120
00121 {}
00122
00123 ~Private() {
00124 delete m_kDirWatch;
00125 }
00126
00127 mutable QDomDocument m_doc;
00128 mutable QDomDocument m_toolbarDoc;
00129 QString m_bookmarksFile;
00130 QString m_dbusObjectName;
00131 mutable bool m_docIsLoaded;
00132 bool m_update;
00133
00134 bool m_browserEditor;
00135 QString m_editorCaption;
00136
00137 bool m_typeExternal;
00138 KDirWatch * m_kDirWatch;
00139
00140 };
00141
00142
00143
00144
00145 static KBookmarkManager* lookupExisting(const QString& bookmarksFile)
00146 {
00147 for ( KBookmarkManagerList::ConstIterator bmit = s_pSelf->constBegin(), bmend = s_pSelf->constEnd();
00148 bmit != bmend; ++bmit ) {
00149 if ( (*bmit)->path() == bookmarksFile )
00150 return *bmit;
00151 }
00152 return 0;
00153 }
00154
00155
00156 KBookmarkManager* KBookmarkManager::managerForFile( const QString& bookmarksFile, const QString& dbusObjectName )
00157 {
00158 KBookmarkManager* mgr = lookupExisting(bookmarksFile);
00159 if (mgr) return mgr;
00160
00161 mgr = new KBookmarkManager( bookmarksFile, dbusObjectName );
00162 s_pSelf->append( mgr );
00163 return mgr;
00164 }
00165
00166 KBookmarkManager* KBookmarkManager::managerForExternalFile( const QString& bookmarksFile )
00167 {
00168 KBookmarkManager* mgr = lookupExisting(bookmarksFile);
00169 if (mgr) return mgr;
00170
00171 mgr = new KBookmarkManager( bookmarksFile );
00172 s_pSelf->append( mgr );
00173 return mgr;
00174 }
00175
00176
00177
00178 KBookmarkManager* KBookmarkManager::createTempManager()
00179 {
00180 KBookmarkManager* mgr = new KBookmarkManager();
00181 s_pSelf->append( mgr );
00182 return mgr;
00183 }
00184
00185 #define PI_DATA "version=\"1.0\" encoding=\"UTF-8\""
00186
00187 static QDomElement createXbelTopLevelElement(QDomDocument & doc)
00188 {
00189 QDomElement topLevel = doc.createElement("xbel");
00190 topLevel.setAttribute("xmlns:mime", "http://www.freedesktop.org/standards/shared-mime-info");
00191 topLevel.setAttribute("xmlns:bookmark", "http://www.freedesktop.org/standards/desktop-bookmarks");
00192 topLevel.setAttribute("xmlns:kdepriv", "http://www.kde.org/kdepriv");
00193 doc.appendChild( topLevel );
00194 doc.insertBefore( doc.createProcessingInstruction( "xml", PI_DATA), topLevel );
00195 return topLevel;
00196 }
00197
00198 KBookmarkManager::KBookmarkManager( const QString & bookmarksFile, const QString & dbusObjectName)
00199 : d(new Private(false, dbusObjectName))
00200 {
00201 if(dbusObjectName.isNull())
00202 if ( QFile::exists(d->m_bookmarksFile) )
00203 parse();
00204
00205 init( "/KBookmarkManager/"+d->m_dbusObjectName );
00206
00207 d->m_update = true;
00208
00209 Q_ASSERT( !bookmarksFile.isEmpty() );
00210 d->m_bookmarksFile = bookmarksFile;
00211
00212 if ( !QFile::exists(d->m_bookmarksFile) )
00213 {
00214 QDomElement topLevel = createXbelTopLevelElement(d->m_doc);
00215 topLevel.setAttribute("dbusName", dbusObjectName);
00216 d->m_docIsLoaded = true;
00217 }
00218 }
00219
00220 KBookmarkManager::KBookmarkManager(const QString & bookmarksFile)
00221 : d(new Private(false))
00222 {
00223
00224 d->m_typeExternal = true;
00225 d->m_update = true;
00226
00227 Q_ASSERT( !bookmarksFile.isEmpty() );
00228 d->m_bookmarksFile = bookmarksFile;
00229
00230 if ( !QFile::exists(d->m_bookmarksFile) )
00231 {
00232 createXbelTopLevelElement(d->m_doc);
00233 }
00234 else
00235 {
00236 parse();
00237 }
00238 d->m_docIsLoaded = true;
00239
00240
00241 d->m_kDirWatch = new KDirWatch;
00242 d->m_kDirWatch->addFile(d->m_bookmarksFile);
00243 QObject::connect( d->m_kDirWatch, SIGNAL(dirty(const QString&)),
00244 this, SLOT(slotFileChanged(const QString&)));
00245 QObject::connect( d->m_kDirWatch, SIGNAL(created(const QString&)),
00246 this, SLOT(slotFileChanged(const QString&)));
00247 QObject::connect( d->m_kDirWatch, SIGNAL(deleted(const QString&)),
00248 this, SLOT(slotFileChanged(const QString&)));
00249 kDebug(7043) << "starting KDirWatch for " << d->m_bookmarksFile;
00250 }
00251
00252 KBookmarkManager::KBookmarkManager( )
00253 : d(new Private(true))
00254 {
00255 init( "/KBookmarkManager/generated" );
00256 d->m_update = false;
00257
00258 createXbelTopLevelElement(d->m_doc);
00259 }
00260
00261 void KBookmarkManager::init( const QString& dbusPath )
00262 {
00263
00264
00265 if ( dbusPath != "/KBookmarkManager/" && dbusPath != "/KBookmarkManager/generated")
00266 {
00267 new KBookmarkManagerAdaptor(this);
00268 QDBusConnection::sessionBus().registerObject( dbusPath, this );
00269
00270 QDBusConnection::sessionBus().connect(QString(), dbusPath, BOOKMARK_CHANGE_NOTIFY_INTERFACE,
00271 "bookmarksChanged", this, SLOT(notifyChanged(QString,QDBusMessage)));
00272 QDBusConnection::sessionBus().connect(QString(), dbusPath, BOOKMARK_CHANGE_NOTIFY_INTERFACE,
00273 "bookmarkConfigChanged", this, SLOT(notifyConfigChanged()));
00274 }
00275 }
00276
00277 void KBookmarkManager::slotFileChanged(const QString& path)
00278 {
00279 if (path == d->m_bookmarksFile)
00280 {
00281 kDebug(7043) << "file changed (KDirWatch) " << path ;
00282
00283 parse();
00284
00285
00286 emit changed( "", QString() );
00287 }
00288 }
00289
00290 KBookmarkManager::~KBookmarkManager()
00291 {
00292 if(!s_pSelf.isDestroyed())
00293 s_pSelf->removeAll( this );
00294 delete d;
00295 }
00296
00297 void KBookmarkManager::setUpdate( bool update )
00298 {
00299 d->m_update = update;
00300 }
00301
00302 QDomDocument KBookmarkManager::internalDocument() const
00303 {
00304 if(!d->m_docIsLoaded)
00305 {
00306 parse();
00307 d->m_toolbarDoc.clear();
00308 }
00309 return d->m_doc;
00310 }
00311
00312
00313 void KBookmarkManager::parse() const
00314 {
00315 d->m_docIsLoaded = true;
00316
00317 QFile file( d->m_bookmarksFile );
00318 if ( !file.open( QIODevice::ReadOnly ) )
00319 {
00320 kWarning() << "Can't open " << d->m_bookmarksFile;
00321 return;
00322 }
00323 d->m_doc = QDomDocument("xbel");
00324 d->m_doc.setContent( &file );
00325
00326 if ( d->m_doc.documentElement().isNull() )
00327 {
00328 kWarning() << "KBookmarkManager::parse : main tag is missing, creating default " << d->m_bookmarksFile;
00329 QDomElement element = d->m_doc.createElement("xbel");
00330 d->m_doc.appendChild(element);
00331 }
00332
00333 QDomElement docElem = d->m_doc.documentElement();
00334
00335 QString mainTag = docElem.tagName();
00336 if ( mainTag != "xbel" )
00337 kWarning() << "KBookmarkManager::parse : unknown main tag " << mainTag;
00338
00339 if(d->m_dbusObjectName.isNull())
00340 {
00341 d->m_dbusObjectName = docElem.attribute("dbusName");
00342 }
00343 else if(docElem.attribute("dbusName") != d->m_dbusObjectName)
00344 {
00345 docElem.setAttribute("dbusName", d->m_dbusObjectName);
00346 save();
00347 }
00348
00349 QDomNode n = d->m_doc.documentElement().previousSibling();
00350 if ( n.isProcessingInstruction() )
00351 {
00352 QDomProcessingInstruction pi = n.toProcessingInstruction();
00353 pi.parentNode().removeChild(pi);
00354 }
00355
00356 QDomProcessingInstruction pi;
00357 pi = d->m_doc.createProcessingInstruction( "xml", PI_DATA );
00358 d->m_doc.insertBefore( pi, docElem );
00359
00360 file.close();
00361 if ( !s_bk->map )
00362 s_bk->map = new KBookmarkMap( const_cast<KBookmarkManager*>( this ) );
00363 s_bk->map->update();
00364 }
00365
00366 bool KBookmarkManager::save( bool toolbarCache ) const
00367 {
00368 return saveAs( d->m_bookmarksFile, toolbarCache );
00369 }
00370
00371 bool KBookmarkManager::saveAs( const QString & filename, bool toolbarCache ) const
00372 {
00373 kDebug(7043) << "KBookmarkManager::save " << filename;
00374
00375
00376
00377 const QString cacheFilename = filename + QLatin1String(".tbcache");
00378 if(toolbarCache && !root().isToolbarGroup())
00379 {
00380 KSaveFile cacheFile( cacheFilename );
00381 if ( cacheFile.open() )
00382 {
00383 QString str;
00384 QTextStream stream(&str, QIODevice::WriteOnly);
00385 stream << root().findToolbar();
00386 const QByteArray cstr = str.toUtf8();
00387 cacheFile.write( cstr.data(), cstr.length() );
00388 cacheFile.finalize();
00389 }
00390 }
00391 else
00392 {
00393 QFile::remove( cacheFilename );
00394 }
00395
00396 KSaveFile file( filename );
00397 if ( file.open() )
00398 {
00399 file.simpleBackupFile( file.fileName(), QString(), ".bak" );
00400 QTextStream stream(&file);
00401 stream.setCodec( QTextCodec::codecForName( "UTF-8" ) );
00402 stream << internalDocument().toString();
00403 stream.flush();
00404 if ( file.finalize() )
00405 {
00406 return true;
00407 }
00408 }
00409
00410 static int hadSaveError = false;
00411 file.abort();
00412 if ( !hadSaveError ) {
00413 QString error = i18n("Unable to save bookmarks in %1. Reported error was: %2. "
00414 "This error message will only be shown once. The cause "
00415 "of the error needs to be fixed as quickly as possible, "
00416 "which is most likely a full hard drive.",
00417 filename, file.errorString());
00418 if (qApp->type() != QApplication::Tty)
00419 KMessageBox::error( QApplication::activeWindow(), error );
00420 else
00421 kError() << error << endl;
00422 }
00423 hadSaveError = true;
00424 return false;
00425 }
00426
00427 QString KBookmarkManager::path() const
00428 {
00429 return d->m_bookmarksFile;
00430 }
00431
00432 KBookmarkGroup KBookmarkManager::root() const
00433 {
00434 return KBookmarkGroup(internalDocument().documentElement());
00435 }
00436
00437 KBookmarkGroup KBookmarkManager::toolbar()
00438 {
00439 kDebug(7043) << "KBookmarkManager::toolbar begin";
00440
00441 if(!d->m_docIsLoaded)
00442 {
00443 kDebug(7043) << "KBookmarkManager::toolbar trying cache";
00444 const QString cacheFilename = d->m_bookmarksFile + QLatin1String(".tbcache");
00445 QFileInfo bmInfo(d->m_bookmarksFile);
00446 QFileInfo cacheInfo(cacheFilename);
00447 if (d->m_toolbarDoc.isNull() &&
00448 QFile::exists(cacheFilename) &&
00449 bmInfo.lastModified() < cacheInfo.lastModified())
00450 {
00451 kDebug(7043) << "KBookmarkManager::toolbar reading file";
00452 QFile file( cacheFilename );
00453
00454 if ( file.open( QIODevice::ReadOnly ) )
00455 {
00456 d->m_toolbarDoc = QDomDocument("cache");
00457 d->m_toolbarDoc.setContent( &file );
00458 kDebug(7043) << "KBookmarkManager::toolbar opened";
00459 }
00460 }
00461 if (!d->m_toolbarDoc.isNull())
00462 {
00463 kDebug(7043) << "KBookmarkManager::toolbar returning element";
00464 QDomElement elem = d->m_toolbarDoc.firstChild().toElement();
00465 return KBookmarkGroup(elem);
00466 }
00467 }
00468
00469
00470
00471 QDomElement elem = root().findToolbar();
00472 if (elem.isNull())
00473 return root();
00474 else
00475 return KBookmarkGroup(root().findToolbar());
00476 }
00477
00478 KBookmark KBookmarkManager::findByAddress( const QString & address )
00479 {
00480
00481 KBookmark result = root();
00482
00483 const QStringList addresses = address.split(QRegExp("[/+]"),QString::SkipEmptyParts);
00484
00485 for ( QStringList::const_iterator it = addresses.begin() ; it != addresses.end() ; )
00486 {
00487 bool append = ((*it) == "+");
00488 uint number = (*it).toUInt();
00489 Q_ASSERT(result.isGroup());
00490 KBookmarkGroup group = result.toGroup();
00491 KBookmark bk = group.first(), lbk = bk;
00492 for ( uint i = 0 ; ( (i<number) || append ) && !bk.isNull() ; ++i ) {
00493 lbk = bk;
00494 bk = group.next(bk);
00495
00496 }
00497 it++;
00498
00499 result = bk;
00500 }
00501 if (result.isNull()) {
00502 kWarning() << "KBookmarkManager::findByAddress: couldn't find item " << address;
00503 }
00504
00505 return result;
00506 }
00507
00508 void KBookmarkManager::emitChanged()
00509 {
00510 emitChanged(root());
00511 }
00512
00513
00514 void KBookmarkManager::emitChanged( const KBookmarkGroup & group )
00515 {
00516 (void) save();
00517
00518
00519
00520
00521 emit bookmarksChanged(group.address());
00522
00523
00524
00525 }
00526
00527 void KBookmarkManager::emitConfigChanged()
00528 {
00529 emit bookmarkConfigChanged();
00530 }
00531
00532 void KBookmarkManager::notifyCompleteChange( const QString &caller )
00533 {
00534 if (!d->m_update)
00535 return;
00536
00537 kDebug(7043) << "KBookmarkManager::notifyCompleteChange";
00538
00539
00540 parse();
00541
00542
00543 emit changed( "", caller );
00544 }
00545
00546 void KBookmarkManager::notifyConfigChanged()
00547 {
00548 kDebug() << "reloaded bookmark config!";
00549 KBookmarkSettings::self()->readSettings();
00550 parse();
00551 emit configChanged();
00552 }
00553
00554 void KBookmarkManager::notifyChanged( const QString &groupAddress, const QDBusMessage &msg )
00555 {
00556 kDebug() << "KBookmarkManager::notifyChanged ( "<<groupAddress<<")";
00557 if (!d->m_update)
00558 return;
00559
00560
00561
00562 if (msg.service() != QDBusConnection::sessionBus().baseService())
00563 parse();
00564
00565
00566
00567
00568 emit changed( groupAddress, QString() );
00569 }
00570
00571 void KBookmarkManager::setEditorOptions( const QString& caption, bool browser )
00572 {
00573 d->m_editorCaption = caption;
00574 d->m_browserEditor = browser;
00575 }
00576
00577 void KBookmarkManager::slotEditBookmarks()
00578 {
00579 QStringList args;
00580 if ( !d->m_editorCaption.isEmpty() )
00581 args << QLatin1String("--customcaption") << d->m_editorCaption;
00582 if ( !d->m_browserEditor )
00583 args << QLatin1String("--nobrowser");
00584 if( !d->m_dbusObjectName.isEmpty() )
00585 args << QLatin1String("--dbusObjectName") << d->m_dbusObjectName;
00586 args << d->m_bookmarksFile;
00587 QProcess::startDetached("keditbookmarks", args);
00588 }
00589
00590 void KBookmarkManager::slotEditBookmarksAtAddress( const QString& address )
00591 {
00592 QStringList args;
00593 if ( !d->m_editorCaption.isEmpty() )
00594 args << QLatin1String("--customcaption") << d->m_editorCaption;
00595 if ( !d->m_browserEditor )
00596 args << QLatin1String("--nobrowser");
00597 if( !d->m_dbusObjectName.isEmpty() )
00598 args << QLatin1String("--dbusObjectName") << d->m_dbusObjectName;
00599 args << QLatin1String("--address") << address
00600 << d->m_bookmarksFile;
00601 QProcess::startDetached("keditbookmarks", args);
00602 }
00603
00605 bool KBookmarkManager::updateAccessMetadata( const QString & url )
00606 {
00607 if (!s_bk->map) {
00608 s_bk->map = new KBookmarkMap(this);
00609 s_bk->map->update();
00610 }
00611
00612 QList<KBookmark> list = s_bk->map->find(url);
00613 if ( list.count() == 0 )
00614 return false;
00615
00616 for ( QList<KBookmark>::iterator it = list.begin();
00617 it != list.end(); ++it )
00618 (*it).updateAccessMetadata();
00619
00620 return true;
00621 }
00622
00623 void KBookmarkManager::updateFavicon( const QString &url, const QString &faviconurl )
00624 {
00625 Q_UNUSED(faviconurl);
00626
00627 if (!s_bk->map) {
00628 s_bk->map = new KBookmarkMap(this);
00629 s_bk->map->update();
00630 }
00631
00632 QList<KBookmark> list = s_bk->map->find(url);
00633 for ( QList<KBookmark>::iterator it = list.begin();
00634 it != list.end(); ++it )
00635 {
00636
00637
00638
00639 }
00640 }
00641
00642 KBookmarkManager* KBookmarkManager::userBookmarksManager()
00643 {
00644 const QString bookmarksFile = KStandardDirs::locateLocal("data", QString::fromLatin1("konqueror/bookmarks.xml"));
00645 KBookmarkManager* bookmarkManager = KBookmarkManager::managerForFile( bookmarksFile, "konqueror" );
00646 bookmarkManager->setEditorOptions(KGlobal::caption(), true);
00647 return bookmarkManager;
00648 }
00649
00650 KBookmarkSettings* KBookmarkSettings::s_self = 0;
00651
00652 void KBookmarkSettings::readSettings()
00653 {
00654 KConfig config("kbookmarkrc", KConfig::NoGlobals);
00655 KConfigGroup cg(&config, "Bookmarks");
00656
00657
00658 s_self->m_advancedaddbookmark = cg.readEntry("AdvancedAddBookmarkDialog", false);
00659
00660
00661 s_self->m_contextmenu = cg.readEntry("ContextMenuActions", true);
00662 }
00663
00664 KBookmarkSettings *KBookmarkSettings::self()
00665 {
00666 if (!s_self)
00667 {
00668 s_self = new KBookmarkSettings;
00669 readSettings();
00670 }
00671 return s_self;
00672 }
00673
00675
00676 bool KBookmarkOwner::enableOption(BookmarkOption action) const
00677 {
00678 if(action == ShowAddBookmark)
00679 return true;
00680 if(action == ShowEditBookmark)
00681 return true;
00682 return false;
00683 }
00684
00685 KBookmarkDialog * KBookmarkOwner::bookmarkDialog(KBookmarkManager * mgr, QWidget * parent)
00686 {
00687 return new KBookmarkDialog(mgr, parent);
00688 }
00689
00690 void KBookmarkOwner::openFolderinTabs(const KBookmarkGroup &)
00691 {
00692
00693 }
00694
00695 #include "kbookmarkmanager.moc"