/* * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ package org.flexdock.dockbar.layout; import java.awt.Dimension; import java.awt.Insets; import java.awt.Rectangle; import java.util.HashSet; import java.util.Iterator; import java.util.Set; import javax.swing.JComponent; import javax.swing.border.Border; import org.flexdock.dockbar.Dockbar; import org.flexdock.dockbar.DockbarManager; import org.flexdock.dockbar.ViewPane; import org.flexdock.docking.Dockable; import org.flexdock.docking.props.DockablePropertySet; import org.flexdock.docking.state.MinimizationManager; import org.flexdock.util.Utilities; /** * @author Christopher Butler */ public class DockbarLayout { public static final int MINIMUM_VIEW_SIZE = 20; private static final int[] EDGES = { MinimizationManager.LEFT, MinimizationManager.RIGHT, MinimizationManager.BOTTOM }; private DockbarManager manager; private JComponent leftEdgeGuide; private JComponent rightEdgeGuide; private JComponent bottomEdgeGuide; public DockbarLayout(DockbarManager mgr) { manager = mgr; } public void layout() { // makes sure our current insets are up to date before trying to layout // the dockbars. otherwise, they will be layed out with improper bounds. updateInsets(); Rectangle rect = DockbarLayoutManager.getManager().getLayoutArea(manager); int rightX = rect.x + rect.width; int bottomY = rect.y + rect.height; Dockbar leftBar = manager.getLeftBar(); Dockbar rightBar = manager.getRightBar(); Dockbar bottomBar = manager.getBottomBar(); Dimension leftPref = leftBar.getPreferredSize(); Dimension rightPref = rightBar.getPreferredSize(); Dimension bottomPref = bottomBar.getPreferredSize(); // set the dockbar bounds leftBar.setBounds(rect.x, rect.y, leftPref.width, rect.height-bottomPref.height); rightBar.setBounds(rightX-rightPref.width, rect.y, rightPref.width, rect.height-bottomPref.height); // use complete window width for proper statusbar support bottomBar.setBounds(rect.x, bottomY-bottomPref.height, rect.width, bottomPref.height); layoutViewpane(); } public int getDesiredViewpaneSize() { Dockable dockable = manager.getActiveDockable(); if(dockable==null) { return 0; } Rectangle rect = DockbarLayoutManager.getManager().getViewArea(manager, dockable); DockablePropertySet props = dockable.getDockingProperties(); // determine what percentage of the viewable area we want the viewpane to take up float viewSize = props.getPreviewSize().floatValue(); int edge = manager.getActiveEdge(); if(edge==MinimizationManager.LEFT || edge==MinimizationManager.RIGHT) { return (int)(rect.width*viewSize); } return (int)(rect.height*viewSize); } protected void layoutViewpane() { ViewPane viewPane = manager.getViewPane(); Dockable dockable = manager.getActiveDockable(); if(dockable==null) { viewPane.setBounds(0, 0, 0, 0); return; } int edge = manager.getActiveEdge(); int viewpaneSize = viewPane.getPrefSize(); if(viewpaneSize==ViewPane.UNSPECIFIED_PREFERRED_SIZE) { viewpaneSize = getDesiredViewpaneSize(); } Rectangle rect = DockbarLayoutManager.getManager().getViewArea(manager, dockable); if(edge==MinimizationManager.LEFT || edge==MinimizationManager.RIGHT) { if(edge==MinimizationManager.RIGHT) { rect.x = rect.x + rect.width - viewpaneSize; } rect.width = viewpaneSize; } else { if(edge==MinimizationManager.BOTTOM) { rect.y = rect.y + rect.height - viewpaneSize; } rect.height = viewpaneSize; } viewPane.setBounds(rect); } private void updateInsets() { Insets emptyInsets = getEmptyInsets(); boolean changed = resetGuideBorders(); HashSet borderSet = new HashSet(3); add(borderSet, getInsetBorder(MinimizationManager.LEFT)); add(borderSet, getInsetBorder(MinimizationManager.RIGHT)); add(borderSet, getInsetBorder(MinimizationManager.BOTTOM)); HashSet guideSet = new HashSet(3); add(guideSet, getCurrentEdgeGuide(MinimizationManager.LEFT)); add(guideSet, getCurrentEdgeGuide(MinimizationManager.RIGHT)); add(guideSet, getCurrentEdgeGuide(MinimizationManager.BOTTOM)); for(Iterator it=borderSet.iterator(); it.hasNext();) { InsetBorder border = (InsetBorder)it.next(); changed = border.setEmptyInsets(emptyInsets) || changed; } if(changed) { for(Iterator it=guideSet.iterator(); it.hasNext();) { JComponent guide = (JComponent)it.next(); guide.revalidate(); } } } private boolean resetGuideBorders() { boolean changed = resetGuide(MinimizationManager.LEFT); changed = resetGuide(MinimizationManager.RIGHT) || changed ; changed = resetGuide(MinimizationManager.BOTTOM) || changed ; toggleInsetBorder(MinimizationManager.LEFT); toggleInsetBorder(MinimizationManager.RIGHT); toggleInsetBorder(MinimizationManager.BOTTOM); return changed; } private boolean resetGuide(int edge) { JComponent currGuide = getCurrentEdgeGuide(edge); JComponent newGuide = queryEdgeGuide(edge); boolean changed = false; if(Utilities.isChanged(currGuide, newGuide)) { changed = true; // if the edge-guide has changed, then we're going to try to remove the old // InsetBorder from the previous edge-guide Border b = currGuide==null? null: currGuide.getBorder(); InsetBorder fakeBorder = b instanceof InsetBorder? (InsetBorder)b: null; // check to see if the previous edge-guide already had an InsetBorder if(fakeBorder!=null) { // replace the InsetBorder with the old edge-guide's real border Border realBorder = fakeBorder.getWrappedBorder(); currGuide.setBorder(realBorder); fakeBorder.toggleEdge(edge, false); } } // if there is no new edge-guide, then we can't set an InsetBorder for it if(newGuide==null) { return setCurrentEdgeGuide(edge, newGuide) || changed; } Border border = newGuide.getBorder(); // if the new edge-guide doesn't have an InsetBorder, then install one if(!(border instanceof InsetBorder)) { changed = true; InsetBorder insetBorder = InsetBorder.createBorder(border, true, new Insets(-1, -1, -1, -1)); newGuide.setBorder(insetBorder); } // make sure our edge tracking on the InsetBorder is cleared out. // we will rebuild it later on InsetBorder insetBorder = (InsetBorder)newGuide.getBorder(); insetBorder.clearEdges(); // update the edge-guide reference return setCurrentEdgeGuide(edge, newGuide) || changed; } private void toggleInsetBorder(int edge) { InsetBorder border = getInsetBorder(edge); if(border!=null) { border.toggleEdge(edge, true); } } private InsetBorder getInsetBorder(int edge) { JComponent comp = getCurrentEdgeGuide(edge); Border border = comp==null? null: comp.getBorder(); return border instanceof InsetBorder? (InsetBorder)border: null; } private Insets getEmptyInsets() { return new Insets(0, getLeftInset(), getBottomInset(), getRightInset()); } private int getLeftInset() { return getDockbarInset(manager.getLeftBar()); } private int getRightInset() { return getDockbarInset(manager.getRightBar()); } private int getBottomInset() { return getDockbarInset(manager.getBottomBar()); } private int getDockbarInset(Dockbar dockbar) { boolean visible = dockbar.isVisible(); if(!visible) { return 0; } Dimension dim = dockbar.getPreferredSize(); if(dockbar==manager.getLeftBar() || dockbar==manager.getRightBar()) { return dim.width; } return dim.height; } private JComponent queryEdgeGuide(int constraint) { return DockbarLayoutManager.getManager().getEdgeGuide(manager, constraint); } private JComponent getCurrentEdgeGuide(int constraint) { switch(constraint) { case MinimizationManager.LEFT: return leftEdgeGuide; case MinimizationManager.RIGHT: return rightEdgeGuide; case MinimizationManager.BOTTOM: return bottomEdgeGuide; } return null; } private boolean setCurrentEdgeGuide(int constraint, JComponent comp) { boolean changed = getCurrentEdgeGuide(constraint)==comp; switch(constraint) { case MinimizationManager.LEFT: leftEdgeGuide = comp; break; case MinimizationManager.RIGHT: rightEdgeGuide = comp; break; case MinimizationManager.BOTTOM: bottomEdgeGuide = comp; break; } return changed; } private void add(Set set, Object obj) { if(obj!=null) { set.add(obj); } } }