changeset 75:b586ce4f6d97

Large GUI code cleanup.
author Matti Hamalainen <ccr@tnsp.org>
date Thu, 03 Mar 2011 04:04:57 +0200
parents b7adeae80363
children 43f68fe605de
files Makefile game/Engine.java game/G.java game/IDMButton.java game/IDMContainer.java game/IDMWidget.java
diffstat 6 files changed, 488 insertions(+), 221 deletions(-) [+]
line wrap: on
line diff
--- a/Makefile	Wed Mar 02 23:48:17 2011 +0200
+++ b/Makefile	Thu Mar 03 04:04:57 2011 +0200
@@ -3,7 +3,8 @@
 
 RESOURCES=graphics/*.png graphics/*.jpg graphics/font.ttf sounds/*.wav
 
-CLASSES=game/Piece.class game/PieceType.class \
+CLASSES=game/G.class \
+	game/Piece.class game/PieceType.class \
 	game/Engine.class game/Interpolate.class \
 	game/ResourceLoader.class game/Sound.class \
 	game/SoundElement.class game/SoundManager.class \
--- a/game/Engine.java	Wed Mar 02 23:48:17 2011 +0200
+++ b/game/Engine.java	Thu Mar 03 04:04:57 2011 +0200
@@ -18,48 +18,157 @@
 import javax.sound.sampled.*;
 
 
-/*
-class AnimatedElement
+class AboutBox extends IDMWidget
 {
-    float x, y, stime, value;
-    Interpolate lerp;
-    boolean active;
+    BufferedImage aboutImg;
+    boolean aboutSecret;
+
+    public AboutBox()
+    {
+        try {
+            ResourceLoader res = new ResourceLoader("graphics/girl.jpg");
+            aboutImg = ImageIO.read(res.getStream());
+        }
+        catch (IOException e)
+        {
+        }
+
+        aboutSecret = false;
+        
+        setPos(150f, 150f);
+        setSize(600f, 400f);
+    }
+
+    IDMPoint currPos, currOffs;
+    Paint textPaint;
+    Font textFont;
+    FontMetrics textMetrics;
     
-    public AnimatedElement(float x, float y, )
+    public void setTextFont(Font font, FontMetrics metrics)
+    {
+        textFont = font;
+        textMetrics = metrics;
+    }
+    
+    public void setTextPaint(Paint paint)
     {
-        stime = 0;
-        this.x = x;
-        this.y = y;
-        
+        textPaint = paint;
+    }
+
+    public void setCurrPos(IDMPoint npos)
+    {
+        currPos = npos;
+        currOffs = new IDMPoint(0, 0);
+    }
+    
+    public void setCurrPos(float x, float y)
+    {
+        setCurrPos(new IDMPoint(x, y));
     }
     
-    public animate(float time)
+    public void drawString(Graphics2D g, String text)
     {
-        if (!active)
+        Paint savePaint = g.getPaint();
+        g.setPaint(textPaint);
+        
+        int i = 0;
+        while (i < text.length())
         {
-            active = true;
-            stime = time;
+            int p = text.indexOf("\n", i);
+            boolean linefeed;
+            String str;
+            if (p > i)
+            {
+                str = text.substring(i, p);
+                i = p + 1;
+                linefeed = true;
+            }
+            else
+            {
+                str = text.substring(i);
+                i += str.length();
+                linefeed = false;
+            }
+
+            g.drawString(str, currPos.x + currOffs.x, currPos.y + currOffs.y);
+            
+            if (linefeed)
+            {
+                currOffs.x = 0;
+                currOffs.y += textMetrics.getMaxDescent();;
+            }
+            else
+            {
+                currOffs.x += textMetrics.stringWidth(str);
+            }
         }
+
+        g.setPaint(savePaint);
+    }
+
+    public void paint(Graphics2D g)
+    {
+        int x = getScaledX(), y = getScaledY(),
+            w = getScaledWidth(), h = getScaledHeight();
+
+        g.setPaint(new Color(0.0f, 0.0f, 0.0f, 0.6f));
+        g.fill(new RoundRectangle2D.Float(x, y, w, h, 10, 10));
+
+        setTextFont(G.fonts[1], G.metrics[1]);
+        setTextPaint(Color.white);
         
-        float t = (time - stime) / 10.0f;
-        if (t < 100)
-            value = lerp.getValue(t);
+        setCurrPos(x + 15, y + 15);
+        drawString(g, "RistiPolku (CrossPaths) v0.5");
+
+        if (aboutSecret)
+        {
+            g.drawImage(aboutImg, x + 15, y + 35, aboutImg.getWidth(), aboutImg.getHeight(), null); 
+
+            setCurrPos(x + 225, y + 55);
+            drawString(g,
+                "Dedicated to my\n" +
+                "favorite woman" +
+                "in the world.");
+            
+            setCurrPos(x + 350, y + 155);
+            drawString(g, "- Matti");
+        }
         else
         {
-            
+            setTextPaint(Color.yellow);
+            drawString(g, "(c) Copyright 2011 Matti 'ccr' Hämäläinen\n");
+
+            setTextPaint(Color.white);
+            drawString(g, "Programming project for Java-course\n" +
+                          "T740306 taught by Kari Laitinen.");
         }
     }
+
+    public boolean keyPressed(KeyEvent e)
+    {
+        if (e.getKeyCode() == KeyEvent.VK_L)
+        {
+            aboutSecret = true;
+        }
+        else
+        {
+            clicked();
+            aboutSecret = false;
+        }
+        return true;
+    }
     
-    public paint(Graphics2D g, );
+    public void clicked()
     {
+        parent.remove(this);
     }
 }
-*/
+
 
 class GameBoard extends IDMWidget
 {
-    public static final int boardSize = 9;
-    public static final int boardMiddle = 4;
+    static final int boardSize = 9;
+    static final int boardMiddle = 4;
     Piece[][] board;
     float pscale;
 
@@ -69,17 +178,14 @@
     public Piece currPiece, nextPiece;
     int currX, currY, currPoint;
 
-
-    SoundManager soundManager;
     Sound sndPlaced;
     
-    public GameBoard(IDMPoint pos, SoundManager smgr, float pscale)
+    public GameBoard(IDMPoint pos, float ps)
     {
         super(pos);
-        this.pscale = pscale;
+        pscale = ps;
 
-        soundManager = smgr;
-//        sndPlaced = soundManager.getSound("sounds/placed.wav");
+//        sndPlaced = smgr.getSound("sounds/placed.wav");
 
         startNewGame();
     }
@@ -122,12 +228,15 @@
                 pscale - pscale / 10);
         }
     }
