File indexing completed on 2023-05-30 11:30:49
0001 /** 0002 * Copyright (C) 2002-2004 Scott Wheeler <wheeler@kde.org> 0003 * Copyright (C) 2021 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 "playlistbox.h" 0019 0020 #include <kmessagebox.h> 0021 #include <kactioncollection.h> 0022 #include <ktoggleaction.h> 0023 #include <kselectaction.h> 0024 #include <kconfiggroup.h> 0025 #include <kwidgetsaddons_version.h> 0026 #include <KSharedConfig> 0027 0028 #include <QAction> 0029 #include <QDragLeaveEvent> 0030 #include <QDragMoveEvent> 0031 #include <QElapsedTimer> 0032 #include <QFileInfo> 0033 #include <QHeaderView> 0034 #include <QIcon> 0035 #include <QKeyEvent> 0036 #include <QMenu> 0037 #include <QMouseEvent> 0038 #include <QTime> 0039 #include <QTimer> 0040 0041 #include "actioncollection.h" 0042 #include "cache.h" 0043 #include "collectionlist.h" 0044 #include "dbuscollectionproxy.h" 0045 #include "dynamicplaylist.h" 0046 #include "historyplaylist.h" 0047 #include "iconsupport.h" 0048 #include "juk_debug.h" 0049 #include "playermanager.h" 0050 #include "playlist.h" 0051 #include "searchplaylist.h" 0052 #include "tagtransactionmanager.h" 0053 #include "treeviewitemplaylist.h" 0054 #include "upcomingplaylist.h" 0055 #include "viewmode.h" 0056 0057 using namespace ActionCollection; // ""_act and others 0058 using namespace IconSupport; // ""_icon 0059 0060 //////////////////////////////////////////////////////////////////////////////// 0061 // PlaylistBox public methods 0062 //////////////////////////////////////////////////////////////////////////////// 0063 0064 PlaylistBox::PlaylistBox(PlayerManager *player, QWidget *parent, QStackedWidget *playlistStack) 0065 : QTreeWidget(parent) 0066 , PlaylistCollection(player, playlistStack) 0067 { 0068 readConfig(); 0069 setHeaderLabel("Playlists"); 0070 setRootIsDecorated(false); 0071 setContextMenuPolicy(Qt::CustomContextMenu); 0072 viewport()->setAcceptDrops(true); 0073 setDragDropMode(QAbstractItemView::DropOnly); 0074 setDropIndicatorShown(true); 0075 0076 setColumnCount(2); // Use fake column for sorting 0077 setColumnHidden(1, true); 0078 setSortingEnabled(true); 0079 sortByColumn(1, Qt::AscendingOrder); 0080 0081 header()->blockSignals(true); 0082 header()->hide(); 0083 header()->blockSignals(false); 0084 0085 setSelectionMode(QAbstractItemView::ExtendedSelection); 0086 0087 m_contextMenu = new QMenu(this); 0088 0089 m_contextMenu->addAction(action("file_new")); 0090 m_contextMenu->addAction(action("renamePlaylist")); 0091 m_contextMenu->addAction(action("editSearch")); 0092 m_contextMenu->addAction(action("duplicatePlaylist")); 0093 m_contextMenu->addAction(action("reloadPlaylist")); 0094 m_contextMenu->addAction(action("deleteItemPlaylist")); 0095 m_contextMenu->addAction(action("file_save")); 0096 m_contextMenu->addAction(action("file_save_as")); 0097 0098 m_contextMenu->addSeparator(); 0099 0100 // add the view modes stuff 0101 0102 KSelectAction *viewModeAction = 0103 new KSelectAction("view-choose"_icon, i18n("View Modes"), ActionCollection::actions()); 0104 ActionCollection::actions()->addAction("viewModeMenu", viewModeAction); 0105 0106 ViewMode* viewmode = new ViewMode(this); 0107 m_viewModes.append(viewmode); 0108 viewModeAction->addAction("view-list-details"_icon, viewmode->name()); 0109 0110 CompactViewMode* compactviewmode = new CompactViewMode(this); 0111 m_viewModes.append(compactviewmode); 0112 viewModeAction->addAction("view-list-text"_icon, compactviewmode->name()); 0113 0114 // TODO: Fix the broken tree view mode 0115 #if 0 0116 TreeViewMode* treeviewmode = new TreeViewMode(this); 0117 m_viewModes.append(treeviewmode); 0118 viewModeAction->addAction("view-list-tree"_icon, treeviewmode->name()); 0119 #endif 0120 0121 CollectionList::initialize(this); 0122 0123 viewModeAction->setCurrentItem(m_viewModeIndex); 0124 m_viewModes[m_viewModeIndex]->setShown(true); 0125 0126 raise(CollectionList::instance()); 0127 0128 m_contextMenu->addAction(viewModeAction); 0129 connect(viewModeAction, &QAction::triggered, 0130 this, &PlaylistBox::slotSetViewMode); 0131 0132 connect(this, &PlaylistBox::itemSelectionChanged, 0133 this, &PlaylistBox::slotPlaylistChanged); 0134 0135 connect(this, &PlaylistBox::itemDoubleClicked, 0136 this, &PlaylistBox::slotDoubleClicked); 0137 0138 connect(this, &PlaylistBox::customContextMenuRequested, 0139 this, &PlaylistBox::slotShowContextMenu); 0140 0141 connect(this, &PlaylistBox::signalPlayFile, 0142 player, qOverload<const FileHandle &>(&PlayerManager::play)); 0143 0144 const auto *tagManager = TagTransactionManager::instance(); 0145 connect(tagManager, &TagTransactionManager::signalAboutToModifyTags, 0146 this, &PlaylistBox::slotFreezePlaylists); 0147 connect(tagManager, &TagTransactionManager::signalDoneModifyingTags, 0148 this, &PlaylistBox::slotUnfreezePlaylists); 0149 0150 setupUpcomingPlaylist(); 0151 0152 const auto *collectionList = CollectionList::instance(); 0153 connect(collectionList, &CollectionList::signalNewTag, 0154 this, &PlaylistBox::slotAddItem); 0155 connect(collectionList, &CollectionList::signalRemovedTag, 0156 this, &PlaylistBox::slotRemoveItem); 0157 connect(collectionList, &CollectionList::cachedItemsLoaded, 0158 this, &PlaylistBox::slotLoadCachedPlaylists); 0159 0160 KToggleAction *historyAction = 0161 new KToggleAction("view-history"_icon, i18n("Show &History"), ActionCollection::actions()); 0162 ActionCollection::actions()->addAction("showHistory", historyAction); 0163 connect(historyAction, &KToggleAction::triggered, 0164 this, &PlaylistBox::slotSetHistoryPlaylistEnabled); 0165 0166 m_showTimer = new QTimer(this); 0167 m_showTimer->setSingleShot(true); 0168 m_showTimer->setInterval(500); 0169 connect(m_showTimer, &QTimer::timeout, 0170 this, &PlaylistBox::slotShowDropTarget); 0171 0172 // hook up to the D-Bus 0173 (void) new DBusCollectionProxy(this, this); 0174 } 0175 0176 PlaylistBox::~PlaylistBox() 0177 { 0178 PlaylistList l; 0179 CollectionList *collection = CollectionList::instance(); 0180 for(QTreeWidgetItemIterator it(topLevelItem(0)); *it; ++it) { 0181 Item *item = static_cast<Item *>(*it); 0182 if(item->playlist() && item->playlist() != collection) 0183 l.append(item->playlist()); 0184 } 0185 0186 Cache::savePlaylists(l); 0187 saveConfig(); 0188 0189 // Some view modes use event filters onto sibling widgets which may be 0190 // destroyed before the view mode. 0191 // Manually delete the view modes instead. 0192 qDeleteAll(m_viewModes); 0193 m_viewModes.clear(); 0194 } 0195 0196 void PlaylistBox::raise(Playlist *playlist) 0197 { 0198 if(!playlist) 0199 return; 0200 0201 Item *i = m_playlistDict.value(playlist, 0); 0202 0203 if(i) { 0204 clearSelection(); 0205 setCurrentItem(i); 0206 0207 setSingleItem(i); 0208 scrollToItem(currentItem()); 0209 } 0210 else 0211 PlaylistCollection::raise(playlist); 0212 0213 slotPlaylistChanged(); 0214 } 0215 0216 void PlaylistBox::duplicate() 0217 { 0218 Item *item = static_cast<Item *>(currentItem()); 0219 if(!item || !item->playlist()) 0220 return; 0221 0222 QString name = playlistNameDialog(i18nc("verb, copy the playlist", "Duplicate"), item->text(0)); 0223 0224 if(name.isNull()) 0225 return; 0226 0227 Playlist *p = new Playlist(this, name); 0228 p->createItems(item->playlist()->items()); 0229 } 0230 0231 void PlaylistBox::scanFolders() 0232 { 0233 PlaylistCollection::scanFolders(); 0234 emit startupComplete(); 0235 } 0236 0237 bool PlaylistBox::requestPlaybackFor(const FileHandle &file) 0238 { 0239 emit signalPlayFile(file); 0240 return true; 0241 } 0242 0243 //////////////////////////////////////////////////////////////////////////////// 0244 // PlaylistBox public slots 0245 //////////////////////////////////////////////////////////////////////////////// 0246 0247 void PlaylistBox::paste() 0248 { 0249 // TODO: Reimplement 0250 } 0251 0252 //////////////////////////////////////////////////////////////////////////////// 0253 // PlaylistBox protected methods 0254 //////////////////////////////////////////////////////////////////////////////// 0255 0256 void PlaylistBox::slotFreezePlaylists() 0257 { 0258 setDynamicListsFrozen(true); 0259 } 0260 0261 void PlaylistBox::slotUnfreezePlaylists() 0262 { 0263 setDynamicListsFrozen(false); 0264 } 0265 0266 void PlaylistBox::slotPlaylistDataChanged() 0267 { 0268 if(m_savePlaylistTimer) 0269 m_savePlaylistTimer->start(); // Restarts the timer if it's already running. 0270 } 0271 0272 void PlaylistBox::slotSetHistoryPlaylistEnabled(bool enable) 0273 { 0274 setHistoryPlaylistEnabled(enable); 0275 } 0276 0277 void PlaylistBox::setupPlaylist(Playlist *playlist, const QString &iconName) 0278 { 0279 setupPlaylist(playlist, iconName, nullptr); 0280 } 0281 0282 void PlaylistBox::setupPlaylist(Playlist *playlist, const QString &iconName, Item *parentItem) 0283 { 0284 connect(playlist, &Playlist::signalPlaylistItemsDropped, 0285 this, &PlaylistBox::slotPlaylistItemsDropped); 0286 connect(playlist, &Playlist::signalMoveFocusAway, 0287 this, &PlaylistBox::signalMoveFocusAway); 0288 0289 PlaylistCollection::setupPlaylist(playlist, iconName); 0290 0291 if(parentItem) 0292 new Item(parentItem, iconName, playlist->name(), playlist); 0293 else 0294 new Item(this, iconName, playlist->name(), playlist); 0295 } 0296 0297 void PlaylistBox::removePlaylist(Playlist *playlist) 0298 { 0299 // Could be false if setup() wasn't run yet. 0300 if(m_playlistDict.contains(playlist)) { 0301 removeNameFromDict(m_playlistDict[playlist]->text(0)); 0302 delete m_playlistDict[playlist]; // Delete the Item* 0303 } 0304 0305 removeFileFromDict(playlist->fileName()); 0306 m_playlistDict.remove(playlist); 0307 } 0308 0309 Qt::DropActions PlaylistBox::supportedDropActions() const 0310 { 0311 return Qt::CopyAction; 0312 } 0313 0314 bool PlaylistBox::dropMimeData(QTreeWidgetItem *parent, int index, const QMimeData *data, Qt::DropAction action) 0315 { 0316 Q_UNUSED(index); 0317 0318 // The *parent* item won't be null, but index should be zero except in the 0319 // still-broken "tree view" mode. 0320 0321 if(!parent || action != Qt::CopyAction || !data->hasUrls()) { 0322 return false; 0323 } 0324 0325 auto *playlistItem = static_cast<Item *>(parent); 0326 if(!playlistItem) { 0327 return false; 0328 } 0329 0330 auto *playlist = playlistItem->playlist(); 0331 const auto droppedUrls = data->urls(); 0332 PlaylistItem *lastItem = nullptr; 0333 0334 for(const auto &url : droppedUrls) { 0335 lastItem = playlist->createItem(FileHandle(url.toLocalFile()), lastItem); 0336 } 0337 0338 return true; 0339 } 0340 0341 QStringList PlaylistBox::mimeTypes() const 0342 { 0343 auto result = QTreeWidget::mimeTypes(); 0344 0345 // Need to add Playlists's mime type to convince QTreeWidget to allow it as 0346 // a drop option. 0347 result.append(QLatin1String("text/uri-list")); 0348 0349 return result; 0350 } 0351 0352 //////////////////////////////////////////////////////////////////////////////// 0353 // PlaylistBox private methods 0354 //////////////////////////////////////////////////////////////////////////////// 0355 0356 void PlaylistBox::readConfig() 0357 { 0358 KConfigGroup config(KSharedConfig::openConfig(), "PlaylistBox"); 0359 m_viewModeIndex = config.readEntry("ViewMode", 0); 0360 0361 // TODO Restore ability to use Tree View once fixed. 0362 if(m_viewModeIndex == 2) { 0363 m_viewModeIndex = 0; 0364 } 0365 } 0366 0367 void PlaylistBox::saveConfig() 0368 { 0369 KConfigGroup config(KSharedConfig::openConfig(), "PlaylistBox"); 0370 config.writeEntry("ViewMode", action<KSelectAction>("viewModeMenu")->currentItem()); 0371 KSharedConfig::openConfig()->sync(); 0372 } 0373 0374 void PlaylistBox::remove() 0375 { 0376 const ItemList items = selectedBoxItems(); 0377 0378 if(items.isEmpty()) 0379 return; 0380 0381 QStringList files; 0382 QStringList names; 0383 0384 for(const auto &item : items) { 0385 if(!item || !item->playlist()) { 0386 qFatal("Ran into an empty item or item playlist when removing playlists!"); 0387 } 0388 0389 if (!item->playlist()->fileName().isEmpty() && 0390 QFileInfo::exists(item->playlist()->fileName())) 0391 { 0392 files.append(item->playlist()->fileName()); 0393 } 0394 0395 names.append(item->playlist()->name()); 0396 } 0397 0398 if(!files.isEmpty()) { 0399 const auto warnFunc = 0400 #if KWIDGETSADDONS_VERSION >= QT_VERSION_CHECK(5,100,0) 0401 &KMessageBox::warningTwoActionsCancelList; 0402 #else 0403 &KMessageBox::warningYesNoCancelList; 0404 #endif 0405 0406 const auto yesButton = 0407 #if KWIDGETSADDONS_VERSION >= QT_VERSION_CHECK(5,100,0) 0408 KMessageBox::PrimaryAction; 0409 #else 0410 KMessageBox::Yes; 0411 #endif 0412 0413 const auto remove = warnFunc( 0414 this, i18n("Do you want to delete these files from the disk as well?"), 0415 files, QString(), KStandardGuiItem::del(), KGuiItem(i18n("Keep")), 0416 KStandardGuiItem::cancel(), QString(), KMessageBox::Notify 0417 ); 0418 0419 if(remove == yesButton) { 0420 QStringList couldNotDelete; 0421 for(const auto &playlistFile : qAsConst(files)) { 0422 if(!QFile::remove(playlistFile)) 0423 couldNotDelete.append(playlistFile); 0424 } 0425 0426 if(!couldNotDelete.isEmpty()) 0427 KMessageBox::errorList(this, i18n("Could not delete these files."), couldNotDelete); 0428 } 0429 else if(remove == KMessageBox::Cancel) 0430 return; 0431 } 0432 else if(items.count() > 1 || items.front()->playlist() != upcomingPlaylist()) { 0433 if(KMessageBox::Cancel == KMessageBox::warningContinueCancelList( 0434 this, 0435 i18n("Are you sure you want to remove these " 0436 "playlists from your collection?"), 0437 names, 0438 i18n("Remove Items?"), 0439 KGuiItem(i18n("&Remove"), "user-trash"))) 0440 { 0441 return; 0442 } 0443 } 0444 0445 for(const auto &item : items) { 0446 if(item != Item::collectionItem() && 0447 !item->playlist()->readOnly()) 0448 { 0449 if(item->playlist() != upcomingPlaylist()) 0450 delete item; 0451 else { 0452 action<KToggleAction>("showUpcoming")->setChecked(false); 0453 setUpcomingPlaylistEnabled(false); 0454 } 0455 } 0456 } 0457 0458 setSingleItem(Item::collectionItem()); 0459 } 0460 0461 void PlaylistBox::setDynamicListsFrozen(bool frozen) 0462 { 0463 for(auto &playlistBoxItem : qAsConst(m_viewModes)) { 0464 playlistBoxItem->setDynamicListsFrozen(frozen); 0465 } 0466 } 0467 0468 void PlaylistBox::slotSavePlaylists() 0469 { 0470 qCDebug(JUK_LOG) << "Auto-saving playlists."; 0471 0472 PlaylistList l; 0473 CollectionList *collection = CollectionList::instance(); 0474 for(QTreeWidgetItemIterator it(topLevelItem(0)); *it; ++it) { 0475 Item *item = static_cast<Item *>(*it); 0476 if(item->playlist() && item->playlist() != collection) 0477 l.append(item->playlist()); 0478 } 0479 0480 Cache::savePlaylists(l); 0481 } 0482 0483 void PlaylistBox::slotShowDropTarget() 0484 { 0485 if(m_dropItem) raise(m_dropItem->playlist()); 0486 } 0487 0488 void PlaylistBox::slotAddItem(const QString &tag, unsigned column) 0489 { 0490 for(auto &viewMode : qAsConst(m_viewModes)) { 0491 viewMode->addItems(QStringList(tag), column); 0492 } 0493 } 0494 0495 void PlaylistBox::slotRemoveItem(const QString &tag, unsigned column) 0496 { 0497 for(auto &viewMode : qAsConst(m_viewModes)) { 0498 viewMode->removeItem(tag, column); 0499 } 0500 } 0501 0502 void PlaylistBox::mousePressEvent(QMouseEvent *e) 0503 { 0504 if(e->button() == Qt::LeftButton) 0505 m_doingMultiSelect = true; 0506 QTreeWidget::mousePressEvent(e); 0507 } 0508 0509 void PlaylistBox::mouseReleaseEvent(QMouseEvent *e) 0510 { 0511 if(e->button() == Qt::LeftButton) { 0512 m_doingMultiSelect = false; 0513 slotPlaylistChanged(); 0514 } 0515 QTreeWidget::mouseReleaseEvent(e); 0516 } 0517 0518 void PlaylistBox::keyPressEvent(QKeyEvent *e) 0519 { 0520 if((e->key() == Qt::Key_Up || e->key() == Qt::Key_Down) && e->modifiers() == Qt::ShiftModifier) 0521 m_doingMultiSelect = true; 0522 QTreeWidget::keyPressEvent(e); 0523 } 0524 0525 void PlaylistBox::keyReleaseEvent(QKeyEvent *e) 0526 { 0527 if(m_doingMultiSelect && e->key() == Qt::Key_Shift) { 0528 m_doingMultiSelect = false; 0529 slotPlaylistChanged(); 0530 } 0531 QTreeWidget::keyReleaseEvent(e); 0532 } 0533 0534 PlaylistBox::ItemList PlaylistBox::selectedBoxItems() 0535 { 0536 ItemList l; 0537 0538 for(QTreeWidgetItemIterator it(this, QTreeWidgetItemIterator::Selected) 0539 ; *it 0540 ; ++it) 0541 { 0542 l.append(static_cast<Item *>(*it)); 0543 } 0544 0545 return l; 0546 } 0547 0548 void PlaylistBox::setSingleItem(QTreeWidgetItem *item) 0549 { 0550 setSelectionMode(QAbstractItemView::SingleSelection); 0551 setCurrentItem(item); 0552 setSelectionMode(QAbstractItemView::ExtendedSelection); 0553 } 0554 0555 void PlaylistBox::dragMoveEvent(QDragMoveEvent* event) 0556 { 0557 QTreeWidget::dragMoveEvent(event); 0558 0559 Item* hovered_item = static_cast<Item*>(itemAt(event->pos())); 0560 if(hovered_item != m_dropItem){ 0561 m_dropItem = hovered_item; 0562 if(m_dropItem) m_showTimer->start(); 0563 else m_showTimer->stop(); 0564 }; 0565 } 0566 0567 void PlaylistBox::dragLeaveEvent(QDragLeaveEvent* event) 0568 { 0569 QTreeWidget::dragLeaveEvent(event); 0570 m_showTimer->stop(); 0571 } 0572 0573 //////////////////////////////////////////////////////////////////////////////// 0574 // PlaylistBox private slots 0575 //////////////////////////////////////////////////////////////////////////////// 0576 0577 void PlaylistBox::slotPlaylistChanged() 0578 { 0579 // Don't update while the mouse is pressed down. 0580 0581 if(m_doingMultiSelect) 0582 return; 0583 0584 const ItemList items = selectedBoxItems(); 0585 m_hasSelection = !items.isEmpty(); 0586 0587 const bool allowReload = std::any_of(items.begin(), items.end(), 0588 [](const auto &item) { 0589 return item->playlist() && item->playlist()->canReload(); 0590 }); 0591 0592 PlaylistList playlists; 0593 for(const auto &playlistBoxItem : items) { 0594 auto p = playlistBoxItem->playlist(); 0595 if(p) { 0596 playlists.append(p); 0597 } 0598 } 0599 0600 bool singlePlaylist = playlists.count() == 1; 0601 0602 if(playlists.isEmpty() || 0603 (singlePlaylist && 0604 (playlists.front() == CollectionList::instance() || 0605 playlists.front()->readOnly()))) 0606 { 0607 action("file_save")->setEnabled(false); 0608 action("file_save_as")->setEnabled(false); 0609 action("renamePlaylist")->setEnabled(false); 0610 action("deleteItemPlaylist")->setEnabled(false); 0611 } 0612 else { 0613 action("file_save")->setEnabled(true); 0614 action("file_save_as")->setEnabled(true); 0615 action("renamePlaylist")->setEnabled(playlists.count() == 1); 0616 action("deleteItemPlaylist")->setEnabled(true); 0617 } 0618 action("reloadPlaylist")->setEnabled(allowReload); 0619 action("duplicatePlaylist")->setEnabled(!playlists.isEmpty()); 0620 0621 action("editSearch")->setEnabled(singlePlaylist && 0622 playlists.front()->searchIsEditable()); 0623 0624 if(singlePlaylist) { 0625 PlaylistCollection::raise(playlists.front()); 0626 0627 if(playlists.front() == upcomingPlaylist()) { 0628 action("deleteItemPlaylist")->setText(i18n("Hid&e")); 0629 action("deleteItemPlaylist")->setIcon("list-remove"_icon); 0630 } 0631 else { 0632 action("deleteItemPlaylist")->setText(i18n("R&emove")); 0633 action("deleteItemPlaylist")->setIcon("user-trash"_icon); 0634 } 0635 } 0636 else if(!playlists.isEmpty()) 0637 createDynamicPlaylist(playlists); 0638 } 0639 0640 void PlaylistBox::slotDoubleClicked(QTreeWidgetItem *item) 0641 { 0642 if(!item) 0643 return; 0644 auto *playlist = static_cast<Item *>(item)->playlist(); 0645 0646 playlist->slotBeginPlayback(); 0647 } 0648 0649 void PlaylistBox::slotShowContextMenu(const QPoint &point) 0650 { 0651 m_contextMenu->popup(mapToGlobal(point)); 0652 } 0653 0654 void PlaylistBox::slotPlaylistItemsDropped(Playlist *p) 0655 { 0656 raise(p); 0657 } 0658 0659 void PlaylistBox::slotSetViewMode(int index) 0660 { 0661 if(index == m_viewModeIndex) 0662 return; 0663 0664 viewMode()->setShown(false); 0665 m_viewModeIndex = index; 0666 viewMode()->setShown(true); 0667 } 0668 0669 void PlaylistBox::setupItem(Item *item) 0670 { 0671 m_playlistDict.insert(item->playlist(), item); 0672 viewMode()->queueRefresh(); 0673 } 0674 0675 void PlaylistBox::setupUpcomingPlaylist() 0676 { 0677 KConfigGroup config(KSharedConfig::openConfig(), "Playlists"); 0678 bool enable = config.readEntry("showUpcoming", false); 0679 0680 setUpcomingPlaylistEnabled(enable); 0681 action<KToggleAction>("showUpcoming")->setChecked(enable); 0682 } 0683 0684 0685 void PlaylistBox::slotLoadCachedPlaylists() 0686 { 0687 qCDebug(JUK_LOG) << "Loading cached playlists."; 0688 QElapsedTimer stopwatch; 0689 stopwatch.start(); 0690 0691 Cache::loadPlaylists(this); 0692 0693 qCDebug(JUK_LOG) << "Cached playlists loaded, took" << stopwatch.elapsed() << "ms"; 0694 0695 // Auto-save playlists after they change. 0696 m_savePlaylistTimer = new QTimer(this); 0697 m_savePlaylistTimer->setInterval(3000); // 3 seconds with no change? -> commit 0698 m_savePlaylistTimer->setSingleShot(true); 0699 connect(m_savePlaylistTimer, &QTimer::timeout, 0700 this, &PlaylistBox::slotSavePlaylists); 0701 0702 clearSelection(); 0703 setCurrentItem(m_playlistDict[CollectionList::instance()]); 0704 0705 QTimer::singleShot(0, this, [this]() { 0706 CollectionList::instance()->slotCheckCache(); 0707 this->scanFolders(); 0708 }); 0709 } 0710 0711 //////////////////////////////////////////////////////////////////////////////// 0712 // PlaylistBox::Item protected methods 0713 //////////////////////////////////////////////////////////////////////////////// 0714 0715 PlaylistBox::Item *PlaylistBox::Item::m_collectionItem = nullptr; 0716 0717 PlaylistBox::Item::Item(PlaylistBox *listBox, const QString &icon, const QString &text, Playlist *l) 0718 : QObject(listBox), QTreeWidgetItem(listBox, QStringList(text)), 0719 m_playlist(l), m_iconName(icon), m_sortedFirst(false) 0720 { 0721 init(); 0722 } 0723 0724 PlaylistBox::Item::Item(Item *parent, const QString &icon, const QString &text, Playlist *l) 0725 : QObject(parent->listView()), QTreeWidgetItem(parent, QStringList(text)), 0726 m_playlist(l), m_iconName(icon), m_sortedFirst(false) 0727 { 0728 init(); 0729 } 0730 0731 PlaylistBox::Item::~Item() 0732 { 0733 0734 } 0735 0736 void PlaylistBox::Item::setup() 0737 { 0738 listView()->viewMode()->setupItem(this); 0739 } 0740 0741 //////////////////////////////////////////////////////////////////////////////// 0742 // PlaylistBox::Item protected slots 0743 //////////////////////////////////////////////////////////////////////////////// 0744 0745 void PlaylistBox::Item::slotSetName(const QString &name) 0746 { 0747 setText(0, name); // Display name 0748 setText(1, sortTextFor(name)); 0749 setSelected(true); 0750 0751 treeWidget()->scrollToItem(this); 0752 } 0753 0754 void PlaylistBox::Item::playlistItemDataChanged() 0755 { 0756 // This avoids spuriously re-saving all playlists just because play queue 0757 // changes. 0758 if(m_playlist != listView()->upcomingPlaylist()) 0759 listView()->slotPlaylistDataChanged(); 0760 } 0761 0762 //////////////////////////////////////////////////////////////////////////////// 0763 // PlaylistBox::Item private methods 0764 //////////////////////////////////////////////////////////////////////////////// 0765 0766 void PlaylistBox::Item::init() 0767 { 0768 PlaylistBox *list = listView(); 0769 0770 list->setupItem(this); 0771 0772 const QString itemText(text()); 0773 setIcon(0, QIcon::fromTheme(m_iconName)); 0774 list->addNameToDict(itemText); 0775 0776 if(m_playlist) { 0777 connect(m_playlist, &Playlist::signalNameChanged, 0778 this, &Item::slotSetName); 0779 connect(m_playlist, &Playlist::signalEnableDirWatch, 0780 this, [list](bool enable) { 0781 list->enableDirWatch(enable); 0782 }); 0783 } 0784 0785 if(m_playlist == CollectionList::instance()) { 0786 m_sortedFirst = true; 0787 m_collectionItem = this; 0788 list->viewMode()->setupDynamicPlaylists(); 0789 } 0790 0791 if(m_playlist == list->historyPlaylist() || m_playlist == list->upcomingPlaylist()) 0792 m_sortedFirst = true; 0793 0794 setText(1, sortTextFor(itemText)); 0795 0796 connect(&(m_playlist->signaller), &PlaylistInterfaceSignaller::playingItemDataChanged, this, &PlaylistBox::Item::playlistItemDataChanged); 0797 } 0798 0799 QString PlaylistBox::Item::sortTextFor(const QString &name) const 0800 { 0801 // Collection List goes before everything, then 0802 // playlists that 'sort first', then remainder of 0803 // playlists. 0804 const auto prefix 0805 = (playlist() == CollectionList::instance()) 0806 ? QStringLiteral("0") 0807 : m_sortedFirst 0808 ? QStringLiteral("1") 0809 : QStringLiteral("2"); 0810 return prefix + name; 0811 } 0812 0813 // vim: set et sw=4 tw=0 sta: