Expandable Menu in Java Swing app

Hello JFriends,
is it possible to create a expandable menu in java swing app? I am thinking of something like menu in MS Visio: http://img32.imageshack.us/i/menuip.jpg/
It works like that: When user click on a bar it expands (with nice animation) and shows containing components.
Or maybye there are components like that?
Thanks in advance for Your reply and please forgive me if my english is bad.
JRegards :-)

Yes, such constructs are possible. There isn't a pre-made component exactly like that. The NetBeans IDE has a Window - named Palette - that's very similar. The code is open source.
You can read about Java menus here:
[http://java.sun.com/docs/books/tutorial/uiswing/components/menu.html]

Similar Messages

  • Java Swing apps cause Windows 2K to hang

    I have an IBM clone machine running a P4 and Windows 2k that hangs whenever I try to exit out of a window in a Java swing app. Whether the window is the main application window or a menu window instantiated by the application to be a child of the main window, does not matter. It hangs regardless. I have tried multithreaded apps and they do not cause my windows machine to hang. Neither do simple swing apps. But any multithreaded swing app causes it to hang. JEdit causes it. Forte causes it. I am running Java 1.4.1. Thanks for any help in this.

    Freezes under Java 1.4.1 with Windows
    Posted on Dec 14, 2002 - 02:57 PM by slava
    Installation This is turning into a FAQ, so I thought I'd post it on the community site. A lot of people have complained that jEdit can hard freeze their windows system after upgrading to Java 1.4.1. If you experience this, the problem is not with jEdit, but with your video driver. Updating the driver to the latest version should solve the problem; downgrading to Java 1.4.0 might also work.
    http://community.jedit.org/modules.php?op=modload&name=news&file=article&sid=190&mode=thread&order=0&thold=0

  • JAVA / SWING apps, font AA / LaF KDE 4.3

    Hi Everyone,
    I know the title is terrible, but here is my problem:
    I am using Netbeans for most of my development projects. I am very happy with it; however, the look and feel of Netbeans (and most other Java apps, such as FreeMind for example) is appalling and does not integrate into my KDE 4.3 desktop at all. I understand that you can start netbeans with certain options to change the look and feel to GTK, but the fonts still look jagged.
    I followed this forum post http://bbs.archlinux.org/viewtopic.php?id=72892 and tried to set the JRE / JAVA options globally, so that all java apps start with GTK laf and AA. However, non of the methods described work / netbeans overrides them (?).
    But really, the biggest problem are the ugly fonts. Java / SWING apps do not seem to recognize my KDE settings for AA and font style at all. And non of the Cl options work to fix it.
    Does anyone have any tips or tricks to fix any of these issues I am having with the combination Java / Swing / KDE / NB?
    Running:
    Archlinux (duh)
    KDE 4.3 / qt-gtk-engine
    sun-jdk 1.6uX
    Netbeans 6.7.1
    Thanks!
    KnY

    I added what I knew about improving Sun java fonts to the wiki:  http://wiki.archlinux.org/index.php/Jav … _-_Sun_JRE.  You may want to try other fonts than B&H's Lucida.  See the section in the wiki article about changing the default fonts by editing 'fontconfig.properties'.

  • Running java swing apps thru telnet... [Is this possible?]

    Hi All!
    I am just wandering if it is possible to run swing applications thru telnet since everytime I run it... it returns ang error....
    Exception in thread "main" java.lang.NoClassDefFoundError: sun/awt/X11GraphicsEnvironment
    at java.lang.Class.forName1(Native Method)
    at java.lang.Class.forName(Class.java:173)
    at java.awt.GraphicsEnvironment.getLocalGraphicsEnvironment(GraphicsEnvironment.java:90)
    at at sun.awt.motif.MToolkit.<clinit>(MToolkit.java:109).null(Unknown Source)
    at java.lang.Class.forName1(Native Method)
    at java.lang.Class.forName(Class.java:173)
    at java.awt.Toolkit$2.run(Toolkit.java:754)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.awt.Toolkit.getDefaultToolkit(Toolkit.java:745)
    at javax.swing.ImageIcon.<init>(ImageIcon.java:226)
    at javax.swing.LookAndFeel$1.createValue(LookAndFeel.java:295)
    at javax.swing.UIDefaults.getFromHashtable(UIDefaults.java:203)
    at javax.swing.UIDefaults.get(UIDefaults.java:148)
    at javax.swing.MultiUIDefaults.get(MultiUIDefaults.java:65)
    at javax.swing.UIDefaults.getIcon(UIDefaults.java:429)
    at javax.swing.UIManager.getIcon(UIManager.java:562)
    at javax.swing.plaf.basic.BasicOptionPaneUI.getIconForType(BasicOptionPaneUI.java:600)
    at javax.swing.plaf.basic.BasicOptionPaneUI.getIcon(BasicOptionPaneUI.java:586)
    at javax.swing.plaf.basic.BasicOptionPaneUI.createMessageArea(BasicOptionPaneUI.java:337)
    at javax.swing.plaf.basic.BasicOptionPaneUI.installComponents(BasicOptionPaneUI.java:178)
    at javax.swing.plaf.basic.BasicOptionPaneUI.installUI(BasicOptionPaneUI.java:146)
    at javax.swing.JComponent.setUI(JComponent.java:475)
    at javax.swing.JOptionPane.setUI(JOptionPane.java:1725)
    at javax.swing.JOptionPane.updateUI(JOptionPane.java:1747)
    at javax.swing.JOptionPane.<init>(JOptionPane.java:1710)
    at javax.swing.JOptionPane.showOptionDialog(JOptionPane.java:832)
    at javax.swing.JOptionPane.showMessageDialog(JOptionPane.java:646)
    at javax.swing.JOptionPane.showMessageDialog(JOptionPane.java:617)
    at JTest.main(JTest.java:40)
    Source Code:
    import javax.swing.*;
    import java.awt.*;
    public class JTest extends JFrame{
    JPanel pnlMain = new JPanel();
    JLabel lblMsg=new JLabel("This is only a test.");
    Font font=new Font("Arial", Font.BOLD, 28);
    public JTest(){
    try{
    this.setTitle("Unix Frame Testing");
    this.setBounds(10,10,500,100);
    this.setVisible(true);
    this.setDefaultCloseOperation(this.EXIT_ON_CLOSE);
    lblMsg.setFont(font);
    lblMsg.setBounds(100,10,250,50);                    
    pnlMain.setLayout(null);
    pnlMain.add(lblMsg);
    this.setContentPane(pnlMain);
    }catch(Exception e)
    { System.out.println("Unable to Display Window.");
    JOptionPane.showMessageDialog(null,"Unable to Display Window.","Error",JOptionPane.INFORMATION_MESSAGE);
    System.exit(0);
    public static void main(String args[]){
    JTest test=new JTest();
    }

    "scripts" are entirely different from GUI applications. What do you expect to happen when you run a Swing application through telnet on another machine? Do you expect the Swing UI to be magically transported to the local Windows machine?
    Not gonna happen. Yes, like ejp hinted, you could run an X-environment on your local machine and have the Swing UI tunnel its output there, but are you sure you want GUI apps to run on a remote machine like that? It's not gonna be fun to work with, I'll tell you that.
    Why not create a Java WebStart app (or perhaps even an applet) out of your application, so your users can run the application locally?

  • Java swing app distribution & setup

    We developed a java swing standalone app. How you guys distribute this kind app to end users if they know little about app setup(like data entry clerks).
    We like to generate an executable file bundling the app and JRE for our clients. They just run this executable to detect if there is no JRE on the local machine then install it, and then install the app and setup all environment variables. Is there any tool to do this?
    Thanks.

    If all you need to do is package the JRE with the app and install, then an installation tool like ZeroG InstallAnywhere would be a very good solution.
    If all you need to package, install and automatically deploy updates, you can probably cobble together an installer that uses Java Web Start. Note that in addition to the JRE, you will also need ensure that Web Start is installed.
    If you need to package, install, update, rollback, report, monitor, receive error alerts, and otherwise manage the application, you probably need DeployDirector, in which case, I encourage you to evaluate DeployDirector at http://www.sitraka.com/software/deploydirector/
    And yes, both InstallAnywhere and DeployDirector will allow you to deploy and manage a client-side app that communicates with a database and reads and writes from files on the client -- neither of these tools enforce the sandbox. Java Web Start will require you to either sign the application, or or use the JNLP API.
    Sonal Champsee
    [email protected]
    DeployDirector Product Manager
    Sitraka (now part of Quest Software)

  • Java Swing App hangs on socket loop

    H}ello everyone i've run into a bit of a snag with my application, its a simple chat bot right now but will evolve into a chat client soon. I can't do that however since my app hangs when i look through the readLine() function to receive data and i dont know how to fix this error. The code is below.
    The doSocket() function is where the while loop is if anyone can help me with this i'd be very greatful.
    * GuiFrame.java
    * Created on August 13, 2008, 9:36 PM
    import java.net.*;
    import java.io.*;
    import java.util.logging.Level;
    import java.util.logging.Logger;
    * @author  brian
    public class GuiFrame extends javax.swing.JFrame {
        /** Creates new form GuiFrame */
        public GuiFrame() {
            initComponents();
        Socket client_sock = null;
        PrintWriter out = null;
        BufferedReader in = null;
        @SuppressWarnings("unchecked")
        // <editor-fold defaultstate="collapsed" desc="Generated Code">                         
        private void initComponents() {
            convertButton = new javax.swing.JButton();
            setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
            setTitle("Test Bot");
            convertButton.setText("Connect");
            convertButton.addActionListener(new java.awt.event.ActionListener() {
                public void actionPerformed(java.awt.event.ActionEvent evt) {
                    convertButtonActionPerformed(evt);
            javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
            getContentPane().setLayout(layout);
            layout.setHorizontalGroup(
                layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                .addGroup(layout.createSequentialGroup()
                    .addGap(41, 41, 41)
                    .addComponent(convertButton)
                    .addContainerGap(42, Short.MAX_VALUE))
            layout.setVerticalGroup(
                layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                .addGroup(layout.createSequentialGroup()
                    .addGap(22, 22, 22)
                    .addComponent(convertButton)
                    .addContainerGap(26, Short.MAX_VALUE))
            pack();
        }// </editor-fold>                       
    private void convertButtonActionPerformed(java.awt.event.ActionEvent evt) {                                             
        if (convertButton.getText()=="Connect") {
                    String old;
                    try {
                        client_sock = new Socket("www.spinchat.com", 3001);
                        out = new PrintWriter(client_sock.getOutputStream(), true);
                        in = new BufferedReader(new InputStreamReader(client_sock.getInputStream()));
                    } catch (UnknownHostException e) {
                    } catch (IOException e) {
                try {
                    doSocket();
                } catch (IOException ex) {
                    Logger.getLogger(GuiFrame.class.getName()).log(Level.SEVERE, null, ex);
        } else if (convertButton.getText()=="Disconnect") {
            out.println("e");
            out.close();
            try {
                client_sock.close();
                in.close();
            } catch (IOException ex) {
                Logger.getLogger(GuiFrame.class.getName()).log(Level.SEVERE, null, ex);
    private void doSocket() throws IOException {
        BufferedReader stdIn = new BufferedReader(new InputStreamReader(System.in));
        String userInput;
        String old;
            while ((userInput = stdIn.readLine()) != null) {
                old = userInput;
                if (userInput != old) {
                    String proto = userInput.substring(0, 0);
                    if (proto == ":") {
                        out.println("{2");
                        out.println("BI'm a bot.");
                        out.println("aNICKHERE");
                        out.println("bPASSHERE");
                    } else if (proto == "a") {
                        convertButton.setText("Disconnect");
                        out.println("JoinCHannel");
        * @param args the command line arguments
        public static void main(String args[]) {
            java.awt.EventQueue.invokeLater(new Runnable() {
                public void run() {
                    new GuiFrame().setVisible(true);
        // Variables declaration - do not modify                    
        private javax.swing.JButton convertButton;
        // End of variables declaration                  
    }Edited by: briansykes on Aug 13, 2008 9:55 PM
    Edited by: briansykes on Aug 13, 2008 9:56 PM

    >
    ..i've run into a bit of a snag with my application, its a simple chat bot right now but will evolve into a chat client soon. >Is this intended as a GUId app? If so, I would stick to using a GUI for input, rather than reading from the command line (which when I run it, is the DOS window that jumps up in the BG - quite counterintuitive). On the other hand, if it is intended as a non-GUId or headless app., it might be better to avoid any use of JComponents at all.
    My edits stick to using a GUI.
    Other notes:
    - String comparison should be done as
    s1.equals(s2)..rather than..
    s1 == s2- Never [swallow exceptions|http://pscode.org/javafaq.html#stacktrace] in code that does not work.
    - Some basic debugging skills are well called for here, to find where the program is getting stuck. When in doubt, print out!
    - What made you think that
    a) a test of equality on a member against which you had just set the value to the comparator, would logically lead to 'false'?
    old = userInput;
    if (userInput != old) ..b) A substring from indices '0 thru 0' would provide 1 character?
    String proto = userInput.substring(0, 0);
    if (proto == ":") ..
    >
    ..if anyone can help me with this i'd be very greatful.>Gratitude is often best expressed through the offering of [Duke Stars|http://wikis.sun.com/display/SunForums/Duke+Stars+Program+Overview].
    * GuiFrame.java
    * Created on August 13, 2008, 9:36 PM
    import java.net.*;
    import java.io.*;
    import java.util.logging.Level;
    import java.util.logging.Logger;
    import javax.swing.*;
    * @author  brian
    public class GuiFrame extends JFrame {
        /** Creates new form GuiFrame */
        public GuiFrame() {
            initComponents();
        Socket client_sock = null;
        PrintWriter out = null;
        BufferedReader in = null;
        @SuppressWarnings("unchecked")
        // <editor-fold defaultstate="collapsed" desc="Generated Code">
        private void initComponents() {
            convertButton = new JButton();
            setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
            setTitle("Test Bot");
            convertButton.setText("Connect");
            convertButton.addActionListener(new java.awt.event.ActionListener() {
                public void actionPerformed(java.awt.event.ActionEvent evt) {
                    convertButtonActionPerformed(evt);
            GroupLayout layout = new GroupLayout(getContentPane());
            getContentPane().setLayout(layout);
            layout.setHorizontalGroup(
                layout.createParallelGroup(GroupLayout.Alignment.LEADING)
                .addGroup(layout.createSequentialGroup()
                    .addGap(41, 41, 41)
                    .addComponent(convertButton)
                    .addContainerGap(42, Short.MAX_VALUE))
            layout.setVerticalGroup(
                layout.createParallelGroup(GroupLayout.Alignment.LEADING)
                .addGroup(layout.createSequentialGroup()
                    .addGap(22, 22, 22)
                    .addComponent(convertButton)
                    .addContainerGap(26, Short.MAX_VALUE))
            pack();
            setLocationByPlatform(true);
        }// </editor-fold>
    private void convertButtonActionPerformed(java.awt.event.ActionEvent evt) {
        if (convertButton.getText()=="Connect") {
                    String old;
                    try {
                        System.out.println( "Connect start.." );
                        client_sock = new Socket("www.spinchat.com", 3001);
                        out = new PrintWriter(client_sock.getOutputStream(), true);
                        in = new BufferedReader(new InputStreamReader(client_sock.getInputStream()));
                        System.out.println( "Connect end.." );
                    } catch (UnknownHostException e) {
                        e.printStackTrace();
                    } catch (IOException e) {
                        e.printStackTrace();
                try {
                    doSocket();
                } catch (IOException ex) {
                    Logger.getLogger(GuiFrame.class.getName()).log(Level.SEVERE, null, ex);
        } else if (convertButton.getText()=="Disconnect") {
            out.println("e");
            out.close();
            try {
                client_sock.close();
                in.close();
            } catch (IOException ex) {
                Logger.getLogger(GuiFrame.class.getName()).log(Level.SEVERE, null, ex);
        private void doSocket() throws IOException {
            System.out.println( "doSocket start.." );
            String userInput;
            String old;
            userInput = JOptionPane.showInputDialog(this, "Send..");
            while(userInput!=null && userInput.trim().length()>0) {
                System.out.println( "doSocket loop 1.." );
                String proto = userInput.substring(0, 1);
                System.out.println("proto: '" + proto + "'");
                if (proto.equals(":")) {
                    System.out.println("Sending data..");
                    out.println("{2");
                    out.println("BI'm a bot.");
                    out.println("aNICKHERE");
                    out.println("bPASSHERE");
                } else if (proto.equals("a")) {
                    convertButton.setText("Disconnect");
                    out.println("JoinCHannel");
                userInput = JOptionPane.showInputDialog(this, "Send..");
            System.out.println( "doSocket end.." );
        * @param args the command line arguments
        public static void main(String args[]) {
            java.awt.EventQueue.invokeLater(new Runnable() {
                public void run() {
                    new GuiFrame().setVisible(true);
        // Variables declaration - do not modify
        private JButton convertButton;
        // End of variables declaration
    }

  • I need the action for menu in java swing

    hai friends
    have a peace day.
    i created a menu using swing, but i don't know how to give the action.
    eg: i created menu called FILE if i click this menu its goes to specify program
    please give any idea or sample code.
    thank u
    regards
    rex

    Hi,
    u have to implement ActionListener...when u r pressing menu item the events
    will come inside the ActionPerformed function.
    for example
    public class Menuexample implements ActionListener
    public Menuexample()
    filemenuiem=new MenuItem("File");
    filemenuiem.addActionListener(this);
    public void actionPerformed(ActionEvent ae) {
    if(ae.getSource==filemenuitem)
    regards
    pradeep

  • Launching Swing apps in foreground remotely

    We have an agent that runs on our machines waiting to start and stop java applications, but there is a problem when the application has a GUI element to it. We are using the RunTime classes and it executes applications in the background which means that GUIs cannot be seen even though they are running.
    What is the secret to automatically launching java swing apps in the foreground?
    Thanks in advance.

    Replying to myself, as I think I've just found the answer (at least for an windows environment)
    --> Just start your app with "javaw" instead of "java"
    (What would be the right way to do it in Linux, for instance?)
    Joao Clemente

  • In program written with Java Swing, I can't input Chinese

    In program written with Java Swing, I can't input Chinese.
    But if I change my language first, then change the input method tu U.S, open the Java Swing application, finally I can input Chinese. I want to know how to fix this bug.
    My OS is Mac OS X 10.6.8.
    At the JDK version 1.6.0_29, I can input Chinese friendly in Java Swing applications. But after 1.6.0_31, I can't do it anymore. The input methods can input Chinese in other non Java Swing applications so the problem must create by JDK or JRE's Swing part. What's the different between 1.6.0_29's Swing and 1.6.0_31's ? Why ? I heard that Java Swing apps not support Chinese input methods seens 2009... Why haven't fix these yet?

    Chazza wrote:
    Perhaps you need to change your keyboard layout in Xorg?
    https://wiki.archlinux.org/index.php/Ke … ard_layout
    Thanks for your answer!
    I have tried to change the keyboard layout from "en" to "cn", but it is still not work.
    The input method coin on the righttop is right when I change the method.But it still output english even I use ibus-pinyin.There is not a box for my choosing chinese words.
    Last edited by Dilingg (2015-05-15 16:18:43)

  • Launching report from Swing app

    How do you launch a report froma Java Swing app and how does the report get viewed and then saved?
    Thanks.

    hello,
    there are several ways of running a report.
    a) by executing rwrun
    b) by submitting an HTTP request to the reports server
    c) by executing rwcli to submit a request to the reports server
    the output has to be viewed using either a browser window.
    regards,
    the oracle reports team --pw                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       

  • Performance issue Java Swing under Windows 7

    Hello,
    we have MDI Java Swing application running under Window7. We got a big problem with performance in MDI Windows using AERO.
    Exist any way for Java to tell Win7 that it should not use Aero??
    Thank you,
    David.
    Edited by: 969767 on 6.11.2012 3:33

    If you try to open properties dialog for application (or app shortcut) there you can see "Compatibility" tab and there is check box "Disable visual themes". If we disable this theme our java swing app is very better performance under Windows 7. We need to disable visual theme via java code, if it is possible...
    Thanks.

  • Java SE 6 update 1 swing apps run slow

    When I first installed JDK6/JRE SE 6, Netbeans 5.5 and 6 (M9) ran beautifully. All GUI Java applications were quite responsive, and I never noticed a speed issue. However, last week Netbeans started running slowly. You could practically see the entire screen being repainted from top to bottom over the course of a second or two. Naturally, this gets quite annoying when attempting to write code since everytime I scroll, I have to wait a second for the screen to repaint itself. At first I thought this was just a Netbeans issue, but I notice that every other Java GUI app also repaints very slowly. I've been trying to think back as to what could possibly have changed in my system to cause things to run slowly, but can't think of anything.
    I tried reinstalling the JDK and JRE (as well as Netbeans), but it does not seem to have fixed the issue. The reason I'm posting this under the Swing forum is because I suspect this is a Swing issue -- applications that I have written that do not utilize a GUI seem to run at the same speed as before.
    Any suggestions as to what might be causing this slowness?

    could it freezing be related to my delay in updating it ?
    Message was edited by: Sheepo39

  • Java Swing frame for modification Excel file or Word file with All menu...

    Hello All,
    Can Any one help me for making java Swing frame for modification Excel Data or word file with all Menu.. Plz send me java Code for that.. I am bit new in Swing.
    i am waiting for ur help..
    Thanks
    Samir

    hi pbrockway2 ,
    Can you go through this program Sir, i am trying to call Excel content below of menu. when i will press Edit button then excel content should come below with Cut, copy, paste , Save Button..
    Plz help me sir...
    import javax.swing.*;
    import java.io.*;
    import java.awt.event.*;
    public class TestReader
    private static void createAndShowUI()
    JFrame.setDefaultLookAndFeelDecorated(true);
    JFrame frame=new JFrame("Test Reader");
    JButton button=new JButton("Edit");
    button.addActionListener(new ButtonListener());
    frame.getContentPane().add(button);
    frame.setVisible(true);
    frame.pack();
    static class ButtonListener implements ActionListener
    public void actionPerformed(ActionEvent event)
    openTheFile();
    private static void openTheFile()
    try
    String commands[]=new String[3];
    commands[0]="cmd.exe";
    commands[1]="/C";
    commands[2]="INSTALL.LOG"; // here file name is supposed to be in the working dir
    Runtime rt=Runtime.getRuntime();
    Process proc=rt.exec(commands);
    StreamGobbler errorGobbler=new StreamGobbler(proc.getErrorStream(),"ERROR");
    StreamGobbler outputGobbler=new StreamGobbler(proc.getInputStream(),"OUTPUT");
    errorGobbler.start();
    outputGobbler.start();
    catch (Exception e){}
    public static void main(String args[])
    SwingUtilities.invokeLater(new Runnable()
         public void run()
         createAndShowUI();
    static class StreamGobbler extends Thread
    InputStream is;
    String type,root;
    StreamGobbler(InputStream is,String type)
    this.is=is;
    this.type=type;
    public void run()
    try
    InputStreamReader isr=new InputStreamReader(is);
         BufferedReader breader=new BufferedReader(isr);
         String line=null;
         while ((line=breader.readLine())!=null)
         System.out.println(type+">"+line);
    catch (Exception e)
         System.out.println(e);
    Thanks
    SamiR

  • Apply MVC to a Swing app?

    Folks,
    I got inspired by this thread: http://forum.java.sun.com/thread.jspa?threadID=5244847&start=23&tstart=0
    and wrote the below version of MineSweeper (winmine.exe) in Swing as a learning exercise.
    I'm about half way through my TODO list, and the code is "getting ugly". For instance, It took me a couple of hours to figure out what needed to change in order to keep track of the remaining bombs.
    This leads me to believe that the best way forward might be to declare this version to be "The Prototype" and go back to torres.... taking what I've learned about the problems involved, and designing the whole thing from ground up, applying the MVC paradigm.
    But I'm really unsure of how go about doing that... I studied program design back in the heyday of pseudo-code and structure charts, and I was hoping for some free advise (pretty please) or maybe some practical exercises/tutorials on OO program design. I've read some theory, but to be honest, it just goes over my head... in one ear and out the other. I hope that exercise of applying the principles espoused in the theory to a concrete example will help me to bring the theory into focus... at the moment GOF is rattling around in my brain, but it refuses coalesce into a coherent picture.
    I have a rough design in mind... Please would anyone care to comment?
    The Model
    * A Field has Mine's
    * Field would contain a matrix of Mine's ... mines[col][row]
    * Mine would encapsulate the state of each mine, and expose the a low level set of mutators.
    The View
    * Form extends JFrame - the main form
    * Top JPanel - much like the existing one
    * Bottom JPanel
    .... I'm wondering if a JTable would work in place of the existing matrix of JPanels.
    .... or can this be done with a LayoutManager... I'm thinking GridBagLayout?
    * Can I use a JPanel instead of the existing JPanel contains a JButton?
    .... * I've got an issue with the existing MouseListener implementation. You have to hold
    .... the mouse still and click each button rather deliberately. This ruins the fluidity of
    .... the game-play, substantially reducing the players enjoyment. ie: it gives me the sh1ts.
    .... Please does anyone have any ideas? I've used enough swing/awt apps to know
    .... that this isn't an insoluble problem, but I've got no idea how to address it.
    The Controler
    * A Game class orchestrates the whole thing at the high level.
    ... + main - Creates a new Game and calls it's play method.
    ... - play - Creates and shows the gui on the EDT
    * The MineLayer lays Mine's for the MineSweeper to clean up.
    .... But I'm a but lost as to what should "own" the MineLayer and the MineSweeper.
    .... To me it makes sense for the Controler to kick of the View, but from then on
    .... we're event-driven by the View... so should the View "create and own" the
    .... MineLayer and the MineSweeper? or should they be created by the controler
    .... and passed into the constructor of the main form? This OO stuff always leaves me
    .... feeling a bit thick. Back in my day you just called functions. It was ugly and simple.
    .... OO is ugly and complicated.
    package krc.games;
    import java.util.Random;
    import javax.swing.JFrame;
    import javax.swing.JPanel;
    import javax.swing.JLabel;
    import javax.swing.JButton;
    import javax.swing.JOptionPane;
    import java.awt.EventQueue;
    import java.awt.Container;
    import java.awt.Color;
    import java.awt.Insets;
    import java.awt.Font;
    import java.awt.BorderLayout;
    import java.awt.image.ColorModel;
    import java.awt.event.MouseListener;
    import java.awt.event.MouseAdapter;
    import java.awt.event.MouseEvent;
    import java.awt.event.ActionListener;
    import java.awt.event.ActionEvent;
    import javax.swing.ImageIcon;
    import javax.swing.border.LineBorder;
    import javax.imageio.ImageIO;
    import java.awt.image.BufferedImage;
    import java.awt.Graphics;
    import java.awt.Graphics2D;
    import java.io.File;
    import java.io.IOException;
    import java.util.HashMap;
    import java.util.Map;
    // TODO:
    // * implement "you win"... track remaining bombs.                DONE
    // * show "not a bomb" at end of game.                            DONE
    // * display remaining bombs.                                     DONE
    // * display a clock.                                             DONE
    // * Resign/Refactor along MVC lines.
    //   M = A Field has Mine's
    //   V = The JFrame, some JPanels, a Grid?
    //   C = Game orchestrates. The MineLayer lays Mine's for the MineSweeper to clean up.
    // * beginner / intermediate / advanced / custom settings.
    //   - Find out how to use menu's in swing
    // * config board from command line args and/or properties file.
    //   - Use an XML Properties file.
    // * restart the same game. Reset the board with existing bombs. 
    //   - TOO EASY - remember the seed used passed to Random.
    // * high score.
    //   - CAPTURE: JOptionPane("Enter your name")
    //   - DISPLAY: A form with a grid
    //   - STORE  : Java only database
    // * save and load a game.
    //   - A de/serialization exercise.
    // * cheats - IDDKFA ;-)
    //   - keyboard listener to show bombs.
    import java.awt.image.BufferedImage;
    public class MineSweeperGame extends JFrame
      private static final long serialVersionUID = 0L;
      private static final int ROWS = 15;
      private static final int COLS = 30;
      private static final int BOMBS = (int)(ROWS * COLS / 4.5);
      private JLabel bombs = null; //count down the bombs
      private int bombsRemaining = BOMBS;
      private JLabel clock = null; //seconds since first click
      private int elapsedTime;
      private volatile boolean isClockRunning = false;
      private static final String DIR = "C:/Java/home/src/krc/games/";
      private static final BufferedImage FLAG_IMAGE     = loadImage(DIR+"/flag.jpg");
      private static final BufferedImage QUESTION_IMAGE = loadImage(DIR+"/question.jpg");
      private static final BufferedImage BOMB_IMAGE     = loadImage(DIR+"/bomb.jpg");
      private static final BufferedImage EXPLODED_IMAGE = loadImage(DIR+"/exploded_bomb.jpg");
      private static final BufferedImage NOT_BOMB_IMAGE = loadImage(DIR+"/not_bomb.jpg");
      // number colors
      private static final Color[] COLORS = {
        Color.black         // 0 not used
      , Color.blue          // 1 blue
      , new Color(0,153,153)// 4 darkGreen
      , Color.red           // 3 red
      , new Color(0,0,204)  // 4 darkBlue
      , new Color(153,0,0)  // 5 brown
      , Color.pink          // 6 pink
      , Color.orange        // 7 orange
      , Color.white         // 8 white
      , Color.magenta       // 9 magenta
      private static final Font FONT = new Font("Dialog", Font.BOLD, 12);
      private MouseListener mouseListener = new MouseAdapter() {
        public void mouseClicked(MouseEvent event) {
          if(!isClockRunning) startClock();
          Cell cell = ((Cell.Button)event.getSource()).getParentCell();
          if ( event.getButton() == MouseEvent.BUTTON1 ) {
            if (cell.isBomb()) {
              if ( cell.isMarkedAsBomb() ) {
                java.awt.Toolkit.getDefaultToolkit().beep();
              } else {
                cell.explode();
                youLost();
            } else {
              revealCell(cell);
          } else if ( event.getButton() == MouseEvent.BUTTON3 ) {
            cell.nextState();
      private class Cell extends JPanel {
        private static final long serialVersionUID = 0L;
        public static final int WIDTH = 17;
        public static final int HEIGHT = 17;
        public static final float Y_OFFSET = 13F;
        public static final float X_OFFSET = 5F;
        private class Button extends JButton {
          private static final long serialVersionUID = 0L;
          private final Cell parentCell;
          public Button(int width, int height, Cell parentCell) {
            this.parentCell = parentCell;
            this.setBounds(0, 0, width, height);
            this.setMargin(new Insets(1,1,1,1));
            this.setFont(new Font("Dialog",0,8));
            this.addMouseListener(mouseListener); // handle right button
          public void reset() {
            this.setText("");
            this.setVisible(true);
            this.setIcon(null);
          public Cell getParentCell() {
            return this.parentCell;
        private final Button button;
        private final int row, col;
        private boolean isBomb = false;
        private boolean isRevealed = false;
        private boolean isExploded = false;
        private int neighbours = 0;
        private ImageIcon[] stateIcons = new ImageIcon[] {
          null,  new ImageIcon(FLAG_IMAGE), new ImageIcon(QUESTION_IMAGE)
        private int state = 0;
        public static final int STATE_UNMARKED = 0;
        public static final int STATE_MARKED_AS_A_BOMB = 1;
        public static final int STATE_MARKED_AS_A_QUESTION = 2;
        Cell(int row, int col) {
          this.row = row;
          this.col = col;
          this.setBounds(col*WIDTH, row*HEIGHT, WIDTH, HEIGHT);
          this.setLayout(null);
          button = new Button(WIDTH, HEIGHT, this);
          this.add(button);
        public boolean isBomb() { return this.isBomb; }
        public void setBomb(boolean isBomb) { this.isBomb = isBomb; }
        public boolean isRevealed() { return this.isRevealed; }
        public void reveal() {
          if(this.isRevealed) return;
          this.button.setVisible(false);
          this.isRevealed = true;
        public void revealNotABomb() {
          button.setText("");
          button.setIcon(new ImageIcon(NOT_BOMB_IMAGE));
        public boolean isExploded() { return this.isExploded; }
        public void explode() {this.isExploded = true;}
        public int getNeighbours() { return this.neighbours; }
        public void setNeighbours(int neighbours) {this.neighbours = neighbours;}
        public void reset() {
          this.isRevealed = false;
          this.isBomb = false;
          this.isExploded = false;
          this.state = STATE_UNMARKED;
          this.button.reset();
        public boolean isMarkedAsBomb() {
          return this.state == Cell.STATE_MARKED_AS_A_BOMB;
        public void nextState() {
          boolean wasMarkedAsABomb = this.isMarkedAsBomb();
          this.state = (this.state+1) % 3;
          button.setIcon(stateIcons[this.state]);
          if (this.isMarkedAsBomb()) {
            setBombsRemaining(getBombsRemaining()-1);
            if (getBombsRemaining() == 0) {
              if (allMarkedBombsAreReallyBombs()) {
                youWon();
              } else {
                youLost();
          } else if (wasMarkedAsABomb) {
            setBombsRemaining(getBombsRemaining()+1);
        @Override
        public void paintComponent(Graphics g) {
          super.paintComponent(g);
          if ( this.isRevealed() ) {
            Graphics2D g2d = (Graphics2D) g;
            if (this.isExploded) {
              g2d.drawImage(EXPLODED_IMAGE, 0, 0, null);
            } else if (this.isBomb) {
              g2d.drawImage(BOMB_IMAGE, 0, 0, null);
            } else if (this.neighbours > 0) {
              g2d.setColor(COLORS[this.neighbours]);
              g2d.setFont(FONT);
              g2d.drawString(""+this.neighbours, X_OFFSET, Y_OFFSET);
      private Cell[][] cells = new Cell[ROWS][COLS];
      MineSweeperGame() {
        super();
        buildAndShowGUI();
      private void buildAndShowGUI() {
        this.setTitle("Miner");
        final int CELLS_X = Cell.WIDTH*COLS;
        final int WASTE_X = 6;
        final int WIN_WIDTH = CELLS_X+WASTE_X;
        final int CELLS_Y = Cell.HEIGHT*ROWS;
        final int WASTE_Y = 32;
        final int TOP_FRAME_Y = 34;
        final int WIN_HEIGHT = CELLS_Y+WASTE_Y+TOP_FRAME_Y;
        this.setSize(WIN_WIDTH, WIN_HEIGHT);
        this.setDefaultCloseOperation(EXIT_ON_CLOSE);
        this.setResizable(false);
        Container pane = this.getContentPane();
        pane.setLayout(null);
        JPanel top = new JPanel();
        top.setBounds(0,0, WIN_WIDTH,TOP_FRAME_Y);
        top.setBorder(new LineBorder(Color.black));
        top.setLayout(null);
        bombs = new JLabel();
        bombs.setBounds(5,7, 23,17);
        bombs.setBorder(new LineBorder(Color.black));
        top.add(bombs);
        clock = new JLabel("0");
        clock.setBounds(WIN_WIDTH-35,7, 23,17);
        clock.setBorder(new LineBorder(Color.black));
        top.add(clock);
        pane.add(top);
        JPanel bot = new JPanel();
        bot.setLayout(null);
        bot.setBackground(Color.white);
        bot.setBounds(0, TOP_FRAME_Y+1, CELLS_X, CELLS_Y);
        for(int r=0; r<ROWS; r++) {
          for(int c=0; c<COLS; c++) {
            cells[r][c] = new Cell(r, c);
            bot.add(cells[r][c]);
        pane.add(bot);
        resetGame();
        this.setVisible(true);
      private void resetGame() {
        resetClock();
        resetCells();
        placeBombs();
        setBombsRemaining(BOMBS);
        countNeighbouringBombs();
      private void resetCells() {
        // reset all the cells
        for(int r=0; r<ROWS; r++) {
          for(int c=0; c<COLS; c++) {
            cells[r][c].reset();
      private void placeBombs() {
        // randomly place however many bombs in the minefield
        Random rand = new Random(System.currentTimeMillis());
        for(int i=0; i<BOMBS; i++) {
          while(true) {
            Cell cell = this.cells[rand.nextInt(ROWS)][rand.nextInt(COLS)];
            if (!cell.isBomb()) {
              cell.setBomb(true);
              cell.button.setText("b");
              break;
      // count the number of bombs neighbouring each cell
      private void countNeighbouringBombs() {
        for(int r=0; r<ROWS; r++) {
          for(int c=0; c<COLS; c++) {
            cells[r][c].setNeighbours(getNeighbourCount(r, c));
      // the number of bombs neighbouring the given cell
      private int getNeighbourCount(int row, int col) {
        int count = 0;
        int firstRow = Math.max(row-1, 0);
        int lastRow  = Math.min(row+1, ROWS-1);
        int firstCol = Math.max(col-1, 0);
        int lastCol  = Math.min(col+1, COLS-1);
        for(int r=firstRow; r<=lastRow; r++) {
          for(int c=firstCol; c<=lastCol; c++) {
            if( this.cells[r][c].isBomb ) count++;
        return count;
      public void setBombsRemaining(int bombsRemaining) {
        this.bombsRemaining = bombsRemaining;
        this.bombs.setText(""+this.bombsRemaining);
      public int getBombsRemaining() {
        return(this.bombsRemaining);
      private void showBombs() {
        for(int r=0; r<ROWS; r++) {
          for(int c=0; c<COLS; c++) {
            Cell cell = cells[r][c];
            if (cell.isBomb) {
              cell.reveal();
            } else if (cell.isMarkedAsBomb()) {
              cell.revealNotABomb();
      private boolean allMarkedBombsAreReallyBombs() {
        for(int r=0; r<ROWS; r++) {
          for(int c=0; c<COLS; c++) {
            Cell cell = this.cells[r][c];
            if (cell.isMarkedAsBomb() && !cell.isBomb() ) {
              return false;
        return true;
      private void revealCell(Cell cell) {
        if(cell.getNeighbours()==0) {
          revealCells(cell, new HashMap<Cell,Object>());
        } else {
          cell.reveal();
      private void revealCells(Cell cell, Map<Cell,Object> ancestors) {
        if (ancestors.containsKey(cell)) return;
        if (cell.isRevealed()) return;
        ancestors.put(cell, null);
        cell.reveal();
        if(cell.getNeighbours()==0) {
          int firstRow = Math.max(cell.row-1, 0);
          int lastRow  = Math.min(cell.row+1, ROWS-1);
          int firstCol = Math.max(cell.col-1, 0);
          int lastCol  = Math.min(cell.col+1, COLS-1);
          for(int r=firstRow; r<=lastRow; r++) {
            for(int c=firstCol; c<=lastCol; c++) {
              Cell x = cells[r][c];
              if (x == cell) continue;
              if( !x.isBomb() && !x.isRevealed() ) {
                revealCells(x, ancestors);
      private void youLost() {
        stopClock();
        showBombs();
        JOptionPane.showMessageDialog(this, "You Lost.", "Game Over", JOptionPane.WARNING_MESSAGE);
        resetGame();
      private void youWon() {
        stopClock();
        JOptionPane.showMessageDialog(this, "You Won. Congratulations.", "Game Over", JOptionPane.INFORMATION_MESSAGE);
        resetGame();
      private static BufferedImage loadImage(String filename) {
        try {
          return ImageIO.read(new File(filename));
        } catch (IOException e) {
          // the game will still kinda work, you just won't see this image.
          e.printStackTrace();
        return null;
      private void startClock() {
        isClockRunning = true;
        new Thread() {
          public void run() {
            while (isClockRunning) {
              clock.setText(""+(elapsedTime++));
              //repaint();
              try{Thread.sleep(1000);}catch(InterruptedException eaten){}
        }.start();
      private void stopClock() {
        this.isClockRunning = false;
      private void resetClock() {
        elapsedTime = 0;
        clock.setText("0");
      public static void main(String[] args) {
        try {
          EventQueue.invokeLater(
            new Runnable() {
              public void run() {
                MineSweeperGame game = new MineSweeperGame();
        } catch (Exception e) {
          e.printStackTrace();
    }Thanx in advance for any ideas.
    Cheers. Keith.
    Edited by: corlettk on Dec 23, 2007 6:15 AM - typos.

    For anyone who's interested... here's the latest (and complete, I think) version of the program.... there's quit a lot of code here, sorry. It should all compile first time... but you'll need to scrape your own set of images from minesweeper (ie: winmine.exe) and google for the wav files... It's a shame we can't post jar's or zip's here.
    MineSweeperGame.java
    package krc.games.sweeper;
    import java.util.Random;
    import javax.swing.JFrame;
    import javax.swing.JPanel;
    import javax.swing.JLabel;
    import javax.swing.JButton;
    import javax.swing.JOptionPane;
    import javax.swing.JMenuBar;
    import javax.swing.JMenu;
    import javax.swing.JMenuItem;
    import javax.swing.JRadioButtonMenuItem;
    import javax.swing.ButtonGroup;
    import java.awt.Color;
    import java.awt.Insets;
    import java.awt.Font;
    import java.awt.event.MouseListener;
    import java.awt.event.MouseAdapter;
    import java.awt.event.MouseEvent;
    import java.awt.event.ActionListener;
    import java.awt.event.ActionEvent;
    import javax.swing.ImageIcon;
    import javax.swing.border.LineBorder;
    import java.io.File;
    import javax.imageio.ImageIO;
    import java.awt.image.BufferedImage;
    import java.awt.Graphics;
    import java.awt.Graphics2D;
    import java.util.HashMap;
    import java.util.Map;
    import krc.utilz.sound.WavePlayer;
    // TODO:
    // * implement "you win"... track remaining bombs.                    DONE
    // * show "not a bomb" at end of game.                                DONE
    // * display remaining bombs.                                         DONE
    // * display a clock.                                                 DONE
    // * Resign/Refactor along MVC lines.                                 NOT DONE
    //   M = A Field has Mine's
    //   V = The JFrame, some JPanels, a Grid?
    //   C = Controler orcestrates.
    // * beginner / intermediate / advanced / custom settings.            DONE
    //   - Find out how to use menu's in swing
    // * config board from command line args and/or properties file.      DONE
    //   - used a standard properties file
    // * restart the same game. Reset the board with existing bombs.      DONE
    //   - TOO EASY - remember the seed used passed to Random.
    // * high score.
    //   - CAPTURE: JOptionPane("Enter your name")
    //   - DISPLAY: A form with a grid
    //   - STORE  : Java only database
    // * save and load a game.
    //   - A de/serialization exercise.
    // * cheats
    //   until the first 0 is hit
    //   - ctrl-click - is bomb proof (costs 5 seconds)                   DONE
    //   - fifth click reveals the biggest patch of 0's                   DONE
    //   god mode
    //   - ctrl-alt-shift shows the position of all bombs.                DONE
    //     // search for "b" - uncomment that line)
    public class MineSweeperGame extends JFrame
      private static final long serialVersionUID = 0L;
      //these are read from properties files
      private GameSettings settings;
      private JFrame theFrame;
      private JLabel bombs = null; //count down the bombs
      private int bombsRemaining;
      private boolean replayCurrentGame = false;
      private long randomSeed = 0;
      private GameClock clock = null; //seconds since first click
      private static final String DIR = "C:/Java/home/src/krc/games/sweeper/";
      private static final String IMGDIR = DIR+"images/";
      private static final BufferedImage FLAG_IMAGE     = loadImage(IMGDIR+"flag.jpg");
      private static final BufferedImage QUESTION_IMAGE = loadImage(IMGDIR+"question.jpg");
      private static final BufferedImage BOMB_IMAGE     = loadImage(IMGDIR+"bomb.jpg");
      private static final BufferedImage EXPLODED_IMAGE = loadImage(IMGDIR+"exploded_bomb.jpg");
      private static final BufferedImage NOT_BOMB_IMAGE = loadImage(IMGDIR+"not_bomb.jpg");
      private static final String WAVDIR = DIR+"sounds/";
      // number colors
      private static final Color[] COLORS = {
        Color.black         // 0 not used
      , Color.blue          // 1 blue
      , new Color(0,153,153)// 4 darkGreen
      , Color.red           // 3 red
      , new Color(0,0,204)  // 4 darkBlue
      , new Color(153,0,0)  // 5 brown
      , Color.pink          // 6 pink
      , Color.orange        // 7 orange
      , Color.white         // 8 white
      , Color.magenta       // 9 magenta
      private static final Font FONT = new Font("Dialog", Font.BOLD, 12);
      private class GameMenuBar extends JMenuBar
        private static final long serialVersionUID = 0L;
        private ActionListener menuListener = new ActionListener() {
          public void actionPerformed(ActionEvent event) {
            String action = event.getActionCommand().toLowerCase();
            if ("new".equals(action)) {
              hideAndDestroyGUI();
              buildAndShowGUI();
            } else if ("restart".equals(action)) {
              hideAndDestroyGUI();
              replayCurrentGame = true;
              buildAndShowGUI();
            } else if ( "beginner,intermediate,expert,custom".indexOf(action) > -1 ) {
              hideAndDestroyGUI();
              if ( "custom".equals(action) ) {
                CustomSettingsDialog dialog = new CustomSettingsDialog(theFrame, properties.get("custom"));
                settings = dialog.getSettings();
                dialog.dispose();
                properties.set(settings);
              } else {
                settings = properties.get(action);
              buildAndShowGUI();
            } else if ("exit".equals(action)) {
              setVisible(false);
              dispose();
            } else {
              java.awt.Toolkit.getDefaultToolkit().beep();
              System.out.println("Menu item ["+event.getActionCommand()+"] was pressed.");
        public GameMenuBar(String level) {
          JMenu menu = new JMenu("Game");
          menu.add(newJMenuItem("New", 'N'));
          menu.add(newJMenuItem("Restart", 'R'));
          menu.addSeparator();
          ButtonGroup group = new ButtonGroup();
          menu.add(newJRadioButtonMenuItem(group, "Beginner", level));
          menu.add(newJRadioButtonMenuItem(group, "Intermediate", level));
          menu.add(newJRadioButtonMenuItem(group, "Expert", level));
          menu.add(newJRadioButtonMenuItem(group, "Custom", level));
          menu.addSeparator();
          menu.add(newJMenuItem("Exit", 'X'));
          this.add(menu);
        private JMenuItem newJMenuItem(String text, char mneumonic) {
          JMenuItem item = new JMenuItem(text, mneumonic);
          item.addActionListener(this.menuListener);
          return item;
        private JRadioButtonMenuItem newJRadioButtonMenuItem(ButtonGroup group, String text, String level) {
          JRadioButtonMenuItem item = new JRadioButtonMenuItem(text);
          item.addActionListener(this.menuListener);
          group.add(item);
          if(text.equalsIgnoreCase(level)) item.setSelected(true);
          return item;
      private class Cell extends JPanel
        private static final long serialVersionUID = 0L;
        public static final int WIDTH = 17;
        public static final int HEIGHT = 17;
        public static final float Y_OFFSET = 13F;
        public static final float X_OFFSET = 5F;
        private class Button extends JButton {
          private static final long serialVersionUID = 0L;
          private final Cell parentCell;
          public Button(int width, int height, Cell parentCell) {
            this.parentCell = parentCell;
            this.setBounds(0, 0, width, height);
            this.setMargin(new Insets(1,1,1,1));
            this.setFont(new Font("Dialog",0,8));
            this.addMouseListener(mouseListener); // handles left & right button
          public void reset() {
            this.setText("");
            this.setVisible(true);
            this.setIcon(null);
          public Cell getParentCell() {
            return this.parentCell;
          public void removeMouseListener() {
            this.removeMouseListener(mouseListener);
        private final Button button;
        private final int row, col;
        private boolean isBomb = false;
        private boolean isRevealed = false;
        private boolean isExploded = false;
        private int neighbours = 0;
        public static final int STATE_UNMARKED = 0;
        public static final int STATE_MARKED_AS_A_BOMB = 1;
        public static final int STATE_MARKED_AS_A_QUESTION = 2;
        private int state = 0;
        private ImageIcon[] stateIcons = new ImageIcon[] {
          null,  new ImageIcon(FLAG_IMAGE), new ImageIcon(QUESTION_IMAGE)
        Cell(int row, int col) {
          this.row = row;
          this.col = col;
          this.setBounds(col*WIDTH, row*HEIGHT, WIDTH, HEIGHT);
          this.setLayout(null);
          button = new Button(WIDTH, HEIGHT, this);
          this.add(button);
        public boolean isBomb() { return this.isBomb; }
        public void setBomb(boolean isBomb) { this.isBomb = isBomb; }
        public boolean isRevealed() { return this.isRevealed; }
        public void reveal() {
          if(this.isRevealed) return;
          this.button.setVisible(false);
          this.isRevealed = true;
        public void revealNotABomb() {
          button.setText("");
          button.setIcon(new ImageIcon(NOT_BOMB_IMAGE));
        public boolean isExploded() { return this.isExploded; }
        public void explode() {this.isExploded = true;}
        public int getNeighbours() { return this.neighbours; }
        public void setNeighbours(int neighbours) {this.neighbours = neighbours;}
        public void reset() {
          this.isRevealed = false;
          this.isBomb = false;
          this.isExploded = false;
          this.state = STATE_UNMARKED;
          this.button.reset();
        public boolean isMarkedAsBomb() {
          return this.state == Cell.STATE_MARKED_AS_A_BOMB;
        public void nextState() {
          boolean wasMarkedAsABomb = this.isMarkedAsBomb();
          this.state = (this.state+1) % 3;
          button.setIcon(stateIcons[this.state]);
          if (this.isMarkedAsBomb()) {
            setBombsRemaining(getBombsRemaining()-1);
            if (getBombsRemaining() == 0) {
              if (allMarkedBombsAreReallyBombs()) {
                youWon();
              } else {
                youLost();
          } else if (wasMarkedAsABomb) {
            setBombsRemaining(getBombsRemaining()+1);
        public void removeMouseListener() {
          button.removeMouseListener();
        @Override
        public void paintComponent(Graphics g) {
          super.paintComponent(g);
          if ( this.isRevealed() ) {
            Graphics2D g2 = (Graphics2D) g;
            if (this.isExploded) {
              g2.drawImage(EXPLODED_IMAGE, 0, 0, null);
            } else if (this.isBomb) {
              g2.drawImage(BOMB_IMAGE, 0, 0, null);
            } else if (this.neighbours > 0) {
              g2.setColor(COLORS[this.neighbours]);
              g2.setFont(FONT);
              g2.drawString(""+this.neighbours, X_OFFSET, Y_OFFSET);
      private MouseListener mouseListener = null;
      private GameProperties properties = null;
      private Cell[][] cells = null;
      // this game is a "virgin" until the user finds the first 0.
      private boolean isVirgin = false;
      // when the user clears fifth square of a virgin game the
      // largest patch of 0's is automagically cleared.
      private int numCleared = 0;
      MineSweeperGame() {
        theFrame = this;
        this.properties = new GameProperties();
        this.settings = this.properties.get();
        buildAndShowGUI();
      private void setMouseListener(){
        this.mouseListener = new MouseAdapter() {
          public void mouseClicked(MouseEvent event) {
            clock.start();
            Cell cell = ((Cell.Button)event.getSource()).getParentCell();
            if ( event.getButton() == MouseEvent.BUTTON1 ) {
              if(event.isControlDown()) clock.skip(5);
              if (cell.isBomb()) {
                if ( cell.isMarkedAsBomb() ) {
                  java.awt.Toolkit.getDefaultToolkit().beep();
                } else if ( event.isControlDown() ) {
                  WavePlayer.play(WAVDIR+"ricochet.wav");
                } else {
                  WavePlayer.play(WAVDIR+"grenade.wav");
                  cell.explode();
                  youLost();
              } else {
                revealCell(cell);
                if ( isVirgin && ++numCleared==5 ) {
                  WavePlayer.play(WAVDIR+"smokin.wav");
                  revealTheBiggestFreePatch();
            } else if ( event.getButton() == MouseEvent.BUTTON3 ) {
              cell.nextState();
      private void hideAndDestroyGUI() {
        this.setVisible(false);
        for(int r=0; r<settings.rows; r++) {
          for(int c=0; c<settings.cols; c++) {
            cells[r][c].removeMouseListener(); // old mouse listeners send it crazy.
            cells[r][c] = null;
        this.getContentPane().removeAll();
        this.dispose();
      private void buildAndShowGUI() {
        final int CELLS_X = Cell.WIDTH*settings.cols;
        final int WASTE_X = 9;
        final int WIN_WIDTH = CELLS_X+WASTE_X;
        final int CELLS_Y = Cell.HEIGHT*settings.rows;
        final int WASTE_Y = 59;
        final int TOP_FRAME_Y = 34;
        final int WIN_HEIGHT = CELLS_Y+WASTE_Y+TOP_FRAME_Y;
        this.setTitle("Miner");
        this.setSize(WIN_WIDTH, WIN_HEIGHT);
        this.setResizable(false);
        this.setDefaultCloseOperation(EXIT_ON_CLOSE);
        this.setJMenuBar( new GameMenuBar(this.settings.level) );
        this.getContentPane().setLayout(null);
        // the top panel
        JPanel top = new JPanel(null);
        top.setBounds(0,0, WIN_WIDTH-6,TOP_FRAME_Y);
        top.setBorder(new LineBorder(Color.black));
        // the remaining-bomb-counter (on the left)
        bombs = new JLabel();
        bombs.setBounds(5,7, 23,17);
        bombs.setBorder(new LineBorder(Color.black));
        top.add(bombs);
        // the time-counter (on the right)
        this.clock = new GameClock();
        clock.setBounds(WIN_WIDTH-35,7, 23,17);
        top.add(clock);
        this.getContentPane().add(top);
        // the bottom panel
        setMouseListener();
        JPanel bot = new JPanel();
        bot.setLayout(null);
        bot.setBackground(Color.white);
        bot.setBounds(0, TOP_FRAME_Y+1, CELLS_X, CELLS_Y);
        this.cells = new Cell[settings.rows][settings.cols];
        for(int r=0; r<settings.rows; r++) {
          for(int c=0; c<settings.cols; c++) {
            cells[r][c] = new Cell(r, c);
            bot.add(cells[r][c]);
        this.getContentPane().add(bot);
        resetGame();
        this.setVisible(true);
      private void resetGame() {
        clock.reset();
        resetCells();
        isVirgin = true;
        numCleared = 0;
        placeBombs();
        setBombsRemaining(settings.bombs);
        countNeighbouringBombs();
      // reset the state of all the cells to default "empty" values.
      private void resetCells() {
        for(int r=0; r<settings.rows; r++) {
          for(int c=0; c<settings.cols; c++) {
            cells[r][c].reset();
      // randomly place however many bombs in the minefield
      // if replay then reuses the same seed to reproduce the same mine layout.
      private void placeBombs() {
        if(!this.replayCurrentGame) this.randomSeed = System.currentTimeMillis();
        this.replayCurrentGame = false;
        Random rand = new Random(this.randomSeed);
        for(int i=0; i<settings.bombs; i++) {
          while(true) {
            Cell cell = this.cells[rand.nextInt(settings.rows)][rand.nextInt(settings.cols)];
            if (!cell.isBomb()) {
              cell.setBomb(true);
              //cell.button.setText("b");
              break;
      // count the number of bombs neighbouring each cell
      private void countNeighbouringBombs() {
        for(int r=0; r<settings.rows; r++) {
          for(int c=0; c<settings.cols; c++) {
            cells[r][c].setNeighbours(getNeighbourCount(r, c));
      // return the number of bombs neighbouring the given cell
      private int getNeighbourCount(int row, int col) {
        int count = 0;
        int firstRow = Math.max(row-1, 0);
        int lastRow  = Math.min(row+1, settings.rows-1);
        int firstCol = Math.max(col-1, 0);
        int lastCol  = Math.min(col+1, settings.cols-1);
        for(int r=firstRow; r<=lastRow; r++) {
          for(int c=firstCol; c<=lastCol; c++) {
            if( this.cells[r][c].isBomb ) count++;
        return count;
      public void setBombsRemaining(int bombsRemaining) {
        this.bombsRemaining = bombsRemaining;
        this.bombs.setText(""+this.bombsRemaining);
      public int getBombsRemaining() {
        return(this.bombsRemaining);
      private void showBombs() {
        for(int r=0; r<settings.rows; r++) {
          for(int c=0; c<settings.cols; c++) {
            Cell cell = cells[r][c];
            if (cell.isBomb) {
              cell.reveal();
            } else if (cell.isMarkedAsBomb()) {
              cell.revealNotABomb();
      private boolean allMarkedBombsAreReallyBombs() {
        for(int r=0; r<settings.rows; r++) {
          for(int c=0; c<settings.cols; c++) {
            Cell cell = this.cells[r][c];
            if (cell.isMarkedAsBomb() && !cell.isBomb() ) {
              return false;
        return true;
      private void revealCell(Cell cell) {
        if(cell.getNeighbours()==0) {
          revealCells(cell, new HashMap<Cell,Object>());
        } else {
          cell.reveal();
      private void revealTheBiggestFreePatch() {
        Map<Cell,Object> counted = new HashMap<Cell,Object>();
        int count = 0;
        int max = 0;
        Cell maxCell = null;
        for(int r=0; r<settings.rows; r++) {
          for(int c=0; c<settings.cols; c++) {
            Cell cell = this.cells[r][c];
            if(cell.getNeighbours()==0) {
              count = countNeighbouringZeros(cell, counted);
              if(count > max) {
                max = count;
                maxCell = cell;
        revealCell(maxCell);
      private int countNeighbouringZeros(Cell cell, Map<Cell,Object> counted) {
        int count = 0;
        if (counted.containsKey(cell)) return 0;
        counted.put(cell, null);
        if(cell.getNeighbours()==0) {
          count++;
          int firstRow = Math.max(cell.row-1, 0);
          int lastRow  = Math.min(cell.row+1, settings.rows-1);
          int firstCol = Math.max(cell.col-1, 0);
          int lastCol  = Math.min(cell.col+1, settings.cols-1);
          for(int r=firstRow; r<=lastRow; r++) {
            for(int c=firstCol; c<=lastCol; c++) {
              Cell x = cells[r][c];
              if (x == cell) continue;
              if (x.getNeighbours()==0) {
                count += countNeighbouringZeros(x, counted);
        return count;
      private void revealCells(Cell cell, Map<Cell,Object> ancestors) {
        if (ancestors.containsKey(cell)) return;
        if (cell.isRevealed()) return;
        ancestors.put(cell, null);
        cell.reveal();
        if(cell.getNeighbours()==0) {
          isVirgin = false;
          int firstRow = Math.max(cell.row-1, 0);
          int lastRow  = Math.min(cell.row+1, settings.rows-1);
          int firstCol = Math.max(cell.col-1, 0);
          int lastCol  = Math.min(cell.col+1, settings.cols-1);
          for(int r=firstRow; r<=lastRow; r++) {
            for(int c=firstCol; c<=lastCol; c++) {
              Cell x = cells[r][c];
              if (x == cell) continue;
              if( !x.isBomb() && !x.isRevealed() ) {
                revealCells(x, ancestors);
      private void youLost() {
        clock.stop();
        showBombs();
        //WavePlayer.play(WAVDIR+"msstartup.wav");
        Object[] options = { "Replay", "New Game" };
        int choice = JOptionPane.showOptionDialog(
            null
          , "Replay?"
          , "Game Over."
          , JOptionPane.DEFAULT_OPTION
          , JOptionPane.QUESTION_MESSAGE
          , null
          , options
          , options[0]
        if (choice == 0) {
          replayCurrentGame = true;
        resetGame();
      private void youWon() {
        clock.stop();
        WavePlayer.play(WAVDIR+"applause.wav");
        JOptionPane.showMessageDialog(this, "Congratulations. You Won!", "Game Over", JOptionPane.INFORMATION_MESSAGE);
        resetGame();
      private static BufferedImage loadImage(String filename) {
        try {
          return ImageIO.read(new File(filename));
        } catch (Exception e) { //IOExcecption
          // the game will still kinda work, you just won't see the images.
          e.printStackTrace();
        return null;
      public static void main(String[] args) {
        java.awt.EventQueue.invokeLater(
          new Runnable() {
            public void run() {
              MineSweeperGame game = new MineSweeperGame();
    CustomSettingsDialog.java
    package krc.games.sweeper;
    import javax.swing.JDialog;
    import javax.swing.JFrame;
    import javax.swing.JPanel;
    import javax.swing.JLabel;
    import javax.swing.JTextField;
    import javax.swing.JButton;
    import javax.swing.JOptionPane;
    import java.awt.event.ActionListener;
    import java.awt.event.ActionEvent;
    public class CustomSettingsDialog extends JDialog implements ActionListener
      private static final long serialVersionUID = 0L;
      private static final int ROW_HEIGHT = 24;
      private static final int PAD = 5;
      private boolean isOk = false;
      GameSettings settings;
      private JPanel panel;
      private JTextField rowsTextField;
      private JTextField colsTextField;
      private JTextField bombsTextField;
      private JButton okButton;
      private JButton cancelButton;
      CustomSettingsDialog(JFrame parentFrame, GameSettings settings) {
        super(parentFrame, true);
        this.settings = settings;
        this.setTitle("Custom Miner");
        this.setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);
        this.setSize(250, 200);
        this.setResizable(false);
        panel = new JPanel();
        panel.setLayout(null);
        addLabel(0, "rows"); rowsTextField = addField(0, ""+settings.rows);
        addLabel(1, "cols"); colsTextField = addField(1, ""+settings.cols);
        addLabel(2, "bombs"); bombsTextField = addField(2, ""+settings.bombs);
        okButton = addButton(3,0, "Ok");
        cancelButton = addButton(3,1, "Cancel");
        rowsTextField.selectAll();
        rowsTextField.requestFocusInWindow();
        this.add(panel);
        this.setVisible(true);
      boolean isOk() {
        return this.isOk;
      GameSettings getSettings() {
        return this.settings;
      private void addLabel(int row, String caption) {
        JLabel label = new JLabel(caption);
        label.setBounds(PAD, PAD+((ROW_HEIGHT+PAD)*row), 80, ROW_HEIGHT);
        panel.add(label);
      private JTextField addField(int row, String text) {
        JTextField field = new JTextField();
        field.setBounds(80, PAD+((ROW_HEIGHT+PAD)*row), 100, ROW_HEIGHT);
        field.setText(text);
        panel.add(field);
        return(field);
      private JButton addButton(int row, int col, String caption) {
        JButton button = new JButton(caption);
        button.setBounds(20+(col*85), PAD+((ROW_HEIGHT+PAD)*row), 80, ROW_HEIGHT);
        button.addActionListener(this);
        panel.add(button);
        return(button);
      public void actionPerformed(ActionEvent event) {
        if ( okButton == event.getSource() ) {
          try {
            this.settings.level = "custom";
            this.settings.rows = Integer.parseInt(rowsTextField.getText());
            this.settings.cols = Integer.parseInt(colsTextField.getText());
            this.settings.bombs = Integer.parseInt(bombsTextField.getText());
            this.isOk = true;
            this.setVisible(false);
          } catch(NumberFormatException e) {
            JOptionPane.showMessageDialog(this, e.getMessage(), "NumberFormatException", JOptionPane.ERROR_MESSAGE);
        } else if ( cancelButton == event.getSource() ) {
          this.isOk = false;
          this.setVisible(false);
        } else {
          java.awt.Toolkit.getDefaultToolkit().beep();
          System.out.println("CustomSettingsDialog.actionPerformed: Unknown event source "+event.getSource());
    GameProperties.java
    package krc.games.sweeper;
    import java.io.File;
    import java.io.FileReader;
    import java.io.FileWriter;
    class GameProperties extends java.util.Properties
      private static final long serialVersionUID = 0L;
      private static final String FILENAME = "MineSweeperGame.properties";
      public GameProperties() {
        super();
        load();
      public GameSettings get(String level) {
        return new GameSettings(
            level
          , getInt(level+"_rows")
          , getInt(level+"_cols")
          , getInt(level+"_bombs")
      public GameSettings get() {
        return get(getProperty("level"));
      public void set(GameSettings settings) {
        setProperty("level", settings.level);
        setProperty(settings.level+"_rows", ""+settings.rows);
        setProperty(settings.level+"_cols", ""+settings.cols);
        setProperty(settings.level+"_bombs", ""+settings.bombs);
      public void load() {
        try {
          load(new FileReader(FILENAME));
        } catch (Exception e) {
          System.err.println("Failed to load properties from "+FILENAME+" - "+e);
          e.printStackTrace();
      public void save() {
        try {
          store(new FileWriter(FILENAME), FILENAME);
        } catch (Exception e) {
          System.err.println("Failed to save properties to "+FILENAME+" - "+e);
          e.printStackTrace();
      private int getInt(String key) {
        try {
          return Integer.parseInt(getProperty(key));
        } catch (Exception e) {
          e.printStackTrace();
          throw new RuntimeException("Failed to getPropertyAsInt("+key+") from "+FILENAME+" - "+e);
    GameSettings.java
    package krc.games.sweeper;
    class GameSettings
      public String level;
      public int rows;
      public int cols;
      public int bombs;
      GameSettings(String level, int rows, int cols, int bombs) {
        this.level = level;
        this.rows = rows;
        this.cols = cols;
        this.bombs = bombs;
    GameClock.java
    package krc.games.sweeper;
    import javax.swing.JLabel;
    import javax.swing.border.LineBorder;
    import java.awt.Color;
    class GameClock extends JLabel
      private static final long serialVersionUID = 0L;
      private volatile boolean isRunning = false;
      private volatile int elapsedTime;
      public GameClock() {
        super("0");
        elapsedTime = 0;
        this.setBorder(new LineBorder(Color.black));
      public void start() {
        if(isRunning) return;
        isRunning = true;
        new Thread() {
          public void run() {
            while (isRunning) {
              setText(""+(elapsedTime++));
              try{Thread.sleep(1000);}catch(InterruptedException eaten){}
        }.start();
      public void stop() {
        this.isRunning = false;
      public void skip(int seconds) {
        elapsedTime+=seconds;
      public void reset() {
        elapsedTime = 0;
        super.setText("0");
    MineSweeperGame.properties
    #MineSweeperGame.properties
    #Sat Dec 29 18:56:47 GMT+10:00 2007
    level=expert
    beginner_rows=9
    beginner_cols=9
    beginner_bombs=10
    intermediate_rows=16
    intermediate_cols=16
    intermediate_bombs=40
    expert_rows=16
    expert_cols=30
    expert_bombs=99
    custom_rows=30
    custom_cols=50
    custom_bombs=299-----
    BTW... I gave up on the whole refactor thing... I just couldn't work out how to cleanly separate the view from the model... and my design was getting mighty complicated.... bigger than ben hurr... So I decided to revert to the "hack" version, and do a little refactoring to try to split out the more-easily-split-out sections, and tada - I (mostly) got through the TODO list.
    Also... can anyone point me at a good serialization tutorial... sun's is a bit how's your dad.
    Thanx all.

  • Why do swing app frameworks quirey the platform for osx?

    I've been looking around at different implimentations of application frameworks based on swing and have noticed that they always quirey the platform to determine if they are running on mac os x. Why is that? Java swing programmers don't seem to be concerned if their app is running on linux, solaris or windows, why is osx a special concern? I haven't been able to determine much from looking at the code and it has me wondering why it should be a concern for me if I'm working on a swing based application.
    TIA,
    Mark

    Nevermind. Not being at all familiar with the apple operating system I was unaware that the menu bar was in a different place than either Unix, Linux or Windows operating systems. My first search on the topic did not produce the desired answer so I came here... maybe a little prematurely. Subsequent searches produced the answer. Sorry.

Maybe you are looking for