1 /* 
2  * Copyright 2005 Paul Hinds
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package org.tp23.antinstaller.renderer.swing;
17
18import java.awt.Color;
19import java.awt.Dimension;
20import java.awt.Font;
21import java.awt.Graphics;
22import java.awt.Graphics2D;
23import java.awt.Rectangle;
24import java.awt.RenderingHints;
25import java.util.ArrayList;
26import java.util.Iterator;
27import java.util.List;
28
29import javax.swing.JPanel;
30import javax.swing.Scrollable;
31
32import org.apache.tools.ant.BuildEvent;
33import org.apache.tools.ant.Target;
34import org.tp23.antinstaller.InstallerContext;
35
36
37/**
38 * Progress panel prints a graphical view of the progress of the Ant targets
39 * being run.  It supports displaying one layer of dependent targets.
40 * @author Paul Hinds
41 * @version $Id: ProgressPanel.java,v 1.5 2006/12/21 00:02:59 teknopaul Exp $
42 */
43public class ProgressPanel extends JPanel implements Scrollable{
44
45    public static final int tHeight = 19;
46    public static final int leftIndent = 15;
47    public static final int DONE = 0;
48    public static final int INPROGRESS = 1;
49    public static final int TODO = 2;
50    private static final Color progressColor = new Color(0, 125, 0);
51    private static final Font mainFont = new Font("Dialog", Font.PLAIN, 11);
52    private static final Font subFont = new Font("Dialog", Font.PLAIN, 10);
53    List targets = null;
54    
55    private final InstallerContext ctx;
56    private int mainTargetPos = 0;
57    private ProgressModel currentPM = null;
58
59    public ProgressPanel(InstallerContext ctx) {
60        super(true);
61        this.ctx = ctx;
62    }
63
64    public synchronized void prepareCalledTargets(){
65        List targetStrings = ctx.getInstaller().getTargets(ctx);
66        Iterator iter = targetStrings.iterator();
67        targets = new ArrayList();
68        while (iter.hasNext()) {
69            String tgt = (String) iter.next();
70            targets.add(new ProgressModel(tgt));
71        }
72        this.setSize(getSize());// panel size changes
73        revalidate();
74        repaint();
75    }
76    /**
77     * This method assumes that we are send target started methods in order
78     * but that we do not have the information about "depends" targets and have to
79     * insert the information as it arrives.  If a TargetStarted event arrives that
80     * is not the expected target is is assumed to be a depends.
81     * @param buildEvent
82     */
83    public synchronized void targetStarted(BuildEvent buildEvent){
84        try {
85            Target tgt = buildEvent.getTarget();
86            ProgressModel pm = (ProgressModel)targets.get(mainTargetPos);
87            pm.state = INPROGRESS;
88            if(tgt.getName().equals(pm.name)){
89                // main target
90                currentPM = pm;
91                mainTargetPos++;
92                revalidate();
93                repaint();
94            }
95            else {
96                //dependency
97                ProgressModel dependency = new ProgressModel(tgt.getName());
98                dependency.state = INPROGRESS;
99                if(currentPM != null){
00                    currentPM.state = DONE;// this to catch antcall strangenesses
01                }
02                currentPM = dependency;
03                pm.subTargets.add(dependency);
04                this.setSize(getSize());// panel size changes
05                revalidate();
06                repaint();
07            }
08        }
09        catch (Exception e) {
10            ctx.log(e);
11        }
12    }
13    public synchronized void targetFinished() {
14        // BUG 1494105 reports NPE here, looks like no targets specified in his antinstaller-config.xml
15        currentPM.state = DONE;
16    }
17
18    /**
19     * N.B. buildFinished must be fired manually in Ant
20     */
21    public synchronized void buildFinished() {
22        // this is done because antcall sometimes results in targetFinished not being called
23        setToDone(targets);
24        repaint();
25    }
26    
27    private void setToDone(List pModels){
28        if(pModels == null || pModels.size() == 0){
29            return;
30        }
31        Iterator iter = pModels.iterator();
32        while (iter.hasNext()) {
33            ProgressModel pm = (ProgressModel)iter.next();
34            pm.state = DONE;
35            setToDone(pm.subTargets);
36        }
37    }
38    /**
39     */
40    public synchronized void paintComponent(Graphics g){
41        super.paintComponent(g);
42        if(targets == null){
43            return;
44        }
45//      g.setColor(getBackground());
46//      g.fillRect(g.getClipBounds().x,
47//                  g.getClipBounds().y,
48//                  g.getClipBounds().width,
49//                  g.getClipBounds().height);
50        Iterator iter = targets.iterator();
51        int offset = 0;
52        for(int i = 1; iter.hasNext(); i++){
53            ProgressModel pmodel = (ProgressModel)iter.next();
54            drawTarget(pmodel, (Graphics2D)g, offset, i < targets.size(), i > 1);
55            offset += tHeight;
56            offset += pmodel.subTargets.size() * tHeight;
57        }
58    }
59
60    public synchronized Dimension getPreferredSize(){
61        return getSize();
62    }
63    /**
64     * @see java.awt.Component#getSize()
65     */
66    public synchronized Dimension getSize() {
67        if(targets == null){
68            return new Dimension(SizeConstants.PAGE_WIDTH, tHeight * 5);
69        }
70        int count = targets.size();
71        Iterator iter = targets.iterator();
72        while(iter.hasNext()){
73            ProgressModel pmodel = (ProgressModel)iter.next();
74            count += pmodel.subTargets.size();
75        }
76        return new Dimension(SizeConstants.PAGE_WIDTH, count * tHeight);
77    }
78    /**
79     *
80     * @param target the main target to be rendered
81     * @param g
82     * @param yOffset the vertical offset where this target should be drawn
83     * @param hasMore if false this target is the last in the list
84     * @param hasPrev if false this target is the first in the list
85     */
86    private void drawTarget(ProgressModel target, Graphics2D g,
87            int yOffset, boolean hasMore, boolean hasPrev){
88        g.setFont(mainFont);
89        g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
90        g.setColor(Color.gray);
91        //7 vertical line (up)
92        if(hasPrev){
93            g.drawLine(leftIndent + 8, yOffset, leftIndent + 8, yOffset + 8);
94        }
95        //7 vertical line (down)
96        if(hasMore || target.subTargets.size() > 0){
97            g.drawLine(leftIndent + 8, (tHeight/2) + yOffset, 
98                    leftIndent + 8, tHeight + yOffset);
99        }
00        // sideways line
01        int xOffset = 0;
02        g.drawLine(leftIndent + 8, 8 + yOffset, leftIndent + xOffset + 20, 8 + yOffset);
03        if(target.state == DONE){
04            g.setColor(Color.darkGray);
05        }
06        if(target.state == INPROGRESS){
07            g.setColor(progressColor);
08        }
09        if(target.state == TODO){
10            g.setColor(Color.gray);
11        }
12        g.fillRoundRect(leftIndent + xOffset + 3, yOffset + 4, 11, 9, 7, 7);
13        g.setColor(Color.black);
14        g.drawString(target.name, leftIndent + xOffset + 22, 13 + yOffset);
15        if(target.subTargets.size() > 0){
16            Iterator iter = target.subTargets.iterator();
17            for(int i = 1; iter.hasNext(); i++){
18                drawSubTarget((ProgressModel)iter.next(), g, 
19                        yOffset += tHeight, hasMore | i < target.subTargets.size());
20            }
21        }
22    }
23    private void drawSubTarget(ProgressModel target, Graphics2D g, 
24            int yOffset, boolean hasMore){
25        g.setFont(subFont);
26        g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
27        g.setColor(Color.gray);
28        //7 vertical line (up)
29        g.drawLine(leftIndent + 8, yOffset, leftIndent + 8, yOffset + 8);
30        //7 vertical line (down)
31        if(hasMore){
32            g.drawLine(leftIndent + 8, (tHeight/2) + yOffset, leftIndent + 8, tHeight + yOffset);
33        }
34        int xOffset = 15;
35        // sideways line
36        g.drawLine(leftIndent + 8, 8 + yOffset, leftIndent + xOffset + 4, 8 + yOffset);
37        if(target.state == DONE){
38            g.setColor(Color.darkGray);
39        }
40        if(target.state == INPROGRESS){
41            g.setColor(progressColor);
42        }
43        if(target.state == TODO){
44            g.setColor(Color.gray);
45        }
46        g.fillRoundRect(leftIndent + xOffset + 4, yOffset + 5, 9, 7, 7, 7);
47        g.setColor(Color.black);
48        g.drawString(target.name, leftIndent + xOffset + 15, 12 + yOffset);
49    }
50
51    public synchronized Dimension getPreferredScrollableViewportSize(){
52        return getPreferredSize();
53    }
54    public synchronized int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction){
55        return tHeight;
56    }
57    public synchronized int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction){
58        return tHeight * 3;
59    }
60    public synchronized boolean getScrollableTracksViewportWidth(){
61        return true;
62    }
63    public synchronized boolean getScrollableTracksViewportHeight(){
64        return false;
65    }
66    public static class ProgressModel{
67        int state = TODO;
68        String name;
69        List subTargets = new ArrayList();
70
71        public ProgressModel(String name){
72            this.name = name;
73        }
74
75        int getHeight(){
76            return tHeight + subTargets.size() * tHeight;
77        }
78    }
79}
80