Smartlog v. II » Java, Swing and the JFC
Opret egen blog | Nęste blog »

Even an true believer can be converted...

11. maj 2006 09:27, chp

The last couple of months I have had the chance to work on the Microsoft .Net platform. I made some small project in C# and overall the experience has been positive.

There are many things in the .net platform which I think are very very smart - but not everything is as I think it should be. What bothers me the most is the lack of refactoring tools in Visual Studio (I'm using 2005 Team). In Eclipse I have a very rich set of refacotring tools and an even richer set of plugins to choose from. In VS2005 I have only the very basic set of tools (class / variable rename) and a few others. Why is this important? Because refactoring leads to better software. If you don't learn anything during a project - you are either waisting your time or being a complete idiot. Maintainabillity should be a key design parameter for any software project - and any IDE should assist the developer in this.

All that being said I must admit that C# has it's moments. It is general a good robust language and the .net way of letting multiple developers work on the same project in different languages is great. In my case I have been making some C# based dll's with custom functionality - which in turn are being used by my only VB compatible partner...

That all for now
CHP

JavaTail

1. jan 2006 23:45, chp

A new edition of my free tail tool is now available from http://javatail.cpc.dk

It installs via web start and can be used to monitor logfiles as they are written.

Great Java newsletter

20. dec 2005 07:01, chp

Yesterday I got another great newsletter in my inbox from Dr. Heinz M Kabutz. That insipired me to this small commercial for his great and often quite funny newsletter. You can sign up and read past editions from his homepage: http://www.cretesoft.com

Of course this newsletter is also available as an RSS: http://www.cretesoft.com/archive/tjsn.rss

Thats all for now

CHP

Who is calling me?

12. dec 2005 07:54, chp

Have you ever had the problem that you had to track from where a particular method is being called? Of course any decent editor will allow you to see references from the source code, but how do you see this information at runtime? One approach is to let every caller identify them self by either reference or a String name. This is not very elegant - but I have certainly seen it in use several times.

Actually I have stumpled over this approach again this week, which inspired me to write this post with a little tip. Say we have a method that we wish to track who is calling, but we do not want to update the signature of the method. We might not even have access to the source of all callers. The solution is Exceptionally simple :-)

Simply insert the following code block at the point in your method were you want to log who is calling>

if (LOG.isDebugEnabled()) {
 try {
            throw new Exception("Used to debug who caller is");
        } catch (Exception e) {
            StackTraceElement[] stacks = e.getStackTrace();
            LOG.debug("We were called by " +
                    stacks[3].getClassName() + "." +                  stacks[3].getMethodName() + ":"
+ stacks[3].getLineNumber());
        }
}

Of course this will cause some performance, so this is not something we want in a running production enviroment. But then again - you hopefully do not need this type of information from a running production enviroment.

Best Regard
CHP

P.S.
I hope this long long period of silence will be my alltime low...

Dynamicly change classpath at runtim

28. aug 2005 06:09, chp

After a long time without posting, I finally got the time to update this blog. This post shows how to dynamically alter the classpath at runtime. I use this on my current project to let users write their own java based plugin's, but the technique is generally applicable. It could for instance be used to let users supply their own specific JDBC driver for your application to use.

 

In this example I have choosen that users should place all their plugin's in a specific directory. To install a plugin user will simply copy his class file to this directory. Of course plugin's will have to implement a specific interface for us to use them, but that is a different matter.

 

The following class provides a set of static methods that will add a file or url to the classpath of the running program, thus enabling us to generate new instances of these:


import java.io.File;
import java.io.IOException;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;

public class ClassPathHacker {
    private static final Class[] parameters = new Class[] {URL.class};

    public static void addFile(String s) throws IOException {
        addFile(new File(s));
    }

    public static void addFile(File f) throws IOException {
        addURL(f.toURL());
    }

    public static void addURL(URL u) throws IOException {
        URLClassLoader sysloader=(URLClassLoader)ClassLoader.getSystemClassLoader();
        Class sysclass=URLClassLoader.class;
        try {
            Method method=sysclass.getDeclaredMethod("addURL", parameters);
            method.setAccessible(true);
            method.invoke(sysloader, new Object[] {u});
        }
        catch (Throwable t) {
            t.printStackTrace();
            throw new IOException("Error, could not add URL to system classloader");
        }
    }
}