-
-    public boolean contains(Point where)
+    
+    public boolean contains(float x, float y)
     {
-        return (where.x >= getScaledX() && where.y >= getScaledY() &&
-                where.x < getScaledX() + boardSize * pscale &&
-                where.y < getScaledY() + boardSize * pscale);
+        boolean foo = (x >= getScaledX() &&
+                y >= getScaledY() &&
+                x < getScaledX() + boardSize * pscale &&
+                y < getScaledY() + boardSize * pscale);
+        System.out.print("contains "+x+", "+y+" ("+getScaledX()+", "+getScaledY()+", "+(getScaledX() + boardSize * pscale)+"): "+foo+"\n");
+        return foo;
     }
     
     public void animate(float time)
@@ -148,6 +257,8 @@
         }
     }
 
+    // Change coordinates based on the "outgoing"
+    // piece connection point.
     private void pieceMoveTo(int point)
     {
         switch (point)
@@ -183,7 +294,9 @@
         }
     }
     
-    public boolean pieceCheck(Piece piece)
+    // Check one piece, set connections, find the new placement
+    // based on piece rotations etc.
+    private boolean pieceCheck(Piece piece)
     {
         if (piece == null)
         {
@@ -228,6 +341,8 @@
         return false;
     }
 
+    // Finish one move/turn of the game, resolve path and find placement
+    // of the next piece, or set "game over" state if required.
     public void pieceFinishTurn()
     {
         boolean finished = false;
@@ -235,7 +350,7 @@
 
         if (currPiece != null)
         {
-            soundManager.play(sndPlaced);
+            G.smgr.play(sndPlaced);
         }
         
         while (!finished)
@@ -275,6 +390,7 @@
         return true;
     }
     
+/*
     public boolean mouseClicked(MouseEvent e)
     {
         if (flagGameOver)
@@ -288,6 +404,12 @@
         else
             return false;
     }
+*/
+
+    public void clicked()
+    {
+            pieceFinishTurn();
+    }
 
     public boolean keyPressed(KeyEvent e)
     {
@@ -321,24 +443,14 @@
 {
     long startTime;
     float gameClock, gameFrames;
+
     Thread animThread;
     boolean animEnable = false;
 
-    Font fontMain, font1, font2;
-    FontMetrics metrics1, metrics2;
-    
     GameBoard lauta = null;
-    BufferedImage lautaBG = null, lautaBGScaled = null;
-    Dimension lautaDim;
-
-    static final AudioFormat sfmt = new AudioFormat(22050, 16, 1, true, false);
-    SoundManager soundManager;
     InputStream musa;
-
-    boolean aboutEnabled = false, aboutSecret = false;
-    BufferedImage aboutImg = null;
-
     IDMContainer widgets;
+    AboutBox aboutBox;
 
     public Engine()
     {
@@ -346,23 +458,23 @@
         System.out.print("Engine() constructor\n");
         
         // Sound system
-        soundManager = new SoundManager(sfmt, 16);
+        G.smgr = new SoundManager(new AudioFormat(22050, 16, 1, true, false), 4);
 
         // Load resources
         try
         {
             ResourceLoader res = new ResourceLoader("graphics/board.jpg");
-            lautaBG = ImageIO.read(res.getStream());
-
-            res = new ResourceLoader("graphics/girl.jpg");
-            aboutImg = ImageIO.read(res.getStream());
+            G.lautaBG = ImageIO.read(res.getStream());
 
             try {
                 res = new ResourceLoader("graphics/font.ttf");
-                fontMain = Font.createFont(Font.TRUETYPE_FONT, res.getStream());
+                
                 
-                font1 = fontMain.deriveFont(24f);
-                font2 = fontMain.deriveFont(64f);
+                G.fonts = new Font[G.numFonts];
+                G.fonts[0] = Font.createFont(Font.TRUETYPE_FONT, res.getStream());
+                G.fonts[1] = G.fonts[0].deriveFont(24f);
+                G.fonts[2] = G.fonts[0].deriveFont(64f);
+                
             }
             catch (FontFormatException e)
             {
@@ -385,15 +497,16 @@
         // Create IDM GUI widgets
         widgets = new IDMContainer();
 
-        widgets.add(new BtnSwapPiece(0.75f, 0.60f));
-        widgets.add(new BtnAbout(0.75f, 0.75f));
-        widgets.add(new BtnNewGame(0.75f, 0.85f));
+        widgets.add(new BtnSwapPiece(767f, 450f));
+        widgets.add(new BtnAbout    (767f, 570f));
+        widgets.add(new BtnNewGame  (767f, 650f));
 
-        lauta = new GameBoard(new IDMPoint(0.09f, 0.18f), soundManager, 63);
+        lauta = new GameBoard(new IDMPoint(95, 130), 63);
         widgets.add(lauta);
 
+        aboutBox = new AboutBox();
+        
         // Game
-        aboutEnabled = false;
         startNewGame();
         
         // Initialize event listeners
@@ -408,7 +521,7 @@
             requestFocus();
         }
         
-//        soundManager.play(musa);
+//        G.smgr.play(musa);
     }
 
     public void startNewGame()
@@ -419,41 +532,6 @@
         lauta.startNewGame();
     }
 
-    public void paintAbout(Graphics2D g)
-    {
-        int width = lautaDim.width / 2,
-            height = lautaDim.height / 4;
-        
-        int x = (lautaDim.width - width) / 2,
-            y = (lautaDim.height - height) / 2;
-        
-        g.setPaint(new Color(0.0f, 0.0f, 0.0f, 0.6f));
-        g.fill(new RoundRectangle2D.Float(x, y, width, height, 10, 10));
-
-
-        g.setFont(font1);
-        g.setPaint(Color.white);
-
-        g.drawString("RistiPolku (CrossPaths) v0.5", x + 10, y + 25);
-        if (aboutSecret)
-        {
-            g.drawImage(aboutImg, x + 15, y + 35, aboutImg.getWidth(), aboutImg.getHeight(), null); 
-            g.drawString("Dedicated to my", x + 225, y + 55);
-            g.drawString("favorite woman", x + 225, y + 85);
-            g.drawString("in the world.", x + 225, y + 115);
-            g.drawString("- Matti", x + 350, y + 155);
-        }
-        else
-        {
-            g.setPaint(Color.yellow);
-            g.drawString("(c) Copyright 2011 Matti 'ccr' Hämäläinen", x + 10, y + 55);
-
-            g.setPaint(Color.white);
-            g.drawString("Programming project for Java-course", x + 10, y + 105);
-            g.drawString("T740306 taught by Kari Laitinen.", x + 10, y + 135);
-        }
-    }
-
     public void paintComponent(Graphics g)
     {
         Graphics2D g2 = (Graphics2D) g;
@@ -468,34 +546,34 @@
 
         // Rescale if parent component size has changed
         Dimension dim = getSize();
-        if (lautaDim == null || !dim.equals(lautaDim))
+        if (G.screenDim == null || !dim.equals(G.screenDim))
         {
             // Rescale IDM GUI widgets
-            widgets.setScale(dim.width, dim.height);
+            widgets.setScale(dim.width / 1024.0f, dim.height / 768.0f);
+            G.screenDim = dim;
 
             // Rescale background image
-            lautaBGScaled = new BufferedImage(dim.width, dim.height, BufferedImage.TYPE_INT_ARGB);
-            Graphics2D gimg = lautaBGScaled.createGraphics();
+            G.lautaBGScaled = new BufferedImage(dim.width, dim.height, BufferedImage.TYPE_INT_ARGB);
+            Graphics2D gimg = G.lautaBGScaled.createGraphics();
             gimg.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
                                   RenderingHints.VALUE_INTERPOLATION_BICUBIC);
 
-            gimg.drawImage(lautaBG, 0, 0, dim.width, dim.height, null); 
+            gimg.drawImage(G.lautaBG, 0, 0, dim.width, dim.height, null); 
             lauta.paintBackPlate(gimg);
-            lautaDim = dim;
 
             System.out.print("scale changed\n");
         }
         
         // Get font metrics against current Graphics2D context
-        if (metrics1 == null)
-            metrics1 = g2.getFontMetrics(font1);
-
-        if (metrics2 == null)
-            metrics2 = g2.getFontMetrics(font2);
+        if (G.metrics == null)
+        {
+            G.metrics = new FontMetrics[G.numFonts];
+            for (int i = 0; i < G.numFonts; i++)
+                G.metrics[i] = g2.getFontMetrics(G.fonts[i]);
+        }
         
-
         // Draw background image, pieces, widgets
-        g2.drawImage(lautaBGScaled, 0, 0, null);
+        g2.drawImage(G.lautaBGScaled, 0, 0, null);
         widgets.paint(g2);
 
         if (!lauta.flagGameOver)
@@ -512,8 +590,8 @@
         {
             // Game over text
             String text = "Game Over!";
-            int textWidth = metrics2.stringWidth(text);
-            g2.setFont(font2);
+            int textWidth = G.metrics[2].stringWidth(text);
+            g2.setFont(G.fonts[2]);
 
             g2.setPaint(new Color(0.0f, 0.0f, 0.0f, 0.5f));
             g2.drawString(text, (dim.width - textWidth) / 2 + 5, dim.height / 2 + 5);
@@ -523,20 +601,18 @@
             g2.drawString(text, (dim.width - textWidth) / 2 + (float) f, dim.height / 2 + (float) f);
         }
         
-        // Scores, etc
-        g2.setFont(font2);
+        // Score
+        g2.setFont(G.fonts[2]);
         g2.setPaint(Color.white);
-
         g2.drawString(""+ String.format("%05d", lauta.gameScore), dim.width - 230, 220);
 
-        g2.setFont(font1);
+
+        // Frames per second counter
+        g2.setFont(G.fonts[1]);
         long currTime = new Date().getTime();
         g2.drawString("fps = "+ ((gameFrames * 1000) / (currTime - startTime)), dim.width - 120, 20);
         
-        if (aboutEnabled)
-        {
-            paintAbout(g2);
-        }
+        gameFrames++;
     }
 
     public void startThreads()
@@ -563,21 +639,31 @@
         }
 
         // Shut down sound manager
-        soundManager.close();
+        G.smgr.close();
     }
 
