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