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