-    public void mouseEntered(MouseEvent e) { }
-    public void mouseExited(MouseEvent e) { }
+    public void mouseEntered(MouseEvent e)
+    {
+        widgets.mouseEntered(e);
+    }
+    public void mouseExited(MouseEvent e)
+    {
+        widgets.mouseExited(e);
+    }
 
     public void mousePressed(MouseEvent e)
     {
-        if (!aboutEnabled)
+        if (widgets.containsObject(aboutBox))
+            aboutBox.mousePressed(e);
+        else
             widgets.mousePressed(e);
     }
 
     public void mouseReleased(MouseEvent e)
     {
-        if (!aboutEnabled)
+        if (widgets.containsObject(aboutBox))
+            aboutBox.mouseReleased(e);
+        else
             widgets.mouseReleased(e);
     }
 
@@ -588,10 +674,6 @@
             System.out.print("Requesting focus\n");
             requestFocus();
         }
-        if (!aboutEnabled)
-        {
-            lauta.mouseClicked(e);
-        }
     }
 
     public void mouseWheelMoved(MouseWheelEvent e)
@@ -599,36 +681,16 @@
         lauta.mouseWheelMoved(e);
     }
 
-    public void keyTyped(KeyEvent e)
-    {
-    }
-   
-    public void keyReleased(KeyEvent e)
-    {
-    }
+    public void keyTyped(KeyEvent e) { }
+    public void keyReleased(KeyEvent e) { }
    
     public void keyPressed(KeyEvent e)
     {
-        if (aboutEnabled)
-        {
-            if (e.getKeyCode() == KeyEvent.VK_L)
-            {
-                aboutSecret = true;
-            }
-            else
-            {
-                aboutEnabled = false;
-                aboutSecret = false;
-            }
-        }
+        // Handle keyboard input
+        if (widgets.containsObject(aboutBox))
+            aboutBox.keyPressed(e);
         else
-        {
-            // Handle keyboard input
-            if (lauta.keyPressed(e))
-                return;
-
             widgets.keyPressed(e);
-        }
     }
     
     public void run()
