1 /*
2 * Cantata
3 *
4 * Copyright (c) 2011-2020 Craig Drummond <craig.p.drummond@gmail.com>
5 *
6 * ----
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; see the file COPYING. If not, write to
20 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21 * Boston, MA 02110-1301, USA.
22 */
23
24 #include "devicespage.h"
25 #include "models/musiclibraryitemroot.h"
26 #include "models/musiclibraryitemartist.h"
27 #include "models/musiclibraryitemalbum.h"
28 #include "models/musiclibraryitemsong.h"
29 #include "models/devicesmodel.h"
30 #include "gui/settings.h"
31 #include "support/messagebox.h"
32 #include "support/configuration.h"
33 #include "widgets/icons.h"
34 #include "widgets/menubutton.h"
35 #include "support/action.h"
36 #include "support/monoicon.h"
37 #include "gui/stdactions.h"
38 #include "syncdialog.h"
39 #ifdef ENABLE_REMOTE_DEVICES
40 #include "remotedevicepropertiesdialog.h"
41 #include "devicepropertieswidget.h"
42 #endif
43 #ifdef ENABLE_REPLAYGAIN_SUPPORT
44 #include "replaygain/rgdialog.h"
45 #endif
46 #include "tags/tageditor.h"
47 #include "actiondialog.h"
48 #include "tags/trackorganiser.h"
49 #include "gui/preferencesdialog.h"
50 #include "gui/coverdialog.h"
51 #include "mpd-interface/mpdconnection.h"
52 #if defined CDDB_FOUND || defined MUSICBRAINZ5_FOUND
53 #include "audiocddevice.h"
54 #include "albumdetailsdialog.h"
55 #include "cddbselectiondialog.h"
56 #endif
57 #include <QMenu>
58
DevicesPage(QWidget * p)59 DevicesPage::DevicesPage(QWidget *p)
60 : SinglePageWidget(p)
61 {
62 copyAction = new Action(Icons::self()->downloadIcon, tr("Copy To Library"), this);
63 ToolButton *copyToLibraryButton=new ToolButton(this);
64 copyToLibraryButton->setDefaultAction(copyAction);
65 syncAction = new Action(MonoIcon::icon(FontAwesome::exchange, Utils::monoIconColor()), tr("Synchronise"), this);
66 syncAction->setEnabled(false);
67 connect(syncAction, SIGNAL(triggered()), this, SLOT(sync()));
68 #ifdef ENABLE_REMOTE_DEVICES
69 forgetDeviceAction=new Action(tr("Forget Device"), this);
70 connect(forgetDeviceAction, SIGNAL(triggered()), this, SLOT(forgetRemoteDevice()));
71 #endif
72 connect(DevicesModel::self()->connectAct(), SIGNAL(triggered()), this, SLOT(toggleDevice()));
73 connect(DevicesModel::self()->disconnectAct(), SIGNAL(triggered()), this, SLOT(toggleDevice()));
74 connect(DevicesModel::self(), SIGNAL(updated(QModelIndex)), this, SLOT(updated(QModelIndex)));
75 connect(view, SIGNAL(doubleClicked(const QModelIndex &)), this, SLOT(itemDoubleClicked(const QModelIndex &)));
76 connect(view, SIGNAL(searchItems()), this, SLOT(searchItems()));
77 connect(view, SIGNAL(itemsSelected(bool)), SLOT(controlActions()));
78 connect(copyAction, SIGNAL(triggered()), this, SLOT(copyToLibrary()));
79 connect(DevicesModel::self()->configureAct(), SIGNAL(triggered()), this, SLOT(configureDevice()));
80 connect(DevicesModel::self()->refreshAct(), SIGNAL(triggered()), this, SLOT(refreshDevice()));
81 #if defined CDDB_FOUND || defined MUSICBRAINZ5_FOUND
82 connect(DevicesModel::self()->editAct(), SIGNAL(triggered()), this, SLOT(editDetails()));
83 connect(DevicesModel::self(), SIGNAL(matches(const QString &, const QList<CdAlbum> &)),
84 SLOT(cdMatches(const QString &, const QList<CdAlbum> &)));
85 #endif
86 proxy.setSourceModel(DevicesModel::self());
87 view->setModel(&proxy);
88 view->setRootIsDecorated(false);
89 view->setSearchResetLevel(1);
90 Configuration config(metaObject()->className());
91 view->load(config);
92 MenuButton *menu=new MenuButton(this);
93 menu->addAction(createViewMenu(QList<ItemView::Mode>() << ItemView::Mode_BasicTree << ItemView::Mode_SimpleTree
94 << ItemView::Mode_DetailedTree << ItemView::Mode_List));
95 menu->addSeparator();
96 menu->addAction(DevicesModel::self()->configureAct());
97 menu->addAction(DevicesModel::self()->refreshAct());
98 #ifdef ENABLE_REMOTE_DEVICES
99 menu->addSeparator();
100 Action *addRemote=new Action(tr("Add Device"), this);
101 connect(addRemote, SIGNAL(triggered()), this, SLOT(addRemoteDevice()));
102 menu->addAction(addRemote);
103 menu->addAction(forgetDeviceAction);
104 #endif
105 init(ReplacePlayQueue|AppendToPlayQueue, QList<QWidget *>() << menu, QList<QWidget *>() << copyToLibraryButton);
106
107 view->addAction(copyAction);
108 view->addAction(syncAction);
109 view->addAction(StdActions::self()->organiseFilesAction);
110 view->addAction(StdActions::self()->editTagsAction);
111 #ifdef ENABLE_REPLAYGAIN_SUPPORT
112 view->addAction(StdActions::self()->replaygainAction);
113 #endif
114 #ifdef ENABLE_REMOTE_DEVICES
115 view->addSeparator();
116 view->addAction(forgetDeviceAction);
117 #endif
118 view->addSeparator();
119 view->addAction(StdActions::self()->deleteSongsAction);
120 view->setInfoText(tr("Any supported devices will appear here when attached to your computer."));
121 }
122
~DevicesPage()123 DevicesPage::~DevicesPage()
124 {
125 Configuration config(metaObject()->className());
126 view->save(config);
127 }
128
clear()129 void DevicesPage::clear()
130 {
131 DevicesModel::self()->clear();
132 view->goToTop();
133 }
134
activeFsDeviceUdi() const135 QString DevicesPage::activeFsDeviceUdi() const
136 {
137 Device *dev=activeFsDevice();
138 return dev ? dev->id() : QString();
139 }
140
activeFsDevice() const141 Device * DevicesPage::activeFsDevice() const
142 {
143 const QModelIndexList selected = view->selectedIndexes(false); // Dont need sorted selection here...
144
145 if (selected.isEmpty()) {
146 return nullptr;
147 }
148
149 QString udi;
150 Device *activeDev=nullptr;
151 for (const QModelIndex &idx: selected) {
152 QModelIndex index = proxy.mapToSource(idx);
153 MusicLibraryItem *item=static_cast<MusicLibraryItem *>(index.internalPointer());
154
155 if (item && MusicLibraryItem::Type_Root!=item->itemType()) {
156 while(item->parentItem()) {
157 item=item->parentItem();
158 }
159 }
160
161 if (item && MusicLibraryItem::Type_Root==item->itemType()) {
162 Device *dev=static_cast<Device *>(item);
163 if (Device::Ums!=dev->devType() && Device::RemoteFs!=dev->devType()) {
164 return nullptr;
165 }
166 if (activeDev) {
167 return nullptr;
168 }
169 activeDev=dev;
170 }
171 }
172
173 return activeDev;
174 }
175
playableUrls() const176 QStringList DevicesPage::playableUrls() const
177 {
178 QModelIndexList selected = view->selectedIndexes();
179 if (selected.isEmpty()) {
180 return QStringList();
181 }
182
183 QModelIndexList mapped;
184 for (const QModelIndex &idx: selected) {
185 mapped.append(proxy.mapToSource(idx));
186 }
187
188 return DevicesModel::self()->playableUrls(mapped);
189 }
190
selectedSongs(bool allowPlaylists) const191 QList<Song> DevicesPage::selectedSongs(bool allowPlaylists) const
192 {
193 Q_UNUSED(allowPlaylists)
194 QModelIndexList selected = view->selectedIndexes();
195 if (selected.isEmpty()) {
196 return QList<Song>();
197 }
198
199 // Ensure all songs are from UMS/Remote devices...
200 for (const QModelIndex &idx: selected) {
201 MusicLibraryItem *item=static_cast<MusicLibraryItem *>(proxy.mapToSource(idx).internalPointer());
202 if (item && MusicLibraryItem::Type_Root!=item->itemType()) {
203 while(item->parentItem()) {
204 item=item->parentItem();
205 }
206 }
207
208 if (item && MusicLibraryItem::Type_Root==item->itemType()) {
209 Device *dev=static_cast<Device *>(item);
210 if (Device::Ums!=dev->devType() && Device::RemoteFs!=dev->devType()) {
211 return QList<Song>();
212 }
213 }
214 }
215
216 return DevicesModel::self()->songs(proxy.mapToSource(selected));
217 }
218
addSelectionToPlaylist(const QString & name,int action,quint8 priority,bool decreasePriority)219 void DevicesPage::addSelectionToPlaylist(const QString &name, int action, quint8 priority, bool decreasePriority)
220 {
221 QStringList files=playableUrls();
222 if (!files.isEmpty()) {
223 if (name.isEmpty()) {
224 emit add(files, action, priority, decreasePriority);
225 } else {
226 emit addSongsToPlaylist(name, files);
227 }
228 view->clearSelection();
229 }
230 }
231
refresh()232 void DevicesPage::refresh()
233 {
234 view->goToTop();
235 DevicesModel::self()->resetModel();
236 if (ItemView::Mode_SimpleTree==view->viewMode() || ItemView::Mode_DetailedTree==view->viewMode()) {
237 for (int i=0; i<DevicesModel::self()->rowCount(QModelIndex()); ++i) {
238 view->setExpanded(proxy.mapFromSource(DevicesModel::self()->index(i, 0, QModelIndex())));
239 }
240 }
241 }
242
itemDoubleClicked(const QModelIndex &)243 void DevicesPage::itemDoubleClicked(const QModelIndex &)
244 {
245 // const QModelIndexList selected = view->selectedIndexes();
246 // if (1!=selected.size()) {
247 // return; //doubleclick should only have one selected item
248 // }
249 // MusicDevicesItem *item = DevicesModel::self()->toItem(proxy.mapToSource(selected.at(0)));
250 // if (MusicDevicesItem::Type_Song==item->itemType()) {
251 // addSelectionToPlaylist();
252 // }
253 }
254
searchItems()255 void DevicesPage::searchItems()
256 {
257 QString text=view->searchText().trimmed();
258 proxy.update(text);
259 if (proxy.enabled() && !proxy.filterText().isEmpty()) {
260 view->expandAll();
261 }
262 }
263
controlActions()264 void DevicesPage::controlActions()
265 {
266 QModelIndexList selected=view->selectedIndexes(false); // Dont need sorted selection here...
267 bool haveTracks=false;
268 bool onlyFs=true;
269 bool singleUdi=true;
270 bool connected=false;
271 #ifdef ENABLE_REMOTE_DEVICES
272 bool remoteDev=false;
273 #endif
274 bool deviceSelected=false;
275 bool busyDevice=false;
276 bool audioCd=false;
277 bool canPlay=false;
278 QString udi;
279
280 for (const QModelIndex &idx: selected) {
281 MusicLibraryItem *item=static_cast<MusicLibraryItem *>(proxy.mapToSource(idx).internalPointer());
282
283 if (item && MusicLibraryItem::Type_Root==item->itemType()) {
284 deviceSelected=true;
285 }
286
287 if (item && MusicLibraryItem::Type_Root!=item->itemType()) {
288 while(item->parentItem()) {
289 item=item->parentItem();
290 }
291 }
292
293 if (item && MusicLibraryItem::Type_Root==item->itemType()) {
294 Device *dev=static_cast<Device *>(item);
295 if (!dev->isStdFs()) {
296 onlyFs=false;
297 }
298 if (!dev->isIdle()) {
299 busyDevice=true;
300 }
301 if (Device::AudioCd==dev->devType()) {
302 audioCd=true;
303 }
304 #ifdef ENABLE_REMOTE_DEVICES
305 else if (Device::RemoteFs==dev->devType()) {
306 remoteDev=true;
307 }
308 #endif
309 canPlay=dev->canPlaySongs();
310 if (udi.isEmpty()) {
311 udi=dev->id();
312 } else if (udi!=dev->id()) {
313 singleUdi=false;
314 }
315 if (!haveTracks) {
316 haveTracks=dev->childCount()>0;
317 }
318 connected=dev->isConnected();
319 }
320 }
321
322 DevicesModel::self()->configureAct()->setEnabled(!busyDevice && 1==selected.count() && !audioCd);
323 DevicesModel::self()->refreshAct()->setEnabled(!busyDevice && 1==selected.count());
324 copyAction->setEnabled(!busyDevice && haveTracks && (!deviceSelected || audioCd));
325 syncAction->setEnabled(!audioCd && !busyDevice && deviceSelected && connected && 1==selected.count() && singleUdi &&
326 MPDConnection::self()->getDetails().dirReadable);
327 StdActions::self()->deleteSongsAction->setEnabled(!audioCd && !busyDevice && haveTracks && !deviceSelected);
328 StdActions::self()->editTagsAction->setEnabled(!busyDevice && haveTracks && onlyFs && singleUdi && !deviceSelected);
329 #ifdef ENABLE_REPLAYGAIN_SUPPORT
330 StdActions::self()->replaygainAction->setEnabled(!busyDevice && haveTracks && onlyFs && singleUdi && !deviceSelected);
331 #endif
332 StdActions::self()->organiseFilesAction->setEnabled(!busyDevice && haveTracks && onlyFs && singleUdi && !deviceSelected);
333 StdActions::self()->enableAddToPlayQueue(canPlay && !selected.isEmpty() && singleUdi && !busyDevice && haveTracks && (audioCd || !deviceSelected));
334 #ifdef ENABLE_REMOTE_DEVICES
335 forgetDeviceAction->setEnabled(singleUdi && remoteDev);
336 #endif
337 #if defined CDDB_FOUND || defined MUSICBRAINZ5_FOUND
338 DevicesModel::self()->editAct()->setEnabled(!AlbumDetailsDialog::instanceCount() && !busyDevice && 1==selected.count() && audioCd && haveTracks && deviceSelected);
339 #endif
340 }
341
copyToLibrary()342 void DevicesPage::copyToLibrary()
343 {
344 const QModelIndexList selected = view->selectedIndexes();
345
346 if (selected.isEmpty()) {
347 return;
348 }
349
350 QModelIndexList mapped;
351 for (const QModelIndex &idx: selected) {
352 mapped.append(proxy.mapToSource(idx));
353 }
354
355 MusicLibraryItem *item=static_cast<MusicLibraryItem *>(mapped.first().internalPointer());
356 while (item->parentItem()) {
357 item=item->parentItem();
358 }
359 QString udi;
360 if (MusicLibraryItem::Type_Root==item->itemType()) {
361 udi=static_cast<Device *>(item)->id();
362 }
363
364 if (udi.isEmpty()) {
365 return;
366 }
367
368 QList<Song> songs=DevicesModel::self()->songs(mapped);
369
370 if (!songs.isEmpty()) {
371 emit addToDevice(udi, QString(), songs);
372 view->clearSelection();
373 }
374 }
375
configureDevice()376 void DevicesPage::configureDevice()
377 {
378 const QModelIndexList selected = view->selectedIndexes(false); // Dont need sorted selection here...
379
380 if (1!=selected.size()) {
381 return;
382 }
383
384 MusicLibraryItem *item=static_cast<MusicLibraryItem *>(proxy.mapToSource(selected.first()).internalPointer());
385
386 if (MusicLibraryItem::Type_Root==item->itemType()) {
387 static_cast<Device *>(item)->configure(this);
388 }
389 }
390
refreshDevice()391 void DevicesPage::refreshDevice()
392 {
393 const QModelIndexList selected = view->selectedIndexes(false); // Dont need sorted selection here...
394
395 if (1!=selected.size()) {
396 return;
397 }
398
399 MusicLibraryItem *item=static_cast<MusicLibraryItem *>(proxy.mapToSource(selected.first()).internalPointer());
400
401 if (MusicLibraryItem::Type_Root==item->itemType()) {
402 Device *dev=static_cast<Device *>(item);
403 QString udi=dev->id();
404 bool full=true;
405
406 if (Device::AudioCd==dev->devType()) {
407 // Bit hacky - we use 'full' to determine CDDB/MusicBrainz!
408 #if defined CDDB_FOUND && defined MUSICBRAINZ5_FOUND
409 switch (MessageBox::questionYesNoCancel(this, tr("Lookup album and track details?"),
410 tr("Refresh"), GuiItem(tr("Via CDDB")), GuiItem(tr("Via MusicBrainz")))) {
411 case MessageBox::Yes:
412 full=true; // full==true => CDDB
413 break;
414 case MessageBox::No:
415 full=false; // full==false => MusicBrainz
416 break;
417 default:
418 return;
419 }
420 #else
421 if (MessageBox::No==MessageBox::questionYesNo(this, tr("Lookup album and track details?"),
422 tr("Refresh"), GuiItem(tr("Refresh")), StdGuiItem::cancel())) {
423 return;
424 }
425 #endif
426 dev=DevicesModel::self()->device(udi);
427 } else {
428 if (dev->childCount() && Device::Mtp!=dev->devType()) {
429 static const QChar constBullet(0x2022);
430
431 switch (MessageBox::questionYesNoCancel(this, tr("Which type of refresh do you wish to perform?")+QLatin1String("\n\n")+
432 constBullet+QLatin1Char(' ')+tr("Partial - Only new songs are scanned (quick)")+QLatin1Char('\n')+
433 constBullet+QLatin1Char(' ')+tr("Full - All songs are rescanned (slow)"),
434 tr("Refresh"), GuiItem(tr("Partial")), GuiItem(tr("Full")))) {
435 case MessageBox::Yes:
436 full=false;
437 case MessageBox::No:
438 break;
439 default:
440 return;
441 }
442 // We have to query for device again, as it could have been unplugged whilst the above question dialog was visible...
443 dev=DevicesModel::self()->device(udi);
444 }
445 }
446 if (dev) {
447 dev->rescan(full);
448 }
449 }
450 }
451
deleteSongs()452 void DevicesPage::deleteSongs()
453 {
454 const QModelIndexList selected = view->selectedIndexes();
455
456 if (selected.isEmpty()) {
457 return;
458 }
459
460 QModelIndexList mapped;
461 for (const QModelIndex &idx: selected) {
462 mapped.append(proxy.mapToSource(idx));
463 }
464
465 MusicLibraryItem *item=static_cast<MusicLibraryItem *>(mapped.first().internalPointer());
466 while (item->parentItem()) {
467 item=item->parentItem();
468 }
469 QString udi;
470 if (MusicLibraryItem::Type_Root==item->itemType()) {
471 udi=static_cast<Device *>(item)->id();
472 }
473
474 if (udi.isEmpty()) {
475 return;
476 }
477
478 QList<Song> songs=DevicesModel::self()->songs(mapped);
479
480 if (!songs.isEmpty()) {
481 if (MessageBox::Yes==MessageBox::warningYesNo(this, tr("Are you sure you wish to delete the selected songs?\n\nThis cannot be undone."),
482 tr("Delete Songs"), StdGuiItem::del(), StdGuiItem::cancel())) {
483 emit deleteSongs(udi, songs);
484 }
485 view->clearSelection();
486 }
487 }
488
addRemoteDevice()489 void DevicesPage::addRemoteDevice()
490 {
491 #ifdef ENABLE_REMOTE_DEVICES
492 RemoteDevicePropertiesDialog *dlg=new RemoteDevicePropertiesDialog(this);
493 dlg->show(DeviceOptions(QLatin1String("cover.jpg")), RemoteFsDevice::Details(), DevicePropertiesWidget::Prop_All-DevicePropertiesWidget::Prop_Folder, true);
494 connect(dlg, SIGNAL(updatedSettings(const DeviceOptions &, RemoteFsDevice::Details)),
495 DevicesModel::self(), SLOT(addRemoteDevice(const DeviceOptions &, RemoteFsDevice::Details)));
496 #endif
497 }
498
forgetRemoteDevice()499 void DevicesPage::forgetRemoteDevice()
500 {
501 #ifdef ENABLE_REMOTE_DEVICES
502 Device *dev=activeFsDevice();
503 if (!dev) {
504 return;
505 }
506 QString udi=dev->id();
507 QString devName=dev->data();
508 if (MessageBox::Yes==MessageBox::warningYesNo(this, tr("Are you sure you wish to forget '%1'?").arg(devName))) {
509 DevicesModel::self()->removeRemoteDevice(udi);
510 }
511 #endif
512 }
513
toggleDevice()514 void DevicesPage::toggleDevice()
515 {
516 const QModelIndexList selected = view->selectedIndexes(false); // Dont need sorted selection here...
517 if (1!=selected.size()) {
518 return;
519 }
520
521 MusicLibraryItem *item=static_cast<MusicLibraryItem *>(proxy.mapToSource(selected.first()).internalPointer());
522
523 if (MusicLibraryItem::Type_Root==item->itemType()) {
524 Device *dev=static_cast<Device *>(item);
525 if (dev->isConnected() &&
526 (Device::AudioCd==dev->devType()
527 ? MessageBox::No==MessageBox::warningYesNo(this, tr("Are you sure you wish to eject Audio CD '%1 - %2'?").arg(dev->data()).arg(dev->subText()),
528 tr("Eject"), GuiItem(tr("Eject")), StdGuiItem::cancel())
529 : MessageBox::No==MessageBox::warningYesNo(this, tr("Are you sure you wish to disconnect '%1'?").arg(dev->data()),
530 tr("Disconnect"), GuiItem(tr("Disconnect")), StdGuiItem::cancel()))) {
531 return;
532 }
533 static_cast<Device *>(item)->toggle();
534 }
535 }
536
sync()537 void DevicesPage::sync()
538 {
539 if (0!=SyncDialog::instanceCount()) {
540 return;
541 }
542
543 if (0!=PreferencesDialog::instanceCount() || 0!=TagEditor::instanceCount() || 0!=TrackOrganiser::instanceCount()
544 || 0!=ActionDialog::instanceCount() || 0!=CoverDialog::instanceCount()
545 #ifdef ENABLE_REPLAYGAIN_SUPPORT
546 || 0!=RgDialog::instanceCount()
547 #endif
548 ) {
549 MessageBox::error(this, tr("Please close other dialogs first."));
550 return;
551 }
552
553 const QModelIndexList selected = view->selectedIndexes();
554
555 if (1!=selected.size()) {
556 return;
557 }
558
559 MusicLibraryItem *item=static_cast<MusicLibraryItem *>(proxy.mapToSource(selected.first()).internalPointer());
560
561 if (MusicLibraryItem::Type_Root==item->itemType()) {
562 SyncDialog *dlg=new SyncDialog(this);
563 dlg->sync(static_cast<Device *>(item)->id());
564 }
565 }
566
updated(const QModelIndex & idx)567 void DevicesPage::updated(const QModelIndex &idx)
568 {
569 view->setExpanded(proxy.mapFromSource(idx));
570 }
571
cdMatches(const QString & udi,const QList<CdAlbum> & albums)572 void DevicesPage::cdMatches(const QString &udi, const QList<CdAlbum> &albums)
573 {
574 #if defined CDDB_FOUND || defined MUSICBRAINZ5_FOUND
575 int chosen=0;
576 Device *dev=DevicesModel::self()->device(udi);
577 if (dev && Device::AudioCd==dev->devType()) {
578 CddbSelectionDialog *dlg=new CddbSelectionDialog(this);
579 chosen=dlg->select(albums);
580 if (chosen<0 || chosen>=albums.count()) {
581 chosen=0;
582 }
583 }
584
585 // Need to get device again, as it may have been removed!
586 dev=DevicesModel::self()->device(udi);
587 if (dev && Device::AudioCd==dev->devType()) {
588 static_cast<AudioCdDevice *>(dev)->setDetails(albums.at(chosen));
589 }
590 #else
591 Q_UNUSED(udi)
592 Q_UNUSED(albums)
593 #endif
594 }
595
editDetails()596 void DevicesPage::editDetails()
597 {
598 #if defined CDDB_FOUND || defined MUSICBRAINZ5_FOUND
599 if (AlbumDetailsDialog::instanceCount()) {
600 return;
601 }
602 const QModelIndexList selected = view->selectedIndexes(false); // Dont need sorted selection here...
603 if (1!=selected.size()) {
604 return;
605 }
606
607 MusicLibraryItem *item=static_cast<MusicLibraryItem *>(proxy.mapToSource(selected.first()).internalPointer());
608
609 if (MusicLibraryItem::Type_Root==item->itemType()) {
610 Device *dev=static_cast<Device *>(item);
611 if (Device::AudioCd==dev->devType()) {
612 AlbumDetailsDialog *dlg=new AlbumDetailsDialog(this);
613 dlg->show(static_cast<AudioCdDevice *>(dev));
614 }
615 }
616 #endif
617 }
618
619 #include "moc_devicespage.cpp"
620