How did we do that?
Well a few tricks (hacks) are being used here. First is the fact that the system classloader on a standard JRE is always an URLClassLoader. The second, and more rude hack is the use of reflection to make the private addURL method accessible. This allows us to call this method and thereby adding files to the classpath of the systems default classloader. Simple but efficient...
:-)


Until next time - Best Regards

Christian Petersen

Modal locking issue revisited

2. aug 2005 13:54, chp

After a little more research into the problem described in my earlier post I now have a better understanding of the problem.


The application freezes because focus is given to a window which is waiting for a modal sub dialog. To demonstrate this I have added some WindowFocusListeners to the demo application. Here is the updated source code:


import java.awt.*;
import java.awt.event.*;

import javax.swing.*;

public class ModalLockTest extends JDialog {

    private static int counter = 0;
   
    private Frame frame;
    private ModalLockTest lockChildDiag;
    private ModalLockTest nonLockChildDiag;
    private int index;
    private boolean locked;
   
    public ModalLockTest(Frame f) {
        super(f, true);
        frame = f;
        locked = true;
        init();
        addWindowFocusListener(new WindowFocusListener() {

            public void windowGainedFocus(WindowEvent e) {
                System.out.println("Dialog #" + index + " gained focus");
            }

            public void windowLostFocus(WindowEvent e) {
                System.out.println("Dialog #" + index + " lost focus");
            }
           
        });
    }
   
    public ModalLockTest(Frame f, JDialog d) {
        super(d, true);
        frame = f;
        locked = false;
        init();
    }
   
    private void init() {
        index = counter++;
        JButton first = new JButton("Open non-locking dialog");
        JButton second = new JButton("Open locking dialog");
        JButton close = new JButton("Close");
       
        getContentPane().setLayout(new GridLayout(3,1));
        getContentPane().add(first);
        getContentPane().add(second);
        getContentPane().add(close);
       
        first.addActionListener(new ActionListener() {

            public void actionPerformed(ActionEvent arg0) {
                if (nonLockChildDiag == null) {
                    nonLockChildDiag = new ModalLockTest(frame, ModalLockTest.this);
                }
                System.out.println("Showing non-locking dialog #" + nonLockChildDiag.index);
                nonLockChildDiag.setVisible(true);
                System.out.println("Showed non-locking dialog #" + nonLockChildDiag.index);
            }
           
        });
       
        second.addActionListener(new ActionListener() {

            public void actionPerformed(ActionEvent arg0) {
                if (lockChildDiag == null) {
                    lockChildDiag = new ModalLockTest(frame);
                }
                System.out.println("Showing locking dialog #" + lockChildDiag.index);
                lockChildDiag.setVisible(true);
                System.out.println("Showed locking dialog #" + lockChildDiag.index);
               
            }
           
        });
       
        close.addActionListener(new ActionListener() {

            public void actionPerformed(ActionEvent arg0) {
                ModalLockTest.this.setVisible(false);
                System.out.println("Closing " + ((locked) ? "locked" : "unlocked") + " dialog #" + index);
            }
           
        });
        setTitle("Test of modal lock problem");
        setSize(300,300);
        setLocationRelativeTo(frame);
    }
   
    public static void main(String[] args) {
        final JFrame fra = new JFrame("Modal Lock Test");
        JButton open = new JButton("Open Dialog");
        JButton close = new JButton("Close Test App");
        final ModalLockTest diag = new ModalLockTest(fra);
        open.addActionListener(new ActionListener() {

            public void actionPerformed(ActionEvent arg0) {
                System.out.println("Showing dialog #" + diag.index);
                diag.setVisible(true);
                System.out.println("Showed dialog #" + diag.index);
            }
           
        });
       
        close.addActionListener(new ActionListener() {

            public void actionPerformed(ActionEvent arg0) {
                fra.setVisible(false);
                System.out.println("Closed frame");
                System.exit(0);
            }
           
        });
        fra.addWindowFocusListener(new WindowFocusListener() {

            public void windowGainedFocus(WindowEvent e) {
                System.out.println("Top Frame gained focus");
            }

            public void windowLostFocus(WindowEvent e) {
                System.out.println("Top Frame lost focus");
            }
           
        });
        fra.getContentPane().setLayout(new GridLayout(2,1));
        fra.getContentPane().add(open);
        fra.getContentPane().add(close);
        fra.setSize(300,300);
        fra.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        fra.setVisible(true);
    }
   
   
}

