Logo Search packages:      
Sourcecode: tcm version File versions  Download package

diagram.c

////////////////////////////////////////////////////////////////////////////////
//
// This file is part of Toolkit for Conceptual Modeling (TCM).
// (c) copyright 1995, Vrije Universiteit Amsterdam.
// Author: Frank Dehne (frank@cs.vu.nl).
//
// TCM is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// TCM is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with TCM; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
// 02111-1307, USA.
////////////////////////////////////////////////////////////////////////////////
#include "graph.h"
#include "diagramstubs.h"
#include "texteditdialog.h"
#include "questiondialog.h"
#include "finddialog.h"
#include "replacedialog.h"
#include "node.h"
#include "edge.h"
#include "line.h"
#include "nodeshape.h"
#include "assoclist.h"
#include "inputfile.h"
#include "outputfile.h"
#include <stdlib.h>
#include <stdio.h>
#include "diagram.h"
#include "buffer.h"
#include "subjects.h"
#include "shapes.h"
#include "menu.h"

Sequence Diagram::sequence;

Diagram::Diagram(Config *c, DiagramWindow *d, DiagramViewer *v, Graph *g): 
             Document(c,d,v) {
      dviewer = v;
      graph = g;
      maxid = 0;
      assocList = new AssocList();
      annotationSubject = 0;
      GetEltAnnotationDialog()->
            SetOKCallback(DiagramStubs::SubjectAnnotationOKCB, this);
}

Diagram::~Diagram() {
      delete graph;
      assocList->clear();
      delete assocList;
}

00062 void Diagram::Initialize() {
      Document::Initialize();
      sequence.Reset();
      dviewer->Initialize();
}

00068 bool Diagram::CheckConnection(Subject *n1, Subject *n2) {
      if (!graph->CheckConnection(
            n1->GetClassType(), n2->GetClassType(), GetEdgeType())) {
            ShowDialog(MessageDialog::ERROR, "Error", 
                     "This is not a valid connection");
            return False;
      }
      return True;
}

00078 void Diagram::AddSubject(Subject *s){
      if (s->IsEdge())
            graph->AddEdge((Edge *)s);
      else
            graph->AddNode((Node *)s);
}

00085 void Diagram::RemoveSubject(Subject *s){
      if (s->IsEdge())
            graph->RemoveEdge((Edge *)s);
      else
            graph->RemoveNode((Node *)s);
}

00092 void Diagram::AddSubjects(List<Subject *> *l) {
      for (l->first(); !l->done(); l->next()) {
            Subject *s = l->cur();
            AddSubject(s);
            if (!s->IsEdge()) {
                  Node *n = (Node *)s;
                  if (HasIndexNode(n->GetClassType()) &&
                      *n->GetIndex() != "")
                        SetNextIndex((Node *)s);
            }
      }
}

00105 void Diagram::RemoveSubjects(List<Subject *> *l) {
      for (l->first(); !l->done(); l->next())
            RemoveSubject(l->cur());
}

00110 void Diagram::CompleteSubjects(List<Subject *> *l) {
      graph->CompleteSubjects(l);
}

00114 void Diagram::CompleteEdges(List<Subject *> *l) {
      graph->CompleteEdges(l);
}

00118 void Diagram::CompleteSubject(List<Subject *> *l, Subject *s) {
      graph->CompleteSubject(l, s);
}

00122 void Diagram::RemoveAll() {
      assocList->clear();
      Buffer *buffer = dviewer->GetBuffer();
      Shapes *shapes = 0;
        Subjects *subjects = 0;
      // make a backup of old buffer contents.
      // this is because DeleteCommands deletes the buffer contents.
        if (buffer->GetSubjects())
                  subjects = buffer->GetSubjectsCopy();
        if (buffer->GetShapes())
                  shapes = buffer->GetShapesCopy();
      if (!subjects)
                  subjects = new Subjects(assocList);
      if (!shapes)
                  shapes = new Shapes(assocList);
      DeleteDiagram();
      // restore buffer contents.
        buffer->Put(subjects, shapes);
      buffer->SetUser(0);
      delete subjects;
      delete shapes;
}

