001: /*
002: * Copyright 2005 Paul Hinds
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016: package org.tp23.antinstaller.renderer.swing;
017:
018: import java.awt.Color;
019: import java.awt.Dimension;
020: import java.awt.Font;
021: import java.awt.Graphics;
022: import java.awt.Graphics2D;
023: import java.awt.Rectangle;
024: import java.awt.RenderingHints;
025: import java.util.ArrayList;
026: import java.util.Iterator;
027: import java.util.List;
028:
029: import javax.swing.JPanel;
030: import javax.swing.Scrollable;
031:
032: import org.apache.tools.ant.BuildEvent;
033: import org.apache.tools.ant.Target;
034: import org.tp23.antinstaller.InstallerContext;
035:
036: /**
037: * Progress panel prints a graphical view of the progress of the Ant targets
038: * being run. It supports displaying one layer of dependent targets.
039: * @author Paul Hinds
040: * @version $Id: ProgressPanel.java,v 1.5 2006/12/21 00:02:59 teknopaul Exp $
041: */
042: public class ProgressPanel extends JPanel implements Scrollable {
043:
044: public static final int tHeight = 19;
045: public static final int leftIndent = 15;
046: public static final int DONE = 0;
047: public static final int INPROGRESS = 1;
048: public static final int TODO = 2;
049: private static final Color progressColor = new Color(0, 125, 0);
050: private static final Font mainFont = new Font("Dialog", Font.PLAIN,
051: 11);
052: private static final Font subFont = new Font("Dialog", Font.PLAIN,
053: 10);
054:
055: private List targets = null;
056:
057: private final InstallerContext ctx;
058: private int mainTargetPos = 0;
059: private ProgressModel currentPM = null;
060:
061: public ProgressPanel(InstallerContext ctx) {
062: super (true);
063: this .ctx = ctx;
064: }
065:
066: public synchronized void prepareCalledTargets() {
067: List targetStrings = ctx.getInstaller().getTargets(ctx);
068: Iterator iter = targetStrings.iterator();
069: targets = new ArrayList();
070: while (iter.hasNext()) {
071: String tgt = (String) iter.next();
072: targets.add(new ProgressModel(tgt));
073: }
074: this .setSize(getSize());// panel size changes
075: revalidate();
076: repaint();
077: }
078:
079: /**
080: * This method assumes that we are send target started methods in order
081: * but that we do not have the information about "depends" targets and have to
082: * insert the information as it arrives. If a TargetStarted event arrives that
083: * is not the expected target is is assumed to be a depends.
084: * @param buildEvent
085: */
086: public synchronized void targetStarted(BuildEvent buildEvent) {
087: try {
088: Target tgt = buildEvent.getTarget();
089: ProgressModel pm = (ProgressModel) targets
090: .get(mainTargetPos);
091: pm.state = INPROGRESS;
092: if (tgt.getName().equals(pm.name)) {
093: // main target
094: currentPM = pm;
095: mainTargetPos++;
096: } else {
097: //dependency
098: ProgressModel dependency = new ProgressModel(tgt
099: .getName());
100: dependency.state = INPROGRESS;
101: if (currentPM != null) {
102: currentPM.state = DONE;// this to catch antcall strangenesses
103: }
104: currentPM = dependency;
105: pm.subTargets.add(dependency);
106: this .setSize(getSize());// panel size changes
107: }
108:
109: // scroll to latest
110: // [ 1735642 ] progress page, scrollbar not following targets
111: Iterator iter = targets.iterator();
112: int offset = 0;
113: while (iter.hasNext()) {
114: ProgressModel pmodel = (ProgressModel) iter.next();
115: offset += tHeight;
116: Iterator subiter = pmodel.subTargets.iterator();
117: while (subiter.hasNext()) {
118: offset += tHeight;
119: ProgressModel spmodel = (ProgressModel) subiter
120: .next();
121: if (spmodel.state == INPROGRESS) {
122: break;
123: }
124: }
125: if (pmodel.state == INPROGRESS) {
126: break;
127: }
128: }
129: scrollRectToVisible(new Rectangle(1, offset, 1, 1));
130:
131: revalidate();
132: repaint();
133:
134: } catch (Exception e) {
135: ctx.log(e);
136: }
137: }
138:
139: public synchronized void targetFinished() {
140: // BUG 1494105 reports NPE here, looks like no targets specified in his antinstaller-config.xml
141: currentPM.state = DONE;
142:
143: }
144:
145: /**
146: * N.B. buildFinished must be fired manually in Ant
147: */
148: public synchronized void buildFinished() {
149: // this is done because antcall sometimes results in targetFinished not being called
150: setToDone(targets);
151: repaint();
152: }
153:
154: private void setToDone(List pModels) {
155: if (pModels == null || pModels.size() == 0) {
156: return;
157: }
158: Iterator iter = pModels.iterator();
159: while (iter.hasNext()) {
160: ProgressModel pm = (ProgressModel) iter.next();
161: pm.state = DONE;
162: setToDone(pm.subTargets);
163: }
164: }
165:
166: /**
167: */
168: public synchronized void paintComponent(Graphics g) {
169: if (getParent().isOpaque()) {
170: super .paintComponent(g);
171: }
172: if (targets == null) {
173: return;
174: }
175: g.setColor(getBackground());
176:
177: g.fillRect(g.getClipBounds().x, g.getClipBounds().y, g
178: .getClipBounds().width, g.getClipBounds().height);
179: // experiments with setOpaque(false) did not work
180: // g.clearRect(g.getClipBounds().x,
181: // g.getClipBounds().y,
182: // g.getClipBounds().width,
183: // g.getClipBounds().height);
184: Iterator iter = targets.iterator();
185: int offset = 0;
186: for (int i = 1; iter.hasNext(); i++) {
187: ProgressModel pmodel = (ProgressModel) iter.next();
188: drawTarget(pmodel, (Graphics2D) g, offset, i < targets
189: .size(), i > 1);
190: offset += tHeight;
191: offset += pmodel.subTargets.size() * tHeight;
192: }
193: }
194:
195: public synchronized Dimension getPreferredSize() {
196: return getSize();
197: }
198:
199: /**
200: * @see java.awt.Component#getSize()
201: */
202: public synchronized Dimension getSize() {
203: if (targets == null) {
204: return new Dimension(SizeConstants.PAGE_WIDTH, tHeight * 5);
205: }
206: int count = targets.size();
207: Iterator iter = targets.iterator();
208: while (iter.hasNext()) {
209: ProgressModel pmodel = (ProgressModel) iter.next();
210: count += pmodel.subTargets.size();
211: }
212: return new Dimension(SizeConstants.PAGE_WIDTH, count * tHeight);
213: }
214:
215: /**
216: *
217: * @param target the main target to be rendered
218: * @param g
219: * @param yOffset the vertical offset where this target should be drawn
220: * @param hasMore if false this target is the last in the list
221: * @param hasPrev if false this target is the first in the list
222: */
223: private void drawTarget(ProgressModel target, Graphics2D g,
224: int yOffset, boolean hasMore, boolean hasPrev) {
225: g.setFont(mainFont);
226: g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
227: RenderingHints.VALUE_ANTIALIAS_ON);
228: g.setColor(Color.gray);
229: //7 vertical line (up)
230: if (hasPrev) {
231: g.drawLine(leftIndent + 8, yOffset, leftIndent + 8,
232: yOffset + 8);
233: }
234: //7 vertical line (down)
235: if (hasMore || target.subTargets.size() > 0) {
236: g.drawLine(leftIndent + 8, (tHeight / 2) + yOffset,
237: leftIndent + 8, tHeight + yOffset);
238: }
239: // sideways line
240: int xOffset = 0;
241: g.drawLine(leftIndent + 8, 8 + yOffset, leftIndent + xOffset
242: + 20, 8 + yOffset);
243: if (target.state == DONE) {
244: g.setColor(Color.darkGray);
245: }
246: if (target.state == INPROGRESS) {
247: g.setColor(progressColor);
248: }
249: if (target.state == TODO) {
250: g.setColor(Color.gray);
251: }
252: g.fillRoundRect(leftIndent + xOffset + 3, yOffset + 4, 11, 9,
253: 7, 7);
254: g.setColor(Color.black);
255: g.drawString(target.name, leftIndent + xOffset + 22,
256: 13 + yOffset);
257: if (target.subTargets.size() > 0) {
258: Iterator iter = target.subTargets.iterator();
259: for (int i = 1; iter.hasNext(); i++) {
260: drawSubTarget((ProgressModel) iter.next(), g,
261: yOffset += tHeight, hasMore
262: | i < target.subTargets.size());
263: }
264: }
265: }
266:
267: private void drawSubTarget(ProgressModel target, Graphics2D g,
268: int yOffset, boolean hasMore) {
269: g.setFont(subFont);
270: g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
271: RenderingHints.VALUE_ANTIALIAS_ON);
272: g.setColor(Color.gray);
273: //7 vertical line (up)
274: g
275: .drawLine(leftIndent + 8, yOffset, leftIndent + 8,
276: yOffset + 8);
277: //7 vertical line (down)
278: if (hasMore) {
279: g.drawLine(leftIndent + 8, (tHeight / 2) + yOffset,
280: leftIndent + 8, tHeight + yOffset);
281: }
282: int xOffset = 15;
283: // sideways line
284: g.drawLine(leftIndent + 8, 8 + yOffset, leftIndent + xOffset
285: + 4, 8 + yOffset);
286: if (target.state == DONE) {
287: g.setColor(Color.darkGray);
288: }
289: if (target.state == INPROGRESS) {
290: g.setColor(progressColor);
291: }
292: if (target.state == TODO) {
293: g.setColor(Color.gray);
294: }
295: g.fillRoundRect(leftIndent + xOffset + 4, yOffset + 5, 9, 7, 7,
296: 7);
297: g.setColor(Color.black);
298: g.drawString(target.name, leftIndent + xOffset + 15,
299: 12 + yOffset);
300: }
301:
302: public synchronized Dimension getPreferredScrollableViewportSize() {
303: return getPreferredSize();
304: }
305:
306: public synchronized int getScrollableUnitIncrement(
307: Rectangle visibleRect, int orientation, int direction) {
308: return tHeight;
309: }
310:
311: public synchronized int getScrollableBlockIncrement(
312: Rectangle visibleRect, int orientation, int direction) {
313: return tHeight * 3;
314: }
315:
316: public synchronized boolean getScrollableTracksViewportWidth() {
317: return true;
318: }
319:
320: public synchronized boolean getScrollableTracksViewportHeight() {
321: return false;
322: }
323:
324: public boolean getOpaque() {
325: return false;
326: }
327:
328: public static class ProgressModel {
329: int state = TODO;
330: String name;
331: List subTargets = new ArrayList();
332:
333: public ProgressModel(String name) {
334: this .name = name;
335: }
336:
337: int getHeight() {
338: return tHeight + subTargets.size() * tHeight;
339: }
340: }
341:
342: /**
343: * Use for unit testing
344: * @param targets
345: */
346: protected void setTargets(List targets) {
347: this.targets = targets;
348: }
349: }
|