JComponentBounds: Unterschied zwischen den Versionen

Aus Byte-Welt Wiki
Zur Navigation springenZur Suche springen
K
 
(18 dazwischenliegende Versionen von 2 Benutzern werden nicht angezeigt)
Zeile 1: Zeile 1:
Die Klasse "JComponentBounds" erlaubt es, JComponents zu verschieben und in der Grösse zu verändern. Die Klasse "PictureDemo" weiter unten zeigt ein Anwendungsbeispiel.
+
__TOC__
<code=java>
+
=Die Klasse und ein Anwendungsbeispiel=
 +
==JComponentBounds==
 +
Die Klasse <code>JComponentBounds</code> erlaubt es, {{JAPI|JComponent}}s zu verschieben und in der Grösse zu verändern. Die Klasse "PictureDemo" weiter unten zeigt ein Anwendungsbeispiel.
 +
<syntaxhighlight lang="java">
 
/*
 
/*
 
  * JComponentBounds.java
 
  * JComponentBounds.java
  *  
+
  *
  * This class aims at making JComponents moveable and resizable.
+
  * This class aims at making any JComponent resizable
  *  
+
* simply by putting it inside a JInternalFrame.
 +
* The wrapped JComponent is made moveable
 +
* by means of a MouseInputAdapter.
 +
* The class includes a "snap to grid" option.
 +
  *
 
  * author: Andre Uhres
 
  * author: Andre Uhres
  * last update November 23, 2009 at 14:00
+
  * update April 15, 2010 at 6:00
 +
* update December 21, 2010 at 15:00
 
  */
 
  */
  
Zeile 18: Zeile 26:
 
public class JComponentBounds extends JInternalFrame {
 
public class JComponentBounds extends JInternalFrame {
  
 +
    public static final int FOCUS_GAINED = 97531;
 
     private JComponent componentsContainer;
 
     private JComponent componentsContainer;
 
     private boolean focused;
 
     private boolean focused;
    private int deltaX;
 
    private int deltaY;
 
 
     private JComponent comp;
 
     private JComponent comp;
 
     private static final Color TRANSPARENT = new Color(0.0f, 0.0f, 0.0f, 0.0f);
 
