001: /*
002: * Copyright (C) Jakub Neubauer, 2007
003: *
004: * This file is part of TaskBlocks
005: *
006: * TaskBlocks is free software; you can redistribute it and/or modify
007: * it under the terms of the GNU General Public License as published by
008: * the Free Software Foundation; either version 3 of the License, or
009: * (at your option) any later version.
010: *
011: * TaskBlocks is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
014: * GNU General Public License for more details.
015: *
016: * You should have received a copy of the GNU General Public License
017: * along with this program. If not, see <http://www.gnu.org/licenses/>.
018: */
019:
020: package taskblocks.graph;
021:
022: import java.awt.FontMetrics;
023: import java.awt.Graphics2D;
024: import java.util.ArrayList;
025: import java.util.List;
026:
027: /**
028: * This class is used to count real positions of tasks on the component
029: *
030: * @author jakub
031: *
032: */
033: class TaskLayouter {
034:
035: /**
036: * Recounts tasks and rows bounds
037: */
038: static void recountBounds(int _graphTop, int _rowHeight,
039: TaskGraphRepresentation _builder,
040: TaskGraphComponent _graph, Graphics2D g2) {
041:
042: int rowIndex = 0;
043: FontMetrics fm = g2.getFontMetrics();
044: int maxRowWidth = fm.stringWidth("Worker");
045: for (TaskRow row : _builder._rows) {
046: maxRowWidth = Math.max(maxRowWidth, fm
047: .stringWidth(row._name));
048: }
049: _graph._headerWidth = maxRowWidth + 20;
050: _graph.recountBounds();
051:
052: // count rows and tasks boundaries
053: int cummulatedRowAdd = _graph._scrollTop;
054: for (TaskRow row : _builder._rows) {
055: int rowTop = (int) (_graphTop + rowIndex * _rowHeight + cummulatedRowAdd);
056:
057: // count connections paddings and modify cummulatedRowAdd and rowTop
058: // according to them.
059: int maxUpPadding = 0;
060: int maxDownPadding = 0;
061: List<Connection> processedConnections = new ArrayList<Connection>();
062: for (Task t : row._tasks) {
063: for (int i = 0; i < t._outgoingConnections.length; i++) {
064: Connection c = t._outgoingConnections[i];
065: // recognize, if connection is going up or down
066: boolean goingDown = row._index <= c._toTask
067: .getRow()._index;
068: int connPadding = 1;
069:
070: // simple hack - if connection is in the same row, padding
071: // should be
072: // greater that 1, so the arrow will look nicer.
073: if (c._fromTask.getRow() == c._toTask.getRow()) {
074: connPadding = 2;
075: }
076: // now check if the connection crosses something in the same
077: // direction.
078: // if yes, increase connPadding until nothings crosses.
079: // Note, that we must check only already processed
080: // connections.
081: boolean run = true;
082: while (run) {
083: run = false;
084: for (Connection c2 : processedConnections) {
085: boolean c2GoingDown = row._index <= c2._toTask
086: .getRow()._index;
087: if (goingDown == c2GoingDown
088: && connPadding == c2._padding
089: && crosses(c, c2)) {
090: connPadding++;
091: run = true;
092: break; // breaks for loop and repeates again.
093: }
094: }
095: }
096: c._padding = connPadding;
097: processedConnections.add(c);
098: if (goingDown) {
099: maxDownPadding = Math.max(maxDownPadding,
100: connPadding);
101: } else {
102: maxUpPadding = Math.max(maxUpPadding,
103: connPadding);
104: }
105: }
106: }
107: rowTop += maxUpPadding
108: * TaskGraphComponent.CONN_PADDING_FACTOR;
109: cummulatedRowAdd += (maxDownPadding + 1)
110: * TaskGraphComponent.CONN_PADDING_FACTOR
111: + maxUpPadding
112: * TaskGraphComponent.CONN_PADDING_FACTOR;
113: row._topPosition = rowTop;
114: row._topPadding = maxUpPadding;
115: row._bottomPadding = maxDownPadding + 1;
116:
117: for (Task t : row._tasks) {
118:
119: double left = _graph.timeToX(t.getStartTime());
120: double width = t.getRealDuration() * _graph._dayWidth;
121: double top = rowTop
122: + TaskGraphComponent.CONN_PADDING_FACTOR;
123: double height = _rowHeight
124: - TaskGraphComponent.CONN_PADDING_FACTOR;
125: t._bounds.setBounds((int) left + 2, (int) top,
126: (int) width - 4, (int) height);
127: }
128: rowIndex++;
129:
130: } // for all rows
131:
132: for (Connection c : _builder._connections) {
133: recountConnectionBounds(c, _graph);
134: }
135:
136: _builder.clearPaintDirtyFlag();
137:
138: }
139:
140: /**
141: * Checks if the given connections crosses each other (without padding)
142: *
143: * @param c1
144: * @param c2
145: * @return
146: */
147: static boolean crosses(Connection c1, Connection c2) {
148: long c1Left = c1._fromTask.getFinishTime();
149: long c1Right = c1._toTask.getStartTime();
150: long c2Left = c2._fromTask.getFinishTime();
151: long c2Right = c2._toTask.getStartTime();
152:
153: // c1.left inside c2
154: if (c1Left >= c2Left && c1Left <= c2Right) {
155: return true;
156: }
157: // c1.right inside c2
158: if (c1Right >= c2Left && c1Right <= c2Right) {
159: return true;
160: }
161:
162: // c2.left inside c1
163: if (c2Left >= c1Left && c2Left <= c1Right) {
164: return true;
165: }
166: // c2.right inside c1
167: if (c2Right >= c1Left && c2Right <= c1Right) {
168: return true;
169: }
170:
171: return false;
172: }
173:
174: static void recountConnectionBounds(Connection c,
175: TaskGraphComponent _graph) {
176: long fromTime = c._fromTask.getFinishTime();
177: long toTime = c._toTask.getStartTime();
178: int x1 = _graph.timeToX(fromTime);
179: int x2 = _graph.timeToX(toTime);
180: int y1, y2;
181: boolean goingDown = c._fromTask.getRow()._index <= c._toTask
182: .getRow()._index;
183: if (goingDown) {
184: y1 = c._fromTask.getRow()._topPosition
185: + TaskGraphComponent.ROW_HEIGHT;
186: } else {
187: y1 = c._fromTask.getRow()._topPosition
188: + TaskGraphComponent.CONN_PADDING_FACTOR;
189: }
190: boolean commingFromUp = c._fromTask.getRow()._index < c._toTask
191: .getRow()._index;
192: if (commingFromUp) {
193: y2 = c._toTask.getRow()._topPosition
194: + TaskGraphComponent.CONN_PADDING_FACTOR;
195: } else {
196: y2 = c._toTask.getRow()._topPosition
197: + TaskGraphComponent.ROW_HEIGHT;
198: }
199:
200: int mediumY;
201: if (y2 >= y1) {
202: mediumY = y1 + c._padding
203: * TaskGraphComponent.CONN_PADDING_FACTOR;
204: } else {
205: mediumY = y1 - c._padding
206: * TaskGraphComponent.CONN_PADDING_FACTOR;
207: }
208: c._path.xpoints[0] = x1;
209: c._path.ypoints[0] = y1;
210: c._path.xpoints[1] = x1;
211: c._path.ypoints[1] = mediumY;
212: c._path.xpoints[2] = x2;
213: c._path.ypoints[2] = mediumY;
214: c._path.xpoints[3] = x2;
215: c._path.ypoints[3] = y2;
216:
217: }
218:
219: }
|