Last look at the Buffon Needle test

23. jul 2005 11:58, chp

Finally, I have had some time to write what will be my last post on the Buffon Needle example posted below. First we will have a look at the NEEDLE_GENERATOR Runnable field of the CustomComponent class.

Note: This post discusses the Buffon Needle demo defined in a previous post.


Having a Runnable as a field of a GUI component might seem a bit ought, but this is a deliberate design choice on my part. The purpose of the Runnable is to generate new needles and add these to the CustomComponent. By declaring this Runnable as a non static anonymous inner class gives us a few advantages:

·         We are within the scope of the CustomComponent class

·         Simple design, no need to transfer values of width, height and unit size.

·         Needle generation can be done in a separate thread. This is very important.

 

The run method of the NEEDLE_GENERATOR is very simple. First we enter a for loop that generates and adds the needles. Then we order a repaint and instruct the results panel to update its values. If we wanted to create an animation effect we would move the last two lines inside the for loop. However, given the nature of our application we would spend most of the time repainting. Here is a look at the code:

 

public void run() {

            for (int i = 0;i < needles_to_generate;i++) {
                addNeedle(new BuffonNeedle(width, height, unit));               
            }
            repaint();
            results.updateValues();
        }

 

The NEEDLE_GENERATOR is used to generate a new thread by the ActionListener registered on the "Add needles" button on the PaintController. The actionPerformed method looks like this:

 

public void actionPerformed(ActionEvent e) {
                component.setNeedles_to_generate(((Number) spinner.getValue()).intValue());
                new Thread(component.NEEDLE_GENERATOR).start();
            }

 

Also a very simple method. First we update the instance of CustomComponent denoted by the component reference with the current value of the number of needles to generate spinner. This way the user of our application will be allowed to control how many needles are generated. The we start a new thread and let the AWTEventQueue continue. This keeps out app responsive no matter how many needles the user chooses to generate.

 

All in all the Buffon Needle test is a very good example of a simple custom swing component, that could still cause massive performance problems if implemented wrong.

 

I hope that this has given you inspiration to develop your own custom swing components, and thereby driving your java apps further.

 

The source code is available here. :-)

Best Regards
CHP

Further look at the Buffon Needle test Swing component

19. jul 2005 13:43, chp

Hi again - as I promised earlier I will now start a more detailed look at the swing part of the Buffon Needle app.

Note: This post discusses a method of the CustomComponent class descriped in an earlier post. You can find the post and the full source below this post...

Lets start by examining the paintComponent(Graphics gr) method. The first thing we do is to call supers implementation as this will take care of any borders user may have set and so on.    

 super.paintComponent(gr);

 We then specialize the given java.awt.Graphics object to a java.awt.Graphics2D object.

Graphics2D g2d = (Graphics2D) gr;

Through the PaintController panel (which we will look at in a later post :-)) a variety of properties are available. One of these is whether or not our rendered image will be drawn anti aliased.  Anti aliasing is a nice effect that makes our panel look more professional. In some cases anti aliasing is not desired as you do not have total control of the pixels drawn. We toggle anti aliasing using the following code:

           if (antiAliased) {
             g2d.setRenderingHint(                                            RenderingHints.KEY_ANTIALIASING,                              RenderingHints.VALUE_ANTIALIAS_ON);
           }

We now have to determine the length of 1 unit. This is done based on the current size of the panel. We have a fixed number of parallel lines (10) which means that 1 unit = height / 11. We do also extract and store the width as the "paper" size is needed for generating new needles.

            Dimension dimension = getSize();
            width = (int) dimension.getWidth();
            height = (int) dimension.getHeight();
         
            unit = height / 11;

 

