Work on better parsing of input import data.
This commit is contained in:
parent
22a6da1604
commit
8e86655beb
@ -29,6 +29,7 @@ WidgetWithStatus::WidgetWithStatus(QWidget *parent)
|
||||
m_btnStatus = new PlainToolButton(this);
|
||||
m_btnStatus->setFocusPolicy(Qt::NoFocus);
|
||||
|
||||
m_iconProgress = qApp->icons()->fromTheme(QSL("item-sync"));
|
||||
m_iconInformation = qApp->icons()->fromTheme(QSL("dialog-information"));
|
||||
m_iconWarning = qApp->icons()->fromTheme(QSL("dialog-warning"));
|
||||
m_iconError = qApp->icons()->fromTheme(QSL("dialog-error"));
|
||||
@ -52,6 +53,10 @@ void WidgetWithStatus::setStatus(WidgetWithStatus::StatusType status, const QStr
|
||||
m_btnStatus->setIcon(m_iconInformation);
|
||||
break;
|
||||
|
||||
case Progress:
|
||||
m_btnStatus->setIcon(m_iconProgress);
|
||||
break;
|
||||
|
||||
case Warning:
|
||||
m_btnStatus->setIcon(m_iconWarning);
|
||||
break;
|
||||
|
4
src/gui/widgetwithstatus.h
Normal file → Executable file
4
src/gui/widgetwithstatus.h
Normal file → Executable file
@ -33,7 +33,8 @@ class WidgetWithStatus : public QWidget {
|
||||
Information,
|
||||
Warning,
|
||||
Error,
|
||||
Ok
|
||||
Ok,
|
||||
Progress
|
||||
};
|
||||
|
||||
// Constructors and destructors.
|
||||
@ -53,6 +54,7 @@ class WidgetWithStatus : public QWidget {
|
||||
PlainToolButton *m_btnStatus;
|
||||
QHBoxLayout *m_layout;
|
||||
|
||||
QIcon m_iconProgress;
|
||||
QIcon m_iconInformation;
|
||||
QIcon m_iconWarning;
|
||||
QIcon m_iconError;
|
||||
|
@ -36,6 +36,10 @@ FormStandardImportExport::FormStandardImportExport(StandardServiceRoot *service_
|
||||
m_ui->setupUi(this);
|
||||
m_model = new FeedsImportExportModel(m_ui->m_treeFeeds);
|
||||
|
||||
connect(m_model, SIGNAL(parsingStarted()), this, SLOT(onParsingStarted()));
|
||||
connect(m_model, SIGNAL(parsingFinished(int,int,bool)), this, SLOT(onParsingFinished(int,int,bool)));
|
||||
connect(m_model, SIGNAL(parsingProgress(int,int)), this, SLOT(onParsingProgress(int,int)));
|
||||
|
||||
setWindowFlags(Qt::MSWindowsFixedSizeDialogHint | Qt::Dialog | Qt::WindowSystemMenuHint);
|
||||
|
||||
m_ui->m_lblSelectFile->setStatus(WidgetWithStatus::Error, tr("No file is selected."), tr("No file is selected."));
|
||||
@ -100,6 +104,41 @@ void FormStandardImportExport::selectFile() {
|
||||
}
|
||||
}
|
||||
|
||||
void FormStandardImportExport::onParsingStarted() {
|
||||
m_ui->m_lblResult->setStatus(WidgetWithStatus::Progress, tr("Parsing data..."), tr("Parsing data..."));
|
||||
m_ui->m_btnSelectFile->setEnabled(false);
|
||||
m_ui->m_progressBar->setEnabled(true);
|
||||
m_ui->m_progressBar->setValue(0);
|
||||
}
|
||||
|
||||
void FormStandardImportExport::onParsingFinished(int count_failed, int count_succeeded, bool parsing_error) {
|
||||
Q_UNUSED(count_failed)
|
||||
Q_UNUSED(count_succeeded)
|
||||
|
||||
m_ui->m_progressBar->setValue(0);
|
||||
m_ui->m_progressBar->setEnabled(false);
|
||||
m_model->checkAllItems();
|
||||
|
||||
if (!parsing_error) {
|
||||
m_ui->m_lblResult->setStatus(WidgetWithStatus::Ok, tr("Feeds were loaded."), tr("Feeds were loaded."));
|
||||
m_ui->m_groupFeeds->setEnabled(true);
|
||||
m_ui->m_btnSelectFile->setEnabled(true);
|
||||
m_ui->m_treeFeeds->setModel(m_model);
|
||||
m_ui->m_treeFeeds->expandAll();
|
||||
}
|
||||
else {
|
||||
m_ui->m_lblResult->setStatus(WidgetWithStatus::Error, tr("Error, file is not well-formed. Select another file."),
|
||||
tr("Error occurred. File is not well-formed. Select another file."));
|
||||
}
|
||||
|
||||
m_ui->m_buttonBox->button(QDialogButtonBox::Ok)->setEnabled(!parsing_error);
|
||||
}
|
||||
|
||||
void FormStandardImportExport::onParsingProgress(int completed, int total) {
|
||||
m_ui->m_progressBar->setMaximum(total);
|
||||
m_ui->m_progressBar->setValue(completed);
|
||||
}
|
||||
|
||||
void FormStandardImportExport::selectExportFile() {
|
||||
QString filter_opml20 = tr("OPML 2.0 files (*.opml)");
|
||||
QString filter_txt_url_per_line = tr("TXT files (one URL per line) (*.txt)");
|
||||
@ -162,7 +201,6 @@ void FormStandardImportExport::selectImportFile() {
|
||||
|
||||
m_ui->m_lblSelectFile->setStatus(WidgetWithStatus::Ok, QDir::toNativeSeparators(selected_file), tr("File is selected."));
|
||||
parseImportFile(selected_file);
|
||||
m_model->checkAllItems();
|
||||
}
|
||||
}
|
||||
|
||||
@ -179,15 +217,13 @@ void FormStandardImportExport::parseImportFile(const QString &file_name) {
|
||||
return;
|
||||
}
|
||||
|
||||
bool parsing_result;
|
||||
|
||||
switch (m_conversionType) {
|
||||
case OPML20:
|
||||
parsing_result = m_model->importAsOPML20(input_data);
|
||||
m_model->importAsOPML20(input_data);
|
||||
break;
|
||||
|
||||
case TXTUrlPerLine:
|
||||
parsing_result = m_model->importAsTxtURLPerLine(input_data);
|
||||
m_model->importAsTxtURLPerLine(input_data);
|
||||
break;
|
||||
|
||||
// TODO: V celém kódu nově zavést pořádně všude const, i v lokálních metodových proměnných
|
||||
@ -198,19 +234,6 @@ void FormStandardImportExport::parseImportFile(const QString &file_name) {
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
if (parsing_result) {
|
||||
m_ui->m_lblResult->setStatus(WidgetWithStatus::Ok, tr("Feeds were loaded."), tr("Feeds were loaded."));
|
||||
m_ui->m_groupFeeds->setEnabled(true);
|
||||
m_ui->m_treeFeeds->setModel(m_model);
|
||||
m_ui->m_treeFeeds->expandAll();
|
||||
}
|
||||
else {
|
||||
m_ui->m_lblResult->setStatus(WidgetWithStatus::Error, tr("Error, file is not well-formed. Select another file."),
|
||||
tr("Error occurred. File is not well-formed. Select another file."));
|
||||
}
|
||||
|
||||
m_ui->m_buttonBox->button(QDialogButtonBox::Ok)->setEnabled(parsing_result);
|
||||
}
|
||||
|
||||
void FormStandardImportExport::performAction() {
|
||||
|
@ -48,6 +48,10 @@ class FormStandardImportExport : public QDialog {
|
||||
void performAction();
|
||||
void selectFile();
|
||||
|
||||
void onParsingStarted();
|
||||
void onParsingFinished(int count_failed, int count_succeeded, bool parsing_error);
|
||||
void onParsingProgress(int completed, int total);
|
||||
|
||||
private:
|
||||
void selectExportFile();
|
||||
void selectImportFile();
|
||||
|
@ -114,6 +114,16 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0" colspan="2">
|
||||
<widget class="QProgressBar" name="m_progressBar">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>0</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
|
@ -282,7 +282,9 @@ void StandardFeed::fetchMetadataForItself() {
|
||||
}
|
||||
}
|
||||
|
||||
QPair<StandardFeed*,QNetworkReply::NetworkError> StandardFeed::guessFeed(const QString &url, const QString &username, const QString &password) {
|
||||
QPair<StandardFeed*,QNetworkReply::NetworkError> StandardFeed::guessFeed(const QString &url,
|
||||
const QString &username,
|
||||
const QString &password) {
|
||||
QPair<StandardFeed*,QNetworkReply::NetworkError> result; result.first = NULL;
|
||||
|
||||
QByteArray feed_contents;
|
||||
|
@ -145,7 +145,9 @@ class StandardFeed : public Feed {
|
||||
// Returns pointer to guessed feed (if at least partially
|
||||
// guessed) and retrieved error/status code from network layer
|
||||
// or NULL feed.
|
||||
static QPair<StandardFeed*,QNetworkReply::NetworkError> guessFeed(const QString &url, const QString &username, const QString &password);
|
||||
static QPair<StandardFeed*,QNetworkReply::NetworkError> guessFeed(const QString &url,
|
||||
const QString &username = QString(),
|
||||
const QString &password = QString());
|
||||
|
||||
// Converts particular feed type to string.
|
||||
static QString typeToString(Type type);
|
||||
|
@ -154,19 +154,22 @@ bool FeedsImportExportModel::exportToOMPL20(QByteArray &result) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FeedsImportExportModel::importAsOPML20(const QByteArray &data) {
|
||||
void FeedsImportExportModel::importAsOPML20(const QByteArray &data) {
|
||||
emit parsingStarted();
|
||||
|
||||
QDomDocument opml_document;
|
||||
|
||||
if (!opml_document.setContent(data)) {
|
||||
return false;
|
||||
emit parsingFinished(0, 0, true);
|
||||
}
|
||||
|
||||
if (opml_document.documentElement().isNull() || opml_document.documentElement().tagName() != QSL("opml") ||
|
||||
opml_document.documentElement().elementsByTagName(QSL("body")).size() != 1) {
|
||||
// This really is not an OPML file.
|
||||
return false;
|
||||
emit parsingFinished(0, 0, true);
|
||||
}
|
||||
|
||||
int completed = 0, total = 0;
|
||||
StandardServiceRoot *root_item = new StandardServiceRoot();
|
||||
QStack<RootItem*> model_items; model_items.push(root_item);
|
||||
QStack<QDomElement> elements_to_process; elements_to_process.push(opml_document.documentElement().elementsByTagName(QSL("body")).at(0).toElement());
|
||||
@ -175,7 +178,10 @@ bool FeedsImportExportModel::importAsOPML20(const QByteArray &data) {
|
||||
RootItem *active_model_item = model_items.pop();
|
||||
QDomElement active_element = elements_to_process.pop();
|
||||
|
||||
for (int i = 0; i < active_element.childNodes().size(); i++) {
|
||||
int current_count = active_element.childNodes().size();
|
||||
total += current_count;
|
||||
|
||||
for (int i = 0; i < current_count; i++) {
|
||||
QDomNode child = active_element.childNodes().at(i);
|
||||
|
||||
if (child.isElement()) {
|
||||
@ -242,6 +248,8 @@ bool FeedsImportExportModel::importAsOPML20(const QByteArray &data) {
|
||||
elements_to_process.push(child_element);
|
||||
model_items.push(new_category);
|
||||
}
|
||||
|
||||
emit parsingProgress(++completed, total);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -250,8 +258,7 @@ bool FeedsImportExportModel::importAsOPML20(const QByteArray &data) {
|
||||
emit layoutAboutToBeChanged();
|
||||
setRootItem(root_item);
|
||||
emit layoutChanged();
|
||||
|
||||
return true;
|
||||
emit parsingFinished(0, completed, false);
|
||||
}
|
||||
|
||||
bool FeedsImportExportModel::exportToTxtURLPerLine(QByteArray &result) {
|
||||
@ -262,32 +269,49 @@ bool FeedsImportExportModel::exportToTxtURLPerLine(QByteArray &result) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FeedsImportExportModel::importAsTxtURLPerLine(const QByteArray &data) {
|
||||
void FeedsImportExportModel::importAsTxtURLPerLine(const QByteArray &data) {
|
||||
emit parsingStarted();
|
||||
|
||||
int completed = 0, succeded = 0, failed = 0;
|
||||
StandardServiceRoot *root_item = new StandardServiceRoot();
|
||||
QList<QByteArray> urls = data.split('\n');
|
||||
|
||||
foreach (const QByteArray &url, data.split('\n')) {
|
||||
foreach (const QByteArray &url, urls) {
|
||||
if (!url.isEmpty()) {
|
||||
QPair<StandardFeed*,QNetworkReply::NetworkError> guessed = StandardFeed::guessFeed(url);
|
||||
|
||||
StandardFeed *feed = new StandardFeed();
|
||||
if (guessed.second == QNetworkReply::NoError) {
|
||||
guessed.first->setUrl(url);
|
||||
root_item->appendChild(guessed.first);
|
||||
succeded++;
|
||||
}
|
||||
else {
|
||||
StandardFeed *feed = new StandardFeed();
|
||||
|
||||
// TODO: co guessovat ten feed?
|
||||
feed->setUrl(url);
|
||||
feed->setTitle(url);
|
||||
feed->setCreationDate(QDateTime::currentDateTime());
|
||||
feed->setIcon(qApp->icons()->fromTheme(QSL("folder-feed")));
|
||||
feed->setEncoding(DEFAULT_FEED_ENCODING);
|
||||
root_item->appendChild(feed);
|
||||
failed++;
|
||||
}
|
||||
|
||||
feed->setUrl(url);
|
||||
feed->setTitle(url);
|
||||
feed->setCreationDate(QDateTime::currentDateTime());
|
||||
feed->setIcon(qApp->icons()->fromTheme(QSL("folder-feed")));
|
||||
feed->setEncoding(DEFAULT_FEED_ENCODING);
|
||||
|
||||
root_item->appendChild(feed);
|
||||
qApp->processEvents();
|
||||
}
|
||||
else {
|
||||
qWarning("Detected empty URL when parsing input TXT (one URL per line) data.");
|
||||
failed++;
|
||||
}
|
||||
|
||||
emit parsingProgress(++completed, urls.size());
|
||||
}
|
||||
|
||||
// Now, XML is processed and we have result in form of pointer item structure.
|
||||
emit layoutAboutToBeChanged();
|
||||
setRootItem(root_item);
|
||||
emit layoutChanged();
|
||||
|
||||
return true;
|
||||
emit parsingFinished(failed, succeded, false);
|
||||
}
|
||||
|
||||
FeedsImportExportModel::Mode FeedsImportExportModel::mode() const {
|
||||
|
@ -60,12 +60,12 @@ class FeedsImportExportModel : public QAbstractItemModel {
|
||||
// Exports to OPML 2.0
|
||||
// NOTE: http://dev.opml.org/spec2.html
|
||||
bool exportToOMPL20(QByteArray &result);
|
||||
bool importAsOPML20(const QByteArray &data);
|
||||
void importAsOPML20(const QByteArray &data);
|
||||
|
||||
// Exports to plain text format
|
||||
// where there is one feed URL per line.
|
||||
bool exportToTxtURLPerLine(QByteArray &result);
|
||||
bool importAsTxtURLPerLine(const QByteArray &data);
|
||||
void importAsTxtURLPerLine(const QByteArray &data);
|
||||
|
||||
Mode mode() const;
|
||||
void setMode(const Mode &mode);
|
||||
@ -74,8 +74,15 @@ class FeedsImportExportModel : public QAbstractItemModel {
|
||||
void checkAllItems();
|
||||
void uncheckAllItems();
|
||||
|
||||
signals:
|
||||
// These signals are emitted when user selects some data
|
||||
// to be imported/parsed into the model.
|
||||
void parsingStarted();
|
||||
void parsingProgress(int completed, int total);
|
||||
void parsingFinished(int count_failed, int count_succeeded, bool parsing_error);
|
||||
|
||||
private:
|
||||
QHash<RootItem*, Qt::CheckState> m_checkStates;
|
||||
QHash<RootItem*,Qt::CheckState> m_checkStates;
|
||||
RootItem *m_rootItem;
|
||||
|
||||
bool m_recursiveChange;
|
||||
|
Loading…
x
Reference in New Issue
Block a user