     private static final Color TRANSPARENT = new Color(0.0f, 0.0f, 0.0f, 0.0f);
Zeile 42: Zeile 49:
 
     }
 
     }
  
     public JComponentBounds(final JComponent comp, final int x, final int y, final boolean focused) {
+
     public JComponentBounds(final JComponent comp, final int x, final int y,
 +
            final boolean focused) {
 
         super();
 
         super();
 
         this.comp = comp;
 
         this.comp = comp;
 
         this.focused = focused;
 
         this.focused = focused;
         setBounds(x, y, comp.getPreferredSize().width, comp.getPreferredSize().height);
+
         setBounds(x, y,
 +
                comp.getPreferredSize().width, comp.getPreferredSize().height);
 
         setResizable(true);
 
         setResizable(true);
 
         setVisible(true);
 
         setVisible(true);
Zeile 52: Zeile 61:
 
         setBackground(TRANSPARENT);
 
         setBackground(TRANSPARENT);
 
         ((BasicInternalFrameUI) getUI()).setNorthPane(null);
 
         ((BasicInternalFrameUI) getUI()).setNorthPane(null);
         setBorder(BorderFactory.createEmptyBorder(MARGIN, MARGIN, MARGIN, MARGIN));
+
         setBorder(BorderFactory.createEmptyBorder(
 +
                MARGIN, MARGIN, MARGIN, MARGIN));
 
         comp.setFocusable(true);
 
         comp.setFocusable(true);
 
         add(comp);
 
         add(comp);
 
         ml = new MouseInputAdapter() {
 
         ml = new MouseInputAdapter() {
 +
 +
            private Point p1;
  
 
             @Override
 
             @Override
 
             public void mousePressed(final MouseEvent e) {
 
             public void mousePressed(final MouseEvent e) {
 +
                p1 = e.getPoint();
 
                 componentsContainer = (JComponent) getParent();
 
                 componentsContainer = (JComponent) getParent();
                deltaX = e.getPoint().x;
 
                deltaY = e.getPoint().y;
 
 
                 e.setSource(JComponentBounds.this);
 
                 e.setSource(JComponentBounds.this);
                 //componentsContainer might use the event  
+
                 //componentsContainer might use the mousePressed event
 
                 //to take hidden components to the top:
 
                 //to take hidden components to the top:
 
                 componentsContainer.dispatchEvent(e);
 
                 componentsContainer.dispatchEvent(e);
Zeile 72: Zeile 83:
 
                 componentsContainer = (JComponent) getParent();
 
                 componentsContainer = (JComponent) getParent();
 
                 e.setSource(JComponentBounds.this);
 
                 e.setSource(JComponentBounds.this);
                 //componentsContainer might use the event  
+
                 //componentsContainer might use the mouseReleased event
 
                 //to take hidden components to the top:
 
                 //to take hidden components to the top:
 
                 componentsContainer.dispatchEvent(e);
 
                 componentsContainer.dispatchEvent(e);
Zeile 82: Zeile 93:
 
                     return;
 
                     return;
 
                 }
 
                 }
 +
                //drag operation:
 +
                Point p2 = e.getPoint();
 +
                Point loc = getLocation();
 +
                loc.translate(p2.x - p1.x, p2.y - p1.y);
 +
                setLocation(loc);
 
                 componentsContainer = (JComponent) getParent();
 
                 componentsContainer = (JComponent) getParent();
                 componentsContainer.setComponentZOrder(JComponentBounds.this, 0);
+
                //auto scroll:
                setLocation(getLocation().x + e.getPoint().x - deltaX,
+
                 componentsContainer.scrollRectToVisible(
                        getLocation().y + e.getPoint().y - deltaY);
+
                        new Rectangle(loc.x, loc.y, getWidth(), getHeight()));
                 componentsContainer.repaint();
+
                 //componentsContainer might use the focusGained event
                 setZorderAllowed(false);//let dragged component on the top
+
                //to keep the dragged component in front:
 +
                FocusListener[] focusListeners = componentsContainer.getFocusListeners();
 +
                 if (focusListeners != null && focusListeners.length > 0) {
 +
                    FocusListener fl = focusListeners[0];
 +
                    fl.focusGained(
 +
                            new FocusEvent(JComponentBounds.this,
 +
                            JComponentBounds.FOCUS_GAINED));
 +
                }
 
             }
 
             }
 
         };
 
         };
Zeile 95: Zeile 118:
 
             public void keyPressed(final KeyEvent e) {
 
             public void keyPressed(final KeyEvent e) {
 
                 componentsContainer = (JComponent) getParent();
 
                 componentsContainer = (JComponent) getParent();
                 //componentsContainer might use the event  
+
                 //componentsContainer might use the keyPressed event
 
                 //to delete the component:
 
                 //to delete the component:
 
                 componentsContainer.dispatchEvent(e);
 
                 componentsContainer.dispatchEvent(e);
 
 
             }
 
             }
 
         };
 
         };
Zeile 105: Zeile 127:
 
             @Override
 
             @Override
 
             public void componentResized(final ComponentEvent e) {
 
             public void componentResized(final ComponentEvent e) {
                 adaptZorder(e);
+
                 ((JComponent) e.getSource()).revalidate();
                 componentsContainer = (JComponent) e.getSource();
+
                 componentsContainer = (JComponent) getParent();
                 componentsContainer.revalidate();
+
                 //componentsContainer might use the componentResized event
 +
                //to transfer focus to the resizing component:
 +
                if(componentsContainer != null){
 +
                    componentsContainer.dispatchEvent(e);
 +
                }
 
             }
 
             }
  
 
             @Override
 
             @Override
 
             public void componentMoved(final ComponentEvent e) {
 
             public void componentMoved(final ComponentEvent e) {
                 componentsContainer = (JComponent) e.getSource();
+
                 ((JComponent) e.getSource()).revalidate();
                componentsContainer.revalidate();
 
 
             }
 
             }
 
         };
 
         };
