1/* This file is part of the KDE project
2 Copyright (C) 2008 Norbert Frese <nf2@scheinwelt.at>
3
4 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Library General Public
6 License version 2 as published by the Free Software Foundation.
7
8 This library is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 Library General Public License for more details.
12
13 You should have received a copy of the GNU Library General Public License
14 along with this library; see the file COPYING.LIB. If not, write to
15 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
16 Boston, MA 02110-1301, USA.
17
18*/
19
20#include "kfileplacessharedbookmarks_p.h"
21
22#include <QtCore/QObject>
23#include <QtCore/QTextStream>
24#include <QtCore/QFile>
25#include <kstandarddirs.h>
26#include <kbookmarkmanager.h>
27#include <kbookmark.h>
28#include <kdebug.h>
29
30//////////////// utility functions
31
32static bool compareBookmarks(const KBookmark & bookmark1, const KBookmark & bookmark2)
33{
34 return (bookmark1.url() == bookmark2.url() || bookmark1.text() == bookmark2.text());
35}
36
37static bool deepCompareDomNodes(const QDomNode & node1, const QDomNode & node2)
38{
39
40 // compare name and value
41 if (node1.nodeName() != node2.nodeName() || node1.nodeValue() != node2.nodeValue())
42 return false;
43
44 // recursively compare children
45 const QDomNodeList node1Children = node1.childNodes();
46 const QDomNodeList node2Children = node2.childNodes();
47
48 if (node1Children.count () != node2Children.count ())
49 return false;
50
51 for (int i=0; i<node1Children.count ();i++) {
52 if (!deepCompareDomNodes(node1Children.at(i), node2Children.at(i) ))
53 return false;
54 }
55 return true;
56}
57
58/*
59static QString nodeAsString(const QDomNode & node1)
60{
61 QString str;
62 QTextStream ts( &str, QIODevice::WriteOnly );
63 ts << node1;
64 return str;
65}
66*/
67
68static bool exactCompareBookmarks(const KBookmark & bookmark1, const KBookmark & bookmark2)
69{
70 //kDebug() << "excat comparing:\n" << nodeAsString(bookmark1.internalElement()) << "\nwith:\n" << nodeAsString(bookmark2.internalElement());
71 return deepCompareDomNodes(bookmark1.internalElement(), bookmark2.internalElement());
72}
73
74static void cloneBookmarkContents(const KBookmark & target, const KBookmark & source)
75{
76 const QDomElement targetEl = target.internalElement();
77 QDomNode parent = targetEl.parentNode ();
78 QDomNode clonedNode = source.internalElement().cloneNode(true);
79 parent.replaceChild (clonedNode , targetEl );
80}
81
82static KBookmark cloneBookmark(const KBookmark & toClone)
83{
84 const QDomNode cloned = toClone.internalElement().cloneNode(true);
85 return KBookmark(cloned.toElement ());
86}
87
88
89static void emptyBookmarkGroup(KBookmarkGroup & root)
90{
91 KBookmark bookmark = root.first();
92 while (!bookmark.isNull()) {
93 KBookmark bookmarkToRemove = bookmark;
94 bookmark = root.next(bookmark);
95 root.deleteBookmark(bookmarkToRemove);
96 }
97}
98
99static int bookmarkGroupSize(KBookmarkGroup & root)
100{
101 int count=0;
102 KBookmark bookmark = root.first();
103 while (!bookmark.isNull()) {
104 count++;
105 bookmark = root.next(bookmark);
106 }
107 return count;
108}
109
110//////////////// class KFilePlacesSharedBookmarks
111
112KFilePlacesSharedBookmarks::KFilePlacesSharedBookmarks(KBookmarkManager * mgr)
113{
114 m_placesBookmarkManager = mgr;
115
116 // we check later if the directory exists
117 KStandardDirs::makeDir(KStandardDirs().localxdgdatadir());
118 const QString file = KStandardDirs().localxdgdatadir() + "user-places.xbel";
119 m_sharedBookmarkManager = KBookmarkManager::managerForExternalFile(file);
120
121 connect(m_sharedBookmarkManager, SIGNAL(changed(QString,QString)),
122 this, SLOT(slotSharedBookmarksChanged()));
123 connect(m_sharedBookmarkManager, SIGNAL(bookmarksChanged(QString)),
124 this, SLOT(slotSharedBookmarksChanged()));
125
126 connect(m_placesBookmarkManager, SIGNAL(changed(QString,QString)),
127 this, SLOT(slotBookmarksChanged()));
128 connect(m_placesBookmarkManager, SIGNAL(bookmarksChanged(QString)),
129 this, SLOT(slotBookmarksChanged()));
130
131 integrateSharedBookmarks();
132}
133
134bool KFilePlacesSharedBookmarks::integrateSharedBookmarks()
135{
136 KBookmarkGroup root = m_placesBookmarkManager->root();
137 KBookmark bookmark = root.first();
138
139 KBookmarkGroup sharedRoot = m_sharedBookmarkManager->root();
140 KBookmark sharedBookmark = sharedRoot.first();
141
142 bool dirty = false;
143
144 while (!bookmark.isNull()) {
145 //kDebug() << "importing" << bookmark.text();
146
147 // skip over system items
148 if (bookmark.metaDataItem("isSystemItem") == "true") {
149 bookmark = root.next(bookmark);
150 continue;
151 }
152
153 // do the bookmarks match?
154 if (!sharedBookmark.isNull() && compareBookmarks(bookmark, sharedBookmark)) {
155 //kDebug() << "excat comparing: targetbk:\n" << nodeAsString(bookmark.internalElement()) << "\nsourcbk:\n" << nodeAsString(sharedBookmark.internalElement());
156
157 if (!exactCompareBookmarks(bookmark, sharedBookmark)) {
158 KBookmark cloneTarget=bookmark;
159 KBookmark cloneSource = sharedBookmark;
160
161 sharedBookmark = sharedRoot.next(sharedBookmark);
162 bookmark = root.next(bookmark);
163
164 //kDebug() << "cloning" << cloneSource.text();
165 //kDebug() << "cloning: target=\n" << nodeAsString(cloneTarget.internalElement()) << "\n source:\n" << nodeAsString(cloneSource.internalElement());
166
167 cloneBookmarkContents(cloneTarget, cloneSource);
168 dirty = true;
169 continue;
170 } else {
171 //kDebug() << "keeping" << bookmark.text();
172 }
173 sharedBookmark = sharedRoot.next(sharedBookmark);
174 bookmark = root.next(bookmark);
175 continue;
176 }
177
178 // they don't match -> remove
179 //kDebug() << "removing" << bookmark.text();
180 KBookmark bookmarkToRemove = bookmark;
181 bookmark = root.next(bookmark);
182 root.deleteBookmark(bookmarkToRemove);
183
184 dirty = true;
185 }
186
187 // append the remaining shared bookmarks
188 while(!sharedBookmark.isNull()) {
189 root.addBookmark(cloneBookmark(sharedBookmark));
190 sharedBookmark = sharedRoot.next(sharedBookmark);
191 dirty = true;
192 }
193
194 return dirty;
195}
196
197bool KFilePlacesSharedBookmarks::exportSharedBookmarks()
198{
199 KBookmarkGroup root = m_placesBookmarkManager->root();
200 KBookmark bookmark = root.first();
201
202 KBookmarkGroup sharedRoot = m_sharedBookmarkManager->root();
203 KBookmark sharedBookmark = sharedRoot.first();
204
205 bool dirty = false;
206
207 // first check if they are the same
208 int count=0;
209 while (!bookmark.isNull()) {
210 //kDebug() << "exporting..." << bookmark.text();
211
212 // skip over system items
213 if (bookmark.metaDataItem("isSystemItem") == "true") {
214 bookmark = root.next(bookmark);
215 continue;
216 }
217 count++;
218
219 // end of sharedBookmarks?
220 if (sharedBookmark.isNull()) {
221 dirty=true;
222 break;
223 }
224
225 // do the bookmarks match?
226 if (compareBookmarks(bookmark, sharedBookmark)) {
227 if (!exactCompareBookmarks(bookmark, sharedBookmark)) {
228 dirty = true;
229 break;
230 }
231 } else {
232 dirty=true;
233 break;
234 }
235 sharedBookmark = sharedRoot.next(sharedBookmark);
236 bookmark = root.next(bookmark);
237 }
238
239 //kDebug() << "dirty=" << dirty << " oldsize=" << bookmarkGroupSize(sharedRoot) << " count=" << count;
240
241 if (bookmarkGroupSize(sharedRoot) != count)
242 dirty=true;
243
244 if (dirty) {
245 emptyBookmarkGroup(sharedRoot);
246
247 // append all bookmarks
248 KBookmark bookmark = root.first();
249
250 while(!bookmark.isNull()) {
251
252 if (bookmark.metaDataItem("isSystemItem") == "true") {
253 bookmark = root.next(bookmark);
254 continue;
255 }
256
257 sharedRoot.addBookmark(cloneBookmark(bookmark));
258 bookmark = root.next(bookmark);
259 dirty = true;
260 }
261 }
262
263 return dirty;
264
265}
266
267void KFilePlacesSharedBookmarks::slotSharedBookmarksChanged()
268{
269 //kDebug() << "shared bookmarks changed";
270 bool dirty = integrateSharedBookmarks();
271 if (dirty) m_placesBookmarkManager->emitChanged();
272}
273
274void KFilePlacesSharedBookmarks::slotBookmarksChanged()
275{
276 //kDebug() << "places bookmarks changed";
277 bool dirty = exportSharedBookmarks();
278 if (dirty) m_sharedBookmarkManager->emitChanged();
279}
280
281#include "kfileplacessharedbookmarks_p.moc"
282