|
|
|
/***************************************************************************
|
|
|
|
copyright : (C) 2004 Nathan Toone
|
|
|
|
: (C) 2005 Michael Pyne <michael.pyne@kdemail.net>
|
|
|
|
email : nathan@toonetown.com
|
|
|
|
***************************************************************************/
|
|
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
* *
|
|
|
|
* 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 <kglobal.h>
|
|
|
|
#include <kapplication.h>
|
|
|
|
#include <kstandarddirs.h>
|
|
|
|
#include <kdebug.h>
|
|
|
|
|
|
|
|
#include <tqregexp.h>
|
|
|
|
#include <tqlayout.h>
|
|
|
|
#include <tqlabel.h>
|
|
|
|
#include <tqcursor.h>
|
|
|
|
|
|
|
|
#include "collectionlist.h"
|
|
|
|
#include "playlistsearch.h"
|
|
|
|
#include "playlistitem.h"
|
|
|
|
#include "coverinfo.h"
|
|
|
|
#include "tag.h"
|
|
|
|
|
|
|
|
struct CoverPopup : public QWidget
|
|
|
|
{
|
|
|
|
CoverPopup(const TQPixmap &image, const TQPoint &p) :
|
|
|
|
TQWidget(0, 0, WDestructiveClose | WX11BypassWM)
|
|
|
|
{
|
|
|
|
TQHBoxLayout *layout = new TQHBoxLayout(this);
|
|
|
|
TQLabel *label = new TQLabel(this);
|
|
|
|
|
|
|
|
layout->addWidget(label);
|
|
|
|
label->setFrameStyle(TQFrame::Box | TQFrame::Raised);
|
|
|
|
label->setLineWidth(1);
|
|
|
|
label->setPixmap(image);
|
|
|
|
|
|
|
|
setGeometry(p.x(), p.y(), label->width(), label->height());
|
|
|
|
show();
|
|
|
|
}
|
|
|
|
virtual void leaveEvent(TQEvent *) { close(); }
|
|
|
|
virtual void mouseReleaseEvent(TQMouseEvent *) { close(); }
|
|
|
|
};
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// public members
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
|
|
CoverInfo::CoverInfo(const FileHandle &file) :
|
|
|
|
m_file(file),
|
|
|
|
m_hasCover(false),
|
|
|
|
m_haveCheckedForCover(false),
|
|
|
|
m_coverKey(CoverManager::NoMatch),
|
|
|
|
m_needsConverting(false)
|
|
|
|
{
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CoverInfo::hasCover()
|
|
|
|
{
|
|
|
|
if(m_haveCheckedForCover)
|
|
|
|
return m_hasCover;
|
|
|
|
|
|
|
|
m_haveCheckedForCover = true;
|
|
|
|
|
|
|
|
// Check for new-style covers. First let's determine what our coverKey is
|
|
|
|
// if it's not already set, as that's also tracked by the CoverManager.
|
|
|
|
if(m_coverKey == CoverManager::NoMatch)
|
|
|
|
m_coverKey = CoverManager::idForTrack(m_file.absFilePath());
|
|
|
|
|
|
|
|
// We were assigned a key, let's see if we already have a cover. Notice
|
|
|
|
// that due to the way the CoverManager is structured, we should have a
|
|
|
|
// cover if we have a cover key. If we don't then either there's a logic
|
|
|
|
// error, or the user has been mucking around where they shouldn't.
|
|
|
|
if(m_coverKey != CoverManager::NoMatch)
|
|
|
|
m_hasCover = CoverManager::hasCover(m_coverKey);
|
|
|
|
|
|
|
|
// We *still* don't have it? Check the old-style covers then.
|
|
|
|
if(!m_hasCover) {
|
|
|
|
m_hasCover = TQFile(coverLocation(FullSize)).exists();
|
|
|
|
|
|
|
|
if(m_hasCover)
|
|
|
|
m_needsConverting = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return m_hasCover;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CoverInfo::clearCover()
|
|
|
|
{
|
|
|
|
m_hasCover = false;
|
|
|
|
|
|
|
|
// Yes, we have checked, and we don't have it. ;)
|
|
|
|
m_haveCheckedForCover = true;
|
|
|
|
|
|
|
|
m_needsConverting = false;
|
|
|
|
|
|
|
|
// We don't need to call removeCover because the CoverManager will
|
|
|
|
// automatically unlink the cover if we were the last track to use it.
|
|
|
|
CoverManager::setIdForTrack(m_file.absFilePath(), CoverManager::NoMatch);
|
|
|
|
m_coverKey = CoverManager::NoMatch;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CoverInfo::setCover(const TQImage &image)
|
|
|
|
{
|
|
|
|
if(image.isNull())
|
|
|
|
return;
|
|
|
|
|
|
|
|
m_haveCheckedForCover = true;
|
|
|
|
m_needsConverting = false;
|
|
|
|
m_hasCover = true;
|
|
|
|
|
|
|
|
TQPixmap cover;
|
|
|
|
cover.convertFromImage(image);
|
|
|
|
|
|
|
|
// If we use replaceCover we'll change the cover for every other track
|
|
|
|
// with the same coverKey, which we don't want since that case will be
|
|
|
|
// handled by Playlist. Instead just replace this track's cover.
|
|
|
|
m_coverKey = CoverManager::addCover(cover, m_file.tag()->artist(), m_file.tag()->album());
|
|
|
|
if(m_coverKey != CoverManager::NoMatch)
|
|
|
|
CoverManager::setIdForTrack(m_file.absFilePath(), m_coverKey);
|
|
|
|
}
|
|
|
|
|
|
|
|
void CoverInfo::setCoverId(coverKey id)
|
|
|
|
{
|
|
|
|
m_coverKey = id;
|
|
|
|
m_haveCheckedForCover = true;
|
|
|
|
m_needsConverting = false;
|
|
|
|
m_hasCover = id != CoverManager::NoMatch;
|
|
|
|
|
|
|
|
// Inform CoverManager of the change.
|
|
|
|
CoverManager::setIdForTrack(m_file.absFilePath(), m_coverKey);
|
|
|
|
}
|
|
|
|
|
|
|
|
void CoverInfo::applyCoverToWholeAlbum(bool overwriteExistingCovers) const
|
|
|
|
{
|
|
|
|
TQString artist = m_file.tag()->artist();
|
|
|
|
TQString album = m_file.tag()->album();
|
|
|
|
PlaylistSearch::ComponentList components;
|
|
|
|
ColumnList columns;
|
|
|
|
|
|
|
|
columns.append(PlaylistItem::ArtistColumn);
|
|
|
|
components.append(PlaylistSearch::Component(artist, false, columns, PlaylistSearch::Component::Exact));
|
|
|
|
|
|
|
|
columns.clear();
|
|
|
|
columns.append(PlaylistItem::AlbumColumn);
|
|
|
|
components.append(PlaylistSearch::Component(album, false, columns, PlaylistSearch::Component::Exact));
|
|
|
|
|
|
|
|
PlaylistList playlists;
|
|
|
|
playlists.append(CollectionList::instance());
|
|
|
|
|
|
|
|
PlaylistSearch search(playlists, components, PlaylistSearch::MatchAll);
|
|
|
|
|
|
|
|
// Search done, iterate through results.
|
|
|
|
|
|
|
|
PlaylistItemList results = search.matchedItems();
|
|
|
|
PlaylistItemList::ConstIterator it = results.constBegin();
|
|
|
|
for(; it != results.constEnd(); ++it) {
|
|
|
|
|
|
|
|
// Don't worry about files that somehow already have a tag,
|
|
|
|
// unless the coversion is forced.
|
|
|
|
if(!overwriteExistingCovers && !(*it)->file().coverInfo()->m_needsConverting)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
kdDebug(65432) << "Setting cover for: " << *it << endl;
|
|
|
|
(*it)->file().coverInfo()->setCoverId(m_coverKey);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
TQPixmap CoverInfo::pixmap(CoverSize size) const
|
|
|
|
{
|
|
|
|
if(m_needsConverting)
|
|
|
|
convertOldStyleCover();
|
|
|
|
|
|
|
|
if(m_coverKey == CoverManager::NoMatch)
|
|
|
|
return TQPixmap();
|
|
|
|
|
|
|
|
if(size == Thumbnail)
|
|
|
|
return CoverManager::coverFromId(m_coverKey, CoverManager::Thumbnail);
|
|
|
|
else
|
|
|
|
return CoverManager::coverFromId(m_coverKey, CoverManager::FullSize);
|
|
|
|
}
|
|
|
|
|
|
|
|
void CoverInfo::popup() const
|
|
|
|
{
|
|
|
|
TQPixmap image = pixmap(FullSize);
|
|
|
|
TQPoint mouse = TQCursor::pos();
|
|
|
|
TQRect desktop = KApplication::desktop()->screenGeometry(mouse);
|
|
|
|
|
|
|
|
int x = mouse.x();
|
|
|
|
int y = mouse.y();
|
|
|
|
int height = image.size().height() + 4;
|
|
|
|
int width = image.size().width() + 4;
|
|
|
|
|
|
|
|
// Detect the right direction to pop up (always towards the center of the
|
|
|
|
// screen), try to pop up with the mouse pointer 10 pixels into the image in
|
|
|
|
// both directions. If we're too close to the screen border for this margin,
|
|
|
|
// show it at the screen edge, accounting for the four pixels (two on each
|
|
|
|
// side) for the window border.
|
|
|
|
|
|
|
|
if(x - desktop.x() < desktop.width() / 2)
|
|
|
|
x = (x - desktop.x() < 10) ? desktop.x() : (x - 10);
|
|
|
|
else
|
|
|
|
x = (x - desktop.x() > desktop.width() - 10) ? desktop.width() - width +desktop.x() : (x - width + 10);
|
|
|
|
|
|
|
|
if(y - desktop.y() < desktop.height() / 2)
|
|
|
|
y = (y - desktop.y() < 10) ? desktop.y() : (y - 10);
|
|
|
|
else
|
|
|
|
y = (y - desktop.y() > desktop.height() - 10) ? desktop.height() - height + desktop.y() : (y - height + 10);
|
|
|
|
|
|
|
|
new CoverPopup(image, TQPoint(x, y));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* DEPRECATED
|
|
|
|
*/
|
|
|
|
TQString CoverInfo::coverLocation(CoverSize size) const
|
|
|
|
{
|
|
|
|
TQString fileName(TQFile::encodeName(m_file.tag()->artist() + " - " + m_file.tag()->album()));
|
|
|
|
TQRegExp maskedFileNameChars("[ /?:\"]");
|
|
|
|
|
|
|
|
fileName.replace(maskedFileNameChars, "_");
|
|
|
|
fileName.append(".png");
|
|
|
|
|
|
|
|
TQString dataDir = KGlobal::dirs()->saveLocation("appdata");
|
|
|
|
TQString subDir;
|
|
|
|
|
|
|
|
switch (size) {
|
|
|
|
case FullSize:
|
|
|
|
subDir = "large/";
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
TQString fileLocation = dataDir + "covers/" + subDir + fileName.lower();
|
|
|
|
|
|
|
|
return fileLocation;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CoverInfo::convertOldStyleCover() const
|
|
|
|
{
|
|
|
|
// Ah, old-style cover. Let's transfer it to the new system.
|
|
|
|
kdDebug() << "Found old style cover for " << m_file.absFilePath() << endl;
|
|
|
|
|
|
|
|
TQString artist = m_file.tag()->artist();
|
|
|
|
TQString album = m_file.tag()->album();
|
|
|
|
TQString oldLocation = coverLocation(FullSize);
|
|
|
|
m_coverKey = CoverManager::addCover(oldLocation, artist, album);
|
|
|
|
|
|
|
|
m_needsConverting = false;
|
|
|
|
|
|
|
|
if(m_coverKey != CoverManager::NoMatch) {
|
|
|
|
CoverManager::setIdForTrack(m_file.absFilePath(), m_coverKey);
|
|
|
|
|
|
|
|
// Now let's also set the ID for the tracks matching the track and
|
|
|
|
// artist at this point so that the conversion is complete, otherwise
|
|
|
|
// we can't tell apart the "No cover on purpose" and "Has no cover yet"
|
|
|
|
// possibilities.
|
|
|
|
|
|
|
|
applyCoverToWholeAlbum();
|
|
|
|
|
|
|
|
// If we convert we need to remove the old cover otherwise we'll find
|
|
|
|
// it later if the user un-sets the new cover.
|
|
|
|
if(!TQFile::remove(oldLocation))
|
|
|
|
kdError(65432) << "Unable to remove converted cover at " << oldLocation << endl;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
kdDebug() << "We were unable to replace the old style cover.\n";
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// vim: set et sw=4 ts=8:
|