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 << internalDocument().toString();
00402 stream.flush();
00403 if ( file.finalize() )
00404 {
00405 return true;
00406 }
00407 }
00408
00409 static int hadSaveError = false;
00410 file.abort();
00411 if ( !hadSaveError ) {
00412 QString error = i18n("Unable to save bookmarks in %1. Reported error was: %2. "
00413 "This error message will only be shown once. The cause "
00414 "of the error needs to be fixed as quickly as possible, "
00415 "which is most likely a full hard drive.",
00416 filename, file.errorString());
00417 if (qApp->type() != QApplication::Tty)
00418 KMessageBox::error( QApplication::activeWindow(), error );
00419 else
00420 kError() << error << endl;
00421 }
00422 hadSaveError = true;
00423 return false;
00424 }
00425
00426 QString KBookmarkManager::path() const
00427 {
00428 return d->m_bookmarksFile;
00429 }
00430
00431 KBookmarkGroup KBookmarkManager::root() const
00432 {
00433 return KBookmarkGroup(internalDocument().documentElement());
00434 }
00435
00436 KBookmarkGroup KBookmarkManager::toolbar()
00437 {
00438 kDebug(7043) << "KBookmarkManager::toolbar begin";
00439
00440 if(!d->m_docIsLoaded)
00441 {
00442 kDebug(7043) << "KBookmarkManager::toolbar trying cache";
00443 const QString cacheFilename = d->m_bookmarksFile + QLatin1String(".tbcache");
00444 QFileInfo bmInfo(d->m_bookmarksFile);
00445 QFileInfo cacheInfo(cacheFilename);
00446 if (d->m_toolbarDoc.isNull() &&
00447 QFile::exists(cacheFilename) &&
00448 bmInfo.lastModified() < cacheInfo.lastModified())
00449 {
00450 kDebug(7043) << "KBookmarkManager::toolbar reading file";
00451 QFile file( cacheFilename );
00452
00453 if ( file.open( QIODevice::ReadOnly ) )
00454 {
00455 d->m_toolbarDoc = QDomDocument("cache");
00456 d->m_toolbarDoc.setContent( &file );
00457 kDebug(7043) << "KBookmarkManager::toolbar opened";
00458 }
00459 }
00460 if (!d->m_toolbarDoc.isNull())
00461 {
00462 kDebug(7043) << "KBookmarkManager::toolbar returning element";
00463 QDomElement elem = d->m_toolbarDoc.firstChild().toElement();
00464 return KBookmarkGroup(elem);
00465 }
00466 }
00467
00468
00469
00470 QDomElement elem = root().findToolbar();
00471 if (elem.isNull())
00472 return root();
00473 else
00474 return KBookmarkGroup(root().findToolbar());
00475 }
00476
00477 KBookmark KBookmarkManager::findByAddress( const QString & address )
00478 {
00479
00480 KBookmark result = root();
00481
00482 const QStringList addresses = address.split(QRegExp("[/+]"),QString::SkipEmptyParts);
00483
00484 for ( QStringList::const_iterator it = addresses.begin() ; it != addresses.end() ; )
00485 {
00486 bool append = ((*it) == "+");
00487 uint number = (*it).toUInt();
00488 Q_ASSERT(result.isGroup());
00489 KBookmarkGroup group = result.toGroup();
00490 KBookmark bk = group.first(), lbk = bk;
00491 for ( uint i = 0 ; ( (i<number) || append ) && !bk.isNull() ; ++i ) {
00492 lbk = bk;
00493 bk = group.next(bk);
00494
00495 }
00496 it++;
00497
00498 result = bk;
00499 }
00500 if (result.isNull()) {
00501 kWarning() << "KBookmarkManager::findByAddress: couldn't find item " << address;
00502 }
00503
00504 return result;
00505 }
00506
00507 void KBookmarkManager::emitChanged()
00508 {
00509 emitChanged(root());
00510 }
00511
00512
00513 void KBookmarkManager::emitChanged( const KBookmarkGroup & group )
00514 {
00515 (void) save();
00516
00517
00518
00519
00520 emit bookmarksChanged(group.address());
00521
00522
00523
00524 }
00525
00526 void KBookmarkManager::emitConfigChanged()
00527 {
00528 emit bookmarkConfigChanged();
00529 }
00530
00531 void KBookmarkManager::notifyCompleteChange( const QString &caller )
00532 {
00533 if (!d->m_update)
00534 return;
00535
00536 kDebug(7043) << "KBookmarkManager::notifyCompleteChange";
00537
00538
00539 parse();
00540
00541
00542 emit changed( "", caller );
00543 }
00544
00545 void KBookmarkManager::notifyConfigChanged()
00546 {
00547 kDebug() << "reloaded bookmark config!";
00548 KBookmarkSettings::self()->readSettings();
00549 parse();
00550 emit configChanged();
00551 }
00552
00553 void KBookmarkManager::notifyChanged( const QString &groupAddress, const QDBusMessage &msg )
00554 {
00555 kDebug() << "KBookmarkManager::notifyChanged ( "<<groupAddress<<")";
00556 if (!d->m_update)
00557 return;
00558
00559
00560
00561 if (msg.service() != QDBusConnection::sessionBus().baseService())
00562 parse();
00563
00564
00565
00566
00567 emit changed( groupAddress, QString() );
00568 }
00569
00570 void KBookmarkManager::setEditorOptions( const QString& caption, bool browser )
00571 {
00572 d->m_editorCaption = caption;
00573 d->m_browserEditor = browser;
00574 }
00575
00576 void KBookmarkManager::slotEditBookmarks()
00577 {
00578 QStringList args;
00579 if ( !d->m_editorCaption.isEmpty() )
00580 args << QLatin1String("--customcaption") << d->m_editorCaption;
00581 if ( !d->m_browserEditor )
00582 args << QLatin1String("--nobrowser");
00583 if( !d->m_dbusObjectName.isEmpty() )
00584 args << QLatin1String("--dbusObjectName") << d->m_dbusObjectName;
00585 args << d->m_bookmarksFile;
00586 QProcess::startDetached("keditbookmarks", args);
00587 }
00588
00589 void KBookmarkManager::slotEditBookmarksAtAddress( const QString& address )
00590 {
00591 QStringList args;
00592 if ( !d->m_editorCaption.isEmpty() )
00593 args << QLatin1String("--customcaption") << d->m_editorCaption;
00594 if ( !d->m_browserEditor )
00595 args << QLatin1String("--nobrowser");
00596 if( !d->m_dbusObjectName.isEmpty() )
00597 args << QLatin1String("--dbusObjectName") << d->m_dbusObjectName;
00598 args << QLatin1String("--address") << address
00599 << d->m_bookmarksFile;
00600 QProcess::startDetached("keditbookmarks", args);
00601 }
00602
00604 bool KBookmarkManager::updateAccessMetadata( const QString & url )
00605 {
00606 if (!s_bk->map) {
00607 s_bk->map = new KBookmarkMap(this);
00608 s_bk->map->update();
00609 }
00610
00611 QList<KBookmark> list = s_bk->map->find(url);
00612 if ( list.count() == 0 )
00613 return false;
00614
00615 for ( QList<KBookmark>::iterator it = list.begin();
00616 it != list.end(); ++it )
00617 (*it).updateAccessMetadata();
00618
00619 return true;
00620 }
00621
00622 void KBookmarkManager::updateFavicon( const QString &url, const QString &faviconurl )
00623 {
00624 Q_UNUSED(faviconurl);
00625
00626 if (!s_bk->map) {
00627 s_bk->map = new KBookmarkMap(this);
00628 s_bk->map->update();
00629 }
00630
00631 QList<KBookmark> list = s_bk->map->find(url);
00632 for ( QList<KBookmark>::iterator it = list.begin();
00633 it != list.end(); ++it )
00634 {
00635
00636
00637
00638 }
00639 }
00640
00641 KBookmarkManager* KBookmarkManager::userBookmarksManager()
00642 {
00643 QString bookmarksFile = KStandardDirs::locateLocal("data", QLatin1String("konqueror/bookmarks.xml"));
00644 return KBookmarkManager::managerForFile( bookmarksFile, "konqueror" );
00645 }
00646
00647 KBookmarkSettings* KBookmarkSettings::s_self = 0;
00648
00649 void KBookmarkSettings::readSettings()
00650 {
00651 KConfig config("kbookmarkrc", KConfig::NoGlobals);
00652 KConfigGroup cg(&config, "Bookmarks");
00653
00654
00655 s_self->m_advancedaddbookmark = cg.readEntry("AdvancedAddBookmarkDialog", false);
00656
00657
00658 s_self->m_contextmenu = cg.readEntry("ContextMenuActions", true);
00659 }
00660
00661 KBookmarkSettings *KBookmarkSettings::self()
00662 {
00663 if (!s_self)
00664 {
00665 s_self = new KBookmarkSettings;
00666 readSettings();
00667 }
00668 return s_self;
00669 }
00670
00672
00673 bool KBookmarkOwner::enableOption(BookmarkOption action) const
00674 {
00675 if(action == ShowAddBookmark)
00676 return true;
00677 if(action == ShowEditBookmark)
00678 return true;
00679 return false;
00680 }
00681
00682 KBookmarkDialog * KBookmarkOwner::bookmarkDialog(KBookmarkManager * mgr, QWidget * parent)
00683 {
00684 return new KBookmarkDialog(mgr, parent);
00685 }
00686
00687 void KBookmarkOwner::openFolderinTabs(const KBookmarkGroup &)
00688 {
00689
00690 }
00691
00692 #include "kbookmarkmanager.moc"