File indexing completed on 2025-01-19 03:58:02
0001 /* ============================================================ 0002 * 0003 * This file is a part of digiKam project 0004 * https://www.digikam.org 0005 * 0006 * Date : 2017-05-15 0007 * Description : a node container for bookmarks 0008 * 0009 * SPDX-FileCopyrightText: 2017-2024 by Gilles Caulier <caulier dot gilles at gmail dot com> 0010 * 0011 * SPDX-License-Identifier: GPL-2.0-or-later 0012 * 0013 * ============================================================ */ 0014 0015 #include "bookmarknode.h" 0016 0017 // Qt includes 0018 0019 #include <QPointer> 0020 #include <QFile> 0021 0022 // KDE includes 0023 0024 #include <klocalizedstring.h> 0025 0026 // Local includes 0027 0028 #include "digikam_debug.h" 0029 0030 namespace Digikam 0031 { 0032 0033 class Q_DECL_HIDDEN BookmarkNode::Private 0034 { 0035 public: 0036 0037 explicit Private() 0038 : parent(nullptr), 0039 type (BookmarkNode::Root) 0040 { 0041 } 0042 0043 BookmarkNode* parent; 0044 Type type; 0045 QList<BookmarkNode*> children; 0046 }; 0047 0048 BookmarkNode::BookmarkNode(BookmarkNode::Type type, BookmarkNode* const parent) 0049 : QObject(nullptr), 0050 d (new Private) 0051 { 0052 expanded = false; 0053 d->parent = parent; 0054 d->type = type; 0055 0056 if (parent) 0057 { 0058 parent->add(this); 0059 } 0060 } 0061 0062 BookmarkNode::~BookmarkNode() 0063 { 0064 if (d->parent) 0065 { 0066 d->parent->remove(this); 0067 } 0068 0069 while (d->children.size()) 0070 { 0071 delete d->children.takeFirst(); 0072 } 0073 0074 delete d; 0075 } 0076 0077 bool BookmarkNode::operator==(const BookmarkNode& other) const 0078 { 0079 if ((url != other.url) || 0080 (title != other.title) || 0081 (desc != other.desc) || 0082 (expanded != other.expanded) || 0083 (dateAdded != other.dateAdded) || 0084 (d->type != other.d->type) || 0085 (d->children.count() != other.d->children.count())) 0086 { 0087 return false; 0088 } 0089 0090 for (int i = 0 ; i < d->children.count() ; ++i) 0091 { 0092 if (!((*(d->children[i])) == (*(other.d->children[i])))) 0093 { 0094 return false; 0095 } 0096 } 0097 0098 return true; 0099 } 0100 0101 BookmarkNode::Type BookmarkNode::type() const 0102 { 0103 return d->type; 0104 } 0105 0106 void BookmarkNode::setType(Type type) 0107 { 0108 d->type = type; 0109 } 0110 0111 QList<BookmarkNode*> BookmarkNode::children() const 0112 { 0113 return d->children; 0114 } 0115 0116 BookmarkNode* BookmarkNode::parent() const 0117 { 0118 return d->parent; 0119 } 0120 0121 void BookmarkNode::add(BookmarkNode* const child, int offset) 0122 { 0123 Q_ASSERT(child->d->type != Root); 0124 0125 if (child->d->parent) 0126 { 0127 child->d->parent->remove(child); 0128 } 0129 0130 child->d->parent = this; 0131 0132 if (offset == -1) 0133 { 0134 offset = d->children.size(); 0135 } 0136 0137 d->children.insert(offset, child); 0138 } 0139 0140 void BookmarkNode::remove(BookmarkNode* const child) 0141 { 0142 child->d->parent = nullptr; 0143 d->children.removeAll(child); 0144 } 0145 0146 // ------------------------------------------------------- 0147 0148 XbelReader::XbelReader() 0149 { 0150 } 0151 0152 BookmarkNode* XbelReader::read(const QString& fileName) 0153 { 0154 QFile file(fileName); 0155 0156 if (!file.exists() || !file.open(QFile::ReadOnly)) 0157 { 0158 BookmarkNode* const root = new BookmarkNode(BookmarkNode::Root); 0159 BookmarkNode* const folder = new BookmarkNode(BookmarkNode::RootFolder, root); 0160 folder->title = i18n("Bookmark folder"); 0161 0162 return root; 0163 } 0164 0165 return read(&file, true); 0166 } 0167 0168 BookmarkNode* XbelReader::read(QIODevice* const device, bool addRootFolder) 0169 { 0170 BookmarkNode* const root = new BookmarkNode(BookmarkNode::Root); 0171 setDevice(device); 0172 0173 if (readNextStartElement()) 0174 { 0175 QString version = attributes().value(QLatin1String("version")).toString(); 0176 0177 if ((name() == QLatin1String("xbel")) && 0178 (version.isEmpty() || (version == QLatin1String("1.0")))) 0179 { 0180 if (addRootFolder) 0181 { 0182 BookmarkNode* const folder = new BookmarkNode(BookmarkNode::RootFolder, root); 0183 folder->title = i18n("Bookmark folder"); 0184 readXBEL(folder); 0185 } 0186 else 0187 { 0188 readXBEL(root); 0189 } 0190 } 0191 else 0192 { 0193 raiseError(i18n("The file is not an XBEL version 1.0 file.")); 0194 } 0195 } 0196 0197 return root; 0198 } 0199 0200 void XbelReader::readXBEL(BookmarkNode* const parent) 0201 { 0202 Q_ASSERT(isStartElement() && (name() == QLatin1String("xbel"))); 0203 0204 while (readNextStartElement()) 0205 { 0206 if (name() == QLatin1String("folder")) 0207 { 0208 readFolder(parent); 0209 } 0210 else if (name() == QLatin1String("bookmark")) 0211 { 0212 readBookmarkNode(parent); 0213 } 0214 else if (name() == QLatin1String("separator")) 0215 { 0216 readSeparator(parent); 0217 } 0218 else 0219 { 0220 skipCurrentElement(); 0221 } 0222 } 0223 } 0224 0225 void XbelReader::readFolder(BookmarkNode* const parent) 0226 { 0227 Q_ASSERT(isStartElement() && (name() == QLatin1String("folder"))); 0228 0229 QPointer<BookmarkNode> folder = new BookmarkNode(BookmarkNode::Folder, parent); 0230 folder->expanded = (attributes().value(QLatin1String("folded")) == QLatin1String("no")); 0231 0232 while (readNextStartElement()) 0233 { 0234 if (name() == QLatin1String("title")) 0235 { 0236 readTitle(folder); 0237 } 0238 else if (name() == QLatin1String("desc")) 0239 { 0240 readDescription(folder); 0241 } 0242 else if (name() == QLatin1String("folder")) 0243 { 0244 readFolder(folder); 0245 } 0246 else if (name() == QLatin1String("bookmark")) 0247 { 0248 readBookmarkNode(folder); 0249 } 0250 else if (name() == QLatin1String("separator")) 0251 { 0252 readSeparator(folder); 0253 } 0254 else 0255 { 0256 skipCurrentElement(); 0257 } 0258 } 0259 } 0260 0261 void XbelReader::readTitle(BookmarkNode* const parent) 0262 { 0263 Q_ASSERT(isStartElement() && (name() == QLatin1String("title"))); 0264 0265 parent->title = readElementText(); 0266 } 0267 0268 void XbelReader::readDescription(BookmarkNode* const parent) 0269 { 0270 Q_ASSERT(isStartElement() && (name() == QLatin1String("desc"))); 0271 0272 parent->desc = readElementText(); 0273 } 0274 0275 void XbelReader::readSeparator(BookmarkNode* const parent) 0276 { 0277 new BookmarkNode(BookmarkNode::Separator, parent); 0278 0279 // empty elements have a start and end element 0280 0281 readNext(); 0282 } 0283 0284 void XbelReader::readBookmarkNode(BookmarkNode* const parent) 0285 { 0286 Q_ASSERT(isStartElement() && (name() == QLatin1String("bookmark"))); 0287 0288 BookmarkNode* const bookmark = new BookmarkNode(BookmarkNode::Bookmark, parent); 0289 bookmark->url = attributes().value(QLatin1String("href")).toString(); 0290 QString date = attributes().value(QLatin1String("added")).toString(); 0291 bookmark->dateAdded = QDateTime::fromString(date, Qt::ISODate); 0292 0293 while (readNextStartElement()) 0294 { 0295 if (name() == QLatin1String("title")) 0296 { 0297 readTitle(bookmark); 0298 } 0299 else if (name() == QLatin1String("desc")) 0300 { 0301 readDescription(bookmark); 0302 } 0303 else 0304 { 0305 skipCurrentElement(); 0306 } 0307 } 0308 0309 if (bookmark->title.isEmpty()) 0310 { 0311 bookmark->title = i18n("Unknown title"); 0312 } 0313 } 0314 0315 // ------------------------------------------------------- 0316 0317 XbelWriter::XbelWriter() 0318 { 0319 setAutoFormatting(true); 0320 } 0321 0322 bool XbelWriter::write(const QString& fileName, const BookmarkNode* const root) 0323 { 0324 QFile file(fileName); 0325 0326 if (!root || !file.open(QFile::WriteOnly)) 0327 { 0328 return false; 0329 } 0330 0331 return write(&file, root); 0332 } 0333 0334 bool XbelWriter::write(QIODevice* const device, const BookmarkNode* const root) 0335 { 0336 setDevice(device); 0337 0338 writeStartDocument(); 0339 writeDTD(QLatin1String("<!DOCTYPE xbel>")); 0340 writeStartElement(QLatin1String("xbel")); 0341 writeAttribute(QLatin1String("version"), QLatin1String("1.0")); 0342 0343 if (root->type() == BookmarkNode::Root) 0344 { 0345 BookmarkNode* const rootFolder = root->children().constFirst(); 0346 0347 for (int i = 0 ; i < rootFolder->children().count() ; ++i) 0348 { 0349 writeItem(rootFolder->children().at(i)); 0350 } 0351 } 0352 else 0353 { 0354 writeItem(root); 0355 } 0356 0357 writeEndDocument(); 0358 0359 return true; 0360 } 0361 0362 void XbelWriter::writeItem(const BookmarkNode* const parent) 0363 { 0364 switch (parent->type()) 0365 { 0366 case BookmarkNode::Folder: 0367 writeStartElement(QLatin1String("folder")); 0368 writeAttribute(QLatin1String("folded"), parent->expanded ? QLatin1String("no") : QLatin1String("yes")); 0369 writeTextElement(QLatin1String("title"), parent->title); 0370 0371 for (int i = 0 ; i < parent->children().count() ; ++i) 0372 { 0373 writeItem(parent->children().at(i)); 0374 } 0375 0376 writeEndElement(); 0377 break; 0378 0379 case BookmarkNode::Bookmark: 0380 writeStartElement(QLatin1String("bookmark")); 0381 0382 if (!parent->url.isEmpty()) 0383 { 0384 writeAttribute(QLatin1String("href"), parent->url); 0385 } 0386 0387 if (parent->dateAdded.isValid()) 0388 { 0389 writeAttribute(QLatin1String("added"), parent->dateAdded.toString(Qt::ISODate)); 0390 } 0391 0392 if (!parent->desc.isEmpty()) 0393 { 0394 writeAttribute(QLatin1String("desc"), parent->desc); 0395 } 0396 0397 writeTextElement(QLatin1String("title"), parent->title); 0398 0399 writeEndElement(); 0400 break; 0401 0402 case BookmarkNode::Separator: 0403 writeEmptyElement(QLatin1String("separator")); 0404 break; 0405 0406 default: 0407 break; 0408 } 0409 } 0410 0411 } // namespace Digikam 0412 0413 #include "moc_bookmarknode.cpp"