From f31a7264cdbfeaf3697f998bb8a8d4e8a0b4c3ef Mon Sep 17 00:00:00 2001 From: "craig.p.drummond" Date: Fri, 8 Jun 2012 17:53:35 +0000 Subject: [PATCH] Add support for multiple MPD servers. --- CMakeLists.txt | 5 +- ChangeLog | 3 + devices/actiondialog.cpp | 16 +-- devices/trackorganiser.cpp | 2 +- devices/utils.cpp | 18 +++ devices/utils.h | 1 + dynamic/dynamic.cpp | 6 +- gui/albumspage.cpp | 2 +- gui/covers.cpp | 20 ++-- gui/folderpage.cpp | 6 +- gui/librarypage.cpp | 2 +- gui/mainwindow.cpp | 195 ++++++++++++++++++++++++------ gui/mainwindow.h | 10 +- gui/playbacksettings.cpp | 79 ------------- gui/playbacksettings.h | 16 --- gui/playbacksettings.ui | 165 +++++--------------------- gui/preferencesdialog.cpp | 26 ++-- gui/preferencesdialog.h | 4 + gui/serverplaybacksettings.cpp | 127 ++++++++++++++++++++ gui/serverplaybacksettings.h | 57 +++++++++ gui/serverplaybacksettings.ui | 134 +++++++++++++++++++++ gui/serversettings.cpp | 152 ++++++++++++++++++++++-- gui/serversettings.h | 19 +++ gui/serversettings.ui | 200 +++++++++++++++++++------------ gui/settings.cpp | 210 +++++++++++++++++++++------------ gui/settings.h | 19 ++- gui/tageditor.cpp | 4 +- lyrics/lyricspage.cpp | 14 +-- models/albumsmodel.cpp | 4 +- models/dirviewmodel.cpp | 4 +- models/musiclibrarymodel.cpp | 13 +- models/playlistsmodel.cpp | 21 +--- models/playqueuemodel.cpp | 6 +- mpd/mpdconnection.cpp | 95 +++++++-------- mpd/mpdconnection.h | 35 ++++-- replaygain/rgdialog.cpp | 4 +- 36 files changed, 1125 insertions(+), 569 deletions(-) create mode 100644 gui/serverplaybacksettings.cpp create mode 100644 gui/serverplaybacksettings.h create mode 100644 gui/serverplaybacksettings.ui diff --git a/CMakeLists.txt b/CMakeLists.txt index e873fbca0..97a5c51d3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -70,6 +70,7 @@ SET( CANTATA_SRCS gui/interfacesettings.cpp gui/externalsettings.cpp gui/playbacksettings.cpp + gui/serverplaybacksettings.cpp gui/serversettings.cpp gui/httpserversettings.cpp gui/streamspage.cpp @@ -151,7 +152,8 @@ SET( CANTATA_MOC_HDRS gui/serverinfopage.h gui/streamdialog.h gui/streamcategorydialog.h - gui/playbacksettings.h + gui/serverplaybacksettings.h + gui/serversettings.h gui/preferencesdialog.h gui/interfacesettings.h gui/externalsettings.h @@ -206,6 +208,7 @@ SET( CANTATA_UIS gui/interfacesettings.ui gui/externalsettings.ui gui/playbacksettings.ui + gui/serverplaybacksettings.ui gui/serversettings.ui gui/httpserversettings.ui gui/tageditor.ui diff --git a/ChangeLog b/ChangeLog index d1be1dae8..60b7ae161 100644 --- a/ChangeLog +++ b/ChangeLog @@ -21,6 +21,9 @@ Beliaev for the patch. 13. When adding items to the playqueue, sort the selected items based upon their QModelIndex. +14. Add support for multiple MPD servers. Only 1 is ever active. If there + are 2 or more servers defined, the settings menu will contain a sub menu + allow quick access to each server. 0.7.1 ----- diff --git a/devices/actiondialog.cpp b/devices/actiondialog.cpp index 4bd433b3c..fd17a4215 100644 --- a/devices/actiondialog.cpp +++ b/devices/actiondialog.cpp @@ -112,7 +112,7 @@ void ActionDialog::copy(const QString &srcUdi, const QString &dstUdi, const QLis capacityString=dev->capacityString(); usedCapacity=dev->usedCapacity(); } else { - KDiskFreeSpaceInfo inf=KDiskFreeSpaceInfo::freeSpaceInfo(Settings::self()->mpdDir()); + KDiskFreeSpaceInfo inf=KDiskFreeSpaceInfo::freeSpaceInfo(MPDConnection::self()->getDetails().dir); spaceAvailable=inf.size()-inf.used(); usedCapacity=(inf.used()*1.0)/(inf.size()*1.0); capacityString=i18n("%1 free", KGlobal::locale()->formatByteSize(inf.size()-inf.used()), 1); @@ -161,7 +161,7 @@ void ActionDialog::remove(const QString &udi, const QList &songs) QString baseDir; if (udi.isEmpty()) { - baseDir=Settings::self()->mpdDir(); + baseDir=MPDConnection::self()->getDetails().dir; } else { Device *dev=getDevice(udi); @@ -320,7 +320,7 @@ void ActionDialog::doNext() performingAction=true; if (copyToDev) { destFile=dev->path()+dev->options().createFilename(currentSong); - currentSong.file=Settings::self()->mpdDir()+currentSong.file; + currentSong.file=MPDConnection::self()->getDetails().dir+currentSong.file; dev->addSong(currentSong, overwrite->isChecked()); } else { Song copy=currentSong; @@ -328,14 +328,14 @@ void ActionDialog::doNext() Device::fixVariousArtists(QString(), copy, false); } QString fileName=namingOptions.createFilename(copy); - destFile=Settings::self()->mpdDir()+fileName; - dev->copySongTo(currentSong, Settings::self()->mpdDir(), fileName, overwrite->isChecked()); + destFile=MPDConnection::self()->getDetails().dir+fileName; + dev->copySongTo(currentSong, MPDConnection::self()->getDetails().dir, fileName, overwrite->isChecked()); } } } else { if (sourceUdi.isEmpty()) { performingAction=true; - currentSong.file=Settings::self()->mpdDir()+currentSong.file; + currentSong.file=MPDConnection::self()->getDetails().dir+currentSong.file; removeSong(currentSong); } else { Device *dev=getDevice(sourceUdi); @@ -358,7 +358,7 @@ void ActionDialog::doNext() dev->cleanDirs(dirsToClean); } else { foreach (const QString &d, dirsToClean) { - Utils::cleanDir(d, Settings::self()->mpdDir(), QString()); + Utils::cleanDir(d, MPDConnection::self()->getDetails().dir, QString()); } } } @@ -449,7 +449,7 @@ void ActionDialog::configure(const QString &udi) connect(dlg, SIGNAL(updatedSettings(const QString &, const QString &, const DeviceOptions &)), SLOT(saveProperties(const QString &, const QString &, const DeviceOptions &))); dlg->setCaption(i18n("Local Music Library Properties")); - dlg->show(Settings::self()->mpdDir(), QString(), namingOptions, DevicePropertiesWidget::Prop_Basic); + dlg->show(MPDConnection::self()->getDetails().dir, QString(), namingOptions, DevicePropertiesWidget::Prop_Basic); } else { Device *dev=DevicesModel::self()->device(udi); if (dev) { diff --git a/devices/trackorganiser.cpp b/devices/trackorganiser.cpp index 4df1b70fa..d34cdb484 100644 --- a/devices/trackorganiser.cpp +++ b/devices/trackorganiser.cpp @@ -215,7 +215,7 @@ void TrackOrganiser::renameFile() musicFolder=dev->path(); } else #endif - musicFolder=Settings::self()->mpdDir(); + musicFolder=MPDConnection::self()->getDetails().dir; if (modified!=s.file) { QString source=musicFolder+s.file; diff --git a/devices/utils.cpp b/devices/utils.cpp index 5690a409f..8143a4ea3 100644 --- a/devices/utils.cpp +++ b/devices/utils.cpp @@ -40,6 +40,24 @@ #include #endif +QString Utils::strippedText(QString s) +{ + s.remove(QString::fromLatin1("...")); + int i = 0; + while (i < s.size()) { + ++i; + if (s.at(i - 1) != QLatin1Char('&')) { + continue; + } + + if (i < s.size() && s.at(i) == QLatin1Char('&')) { + ++i; + } + s.remove(i - 1, 1); + } + return s.trimmed(); +} + QString Utils::dirSyntax(const QString &d) { if(!d.isEmpty()) diff --git a/devices/utils.h b/devices/utils.h index 5270d4574..9b10ef9f7 100644 --- a/devices/utils.h +++ b/devices/utils.h @@ -38,6 +38,7 @@ namespace Utils return (fabs(d1 - d2) < precision); } + extern QString strippedText(QString s); extern QString dirSyntax(const QString &d); extern QString getDir(const QString &file); extern QString changeExtension(const QString &file, const QString &extension); diff --git a/dynamic/dynamic.cpp b/dynamic/dynamic.cpp index d0fefe248..fe17218cb 100644 --- a/dynamic/dynamic.cpp +++ b/dynamic/dynamic.cpp @@ -389,9 +389,9 @@ bool Dynamic::controlApp(bool isStart) if (isStart) { QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); - QString p=Settings::self()->connectionPasswd(); - env.insert("MPD_HOST", p.isEmpty() ? Settings::self()->connectionHost() : (p+'@'+Settings::self()->connectionHost())); - env.insert("MPD_PORT", QString::number(Settings::self()->connectionPort())); + MPDConnectionDetails details=MPDConnection::self()->getDetails(); + env.insert("MPD_HOST", details.password.isEmpty() ? details.hostname : (details.password+'@'+details.hostname)); + env.insert("MPD_PORT", QString::number(details.port)); process.setProcessEnvironment(env); } process.start(cmd, QStringList() << QLatin1String(isStart ? "start" : "stop"), QIODevice::WriteOnly); diff --git a/gui/albumspage.cpp b/gui/albumspage.cpp index b2fd2e240..45172edd0 100644 --- a/gui/albumspage.cpp +++ b/gui/albumspage.cpp @@ -210,7 +210,7 @@ void AlbumsPage::controlActions() mw->addToPlayQueueAction->setEnabled(enable); mw->replacePlayQueueAction->setEnabled(enable); mw->addToStoredPlaylistAction->setEnabled(enable); - mw->organiseFilesAction->setEnabled(enable && Settings::self()->canReadMpdDir()); + mw->organiseFilesAction->setEnabled(enable && MPDConnection::self()->getDetails().dirReadable); mw->editTagsAction->setEnabled(mw->organiseFilesAction->isEnabled()); #ifdef ENABLE_REPLAYGAIN_SUPPORT mw->replaygainAction->setEnabled(mw->organiseFilesAction->isEnabled()); diff --git a/gui/covers.cpp b/gui/covers.cpp index 20958aa9c..807bdc6ea 100644 --- a/gui/covers.cpp +++ b/gui/covers.cpp @@ -25,6 +25,7 @@ #include "song.h" #include "utils.h" #include "mpdparseutils.h" +#include "mpdconnection.h" #include "maiaXmlRpcClient.h" #include "networkaccessmanager.h" #include "network.h" @@ -74,7 +75,8 @@ void initCoverNames() static bool canSaveTo(const QString &dir) { - return !dir.isEmpty() && !Settings::self()->mpdDir().isEmpty() && QDir(Settings::self()->mpdDir()).exists() && dir.startsWith(Settings::self()->mpdDir()); + QString mpdDir=MPDConnection::self()->getDetails().dir; + return !dir.isEmpty() && !mpdDir.isEmpty() && QDir(mpdDir).exists() && dir.startsWith(mpdDir); } static const QString typeFromRaw(const QByteArray &raw) @@ -96,7 +98,7 @@ static QString save(const QString &mimeType, const QString &extension, const QSt QFile f(filePrefix+mimeType); if (f.open(QIODevice::WriteOnly) && raw.size()==f.write(raw)) { - if (!Settings::self()->mpdDir().isEmpty() && filePrefix.startsWith(Settings::self()->mpdDir())) { + if (!MPDConnection::self()->getDetails().dir.isEmpty() && filePrefix.startsWith(MPDConnection::self()->getDetails().dir)) { Utils::setFilePerms(filePrefix+mimeType); } return filePrefix+mimeType; @@ -109,7 +111,7 @@ static QString save(const QString &mimeType, const QString &extension, const QSt } if (img.save(filePrefix+extension)) { - if (!Settings::self()->mpdDir().isEmpty() && filePrefix.startsWith(Settings::self()->mpdDir())) { + if (!MPDConnection::self()->getDetails().dir.isEmpty() && filePrefix.startsWith(MPDConnection::self()->getDetails().dir)) { Utils::setFilePerms(filePrefix+mimeType); } return filePrefix+extension; @@ -432,9 +434,9 @@ Covers::Image Covers::getImage(const Song &song) songFile=u.hasQueryItem("file") ? u.queryItemValue("file") : QString(); } if (!songFile.isEmpty() && - (haveAbsPath || !Settings::self()->mpdDir().isEmpty())) { - dirName=songFile.endsWith('/') ? (haveAbsPath ? QString() : Settings::self()->mpdDir())+songFile - : MPDParseUtils::getDir((haveAbsPath ? QString() : Settings::self()->mpdDir())+songFile); + (haveAbsPath || !MPDConnection::self()->getDetails().dir.isEmpty())) { + dirName=songFile.endsWith('/') ? (haveAbsPath ? QString() : MPDConnection::self()->getDetails().dir)+songFile + : MPDParseUtils::getDir((haveAbsPath ? QString() : MPDConnection::self()->getDetails().dir)+songFile); initCoverNames(); foreach (const QString &fileName, coverFileNames) { if (QFile::exists(dirName+fileName)) { @@ -563,9 +565,9 @@ void Covers::download(const Song &song) bool haveAbsPath=song.file.startsWith('/'); QString dirName; - if (haveAbsPath || !Settings::self()->mpdDir().isEmpty()) { - dirName=song.file.endsWith('/') ? (haveAbsPath ? QString() : Settings::self()->mpdDir())+song.file - : MPDParseUtils::getDir((haveAbsPath ? QString() : Settings::self()->mpdDir())+song.file); + if (haveAbsPath || !MPDConnection::self()->getDetails().dir.isEmpty()) { + dirName=song.file.endsWith('/') ? (haveAbsPath ? QString() : MPDConnection::self()->getDetails().dir)+song.file + : MPDParseUtils::getDir((haveAbsPath ? QString() : MPDConnection::self()->getDetails().dir)+song.file); } Job job(song, dirName); diff --git a/gui/folderpage.cpp b/gui/folderpage.cpp index 5e6b537a9..5b599e111 100644 --- a/gui/folderpage.cpp +++ b/gui/folderpage.cpp @@ -143,7 +143,7 @@ void FolderPage::controlActions() mw->addToPlayQueueAction->setEnabled(enable); mw->replacePlayQueueAction->setEnabled(enable); mw->addToStoredPlaylistAction->setEnabled(enable); - mw->organiseFilesAction->setEnabled(enable && Settings::self()->canReadMpdDir()); + mw->organiseFilesAction->setEnabled(enable && MPDConnection::self()->getDetails().dirReadable); mw->editTagsAction->setEnabled(mw->organiseFilesAction->isEnabled()); #ifdef ENABLE_REPLAYGAIN_SUPPORT mw->replaygainAction->setEnabled(mw->organiseFilesAction->isEnabled()); @@ -155,7 +155,7 @@ void FolderPage::controlActions() #ifdef ENABLE_KDE_SUPPORT browseAction->setEnabled(false); - if (1==selected.count() && QDir(Settings::self()->mpdDir()).isReadable()) { + if (1==selected.count() && MPDConnection::self()->getDetails().dirReadable) { DirViewItem *item = static_cast(proxy.mapToSource(selected.at(0)).internalPointer()); browseAction->setEnabled(DirViewItem::Type_Dir==item->type()); } @@ -185,7 +185,7 @@ void FolderPage::openFileManager() DirViewItem *item = static_cast(proxy.mapToSource(selected.at(0)).internalPointer()); if (DirViewItem::Type_Dir==item->type()) { - KRun::runUrl(KUrl(Settings::self()->mpdDir()+item->fullName()), "inode/directory", this); + KRun::runUrl(KUrl(MPDConnection::self()->getDetails().dir+item->fullName()), "inode/directory", this); } } #endif diff --git a/gui/librarypage.cpp b/gui/librarypage.cpp index 7963be3c1..da837892a 100644 --- a/gui/librarypage.cpp +++ b/gui/librarypage.cpp @@ -257,7 +257,7 @@ void LibraryPage::controlActions() mw->addToPlayQueueAction->setEnabled(enable); mw->replacePlayQueueAction->setEnabled(enable); mw->addToStoredPlaylistAction->setEnabled(enable); - mw->organiseFilesAction->setEnabled(enable && Settings::self()->canReadMpdDir()); + mw->organiseFilesAction->setEnabled(enable && MPDConnection::self()->getDetails().dirReadable); mw->editTagsAction->setEnabled(mw->organiseFilesAction->isEnabled()); #ifdef ENABLE_REPLAYGAIN_SUPPORT mw->replaygainAction->setEnabled(mw->organiseFilesAction->isEnabled()); diff --git a/gui/mainwindow.cpp b/gui/mainwindow.cpp index 360361b4b..6c0e3def9 100644 --- a/gui/mainwindow.cpp +++ b/gui/mainwindow.cpp @@ -382,6 +382,9 @@ MainWindow::MainWindow(QWidget *parent) connectAction = actionCollection()->addAction("connect"); connectAction->setText(i18n("Connect")); + connectionsAction = actionCollection()->addAction("connections"); + connectionsAction->setText(i18n("Connection")); + refreshAction = actionCollection()->addAction("refresh"); refreshAction->setText(i18n("Refresh Database")); @@ -545,6 +548,7 @@ MainWindow::MainWindow(QWidget *parent) smallControlButtonsAction = new QAction(tr("Small Control Buttons"), this); refreshAction = new QAction(tr("Refresh"), this); connectAction = new QAction(tr("Connect"), this); + connectionsAction = new QAction(tr("Connections"), this); prevTrackAction = new QAction(tr("Previous Track"), this); nextTrackAction = new QAction(tr("Next Track"), this); playPauseTrackAction = new QAction(tr("Play/Pause"), this); @@ -685,6 +689,9 @@ MainWindow::MainWindow(QWidget *parent) expandInterfaceAction->setIcon(Icon("view-media-playlist")); refreshAction->setIcon(Icon("view-refresh")); connectAction->setIcon(Icon("network-connect")); + connectionsAction->setIcon(Icon("network-server")); + connectionsAction->setMenu(new QMenu(this)); + connectionsGroup=new QActionGroup(connectionsAction->menu()); #ifdef ENABLE_KDE_SUPPORT libraryTabAction->setIcon(Icon("cantata-view-media-library")); #else @@ -882,6 +889,7 @@ MainWindow::MainWindow(QWidget *parent) mainMenu->addAction(expandInterfaceAction); QAction *menuAct=mainMenu->addAction(tr("Configure Cantata..."), this, SLOT(showPreferencesDialog())); menuAct->setIcon(Icon("configure")); + mainMenu->addAction(connectionsAction); #ifdef ENABLE_KDE_SUPPORT mainMenu->addAction(actionCollection()->action(KStandardAction::name(KStandardAction::KeyBindings))); mainMenu->addSeparator(); @@ -915,7 +923,7 @@ MainWindow::MainWindow(QWidget *parent) connect(this, SIGNAL(currentSong()), MPDConnection::self(), SLOT(currentSong())); connect(this, SIGNAL(setSeekId(quint32, quint32)), MPDConnection::self(), SLOT(setSeekId(quint32, quint32))); connect(this, SIGNAL(startPlayingSongId(quint32)), MPDConnection::self(), SLOT(startPlayingSongId(quint32))); - connect(this, SIGNAL(setDetails(const QString &, quint16, const QString &)), MPDConnection::self(), SLOT(setDetails(const QString &, quint16, const QString &))); + connect(this, SIGNAL(setDetails(const MPDConnectionDetails &)), MPDConnection::self(), SLOT(setDetails(const MPDConnectionDetails &))); connect(&playQueueModel, SIGNAL(statsUpdated(int, int, int, quint32)), this, SLOT(updatePlayQueueStats(int, int, int, quint32))); playQueueProxyModel.setSourceModel(&playQueueModel); @@ -948,6 +956,7 @@ MainWindow::MainWindow(QWidget *parent) connect(MPDConnection::self(), SIGNAL(storedPlayListUpdated()), MPDConnection::self(), SLOT(listPlaylists())); connect(MPDConnection::self(), SIGNAL(stateChanged(bool)), SLOT(mpdConnectionStateChanged(bool))); connect(MPDConnection::self(), SIGNAL(error(const QString &, bool)), SLOT(showError(const QString &, bool))); + connect(MPDConnection::self(), SIGNAL(dirChanged()), SLOT(checkMpdDir())); #ifndef Q_WS_WIN connect(Dynamic::self(), SIGNAL(error(const QString &)), SLOT(showError(const QString &))); connect(Dynamic::self(), SIGNAL(running(bool)), dynamicLabel, SLOT(setVisible(bool))); @@ -1142,7 +1151,7 @@ void MainWindow::load(const QList &urls) QStringList useable; bool haveHttp=HttpServer::self()->isAlive(); bool alwaysUseHttp=haveHttp && Settings::self()->alwaysUseHttp(); - bool mpdLocal=MPDConnection::self()->isLocal(); + bool mpdLocal=MPDConnection::self()->getDetails().isLocal(); bool allowLocal=haveHttp || mpdLocal; foreach (QUrl u, urls) { @@ -1244,6 +1253,13 @@ void MainWindow::showError(const QString &message, bool showActions) } } +void MainWindow::showInformation(const QString &message) +{ + messageWidget->setInformation(message); + messageWidget->removeAction(prefAction); + messageWidget->removeAction(connectAction); +} + void MainWindow::messageWidgetVisibility(bool v) { if (v && !splitter->isVisible()) { @@ -1273,7 +1289,6 @@ void MainWindow::mpdConnectionStateChanged(bool connected) playQueueModel.clear(); lyricsPage->text->clear(); serverInfoPage->clear(); - QString host=MPDConnection::self()->getHost(); connectedState=CS_Disconnected; } } @@ -1322,10 +1337,30 @@ void MainWindow::playQueueItemsSelected(bool s) } } -void MainWindow::connectToMpd() +void MainWindow::connectToMpd(const MPDConnectionDetails &details) { messageWidget->hide(); - emit setDetails(Settings::self()->connectionHost(), Settings::self()->connectionPort(), Settings::self()->connectionPasswd()); + + if (details!=MPDConnection::self()->getDetails()) { + libraryPage->clear(); + albumsPage->clear(); + folderPage->clear(); + playlistsPage->clear(); + playQueueModel.clear(); + lyricsPage->text->clear(); + serverInfoPage->clear(); + #ifndef Q_WS_WIN + Dynamic::self()->stop(); + #endif + } + + showInformation(i18n("Connecting to %1").arg(details.description())); + emit setDetails(details); +} + +void MainWindow::connectToMpd() +{ + connectToMpd(Settings::self()->connectionDetails()); } void MainWindow::refresh() @@ -1335,62 +1370,49 @@ void MainWindow::refresh() emit getStats(); } +#define DIALOG_ERROR MessageBox::error(this, i18n("Action is not currently possible, due to other open dialogs.")); return + void MainWindow::showPreferencesDialog() { static bool showing=false; if (!showing) { + if (0!=TagEditor::instanceCount()) { + DIALOG_ERROR; + } + #ifdef ENABLE_DEVICES_SUPPORT + if (0!=ActionDialog::instanceCount() || 0!=TrackOrganiser::instanceCount() || 0!=SyncDialog::instanceCount()) { + DIALOG_ERROR; + } + #endif + #ifdef ENABLE_REPLAYGAIN_SUPPORT + if (0!=RgDialog::instanceCount()) { + DIALOG_ERROR; + } + #endif + showing=true; PreferencesDialog pref(this, lyricsPage); connect(&pref, SIGNAL(settingsSaved()), this, SLOT(updateSettings())); + connect(&pref, SIGNAL(connectTo(const MPDConnectionDetails &)), this, SLOT(connectToMpd(const MPDConnectionDetails &))); pref.exec(); showing=false; } } -void MainWindow::readSettings() +void MainWindow::checkMpdDir() { - // Force MPD dir setting to be read, and then checked if it is readable... - Settings::self()->mpdDir(); - Covers::self()->setSaveInMpdDir(Settings::self()->storeCoversInMpdDir()); - HttpServer::self()->setPort(Settings::self()->enableHttp() ? Settings::self()->httpPort() : 0); - editPlayQueueTagsAction->setEnabled(Settings::self()->canReadMpdDir()); + editPlayQueueTagsAction->setEnabled(MPDConnection::self()->getDetails().dirReadable); organiseFilesAction->setEnabled(editPlayQueueTagsAction->isEnabled()); #ifdef ENABLE_DEVICES_SUPPORT copyToDeviceAction->setEnabled(editPlayQueueTagsAction->isEnabled()); deleteSongsAction->setEnabled(editPlayQueueTagsAction->isEnabled()); // burnAction->setEnabled(copyToDeviceAction->isEnabled()); - deleteSongsAction->setVisible(Settings::self()->showDeleteAction()); #endif #ifdef ENABLE_REPLAYGAIN_SUPPORT replaygainAction->setEnabled(editPlayQueueTagsAction->isEnabled()); #endif - lyricsPage->setEnabledProviders(Settings::self()->lyricProviders()); - MPDParseUtils::setGroupSingle(Settings::self()->groupSingle()); - MPDParseUtils::setGroupMultiple(Settings::self()->groupMultiple()); - albumsPage->setView(Settings::self()->albumsView()); - AlbumsModel::self()->setAlbumSort(Settings::self()->albumSort()); - - #ifdef PHONON_FOUND - streamButton->setVisible(!Settings::self()->streamUrl().isEmpty()); - if (phononStream && streamButton->isVisible()) { - phononStream->setCurrentSource(Settings::self()->streamUrl()); - } - #endif - libraryPage->setView(0==Settings::self()->libraryView()); - playlistsPage->setView(Settings::self()->playlistsView()); - streamsPage->setView(0==Settings::self()->streamsView()); - folderPage->setView(0==Settings::self()->folderView()); - #ifdef ENABLE_DEVICES_SUPPORT - devicesPage->setView(0==Settings::self()->devicesView()); - #endif - setupTrayIcon(); - #ifndef Q_WS_WIN - toggleDockManager(); - toggleMpris(); - #endif - autoScrollPlayQueue=Settings::self()->playQueueScroll(); switch (tabWidget->current_index()) { #ifdef ENABLE_DEVICES_SUPPORT case PAGE_DEVICES: devicesPage->controlActions(); break; @@ -1410,10 +1432,72 @@ void MainWindow::readSettings() case PAGE_SERVER_INFO: break; default: break; } +} + +void MainWindow::readSettings() +{ + checkMpdDir(); + Covers::self()->setSaveInMpdDir(Settings::self()->storeCoversInMpdDir()); + HttpServer::self()->setPort(Settings::self()->enableHttp() ? Settings::self()->httpPort() : 0); + #ifdef ENABLE_DEVICES_SUPPORT + deleteSongsAction->setVisible(Settings::self()->showDeleteAction()); + #endif + lyricsPage->setEnabledProviders(Settings::self()->lyricProviders()); + MPDParseUtils::setGroupSingle(Settings::self()->groupSingle()); + MPDParseUtils::setGroupMultiple(Settings::self()->groupMultiple()); + albumsPage->setView(Settings::self()->albumsView()); + AlbumsModel::self()->setAlbumSort(Settings::self()->albumSort()); + #ifdef PHONON_FOUND streamButton->setVisible(!Settings::self()->streamUrl().isEmpty()); streamPlayAction->setChecked(Settings::self()->playStream()); + if (phononStream && streamButton->isVisible()) { + phononStream->setCurrentSource(Settings::self()->streamUrl()); + } #endif + libraryPage->setView(0==Settings::self()->libraryView()); + playlistsPage->setView(Settings::self()->playlistsView()); + streamsPage->setView(0==Settings::self()->streamsView()); + folderPage->setView(0==Settings::self()->folderView()); + #ifdef ENABLE_DEVICES_SUPPORT + devicesPage->setView(0==Settings::self()->devicesView()); + #endif + setupTrayIcon(); + #ifndef Q_WS_WIN + toggleDockManager(); + toggleMpris(); + #endif + autoScrollPlayQueue=Settings::self()->playQueueScroll(); + + QList connections=Settings::self()->allConnections(); + if (connections.count()<2) { + connectionsAction->setVisible(false); + } else { + connectionsAction->setVisible(true); + QSet cfg; + QSet menuItems; + QMenu *menu=connectionsAction->menu(); + foreach (const MPDConnectionDetails &d, connections) { + cfg.insert(d.name); + } + + foreach (QAction *act, menu->actions()) { + menuItems.insert(act->data().toString()); + } + + if (menuItems!=cfg) { + menu->clear(); + qSort(connections); + QString current=Settings::self()->currentConnection(); + foreach (const MPDConnectionDetails &d, connections) { + QAction *act=menu->addAction(d.name.isEmpty() ? i18n("Default") : d.name, this, SLOT(changeConnection())); + act->setData(d.name); + act->setCheckable(true); + act->setChecked(d.name==current); + act->setActionGroup(connectionsGroup); + } + } + } } void MainWindow::updateSettings() @@ -1484,6 +1568,41 @@ void MainWindow::updateSettings() } } +void MainWindow::changeConnection() +{ + bool allowChange=true; + if (0!=TagEditor::instanceCount()) { + allowChange=false; + } + #ifdef ENABLE_DEVICES_SUPPORT + if (0!=ActionDialog::instanceCount() || 0!=TrackOrganiser::instanceCount() || 0!=SyncDialog::instanceCount()) { + allowChange=false; + } + #endif + #ifdef ENABLE_REPLAYGAIN_SUPPORT + if (0!=RgDialog::instanceCount()) { + allowChange=false; + } + #endif + + if (allowChange) { + QAction *act=qobject_cast(sender()); + + if (act) { + Settings::self()->saveCurrentConnection(act->data().toString()); + connectToMpd(); + } + } else { + QString current=Settings::self()->currentConnection(); + foreach (QAction *act, connectionsAction->menu()->actions()) { + if (act->data().toString()==current) { + act->setChecked(true); + } + break; + } + } +} + #ifndef ENABLE_KDE_SUPPORT void MainWindow::showAboutDialog() { @@ -2682,8 +2801,6 @@ void MainWindow::editPlayQueueTags() editTags(playQueue->selectedSongs(), true); } -#define DIALOG_ERROR MessageBox::error(this, i18n("Action is not currently possible, due to other open dialogs.")); return - void MainWindow::editTags(const QList &songs, bool isPlayQueue) { if (songs.isEmpty()) { diff --git a/gui/mainwindow.h b/gui/mainwindow.h index 705fa65e8..cb947ab60 100644 --- a/gui/mainwindow.h +++ b/gui/mainwindow.h @@ -46,6 +46,7 @@ #include "playqueuemodel.h" #include "playqueueproxymodel.h" #include "mpdstatus.h" +#include "mpdconnection.h" #include "song.h" #include "config.h" #ifdef PHONON_FOUND @@ -85,6 +86,7 @@ class Mpris; #endif class QTimer; class QPropertyAnimation; +class QActionGroup; class DeleteKeyEventHandler : public QObject { @@ -208,7 +210,7 @@ private: Q_SIGNALS: // These are for communicating with MPD object (which is in its own thread, so need to talk via signal/slots) - void setDetails(const QString &host, quint16 port, const QString &pass); + void setDetails(const MPDConnectionDetails &det); void removeSongs(const QList &); void pause(bool p); void play(); @@ -224,6 +226,7 @@ Q_SIGNALS: public Q_SLOTS: void showError(const QString &message, bool showActions=false); + void showInformation(const QString &message); void showPage(const QString &page, bool focusSearch); #ifndef Q_WS_WIN void dynamicStatus(const QString &message); @@ -242,7 +245,9 @@ private Q_SLOTS: void showVolumeControl(); void showPreferencesDialog(); void updateSettings(); + void changeConnection(); void connectToMpd(); + void connectToMpd(const MPDConnectionDetails &details); void refresh(); #ifndef ENABLE_KDE_SUPPORT void showAboutDialog(); @@ -322,6 +327,7 @@ private Q_SLOTS: void focusSearch(); void expandAll(); void collapseAll(); + void checkMpdDir(); private: void readSettings(); @@ -351,6 +357,8 @@ private: VolumeControl *volumeControl; Action *prefAction; Action *connectAction; + Action *connectionsAction; + QActionGroup *connectionsGroup; Action *prevTrackAction; Action *nextTrackAction; Action *playPauseTrackAction; diff --git a/gui/playbacksettings.cpp b/gui/playbacksettings.cpp index 1f250378c..fece3a2a8 100644 --- a/gui/playbacksettings.cpp +++ b/gui/playbacksettings.cpp @@ -22,108 +22,29 @@ */ #include "playbacksettings.h" -#include "mpdconnection.h" #include "settings.h" #include "localize.h" -#include -#include "config.h" - -static const int constIconSize=48; PlaybackSettings::PlaybackSettings(QWidget *p) : QWidget(p) { setupUi(this); - replayGain->addItem(i18n("None"), QVariant("off")); - replayGain->addItem(i18n("Track"), QVariant("track")); - replayGain->addItem(i18n("Album"), QVariant("album")); stopFadeDuration->setSpecialValueText(i18n("Do not fadeout")); stopFadeDuration->setSuffix(i18n(" ms")); stopFadeDuration->setRange(Settings::MinFade, Settings::MaxFade); stopFadeDuration->setSingleStep(100); - connect(MPDConnection::self(), SIGNAL(replayGain(const QString &)), this, SLOT(replayGainSetting(const QString &))); - connect(MPDConnection::self(), SIGNAL(outputsUpdated(const QList &)), this, SLOT(updateOutpus(const QList &))); - connect(MPDConnection::self(), SIGNAL(stateChanged(bool)), this, SLOT(mpdConnectionStateChanged(bool))); - connect(this, SIGNAL(enable(int)), MPDConnection::self(), SLOT(enableOutput(int))); - connect(this, SIGNAL(disable(int)), MPDConnection::self(), SLOT(disableOutput(int))); - connect(this, SIGNAL(outputs()), MPDConnection::self(), SLOT(outputs())); - connect(this, SIGNAL(setReplayGain(const QString &)), MPDConnection::self(), SLOT(setReplayGain(const QString &))); - connect(this, SIGNAL(setCrossFade(int)), MPDConnection::self(), SLOT(setCrossFade(int))); - connect(this, SIGNAL(getReplayGain()), MPDConnection::self(), SLOT(getReplayGain())); - #ifndef PHONON_FOUND - streamUrl->setVisible(false); - streamUrlLabel->setVisible(false); - streamUrlInfoLabel->setVisible(false); - #endif - mpdConnectionStateChanged(MPDConnection::self()->isConnected()); - errorIcon->setMinimumSize(constIconSize, constIconSize); - errorIcon->setMaximumSize(constIconSize, constIconSize); - errorIcon->setPixmap(QIcon::fromTheme("dialog-warning").pixmap(constIconSize, constIconSize)); }; void PlaybackSettings::load() { - crossfading->setValue(MPDStatus::self()->crossFade()); - emit getReplayGain(); - emit outputs(); stopOnExit->setChecked(Settings::self()->stopOnExit()); stopDynamizerOnExit->setChecked(Settings::self()->stopDynamizerOnExit()); stopFadeDuration->setValue(Settings::self()->stopFadeDuration()); - #ifdef PHONON_FOUND - streamUrl->setText(Settings::self()->streamUrl()); - #endif } void PlaybackSettings::save() { - emit setCrossFade(crossfading->value()); - emit setReplayGain(replayGain->itemData(replayGain->currentIndex()).toString()); Settings::self()->saveStopOnExit(stopOnExit->isChecked()); Settings::self()->saveStopDynamizerOnExit(stopDynamizerOnExit->isChecked()); Settings::self()->saveStopFadeDuration(stopFadeDuration->value()); - for (int i=0; icount(); ++i) { - QListWidgetItem *item=view->item(i); - - if (Qt::Checked==item->checkState()) { - emit enable(item->data(Qt::UserRole).toInt()); - } else { - emit disable(item->data(Qt::UserRole).toInt()); - } - } - #ifdef PHONON_FOUND - Settings::self()->saveStreamUrl(streamUrl->text().trimmed()); - #endif -} - -void PlaybackSettings::replayGainSetting(const QString &rg) -{ - replayGain->setCurrentIndex(0); - - for(int i=0; icount(); ++i) { - if (replayGain->itemData(i).toString()==rg){ - replayGain->setCurrentIndex(i); - break; - } - } -} - -void PlaybackSettings::updateOutpus(const QList &outputs) -{ - view->clear(); - foreach(const Output &output, outputs) { - QListWidgetItem *item=new QListWidgetItem(output.name, view); - item->setCheckState(output.enabled ? Qt::Checked : Qt::Unchecked); - item->setData(Qt::UserRole, output.id); - } -} - -void PlaybackSettings::mpdConnectionStateChanged(bool c) -{ - errorIcon->setVisible(!c); - errorLabel->setVisible(!c); - outputFrame->setEnabled(c); - crossfading->setEnabled(c); - replayGain->setEnabled(c); - crossfadingLabel->setEnabled(c); - replayGainLabel->setEnabled(c); } diff --git a/gui/playbacksettings.h b/gui/playbacksettings.h index 8c40736fa..a8e454053 100644 --- a/gui/playbacksettings.h +++ b/gui/playbacksettings.h @@ -30,28 +30,12 @@ class PlaybackSettings : public QWidget, private Ui::PlaybackSettings { - Q_OBJECT - public: PlaybackSettings(QWidget *p); virtual ~PlaybackSettings() { } void load(); void save(); - -Q_SIGNALS: - // These are for communicating with MPD object (which is in its own thread, so need to talk via signal/slots) - void getReplayGain(); - void setReplayGain(const QString &); - void setCrossFade(int secs); - void outputs(); - void enable(int id); - void disable(int id); - -private Q_SLOTS: - void replayGainSetting(const QString &rg); - void updateOutpus(const QList &outputs); - void mpdConnectionStateChanged(bool c); }; #endif diff --git a/gui/playbacksettings.ui b/gui/playbacksettings.ui index 0c7d93c70..ab9b166e7 100644 --- a/gui/playbacksettings.ui +++ b/gui/playbacksettings.ui @@ -6,158 +6,60 @@ 0 0 - 356 - 256 + 284 + 172 Form - + 0 - - - - QFormLayout::ExpandingFieldsGrow - - - - - Crossfading: - - - crossfading - - - - - - - seconds - - - 20 - - - - - - - Replay gain: - - - replayGain - - - - - - - - - - Fadeout on stop: - - - stopFadeDuration - - - - - - - - - - Stop playback on exit: - - - stopOnExit - - - - - - - - - - Stop dynamizer on exit: - - - stopDynamizerOnExit - - - - - - - - - - - - - - Output HTTP stream: - - - streamUrl - - - - - - - - - - + + - <i><b>NOTE:</b> Output HTTP stream is only of use if you have MPD configured to output to a HTTP stream, and you wish Cantata to be able to play that stream.</i> + Fadeout on stop: - - true + + stopFadeDuration - - - - Output + + + + + + + Stop playback on exit: + + + stopOnExit - - - - - - - - - - 0 - 0 - + + + + + + + Stop dynamizer on exit: + + stopDynamizerOnExit + + + + + - - - - <i><b>Not Connected.</b><br/>Some of the entries above cannot be modified, as Cantata is not connected to MPD.</i> - - - true - - - @@ -168,12 +70,9 @@ - crossfading - replayGain stopFadeDuration stopOnExit stopDynamizerOnExit - view diff --git a/gui/preferencesdialog.cpp b/gui/preferencesdialog.cpp index 6b280997c..d77acd76e 100644 --- a/gui/preferencesdialog.cpp +++ b/gui/preferencesdialog.cpp @@ -26,12 +26,14 @@ #include "settings.h" #include "interfacesettings.h" #include "externalsettings.h" -#include "playbacksettings.h" #include "serversettings.h" +#include "serverplaybacksettings.h" +#include "playbacksettings.h" #include "httpserversettings.h" #include "lyricsettings.h" #include "lyricspage.h" #include "localize.h" +#include "mpdconnection.h" #ifdef ENABLE_KDE_SUPPORT #include #include @@ -80,12 +82,14 @@ PreferencesDialog::PreferencesDialog(QWidget *parent, LyricsPage *lp) #endif server = new ServerSettings(widget); + serverplayback = new ServerPlaybackSettings(widget); playback = new PlaybackSettings(widget); interface = new InterfaceSettings(widget); ext = new ExternalSettings(widget); http = new HttpServerSettings(widget); lyrics = new LyricSettings(widget); server->load(); + serverplayback->load(); playback->load(); interface->load(); ext->load(); @@ -93,14 +97,15 @@ PreferencesDialog::PreferencesDialog(QWidget *parent, LyricsPage *lp) const QList &lprov=lp->getProviders(); lyrics->Load(lprov); #ifdef ENABLE_KDE_SUPPORT - KPageWidgetItem *page=widget->addPage(server, i18n("Server")); - page->setHeader(i18n("MPD Backend Settings")); + KPageWidgetItem *page=widget->addPage(server, i18n("Connection")); + page->setHeader(i18n("Connection Settings")); page->setIcon(KIcon("server-database")); + page=widget->addPage(serverplayback, i18n("Output")); + page->setHeader(i18n("Output Settings")); + page->setIcon(KIcon("speaker")); page=widget->addPage(playback, i18n("Playback")); page->setHeader(i18n("Playback Settings")); page->setIcon(KIcon("media-playback-start")); - page->setHeader(i18n("Control Active Outputs")); - page->setIcon(KIcon("speaker")); page=widget->addPage(interface, i18n("Interface")); page->setHeader(i18n("Interface Settings")); page->setIcon(KIcon("view-choose")); @@ -114,8 +119,10 @@ PreferencesDialog::PreferencesDialog(QWidget *parent, LyricsPage *lp) page->setHeader(i18n("Lyrics Settings")); page->setIcon(KIcon("view-media-lyrics")); #else - widget->AddTab(new ConfigPage(this, tr("MPD Backend Settings"), QIcon::fromTheme("server-database"), server), - QIcon::fromTheme("server-database"), tr("Server")); + widget->AddTab(new ConfigPage(this, tr("Connection Settings"), QIcon::fromTheme("server-database"), server), + QIcon::fromTheme("server-database"), tr("Connection")); + widget->AddTab(new ConfigPage(this, tr("Output Settings"), QIcon::fromTheme("speaker"), serverplayback), + QIcon::fromTheme("speaker"), tr("Output")); widget->AddTab(new ConfigPage(this, tr("Playback Settings"), QIcon::fromTheme("media-playback-start"), playback), QIcon::fromTheme("media-playback-start"), tr("Playback")); widget->AddTab(new ConfigPage(this, tr("Interface Settings"), QIcon::fromTheme("view-choose"), interface), @@ -134,13 +141,16 @@ PreferencesDialog::PreferencesDialog(QWidget *parent, LyricsPage *lp) #endif setCaption(i18n("Configure")); setMainWidget(widget); - resize(600, 450); + resize(600, 500); + connect(server, SIGNAL(connectTo(const MPDConnectionDetails &)), SIGNAL(connectTo(const MPDConnectionDetails &))); + connect(server, SIGNAL(disconnectFromMpd()), MPDConnection::self(), SLOT(disconnectMpd())); } void PreferencesDialog::writeSettings() { // *Must* save server settings first, so that MPD settings go to the correct instance! server->save(); + serverplayback->save(); playback->save(); interface->save(); ext->save(); diff --git a/gui/preferencesdialog.h b/gui/preferencesdialog.h index 7fe6d6ac7..64b522070 100644 --- a/gui/preferencesdialog.h +++ b/gui/preferencesdialog.h @@ -30,12 +30,14 @@ class ProxySettings; #endif class ServerSettings; +class ServerPlaybackSettings; class PlaybackSettings; class InterfaceSettings; class LyricSettings; class LyricsPage; class ExternalSettings; class HttpServerSettings; +class MPDConnectionDetails; class PreferencesDialog : public Dialog { @@ -52,9 +54,11 @@ private Q_SLOTS: Q_SIGNALS: void settingsSaved(); + void connectTo(const MPDConnectionDetails &details); private: ServerSettings *server; + ServerPlaybackSettings *serverplayback; PlaybackSettings *playback; InterfaceSettings *interface; ExternalSettings *ext; diff --git a/gui/serverplaybacksettings.cpp b/gui/serverplaybacksettings.cpp new file mode 100644 index 000000000..320f59e24 --- /dev/null +++ b/gui/serverplaybacksettings.cpp @@ -0,0 +1,127 @@ +/* + * Cantata + * + * Copyright (c) 2011-2012 Craig Drummond + * + * ---- + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "serverplaybacksettings.h" +#include "mpdconnection.h" +#include "localize.h" +#include "settings.h" +#include + +static const int constIconSize=48; + +ServerPlaybackSettings::ServerPlaybackSettings(QWidget *p) + : QWidget(p) +{ + setupUi(this); + replayGain->addItem(i18n("None"), QVariant("off")); + replayGain->addItem(i18n("Track"), QVariant("track")); + replayGain->addItem(i18n("Album"), QVariant("album")); + connect(MPDConnection::self(), SIGNAL(replayGain(const QString &)), this, SLOT(replayGainSetting(const QString &))); + connect(MPDConnection::self(), SIGNAL(outputsUpdated(const QList &)), this, SLOT(updateOutpus(const QList &))); + connect(MPDConnection::self(), SIGNAL(stateChanged(bool)), this, SLOT(mpdConnectionStateChanged(bool))); + connect(this, SIGNAL(enable(int)), MPDConnection::self(), SLOT(enableOutput(int))); + connect(this, SIGNAL(disable(int)), MPDConnection::self(), SLOT(disableOutput(int))); + connect(this, SIGNAL(outputs()), MPDConnection::self(), SLOT(outputs())); + connect(this, SIGNAL(setReplayGain(const QString &)), MPDConnection::self(), SLOT(setReplayGain(const QString &))); + connect(this, SIGNAL(setCrossFade(int)), MPDConnection::self(), SLOT(setCrossFade(int))); + connect(this, SIGNAL(getReplayGain()), MPDConnection::self(), SLOT(getReplayGain())); + messageIcon->setMinimumSize(constIconSize, constIconSize); + messageIcon->setMaximumSize(constIconSize, constIconSize); + mpdConnectionStateChanged(MPDConnection::self()->isConnected()); + #ifndef PHONON_FOUND + streamUrl->setVisible(false); + streamUrlLabel->setVisible(false); + streamUrlInfoLabel->setVisible(false); + #endif +}; + +void ServerPlaybackSettings::load() +{ + crossfading->setValue(MPDStatus::self()->crossFade()); + emit getReplayGain(); + emit outputs(); + #ifdef PHONON_FOUND + streamUrl->setText(Settings::self()->streamUrl()); + #endif +} + +void ServerPlaybackSettings::save() +{ + emit setCrossFade(crossfading->value()); + emit setReplayGain(replayGain->itemData(replayGain->currentIndex()).toString()); + for (int i=0; icount(); ++i) { + QListWidgetItem *item=view->item(i); + + if (Qt::Checked==item->checkState()) { + emit enable(item->data(Qt::UserRole).toInt()); + } else { + emit disable(item->data(Qt::UserRole).toInt()); + } + } + #ifdef PHONON_FOUND + Settings::self()->saveStreamUrl(streamUrl->text().trimmed()); + #endif +} + +void ServerPlaybackSettings::replayGainSetting(const QString &rg) +{ + replayGain->setCurrentIndex(0); + + for(int i=0; icount(); ++i) { + if (replayGain->itemData(i).toString()==rg){ + replayGain->setCurrentIndex(i); + break; + } + } +} + +void ServerPlaybackSettings::updateOutpus(const QList &outputs) +{ + view->clear(); + foreach(const Output &output, outputs) { + QListWidgetItem *item=new QListWidgetItem(output.name, view); + item->setCheckState(output.enabled ? Qt::Checked : Qt::Unchecked); + item->setData(Qt::UserRole, output.id); + } +} + +void ServerPlaybackSettings::mpdConnectionStateChanged(bool c) +{ + outputFrame->setEnabled(c); + crossfading->setEnabled(c); + replayGain->setEnabled(c); + crossfadingLabel->setEnabled(c); + replayGainLabel->setEnabled(c); + #ifdef PHONON_FOUND + streamUrl->setEnabled(c); + streamUrlLabel->setEnabled(c); + #endif + messageIcon->setPixmap(QIcon::fromTheme(c ? "dialog-information" : "dialog-warning").pixmap(constIconSize, constIconSize)); + if (c) { + messageLabel->setText(i18n("Connected to %1
The entries below apply to the currently connected MPD instance.
") + .arg(MPDConnection::self()->getDetails().description())); + } else { + messageLabel->setText(i18n("Not Connected.
The entries below cannot be modified, as Cantata is not connected to MPD.
")); + view->clear(); + } +} diff --git a/gui/serverplaybacksettings.h b/gui/serverplaybacksettings.h new file mode 100644 index 000000000..49d5e18d6 --- /dev/null +++ b/gui/serverplaybacksettings.h @@ -0,0 +1,57 @@ +/* + * Cantata + * + * Copyright (c) 2011-2012 Craig Drummond + * + * ---- + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef SERVERPLAYBACKSETTINGS_H +#define SERVERPLAYBACKSETTINGS_H + +#include "ui_serverplaybacksettings.h" +#include "output.h" +#include + +class ServerPlaybackSettings : public QWidget, private Ui::ServerPlaybackSettings +{ + Q_OBJECT + +public: + ServerPlaybackSettings(QWidget *p); + virtual ~ServerPlaybackSettings() { } + + void load(); + void save(); + +Q_SIGNALS: + // These are for communicating with MPD object (which is in its own thread, so need to talk via signal/slots) + void getReplayGain(); + void setReplayGain(const QString &); + void setCrossFade(int secs); + void outputs(); + void enable(int id); + void disable(int id); + +private Q_SLOTS: + void replayGainSetting(const QString &rg); + void updateOutpus(const QList &outputs); + void mpdConnectionStateChanged(bool c); +}; + +#endif diff --git a/gui/serverplaybacksettings.ui b/gui/serverplaybacksettings.ui new file mode 100644 index 000000000..f7c10dafa --- /dev/null +++ b/gui/serverplaybacksettings.ui @@ -0,0 +1,134 @@ + + + ServerPlaybackSettings + + + + 0 + 0 + 431 + 311 + + + + Form + + + + 0 + + + + + + 0 + 0 + + + + + + + + + + + <i><b>Not Connected.</b><br/>The entries below cannot be modified, as Cantata is not connected to MPD.</i> + + + true + + + + + + + QFormLayout::ExpandingFieldsGrow + + + + + Crossfading: + + + crossfading + + + + + + + seconds + + + 20 + + + + + + + Replay gain: + + + replayGain + + + + + + + + + + Output HTTP stream: + + + streamUrl + + + + + + + + + + + + <i><b>NOTE:</b> Output HTTP stream is only of use if you have MPD configured to output to a HTTP stream, and you wish Cantata to be able to play that stream.</i> + + + true + + + + + + + Output + + + + + + + + + + + + + BuddyLabel + QLabel +
buddylabel.h
+
+
+ + crossfading + replayGain + view + + + +
diff --git a/gui/serversettings.cpp b/gui/serversettings.cpp index 83dc91aba..51f1341b6 100644 --- a/gui/serversettings.cpp +++ b/gui/serversettings.cpp @@ -24,6 +24,12 @@ #include "serversettings.h" #include "settings.h" #include "localize.h" +#include "inputdialog.h" +#include "messagebox.h" +#include +#include +#include +#include ServerSettings::ServerSettings(QWidget *p) : QWidget(p) @@ -34,20 +40,150 @@ ServerSettings::ServerSettings(QWidget *p) i18n(" This folder will also be used to locate music files " "for transferring to (and from) devices.")); #endif + connect(MPDConnection::self(), SIGNAL(stateChanged(bool)), this, SLOT(mpdConnectionStateChanged(bool))); + connect(combo, SIGNAL(activated(int)), SLOT(showDetails(int))); + connect(saveButton, SIGNAL(clicked(bool)), SLOT(saveAs())); + connect(removeButton, SIGNAL(clicked(bool)), SLOT(remove())); + connect(connectButton, SIGNAL(clicked(bool)), SLOT(toggleConnection())); + saveButton->setIcon(QIcon::fromTheme("document-save-as")); + removeButton->setIcon(QIcon::fromTheme("edit-delete")); }; void ServerSettings::load() { - hostLineEdit->setText(Settings::self()->connectionHost()); - portSpinBox->setValue(Settings::self()->connectionPort()); - passwordLineEdit->setText(Settings::self()->connectionPasswd()); - mpdDir->setText(Settings::self()->mpdDir()); + QList all=Settings::self()->allConnections(); + QString currentCon=Settings::self()->currentConnection(); + MPDConnectionDetails current=MPDConnection::self()->getDetails(); + + combo->clear(); + int idx=0; + int cur=0; + foreach (const MPDConnectionDetails &d, all) { + combo->addItem(d.name.isEmpty() ? i18n("Default") : d.name, d.name); + if (d.name==currentCon) { + cur=idx; + } + idx++; + } + showDetails(cur); } void ServerSettings::save() { - Settings::self()->saveConnectionHost(hostLineEdit->text()); - Settings::self()->saveConnectionPort(portSpinBox->value()); - Settings::self()->saveConnectionPasswd(passwordLineEdit->text()); - Settings::self()->saveMpdDir(mpdDir->text()); + Settings::self()->saveConnectionDetails(getDetails()); +} + +void ServerSettings::mpdConnectionStateChanged(bool c) +{ + enableWidgets(!c || MPDConnection::self()->getDetails()!=getDetails()); +} + +void ServerSettings::showDetails(int index) +{ + MPDConnectionDetails d=Settings::self()->connectionDetails(combo->itemData(index).toString()); + setDetails(d); + enableWidgets(!MPDConnection::self()->isConnected() || MPDConnection::self()->getDetails()!=d); +} + +void ServerSettings::toggleConnection() +{ + bool con=hostLabel->isEnabled(); + enableWidgets(!con); + if (con) { + emit connectTo(getDetails()); + } else { + emit disconnectFromMpd(); + } +} + +void ServerSettings::saveAs() +{ + bool ok=false; + int currentIndex=combo->currentIndex(); + QString name=combo->itemText(currentIndex); + + for (;;) { + name=InputDialog::getText(i18n("Save As"), i18n("Enter name for settings:"), name, &ok, this).trimmed(); + if (!ok || name.isEmpty()) { + return; + } + + bool found=false; + int idx=0; + for (idx=0; idxcount() && !found; ++idx) { + if (combo->itemText(idx)==name || combo->itemData(idx).toString()==name) { + found=true; + break; + } + } + + if (found && idx!=currentIndex) { + switch (MessageBox::warningYesNoCancel(this, i18n("A setting named %1 already exists!\nOverwrite?").arg(name))) { + case MessageBox::No: + continue; + case MessageBox::Cancel: + return; + case MessageBox::Yes: + break; + } + } + + MPDConnectionDetails details=getDetails(); + details.name=name; + Settings::self()->saveConnectionDetails(details); + if (found) { + if (idx!=currentIndex) { + combo->setCurrentIndex(idx); + } + } else { + combo->addItem(details.name, details.name); + } + break; + } +} + +void ServerSettings::remove() +{ + int index=combo->currentIndex(); + QString name=combo->itemData(index).toString(); + if (combo->count()>1 && MessageBox::Yes==MessageBox::questionYesNo(this, i18n("Delete %1?").arg(name))) { + Settings::self()->removeConnectionDetails(combo->itemText(index)); + combo->removeItem(index); + } +} + +void ServerSettings::enableWidgets(bool e) +{ + host->setEnabled(e); + port->setEnabled(e); + password->setEnabled(e); + dir->setEnabled(e); + hostLabel->setEnabled(e); + portLabel->setEnabled(e); + passwordLabel->setEnabled(e); + dirLabel->setEnabled(e); + connectButton->setText(e ? i18n("Connect") : i18n("Disconnect")); + connectButton->setIcon(QIcon::fromTheme(e ? "network-connect" : "network-disconnect")); + removeButton->setEnabled(e); + saveButton->setEnabled(e); +} + +void ServerSettings::setDetails(const MPDConnectionDetails &details) +{ + host->setText(details.hostname); + port->setValue(details.port); + password->setText(details.password); + dir->setText(details.dir); +} + +MPDConnectionDetails ServerSettings::getDetails() const +{ + MPDConnectionDetails details; + details.name=combo->itemData(combo->currentIndex()).toString(); + details.hostname=host->text(); + details.port=port->value(); + details.password=password->text(); + details.dir=dir->text(); + details.dirReadable=details.dir.isEmpty() ? false : QDir(details.dir).isReadable(); + return details; } diff --git a/gui/serversettings.h b/gui/serversettings.h index adf7d30f8..ad2aa8ede 100644 --- a/gui/serversettings.h +++ b/gui/serversettings.h @@ -25,15 +25,34 @@ #define SERVERSETTINGS_H #include "ui_serversettings.h" +#include "mpdconnection.h" class ServerSettings : public QWidget, private Ui::ServerSettings { + Q_OBJECT + public: ServerSettings(QWidget *p); virtual ~ServerSettings() { } void load(); void save(); + +Q_SIGNALS: + void connectTo(const MPDConnectionDetails &details); + void disconnectFromMpd(); + +private Q_SLOTS: + void showDetails(int index); + void mpdConnectionStateChanged(bool c); + void toggleConnection(); + void saveAs(); + void remove(); + +private: + void setDetails(const MPDConnectionDetails &details); + MPDConnectionDetails getDetails() const; + void enableWidgets(bool e); }; #endif diff --git a/gui/serversettings.ui b/gui/serversettings.ui index 8eb099a03..a4b5ee29d 100644 --- a/gui/serversettings.ui +++ b/gui/serversettings.ui @@ -6,8 +6,8 @@ 0 0 - 349 - 239 + 406 + 332 @@ -18,77 +18,123 @@ 0 - - - QFormLayout::ExpandingFieldsGrow + + + + + + Details - - - - Host (or local socket): - - - hostLineEdit - - - - - - - - - - Port: - - - portSpinBox - - - - - - - 1 - - - 66000 - - - 6600 - - - - - - - Password: - - - passwordLineEdit - - - - - - - QLineEdit::Password - - - - - - - Music folder: - - - mpdDir - - - - - - -
+ + + + + QFormLayout::ExpandingFieldsGrow + + + + + Host (or local socket): + + + host + + + + + + + + + + Port: + + + port + + + + + + + 1 + + + 66000 + + + 6600 + + + + + + + Password: + + + password + + + + + + + QLineEdit::Password + + + + + + + Music folder: + + + dir + + + + + + + + + + + + Connect + + + + + + + Qt::Horizontal + + + + 165 + 20 + + + + + + + + Save As + + + + + + + Remove + + + + + @@ -158,6 +204,11 @@ + + BuddyLabel + QLabel +
buddylabel.h
+
LineEdit QLineEdit @@ -169,11 +220,6 @@
dirrequester.h
1
- - BuddyLabel - QLabel -
buddylabel.h
-
diff --git a/gui/settings.cpp b/gui/settings.cpp index 2907d287a..1eda9beff 100644 --- a/gui/settings.cpp +++ b/gui/settings.cpp @@ -47,7 +47,7 @@ Settings * Settings::self() return instance; #else static Settings *instance=0;; - if(!instance) { + if (!instance) { instance=new Settings; } return instance; @@ -112,13 +112,23 @@ struct MpdDefaults static MpdDefaults mpdDefaults; #ifdef ENABLE_KDE_SUPPORT -#define GET_STRING(KEY, DEF) (cfg.readEntry(KEY, QString(DEF))) -#define GET_STRINGLIST(KEY, DEF) (cfg.readEntry(KEY, DEF)) -#define GET_BOOL(KEY, DEF) (cfg.readEntry(KEY, DEF)) -#define GET_INT(KEY, DEF) (cfg.readEntry(KEY, DEF)) -#define GET_BYTE_ARRAY(KEY) (cfg.readEntry(KEY, QByteArray())) -#define GET_SIZE(KEY) (cfg.readEntry(KEY, QSize())) -#define SET_VALUE(KEY, V) (cfg.writeEntry(KEY, V)) +#define CFG_GET_STRING(CFG, KEY, DEF) (CFG.readEntry(KEY, QString(DEF))) +#define CFG_GET_STRINGLIST(CFG, KEY, DEF) (CFG.readEntry(KEY, DEF)) +#define CFG_GET_BOOL(CFG, KEY, DEF) (CFG.readEntry(KEY, DEF)) +#define CFG_GET_INT(CFG, KEY, DEF) (CFG.readEntry(KEY, DEF)) +#define CFG_GET_BYTE_ARRAY(CFG, KEY) (CFG.readEntry(KEY, QByteArray())) +#define CFG_GET_SIZE(CFG, KEY) (CFG.readEntry(KEY, QSize())) +#define CFG_SET_VALUE(CFG, KEY, V) (CFG.writeEntry(KEY, V)) +#define HAS_GROUP(GRP) (KGlobal::config()->hasGroup(GRP)) +#define REMOVE_GROUP(KEY) (cfg.deleteGroup(KEY)) +#define REMOVE_ENTRY(KEY) (cfg.deleteEntry(KEY)) +#define GET_STRING(KEY, DEF) CFG_GET_STRING(cfg, KEY, DEF) +#define GET_STRINGLIST(KEY, DEF) CFG_GET_STRINGLIST(cfg, KEY, DEF) +#define GET_BOOL(KEY, DEF) CFG_GET_BOOL(cfg, KEY, DEF) +#define GET_INT(KEY, DEF) CFG_GET_INT(cfg, KEY, DEF) +#define GET_BYTE_ARRAY(KEY) CFG_GET_BYTE_ARRAY(cfg, KEY) +#define GET_SIZE(KEY) CFG_GET_SIZE(cfg, KEY) +#define SET_VALUE(KEY, V) CFG_SET_VALUE(cfg, KEY, V) #else #define GET_STRING(KEY, DEF) (cfg.contains(KEY) ? cfg.value(KEY).toString() : QString(DEF)) #define GET_STRINGLIST(KEY, DEF) (cfg.contains(KEY) ? cfg.value(KEY).toStringList() : DEF) @@ -127,11 +137,18 @@ static MpdDefaults mpdDefaults; #define GET_BYTE_ARRAY(KEY) (cfg.value(KEY).toByteArray()) #define GET_SIZE(KEY) (cfg.contains(KEY) ? cfg.value(KEY).toSize() : QSize()) #define SET_VALUE(KEY, V) (cfg.setValue(KEY, V)) +#define HAS_GROUP(GRP) (cfg.contains(GRP)) +#define REMOVE_GROUP(KEY) (cfg.remove(KEY)) +#define REMOVE_ENTRY(KEY) (cfg.remove(KEY)) #endif +static QString connGroupName(const QString &n=QString()) +{ + return n.isEmpty() ? "Connection" : ("Connection-"+n); +} + Settings::Settings() - : mpdDirReadable(false) - , timer(0) + : timer(0) , ver(-1) #ifdef ENABLE_KDE_SUPPORT , cfg(KGlobal::config(), "General") @@ -139,7 +156,9 @@ Settings::Settings() #endif { // Only need to read system defaults if we have not previously been configured... - if (GET_STRING("connectionHost", QString()).isEmpty()) { + if (version()readPassword("mpd", details.password); + } + } else if (name.isEmpty()) { + details.password=mpdDefaults.passwd; + } + #else + details.password=GET_STRING("connectionPasswd", name.isEmpty() ? mpdDefaults.passwd : QString()); + #endif + details.port=GET_INT("connectionPort", name.isEmpty() ? mpdDefaults.port : 6600); + details.dir=MPDParseUtils::fixPath(GET_STRING("mpdDir", mpdDefaults.dir)); + } else { + QString n=connGroupName(name); + details.name=name; + if (HAS_GROUP(n)) { + #ifdef ENABLE_KDE_SUPPORT + KConfigGroup grp(KGlobal::config(), n); + details.hostname=CFG_GET_STRING(grp, "host", name.isEmpty() ? mpdDefaults.host : QString()); + details.port=CFG_GET_INT(grp, "port", name.isEmpty() ? mpdDefaults.port : 6600); + details.dir=MPDParseUtils::fixPath(CFG_GET_STRING(grp, "dir", name.isEmpty() ? mpdDefaults.dir : "/var/lib/mpd/music")); + if (CFG_GET_BOOL(grp, "passwd", false)) { + if (openWallet()) { + wallet->readPassword(name.isEmpty() ? "mpd" : name, details.password); + } + } else if (name.isEmpty()) { + details.password=mpdDefaults.passwd; + } + #else + cfg.beginGroup(name); + details.hostname=GET_STRING("host", name.isEmpty() ? mpdDefaults.host : QString()); + details.port=GET_INT("port", name.isEmpty() ? mpdDefaults.port : 6600); + details.dir=MPDParseUtils::fixPath(GET_STRING("dir", name.isEmpty() ? mpdDefaults.dir : "/var/lib/mpd/music")); + details.password=GET_STRING("passwd", name.isEmpty() ? mpdDefaults.passwd : QString()); + cfg.endGroup(); + #endif + } + } + details.dirReadable=details.dir.isEmpty() ? false : QDir(details.dir).isReadable(); + return details; +} + +QList Settings::allConnections() +{ + #ifdef ENABLE_KDE_SUPPORT + QStringList groups=KGlobal::config()->groupList(); + #else + QStringList groups=cfg.childGroups(); + #endif + + QList connections; + foreach (const QString &grp, groups) { + if (grp.startsWith("Connection")) { + connections.append(connectionDetails(grp=="Connection" ? QString() : grp.mid(11))); + } + } + + if (connections.isEmpty()) { + // If we are empty, add at lease the default connection... + connections.append(connectionDetails()); + } + return connections; } #ifdef ENABLE_KDE_SUPPORT bool Settings::openWallet() { - if(wallet) { + if (wallet) { return true; } wallet=KWallet::Wallet::openWallet(KWallet::Wallet::LocalWallet(), QApplication::activeWindow() ? QApplication::activeWindow()->winId() : 0); - if(wallet) { - if(!wallet->hasFolder(PACKAGE_NAME)) { + if (wallet) { + if (!wallet->hasFolder(PACKAGE_NAME)) { wallet->createFolder(PACKAGE_NAME); } wallet->setFolder(PACKAGE_NAME); @@ -176,29 +265,6 @@ bool Settings::openWallet() } #endif -QString Settings::connectionPasswd() -{ - #ifdef ENABLE_KDE_SUPPORT - if(passwd.isEmpty()) { - if (GET_BOOL("connectionPasswd", false)) { - if (openWallet()) { - wallet->readPassword("mpd", passwd); - } - } else { - passwd=mpdDefaults.passwd; - } - } - return passwd; - #else - return GET_STRING("connectionPasswd", ""); - #endif -} - -int Settings::connectionPort() -{ - return GET_INT("connectionPort", mpdDefaults.port); -} - bool Settings::showPlaylist() { return GET_BOOL("showPlaylist", true); @@ -259,15 +325,6 @@ bool Settings::smallControlButtons() return GET_BOOL("smallControlButtons", false); } -const QString & Settings::mpdDir() -{ - if (mpdDirSetting.isEmpty()) { - mpdDirSetting=MPDParseUtils::fixPath(GET_STRING("mpdDir", mpdDefaults.dir)); - mpdDirReadable=QDir(mpdDirSetting).isReadable(); - } - return mpdDirSetting; -} - bool Settings::storeCoversInMpdDir() { return GET_BOOL("storeCoversInMpdDir", true); @@ -497,35 +554,49 @@ QString Settings::streamUrl() } #endif -void Settings::saveConnectionHost(const QString &v) +void Settings::removeConnectionDetails(const QString &v) { - SET_VALUE("connectionHost", v); + REMOVE_GROUP(connGroupName(v)); } -void Settings::saveConnectionPasswd(const QString &v) +void Settings::saveConnectionDetails(const MPDConnectionDetails &v) { - #ifdef ENABLE_KDE_SUPPORT - if(v!=passwd) { - passwd=v; - SET_VALUE("connectionPasswd", !passwd.isEmpty()); + if (v.name.isEmpty()) { + REMOVE_ENTRY("connectionHost"); + REMOVE_ENTRY("connectionPasswd"); + REMOVE_ENTRY("connectionPort"); + REMOVE_ENTRY("mpdDir"); + } - if(passwd.isEmpty()) { - if(wallet) { - wallet->removeEntry("mpd"); - } - } - else if(openWallet()) { - wallet->writePassword("mpd", passwd); + QString n=connGroupName(v.name); + #ifdef ENABLE_KDE_SUPPORT + KConfigGroup grp(KGlobal::config(), n); + CFG_SET_VALUE(grp, "host", v.hostname); + CFG_SET_VALUE(grp, "port", (int)v.port); + CFG_SET_VALUE(grp, "dir", v.dir); + CFG_SET_VALUE(grp, "passwd", !v.password.isEmpty()); + QString walletEntry=v.name.isEmpty() ? "mpd" : v.name; + if (v.password.isEmpty()) { + if (wallet) { + wallet->removeEntry(walletEntry); } } + else if (openWallet()) { + wallet->writePassword(walletEntry, v.password); + } #else - SET_VALUE("connectionPasswd", v); + cfg.beginGroup(n); + SET_VALUE("host", v.hostname); + SET_VALUE("port", (int)v.port); + SET_VALUE("dir", v.dir); + SET_VALUE("passwd", v.password); + cfg.endGroup(); #endif } -void Settings::saveConnectionPort(int v) +void Settings::saveCurrentConnection(const QString &v) { - SET_VALUE("connectionPort", v); + SET_VALUE("currentConnection", v); } void Settings::saveShowPlaylist(bool v) @@ -588,13 +659,6 @@ void Settings::saveSmallControlButtons(bool v) SET_VALUE("smallControlButtons", v); } -void Settings::saveMpdDir(const QString &v) -{ - mpdDirSetting=MPDParseUtils::fixPath(v); - mpdDirReadable=QDir(mpdDirSetting).isReadable(); - SET_VALUE("mpdDir", mpdDirSetting); -} - void Settings::saveStoreCoversInMpdDir(bool v) { SET_VALUE("storeCoversInMpdDir", v); @@ -726,7 +790,7 @@ void Settings::saveStopFadeDuration(int v) { if (v<=MinFade) { v=0; - } else if(v>MaxFade) { + } else if (v>MaxFade) { v=MaxFade; } SET_VALUE("stopFadeDuration", v); diff --git a/gui/settings.h b/gui/settings.h index 17ddd2ccc..a4a867c81 100644 --- a/gui/settings.h +++ b/gui/settings.h @@ -33,6 +33,7 @@ class Wallet; #include #endif #include "config.h" +#include "mpdconnection.h" #define CANTATA_MAKE_VERSION(a, b, c) (((a) << 16) | ((b) << 8) | (c)) @@ -55,9 +56,9 @@ public: Settings(); ~Settings(); - QString connectionHost(); - QString connectionPasswd(); - int connectionPort(); + QString currentConnection(); + MPDConnectionDetails connectionDetails(const QString &name=Settings::self()->currentConnection()); + QList allConnections(); bool showPlaylist(); QByteArray playQueueHeaderState(); QByteArray splitterState(); @@ -70,8 +71,6 @@ public: bool stopDynamizerOnExit(); bool smallPlaybackButtons(); bool smallControlButtons(); - const QString & mpdDir(); - bool canReadMpdDir() const { return mpdDirReadable; } bool storeCoversInMpdDir(); bool storeLyricsInMpdDir(); int libraryView(); @@ -114,9 +113,9 @@ public: QString streamUrl(); #endif - void saveConnectionHost(const QString &v); - void saveConnectionPasswd(const QString &v); - void saveConnectionPort(int v); + void removeConnectionDetails(const QString &v); + void saveConnectionDetails(const MPDConnectionDetails &v); + void saveCurrentConnection(const QString &v); void saveShowPlaylist(bool v); void saveStopOnExit(bool v); void saveStopDynamizerOnExit(bool v); @@ -129,7 +128,6 @@ public: void saveMainWindowCollapsedSize(const QSize &v); void saveUseSystemTray(bool v); void saveShowPopups(bool v); - void saveMpdDir(const QString &v); void saveStoreCoversInMpdDir(bool v); void saveStoreLyricsInMpdDir(bool v); void saveLibraryView(int v); @@ -179,14 +177,11 @@ private Q_SLOTS: void actualSave(); private: - bool mpdDirReadable; - QString mpdDirSetting; QTimer *timer; int ver; #ifdef ENABLE_KDE_SUPPORT KConfigGroup cfg; KWallet::Wallet *wallet; - QString passwd; #else QSettings cfg; #endif diff --git a/gui/tageditor.cpp b/gui/tageditor.cpp index d92ddad9d..b48e799d3 100644 --- a/gui/tageditor.cpp +++ b/gui/tageditor.cpp @@ -81,7 +81,7 @@ TagEditor::TagEditor(QWidget *parent, const QList &songs, original=songs; #ifdef ENABLE_DEVICES_SUPPORT if (deviceUdi.isEmpty()) { - baseDir=Settings::self()->mpdDir(); + baseDir=MPDConnection::self()->getDetails().dir; } else { Device *dev=getDevice(udi, parentWidget()); @@ -93,7 +93,7 @@ TagEditor::TagEditor(QWidget *parent, const QList &songs, baseDir=dev->path(); } #else - baseDir=Settings::self()->mpdDir(); + baseDir=MPDConnection::self()->getDetails().dir; #endif qSort(original); diff --git a/lyrics/lyricspage.cpp b/lyrics/lyricspage.cpp index fe7b48d87..78dfa3f41 100644 --- a/lyrics/lyricspage.cpp +++ b/lyrics/lyricspage.cpp @@ -307,9 +307,9 @@ void LyricsPage::update(const Song &song, bool force) currentProvider=-1; } - if (!Settings::self()->mpdDir().isEmpty() && !song.file.isEmpty() && !song.isStream()) { + if (!MPDConnection::self()->getDetails().dir.isEmpty() && !song.file.isEmpty() && !song.isStream()) { // Check for MPD file... - QString mpdLyrics=changeExt(Settings::self()->mpdDir()+song.file, constExtension); + QString mpdLyrics=changeExt(MPDConnection::self()->getDetails().dir+song.file, constExtension); // if (force && QFile::exists(mpdLyrics)) { // QFile::remove(mpdLyrics); @@ -359,7 +359,7 @@ void LyricsPage::resultReady(int id, const QString &lyrics) text->setText(text->toPlainText()); lyricsFile=QString(); if (! ( Settings::self()->storeLyricsInMpdDir() && - saveFile(changeExt(Settings::self()->mpdDir()+currentSong.file, constExtension))) ) { + saveFile(changeExt(MPDConnection::self()->getDetails().dir+currentSong.file, constExtension))) ) { saveFile(cacheFile(currentSong.artist, currentSong.title, true)); } setMode(Mode_Display); @@ -382,8 +382,8 @@ bool LyricsPage::saveFile(const QString &fileName) QString LyricsPage::mpdFileName() const { - return currentSong.file.isEmpty() || Settings::self()->mpdDir().isEmpty() || currentSong.isStream() - ? QString() : changeExt(Settings::self()->mpdDir()+currentSong.file, constExtension); + return currentSong.file.isEmpty() || MPDConnection::self()->getDetails().dir.isEmpty() || currentSong.isStream() + ? QString() : changeExt(MPDConnection::self()->getDetails().dir+currentSong.file, constExtension); } QString LyricsPage::cacheFileName() const @@ -426,7 +426,7 @@ void LyricsPage::getLyrics() currentProvider=-1; // Set lyrics file anyway - so that editing is enabled! lyricsFile=Settings::self()->storeLyricsInMpdDir() - ? changeExt(Settings::self()->mpdDir()+currentSong.file, constExtension) + ? changeExt(MPDConnection::self()->getDetails().dir+currentSong.file, constExtension) : cacheFile(currentSong.artist, currentSong.title); setMode(Mode_Display); return; @@ -444,7 +444,7 @@ void LyricsPage::setMode(Mode m) saveAction->setEnabled(Mode_Edit==m); cancelAction->setEnabled(Mode_Edit==m); editAction->setEnabled(editable); - delAction->setEnabled(editable && !Settings::self()->mpdDir().isEmpty() && QFile::exists(changeExt(Settings::self()->mpdDir()+currentSong.file, constExtension))); + delAction->setEnabled(editable && !MPDConnection::self()->getDetails().dir.isEmpty() && QFile::exists(changeExt(MPDConnection::self()->getDetails().dir+currentSong.file, constExtension))); text->setReadOnly(Mode_Edit!=m); songLabel->setVisible(Mode_Edit==m); #ifdef ENABLE_KDE_SUPPORT diff --git a/models/albumsmodel.cpp b/models/albumsmodel.cpp index 6495bfae8..2433130da 100644 --- a/models/albumsmodel.cpp +++ b/models/albumsmodel.cpp @@ -353,10 +353,10 @@ QMimeData * AlbumsModel::mimeData(const QModelIndexList &indexes) const QMimeData *mimeData = new QMimeData(); QStringList files=filenames(indexes); PlayQueueModel::encode(*mimeData, PlayQueueModel::constFileNameMimeType, files); - if (!Settings::self()->mpdDir().isEmpty()) { + if (!MPDConnection::self()->getDetails().dir.isEmpty()) { QStringList paths; foreach (const QString &f, files) { - paths << Settings::self()->mpdDir()+f; + paths << MPDConnection::self()->getDetails().dir+f; } PlayQueueModel::encode(*mimeData, PlayQueueModel::constUriMimeType, paths); } diff --git a/models/dirviewmodel.cpp b/models/dirviewmodel.cpp index ac8e44539..474917a62 100644 --- a/models/dirviewmodel.cpp +++ b/models/dirviewmodel.cpp @@ -320,10 +320,10 @@ QMimeData *DirViewModel::mimeData(const QModelIndexList &indexes) const QMimeData *mimeData = new QMimeData(); QStringList files=filenames(indexes); PlayQueueModel::encode(*mimeData, PlayQueueModel::constFileNameMimeType, files); - if (!Settings::self()->mpdDir().isEmpty()) { + if (!MPDConnection::self()->getDetails().dir.isEmpty()) { QStringList paths; foreach (const QString &f, files) { - paths << Settings::self()->mpdDir()+f; + paths << MPDConnection::self()->getDetails().dir+f; } PlayQueueModel::encode(*mimeData, PlayQueueModel::constUriMimeType, paths); } diff --git a/models/musiclibrarymodel.cpp b/models/musiclibrarymodel.cpp index f245a1f49..38f49482a 100644 --- a/models/musiclibrarymodel.cpp +++ b/models/musiclibrarymodel.cpp @@ -36,6 +36,7 @@ #include "covers.h" #include "itemview.h" #include "mpdparseutils.h" +#include "mpdconnection.h" #include "network.h" #include "localize.h" #include @@ -74,7 +75,7 @@ static const QLatin1String constLibraryExt(".xml"); static const QString cacheFileName() { - QString fileName=Settings::self()->connectionHost()+constLibraryExt; + QString fileName=MPDConnection::self()->getDetails().hostname+constLibraryExt; fileName.replace('/', '_'); return Network::cacheDir(constLibraryCache)+fileName; } @@ -632,7 +633,7 @@ QList MusicLibraryModel::songs(const QModelIndexList &indexes) const foreach (const MusicLibraryItem *album, static_cast(item)->childItems()) { foreach (const MusicLibraryItem *song, static_cast(album)->childItems()) { if (MusicLibraryItem::Type_Song==song->itemType() && !songs.contains(static_cast(song)->song())) { - static_cast(song)->song().updateSize(Settings::self()->mpdDir()); + static_cast(song)->song().updateSize(MPDConnection::self()->getDetails().dir); songs << static_cast(song)->song(); } } @@ -641,14 +642,14 @@ QList MusicLibraryModel::songs(const QModelIndexList &indexes) const case MusicLibraryItem::Type_Album: foreach (const MusicLibraryItem *song, static_cast(item)->childItems()) { if (MusicLibraryItem::Type_Song==song->itemType() && !songs.contains(static_cast(song)->song())) { - static_cast(song)->song().updateSize(Settings::self()->mpdDir()); + static_cast(song)->song().updateSize(MPDConnection::self()->getDetails().dir); songs << static_cast(song)->song(); } } break; case MusicLibraryItem::Type_Song: if (!songs.contains(static_cast(item)->song())) { - static_cast(item)->song().updateSize(Settings::self()->mpdDir()); + static_cast(item)->song().updateSize(MPDConnection::self()->getDetails().dir); songs << static_cast(item)->song(); } break; @@ -691,10 +692,10 @@ QMimeData *MusicLibraryModel::mimeData(const QModelIndexList &indexes) const QMimeData *mimeData = new QMimeData(); QStringList files=filenames(indexes); PlayQueueModel::encode(*mimeData, PlayQueueModel::constFileNameMimeType, files); - if (!Settings::self()->mpdDir().isEmpty()) { + if (!MPDConnection::self()->getDetails().dir.isEmpty()) { QStringList paths; foreach (const QString &f, files) { - paths << Settings::Settings::self()->mpdDir()+f; + paths << MPDConnection::self()->getDetails().dir+f; } PlayQueueModel::encode(*mimeData, PlayQueueModel::constUriMimeType, paths); } diff --git a/models/playlistsmodel.cpp b/models/playlistsmodel.cpp index 9e46f8894..077d3bd6f 100644 --- a/models/playlistsmodel.cpp +++ b/models/playlistsmodel.cpp @@ -32,6 +32,7 @@ #include "itemview.h" #include "groupedview.h" #include "localize.h" +#include "utils.h" #ifdef ENABLE_KDE_SUPPORT #include K_GLOBAL_STATIC(PlaylistsModel, instance) @@ -681,30 +682,12 @@ void PlaylistsModel::movedInPlaylist(const QString &name, const QList & emit updated(parent); } -static QString qt_strippedText(QString s) -{ - s.remove(QString::fromLatin1("...")); - int i = 0; - while (i < s.size()) { - ++i; - if (s.at(i - 1) != QLatin1Char('&')) { - continue; - } - - if (i < s.size() && s.at(i) == QLatin1Char('&')) { - ++i; - } - s.remove(i - 1, 1); - } - return s.trimmed(); -} - void PlaylistsModel::emitAddToExisting() { QAction *act=qobject_cast(sender()); if (act) { - emit addToExisting(qt_strippedText(act->text())); + emit addToExisting(Utils::strippedText(act->text())); } } diff --git a/models/playqueuemodel.cpp b/models/playqueuemodel.cpp index 4f55f80f9..000f31b1a 100644 --- a/models/playqueuemodel.cpp +++ b/models/playqueuemodel.cpp @@ -405,7 +405,7 @@ QStringList PlayQueueModel::mimeTypes() const QStringList types; types << constMoveMimeType; types << constFileNameMimeType; - if (MPDConnection::self()->isLocal() || HttpServer::self()->isAlive()) { + if (MPDConnection::self()->getDetails().isLocal() || HttpServer::self()->isAlive()) { types << constUriMimeType; } return types; @@ -483,12 +483,12 @@ bool PlayQueueModel::dropMimeData(const QMimeData *data, //Act on moves from the music library and dir view addItems(reverseList(decode(*data, constFileNameMimeType)), row, false); return true; - } else if(data->hasFormat(constUriMimeType)/* && MPDConnection::self()->isLocal()*/) { + } else if(data->hasFormat(constUriMimeType)/* && MPDConnection::self()->getDetails().isLocal()*/) { QStringList orig=reverseList(decode(*data, constUriMimeType)); QStringList useable; bool haveHttp=HttpServer::self()->isAlive(); bool alwaysUseHttp=haveHttp && Settings::self()->alwaysUseHttp(); - bool mpdLocal=MPDConnection::self()->isLocal(); + bool mpdLocal=MPDConnection::self()->getDetails().isLocal(); bool allowLocal=haveHttp || mpdLocal; foreach (QString u, orig) { diff --git a/mpd/mpdconnection.cpp b/mpd/mpdconnection.cpp index 5abb2cb2e..2842d275a 100644 --- a/mpd/mpdconnection.cpp +++ b/mpd/mpdconnection.cpp @@ -167,6 +167,29 @@ QString MPDConnection::Response::getError() return data; } +MPDConnectionDetails::MPDConnectionDetails() + : dirReadable(false) +{ +} + +QString MPDConnectionDetails::description() const +{ + QString n=name.isEmpty() ? i18n("Default") : name; + #ifdef ENABLE_KDE_SUPPORT + if (hostname.startsWith('/')) { + return i18nc("name (host)", "%1 (%2)").arg(n).arg(hostname); + } else { + return i18nc("name (host:port)", "%1 (%2:%3)").arg(n).arg(hostname).arg(port); + } + #else + if (hostname.startsWith('/')) { + return QObject::tr("%1 (%2)").arg(n).arg(hostname); + } else { + return QObject::tr("%1 (%2:%3)").arg(n).arg(hostname).arg(port); + } + #endif +} + MPDConnection::MPDConnection() : ver(0) , sock(this) @@ -184,6 +207,7 @@ MPDConnection::MPDConnection() qRegisterMetaType("QAbstractSocket::SocketState"); qRegisterMetaType("MPDStats"); qRegisterMetaType("MPDStatusValues"); + qRegisterMetaType("MPDConnectionDetails"); } MPDConnection::~MPDConnection() @@ -199,12 +223,12 @@ MPDConnection::ConnectionReturn MPDConnection::connectToMPD(MpdSocket &socket, b { if (QAbstractSocket::ConnectedState!=socket.state()) { DBUG << (void *)(&socket) << "Connecting" << (enableIdle ? "(idle)" : "(std)"); - if (hostname.isEmpty() || port == 0) { + if (details.isEmpty()) { DBUG << "no hostname and/or port supplied."; return Failed; } - socket.connectToHost(hostname, port); + socket.connectToHost(details.hostname, details.port); if (socket.waitForConnected(5000)) { DBUG << (void *)(&socket) << "established"; QByteArray recvdata = readFromSocket(socket); @@ -226,9 +250,9 @@ MPDConnection::ConnectionReturn MPDConnection::connectToMPD(MpdSocket &socket, b recvdata.clear(); - if (!password.isEmpty()) { + if (!details.password.isEmpty()) { DBUG << (void *)(&socket) << "setting password..."; - socket.write("password "+password.toUtf8()+"\n"); + socket.write("password "+details.password.toUtf8()+"\n"); socket.waitForBytesWritten(5000); if (!readReply(socket).ok) { DBUG << (void *)(&socket) << "password rejected"; @@ -286,17 +310,17 @@ void MPDConnection::disconnectFromMPD() sock.close(); idleSocket.close(); state=State_Disconnected; + ver=0; } -void MPDConnection::setDetails(const QString &host, quint16 p, const QString &pass) +void MPDConnection::setDetails(const MPDConnectionDetails &det) { - if (hostname!=host || (!sock.isLocal() && port!=p) || password!=pass || State_Connected!=state) { - DBUG << "setDetails" << host << p << (pass.isEmpty() ? false : true); + bool changedDir=det.dir!=details.dir; + if (details!=det) { + DBUG << "setDetails" << det.hostname << det.port << (det.password.isEmpty() ? false : true); bool wasConnected=State_Connected==state; disconnectFromMPD(); - hostname=host; - port=p; - password=pass; + details=det; DBUG << "call connectToMPD"; switch (connectToMPD()) { case Success: @@ -307,38 +331,25 @@ void MPDConnection::setDetails(const QString &host, quint16 p, const QString &pa break; case Failed: emit stateChanged(false); - #ifdef ENABLE_KDE_SUPPORT - if (host.startsWith('/')) { - emit error(i18n("Connection to %1 failed", host), true); - } else { - emit error(i18nc("Connection to host:port", "Connection to %1:%2 failed", host, QString::number(MPDConnection::self()->getPort())), true); - } - #else - if (host.startsWith('/')) { - emit error(tr("Connection to %1 failed").arg(host), true); - } else { - emit error(tr("Connection to %1:%2 failed").arg(host).arg(QString::number(MPDConnection::self()->getPort())), true); - } - #endif + emit error(i18n("Connection to %1 failed").arg(details.description()), true); break; case IncorrectPassword: emit stateChanged(false); - #ifdef ENABLE_KDE_SUPPORT - if (host.startsWith('/')) { - emit error(i18n("Connection to %1 failed - incorrect password", host), true); - } else { - emit error(i18nc("Connection to host:port", "Connection to %1:%2 failed - incorrect password", host, QString::number(MPDConnection::self()->getPort())), true); - } - #else - if (host.startsWith('/')) { - emit error(tr("Connection to %1 failed - incorrect password").arg(host), true); - } else { - emit error(tr("Connection to %1:%2 failed - incorrect password").arg(host).arg(QString::number(MPDConnection::self()->getPort())), true); - } - #endif + emit error(i18n("Connection to %1 failed - incorrect password").arg(details.description()), true); break; } } + if (changedDir) { + emit dirChanged(); + } +} + +void MPDConnection::disconnectMpd() +{ + if (State_Connected==state) { + disconnectFromMPD(); + emit stateChanged(false); + } } MPDConnection::Response MPDConnection::sendCommand(const QByteArray &command, bool emitErrors, bool retry) @@ -370,9 +381,9 @@ MPDConnection::Response MPDConnection::sendCommand(const QByteArray &command, bo } if (emitErrors) { if ((command.startsWith("add ") || command.startsWith("command_list_begin\nadd ")) && -1!=command.indexOf("\"file:///")) { - if (isLocal() && response.data=="Permission denied") { + if (details.isLocal() && response.data=="Permission denied") { emit error(i18n("Failed to load. Please check user \"mpd\" has read permission.")); - } else if (!isLocal() && response.data=="Access denied") { + } else if (!details.isLocal() && response.data=="Access denied") { emit error(i18n("Failed to load. MPD can only play local files if connected via a local socket.")); } else { emit error(response.getError()); @@ -992,11 +1003,7 @@ void MPDConnection::renamePlaylist(const QString oldName, const QString newName) if (sendCommand("rename "+encodeName(oldName)+' '+encodeName(newName), false).ok) { emit playlistRenamed(oldName, newName); } else { - #ifdef ENABLE_KDE_SUPPORT emit error(i18n("Failed to rename %1 to %2").arg(oldName).arg(newName)); - #else - emit error(tr("Failed to rename %1 to %2").arg(oldName).arg(newName)); - #endif } } @@ -1008,11 +1015,7 @@ void MPDConnection::removePlaylist(const QString &name) void MPDConnection::savePlaylist(const QString &name) { if (!sendCommand("save "+encodeName(name), false).ok) { - #ifdef ENABLE_KDE_SUPPORT emit error(i18n("Failed to save %1").arg(name)); - #else - emit error(tr("Failed to save %1").arg(name)); - #endif } } diff --git a/mpd/mpdconnection.h b/mpd/mpdconnection.h index 72513ee2f..2983812e9 100644 --- a/mpd/mpdconnection.h +++ b/mpd/mpdconnection.h @@ -125,6 +125,29 @@ private: QLocalSocket *local; }; +struct MPDConnectionDetails { + MPDConnectionDetails(); + QString description() const; + bool isLocal() const { return hostname.startsWith('/'); } + bool isEmpty() const { return hostname.isEmpty() || (!isLocal() && 0==port); } + bool operator==(const MPDConnectionDetails &o) const { + return hostname==o.hostname && isLocal()==o.isLocal() && (!isLocal() || port==o.port) && password==o.password; + } + bool operator!=(const MPDConnectionDetails &o) const { + return !(*this==o); + } + bool operator<(const MPDConnectionDetails &o) const { + return name.localeAwareCompare(o.name)<0; + } + QString name; + QString hostname; + quint16 port; + QString password; + QString dir; + bool dirReadable; +}; + + class MPDConnection : public QObject { Q_OBJECT @@ -142,13 +165,12 @@ public: MPDConnection(); ~MPDConnection(); - bool isLocal() const { return hostname.startsWith('/'); } - const QString & getHost() const { return hostname; } - quint16 getPort() const { return port; } + const MPDConnectionDetails & getDetails() const { return details; } bool isConnected() const { return State_Connected==state; } public Q_SLOTS: - void setDetails(const QString &host, quint16 port, const QString &pass); + void setDetails(const MPDConnectionDetails &det); + void disconnectMpd(); // Current Playlist void add(const QStringList &files, bool replace); void addid(const QStringList &files, quint32 pos, quint32 size, bool replace); @@ -237,6 +259,7 @@ Q_SIGNALS: void updatedFileList(); void error(const QString &err, bool showActions=false); void urlHandlers(const QStringList &handlers); + void dirChanged(); private Q_SLOTS: void idleDataReady(); @@ -260,9 +283,7 @@ private: private: long ver; - QString hostname; - quint16 port; - QString password; + MPDConnectionDetails details; // Use 2 sockets, 1 for commands and 1 to receive MPD idle events. // Cant use 1, as we could write a command just as an idle event is ready to read MpdSocket sock; diff --git a/replaygain/rgdialog.cpp b/replaygain/rgdialog.cpp index 05bd04991..512e20f12 100644 --- a/replaygain/rgdialog.cpp +++ b/replaygain/rgdialog.cpp @@ -141,7 +141,7 @@ void RgDialog::show(const QList &songs, const QString &udi) origSongs=songs; #ifdef ENABLE_DEVICES_SUPPORT if (udi.isEmpty()) { - base=Settings::self()->mpdDir(); + base=MPDConnection::self()->getDetails().dir; } else { Device *dev=getDevice(udi, parentWidget()); @@ -154,7 +154,7 @@ void RgDialog::show(const QList &songs, const QString &udi) } #else Q_UNUSED(udi) - base=Settings::self()->mpdDir(); + base=MPDConnection::self()->getDetails().dir; #endif state=State_Idle; enableButton(User1, songs.count());