@@ -645,10 +707,7 @@
             
             // Repaint with a frame limiter
             if (gameClock % 4 == 1)
-            {
                 repaint();
-                gameFrames++;
-            }
             
             // Sleep for a moment
             try {
@@ -663,7 +722,7 @@
     {
         public BtnNewGame(float x, float y)
         {
-            super(x, y, KeyEvent.VK_ESCAPE, font1, "New Game");
+            super(x, y, KeyEvent.VK_ESCAPE, G.fonts[1], "New Game");
         }
 
         public void clicked()
@@ -676,7 +735,7 @@
     {
         public BtnSwapPiece(float x, float y)
         {
-            super(x, y, KeyEvent.VK_SPACE, font1, "Swap");
+            super(x, y, KeyEvent.VK_SPACE, G.fonts[1], "Swap");
         }
 
         public void clicked()
@@ -689,12 +748,13 @@
     {
         public BtnAbout(float x, float y)
         {
-            super(x, y, KeyEvent.VK_A, font1, "About");
+            super(x, y, KeyEvent.VK_A, G.fonts[1], "About");
         }
 
         public void clicked()
         {
-            aboutEnabled = true;
+            if (!widgets.containsObject(aboutBox))
+                widgets.add(aboutBox);
         }
     }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/game/G.java	Thu Mar 03 04:04:57 2011 +0200
@@ -0,0 +1,27 @@
+package game;
+
+import java.awt.*;
+import java.awt.geom.*;
+import java.awt.event.*;
+import java.awt.image.*;
+import java.awt.event.*;
+import java.awt.font.*;
+import javax.imageio.*;
+import javax.swing.*;
+import java.util.*;
+import java.io.*;
+import game.*;
+import javax.sound.sampled.*;
+
+public class G
+{
+    public static final int numFonts = 3;
+    public static Font fonts[];
+    public static FontMetrics metrics[];
+
+    public static SoundManager smgr;
+
+    public static Dimension screenDim;
+
+    public static BufferedImage lautaBG = null, lautaBGScaled = null;
+}
--- a/game/IDMButton.java	Wed Mar 02 23:48:17 2011 +0200
+++ b/game/IDMButton.java	Thu Mar 03 04:04:57 2011 +0200
@@ -24,11 +24,14 @@
  
     public IDMButton(IDMPoint pos, int keyCode, Font font, String text)
     {
+        super(pos);
         loadImages();
-        this.pos = pos;
+        setSize(imgUp.getWidth(), imgUp.getHeight());
+
         this.font = font;
         this.keyCode = keyCode;
         this.text = text;
+
         state = State.NORMAL;
         active = false;
     }
@@ -82,39 +85,46 @@
         g.setFont(font);
         g.setPaint(Color.black);
         g.drawString(text,
-           getScaledX() + xoffs * 2 + (img.getWidth() - textWidth) / 2,
-           getScaledY() + yoffs * 2 + img.getHeight() / 2);
+           getScaledX() + xoffs + (getScaledWidth() - textWidth) / 2,
+           getScaledY() + yoffs + getScaledHeight() / 2);
     }
     
-    public boolean contains(Point where)
-    {
-        return (where.x >= getScaledX() && where.y >= getScaledY() &&
-                where.x < getScaledX() + imgUp.getWidth() &&
-                where.y < getScaledY() + imgUp.getHeight());
-    }
     
-    public void mousePressed(MouseEvent e)
+    public boolean mousePressed(MouseEvent e)
     {
         state = State.PRESSED;
         active = true;
+        return true;
     }
 
-    public void mouseReleased(MouseEvent e)
+    public boolean mouseReleased(MouseEvent e)
     {
+        boolean oldActive = active;
         super.mouseReleased(e);
         state = State.NORMAL;
         active = false;
+        return oldActive;
     }
 
-    public void mouseEntered(MouseEvent e)
+    public boolean mouseEntered(MouseEvent e)
     {
         if (active)
+        {
             state = State.PRESSED;
+            return true;
+        }
+        else
+            return false;
     }
 
-    public void mouseExited(MouseEvent e)
+    public boolean mouseExited(MouseEvent e)
     {
         if (active)
+        {
             state = State.NORMAL;
+            return true;
+        }
+        else
+            return false;
     }
 }
