/*
* Cantata
*
* Copyright (c) 2011-2022 Craig Drummond
*
*/
/*
* 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 .
*/
#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"
#ifdef ENABLE_SIMPLE_MPD_SUPPORT
#include "mpd-interface/mpduser.h"
#endif
#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
#include
#include
#include
#include
#ifdef Q_OS_WIN
#include "windows/thumbnailtoolbar.h"
#endif
#include
#include
#include
#include
#include
#include
#include "mediakeys.h"
#include
#include
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(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 seeks=QList() << 5 << 30 << 60;
QList seekShortcuts=QList() << (int)Qt::ControlModifier << (int)Qt::ShiftModifier << (int)(Qt::ControlModifier|Qt::ShiftModifier);
for (int i=0; icreateAction("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 (isetShortcut((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 &)), SLOT(copyToDevice(const QString &, const QString &, const QList &)));
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)), this, SLOT(locateTracks(QList)));
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("Enter a string to search artist, album, title, etc. To filter based on year, add #year-range to search string - e.g.
"
"- #2000 return tracks from 2000
"
"- #1980-1989 return tracks from the 80's
"
"- Blah #2000 to search for string Blah and only return tracks from 2000
"
"
"));
QList playbackBtns=QList() << prevTrackButton << stopTrackButton << playPauseTrackButton << nextTrackButton;
QList controlBtns=QList() << 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 pqWidgets = QList() << 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() << 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() << 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; screateAction(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 &)), SLOT(copyToDevice(const QString &, const QString &, const QList &)));
connect(folderPage->mpd(), SIGNAL(addToDevice(const QString &, const QString &, const QList &)), SLOT(copyToDevice(const QString &, const QString &, const QList &)));
connect(playlistsPage, SIGNAL(addToDevice(const QString &, const QString &, const QList &)), SLOT(copyToDevice(const QString &, const QString &, const QList &)));
connect(devicesPage, SIGNAL(addToDevice(const QString &, const QString &, const QList &)), SLOT(copyToDevice(const QString &, const QString &, const QList &)));
connect(searchPage, SIGNAL(addToDevice(const QString &, const QString &, const QList &)), SLOT(copyToDevice(const QString &, const QString &, const QList &)));
connect(StdActions::self()->deleteSongsAction, SIGNAL(triggered()), SLOT(deleteSongs()));
connect(devicesPage, SIGNAL(deleteSongs(const QString &, const QList &)), SLOT(deleteSongs(const QString &, const QList &)));
connect(libraryPage, SIGNAL(deleteSongs(const QString &, const QList &)), SLOT(deleteSongs(const QString &, const QList &)));
connect(folderPage->mpd(), SIGNAL(deleteSongs(const QString &, const QList &)), SLOT(deleteSongs(const QString &, const QList &)));
connect(searchPage, SIGNAL(deleteSongs(const QString &, const QList &)), SLOT(deleteSongs(const QString &, const QList &)));
#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 &)), this, SLOT(partitionsUpdated(const QList &)));
connect(MPDConnection::self(), SIGNAL(outputsUpdated(const QList