///////////////////////////////////////////////////////////////////////////// // Name: dbentity.cpp // Purpose: Database Objects // Author: Daniel Horak // Modified by: // RCS-ID: $Id: dbentity.cc,v 1.7 2004/01/04 18:32:16 horakdan Exp $ // Copyright: (c) Daniel Horak // Licence: GPL ///////////////////////////////////////////////////////////////////////////// // ============================================================================ // declarations // ============================================================================ // ---------------------------------------------------------------------------- // headers // ---------------------------------------------------------------------------- // For compilers that support precompilation, includes "wx/wx.h". #include #ifdef __BORLANDC__ #pragma hdrstop #endif // for all others, include the necessary headers (this file is usually all you // need because it includes almost all "standard" wxWindows headers #ifndef WX_PRECOMP #include #endif #include #include #include "config.h" #include "xml.h" #include "dbobject.h" #include "dbentity.h" #include "dbattribute.h" #include "dbindex.h" #include "dbtrigger.h" #include "dbconstraint.h" #include "dbsimpleattr.h" #include "dbmodelentity.h" #include "dbrelation.h" #include "schema.h" #include "project.h" DBEntity::DBEntity(DataDesignerProject *project, DataDesignerContainer *container) :DBObject(DBEntityType, "entity", project, container), m_attrs(NULL), m_indexes(NULL), m_triggers(NULL), m_constraints(NULL), m_placed(FALSE), m_xpos(100), m_ypos(100) { } DBEntity::~DBEntity() { if (m_attrs) { m_project->DeleteChildren(m_attrs->GetTreeItemId()); delete m_attrs; } if (m_indexes) { m_project->DeleteChildren(m_indexes->GetTreeItemId()); delete m_indexes; } if (m_triggers) { m_project->DeleteChildren(m_triggers->GetTreeItemId()); delete m_triggers; } if (m_constraints) { m_project->DeleteChildren(m_constraints->GetTreeItemId()); delete m_constraints; } wxDBObjectListNode *node; // must delete all modelentities derived from this real entity node = m_modelentities.GetFirst(); while (node) { delete node->GetData(); node = node->GetNext(); } } void DBEntity::LoadXmlNode(wxXmlNode *node) { wxXmlNode *child; wxString number; if (node->GetName() == m_typestr) { DBObject::LoadXmlNode(node); number = node->GetPropVal("xpos", "100"); m_xpos = wxAtoi(number); number = node->GetPropVal("ypos", "100"); m_ypos = wxAtoi(number); child = node->GetChildren(); while (child) { if (child->GetName() == "attributes") { m_attrs->LoadXmlNode(child); } else if (child->GetName() == "indexes") { m_indexes->LoadXmlNode(child); } else if (child->GetName() == "triggers") { m_triggers->LoadXmlNode(child); } else if (child->GetName() == "constraints") { m_constraints->LoadXmlNode(child); } child = child->GetNext(); } } else { wxLogMessage("wrong type '%s'", node->GetName().c_str()); } } wxTreeItemId DBEntity::AppendItem() { DataDesignerSchema *schema; if (! m_appended) { m_treeitemid = m_project->AppendItem(m_container->GetTreeItemId(), m_name, -1, -1, new DataDesignerItemData(this)); m_attrs = new DBAttributeContainer(m_project, m_project->AppendItem(m_treeitemid, _("Attributes"))); m_indexes = new DBIndexContainer(m_project, m_project->AppendItem(m_treeitemid, _("Indexes"))); m_triggers = new DBTriggerContainer(m_project, m_project->AppendItem(m_treeitemid, _("Triggers"))); m_constraints = new DBConstraintContainer(m_project, m_project->AppendItem(m_treeitemid, _("Constraints"))); if ((schema = m_project->GetSchema()) != NULL) { schema->AddObject(this); schema->Refresh(); } m_appended = TRUE; } return m_treeitemid; } wxDialog *DBEntity::Editor(bool edit) { return new DBEntityEditor(this, edit); } wxXmlNode *DBEntity::GetXmlNode() { wxXmlNode *node = DBObject::GetXmlNode(); wxString number; number.Printf("%d", m_xpos); node->AddProperty("xpos", number); number.Printf("%d", m_ypos); node->AddProperty("ypos", number); node->AddChild(m_attrs->GetXmlNode()); node->AddChild(m_indexes->GetXmlNode()); node->AddChild(m_triggers->GetXmlNode()); node->AddChild(m_constraints->GetXmlNode()); return node; } void DBEntity::CreateShape() { m_shape = new DBEntityShape(this); m_shape->Show(TRUE); } int DBEntity::GetAttributeCount() { return GetProject()->GetChildrenCount(m_attrs->GetTreeItemId(), FALSE); } DBAttribute *DBEntity::GetAttributeByName(const wxString& name) { wxLogMessage("DBEntity::GetAttributeByName - not implemented"); return NULL; } DBAttribute *DBEntity::AddAttribute(DBAttribute *attr) { DBAttribute *nattr; #ifdef ENABLE_DEBUG wxLogMessage("DBEntity::AddAttribute"); #endif if (attr == NULL) { #ifdef ENABLE_DEBUG wxLogMessage("DBEntity::AddAttribute - attr == NULL"); #endif return NULL; } nattr = (DBAttribute *)m_attrs->CreateObject(); nattr->Copy(attr); nattr->AppendItem(); return nattr; } void DBEntity::AddAttributeToPrimaryKey(DBAttribute *attr) { DBConstraint *pk; #ifdef ENABLE_DEBUG wxLogMessage("DBEntity::AddAttributeToPrimaryKey"); #endif if (attr == NULL) return; attr->m_primarykey = TRUE; pk = GetPrimaryKeyConstraint(); if (pk == NULL) { // create a PK constraint #ifdef ENABLE_DEBUG wxLogMessage("DBEntity::AddAttributeToPrimaryKey - creating PK constraint"); #endif pk = (DBConstraint *)m_constraints->CreateObject(); pk->m_type = DBO_CONSTRAINT_TYPE_PRIMARY_KEY; pk->m_name = m_name + "_pk"; pk->CreatePName(); pk->AppendItem(); } pk->AddAttribute(attr); } void DBEntity::DeleteAttributeFromPrimaryKey(DBAttribute *attr) { DBConstraint *pk; pk = GetPrimaryKeyConstraint(); if (pk != NULL) { pk->DeleteAttribute(attr->GetName()); if (pk->m_attrs->GetChildrenCount() == 0) { // if there is no attribute in PK then destroy whole PK constraint delete pk; } } } DBConstraint *DBEntity::GetPrimaryKeyConstraint() { DBConstraint *pk = NULL, *constr; long cookie; wxTreeItemId child; if (m_constraints == NULL) { wxLogMessage("DBEntity::GetPrimaryKeyConstraint - m_constraints == NULL"); return NULL; } child = GetProject()->GetFirstChild(m_constraints->GetTreeItemId(), cookie); while (child.IsOk()) { constr = (DBConstraint *)(((DataDesignerItemData *)m_project->GetItemData(child))->GetObject()); if (constr->m_type == DBO_CONSTRAINT_TYPE_PRIMARY_KEY) { pk = constr; break; } child = GetProject()->GetNextChild(m_constraints->GetTreeItemId(), cookie); } #ifdef ENABLE_DEBUG wxLogMessage("DBEntity::GetPrimaryKeyConstraint - PK was %sfound", pk ? "" : "not "); #endif return pk; } void DBEntity::AddForeignKeyConstraint(DBRelation *relation, bool identifying) { DBEntity *parent_ent; long cookie; wxTreeItemId child; DBConstraint *parent_pk; DBConstraint *fk; DBSimpleAttribute *simpleattr; DBAttribute *attr; #ifdef ENABLE_DEBUG wxLogMessage("DBEntity::AddForeignKeyConstraint - this entity '%s'", m_name.c_str()); #endif parent_ent = (DBEntity *)(GetProject()->m_top_entities->GetObjectByName(relation->m_parent)); if (parent_ent == NULL) { wxLogMessage("DBEntity::AddForeignKeyConstraint - cannot find parent entity '%s'", relation->m_parent.c_str()); return; } parent_pk = parent_ent->GetPrimaryKeyConstraint(); if (parent_pk == NULL) { wxLogMessage("DBEntity::AddForeignKeyConstraint - parent entity '%s' has no primary key", parent_ent->GetName().c_str()); return; } fk = (DBConstraint *)m_constraints->CreateObject(); fk->m_type = DBO_CONSTRAINT_TYPE_FOREIGN_KEY; fk->m_name = parent_ent->GetName() + "_fk"; fk->m_relation = relation->GetName(); fk->CreatePName(); fk->AppendItem(); // iterate through attributes in parent's PK child = GetProject()->GetFirstChild(parent_pk->GetAttributes()->GetTreeItemId(), cookie); while (child.IsOk()) { simpleattr = (DBSimpleAttribute *)(((DataDesignerItemData *)GetProject()->GetItemData(child))->GetObject()); #ifdef ENABLE_DEBUG wxLogMessage("DBEntity::AddForeignKeyConstraint - processing attr '%s'", simpleattr->GetName().c_str()); #endif attr = AddAttribute(simpleattr->GetRealAttribute()); if (attr == NULL) { wxLogFatalError("attr == NULL"); break; } attr->m_imported = TRUE; attr->m_foreignkey = TRUE; attr->m_relation = relation->GetName(); fk->AddAttribute(attr); if ((identifying == TRUE)) AddAttributeToPrimaryKey(attr); child = GetProject()->GetNextChild(parent_pk->GetTreeItemId(), cookie); } } void DBEntity::AddParentsPrimaryKey(DBRelation *relation) { DBEntity *parent_ent; long cookie; wxTreeItemId child; DBConstraint *parent_pk; DBSimpleAttribute *simpleattr; DBAttribute *attr; #ifdef ENABLE_DEBUG wxLogMessage("DBEntity::AddParentKey - this entity '%s'", m_name.c_str()); #endif parent_ent = (DBEntity *)(GetProject()->m_top_entities->GetObjectByName(relation->m_parent)); if (parent_ent == NULL) { wxLogMessage("DBEntity::AddParentKey - cannot find parent entity '%s'", relation->m_parent.c_str()); return; } parent_pk = parent_ent->GetPrimaryKeyConstraint(); if (parent_pk == NULL) { wxLogMessage("DBEntity::AddParentKey - parent entity '%s' has no primary key", parent_ent->GetName().c_str()); return; } // iterate through attributes in parent's PK child = GetProject()->GetFirstChild(parent_pk->GetAttributes()->GetTreeItemId(), cookie); while (child.IsOk()) { simpleattr = (DBSimpleAttribute *)(((DataDesignerItemData *)GetProject()->GetItemData(child))->GetObject()); #ifdef ENABLE_DEBUG wxLogMessage("DBEntity::AddParentKey - processing attr '%s'", simpleattr->GetName().c_str()); #endif attr = AddAttribute(simpleattr->GetRealAttribute()); attr->m_imported = TRUE; attr->m_foreignkey = TRUE; attr->m_relation = relation->GetName(); child = GetProject()->GetNextChild(parent_pk->GetTreeItemId(), cookie); } } void DBEntity::AddModelEntity(DBModelEntity *ent) { m_modelentities.Append(ent); } void DBEntity::DeleteModelEntity(DBModelEntity *ent) { if (m_modelentities.IndexOf(ent) != wxNOT_FOUND) { m_modelentities.DeleteObject(ent); } } /* * Editor */ BEGIN_EVENT_TABLE(DBEntityEditor, DBObjectEditor) EVT_BUTTON(wxID_APPLY, DBEntityEditor::OnApply) END_EVENT_TABLE() DBEntityEditor::DBEntityEditor(DBObject *object, bool edit) : DBObjectEditor(_("Entity"), wxSize(500,400), object, edit) { m_page_attributes = new wxPanel(m_notebook); m_page_constraints = new wxPanel(m_notebook); m_page_indexes = new wxPanel(m_notebook); m_page_triggers = new wxPanel(m_notebook); m_list_attrs = new DBAttributeListCtrl (m_page_attributes, ((DBEntity *)GetObject())->m_attrs); m_list_constraints = new DBConstraintListCtrl (m_page_constraints, ((DBEntity *)GetObject())->m_constraints); m_list_indexes = new DBIndexListCtrl (m_page_indexes, ((DBEntity *)GetObject())->m_indexes); m_list_triggers = new DBTriggerListCtrl (m_page_triggers, ((DBEntity *)GetObject())->m_triggers); if (m_edit) { ((DBEntity *)GetObject())->m_attrs->SetList(m_list_attrs); ((DBEntity *)GetObject())->m_attrs->AddObjectsToList(); ((DBEntity *)GetObject())->m_constraints->SetList(m_list_constraints); ((DBEntity *)GetObject())->m_constraints->AddObjectsToList(); ((DBEntity *)GetObject())->m_indexes->SetList(m_list_indexes); ((DBEntity *)GetObject())->m_indexes->AddObjectsToList(); ((DBEntity *)GetObject())->m_triggers->SetList(m_list_triggers); ((DBEntity *)GetObject())->m_triggers->AddObjectsToList(); } wxLayoutConstraints *c = new wxLayoutConstraints; c->top.SameAs (m_page_attributes, wxTop); c->left.SameAs (m_page_attributes, wxLeft); c->right.SameAs (m_page_attributes, wxRight); c->bottom.SameAs(m_page_attributes, wxBottom); m_list_attrs->SetConstraints(c); c = new wxLayoutConstraints; c->top.SameAs (m_page_constraints, wxTop); c->left.SameAs (m_page_constraints, wxLeft); c->right.SameAs (m_page_constraints, wxRight); c->bottom.SameAs(m_page_constraints, wxBottom); m_list_constraints->SetConstraints(c); c = new wxLayoutConstraints; c->top.SameAs (m_page_indexes, wxTop); c->left.SameAs (m_page_indexes, wxLeft); c->right.SameAs (m_page_indexes, wxRight); c->bottom.SameAs(m_page_indexes, wxBottom); m_list_indexes->SetConstraints(c); c = new wxLayoutConstraints; c->top.SameAs (m_page_triggers, wxTop); c->left.SameAs (m_page_triggers, wxLeft); c->right.SameAs (m_page_triggers, wxRight); c->bottom.SameAs(m_page_triggers, wxBottom); m_list_triggers->SetConstraints(c); m_page_attributes->SetAutoLayout(TRUE); m_page_constraints->SetAutoLayout(TRUE); m_page_indexes->SetAutoLayout(TRUE); m_page_triggers->SetAutoLayout(TRUE); m_notebook->InsertPage(m_notebook->GetPageCount() - 1, m_page_attributes, _("Attributes")); m_notebook->InsertPage(m_notebook->GetPageCount() - 1, m_page_constraints, _("Constraints")); m_notebook->InsertPage(m_notebook->GetPageCount() - 1, m_page_indexes, _("Indexes")); m_notebook->InsertPage(m_notebook->GetPageCount() - 1, m_page_triggers, _("Triggers")); m_button_apply = AddButton(wxID_APPLY, _("Apply"), wxSize(60,-1)); if (m_edit) { m_button_ok->SetDefault(); } else { m_button_apply->SetDefault(); m_page_attributes->Disable(); m_page_constraints->Disable(); m_page_indexes->Disable(); m_page_triggers->Disable(); } } DBEntityEditor::~DBEntityEditor() { } bool DBEntityEditor::TransferDataFromWindow() { DBEntity *entity = (DBEntity *)GetObject(); DBObjectEditor::TransferDataFromWindow(); return TRUE; } bool DBEntityEditor::TransferDataToWindow() { DBEntity *entity = (DBEntity *)GetObject(); DBObjectEditor::TransferDataToWindow(); return TRUE; } void DBEntityEditor::OnApply(wxCommandEvent& event) { DBObject *object; if (Validate() == FALSE) return; TransferDataFromWindow(); object = GetObject(); object->AppendItem(); m_list_attrs->SetContainer(((DBEntity *)GetObject())->m_attrs); m_list_constraints->SetContainer(((DBEntity *)GetObject())->m_constraints); m_list_indexes->SetContainer(((DBEntity *)GetObject())->m_indexes); m_list_triggers->SetContainer(((DBEntity *)GetObject())->m_triggers); m_page_attributes->Enable(); m_page_constraints->Enable(); m_page_indexes->Enable(); m_page_triggers->Enable(); m_button_apply->Disable(); m_button_ok->SetDefault(); m_edit = TRUE; } /* * Container */ DBEntityContainer::DBEntityContainer(DataDesignerProject *project, const wxTreeItemId& parent) : DataDesignerContainer(project, parent, "entities") { } DBObject *DBEntityContainer::CreateObject() { return new DBEntity(GetProject(), this); } void DBEntityContainer::ShowList() { SetList(new DBEntityListCtrl(GetProject()->GetSplitter(), this)); DataDesignerContainer::AddObjectsToListAndShow(); } /* * ObjectList */ DBEntityListCtrl::DBEntityListCtrl(wxWindow *parent, DataDesignerContainer *container) : DBObjectListCtrl(parent, container) { SetColumnWidth(0, 200); InsertColumn(1, _("Remark")); SetColumnWidth(1, 400); } DBEntityListCtrl::~DBEntityListCtrl() { } void DBEntityListCtrl::SetObject(long item, DBObject *object) { DBEntity *entity = (DBEntity *)object; } /* * DBEntityShape */ DBEntityShape::DBEntityShape(DBEntity *entity) : wxRectangleShape(), m_mentity(NULL) { Create(entity); m_xpos = entity->m_xpos; m_ypos = entity->m_ypos; } DBEntityShape::DBEntityShape(DBModelEntity *mentity) : wxRectangleShape(), m_mentity(mentity) { Create(mentity->GetRealEntity()); m_xpos = mentity->m_xpos; m_ypos = mentity->m_ypos; } void DBEntityShape::Create(DBEntity *entity) { SetClientData(entity); m_attrcount = entity->GetAttributeCount(); m_pen = wxBLACK_PEN; m_brush = wxGREEN_BRUSH; m_font = g_oglNormalFont; m_width = 100; m_height = (m_attrcount + 1) * 20 + 5; m_fixedWidth = TRUE; m_fixedHeight = TRUE; SetDefaultRegionSize(); } void DBEntityShape::OnDraw(wxDC& dc) { DBEntity *entity; wxCoord left, top; wxCoord x, y; int cnt; wxTreeItemId child, attrsid; long cookie; DBAttribute *attr; DataDesignerProject *project; wxString attr_txt; entity = (DBEntity *)GetClientData(); project = entity->GetProject(); attrsid = entity->m_attrs->GetTreeItemId(); cnt = entity->GetAttributeCount(); if (cnt != m_attrcount) { m_attrcount = cnt; m_height = (m_attrcount + 1) * 20 + 5; } left = (wxCoord)(m_xpos - m_width/2); top = (wxCoord)(m_ypos - m_height/2); wxRectangleShape::OnDraw(dc); dc.SetFont(*m_font); x = left + 5; y = top + 5; dc.DrawText(entity->m_name, x, y); y = (wxCoord)(top + 20); dc.DrawLine(left, y, left + (wxCoord)m_width, y); y += 5; child = project->GetFirstChild(attrsid, cookie); while (child.IsOk()) { attr = (DBAttribute *)(((DataDesignerItemData *)(project->GetItemData(child)))->GetObject()); attr_txt = attr->m_name; if (attr->m_primarykey || attr->m_foreignkey) { attr_txt.Append(' '); if (attr->m_primarykey) attr_txt.Append('P'); if (attr->m_foreignkey) attr_txt.Append('F'); attr_txt.Append('K'); } dc.DrawText(attr_txt, left + 5, y); y += 20; child = project->GetNextChild(attrsid, cookie); } #if 0 Move(dc, GetX(), GetY()); // redraw ends of relations too #endif } void DBEntityShape::OnEndDragLeft(double x, double y, int keys, int attachment) { DBEntity *entity; wxRectangleShape::OnEndDragLeft(x, y, keys, attachment); entity = (DBEntity *)GetClientData(); if (m_mentity) { m_mentity->m_xpos = (wxCoord)m_xpos; m_mentity->m_ypos = (wxCoord)m_ypos; } else { entity->m_xpos = (wxCoord)m_xpos; entity->m_ypos = (wxCoord)m_ypos; } entity->GetProject()->GetSplitter()->GetView()->GetDocument()->Modify(TRUE); } void DBEntityShape::OnLeftDoubleClick(double x, double y, int keys, int attachment) { DBObject *object; wxDialog *editor; wxRectangleShape::OnLeftDoubleClick(x, y, keys, attachment); object = (DBObject *)GetClientData(); editor = object->Editor(TRUE); if (editor) { editor->ShowModal(); editor->Destroy(); } } void DBEntityShape::OnLeftClick(double x, double y, int keys, int attachment) { DBObject *object; wxRectangleShape::OnLeftClick(x, y, keys, attachment); #ifdef NOT_USED_YET wxClientDC dc(GetCanvas()); GetCanvas()->PrepareDC(dc); object = (DBObject *)GetClientData(); if (keys == 0) { bool selected = Selected(); selected = !selected; Select(selected, &dc); // object->GetProject()->GetSchema()->SelectAll(FALSE); } else if (keys & KEY_SHIFT) { if (Selected()) { Select(FALSE, &dc); } else { Select(TRUE, &dc); } } #endif } void DBEntityShape::OnBeginDragRight(double x, double y, int keys, int attachment) { wxRectangleShape::OnBeginDragRight(x, y, keys, attachment); attachment = 0; wxClientDC dc(GetCanvas()); GetCanvas()->PrepareDC(dc); wxPen dottedPen(wxColour(0,0,0), 1, wxDOT); dc.SetLogicalFunction(OGLRBLF); dc.SetPen(dottedPen); double xp, yp; GetAttachmentPosition(attachment, &xp, &yp); dc.DrawLine((wxCoord) xp, (wxCoord) yp, (wxCoord) x, (wxCoord) y); GetCanvas()->CaptureMouse(); DBObject *object = (DBObject *)GetClientData(); } void DBEntityShape::OnDragRight(bool draw, double x, double y, int keys, int attachment) { wxRectangleShape::OnDragRight(draw, x, y, keys, attachment); attachment = 0; wxClientDC dc(GetCanvas()); GetCanvas()->PrepareDC(dc); wxPen dottedPen(wxColour(0, 0, 0), 1, wxDOT); dc.SetLogicalFunction(OGLRBLF); dc.SetPen(dottedPen); double xp, yp; GetAttachmentPosition(attachment, &xp, &yp); dc.DrawLine((wxCoord) xp, (wxCoord) yp, (wxCoord) x, (wxCoord) y); } void DBEntityShape::OnEndDragRight(double x, double y, int keys, int attachment) { wxRectangleShape::OnEndDragRight(x, y, keys, attachment); GetCanvas()->ReleaseMouse(); int new_attachment; wxShape *other = GetCanvas()->FindFirstSensitiveShape(x, y, &new_attachment, OP_DRAG_RIGHT); if (other == NULL) { // there is no object on the end, so exit return; } DBObject *object_from = (DBObject *)GetClientData(); DBObject *object_to = (DBObject *)(other->GetClientData()); if (!object_to || (object_to->GetType() != DBEntityType) || (object_to == object_from)) { #ifdef ENABLE_DEBUG wxLogMessage("DBEntityShape::OnEndDragRight - no end for relation"); #endif return; } DBRelation *relation = (DBRelation *)(object_from->GetProject()->m_top_relations->CreateObject()); relation->m_parent = object_from->GetName(); relation->m_child = object_to->GetName(); relation->m_name = object_from->m_pname + "_" + object_to->m_pname; relation->AppendItem(); wxDialog *editor = relation->Editor(TRUE); if (editor) { if (editor->ShowModal() != wxID_OK) { object_from->GetProject()->Delete(relation->GetTreeItemId()); delete relation; relation = NULL; } editor->Destroy(); if (relation) { // copy PK from parent as FK into child relation->LinkAttributes(); // attach relation to model iff schema is a model } } }