import java.awt.BorderLayout; import java.awt.Color; import java.awt.Dimension; import java.awt.Graphics; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.awt.event.MouseMotionListener; import java.io.PrintWriter; import java.io.StringWriter; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTextArea; public class Display extends JPanel implements MouseListener, MouseMotionListener, KeyListener { /** Shows the windowing system application. */ public static void main(String[] args) { JTextArea log = new JTextArea(10, 30); log.setEditable(false); JScrollPane logPane = new JScrollPane(log); Display display = new Display(log); JFrame frame = new JFrame("Windows"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.getContentPane().add(display); frame.getContentPane().add(logPane, BorderLayout.SOUTH); frame.pack(); frame.setVisible(true); } /** The minimum size of a window. */ private static final int MIN_SIZE = 25; /** The threshold mouse drag distance to distinguish between * a click on a window (to bring it to the front) or a drag * on a window (to move it). */ private static final int MIN_MOVEMENT = 10; /** The colors to use for successively created windows. */ private static final Color[] colors = { Color.RED, Color.GREEN, Color.BLUE, Color.YELLOW, Color.CYAN, Color.MAGENTA, Color.ORANGE, new Color(64, 64, 0), Color.PINK, new Color(128, 128, 255), new Color(128, 0, 0), new Color(0, 128, 0), new Color(0, 0, 128), Color.DARK_GRAY, Color.WHITE, Color.BLACK }; /** The names to use for successively created windows. */ private static final String[] names = { "Red", "Green", "Blue", "Yellow", "Cyan", "Magenta", "Orange", "Brown", "Pink", "Lt Blue", "Dk Red", "Dk Green", "Dk Blue", "Dk Gray", "White", "Black" }; /** The log area where to display messages about actions. */ private JTextArea log; /** The window manager. */ private Manager manager; /** The number of windows created thus far. */ private int windowsCreated; /** The last window manipulated. */ private Window current; /** Whether the last mouse press indicated that we are * lifting the current window. */ private boolean isLifting; /** Whether the last mouse press indicated that we are * resizing the current window. */ private boolean isResizing; /** Whether the user has yet dragged far enough to indicate * that we are moving the current window. */ private boolean isMoving; /** The x-coordinate where the mouse was pressed. */ private int startX; /** The y-coordinate where the mouse was pressed. */ private int startY; /** Whether to draw a ghost indicating the action when the * mouse is released. */ private boolean ghostValid; /** The x-coordinate of the ghost's left edge. */ private int ghostX; /** The y-coordinate of the ghost's top edge. */ private int ghostY; /** The width of the ghost, in pixels. */ private int ghostWidth; /** The height of the ghost, in pixels. */ private int ghostHeight; public Display(JTextArea myLog) { log = myLog; manager = new Manager(); windowsCreated = 0; current = null; ghostValid = false; setBackground(Color.LIGHT_GRAY); addMouseListener(this); addMouseMotionListener(this); log.addKeyListener(this); addKeyListener(this); setPreferredSize(new Dimension(320, 240)); } /** Paints the display area. */ public void paintComponent(Graphics g) { super.paintComponent(g); // this will paint the background color // paint windows try { manager.paintAll(g, current); } catch(Throwable t) { appendError("Manager.paintAll", t); } // paint ghost if(ghostValid) { g.setColor(Color.GRAY); g.drawRect(ghostX, ghostY, ghostWidth, ghostHeight); } } /** Does nothing; required for the MouseListener interface. */ public void mouseClicked(MouseEvent event) { } /** Starts a drag by determining which window the user is * manipulating (if any). */ public void mousePressed(MouseEvent event) { startX = event.getX(); startY = event.getY(); log.append("Manager.find " + startX + "," + startY + ": "); try { Window w = manager.find(startX, startY); isLifting = w != null && w == current; current = w; log.append(current + "\n"); } catch(Throwable t) { current = null; log.append(" aborted due to exception\n"); appendError("Manager.find", t); } if(current == null) { ghostX = startX; ghostY = startY; ghostWidth = MIN_SIZE; ghostHeight = MIN_SIZE; } else { ghostX = current.getX(); ghostY = current.getY(); ghostWidth = current.getWidth(); ghostHeight = current.getHeight(); isResizing = current.isInResizeRegion(startX, startY); isMoving = false; if(isResizing) isLifting = false; } ghostValid = true; repaint(); } /** Completes a mouse drag by performing the requested action. */ public void mouseReleased(MouseEvent event) { resizeRectangle(event); // update the ghost for the final location if(current == null) { int index = windowsCreated % colors.length; windowsCreated++; current = new Window(names[index], colors[index], ghostX, ghostY, ghostWidth, ghostHeight); log.append("Manager.add " + current + "\n"); try { manager.add(current); } catch(Throwable t) { appendError("Manager.add", t); } } else if(isResizing) { log.append("Window.setSize " + current + "\n"); current.setSize(ghostWidth, ghostHeight); } else if(isMoving) { log.append("Window.setLocation " + current + "\n"); current.setLocation(ghostX, ghostY); } else if(isLifting) { log.append("Manager.toFront " + current + "\n"); try { manager.toFront(current); } catch(Throwable t) { appendError("Manager.toFront", t); } } ghostValid = false; repaint(); } /** Does nothing; required for the MouseListener interface. */ public void mouseEntered(MouseEvent event) { } /** Does nothing; required for the MouseListener interface. */ public void mouseExited(MouseEvent event) { } /** Updates the ghost to reflect the current mouse location. */ public void mouseDragged(MouseEvent event) { resizeRectangle(event); if(isLifting) System.err.println(current + " " + isLifting); isLifting = false; repaint(); } private void resizeRectangle(MouseEvent event) { int x = Math.max(0, Math.min(getWidth(), event.getX())); int y = Math.max(0, Math.min(getHeight(), event.getY())); if(current == null) { ghostX = Math.min(x, startX); ghostY = Math.min(y, startY); ghostWidth = Math.max(MIN_SIZE, Math.abs(x - startX)); ghostHeight = Math.max(MIN_SIZE, Math.abs(y - startY)); } else if(isResizing) { ghostWidth = Math.max(MIN_SIZE, current.getWidth() + x - startX); ghostHeight = Math.max(MIN_SIZE, current.getHeight() + y - startY); } else { int newX = Math.max(0, current.getX() + (x - startX)); int newY = Math.max(0, current.getY() + (y - startY)); int moveDist = Math.abs(newX - current.getX()) + Math.abs(newY - current.getY()); if(moveDist >= MIN_MOVEMENT) isMoving = true; if(isMoving) { ghostX = newX; ghostY = newY; } } } /** Does nothing; required for the MouseMotionListener interface. */ public void mouseMoved(MouseEvent event) { } /** Processes a key typed by the user. */ public void keyTyped(KeyEvent event) { char c = event.getKeyChar(); if(c == '\b' || c == '\u007f' || c == '\u0008') { if(current != null) { log.append("Manager.remove " + current + "\n"); try { manager.remove(current); } catch(Throwable t) { appendError("Manager.remove", t); } current = null; repaint(); } } } /** Does nothing; required for the KeyListener interface. */ public void keyPressed(KeyEvent e) { } /** Does nothing; required for the KeyListener interface. */ public void keyReleased(KeyEvent e) { } private void appendError(String method, Throwable thrown) { StringWriter sOut = new StringWriter(); PrintWriter pOut = new PrintWriter(sOut); thrown.printStackTrace(pOut); log.append("Exception in " + method + ": " + sOut.toString()); } }