Zeile 119: Zeile 144:
 
     }
 
     }
  
 +
    /* Move and resize this component.
 +
    * This method has been overridden to implement the "snap to grid" option.
 +
    */
 
     @Override
 
     @Override
     public void reshape(final int x, final int y, final int width, final int height) {
+
     public void reshape(final int x, final int y,
 +
            final int width, final int height) {
 
         if (snapToGrid) {
 
         if (snapToGrid) {
             int x2 = x / MARGIN * MARGIN;
+
            //convert all parameters to multiples of MARGIN:
             int y2 = y / MARGIN * MARGIN;
+
             int gridx = x / MARGIN * MARGIN;
             int w2 = width / MARGIN * MARGIN;
+
             int gridy = y / MARGIN * MARGIN;
             int h2 = height / MARGIN * MARGIN;
+
             int gridwidth = width / MARGIN * MARGIN;
             if (x2 != x && w2 != width) {
+
             int gridheight = height / MARGIN * MARGIN;
                 w2 += MARGIN;
+
            //if not horizontally on the grid, round up the grid width:
 +
             if (gridx != x && gridwidth != width) {
 +
                 gridwidth += MARGIN;
 
             }
 
             }
             if (y2 != y && h2 != height) {
+
            //if not vertically on the grid, round up the grid height:
                 h2 += MARGIN;
+
             if (gridy != y && gridheight != height) {
 +
                 gridheight += MARGIN;
 
             }
 
             }
             super.reshape(x2, y2, w2, h2);
+
            //snap to the grid:
         }else{
+
             super.reshape(gridx, gridy, gridwidth, gridheight);
 +
         } else {
 
             super.reshape(x, y, width, height);
 
             super.reshape(x, y, width, height);
 
         }
 
         }
Zeile 144: Zeile 177:
 
     public void setSnapToGrid(final boolean snapToGrid) {
 
     public void setSnapToGrid(final boolean snapToGrid) {
 
         this.snapToGrid = snapToGrid;
 
         this.snapToGrid = snapToGrid;
    }
 
 
    //Must be overridden to let dragged component on the top:
 
    public void setZorderAllowed(final boolean b) {
 
    }
 
 
    public void adaptZorder(final ComponentEvent e) {
 
        componentsContainer = (JComponent) getParent();
 
        Object source = e.getSource();
 
        if (componentsContainer instanceof ComponentsContainer && source instanceof JComponentBounds) {
 
            ((ComponentsContainer) componentsContainer).adaptZorder(source, new Point(0, 0));
 
        }
 
 
     }
 
     }
  
Zeile 190: Zeile 211:
 
     public boolean isFocused() {
 
     public boolean isFocused() {
 
         return focused;
 
         return focused;
 +
    }
 +
 +
    public Rectangle getComponentRect() {
 +
        Insets insets = getInsets();
 +
        Rectangle rect = getBounds();
 +
        rect.x += insets.left;
 +
        rect.y += insets.top;
 +
        rect.width -= (insets.right + insets.left);
 +
        rect.height -= (insets.top + insets.bottom);
 +
        return rect;
 
     }
 
     }
 
}
 
}
</code=java>
+
</syntaxhighlight>
 +
 
 +
==PictureDemo==
 +
 
 +
Die Klasse "PictureDemo" ermöglicht das Laden eines Bildes durch einen Klick auf das Fenster.
 +
 
 +
[[Datei:PictureDemo1.jpg]]
 +
 
 +
Das geladene Bild kann man anschließend in der Größe und Position verändern.
 +
 
 +
[[Datei:PictureDemo.jpg]]
 +
 
 +
<syntaxhighlight lang="java">
 +
/*
 +
* PictureDemo.java
 +
*/
  
<code=java>
 
 
import container.JComponentBounds;
 
import container.JComponentBounds;
 
import java.awt.*;
 
import java.awt.*;
Zeile 204: Zeile 249:
 
import javax.swing.*;
 
import javax.swing.*;
  
 +
 +
//Model
 +
class PictureStorage extends Observable {
 +
 +
    private BufferedImage data;
 +
 +
    public PictureStorage() {
 +
    }
 +
 +
    public void loadPicture(final String filename) {
 +
        System.out.println("loading " + filename);
 +
        SwingWorker<BufferedImage, Void> loadPicture = new SwingWorker<BufferedImage, Void>() {
 +
 +
            @Override
 +
            protected BufferedImage doInBackground() throws Exception {
 +
                try {
 +
                    data = ImageIO.read(new File(filename));
 +
                } catch (IOException ex) {
 +
                    System.out.println(ex);
 +
                }
 +
                return data;
 +
            }
 +
 +
            @Override
 +
            protected void done() {
 +
                setChanged();
 +
                notifyObservers(data); //sends change notification to the view
 +
            }
 +
        };
 +
        loadPicture.execute();
 +
    }
 +
}
 +
 +
//View
 
