rssguard/src/librssguard/services/standard/gui/formstandardimportexport.cpp
2020-07-01 10:13:44 +02:00

296 lines
11 KiB
C++

// For license of this file, see <project-root-folder>/LICENSE.md.
#include "services/standard/gui/formstandardimportexport.h"
#include "core/feedsmodel.h"
#include "exceptions/ioexception.h"
#include "gui/dialogs/formmain.h"
#include "gui/feedmessageviewer.h"
#include "gui/feedsview.h"
#include "gui/messagebox.h"
#include "miscellaneous/application.h"
#include "services/abstract/category.h"
#include "services/standard/standardfeedsimportexportmodel.h"
#include "services/standard/standardserviceroot.h"
#include <QFileDialog>
#include <QTextStream>
FormStandardImportExport::FormStandardImportExport(StandardServiceRoot* service_root, QWidget* parent)
: QDialog(parent), m_ui(new Ui::FormStandardImportExport), m_serviceRoot(service_root) {
m_ui->setupUi(this);
m_model = new FeedsImportExportModel(m_ui->m_treeFeeds);
connect(m_model, &FeedsImportExportModel::parsingStarted, this, &FormStandardImportExport::onParsingStarted);
connect(m_model, &FeedsImportExportModel::parsingFinished, this, &FormStandardImportExport::onParsingFinished);
connect(m_model, &FeedsImportExportModel::parsingProgress, this, &FormStandardImportExport::onParsingProgress);
setWindowFlags(Qt::MSWindowsFixedSizeDialogHint | Qt::Dialog | Qt::WindowSystemMenuHint);
m_ui->m_lblSelectFile->setStatus(WidgetWithStatus::StatusType::Error, tr("No file is selected."), tr("No file is selected."));
m_ui->m_buttonBox->button(QDialogButtonBox::Ok)->disconnect();
m_ui->m_lblResult->setStatus(WidgetWithStatus::StatusType::Warning, tr("No operation executed yet."), tr("No operation executed yet."));
connect(m_ui->m_buttonBox->button(QDialogButtonBox::Ok), &QPushButton::clicked, this, &FormStandardImportExport::performAction);
connect(m_ui->m_btnSelectFile, &QPushButton::clicked, this, &FormStandardImportExport::selectFile);
connect(m_ui->m_btnCheckAllItems, &QPushButton::clicked, m_model, &FeedsImportExportModel::checkAllItems);
connect(m_ui->m_btnUncheckAllItems, &QPushButton::clicked, m_model, &FeedsImportExportModel::uncheckAllItems);
}
FormStandardImportExport::~FormStandardImportExport() = default;
void FormStandardImportExport::setMode(const FeedsImportExportModel::Mode& mode) {
m_model->setMode(mode);
m_ui->m_progressBar->setVisible(false);
switch (mode) {
case FeedsImportExportModel::Mode::Export: {
m_model->setRootItem(m_serviceRoot);
m_model->checkAllItems();
m_ui->m_treeFeeds->setModel(m_model);
m_ui->m_treeFeeds->expandAll();
m_ui->m_cmbRootNode->setVisible(false);
m_ui->m_lblRootNode->setVisible(false);
m_ui->m_groupFile->setTitle(tr("Destination file"));
m_ui->m_groupFeeds->setTitle(tr("Source feeds && categories"));
m_ui->m_buttonBox->button(QDialogButtonBox::StandardButton::Ok)->setText(tr("&Export to file"));
setWindowTitle(tr("Export feeds"));
setWindowIcon(qApp->icons()->fromTheme(QSL("document-export")));
break;
}
case FeedsImportExportModel::Mode::Import: {
m_ui->m_groupFile->setTitle(tr("Source file"));
m_ui->m_groupFeeds->setTitle(tr("Target feeds && categories"));
m_ui->m_groupFeeds->setDisabled(true);
m_ui->m_buttonBox->button(QDialogButtonBox::StandardButton::Ok)->setText(tr("&Import from file"));
// Load categories.
loadCategories(m_serviceRoot->getSubTreeCategories(), m_serviceRoot);
setWindowTitle(tr("Import feeds"));
setWindowIcon(qApp->icons()->fromTheme(QSL("document-import")));
break;
}
default:
break;
}
m_ui->m_buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
}
void FormStandardImportExport::selectFile() {
switch (m_model->mode()) {
case FeedsImportExportModel::Mode::Import:
selectImportFile();
break;
case FeedsImportExportModel::Mode::Export: {
selectExportFile();
break;
}
default:
break;
}
}
void FormStandardImportExport::onParsingStarted() {
m_ui->m_lblResult->setStatus(WidgetWithStatus::StatusType::Progress, tr("Parsing data..."), tr("Parsing data..."));
m_ui->m_btnSelectFile->setEnabled(false);
m_ui->m_groupFeeds->setEnabled(false);
m_ui->m_progressBar->setValue(0);
m_ui->m_progressBar->setVisible(true);
m_ui->m_buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
}
void FormStandardImportExport::onParsingFinished(int count_failed, int count_succeeded, bool parsing_error) {
Q_UNUSED(count_failed)
Q_UNUSED(count_succeeded)
m_ui->m_progressBar->setVisible(false);
m_ui->m_progressBar->setValue(0);
m_model->checkAllItems();
if (!parsing_error) {
m_ui->m_lblResult->setStatus(WidgetWithStatus::StatusType::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_groupFeeds->setEnabled(false);
m_ui->m_lblResult->setStatus(WidgetWithStatus::StatusType::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(true);
}
void FormStandardImportExport::onParsingProgress(int completed, int total) {
m_ui->m_progressBar->setMaximum(total);
m_ui->m_progressBar->setValue(completed);
}
void FormStandardImportExport::selectExportFile() {
const QString filter_opml20 = tr("OPML 2.0 files (*.opml)");
const QString filter_txt_url_per_line = tr("TXT files [one URL per line] (*.txt)");
QString filter;
QString selected_filter;
// Add more filters here.
filter += filter_opml20;
filter += ";;";
filter += filter_txt_url_per_line;
QString selected_file = QFileDialog::getSaveFileName(this, tr("Select file for feeds export"),
qApp->homeFolder(), filter, &selected_filter);
if (!selected_file.isEmpty()) {
if (selected_filter == filter_opml20) {
m_conversionType = OPML20;
if (!selected_file.endsWith(QL1S(".opml"))) {
selected_file += QL1S(".opml");
}
}
else if (selected_filter == filter_txt_url_per_line) {
m_conversionType = TXTUrlPerLine;
if (!selected_file.endsWith(QL1S(".txt"))) {
selected_file += QL1S(".txt");
}
}
m_ui->m_lblSelectFile->setStatus(WidgetWithStatus::StatusType::Ok, QDir::toNativeSeparators(selected_file), tr("File is selected."));
}
m_ui->m_buttonBox->button(QDialogButtonBox::Ok)->setEnabled(m_ui->m_lblSelectFile->status() == WidgetWithStatus::StatusType::Ok);
}
void FormStandardImportExport::selectImportFile() {
const QString filter_opml20 = tr("OPML 2.0 files (*.opml)");
const QString filter_txt_url_per_line = tr("TXT files [one URL per line] (*.txt)");
QString filter;
QString selected_filter;
// Add more filters here.
filter += filter_opml20;
filter += ";;";
filter += filter_txt_url_per_line;
const QString selected_file = QFileDialog::getOpenFileName(this, tr("Select file for feeds import"), qApp->homeFolder(),
filter, &selected_filter);
if (!selected_file.isEmpty()) {
if (selected_filter == filter_opml20) {
m_conversionType = OPML20;
}
else if (selected_filter == filter_txt_url_per_line) {
m_conversionType = TXTUrlPerLine;
}
m_ui->m_lblSelectFile->setStatus(WidgetWithStatus::StatusType::Ok, QDir::toNativeSeparators(selected_file), tr("File is selected."));
QMessageBox::StandardButton answer = MessageBox::show(this,
QMessageBox::Warning,
tr("Get online metadata"),
tr("Metadata for your feeds can be fetched online. Note that the action "
"could take several minutes, depending on number of feeds."),
tr("Do you want to fetch feed metadata online?"),
QString(),
QMessageBox::Yes | QMessageBox::No,
QMessageBox::Yes);
parseImportFile(selected_file, answer == QMessageBox::Yes);
}
}
void FormStandardImportExport::parseImportFile(const QString& file_name, bool fetch_metadata_online) {
QFile input_file(file_name);
QByteArray input_data;
if (input_file.open(QIODevice::Text | QIODevice::Unbuffered | QIODevice::ReadOnly)) {
input_data = input_file.readAll();
input_file.close();
}
else {
m_ui->m_lblResult->setStatus(WidgetWithStatus::StatusType::Error, tr("Cannot open source file."), tr("Cannot open source file."));
return;
}
switch (m_conversionType) {
case OPML20:
m_model->importAsOPML20(input_data, fetch_metadata_online);
break;
case TXTUrlPerLine:
m_model->importAsTxtURLPerLine(input_data, fetch_metadata_online);
break;
default:
return;
}
}
void FormStandardImportExport::performAction() {
switch (m_model->mode()) {
case FeedsImportExportModel::Mode::Import:
importFeeds();
break;
case FeedsImportExportModel::Mode::Export:
exportFeeds();
break;
default:
break;
}
}
void FormStandardImportExport::exportFeeds() {
QByteArray result_data;
bool result_export = false;
switch (m_conversionType) {
case OPML20:
result_export = m_model->exportToOMPL20(result_data);
break;
case TXTUrlPerLine:
result_export = m_model->exportToTxtURLPerLine(result_data);
break;
default:
break;
}
if (result_export) {
try {
IOFactory::writeFile(m_ui->m_lblSelectFile->label()->text(), result_data);
m_ui->m_lblResult->setStatus(WidgetWithStatus::StatusType::Ok, tr("Feeds were exported successfully."),
tr("Feeds were exported successfully."));
}
catch (IOException& ex) {
m_ui->m_lblResult->setStatus(WidgetWithStatus::StatusType::Error, tr("Cannot write into destination file: '%1'."), ex.message());
}
}
else {
m_ui->m_lblResult->setStatus(WidgetWithStatus::StatusType::Error, tr("Critical error occurred."), tr("Critical error occurred."));
}
}
void FormStandardImportExport::importFeeds() {
QString output_message;
RootItem* parent = static_cast<RootItem*>(m_ui->m_cmbRootNode->itemData(m_ui->m_cmbRootNode->currentIndex()).value<void*>());
if (m_serviceRoot->mergeImportExportModel(m_model, parent, output_message)) {
m_serviceRoot->requestItemExpand(parent->getSubTree(), true);
m_ui->m_lblResult->setStatus(WidgetWithStatus::StatusType::Ok, output_message, output_message);
}
else {
m_ui->m_lblResult->setStatus(WidgetWithStatus::StatusType::Error, output_message, output_message);
}
}
void FormStandardImportExport::loadCategories(const QList<Category*>& categories, RootItem* root_item) {
m_ui->m_cmbRootNode->addItem(root_item->icon(), root_item->title(), QVariant::fromValue((void*) root_item));
for (Category* category : categories) {
m_ui->m_cmbRootNode->addItem(category->icon(), category->title(), QVariant::fromValue((void*) category));
}
}