From e2de64d6f1beb9e492daf5b886e19933c1fa41dd Mon Sep 17 00:00:00 2001 From: toma Date: Wed, 25 Nov 2009 17:56:58 +0000 Subject: Copy the KDE 3.5 branch to branches/trinity for new KDE 3.5 features. BUG:215923 git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/kdemultimedia@1054174 283d02a7-25f6-0310-bc7c-ecb5cbfe19da --- juk/collectionlist.cpp | 511 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 511 insertions(+) create mode 100644 juk/collectionlist.cpp (limited to 'juk/collectionlist.cpp') diff --git a/juk/collectionlist.cpp b/juk/collectionlist.cpp new file mode 100644 index 00000000..9540be85 --- /dev/null +++ b/juk/collectionlist.cpp @@ -0,0 +1,511 @@ +/*************************************************************************** + begin : Fri Sep 13 2002 + copyright : (C) 2002 - 2004 by Scott Wheeler + email : wheeler@kde.org + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "collectionlist.h" +#include "playlistcollection.h" +#include "splashscreen.h" +#include "stringshare.h" +#include "cache.h" +#include "actioncollection.h" +#include "tag.h" +#include "viewmode.h" +#include "coverinfo.h" +#include "covermanager.h" + +using namespace ActionCollection; + +//////////////////////////////////////////////////////////////////////////////// +// static methods +//////////////////////////////////////////////////////////////////////////////// + +CollectionList *CollectionList::m_list = 0; + +CollectionList *CollectionList::instance() +{ + return m_list; +} + +void CollectionList::initialize(PlaylistCollection *collection) +{ + if(m_list) + return; + + // We have to delay initilaization here because dynamic_cast or comparing to + // the collection instance won't work in the PlaylistBox::Item initialization + // won't work until the CollectionList is fully constructed. + + m_list = new CollectionList(collection); + m_list->setName(i18n("Collection List")); + + FileHandleHash::Iterator end = Cache::instance()->end(); + for(FileHandleHash::Iterator it = Cache::instance()->begin(); it != end; ++it) + new CollectionListItem(*it); + + SplashScreen::update(); + + // The CollectionList is created with sorting disabled for speed. Re-enable + // it here, and perform the sort. + KConfigGroup config(KGlobal::config(), "Playlists"); + + SortOrder order = Descending; + if(config.readBoolEntry("CollectionListSortAscending", true)) + order = Ascending; + + m_list->setSortOrder(order); + m_list->setSortColumn(config.readNumEntry("CollectionListSortColumn", 1)); + + m_list->sort(); + + collection->setupPlaylist(m_list, "folder_sound"); +} + +//////////////////////////////////////////////////////////////////////////////// +// public methods +//////////////////////////////////////////////////////////////////////////////// + +PlaylistItem *CollectionList::createItem(const FileHandle &file, QListViewItem *, bool) +{ + // It's probably possible to optimize the line below away, but, well, right + // now it's more important to not load duplicate items. + + if(m_itemsDict.find(file.absFilePath())) + return 0; + + PlaylistItem *item = new CollectionListItem(file); + + if(!item->isValid()) { + kdError() << "CollectionList::createItem() -- A valid tag was not created for \"" + << file.absFilePath() << "\"" << endl; + delete item; + return 0; + } + + setupItem(item); + + return item; +} + +void CollectionList::clearItems(const PlaylistItemList &items) +{ + for(PlaylistItemList::ConstIterator it = items.begin(); it != items.end(); ++it) { + Cache::instance()->remove((*it)->file()); + clearItem(*it, false); + } + + dataChanged(); +} + +void CollectionList::setupTreeViewEntries(ViewMode *viewMode) const +{ + TreeViewMode *treeViewMode = dynamic_cast(viewMode); + if(!treeViewMode) { + kdWarning(65432) << "Can't setup entries on a non-tree-view mode!\n"; + return; + } + + QValueList columnList; + columnList << PlaylistItem::ArtistColumn; + columnList << PlaylistItem::GenreColumn; + columnList << PlaylistItem::AlbumColumn; + + QStringList items; + for(QValueList::Iterator colIt = columnList.begin(); colIt != columnList.end(); ++colIt) { + items.clear(); + for(TagCountDictIterator it(*m_columnTags[*colIt]); it.current(); ++it) + items << it.currentKey(); + + treeViewMode->addItems(items, *colIt); + } +} + +void CollectionList::slotNewItems(const KFileItemList &items) +{ + QStringList files; + + for(KFileItemListIterator it(items); it.current(); ++it) + files.append((*it)->url().path()); + + addFiles(files); + update(); +} + +void CollectionList::slotRefreshItems(const KFileItemList &items) +{ + for(KFileItemListIterator it(items); it.current(); ++it) { + CollectionListItem *item = lookup((*it)->url().path()); + + if(item) { + item->refreshFromDisk(); + + // If the item is no longer on disk, remove it from the collection. + + if(item->file().fileInfo().exists()) + item->repaint(); + else + clearItem(item); + } + } + + update(); +} + +void CollectionList::slotDeleteItem(KFileItem *item) +{ + CollectionListItem *listItem = lookup(item->url().path()); + if(listItem) + clearItem(listItem); +} + +//////////////////////////////////////////////////////////////////////////////// +// public slots +//////////////////////////////////////////////////////////////////////////////// + +void CollectionList::clear() +{ + int result = KMessageBox::warningContinueCancel(this, + i18n("Removing an item from the collection will also remove it from " + "all of your playlists. Are you sure you want to continue?\n\n" + "Note, however, that if the directory that these files are in is in " + "your \"scan on startup\" list, they will be readded on startup.")); + + if(result == KMessageBox::Continue) { + Playlist::clear(); + emit signalCollectionChanged(); + } +} + +void CollectionList::slotCheckCache() +{ + PlaylistItemList invalidItems; + + for(QDictIteratorit(m_itemsDict); it.current(); ++it) { + if(!it.current()->checkCurrent()) + invalidItems.append(*it); + processEvents(); + } + + clearItems(invalidItems); +} + +void CollectionList::slotRemoveItem(const QString &file) +{ + clearItem(m_itemsDict[file]); +} + +void CollectionList::slotRefreshItem(const QString &file) +{ + m_itemsDict[file]->refresh(); +} + +//////////////////////////////////////////////////////////////////////////////// +// protected methods +//////////////////////////////////////////////////////////////////////////////// + +CollectionList::CollectionList(PlaylistCollection *collection) : + Playlist(collection, true), + m_itemsDict(5003), + m_columnTags(15, 0) +{ + new KAction(i18n("Show Playing"), KShortcut(), actions(), "showPlaying"); + + connect(action("showPlaying"), SIGNAL(activated()), this, SLOT(slotShowPlaying())); + + connect(action("back")->popupMenu(), SIGNAL(aboutToShow()), + this, SLOT(slotPopulateBackMenu())); + connect(action("back")->popupMenu(), SIGNAL(activated(int)), + this, SLOT(slotPlayFromBackMenu(int))); + setSorting(-1); // Temporarily disable sorting to add items faster. + + m_columnTags[PlaylistItem::ArtistColumn] = new TagCountDict(5001, false); + m_columnTags[PlaylistItem::ArtistColumn]->setAutoDelete(true); + + m_columnTags[PlaylistItem::AlbumColumn] = new TagCountDict(5001, false); + m_columnTags[PlaylistItem::AlbumColumn]->setAutoDelete(true); + + m_columnTags[PlaylistItem::GenreColumn] = new TagCountDict(5001, false); + m_columnTags[PlaylistItem::GenreColumn]->setAutoDelete(true); + + polish(); +} + +CollectionList::~CollectionList() +{ + KConfigGroup config(KGlobal::config(), "Playlists"); + config.writeEntry("CollectionListSortColumn", sortColumn()); + config.writeEntry("CollectionListSortAscending", sortOrder() == Ascending); + + // The CollectionListItems will try to remove themselves from the + // m_columnTags member, so we must make sure they're gone before we + // are. + + clearItems(items()); + for(TagCountDicts::Iterator it = m_columnTags.begin(); it != m_columnTags.end(); ++it) + delete *it; +} + +void CollectionList::contentsDropEvent(QDropEvent *e) +{ + if(e->source() == this) + return; // Don't rearrange in the CollectionList. + else + Playlist::contentsDropEvent(e); +} + +void CollectionList::contentsDragMoveEvent(QDragMoveEvent *e) +{ + if(canDecode(e) && e->source() != this) + e->accept(true); + else + e->accept(false); +} + +QString CollectionList::addStringToDict(const QString &value, unsigned column) +{ + if(column > m_columnTags.count() || value.stripWhiteSpace().isEmpty()) + return QString::null; + + int *refCountPtr = m_columnTags[column]->find(value); + if(refCountPtr) + ++(*refCountPtr); + else { + m_columnTags[column]->insert(value, new int(1)); + emit signalNewTag(value, column); + } + + return value; +} + +QStringList CollectionList::uniqueSet(UniqueSetType t) const +{ + int column; + + switch(t) + { + case Artists: + column = PlaylistItem::ArtistColumn; + break; + + case Albums: + column = PlaylistItem::AlbumColumn; + break; + + case Genres: + column = PlaylistItem::GenreColumn; + break; + + default: + return QStringList(); + } + + if((unsigned) column >= m_columnTags.count()) + return QStringList(); + + TagCountDictIterator it(*m_columnTags[column]); + QStringList list; + + for(; it.current(); ++it) + list += it.currentKey(); + + return list; +} + +void CollectionList::removeStringFromDict(const QString &value, unsigned column) +{ + if(column > m_columnTags.count() || value.isEmpty()) + return; + + int *refCountPtr = m_columnTags[column]->find(value); + if(refCountPtr) { + --(*refCountPtr); + if(*refCountPtr == 0) { + emit signalRemovedTag(value, column); + m_columnTags[column]->remove(value); + } + } +} + +//////////////////////////////////////////////////////////////////////////////// +// CollectionListItem public methods +//////////////////////////////////////////////////////////////////////////////// + +void CollectionListItem::refresh() +{ + int offset = static_cast(listView())->columnOffset(); + int columns = lastColumn() + offset + 1; + + data()->local8Bit.resize(columns); + data()->cachedWidths.resize(columns); + + for(int i = offset; i < columns; i++) { + int id = i - offset; + if(id != TrackNumberColumn && id != LengthColumn) { + // All columns other than track num and length need local-encoded data for sorting + + QCString lower = text(i).lower().local8Bit(); + + // For some columns, we may be able to share some strings + + if((id == ArtistColumn) || (id == AlbumColumn) || + (id == GenreColumn) || (id == YearColumn) || + (id == CommentColumn)) + { + lower = StringShare::tryShare(lower); + + if(id != YearColumn && id != CommentColumn && data()->local8Bit[id] != lower) { + CollectionList::instance()->removeStringFromDict(data()->local8Bit[id], id); + CollectionList::instance()->addStringToDict(text(i), id); + } + } + + data()->local8Bit[id] = lower; + } + + int newWidth = width(listView()->fontMetrics(), listView(), i); + data()->cachedWidths[i] = newWidth; + + if(newWidth != data()->cachedWidths[i]) + playlist()->slotWeightDirty(i); + } + + file().coverInfo()->setCover(); + + if(listView()->isVisible()) + repaint(); + + for(PlaylistItemList::Iterator it = m_children.begin(); it != m_children.end(); ++it) { + (*it)->playlist()->update(); + (*it)->playlist()->dataChanged(); + if((*it)->listView()->isVisible()) + (*it)->repaint(); + } + + CollectionList::instance()->dataChanged(); + emit CollectionList::instance()->signalCollectionChanged(); +} + +PlaylistItem *CollectionListItem::itemForPlaylist(const Playlist *playlist) +{ + if(playlist == CollectionList::instance()) + return this; + + PlaylistItemList::ConstIterator it; + for(it = m_children.begin(); it != m_children.end(); ++it) + if((*it)->playlist() == playlist) + return *it; + return 0; +} + +void CollectionListItem::updateCollectionDict(const QString &oldPath, const QString &newPath) +{ + CollectionList *collection = CollectionList::instance(); + + if(!collection) + return; + + collection->removeFromDict(oldPath); + collection->addToDict(newPath, this); +} + +void CollectionListItem::repaint() const +{ + QListViewItem::repaint(); + for(PlaylistItemList::ConstIterator it = m_children.begin(); it != m_children.end(); ++it) + (*it)->repaint(); +} + +//////////////////////////////////////////////////////////////////////////////// +// CollectionListItem protected methods +//////////////////////////////////////////////////////////////////////////////// + +CollectionListItem::CollectionListItem(const FileHandle &file) : + PlaylistItem(CollectionList::instance()), + m_shuttingDown(false) +{ + CollectionList *l = CollectionList::instance(); + if(l) { + l->addToDict(file.absFilePath(), this); + + data()->fileHandle = file; + + if(file.tag()) { + refresh(); + l->dataChanged(); + // l->addWatched(m_path); + } + else + kdError() << "CollectionListItem::CollectionListItem() -- Tag() could not be created." << endl; + } + else + kdError(65432) << "CollectionListItems should not be created before " + << "CollectionList::initialize() has been called." << endl; + + SplashScreen::increment(); +} + +CollectionListItem::~CollectionListItem() +{ + m_shuttingDown = true; + + for(PlaylistItemList::ConstIterator it = m_children.begin(); + it != m_children.end(); + ++it) + { + (*it)->playlist()->clearItem(*it); + } + + CollectionList *l = CollectionList::instance(); + if(l) { + l->removeFromDict(file().absFilePath()); + l->removeStringFromDict(file().tag()->album(), AlbumColumn); + l->removeStringFromDict(file().tag()->artist(), ArtistColumn); + l->removeStringFromDict(file().tag()->genre(), GenreColumn); + } +} + +void CollectionListItem::addChildItem(PlaylistItem *child) +{ + m_children.append(child); +} + +void CollectionListItem::removeChildItem(PlaylistItem *child) +{ + if(!m_shuttingDown) + m_children.remove(child); +} + +bool CollectionListItem::checkCurrent() +{ + if(!file().fileInfo().exists() || !file().fileInfo().isFile()) + return false; + + if(!file().current()) { + file().refresh(); + refresh(); + } + + return true; +} + +#include "collectionlist.moc" -- cgit v1.2.1