Files
qt5-legatus-legacy/Lts/Tools/SqlBrowser/Query.cpp
T
2026-08-13 18:33:10 +02:00

475 lines
12 KiB
C++
Executable File

#include "ui_QueryUi.h"
#include "Query.h"
#include "QueryThread.h"
#include "AddLine.h"
#include <QMessageBox>
#include <QFileDialog>
#include <QCheckBox>
#include <QRadioButton>
#include <QDebug>
#ifdef Q_OS_WIN
#define NEWLINE "\r\n"
#else
#define NEWLINE "\n"
#endif
Query::Query(//const QString &queryString,
QueryWorker *queryWorker,
QWidget* parent) // MV[C]
: QWidget(parent)
, m_ui(new Ui::Query)
, m_model(new DataModel())
, m_queryWorker(queryWorker)
, m_addLines(nullptr)
{
m_ui->setupUi(this);
setCustomQuery(); // for safety, disable table sensitive buttons, until table name is set
qRegisterMetaType<QList<QModelIndex>>("QList<QModelIndex>");
connect(m_ui->pbQueryDelete, &QPushButton::clicked, this, &Query::onQueryDelete);
connect(m_ui->pbQueryInsert, &QPushButton::clicked, this, &Query::onQueryInsert);
connect(m_ui->pbQueryUpdate, &QPushButton::clicked, this, &Query::onQueryUpdate);
// NEW ARCH
// Delete Lines
connect(this, &Query::signalDeleteLines,
queryWorker, &QueryWorker::slotDeleteLines); // request => QueryWorker
connect(queryWorker, &QueryWorker::signalDeleteLinesResult,
this, &Query::slotDeleteLinesResult); // reply => this
// Custom Query
connect(this, &Query::signalCustomQuery,
queryWorker, &QueryWorker::slotCustomQuery);
connect(queryWorker, &QueryWorker::signalCustomQueryResult,
this, &Query::slotCustomQueryResult);
// Table Query
connect(this, &Query::signalTableQuery,
queryWorker, &QueryWorker::slotTableQuery);
connect(queryWorker, &QueryWorker::signalTableQueryResult,
this, &Query::slotTableQueryResult);
// Add Lines
connect(this, &Query::signalAddLines,
queryWorker, &QueryWorker::slotAddLines);
connect(queryWorker, &QueryWorker::signalAddLinesResult,
this, &Query::slotAddLinesResult);
// Set Data
connect(m_model, &DataModel::signalSetData, // comes from the model ..
this, &Query::slotSetData);
connect(this, &Query::signalSetData,
queryWorker, &QueryWorker::slotSetData);
connect(queryWorker, &QueryWorker::signalSetDataResult, // .. and comes back to THIS ! (query)
this, &Query::slotSetDataResult);
connect(m_ui->pbQuery, &QPushButton::clicked, this, &Query::slotCustomQuery);
connect(m_ui->pbAddLine, &QPushButton::clicked, this, &Query::slotStartAddLines);
connect(m_ui->pbDeleteLine, &QPushButton::clicked, this, &Query::slotDeleteLine);
connect(m_ui->pbReload, &QPushButton::clicked, this, &Query::slotTableQuery);
connect(m_ui->pbExport, &QPushButton::clicked, this, &Query::slotExportCSV);
m_ui->dataTable->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents); // TODO: qt4
setModel();
}
Query::~Query()
{
delete m_ui;
delete m_model;
}
void Query::slotExportCSV()
{
//qWarning() << "slotExportCSV";
QFileDialog dialog(this); // save dialog
//dialog.setFileMode(QFileDialog::ExistingFile); // single file selection
dialog.setNameFilter(tr("CSV File (*.csv)"));
dialog.setAcceptMode(QFileDialog::AcceptSave);
QStringList fileNames;
if (dialog.exec())
fileNames = dialog.selectedFiles();
if (!fileNames.count())
{
return;
}
QFile file;
file.setFileName(fileNames.at(0));
if (!file.open(QFile::ReadWrite))
{
QMessageBox::critical(this, tr("CSV Export Error"), tr("File '%1' is not accessible!").arg(fileNames.at(0)));
return;
}
// file is open, now save to csv file
int colCount = m_model->columnCount();
// get column names
QStringList columnNames;
columnNames.reserve(colCount);
for (int i = 0; i < colCount; i++)
{
columnNames.append(m_model->headerData(i, Qt::Horizontal).toString());
}
// get rows & row values
QStringList rows;
int rowsCount = m_sortModel.rowCount();
rows.reserve(rowsCount);
QStringList rowData;
for (int row = 0; row < rowsCount; row++)
{
// get row's values
rowData.clear();
for (int col = 0; col < colCount; col++)
{
rowData.append(m_sortModel.data(m_sortModel.index(row, col)).toString());
}
rows.append(rowData.join(QLatin1Char(',')));
}
file.write(columnNames.join(QLatin1Char(',')).toUtf8()); // write header
file.write(NEWLINE);
for (const QString &row : qAsConst(rows))
{
file.write(row.toUtf8());
file.write(NEWLINE);
}
file.close();
QMessageBox::information(this, tr("CSV Export Success"), tr("CSV Export was successful"));
}
void Query::onQueryDelete()
{
m_ui->queryText->setText(tr("DELETE FROM %1 WHERE %2").arg(m_model->getTableName(), m_idInWhere ? QStringLiteral("id = ") : QString()));
}
void Query::onQueryInsert()
{
m_ui->queryText->setText(tr("INSERT INTO %1 (%2) VALUES ()").arg(m_model->getTableName(), checkedColumnsNames().join(QStringLiteral(", "))));
}
void Query::onQueryUpdate()
{
const QStringList sl = checkedColumnsNames();
QStringList sl2;
sl2.reserve(sl.size());
for (const QString &s : sl)
sl2.append(tr("%1 = ").arg(s));
m_ui->queryText->setText(tr("UPDATE %1 SET %2 WHERE %3").arg(m_model->getTableName(), sl2.join(QStringLiteral(", ")), m_idInWhere ? QStringLiteral("id = ") : QString()));
}
void Query::handleError(const QString &errorText)
{
emit signalHideWait();
if (errorText.isEmpty())
return; // nothing to handle
QMessageBox::critical(this, tr("Error while executing query"),
tr("There was an error executing the query:\n%1").arg(errorText));
}
// Delete Lines
void Query::slotDeleteLine() // called from UI
{
int result = QMessageBox::question(this, tr("SqlBrowser"),
tr("Do you really want to delete selected data?"),
QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
if (result == QMessageBox::No)
return;
emit signalShowWait(QStringLiteral("Deleting line(s).."));
const QModelIndexList unmapped = m_ui->dataTable->selectionModel()->selectedRows();
QModelIndexList mapped;
mapped.reserve(unmapped.size());
for (const auto &idx : unmapped)
mapped.append(m_sortModel.mapToSource(idx));
emit signalDeleteLines(this, mapped, m_model); // => QueryWorker
}
void Query::slotDeleteLinesResult(Query* caller, int affectedRowsCount, QString errorText, const QList<QModelIndex> &affectedRowsList) // => from QueryWorker
{
if (caller != this)
return;
if (errorText.isEmpty() && !affectedRowsCount) // no error but no rows affected?
{
errorText = tr("The row to be deleted could not be found - This is probably a type-conversion error! Please report that to Mike");
}
handleError(errorText);
//qWarning() << "Query::slotDeleteLinesResult: errorText = " << errorText << "affectedRowsCount = " << affectedRowsCount;
if (affectedRowsCount == affectedRowsList.count())
{
m_model->deleteLines(affectedRowsList); // tell the model to delete that rows (and tell the view about it)
}
}
// Custom Query
void Query::slotCustomQuery()
{
emit signalShowWait(QStringLiteral("Executing query.."));
emit signalCustomQuery(this, m_ui->queryText->document()->toPlainText(), m_model);
}
void Query::slotCustomQueryResult(Query* caller, const QString &errorText)
{
if (caller != this)
return;
setCustomQuery();
handleError(errorText);
//qWarning() << "Query::slotCustomQueryResult: errorText = " << errorText;
}
// Table Query
void Query::slotTableQuery()
{
emit signalShowWait(QStringLiteral("Getting table data .."));
//qWarning() << "Query: model" << m_model;
emit signalTableQuery(this, m_model);
}
void Query::slotTableQueryResult(Query* caller, const QString &errorText)
{
if (caller != this)
return;
handleError(errorText);
// Don't put it in twice
if (!m_checkboxes.isEmpty())
return;
for(const auto &item : m_model->getHeaderList())
{
QCheckBox *cb = new QCheckBox(item.first, this);
m_ui->checkboxes->addWidget(cb);
m_checkboxes << cb;
if (item.first.compare(QStringLiteral("id"), Qt::CaseInsensitive) != 0)
continue;
// Only for ID:
QRadioButton *rbwhere = new QRadioButton(tr("in WHERE"), this);
m_ui->checkboxes->addWidget(rbwhere);
QRadioButton *rbnormal = new QRadioButton(tr("normal"), this);
m_ui->checkboxes->addWidget(rbnormal);
rbnormal->setEnabled(cb->isChecked());
rbwhere->setEnabled(cb->isChecked());
connect(cb, &QCheckBox::toggled, rbwhere, &QRadioButton::setEnabled);
connect(cb, &QCheckBox::toggled, rbnormal, &QRadioButton::setEnabled);
connect(rbwhere, &QRadioButton::toggled, this, [this, rbwhere, cb] { m_idInWhere = rbwhere->isChecked() && cb->isChecked(); });
connect(cb, &QRadioButton::toggled, this, [this, rbwhere, cb] { m_idInWhere = rbwhere->isChecked() && cb->isChecked(); });
rbwhere->setChecked(true);
}
m_ui->checkboxes->addItem(new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum));
//qWarning() << "Query::slotTableQueryResult: errorText = " << errorText;
}
QStringList Query::checkedColumnsNames() const
{
QStringList sl;
for(const auto &cb : m_checkboxes)
{
if (cb->isChecked())
{
if ((cb->text().compare(QStringLiteral("id"), Qt::CaseInsensitive) == 0) && m_idInWhere)
continue;
sl << cb->text();
}
}
return sl;
}
// Add Lines
void Query::slotStartAddLines()
{
if (!m_addLines)
{
m_addLines = new AddLines(this);
connect(m_addLines, &AddLines::signalAddLines,
this, &Query::slotAddLines);
m_addLines->copyDataFromModel(m_model);
}
m_addLines->show();
}
void Query::slotAddLines(AddModel *model)
{
emit signalShowWait(QStringLiteral("Adding line(s).."));
emit signalAddLines(this, model);
}
void Query::slotAddLinesResult(Query* caller, int , const QString &errorText)
{
if (caller != this)
return;
handleError(errorText);
//qWarning() << "Query::slotAddLinesResult: affectedRows = " << affectedRows << "errorText = " << errorText;
if (errorText.isEmpty()) // no error
{
// if (affectedRows) // only get new data when something changed
// {
// emit signalTableQuery(m_model); // re-get table!
// }
m_addLines->deleteLater(); // done, close add lines window and delete it..
m_addLines = nullptr;
emit signalTableQuery(this, m_model); // get fresh data from db
}
else // error
{
if (!m_addLines) // FIXME: needed! but not logically needed ;) what's going on here?
{
//qWarning() << "Query::slotAddLinesResult: !m_addLines";
}
else
m_addLines->show(); // show again, error!
}
}
// Set Data
void Query::slotSetData(DataModel* model, const QModelIndex &index, const QVariant &val)
{
int result = QMessageBox::question(this, tr("SqlBrowser"),
tr("Do you really want to change the data?"),
QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
if (result == QMessageBox::No)
return;
emit signalShowWait(QStringLiteral("Setting data.."));
emit signalSetData(this, model, index, val);
}
void Query::slotSetDataResult(Query* caller, const QString &errorText)
{
if (caller != this)
return;
handleError(errorText);
//qWarning() << "Query::slotSetDataResult: errorText =" << errorText;
}
void Query::setTable(const QString &tableName)
{
emit signalShowWait(QStringLiteral("Updating value.."));
if (tableName.isEmpty())
{
//qWarning() << "Query::setTable: tableName.isEmpty()";
return;
}
m_model->setTableName(tableName);
setTableQuery();
slotTableQuery();
}
void Query::setModel()
{
m_sortModel.setSourceModel(m_model);
m_ui->dataTable->setModel(/*(QAbstractTableModel*)*/&m_sortModel);
}
void Query::setCustomQuery() // cannot add/delete/reload
{
m_ui->pbAddLine->setEnabled(false);
m_ui->pbDeleteLine->setEnabled(false);
m_ui->pbReload->setEnabled(false);
}
void Query::setTableQuery() // cannot add/delete/reload
{
m_ui->pbAddLine->setEnabled(true);
m_ui->pbDeleteLine->setEnabled(true);
m_ui->pbReload->setEnabled(true);
}