class GUI extends JFrame implements Observer {
 
class GUI extends JFrame implements Observer {
  
Zeile 227: Zeile 306:
  
 
             @Override
 
             @Override
             public void mousePressed(MouseEvent e) {
+
             public void mousePressed(final MouseEvent e) {
 
                 FileDialog fileDialog = new FileDialog(frame);
 
                 FileDialog fileDialog = new FileDialog(frame);
 
                 fileDialog.setVisible(true);
 
                 fileDialog.setVisible(true);
Zeile 242: Zeile 321:
  
 
             @Override
 
             @Override
             protected void paintComponent(Graphics g) {
+
             protected void paintComponent(final Graphics g) {
 
                 super.paintComponent(g);
 
                 super.paintComponent(g);
 
//                g.drawImage(image, 0, 0, this);
 
//                g.drawImage(image, 0, 0, this);
Zeile 268: Zeile 347:
 
}
 
}
  
class PictureStorage extends Observable {
+
//Controller
 
 
    private BufferedImage data;
 
 
 
    public PictureStorage() {
 
    }
 
 
 
    public void loadPicture(final String filename) {
 
        System.out.println("loading " + filename);
 
        SwingWorker<BufferedImage, Void> loadPicture = new SwingWorker<BufferedImage, Void>() {
 
 
 
            @Override
 
            protected BufferedImage doInBackground() throws Exception {
 
                try {
 
                    data = ImageIO.read(new File(filename));
 
                } catch (IOException ex) {
 
                    ex.printStackTrace();
 
                }
 
                return data;
 
            }
 
 
 
            @Override
 
            protected void done() {
 
                setChanged();
 
                notifyObservers(data); //sends change notification to the view
 
            }
 
        };
 
        loadPicture.execute();
 
    }
 
}
 
 
 
 
public class PictureDemo {
 
public class PictureDemo {
  
Zeile 310: Zeile 359:
 
     }
 
     }
  
     public void loadPicture(String file) {
+
     public void loadPicture(final String file) {
 
         model.loadPicture(file);
 
         model.loadPicture(file);
 
     }
 
     }
Zeile 319: Zeile 368:
 
             @Override
 
             @Override
 
             public void run() {
 
             public void run() {
                 new PictureDemo();
+
                 PictureDemo pictureDemo = new PictureDemo();
 
             }
 
             }
 
         };
 
         };
Zeile 326: Zeile 375:
 
     }
 
     }
 
}
 
}
</code=java>
+
</syntaxhighlight>
  
[[Kategorie:Java]]
 
 
[[Kategorie:Swing]]
 
[[Kategorie:Swing]]
 
[[Kategorie:Java-Codeschnipsel]]
 
[[Kategorie:Java-Codeschnipsel]]

Aktuelle Version vom 8. April 2018, 11:47 Uhr

Die Klasse und ein Anwendungsbeispiel

JComponentBounds

Die Klasse JComponentBounds erlaubt es, JComponents zu verschieben und in der Grösse zu verändern. Die Klasse "PictureDemo" weiter unten zeigt ein Anwendungsbeispiel.

/*
 * JComponentBounds.java
 *
 * This class aims at making any JComponent resizable
 * simply by putting it inside a JInternalFrame.
 * The wrapped JComponent is made moveable
 * by means of a MouseInputAdapter.
 * The class includes a "snap to grid" option.
 *
 * author: Andre Uhres
 * update April 15, 2010 at 6:00
 * update December 21, 2010 at 15:00
 */

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.plaf.basic.*;

public class JComponentBounds extends JInternalFrame {

    public static final int FOCUS_GAINED = 97531;
    private JComponent componentsContainer;
    private boolean focused;
    private JComponent comp;
    private static final Color TRANSPARENT = new Color(0.0f, 0.0f, 0.0f, 0.0f);
    public static final int MARGIN = 5;
    private KeyAdapter kl;
    private MouseInputAdapter ml;
    private ComponentAdapter cl;
    private boolean snapToGrid;

    public JComponentBounds() {
        this(new JLabel("jComponentBounds"));
    }

    public JComponentBounds(final JComponent comp) {
        this(comp, 0, 0, false);
    }

    public JComponentBounds(final JComponent comp, final boolean focused) {
        this(comp, 0, 0, focused);
    }

