File indexing completed on 2024-05-05 04:40:50

0001 /*
0002     SPDX-FileCopyrightText: 2010 Benjamin Port <port.benjamin@gmail.com>
0003     SPDX-FileCopyrightText: 2014 Kevin Funk <kfunk@kde.org>
0004     SPDX-FileCopyrightText: 2016 Andreas Cord-Landwehr <cordlandwehr@kde.org>
0005 
0006     SPDX-License-Identifier: LGPL-2.0-or-later
0007 */
0008 
0009 #include "qthelpconfig.h"
0010 
0011 #include <QHelpEngineCore>
0012 #include <QToolButton>
0013 #include <QHeaderView>
0014 #include <QPointer>
0015 
0016 #include <KMessageBox>
0017 #include <KLocalizedString>
0018 #include <KNSWidgets/Button>
0019 #include <kio_version.h>
0020 
0021 #include "ui_qthelpconfig.h"
0022 #include "ui_qthelpconfigeditdialog.h"
0023 #include "qthelp_config_shared.h"
0024 #include "debug.h"
0025 #include "qthelpplugin.h"
0026 
0027 enum Column
0028 {
0029     NameColumn,
0030     PathColumn,
0031     IconColumn,
0032     GhnsColumn,
0033     ConfigColumn
0034 };
0035 
0036 class QtHelpConfigEditDialog : public QDialog, public Ui_QtHelpConfigEditDialog
0037 {
0038     Q_OBJECT
0039 public:
0040     explicit QtHelpConfigEditDialog(QTreeWidgetItem* modifiedItem, QtHelpConfig* parent = nullptr)
0041         : QDialog(parent)
0042         , m_modifiedItem(modifiedItem)
0043         , m_config(parent)
0044     {
0045         setupUi(this);
0046 #if KIO_VERSION >= QT_VERSION_CHECK(5, 108, 0)
0047         qchRequester->setNameFilter(i18n("Qt Compressed Help Files") + QLatin1String(" (*.qch)"));
0048 #else
0049         qchRequester->setFilter(QLatin1String("*.qch|") + i18n("Qt Compressed Help Files"));
0050 #endif
0051 
0052         if (modifiedItem) {
0053             setWindowTitle(i18nc("@title:window", "Modify Entry"));
0054         } else {
0055             setWindowTitle(i18nc("@title:window", "Add New Entry"));
0056         }
0057         qchIcon->setIcon(QStringLiteral("qtlogo"));
0058     }
0059 
0060     bool checkQtHelpFile();
0061 
0062     void accept() override;
0063 
0064 private:
0065     QTreeWidgetItem* m_modifiedItem;
0066     QtHelpConfig* m_config;
0067 };
0068 
0069 bool QtHelpConfigEditDialog::checkQtHelpFile()
0070 {
0071     //verify if the file is valid and if there is a name
0072     if(qchName->text().isEmpty()){
0073         KMessageBox::error(this, i18n("Name cannot be empty."));
0074         return false;
0075     }
0076 
0077     return m_config->checkNamespace(qchRequester->text(), m_modifiedItem);
0078 }
0079 
0080 void QtHelpConfigEditDialog::accept()
0081 {
0082     if (!checkQtHelpFile())
0083         return;
0084 
0085     QDialog::accept();
0086 }
0087 
0088 QtHelpConfig::QtHelpConfig(QtHelpPlugin* plugin, QWidget *parent)
0089     : KDevelop::ConfigPage(plugin, nullptr, parent)
0090 {
0091     m_configWidget = new Ui::QtHelpConfigUI;
0092     m_configWidget->setupUi(this);
0093     m_configWidget->addButton->setIcon(QIcon::fromTheme(QStringLiteral("list-add")));
0094     connect(m_configWidget->addButton, &QPushButton::clicked, this, &QtHelpConfig::add);
0095 
0096     // Table
0097     m_configWidget->qchTable->setColumnHidden(IconColumn, true);
0098     m_configWidget->qchTable->setColumnHidden(GhnsColumn, true);
0099     m_configWidget->qchTable->model()->setHeaderData(ConfigColumn, Qt::Horizontal, QVariant());
0100     m_configWidget->qchTable->header()->setSectionsMovable(false);
0101     m_configWidget->qchTable->header()->setStretchLastSection(false);
0102     m_configWidget->qchTable->header()->setSectionResizeMode(NameColumn, QHeaderView::Stretch);
0103     m_configWidget->qchTable->header()->setSectionResizeMode(PathColumn, QHeaderView::Stretch);
0104     m_configWidget->qchTable->header()->setSectionResizeMode(ConfigColumn, QHeaderView::Fixed);
0105 
0106     // Add GHNS button
0107     auto* knsButton = new KNSWidgets::Button(
0108         i18nc("@action:button Allow user to get some API documentation with GHNS", "Get New Documentation"),
0109         QStringLiteral("kdevelop-qthelp.knsrc"), m_configWidget->boxQchManage);
0110     m_configWidget->tableCtrlLayout->insertWidget(1, knsButton);
0111     connect(knsButton, &KNSWidgets::Button::dialogFinished, this, &QtHelpConfig::knsUpdate);
0112     connect(m_configWidget->loadQtDocsCheckBox, &QCheckBox::toggled,
0113             this, QOverload<>::of(&QtHelpConfig::changed));
0114     m_configWidget->qchSearchDir->setMode(KFile::Directory);
0115     connect(m_configWidget->qchSearchDir, &KUrlRequester::textChanged,
0116             this, &QtHelpConfig::changed);
0117 
0118     // Set availability information for QtHelp
0119     m_configWidget->messageAvailabilityQtDocs->setCloseButtonVisible(false);
0120     if(plugin->isQtHelpAvailable()) {
0121         m_configWidget->messageAvailabilityQtDocs->setVisible(false);
0122     } else {
0123         m_configWidget->messageAvailabilityQtDocs->setText(
0124             i18n("The command \"qmake -query\" could not provide a path to a QtHelp file (QCH)."));
0125         m_configWidget->loadQtDocsCheckBox->setVisible(false);
0126     }
0127     reset();
0128 }
0129 
0130 QtHelpConfig::~QtHelpConfig()
0131 {
0132     delete m_configWidget;
0133 }
0134 
0135 KDevelop::ConfigPage::ConfigPageType QtHelpConfig::configPageType() const
0136 {
0137     return KDevelop::ConfigPage::DocumentationConfigPage;
0138 }
0139 
0140 void QtHelpConfig::apply()
0141 {
0142     QStringList iconList, nameList, pathList, ghnsList;
0143     for (int i = 0; i < m_configWidget->qchTable->topLevelItemCount(); i++) {
0144         const QTreeWidgetItem* item = m_configWidget->qchTable->topLevelItem(i);
0145         nameList << item->text(0);
0146         pathList << item->text(1);
0147         iconList << item->text(2);
0148         ghnsList << item->text(3);
0149     }
0150     QString searchDir = m_configWidget->qchSearchDir->text();
0151     bool loadQtDoc = m_configWidget->loadQtDocsCheckBox->isChecked();
0152 
0153     qtHelpWriteConfig(iconList, nameList, pathList, ghnsList, searchDir, loadQtDoc);
0154     static_cast<QtHelpPlugin*>(plugin())->readConfig();
0155 }
0156 
0157 void QtHelpConfig::reset()
0158 {
0159     m_configWidget->qchTable->clear();
0160 
0161     QStringList iconList, nameList, pathList, ghnsList;
0162     QString searchDir;
0163     bool loadQtDoc;
0164     qtHelpReadConfig(iconList, nameList, pathList, ghnsList, searchDir, loadQtDoc);
0165 
0166     const int size = qMin(qMin(iconList.size(), nameList.size()), pathList.size());
0167     for(int i = 0; i < size; ++i) {
0168         QString ghnsStatus = ghnsList.size()>i ? ghnsList.at(i) : QStringLiteral("0");
0169         addTableItem(iconList.at(i), nameList.at(i), pathList.at(i), ghnsStatus);
0170     }
0171     m_configWidget->qchSearchDir->setText(searchDir);
0172     m_configWidget->loadQtDocsCheckBox->setChecked(loadQtDoc);
0173 
0174     emit changed();
0175 }
0176 
0177 void QtHelpConfig::defaults()
0178 {
0179     bool change = false;
0180     if(m_configWidget->qchTable->topLevelItemCount() > 0) {
0181         m_configWidget->qchTable->clear();
0182         change = true;
0183     }
0184     if(!m_configWidget->loadQtDocsCheckBox->isChecked()){
0185         m_configWidget->loadQtDocsCheckBox->setChecked(true);
0186         change = true;
0187     }
0188 
0189     if (change) {
0190         emit changed();
0191     }
0192 }
0193 
0194 void QtHelpConfig::add()
0195 {
0196     QPointer<QtHelpConfigEditDialog> dialog = new QtHelpConfigEditDialog(nullptr, this);
0197     if (dialog->exec()) {
0198         QTreeWidgetItem* item = addTableItem(dialog->qchIcon->icon(), dialog->qchName->text(), dialog->qchRequester->text(), QStringLiteral("0"));
0199         m_configWidget->qchTable->setCurrentItem(item);
0200         emit changed();
0201     }
0202     delete dialog;
0203 }
0204 
0205 void QtHelpConfig::modify(QTreeWidgetItem* item)
0206 {
0207     if (!item)
0208         return;
0209 
0210     QPointer<QtHelpConfigEditDialog> dialog = new QtHelpConfigEditDialog(item, this);
0211     if (item->text(GhnsColumn) != QLatin1String("0")) {
0212         dialog->qchRequester->setText(i18n("Documentation provided by GHNS"));
0213         dialog->qchRequester->setEnabled(false);
0214     } else {
0215         dialog->qchRequester->setText(item->text(PathColumn));
0216         dialog->qchRequester->setEnabled(true);
0217     }
0218     dialog->qchName->setText(item->text(NameColumn));
0219     dialog->qchIcon->setIcon(item->text(IconColumn));
0220     if (dialog->exec()) {
0221         item->setIcon(NameColumn, QIcon(dialog->qchIcon->icon()));
0222         item->setText(NameColumn, dialog->qchName->text());
0223         item->setText(IconColumn, dialog->qchIcon->icon());
0224         if(item->text(GhnsColumn) == QLatin1String("0")) {
0225             item->setText(PathColumn, dialog->qchRequester->text());
0226         }
0227 
0228         emit changed();
0229     }
0230     delete dialog;
0231 }
0232 
0233 bool QtHelpConfig::checkNamespace(const QString& filename, QTreeWidgetItem* modifiedItem)
0234 {
0235     QString qtHelpNamespace = QHelpEngineCore::namespaceName(filename);
0236     if (qtHelpNamespace.isEmpty()) {
0237         // Open error message (not valid Qt Compressed Help file)
0238         KMessageBox::error(this, i18n("Qt Compressed Help file is not valid."));
0239         return false;
0240     }
0241     // verify if it's the namespace it's not already in the list
0242     for(int i=0; i < m_configWidget->qchTable->topLevelItemCount(); i++) {
0243         const QTreeWidgetItem* item = m_configWidget->qchTable->topLevelItem(i);
0244         if (item != modifiedItem){
0245             if (qtHelpNamespace == QHelpEngineCore::namespaceName(item->text(PathColumn))) {
0246                 // Open error message, documentation already imported
0247                 KMessageBox::error(this, i18n("Documentation already imported"));
0248                 return false;
0249             }
0250         }
0251     }
0252     return true;
0253 }
0254 
0255 void QtHelpConfig::remove(QTreeWidgetItem* item)
0256 {
0257     if (!item)
0258         return;
0259 
0260     delete item;
0261     emit changed();
0262 }
0263 
0264 void QtHelpConfig::knsUpdate(const QList<KNSCore::Entry>& list)
0265 {
0266     if (list.isEmpty())
0267         return;
0268 
0269     for (const auto& e : list) {
0270         if(e.status() == KNS3::Entry::Installed) {
0271             // For zipped/tarred QCH fules KNewStuff also adds the directory as installed file, first file entry is assumed to be QCH file though
0272             if (e.installedFiles().size() >= 1) {
0273                 QString filename = e.installedFiles().at(0);
0274                 if(checkNamespace(filename, nullptr)){
0275                     QTreeWidgetItem* item = addTableItem(QStringLiteral("documentation"), e.name(), filename, QStringLiteral("1"));
0276                     m_configWidget->qchTable->setCurrentItem(item);
0277                 } else {
0278                     qCDebug(QTHELP) << "namespace error";
0279                 }
0280             }
0281         } else if(e.status() ==  KNS3::Entry::Deleted) {
0282             // cmp. note above for installed files
0283             if (e.uninstalledFiles().size() >= 1) {
0284                 for(int i=0; i < m_configWidget->qchTable->topLevelItemCount(); i++) {
0285                     QTreeWidgetItem* item = m_configWidget->qchTable->topLevelItem(i);
0286                     if (e.uninstalledFiles().at(0) == item->text(PathColumn)) {
0287                         delete item;
0288                         break;
0289                     }
0290                 }
0291             }
0292         }
0293     }
0294     emit changed();
0295 }
0296 
0297 QString QtHelpConfig::name() const
0298 {
0299     return i18nc("@title:tab", "Qt Help");
0300 }
0301 
0302 QString QtHelpConfig::fullName() const
0303 {
0304     return i18nc("@title:tab", "Configure Qt Help Settings");
0305 }
0306 
0307 QIcon QtHelpConfig::icon() const
0308 {
0309     return QIcon::fromTheme(QStringLiteral("qtlogo"));
0310 }
0311 
0312 QTreeWidgetItem * QtHelpConfig::addTableItem(const QString &icon, const QString &name,
0313                                              const QString &path, const QString &ghnsStatus)
0314 {
0315     auto *item = new QTreeWidgetItem(m_configWidget->qchTable);
0316     item->setIcon(NameColumn, QIcon::fromTheme(icon));
0317     item->setText(NameColumn, name);
0318     item->setToolTip(NameColumn, name);
0319     item->setText(PathColumn, path);
0320     item->setToolTip(PathColumn, path);
0321     item->setText(IconColumn, icon);
0322     item->setText(GhnsColumn, ghnsStatus);
0323 
0324     auto* ctrlWidget = new QWidget(item->treeWidget());
0325     ctrlWidget->setLayout(new QHBoxLayout(ctrlWidget));
0326 
0327     auto *modifyBtn = new QToolButton(item->treeWidget());
0328     modifyBtn->setIcon(QIcon::fromTheme(QStringLiteral("document-edit")));
0329     modifyBtn->setToolTip(i18nc("@info:tooltip", "Modify"));
0330     connect(modifyBtn, &QPushButton::clicked, this, [=](){
0331         modify(item);
0332     });
0333     auto *removeBtn = new QToolButton(item->treeWidget());
0334     removeBtn->setIcon(QIcon::fromTheme(QStringLiteral("entry-delete")));
0335     removeBtn->setToolTip(i18nc("@info:tooltip", "Delete"));
0336     if (item->text(GhnsColumn) != QLatin1String("0")) {
0337         // KNS3 currently does not provide API to uninstall entries
0338         // just removing the files results in wrong installed states in the KNS3 dialog
0339         // TODO: add API to KNS to remove files without UI interaction
0340         removeBtn->setEnabled(false);
0341         removeBtn->setToolTip(i18nc("@info:tooltip", "Please uninstall this via GHNS."));
0342     } else {
0343         connect(removeBtn, &QPushButton::clicked, this, [=](){
0344             remove(item);
0345         });
0346     }
0347     ctrlWidget->layout()->addWidget(modifyBtn);
0348     ctrlWidget->layout()->addWidget(removeBtn);
0349     m_configWidget->qchTable->setItemWidget(item, ConfigColumn, ctrlWidget);
0350 
0351     return item;
0352 }
0353 
0354 #include "qthelpconfig.moc"
0355 #include "moc_qthelpconfig.cpp"