void Diagram::DeleteDiagram() {
      // delete all commands & prevent undo.
      dviewer->DeleteCommands();
      // delete all shapes and views.
      dviewer->DeleteAllViews();
      // extra delete for graph elements without shapes.
      graph->ClearNodes();
      graph->ClearEdges();
}

00155 bool Diagram::RedirectEdge(Line *line, bool first, const Point *pt) {
      // Does pt hit a node shape other than origShape?
      ShapeView *view = line->GetView();
      NodeShape *newShape = view->HitNodeShape(pt->x,pt->y);
      GShape *origShape;
      if (first)
            origShape = line->GetFromShape();
      else
            origShape = line->GetToShape();
      if (newShape && newShape != origShape) {
            if (newShape->IsLine() && ((Line*)newShape)->IsCurved())
                  return False;
            Edge *edge = (Edge *)line->GetSubject();
            GShape *end1 = line->GetFromShape();
            GShape *end2 = line->GetToShape();
            Subject *subject = newShape->GetSubject();
            Subject *subject1 = edge->GetSubject1();
            Subject *subject2 = edge->GetSubject2();
            if (first) {
                  // redirect subject1 to subject.
                  if (line->NrPoints()<=3 && newShape==end2)
                        return False;
                  int tmpEdgeType = GetEdgeType();
                  SetEdgeType(edge->GetClassType());
                  Edge *tmpEdge;
                  if ((tmpEdge = CreateEdge(subject, subject2))) {
                        line->SetFromShape(newShape);
                        edge->SetSubject1(subject);
                        SetEdgeType(tmpEdgeType);
                        delete tmpEdge;
                  }
                  else {
                        SetEdgeType(tmpEdgeType);
                        return False;
                  }
            }
            else  {
                  // redirect subject2 to subject.
                  if (line->NrPoints()<=3 && newShape==end1)
                        return False;
                  int tmpEdgeType = GetEdgeType();
                  SetEdgeType(edge->GetClassType());
                  Edge *tmpEdge;
                  if ((tmpEdge = CreateEdge(subject1, subject))) {
                        line->SetToShape(newShape);
                        edge->SetSubject2(subject);
                        SetEdgeType(tmpEdgeType);
                        delete tmpEdge;
                  }
                  else {
                        SetEdgeType(tmpEdgeType);
                        return False;
                  }
            }
            string subjName = *edge->GetName();
            if (subjName != "") {
                  // Check edge name constraint.
                  string empty("");
                  (void)edge->SetName(&empty);
                  if (!SetSubjectName(edge, &subjName)) {
                        // restore old situation.
                        line->SetFromShape(end1);
                        edge->SetSubject1(subject1);
                        line->SetToShape(end2);
                        edge->SetSubject2(subject2);
                        (void)edge->SetName(&subjName);
                  }
            }
            // update order-count of straight redirected line.

            return True;
      }
      return False;
}

00230 void Diagram::SelectSubject(Subject *s) {
      List<GShape *> shapes;
      dviewer->GetShapes(s, &shapes);
      for (shapes.first(); !shapes.done(); shapes.next()) {
            GShape *shape = shapes.cur();
            shape->GetView()->SelectShape(shape);
      }
}

00239 void Diagram::SelectSubjects(List<Subject *> *s) {
      for (s->first(); !s->done(); s->next())
            SelectSubject(s->cur());
}

00244 void Diagram::Move(MoveType t){ 
      dviewer->MoveAllShapes(t);
}