--- a/game/IDMContainer.java	Wed Mar 02 23:48:17 2011 +0200
+++ b/game/IDMContainer.java	Thu Mar 03 04:04:57 2011 +0200
@@ -9,75 +9,182 @@
 import java.util.*;
 
 
+
 public class IDMContainer extends IDMWidget
 {
-    ArrayList<IDMWidget> widgets;
+    private ArrayList<IDMWidget> children, queue;
+    private IDMWidget modal;
+    private int iterated;
+    private boolean modified;
 
     public IDMContainer()
     {
-        widgets = new ArrayList<IDMWidget>();
+        children = new ArrayList<IDMWidget>();
+        iterated = 0;
+        modified = false;
     }
     
-    public void add(IDMWidget widget)
+    synchronized public void add(IDMWidget widget)
     {
-        widgets.add(widget);
+        widget.setParent(this);
+        if (iterated > 0)
+        {
+            queue.add(0, widget);
+            modified = true;
+        }
+        else
+            children.add(widget);
     }
     
-    public void paint(Graphics2D g)
+    synchronized public void remove(IDMWidget widget)
     {
-        for (IDMWidget widget : widgets)
-            widget.paint(g);
+        widget.setParent(null);
+        if (iterated > 0)
+        {
+            queue.remove(widget);
+            modified = true;
+        }
+        else
+            children.remove(widget);
     }
-    
-    public void mousePressed(MouseEvent e)
+
+    synchronized private void beginIteration()
     {
-        for (IDMWidget widget : widgets)
+        modified = false;
+        if (iterated == 0)
         {
-            if (widget.contains(e.getPoint()))
-                widget.mousePressed(e);
+            queue = new ArrayList<IDMWidget>();
+            for (IDMWidget widget : children)
+                queue.add(widget);
         }
+        iterated++;
+    }
+
+    synchronized private void endIteration()
+    {
+        if (modified && iterated == 1)
+        {
+            children = queue;
+            queue = null;
+        }
+        iterated--;
     }
 
-    public void mouseReleased(MouseEvent e)
+    synchronized public void paint(Graphics2D g)
     {
-        for (IDMWidget widget : widgets)
+        // Paint in reverse order
+        ListIterator<IDMWidget> iter = children.listIterator(children.size());
+
+        while (iter.hasPrevious())
         {
-            widget.mouseReleased(e);
-        }
-    }
-
-    public void mouseExited(MouseEvent e)
-    {
-        for (IDMWidget widget : widgets)
-        {
-            widget.mouseExited(e);
+            final IDMWidget widget = iter.previous();
+            widget.paint(g);
         }
     }
+    
+    synchronized public boolean mousePressed(MouseEvent e)
+    {
+        try {
+            beginIteration();
+            for (IDMWidget widget : children)
+            {
+                if (widget.contains(e.getPoint()))
+                {
+                    if (widget.mousePressed(e))
+                        return true;
+                }
+            }
+        }
+        finally { endIteration(); }
+        return false;
+    }
 
-    public void mouseEntered(MouseEvent e)
+    synchronized public boolean mouseReleased(MouseEvent e)
     {
-        for (IDMWidget widget : widgets)
-        {
-            if (widget.contains(e.getPoint()))
-                widget.mouseEntered(e);
+        try {
+            beginIteration();
+            for (IDMWidget widget : children)
+            {
+                if (widget.mouseReleased(e))
+                    return true;
+            }
         }
+        finally { endIteration(); }
+        return false;
+    }
+
+    synchronized public boolean mouseExited(MouseEvent e)
+    {
+        try {
+            beginIteration();
+            for (IDMWidget widget : children)
+            {
+                if (widget.mouseExited(e))
+                    return true;
+            }
+        }
+        finally { endIteration(); }
+        return false;
     }
 
-    public boolean keyPressed(KeyEvent e)
+    synchronized public boolean mouseEntered(MouseEvent e)
+    {
+        try {
+            beginIteration();
+            for (IDMWidget widget : children)
+            {
+                if (widget.contains(e.getPoint()))
+                {
+                    if (widget.mouseEntered(e))
+                        return true;
+                }
+            }
+        }
+        finally { endIteration(); }
+        return false;
+    }
+
+    synchronized public boolean keyPressed(KeyEvent e)
     {
-        for (IDMWidget widget : widgets)
+        try {
+            beginIteration();
+            for (IDMWidget widget : children)
+            {
+                 if (widget.keyPressed(e))
+                    return true;
+            }
+        }
+        finally { endIteration(); }
+        return false;
+    }
+
+    synchronized public void setScale(IDMPoint scale)
+    {
+        beginIteration();
+        for (IDMWidget widget : children)
         {
-            if (widget.keyPressed(e))
+            widget.setScale(scale);
+        }
+        endIteration();
+    }
+
+    synchronized public boolean hasObject(Class c)
+    {
+        for (IDMWidget widget : children)
+        {
+            if (widget.getClass() == c)
                 return true;
         }
         return false;
     }
 
-    public void setScale(IDMPoint scale)
+    synchronized public boolean containsObject(Object o)
     {
-        for (IDMWidget widget : widgets)
+        for (IDMWidget widget : children)
         {
-            widget.setScale(scale);
+            if (widget == o)
+                return true;
         }
+        return false;
     }
 }
--- a/game/IDMWidget.java	Wed Mar 02 23:48:17 2011 +0200
+++ b/game/IDMWidget.java	Thu Mar 03 04:04:57 2011 +0200
@@ -10,7 +10,8 @@
 
 public class IDMWidget
 {
-    IDMPoint pos, scale;
+    IDMWidget parent;
+    IDMPoint pos, size, scale;
     int keyCode;
     
     public IDMWidget()
@@ -18,6 +19,7 @@
         keyCode = -1;
         this.scale = new IDMPoint(1, 1);
         this.pos = new IDMPoint(0, 0);
+        this.size = new IDMPoint(0, 0);
     }
 
     public IDMWidget(IDMPoint pos)
@@ -26,15 +28,45 @@
         this.pos = pos;
     }
 
