filerenamer.cpp 6.64 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13
/*
 * filerenamer.cpp - (c) 2003 Frerich Raabe <raabe@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 "filerenamer.h"
#include "playlistitem.h"

#include <kapplication.h>
#include <kconfig.h>
Scott Wheeler's avatar
build  
Scott Wheeler committed
14
#include <kdebug.h>
15
#include <klocale.h>
16
#include <kmacroexpander.h>
17
#include <kmessagebox.h>
18 19 20

#include <qdir.h>

21 22
FileRenamer::Config::Config(KConfigBase *cfg)
    : m_grp(cfg, "FileRenamer")
23 24 25
{
}

26
QString FileRenamer::Config::filenameScheme() const
27
{
28
    return m_grp.readEntry("FilenameScheme", QDir::homeDirPath() + "/Music/%a%A%T%t%c");
29 30
}

31
void FileRenamer::Config::setFilenameScheme(const QString &scheme)
32
{
33
    m_grp.writeEntry("FilenameScheme", scheme);
34 35
}

36
QString FileRenamer::Config::getToken(TokenType type) const
37
{
38 39 40 41 42 43 44 45 46
    QString fallback;
    switch(type) {
        case Title: fallback = "%s"; break;
        case Artist: fallback = "%s/"; break;
        case Album: fallback = "%s/"; break;
        case Track: fallback = "[%s] "; break;
        case Comment: fallback = " (%s)"; break;
    }
    return m_grp.readEntry(tokenToString(type) + "Token", fallback);
47
}
48

49 50 51 52
void FileRenamer::Config::setToken(TokenType type, const QString &value)
{
    m_grp.writeEntry(tokenToString(type) + "Token", value);
}
53

54 55
bool FileRenamer::Config::tokenNeedsValue(TokenType type) const
{
56 57
    bool fallback = type != Title ? true : false;
    return m_grp.readBoolEntry("Need" + tokenToString(type) + "Value", fallback);
58 59
}

60
void FileRenamer::Config::setTokenNeedsValue(TokenType type, bool needsValue)
61
{
62
    m_grp.writeEntry("Need" + tokenToString(type) + "Value", needsValue);
63 64
}

65
QString FileRenamer::tokenToString(TokenType type)
66
{
67 68 69 70 71 72 73 74
    switch(type) {
        case Title: return "Title";
        case Artist: return "Artist";
        case Album: return "Album";
        case Track: return "Track";
        case Comment: return "Comment";
    }
    return QString::null;
75 76
}

77 78
FileRenamer::FileRenamer()
    : m_cfg(kapp->config())
79 80 81
{
}

82 83
FileRenamer::FileRenamer(const PlaylistItem *item)
    : m_cfg(kapp->config())
84
{
85
    rename(item);
86 87
}

88
QString FileRenamer::expandToken(TokenType type, const QString &value_) const
89
{
90 91
    const bool needValue = m_cfg.tokenNeedsValue(type);

92
    QString value = value_;
93
    QString token = m_cfg.getToken(type);
94
    if(value.find(QDir::separator()) > -1) {
95
        kdWarning() << "Found token value with dir separators!" << endl;
96
        value.replace(QDir::separator(), "");
97
    }
98 99 100 101 102

    if((needValue) && value.isEmpty())
        return QString();

    token.replace("%s", value);
103
    return token;
104 105
}

106 107 108 109 110
void FileRenamer::rename(const PlaylistItem *item)
{
    if(item == 0 || item->tag() == 0)
        return;

111
    QString newFilename = rename(item->absFilePath(), *item->tag());
112
    if(KMessageBox::warningContinueCancel(0,
113 114
        i18n("<qt>You're about to rename the file<br/><br/> '%1'<br/><br/> to <br/><br/>'%2'<br/><br/>Are you sure you "
             "want to continue?</qt>").arg(item->absFilePath()).arg(newFilename),
115 116 117 118 119 120 121 122 123 124 125 126 127 128 129
              i18n("Warning"), KStdGuiItem::cont(), "ShowFileRenamerWarning")
       == KMessageBox::Continue) {
        moveFile(item->absFilePath(), newFilename);
    }
}

void FileRenamer::rename(const PlaylistItemList &items)
{
    QMap<QString, QString> map;
    QStringList filenames;

    PlaylistItemList::ConstIterator it = items.begin();
    for(; it != items.end(); ++it) {
        const QString oldName = (*it)->absFilePath();
        const QString newName = rename(oldName, *(*it)->tag());
130 131
        filenames += oldName;
        filenames += " => " + newName;
132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148
        map[oldName] = newName;
    }

    if(KMessageBox::warningContinueCancelList(0, i18n("You're about to "
       "rename the following files. Are you sure you want to continue?"),
       filenames, i18n("Warning"), KStdGuiItem::cont(), "ShowFileRenamerWarning")
       == KMessageBox::Continue) {
        KApplication::setOverrideCursor(Qt::waitCursor);
        int j = 1;
        QMap<QString, QString>::ConstIterator it = map.begin();
        for(; it != map.end(); ++it, ++j) {
            moveFile(it.key(), it.data());
            if(j % 5 == 0)
                kapp->processEvents();
        }
        KApplication::restoreOverrideCursor();
    }
149 150 151 152
}

QString FileRenamer::rename(const QString &filename, const Tag &tag) const
{
153
    QString newFilename = m_cfg.filenameScheme();
154 155

    QMap<QChar, QString> substitutions;
156 157 158 159 160
    substitutions[ 't' ] = expandToken(Title, tag.track());
    substitutions[ 'a' ] = expandToken(Artist, tag.artist());
    substitutions[ 'A' ] = expandToken(Album, tag.album());
    substitutions[ 'T' ] = expandToken(Track, tag.trackNumberString());
    substitutions[ 'c' ] = expandToken(Comment, tag.comment());
161 162 163 164 165

    newFilename = KMacroExpander::expandMacros(newFilename, substitutions);
    newFilename = newFilename.stripWhiteSpace();

    if(QFileInfo(newFilename).isRelative())
166
        newFilename = filename.left( filename.findRev( "/" ) )
167
            + "/" + newFilename;
168
    newFilename += "." + QFileInfo(filename).extension();
169

170
    return newFilename;
171 172 173 174
}

void FileRenamer::moveFile(const QString &src, const QString &dest)
{
Zack Rusin's avatar
Zack Rusin committed
175
    kdDebug(65432) << "Moving file " << src << " to " << dest << endl;
176 177 178 179 180 181 182 183 184 185 186 187

    if(src == dest)
        return;

    QString dest_ = dest.mid(1); // strip the leading "/"
    if(dest_.find("/") > 0) {
        const QStringList components = QStringList::split("/", dest_.left( dest.findRev("/")));
        QStringList::ConstIterator it = components.begin();
        QStringList::ConstIterator end = components.end();
        QString processedComponents;
        for(; it != end; ++it) {
            processedComponents += "/" + *it;
Zack Rusin's avatar
Zack Rusin committed
188
            kdDebug(65432) << "Checking path " << processedComponents << endl;
189
            QDir dir(processedComponents);
190 191
            if(!dir.exists()) {
                dir.mkdir(processedComponents, true);
Zack Rusin's avatar
Zack Rusin committed
192
                kdDebug(65432) << "Need to create " << processedComponents << endl;
193
            }
194 195 196 197 198
        }
    }

    QFile srcFile(src);
    if(!srcFile.open(IO_ReadOnly)) {
199
        KMessageBox::error(0, i18n("Could not open %1 for reading.").arg(src));
200 201 202 203 204
        return;
    }

    QFile destFile(dest);
    if(!destFile.open(IO_WriteOnly)) {
205
        KMessageBox::error(0, i18n("Could not open %1 for writing.").arg(dest));
206 207 208 209 210 211
        return;
    }

    destFile.writeBlock(srcFile.readAll());

    if(!srcFile.remove())
212 213 214
        KMessageBox::sorry(0, i18n("Renamed the file, but failed the source "
                                      "file %1. You might want to do so by "
                                      "hand.").arg(src));
215
}
216 217

// vim:ts=4:sw=4:et