    public JComponentBounds(final JComponent comp, final int x, final int y,
            final boolean focused) {
        super();
        this.comp = comp;
        this.focused = focused;
        setBounds(x, y,
                comp.getPreferredSize().width, comp.getPreferredSize().height);
        setResizable(true);
        setVisible(true);
        setOpaque(false);
        setBackground(TRANSPARENT);
        ((BasicInternalFrameUI) getUI()).setNorthPane(null);
        setBorder(BorderFactory.createEmptyBorder(
                MARGIN, MARGIN, MARGIN, MARGIN));
        comp.setFocusable(true);
        add(comp);
        ml = new MouseInputAdapter() {

            private Point p1;

            @Override
            public void mousePressed(final MouseEvent e) {
                p1 = e.getPoint();
                componentsContainer = (JComponent) getParent();
                e.setSource(JComponentBounds.this);
                //componentsContainer might use the mousePressed event
                //to take hidden components to the top:
                componentsContainer.dispatchEvent(e);
            }

            @Override
            public void mouseReleased(final MouseEvent e) {
                componentsContainer = (JComponent) getParent();
                e.setSource(JComponentBounds.this);
                //componentsContainer might use the mouseReleased event
                //to take hidden components to the top:
                componentsContainer.dispatchEvent(e);
            }

            @Override
            public void mouseDragged(final MouseEvent e) {
                if (!JComponentBounds.this.isFocused()) {
                    return;
                }
                //drag operation:
                Point p2 = e.getPoint();
                Point loc = getLocation();
                loc.translate(p2.x - p1.x, p2.y - p1.y);
                setLocation(loc);
                componentsContainer = (JComponent) getParent();
                //auto scroll:
                componentsContainer.scrollRectToVisible(
                        new Rectangle(loc.x, loc.y, getWidth(), getHeight()));
                //componentsContainer might use the focusGained event
                //to keep the dragged component in front:
                FocusListener[] focusListeners = componentsContainer.getFocusListeners();
                if (focusListeners != null && focusListeners.length > 0) {
                    FocusListener fl = focusListeners[0];
                    fl.focusGained(
                            new FocusEvent(JComponentBounds.this,
                            JComponentBounds.FOCUS_GAINED));
                }
            }
        };
        kl = new KeyAdapter() {

            @Override
            public void keyPressed(final KeyEvent e) {
                componentsContainer = (JComponent) getParent();
                //componentsContainer might use the keyPressed event
                //to delete the component:
                componentsContainer.dispatchEvent(e);
            }
        };
        cl = new ComponentAdapter() {

            @Override
            public void componentResized(final ComponentEvent e) {
                ((JComponent) e.getSource()).revalidate();
                componentsContainer = (JComponent) getParent();
                //componentsContainer might use the componentResized event
                //to transfer focus to the resizing component:
                if(componentsContainer != null){
                    componentsContainer.dispatchEvent(e);
                }
            }

            @Override
            public void componentMoved(final ComponentEvent e) {
                ((JComponent) e.getSource()).revalidate();
            }
        };
        addAllListeners();
    }

    /* Move and resize this component.
     * This method has been overridden to implement the "snap to grid" option.
     */
    @Override
    public void reshape(final int x, final int y,
            final int width, final int height) {
        if (snapToGrid) {
            //convert all parameters to multiples of MARGIN:
            int gridx = x / MARGIN * MARGIN;
            int gridy = y / MARGIN * MARGIN;
            int gridwidth = width / MARGIN * MARGIN;
            int gridheight = height / MARGIN * MARGIN;
            //if not horizontally on the grid, round up the grid width:
            if (gridx != x && gridwidth != width) {
                gridwidth += MARGIN;
            }
            //if not vertically on the grid, round up the grid height:
            if (gridy != y && gridheight != height) {
                gridheight += MARGIN;
            }
            //snap to the grid:
            super.reshape(gridx, gridy, gridwidth, gridheight);
        } else {
            super.reshape(x, y, width, height);
        }
    }

    public boolean isSnapToGrid() {
        return snapToGrid;
    }

    public void setSnapToGrid(final boolean snapToGrid) {
        this.snapToGrid = snapToGrid;
    }

    private void addAllListeners() {
        comp.addMouseListener(ml);
        comp.addMouseMotionListener(ml);
        comp.addKeyListener(kl);
        addComponentListener(cl);
    }

