diff options
author | Lukáš Lalinský <lalinsky@gmail.com> | 2008-11-23 22:44:06 +0100 |
---|---|---|
committer | Lukáš Lalinský <lalinsky@gmail.com> | 2008-11-23 22:44:06 +0100 |
commit | 5da39fc03fe42f7e4b572374e983ea1afb80033d (patch) | |
tree | 4d6fcca41e63b37f8c77577c55d89b510fe959fe | |
download | dbmodel-5da39fc03fe42f7e4b572374e983ea1afb80033d.tar.xz |
Initial commit
28 files changed, 1646 insertions, 0 deletions
diff --git a/.bzrignore b/.bzrignore new file mode 100644 index 0000000..77c11f9 --- /dev/null +++ b/.bzrignore @@ -0,0 +1,6 @@ +moc_*.cpp +moc_*.h +ui_*.h +Makefile +qrc_*.cpp +./dbmodel diff --git a/column.cpp b/column.cpp new file mode 100644 index 0000000..19c9e88 --- /dev/null +++ b/column.cpp @@ -0,0 +1,55 @@ +#include "databasetable.h" +#include "column.h" + +Column::Column(DatabaseTable *table) + : m_table(table), m_primaryKey(false), m_required(false) +{ +} + +void +Column::setName(const QString &name) +{ + if (m_name != name) { + m_name = name; + m_table->updateLayout(); + } +} + +void +Column::setNotes(const QString ¬es) +{ + if (m_notes != notes) { + m_notes = notes; + m_table->updateLayout(); + } +} + +void +Column::setDataType(const QString &dataType) +{ + if (m_dataType != dataType) { + m_dataType = dataType; + m_table->updateLayout(); + } +} + +void +Column::setPrimaryKey(bool primaryKey) +{ + if (m_primaryKey != primaryKey) { + m_primaryKey = primaryKey; + if (primaryKey) { + m_required = true; + } + m_table->updateLayout(); + } +} + +void +Column::setRequired(bool required) +{ + if (m_required != required) { + m_required = required; + m_table->updateLayout(); + } +} diff --git a/column.h b/column.h new file mode 100644 index 0000000..3baa5ea --- /dev/null +++ b/column.h @@ -0,0 +1,37 @@ +#ifndef COLUMN_H +#define COLUMN_H + +#include <QString> + +class DatabaseTable; + +class Column +{ +public: + Column(DatabaseTable *table); + + QString name() const { return m_name; } + void setName(const QString &name); + + QString notes() const { return m_notes; } + void setNotes(const QString ¬es); + + QString dataType() const { return m_dataType; } + void setDataType(const QString &dataType); + + bool isPrimaryKey() const { return m_primaryKey; } + void setPrimaryKey(bool primaryKey); + + bool isRequired() const { return m_required; } + void setRequired(bool required); + +private: + DatabaseTable *m_table; + QString m_name; + QString m_notes; + QString m_dataType; + bool m_primaryKey; + bool m_required; +}; + +#endif diff --git a/databasemodel.cpp b/databasemodel.cpp new file mode 100644 index 0000000..8f75ea2 --- /dev/null +++ b/databasemodel.cpp @@ -0,0 +1,283 @@ +#include "databasemodel.h" +#include "databasetable.h" +#include "databaserelation.h" +#include "column.h" +#include <QGraphicsItem> +#include <QDebug> +#include <QFile> +#include <QTextStream> + +DatabaseModel::DatabaseModel(QObject *parent) + : QGraphicsScene(parent), m_line(NULL) +{ + QFont font("Sans"); + font.setPointSize(9); + setFont(font); + connect(this, SIGNAL(tableMoved(DatabaseTable *)), SLOT(updatePositions(DatabaseTable *))); +} + +DatabaseModel::Mode +DatabaseModel::mode() +{ + return m_mode; +} + +void +DatabaseModel::setMode(Mode mode) +{ + m_mode = mode; + emit modeChanged(mode); +} + +void +DatabaseModel::updatePositions(DatabaseTable *table) +{ + foreach (DatabaseRelation *relation, findTableRelations(table)) { + relation->updatePositions(); + } +} + +void +DatabaseModel::mousePressEvent(QGraphicsSceneMouseEvent *event) +{ + if (m_mode == AddTable && event->button() == Qt::LeftButton) { + DatabaseTable *table = new DatabaseTable(); + table->setPos(event->scenePos()); + table->setZValue(10.0); + m_tables << table; + addItem(table); + clearSelection(); + table->setSelected(true); + setMode(Select); + event->accept(); + return; + } + if (m_mode == AddRelation && event->button() == Qt::LeftButton) { + m_line = new QGraphicsLineItem(); + m_line->setLine(QLineF(event->scenePos(), event->scenePos())); + m_line->setZValue(1000.0); + addItem(m_line); + event->accept(); + return; + } + QGraphicsScene::mousePressEvent(event); +} + +void +DatabaseModel::mouseMoveEvent(QGraphicsSceneMouseEvent *event) +{ + if (m_line) { + m_line->setLine(QLineF(m_line->line().p1(), event->scenePos())); + event->accept(); + return; + } + QGraphicsScene::mouseMoveEvent(event); +} + +void +DatabaseModel::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) +{ + if (m_line) { + removeItem(m_line); + DatabaseTable *source = qgraphicsitem_cast<DatabaseTable *>(itemAt(m_line->line().p1())); + DatabaseTable *target = qgraphicsitem_cast<DatabaseTable *>(itemAt(m_line->line().p2())); + if (source && target && source != target) { + qDebug() << "Add relation between " << source << " and " << target; + DatabaseRelation *relation = new DatabaseRelation(); + relation->setSource(source); + relation->setTarget(target); + relation->setZValue(1.0); + addItem(relation); + m_relations << relation; + } + + delete m_line; + m_line = NULL; + setMode(Select); + event->accept(); + return; + } + QGraphicsScene::mouseReleaseEvent(event); +} + +DatabaseTable * +DatabaseModel::selectedTable() +{ + QList<QGraphicsItem *> items = selectedItems(); + if (items.size() != 1) + return NULL; + return qgraphicsitem_cast<DatabaseTable *>(items[0]); +} + +void +DatabaseModel::deleteSelectedItems() +{ + foreach (QGraphicsItem *item, selectedItems()) { + DatabaseTable *table = qgraphicsitem_cast<DatabaseTable *>(item); + if (table) { + foreach (DatabaseRelation *relation, findTableRelations(table)) { + removeItem(relation); + m_relations.removeAll(relation); + } + removeItem(table); + m_tables.removeAll(table); + } + } +} + +QList<DatabaseRelation *> +DatabaseModel::findTableRelations(DatabaseTable *table) +{ + QList<DatabaseRelation *> result; + foreach (DatabaseRelation *relation, m_relations) { + if (relation->source() == table || relation->target() == table) { + result << relation; + } + } + return result; +} + +void +appendStringElement(QDomDocument &doc, QDomElement &parent, const QString &name, const QString &value) +{ + if (!value.isEmpty()) { + QDomElement element = doc.createElement(name); + element.appendChild(doc.createTextNode(value)); + parent.appendChild(element); + } +} + +QString +readStringElement(QDomElement &parent, const QString &name, const QString &defaultValue = QString()) +{ + QDomElement element = parent.firstChildElement(name); + if (!element.isNull()) { + return element.text(); + } + return defaultValue; +} + +void +DatabaseModel::save(const QString &fileName) +{ + QDomDocument doc; + + QDomElement root = doc.createElement("database-model"); + doc.appendChild(root); + + QDomElement tableListElement = doc.createElement("table-list"); + root.appendChild(tableListElement); + + foreach (DatabaseTable *table, m_tables) { + QDomElement tableElement = doc.createElement("table"); + tableListElement.appendChild(tableElement); + + QDomElement positionElement = doc.createElement("position"); + positionElement.setAttribute("x", table->pos().x()); + positionElement.setAttribute("y", table->pos().y()); + tableElement.appendChild(positionElement); + + QDomElement nameElement = doc.createElement("name"); + nameElement.appendChild(doc.createTextNode(table->name())); + tableElement.appendChild(nameElement); + + QDomElement columnListElement = doc.createElement("column-list"); + tableElement.appendChild(columnListElement); + + foreach (Column *column, table->columns()) { + QDomElement columnElement = doc.createElement("column"); + columnListElement.appendChild(columnElement); + appendStringElement(doc, columnElement, "name", column->name()); + appendStringElement(doc, columnElement, "data-type", column->dataType()); + appendStringElement(doc, columnElement, "required", column->isRequired() ? "yes" : QString()); + appendStringElement(doc, columnElement, "primary-key", column->isPrimaryKey() ? "yes" : QString()); + appendStringElement(doc, columnElement, "notes", column->notes()); + } + } + + QDomElement relationListElement = doc.createElement("relation-list"); + root.appendChild(relationListElement); + + foreach (DatabaseRelation *relation, m_relations) { + QDomElement relationElement = doc.createElement("relation"); + relationElement.setAttribute("from", QString::number(m_tables.indexOf(relation->source()))); + relationElement.setAttribute("to", QString::number(m_tables.indexOf(relation->target()))); + relationListElement.appendChild(relationElement); + } + + QFile file(fileName); + if (file.open(QIODevice::WriteOnly)) { + QTextStream stream(&file); + doc.save(stream, 0); + file.close(); + } + + setFileName(fileName); +} + +void +DatabaseModel::load(const QString &fileName) +{ + QDomDocument doc; + QFile file(fileName); + if (file.open(QIODevice::ReadOnly)) { + doc.setContent(&file); + file.close(); + } + + setFileName(fileName); + + QDomElement root = doc.firstChildElement("database-model"); + + QDomElement tableListElement = root.firstChildElement("table-list"); + QDomElement tableElement = tableListElement.firstChildElement("table"); + while (!tableElement.isNull()) { + DatabaseTable *table = new DatabaseTable; + + QDomElement positionElement = tableElement.firstChildElement("position"); + if (!positionElement.isNull()) { + QPoint pos; + pos.setX(positionElement.attribute("x", "0").toInt()); + pos.setY(positionElement.attribute("y", "0").toInt()); + table->setPos(pos); + } + + table->setName(readStringElement(tableElement, "name")); + + QDomElement columnListElement = tableElement.firstChildElement("column-list"); + QDomElement columnElement = columnListElement.firstChildElement("column"); + while (!columnElement.isNull()) { + Column *column = table->addColumn(); + column->setName(readStringElement(columnElement, "name")); + column->setDataType(readStringElement(columnElement, "data-type")); + column->setRequired(readStringElement(columnElement, "required") == "yes"); + column->setPrimaryKey(readStringElement(columnElement, "primary-key") == "yes"); + column->setNotes(readStringElement(columnElement, "notes")); + columnElement = columnElement.nextSiblingElement("column"); + } + + m_tables << table; + addItem(table); + + tableElement = tableElement.nextSiblingElement("table"); + } + + QDomElement relationListElement = root.firstChildElement("relation-list"); + QDomElement relationElement = relationListElement.firstChildElement("relation"); + while (!relationElement.isNull()) { + relationElement = relationElement.nextSiblingElement("relation"); + bool ok; + int index0 = relationElement.attribute("from").toInt(&ok); + if (ok) { + int index1 = relationElement.attribute("to").toInt(&ok); + if (ok) { + DatabaseRelation *relation = new DatabaseRelation(); + relation->setSource(m_tables[index0]); + relation->setTarget(m_tables[index1]); + m_relations << relation; + addItem(relation); + } + } + } + +} diff --git a/databasemodel.h b/databasemodel.h new file mode 100644 index 0000000..48d56d0 --- /dev/null +++ b/databasemodel.h @@ -0,0 +1,60 @@ +#ifndef DATABASEMODEL_H +#define DATABASEMODEL_H + +#include <QGraphicsScene> +#include <QGraphicsSceneMouseEvent> +#include <QDomDocument> +#include <QGraphicsLineItem> + +class DatabaseTable; +class DatabaseRelation; + +class DatabaseModel : public QGraphicsScene +{ + Q_OBJECT + +public: + DatabaseModel(QObject *parent = 0); + + enum Mode { + Select = 0, + AddTable, + AddRelation + }; + + Mode mode(); + void setMode(Mode mode); + + DatabaseTable *selectedTable(); + void deleteSelectedItems(); + QList<DatabaseRelation *> findTableRelations(DatabaseTable *table); + + void save(const QString &fileName); + void load(const QString &fileName); + + QString fileName() { return m_fileName; } + void setFileName(const QString &fileName) { m_fileName = fileName; } + +signals: + void modeChanged(DatabaseModel::Mode mode); + void tableMoved(DatabaseTable *table); + + friend class DatabaseTable; + +protected slots: + void updatePositions(DatabaseTable *table); + +protected: + void mousePressEvent(QGraphicsSceneMouseEvent *event); + void mouseMoveEvent(QGraphicsSceneMouseEvent *event); + void mouseReleaseEvent(QGraphicsSceneMouseEvent *event); + +private: + Mode m_mode; + QString m_fileName; + QGraphicsLineItem *m_line; + QList<DatabaseTable *> m_tables; + QList<DatabaseRelation *> m_relations; +}; + +#endif diff --git a/databasemodelitem.cpp b/databasemodelitem.cpp new file mode 100644 index 0000000..91520b4 --- /dev/null +++ b/databasemodelitem.cpp @@ -0,0 +1,6 @@ +#include "databasemodelitem.h" + +DatabaseModelItem::DatabaseModelItem(QGraphicsItem *parent) + : QGraphicsItem(parent) +{ +} diff --git a/databasemodelitem.h b/databasemodelitem.h new file mode 100644 index 0000000..52dc3d4 --- /dev/null +++ b/databasemodelitem.h @@ -0,0 +1,16 @@ +#ifndef DATABASEMODELITEM_H +#define DATABASEMODELITEM_H + +#include <QGraphicsItem> + +class DatabaseModelItem : public QGraphicsItem +{ +public: + enum { + Table = UserType + 1, + Relation + }; + DatabaseModelItem(QGraphicsItem *parent = 0); +}; + +#endif diff --git a/databasemodelview.cpp b/databasemodelview.cpp new file mode 100644 index 0000000..0bb6c91 --- /dev/null +++ b/databasemodelview.cpp @@ -0,0 +1,67 @@ +#include <QMouseEvent> +#include <QScrollBar> +#include <QDebug> +#include "databasemodelview.h" + +DatabaseModelView::DatabaseModelView(QWidget *parent) + : QGraphicsView(parent), m_handScrolling(false) +{ + setAlignment(Qt::AlignLeft | Qt::AlignTop); + setRenderHint(QPainter::Antialiasing); +} + +void +DatabaseModelView::setScene(QGraphicsScene *scene) +{ + QGraphicsView::setScene(scene); + updateSceneRect2(scene->sceneRect()); + connect(scene, SIGNAL(sceneRectChanged(const QRectF &)), SLOT(updateSceneRect2(const QRectF &))); +} + +void +DatabaseModelView::updateSceneRect2(const QRectF &rect) +{ + QRectF sceneRect = rect.united(QRectF(QPointF(0, 0), QPointF(100, 100))); + setSceneRect(sceneRect); +} + +void +DatabaseModelView::mousePressEvent(QMouseEvent *event) +{ + if (event->button() == Qt::MidButton) { + m_handScrolling = true; + m_handScrollingOrigin = QPoint(event->pos()); + m_handScrollingValueX = horizontalScrollBar()->value(); + m_handScrollingValueY = verticalScrollBar()->value(); + m_savedCursor = viewport()->cursor(); + viewport()->setCursor(Qt::ClosedHandCursor); + event->accept(); + return; + } + QGraphicsView::mousePressEvent(event); +} + +void +DatabaseModelView::mouseMoveEvent(QMouseEvent *event) +{ + if (m_handScrolling) { + QPoint delta = event->pos() - m_handScrollingOrigin; + horizontalScrollBar()->setValue(m_handScrollingValueX - delta.x()); + verticalScrollBar()->setValue(m_handScrollingValueY - delta.y()); + event->accept(); + return; + } + QGraphicsView::mouseMoveEvent(event); +} + +void +DatabaseModelView::mouseReleaseEvent(QMouseEvent *event) +{ + if (m_handScrolling) { + m_handScrolling = false; + viewport()->setCursor(m_savedCursor); + event->accept(); + return; + } + QGraphicsView::mouseReleaseEvent(event); +} diff --git a/databasemodelview.h b/databasemodelview.h new file mode 100644 index 0000000..27b5cf2 --- /dev/null +++ b/databasemodelview.h @@ -0,0 +1,31 @@ +#ifndef DATABASEMODELVIEW_H +#define DATABASEMODELVIEW_H + +#include <QGraphicsView> + +class DatabaseModelView : public QGraphicsView +{ + Q_OBJECT + +public: + DatabaseModelView(QWidget *parent = 0); + + void setScene(QGraphicsScene *scene); + +protected: + void mousePressEvent(QMouseEvent *event); + void mouseMoveEvent(QMouseEvent *event); + void mouseReleaseEvent(QMouseEvent *event); + +protected slots: + void updateSceneRect2(const QRectF &rect); + +private: + bool m_handScrolling; + QPoint m_handScrollingOrigin; + QCursor m_savedCursor; + int m_handScrollingValueX; + int m_handScrollingValueY; +}; + +#endif diff --git a/databaserelation.cpp b/databaserelation.cpp new file mode 100644 index 0000000..1f8d327 --- /dev/null +++ b/databaserelation.cpp @@ -0,0 +1,126 @@ +#include <QGraphicsScene> +#include <QDebug> +#include "databasetable.h" +#include "databaserelation.h" + +DatabaseRelation::DatabaseRelation(QGraphicsItem *parent) + : DatabaseModelItem(parent), m_source(0), m_target(0) +{ + setFlag(ItemIsSelectable); +} + +QRectF +DatabaseRelation::boundingRect() const +{ + return shape().boundingRect(); + //return QRectF(m_line.p1(), m_line.p2()).normalized(); +} + +QPainterPath +DatabaseRelation::shape() const +{ + QPainterPath path; + path.moveTo(m_line.p1()); + path.lineTo(m_line.p2()); + path.addPolygon(m_arrowHead); + QPen pen(QPen(QColor(0, 0, 0), 1)); + QPainterPathStroker ps; + ps.setCapStyle(pen.capStyle()); + ps.setWidth(pen.widthF() + 0.3); + ps.setJoinStyle(pen.joinStyle()); + ps.setMiterLimit(pen.miterLimit()); + return ps.createStroke(path); +} + +void +DatabaseRelation::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) +{ + Q_UNUSED(option); + Q_UNUSED(widget); + + QPen pen(QPen(QColor(0, 0, 0), 1)); + painter->setPen(pen); + painter->drawLine(m_line); + painter->setBrush(pen.color()); + painter->drawPolygon(m_arrowHead); +} + +void +DatabaseRelation::setSource(DatabaseTable *source) +{ + m_source = source; + if (scene()) { + updateLayout(); + } +} + +void +DatabaseRelation::setTarget(DatabaseTable *target) +{ + m_target = target; + if (scene()) { + updateLayout(); + } +} + +QVariant +DatabaseRelation::itemChange(GraphicsItemChange change, const QVariant &value) +{ + if (change == ItemSceneHasChanged) { + updateLayout(); + } + return QGraphicsItem::itemChange(change, value); +} + +void +DatabaseRelation::updatePositions() +{ + updateLayout(); +} + +void +DatabaseRelation::updateLayout() +{ + if (!scene()) + return; + + prepareGeometryChange(); + + QRectF rect1 = m_source->boundingRect().translated(m_source->pos()); + QRectF rect2 = m_target->boundingRect().translated(m_target->pos()); + + QPointF p1 = rect1.center(); + QPointF p2 = rect2.center(); + m_line = QLineF(p1, p2); + + QPolygonF polygon; + + polygon = rect1; + for (int i = 0; i < 4; i++) { + QLineF line(polygon[i], polygon[(i+1)%4]); + QPointF intersectionPoint; + if (m_line.intersect(line, &intersectionPoint) == QLineF::BoundedIntersection) { + m_line.setP1(intersectionPoint); + break; + } + } + + polygon = rect2; + for (int i = 0; i < 4; i++) { + QLineF line(polygon[i], polygon[(i+1)%4]); + QPointF intersectionPoint; + if (m_line.intersect(line, &intersectionPoint) == QLineF::BoundedIntersection) { + m_line.setP2(intersectionPoint); + break; + } + } + + m_arrowHead.resize(3); + m_arrowHead[0] = QPointF(0.0, 0.0); + m_arrowHead[1] = QPointF(-4.5, 10.0); + m_arrowHead[2] = QPointF(4.5, 10.0); + QMatrix matrix; + matrix.rotate(-m_line.angle() + 90); + m_arrowHead = matrix.map(m_arrowHead); + m_arrowHead.translate(m_line.p2()); +} diff --git a/databaserelation.h b/databaserelation.h new file mode 100644 index 0000000..b1e79b0 --- /dev/null +++ b/databaserelation.h @@ -0,0 +1,44 @@ +#ifndef DATABASERELATION_H +#define DATABASERELATION_H + +#include <QGraphicsItem> +#include <QPainter> +#include "databasemodelitem.h" + +class DatabaseTable; + +class DatabaseRelation : public DatabaseModelItem +{ +public: + DatabaseRelation(QGraphicsItem *parent = 0); + + QRectF boundingRect() const; + QPainterPath shape() const; + void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0); + + DatabaseTable *source() { return m_source; } + void setSource(DatabaseTable *source); + + DatabaseTable *target() { return m_target; } + void setTarget(DatabaseTable *target); + + enum { Type = DatabaseModelItem::Relation }; + virtual int type() const { return Type; } + + void updatePositions(); + +protected slots: + void updateLayout(); + +protected: + QVariant itemChange(GraphicsItemChange change, const QVariant &value); + +private: + DatabaseTable *m_source; + DatabaseTable *m_target; + + QLineF m_line; + QPolygonF m_arrowHead; +}; + +#endif diff --git a/databasetable.cpp b/databasetable.cpp new file mode 100644 index 0000000..53af2f1 --- /dev/null +++ b/databasetable.cpp @@ -0,0 +1,176 @@ +#include <QGraphicsScene> +#include <QDebug> +#include "databasetable.h" +#include "databasemodel.h" +#include "column.h" + +DatabaseTable::DatabaseTable(QGraphicsItem *parent) + : DatabaseModelItem(parent) +{ + setFlag(ItemIsMovable); + setFlag(ItemIsSelectable); + m_name = "Table"; +} + +QRectF +DatabaseTable::boundingRect() const +{ + return m_outerRect; +} + +void +DatabaseTable::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) +{ + Q_UNUSED(option); + Q_UNUSED(widget); + + QFont font = scene()->font(); + QFontMetricsF fontMetrics(font); + + QFont boldFont = font; + boldFont.setBold(true); + + QPen pen(QPen(QColor(0, 0, 0))); + pen.setJoinStyle(Qt::MiterJoin); + QPen borderPen(pen); + if (isSelected()) { + borderPen.setColor(QColor(0, 96, 255)); + borderPen.setWidth(2); + } + + painter->setPen(pen); + painter->setFont(font); + + painter->fillRect(m_outerRect, QColor(255, 255, 255)); + + // Draw the table name + painter->fillRect(m_nameBgRect, QColor(205, 205, 205)); + painter->drawLine(m_nameBgRect.bottomLeft(), m_nameBgRect.bottomRight()); + painter->drawText(m_namePos, m_name); + + painter->drawLine( + m_leftSideWidth, m_nameBgRect.bottom(), + m_leftSideWidth, m_outerRect.bottom()); + + // Draw the table name + QPointF colPos = m_firstColPos; + QPointF leftSizePos = colPos - QPointF(m_leftSideWidth, 0); + foreach (Column *column, m_columns) { + painter->setFont(column->isRequired() ? boldFont : font); + painter->drawText(colPos, column->name()); + colPos += m_colPosIncrement; + if (column->isPrimaryKey()) { + painter->drawText(leftSizePos, "PK"); + } + leftSizePos += m_colPosIncrement; + } + + // Draw the outside border + painter->setPen(borderPen); + painter->drawRect(m_outerRect); +} + +void +DatabaseTable::setName(const QString &name) +{ + m_name = name; + updateLayout(); +} + +Column * +DatabaseTable::addColumn(const QString &name) +{ + Column *column = new Column(this); + column->setName(name); + m_columns.append(column); + updateLayout(); + return column; +} + +void +DatabaseTable::setColumn(int index, const QString &name) +{ + m_columns[index]->setName(name); + updateLayout(); +} + +void +DatabaseTable::removeColumn(int index) +{ + delete m_columns.takeAt(index); + updateLayout(); +} + +void +DatabaseTable::swapColumns(int index1, int index2) +{ + qSwap(m_columns[index1], m_columns[index2]); + updateLayout(); +} + +QVariant +DatabaseTable::itemChange(GraphicsItemChange change, const QVariant &value) +{ + if (change == ItemSceneHasChanged) { + updateLayout(); + } + if (change == ItemPositionChange) { + DatabaseModel *model = qobject_cast<DatabaseModel *>(scene()); + if (model) { + emit model->tableMoved(this); + } + } + return QGraphicsItem::itemChange(change, value); +} + +void +DatabaseTable::updateLayout() +{ + if (!scene()) + return; + + prepareGeometryChange(); + + QFont font = scene()->font(); + QFontMetricsF fontMetrics(font); + + QFont boldFont = font; + boldFont.setBold(true); + QFontMetricsF boldFontMetrics(boldFont); + + qreal spaceWidth = fontMetrics.width(' '); + qreal nameWidth = fontMetrics.width(m_name); + qreal height = fontMetrics.height(); + + m_leftSideWidth = fontMetrics.width("PK"); + + qreal width = nameWidth + spaceWidth * 2; + + qreal maxColumnWidth = 0; + foreach (Column *column, m_columns) { + qreal columnWidth = column->isRequired() ? boldFontMetrics.width(column->name()) : fontMetrics.width(column->name()); + maxColumnWidth = qMax(maxColumnWidth, columnWidth); + qreal columnLeftSideWidth = 0; + if (column->isPrimaryKey()) { + if (column->isRequired()) { + columnLeftSideWidth = boldFontMetrics.width("PK"); + } + else { + columnLeftSideWidth = fontMetrics.width("PK"); + } + } + m_leftSideWidth = qMax(m_leftSideWidth, columnLeftSideWidth); + } + m_leftSideWidth += spaceWidth * 2; + width = qMax(nameWidth + spaceWidth * 4, m_leftSideWidth + maxColumnWidth + spaceWidth * 2); + + m_outerRect = QRectF(0, 0, width, height + qMax(0.66, qreal(m_columns.size())) * height); + + m_nameBgRect = QRectF(0, 0, width, height); + m_namePos = QPointF((width - nameWidth) / 2, fontMetrics.ascent()); + + m_firstColPos = QPointF(m_leftSideWidth + spaceWidth * 1 - 1, height + fontMetrics.ascent() + 2); + m_colPosIncrement = QPointF(0, fontMetrics.lineSpacing()); + + update(); +} diff --git a/databasetable.h b/databasetable.h new file mode 100644 index 0000000..3d48ede --- /dev/null +++ b/databasetable.h @@ -0,0 +1,50 @@ +#ifndef DATABASETABLE_H +#define DATABASETABLE_H + +#include <QGraphicsItem> +#include <QPainter> +#include "databasemodelitem.h" + +class Column; + +class DatabaseTable : public DatabaseModelItem +{ +public: + DatabaseTable(QGraphicsItem *parent = 0); + + QRectF boundingRect() const; + void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0); + + QString name() const { return m_name; } + void setName(const QString &name); + + QList<Column *> columns() const { return m_columns; } + Column *column(int index) const { return m_columns[index]; } + int columnCount() const { return m_columns.size(); } + + void setColumn(int index, const QString &name); + void removeColumn(int index); + Column *addColumn(const QString &name = QString()); + void swapColumns(int index1, int index2); + + enum { Type = DatabaseModelItem::Table }; + virtual int type() const { return Type; } + +protected slots: + void updateLayout(); + + friend class Column; + +protected: + QVariant itemChange(GraphicsItemChange change, const QVariant &value); + +private: + QString m_name; + QList<Column *> m_columns; + + QRectF m_outerRect, m_nameBgRect; + QPointF m_namePos, m_firstColPos, m_colPosIncrement; + qreal m_leftSideWidth; +}; + +#endif diff --git a/dbmodel.pro b/dbmodel.pro new file mode 100644 index 0000000..c3e7140 --- /dev/null +++ b/dbmodel.pro @@ -0,0 +1,22 @@ +QT += xml +SOURCES = \ + column.cpp \ + main.cpp \ + mainwindow.cpp \ + databasemodelview.cpp \ + databasemodel.cpp \ + databasemodelitem.cpp \ + databasetable.cpp \ + databaserelation.cpp \ + tableproperties.cpp +HEADERS = \ + column.h \ + mainwindow.h \ + databasemodelview.h \ + databasemodel.h \ + databasemodelitem.h \ + databasetable.h \ + databaserelation.h \ + tableproperties.h +RESOURCES = dbmodel.qrc +FORMS = tableproperties.ui diff --git a/dbmodel.qrc b/dbmodel.qrc new file mode 100644 index 0000000..ec6a088 --- /dev/null +++ b/dbmodel.qrc @@ -0,0 +1,11 @@ +<!DOCTYPE RCC><RCC version="1.0"> +<qresource> + <file>icons/cr16-action-table_newobj.png</file> + <file>icons/cr16-action-relation_newobj.png</file> + <file>icons/cr16-action-mouse_pointer.png</file> + <file>icons/16x16/document-new.png</file> + <file>icons/16x16/document-open.png</file> + <file>icons/16x16/document-save.png</file> + <file>icons/16x16/document-save-as.png</file> +</qresource> +</RCC>
\ No newline at end of file diff --git a/icons/16x16/document-new.png b/icons/16x16/document-new.png Binary files differnew file mode 100644 index 0000000..4c3efdd --- /dev/null +++ b/icons/16x16/document-new.png diff --git a/icons/16x16/document-open.png b/icons/16x16/document-open.png Binary files differnew file mode 100644 index 0000000..69dd8d4 --- /dev/null +++ b/icons/16x16/document-open.png diff --git a/icons/16x16/document-save-as.png b/icons/16x16/document-save-as.png Binary files differnew file mode 100644 index 0000000..9bed143 --- /dev/null +++ b/icons/16x16/document-save-as.png diff --git a/icons/16x16/document-save.png b/icons/16x16/document-save.png Binary files differnew file mode 100644 index 0000000..22ff495 --- /dev/null +++ b/icons/16x16/document-save.png diff --git a/icons/cr16-action-mouse_pointer.png b/icons/cr16-action-mouse_pointer.png Binary files differnew file mode 100644 index 0000000..4fbe527 --- /dev/null +++ b/icons/cr16-action-mouse_pointer.png diff --git a/icons/cr16-action-relation_newobj.png b/icons/cr16-action-relation_newobj.png Binary files differnew file mode 100644 index 0000000..2057485 --- /dev/null +++ b/icons/cr16-action-relation_newobj.png diff --git a/icons/cr16-action-table_newobj.png b/icons/cr16-action-table_newobj.png Binary files differnew file mode 100644 index 0000000..fa728b3 --- /dev/null +++ b/icons/cr16-action-table_newobj.png diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..eac9359 --- /dev/null +++ b/main.cpp @@ -0,0 +1,13 @@ +#include <QApplication> +#include "mainwindow.h" + +int +main(int argc, char **argv) +{ + Q_INIT_RESOURCE(dbmodel); + + QApplication app(argc, argv); + MainWindow window; + window.show(); + return app.exec(); +} diff --git a/mainwindow.cpp b/mainwindow.cpp new file mode 100644 index 0000000..9d090f8 --- /dev/null +++ b/mainwindow.cpp @@ -0,0 +1,230 @@ +#include <QMenuBar> +#include <QMenu> +#include <QComboBox> +#include <QToolBar> +#include <QFileDialog> +#include <QIcon> +#include <QDebug> +#include "mainwindow.h" +#include "databasetable.h" + +MainWindow::MainWindow() + : QMainWindow(), m_model(NULL) +{ + setupActions(); + setupUi(); + newModel(); +} + +void +MainWindow::setupUi() +{ + setWindowTitle(tr("DB Model")); + resize(QSize(780, 580)); + + setupToolBar(); + setupMenuBar(); + + m_properties = new TableProperties(this); + + m_view = new DatabaseModelView(this); + + m_splitter = new QSplitter(Qt::Vertical, this); + setCentralWidget(m_splitter); + m_splitter->addWidget(m_view); + m_splitter->addWidget(m_properties); + + m_splitter->setStretchFactor(0, 6); + m_splitter->setStretchFactor(1, 1); +} + +void +MainWindow::setupActions() +{ + m_actionNew = new QAction(this); + m_actionNew->setText(tr("&New")); + m_actionNew->setIcon(QIcon(":/icons/16x16/document-new.png")); + m_actionNew->setShortcut(QKeySequence(tr("Ctrl+N"))); + connect(m_actionNew, SIGNAL(triggered(bool)), SLOT(newModel())); + + m_actionOpen = new QAction(this); + m_actionOpen->setText(tr("&Open...")); + m_actionOpen->setIcon(QIcon(":/icons/16x16/document-open.png")); + m_actionOpen->setShortcut(QKeySequence(tr("Ctrl+O"))); + connect(m_actionOpen, SIGNAL(triggered(bool)), SLOT(open())); + + m_actionSave = new QAction(this); + m_actionSave->setText(tr("&Save")); + m_actionSave->setIcon(QIcon(":/icons/16x16/document-save.png")); + m_actionSave->setShortcut(QKeySequence(tr("Ctrl+S"))); + connect(m_actionSave, SIGNAL(triggered(bool)), SLOT(save())); + + m_actionSaveAs = new QAction(this); + m_actionSaveAs->setText(tr("Save &As...")); + m_actionSaveAs->setIcon(QIcon(":/icons/16x16/document-save-as.png")); + connect(m_actionSaveAs, SIGNAL(triggered(bool)), SLOT(saveAs())); + + m_actionSwitchMode[0] = new QAction(this); + m_actionSwitchMode[0]->setText(tr("Select")); + m_actionSwitchMode[0]->setIcon(QIcon(":/icons/cr16-action-mouse_pointer.png")); + m_actionSwitchMode[0]->setCheckable(true); + m_actionSwitchMode[0]->setChecked(true); + + m_actionSwitchMode[1] = new QAction(this); + m_actionSwitchMode[1]->setText(tr("Add new table")); + m_actionSwitchMode[1]->setIcon(QIcon(":/icons/cr16-action-table_newobj.png")); + m_actionSwitchMode[1]->setCheckable(true); + + m_actionSwitchMode[2] = new QAction(this); + m_actionSwitchMode[2]->setText(tr("Add new relation")); + m_actionSwitchMode[2]->setIcon(QIcon(":/icons/cr16-action-relation_newobj.png")); + m_actionSwitchMode[2]->setCheckable(true); + + connect(m_actionSwitchMode[0], SIGNAL(triggered(bool)), SLOT(switchModeSelect())); + connect(m_actionSwitchMode[1], SIGNAL(triggered(bool)), SLOT(switchModeAddTable())); + connect(m_actionSwitchMode[2], SIGNAL(triggered(bool)), SLOT(switchModeAddRelation())); +} + +void +MainWindow::setupToolBar() +{ + QToolBar *toolBar = addToolBar(tr("&File")); + toolBar->setIconSize(QSize(16, 16)); + toolBar->addAction(m_actionNew); + toolBar->addAction(m_actionOpen); + toolBar->addSeparator(); + toolBar->addAction(m_actionSave); + toolBar->addAction(m_actionSaveAs); + toolBar = addToolBar(tr("&Mode")); + toolBar->setIconSize(QSize(16, 16)); + toolBar->addAction(m_actionSwitchMode[0]); + toolBar->addAction(m_actionSwitchMode[1]); + toolBar->addAction(m_actionSwitchMode[2]); + + QComboBox *sceneScaleCombo = new QComboBox; + QStringList scales; + scales << tr("50%") << tr("70%") << tr("85%") << tr("100%") << tr("125%") << tr("150%"); + sceneScaleCombo->addItems(scales); + sceneScaleCombo->setEditable(true); + sceneScaleCombo->setCurrentIndex(3); + connect(sceneScaleCombo, SIGNAL(currentIndexChanged(const QString &)), + this, SLOT(setViewScale(const QString &))); + + toolBar = addToolBar(tr("View")); + toolBar->addWidget(sceneScaleCombo); + +} + +void +MainWindow::setViewScale(const QString &scale) +{ + double newScale = scale.left(scale.indexOf(tr("%"))).toDouble() / 100.0; + QMatrix oldMatrix = m_view->matrix(); + m_view->resetMatrix(); + m_view->translate(oldMatrix.dx(), oldMatrix.dy()); + m_view->scale(newScale, newScale); +} + +void +MainWindow::setupMenuBar() +{ + QMenu *menu = menuBar()->addMenu("&File"); + menu->addAction(m_actionNew); + menu->addAction(m_actionOpen); + menu->addSeparator(); + menu->addAction(m_actionSave); + menu->addAction(m_actionSaveAs); + menu->addSeparator(); + menu->addAction(tr("&Quit"), this, SLOT(close()), QKeySequence(tr("Ctrl+Q"))); + + menu = menuBar()->addMenu("&Edit"); + menu->addAction(tr("&Delete"), this, SLOT(deleteSelectedItems()), QKeySequence(tr("Del"))); +} + +void +MainWindow::deleteSelectedItems() +{ + m_model->deleteSelectedItems(); +} + +void +MainWindow::open() +{ + QString fileName = QFileDialog::getOpenFileName(this); + if (!fileName.isNull()) { + DatabaseModel *model = new DatabaseModel(this); + model->load(fileName); + newModel(model); + } +} + +void +MainWindow::save() +{ + QString fileName = m_model->fileName(); + if (fileName.isEmpty()) { + saveAs(); + } + else { + m_model->save(fileName); + } +} + +void +MainWindow::saveAs() +{ + QString fileName = QFileDialog::getSaveFileName(this, QString(), m_model->fileName()); + if (!fileName.isNull()) { + m_model->save(fileName); + } +} + +void +MainWindow::newModel(DatabaseModel *newModel) +{ + if (!newModel) + newModel = new DatabaseModel(this); + + m_view->setScene(newModel); + if (m_model) + delete m_model; + m_model = newModel; + + connect(m_model, + SIGNAL(modeChanged(DatabaseModel::Mode)), + SLOT(updateMode(DatabaseModel::Mode))); + connect(m_model, SIGNAL(selectionChanged()), SLOT(updateSelection())); +} + +void +MainWindow::updateSelection() +{ + DatabaseTable *table = m_model->selectedTable(); + m_properties->setTable(table); +} + +void +MainWindow::updateMode(DatabaseModel::Mode mode) +{ + m_actionSwitchMode[0]->setChecked(mode == DatabaseModel::Select); + m_actionSwitchMode[1]->setChecked(mode == DatabaseModel::AddTable); + m_actionSwitchMode[2]->setChecked(mode == DatabaseModel::AddRelation); +} + +void +MainWindow::switchModeSelect() +{ + m_model->setMode(DatabaseModel::Select); +} + +void +MainWindow::switchModeAddTable() +{ + m_model->setMode(DatabaseModel::AddTable); +} + +void +MainWindow::switchModeAddRelation() +{ + m_model->setMode(DatabaseModel::AddRelation); +} diff --git a/mainwindow.h b/mainwindow.h new file mode 100644 index 0000000..c535b94 --- /dev/null +++ b/mainwindow.h @@ -0,0 +1,54 @@ +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include <QMainWindow> +#include <QSplitter> +#include <QStackedWidget> +#include <QAction> +#include "databasemodelview.h" +#include "databasemodel.h" +#include "tableproperties.h" + +class MainWindow: public QMainWindow +{ + Q_OBJECT + +public: + MainWindow(); + +public slots: + void newModel(DatabaseModel *newModel = 0); + + void updateMode(DatabaseModel::Mode mode); + void switchModeSelect(); + void switchModeAddTable(); + void switchModeAddRelation(); + + void updateSelection(); + void deleteSelectedItems(); + + void open(); + void save(); + void saveAs(); + void setViewScale(const QString &scale); + +protected: + void setupUi(); + void setupActions(); + void setupToolBar(); + void setupMenuBar(); + +private: + QSplitter *m_splitter; + TableProperties *m_properties; + DatabaseModelView *m_view; + DatabaseModel *m_model; + + QAction *m_actionSwitchMode[3]; + QAction *m_actionNew; + QAction *m_actionOpen; + QAction *m_actionSave; + QAction *m_actionSaveAs; +}; + +#endif diff --git a/tableproperties.cpp b/tableproperties.cpp new file mode 100644 index 0000000..4b2e5de --- /dev/null +++ b/tableproperties.cpp @@ -0,0 +1,171 @@ +#include "tableproperties.h" +#include "databasetable.h" +#include "column.h" +#include <QDebug> + +TableProperties::TableProperties(QWidget *parent) + : QWidget(parent) +{ + ui.setupUi(this); + setTable(0); + connect(ui.nameEdit, + SIGNAL(textEdited(const QString &)), + SLOT(setSelectedTableName(const QString &))); + connect(ui.columnsWidget, + SIGNAL(itemChanged(QTreeWidgetItem *, int)), + SLOT(setSelectedTableColumnName(QTreeWidgetItem *, int))); + connect(ui.addColumnButton, SIGNAL(clicked()), SLOT(addColumn())); + connect(ui.removeColumnButton, SIGNAL(clicked()), SLOT(removeColumn())); + connect(ui.moveColumnUpButton, SIGNAL(clicked()), SLOT(moveColumnUp())); + connect(ui.moveColumnDownButton, SIGNAL(clicked()), SLOT(moveColumnDown())); + connect(ui.columnsWidget, SIGNAL(itemSelectionChanged()), SLOT(updateColumnSelection())); +} + +void +TableProperties::setSelectedTableName(const QString &name) +{ + if (m_table) { + m_table->setName(name); + } +} + +void +setColumnItem(QTreeWidgetItem *item, Column *column) +{ + item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled| Qt::ItemIsEditable); + item->setText(0, column->name()); + item->setText(1, column->dataType()); + item->setText(2, column->isRequired() ? "Y" : "N"); + item->setText(3, column->isPrimaryKey() ? "Y" : "N"); + item->setText(4, column->notes()); +} + +void +TableProperties::setSelectedTableColumnName(QTreeWidgetItem *item, int column) +{ + if (m_table) { + QString text = item->text(column); + int index = ui.columnsWidget->indexOfTopLevelItem(item); + if (column == 0) { + m_table->column(index)->setName(text); + } + else if (column == 1) { + m_table->column(index)->setDataType(text); + } + else if (column == 2) { + m_table->column(index)->setRequired(text.toLower() == "y"); + setColumnItem(item, m_table->column(index)); + } + else if (column == 3) { + m_table->column(index)->setPrimaryKey(text.toLower() == "y"); + setColumnItem(item, m_table->column(index)); + } + else if (column == 4) { + m_table->column(index)->setNotes(text); + } + } +} + +void +TableProperties::setTable(DatabaseTable *table) +{ + m_table = NULL; + if (table == NULL) { + setEnabled(false); + ui.nameEdit->clear(); + ui.columnsWidget->clear(); + } + else { + setEnabled(true); + ui.nameEdit->setText(table->name()); + ui.columnsWidget->clear(); + foreach (Column *column, table->columns()) { + QTreeWidgetItem *item = new QTreeWidgetItem(ui.columnsWidget); + setColumnItem(item, column); + } + } + m_table = table; + updateColumnSelection(); +} + +void +TableProperties::addColumn() +{ + if (!m_table) + return; + + QString name = QString("column%0").arg(m_table->columnCount() + 1); + Column *column = m_table->addColumn(name); + QTreeWidgetItem *item = new QTreeWidgetItem(ui.columnsWidget); + setColumnItem(item, column); + ui.columnsWidget->setCurrentItem(item, 0); + ui.columnsWidget->editItem(item, 0); +} + +void +TableProperties::removeColumn() +{ + if (!m_table) + return; + + QList<QTreeWidgetItem *> items = ui.columnsWidget->selectedItems(); + foreach (QTreeWidgetItem *item, items) { + int index = ui.columnsWidget->indexOfTopLevelItem(item); + ui.columnsWidget->takeTopLevelItem(index); + m_table->removeColumn(index); + } +} + +void +TableProperties::moveColumnUp() +{ + if (m_table) { + QList<QTreeWidgetItem *> items = ui.columnsWidget->selectedItems(); + if (items.size() == 1) { + QTreeWidgetItem *item = items[0]; + int index = ui.columnsWidget->indexOfTopLevelItem(item); + if (index > 0) { + ui.columnsWidget->takeTopLevelItem(index); + ui.columnsWidget->insertTopLevelItem(index - 1, item); + ui.columnsWidget->setCurrentItem(item); + m_table->swapColumns(index, index - 1); + } + } + } +} + +void +TableProperties::moveColumnDown() +{ + if (m_table) { + QList<QTreeWidgetItem *> items = ui.columnsWidget->selectedItems(); + if (items.size() == 1) { + QTreeWidgetItem *item = items[0]; + int index = ui.columnsWidget->indexOfTopLevelItem(item); + if (index + 1 < ui.columnsWidget->topLevelItemCount()) { + ui.columnsWidget->takeTopLevelItem(index); + ui.columnsWidget->insertTopLevelItem(index + 1, item); + ui.columnsWidget->setCurrentItem(item); + m_table->swapColumns(index, index + 1); + } + } + } +} + +void +TableProperties::updateColumnSelection() +{ + QList<QTreeWidgetItem *> items = ui.columnsWidget->selectedItems(); + if (items.isEmpty()) { + ui.removeColumnButton->setEnabled(false); + ui.moveColumnUpButton->setEnabled(false); + ui.moveColumnDownButton->setEnabled(false); + } + else { + QTreeWidgetItem *item = items[0]; + int index = ui.columnsWidget->indexOfTopLevelItem(item); + ui.removeColumnButton->setEnabled(true); + ui.moveColumnUpButton->setEnabled(index > 0); + ui.moveColumnDownButton->setEnabled(index + 1 < ui.columnsWidget->topLevelItemCount()); + } +} diff --git a/tableproperties.h b/tableproperties.h new file mode 100644 index 0000000..99e8fdb --- /dev/null +++ b/tableproperties.h @@ -0,0 +1,33 @@ +#ifndef TABLEPROPERTIES_H +#define TABLEPROPERTIES_H + +#include <QWidget> +#include "ui_tableproperties.h" + +class DatabaseTable; + +class TableProperties : public QWidget +{ + Q_OBJECT + +public: + TableProperties(QWidget *parent = 0); + + DatabaseTable *table() { return m_table; } + void setTable(DatabaseTable *table); + +protected slots: + void setSelectedTableName(const QString &name); + void setSelectedTableColumnName(QTreeWidgetItem *item, int column); + void addColumn(); + void removeColumn(); + void moveColumnUp(); + void moveColumnDown(); + void updateColumnSelection(); + +private: + DatabaseTable *m_table; + Ui_TableProperties ui; +}; + +#endif diff --git a/tableproperties.ui b/tableproperties.ui new file mode 100644 index 0000000..c3f0599 --- /dev/null +++ b/tableproperties.ui @@ -0,0 +1,155 @@ +<ui version="4.0" > + <class>TableProperties</class> + <widget class="QWidget" name="TableProperties" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>471</width> + <height>192</height> + </rect> + </property> + <layout class="QVBoxLayout" name="verticalLayout" > + <property name="margin" > + <number>0</number> + </property> + <item> + <widget class="QTabWidget" name="tabWidget" > + <property name="currentIndex" > + <number>0</number> + </property> + <widget class="QWidget" name="tab" > + <attribute name="title" > + <string>&Definition</string> + </attribute> + <layout class="QGridLayout" name="gridLayout_2" > + <item row="0" column="0" > + <widget class="QLabel" name="label" > + <property name="text" > + <string>Name:</string> + </property> + </widget> + </item> + <item row="0" column="1" > + <widget class="QLineEdit" name="nameEdit" /> + </item> + <item row="1" column="0" colspan="2" > + <spacer name="verticalSpacer" > + <property name="orientation" > + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0" > + <size> + <width>158</width> + <height>113</height> + </size> + </property> + </spacer> + </item> + </layout> + </widget> + <widget class="QWidget" name="tab_2" > + <attribute name="title" > + <string>&Columns</string> + </attribute> + <layout class="QGridLayout" name="gridLayout_3" > + <item rowspan="5" row="0" column="0" > + <widget class="QTreeWidget" name="columnsWidget" > + <property name="rootIsDecorated" > + <bool>false</bool> + </property> + <property name="itemsExpandable" > + <bool>false</bool> + </property> + <property name="expandsOnDoubleClick" > + <bool>false</bool> + </property> + <column> + <property name="text" > + <string>Name</string> + </property> + </column> + <column> + <property name="text" > + <string>Data Type</string> + </property> + </column> + <column> + <property name="text" > + <string>Req'd</string> + </property> + </column> + <column> + <property name="text" > + <string>PK</string> + </property> + </column> + <column> + <property name="text" > + <string>Notes</string> + </property> + </column> + </widget> + </item> + <item row="0" column="1" > + <widget class="QPushButton" name="addColumnButton" > + <property name="toolTip" > + <string>Add new column</string> + </property> + <property name="text" > + <string>&Add</string> + </property> + </widget> + </item> + <item row="1" column="1" > + <widget class="QPushButton" name="removeColumnButton" > + <property name="toolTip" > + <string>Remove selected column</string> + </property> + <property name="text" > + <string>&Remove</string> + </property> + </widget> + </item> + <item row="2" column="1" > + <widget class="QPushButton" name="moveColumnUpButton" > + <property name="toolTip" > + <string>Move selected column up</string> + </property> + <property name="text" > + <string>Move &Up</string> + </property> + </widget> + </item> + <item row="3" column="1" > + <widget class="QPushButton" name="moveColumnDownButton" > + <property name="toolTip" > + <string>Move selected column down</string> + </property> + <property name="text" > + <string>Move &Down</string> + </property> + </widget> + </item> + <item row="4" column="1" > + <spacer name="verticalSpacer_2" > + <property name="orientation" > + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0" > + <size> + <width>17</width> + <height>21</height> + </size> + </property> + </spacer> + </item> + </layout> + </widget> + </widget> + </item> + </layout> + </widget> + <resources/> + <connections/> +</ui> |