00248 void Diagram::LoadEntries() {
      int classNr;
      unsigned long value;
      string wrd;
      dviewer->TextModeOff();
      maxid = 0;
      if (IsAppending()) {
            // keep all existing things (but deselected).
            dviewer->DeselectAll();
      }
      else {
            // delete the entire document contents.
            RemoveAll();
            //
            Initialize();
      }
      // delete assoc objects.
      assocList->clear();
      viewsRead.empty();
      InputFile *ipf = GetInputFile(); 
      while(!ipf->Eof()) {
            // read type of the Thing
            if (!ipf->ReadWord(&wrd))
                  break;
            classNr = Code::GetCode(wrd.getstr());
            // workaround for old style Dashed shape types.
            // without explicit line style.
            bool dashedShape = False;
            if (GetLoadFormat() <= 1.20) {
                  if (wrd.contains("Dashed"))
                        dashedShape = True;
            }
            if (classNr == 0) {
                  error("%s, line %d: unknown type name %s\n", 
                        ipf->GetFileName()->getstr(), 
                        ipf->LineNumber(), wrd.getstr());
                  break;
            }
            // read Thing ID.
            if (!ipf->ReadWord(&wrd))
                  break;
            value = wrd.toulong();
            if (!ipf->LookupChar('{'))
                  break;
            if (ipf->Eof())
                  break;
            // create and read rest of thing
            ReadThing(classNr, value, dashedShape);
            if (!ipf->LookupChar('}'))
                  break;
      }
      // place the views that are read in diagramviewer.
      PlaceViews();
      // place shapes in view the views read
      PlaceShapes();
      if (IsAppending()) {
                // appended shapes were selected.
            // so simply do a cut+paste.
                dviewer->Cut();
                dviewer->Paste();
        }
        else
                dviewer->Refresh();
      // adjust grafport for the new shapes.
      FitShapes();
      // delete views that were not be made part of diagramviewer.
      PurgeViews();
      /// Check all shapes in all views.
      dviewer->CheckShapes();
      // Check all subjects in the graph.
      CheckSubjects();
      // Set sequence number to highest id that was read.
      if (!IsAppending())
            sequence.Set(maxid);
      // delete assoc objects.
      assocList->clear();
}

void Diagram::ReadThing(int classNr, unsigned long value, bool dashedShape) {
      Thing *thing; 
      Assoc *assoc;
      int type = 0;
      thing = CreateThing(classNr);
      if (thing) {
            if (thing->IsView()) {
                  type = Thing::VIEW;
                  viewsRead.add((ShapeView *)thing);
            }
            else if (thing->IsSubject())
                  type = Thing::SUBJECT;
            else if (thing->IsShape())
                  type = Thing::SHAPE;
            else {
                  delete thing;
                  thing = 0;
            }
      }
      InputFile *ipf = GetInputFile(); 
      if (thing && thing->ReadMembers(ipf, GetLoadFormat())) {
            assoc = new Assoc((void *)value);
            // Check for double ids.
            if (assocList->Get(assoc)) {
                  error("%s, line %d: %ld already used as "
                        "an id of a thing\n", 
                        ipf->GetFileName()->getstr(), 
                        ipf->LineNumber(), value);
                  delete assoc;
                  delete thing;
                  return;
            }
            if (!IsAppending()) {
                  // keep the same ids as in the file.
                  thing->SetId(value);
                  maxid = max(value, maxid);
            }
            if (dashedShape && (check(type = Thing::SHAPE))) {
                  if (check(!((Shape *)thing)->IsTextShape()))  
                        ((GShape *)thing)->
                              SetLineStyle(LineStyle::DASHED);
            }
      }
      else {
            error("%s, line %d: thing %ld can't be read in\n", 
                  ipf->GetFileName()->getstr(), ipf->LineNumber(), value);
            delete thing;
            return;
      }
      // remember read thing.
      assoc->Add(thing, type);
      assocList->Put(assoc);
}

void Diagram::PlaceViews() {
      for (viewsRead.first(); !viewsRead.done(); viewsRead.next()) {
            ShapeView *view = viewsRead.cur();
            if (check(view) && view->SetReferences(assocList))
                  dviewer->AddView(view);
            else 
                  error("%s: view %ld can not be read\n", 
                        GetInputFile()->GetFileName()->getstr(), 
                        view->GetId());
      }
}