Now we have all the data we need to draw the parallel lines. Since the PaintController also allows the user to specify the Color of the lines we will start by setting the foreground color to the current value of the lineColor object.

            g2d.setColor(lineColor);

We then proceed directly to a for loop drawing the ten horizontal lines. Since each line is exactly one unit apart the for loop is pretty straight forward. One thing to note here is the fact that the top left corner of the panel is always (0,0). This means that we in fact are working in the 4. quadrant of the standard geometric coordinate system.

            for (int i = 0;i < 10;i++) {
                int y = unit + (i*unit);
                g2d.drawLine(0, y, width, y);
            }

The last bit of our paint method takes care of painting the needles. Note that the number of needles that we paint is limited. This is to protect our little application from the memory leaks that would occur if we kept references to all generated needles. This limit does not have any influence on the precision of calculated value, since this calculation relies on the static fields of the BuffonNeedle class. Since we keep the needle references in a generic Collection we can use the Iterable interface through the new for loop to loop over all needles.  First  we set the specified needleColor which is also configurable.

            g2d.setColor(needleColor);
            for (BuffonNeedle bn : needles) {
                g2d.drawLine(bn.getX1(), bn.getY1(),                                      bn.getX2(), bn.getY2());
            }

That is all there is to it... These few lines of code is all it takes for our new custom component to paint itself. If you look at the full source for this class you will se that there is really not any other "magic" in this class. Except perhaps that Runnable which we will discuss in a later post.

 

If you have any questions to this (or any other java question)  please do not hesitate contacting me.

Source files for Buffon Needle Custom Component

19. jul 2005 05:16, chp

Here is a link to download the source files. Later today I will post a detailed description of what goes on the paintComponent(Graphics gr) method.

Creating a custom component in swing

18. jul 2005 08:03, chp

Although Swing has a very rich set of good components it is very often necessary to create a custom component to display your data just right. When creating a custom component there are many things to consider. I have created a small example of a custom component that is usable for a very specific need, but of course it is always a good idea to evaluate if your custom component has a broader use than just your current app (i.e. a Calendar pop up or something). In this example we ignore all this common sense and assume that our custom component will only be used for this specific purpose.

Preface: For demonstration purposes I have created a very simple implementation of Buffons needle test. This is an old (16th century) way to determine the value of Mathematical constant PI. In theory it is very precise, but in practise it takes a LOT of trials to get even a basic value of PI. The experiment uses the Monte Carlo simulation method which uses random numbers to compute a value. Monte Carlo algorithms can succeed where there are no simple computational formulas.

 

This description contains some math and assumes some pre-existing understanding of geometry. If all you care about is the swing stuff you can skip this section. :-)

 

Description of Buffons needle test:

Suppose we dropped needles, each of length 1, at random on to a piece of paper ruled with parallel lines 1 unit apart. What is the probability that a needle crosses a line?

 

We use three uniformly distributed random values to represent a needle drop:

·         X the x coordinate of needles midpoint

·         Y the y coordinate of needles midpoint

·         Θ (theta) the needles angle of rotation

 

Using X and Y we can deduct the perpendicular distance d to the nearest line.

Here is a small sketch of the setup:

Sketch of Buffons Needle drop test

We can see that the needle crosses the line whenever: Line cross condition

The value of d can range from 0 to ½ and the value of Θ can range from 0 to π. This is the rectangle below which has an area of:

 Area of rectangle

Rectangle

The curve inside the rectangle is the function:

 Function

The grey area under the curve is where:

 Line Crossings

The area of the grey region can be determined:

Area under curve

The probability that a needle crosses a line is the ratio of the shaded area under the curve to the total area of the rectangle:

Probability

This means that if continue to drop needles the ratio of the number of needles that cross a line to the total number of needle will approach

  Probability from which we can compute the value of π.

Very smart right? Personally I find this little algorithm very elegant (although it is neither terribly precise nor fast).

