File indexing completed on 2025-01-12 04:35:48

0001 /***************************************************************************
0002  *   SPDX-License-Identifier: GPL-2.0-or-later
0003  *                                                                         *
0004  *   SPDX-FileCopyrightText: 2004-2023 Thomas Fischer <fischer@unix-ag.uni-kl.de>
0005  *                              and contributors                           *
0006  *                                                                         *
0007  *   Contributions to this file were made by                               *
0008  *   - Jurgen Spitzmuller <juergen@spitzmueller.org>                       *
0009  *                                                                         *
0010  *   This program is free software; you can redistribute it and/or modify  *
0011  *   it under the terms of the GNU General Public License as published by  *
0012  *   the Free Software Foundation; either version 2 of the License, or     *
0013  *   (at your option) any later version.                                   *
0014  *                                                                         *
0015  *   This program is distributed in the hope that it will be useful,       *
0016  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
0017  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
0018  *   GNU General Public License for more details.                          *
0019  *                                                                         *
0020  *   You should have received a copy of the GNU General Public License     *
0021  *   along with this program; if not, see <https://www.gnu.org/licenses/>. *
0022  ***************************************************************************/
0023 
0024 #include "referencepreview.h"
0025 
0026 #include <QFrame>
0027 #include <QBuffer>
0028 #include <QTextDocument>
0029 #include <QLayout>
0030 #include <QApplication>
0031 #include <QTextStream>
0032 #include <QTemporaryFile>
0033 #include <QPalette>
0034 #include <QMimeType>
0035 #include <QFileDialog>
0036 #include <QPushButton>
0037 #include <QFontDatabase>
0038 #include <QComboBox>
0039 #include <QPointer>
0040 
0041 #include <kio_version.h>
0042 #include <KLocalizedString>
0043 #if KIO_VERSION >= QT_VERSION_CHECK(5, 71, 0)
0044 #include <KIO/OpenUrlJob>
0045 #if KIO_VERSION >= QT_VERSION_CHECK(5, 98, 0)
0046 #include <KIO/JobUiDelegateFactory>
0047 #else // < 5.98.0
0048 #include <KIO/JobUiDelegate>
0049 #endif // QT_VERSION_CHECK(5, 98, 0)
0050 #else // < 5.71.0
0051 #include <KRun>
0052 #endif // KIO_VERSION >= QT_VERSION_CHECK(5, 71, 0)
0053 #include <KIO/CopyJob>
0054 #include <KJobWidgets>
0055 #include <KSharedConfig>
0056 #include <KConfigGroup>
0057 #include <KTextEdit>
0058 
0059 #include <Element>
0060 #include <Entry>
0061 #include <File>
0062 #include <FileExporterBibTeX>
0063 #include <FileExporterBibTeX2HTML>
0064 #include <FileExporterRIS>
0065 #include <FileExporterXML>
0066 #include <file/FileView>
0067 #include "logging_program.h"
0068 
0069 typedef struct {
0070     int id;
0071     QString label;
0072     QHash<QString, QVariant> attributes;
0073 } PreviewStyle;
0074 
0075 Q_DECLARE_METATYPE(PreviewStyle)
0076 
0077 class ReferencePreview::Private
0078 {
0079 private:
0080     ReferencePreview *p;
0081 
0082     QVector<PreviewStyle> previewStyles() const {
0083         static QVector<PreviewStyle> listOfStyles;
0084         if (listOfStyles.isEmpty()) {
0085             listOfStyles = {
0086                 {1000, i18n("Source (BibTeX)"), {{QStringLiteral("exporter"), QStringLiteral("bibtex")}, {QStringLiteral("fixed-font"), true}}},
0087                 {1010, i18n("Source (RIS)"), {{QStringLiteral("exporter"), QStringLiteral("ris")}, {QStringLiteral("fixed-font"), true}}},
0088                 {1210, i18n("Standard"), {{QStringLiteral("exporter"), QStringLiteral("xml")}, {QStringLiteral("style"), QVariant::fromValue(FileExporterXML::OutputStyle::HTML_Standard)}, {QStringLiteral("html"), true}}},
0089                 {1220, i18n("Wikipedia Citation"), {{QStringLiteral("exporter"), QStringLiteral("xml")}, {QStringLiteral("style"), QVariant::fromValue(FileExporterXML::OutputStyle::Plain_WikipediaCite)}, {QStringLiteral("fixed-font"), true}}},
0090                 {1230, i18n("Abstract-only"), {{QStringLiteral("exporter"), QStringLiteral("xml")}, {QStringLiteral("style"), QVariant::fromValue(FileExporterXML::OutputStyle::HTML_AbstractOnly)}, {QStringLiteral("html"), true}}}
0091             };
0092             if (!QStandardPaths::findExecutable(QStringLiteral("bibtex2html")).isEmpty()) {
0093                 // If 'bibtex2html' binary is available ...
0094                 int id = 2000;
0095                 // Query from FileExporterBibTeX2HTML which BibTeX styles are available
0096                 for (const QString &bibtex2htmlStyle : FileExporterBibTeX2HTML::availableLaTeXBibliographyStyles()) {
0097                     const QString label{QString(QStringLiteral("%2 (bibtex2html)")).arg(bibtex2htmlStyle)};
0098                     listOfStyles.append({id++, label, {{QStringLiteral("exporter"), QStringLiteral("bibtex2html")}, {QStringLiteral("html"), true}, {QStringLiteral("externalprogram"), true}, {QStringLiteral("bibtexstyle"), bibtex2htmlStyle}}});
0099                 }
0100             }
0101         }
0102         return listOfStyles;
0103     }
0104 
0105 public:
0106     KSharedConfigPtr config;
0107     static const QString configGroupName;
0108     static const QString configKeyName;
0109     static const QString errorMessageInnerTemplate;
0110     const QString htmlStart, errorMessageOuterTemplate;
0111 
0112     QPushButton *buttonOpen, *buttonSaveAsHTML;
0113     QString htmlText;
0114     QUrl baseUrl;
0115     QTextDocument *htmlDocument;
0116     KTextEdit *htmlView;
0117     QComboBox *comboBox;
0118     QSharedPointer<const Element> element;
0119     const File *file;
0120     FileView *fileView;
0121 
0122     Private(ReferencePreview *parent)
0123             : p(parent), config(KSharedConfig::openConfig(QStringLiteral("kbibtexrc"))),
0124           htmlStart(QString(QStringLiteral("<!DOCTYPE html>\n<html>\n<head>\n<meta charset=\"utf-8\">\n<style type=\"text/css\">\npre {\n white-space: pre-wrap;\n white-space: -moz-pre-wrap;\n white-space: -pre-wrap;\n white-space: -o-pre-wrap;\n word-wrap: break-word;\n}\n</style>\n</head>\n<body style=\"color: %1; background-color: '%2'; font-family: '%3'; font-size: %4pt\">")).arg(QApplication::palette().text().color().name(QColor::HexRgb), QApplication::palette().base().color().name(QColor::HexRgb), QFontDatabase::systemFont(QFontDatabase::GeneralFont).family(), QString::number(QFontDatabase::systemFont(QFontDatabase::GeneralFont).pointSize()))),
0125           errorMessageOuterTemplate(htmlStart + errorMessageInnerTemplate + QStringLiteral("</body></html>")),
0126           file(nullptr), fileView(nullptr)
0127     {
0128         QGridLayout *gridLayout = new QGridLayout(p);
0129         gridLayout->setContentsMargins(0, 0, 0, 0);
0130         gridLayout->setColumnStretch(0, 1);
0131         gridLayout->setColumnStretch(1, 0);
0132         gridLayout->setColumnStretch(2, 0);
0133 
0134         comboBox = new QComboBox(p);
0135         gridLayout->addWidget(comboBox, 0, 0, 1, 3);
0136 
0137         QFrame *frame = new QFrame(p);
0138         gridLayout->addWidget(frame, 1, 0, 1, 3);
0139         frame->setFrameShadow(QFrame::Sunken);
0140         frame->setFrameShape(QFrame::StyledPanel);
0141 
0142         QVBoxLayout *layout = new QVBoxLayout(frame);
0143         layout->setContentsMargins(0, 0, 0, 0);
0144         htmlView = new KTextEdit(frame);
0145         htmlView->setReadOnly(true);
0146         htmlDocument = new QTextDocument(htmlView);
0147         htmlView->setDocument(htmlDocument);
0148         layout->addWidget(htmlView);
0149 
0150         buttonOpen = new QPushButton(QIcon::fromTheme(QStringLiteral("document-open")), i18n("Open"), p);
0151         buttonOpen->setToolTip(i18n("Open reference in web browser."));
0152         gridLayout->addWidget(buttonOpen, 2, 1, 1, 1);
0153 
0154         buttonSaveAsHTML = new QPushButton(QIcon::fromTheme(QStringLiteral("document-save")), i18n("Save as HTML"), p);
0155         buttonSaveAsHTML->setToolTip(i18n("Save reference as HTML fragment."));
0156         gridLayout->addWidget(buttonSaveAsHTML, 2, 2, 1, 1);
0157     }
0158 
0159     bool saveHTML(const QUrl &url) const {
0160         QTemporaryFile tempFile;
0161         tempFile.setAutoRemove(true);
0162 
0163         bool result = saveHTML(tempFile);
0164 
0165         if (result) {
0166             KIO::CopyJob *copyJob = KIO::copy(QUrl::fromLocalFile(tempFile.fileName()), url, KIO::Overwrite);
0167             KJobWidgets::setWindow(copyJob, p);
0168             result = copyJob->exec();
0169         }
0170 
0171         return result;
0172     }
0173 
0174     bool saveHTML(QTemporaryFile &tempFile) const {
0175         if (tempFile.open()) {
0176             QTextStream ts(&tempFile);
0177             // https://forum.qt.io/topic/135724/qt-6-replacement-for-qtextcodec
0178 #if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
0179             ts.setCodec("utf-8");
0180 #else
0181             ts.setEncoding(QStringConverter::Utf8);
0182 #endif
0183             static const QRegularExpression kbibtexHrefRegExp(QStringLiteral("<a[^>]+href=\"kbibtex:[^>]+>(.+?)</a>"));
0184             QString modifiedHtmlText = htmlText;
0185             modifiedHtmlText = modifiedHtmlText.replace(kbibtexHrefRegExp, QStringLiteral("\\1"));
0186             ts << modifiedHtmlText;
0187             tempFile.close();
0188             return true;
0189         }
0190 
0191         return false;
0192     }
0193 
0194     void loadState() {
0195         KConfigGroup configGroup(config, configGroupName);
0196         const int previousStyleId = configGroup.readEntry(configKeyName, -1);
0197 
0198         comboBox->clear();
0199 
0200         int styleIndex = 0, c = 0;
0201         for (const PreviewStyle &previewStyle : previewStyles()) {
0202             comboBox->addItem(previewStyle.label, QVariant::fromValue(previewStyle));
0203             if (previousStyleId == previewStyle.id)
0204                 styleIndex = c;
0205             ++c;
0206         }
0207         comboBox->setCurrentIndex(styleIndex);
0208     }
0209 
0210     void saveState() {
0211         KConfigGroup configGroup(config, configGroupName);
0212         configGroup.writeEntry(configKeyName, comboBox->itemData(comboBox->currentIndex()).value<PreviewStyle>().id);
0213         config->sync();
0214     }
0215 
0216     void setHtml(const QString &html, bool buttonsEnabled)
0217     {
0218         htmlText = QString(html).remove(QStringLiteral("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"));
0219         htmlDocument->setHtml(htmlText);
0220         buttonOpen->setEnabled(buttonsEnabled);
0221         buttonSaveAsHTML->setEnabled(buttonsEnabled);
0222     }
0223 };
0224 
0225 const QString ReferencePreview::Private::configGroupName {QStringLiteral("Reference Preview Docklet")};
0226 const QString ReferencePreview::Private::configKeyName {QStringLiteral("Style")};
0227 const QString ReferencePreview::Private::errorMessageInnerTemplate {QString(QStringLiteral("<p style=\"font-style: italic;\">%1</p><p style=\"font-size: 90%;\">%2 %3</p>")).arg(i18n("No preview available"), i18n("Reason:"))};
0228 
0229 ReferencePreview::ReferencePreview(QWidget *parent)
0230         : QWidget(parent), d(new Private(this))
0231 {
0232     d->loadState();
0233 
0234     connect(d->buttonOpen, &QPushButton::clicked, this, &ReferencePreview::openAsHTML);
0235     connect(d->buttonSaveAsHTML, &QPushButton::clicked, this, &ReferencePreview::saveAsHTML);
0236     connect(d->comboBox, static_cast<void(QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, &ReferencePreview::renderHTML);
0237 
0238     setEnabled(false);
0239 }
0240 
0241 ReferencePreview::~ReferencePreview()
0242 {
0243     delete d;
0244 }
0245 
0246 void ReferencePreview::setEnabled(bool enabled)
0247 {
0248     if (enabled)
0249         d->setHtml(d->htmlText, true);
0250     else
0251         d->setHtml(d->errorMessageOuterTemplate.arg(i18nc("Message in case reference preview widget is disabled", "Preview disabled")), false);
0252     d->htmlView->setEnabled(enabled);
0253     d->comboBox->setEnabled(enabled);
0254 }
0255 
0256 void ReferencePreview::setElement(QSharedPointer<const Element> element, const File *file)
0257 {
0258     d->element = element;
0259     d->file = file;
0260     renderHTML();
0261 }
0262 
0263 void ReferencePreview::renderHTML()
0264 {
0265     if (d->element.isNull()) {
0266         d->setHtml(d->errorMessageOuterTemplate.arg(i18nc("Message in case no bibliographic element is selected for preview", "No element selected")), false);
0267         return;
0268     }
0269 
0270     const bool elementIsEntry {!d->element.dynamicCast<const Entry>().isNull()};
0271     const PreviewStyle previewStyle = d->comboBox->itemData(d->comboBox->currentIndex()).value<PreviewStyle>();
0272 
0273     const QString exporterName = previewStyle.attributes.value(QStringLiteral("exporter"), QString()).toString();
0274     QScopedPointer<FileExporter> exporter([this, &previewStyle, &exporterName]() {
0275         if (exporterName == QStringLiteral("bibtex")) {
0276             FileExporterBibTeX *exporterBibTeX = new FileExporterBibTeX(this);
0277             exporterBibTeX->setEncoding(QStringLiteral("utf-8"));
0278             return qobject_cast<FileExporter *>(exporterBibTeX);
0279         } else if (exporterName == QStringLiteral("ris"))
0280             return qobject_cast<FileExporter * >(new FileExporterRIS(this));
0281         else if (exporterName == QStringLiteral("bibtex2html")) {
0282             FileExporterBibTeX2HTML *exporterBibTeX2HTML = new FileExporterBibTeX2HTML(this);
0283             exporterBibTeX2HTML->setLaTeXBibliographyStyle(previewStyle.attributes.value(QStringLiteral("bibtexstyle"), QString()).toString());
0284             return qobject_cast<FileExporter *>(exporterBibTeX2HTML);
0285         } else if (exporterName == QStringLiteral("xml")) {
0286             FileExporterXML *exporterXML = new FileExporterXML(this);
0287             exporterXML->setOutputStyle(previewStyle.attributes.value(QStringLiteral("style"), QVariant::fromValue(FileExporterXML::OutputStyle::XML_KBibTeX)).value<FileExporterXML::OutputStyle>());
0288             return qobject_cast<FileExporter *>(exporterXML);
0289         } else if (!exporterName.isEmpty())
0290             qCWarning(LOG_KBIBTEX_PROGRAM) << "Don't know how to handle exporter " << exporterName << " for preview style " << previewStyle.label << "(" << previewStyle.id << ")";
0291 
0292         return static_cast<FileExporter *>(nullptr);
0293     }());
0294 
0295     QString text;
0296     bool exporterResult = false;
0297     bool textIsErrorMessage = false;
0298     if (!exporter.isNull()) {
0299         const bool externalProgramInvocation {previewStyle.attributes.value(QStringLiteral("externalprogram"), false).toBool()};
0300         if (externalProgramInvocation) {
0301             // Exporter may invoke external programs which take time, so set the busy cursor
0302             QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
0303         }
0304         QBuffer buffer(this);
0305         buffer.open(QBuffer::WriteOnly);
0306         exporterResult = exporter->save(&buffer, d->element, d->file);
0307         buffer.close();
0308         if (externalProgramInvocation)
0309             QApplication::restoreOverrideCursor();
0310 
0311         buffer.open(QBuffer::ReadOnly);
0312         text = QString::fromUtf8(buffer.readAll().constData()).trimmed();
0313         buffer.close();
0314     } else {
0315         text = d->errorMessageInnerTemplate.arg(i18nc("No exporter clould be located to generate output", "No output generated"));
0316         textIsErrorMessage = true;
0317     }
0318 
0319     // Remove comments
0320     int p = 0;
0321     while ((p = text.indexOf(QStringLiteral("<!--"), p)) >= 0) {
0322         const int p2 = text.indexOf(QStringLiteral("-->"), p + 3);
0323         if (p2 > p) {
0324             text = text.left(p).trimmed() + text.mid(p2 + 3).trimmed();
0325         } else
0326             break;
0327     }
0328 
0329     if (exporterName == QStringLiteral("bibtex")) {
0330         // Remove special comments from BibTeX code
0331         int xsomethingpos = 0;
0332         while ((xsomethingpos = text.indexOf(QStringLiteral("@comment{x-"), xsomethingpos)) >= 0) {
0333             int endofxsomethingpos = text.indexOf(QStringLiteral("}"), xsomethingpos + 11);
0334             if (endofxsomethingpos > xsomethingpos) {
0335                 // Trim empty lines around match
0336                 while (xsomethingpos > 0 && text[xsomethingpos - 1] == QLatin1Char('\n')) --xsomethingpos;
0337                 while (endofxsomethingpos + 1 < text.length() && text[endofxsomethingpos + 1] == QLatin1Char('\n')) ++endofxsomethingpos;
0338                 // Clip comment out of text
0339                 text = text.left(xsomethingpos) + text.mid(endofxsomethingpos + 1);
0340             }
0341         }
0342     } else if (exporterName == QStringLiteral("bibtex2html")) {
0343         // Remove bibtex2html's output after the horizontal line
0344         const int hrPos = text.indexOf(QStringLiteral("<hr"));
0345         if (hrPos >= 0)
0346             text = text.left(hrPos);
0347 
0348         // Remove various HTML artifacts
0349         static const QSet<const QRegularExpression> toBeRemovedSet{QRegularExpression(QStringLiteral("<[/]?(font)[^>]*>")), QRegularExpression(QStringLiteral("^.*?<td.*?</td.*?<td>")), QRegularExpression(QStringLiteral("</td>.*$")), QRegularExpression(QStringLiteral("\\[ <a.*?</a> \\]"))};
0350         for (const auto &toBeRemoved : toBeRemovedSet)
0351             text.remove(toBeRemoved);
0352     }
0353 
0354     if ((!exporterResult || text.isEmpty()) && !elementIsEntry) {
0355         // Some exporters do not handle things like macros or preamble.
0356         // Assume that this is the case here and provide an approriate error message for that
0357         text = d->errorMessageInnerTemplate.arg(i18n("Cannot show a preview for this type of element"));
0358         textIsErrorMessage = true;
0359     }
0360 
0361     if (!textIsErrorMessage && previewStyle.attributes.value(QStringLiteral("fixed-font"), false).toBool()) {
0362         // Preview text gets formatted as verbatim text in monospaced font
0363         text.prepend(QStringLiteral("';\">"));
0364         text.prepend(QFontDatabase::systemFont(QFontDatabase::FixedFont).family());
0365         text.prepend(QStringLiteral("<pre style=\"font-family: '"));
0366         text.append(QStringLiteral("</pre>"));
0367     } else if (!textIsErrorMessage && previewStyle.attributes.value(QStringLiteral("html"), false).toBool()) {
0368         // Remove existing header up to and including <body>
0369         int p = text.indexOf(QStringLiteral("<body"));
0370         if (p >= 0) {
0371             p = text.indexOf(QStringLiteral(">"), p + 4);
0372             if (p >= 0)
0373                 text = text.mid(p + 1).trimmed();
0374         }
0375         // Remove existing footer starting with </body>
0376         p = text.indexOf(QStringLiteral("</body"));
0377         if (p >= 0)
0378             text = text.left(p).trimmed();
0379     }
0380 
0381     if (previewStyle.attributes.value(QStringLiteral("style"), QVariant::fromValue(FileExporterXML::OutputStyle::XML_KBibTeX)).value<FileExporterXML::OutputStyle>() == FileExporterXML::OutputStyle::Plain_WikipediaCite)
0382         text.remove(QStringLiteral("\n")).replace(QStringLiteral("| "), QStringLiteral("|"));
0383     else if (previewStyle.attributes.value(QStringLiteral("style"), QVariant::fromValue(FileExporterXML::OutputStyle::XML_KBibTeX)).value<FileExporterXML::OutputStyle>() == FileExporterXML::OutputStyle::HTML_AbstractOnly) {
0384         if (text.isEmpty()) {
0385             text = d->errorMessageInnerTemplate.arg(i18nc("Cannot show a reference preview as entry does not contain an abstract", "No abstract"));
0386             textIsErrorMessage = true;
0387         }
0388     }
0389 
0390     // Beautify text
0391     text.replace(QStringLiteral("``"), QStringLiteral("&ldquo;"));
0392     text.replace(QStringLiteral("''"), QStringLiteral("&rdquo;"));
0393     static const QRegularExpression openingSingleQuotationRegExp(QStringLiteral("(^|[> ,.;:!?])`(\\S)"));
0394     static const QRegularExpression closingSingleQuotationRegExp(QStringLiteral("(\\S)'([ ,.;:!?<]|$)"));
0395     text.replace(openingSingleQuotationRegExp, QStringLiteral("\\1&lsquo;\\2"));
0396     text.replace(closingSingleQuotationRegExp, QStringLiteral("\\1&rsquo;\\2"));
0397 
0398     // Replace ASCII art with Unicode characters
0399     text.replace(QStringLiteral("---"), QString(QChar(0x2014)));
0400     text.replace(QStringLiteral("--"), QString(QChar(0x2013)));
0401 
0402     // Add common HTML start and end
0403     text.prepend(d->htmlStart);
0404     text.append(QStringLiteral("</body></html>"));
0405 
0406     d->setHtml(text, !textIsErrorMessage);
0407     d->saveState();
0408 }
0409 
0410 void ReferencePreview::openAsHTML()
0411 {
0412     QTemporaryFile file(QStandardPaths::writableLocation(QStandardPaths::TempLocation) + QDir::separator() + QStringLiteral("referencePreview-openAsHTML-XXXXXX.html"));
0413     file.setAutoRemove(false); /// let file stay alive for browser
0414     d->saveHTML(file);
0415 
0416     /// Ask KDE subsystem to open url in viewer matching mime type
0417     const QUrl url{QUrl::fromLocalFile(file.fileName())};
0418 #if KIO_VERSION < QT_VERSION_CHECK(5, 71, 0)
0419     KRun::runUrl(url, QStringLiteral("text/html"), this, KRun::RunFlags());
0420 #else // KIO_VERSION < QT_VERSION_CHECK(5, 71, 0) // >= 5.71.0
0421     KIO::OpenUrlJob *job = new KIO::OpenUrlJob(url, QStringLiteral("text/html"));
0422 #if KIO_VERSION < QT_VERSION_CHECK(5, 98, 0) // < 5.98.0
0423     job->setUiDelegate(new KIO::JobUiDelegate());
0424 #else // KIO_VERSION < QT_VERSION_CHECK(5, 98, 0) // >= 5.98.0
0425     job->setUiDelegate(KIO::createDefaultJobUiDelegate(KJobUiDelegate::AutoHandlingEnabled, this));
0426 #endif // KIO_VERSION < QT_VERSION_CHECK(5, 98, 0)
0427     job->start();
0428 #endif // KIO_VERSION < QT_VERSION_CHECK(5, 71, 0)
0429 }
0430 
0431 void ReferencePreview::saveAsHTML()
0432 {
0433     QPointer<QFileDialog> dlg = new QFileDialog(this, i18n("Save as HTML"));
0434     dlg->setMimeTypeFilters({QStringLiteral("text/html")});
0435     const int result = dlg->exec();
0436     QUrl url;
0437     if (result == QDialog::Accepted && !dlg->selectedUrls().isEmpty() && (url = dlg->selectedUrls().first()).isValid())
0438         d->saveHTML(url);
0439 }
0440 
0441 void ReferencePreview::linkClicked(const QUrl &url)
0442 {
0443     QString text = url.toDisplayString();
0444     if (text.startsWith(QStringLiteral("kbibtex:filter:"))) {
0445         text = text.mid(15);
0446         if (d->fileView != nullptr) {
0447             int p = text.indexOf(QStringLiteral("="));
0448             SortFilterFileModel::FilterQuery fq;
0449             fq.terms << text.mid(p + 1);
0450             fq.combination = SortFilterFileModel::FilterCombination::EveryTerm;
0451             fq.field = text.left(p);
0452             fq.searchPDFfiles = false;
0453             d->fileView->setFilterBarFilter(fq);
0454         }
0455     }
0456 }
0457 
0458 void ReferencePreview::setFileView(FileView *fileView)
0459 {
0460     d->fileView = fileView;
0461 }