Members of the KDE Community are recommended to subscribe to the kde-community mailing list at https://mail.kde.org/mailman/listinfo/kde-community to allow them to participate in important discussions and receive other important announcements

filehandle.cpp 7.38 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
/***************************************************************************
    begin                : Sun Feb 29 2004
    copyright            : (C) 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.                                   *
 *                                                                         *
 ***************************************************************************/

16
#include "filehandle.h"
17

18 19
#include <kdebug.h>

20 21 22 23
#include <QFileInfo>

#include <limits.h>
#include <stdlib.h>
24

25
#include "filehandleproperties.h"
26
#include "tag.h"
27
#include "cache.h"
28
#include "coverinfo.h"
29

30 31 32 33 34 35
/**
 * Used to give every track added in the program a unique identifier. See
 * PlaylistItem
 */
quint32 g_trackID = 0;

Dirk Mueller's avatar
Dirk Mueller committed
36 37 38 39 40 41 42 43 44 45 46
AddProperty(Title, tag()->title())
AddProperty(Artist, tag()->artist())
AddProperty(Album, tag()->album())
AddProperty(Genre, tag()->genre())
AddNumberProperty(Track, tag()->track())
AddNumberProperty(Year, tag()->year())
AddProperty(Comment, tag()->comment())
AddNumberProperty(Seconds, tag()->seconds())
AddNumberProperty(Bitrate, tag()->bitrate())
AddProperty(Path, absFilePath())
AddNumberProperty(Size, fileInfo().size())
47
AddProperty(Extension, fileInfo().suffix())
48

49 50 51 52
static QString resolveSymLinks(const QFileInfo &file) // static
{
    char real[PATH_MAX];

53
    if(file.exists() && realpath(QFile::encodeName(file.absoluteFilePath()).data(), real))
54
        return QFile::decodeName(real);
55
    else
56
        return file.filePath();
57 58
}

59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77
/**
 * A simple reference counter -- pasted from TagLib.
 */

class RefCounter
{
public:
    RefCounter() : refCount(1) {}
    void ref() { refCount++; }
    bool deref() { return ! --refCount ; }
    int count() const { return refCount; }
private:
    uint refCount;
};

class FileHandle::FileHandlePrivate : public RefCounter
{
public:
    FileHandlePrivate() :
78
        tag(0),
79 80 81 82
        coverInfo(0),
        id(g_trackID++)
        {
        }
83

Scott Wheeler's avatar
Scott Wheeler committed
84 85 86
    ~FileHandlePrivate()
    {
        delete tag;
87
        delete coverInfo;
Scott Wheeler's avatar
Scott Wheeler committed
88 89
    }

90
    mutable Tag *tag;
91
    mutable CoverInfo *coverInfo;
92
    mutable QString absFilePath;
93 94 95
    QFileInfo fileInfo;
    QDateTime modificationTime;
    QDateTime lastModified;
96
    quint32 id;
97 98 99 100 101 102 103 104
};

////////////////////////////////////////////////////////////////////////////////
// public methods
////////////////////////////////////////////////////////////////////////////////

FileHandle::FileHandle()
{
105 106
    static FileHandlePrivate nullPrivate;
    d = &nullPrivate;
107
    d->ref();
108 109
}

110 111
FileHandle::FileHandle(const FileHandle &f) :
    d(f.d)
112
{
113
    if(!d) {
114
        kDebug() << "The source FileHandle was not initialized.";
115
        d = null().d;
116
    }
117 118 119
    d->ref();
}

120 121
FileHandle::FileHandle(const QFileInfo &info, const QString &path) :
    d(0)
122
{
123
    setup(info, path);
124 125
}

126 127
FileHandle::FileHandle(const QString &path) :
    d(0)
128
{
129
    setup(QFileInfo(path), path);
130 131
}

132 133 134
FileHandle::FileHandle(const QString &path, CacheDataStream &s)
{
    d = new FileHandlePrivate;
135 136 137 138
    if(!QFile::exists(path)) {
        kWarning() << "File" << path << "no longer exists!";
        return;
    }
139
    d->fileInfo = QFileInfo(path);
140
    d->absFilePath = path;
141 142 143 144
    read(s);
    Cache::instance()->insert(*this);
}

145 146 147 148 149 150
FileHandle::~FileHandle()
{
    if(d->deref())
        delete d;
}

151 152 153 154
void FileHandle::refresh()
{
    d->fileInfo.refresh();
    delete d->tag;
155
    d->tag = new Tag(d->absFilePath);
156 157
}

