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