File indexing completed on 2023-05-30 11:30:51
0001 /** 0002 * Copyright (C) 2002-2004 Scott Wheeler <wheeler@kde.org> 0003 * Copyright (C) 2009 Michael Pyne <mpyne@kde.org> 0004 * 0005 * This program is free software; you can redistribute it and/or modify it under 0006 * the terms of the GNU General Public License as published by the Free Software 0007 * Foundation; either version 2 of the License, or (at your option) any later 0008 * version. 0009 * 0010 * This program is distributed in the hope that it will be useful, but WITHOUT ANY 0011 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 0012 * PARTICULAR PURPOSE. See the GNU General Public License for more details. 0013 * 0014 * You should have received a copy of the GNU General Public License along with 0015 * this program. If not, see <http://www.gnu.org/licenses/>. 0016 */ 0017 0018 #include "playlistsplitter.h" 0019 0020 #include <kactioncollection.h> 0021 #include <ktoggleaction.h> 0022 #include <kacceleratormanager.h> 0023 #include <KConfigGroup> 0024 #include <KLocalizedString> 0025 #include <KSharedConfig> 0026 0027 #include <QAction> 0028 #include <QLatin1String> 0029 #include <QList> 0030 #include <QSizePolicy> 0031 #include <QStackedWidget> 0032 #include <QVBoxLayout> 0033 0034 #include "actioncollection.h" 0035 #include "collectionlist.h" 0036 #include "iconsupport.h" 0037 #include "juk_debug.h" 0038 #include "lyricswidget.h" 0039 #include "mpris2/mpris2.h" 0040 #include "nowplaying.h" 0041 #include "playermanager.h" 0042 #include "playlistbox.h" 0043 #include "playlistsearch.h" 0044 #include "searchwidget.h" 0045 #include "tageditor.h" 0046 0047 //////////////////////////////////////////////////////////////////////////////// 0048 // public methods 0049 //////////////////////////////////////////////////////////////////////////////// 0050 0051 PlaylistSplitter::PlaylistSplitter(PlayerManager *player, QWidget *parent) 0052 : QSplitter(Qt::Horizontal, parent) 0053 , m_player(player) 0054 { 0055 setObjectName(QLatin1String("playlistSplitter")); 0056 0057 setupActions(); 0058 setupLayout(); 0059 readConfig(); 0060 0061 m_editor->slotUpdateCollection(); 0062 m_editor->setupObservers(); 0063 } 0064 0065 PlaylistSplitter::~PlaylistSplitter() 0066 { 0067 saveConfig(); 0068 0069 m_playlistBox->stop(); // Remove playing item U/I state 0070 0071 // TagEditor needs to write its configuration out while it's still valid, 0072 // destroy it now. 0073 0074 delete m_editor; 0075 m_editor = nullptr; 0076 0077 delete m_lyricsWidget; 0078 m_lyricsWidget = nullptr; 0079 0080 // NowPlaying depends on the PlaylistCollection, so kill it now. 0081 delete m_nowPlaying; 0082 m_nowPlaying = nullptr; 0083 0084 delete m_searchWidget; // Take no chances here either. 0085 m_searchWidget = nullptr; 0086 0087 // Since we want to ensure that the shutdown process for the PlaylistCollection 0088 // (a base class for PlaylistBox) has a chance to write the playlists to disk 0089 // before they are deleted we're explicitly deleting the PlaylistBox here. 0090 0091 delete m_playlistBox; 0092 m_playlistBox = nullptr; 0093 } 0094 0095 PlaylistInterface *PlaylistSplitter::playlist() const 0096 { 0097 return m_playlistBox; 0098 } 0099 0100 //////////////////////////////////////////////////////////////////////////////// 0101 // public slots 0102 //////////////////////////////////////////////////////////////////////////////// 0103 0104 void PlaylistSplitter::setFocus() 0105 { 0106 if(m_searchWidget->isVisible()) { 0107 m_searchWidget->setFocus(); 0108 } else { 0109 slotFocusCurrentPlaylist(); 0110 } 0111 } 0112 0113 void PlaylistSplitter::slotFocusCurrentPlaylist() 0114 { 0115 Playlist *playlist = m_playlistBox->visiblePlaylist(); 0116 0117 if(!playlist) { 0118 return; 0119 } 0120 0121 playlist->setFocus(); 0122 playlist->clearSelection(); 0123 0124 // Select the top visible (and matching) item. 0125 0126 PlaylistItem *item = static_cast<PlaylistItem *>(playlist->itemAt(QPoint(0, 0))); 0127 0128 if(!item) { 0129 return; 0130 } 0131 0132 // A little bit of a hack to make QListView repaint things properly. Switch 0133 // to single selection mode, set the selection and then switch back. 0134 0135 playlist->setSelectionMode(QTreeWidget::SingleSelection); 0136 0137 playlist->setCurrentItem(item); 0138 0139 playlist->setSelectionMode(QTreeWidget::ExtendedSelection); 0140 } 0141 0142 //////////////////////////////////////////////////////////////////////////////// 0143 // private members 0144 //////////////////////////////////////////////////////////////////////////////// 0145 0146 Playlist *PlaylistSplitter::visiblePlaylist() const 0147 { 0148 return m_newVisible ? m_newVisible : m_playlistBox->visiblePlaylist(); 0149 } 0150 0151 void PlaylistSplitter::setupActions() 0152 { 0153 using namespace IconSupport; 0154 0155 KActionCollection* coll = ActionCollection::actions(); 0156 KToggleAction *showSearch = 0157 new KToggleAction("edit-find"_icon, i18n("Show &Search Bar"), this); 0158 coll->addAction("showSearch", showSearch); 0159 0160 QAction *act = new QAction("edit-clear"_icon, i18n("Edit Track Search"), this); 0161 coll->addAction("editTrackSearch", act); 0162 coll->setDefaultShortcut(act, Qt::Key_F6); 0163 connect(act, &QAction::triggered, 0164 this, &PlaylistSplitter::setFocus); 0165 } 0166 0167 void PlaylistSplitter::setupLayout() 0168 { 0169 using namespace ActionCollection; // add literal 0170 0171 setOpaqueResize(false); 0172 0173 // Disable the GUI until startup is complete (as indicated by PlaylistBox) 0174 0175 setEnabled(false); 0176 0177 // Create a splitter to go between the playlists and the editor. 0178 0179 m_editorSplitter = new QSplitter(Qt::Vertical, this); 0180 m_editorSplitter->setObjectName(QLatin1String("editorSplitter")); 0181 0182 // Make sure none of the optional widgets are collapsible, this causes the 0183 // widget to be essentially invisible but logically shown. 0184 0185 this->setChildrenCollapsible(false); 0186 m_editorSplitter->setChildrenCollapsible(false); 0187 0188 // Create the playlist and the editor. 0189 0190 QWidget *top = new QWidget(m_editorSplitter); 0191 QVBoxLayout *topLayout = new QVBoxLayout(top); 0192 topLayout->setContentsMargins(0, 0, 0, 0); 0193 topLayout->setSpacing(0); 0194 0195 m_playlistStack = new QStackedWidget(top); 0196 m_playlistStack->setObjectName(QLatin1String("playlistStack")); 0197 m_playlistStack->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); 0198 m_playlistStack->hide(); // Will be shown after CollectionList filled. 0199 0200 m_editor = new TagEditor(m_editorSplitter); 0201 m_editor->setObjectName(QLatin1String("TagEditor")); 0202 0203 // Create the lyrics widget 0204 m_lyricsWidget = new LyricsWidget(this); 0205 insertWidget(2, m_lyricsWidget); 0206 0207 // Create the PlaylistBox 0208 m_playlistBox = new PlaylistBox(m_player, this, m_playlistStack); 0209 m_playlistBox->setObjectName(QLatin1String("playlistBox")); 0210 0211 const auto &plistActions = m_playlistBox->collectionActions(); 0212 connect(plistActions, &PlaylistCollection::ActionHandler::signalSelectedItemsChanged, 0213 this, &PlaylistSplitter::slotPlaylistSelectionChanged); 0214 connect(m_playlistBox, &PlaylistBox::signalPlaylistDestroyed, 0215 m_editor, &TagEditor::slotPlaylistDestroyed); 0216 connect(m_playlistBox, &QTreeWidget::currentItemChanged, 0217 this, &PlaylistSplitter::slotCurrentPlaylistChanged); 0218 0219 // Let interested parties know we're ready 0220 connect(m_playlistBox, &PlaylistBox::startupComplete, this, [this]() { 0221 this->slotEnable(); 0222 this->setFocus(); 0223 0224 // Do this after initial playlist setup otherwise we'll waste 0225 // a lot of time starting up with the tag editor trying to 0226 // re-update after every item is loaded. 0227 connect(CollectionList::instance(), 0228 &CollectionList::signalCollectionChanged, 0229 m_editor, 0230 &TagEditor::slotUpdateCollection); 0231 0232 emit guiReady(); 0233 }); 0234 0235 m_player->setPlaylistInterface(m_playlistBox); 0236 0237 insertWidget(0, m_playlistBox); 0238 0239 m_nowPlaying = new NowPlaying(top, m_playlistBox); 0240 connect(m_player, &PlayerManager::signalItemChanged, 0241 m_nowPlaying, &NowPlaying::slotUpdate); 0242 connect(m_player, &PlayerManager::signalItemChanged, 0243 m_lyricsWidget, &LyricsWidget::playing); 0244 0245 // Create the search widget -- this must be done after the CollectionList is created. 0246 0247 m_searchWidget = new SearchWidget(top); 0248 0249 // auto-shortcuts don't seem to work and aren't needed anyway. 0250 KAcceleratorManager::setNoAccel(m_searchWidget); 0251 0252 connect(m_searchWidget, &SearchWidget::signalQueryChanged, 0253 this, &PlaylistSplitter::slotShowSearchResults); 0254 connect(m_searchWidget, &SearchWidget::signalDownPressed, 0255 this, &PlaylistSplitter::slotFocusCurrentPlaylist); 0256 connect(m_searchWidget, &SearchWidget::signalShown, 0257 m_playlistBox, [this](bool e) { m_playlistBox->setSearchEnabled(e); }); 0258 connect(m_searchWidget, &SearchWidget::returnPressed, 0259 m_playlistBox, [this]() { m_playlistBox->playFirst(); }); 0260 connect("showSearch"_act, &QAction::toggled, 0261 m_searchWidget, &SearchWidget::setEnabled); 0262 connect(m_playlistBox, &PlaylistBox::signalMoveFocusAway, 0263 m_searchWidget, qOverload<>(&SearchWidget::setFocus)); 0264 0265 topLayout->addWidget(m_nowPlaying); 0266 topLayout->addWidget(m_searchWidget); 0267 topLayout->insertStretch(-1); // Force search bar to top while playlistStack hides 0268 topLayout->addWidget(m_playlistStack, 1); 0269 0270 connect(m_playlistStack, &QStackedWidget::currentChanged, 0271 this, &PlaylistSplitter::slotPlaylistChanged); 0272 0273 // Show the collection on startup. 0274 m_playlistBox->setCurrentItem(m_playlistBox->topLevelItem(0)); 0275 } 0276 0277 void PlaylistSplitter::readConfig() 0278 { 0279 using namespace ActionCollection; // add literal 0280 0281 KConfigGroup config(KSharedConfig::openConfig(), "Splitter"); 0282 0283 QList<int> splitterSizes = config.readEntry("PlaylistSplitterSizes",QList<int>()); 0284 if(splitterSizes.isEmpty()) { 0285 splitterSizes.append(100); 0286 splitterSizes.append(640); 0287 } 0288 setSizes(splitterSizes); 0289 0290 bool showSearch = config.readEntry("ShowSearch", true); 0291 "showSearch"_act->setChecked(showSearch); 0292 m_searchWidget->setHidden(!showSearch); 0293 0294 splitterSizes = config.readEntry("EditorSplitterSizes",QList<int>()); 0295 if(splitterSizes.isEmpty()) { 0296 // If no sizes were saved, use default sizes for the playlist and the 0297 // editor, respectively. The values are just hints for the actual size, 0298 // m_editorSplitter will distribute the space according to their 0299 // relative weight. 0300 splitterSizes.append(300); 0301 splitterSizes.append(200); 0302 } 0303 m_editorSplitter->setSizes(splitterSizes); 0304 } 0305 0306 void PlaylistSplitter::saveConfig() 0307 { 0308 using namespace ActionCollection; // add literal 0309 0310 KConfigGroup config(KSharedConfig::openConfig(), "Splitter"); 0311 config.writeEntry("PlaylistSplitterSizes", sizes()); 0312 config.writeEntry("ShowSearch", "showSearch"_act->isChecked()); 0313 config.writeEntry("EditorSplitterSizes", m_editorSplitter->sizes()); 0314 } 0315 0316 void PlaylistSplitter::slotShowSearchResults() 0317 { 0318 visiblePlaylist()->setSearch(m_searchWidget->search(visiblePlaylist())); 0319 } 0320 0321 void PlaylistSplitter::slotPlaylistSelectionChanged() 0322 { 0323 m_editor->slotSetItems(visiblePlaylist()->selectedItems()); 0324 } 0325 0326 void PlaylistSplitter::slotPlaylistChanged(int i) 0327 { 0328 Playlist *p = qobject_cast<Playlist *>(m_playlistStack->widget(i)); 0329 0330 if(!p) 0331 return; 0332 0333 m_newVisible = p; 0334 m_searchWidget->setSearch(p->search()); 0335 m_newVisible = nullptr; 0336 } 0337 0338 void PlaylistSplitter::slotCurrentPlaylistChanged(QTreeWidgetItem *item) 0339 { 0340 auto pItem = static_cast<PlaylistBox::Item*>(item); 0341 emit currentPlaylistChanged(*(pItem->playlist())); 0342 } 0343 0344 void PlaylistSplitter::slotEnable() 0345 { 0346 setEnabled(true); // Ready to go. 0347 m_playlistStack->show(); 0348 0349 (void) new Mpris2(this); 0350 } 0351 0352 // vim: set et sw=4 tw=0 sta: