Warning, file /education/kalzium/src/kdeeduglossary.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
0001 /* 0002 SPDX-FileCopyrightText: 2005, 2006 Carsten Niehaus <cniehaus@kde.org> 0003 SPDX-FileCopyrightText: 2005-2007 Pino Toscano <pino@kde.org> 0004 0005 SPDX-License-Identifier: GPL-2.0-or-later 0006 */ 0007 0008 #include "kdeeduglossary.h" 0009 0010 #include <KActionCollection> 0011 #include <KLocalizedString> 0012 #include <KTreeWidgetSearchLine> 0013 0014 #include "kalzium_debug.h" 0015 #include <QDialogButtonBox> 0016 #include <QDomDocument> 0017 #include <QFile> 0018 #include <QHeaderView> 0019 #include <QKeyEvent> 0020 #include <QList> 0021 #include <QPushButton> 0022 #include <QRegularExpression> 0023 #include <QSplitter> 0024 #include <QStringList> 0025 #include <QTextBrowser> 0026 #include <QTreeWidget> 0027 #include <QVBoxLayout> 0028 0029 static const int FirstLetterRole = 0x00b00a00; 0030 0031 static const int GlossaryTreeItemType = QTreeWidgetItem::UserType + 1; 0032 0033 class GlossaryTreeItem : public QTreeWidgetItem 0034 { 0035 public: 0036 GlossaryTreeItem(Glossary *g, GlossaryItem *gi) 0037 : QTreeWidgetItem(GlossaryTreeItemType) 0038 , m_g(g) 0039 , m_gi(gi) 0040 { 0041 setText(0, m_gi->name()); 0042 } 0043 0044 Glossary *glossary() const 0045 { 0046 return m_g; 0047 } 0048 0049 GlossaryItem *glossaryItem() const 0050 { 0051 return m_gi; 0052 } 0053 0054 private: 0055 Glossary *m_g; 0056 GlossaryItem *m_gi; 0057 }; 0058 0059 struct GlossaryInfo { 0060 GlossaryInfo(Glossary *g) 0061 : glossary(g) 0062 , folded(true) 0063 { 0064 } 0065 0066 Glossary *glossary; 0067 bool folded; 0068 }; 0069 0070 class GlossaryDialog::Private 0071 { 0072 public: 0073 Private(GlossaryDialog *qq) 0074 : q(qq) 0075 { 0076 } 0077 0078 ~Private() 0079 { 0080 QList<GlossaryInfo>::ConstIterator it = m_glossaries.constBegin(); 0081 QList<GlossaryInfo>::ConstIterator itEnd = m_glossaries.constEnd(); 0082 for (; it != itEnd; ++it) { 0083 delete (*it).glossary; 0084 } 0085 0086 delete m_htmlpart; 0087 } 0088 0089 void rebuildTree(); 0090 QTreeWidgetItem *createItem(const GlossaryInfo &gi) const; 0091 QTreeWidgetItem *findTreeWithLetter(const QChar &l, QTreeWidgetItem *item) const; 0092 0093 // slots 0094 void itemActivated(QTreeWidgetItem *item, int column); 0095 0096 GlossaryDialog *q; 0097 0098 QList<GlossaryInfo> m_glossaries; 0099 0100 QTextBrowser *m_htmlpart; 0101 QTreeWidget *m_glosstree; 0102 KTreeWidgetSearchLine *m_search; 0103 QString m_htmlbasestring; 0104 0105 KActionCollection *m_actionCollection; 0106 }; 0107 0108 Glossary::Glossary(const QUrl &url, const QString &path) 0109 { 0110 init(url, path); 0111 } 0112 0113 Glossary::Glossary() 0114 { 0115 init(QUrl(), QString()); 0116 } 0117 0118 Glossary::~Glossary() 0119 { 0120 qDeleteAll(m_itemlist); 0121 } 0122 0123 void Glossary::init(const QUrl &url, const QString &path) 0124 { 0125 // setting a generic name for a new glossary 0126 m_name = i18n("Glossary"); 0127 0128 setPicturePath(path); 0129 0130 if (!url.isEmpty()) { 0131 QDomDocument doc(QStringLiteral("document")); 0132 0133 if (loadLayout(doc, url)) { 0134 setItemlist(readItems(doc)); 0135 if (!m_picturepath.isEmpty()) { 0136 fixImagePath(); 0137 } 0138 } 0139 } 0140 } 0141 0142 bool Glossary::loadLayout(QDomDocument &Document, const QUrl &url) 0143 { 0144 QFile layoutFile(url.path()); 0145 0146 if (!layoutFile.exists()) { 0147 qCDebug(KALZIUM_LOG) << "no such file: " << layoutFile.fileName(); 0148 return false; 0149 } 0150 0151 if (!layoutFile.open(QIODevice::ReadOnly)) { 0152 return false; 0153 } 0154 0155 // check if document is well-formed 0156 if (!Document.setContent(&layoutFile)) { 0157 qCDebug(KALZIUM_LOG) << "wrong xml of " << layoutFile.fileName(); 0158 layoutFile.close(); 0159 return false; 0160 } 0161 layoutFile.close(); 0162 0163 return true; 0164 } 0165 0166 bool Glossary::isEmpty() const 0167 { 0168 return m_itemlist.count() == 0; 0169 } 0170 0171 void Glossary::setName(const QString &name) 0172 { 0173 if (name.isEmpty()) { 0174 return; 0175 } 0176 m_name = name; 0177 } 0178 0179 void Glossary::setPicturePath(const QString &path) 0180 { 0181 if (path.isEmpty()) { 0182 return; 0183 } 0184 m_picturepath = path; 0185 } 0186 0187 void Glossary::setBackgroundPicture(const QString &filename) 0188 { 0189 if (filename.isEmpty()) { 0190 return; 0191 } 0192 m_backgroundpicture = filename; 0193 } 0194 0195 void Glossary::fixImagePath() 0196 { 0197 QString imgtag = "<img src=\"file://" + m_picturepath + '/' + "\\1\" />"; 0198 QRegularExpression exp(R"(\[img\]([^[]+)\[/img\])"); 0199 0200 foreach (GlossaryItem *item, m_itemlist) { 0201 QString tmp = item->desc(); 0202 while (exp.match(tmp).hasMatch()) { 0203 tmp = tmp.replace(exp, imgtag); 0204 } 0205 item->setDesc(tmp); 0206 } 0207 } 0208 0209 QList<GlossaryItem *> Glossary::readItems(QDomDocument &itemDocument) 0210 { 0211 QList<GlossaryItem *> list; 0212 0213 QDomNodeList itemList; 0214 QDomNodeList refNodeList; 0215 QDomElement itemElement; 0216 QStringList reflist; 0217 0218 itemList = itemDocument.elementsByTagName(QStringLiteral("item")); 0219 0220 const uint num = itemList.count(); 0221 for (uint i = 0; i < num; ++i) { 0222 reflist.clear(); 0223 auto item = new GlossaryItem(); 0224 0225 itemElement = itemList.item(i).toElement(); 0226 0227 QDomNode nameNode = itemElement.namedItem(QStringLiteral("name")); 0228 QDomNode descNode = itemElement.namedItem(QStringLiteral("desc")); 0229 0230 QString picName = itemElement.namedItem(QStringLiteral("picture")).toElement().text(); 0231 QDomElement refNode = itemElement.namedItem(QStringLiteral("references")).toElement(); 0232 0233 QString desc = i18n(descNode.toElement().text().toUtf8().constData()); 0234 if (!picName.isEmpty()) { 0235 desc.prepend("[br][img]" + picName + "[/img][brclear][br]"); 0236 } 0237 0238 item->setName(i18n(nameNode.toElement().text().toUtf8().constData())); 0239 0240 desc = desc.replace(QLatin1String("[b]"), QLatin1String("<b>")); 0241 desc = desc.replace(QLatin1String("[/b]"), QLatin1String("</b>")); 0242 desc = desc.replace(QLatin1String("[i]"), QLatin1String("<i>")); 0243 desc = desc.replace(QLatin1String("[/i]"), QLatin1String("</i>")); 0244 desc = desc.replace(QLatin1String("[sub]"), QLatin1String("<sub>")); 0245 desc = desc.replace(QLatin1String("[/sub]"), QLatin1String("</sub>")); 0246 desc = desc.replace(QLatin1String("[sup]"), QLatin1String("<sup>")); 0247 desc = desc.replace(QLatin1String("[/sup]"), QLatin1String("</sup>")); 0248 desc = desc.replace(QLatin1String("[br]"), QLatin1String("<br />")); 0249 desc = desc.replace(QLatin1String("[brclear]"), QLatin1String("<br clear=\"left\"/>")); 0250 item->setDesc(desc); 0251 0252 refNodeList = refNode.elementsByTagName(QStringLiteral("refitem")); 0253 for (int it = 0; it < refNodeList.count(); ++it) { 0254 reflist << i18n(refNodeList.item(it).toElement().text().toUtf8().constData()); 0255 } 0256 item->setRef(reflist); 0257 0258 list.append(item); 0259 } 0260 0261 return list; 0262 } 0263 0264 void Glossary::addItem(GlossaryItem *item) 0265 { 0266 m_itemlist.append(item); 0267 } 0268 0269 QList<GlossaryItem *> Glossary::itemlist() const 0270 { 0271 return m_itemlist; 0272 } 0273 0274 void Glossary::clear() 0275 { 0276 m_itemlist.clear(); 0277 } 0278 0279 QString Glossary::name() const 0280 { 0281 return m_name; 0282 } 0283 0284 void Glossary::setItemlist(QList<GlossaryItem *> list) 0285 { 0286 m_itemlist = list; 0287 } 0288 0289 QString Glossary::picturePath() const 0290 { 0291 return m_picturepath; 0292 } 0293 0294 QString Glossary::backgroundPicture() const 0295 { 0296 return m_backgroundpicture; 0297 } 0298 0299 GlossaryDialog::GlossaryDialog(QWidget *parent) 0300 : QDialog(parent) 0301 , d(new Private(this)) 0302 { 0303 setWindowTitle(i18nc("@title:window", "Glossary")); 0304 0305 // this string will be used for all items. If a backgroundpicture should 0306 // be used call Glossary::setBackgroundPicture(). 0307 d->m_htmlbasestring = 0308 QStringLiteral("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\"><html><body%1>"); 0309 0310 auto main = new QWidget(this); 0311 auto vbox = new QVBoxLayout(main); 0312 setLayout(vbox); 0313 vbox->setContentsMargins(0, 0, 0, 0); 0314 0315 auto hbox = new QHBoxLayout(); 0316 0317 d->m_search = new KTreeWidgetSearchLine(main); 0318 d->m_search->setObjectName(QStringLiteral("search-line")); 0319 hbox->addWidget(d->m_search); 0320 vbox->addLayout(hbox); 0321 setFocusProxy(d->m_search); 0322 0323 auto vs = new QSplitter(main); 0324 vbox->addWidget(vs); 0325 0326 d->m_glosstree = new QTreeWidget(vs); 0327 d->m_glosstree->setObjectName(QStringLiteral("treeview")); 0328 d->m_glosstree->setHeaderLabel(QStringLiteral("entries")); 0329 d->m_glosstree->header()->hide(); 0330 d->m_glosstree->setRootIsDecorated(true); 0331 0332 d->m_search->addTreeWidget(d->m_glosstree); 0333 0334 d->m_htmlpart = new QTextBrowser(vs); 0335 d->m_htmlpart->setOpenLinks(false); 0336 0337 connect(d->m_glosstree, SIGNAL(itemActivated(QTreeWidgetItem *, int)), this, SLOT(itemActivated(QTreeWidgetItem *, int))); 0338 connect(d->m_htmlpart, &QTextBrowser::anchorClicked, this, [=](const QUrl &link) { 0339 // using the "path" part of a qurl as reference 0340 QString myurl = link.path().toLower(); 0341 QTreeWidgetItemIterator it(this->d->m_glosstree); 0342 while (*it) { 0343 if ((*it)->type() == GlossaryTreeItemType && (*it)->text(0).toLower() == myurl) { 0344 // force the item to be selected 0345 this->d->m_glosstree->setCurrentItem(*it); 0346 // display its content 0347 Q_EMIT this->d->itemActivated((*it), 0); 0348 break; 0349 } else { 0350 ++it; 0351 } 0352 } 0353 }); 0354 0355 auto buttonBox = new QDialogButtonBox(QDialogButtonBox::Close); 0356 connect(buttonBox, &QDialogButtonBox::rejected, this, &GlossaryDialog::reject); 0357 buttonBox->button(QDialogButtonBox::Close)->setDefault(true); 0358 vbox->addWidget(buttonBox); 0359 0360 resize(800, 400); 0361 } 0362 0363 GlossaryDialog::~GlossaryDialog() 0364 { 0365 delete d; 0366 } 0367 0368 void GlossaryDialog::keyPressEvent(QKeyEvent *e) 0369 { 0370 if (e->key() == Qt::Key_Return) { 0371 e->ignore(); 0372 } 0373 QDialog::keyPressEvent(e); 0374 } 0375 0376 void GlossaryDialog::Private::rebuildTree() 0377 { 0378 m_glosstree->clear(); 0379 0380 QList<GlossaryInfo>::ConstIterator it = m_glossaries.constBegin(); 0381 QList<GlossaryInfo>::ConstIterator itEnd = m_glossaries.constEnd(); 0382 for (; it != itEnd; ++it) { 0383 m_glosstree->addTopLevelItem(createItem(*it)); 0384 } 0385 } 0386 0387 QTreeWidgetItem *GlossaryDialog::Private::createItem(const GlossaryInfo &gi) const 0388 { 0389 Glossary *glossary = gi.glossary; 0390 bool folded = gi.folded; 0391 auto main = new QTreeWidgetItem(); 0392 main->setText(0, glossary->name()); 0393 main->setFlags(Qt::ItemIsEnabled); 0394 foreach (GlossaryItem *item, glossary->itemlist()) { 0395 if (folded) { 0396 QChar thisletter = item->name().toUpper().at(0); 0397 QTreeWidgetItem *thisletteritem = findTreeWithLetter(thisletter, main); 0398 if (!thisletteritem) { 0399 thisletteritem = new QTreeWidgetItem(main); 0400 thisletteritem->setText(0, QString(thisletter)); 0401 thisletteritem->setFlags(Qt::ItemIsEnabled); 0402 thisletteritem->setData(0, FirstLetterRole, thisletter); 0403 } 0404 thisletteritem->addChild(new GlossaryTreeItem(glossary, item)); 0405 } else { 0406 main->addChild(new GlossaryTreeItem(glossary, item)); 0407 } 0408 } 0409 return main; 0410 } 0411 0412 void GlossaryDialog::addGlossary(Glossary *newgloss, bool folded) 0413 { 0414 if (!newgloss || newgloss->isEmpty()) { 0415 return; 0416 } 0417 0418 GlossaryInfo gi(newgloss); 0419 gi.folded = folded; 0420 d->m_glossaries.append(gi); 0421 0422 d->m_glosstree->addTopLevelItem(d->createItem(gi)); 0423 d->m_glosstree->sortItems(0, Qt::AscendingOrder); 0424 } 0425 0426 QTreeWidgetItem *GlossaryDialog::Private::findTreeWithLetter(const QChar &l, QTreeWidgetItem *item) const 0427 { 0428 int count = item->childCount(); 0429 for (int i = 0; i < count; ++i) { 0430 QTreeWidgetItem *itemchild = item->child(i); 0431 if (itemchild->data(0, FirstLetterRole).toChar() == l) { 0432 return itemchild; 0433 } 0434 } 0435 return nullptr; 0436 } 0437 0438 void GlossaryDialog::Private::itemActivated(QTreeWidgetItem *item, int column) 0439 { 0440 Q_UNUSED(column) 0441 if (!item || item->type() != GlossaryTreeItemType) { 0442 return; 0443 } 0444 0445 auto glosstreeitem = static_cast<GlossaryTreeItem *>(item); 0446 GlossaryItem *glossitem = glosstreeitem->glossaryItem(); 0447 QString html; 0448 QString bg_picture = glosstreeitem->glossary()->backgroundPicture(); 0449 if (!bg_picture.isEmpty()) { 0450 html = " background=\"file://" + bg_picture + "\""; 0451 } 0452 0453 html = m_htmlbasestring.arg(html); 0454 html += "<div style=\"margin-left: 10px\">" + glossitem->toHtml() + "</div></body></html>"; 0455 0456 m_htmlpart->setHtml(html); 0457 } 0458 0459 void GlossaryItem::setRef(const QStringList &s) 0460 { 0461 m_ref = s; 0462 m_ref.sort(); 0463 } 0464 0465 QString GlossaryItem::toHtml() const 0466 { 0467 QString code = "<h1>" + m_name + "</h1>" + "<p>" + m_desc + "</p>" + parseReferences(); 0468 0469 return code; 0470 } 0471 0472 QString GlossaryItem::parseReferences() const 0473 { 0474 if (m_ref.isEmpty()) { 0475 return {}; 0476 } 0477 0478 QString htmlcode = "<h3>" + i18n("References") + "</h3><ul type=\"disc\">"; 0479 static QString basehref = QStringLiteral("<li><a href=\"item:%1\" title=\"%2\">%3</a></li>"); 0480 0481 foreach (const QString &ref, m_ref) { 0482 htmlcode += basehref.arg(QUrl::toPercentEncoding(ref), i18n("Go to '%1'", ref), ref); 0483 } 0484 htmlcode += QLatin1String("</ul>"); 0485 0486 return htmlcode; 0487 } 0488 0489 void GlossaryItem::setName(const QString &s) 0490 { 0491 m_name = s; 0492 } 0493 0494 void GlossaryItem::setDesc(const QString &s) 0495 { 0496 m_desc = s; 0497 } 0498 0499 void GlossaryItem::setPictures(const QString &s) 0500 { 0501 m_pic = QStringList(s); 0502 } 0503 0504 QString GlossaryItem::name() const 0505 { 0506 return m_name; 0507 } 0508 0509 QString GlossaryItem::desc() const 0510 { 0511 return m_desc; 0512 } 0513 0514 QStringList GlossaryItem::ref() const 0515 { 0516 return m_ref; 0517 } 0518 0519 QStringList GlossaryItem::pictures() const 0520 { 0521 return m_pic; 0522 } 0523 0524 #include "moc_kdeeduglossary.cpp"