1
////////////////////////////////////////////////////////////////////////////////
3
// This file is part of Toolkit for Conceptual Modeling (TCM).
4
// (c) copyright 1996, 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
////////////////////////////////////////////////////////////////////////////////
23
#include "inputfile.h"
24
#include "outputfile.h"
27
#include "transition.h"
28
#include "transitionarrow.h"
29
#include "diagramviewer.h"
31
const int TransitionArrow::HLINELEN = 20;
32
const int TransitionArrow::HLINEXOFFSET = 4;
33
const int TransitionArrow::HLINEYOFFSET = 2;
34
const int TransitionArrow::HLINEDIST = 10;
36
TransitionArrow::TransitionArrow(ShapeView *v, Grafport *g,
37
GShape *node1, GShape *node2, List<Point *> *aline, bool Curved):
38
Line(v, g, node1, node2, aline, Curved) {
39
event = new TextShape(v, g, this);
40
event->SetAlignment(TextAlign::LEFT);
41
event->SetDescription("Event");
42
actions = new List<TextShape *>;
45
SetEnd2(LineEnd::FILLED_ARROW);
48
TransitionArrow::TransitionArrow(ShapeView *v, Grafport *g, GShape *node1,
49
GShape *node2, bool Curved): Line(v, g, node1, node2, Curved) {
50
event = new TextShape(v, g, this);
51
event->SetAlignment(TextAlign::LEFT);
52
event->SetDescription("Event");
53
actions = new List<TextShape *>;
55
SetEnd2(LineEnd::FILLED_ARROW);
58
TransitionArrow::TransitionArrow(const TransitionArrow &aline): Line(aline) {
59
hlineFrom = aline.hlineFrom;
60
hlineTo = aline.hlineTo;
61
anchorPoint = aline.anchorPoint;
62
separator = aline.separator;
63
event = new TextShape(*(TextShape *)(aline.event));
64
event->SetParent(this);
65
actions = new List<TextShape *>;
66
for (aline.actions->first(); !aline.actions->done(); aline.actions->next()) {
67
TextShape *a = aline.actions->cur();
68
TextShape *b = new TextShape (*a);
74
TransitionArrow::~TransitionArrow() {
80
void TransitionArrow::DrawShape() {
84
GetGrafport()->DrawLine(hlineFrom.x, hlineFrom.y, hlineTo.x, hlineTo.y);
85
for (actions->first(); !actions->done(); actions->next())
86
actions->cur()->Draw();
89
void TransitionArrow::UpdateAnchor(const Point *p, const Point *t) {
90
Point pt = anchorPoint;
95
else if (separator == LEFT || separator == RIGHT) {
96
// find segment where the anchor point is located.
97
// (void) GetLine(pt.x, pt.y);
99
// shift left <-> RIGHT ?
101
if (dp.x < anchorPoint.x)
108
// shift up <-> down ?
110
if (dp.y < anchorPoint.y)
118
else if (separator == LEFT || separator == RIGHT) {
120
// make anchorpoint a point on the line.
121
(void) ContainsPtLine(pt.x, pt.y,
122
(*GetLine())[GetLineNumber()-1],
123
(*GetLine())[GetLineNumber()], cx, cy);
124
anchorPoint = Point(cx, cy);
132
void TransitionArrow::CalcPosition() {
133
Line::CalcPosition();
134
anchorPoint = *GetPosition();
139
void TransitionArrow::CalcHLinePart() {
142
CalcPositionActions();
145
void TransitionArrow::CalcSeparator() {
148
alpha = atan((double)GetHeight()/(double)GetWidth());
152
// if (fabs(alpha) > 0.5*atan(1)) {
153
if (fabs(alpha) > atan(1)) {
154
if (separator == UP || separator == DOWN)
158
if (separator == LEFT || separator == RIGHT)
164
void TransitionArrow::CalcPositionHLine() {
165
int d1 = HLineLength();
166
if (separator == RIGHT) { // attach right to line.
167
hlineFrom = anchorPoint;
168
hlineTo = Point(hlineFrom.x + d1, hlineFrom.y);
170
else if (separator == LEFT) { // attach left to line.
171
hlineTo = anchorPoint;
172
hlineFrom = Point(hlineTo.x - d1, hlineTo.y);
174
else { // put above/below the line.
175
double alpha = atan((double)GetHeight()/(double)GetWidth());
177
if (separator == UP) {
178
int d2 = (int)(cos(alpha)*max(HLINEDIST,
179
HLINEYOFFSET+actions->count()*GetName()->GetFontHeight()));
181
hlineFrom = Point(anchorPoint.x - d1/2,
183
hlineTo = Point(anchorPoint.x + d1/2,
186
else { // separator == DOWN
187
int textHeight = max(GetName()->GetFontHeight(),
188
event->GetStringHeight());
189
int d2 = (int)(cos(alpha)*max(HLINEDIST,HLINEYOFFSET+
192
hlineFrom = Point(anchorPoint.x - d1/2,
194
hlineTo = Point(anchorPoint.x + d1/2,
198
hlineFrom = Point(max(3,hlineFrom.x), max(3,hlineFrom.y));
199
hlineTo = Point(max(3,hlineTo.x), max(3,hlineTo.y));
202
void TransitionArrow::CalcPositionEvent() {
204
const string *s = event->GetString();
206
pt.x = hlineFrom.x + event->GetStringWidth()/2;
207
pt.y = hlineFrom.y - event->GetStringHeight()/2;
208
pt.y -= 2*HLINEYOFFSET;
211
pt.x = (hlineFrom.x + hlineTo.x)/2;
212
pt.y = hlineFrom.y - event->GetFontHeight()/2;
214
pt.x += HLINEXOFFSET;
215
event->SetPosition(&pt);
218
void TransitionArrow::CalcPositionActions() {
220
int textHeight = GetName()->GetFontHeight();
222
pt.y += HLINEYOFFSET + textHeight/2;
223
for (actions->first(); !actions->done(); actions->next()) {
224
w = actions->cur()->GetStringWidth();
225
h = actions->cur()->GetStringHeight();
226
pt.x = hlineFrom.x + w/2 + HLINEXOFFSET;
227
actions->cur()->SetPosition(&pt);
232
void TransitionArrow::UpdateEvent(const string *c) {
241
void TransitionArrow::UpdateAction(const string *s, unsigned n, bool update) {
244
SetAction(s, n, update);
250
void TransitionArrow::SetSelect(bool set) {
251
Line::SetSelect(set);
254
bool TransitionArrow::ContainsPt(int x, int y) {
255
if (Line::ContainsPt(x,y))
258
return HitHLine(x, y);
261
bool TransitionArrow::HitHLine(int x, int y) {
262
// point near horizontal line ?
263
int textHeight = GetName()->GetFontHeight();
264
return (x >= hlineFrom.x && x <= hlineTo.x &&
265
y >= hlineFrom.y - textHeight &&
266
y <= hlineFrom.y + textHeight);
269
int TransitionArrow::HLineLength() {
271
int n = event->GetStringWidth()+HLINEXOFFSET;
274
for (actions->first(); !actions->done(); actions->next()) {
275
n = actions->cur()->GetStringWidth()+HLINEXOFFSET;
282
bool TransitionArrow::InTextArea(int x, int y) {
283
if (Line::InTextArea(x,y))
285
else if (event->ContainsPt(x, y))
287
for (actions->first(); !actions->done(); actions->next()) {
288
if (actions->cur()->ContainsPt(x, y))
294
TextShape *TransitionArrow::HitTextShape(int x, int y) {
295
if (event->ContainsPt(x, y))
297
for (actions->first(); !actions->done(); actions->next()) {
298
if (actions->cur()->ContainsPt(x, y))
299
return actions->cur();
304
TextShape *TransitionArrow::ChooseTextShape(int x, int y) {
305
// This is the winner of the "clumsiest function award".
306
int textHeight = GetName()->GetFontHeight();
308
if (event->ContainsPt(x, y))
310
// hit action string.
311
if (actions->first()) {
312
// action text shapes can overlap
313
// make sure that you choose the right one.
315
int pt_y = actions->cur()->GetTopMost();
317
if (actions->cur()->ContainsPt(x,y) && y <= pt_y) {
318
// return the action.
319
TextShape *t = actions->cur();
322
// pt_y += textHeight;
323
} while (actions->next());
324
if (actions->last()) {
325
if (actions->cur()->ContainsPt(x,y))
326
return actions->cur();
330
if (HitHLine(x, y)) {
331
if (y <= hlineFrom.y) {
332
// above line -> return event.
333
Point pt = *event->GetPosition();
334
pt.x -= HLINEXOFFSET;
335
pt.y -= 2*HLINEYOFFSET;
336
event->UpdatePosition(&pt);
340
// if there are actions return the first one.
341
if (actions->count() > 0) {
342
// return the first action.
343
TextShape *t = (*actions)[0];
346
// create a new action.
348
TextShape *t = new TextShape(
349
GetView(), GetGrafport(), this);
350
t->SetAlignment(TextAlign::LEFT);
352
t->SetDescription("Action");
353
t->SetFont(GetName()->GetFont());
355
CalcPositionActions();
356
// move the action a bit up.
357
Point pt = *t->GetPosition();
358
pt.x = (hlineFrom.x + hlineTo.x)/2;
359
pt.y -= HLINEYOFFSET;
365
// hit some spot on line (but not the hline or a label).
366
// Recalculate hline position.
367
if (GetViewer()->IsEditing() && GetViewer()->IsInlineEdit())
371
if (!GetLine()->first())
374
pt1 = GetLine()->cur();
376
while (GetLine()->next()) {
377
pt2 = GetLine()->cur();
378
if (ContainsPtLine(x, y, pt1, pt2, xx, yy))
381
if (GetLineNumber() < GetLine()->count()-1)
382
SetLineNumber(GetLineNumber()+1);
385
// hit (empty ?) event string.
386
if (y <= hlineFrom.y) {
390
if (actions->count() > 0) {
391
// return last action.
392
TextShape *t = (*actions)[actions->count()-1];
396
// create new action.
397
TextShape *t = new TextShape(
398
GetView(), GetGrafport(), this);
399
t->SetDescription("Action");
400
t->SetAlignment(TextAlign::LEFT);
402
t->SetFont(GetName()->GetFont());
404
CalcPositionActions();
405
Point pt = *t->GetPosition();
406
pt.x = (hlineFrom.x + hlineTo.x)/2;
407
pt.y -= HLINEYOFFSET;
414
bool TransitionArrow::HasTextShape(TextShape *t) const {
415
if (Line::HasTextShape(t))
419
for (actions->first(); !actions->done(); actions->next())
420
if (actions->cur() == t)
426
int TransitionArrow::GetLeftMost() const {
427
int a = Line::GetLeftMost();
428
int b = event->GetLeftMost();
429
for (actions->first(); !actions->done(); actions->next()) {
430
int c = actions->cur()->GetLeftMost();
437
int TransitionArrow::GetTopMost() const {
438
int a = Line::GetTopMost();
439
int b = event->GetTopMost();
440
for (actions->first(); !actions->done(); actions->next()) {
441
int c = actions->cur()->GetTopMost();
448
int TransitionArrow::GetRightMost() const {
449
int a = Line::GetRightMost();
450
int b = event->GetRightMost();
451
for (actions->first(); !actions->done(); actions->next()) {
452
int c = actions->cur()->GetRightMost();
459
int TransitionArrow::GetBottomMost() const {
460
int a = Line::GetBottomMost();
461
int b = event->GetBottomMost();
462
for (actions->first(); !actions->done(); actions->next()) {
463
int c = actions->cur()->GetBottomMost();
470
const char *TransitionArrow::Sep2String(SepType sep) {
473
else if (sep == RIGHT)
477
else // (sep == DOWN)
481
TransitionArrow::SepType TransitionArrow::String2Sep(const string *s) {
484
else if (*s %= "Right")
488
else // (*s %= "Down")
492
void TransitionArrow::WriteMembers(OutputFile *ofile) {
493
Line::WriteMembers(ofile);
494
(*ofile) << "\t{ AnchorPoint " << anchorPoint << " }\n";
495
(*ofile) << "\t{ Separator " << Sep2String(separator) << " }\n";
496
(*ofile) << "\t{ LineNumber " << GetLineNumber() << " }\n";
499
bool TransitionArrow::ReadMembers(InputFile *ifile, double format) {
501
if (!Line::ReadMembers(ifile, format))
503
if (format <= 1.10) {
504
if (!ifile->ReadAttribute2("HLineFrom", &val1, &val2))
506
hlineFrom = Point(val1.toint(), val2.toint());
507
if (!ifile->ReadAttribute2("HLineTo", &val1, &val2))
509
hlineTo = Point(val1.toint(), val2.toint());
511
else { // (format >= 1.11)
512
if (!ifile->ReadAttribute2("AnchorPoint", &val1, &val2))
514
anchorPoint = Point(val1.toint(), val2.toint());
515
if (!ifile->ReadAttribute("Separator", &val1))
517
separator = String2Sep(&val1);
518
if (!ifile->ReadAttribute("LineNumber", &val1))
520
SetLineNumber(val1.toint());
525
bool TransitionArrow::SetAssocSubject(AssocList *al) {
526
if (!Line::SetAssocSubject(al))
528
if (check(GetSubject() &&
529
GetSubject()->GetClassType() == Code::TRANSITION))
535
void TransitionArrow::SetTextShape() {
536
int n = GetLineNumber(); // line number has to stay unaltered.
537
Line::SetTextShape();
538
SetLineNumber(max(1, min(n, GetLine()->count()-1)));
539
if (check(GetSubject() && GetSubject()->GetClassType() == Code::TRANSITION))
540
event->SetString(((Transition *)GetSubject())->GetEvent());
541
event->SetParent(this);
543
if (!check(GetSubject()))
545
unsigned numItems = ((Transition *)GetSubject())->NrActions();
546
for (unsigned i=0; i<numItems; i++) {
547
TextShape *t = new TextShape(GetView(), GetGrafport(), this);
548
t->SetDescription("Action");
549
t->SetFont(GetName()->GetFont());
550
t->SetAlignment(TextAlign::LEFT);
552
t->SetString(((Transition *)GetSubject())->GetAction(i));
559
void TransitionArrow::SetGrafport(Grafport *g) {
560
Line::SetGrafport(g);
561
for (actions->first(); !actions->done(); actions->next())
562
actions->cur()->SetGrafport(g);
563
event->SetGrafport(g);
566
void TransitionArrow::SetView(ShapeView *v) {
568
for (actions->first(); !actions->done(); actions->next())
569
actions->cur()->SetView(v);
573
void TransitionArrow::SetFont(XFont *ft) {
575
for (actions->first(); !actions->done(); actions->next())
576
actions->cur()->SetFont(ft);
581
void TransitionArrow::SetTextColor(const string *c) {
582
Line::SetTextColor(c);
583
for (actions->first(); !actions->done(); actions->next())
584
actions->cur()->SetColor(c);
588
void TransitionArrow::SetAction(const string *s, unsigned n, bool update) {
590
if (n > actions->count())
591
error("%s, line %d: impl error: "
592
"illegal action nr %d\n", __FILE__, __LINE__, n);
593
else if (n == actions->count()) {
594
// insert new action.
595
TextShape *t = new TextShape(GetView(), GetGrafport(), this);
596
t->SetDescription("Action");
597
t->SetAlignment(TextAlign::LEFT);
599
t->SetFont(GetName()->GetFont());
603
// update existing action
604
(*actions)[n]->SetString(s);
606
// delete existing action
607
unsigned i = actions->count() -1;
608
for (unsigned j = n; j < i; j++) {
609
const string *nm = (*actions)[j+1]->GetString();
610
(*actions)[j]->SetString(nm);
612
TextShape *x = (*actions)[i];
618
if (n > actions->count())
619
error("%s, line %d: impl error: "
620
"illegal action nr %d\n", __FILE__, __LINE__, n);
622
// insert new action.
623
TextShape *t = new TextShape(GetView(), GetGrafport(), this);
624
t->SetDescription("Action");
625
t->SetAlignment(TextAlign::LEFT);
628
t->SetFont(GetName()->GetFont());
629
actions->insert(t, n);
630
for (unsigned i=n+1; i<actions->count(); i++)
631
(*actions)[i]->SetSequence(i);
633
// ignore empty strings;
637
bool TransitionArrow::HasString(const string *s, bool sens, bool sub) const {
638
if (*s != "" && event->HasString(s, sens, sub))
640
for (actions->first(); !actions->done(); actions->next()) {
641
if (actions->cur()->HasString(s, sens, sub))
647
bool TransitionArrow::HasString(const string *s, bool sens, bool sub,
648
List<TextShape *> *list) {
651
bool b = event->HasString(s, sens, sub, list);
652
List<TextShape *> actions2 (*actions);
653
for (actions2.first(); !actions2.done(); actions2.next())
654
b += actions2.cur()->HasString(s, sens, sub, list);
658
void TransitionArrow::MoveRaw(const Point *delta) {
659
Line::MoveRaw(delta);
660
for (actions->first(); !actions->done(); actions->next())
661
actions->cur()->Move(delta);
663
anchorPoint = anchorPoint+*delta;