File indexing completed on 2024-04-28 11:20:43

0001 /*
0002     SPDX-License-Identifier: GPL-2.0-or-later
0003     SPDX-FileCopyrightText: 2020 Shubham <aryan100jangid@gmail.com>
0004     SPDX-FileCopyrightText: 2020-2021 Alexander Semke <alexander.semke@web.de>
0005  */
0006 
0007 #include <QDebug>
0008 #include <QHeaderView>
0009 #include <QHelpEngineCore>
0010 #include <QPointer>
0011 #include <QToolButton>
0012 
0013 #include <KConfigGroup>
0014 #include <KLocalizedString>
0015 #include <KMessageBox>
0016 #include <KNSWidgets/Button>
0017 #include <KSharedConfig>
0018 
0019 #include "qthelpconfig.h"
0020 
0021 #include "ui_qthelpconfigeditdialog.h"
0022 #include "ui_qthelpconfig.h"
0023 
0024 enum Column
0025 {
0026     NameColumn,
0027     PathColumn,
0028     IconColumn,
0029     GhnsColumn,
0030     ConfigColumn
0031 };
0032 
0033 class QtHelpConfigEditDialog : public QDialog, public Ui_QtHelpConfigEditDialog
0034 {
0035     Q_OBJECT
0036 public:
0037     explicit QtHelpConfigEditDialog(QTreeWidgetItem* modifiedItem, QtHelpConfig* parent = nullptr)
0038         : QDialog(parent)
0039         , m_modifiedItem(modifiedItem)
0040         , m_config(parent)
0041     {
0042         setupUi(this);
0043 
0044         if (modifiedItem)
0045             setWindowTitle(i18nc("@title:window", "Modify Entry"));
0046         else
0047             setWindowTitle(i18nc("@title:window", "Add New Entry"));
0048 
0049         qchIcon->setIcon(QStringLiteral("qtlogo"));
0050     }
0051 
0052     bool checkQtHelpFile();
0053     void accept() override;
0054 
0055 private:
0056     QTreeWidgetItem* m_modifiedItem;
0057     QtHelpConfig* m_config;
0058 };
0059 
0060 bool QtHelpConfigEditDialog::checkQtHelpFile()
0061 {
0062     //verify if the file is valid and if there is a name
0063     if(qchName->text().isEmpty()){
0064         KMessageBox::error(this, i18n("Name cannot be empty."));
0065         return false;
0066     }
0067 
0068     return m_config->checkNamespace(qchRequester->text(), m_modifiedItem);
0069 }
0070 
0071 void QtHelpConfigEditDialog::accept()
0072 {
0073     if (!checkQtHelpFile())
0074         return;
0075 
0076     QDialog::accept();
0077 }
0078 
0079 QtHelpConfig::QtHelpConfig(const QString& backend) : QWidget(), m_backend(backend)
0080 {
0081     auto* ui = new Ui::QtHelpConfigUI;
0082     ui->setupUi(this);
0083     ui->addButton->setIcon(QIcon::fromTheme(QStringLiteral("list-add")));
0084     ui->addButton->setToolTip(i18n("Add local documentation"));
0085     connect(ui->addButton, &QPushButton::clicked, this, &QtHelpConfig::add);
0086 
0087     m_treeWidget = ui->qchTable;
0088 
0089     // Table
0090     m_treeWidget->setColumnHidden(IconColumn, true);
0091     m_treeWidget->setColumnHidden(GhnsColumn, true);
0092     m_treeWidget->model()->setHeaderData(ConfigColumn, Qt::Horizontal, QVariant());
0093     m_treeWidget->header()->setSectionsMovable(false);
0094     m_treeWidget->header()->setStretchLastSection(false);
0095     m_treeWidget->header()->setSectionResizeMode(NameColumn, QHeaderView::ResizeToContents);
0096     m_treeWidget->header()->setSectionResizeMode(PathColumn, QHeaderView::Stretch);
0097     m_treeWidget->header()->setSectionResizeMode(ConfigColumn, QHeaderView::Fixed);
0098 
0099     // Add GHNS button
0100     auto* knsButton = new KNSWidgets::Button(i18nc("@action:button Allow user to get some API documentation with GHNS", "Get New Documentation"),
0101                                        QStringLiteral("cantor-documentation.knsrc"),
0102                                        this);
0103     knsButton->setToolTip(i18n("Download additional documentations"));
0104     ui->tableCtrlLayout->insertWidget(1, knsButton);
0105     connect(knsButton, &KNSWidgets::Button::dialogFinished, this, &QtHelpConfig::knsUpdate);
0106 
0107     connect(this, &QtHelpConfig::settingsChanged, this, &QtHelpConfig::saveSettings);
0108 
0109     // load settings for Install Additional Help Files widget
0110     loadSettings();
0111 }
0112 
0113 QtHelpConfig::~QtHelpConfig() = default;
0114 
0115 void QtHelpConfig::add()
0116 {
0117     QPointer<QtHelpConfigEditDialog> dialog = new QtHelpConfigEditDialog(nullptr, this);
0118     if (dialog->exec()) {
0119         auto* item = addTableItem(dialog->qchIcon->icon(),
0120                                   dialog->qchName->text(),
0121                                   dialog->qchRequester->text(),
0122                                   QStringLiteral("0"));
0123         m_treeWidget->setCurrentItem(item);
0124         emit settingsChanged();
0125     }
0126 
0127     delete dialog;
0128 }
0129 
0130 void QtHelpConfig::modify(QTreeWidgetItem* item)
0131 {
0132     if (!item)
0133         return;
0134 
0135     QPointer<QtHelpConfigEditDialog> dialog = new QtHelpConfigEditDialog(item, this);
0136 
0137     if (item->text(GhnsColumn) != QLatin1String("0"))
0138     {
0139         dialog->qchRequester->hide();
0140         dialog->lPath->hide();
0141         //resize the dialog to fit the content after the widgets were hidden
0142         dialog->layout()->activate();
0143         dialog->resize( QSize(dialog->width(), 0).expandedTo(dialog->minimumSize()) );
0144     }
0145     else
0146     {
0147         dialog->qchRequester->setText(item->text(PathColumn));
0148         dialog->qchRequester->setEnabled(true);
0149     }
0150 
0151     dialog->qchName->setText(item->text(NameColumn));
0152     dialog->qchIcon->setIcon(item->text(IconColumn));
0153 
0154     if (dialog->exec()) {
0155         item->setIcon(NameColumn, QIcon(dialog->qchIcon->icon()));
0156         item->setText(NameColumn, dialog->qchName->text());
0157         item->setText(IconColumn, dialog->qchIcon->icon());
0158         if(item->text(GhnsColumn) == QLatin1String("0"))
0159             item->setText(PathColumn, dialog->qchRequester->text());
0160 
0161         emit settingsChanged();
0162     }
0163 
0164     delete dialog;
0165 }
0166 
0167 bool QtHelpConfig::checkNamespace(const QString& filename, QTreeWidgetItem* modifiedItem)
0168 {
0169     QString qtHelpNamespace = QHelpEngineCore::namespaceName(filename);
0170     if (qtHelpNamespace.isEmpty()) {
0171         KMessageBox::error(this, i18n("Qt Compressed Help file is not valid."));
0172         return false;
0173     }
0174 
0175     // verify if it's the namespace it's not already in the list
0176     for(int i=0; i < m_treeWidget->topLevelItemCount(); i++) {
0177         const auto* item = m_treeWidget->topLevelItem(i);
0178         if (item != modifiedItem){
0179             if (qtHelpNamespace == QHelpEngineCore::namespaceName(item->text(PathColumn))) {
0180                 KMessageBox::error(this, i18n("Documentation already imported"));
0181                 return false;
0182             }
0183         }
0184     }
0185     return true;
0186 }
0187 
0188 void QtHelpConfig::remove(QTreeWidgetItem* item)
0189 {
0190     if (!item)
0191         return;
0192 
0193     delete item;
0194     emit settingsChanged();
0195 }
0196 
0197 void QtHelpConfig::knsUpdate(const QList<KNSCore::Entry>& list)
0198 {
0199     if (list.isEmpty())
0200         return;
0201 
0202     for (const auto& e : list)
0203     {
0204         if(e.status() == KNS3::Entry::Installed && e.installedFiles().size() == 1)
0205         {
0206             //we're downloading a zip archive and after unpacking KSN::Entry::installedFiles()
0207             //returns one single entry for the path with the wildcard standing for all file like in
0208             //"$HOME/.local/share/cantor/documentation/Maxima_v5.44/*"
0209             //we need to remove the wildcard and to determine the actual path for the qch.file
0210 
0211             //determine the path for the qch file
0212             QString qchPath;
0213             QString iconPath = QStringLiteral("documentation");
0214             QString path = e.installedFiles().at(0);
0215             path.chop(1);
0216             QDir dir(path);
0217             const auto& fileInfos = dir.entryInfoList();
0218             for (const auto& fileInfo : fileInfos)
0219             {
0220                 if (fileInfo.suffix() == QLatin1String("qch"))
0221                     qchPath = fileInfo.filePath();
0222 
0223                 if (fileInfo.suffix() == QLatin1String("svg"))
0224                     iconPath = fileInfo.filePath();
0225             }
0226 
0227             //add the qch file if valid
0228             if(checkNamespace(qchPath, nullptr))
0229             {
0230                 auto* item = addTableItem(iconPath, e.name(), qchPath, QStringLiteral("1"));
0231                 m_treeWidget->setCurrentItem(item);
0232             }
0233         }
0234         else if(e.status() ==  KNS3::Entry::Deleted && e.uninstalledFiles().size() > 0) {
0235             //determine the path for the qch file
0236             QString path = e.uninstalledFiles().at(0);
0237             path.chop(1);//remove '*' at the end
0238 
0239             //delete the corresponding item in the table
0240             for(int i=0; i < m_treeWidget->topLevelItemCount(); i++) {
0241                 const auto* item = m_treeWidget->topLevelItem(i);
0242                 if (item->text(PathColumn).startsWith(path)) {
0243                     delete item;
0244                     break;
0245                 }
0246             }
0247         }
0248     }
0249 
0250     emit settingsChanged();
0251 }
0252 
0253 QTreeWidgetItem* QtHelpConfig::addTableItem(const QString& icon, const QString& name,
0254                                              const QString& path, const QString& ghnsStatus)
0255 {
0256     auto* item = new QTreeWidgetItem(m_treeWidget);
0257     item->setIcon(NameColumn, QIcon::fromTheme(icon));
0258     item->setText(NameColumn, name);
0259     item->setToolTip(NameColumn, name);
0260     item->setText(PathColumn, path);
0261     item->setToolTip(PathColumn, path);
0262     item->setText(IconColumn, icon);
0263     item->setText(GhnsColumn, ghnsStatus);
0264 
0265     auto* ctrlWidget = new QWidget(item->treeWidget());
0266     ctrlWidget->setLayout(new QHBoxLayout(ctrlWidget));
0267 
0268     auto* modifyBtn = new QToolButton(item->treeWidget());
0269     modifyBtn->setIcon(QIcon::fromTheme(QStringLiteral("document-edit")));
0270     modifyBtn->setToolTip(i18nc("@info:tooltip", "Modify"));
0271     connect(modifyBtn, &QPushButton::clicked, this, [=](){ modify(item); });
0272 
0273     auto *removeBtn = new QToolButton(item->treeWidget());
0274     removeBtn->setIcon(QIcon::fromTheme(QStringLiteral("entry-delete")));
0275     removeBtn->setToolTip(i18nc("@info:tooltip", "Delete"));
0276 
0277     if (item->text(GhnsColumn) != QLatin1String("0"))
0278     {
0279         // KNS3 currently does not provide API to uninstall entries
0280         // just removing the files results in wrong installed states in the KNS3 dialog
0281         // TODO: add API to KNS to remove files without UI interaction
0282         removeBtn->setEnabled(false);
0283         removeBtn->setToolTip(i18nc("@info:tooltip", "Please uninstall this via GHNS."));
0284     } else
0285         connect(removeBtn, &QPushButton::clicked, this, [=](){ remove(item); });
0286 
0287     ctrlWidget->layout()->addWidget(modifyBtn);
0288     ctrlWidget->layout()->addWidget(removeBtn);
0289 
0290     m_treeWidget->setItemWidget(item, ConfigColumn, ctrlWidget);
0291 
0292     return item;
0293 }
0294 
0295 void QtHelpConfig::loadSettings()
0296 {
0297     // load settings for current backend and then update the QTreeWidget
0298     const auto& group = KSharedConfig::openConfig(QStringLiteral("cantorrc"))->group(m_backend);
0299 
0300     const auto& nameList = group.readEntry(QLatin1String("Names"), QStringList());
0301     const auto& pathList = group.readEntry(QLatin1String("Paths"), QStringList());
0302     const auto& iconList = group.readEntry(QLatin1String("Icons"), QStringList());
0303     const auto& ghnsList = group.readEntry(QLatin1String("Ghns"), QStringList());
0304 
0305     // iterate through Name Location pairs and update the QTreeWidget
0306     for(int i = 0; i < nameList.size(); i++)
0307     {
0308         QTreeWidgetItem* item = addTableItem(iconList.at(i), nameList.at(i), pathList.at(i), ghnsList.at(i));
0309         m_treeWidget->setCurrentItem(item);
0310     }
0311 }
0312 
0313 void QtHelpConfig::saveSettings()
0314 {
0315     // create seperate group for seperate backends
0316     auto group = KSharedConfig::openConfig(QStringLiteral("cantorrc"))->group(m_backend);
0317 
0318     QStringList nameList;
0319     QStringList pathList;
0320     QStringList iconList;
0321     QStringList ghnsList;
0322 
0323     for (int i = 0; i < m_treeWidget->topLevelItemCount(); i++)
0324     {
0325         const auto* item = m_treeWidget->topLevelItem(i);
0326         nameList << item->text(0);
0327         pathList << item->text(1);
0328         iconList << item->text(2);
0329         ghnsList << item->text(3);
0330     }
0331 
0332     group.writeEntry(QLatin1String("Names"), nameList);
0333     group.writeEntry(QLatin1String("Paths"), pathList);
0334     group.writeEntry(QLatin1String("Icons"), iconList);
0335     group.writeEntry(QLatin1String("Ghns"), ghnsList);
0336 }
0337 
0338 #include "qthelpconfig.moc"