The dropping of needles (i.e. generation of random numbers and determination of line cross) is handled by a very basic class called BuffonNeedle. Every instance of this class models a single needle, and some static variables keep track of results. Please bear in mind that this is neither a Monte Carlo demo nor a PI finding demo. Therefore the BuffonNeedle class is somewhat naive in its design and is not at all expected to perform well. J In practice we will spend most of our time doing GC.

Swing stuff

We do however want to display our needle dropping in swing, and since none of the standard components seems appropriate we will make our own. The first thing we have to do is to decide which class should be our super class. We need to extend an existing swing (or AWT) class in order to be able to use our custom component as any other swing component. Since our component is to only draw our needles as they are dropped JPanel seems like a good choice. We create the following class:

public class CustomComponent extends javax.swing.JPanel

When customizing our subclass we have two choices - we can override either paint(Graphics gr) or paintComponent(Graphics gr). The paint() is to be considered as a more "raw" way of doing it. If your current look and feel specifies a special background color (almost all LF do this), you will have to set this your self whereas paintComponent is executed after super has taken care of such things. We therefore choose to override paintComponent(Graphics gr).

We also create two other panels, one to display results of our calculations and one to allow us to play a little with the way of paintComponent method draws.

Screenshot of application running:

Screen shot

The CustomComponent class also contains a static void main method that will create and show a JFrame containg all three panels.

 

The source code for CustomComponent.java:

*************************************
import java.awt.*;
import java.util.LinkedList;
import java.util.Vector;

import javax.swing.JFrame;
import javax.swing.JPanel;


/**
 * CustomComponentTest is used to demonstrate various painting techniques.
 *
 * Creation Date: 15/07-2005
 * @author Christian Petersen
 * @version 1.0
 */

public class CustomComponent extends JPanel {

    private boolean antiAliased = true;
   
    private Color lineColor = Color.BLACK;
    private Color needleColor = Color.GRAY;
   
    //We use a Vector since this is threadsafe.
    private Vector<BuffonNeedle> needles = new Vector<BuffonNeedle>();
   
    //Since we do not what to meat Mr. OutOfMemoryException we limit the number of needles we keep and paint.
    private int maximumNeedleCount = 1000;
   
    private int unit;
    private int height;
    private int width;
    private int needles_to_generate;
    private BuffonResultsPanel results;
   
   
    public CustomComponent(BuffonResultsPanel res) {
        super();
        results = res;
    }
   
    /**
     * Adds a needle to list and removes oldest if max size has been reached.
     * @param bn The needle to add
     */
    public void addNeedle(BuffonNeedle bn) {
        needles.add(bn);
        if (needles.size() > maximumNeedleCount) {
            needles.remove(0);
        }
    }
   
    public Runnable NEEDLE_GENERATOR = new Runnable() {

        public void run() {
            //Generate and add needles.
            for (int i = 0;i < needles_to_generate;i++) {
                addNeedle(new BuffonNeedle(width, height, unit));               
            }
            repaint();
            results.updateValues();
        }
       
    };
   
    /**
     * Paints the current paper with lines and any needles currently in the list.
     */
    public void paintComponent(Graphics gr) {
        //First we let super draw itself, this will draw background color and borders
        super.paintComponent(gr);
       
        //We cast the given java.awt.Graphics objet to a java.awt.Graphics2D object.
        Graphics2D g2d = (Graphics2D) gr;
       
        //Set anti aliasing
        if (antiAliased) {
            g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        }
       
        //Get size of panel
        Dimension dimension = getSize();
        width = (int) dimension.getWidth();
        height = (int) dimension.getHeight();
        //Calculate and store the value of 1 unit
        unit = height / 11;
       
        //Draw horizontal lines
        g2d.setColor(lineColor);
        for (int i = 0;i < 10;i++) {
            int y = unit + (i*unit);
            g2d.drawLine(0, y, width, y);
        }
       
        //Set needle color and draw all needles in list
        g2d.setColor(needleColor);
        for (BuffonNeedle bn : needles) {
            g2d.drawLine(bn.getX1(), bn.getY1(), bn.getX2(), bn.getY2());
        }   
    }
   
