summaryrefslogtreecommitdiffstats
path: root/SQLiteStudio3/guiSQLiteStudio/windows/tablestructuremodel.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'SQLiteStudio3/guiSQLiteStudio/windows/tablestructuremodel.cpp')
-rw-r--r--SQLiteStudio3/guiSQLiteStudio/windows/tablestructuremodel.cpp604
1 files changed, 604 insertions, 0 deletions
diff --git a/SQLiteStudio3/guiSQLiteStudio/windows/tablestructuremodel.cpp b/SQLiteStudio3/guiSQLiteStudio/windows/tablestructuremodel.cpp
new file mode 100644
index 0000000..80c4567
--- /dev/null
+++ b/SQLiteStudio3/guiSQLiteStudio/windows/tablestructuremodel.cpp
@@ -0,0 +1,604 @@
+#include "tablestructuremodel.h"
+#include "iconmanager.h"
+#include "common/unused.h"
+#include "uiconfig.h"
+#include <QFont>
+#include <QDebug>
+#include <QMimeData>
+
+TableStructureModel::TableStructureModel(QObject *parent) :
+ QAbstractTableModel(parent)
+{
+}
+
+int TableStructureModel::rowCount(const QModelIndex& parent) const
+{
+ UNUSED(parent);
+ if (createTable.isNull())
+ return 0;
+
+ return createTable->columns.size();
+}
+
+int TableStructureModel::columnCount(const QModelIndex& parent) const
+{
+ UNUSED(parent);
+ if (createTable.isNull())
+ return 0;
+
+ switch (createTable->dialect)
+ {
+ case Dialect::Sqlite3:
+ return 9;
+ case Dialect::Sqlite2:
+ return 7;
+ }
+ return 0;
+}
+
+QVariant TableStructureModel::data(const QModelIndex& index, int role) const
+{
+ if (!index.isValid())
+ return QVariant();
+
+ if (createTable.isNull())
+ return QVariant();
+
+ if (!isValidColumnIdx(index.column()))
+ return QVariant();
+
+ int row = index.row();
+ if (createTable->columns.size() <= row)
+ return QVariant();
+
+ switch (getHeaderColumn(index.column()))
+ {
+ case TableStructureModel::Columns::NAME:
+ {
+ if (role != Qt::DisplayRole)
+ break;
+
+ return getColumnName(row);
+ }
+ case TableStructureModel::Columns::TYPE:
+ {
+ if (role != Qt::DisplayRole)
+ break;
+
+ return getColumnType(row);
+ }
+ case TableStructureModel::Columns::PK:
+ {
+ if (role != Qt::DecorationRole)
+ break;
+
+ return getColumnPk(row);
+ }
+ case TableStructureModel::Columns::FK:
+ {
+ if (role != Qt::DecorationRole)
+ break;
+
+ return getColumnFk(row);
+ }
+ case TableStructureModel::Columns::UNIQUE:
+ {
+ if (role != Qt::DecorationRole)
+ break;
+
+ return getColumnUnique(row);
+ }
+ case TableStructureModel::Columns::CHECK:
+ {
+ if (role != Qt::DecorationRole)
+ break;
+
+ return getColumnCheck(row);
+ }
+ case TableStructureModel::Columns::NOTNULL:
+ {
+ if (role != Qt::DecorationRole)
+ break;
+
+ return getColumnNotNull(row);
+ }
+ case TableStructureModel::Columns::COLLATE:
+ {
+ if (role != Qt::DecorationRole)
+ break;
+
+ return getColumnCollate(row);
+ }
+ case TableStructureModel::Columns::DEFAULT:
+ {
+ if (role == Qt::FontRole)
+ return getColumnDefaultFont(row);
+
+ if (role == Qt::ForegroundRole)
+ return getColumnDefaultColor(row);
+
+ if (role == Qt::DisplayRole)
+ return getColumnDefaultValue(row);
+
+ break;
+ }
+ }
+ return QVariant();
+}
+
+QVariant TableStructureModel::headerData(int section, Qt::Orientation orientation, int role) const
+{
+ if (role != Qt::DisplayRole)
+ return QAbstractTableModel::headerData(section, orientation, role);
+
+ if (orientation == Qt::Vertical)
+ return section + 1;
+
+ // Now it's horizontal orientation with DisplayRole
+ return columnLabel(section);
+}
+
+TableStructureModel::Columns TableStructureModel::getHeaderColumn(int colIdx) const
+{
+ if (!createTable.isNull() && createTable->dialect == Dialect::Sqlite2)
+ {
+ if (colIdx >= 3)
+ colIdx++; // skip FK
+
+ if (colIdx >= 7)
+ colIdx++; // skip COLLATE
+ }
+ return static_cast<Columns>(colIdx);
+}
+
+bool TableStructureModel::isValidColumnIdx(int colIdx) const
+{
+ if (!createTable.isNull() && createTable->dialect == Dialect::Sqlite2)
+ return colIdx >= 0 && colIdx < 7;
+
+ return colIdx >= 0 && colIdx < 9;
+}
+
+SqliteCreateTable::Column* TableStructureModel::getColumn(int colIdx) const
+{
+ if (createTable.isNull())
+ return nullptr;
+
+ return createTable->columns[colIdx];
+}
+
+void TableStructureModel::replaceColumn(int colIdx, SqliteCreateTable::Column* column)
+{
+ if (createTable.isNull())
+ return;
+
+ SqliteCreateTable::Column* oldColumn = createTable->columns[colIdx];
+ QString oldColumnName = oldColumn->name;
+
+ delete oldColumn;
+ createTable->columns[colIdx] = column;
+ column->setParent(createTable);
+ modified = true;
+
+ emit modifiyStateChanged();
+ emit dataChanged(createIndex(colIdx, 0), createIndex(colIdx, columnCount()-1));
+ emit columnModified(oldColumnName, column);
+}
+
+void TableStructureModel::insertColumn(int colIdx, SqliteCreateTable::Column* column)
+{
+ if (createTable.isNull())
+ return;
+
+ beginInsertRows(QModelIndex(), colIdx, colIdx);
+ createTable->columns.insert(colIdx, column);
+ column->setParent(createTable);
+ endInsertRows();
+
+ modified = true;
+ emit modifiyStateChanged();
+}
+
+void TableStructureModel::appendColumn(SqliteCreateTable::Column* column)
+{
+ if (createTable.isNull())
+ return;
+
+ beginInsertRows(QModelIndex(), rowCount(), rowCount());
+ createTable->columns.append(column);
+ column->setParent(createTable);
+ endInsertRows();
+
+ modified = true;
+ emit modifiyStateChanged();
+}
+
+void TableStructureModel::delColumn(int colIdx)
+{
+ if (createTable.isNull())
+ return;
+
+ QString name = createTable->columns[colIdx]->name;
+
+ beginRemoveRows(QModelIndex(), colIdx, colIdx);
+ delete createTable->columns[colIdx];
+ createTable->columns.removeAt(colIdx);
+ endRemoveRows();
+
+ modified = true;
+ emit modifiyStateChanged();
+ emit columnDeleted(name);
+}
+
+void TableStructureModel::moveColumnUp(int colIdx)
+{
+ moveColumnTo(colIdx, colIdx-1);
+}
+
+void TableStructureModel::moveColumnDown(int colIdx)
+{
+ moveColumnTo(colIdx, colIdx+1);
+}
+
+void TableStructureModel::moveColumnTo(int colIdx, int newIdx)
+{
+ if (createTable.isNull())
+ return;
+
+ if (newIdx == colIdx)
+ return;
+
+ int totalCols = createTable->columns.size();
+ if (colIdx + 1 == totalCols && newIdx == totalCols) // Moving last column out of range? Nothing to do.
+ return;
+
+ if (newIdx == colIdx+1)
+ {
+ // From Qt docs: "you must ensure that the destinationChild is not within the range of sourceFirst and sourceLast + 1".
+ // So in this case - which is easy to handle - we will invert operation. We will move target index one level up,
+ // instead of moving source index down.
+ int tmpIdx = newIdx;
+ newIdx = colIdx;
+ colIdx = tmpIdx;
+ }
+
+ beginMoveRows(QModelIndex(), colIdx, colIdx, QModelIndex(), newIdx);
+ if (newIdx >= totalCols)
+ {
+ SqliteCreateTable::Column* col = createTable->columns.takeAt(colIdx);
+ createTable->columns.append(col);
+ }
+ else
+ createTable->columns.move(colIdx, newIdx);
+
+ endMoveRows();
+
+ modified = true;
+ emit modifiyStateChanged();
+ emit columnsOrderChanged();
+}
+
+QModelIndex TableStructureModel::findColumn(const QString& columnName, Qt::CaseSensitivity cs) const
+{
+ int row = 0;
+ foreach (SqliteCreateTable::Column* col, createTable->columns)
+ {
+ if (col->name.compare(columnName, cs) == 0)
+ return createIndex(row, 0);
+
+ row++;
+ }
+ return QModelIndex();
+}
+
+QString TableStructureModel::columnLabel(int column) const
+{
+ switch (getHeaderColumn(column))
+ {
+ case Columns::NAME:
+ return tr("Name", "table structure columns");
+ case Columns::TYPE:
+ return tr("Data type", "table structure columns");
+ case Columns::PK:
+ return "P";
+ case Columns::FK:
+ return "F";
+ case Columns::UNIQUE:
+ return "U";
+ case Columns::CHECK:
+ return "H";
+ case Columns::NOTNULL:
+ return "N";
+ case Columns::COLLATE:
+ return "C";
+ case Columns::DEFAULT:
+ return tr("Default value", "table structure columns");
+ }
+ return QString::null;
+}
+
+QVariant TableStructureModel::getColumnName(int row) const
+{
+ return getColumn(row)->name;
+}
+
+QVariant TableStructureModel::getColumnType(int row) const
+{
+ SqliteColumnType* type = getColumn(row)->type;
+ return type ? type->detokenize() : "";
+}
+
+QVariant TableStructureModel::getColumnPk(int row) const
+{
+ if (isColumnPk(getColumn(row)))
+ return ICONS.CONSTRAINT_PRIMARY_KEY;
+
+ return QVariant();
+}
+
+QVariant TableStructureModel::getColumnFk(int row) const
+{
+ if (isColumnFk(getColumn(row)))
+ return ICONS.CONSTRAINT_FOREIGN_KEY;
+
+ return QVariant();
+}
+
+QVariant TableStructureModel::getColumnUnique(int row) const
+{
+ if (isColumnUnique(getColumn(row)))
+ return ICONS.CONSTRAINT_UNIQUE;
+
+ return QVariant();
+}
+
+QVariant TableStructureModel::getColumnCheck(int row) const
+{
+ if (isColumnCheck(getColumn(row)))
+ return ICONS.CONSTRAINT_CHECK;
+
+ return QVariant();
+}
+
+QVariant TableStructureModel::getColumnNotNull(int row) const
+{
+ if (isColumnNotNull(getColumn(row)))
+ return ICONS.CONSTRAINT_NOT_NULL;
+
+ return QVariant();
+}
+
+QVariant TableStructureModel::getColumnCollate(int row) const
+{
+ if (isColumnCollate(getColumn(row)))
+ return ICONS.CONSTRAINT_COLLATION;
+
+ return QVariant();
+}
+
+QVariant TableStructureModel::getColumnDefaultValue(int row) const
+{
+ QVariant value = getColumnDefault(row);
+ if (value.isNull())
+ return "NULL";
+
+ return value;
+}
+
+QVariant TableStructureModel::getColumnDefaultFont(int row) const
+{
+ QVariant value = getColumnDefault(row);
+ if (value.isNull())
+ {
+ QFont font;
+ font.setItalic(true);
+ return font;
+ }
+ return QVariant();
+}
+
+QVariant TableStructureModel::getColumnDefaultColor(int row) const
+{
+ QVariant value = getColumnDefault(row);
+ if (value.isNull())
+ return QColor(CFG_UI.Colors.DataNullFg);
+
+ return QVariant();
+}
+
+QVariant TableStructureModel::getColumnDefault(int row) const
+{
+ SqliteCreateTable::Column::Constraint* constr = getColumn(row)->getConstraint(SqliteCreateTable::Column::Constraint::DEFAULT);
+ if (!constr)
+ return QVariant();
+
+ if (!constr->id.isNull())
+ return constr->id;
+ else if (!constr->literalValue.isNull())
+ return constr->literalValue;
+ else if (!constr->ctime.isNull())
+ return constr->ctime;
+ else if (constr->expr)
+ return constr->expr->detokenize();
+ else
+ return QVariant();
+}
+
+bool TableStructureModel::isColumnPk(SqliteCreateTable::Column* column) const
+{
+ if (column->hasConstraint(SqliteCreateTable::Column::Constraint::PRIMARY_KEY))
+ return true;
+
+ QList<SqliteCreateTable::Constraint*> constraints = createTable->getConstraints(SqliteCreateTable::Constraint::PRIMARY_KEY);
+ foreach (SqliteCreateTable::Constraint* constr, constraints)
+ if (constr->doesAffectColumn(column->name))
+ return true;
+
+ return false;
+}
+
+bool TableStructureModel::isColumnFk(SqliteCreateTable::Column* column) const
+{
+ if (column->hasConstraint(SqliteCreateTable::Column::Constraint::FOREIGN_KEY))
+ return true;
+
+ QList<SqliteCreateTable::Constraint*> constraints = createTable->getConstraints(SqliteCreateTable::Constraint::FOREIGN_KEY);
+ foreach (SqliteCreateTable::Constraint* constr, constraints)
+ if (constr->doesAffectColumn(column->name))
+ return true;
+
+ return false;
+}
+
+bool TableStructureModel::isColumnUnique(SqliteCreateTable::Column* column) const
+{
+ if (column->hasConstraint(SqliteCreateTable::Column::Constraint::UNIQUE))
+ return true;
+
+ QList<SqliteCreateTable::Constraint*> constraints = createTable->getConstraints(SqliteCreateTable::Constraint::UNIQUE);
+ foreach (SqliteCreateTable::Constraint* constr, constraints)
+ if (constr->doesAffectColumn(column->name))
+ return true;
+
+ return false;
+}
+
+bool TableStructureModel::isColumnCheck(SqliteCreateTable::Column* column) const
+{
+ if (column->hasConstraint(SqliteCreateTable::Column::Constraint::CHECK))
+ return true;
+
+ QList<SqliteCreateTable::Constraint*> constraints = createTable->getConstraints(SqliteCreateTable::Constraint::CHECK);
+ foreach (SqliteCreateTable::Constraint* constr, constraints)
+ if (constr->expr->getContextColumns(false).contains(column->name, Qt::CaseInsensitive))
+ return true;
+
+ return false;
+}
+
+bool TableStructureModel::isColumnNotNull(SqliteCreateTable::Column* column) const
+{
+ if (column->hasConstraint(SqliteCreateTable::Column::Constraint::NOT_NULL))
+ return true;
+
+ return false;
+}
+
+bool TableStructureModel::isColumnCollate(SqliteCreateTable::Column* column) const
+{
+ if (column->hasConstraint(SqliteCreateTable::Column::Constraint::COLLATE))
+ return true;
+
+ return false;
+}
+
+void TableStructureModel::setCreateTable(SqliteCreateTable* value)
+{
+ beginResetModel();
+ createTable = value;
+ endResetModel();
+
+ modified = false;
+ emit modifiyStateChanged();
+}
+
+bool TableStructureModel::isModified() const
+{
+ return modified;
+}
+
+Qt::DropActions TableStructureModel::supportedDropActions() const
+{
+ return Qt::MoveAction;
+}
+
+Qt::DropActions TableStructureModel::supportedDragActions() const
+{
+ return Qt::CopyAction|Qt::MoveAction;
+}
+
+
+QStringList TableStructureModel::mimeTypes() const
+{
+ return {mimeType};
+}
+
+QMimeData* TableStructureModel::mimeData(const QModelIndexList& indexes) const
+{
+ if (indexes.size() < 1)
+ return nullptr;
+
+ QModelIndex idx = indexes.first();
+
+ QMimeData *data = new QMimeData();
+
+ QByteArray output;
+ QDataStream stream(&output, QIODevice::WriteOnly);
+
+ stream << idx.row();
+ data->setData(mimeType, output);
+
+ return data;
+}
+
+
+bool TableStructureModel::canDropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent) const
+{
+ UNUSED(action);
+ UNUSED(row);
+ UNUSED(column);
+ UNUSED(parent);
+
+ if (!data)
+ return false;
+
+ if (!data->hasFormat(mimeType))
+ return false;
+
+ return true;
+}
+
+bool TableStructureModel::dropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent)
+{
+ UNUSED(column);
+
+ if (action == Qt::IgnoreAction)
+ return true;
+
+ if (!data)
+ return false;
+
+ if (!data->hasFormat(mimeType))
+ return false;
+
+ if (action != Qt::MoveAction)
+ return false;
+
+ if (row < 0)
+ {
+ if (!parent.isValid() && !createTable.isNull())
+ row = createTable->columns.size();
+ else
+ row = parent.row();
+ }
+
+ if (row < 0)
+ return false;
+
+ QByteArray byteData = data->data(mimeType);
+ QDataStream stream(&byteData, QIODevice::ReadOnly);
+ int oldRow;
+ stream >> oldRow;
+
+ moveColumnTo(oldRow, row);
+ return true;
+}
+
+Qt::ItemFlags TableStructureModel::flags(const QModelIndex& index) const
+{
+ Qt::ItemFlags defFlags = QAbstractItemModel::flags(index);
+ if (!index.isValid())
+ return defFlags|Qt::ItemIsDropEnabled;
+
+ return defFlags|Qt::ItemIsDragEnabled|Qt::ItemIsDropEnabled;
+}