/*
* Copyright (c) 2011, grossmann, Nikolaus Moll
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the jo-widgets.org nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL jo-widgets.org BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*/
package org.jowidgets.impl.layout.miglayout;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import org.jowidgets.api.layout.miglayout.IMigLayout;
import org.jowidgets.api.widgets.IContainer;
import org.jowidgets.api.widgets.IControl;
import org.jowidgets.common.types.Dimension;
import org.jowidgets.common.types.Rectangle;
final class MigLayout implements IMigLayout {
private final IContainer container;
private transient IContainerWrapperCommon cacheParentW = null;
// Hold the serializable text representation of the constraints.
private Object constraints;
private Object columnConstraints;
private Object rowConstraints;
private transient LCCommon lc = null;
private transient ACCommon colSpecs = null;
private transient ACCommon rowSpecs = null;
private final transient Map<IComponentWrapperCommon, CCCommon> ccMap = new HashMap<IComponentWrapperCommon, CCCommon>(8);
private transient GridCommon grid = null;
// The component to string constraints mappings.
private final Map<IControl, Object> scrConstrMap = new IdentityHashMap<IControl, Object>(8);
private transient ArrayList<AbstractLayoutCallbackCommon> callbackList = null;
private transient int lastModCount = MigLayoutToolkitImpl.getMigPlatformDefaults().getModCount();
private transient int lastHash = -1;
private final StringBuilder reason = new StringBuilder();
private final long cacheTime = 0;
//private long cacheTimeSetMs = 0;
private final long cacheTimeSetNano = 0;
private final LayoutUtilCommon layoutUtil;
MigLayout(final IContainer container, final Object constraints, final Object columnConstraints, final Object rowConstraints) {
this.container = container;
this.cacheParentW = new JoMigContainerWrapper(container);
layoutUtil = MigLayoutToolkitImpl.getMigLayoutUtil();
setLayoutConstraints(constraints);
setColumnConstraints(columnConstraints);
setRowConstraints(rowConstraints);
}
@Override
public void setLayoutConstraints(Object constraints) {
if (constraints == null || constraints instanceof String) {
constraints = ConstraintParserCommon.prepare((String) constraints);
lc = ConstraintParserCommon.parseLayoutConstraint((String) constraints);
}
else if (constraints instanceof LCWrapper) {
lc = ((LCWrapper) constraints).getLC();
}
else if (constraints instanceof LCCommon) {
lc = (LCCommon) constraints;
}
else {
throw new IllegalArgumentException("Illegal constraint type: " + constraints.getClass().toString());
}
this.constraints = constraints;
grid = null;
}
@Override
public Object getLayoutConstraints() {
return constraints;
}
@Override
public void setColumnConstraints(Object constraints) {
if (constraints == null || constraints instanceof String) {
constraints = ConstraintParserCommon.prepare((String) constraints);
colSpecs = ConstraintParserCommon.parseColumnConstraints((String) constraints);
}
else if (constraints instanceof ACWrapper) {
colSpecs = ((ACWrapper) constraints).getAC();
}
else if (constraints instanceof ACCommon) {
colSpecs = (ACCommon) constraints;
}
else {
throw new IllegalArgumentException("Illegal constraint type: " + constraints.getClass().toString());
}
columnConstraints = constraints;
grid = null;
}
@Override
public Object getColumnConstraints() {
return columnConstraints;
}
@Override
public void setRowConstraints(Object constraints) {
if (constraints == null || constraints instanceof String) {
constraints = ConstraintParserCommon.prepare((String) constraints);
rowSpecs = ConstraintParserCommon.parseRowConstraints((String) constraints);
}
else if (constraints instanceof ACWrapper) {
rowSpecs = ((ACWrapper) constraints).getAC();
}
else if (constraints instanceof ACCommon) {
rowSpecs = (ACCommon) constraints;
}
else {
throw new IllegalArgumentException("Illegal constraint type: " + constraints.getClass().toString());
}
rowConstraints = constraints;
grid = null;
}
@Override
public Object getRowConstraints() {
return rowConstraints;
}
@Override
public void setConstraintMap(final Map<IControl, Object> map) {
scrConstrMap.clear();
ccMap.clear();
for (final Map.Entry<IControl, Object> e : map.entrySet()) {
setComponentConstraintsImpl(e.getKey(), e.getValue(), true);
}
}
@Override
public Map<IControl, Object> getConstraintMap() {
return new IdentityHashMap<IControl, Object>(scrConstrMap);
}
/**
* Sets the component constraint for the component that already must be handleded by this layout manager.
* <p>
* See the class JavaDocs for information on how this string is formatted.
*
* @param constr The component constraints as a String or {@link CCCommon.miginfocom.layout.CC}. <code>null</code> is ok.
* @param comp The component to set the constraints for.
* @param noCheck Doesn't check if control already is managed.
* @throws RuntimeException if the constaint was not valid.
* @throws IllegalArgumentException If the component is not handling the component.
*/
private void setComponentConstraintsImpl(final IControl comp, final Object constr, final boolean noCheck) {
if (noCheck == false && scrConstrMap.containsKey(comp) == false) {
throw new IllegalArgumentException("Component must already be added to parent!");
}
final IComponentWrapperCommon cw = new JoMigComponentWrapper(comp);
if (constr == null || constr instanceof String) {
final String cStr = ConstraintParserCommon.prepare((String) constr);
scrConstrMap.put(comp, constr);
ccMap.put(cw, ConstraintParserCommon.parseComponentConstraint(cStr));
}
else if (constr instanceof CCWrapper) {
scrConstrMap.put(comp, constr);
ccMap.put(cw, ((CCWrapper) constr).getCC());
}
else if (constr instanceof CCCommon) {
scrConstrMap.put(comp, constr);
ccMap.put(cw, (CCCommon) constr);
}
else {
throw new IllegalArgumentException(
"Constraint must be String or ComponentConstraint: " + constr.getClass().toString());
}
grid = null;
}
@Override
public boolean isManagingComponent(final IControl control) {
return scrConstrMap.containsKey(control);
}
public void addLayoutCallback(final AbstractLayoutCallbackCommon callback) {
if (callback == null) {
throw new NullPointerException();
}
if (callbackList == null) {
callbackList = new ArrayList<AbstractLayoutCallbackCommon>(1);
}
callbackList.add(callback);
}
public void removeLayoutCallback(final AbstractLayoutCallbackCommon callback) {
if (callbackList != null) {
callbackList.remove(callback);
}
}
private void checkChildren() {
final List<IControl> comps = new LinkedList<IControl>(container.getChildren());
final List<IComponentWrapperCommon> removed = new LinkedList<IComponentWrapperCommon>();
for (final IComponentWrapperCommon cw : ccMap.keySet()) {
if (comps.contains(cw.getComponent())) {
comps.remove(cw.getComponent());
continue;
}
removed.add(cw);
}
for (final IComponentWrapperCommon cw : removed) {
scrConstrMap.remove(cw.getComponent());
ccMap.remove(cw);
}
if (comps.size() != scrConstrMap.size()) {
for (final IControl c : comps) {
if (scrConstrMap.containsKey(c)) {
continue;
}
setComponentConstraintsImpl(c, c.getLayoutConstraints(), true);
}
}
}
@SuppressWarnings("unused")
private boolean calculateCache() {
// if (Math.abs(cacheTimeSetMs - System.currentTimeMillis()) > 1000) {
// cacheTime = 0;
// return true;
// }
//CHECKSTYLE:OFF
if (System.nanoTime() - cacheTimeSetNano < 2 * cacheTime) {
// if (this.thread == null) {
// this.thread = new LayoutThread(this, 2 * cacheTime);
// }
// else {
// thread.reset();
// }
// return false;
}
//CHECKSTYLE:ON
return true;
}
/**
* Check if something has changed and if so recrete it to the cached objects.
*/
private void checkCache() {
// final long start = System.nanoTime();
// final boolean calculateCache = calculateCache();
// if (calculateCache) {
checkChildren();
// Check if the grid is valid
final int mc = MigLayoutToolkitImpl.getMigPlatformDefaults().getModCount();
if (lastModCount != mc) {
grid = null;
lastModCount = mc;
reason.append("lastmodcount,");
}
int hash = container.getSize().hashCode();
for (final Iterator<IComponentWrapperCommon> it = ccMap.keySet().iterator(); it.hasNext();) {
hash += it.next().getLayoutHashCode();
}
if (hash != lastHash) {
reason.append("hash " + hash + " vs " + lastHash + ",");
reason.append(container.getSize() + ",");
grid = null;
lastHash = hash;
}
// }
if (grid == null) {
grid = new GridCommon(cacheParentW, lc, rowSpecs, colSpecs, ccMap, callbackList);
reason.setLength(0);
}
// if (calculateCache) {
// final long end = System.nanoTime();
// final long currentTime = end - start;
//
// if (currentTime > 1000000) {
// return;
// }
// if (cacheTime < currentTime) {
// cacheTime = currentTime;
// //cacheTimeSetMs = System.currentTimeMillis();
// cacheTimeSetNano = end;
// }
// }
}
@Override
public void layout() {
checkCache();
final Rectangle r = container.getClientArea();
final int[] b = new int[] {r.getX(), r.getY(), r.getWidth(), r.getHeight()};
final boolean layoutAgain = grid.layout(b, lc.getAlignX(), lc.getAlignY(), false, true);
if (layoutAgain) {
grid = null;
checkCache();
grid.layout(b, lc.getAlignX(), lc.getAlignY(), false, false);
}
}
private Dimension getSize(final int type) {
checkCache();
final int w = layoutUtil.getSizeSafe(grid != null ? grid.getWidth() : null, type);
final int h = layoutUtil.getSizeSafe(grid != null ? grid.getHeight() : null, type);
return new Dimension(w, h);
}
@Override
public Dimension getMinSize() {
return container.computeDecoratedSize(getSize(LayoutUtilCommon.MIN));
}
@Override
public Dimension getPreferredSize() {
return container.computeDecoratedSize(getSize(LayoutUtilCommon.PREF));
}
@Override
public Dimension getMaxSize() {
return container.computeDecoratedSize(getSize(LayoutUtilCommon.MAX));
}
@Override
public void invalidate() {
//reason.append("invalidate,");
//grid = null;
}
}