package alm;
import java.awt.*;
import java.io.*;
import java.util.*;
import java.util.List;
import javax.swing.JComponent;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Unmarshaller;
import jaxb.ALMSchema;
import jaxb.ProcessXML;
import linearProgramming.*;
/**
* A GUI layout engine using the ALM.
*/
public class ALMLayout implements LayoutManager {
/**
* The specification used for calculating the layout.
*/
private LayoutSpec layoutSpec;
/**
* A list of all the components added to the ALM.
* Will expand when addArea is called and be reduced when
* layoutComponent is called.
*/
private List<Component> componentsToAdd;
/**
* The manner in which the GUI is dynamically adjusted. The default is to
* fit the child controls into their parent.
*/
private LayoutStyleType LayoutStyle = LayoutStyleType.FIT_TO_SIZE;
boolean recoverlayout = false;
boolean activated = true;
protected JComponent parent;
/**
* Creates a new layout engine with an empty specification.
*/
public ALMLayout() throws ALMException {
super();
try {
layoutSpec = new LayoutSpec();
} catch (LinearProgrammingException e) {
throw new ALMException("Could not construct ALM Specification.", e);
}
componentsToAdd = new ArrayList<Component>();
}
/**
* Creates a new layout engine with the given specification.
*
* @param ls
* the layout specification
*/
public ALMLayout(LayoutSpec ls) throws ALMException {
this();
layoutSpec = ls;
}
/**
* Creates a new layout engine to load a file.
*
* @param
* file a reference to the file to load
*/
public ALMLayout(File file) throws ALMException {
this();
load(file);
}
/**
* Creates a new layout engine to load a file from a filename.
*
* @param filename
* the filename of given file
*/
public ALMLayout(String filename) throws ALMException {
this();
load(filename);
}
// public boolean layout(object container, LayoutEventArgs layoutEventArgs)
// {
// JComponent parent = container as JComponent;
// layout(parent);
// return false; // False means that container's parent should not perform
// layout.
// }
/**
* Calculate and set the layout. If no layout specification is given, a
* specification is reverse engineered automatically.
*
* @param parent
* the parent control of the controls in the layout
*/
public void layout(Container parent) throws LinearProgrammingException {
// make sure that layout events occuring during layout are ignored
// i.e. activated is set to false during layout caluclation
if (!activated)
return;
activated = false;
// reverse engineer a layout specification if requested
if (recoverlayout) {
recoverLayout(parent);
recoverlayout = false;
}
// if the layout engine is set to fit the GUI to the given size,
// then the given size is enforced by setting absolute positions for
// Right and Bottom
if (LayoutStyle == LayoutStyleType.FIT_TO_SIZE) {
layoutSpec.getRight().setRange(parent.getBounds().getWidth(), parent
.getBounds().getWidth());
layoutSpec.getBottom().setRange(parent.getBounds().getHeight(), parent
.getBounds().getHeight());
}
layoutSpec.solveLayout();
// if new layout is infeasible, use previous layout
if (layoutSpec.getResult() == ResultType.INFEASIBLE) {
activated = true; // now layout calculation is allowed to run again
return;
}
if (layoutSpec.getResult() != ResultType.OPTIMAL) {
layoutSpec.save("failed-layout.txt");
throw new LinearProgrammingException("Could not solve the layout specification ("
+ layoutSpec.getResult().toString()
+ "). Saved specification in file failed-layout.txt");
}
// layoutSpec.Save("last-feasible-layout.txt");
// change the size of the GUI according to the calculated size
// if the layout engine was configured to do so
if (LayoutStyle == LayoutStyleType.ADJUST_SIZE) {
parent.setSize(new Dimension((int) Math
.round(layoutSpec.getRight().getValue() - layoutSpec.getLeft().getValue()),
(int) Math.round(layoutSpec.getBottom().getValue()
- layoutSpec.getTop().getValue())));
}
// set the calculated positions and sizes for every area
for (Area a : layoutSpec.getAreas())
a.doLayout();
activated = true; // now layout calculation is allowed to run again
}
// #region Editing
// / <summary>
// / Indicates whether editing mode is on.
// / </summary>
boolean editing = false;
// / <summary>
// / List that stores original controls while in edit mode
// / </summary>
public List<JComponent> savedControls;
// / <summary>
// / Edit form used
// / </summary>
public PropertiesWindow editForm;
public void edit(JComponent parent) throws ALMException {
layoutContainer(parent);
activated = false;
editing = true;
try {
layoutSpec.solve();
} catch (LinearProgrammingException e) {
throw new ALMException("Could not solve layout.", e);
}
savedControls = new ArrayList<JComponent>();
for (Component control : parent.getComponents()) {
JComponent c = (JComponent) control;
savedControls.add(c);
}
this.editForm = new PropertiesWindow(parent, this);
activated = true;
editForm.setVisible(true);
editForm.update();
}
public void quitEdit(Container parent) {
editing = false;
parent.removeAll();
for (JComponent control : savedControls) {
parent.add(control);
}
}
/**
* Reverse engineers the current GUI and recovers an ALM specification.
*/
public void recoverLayout() {
recoverlayout = true;
}
/**
* Reverse engineers a GUI and recovers an ALM specification.
*
* @param parent
* the parent container of the GUI
*/
public void recoverLayout(Container parent) throws LinearProgrammingException {
layoutSpec = new LayoutSpec();
TreeMap<Integer, XTab> xtabs = new TreeMap<Integer, XTab>();
TreeMap<Integer, YTab> ytabs = new TreeMap<Integer, YTab>();
xtabs.put((int) parent.getBounds().getX(), layoutSpec.getLeft());
xtabs.put((int) (parent.getBounds().getX() + parent.getBounds()
.getWidth()), layoutSpec.getRight());
ytabs.put((int) parent.getBounds().getY(), layoutSpec.getTop());
ytabs.put((int) (parent.getBounds().getY() + parent.getBounds()
.getHeight()), layoutSpec.getBottom());
for (Component c : parent.getComponents()) {
JComponent cJ = (JComponent) c;
XTab x1, x2;
YTab y1, y2;
if (xtabs.containsKey((int) cJ.getVisibleRect().getX()))
x1 = xtabs.get((int) cJ.getVisibleRect().getX());
else
xtabs.put((int) cJ.getVisibleRect().getX(), x1 = new XTab(
layoutSpec));
if (xtabs.containsKey((int) (cJ.getVisibleRect().getX() + cJ
.getVisibleRect().getWidth())))
x2 = xtabs.get((int) (cJ.getVisibleRect().getX() + cJ
.getVisibleRect().getWidth()));
else
xtabs.put((int) (cJ.getVisibleRect().getX() + cJ
.getVisibleRect().getWidth()),
x2 = new XTab(layoutSpec));
if (ytabs.containsKey((int) cJ.getVisibleRect().getY()))
y1 = ytabs.get((int) cJ.getVisibleRect().getY());
else
ytabs.put((int) cJ.getVisibleRect().getY(), y1 = new YTab(
layoutSpec));
if (ytabs.containsKey((int) (cJ.getVisibleRect().getY() + cJ
.getVisibleRect().getHeight())))
y2 = ytabs.get((int) (cJ.getVisibleRect().getY() + cJ
.getVisibleRect().getHeight()));
else
ytabs.put((int) (cJ.getVisibleRect().getY() + cJ
.getVisibleRect().getHeight()), y2 = new YTab(
layoutSpec));
layoutSpec.addArea(x1, y1, x2, y2, cJ);
x2.leftLink = true;
y2.topLink = true;
}
// adding additional constraints (links in the PO) for margins between
// areas
XTab currentXTab = null;
XTab previousXTab = null;
int currentXKey = 0;
int previousXKey = 0;
Iterator xTabsIterator = xtabs.values().iterator();
Iterator xKeyIterator = xtabs.keySet().iterator();
while (xTabsIterator.hasNext()) {
currentXTab = (XTab) xTabsIterator.next();
currentXKey = Integer.parseInt(xKeyIterator.next().toString());
if (!currentXTab.leftLink)
layoutSpec.addConstraint(-1, previousXTab, 1, currentXTab,
OperatorType.EQ, -previousXKey + currentXKey);
previousXTab = currentXTab;
previousXKey = currentXKey;
}
YTab currentYTab = null;
YTab previousYTab = null;
int currentYKey = 0;
int previousYKey = 0;
Iterator yTabsIterator = ytabs.values().iterator();
Iterator yKeyIterator = ytabs.keySet().iterator();
while (yTabsIterator.hasNext()) {
currentYTab = (YTab) yTabsIterator.next();
currentYKey = Integer.parseInt(yKeyIterator.next().toString());
if (!currentYTab.topLink)
layoutSpec.addConstraint(-1, previousYTab, 1, currentYTab,
OperatorType.EQ, -previousYKey + currentYKey);
previousYTab = currentYTab;
previousYKey = currentYKey;
}
}
// #endregion RE
public void removeLayoutComponent(Component comp) {
}
public void layoutContainer(Container parent) {
this.parent = (JComponent) parent;
for (Component c : componentsToAdd) {
parent.add(c);
}
componentsToAdd.clear();
try {
layout(parent);
} catch (Exception e) {
e.printStackTrace();
}
}
public void addLayoutComponent(String name, Component comp) {}
public Dimension minimumLayoutSize(Container parent) {
return parent.getSize();
}
public Dimension preferredLayoutSize(Container parent) {
return parent.getSize();
}
public LayoutSpec getLayoutSpec() {
return layoutSpec;
}
public void setLayoutSpec(LayoutSpec ls) {
layoutSpec = ls;
}
public void load(File file) throws ALMException {
try {
ProcessXML pro = new ProcessXML(this);
JAXBContext jc = JAXBContext.newInstance("jaxb");
Unmarshaller unmarshaller = jc.createUnmarshaller();
ALMSchema almlayout = (ALMSchema) unmarshaller.unmarshal(file);
List<ALMSchema.Area> areaList = almlayout.getArea();
List<ALMSchema.Constraint> constraintList = almlayout.getConstraint();
for (int i = 0; i < areaList.size(); i++) {
pro.parseArea(areaList.get(i));
}
for (int i = 0; i < constraintList.size(); i++) {
pro.parseConstraint(constraintList.get(i));
}
pro.getNewLayoutSpec().solve();
} catch (Exception e) {
throw new ALMException("Could not load "+file.getName()+".", e);
}
}
public void load(String filename) throws ALMException {
load(new File(filename));
}
/**
* Saves the layout configuration to a file
* @param file
* the configuration file to save
*/
public void save(File file) {
try {
OutputStream fout = new FileOutputStream(file);
OutputStream bout = new BufferedOutputStream(fout);
OutputStreamWriter out = new OutputStreamWriter(bout, "UTF8");
out.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
out.write("<almlayout>\n");
out.write("\t<version> 2 </version>\n");
layoutSpec.writeXML(out);
out.write("</almlayout>");
out.flush();
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
public void save(String filename) {
save(new File(filename));
}
// Modified wrapper methods for LayoutSpec
public Area addArea(XTab left, YTab top, XTab right, YTab bottom,
JComponent content, Dimension minContentSize) {
componentsToAdd.add(content);
return layoutSpec.addArea(left, top, right, bottom, content, minContentSize);
}
public Area addArea(Row row, Column column, JComponent content,
Dimension minContentSize) {
componentsToAdd.add(content);
return layoutSpec.addArea(row, column, content, minContentSize);
}
public Area addArea(XTab left, YTab top, XTab right, YTab bottom,
JComponent content) throws ALMException {
try {
componentsToAdd.add(content);
return layoutSpec.addArea(left, top, right, bottom, content);
} catch (LinearProgrammingException e) {
throw new ALMException("Could not add area to layout.", e);
}
}
public Area addArea(Row row, Column column, JComponent content) throws ALMException {
componentsToAdd.add(content);
try {
return layoutSpec.addArea(row, column, content);
} catch (LinearProgrammingException e) {
throw new ALMException("Could not add area to layout.", e);
}
}
// Wrapper methods for LayoutSpec
public XTab addXTab() {
return layoutSpec.addXTab();
}
public YTab addYTab() {
return layoutSpec.addYTab();
}
public Row addRow() {
return layoutSpec.addRow();
}
public Row addRow(YTab top, YTab bottom) throws ALMException {
try {
return layoutSpec.addRow(top, bottom);
} catch (LinearProgrammingException e) {
throw new ALMException("Cannot add row to layout.", e);
}
}
public Column addColumn() {
return layoutSpec.addColumn();
}
public Column addColumn(XTab left, XTab right) throws ALMException {
try {
return layoutSpec.addColumn(left, right);
} catch (LinearProgrammingException e) {
throw new ALMException("Cannot add column to layout.", e);
}
}
public Area areaOf(JComponent c) {
return layoutSpec.areaOf(c);
}
public List<Area> getAreas() {
return layoutSpec.getAreas();
}
public List<Row> getRows() {
return layoutSpec.getRows();
}
public List<Column> getColumns() {
return layoutSpec.getColumns();
}
public XTab getLeft() {
return layoutSpec.getLeft();
}
public XTab getRight() {
return layoutSpec.getRight();
}
public YTab getTop() {
return layoutSpec.getTop();
}
public YTab getBottom() {
return layoutSpec.getBottom();
}
public Dimension getMinSize() throws LinearProgrammingException {
return layoutSpec.getMinSize();
}
public Dimension getMaxSize() throws LinearProgrammingException {
return layoutSpec.getMaxSize();
}
public Dimension getPreferredSize() throws LinearProgrammingException {
return layoutSpec.getPreferredSize();
}
public void setMinSize(Dimension minSize) {
layoutSpec.setMinSize(minSize);
}
public void setMaxSize(Dimension maxSize) {
layoutSpec.setMaxSize(maxSize);
}
public void setPreferredSize(Dimension preferredSize) {
layoutSpec.setPreferredSize(preferredSize);
}
// Wrapper methods for LinearSpec
public Constraint addConstraint(Summand[] summands, OperatorType op, double rightSide) throws ALMException {
try {
return layoutSpec.addConstraint(summands, op, rightSide);
} catch (LinearProgrammingException e) {
throw new ALMException("Could not add constraint to linear specification.", e);
}
}
public Constraint addConstraint(double coeff1, Variable var1, OperatorType op, double rightSide) throws ALMException {
try {
return layoutSpec.addConstraint(coeff1, var1, op, rightSide);
} catch (LinearProgrammingException e) {
throw new ALMException("Could not add constraint to linear specification.", e);
}
}
public Constraint addConstraint(double coeff1, Variable var1,
double coeff2, Variable var2, OperatorType op, double rightSide) throws ALMException {
try {
return layoutSpec.addConstraint(coeff1, var1, coeff2, var2, op, rightSide);
} catch (LinearProgrammingException e) {
throw new ALMException("Could not add constraint to linear specification.", e);
}
}
public Constraint addConstraint(double coeff1, Variable var1,
double coeff2, Variable var2, double coeff3, Variable var3, OperatorType op, double rightSide) throws ALMException {
try {
return layoutSpec.addConstraint(coeff1, var1, coeff2, var2, coeff3, var3, op, rightSide);
} catch (LinearProgrammingException e) {
throw new ALMException("Could not add constraint to linear specification.", e);
}
}
public Constraint addConstraint(double coeff1, Variable var1,
double coeff2, Variable var2, double coeff3, Variable var3,
double coeff4, Variable var4, OperatorType op, double rightSide) throws ALMException {
try {
return layoutSpec.addConstraint(coeff1, var1, coeff2, var2, coeff3, var3, coeff4, var4, op, rightSide);
} catch (LinearProgrammingException e) {
throw new ALMException("Could not add constraint to linear specification.", e);
}
}
public Constraint addConstraint(Summand[] summands, OperatorType op,
double penaltyNeg, double penaltyPos, double rightSide) throws ALMException {
try {
return layoutSpec.addConstraint(summands, op, penaltyNeg, penaltyPos, rightSide);
} catch (LinearProgrammingException e) {
throw new ALMException("Could not add constraint to linear specification.", e);
}
}
public Constraint addConstraint(double coeff1, Variable var1,
OperatorType op, double rightSide, double penaltyNeg, double penaltyPos) throws ALMException {
try {
return layoutSpec.addConstraint(coeff1, var1, op, rightSide, penaltyNeg, penaltyPos);
} catch (LinearProgrammingException e) {
throw new ALMException("Could not add constraint to linear specification.", e);
}
}
public Constraint addConstraint(double coeff1, Variable var1, double coeff2, Variable var2,
OperatorType op, double rightSide, double penaltyNeg, double penaltyPos) throws ALMException {
try {
return layoutSpec.addConstraint(coeff1, var1, coeff2, var2, op, rightSide, penaltyNeg, penaltyPos);
} catch (LinearProgrammingException e) {
throw new ALMException("Could not add constraint to linear specification.", e);
}
}
public Constraint addConstraint(double coeff1, Variable var1, double coeff2, Variable var2,
double coeff3, Variable var3, OperatorType op, double rightSide, double penaltyNeg,
double penaltyPos) throws ALMException {
try {
return layoutSpec.addConstraint(coeff1, var1, coeff2, var2, coeff3, var3, op, rightSide, penaltyNeg, penaltyPos);
} catch (LinearProgrammingException e) {
throw new ALMException("Could not add constraint to linear specification.", e);
}
}
public Constraint addConstraint(double coeff1, Variable var1, double coeff2, Variable var2,
double coeff3, Variable var3, double coeff4, Variable var4, OperatorType op,
double rightSide, double penaltyNeg, double penaltyPos) throws ALMException {
try {
return layoutSpec.addConstraint(coeff1, var1, coeff2, var2, coeff3, var3, coeff4, var4, op, rightSide, penaltyNeg, penaltyPos);
} catch (LinearProgrammingException e) {
throw new ALMException("Could not add constraint to linear specification.", e);
}
}
}