    public static void main(String[] args) {
        JFrame frame = new JFrame();
        frame.getContentPane().setLayout(new BorderLayout());
        BuffonResultsPanel res = new BuffonResultsPanel();
        CustomComponent cmb = new CustomComponent(res);
        frame.getContentPane().add(new PaintController(cmb), BorderLayout.NORTH);
        frame.getContentPane().add(cmb, BorderLayout.CENTER);
        frame.getContentPane().add(res, BorderLayout.SOUTH);
        frame.setSize(800, 600);
        frame.setTitle("Test of custom swing component");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setVisible(true);
    }

    public boolean isAntiAliased() {
        return antiAliased;
    }

    public void setAntiAliased(boolean antiAliased) {
        this.antiAliased = antiAliased;
    }

    public Color getLineColor() {
        return lineColor;
    }

    public void setLineColor(Color lineColor) {
        this.lineColor = lineColor;
    }

    public int getMaximumNeedleCount() {
        return maximumNeedleCount;
    }

    public void setMaximumNeedleCount(int maximumNeedleCount) {
        this.maximumNeedleCount = maximumNeedleCount;
    }

    public Color getNeedleColor() {
        return needleColor;
    }

    public void setNeedleColor(Color needleColor) {
        this.needleColor = needleColor;
    }

    public int getUnit() {
        return unit;
    }

    public int getNeedles_to_generate() {
        return needles_to_generate;
    }

    public void setNeedles_to_generate(int needles_to_generate) {
        this.needles_to_generate = needles_to_generate;
    }
   
}

*************************************

The source code for BuffonNeedle.java

*************************************
import java.util.Random;

/**
 * This class implements the BuffonNeedle, which uses the MonteCarlo simulation method to determine the value of PI.
 * This implementation has been created for demonstration purposes only and is NOT designed to perform well.
 *
 * Creation Date: 18/07-2005
 *
 * @author Christian Petersen (christian.petersen@ciber.dk)
 * @version 1.0
 */
public class BuffonNeedle {

    /** Generator used to get uniform distributed values */
    private static final Random RANDOM = new Random(System.currentTimeMillis());
   
    /** Total number of generated needles */
    public static int TOTAL_NEEDLE_COUNT = 0;
   
    /** Total number of needles that cross a line */
    public static int TOTAL_CROSSINGS = 0;
   
    /** x coord of one end of a needle */       
    private double x1;
   
    /** x coord of the other end of a needle */ 
    private double x2;
   
    /** y coord of one end of a needle */       
    private double y1;
   
    /** y coord of the other end of a needle */ 
    private double y2;
   
    /** Angle of rotation about the midpoint */ 
    private double theta;
   
    /** Indication of line cross */
    private boolean crossingLine;

   
    /**
     * Generate new needle. This will drop the needle randomly at paper, and compute if line is crossed.
     * NOTE: Paper is assumed to start at (0,0).
     * @param xWidth The width of paper
     * @param yHeight The height of paper
     */
    public BuffonNeedle(double xWidth, double yHeight, double unit) {
        TOTAL_NEEDLE_COUNT++;
        // Compute random values for the x and y coordinates of the
        // needle's midpoint, and for the needle's angle of rotation.
        double xCenter =  xWidth*RANDOM.nextDouble();
        double yCenter = yHeight*RANDOM.nextDouble();
        double theta = Math.PI*RANDOM.nextFloat();

        double sin = Math.sin(theta);
        double cos = Math.cos(theta);

        // Rotate about the origin a 1-unit length needle on
        // the x axis with endpoints at -0.5 and +0.5.
        x1 = (-0.5d * unit) * cos;
        x2 = (+0.5d * unit) * cos;
        y1 = (-0.5d * unit) * (-sin);
        y2 = (+0.5d * unit) * (-sin);

        // Translate the needle to its location.
        x1 += xCenter;
        x2 += xCenter;
        y1 += yCenter;
        y2 += yCenter;

        // Does the needle cross a line?
        if (Math.floor((y1 / unit)) != Math.floor((y2 / unit))) {TOTAL_CROSSINGS++;}
   }
   
