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 | |
32 | static bool compareBookmarks(const KBookmark & bookmark1, const KBookmark & bookmark2) |
33 | { |
34 | return (bookmark1.url() == bookmark2.url() || bookmark1.text() == bookmark2.text()); |
35 | } |
36 | |
37 | static 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 | /* |
59 | static QString nodeAsString(const QDomNode & node1) |
60 | { |
61 | QString str; |
62 | QTextStream ts( &str, QIODevice::WriteOnly ); |
63 | ts << node1; |
64 | return str; |
65 | } |
66 | */ |
67 | |
68 | static 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 | |
74 | static 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 | |
82 | static KBookmark cloneBookmark(const KBookmark & toClone) |
83 | { |
84 | const QDomNode cloned = toClone.internalElement().cloneNode(true); |
85 | return KBookmark(cloned.toElement ()); |
86 | } |
87 | |
88 | |
89 | static 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 | |
99 | static 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 | |
112 | KFilePlacesSharedBookmarks::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 | |
134 | bool 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 | |
197 | bool 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 | |
267 | void KFilePlacesSharedBookmarks::slotSharedBookmarksChanged() |
268 | { |
269 | //kDebug() << "shared bookmarks changed"; |
270 | bool dirty = integrateSharedBookmarks(); |
271 | if (dirty) m_placesBookmarkManager->emitChanged(); |
272 | } |
273 | |
274 | void 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 | |