158 159
void FileHandle::setFile(const QString &path)
{
Michael Pyne's avatar
Michael Pyne committed
160 161 162 163 164 165
    if(path.isEmpty()) {
        kError() << "trying to set an empty path" << endl;
        return;
    }

    if(!QFile::exists(path)) {
Jan Gerrit Marker's avatar
Jan Gerrit Marker committed
166
        kError() << "trying to set non-existent file: " << path << endl;
Michael Pyne's avatar
Michael Pyne committed
167 168 169
        return;
    }

170 171 172
    if(!d || isNull())
        setup(QFileInfo(path), path);
    else {
173
        d->absFilePath = resolveSymLinks(path);
174
        d->fileInfo.setFile(path);
175
        d->tag->setFileName(d->absFilePath);
176
    }
177 178
}

179 180
Tag *FileHandle::tag() const
{
181
    if(!d->tag)
182
        d->tag = new Tag(d->absFilePath);
183

184 185 186
    return d->tag;
}

187 188 189
CoverInfo *FileHandle::coverInfo() const
{
    if(!d->coverInfo)
190
        d->coverInfo = new CoverInfo(*this);
191 192 193 194

    return d->coverInfo;
}

195 196
QString FileHandle::absFilePath() const
{
197
    if(d->absFilePath.isEmpty())
198
        d->absFilePath = resolveSymLinks(d->fileInfo.absoluteFilePath());
199 200 201 202 203 204 205 206
    return d->absFilePath;
}

const QFileInfo &FileHandle::fileInfo() const
{
    return d->fileInfo;
}

207 208 209 210 211
bool FileHandle::isNull() const
{
    return *this == null();
}

212 213
bool FileHandle::current() const
{
214 215 216
    return (d->modificationTime.isValid() &&
            lastModified().isValid() &&
            d->modificationTime >= lastModified());
217 218 219 220
}

const QDateTime &FileHandle::lastModified() const
{
221 222 223 224 225 226
    if(d->lastModified.isNull())
        d->lastModified = d->fileInfo.lastModified();

    return d->lastModified;
}

227 228 229 230 231
quint32 FileHandle::id() const
{
    return d->id;
}

232 233 234 235 236 237
void FileHandle::read(CacheDataStream &s)
{
    switch(s.cacheVersion()) {
    case 1:
    default:
        if(!d->tag)
238
            d->tag = new Tag(d->absFilePath, true);
239

240 241 242 243
        s >> *(d->tag);
        s >> d->modificationTime;
        break;
    }
244 245
}

246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263
FileHandle &FileHandle::operator=(const FileHandle &f)
{
    if(&f == this)
        return *this;

    if(d->deref())
        delete d;

    d = f.d;
    d->ref();

    return *this;
}

bool FileHandle::operator==(const FileHandle &f) const
{
    return d == f.d;
}
264 265 266 267 268 269

bool FileHandle::operator!=(const FileHandle &f) const
{
    return d != f.d;
}

270 271 272 273 274 275 276
QStringList FileHandle::properties() // static
{
    return FileHandleProperties::properties();
}

QString FileHandle::property(const QString &name) const
{
Dirk Mueller's avatar
Dirk Mueller committed
277
    return FileHandleProperties::property(*this, name.toAscii());
278 279
}

280 281 282 283 284 285 286 287 288 289 290 291 292 293 294
const FileHandle &FileHandle::null() // static
{
    static FileHandle f;
    return f;
}

////////////////////////////////////////////////////////////////////////////////
// private methods
////////////////////////////////////////////////////////////////////////////////

void FileHandle::setup(const QFileInfo &info, const QString &path)
{
    if(d && !isNull())
        return;

295
    QString fileName = path.isEmpty() ? info.absoluteFilePath() : path;
296

297
    FileHandle cached = Cache::instance()->value(resolveSymLinks(fileName));
298 299 300 301 302 303 304 305

    if(cached != null()) {
        d = cached.d;
        d->ref();
    }
    else {
        d = new FileHandlePrivate;
        d->fileInfo = info;
306
        d->absFilePath = resolveSymLinks(fileName);
307
        d->modificationTime = info.lastModified();
308 309 310 311
        if(info.exists())
            Cache::instance()->insert(*this);
        else
            kWarning() << "File" << path << "no longer exists!";
312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331
    }
}

////////////////////////////////////////////////////////////////////////////////
// related functions
////////////////////////////////////////////////////////////////////////////////

QDataStream &operator<<(QDataStream &s, const FileHandle &f)
{
    s << *(f.tag())
      << f.lastModified();

    return s;
}

CacheDataStream &operator>>(CacheDataStream &s, FileHandle &f)
{
    f.read(s);
    return s;
}
332 333

// vim: set et sw=4 tw=0 sta: