MPD allows clients to create named "partitions", each with its own queue, player and outputs. This commit adds controls to allow the user to create, delete, and switch between partitions.
2946 lines
118 KiB
C++
2946 lines
118 KiB
C++
/*
|
|
* Cantata
|
|
*
|
|
* Copyright (c) 2011-2021 Craig Drummond <craig.p.drummond@gmail.com>
|
|
*
|
|
*/
|
|
/*
|
|
* Copyright (c) 2008 Sander Knopper (sander AT knopper DOT tk) and
|
|
* Roeland Douma (roeland AT rullzer DOT com)
|
|
*
|
|
* This file is part of QtMPC.
|
|
*
|
|
* QtMPC 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.
|
|
*
|
|
* QtMPC 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 QtMPC. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "mainwindow.h"
|
|
#include "application.h"
|
|
#include "support/thread.h"
|
|
#include "trayitem.h"
|
|
#include "support/messagebox.h"
|
|
#include "support/inputdialog.h"
|
|
#include "models/playlistsmodel.h"
|
|
#include "covers.h"
|
|
#include "coverdialog.h"
|
|
#include "currentcover.h"
|
|
#include "preferencesdialog.h"
|
|
#include "mpd-interface/mpdstats.h"
|
|
#include "mpd-interface/mpdparseutils.h"
|
|
#include "settings.h"
|
|
#include "support/utils.h"
|
|
#include "models/musiclibraryitemartist.h"
|
|
#include "models/musiclibraryitemalbum.h"
|
|
#include "models/mpdlibrarymodel.h"
|
|
#include "librarypage.h"
|
|
#include "folderpage.h"
|
|
#include "streams/streamdialog.h"
|
|
#include "searchpage.h"
|
|
#include "customactions.h"
|
|
#include "apikeys.h"
|
|
#include "support/gtkstyle.h"
|
|
#include "widgets/mirrormenu.h"
|
|
#ifdef ENABLE_DEVICES_SUPPORT
|
|
#include "devices/filejob.h"
|
|
#include "devices/devicespage.h"
|
|
#include "models/devicesmodel.h"
|
|
#include "devices/actiondialog.h"
|
|
#include "devices/syncdialog.h"
|
|
#if defined CDDB_FOUND || defined MUSICBRAINZ5_FOUND
|
|
#include "devices/audiocddevice.h"
|
|
#endif
|
|
#endif
|
|
#include "online/onlineservicespage.h"
|
|
#include "http/httpserver.h"
|
|
#ifdef TAGLIB_FOUND
|
|
#include "tags/trackorganiser.h"
|
|
#include "tags/tageditor.h"
|
|
#include "tags/tags.h"
|
|
#ifdef ENABLE_REPLAYGAIN_SUPPORT
|
|
#include "replaygain/rgdialog.h"
|
|
#endif
|
|
#endif
|
|
#include "models/streamsmodel.h"
|
|
#include "playlists/playlistspage.h"
|
|
#include "support/fancytabwidget.h"
|
|
#include "support/monoicon.h"
|
|
#ifdef QT_QTDBUS_FOUND
|
|
#include "dbus/mpris.h"
|
|
#include "cantataadaptor.h"
|
|
#ifdef Q_OS_LINUX
|
|
#include "dbus/powermanagement.h"
|
|
#endif
|
|
#endif
|
|
#if !defined Q_OS_WIN && !defined Q_OS_MAC
|
|
#include "devices/mountpoints.h"
|
|
#endif
|
|
#ifdef Q_OS_MAC
|
|
#include "support/windowmanager.h"
|
|
#include "support/osxstyle.h"
|
|
#include "mac/dockmenu.h"
|
|
#ifdef MAC_MEDIAPLAYER_FOUND
|
|
#include "mac/macnowplaying.h"
|
|
#endif
|
|
#ifdef IOKIT_FOUND
|
|
#include "mac/powermanagement.h"
|
|
#endif
|
|
#endif
|
|
#include "playlists/dynamicplaylists.h"
|
|
#include "support/messagewidget.h"
|
|
#include "widgets/groupedview.h"
|
|
#include "widgets/actionitemdelegate.h"
|
|
#include "widgets/icons.h"
|
|
#include "widgets/volumecontrol.h"
|
|
#include "support/action.h"
|
|
#include "support/actioncollection.h"
|
|
#include "stdactions.h"
|
|
#ifdef ENABLE_HTTP_STREAM_PLAYBACK
|
|
#include "mpd-interface/httpstream.h"
|
|
#endif
|
|
#include <QSet>
|
|
#include <QString>
|
|
#include <QTimer>
|
|
#include <QToolBar>
|
|
#include <QProcess>
|
|
#ifdef Q_OS_WIN
|
|
#include "windows/thumbnailtoolbar.h"
|
|
#endif
|
|
#include <QDialogButtonBox>
|
|
#include <QKeyEvent>
|
|
#include <QSpacerItem>
|
|
#include <QMenuBar>
|
|
#include <QFileDialog>
|
|
#include "mediakeys.h"
|
|
#include <cstdlib>
|
|
#include <algorithm>
|
|
|
|
static int nextKey(int &key)
|
|
{
|
|
int k=key;
|
|
if (Qt::Key_0==key) {
|
|
key=Qt::Key_A;
|
|
} else if (Qt::Key_Colon==++key) {
|
|
key=Qt::Key_0;
|
|
}
|
|
return k;
|
|
}
|
|
|
|
static const char * constRatingKey="rating";
|
|
static const char * constUserSettingProp = "user-setting-1";
|
|
static const char * constUserSetting2Prop = "user-setting-2";
|
|
|
|
MainWindow::MainWindow(QWidget *parent)
|
|
: QMainWindow(parent)
|
|
, prevPage(-1)
|
|
, lastState(MPDState_Inactive)
|
|
, lastSongId(-1)
|
|
, autoScrollPlayQueue(true)
|
|
, singlePane(false)
|
|
, shown(false)
|
|
, currentPage(nullptr)
|
|
#ifdef QT_QTDBUS_FOUND
|
|
, mpris(nullptr)
|
|
#endif
|
|
, statusTimer(nullptr)
|
|
, playQueueSearchTimer(nullptr)
|
|
#if !defined Q_OS_WIN && !defined Q_OS_MAC
|
|
, mpdAccessibilityTimer(nullptr)
|
|
, showMenubarAction(nullptr)
|
|
#endif
|
|
, contextTimer(nullptr)
|
|
, contextSwitchTime(0)
|
|
, connectedState(CS_Init)
|
|
, stopAfterCurrent(false)
|
|
, responsiveSidebar(false)
|
|
#if defined Q_OS_WIN
|
|
, thumbnailTooolbar(0)
|
|
#endif
|
|
{
|
|
stopTrackButton=nullptr;
|
|
coverWidget=nullptr;
|
|
tabWidget=nullptr;
|
|
savePlayQueueButton=nullptr;
|
|
centerPlayQueueButton=nullptr;
|
|
midSpacer=nullptr;
|
|
|
|
QPoint p=pos();
|
|
ActionCollection::setMainWidget(this);
|
|
trayItem=new TrayItem(this);
|
|
#ifdef QT_QTDBUS_FOUND
|
|
new CantataAdaptor(this);
|
|
QDBusConnection::sessionBus().registerObject("/cantata", this);
|
|
#endif
|
|
setMinimumHeight(Utils::scaleForDpi(480));
|
|
setMinimumWidth(Utils::scaleForDpi(400));
|
|
QWidget *widget = new QWidget(this);
|
|
setupUi(widget);
|
|
setCentralWidget(widget);
|
|
messageWidget->hide();
|
|
|
|
// Need to set these values here, as used in library/device loading...
|
|
Song::setComposerGenres(Settings::self()->composerGenres());
|
|
Song::setUseOriginalYear(Settings::self()->useOriginalYear());
|
|
|
|
int hSpace=Utils::layoutSpacing(this);
|
|
int vSpace=Utils::scaleForDpi(2);
|
|
toolbarLayout->setContentsMargins(hSpace, vSpace, hSpace, vSpace);
|
|
toolbar->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
|
|
setWindowTitle("Cantata");
|
|
QWidget *tb=toolbar;
|
|
#ifdef Q_OS_MAC
|
|
setUnifiedTitleAndToolBarOnMac(true);
|
|
QToolBar *topToolBar = addToolBar("ToolBar");
|
|
WindowManager *wm=new WindowManager(topToolBar);
|
|
wm->initialize(WindowManager::WM_DRAG_MENU_AND_TOOLBAR);
|
|
wm->registerWidgetAndChildren(topToolBar);
|
|
topToolBar->setObjectName("MainToolBar");
|
|
topToolBar->addWidget(toolbar);
|
|
topToolBar->setMovable(false);
|
|
topToolBar->setContextMenuPolicy(Qt::PreventContextMenu);
|
|
|
|
topToolBar->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
|
|
topToolBar->setContentsMargins(0, 0, 0, 0);
|
|
QLayout *l=topToolBar->layout();
|
|
if (l) {
|
|
l->setMargin(0);
|
|
l->setSpacing(0);
|
|
}
|
|
topToolBar->ensurePolished();
|
|
toolbar=topToolBar;
|
|
#elif !defined Q_OS_WIN
|
|
QProxyStyle *proxy=qobject_cast<QProxyStyle *>(style());
|
|
QStyle *check=proxy && proxy->baseStyle() ? proxy->baseStyle() : style();
|
|
if (check->inherits("Kvantum::Style")) {
|
|
QToolBar *topToolBar = addToolBar("ToolBar");
|
|
topToolBar->setObjectName("MainToolBar");
|
|
topToolBar->addWidget(toolbar);
|
|
topToolBar->setMovable(false);
|
|
topToolBar->setContextMenuPolicy(Qt::PreventContextMenu);
|
|
topToolBar->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
|
|
topToolBar->setContentsMargins(0, 0, 0, 0);
|
|
QLayout *l=topToolBar->layout();
|
|
if (l) {
|
|
l->setMargin(0);
|
|
l->setSpacing(0);
|
|
}
|
|
topToolBar->ensurePolished();
|
|
toolbar=topToolBar;
|
|
} else {
|
|
toolbar->setFixedHeight(qMax(54, (int)(fontMetrics().height()*3.25)+(toolbarLayout->spacing()*3)+(vSpace*2)));
|
|
}
|
|
#endif
|
|
|
|
toolbar->ensurePolished();
|
|
toolbar->adjustSize();
|
|
coverWidget->setSize(toolbar->height()-(vSpace*2));
|
|
tb->setMinimumHeight(toolbar->height());
|
|
nowPlaying->initColors();
|
|
nowPlaying->adjustSize();
|
|
nowPlaying->setFixedHeight(nowPlaying->height());
|
|
volumeSlider->setColor(nowPlaying->textColor());
|
|
Icons::self()->initToolbarIcons(nowPlaying->textColor());
|
|
Icons::self()->initSidebarIcons();
|
|
QColor iconCol=Utils::monoIconColor();
|
|
|
|
setWindowIcon(Icons::self()->appIcon);
|
|
|
|
prefAction=ActionCollection::get()->createAction("configure", Utils::KDE==Utils::currentDe() ? tr("Configure Cantata...") : tr("Preferences..."),
|
|
Icons::self()->configureIcon);
|
|
connect(prefAction, SIGNAL(triggered()),this, SLOT(showPreferencesDialog()));
|
|
quitAction = ActionCollection::get()->createAction("quit", tr("Quit"), MonoIcon::icon(FontAwesome::poweroff, MonoIcon::constRed, MonoIcon::constRed));
|
|
connect(quitAction, SIGNAL(triggered()), this, SLOT(quit()));
|
|
quitAction->setShortcut(QKeySequence::Quit);
|
|
Action *aboutAction=ActionCollection::get()->createAction("about", tr("About Cantata..."), Icons::self()->appIcon);
|
|
connect(aboutAction, SIGNAL(triggered()),this, SLOT(showAboutDialog()));
|
|
#ifdef Q_OS_MAC
|
|
prefAction->setMenuRole(QAction::PreferencesRole);
|
|
quitAction->setMenuRole(QAction::QuitRole);
|
|
aboutAction->setMenuRole(QAction::AboutRole);
|
|
#endif
|
|
restoreAction = new Action(tr("Show Window"), this);
|
|
connect(restoreAction, SIGNAL(triggered()), this, SLOT(restoreWindow()));
|
|
|
|
serverInfoAction=ActionCollection::get()->createAction("mpdinfo", tr("Server information..."), MonoIcon::icon(FontAwesome::server, iconCol));
|
|
connect(serverInfoAction, SIGNAL(triggered()),this, SLOT(showServerInfo()));
|
|
serverInfoAction->setEnabled(Settings::self()->firstRun());
|
|
refreshDbAction = ActionCollection::get()->createAction("refresh", tr("Refresh Database"), Icons::self()->refreshIcon);
|
|
doDbRefreshAction = new Action(refreshDbAction->icon(), tr("Refresh"), this);
|
|
refreshDbAction->setEnabled(false);
|
|
connectAction = new Action(Icons::self()->connectIcon, tr("Connect"), this);
|
|
connectionsAction = new Action(MonoIcon::icon(FontAwesome::server, iconCol), tr("Collection"), this);
|
|
partitionsAction = new Action(MonoIcon::icon(FontAwesome::columns, iconCol), tr("Partitions"), this);
|
|
outputsAction = new Action(MonoIcon::icon(FontAwesome::volumeup, iconCol), tr("Outputs"), this);
|
|
stopAfterTrackAction = ActionCollection::get()->createAction("stopaftertrack", tr("Stop After Track"), Icons::self()->toolbarStopIcon);
|
|
|
|
QList<int> seeks=QList<int>() << 5 << 30 << 60;
|
|
QList<int> seekShortcuts=QList<int>() << (int)Qt::ControlModifier << (int)Qt::ShiftModifier << (int)(Qt::ControlModifier|Qt::ShiftModifier);
|
|
for (int i=0; i<seeks.length(); ++i) {
|
|
int seek=seeks.at(i);
|
|
Action *fwdAction = ActionCollection::get()->createAction("seekfwd"+QString::number(seek), tr("Seek forward (%1 seconds)").arg(seek));
|
|
Action *revAction = ActionCollection::get()->createAction("seekrev"+QString::number(seek), tr("Seek backward (%1 seconds)").arg(seek));
|
|
fwdAction->setProperty("offset", seek);
|
|
revAction->setProperty("offset", -1*seek);
|
|
connect(fwdAction, SIGNAL(triggered()), MPDConnection::self(), SLOT(seek()));
|
|
connect(revAction, SIGNAL(triggered()), MPDConnection::self(), SLOT(seek()));
|
|
addAction(fwdAction);
|
|
addAction(revAction);
|
|
if (i<seekShortcuts.length()) {
|
|
fwdAction->setShortcut((Qt::RightToLeft==layoutDirection() ? Qt::Key_Left : Qt::Key_Right)+seekShortcuts.at(i));
|
|
revAction->setShortcut((Qt::RightToLeft==layoutDirection() ? Qt::Key_Right : Qt::Key_Left)+seekShortcuts.at(i));
|
|
}
|
|
}
|
|
|
|
addPlayQueueToStoredPlaylistAction = new Action(Icons::self()->playlistListIcon, tr("Add To Stored Playlist"), this);
|
|
#ifdef ENABLE_DEVICES_SUPPORT
|
|
copyToDeviceAction = new Action(StdActions::self()->copyToDeviceAction->icon(), Utils::strippedText(StdActions::self()->copyToDeviceAction->text()), this);
|
|
copyToDeviceAction->setMenu(DevicesModel::self()->menu()->duplicate(nullptr));
|
|
#endif
|
|
cropPlayQueueAction = ActionCollection::get()->createAction("cropplaylist", tr("Crop Others"));
|
|
addStreamToPlayQueueAction = ActionCollection::get()->createAction("addstreamtoplayqueue", tr("Add Stream URL"));
|
|
addLocalFilesToPlayQueueAction = ActionCollection::get()->createAction("addlocalfiles", tr("Add Local Files"));
|
|
QIcon clearIcon = MonoIcon::icon(FontAwesome::times, MonoIcon::constRed, MonoIcon::constRed);
|
|
clearPlayQueueAction = ActionCollection::get()->createAction("clearplaylist", tr("Clear"), clearIcon);
|
|
clearPlayQueueAction->setShortcut(Qt::ControlModifier+Qt::Key_K);
|
|
centerPlayQueueAction = ActionCollection::get()->createAction("centerplaylist", tr("Center On Current Track"), Icons::self()->centrePlayQueueOnTrackIcon);
|
|
expandInterfaceAction = ActionCollection::get()->createAction("expandinterface", tr("Expanded Interface"), MonoIcon::icon(FontAwesome::expand, iconCol));
|
|
expandInterfaceAction->setCheckable(true);
|
|
songInfoAction = ActionCollection::get()->createAction("showsonginfo", tr("Show Current Song Information"), Icons::self()->infoIcon);
|
|
songInfoAction->setShortcut(Qt::Key_F12);
|
|
songInfoAction->setCheckable(true);
|
|
fullScreenAction = ActionCollection::get()->createAction("fullScreen", tr("Full Screen"), MonoIcon::icon(FontAwesome::arrowsalt, iconCol));
|
|
#ifndef Q_OS_MAC
|
|
fullScreenAction->setShortcut(Qt::Key_F11);
|
|
#endif
|
|
randomPlayQueueAction = ActionCollection::get()->createAction("randomplaylist", tr("Random"), Icons::self()->shuffleIcon);
|
|
repeatPlayQueueAction = ActionCollection::get()->createAction("repeatplaylist", tr("Repeat"), Icons::self()->repeatIcon);
|
|
singlePlayQueueAction = ActionCollection::get()->createAction("singleplaylist", tr("Single"), Icons::self()->singleIcon, tr("When 'Single' is activated, playback is stopped after current song, or song is repeated if 'Repeat' is enabled."));
|
|
consumePlayQueueAction = ActionCollection::get()->createAction("consumeplaylist", tr("Consume"), Icons::self()->consumeIcon, tr("When consume is activated, a song is removed from the play queue after it has been played."));
|
|
searchPlayQueueAction = ActionCollection::get()->createAction("searchplaylist", tr("Find in Play Queue"), Icons::self()->searchIcon);
|
|
addAction(searchPlayQueueAction);
|
|
searchPlayQueueAction->setShortcut(Qt::ControlModifier+Qt::ShiftModifier+Qt::Key_F);
|
|
#ifdef ENABLE_HTTP_STREAM_PLAYBACK
|
|
streamPlayAction = ActionCollection::get()->createAction("streamplay", tr("Play HTTP Output Stream"), Icons::self()->httpStreamIcon);
|
|
streamPlayAction->setCheckable(true);
|
|
streamPlayAction->setChecked(false);
|
|
streamPlayAction->setVisible(false);
|
|
#endif
|
|
|
|
locateAction = new Action(Icons::self()->searchIcon, tr("Locate In Library"), this);
|
|
locateArtistAction = ActionCollection::get()->createAction("locateartist", tr("Artist"));
|
|
locateAlbumAction = ActionCollection::get()->createAction("locatealbum", tr("Album"));
|
|
locateTrackAction = ActionCollection::get()->createAction("locatetrack", tr("Track"));
|
|
locateArtistAction->setSettingsText(locateAction);
|
|
locateAlbumAction->setSettingsText(locateAction);
|
|
locateTrackAction->setSettingsText(locateAction);
|
|
addAction(locateAction);
|
|
|
|
QMenu *locateMenu=new QMenu();
|
|
locateMenu->addAction(locateArtistAction);
|
|
locateMenu->addAction(locateAlbumAction);
|
|
locateMenu->addAction(locateTrackAction);
|
|
locateAction->setMenu(locateMenu);
|
|
|
|
playNextAction = ActionCollection::get()->createAction("playnext", tr("Play Next"));
|
|
#ifdef TAGLIB_FOUND
|
|
editPlayQueueTagsAction = ActionCollection::get()->createAction("editpqtags", Utils::strippedText(StdActions::self()->editTagsAction->text()), StdActions::self()->editTagsAction->icon());
|
|
editPlayQueueTagsAction->setSettingsText(tr("Edit Track Information (Play Queue)"));
|
|
#endif
|
|
addAction(expandAllAction = ActionCollection::get()->createAction("expandall", tr("Expand All")));
|
|
expandAllAction->setShortcut(Qt::ControlModifier+Qt::Key_Down);
|
|
addAction(collapseAllAction = ActionCollection::get()->createAction("collapseall", tr("Collapse All")));
|
|
collapseAllAction->setShortcut(Qt::ControlModifier+Qt::Key_Up);
|
|
cancelAction = ActionCollection::get()->createAction("cancel", tr("Cancel"), Icons::self()->cancelIcon);
|
|
cancelAction->setShortcut(Qt::AltModifier+Qt::Key_Escape);
|
|
connect(cancelAction, SIGNAL(triggered()), messageWidget, SLOT(animatedHide()));
|
|
|
|
StdActions::self()->playPauseTrackAction->setEnabled(false);
|
|
StdActions::self()->nextTrackAction->setEnabled(false);
|
|
updateNextTrack(-1);
|
|
StdActions::self()->prevTrackAction->setEnabled(false);
|
|
enableStopActions(false);
|
|
volumeSlider->initActions();
|
|
|
|
connectionsAction->setMenu(new QMenu(this));
|
|
connectionsGroup=new QActionGroup(connectionsAction->menu());
|
|
partitionsAction->setMenu(new QMenu(this));
|
|
partitionsGroup=new QActionGroup(partitionsAction->menu());
|
|
outputsAction->setMenu(new QMenu(this));
|
|
outputsAction->setVisible(false);
|
|
addPlayQueueToStoredPlaylistAction->setMenu(PlaylistsModel::self()->menu()->duplicate(nullptr));
|
|
|
|
playPauseTrackButton->setDefaultAction(StdActions::self()->playPauseTrackAction);
|
|
stopTrackButton->setDefaultAction(StdActions::self()->stopPlaybackAction);
|
|
nextTrackButton->setDefaultAction(StdActions::self()->nextTrackAction);
|
|
prevTrackButton->setDefaultAction(StdActions::self()->prevTrackAction);
|
|
|
|
QMenu *stopMenu=new QMenu(this);
|
|
stopMenu->addAction(StdActions::self()->stopPlaybackAction);
|
|
stopMenu->addAction(StdActions::self()->stopAfterCurrentTrackAction);
|
|
stopTrackButton->setMenu(stopMenu);
|
|
stopTrackButton->setPopupMode(QToolButton::DelayedPopup);
|
|
|
|
clearPlayQueueAction->setEnabled(false);
|
|
centerPlayQueueAction->setEnabled(false);
|
|
StdActions::self()->savePlayQueueAction->setEnabled(false);
|
|
addStreamToPlayQueueAction->setEnabled(false);
|
|
addLocalFilesToPlayQueueAction->setEnabled(false);
|
|
clearPlayQueueButton->setDefaultAction(clearPlayQueueAction);
|
|
savePlayQueueButton->setDefaultAction(StdActions::self()->savePlayQueueAction);
|
|
centerPlayQueueButton->setDefaultAction(centerPlayQueueAction);
|
|
randomButton->setDefaultAction(randomPlayQueueAction);
|
|
repeatButton->setDefaultAction(repeatPlayQueueAction);
|
|
singleButton->setDefaultAction(singlePlayQueueAction);
|
|
consumeButton->setDefaultAction(consumePlayQueueAction);
|
|
|
|
QStringList hiddenPages=Settings::self()->hiddenPages();
|
|
playQueuePage=new PlayQueuePage(this);
|
|
contextPage=new ContextPage(this);
|
|
QBoxLayout *layout=new QBoxLayout(QBoxLayout::TopToBottom, playQueuePage);
|
|
layout->setContentsMargins(0, 0, 0, 0);
|
|
bool playQueueInSidebar=!hiddenPages.contains(playQueuePage->metaObject()->className());
|
|
bool contextInSidebar=!hiddenPages.contains(contextPage->metaObject()->className());
|
|
layout=new QBoxLayout(QBoxLayout::TopToBottom, contextPage);
|
|
layout->setContentsMargins(0, 0, 0, 0);
|
|
|
|
// Build sidebar...
|
|
#define TAB_ACTION(A) A->icon(), A->text(), A->text()
|
|
int sidebarPageShortcutKey=Qt::Key_1;
|
|
addAction(showPlayQueueAction = ActionCollection::get()->createAction("showplayqueue", tr("Play Queue"), Icons::self()->playqueueIcon));
|
|
showPlayQueueAction->setShortcut(Qt::ControlModifier+Qt::ShiftModifier+Qt::Key_Q);
|
|
tabWidget->addTab(playQueuePage, TAB_ACTION(showPlayQueueAction), playQueueInSidebar);
|
|
connect(showPlayQueueAction, SIGNAL(triggered()), this, SLOT(showPlayQueue()));
|
|
libraryPage = new LibraryPage(this);
|
|
addAction(libraryTabAction = ActionCollection::get()->createAction("showlibrarytab", tr("Library"), Icons::self()->libraryIcon));
|
|
libraryTabAction->setShortcut(Qt::ControlModifier+Qt::ShiftModifier+nextKey(sidebarPageShortcutKey));
|
|
tabWidget->addTab(libraryPage, TAB_ACTION(libraryTabAction), !hiddenPages.contains(libraryPage->metaObject()->className()));
|
|
connect(libraryTabAction, SIGNAL(triggered()), this, SLOT(showLibraryTab()));
|
|
folderPage = new FolderPage(this);
|
|
addAction(foldersTabAction = ActionCollection::get()->createAction("showfolderstab", tr("Folders"), Icons::self()->foldersIcon));
|
|
foldersTabAction->setShortcut(Qt::ControlModifier+Qt::ShiftModifier+nextKey(sidebarPageShortcutKey));
|
|
tabWidget->addTab(folderPage, TAB_ACTION(foldersTabAction), !hiddenPages.contains(folderPage->metaObject()->className()));
|
|
connect(foldersTabAction, SIGNAL(triggered()), this, SLOT(showFoldersTab()));
|
|
folderPage->setEnabled(!hiddenPages.contains(folderPage->metaObject()->className()));
|
|
playlistsPage = new PlaylistsPage(this);
|
|
addAction(playlistsTabAction = ActionCollection::get()->createAction("showplayliststab", tr("Playlists"), Icons::self()->playlistsIcon));
|
|
playlistsTabAction->setShortcut(Qt::ControlModifier+Qt::ShiftModifier+nextKey(sidebarPageShortcutKey));
|
|
tabWidget->addTab(playlistsPage, TAB_ACTION(playlistsTabAction), !hiddenPages.contains(playlistsPage->metaObject()->className()));
|
|
connect(playlistsTabAction, SIGNAL(triggered()), this, SLOT(showPlaylistsTab()));
|
|
connect(playlistsPage, SIGNAL(error(const QString &)), SLOT(showError(const QString &)));
|
|
connect(DynamicPlaylists::self(), SIGNAL(error(const QString &)), SLOT(showError(const QString &)));
|
|
connect(DynamicPlaylists::self(), SIGNAL(running(bool)), dynamicLabel, SLOT(setVisible(bool)));
|
|
connect(DynamicPlaylists::self(), SIGNAL(running(bool)), this, SLOT(controlDynamicButton()));
|
|
stopDynamicButton->setDefaultAction(DynamicPlaylists::self()->stopAct());
|
|
onlinePage = new OnlineServicesPage(this);
|
|
addAction(onlineTabAction = ActionCollection::get()->createAction("showonlinetab", tr("Internet"), Icons::self()->onlineIcon));
|
|
onlineTabAction->setShortcut(Qt::ControlModifier+Qt::ShiftModifier+nextKey(sidebarPageShortcutKey));
|
|
tabWidget->addTab(onlinePage, TAB_ACTION(onlineTabAction), !hiddenPages.contains(onlinePage->metaObject()->className()));
|
|
onlinePage->setEnabled(!hiddenPages.contains(onlinePage->metaObject()->className()));
|
|
connect(onlineTabAction, SIGNAL(triggered()), this, SLOT(showOnlineTab()));
|
|
// connect(onlinePage, SIGNAL(addToDevice(const QString &, const QString &, const QList<Song> &)), SLOT(copyToDevice(const QString &, const QString &, const QList<Song> &)));
|
|
connect(onlinePage, SIGNAL(error(const QString &)), this, SLOT(showError(const QString &)));
|
|
#ifdef ENABLE_DEVICES_SUPPORT
|
|
devicesPage = new DevicesPage(this);
|
|
addAction(devicesTabAction = ActionCollection::get()->createAction("showdevicestab", tr("Devices"), Icons::self()->devicesIcon));
|
|
devicesTabAction->setShortcut(Qt::ControlModifier+Qt::ShiftModifier+nextKey(sidebarPageShortcutKey));
|
|
tabWidget->addTab(devicesPage, TAB_ACTION(devicesTabAction), !hiddenPages.contains(devicesPage->metaObject()->className()));
|
|
DevicesModel::self()->setEnabled(!hiddenPages.contains(devicesPage->metaObject()->className()));
|
|
connect(devicesTabAction, SIGNAL(triggered()), this, SLOT(showDevicesTab()));
|
|
#endif
|
|
searchPage = new SearchPage(this);
|
|
addAction(searchTabAction = ActionCollection::get()->createAction("showsearchtab", tr("Search"), Icons::self()->searchTabIcon));
|
|
searchTabAction->setShortcut(Qt::ControlModifier+Qt::ShiftModifier+nextKey(sidebarPageShortcutKey));
|
|
connect(searchTabAction, SIGNAL(triggered()), this, SLOT(showSearchTab()));
|
|
connect(searchPage, SIGNAL(locate(QList<Song>)), this, SLOT(locateTracks(QList<Song>)));
|
|
tabWidget->addTab(searchPage, TAB_ACTION(searchTabAction), !hiddenPages.contains(searchPage->metaObject()->className()));
|
|
tabWidget->addTab(contextPage, Icons::self()->infoSidebarIcon, tr("Info"), songInfoAction->text(),
|
|
!hiddenPages.contains(contextPage->metaObject()->className()));
|
|
tabWidget->setStyle(Settings::self()->sidebar());
|
|
|
|
if (playQueueInSidebar) {
|
|
tabToggled(PAGE_PLAYQUEUE);
|
|
} else {
|
|
tabWidget->setCurrentIndex(PAGE_LIBRARY);
|
|
}
|
|
|
|
if (contextInSidebar) {
|
|
tabToggled(PAGE_CONTEXT);
|
|
} else {
|
|
tabWidget->toggleTab(PAGE_CONTEXT, false);
|
|
}
|
|
|
|
expandInterfaceAction->setChecked(Settings::self()->showPlaylist());
|
|
fullScreenAction->setEnabled(expandInterfaceAction->isChecked());
|
|
if (fullScreenAction->isEnabled()) {
|
|
fullScreenAction->setChecked(Settings::self()->showFullScreen());
|
|
}
|
|
randomPlayQueueAction->setCheckable(true);
|
|
repeatPlayQueueAction->setCheckable(true);
|
|
singlePlayQueueAction->setCheckable(true);
|
|
consumePlayQueueAction->setCheckable(true);
|
|
|
|
songInfoButton->setDefaultAction(songInfoAction);
|
|
fullScreenLabel->setVisible(false);
|
|
connect(fullScreenLabel, SIGNAL(leftClickedUrl()), fullScreenAction, SIGNAL(triggered()));
|
|
connect(playQueueSearchWidget, SIGNAL(active(bool)), playQueue, SLOT(searchActive(bool)));
|
|
if (Configuration(playQueuePage->metaObject()->className()).get(ItemView::constSearchActiveKey, false)) {
|
|
playQueueSearchWidget->activate();
|
|
} else {
|
|
playQueueSearchWidget->setVisible(false);
|
|
}
|
|
playQueueSearchWidget->setToolTip(tr("<p>Enter a string to search artist, album, title, etc. To filter based on year, add <i>#year-range</i> to search string - e.g.</p><ul>"
|
|
"<li><b><i>#2000</i></b> return tracks from 2000</li>"
|
|
"<li><b><i>#1980-1989</i></b> return tracks from the 80's</li>"
|
|
"<li><b><i>Blah #2000</i></b> to search for string <i>Blah</i> and only return tracks from 2000</li>"
|
|
"</ul></p>"));
|
|
QList<QToolButton *> playbackBtns=QList<QToolButton *>() << prevTrackButton << stopTrackButton << playPauseTrackButton << nextTrackButton;
|
|
QList<QToolButton *> controlBtns=QList<QToolButton *>() << menuButton << songInfoButton;
|
|
int playbackIconSizeNonScaled=24==Icons::self()->toolbarPlayIcon.actualSize(QSize(24, 24)).width() ? 24 : 28;
|
|
int playbackIconSize=Utils::scaleForDpi(playbackIconSizeNonScaled);
|
|
int playPauseIconSize=Utils::scaleForDpi(32);
|
|
int controlIconSize=Utils::scaleForDpi(22);
|
|
int controlButtonSize=Utils::scaleForDpi(32);
|
|
int playbackButtonSize=28==playbackIconSizeNonScaled ? Utils::scaleForDpi(34) : controlButtonSize;
|
|
|
|
for (QToolButton *b: controlBtns) {
|
|
b->setAutoRaise(true);
|
|
b->setToolButtonStyle(Qt::ToolButtonIconOnly);
|
|
b->setFixedSize(QSize(controlButtonSize, controlButtonSize));
|
|
b->setIconSize(QSize(controlIconSize, controlIconSize));
|
|
}
|
|
for (QToolButton *b: playbackBtns) {
|
|
b->setAutoRaise(true);
|
|
b->setToolButtonStyle(Qt::ToolButtonIconOnly);
|
|
b->setFixedSize(QSize(playbackButtonSize, playbackButtonSize));
|
|
b->setIconSize(QSize(playbackIconSize, playbackIconSize));
|
|
}
|
|
|
|
playPauseTrackButton->setIconSize(QSize(playPauseIconSize, playPauseIconSize));
|
|
playPauseTrackButton->setFixedSize(QSize(playPauseIconSize+6, playPauseIconSize+6));
|
|
|
|
QList<QWidget *> pqWidgets = QList<QWidget *>() << stopDynamicButton << dynamicLabel << playQueueStatsLabel << fullScreenLabel
|
|
<< repeatButton << singleButton << randomButton << consumeButton << midSpacer
|
|
<< centerPlayQueueButton << savePlayQueueButton << clearPlayQueueButton << sizeGrip;
|
|
for (const auto &item: pqWidgets) {
|
|
Application::fixSize(item);
|
|
}
|
|
|
|
if (fullScreenAction->isEnabled()) {
|
|
fullScreenAction->setChecked(Settings::self()->showFullScreen());
|
|
}
|
|
|
|
randomPlayQueueAction->setChecked(false);
|
|
repeatPlayQueueAction->setChecked(false);
|
|
singlePlayQueueAction->setChecked(false);
|
|
consumePlayQueueAction->setChecked(false);
|
|
|
|
expandedSize=Settings::self()->mainWindowSize();
|
|
collapsedSize=Settings::self()->mainWindowCollapsedSize();
|
|
|
|
int width=playPauseTrackButton->width()*25;
|
|
QSize defaultSize(playPauseTrackButton->width()*25, playPauseTrackButton->height()*18);
|
|
|
|
if (Settings::self()->firstRun() || (expandInterfaceAction->isChecked() && expandedSize.isEmpty())) {
|
|
resize(defaultSize);
|
|
splitter->setSizes(QList<int>() << width*0.4 << width*0.6);
|
|
} else {
|
|
if (expandInterfaceAction->isChecked()) {
|
|
if (!expandedSize.isEmpty()) {
|
|
if (!Settings::self()->maximized() && expandedSize.width()>1) {
|
|
resize(expandedSize);
|
|
expandOrCollapse(false);
|
|
} else {
|
|
resize(expandedSize.width()>1 ? expandedSize : defaultSize);
|
|
// Issue #1137 Under Windows, Cantata does not restore maximized correctly if context is in sidebar
|
|
// ...so, set maximized after shown
|
|
QTimer::singleShot(0, this, SLOT(showMaximized()));
|
|
if (expandedSize.width()<=1) {
|
|
expandedSize=defaultSize;
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
if (!collapsedSize.isEmpty() && collapsedSize.width()>1) {
|
|
resize(collapsedSize);
|
|
expandOrCollapse(false);
|
|
}
|
|
}
|
|
|
|
if (!playQueueInSidebar) {
|
|
QByteArray state=Settings::self()->splitterState();
|
|
if (state.isEmpty()) {
|
|
int width=playPauseTrackButton->width()*25;
|
|
splitter->setSizes(QList<int>() << width*0.4 << width*0.6);
|
|
} else {
|
|
splitter->restoreState(Settings::self()->splitterState());
|
|
}
|
|
}
|
|
if (fullScreenAction->isChecked()) {
|
|
fullScreen();
|
|
} else {
|
|
QPoint p = Settings::self()->mainWindowPos();
|
|
if (!p.isNull()) {
|
|
move(p);
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifndef Q_OS_WIN
|
|
#ifdef Q_OS_MAC
|
|
bool showMenubar = true;
|
|
#else
|
|
bool showMenubar = Utils::Gnome!=Utils::currentDe();
|
|
#endif
|
|
if (showMenubar) {
|
|
#ifdef Q_OS_MAC
|
|
menuButton->setVisible(false);
|
|
#else
|
|
showMenubarAction = ActionCollection::get()->createAction("showmenubar", tr("Show Menubar"));
|
|
showMenubarAction->setShortcut(Qt::ControlModifier+Qt::Key_M);
|
|
showMenubarAction->setCheckable(true);
|
|
connect(showMenubarAction, SIGNAL(toggled(bool)), this, SLOT(toggleMenubar()));
|
|
#endif
|
|
|
|
QMenu *menu=new QMenu(tr("&Music"), this);
|
|
addMenuAction(menu, refreshDbAction);
|
|
menu->addSeparator();
|
|
addMenuAction(menu, connectionsAction);
|
|
addMenuAction(menu, partitionsAction);
|
|
addMenuAction(menu, outputsAction);
|
|
#ifdef ENABLE_HTTP_STREAM_PLAYBACK
|
|
addMenuAction(menu, streamPlayAction);
|
|
#endif
|
|
menu->addSeparator();
|
|
addMenuAction(menu, quitAction);
|
|
menuBar()->addMenu(menu);
|
|
menu=new QMenu(tr("&Edit"), this);
|
|
addMenuAction(menu, PlayQueueModel::self()->undoAct());
|
|
addMenuAction(menu, PlayQueueModel::self()->redoAct());
|
|
menu->addSeparator();
|
|
addMenuAction(menu, StdActions::self()->searchAction);
|
|
addMenuAction(menu, searchPlayQueueAction);
|
|
if (Utils::KDE!=Utils::currentDe()) {
|
|
menu->addSeparator();
|
|
addMenuAction(menu, prefAction);
|
|
}
|
|
menuBar()->addMenu(menu);
|
|
if (Utils::KDE!=Utils::currentDe()) {
|
|
menu=new QMenu(tr("&View"), this);
|
|
#ifndef Q_OS_MAC
|
|
if (showMenubarAction) {
|
|
addMenuAction(menu, showMenubarAction);
|
|
menu->addSeparator();
|
|
}
|
|
#endif
|
|
addMenuAction(menu, expandInterfaceAction);
|
|
addMenuAction(menu, fullScreenAction);
|
|
//addMenuAction(menu, songInfoAction);
|
|
menuBar()->addMenu(menu);
|
|
}
|
|
menu=new QMenu(tr("&Queue"), this);
|
|
addMenuAction(menu, clearPlayQueueAction);
|
|
addMenuAction(menu, StdActions::self()->savePlayQueueAction);
|
|
addMenuAction(menu, addStreamToPlayQueueAction);
|
|
addMenuAction(menu, addLocalFilesToPlayQueueAction);
|
|
menu->addSeparator();
|
|
addMenuAction(menu, PlayQueueModel::self()->shuffleAct());
|
|
addMenuAction(menu, PlayQueueModel::self()->sortAct());
|
|
menuBar()->addMenu(menu);
|
|
if (Utils::KDE==Utils::currentDe()) {
|
|
menu=new QMenu(tr("&Settings"), this);
|
|
#ifndef Q_OS_MAC
|
|
if (showMenubarAction) {
|
|
addMenuAction(menu, showMenubarAction);
|
|
}
|
|
#endif
|
|
addMenuAction(menu, expandInterfaceAction);
|
|
addMenuAction(menu, fullScreenAction);
|
|
//addMenuAction(menu, songInfoAction);
|
|
menu->addSeparator();
|
|
addMenuAction(menu, prefAction);
|
|
menuBar()->addMenu(menu);
|
|
}
|
|
#ifdef Q_OS_MAC
|
|
OSXStyle::self()->initWindowMenu(this);
|
|
#endif
|
|
menu=new QMenu(tr("&Help"), this);
|
|
addMenuAction(menu, serverInfoAction);
|
|
addMenuAction(menu, aboutAction);
|
|
menuBar()->addMenu(menu);
|
|
}
|
|
#endif // infdef Q_OS_WIN
|
|
|
|
#ifndef Q_OS_MAC
|
|
QMenu *mainMenu=new QMenu(this);
|
|
mainMenu->addAction(expandInterfaceAction);
|
|
#ifndef Q_OS_WIN
|
|
if (showMenubarAction) {
|
|
mainMenu->addAction(showMenubarAction);
|
|
showMenubarAction->setChecked(Settings::self()->showMenubar());
|
|
toggleMenubar();
|
|
}
|
|
#endif
|
|
mainMenu->addAction(fullScreenAction);
|
|
mainMenu->addAction(connectionsAction);
|
|
mainMenu->addAction(partitionsAction);
|
|
mainMenu->addAction(outputsAction);
|
|
#ifdef ENABLE_HTTP_STREAM_PLAYBACK
|
|
mainMenu->addAction(streamPlayAction);
|
|
#endif
|
|
mainMenu->addAction(prefAction);
|
|
mainMenu->addAction(refreshDbAction);
|
|
mainMenu->addSeparator();
|
|
mainMenu->addAction(StdActions::self()->searchAction);
|
|
mainMenu->addAction(searchPlayQueueAction);
|
|
mainMenu->addSeparator();
|
|
mainMenu->addAction(serverInfoAction);
|
|
mainMenu->addAction(aboutAction);
|
|
mainMenu->addSeparator();
|
|
mainMenu->addAction(quitAction);
|
|
menuButton->setIcon(Icons::self()->toolbarMenuIcon);
|
|
menuButton->setAlignedMenu(mainMenu);
|
|
#endif // ifndef Q_OS_MAC
|
|
|
|
dynamicLabel->setVisible(false);
|
|
stopDynamicButton->setVisible(false);
|
|
StdActions::self()->addWithPriorityAction->setVisible(false);
|
|
StdActions::self()->setPriorityAction->setVisible(false);
|
|
|
|
playQueueProxyModel.setSourceModel(PlayQueueModel::self());
|
|
playQueue->setModel(&playQueueProxyModel);
|
|
playQueue->addAction(playQueue->removeFromAct());
|
|
ratingAction=new Action(tr("Set Rating"), this);
|
|
ratingAction->setMenu(new QMenu(nullptr));
|
|
for (int i=0; i<((Song::Rating_Max/Song::Rating_Step)+1); ++i) {
|
|
QString text;
|
|
if (0==i) {
|
|
text=tr("No Rating");
|
|
} else {
|
|
for (int s=0; s<i; ++s) {
|
|
text+=QChar(0x2605);
|
|
}
|
|
}
|
|
Action *action=ActionCollection::get()->createAction(QLatin1String("rating")+QString::number(i), text);
|
|
action->setProperty(constRatingKey, i*Song::Rating_Step);
|
|
action->setShortcut(Qt::AltModifier+Qt::Key_0+i);
|
|
action->setSettingsText(ratingAction);
|
|
ratingAction->menu()->addAction(action);
|
|
connect(action, SIGNAL(triggered()), SLOT(setRating()));
|
|
}
|
|
playQueue->addAction(ratingAction);
|
|
playQueue->addAction(StdActions::self()->setPriorityAction);
|
|
playQueue->addAction(stopAfterTrackAction);
|
|
playQueue->addAction(locateAction);
|
|
#ifdef TAGLIB_FOUND
|
|
playQueue->addAction(editPlayQueueTagsAction);
|
|
#endif
|
|
playQueue->addAction(playNextAction);
|
|
Action *sep=new Action(this);
|
|
sep->setSeparator(true);
|
|
playQueue->addAction(sep);
|
|
playQueue->addAction(PlayQueueModel::self()->removeDuplicatesAct());
|
|
playQueue->addAction(clearPlayQueueAction);
|
|
playQueue->addAction(cropPlayQueueAction);
|
|
playQueue->addAction(StdActions::self()->savePlayQueueAction);
|
|
playQueue->addAction(addStreamToPlayQueueAction);
|
|
playQueue->addAction(addLocalFilesToPlayQueueAction);
|
|
playQueue->addAction(addPlayQueueToStoredPlaylistAction);
|
|
#ifdef ENABLE_DEVICES_SUPPORT
|
|
playQueue->addAction(copyToDeviceAction);
|
|
#endif
|
|
playQueue->addAction(PlayQueueModel::self()->shuffleAct());
|
|
playQueue->addAction(PlayQueueModel::self()->sortAct());
|
|
playQueue->addAction(PlayQueueModel::self()->undoAct());
|
|
playQueue->addAction(PlayQueueModel::self()->redoAct());
|
|
playQueue->readConfig();
|
|
|
|
#ifdef ENABLE_DEVICES_SUPPORT
|
|
connect(DevicesModel::self(), SIGNAL(addToDevice(const QString &)), this, SLOT(addToDevice(const QString &)));
|
|
connect(DevicesModel::self(), SIGNAL(error(const QString &)), this, SLOT(showError(const QString &)));
|
|
connect(libraryPage, SIGNAL(addToDevice(const QString &, const QString &, const QList<Song> &)), SLOT(copyToDevice(const QString &, const QString &, const QList<Song> &)));
|
|
connect(folderPage->mpd(), SIGNAL(addToDevice(const QString &, const QString &, const QList<Song> &)), SLOT(copyToDevice(const QString &, const QString &, const QList<Song> &)));
|
|
connect(playlistsPage, SIGNAL(addToDevice(const QString &, const QString &, const QList<Song> &)), SLOT(copyToDevice(const QString &, const QString &, const QList<Song> &)));
|
|
connect(devicesPage, SIGNAL(addToDevice(const QString &, const QString &, const QList<Song> &)), SLOT(copyToDevice(const QString &, const QString &, const QList<Song> &)));
|
|
connect(searchPage, SIGNAL(addToDevice(const QString &, const QString &, const QList<Song> &)), SLOT(copyToDevice(const QString &, const QString &, const QList<Song> &)));
|
|
connect(StdActions::self()->deleteSongsAction, SIGNAL(triggered()), SLOT(deleteSongs()));
|
|
connect(devicesPage, SIGNAL(deleteSongs(const QString &, const QList<Song> &)), SLOT(deleteSongs(const QString &, const QList<Song> &)));
|
|
connect(libraryPage, SIGNAL(deleteSongs(const QString &, const QList<Song> &)), SLOT(deleteSongs(const QString &, const QList<Song> &)));
|
|
connect(folderPage->mpd(), SIGNAL(deleteSongs(const QString &, const QList<Song> &)), SLOT(deleteSongs(const QString &, const QList<Song> &)));
|
|
connect(searchPage, SIGNAL(deleteSongs(const QString &, const QList<Song> &)), SLOT(deleteSongs(const QString &, const QList<Song> &)));
|
|
#endif
|
|
for (QAction *act: StdActions::self()->setPriorityAction->menu()->actions()) {
|
|
connect(act, SIGNAL(triggered()), this, SLOT(addWithPriority()));
|
|
}
|
|
for (QAction *act: StdActions::self()->addWithPriorityAction->menu()->actions()) {
|
|
connect(act, SIGNAL(triggered()), this, SLOT(addWithPriority()));
|
|
}
|
|
connect(StdActions::self()->appendToPlayQueueAndPlayAction, SIGNAL(triggered()), this, SLOT(appendToPlayQueueAndPlay()));
|
|
connect(StdActions::self()->addToPlayQueueAndPlayAction, SIGNAL(triggered()), this, SLOT(addToPlayQueueAndPlay()));
|
|
connect(StdActions::self()->insertAfterCurrentAction, SIGNAL(triggered()), this, SLOT(insertIntoPlayQueue()));
|
|
connect(MPDConnection::self(), SIGNAL(partitionsUpdated(const QList<Partition> &)), this, SLOT(partitionsUpdated(const QList<Partition> &)));
|
|
connect(MPDConnection::self(), SIGNAL(outputsUpdated(const QList<Output> &)), this, SLOT(outputsUpdated(const QList<Output> &)));
|
|
connect(this, SIGNAL(changePartition(QString)), MPDConnection::self(), SLOT(changePartition(QString)));
|
|
connect(this, SIGNAL(newPartition(QString)), MPDConnection::self(), SLOT(newPartition(QString)));
|
|
connect(this, SIGNAL(delPartition(QString)), MPDConnection::self(), SLOT(delPartition(QString)));
|
|
connect(this, SIGNAL(enableOutput(quint32, bool)), MPDConnection::self(), SLOT(enableOutput(quint32, bool)));
|
|
connect(this, SIGNAL(outputs()), MPDConnection::self(), SLOT(outputs()));
|
|
connect(this, SIGNAL(pause(bool)), MPDConnection::self(), SLOT(setPause(bool)));
|
|
connect(this, SIGNAL(play()), MPDConnection::self(), SLOT(play()));
|
|
connect(this, SIGNAL(stop(bool)), MPDConnection::self(), SLOT(stopPlaying(bool)));
|
|
connect(this, SIGNAL(terminating()), MPDConnection::self(), SLOT(stop()));
|
|
connect(this, SIGNAL(getStatus()), MPDConnection::self(), SLOT(getStatus()));
|
|
connect(this, SIGNAL(playListInfo()), MPDConnection::self(), SLOT(playListInfo()));
|
|
connect(this, SIGNAL(currentSong()), MPDConnection::self(), SLOT(currentSong()));
|
|
connect(this, SIGNAL(setSeekId(qint32, quint32)), MPDConnection::self(), SLOT(setSeekId(qint32, quint32)));
|
|
connect(this, SIGNAL(startPlayingSongId(qint32)), MPDConnection::self(), SLOT(startPlayingSongId(qint32)));
|
|
connect(this, SIGNAL(setDetails(const MPDConnectionDetails &)), MPDConnection::self(), SLOT(setDetails(const MPDConnectionDetails &)));
|
|
connect(this, SIGNAL(setPriority(const QList<qint32> &, quint8, bool)), MPDConnection::self(), SLOT(setPriority(const QList<qint32> &, quint8, bool)));
|
|
connect(this, SIGNAL(addSongsToPlaylist(const QString &, const QStringList &)), MPDConnection::self(), SLOT(addToPlaylist(const QString &, const QStringList &)));
|
|
connect(PlayQueueModel::self(), SIGNAL(statsUpdated(int, quint32)), this, SLOT(updatePlayQueueStats(int, quint32)));
|
|
connect(PlayQueueModel::self(), SIGNAL(fetchingStreams()), playQueue, SLOT(showSpinner()));
|
|
connect(PlayQueueModel::self(), SIGNAL(streamsFetched()), playQueue, SLOT(hideSpinner()));
|
|
connect(PlayQueueModel::self(), SIGNAL(updateCurrent(Song)), SLOT(updateCurrentSong(Song)));
|
|
connect(PlayQueueModel::self(), SIGNAL(streamFetchStatus(QString)), playQueue, SLOT(streamFetchStatus(QString)));
|
|
connect(PlayQueueModel::self(), SIGNAL(error(QString)), SLOT(showError(QString)));
|
|
connect(playQueue, SIGNAL(cancelStreamFetch()), PlayQueueModel::self(), SLOT(cancelStreamFetch()));
|
|
connect(playQueue, SIGNAL(itemsSelected(bool)), SLOT(playQueueItemsSelected(bool)));
|
|
connect(MPDStatus::self(), SIGNAL(updated()), this, SLOT(updateStatus()));
|
|
connect(MPDConnection::self(), SIGNAL(playlistUpdated(const QList<Song> &, bool)), this, SLOT(updatePlayQueue(const QList<Song> &, bool)));
|
|
connect(MPDConnection::self(), SIGNAL(currentSongUpdated(Song)), this, SLOT(updateCurrentSong(Song)));
|
|
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(info(const QString &)), SLOT(showInformation(const QString &)));
|
|
connect(MPDConnection::self(), SIGNAL(dirChanged()), SLOT(checkMpdDir()));
|
|
connect(MPDConnection::self(), SIGNAL(connectionNotChanged(QString)), SLOT(mpdConnectionName(QString)));
|
|
connect(MpdLibraryModel::self(), SIGNAL(error(QString)), SLOT(showError(QString)));
|
|
connect(ApiKeys::self(), SIGNAL(error(const QString &)), SLOT(showError(const QString &)));
|
|
connect(refreshDbAction, SIGNAL(triggered()), this, SLOT(refreshDbPromp()));
|
|
connect(doDbRefreshAction, SIGNAL(triggered()), MpdLibraryModel::self(), SLOT(clearDb()));
|
|
connect(doDbRefreshAction, SIGNAL(triggered()), MPDConnection::self(), SLOT(update()));
|
|
connect(doDbRefreshAction, SIGNAL(triggered()), messageWidget, SLOT(animatedHide()));
|
|
connect(connectAction, SIGNAL(triggered()), this, SLOT(connectToMpd()));
|
|
connect(StdActions::self()->prevTrackAction, SIGNAL(triggered()), MPDConnection::self(), SLOT(goToPrevious()));
|
|
connect(StdActions::self()->nextTrackAction, SIGNAL(triggered()), MPDConnection::self(), SLOT(goToNext()));
|
|
connect(StdActions::self()->playPauseTrackAction, SIGNAL(triggered()), this, SLOT(playPauseTrack()));
|
|
connect(StdActions::self()->stopPlaybackAction, SIGNAL(triggered()), this, SLOT(stopPlayback()));
|
|
connect(StdActions::self()->stopAfterCurrentTrackAction, SIGNAL(triggered()), this, SLOT(stopAfterCurrentTrack()));
|
|
connect(stopAfterTrackAction, SIGNAL(triggered()), this, SLOT(stopAfterTrack()));
|
|
connect(this, SIGNAL(setVolume(int)), MPDConnection::self(), SLOT(setVolume(int)));
|
|
connect(nowPlaying, SIGNAL(sliderReleased()), this, SLOT(setPosition()));
|
|
connect(PlayQueueModel::self(), SIGNAL(currentSongRating(QString,quint8)), nowPlaying, SLOT(rating(QString,quint8)));
|
|
connect(randomPlayQueueAction, SIGNAL(triggered(bool)), MPDConnection::self(), SLOT(setRandom(bool)));
|
|
connect(repeatPlayQueueAction, SIGNAL(triggered(bool)), MPDConnection::self(), SLOT(setRepeat(bool)));
|
|
connect(singlePlayQueueAction, SIGNAL(triggered(bool)), MPDConnection::self(), SLOT(setSingle(bool)));
|
|
connect(consumePlayQueueAction, SIGNAL(triggered(bool)), MPDConnection::self(), SLOT(setConsume(bool)));
|
|
#ifdef ENABLE_HTTP_STREAM_PLAYBACK
|
|
connect(streamPlayAction, SIGNAL(triggered(bool)), HttpStream::self(), SLOT(setEnabled(bool)));
|
|
connect(MPDConnection::self(), SIGNAL(streamUrl(QString)), SLOT(streamUrl(QString)));
|
|
#endif
|
|
connect(playQueueSearchWidget, SIGNAL(returnPressed()), this, SLOT(searchPlayQueue()));
|
|
connect(playQueueSearchWidget, SIGNAL(textChanged(const QString)), this, SLOT(searchPlayQueue()));
|
|
connect(playQueueSearchWidget, SIGNAL(active(bool)), this, SLOT(playQueueSearchActivated(bool)));
|
|
connect(playQueue, SIGNAL(doubleClicked(const QModelIndex &)), this, SLOT(playQueueItemActivated(const QModelIndex &)));
|
|
connect(StdActions::self()->removeAction, SIGNAL(triggered()), this, SLOT(removeItems()));
|
|
connect(StdActions::self()->appendToPlayQueueAction, SIGNAL(triggered()), this, SLOT(appendToPlayQueue()));
|
|
connect(StdActions::self()->replacePlayQueueAction, SIGNAL(triggered()), this, SLOT(replacePlayQueue()));
|
|
connect(playQueue->removeFromAct(), SIGNAL(triggered()), this, SLOT(removeFromPlayQueue()));
|
|
connect(clearPlayQueueAction, SIGNAL(triggered()), playQueueSearchWidget, SLOT(clear()));
|
|
connect(clearPlayQueueAction, SIGNAL(triggered()), this, SLOT(clearPlayQueue()));
|
|
connect(centerPlayQueueAction, SIGNAL(triggered()), this, SLOT(centerPlayQueue()));
|
|
connect(cropPlayQueueAction, SIGNAL(triggered()), this, SLOT(cropPlayQueue()));
|
|
connect(songInfoAction, SIGNAL(triggered()), this, SLOT(showSongInfo()));
|
|
connect(expandInterfaceAction, SIGNAL(triggered()), this, SLOT(expandOrCollapse()));
|
|
connect(fullScreenAction, SIGNAL(triggered()), this, SLOT(fullScreen()));
|
|
#ifdef TAGLIB_FOUND
|
|
connect(StdActions::self()->editTagsAction, SIGNAL(triggered()), this, SLOT(editTags()));
|
|
connect(editPlayQueueTagsAction, SIGNAL(triggered()), this, SLOT(editTags()));
|
|
connect(StdActions::self()->organiseFilesAction, SIGNAL(triggered()), SLOT(organiseFiles()));
|
|
#endif
|
|
connect(context, SIGNAL(findArtist(QString)), this, SLOT(locateArtist(QString)));
|
|
connect(context, SIGNAL(findAlbum(QString,QString)), this, SLOT(locateAlbum(QString,QString)));
|
|
connect(context, SIGNAL(playSong(QString)), PlayQueueModel::self(), SLOT(playSong(QString)));
|
|
connect(locateTrackAction, SIGNAL(triggered()), this, SLOT(locateTrack()));
|
|
connect(locateAlbumAction, SIGNAL(triggered()), this, SLOT(locateTrack()));
|
|
connect(locateArtistAction, SIGNAL(triggered()), this, SLOT(locateTrack()));
|
|
connect(this, SIGNAL(playNext(QList<quint32>,quint32,quint32)), MPDConnection::self(), SLOT(move(QList<quint32>,quint32,quint32)));
|
|
connect(playNextAction, SIGNAL(triggered()), this, SLOT(moveSelectionAfterCurrentSong()));
|
|
connect(qApp, SIGNAL(paletteChanged(const QPalette &)), this, SLOT(paletteChanged()));
|
|
|
|
connect(StdActions::self()->searchAction, SIGNAL(triggered()), SLOT(showSearch()));
|
|
connect(searchPlayQueueAction, SIGNAL(triggered()), this, SLOT(showPlayQueueSearch()));
|
|
connect(playQueue, SIGNAL(focusSearch(QString)), playQueueSearchWidget, SLOT(activate(QString)));
|
|
connect(expandAllAction, SIGNAL(triggered()), this, SLOT(expandAll()));
|
|
connect(collapseAllAction, SIGNAL(triggered()), this, SLOT(collapseAll()));
|
|
connect(addStreamToPlayQueueAction, SIGNAL(triggered()), this, SLOT(addStreamToPlayQueue()));
|
|
connect(addLocalFilesToPlayQueueAction, SIGNAL(triggered()), this, SLOT(addLocalFilesToPlayQueue()));
|
|
connect(splitter, SIGNAL(splitterMoved(int, int)), this, SLOT(controlPlayQueueButtons()));
|
|
connect(StdActions::self()->setCoverAction, SIGNAL(triggered()), SLOT(setCover()));
|
|
#ifdef ENABLE_REPLAYGAIN_SUPPORT
|
|
connect(StdActions::self()->replaygainAction, SIGNAL(triggered()), SLOT(replayGain()));
|
|
#endif
|
|
connect(PlaylistsModel::self(), SIGNAL(addToNew()), this, SLOT(addToNewStoredPlaylist()));
|
|
connect(PlaylistsModel::self(), SIGNAL(addToExisting(const QString &)), this, SLOT(addToExistingStoredPlaylist(const QString &)));
|
|
// TODO: Why is this here???
|
|
// connect(playlistsPage, SIGNAL(add(const QStringList &, bool, quint8)), PlayQueueModel::self(), SLOT(addItems(const QStringList &, bool, quint8)));
|
|
connect(coverWidget, SIGNAL(clicked()), expandInterfaceAction, SLOT(trigger()));
|
|
#if !defined Q_OS_WIN && !defined Q_OS_MAC
|
|
connect(MountPoints::self(), SIGNAL(updated()), SLOT(checkMpdAccessibility()));
|
|
connect(qApp, SIGNAL(commitDataRequest(QSessionManager&)), SLOT(commitDataRequest(QSessionManager&)));
|
|
#endif
|
|
playQueueItemsSelected(false);
|
|
playQueue->setFocus();
|
|
|
|
MPDConnection::self()->start();
|
|
connectToMpd();
|
|
|
|
QString page=Settings::self()->page();
|
|
for (int i=0; i<tabWidget->count(); ++i) {
|
|
if (tabWidget->widget(i)->metaObject()->className()==page) {
|
|
tabWidget->setCurrentIndex(i);
|
|
break;
|
|
}
|
|
}
|
|
|
|
connect(tabWidget, SIGNAL(currentChanged(int)), this, SLOT(currentTabChanged(int)));
|
|
connect(tabWidget, SIGNAL(tabToggled(int)), this, SLOT(tabToggled(int)));
|
|
connect(tabWidget, SIGNAL(styleChanged(int)), this, SLOT(sidebarModeChanged()));
|
|
|
|
readSettings();
|
|
updateConnectionsMenu();
|
|
ActionCollection::get()->readSettings();
|
|
|
|
if (testAttribute(Qt::WA_TranslucentBackground)) {
|
|
// BUG: 146 - Work-around non-showing main window on start-up with transparent QtCurve windows.
|
|
move(p.isNull() ? QPoint(96, 96) : p);
|
|
}
|
|
|
|
currentTabChanged(tabWidget->currentIndex());
|
|
|
|
if (Settings::self()->firstRun() && MPDConnection::self()->isConnected()) {
|
|
mpdConnectionStateChanged(true);
|
|
}
|
|
MediaKeys::self()->start();
|
|
#ifdef Q_OS_MAC
|
|
dockMenu=new DockMenu(this);
|
|
#endif
|
|
#ifdef MAC_MEDIAPLAYER_FOUND
|
|
macNowPlaying=new MacNowPlaying(this);
|
|
#endif
|
|
updateActionToolTips();
|
|
CustomActions::self()->setMainWindow(this);
|
|
|
|
if (Utils::useSystemTray() && Settings::self()->startHidden()) {
|
|
hide();
|
|
} else {
|
|
show();
|
|
}
|
|
QTimer::singleShot(0, this, SLOT(controlPlayQueueButtons()));
|
|
}
|
|
|
|
MainWindow::~MainWindow()
|
|
{
|
|
#if !defined Q_OS_WIN && !defined Q_OS_MAC
|
|
if (showMenubarAction) {
|
|
Settings::self()->saveShowMenubar(showMenubarAction->isChecked());
|
|
}
|
|
#endif
|
|
#ifdef ENABLE_HTTP_STREAM_PLAYBACK
|
|
HttpStream::self()->save();
|
|
#endif
|
|
bool hadCantataStreams=PlayQueueModel::self()->removeCantataStreams();
|
|
Settings::self()->saveShowFullScreen(fullScreenAction->isChecked());
|
|
if (!fullScreenAction->isChecked()) {
|
|
if (expandInterfaceAction->isChecked()) {
|
|
Settings::self()->saveMaximized(isMaximized());
|
|
Settings::self()->saveMainWindowSize(isMaximized() ? previousSize : size());
|
|
} else {
|
|
Settings::self()->saveMaximized(false);
|
|
Settings::self()->saveMainWindowSize(expandedSize);
|
|
}
|
|
Settings::self()->saveMainWindowCollapsedSize(expandInterfaceAction->isChecked() ? collapsedSize : size());
|
|
Settings::self()->saveShowPlaylist(expandInterfaceAction->isChecked());
|
|
Settings::self()->saveMainWindowPos(pos());
|
|
}
|
|
#ifdef ENABLE_HTTP_STREAM_PLAYBACK
|
|
Settings::self()->savePlayStream(streamPlayAction->isVisible() && streamPlayAction->isChecked());
|
|
#endif
|
|
if (!fullScreenAction->isChecked()) {
|
|
if (!tabWidget->isEnabled(PAGE_PLAYQUEUE)) {
|
|
Settings::self()->saveSplitterState(splitter->saveState());
|
|
}
|
|
}
|
|
Settings::self()->savePage(tabWidget->currentWidget()->metaObject()->className());
|
|
playQueue->saveConfig();
|
|
// playlistsPage->saveConfig();
|
|
context->saveConfig();
|
|
StreamsModel::self()->save();
|
|
nowPlaying->saveConfig();
|
|
Settings::self()->saveForceSingleClick(TreeView::getForceSingleClick());
|
|
if (Utils::useSystemTray()) {
|
|
Settings::StartupState startupState=Settings::self()->startupState();
|
|
Settings::self()->saveStartHidden(trayItem->isActive() && Settings::self()->minimiseOnClose() &&
|
|
((isHidden() && Settings::SS_ShowMainWindow!=startupState) || (Settings::SS_HideMainWindow==startupState)));
|
|
}
|
|
Settings::self()->save();
|
|
disconnect(MPDConnection::self(), nullptr, nullptr, nullptr);
|
|
if (Settings::self()->stopOnExit()) {
|
|
DynamicPlaylists::self()->stop();
|
|
}
|
|
if (Settings::self()->stopOnExit()) {
|
|
emit terminating();
|
|
Utils::sleep(); // Allow time for stop to be sent...
|
|
} else if (hadCantataStreams) {
|
|
Utils::sleep(); // Allow time for removal of cantata streams...
|
|
}
|
|
#ifdef ENABLE_DEVICES_SUPPORT
|
|
DevicesModel::self()->stop();
|
|
#if defined CDDB_FOUND || defined MUSICBRAINZ5_FOUND
|
|
Covers::self()->cleanCdda();
|
|
#endif
|
|
#endif
|
|
MediaKeys::self()->stop();
|
|
#ifdef TAGLIB_FOUND
|
|
Tags::stop();
|
|
#endif
|
|
ThreadCleaner::self()->stopAll();
|
|
Configuration(playQueuePage->metaObject()->className()).set(ItemView::constSearchActiveKey, playQueueSearchWidget->isActive());
|
|
}
|
|
|
|
QList<Song> MainWindow::selectedSongs() const
|
|
{
|
|
return currentPage ? currentPage->selectedSongs() : QList<Song>();
|
|
}
|
|
|
|
QStringList MainWindow::listActions() const
|
|
{
|
|
QStringList allActions;
|
|
for(int i = 0; i < ActionCollection::get()->actions().count(); i++) {
|
|
Action *action = qobject_cast<Action *>(ActionCollection::get()->actions().at(i));
|
|
if (!action) {
|
|
continue;
|
|
}
|
|
allActions.append(action->objectName()+" - "+Action::settingsText(action));
|
|
}
|
|
return allActions;
|
|
}
|
|
|
|
void MainWindow::triggerAction(const QString &name)
|
|
{
|
|
QAction *act=ActionCollection::get()->action(name);
|
|
if (act) {
|
|
act->trigger();
|
|
}
|
|
}
|
|
|
|
#if !defined Q_OS_WIN
|
|
void MainWindow::addMenuAction(QMenu *menu, QAction *action)
|
|
{
|
|
menu->addAction(action);
|
|
addAction(action); // Bind action to window, so that it works when fullscreen!
|
|
}
|
|
#endif
|
|
|
|
void MainWindow::showError(const QString &message, bool showActions)
|
|
{
|
|
if (!message.isEmpty()) {
|
|
if (!isVisible()) {
|
|
show();
|
|
}
|
|
expand();
|
|
}
|
|
|
|
if (QLatin1String("NO_SONGS")==message) {
|
|
messageWidget->setError(tr("Failed to locate any songs matching the dynamic playlist rules."));
|
|
} else {
|
|
messageWidget->setError(message);
|
|
}
|
|
if (showActions) {
|
|
messageWidget->setActions(QList<QAction*>() << prefAction << connectAction);
|
|
} else {
|
|
messageWidget->removeAllActions();
|
|
}
|
|
QApplication::alert(this);
|
|
}
|
|
|
|
void MainWindow::showInformation(const QString &message)
|
|
{
|
|
if (!messageWidget->showingError()) {
|
|
messageWidget->setInformation(message);
|
|
messageWidget->removeAllActions();
|
|
}
|
|
}
|
|
|
|
void MainWindow::mpdConnectionStateChanged(bool connected)
|
|
{
|
|
serverInfoAction->setEnabled(connected && !MPDConnection::self()->isMopidy());
|
|
refreshDbAction->setEnabled(connected);
|
|
addStreamToPlayQueueAction->setEnabled(connected);
|
|
addLocalFilesToPlayQueueAction->setEnabled(connected && (HttpServer::self()->isAlive() || MPDConnection::self()->localFilePlaybackSupported()));
|
|
if (connected) {
|
|
//if (!messageWidget->showingError()) {
|
|
messageWidget->hide();
|
|
//}
|
|
if (CS_Connected!=connectedState) {
|
|
emit playListInfo();
|
|
emit outputs();
|
|
MpdLibraryModel::self()->reload();
|
|
if (CS_Init!=connectedState) {
|
|
currentTabChanged(tabWidget->currentIndex());
|
|
}
|
|
connectedState=CS_Connected;
|
|
StdActions::self()->addWithPriorityAction->setVisible(MPDConnection::self()->canUsePriority());
|
|
StdActions::self()->setPriorityAction->setVisible(MPDConnection::self()->canUsePriority());
|
|
}
|
|
} else {
|
|
libraryPage->clear();
|
|
folderPage->mpd()->clear();
|
|
PlaylistsModel::self()->clear();
|
|
PlayQueueModel::self()->clear();
|
|
searchPage->clear();
|
|
connectedState=CS_Disconnected;
|
|
outputsAction->setVisible(false);
|
|
MPDStatus dummyStatus;
|
|
updateStatus(&dummyStatus);
|
|
}
|
|
updateWindowTitle();
|
|
}
|
|
|
|
void MainWindow::keyPressEvent(QKeyEvent *event)
|
|
{
|
|
if ((Qt::Key_Enter==event->key() || Qt::Key_Return==event->key()) && playQueue->hasFocus()) {
|
|
QModelIndexList selection=playQueue->selectedIndexes();
|
|
if (!selection.isEmpty()) {
|
|
//play the first selected song
|
|
playQueueItemActivated(selection.first());
|
|
}
|
|
}
|
|
}
|
|
|
|
void MainWindow::showEvent(QShowEvent *event)
|
|
{
|
|
#if defined Q_OS_WIN
|
|
if (!thumbnailTooolbar) {
|
|
thumbnailTooolbar=new ThumbnailToolBar(this);
|
|
}
|
|
#endif
|
|
QMainWindow::showEvent(event);
|
|
controlView();
|
|
if (!shown) {
|
|
shown=true;
|
|
// Work-around for qt5ct palette issues...
|
|
QTimer::singleShot(0, this, SLOT(paletteChanged()));
|
|
}
|
|
}
|
|
|
|
void MainWindow::closeEvent(QCloseEvent *event)
|
|
{
|
|
if (trayItem->isActive() && Settings::self()->minimiseOnClose() && event->spontaneous()) {
|
|
lastPos=pos();
|
|
hide();
|
|
event->ignore();
|
|
} else if (canClose()) {
|
|
QMainWindow::closeEvent(event);
|
|
}
|
|
}
|
|
|
|
void MainWindow::resizeEvent(QResizeEvent *event)
|
|
{
|
|
previousSize=event->oldSize();
|
|
QMainWindow::resizeEvent(event);
|
|
controlView();
|
|
}
|
|
|
|
void MainWindow::playQueueItemsSelected(bool s)
|
|
{
|
|
int rc=playQueue->model() ? playQueue->model()->rowCount() : 0;
|
|
bool haveItems=rc>0;
|
|
bool singleSelection=1==playQueue->selectedIndexes(false).count(); // Dont need sorted selection here...
|
|
playQueue->removeFromAct()->setEnabled(s && haveItems);
|
|
StdActions::self()->setPriorityAction->setEnabled(s && haveItems);
|
|
locateAction->setEnabled(singleSelection);
|
|
cropPlayQueueAction->setEnabled(playQueue->haveUnSelectedItems() && haveItems);
|
|
#ifdef TAGLIB_FOUND
|
|
editPlayQueueTagsAction->setEnabled(s && haveItems && MPDConnection::self()->getDetails().dirReadable);
|
|
#endif
|
|
addPlayQueueToStoredPlaylistAction->setEnabled(haveItems);
|
|
#ifdef ENABLE_DEVICES_SUPPORT
|
|
copyToDeviceAction->setEnabled(editPlayQueueTagsAction->isEnabled());
|
|
#endif
|
|
stopAfterTrackAction->setEnabled(singleSelection);
|
|
ratingAction->setEnabled(s && haveItems);
|
|
}
|
|
|
|
void MainWindow::connectToMpd(const MPDConnectionDetails &details)
|
|
{
|
|
if (!MPDConnection::self()->isConnected() || details!=MPDConnection::self()->getDetails()) {
|
|
libraryPage->clear();
|
|
folderPage->mpd()->clear();
|
|
PlaylistsModel::self()->clear();
|
|
PlayQueueModel::self()->clear();
|
|
searchPage->clear();
|
|
if (!MPDConnection::self()->getDetails().isEmpty() && details!=MPDConnection::self()->getDetails()) {
|
|
DynamicPlaylists::self()->stop();
|
|
}
|
|
showInformation(tr("Connecting to %1").arg(details.description()));
|
|
outputsAction->setVisible(false);
|
|
if (CS_Init!=connectedState) {
|
|
connectedState=CS_Disconnected;
|
|
}
|
|
}
|
|
emit setDetails(details);
|
|
}
|
|
|
|
void MainWindow::connectToMpd()
|
|
{
|
|
messageWidget->hide();
|
|
connectToMpd(Settings::self()->connectionDetails());
|
|
}
|
|
|
|
void MainWindow::streamUrl(const QString &u)
|
|
{
|
|
#ifdef ENABLE_HTTP_STREAM_PLAYBACK
|
|
streamPlayAction->setVisible(!u.isEmpty());
|
|
streamPlayAction->setChecked(streamPlayAction->isVisible() && Settings::self()->playStream());
|
|
HttpStream::self()->setEnabled(streamPlayAction->isChecked());
|
|
#else
|
|
Q_UNUSED(u)
|
|
#endif
|
|
}
|
|
|
|
void MainWindow::refreshDbPromp()
|
|
{
|
|
int btnLayout=style()->styleHint(QStyle::SH_DialogButtonLayout);
|
|
if (QDialogButtonBox::GnomeLayout==btnLayout || QDialogButtonBox::MacLayout==btnLayout) {
|
|
messageWidget->setActions(QList<QAction*>() << cancelAction << doDbRefreshAction);
|
|
} else {
|
|
messageWidget->setActions(QList<QAction*>() << doDbRefreshAction << cancelAction);
|
|
}
|
|
messageWidget->setWarning(tr("Refresh MPD Database?"), false);
|
|
expand();
|
|
}
|
|
|
|
void MainWindow::showAboutDialog()
|
|
{
|
|
QMessageBox::about(this, tr("About Cantata"),
|
|
tr("<b>Cantata %1</b><br/><br/>MPD client.<br/><br/>"
|
|
"© 2011-2021 Craig Drummond<br/>Released under the <a href=\"http://www.gnu.org/licenses/gpl.html\">GPLv3</a>").arg(PACKAGE_VERSION_STRING)+
|
|
QLatin1String("<br/><br/>")+
|
|
tr("Please refer to <a href=\"https://github.com/CDrummond/cantata/issues\">Cantata's issue tracker</a> for a list of known issues, and to report new issues.")+
|
|
QLatin1String("<br/><br/><i><small>")+
|
|
tr("Based upon <a href=\"http://lowblog.nl\">QtMPC</a> - © 2007-2010 The QtMPC Authors<br/>")+
|
|
tr("Context view backdrops courtesy of <a href=\"http://www.fanart.tv\">FanArt.tv</a>")+QLatin1String("<br/>")+
|
|
tr("Context view metadata courtesy of <a href=\"http://www.wikipedia.org\">Wikipedia</a> and <a href=\"http://www.last.fm\">Last.fm</a>")+
|
|
QLatin1String("<br/><br/>")+tr("Please consider uploading your own music fan-art to <a href=\"http://www.fanart.tv\">FanArt.tv</a>")+
|
|
QLatin1String("</small></i>"));
|
|
}
|
|
|
|
bool MainWindow::canClose()
|
|
{
|
|
if (onlinePage->isDownloading() &&
|
|
MessageBox::No==MessageBox::warningYesNo(this, tr("A Podcast is currently being downloaded\n\nQuitting now will abort the download."),
|
|
QString(), GuiItem(tr("Abort download and quit")), GuiItem("Do not quit just yet"))) {
|
|
return false;
|
|
}
|
|
onlinePage->cancelAll();
|
|
return true;
|
|
}
|
|
|
|
void MainWindow::expand()
|
|
{
|
|
if (!expandInterfaceAction->isChecked()) {
|
|
expandInterfaceAction->setChecked(true);
|
|
expandOrCollapse();
|
|
}
|
|
}
|
|
|
|
bool MainWindow::canShowDialog()
|
|
{
|
|
if (PreferencesDialog::instanceCount() || CoverDialog::instanceCount()
|
|
#ifdef TAGLIB_FOUND
|
|
|| TagEditor::instanceCount() || TrackOrganiser::instanceCount()
|
|
#endif
|
|
#ifdef ENABLE_DEVICES_SUPPORT
|
|
|| ActionDialog::instanceCount() || SyncDialog::instanceCount()
|
|
#endif
|
|
#ifdef ENABLE_REPLAYGAIN_SUPPORT
|
|
|| RgDialog::instanceCount()
|
|
#endif
|
|
) {
|
|
MessageBox::error(this, tr("Please close other dialogs first."));
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void MainWindow::showPreferencesDialog(const QString &page)
|
|
{
|
|
if (PreferencesDialog::instanceCount()) {
|
|
emit showPreferencesPage(page.isEmpty() ? "collection" : page);
|
|
}
|
|
if (PreferencesDialog::instanceCount() || !canShowDialog()) {
|
|
return;
|
|
}
|
|
PreferencesDialog *pref=new PreferencesDialog(this);
|
|
controlConnectionsMenu(false);
|
|
connect(pref, SIGNAL(settingsSaved()), this, SLOT(updateSettings()));
|
|
connect(pref, SIGNAL(destroyed()), SLOT(controlConnectionsMenu()));
|
|
connect(this, SIGNAL(showPreferencesPage(QString)), pref, SLOT(showPage(QString)));
|
|
if (!page.isEmpty()) {
|
|
pref->showPage(page);
|
|
}
|
|
pref->show();
|
|
}
|
|
|
|
void MainWindow::quit()
|
|
{
|
|
if (!canClose()) {
|
|
return;
|
|
}
|
|
#ifdef ENABLE_REPLAYGAIN_SUPPORT
|
|
if (RgDialog::instanceCount()) {
|
|
return;
|
|
}
|
|
#endif
|
|
#ifdef TAGLIB_FOUND
|
|
if (TagEditor::instanceCount() || 0!=TrackOrganiser::instanceCount()) {
|
|
return;
|
|
}
|
|
#endif
|
|
#ifdef ENABLE_DEVICES_SUPPORT
|
|
if (ActionDialog::instanceCount() || SyncDialog::instanceCount()) {
|
|
return;
|
|
}
|
|
#endif
|
|
qApp->quit();
|
|
}
|
|
|
|
void MainWindow::commitDataRequest(QSessionManager &mgr)
|
|
{
|
|
Q_UNUSED(mgr)
|
|
#if !defined Q_OS_WIN && !defined Q_OS_MAC
|
|
if (isVisible() && trayItem->isActive() && Settings::self()->minimiseOnClose()) {
|
|
// Issue 1183 If We are using a tray item, and have window open - then cantata intercepts close events
|
|
// and hides the window. This seems to prevent the logout dialog appwaring under GNOME Shell.
|
|
// To work-around this if qApp emits commitDataRequest and our window is open, them close.
|
|
QMainWindow::close();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void MainWindow::checkMpdDir()
|
|
{
|
|
#ifdef Q_OS_LINUX
|
|
if (mpdAccessibilityTimer) {
|
|
mpdAccessibilityTimer->stop();
|
|
}
|
|
MPDConnection::self()->setDirReadable();
|
|
#endif
|
|
|
|
#ifdef TAGLIB_FOUND
|
|
if (StdActions::self()->editTagsAction->isEnabled()) {
|
|
StdActions::self()->editTagsAction->setEnabled(MPDConnection::self()->getDetails().dirReadable);
|
|
}
|
|
#endif
|
|
if (currentPage) {
|
|
currentPage->controlActions();
|
|
}
|
|
}
|
|
|
|
|
|
void MainWindow::partitionsUpdated(const QList<Partition> &partitions)
|
|
{
|
|
QMenu *menu=partitionsAction->menu();
|
|
menu->clear();
|
|
|
|
const QString ¤t = MPDStatus::self()->partition();
|
|
QList<Partition> sortedPartitions=partitions;
|
|
std::sort(sortedPartitions.begin(), sortedPartitions.end());
|
|
for (const Partition &p: sortedPartitions) {
|
|
QAction *act=menu->addAction(p.name, this, SLOT(selectPartition()));
|
|
act->setData(p.name);
|
|
act->setCheckable(true);
|
|
act->setChecked(p.name==current);
|
|
act->setActionGroup(partitionsGroup);
|
|
}
|
|
menu->addSeparator();
|
|
menu->addAction(tr("Create new partition"), this, SLOT(createNewPartition()));
|
|
QMenu* deleteMenu = menu->addMenu(tr("Remove partition"));
|
|
deleteMenu->menuAction()->setData("deletepartition");
|
|
for (const Partition &p: sortedPartitions) {
|
|
QAction *act=deleteMenu->addAction(p.name, this, SLOT(deleteAPartition()));
|
|
act->setData(p.name);
|
|
act->setVisible(p.name!=current);
|
|
}
|
|
deleteMenu->menuAction()->setVisible(deleteMenu->actions().count()>1);
|
|
|
|
partitionsAction->setVisible(partitions.count()>0);
|
|
trayItem->updatePartitions();
|
|
}
|
|
|
|
void MainWindow::outputsUpdated(const QList<Output> &outputs)
|
|
{
|
|
const char *constMpdConName="mpd-name";
|
|
const char *constMpdEnabledOuptuts="mpd-outputs";
|
|
QString lastConn=property(constMpdConName).toString();
|
|
QString newConn=MPDConnection::self()->getDetails().name;
|
|
setProperty(constMpdConName, newConn);
|
|
outputsAction->setVisible(true);
|
|
QSet<QString> enabledMpd;
|
|
QSet<QString> lastEnabledMpd=QSet<QString>::fromList(property(constMpdEnabledOuptuts).toStringList());
|
|
QSet<QString> mpd;
|
|
QSet<QString> menuItems;
|
|
QMenu *menu=outputsAction->menu();
|
|
for (const Output &o: outputs) {
|
|
if (o.enabled) {
|
|
enabledMpd.insert(o.name);
|
|
}
|
|
mpd.insert(o.name);
|
|
}
|
|
|
|
for (QAction *act: menu->actions()) {
|
|
menuItems.insert(act->data().toString());
|
|
}
|
|
|
|
if (menuItems!=mpd) {
|
|
menu->clear();
|
|
QList<Output> out=outputs;
|
|
std::sort(out.begin(), out.end());
|
|
int i=Qt::Key_1;
|
|
for (const Output &o: out) {
|
|
QAction *act=menu->addAction(o.name, this, SLOT(toggleOutput()));
|
|
act->setData(o.id);
|
|
act->setCheckable(true);
|
|
act->setChecked(o.enabled);
|
|
act->setShortcut(Qt::ControlModifier+Qt::AltModifier+nextKey(i));
|
|
}
|
|
} else {
|
|
for (const Output &o: outputs) {
|
|
for (QAction *act: menu->actions()) {
|
|
if (Utils::strippedText(act->text())==o.name) {
|
|
act->setChecked(o.enabled);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (newConn==lastConn && enabledMpd!=lastEnabledMpd && !menuItems.isEmpty()) {
|
|
QSet<QString> switchedOn=enabledMpd-lastEnabledMpd;
|
|
QSet<QString> switchedOff=lastEnabledMpd-enabledMpd;
|
|
|
|
if (!switchedOn.isEmpty() && switchedOff.isEmpty()) {
|
|
QStringList names=switchedOn.toList();
|
|
std::sort(names.begin(), names.end());
|
|
trayItem->showMessage(tr("Outputs"), tr("Enabled: %1").arg(names.join(QLatin1String(", "))));
|
|
} else if (!switchedOff.isEmpty() && switchedOn.isEmpty()) {
|
|
QStringList names=switchedOff.toList();
|
|
std::sort(names.begin(), names.end());
|
|
trayItem->showMessage(tr("Outputs"), tr("Disabled: %1").arg(names.join(QLatin1String(", "))));
|
|
} else if (!switchedOn.isEmpty() && !switchedOff.isEmpty()) {
|
|
QStringList on=switchedOn.toList();
|
|
std::sort(on.begin(), on.end());
|
|
QStringList off=switchedOff.toList();
|
|
std::sort(off.begin(), off.end());
|
|
trayItem->showMessage(tr("Outputs"),
|
|
tr("Enabled: %1").arg(on.join(QLatin1String(", ")))+QLatin1Char('\n')+
|
|
tr("Disabled: %1").arg(off.join(QLatin1String(", "))));
|
|
}
|
|
}
|
|
setProperty(constMpdEnabledOuptuts, QStringList() << enabledMpd.toList());
|
|
outputsAction->setVisible(outputs.count()>1);
|
|
trayItem->updateOutputs();
|
|
}
|
|
|
|
void MainWindow::updateConnectionsMenu()
|
|
{
|
|
QList<MPDConnectionDetails> connections=Settings::self()->allConnections();
|
|
if (connections.count()<2) {
|
|
connectionsAction->setVisible(false);
|
|
} else {
|
|
connectionsAction->setVisible(true);
|
|
QString currentConn=Settings::self()->currentConnection();
|
|
QSet<QString> cfg;
|
|
QSet<QString> menuItems;
|
|
QMenu *menu=connectionsAction->menu();
|
|
for (const MPDConnectionDetails &d: connections) {
|
|
cfg.insert(d.name);
|
|
}
|
|
|
|
for (QAction *act: menu->actions()) {
|
|
menuItems.insert(act->data().toString());
|
|
act->setChecked(act->data().toString()==currentConn);
|
|
}
|
|
|
|
if (menuItems!=cfg) {
|
|
menu->clear();
|
|
std::sort(connections.begin(), connections.end());
|
|
int i=Qt::Key_1;
|
|
for (const MPDConnectionDetails &d: connections) {
|
|
QAction *act=menu->addAction(d.getName(), this, SLOT(changeConnection()));
|
|
act->setData(d.name);
|
|
act->setCheckable(true);
|
|
act->setChecked(d.name==currentConn);
|
|
act->setActionGroup(connectionsGroup);
|
|
act->setShortcut(Qt::ControlModifier+nextKey(i));
|
|
}
|
|
}
|
|
}
|
|
trayItem->updateConnections();
|
|
}
|
|
|
|
void MainWindow::controlConnectionsMenu(bool enable)
|
|
{
|
|
if (enable) {
|
|
updateConnectionsMenu();
|
|
}
|
|
for (QAction *act: connectionsAction->menu()->actions()) {
|
|
act->setEnabled(enable);
|
|
}
|
|
}
|
|
|
|
void MainWindow::controlDynamicButton()
|
|
{
|
|
stopDynamicButton->setVisible(DynamicPlaylists::self()->isRunning());
|
|
PlayQueueModel::self()->enableUndo(!DynamicPlaylists::self()->isRunning());
|
|
}
|
|
|
|
void MainWindow::setRating()
|
|
{
|
|
Action *act=qobject_cast<Action *>(sender());
|
|
if (act) {
|
|
PlayQueueModel::self()->setRating(playQueueProxyModel.mapToSourceRows(playQueue->selectedIndexes()), act->property(constRatingKey).toUInt());
|
|
}
|
|
}
|
|
|
|
void MainWindow::initMpris()
|
|
{
|
|
#ifdef QT_QTDBUS_FOUND
|
|
if (Settings::self()->mpris()) {
|
|
if (!mpris) {
|
|
mpris=new Mpris(this);
|
|
connect(mpris, SIGNAL(showMainWindow()), this, SLOT(restoreWindow()));
|
|
}
|
|
} else if (mpris) {
|
|
disconnect(mpris, SIGNAL(showMainWindow()), this, SLOT(restoreWindow()));
|
|
mpris->deleteLater();
|
|
mpris=nullptr;
|
|
}
|
|
CurrentCover::self()->setEnabled(mpris || Settings::self()->showPopups() || 0!=Settings::self()->playQueueBackground() || Settings::self()->showCoverWidget());
|
|
#endif
|
|
}
|
|
|
|
void MainWindow::toggleMenubar()
|
|
{
|
|
#if !defined Q_OS_WIN && !defined Q_OS_MAC
|
|
if (showMenubarAction) {
|
|
menuButton->setVisible(!showMenubarAction->isChecked());
|
|
menuBar()->setVisible(showMenubarAction->isChecked());
|
|
setCollapsedSize();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void MainWindow::paletteChanged()
|
|
{
|
|
QColor before = nowPlaying->textColor();
|
|
nowPlaying->initColors();
|
|
if (before == nowPlaying->textColor()) {
|
|
return;
|
|
}
|
|
|
|
volumeSlider->setColor(nowPlaying->textColor());
|
|
|
|
Icons::self()->initToolbarIcons(nowPlaying->textColor());
|
|
StdActions::self()->prevTrackAction->setIcon(Icons::self()->toolbarPrevIcon);
|
|
StdActions::self()->nextTrackAction->setIcon(Icons::self()->toolbarNextIcon);
|
|
StdActions::self()->playPauseTrackAction->setIcon(MPDState_Playing==MPDStatus::self()->state() ? Icons::self()->toolbarPauseIcon : Icons::self()->toolbarPlayIcon);
|
|
StdActions::self()->stopPlaybackAction->setIcon(Icons::self()->toolbarStopIcon);
|
|
StdActions::self()->stopAfterCurrentTrackAction->setIcon(Icons::self()->toolbarStopIcon);
|
|
StdActions::self()->stopAfterTrackAction->setIcon(Icons::self()->toolbarStopIcon);
|
|
songInfoAction->setIcon(Icons::self()->infoIcon);
|
|
menuButton->setIcon(Icons::self()->toolbarMenuIcon);
|
|
}
|
|
|
|
void MainWindow::readSettings()
|
|
{
|
|
#ifdef QT_QTDBUS_FOUND
|
|
// It appears as if the KDE MPRIS code does not like the MPRIS interface to be setup before the window is visible.
|
|
// to work-around this, initMpris in the next event loop iteration.
|
|
// See #863
|
|
QTimer::singleShot(0, this, SLOT(initMpris()));
|
|
#else
|
|
CurrentCover::self()->setEnabled(Settings::self()->showPopups() || 0!=Settings::self()->playQueueBackground() || Settings::self()->showCoverWidget());
|
|
#endif
|
|
|
|
checkMpdDir();
|
|
Song::setIgnorePrefixes(Settings::self()->ignorePrefixes());
|
|
Covers::self()->readConfig();
|
|
HttpServer::self()->readConfig();
|
|
#ifdef ENABLE_DEVICES_SUPPORT
|
|
StdActions::self()->deleteSongsAction->setVisible(Settings::self()->showDeleteAction());
|
|
#endif
|
|
Song::setComposerGenres(Settings::self()->composerGenres());
|
|
trayItem->setup();
|
|
autoScrollPlayQueue=Settings::self()->playQueueScroll();
|
|
TreeView::setForceSingleClick(Settings::self()->forceSingleClick());
|
|
#if (defined Q_OS_LINUX && defined QT_QTDBUS_FOUND) || (defined Q_OS_MAC && defined IOKIT_FOUND)
|
|
PowerManagement::self()->setInhibitSuspend(Settings::self()->inhibitSuspend());
|
|
#endif
|
|
context->readConfig();
|
|
tabWidget->setProperty(constUserSettingProp, Settings::self()->hiddenPages());
|
|
tabWidget->setProperty(constUserSetting2Prop, Settings::self()->sidebar());
|
|
coverWidget->setProperty(constUserSettingProp, Settings::self()->showCoverWidget());
|
|
stopTrackButton->setProperty(constUserSettingProp, Settings::self()->showStopButton());
|
|
responsiveSidebar=Settings::self()->responsiveSidebar();
|
|
controlView(true);
|
|
#if defined Q_OS_WIN
|
|
if (thumbnailTooolbar) {
|
|
thumbnailTooolbar->readSettings();
|
|
}
|
|
#endif
|
|
searchPlayQueueAction->setEnabled(Settings::self()->playQueueSearch());
|
|
searchPlayQueueAction->setVisible(Settings::self()->playQueueSearch());
|
|
nowPlaying->readConfig();
|
|
setCollapsedSize();
|
|
toggleSplitterAutoHide();
|
|
if (contextSwitchTime!=Settings::self()->contextSwitchTime()) {
|
|
contextSwitchTime=Settings::self()->contextSwitchTime();
|
|
if (0==contextSwitchTime && contextTimer) {
|
|
contextTimer->stop();
|
|
contextTimer->deleteLater();
|
|
contextTimer=nullptr;
|
|
}
|
|
}
|
|
MPDConnection::self()->setVolumeFadeDuration(Settings::self()->stopFadeDuration());
|
|
MPDParseUtils::setSingleTracksFolders(Settings::self()->singleTracksFolders());
|
|
MPDParseUtils::setCueFileSupport(Settings::self()->cueSupport());
|
|
volumeSlider->setPageStep(Settings::self()->volumeStep());
|
|
}
|
|
|
|
void MainWindow::updateSettings()
|
|
{
|
|
connectToMpd();
|
|
Settings::self()->save();
|
|
readSettings();
|
|
|
|
bool wasAutoExpand=playQueue->isAutoExpand();
|
|
bool wasStartClosed=playQueue->isStartClosed();
|
|
bool wasGrouped=playQueue->isGrouped();
|
|
playQueue->readConfig();
|
|
|
|
if (wasGrouped!=playQueue->isGrouped() ||
|
|
(playQueue->isGrouped() && (wasAutoExpand!=playQueue->isAutoExpand() || wasStartClosed!=playQueue->isStartClosed())) ) {
|
|
QModelIndex idx=playQueueProxyModel.mapFromSource(PlayQueueModel::self()->index(PlayQueueModel::self()->currentSongRow(), 0));
|
|
playQueue->updateRows(idx.row(), current.key, autoScrollPlayQueue && playQueueProxyModel.isEmpty() && MPDState_Playing==MPDStatus::self()->state());
|
|
}
|
|
|
|
updateActionToolTips();
|
|
}
|
|
|
|
void MainWindow::toggleOutput()
|
|
{
|
|
QAction *act=qobject_cast<QAction *>(sender());
|
|
if (act) {
|
|
emit enableOutput(act->data().toUInt(), act->isChecked());
|
|
}
|
|
}
|
|
|
|
void MainWindow::selectPartition()
|
|
{
|
|
QAction *act=qobject_cast<QAction *>(sender());
|
|
if (act) {
|
|
emit changePartition(act->data().toString());
|
|
}
|
|
}
|
|
|
|
void MainWindow::createNewPartition()
|
|
{
|
|
QString name = InputDialog::getText(tr("Partition Name"), tr("Enter a name for the partition:"));
|
|
if (!name.isEmpty()) {
|
|
emit newPartition(name);
|
|
}
|
|
}
|
|
|
|
void MainWindow::deleteAPartition()
|
|
{
|
|
QAction *act=qobject_cast<QAction *>(sender());
|
|
if (act) {
|
|
QString name = act->data().toString();
|
|
if (MessageBox::Yes==MessageBox::warningYesNo(
|
|
this,
|
|
tr("Are you sure you wish to remove partition \"%1\"?\n\nThis cannot be undone.").arg(name),
|
|
tr("Remove Partition"),
|
|
StdGuiItem::remove(),
|
|
StdGuiItem::cancel()
|
|
)) {
|
|
emit delPartition(name);
|
|
}
|
|
}
|
|
}
|
|
|
|
void MainWindow::changeConnection()
|
|
{
|
|
QAction *act=qobject_cast<QAction *>(sender());
|
|
if (act) {
|
|
Settings::self()->saveCurrentConnection(act->data().toString());
|
|
connectToMpd();
|
|
}
|
|
}
|
|
|
|
void MainWindow::showServerInfo()
|
|
{
|
|
QStringList handlers=MPDConnection::self()->urlHandlers().toList();
|
|
QStringList tags=MPDConnection::self()->tags().toList();
|
|
std::sort(handlers.begin(), handlers.end());
|
|
std::sort(tags.begin(), tags.end());
|
|
long version=MPDConnection::self()->version();
|
|
QDateTime dbUpdate;
|
|
dbUpdate.setTime_t(MPDStats::self()->dbUpdate());
|
|
MessageBox::information(this,
|
|
#ifdef Q_OS_MAC
|
|
tr("Server Information")+QLatin1String("<br/><br/>")+
|
|
#endif
|
|
QLatin1String("<p><table>")+
|
|
tr("<tr><td colspan=\"2\"><b>Server</b></td></tr>"
|
|
"<tr><td align=\"right\">Protocol: </td><td>%1.%2.%3</td></tr>"
|
|
"<tr><td align=\"right\">Uptime: </td><td>%4</td></tr>"
|
|
"<tr><td align=\"right\">Playing: </td><td>%5</td></tr>"
|
|
"<tr><td align=\"right\">Handlers: </td><td>%6</td></tr>"
|
|
"<tr><td align=\"right\">Tags: </td><td>%7</td></tr>")
|
|
.arg((version>>16)&0xFF).arg((version>>8)&0xFF).arg(version&0xFF)
|
|
.arg(Utils::formatDuration(MPDStats::self()->uptime()))
|
|
.arg(Utils::formatDuration(MPDStats::self()->playtime()))
|
|
.arg(handlers.join(", ")).arg(tags.join(", "))+
|
|
QLatin1String("<tr/>")+
|
|
tr("<tr><td colspan=\"2\"><b>Database</b></td></tr>"
|
|
"<tr><td align=\"right\">Artists: </td><td>%1</td></tr>"
|
|
"<tr><td align=\"right\">Albums: </td><td>%2</td></tr>"
|
|
"<tr><td align=\"right\">Songs: </td><td>%3</td></tr>"
|
|
"<tr><td align=\"right\">Duration: </td><td>%4</td></tr>"
|
|
"<tr><td align=\"right\">Updated: </td><td>%5</td></tr>")
|
|
.arg(MPDStats::self()->artists()).arg(MPDStats::self()->albums()).arg(MPDStats::self()->songs())
|
|
.arg(Utils::formatDuration(MPDStats::self()->dbPlaytime())).arg(dbUpdate.toString(Qt::SystemLocaleShortDate))+
|
|
QLatin1String("</table></p>"),
|
|
tr("Server Information"));
|
|
}
|
|
|
|
void MainWindow::enableStopActions(bool enable)
|
|
{
|
|
StdActions::self()->stopAfterCurrentTrackAction->setEnabled(enable);
|
|
StdActions::self()->stopPlaybackAction->setEnabled(enable);
|
|
}
|
|
|
|
void MainWindow::stopPlayback()
|
|
{
|
|
emit stop();
|
|
enableStopActions(false);
|
|
StdActions::self()->nextTrackAction->setEnabled(false);
|
|
StdActions::self()->prevTrackAction->setEnabled(false);
|
|
}
|
|
|
|
void MainWindow::stopAfterCurrentTrack()
|
|
{
|
|
PlayQueueModel::self()->clearStopAfterTrack();
|
|
emit stop(true);
|
|
}
|
|
|
|
void MainWindow::stopAfterTrack()
|
|
{
|
|
QModelIndexList selected=playQueue->selectedIndexes(false); // Dont need sorted selection here...
|
|
if (1==selected.count()) {
|
|
QModelIndex idx=playQueueProxyModel.mapToSource(selected.first());
|
|
PlayQueueModel::self()->setStopAfterTrack(PlayQueueModel::self()->getIdByRow(idx.row()));
|
|
}
|
|
}
|
|
|
|
void MainWindow::playPauseTrack()
|
|
{
|
|
switch (MPDStatus::self()->state()) {
|
|
case MPDState_Playing:
|
|
emit pause(true);
|
|
break;
|
|
case MPDState_Paused:
|
|
emit pause(false);
|
|
break;
|
|
default:
|
|
if (PlayQueueModel::self()->rowCount()>0) {
|
|
if (-1!=PlayQueueModel::self()->currentSong() && -1!=PlayQueueModel::self()->currentSongRow()) {
|
|
emit startPlayingSongId(PlayQueueModel::self()->currentSong());
|
|
} else {
|
|
emit play();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void MainWindow::setPosition()
|
|
{
|
|
emit setSeekId(MPDStatus::self()->songId(), nowPlaying->value());
|
|
}
|
|
|
|
void MainWindow::searchPlayQueue()
|
|
{
|
|
if (playQueueSearchWidget->text().isEmpty()) {
|
|
if (playQueueSearchTimer) {
|
|
playQueueSearchTimer->stop();
|
|
}
|
|
realSearchPlayQueue();
|
|
} else {
|
|
if (!playQueueSearchTimer) {
|
|
playQueueSearchTimer=new QTimer(this);
|
|
playQueueSearchTimer->setSingleShot(true);
|
|
connect(playQueueSearchTimer, SIGNAL(timeout()), SLOT(realSearchPlayQueue()));
|
|
}
|
|
playQueueSearchTimer->start(250);
|
|
}
|
|
}
|
|
|
|
void MainWindow::realSearchPlayQueue()
|
|
{
|
|
if (playQueueSearchTimer) {
|
|
playQueueSearchTimer->stop();
|
|
}
|
|
QString filter=playQueueSearchWidget->text().trimmed();
|
|
if (filter.length()<2) {
|
|
filter=QString();
|
|
}
|
|
|
|
if (filter!=playQueueProxyModel.filterText()) {
|
|
playQueue->setFilterActive(!filter.isEmpty());
|
|
playQueue->clearSelection();
|
|
playQueueProxyModel.update(filter);
|
|
QModelIndex idx=playQueueProxyModel.mapFromSource(PlayQueueModel::self()->index(PlayQueueModel::self()->currentSongRow(), 0));
|
|
playQueue->updateRows(idx.row(), current.key, autoScrollPlayQueue && playQueueProxyModel.isEmpty() && MPDState_Playing==MPDStatus::self()->state());
|
|
scrollPlayQueue();
|
|
}
|
|
}
|
|
|
|
void MainWindow::playQueueSearchActivated(bool a)
|
|
{
|
|
if (!a && playQueue->isVisible()) {
|
|
playQueue->setFocus();
|
|
}
|
|
}
|
|
|
|
void MainWindow::updatePlayQueue(const QList<Song> &songs, bool isComplete)
|
|
{
|
|
StdActions::self()->playPauseTrackAction->setEnabled(!songs.isEmpty());
|
|
StdActions::self()->nextTrackAction->setEnabled(StdActions::self()->stopPlaybackAction->isEnabled() && !songs.isEmpty() && MPDStatus::self()->nextSongId()!=-1);
|
|
StdActions::self()->prevTrackAction->setEnabled(StdActions::self()->stopPlaybackAction->isEnabled() && !songs.isEmpty() && (songs.count()>1 || current.time>5));
|
|
StdActions::self()->savePlayQueueAction->setEnabled(!songs.isEmpty());
|
|
clearPlayQueueAction->setEnabled(!songs.isEmpty());
|
|
|
|
int topRow=-1;
|
|
QModelIndex topIndex=PlayQueueModel::self()->lastCommandWasUnodOrRedo() ? playQueue->indexAt(QPoint(0, 0)) : QModelIndex();
|
|
if (topIndex.isValid()) {
|
|
topRow=playQueueProxyModel.mapToSource(topIndex).row();
|
|
}
|
|
bool wasEmpty=0==PlayQueueModel::self()->rowCount();
|
|
bool songChanged=false;
|
|
|
|
PlayQueueModel::self()->update(songs, isComplete);
|
|
|
|
if (songs.isEmpty()) {
|
|
updateCurrentSong(Song(), wasEmpty);
|
|
} else {
|
|
// Check to see if it has been updated...
|
|
Song pqSong=PlayQueueModel::self()->getSongByRow(PlayQueueModel::self()->currentSongRow());
|
|
if (wasEmpty || pqSong.isDifferent(current) ) {
|
|
updateCurrentSong(pqSong, wasEmpty);
|
|
songChanged=true;
|
|
}
|
|
}
|
|
|
|
if (songChanged) {
|
|
StdActions::self()->prevTrackAction->setEnabled(StdActions::self()->stopPlaybackAction->isEnabled() && !songs.isEmpty() && (songs.count()>1 || current.time>5));
|
|
}
|
|
|
|
QModelIndex idx=playQueueProxyModel.mapFromSource(PlayQueueModel::self()->index(PlayQueueModel::self()->currentSongRow(), 0));
|
|
bool scroll=songChanged && autoScrollPlayQueue && playQueueProxyModel.isEmpty() && (wasEmpty || MPDState_Playing==MPDStatus::self()->state());
|
|
playQueue->updateRows(idx.row(), current.key, scroll, wasEmpty);
|
|
if (!scroll && topRow>0 && topRow<PlayQueueModel::self()->rowCount()) {
|
|
playQueue->scrollTo(playQueueProxyModel.mapFromSource(PlayQueueModel::self()->index(topRow, PlayQueueModel::COL_TITLE)), QAbstractItemView::PositionAtTop);
|
|
}
|
|
|
|
playQueueItemsSelected(playQueue->haveSelectedItems());
|
|
}
|
|
|
|
void MainWindow::updateWindowTitle()
|
|
{
|
|
bool multipleConnections=connectionsAction->isVisible();
|
|
QString connection=MPDConnection::self()->getDetails().getName();
|
|
setWindowTitle(multipleConnections ? tr("Cantata (%1)").arg(connection) : "Cantata");
|
|
}
|
|
|
|
void MainWindow::updateCurrentSong(Song song, bool wasEmpty)
|
|
{
|
|
if (song.isCdda()) {
|
|
emit getStatus();
|
|
if (song.isUnknownAlbum()) {
|
|
Song pqSong=PlayQueueModel::self()->getSongById(song.id);
|
|
if (!pqSong.isEmpty()) {
|
|
song=pqSong;
|
|
}
|
|
}
|
|
}
|
|
|
|
// if (song.isCantataStream()) {
|
|
// Song mod=HttpServer::self()->decodeUrl(song.file);
|
|
// if (!mod.title.isEmpty()) {
|
|
// mod.id=song.id;
|
|
// song=mod;
|
|
// }
|
|
// }
|
|
|
|
bool diffSong=song.isDifferent(current);
|
|
current=song;
|
|
|
|
CurrentCover::self()->update(current);
|
|
if (current.time<5 && MPDStatus::self()->songId()==current.id && MPDStatus::self()->timeTotal()>5) {
|
|
current.time=MPDStatus::self()->timeTotal();
|
|
}
|
|
nowPlaying->setEnabled(-1!=current.id && !current.isCdda() && (!currentIsStream() || current.time>5));
|
|
nowPlaying->update(current);
|
|
bool isPlaying=MPDState_Playing==MPDStatus::self()->state();
|
|
PlayQueueModel::self()->updateCurrentSong(current.id);
|
|
QModelIndex idx=playQueueProxyModel.mapFromSource(PlayQueueModel::self()->index(PlayQueueModel::self()->currentSongRow(), 0));
|
|
playQueue->updateRows(idx.row(), current.key, autoScrollPlayQueue && playQueueProxyModel.isEmpty() && isPlaying, wasEmpty);
|
|
scrollPlayQueue(wasEmpty);
|
|
if (diffSong) {
|
|
#ifdef QT_QTDBUS_FOUND
|
|
if (mpris) {
|
|
mpris->updateCurrentSong(current);
|
|
}
|
|
#endif
|
|
#ifdef Q_OS_WIN
|
|
if (thumbnailTooolbar) {
|
|
thumbnailTooolbar->updateCurrentSong(current);
|
|
}
|
|
#endif
|
|
#ifdef MAC_MEDIAPLAYER_FOUND
|
|
macNowPlaying->updateCurrentSong(current);
|
|
#endif
|
|
context->update(current);
|
|
trayItem->songChanged(song, isPlaying);
|
|
}
|
|
centerPlayQueueAction->setEnabled(!song.isEmpty());
|
|
}
|
|
|
|
void MainWindow::scrollPlayQueue(bool wasEmpty)
|
|
{
|
|
if (autoScrollPlayQueue && (wasEmpty || MPDState_Playing==MPDStatus::self()->state()) && !playQueue->isGrouped()) {
|
|
qint32 row=PlayQueueModel::self()->currentSongRow();
|
|
if (row>=0) {
|
|
playQueue->scrollTo(playQueueProxyModel.mapFromSource(PlayQueueModel::self()->index(row, 0)), QAbstractItemView::PositionAtCenter);
|
|
}
|
|
}
|
|
}
|
|
|
|
void MainWindow::updateStatus()
|
|
{
|
|
updateStatus(MPDStatus::self());
|
|
}
|
|
|
|
void MainWindow::updateStatus(MPDStatus * const status)
|
|
{
|
|
if (!status->error().isEmpty()) {
|
|
showError(tr("MPD reported the following error: %1").arg(status->error()));
|
|
}
|
|
|
|
if (status->partition()!=lastPartition) {
|
|
for (QAction *act: partitionsAction->menu()->actions()) {
|
|
if (act->isCheckable()) {
|
|
if (act->data().toString() == status->partition()) {
|
|
act->setChecked(true);
|
|
}
|
|
} else if (act->menu() && act->data().toString()=="deletepartition") {
|
|
for (QAction* sub_act: act->menu()->actions()) {
|
|
sub_act->setVisible(sub_act->data().toString()!=status->partition());
|
|
}
|
|
}
|
|
}
|
|
|
|
// SongIDs are not unique between partitions, so reset the play queue's state.
|
|
PlayQueueModel::self()->clear();
|
|
}
|
|
|
|
if (MPDState_Stopped==status->state() || MPDState_Inactive==status->state()) {
|
|
nowPlaying->clearTimes();
|
|
PlayQueueModel::self()->clearStopAfterTrack();
|
|
if (statusTimer) {
|
|
statusTimer->stop();
|
|
statusTimer->setProperty("count", 0);
|
|
}
|
|
} else {
|
|
nowPlaying->setRange(0, 0==status->timeTotal() && 0!=current.time && (current.isCdda() || current.isCantataStream())
|
|
? current.time : status->timeTotal());
|
|
nowPlaying->setValue(status->timeElapsed());
|
|
if (0==status->timeTotal() && 0==status->timeElapsed()) {
|
|
if (!statusTimer) {
|
|
statusTimer=new QTimer(this);
|
|
statusTimer->setSingleShot(true);
|
|
connect(statusTimer, SIGNAL(timeout()), SIGNAL(getStatus()));
|
|
}
|
|
QVariant id=statusTimer->property("id");
|
|
if (!id.isValid() || id.toInt()!=current.id) {
|
|
statusTimer->setProperty("id", current.id);
|
|
statusTimer->setProperty("count", 0);
|
|
statusTimer->start(250);
|
|
} else if (statusTimer->property("count").toInt()<12) {
|
|
statusTimer->setProperty("count", statusTimer->property("count").toInt()+1);
|
|
statusTimer->start(250);
|
|
}
|
|
} else if (!nowPlaying->isEnabled()) {
|
|
nowPlaying->setEnabled(-1!=current.id && !current.isCdda() && (!currentIsStream() || status->timeTotal()>5));
|
|
}
|
|
}
|
|
|
|
randomPlayQueueAction->setChecked(status->random());
|
|
repeatPlayQueueAction->setChecked(status->repeat());
|
|
singlePlayQueueAction->setChecked(status->single());
|
|
consumePlayQueueAction->setChecked(status->consume());
|
|
updateNextTrack(status->nextSongId());
|
|
|
|
if (status->timeElapsed()<64800 && (!currentIsStream() || (status->timeTotal()>0 && status->timeElapsed()<=status->timeTotal()))) {
|
|
if (status->state() == MPDState_Stopped || status->state() == MPDState_Inactive) {
|
|
nowPlaying->setRange(0, 0);
|
|
} else {
|
|
nowPlaying->setValue(status->timeElapsed());
|
|
}
|
|
}
|
|
|
|
PlayQueueModel::self()->setState(status->state());
|
|
StdActions::self()->playPauseTrackAction->setEnabled(status->playlistLength()>0);
|
|
switch (status->state()) {
|
|
case MPDState_Playing:
|
|
StdActions::self()->playPauseTrackAction->setIcon(Icons::self()->toolbarPauseIcon);
|
|
enableStopActions(true);
|
|
StdActions::self()->nextTrackAction->setEnabled(status->nextSongId()!=-1);
|
|
StdActions::self()->prevTrackAction->setEnabled(status->playlistLength()>1 || status->timeTotal()>5 || current.time>5);
|
|
nowPlaying->startTimer();
|
|
break;
|
|
case MPDState_Inactive:
|
|
case MPDState_Stopped:
|
|
StdActions::self()->playPauseTrackAction->setIcon(Icons::self()->toolbarPlayIcon);
|
|
enableStopActions(false);
|
|
StdActions::self()->nextTrackAction->setEnabled(false);
|
|
StdActions::self()->prevTrackAction->setEnabled(false);
|
|
if (!StdActions::self()->playPauseTrackAction->isEnabled()) {
|
|
current=Song();
|
|
nowPlaying->update(current);
|
|
CurrentCover::self()->update(current);
|
|
context->update(current);
|
|
}
|
|
current.id=0;
|
|
trayItem->setToolTip("cantata", tr("Cantata"), QLatin1String("<i>")+tr("Playback stopped")+QLatin1String("</i>"));
|
|
nowPlaying->stopTimer();
|
|
break;
|
|
case MPDState_Paused:
|
|
StdActions::self()->playPauseTrackAction->setIcon(Icons::self()->toolbarPlayIcon);
|
|
enableStopActions(0!=status->playlistLength());
|
|
StdActions::self()->nextTrackAction->setEnabled(status->nextSongId()!=-1);
|
|
StdActions::self()->prevTrackAction->setEnabled(status->playlistLength()>1 || status->timeTotal()>5 || current.time>5);
|
|
nowPlaying->stopTimer();
|
|
default:
|
|
break;
|
|
}
|
|
|
|
// Check if song has changed or we're playing again after being stopped, and update song info if needed
|
|
if (MPDState_Inactive!=status->state() &&
|
|
(MPDState_Inactive==lastState || (MPDState_Stopped==lastState && MPDState_Playing==status->state()) || lastSongId != status->songId())) {
|
|
emit currentSong();
|
|
}
|
|
if (status->state()!=lastState && (MPDState_Playing==status->state() || MPDState_Stopped==status->state())) {
|
|
startContextTimer();
|
|
}
|
|
|
|
// Update status info
|
|
lastState = status->state();
|
|
lastSongId = status->songId();
|
|
lastPartition = status->partition();
|
|
#ifdef QT_QTDBUS_FOUND
|
|
if (mpris) {
|
|
mpris->updateStatus(status);
|
|
}
|
|
#endif
|
|
#if defined Q_OS_WIN
|
|
if (thumbnailTooolbar) {
|
|
thumbnailTooolbar->updateStatus(status);
|
|
}
|
|
#endif
|
|
#ifdef Q_OS_MAC
|
|
dockMenu->update(status);
|
|
#endif
|
|
#ifdef MAC_MEDIAPLAYER_FOUND
|
|
macNowPlaying->updateStatus(status);
|
|
#endif
|
|
}
|
|
|
|
void MainWindow::playQueueItemActivated(const QModelIndex &index)
|
|
{
|
|
if (index.isValid()) {
|
|
emit startPlayingSongId(PlayQueueModel::self()->getIdByRow(playQueueProxyModel.mapToSource(index).row()));
|
|
}
|
|
}
|
|
|
|
void MainWindow::clearPlayQueue()
|
|
{
|
|
if (!Settings::self()->playQueueConfirmClear() ||
|
|
MessageBox::Yes==MessageBox::questionYesNo(this, tr("Remove all songs from play queue?"))) {
|
|
if (dynamicLabel->isVisible()) {
|
|
DynamicPlaylists::self()->stop(true);
|
|
} else {
|
|
PlayQueueModel::self()->removeAll();
|
|
}
|
|
}
|
|
}
|
|
|
|
void MainWindow::centerPlayQueue()
|
|
{
|
|
QModelIndex idx=playQueueProxyModel.mapFromSource(PlayQueueModel::self()->index(PlayQueueModel::self()->currentSongRow(),
|
|
playQueue->isGrouped() ? 0 : PlayQueueModel::COL_TITLE));
|
|
if (idx.isValid()) {
|
|
if (playQueue->isGrouped()) {
|
|
playQueue->updateRows(idx.row(), current.key, true, true);
|
|
} else {
|
|
playQueue->scrollTo(idx, QAbstractItemView::PositionAtCenter);
|
|
}
|
|
}
|
|
}
|
|
|
|
void MainWindow::appendToPlayQueue(int action, quint8 priority, bool decreasePriority)
|
|
{
|
|
playQueueSearchWidget->clear();
|
|
if (currentPage) {
|
|
currentPage->addSelectionToPlaylist(QString(), action, priority, decreasePriority);
|
|
}
|
|
}
|
|
|
|
void MainWindow::addWithPriority()
|
|
{
|
|
QAction *act=qobject_cast<QAction *>(sender());
|
|
|
|
if (!act || !MPDConnection::self()->canUsePriority()) {
|
|
return;
|
|
}
|
|
|
|
int prio=act->data().toInt();
|
|
bool isPlayQueue=playQueue->hasFocus();
|
|
bool decreasePriority=false;
|
|
QModelIndexList pqItems;
|
|
|
|
if (isPlayQueue) {
|
|
pqItems=playQueue->selectedIndexes();
|
|
if (pqItems.isEmpty()) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (-1==prio) {
|
|
InputDialog dlg(tr("Priority"), tr("Enter priority (0..255):"), 150, 0, 255, 5, this);
|
|
QCheckBox *dec = new QCheckBox(tr("Decrease priority for each subsequent track"), this);
|
|
dec->setChecked(false);
|
|
dlg.addExtraWidget(dec);
|
|
if (QDialog::Accepted!=dlg.exec()) {
|
|
return;
|
|
}
|
|
prio=dlg.spinBox()->value();
|
|
decreasePriority=dec->isChecked();
|
|
}
|
|
|
|
if (prio>=0 && prio<=255) {
|
|
if (isPlayQueue) {
|
|
QList<qint32> ids;
|
|
for (const QModelIndex &idx: pqItems) {
|
|
ids.append(PlayQueueModel::self()->getIdByRow(playQueueProxyModel.mapToSource(idx).row()));
|
|
}
|
|
emit setPriority(ids, prio, decreasePriority);
|
|
} else {
|
|
appendToPlayQueue(false, prio, decreasePriority);
|
|
}
|
|
}
|
|
}
|
|
|
|
void MainWindow::addToNewStoredPlaylist()
|
|
{
|
|
bool pq=playQueue->hasFocus();
|
|
for(;;) {
|
|
QString name = InputDialog::getText(tr("Playlist Name"), tr("Enter a name for the playlist:"), QString(), nullptr, this);
|
|
|
|
if (name==MPDConnection::constStreamsPlayListName) {
|
|
MessageBox::error(this, tr("'%1' is used to store favorite streams, please choose another name.").arg(name));
|
|
continue;
|
|
}
|
|
if (PlaylistsModel::self()->exists(name)) {
|
|
switch(MessageBox::warningYesNoCancel(this, tr("A playlist named '%1' already exists!\n\nAdd to that playlist?").arg(name),
|
|
tr("Existing Playlist"))) {
|
|
case MessageBox::Cancel: return;
|
|
case MessageBox::Yes: break;
|
|
case MessageBox::No:
|
|
default: continue;
|
|
}
|
|
}
|
|
|
|
if (!name.isEmpty()) {
|
|
addToExistingStoredPlaylist(name, pq);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
void MainWindow::addToExistingStoredPlaylist(const QString &name, bool pq)
|
|
{
|
|
if (pq) {
|
|
QModelIndexList items = playQueue->selectedIndexes();
|
|
QStringList files;
|
|
if (items.isEmpty()) {
|
|
files = PlayQueueModel::self()->filenames();
|
|
} else {
|
|
for (const QModelIndex &idx: items) {
|
|
Song s = PlayQueueModel::self()->getSongByRow(playQueueProxyModel.mapToSource(idx).row());
|
|
if (!s.file.isEmpty()) {
|
|
files.append(s.file);
|
|
}
|
|
}
|
|
}
|
|
if (!files.isEmpty()) {
|
|
emit addSongsToPlaylist(name, files);
|
|
}
|
|
} else if (currentPage) {
|
|
currentPage->addSelectionToPlaylist(name);
|
|
}
|
|
}
|
|
|
|
void MainWindow::addStreamToPlayQueue()
|
|
{
|
|
StreamDialog dlg(this, true);
|
|
|
|
if (QDialog::Accepted==dlg.exec()) {
|
|
QString url=dlg.url();
|
|
|
|
if (dlg.save()) {
|
|
StreamsModel::self()->addToFavourites(url, dlg.name());
|
|
}
|
|
PlayQueueModel::self()->addItems(QStringList() << StreamsModel::modifyUrl(url, true, dlg.name()), MPDConnection::Append, 0, false);
|
|
}
|
|
}
|
|
|
|
void MainWindow::addLocalFilesToPlayQueue()
|
|
{
|
|
QString extensions;
|
|
for (const auto &ext: PlayQueueModel::constFileExtensions) {
|
|
if (!extensions.isEmpty()) {
|
|
extensions+=" ";
|
|
}
|
|
extensions+="*"+ext;
|
|
}
|
|
QStringList files=QFileDialog::getOpenFileNames(this, tr("Select Music Files"), QString(), tr("Music Files ")+"("+extensions+")");
|
|
if (!files.isEmpty()) {
|
|
PlayQueueModel::self()->load(files);
|
|
}
|
|
}
|
|
|
|
void MainWindow::removeItems()
|
|
{
|
|
if (currentPage) {
|
|
currentPage->removeItems();
|
|
}
|
|
}
|
|
|
|
void MainWindow::checkMpdAccessibility()
|
|
{
|
|
#ifdef Q_OS_LINUX
|
|
if (!mpdAccessibilityTimer) {
|
|
mpdAccessibilityTimer=new QTimer(this);
|
|
connect(mpdAccessibilityTimer, SIGNAL(timeout()), SLOT(checkMpdDir()));
|
|
}
|
|
mpdAccessibilityTimer->start(500);
|
|
#endif
|
|
}
|
|
|
|
void MainWindow::updatePlayQueueStats(int songs, quint32 time)
|
|
{
|
|
if (0==songs) {
|
|
playQueueStatsLabel->setText(QString());
|
|
} else if (0==time) {
|
|
playQueueStatsLabel->setText(tr("%n Track(s)", "", songs));
|
|
} else {
|
|
playQueueStatsLabel->setText(tr("%n Tracks (%1)", "", songs).arg(Utils::formatDuration(time)));
|
|
}
|
|
}
|
|
|
|
void MainWindow::showSongInfo()
|
|
{
|
|
if (songInfoAction->isCheckable()) {
|
|
stack->setCurrentWidget(songInfoAction->isChecked() ? (QWidget *)context : (QWidget *)splitter);
|
|
} else {
|
|
showTab(PAGE_CONTEXT);
|
|
}
|
|
}
|
|
|
|
void MainWindow::fullScreen()
|
|
{
|
|
if (expandInterfaceAction->isChecked()) {
|
|
#ifndef Q_OS_MAC
|
|
fullScreenLabel->setVisible(!isFullScreen());
|
|
#endif
|
|
expandInterfaceAction->setEnabled(isFullScreen());
|
|
if (isFullScreen()) {
|
|
showNormal();
|
|
} else {
|
|
showFullScreen();
|
|
}
|
|
} else {
|
|
fullScreenAction->setChecked(false);
|
|
}
|
|
}
|
|
|
|
void MainWindow::sidebarModeChanged()
|
|
{
|
|
if (expandInterfaceAction->isChecked()) {
|
|
setMinimumHeight(calcMinHeight());
|
|
}
|
|
}
|
|
|
|
void MainWindow::currentTabChanged(int index)
|
|
{
|
|
prevPage=index;
|
|
controlDynamicButton();
|
|
switch(index) {
|
|
case PAGE_LIBRARY: currentPage=libraryPage; break;
|
|
case PAGE_FOLDERS: currentPage=folderPage; break;
|
|
case PAGE_PLAYLISTS: currentPage=playlistsPage; break;
|
|
case PAGE_ONLINE: currentPage=onlinePage; break;
|
|
#ifdef ENABLE_DEVICES_SUPPORT
|
|
case PAGE_DEVICES: currentPage=devicesPage; break;
|
|
#endif
|
|
case PAGE_SEARCH: currentPage=searchPage; break;
|
|
default: currentPage=nullptr; break;
|
|
}
|
|
if (currentPage) {
|
|
currentPage->controlActions();
|
|
}
|
|
}
|
|
|
|
void MainWindow::tabToggled(int index)
|
|
{
|
|
switch (index) {
|
|
case PAGE_PLAYQUEUE:
|
|
if (tabWidget->isEnabled(index)) {
|
|
splitter->setAutohidable(0, Settings::self()->splitterAutoHide() && !tabWidget->isEnabled(PAGE_PLAYQUEUE));
|
|
playQueueWidget->setParent(playQueuePage);
|
|
playQueuePage->layout()->addWidget(playQueueWidget);
|
|
playQueueWidget->setVisible(true);
|
|
} else {
|
|
playQueuePage->layout()->removeWidget(playQueueWidget);
|
|
playQueueWidget->setParent(splitter);
|
|
playQueueWidget->setVisible(true);
|
|
splitter->setAutohidable(0, Settings::self()->splitterAutoHide() && !tabWidget->isEnabled(PAGE_PLAYQUEUE));
|
|
}
|
|
playQueue->updatePalette();
|
|
break;
|
|
case PAGE_CONTEXT:
|
|
if (tabWidget->isEnabled(index) && songInfoAction->isCheckable()) {
|
|
context->setParent(contextPage);
|
|
contextPage->layout()->addWidget(context);
|
|
context->setVisible(true);
|
|
songInfoButton->setVisible(false);
|
|
songInfoAction->setCheckable(false);
|
|
} else if (!songInfoAction->isCheckable()) {
|
|
contextPage->layout()->removeWidget(context);
|
|
stack->addWidget(context);
|
|
songInfoButton->setVisible(true);
|
|
songInfoAction->setCheckable(true);
|
|
}
|
|
break;
|
|
case PAGE_LIBRARY:
|
|
locateAction->setVisible(tabWidget->isEnabled(index));
|
|
break;
|
|
case PAGE_FOLDERS:
|
|
folderPage->setEnabled(!folderPage->isEnabled());
|
|
break;
|
|
case PAGE_PLAYLISTS:
|
|
break;
|
|
case PAGE_ONLINE:
|
|
onlinePage->setEnabled(onlinePage->isEnabled());
|
|
break;
|
|
#ifdef ENABLE_DEVICES_SUPPORT
|
|
case PAGE_DEVICES:
|
|
DevicesModel::self()->setEnabled(!DevicesModel::self()->isEnabled());
|
|
StdActions::self()->copyToDeviceAction->setVisible(DevicesModel::self()->isEnabled());
|
|
break;
|
|
#endif
|
|
default:
|
|
break;
|
|
}
|
|
sidebarModeChanged();
|
|
}
|
|
|
|
void MainWindow::toggleSplitterAutoHide()
|
|
{
|
|
bool ah=Settings::self()->splitterAutoHide();
|
|
if (splitter->isAutoHideEnabled()!=ah) {
|
|
splitter->setAutoHideEnabled(ah);
|
|
splitter->setAutohidable(0, ah);
|
|
}
|
|
}
|
|
|
|
void MainWindow::locateTracks(const QList<Song> &songs)
|
|
{
|
|
if (!songs.isEmpty() && tabWidget->isEnabled(PAGE_LIBRARY)) {
|
|
showLibraryTab();
|
|
libraryPage->showSongs(songs);
|
|
}
|
|
}
|
|
|
|
void MainWindow::locateTrack()
|
|
{
|
|
if (!locateAction->isVisible() || !locateAction->isEnabled()) {
|
|
return;
|
|
}
|
|
Action *act = qobject_cast<Action *>(sender());
|
|
if (!act) {
|
|
return;
|
|
}
|
|
|
|
QList<Song> songs = playQueue->selectedSongs();
|
|
if (1!=songs.count() || !tabWidget->isEnabled(PAGE_LIBRARY)) {
|
|
return;
|
|
}
|
|
showLibraryTab();
|
|
if (locateTrackAction==act) {
|
|
libraryPage->showSongs(songs);
|
|
}
|
|
Song s = songs.first();
|
|
if (locateAlbumAction==act) {
|
|
libraryPage->showAlbum(s.albumArtist(), s.albumId());
|
|
}
|
|
if (locateArtistAction==act) {
|
|
libraryPage->showArtist(s.albumArtist());
|
|
}
|
|
}
|
|
|
|
void MainWindow::moveSelectionAfterCurrentSong()
|
|
{
|
|
QList<int> selectedRows = playQueueProxyModel.mapToSourceRows(playQueue->selectedIndexes());
|
|
int currentSongIdx = PlayQueueModel::self()->currentSongRow();
|
|
QList<quint32> selectedSongIds;
|
|
|
|
for (int row: selectedRows) {
|
|
if (currentSongIdx!=row) {
|
|
selectedSongIds.append( (quint32) row);
|
|
}
|
|
}
|
|
|
|
if( !selectedSongIds.empty() ) {
|
|
emit playNext(selectedSongIds, currentSongIdx+1, PlayQueueModel::self()->rowCount());
|
|
}
|
|
}
|
|
|
|
void MainWindow::locateArtist(const QString &artist)
|
|
{
|
|
if (songInfoAction->isCheckable()) {
|
|
songInfoAction->setChecked(false);
|
|
showSongInfo();
|
|
}
|
|
showLibraryTab();
|
|
libraryPage->showArtist(artist);
|
|
}
|
|
|
|
void MainWindow::locateAlbum(const QString &artist, const QString &album)
|
|
{
|
|
if (songInfoAction->isCheckable()) {
|
|
songInfoAction->setChecked(false);
|
|
showSongInfo();
|
|
}
|
|
showLibraryTab();
|
|
libraryPage->showAlbum(artist, album);
|
|
}
|
|
|
|
void MainWindow::dynamicStatus(const QString &message)
|
|
{
|
|
DynamicPlaylists::self()->helperMessage(message);
|
|
}
|
|
|
|
void MainWindow::setCollection(const QString &collection)
|
|
{
|
|
if (!connectionsAction->isVisible()) {
|
|
return;
|
|
}
|
|
for (QAction *act: connectionsAction->menu()->actions()) {
|
|
if (Utils::strippedText(act->text())==collection) {
|
|
if (!act->isChecked()) {
|
|
act->trigger();
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void MainWindow::mpdConnectionName(const QString &name)
|
|
{
|
|
for (QAction *act: connectionsAction->menu()->actions()) {
|
|
if (Utils::strippedText(act->text())==name) {
|
|
if (!act->isChecked()) {
|
|
act->setChecked(true);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void MainWindow::showPlayQueueSearch()
|
|
{
|
|
playQueueSearchWidget->activate();
|
|
}
|
|
|
|
void MainWindow::showSearch()
|
|
{
|
|
if (!searchPlayQueueAction->isEnabled() && playQueue->hasFocus()) {
|
|
playQueueSearchWidget->activate();
|
|
} else if (context->isVisible()) {
|
|
context->search();
|
|
} else if (currentPage && splitter->sizes().at(0)>0) {
|
|
if (currentPage==folderPage && folderPage->mpd()->isVisible()) {
|
|
showTab(PAGE_SEARCH);
|
|
if (PAGE_SEARCH==tabWidget->currentIndex()) {
|
|
searchPage->setSearchCategory("file");
|
|
}
|
|
} else {
|
|
currentPage->focusSearch();
|
|
}
|
|
} else if (!searchPlayQueueAction->isEnabled() && (playQueuePage->isVisible() || playQueue->isVisible())) {
|
|
playQueueSearchWidget->activate();
|
|
}
|
|
}
|
|
|
|
void MainWindow::expandAll()
|
|
{
|
|
QWidget *f=QApplication::focusWidget();
|
|
if (f && qobject_cast<TreeView *>(f) && !qobject_cast<GroupedView *>(f)) {
|
|
static_cast<TreeView *>(f)->expandAll(QModelIndex(), true);
|
|
}
|
|
}
|
|
|
|
void MainWindow::collapseAll()
|
|
{
|
|
QWidget *f=QApplication::focusWidget();
|
|
if (f && qobject_cast<QTreeView *>(f) && !qobject_cast<GroupedView *>(f)) {
|
|
static_cast<QTreeView *>(f)->collapseAll();
|
|
}
|
|
}
|
|
|
|
void MainWindow::editTags()
|
|
{
|
|
#ifdef TAGLIB_FOUND
|
|
if (TagEditor::instanceCount() || !canShowDialog()) {
|
|
return;
|
|
}
|
|
QList<Song> songs;
|
|
bool isPlayQueue=playQueue->hasFocus();
|
|
if (isPlayQueue) {
|
|
songs=playQueue->selectedSongs();
|
|
} else if (currentPage) {
|
|
songs=currentPage->selectedSongs();
|
|
}
|
|
if (songs.isEmpty()) {
|
|
return;
|
|
}
|
|
QSet<QString> artists, albumArtists, composers, albums, genres;
|
|
QString udi;
|
|
#ifdef ENABLE_DEVICES_SUPPORT
|
|
if (!isPlayQueue && currentPage==devicesPage) {
|
|
DevicesModel::self()->getDetails(artists, albumArtists, composers, albums, genres);
|
|
udi=devicesPage->activeFsDeviceUdi();
|
|
if (udi.isEmpty()) {
|
|
return;
|
|
}
|
|
}
|
|
#endif
|
|
MpdLibraryModel::self()->getDetails(artists, albumArtists, composers, albums, genres);
|
|
TagEditor *dlg=new TagEditor(this, songs, artists, albumArtists, composers, albums, genres, udi);
|
|
dlg->show();
|
|
#endif
|
|
}
|
|
|
|
void MainWindow::organiseFiles()
|
|
{
|
|
#ifdef TAGLIB_FOUND
|
|
if (TrackOrganiser::instanceCount() || !canShowDialog()) {
|
|
return;
|
|
}
|
|
|
|
QList<Song> songs;
|
|
if (currentPage) {
|
|
songs=currentPage->selectedSongs();
|
|
}
|
|
|
|
if (!songs.isEmpty()) {
|
|
QString udi;
|
|
#ifdef ENABLE_DEVICES_SUPPORT
|
|
if (currentPage==devicesPage) {
|
|
udi=devicesPage->activeFsDeviceUdi();
|
|
if (udi.isEmpty()) {
|
|
return;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
TrackOrganiser *dlg=new TrackOrganiser(this);
|
|
dlg->show(songs, udi);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void MainWindow::addToDevice(const QString &udi)
|
|
{
|
|
#ifdef ENABLE_DEVICES_SUPPORT
|
|
if (playQueue->hasFocus()) {
|
|
copyToDevice(QString(), udi, playQueue->selectedSongs());
|
|
} else if (currentPage) {
|
|
currentPage->addSelectionToDevice(udi);
|
|
}
|
|
#else
|
|
Q_UNUSED(udi)
|
|
#endif
|
|
}
|
|
|
|
void MainWindow::deleteSongs()
|
|
{
|
|
#ifdef ENABLE_DEVICES_SUPPORT
|
|
if (!StdActions::self()->deleteSongsAction->isVisible()) {
|
|
return;
|
|
}
|
|
if (currentPage) {
|
|
currentPage->deleteSongs();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void MainWindow::copyToDevice(const QString &from, const QString &to, const QList<Song> &songs)
|
|
{
|
|
#ifdef ENABLE_DEVICES_SUPPORT
|
|
if (songs.isEmpty() || ActionDialog::instanceCount() || !canShowDialog()) {
|
|
return;
|
|
}
|
|
ActionDialog *dlg=new ActionDialog(this);
|
|
dlg->copy(from, to, songs);
|
|
#else
|
|
Q_UNUSED(from) Q_UNUSED(to) Q_UNUSED(songs)
|
|
#endif
|
|
}
|
|
|
|
void MainWindow::deleteSongs(const QString &from, const QList<Song> &songs)
|
|
{
|
|
#ifdef ENABLE_DEVICES_SUPPORT
|
|
if (songs.isEmpty() || ActionDialog::instanceCount() || !canShowDialog()) {
|
|
return;
|
|
}
|
|
ActionDialog *dlg=new ActionDialog(this);
|
|
dlg->remove(from, songs);
|
|
#else
|
|
Q_UNUSED(from) Q_UNUSED(songs)
|
|
#endif
|
|
}
|
|
|
|
void MainWindow::replayGain()
|
|
{
|
|
#ifdef ENABLE_REPLAYGAIN_SUPPORT
|
|
if (RgDialog::instanceCount() || !canShowDialog()) {
|
|
return;
|
|
}
|
|
|
|
QList<Song> songs;
|
|
if (currentPage) {
|
|
songs=currentPage->selectedSongs();
|
|
}
|
|
|
|
if (!songs.isEmpty()) {
|
|
QString udi;
|
|
#ifdef ENABLE_DEVICES_SUPPORT
|
|
if (currentPage==devicesPage) {
|
|
udi=devicesPage->activeFsDeviceUdi();
|
|
if (udi.isEmpty()) {
|
|
return;
|
|
}
|
|
}
|
|
#endif
|
|
RgDialog *dlg=new RgDialog(this);
|
|
dlg->show(songs, udi);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void MainWindow::setCover()
|
|
{
|
|
if (CoverDialog::instanceCount() || !canShowDialog()) {
|
|
return;
|
|
}
|
|
|
|
Song song;
|
|
if (currentPage) {
|
|
song=currentPage->coverRequest();
|
|
}
|
|
|
|
if (!song.isEmpty()) {
|
|
CoverDialog *dlg=new CoverDialog(this);
|
|
dlg->show(song);
|
|
}
|
|
}
|
|
|
|
void MainWindow::updateNextTrack(int nextTrackId)
|
|
{
|
|
if (-1!=nextTrackId && MPDState_Stopped==MPDStatus::self()->state()) {
|
|
nextTrackId=-1; // nextSongId is not accurate if we are stopped.
|
|
}
|
|
QString tt=StdActions::self()->nextTrackAction->property("tooltip").toString();
|
|
if (-1==nextTrackId && tt.isEmpty()) {
|
|
StdActions::self()->nextTrackAction->setProperty("tooltip", StdActions::self()->nextTrackAction->toolTip());
|
|
} else if (-1==nextTrackId) {
|
|
StdActions::self()->nextTrackAction->setToolTip(tt);
|
|
StdActions::self()->nextTrackAction->setProperty("trackid", nextTrackId);
|
|
} else if (nextTrackId!=StdActions::self()->nextTrackAction->property("trackid").toInt()) {
|
|
Song s=PlayQueueModel::self()->getSongByRow(PlayQueueModel::self()->getRowById(nextTrackId));
|
|
if (!s.artist.isEmpty() && !s.title.isEmpty()) {
|
|
tt+=QLatin1String("<br/><i><small>")+s.artistSong()+QLatin1String("</small></i>");
|
|
} else {
|
|
nextTrackId=-1;
|
|
}
|
|
StdActions::self()->nextTrackAction->setToolTip(tt);
|
|
StdActions::self()->nextTrackAction->setProperty("trackid", nextTrackId);
|
|
}
|
|
}
|
|
|
|
void MainWindow::updateActionToolTips()
|
|
{
|
|
ActionCollection::get()->updateToolTips();
|
|
tabWidget->setToolTip(PAGE_PLAYQUEUE, showPlayQueueAction->toolTip());
|
|
tabWidget->setToolTip(PAGE_LIBRARY, libraryTabAction->toolTip());
|
|
tabWidget->setToolTip(PAGE_FOLDERS, foldersTabAction->toolTip());
|
|
tabWidget->setToolTip(PAGE_PLAYLISTS, playlistsTabAction->toolTip());
|
|
tabWidget->setToolTip(PAGE_ONLINE, onlineTabAction->toolTip());
|
|
#ifdef ENABLE_DEVICES_SUPPORT
|
|
tabWidget->setToolTip(PAGE_DEVICES, devicesTabAction->toolTip());
|
|
#endif
|
|
tabWidget->setToolTip(PAGE_SEARCH, searchTabAction->toolTip());
|
|
tabWidget->setToolTip(PAGE_CONTEXT, songInfoAction->toolTip());
|
|
}
|
|
|
|
void MainWindow::startContextTimer()
|
|
{
|
|
if (!contextSwitchTime || current.isStandardStream()) {
|
|
return;
|
|
}
|
|
if (!contextTimer) {
|
|
contextTimer=new QTimer(this);
|
|
contextTimer->setSingleShot(true);
|
|
connect(contextTimer, SIGNAL(timeout()), this, SLOT(toggleContext()));
|
|
}
|
|
contextTimer->start(contextSwitchTime);
|
|
}
|
|
|
|
int MainWindow::calcMinHeight()
|
|
{
|
|
return tabWidget->style()&FancyTabWidget::Side && tabWidget->style()&FancyTabWidget::Large
|
|
? calcCollapsedSize()+(tabWidget->visibleCount()*tabWidget->tabSize().height())
|
|
: Utils::scaleForDpi(256);
|
|
}
|
|
|
|
int MainWindow::calcCollapsedSize()
|
|
{
|
|
#if !defined Q_OS_MAC && !defined Q_OS_WIN
|
|
return toolbar->height()+(showMenubarAction && menuBar() && menuBar()->isVisible() ? menuBar()->height() : 0);
|
|
#else
|
|
return toolbar->height();
|
|
#endif
|
|
}
|
|
|
|
void MainWindow::setCollapsedSize()
|
|
{
|
|
if (!expandInterfaceAction->isChecked()) {
|
|
int w=width();
|
|
adjustSize();
|
|
collapsedSize=QSize(w, calcCollapsedSize());
|
|
resize(collapsedSize);
|
|
setFixedHeight(collapsedSize.height());
|
|
}
|
|
}
|
|
|
|
void MainWindow::controlView(bool forceUpdate)
|
|
{
|
|
if (!stopTrackButton || !coverWidget || !tabWidget) {
|
|
controlPlayQueueButtons();
|
|
return;
|
|
}
|
|
|
|
static int lastWidth=-1;
|
|
|
|
if (forceUpdate || -1==lastWidth || qAbs(lastWidth-width())>20) {
|
|
bool stopEnabled = stopTrackButton->property(constUserSettingProp).toBool();
|
|
bool coverWidgetEnabled = coverWidget->property(constUserSettingProp).toBool();
|
|
int tabWidgetStyle = tabWidget->property(constUserSetting2Prop).toInt();
|
|
QStringList tabWidgetPages = tabWidget->property(constUserSettingProp).toStringList();
|
|
|
|
if ((!stopEnabled && !coverWidgetEnabled) || width()<Utils::scaleForDpi(450)) {
|
|
stopTrackButton->setVisible(false);
|
|
coverWidget->setEnabled(false);
|
|
} else if (stopEnabled && coverWidgetEnabled) {
|
|
if (width()>Utils::scaleForDpi(550)) {
|
|
stopTrackButton->setVisible(true);
|
|
coverWidget->setEnabled(true);
|
|
} else if (width()>Utils::scaleForDpi(450)) {
|
|
stopTrackButton->setVisible(false);
|
|
coverWidget->setEnabled(true);
|
|
}
|
|
} else {
|
|
coverWidget->setEnabled(coverWidgetEnabled);
|
|
stopTrackButton->setVisible(stopEnabled);
|
|
}
|
|
|
|
if (expandInterfaceAction->isChecked() && (responsiveSidebar || forceUpdate)) {
|
|
if (!responsiveSidebar || width()>Utils::scaleForDpi(450)) {
|
|
if (forceUpdate || singlePane) {
|
|
int index=tabWidget->currentIndex();
|
|
if (forceUpdate || tabWidget->style()!=tabWidgetStyle) {
|
|
tabWidget->setStyle(tabWidgetStyle);
|
|
}
|
|
tabWidget->setHiddenPages(tabWidgetPages);
|
|
tabWidget->setCurrentIndex(index);
|
|
}
|
|
singlePane=false;
|
|
} else if (responsiveSidebar) {
|
|
if (forceUpdate || !singlePane) {
|
|
int index=tabWidget->currentIndex();
|
|
int smallStyle=FancyTabWidget::Small|FancyTabWidget::Top|FancyTabWidget::IconOnly;
|
|
if (forceUpdate || tabWidget->style()!=smallStyle) {
|
|
tabWidget->setStyle(smallStyle);
|
|
}
|
|
tabWidgetPages.removeAll(QLatin1String("PlayQueuePage"));
|
|
tabWidgetPages.removeAll(QLatin1String("ContextPage"));
|
|
tabWidget->setHiddenPages(tabWidgetPages);
|
|
|
|
if (forceUpdate && !isVisible() && PAGE_PLAYQUEUE!=index && PAGE_CONTEXT!=index) {
|
|
QString page=Settings::self()->page();
|
|
for (int i=0; i<tabWidget->count(); ++i) {
|
|
if (tabWidget->widget(i)->metaObject()->className()==page) {
|
|
index=i;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
tabWidget->setCurrentIndex(index);
|
|
}
|
|
singlePane=true;
|
|
}
|
|
}
|
|
}
|
|
controlPlayQueueButtons();
|
|
}
|
|
|
|
void MainWindow::controlPlayQueueButtons()
|
|
{
|
|
if (!savePlayQueueButton || !centerPlayQueueButton || !midSpacer) {
|
|
return;
|
|
}
|
|
|
|
savePlayQueueButton->setVisible(singlePane || playQueueWidget->width()>Utils::scaleForDpi(320));
|
|
centerPlayQueueButton->setVisible(singlePane || playQueueWidget->width()>(Utils::scaleForDpi(320)+centerPlayQueueButton->width()));
|
|
midSpacer->setVisible(singlePane || centerPlayQueueButton->isVisible());
|
|
}
|
|
|
|
void MainWindow::expandOrCollapse(bool saveCurrentSize)
|
|
{
|
|
if (!expandInterfaceAction->isChecked() && (isFullScreen() || isMaximized() || messageWidget->isVisible())) {
|
|
expandInterfaceAction->setChecked(true);
|
|
return;
|
|
}
|
|
|
|
static bool lastMax=false;
|
|
|
|
bool showing=expandInterfaceAction->isChecked();
|
|
QPoint p(isVisible() ? pos() : QPoint());
|
|
|
|
if (!showing) {
|
|
setMinimumHeight(0);
|
|
lastMax=isMaximized();
|
|
if (saveCurrentSize) {
|
|
expandedSize=size();
|
|
}
|
|
} else {
|
|
if (saveCurrentSize) {
|
|
collapsedSize=size();
|
|
}
|
|
setMinimumHeight(calcMinHeight());
|
|
setMaximumHeight(QWIDGETSIZE_MAX);
|
|
}
|
|
int prevWidth=size().width();
|
|
stack->setVisible(showing);
|
|
if (!showing) {
|
|
setWindowState(windowState()&~Qt::WindowMaximized);
|
|
}
|
|
adjustSize();
|
|
|
|
if (showing) {
|
|
resize(qMax(prevWidth, expandedSize.width()), expandedSize.height());
|
|
if (lastMax) {
|
|
showMaximized();
|
|
}
|
|
controlView();
|
|
} else {
|
|
// Width also sometimes expands, so make sure this is no larger than it was before...
|
|
collapsedSize=QSize(collapsedSize.isValid() ? qMin(collapsedSize.width(), prevWidth) : prevWidth, calcCollapsedSize());
|
|
resize(collapsedSize);
|
|
setFixedHeight(size().height());
|
|
}
|
|
if (!p.isNull()) {
|
|
move(p);
|
|
}
|
|
|
|
fullScreenAction->setEnabled(showing);
|
|
songInfoButton->setVisible(songInfoAction->isCheckable() && showing);
|
|
songInfoAction->setEnabled(showing);
|
|
}
|
|
|
|
void MainWindow::toggleContext()
|
|
{
|
|
if ( songInfoButton->isVisible()) {
|
|
if ( (MPDState_Playing==MPDStatus::self()->state() && !songInfoAction->isChecked()) ||
|
|
(MPDState_Stopped==MPDStatus::self()->state() && songInfoAction->isChecked()) ) {
|
|
songInfoAction->trigger();
|
|
}
|
|
} else if (MPDState_Playing==MPDStatus::self()->state() && PAGE_CONTEXT!=tabWidget->currentIndex()) {
|
|
int pp=prevPage;
|
|
showTab(PAGE_CONTEXT);
|
|
prevPage=pp;
|
|
} else if (MPDState_Stopped==MPDStatus::self()->state() && PAGE_CONTEXT==tabWidget->currentIndex() && -1!=prevPage) {
|
|
showTab(prevPage);
|
|
}
|
|
}
|
|
|
|
void MainWindow::hideWindow()
|
|
{
|
|
lastPos=pos();
|
|
hide();
|
|
}
|
|
|
|
void MainWindow::restoreWindow()
|
|
{
|
|
bool wasHidden=isHidden();
|
|
Utils::raiseWindow(this);
|
|
if (wasHidden && !lastPos.isNull()) {
|
|
move(lastPos);
|
|
}
|
|
}
|
|
|
|
#include "moc_mainwindow.cpp"
|