1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package de.jaret.util.ui.timebars.swing.renderer;
21
22 import java.awt.BasicStroke;
23 import java.awt.Color;
24 import java.awt.Graphics;
25 import java.awt.Graphics2D;
26 import java.awt.Point;
27 import java.awt.Rectangle;
28 import java.awt.Stroke;
29 import java.util.ArrayList;
30 import java.util.List;
31
32 import de.jaret.util.date.Interval;
33 import de.jaret.util.ui.timebars.TimeBarViewerDelegate;
34 import de.jaret.util.ui.timebars.model.IIntervalRelation;
35 import de.jaret.util.ui.timebars.model.IIntervalRelation.Type;
36 import de.jaret.util.ui.timebars.model.IRelationalInterval;
37 import de.jaret.util.ui.timebars.model.TimeBarRow;
38
39
40
41
42
43
44
45
46 public class DefaultRelationRenderer implements IRelationRenderer {
47
48 private static final int DEFAULT_CACHE_SIZE = 200;
49
50
51 private static final Color DEFAULT_LINE_COLOR = Color.BLACK;
52
53 private static final Color DEFAULT_SELECTED_COLOR = Color.BLUE;
54
55 private static final int DEFAULT_LINE_WIDTH = 1;
56
57 private static final int DEFAULT_ARROW_SIZE = 5;
58
59
60 private List<Line> _cache;
61
62 protected Color _lineColor = DEFAULT_LINE_COLOR;
63
64 protected Color _selectedColor = DEFAULT_SELECTED_COLOR;
65
66
67 protected int _lineWidth = DEFAULT_LINE_WIDTH;
68
69 protected int _arrowSize = DEFAULT_ARROW_SIZE;
70
71
72
73
74 public DefaultRelationRenderer() {
75 }
76
77
78
79
80 public void renderRelations(TimeBarViewerDelegate delegate, Graphics graphics) {
81 _cache = new ArrayList<Line>(DEFAULT_CACHE_SIZE);
82
83 int firstRow = delegate.getFirstRow();
84
85
86 Rectangle clipSave = graphics.getClipBounds();
87 Rectangle nc = new Rectangle(delegate.getDiagramRect().x, delegate.getDiagramRect().y, delegate
88 .getDiagramRect().width, delegate.getDiagramRect().height);
89 graphics.setClip(graphics.getClipBounds().intersection(nc));
90
91 int upperYBound = delegate.getDiagramRect().y;
92 int lowerYBound = upperYBound + delegate.getDiagramRect().height;
93
94 int rowsDisplayed = delegate.getRowsDisplayed();
95 for (int r = firstRow; r <= firstRow + rowsDisplayed + 1 && r < delegate.getRowCount(); r++) {
96 TimeBarRow row = delegate.getRow(r);
97 int y = delegate.yForRow(row);
98 int rowHeight = delegate.getTimeBarViewState().getRowHeight(row);
99 if (y == -1) {
100
101 break;
102 }
103
104
105
106
107
108 if ((y >= upperYBound && y <= lowerYBound)
109 || (y + rowHeight >= upperYBound && y + rowHeight <= lowerYBound)
110 || (upperYBound > y && upperYBound < y + rowHeight)) {
111 drawRow(delegate, graphics, delegate.getRow(r), y);
112 }
113 }
114 graphics.setClip(clipSave);
115
116 }
117
118
119
120
121
122
123
124
125
126 private void drawRow(TimeBarViewerDelegate delegate, Graphics graphics, TimeBarRow row, int y) {
127 for (Interval interval : row.getIntervals()) {
128
129 if (delegate.getIntervalFilter() == null || delegate.getIntervalFilter().isInResult(interval)) {
130 if (interval instanceof IRelationalInterval) {
131 IRelationalInterval rInterval = (IRelationalInterval) interval;
132 for (IIntervalRelation relation : rInterval.getRelations()) {
133
134 if (delegate.getIntervalFilter() == null
135 || (delegate.getIntervalFilter().isInResult(relation.getStartInterval()) && delegate
136 .getIntervalFilter().isInResult(relation.getEndInterval()))) {
137 if (!hasBeenDrawn(relation)) {
138 drawDependency(delegate, graphics, rInterval, y, row, relation);
139 }
140 }
141 }
142 }
143 }
144 }
145
146 }
147
148
149
150
151
152
153
154
155
156
157
158 private void drawDependency(TimeBarViewerDelegate delegate, Graphics graphics, IRelationalInterval rInterval,
159 int y, TimeBarRow row, IIntervalRelation relation) {
160 int off = _arrowSize + _arrowSize / 2;
161
162 boolean selected = delegate.getSelectionModel().isSelected(relation);
163
164 Graphics2D g2d = (Graphics2D) graphics;
165
166 Color fg = graphics.getColor();
167 Stroke lineWidth = g2d.getStroke();
168
169 Stroke stroke = new BasicStroke(_lineWidth, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND);
170 g2d.setStroke(stroke);
171
172 Color color = null;
173 if (selected) {
174 color = _selectedColor;
175 } else {
176 color = _lineColor;
177 }
178 graphics.setColor(color);
179
180 IRelationalInterval beginTask;
181 TimeBarRow beginRow;
182 Point begin;
183
184 IRelationalInterval endTask;
185 TimeBarRow endRow;
186 Point end;
187
188 if (relation.getStartInterval().equals(rInterval)) {
189 beginTask = rInterval;
190 beginRow = row;
191
192 endTask = relation.getEndInterval();
193 endRow = getRowForInterval(delegate, endTask);
194 } else {
195 endTask = rInterval;
196 endRow = row;
197
198 beginTask = relation.getStartInterval();
199 beginRow = getRowForInterval(delegate, beginTask);
200 }
201
202 int rheight = delegate.getTimeBarViewState().getRowHeight(beginRow);
203
204 if (relation.getType() == IIntervalRelation.Type.END_BEGIN) {
205 begin = getRightPoint(delegate, beginRow, beginTask);
206 end = getLeftPoint(delegate, endRow, endTask);
207 int ydir = end.y > begin.y ? 1 : -1;
208
209 int[] points = new int[] {begin.x, begin.y, begin.x + off, begin.y, begin.x + off,
210 begin.y + (rheight / 2 * ydir), end.x - off, begin.y + (rheight / 2 * ydir), end.x - off, end.y,
211 end.x, end.y};
212 g2d.drawPolyline(xPoints(points), yPoints(points), points.length / 2);
213 registerLines(relation, points);
214
215 drawArrow(graphics, begin, end, color, relation.getDirection(), relation.getType());
216
217 } else if (relation.getType() == IIntervalRelation.Type.BEGIN_BEGIN) {
218 begin = getLeftPoint(delegate, beginRow, beginTask);
219 end = getLeftPoint(delegate, endRow, endTask);
220 int ydir = end.y > begin.y ? 1 : -1;
221
222 int[] points = new int[] {begin.x, begin.y, begin.x - off, begin.y, begin.x - off,
223 begin.y + (rheight / 2 * ydir), end.x - off, begin.y + (rheight / 2 * ydir), end.x - off, end.y,
224 end.x, end.y};
225
226 g2d.drawPolyline(xPoints(points), yPoints(points), points.length / 2);
227 registerLines(relation, points);
228 drawArrow(graphics, begin, end, color, relation.getDirection(), relation.getType());
229 } else if (relation.getType() == IIntervalRelation.Type.END_END) {
230 begin = getRightPoint(delegate, beginRow, beginTask);
231 end = getRightPoint(delegate, endRow, endTask);
232 int ydir = end.y > begin.y ? 1 : -1;
233
234 int[] points = new int[] {begin.x, begin.y, begin.x + off, begin.y, begin.x + off,
235 begin.y + (rheight / 2 * ydir), end.x + off, begin.y + (rheight / 2 * ydir), end.x + off, end.y,
236 end.x, end.y};
237 g2d.drawPolyline(xPoints(points), yPoints(points), points.length / 2);
238 registerLines(relation, points);
239 drawArrow(graphics, begin, end, color, relation.getDirection(), relation.getType());
240 } else if (relation.getType() == IIntervalRelation.Type.BEGIN_END) {
241 begin = getLeftPoint(delegate, beginRow, beginTask);
242 end = getRightPoint(delegate, endRow, endTask);
243 int ydir = end.y > begin.y ? 1 : -1;
244
245
246 int[] points = new int[] {begin.x, begin.y, begin.x - off, begin.y, begin.x - off,
247 begin.y + (rheight / 2 * ydir), end.x + off, begin.y + (rheight / 2 * ydir), end.x + off, end.y,
248 end.x, end.y};
249 g2d.drawPolyline(xPoints(points), yPoints(points), points.length / 2);
250 registerLines(relation, points);
251 drawArrow(graphics, begin, end, color, relation.getDirection(), relation.getType());
252 }
253 graphics.setColor(fg);
254 g2d.setStroke(lineWidth);
255 }
256
257
258
259
260
261
262
263 private int[] xPoints(int[] points) {
264 int[] result = new int[points.length / 2];
265 for (int i = 0; i < points.length; i += 2) {
266 result[i / 2] = points[i];
267 }
268 return result;
269 }
270
271
272
273
274
275
276
277 private int[] yPoints(int[] points) {
278 int[] result = new int[points.length / 2];
279 for (int i = 1; i < points.length; i += 2) {
280 result[i / 2] = points[i];
281 }
282 return result;
283 }
284
285
286
287
288
289
290
291
292
293 private Point getRightPoint(TimeBarViewerDelegate delegate, TimeBarRow row, Interval interval) {
294 java.awt.Rectangle rect = delegate.getIntervalBounds(row, interval);
295 return new Point(rect.x + rect.width, rect.y + rect.height / 2);
296 }
297
298
299
300
301
302
303
304
305
306 private Point getLeftPoint(TimeBarViewerDelegate delegate, TimeBarRow row, Interval interval) {
307 java.awt.Rectangle rect = delegate.getIntervalBounds(row, interval);
308 return new Point(rect.x, rect.y + rect.height / 2);
309 }
310
311
312
313
314
315
316
317
318 private TimeBarRow getRowForInterval(TimeBarViewerDelegate delegate, Interval interval) {
319 return delegate.getModel().getRowForInterval(interval);
320 }
321
322
323
324
325
326
327
328 private boolean hasBeenDrawn(IIntervalRelation relation) {
329 for (Line l : _cache) {
330 if (l.relation.equals(relation)) {
331 return true;
332 }
333 }
334 return false;
335 }
336
337
338
339
340
341
342
343 private void registerLines(IIntervalRelation relation, int[] coords) {
344 for (int i = 0; i <= coords.length - 4; i += 2) {
345 _cache.add(new Line(relation, coords[i], coords[i + 1], coords[i + 2], coords[i + 3]));
346 }
347 }
348
349
350
351
352
353
354
355
356
357
358
359 private void drawArrow(Graphics graphics, Point begin, Point end, Color color,
360 IIntervalRelation.Direction direction, Type type) {
361
362 if (direction.equals(IIntervalRelation.Direction.BACK) || direction.equals(IIntervalRelation.Direction.BI)) {
363 if (type.equals(Type.END_BEGIN) || type.equals(Type.END_END)) {
364 drawArrow(graphics, begin, false, color);
365 } else {
366 drawArrow(graphics, begin, true, color);
367 }
368 }
369 if (direction.equals(IIntervalRelation.Direction.FORWARD) || direction.equals(IIntervalRelation.Direction.BI)) {
370 if (type.equals(Type.BEGIN_END) || type.equals(Type.END_END)) {
371 drawArrow(graphics, end, false, color);
372 } else {
373 drawArrow(graphics, end, true, color);
374 }
375 }
376 }
377
378
379
380
381
382
383
384
385
386 private void drawArrow(Graphics graphics, Point p, boolean leftToRight, Color color) {
387 Color bg = graphics.getColor();
388 graphics.setColor(color);
389 int off = _arrowSize;
390 int[] points;
391 if (leftToRight) {
392 int[] pts = {p.x, p.y, p.x - off, p.y - off, p.x - off, p.y + off};
393 points = pts;
394 } else {
395 int[] pts = {p.x, p.y, p.x + off, p.y - off, p.x + off, p.y + off};
396 points = pts;
397 }
398 Graphics2D g2d = (Graphics2D) graphics;
399 g2d.fillPolygon(xPoints(points), yPoints(points), points.length / 2);
400 graphics.setColor(bg);
401 }
402
403
404
405
406 public List<IIntervalRelation> getRelationsForCoord(int x, int y) {
407 List<IIntervalRelation> result = new ArrayList<IIntervalRelation>(2);
408 for (Line line : _cache) {
409 if (line.hit(x, y, 2)) {
410 result.add(line.relation);
411 }
412 }
413
414 return result;
415 }
416
417
418
419
420 public String getTooltip(int x, int y) {
421 List<IIntervalRelation> result = getRelationsForCoord(x, y);
422 if (result.size() == 0) {
423 return null;
424 } else {
425 return result.get(0).toString();
426 }
427 }
428
429
430
431
432
433
434
435 public class Line {
436
437 public IIntervalRelation relation;
438
439 public int x1;
440
441 public int y1;
442
443 public int x2;
444
445 public int y2;
446
447
448
449
450
451
452
453
454
455
456 public Line(IIntervalRelation relation, int x1, int y1, int x2, int y2) {
457 this.relation = relation;
458 this.x1 = x1;
459 this.y1 = y1;
460 this.x2 = x2;
461 this.y2 = y2;
462 }
463
464
465
466
467
468
469
470
471
472 public boolean hit(int x, int y, int tolerance) {
473 if (x1 == x2) {
474 if (x1 - tolerance <= x && x <= x1 + tolerance) {
475 if ((y1 < y2 && y1 - tolerance <= y && y <= y2 + tolerance)
476 || (y2 < y1 && y2 - tolerance <= y && y <= y1 + tolerance)) {
477 return true;
478 }
479 }
480 } else {
481 if (y1 == y2) {
482 if (y1 - tolerance <= y && y <= y1 + tolerance) {
483 if ((x1 < x2 && x1 - tolerance <= x && x <= x2 + tolerance)
484 || (x2 < x1 && x2 - tolerance <= x && x <= x1 + tolerance)) {
485 return true;
486 }
487 }
488 }
489 }
490 return false;
491 }
492 }
493
494
495
496
497
498
499 public int getLineWidth() {
500 return _lineWidth;
501 }
502
503
504
505
506
507
508 public void setLineWidth(int lineWidth) {
509 _lineWidth = lineWidth;
510 }
511
512
513
514
515
516
517 public int getArrowSize() {
518 return _arrowSize;
519 }
520
521
522
523
524
525
526 public void setArrowSize(int arrowSize) {
527 _arrowSize = arrowSize;
528 }
529
530 }