package com.robonobo.gui.frames; import furbelow.AbstractComponentDecorator; import info.clearthought.layout.TableLayout; import java.awt.*; import java.awt.event.*; import java.util.LinkedList; import java.util.concurrent.locks.ReentrantLock; import javax.swing.*; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import com.robonobo.common.exceptions.SeekInnerCalmException; import com.robonobo.gui.RoboColor; import com.robonobo.gui.sheets.Sheet; @SuppressWarnings("serial") public class SheetableFrame extends JFrame { private Dimmer dimmer; private JPanel glass; protected LinkedList<Sheet> sheetStack = new LinkedList<Sheet>(); Log log = LogFactory.getLog(getClass()); protected ReentrantLock sheetLock = new ReentrantLock(); // One at a time, fellas public SheetableFrame() { glass = (JPanel) getGlassPane(); } public void showSheet(Sheet newSheet) { if (newSheet == null) throw new SeekInnerCalmException(); sheetLock.lock(); try { // If we already have a sheet showing, hide it if (sheetStack.size() > 0) { sheetStack.getFirst().hideSheet(); repaint(); } else dimmer = new Dimmer(); sheetStack.addFirst(newSheet); addSheetToGlass(newSheet); } finally { sheetLock.unlock(); } newSheet.onShow(); } private void addSheetToGlass(Sheet s) { SheetContainer sc = new SheetContainer(s); glass.removeAll(); double[][] cellSizen = { { TableLayout.FILL, sc.getPreferredSize().width, TableLayout.FILL }, { sc.getPreferredSize().height, TableLayout.FILL } }; glass.setLayout(new TableLayout(cellSizen)); glass.add(sc, "1,0"); glass.setVisible(true); getRootPane().setDefaultButton(s.defaultButton()); } public void discardTopSheet() { Sheet oldSheet = null; sheetLock.lock(); try { if (sheetStack.size() == 0) return; Sheet showingSheet = sheetStack.removeFirst(); showingSheet.hideSheet(); if (sheetStack.size() == 0) { glass.setVisible(false); if (dimmer != null) { dimmer.dispose(); dimmer = null; } getRootPane().setDefaultButton(null); } else { // There was a sheet there before this one - show it now repaint(); oldSheet = sheetStack.getFirst(); oldSheet.setVisible(true); addSheetToGlass(oldSheet); } } finally { sheetLock.unlock(); } if(oldSheet != null) oldSheet.onShow(); } public boolean isShowingSheet() { sheetLock.lock(); try { return (sheetStack.size() > 0); } finally { sheetLock.unlock(); } } protected Sheet getTopSheet() { sheetLock.lock(); try { if(sheetStack.size() == 0) return null; return sheetStack.getFirst(); } finally { sheetLock.unlock(); } } class SheetContainer extends JPanel { public SheetContainer(JComponent sheet) { // Make a 1px grey border and a 5px white background around the sheet double[][] cellSizen = { { 3, sheet.getPreferredSize().width, 5 }, { 2, sheet.getPreferredSize().height } }; setLayout(new TableLayout(cellSizen)); add(sheet, "1,1"); Dimension sz = new Dimension(sheet.getPreferredSize().width + 8, sheet.getPreferredSize().height + 7); setPreferredSize(sz); setOpaque(true); setBackground(Color.WHITE); setBorder(BorderFactory.createLineBorder(RoboColor.LIGHT_GRAY)); } } class Dimmer extends AbstractComponentDecorator implements KeyEventDispatcher { public Dimmer() { super(SheetableFrame.this.getLayeredPane()); KeyboardFocusManager.getCurrentKeyboardFocusManager().addKeyEventDispatcher(this); getPainter().addMouseListener(new MouseAdapter() { }); getPainter().addMouseMotionListener(new MouseMotionAdapter() { }); } @Override public void paint(Graphics g) { Color bg = getComponent().getBackground(); Color c = new Color(bg.getRed(), bg.getGreen(), bg.getBlue(), 128); Rectangle r = getDecorationBounds(); g = g.create(); g.setColor(c); g.fillRect(r.x, r.y, r.width, r.height); g.dispose(); } @Override public void dispose() { super.dispose(); KeyboardFocusManager.getCurrentKeyboardFocusManager().removeKeyEventDispatcher(this); } @Override public boolean dispatchKeyEvent(KeyEvent e) { return SwingUtilities.isDescendingFrom(e.getComponent(), getComponent()); } } }