    public void removeAllListeners() {
        comp.removeMouseListener(ml);
        comp.removeMouseMotionListener(ml);
        comp.removeKeyListener(kl);
        removeComponentListener(cl);
    }

    public void setComponent(final JComponent comp) {
        //remove old component:
        removeAllListeners();
        remove(this.comp);
        //add new component:
        this.comp = comp;
        comp.setFocusable(true);
        add(comp);
        addAllListeners();
    }

    public void setFocused(final boolean focused) {
        this.focused = focused;
        repaint();
    }

    public boolean isFocused() {
        return focused;
    }

    public Rectangle getComponentRect() {
        Insets insets = getInsets();
        Rectangle rect = getBounds();
        rect.x += insets.left;
        rect.y += insets.top;
        rect.width -= (insets.right + insets.left);
        rect.height -= (insets.top + insets.bottom);
        return rect;
    }
}

PictureDemo

Die Klasse "PictureDemo" ermöglicht das Laden eines Bildes durch einen Klick auf das Fenster.

PictureDemo1.jpg

Das geladene Bild kann man anschließend in der Größe und Position verändern.

PictureDemo.jpg

/*
 * PictureDemo.java
 */

import container.JComponentBounds;
import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import java.io.*;
import java.util.*;
import javax.imageio.*;
import javax.swing.*;


//Model
class PictureStorage extends Observable {

    private BufferedImage data;

    public PictureStorage() {
    }

    public void loadPicture(final String filename) {
        System.out.println("loading " + filename);
        SwingWorker<BufferedImage, Void> loadPicture = new SwingWorker<BufferedImage, Void>() {

            @Override
            protected BufferedImage doInBackground() throws Exception {
                try {
                    data = ImageIO.read(new File(filename));
                } catch (IOException ex) {
                    System.out.println(ex);
                }
                return data;
            }

            @Override
            protected void done() {
                setChanged();
                notifyObservers(data); //sends change notification to the view
            }
        };
        loadPicture.execute();
    }
}

//View
class GUI extends JFrame implements Observer {

    private PictureDemo demo;
    private JComponent picture;
    private JComponentBounds pictureBounds;
    private GUI frame;
    private Image image;

    public GUI(final PictureDemo demo) {
        super();
        this.demo = demo;
        this.init();
    }

    private void init() {
        frame = this;
        setTitle("Demo: click to load picture");
        setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        setSize(800, 600);
        setLocationRelativeTo(null);
        addMouseListener(new MouseAdapter() {

            @Override
            public void mousePressed(final MouseEvent e) {
                FileDialog fileDialog = new FileDialog(frame);
                fileDialog.setVisible(true);
                String dir = fileDialog.getDirectory();
                String file = fileDialog.getFile();
                if (dir != null && file != null) {
                    demo.loadPicture(dir + file);
                }
            }
        });
        //configure picture:
        setLayout(null);
        picture = new JLabel() {

            @Override
            protected void paintComponent(final Graphics g) {
                super.paintComponent(g);
//                g.drawImage(image, 0, 0, this);
                g.drawImage(image, 0, 0, pictureBounds.getWidth(), pictureBounds.getHeight(), this);
            }
        };
        picture.setPreferredSize(new Dimension(100, 100));
        pictureBounds = new JComponentBounds(picture, 10, 10, true);
        pictureBounds.setMinimumSize(new Dimension(40, 20));
//        pictureBounds.setSnapToGrid(true);
        add(pictureBounds);
    }

    public void update(final Observable observable, final Object arg) {
        image = (Image) arg;
        if (image == null) {
            JOptionPane.showMessageDialog(this, "Picture not found");
        } else {
            pictureBounds.setSize(image.getWidth(null), image.getHeight(null));
            picture.setOpaque(true);
            picture.setBackground(Color.LIGHT_GRAY);
            picture.repaint();
        }
    }
}

//Controller
public class PictureDemo {

    private PictureStorage model;

    public PictureDemo() {
        model = new PictureStorage();
        GUI gui = new GUI(this);
        model.addObserver(gui);
        gui.setVisible(true);
    }

    public void loadPicture(final String file) {
        model.loadPicture(file);
    }

    public static void main(final String[] args) {
        Runnable gui = new Runnable() {

            @Override
            public void run() {
                PictureDemo pictureDemo = new PictureDemo();
            }
        };
        //GUI must start on EventDispatchThread:
        SwingUtilities.invokeLater(gui);
    }
}