File indexing completed on 2023-05-30 10:40:14
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"