1 | /* This file is part of the KDE project |
2 | Copyright (C) 2003 Waldo Bastian <bastian@kde.org> |
3 | Copyright (C) 2003 David Faure <faure@kde.org> |
4 | Copyright (C) 2002 Daniel Molkentin <molkentin@kde.org> |
5 | |
6 | This program is free software; you can redistribute it and/or |
7 | modify it under the terms of the GNU General Public |
8 | License version 2 as published by the Free Software Foundation. |
9 | |
10 | This program is distributed in the hope that it will be useful, |
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
13 | General Public License for more details. |
14 | |
15 | You should have received a copy of the GNU General Public License |
16 | along with this program; see the file COPYING. If not, write to |
17 | the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
18 | Boston, MA 02110-1301, USA. |
19 | */ |
20 | |
21 | // Own |
22 | #include "kservicelistwidget.h" |
23 | |
24 | // std |
25 | #include <unistd.h> |
26 | |
27 | // Qt |
28 | #include <QLayout> |
29 | #include <QHBoxLayout> |
30 | |
31 | // KDE |
32 | #include <kapplication.h> |
33 | #include <kdebug.h> |
34 | #include <klocale.h> |
35 | #include <kmessagebox.h> |
36 | #include <knotification.h> |
37 | #include <kopenwithdialog.h> |
38 | #include <kpropertiesdialog.h> |
39 | #include <kpushbutton.h> |
40 | #include <kicon.h> |
41 | #include <kstandarddirs.h> |
42 | |
43 | // Local |
44 | #include "kserviceselectdlg.h" |
45 | #include "mimetypedata.h" |
46 | |
47 | KServiceListItem::KServiceListItem( const KService::Ptr& pService, int kind ) |
48 | : QListWidgetItem(), storageId(pService->storageId()), desktopPath(pService->entryPath()) |
49 | { |
50 | if ( kind == KServiceListWidget::SERVICELIST_APPLICATIONS ) |
51 | setText( pService->name() ); |
52 | else |
53 | setText( i18n( "%1 (%2)" , pService->name(), pService->desktopEntryName() ) ); |
54 | |
55 | if (!pService->isApplication()) |
56 | localPath = KStandardDirs::locateLocal("services" , desktopPath); |
57 | else |
58 | localPath = pService->locateLocal(); |
59 | } |
60 | |
61 | bool KServiceListItem::isImmutable() const |
62 | { |
63 | return !KStandardDirs::checkAccess(localPath, W_OK); |
64 | } |
65 | |
66 | |
67 | |
68 | |
69 | |
70 | KServiceListWidget::KServiceListWidget(int kind, QWidget *parent) |
71 | : QGroupBox( kind == SERVICELIST_APPLICATIONS ? i18n("Application Preference Order" ) |
72 | : i18n("Services Preference Order" ), parent ), |
73 | m_kind( kind ), m_mimeTypeData( 0L ) |
74 | { |
75 | QHBoxLayout *lay= new QHBoxLayout(this); |
76 | |
77 | servicesLB = new QListWidget(this); |
78 | connect(servicesLB, SIGNAL(itemSelectionChanged()), SLOT(enableMoveButtons())); |
79 | lay->addWidget(servicesLB); |
80 | connect( servicesLB, SIGNAL( itemDoubleClicked(QListWidgetItem*)), this, SLOT( editService())); |
81 | |
82 | QString wtstr = |
83 | (kind == SERVICELIST_APPLICATIONS ? |
84 | i18n("This is a list of applications associated with files of the selected" |
85 | " file type. This list is shown in Konqueror's context menus when you select" |
86 | " \"Open With...\". If more than one application is associated with this file type," |
87 | " then the list is ordered by priority with the uppermost item taking precedence" |
88 | " over the others." ) : |
89 | i18n("This is a list of services associated with files of the selected" |
90 | " file type. This list is shown in Konqueror's context menus when you select" |
91 | " a \"Preview with...\" option. If more than one service is associated with this file type," |
92 | " then the list is ordered by priority with the uppermost item taking precedence" |
93 | " over the others." )); |
94 | |
95 | setWhatsThis( wtstr ); |
96 | servicesLB->setWhatsThis( wtstr ); |
97 | |
98 | QVBoxLayout *btnsLay= new QVBoxLayout(); |
99 | lay->addLayout(btnsLay); |
100 | |
101 | servUpButton = new KPushButton(i18n("Move &Up" ), this); |
102 | servUpButton->setIcon(KIcon("arrow-up" )); |
103 | servUpButton->setEnabled(false); |
104 | connect(servUpButton, SIGNAL(clicked()), SLOT(promoteService())); |
105 | btnsLay->addWidget(servUpButton); |
106 | |
107 | servUpButton->setWhatsThis( kind == SERVICELIST_APPLICATIONS ? |
108 | i18n("Assigns a higher priority to the selected\n" |
109 | "application, moving it up in the list. Note: This\n" |
110 | "only affects the selected application if the file type is\n" |
111 | "associated with more than one application." ) : |
112 | i18n("Assigns a higher priority to the selected\n" |
113 | "service, moving it up in the list." )); |
114 | |
115 | servDownButton = new KPushButton(i18n("Move &Down" ), this); |
116 | servDownButton->setIcon(KIcon("arrow-down" )); |
117 | servDownButton->setEnabled(false); |
118 | connect(servDownButton, SIGNAL(clicked()), SLOT(demoteService())); |
119 | btnsLay->addWidget(servDownButton); |
120 | servDownButton->setWhatsThis( kind == SERVICELIST_APPLICATIONS ? |
121 | i18n("Assigns a lower priority to the selected\n" |
122 | "application, moving it down in the list. Note: This \n" |
123 | "only affects the selected application if the file type is\n" |
124 | "associated with more than one application." ): |
125 | i18n("Assigns a lower priority to the selected\n" |
126 | "service, moving it down in the list." )); |
127 | |
128 | servNewButton = new KPushButton(i18n("Add..." ), this); |
129 | servNewButton->setIcon(KIcon("list-add" )); |
130 | servNewButton->setEnabled(false); |
131 | connect(servNewButton, SIGNAL(clicked()), SLOT(addService())); |
132 | btnsLay->addWidget(servNewButton); |
133 | servNewButton->setWhatsThis( i18n( "Add a new application for this file type." ) ); |
134 | |
135 | |
136 | servEditButton = new KPushButton(i18n("Edit..." ), this); |
137 | servEditButton->setIcon(KIcon("edit-rename" )); |
138 | servEditButton->setEnabled(false); |
139 | connect(servEditButton, SIGNAL(clicked()), SLOT(editService())); |
140 | btnsLay->addWidget(servEditButton); |
141 | servEditButton->setWhatsThis( i18n( "Edit command line of the selected application." ) ); |
142 | |
143 | |
144 | servRemoveButton = new KPushButton(i18n("Remove" ), this); |
145 | servRemoveButton->setIcon(KIcon("list-remove" )); |
146 | servRemoveButton->setEnabled(false); |
147 | connect(servRemoveButton, SIGNAL(clicked()), SLOT(removeService())); |
148 | btnsLay->addWidget(servRemoveButton); |
149 | servRemoveButton->setWhatsThis( i18n( "Remove the selected application from the list." ) ); |
150 | |
151 | btnsLay->addStretch(1); |
152 | } |
153 | |
154 | void KServiceListWidget::setMimeTypeData( MimeTypeData * mimeTypeData ) |
155 | { |
156 | m_mimeTypeData = mimeTypeData; |
157 | if ( servNewButton ) |
158 | servNewButton->setEnabled(true); |
159 | // will need a selection |
160 | servUpButton->setEnabled(false); |
161 | servDownButton->setEnabled(false); |
162 | |
163 | servicesLB->clear(); |
164 | servicesLB->setEnabled(false); |
165 | |
166 | if (m_mimeTypeData) { |
167 | const QStringList services = ( m_kind == SERVICELIST_APPLICATIONS ) |
168 | ? m_mimeTypeData->appServices() |
169 | : m_mimeTypeData->embedServices(); |
170 | |
171 | if (services.isEmpty()) { |
172 | if (m_kind == SERVICELIST_APPLICATIONS) |
173 | servicesLB->addItem(i18nc("No applications associated with this file type" , "None" )); |
174 | else |
175 | servicesLB->addItem(i18nc("No components associated with this file type" , "None" )); |
176 | } else { |
177 | Q_FOREACH(const QString& service, services) { |
178 | KService::Ptr pService = KService::serviceByStorageId(service); |
179 | if (pService) |
180 | servicesLB->addItem( new KServiceListItem(pService, m_kind) ); |
181 | } |
182 | servicesLB->setEnabled(true); |
183 | } |
184 | } |
185 | |
186 | if (servRemoveButton) |
187 | servRemoveButton->setEnabled(servicesLB->currentRow() > -1); |
188 | if (servEditButton) |
189 | servEditButton->setEnabled(servicesLB->currentRow() > -1); |
190 | } |
191 | |
192 | void KServiceListWidget::promoteService() |
193 | { |
194 | if (!servicesLB->isEnabled()) { |
195 | KNotification::beep(); |
196 | return; |
197 | } |
198 | |
199 | int selIndex = servicesLB->currentRow(); |
200 | if (selIndex == 0) { |
201 | KNotification::beep(); |
202 | return; |
203 | } |
204 | |
205 | QListWidgetItem *selItem = servicesLB->item(selIndex); |
206 | servicesLB->takeItem(selIndex); |
207 | servicesLB->insertItem(selIndex-1,selItem); |
208 | servicesLB->setCurrentRow(selIndex - 1); |
209 | |
210 | updatePreferredServices(); |
211 | |
212 | emit changed(true); |
213 | } |
214 | |
215 | void KServiceListWidget::demoteService() |
216 | { |
217 | if (!servicesLB->isEnabled()) { |
218 | KNotification::beep(); |
219 | return; |
220 | } |
221 | |
222 | int selIndex = servicesLB->currentRow(); |
223 | if (selIndex == servicesLB->count() - 1) { |
224 | KNotification::beep(); |
225 | return; |
226 | } |
227 | |
228 | QListWidgetItem *selItem = servicesLB->item(selIndex); |
229 | servicesLB->takeItem(selIndex); |
230 | servicesLB->insertItem(selIndex + 1, selItem); |
231 | servicesLB->setCurrentRow(selIndex + 1); |
232 | |
233 | updatePreferredServices(); |
234 | |
235 | emit changed(true); |
236 | } |
237 | |
238 | void KServiceListWidget::addService() |
239 | { |
240 | if (!m_mimeTypeData) |
241 | return; |
242 | |
243 | KService::Ptr service; |
244 | if ( m_kind == SERVICELIST_APPLICATIONS ) |
245 | { |
246 | KOpenWithDialog dlg(m_mimeTypeData->name(), QString(), this); |
247 | dlg.setSaveNewApplications(true); |
248 | if (dlg.exec() != QDialog::Accepted) |
249 | return; |
250 | |
251 | service = dlg.service(); |
252 | |
253 | Q_ASSERT(service); |
254 | if (!service) |
255 | return; // Don't crash if KOpenWith wasn't able to create service. |
256 | } |
257 | else |
258 | { |
259 | KServiceSelectDlg dlg(m_mimeTypeData->name(), QString(), this); |
260 | if (dlg.exec() != QDialog::Accepted) |
261 | return; |
262 | service = dlg.service(); |
263 | Q_ASSERT(service); |
264 | if (!service) |
265 | return; |
266 | } |
267 | |
268 | // Did the list simply show "None"? |
269 | const bool hadDummyEntry = ( m_kind == SERVICELIST_APPLICATIONS ) |
270 | ? m_mimeTypeData->appServices().isEmpty() |
271 | : m_mimeTypeData->embedServices().isEmpty(); |
272 | |
273 | if (hadDummyEntry) { |
274 | delete servicesLB->takeItem(0); // Remove the "None" item. |
275 | servicesLB->setEnabled(true); |
276 | } else { |
277 | // check if it is a duplicate entry |
278 | for (int index = 0; index < servicesLB->count(); index++) { |
279 | if (static_cast<KServiceListItem*>( servicesLB->item(index) )->desktopPath |
280 | == service->entryPath()) { |
281 | // ##### shouldn't we make the existing entry the default one? |
282 | return; |
283 | } |
284 | } |
285 | } |
286 | |
287 | servicesLB->insertItem(0, new KServiceListItem(service, m_kind)); |
288 | servicesLB->setCurrentItem(0); |
289 | |
290 | updatePreferredServices(); |
291 | |
292 | emit changed(true); |
293 | } |
294 | |
295 | void KServiceListWidget::editService() |
296 | { |
297 | if (!m_mimeTypeData) |
298 | return; |
299 | const int selected = servicesLB->currentRow(); |
300 | if (selected < 0) |
301 | return; |
302 | |
303 | // Only edit applications, not services as |
304 | // they don't have any parameters |
305 | if (m_kind != SERVICELIST_APPLICATIONS) |
306 | return; |
307 | |
308 | // Just like popping up an add dialog except that we |
309 | // pass the current command line as a default |
310 | KServiceListItem *selItem = (KServiceListItem*)servicesLB->item(selected); |
311 | const QString desktopPath = selItem->desktopPath; |
312 | |
313 | KService::Ptr service = KService::serviceByDesktopPath(desktopPath); |
314 | if (!service) |
315 | return; |
316 | |
317 | QString path = service->entryPath(); |
318 | |
319 | // If the path to the desktop file is relative, try to get the full |
320 | // path from KStandardDirs. |
321 | path = KStandardDirs::locate("apps" , path); // TODO use xdgdata-apps instead? |
322 | |
323 | KFileItem item(KUrl(path), "application/x-desktop" , KFileItem::Unknown); |
324 | KPropertiesDialog dlg(item, this); |
325 | if (dlg.exec() != QDialog::Accepted) |
326 | return; |
327 | |
328 | // Note that at this point, ksycoca has been updated, |
329 | // and setMimeTypeData has been called again, so all the items have been recreated. |
330 | |
331 | // Reload service |
332 | service = KService::serviceByDesktopPath(desktopPath); |
333 | if (!service) |
334 | return; |
335 | |
336 | // Remove the old one... |
337 | delete servicesLB->takeItem(selected); |
338 | |
339 | // ...check that it's not a duplicate entry... |
340 | bool addIt = true; |
341 | for (int index = 0; index < servicesLB->count(); index++) { |
342 | if (static_cast<KServiceListItem*>(servicesLB->item(index))->desktopPath |
343 | == service->entryPath()) { |
344 | addIt = false; |
345 | break; |
346 | } |
347 | } |
348 | |
349 | // ...and add it in the same place as the old one: |
350 | if (addIt) { |
351 | servicesLB->insertItem(selected, new KServiceListItem(service, m_kind)); |
352 | servicesLB->setCurrentRow(selected); |
353 | } |
354 | |
355 | updatePreferredServices(); |
356 | |
357 | emit changed(true); |
358 | } |
359 | |
360 | void KServiceListWidget::removeService() |
361 | { |
362 | if (!m_mimeTypeData) return; |
363 | |
364 | int selected = servicesLB->currentRow(); |
365 | |
366 | if ( selected >= 0 ) { |
367 | // Check if service is associated with this mimetype or with one of its parents |
368 | KServiceListItem *serviceItem = static_cast<KServiceListItem *>(servicesLB->item(selected)); |
369 | if (serviceItem->isImmutable()) |
370 | { |
371 | KMessageBox::sorry(this, i18n("You are not authorized to remove this service." )); |
372 | } |
373 | else |
374 | { |
375 | delete servicesLB->takeItem( selected ); |
376 | updatePreferredServices(); |
377 | |
378 | emit changed(true); |
379 | } |
380 | } |
381 | |
382 | // Update buttons and service list again (e.g. to re-add "None") |
383 | setMimeTypeData(m_mimeTypeData); |
384 | } |
385 | |
386 | void KServiceListWidget::updatePreferredServices() |
387 | { |
388 | if (!m_mimeTypeData) |
389 | return; |
390 | QStringList sl; |
391 | unsigned int count = servicesLB->count(); |
392 | |
393 | for (unsigned int i = 0; i < count; i++) { |
394 | KServiceListItem *sli = (KServiceListItem *) servicesLB->item(i); |
395 | sl.append( sli->storageId ); |
396 | } |
397 | sl.removeDuplicates(); |
398 | if ( m_kind == SERVICELIST_APPLICATIONS ) |
399 | m_mimeTypeData->setAppServices(sl); |
400 | else |
401 | m_mimeTypeData->setEmbedServices(sl); |
402 | } |
403 | |
404 | void KServiceListWidget::enableMoveButtons() |
405 | { |
406 | int idx = servicesLB->currentRow(); |
407 | if (servicesLB->model()->rowCount() <= 1) |
408 | { |
409 | servUpButton->setEnabled(false); |
410 | servDownButton->setEnabled(false); |
411 | } |
412 | else if ( idx == (servicesLB->model()->rowCount() - 1) ) |
413 | { |
414 | servUpButton->setEnabled(true); |
415 | servDownButton->setEnabled(false); |
416 | } |
417 | else if (idx == 0) |
418 | { |
419 | servUpButton->setEnabled(false); |
420 | servDownButton->setEnabled(true); |
421 | } |
422 | else |
423 | { |
424 | servUpButton->setEnabled(true); |
425 | servDownButton->setEnabled(true); |
426 | } |
427 | |
428 | if ( servRemoveButton ) |
429 | servRemoveButton->setEnabled(true); |
430 | |
431 | if ( servEditButton ) |
432 | servEditButton->setEnabled( m_kind == SERVICELIST_APPLICATIONS ); |
433 | } |
434 | |
435 | #include "kservicelistwidget.moc" |
436 | |