File indexing completed on 2024-04-28 05:27:03
0001 /* This file is part of the KDE project 0002 SPDX-FileCopyrightText: 2003 Waldo Bastian <bastian@kde.org> 0003 SPDX-FileCopyrightText: 2003 David Faure <faure@kde.org> 0004 SPDX-FileCopyrightText: 2002 Daniel Molkentin <molkentin@kde.org> 0005 0006 SPDX-License-Identifier: GPL-2.0-only 0007 */ 0008 0009 // Own 0010 #include "kservicelistwidget.h" 0011 0012 // Qt 0013 #include <QHBoxLayout> 0014 #include <QPushButton> 0015 #include <QStandardPaths> 0016 #include <QVBoxLayout> 0017 0018 // KDE 0019 #include <KLocalizedString> 0020 #include <KMessageBox> 0021 #include <KOpenWithDialog> 0022 #include <KPropertiesDialog> 0023 0024 // Local 0025 #include "kserviceselectdlg.h" 0026 #include "mimetypedata.h" 0027 0028 KServiceListItem::KServiceListItem(const KService::Ptr &pService) 0029 : QListWidgetItem() 0030 , storageId(pService->storageId()) 0031 , desktopPath(pService->entryPath()) 0032 { 0033 setText(pService->name()); 0034 setIcon(QIcon::fromTheme(pService->icon())); 0035 0036 if (!pService->isApplication()) { 0037 localPath = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QStringLiteral("/kservices6/") + desktopPath; 0038 } else { 0039 localPath = pService->locateLocal(); 0040 } 0041 } 0042 0043 PluginListItem::PluginListItem(const KPluginMetaData &data) 0044 : QListWidgetItem() 0045 , metaData(data) 0046 { 0047 setText(i18n("%1 (%2)", metaData.name(), metaData.pluginId())); 0048 setIcon(QIcon::fromTheme(metaData.iconName())); 0049 } 0050 0051 KServiceListWidget::KServiceListWidget(Kind kind, QWidget *parent) 0052 : QGroupBox(kind == SERVICELIST_APPLICATIONS ? i18n("Application Preference Order") : i18n("Services Preference Order"), parent) 0053 , m_kind(kind) 0054 , m_mimeTypeData(nullptr) 0055 , m_allowMultiApply(false) 0056 { 0057 QHBoxLayout *lay = new QHBoxLayout(this); 0058 0059 servicesLB = new QListWidget(this); 0060 connect(servicesLB, &QListWidget::itemSelectionChanged, this, &KServiceListWidget::enableMoveButtons); 0061 lay->addWidget(servicesLB); 0062 connect(servicesLB, &QListWidget::itemDoubleClicked, this, &KServiceListWidget::editService); 0063 0064 QString wtstr = (kind == SERVICELIST_APPLICATIONS ? i18n("This is a list of applications associated with files of the selected" 0065 " file type. This list is shown in Konqueror's context menus when you select" 0066 " \"Open With...\". If more than one application is associated with this file type," 0067 " then the list is ordered by priority with the uppermost item taking precedence" 0068 " over the others.") 0069 : i18n("This is a list of services associated with files of the selected" 0070 " file type. This list is shown in Konqueror's context menus when you select" 0071 " a \"Preview with...\" option. If more than one service is associated with this file type," 0072 " then the list is ordered by priority with the uppermost item taking precedence" 0073 " over the others.")); 0074 0075 setWhatsThis(wtstr); 0076 servicesLB->setWhatsThis(wtstr); 0077 0078 QVBoxLayout *btnsLay = new QVBoxLayout(); 0079 lay->addLayout(btnsLay); 0080 0081 servUpButton = new QPushButton(i18n("Move &Up"), this); 0082 servUpButton->setIcon(QIcon::fromTheme(QStringLiteral("arrow-up"))); 0083 servUpButton->setEnabled(false); 0084 connect(servUpButton, &QAbstractButton::clicked, this, &KServiceListWidget::promoteService); 0085 btnsLay->addWidget(servUpButton); 0086 0087 servUpButton->setWhatsThis(kind == SERVICELIST_APPLICATIONS ? i18n("Assigns a higher priority to the selected\n" 0088 "application, moving it up in the list. Note: This\n" 0089 "only affects the selected application if the file type is\n" 0090 "associated with more than one application.") 0091 : i18n("Assigns a higher priority to the selected\n" 0092 "service, moving it up in the list.")); 0093 0094 servDownButton = new QPushButton(i18n("Move &Down"), this); 0095 servDownButton->setIcon(QIcon::fromTheme(QStringLiteral("arrow-down"))); 0096 servDownButton->setEnabled(false); 0097 connect(servDownButton, &QAbstractButton::clicked, this, &KServiceListWidget::demoteService); 0098 btnsLay->addWidget(servDownButton); 0099 servDownButton->setWhatsThis(kind == SERVICELIST_APPLICATIONS ? i18n("Assigns a lower priority to the selected\n" 0100 "application, moving it down in the list. Note: This \n" 0101 "only affects the selected application if the file type is\n" 0102 "associated with more than one application.") 0103 : i18n("Assigns a lower priority to the selected\n" 0104 "service, moving it down in the list.")); 0105 0106 servNewButton = new QPushButton(i18n("Add..."), this); 0107 servNewButton->setIcon(QIcon::fromTheme(QStringLiteral("list-add"))); 0108 servNewButton->setEnabled(false); 0109 connect(servNewButton, &QAbstractButton::clicked, this, &KServiceListWidget::addService); 0110 btnsLay->addWidget(servNewButton); 0111 servNewButton->setWhatsThis(i18n("Add a new application for this file type.")); 0112 0113 servEditButton = new QPushButton(i18n("Edit..."), this); 0114 servEditButton->setIcon(QIcon::fromTheme(QStringLiteral("edit-rename"))); 0115 servEditButton->setEnabled(false); 0116 connect(servEditButton, &QAbstractButton::clicked, this, &KServiceListWidget::editService); 0117 btnsLay->addWidget(servEditButton); 0118 servEditButton->setWhatsThis(i18n("Edit command line of the selected application.")); 0119 0120 servRemoveButton = new QPushButton(i18n("Remove"), this); 0121 servRemoveButton->setIcon(QIcon::fromTheme(QStringLiteral("list-remove"))); 0122 servRemoveButton->setEnabled(false); 0123 connect(servRemoveButton, &QAbstractButton::clicked, this, &KServiceListWidget::removeService); 0124 btnsLay->addWidget(servRemoveButton); 0125 servRemoveButton->setWhatsThis(i18n("Remove the selected application from the list.")); 0126 0127 servApplyToButton = new QPushButton(i18n("Apply To..."), this); 0128 servApplyToButton->setIcon(QIcon::fromTheme(QStringLiteral("edit-copy"))); 0129 servApplyToButton->setEnabled(false); 0130 connect(servApplyToButton, &QAbstractButton::clicked, this, &KServiceListWidget::applyTo); 0131 btnsLay->addWidget(servApplyToButton); 0132 servApplyToButton->setWhatsThis(i18n("Apply the current preference order to other file types.")); 0133 0134 btnsLay->addStretch(1); 0135 } 0136 0137 void KServiceListWidget::setMimeTypeData(MimeTypeData *mimeTypeData) 0138 { 0139 m_mimeTypeData = mimeTypeData; 0140 if (servNewButton) { 0141 servNewButton->setEnabled(true); 0142 } 0143 // will need a selection 0144 servUpButton->setEnabled(false); 0145 servDownButton->setEnabled(false); 0146 0147 servicesLB->clear(); 0148 servicesLB->setEnabled(false); 0149 0150 if (m_mimeTypeData) { 0151 if (m_kind == SERVICELIST_APPLICATIONS) { 0152 const QStringList services = m_mimeTypeData->appServices(); 0153 if (services.isEmpty()) { 0154 if (m_kind == SERVICELIST_APPLICATIONS) { 0155 servicesLB->addItem(i18nc("No applications associated with this file type", "None")); 0156 } 0157 } else { 0158 for (const QString &service : services) { 0159 if (KService::Ptr pService = KService::serviceByStorageId(service)) { 0160 servicesLB->addItem(new KServiceListItem(pService)); 0161 } 0162 } 0163 servicesLB->setEnabled(true); 0164 } 0165 } else { 0166 const QStringList parts = m_mimeTypeData->embedParts(); 0167 if (parts.isEmpty()) { 0168 servicesLB->addItem(new QListWidgetItem(i18nc("No components associated with this file type", "None"), nullptr, -1)); 0169 } else { 0170 servicesLB->setEnabled(true); 0171 for (const QString &partId : parts) { 0172 if (KPluginMetaData data(QStringLiteral("kf6/parts/") + partId); data.isValid()) { 0173 servicesLB->addItem(new PluginListItem(data)); 0174 } 0175 } 0176 } 0177 } 0178 } 0179 0180 if (servRemoveButton) { 0181 servRemoveButton->setEnabled(servicesLB->currentRow() > -1); 0182 } 0183 if (servEditButton) { 0184 servEditButton->setEnabled(servicesLB->currentRow() > -1); 0185 } 0186 if (servApplyToButton) { 0187 servApplyToButton->setEnabled(m_allowMultiApply); 0188 } 0189 } 0190 0191 void KServiceListWidget::allowMultiApply(bool allow) 0192 { 0193 m_allowMultiApply = allow; 0194 0195 if (m_mimeTypeData && servApplyToButton) { 0196 servApplyToButton->setEnabled(m_allowMultiApply); 0197 } 0198 } 0199 0200 void KServiceListWidget::promoteService() 0201 { 0202 if (!servicesLB->isEnabled()) { 0203 return; 0204 } 0205 0206 int selIndex = servicesLB->currentRow(); 0207 if (selIndex == 0) { 0208 return; 0209 } 0210 0211 QListWidgetItem *selItem = servicesLB->item(selIndex); 0212 servicesLB->takeItem(selIndex); 0213 servicesLB->insertItem(selIndex - 1, selItem); 0214 servicesLB->setCurrentRow(selIndex - 1); 0215 0216 updatePreferredServices(); 0217 0218 Q_EMIT changed(true); 0219 } 0220 0221 void KServiceListWidget::demoteService() 0222 { 0223 if (!servicesLB->isEnabled()) { 0224 return; 0225 } 0226 0227 int selIndex = servicesLB->currentRow(); 0228 if (selIndex == servicesLB->count() - 1) { 0229 return; 0230 } 0231 0232 QListWidgetItem *selItem = servicesLB->item(selIndex); 0233 servicesLB->takeItem(selIndex); 0234 servicesLB->insertItem(selIndex + 1, selItem); 0235 servicesLB->setCurrentRow(selIndex + 1); 0236 0237 updatePreferredServices(); 0238 0239 Q_EMIT changed(true); 0240 } 0241 0242 void KServiceListWidget::addService() 0243 { 0244 if (!m_mimeTypeData) { 0245 return; 0246 } 0247 0248 if (m_kind == SERVICELIST_APPLICATIONS) { 0249 KOpenWithDialog dlg(m_mimeTypeData->name(), QString(), this); 0250 dlg.setSaveNewApplications(true); 0251 if (dlg.exec() != QDialog::Accepted) { 0252 return; 0253 } 0254 0255 KService::Ptr service = dlg.service(); 0256 0257 Q_ASSERT(service); 0258 if (!service) { 0259 return; // Don't crash if KOpenWith wasn't able to create service. 0260 } 0261 if (m_mimeTypeData->appServices().contains(service->storageId())) { 0262 return; 0263 } 0264 servicesLB->insertItem(0, new KServiceListItem(service)); 0265 } else { 0266 KPartSelectDlg dlg(this); 0267 if (dlg.exec() != QDialog::Accepted) { 0268 return; 0269 } 0270 const bool valid = dlg.chosenPart().isValid(); 0271 Q_ASSERT(valid); 0272 if (m_mimeTypeData->embedParts().contains(dlg.chosenPart().pluginId())) { 0273 return; 0274 } 0275 auto item = new PluginListItem(dlg.chosenPart()); 0276 servicesLB->insertItem(0, item); 0277 } 0278 0279 // We inserted a new service at the beginning, if we had a None entry before, it must 0280 // be the second on in the element. Check the type to be sure of it 0281 if (servicesLB->count() > 0 && servicesLB->item(1)->type() == -1) { 0282 delete servicesLB->takeItem(1); 0283 servicesLB->setEnabled(true); 0284 } 0285 0286 servicesLB->setCurrentItem(nullptr); 0287 updatePreferredServices(); 0288 Q_EMIT changed(true); 0289 } 0290 0291 void KServiceListWidget::editService() 0292 { 0293 if (!m_mimeTypeData) { 0294 return; 0295 } 0296 const int selected = servicesLB->currentRow(); 0297 if (selected < 0) { 0298 return; 0299 } 0300 0301 // Only edit applications, not services as 0302 // they don't have any parameters 0303 if (m_kind != SERVICELIST_APPLICATIONS) { 0304 return; 0305 } 0306 0307 // Just like popping up an add dialog except that we 0308 // pass the current command line as a default 0309 KServiceListItem *selItem = (KServiceListItem *)servicesLB->item(selected); 0310 const QString desktopPath = selItem->desktopPath; 0311 0312 KService::Ptr service = KService::serviceByDesktopPath(desktopPath); 0313 if (!service) { 0314 return; 0315 } 0316 0317 QString path = service->entryPath(); 0318 { 0319 // If the path to the desktop file is relative, try to get the full 0320 // path from QStandardPaths. 0321 QString fullPath = QStandardPaths::locate(QStandardPaths::ApplicationsLocation, path); 0322 if (!fullPath.isEmpty()) { 0323 path = fullPath; 0324 } 0325 } 0326 0327 KFileItem item(QUrl::fromLocalFile(path), QStringLiteral("application/x-desktop"), KFileItem::Unknown); 0328 KPropertiesDialog dlg(item, this); 0329 if (dlg.exec() != QDialog::Accepted) { 0330 return; 0331 } 0332 0333 // Note that at this point, ksycoca has been updated, 0334 // and setMimeTypeData has been called again, so all the items have been recreated. 0335 0336 // Reload service 0337 service = KService::serviceByDesktopPath(desktopPath); 0338 if (!service) { 0339 return; 0340 } 0341 0342 // Remove the old one... 0343 delete servicesLB->takeItem(selected); 0344 0345 // ...check that it's not a duplicate entry... 0346 bool addIt = true; 0347 for (int index = 0; index < servicesLB->count(); index++) { 0348 if (static_cast<KServiceListItem *>(servicesLB->item(index))->desktopPath == service->entryPath()) { 0349 addIt = false; 0350 break; 0351 } 0352 } 0353 0354 // ...and add it in the same place as the old one: 0355 if (addIt) { 0356 servicesLB->insertItem(selected, new KServiceListItem(service)); 0357 servicesLB->setCurrentRow(selected); 0358 } 0359 0360 updatePreferredServices(); 0361 0362 Q_EMIT changed(true); 0363 } 0364 0365 void KServiceListWidget::removeService() 0366 { 0367 if (!m_mimeTypeData) { 0368 return; 0369 } 0370 0371 int selected = servicesLB->currentRow(); 0372 0373 if (selected >= 0) { 0374 delete servicesLB->takeItem(selected); 0375 updatePreferredServices(); 0376 0377 Q_EMIT changed(true); 0378 } 0379 0380 // Update buttons and service list again (e.g. to re-add "None") 0381 setMimeTypeData(m_mimeTypeData); 0382 } 0383 0384 void KServiceListWidget::updatePreferredServices() 0385 { 0386 if (!m_mimeTypeData) { 0387 return; 0388 } 0389 QStringList sl; 0390 unsigned int count = servicesLB->count(); 0391 for (unsigned int i = 0; i < count; i++) { 0392 Q_ASSERT(servicesLB->item(i)->type() != -1); 0393 if (m_kind == SERVICELIST_APPLICATIONS) { 0394 auto sli = (KServiceListItem *)servicesLB->item(i); 0395 sl.append(sli->storageId); 0396 } else { 0397 auto pluginItem = (PluginListItem *)servicesLB->item(i); 0398 sl.append(pluginItem->metaData.pluginId()); 0399 } 0400 } 0401 sl.removeDuplicates(); 0402 if (m_kind == SERVICELIST_APPLICATIONS) { 0403 m_mimeTypeData->setAppServices(sl); 0404 } else { 0405 m_mimeTypeData->setEmbedParts(sl); 0406 } 0407 } 0408 0409 void KServiceListWidget::applyTo() 0410 { 0411 Q_EMIT multiApply(m_kind); 0412 } 0413 0414 void KServiceListWidget::enableMoveButtons() 0415 { 0416 int idx = servicesLB->currentRow(); 0417 if (servicesLB->model()->rowCount() <= 1) { 0418 servUpButton->setEnabled(false); 0419 servDownButton->setEnabled(false); 0420 } else if (idx == (servicesLB->model()->rowCount() - 1)) { 0421 servUpButton->setEnabled(true); 0422 servDownButton->setEnabled(false); 0423 } else if (idx == 0) { 0424 servUpButton->setEnabled(false); 0425 servDownButton->setEnabled(true); 0426 } else { 0427 servUpButton->setEnabled(true); 0428 servDownButton->setEnabled(true); 0429 } 0430 0431 if (servRemoveButton) { 0432 servRemoveButton->setEnabled(true); 0433 } 0434 0435 if (servEditButton) { 0436 servEditButton->setEnabled(m_kind == SERVICELIST_APPLICATIONS); 0437 } 0438 } 0439 0440 #include "moc_kservicelistwidget.cpp"