Commit 42ba70ef authored by Laurent Montel's avatar Laurent Montel 😁
Browse files

Add import dialog for csv

parent d978e647
set(kaddressbook_importexport_csv_SRCS
csvimportexportplugin.cpp
csvimportexportplugininterface.cpp
#import/csvimportdialog.cpp
import/dateparser.cpp
import/qcsvmodel.cpp
import/qcsvreader.cpp
import/templateselectiondialog.cpp
)
......
/*
This file is part of KAddressBook.
Copyright (c) 2009 Tobias Koenig <tokoe@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.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef CSVIMPORTDIALOG_H
#define CSVIMPORTDIALOG_H
#include "contactfields.h"
#include <KContacts/Addressee>
#include <QDialog>
#include <QList>
class KComboBox;
class QLineEdit;
class KUrlRequester;
class QButtonGroup;
class QCheckBox;
class QCsvModel;
class QTableView;
class CSVImportDialog : public QDialog
{
Q_OBJECT
public:
explicit CSVImportDialog(QWidget *parent = Q_NULLPTR);
~CSVImportDialog();
KContacts::AddresseeList contacts() const;
private Q_SLOTS:
void setFile(const QString &);
void setUrl(const QUrl &);
void urlChanged(const QString &);
void customDelimiterChanged();
void customDelimiterChanged(const QString &, bool reload = true);
void delimiterClicked(int, bool reload = true);
void textQuoteChanged(const QString &, bool reload = true);
void skipFirstRowChanged(bool, bool reload = true);
void codecChanged(bool reload = true);
void modelFinishedLoading();
void finalizeApplyTemplate();
void slotSaveTemplate();
void slotApplyTemplate();
void slotOk();
private:
void applyTemplate();
void saveTemplate();
QTableView *mTable;
QButtonGroup *mDelimiterGroup;
QLineEdit *mDelimiterEdit;
QLineEdit *mDatePatternEdit;
KComboBox *mComboQuote;
KComboBox *mCodecCombo;
QCheckBox *mSkipFirstRow;
KUrlRequester *mUrlRequester;
QCsvModel *mModel;
void initGUI();
void reloadCodecs();
QTextCodec *currentCodec();
QList<QTextCodec *> mCodecs;
QChar mTextQuote;
QString mDelimiter;
QMap<QString, uint> mTypeMap;
QIODevice *mDevice;
ContactFields::Fields mFieldSelection;
QPushButton *mUser1Button;
QPushButton *mUser2Button;
QPushButton *mOkButton;
};
#endif
/*
This file is part of KAddressBook.
Copyright (c) 2009 Tobias Koenig <tokoe@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.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "dateparser.h"
DateParser::DateParser(const QString &pattern)
: mPattern(pattern)
{
}
DateParser::~DateParser()
{
}
QDateTime DateParser::parse(const QString &dateStr) const
{
int year, month, day, hour, minute, second;
year = month = day = hour = minute = second = 0;
int currPos = 0;
for (int i = 0; i < mPattern.length(); ++i) {
if (mPattern[ i ] == QLatin1Char('y')) { // 19YY
if (currPos + 1 < dateStr.length()) {
year = 1900 + dateStr.midRef(currPos, 2).toInt();
currPos += 2;
} else {
return QDateTime();
}
} else if (mPattern[ i ] == QLatin1Char('Y')) { // YYYY
if (currPos + 3 < dateStr.length()) {
year = dateStr.midRef(currPos, 4).toInt();
currPos += 4;
} else {
return QDateTime();
}
} else if (mPattern[ i ] == QLatin1Char('m')) { // M or MM
if (currPos + 1 < dateStr.length()) {
if (dateStr[ currPos ].isDigit()) {
if (dateStr[ currPos + 1 ].isDigit()) {
month = dateStr.midRef(currPos, 2).toInt();
currPos += 2;
continue;
}
}
}
if (currPos < dateStr.length()) {
if (dateStr[ currPos ].isDigit()) {
month = dateStr.midRef(currPos, 1).toInt();
currPos++;
continue;
}
}
return QDateTime();
} else if (mPattern[ i ] == QLatin1Char('M')) { // 0M or MM
if (currPos + 1 < dateStr.length()) {
month = dateStr.midRef(currPos, 2).toInt();
currPos += 2;
} else {
return QDateTime();
}
} else if (mPattern[ i ] == QLatin1Char('d')) { // D or DD
if (currPos + 1 < dateStr.length()) {
if (dateStr[ currPos ].isDigit()) {
if (dateStr[ currPos + 1 ].isDigit()) {
day = dateStr.midRef(currPos, 2).toInt();
currPos += 2;
continue;
}
}
}
if (currPos < dateStr.length()) {
if (dateStr[ currPos ].isDigit()) {
day = dateStr.midRef(currPos, 1).toInt();
currPos++;
continue;
}
}
return QDateTime();
} else if (mPattern[ i ] == QLatin1Char('D')) { // 0D or DD
if (currPos + 1 < dateStr.length()) {
day = dateStr.midRef(currPos, 2).toInt();
currPos += 2;
} else {
return QDateTime();
}
} else if (mPattern[ i ] == QLatin1Char('H')) { // 0H or HH
if (currPos + 1 < dateStr.length()) {
hour = dateStr.midRef(currPos, 2).toInt();
currPos += 2;
} else {
return QDateTime();
}
} else if (mPattern[ i ] == QLatin1Char('I')) { // 0I or II
if (currPos + 1 < dateStr.length()) {
minute = dateStr.midRef(currPos, 2).toInt();
currPos += 2;
} else {
return QDateTime();
}
} else if (mPattern[ i ] == QLatin1Char('S')) { // 0S or SS
if (currPos + 1 < dateStr.length()) {
second = dateStr.midRef(currPos, 2).toInt();
currPos += 2;
} else {
return QDateTime();
}
} else {
currPos++;
}
}
return QDateTime(QDate(year, month, day), QTime(hour, minute, second));
}
/*
This file is part of KAddressBook.
Copyright (c) 2009 Tobias Koenig <tokoe@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.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef DATEPARSER_H
#define DATEPARSER_H
#include <QDateTime>
#include <QString>
/**
This class parses the datetime out of a given string with the
help of a pattern.
The pattern can contain the following place holders:
y = year (e.g. 82)
Y = year (e.g. 1982)
m = month (e.g. 7, 07 or 12)
M = month (e.g. 07 or 12)
d = day (e.g. 3, 03 or 17)
D = day (e.g. 03 or 17)
H = hour (e.g. 12)
I = minute (e.g. 56)
S = second (e.g. 30)
*/
class DateParser
{
public:
explicit DateParser(const QString &pattern);
~DateParser();
QDateTime parse(const QString &dateStr) const;
private:
QString mPattern;
};
#endif
/*
Copyright (c) 2009 Tobias Koenig <tokoe@kde.org>
This library is free software; you can redistribute it and/or modify it
under the terms of the GNU Library General Public License as published by
the Free Software Foundation; either version 2 of the License, or (at your
option) any later version.
This library is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to the
Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301, USA.
*/
#include "qcsvmodel.h"
#include "qcsvmodel_p.h"
#include "qcsvreader.h"
#include <QMap>
#include <QPair>
#include <QStringList>
#include <QVector>
CsvParser::CsvParser(QObject *parent)
: QThread(parent), mDevice(Q_NULLPTR), mRowCount(0), mColumnCount(0), mCacheCounter(0)
{
mReader = new QCsvReader(this);
}
CsvParser::~CsvParser()
{
delete mReader;
}
void CsvParser::load(QIODevice *device)
{
mDevice = device;
start();
}
void CsvParser::begin()
{
mCacheCounter = 0;
mRowCount = 0;
mColumnCount = 0;
}
void CsvParser::beginLine()
{
mRowCount++;
}
void CsvParser::field(const QString &data, uint row, uint column)
{
const int tmp = qMax(mColumnCount, (int)column + 1);
if (tmp != mColumnCount) {
mColumnCount = tmp;
Q_EMIT columnCountChanged(tmp);
}
Q_EMIT dataChanged(data, row, column);
}
void CsvParser::endLine()
{
mCacheCounter++;
if (mCacheCounter == 50) {
Q_EMIT rowCountChanged(mRowCount);
mCacheCounter = 0;
}
}
void CsvParser::end()
{
Q_EMIT rowCountChanged(mRowCount);
Q_EMIT ended();
}
void CsvParser::error(const QString &)
{
}
void CsvParser::run()
{
if (!mDevice->isOpen()) {
mDevice->open(QIODevice::ReadOnly);
}
mDevice->reset();
mReader->read(mDevice);
}
class QCsvModel::Private
{
public:
Private(QCsvModel *model)
: mParent(model), mParser(Q_NULLPTR),
mDevice(Q_NULLPTR), mRowCount(0), mColumnCount(0)
{
}
void columnCountChanged(int columns);
void rowCountChanged(int rows);
void fieldChanged(const QString &data, int row, int column);
void finishedLoading();
QCsvModel *mParent;
CsvParser *mParser;
QVector<QString> mFieldIdentifiers;
QMap< QPair<int, int>, QString> mFields;
QIODevice *mDevice;
int mRowCount;
int mColumnCount;
};
void QCsvModel::Private::columnCountChanged(int columns)
{
mColumnCount = columns;
mFieldIdentifiers.resize(columns);
mFieldIdentifiers[ columns - 1 ] = QStringLiteral("0");
Q_EMIT mParent->layoutChanged();
}
void QCsvModel::Private::rowCountChanged(int rows)
{
mRowCount = rows;
Q_EMIT mParent->layoutChanged();
}
void QCsvModel::Private::fieldChanged(const QString &data, int row, int column)
{
mFields.insert(QPair<int, int>(row, column), data);
}
void QCsvModel::Private::finishedLoading()
{
Q_EMIT mParent->finishedLoading();
}
QCsvModel::QCsvModel(QObject *parent)
: QAbstractTableModel(parent), d(new Private(this))
{
d->mParser = new CsvParser(this);
connect(d->mParser, SIGNAL(columnCountChanged(int)),
this, SLOT(columnCountChanged(int)), Qt::QueuedConnection);
connect(d->mParser, SIGNAL(rowCountChanged(int)),
this, SLOT(rowCountChanged(int)), Qt::QueuedConnection);
connect(d->mParser, SIGNAL(dataChanged(QString,int,int)),
this, SLOT(fieldChanged(QString,int,int)), Qt::QueuedConnection);
connect(d->mParser, &CsvParser::ended, this, &QCsvModel::finishedLoading);
}
QCsvModel::~QCsvModel()
{
delete d;
}
bool QCsvModel::load(QIODevice *device)
{
d->mDevice = device;
d->mRowCount = 0;
d->mColumnCount = 0;
Q_EMIT layoutChanged();
d->mParser->load(device);
return true;
}
void QCsvModel::setTextQuote(const QChar &textQuote)
{
const bool isRunning = d->mParser->isRunning();
if (isRunning) {
d->mParser->reader()->terminate();
d->mParser->wait();
}
d->mParser->reader()->setTextQuote(textQuote);
if (isRunning) {
load(d->mDevice);
}
}
QChar QCsvModel::textQuote() const
{
return d->mParser->reader()->textQuote();
}
void QCsvModel::setDelimiter(const QChar &delimiter)
{
const bool isRunning = d->mParser->isRunning();
if (isRunning) {
d->mParser->reader()->terminate();
d->mParser->wait();
}
d->mParser->reader()->setDelimiter(delimiter);
if (isRunning) {
load(d->mDevice);
}
}
QChar QCsvModel::delimiter() const
{
return d->mParser->reader()->delimiter();
}
void QCsvModel::setStartRow(uint startRow)
{
const bool isRunning = d->mParser->isRunning();
if (isRunning) {
d->mParser->reader()->terminate();
d->mParser->wait();
}
d->mParser->reader()->setStartRow(startRow);
if (isRunning) {
load(d->mDevice);
}
}
uint QCsvModel::startRow() const
{
return d->mParser->reader()->startRow();
}
void QCsvModel::setTextCodec(QTextCodec *textCodec)
{
const bool isRunning = d->mParser->isRunning();
if (isRunning) {
d->mParser->reader()->terminate();
d->mParser->wait();
}
d->mParser->reader()->setTextCodec(textCodec);
if (isRunning) {
load(d->mDevice);
}
}
QTextCodec *QCsvModel::textCodec() const
{
return d->mParser->reader()->textCodec();
}
int QCsvModel::columnCount(const QModelIndex &parent) const
{
if (!parent.isValid()) {
return d->mColumnCount;
} else {
return 0;
}
}
int QCsvModel::rowCount(const QModelIndex &parent) const
{
if (!parent.isValid()) {
return d->mRowCount + 1; // +1 for the header row
} else {
return 0;
}
}
QVariant QCsvModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid()) {
return QVariant();
}
if (index.row() == 0) {
if (index.column() >= d->mFieldIdentifiers.count()) {
return QVariant();
}
if (role == Qt::DisplayRole || role == Qt::EditRole) {
return d->mFieldIdentifiers.at(index.column());
}