     /**
     * Return the current computed value of pi
     * @return the value
     */
    public static double getPi() {
        return (2d * TOTAL_NEEDLE_COUNT) / TOTAL_CROSSINGS;
    }
   
    public boolean isCrossingLine() {
        return crossingLine;
    }

    public void setCrossingLine(boolean crossingLine) {
        this.crossingLine = crossingLine;
    }

    public double getTheta() {
        return theta;
    }

    /**
     * Returned as an int so value can be used directly by .draw methods
     * @return
     */
    public int getX1() {
        return (int) x1;
    }

    /**
     * Returned as an int so value can be used directly by .draw methods
     * @return
     */
    public int getX2() {
        return (int) x2;
    }

    /**
     * Returned as an int so value can be used directly by .draw methods
     * @return
     */
    public int getY1() {
        return (int) y1;
    }

    /**
     * Returned as an int so value can be used directly by .draw methods
     * @return
     */
    public int getY2() {
        return (int) y2;
    }

   
   
}

*************************************

The source code for PaintController.java

*************************************

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.*;

public class PaintController extends JPanel {

    private CustomComponent component;
   
    public PaintController(CustomComponent cmp) {
        super();
        component = cmp;
        JCheckBox jch = new JCheckBox("Draw Anti-aliased");
        jch.setSelected(component.isAntiAliased());
        jch.addActionListener(new ActionListener() {

            public void actionPerformed(ActionEvent e) {
                //We just change value - assuming JCheckBox has been initialized correct
                component.setAntiAliased(!component.isAntiAliased());
                component.repaint();
            }
           
        });
        add(jch);
       
        JButton lcolor = new JButton("Line Color");
        lcolor.addActionListener(new ActionListener() {

            public void actionPerformed(ActionEvent e) {
                //We use static convenience method on JColorChooser to show a modal dialog
                component.setLineColor(JColorChooser.showDialog(PaintController.this,
                                                                "Choose color of lines",
                                                                component.getLineColor()));
                component.repaint();
            }
        });
        add(lcolor);
       
        JButton ncolor = new JButton("Needle Color");
        ncolor.addActionListener(new ActionListener() {

            public void actionPerformed(ActionEvent e) {
                //We use static convenience method on JColorChooser to show a modal dialog
                component.setNeedleColor(JColorChooser.showDialog(PaintController.this,
                                                                "Choose color of needles",
                                                                component.getNeedleColor()));
                component.repaint();
            }
        });
        add(ncolor);
       
        final JSpinner spinner = new JSpinner(new SpinnerNumberModel(10000, 1, 100000000, 10000));
        add(spinner);

       
        JButton dropNeedles = new JButton("Add needles");
        dropNeedles.addActionListener(new ActionListener() {

            public void actionPerformed(ActionEvent e) {
                //Set number of needles to add
                component.setNeedles_to_generate(((Number) spinner.getValue()).intValue());
                //Start new thread to generate needles. This will allow this method to return and the AWTEventQueue to continue
                new Thread(component.NEEDLE_GENERATOR).start();
            }
        });
        add(dropNeedles);
       
       
    }
   
   
}

*************************************

The soure code for BuffonResultsPanel.java

*************************************
import javax.swing.*;

public class BuffonResultsPanel extends JPanel {

    private JLabel pi = new JLabel("\u03C0" + " = ");
    private JLabel pi_value = new JLabel("no data");
    private JLabel needles = new JLabel("Total needles = ");
    private JLabel needles_value = new JLabel("0");
    private JLabel crossings = new JLabel("Crossings = ");
    private JLabel crossings_value = new JLabel("0");
   
    public BuffonResultsPanel() {
        super();
        add(pi);
        add(pi_value);
        add(needles);
        add(needles_value);
        add(crossings);
        add(crossings_value);
    }

    public void updateValues() {
        pi_value.setText(Double.toString(BuffonNeedle.getPi()));
        needles_value.setText(Integer.toString(BuffonNeedle.TOTAL_NEEDLE_COUNT));
        crossings_value.setText(Integer.toString(BuffonNeedle.TOTAL_CROSSINGS));
        repaint();
    }
   
}

