package featurecat.lizzie.gui; import featurecat.lizzie.Lizzie; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Component; import java.awt.Container; import java.awt.Dialog; import java.awt.Dimension; import java.awt.Frame; import java.awt.Graphics; import java.awt.IllegalComponentStateException; import java.awt.Insets; import java.awt.LayoutManager; import java.awt.Point; import java.awt.Window; import java.awt.event.ContainerEvent; import java.awt.event.ContainerListener; import java.awt.event.FocusEvent; import java.awt.event.FocusListener; import java.awt.event.KeyEvent; import java.awt.event.MouseEvent; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.awt.event.WindowListener; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import javax.swing.InputMap; import javax.swing.JComponent; import javax.swing.JDialog; import javax.swing.JRootPane; import javax.swing.KeyStroke; import javax.swing.LookAndFeel; import javax.swing.RootPaneContainer; import javax.swing.SwingConstants; import javax.swing.SwingUtilities; import javax.swing.UIManager; import javax.swing.event.MouseInputListener; import javax.swing.plaf.ComponentUI; public class BasicLizziePaneUI extends LizziePaneUI implements SwingConstants { protected LizziePane lizziePane; private boolean floating; private int floatingX; private int floatingY; private RootPaneContainer floatingLizziePane; protected DragWindow dragWindow; private Container dockingSource; protected int focusedCompIndex = -1; private Dimension originSize = null; protected MouseInputListener dockingListener; protected PropertyChangeListener propertyListener; protected ContainerListener lizziePaneContListener; protected FocusListener lizziePaneFocusListener; private Handler handler; protected String constraintBeforeFloating; private static String FOCUSED_COMP_INDEX = "LizziePane.focusedCompIndex"; public static ComponentUI createUI(JComponent c) { return new BasicLizziePaneUI(); } public void installUI(JComponent c) { lizziePane = (LizziePane) c; // Set defaults installDefaults(); installComponents(); // Default disabled drag // installListeners(); // installKeyboardActions(); // Initialize instance vars floating = false; floatingX = floatingY = 0; floatingLizziePane = null; LookAndFeel.installProperty(c, "opaque", Boolean.TRUE); if (c.getClientProperty(FOCUSED_COMP_INDEX) != null) { focusedCompIndex = ((Integer) (c.getClientProperty(FOCUSED_COMP_INDEX))).intValue(); } } public void uninstallUI(JComponent c) { // Clear defaults uninstallDefaults(); uninstallComponents(); uninstallListeners(); // uninstallKeyboardActions(); // Clear instance vars if (isFloating()) setFloating(false, null); floatingLizziePane = null; dragWindow = null; dockingSource = null; c.putClientProperty(FOCUSED_COMP_INDEX, Integer.valueOf(focusedCompIndex)); } protected void installDefaults() { LookAndFeel.installBorder(lizziePane, "LizziePane.border"); LookAndFeel.installColorsAndFont( lizziePane, "LizziePane.background", "LizziePane.foreground", "LizziePane.font"); } protected void uninstallDefaults() { LookAndFeel.uninstallBorder(lizziePane); } protected void installComponents() {} protected void uninstallComponents() {} public void installListeners() { dockingListener = createDockingListener(); if (dockingListener != null) { lizziePane.addMouseMotionListener(dockingListener); lizziePane.addMouseListener(dockingListener); } propertyListener = createPropertyListener(); // added in setFloating if (propertyListener != null) { lizziePane.addPropertyChangeListener(propertyListener); } lizziePaneContListener = createLizziePaneContListener(); if (lizziePaneContListener != null) { lizziePane.addContainerListener(lizziePaneContListener); } lizziePaneFocusListener = createLizziePaneFocusListener(); if (lizziePaneFocusListener != null) { // Put focus listener on all components in lizziePane Component[] components = lizziePane.getComponents(); for (Component component : components) { component.addFocusListener(lizziePaneFocusListener); } } } public void uninstallListeners() { if (dockingListener != null) { lizziePane.removeMouseMotionListener(dockingListener); lizziePane.removeMouseListener(dockingListener); dockingListener = null; } if (propertyListener != null) { lizziePane.removePropertyChangeListener(propertyListener); propertyListener = null; // removed in setFloating } if (lizziePaneContListener != null) { lizziePane.removeContainerListener(lizziePaneContListener); lizziePaneContListener = null; } if (lizziePaneFocusListener != null) { // Remove focus listener from all components in lizziePane Component[] components = lizziePane.getComponents(); for (Component component : components) { component.removeFocusListener(lizziePaneFocusListener); } lizziePaneFocusListener = null; } handler = null; } protected void installKeyboardActions() { InputMap km = getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT); SwingUtilities.replaceUIInputMap(lizziePane, JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, km); } InputMap getInputMap(int condition) { if (condition == JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT) { return (InputMap) UIManager.get("LizziePane.ancestorInputMap", lizziePane.getLocale()); } return null; } protected void uninstallKeyboardActions() { SwingUtilities.replaceUIActionMap(lizziePane, null); SwingUtilities.replaceUIInputMap( lizziePane, JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, null); } /** * Creates a window which contains the lizziePane after it has been dragged out from its container * * @return a <code>RootPaneContainer</code> object, containing the lizziePane. */ protected RootPaneContainer createFloatingWindow(LizziePane lizziePane) { class LizziePaneDialog extends JDialog { public LizziePaneDialog(Frame owner, String title, boolean modal) { super(owner, title, modal); } public LizziePaneDialog(Dialog owner, String title, boolean modal) { super(owner, title, modal); } protected JRootPane createRootPane() { JRootPane rootPane = new JRootPane(); rootPane.setOpaque(false); rootPane.registerKeyboardAction( e -> { if (Lizzie.frame.isDesignMode()) { Lizzie.frame.toggleDesignMode(); } }, KeyStroke.getKeyStroke(KeyEvent.VK_W, KeyEvent.ALT_DOWN_MASK), JComponent.WHEN_IN_FOCUSED_WINDOW); return rootPane; } } JDialog dialog; Window window = SwingUtilities.getWindowAncestor(lizziePane); if (window instanceof Frame) { dialog = new LizziePaneDialog((Frame) window, lizziePane.getName(), false); } else if (window instanceof Dialog) { dialog = new LizziePaneDialog((Dialog) window, lizziePane.getName(), false); } else { dialog = new LizziePaneDialog((Frame) null, lizziePane.getName(), false); } dialog.getRootPane().setName("LizziePane.FloatingWindow"); dialog.setTitle(lizziePane.getName()); dialog.setResizable(true); // dialog.setSize(lizziePane.getSize()); WindowListener wl = createFrameListener(); dialog.addWindowListener(wl); return dialog; } protected DragWindow createDragWindow(LizziePane lizziePane) { Window frame = null; if (lizziePane != null) { Container p; for (p = lizziePane.getParent(); p != null && !(p instanceof Window); p = p.getParent()) ; if (p != null && p instanceof Window) frame = (Window) p; } if (floatingLizziePane == null) { floatingLizziePane = createFloatingWindow(lizziePane); } if (floatingLizziePane instanceof Window) frame = (Window) floatingLizziePane; DragWindow dragWindow = new DragWindow(frame); return dragWindow; } public void setFloatingLocation(int x, int y) { floatingX = x; floatingY = y; } public boolean isFloating() { return floating; } public void setFloating(boolean b, Point p) { if (lizziePane.isFloatable()) { boolean visible = false; Window ancestor = SwingUtilities.getWindowAncestor(lizziePane); if (ancestor != null) { visible = ancestor.isVisible(); } if (dragWindow != null) dragWindow.setVisible(false); this.floating = b; if (floatingLizziePane == null) { floatingLizziePane = createFloatingWindow(lizziePane); } if (b == true) { if (dockingSource == null) { dockingSource = lizziePane.getParent(); dockingSource.remove(lizziePane); } constraintBeforeFloating = calculateConstraint(); if (propertyListener != null) UIManager.addPropertyChangeListener(propertyListener); floatingLizziePane.getContentPane().add(lizziePane, BorderLayout.CENTER); if (floatingLizziePane instanceof Window) { ((Window) floatingLizziePane).pack(); ((Window) floatingLizziePane).setLocation(floatingX, floatingY); Insets insets = ((Window) floatingLizziePane).getInsets(); Dimension d = new Dimension( originSize.width + insets.left + insets.right, originSize.height + insets.top + insets.bottom); ((Window) floatingLizziePane).setSize(d); if (visible) { ((Window) floatingLizziePane).setVisible(true); } else { ancestor.addWindowListener( new WindowAdapter() { public void windowOpened(WindowEvent e) { ((Window) floatingLizziePane).setVisible(true); } }); } } } else { if (floatingLizziePane == null) floatingLizziePane = createFloatingWindow(lizziePane); if (floatingLizziePane instanceof Window) ((Window) floatingLizziePane).setVisible(false); floatingLizziePane.getContentPane().remove(lizziePane); String constraint = getDockingConstraint(dockingSource, p); if (constraint != null) { if (dockingSource == null) dockingSource = lizziePane.getParent(); if (propertyListener != null) UIManager.removePropertyChangeListener(propertyListener); dockingSource.add(constraint, lizziePane); } } dockingSource.invalidate(); Container dockingSourceParent = dockingSource.getParent(); if (dockingSourceParent != null) dockingSourceParent.validate(); dockingSource.repaint(); } } public boolean canDock(Component c, Point p) { return (p != null && getDockingConstraint(c, p) != null); } private String calculateConstraint() { String constraint = null; LayoutManager lm = dockingSource.getLayout(); if (lm instanceof LizzieLayout) { constraint = (String) ((LizzieLayout) lm).getConstraints(lizziePane); } return (constraint != null) ? constraint : constraintBeforeFloating; } private String getDockingConstraint(Component c, Point p) { if (p == null) return constraintBeforeFloating; return null; } protected void dragTo(Point position, Point origin) { originSize = lizziePane.getSize(); if (lizziePane.isFloatable()) { try { if (dragWindow == null) dragWindow = createDragWindow(lizziePane); Point offset = dragWindow.getOffset(); if (offset == null) { Dimension size = lizziePane.getSize(); offset = new Point(size.width / 2, size.height / 2); dragWindow.setOffset(offset); } Point global = new Point(origin.x + position.x, origin.y + position.y); Point dragPoint = new Point(global.x - offset.x, global.y - offset.y); if (dockingSource == null) dockingSource = lizziePane.getParent(); constraintBeforeFloating = calculateConstraint(); dragWindow.setLocation(dragPoint.x, dragPoint.y); if (dragWindow.isVisible() == false) { Dimension size = lizziePane.getSize(); dragWindow.setSize(size.width, size.height); dragWindow.setVisible(true); } } catch (IllegalComponentStateException e) { } } } protected void floatAt(Point position, Point origin) { if (lizziePane.isFloatable()) { try { Point offset = dragWindow.getOffset(); if (offset == null) { offset = position; dragWindow.setOffset(offset); } Point global = new Point(origin.x + position.x, origin.y + position.y); setFloatingLocation(global.x - offset.x, global.y - offset.y); if (dockingSource != null) { Point dockingPosition = dockingSource.getLocationOnScreen(); Point comparisonPoint = new Point(global.x - dockingPosition.x, global.y - dockingPosition.y); if (canDock(dockingSource, comparisonPoint)) { setFloating(false, comparisonPoint); } else { setFloating(true, null); } } else { setFloating(true, null); } dragWindow.setOffset(null); } catch (IllegalComponentStateException e) { } } } public void toWindow(Point position, Dimension size) { if (lizziePane.isFloatable()) { try { originSize = size; if (dragWindow == null) dragWindow = createDragWindow(lizziePane); if (dockingSource == null) dockingSource = lizziePane.getParent(); constraintBeforeFloating = calculateConstraint(); setFloatingLocation(position.x, position.y); setFloating(true, null); } catch (IllegalComponentStateException e) { } } } private Handler getHandler() { if (handler == null) { handler = new Handler(); } return handler; } protected ContainerListener createLizziePaneContListener() { return getHandler(); } protected FocusListener createLizziePaneFocusListener() { return getHandler(); } protected PropertyChangeListener createPropertyListener() { return getHandler(); } protected MouseInputListener createDockingListener() { getHandler().lp = lizziePane; return getHandler(); } protected WindowListener createFrameListener() { return new FrameListener(); } /** * Paints the contents of the window used for dragging. * * @param g Graphics to paint to. * @throws NullPointerException is <code>g</code> is null */ protected void paintDragWindow(Graphics g) { g.setColor(dragWindow.getBackground()); int w = dragWindow.getWidth(); int h = dragWindow.getHeight(); g.fillRect(0, 0, w, h); g.setColor(dragWindow.getBorderColor()); g.drawRect(0, 0, w - 1, h - 1); } private class Handler implements ContainerListener, FocusListener, MouseInputListener, PropertyChangeListener { // // ContainerListener // public void componentAdded(ContainerEvent evt) { Component c = evt.getChild(); if (lizziePaneFocusListener != null) { c.addFocusListener(lizziePaneFocusListener); } } public void componentRemoved(ContainerEvent evt) { Component c = evt.getChild(); if (lizziePaneFocusListener != null) { c.removeFocusListener(lizziePaneFocusListener); } } public void focusGained(FocusEvent evt) { Component c = evt.getComponent(); focusedCompIndex = lizziePane.getComponentIndex(c); } public void focusLost(FocusEvent evt) {} LizziePane lp; boolean isDragging = false; Point origin = null; public void mousePressed(MouseEvent evt) { if (!lp.isEnabled()) { return; } isDragging = false; } public void mouseReleased(MouseEvent evt) { if (!lp.isEnabled()) { return; } if (isDragging) { Point position = evt.getPoint(); if (origin == null) origin = evt.getComponent().getLocationOnScreen(); floatAt(position, origin); } origin = null; isDragging = false; } public void mouseDragged(MouseEvent evt) { if (!lp.isEnabled()) { return; } isDragging = true; Point position = evt.getPoint(); if (origin == null) { origin = evt.getComponent().getLocationOnScreen(); } dragTo(position, origin); } public void mouseClicked(MouseEvent evt) {} public void mouseEntered(MouseEvent evt) {} public void mouseExited(MouseEvent evt) {} public void mouseMoved(MouseEvent evt) {} public void propertyChange(PropertyChangeEvent evt) { String propertyName = evt.getPropertyName(); if (propertyName == "lookAndFeel") { lizziePane.updateUI(); } } } protected class FrameListener extends WindowAdapter { public void windowClosing(WindowEvent w) { if (lizziePane.isFloatable()) { if (dragWindow != null) dragWindow.setVisible(false); floating = false; if (floatingLizziePane == null) floatingLizziePane = createFloatingWindow(lizziePane); if (floatingLizziePane instanceof Window) ((Window) floatingLizziePane).setVisible(false); floatingLizziePane.getContentPane().remove(lizziePane); String constraint = constraintBeforeFloating; if (dockingSource == null) dockingSource = lizziePane.getParent(); if (propertyListener != null) UIManager.removePropertyChangeListener(propertyListener); dockingSource.add(lizziePane, constraint); dockingSource.invalidate(); Container dockingSourceParent = dockingSource.getParent(); if (dockingSourceParent != null) dockingSourceParent.validate(); dockingSource.repaint(); } } } protected class LizziePaneContListener implements ContainerListener { public void componentAdded(ContainerEvent e) { getHandler().componentAdded(e); } public void componentRemoved(ContainerEvent e) { getHandler().componentRemoved(e); } } protected class LizziePaneFocusListener implements FocusListener { public void focusGained(FocusEvent e) { getHandler().focusGained(e); } public void focusLost(FocusEvent e) { getHandler().focusLost(e); } } protected class PropertyListener implements PropertyChangeListener { public void propertyChange(PropertyChangeEvent e) { getHandler().propertyChange(e); } } /** * This class should be treated as a "protected" inner class. Instantiate it only within * subclasses of LizziePaneUI. */ public class DockingListener implements MouseInputListener { protected LizziePane lizziePane; protected boolean isDragging = false; protected Point origin = null; public DockingListener(LizziePane t) { this.lizziePane = t; getHandler().lp = t; } public void mouseClicked(MouseEvent e) { getHandler().mouseClicked(e); } public void mousePressed(MouseEvent e) { getHandler().lp = lizziePane; getHandler().mousePressed(e); isDragging = getHandler().isDragging; } public void mouseReleased(MouseEvent e) { getHandler().lp = lizziePane; getHandler().isDragging = isDragging; getHandler().origin = origin; getHandler().mouseReleased(e); isDragging = getHandler().isDragging; origin = getHandler().origin; } public void mouseEntered(MouseEvent e) { getHandler().mouseEntered(e); } public void mouseExited(MouseEvent e) { getHandler().mouseExited(e); } public void mouseDragged(MouseEvent e) { getHandler().lp = lizziePane; getHandler().origin = origin; getHandler().mouseDragged(e); isDragging = getHandler().isDragging; origin = getHandler().origin; } public void mouseMoved(MouseEvent e) { getHandler().mouseMoved(e); } } protected class DragWindow extends Window { Color borderColor = Color.gray; Point offset; // offset of the mouse cursor inside the DragWindow DragWindow(Window w) { super(w); } public Point getOffset() { return offset; } public void setOffset(Point p) { this.offset = p; } public void setBorderColor(Color c) { if (this.borderColor == c) return; this.borderColor = c; repaint(); } public Color getBorderColor() { return this.borderColor; } public void paint(Graphics g) { paintDragWindow(g); // Paint the children super.paint(g); } public Insets getInsets() { return new Insets(1, 1, 1, 1); } } }