void Diagram::PlaceShapes() {
      List<Shape *> deleteShapes;
      InputFile *ipf = GetInputFile(); 
      for (assocList->first(); !assocList->done(); assocList->next()) {
            Assoc *assoc = assocList->cur();
            if (assoc->type == Thing::SHAPE) {
                  GShape *shape = (GShape *)assoc->copy;
                  // set view, subject and shape pointers.
                  if (check(shape) && shape->SetAssocView(assocList) &&
                      shape->SetAssocSubject(assocList) &&
                      shape->SetReferences(assocList)) {
                        shape->SetTextShape();
                        if (IsAppending()) {
                              shape->SetSelect(True);
                              shape->SetVisible(False);
                        }
                        // add to view
                        shape->GetView()->AddShapeRaw(shape);
                  }
                  else {
                        deleteShapes.add(shape);
                        error("%s: shape %ld can not be read\n", 
                              ipf->GetFileName()->getstr(),
                              assoc->original);
                  }
            }
            else if (assoc->type == Thing::SUBJECT) {
                  Subject *subject = (Subject *)assoc->copy;
                  // set subject pointers in edges
                  if (!check(subject)) 
                        error("%s: subject %ld can not be read\n", 
                              ipf->GetFileName()->getstr(), 
                              assoc->original);
                  // refs are checked later on.
                  (void) subject->SetReferences(assocList);
                  // add to graph.
                  if (!IsAppending()) {
                        Diagram::AddSubject(subject);
                  }
            }
            else if (assoc->type == Thing::VIEW) {
                  ; // do nothing.
            }
            else
                  error("%s: thing %ld can not be read\n", 
                        ipf->GetFileName()->getstr(), assoc->original);
      }
      // update duplication marks.
      for (assocList->first(); !assocList->done(); assocList->next()) {
            Assoc *assoc = assocList->cur();
            if (assoc->type == Thing::SUBJECT) {
                  Subject *subject = (Subject *)assoc->copy;
                  if (check(subject))
                        dviewer->UpdateDuplicationMarks(subject);
            }
      }
      deleteShapes.clear();
}

void Diagram::PurgeViews() {
      for (viewsRead.first(); !viewsRead.done(); viewsRead.next()) {
            ShapeView *view = viewsRead.cur();
            // delete views that were created during reading but
            // which are not part of diagram viewer.
            if (!dviewer->HasView(view)) {
                  ShapeView *v2 = dviewer->GetView(view->GetIndex());
                  v2->SetId(view->GetId());
                  delete view;
            }
      }
}

void Diagram::CheckSubjects() {
      List<Subject *> deleteSubjects;
      List<Subject *> nodes;
      graph->GetNodes(&nodes);
      for (nodes.first(); !nodes.done(); nodes.next()) {
            Subject *node = nodes.cur();
            if (!check(node))
                  continue;
            if (!node->CheckReferences())
                  deleteSubjects.add(node);
            else if (!dviewer->GetShape(node)) {
                  error("node %ld has no shape representation\n", 
                        node->GetId());
                  deleteSubjects.add(node);
            }
      }
      List<Subject *> edges;
      graph->GetEdges(&edges);
      for (edges.first(); !edges.done(); edges.next()) {
            Edge *edge = (Edge *)edges.cur();
            if (!check(edge))
                  continue;
            if (!edge->CheckReferences())
                  deleteSubjects.add(edge);
            else if (!dviewer->GetShape(edge)) {
                  error("edge %ld has no shape representation\n", 
                        edge->GetId());
                  deleteSubjects.add(edge);
            }
      }
      graph->CompleteSubjects(&deleteSubjects);
      RemoveSubjects(&deleteSubjects);
}

00498 void Diagram::FitShapes() {
      Point p1, p2;
      dviewer->CalcSizeElements(p1, p2);
      double wd = dviewer->Scale(p2.x) + 300.0;
      double ht = dviewer->Scale(p2.y) + 300.0;
      ((DiagramWindow *)GetMainWindow())->FitDocument(int(0.5 + wd), int(0.5 + ht));
}

00506 void Diagram::SaveEntries() {
      // write all subjects and shapes to file.
      dviewer->TextModeOff();
      OutputFile *opf = GetOutputFile();
      if (IsSaveSelection())
            dviewer->WriteSelection(opf);
      else {
            graph->WriteSubjects(opf);
            dviewer->WriteShapes(opf);
      }
}

00518 bool Diagram::SetText(TextShape *t, const string *s) {
      const string *description = t->GetDescription();
      Subject *subj = t->GetParent()->GetSubject();
      if (*description == "Index")
            return SetIndex((Node *)subj, s);
      else if (*description == "Name")
            return SetSubjectName(subj, s);
      else {
            error("%s, line %d: impl error: "
                  "unknown text shape, %s\n", 
                  __FILE__,__LINE__, s->getstr());
            return False;
      }
}

