///////////////////////////////////////////////////////////////////////////////
// LameXP - Audio Encoder Front-End
-// Copyright (C) 2004-2010 LoRd_MuldeR <MuldeR2@GMX.de>
+// Copyright (C) 2004-2012 LoRd_MuldeR <MuldeR2@GMX.de>
//
// 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
#include "Model_FileList.h"
+#include "Global.h"
+
#include <QFileInfo>
+#include <QDir>
+#include <QFile>
+#include <QTextCodec>
+#include <QTextStream>
+#include <QInputDialog>
+
+#define EXPAND(STR) QString(STR).leftJustified(96, ' ')
+#define CHECK_HDR(STR,NAM) (!(STR).compare((NAM), Qt::CaseInsensitive))
+#define MAKE_KEY(PATH) (QDir::fromNativeSeparators(PATH).toLower())
////////////////////////////////////////////////////////////
// Constructor & Destructor
////////////////////////////////////////////////////////////
FileListModel::FileListModel(void)
- : m_fileIcon(":/icons/page_white_cd.png")
+:
+ m_blockUpdates(false),
+ m_fileIcon(":/icons/page_white_cd.png")
{
- m_fileList.append(AudioFileModel("C:/Music/Buckethead - Crime Slunk Scene/The Fairy and the Devil.ogg", "The Fairy and the Devil"));
}
FileListModel::~FileListModel(void)
switch(index.column())
{
case 0:
- return m_fileList.at(index.row()).fileName();
+ return m_fileStore.value(m_fileList.at(index.row())).fileName();
break;
case 1:
- return m_fileList.at(index.row()).filePath();
+ return QDir::toNativeSeparators(m_fileStore.value(m_fileList.at(index.row())).filePath());
break;
default:
return QVariant();
switch(section)
{
case 0:
- return QVariant("Title");
+ return QVariant(tr("Title"));
break;
case 1:
- return QVariant("Full Path");
+ return QVariant(tr("Full Path"));
break;
default:
return QVariant();
void FileListModel::addFile(const QString &filePath)
{
QFileInfo fileInfo(filePath);
+ const QString key = MAKE_KEY(fileInfo.canonicalFilePath());
+ const bool flag = (!m_blockUpdates);
- for(int i = 0; i < m_fileList.count(); i++)
+ if(!m_fileStore.contains(key))
{
- if(m_fileList.at(i).filePath().compare(fileInfo.absoluteFilePath(), Qt::CaseInsensitive) == 0)
- {
- return;
- }
+ if(flag) beginInsertRows(QModelIndex(), m_fileList.count(), m_fileList.count());
+ m_fileStore.insert(key, AudioFileModel(fileInfo.canonicalFilePath(), fileInfo.baseName()));
+ m_fileList.append(key);
+ if(flag) endInsertRows();
+ emit rowAppended();
}
-
- beginResetModel();
- m_fileList.append(AudioFileModel(fileInfo.absoluteFilePath(), fileInfo.baseName()));
- endResetModel();
}
void FileListModel::addFile(const AudioFileModel &file)
{
- for(int i = 0; i < m_fileList.count(); i++)
+ const QString key = MAKE_KEY(file.filePath());
+ const bool flag = (!m_blockUpdates);
+
+ if(!m_fileStore.contains(key))
{
- if(m_fileList.at(i).filePath().compare(file.filePath(), Qt::CaseInsensitive) == 0)
- {
- return;
- }
+ if(flag) beginInsertRows(QModelIndex(), m_fileList.count(), m_fileList.count());
+ m_fileStore.insert(key, file);
+ m_fileList.append(key);
+ if(flag) endInsertRows();
+ emit rowAppended();
}
-
- beginResetModel();
- m_fileList.append(file);
- endResetModel();
}
bool FileListModel::removeFile(const QModelIndex &index)
if(index.row() >= 0 && index.row() < m_fileList.count())
{
beginResetModel();
+ m_fileStore.remove(m_fileList.at(index.row()));
m_fileList.removeAt(index.row());
endResetModel();
return true;
{
beginResetModel();
m_fileList.clear();
+ m_fileStore.clear();
endResetModel();
}
{
if(index.row() >= 0 && index.row() < m_fileList.count())
{
- return m_fileList.at(index.row());
+ return m_fileStore.value(m_fileList.at(index.row()));
}
else
{
AudioFileModel &FileListModel::operator[] (const QModelIndex &index)
{
- return m_fileList[index.row()];
+ const QString key = m_fileList.at(index.row());
+ return m_fileStore[key];
}
bool FileListModel::setFile(const QModelIndex &index, const AudioFileModel &audioFile)
{
if(index.row() >= 0 && index.row() < m_fileList.count())
{
+ const QString oldKey = m_fileList.at(index.row());
+ const QString newKey = MAKE_KEY(audioFile.filePath());
+
beginResetModel();
- m_fileList.replace(index.row(), audioFile);
+ m_fileList.replace(index.row(), newKey);
+ m_fileStore.remove(oldKey);
+ m_fileStore.insert(newKey, audioFile);
endResetModel();
return true;
}
return false;
}
}
+
+int FileListModel::exportToCsv(const QString &outFile)
+{
+ const int nFiles = m_fileList.count();
+
+ bool havePosition = false, haveTitle = false, haveArtist = false, haveAlbum = false, haveGenre = false, haveYear = false, haveComment = false;
+
+ for(int i = 0; i < nFiles; i++)
+ {
+ AudioFileModel current = m_fileStore.value(m_fileList.at(i));
+
+ if(current.filePosition() > 0) havePosition = true;
+ if(!current.fileName().isEmpty()) haveTitle = true;
+ if(!current.fileArtist().isEmpty()) haveArtist = true;
+ if(!current.fileAlbum().isEmpty()) haveAlbum = true;
+ if(!current.fileGenre().isEmpty()) haveGenre = true;
+ if(current.fileYear() > 0) haveYear = true;
+ if(!current.fileComment().isEmpty()) haveComment = true;
+ }
+
+ if(!(haveTitle || haveArtist || haveAlbum || haveGenre || haveYear || haveComment))
+ {
+ return CsvError_NoTags;
+ }
+
+ QFile file(outFile);
+
+ if(!file.open(QIODevice::WriteOnly))
+ {
+ return CsvError_FileOpen;
+ }
+ else
+ {
+ QStringList line;
+
+ if(havePosition) line << "POSITION";
+ if(haveTitle) line << "TITLE";
+ if(haveArtist) line << "ARTIST";
+ if(haveAlbum) line << "ALBUM";
+ if(haveGenre) line << "GENRE";
+ if(haveYear) line << "YEAR";
+ if(haveComment) line << "COMMENT";
+
+ if(file.write(line.join(";").append("\r\n").toUtf8().prepend("\xef\xbb\xbf")) < 1)
+ {
+ file.close();
+ return CsvError_FileWrite;
+ }
+ }
+
+ for(int i = 0; i < nFiles; i++)
+ {
+ QStringList line;
+ AudioFileModel current = m_fileStore.value(m_fileList.at(i));
+
+ if(havePosition) line << QString::number(current.filePosition());
+ if(haveTitle) line << current.fileName().trimmed();
+ if(haveArtist) line << current.fileArtist().trimmed();
+ if(haveAlbum) line << current.fileAlbum().trimmed();
+ if(haveGenre) line << current.fileGenre().trimmed();
+ if(haveYear) line << QString::number(current.fileYear());
+ if(haveComment) line << current.fileComment().trimmed();
+
+ if(file.write(line.replaceInStrings(";", ",").join(";").append("\r\n").toUtf8()) < 1)
+ {
+ file.close();
+ return CsvError_FileWrite;
+ }
+ }
+
+ file.close();
+ return CsvError_OK;
+}
+
+int FileListModel::importFromCsv(QWidget *parent, const QString &inFile)
+{
+ QFile file(inFile);
+ if(!file.open(QIODevice::ReadOnly))
+ {
+ return CsvError_FileOpen;
+ }
+
+ QTextCodec *codec = NULL;
+ QByteArray bomCheck = file.peek(16);
+
+ if((!bomCheck.isEmpty()) && bomCheck.startsWith("\xef\xbb\xbf"))
+ {
+ codec = QTextCodec::codecForName("UTF-8");
+ }
+ else if((!bomCheck.isEmpty()) && bomCheck.startsWith("\xff\xfe"))
+ {
+ codec = QTextCodec::codecForName("UTF-16LE");
+ }
+ else if((!bomCheck.isEmpty()) && bomCheck.startsWith("\xfe\xff"))
+ {
+ codec = QTextCodec::codecForName("UTF-16BE");
+ }
+ else
+ {
+ const QString systemDefault = tr("(System Default)");
+
+ QStringList codecList;
+ codecList.append(systemDefault);
+ codecList.append(lamexp_available_codepages());
+
+ QInputDialog *input = new QInputDialog(parent);
+ input->setLabelText(EXPAND(tr("Select ANSI Codepage for CSV file:")));
+ input->setOkButtonText(tr("OK"));
+ input->setCancelButtonText(tr("Cancel"));
+ input->setTextEchoMode(QLineEdit::Normal);
+ input->setComboBoxItems(codecList);
+
+ if(input->exec() < 1)
+ {
+ LAMEXP_DELETE(input);
+ return CsvError_Aborted;
+ }
+
+ if(input->textValue().compare(systemDefault, Qt::CaseInsensitive))
+ {
+ qDebug("User-selected codec is: %s", input->textValue().toLatin1().constData());
+ codec = QTextCodec::codecForName(input->textValue().toLatin1().constData());
+ }
+ else
+ {
+ qDebug("Going to use the system's default codec!");
+ codec = QTextCodec::codecForName("System");
+ }
+
+ LAMEXP_DELETE(input);
+ }
+
+ bomCheck.clear();
+
+ //----------------------//
+
+ QTextStream stream(&file);
+ stream.setAutoDetectUnicode(false);
+ stream.setCodec(codec);
+
+ QString headerLine = stream.readLine().simplified();
+
+ while(headerLine.isEmpty())
+ {
+ if(stream.atEnd())
+ {
+ qWarning("The file appears to be empty!");
+ return CsvError_FileRead;
+ }
+ qWarning("Skipping a blank line at beginning of CSV file!");
+ headerLine = stream.readLine().simplified();
+ }
+
+ QStringList header = headerLine.split(";", QString::KeepEmptyParts);
+
+ const int nCols = header.count();
+ const int nFiles = m_fileList.count();
+
+ if(nCols < 1)
+ {
+ qWarning("Header appears to be empty!");
+ return CsvError_FileRead;
+ }
+
+ bool *ignore = new bool[nCols];
+ memset(ignore, 0, sizeof(bool) * nCols);
+
+ for(int i = 0; i < nCols; i++)
+ {
+ if((header[i] = header[i].trimmed()).isEmpty())
+ {
+ ignore[i] = true;
+ }
+ }
+
+ //----------------------//
+
+ for(int i = 0; i < nFiles; i++)
+ {
+ if(stream.atEnd())
+ {
+ LAMEXP_DELETE_ARRAY(ignore);
+ return CsvError_Incomplete;
+ }
+
+ QString line = stream.readLine().simplified();
+
+ if(line.isEmpty())
+ {
+ qWarning("Skipping a blank line in CSV file!");
+ continue;
+ }
+
+ QStringList data = line.split(";", QString::KeepEmptyParts);
+
+ if(data.count() < header.count())
+ {
+ qWarning("Skipping an incomplete line in CSV file!");
+ continue;
+ }
+
+ const QString key = m_fileList[i];
+
+ for(int j = 0; j < nCols; j++)
+ {
+ if(ignore[j])
+ {
+ continue;
+ }
+ else if(CHECK_HDR(header.at(j), "POSITION"))
+ {
+ bool ok = false;
+ unsigned int temp = data.at(j).trimmed().toUInt(&ok);
+ if(ok) m_fileStore[key].setFilePosition(temp);
+ }
+ else if(CHECK_HDR(header.at(j), "TITLE"))
+ {
+ QString temp = data.at(j).trimmed();
+ if(!temp.isEmpty()) m_fileStore[key].setFileName(temp);
+ }
+ else if(CHECK_HDR(header.at(j), "ARTIST"))
+ {
+ QString temp = data.at(j).trimmed();
+ if(!temp.isEmpty()) m_fileStore[key].setFileArtist(temp);
+ }
+ else if(CHECK_HDR(header.at(j), "ALBUM"))
+ {
+ QString temp = data.at(j).trimmed();
+ if(!temp.isEmpty()) m_fileStore[key].setFileAlbum(temp);
+ }
+ else if(CHECK_HDR(header.at(j), "GENRE"))
+ {
+ QString temp = data.at(j).trimmed();
+ if(!temp.isEmpty()) m_fileStore[key].setFileGenre(temp);
+ }
+ else if(CHECK_HDR(header.at(j), "YEAR"))
+ {
+ bool ok = false;
+ unsigned int temp = data.at(j).trimmed().toUInt(&ok);
+ if(ok) m_fileStore[key].setFileYear(temp);
+ }
+ else if(CHECK_HDR(header.at(j), "COMMENT"))
+ {
+ QString temp = data.at(j).trimmed();
+ if(!temp.isEmpty()) m_fileStore[key].setFileComment(temp);
+ }
+ else
+ {
+ qWarning("Unkonw field '%s' will be ignored!", header.at(j).toUtf8().constData());
+ ignore[j] = true;
+
+ if(!checkArray(ignore, false, nCols))
+ {
+ qWarning("No known fields left, aborting!");
+ return CsvError_NoTags;
+ }
+ }
+ }
+ }
+
+ //----------------------//
+
+ LAMEXP_DELETE_ARRAY(ignore);
+ return CsvError_OK;
+}
+
+bool FileListModel::checkArray(const bool *a, const bool val, size_t len)
+{
+ for(size_t i = 0; i < len; i++)
+ {
+ if(a[i] == val) return true;
+ }
+
+ return false;
+}