// // MappingDialog.java // /* VisAD system for interactive analysis and visualization of numerical data. Copyright (C) 1996 - 2017 Bill Hibbard, Curtis Rueden, Tom Rink, Dave Glowacki, Steve Emmerson, Tom Whittaker, Don Murray, and Tommy Jasmin. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ package visad.ss; import java.awt.*; import java.awt.image.BufferedImage; import java.awt.event.*; import java.net.URL; import java.rmi.RemoteException; import java.util.Hashtable; import java.util.Vector; import javax.swing.*; import javax.swing.event.*; import visad.*; import visad.util.*; /** * MappingDialog is a dialog that lets the user create ScalarMaps. */ public class MappingDialog extends JDialog implements ActionListener, ListSelectionListener, MouseListener { /** * Flag whether user hit Done or Cancel button. */ private boolean Confirm = false; /** * ScalarMaps selected by the user. */ private ScalarMap[] ScalarMaps; // -- GUI components -- private JComponent MathCanvas; private JScrollPane MathCanvasView; private JComponent CoordCanvas; private JScrollPane CoordCanvasView; private boolean CoordRefs; private JList MathList; private JComponent DisplayCanvas; private DefaultListModel CurMaps; private JList CurrentMaps; private JScrollPane CurrentMapsView; private JLabel description; // -- State info -- /** * Array of ScalarTypes. */ private ScalarType[] MathTypes; /** * Array of ScalarType names. */ private String[] Scalars; /** * Array of ScalarType widths. */ private int[] ScW; /** * Vector of MDTuples indicating ScalarType locations. */ private Vector[] ScP; /** * ScalarType height. */ private int ScH; /** * Width and height of MathType string. */ private Dimension StrSize; /** * Width and height of CoordinateSystem string. */ private Dimension CoordSize; /** * Flags marking whether each possible ScalarMap has been assigned. */ private boolean[][][] Maps; /** * String representation of each possible ScalarMap. */ private String[][][] CurMapLabel; /** * 11 pt monospaced font. */ private static final Font Mono = new Font("Monospaced", Font.PLAIN, 11); /** * Names of system intrinsic DisplayRealTypes. */ private static final String[][] MapNames = { {"X Axis", "Y Axis", "Z Axis", "X Offset", "Y Offset", "Z Offset"}, {"Latitude", "Longitude", "Radius", "Cyl Radius", "Cyl Azimuth", "Cyl Z Axis"}, {"Flow1 X", "Flow1 Y", "Flow1 Z", "Flow2 X", "Flow2 Y", "Flow2 Z"}, {"Flow1 Elevation", "Flow1 Azimuth", "Flow1 Radial", "Flow2 Elevation", "Flow2 Azimuth", "Flow2 Radial"}, {"Red", "Green", "Blue", "RGB", "RGBA", "Alpha"}, {"Cyan", "Magenta", "Yellow", "CMY", "Animation", "Iso-contour"}, {"Hue", "Saturation", "Value", "HSV", "Select Value", "Select Range"}, {"", "Text", "", "", "Shape", ""} }; /** * List of system intrinsic DisplayRealTypes. */ private static final DisplayRealType[][] MapTypes = { {Display.XAxis, Display.YAxis, Display.ZAxis, Display.XAxisOffset, Display.YAxisOffset, Display.ZAxisOffset}, {Display.Latitude, Display.Longitude, Display.Radius, Display.CylRadius, Display.CylAzimuth, Display.CylZAxis}, {Display.Flow1X, Display.Flow1Y, Display.Flow1Z, Display.Flow2X, Display.Flow2Y, Display.Flow2Z}, {Display.Flow1Elevation, Display.Flow1Azimuth, Display.Flow1Radial, Display.Flow2Elevation, Display.Flow2Azimuth, Display.Flow2Radial}, {Display.Red, Display.Green, Display.Blue, Display.RGB, Display.RGBA, Display.Alpha}, {Display.Cyan, Display.Magenta, Display.Yellow, Display.CMY, Display.Animation, Display.IsoContour}, {Display.Hue, Display.Saturation, Display.Value, Display.HSV, Display.SelectValue, Display.SelectRange}, {null, Display.Text, null, null, Display.Shape, null} }; /** * Indices into MapTypes of alpha-related DisplayRealTypes. */ private static final Point[] AlphaMaps = { new Point(4, 4), // RGBA new Point(4, 5) // Alpha }; /** * Indices into MapTypes of 3D-related DisplayRealTypes. */ private static final Point[] ThreeDMaps = { new Point(0, 2), // ZAxis new Point(0, 5), // ZAxisOffset new Point(1, 0), // Latitude new Point(1, 5), // CylZAxis new Point(2, 2), // Flow1Z new Point(2, 5), // Flow2Z new Point(3, 0), // Flow1Elevation new Point(3, 3) // Flow2Elevation }; /** * Width of mapping arrays. */ private static final int MapWidth = MapTypes[0].length; /** * Height of mapping arrays. */ private static final int MapHeight = MapTypes.length; /** * display.gif image. */ private static Image DRT = null; /** * Whether DRT image has been initialized. */ private static boolean Inited = false; /** * Pre-loads the display.gif file, so it's ready * when mapping dialog is requested. */ public static void initDialog() { if (DRT == null) { URL url = MappingDialog.class.getResource("display.gif"); DRT = Toolkit.getDefaultToolkit().getImage(url); } Inited = true; } /** * Returns a human-readable list of CoordinateSystem dependencies. */ private static String prettyCoordSys(MathType type) { String s = ""; if (type instanceof FunctionType) s = s + pcsFunction((FunctionType) type); else if (type instanceof SetType) s = s + pcsSet((SetType) type); else if (type instanceof TupleType) s = s + pcsTuple((TupleType) type); return s; } /** * prettyCoordSys helper method. */ private static String pcsFunction(FunctionType mathType) { String s = ""; // extract domain RealTupleType domain = mathType.getDomain(); s = s + pcsTuple((TupleType) domain); // extract range MathType range = mathType.getRange(); s = s + prettyCoordSys(range); return s; } /** * prettyCoordSys helper method. */ private static String pcsSet(SetType mathType) { // extract domain RealTupleType domain = mathType.getDomain(); return pcsTuple((TupleType) domain); } /** * prettyCoordSys helper method. */ private static String pcsTuple(TupleType mathType) { String s = ""; if (mathType instanceof RealTupleType) { RealTupleType rtt = (RealTupleType) mathType; CoordinateSystem cs = rtt.getCoordinateSystem(); if (cs != null) { RealTupleType ref = cs.getReference(); if (ref != null) { s = s + rtt.prettyString() + " ==> " + ref.prettyString() + "\n"; } } } else { // extract components for (int j=0; j<mathType.getDimension(); j++) { MathType cType = null; try { cType = mathType.getComponent(j); } catch (VisADException exc) { } if (cType != null) s = s + prettyCoordSys(cType); } } return s; } /** * Converts a string index into a graphical position. */ private static Point indexToPoint(int ndx, int[] lines, int w, int h) { int i = 0; while (i < lines.length && lines[i] <= ndx) i++; int surplus = ndx - lines[i - 1]; return new Point(w * surplus + 5, (h + 2) * (i - 1) + 6); } /** * For synchronization. */ private Object Lock = new Object(); /** * Flags marking whether each DisplayRealType is illegal. */ private boolean[][] Illegal = new boolean[MapHeight][MapWidth]; /** * This MappingDialog's copy of DRT with certain * DisplayRealTypes blacked out as necessary. */ private Image MapTo; /** * The MathType with which this mapping dialog works. */ private MathType[] Types = null; /** * Whether this mapping dialog allows mappings to Alpha and RGBA. */ private boolean AllowAlpha; /** * Whether this mapping dialog allows mappings to Z-Axis, * Latitude, and Z-Offset. */ private boolean Allow3D; /** * Constructs a MappingDialog from a single Data object. */ public MappingDialog(Frame parent, Data data, ScalarMap[] startMaps, boolean allowAlpha, boolean allow3D) { this(parent, new Data[] {data}, startMaps, allowAlpha, allow3D); } /** * Constructs a MappingDialog from multiple Data objects. */ public MappingDialog(Frame parent, Data[] data, ScalarMap[] startMaps, boolean allowAlpha, boolean allow3D) { super(parent, "Set up data mappings", true); AllowAlpha = allowAlpha; Allow3D = allow3D; // set up content pane setBackground(Color.white); JPanel contentPane = new JPanel(); setContentPane(contentPane); contentPane.setLayout(new BoxLayout(contentPane, BoxLayout.Y_AXIS)); contentPane.add(Box.createRigidArea(new Dimension(0, 5))); // parse MathTypes int numData = data.length; Types = new MathType[numData]; String typeString = ""; String coordString = ""; for (int i=0; i<numData; i++) { try { Data d = data[i]; if (d == null) Types[i] = null; else { Types[i] = d.getType(); typeString = typeString + Types[i].prettyString() + "\n"; coordString = coordString + prettyCoordSys(Types[i]); } } catch (VisADException exc) { if (BasicSSCell.DEBUG) exc.printStackTrace(); } catch (RemoteException exc) { if (BasicSSCell.DEBUG) exc.printStackTrace(); } } Vector v = new Vector(); int dupl = 0; try { dupl = DataUtility.getScalarTypes(data, v, true, true); } catch (VisADException exc) { if (BasicSSCell.DEBUG) exc.printStackTrace(); } catch (RemoteException exc) { if (BasicSSCell.DEBUG) exc.printStackTrace(); } // determine width and height of MathType string FontMetrics fm = getFontMetrics(Mono); ScH = fm.getHeight(); int charWidth = fm.stringWidth(" "); int width = 1; int pos = -1; int numLines = 0; do { int nextPos = typeString.indexOf("\n", pos + 1); if (nextPos < 0) nextPos = typeString.length(); int w = charWidth * (nextPos - pos); if (w > width) width = w; pos = nextPos; numLines++; } while (pos < typeString.length()); StrSize = new Dimension(width, (ScH + 2) * numLines + 10); // get starting line position for each line of MathType string int[] mathLines = new int[numLines]; pos = -1; for (int i=0; i<numLines; i++) { mathLines[i] = pos + 1; pos = typeString.indexOf("\n", pos + 1); } // determine width and height of CoordinateSystem string width = 1; pos = -1; numLines = 0; do { int nextPos = coordString.indexOf("\n", pos + 1); int w = charWidth * (nextPos - pos); if (w > width) width = w; pos = nextPos; numLines++; } while (pos < coordString.length() - 1); CoordSize = new Dimension(width, (ScH + 2) * numLines + 10); // get starting line position for each line of CoordinateSystem string int[] coordLines = new int[numLines]; pos = -1; for (int i=0; i<numLines; i++) { coordLines[i] = pos + 1; pos = coordString.indexOf("\n", pos + 1); } // extract information about ScalarType locations within MathType strings int len = v.size(); int unique = len - dupl; MathTypes = new ScalarType[unique]; Scalars = new String[unique]; ScW = new int[unique]; ScP = new Vector[unique]; Hashtable nameToIndex = new Hashtable(); int s = 0; boolean inCoord = false; pos = -1; for (int i=0; i<len; i++) { ScalarType type = (ScalarType) v.elementAt(i); String name = type.getName(); if (v.indexOf(type) == i) { // first occurrence of this ScalarType MathTypes[s] = type; Scalars[s] = name; ScW[s] = charWidth * name.length(); ScP[s] = new Vector(); nameToIndex.put(name, new Integer(s)); s++; } // determine position of ScalarType and add to position vector int index = ((Integer) nameToIndex.get(name)).intValue(); MDTuple tuple = null; if (!inCoord) { pos = typeString.indexOf(name, pos + 1); if (pos == -1) inCoord = true; else { Point p = indexToPoint(pos, mathLines, charWidth, ScH); tuple = new MDTuple(p.x, p.y, false); } } if (inCoord) { pos = coordString.indexOf(name, pos + 1); Point p = indexToPoint(pos, coordLines, charWidth, ScH); tuple = new MDTuple(p.x, p.y, true); } ScP[index].add(tuple); } // alphabetize Scalars list sort(0, unique - 1); // mark whether there are CoordinateSystem references CoordRefs = !coordString.equals(""); // set up "MathType" label JLabel l0 = new JLabel("MathType:"); l0.setAlignmentX(JLabel.CENTER_ALIGNMENT); contentPane.add(l0); // set up top panel JPanel topPanel = new JPanel(); topPanel.setLayout(new BoxLayout(topPanel, BoxLayout.X_AXIS)); contentPane.add(topPanel); contentPane.add(Box.createRigidArea(new Dimension(0, 5))); // set up "CoordinateSystem references" label if (CoordRefs) { JLabel l1 = new JLabel("CoordinateSystem references:"); l1.setAlignmentX(JLabel.CENTER_ALIGNMENT); contentPane.add(l1); } // set up second top panel JPanel topPanel2 = new JPanel(); topPanel2.setLayout(new BoxLayout(topPanel2, BoxLayout.X_AXIS)); contentPane.add(topPanel2); // draw the pretty-print MathType sequence to an Image (slow!) final BufferedImage ppImg = new BufferedImage( StrSize.width, StrSize.height, BufferedImage.TYPE_INT_RGB); Graphics g = ppImg.getGraphics(); g.setFont(Mono); g.setColor(Color.white); g.fillRect(0, 0, StrSize.width, StrSize.height); g.setColor(Color.black); for (int i=0; i<mathLines.length; i++) { int start = mathLines[i]; int end = (i < mathLines.length - 1 ? mathLines[i + 1] - 1 : typeString.length()); String line = typeString.substring(start, end); g.drawString(line, 5, (ScH + 2) * (i + 1)); } g.dispose(); // set up MathType canvas MathCanvas = new JComponent() { public void paint(Graphics g2) { // draw pretty-print MathType using its Image g2.drawImage(ppImg, 0, 0, this); int ndx = MathList.getSelectedIndex(); if (ndx >= 0) { for (int i=0; i<ScP[ndx].size(); i++) { MDTuple tuple = (MDTuple) ScP[ndx].elementAt(i); if (!tuple.b) { g2.setFont(Mono); String ss = Scalars[ndx]; int x = tuple.x; int y = tuple.y; g2.setColor(Color.blue); g2.fillRect(tuple.x, tuple.y, ScW[ndx], ScH); g2.setColor(Color.white); g2.drawString(ss, tuple.x, tuple.y + ScH - 4); } } } // work-around for JDK 1.3 bug Dimension d = MathCanvas.getSize(); g2.setColor(Color.white); g2.fillRect(StrSize.width, 0, d.width, d.height); g2.fillRect(0, StrSize.height, StrSize.width, d.height); } }; MathCanvas.setMinimumSize(StrSize); MathCanvas.setPreferredSize(StrSize); MathCanvas.addMouseListener(this); MathCanvas.setBackground(Color.white); // set up MathCanvas's ScrollPane MathCanvasView = new JScrollPane(MathCanvas); MathCanvasView.setMinimumSize(new Dimension(0, 0)); int prefMCHeight = StrSize.height + 10; if (prefMCHeight < 70) prefMCHeight = 70; int maxMCHeight = Toolkit.getDefaultToolkit().getScreenSize().height / 2; if (prefMCHeight > maxMCHeight) prefMCHeight = maxMCHeight; MathCanvasView.setPreferredSize(new Dimension(0, prefMCHeight)); MathCanvasView.setBackground(Color.white); JScrollBar horiz = MathCanvasView.getHorizontalScrollBar(); JScrollBar verti = MathCanvasView.getVerticalScrollBar(); horiz.setBlockIncrement(5 * ScH + 10); horiz.setUnitIncrement(ScH+2); verti.setBlockIncrement(5 * ScH + 10); verti.setUnitIncrement(ScH+2); topPanel.add(Box.createRigidArea(new Dimension(5, 0))); JPanel whitePanel = new JPanel(); whitePanel.setBackground(Color.white); whitePanel.setLayout(new BoxLayout(whitePanel, BoxLayout.X_AXIS)); whitePanel.add(MathCanvasView); topPanel.add(whitePanel); topPanel.add(Box.createRigidArea(new Dimension(5, 0))); // set up description label description = new JLabel(" "); JPanel descPanel = new JPanel(); descPanel.setLayout(new BoxLayout(descPanel, BoxLayout.X_AXIS)); contentPane.add(descPanel); contentPane.add(Box.createRigidArea(new Dimension(0, 15))); descPanel.add(description); // set up lower panel JPanel lowerPanel = new JPanel(); lowerPanel.setLayout(new BoxLayout(lowerPanel, BoxLayout.X_AXIS)); contentPane.add(lowerPanel); contentPane.add(Box.createRigidArea(new Dimension(0, 5))); // set up far-left-side panel JPanel flsPanel = new JPanel(); flsPanel.setLayout(new BoxLayout(flsPanel, BoxLayout.Y_AXIS)); lowerPanel.add(Box.createRigidArea(new Dimension(5, 0))); lowerPanel.add(flsPanel); // only do CoordSys stuff if there are CoordinateSystem references if (CoordRefs) { // draw the pretty-print CoordinateSystem references to an Image (slow!) final BufferedImage csImg = new BufferedImage( CoordSize.width, CoordSize.height, BufferedImage.TYPE_INT_RGB); g = csImg.getGraphics(); g.setFont(Mono); g.setColor(Color.white); g.fillRect(0, 0, CoordSize.width, CoordSize.height); g.setColor(Color.black); for (int i=0; i<coordLines.length; i++) { int start = coordLines[i]; int end = (i < coordLines.length - 1 ? coordLines[i + 1] - 1 : coordString.length() - 1); String line = coordString.substring(start, end); g.drawString(line, 5, (ScH + 2) * (i + 1)); } g.dispose(); // set up CoordinateSystem references canvas CoordCanvas = new JComponent() { public void paint(Graphics g2) { // draw pretty-print CoordinateSystem reference list using its Image g2.drawImage(csImg, 0, 0, this); int ndx = MathList.getSelectedIndex(); if (ndx >= 0) { for (int i=0; i<ScP[ndx].size(); i++) { MDTuple tuple = (MDTuple) ScP[ndx].elementAt(i); if (tuple.b) { g2.setFont(Mono); String ss = Scalars[ndx]; int x = tuple.x; int y = tuple.y; g2.setColor(Color.blue); g2.fillRect(tuple.x, tuple.y, ScW[ndx], ScH); g2.setColor(Color.white); g2.drawString(ss, tuple.x, tuple.y + ScH - 4); } } } // work-around for JDK 1.3 bug Dimension d = CoordCanvas.getSize(); g2.setColor(Color.white); g2.fillRect(CoordSize.width, 0, d.width, d.height); g2.fillRect(0, CoordSize.height, CoordSize.width, d.height); } }; CoordCanvas.setMinimumSize(CoordSize); CoordCanvas.setPreferredSize(CoordSize); CoordCanvas.addMouseListener(this); CoordCanvas.setBackground(Color.white); // set up CoordCanvas's ScrollPane CoordCanvasView = new JScrollPane(CoordCanvas); CoordCanvasView.setMinimumSize(new Dimension(0, 0)); CoordCanvasView.setPreferredSize(new Dimension(0, CoordSize.height)); int prefCCHeight = CoordSize.height + 10; if (prefCCHeight < 70) prefCCHeight = 70; int maxCCHeight = Toolkit.getDefaultToolkit().getScreenSize().height / 2; if (prefCCHeight > maxCCHeight) prefCCHeight = maxCCHeight; CoordCanvasView.setPreferredSize(new Dimension(0, prefCCHeight)); CoordCanvasView.setBackground(Color.white); horiz = CoordCanvasView.getHorizontalScrollBar(); verti = CoordCanvasView.getVerticalScrollBar(); horiz.setBlockIncrement(5 * ScH + 10); horiz.setUnitIncrement(ScH + 2); verti.setBlockIncrement(5 * ScH + 10); verti.setUnitIncrement(ScH + 2); topPanel2.add(Box.createRigidArea(new Dimension(5, 0))); JPanel whitePanel2 = new JPanel(); whitePanel2.setBackground(Color.white); whitePanel2.setLayout(new BoxLayout(whitePanel2, BoxLayout.X_AXIS)); whitePanel2.add(CoordCanvasView); topPanel2.add(whitePanel2); topPanel2.add(Box.createRigidArea(new Dimension(5, 0))); } // set up left-side panel JPanel lsPanel = new JPanel(); lsPanel.setLayout(new BoxLayout(lsPanel, BoxLayout.Y_AXIS)); lowerPanel.add(Box.createRigidArea(new Dimension(5, 0))); lowerPanel.add(lsPanel); // begin set up "current mappings" list CurMaps = new DefaultListModel(); int num = MathTypes.length; CurMaps.ensureCapacity(num * MapWidth * MapHeight); CurrentMaps = new JList(CurMaps); // set up "map from" list Maps = new boolean[num][MapHeight][MapWidth]; CurMapLabel = new String[num][MapHeight][MapWidth]; for (int i=0; i<num; i++) { for (int j=0; j<MapHeight; j++) { for (int k=0; k<MapWidth; k++) { Maps[i][j][k] = false; CurMapLabel[i][j][k] = Scalars[i] + " -> " + MapNames[j][k]; if (startMaps != null) { for (int m=0; m<startMaps.length; m++) { if (startMaps[m].getScalar().equals(MathTypes[i]) && startMaps[m].getDisplayScalar().equals(MapTypes[j][k])) { Maps[i][j][k] = true; CurMaps.addElement(CurMapLabel[i][j][k]); } } } } } } MathList = new JList(Scalars); MathList.addListSelectionListener(this); MathList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); JScrollPane mathListView = new JScrollPane(MathList) { public Dimension getMinimumSize() { return new Dimension(0, 40 * MapHeight + 64); } public Dimension getPreferredSize() { return new Dimension(200, 40 * MapHeight + 64); } public Dimension getMaximumSize() { return new Dimension(Integer.MAX_VALUE, 40 * MapHeight + 64); } }; JLabel l2 = new JLabel("Map from:"); l2.setAlignmentX(JLabel.CENTER_ALIGNMENT); lsPanel.add(l2); lsPanel.add(mathListView); lowerPanel.add(Box.createRigidArea(new Dimension(5, 0))); // set up center panel JPanel centerPanel = new JPanel(); centerPanel.setLayout(new BoxLayout(centerPanel, BoxLayout.Y_AXIS)); lowerPanel.add(centerPanel); lowerPanel.add(Box.createRigidArea(new Dimension(5, 0))); JLabel l3 = new JLabel("Map to:"); l3.setAlignmentX(JLabel.CENTER_ALIGNMENT); centerPanel.add(l3); // finish loading DRT if necessary if (!Inited) initDialog(); MediaTracker mtracker = new MediaTracker(this); mtracker.addImage(DRT, 0); try { mtracker.waitForID(0); } catch (InterruptedException exc) { } // flag illegal DisplayRealTypes for (int i=0; i<MapHeight; i++) { for (int j=0; j<MapWidth; j++) Illegal[i][j] = MapTypes[i][j] == null; } if (!AllowAlpha) { for (int i=0; i<AlphaMaps.length; i++) { Point p = AlphaMaps[i]; Illegal[p.x][p.y] = true; } } if (!Allow3D) { for (int i=0; i<ThreeDMaps.length; i++) { Point p = ThreeDMaps[i]; Illegal[p.x][p.y] = true; } } // copy DRT into MapTo and black out icons of illegal DisplayRealTypes MapTo = new BufferedImage(40 * MapWidth, 40 * MapHeight, BufferedImage.TYPE_INT_RGB); Graphics gr = MapTo.getGraphics(); gr.drawImage(DRT, 0, 0, this); for (int i=0; i<MapHeight; i++) { for (int j=0; j<MapWidth; j++) { if (Illegal[i][j]) eraseBox(j, i, gr); } } gr.dispose(); // set up "map to" canvas DisplayCanvas = new JComponent() { public void paint(Graphics g2) { g2.drawImage(MapTo, 0, 0, this); int ndx = MathList.getSelectedIndex(); if (ndx >= 0) { for (int col=0; col<MapWidth; col++) { for (int row=0; row<MapHeight; row++) { if (Maps[ndx][row][col]) highlightBox(col, row, g2); } } } } public Dimension getMinimumSize() { return new Dimension(40 * MapWidth, 40 * MapHeight); } public Dimension getPreferredSize() { return new Dimension(40 * MapWidth, 40 * MapHeight); } public Dimension getMaximumSize() { return new Dimension(40 * MapWidth, 40 * MapHeight); } }; DisplayCanvas.addMouseListener(this); centerPanel.add(DisplayCanvas); centerPanel.add(Box.createRigidArea(new Dimension(0, 5))); // set up button panel 1 JPanel b1Panel = new JPanel(); b1Panel.setLayout(new BoxLayout(b1Panel, BoxLayout.X_AXIS)); centerPanel.add(b1Panel); centerPanel.add(Box.createRigidArea(new Dimension(0, 5))); b1Panel.add(Box.createHorizontalGlue()); // set up "clear all" button JButton clearAll = new JButton("Clear all"); clearAll.setAlignmentX(JButton.CENTER_ALIGNMENT); clearAll.setToolTipText("Clear all mappings from mappings box"); clearAll.setActionCommand("all"); clearAll.addActionListener(this); b1Panel.add(clearAll); b1Panel.add(Box.createRigidArea(new Dimension(5, 0))); // set up "clear selected" button JButton clearSel = new JButton("Clear selected"); clearSel.setAlignmentX(JButton.CENTER_ALIGNMENT); clearSel.setToolTipText("Clear selected mappings from mappings box"); clearSel.setActionCommand("sel"); clearSel.addActionListener(this); b1Panel.add(clearSel); b1Panel.add(Box.createHorizontalGlue()); // set up button panel 2 JPanel b2Panel = new JPanel(); b2Panel.setLayout(new BoxLayout(b2Panel, BoxLayout.X_AXIS)); centerPanel.add(b2Panel); b2Panel.add(Box.createHorizontalGlue()); // set up done button JButton done = new JButton("Done"); done.setAlignmentX(JButton.CENTER_ALIGNMENT); done.setToolTipText("Apply selected mappings"); done.setActionCommand("done"); done.addActionListener(this); b2Panel.add(done); b2Panel.add(Box.createRigidArea(new Dimension(5, 0))); // set up cancel button JButton cancel = new JButton("Cancel"); cancel.setAlignmentX(JButton.CENTER_ALIGNMENT); cancel.setToolTipText("Close dialog box without applying mappings"); cancel.setActionCommand("cancel"); cancel.addActionListener(this); b2Panel.add(cancel); b2Panel.add(Box.createRigidArea(new Dimension(5, 0))); // set up detect button JButton detect = new JButton("Detect"); detect.setAlignmentX(JButton.CENTER_ALIGNMENT); detect.setToolTipText("Automatically identify some good mappings"); detect.setActionCommand("detect"); detect.addActionListener(this); b2Panel.add(detect); b2Panel.add(Box.createHorizontalGlue()); // set up right-side panel JPanel rsPanel = new JPanel(); rsPanel.setLayout(new BoxLayout(rsPanel, BoxLayout.Y_AXIS)); lowerPanel.add(rsPanel); lowerPanel.add(Box.createRigidArea(new Dimension(5, 0))); // finish set up "current mappings" list CurrentMaps.addListSelectionListener(this); CurrentMapsView = new JScrollPane(CurrentMaps) { public Dimension getMinimumSize() { return new Dimension(0, 40 * MapHeight + 64); } public Dimension getPreferredSize() { return new Dimension(200, 40 * MapHeight + 64); } public Dimension getMaximumSize() { return new Dimension(Integer.MAX_VALUE, 40 * MapHeight + 64); } }; JLabel l4 = new JLabel("Current maps:"); l4.setAlignmentX(JLabel.CENTER_ALIGNMENT); rsPanel.add(l4); rsPanel.add(CurrentMapsView); } /** * Ensure mapping dialog is not larger than the screen size. */ public Dimension getPreferredSize() { Dimension max = super.getPreferredSize(); // take Windows Start bar into account String os = System.getProperty("os.name"); final int pad = os.startsWith("Windows") ? 60 : 20; // ensure dialog size does not exceed screen size int w = max.width; int h = max.height; Dimension screen = Toolkit.getDefaultToolkit().getScreenSize(); if (w > screen.width - pad) w = screen.width - pad; if (h > screen.height - pad) h = screen.height - pad; return new Dimension(w, h); } /** * Displays the dialog in the center of the screen. */ public void display() { pack(); Util.centerWindow(this); setVisible(true); } /** * Gets the mappings selected by the user. */ public ScalarMap[] getMaps() { return ScalarMaps; } /** * Gets whether the user pressed the Ok button. */ public boolean okPressed() { return Confirm; } /** * Recursive quick-sort routine for alphabetizing scalars. */ private void sort(int lo0, int hi0) { if (hi0 < lo0) return; int lo = lo0; int hi = hi0; String mid = Scalars[(lo0 + hi0) / 2].toLowerCase(); while (lo <= hi) { while (lo < hi0 && Scalars[lo].toLowerCase().compareTo(mid) < 0) ++lo; while (hi > lo0 && Scalars[hi].toLowerCase().compareTo(mid) > 0) --hi; if (lo <= hi) { // MathTypes[lo] <-> MathTypes[hi] ScalarType st = MathTypes[lo]; MathTypes[lo] = MathTypes[hi]; MathTypes[hi] = st; // Scalars[lo] <-> Scalars[hi] String s = Scalars[lo]; Scalars[lo] = Scalars[hi]; Scalars[hi] = s; // ScW[lo] <-> ScW[hi] int i = ScW[lo]; ScW[lo] = ScW[hi]; ScW[hi] = i; // ScP[lo] <-> ScP[hi] Vector v = ScP[lo]; ScP[lo] = ScP[hi]; ScP[hi] = v; lo++; hi--; } } if (lo0 < hi) sort(lo0, hi); if (lo < hi0) sort(lo, hi0); } /** * Clears a box in the "map to" canvas. */ void eraseBox(int col, int row, Graphics g) { int x = 40 * col; int y = 40 * row; g.setColor(Color.black); for (int i=0; i<40; i+=2) { g.drawLine(x, y + i, x + i, y); g.drawLine(x + i, y + 38, x + 38, y + i); } } /** * Highlights a box in the "map to" canvas. */ void highlightBox(int col, int row, Graphics g) { int x = 40 * col; int y = 40 * row; final int n1 = 11; final int n2 = 29; g.setColor(Color.blue); g.drawRect(x, y, 39, 39); g.drawLine(x, y + n1, x + n1, y); g.drawLine(x, y + n2, x + n2, y); g.drawLine(x + n1, y + 39, x + 39, y + n1); g.drawLine(x + n2, y + 39, x + 39, y + n2); g.drawLine(x + n2, y, x + 39, y + n1); g.drawLine(x + n1, y, x + 39, y + n2); g.drawLine(x, y + n1, x + n2, y + 39); g.drawLine(x, y + n2, x + n1, y + 39); } /** * Clears all maps from the current mappings list. */ private void clearAll() { CurrentMaps.clearSelection(); // work-around for nasty swing bug CurMaps.removeAllElements(); for (int i=0; i<CurMapLabel.length; i++) { for (int j=0; j<MapHeight; j++) { for (int k=0; k<MapWidth; k++) Maps[i][j][k] = false; } } } /** * Updates the description label to match the currently selected ScalarType. */ private void updateDescriptionLabel(int i) { ScalarType st = MathTypes[i]; String desc = " " + Scalars[i]; if (st instanceof RealType) { RealType r = (RealType) st; Unit unit = r.getDefaultUnit(); Set set = r.getDefaultSet(); String u = unit == null ? "none" : unit.toString(); String s; if (set == null) s = "none"; else { String setType = set.getClass().getName(); int index = setType.lastIndexOf("."); if (index >= 0) setType = setType.substring(index + 1); int dim = set.getDimension(); s = setType + "(" + dim + ")"; } desc = desc + ": Unit=" + u + "; Set=" + s; } else { desc = desc + " (text)"; } description.setText(desc); } /** * Handles button press events. */ public void actionPerformed(ActionEvent e) { String cmd = e.getActionCommand(); synchronized (Lock) { if (cmd.equals("all")) { // clear all if (CurMaps.getSize() > 0) { clearAll(); // update components Graphics g = DisplayCanvas.getGraphics(); DisplayCanvas.paint(g); g.dispose(); CurrentMaps.repaint(); CurrentMapsView.validate(); } } else if (cmd.equals("sel")) { // clear selected int[] ndx = CurrentMaps.getSelectedIndices(); int len = ndx.length; for (int x=len-1; x>=0; x--) { String s = (String) CurMaps.getElementAt(ndx[x]); boolean looking = true; for (int i=0; i<CurMapLabel.length && looking; i++) { for (int j=0; j<MapHeight && looking; j++) { for (int k=0; k<MapWidth && looking; k++) { if (CurMapLabel[i][j][k] == s) { Maps[i][j][k] = false; looking = false; } } } } // take map off list CurMaps.removeElementAt(ndx[x]); } if (len > 0) { // update components Graphics g = DisplayCanvas.getGraphics(); DisplayCanvas.paint(g); g.dispose(); CurrentMaps.repaint(); CurrentMapsView.validate(); } } else if (cmd.equals("detect")) { ScalarMap[] maps = DataUtility.guessMaps(Types, Allow3D); if (CurMaps.getSize() > 0) clearAll(); if (maps != null) { for (int i=0; i<MathTypes.length; i++) { for (int j=0; j<MapHeight; j++) { for (int k=0; k<MapWidth; k++) { for (int m=0; m<maps.length; m++) { if (maps[m].getScalar() == MathTypes[i] && maps[m].getDisplayScalar() == MapTypes[j][k]) { Maps[i][j][k] = true; CurMaps.addElement(CurMapLabel[i][j][k]); } } } } } } // update components Graphics g = DisplayCanvas.getGraphics(); DisplayCanvas.paint(g); g.dispose(); CurrentMaps.repaint(); CurrentMapsView.validate(); } else if (cmd.equals("done")) { boolean okay = true; int size = CurMaps.getSize(); ScalarMaps = new ScalarMap[size]; int s = 0; for (int i=0; i<CurMapLabel.length; i++) { for (int j=0; j<MapHeight; j++) { for (int k=0; k<MapWidth; k++) { if (Maps[i][j][k]) { try { ScalarMaps[s++] = new ScalarMap(MathTypes[i], MapTypes[j][k]); } catch (VisADException exc) { okay = false; JOptionPane.showMessageDialog(this, "The mapping (" + Scalars[i] + " -> " + MapNames[j][k] + ") is not valid.", "Illegal mapping", JOptionPane.ERROR_MESSAGE); } } } } } if (okay) { Confirm = true; setVisible(false); } } else if (cmd.equals("cancel")) setVisible(false); } } /** * Handles list selection change events. */ public void valueChanged(ListSelectionEvent e) { synchronized (Lock) { if (!e.getValueIsAdjusting()) { if ((JList) e.getSource() == MathList) { Graphics g = DisplayCanvas.getGraphics(); DisplayCanvas.paint(g); g.dispose(); int i = MathList.getSelectedIndex(); MDTuple tuple = (MDTuple) ScP[i].elementAt(0); Rectangle r = new Rectangle(tuple.x, tuple.y, ScW[i], ScH); MathList.ensureIndexIsVisible(i); if (tuple.b) CoordCanvas.scrollRectToVisible(r); else MathCanvas.scrollRectToVisible(r); MathCanvas.repaint(); if (CoordRefs) CoordCanvas.repaint(); updateDescriptionLabel(i); } } } } /** * Handles mouse clicks in the MathType window and "map to" canvas. */ public void mousePressed(MouseEvent e) { synchronized (Lock) { Component c = e.getComponent(); if (c == MathCanvas) { Point p = e.getPoint(); for (int i=0; i<Scalars.length; i++) { for (int j=0; j<ScP[i].size(); j++) { MDTuple tuple = (MDTuple) ScP[i].elementAt(j); if (!tuple.b) { Rectangle r = new Rectangle(tuple.x, tuple.y, ScW[i], ScH); if (r.contains(p)) { // highlight clicked ScalarType MathList.setSelectedIndex(i); MathList.ensureIndexIsVisible(i); MathCanvas.scrollRectToVisible(r); updateDescriptionLabel(i); return; } } } } } else if (c == DisplayCanvas) { int col = e.getX() / 40; int row = e.getY() / 40; int ndx = MathList.getSelectedIndex(); if (ndx >= 0 && row >= 0 && col >= 0 && row < MapHeight && col < MapWidth && !Illegal[row][col]) { Maps[ndx][row][col] = !Maps[ndx][row][col]; if (Maps[ndx][row][col]) { CurMaps.addElement(CurMapLabel[ndx][row][col]); } else { CurrentMaps.clearSelection(); CurMaps.removeElement(CurMapLabel[ndx][row][col]); } // redraw DisplayCanvas Graphics g = DisplayCanvas.getGraphics(); g.setClip(40 * col, 40 * row, 41, 41); DisplayCanvas.paint(g); g.dispose(); // redraw CurrentMaps CurrentMaps.repaint(); CurrentMapsView.validate(); } } else if (c == CoordCanvas) { Point p = e.getPoint(); for (int i=0; i<Scalars.length; i++) { for (int j=0; j<ScP[i].size(); j++) { MDTuple tuple = (MDTuple) ScP[i].elementAt(j); if (tuple.b) { Rectangle r = new Rectangle(tuple.x, tuple.y, ScW[i], ScH); if (r.contains(p)) { MathList.setSelectedIndex(i); MathList.ensureIndexIsVisible(i); CoordCanvas.scrollRectToVisible(r); return; } } } } } } } /** * Unused MouseListener method. */ public void mouseEntered(MouseEvent e) { } /** * Unused MouseListener method. */ public void mouseExited(MouseEvent e) { } /** * Unused MouseListener method. */ public void mouseClicked(MouseEvent e) { } /** * Unused MouseListener method. */ public void mouseReleased(MouseEvent e) { } /** * Helper class representing an (int, int, boolean) tuple. */ class MDTuple { int x, y; boolean b; MDTuple(int x, int y, boolean b) { this.x = x; this.y = y; this.b = b; } } }