00533 bool Diagram::SetSubjectName(Subject *subject, const string *s) {
      Subject::NameErrType reason = subject->SetName(s);
      if (reason != Subject::OK) {
            string txt = "'" + *s + "' ";
            switch (reason) {
            case Subject::DOUBLE_EDGE:
                  txt += " conflicts\n with an already existing edge name";
                  break;
            case Subject::DOUBLE_NODE:
                  txt += " conflicts\n with an already existing node name";
                  break;
            case Subject::HAS_ATTRIBUTE:
                  txt += " conflicts\n with an already existing attribute name";
                  break;
            case Subject::HAS_ACTION:
                  txt += " conflicts\n with an already existing action name";
                  break;
            case Subject::IMPOSSIBLE_NAME:
                  txt += "\n is not a syntactically correct name";
                  break;
            case Subject::WRONG_NAME:
                  txt += "wrong name\n for ";
                  txt += Code::GetName(subject->GetClassType()); 
                  break;
            case Subject::WRONG_NAME_CONNECTION:
                  txt += "wrong name\n for "; 
                  txt += Code::GetName(subject->GetClassType());
                  txt += " in this kind of connection";
                  break;
            default:
                  error("impl. error: unknown name error type\n");
                  break;
            }
            ShowDialog(MessageDialog::ERROR, "Error", &txt);
            return False;
      }
      else {
            UpdateNameStrings(subject);
            IncChanges();
            return True;
      }
}

00576 void Diagram::UpdateNameStrings(Subject *subject) {
      // update all name strings of shapes in the viewer.
      List<GShape *> shapes;
      dviewer->GetShapes(subject, &shapes);
      for (check(shapes.first()); !shapes.done(); shapes.next()) {
            const string *str = subject->GetName();
            GShape *shape = shapes.cur();
            if (check(!shape->IsTextShape())) {
                  shape->UpdateNameString(str);
                  if (dviewer->IsAutoResize())
                        shape->AdjustSize();
            }
      }
}

00591 bool Diagram::SetIndex(Node *node, const string *s) {
      List<GShape *> shapes;
      dviewer->GetShapes(node, &shapes);
      Subject::NameErrType n = node->SetIndex(s);
      if (n == Subject::OK) {
            for (shapes.first(); !shapes.done(); shapes.next()) {
                  NodeShape *t = (NodeShape *)shapes.cur();
                  if (dviewer->IsShowIndexes())
                        t->UpdateIndexLabel(s);
                  else {
                        string empty("");
                        t->UpdateIndexLabel(&empty);
                  }
            }
            if (!shapes.first()) {
                  error("%s, line %d: shape does not exist\n",
                        __FILE__, __LINE__);
                  return False;
            }
            IncChanges();
            return True;
      }
      else if (n == Subject::IMPOSSIBLE_NAME) {
            string txt = "'" + *s + "' wrong syntax for an index";
            ShowDialog(MessageDialog::ERROR, "Error", &txt);
            return False;
      }
      else if (n == Subject::DOUBLE_NODE) {
            string txt = "'" + *s + "' already exists as an index";
            ShowDialog(MessageDialog::ERROR, "Error", &txt);
            return False;
      }
      else {
            error("%s, line %d: case not handled\n",
                        __FILE__, __LINE__);
      }
      return True;
}

00630 void Diagram::FindAll(const string *s, bool sens, bool substr, bool nameOnly) {
      dviewer->FindAll(s, sens, substr, nameOnly);
}

00634 void Diagram::FindNext(const string *s, bool sens, bool substr, bool nameOnly) {
      dviewer->FindNext(s, sens, substr, nameOnly);
}

00638 void Diagram::ReplaceNext(const string *s1, const string *s2, 
            bool sens, bool substr, bool nameOnly) {
      dviewer->ReplaceNext(s1, s2, sens, substr, nameOnly);
}