-    public void move(IDMPoint pos)
+    public IDMWidget(IDMPoint pos, IDMPoint size)
+    {
+        this();
+        this.pos = pos;
+        this.size = size;
+    }
+
+    public void setParent(IDMWidget par)
+    {
+        this.parent = par;
+    }
+
+    public void add(IDMWidget widget)
+    {
+    }
+    
+    public void remove(IDMWidget widget)
+    {
+    }
+
+    public void setPos(IDMPoint pos)
     {
         this.pos = pos;
     }
 
-    public void move(float x, float y)
+    public void setPos(float x, float y)
     {
         this.pos = new IDMPoint(x, y);
     }
+    
+    public void setSize(IDMPoint size)
+    {
+        this.size = size;
+    }
+    
+    public void setSize(float w, float h)
+    {
+        this.size = new IDMPoint(w, h);
+    }
 
     public void setScale(IDMPoint scale)
     {
@@ -48,39 +80,69 @@
 
     public int getScaledX()
     {
-        return (int) (this.pos.x * this.scale.x);
+        return (int) (pos.x * scale.x);
     }
 
     public int getScaledY()
     {
-        return (int) (this.pos.y * this.scale.y);
+        return (int) (pos.y * scale.y);
+    }
+
+    public int getScaledWidth()
+    {
+        return (int) (size.x * scale.x);
+    }
+
+    public int getScaledHeight()
+    {
+        return (int) (size.y * scale.y);
     }
 
+    public boolean contains(float x, float y)
+    {
+        return (x >= getScaledX() &&
+                y >= getScaledY() &&
+                x < getScaledX() + getScaledWidth() &&
+                y < getScaledY() + getScaledHeight());
+    }
+
+    public boolean contains(Point where)
+    {
+        return contains(where.x, where.y);
+    }
+
+    public boolean contains(IDMPoint where)
+    {
+        return contains(where.x, where.y);
+    }
+    
     public void paint(Graphics2D g)
     {
     }
     
-    public boolean contains(Point pos)
+    public boolean mousePressed(MouseEvent e)
     {
         return false;
     }
-    
-    public void mousePressed(MouseEvent e)
-    {
-    }
 
-    public void mouseReleased(MouseEvent e)
+    public boolean mouseReleased(MouseEvent e)
     {
         if (contains(e.getPoint()))
+        {
             clicked();
+            return true;
+        }
+        return false;
     }
 
-    public void mouseEntered(MouseEvent e)
+    public boolean mouseEntered(MouseEvent e)
     {
+        return false;
     }
 
-    public void mouseExited(MouseEvent e)
+    public boolean mouseExited(MouseEvent e)
     {
+        return false;
     }
 
     // Generic key handler