/*
* Copyright 2004-2010 Information & Software Engineering Group (188/1)
* Institute of Software Technology and Interactive Systems
* Vienna University of Technology, Austria
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.ifs.tuwien.ac.at/dm/somtoolbox/license.html
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package at.tuwien.ifs.somtoolbox.apps.viewer.controls;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Vector;
import java.util.logging.Logger;
import javax.swing.ButtonGroup;
import javax.swing.ButtonModel;
import javax.swing.JButton;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JToggleButton;
import at.tuwien.ifs.somtoolbox.apps.viewer.CommonSOMViewerStateData;
import at.tuwien.ifs.somtoolbox.apps.viewer.GeneralUnitPNode;
import at.tuwien.ifs.somtoolbox.apps.viewer.SOMFrame;
import at.tuwien.ifs.somtoolbox.apps.viewer.SOMPane;
import at.tuwien.ifs.somtoolbox.layers.GrowingLayer;
import at.tuwien.ifs.somtoolbox.layers.Unit;
import at.tuwien.ifs.somtoolbox.models.GHSOMHierarchyRoot;
import at.tuwien.ifs.somtoolbox.models.GHSOMLevelLayer;
/**
* A panel providing drill down and roll up features for a hierarchical growing som.
*
* @author Philip Langer
* <p[dot]langer[at]gmail[dot]com>
*/
public class GHSOMNavigationPanel extends AbstractSelectionPanel implements ActionListener {
/**
* The zoom in action command.
*/
private static final String AC_DRILL_DOWN = "drill-down";
/**
* The zoom out action command.
*/
private static final String AC_ROLL_UP = "roll-up";
/**
* The label for the zoom in button.
*/
private static final String LBL_DRILL_DOWN_BUTTON = "Drill down";
/**
* The label for the zoom out button.
*/
private static final String LBL_ROLL_UP_BUTTON = "Roll up";
/**
* The serial id.
*/
private static final long serialVersionUID = 6360870188975691823L;
/**
* The drill down {@link JButton}.
*/
private JButton btDrillDown;
/**
* The roll up {@link JButton}.
*/
private JButton btRollUp;
/**
* the current viewed level
*/
private int currentLevel = -1;
/**
* The currently selected {@link Unit}.
*/
private Unit currentUnit = null;
/**
* The logger for this type.
*/
private Logger logger = Logger.getLogger("at.tuwien.ifs.somtoolbox");
/**
* a vector containing all opened sub-frames
*/
private Vector<SOMFrame> openedFrames = new Vector<SOMFrame>();
private GHSOMHierarchyRoot rootLayer = new GHSOMHierarchyRoot(state.growingSOM.getLayer());
/**
* Reference to the som pane.
*/
private SOMPane somPane;
/**
* Constructor.
*
* @param state state.
* @param mapPane som map.
*/
public GHSOMNavigationPanel(CommonSOMViewerStateData state, SOMPane mapPane) {
super(new GridBagLayout(), state, "GHSOM Navigation Panel");
this.somPane = mapPane;
this.initGUIElements();
// set current level to rootLevel
currentLevel = 0;
setVisible(true);
}
@Override
public void actionPerformed(ActionEvent ae) {
if (ae.getActionCommand() == AC_DRILL_DOWN) {
drillDown(currentUnit);
} else if (ae.getActionCommand() == AC_ROLL_UP) {
rollUp();
}
}
private void addLayerFrame(GrowingLayer layer) {
SOMFrame newSomFrame = new SOMFrame(state);
openedFrames.add(newSomFrame);
newSomFrame.setVisible(true);
newSomFrame.setSize(400, 400);
if (layer.getSuperUnit() != null) {
newSomFrame.setTitle("SubLayer of Unit " + layer.getSuperUnit().getXPos() + "/"
+ layer.getSuperUnit().getYPos());
} else {
newSomFrame.setTitle("no parent detected");
}
SOMPane newSomPane = new SOMPane(somPane.getMap().getParentFrame(), state.growingSOM, state.growingLayer, state);
newSomPane.setSize(newSomFrame.getWidth() - 15, newSomFrame.getHeight() - 15);
newSomFrame.add(newSomPane);
newSomFrame.setResizable(true);
somPane.getCanvas().add(newSomFrame);
newSomPane.validate();
newSomPane.centerAndFitMapToScreen(0);
}
/**
* Drills down the underlying map of the specified {@link Unit}.
*
* @param unit the node to zoom into.
*/
private void drillDown(Unit unit) {
// check if null
if (unit == null) {
throw new IllegalArgumentException("Specified unit node is null");
}
// check if drill down able
if (!isDrillDownable(unit)) {
throw new IllegalArgumentException("Specified unit node is drillable");
}
addLayerFrame(unit.getMappedSOM().getLayer());
// TODO doesn't work
// switchMainMap((GrowingLayer)unit.getMappedSOM().getLayer());
logger.info("drill down to " + unit.toString());
somPane.getBorder();
somPane.addNotify();
// TODO select underlying map and repaint with it
}
public int getCurrentLevel() {
return currentLevel;
}
@Override
public Dimension getMinimumSize() {
return new Dimension(state.controlElementsWidth, 200);
}
// @Override
// public Dimension getPreferredSize() {
// return new Dimension(state.controlElementsWidth, 300);
// }
/**
* Initializes the GUI elements.
*/
private void initGUIElements() {
JPanel ghsomPanel = new JPanel(new GridBagLayout());
JPanel navigPanel = new JPanel(new GridBagLayout());
GridBagConstraints c = new GridBagConstraints();
c.fill = GridBagConstraints.HORIZONTAL;
// drill down button
btDrillDown = new JButton(LBL_DRILL_DOWN_BUTTON);
btDrillDown.setActionCommand(AC_DRILL_DOWN);
btDrillDown.addActionListener(this);
btDrillDown.setEnabled(false);
c.gridx = 0;
c.gridy = 0;
c.anchor = GridBagConstraints.NORTH;
navigPanel.add(btDrillDown, c);
// drill down button
btRollUp = new JButton(LBL_ROLL_UP_BUTTON);
btRollUp.setActionCommand(AC_ROLL_UP);
btRollUp.addActionListener(this);
btRollUp.setEnabled(false);
c.gridx = 1;
c.gridy = 0;
c.anchor = GridBagConstraints.NORTH;
navigPanel.add(btRollUp, c);
ghsomPanel.add(navigPanel, c);
JPanel levelPanel = new JPanel(new GridLayout(4, 1));
// create a toggle-button for each layer
ButtonGroup group = new ButtonGroup();
JToggleButton button;
for (int i = 0; i <= GHSOMLevelLayer.getDepth(); i++) {
button = new JToggleButton(); // TODO overwrite jtoggelbutton and
// add information about current
// level
button.setName("" + i); // always must contain the Level-Number!
button.setText("level " + i);
// if level = 0, select the root level element
if (i == 0) {
button.setSelected(true);
}
button.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent event) {
// TODO Auto-generated method stub
JToggleButton button = (JToggleButton) event.getSource();
if (!button.getName().equals("" + currentLevel)) {
currentLevel = Integer.parseInt(button.getName());
System.out.println(button.getName() + "selected: " + button.isSelected());
// close former windows
for (SOMFrame openFrame : openedFrames) {
openFrame.dispose();
}
// if level = 0, set the root map to visible again
if (currentLevel == 0) {
somPane.getMap().setVisible(true);
} else {
// if level != 0 set root map to invisible
somPane.getMap().setVisible(false);
// create level frames
ButtonModel buttonModel = button.getModel();
boolean armed = buttonModel.isArmed();
boolean pressed = buttonModel.isPressed();
boolean selected = buttonModel.isSelected();
System.out.println("armed / pressed / selected " + armed + " / " + pressed + " / "
+ selected);
// load level of SOM
GHSOMLevelLayer layer = rootLayer.getLevel(Integer.parseInt(button.getName()));
// for each growingLayer of the level create a
// window
CommonSOMViewerStateData state = somPane.getMap().getState();
for (GrowingLayer glayer : layer.getLevelLayer()) {
state.growingLayer = glayer;
addLayerFrame(glayer);
System.out.println("somframe created");
}
// arrange frames
for (int i = 0; i < openedFrames.size(); i++) {
openedFrames.get(i).setLocation(15 * i, 15 * i);
}
}
}
somPane.validate();
}
});
group.add(button);
levelPanel.add(button);
}
// add scrollpane
// TODO doesn't work propperly
JScrollPane scrollPane = new JScrollPane();
scrollPane.setViewportView(levelPanel);
c.fill = GridBagConstraints.BOTH;
c.gridx = 1;
c.gridy = 1;
c.anchor = GridBagConstraints.CENTER;
ghsomPanel.add(scrollPane, c);
getContentPane().add(ghsomPanel, c);
}
/**
* Returns <code>true</code> if the specified <code>unit</code> has an underlying map and is therefore
* drill-down-able.
*
* @param unit {@link Unit} to check.
* @return <code>true</code> if drill-down-able, <code>false</code> otherwhise.
*/
private boolean isDrillDownable(Unit unit) {
return unit.getMappedSOM() != null;
}
/**
* Returns <code>true</code> if the current map is a child of a {@link Unit}.
*
* @return <code>true</code> if roll up is possible, <code>false</code> otherwise.
*/
private boolean isRollUpable(GrowingLayer growingLayer) {
return growingLayer.getSuperUnit() != null;
}
/**
* Rolls up to the parent of the current layer.
*/
private void rollUp() {
// TODO select parent map of current map and repaint
}
public void setCurrentLevel(int currentLevel) {
this.currentLevel = currentLevel;
}
// private void switchMainMap(GrowingLayer layer) {
// // FIXME doesn't work ...
// SOMPane newSomPane = new SOMPane(somPane.getMap().getParentFrame(), state.growingSOM, state.growingLayer, state);
// somPane = newSomPane;
// newSomPane.validate();
// newSomPane.centerAndFitMapToScreen(0);
// }
@Override
public void unitSelectionChanged(Object[] selection, boolean newSelection) {
super.unitSelectionChanged(selection, newSelection);
// as we just can zoom into one unit we just track the
// first of the selected nodes.
logger.fine(String.valueOf(selection.length));
if (selection.length == 1) {
currentUnit = ((GeneralUnitPNode) selection[0]).getUnit();
// check if unit is drillable.
if (isDrillDownable(currentUnit)) {
btDrillDown.setEnabled(true);
} else {
btDrillDown.setEnabled(false);
}
} else {
btDrillDown.setEnabled(false);
}
if (selection.length > 0 && selection[0] instanceof GeneralUnitPNode) {
GrowingLayer layer = (GrowingLayer) ((GeneralUnitPNode) selection[0]).getUnit().getLayer();
// update roll up button
btRollUp.setEnabled(isRollUpable(layer));
}
}
}