1
////////////////////////////////////////////////////////////////////////////////
3
// This file is part of Toolkit for Conceptual Modeling (TCM).
4
// (c) copyright 1995, Vrije Universiteit Amsterdam.
5
// Author: Frank Dehne (frank@cs.vu.nl).
7
// TCM is free software; you can redistribute it and/or modify
8
// it under the terms of the GNU General Public License as published by
9
// the Free Software Foundation; either version 2 of the License, or
10
// (at your option) any later version.
12
// TCM is distributed in the hope that it will be useful,
13
// but WITHOUT ANY WARRANTY; without even the implied warranty of
14
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
// GNU General Public License for more details.
17
// You should have received a copy of the GNU General Public License
18
// along with TCM; if not, write to the Free Software
19
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
21
////////////////////////////////////////////////////////////////////////////////
26
#include "emptyedge.h"
27
#include "psprocess.h"
28
#include "urlabeledbox.h"
32
#include "messagedialog.h"
33
#include "psdiagram.h"
35
PSDiagram::PSDiagram(Config *c, PSWindow *d, PSViewer *v, PSGraph *g):
41
psChecks = new PSChecks(this,g);
44
PSDiagram::~PSDiagram() {
48
Thing *PSDiagram::CreateThing(int classNr) {
49
Grafport *g = GetDiagramViewer()->GetGrafport();
50
ShapeView *v = GetDiagramViewer()->GetCurView();
51
PSGraph *pg = (PSGraph *)GetGraph();
54
if (classNr == Code::VIEW)
55
thing = new ShapeView(GetDiagramViewer());
57
else if (classNr == Code::UR_LABELED_BOX)
58
thing = new URLabeledBox(v, g, 0, 0);
59
else if (classNr == Code::TEXT_BOX)
60
thing = new TextBox(v, g, 0, 0);
62
else if (classNr == Code::LINE) {
63
Line *line = new Line(v, g, 0, 0, 0);
64
line->SetFixedName(True);
68
else if (classNr == Code::PS_PROCESS)
69
thing = new PSProcess(pg);
70
else if (classNr == Code::COMMENT)
71
thing = new Comment(pg);
73
else if (classNr == Code::EMPTY_EDGE)
74
thing = new EmptyEdge(pg, 0, 0);
76
error("%s, line %d: impl error: "
77
"wrong class number %d in file\n", __FILE__, __LINE__, classNr);
81
Node *PSDiagram::CreateNode(){
83
PSGraph *pg = (PSGraph *)GetGraph();
84
if (GetNodeType() == Code::PS_PROCESS)
85
node = new PSProcess(pg);
86
else if (GetNodeType() == Code::COMMENT)
87
node = new Comment(pg);
89
error("%s, line %d: impl error: "
90
"unknown node type\n", __FILE__, __LINE__);
94
Edge *PSDiagram::CreateEdge(Subject *n1, Subject *n2){
95
if (!CheckEdgeConstraints(n1, n2))
98
PSGraph *pg = (PSGraph *)GetGraph();
99
if (GetEdgeType() == Code::EMPTY_EDGE)
100
edge = new EmptyEdge(pg, n1, n2);
102
error("%s, line %d: impl error: "
103
"unknown edge type\n", __FILE__, __LINE__);
107
NodeShape *PSDiagram::CreateNodeShape(Node *node, int x, int y) {
108
NodeShape *shape = 0;
109
Grafport *g = GetDiagramViewer()->GetGrafport();
110
ShapeView *v = GetDiagramViewer()->GetCurView();
111
if (GetNodeShapeType() == Code::TEXT_BOX)
112
shape = new TextBox(v, g, x, y);
113
else if (GetNodeShapeType() == Code::UR_LABELED_BOX)
114
shape = new URLabeledBox(v, g, x, y);
116
error("%s, line %d: impl error: "
117
"node shape type does not exist\n", __FILE__, __LINE__);
120
shape->SetSubject(node);
121
shape->SetTextShape();
126
Line *PSDiagram::CreateLine(
127
Edge *edge, GShape *from, GShape *to, List<Point *> *l) {
129
Grafport *g = GetDiagramViewer()->GetGrafport();
130
ShapeView *v = GetDiagramViewer()->GetCurView();
131
*((*l)[0]) = *(from->GetPosition());
132
*((*l)[l->count()-1]) = *(to->GetPosition());
133
if (GetLineType() == Code::LINE) {
134
line = new Line(v, g, from, to, l, IsCurve());
135
line->SetFixedName(True);
138
error("%s, line %d: impl error: "
139
"line type does not exist\n", __FILE__, __LINE__);
142
line->SetSubject(edge);
143
line->SetTextShape();
148
void PSDiagram::UpdateNodeType(int num) {
149
((DiagramWindow *)GetMainWindow())->SetNodeName(num);
151
case 1: SetNodeType(Code::PS_PROCESS);
152
SetNodeShapeType(Code::UR_LABELED_BOX);
154
case 2: SetNodeType(Code::COMMENT);
155
SetNodeShapeType(Code::TEXT_BOX);
158
error("%s, line %d: impl error: "
159
"unknown node type selected\n", __FILE__, __LINE__);
163
void PSDiagram::UpdateEdgeType(int num) {
164
((DiagramWindow *)GetMainWindow())->SetEdgeName(num);
166
case 1: SetEdgeType(Code::EMPTY_EDGE);
167
SetLineType(Code::LINE);
170
error("%s, line %d: impl error: "
171
"unknown edge type selected\n", __FILE__, __LINE__);
175
bool PSDiagram::CheckEdgeConstraints(Subject *subj1, Subject *subj2) {
176
if (!CheckConnection(subj1, subj2))
178
if ((subj1 == subj2) || GetGraph()->PathExists(subj1, subj2)) {
179
ShowDialog(MessageDialog::ERROR, "Error",
180
"This connection violates the tree constraint");
186
void PSDiagram::SaveEntries() {
188
Diagram::SaveEntries();
189
((PSViewer *)GetDiagramViewer())->UpdateShowSequences();
192
unsigned PSDiagram::TraverseTree(bool chec) {
194
List<Subject *> all_nodes;
196
actionSequenceNr = 0;
197
GetGraph()->GetNodes(&all_nodes, Code::PS_PROCESS);
199
unsigned nrRoots = FindRoot(&all_nodes, &root);
200
List<Subject *> tree_nodes;
204
tree_nodes.add(root);
206
errors += psChecks->CheckRootOperator(root, chkbuf);
207
errors += TraverseChildren(root, &tree_nodes, chec);
209
else if (nrRoots == 0 && chec) {
210
chkbuf +="* Error: there is no root process\n";
213
else if (nrRoots > 1 && chec) {
214
chkbuf +="* Error: there is no unique root process\n";
217
// Check if there are remaining nodes not part of the tree:
218
for (all_nodes.first(); !all_nodes.done(); all_nodes.next()) {
219
Subject *node = all_nodes.cur();
220
if (tree_nodes.find(node) == -1) {
221
// give remaining node unused sequence numbers.
222
((PSProcess *)node)->SetTreeSequence(0);
223
((PSProcess *)node)->SetActionSequence(0);
224
// give error if required.
225
if (chec && nrRoots == 1 && *node->GetName() != "") {
226
// empty names are noted elsewhere.
228
chkbuf += "* Error: Process '";
229
chkbuf += *node->GetName();
230
chkbuf += "' is not part of the main tree\n";
237
unsigned PSDiagram::FindRoot(List<Subject *> *nodes, PSProcess **root) {
238
// find the root: the subject that has the shape with
239
// the smallest y-coordinate.
242
if (nodes->first()) {
243
PSProcess *node = (PSProcess *)nodes->cur();
244
Shape *nshape = GetDiagramViewer()->GetShape(node);
245
root_pos = *nshape->GetPosition();
248
while (nodes->next()) {
249
node = (PSProcess *)nodes->cur();
250
nshape = GetDiagramViewer()->GetShape(node);
251
const Point *pt = nshape->GetPosition();
252
if (root_pos.y > pt->y) {
257
else if (root_pos.y == pt->y) {
258
// more than one root ?
266
void PSDiagram::FindChildren(PSProcess *parent, List<Subject *> *nodes,
267
List<Subject *> *children) {
268
List<Subject *> edges;
269
List<Subject *> child_edges;
270
// find all edges that connect the parent with its children.
271
GetGraph()->CompleteSubject(&edges, parent);
272
for (edges.first(); !edges.done(); edges.next()) {
273
Edge *edge = (Edge *)edges.cur();
274
Subject *n1 = edge->GetSubject1();
275
Subject *n2 = edge->GetSubject2();
276
if (n1 == parent && nodes->find(n2) == -1) {
277
child_edges.add(edge);
279
else if (n2 == parent && nodes->find(n1) == -1) {
280
child_edges.add(edge);
285
// look repeatedly for the 'leftMost' edge: i.e. the
286
// edge whose line connects to the shape of the parent
287
// with the smallest x-coordinate.
288
while ((leftMost = FindLeftMostEdge(parent, &child_edges))) {
289
if (leftMost->GetSubject2() == parent)
290
children->add(leftMost->GetSubject1());
292
children->add(leftMost->GetSubject2());
293
child_edges.remove(leftMost);
297
Edge *PSDiagram::FindLeftMostEdge(PSProcess *parent, List<Subject *> *child_edges) {
300
if (child_edges->first()) {
301
Edge *edge = (Edge *)(*child_edges)[0];
302
Line *line = (Line *)GetDiagramViewer()->GetShape(edge);
303
Shape *nshape = GetDiagramViewer()->GetShape(parent);
304
// we have to take care of the two different directions
306
if (line->GetFromShape() == nshape)
307
min_x = (*line->GetLine())[0]->x;
309
min_x = (*line->GetLine())[line->NrPoints()-1]->x;
311
while (child_edges->next()) {
312
edge = (Edge *)child_edges->cur();
313
line = (Line *)GetDiagramViewer()->GetShape(edge);
315
if (line->GetFromShape() == nshape)
316
pt = (*line->GetLine())[0];
318
pt = (*line->GetLine())[line->NrPoints()-1];
328
unsigned PSDiagram::TraverseChildren(PSProcess *parent, List<Subject *> *nodes, bool chec) {
330
List<Subject *> children;
331
FindChildren(parent, nodes, &children);
332
// list name of parent and of their children.
333
// Call this function recursively for all the children.
334
for (children.first(); !children.done(); children.next()) {
335
PSProcess *child = (PSProcess *)children.cur();
336
child->SetRoot(False);
337
nodes->add(children.cur());
340
errors += psChecks->CheckChildPositions(
341
GetDiagramViewer(), parent, &children, chkbuf);
342
errors += psChecks->CheckChildOperators(parent, &children, chkbuf);
344
for (children.first(); !children.done(); children.next())
345
errors += TraverseChildren((PSProcess *)children.cur(), nodes, chec);
346
if (children.count() == 0) {
347
char op = parent->GetOperator();
348
if (op != '?' && op != '!')
349
parent->SetAction(True);
351
parent->SetAction(False);
353
else if (children.count() == 1) {
354
// parent of a '!' process is also considered as a action.
355
PSProcess *p = (PSProcess *)children[0];
356
if (p->GetOperator() == '!')
357
parent->SetAction(True);
359
parent->SetAction(False);
362
parent->SetAction(False);
363
parent->SetTreeSequence(++treeSequenceNr);
364
if (parent->IsAction())
365
parent->SetActionSequence(++actionSequenceNr);
367
parent->SetActionSequence(0);
372
void PSDiagram::CheckDocument() {
375
errors += TraverseTree(True);
376
((PSViewer *)GetDiagramViewer())->UpdateShowSequences();
377
errors += psChecks->CheckNamelessNodes(Code::PS_PROCESS, chkbuf);
378
errors += psChecks->CheckDoubleProcessNames(chkbuf);
379
ReportCheck(errors, &chkbuf);
382
bool PSDiagram::SetOperator(PSProcess *proc, const string *s) {
383
List<GShape *> shapes;
384
GetDiagramViewer()->GetShapes(proc, &shapes);
385
// empty string means sequence (space).
387
if (s->length() == 0)
391
if (proc->SetOperator(op)) {
392
if (check(shapes.first()))
394
((URLabeledBox *)shapes.cur())->UpdateURLabel(s);
395
while (shapes.next());
400
string txt = *s + " is not a valid process operator";
401
ShowDialog(MessageDialog::ERROR, "Error", &txt);
407
bool PSDiagram::SetText(TextShape *t, const string *s) {
408
const string *description = t->GetDescription();
409
Subject *subj = t->GetParent()->GetSubject();
410
if (*description == "Process Operator" &&
411
(subj->GetClassType()==Code::PS_PROCESS))
412
return SetOperator((PSProcess *)subj, s);
414
return Diagram::SetText(t, s);