summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.bzrignore6
-rw-r--r--column.cpp55
-rw-r--r--column.h37
-rw-r--r--databasemodel.cpp283
-rw-r--r--databasemodel.h60
-rw-r--r--databasemodelitem.cpp6
-rw-r--r--databasemodelitem.h16
-rw-r--r--databasemodelview.cpp67
-rw-r--r--databasemodelview.h31
-rw-r--r--databaserelation.cpp126
-rw-r--r--databaserelation.h44
-rw-r--r--databasetable.cpp176
-rw-r--r--databasetable.h50
-rw-r--r--dbmodel.pro22
-rw-r--r--dbmodel.qrc11
-rw-r--r--icons/16x16/document-new.pngbin0 -> 477 bytes
-rw-r--r--icons/16x16/document-open.pngbin0 -> 537 bytes
-rw-r--r--icons/16x16/document-save-as.pngbin0 -> 866 bytes
-rw-r--r--icons/16x16/document-save.pngbin0 -> 911 bytes
-rw-r--r--icons/cr16-action-mouse_pointer.pngbin0 -> 483 bytes
-rw-r--r--icons/cr16-action-relation_newobj.pngbin0 -> 763 bytes
-rw-r--r--icons/cr16-action-table_newobj.pngbin0 -> 612 bytes
-rw-r--r--main.cpp13
-rw-r--r--mainwindow.cpp230
-rw-r--r--mainwindow.h54
-rw-r--r--tableproperties.cpp171
-rw-r--r--tableproperties.h33
-rw-r--r--tableproperties.ui155
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 &notes)
+{
+ 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 &notes);
+
+ 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
new file mode 100644
index 0000000..4c3efdd
--- /dev/null
+++ b/icons/16x16/document-new.png
Binary files differ
diff --git a/icons/16x16/document-open.png b/icons/16x16/document-open.png
new file mode 100644
index 0000000..69dd8d4
--- /dev/null
+++ b/icons/16x16/document-open.png
Binary files differ
diff --git a/icons/16x16/document-save-as.png b/icons/16x16/document-save-as.png
new file mode 100644
index 0000000..9bed143
--- /dev/null
+++ b/icons/16x16/document-save-as.png
Binary files differ
diff --git a/icons/16x16/document-save.png b/icons/16x16/document-save.png
new file mode 100644
index 0000000..22ff495
--- /dev/null
+++ b/icons/16x16/document-save.png
Binary files differ
diff --git a/icons/cr16-action-mouse_pointer.png b/icons/cr16-action-mouse_pointer.png
new file mode 100644
index 0000000..4fbe527
--- /dev/null
+++ b/icons/cr16-action-mouse_pointer.png
Binary files differ
diff --git a/icons/cr16-action-relation_newobj.png b/icons/cr16-action-relation_newobj.png
new file mode 100644
index 0000000..2057485
--- /dev/null
+++ b/icons/cr16-action-relation_newobj.png
Binary files differ
diff --git a/icons/cr16-action-table_newobj.png b/icons/cr16-action-table_newobj.png
new file mode 100644
index 0000000..fa728b3
--- /dev/null
+++ b/icons/cr16-action-table_newobj.png
Binary files differ
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>&amp;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>&amp;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>&amp;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>&amp;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 &amp;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 &amp;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>