/* File: FormContainer.java * Copyright (C) 2002-2003 The University of Iowa * Created by: Jeremy Faden * Jessica Swanner * Edward E. West * * This file is part of the das2 library. * * das2 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program 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 for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package org.das2.dasml; import org.das2.DasApplication; import org.das2.DasException; import org.das2.graph.DasCanvas; import org.das2.graph.dnd.TransferableCanvas; import org.w3c.dom.Document; import org.w3c.dom.Element; import javax.swing.*; import javax.swing.border.EmptyBorder; import javax.swing.border.EtchedBorder; import javax.swing.border.TitledBorder; import java.awt.*; import java.awt.datatransfer.DataFlavor; import java.awt.datatransfer.Transferable; import java.awt.datatransfer.UnsupportedFlavorException; import java.io.IOException; import java.util.List; import org.das2.DasNameException; import org.das2.components.propertyeditor.Editable; /** * A subclass of JPanel to override the default Beans properties of * a JPanel * * @author Edward West */ public abstract class FormContainer extends JPanel implements Editable, FormComponent { float horizontalComponentAlignment = JComponent.LEFT_ALIGNMENT; final float verticalComponentAlignment = JComponent.TOP_ALIGNMENT; boolean onHover = false; org.das2.util.DnDSupport dndSupport; List flavorList; int dropPosition; boolean editable; /** * The axis along which child components will be laid out. */ private int axis = BoxLayout.X_AXIS; /** * The titled displayed along the panel border. */ private String borderTitle = ""; /** * If true, the panel has an etched border. */ private boolean hasBorder = false; /** * Empty constructor for use with super classes. */ protected FormContainer() { setLayout(new BoxLayout(this, BoxLayout.X_AXIS)); setBorder(new NoBorder()); } /** * Returns true if this panel has a border */ public boolean hasBorder() { return hasBorder; } /** * If the given boolean is true then the panel will be * given a border, if is does not already have one. If the given * boolean is false then the panel's border will be * removed, if it has one. */ public void setHasBorder(boolean b) { if (hasBorder != b) { hasBorder = b; if (hasBorder) { EtchedBorder etchedBorder = new EtchedBorder(); TitledBorder titledBorder = new TitledBorder(etchedBorder, borderTitle, TitledBorder.LEFT, TitledBorder.TOP); setBorder(titledBorder); } else { setBorder(new NoBorder()); } } } /** * Returns the title that is displayed along the top of this panels border * (if it has one). */ public String getBorderTitle() { return borderTitle; } /** * Set the title that is displayed along the top of this panels border * (if it has one) to the given String. */ public void setBorderTitle(String s) { if (!s.equals(borderTitle)) { borderTitle = s; if (hasBorder) { EtchedBorder etchedBorder = new EtchedBorder(); TitledBorder titledBorder = new TitledBorder(etchedBorder, borderTitle, TitledBorder.LEFT, TitledBorder.TOP); setBorder(titledBorder); } } } /** Adds the specified component to this container at the specified * index. This method also notifies the layout manager to add * the component to this container's layout using the specified * constraints object via the addLayoutComponent * method. The constraints are * defined by the particular layout manager being used. For * example, the BorderLayout class defines five * constraints: BorderLayout.NORTH, * BorderLayout.SOUTH, BorderLayout.EAST, * BorderLayout.WEST, and BorderLayout.CENTER. * *

Note that if the component already exists * in this container or a child of this container, * it is removed from that container before * being added to this container. *

* This is the method to override if a program needs to track * every add request to a container as all other add methods defer * to this one. An overriding method should * usually include a call to the superclass's version of the method: *

*

* super.addImpl(comp, constraints, index) *
*

* @param comp the component to be added * @param constraints an object expressing layout constraints * for this component * @param index the position in the container's list at which to * insert the component, where -1 * means append to the end * @exception IllegalArgumentException if index is invalid * @exception IllegalArgumentException if adding the container's parent * to itself * @exception IllegalArgumentException if adding a window to a container * @see java.awt.Container#add(Component) * @see java.awt.Container#add(Component, int) * @see java.awt.Container#add(Component, java.lang.Object) * @see java.awt.LayoutManager * @see java.awt.LayoutManager2 */ protected void addImpl(Component comp, Object constraints, int index) { if (comp instanceof JComponent) { ((JComponent)comp).setAlignmentX(horizontalComponentAlignment); ((JComponent)comp).setAlignmentY(verticalComponentAlignment); } super.addImpl(comp, constraints, index); if (comp instanceof FormComponent) { FormComponent fc = (FormComponent)comp; org.das2.util.DnDSupport childDnDSupport = fc.getDnDSupport(); if (childDnDSupport != null) { childDnDSupport.setParent(dndSupport); } fc.setEditingMode(getEditingMode()); } packFormWindowAnscestor(); } private void packFormWindowAnscestor() { if (isDisplayable()) { if (this instanceof FormWindow) { ((FormWindow)this).pack(); } else { FormWindow fw = (FormWindow)SwingUtilities.getAncestorOfClass(FormWindow.class, this); if (fw != null) { fw.pack(); } } } } public void removeAll() { int ncomponents = getComponentCount(); for (int index = ncomponents-1; index >= 0; index--) { remove(index); } } public void remove(int index) { super.remove(index); packFormWindowAnscestor(); } public void remove(Component c) { super.remove(c); packFormWindowAnscestor(); } public void setDirection(Orientation direction) { if (direction == Orientation.HORIZONTAL) { if (axis != BoxLayout.X_AXIS) { axis = BoxLayout.X_AXIS; setLayout(new BoxLayout(this, axis)); getForm().validate(); } } else if (axis != BoxLayout.Y_AXIS) { axis = BoxLayout.Y_AXIS; setLayout(new BoxLayout(this, axis)); revalidate(); } } public Orientation getDirection() { if (axis == BoxLayout.X_AXIS) return Orientation.HORIZONTAL; if (axis == BoxLayout.Y_AXIS) return Orientation.VERTICAL; throw new AssertionError("Invalid value for axis"); } /** * Returns the FormBase object this component is associated with, or null */ public FormBase getForm() { FormComponent parent = (FormComponent)getParent(); if (parent == null) { return null; } return parent.getForm(); } /** * Returns true if this component is in an editing state. * @return true if this component is in an editing state. */ public boolean getEditingMode() { return editable; } public void paint(Graphics g) { super.paint(g); if (onHover) { Graphics2D g2 = (Graphics2D)g.create(); Stroke thick = new BasicStroke(3.0f); g2.setStroke(thick); g2.setPaint(Color.GRAY); g2.drawRect(1, 1, getWidth() - 2, getHeight() - 2); g2.setPaint(Color.ORANGE); if (getDirection() == Orientation.HORIZONTAL) { g2.drawLine(dropPosition, 4, dropPosition, getHeight() - 4); } else { g2.drawLine(4, dropPosition, getHeight() - 4, dropPosition); } g2.dispose(); } } public void setEditingMode(boolean b) { if (editable == b) return; editable = b; int componentCount = getComponentCount(); for (int i = 0; i < componentCount; i++) { if (getComponent(i) instanceof FormComponent) { ((FormComponent)getComponent(i)).setEditingMode(b); } } } private int[] getInsertionPositions() { int componentCount = getComponentCount(); int[] positions = new int[componentCount + 1]; if (getDirection() == Orientation.HORIZONTAL) { positions[0] = 2; for (int i = 1; i <= componentCount; i++) { Component c = getComponent(i - 1); positions[i] = c.getX() + c.getWidth() + 1; } } else { positions[0] = 2; for (int i = 1; i <= componentCount; i++) { Component c = getComponent(i - 1); positions[i] = c.getY() + c.getHeight() + 1; } } return positions; } private int getInsertionPosition(int p) { int[] positions = getInsertionPositions(); int insertionPosition = 0; int dp = Integer.MAX_VALUE; for (int i = 0; i < positions.length; i++) { int delta = Math.abs(p - positions[i]); if (delta < dp) { dp = delta; insertionPosition = positions[i]; } } return insertionPosition; } private int getInsertionIndex(int p) { int[] positions = getInsertionPositions(); int insertionIndex = 0; int dp = Integer.MAX_VALUE; for (int i = 0; i < positions.length; i++) { int delta = Math.abs(p - positions[i]); if (delta < dp) { dp = delta; insertionIndex = i; } } return insertionIndex; } public boolean startDrag(int x, int y, int action, java.awt.event.MouseEvent evt) { for (int i = 0; i < getComponentCount(); i++) { if (getComponent(i).getBounds().contains(x, y)) { dndSupport.startDrag(x, y, action, evt); return true; } } return false; } public Dimension getPreferredSize() { if (getComponentCount() == 0) { return new Dimension(100, 100); } else { return super.getPreferredSize(); } } public Dimension getMinimumSize() { if (getComponentCount() == 0) { return new Dimension(100, 100); } else { return super.getMinimumSize(); } } public Dimension getMaximumSize() { Dimension pref = getPreferredSize(); Dimension max = super.getMaximumSize(); max.width = Math.max(max.width, 100); max.height = pref.height; return max; } public String getDasName() { return null; } public void setDasName(String name) throws org.das2.DasNameException { throw new org.das2.DasNameException(); } public void deregisterComponent() { for (int index = 0; index < getComponentCount(); index++) { Component c = getComponent(index); if (c instanceof FormComponent) { ((FormComponent)c).deregisterComponent(); } } } public DasApplication getDasApplication() { Container p = getParent(); if (p instanceof FormComponent) { return ((FormComponent)p).getDasApplication(); } else { return null; } } public void registerComponent() throws DasException { try { for (int index = 0; index < getComponentCount(); index++) { Component c = getComponent(index); if (c instanceof FormComponent) { ((FormComponent)c).registerComponent(); } } } catch (DasNameException dne) { deregisterComponent(); throw dne; } } class NoBorder extends EmptyBorder { Color color = Color.GRAY; NoBorder() { super(5, 5, 5, 5); } public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) { if (editable) { g.setColor(color); g.drawRect(x + 2, y + 2, width - 4, height - 4); } } } protected class ContainerDnDSupport extends org.das2.util.DnDSupport { ContainerDnDSupport(org.das2.util.DnDSupport parent) { super(FormContainer.this, java.awt.dnd.DnDConstants.ACTION_COPY_OR_MOVE, parent); } protected int canAccept(DataFlavor[] flavors, int x, int y, int action) { if (flavorList != null && getEditingMode()) { for (int i = 0; i < flavors.length; i++) { if (flavorList.contains(flavors[i])) { onHover = true; if (getDirection() == Orientation.HORIZONTAL) { dropPosition = getInsertionPosition(x); } else { dropPosition = getInsertionPosition(y); } repaint(); return action; } } } return -1; } protected void done() { onHover = false; repaint(); } protected boolean importData(Transferable t, int x, int y, int action) { boolean success = false; try { int insertionIndex; if (getDirection() == Orientation.HORIZONTAL) { insertionIndex = getInsertionIndex(x); } else { insertionIndex = getInsertionIndex(y); } Component c = null; if (t.isDataFlavorSupported(TransferableFormComponent.COMPONENT_FLAVOR)) { c = (Component)t.getTransferData(TransferableFormComponent.COMPONENT_FLAVOR); } else if (t.isDataFlavorSupported(TransferableCanvas.CANVAS_FLAVOR)) { c = (Component)t.getTransferData(TransferableCanvas.CANVAS_FLAVOR); } else if (t.isDataFlavorSupported(TransferableFormComponent.DASML_FRAGMENT_FLAVOR)) { c = getComponentFromDasMLFragment((String)t.getTransferData(TransferableFormComponent.DASML_FRAGMENT_FLAVOR)); } if (c != null) { if (!(c instanceof FormTab) && !(c instanceof FormWindow)) { if (c != FormContainer.this && !SwingUtilities.isDescendingFrom(FormContainer.this, c)) { if (c.getParent() == FormContainer.this) { int cIndex = -1; int componentCount = getComponentCount(); for (int i = 0; i < componentCount; i++) { if (getComponent(i) == c) { cIndex = i; break; } } if (insertionIndex > cIndex) { insertionIndex--; } remove(cIndex); add(c, insertionIndex); success = true; } else { add(c, insertionIndex); success = true; } } } } } catch (UnsupportedFlavorException ufe) { } catch (IOException ioe) { } if (success) { revalidate(); } return success; } private Component getComponentFromDasMLFragment(String dasML) { try { Document document = FormBase.parseDasML(new java.io.StringReader(dasML), null); Element element = document.getDocumentElement(); String tag = element.getTagName(); if (tag.equals("panel")) { return new FormPanel(element, getForm()); } else if (tag.equals("radiobutton")) { return new FormRadioButton(element, getForm()); } else if (tag.equals("textfield")) { return new FormTextField(element, getForm()); } else if (tag.equals("text")) { return new FormText(element); } else if (tag.equals("button")) { return new FormButton(element, getForm()); } else if (tag.equals("checkbox")) { return new FormCheckBox(element, getForm()); } else if (tag.equals("buttongroup")) { return new FormRadioButtonGroup(element, getForm()); } else if (tag.equals("canvas")) { org.das2.graph.DasCanvas canvas = Processor.processCanvasElement(element, getForm() ); add(canvas); } else if (tag.equals("choice")) { return new FormChoice(element, getForm()); } } catch (javax.xml.parsers.ParserConfigurationException pce) { throw new RuntimeException(pce); } catch (org.xml.sax.SAXException se) { throw new RuntimeException(se); } catch (org.das2.DasException de) { org.das2.util.DasExceptionHandler.handle(de); } catch (org.das2.dasml.ParsedExpressionException pee) { org.das2.util.DasExceptionHandler.handle(pee); } catch (IOException ioe) { org.das2.util.DasExceptionHandler.handle(ioe); } catch ( java.text.ParseException ex ) { org.das2.util.DasExceptionHandler.handle(ex); } return null; } protected Transferable getTransferable(int x, int y, int action) { for (int i = 0; i < getComponentCount(); i++) { Component c = getComponent(i); if (c.getBounds().contains(x, y)) { if (c instanceof DasCanvas) { return new TransferableCanvas((DasCanvas)c); } else if (c instanceof FormPanel) { return new TransferableFormComponent((FormPanel)c); } else if (c instanceof FormText) { return new TransferableFormComponent((FormText)c); } else if (c instanceof FormTextField) { return new TransferableFormComponent((FormTextField)c); } else if (c instanceof FormButton) { return new TransferableFormComponent((FormButton)c); } else if (c instanceof FormCheckBox) { return new TransferableFormComponent((FormCheckBox)c); } else if (c instanceof FormRadioButtonGroup) { return new TransferableFormComponent((FormRadioButtonGroup)c); } else if (c instanceof FormRadioButton) { return new TransferableFormComponent((FormRadioButton)c); } else if (c instanceof FormTab) { return new TransferableFormComponent((FormTab)c); } else if (c instanceof FormChoice) { return new TransferableFormComponent((FormChoice)c); } else if (c instanceof FormList) { return new TransferableFormComponent((FormList)c); } } } return null; } protected void exportDone(Transferable t, int action) { } } }