*************************************

 

 

Test program for Modal Deadlock issue

14. jul 2005 15:07, chp

The following is a small program that will enable you to test / demonstrate the modal deadlock issue I descriped in my previous post. Please don't mind the syntax as this was done very quickly. :-)

On my pc (WinXP SP2, JDK 1.5.0_04) the deadlock occurs if I do the following:

Start the app -> Click Open Dialog -> Open locking dialog -> Open locking dialog -> Open locking dialog -> Close -> Close -> Close -> Open locking dialog -> Open locking dialog -> Open locking dialog -> Close

I know this is a bit tricky, but my quess is that a lot of applications out there will be struck by this hard to find bug.

Here comes the code: [Save and compile in ModalLockTest.java]
-------------------------------------
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
public class ModalLockTest extends JDialog{
private static int counter = 0;
    private Frame frame;
private ModalLockTest lockChildDiag;
private ModalLockTest nonLockChildDiag;
private int index;
private boolean locked;
    
    public ModalLockTest(Frame f) {
        super(f, true);
        frame = f;
locked = true;
    
init();
}
public ModalLockTest(Frame f, JDialog d) {
super(d, true);
frame = f;
locked = false;
init();
}
private void init() {
index = counter++;
JButton first = new JButton("Open non-locking dialog");
        JButton second = new JButton("Open locking dialog");
JButton close = new JButton("Close");
        
        getContentPane().setLayout(new GridLayout(3,1));
        getContentPane().add(first);
        getContentPane().add(second);
getContentPane().add(close);
        
        first.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent arg0) {
if (nonLockChildDiag == null) {
nonLockChildDiag = new ModalLockTest(frame, ModalLockTest.this);
}
System.out.println("Showing non-locking dialog #" + nonLockChildDiag.index);
nonLockChildDiag.setVisible(true);
System.out.println("Showed non-locking dialog #" + nonLockChildDiag.index);
            }
            
        });
        
        second.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent arg0) {
if (lockChildDiag == null) {
lockChildDiag = new ModalLockTest(frame);
}
System.out.println("Showing locking dialog #" + lockChildDiag.index);
lockChildDiag.setVisible(true);
System.out.println("Showed locking dialog #" + lockChildDiag.index);
            }
            
        });
close.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
ModalLockTest.this.setVisible(false);
System.out.println("Closing " + ((locked) ? "locked" : "unlocked") + " dialog #" + index);
}
});
        setTitle("Test of modal lock problem");
        setSize(300,300);
        setLocationRelativeTo(frame);
    }
    
    public static void main(String[] args) {
        final JFrame fra = new JFrame("Modal Lock Test");
        JButton open = new JButton("Open Dialog");
JButton close = new JButton("Close Test App");
        final ModalLockTest diag = new ModalLockTest(fra);
        open.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent arg0) {
System.out.println("Showing dialog #" + diag.index);
                diag.setVisible(true);
System.out.println("Showed dialog #" + diag.index);
            }
            
        });
close.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
fra.setVisible(false);
System.out.println("Closed frame");
System.exit(0);
}
});
fra.getContentPane().setLayout(new GridLayout(2,1));
        fra.getContentPane().add(open);
fra.getContentPane().add(close);
        fra.setSize(300,300);
        fra.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        fra.setVisible(true);
    }
}

JDK 1.5.0_04 Modal dialog deadlock issue.

14. jul 2005 15:05, chp

It appears that something has changed in the AWT EventQueue thread with the latest JDK update.
 I have not yet tested if _03 also have this. This change will cause your modal dialogs to run into a deadlock when being closed, if the dialogs are not constructed with the right parent.
I will post a test program soon, which will demonstrate this. But the short solution is: Use the right parent (ie. the openener, not the bottom frame).

First entry in my new blog

14. jul 2005 15:02, chp

Hi there

You are reading the very first post in my new blog. Here I will post on various java related issues.

Of course I will be delighted to hear any comments you may have.

BR
Christian Petersen

P.S.
The first couple of posts have been transfered from my old blog, so their timestamps are somewhat misleading... :-)