This repository has been archived on 2020-09-07. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
libs/qad/sql_table/sql_table_widget.cpp

1082 lines
36 KiB
C++

#include "sql_table_widget.h"
#include "ui_sql_table_widget.h"
#include <QFile>
#include <QTextStream>
#include <QFileInfo>
#include <QItemDelegate>
#include <QApplication>
#include "qad_types.h"
QString SQLTableWidget::_dir;
ColumnProperties::ColumnProperties(const QString & table_, const QString & type_, const QString & name_, const QString & def_, bool auto_, bool prim_, bool option_) {
visible = true;
table = table_;
name = name_;
def = def_;
auto_increment = auto_;
primary = prim_;
optional = option_;
int bs = type_.indexOf("("), bf = type_.indexOf(")");
size = type_.mid(bs + 1, bf - bs - 1).toInt();
key_column = 0;
type = typeFromString(type_.toLower());
is_text = (type == ColumnProperties::Chars || type == ColumnProperties::Text);
}
ColumnProperties::DataType ColumnProperties::typeFromString(const QString & n) {
if (n.indexOf("datetime") >= 0 || n.indexOf("timestamp") >= 0) return ColumnProperties::DateTime;
if (n.indexOf("date") >= 0) return ColumnProperties::Date;
if (n.indexOf("time") >= 0) return ColumnProperties::Time;
if (n.indexOf("int") >= 0) return ColumnProperties::Int;
if (n.indexOf("char") >= 0) return ColumnProperties::Chars;
if (n.indexOf("dec") >= 0 || n.indexOf("numeric") >= 0 || n.indexOf("real") >= 0 ||
n.indexOf("float") >= 0 || n.indexOf("double") >= 0) return ColumnProperties::Float;
if (n.indexOf("text") >= 0) return ColumnProperties::Text;
if (n.indexOf("blob") >= 0) return ColumnProperties::Binary;
return ColumnProperties::Unknown;
}
QVariant SQLQueryModel::data(const QModelIndex & item, int role) const {
QVariant var = QSqlQueryModel::data(item, role);
//QVariant::Type t = var.type();
/*if (t == QVariant::Date) return var.toDate().toString("dd.MM.yyyy");
if (t == QVariant::Time) return var.toTime().toString("hh:mm:ss");
if (t == QVariant::DateTime) return var.toDateTime().toString("dd.MM.yyyy hh:mm:ss");*/
return var;
}
bool SQLQueryModel::setData(const QModelIndex& index, const QVariant & value, int role) {
if (value.isValid() && role == Qt::EditRole) {
QSqlQuery q(QSqlDatabase::database(conn_name));
int col = mapColumn(index.column());
ColumnProperties::DataType t = column_props[col].type;
bool quotes = (t == ColumnProperties::Chars || t == ColumnProperties::Text ||
t == ColumnProperties::Date || t == ColumnProperties::Time ||
t == ColumnProperties::ColumnProperties::DateTime);
QString val = value.toString();
if (t == ColumnProperties::Date) val = value.toDate().toString("yyyy-MM-dd");
if (t == ColumnProperties::Time) val = value.toTime().toString("hh:mm:ss");
if (t == ColumnProperties::DateTime) val = value.toDateTime().toString("yyyy-MM-dd hh:mm:ss");
if (!q.exec("UPDATE " + table_ + " SET " + column_props[col].name + "=" +
(quotes ? "'" : "") + val + (quotes ? "'" : "") +
" WHERE " + column_props[0].name + "=" + record(index.row()).value(0).toString() + ";")) {
qDebug() << "[UPDATE]" << q.lastQuery() << "Error:" << q.lastError();
return false;
}
//qDebug() << "[UPDATE]" << q.lastQuery();
dataChanged(index, index);
updateTable(true);
emit tableChanged();
return true;
}
return false;
}
SQLUniEdit::SQLUniEdit(const ColumnProperties & prop, const QString & conn_name, QWidget * parent): QWidget(parent), prop_(prop), connection_name(conn_name) {
wtext = 0; wint = 0; wfloat = 0; wdate = 0; wtime = 0; wdatetime = 0; wrelation = 0;
setProp(prop_);
w_ << (QWidget **)&wtext << (QWidget **)&wint << (QWidget **)&wfloat << (QWidget **)&wdate << (QWidget **)&wtime << (QWidget **)&wdatetime << (QWidget **)&wrelation;
}
void SQLUniEdit::setProp(const ColumnProperties & p) {
for (int i = 0; i < w_.size(); ++i) deleteW(*w_[i]);
wtext = 0; wint = 0; wfloat = 0; wdate = 0; wtime = 0; wdatetime = 0; wrelation = 0;
prop_ = p;
if (prop_.isRelation()) {
wrelation = new QComboBox(this);
wrelation->installEventFilter(this);
for (int i = 0; i < prop_.relation_list.size(); ++i)
wrelation->addItem(prop_.relation_list[i].second, prop_.relation_list[i].first);
connect(wrelation, SIGNAL(currentIndexChanged(int)), this, SLOT(value_int(int)));
} else {
ColumnProperties::DataType t = prop_.getType();
switch (t) {
case ColumnProperties::Int: wint = new QSpinBox(this); wint->setRange(-0x7FFFFFFF, 0x7FFFFFFF); connect(wint, SIGNAL(valueChanged(int)), this, SLOT(value_int(int))); break;
case ColumnProperties::Float: wfloat = new QDoubleSpinBox(this); wfloat->setRange(-1E+300, 1E+300); wfloat->setDecimals(2); connect(wfloat, SIGNAL(valueChanged(double)), this, SLOT(value_float(double))); break;
case ColumnProperties::Date: wdate = new QDateEdit(this); wdate->setDisplayFormat("dd.MM.yyyy"); wdate->setCalendarPopup(true); wdate->setDate(QDate::currentDate()); connect(wdate, SIGNAL(dateChanged(QDate)), this, SLOT(value_date(QDate))); break;
case ColumnProperties::Time: wtime = new QTimeEdit(this); wtime->setDisplayFormat("hh:mm:ss"); wtime->setTime(QTime::currentTime()); connect(wtime, SIGNAL(timeChanged(QTime)), this, SLOT(value_time(QTime))); break;
case ColumnProperties::DateTime: wdatetime = new QDateTimeEdit(this); wdatetime->setDisplayFormat("dd.MM.yyyy hh:mm:ss"); wdatetime->setCalendarPopup(true); wdatetime->setDateTime(QDateTime::currentDateTime()); connect(wdatetime, SIGNAL(dateTimeChanged(QDateTime)), this, SLOT(value_datetime(QDateTime))); break;
case ColumnProperties::Chars:
case ColumnProperties::Text:
case ColumnProperties::Unknown:
default: wtext = new QLineEdit(this); connect(wtext, SIGNAL(textChanged(QString)), this, SLOT(value_text(QString))); break;
}
}
for (int i = 0; i < w_.size(); ++i)
if (*w_[i]) (*w_[i])->setHidden(false);
resizeEvent(0);
}
QString SQLUniEdit::value(bool for_insert) {
QString ret;
ColumnProperties::DataType t = (for_insert ? prop_.type : prop_.getType());
bool quotes = (t == ColumnProperties::Chars || t == ColumnProperties::Text ||
t == ColumnProperties::Date || t == ColumnProperties::Time ||
t == ColumnProperties::ColumnProperties::DateTime);
if (quotes) ret = "'";
if (prop_.isRelation())
if (for_insert)
ret += wrelation->itemData(wrelation->currentIndex()).toString();
else
ret += wrelation->currentText();
else {
if (wint != 0) ret += QString::number(wint->value());
else {
if (wfloat != 0) ret += QString::number(wfloat->value());
else {
if (wdate != 0) ret += wdate->date().toString("yyyy-MM-dd");
else {
if (wtime != 0) ret += wtime->time().toString("hh:mm:ss");
else {
if (wdatetime != 0) ret += wdatetime->dateTime().toString("yyyy-MM-dd hh:mm:ss");
else {
ret += wtext->text();
}
}
}
}
}
}
if (quotes) ret += "'";
if (ret == "''") ret.clear();
return ret;
}
bool SQLUniEdit::eventFilter(QObject * o, QEvent * e) {
if (e->type() == QEvent::MouseButtonPress)
updateRelation();
return QWidget::eventFilter(o, e);
}
void SQLUniEdit::setValue(const QVariant & value) {
if (prop_.isRelation()) return;
if (wint != 0) wint->setValue(value.toInt());
else {
if (wfloat != 0) wfloat->setValue(value.toDouble());
else {
if (wdate != 0) wdate->setDate(value.toDate());
else {
if (wtime != 0) wtime->setTime(value.toTime());
else {
if (wdatetime != 0) wdatetime->setDateTime(value.toDateTime());
else {
wtext->setText(value.toString());
}
}
}
}
}
}
void SQLUniEdit::updateRelation() {
prop_.relation_list = SQLTableWidget::getColumnValues(prop_.relation_table, prop_.relation_key, prop_.relation_column, connection_name);
int pi = wrelation->currentIndex();
wrelation->blockSignals(true);
wrelation->clear();
for (int i = 0; i < prop_.relation_list.size(); ++i)
wrelation->addItem(prop_.relation_list[i].second, prop_.relation_list[i].first);
wrelation->setCurrentIndex(pi);
wrelation->blockSignals(false);
}
SQLNewEdit::SQLNewEdit(const ColumnProperties & prop, const QString & conn_name, QWidget * parent): QWidget(parent), prop_(prop) {
line = new SQLUniEdit(prop, conn_name);
line->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
check = new QCheckBox();
check->setChecked(true);
check->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred);
connect(check, SIGNAL(toggled(bool)), line, SLOT(setEnabled(bool)));
QLayout * lay = new QHBoxLayout();
lay->addWidget(check);
lay->addWidget(line);
lay->setContentsMargins(0, 0, 0, 0);
lay->setSpacing(0);
setLayout(lay);
setProp(prop);
check->setChecked(!prop.primary);
}
void SQLNewEdit::setProp(const ColumnProperties & p) {
prop_ = p;
line->setProp(p);
check->setEnabled(p.optional);
if (!check->isEnabled()) check->setChecked(true);
}
SQLFilterEdit::SQLFilterEdit(const ColumnProperties & prop, const QString & conn_name, QWidget * parent): QWidget(parent), prop_(prop) {
line = new SQLUniEdit(prop, conn_name);
line->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
combo = new QComboBox();
combo->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred);
QStringList sl;
sl << " " << "=" << QString::fromUtf8("") << QString::fromUtf8("") << ">" << QString::fromUtf8("") << "<" << QString::fromUtf8("");
combo->addItems(sl);
combo->setCurrentIndex(0);
connect(line, SIGNAL(valueChanged(QVariant)), this, SIGNAL(filterChanged()));
connect(line, SIGNAL(valueChanged(QVariant)), this, SLOT(value_changed()));
connect(combo, SIGNAL(currentIndexChanged(int)), this, SIGNAL(filterChanged()));
QLayout * lay = new QHBoxLayout();
lay->addWidget(combo);
lay->addWidget(line);
lay->setContentsMargins(0, 0, 0, 0);
lay->setSpacing(2);
setLayout(lay);
setProp(prop);
}
QString SQLFilterEdit::filter() const {
QString ret;
if (combo->currentIndex() == 0) return ret;
ret = prop_.fullName();
switch (combo->currentIndex()) {
case 1: ret.append("="); break;
case 2: ret.append("="); break;
case 3: ret.append("!="); break;
case 4: ret.append(">"); break;
case 5: ret.append(">="); break;
case 6: ret.append("<"); break;
case 7: ret.append("<="); break;
};
QString val = line->value();
if (prop_.is_text && combo->currentIndex() == 2) {
ret.chop(1);
ret.append(" LIKE '%");
ret.append(val.mid(1, val.length() - 2));
ret.append("%'");
} else
ret.append(val);
return ret;
}
void SQLFilterEdit::clear() {
line->setValue(QVariant());
combo->setCurrentIndex(0);
}
SQLItemDelegate::SQLItemDelegate(QList<ColumnProperties> & cp, QVector<int> & ci, bool & ro, const QString & conn_name, QObject * parent):
QStyledItemDelegate(parent), column_props(cp), column_indexes(ci), read_only(ro), connection_name(conn_name) {;}
QWidget * SQLItemDelegate::createEditor(QWidget * parent, const QStyleOptionViewItem & option, const QModelIndex & index) const {
if (read_only) return 0;
ColumnProperties & cp(column_props[mapColumn(index.column())]);
if (cp.isRelation()) {
cp.relation_list = SQLTableWidget::getColumnValues(cp.relation_table, cp.relation_key, cp.relation_column, connection_name);
QComboBox * w = new QComboBox(parent);
w->setGeometry(option.rect);
for (int i = 0; i < cp.relation_list.size(); ++i) {
w->addItem(cp.relation_list[i].second, cp.relation_list[i].first);
if (index.data().toString() == cp.relation_list[i].second)
w->setCurrentIndex(i);
}
return w;
} else {
QWidget * w = QStyledItemDelegate::createEditor(parent, option, index);
if (qobject_cast<QTimeEdit * >(w)) {qobject_cast<QTimeEdit * >(w)->setDisplayFormat("hh:mm:ss");}
else if (qobject_cast<QDateEdit * >(w)) {qobject_cast<QDateEdit * >(w)->setCalendarPopup(true); qobject_cast<QDateEdit * >(w)->setDisplayFormat("dd.MM.yyyy");}
else if (qobject_cast<QDateTimeEdit * >(w)) {qobject_cast<QDateTimeEdit * >(w)->setCalendarPopup(true); qobject_cast<QDateTimeEdit * >(w)->setDisplayFormat("dd.MM.yyyy hh:mm:ss");}
return w;
}
return 0;
}
QString SQLItemDelegate::displayText(const QVariant & value, const QLocale & locale) const {
QVariant::Type t = value.type();
if (t == QVariant::Date) return value.toDate().toString("dd.MM.yyyy");
if (t == QVariant::Time) return value.toTime().toString("hh:mm:ss");
if (t == QVariant::DateTime) return value.toDateTime().toString("dd.MM.yyyy hh:mm:ss");
return QStyledItemDelegate::displayText(value, locale);
}
void SQLItemDelegate::setModelData(QWidget * editor, QAbstractItemModel * model, const QModelIndex & index) const {
if (qobject_cast<QComboBox * >(editor)) {
model->setData(index, qobject_cast<QComboBox * >(editor)->itemData(qobject_cast<QComboBox * >(editor)->currentIndex()));
return;
}
QStyledItemDelegate::setModelData(editor, model, index);
}
SQLTableWidget::SQLTableWidget(QWidget * parent): QWidget(parent), filters_group(this) {
ui = new Ui::SQLTableWidget();
ui->setupUi(this);
model = 0;
timer = 0;
connection_name = QSqlDatabase::defaultConnection;
filters_active = first_update = true;
table_opened = read_only = false;
ui->labelNew->setFixedSize(preferredIconSize(1.2, this));
ui->labelFilter->setFixedSize(preferredIconSize(1.2, this));
ui->view->installEventFilter(this);
ui->view->viewport()->installEventFilter(this);
ui->view->horizontalHeader()->setSortIndicator(0, Qt::AscendingOrder);
ui->view->horizontalHeader()->viewport()->installEventFilter(this);
ui->view->setItemDelegate(new SQLItemDelegate(column_props, column_indexes, read_only, connection_name));
ui->view->horizontalHeader()->setItemDelegate(new QItemDelegate());
ui->view->verticalHeader()->setDefaultSectionSize(fontHeight() * 1.5);
//qDebug() << view->horizontalHeader()->itemDelegate();
//qDebug() << fontMetrics().elidedText(, Qt::ElideNone, 30, Qt::TextWordWrap);
setTableVisible(false);
connect(ui->view->horizontalHeader(), SIGNAL(sectionResized(int, int, int)), this, SLOT(header_sectionResized(int, int, int)));
connect(ui->view->horizontalHeader(), SIGNAL(sortIndicatorChanged(int, Qt::SortOrder)), this, SLOT(header_sortIndicatorChanged(int, Qt::SortOrder)));
connect(ui->view->horizontalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(scrolled(int)));
//act_add.setText(tr("Add ..."));
//act_del.setText(tr("Remove selected"));
action_del = new QAction(QIcon(":/icons/edit-delete.png"), tr("Remove selected"), this);
connect(action_del, SIGNAL(triggered(bool)), this, SLOT(del_triggered()));
popup_menu.addAction(action_del);
ui->actionFilter_AND->setChecked(true);
filters_group.addAction(ui->actionFilter_AND);
filters_group.addAction(ui->actionFilter_OR);
connect(&filters_group, SIGNAL(triggered(QAction * )), this, SLOT(updateTable()));
popup_filter.addAction(ui->actionFiltersClear);
popup_filter.addSeparator();
popup_filter.addAction(ui->actionFilter_AND);
popup_filter.addAction(ui->actionFilter_OR);
ui->buttonFilterConfig->setMenu(&popup_filter);
//connect(&act_add, SIGNAL(triggered(bool)), this, SLOT(add_triggered()));
//connect(&act_del, SIGNAL(triggered(bool)), this, SLOT(del_triggered()));
}
SQLTableWidget::~SQLTableWidget() {
stopTimer();
if (model != 0) delete model;
}
bool SQLTableWidget::eventFilter(QObject * o, QEvent * e) {
if (o == ui->view) {
if (e->type() == QEvent::KeyPress) {
QKeyEvent * ke = (QKeyEvent * )e;
if (ke->key() == Qt::Key_Home) {ui->view->selectRow(0); return true;}
if (ke->key() == Qt::Key_End) {ui->view->selectRow(ui->view->verticalHeader()->count() - 1); return true;}
}
}
if (o == ui->view->viewport()) {
if (e->type() == QEvent::MouseButtonPress) {
QMouseEvent * me = (QMouseEvent * )e;
if (me->button() == Qt::RightButton) {
action_del->setVisible(!read_only);
popup_menu.popup(me->globalPos());
}
}
}
if (o == ui->view->horizontalHeader()->viewport()) {
QMouseEvent * me = (QMouseEvent * )e;
if (e->type() == QEvent::MouseButtonPress)
pp = me->pos();
if (e->type() == QEvent::MouseButtonRelease) {
if ((me->pos() - pp).manhattanLength() < QApplication::startDragDistance()) {
if (me->button() == Qt::LeftButton) {
int ci = ui->view->horizontalHeader()->logicalIndexAt(me->pos()),
ri = ui->view->horizontalHeader()->logicalIndexAt(me->pos() + QPoint(3, 0)),
li = ui->view->horizontalHeader()->logicalIndexAt(me->pos() - QPoint(3, 0));
//qDebug() << ci << ri << li;
if ((ci < 0) || (ci >= ui->view->horizontalHeader()->count()) || (ci != ri) || (ci != li) || (tm.elapsed() <= QApplication::doubleClickInterval())) {
tm.restart();
return QObject::eventFilter(o, e);
}
tm.restart();
Qt::SortOrder o = Qt::AscendingOrder;
if (ui->view->horizontalHeader()->sortIndicatorSection() == ci &&
ui->view->horizontalHeader()->sortIndicatorOrder() == Qt::AscendingOrder)
o = Qt::DescendingOrder;
ui->view->horizontalHeader()->setSortIndicator(ci, o);
return true;
}
if (me->button() == Qt::RightButton) {
popup_col.clear();
for (int i = 0; i < column_props.size(); ++i) {
QPair<QString, QString> ctr = trColumn(column_props[i].name);
QAction * a = popup_col.addAction(ctr.first, this, SLOT(column_triggered(bool)));
a->setToolTip(ctr.second);
a->setCheckable(true);
a->setChecked(column_props[i].visible);
a->setData(i);
}
popup_col.popup(me->globalPos());
return true;
}
} else return true;
}
}
return QWidget::eventFilter(o, e);
}
void SQLTableWidget::timerEvent(QTimerEvent * ) {
setTableName(table_);
}
void SQLTableWidget::setTableName(const QString & t) {
ui->labelEmpty->setText(tr("Table \"%1\" doesn`t exists!").arg(t));
stopTimer();
table_opened = false;
first_update = true;
table_ = t;
if (!QSqlDatabase::database(connection_name).isOpen()) {
qDebug() << "[setTableName] Database in not opened!";
setTableVisible(false);
timer = startTimer(1000);
return;
}
model = new SQLQueryModel(table_, connection_name, column_props, column_indexes, this);
connect(model, SIGNAL(updateTable(bool)), this, SLOT(updateTable(bool)));
connect(model, SIGNAL(tableChanged()), this, SIGNAL(tableChanged()));
//model->setTable(table_);
cquery = "SELECT * FROM " + table_ + ";";
model->setQuery(cquery, QSqlDatabase::database(connection_name));
ui->view->setModel(model);
connect(ui->view->selectionModel(), SIGNAL(currentRowChanged(QModelIndex, QModelIndex)), this, SIGNAL(selectionChanged()), Qt::UniqueConnection);
//((QSqlQueryModel * )model)->setQuery(QSqlQuery("SELECT id FROM " + table_ + ";"));
setTableVisible(checkTable());
updateTable();
foreach (const QString & wr, wait_rels) {
QStringList wrl = wr.split(";");
addRelation(wrl[0], wrl[1], wrl[2], wrl[3]);
}
QMapIterator<QString, bool> cnit(col_vis);
while (cnit.hasNext()) {
cnit.next();
setColumnVisible(cnit.key(), cnit.value());
}
}
bool SQLTableWidget::checkTable() {
table_opened = false;
QSqlDatabase db = QSqlDatabase::database(connection_name);
if (db.driver() == 0) return false;
bool te = db.tables(QSql::Tables).contains(table_, Qt::CaseInsensitive );
//qDebug() << "tables:" << db.tables(QSql::Tables) << table_ << table_opened;
if (!te) {
timer = startTimer(1000);
return false;
}
/*if (!q.exec("SHOW TABLES")) {
qDebug() << "[SHOW TABLES]" << q.lastError();
timer = startTimer(1000);
return false;
}
while (q.next()) {
//qDebug() << q.value(0);
if (q.value(0) == table_)
{table_opened = true; break;}
}
if (!table_opened) {
timer = startTimer(1000);
return false;
}*/
QSqlRecord header = db.record(table_);
QString pin = db.primaryIndex(table_).fieldName(0);
table_opened = true;
column_props.clear();
for (int i = 0; i < header.count(); ++i) {
QSqlField f = header.field(i);
column_props << ColumnProperties(table_,
f.type() == QVariant::String ? "text" : QVariant::typeToName(f.type()),
f.name(),
f.defaultValue().toString(),
f.isAutoValue(),
f.name() == pin,
f.requiredStatus() == QSqlField::Optional
);
}
/*if (!q.exec("DESCRIBE " + table_ + ";")) {
qDebug() << "[DESCRIBE " + table_ + "]" << q.lastError();
timer = startTimer(1000);
return false;
}
while (q.next()) {
column_props << ColumnProperties(table_,
q.value(1).toString().toLower(),
q.value(0).toString().toLower(),
q.value(4).toString().toLower(),
q.value(5).toString().toLower().indexOf("auto_increment") >= 0,
q.value(3).toString().toLower().indexOf("pri") >= 0);
}*/
//ui->labelNew->setFixedWidth(qMax<int>(ui->view->verticalHeader()->sizeHint().width() + 2, 16));
//ui->labelFilter->setFixedWidth(qMax<int>(ui->view->verticalHeader()->sizeHint().width() + 2, 16));
ui->scrollAreaNew->setFixedHeight(QLineEdit().sizeHint().height() + 2);
ui->scrollAreaFilter->setFixedHeight(QLineEdit().sizeHint().height() + 2);
qDeleteAll(column_news);
qDeleteAll(column_filters);
column_news.clear();
column_filters.clear();
SQLNewEdit * le = 0;
for (int i = 0; i < column_props.size(); ++i) {
le = new SQLNewEdit(column_props[i], connection_name);
le->setFixedWidth(ui->view->horizontalHeader()->sectionSize(i));
column_news.push_back(le);
ui->layoutNew->insertWidget(i, le);
}
for (int i = 0; i < column_props.size(); ++i) {
SQLFilterEdit * fe = new SQLFilterEdit(column_props[i], connection_name);
fe->setFixedWidth(ui->view->horizontalHeader()->sectionSize(i));
column_filters.push_back(fe);
ui->layoutFilter->insertWidget(i, fe);
connect(fe, SIGNAL(filterChanged()), this, SLOT(updateTable()));
}
return true;
}
QStringList SQLTableWidget::getTableColumns(const QString & t) {
QStringList ret;
QSqlDatabase db = QSqlDatabase::database(connection_name);
QSqlRecord header = db.record(t);
for (int i = 0; i < header.count(); ++i)
ret << header.field(i).name();
return ret;
}
QString SQLTableWidget::getColumnType(const QString & t, const QString & c) {
QSqlDatabase db = QSqlDatabase::database(connection_name);
QSqlRecord header = db.record(t);
for (int i = 0; i < header.count(); ++i) {
QSqlField f = header.field(i);
if (f.name() != c) continue;
return (f.type() == QVariant::String ? "text" : QVariant::typeToName(f.type()));
}
return QString();
}
QList<QPair<QString, QString> > SQLTableWidget::getColumnValues(const QString &t, const QString &k, const QString &c, const QString &conn_name) {
QList<QPair<QString, QString> > ret;
QSqlQuery q(QSqlDatabase::database(conn_name));
if (!q.exec("SELECT " + t + "." + k + "," + t + "." + c + " FROM " + t + ";"))
return ret;
while (q.next())
ret << QPair<QString, QString>(q.value(0).toString(), q.value(1).toString());
return ret;
}
QPair<QString, QString> SQLTableWidget::trColumn(const QString & n) {
QPair<QString, QString> ftr = fixed_translates.value(n);
if (!ftr.first.isEmpty())
return ftr;
QPair<QString, QString> trn = translates.value(n);
if (trn.first.isEmpty()) return QPair<QString, QString>(n, "");
return trn;
}
void SQLTableWidget::updateTable(bool save_selection) {
if (!filters_active || !table_opened) return;
int vp = ui->view->verticalScrollBar()->value();
int hp = ui->view->horizontalScrollBar()->value();
//bool focus = view->hasFocus();
QModelIndex csi;
QModelIndexList sl;
if (save_selection) {
csi = ui->view->selectionModel()->currentIndex();
sl = ui->view->selectionModel()->selectedRows();
}
if (custom_query.isEmpty()) {
QString cr = columnRelations(), cf = columnFilters();
bool where = !cr.isEmpty() || !cf.isEmpty(), and_ = !cr.isEmpty() && !cf.isEmpty();
cquery = "SELECT " + columnNames() + " FROM " + tableNames() +
(where ? " WHERE " : "") + cr + (and_ ? " AND " : "") + cf + " ORDER BY " +
column_props[ui->view->horizontalHeader()->sortIndicatorSection()].fullName() +
(ui->view->horizontalHeader()->sortIndicatorOrder() == Qt::DescendingOrder ? " DESC" : " ASC") + ";";
} else
cquery = custom_query;
//qDebug() << cquery;
model->setQuery(cquery, QSqlDatabase::database(connection_name));
if (model->lastError().isValid())
qDebug() << model->lastError();
if (first_update) {
ui->view->resizeColumnsToContents();
for (int i = 0; i < ui->view->horizontalHeader()->count(); ++i)
if (ui->view->horizontalHeader()->sectionSize(i) < 100)
ui->view->horizontalHeader()->resizeSection(i, 100);
first_update = false;
}
//ui->labelNew->setFixedWidth(qMax<int>(ui->view->verticalHeader()->sizeHint().width() + 2, 16));
//ui->labelFilter->setFixedWidth(qMax<int>(ui->view->verticalHeader()->sizeHint().width() + 2, 16));
ui->layoutNew->invalidate();
if (save_selection) {
QItemSelectionModel * sm = ui->view->selectionModel();
foreach (const QModelIndex & i, sl)
sm->select(i, QItemSelectionModel::Select | QItemSelectionModel::Rows);
sm->setCurrentIndex(csi, QItemSelectionModel::Select);
//ui->view->setFocus();
}
if (custom_query.isEmpty()) {
for (int i = 0; i < model->columnCount(); ++i) {
QPair<QString, QString> ctr = trColumn(column_props[mapColumn(i)].name);
model->setHeaderData(i, Qt::Horizontal, ctr.first, Qt::DisplayRole);
model->setHeaderData(i, Qt::Horizontal, ctr.second, Qt::ToolTipRole);
}
} else {
if (custom_col_names.isEmpty()) {
QSqlRecord header = model->record();
for (int i = 0; i < header.count(); ++i) {
QSqlField f = header.field(i);
QPair<QString, QString> ctr = trColumn(f.name());
model->setHeaderData(i, Qt::Horizontal, ctr.first, Qt::DisplayRole);
model->setHeaderData(i, Qt::Horizontal, ctr.second, Qt::ToolTipRole);
}
} else {
for (int i = 0; i < qMin<int>(custom_col_names.size(), model->columnCount()); ++i) {
QPair<QString, QString> ctr = trColumn(custom_col_names[i]);
model->setHeaderData(i, Qt::Horizontal, ctr.first, Qt::DisplayRole);
model->setHeaderData(i, Qt::Horizontal, ctr.second, Qt::ToolTipRole);
}
}
}
ui->view->verticalScrollBar()->setValue(vp);
ui->view->horizontalScrollBar()->setValue(hp);
//if (focus) view->setFocus();
}
void SQLTableWidget::setConnectionName(const QString & conn_name) {
connection_name = conn_name;
ui->view->setItemDelegate(new SQLItemDelegate(column_props, column_indexes, read_only, connection_name));
}
QString SQLTableWidget::tableNames() {
QString ret = table_;
QSet<QString> rtables;
for (int i = 0; i < column_props.size(); ++i) {
ColumnProperties & cp(column_props[i]);
if (!cp.visible || cp.relation_key.isEmpty() || cp.relation_column.isEmpty())
continue;
if (rtables.contains(cp.relation_table))
continue;
rtables << cp.relation_table;
ret.append("," + cp.relation_table);
}
return ret;
}
QString SQLTableWidget::columnNames() {
QString ret;
bool first = true;
column_indexes.clear();
for (int i = 0; i < column_props.size(); ++i) {
ColumnProperties & cp(column_props[i]);
column_news[i]->setVisible(cp.visible);
column_filters[i]->setVisible(cp.visible);
if (!cp.visible && !cp.primary)
continue;
if (!first) ret.append(",");
first = false;
ret.append(cp.fullName());
if (i > 0) column_indexes.push_back(i);
}
//qDebug() << column_indexes;
return ret;
}
QString SQLTableWidget::columnFilters() {
QString ret;
bool and_ = ui->actionFilter_AND->isChecked();
for (int i = 0; i < column_props.size(); ++i) {
if (!column_filters[i]->isVisible() || column_filters[i]->isEmpty())
continue;
if (!ret.isEmpty())
ret.append(and_ ? " AND " : " OR ");
ret.append(column_filters[i]->filter());
}
if (!ret.isEmpty()) {
ret.prepend("(");
ret.append(")");
}
return ret;
}
QString SQLTableWidget::columnRelations() {
QString ret;
for (int i = 0; i < column_props.size(); ++i) {
ColumnProperties & cp(column_props[i]);
if (!cp.visible || cp.relation_key.isEmpty() || cp.relation_column.isEmpty())
continue;
if (!ret.isEmpty())
ret.append(" AND ");
ret.append(table_ + "." + cp.name + "=");
ret.append(cp.relation_table + "." + cp.relation_key);
}
if (!ret.isEmpty()) {
ret.prepend("(");
ret.append(")");
}
return ret;
}
void SQLTableWidget::on_buttonAdd_clicked() {
QStringList values;
bool at_least = false;
for (int i = 0; i < column_props.size(); ++i) {
values.append(column_news[i]->value());
if (!values.back().isEmpty()) at_least = true;
}
if (!at_least) return;
QString qs("INSERT INTO " + table_ + " (");
bool first = true;
for (int i = 0; i < column_props.size(); ++i) {
if (values[i].isEmpty() || !column_news[i]->isEnabled()) continue;
if (!first) qs.append(",");
qs.append(column_props[i].name);
first = false;
}
qs.append(") VALUES(");
first = true;
for (int i = 0; i < column_props.size(); ++i) {
if (values[i].isEmpty() || !column_news[i]->isEnabled()) continue;
if (!first) qs.append(",");
qs.append(values[i]);
first = false;
}
qs.append(");");
QSqlQuery q(QSqlDatabase::database(connection_name));
if (q.exec(qs)) {
updateTable();
emit tableChanged();
} else
qDebug() << q.lastError();
//qDebug() << q.lastQuery();
}
void SQLTableWidget::on_actionFiltersClear_triggered() {
filters_active = false;
foreach (SQLFilterEdit * l, column_filters)
l->clear();
filters_active = true;
updateTable();
}
void SQLTableWidget::on_view_clicked(const QModelIndex & index) {
emit rowClicked(index.row());
emit recordClicked(model->record(index.row()));
}
void SQLTableWidget::del_triggered() {
QModelIndexList si = ui->view->selectionModel()->selectedIndexes();
QSet<int> ids;
foreach (const QModelIndex & i, si)
ids << model->index(i.row(), 0).data().toInt();
if (ids.isEmpty()) return;
QString qs("DELETE FROM " + table_ + " WHERE " + column_props[0].name + " IN (");
bool first = true;
foreach (int i, ids) {
if (!first) qs.append(",");
qs.append(QString::number(i));
first = false;
}
qs.append(");");
//qDebug() << qs; return;
QSqlQuery q(QSqlDatabase::database(connection_name));
if (q.exec(qs)) {
updateTable();
emit tableChanged();
} else
qDebug() << q.lastError();
}
void SQLTableWidget::setColumnVisible(int ind, bool visible) {
if (!columnExists(ind)) return;
column_props[ind].visible = visible;
if (ind == 0) {
ui->view->setColumnHidden(0, !visible);
if (visible) ui->view->horizontalHeader()->resizeSection(0, ui->view->horizontalHeader()->defaultSectionSize());
}
//qDebug() << ind << on;
updateTable();
for (int i = 0; i < ui->view->horizontalHeader()->count(); ++i) {
column_news[mapColumn(i)]->setFixedWidth(ui->view->horizontalHeader()->sectionSize(i));
column_filters[mapColumn(i)]->setFixedWidth(ui->view->horizontalHeader()->sectionSize(i));
}
}
bool SQLTableWidget::addRelation(const QString & this_column, const QString & other_table, const QString & other_key, const QString & other_column) {
QString wr = this_column + ";" + other_table + ";" + other_key + ";" + other_column;
if (!wait_rels.contains(wr)) wait_rels << wr;
ColumnProperties * cp = const_cast<ColumnProperties * >(columnProperty(this_column));
if (cp == 0) return false;
cp->relation_table = other_table;
cp->relation_key = other_key;
cp->relation_column = other_column;
cp->relation_type = ColumnProperties::typeFromString(getColumnType(other_table, other_column));
cp->relation_list = getColumnValues(other_table, other_key, other_column, connection_name);
for (int i = 0; i < column_props.size(); ++i)
column_news[i]->setProp(column_props[i]);
for (int i = 0; i < column_props.size(); ++i)
column_filters[i]->setProp(column_props[i]);
updateTable();
return true;
}
void SQLTableWidget::addTranslation(const QString & file) {
QPIConfig conf(file, QIODevice::ReadOnly, QPIConfig::Config);
QPIConfig::Branch ae = conf.allLeaves();
foreach (QPIConfig::Entry * e, ae)
translates[e->name()] = QPair<QString, QString>(e->value(), e->comment());
}
void SQLTableWidget::addFixedColumnTranslation(const QString & col_name, const QString & col_tr, const QString & col_tt) {
fixed_translates[col_name] = QPair<QString, QString>(col_tr, col_tt);
updateTable();
}
void SQLTableWidget::selectId(int id) {
if (!model) return;
for (int i = 0; i < model->rowCount(); ++i)
if (model->data(model->index(i, 0)).toInt() == id) {
ui->view->selectRow(i);
break;
}
}
void SQLTableWidget::setAdditionalActions(QList<QAction * > a) {
foreach (QAction * i, add_actions)
popup_menu.removeAction(i);
add_actions = a;
popup_menu.addActions(a);
}
void SQLTableWidget::setCustomQuery(const QString & q) {
custom_query = q;
updateTable();
}
QString SQLTableWidget::preprocessScript(QString text) {
QTextStream s(&text, QIODevice::ReadOnly);
QString out;
int ce = 0;
while (!s.atEnd()) {
QString line = s.readLine();
ce = line.indexOf("--");
if (ce >= 0)
line.chop(line.length() - ce);
out.append(line).append("\n");
}
int cs = out.indexOf("/*");
while (cs >= 0) {
ce = out.indexOf("*/", cs);
if (ce > cs)
out.remove(cs, ce - cs + 2);
cs = out.indexOf("/*");
}
//qDebug() << out;
return out;
}
bool SQLTableWidget::executeScript(const QString & text_, QSqlDatabase db, bool skip_errors, bool sqlite) {
QString text = preprocessScript(text_);
int ls = 0, le = text.indexOf(';');
QSqlQuery q(db);
db.transaction();
while (le > 0) {
QString line = text.mid(ls, le - ls + 1).trimmed(), fword;
if (sqlite) line.replace("auto_increment", "autoincrement");
QTextStream(&line, QIODevice::ReadOnly) >> fword;
fword = fword.trimmed().toLower();
if (fword == "exec" || fword == "execute") {
line = line.right(line.length() - fword.length()).trimmed();
if (line.endsWith(";")) line.chop(1);
line = line.trimmed();
if (line.endsWith("'") || line.endsWith("\"")) line.chop(1);
if (line.startsWith("'") || line.startsWith("\"")) line.remove(0, 1);
executeScriptFile(line, db, skip_errors, sqlite);
} else {
if (!q.exec(line)) {
qDebug() << q.lastError();
if (!skip_errors)
return false;
}
}
ls = le + 1;
le = text.indexOf(';', ls);
}
db.commit();
return true;
}
bool SQLTableWidget::executeScriptFile(const QString & file, QSqlDatabase db, bool skip_errors, bool sqlite) {
QFile f(_dir + file);
if (!f.open(QIODevice::ReadOnly)) {
qDebug() << "Can`t open file" << file << "!";
return false;
}
QString pd = _dir;
_dir = QFileInfo(f).absolutePath() + "/";
QByteArray ba = f.readAll();
bool ok = executeScript(QString::fromUtf8(ba.data(), ba.size()), db, skip_errors, sqlite);
f.close();
_dir = pd;
return ok;
}
void SQLTableWidget::column_triggered(bool on) {
setColumnVisible(((QAction * )sender())->data().toInt(), on);
}
void SQLTableWidget::header_sectionResized(int logicalIndex, int oldSize, int newSize) {
column_news[mapColumn(logicalIndex)]->setFixedWidth(newSize);
column_filters[mapColumn(logicalIndex)]->setFixedWidth(newSize);
}
void SQLTableWidget::header_sortIndicatorChanged(int logicalIndex, Qt::SortOrder order) {
updateTable();
}
bool SQLTableWidget::connectToDatabase(const QString & config, const QString & conn_name) {
bool ok = true;
{
QPIConfig conf(config, QIODevice::ReadOnly, QPIConfig::Config);
if (!conf.isOpen())
ok = false;
else {
QSqlDatabase base = QSqlDatabase::addDatabase(conf.getValue("driver", "QMYSQL").stringValue(), conn_name);
if (!base.isValid()) {
qDebug() << "[QSqlDatabase::addDatabase] Error:" << base.lastError();
ok = false;
} else {
base.setHostName(conf.getValue("host", "").stringValue());
base.setPort(conf.getValue("port", -1));
base.setDatabaseName(conf.getValue("database", "").stringValue());
if (!base.open(conf.getValue("login", "").stringValue(), conf.getValue("password", "").stringValue())) {
qDebug() << "[QSqlDatabase::open] Error:" << base.lastError();
ok = false;
}
}
}
}
if (!ok)
QSqlDatabase::removeDatabase(conn_name);
/*QSqlQuery q;
QString dbname = conf.getValue("database", "").stringValue();
bool dbex = false;
if (!q.exec("SHOW DATABASES;")) {
qDebug() << "[SHOW DATABASES]" << q.lastError();
return false;
}
while (q.next())
if (q.value(0) == dbname)
{dbex = true; break;}
if (!dbex) {
if (!q.exec("CREATE DATABASE " + dbname + ";")) {
qDebug() << "[CREATE DATABASE]" << q.lastError();
return false;
}
}
if (!q.exec("USE " + dbname + ";")) {
qDebug() << "[USE]" << q.lastError();
return false;
}*/
return ok;
}
QTableView * SQLTableWidget::tableView() {
return ui->view;
}
bool SQLTableWidget::lineNewVisible() const {
return ui->widgetNew->isVisibleTo(ui->pageTable);
}
bool SQLTableWidget::lineFilterVisible() const {
return ui->widgetFilter->isVisibleTo(ui->pageTable);
}
QSqlRecord SQLTableWidget::currentRecord() const {
if (model) return model->record(ui->view->currentIndex().row());
return QSqlRecord();
}
void SQLTableWidget::setTableVisible(bool on) {
ui->stackedWidget->setCurrentIndex(on ? 1 : 0);
}
void SQLTableWidget::setLineNewVisible(bool on) {
ui->widgetNew->setVisible(on);
ui->buttonAdd->setVisible(on);
}
void SQLTableWidget::setLineFilterVisible(bool on) {
ui->widgetFilter->setVisible(on);
ui->buttonFilterConfig->setVisible(on);
}
void SQLTableWidget::setReadOnly(bool yes) {
read_only = yes;
ui->scrollAreaNew->setDisabled(yes);
ui->buttonAdd->setDisabled(yes);
}
void SQLTableWidget::scrolled(int value) {
ui->scrollAreaNew->horizontalScrollBar()->setValue(value);
ui->scrollAreaFilter->horizontalScrollBar()->setValue(value);
}