00643 void Diagram::ReplaceAll(const string *s1, const string *s2, 
            bool sens, bool substr, bool nameOnly) {
      dviewer->ReplaceAll(s1, s2, sens, substr, nameOnly);
}

00648 void Diagram::AnnotateSubject(Subject *s) {
      if (GetEltAnnotationDialog()->IsManaged()) {
            ShowDialog(MessageDialog::WARNING, "Warning", 
                  "At most one subject annotation dialog "
                  "can be opened at a time");
            return;
      }
      const string *n = s->GetName();
      const string *annot = s->GetAnnotation();
      annotationSubject = s;
      string title = "Annotation of subject " + *n;
      if (*n == "")
            title += "<unnamed>";
      GetEltAnnotationDialog()->SetTitle(&title);
      GetEltAnnotationDialog()->SetTextString(annot);
      GetEltAnnotationDialog()->Popup();
      GetEltAnnotationDialog()->SetStatus(&title);
}

00667 void Diagram::SetSubjectAnnotation(string *s) {
      SetStatus("action: update subject annotation");
      if (!check(annotationSubject))
            return;
      // make here an undoable command!
      annotationSubject->SetAnnotation(s);
}

00675 void Diagram::SetCurve(bool set) {
      string txt = "curve: ";
      if (set) {
            txt += "on";
            dviewer->SetDefaultCurve(True);
      }
      else {
            txt += "off";
            dviewer->SetDefaultCurve(False);
      }
      SetStatus(&txt);
}


00689 void Diagram::SetNextIndex(Node *n) {
      string s("");
      n->SetIndex(&s);
      if (dviewer->IsShowIndexes()) {
            // set next free number in process index
            graph->GetNextIndex(&s);
            n->SetIndex(&s);
      }
      // update the node shapes of the node.
      List<GShape *> shapes; 
      dviewer->GetShapes(n, &shapes);
      for (shapes.first(); !shapes.done(); shapes.next()) {
            NodeShape *t = (NodeShape *)shapes.cur();
            if (dviewer->IsShowIndexes() && 
                HasIndexShape(t->GetClassType()))
                  t->UpdateIndexLabel(&s);
            else {
                  string empty("");
                  t->UpdateIndexLabel(&empty);
            }
      }
}

void Diagram::Reindex() {
      List<Subject *> nodes;
      List<Node *> nodes2;
      graph->GetNodes(&nodes);
      const string *pref = graph->GetIndexPrefix();
      for (nodes.first(); !nodes.done(); nodes.next()) {
            Node *node = (Node *)nodes.cur();
            if (HasIndexNode(node->GetClassType())) {
                  string pi;
                  node->GetParentIndex(&pi);
                  if (pi == *pref)
                        nodes2.add(node);
            }
      }
      dviewer->Reindex(&nodes2);
}


00730 /* virtual */ void Diagram::SetHierarchic(bool s) {
      if ( IsHierarchic() != s ) {
            Document::SetHierarchic(s);
            string txt = "Document hierarchy is ";
            if ( IsHierarchic() )
                  txt += "on";
            else
                  txt += "off";
            SetStatus(&txt);
            DiagramWindow *mw =
                        dynamic_cast<DiagramWindow *>(GetMainWindow());
            if ( mw ) {
                  mw->SetHierarchic(IsHierarchic());
                  Menu *menu = mw->GetDocumentMenu();
                  if ( menu )
                        Menu::SetChildToggleState(menu->GetWidget(),
                              Menu::PULLDOWN, "Hierarchic Document",
                              IsHierarchic());
            }
            dviewer->RecalcAllParents(IsHierarchic());
      }
}


00754 /* virtual */ bool Diagram::AllowHierarchic() const {
      int t = GetMainWindow()->GetTool();
      if ( ! Toolkit::HierarchicEditor(t) )
            return False;
      if ( Toolkit::EditorWithoutDuplicates(t) )
            return True;
      if ( dviewer->HasDuplicates() ) {
            ShowDialog(MessageDialog::ERROR, (const char *) "Error", 
                  (const char *) "Cannot produce hierarchic documents\n"
                              "while there are duplicates");
            return False;
      }
      return True;
}

Generated by  Doxygen 1.6.0   Back to index