/* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright 2012 Neil C Smith. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3 only, as * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 3 for more details. * * You should have received a copy of the GNU General Public License version 3 * along with this work; if not, see http://www.gnu.org/licenses/ * * * Please visit http://neilcsmith.net if you need additional information or * have any questions. */ package net.neilcsmith.praxis.live.pxr.gui; import java.awt.Component; import java.awt.Rectangle; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.Set; import java.util.TreeSet; import java.util.logging.Level; import java.util.logging.Logger; import javax.swing.JComponent; import net.miginfocom.layout.CC; import net.miginfocom.layout.IDEUtil; import net.neilcsmith.praxis.core.ComponentAddress; import net.neilcsmith.praxis.gui.Keys; import net.neilcsmith.praxis.live.model.ComponentProxy; import net.neilcsmith.praxis.live.model.ContainerProxy; import net.neilcsmith.praxis.live.model.RootProxy; /** * * @author Neil C Smith <http://neilcsmith.net> */ class Utils { private final static Logger LOG = Logger.getLogger(Utils.class.getName()); private Utils() { } static void enableAll(JComponent component) { component.setEnabled(true); for (Component cmp : component.getComponents()) { if (cmp instanceof JComponent) { enableAll((JComponent) cmp); } } } static void disableAll(JComponent component) { component.setEnabled(false); for (Component cmp : component.getComponents()) { if (cmp instanceof JComponent) { disableAll((JComponent) cmp); } } } static ComponentProxy findComponentProxy(RootProxy root, ComponentAddress address) { if (address.getRootID().equals(root.getAddress().getRootID())) { ComponentProxy comp = root; for (int i = 1; i < address.getDepth(); i++) { if (comp instanceof ContainerProxy) { comp = ((ContainerProxy) comp).getChild(address.getComponentID(i)); } else { return null; } } return comp; } return null; } static ComponentProxy findComponentProxy(RootProxy root, JComponent cmp) { Object o = cmp.getClientProperty(Keys.Address); if (o instanceof ComponentAddress) { return findComponentProxy(root, (ComponentAddress) o); } return null; } static JComponent findAddressedComponent(Component cmp) { do { if (cmp instanceof JComponent && ((JComponent) cmp).getClientProperty(Keys.Address) != null) { return (JComponent) cmp; } cmp = cmp.getParent(); } while (cmp != null); return null; } static ComponentAddress getComponentAddress(Component cmp) { if (cmp instanceof JComponent) { Object ad = ((JComponent) cmp).getClientProperty(Keys.Address); if (ad instanceof ComponentAddress) { return (ComponentAddress) ad; } } return null; } static JComponent findContainerComponent(RootProxy root, Component cmp) { ComponentProxy pxy; do { cmp = findAddressedComponent(cmp); if (cmp instanceof JComponent) { pxy = findComponentProxy(root, (JComponent) cmp); if (pxy instanceof ContainerProxy) { return (JComponent) cmp; } } cmp = cmp.getParent(); } while (cmp != null); return null; } static int[] getGridPosition(JComponent container, JComponent component) { HashMap<Object, int[]> gridPositions = IDEUtil.getGridPositions(container); if (gridPositions == null) { return null; } int[] result = gridPositions.get(component); if (LOG.isLoggable(Level.FINE)) { if (result == null) { LOG.log(Level.FINE, "getGridPosition() null for {0} in {1}", new Object[]{Utils.getComponentAddress(component), Utils.getComponentAddress(container)}); } else { LOG.log(Level.FINEST, "getGridPosition() is {0} for {1} in {2}", new Object[]{Arrays.toString(result), Utils.getComponentAddress(component), Utils.getComponentAddress(container)}); } } return result; } static int[] getGridPosition(JComponent container, int mouseX, int mouseY) { int[] result = new int[]{0, 0}; int[][] axisSizes = IDEUtil.getColumnSizes(container); int[] sizes = axisSizes[1]; int i = 0; int accum = 0; for (; i < sizes.length; i++) { accum += sizes[i]; if (accum > mouseX) { break; } } i = i / 2; if (i < axisSizes[0].length) { result[0] = axisSizes[0][i]; } else { result[0] = axisSizes[0][axisSizes[0].length - 1] + 1; } axisSizes = IDEUtil.getRowSizes(container); sizes = axisSizes[1]; i = 0; accum = 0; for (; i < sizes.length; i++) { accum += sizes[i]; if (accum > mouseY) { break; } } i = i / 2; if (i < axisSizes[0].length) { result[1] = axisSizes[0][i]; } else { result[1] = axisSizes[0][axisSizes[0].length - 1] + 1; } return result; } static CC getConstraints(JComponent cmp) { Object val = cmp.getClientProperty(Keys.LayoutConstraint); if (val instanceof CC) { return (CC) val; } else { return new CC(); } } static void updateConstraints(JComponent cmp, CC cc) { cmp.putClientProperty(Keys.LayoutConstraint, null); cmp.putClientProperty(Keys.LayoutConstraint, cc); } static boolean isOccupied(JComponent container, int x, int y, int width, int height, Set<JComponent> ignored) { return !requiredSpace(container, x, y, width, height, ignored).isEmpty(); } static void ensureSpace(JComponent container, int x, int y, int width, int height, Set<JComponent> ignored, boolean vertical) { Rectangle space = requiredSpace(container, x, y, width, height, ignored); if (space.isEmpty()) { return; } if (vertical) { moveChildren(container, space.y, (y + height) - space.y, vertical, ignored); } else { moveChildren(container, space.x, (x + width) - space.x, vertical, ignored); } } static void move(RootProxy root, JComponent cmp, int dx, int dy) { changeBounds(root, cmp, dx, dy, 0, 0, Math.abs(dy) > Math.abs(dx)); } static void resize(RootProxy root, JComponent cmp, int dSpanX, int dSpanY) { changeBounds(root, cmp, 0, 0, dSpanX, dSpanY, Math.abs(dSpanY) > Math.abs(dSpanX)); } private static void changeBounds(RootProxy root, JComponent cmp, int dx, int dy, int dSpanX, int dSpanY, boolean vertical) { JComponent container = findContainerComponent(root, cmp); if (container == cmp) { container = findContainerComponent(root, cmp.getParent()); } int[] pos = getGridPosition(container, cmp); if (pos == null) { LOG.log(Level.FINE, "changeBounds() can't find component {0} in {1}", new Object[]{getComponentAddress(cmp), getComponentAddress(container)}); return; } CC cc = getConstraints(cmp); Set<JComponent> ignore = Collections.singleton(cmp); int x = pos[0] + dx; int y = pos[1] + dy; int spanX = pos[2] + dSpanX; int spanY = pos[3] + dSpanY; if (x < 0) { x = 0; } if (y < 0) { y = 0; } if (spanX < 1) { spanX = 1; } if (spanY < 1) { spanY = 1; } ensureSpace(container, x, y, spanX, spanY, ignore, vertical); cc.setCellX(x); cc.setCellY(y); cc.setSpanX(spanX); cc.setSpanY(spanY); updateConstraints(cmp, cc); compactGrid(container); } private static Rectangle requiredSpace(JComponent container, int x, int y, int width, int height, Set<JComponent> ignored) { HashMap<Object, int[]> gridPositions = IDEUtil.getGridPositions(container); if (gridPositions == null || gridPositions.isEmpty()) { return new Rectangle(); } Rectangle space = new Rectangle(x, y, width, height); Rectangle intersection = new Rectangle(); Rectangle cmp = new Rectangle(); for (Map.Entry<Object, int[]> entry : gridPositions.entrySet()) { if (ignored.contains(entry.getKey())) { continue; } int[] pos = entry.getValue(); if (pos.length < 4) { continue; } cmp.x = pos[0]; cmp.y = pos[1]; cmp.width = pos[2]; cmp.height = pos[3]; if (cmp.intersects(space)) { if (intersection.isEmpty()) { intersection.setBounds(cmp); } else { intersection.add(cmp); } } } return intersection; } static void compactGrid(JComponent container) { container.validate(); compactAxis(container, true); compactAxis(container, false); } private static void compactAxis(JComponent container, boolean vertical) { int[] ax = getAxisIndexes(container, vertical); int exp = 0; int i = 0; int overall = 0; int cur; while (i < ax.length && overall < 50) { overall++; cur = ax[i]; if (cur != exp) { if (LOG.isLoggable(Level.FINE)) { String orientation = vertical ? "Rows" : "Columns"; LOG.fine("Found gap in " + orientation + " , removing " + exp + " -> " + cur); } moveChildren(container, cur, exp - cur, vertical, Collections.<JComponent>emptySet()); ax = getAxisIndexes(container, vertical); exp = 0; i = 0; } else { i++; exp++; } } } private static int[] getAxisIndexes(JComponent container, boolean vertical) { // int[][] axes = vertical ? IDEUtil.getRowSizes(container) : IDEUtil.getColumnSizes(container); // if (axes == null) { // if (LOG.isLoggable(Level.FINE)) { // String orientation = vertical ? "Rows" : "Columns"; // LOG.log(Level.FINE, "No {0} found in {1}", new Object[]{ // orientation, Utils.getComponentAddress(container)}); // } // return new int[0]; // } else { // if (LOG.isLoggable(Level.FINE)) { // String orientation = vertical ? "Rows" : "Columns"; // LOG.log(Level.FINE, "Found {0} {1} in {2}", new Object[]{ // orientation, Arrays.toString(axes[0]), Utils.getComponentAddress(container)}); // } // return axes[0]; // } HashMap<Object, int[]> gridPositions = IDEUtil.getGridPositions(container); if (gridPositions == null || gridPositions.isEmpty()) { if (LOG.isLoggable(Level.FINE)) { String orientation = vertical ? "Rows" : "Columns"; LOG.log(Level.FINE, "No {0} found in {1}", new Object[]{ orientation, Utils.getComponentAddress(container)}); } return new int[0]; } TreeSet<Integer> axes = new TreeSet<Integer>(); int val, span; for (Map.Entry<Object, int[]> entry : gridPositions.entrySet()) { int[] pos = entry.getValue(); if (pos.length < 4) { continue; } if (vertical) { val = pos[1]; span = pos[3]; } else { val = pos[0]; span = pos[2]; } axes.add(val); while (span > 1) { LOG.fine("Adding span"); val++; axes.add(val); span--; } } int[] result = new int[axes.size()]; int i = 0; for (Integer ax : axes) { result[i++] = ax; } if (LOG.isLoggable(Level.FINE)) { String orientation = vertical ? "Rows" : "Columns"; LOG.log(Level.FINE, "Found {0} {1} in {2}", new Object[]{ orientation, Arrays.toString(result), Utils.getComponentAddress(container)}); } return result; } private static void moveChildren(JComponent container, int from, int diff, boolean vertical, Set<JComponent> ignored) { for (Component cmp : container.getComponents()) { if (cmp instanceof JComponent) { if (ignored.contains(cmp)) { continue; } JComponent jc = (JComponent) cmp; Object val = jc.getClientProperty(Keys.LayoutConstraint); if (val instanceof CC) { CC cc = (CC) val; if (vertical) { int y = cc.getCellY(); if (y >= from) { cc.setCellY(y + diff); } } else { int x = cc.getCellX(); if (x >= from) { cc.setCellX(x + diff); } } jc.putClientProperty(Keys.LayoutConstraint, null); jc.putClientProperty(Keys.LayoutConstraint, cc); } } } container.validate(); } }